1/**************************************************************************
2
3Copyright © 2006 Dave Airlie
4
5All Rights Reserved.
6
7Permission is hereby granted, free of charge, to any person obtaining a
8copy of this software and associated documentation files (the
9"Software"), to deal in the Software without restriction, including
10without limitation the rights to use, copy, modify, merge, publish,
11distribute, sub license, and/or sell copies of the Software, and to
12permit persons to whom the Software is furnished to do so, subject to
13the following conditions:
14
15The above copyright notice and this permission notice (including the
16next paragraph) shall be included in all copies or substantial portions
17of the Software.
18
19THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
22IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
23ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26
27**************************************************************************/
28
29#ifdef HAVE_CONFIG_H
30#include "config.h"
31#endif
32
33#include <stdint.h>
34#include <string.h>
35#include "xf86.h"
36#include "xf86_OSproc.h"
37#include "compiler.h"
38#include "miscstruct.h"
39#include "xf86i2c.h"
40#include "xf86Crtc.h"
41#ifdef HAVE_XEXTPROTO_71
42#include <X11/extensions/dpmsconst.h>
43#else
44#define DPMS_SERVER
45#include <X11/extensions/dpms.h>
46#endif
47
48
49#include "../i2c_vid.h"
50#include "ch7xxx.h"
51#include "ch7xxx_reg.h"
52
53/** @file
54 * driver for the Chrontel 7xxx DVI chip over DVO.
55 */
56
57static struct ch7xxx_id_struct {
58    uint8_t vid;
59    char *name;
60} ch7xxx_ids[] = {
61	{ CH7011_VID, "CH7011" },
62	{ CH7009A_VID, "CH7009A" },
63	{ CH7009B_VID, "CH7009B" },
64	{ CH7301_VID, "CH7301" },
65};
66
67#define ID_ARRAY_SIZE (sizeof(ch7xxx_ids) / sizeof(ch7xxx_ids[0]))
68
69struct ch7xxx_reg_state {
70    uint8_t regs[CH7xxx_NUM_REGS];
71};
72
73struct ch7xxx_priv {
74    I2CDevRec d;
75    Bool quiet;
76
77    struct ch7xxx_reg_state SavedReg;
78    struct ch7xxx_reg_state ModeReg;
79    uint8_t save_TCTL, save_TPCP, save_TPD, save_TPVT;
80    uint8_t save_TLPF, save_TCT, save_PM, save_IDF;
81};
82
83static void ch7xxx_save(I2CDevPtr d);
84
85static char *ch7xxx_get_id(uint8_t vid)
86{
87    int i;
88
89    for (i = 0; i < ID_ARRAY_SIZE; i++) {
90        if (ch7xxx_ids[i].vid == vid)
91		return ch7xxx_ids[i].name;
92    }
93
94    return NULL;
95}
96
97/** Reads an 8 bit register */
98static Bool
99ch7xxx_read(struct ch7xxx_priv *dev_priv, int addr, unsigned char *ch)
100{
101    if (!xf86I2CReadByte(&dev_priv->d, addr, ch)) {
102	if (!dev_priv->quiet) {
103	    xf86DrvMsg(dev_priv->d.pI2CBus->scrnIndex,
104		       X_ERROR, "Unable to read from %s Slave %d.\n",
105		       dev_priv->d.pI2CBus->BusName, dev_priv->d.SlaveAddr);
106	}
107	return FALSE;
108    }
109
110    return TRUE;
111}
112
113/** Writes an 8 bit register */
114static Bool
115ch7xxx_write(struct ch7xxx_priv *dev_priv, int addr, unsigned char ch)
116{
117    if (!xf86I2CWriteByte(&dev_priv->d, addr, ch)) {
118	if (!dev_priv->quiet) {
119	    xf86DrvMsg(dev_priv->d.pI2CBus->scrnIndex, X_ERROR,
120		       "Unable to write to %s Slave %d.\n",
121		       dev_priv->d.pI2CBus->BusName, dev_priv->d.SlaveAddr);
122	}
123	return FALSE;
124    }
125
126    return TRUE;
127}
128
129static void *
130ch7xxx_init(I2CBusPtr b, I2CSlaveAddr addr)
131{
132    /* this will detect the CH7xxx chip on the specified i2c bus */
133    struct ch7xxx_priv *dev_priv;
134    uint8_t vendor, device;
135    char *name;
136
137    dev_priv = xcalloc(1, sizeof(struct ch7xxx_priv));
138    if (dev_priv == NULL)
139	return NULL;
140
141    dev_priv->d.DevName = "CH7xxx TMDS Controller";
142    dev_priv->d.SlaveAddr = addr;
143    dev_priv->d.pI2CBus = b;
144    dev_priv->d.StartTimeout = b->StartTimeout;
145    dev_priv->d.BitTimeout = b->BitTimeout;
146    dev_priv->d.AcknTimeout = b->AcknTimeout;
147    dev_priv->d.ByteTimeout = b->ByteTimeout;
148    dev_priv->d.DriverPrivate.ptr = dev_priv;
149
150    dev_priv->quiet = TRUE;
151    if (!ch7xxx_read(dev_priv, CH7xxx_REG_VID, &vendor))
152	goto out;
153
154    name = ch7xxx_get_id(vendor);
155    if (!name) {
156	xf86DrvMsg(dev_priv->d.pI2CBus->scrnIndex, X_INFO,
157		   "ch7xxx not detected; got 0x%02x from %s slave %d.\n",
158		   vendor, dev_priv->d.pI2CBus->BusName,
159		   dev_priv->d.SlaveAddr);
160	goto out;
161    }
162
163
164    if (!ch7xxx_read(dev_priv, CH7xxx_REG_DID, &device))
165	goto out;
166
167    if (device != CH7xxx_DID) {
168	xf86DrvMsg(dev_priv->d.pI2CBus->scrnIndex, X_INFO,
169		   "ch7xxx not detected; got 0x%02x from %s slave %d.\n",
170		   device, dev_priv->d.pI2CBus->BusName,
171		   dev_priv->d.SlaveAddr);
172	goto out;
173    }
174    dev_priv->quiet = FALSE;
175
176    xf86DrvMsg(dev_priv->d.pI2CBus->scrnIndex, X_INFO,
177	       "Detected %s chipset, vendor/device ID 0x%02x/0x%02x\n",
178	       name, vendor, device);
179
180    if (!xf86I2CDevInit(&dev_priv->d)) {
181	goto out;
182    }
183
184    return dev_priv;
185
186out:
187    xfree(dev_priv);
188    return NULL;
189}
190
191static xf86OutputStatus
192ch7xxx_detect(I2CDevPtr d)
193{
194    struct ch7xxx_priv *dev_priv = d->DriverPrivate.ptr;
195    uint8_t cdet, orig_pm, pm;
196
197    ch7xxx_read(dev_priv, CH7xxx_PM, &orig_pm);
198
199    pm = orig_pm;
200    pm &= ~CH7xxx_PM_FPD;
201    pm |= CH7xxx_PM_DVIL | CH7xxx_PM_DVIP;
202
203    ch7xxx_write(dev_priv, CH7xxx_PM, pm);
204
205    ch7xxx_read(dev_priv, CH7xxx_CONNECTION_DETECT, &cdet);
206
207    ch7xxx_write(dev_priv, CH7xxx_PM, orig_pm);
208
209    if (cdet & CH7xxx_CDET_DVI)
210    	return XF86OutputStatusConnected;
211    return XF86OutputStatusDisconnected;
212}
213
214static ModeStatus
215ch7xxx_mode_valid(I2CDevPtr d, DisplayModePtr mode)
216{
217    if (mode->Clock > 165000)
218	return MODE_CLOCK_HIGH;
219
220    return MODE_OK;
221}
222
223static void
224ch7xxx_mode_set(I2CDevPtr d, DisplayModePtr mode, DisplayModePtr adjusted_mode)
225{
226    struct ch7xxx_priv *dev_priv = d->DriverPrivate.ptr;
227    uint8_t tvco, tpcp, tpd, tlpf, idf;
228
229    if (mode->Clock <= 65000) {
230	tvco = 0x23;
231	tpcp = 0x08;
232	tpd = 0x16;
233	tlpf = 0x60;
234    } else {
235	tvco = 0x2d;
236	tpcp = 0x06;
237	tpd = 0x26;
238	tlpf = 0xa0;
239    }
240
241    ch7xxx_write(dev_priv, CH7xxx_TCTL, 0x00);
242    ch7xxx_write(dev_priv, CH7xxx_TVCO, tvco);
243    ch7xxx_write(dev_priv, CH7xxx_TPCP, tpcp);
244    ch7xxx_write(dev_priv, CH7xxx_TPD, tpd);
245    ch7xxx_write(dev_priv, CH7xxx_TPVT, 0x30);
246    ch7xxx_write(dev_priv, CH7xxx_TLPF, tlpf);
247    ch7xxx_write(dev_priv, CH7xxx_TCT, 0x00);
248
249    ch7xxx_read(dev_priv, CH7xxx_IDF, &idf);
250
251    idf &= ~(CH7xxx_IDF_HSP | CH7xxx_IDF_VSP);
252    if (mode->Flags & V_PHSYNC)
253	idf |= CH7xxx_IDF_HSP;
254
255    if (mode->Flags & V_PVSYNC)
256	idf |= CH7xxx_IDF_HSP;
257
258    ch7xxx_write(dev_priv, CH7xxx_IDF, idf);
259}
260
261/* set the CH7xxx power state */
262static void
263ch7xxx_dpms(I2CDevPtr d, int mode)
264{
265    struct ch7xxx_priv *dev_priv = d->DriverPrivate.ptr;
266
267    if (mode == DPMSModeOn)
268	ch7xxx_write(dev_priv, CH7xxx_PM, CH7xxx_PM_DVIL | CH7xxx_PM_DVIP);
269    else
270	ch7xxx_write(dev_priv, CH7xxx_PM, CH7xxx_PM_FPD);
271}
272
273static void
274ch7xxx_dump_regs(I2CDevPtr d)
275{
276    struct ch7xxx_priv *dev_priv = d->DriverPrivate.ptr;
277    int i;
278
279    for (i = 0; i < CH7xxx_NUM_REGS; i++) {
280	if (( i % 8 ) == 0 )
281	    ErrorF("\n %02X: ", i);
282	ErrorF("%02X ", dev_priv->ModeReg.regs[i]);
283    }
284}
285
286static void
287ch7xxx_save(I2CDevPtr d)
288{
289    struct ch7xxx_priv *dev_priv = d->DriverPrivate.ptr;
290
291    ch7xxx_read(dev_priv, CH7xxx_TCTL, &dev_priv->save_TCTL);
292    ch7xxx_read(dev_priv, CH7xxx_TPCP, &dev_priv->save_TPCP);
293    ch7xxx_read(dev_priv, CH7xxx_TPD, &dev_priv->save_TPD);
294    ch7xxx_read(dev_priv, CH7xxx_TPVT, &dev_priv->save_TPVT);
295    ch7xxx_read(dev_priv, CH7xxx_TLPF, &dev_priv->save_TLPF);
296    ch7xxx_read(dev_priv, CH7xxx_PM, &dev_priv->save_PM);
297    ch7xxx_read(dev_priv, CH7xxx_IDF, &dev_priv->save_IDF);
298}
299
300static void
301ch7xxx_restore(I2CDevPtr d)
302{
303    struct ch7xxx_priv *dev_priv = d->DriverPrivate.ptr;
304
305    ch7xxx_write(dev_priv, CH7xxx_TCTL, dev_priv->save_TCTL);
306    ch7xxx_write(dev_priv, CH7xxx_TPCP, dev_priv->save_TPCP);
307    ch7xxx_write(dev_priv, CH7xxx_TPD, dev_priv->save_TPD);
308    ch7xxx_write(dev_priv, CH7xxx_TPVT, dev_priv->save_TPVT);
309    ch7xxx_write(dev_priv, CH7xxx_TLPF, dev_priv->save_TLPF);
310    ch7xxx_write(dev_priv, CH7xxx_IDF, dev_priv->save_IDF);
311    ch7xxx_write(dev_priv, CH7xxx_PM, dev_priv->save_PM);
312}
313
314_X_EXPORT I830I2CVidOutputRec CH7xxxVidOutput = {
315    .init = ch7xxx_init,
316    .detect = ch7xxx_detect,
317    .mode_valid = ch7xxx_mode_valid,
318    .mode_set = ch7xxx_mode_set,
319    .dpms = ch7xxx_dpms,
320    .dump_regs = ch7xxx_dump_regs,
321    .save = ch7xxx_save,
322    .restore = ch7xxx_restore,
323};
324