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_sii164.h" 34 35static void 36viaSiI164DumpRegisters(ScrnInfoPtr pScrn, I2CDevPtr pDev) 37{ 38 int i; 39 CARD8 tmp; 40 41 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 42 "Entered viaSiI164DumpRegisters.\n")); 43 44 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, "SiI 164: dumping registers:\n")); 45 for (i = 0; i <= 0x0f; i++) { 46 xf86I2CReadByte(pDev, i, &tmp); 47 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, "SiI 164: 0x%02x: 0x%02x\n", i, tmp)); 48 } 49 50 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 51 "Exiting viaSiI164DumpRegisters.\n")); 52} 53 54static void 55viaSiI164InitRegisters(ScrnInfoPtr pScrn, I2CDevPtr pDev) 56{ 57 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 58 "Entered viaSiI164InitRegisters.\n")); 59 60 xf86I2CWriteByte(pDev, 0x08, 61 VIA_SII164_VEN | VIA_SII164_HEN | 62 VIA_SII164_DSEL | VIA_SII164_EDGE | VIA_SII164_PDB); 63 64 /* Route receiver detect bit (Offset 0x09[2]) as the output of 65 * MSEN pin. */ 66 xf86I2CWriteByte(pDev, 0x09, 0x20); 67 68 xf86I2CWriteByte(pDev, 0x0A, 0x90); 69 70 xf86I2CWriteByte(pDev, 0x0C, 0x89); 71 72 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 73 "Exiting viaSiI164InitRegisters.\n")); 74} 75 76/* 77 * Returns TMDS receiver detection state for Silicon Image SiI 164 78 * external TMDS transmitter. 79 */ 80static Bool 81viaSiI164Sense(ScrnInfoPtr pScrn, I2CDevPtr pDev) 82{ 83 CARD8 tmp; 84 Bool receiverDetected = FALSE; 85 86 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 87 "Entered viaSiI164Sense.\n")); 88 89 xf86I2CReadByte(pDev, 0x09, &tmp); 90 if (tmp & 0x04) { 91 receiverDetected = TRUE; 92 } 93 94 xf86DrvMsg(pScrn->scrnIndex, X_INFO, 95 "SiI 164 %s a TMDS receiver.\n", 96 receiverDetected ? "detected" : "did not detect"); 97 98 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 99 "Exiting viaSiI164Sense.\n")); 100 return receiverDetected; 101} 102 103static void 104viaSiI164Power(ScrnInfoPtr pScrn, I2CDevPtr pDev, Bool powerState) 105{ 106 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 107 "Entered viaSiI164Power.\n")); 108 109 xf86I2CMaskByte(pDev, 0x08, powerState ? 0x01 : 0x00, 0x01); 110 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "SiI 164 (DVI) Power: %s\n", 111 powerState ? "On" : "Off"); 112 113 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 114 "Exiting viaSiI164Power.\n")); 115} 116 117static void 118viaSiI164SaveRegisters(ScrnInfoPtr pScrn, I2CDevPtr pDev, 119 viaSiI164RecPtr pSiI164Rec) 120{ 121 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 122 "Entered viaSiI164SaveRegisters.\n")); 123 124 xf86DrvMsg(pScrn->scrnIndex, X_INFO, 125 "Saving SiI 164 registers.\n"); 126 xf86I2CReadByte(pDev, 0x08, &pSiI164Rec->Register08); 127 xf86I2CReadByte(pDev, 0x09, &pSiI164Rec->Register09); 128 xf86I2CReadByte(pDev, 0x0A, &pSiI164Rec->Register0A); 129 xf86I2CReadByte(pDev, 0x0C, &pSiI164Rec->Register0C); 130 131 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 132 "Exiting viaSiI164SaveRegisters.\n")); 133} 134 135static void 136viaSiI164RestoreRegisters(ScrnInfoPtr pScrn, I2CDevPtr pDev, 137 viaSiI164RecPtr pSiI164Rec) 138{ 139 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 140 "Entered viaSiI164RestoreRegisters.\n")); 141 142 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 143 "Restoring SiI 164 registers.\n")); 144 xf86I2CWriteByte(pDev, 0x08, pSiI164Rec->Register08); 145 xf86I2CWriteByte(pDev, 0x09, pSiI164Rec->Register09); 146 xf86I2CWriteByte(pDev, 0x0A, pSiI164Rec->Register0A); 147 xf86I2CWriteByte(pDev, 0x0C, pSiI164Rec->Register0C); 148 149 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 150 "Exiting viaSiI164RestoreRegisters.\n")); 151} 152 153static int 154viaSiI164CheckModeValidity(xf86OutputPtr output, DisplayModePtr pMode) 155{ 156 ScrnInfoPtr pScrn = output->scrn; 157 viaSiI164RecPtr pSiI164Rec = output->driver_private; 158 int status = MODE_OK; 159 160 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 161 "Entered viaSiI164CheckModeValidity.\n")); 162 163 if (pMode->Clock < pSiI164Rec->DotclockMin) { 164 status = MODE_CLOCK_LOW; 165 goto exit; 166 } 167 168 if (pMode->Clock > pSiI164Rec->DotclockMax) { 169 status = MODE_CLOCK_HIGH; 170 } 171 172exit: 173 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 174 "Exiting viaSiI164CheckModeValidity.\n")); 175 return status; 176} 177 178static void 179via_sii164_create_resources(xf86OutputPtr output) 180{ 181} 182 183static void 184via_sii164_dpms(xf86OutputPtr output, int mode) 185{ 186 ScrnInfoPtr pScrn = output->scrn; 187 viaSiI164RecPtr pSiI164Rec = output->driver_private; 188 189 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 190 "Entered via_sii164_dpms.\n")); 191 192 switch (mode) { 193 case DPMSModeOn: 194 viaSiI164Power(pScrn, pSiI164Rec->SiI164I2CDev, TRUE); 195 break; 196 case DPMSModeStandby: 197 case DPMSModeSuspend: 198 case DPMSModeOff: 199 viaSiI164Power(pScrn, pSiI164Rec->SiI164I2CDev, FALSE); 200 break; 201 default: 202 break; 203 } 204 205 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 206 "Exiting via_sii164_dpms.\n")); 207} 208 209static void 210via_sii164_save(xf86OutputPtr output) 211{ 212 ScrnInfoPtr pScrn = output->scrn; 213 viaSiI164RecPtr pSiI164Rec = output->driver_private; 214 215 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 216 "Entered via_sii164_save.\n")); 217 218 viaSiI164SaveRegisters(pScrn, pSiI164Rec->SiI164I2CDev, pSiI164Rec); 219 220 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 221 "Exiting via_sii164_save.\n")); 222} 223 224static void 225via_sii164_restore(xf86OutputPtr output) 226{ 227 ScrnInfoPtr pScrn = output->scrn; 228 viaSiI164RecPtr pSiI164Rec = output->driver_private; 229 230 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 231 "Entered via_sii164_restore.\n")); 232 233 viaSiI164RestoreRegisters(pScrn, pSiI164Rec->SiI164I2CDev, 234 pSiI164Rec); 235 236 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 237 "Exiting via_sii164_restore.\n")); 238} 239 240static int 241via_sii164_mode_valid(xf86OutputPtr output, DisplayModePtr pMode) 242{ 243 return viaSiI164CheckModeValidity(output, pMode); 244} 245 246static Bool 247via_sii164_mode_fixup(xf86OutputPtr output, DisplayModePtr mode, 248 DisplayModePtr adjusted_mode) 249{ 250 return TRUE; 251} 252 253static void 254via_sii164_prepare(xf86OutputPtr output) 255{ 256} 257 258static void 259via_sii164_commit(xf86OutputPtr output) 260{ 261} 262 263static void 264via_sii164_mode_set(xf86OutputPtr output, DisplayModePtr mode, 265 DisplayModePtr adjusted_mode) 266{ 267 ScrnInfoPtr pScrn = output->scrn; 268 drmmode_crtc_private_ptr iga = output->crtc->driver_private; 269 viaSiI164RecPtr pSiI164Rec = output->driver_private; 270 271 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 272 "Entered via_sii164_mode_set.\n")); 273 274 if (output->crtc) { 275 viaExtTMDSSetClockDriveStrength(pScrn, 0x03); 276 viaExtTMDSSetDataDriveStrength(pScrn, 0x03); 277 viaExtTMDSEnableIOPads(pScrn, 0x03); 278 279 viaSiI164DumpRegisters(pScrn, pSiI164Rec->SiI164I2CDev); 280 viaSiI164InitRegisters(pScrn, pSiI164Rec->SiI164I2CDev); 281 viaSiI164DumpRegisters(pScrn, pSiI164Rec->SiI164I2CDev); 282 283 viaExtTMDSSetDisplaySource(pScrn, iga->index ? 0x01 : 0x00); 284 } 285 286 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 287 "Exiting via_sii164_mode_set.\n")); 288} 289 290static xf86OutputStatus 291via_sii164_detect(xf86OutputPtr output) 292{ 293 xf86MonPtr mon; 294 xf86OutputStatus status = XF86OutputStatusDisconnected; 295 ScrnInfoPtr pScrn = output->scrn; 296 viaSiI164RecPtr pSiI164Rec = output->driver_private; 297 298 /* Check for the DVI presence via SiI 164 first before accessing 299 * I2C bus. */ 300 if (viaSiI164Sense(pScrn, pSiI164Rec->SiI164I2CDev)) { 301 302 /* Since DVI presence was established, access the I2C bus 303 * assigned to DVI. */ 304 mon = xf86OutputGetEDID(output, pSiI164Rec->SiI164I2CDev->pI2CBus); 305 306 /* Is the interface type digital? */ 307 if (mon && DIGITAL(mon->features.input_type)) { 308 status = XF86OutputStatusConnected; 309 xf86DrvMsg(pScrn->scrnIndex, X_PROBED, 310 "Detected a monitor connected to DVI.\n"); 311 xf86OutputSetEDID(output, mon); 312 } else { 313 xf86DrvMsg(pScrn->scrnIndex, X_PROBED, 314 "Could not obtain EDID from a monitor " 315 "connected to DVI.\n"); 316 } 317 } 318 319 return status; 320} 321 322#ifdef RANDR_12_INTERFACE 323static Bool 324via_sii164_set_property(xf86OutputPtr output, Atom property, 325 RRPropertyValuePtr value) 326{ 327 return TRUE; 328} 329#endif 330 331#ifdef RANDR_13_INTERFACE 332static Bool 333via_sii164_get_property(xf86OutputPtr output, Atom property) 334{ 335 return FALSE; 336} 337#endif 338 339static void 340via_sii164_destroy(xf86OutputPtr output) 341{ 342} 343 344const xf86OutputFuncsRec via_sii164_funcs = { 345 .create_resources = via_sii164_create_resources, 346 .dpms = via_sii164_dpms, 347 .save = via_sii164_save, 348 .restore = via_sii164_restore, 349 .mode_valid = via_sii164_mode_valid, 350 .mode_fixup = via_sii164_mode_fixup, 351 .prepare = via_sii164_prepare, 352 .commit = via_sii164_commit, 353 .mode_set = via_sii164_mode_set, 354 .detect = via_sii164_detect, 355 .get_modes = xf86OutputGetEDIDModes, 356#ifdef RANDR_12_INTERFACE 357 .set_property = via_sii164_set_property, 358#endif 359#ifdef RANDR_13_INTERFACE 360 .get_property = via_sii164_get_property, 361#endif 362 .destroy = via_sii164_destroy, 363}; 364 365Bool 366viaSiI164Init(ScrnInfoPtr pScrn, I2CBusPtr pI2CBus) 367{ 368 xf86OutputPtr output; 369 VIAPtr pVia = VIAPTR(pScrn); 370 viaSiI164RecPtr pSiI164Rec = NULL; 371 I2CDevPtr pI2CDevice = NULL; 372 I2CSlaveAddr i2cAddr = 0x70; 373 CARD8 buf; 374 CARD16 vendorID, deviceID; 375 Bool status = FALSE; 376 char outputNameBuffer[32]; 377 378 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 379 "Entered viaSiI164Init.\n")); 380 381 if (!xf86I2CProbeAddress(pI2CBus, i2cAddr)) { 382 xf86DrvMsg(pScrn->scrnIndex, X_PROBED, 383 "I2C device not found.\n"); 384 goto exit; 385 } 386 387 pI2CDevice = xf86CreateI2CDevRec(); 388 if (!pI2CDevice) { 389 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, 390 "Failed to create an I2C bus device record.\n"); 391 goto exit; 392 } 393 394 pI2CDevice->DevName = "SiI 164"; 395 pI2CDevice->SlaveAddr = i2cAddr; 396 pI2CDevice->pI2CBus = pI2CBus; 397 if (!xf86I2CDevInit(pI2CDevice)) { 398 xf86DestroyI2CDevRec(pI2CDevice, TRUE); 399 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, 400 "Failed to initialize a device on I2C bus.\n"); 401 goto exit; 402 } 403 404 xf86I2CReadByte(pI2CDevice, 0, &buf); 405 vendorID = buf; 406 xf86I2CReadByte(pI2CDevice, 1, &buf); 407 vendorID |= buf << 8; 408 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 409 "Vendor ID: 0x%04x\n", vendorID)); 410 411 xf86I2CReadByte(pI2CDevice, 2, &buf); 412 deviceID = buf; 413 xf86I2CReadByte(pI2CDevice, 3, &buf); 414 deviceID |= buf << 8; 415 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 416 "Device ID: 0x%04x\n", deviceID)); 417 418 if ((vendorID != 0x0001) || (deviceID != 0x0006)) { 419 xf86DestroyI2CDevRec(pI2CDevice, TRUE); 420 xf86DrvMsg(pScrn->scrnIndex, X_PROBED, 421 "SiI 164 external TMDS transmitter not detected.\n"); 422 goto exit; 423 } 424 425 xf86DrvMsg(pScrn->scrnIndex, X_PROBED, 426 "SiI 164 external TMDS transmitter detected.\n"); 427 428 pSiI164Rec = xnfcalloc(1, sizeof(viaSiI164Rec)); 429 if (!pSiI164Rec) { 430 xf86DestroyI2CDevRec(pI2CDevice, TRUE); 431 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, 432 "Failed to allocate working storage for SiI 164.\n"); 433 goto exit; 434 } 435 436 // Remembering which I2C bus is used for SiI 164. 437 pSiI164Rec->SiI164I2CDev = pI2CDevice; 438 439 xf86I2CReadByte(pI2CDevice, 0x06, &buf); 440 pSiI164Rec->DotclockMin = buf * 1000; 441 442 xf86I2CReadByte(pI2CDevice, 0x07, &buf); 443 pSiI164Rec->DotclockMax = (buf + 65) * 1000; 444 445 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Supported SiI 164 Dot Clock Range: " 446 "%d to %d MHz\n", 447 pSiI164Rec->DotclockMin / 1000, 448 pSiI164Rec->DotclockMax / 1000); 449 450 /* The code to dynamically designate the particular DVI (i.e., DVI-1, 451 * DVI-2, etc.) for xrandr was borrowed from xf86-video-r128 DDX. */ 452 sprintf(outputNameBuffer, "DVI-%d", (pVia->numberDVI + 1)); 453 output = xf86OutputCreate(pScrn, &via_sii164_funcs, outputNameBuffer); 454 if (!output) { 455 free(pSiI164Rec); 456 xf86DestroyI2CDevRec(pI2CDevice, TRUE); 457 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, 458 "Failed to allocate X Server display output record for " 459 "SiI 164.\n"); 460 goto exit; 461 } 462 463 output->driver_private = pSiI164Rec; 464 465 /* Since there are two (2) display controllers registered with the 466 * X.Org Server and both IGA1 and IGA2 can handle DVI without any 467 * limitations, possible_crtcs should be set to 0x3 (0b11) so that 468 * either display controller can get assigned to handle DVI. */ 469 output->possible_crtcs = (1 << 1) | (1 << 0); 470 471 output->possible_clones = 0; 472 output->interlaceAllowed = FALSE; 473 output->doubleScanAllowed = FALSE; 474 475 viaSiI164DumpRegisters(pScrn, pI2CDevice); 476 477 pVia->numberDVI++; 478 status = TRUE; 479exit: 480 DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 481 "Exiting viaSiI164Init.\n")); 482 return status; 483} 484