summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorChris Pascoe <c.pascoe@itee.uq.edu.au>2007-11-19 04:31:58 -0300
committerMauro Carvalho Chehab <mchehab@infradead.org>2008-01-25 19:02:22 -0200
commit7d58d1117ec02f5fe22ddd03ca08fe2a8c777ea2 (patch)
tree1672eb110109e87ab050415d7727383e8e26f2dc /drivers
parente155d908f72cc429b538c101ee8ffcd10a44b69b (diff)
downloadlinux-3.10-7d58d1117ec02f5fe22ddd03ca08fe2a8c777ea2.tar.gz
linux-3.10-7d58d1117ec02f5fe22ddd03ca08fe2a8c777ea2.tar.bz2
linux-3.10-7d58d1117ec02f5fe22ddd03ca08fe2a8c777ea2.zip
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 <c.pascoe@itee.uq.edu.au> Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/media/video/tuner-i2c.h13
-rw-r--r--drivers/media/video/tuner-xc2028.c55
2 files changed, 43 insertions, 25 deletions
diff --git a/drivers/media/video/tuner-i2c.h b/drivers/media/video/tuner-i2c.h
index b5ac189ba40..d7cf72c3fd7 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 166fede7d0e..a49a4e88689 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