aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDevin Heitmueller <dheitmueller@kernellabs.com>2010-02-17 20:47:55 -0500
committerMauro Carvalho Chehab <mchehab@redhat.com>2010-05-17 23:46:21 -0400
commit7f2c983cf5978186ee2c379fd63d04316158fc9b (patch)
treee8db82b72129777469ea7ba3a313f4ce237447e3
parent535653b1c22c29d4e8f554928efc87fe138f917d (diff)
V4L/DVB: au8522: fix race condition in switching from digital to analog mode
With applications like MythTV, switching inputs results in closing the digital side and then immediately opening the analog side. This exposes a race condition where the dvb_frontend kernel thread powers down the chip and closes the i2c gate even though we're in the middle of bringing up the analog part of the chip (since the shutdown of the dvb_frontend kernel thread occurs asychronously). Introduce a construct to keep track of what mode we're in, and drop requests to power down or management the gate if we've already switched to analog mode. Thanks to Zaphod Beeblebrox for reporting this issue. Signed-off-by: Devin Heitmueller <dheitmueller@kernellabs.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
-rw-r--r--drivers/media/dvb/frontends/au8522_decoder.c2
-rw-r--r--drivers/media/dvb/frontends/au8522_dig.c21
-rw-r--r--drivers/media/dvb/frontends/au8522_priv.h5
3 files changed, 28 insertions, 0 deletions
diff --git a/drivers/media/dvb/frontends/au8522_decoder.c b/drivers/media/dvb/frontends/au8522_decoder.c
index 24268ef2753d..ef5fd49363c2 100644
--- a/drivers/media/dvb/frontends/au8522_decoder.c
+++ b/drivers/media/dvb/frontends/au8522_decoder.c
@@ -664,6 +664,8 @@ static int au8522_reset(struct v4l2_subdev *sd, u32 val)
664{ 664{
665 struct au8522_state *state = to_state(sd); 665 struct au8522_state *state = to_state(sd);
666 666
667 state->operational_mode = AU8522_ANALOG_MODE;
668
667 au8522_writereg(state, 0xa4, 1 << 5); 669 au8522_writereg(state, 0xa4, 1 << 5);
668 670
669 return 0; 671 return 0;
diff --git a/drivers/media/dvb/frontends/au8522_dig.c b/drivers/media/dvb/frontends/au8522_dig.c
index a1fed0fa8ed4..e2efb25d1a75 100644
--- a/drivers/media/dvb/frontends/au8522_dig.c
+++ b/drivers/media/dvb/frontends/au8522_dig.c
@@ -84,6 +84,14 @@ static int au8522_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
84 84
85 dprintk("%s(%d)\n", __func__, enable); 85 dprintk("%s(%d)\n", __func__, enable);
86 86
87 if (state->operational_mode == AU8522_ANALOG_MODE) {
88 /* We're being asked to manage the gate even though we're
89 not in digital mode. This can occur if we get switched
90 over to analog mode before the dvb_frontend kernel thread
91 has completely shutdown */
92 return 0;
93 }
94
87 if (enable) 95 if (enable)
88 return au8522_writereg(state, 0x106, 1); 96 return au8522_writereg(state, 0x106, 1);
89 else 97 else
@@ -608,6 +616,8 @@ int au8522_init(struct dvb_frontend *fe)
608 struct au8522_state *state = fe->demodulator_priv; 616 struct au8522_state *state = fe->demodulator_priv;
609 dprintk("%s()\n", __func__); 617 dprintk("%s()\n", __func__);
610 618
619 state->operational_mode = AU8522_DIGITAL_MODE;
620
611 au8522_writereg(state, 0xa4, 1 << 5); 621 au8522_writereg(state, 0xa4, 1 << 5);
612 622
613 au8522_i2c_gate_ctrl(fe, 1); 623 au8522_i2c_gate_ctrl(fe, 1);
@@ -704,6 +714,15 @@ int au8522_sleep(struct dvb_frontend *fe)
704 struct au8522_state *state = fe->demodulator_priv; 714 struct au8522_state *state = fe->demodulator_priv;
705 dprintk("%s()\n", __func__); 715 dprintk("%s()\n", __func__);
706 716
717 /* Only power down if the digital side is currently using the chip */
718 if (state->operational_mode == AU8522_ANALOG_MODE) {
719 /* We're not in one of the expected power modes, which means
720 that the DVB thread is probably telling us to go to sleep
721 even though the analog frontend has already started using
722 the chip. So ignore the request */
723 return 0;
724 }
725
707 /* turn off led */ 726 /* turn off led */
708 au8522_led_ctrl(state, 0); 727 au8522_led_ctrl(state, 0);
709 728
@@ -932,6 +951,8 @@ struct dvb_frontend *au8522_attach(const struct au8522_config *config,
932 /* setup the state */ 951 /* setup the state */
933 state->config = config; 952 state->config = config;
934 state->i2c = i2c; 953 state->i2c = i2c;
954 state->operational_mode = AU8522_DIGITAL_MODE;
955
935 /* create dvb_frontend */ 956 /* create dvb_frontend */
936 memcpy(&state->frontend.ops, &au8522_ops, 957 memcpy(&state->frontend.ops, &au8522_ops,
937 sizeof(struct dvb_frontend_ops)); 958 sizeof(struct dvb_frontend_ops));
diff --git a/drivers/media/dvb/frontends/au8522_priv.h b/drivers/media/dvb/frontends/au8522_priv.h
index c74c4e72fe91..609cf04bc312 100644
--- a/drivers/media/dvb/frontends/au8522_priv.h
+++ b/drivers/media/dvb/frontends/au8522_priv.h
@@ -34,10 +34,15 @@
34#include "au8522.h" 34#include "au8522.h"
35#include "tuner-i2c.h" 35#include "tuner-i2c.h"
36 36
37#define AU8522_ANALOG_MODE 0
38#define AU8522_DIGITAL_MODE 1
39
37struct au8522_state { 40struct au8522_state {
38 struct i2c_client *c; 41 struct i2c_client *c;
39 struct i2c_adapter *i2c; 42 struct i2c_adapter *i2c;
40 43
44 u8 operational_mode;
45
41 /* Used for sharing of the state between analog and digital mode */ 46 /* Used for sharing of the state between analog and digital mode */
42 struct tuner_i2c_props i2c_props; 47 struct tuner_i2c_props i2c_props;
43 struct list_head hybrid_tuner_instance_list; 48 struct list_head hybrid_tuner_instance_list;