From 7d58d1117ec02f5fe22ddd03ca08fe2a8c777ea2 Mon Sep 17 00:00:00 2001 From: Chris Pascoe Date: Mon, 19 Nov 2007 04:31:58 -0300 Subject: [PATCH] V4L/DVB (6633): xc2028: make register reads atomic Issuing register reads as a separate address write and data read transactions means that other I2C activity could occur in between and state could get out of sync. Issue both the write and read in a single transaction so that the i2c layer can prevent other users accessing the bus until we are complete. Signed-off-by: Chris Pascoe Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/tuner-i2c.h | 13 +++++++++ drivers/media/video/tuner-xc2028.c | 55 +++++++++++++++++++++----------------- 2 files changed, 43 insertions(+), 25 deletions(-) diff --git a/drivers/media/video/tuner-i2c.h b/drivers/media/video/tuner-i2c.h index b5ac189..d7cf72c 100644 --- a/drivers/media/video/tuner-i2c.h +++ b/drivers/media/video/tuner-i2c.h @@ -46,6 +46,19 @@ static inline int tuner_i2c_xfer_recv(struct tuner_i2c_props *props, char *buf, return (ret == 1) ? len : ret; } +static inline int tuner_i2c_xfer_send_recv(struct tuner_i2c_props *props, + char *obuf, int olen, + char *ibuf, int ilen) +{ + struct i2c_msg msg[2] = { { .addr = props->addr, .flags = 0, + .buf = obuf, .len = olen }, + { .addr = props->addr, .flags = I2C_M_RD, + .buf = ibuf, .len = ilen } }; + int ret = i2c_transfer(props->adap, msg, 2); + + return (ret == 2) ? ilen : ret; +} + #ifndef __TUNER_DRIVER_H__ #define tuner_warn(fmt, arg...) do { \ printk(KERN_WARNING "%s %d-%04x: " fmt, PREFIX, \ diff --git a/drivers/media/video/tuner-xc2028.c b/drivers/media/video/tuner-xc2028.c index 166fede..a49a4e8 100644 --- a/drivers/media/video/tuner-xc2028.c +++ b/drivers/media/video/tuner-xc2028.c @@ -101,6 +101,16 @@ struct xc2028_data { _rc; \ }) +#define i2c_send_recv(priv, obuf, osize, ibuf, isize) ({ \ + int _rc; \ + _rc = tuner_i2c_xfer_send_recv(&priv->i2c_props, obuf, osize, \ + ibuf, isize); \ + if (isize != _rc) \ + tuner_err("i2c input error: rc = %d (should be %d)\n", \ + _rc, (int)isize); \ + _rc; \ +}) + #define send_seq(priv, data...) ({ \ static u8 _val[] = data; \ int _rc; \ @@ -113,25 +123,21 @@ struct xc2028_data { _rc; \ }) -static unsigned int xc2028_get_reg(struct xc2028_data *priv, u16 reg) +static unsigned int xc2028_get_reg(struct xc2028_data *priv, u16 reg, u16 *val) { - int rc; unsigned char buf[2]; + unsigned char ibuf[2]; - tuner_dbg("%s called\n", __FUNCTION__); + tuner_dbg("%s %04x called\n", __FUNCTION__, reg); - buf[0] = reg>>8; + buf[0] = reg >> 8; buf[1] = (unsigned char) reg; - rc = i2c_send(priv, buf, 2); - if (rc < 0) - return rc; - - rc = i2c_rcv(priv, buf, 2); - if (rc < 0) - return rc; + if (i2c_send_recv(priv, buf, 2, ibuf, 2) != 2) + return -EIO; - return (buf[1]) | (buf[0] << 8); + *val = (ibuf[1]) | (ibuf[0] << 8); + return 0; } void dump_firm_type(unsigned int type) @@ -567,7 +573,8 @@ static int check_firmware(struct dvb_frontend *fe, enum tuner_mode new_mode, v4l2_std_id std, fe_bandwidth_t bandwidth) { struct xc2028_data *priv = fe->tuner_priv; - int rc, version, hwmodel; + int rc; + u16 version, hwmodel; v4l2_std_id std0 = 0; unsigned int type0 = 0, type = 0; int change_digital_bandwidth; @@ -692,8 +699,8 @@ static int check_firmware(struct dvb_frontend *fe, enum tuner_mode new_mode, rc = load_scode(fe, type, &std, 0); - version = xc2028_get_reg(priv, 0x0004); - hwmodel = xc2028_get_reg(priv, 0x0008); + xc2028_get_reg(priv, 0x0004, &version); + xc2028_get_reg(priv, 0x0008, &hwmodel); tuner_info("Device is Xceive %d version %d.%d, " "firmware version %d.%d\n", @@ -708,33 +715,31 @@ static int check_firmware(struct dvb_frontend *fe, enum tuner_mode new_mode, static int xc2028_signal(struct dvb_frontend *fe, u16 *strength) { struct xc2028_data *priv = fe->tuner_priv; - int frq_lock, signal = 0; + u16 frq_lock, signal = 0; + int rc; tuner_dbg("%s called\n", __FUNCTION__); mutex_lock(&priv->lock); - *strength = 0; - /* Sync Lock Indicator */ - frq_lock = xc2028_get_reg(priv, 0x0002); - if (frq_lock <= 0) + rc = xc2028_get_reg(priv, 0x0002, &frq_lock); + if (rc < 0 || frq_lock == 0) goto ret; /* Frequency is locked. Return signal quality */ /* Get SNR of the video signal */ - signal = xc2028_get_reg(priv, 0x0040); - - if (signal <= 0) - signal = frq_lock; + rc = xc2028_get_reg(priv, 0x0040, &signal); + if (rc < 0) + signal = -frq_lock; ret: mutex_unlock(&priv->lock); *strength = signal; - return 0; + return rc; } #define DIV 15625 -- 1.8.2.3