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