radeon_modes.c revision 9d25673e
1/*
2 * Copyright 2000 ATI Technologies Inc., Markham, Ontario, and
3 *                VA Linux Systems Inc., Fremont, California.
4 *
5 * All Rights Reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining
8 * a copy of this software and associated documentation files (the
9 * "Software"), to deal in the Software without restriction, including
10 * without limitation on the rights to use, copy, modify, merge,
11 * publish, distribute, sublicense, and/or sell copies of the Software,
12 * and to permit persons to whom the Software is furnished to do so,
13 * subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice (including the
16 * next paragraph) shall be included in all copies or substantial
17 * portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 * NON-INFRINGEMENT.  IN NO EVENT SHALL ATI, VA LINUX SYSTEMS AND/OR
23 * THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
24 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 * DEALINGS IN THE SOFTWARE.
27 */
28
29#ifdef HAVE_CONFIG_H
30#include "config.h"
31#endif
32
33/*
34 * Authors:
35 *   Kevin E. Martin <martin@xfree86.org>
36 *   Rickard E. Faith <faith@valinux.com>
37 *   Alan Hourihane <alanh@fairlite.demon.co.uk>
38 */
39
40#include <string.h>
41#include <stdio.h>
42
43#include "xf86.h"
44				/* Driver data structures */
45#include "randrstr.h"
46#include "radeon_probe.h"
47#include "radeon.h"
48#include "radeon_reg.h"
49#include "radeon_macros.h"
50#include "radeon_version.h"
51#include "radeon_atombios.h"
52
53#include "xf86Modes.h"
54				/* DDC support */
55#include "xf86DDC.h"
56#include <randrstr.h>
57
58void RADEONSetPitch (ScrnInfoPtr pScrn)
59{
60    int  dummy = pScrn->virtualX;
61    RADEONInfoPtr info = RADEONPTR(pScrn);
62    int pitch_mask = 0;
63    int align_large;
64
65    align_large = info->allowColorTiling || IS_AVIVO_VARIANT;
66
67    /* FIXME: May need to validate line pitch here */
68    if (info->ChipFamily < CHIP_FAMILY_R600) {
69	switch (pScrn->depth / 8) {
70	case 1: pitch_mask = align_large ? 256 : 128;
71	    break;
72	case 2: pitch_mask = align_large ? 128 : 32;
73	    break;
74	case 3:
75	case 4: pitch_mask = align_large ? 64 : 16;
76	    break;
77	}
78    } else
79	pitch_mask = 256; /* r6xx/r7xx need 256B alignment for accel */
80
81    dummy = RADEON_ALIGN(pScrn->virtualX, pitch_mask);
82    pScrn->displayWidth = dummy;
83    info->CurrentLayout.displayWidth = pScrn->displayWidth;
84
85}
86
87static DisplayModePtr
88RADEONTVModes(xf86OutputPtr output)
89{
90    DisplayModePtr new  = NULL;
91
92    /* just a place holder */
93    new = xf86CVTMode(800, 600, 60.00, FALSE, FALSE);
94    xf86SetModeDefaultName(new);
95    new->type = M_T_DRIVER | M_T_PREFERRED;
96
97    return new;
98}
99
100static DisplayModePtr
101RADEONATOMTVModes(xf86OutputPtr output)
102{
103    DisplayModePtr  last       = NULL;
104    DisplayModePtr  new        = NULL;
105    DisplayModePtr  first      = NULL;
106    int i;
107    /* Add some common sizes */
108    int widths[5] =  {640, 720, 800, 848, 1024};
109    int heights[5] = {480, 480, 600, 480,  768};
110
111    for (i = 0; i < 5; i++) {
112	new = xf86CVTMode(widths[i], heights[i], 60.0, FALSE, FALSE);
113	xf86SetModeDefaultName(new);
114
115	new->type       = M_T_DRIVER;
116
117	new->next       = NULL;
118	new->prev       = last;
119
120	if (last) last->next = new;
121	last = new;
122	if (!first) first = new;
123    }
124
125    if (last) {
126	last->next   = NULL; //first;
127	first->prev  = NULL; //last;
128    }
129
130    return first;
131}
132
133/* This is used only when no mode is specified for FP and no ddc is
134 * available.  We force it to native mode, if possible.
135 */
136static DisplayModePtr RADEONFPNativeMode(xf86OutputPtr output)
137{
138    ScrnInfoPtr pScrn = output->scrn;
139    RADEONOutputPrivatePtr radeon_output = output->driver_private;
140    radeon_native_mode_ptr native_mode = &radeon_output->native_mode;
141    DisplayModePtr  new   = NULL;
142    char            stmp[32];
143
144    if (native_mode->PanelXRes != 0 &&
145	native_mode->PanelYRes != 0 &&
146	native_mode->DotClock != 0) {
147
148	new             = xnfcalloc(1, sizeof (DisplayModeRec));
149	sprintf(stmp, "%dx%d", native_mode->PanelXRes, native_mode->PanelYRes);
150	new->name       = xnfalloc(strlen(stmp) + 1);
151	/*
152	 * XXX - expanded __UNCONST() version, new->name became const in
153	 * xorg-server 21.*
154	 */
155	strcpy((void *)(unsigned long)(const void *)new->name, stmp);
156	new->HDisplay   = native_mode->PanelXRes;
157	new->VDisplay   = native_mode->PanelYRes;
158
159	new->HTotal     = new->HDisplay + native_mode->HBlank;
160	new->HSyncStart = new->HDisplay + native_mode->HOverPlus;
161	new->HSyncEnd   = new->HSyncStart + native_mode->HSyncWidth;
162	new->VTotal     = new->VDisplay + native_mode->VBlank;
163	new->VSyncStart = new->VDisplay + native_mode->VOverPlus;
164	new->VSyncEnd   = new->VSyncStart + native_mode->VSyncWidth;
165
166	new->Clock      = native_mode->DotClock;
167	new->Flags      = native_mode->Flags;
168
169	if (new) {
170	    new->type       = M_T_DRIVER | M_T_PREFERRED;
171
172	    new->next       = NULL;
173	    new->prev       = NULL;
174	}
175
176	xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Added native panel mode: %dx%d\n",
177		   native_mode->PanelXRes, native_mode->PanelYRes);
178    } else if (native_mode->PanelXRes != 0 &&
179	       native_mode->PanelYRes != 0) {
180
181	new = xf86CVTMode(native_mode->PanelXRes, native_mode->PanelYRes, 60.0, TRUE, FALSE);
182
183	if (new) {
184	    new->type       = M_T_DRIVER | M_T_PREFERRED;
185	    xf86SetModeDefaultName(new);
186
187	    new->next       = NULL;
188	    new->prev       = NULL;
189	}
190
191	xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Added native panel mode using CVT: %dx%d\n",
192		   native_mode->PanelXRes, native_mode->PanelYRes);
193    }
194
195    return new;
196}
197
198#if defined(__powerpc__)
199/* Apple eMacs need special modes for the internal CRT, e.g.,
200 * Modeline "640x480"    62.12   640  680  752  864  480 481 484  521 +HSync +Vsync
201 * Modeline "800x600"    76.84   800  848  936 1072  600 601 604  640 +HSync +Vsync
202 * Modeline "1024x768"   99.07  1024 1088 1200 1376  768 769 772  809 +HSync +Vsync
203 * Modeline "1152x864"  112.36  1152 1224 1352 1552  864 865 868  905 +HSync +Vsync
204 * Modeline "1280x960"  124.54  1280 1368 1504 1728  960 961 964 1001 +HSync +Vsync
205 */
206static DisplayModePtr RADEONeMacModes(xf86OutputPtr output)
207{
208    ScrnInfoPtr pScrn = output->scrn;
209    DisplayModePtr last=NULL, new=NULL, first=NULL;
210    int i, *modep;
211    static const char *modenames[5] = {
212	"640x480", "800x600", "1024x768", "1152x864", "1280x960"
213    };
214    static int modes[9*5] = {
215	 62120,  640,  680,  752,  864, 480, 481, 484,  521,
216	 76840,  800,  848,  936, 1072, 600, 601, 604,  640,
217	 99070, 1024, 1088, 1200, 1376, 768, 769, 772,  809,
218	112360, 1152, 1224, 1352, 1552, 864, 865, 868,  905,
219	124540, 1280, 1368, 1504, 1728, 960, 961, 964, 1001
220    };
221    modep = modes;
222
223    for (i=0; i<5; i++) {
224	new = xnfcalloc(1, sizeof (DisplayModeRec));
225	if (new) {
226	    new->name       = xnfalloc(strlen(modenames[i]) + 1);
227	    strcpy(new->name, modenames[i]);
228	    new->Clock      = *modep++;
229
230	    new->HDisplay   = *modep++;
231	    new->HSyncStart = *modep++;
232	    new->HSyncEnd   = *modep++;
233	    new->HTotal     = *modep++;
234
235	    new->VDisplay   = *modep++;
236	    new->VSyncStart = *modep++;
237	    new->VSyncEnd   = *modep++;
238	    new->VTotal     = *modep++;
239
240	    new->Flags      = 0;
241	    new->type       = M_T_DRIVER;
242	    if (i==2)
243		new->type |= M_T_PREFERRED;
244	    new->next       = NULL;
245	    new->prev       = last;
246	    if (last) last->next = new;
247	    last = new;
248	    if (!first) first = new;
249	    xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Added eMac mode %s\n", modenames[i]);
250	}
251    }
252
253    return first;
254}
255#endif
256
257/* this function is basically a hack to add the screen modes */
258static void RADEONAddScreenModes(xf86OutputPtr output, DisplayModePtr *modeList)
259{
260    ScrnInfoPtr pScrn = output->scrn;
261    RADEONOutputPrivatePtr radeon_output = output->driver_private;
262    radeon_native_mode_ptr native_mode = &radeon_output->native_mode;
263    DisplayModePtr  last       = NULL;
264    DisplayModePtr  new        = NULL;
265    DisplayModePtr  first      = NULL;
266    int             count      = 0;
267    int             i, width, height;
268    const char **ppModeName = pScrn->display->modes;
269
270    first = last = *modeList;
271
272    /* We have a flat panel connected to the primary display, and we
273     * don't have any DDC info.
274     */
275    for (i = 0; ppModeName[i] != NULL; i++) {
276
277	if (sscanf(ppModeName[i], "%dx%d", &width, &height) != 2) continue;
278
279	if (radeon_output->active_device & (ATOM_DEVICE_LCD_SUPPORT)) {
280	    /* already added the native mode */
281	    if (width == native_mode->PanelXRes && height == native_mode->PanelYRes)
282		continue;
283
284	    /* Note: We allow all non-standard modes as long as they do not
285	     * exceed the native resolution of the panel.  Since these modes
286	     * need the internal RMX unit in the video chips (and there is
287	     * only one per card), this will only apply to the primary head.
288	     */
289	    if (width < 320 || width > native_mode->PanelXRes ||
290		height < 200 || height > native_mode->PanelYRes) {
291		xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
292			   "Mode %s is out of range.\n", ppModeName[i]);
293		xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
294			   "Valid FP modes must be between 320x200-%dx%d\n",
295			   native_mode->PanelXRes, native_mode->PanelYRes);
296		continue;
297	    }
298	}
299
300	new = xf86CVTMode(width, height, 60.0, FALSE, FALSE);
301	xf86SetModeDefaultName(new);
302
303	new->type      |= M_T_USERDEF;
304
305	new->next       = NULL;
306	new->prev       = last;
307
308	if (last) last->next = new;
309	last = new;
310	if (!first) first = new;
311
312	count++;
313	xf86DrvMsg(pScrn->scrnIndex, X_INFO,
314		   "Adding Screen mode: %s\n", new->name);
315    }
316
317
318    /* Close the doubly-linked mode list, if we found any usable modes */
319    if (last) {
320	last->next   = NULL; //first;
321	first->prev  = NULL; //last;
322	*modeList = first;
323    }
324
325    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
326	       "Total number of valid Screen mode(s) added: %d\n", count);
327
328}
329
330/* BIOS may not have right panel size, we search through all supported
331 * DDC modes looking for the maximum panel size.
332 */
333static void
334RADEONUpdatePanelSize(xf86OutputPtr output)
335{
336    ScrnInfoPtr pScrn = output->scrn;
337    RADEONInfoPtr  info       = RADEONPTR(pScrn);
338    RADEONOutputPrivatePtr radeon_output = output->driver_private;
339    radeon_native_mode_ptr native_mode = &radeon_output->native_mode;
340    int             j;
341    xf86MonPtr ddc = output->MonInfo;
342    DisplayModePtr  p;
343
344    // update output's native mode
345    if (radeon_output->active_device & (ATOM_DEVICE_LCD_SUPPORT)) {
346	radeon_encoder_ptr radeon_encoder = radeon_get_encoder(output);
347	if (radeon_encoder) {
348	    radeon_lvds_ptr lvds = (radeon_lvds_ptr)radeon_encoder->dev_priv;
349	    if (lvds)
350		radeon_output->native_mode = lvds->native_mode;
351	}
352    }
353
354    // crtc should handle?
355    if ((info->UseBiosDividers && native_mode->DotClock != 0) || (ddc == NULL))
356       return;
357
358    /* Go thru detailed timing table first */
359    for (j = 0; j < 4; j++) {
360	if (ddc->det_mon[j].type == 0) {
361	    struct detailed_timings *d_timings =
362		&ddc->det_mon[j].section.d_timings;
363           int match = 0;
364
365           /* If we didn't get a panel clock or guessed one, try to match the
366            * mode with the panel size. We do that because we _need_ a panel
367            * clock, or ValidateFPModes will fail, even when UseBiosDividers
368            * is set.
369            */
370           if (native_mode->DotClock == 0 &&
371               native_mode->PanelXRes == d_timings->h_active &&
372               native_mode->PanelYRes == d_timings->v_active)
373               match = 1;
374
375           /* If we don't have a BIOS provided panel data with fixed dividers,
376            * check for a larger panel size
377            */
378	    if (native_mode->PanelXRes < d_timings->h_active &&
379		native_mode->PanelYRes < d_timings->v_active &&
380		!info->UseBiosDividers)
381		match = 1;
382
383             if (match) {
384		native_mode->PanelXRes  = d_timings->h_active;
385		native_mode->PanelYRes  = d_timings->v_active;
386		native_mode->DotClock   = d_timings->clock / 1000;
387		native_mode->HOverPlus  = d_timings->h_sync_off;
388		native_mode->HSyncWidth = d_timings->h_sync_width;
389		native_mode->HBlank     = d_timings->h_blanking;
390		native_mode->VOverPlus  = d_timings->v_sync_off;
391		native_mode->VSyncWidth = d_timings->v_sync_width;
392		native_mode->VBlank     = d_timings->v_blanking;
393                native_mode->Flags      = (d_timings->interlaced ? V_INTERLACE : 0);
394                switch (d_timings->misc) {
395                case 0: native_mode->Flags |= V_NHSYNC | V_NVSYNC; break;
396                case 1: native_mode->Flags |= V_PHSYNC | V_NVSYNC; break;
397                case 2: native_mode->Flags |= V_NHSYNC | V_PVSYNC; break;
398                case 3: native_mode->Flags |= V_PHSYNC | V_PVSYNC; break;
399                }
400                xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Panel infos found from DDC detailed: %dx%d\n",
401                           native_mode->PanelXRes, native_mode->PanelYRes);
402	    }
403	}
404    }
405
406    if (info->UseBiosDividers && native_mode->DotClock != 0)
407       return;
408
409    /* Search thru standard VESA modes from EDID */
410    for (j = 0; j < 8; j++) {
411	if ((native_mode->PanelXRes < ddc->timings2[j].hsize) &&
412	    (native_mode->PanelYRes < ddc->timings2[j].vsize)) {
413	    for (p = pScrn->monitor->Modes; p; p = p->next) {
414		if ((ddc->timings2[j].hsize == p->HDisplay) &&
415		    (ddc->timings2[j].vsize == p->VDisplay)) {
416		    float  refresh =
417			(float)p->Clock * 1000.0 / p->HTotal / p->VTotal;
418
419		    if (fabsf((float)ddc->timings2[j].refresh - refresh) < 1.0) {
420			/* Is this good enough? */
421			native_mode->PanelXRes  = ddc->timings2[j].hsize;
422			native_mode->PanelYRes  = ddc->timings2[j].vsize;
423			native_mode->HBlank     = p->HTotal - p->HDisplay;
424			native_mode->HOverPlus  = p->HSyncStart - p->HDisplay;
425			native_mode->HSyncWidth = p->HSyncEnd - p->HSyncStart;
426			native_mode->VBlank     = p->VTotal - p->VDisplay;
427			native_mode->VOverPlus  = p->VSyncStart - p->VDisplay;
428			native_mode->VSyncWidth = p->VSyncEnd - p->VSyncStart;
429			native_mode->DotClock   = p->Clock;
430                        native_mode->Flags      = p->Flags;
431                        xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Panel infos found from DDC VESA/EDID: %dx%d\n",
432                                   native_mode->PanelXRes, native_mode->PanelYRes);
433		    }
434		}
435	    }
436	}
437    }
438}
439
440static void
441radeon_add_common_modes(xf86OutputPtr output, DisplayModePtr modes)
442{
443    RADEONOutputPrivatePtr radeon_output = output->driver_private;
444    radeon_native_mode_ptr native_mode = &radeon_output->native_mode;
445    DisplayModePtr  last       = NULL;
446    DisplayModePtr  new        = NULL;
447    DisplayModePtr  first      = NULL;
448    int i;
449    /* Add some common sizes */
450    int widths[15]  = {640, 800, 1024, 1152, 1280, 1280, 1280, 1280, 1280, 1440, 1400, 1680, 1600, 1920, 1920};
451    int heights[15] = {480, 600,  768,  768,  720,  800,  854,  960, 1024,  900, 1050, 1050, 1200, 1080, 1200};
452
453    for (i = 0; i < 15; i++) {
454	if (radeon_output->active_device & (ATOM_DEVICE_LCD_SUPPORT)) {
455	    /* already added the native mode */
456	    if (widths[i] == native_mode->PanelXRes && heights[i] == native_mode->PanelYRes)
457		continue;
458
459	    /* Note: We allow all non-standard modes as long as they do not
460	     * exceed the native resolution of the panel.  Since these modes
461	     * need the internal RMX unit in the video chips (and there is
462	     * only one per card), this will only apply to the primary head.
463	     */
464	    if (widths[i] < 320 || widths[i] > native_mode->PanelXRes ||
465		heights[i] < 200 || heights[i] > native_mode->PanelYRes)
466		continue;
467	}
468
469	new = xf86CVTMode(widths[i], heights[i], 60.0, FALSE, FALSE);
470	xf86SetModeDefaultName(new);
471
472	new->type       = M_T_DRIVER;
473
474	new->next       = NULL;
475	new->prev       = last;
476
477	if (last) last->next = new;
478	last = new;
479	if (!first) first = new;
480    }
481
482    if (last) {
483	last->next   = NULL; //first;
484	first->prev  = NULL; //last;
485    }
486
487    xf86ModesAdd(modes, first);
488
489}
490
491DisplayModePtr
492RADEONProbeOutputModes(xf86OutputPtr output)
493{
494    RADEONOutputPrivatePtr radeon_output = output->driver_private;
495    ScrnInfoPtr pScrn = output->scrn;
496    RADEONInfoPtr info = RADEONPTR(pScrn);
497    DisplayModePtr	    modes = NULL;
498    AtomBiosArgRec atomBiosArg;
499    AtomBiosResult atomBiosResult;
500
501    if (output->status == XF86OutputStatusConnected) {
502	if (radeon_output->active_device & (ATOM_DEVICE_TV_SUPPORT)) {
503	    if (IS_AVIVO_VARIANT)
504		modes = RADEONATOMTVModes(output);
505	    else
506		modes = RADEONTVModes(output);
507	} else if (radeon_output->active_device & (ATOM_DEVICE_CV_SUPPORT)) {
508	    atomBiosResult = RHDAtomBiosFunc(pScrn, info->atomBIOS,
509					     ATOMBIOS_GET_CV_MODES, &atomBiosArg);
510	    if (atomBiosResult == ATOM_SUCCESS) {
511		modes = atomBiosArg.modes;
512	    }
513	} else {
514	    if (radeon_output->active_device & (ATOM_DEVICE_DFP_SUPPORT | ATOM_DEVICE_LCD_SUPPORT))
515		RADEONUpdatePanelSize(output);
516	    if (output->MonInfo)
517		modes = xf86OutputGetEDIDModes (output);
518#if defined(__powerpc__)
519	    if ((info->MacModel == RADEON_MAC_EMAC) &&
520		(radeon_output->active_device & ATOM_DEVICE_CRT1_SUPPORT) &&
521		(modes == NULL))
522		modes = RADEONeMacModes(output);
523#endif
524	    if (modes == NULL) {
525		if ((radeon_output->active_device & (ATOM_DEVICE_LCD_SUPPORT)) && info->IsAtomBios) {
526		    atomBiosResult = RHDAtomBiosFunc(pScrn,
527						     info->atomBIOS,
528						     ATOMBIOS_GET_PANEL_EDID, &atomBiosArg);
529		    if (atomBiosResult == ATOM_SUCCESS) {
530			output->MonInfo = xf86InterpretEDID(pScrn->scrnIndex,
531							    atomBiosArg.EDIDBlock);
532			modes = xf86OutputGetEDIDModes(output);
533		    }
534		}
535		if (modes == NULL) {
536		    if (radeon_output->active_device & (ATOM_DEVICE_LCD_SUPPORT))
537			modes = RADEONFPNativeMode(output);
538		    /* add the screen modes */
539		    if (modes == NULL)
540			RADEONAddScreenModes(output, &modes);
541		}
542	    }
543	}
544    }
545
546    if (radeon_output->active_device & (ATOM_DEVICE_LCD_SUPPORT))
547	radeon_add_common_modes(output, modes);
548
549    return modes;
550}
551
552