diff options
author | Chris Pascoe <c.pascoe@itee.uq.edu.au> | 2007-11-19 02:31:58 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@infradead.org> | 2008-01-25 16:02:22 -0500 |
commit | 7d58d1117ec02f5fe22ddd03ca08fe2a8c777ea2 (patch) | |
tree | 1672eb110109e87ab050415d7727383e8e26f2dc /drivers/media/video/tuner-xc2028.c | |
parent | e155d908f72cc429b538c101ee8ffcd10a44b69b (diff) |
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/media/video/tuner-xc2028.c')
-rw-r--r-- | drivers/media/video/tuner-xc2028.c | 55 |
1 files changed, 30 insertions, 25 deletions
diff --git a/drivers/media/video/tuner-xc2028.c b/drivers/media/video/tuner-xc2028.c index 166fede7d0e3..a49a4e886897 100644 --- a/drivers/media/video/tuner-xc2028.c +++ b/drivers/media/video/tuner-xc2028.c | |||
@@ -101,6 +101,16 @@ struct xc2028_data { | |||
101 | _rc; \ | 101 | _rc; \ |
102 | }) | 102 | }) |
103 | 103 | ||
104 | #define i2c_send_recv(priv, obuf, osize, ibuf, isize) ({ \ | ||
105 | int _rc; \ | ||
106 | _rc = tuner_i2c_xfer_send_recv(&priv->i2c_props, obuf, osize, \ | ||
107 | ibuf, isize); \ | ||
108 | if (isize != _rc) \ | ||
109 | tuner_err("i2c input error: rc = %d (should be %d)\n", \ | ||
110 | _rc, (int)isize); \ | ||
111 | _rc; \ | ||
112 | }) | ||
113 | |||
104 | #define send_seq(priv, data...) ({ \ | 114 | #define send_seq(priv, data...) ({ \ |
105 | static u8 _val[] = data; \ | 115 | static u8 _val[] = data; \ |
106 | int _rc; \ | 116 | int _rc; \ |
@@ -113,25 +123,21 @@ struct xc2028_data { | |||
113 | _rc; \ | 123 | _rc; \ |
114 | }) | 124 | }) |
115 | 125 | ||
116 | static unsigned int xc2028_get_reg(struct xc2028_data *priv, u16 reg) | 126 | static unsigned int xc2028_get_reg(struct xc2028_data *priv, u16 reg, u16 *val) |
117 | { | 127 | { |
118 | int rc; | ||
119 | unsigned char buf[2]; | 128 | unsigned char buf[2]; |
129 | unsigned char ibuf[2]; | ||
120 | 130 | ||
121 | tuner_dbg("%s called\n", __FUNCTION__); | 131 | tuner_dbg("%s %04x called\n", __FUNCTION__, reg); |
122 | 132 | ||
123 | buf[0] = reg>>8; | 133 | buf[0] = reg >> 8; |
124 | buf[1] = (unsigned char) reg; | 134 | buf[1] = (unsigned char) reg; |
125 | 135 | ||
126 | rc = i2c_send(priv, buf, 2); | 136 | if (i2c_send_recv(priv, buf, 2, ibuf, 2) != 2) |
127 | if (rc < 0) | 137 | return -EIO; |
128 | return rc; | ||
129 | |||
130 | rc = i2c_rcv(priv, buf, 2); | ||
131 | if (rc < 0) | ||
132 | return rc; | ||
133 | 138 | ||
134 | return (buf[1]) | (buf[0] << 8); | 139 | *val = (ibuf[1]) | (ibuf[0] << 8); |
140 | return 0; | ||
135 | } | 141 | } |
136 | 142 | ||
137 | void dump_firm_type(unsigned int type) | 143 | void dump_firm_type(unsigned int type) |
@@ -567,7 +573,8 @@ static int check_firmware(struct dvb_frontend *fe, enum tuner_mode new_mode, | |||
567 | v4l2_std_id std, fe_bandwidth_t bandwidth) | 573 | v4l2_std_id std, fe_bandwidth_t bandwidth) |
568 | { | 574 | { |
569 | struct xc2028_data *priv = fe->tuner_priv; | 575 | struct xc2028_data *priv = fe->tuner_priv; |
570 | int rc, version, hwmodel; | 576 | int rc; |
577 | u16 version, hwmodel; | ||
571 | v4l2_std_id std0 = 0; | 578 | v4l2_std_id std0 = 0; |
572 | unsigned int type0 = 0, type = 0; | 579 | unsigned int type0 = 0, type = 0; |
573 | int change_digital_bandwidth; | 580 | int change_digital_bandwidth; |
@@ -692,8 +699,8 @@ static int check_firmware(struct dvb_frontend *fe, enum tuner_mode new_mode, | |||
692 | 699 | ||
693 | rc = load_scode(fe, type, &std, 0); | 700 | rc = load_scode(fe, type, &std, 0); |
694 | 701 | ||
695 | version = xc2028_get_reg(priv, 0x0004); | 702 | xc2028_get_reg(priv, 0x0004, &version); |
696 | hwmodel = xc2028_get_reg(priv, 0x0008); | 703 | xc2028_get_reg(priv, 0x0008, &hwmodel); |
697 | 704 | ||
698 | tuner_info("Device is Xceive %d version %d.%d, " | 705 | tuner_info("Device is Xceive %d version %d.%d, " |
699 | "firmware version %d.%d\n", | 706 | "firmware version %d.%d\n", |
@@ -708,33 +715,31 @@ static int check_firmware(struct dvb_frontend *fe, enum tuner_mode new_mode, | |||
708 | static int xc2028_signal(struct dvb_frontend *fe, u16 *strength) | 715 | static int xc2028_signal(struct dvb_frontend *fe, u16 *strength) |
709 | { | 716 | { |
710 | struct xc2028_data *priv = fe->tuner_priv; | 717 | struct xc2028_data *priv = fe->tuner_priv; |
711 | int frq_lock, signal = 0; | 718 | u16 frq_lock, signal = 0; |
719 | int rc; | ||
712 | 720 | ||
713 | tuner_dbg("%s called\n", __FUNCTION__); | 721 | tuner_dbg("%s called\n", __FUNCTION__); |
714 | 722 | ||
715 | mutex_lock(&priv->lock); | 723 | mutex_lock(&priv->lock); |
716 | 724 | ||
717 | *strength = 0; | ||
718 | |||
719 | /* Sync Lock Indicator */ | 725 | /* Sync Lock Indicator */ |
720 | frq_lock = xc2028_get_reg(priv, 0x0002); | 726 | rc = xc2028_get_reg(priv, 0x0002, &frq_lock); |
721 | if (frq_lock <= 0) | 727 | if (rc < 0 || frq_lock == 0) |
722 | goto ret; | 728 | goto ret; |
723 | 729 | ||
724 | /* Frequency is locked. Return signal quality */ | 730 | /* Frequency is locked. Return signal quality */ |
725 | 731 | ||
726 | /* Get SNR of the video signal */ | 732 | /* Get SNR of the video signal */ |
727 | signal = xc2028_get_reg(priv, 0x0040); | 733 | rc = xc2028_get_reg(priv, 0x0040, &signal); |
728 | 734 | if (rc < 0) | |
729 | if (signal <= 0) | 735 | signal = -frq_lock; |
730 | signal = frq_lock; | ||
731 | 736 | ||
732 | ret: | 737 | ret: |
733 | mutex_unlock(&priv->lock); | 738 | mutex_unlock(&priv->lock); |
734 | 739 | ||
735 | *strength = signal; | 740 | *strength = signal; |
736 | 741 | ||
737 | return 0; | 742 | return rc; |
738 | } | 743 | } |
739 | 744 | ||
740 | #define DIV 15625 | 745 | #define DIV 15625 |