1983b4bf2Smrg/* 2983b4bf2Smrg * Copyright 2016 Kevin Brace 3983b4bf2Smrg * Copyright 2016 The OpenChrome Project 4983b4bf2Smrg * [https://www.freedesktop.org/wiki/Openchrome] 5983b4bf2Smrg * Copyright 2014 SHS SERVICES GmbH 6983b4bf2Smrg * Copyright 2006-2009 Luc Verhaegen. 7983b4bf2Smrg * 8983b4bf2Smrg * Permission is hereby granted, free of charge, to any person obtaining a 9983b4bf2Smrg * copy of this software and associated documentation files (the "Software"), 10983b4bf2Smrg * to deal in the Software without restriction, including without limitation 11983b4bf2Smrg * the rights to use, copy, modify, merge, publish, distribute, sub license, 12983b4bf2Smrg * and/or sell copies of the Software, and to permit persons to whom the 13983b4bf2Smrg * Software is furnished to do so, subject to the following conditions: 14983b4bf2Smrg * 15983b4bf2Smrg * The above copyright notice and this permission notice (including the 16983b4bf2Smrg * next paragraph) shall be included in all copies or substantial portions 17983b4bf2Smrg * of the Software. 18983b4bf2Smrg * 19983b4bf2Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20983b4bf2Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21983b4bf2Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 22983b4bf2Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23983b4bf2Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24983b4bf2Smrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25983b4bf2Smrg * DEALINGS IN THE SOFTWARE. 26983b4bf2Smrg */ 27983b4bf2Smrg 28983b4bf2Smrg#ifdef HAVE_CONFIG_H 29983b4bf2Smrg#include "config.h" 30983b4bf2Smrg#endif 31983b4bf2Smrg 32983b4bf2Smrg#include "via_driver.h" 33983b4bf2Smrg#include "via_sii164.h" 34983b4bf2Smrg 35983b4bf2Smrgstatic void 36983b4bf2SmrgviaSiI164DumpRegisters(ScrnInfoPtr pScrn, I2CDevPtr pDev) 37983b4bf2Smrg{ 38983b4bf2Smrg int i; 39983b4bf2Smrg CARD8 tmp; 40983b4bf2Smrg 41983b4bf2Smrg DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 42983b4bf2Smrg "Entered viaSiI164DumpRegisters.\n")); 43983b4bf2Smrg 44983b4bf2Smrg DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, "SiI 164: dumping registers:\n")); 45983b4bf2Smrg for (i = 0; i <= 0x0f; i++) { 46983b4bf2Smrg xf86I2CReadByte(pDev, i, &tmp); 47983b4bf2Smrg DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, "SiI 164: 0x%02x: 0x%02x\n", i, tmp)); 48983b4bf2Smrg } 49983b4bf2Smrg 50983b4bf2Smrg DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 51983b4bf2Smrg "Exiting viaSiI164DumpRegisters.\n")); 52983b4bf2Smrg} 53983b4bf2Smrg 54983b4bf2Smrgstatic void 55983b4bf2SmrgviaSiI164InitRegisters(ScrnInfoPtr pScrn, I2CDevPtr pDev) 56983b4bf2Smrg{ 57983b4bf2Smrg DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 58983b4bf2Smrg "Entered viaSiI164InitRegisters.\n")); 59983b4bf2Smrg 60983b4bf2Smrg xf86I2CWriteByte(pDev, 0x08, 61983b4bf2Smrg VIA_SII164_VEN | VIA_SII164_HEN | 62983b4bf2Smrg VIA_SII164_DSEL | VIA_SII164_EDGE | VIA_SII164_PDB); 63983b4bf2Smrg 64983b4bf2Smrg /* Route receiver detect bit (Offset 0x09[2]) as the output of 65983b4bf2Smrg * MSEN pin. */ 66983b4bf2Smrg xf86I2CWriteByte(pDev, 0x09, 0x20); 67983b4bf2Smrg 68983b4bf2Smrg xf86I2CWriteByte(pDev, 0x0A, 0x90); 69983b4bf2Smrg 70983b4bf2Smrg xf86I2CWriteByte(pDev, 0x0C, 0x89); 71983b4bf2Smrg 72983b4bf2Smrg DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 73983b4bf2Smrg "Exiting viaSiI164InitRegisters.\n")); 74983b4bf2Smrg} 75983b4bf2Smrg 76983b4bf2Smrg/* 77983b4bf2Smrg * Returns TMDS receiver detection state for Silicon Image SiI 164 78983b4bf2Smrg * external TMDS transmitter. 79983b4bf2Smrg */ 80983b4bf2Smrgstatic Bool 81983b4bf2SmrgviaSiI164Sense(ScrnInfoPtr pScrn, I2CDevPtr pDev) 82983b4bf2Smrg{ 83983b4bf2Smrg CARD8 tmp; 84983b4bf2Smrg Bool receiverDetected = FALSE; 85983b4bf2Smrg 86983b4bf2Smrg DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 87983b4bf2Smrg "Entered viaSiI164Sense.\n")); 88983b4bf2Smrg 89983b4bf2Smrg xf86I2CReadByte(pDev, 0x09, &tmp); 90983b4bf2Smrg if (tmp & 0x04) { 91983b4bf2Smrg receiverDetected = TRUE; 92983b4bf2Smrg } 93983b4bf2Smrg 94983b4bf2Smrg xf86DrvMsg(pScrn->scrnIndex, X_INFO, 95983b4bf2Smrg "SiI 164 %s a TMDS receiver.\n", 96983b4bf2Smrg receiverDetected ? "detected" : "did not detect"); 97983b4bf2Smrg 98983b4bf2Smrg DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 99983b4bf2Smrg "Exiting viaSiI164Sense.\n")); 100983b4bf2Smrg return receiverDetected; 101983b4bf2Smrg} 102983b4bf2Smrg 103983b4bf2Smrgstatic void 104983b4bf2SmrgviaSiI164Power(ScrnInfoPtr pScrn, I2CDevPtr pDev, Bool powerState) 105983b4bf2Smrg{ 106983b4bf2Smrg DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 107983b4bf2Smrg "Entered viaSiI164Power.\n")); 108983b4bf2Smrg 109983b4bf2Smrg xf86I2CMaskByte(pDev, 0x08, powerState ? 0x01 : 0x00, 0x01); 110983b4bf2Smrg xf86DrvMsg(pScrn->scrnIndex, X_INFO, "SiI 164 (DVI) Power: %s\n", 111983b4bf2Smrg powerState ? "On" : "Off"); 112983b4bf2Smrg 113983b4bf2Smrg DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 114983b4bf2Smrg "Exiting viaSiI164Power.\n")); 115983b4bf2Smrg} 116983b4bf2Smrg 117983b4bf2Smrgstatic void 118983b4bf2SmrgviaSiI164SaveRegisters(ScrnInfoPtr pScrn, I2CDevPtr pDev, 119983b4bf2Smrg viaSiI164RecPtr pSiI164Rec) 120983b4bf2Smrg{ 121983b4bf2Smrg DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 122983b4bf2Smrg "Entered viaSiI164SaveRegisters.\n")); 123983b4bf2Smrg 124983b4bf2Smrg xf86DrvMsg(pScrn->scrnIndex, X_INFO, 125983b4bf2Smrg "Saving SiI 164 registers.\n"); 126983b4bf2Smrg xf86I2CReadByte(pDev, 0x08, &pSiI164Rec->Register08); 127983b4bf2Smrg xf86I2CReadByte(pDev, 0x09, &pSiI164Rec->Register09); 128983b4bf2Smrg xf86I2CReadByte(pDev, 0x0A, &pSiI164Rec->Register0A); 129983b4bf2Smrg xf86I2CReadByte(pDev, 0x0C, &pSiI164Rec->Register0C); 130983b4bf2Smrg 131983b4bf2Smrg DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 132983b4bf2Smrg "Exiting viaSiI164SaveRegisters.\n")); 133983b4bf2Smrg} 134983b4bf2Smrg 135983b4bf2Smrgstatic void 136983b4bf2SmrgviaSiI164RestoreRegisters(ScrnInfoPtr pScrn, I2CDevPtr pDev, 137983b4bf2Smrg viaSiI164RecPtr pSiI164Rec) 138983b4bf2Smrg{ 139983b4bf2Smrg DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 140983b4bf2Smrg "Entered viaSiI164RestoreRegisters.\n")); 141983b4bf2Smrg 142983b4bf2Smrg DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 143983b4bf2Smrg "Restoring SiI 164 registers.\n")); 144983b4bf2Smrg xf86I2CWriteByte(pDev, 0x08, pSiI164Rec->Register08); 145983b4bf2Smrg xf86I2CWriteByte(pDev, 0x09, pSiI164Rec->Register09); 146983b4bf2Smrg xf86I2CWriteByte(pDev, 0x0A, pSiI164Rec->Register0A); 147983b4bf2Smrg xf86I2CWriteByte(pDev, 0x0C, pSiI164Rec->Register0C); 148983b4bf2Smrg 149983b4bf2Smrg DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 150983b4bf2Smrg "Exiting viaSiI164RestoreRegisters.\n")); 151983b4bf2Smrg} 152983b4bf2Smrg 153983b4bf2Smrgstatic int 154983b4bf2SmrgviaSiI164CheckModeValidity(xf86OutputPtr output, DisplayModePtr pMode) 155983b4bf2Smrg{ 156983b4bf2Smrg ScrnInfoPtr pScrn = output->scrn; 157983b4bf2Smrg viaSiI164RecPtr pSiI164Rec = output->driver_private; 158983b4bf2Smrg int status = MODE_OK; 159983b4bf2Smrg 160983b4bf2Smrg DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 161983b4bf2Smrg "Entered viaSiI164CheckModeValidity.\n")); 162983b4bf2Smrg 163983b4bf2Smrg if (pMode->Clock < pSiI164Rec->DotclockMin) { 164983b4bf2Smrg status = MODE_CLOCK_LOW; 165983b4bf2Smrg goto exit; 166983b4bf2Smrg } 167983b4bf2Smrg 168983b4bf2Smrg if (pMode->Clock > pSiI164Rec->DotclockMax) { 169983b4bf2Smrg status = MODE_CLOCK_HIGH; 170983b4bf2Smrg } 171983b4bf2Smrg 172983b4bf2Smrgexit: 173983b4bf2Smrg DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 174983b4bf2Smrg "Exiting viaSiI164CheckModeValidity.\n")); 175983b4bf2Smrg return status; 176983b4bf2Smrg} 177983b4bf2Smrg 178983b4bf2Smrgstatic void 179983b4bf2Smrgvia_sii164_create_resources(xf86OutputPtr output) 180983b4bf2Smrg{ 181983b4bf2Smrg} 182983b4bf2Smrg 183983b4bf2Smrgstatic void 184983b4bf2Smrgvia_sii164_dpms(xf86OutputPtr output, int mode) 185983b4bf2Smrg{ 186983b4bf2Smrg ScrnInfoPtr pScrn = output->scrn; 187983b4bf2Smrg viaSiI164RecPtr pSiI164Rec = output->driver_private; 188983b4bf2Smrg 189983b4bf2Smrg DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 190983b4bf2Smrg "Entered via_sii164_dpms.\n")); 191983b4bf2Smrg 192983b4bf2Smrg switch (mode) { 193983b4bf2Smrg case DPMSModeOn: 194983b4bf2Smrg viaSiI164Power(pScrn, pSiI164Rec->SiI164I2CDev, TRUE); 195983b4bf2Smrg break; 196983b4bf2Smrg case DPMSModeStandby: 197983b4bf2Smrg case DPMSModeSuspend: 198983b4bf2Smrg case DPMSModeOff: 199983b4bf2Smrg viaSiI164Power(pScrn, pSiI164Rec->SiI164I2CDev, FALSE); 200983b4bf2Smrg break; 201983b4bf2Smrg default: 202983b4bf2Smrg break; 203983b4bf2Smrg } 204983b4bf2Smrg 205983b4bf2Smrg DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 206983b4bf2Smrg "Exiting via_sii164_dpms.\n")); 207983b4bf2Smrg} 208983b4bf2Smrg 209983b4bf2Smrgstatic void 210983b4bf2Smrgvia_sii164_save(xf86OutputPtr output) 211983b4bf2Smrg{ 212983b4bf2Smrg ScrnInfoPtr pScrn = output->scrn; 213983b4bf2Smrg viaSiI164RecPtr pSiI164Rec = output->driver_private; 214983b4bf2Smrg 215983b4bf2Smrg DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 216983b4bf2Smrg "Entered via_sii164_save.\n")); 217983b4bf2Smrg 218983b4bf2Smrg viaSiI164SaveRegisters(pScrn, pSiI164Rec->SiI164I2CDev, pSiI164Rec); 219983b4bf2Smrg 220983b4bf2Smrg DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 221983b4bf2Smrg "Exiting via_sii164_save.\n")); 222983b4bf2Smrg} 223983b4bf2Smrg 224983b4bf2Smrgstatic void 225983b4bf2Smrgvia_sii164_restore(xf86OutputPtr output) 226983b4bf2Smrg{ 227983b4bf2Smrg ScrnInfoPtr pScrn = output->scrn; 228983b4bf2Smrg viaSiI164RecPtr pSiI164Rec = output->driver_private; 229983b4bf2Smrg 230983b4bf2Smrg DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 231983b4bf2Smrg "Entered via_sii164_restore.\n")); 232983b4bf2Smrg 233983b4bf2Smrg viaSiI164RestoreRegisters(pScrn, pSiI164Rec->SiI164I2CDev, 234983b4bf2Smrg pSiI164Rec); 235983b4bf2Smrg 236983b4bf2Smrg DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 237983b4bf2Smrg "Exiting via_sii164_restore.\n")); 238983b4bf2Smrg} 239983b4bf2Smrg 240983b4bf2Smrgstatic int 241983b4bf2Smrgvia_sii164_mode_valid(xf86OutputPtr output, DisplayModePtr pMode) 242983b4bf2Smrg{ 243983b4bf2Smrg return viaSiI164CheckModeValidity(output, pMode); 244983b4bf2Smrg} 245983b4bf2Smrg 246983b4bf2Smrgstatic Bool 247983b4bf2Smrgvia_sii164_mode_fixup(xf86OutputPtr output, DisplayModePtr mode, 248983b4bf2Smrg DisplayModePtr adjusted_mode) 249983b4bf2Smrg{ 250983b4bf2Smrg return TRUE; 251983b4bf2Smrg} 252983b4bf2Smrg 253983b4bf2Smrgstatic void 254983b4bf2Smrgvia_sii164_prepare(xf86OutputPtr output) 255983b4bf2Smrg{ 256983b4bf2Smrg} 257983b4bf2Smrg 258983b4bf2Smrgstatic void 259983b4bf2Smrgvia_sii164_commit(xf86OutputPtr output) 260983b4bf2Smrg{ 261983b4bf2Smrg} 262983b4bf2Smrg 263983b4bf2Smrgstatic void 264983b4bf2Smrgvia_sii164_mode_set(xf86OutputPtr output, DisplayModePtr mode, 265983b4bf2Smrg DisplayModePtr adjusted_mode) 266983b4bf2Smrg{ 267983b4bf2Smrg ScrnInfoPtr pScrn = output->scrn; 268983b4bf2Smrg drmmode_crtc_private_ptr iga = output->crtc->driver_private; 269983b4bf2Smrg viaSiI164RecPtr pSiI164Rec = output->driver_private; 270983b4bf2Smrg 271983b4bf2Smrg DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 272983b4bf2Smrg "Entered via_sii164_mode_set.\n")); 273983b4bf2Smrg 274983b4bf2Smrg if (output->crtc) { 275983b4bf2Smrg viaExtTMDSSetClockDriveStrength(pScrn, 0x03); 276983b4bf2Smrg viaExtTMDSSetDataDriveStrength(pScrn, 0x03); 277983b4bf2Smrg viaExtTMDSEnableIOPads(pScrn, 0x03); 278983b4bf2Smrg 279983b4bf2Smrg viaSiI164DumpRegisters(pScrn, pSiI164Rec->SiI164I2CDev); 280983b4bf2Smrg viaSiI164InitRegisters(pScrn, pSiI164Rec->SiI164I2CDev); 281983b4bf2Smrg viaSiI164DumpRegisters(pScrn, pSiI164Rec->SiI164I2CDev); 282983b4bf2Smrg 283983b4bf2Smrg viaExtTMDSSetDisplaySource(pScrn, iga->index ? 0x01 : 0x00); 284983b4bf2Smrg } 285983b4bf2Smrg 286983b4bf2Smrg DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 287983b4bf2Smrg "Exiting via_sii164_mode_set.\n")); 288983b4bf2Smrg} 289983b4bf2Smrg 290983b4bf2Smrgstatic xf86OutputStatus 291983b4bf2Smrgvia_sii164_detect(xf86OutputPtr output) 292983b4bf2Smrg{ 293983b4bf2Smrg xf86MonPtr mon; 294983b4bf2Smrg xf86OutputStatus status = XF86OutputStatusDisconnected; 295983b4bf2Smrg ScrnInfoPtr pScrn = output->scrn; 296983b4bf2Smrg viaSiI164RecPtr pSiI164Rec = output->driver_private; 297983b4bf2Smrg 298983b4bf2Smrg /* Check for the DVI presence via SiI 164 first before accessing 299983b4bf2Smrg * I2C bus. */ 300983b4bf2Smrg if (viaSiI164Sense(pScrn, pSiI164Rec->SiI164I2CDev)) { 301983b4bf2Smrg 302983b4bf2Smrg /* Since DVI presence was established, access the I2C bus 303983b4bf2Smrg * assigned to DVI. */ 304983b4bf2Smrg mon = xf86OutputGetEDID(output, pSiI164Rec->SiI164I2CDev->pI2CBus); 305983b4bf2Smrg 306983b4bf2Smrg /* Is the interface type digital? */ 307983b4bf2Smrg if (mon && DIGITAL(mon->features.input_type)) { 308983b4bf2Smrg status = XF86OutputStatusConnected; 309983b4bf2Smrg xf86DrvMsg(pScrn->scrnIndex, X_PROBED, 310983b4bf2Smrg "Detected a monitor connected to DVI.\n"); 311983b4bf2Smrg xf86OutputSetEDID(output, mon); 312983b4bf2Smrg } else { 313983b4bf2Smrg xf86DrvMsg(pScrn->scrnIndex, X_PROBED, 314983b4bf2Smrg "Could not obtain EDID from a monitor " 315983b4bf2Smrg "connected to DVI.\n"); 316983b4bf2Smrg } 317983b4bf2Smrg } 318983b4bf2Smrg 319983b4bf2Smrg return status; 320983b4bf2Smrg} 321983b4bf2Smrg 322983b4bf2Smrg#ifdef RANDR_12_INTERFACE 323983b4bf2Smrgstatic Bool 324983b4bf2Smrgvia_sii164_set_property(xf86OutputPtr output, Atom property, 325983b4bf2Smrg RRPropertyValuePtr value) 326983b4bf2Smrg{ 327983b4bf2Smrg return TRUE; 328983b4bf2Smrg} 329983b4bf2Smrg#endif 330983b4bf2Smrg 331983b4bf2Smrg#ifdef RANDR_13_INTERFACE 332983b4bf2Smrgstatic Bool 333983b4bf2Smrgvia_sii164_get_property(xf86OutputPtr output, Atom property) 334983b4bf2Smrg{ 335983b4bf2Smrg return FALSE; 336983b4bf2Smrg} 337983b4bf2Smrg#endif 338983b4bf2Smrg 339983b4bf2Smrgstatic void 340983b4bf2Smrgvia_sii164_destroy(xf86OutputPtr output) 341983b4bf2Smrg{ 342983b4bf2Smrg} 343983b4bf2Smrg 344983b4bf2Smrgconst xf86OutputFuncsRec via_sii164_funcs = { 345983b4bf2Smrg .create_resources = via_sii164_create_resources, 346983b4bf2Smrg .dpms = via_sii164_dpms, 347983b4bf2Smrg .save = via_sii164_save, 348983b4bf2Smrg .restore = via_sii164_restore, 349983b4bf2Smrg .mode_valid = via_sii164_mode_valid, 350983b4bf2Smrg .mode_fixup = via_sii164_mode_fixup, 351983b4bf2Smrg .prepare = via_sii164_prepare, 352983b4bf2Smrg .commit = via_sii164_commit, 353983b4bf2Smrg .mode_set = via_sii164_mode_set, 354983b4bf2Smrg .detect = via_sii164_detect, 355983b4bf2Smrg .get_modes = xf86OutputGetEDIDModes, 356983b4bf2Smrg#ifdef RANDR_12_INTERFACE 357983b4bf2Smrg .set_property = via_sii164_set_property, 358983b4bf2Smrg#endif 359983b4bf2Smrg#ifdef RANDR_13_INTERFACE 360983b4bf2Smrg .get_property = via_sii164_get_property, 361983b4bf2Smrg#endif 362983b4bf2Smrg .destroy = via_sii164_destroy, 363983b4bf2Smrg}; 364983b4bf2Smrg 365983b4bf2SmrgBool 366983b4bf2SmrgviaSiI164Init(ScrnInfoPtr pScrn, I2CBusPtr pI2CBus) 367983b4bf2Smrg{ 368983b4bf2Smrg xf86OutputPtr output; 369983b4bf2Smrg VIAPtr pVia = VIAPTR(pScrn); 370983b4bf2Smrg viaSiI164RecPtr pSiI164Rec = NULL; 371983b4bf2Smrg I2CDevPtr pI2CDevice = NULL; 372983b4bf2Smrg I2CSlaveAddr i2cAddr = 0x70; 373983b4bf2Smrg CARD8 buf; 374983b4bf2Smrg CARD16 vendorID, deviceID; 375983b4bf2Smrg Bool status = FALSE; 376983b4bf2Smrg char outputNameBuffer[32]; 377983b4bf2Smrg 378983b4bf2Smrg DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 379983b4bf2Smrg "Entered viaSiI164Init.\n")); 380983b4bf2Smrg 381983b4bf2Smrg if (!xf86I2CProbeAddress(pI2CBus, i2cAddr)) { 382983b4bf2Smrg xf86DrvMsg(pScrn->scrnIndex, X_PROBED, 383983b4bf2Smrg "I2C device not found.\n"); 384983b4bf2Smrg goto exit; 385983b4bf2Smrg } 386983b4bf2Smrg 387983b4bf2Smrg pI2CDevice = xf86CreateI2CDevRec(); 388983b4bf2Smrg if (!pI2CDevice) { 389983b4bf2Smrg xf86DrvMsg(pScrn->scrnIndex, X_ERROR, 390983b4bf2Smrg "Failed to create an I2C bus device record.\n"); 391983b4bf2Smrg goto exit; 392983b4bf2Smrg } 393983b4bf2Smrg 394983b4bf2Smrg pI2CDevice->DevName = "SiI 164"; 395983b4bf2Smrg pI2CDevice->SlaveAddr = i2cAddr; 396983b4bf2Smrg pI2CDevice->pI2CBus = pI2CBus; 397983b4bf2Smrg if (!xf86I2CDevInit(pI2CDevice)) { 398983b4bf2Smrg xf86DestroyI2CDevRec(pI2CDevice, TRUE); 399983b4bf2Smrg xf86DrvMsg(pScrn->scrnIndex, X_ERROR, 400983b4bf2Smrg "Failed to initialize a device on I2C bus.\n"); 401983b4bf2Smrg goto exit; 402983b4bf2Smrg } 403983b4bf2Smrg 404983b4bf2Smrg xf86I2CReadByte(pI2CDevice, 0, &buf); 405983b4bf2Smrg vendorID = buf; 406983b4bf2Smrg xf86I2CReadByte(pI2CDevice, 1, &buf); 407983b4bf2Smrg vendorID |= buf << 8; 408983b4bf2Smrg DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 409983b4bf2Smrg "Vendor ID: 0x%04x\n", vendorID)); 410983b4bf2Smrg 411983b4bf2Smrg xf86I2CReadByte(pI2CDevice, 2, &buf); 412983b4bf2Smrg deviceID = buf; 413983b4bf2Smrg xf86I2CReadByte(pI2CDevice, 3, &buf); 414983b4bf2Smrg deviceID |= buf << 8; 415983b4bf2Smrg DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 416983b4bf2Smrg "Device ID: 0x%04x\n", deviceID)); 417983b4bf2Smrg 418983b4bf2Smrg if ((vendorID != 0x0001) || (deviceID != 0x0006)) { 419983b4bf2Smrg xf86DestroyI2CDevRec(pI2CDevice, TRUE); 420983b4bf2Smrg xf86DrvMsg(pScrn->scrnIndex, X_PROBED, 421983b4bf2Smrg "SiI 164 external TMDS transmitter not detected.\n"); 422983b4bf2Smrg goto exit; 423983b4bf2Smrg } 424983b4bf2Smrg 425983b4bf2Smrg xf86DrvMsg(pScrn->scrnIndex, X_PROBED, 426983b4bf2Smrg "SiI 164 external TMDS transmitter detected.\n"); 427983b4bf2Smrg 428983b4bf2Smrg pSiI164Rec = xnfcalloc(1, sizeof(viaSiI164Rec)); 429983b4bf2Smrg if (!pSiI164Rec) { 430983b4bf2Smrg xf86DestroyI2CDevRec(pI2CDevice, TRUE); 431983b4bf2Smrg xf86DrvMsg(pScrn->scrnIndex, X_ERROR, 432983b4bf2Smrg "Failed to allocate working storage for SiI 164.\n"); 433983b4bf2Smrg goto exit; 434983b4bf2Smrg } 435983b4bf2Smrg 436983b4bf2Smrg // Remembering which I2C bus is used for SiI 164. 437983b4bf2Smrg pSiI164Rec->SiI164I2CDev = pI2CDevice; 438983b4bf2Smrg 439983b4bf2Smrg xf86I2CReadByte(pI2CDevice, 0x06, &buf); 440983b4bf2Smrg pSiI164Rec->DotclockMin = buf * 1000; 441983b4bf2Smrg 442983b4bf2Smrg xf86I2CReadByte(pI2CDevice, 0x07, &buf); 443983b4bf2Smrg pSiI164Rec->DotclockMax = (buf + 65) * 1000; 444983b4bf2Smrg 445983b4bf2Smrg xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Supported SiI 164 Dot Clock Range: " 446983b4bf2Smrg "%d to %d MHz\n", 447983b4bf2Smrg pSiI164Rec->DotclockMin / 1000, 448983b4bf2Smrg pSiI164Rec->DotclockMax / 1000); 449983b4bf2Smrg 450983b4bf2Smrg /* The code to dynamically designate the particular DVI (i.e., DVI-1, 451983b4bf2Smrg * DVI-2, etc.) for xrandr was borrowed from xf86-video-r128 DDX. */ 452983b4bf2Smrg sprintf(outputNameBuffer, "DVI-%d", (pVia->numberDVI + 1)); 453983b4bf2Smrg output = xf86OutputCreate(pScrn, &via_sii164_funcs, outputNameBuffer); 454983b4bf2Smrg if (!output) { 455983b4bf2Smrg free(pSiI164Rec); 456983b4bf2Smrg xf86DestroyI2CDevRec(pI2CDevice, TRUE); 457983b4bf2Smrg xf86DrvMsg(pScrn->scrnIndex, X_ERROR, 458983b4bf2Smrg "Failed to allocate X Server display output record for " 459983b4bf2Smrg "SiI 164.\n"); 460983b4bf2Smrg goto exit; 461983b4bf2Smrg } 462983b4bf2Smrg 463983b4bf2Smrg output->driver_private = pSiI164Rec; 464983b4bf2Smrg 465983b4bf2Smrg /* Since there are two (2) display controllers registered with the 466983b4bf2Smrg * X.Org Server and both IGA1 and IGA2 can handle DVI without any 467983b4bf2Smrg * limitations, possible_crtcs should be set to 0x3 (0b11) so that 468983b4bf2Smrg * either display controller can get assigned to handle DVI. */ 469983b4bf2Smrg output->possible_crtcs = (1 << 1) | (1 << 0); 470983b4bf2Smrg 471983b4bf2Smrg output->possible_clones = 0; 472983b4bf2Smrg output->interlaceAllowed = FALSE; 473983b4bf2Smrg output->doubleScanAllowed = FALSE; 474983b4bf2Smrg 475983b4bf2Smrg viaSiI164DumpRegisters(pScrn, pI2CDevice); 476983b4bf2Smrg 477983b4bf2Smrg pVia->numberDVI++; 478983b4bf2Smrg status = TRUE; 479983b4bf2Smrgexit: 480983b4bf2Smrg DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, 481983b4bf2Smrg "Exiting viaSiI164Init.\n")); 482983b4bf2Smrg return status; 483983b4bf2Smrg} 484