aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/cx18/cx18-i2c.c
diff options
context:
space:
mode:
authorHans Verkuil <hverkuil@xs4all.nl>2008-04-28 19:24:33 -0400
committerMauro Carvalho Chehab <mchehab@infradead.org>2008-04-29 17:41:41 -0400
commit1c1e45d17b663d4749af456ab7c2fc1f36405ef8 (patch)
tree03704d6fd888c4c617baa81a60df0d80815b2607 /drivers/media/video/cx18/cx18-i2c.c
parentd74bee8b4776b5051c650a90f49a2022d46d8588 (diff)
V4L/DVB (7786): cx18: new driver for the Conexant CX23418 MPEG encoder chip
Many thanks to Steve Toth from Hauppauge and Nattu Dakshinamurthy from Conexant for their support. I am in particular thankful to Hauppauge since without their help this driver would not exist. It should also be noted that Steve did the work to get the DVB part up and running. Thank you! Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl> Signed-off-by: Steven Toth <stoth@hauppauge.com> Signed-off-by: Michael Krufky <mkrufky@linuxtv.org> Signed-off-by: G. Andrew Walls <awalls@radix.net> Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Diffstat (limited to 'drivers/media/video/cx18/cx18-i2c.c')
-rw-r--r--drivers/media/video/cx18/cx18-i2c.c431
1 files changed, 431 insertions, 0 deletions
diff --git a/drivers/media/video/cx18/cx18-i2c.c b/drivers/media/video/cx18/cx18-i2c.c
new file mode 100644
index 000000000000..18c88d1e4833
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-i2c.c
@@ -0,0 +1,431 @@
1/*
2 * cx18 I2C functions
3 *
4 * Derived from ivtv-i2c.c
5 *
6 * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21 * 02111-1307 USA
22 */
23
24#include "cx18-driver.h"
25#include "cx18-cards.h"
26#include "cx18-gpio.h"
27#include "cx18-av-core.h"
28
29#include <media/ir-kbd-i2c.h>
30
31#define CX18_REG_I2C_1_WR 0xf15000
32#define CX18_REG_I2C_1_RD 0xf15008
33#define CX18_REG_I2C_2_WR 0xf25100
34#define CX18_REG_I2C_2_RD 0xf25108
35
36#define SETSCL_BIT 0x0001
37#define SETSDL_BIT 0x0002
38#define GETSCL_BIT 0x0004
39#define GETSDL_BIT 0x0008
40
41#ifndef I2C_ADAP_CLASS_TV_ANALOG
42#define I2C_ADAP_CLASS_TV_ANALOG I2C_CLASS_TV_ANALOG
43#endif
44
45#define CX18_CS5345_I2C_ADDR 0x4c
46
47/* This array should match the CX18_HW_ defines */
48static const u8 hw_driverids[] = {
49 I2C_DRIVERID_TUNER,
50 I2C_DRIVERID_TVEEPROM,
51 I2C_DRIVERID_CS5345,
52 0, /* CX18_HW_GPIO dummy driver ID */
53 0 /* CX18_HW_CX23418 dummy driver ID */
54};
55
56/* This array should match the CX18_HW_ defines */
57static const u8 hw_addrs[] = {
58 0,
59 0,
60 CX18_CS5345_I2C_ADDR,
61 0, /* CX18_HW_GPIO dummy driver ID */
62 0, /* CX18_HW_CX23418 dummy driver ID */
63};
64
65/* This array should match the CX18_HW_ defines */
66/* This might well become a card-specific array */
67static const u8 hw_bus[] = {
68 0,
69 0,
70 0,
71 0, /* CX18_HW_GPIO dummy driver ID */
72 0, /* CX18_HW_CX23418 dummy driver ID */
73};
74
75/* This array should match the CX18_HW_ defines */
76static const char * const hw_drivernames[] = {
77 "tuner",
78 "tveeprom",
79 "cs5345",
80 "gpio",
81 "cx23418",
82};
83
84int cx18_i2c_register(struct cx18 *cx, unsigned idx)
85{
86 struct i2c_board_info info;
87 struct i2c_client *c;
88 u8 id, bus;
89 int i;
90
91 CX18_DEBUG_I2C("i2c client register\n");
92 if (idx >= ARRAY_SIZE(hw_driverids) || hw_driverids[idx] == 0)
93 return -1;
94 id = hw_driverids[idx];
95 bus = hw_bus[idx];
96 memset(&info, 0, sizeof(info));
97 strlcpy(info.driver_name, hw_drivernames[idx],
98 sizeof(info.driver_name));
99 info.addr = hw_addrs[idx];
100 for (i = 0; i < I2C_CLIENTS_MAX; i++)
101 if (cx->i2c_clients[i] == NULL)
102 break;
103
104 if (i == I2C_CLIENTS_MAX) {
105 CX18_ERR("insufficient room for new I2C client!\n");
106 return -ENOMEM;
107 }
108
109 if (id != I2C_DRIVERID_TUNER) {
110 c = i2c_new_device(&cx->i2c_adap[bus], &info);
111 if (c->driver == NULL)
112 i2c_unregister_device(c);
113 else
114 cx->i2c_clients[i] = c;
115 return cx->i2c_clients[i] ? 0 : -ENODEV;
116 }
117
118 /* special tuner handling */
119 c = i2c_new_probed_device(&cx->i2c_adap[1], &info, cx->card_i2c->radio);
120 if (c && c->driver == NULL)
121 i2c_unregister_device(c);
122 else if (c)
123 cx->i2c_clients[i++] = c;
124 c = i2c_new_probed_device(&cx->i2c_adap[1], &info, cx->card_i2c->demod);
125 if (c && c->driver == NULL)
126 i2c_unregister_device(c);
127 else if (c)
128 cx->i2c_clients[i++] = c;
129 c = i2c_new_probed_device(&cx->i2c_adap[1], &info, cx->card_i2c->tv);
130 if (c && c->driver == NULL)
131 i2c_unregister_device(c);
132 else if (c)
133 cx->i2c_clients[i++] = c;
134 return 0;
135}
136
137static int attach_inform(struct i2c_client *client)
138{
139 return 0;
140}
141
142static int detach_inform(struct i2c_client *client)
143{
144 int i;
145 struct cx18 *cx = (struct cx18 *)i2c_get_adapdata(client->adapter);
146
147 CX18_DEBUG_I2C("i2c client detach\n");
148 for (i = 0; i < I2C_CLIENTS_MAX; i++) {
149 if (cx->i2c_clients[i] == client) {
150 cx->i2c_clients[i] = NULL;
151 break;
152 }
153 }
154 CX18_DEBUG_I2C("i2c detach [client=%s,%s]\n",
155 client->name, (i < I2C_CLIENTS_MAX) ? "ok" : "failed");
156
157 return 0;
158}
159
160static void cx18_setscl(void *data, int state)
161{
162 struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx;
163 int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index;
164 u32 addr = bus_index ? CX18_REG_I2C_2_WR : CX18_REG_I2C_1_WR;
165 u32 r = read_reg(addr);
166
167 if (state)
168 write_reg_sync(r | SETSCL_BIT, addr);
169 else
170 write_reg_sync(r & ~SETSCL_BIT, addr);
171}
172
173static void cx18_setsda(void *data, int state)
174{
175 struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx;
176 int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index;
177 u32 addr = bus_index ? CX18_REG_I2C_2_WR : CX18_REG_I2C_1_WR;
178 u32 r = read_reg(addr);
179
180 if (state)
181 write_reg_sync(r | SETSDL_BIT, addr);
182 else
183 write_reg_sync(r & ~SETSDL_BIT, addr);
184}
185
186static int cx18_getscl(void *data)
187{
188 struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx;
189 int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index;
190 u32 addr = bus_index ? CX18_REG_I2C_2_RD : CX18_REG_I2C_1_RD;
191
192 return read_reg(addr) & GETSCL_BIT;
193}
194
195static int cx18_getsda(void *data)
196{
197 struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx;
198 int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index;
199 u32 addr = bus_index ? CX18_REG_I2C_2_RD : CX18_REG_I2C_1_RD;
200
201 return read_reg(addr) & GETSDL_BIT;
202}
203
204/* template for i2c-bit-algo */
205static struct i2c_adapter cx18_i2c_adap_template = {
206 .name = "cx18 i2c driver",
207 .id = I2C_HW_B_CX2341X,
208 .algo = NULL, /* set by i2c-algo-bit */
209 .algo_data = NULL, /* filled from template */
210 .client_register = attach_inform,
211 .client_unregister = detach_inform,
212 .owner = THIS_MODULE,
213};
214
215#define CX18_SCL_PERIOD (10) /* usecs. 10 usec is period for a 100 KHz clock */
216#define CX18_ALGO_BIT_TIMEOUT (2) /* seconds */
217
218static struct i2c_algo_bit_data cx18_i2c_algo_template = {
219 .setsda = cx18_setsda,
220 .setscl = cx18_setscl,
221 .getsda = cx18_getsda,
222 .getscl = cx18_getscl,
223 .udelay = CX18_SCL_PERIOD/2, /* 1/2 clock period in usec*/
224 .timeout = CX18_ALGO_BIT_TIMEOUT*HZ /* jiffies */
225};
226
227static struct i2c_client cx18_i2c_client_template = {
228 .name = "cx18 internal",
229};
230
231int cx18_call_i2c_client(struct cx18 *cx, int addr, unsigned cmd, void *arg)
232{
233 struct i2c_client *client;
234 int retval;
235 int i;
236
237 CX18_DEBUG_I2C("call_i2c_client addr=%02x\n", addr);
238 for (i = 0; i < I2C_CLIENTS_MAX; i++) {
239 client = cx->i2c_clients[i];
240 if (client == NULL || client->driver == NULL ||
241 client->driver->command == NULL)
242 continue;
243 if (addr == client->addr) {
244 retval = client->driver->command(client, cmd, arg);
245 return retval;
246 }
247 }
248 if (cmd != VIDIOC_G_CHIP_IDENT)
249 CX18_ERR("i2c addr 0x%02x not found for cmd 0x%x!\n",
250 addr, cmd);
251 return -ENODEV;
252}
253
254/* Find the i2c device based on the driver ID and return
255 its i2c address or -ENODEV if no matching device was found. */
256static int cx18_i2c_id_addr(struct cx18 *cx, u32 id)
257{
258 struct i2c_client *client;
259 int retval = -ENODEV;
260 int i;
261
262 for (i = 0; i < I2C_CLIENTS_MAX; i++) {
263 client = cx->i2c_clients[i];
264 if (client == NULL || client->driver == NULL)
265 continue;
266 if (id == client->driver->id) {
267 retval = client->addr;
268 break;
269 }
270 }
271 return retval;
272}
273
274/* Find the i2c device name matching the DRIVERID */
275static const char *cx18_i2c_id_name(u32 id)
276{
277 int i;
278
279 for (i = 0; i < ARRAY_SIZE(hw_driverids); i++)
280 if (hw_driverids[i] == id)
281 return hw_drivernames[i];
282 return "unknown device";
283}
284
285/* Find the i2c device name matching the CX18_HW_ flag */
286static const char *cx18_i2c_hw_name(u32 hw)
287{
288 int i;
289
290 for (i = 0; i < ARRAY_SIZE(hw_driverids); i++)
291 if (1 << i == hw)
292 return hw_drivernames[i];
293 return "unknown device";
294}
295
296/* Find the i2c device matching the CX18_HW_ flag and return
297 its i2c address or -ENODEV if no matching device was found. */
298int cx18_i2c_hw_addr(struct cx18 *cx, u32 hw)
299{
300 int i;
301
302 for (i = 0; i < ARRAY_SIZE(hw_driverids); i++)
303 if (1 << i == hw)
304 return cx18_i2c_id_addr(cx, hw_driverids[i]);
305 return -ENODEV;
306}
307
308/* Calls i2c device based on CX18_HW_ flag. If hw == 0, then do nothing.
309 If hw == CX18_HW_GPIO then call the gpio handler. */
310int cx18_i2c_hw(struct cx18 *cx, u32 hw, unsigned int cmd, void *arg)
311{
312 int addr;
313
314 if (hw == CX18_HW_GPIO || hw == 0)
315 return 0;
316 if (hw == CX18_HW_CX23418)
317 return cx18_av_cmd(cx, cmd, arg);
318
319 addr = cx18_i2c_hw_addr(cx, hw);
320 if (addr < 0) {
321 CX18_ERR("i2c hardware 0x%08x (%s) not found for cmd 0x%x!\n",
322 hw, cx18_i2c_hw_name(hw), cmd);
323 return addr;
324 }
325 return cx18_call_i2c_client(cx, addr, cmd, arg);
326}
327
328/* Calls i2c device based on I2C driver ID. */
329int cx18_i2c_id(struct cx18 *cx, u32 id, unsigned int cmd, void *arg)
330{
331 int addr;
332
333 addr = cx18_i2c_id_addr(cx, id);
334 if (addr < 0) {
335 if (cmd != VIDIOC_G_CHIP_IDENT)
336 CX18_ERR("i2c ID 0x%08x (%s) not found for cmd 0x%x!\n",
337 id, cx18_i2c_id_name(id), cmd);
338 return addr;
339 }
340 return cx18_call_i2c_client(cx, addr, cmd, arg);
341}
342
343/* broadcast cmd for all I2C clients and for the gpio subsystem */
344void cx18_call_i2c_clients(struct cx18 *cx, unsigned int cmd, void *arg)
345{
346 if (cx->i2c_adap[0].algo == NULL || cx->i2c_adap[1].algo == NULL) {
347 CX18_ERR("adapter is not set\n");
348 return;
349 }
350 cx18_av_cmd(cx, cmd, arg);
351 i2c_clients_command(&cx->i2c_adap[0], cmd, arg);
352 i2c_clients_command(&cx->i2c_adap[1], cmd, arg);
353}
354
355/* init + register i2c algo-bit adapter */
356int init_cx18_i2c(struct cx18 *cx)
357{
358 int i;
359 CX18_DEBUG_I2C("i2c init\n");
360
361 for (i = 0; i < 2; i++) {
362 memcpy(&cx->i2c_adap[i], &cx18_i2c_adap_template,
363 sizeof(struct i2c_adapter));
364 memcpy(&cx->i2c_algo[i], &cx18_i2c_algo_template,
365 sizeof(struct i2c_algo_bit_data));
366 cx->i2c_algo_cb_data[i].cx = cx;
367 cx->i2c_algo_cb_data[i].bus_index = i;
368 cx->i2c_algo[i].data = &cx->i2c_algo_cb_data[i];
369 cx->i2c_adap[i].algo_data = &cx->i2c_algo[i];
370
371 sprintf(cx->i2c_adap[i].name + strlen(cx->i2c_adap[i].name),
372 " #%d-%d", cx->num, i);
373 i2c_set_adapdata(&cx->i2c_adap[i], cx);
374
375 memcpy(&cx->i2c_client[i], &cx18_i2c_client_template,
376 sizeof(struct i2c_client));
377 sprintf(cx->i2c_client[i].name +
378 strlen(cx->i2c_client[i].name), "%d", i);
379 cx->i2c_client[i].adapter = &cx->i2c_adap[i];
380 cx->i2c_adap[i].dev.parent = &cx->dev->dev;
381 }
382
383 if (read_reg(CX18_REG_I2C_2_WR) != 0x0003c02f) {
384 /* Reset/Unreset I2C hardware block */
385 write_reg(0x10000000, 0xc71004); /* Clock select 220MHz */
386 write_reg_sync(0x10001000, 0xc71024); /* Clock Enable */
387 }
388 /* courtesy of Steven Toth <stoth@hauppauge.com> */
389 write_reg_sync(0x00c00000, 0xc7001c);
390 mdelay(10);
391 write_reg_sync(0x00c000c0, 0xc7001c);
392 mdelay(10);
393 write_reg_sync(0x00c00000, 0xc7001c);
394
395 write_reg_sync(0x00c00000, 0xc730c8); /* Set to edge-triggered intrs. */
396 write_reg_sync(0x00c00000, 0xc730c4); /* Clear any stale intrs */
397
398 /* Hw I2C1 Clock Freq ~100kHz */
399 write_reg_sync(0x00021c0f & ~4, CX18_REG_I2C_1_WR);
400 cx18_setscl(&cx->i2c_algo_cb_data[0], 1);
401 cx18_setsda(&cx->i2c_algo_cb_data[0], 1);
402
403 /* Hw I2C2 Clock Freq ~100kHz */
404 write_reg_sync(0x00021c0f & ~4, CX18_REG_I2C_2_WR);
405 cx18_setscl(&cx->i2c_algo_cb_data[1], 1);
406 cx18_setsda(&cx->i2c_algo_cb_data[1], 1);
407
408 return i2c_bit_add_bus(&cx->i2c_adap[0]) ||
409 i2c_bit_add_bus(&cx->i2c_adap[1]);
410}
411
412void exit_cx18_i2c(struct cx18 *cx)
413{
414 int i;
415 CX18_DEBUG_I2C("i2c exit\n");
416 write_reg(read_reg(CX18_REG_I2C_1_WR) | 4, CX18_REG_I2C_1_WR);
417 write_reg(read_reg(CX18_REG_I2C_2_WR) | 4, CX18_REG_I2C_2_WR);
418
419 for (i = 0; i < 2; i++) {
420 i2c_del_adapter(&cx->i2c_adap[i]);
421 }
422}
423
424/*
425 Hauppauge HVR1600 should have:
426 32 cx24227
427 98 unknown
428 a0 eeprom
429 c2 tuner
430 e? zilog ir
431 */