1/* -*- c-basic-offset: 4 -*- */
2/**************************************************************************
3
4Copyright © 2006 Dave Airlie
5
6All Rights Reserved.
7
8Permission is hereby granted, free of charge, to any person obtaining a
9copy of this software and associated documentation files (the
10"Software"), to deal in the Software without restriction, including
11without limitation the rights to use, copy, modify, merge, publish,
12distribute, sub license, and/or sell copies of the Software, and to
13permit persons to whom the Software is furnished to do so, subject to
14the following conditions:
15
16The above copyright notice and this permission notice (including the
17next paragraph) shall be included in all copies or substantial portions
18of the Software.
19
20THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
23IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
24ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
25TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
26SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27
28**************************************************************************/
29
30#ifdef HAVE_CONFIG_H
31#include "config.h"
32#endif
33
34#include <stdint.h>
35
36#include "xf86.h"
37#include "xf86_OSproc.h"
38#include "compiler.h"
39#include "miscstruct.h"
40#include "xf86i2c.h"
41#include "xf86Crtc.h"
42#ifdef HAVE_XEXTPROTO_71
43#include <X11/extensions/dpmsconst.h>
44#else
45#define DPMS_SERVER
46#include <X11/extensions/dpms.h>
47#endif
48
49
50#include "../i2c_vid.h"
51#include "sil164.h"
52#include "sil164_reg.h"
53
54typedef struct _Sil164SaveRec {
55    uint8_t reg8;
56    uint8_t reg9;
57    uint8_t regc;
58} SIL164SaveRec;
59
60typedef struct {
61    I2CDevRec d;
62    Bool quiet;
63    SIL164SaveRec SavedReg;
64    SIL164SaveRec ModeReg;
65} SIL164Rec, *SIL164Ptr;
66
67#define SILPTR(d) ((SIL164Ptr)(d->DriverPrivate.ptr))
68
69static Bool
70sil164ReadByte(SIL164Ptr sil, int addr, uint8_t *ch)
71{
72    if (!xf86I2CReadByte(&(sil->d), addr, ch)) {
73	if (!sil->quiet) {
74	    xf86DrvMsg(sil->d.pI2CBus->scrnIndex, X_ERROR,
75		       "Unable to read from %s Slave %d.\n",
76		       sil->d.pI2CBus->BusName, sil->d.SlaveAddr);
77	}
78	return FALSE;
79    }
80    return TRUE;
81}
82
83static Bool
84sil164WriteByte(SIL164Ptr sil, int addr, uint8_t ch)
85{
86    if (!xf86I2CWriteByte(&(sil->d), addr, ch)) {
87	if (!sil->quiet) {
88	    xf86DrvMsg(sil->d.pI2CBus->scrnIndex, X_ERROR,
89		       "Unable to write to %s Slave %d.\n",
90		       sil->d.pI2CBus->BusName, sil->d.SlaveAddr);
91	}
92	return FALSE;
93    }
94    return TRUE;
95}
96
97/* Silicon Image 164 driver for chip on i2c bus */
98static void *
99sil164_init(I2CBusPtr b, I2CSlaveAddr addr)
100{
101    /* this will detect the SIL164 chip on the specified i2c bus */
102    SIL164Ptr sil;
103    unsigned char ch;
104
105    sil = xcalloc(1, sizeof(SIL164Rec));
106    if (sil == NULL)
107	return NULL;
108
109    sil->d.DevName = "SIL164 TMDS Controller";
110    sil->d.SlaveAddr = addr;
111    sil->d.pI2CBus = b;
112    sil->d.StartTimeout = b->StartTimeout;
113    sil->d.BitTimeout = b->BitTimeout;
114    sil->d.AcknTimeout = b->AcknTimeout;
115    sil->d.ByteTimeout = b->ByteTimeout;
116    sil->d.DriverPrivate.ptr = sil;
117    sil->quiet = TRUE;
118
119    if (!sil164ReadByte(sil, SIL164_VID_LO, &ch))
120	goto out;
121
122    if (ch!=(SIL164_VID & 0xFF)) {
123	xf86DrvMsg(sil->d.pI2CBus->scrnIndex, X_ERROR,
124		   "sil164 not detected got %d: from %s Slave %d.\n",
125		   ch, sil->d.pI2CBus->BusName, sil->d.SlaveAddr);
126	goto out;
127    }
128
129    if (!sil164ReadByte(sil, SIL164_DID_LO, &ch))
130	goto out;
131
132    if (ch!=(SIL164_DID & 0xFF)) {
133	xf86DrvMsg(sil->d.pI2CBus->scrnIndex, X_ERROR,
134		   "sil164 not detected got %d: from %s Slave %d.\n",
135		   ch, sil->d.pI2CBus->BusName, sil->d.SlaveAddr);
136	goto out;
137    }
138    sil->quiet = FALSE;
139
140    if (!xf86I2CDevInit(&(sil->d))) {
141	goto out;
142    }
143
144    return sil;
145
146out:
147    xfree(sil);
148    return NULL;
149}
150
151static xf86OutputStatus
152sil164_detect(I2CDevPtr d)
153{
154    SIL164Ptr sil = SILPTR(d);
155    uint8_t reg9;
156
157    sil164ReadByte(sil, SIL164_REG9, &reg9);
158
159    if (reg9 & SIL164_9_HTPLG)
160	return XF86OutputStatusConnected;
161    else
162	return XF86OutputStatusDisconnected;
163}
164
165static ModeStatus
166sil164_mode_valid(I2CDevPtr d, DisplayModePtr mode)
167{
168    return MODE_OK;
169}
170
171static void
172sil164_mode_set(I2CDevPtr d, DisplayModePtr mode, DisplayModePtr adjusted_mode)
173{
174    /* As long as the basics are set up, since we don't have clock dependencies
175     * in the mode setup, we can just leave the registers alone and everything
176     * will work fine.
177     */
178    /* recommended programming sequence from doc */
179    /*sil164WriteByte(sil, 0x08, 0x30);
180      sil164WriteByte(sil, 0x09, 0x00);
181      sil164WriteByte(sil, 0x0a, 0x90);
182      sil164WriteByte(sil, 0x0c, 0x89);
183      sil164WriteByte(sil, 0x08, 0x31);*/
184    /* don't do much */
185    return;
186}
187
188/* set the SIL164 power state */
189static void
190sil164_dpms(I2CDevPtr d, int mode)
191{
192    SIL164Ptr sil = SILPTR(d);
193    int ret;
194    unsigned char ch;
195
196    ret = sil164ReadByte(sil, SIL164_REG8, &ch);
197    if (ret == FALSE)
198	return;
199
200    if (mode == DPMSModeOn)
201	ch |= SIL164_8_PD;
202    else
203	ch &= ~SIL164_8_PD;
204
205    sil164WriteByte(sil, SIL164_REG8, ch);
206
207    return;
208}
209
210static void
211sil164_dump_regs(I2CDevPtr d)
212{
213    SIL164Ptr sil = SILPTR(d);
214    uint8_t val;
215
216    sil164ReadByte(sil, SIL164_FREQ_LO, &val);
217    xf86DrvMsg(sil->d.pI2CBus->scrnIndex, X_INFO, "SIL164_FREQ_LO: 0x%02x\n",
218	       val);
219    sil164ReadByte(sil, SIL164_FREQ_HI, &val);
220    xf86DrvMsg(sil->d.pI2CBus->scrnIndex, X_INFO, "SIL164_FREQ_HI: 0x%02x\n",
221	       val);
222    sil164ReadByte(sil, SIL164_REG8, &val);
223    xf86DrvMsg(sil->d.pI2CBus->scrnIndex, X_INFO, "SIL164_REG8: 0x%02x\n", val);
224    sil164ReadByte(sil, SIL164_REG9, &val);
225    xf86DrvMsg(sil->d.pI2CBus->scrnIndex, X_INFO, "SIL164_REG9: 0x%02x\n", val);
226    sil164ReadByte(sil, SIL164_REGC, &val);
227    xf86DrvMsg(sil->d.pI2CBus->scrnIndex, X_INFO, "SIL164_REGC: 0x%02x\n", val);
228}
229
230static void
231sil164_save(I2CDevPtr d)
232{
233    SIL164Ptr sil = SILPTR(d);
234
235    if (!sil164ReadByte(sil, SIL164_REG8, &sil->SavedReg.reg8))
236	return;
237
238    if (!sil164ReadByte(sil, SIL164_REG9, &sil->SavedReg.reg9))
239	return;
240
241    if (!sil164ReadByte(sil, SIL164_REGC, &sil->SavedReg.regc))
242	return;
243
244    return;
245}
246
247static void
248sil164_restore(I2CDevPtr d)
249{
250    SIL164Ptr sil = SILPTR(d);
251
252    /* Restore it powered down initially */
253    sil164WriteByte(sil, SIL164_REG8, sil->SavedReg.reg8 & ~0x1);
254
255    sil164WriteByte(sil, SIL164_REG9, sil->SavedReg.reg9);
256    sil164WriteByte(sil, SIL164_REGC, sil->SavedReg.regc);
257    sil164WriteByte(sil, SIL164_REG8, sil->SavedReg.reg8);
258}
259
260
261_X_EXPORT I830I2CVidOutputRec SIL164VidOutput = {
262    .init = sil164_init,
263    .detect = sil164_detect,
264    .mode_valid = sil164_mode_valid,
265    .mode_set = sil164_mode_set,
266    .dpms = sil164_dpms,
267    .dump_regs = sil164_dump_regs,
268    .save = sil164_save,
269    .restore = sil164_restore,
270};
271