r128_output.c revision 6f68ce78
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#include <string.h> 34#include <stdio.h> 35 36#include "xf86.h" 37#include "xf86Modes.h" 38 39#ifdef HAVE_XEXTPROTO_71 40#include "X11/extensions/dpmsconst.h" 41#else 42#define DPMS_SERVER 43#include "X11/extensions/dpms.h" 44#endif 45 46#include "r128.h" 47#include "r128_probe.h" 48#include "r128_reg.h" 49#include "xf86Priv.h" 50#include "xf86Privstr.h" 51 52#ifdef __NetBSD__ 53#include <sys/time.h> 54#include <sys/ioctl.h> 55#include <dev/wscons/wsconsio.h> 56#endif 57 58 59static void R128ConnectorFindMonitor(ScrnInfoPtr pScrn, xf86OutputPtr output); 60 61static void r128_dpms(xf86OutputPtr output, int mode) 62{ 63 switch(mode) { 64 case DPMSModeOn: 65 R128DPMSSetOn(output); 66 break; 67 case DPMSModeStandby: 68 case DPMSModeSuspend: 69 case DPMSModeOff: 70 R128DPMSSetOff(output); 71 break; 72 } 73} 74 75static void r128_save(xf86OutputPtr output) 76{ 77} 78 79static void r128_restore(xf86OutputPtr output) 80{ 81} 82 83static int r128_mode_valid(xf86OutputPtr output, DisplayModePtr mode) 84{ 85 return MODE_OK; 86} 87 88static Bool r128_mode_fixup(xf86OutputPtr output, DisplayModePtr mode, DisplayModePtr adjusted_mode) 89{ 90 return TRUE; 91} 92 93static void r128_mode_prepare(xf86OutputPtr output) 94{ 95 r128_dpms(output, DPMSModeOff); 96} 97 98static void r128_mode_set(xf86OutputPtr output, DisplayModePtr mode, DisplayModePtr adjusted_mode) 99{ 100 ScrnInfoPtr pScrn = output->scrn; 101 R128InfoPtr info = R128PTR(pScrn); 102 R128OutputPrivatePtr r128_output = output->driver_private; 103 xf86CrtcPtr crtc = output->crtc; 104 R128CrtcPrivatePtr r128_crtc = crtc->driver_private; 105 106 if (r128_crtc->crtc_id == 0 && !info->isPro2) 107 R128InitRMXRegisters(&info->SavedReg, &info->ModeReg, output, adjusted_mode); 108 109 if (r128_output->MonType == MT_DFP) 110 R128InitFPRegisters(&info->SavedReg, &info->ModeReg, output); 111 else if (r128_output->MonType == MT_LCD) 112 R128InitLVDSRegisters(&info->SavedReg, &info->ModeReg, output); 113 else if (r128_output->MonType == MT_CRT) 114 R128InitDACRegisters(&info->SavedReg, &info->ModeReg, output); 115 116 if (r128_crtc->crtc_id == 0 && !info->isPro2) 117 R128RestoreRMXRegisters(pScrn, &info->ModeReg); 118 119 if (r128_output->MonType == MT_DFP) 120 R128RestoreFPRegisters(pScrn, &info->ModeReg); 121 else if (r128_output->MonType == MT_LCD) 122 R128RestoreLVDSRegisters(pScrn, &info->ModeReg); 123 else if (r128_output->MonType == MT_CRT) 124 R128RestoreDACRegisters(pScrn, &info->ModeReg); 125} 126 127static void r128_mode_commit(xf86OutputPtr output) 128{ 129 r128_dpms(output, DPMSModeOn); 130} 131 132static xf86OutputStatus r128_detect(xf86OutputPtr output) 133{ 134 ScrnInfoPtr pScrn = output->scrn; 135 R128OutputPrivatePtr r128_output = output->driver_private; 136 137 r128_output->MonType = MT_UNKNOWN; 138 R128ConnectorFindMonitor(pScrn, output); 139 140 if (r128_output->MonType == MT_UNKNOWN) { 141 output->subpixel_order = SubPixelUnknown; 142 return XF86OutputStatusUnknown; 143 } else if (r128_output->MonType == MT_NONE) { 144 output->subpixel_order = SubPixelUnknown; 145 return XF86OutputStatusDisconnected; 146 } else { 147 switch(r128_output->MonType) { 148 case MT_LCD: 149 case MT_DFP: 150 output->subpixel_order = SubPixelHorizontalRGB; 151 break; 152 default: 153 output->subpixel_order = SubPixelNone; 154 break; 155 } 156 157 return XF86OutputStatusConnected; 158 } 159} 160 161static DisplayModePtr r128_get_modes(xf86OutputPtr output) 162{ 163 DisplayModePtr modes; 164 modes = R128ProbeOutputModes(output); 165 return modes; 166} 167 168static void r128_destroy(xf86OutputPtr output) 169{ 170 if (output->driver_private) 171 free(output->driver_private); 172} 173 174static const xf86OutputFuncsRec r128_output_funcs = { 175 .dpms = r128_dpms, 176 .save = r128_save, 177 .restore = r128_restore, 178 .mode_valid = r128_mode_valid, 179 .mode_fixup = r128_mode_fixup, 180 .prepare = r128_mode_prepare, 181 .mode_set = r128_mode_set, 182 .commit = r128_mode_commit, 183 .detect = r128_detect, 184 .get_modes = r128_get_modes, 185 .destroy = r128_destroy, 186}; 187 188void R128DPMSSetOn(xf86OutputPtr output) 189{ 190 ScrnInfoPtr pScrn = output->scrn; 191 R128InfoPtr info = R128PTR(pScrn); 192 unsigned char *R128MMIO = info->MMIO; 193 R128OutputPrivatePtr r128_output = output->driver_private; 194 R128MonitorType MonType = r128_output->MonType; 195 R128SavePtr save = &info->ModeReg; 196 197 switch(MonType) { 198 case MT_LCD: 199#ifdef __NetBSD__ 200 if (info->HaveBacklightControl) { 201 struct wsdisplay_param p; 202 203 p.param = WSDISPLAYIO_PARAM_BACKLIGHT; 204 p.curval = 1; 205 ioctl(xf86Info.consoleFd, WSDISPLAYIO_SETPARAM, &p); 206 } else 207#endif 208 { 209 OUTREGP(R128_LVDS_GEN_CNTL, R128_LVDS_BLON, ~R128_LVDS_BLON); 210 usleep(r128_output->PanelPwrDly * 1000); 211 OUTREGP(R128_LVDS_GEN_CNTL, R128_LVDS_ON, ~R128_LVDS_ON); 212 } 213 save->lvds_gen_cntl |= (R128_LVDS_ON | R128_LVDS_BLON); 214 break; 215 case MT_DFP: 216 OUTREGP(R128_FP_GEN_CNTL, (R128_FP_FPON | R128_FP_TMDS_EN), ~(R128_FP_FPON | R128_FP_TMDS_EN)); 217 save->fp_gen_cntl |= (R128_FP_FPON | R128_FP_TMDS_EN); 218 break; 219 case MT_CRT: 220 OUTREGP(R128_CRTC_EXT_CNTL, R128_CRTC_CRT_ON, ~R128_CRTC_CRT_ON); 221 save->crtc_ext_cntl |= R128_CRTC_CRT_ON; 222 break; 223 default: 224 break; 225 } 226} 227 228void R128DPMSSetOff(xf86OutputPtr output) 229{ 230 ScrnInfoPtr pScrn = output->scrn; 231 R128InfoPtr info = R128PTR(pScrn); 232 unsigned char *R128MMIO = info->MMIO; 233 R128OutputPrivatePtr r128_output = output->driver_private; 234 R128MonitorType MonType = r128_output->MonType; 235 R128SavePtr save = &info->ModeReg; 236 237 switch(MonType) { 238 case MT_LCD: 239#ifdef __NetBSD__ 240 if (info->HaveBacklightControl) { 241 struct wsdisplay_param p; 242 243 p.param = WSDISPLAYIO_PARAM_BACKLIGHT; 244 p.curval = 0; 245 ioctl(xf86Info.consoleFd, WSDISPLAYIO_SETPARAM, &p); 246 } else 247#endif 248 { 249 OUTREGP(R128_LVDS_GEN_CNTL, 0, ~(R128_LVDS_BLON | R128_LVDS_ON)); 250 } 251 save->lvds_gen_cntl &= ~(R128_LVDS_BLON | R128_LVDS_ON); 252 break; 253 case MT_DFP: 254 OUTREGP(R128_FP_GEN_CNTL, 0, ~(R128_FP_FPON | R128_FP_TMDS_EN)); 255 save->fp_gen_cntl &= ~(R128_FP_FPON | R128_FP_TMDS_EN); 256 break; 257 case MT_CRT: 258 OUTREGP(R128_CRTC_EXT_CNTL, 0, ~(R128_CRTC_CRT_ON)); 259 save->crtc_ext_cntl &= ~(R128_CRTC_CRT_ON); 260 break; 261 default: 262 break; 263 } 264} 265 266static R128MonitorType R128DisplayDDCConnected(xf86OutputPtr output) 267{ 268 ScrnInfoPtr pScrn = output->scrn; 269 R128InfoPtr info = R128PTR(pScrn); 270 R128EntPtr pR128Ent = R128EntPriv(pScrn); 271 unsigned char *R128MMIO = info->MMIO; 272 R128OutputPrivatePtr r128_output = output->driver_private; 273 274 R128MonitorType MonType = MT_CRT; 275 xf86MonPtr *MonInfo = &output->MonInfo; 276 uint32_t mask1, mask2; 277 278 if (r128_output->type == OUTPUT_LVDS) { 279#ifdef __NetBSD__ 280 if (info->HaveWSDisplay) { 281 struct wsdisplayio_edid_info ei; 282 char *buffer; 283 xf86MonPtr tmp; 284 285 buffer = malloc(1024); 286 ei.edid_data = buffer; 287 ei.buffer_size = 1024; 288 if (ioctl(xf86Info.consoleFd, WSDISPLAYIO_GET_EDID, &ei) != -1) { 289 xf86Msg(X_INFO, "got %d bytes worth of EDID from wsdisplay\n", 290 ei.data_size); 291 tmp = xf86InterpretEEDID(pScrn->scrnIndex, buffer); 292 tmp->flags |= MONITOR_EDID_COMPLETE_RAWDATA; 293 *MonInfo = tmp; 294 xf86OutputSetEDID(output, tmp); 295 } else 296 free(buffer); 297 } 298#endif 299 return MT_LCD; 300 } else if (r128_output->type == OUTPUT_VGA) { 301 mask1 = R128_GPIO_MONID_MASK_1 | (pR128Ent->HasCRTC2 ? R128_GPIO_MONID_MASK_3 : R128_GPIO_MONID_MASK_2); 302 mask2 = R128_GPIO_MONID_A_1 | (pR128Ent->HasCRTC2 ? R128_GPIO_MONID_A_3 : R128_GPIO_MONID_A_2); 303 } else { 304 mask1 = R128_GPIO_MONID_MASK_0 | R128_GPIO_MONID_MASK_3; 305 mask2 = R128_GPIO_MONID_A_0 | R128_GPIO_MONID_A_3; 306 } 307 308 if (r128_output->pI2CBus) { 309 R128I2CBusPtr pR128I2CBus = &(r128_output->ddc_i2c); 310 311 /* XXX: Radeon does something here to appease old monitors. */ 312 OUTREG(pR128I2CBus->ddc_reg, INREG(pR128I2CBus->ddc_reg) | mask1); 313 OUTREG(pR128I2CBus->ddc_reg, INREG(pR128I2CBus->ddc_reg) & ~mask2); 314 *MonInfo = xf86DoEDID_DDC2(XF86_SCRN_ARG(pScrn), r128_output->pI2CBus); 315 } else { 316 xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "DDC2/I2C is not properly initialized\n"); 317 return MT_NONE; 318 } 319 320 if (*MonInfo) { 321 if (r128_output->type == OUTPUT_VGA) { 322 MonType = MT_CRT; 323 } else { 324 if ((*MonInfo)->rawData[0x14] & 0x80) 325 MonType = MT_DFP; 326 else 327 MonType = MT_CRT; 328 } 329 } 330 331 return MonType; 332} 333 334static void R128ConnectorFindMonitor(ScrnInfoPtr pScrn, xf86OutputPtr output) 335{ 336 R128OutputPrivatePtr r128_output = output->driver_private; 337 338 /* XXX: We should figure out how the DAC and BIOS scratch registers work 339 * to handle the non-DDC case. */ 340 if (r128_output->MonType == MT_UNKNOWN) 341 r128_output->MonType = R128DisplayDDCConnected(output); 342} 343 344DisplayModePtr R128ProbeOutputModes(xf86OutputPtr output) 345{ 346 ScrnInfoPtr pScrn = output->scrn; 347 R128OutputPrivatePtr r128_output = output->driver_private; 348 DisplayModePtr modes = NULL; 349 DisplayModePtr mode; 350 xf86MonPtr edid_mon; 351 352 if (r128_output->pI2CBus) { 353 edid_mon = xf86OutputGetEDID(output, r128_output->pI2CBus); 354 xf86OutputSetEDID(output, edid_mon); 355 } 356 modes = xf86OutputGetEDIDModes(output); 357 358 /* Letting this function return NULL would be a bad idea. With old cards 359 * like r128, users often specify a small resolution in order to get DRI. 360 * If the X server has to guess modes, the list it comes up with includes 361 * high resolutions. 362 */ 363 if (!modes) 364 modes = xf86GetDefaultModes(); 365 366 for (mode = modes; mode != NULL; mode = mode->next) { 367 if (r128_output->type == OUTPUT_DVI) { 368 if (mode->type & (M_T_DRIVER | M_T_PREFERRED)) { 369 r128_output->PanelXRes = mode->HDisplay; 370 r128_output->PanelYRes = mode->VDisplay; 371 } 372 } 373 374 xf86SetModeCrtc(mode, INTERLACE_HALVE_V); 375 if (mode->status == MODE_OK) 376 mode->status = R128DoValidMode(output, mode, MODECHECK_FINAL); 377 } 378 379 xf86ValidateModesUserConfig(pScrn, modes); 380 xf86PruneInvalidModes(pScrn, &modes, FALSE); 381 382 return modes; 383} 384 385static xf86OutputPtr R128OutputCreate(ScrnInfoPtr pScrn, const char *name, int i) 386{ 387 char buf[32]; 388 sprintf(buf, name, i); 389 return xf86OutputCreate(pScrn, &r128_output_funcs, buf); 390} 391 392static void R128I2CGetBits(I2CBusPtr b, int *Clock, int *data) 393{ 394 ScrnInfoPtr pScrn = xf86Screens[b->scrnIndex]; 395 R128InfoPtr info = R128PTR(pScrn); 396 unsigned long val; 397 unsigned char *R128MMIO = info->MMIO; 398 R128I2CBusPtr pR128I2CBus = b->DriverPrivate.ptr; 399 400 /* Get the result. */ 401 val = INREG(pR128I2CBus->ddc_reg); 402 *Clock = (val & pR128I2CBus->get_clk_mask) != 0; 403 *data = (val & pR128I2CBus->get_data_mask) != 0; 404} 405 406static void R128I2CPutBits(I2CBusPtr b, int Clock, int data) 407{ 408 ScrnInfoPtr pScrn = xf86Screens[b->scrnIndex]; 409 R128InfoPtr info = R128PTR(pScrn); 410 unsigned long val; 411 unsigned char *R128MMIO = info->MMIO; 412 R128I2CBusPtr pR128I2CBus = b->DriverPrivate.ptr; 413 414 val = INREG(pR128I2CBus->ddc_reg) 415 & ~(uint32_t)(pR128I2CBus->put_clk_mask | pR128I2CBus->put_data_mask); 416 val |= (Clock ? 0 : pR128I2CBus->put_clk_mask); 417 val |= (data ? 0 : pR128I2CBus->put_data_mask); 418 OUTREG(pR128I2CBus->ddc_reg, val); 419} 420 421static Bool R128I2CInit(xf86OutputPtr output, I2CBusPtr *bus_ptr, char *name) 422{ 423 ScrnInfoPtr pScrn = output->scrn; 424 R128OutputPrivatePtr r128_output = output->driver_private; 425 R128I2CBusPtr pR128I2CBus = &(r128_output->ddc_i2c); 426 I2CBusPtr pI2CBus; 427 428 pI2CBus = xf86CreateI2CBusRec(); 429 if(!pI2CBus) return FALSE; 430 431 pI2CBus->BusName = name; 432 pI2CBus->scrnIndex = pScrn->scrnIndex; 433 pI2CBus->I2CPutBits = R128I2CPutBits; 434 pI2CBus->I2CGetBits = R128I2CGetBits; 435 pI2CBus->AcknTimeout = 5; 436 437 pI2CBus->DriverPrivate.ptr = (pointer)pR128I2CBus; 438 if (!xf86I2CBusInit(pI2CBus)) return FALSE; 439 440 *bus_ptr = pI2CBus; 441 return TRUE; 442} 443 444void R128SetupGenericConnectors(ScrnInfoPtr pScrn, R128OutputType *otypes) 445{ 446 R128InfoPtr info = R128PTR(pScrn); 447 R128EntPtr pR128Ent = R128EntPriv(pScrn); 448 449 if (!pR128Ent->HasCRTC2 && !info->isDFP) { 450 otypes[0] = OUTPUT_VGA; 451 otypes[1] = OUTPUT_NONE; 452 return; 453 } else if (!pR128Ent->HasCRTC2) { 454 otypes[0] = OUTPUT_DVI; 455 otypes[1] = OUTPUT_NONE; 456 return; 457 } 458 459 otypes[0] = OUTPUT_LVDS; 460 otypes[1] = OUTPUT_VGA; 461} 462 463void R128GetConnectorInfoFromBIOS(ScrnInfoPtr pScrn, R128OutputType *otypes) 464{ 465 R128InfoPtr info = R128PTR(pScrn); 466 uint16_t bios_header; 467 int offset; 468 469 /* XXX: Currently, this function only finds VGA ports misidentified as DVI. */ 470 if (!info->VBIOS || otypes[0] != OUTPUT_DVI) return; 471 472 bios_header = R128_BIOS16(0x48); 473 offset = R128_BIOS16(bios_header + 0x60); 474 475 if (offset) { 476 otypes[0] = OUTPUT_VGA; 477 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Found CRT table, assuming VGA connector\n"); 478 } 479} 480 481Bool R128SetupConnectors(ScrnInfoPtr pScrn) 482{ 483 R128InfoPtr info = R128PTR(pScrn); 484 R128EntPtr pR128Ent = R128EntPriv(pScrn); 485 486 R128OutputType otypes[R128_MAX_BIOS_CONNECTOR]; 487 xf86OutputPtr output; 488 int num_vga = 0; 489 int num_dvi = 0; 490 int i; 491 492 R128SetupGenericConnectors(pScrn, otypes); 493 R128GetConnectorInfoFromBIOS(pScrn, otypes); 494 495 for (i = 0; i < R128_MAX_BIOS_CONNECTOR; i++) { 496 if (otypes[i] == OUTPUT_VGA) 497 num_vga++; 498 else if (otypes[i] == OUTPUT_DVI) 499 num_dvi++; 500 } 501 502 for (i = 0; i < R128_MAX_BIOS_CONNECTOR; i++) { 503 if (otypes[i] == OUTPUT_NONE) continue; 504 505 R128I2CBusRec i2c; 506 R128OutputPrivatePtr r128_output; 507 508 r128_output = xnfcalloc(sizeof(R128OutputPrivateRec), 1); 509 if (!r128_output) return FALSE; 510 511 r128_output->MonType = MT_UNKNOWN; 512 r128_output->type = otypes[i]; 513 r128_output->num = i; 514 515 if (otypes[i] == OUTPUT_LVDS) { 516 output = R128OutputCreate(pScrn, "LVDS", 0); 517 } else if (otypes[i] == OUTPUT_VGA) { 518 output = R128OutputCreate(pScrn, "VGA-%d", --num_vga); 519 } else { 520 output = R128OutputCreate(pScrn, "DVI-%d", --num_dvi); 521 } 522 523 if (!output) return FALSE; 524 output->interlaceAllowed = TRUE; 525 output->doubleScanAllowed = TRUE; 526 output->driver_private = r128_output; 527 output->possible_clones = 0; 528 if (otypes[i] == OUTPUT_LVDS || !pR128Ent->HasCRTC2) 529 output->possible_crtcs = 1; 530 else 531 output->possible_crtcs = 2; 532 533 if (otypes[i] != OUTPUT_LVDS && info->DDC) { 534 i2c.ddc_reg = R128_GPIO_MONID; 535 if (otypes[i] == OUTPUT_VGA && !pR128Ent->HasCRTC2) { 536 i2c.put_clk_mask = R128_GPIO_MONID_EN_2; 537 i2c.get_clk_mask = R128_GPIO_MONID_Y_2; 538 } else { 539 i2c.put_clk_mask = R128_GPIO_MONID_EN_3; 540 i2c.get_clk_mask = R128_GPIO_MONID_Y_3; 541 } 542 if (otypes[i] == OUTPUT_VGA) { 543 i2c.put_data_mask = R128_GPIO_MONID_EN_1; 544 i2c.get_data_mask = R128_GPIO_MONID_Y_1; 545 } else { 546 i2c.put_data_mask = R128_GPIO_MONID_EN_0; 547 i2c.get_data_mask = R128_GPIO_MONID_Y_0; 548 } 549 r128_output->ddc_i2c = i2c; 550 R128I2CInit(output, &r128_output->pI2CBus, output->name); 551 } 552 553 if (otypes[i] == OUTPUT_LVDS) 554 R128GetPanelInfoFromBIOS(output); 555 } 556 557 return TRUE; 558} 559