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