All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] af9013: fix i2c failures for dual-tuner devices
@ 2012-01-20 21:18 Gordon Hecker
  2012-01-20 22:30 ` Antti Palosaari
  0 siblings, 1 reply; 4+ messages in thread
From: Gordon Hecker @ 2012-01-20 21:18 UTC (permalink / raw)
  To: linux-media; +Cc: Gordon Hecker

This patch fixes the following error messages with a
Terratec Cinergy T Stick Dual RC DVB-T device.

af9013: i2c wr failed=-1 reg=d607 len=1
af9015: command failed:2
af9013: i2c rd failed=-1 reg=d607 len=1
af9015: command failed:1

It implements exclusive access to i2c for only one frontend at a time
through a use-counter that is increased for each af9013_i2c_gate_ctrl-enable
or i2c-read/write and decreased accordingly. The use-counter remains
incremented after af9013_i2c_gate_ctrl-enable until the corresponding
disable.

Debug output was added.

ToDo:
 * Replace frontend by adapter (the dual-tuner devices do actually
   provide two adapters with one frontend each)
 * move af9013_i2c_gate_mutex, locked_fe, af9013_i2c_gate_ctrl_usecnt
   to the usb device
---
 drivers/media/dvb/frontends/af9013.c |   93 +++++++++++++++++++++++++++++-----
 1 files changed, 80 insertions(+), 13 deletions(-)

diff --git a/drivers/media/dvb/frontends/af9013.c b/drivers/media/dvb/frontends/af9013.c
index 6bcbcf5..ab69585 100644
--- a/drivers/media/dvb/frontends/af9013.c
+++ b/drivers/media/dvb/frontends/af9013.c
@@ -28,6 +28,54 @@ int af9013_debug;
 module_param_named(debug, af9013_debug, int, 0644);
 MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
 
+static DEFINE_MUTEX(af9013_i2c_gate_mutex);
+static struct dvb_frontend *locked_fe = 0;
+static int af9013_i2c_gate_ctrl_usecnt = 0;
+
+#define GATE_NOOP    0
+#define GATE_ENABLE  1
+#define GATE_DISABLE 2
+
+static int af9013_i2c_gate_inc_use_count(struct dvb_frontend *fe, int gate_op)
+{
+	int success = 0;
+	while (1) {
+		if (mutex_lock_interruptible(&af9013_i2c_gate_mutex) != 0) {
+			return -EAGAIN;
+		}
+		if (af9013_i2c_gate_ctrl_usecnt == 0 || locked_fe == fe) {
+			success = 1;
+			locked_fe = fe;
+			if (gate_op != GATE_DISABLE) {
+				af9013_i2c_gate_ctrl_usecnt++;
+			}
+		}
+		mutex_unlock(&af9013_i2c_gate_mutex);
+		if (success) {
+			break;
+		}
+		schedule();
+	}
+	dbg("%s: %d (%d)", __func__, af9013_i2c_gate_ctrl_usecnt, fe->dvb->num);
+	return 0;
+}
+
+static int af9013_i2c_gate_dec_use_count(struct dvb_frontend *fe, int gate_op)
+{
+	if (mutex_lock_interruptible(&af9013_i2c_gate_mutex) != 0) {
+		return -EAGAIN;
+	}
+	if (gate_op != GATE_ENABLE) {
+		af9013_i2c_gate_ctrl_usecnt--;
+	}
+	if (af9013_i2c_gate_ctrl_usecnt == 0) {
+		locked_fe = 0;
+	}
+	mutex_unlock(&af9013_i2c_gate_mutex);
+	dbg("%s: %d (%d)", __func__, af9013_i2c_gate_ctrl_usecnt, fe->dvb->num);
+	return 0;
+}
+
 struct af9013_state {
 	struct i2c_adapter *i2c;
 	struct dvb_frontend fe;
@@ -44,7 +92,6 @@ struct af9013_state {
 	unsigned long set_frontend_jiffies;
 	unsigned long read_status_jiffies;
 	bool first_tune;
-	bool i2c_gate_state;
 	unsigned int statistics_step:3;
 	struct delayed_work statistics_work;
 };
@@ -54,6 +101,7 @@ static int af9013_wr_regs_i2c(struct af9013_state *priv, u8 mbox, u16 reg,
 	const u8 *val, int len)
 {
 	int ret;
+	struct dvb_frontend *fe = &(priv->fe);
 	u8 buf[3+len];
 	struct i2c_msg msg[1] = {
 		{
@@ -69,6 +117,10 @@ static int af9013_wr_regs_i2c(struct af9013_state *priv, u8 mbox, u16 reg,
 	buf[2] = mbox;
 	memcpy(&buf[3], val, len);
 
+	if (af9013_i2c_gate_inc_use_count(fe, GATE_NOOP) != 0) {
+		return -EAGAIN;
+	}
+	dbg("%s: I2C write reg:%04x (%d)", __func__, reg, fe->dvb->num);
 	ret = i2c_transfer(priv->i2c, msg, 1);
 	if (ret == 1) {
 		ret = 0;
@@ -76,6 +128,10 @@ static int af9013_wr_regs_i2c(struct af9013_state *priv, u8 mbox, u16 reg,
 		warn("i2c wr failed=%d reg=%04x len=%d", ret, reg, len);
 		ret = -EREMOTEIO;
 	}
+
+	if (af9013_i2c_gate_dec_use_count(fe, GATE_NOOP) != 0) {
+		return -EAGAIN;
+	}
 	return ret;
 }
 
@@ -84,6 +140,7 @@ static int af9013_rd_regs_i2c(struct af9013_state *priv, u8 mbox, u16 reg,
 	u8 *val, int len)
 {
 	int ret;
+	struct dvb_frontend *fe = &(priv->fe);
 	u8 buf[3];
 	struct i2c_msg msg[2] = {
 		{
@@ -103,6 +160,10 @@ static int af9013_rd_regs_i2c(struct af9013_state *priv, u8 mbox, u16 reg,
 	buf[1] = (reg >> 0) & 0xff;
 	buf[2] = mbox;
 
+	if (af9013_i2c_gate_inc_use_count(fe, GATE_NOOP) != 0) {
+		return -EAGAIN;
+	}
+	dbg("%s: I2C read reg:%04x (%d)", __func__, reg, priv->fe.dvb->num);
 	ret = i2c_transfer(priv->i2c, msg, 2);
 	if (ret == 2) {
 		ret = 0;
@@ -110,6 +171,9 @@ static int af9013_rd_regs_i2c(struct af9013_state *priv, u8 mbox, u16 reg,
 		warn("i2c rd failed=%d reg=%04x len=%d", ret, reg, len);
 		ret = -EREMOTEIO;
 	}
+	if (af9013_i2c_gate_dec_use_count(fe, GATE_NOOP) != 0) {
+		return -EAGAIN;
+	}
 	return ret;
 }
 
@@ -1297,25 +1361,28 @@ static int af9013_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
 	int ret;
 	struct af9013_state *state = fe->demodulator_priv;
 
-	dbg("%s: enable=%d", __func__, enable);
-
-	/* gate already open or close */
-	if (state->i2c_gate_state == enable)
-		return 0;
+	if (af9013_i2c_gate_inc_use_count(fe,
+				enable ? GATE_ENABLE : GATE_DISABLE) != 0) {
+		return -EAGAIN;
+	}
+	dbg("%s: enable:%d (%d)", __func__, enable, fe->dvb->num);
 
 	if (state->config.ts_mode == AF9013_TS_USB)
 		ret = af9013_wr_reg_bits(state, 0xd417, 3, 1, enable);
 	else
 		ret = af9013_wr_reg_bits(state, 0xd607, 2, 1, enable);
-	if (ret)
-		goto err;
-
-	state->i2c_gate_state = enable;
+	
+	if (ret) {
+		dbg("%s: failed=%d", __func__, ret);
+	}
+	
+	if (!enable) {
+		if (af9013_i2c_gate_dec_use_count(fe, GATE_DISABLE) != 0) {
+			return -EAGAIN;
+		}
+	}
 
 	return ret;
-err:
-	dbg("%s: failed=%d", __func__, ret);
-	return ret;
 }
 
 static void af9013_release(struct dvb_frontend *fe)
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH] af9013: fix i2c failures for dual-tuner devices
  2012-01-20 21:18 [PATCH] af9013: fix i2c failures for dual-tuner devices Gordon Hecker
@ 2012-01-20 22:30 ` Antti Palosaari
  2012-01-22 21:13   ` [PATCH] af9015: " Gordon Hecker
  0 siblings, 1 reply; 4+ messages in thread
From: Antti Palosaari @ 2012-01-20 22:30 UTC (permalink / raw)
  To: Gordon Hecker; +Cc: linux-media

On 01/20/2012 11:18 PM, Gordon Hecker wrote:
> This patch fixes the following error messages with a
> Terratec Cinergy T Stick Dual RC DVB-T device.
>
> af9013: i2c wr failed=-1 reg=d607 len=1
> af9015: command failed:2
> af9013: i2c rd failed=-1 reg=d607 len=1
> af9015: command failed:1
>
> It implements exclusive access to i2c for only one frontend at a time
> through a use-counter that is increased for each af9013_i2c_gate_ctrl-enable
> or i2c-read/write and decreased accordingly. The use-counter remains
> incremented after af9013_i2c_gate_ctrl-enable until the corresponding
> disable.
>
> Debug output was added.
>
> ToDo:
>   * Replace frontend by adapter (the dual-tuner devices do actually
>     provide two adapters with one frontend each)
>   * move af9013_i2c_gate_mutex, locked_fe, af9013_i2c_gate_ctrl_usecnt
>     to the usb device

Again that same issue. But after af9013 rewrote those should not be very 
significant problem... If you looked, as I can guess, AF9015 code you 
can see callbacks lock that are for resolving same issue. Almost all 
callbacks are locked, but I left few rarely called callbacks without 
lock due to performance point of view. You can guess it causes always 
latency to wait other thread finish callback...

I think you may resolve that just adding one or two callback lock more, 
likely for tuner init() and sleep().

I don't like that you are adding such code for the demod driver - 
problem is USB-bridge, its I2C adapter and firmware. Due to that such 
code should be in AF9015 driver. There has been originally two I2C 
adapters in af9015 but I removed those. And there is actually only one 
I2C adapter in AF9015.
See these for more info about I2C bus connections:
http://palosaari.fi/linux/v4l-dvb/controlling_tuner_af9015_dual_demod.txt
http://palosaari.fi/linux/v4l-dvb/controlling_tuner.txt

I think that piece of code is not very important and I dont like to see 
it in current af9013 driver. Do needed changes for af9015 instead.

regards
Antti

-- 
http://palosaari.fi/

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH] af9015: fix i2c failures for dual-tuner devices
  2012-01-20 22:30 ` Antti Palosaari
