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