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