1/* 2 * Copyright 2016 Kevin Brace 3 * Copyright 2016 The OpenChrome Project 4 * [https://www.freedesktop.org/wiki/Openchrome] 5 * Copyright 2014 SHS SERVICES GmbH 6 * Copyright 2006-2009 Luc Verhaegen. 7 * 8 * Permission is hereby granted, free of charge, to any person obtaining a 9 * copy of this software and associated documentation files (the "Software"), 10 * to deal in the Software without restriction, including without limitation 11 * the rights to use, copy, modify, merge, publish, distribute, sub license, 12 * and/or sell copies of the Software, and to permit persons to whom the 13 * Software is furnished to do so, 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 portions 17 * of the Software. 18 * 19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 22 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 * DEALINGS IN THE SOFTWARE. 26 */ 27 28#ifdef HAVE_CONFIG_H 29#include "config.h" 30#endif 31 32#include "via_driver.h" 33#include "via_vt1632.h" 34 35static void 36viaVT1632DumpRegisters(ScrnInfoPtr pScrn, I2CDevPtr pDev) 37{ 38 int i; 39 CARD8 tmp; 40 41 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 42 "Entered viaVT1632DumpRegisters.\n")); 43 44 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Dumping VT1632(A) registers.\n")); 45 for (i = 0; i <= 0x0f; i++) { 46 xf86I2CReadByte(pDev, i, &tmp); 47 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, "0x%02x: 0x%02x\n", i, tmp)); 48 } 49 50 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 51 "Exiting viaVT1632DumpRegisters.\n")); 52} 53 54static void 55viaVT1632InitRegisters(ScrnInfoPtr pScrn, I2CDevPtr pDev) 56{ 57 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 58 "Entered viaVT1632InitRegisters.\n")); 59 60 /* For Wyse C00X VX855 chipset DVP1 (Digital Video Port 1), use 61 * 12-bit mode with dual edge transfer, along with rising edge 62 * data capture first mode. This is likely true for CX700, VX700, 63 * VX800, and VX900 chipsets as well. */ 64 xf86I2CWriteByte(pDev, 0x08, 65 VIA_VT1632_VEN | VIA_VT1632_HEN | 66 VIA_VT1632_DSEL | 67 VIA_VT1632_EDGE | VIA_VT1632_PDB); 68 69 /* Route receiver detect bit (Offset 0x09[2]) as the output of 70 * MSEN pin. */ 71 xf86I2CWriteByte(pDev, 0x09, 0x20); 72 73 /* Turning on deskew feature caused screen display issues. 74 * This was observed with Wyse C00X. */ 75 xf86I2CWriteByte(pDev, 0x0A, 0x00); 76 77 /* While VIA Technologies VT1632A datasheet insists on setting this 78 * register to 0x89 as the recommended setting, in practice, this 79 * leads to a blank screen on the display with Wyse C00X. According to 80 * Silicon Image SiI 164 datasheet (VT1632A is a pin and mostly 81 * register compatible chip), offset 0x0C is for PLL filter enable, 82 * PLL filter setting, and continuous SYNC enable bits. All of these are 83 * turned off for proper operation. */ 84 xf86I2CWriteByte(pDev, 0x0C, 0x00); 85 86 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 87 "Exiting viaVT1632InitRegisters.\n")); 88} 89 90/* 91 * Returns TMDS receiver detection state for VIA Technologies VT1632 92 * external TMDS transmitter. 93 */ 94static Bool 95viaVT1632Sense(ScrnInfoPtr pScrn, I2CDevPtr pDev) 96{ 97 CARD8 tmp; 98 Bool receiverDetected = FALSE; 99 100 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 101 "Entered viaVT1632Sense.\n")); 102 103 xf86I2CReadByte(pDev, 0x09, &tmp); 104 if (tmp & 0x04) { 105 receiverDetected = TRUE; 106 } 107 108 xf86DrvMsg(pScrn->scrnIndex, X_INFO, 109 "VT1632 %s a TMDS receiver.\n", 110 receiverDetected ? "detected" : "did not detect"); 111 112 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 113 "Exiting viaVT1632Sense.\n")); 114 return receiverDetected; 115} 116 117static void 118viaVT1632Power(ScrnInfoPtr pScrn, I2CDevPtr pDev, Bool powerState) 119{ 120 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 121 "Entered viaVT1632Power.\n")); 122 123 xf86I2CMaskByte(pDev, 0x08, powerState ? 0x01 : 0x00, 0x01); 124 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "VT1632 (DVI) Power: %s\n", 125 powerState ? "On" : "Off"); 126 127 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 128 "Exiting viaVT1632Power.\n")); 129} 130 131static void 132viaVT1632SaveRegisters(ScrnInfoPtr pScrn, I2CDevPtr pDev, 133 viaVT1632RecPtr pVIAVT1632Rec) 134{ 135 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 136 "Entered viaVT1632SaveRegisters.\n")); 137 138 xf86DrvMsg(pScrn->scrnIndex, X_INFO, 139 "Saving VT1632 registers.\n"); 140 xf86I2CReadByte(pDev, 0x08, &pVIAVT1632Rec->Register08); 141 xf86I2CReadByte(pDev, 0x09, &pVIAVT1632Rec->Register09); 142 xf86I2CReadByte(pDev, 0x0A, &pVIAVT1632Rec->Register0A); 143 xf86I2CReadByte(pDev, 0x0C, &pVIAVT1632Rec->Register0C); 144 145 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 146 "Exiting viaVT1632SaveRegisters.\n")); 147} 148 149static void 150viaVT1632RestoreRegisters(ScrnInfoPtr pScrn, I2CDevPtr pDev, 151 viaVT1632RecPtr pVIAVT1632Rec) 152{ 153 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 154 "Entered viaVT1632RestoreRegisters.\n")); 155 156 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 157 "Restoring VT1632 registers.\n")); 158 xf86I2CWriteByte(pDev, 0x08, pVIAVT1632Rec->Register08); 159 xf86I2CWriteByte(pDev, 0x09, pVIAVT1632Rec->Register09); 160 xf86I2CWriteByte(pDev, 0x0A, pVIAVT1632Rec->Register0A); 161 xf86I2CWriteByte(pDev, 0x0C, pVIAVT1632Rec->Register0C); 162 163 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 164 "Exiting viaVT1632RestoreRegisters.\n")); 165} 166 167static int 168viaVT1632CheckModeValidity(xf86OutputPtr output, DisplayModePtr pMode) 169{ 170 ScrnInfoPtr pScrn = output->scrn; 171 viaVT1632RecPtr pVIAVT1632Rec = output->driver_private; 172 int status = MODE_OK; 173 174 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 175 "Entered viaVT1632CheckModeValidity.\n")); 176 177 if (pMode->Clock < pVIAVT1632Rec->DotclockMin) { 178 status = MODE_CLOCK_LOW; 179 goto exit; 180 } 181 182 if (pMode->Clock > pVIAVT1632Rec->DotclockMax) { 183 status = MODE_CLOCK_HIGH; 184 } 185 186exit: 187 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 188 "Exiting viaVT1632CheckModeValidity.\n")); 189 return status; 190} 191 192static void 193via_vt1632_create_resources(xf86OutputPtr output) 194{ 195} 196 197static void 198via_vt1632_dpms(xf86OutputPtr output, int mode) 199{ 200 ScrnInfoPtr pScrn = output->scrn; 201 viaVT1632RecPtr pVIAVT1632Rec = output->driver_private; 202 203 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 204 "Entered via_vt1632_dpms.\n")); 205 206 switch (mode) { 207 case DPMSModeOn: 208 viaVT1632Power(pScrn, pVIAVT1632Rec->VT1632I2CDev, TRUE); 209 break; 210 case DPMSModeStandby: 211 case DPMSModeSuspend: 212 case DPMSModeOff: 213 viaVT1632Power(pScrn, pVIAVT1632Rec->VT1632I2CDev, FALSE); 214 break; 215 default: 216 break; 217 } 218 219 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 220 "Exiting via_vt1632_dpms.\n")); 221} 222 223static void 224via_vt1632_save(xf86OutputPtr output) 225{ 226 ScrnInfoPtr pScrn = output->scrn; 227 viaVT1632RecPtr pVIAVT1632Rec = output->driver_private; 228 229 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 230 "Entered via_vt1632_save.\n")); 231 232 viaVT1632SaveRegisters(pScrn, pVIAVT1632Rec->VT1632I2CDev, pVIAVT1632Rec); 233 234 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 235 "Exiting via_vt1632_save.\n")); 236} 237 238static void 239via_vt1632_restore(xf86OutputPtr output) 240{ 241 ScrnInfoPtr pScrn = output->scrn; 242 viaVT1632RecPtr pVIAVT1632Rec = output->driver_private; 243 244 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 245 "Entered via_vt1632_restore.\n")); 246 247 viaVT1632RestoreRegisters(pScrn, pVIAVT1632Rec->VT1632I2CDev, 248 pVIAVT1632Rec); 249 250 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 251 "Exiting via_vt1632_restore.\n")); 252} 253 254static int 255via_vt1632_mode_valid(xf86OutputPtr output, DisplayModePtr pMode) 256{ 257 return viaVT1632CheckModeValidity(output, pMode); 258} 259 260static Bool 261via_vt1632_mode_fixup(xf86OutputPtr output, DisplayModePtr mode, 262 DisplayModePtr adjusted_mode) 263{ 264 return TRUE; 265} 266 267static void 268via_vt1632_prepare(xf86OutputPtr output) 269{ 270} 271 272static void 273via_vt1632_commit(xf86OutputPtr output) 274{ 275} 276 277static void 278via_vt1632_mode_set(xf86OutputPtr output, DisplayModePtr mode, 279 DisplayModePtr adjusted_mode) 280{ 281 ScrnInfoPtr pScrn = output->scrn; 282 drmmode_crtc_private_ptr iga = output->crtc->driver_private; 283 viaVT1632RecPtr pVIAVT1632Rec = output->driver_private; 284 285 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 286 "Entered via_vt1632_mode_set.\n")); 287 288 if (output->crtc) { 289 viaExtTMDSSetClockDriveStrength(pScrn, 0x03); 290 viaExtTMDSSetDataDriveStrength(pScrn, 0x03); 291 viaExtTMDSEnableIOPads(pScrn, 0x03); 292 293 viaVT1632DumpRegisters(pScrn, pVIAVT1632Rec->VT1632I2CDev); 294 viaVT1632InitRegisters(pScrn, pVIAVT1632Rec->VT1632I2CDev); 295 viaVT1632DumpRegisters(pScrn, pVIAVT1632Rec->VT1632I2CDev); 296 297 viaExtTMDSSetDisplaySource(pScrn, iga->index ? 0x01 : 0x00); 298 } 299 300 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 301 "Exiting via_vt1632_mode_set.\n")); 302} 303 304static xf86OutputStatus 305via_vt1632_detect(xf86OutputPtr output) 306{ 307 xf86MonPtr mon; 308 xf86OutputStatus status = XF86OutputStatusDisconnected; 309 ScrnInfoPtr pScrn = output->scrn; 310 viaVT1632RecPtr pVIAVT1632Rec = output->driver_private; 311 312 /* Check for the DVI presence via VT1632 first before accessing 313 * I2C bus. */ 314 if (viaVT1632Sense(pScrn, pVIAVT1632Rec->VT1632I2CDev)) { 315 316 /* Since DVI presence was established, access the I2C bus 317 * assigned to DVI. */ 318 mon = xf86OutputGetEDID(output, pVIAVT1632Rec->VT1632I2CDev->pI2CBus); 319 320 /* Is the interface type digital? */ 321 if (mon && DIGITAL(mon->features.input_type)) { 322 status = XF86OutputStatusConnected; 323 xf86DrvMsg(pScrn->scrnIndex, X_PROBED, 324 "Detected a monitor connected to DVI.\n"); 325 xf86OutputSetEDID(output, mon); 326 } else { 327 xf86DrvMsg(pScrn->scrnIndex, X_PROBED, 328 "Could not obtain EDID from a monitor " 329 "connected to DVI.\n"); 330 } 331 } 332 333 return status; 334} 335 336#ifdef RANDR_12_INTERFACE 337static Bool 338via_vt1632_set_property(xf86OutputPtr output, Atom property, 339 RRPropertyValuePtr value) 340{ 341 return TRUE; 342} 343 344static Bool 345via_vt1632_get_property(xf86OutputPtr output, Atom property) 346{ 347 return FALSE; 348} 349#endif 350 351static void 352via_vt1632_destroy(xf86OutputPtr output) 353{ 354} 355 356const xf86OutputFuncsRec via_vt1632_funcs = { 357 .create_resources = via_vt1632_create_resources, 358 .dpms = via_vt1632_dpms, 359 .save = via_vt1632_save, 360 .restore = via_vt1632_restore, 361 .mode_valid = via_vt1632_mode_valid, 362 .mode_fixup = via_vt1632_mode_fixup, 363 .prepare = via_vt1632_prepare, 364 .commit = via_vt1632_commit, 365 .mode_set = via_vt1632_mode_set, 366 .detect = via_vt1632_detect, 367 .get_modes = xf86OutputGetEDIDModes, 368#ifdef RANDR_12_INTERFACE 369 .set_property = via_vt1632_set_property, 370#endif 371#ifdef RANDR_13_INTERFACE 372 .get_property = via_vt1632_get_property, 373#endif 374 .destroy = via_vt1632_destroy, 375}; 376 377Bool 378viaVT1632Init(ScrnInfoPtr pScrn, I2CBusPtr pI2CBus) 379{ 380 xf86OutputPtr output; 381 VIAPtr pVia = VIAPTR(pScrn); 382 viaVT1632RecPtr pVIAVT1632Rec = NULL; 383 I2CDevPtr pI2CDevice = NULL; 384 I2CSlaveAddr i2cAddr = 0x10; 385 CARD8 buf; 386 CARD16 vendorID, deviceID; 387 Bool status = FALSE; 388 char outputNameBuffer[32]; 389 390 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 391 "Entered viaVT1632Init.\n")); 392 393 if (!xf86I2CProbeAddress(pI2CBus, i2cAddr)) { 394 xf86DrvMsg(pScrn->scrnIndex, X_PROBED, 395 "I2C device not found.\n"); 396 goto exit; 397 } 398 399 pI2CDevice = xf86CreateI2CDevRec(); 400 if (!pI2CDevice) { 401 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, 402 "Failed to create an I2C bus device record.\n"); 403 goto exit; 404 } 405 406 pI2CDevice->DevName = "VT1632"; 407 pI2CDevice->SlaveAddr = i2cAddr; 408 pI2CDevice->pI2CBus = pI2CBus; 409 if (!xf86I2CDevInit(pI2CDevice)) { 410 xf86DestroyI2CDevRec(pI2CDevice, TRUE); 411 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, 412 "Failed to initialize a device on I2C bus.\n"); 413 goto exit; 414 } 415 416 xf86I2CReadByte(pI2CDevice, 0, &buf); 417 vendorID = buf; 418 xf86I2CReadByte(pI2CDevice, 1, &buf); 419 vendorID |= buf << 8; 420 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 421 "Vendor ID: 0x%04x\n", vendorID)); 422 423 xf86I2CReadByte(pI2CDevice, 2, &buf); 424 deviceID = buf; 425 xf86I2CReadByte(pI2CDevice, 3, &buf); 426 deviceID |= buf << 8; 427 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 428 "Device ID: 0x%04x\n", deviceID)); 429 430 if ((vendorID != 0x1106) || (deviceID != 0x3192)) { 431 xf86DestroyI2CDevRec(pI2CDevice, TRUE); 432 xf86DrvMsg(pScrn->scrnIndex, X_PROBED, 433 "VT1632 external TMDS transmitter not detected.\n"); 434 goto exit; 435 } 436 437 xf86DrvMsg(pScrn->scrnIndex, X_PROBED, 438 "VT1632 external TMDS transmitter detected.\n"); 439 440 pVIAVT1632Rec = xnfcalloc(1, sizeof(viaVT1632Rec)); 441 if (!pVIAVT1632Rec) { 442 xf86DestroyI2CDevRec(pI2CDevice, TRUE); 443 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, 444 "Failed to allocate working storage for VT1632.\n"); 445 goto exit; 446 } 447 448 // Remembering which I2C bus is used for VT1632. 449 pVIAVT1632Rec->VT1632I2CDev = pI2CDevice; 450 451 xf86I2CReadByte(pI2CDevice, 0x06, &buf); 452 pVIAVT1632Rec->DotclockMin = buf * 1000; 453 454 xf86I2CReadByte(pI2CDevice, 0x07, &buf); 455 pVIAVT1632Rec->DotclockMax = (buf + 65) * 1000; 456 457 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Supported VT1632 Dot Clock Range: " 458 "%d to %d MHz\n", 459 pVIAVT1632Rec->DotclockMin / 1000, 460 pVIAVT1632Rec->DotclockMax / 1000); 461 462 /* The code to dynamically designate the particular DVI (i.e., DVI-1, 463 * DVI-2, etc.) for xrandr was borrowed from xf86-video-r128 DDX. */ 464 sprintf(outputNameBuffer, "DVI-%d", (pVia->numberDVI + 1)); 465 output = xf86OutputCreate(pScrn, &via_vt1632_funcs, outputNameBuffer); 466 if (!output) { 467 free(pVIAVT1632Rec); 468 xf86DestroyI2CDevRec(pI2CDevice, TRUE); 469 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, 470 "Failed to allocate X Server display output record for " 471 "VT1632.\n"); 472 goto exit; 473 } 474 475 output->driver_private = pVIAVT1632Rec; 476 477 /* Since there are two (2) display controllers registered with the 478 * X.Org Server and both IGA1 and IGA2 can handle DVI without any 479 * limitations, possible_crtcs should be set to 0x3 (0b11) so that 480 * either display controller can get assigned to handle DVI. */ 481 output->possible_crtcs = (1 << 1) | (1 << 0); 482 483 output->possible_clones = 0; 484 output->interlaceAllowed = FALSE; 485 output->doubleScanAllowed = FALSE; 486 487 viaVT1632DumpRegisters(pScrn, pI2CDevice); 488 489 pVia->numberDVI++; 490 status = TRUE; 491exit: 492 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 493 "Exiting viaVT1632Init.\n")); 494 return status; 495} 496