1/* -*- c-basic-offset: 4 -*- */
2/*
3 * Copyright © 2007 Dave Mueller
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 * IN THE SOFTWARE.
23 *
24 * Authors:
25 *    Dave Mueller <dave.mueller@gmx.ch>
26 *
27 */
28
29#ifdef HAVE_CONFIG_H
30#include "config.h"
31#endif
32
33#include <stdint.h>
34
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 "tfp410.h"
51#include "tfp410_reg.h"
52
53typedef struct _TFP410SaveRec {
54    uint8_t ctl1;
55    uint8_t ctl2;
56} TFP410SaveRec;
57
58typedef struct {
59    I2CDevRec d;
60    Bool quiet;
61
62    TFP410SaveRec SavedReg;
63    TFP410SaveRec ModeReg;
64} TFP410Rec, *TFP410Ptr;
65
66#define TFPPTR(d) ((TFP410Ptr)(d->DriverPrivate.ptr))
67
68static Bool
69tfp410ReadByte(TFP410Ptr tfp, int addr, uint8_t *ch)
70{
71    if (!xf86I2CReadByte(&(tfp->d), addr, ch)) {
72	if (!tfp->quiet) {
73	    xf86DrvMsg(tfp->d.pI2CBus->scrnIndex, X_ERROR,
74		       "Unable to read from %s Slave %d.\n",
75		       tfp->d.pI2CBus->BusName, tfp->d.SlaveAddr);
76	}
77	return FALSE;
78    }
79    return TRUE;
80}
81
82static Bool
83tfp410WriteByte(TFP410Ptr tfp, int addr, uint8_t ch)
84{
85    if (!xf86I2CWriteByte(&(tfp->d), addr, ch)) {
86	if (!tfp->quiet) {
87	    xf86DrvMsg(tfp->d.pI2CBus->scrnIndex, X_ERROR,
88		       "Unable to write to %s Slave %d.\n",
89		       tfp->d.pI2CBus->BusName, tfp->d.SlaveAddr);
90	}
91	return FALSE;
92    }
93    return TRUE;
94}
95
96static int
97tfp410GetID(TFP410Ptr tfp, int addr)
98{
99    unsigned char ch1, ch2;
100
101    if (tfp410ReadByte(tfp, addr+0, &ch1) &&
102        tfp410ReadByte(tfp, addr+1, &ch2)) {
103
104	return ((ch2<<8) & 0xFF00) | (ch1 & 0x00FF);
105    }
106    return -1;
107}
108
109/* Ti TFP410 driver for chip on i2c bus */
110static void *
111tfp410_init(I2CBusPtr b, I2CSlaveAddr addr)
112{
113    /* this will detect the tfp410 chip on the specified i2c bus */
114    TFP410Ptr tfp;
115    int id;
116
117    tfp = xcalloc(1, sizeof(TFP410Rec));
118    if (tfp == NULL)
119	return NULL;
120
121    tfp->d.DevName = "TFP410 TMDS Controller";
122    tfp->d.SlaveAddr = addr;
123    tfp->d.pI2CBus = b;
124    tfp->d.StartTimeout = b->StartTimeout;
125    tfp->d.BitTimeout = b->BitTimeout;
126    tfp->d.AcknTimeout = b->AcknTimeout;
127    tfp->d.ByteTimeout = b->ByteTimeout;
128    tfp->d.DriverPrivate.ptr = tfp;
129    tfp->quiet = TRUE;
130
131    if ((id = tfp410GetID(tfp, TFP410_VID_LO)) != TFP410_VID) {
132	if (id != 0xffffffff) {
133	    xf86DrvMsg(tfp->d.pI2CBus->scrnIndex, X_ERROR,
134		       "tfp410 not detected got VID %X: from %s Slave %d.\n",
135		       id, tfp->d.pI2CBus->BusName, tfp->d.SlaveAddr);
136	}
137	goto out;
138    }
139
140    if ((id = tfp410GetID(tfp, TFP410_DID_LO)) != TFP410_DID) {
141	xf86DrvMsg(tfp->d.pI2CBus->scrnIndex, X_ERROR,
142		   "tfp410 not detected got DID %X: from %s Slave %d.\n",
143		   id, tfp->d.pI2CBus->BusName, tfp->d.SlaveAddr);
144	goto out;
145    }
146    tfp->quiet = FALSE;
147
148    if (!xf86I2CDevInit(&(tfp->d))) {
149	goto out;
150    }
151
152    return tfp;
153
154out:
155    xfree(tfp);
156    return NULL;
157}
158
159static xf86OutputStatus
160tfp410_detect(I2CDevPtr d)
161{
162    TFP410Ptr tfp = TFPPTR(d);
163    xf86OutputStatus ret = XF86OutputStatusDisconnected;
164    unsigned char ctl2;
165
166    if (tfp410ReadByte(tfp, TFP410_CTL_2, &ctl2)) {
167	if (ctl2 & TFP410_CTL_2_HTPLG)
168	    ret = XF86OutputStatusConnected;
169	else
170	    ret = XF86OutputStatusDisconnected;
171    }
172
173    return ret;
174}
175
176static ModeStatus
177tfp410_mode_valid(I2CDevPtr d, DisplayModePtr mode)
178{
179    return MODE_OK;
180}
181
182static void
183tfp410_mode_set(I2CDevPtr d, DisplayModePtr mode, DisplayModePtr adjusted_mode)
184{
185    /* As long as the basics are set up, since we don't have clock dependencies
186     * in the mode setup, we can just leave the registers alone and everything
187     * will work fine.
188     */
189    /* don't do much */
190    return;
191}
192
193/* set the tfp410 power state */
194static void
195tfp410_dpms(I2CDevPtr d, int mode)
196{
197    TFP410Ptr tfp = TFPPTR(d);
198    unsigned char ctl1;
199
200    if (!tfp410ReadByte(tfp, TFP410_CTL_1, &ctl1))
201	return;
202
203    if (mode == DPMSModeOn)
204	ctl1 |= TFP410_CTL_1_PD;
205    else
206	ctl1 &= ~TFP410_CTL_1_PD;
207
208    tfp410WriteByte(tfp, TFP410_CTL_1, ctl1);
209}
210
211static void
212tfp410_dump_regs(I2CDevPtr d)
213{
214    TFP410Ptr tfp = TFPPTR(d);
215    uint8_t val, val2;
216
217    tfp410ReadByte(tfp, TFP410_REV, &val);
218    xf86DrvMsg(tfp->d.pI2CBus->scrnIndex, X_INFO,
219	       "TFP410_REV: 0x%02X\n", val);
220    tfp410ReadByte(tfp, TFP410_CTL_1, &val);
221    xf86DrvMsg(tfp->d.pI2CBus->scrnIndex, X_INFO,
222	       "TFP410_CTL1: 0x%02X\n", val);
223    tfp410ReadByte(tfp, TFP410_CTL_2, &val);
224    xf86DrvMsg(tfp->d.pI2CBus->scrnIndex, X_INFO,
225	       "TFP410_CTL2: 0x%02X\n", val);
226    tfp410ReadByte(tfp, TFP410_CTL_3, &val);
227    xf86DrvMsg(tfp->d.pI2CBus->scrnIndex, X_INFO,
228	       "TFP410_CTL3: 0x%02X\n", val);
229    tfp410ReadByte(tfp, TFP410_USERCFG, &val);
230    xf86DrvMsg(tfp->d.pI2CBus->scrnIndex, X_INFO,
231	       "TFP410_USERCFG: 0x%02X\n", val);
232    tfp410ReadByte(tfp, TFP410_DE_DLY, &val);
233    xf86DrvMsg(tfp->d.pI2CBus->scrnIndex, X_INFO,
234	       "TFP410_DE_DLY: 0x%02X\n", val);
235    tfp410ReadByte(tfp, TFP410_DE_CTL, &val);
236    xf86DrvMsg(tfp->d.pI2CBus->scrnIndex, X_INFO,
237	       "TFP410_DE_CTL: 0x%02X\n", val);
238    tfp410ReadByte(tfp, TFP410_DE_TOP, &val);
239    xf86DrvMsg(tfp->d.pI2CBus->scrnIndex, X_INFO,
240	       "TFP410_DE_TOP: 0x%02X\n", val);
241    tfp410ReadByte(tfp, TFP410_DE_CNT_LO, &val);
242    tfp410ReadByte(tfp, TFP410_DE_CNT_HI, &val2);
243    xf86DrvMsg(tfp->d.pI2CBus->scrnIndex, X_INFO,
244	       "TFP410_DE_CNT: 0x%02X%02X\n", val2, val);
245    tfp410ReadByte(tfp, TFP410_DE_LIN_LO, &val);
246    tfp410ReadByte(tfp, TFP410_DE_LIN_HI, &val2);
247    xf86DrvMsg(tfp->d.pI2CBus->scrnIndex, X_INFO,
248	       "TFP410_DE_LIN: 0x%02X%02X\n", val2, val);
249    tfp410ReadByte(tfp, TFP410_H_RES_LO, &val);
250    tfp410ReadByte(tfp, TFP410_H_RES_HI, &val2);
251    xf86DrvMsg(tfp->d.pI2CBus->scrnIndex, X_INFO,
252	       "TFP410_H_RES: 0x%02X%02X\n", val2, val);
253    tfp410ReadByte(tfp, TFP410_V_RES_LO, &val);
254    tfp410ReadByte(tfp, TFP410_V_RES_HI, &val2);
255    xf86DrvMsg(tfp->d.pI2CBus->scrnIndex, X_INFO,
256	       "TFP410_V_RES: 0x%02X%02X\n", val2, val);
257}
258
259static void
260tfp410_save(I2CDevPtr d)
261{
262    TFP410Ptr tfp = TFPPTR(d);
263
264    if (!tfp410ReadByte(tfp, TFP410_CTL_1, &tfp->SavedReg.ctl1))
265	return;
266
267    if (!tfp410ReadByte(tfp, TFP410_CTL_2, &tfp->SavedReg.ctl2))
268	return;
269}
270
271static void
272tfp410_restore(I2CDevPtr d)
273{
274    TFP410Ptr tfp = TFPPTR(d);
275
276    /* Restore it powered down initially */
277    tfp410WriteByte(tfp, TFP410_CTL_1, tfp->SavedReg.ctl1 & ~0x1);
278
279    tfp410WriteByte(tfp, TFP410_CTL_2, tfp->SavedReg.ctl2);
280    tfp410WriteByte(tfp, TFP410_CTL_1, tfp->SavedReg.ctl1);
281}
282
283_X_EXPORT I830I2CVidOutputRec TFP410VidOutput = {
284    .init = tfp410_init,
285    .detect = tfp410_detect,
286    .mode_valid = tfp410_mode_valid,
287    .mode_set = tfp410_mode_set,
288    .dpms = tfp410_dpms,
289    .dump_regs = tfp410_dump_regs,
290    .save = tfp410_save,
291    .restore = tfp410_restore,
292};
293