diff options
author | Antti Palosaari <crope@iki.fi> | 2019-08-23 20:53:37 -0300 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab+samsung@kernel.org> | 2019-10-07 07:48:13 -0300 |
commit | 1ea76d16569b7fc242b860c7e19549be028b13d1 (patch) | |
tree | bf9961bd0c8d06522921812284b7fd5fdb5e6ead /drivers/media/usb | |
parent | cecf0bbbcb6f035a5ca2197f3e11ec2b7fb3da83 (diff) | |
download | linux-1ea76d16569b7fc242b860c7e19549be028b13d1.tar.bz2 |
media: gl861: re-implement I2C adapter logic
Device I2C adapter is capable of writing and reading large messages.
For I2C writes there is 2 methods: simple for max 2 byte messages and
usb_control_msg() with payload data for larger I2C messages. Add I2C
adapter logic which selects suitable method according to message size.
Add also support for plain I2C read.
Cc: Akihiro TSUKADA <tskd08@gmail.com>
Signed-off-by: Antti Palosaari <crope@iki.fi>
Signed-off-by: Sean Young <sean@mess.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
Diffstat (limited to 'drivers/media/usb')
-rw-r--r-- | drivers/media/usb/dvb-usb-v2/gl861.c | 216 |
1 files changed, 159 insertions, 57 deletions
diff --git a/drivers/media/usb/dvb-usb-v2/gl861.c b/drivers/media/usb/dvb-usb-v2/gl861.c index c7197e534c02..f4003e3d4b18 100644 --- a/drivers/media/usb/dvb-usb-v2/gl861.c +++ b/drivers/media/usb/dvb-usb-v2/gl861.c @@ -14,6 +14,154 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +struct gl861 { + /* USB control message buffer */ + u8 buf[16]; + + struct i2c_adapter *demod_sub_i2c; + struct i2c_client *i2c_client_demod; + struct i2c_client *i2c_client_tuner; + struct i2c_adapter tuner_adap; +}; + +#define CMD_WRITE_SHORT 0x01 +#define CMD_READ 0x02 +#define CMD_WRITE 0x03 + +static int gl861_ctrl_msg(struct dvb_usb_device *d, u8 request, u16 value, + u16 index, void *data, u16 size) +{ + struct gl861 *ctx = d_to_priv(d); + struct usb_interface *intf = d->intf; + int ret; + unsigned int pipe; + u8 requesttype; + + mutex_lock(&d->usb_mutex); + + switch (request) { + case CMD_WRITE: + memcpy(ctx->buf, data, size); + /* Fall through */ + case CMD_WRITE_SHORT: + pipe = usb_sndctrlpipe(d->udev, 0); + requesttype = USB_TYPE_VENDOR | USB_DIR_OUT; + break; + case CMD_READ: + pipe = usb_rcvctrlpipe(d->udev, 0); + requesttype = USB_TYPE_VENDOR | USB_DIR_IN; + break; + default: + ret = -EINVAL; + goto err_mutex_unlock; + } + + ret = usb_control_msg(d->udev, pipe, request, requesttype, value, + index, ctx->buf, size, 200); + dev_dbg(&intf->dev, "%d | %02x %02x %*ph %*ph %*ph %s %*ph\n", + ret, requesttype, request, 2, &value, 2, &index, 2, &size, + (requesttype & USB_DIR_IN) ? "<<<" : ">>>", size, ctx->buf); + if (ret < 0) + goto err_mutex_unlock; + + if (request == CMD_READ) + memcpy(data, ctx->buf, size); + + usleep_range(1000, 2000); /* Avoid I2C errors */ + + mutex_unlock(&d->usb_mutex); + + return 0; + +err_mutex_unlock: + mutex_unlock(&d->usb_mutex); + dev_dbg(&intf->dev, "failed %d\n", ret); + return ret; +} + +static int gl861_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + struct usb_interface *intf = d->intf; + struct gl861 *ctx = d_to_priv(d); + int ret; + u8 request, *data; + u16 value, index, size; + + /* XXX: I2C adapter maximum data lengths are not tested */ + if (num == 1 && !(msg[0].flags & I2C_M_RD)) { + /* I2C write */ + if (msg[0].len < 2 || msg[0].len > sizeof(ctx->buf)) { + ret = -EOPNOTSUPP; + goto err; + } + + value = (msg[0].addr << 1) << 8; + index = msg[0].buf[0]; + + if (msg[0].len == 2) { + request = CMD_WRITE_SHORT; + value |= msg[0].buf[1]; + size = 0; + data = NULL; + } else { + request = CMD_WRITE; + size = msg[0].len - 1; + data = &msg[0].buf[1]; + } + + ret = gl861_ctrl_msg(d, request, value, index, data, size); + } else if (num == 2 && !(msg[0].flags & I2C_M_RD) && + (msg[1].flags & I2C_M_RD)) { + /* I2C write + read */ + if (msg[0].len > 1 || msg[1].len > sizeof(ctx->buf)) { + ret = -EOPNOTSUPP; + goto err; + } + + value = (msg[0].addr << 1) << 8; + index = msg[0].buf[0]; + request = CMD_READ; + + ret = gl861_ctrl_msg(d, request, value, index, + msg[1].buf, msg[1].len); + } else if (num == 1 && (msg[0].flags & I2C_M_RD)) { + /* I2C read */ + if (msg[0].len > sizeof(ctx->buf)) { + ret = -EOPNOTSUPP; + goto err; + } + value = (msg[0].addr << 1) << 8; + index = 0x0100; + request = CMD_READ; + + ret = gl861_ctrl_msg(d, request, value, index, + msg[0].buf, msg[0].len); + } else { + /* Unsupported I2C message */ + dev_dbg(&intf->dev, "unknown i2c msg, num %u\n", num); + ret = -EOPNOTSUPP; + } + if (ret) + goto err; + + return num; +err: + dev_dbg(&intf->dev, "failed %d\n", ret); + return ret; +} + +static u32 gl861_i2c_functionality(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm gl861_i2c_algo = { + .master_xfer = gl861_i2c_master_xfer, + .functionality = gl861_i2c_functionality, +}; + static int gl861_i2c_msg(struct dvb_usb_device *d, u8 addr, u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) { @@ -63,46 +211,6 @@ static int gl861_i2c_msg(struct dvb_usb_device *d, u8 addr, return ret; } -/* I2C */ -static int gl861_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], - int num) -{ - struct dvb_usb_device *d = i2c_get_adapdata(adap); - int i; - - if (num > 2) - return -EINVAL; - - if (mutex_lock_interruptible(&d->i2c_mutex) < 0) - return -EAGAIN; - - for (i = 0; i < num; i++) { - /* write/read request */ - if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) { - if (gl861_i2c_msg(d, msg[i].addr, msg[i].buf, - msg[i].len, msg[i+1].buf, msg[i+1].len) < 0) - break; - i++; - } else - if (gl861_i2c_msg(d, msg[i].addr, msg[i].buf, - msg[i].len, NULL, 0) < 0) - break; - } - - mutex_unlock(&d->i2c_mutex); - return i; -} - -static u32 gl861_i2c_func(struct i2c_adapter *adapter) -{ - return I2C_FUNC_I2C; -} - -static struct i2c_algorithm gl861_i2c_algo = { - .master_xfer = gl861_i2c_xfer, - .functionality = gl861_i2c_func, -}; - /* Callbacks for DVB USB */ static struct zl10353_config gl861_zl10353_config = { .demod_address = 0x0f, @@ -149,6 +257,8 @@ static struct dvb_usb_device_properties gl861_props = { .owner = THIS_MODULE, .adapter_nr = adapter_nr, + .size_of_priv = sizeof(struct gl861), + .i2c_algo = &gl861_i2c_algo, .frontend_attach = gl861_frontend_attach, .tuner_attach = gl861_tuner_attach, @@ -166,14 +276,6 @@ static struct dvb_usb_device_properties gl861_props = { /* * For Friio */ - -struct friio_priv { - struct i2c_adapter *demod_sub_i2c; - struct i2c_client *i2c_client_demod; - struct i2c_client *i2c_client_tuner; - struct i2c_adapter tuner_adap; -}; - struct friio_config { struct i2c_board_info demod_info; struct tc90522_config demod_cfg; @@ -242,7 +344,7 @@ gl861_i2c_read_ex(struct dvb_usb_device *d, u8 addr, u8 *rbuf, u16 rlen) static int friio_i2c_tuner_read(struct dvb_usb_device *d, struct i2c_msg *msg) { - struct friio_priv *priv; + struct gl861 *priv; u8 addr; priv = d_to_priv(d); @@ -255,7 +357,7 @@ friio_i2c_tuner_write(struct dvb_usb_device *d, struct i2c_msg *msg) { u8 *buf; int ret; - struct friio_priv *priv; + struct gl861 *priv; priv = d_to_priv(d); @@ -308,7 +410,7 @@ static int friio_tuner_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], static struct i2c_algorithm friio_tuner_i2c_algo = { .master_xfer = friio_tuner_i2c_xfer, - .functionality = gl861_i2c_func, + .functionality = gl861_i2c_functionality, }; /* GPIO control in Friio */ @@ -488,7 +590,7 @@ static int friio_frontend_attach(struct dvb_usb_adapter *adap) struct dvb_usb_device *d; struct tc90522_config cfg; struct i2c_client *cl; - struct friio_priv *priv; + struct gl861 *priv; info = &friio_config.demod_info; d = adap_to_d(adap); @@ -513,7 +615,7 @@ static int friio_frontend_attach(struct dvb_usb_adapter *adap) static int friio_frontend_detach(struct dvb_usb_adapter *adap) { - struct friio_priv *priv; + struct gl861 *priv; priv = adap_to_priv(adap); i2c_del_adapter(&priv->tuner_adap); @@ -526,7 +628,7 @@ static int friio_tuner_attach(struct dvb_usb_adapter *adap) const struct i2c_board_info *info; struct dvb_pll_config cfg; struct i2c_client *cl; - struct friio_priv *priv; + struct gl861 *priv; priv = adap_to_priv(adap); info = &friio_config.tuner_info; @@ -543,7 +645,7 @@ static int friio_tuner_attach(struct dvb_usb_adapter *adap) static int friio_tuner_detach(struct dvb_usb_adapter *adap) { - struct friio_priv *priv; + struct gl861 *priv; priv = adap_to_priv(adap); dvb_module_release(priv->i2c_client_tuner); @@ -554,7 +656,7 @@ static int friio_init(struct dvb_usb_device *d) { int i; int ret; - struct friio_priv *priv; + struct gl861 *priv; static const u8 demod_init[][2] = { {0x01, 0x40}, {0x04, 0x38}, {0x05, 0x40}, {0x07, 0x40}, @@ -606,7 +708,7 @@ static struct dvb_usb_device_properties friio_props = { .owner = THIS_MODULE, .adapter_nr = adapter_nr, - .size_of_priv = sizeof(struct friio_priv), + .size_of_priv = sizeof(struct gl861), .i2c_algo = &gl861_i2c_algo, .power_ctrl = friio_power_ctrl, |