@ 2012-01-22 21:13   ` Gordon Hecker
  2012-03-07 15:51     ` poma
  0 siblings, 1 reply; 4+ messages in thread
From: Gordon Hecker @ 2012-01-22 21:13 UTC (permalink / raw)
  To: linux-media, Antti Palosaari; +Cc: Gordon Hecker

The i2c failures were caused by enabling both i2c gates
at the same time while putting the tuners asleep.

This patch removes the init() and sleep() callbacks from the tuner,
to prevent frontend.c from calling
  i2c_gate_ctrl
  tuner init / sleep
  i2c_gate_ctrl
without holding the lock.
tuner init() and sleep() are instead called in frontend init() and
sleep().

Signed-off-by: Gordon Hecker <ghecker@gmx.de>
---
 drivers/media/dvb/dvb-usb/af9015.c |   31 +++++++++++++++++++++++++++++++
 drivers/media/dvb/dvb-usb/af9015.h |    2 ++
 2 files changed, 33 insertions(+), 0 deletions(-)

diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c
index e755d76..b69b43b 100644
--- a/drivers/media/dvb/dvb-usb/af9015.c
+++ b/drivers/media/dvb/dvb-usb/af9015.c
@@ -1141,7 +1141,18 @@ static int af9015_af9013_init(struct dvb_frontend *fe)
 		return -EAGAIN;
 
 	ret = priv->init[adap->id](fe);
