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>
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 @@
 	_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 @@
 	_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;
+	if (i2c_send_recv(priv, buf, 2, ibuf, 2) != 2)
+		return -EIO;
 
-	rc = i2c_rcv(priv, buf, 2);
-	if (rc < 0)
-		return rc;
-
-	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 @@
 			  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 @@
 
 	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 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