Based on other included backports. Includes unlocked i2c gate control callbacks. Signed-off-by: Brad Love <brad@xxxxxxxxxxxxxxxx> --- backports/v4.6_i2c_mux.patch | 213 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 213 insertions(+) diff --git a/backports/v4.6_i2c_mux.patch b/backports/v4.6_i2c_mux.patch index 8f1ab88..ce89faa 100644 --- a/backports/v4.6_i2c_mux.patch +++ b/backports/v4.6_i2c_mux.patch @@ -1455,3 +1455,216 @@ index c76e78f..5c805f8 100644 pdata.dvb_frontend = adap->fe[0]; pdata.dvb_usb_device = d; pdata.v4l2_subdev = subdev; +diff --git a/drivers/media/dvb-frontends/lgdt3306a.c b/drivers/media/dvb-frontends/lgdt3306a.c +index ab16d3a..2988262 100644 +--- a/drivers/media/dvb-frontends/lgdt3306a.c ++++ b/drivers/media/dvb-frontends/lgdt3306a.c +@@ -79,8 +79,6 @@ struct lgdt3306a_state { + enum fe_modulation current_modulation; + u32 current_frequency; + u32 snr; +- +- struct i2c_mux_core *muxc; + }; + + /* +@@ -2182,20 +2180,142 @@ static const struct dvb_frontend_ops lgdt3306a_ops = { + .search = lgdt3306a_search, + }; + +-static int lgdt3306a_select(struct i2c_mux_core *muxc, u32 chan) ++/* ++ * I2C gate logic ++ * We must use unlocked I2C I/O because I2C adapter lock is already taken ++ * by the caller (usually tuner driver). ++ * select/unselect are unlocked versions of lgdt3306a_i2c_gate_ctrl ++ */ ++static int lgdt3306a_select(struct i2c_adapter *adap, void *mux_priv, u32 chan) + { +- struct i2c_client *client = i2c_mux_priv(muxc); +- struct lgdt3306a_state *state = i2c_get_clientdata(client); ++ struct i2c_client *client = mux_priv; ++ int ret; ++ u8 val; ++ u8 buf[3]; ++ ++ struct i2c_msg read_msg_1 = { ++ .addr = client->addr, ++ .flags = 0, ++ .buf = "\x00\x02", ++ .len = 2, ++ }; ++ struct i2c_msg read_msg_2 = { ++ .addr = client->addr, ++ .flags = I2C_M_RD, ++ .buf = &val, ++ .len = 1, ++ }; ++ ++ struct i2c_msg write_msg = { ++ .addr = client->addr, ++ .flags = 0, ++ .len = 3, ++ .buf = buf, ++ }; ++ ++ ret = __i2c_transfer(client->adapter, &read_msg_1, 1); ++ if (ret != 1) ++ { ++ pr_err("error (addr %02x reg 0x002 error (ret == %i)\n", ++ client->addr, ret); ++ if (ret < 0) ++ return ret; ++ else ++ return -EREMOTEIO; ++ } + +- return lgdt3306a_i2c_gate_ctrl(&state->frontend, 1); ++ ret = __i2c_transfer(client->adapter, &read_msg_2, 1); ++ if (ret != 1) ++ { ++ pr_err("error (addr %02x reg 0x002 error (ret == %i)\n", ++ client->addr, ret); ++ if (ret < 0) ++ return ret; ++ else ++ return -EREMOTEIO; ++ } ++ ++ buf[0] = 0x00; ++ buf[1] = 0x02; ++ val &= 0x7F; ++ val |= LG3306_TUNERI2C_ON; ++ buf[2] = val; ++ ret = __i2c_transfer(client->adapter, &write_msg, 1); ++ if (ret != 1) { ++ pr_err("error (addr %02x %02x <- %02x, err = %i)\n", ++ write_msg.buf[0], write_msg.buf[1], write_msg.buf[2], ret); ++ if (ret < 0) ++ return ret; ++ else ++ return -EREMOTEIO; ++ } ++ return 0; + } + +-static int lgdt3306a_deselect(struct i2c_mux_core *muxc, u32 chan) ++static int lgdt3306a_deselect(struct i2c_adapter *adap, void *mux_priv, u32 chan) + { +- struct i2c_client *client = i2c_mux_priv(muxc); +- struct lgdt3306a_state *state = i2c_get_clientdata(client); ++ struct i2c_client *client = mux_priv; ++ int ret; ++ u8 val; ++ u8 buf[3]; ++ ++ struct i2c_msg read_msg_1 = { ++ .addr = client->addr, ++ .flags = 0, ++ .buf = "\x00\x02", ++ .len = 2, ++ }; ++ struct i2c_msg read_msg_2 = { ++ .addr = client->addr, ++ .flags = I2C_M_RD, ++ .buf = &val, ++ .len = 1, ++ }; + +- return lgdt3306a_i2c_gate_ctrl(&state->frontend, 0); ++ struct i2c_msg write_msg = { ++ .addr = client->addr, ++ .flags = 0, ++ .len = 3, ++ .buf = buf, ++ }; ++ ++ ret = __i2c_transfer(client->adapter, &read_msg_1, 1); ++ if (ret != 1) ++ { ++ pr_err("error (addr %02x reg 0x002 error (ret == %i)\n", ++ client->addr, ret); ++ if (ret < 0) ++ return ret; ++ else ++ return -EREMOTEIO; ++ } ++ ++ ret = __i2c_transfer(client->adapter, &read_msg_2, 1); ++ if (ret != 1) ++ { ++ pr_err("error (addr %02x reg 0x002 error (ret == %i)\n", ++ client->addr, ret); ++ if (ret < 0) ++ return ret; ++ else ++ return -EREMOTEIO; ++ } ++ ++ buf[0] = 0x00; ++ buf[1] = 0x02; ++ val &= 0x7F; ++ val |= LG3306_TUNERI2C_OFF; ++ buf[2] = val; ++ ret = __i2c_transfer(client->adapter, &write_msg, 1); ++ if (ret != 1) { ++ pr_err("error (addr %02x %02x <- %02x, err = %i)\n", ++ write_msg.buf[0], write_msg.buf[1], write_msg.buf[2], ret); ++ if (ret < 0) ++ return ret; ++ else ++ return -EREMOTEIO; ++ } ++ return 0; + } + + static int lgdt3306a_probe(struct i2c_client *client, +@@ -2227,21 +2347,16 @@ static int lgdt3306a_probe(struct i2c_client *client, + state->frontend.ops.release = NULL; + + /* create mux i2c adapter for tuner */ +- state->muxc = i2c_mux_alloc(client->adapter, &client->dev, +- 1, 0, I2C_MUX_LOCKED, +- lgdt3306a_select, lgdt3306a_deselect); +- if (!state->muxc) { +- ret = -ENOMEM; ++ state->i2c_adap = i2c_add_mux_adapter(client->adapter, &client->dev, ++ client, 0, 0, 0, lgdt3306a_select, lgdt3306a_deselect); ++ if (state->i2c_adap == NULL) { ++ ret = -ENODEV; + goto err_kfree; + } +- state->muxc->priv = client; +- ret = i2c_mux_add_adapter(state->muxc, 0, 0, 0); +- if (ret) +- goto err_kfree; + + /* create dvb_frontend */ + fe->ops.i2c_gate_ctrl = NULL; +- *config->i2c_adapter = state->muxc->adapter[0]; ++ *config->i2c_adapter = state->i2c_adap; + *config->fe = fe; + + dev_info(&client->dev, "LG Electronics LGDT3306A successfully identified\n"); +@@ -2261,7 +2376,7 @@ static int lgdt3306a_remove(struct i2c_client *client) + { + struct lgdt3306a_state *state = i2c_get_clientdata(client); + +- i2c_mux_del_adapters(state->muxc); ++ i2c_del_mux_adapter(state->i2c_adap); + + state->frontend.ops.release = NULL; + state->frontend.demodulator_priv = NULL; +diff --git a/drivers/media/dvb-frontends/lgdt3306a.h b/drivers/media/dvb-frontends/lgdt3306a.h +index 8b53044..99b28be 100644 +--- a/drivers/media/dvb-frontends/lgdt3306a.h ++++ b/drivers/media/dvb-frontends/lgdt3306a.h +@@ -21,6 +21,8 @@ + #include <linux/i2c.h> + #include <media/dvb_frontend.h> + ++#define LG3306_TUNERI2C_ON 0x00 ++#define LG3306_TUNERI2C_OFF 0x80 + + enum lgdt3306a_mpeg_mode { + LGDT3306A_MPEG_PARALLEL = 0, -- 2.7.4