Commit 7d58d111 authored by Chris Pascoe's avatar Chris Pascoe Committed by Mauro Carvalho Chehab
Browse files

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: default avatarChris Pascoe <c.pascoe@itee.uq.edu.au>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@infradead.org>
parent e155d908
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -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,			\
+30 −25
Original line number Diff line number Diff line
@@ -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[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