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