aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media
diff options
context:
space:
mode:
authorRichard Röjfors <richard.rojfors@mocean-labs.com>2009-09-22 05:07:06 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2009-12-05 15:40:43 -0500
commit42752f7a3f4afbabb513d5769c590e9abe2d0cd6 (patch)
tree761b9729772e37fb07b27b8d691f33ceac864c3a /drivers/media
parent527aebf29c64a56557f9b44d2852ccb99f91ac3e (diff)
V4L/DVB (13176): adv7180: Support checking standard via interrupts
If the I2C device provides an interrupt it is registered and the standard is updated via interrupts rather than polling. Since I2C communication is needed, the interrupt handler fires off a work which will check the new standard, and store it in the internal structure. To handle mutual exclusion a mutex is introduced. Signed-off-by: Richard Röjfors <richard.rojfors@mocean-labs.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media')
-rw-r--r--drivers/media/video/adv7180.c185
1 files changed, 168 insertions, 17 deletions
diff --git a/drivers/media/video/adv7180.c b/drivers/media/video/adv7180.c
index 77eada64cf74..0826f0dabc17 100644
--- a/drivers/media/video/adv7180.c
+++ b/drivers/media/video/adv7180.c
@@ -27,6 +27,7 @@
27#include <linux/videodev2.h> 27#include <linux/videodev2.h>
28#include <media/v4l2-device.h> 28#include <media/v4l2-device.h>
29#include <media/v4l2-chip-ident.h> 29#include <media/v4l2-chip-ident.h>
30#include <linux/mutex.h>
30 31
31#define DRIVER_NAME "adv7180" 32#define DRIVER_NAME "adv7180"
32 33
@@ -48,9 +49,14 @@
48#define ADV7180_INPUT_CONTROL_PAL_SECAM 0xe0 49#define ADV7180_INPUT_CONTROL_PAL_SECAM 0xe0
49#define ADV7180_INPUT_CONTROL_PAL_SECAM_PED 0xf0 50#define ADV7180_INPUT_CONTROL_PAL_SECAM_PED 0xf0
50 51
51#define ADV7180_AUTODETECT_ENABLE_REG 0x07 52#define ADV7180_EXTENDED_OUTPUT_CONTROL_REG 0x04
52#define ADV7180_AUTODETECT_DEFAULT 0x7f 53#define ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS 0xC5
53 54
55#define ADV7180_AUTODETECT_ENABLE_REG 0x07
56#define ADV7180_AUTODETECT_DEFAULT 0x7f
57
58#define ADV7180_ADI_CTRL_REG 0x0e
59#define ADV7180_ADI_CTRL_IRQ_SPACE 0x20
54 60
55#define ADV7180_STATUS1_REG 0x10 61#define ADV7180_STATUS1_REG 0x10
56#define ADV7180_STATUS1_IN_LOCK 0x01 62#define ADV7180_STATUS1_IN_LOCK 0x01
@@ -67,9 +73,28 @@
67#define ADV7180_IDENT_REG 0x11 73#define ADV7180_IDENT_REG 0x11
68#define ADV7180_ID_7180 0x18 74#define ADV7180_ID_7180 0x18
69 75
76#define ADV7180_ICONF1_ADI 0x40
77#define ADV7180_ICONF1_ACTIVE_LOW 0x01
78#define ADV7180_ICONF1_PSYNC_ONLY 0x10
79#define ADV7180_ICONF1_ACTIVE_TO_CLR 0xC0
80
81#define ADV7180_IRQ1_LOCK 0x01
82#define ADV7180_IRQ1_UNLOCK 0x02
83#define ADV7180_ISR1_ADI 0x42
84#define ADV7180_ICR1_ADI 0x43
85#define ADV7180_IMR1_ADI 0x44
86#define ADV7180_IMR2_ADI 0x48
87#define ADV7180_IRQ3_AD_CHANGE 0x08
88#define ADV7180_ISR3_ADI 0x4A
89#define ADV7180_ICR3_ADI 0x4B
90#define ADV7180_IMR3_ADI 0x4C
91#define ADV7180_IMR4_ADI 0x50
70 92
71struct adv7180_state { 93struct adv7180_state {
72 struct v4l2_subdev sd; 94 struct v4l2_subdev sd;
95 struct work_struct work;
96 struct mutex mutex; /* mutual excl. when accessing chip */
97 int irq;
73 v4l2_std_id curr_norm; 98 v4l2_std_id curr_norm;
74 bool autodetect; 99 bool autodetect;
75}; 100};
@@ -153,19 +178,30 @@ static inline struct adv7180_state *to_state(struct v4l2_subdev *sd)
153static int adv7180_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) 178static int adv7180_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
154{ 179{
155 struct adv7180_state *state = to_state(sd); 180 struct adv7180_state *state = to_state(sd);
156 int err = 0; 181 int err = mutex_lock_interruptible(&state->mutex);
182 if (err)
183 return err;
157 184
158 if (!state->autodetect) 185 /* when we are interrupt driven we know the state */
186 if (!state->autodetect || state->irq > 0)
159 *std = state->curr_norm; 187 *std = state->curr_norm;
160 else 188 else
161 err = __adv7180_status(v4l2_get_subdevdata(sd), NULL, std); 189 err = __adv7180_status(v4l2_get_subdevdata(sd), NULL, std);
162 190
191 mutex_unlock(&state->mutex);
163 return err; 192 return err;
164} 193}
165 194
166static int adv7180_g_input_status(struct v4l2_subdev *sd, u32 *status) 195static int adv7180_g_input_status(struct v4l2_subdev *sd, u32 *status)
167{ 196{
168 return __adv7180_status(v4l2_get_subdevdata(sd), status, NULL); 197 struct adv7180_state *state = to_state(sd);
198 int ret = mutex_lock_interruptible(&state->mutex);
199 if (ret)
200 return ret;
201
202 ret = __adv7180_status(v4l2_get_subdevdata(sd), status, NULL);
203 mutex_unlock(&state->mutex);
204 return ret;
169} 205}
170 206
171static int adv7180_g_chip_ident(struct v4l2_subdev *sd, 207static int adv7180_g_chip_ident(struct v4l2_subdev *sd,
@@ -180,7 +216,9 @@ static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
180{ 216{
181 struct adv7180_state *state = to_state(sd); 217 struct adv7180_state *state = to_state(sd);
182 struct i2c_client *client = v4l2_get_subdevdata(sd); 218 struct i2c_client *client = v4l2_get_subdevdata(sd);
183 int ret; 219 int ret = mutex_lock_interruptible(&state->mutex);
220 if (ret)
221 return ret;
184 222
185 /* all standards -> autodetect */ 223 /* all standards -> autodetect */
186 if (std == V4L2_STD_ALL) { 224 if (std == V4L2_STD_ALL) {
@@ -190,6 +228,7 @@ static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
190 if (ret < 0) 228 if (ret < 0)
191 goto out; 229 goto out;
192 230
231 __adv7180_status(client, NULL, &state->curr_norm);
193 state->autodetect = true; 232 state->autodetect = true;
194 } else { 233 } else {
195 ret = v4l2_std_to_adv7180(std); 234 ret = v4l2_std_to_adv7180(std);
@@ -206,6 +245,7 @@ static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
206 } 245 }
207 ret = 0; 246 ret = 0;
208out: 247out:
248 mutex_unlock(&state->mutex);
209 return ret; 249 return ret;
210} 250}
211 251
@@ -224,6 +264,39 @@ static const struct v4l2_subdev_ops adv7180_ops = {
224 .video = &adv7180_video_ops, 264 .video = &adv7180_video_ops,
225}; 265};
226 266
267static void adv7180_work(struct work_struct *work)
268{
269 struct adv7180_state *state = container_of(work, struct adv7180_state,
270 work);
271 struct i2c_client *client = v4l2_get_subdevdata(&state->sd);
272 u8 isr3;
273
274 mutex_lock(&state->mutex);
275 i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG,
276 ADV7180_ADI_CTRL_IRQ_SPACE);
277 isr3 = i2c_smbus_read_byte_data(client, ADV7180_ISR3_ADI);
278 /* clear */
279 i2c_smbus_write_byte_data(client, ADV7180_ICR3_ADI, isr3);
280 i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, 0);
281
282 if (isr3 & ADV7180_IRQ3_AD_CHANGE && state->autodetect)
283 __adv7180_status(client, NULL, &state->curr_norm);
284 mutex_unlock(&state->mutex);
285
286 enable_irq(state->irq);
287}
288
289static irqreturn_t adv7180_irq(int irq, void *devid)
290{
291 struct adv7180_state *state = devid;
292
293 schedule_work(&state->work);
294
295 disable_irq_nosync(state->irq);
296
297 return IRQ_HANDLED;
298}
299
227/* 300/*
228 * Generic i2c probe 301 * Generic i2c probe
229 * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' 302 * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
@@ -244,33 +317,111 @@ static __devinit int adv7180_probe(struct i2c_client *client,
244 client->addr << 1, client->adapter->name); 317 client->addr << 1, client->adapter->name);
245 318
246 state = kzalloc(sizeof(struct adv7180_state), GFP_KERNEL); 319 state = kzalloc(sizeof(struct adv7180_state), GFP_KERNEL);
247 if (state == NULL) 320 if (state == NULL) {
248 return -ENOMEM; 321 ret = -ENOMEM;
322 goto err;
323 }
324
325 state->irq = client->irq;
326 INIT_WORK(&state->work, adv7180_work);
327 mutex_init(&state->mutex);
249 state->autodetect = true; 328 state->autodetect = true;
250 sd = &state->sd; 329 sd = &state->sd;
251 v4l2_i2c_subdev_init(sd, client, &adv7180_ops); 330 v4l2_i2c_subdev_init(sd, client, &adv7180_ops);
252 331
253 /* Initialize adv7180 */ 332 /* Initialize adv7180 */
254 /* enable autodetection */ 333 /* Enable autodetection */
255 ret = i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG, 334 ret = i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG,
256 ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM); 335 ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM);
257 if (ret > 0) 336 if (ret < 0)
258 ret = i2c_smbus_write_byte_data(client, 337 goto err_unreg_subdev;
259 ADV7180_AUTODETECT_ENABLE_REG, 338
260 ADV7180_AUTODETECT_DEFAULT); 339 ret = i2c_smbus_write_byte_data(client, ADV7180_AUTODETECT_ENABLE_REG,
261 if (ret < 0) { 340 ADV7180_AUTODETECT_DEFAULT);
262 printk(KERN_ERR DRIVER_NAME 341 if (ret < 0)
263 ": Failed to communicate to chip: %d\n", ret); 342 goto err_unreg_subdev;
264 return ret; 343
344 /* ITU-R BT.656-4 compatible */
345 ret = i2c_smbus_write_byte_data(client,
346 ADV7180_EXTENDED_OUTPUT_CONTROL_REG,
347 ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS);
348 if (ret < 0)
349 goto err_unreg_subdev;
350
351 /* read current norm */
352 __adv7180_status(client, NULL, &state->curr_norm);
353
354 /* register for interrupts */
355 if (state->irq > 0) {
356 ret = request_irq(state->irq, adv7180_irq, 0, DRIVER_NAME,
357 state);
358 if (ret)
359 goto err_unreg_subdev;
360
361 ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG,
362 ADV7180_ADI_CTRL_IRQ_SPACE);
363 if (ret < 0)
364 goto err_unreg_subdev;
365
366 /* config the Interrupt pin to be active low */
367 ret = i2c_smbus_write_byte_data(client, ADV7180_ICONF1_ADI,
368 ADV7180_ICONF1_ACTIVE_LOW | ADV7180_ICONF1_PSYNC_ONLY);
369 if (ret < 0)
370 goto err_unreg_subdev;
371
372 ret = i2c_smbus_write_byte_data(client, ADV7180_IMR1_ADI, 0);
373 if (ret < 0)
374 goto err_unreg_subdev;
375
376 ret = i2c_smbus_write_byte_data(client, ADV7180_IMR2_ADI, 0);
377 if (ret < 0)
378 goto err_unreg_subdev;
379
380 /* enable AD change interrupts interrupts */
381 ret = i2c_smbus_write_byte_data(client, ADV7180_IMR3_ADI,
382 ADV7180_IRQ3_AD_CHANGE);
383 if (ret < 0)
384 goto err_unreg_subdev;
385
386 ret = i2c_smbus_write_byte_data(client, ADV7180_IMR4_ADI, 0);
387 if (ret < 0)
388 goto err_unreg_subdev;
389
390 ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG,
391 0);
392 if (ret < 0)
393 goto err_unreg_subdev;
265 } 394 }
266 395
267 return 0; 396 return 0;
397
398err_unreg_subdev:
399 mutex_destroy(&state->mutex);
400 v4l2_device_unregister_subdev(sd);
401 kfree(state);
402err:
403 printk(KERN_ERR DRIVER_NAME ": Failed to probe: %d\n", ret);
404 return ret;
268} 405}
269 406
270static __devexit int adv7180_remove(struct i2c_client *client) 407static __devexit int adv7180_remove(struct i2c_client *client)
271{ 408{
272 struct v4l2_subdev *sd = i2c_get_clientdata(client); 409 struct v4l2_subdev *sd = i2c_get_clientdata(client);
410 struct adv7180_state *state = to_state(sd);
411
412 if (state->irq > 0) {
413 free_irq(client->irq, state);
414 if (cancel_work_sync(&state->work)) {
415 /*
416 * Work was pending, therefore we need to enable
417 * IRQ here to balance the disable_irq() done in the
418 * interrupt handler.
419 */
420 enable_irq(state->irq);
421 }
422 }
273 423
424 mutex_destroy(&state->mutex);
274 v4l2_device_unregister_subdev(sd); 425 v4l2_device_unregister_subdev(sd);
275 kfree(to_state(sd)); 426 kfree(to_state(sd));
276 return 0; 427 return 0;