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