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