+	if (ret)
+		goto err_unlock;
+
+	if (priv->tuner_ops_init[adap->id]) {
+		if (fe->ops.i2c_gate_ctrl)
+			fe->ops.i2c_gate_ctrl(fe, 1);
+		ret = priv->tuner_ops_init[adap->id](fe);
+		if (fe->ops.i2c_gate_ctrl)
+			fe->ops.i2c_gate_ctrl(fe, 0);
+	}
 
+err_unlock:
 	mutex_unlock(&adap->dev->usb_mutex);
 
 	return ret;
@@ -1157,8 +1168,19 @@ static int af9015_af9013_sleep(struct dvb_frontend *fe)
 	if (mutex_lock_interruptible(&adap->dev->usb_mutex))
 		return -EAGAIN;
 
+	if (priv->tuner_ops_sleep[adap->id]) {
+		if (fe->ops.i2c_gate_ctrl)
+			fe->ops.i2c_gate_ctrl(fe, 1);
+		ret = priv->tuner_ops_sleep[adap->id](fe);
+		if (fe->ops.i2c_gate_ctrl)
+			fe->ops.i2c_gate_ctrl(fe, 0);
+		if (ret)
+			goto err_unlock;
+	}
+
 	ret = priv->sleep[adap->id](fe);
 
