[PATCH 1/5] mxl5007t: fix buggy register read

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Chip uses WRITE + STOP + READ + STOP sequence for I2C register read.
Driver was using REPEATED START condition which makes it failing if
I2C adapter was implemented correctly.

Add use_broken_read_reg_intentionally option to keep old buggy
implantation as there is buggy I2C adapter implementation relying
that bug...

Signed-off-by: Antti Palosaari <crope@xxxxxx>
---
 drivers/media/tuners/mxl5007t.c             | 56 ++++++++++++++++++++++++++++-
 drivers/media/tuners/mxl5007t.h             |  7 ++++
 drivers/media/usb/au0828/au0828-dvb.c       |  1 +
 drivers/media/usb/dvb-usb-v2/af9015.c       |  1 +
 drivers/media/usb/dvb-usb-v2/af9035.c       |  2 ++
 drivers/media/usb/dvb-usb/dib0700_devices.c |  1 +
 6 files changed, 67 insertions(+), 1 deletion(-)

diff --git a/drivers/media/tuners/mxl5007t.c b/drivers/media/tuners/mxl5007t.c
index 69e453e..36605ea 100644
--- a/drivers/media/tuners/mxl5007t.c
+++ b/drivers/media/tuners/mxl5007t.c
@@ -156,6 +156,7 @@ struct mxl5007t_state {
 	struct tuner_i2c_props i2c_props;
 
 	struct mutex lock;
+	struct mutex i2c_lock;
 
 	struct mxl5007t_config *config;
 
@@ -490,7 +491,8 @@ static int mxl5007t_write_regs(struct mxl5007t_state *state,
 	return ret;
 }
 
-static int mxl5007t_read_reg(struct mxl5007t_state *state, u8 reg, u8 *val)
+/* XXX: bad implementation for avoiding regressions */
+static int mxl5007t_read_reg_bad(struct mxl5007t_state *state, u8 reg, u8 *val)
 {
 	u8 buf[2] = { 0xfb, reg };
 	struct i2c_msg msg[] = {
@@ -509,6 +511,57 @@ static int mxl5007t_read_reg(struct mxl5007t_state *state, u8 reg, u8 *val)
 	return 0;
 }
 
+/* chip uses I2C write + read with STOP condition */
+static int mxl5007t_read_reg_good(struct mxl5007t_state *state, u8 reg, u8 *val)
+{
+	int ret;
+	u8 buf[2] = { 0xfb, reg };
+	struct i2c_msg msg1[] = {
+		{
+			.addr = state->i2c_props.addr,
+			.flags = 0,
+			.buf = buf,
+			.len = 2,
+		},
+	};
+	struct i2c_msg msg2[] = {
+		{
+			.addr = state->i2c_props.addr,
+			.flags = I2C_M_RD,
+			.buf = val,
+			.len = 1,
+		},
+	};
+
+	mutex_lock(&state->i2c_lock);
+
+	ret = i2c_transfer(state->i2c_props.adap, msg1, 1);
+	if (ret != 1) {
+		mxl_err("failed!");
+		ret = -EREMOTEIO;
+		goto fail;
+	}
+
+	ret = i2c_transfer(state->i2c_props.adap, msg2, 1);
+	if (ret != 1) {
+		mxl_err("failed!");
+		ret = -EREMOTEIO;
+		goto fail;
+	}
+fail:
+	mutex_unlock(&state->i2c_lock);
+
+	return ret;
+}
+
+static int mxl5007t_read_reg(struct mxl5007t_state *state, u8 reg, u8 *val)
+{
+	if (state->config->use_broken_read_reg_intentionally)
+		return mxl5007t_read_reg_bad(state, reg, val);
+	else
+		return mxl5007t_read_reg_good(state, reg, val);
+}
+
 static int mxl5007t_soft_reset(struct mxl5007t_state *state)
 {
 	u8 d = 0xff;
@@ -883,6 +936,7 @@ struct dvb_frontend *mxl5007t_attach(struct dvb_frontend *fe,
 		state->config = cfg;
 
 		mutex_init(&state->lock);
+		mutex_init(&state->i2c_lock);
 
 		if (fe->ops.i2c_gate_ctrl)
 			fe->ops.i2c_gate_ctrl(fe, 1);
diff --git a/drivers/media/tuners/mxl5007t.h b/drivers/media/tuners/mxl5007t.h
index 37b0942..728779b 100644
--- a/drivers/media/tuners/mxl5007t.h
+++ b/drivers/media/tuners/mxl5007t.h
@@ -75,6 +75,13 @@ struct mxl5007t_config {
 	unsigned int invert_if:1;
 	unsigned int loop_thru_enable:1;
 	unsigned int clk_out_enable:1;
+	/*
+	 * XXX: This should not be used. Defined for avoiding regressions.
+	 * Remove use of that option after device is tested to be working with
+	 * correct implementation.
+	 * MxL5007t does not use I2C REPEATED START condition for register read.
+	 */
+	unsigned int use_broken_read_reg_intentionally:1;
 };
 
 #if IS_ENABLED(CONFIG_MEDIA_TUNER_MXL5007T)
diff --git a/drivers/media/usb/au0828/au0828-dvb.c b/drivers/media/usb/au0828/au0828-dvb.c
index 9a6f156..7d32a0c 100644
--- a/drivers/media/usb/au0828/au0828-dvb.c
+++ b/drivers/media/usb/au0828/au0828-dvb.c
@@ -95,6 +95,7 @@ static struct xc5000_config hauppauge_xc5000c_config = {
 static struct mxl5007t_config mxl5007t_hvr950q_config = {
 	.xtal_freq_hz = MxL_XTAL_24_MHZ,
 	.if_freq_hz = MxL_IF_6_MHZ,
+	.use_broken_read_reg_intentionally = 1,
 };
 
 static struct tda18271_config hauppauge_woodbury_tunerconfig = {
diff --git a/drivers/media/usb/dvb-usb-v2/af9015.c b/drivers/media/usb/dvb-usb-v2/af9015.c
index d556042..b943304 100644
--- a/drivers/media/usb/dvb-usb-v2/af9015.c
+++ b/drivers/media/usb/dvb-usb-v2/af9015.c
@@ -931,6 +931,7 @@ static struct tda18218_config af9015_tda18218_config = {
 static struct mxl5007t_config af9015_mxl5007t_config = {
 	.xtal_freq_hz = MxL_XTAL_24_MHZ,
 	.if_freq_hz = MxL_IF_4_57_MHZ,
+	.use_broken_read_reg_intentionally = 1,
 };
 
 static int af9015_tuner_attach(struct dvb_usb_adapter *adap)
diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c
index b638fc1..b7e7135 100644
--- a/drivers/media/usb/dvb-usb-v2/af9035.c
+++ b/drivers/media/usb/dvb-usb-v2/af9035.c
@@ -947,6 +947,7 @@ static struct mxl5007t_config af9035_mxl5007t_config[] = {
 		.loop_thru_enable = 0,
 		.clk_out_enable = 0,
 		.clk_out_amp = MxL_CLKOUT_AMP_0_94V,
+		.use_broken_read_reg_intentionally = 1,
 	}, {
 		.xtal_freq_hz = MxL_XTAL_24_MHZ,
 		.if_freq_hz = MxL_IF_4_57_MHZ,
@@ -954,6 +955,7 @@ static struct mxl5007t_config af9035_mxl5007t_config[] = {
 		.loop_thru_enable = 1,
 		.clk_out_enable = 1,
 		.clk_out_amp = MxL_CLKOUT_AMP_0_94V,
+		.use_broken_read_reg_intentionally = 1,
 	}
 };
 
diff --git a/drivers/media/usb/dvb-usb/dib0700_devices.c b/drivers/media/usb/dvb-usb/dib0700_devices.c
index 1179842..c58c6ea 100644
--- a/drivers/media/usb/dvb-usb/dib0700_devices.c
+++ b/drivers/media/usb/dvb-usb/dib0700_devices.c
@@ -3433,6 +3433,7 @@ static struct mxl5007t_config hcw_mxl5007t_config = {
 	.xtal_freq_hz = MxL_XTAL_25_MHZ,
 	.if_freq_hz = MxL_IF_6_MHZ,
 	.invert_if = 1,
+	.use_broken_read_reg_intentionally = 1,
 };
 
 /* TIGER-ATSC map:
-- 
1.7.11.7

--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux