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