radeon_modes.c revision a7f02474
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    new->type = M_T_DRIVER | M_T_PREFERRED;
95
96    return new;
97}
98
99static DisplayModePtr
100RADEONATOMTVModes(xf86OutputPtr output)
101{
102    DisplayModePtr  last       = NULL;
103    DisplayModePtr  new        = NULL;
104    DisplayModePtr  first      = NULL;
105    int i;
106    /* Add some common sizes */
107    int widths[5] =  {640, 720, 800, 848, 1024};
108    int heights[5] = {480, 480, 600, 480,  768};
109
110    for (i = 0; i < 5; i++) {
111	new = xf86CVTMode(widths[i], heights[i], 60.0, FALSE, FALSE);
112
113	new->type       = M_T_DRIVER;
114
115	new->next       = NULL;
116	new->prev       = last;
117
118	if (last) last->next = new;
119	last = new;
120	if (!first) first = new;
121    }
122
123    if (last) {
124	last->next   = NULL; //first;
125	first->prev  = NULL; //last;
126    }
127
128    return first;
129}
130
131/* This is used only when no mode is specified for FP and no ddc is
132 * available.  We force it to native mode, if possible.
133 */
134static DisplayModePtr RADEONFPNativeMode(xf86OutputPtr output)
135{
136    ScrnInfoPtr pScrn = output->scrn;
137    RADEONOutputPrivatePtr radeon_output = output->driver_private;
138    radeon_native_mode_ptr native_mode = &radeon_output->native_mode;
139    DisplayModePtr  new   = NULL;
140    char            stmp[32];
141
142    if (native_mode->PanelXRes != 0 &&
143	native_mode->PanelYRes != 0 &&
144	native_mode->DotClock != 0) {
145
146	new             = xnfcalloc(1, sizeof (DisplayModeRec));
147	sprintf(stmp, "%dx%d", native_mode->PanelXRes, native_mode->PanelYRes);
148	new->name       = xnfalloc(strlen(stmp) + 1);
149	/*
150	 * XXX - expanded __UNCONST() version, new->name became const in
151	 * xorg-server 21.*
152	 */
153	strcpy((void *)(unsigned long)(const void *)new->name, stmp);
154	new->HDisplay   = native_mode->PanelXRes;
155	new->VDisplay   = native_mode->PanelYRes;
156
157	new->HTotal     = new->HDisplay + native_mode->HBlank;
158	new->HSyncStart = new->HDisplay + native_mode->HOverPlus;
159	new->HSyncEnd   = new->HSyncStart + native_mode->HSyncWidth;
160	new->VTotal     = new->VDisplay + native_mode->VBlank;
161	new->VSyncStart = new->VDisplay + native_mode->VOverPlus;
162	new->VSyncEnd   = new->VSyncStart + native_mode->VSyncWidth;
163
164	new->Clock      = native_mode->DotClock;
165	new->Flags      = native_mode->Flags;
166
167	if (new) {
168	    new->type       = M_T_DRIVER | M_T_PREFERRED;
169
170	    new->next       = NULL;
171	    new->prev       = NULL;
172	}
173
174	xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Added native panel mode: %dx%d\n",
175		   native_mode->PanelXRes, native_mode->PanelYRes);
176    } else if (native_mode->PanelXRes != 0 &&
177	       native_mode->PanelYRes != 0) {
178
179	new = xf86CVTMode(native_mode->PanelXRes, native_mode->PanelYRes, 60.0, TRUE, FALSE);
180
181	if (new) {
182	    new->type       = M_T_DRIVER | M_T_PREFERRED;
183
184	    new->next       = NULL;
185	    new->prev       = NULL;
186	}
187
188	xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Added native panel mode using CVT: %dx%d\n",
189		   native_mode->PanelXRes, native_mode->PanelYRes);
190    }
191
192    return new;
193}
194
195#if defined(__powerpc__)
196/* Apple eMacs need special modes for the internal CRT, e.g.,
197 * Modeline "640x480"    62.12   640  680  752  864  480 481 484  521 +HSync +Vsync
198 * Modeline "800x600"    76.84   800  848  936 1072  600 601 604  640 +HSync +Vsync
199 * Modeline "1024x768"   99.07  1024 1088 1200 1376  768 769 772  809 +HSync +Vsync
200 * Modeline "1152x864"  112.36  1152 1224 1352 1552  864 865 868  905 +HSync +Vsync
201 * Modeline "1280x960"  124.54  1280 1368 1504 1728  960 961 964 1001 +HSync +Vsync
202 */
203static DisplayModePtr RADEONeMacModes(xf86OutputPtr output)
204{
205    ScrnInfoPtr pScrn = output->scrn;
206    DisplayModePtr last=NULL, new=NULL, first=NULL;
207    int i, *modep;
208    static const char *modenames[5] = {
209	"640x480", "800x600", "1024x768", "1152x864", "1280x960"
210    };
211    static int modes[9*5] = {
212	 62120,  640,  680,  752,  864, 480, 481, 484,  521,
213	 76840,  800,  848,  936, 1072, 600, 601, 604,  640,
214	 99070, 1024, 1088, 1200, 1376, 768, 769, 772,  809,
215	112360, 1152, 1224, 1352, 1552, 864, 865, 868,  905,
216	124540, 1280, 1368, 1504, 1728, 960, 961, 964, 1001
217    };
218    modep = modes;
219
220    for (i=0; i<5; i++) {
221	new = xnfcalloc(1, sizeof (DisplayModeRec));
222	if (new) {
223	    new->name       = xnfalloc(strlen(modenames[i]) + 1);
224	    strcpy(new->name, modenames[i]);
225	    new->Clock      = *modep++;
226
227	    new->HDisplay   = *modep++;
228	    new->HSyncStart = *modep++;
229	    new->HSyncEnd   = *modep++;
230	    new->HTotal     = *modep++;
231
232	    new->VDisplay   = *modep++;
233	    new->VSyncStart = *modep++;
234	    new->VSyncEnd   = *modep++;
235	    new->VTotal     = *modep++;
236
237	    new->Flags      = 0;
238	    new->type       = M_T_DRIVER;
239	    if (i==2)
240		new->type |= M_T_PREFERRED;
241	    new->next       = NULL;
242	    new->prev       = last;
243	    if (last) last->next = new;
244	    last = new;
245	    if (!first) first = new;
246	    xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Added eMac mode %s\n", modenames[i]);
247	}
248    }
249
250    return first;
251}
252#endif
253
254/* this function is basically a hack to add the screen modes */
255static void RADEONAddScreenModes(xf86OutputPtr output, DisplayModePtr *modeList)
256{
257    ScrnInfoPtr pScrn = output->scrn;
258    RADEONOutputPrivatePtr radeon_output = output->driver_private;
259    radeon_native_mode_ptr native_mode = &radeon_output->native_mode;
260    DisplayModePtr  last       = NULL;
261    DisplayModePtr  new        = NULL;
262    DisplayModePtr  first      = NULL;
263    int             count      = 0;
264    int             i, width, height;
265    const char **ppModeName = pScrn->display->modes;
266
267    first = last = *modeList;
268
269    /* We have a flat panel connected to the primary display, and we
270     * don't have any DDC info.
271     */
272    for (i = 0; ppModeName[i] != NULL; i++) {
273
274	if (sscanf(ppModeName[i], "%dx%d", &width, &height) != 2) continue;
275
276	if (radeon_output->active_device & (ATOM_DEVICE_LCD_SUPPORT)) {
277	    /* already added the native mode */
278	    if (width == native_mode->PanelXRes && height == native_mode->PanelYRes)
279		continue;
280
281	    /* Note: We allow all non-standard modes as long as they do not
282	     * exceed the native resolution of the panel.  Since these modes
283	     * need the internal RMX unit in the video chips (and there is
284	     * only one per card), this will only apply to the primary head.
285	     */
286	    if (width < 320 || width > native_mode->PanelXRes ||
287		height < 200 || height > native_mode->PanelYRes) {
288		xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
289			   "Mode %s is out of range.\n", ppModeName[i]);
290		xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
291			   "Valid FP modes must be between 320x200-%dx%d\n",
292			   native_mode->PanelXRes, native_mode->PanelYRes);
293		continue;
294	    }
295	}
296
297	new = xf86CVTMode(width, height, 60.0, FALSE, FALSE);
298
299	new->type      |= M_T_USERDEF;
300
301	new->next       = NULL;
302	new->prev       = last;
303
304	if (last) last->next = new;
305	last = new;
306	if (!first) first = new;
307
308	count++;
309	xf86DrvMsg(pScrn->scrnIndex, X_INFO,
310		   "Adding Screen mode: %s\n", new->name);
311    }
312
313
314    /* Close the doubly-linked mode list, if we found any usable modes */
315    if (last) {
316	last->next   = NULL; //first;
317	first->prev  = NULL; //last;
318	*modeList = first;
319    }
320
321    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
322	       "Total number of valid Screen mode(s) added: %d\n", count);
323
324}
325
326/* BIOS may not have right panel size, we search through all supported
327 * DDC modes looking for the maximum panel size.
328 */
329static void
330RADEONUpdatePanelSize(xf86OutputPtr output)
331{
332    ScrnInfoPtr pScrn = output->scrn;
333    RADEONInfoPtr  info       = RADEONPTR(pScrn);
334    RADEONOutputPrivatePtr radeon_output = output->driver_private;
335    radeon_native_mode_ptr native_mode = &radeon_output->native_mode;
336    int             j;
337    xf86MonPtr ddc = output->MonInfo;
338    DisplayModePtr  p;
339
340    // update output's native mode
341    if (radeon_output->active_device & (ATOM_DEVICE_LCD_SUPPORT)) {
342	radeon_encoder_ptr radeon_encoder = radeon_get_encoder(output);
343	if (radeon_encoder) {
344	    radeon_lvds_ptr lvds = (radeon_lvds_ptr)radeon_encoder->dev_priv;
345	    if (lvds)
346		radeon_output->native_mode = lvds->native_mode;
347	}
348    }
349
350    // crtc should handle?
351    if ((info->UseBiosDividers && native_mode->DotClock != 0) || (ddc == NULL))
352       return;
353
354    /* Go thru detailed timing table first */
355    for (j = 0; j < 4; j++) {
356	if (ddc->det_mon[j].type == 0) {
357	    struct detailed_timings *d_timings =
358		&ddc->det_mon[j].section.d_timings;
359           int match = 0;
360
361           /* If we didn't get a panel clock or guessed one, try to match the
362            * mode with the panel size. We do that because we _need_ a panel
363            * clock, or ValidateFPModes will fail, even when UseBiosDividers
364            * is set.
365            */
366           if (native_mode->DotClock == 0 &&
367               native_mode->PanelXRes == d_timings->h_active &&
368               native_mode->PanelYRes == d_timings->v_active)
369               match = 1;
370
371           /* If we don't have a BIOS provided panel data with fixed dividers,
372            * check for a larger panel size
373            */
374	    if (native_mode->PanelXRes < d_timings->h_active &&
375		native_mode->PanelYRes < d_timings->v_active &&
376		!info->UseBiosDividers)
377		match = 1;
378
379             if (match) {
380		native_mode->PanelXRes  = d_timings->h_active;
381		native_mode->PanelYRes  = d_timings->v_active;
382		native_mode->DotClock   = d_timings->clock / 1000;
383		native_mode->HOverPlus  = d_timings->h_sync_off;
384		native_mode->HSyncWidth = d_timings->h_sync_width;
385		native_mode->HBlank     = d_timings->h_blanking;
386		native_mode->VOverPlus  = d_timings->v_sync_off;
387		native_mode->VSyncWidth = d_timings->v_sync_width;
388		native_mode->VBlank     = d_timings->v_blanking;
389                native_mode->Flags      = (d_timings->interlaced ? V_INTERLACE : 0);
390                switch (d_timings->misc) {
391                case 0: native_mode->Flags |= V_NHSYNC | V_NVSYNC; break;
392                case 1: native_mode->Flags |= V_PHSYNC | V_NVSYNC; break;
393                case 2: native_mode->Flags |= V_NHSYNC | V_PVSYNC; break;
394                case 3: native_mode->Flags |= V_PHSYNC | V_PVSYNC; break;
395                }
396                xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Panel infos found from DDC detailed: %dx%d\n",
397                           native_mode->PanelXRes, native_mode->PanelYRes);
398	    }
399	}
400    }
401
402    if (info->UseBiosDividers && native_mode->DotClock != 0)
403       return;
404
405    /* Search thru standard VESA modes from EDID */
406    for (j = 0; j < 8; j++) {
407	if ((native_mode->PanelXRes < ddc->timings2[j].hsize) &&
408	    (native_mode->PanelYRes < ddc->timings2[j].vsize)) {
409	    for (p = pScrn->monitor->Modes; p; p = p->next) {
410		if ((ddc->timings2[j].hsize == p->HDisplay) &&
411		    (ddc->timings2[j].vsize == p->VDisplay)) {
412		    float  refresh =
413			(float)p->Clock * 1000.0 / p->HTotal / p->VTotal;
414
415		    if (fabsf((float)ddc->timings2[j].refresh - refresh) < 1.0) {
416			/* Is this good enough? */
417			native_mode->PanelXRes  = ddc->timings2[j].hsize;
418			native_mode->PanelYRes  = ddc->timings2[j].vsize;
419			native_mode->HBlank     = p->HTotal - p->HDisplay;
420			native_mode->HOverPlus  = p->HSyncStart - p->HDisplay;
421			native_mode->HSyncWidth = p->HSyncEnd - p->HSyncStart;
422			native_mode->VBlank     = p->VTotal - p->VDisplay;
423			native_mode->VOverPlus  = p->VSyncStart - p->VDisplay;
424			native_mode->VSyncWidth = p->VSyncEnd - p->VSyncStart;
425			native_mode->DotClock   = p->Clock;
426                        native_mode->Flags      = p->Flags;
427                        xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Panel infos found from DDC VESA/EDID: %dx%d\n",
428                                   native_mode->PanelXRes, native_mode->PanelYRes);
429		    }
430		}
431	    }
432	}
433    }
434}
435
436static void
437radeon_add_common_modes(xf86OutputPtr output, DisplayModePtr modes)
438{
439    RADEONOutputPrivatePtr radeon_output = output->driver_private;
440    radeon_native_mode_ptr native_mode = &radeon_output->native_mode;
441    DisplayModePtr  last       = NULL;
442    DisplayModePtr  new        = NULL;
443    DisplayModePtr  first      = NULL;
444    int i;
445    /* Add some common sizes */
446    int widths[15]  = {640, 800, 1024, 1152, 1280, 1280, 1280, 1280, 1280, 1440, 1400, 1680, 1600, 1920, 1920};
447    int heights[15] = {480, 600,  768,  768,  720,  800,  854,  960, 1024,  900, 1050, 1050, 1200, 1080, 1200};
448
449    for (i = 0; i < 15; i++) {
450	if (radeon_output->active_device & (ATOM_DEVICE_LCD_SUPPORT)) {
451	    /* already added the native mode */
452	    if (widths[i] == native_mode->PanelXRes && heights[i] == native_mode->PanelYRes)
453		continue;
454
455	    /* Note: We allow all non-standard modes as long as they do not
456	     * exceed the native resolution of the panel.  Since these modes
457	     * need the internal RMX unit in the video chips (and there is
458	     * only one per card), this will only apply to the primary head.
459	     */
460	    if (widths[i] < 320 || widths[i] > native_mode->PanelXRes ||
461		heights[i] < 200 || heights[i] > native_mode->PanelYRes)
462		continue;
463	}
464
465	new = xf86CVTMode(widths[i], heights[i], 60.0, FALSE, FALSE);
466
467	new->type       = M_T_DRIVER;
468
469	new->next       = NULL;
470	new->prev       = last;
471
472	if (last) last->next = new;
473	last = new;
474	if (!first) first = new;
475    }
476
477    if (last) {
478	last->next   = NULL; //first;
479	first->prev  = NULL; //last;
480    }
481
482    xf86ModesAdd(modes, first);
483
484}
485
486DisplayModePtr
487RADEONProbeOutputModes(xf86OutputPtr output)
488{
489    RADEONOutputPrivatePtr radeon_output = output->driver_private;
490    ScrnInfoPtr pScrn = output->scrn;
491    RADEONInfoPtr info = RADEONPTR(pScrn);
492    DisplayModePtr	    modes = NULL;
493    AtomBiosArgRec atomBiosArg;
494    AtomBiosResult atomBiosResult;
495
496    if (output->status == XF86OutputStatusConnected) {
497	if (radeon_output->active_device & (ATOM_DEVICE_TV_SUPPORT)) {
498	    if (IS_AVIVO_VARIANT)
499		modes = RADEONATOMTVModes(output);
500	    else
501		modes = RADEONTVModes(output);
502	} else if (radeon_output->active_device & (ATOM_DEVICE_CV_SUPPORT)) {
503	    atomBiosResult = RHDAtomBiosFunc(pScrn, info->atomBIOS,
504					     ATOMBIOS_GET_CV_MODES, &atomBiosArg);
505	    if (atomBiosResult == ATOM_SUCCESS) {
506		modes = atomBiosArg.modes;
507	    }
508	} else {
509	    if (radeon_output->active_device & (ATOM_DEVICE_DFP_SUPPORT | ATOM_DEVICE_LCD_SUPPORT))
510		RADEONUpdatePanelSize(output);
511	    if (output->MonInfo)
512		modes = xf86OutputGetEDIDModes (output);
513#if defined(__powerpc__)
514	    if ((info->MacModel == RADEON_MAC_EMAC) &&
515		(radeon_output->active_device & ATOM_DEVICE_CRT1_SUPPORT) &&
516		(modes == NULL))
517		modes = RADEONeMacModes(output);
518#endif
519	    if (modes == NULL) {
520		if ((radeon_output->active_device & (ATOM_DEVICE_LCD_SUPPORT)) && info->IsAtomBios) {
521		    atomBiosResult = RHDAtomBiosFunc(pScrn,
522						     info->atomBIOS,
523						     ATOMBIOS_GET_PANEL_EDID, &atomBiosArg);
524		    if (atomBiosResult == ATOM_SUCCESS) {
525			output->MonInfo = xf86InterpretEDID(pScrn->scrnIndex,
526							    atomBiosArg.EDIDBlock);
527			modes = xf86OutputGetEDIDModes(output);
528		    }
529		}
530		if (modes == NULL) {
531		    if (radeon_output->active_device & (ATOM_DEVICE_LCD_SUPPORT))
532			modes = RADEONFPNativeMode(output);
533		    /* add the screen modes */
534		    if (modes == NULL)
535			RADEONAddScreenModes(output, &modes);
536		}
537	    }
538	}
539    }
540
541    if (radeon_output->active_device & (ATOM_DEVICE_LCD_SUPPORT))
542	radeon_add_common_modes(output, modes);
543
544    return modes;
545}
546
547