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