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