+err_unlock:
 	mutex_unlock(&adap->dev->usb_mutex);
 
 	return ret;
@@ -1283,6 +1305,7 @@ static struct mxl5007t_config af9015_mxl5007t_config = {
 static int af9015_tuner_attach(struct dvb_usb_adapter *adap)
 {
 	int ret;
+	struct af9015_state *state = adap->dev->priv;
 	deb_info("%s:\n", __func__);
 
 	switch (af9015_af9013_config[adap->id].tuner) {
@@ -1340,6 +1363,14 @@ static int af9015_tuner_attach(struct dvb_usb_adapter *adap)
 		err("Unknown tuner id:%d",
 			af9015_af9013_config[adap->id].tuner);
 	}
+
+	state->tuner_ops_sleep[adap->id] =
+				adap->fe_adap[0].fe->ops.tuner_ops.sleep;
+	adap->fe_adap[0].fe->ops.tuner_ops.sleep = 0;
+
+	state->tuner_ops_init[adap->id] =
+				adap->fe_adap[0].fe->ops.tuner_ops.init;
+	adap->fe_adap[0].fe->ops.tuner_ops.init = 0;
 	return ret;
 }
 
diff --git a/drivers/media/dvb/dvb-usb/af9015.h b/drivers/media/dvb/dvb-usb/af9015.h
index f619063..ee2ec5b 100644
--- a/drivers/media/dvb/dvb-usb/af9015.h
+++ b/drivers/media/dvb/dvb-usb/af9015.h
@@ -108,6 +108,8 @@ struct af9015_state {
 	int (*read_status[2]) (struct dvb_frontend *fe, fe_status_t *status);
 	int (*init[2]) (struct dvb_frontend *fe);
 	int (*sleep[2]) (struct dvb_frontend *fe);
+	int (*tuner_ops_init[2]) (struct dvb_frontend *fe);
+	int (*tuner_ops_sleep[2]) (struct dvb_frontend *fe);
 };
 
 struct af9015_config {
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH] af9015: fix i2c failures for dual-tuner devices
  2012-01-22 21:13   ` [PATCH] af9015: " Gordon Hecker
@ 2012-03-07 15:51     ` poma
  0 siblings, 0 replies; 4+ messages in thread
From: poma @ 2012-03-07 15:51 UTC (permalink / raw)
  To: linux-media

On 22.01.2012 22:13, Gordon Hecker wrote:
> The i2c failures were caused by enabling both i2c gates
> at the same time while putting the tuners asleep.
> 
> This patch removes the init() and sleep() callbacks from the tuner,
> to prevent frontend.c from calling
>   i2c_gate_ctrl
>   tuner init / sleep
>   i2c_gate_ctrl
> without holding the lock.
> tuner init() and sleep() are instead called in frontend init() and
> sleep().
> 
> Signed-off-by: Gordon Hecker <ghecker@gmx.de>
> ---
>  drivers/media/dvb/dvb-usb/af9015.c |   31 +++++++++++++++++++++++++++++++
>  drivers/media/dvb/dvb-usb/af9015.h |    2 ++
>  2 files changed, 33 insertions(+), 0 deletions(-)
> 
> diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c
> index e755d76..b69b43b 100644
> --- a/drivers/media/dvb/dvb-usb/af9015.c
> +++ b/drivers/media/dvb/dvb-usb/af9015.c
> @@ -1141,7 +1141,18 @@ static int af9015_af9013_init(struct dvb_frontend *fe)
>  		return -EAGAIN;
>  
>  	ret = priv->init[adap->id](fe);
> +	if (ret)
> +		goto err_unlock;
> +
> +	if (priv->tuner_ops_init[adap->id]) {
> +		if (fe->ops.i2c_gate_ctrl)
> +			fe->ops.i2c_gate_ctrl(fe, 1);
> +		ret = priv->tuner_ops_init[adap->id](fe);
> +		if (fe->ops.i2c_gate_ctrl)
> +			fe->ops.i2c_gate_ctrl(fe, 0);
> +	}
>  
> +err_unlock:
>  	mutex_unlock(&adap->dev->usb_mutex);
>  
>  	return ret;
> @@ -1157,8 +1168,19 @@ static int af9015_af9013_sleep(struct dvb_frontend *fe)
>  	if (mutex_lock_interruptible(&adap->dev->usb_mutex))
>  		return -EAGAIN;
>  
> +	if (priv->tuner_ops_sleep[adap->id]) {
> +		if (fe->ops.i2c_gate_ctrl)
> +			fe->ops.i2c_gate_ctrl(fe, 1);
> +		ret = priv->tuner_ops_sleep[adap->id](fe);
> +		if (fe->ops.i2c_gate_ctrl)
> +			fe->ops.i2c_gate_ctrl(fe, 0);
> +		if (ret)
> +			goto err_unlock;
> +	}
> +
>  	ret = priv->sleep[adap->id](fe);
>  
> +err_unlock:
>  	mutex_unlock(&adap->dev->usb_mutex);
>  
>  	return ret;
> @@ -1283,6 +1305,7 @@ static struct mxl5007t_config af9015_mxl5007t_config = {
>  static int af9015_tuner_attach(struct dvb_usb_adapter *adap)
>  {
>  	int ret;
> +	struct af9015_state *state = adap->dev->priv;
>  	deb_info("%s:\n", __func__);
>  
>  	switch (af9015_af9013_config[adap->id].tuner) {
> @@ -1340,6 +1363,14 @@ static int af9015_tuner_attach(struct dvb_usb_adapter *adap)
>  		err("Unknown tuner id:%d",
>  			af9015_af9013_config[adap->id].tuner);
>  	}
> +
> +	state->tuner_ops_sleep[adap->id] =
> +				adap->fe_adap[0].fe->ops.tuner_ops.sleep;
> +	adap->fe_adap[0].fe->ops.tuner_ops.sleep = 0;
> +
> +	state->tuner_ops_init[adap->id] =
> +				adap->fe_adap[0].fe->ops.tuner_ops.init;
> +	adap->fe_adap[0].fe->ops.tuner_ops.init = 0;
>  	return ret;
>  }
>  
> diff --git a/drivers/media/dvb/dvb-usb/af9015.h b/drivers/media/dvb/dvb-usb/af9015.h
> index f619063..ee2ec5b 100644
> --- a/drivers/media/dvb/dvb-usb/af9015.h
> +++ b/drivers/media/dvb/dvb-usb/af9015.h
> @@ -108,6 +108,8 @@ struct af9015_state {
>  	int (*read_status[2]) (struct dvb_frontend *fe, fe_status_t *status);
>  	int (*init[2]) (struct dvb_frontend *fe);
>  	int (*sleep[2]) (struct dvb_frontend *fe);
> +	int (*tuner_ops_init[2]) (struct dvb_frontend *fe);
> +	int (*tuner_ops_sleep[2]) (struct dvb_frontend *fe);
>  };
>  
>  struct af9015_config {

Both tuners perform stable.
Tested on mythbackend multiple streams simultaneously.
+1

rgds,
poma

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2012-03-07 15:52 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-01-20 21:18 [PATCH] af9013: fix i2c failures for dual-tuner devices Gordon Hecker
2012-01-20 22:30 ` Antti Palosaari
2012-01-22 21:13   ` [PATCH] af9015: " Gordon Hecker
2012-03-07 15:51     ` poma

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.