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 <cr...@iki.fi>
---
 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 majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to