1/* $NetBSD: cx24227.c,v 1.4 2011/08/05 21:19:23 jmcneill Exp $ */
2
3/*
4 * Copyright (c) 2008, 2011 Jonathan A. Kollasch
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
23 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
25 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
26 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30__KERNEL_RCSID(0, "$NetBSD: cx24227.c,v 1.4 2011/08/05 21:19:23 jmcneill Exp $");
31
32#include <sys/param.h>
33#include <sys/systm.h>
34#include <sys/device.h>
35#include <sys/kmem.h>
36#include <sys/module.h>
37
38#include <dev/i2c/cx24227var.h>
39
40/* #define CX24227_DEBUG */
41
42struct cx24227 {
43	device_t        parent;
44	i2c_tag_t       tag;
45	i2c_addr_t      addr;
46};
47
48static int cx24227_writereg(struct cx24227 *, uint8_t, uint16_t);
49static int cx24227_readreg(struct cx24227 *, uint8_t, uint16_t *);
50
51static int cx24227_init(struct cx24227 *);
52
53static struct documentation_wanted {
54	uint8_t		r;
55	uint16_t	v;
56} documentation_wanted[] = {
57	{ 0x00, 0x0071, },
58	{ 0x01, 0x3213, },
59	{ 0x09, 0x0025, },
60	{ 0x1c, 0x001d, },
61	{ 0x1f, 0x002d, },
62	{ 0x20, 0x001d, },
63	{ 0x22, 0x0022, },
64	{ 0x23, 0x0020, },
65	{ 0x29, 0x110f, },
66	{ 0x2a, 0x10b4, },
67	{ 0x2b, 0x10ae, },
68	{ 0x2c, 0x0031, },
69	{ 0x31, 0x010d, },
70	{ 0x32, 0x0100, },
71	{ 0x44, 0x0510, },
72	{ 0x54, 0x0104, },
73	{ 0x58, 0x2222, },
74	{ 0x59, 0x1162, },
75	{ 0x5a, 0x3211, },
76	{ 0x5d, 0x0370, },
77	{ 0x5e, 0x0296, },
78	{ 0x61, 0x0010, },
79	{ 0x63, 0x4a00, },
80	{ 0x65, 0x0800, },
81	{ 0x71, 0x0003, },
82	{ 0x72, 0x0470, },
83	{ 0x81, 0x0002, },
84	{ 0x82, 0x0600, },
85	{ 0x86, 0x0002, },
86	{ 0x8a, 0x2c38, },
87	{ 0x8b, 0x2a37, },
88	{ 0x92, 0x302f, },
89	{ 0x93, 0x3332, },
90	{ 0x96, 0x000c, },
91	{ 0x99, 0x0101, },
92	{ 0x9c, 0x2e37, },
93	{ 0x9d, 0x2c37, },
94	{ 0x9e, 0x2c37, },
95	{ 0xab, 0x0100, },
96	{ 0xac, 0x1003, },
97	{ 0xad, 0x103f, },
98	{ 0xe2, 0x0100, },
99	{ 0xe3, 0x1000, },
100	{ 0x28, 0x1010, },
101	{ 0xb1, 0x000e, },
102};
103
104
105static int
106cx24227_writereg(struct cx24227 *sc, uint8_t reg, uint16_t data)
107{
108	int error;
109	uint8_t r[3];
110
111	if (iic_acquire_bus(sc->tag, I2C_F_POLL) != 0)
112		return false;
113
114	r[0] = reg;
115	r[1] = (data >> 8) & 0xff;
116	r[2] = data & 0xff;
117	error = iic_exec(sc->tag, I2C_OP_WRITE_WITH_STOP, sc->addr,
118	    r, 3, NULL, 0, I2C_F_POLL);
119
120	iic_release_bus(sc->tag, I2C_F_POLL);
121
122	return error;
123}
124
125static int
126cx24227_readreg(struct cx24227 *sc, uint8_t reg, uint16_t *data)
127{
128	int error;
129	uint8_t r[2];
130
131	if (iic_acquire_bus(sc->tag, I2C_F_POLL) != 0)
132		return -1;
133
134	*data = 0x0000;
135
136	error = iic_exec(sc->tag, I2C_OP_READ_WITH_STOP, sc->addr,
137			 &reg, 1, r, 2, I2C_F_POLL);
138
139	iic_release_bus(sc->tag, I2C_F_POLL);
140
141	*data |= r[0] << 8;
142	*data |= r[1];
143
144	return error;
145}
146
147uint16_t
148cx24227_get_signal(struct cx24227 *sc)
149{
150	uint16_t sig = 0;
151
152	cx24227_readreg(sc, 0xf1, &sig);
153
154	return sig;
155}
156
157fe_status_t
158cx24227_get_dtv_status(struct cx24227 *sc)
159{
160	uint16_t reg;
161	fe_status_t status = 0;
162
163	cx24227_readreg(sc, 0xf1, &reg);
164
165	if(reg & 0x1000)
166		status = FE_HAS_VITERBI | FE_HAS_CARRIER | FE_HAS_SIGNAL;
167	if(reg & 0x8000)
168		status |= FE_HAS_LOCK | FE_HAS_SYNC;
169
170	return status;
171}
172
173int
174cx24227_set_modulation(struct cx24227 *sc, fe_modulation_t modulation)
175{
176	switch (modulation) {
177	case VSB_8:
178	case QAM_64:
179	case QAM_256:
180	case QAM_AUTO:
181		break;
182	default:
183		return EINVAL;
184	}
185
186	/* soft reset */
187	cx24227_writereg(sc, 0xf5, 0x0000);
188	cx24227_writereg(sc, 0xf5, 0x0001);
189
190	switch (modulation) {
191	case VSB_8:
192		/* VSB8 */
193		cx24227_writereg(sc, 0xf4, 0x0000);
194		break;
195	default:
196		/* QAM */
197		cx24227_writereg(sc, 0xf4, 0x0001);
198		cx24227_writereg(sc, 0x85, 0x0110);
199		break;
200	}
201
202	/* soft reset */
203	cx24227_writereg(sc, 0xf5, 0x0000);
204	cx24227_writereg(sc, 0xf5, 0x0001);
205
206#if 0
207	delay(100);
208
209	/* open the i2c gate */
210	cx24227_writereg(sc, 0xf3, 0x0001);
211
212	/* we could tune in here? */
213
214	/* close the i2c gate */
215	cx24227_writereg(sc, 0xf3, 0x0000);
216
217#endif
218	return 0;
219}
220
221void
222cx24227_enable(struct cx24227* sc, bool enable)
223{
224	if (enable == true) {
225		cx24227_init(sc);
226	}
227}
228
229struct cx24227 *
230cx24227_open(device_t parent, i2c_tag_t tag, i2c_addr_t addr)
231{
232	struct cx24227 *sc;
233	int e;
234	uint16_t value;
235
236	sc = kmem_alloc(sizeof(*sc), KM_SLEEP);
237	if (sc == NULL)
238		return NULL;
239
240	sc->parent = parent;
241	sc->tag = tag;
242	sc->addr = addr;
243
244	/* read chip ids */
245	value = 0;
246	e = cx24227_readreg(sc, 0x04, &value);
247	if (e) {
248		device_printf(parent, "cx24227: read failed: %d\n", e);
249		kmem_free(sc, sizeof(*sc));
250		return NULL;
251	}
252#ifdef CX24227_DEBUG
253	device_printf(parent, "cx24227: chipid %04x\n", value);
254#endif
255
256
257	value = 0x0001; /* open the i2c gate */
258	e = cx24227_writereg(sc, 0xf3, value);
259#if 0
260	if (e) {
261		device_printf(parent, "cx24227: write failed: %d\n", e);
262		kmem_free(sc, sizeof(*sc));
263		return NULL;
264	}
265#endif
266
267	cx24227_init(sc);
268
269	return sc;
270}
271
272void
273cx24227_close(struct cx24227 *sc)
274{
275	kmem_free(sc, sizeof(*sc));
276}
277
278
279static void
280cx24227_sleepreset(struct cx24227 *sc)
281{
282	cx24227_writereg(sc, 0xf2, 0);
283	cx24227_writereg(sc, 0xfa, 0);
284}
285
286static int
287cx24227_init(struct cx24227 *sc)
288{
289	unsigned int i;
290	uint16_t reg;
291
292	cx24227_sleepreset(sc);
293
294	for(i = 0; i < __arraycount(documentation_wanted); i++)
295		cx24227_writereg(sc, documentation_wanted[i].r, documentation_wanted[i].v);
296
297	/* Serial */
298	cx24227_readreg(sc, 0xab, &reg);
299	reg |= 0x0100;
300	cx24227_writereg(sc, 0xab, reg);
301
302	/* no spectral inversion */
303	cx24227_writereg(sc, 0x1b, 0x0110);
304
305	/* 44MHz IF */
306	cx24227_writereg(sc, 0x87, 0x01be);
307	cx24227_writereg(sc, 0x88, 0x0436);
308	cx24227_writereg(sc, 0x89, 0x054d);
309
310	/* GPIO on */
311	cx24227_readreg(sc, 0xe3, &reg);
312	reg |= 0x1100;
313	cx24227_writereg(sc, 0xe3, reg);
314
315	/* clocking */
316	cx24227_readreg(sc, 0xac, &reg);
317	reg &= ~0x3000;
318	reg |= 0x1000;
319	cx24227_writereg(sc, 0xac, reg);
320
321	/* soft reset */
322	cx24227_writereg(sc, 0xf5, 0x0000);
323	cx24227_writereg(sc, 0xf5, 0x0001);
324
325	/* open gate */
326	cx24227_writereg(sc, 0xf3, 0x0001);
327
328	return 0;
329}
330
331MODULE(MODULE_CLASS_DRIVER, cx24227, "iic");
332
333static int
334cx24227_modcmd(modcmd_t cmd, void *priv)
335{
336	if (cmd == MODULE_CMD_INIT || cmd == MODULE_CMD_FINI)
337		return 0;
338	return ENOTTY;
339}
340