radeon_modes.c revision b7e1c893
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) {
70b7e1c893Smrg	case 1: pitch_mask = align_large ? 255 : 127;
71b7e1c893Smrg	    break;
72b7e1c893Smrg	case 2: pitch_mask = align_large ? 127 : 31;
73b7e1c893Smrg	    break;
74b7e1c893Smrg	case 3:
75b7e1c893Smrg	case 4: pitch_mask = align_large ? 63 : 15;
76b7e1c893Smrg	    break;
77b7e1c893Smrg	}
78b7e1c893Smrg    } else
79b7e1c893Smrg	pitch_mask = 255; /* r6xx/r7xx need 256B alignment for accel */
80b7e1c893Smrg
81209ff23fSmrg    dummy = (pScrn->virtualX + pitch_mask) & ~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;
161209ff23fSmrg	new->Flags      = 0;
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);
172209ff23fSmrg    }
173209ff23fSmrg
174209ff23fSmrg    return new;
175209ff23fSmrg}
176209ff23fSmrg
177b7e1c893Smrg#if defined(__powerpc__)
178b7e1c893Smrg/* Apple eMacs need special modes for the internal CRT, e.g.,
179b7e1c893Smrg * Modeline "640x480"    62.12   640  680  752  864  480 481 484  521 +HSync +Vsync
180b7e1c893Smrg * Modeline "800x600"    76.84   800  848  936 1072  600 601 604  640 +HSync +Vsync
181b7e1c893Smrg * Modeline "1024x768"   99.07  1024 1088 1200 1376  768 769 772  809 +HSync +Vsync
182b7e1c893Smrg * Modeline "1152x864"  112.36  1152 1224 1352 1552  864 865 868  905 +HSync +Vsync
183b7e1c893Smrg * Modeline "1280x960"  124.54  1280 1368 1504 1728  960 961 964 1001 +HSync +Vsync
184b7e1c893Smrg */
185b7e1c893Smrgstatic DisplayModePtr RADEONeMacModes(xf86OutputPtr output)
186b7e1c893Smrg{
187b7e1c893Smrg    ScrnInfoPtr pScrn = output->scrn;
188b7e1c893Smrg    DisplayModePtr last=NULL, new=NULL, first=NULL;
189b7e1c893Smrg    int i, *modep;
190b7e1c893Smrg    static const char *modenames[5] = {
191b7e1c893Smrg	"640x480", "800x600", "1024x768", "1152x864", "1280x960"
192b7e1c893Smrg    };
193b7e1c893Smrg    static int modes[9*5] = {
194b7e1c893Smrg	 62120,  640,  680,  752,  864, 480, 481, 484,  521,
195b7e1c893Smrg	 76840,  800,  848,  936, 1072, 600, 601, 604,  640,
196b7e1c893Smrg	 99070, 1024, 1088, 1200, 1376, 768, 769, 772,  809,
197b7e1c893Smrg	112360, 1152, 1224, 1352, 1552, 864, 865, 868,  905,
198b7e1c893Smrg	124540, 1280, 1368, 1504, 1728, 960, 961, 964, 1001
199b7e1c893Smrg    };
200b7e1c893Smrg    modep = modes;
201b7e1c893Smrg
202b7e1c893Smrg    for (i=0; i<5; i++) {
203b7e1c893Smrg	new = xnfcalloc(1, sizeof (DisplayModeRec));
204b7e1c893Smrg	if (new) {
205b7e1c893Smrg	    new->name       = xnfalloc(strlen(modenames[i]) + 1);
206b7e1c893Smrg	    strcpy(new->name, modenames[i]);
207b7e1c893Smrg	    new->Clock      = *modep++;
208b7e1c893Smrg
209b7e1c893Smrg	    new->HDisplay   = *modep++;
210b7e1c893Smrg	    new->HSyncStart = *modep++;
211b7e1c893Smrg	    new->HSyncEnd   = *modep++;
212b7e1c893Smrg	    new->HTotal     = *modep++;
213b7e1c893Smrg
214b7e1c893Smrg	    new->VDisplay   = *modep++;
215b7e1c893Smrg	    new->VSyncStart = *modep++;
216b7e1c893Smrg	    new->VSyncEnd   = *modep++;
217b7e1c893Smrg	    new->VTotal     = *modep++;
218b7e1c893Smrg
219b7e1c893Smrg	    new->Flags      = 0;
220b7e1c893Smrg	    new->type       = M_T_DRIVER;
221b7e1c893Smrg	    if (i==2)
222b7e1c893Smrg		new->type |= M_T_PREFERRED;
223b7e1c893Smrg	    new->next       = NULL;
224b7e1c893Smrg	    new->prev       = last;
225b7e1c893Smrg	    if (last) last->next = new;
226b7e1c893Smrg	    last = new;
227b7e1c893Smrg	    if (!first) first = new;
228b7e1c893Smrg	    xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Added eMac mode %s\n", modenames[i]);
229b7e1c893Smrg	}
230b7e1c893Smrg    }
231b7e1c893Smrg
232b7e1c893Smrg    return first;
233b7e1c893Smrg}
234b7e1c893Smrg#endif
235b7e1c893Smrg
236209ff23fSmrg/* this function is basically a hack to add the screen modes */
237209ff23fSmrgstatic void RADEONAddScreenModes(xf86OutputPtr output, DisplayModePtr *modeList)
238209ff23fSmrg{
239209ff23fSmrg    ScrnInfoPtr pScrn = output->scrn;
240209ff23fSmrg    RADEONOutputPrivatePtr radeon_output = output->driver_private;
241b7e1c893Smrg    radeon_native_mode_ptr native_mode = &radeon_output->native_mode;
242209ff23fSmrg    DisplayModePtr  last       = NULL;
243209ff23fSmrg    DisplayModePtr  new        = NULL;
244209ff23fSmrg    DisplayModePtr  first      = NULL;
245209ff23fSmrg    int             count      = 0;
246209ff23fSmrg    int             i, width, height;
247209ff23fSmrg    char **ppModeName = pScrn->display->modes;
248209ff23fSmrg
249209ff23fSmrg    first = last = *modeList;
250209ff23fSmrg
251209ff23fSmrg    /* We have a flat panel connected to the primary display, and we
252209ff23fSmrg     * don't have any DDC info.
253209ff23fSmrg     */
254209ff23fSmrg    for (i = 0; ppModeName[i] != NULL; i++) {
255209ff23fSmrg
256209ff23fSmrg	if (sscanf(ppModeName[i], "%dx%d", &width, &height) != 2) continue;
257209ff23fSmrg
258b7e1c893Smrg	if (radeon_output->active_device & (ATOM_DEVICE_LCD_SUPPORT)) {
259209ff23fSmrg	    /* already added the native mode */
260b7e1c893Smrg	    if (width == native_mode->PanelXRes && height == native_mode->PanelYRes)
261209ff23fSmrg		continue;
262209ff23fSmrg
263209ff23fSmrg	    /* Note: We allow all non-standard modes as long as they do not
264209ff23fSmrg	     * exceed the native resolution of the panel.  Since these modes
265209ff23fSmrg	     * need the internal RMX unit in the video chips (and there is
266209ff23fSmrg	     * only one per card), this will only apply to the primary head.
267209ff23fSmrg	     */
268b7e1c893Smrg	    if (width < 320 || width > native_mode->PanelXRes ||
269b7e1c893Smrg		height < 200 || height > native_mode->PanelYRes) {
270209ff23fSmrg		xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
271209ff23fSmrg			   "Mode %s is out of range.\n", ppModeName[i]);
272209ff23fSmrg		xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
273209ff23fSmrg			   "Valid FP modes must be between 320x200-%dx%d\n",
274b7e1c893Smrg			   native_mode->PanelXRes, native_mode->PanelYRes);
275209ff23fSmrg		continue;
276209ff23fSmrg	    }
277209ff23fSmrg	}
278209ff23fSmrg
279209ff23fSmrg	new = xf86CVTMode(width, height, 60.0, FALSE, FALSE);
280209ff23fSmrg
281209ff23fSmrg	new->type      |= M_T_USERDEF;
282209ff23fSmrg
283209ff23fSmrg	new->next       = NULL;
284209ff23fSmrg	new->prev       = last;
285209ff23fSmrg
286209ff23fSmrg	if (last) last->next = new;
287209ff23fSmrg	last = new;
288209ff23fSmrg	if (!first) first = new;
289209ff23fSmrg
290209ff23fSmrg	count++;
291209ff23fSmrg	xf86DrvMsg(pScrn->scrnIndex, X_INFO,
292209ff23fSmrg		   "Adding Screen mode: %s\n", new->name);
293209ff23fSmrg    }
294209ff23fSmrg
295209ff23fSmrg
296209ff23fSmrg    /* Close the doubly-linked mode list, if we found any usable modes */
297209ff23fSmrg    if (last) {
298209ff23fSmrg	last->next   = NULL; //first;
299209ff23fSmrg	first->prev  = NULL; //last;
300209ff23fSmrg	*modeList = first;
301209ff23fSmrg    }
302209ff23fSmrg
303209ff23fSmrg    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
304209ff23fSmrg	       "Total number of valid Screen mode(s) added: %d\n", count);
305209ff23fSmrg
306209ff23fSmrg}
307209ff23fSmrg
308b7e1c893Smrg/* BIOS may not have right panel size, we search through all supported
309b7e1c893Smrg * DDC modes looking for the maximum panel size.
310b7e1c893Smrg */
311b7e1c893Smrgstatic void
312b7e1c893SmrgRADEONUpdatePanelSize(xf86OutputPtr output)
313b7e1c893Smrg{
314b7e1c893Smrg    ScrnInfoPtr pScrn = output->scrn;
315b7e1c893Smrg    RADEONInfoPtr  info       = RADEONPTR(pScrn);
316b7e1c893Smrg    RADEONOutputPrivatePtr radeon_output = output->driver_private;
317b7e1c893Smrg    radeon_native_mode_ptr native_mode = &radeon_output->native_mode;
318b7e1c893Smrg    int             j;
319b7e1c893Smrg    xf86MonPtr ddc = output->MonInfo;
320b7e1c893Smrg    DisplayModePtr  p;
321b7e1c893Smrg
322b7e1c893Smrg    // update output's native mode
323b7e1c893Smrg    if (radeon_output->active_device & (ATOM_DEVICE_LCD_SUPPORT)) {
324b7e1c893Smrg	radeon_encoder_ptr radeon_encoder = radeon_get_encoder(output);
325b7e1c893Smrg	if (radeon_encoder) {
326b7e1c893Smrg	    radeon_lvds_ptr lvds = (radeon_lvds_ptr)radeon_encoder->dev_priv;
327b7e1c893Smrg	    if (lvds)
328b7e1c893Smrg		radeon_output->native_mode = lvds->native_mode;
329b7e1c893Smrg	}
330b7e1c893Smrg    }
331b7e1c893Smrg
332b7e1c893Smrg    // crtc should handle?
333b7e1c893Smrg    if ((info->UseBiosDividers && native_mode->DotClock != 0) || (ddc == NULL))
334b7e1c893Smrg       return;
335b7e1c893Smrg
336b7e1c893Smrg    /* Go thru detailed timing table first */
337b7e1c893Smrg    for (j = 0; j < 4; j++) {
338b7e1c893Smrg	if (ddc->det_mon[j].type == 0) {
339b7e1c893Smrg	    struct detailed_timings *d_timings =
340b7e1c893Smrg		&ddc->det_mon[j].section.d_timings;
341b7e1c893Smrg           int match = 0;
342b7e1c893Smrg
343b7e1c893Smrg           /* If we didn't get a panel clock or guessed one, try to match the
344b7e1c893Smrg            * mode with the panel size. We do that because we _need_ a panel
345b7e1c893Smrg            * clock, or ValidateFPModes will fail, even when UseBiosDividers
346b7e1c893Smrg            * is set.
347b7e1c893Smrg            */
348b7e1c893Smrg           if (native_mode->DotClock == 0 &&
349b7e1c893Smrg               native_mode->PanelXRes == d_timings->h_active &&
350b7e1c893Smrg               native_mode->PanelYRes == d_timings->v_active)
351b7e1c893Smrg               match = 1;
352b7e1c893Smrg
353b7e1c893Smrg           /* If we don't have a BIOS provided panel data with fixed dividers,
354b7e1c893Smrg            * check for a larger panel size
355b7e1c893Smrg            */
356b7e1c893Smrg	    if (native_mode->PanelXRes < d_timings->h_active &&
357b7e1c893Smrg		native_mode->PanelYRes < d_timings->v_active &&
358b7e1c893Smrg		!info->UseBiosDividers)
359b7e1c893Smrg		match = 1;
360b7e1c893Smrg
361b7e1c893Smrg             if (match) {
362b7e1c893Smrg		native_mode->PanelXRes  = d_timings->h_active;
363b7e1c893Smrg		native_mode->PanelYRes  = d_timings->v_active;
364b7e1c893Smrg		native_mode->DotClock   = d_timings->clock / 1000;
365b7e1c893Smrg		native_mode->HOverPlus  = d_timings->h_sync_off;
366b7e1c893Smrg		native_mode->HSyncWidth = d_timings->h_sync_width;
367b7e1c893Smrg		native_mode->HBlank     = d_timings->h_blanking;
368b7e1c893Smrg		native_mode->VOverPlus  = d_timings->v_sync_off;
369b7e1c893Smrg		native_mode->VSyncWidth = d_timings->v_sync_width;
370b7e1c893Smrg		native_mode->VBlank     = d_timings->v_blanking;
371b7e1c893Smrg                native_mode->Flags      = (d_timings->interlaced ? V_INTERLACE : 0);
372b7e1c893Smrg                switch (d_timings->misc) {
373b7e1c893Smrg                case 0: native_mode->Flags |= V_NHSYNC | V_NVSYNC; break;
374b7e1c893Smrg                case 1: native_mode->Flags |= V_PHSYNC | V_NVSYNC; break;
375b7e1c893Smrg                case 2: native_mode->Flags |= V_NHSYNC | V_PVSYNC; break;
376b7e1c893Smrg                case 3: native_mode->Flags |= V_PHSYNC | V_PVSYNC; break;
377b7e1c893Smrg                }
378b7e1c893Smrg                xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Panel infos found from DDC detailed: %dx%d\n",
379b7e1c893Smrg                           native_mode->PanelXRes, native_mode->PanelYRes);
380b7e1c893Smrg	    }
381b7e1c893Smrg	}
382b7e1c893Smrg    }
383b7e1c893Smrg
384b7e1c893Smrg    if (info->UseBiosDividers && native_mode->DotClock != 0)
385b7e1c893Smrg       return;
386b7e1c893Smrg
387b7e1c893Smrg    /* Search thru standard VESA modes from EDID */
388b7e1c893Smrg    for (j = 0; j < 8; j++) {
389b7e1c893Smrg	if ((native_mode->PanelXRes < ddc->timings2[j].hsize) &&
390b7e1c893Smrg	    (native_mode->PanelYRes < ddc->timings2[j].vsize)) {
391b7e1c893Smrg	    for (p = pScrn->monitor->Modes; p; p = p->next) {
392b7e1c893Smrg		if ((ddc->timings2[j].hsize == p->HDisplay) &&
393b7e1c893Smrg		    (ddc->timings2[j].vsize == p->VDisplay)) {
394b7e1c893Smrg		    float  refresh =
395b7e1c893Smrg			(float)p->Clock * 1000.0 / p->HTotal / p->VTotal;
396b7e1c893Smrg
397b7e1c893Smrg		    if (abs((float)ddc->timings2[j].refresh - refresh) < 1.0) {
398b7e1c893Smrg			/* Is this good enough? */
399b7e1c893Smrg			native_mode->PanelXRes  = ddc->timings2[j].hsize;
400b7e1c893Smrg			native_mode->PanelYRes  = ddc->timings2[j].vsize;
401b7e1c893Smrg			native_mode->HBlank     = p->HTotal - p->HDisplay;
402b7e1c893Smrg			native_mode->HOverPlus  = p->HSyncStart - p->HDisplay;
403b7e1c893Smrg			native_mode->HSyncWidth = p->HSyncEnd - p->HSyncStart;
404b7e1c893Smrg			native_mode->VBlank     = p->VTotal - p->VDisplay;
405b7e1c893Smrg			native_mode->VOverPlus  = p->VSyncStart - p->VDisplay;
406b7e1c893Smrg			native_mode->VSyncWidth = p->VSyncEnd - p->VSyncStart;
407b7e1c893Smrg			native_mode->DotClock   = p->Clock;
408b7e1c893Smrg                        native_mode->Flags      = p->Flags;
409b7e1c893Smrg                        xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Panel infos found from DDC VESA/EDID: %dx%d\n",
410b7e1c893Smrg                                   native_mode->PanelXRes, native_mode->PanelYRes);
411b7e1c893Smrg		    }
412b7e1c893Smrg		}
413b7e1c893Smrg	    }
414b7e1c893Smrg	}
415b7e1c893Smrg    }
416b7e1c893Smrg}
417b7e1c893Smrg
418b7e1c893Smrgstatic void
419b7e1c893Smrgradeon_add_common_modes(xf86OutputPtr output, DisplayModePtr modes)
420b7e1c893Smrg{
421b7e1c893Smrg    RADEONOutputPrivatePtr radeon_output = output->driver_private;
422b7e1c893Smrg    radeon_native_mode_ptr native_mode = &radeon_output->native_mode;
423b7e1c893Smrg    DisplayModePtr  last       = NULL;
424b7e1c893Smrg    DisplayModePtr  new        = NULL;
425b7e1c893Smrg    DisplayModePtr  first      = NULL;
426b7e1c893Smrg    int i;
427b7e1c893Smrg    /* Add some common sizes */
428b7e1c893Smrg    int widths[15]  = {640, 800, 1024, 1152, 1280, 1280, 1280, 1280, 1280, 1440, 1400, 1680, 1600, 1920, 1920};
429b7e1c893Smrg    int heights[15] = {480, 600,  768,  768,  720,  800,  854,  960, 1024,  900, 1050, 1050, 1200, 1080, 1200};
430b7e1c893Smrg
431b7e1c893Smrg    for (i = 0; i < 15; i++) {
432b7e1c893Smrg	if (radeon_output->active_device & (ATOM_DEVICE_LCD_SUPPORT)) {
433b7e1c893Smrg	    /* already added the native mode */
434b7e1c893Smrg	    if (widths[i] == native_mode->PanelXRes && heights[i] == native_mode->PanelYRes)
435b7e1c893Smrg		continue;
436b7e1c893Smrg
437b7e1c893Smrg	    /* Note: We allow all non-standard modes as long as they do not
438b7e1c893Smrg	     * exceed the native resolution of the panel.  Since these modes
439b7e1c893Smrg	     * need the internal RMX unit in the video chips (and there is
440b7e1c893Smrg	     * only one per card), this will only apply to the primary head.
441b7e1c893Smrg	     */
442b7e1c893Smrg	    if (widths[i] < 320 || widths[i] > native_mode->PanelXRes ||
443b7e1c893Smrg		heights[i] < 200 || heights[i] > native_mode->PanelYRes)
444b7e1c893Smrg		continue;
445b7e1c893Smrg	}
446b7e1c893Smrg
447b7e1c893Smrg	new = xf86CVTMode(widths[i], heights[i], 60.0, FALSE, FALSE);
448b7e1c893Smrg
449b7e1c893Smrg	new->type       = M_T_DRIVER;
450b7e1c893Smrg
451b7e1c893Smrg	new->next       = NULL;
452b7e1c893Smrg	new->prev       = last;
453b7e1c893Smrg
454b7e1c893Smrg	if (last) last->next = new;
455b7e1c893Smrg	last = new;
456b7e1c893Smrg	if (!first) first = new;
457b7e1c893Smrg    }
458b7e1c893Smrg
459b7e1c893Smrg    if (last) {
460b7e1c893Smrg	last->next   = NULL; //first;
461b7e1c893Smrg	first->prev  = NULL; //last;
462b7e1c893Smrg    }
463b7e1c893Smrg
464b7e1c893Smrg    xf86ModesAdd(modes, first);
465b7e1c893Smrg
466b7e1c893Smrg}
467b7e1c893Smrg
468209ff23fSmrgDisplayModePtr
469209ff23fSmrgRADEONProbeOutputModes(xf86OutputPtr output)
470209ff23fSmrg{
471209ff23fSmrg    RADEONOutputPrivatePtr radeon_output = output->driver_private;
472209ff23fSmrg    ScrnInfoPtr pScrn = output->scrn;
473209ff23fSmrg    RADEONInfoPtr info = RADEONPTR(pScrn);
474209ff23fSmrg    DisplayModePtr	    modes = NULL;
475209ff23fSmrg    AtomBiosArgRec atomBiosArg;
476209ff23fSmrg    AtomBiosResult atomBiosResult;
477209ff23fSmrg
478209ff23fSmrg    if (output->status == XF86OutputStatusConnected) {
479b7e1c893Smrg	if (radeon_output->active_device & (ATOM_DEVICE_TV_SUPPORT)) {
480209ff23fSmrg	    if (IS_AVIVO_VARIANT)
481209ff23fSmrg		modes = RADEONATOMTVModes(output);
482209ff23fSmrg	    else
483209ff23fSmrg		modes = RADEONTVModes(output);
484b7e1c893Smrg	} else if (radeon_output->active_device & (ATOM_DEVICE_CV_SUPPORT)) {
485209ff23fSmrg	    atomBiosResult = RHDAtomBiosFunc(pScrn->scrnIndex, info->atomBIOS,
486209ff23fSmrg					     ATOMBIOS_GET_CV_MODES, &atomBiosArg);
487209ff23fSmrg	    if (atomBiosResult == ATOM_SUCCESS) {
488209ff23fSmrg		modes = atomBiosArg.modes;
489209ff23fSmrg	    }
490209ff23fSmrg	} else {
491b7e1c893Smrg	    if (radeon_output->active_device & (ATOM_DEVICE_DFP_SUPPORT | ATOM_DEVICE_LCD_SUPPORT))
492b7e1c893Smrg		RADEONUpdatePanelSize(output);
493209ff23fSmrg	    if (output->MonInfo)
494209ff23fSmrg		modes = xf86OutputGetEDIDModes (output);
495b7e1c893Smrg#if defined(__powerpc__)
496b7e1c893Smrg	    if ((info->MacModel == RADEON_MAC_EMAC) &&
497b7e1c893Smrg		(radeon_output->active_device & ATOM_DEVICE_CRT1_SUPPORT) &&
498b7e1c893Smrg		(modes == NULL))
499b7e1c893Smrg		modes = RADEONeMacModes(output);
500b7e1c893Smrg#endif
501209ff23fSmrg	    if (modes == NULL) {
502b7e1c893Smrg		if ((radeon_output->active_device & (ATOM_DEVICE_LCD_SUPPORT)) && info->IsAtomBios) {
503209ff23fSmrg		    atomBiosResult = RHDAtomBiosFunc(pScrn->scrnIndex,
504209ff23fSmrg						     info->atomBIOS,
505209ff23fSmrg						     ATOMBIOS_GET_PANEL_EDID, &atomBiosArg);
506209ff23fSmrg		    if (atomBiosResult == ATOM_SUCCESS) {
507209ff23fSmrg			output->MonInfo = xf86InterpretEDID(pScrn->scrnIndex,
508209ff23fSmrg							    atomBiosArg.EDIDBlock);
509209ff23fSmrg			modes = xf86OutputGetEDIDModes(output);
510209ff23fSmrg		    }
511209ff23fSmrg		}
512209ff23fSmrg		if (modes == NULL) {
513b7e1c893Smrg		    if (radeon_output->active_device & (ATOM_DEVICE_LCD_SUPPORT))
514209ff23fSmrg			modes = RADEONFPNativeMode(output);
515209ff23fSmrg		    /* add the screen modes */
516b7e1c893Smrg		    if (modes == NULL)
517b7e1c893Smrg			RADEONAddScreenModes(output, &modes);
518209ff23fSmrg		}
519209ff23fSmrg	    }
520209ff23fSmrg	}
521209ff23fSmrg    }
522209ff23fSmrg
523b7e1c893Smrg    if (radeon_output->active_device & (ATOM_DEVICE_LCD_SUPPORT))
524b7e1c893Smrg	radeon_add_common_modes(output, modes);
525b7e1c893Smrg
526209ff23fSmrg    return modes;
527209ff23fSmrg}
528209ff23fSmrg
529