radeon_modes.c revision 68105dcb
1209ff23fSmrg/*
2209ff23fSmrg * Copyright 2000 ATI Technologies Inc., Markham, Ontario, and
3209ff23fSmrg *                VA Linux Systems Inc., Fremont, California.
4209ff23fSmrg *
5209ff23fSmrg * All Rights Reserved.
6209ff23fSmrg *
7209ff23fSmrg * Permission is hereby granted, free of charge, to any person obtaining
8209ff23fSmrg * a copy of this software and associated documentation files (the
9209ff23fSmrg * "Software"), to deal in the Software without restriction, including
10209ff23fSmrg * without limitation on the rights to use, copy, modify, merge,
11209ff23fSmrg * publish, distribute, sublicense, and/or sell copies of the Software,
12209ff23fSmrg * and to permit persons to whom the Software is furnished to do so,
13209ff23fSmrg * subject to the following conditions:
14209ff23fSmrg *
15209ff23fSmrg * The above copyright notice and this permission notice (including the
16209ff23fSmrg * next paragraph) shall be included in all copies or substantial
17209ff23fSmrg * portions of the Software.
18209ff23fSmrg *
19209ff23fSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20209ff23fSmrg * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21209ff23fSmrg * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22209ff23fSmrg * NON-INFRINGEMENT.  IN NO EVENT SHALL ATI, VA LINUX SYSTEMS AND/OR
23209ff23fSmrg * THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
24209ff23fSmrg * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25209ff23fSmrg * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26209ff23fSmrg * DEALINGS IN THE SOFTWARE.
27209ff23fSmrg */
28209ff23fSmrg
29209ff23fSmrg#ifdef HAVE_CONFIG_H
30209ff23fSmrg#include "config.h"
31209ff23fSmrg#endif
32209ff23fSmrg
33209ff23fSmrg/*
34209ff23fSmrg * Authors:
35209ff23fSmrg *   Kevin E. Martin <martin@xfree86.org>
36209ff23fSmrg *   Rickard E. Faith <faith@valinux.com>
37209ff23fSmrg *   Alan Hourihane <alanh@fairlite.demon.co.uk>
38209ff23fSmrg */
39209ff23fSmrg
40209ff23fSmrg#include <string.h>
41209ff23fSmrg#include <stdio.h>
42209ff23fSmrg
43209ff23fSmrg#include "xf86.h"
44209ff23fSmrg				/* Driver data structures */
45209ff23fSmrg#include "randrstr.h"
46209ff23fSmrg#include "radeon_probe.h"
47209ff23fSmrg#include "radeon.h"
48209ff23fSmrg#include "radeon_reg.h"
49209ff23fSmrg#include "radeon_macros.h"
50209ff23fSmrg#include "radeon_version.h"
51209ff23fSmrg#include "radeon_atombios.h"
52209ff23fSmrg
53209ff23fSmrg#include "xf86Modes.h"
54209ff23fSmrg				/* DDC support */
55209ff23fSmrg#include "xf86DDC.h"
56209ff23fSmrg#include <randrstr.h>
57209ff23fSmrg
58209ff23fSmrgvoid RADEONSetPitch (ScrnInfoPtr pScrn)
59209ff23fSmrg{
60209ff23fSmrg    int  dummy = pScrn->virtualX;
61209ff23fSmrg    RADEONInfoPtr info = RADEONPTR(pScrn);
62209ff23fSmrg    int pitch_mask = 0;
63209ff23fSmrg    int align_large;
64209ff23fSmrg
65209ff23fSmrg    align_large = info->allowColorTiling || IS_AVIVO_VARIANT;
66209ff23fSmrg
67209ff23fSmrg    /* FIXME: May need to validate line pitch here */
68b7e1c893Smrg    if (info->ChipFamily < CHIP_FAMILY_R600) {
69b7e1c893Smrg	switch (pScrn->depth / 8) {
70ad43ddacSmrg	case 1: pitch_mask = align_large ? 256 : 128;
71b7e1c893Smrg	    break;
72ad43ddacSmrg	case 2: pitch_mask = align_large ? 128 : 32;
73b7e1c893Smrg	    break;
74b7e1c893Smrg	case 3:
75ad43ddacSmrg	case 4: pitch_mask = align_large ? 64 : 16;
76b7e1c893Smrg	    break;
77b7e1c893Smrg	}
78b7e1c893Smrg    } else
79ad43ddacSmrg	pitch_mask = 256; /* r6xx/r7xx need 256B alignment for accel */
80b7e1c893Smrg
81ad43ddacSmrg    dummy = RADEON_ALIGN(pScrn->virtualX, pitch_mask);
82209ff23fSmrg    pScrn->displayWidth = dummy;
83209ff23fSmrg    info->CurrentLayout.displayWidth = pScrn->displayWidth;
84209ff23fSmrg
85209ff23fSmrg}
86209ff23fSmrg
87209ff23fSmrgstatic DisplayModePtr
88209ff23fSmrgRADEONTVModes(xf86OutputPtr output)
89209ff23fSmrg{
90209ff23fSmrg    DisplayModePtr new  = NULL;
91209ff23fSmrg
92209ff23fSmrg    /* just a place holder */
93209ff23fSmrg    new = xf86CVTMode(800, 600, 60.00, FALSE, FALSE);
94209ff23fSmrg    new->type = M_T_DRIVER | M_T_PREFERRED;
95209ff23fSmrg
96209ff23fSmrg    return new;
97209ff23fSmrg}
98209ff23fSmrg
99209ff23fSmrgstatic DisplayModePtr
100209ff23fSmrgRADEONATOMTVModes(xf86OutputPtr output)
101209ff23fSmrg{
102209ff23fSmrg    DisplayModePtr  last       = NULL;
103209ff23fSmrg    DisplayModePtr  new        = NULL;
104209ff23fSmrg    DisplayModePtr  first      = NULL;
105b7e1c893Smrg    int i;
106209ff23fSmrg    /* Add some common sizes */
107b7e1c893Smrg    int widths[5] =  {640, 720, 800, 848, 1024};
108b7e1c893Smrg    int heights[5] = {480, 480, 600, 480,  768};
109209ff23fSmrg
110209ff23fSmrg    for (i = 0; i < 5; i++) {
111b7e1c893Smrg	new = xf86CVTMode(widths[i], heights[i], 60.0, FALSE, FALSE);
112209ff23fSmrg
113209ff23fSmrg	new->type       = M_T_DRIVER;
114209ff23fSmrg
115209ff23fSmrg	new->next       = NULL;
116209ff23fSmrg	new->prev       = last;
117209ff23fSmrg
118209ff23fSmrg	if (last) last->next = new;
119209ff23fSmrg	last = new;
120209ff23fSmrg	if (!first) first = new;
121209ff23fSmrg    }
122209ff23fSmrg
123209ff23fSmrg    if (last) {
124209ff23fSmrg	last->next   = NULL; //first;
125209ff23fSmrg	first->prev  = NULL; //last;
126209ff23fSmrg    }
127209ff23fSmrg
128209ff23fSmrg    return first;
129209ff23fSmrg}
130209ff23fSmrg
131209ff23fSmrg/* This is used only when no mode is specified for FP and no ddc is
132209ff23fSmrg * available.  We force it to native mode, if possible.
133209ff23fSmrg */
134209ff23fSmrgstatic DisplayModePtr RADEONFPNativeMode(xf86OutputPtr output)
135209ff23fSmrg{
136209ff23fSmrg    ScrnInfoPtr pScrn = output->scrn;
137209ff23fSmrg    RADEONOutputPrivatePtr radeon_output = output->driver_private;
138b7e1c893Smrg    radeon_native_mode_ptr native_mode = &radeon_output->native_mode;
139209ff23fSmrg    DisplayModePtr  new   = NULL;
140209ff23fSmrg    char            stmp[32];
141209ff23fSmrg
142b7e1c893Smrg    if (native_mode->PanelXRes != 0 &&
143b7e1c893Smrg	native_mode->PanelYRes != 0 &&
144b7e1c893Smrg	native_mode->DotClock != 0) {
145209ff23fSmrg
146209ff23fSmrg	new             = xnfcalloc(1, sizeof (DisplayModeRec));
147b7e1c893Smrg	sprintf(stmp, "%dx%d", native_mode->PanelXRes, native_mode->PanelYRes);
148209ff23fSmrg	new->name       = xnfalloc(strlen(stmp) + 1);
149209ff23fSmrg	strcpy(new->name, stmp);
150b7e1c893Smrg	new->HDisplay   = native_mode->PanelXRes;
151b7e1c893Smrg	new->VDisplay   = native_mode->PanelYRes;
152209ff23fSmrg
153b7e1c893Smrg	new->HTotal     = new->HDisplay + native_mode->HBlank;
154b7e1c893Smrg	new->HSyncStart = new->HDisplay + native_mode->HOverPlus;
155b7e1c893Smrg	new->HSyncEnd   = new->HSyncStart + native_mode->HSyncWidth;
156b7e1c893Smrg	new->VTotal     = new->VDisplay + native_mode->VBlank;
157b7e1c893Smrg	new->VSyncStart = new->VDisplay + native_mode->VOverPlus;
158b7e1c893Smrg	new->VSyncEnd   = new->VSyncStart + native_mode->VSyncWidth;
159209ff23fSmrg
160b7e1c893Smrg	new->Clock      = native_mode->DotClock;
161ad43ddacSmrg	new->Flags      = native_mode->Flags;
162209ff23fSmrg
163209ff23fSmrg	if (new) {
164209ff23fSmrg	    new->type       = M_T_DRIVER | M_T_PREFERRED;
165209ff23fSmrg
166209ff23fSmrg	    new->next       = NULL;
167209ff23fSmrg	    new->prev       = NULL;
168209ff23fSmrg	}
169209ff23fSmrg
170209ff23fSmrg	xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Added native panel mode: %dx%d\n",
171b7e1c893Smrg		   native_mode->PanelXRes, native_mode->PanelYRes);
172ad43ddacSmrg    } else if (native_mode->PanelXRes != 0 &&
173ad43ddacSmrg	       native_mode->PanelYRes != 0) {
174ad43ddacSmrg
175ad43ddacSmrg	new = xf86CVTMode(native_mode->PanelXRes, native_mode->PanelYRes, 60.0, TRUE, FALSE);
176ad43ddacSmrg
177ad43ddacSmrg	if (new) {
178ad43ddacSmrg	    new->type       = M_T_DRIVER | M_T_PREFERRED;
179ad43ddacSmrg
180ad43ddacSmrg	    new->next       = NULL;
181ad43ddacSmrg	    new->prev       = NULL;
182ad43ddacSmrg	}
183ad43ddacSmrg
184ad43ddacSmrg	xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Added native panel mode using CVT: %dx%d\n",
185ad43ddacSmrg		   native_mode->PanelXRes, native_mode->PanelYRes);
186209ff23fSmrg    }
187209ff23fSmrg
188209ff23fSmrg    return new;
189209ff23fSmrg}
190209ff23fSmrg
191b7e1c893Smrg#if defined(__powerpc__)
192b7e1c893Smrg/* Apple eMacs need special modes for the internal CRT, e.g.,
193b7e1c893Smrg * Modeline "640x480"    62.12   640  680  752  864  480 481 484  521 +HSync +Vsync
194b7e1c893Smrg * Modeline "800x600"    76.84   800  848  936 1072  600 601 604  640 +HSync +Vsync
195b7e1c893Smrg * Modeline "1024x768"   99.07  1024 1088 1200 1376  768 769 772  809 +HSync +Vsync
196b7e1c893Smrg * Modeline "1152x864"  112.36  1152 1224 1352 1552  864 865 868  905 +HSync +Vsync
197b7e1c893Smrg * Modeline "1280x960"  124.54  1280 1368 1504 1728  960 961 964 1001 +HSync +Vsync
198b7e1c893Smrg */
199b7e1c893Smrgstatic DisplayModePtr RADEONeMacModes(xf86OutputPtr output)
200b7e1c893Smrg{
201b7e1c893Smrg    ScrnInfoPtr pScrn = output->scrn;
202b7e1c893Smrg    DisplayModePtr last=NULL, new=NULL, first=NULL;
203b7e1c893Smrg    int i, *modep;
204b7e1c893Smrg    static const char *modenames[5] = {
205b7e1c893Smrg	"640x480", "800x600", "1024x768", "1152x864", "1280x960"
206b7e1c893Smrg    };
207b7e1c893Smrg    static int modes[9*5] = {
208b7e1c893Smrg	 62120,  640,  680,  752,  864, 480, 481, 484,  521,
209b7e1c893Smrg	 76840,  800,  848,  936, 1072, 600, 601, 604,  640,
210b7e1c893Smrg	 99070, 1024, 1088, 1200, 1376, 768, 769, 772,  809,
211b7e1c893Smrg	112360, 1152, 1224, 1352, 1552, 864, 865, 868,  905,
212b7e1c893Smrg	124540, 1280, 1368, 1504, 1728, 960, 961, 964, 1001
213b7e1c893Smrg    };
214b7e1c893Smrg    modep = modes;
215b7e1c893Smrg
216b7e1c893Smrg    for (i=0; i<5; i++) {
217b7e1c893Smrg	new = xnfcalloc(1, sizeof (DisplayModeRec));
218b7e1c893Smrg	if (new) {
219b7e1c893Smrg	    new->name       = xnfalloc(strlen(modenames[i]) + 1);
220b7e1c893Smrg	    strcpy(new->name, modenames[i]);
221b7e1c893Smrg	    new->Clock      = *modep++;
222b7e1c893Smrg
223b7e1c893Smrg	    new->HDisplay   = *modep++;
224b7e1c893Smrg	    new->HSyncStart = *modep++;
225b7e1c893Smrg	    new->HSyncEnd   = *modep++;
226b7e1c893Smrg	    new->HTotal     = *modep++;
227b7e1c893Smrg
228b7e1c893Smrg	    new->VDisplay   = *modep++;
229b7e1c893Smrg	    new->VSyncStart = *modep++;
230b7e1c893Smrg	    new->VSyncEnd   = *modep++;
231b7e1c893Smrg	    new->VTotal     = *modep++;
232b7e1c893Smrg
233b7e1c893Smrg	    new->Flags      = 0;
234b7e1c893Smrg	    new->type       = M_T_DRIVER;
235b7e1c893Smrg	    if (i==2)
236b7e1c893Smrg		new->type |= M_T_PREFERRED;
237b7e1c893Smrg	    new->next       = NULL;
238b7e1c893Smrg	    new->prev       = last;
239b7e1c893Smrg	    if (last) last->next = new;
240b7e1c893Smrg	    last = new;
241b7e1c893Smrg	    if (!first) first = new;
242b7e1c893Smrg	    xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Added eMac mode %s\n", modenames[i]);
243b7e1c893Smrg	}
244b7e1c893Smrg    }
245b7e1c893Smrg
246b7e1c893Smrg    return first;
247b7e1c893Smrg}
248b7e1c893Smrg#endif
249b7e1c893Smrg
250209ff23fSmrg/* this function is basically a hack to add the screen modes */
251209ff23fSmrgstatic void RADEONAddScreenModes(xf86OutputPtr output, DisplayModePtr *modeList)
252209ff23fSmrg{
253209ff23fSmrg    ScrnInfoPtr pScrn = output->scrn;
254209ff23fSmrg    RADEONOutputPrivatePtr radeon_output = output->driver_private;
255b7e1c893Smrg    radeon_native_mode_ptr native_mode = &radeon_output->native_mode;
256209ff23fSmrg    DisplayModePtr  last       = NULL;
257209ff23fSmrg    DisplayModePtr  new        = NULL;
258209ff23fSmrg    DisplayModePtr  first      = NULL;
259209ff23fSmrg    int             count      = 0;
260209ff23fSmrg    int             i, width, height;
261209ff23fSmrg    char **ppModeName = pScrn->display->modes;
262209ff23fSmrg
263209ff23fSmrg    first = last = *modeList;
264209ff23fSmrg
265209ff23fSmrg    /* We have a flat panel connected to the primary display, and we
266209ff23fSmrg     * don't have any DDC info.
267209ff23fSmrg     */
268209ff23fSmrg    for (i = 0; ppModeName[i] != NULL; i++) {
269209ff23fSmrg
270209ff23fSmrg	if (sscanf(ppModeName[i], "%dx%d", &width, &height) != 2) continue;
271209ff23fSmrg
272b7e1c893Smrg	if (radeon_output->active_device & (ATOM_DEVICE_LCD_SUPPORT)) {
273209ff23fSmrg	    /* already added the native mode */
274b7e1c893Smrg	    if (width == native_mode->PanelXRes && height == native_mode->PanelYRes)
275209ff23fSmrg		continue;
276209ff23fSmrg
277209ff23fSmrg	    /* Note: We allow all non-standard modes as long as they do not
278209ff23fSmrg	     * exceed the native resolution of the panel.  Since these modes
279209ff23fSmrg	     * need the internal RMX unit in the video chips (and there is
280209ff23fSmrg	     * only one per card), this will only apply to the primary head.
281209ff23fSmrg	     */
282b7e1c893Smrg	    if (width < 320 || width > native_mode->PanelXRes ||
283b7e1c893Smrg		height < 200 || height > native_mode->PanelYRes) {
284209ff23fSmrg		xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
285209ff23fSmrg			   "Mode %s is out of range.\n", ppModeName[i]);
286209ff23fSmrg		xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
287209ff23fSmrg			   "Valid FP modes must be between 320x200-%dx%d\n",
288b7e1c893Smrg			   native_mode->PanelXRes, native_mode->PanelYRes);
289209ff23fSmrg		continue;
290209ff23fSmrg	    }
291209ff23fSmrg	}
292209ff23fSmrg
293209ff23fSmrg	new = xf86CVTMode(width, height, 60.0, FALSE, FALSE);
294209ff23fSmrg
295209ff23fSmrg	new->type      |= M_T_USERDEF;
296209ff23fSmrg
297209ff23fSmrg	new->next       = NULL;
298209ff23fSmrg	new->prev       = last;
299209ff23fSmrg
300209ff23fSmrg	if (last) last->next = new;
301209ff23fSmrg	last = new;
302209ff23fSmrg	if (!first) first = new;
303209ff23fSmrg
304209ff23fSmrg	count++;
305209ff23fSmrg	xf86DrvMsg(pScrn->scrnIndex, X_INFO,
306209ff23fSmrg		   "Adding Screen mode: %s\n", new->name);
307209ff23fSmrg    }
308209ff23fSmrg
309209ff23fSmrg
310209ff23fSmrg    /* Close the doubly-linked mode list, if we found any usable modes */
311209ff23fSmrg    if (last) {
312209ff23fSmrg	last->next   = NULL; //first;
313209ff23fSmrg	first->prev  = NULL; //last;
314209ff23fSmrg	*modeList = first;
315209ff23fSmrg    }
316209ff23fSmrg
317209ff23fSmrg    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
318209ff23fSmrg	       "Total number of valid Screen mode(s) added: %d\n", count);
319209ff23fSmrg
320209ff23fSmrg}
321209ff23fSmrg
322b7e1c893Smrg/* BIOS may not have right panel size, we search through all supported
323b7e1c893Smrg * DDC modes looking for the maximum panel size.
324b7e1c893Smrg */
325b7e1c893Smrgstatic void
326b7e1c893SmrgRADEONUpdatePanelSize(xf86OutputPtr output)
327b7e1c893Smrg{
328b7e1c893Smrg    ScrnInfoPtr pScrn = output->scrn;
329b7e1c893Smrg    RADEONInfoPtr  info       = RADEONPTR(pScrn);
330b7e1c893Smrg    RADEONOutputPrivatePtr radeon_output = output->driver_private;
331b7e1c893Smrg    radeon_native_mode_ptr native_mode = &radeon_output->native_mode;
332b7e1c893Smrg    int             j;
333b7e1c893Smrg    xf86MonPtr ddc = output->MonInfo;
334b7e1c893Smrg    DisplayModePtr  p;
335b7e1c893Smrg
336b7e1c893Smrg    // update output's native mode
337b7e1c893Smrg    if (radeon_output->active_device & (ATOM_DEVICE_LCD_SUPPORT)) {
338b7e1c893Smrg	radeon_encoder_ptr radeon_encoder = radeon_get_encoder(output);
339b7e1c893Smrg	if (radeon_encoder) {
340b7e1c893Smrg	    radeon_lvds_ptr lvds = (radeon_lvds_ptr)radeon_encoder->dev_priv;
341b7e1c893Smrg	    if (lvds)
342b7e1c893Smrg		radeon_output->native_mode = lvds->native_mode;
343b7e1c893Smrg	}
344b7e1c893Smrg    }
345b7e1c893Smrg
346b7e1c893Smrg    // crtc should handle?
347b7e1c893Smrg    if ((info->UseBiosDividers && native_mode->DotClock != 0) || (ddc == NULL))
348b7e1c893Smrg       return;
349b7e1c893Smrg
350b7e1c893Smrg    /* Go thru detailed timing table first */
351b7e1c893Smrg    for (j = 0; j < 4; j++) {
352b7e1c893Smrg	if (ddc->det_mon[j].type == 0) {
353b7e1c893Smrg	    struct detailed_timings *d_timings =
354b7e1c893Smrg		&ddc->det_mon[j].section.d_timings;
355b7e1c893Smrg           int match = 0;
356b7e1c893Smrg
357b7e1c893Smrg           /* If we didn't get a panel clock or guessed one, try to match the
358b7e1c893Smrg            * mode with the panel size. We do that because we _need_ a panel
359b7e1c893Smrg            * clock, or ValidateFPModes will fail, even when UseBiosDividers
360b7e1c893Smrg            * is set.
361b7e1c893Smrg            */
362b7e1c893Smrg           if (native_mode->DotClock == 0 &&
363b7e1c893Smrg               native_mode->PanelXRes == d_timings->h_active &&
364b7e1c893Smrg               native_mode->PanelYRes == d_timings->v_active)
365b7e1c893Smrg               match = 1;
366b7e1c893Smrg
367b7e1c893Smrg           /* If we don't have a BIOS provided panel data with fixed dividers,
368b7e1c893Smrg            * check for a larger panel size
369b7e1c893Smrg            */
370b7e1c893Smrg	    if (native_mode->PanelXRes < d_timings->h_active &&
371b7e1c893Smrg		native_mode->PanelYRes < d_timings->v_active &&
372b7e1c893Smrg		!info->UseBiosDividers)
373b7e1c893Smrg		match = 1;
374b7e1c893Smrg
375b7e1c893Smrg             if (match) {
376b7e1c893Smrg		native_mode->PanelXRes  = d_timings->h_active;
377b7e1c893Smrg		native_mode->PanelYRes  = d_timings->v_active;
378b7e1c893Smrg		native_mode->DotClock   = d_timings->clock / 1000;
379b7e1c893Smrg		native_mode->HOverPlus  = d_timings->h_sync_off;
380b7e1c893Smrg		native_mode->HSyncWidth = d_timings->h_sync_width;
381b7e1c893Smrg		native_mode->HBlank     = d_timings->h_blanking;
382b7e1c893Smrg		native_mode->VOverPlus  = d_timings->v_sync_off;
383b7e1c893Smrg		native_mode->VSyncWidth = d_timings->v_sync_width;
384b7e1c893Smrg		native_mode->VBlank     = d_timings->v_blanking;
385b7e1c893Smrg                native_mode->Flags      = (d_timings->interlaced ? V_INTERLACE : 0);
386b7e1c893Smrg                switch (d_timings->misc) {
387b7e1c893Smrg                case 0: native_mode->Flags |= V_NHSYNC | V_NVSYNC; break;
388b7e1c893Smrg                case 1: native_mode->Flags |= V_PHSYNC | V_NVSYNC; break;
389b7e1c893Smrg                case 2: native_mode->Flags |= V_NHSYNC | V_PVSYNC; break;
390b7e1c893Smrg                case 3: native_mode->Flags |= V_PHSYNC | V_PVSYNC; break;
391b7e1c893Smrg                }
392b7e1c893Smrg                xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Panel infos found from DDC detailed: %dx%d\n",
393b7e1c893Smrg                           native_mode->PanelXRes, native_mode->PanelYRes);
394b7e1c893Smrg	    }
395b7e1c893Smrg	}
396b7e1c893Smrg    }
397b7e1c893Smrg
398b7e1c893Smrg    if (info->UseBiosDividers && native_mode->DotClock != 0)
399b7e1c893Smrg       return;
400b7e1c893Smrg
401b7e1c893Smrg    /* Search thru standard VESA modes from EDID */
402b7e1c893Smrg    for (j = 0; j < 8; j++) {
403b7e1c893Smrg	if ((native_mode->PanelXRes < ddc->timings2[j].hsize) &&
404b7e1c893Smrg	    (native_mode->PanelYRes < ddc->timings2[j].vsize)) {
405b7e1c893Smrg	    for (p = pScrn->monitor->Modes; p; p = p->next) {
406b7e1c893Smrg		if ((ddc->timings2[j].hsize == p->HDisplay) &&
407b7e1c893Smrg		    (ddc->timings2[j].vsize == p->VDisplay)) {
408b7e1c893Smrg		    float  refresh =
409b7e1c893Smrg			(float)p->Clock * 1000.0 / p->HTotal / p->VTotal;
410b7e1c893Smrg
411b7e1c893Smrg		    if (abs((float)ddc->timings2[j].refresh - refresh) < 1.0) {
412b7e1c893Smrg			/* Is this good enough? */
413b7e1c893Smrg			native_mode->PanelXRes  = ddc->timings2[j].hsize;
414b7e1c893Smrg			native_mode->PanelYRes  = ddc->timings2[j].vsize;
415b7e1c893Smrg			native_mode->HBlank     = p->HTotal - p->HDisplay;
416b7e1c893Smrg			native_mode->HOverPlus  = p->HSyncStart - p->HDisplay;
417b7e1c893Smrg			native_mode->HSyncWidth = p->HSyncEnd - p->HSyncStart;
418b7e1c893Smrg			native_mode->VBlank     = p->VTotal - p->VDisplay;
419b7e1c893Smrg			native_mode->VOverPlus  = p->VSyncStart - p->VDisplay;
420b7e1c893Smrg			native_mode->VSyncWidth = p->VSyncEnd - p->VSyncStart;
421b7e1c893Smrg			native_mode->DotClock   = p->Clock;
422b7e1c893Smrg                        native_mode->Flags      = p->Flags;
423b7e1c893Smrg                        xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Panel infos found from DDC VESA/EDID: %dx%d\n",
424b7e1c893Smrg                                   native_mode->PanelXRes, native_mode->PanelYRes);
425b7e1c893Smrg		    }
426b7e1c893Smrg		}
427b7e1c893Smrg	    }
428b7e1c893Smrg	}
429b7e1c893Smrg    }
430b7e1c893Smrg}
431b7e1c893Smrg
432b7e1c893Smrgstatic void
433b7e1c893Smrgradeon_add_common_modes(xf86OutputPtr output, DisplayModePtr modes)
434b7e1c893Smrg{
435b7e1c893Smrg    RADEONOutputPrivatePtr radeon_output = output->driver_private;
436b7e1c893Smrg    radeon_native_mode_ptr native_mode = &radeon_output->native_mode;
437b7e1c893Smrg    DisplayModePtr  last       = NULL;
438b7e1c893Smrg    DisplayModePtr  new        = NULL;
439b7e1c893Smrg    DisplayModePtr  first      = NULL;
440b7e1c893Smrg    int i;
441b7e1c893Smrg    /* Add some common sizes */
442b7e1c893Smrg    int widths[15]  = {640, 800, 1024, 1152, 1280, 1280, 1280, 1280, 1280, 1440, 1400, 1680, 1600, 1920, 1920};
443b7e1c893Smrg    int heights[15] = {480, 600,  768,  768,  720,  800,  854,  960, 1024,  900, 1050, 1050, 1200, 1080, 1200};
444b7e1c893Smrg
445b7e1c893Smrg    for (i = 0; i < 15; i++) {
446b7e1c893Smrg	if (radeon_output->active_device & (ATOM_DEVICE_LCD_SUPPORT)) {
447b7e1c893Smrg	    /* already added the native mode */
448b7e1c893Smrg	    if (widths[i] == native_mode->PanelXRes && heights[i] == native_mode->PanelYRes)
449b7e1c893Smrg		continue;
450b7e1c893Smrg
451b7e1c893Smrg	    /* Note: We allow all non-standard modes as long as they do not
452b7e1c893Smrg	     * exceed the native resolution of the panel.  Since these modes
453b7e1c893Smrg	     * need the internal RMX unit in the video chips (and there is
454b7e1c893Smrg	     * only one per card), this will only apply to the primary head.
455b7e1c893Smrg	     */
456b7e1c893Smrg	    if (widths[i] < 320 || widths[i] > native_mode->PanelXRes ||
457b7e1c893Smrg		heights[i] < 200 || heights[i] > native_mode->PanelYRes)
458b7e1c893Smrg		continue;
459b7e1c893Smrg	}
460b7e1c893Smrg
461b7e1c893Smrg	new = xf86CVTMode(widths[i], heights[i], 60.0, FALSE, FALSE);
462b7e1c893Smrg
463b7e1c893Smrg	new->type       = M_T_DRIVER;
464b7e1c893Smrg
465b7e1c893Smrg	new->next       = NULL;
466b7e1c893Smrg	new->prev       = last;
467b7e1c893Smrg
468b7e1c893Smrg	if (last) last->next = new;
469b7e1c893Smrg	last = new;
470b7e1c893Smrg	if (!first) first = new;
471b7e1c893Smrg    }
472b7e1c893Smrg
473b7e1c893Smrg    if (last) {
474b7e1c893Smrg	last->next   = NULL; //first;
475b7e1c893Smrg	first->prev  = NULL; //last;
476b7e1c893Smrg    }
477b7e1c893Smrg
478b7e1c893Smrg    xf86ModesAdd(modes, first);
479b7e1c893Smrg
480b7e1c893Smrg}
481b7e1c893Smrg
482209ff23fSmrgDisplayModePtr
483209ff23fSmrgRADEONProbeOutputModes(xf86OutputPtr output)
484209ff23fSmrg{
485209ff23fSmrg    RADEONOutputPrivatePtr radeon_output = output->driver_private;
486209ff23fSmrg    ScrnInfoPtr pScrn = output->scrn;
487209ff23fSmrg    RADEONInfoPtr info = RADEONPTR(pScrn);
488209ff23fSmrg    DisplayModePtr	    modes = NULL;
489209ff23fSmrg    AtomBiosArgRec atomBiosArg;
490209ff23fSmrg    AtomBiosResult atomBiosResult;
491209ff23fSmrg
492209ff23fSmrg    if (output->status == XF86OutputStatusConnected) {
493b7e1c893Smrg	if (radeon_output->active_device & (ATOM_DEVICE_TV_SUPPORT)) {
494209ff23fSmrg	    if (IS_AVIVO_VARIANT)
495209ff23fSmrg		modes = RADEONATOMTVModes(output);
496209ff23fSmrg	    else
497209ff23fSmrg		modes = RADEONTVModes(output);
498b7e1c893Smrg	} else if (radeon_output->active_device & (ATOM_DEVICE_CV_SUPPORT)) {
49968105dcbSveego	    atomBiosResult = RHDAtomBiosFunc(pScrn, info->atomBIOS,
500209ff23fSmrg					     ATOMBIOS_GET_CV_MODES, &atomBiosArg);
501209ff23fSmrg	    if (atomBiosResult == ATOM_SUCCESS) {
502209ff23fSmrg		modes = atomBiosArg.modes;
503209ff23fSmrg	    }
504209ff23fSmrg	} else {
505b7e1c893Smrg	    if (radeon_output->active_device & (ATOM_DEVICE_DFP_SUPPORT | ATOM_DEVICE_LCD_SUPPORT))
506b7e1c893Smrg		RADEONUpdatePanelSize(output);
507209ff23fSmrg	    if (output->MonInfo)
508209ff23fSmrg		modes = xf86OutputGetEDIDModes (output);
509b7e1c893Smrg#if defined(__powerpc__)
510b7e1c893Smrg	    if ((info->MacModel == RADEON_MAC_EMAC) &&
511b7e1c893Smrg		(radeon_output->active_device & ATOM_DEVICE_CRT1_SUPPORT) &&
512b7e1c893Smrg		(modes == NULL))
513b7e1c893Smrg		modes = RADEONeMacModes(output);
514b7e1c893Smrg#endif
515209ff23fSmrg	    if (modes == NULL) {
516b7e1c893Smrg		if ((radeon_output->active_device & (ATOM_DEVICE_LCD_SUPPORT)) && info->IsAtomBios) {
51768105dcbSveego		    atomBiosResult = RHDAtomBiosFunc(pScrn,
518209ff23fSmrg						     info->atomBIOS,
519209ff23fSmrg						     ATOMBIOS_GET_PANEL_EDID, &atomBiosArg);
520209ff23fSmrg		    if (atomBiosResult == ATOM_SUCCESS) {
521209ff23fSmrg			output->MonInfo = xf86InterpretEDID(pScrn->scrnIndex,
522209ff23fSmrg							    atomBiosArg.EDIDBlock);
523209ff23fSmrg			modes = xf86OutputGetEDIDModes(output);
524209ff23fSmrg		    }
525209ff23fSmrg		}
526209ff23fSmrg		if (modes == NULL) {
527b7e1c893Smrg		    if (radeon_output->active_device & (ATOM_DEVICE_LCD_SUPPORT))
528209ff23fSmrg			modes = RADEONFPNativeMode(output);
529209ff23fSmrg		    /* add the screen modes */
530b7e1c893Smrg		    if (modes == NULL)
531b7e1c893Smrg			RADEONAddScreenModes(output, &modes);
532209ff23fSmrg		}
533209ff23fSmrg	    }
534209ff23fSmrg	}
535209ff23fSmrg    }
536209ff23fSmrg
537b7e1c893Smrg    if (radeon_output->active_device & (ATOM_DEVICE_LCD_SUPPORT))
538b7e1c893Smrg	radeon_add_common_modes(output, modes);
539b7e1c893Smrg
540209ff23fSmrg    return modes;
541209ff23fSmrg}
542209ff23fSmrg
543