Commit 44b9055b authored by Antti Palosaari's avatar Antti Palosaari Committed by Mauro Carvalho Chehab

[media] m88ds3103: use I2C mux for tuner I2C adapter

Switch standard I2C adapter to muxed I2C adapter.

David reported that I2C adapter implementation caused deadlock.
I discussed with Jean and he suggested to implement it as a
multiplexed i2c adapter because tuner I2C bus could be seen like
own I2C segment.
Reported-by: default avatarDavid Howells <dhowells@redhat.com>
Cc: Jean Delvare <khali@linux-fr.org>
Signed-off-by: default avatarAntti Palosaari <crope@iki.fi>
Signed-off-by: default avatarMauro Carvalho Chehab <m.chehab@samsung.com>
parent 63c80f70
...@@ -37,7 +37,7 @@ config DVB_STV6110x ...@@ -37,7 +37,7 @@ config DVB_STV6110x
config DVB_M88DS3103 config DVB_M88DS3103
tristate "Montage M88DS3103" tristate "Montage M88DS3103"
depends on DVB_CORE && I2C depends on DVB_CORE && I2C && I2C_MUX
default m if !MEDIA_SUBDRV_AUTOSELECT default m if !MEDIA_SUBDRV_AUTOSELECT
help help
Say Y when you want to support this frontend. Say Y when you want to support this frontend.
......
...@@ -1108,15 +1108,16 @@ static int m88ds3103_get_tune_settings(struct dvb_frontend *fe, ...@@ -1108,15 +1108,16 @@ static int m88ds3103_get_tune_settings(struct dvb_frontend *fe,
return 0; return 0;
} }
static u32 m88ds3103_tuner_i2c_func(struct i2c_adapter *adapter) static void m88ds3103_release(struct dvb_frontend *fe)
{ {
return I2C_FUNC_I2C; struct m88ds3103_priv *priv = fe->demodulator_priv;
i2c_del_mux_adapter(priv->i2c_adapter);
kfree(priv);
} }
static int m88ds3103_tuner_i2c_xfer(struct i2c_adapter *i2c_adap, static int m88ds3103_select(struct i2c_adapter *adap, void *mux_priv, u32 chan)
struct i2c_msg msg[], int num)
{ {
struct m88ds3103_priv *priv = i2c_get_adapdata(i2c_adap); struct m88ds3103_priv *priv = mux_priv;
int ret; int ret;
struct i2c_msg gate_open_msg[1] = { struct i2c_msg gate_open_msg[1] = {
{ {
...@@ -1126,43 +1127,31 @@ static int m88ds3103_tuner_i2c_xfer(struct i2c_adapter *i2c_adap, ...@@ -1126,43 +1127,31 @@ static int m88ds3103_tuner_i2c_xfer(struct i2c_adapter *i2c_adap,
.buf = "\x03\x11", .buf = "\x03\x11",
} }
}; };
dev_dbg(&priv->i2c->dev, "%s: num=%d\n", __func__, num);
mutex_lock(&priv->i2c_mutex); mutex_lock(&priv->i2c_mutex);
/* open i2c-gate */ /* open tuner I2C repeater for 1 xfer, closes automatically */
ret = i2c_transfer(priv->i2c, gate_open_msg, 1); ret = i2c_transfer(priv->i2c, gate_open_msg, 1);
if (ret != 1) { if (ret != 1) {
mutex_unlock(&priv->i2c_mutex); dev_warn(&priv->i2c->dev, "%s: i2c wr failed=%d\n",
dev_warn(&priv->i2c->dev,
"%s: i2c wr failed=%d\n",
KBUILD_MODNAME, ret); KBUILD_MODNAME, ret);
ret = -EREMOTEIO; if (ret >= 0)
goto err; ret = -EREMOTEIO;
}
ret = i2c_transfer(priv->i2c, msg, num); return ret;
mutex_unlock(&priv->i2c_mutex); }
if (ret < 0)
dev_warn(&priv->i2c->dev, "%s: i2c failed=%d\n",
KBUILD_MODNAME, ret);
return ret; return 0;
err:
dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
return ret;
} }
static struct i2c_algorithm m88ds3103_tuner_i2c_algo = { static int m88ds3103_deselect(struct i2c_adapter *adap, void *mux_priv,
.master_xfer = m88ds3103_tuner_i2c_xfer, u32 chan)
.functionality = m88ds3103_tuner_i2c_func,
};
static void m88ds3103_release(struct dvb_frontend *fe)
{ {
struct m88ds3103_priv *priv = fe->demodulator_priv; struct m88ds3103_priv *priv = mux_priv;
i2c_del_adapter(&priv->i2c_adapter);
kfree(priv); mutex_unlock(&priv->i2c_mutex);
return 0;
} }
struct dvb_frontend *m88ds3103_attach(const struct m88ds3103_config *cfg, struct dvb_frontend *m88ds3103_attach(const struct m88ds3103_config *cfg,
...@@ -1228,24 +1217,18 @@ struct dvb_frontend *m88ds3103_attach(const struct m88ds3103_config *cfg, ...@@ -1228,24 +1217,18 @@ struct dvb_frontend *m88ds3103_attach(const struct m88ds3103_config *cfg,
if (ret) if (ret)
goto err; goto err;
/* create mux i2c adapter for tuner */
priv->i2c_adapter = i2c_add_mux_adapter(i2c, &i2c->dev, priv, 0, 0, 0,
m88ds3103_select, m88ds3103_deselect);
if (priv->i2c_adapter == NULL)
goto err;
*tuner_i2c_adapter = priv->i2c_adapter;
/* create dvb_frontend */ /* create dvb_frontend */
memcpy(&priv->fe.ops, &m88ds3103_ops, sizeof(struct dvb_frontend_ops)); memcpy(&priv->fe.ops, &m88ds3103_ops, sizeof(struct dvb_frontend_ops));
priv->fe.demodulator_priv = priv; priv->fe.demodulator_priv = priv;
/* create i2c adapter for tuner */
strlcpy(priv->i2c_adapter.name, KBUILD_MODNAME,
sizeof(priv->i2c_adapter.name));
priv->i2c_adapter.algo = &m88ds3103_tuner_i2c_algo;
priv->i2c_adapter.algo_data = NULL;
i2c_set_adapdata(&priv->i2c_adapter, priv);
ret = i2c_add_adapter(&priv->i2c_adapter);
if (ret) {
dev_err(&i2c->dev, "%s: i2c bus could not be initialized\n",
KBUILD_MODNAME);
goto err;
}
*tuner_i2c_adapter = &priv->i2c_adapter;
return &priv->fe; return &priv->fe;
err: err:
dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret); dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret);
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include "m88ds3103.h" #include "m88ds3103.h"
#include "dvb_math.h" #include "dvb_math.h"
#include <linux/firmware.h> #include <linux/firmware.h>
#include <linux/i2c-mux.h>
#define M88DS3103_FIRMWARE "dvb-demod-m88ds3103.fw" #define M88DS3103_FIRMWARE "dvb-demod-m88ds3103.fw"
#define M88DS3103_MCLK_KHZ 96000 #define M88DS3103_MCLK_KHZ 96000
...@@ -38,7 +39,7 @@ struct m88ds3103_priv { ...@@ -38,7 +39,7 @@ struct m88ds3103_priv {
fe_delivery_system_t delivery_system; fe_delivery_system_t delivery_system;
fe_status_t fe_status; fe_status_t fe_status;
bool warm; /* FW running */ bool warm; /* FW running */
struct i2c_adapter i2c_adapter; struct i2c_adapter *i2c_adapter;
}; };
struct m88ds3103_reg_val { struct m88ds3103_reg_val {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment