diff options
Diffstat (limited to 'drivers/media/video/cx18/cx18-i2c.c')
-rw-r--r-- | drivers/media/video/cx18/cx18-i2c.c | 284 |
1 files changed, 72 insertions, 212 deletions
diff --git a/drivers/media/video/cx18/cx18-i2c.c b/drivers/media/video/cx18/cx18-i2c.c index db7b55281f50..6357dc44ab5b 100644 --- a/drivers/media/video/cx18/cx18-i2c.c +++ b/drivers/media/video/cx18/cx18-i2c.c | |||
@@ -43,31 +43,34 @@ | |||
43 | #define CX18_CS5345_I2C_ADDR 0x4c | 43 | #define CX18_CS5345_I2C_ADDR 0x4c |
44 | 44 | ||
45 | /* This array should match the CX18_HW_ defines */ | 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[] = { | 46 | static const u8 hw_addrs[] = { |
56 | 0, | 47 | 0, /* CX18_HW_TUNER */ |
57 | 0, | 48 | 0, /* CX18_HW_TVEEPROM */ |
58 | CX18_CS5345_I2C_ADDR, | 49 | CX18_CS5345_I2C_ADDR, /* CX18_HW_CS5345 */ |
59 | 0, /* CX18_HW_GPIO dummy driver ID */ | 50 | 0, /* CX18_HW_DVB */ |
60 | 0, /* CX18_HW_CX23418 dummy driver ID */ | 51 | 0, /* CX18_HW_418_AV */ |
52 | 0, /* CX18_HW_GPIO_AUDIO_MUX */ | ||
61 | }; | 53 | }; |
62 | 54 | ||
63 | /* This array should match the CX18_HW_ defines */ | 55 | /* This array should match the CX18_HW_ defines */ |
64 | /* This might well become a card-specific array */ | 56 | /* This might well become a card-specific array */ |
65 | static const u8 hw_bus[] = { | 57 | static const u8 hw_bus[] = { |
66 | 0, | 58 | 1, /* CX18_HW_TUNER */ |
67 | 0, | 59 | 0, /* CX18_HW_TVEEPROM */ |
68 | 0, | 60 | 0, /* CX18_HW_CS5345 */ |
69 | 0, /* CX18_HW_GPIO dummy driver ID */ | 61 | 0, /* CX18_HW_DVB */ |
70 | 0, /* CX18_HW_CX23418 dummy driver ID */ | 62 | 0, /* CX18_HW_418_AV */ |
63 | 0, /* CX18_HW_GPIO_AUDIO_MUX */ | ||
64 | }; | ||
65 | |||
66 | /* This array should match the CX18_HW_ defines */ | ||
67 | static const char * const hw_modules[] = { | ||
68 | "tuner", /* CX18_HW_TUNER */ | ||
69 | NULL, /* CX18_HW_TVEEPROM */ | ||
70 | "cs5345", /* CX18_HW_CS5345 */ | ||
71 | NULL, /* CX18_HW_DVB */ | ||
72 | NULL, /* CX18_HW_418_AV */ | ||
73 | NULL, /* CX18_HW_GPIO_AUDIO_MUX */ | ||
71 | }; | 74 | }; |
72 | 75 | ||
73 | /* This array should match the CX18_HW_ defines */ | 76 | /* This array should match the CX18_HW_ defines */ |
@@ -75,83 +78,66 @@ static const char * const hw_devicenames[] = { | |||
75 | "tuner", | 78 | "tuner", |
76 | "tveeprom", | 79 | "tveeprom", |
77 | "cs5345", | 80 | "cs5345", |
78 | "gpio", | 81 | "cx23418_DTV", |
79 | "cx23418", | 82 | "cx23418_AV", |
83 | "gpio_audio_mux", | ||
80 | }; | 84 | }; |
81 | 85 | ||
82 | int cx18_i2c_register(struct cx18 *cx, unsigned idx) | 86 | int cx18_i2c_register(struct cx18 *cx, unsigned idx) |
83 | { | 87 | { |
84 | struct i2c_board_info info; | 88 | struct v4l2_subdev *sd; |
85 | struct i2c_client *c; | 89 | int bus = hw_bus[idx]; |
86 | u8 id, bus; | 90 | struct i2c_adapter *adap = &cx->i2c_adap[bus]; |
87 | int i; | 91 | const char *mod = hw_modules[idx]; |
88 | 92 | const char *type = hw_devicenames[idx]; | |
89 | CX18_DEBUG_I2C("i2c client register\n"); | 93 | u32 hw = 1 << idx; |
90 | if (idx >= ARRAY_SIZE(hw_driverids) || hw_driverids[idx] == 0) | 94 | |
95 | if (idx >= ARRAY_SIZE(hw_addrs)) | ||
91 | return -1; | 96 | return -1; |
92 | id = hw_driverids[idx]; | ||
93 | bus = hw_bus[idx]; | ||
94 | memset(&info, 0, sizeof(info)); | ||
95 | strlcpy(info.type, hw_devicenames[idx], sizeof(info.type)); | ||
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 | 97 | ||
106 | if (id != I2C_DRIVERID_TUNER) { | 98 | if (hw == CX18_HW_TUNER) { |
107 | c = i2c_new_device(&cx->i2c_adap[bus], &info); | 99 | /* special tuner group handling */ |
108 | if (c->driver == NULL) | 100 | sd = v4l2_i2c_new_probed_subdev(adap, mod, type, |
109 | i2c_unregister_device(c); | 101 | cx->card_i2c->radio); |
110 | else | 102 | if (sd != NULL) |
111 | cx->i2c_clients[i] = c; | 103 | sd->grp_id = hw; |
112 | return cx->i2c_clients[i] ? 0 : -ENODEV; | 104 | sd = v4l2_i2c_new_probed_subdev(adap, mod, type, |
105 | cx->card_i2c->demod); | ||
106 | if (sd != NULL) | ||
107 | sd->grp_id = hw; | ||
108 | sd = v4l2_i2c_new_probed_subdev(adap, mod, type, | ||
109 | cx->card_i2c->tv); | ||
110 | if (sd != NULL) | ||
111 | sd->grp_id = hw; | ||
112 | return sd != NULL ? 0 : -1; | ||
113 | } | 113 | } |
114 | 114 | ||
115 | /* special tuner handling */ | 115 | /* Is it not an I2C device or one we do not wish to register? */ |
116 | c = i2c_new_probed_device(&cx->i2c_adap[1], &info, cx->card_i2c->radio); | 116 | if (!hw_addrs[idx]) |
117 | if (c && c->driver == NULL) | 117 | return -1; |
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 | 118 | ||
134 | static int attach_inform(struct i2c_client *client) | 119 | /* It's an I2C device other than an analog tuner */ |
135 | { | 120 | sd = v4l2_i2c_new_subdev(adap, mod, type, hw_addrs[idx]); |
136 | return 0; | 121 | if (sd != NULL) |
122 | sd->grp_id = hw; | ||
123 | return sd != NULL ? 0 : -1; | ||
137 | } | 124 | } |
138 | 125 | ||
139 | static int detach_inform(struct i2c_client *client) | 126 | /* Find the first member of the subdev group id in hw */ |
127 | struct v4l2_subdev *cx18_find_hw(struct cx18 *cx, u32 hw) | ||
140 | { | 128 | { |
141 | int i; | 129 | struct v4l2_subdev *result = NULL; |
142 | struct cx18 *cx = (struct cx18 *)i2c_get_adapdata(client->adapter); | 130 | struct v4l2_subdev *sd; |
143 | 131 | ||
144 | CX18_DEBUG_I2C("i2c client detach\n"); | 132 | spin_lock(&cx->v4l2_dev.lock); |
145 | for (i = 0; i < I2C_CLIENTS_MAX; i++) { | 133 | v4l2_device_for_each_subdev(sd, &cx->v4l2_dev) { |
146 | if (cx->i2c_clients[i] == client) { | 134 | if (sd->grp_id == hw) { |
147 | cx->i2c_clients[i] = NULL; | 135 | result = sd; |
148 | break; | 136 | break; |
149 | } | 137 | } |
150 | } | 138 | } |
151 | CX18_DEBUG_I2C("i2c detach [client=%s,%s]\n", | 139 | spin_unlock(&cx->v4l2_dev.lock); |
152 | client->name, (i < I2C_CLIENTS_MAX) ? "ok" : "failed"); | 140 | return result; |
153 | |||
154 | return 0; | ||
155 | } | 141 | } |
156 | 142 | ||
157 | static void cx18_setscl(void *data, int state) | 143 | static void cx18_setscl(void *data, int state) |
@@ -204,8 +190,6 @@ static struct i2c_adapter cx18_i2c_adap_template = { | |||
204 | .id = I2C_HW_B_CX2341X, | 190 | .id = I2C_HW_B_CX2341X, |
205 | .algo = NULL, /* set by i2c-algo-bit */ | 191 | .algo = NULL, /* set by i2c-algo-bit */ |
206 | .algo_data = NULL, /* filled from template */ | 192 | .algo_data = NULL, /* filled from template */ |
207 | .client_register = attach_inform, | ||
208 | .client_unregister = detach_inform, | ||
209 | .owner = THIS_MODULE, | 193 | .owner = THIS_MODULE, |
210 | }; | 194 | }; |
211 | 195 | ||
@@ -221,151 +205,27 @@ static struct i2c_algo_bit_data cx18_i2c_algo_template = { | |||
221 | .timeout = CX18_ALGO_BIT_TIMEOUT*HZ /* jiffies */ | 205 | .timeout = CX18_ALGO_BIT_TIMEOUT*HZ /* jiffies */ |
222 | }; | 206 | }; |
223 | 207 | ||
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 | } | ||
245 | if (cmd != VIDIOC_DBG_G_CHIP_IDENT) | ||
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 | |||
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) | ||
278 | return hw_devicenames[i]; | ||
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 | |||
300 | if (hw == 0) | ||
301 | return 0; | ||
302 | |||
303 | if (hw == CX18_HW_GPIO) | ||
304 | return cx18_gpio(cx, cmd, arg); | ||
305 | |||
306 | if (hw == CX18_HW_CX23418) | ||
307 | return v4l2_subdev_command(cx->sd_av, cmd, arg); | ||
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 | |||
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 | } | ||
325 | v4l2_subdev_command(cx->sd_av, cmd, arg); | ||
326 | i2c_clients_command(&cx->i2c_adap[0], cmd, arg); | ||
327 | i2c_clients_command(&cx->i2c_adap[1], cmd, arg); | ||
328 | if (cx->hw_flags & CX18_HW_GPIO) | ||
329 | cx18_gpio(cx, cmd, arg); | ||
330 | } | ||
331 | |||
332 | /* init + register i2c algo-bit adapter */ | 208 | /* init + register i2c algo-bit adapter */ |
333 | int init_cx18_i2c(struct cx18 *cx) | 209 | int init_cx18_i2c(struct cx18 *cx) |
334 | { | 210 | { |
335 | int i; | 211 | int i; |
336 | CX18_DEBUG_I2C("i2c init\n"); | 212 | CX18_DEBUG_I2C("i2c init\n"); |
337 | 213 | ||
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 | |||
350 | for (i = 0; i < 2; i++) { | 214 | for (i = 0; i < 2; i++) { |
351 | memcpy(&cx->i2c_adap[i], &cx18_i2c_adap_template, | 215 | /* Setup algorithm for adapter */ |
352 | sizeof(struct i2c_adapter)); | ||
353 | memcpy(&cx->i2c_algo[i], &cx18_i2c_algo_template, | 216 | memcpy(&cx->i2c_algo[i], &cx18_i2c_algo_template, |
354 | sizeof(struct i2c_algo_bit_data)); | 217 | sizeof(struct i2c_algo_bit_data)); |
355 | cx->i2c_algo_cb_data[i].cx = cx; | 218 | cx->i2c_algo_cb_data[i].cx = cx; |
356 | cx->i2c_algo_cb_data[i].bus_index = i; | 219 | cx->i2c_algo_cb_data[i].bus_index = i; |
357 | cx->i2c_algo[i].data = &cx->i2c_algo_cb_data[i]; | 220 | cx->i2c_algo[i].data = &cx->i2c_algo_cb_data[i]; |
358 | cx->i2c_adap[i].algo_data = &cx->i2c_algo[i]; | ||
359 | 221 | ||
222 | /* Setup adapter */ | ||
223 | memcpy(&cx->i2c_adap[i], &cx18_i2c_adap_template, | ||
224 | sizeof(struct i2c_adapter)); | ||
225 | cx->i2c_adap[i].algo_data = &cx->i2c_algo[i]; | ||
360 | sprintf(cx->i2c_adap[i].name + strlen(cx->i2c_adap[i].name), | 226 | sprintf(cx->i2c_adap[i].name + strlen(cx->i2c_adap[i].name), |
361 | " #%d-%d", cx->instance, i); | 227 | " #%d-%d", cx->instance, i); |
362 | i2c_set_adapdata(&cx->i2c_adap[i], cx); | 228 | i2c_set_adapdata(&cx->i2c_adap[i], &cx->v4l2_dev); |
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]; | ||
369 | cx->i2c_adap[i].dev.parent = &cx->pci_dev->dev; | 229 | cx->i2c_adap[i].dev.parent = &cx->pci_dev->dev; |
370 | } | 230 | } |
371 | 231 | ||