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