xref: /linux/drivers/media/usb/em28xx/em28xx-i2c.c (revision 0b3966e40c99afeb89849b5cf7b100a3bb4271cd)
1a6c2ba28Sakpm@osdl.org /*
2f7abcd38SMauro Carvalho Chehab    em28xx-i2c.c - driver for Empia EM2800/EM2820/2840 USB video capture devices
3a6c2ba28Sakpm@osdl.org 
4f7abcd38SMauro Carvalho Chehab    Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it>
5f7abcd38SMauro Carvalho Chehab 		      Markus Rechberger <mrechberger@gmail.com>
62e7c6dc3SMauro Carvalho Chehab 		      Mauro Carvalho Chehab <mchehab@infradead.org>
7f7abcd38SMauro Carvalho Chehab 		      Sascha Sommer <saschasommer@freenet.de>
8a6c2ba28Sakpm@osdl.org 
9a6c2ba28Sakpm@osdl.org    This program is free software; you can redistribute it and/or modify
10a6c2ba28Sakpm@osdl.org    it under the terms of the GNU General Public License as published by
11a6c2ba28Sakpm@osdl.org    the Free Software Foundation; either version 2 of the License, or
12a6c2ba28Sakpm@osdl.org    (at your option) any later version.
13a6c2ba28Sakpm@osdl.org 
14a6c2ba28Sakpm@osdl.org    This program is distributed in the hope that it will be useful,
15a6c2ba28Sakpm@osdl.org    but WITHOUT ANY WARRANTY; without even the implied warranty of
16a6c2ba28Sakpm@osdl.org    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17a6c2ba28Sakpm@osdl.org    GNU General Public License for more details.
18a6c2ba28Sakpm@osdl.org 
19a6c2ba28Sakpm@osdl.org    You should have received a copy of the GNU General Public License
20a6c2ba28Sakpm@osdl.org    along with this program; if not, write to the Free Software
21a6c2ba28Sakpm@osdl.org    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22a6c2ba28Sakpm@osdl.org  */
23a6c2ba28Sakpm@osdl.org 
24a6c2ba28Sakpm@osdl.org #include <linux/module.h>
25a6c2ba28Sakpm@osdl.org #include <linux/kernel.h>
26a6c2ba28Sakpm@osdl.org #include <linux/usb.h>
27a6c2ba28Sakpm@osdl.org #include <linux/i2c.h>
28a6c2ba28Sakpm@osdl.org 
29f7abcd38SMauro Carvalho Chehab #include "em28xx.h"
306c362c8eSMauro Carvalho Chehab #include "tuner-xc2028.h"
315e453dc7SMichael Krufky #include <media/v4l2-common.h>
32d5e52653SMauro Carvalho Chehab #include <media/tuner.h>
33a6c2ba28Sakpm@osdl.org 
34a6c2ba28Sakpm@osdl.org /* ----------------------------------------------------------- */
35a6c2ba28Sakpm@osdl.org 
36ff699e6bSDouglas Schilling Landgraf static unsigned int i2c_scan;
37a6c2ba28Sakpm@osdl.org module_param(i2c_scan, int, 0444);
38a6c2ba28Sakpm@osdl.org MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time");
39a6c2ba28Sakpm@osdl.org 
40ff699e6bSDouglas Schilling Landgraf static unsigned int i2c_debug;
41a6c2ba28Sakpm@osdl.org module_param(i2c_debug, int, 0644);
42a6c2ba28Sakpm@osdl.org MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]");
43a6c2ba28Sakpm@osdl.org 
446ea54d93SDouglas Schilling Landgraf #define dprintk2(lvl, fmt, args...)			\
456ea54d93SDouglas Schilling Landgraf do {							\
466ea54d93SDouglas Schilling Landgraf 	if (i2c_debug >= lvl) {				\
47d5e52653SMauro Carvalho Chehab 		printk(KERN_DEBUG "%s at %s: " fmt,	\
486ea54d93SDouglas Schilling Landgraf 		       dev->name, __func__ , ##args);	\
496ea54d93SDouglas Schilling Landgraf       } 						\
506ea54d93SDouglas Schilling Landgraf } while (0)
51a6c2ba28Sakpm@osdl.org 
52a6c2ba28Sakpm@osdl.org /*
53f5ae371aSFrank Schaefer  * em2800_i2c_send_bytes()
54f5ae371aSFrank Schaefer  * send up to 4 bytes to the em2800 i2c device
55596d92d5SMauro Carvalho Chehab  */
56f5ae371aSFrank Schaefer static int em2800_i2c_send_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len)
57596d92d5SMauro Carvalho Chehab {
58596d92d5SMauro Carvalho Chehab 	int ret;
59596d92d5SMauro Carvalho Chehab 	int write_timeout;
60a6bad040SFrank Schaefer 	u8 b2[6];
61f5ae371aSFrank Schaefer 
62f5ae371aSFrank Schaefer 	if (len < 1 || len > 4)
63f5ae371aSFrank Schaefer 		return -EOPNOTSUPP;
64f5ae371aSFrank Schaefer 
65596d92d5SMauro Carvalho Chehab 	BUG_ON(len < 1 || len > 4);
66596d92d5SMauro Carvalho Chehab 	b2[5] = 0x80 + len - 1;
67596d92d5SMauro Carvalho Chehab 	b2[4] = addr;
68596d92d5SMauro Carvalho Chehab 	b2[3] = buf[0];
69596d92d5SMauro Carvalho Chehab 	if (len > 1)
70596d92d5SMauro Carvalho Chehab 		b2[2] = buf[1];
71596d92d5SMauro Carvalho Chehab 	if (len > 2)
72596d92d5SMauro Carvalho Chehab 		b2[1] = buf[2];
73596d92d5SMauro Carvalho Chehab 	if (len > 3)
74596d92d5SMauro Carvalho Chehab 		b2[0] = buf[3];
75596d92d5SMauro Carvalho Chehab 
762fcc82d8SFrank Schaefer 	/* trigger write */
773acf2809SMauro Carvalho Chehab 	ret = dev->em28xx_write_regs(dev, 4 - len, &b2[4 - len], 2 + len);
78596d92d5SMauro Carvalho Chehab 	if (ret != 2 + len) {
7945f04e82SFrank Schaefer 		em28xx_warn("failed to trigger write to i2c address 0x%x "
8045f04e82SFrank Schaefer 			    "(error=%i)\n", addr, ret);
8145f04e82SFrank Schaefer 		return (ret < 0) ? ret : -EIO;
82596d92d5SMauro Carvalho Chehab 	}
832fcc82d8SFrank Schaefer 	/* wait for completion */
842fcc82d8SFrank Schaefer 	for (write_timeout = EM2800_I2C_XFER_TIMEOUT; write_timeout > 0;
85596d92d5SMauro Carvalho Chehab 	     write_timeout -= 5) {
863acf2809SMauro Carvalho Chehab 		ret = dev->em28xx_read_reg(dev, 0x05);
8745f04e82SFrank Schaefer 		if (ret == 0x80 + len - 1) {
88596d92d5SMauro Carvalho Chehab 			return len;
8945f04e82SFrank Schaefer 		} else if (ret == 0x94 + len - 1) {
9045f04e82SFrank Schaefer 			return -ENODEV;
9145f04e82SFrank Schaefer 		} else if (ret < 0) {
9245f04e82SFrank Schaefer 			em28xx_warn("failed to get i2c transfer status from "
9345f04e82SFrank Schaefer 				    "bridge register (error=%i)\n", ret);
9445f04e82SFrank Schaefer 			return ret;
9545f04e82SFrank Schaefer 		}
96e8e41da4SMarkus Rechberger 		msleep(5);
97596d92d5SMauro Carvalho Chehab 	}
9845f04e82SFrank Schaefer 	em28xx_warn("write to i2c device at 0x%x timed out\n", addr);
99596d92d5SMauro Carvalho Chehab 	return -EIO;
100596d92d5SMauro Carvalho Chehab }
101596d92d5SMauro Carvalho Chehab 
102596d92d5SMauro Carvalho Chehab /*
103596d92d5SMauro Carvalho Chehab  * em2800_i2c_recv_bytes()
1042fcc82d8SFrank Schaefer  * read up to 4 bytes from the em2800 i2c device
105596d92d5SMauro Carvalho Chehab  */
106a6bad040SFrank Schaefer static int em2800_i2c_recv_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len)
107596d92d5SMauro Carvalho Chehab {
1082fcc82d8SFrank Schaefer 	u8 buf2[4];
109596d92d5SMauro Carvalho Chehab 	int ret;
1102fcc82d8SFrank Schaefer 	int read_timeout;
1112fcc82d8SFrank Schaefer 	int i;
112f5ae371aSFrank Schaefer 
113f5ae371aSFrank Schaefer 	if (len < 1 || len > 4)
114f5ae371aSFrank Schaefer 		return -EOPNOTSUPP;
115f5ae371aSFrank Schaefer 
1162fcc82d8SFrank Schaefer 	/* trigger read */
1172fcc82d8SFrank Schaefer 	buf2[1] = 0x84 + len - 1;
1182fcc82d8SFrank Schaefer 	buf2[0] = addr;
1192fcc82d8SFrank Schaefer 	ret = dev->em28xx_write_regs(dev, 0x04, buf2, 2);
1202fcc82d8SFrank Schaefer 	if (ret != 2) {
1212fcc82d8SFrank Schaefer 		em28xx_warn("failed to trigger read from i2c address 0x%x "
1222fcc82d8SFrank Schaefer 			    "(error=%i)\n", addr, ret);
1232fcc82d8SFrank Schaefer 		return (ret < 0) ? ret : -EIO;
1242fcc82d8SFrank Schaefer 	}
1252fcc82d8SFrank Schaefer 
1262fcc82d8SFrank Schaefer 	/* wait for completion */
1272fcc82d8SFrank Schaefer 	for (read_timeout = EM2800_I2C_XFER_TIMEOUT; read_timeout > 0;
1282fcc82d8SFrank Schaefer 	     read_timeout -= 5) {
1292fcc82d8SFrank Schaefer 		ret = dev->em28xx_read_reg(dev, 0x05);
1302fcc82d8SFrank Schaefer 		if (ret == 0x84 + len - 1) {
1312fcc82d8SFrank Schaefer 			break;
1322fcc82d8SFrank Schaefer 		} else if (ret == 0x94 + len - 1) {
1332fcc82d8SFrank Schaefer 			return -ENODEV;
1342fcc82d8SFrank Schaefer 		} else if (ret < 0) {
1352fcc82d8SFrank Schaefer 			em28xx_warn("failed to get i2c transfer status from "
1362fcc82d8SFrank Schaefer 				    "bridge register (error=%i)\n", ret);
137596d92d5SMauro Carvalho Chehab 			return ret;
138596d92d5SMauro Carvalho Chehab 		}
1392fcc82d8SFrank Schaefer 		msleep(5);
1402fcc82d8SFrank Schaefer 	}
1412fcc82d8SFrank Schaefer 	if (ret != 0x84 + len - 1)
1422fcc82d8SFrank Schaefer 		em28xx_warn("read from i2c device at 0x%x timed out\n", addr);
1432fcc82d8SFrank Schaefer 
1442fcc82d8SFrank Schaefer 	/* get the received message */
1452fcc82d8SFrank Schaefer 	ret = dev->em28xx_read_reg_req_len(dev, 0x00, 4-len, buf2, len);
1462fcc82d8SFrank Schaefer 	if (ret != len) {
1472fcc82d8SFrank Schaefer 		em28xx_warn("reading from i2c device at 0x%x failed: "
1482fcc82d8SFrank Schaefer 			    "couldn't get the received message from the bridge "
1492fcc82d8SFrank Schaefer 			    "(error=%i)\n", addr, ret);
1502fcc82d8SFrank Schaefer 		return (ret < 0) ? ret : -EIO;
1512fcc82d8SFrank Schaefer 	}
1522fcc82d8SFrank Schaefer 	for (i = 0; i < len; i++)
1532fcc82d8SFrank Schaefer 		buf[i] = buf2[len - 1 - i];
1542fcc82d8SFrank Schaefer 
155596d92d5SMauro Carvalho Chehab 	return ret;
156596d92d5SMauro Carvalho Chehab }
1572fcc82d8SFrank Schaefer 
1582fcc82d8SFrank Schaefer /*
1592fcc82d8SFrank Schaefer  * em2800_i2c_check_for_device()
1602fcc82d8SFrank Schaefer  * check if there is an i2c device at the supplied address
1612fcc82d8SFrank Schaefer  */
1622fcc82d8SFrank Schaefer static int em2800_i2c_check_for_device(struct em28xx *dev, u8 addr)
1632fcc82d8SFrank Schaefer {
1642fcc82d8SFrank Schaefer 	u8 buf;
1652fcc82d8SFrank Schaefer 	int ret;
1662fcc82d8SFrank Schaefer 
1672fcc82d8SFrank Schaefer 	ret = em2800_i2c_recv_bytes(dev, addr, &buf, 1);
1682fcc82d8SFrank Schaefer 	if (ret == 1)
1692fcc82d8SFrank Schaefer 		return 0;
1702fcc82d8SFrank Schaefer 	return (ret < 0) ? ret : -EIO;
171596d92d5SMauro Carvalho Chehab }
172596d92d5SMauro Carvalho Chehab 
173596d92d5SMauro Carvalho Chehab /*
1743acf2809SMauro Carvalho Chehab  * em28xx_i2c_send_bytes()
175a6c2ba28Sakpm@osdl.org  */
176a6bad040SFrank Schaefer static int em28xx_i2c_send_bytes(struct em28xx *dev, u16 addr, u8 *buf,
177a6bad040SFrank Schaefer 				 u16 len, int stop)
178a6c2ba28Sakpm@osdl.org {
179bbc70e64SMauro Carvalho Chehab 	int write_timeout, ret;
180a6c2ba28Sakpm@osdl.org 
181f5ae371aSFrank Schaefer 	if (len < 1 || len > 64)
182f5ae371aSFrank Schaefer 		return -EOPNOTSUPP;
18345f04e82SFrank Schaefer 	/* NOTE: limited by the USB ctrl message constraints
18445f04e82SFrank Schaefer 	 * Zero length reads always succeed, even if no device is connected */
185f5ae371aSFrank Schaefer 
18645f04e82SFrank Schaefer 	/* Write to i2c device */
18745f04e82SFrank Schaefer 	ret = dev->em28xx_write_regs_req(dev, stop ? 2 : 3, addr, buf, len);
18845f04e82SFrank Schaefer 	if (ret != len) {
18945f04e82SFrank Schaefer 		if (ret < 0) {
19045f04e82SFrank Schaefer 			em28xx_warn("writing to i2c device at 0x%x failed "
19145f04e82SFrank Schaefer 				    "(error=%i)\n", addr, ret);
19245f04e82SFrank Schaefer 			return ret;
19345f04e82SFrank Schaefer 		} else {
19445f04e82SFrank Schaefer 			em28xx_warn("%i bytes write to i2c device at 0x%x "
19545f04e82SFrank Schaefer 				    "requested, but %i bytes written\n",
19645f04e82SFrank Schaefer 				    len, addr, ret);
19745f04e82SFrank Schaefer 			return -EIO;
19845f04e82SFrank Schaefer 		}
19945f04e82SFrank Schaefer 	}
200a6c2ba28Sakpm@osdl.org 
20145f04e82SFrank Schaefer 	/* Check success of the i2c operation */
2022fcc82d8SFrank Schaefer 	for (write_timeout = EM2800_I2C_XFER_TIMEOUT; write_timeout > 0;
203bbc70e64SMauro Carvalho Chehab 	     write_timeout -= 5) {
204bbc70e64SMauro Carvalho Chehab 		ret = dev->em28xx_read_reg(dev, 0x05);
20545f04e82SFrank Schaefer 		if (ret == 0) { /* success */
20645f04e82SFrank Schaefer 			return len;
20745f04e82SFrank Schaefer 		} else if (ret == 0x10) {
20845f04e82SFrank Schaefer 			return -ENODEV;
20945f04e82SFrank Schaefer 		} else if (ret < 0) {
21045f04e82SFrank Schaefer 			em28xx_warn("failed to read i2c transfer status from "
21145f04e82SFrank Schaefer 				    "bridge (error=%i)\n", ret);
21245f04e82SFrank Schaefer 			return ret;
213bbc70e64SMauro Carvalho Chehab 		}
21445f04e82SFrank Schaefer 		msleep(5);
21545f04e82SFrank Schaefer 		/* NOTE: do we really have to wait for success ?
21645f04e82SFrank Schaefer 		   Never seen anything else than 0x00 or 0x10
21745f04e82SFrank Schaefer 		   (even with high payload) ...			*/
21845f04e82SFrank Schaefer 	}
21945f04e82SFrank Schaefer 	em28xx_warn("write to i2c device at 0x%x timed out\n", addr);
22045f04e82SFrank Schaefer 	return -EIO;
221a6c2ba28Sakpm@osdl.org }
222a6c2ba28Sakpm@osdl.org 
223a6c2ba28Sakpm@osdl.org /*
2243acf2809SMauro Carvalho Chehab  * em28xx_i2c_recv_bytes()
225a6c2ba28Sakpm@osdl.org  * read a byte from the i2c device
226a6c2ba28Sakpm@osdl.org  */
227a6bad040SFrank Schaefer static int em28xx_i2c_recv_bytes(struct em28xx *dev, u16 addr, u8 *buf, u16 len)
228a6c2ba28Sakpm@osdl.org {
229a6c2ba28Sakpm@osdl.org 	int ret;
230f5ae371aSFrank Schaefer 
231f5ae371aSFrank Schaefer 	if (len < 1 || len > 64)
232f5ae371aSFrank Schaefer 		return -EOPNOTSUPP;
23345f04e82SFrank Schaefer 	/* NOTE: limited by the USB ctrl message constraints
23445f04e82SFrank Schaefer 	 * Zero length reads always succeed, even if no device is connected */
235f5ae371aSFrank Schaefer 
23645f04e82SFrank Schaefer 	/* Read data from i2c device */
2373acf2809SMauro Carvalho Chehab 	ret = dev->em28xx_read_reg_req_len(dev, 2, addr, buf, len);
23845f04e82SFrank Schaefer 	if (ret != len) {
239a6c2ba28Sakpm@osdl.org 		if (ret < 0) {
24045f04e82SFrank Schaefer 			em28xx_warn("reading from i2c device at 0x%x failed "
24145f04e82SFrank Schaefer 				    "(error=%i)\n", addr, ret);
24245f04e82SFrank Schaefer 			return ret;
24345f04e82SFrank Schaefer 		} else {
24445f04e82SFrank Schaefer 			em28xx_warn("%i bytes requested from i2c device at "
24545f04e82SFrank Schaefer 				    "0x%x, but %i bytes received\n",
24645f04e82SFrank Schaefer 				    len, addr, ret);
24745f04e82SFrank Schaefer 			return -EIO;
24845f04e82SFrank Schaefer 		}
24945f04e82SFrank Schaefer 	}
25045f04e82SFrank Schaefer 
25145f04e82SFrank Schaefer 	/* Check success of the i2c operation */
25245f04e82SFrank Schaefer 	ret = dev->em28xx_read_reg(dev, 0x05);
25345f04e82SFrank Schaefer 	if (ret < 0) {
25445f04e82SFrank Schaefer 		em28xx_warn("failed to read i2c transfer status from "
25545f04e82SFrank Schaefer 			    "bridge (error=%i)\n", ret);
256a6c2ba28Sakpm@osdl.org 		return ret;
257a6c2ba28Sakpm@osdl.org 	}
25845f04e82SFrank Schaefer 	if (ret > 0) {
25945f04e82SFrank Schaefer 		if (ret == 0x10) {
260a6c2ba28Sakpm@osdl.org 			return -ENODEV;
26145f04e82SFrank Schaefer 		} else {
26245f04e82SFrank Schaefer 			em28xx_warn("unknown i2c error (status=%i)\n", ret);
26345f04e82SFrank Schaefer 			return -EIO;
26445f04e82SFrank Schaefer 		}
26545f04e82SFrank Schaefer 	}
26645f04e82SFrank Schaefer 	return len;
267a6c2ba28Sakpm@osdl.org }
268a6c2ba28Sakpm@osdl.org 
269a6c2ba28Sakpm@osdl.org /*
2703acf2809SMauro Carvalho Chehab  * em28xx_i2c_check_for_device()
271a6c2ba28Sakpm@osdl.org  * check if there is a i2c_device at the supplied address
272a6c2ba28Sakpm@osdl.org  */
273a6bad040SFrank Schaefer static int em28xx_i2c_check_for_device(struct em28xx *dev, u16 addr)
274a6c2ba28Sakpm@osdl.org {
275a6c2ba28Sakpm@osdl.org 	int ret;
27645f04e82SFrank Schaefer 	u8 buf;
277a6c2ba28Sakpm@osdl.org 
27845f04e82SFrank Schaefer 	ret = em28xx_i2c_recv_bytes(dev, addr, &buf, 1);
27945f04e82SFrank Schaefer 	if (ret == 1)
280a6c2ba28Sakpm@osdl.org 		return 0;
28145f04e82SFrank Schaefer 	return (ret < 0) ? ret : -EIO;
282a6c2ba28Sakpm@osdl.org }
283a6c2ba28Sakpm@osdl.org 
284a6c2ba28Sakpm@osdl.org /*
2853acf2809SMauro Carvalho Chehab  * em28xx_i2c_xfer()
286a6c2ba28Sakpm@osdl.org  * the main i2c transfer function
287a6c2ba28Sakpm@osdl.org  */
2883acf2809SMauro Carvalho Chehab static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap,
289a6c2ba28Sakpm@osdl.org 			   struct i2c_msg msgs[], int num)
290a6c2ba28Sakpm@osdl.org {
2913acf2809SMauro Carvalho Chehab 	struct em28xx *dev = i2c_adap->algo_data;
292a6c2ba28Sakpm@osdl.org 	int addr, rc, i, byte;
293a6c2ba28Sakpm@osdl.org 
294a6c2ba28Sakpm@osdl.org 	if (num <= 0)
295a6c2ba28Sakpm@osdl.org 		return 0;
296a6c2ba28Sakpm@osdl.org 	for (i = 0; i < num; i++) {
297a6c2ba28Sakpm@osdl.org 		addr = msgs[i].addr << 1;
298d5e52653SMauro Carvalho Chehab 		dprintk2(2, "%s %s addr=%x len=%d:",
299a6c2ba28Sakpm@osdl.org 			 (msgs[i].flags & I2C_M_RD) ? "read" : "write",
300a6c2ba28Sakpm@osdl.org 			 i == num - 1 ? "stop" : "nonstop", addr, msgs[i].len);
301a6c2ba28Sakpm@osdl.org 		if (!msgs[i].len) { /* no len: check only for device presence */
302505b6d0bSMauro Carvalho Chehab 			if (dev->board.is_em2800)
303596d92d5SMauro Carvalho Chehab 				rc = em2800_i2c_check_for_device(dev, addr);
304596d92d5SMauro Carvalho Chehab 			else
3053acf2809SMauro Carvalho Chehab 				rc = em28xx_i2c_check_for_device(dev, addr);
30645f04e82SFrank Schaefer 			if (rc == -ENODEV) {
30745f04e82SFrank Schaefer 				if (i2c_debug >= 2)
30845f04e82SFrank Schaefer 					printk(" no device\n");
309a6c2ba28Sakpm@osdl.org 				return rc;
310a6c2ba28Sakpm@osdl.org 			}
311596d92d5SMauro Carvalho Chehab 		} else if (msgs[i].flags & I2C_M_RD) {
312a6c2ba28Sakpm@osdl.org 			/* read bytes */
313505b6d0bSMauro Carvalho Chehab 			if (dev->board.is_em2800)
314596d92d5SMauro Carvalho Chehab 				rc = em2800_i2c_recv_bytes(dev, addr,
315596d92d5SMauro Carvalho Chehab 							   msgs[i].buf,
316596d92d5SMauro Carvalho Chehab 							   msgs[i].len);
317596d92d5SMauro Carvalho Chehab 			else
3183acf2809SMauro Carvalho Chehab 				rc = em28xx_i2c_recv_bytes(dev, addr,
319596d92d5SMauro Carvalho Chehab 							   msgs[i].buf,
320a6c2ba28Sakpm@osdl.org 							   msgs[i].len);
321d5e52653SMauro Carvalho Chehab 			if (i2c_debug >= 2) {
3226ea54d93SDouglas Schilling Landgraf 				for (byte = 0; byte < msgs[i].len; byte++)
323a6c2ba28Sakpm@osdl.org 					printk(" %02x", msgs[i].buf[byte]);
324a6c2ba28Sakpm@osdl.org 			}
325a6c2ba28Sakpm@osdl.org 		} else {
326a6c2ba28Sakpm@osdl.org 			/* write bytes */
327d5e52653SMauro Carvalho Chehab 			if (i2c_debug >= 2) {
328a6c2ba28Sakpm@osdl.org 				for (byte = 0; byte < msgs[i].len; byte++)
329a6c2ba28Sakpm@osdl.org 					printk(" %02x", msgs[i].buf[byte]);
330a6c2ba28Sakpm@osdl.org 			}
331505b6d0bSMauro Carvalho Chehab 			if (dev->board.is_em2800)
332596d92d5SMauro Carvalho Chehab 				rc = em2800_i2c_send_bytes(dev, addr,
333596d92d5SMauro Carvalho Chehab 							   msgs[i].buf,
334596d92d5SMauro Carvalho Chehab 							   msgs[i].len);
335596d92d5SMauro Carvalho Chehab 			else
3363acf2809SMauro Carvalho Chehab 				rc = em28xx_i2c_send_bytes(dev, addr,
337596d92d5SMauro Carvalho Chehab 							   msgs[i].buf,
338596d92d5SMauro Carvalho Chehab 							   msgs[i].len,
339a6c2ba28Sakpm@osdl.org 							   i == num - 1);
340e8e41da4SMarkus Rechberger 		}
34145f04e82SFrank Schaefer 		if (rc < 0) {
34245f04e82SFrank Schaefer 			if (i2c_debug >= 2)
34345f04e82SFrank Schaefer 				printk(" ERROR: %i\n", rc);
34445f04e82SFrank Schaefer 			return rc;
34545f04e82SFrank Schaefer 		}
346d5e52653SMauro Carvalho Chehab 		if (i2c_debug >= 2)
347a6c2ba28Sakpm@osdl.org 			printk("\n");
348a6c2ba28Sakpm@osdl.org 	}
349a6c2ba28Sakpm@osdl.org 
350a6c2ba28Sakpm@osdl.org 	return num;
351a6c2ba28Sakpm@osdl.org }
352a6c2ba28Sakpm@osdl.org 
35303910cc3SMauro Carvalho Chehab /* based on linux/sunrpc/svcauth.h and linux/hash.h
35403910cc3SMauro Carvalho Chehab  * The original hash function returns a different value, if arch is x86_64
35503910cc3SMauro Carvalho Chehab  *  or i386.
35603910cc3SMauro Carvalho Chehab  */
35703910cc3SMauro Carvalho Chehab static inline unsigned long em28xx_hash_mem(char *buf, int length, int bits)
35803910cc3SMauro Carvalho Chehab {
35903910cc3SMauro Carvalho Chehab 	unsigned long hash = 0;
36003910cc3SMauro Carvalho Chehab 	unsigned long l = 0;
36103910cc3SMauro Carvalho Chehab 	int len = 0;
36203910cc3SMauro Carvalho Chehab 	unsigned char c;
36303910cc3SMauro Carvalho Chehab 	do {
36403910cc3SMauro Carvalho Chehab 		if (len == length) {
36503910cc3SMauro Carvalho Chehab 			c = (char)len;
36603910cc3SMauro Carvalho Chehab 			len = -1;
36703910cc3SMauro Carvalho Chehab 		} else
36803910cc3SMauro Carvalho Chehab 			c = *buf++;
36903910cc3SMauro Carvalho Chehab 		l = (l << 8) | c;
37003910cc3SMauro Carvalho Chehab 		len++;
37103910cc3SMauro Carvalho Chehab 		if ((len & (32 / 8 - 1)) == 0)
37203910cc3SMauro Carvalho Chehab 			hash = ((hash^l) * 0x9e370001UL);
37303910cc3SMauro Carvalho Chehab 	} while (len);
37403910cc3SMauro Carvalho Chehab 
37503910cc3SMauro Carvalho Chehab 	return (hash >> (32 - bits)) & 0xffffffffUL;
37603910cc3SMauro Carvalho Chehab }
37703910cc3SMauro Carvalho Chehab 
3783acf2809SMauro Carvalho Chehab static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len)
379a6c2ba28Sakpm@osdl.org {
380a6c2ba28Sakpm@osdl.org 	unsigned char buf, *p = eedata;
3813acf2809SMauro Carvalho Chehab 	struct em28xx_eeprom *em_eeprom = (void *)eedata;
38290271964SFrank Schaefer 	int i, err, size = len, block, block_max;
383a6c2ba28Sakpm@osdl.org 
384fec528b7SMauro Carvalho Chehab 	if (dev->chip_id == CHIP_ID_EM2874 ||
385fec528b7SMauro Carvalho Chehab 	    dev->chip_id == CHIP_ID_EM28174 ||
386fec528b7SMauro Carvalho Chehab 	    dev->chip_id == CHIP_ID_EM2884) {
387a527c9f8SDevin Heitmueller 		/* Empia switched to a 16-bit addressable eeprom in newer
388a527c9f8SDevin Heitmueller 		   devices.  While we could certainly write a routine to read
389a527c9f8SDevin Heitmueller 		   the eeprom, there is nothing of use in there that cannot be
390a527c9f8SDevin Heitmueller 		   accessed through registers, and there is the risk that we
391a527c9f8SDevin Heitmueller 		   could corrupt the eeprom (since a 16-bit read call is
392a527c9f8SDevin Heitmueller 		   interpreted as a write call by 8-bit eeproms).
393a527c9f8SDevin Heitmueller 		*/
394a527c9f8SDevin Heitmueller 		return 0;
395a527c9f8SDevin Heitmueller 	}
396a527c9f8SDevin Heitmueller 
397a6c2ba28Sakpm@osdl.org 	dev->i2c_client.addr = 0xa0 >> 1;
398596d92d5SMauro Carvalho Chehab 
399596d92d5SMauro Carvalho Chehab 	/* Check if board has eeprom */
400596d92d5SMauro Carvalho Chehab 	err = i2c_master_recv(&dev->i2c_client, &buf, 0);
401f2a01a00SDouglas Schilling Landgraf 	if (err < 0) {
402c41109fcSMauro Carvalho Chehab 		em28xx_errdev("board has no eeprom\n");
403c41109fcSMauro Carvalho Chehab 		memset(eedata, 0, len);
404c41109fcSMauro Carvalho Chehab 		return -ENODEV;
405f2a01a00SDouglas Schilling Landgraf 	}
406596d92d5SMauro Carvalho Chehab 
407a6c2ba28Sakpm@osdl.org 	buf = 0;
4086ea54d93SDouglas Schilling Landgraf 
4096ea54d93SDouglas Schilling Landgraf 	err = i2c_master_send(&dev->i2c_client, &buf, 1);
4106ea54d93SDouglas Schilling Landgraf 	if (err != 1) {
411a6c2ba28Sakpm@osdl.org 		printk(KERN_INFO "%s: Huh, no eeprom present (err=%d)?\n",
412a6c2ba28Sakpm@osdl.org 		       dev->name, err);
413f2a01a00SDouglas Schilling Landgraf 		return err;
414a6c2ba28Sakpm@osdl.org 	}
41590271964SFrank Schaefer 
41690271964SFrank Schaefer 	if (dev->board.is_em2800)
41790271964SFrank Schaefer 		block_max = 4;
41890271964SFrank Schaefer 	else
41990271964SFrank Schaefer 		block_max = 64;
42090271964SFrank Schaefer 
421a6c2ba28Sakpm@osdl.org 	while (size > 0) {
42290271964SFrank Schaefer 		if (size > block_max)
42390271964SFrank Schaefer 			block = block_max;
424a6c2ba28Sakpm@osdl.org 		else
425a6c2ba28Sakpm@osdl.org 			block = size;
426a6c2ba28Sakpm@osdl.org 
427a6c2ba28Sakpm@osdl.org 		if (block !=
428a6c2ba28Sakpm@osdl.org 		    (err = i2c_master_recv(&dev->i2c_client, p, block))) {
429a6c2ba28Sakpm@osdl.org 			printk(KERN_WARNING
430a6c2ba28Sakpm@osdl.org 			       "%s: i2c eeprom read error (err=%d)\n",
431a6c2ba28Sakpm@osdl.org 			       dev->name, err);
432f2a01a00SDouglas Schilling Landgraf 			return err;
433a6c2ba28Sakpm@osdl.org 		}
434a6c2ba28Sakpm@osdl.org 		size -= block;
435a6c2ba28Sakpm@osdl.org 		p += block;
436a6c2ba28Sakpm@osdl.org 	}
437a6c2ba28Sakpm@osdl.org 	for (i = 0; i < len; i++) {
438a6c2ba28Sakpm@osdl.org 		if (0 == (i % 16))
439a6c2ba28Sakpm@osdl.org 			printk(KERN_INFO "%s: i2c eeprom %02x:", dev->name, i);
440a6c2ba28Sakpm@osdl.org 		printk(" %02x", eedata[i]);
441a6c2ba28Sakpm@osdl.org 		if (15 == (i % 16))
442a6c2ba28Sakpm@osdl.org 			printk("\n");
443a6c2ba28Sakpm@osdl.org 	}
444a6c2ba28Sakpm@osdl.org 
44503910cc3SMauro Carvalho Chehab 	if (em_eeprom->id == 0x9567eb1a)
44603910cc3SMauro Carvalho Chehab 		dev->hash = em28xx_hash_mem(eedata, len, 32);
44703910cc3SMauro Carvalho Chehab 
4481bee0184SMauro Carvalho Chehab 	printk(KERN_INFO "%s: EEPROM ID= 0x%08x, EEPROM hash = 0x%08lx\n",
4491bee0184SMauro Carvalho Chehab 	       dev->name, em_eeprom->id, dev->hash);
4501bee0184SMauro Carvalho Chehab 
4511bee0184SMauro Carvalho Chehab 	printk(KERN_INFO "%s: EEPROM info:\n", dev->name);
452a6c2ba28Sakpm@osdl.org 
453a6c2ba28Sakpm@osdl.org 	switch (em_eeprom->chip_conf >> 4 & 0x3) {
454a6c2ba28Sakpm@osdl.org 	case 0:
4551bee0184SMauro Carvalho Chehab 		printk(KERN_INFO "%s:\tNo audio on board.\n", dev->name);
456a6c2ba28Sakpm@osdl.org 		break;
457a6c2ba28Sakpm@osdl.org 	case 1:
4581bee0184SMauro Carvalho Chehab 		printk(KERN_INFO "%s:\tAC97 audio (5 sample rates)\n",
4591bee0184SMauro Carvalho Chehab 				 dev->name);
460a6c2ba28Sakpm@osdl.org 		break;
461a6c2ba28Sakpm@osdl.org 	case 2:
462a1a6ee74SNicola Soranzo 		printk(KERN_INFO "%s:\tI2S audio, sample rate=32k\n",
463a1a6ee74SNicola Soranzo 				 dev->name);
464a6c2ba28Sakpm@osdl.org 		break;
465a6c2ba28Sakpm@osdl.org 	case 3:
466a1a6ee74SNicola Soranzo 		printk(KERN_INFO "%s:\tI2S audio, 3 sample rates\n",
467a1a6ee74SNicola Soranzo 				 dev->name);
468a6c2ba28Sakpm@osdl.org 		break;
469a6c2ba28Sakpm@osdl.org 	}
470a6c2ba28Sakpm@osdl.org 
471a6c2ba28Sakpm@osdl.org 	if (em_eeprom->chip_conf & 1 << 3)
4721bee0184SMauro Carvalho Chehab 		printk(KERN_INFO "%s:\tUSB Remote wakeup capable\n", dev->name);
473a6c2ba28Sakpm@osdl.org 
474a6c2ba28Sakpm@osdl.org 	if (em_eeprom->chip_conf & 1 << 2)
4751bee0184SMauro Carvalho Chehab 		printk(KERN_INFO "%s:\tUSB Self power capable\n", dev->name);
476a6c2ba28Sakpm@osdl.org 
477a6c2ba28Sakpm@osdl.org 	switch (em_eeprom->chip_conf & 0x3) {
478a6c2ba28Sakpm@osdl.org 	case 0:
4791bee0184SMauro Carvalho Chehab 		printk(KERN_INFO "%s:\t500mA max power\n", dev->name);
480a6c2ba28Sakpm@osdl.org 		break;
481a6c2ba28Sakpm@osdl.org 	case 1:
4821bee0184SMauro Carvalho Chehab 		printk(KERN_INFO "%s:\t400mA max power\n", dev->name);
483a6c2ba28Sakpm@osdl.org 		break;
484a6c2ba28Sakpm@osdl.org 	case 2:
4851bee0184SMauro Carvalho Chehab 		printk(KERN_INFO "%s:\t300mA max power\n", dev->name);
486a6c2ba28Sakpm@osdl.org 		break;
487a6c2ba28Sakpm@osdl.org 	case 3:
4881bee0184SMauro Carvalho Chehab 		printk(KERN_INFO "%s:\t200mA max power\n", dev->name);
489a6c2ba28Sakpm@osdl.org 		break;
490a6c2ba28Sakpm@osdl.org 	}
4911bee0184SMauro Carvalho Chehab 	printk(KERN_INFO "%s:\tTable at 0x%02x, strings=0x%04x, 0x%04x, 0x%04x\n",
4921bee0184SMauro Carvalho Chehab 				dev->name,
4936ea54d93SDouglas Schilling Landgraf 				em_eeprom->string_idx_table,
4946ea54d93SDouglas Schilling Landgraf 				em_eeprom->string1,
4956ea54d93SDouglas Schilling Landgraf 				em_eeprom->string2,
4966ea54d93SDouglas Schilling Landgraf 				em_eeprom->string3);
497a6c2ba28Sakpm@osdl.org 
498a6c2ba28Sakpm@osdl.org 	return 0;
499a6c2ba28Sakpm@osdl.org }
500a6c2ba28Sakpm@osdl.org 
501a6c2ba28Sakpm@osdl.org /* ----------------------------------------------------------- */
502a6c2ba28Sakpm@osdl.org 
503a6c2ba28Sakpm@osdl.org /*
504a6c2ba28Sakpm@osdl.org  * functionality()
505a6c2ba28Sakpm@osdl.org  */
506a6c2ba28Sakpm@osdl.org static u32 functionality(struct i2c_adapter *adap)
507a6c2ba28Sakpm@osdl.org {
508eaf33c40SFrank Schaefer 	struct em28xx *dev = adap->algo_data;
509eaf33c40SFrank Schaefer 	u32 func_flags = I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
510eaf33c40SFrank Schaefer 	if (dev->board.is_em2800)
511eaf33c40SFrank Schaefer 		func_flags &= ~I2C_FUNC_SMBUS_WRITE_BLOCK_DATA;
512eaf33c40SFrank Schaefer 	return func_flags;
513a6c2ba28Sakpm@osdl.org }
514a6c2ba28Sakpm@osdl.org 
5153acf2809SMauro Carvalho Chehab static struct i2c_algorithm em28xx_algo = {
5163acf2809SMauro Carvalho Chehab 	.master_xfer   = em28xx_i2c_xfer,
517a6c2ba28Sakpm@osdl.org 	.functionality = functionality,
518a6c2ba28Sakpm@osdl.org };
519a6c2ba28Sakpm@osdl.org 
5203acf2809SMauro Carvalho Chehab static struct i2c_adapter em28xx_adap_template = {
521a6c2ba28Sakpm@osdl.org 	.owner = THIS_MODULE,
5223acf2809SMauro Carvalho Chehab 	.name = "em28xx",
5233acf2809SMauro Carvalho Chehab 	.algo = &em28xx_algo,
524a6c2ba28Sakpm@osdl.org };
525a6c2ba28Sakpm@osdl.org 
5263acf2809SMauro Carvalho Chehab static struct i2c_client em28xx_client_template = {
5273acf2809SMauro Carvalho Chehab 	.name = "em28xx internal",
528a6c2ba28Sakpm@osdl.org };
529a6c2ba28Sakpm@osdl.org 
530a6c2ba28Sakpm@osdl.org /* ----------------------------------------------------------- */
531a6c2ba28Sakpm@osdl.org 
532a6c2ba28Sakpm@osdl.org /*
533a6c2ba28Sakpm@osdl.org  * i2c_devs
534a6c2ba28Sakpm@osdl.org  * incomplete list of known devices
535a6c2ba28Sakpm@osdl.org  */
536a6c2ba28Sakpm@osdl.org static char *i2c_devs[128] = {
537*0b3966e4SFrank Schaefer 	[0x3e >> 1] = "remote IR sensor",
538a6c2ba28Sakpm@osdl.org 	[0x4a >> 1] = "saa7113h",
539729841edSMartin Blumenstingl 	[0x52 >> 1] = "drxk",
540a6c2ba28Sakpm@osdl.org 	[0x60 >> 1] = "remote IR sensor",
541da45a2a5SMarkus Rechberger 	[0x8e >> 1] = "remote IR sensor",
542a6c2ba28Sakpm@osdl.org 	[0x86 >> 1] = "tda9887",
543a6c2ba28Sakpm@osdl.org 	[0x80 >> 1] = "msp34xx",
544a6c2ba28Sakpm@osdl.org 	[0x88 >> 1] = "msp34xx",
545a6c2ba28Sakpm@osdl.org 	[0xa0 >> 1] = "eeprom",
5462bd1d9ebSVitaly Wool 	[0xb0 >> 1] = "tda9874",
547a6c2ba28Sakpm@osdl.org 	[0xb8 >> 1] = "tvp5150a",
548791a08fcSMauro Carvalho Chehab 	[0xba >> 1] = "webcam sensor or tvp5150a",
549a6c2ba28Sakpm@osdl.org 	[0xc0 >> 1] = "tuner (analog)",
550a6c2ba28Sakpm@osdl.org 	[0xc2 >> 1] = "tuner (analog)",
551a6c2ba28Sakpm@osdl.org 	[0xc4 >> 1] = "tuner (analog)",
552a6c2ba28Sakpm@osdl.org 	[0xc6 >> 1] = "tuner (analog)",
553a6c2ba28Sakpm@osdl.org };
554a6c2ba28Sakpm@osdl.org 
555a6c2ba28Sakpm@osdl.org /*
556a6c2ba28Sakpm@osdl.org  * do_i2c_scan()
557a6c2ba28Sakpm@osdl.org  * check i2c address range for devices
558a6c2ba28Sakpm@osdl.org  */
559fad7b958SSascha Sommer void em28xx_do_i2c_scan(struct em28xx *dev)
560a6c2ba28Sakpm@osdl.org {
561fad7b958SSascha Sommer 	u8 i2c_devicelist[128];
562a6c2ba28Sakpm@osdl.org 	unsigned char buf;
563a6c2ba28Sakpm@osdl.org 	int i, rc;
564a6c2ba28Sakpm@osdl.org 
565fad7b958SSascha Sommer 	memset(i2c_devicelist, 0, ARRAY_SIZE(i2c_devicelist));
566fad7b958SSascha Sommer 
56753c4e955SMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(i2c_devs); i++) {
568fad7b958SSascha Sommer 		dev->i2c_client.addr = i;
569fad7b958SSascha Sommer 		rc = i2c_master_recv(&dev->i2c_client, &buf, 0);
570a6c2ba28Sakpm@osdl.org 		if (rc < 0)
571a6c2ba28Sakpm@osdl.org 			continue;
572fad7b958SSascha Sommer 		i2c_devicelist[i] = i;
573fad7b958SSascha Sommer 		printk(KERN_INFO "%s: found i2c device @ 0x%x [%s]\n",
574fad7b958SSascha Sommer 		       dev->name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???");
575a6c2ba28Sakpm@osdl.org 	}
576fad7b958SSascha Sommer 
577fad7b958SSascha Sommer 	dev->i2c_hash = em28xx_hash_mem(i2c_devicelist,
578fad7b958SSascha Sommer 					ARRAY_SIZE(i2c_devicelist), 32);
579a6c2ba28Sakpm@osdl.org }
580a6c2ba28Sakpm@osdl.org 
581a6c2ba28Sakpm@osdl.org /*
5823acf2809SMauro Carvalho Chehab  * em28xx_i2c_register()
583a6c2ba28Sakpm@osdl.org  * register i2c bus
584a6c2ba28Sakpm@osdl.org  */
5853acf2809SMauro Carvalho Chehab int em28xx_i2c_register(struct em28xx *dev)
586a6c2ba28Sakpm@osdl.org {
587f2a01a00SDouglas Schilling Landgraf 	int retval;
588f2a01a00SDouglas Schilling Landgraf 
5893acf2809SMauro Carvalho Chehab 	BUG_ON(!dev->em28xx_write_regs || !dev->em28xx_read_reg);
5903acf2809SMauro Carvalho Chehab 	BUG_ON(!dev->em28xx_write_regs_req || !dev->em28xx_read_reg_req);
5913acf2809SMauro Carvalho Chehab 	dev->i2c_adap = em28xx_adap_template;
592a6c2ba28Sakpm@osdl.org 	dev->i2c_adap.dev.parent = &dev->udev->dev;
593a6c2ba28Sakpm@osdl.org 	strcpy(dev->i2c_adap.name, dev->name);
594a6c2ba28Sakpm@osdl.org 	dev->i2c_adap.algo_data = dev;
595f2cf250aSDouglas Schilling Landgraf 	i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev);
596f2a01a00SDouglas Schilling Landgraf 
597f2a01a00SDouglas Schilling Landgraf 	retval = i2c_add_adapter(&dev->i2c_adap);
598f2a01a00SDouglas Schilling Landgraf 	if (retval < 0) {
599f2a01a00SDouglas Schilling Landgraf 		em28xx_errdev("%s: i2c_add_adapter failed! retval [%d]\n",
600f2a01a00SDouglas Schilling Landgraf 			__func__, retval);
601f2a01a00SDouglas Schilling Landgraf 		return retval;
602f2a01a00SDouglas Schilling Landgraf 	}
603a6c2ba28Sakpm@osdl.org 
6043acf2809SMauro Carvalho Chehab 	dev->i2c_client = em28xx_client_template;
605a6c2ba28Sakpm@osdl.org 	dev->i2c_client.adapter = &dev->i2c_adap;
606a6c2ba28Sakpm@osdl.org 
607f2a01a00SDouglas Schilling Landgraf 	retval = em28xx_i2c_eeprom(dev, dev->eedata, sizeof(dev->eedata));
608c41109fcSMauro Carvalho Chehab 	if ((retval < 0) && (retval != -ENODEV)) {
609f2a01a00SDouglas Schilling Landgraf 		em28xx_errdev("%s: em28xx_i2_eeprom failed! retval [%d]\n",
610f2a01a00SDouglas Schilling Landgraf 			__func__, retval);
611c41109fcSMauro Carvalho Chehab 
612f2a01a00SDouglas Schilling Landgraf 		return retval;
613f2a01a00SDouglas Schilling Landgraf 	}
614a6c2ba28Sakpm@osdl.org 
615a6c2ba28Sakpm@osdl.org 	if (i2c_scan)
616fad7b958SSascha Sommer 		em28xx_do_i2c_scan(dev);
617c41109fcSMauro Carvalho Chehab 
618a6c2ba28Sakpm@osdl.org 	return 0;
619a6c2ba28Sakpm@osdl.org }
620a6c2ba28Sakpm@osdl.org 
621a6c2ba28Sakpm@osdl.org /*
6223acf2809SMauro Carvalho Chehab  * em28xx_i2c_unregister()
623a6c2ba28Sakpm@osdl.org  * unregister i2c_bus
624a6c2ba28Sakpm@osdl.org  */
6253acf2809SMauro Carvalho Chehab int em28xx_i2c_unregister(struct em28xx *dev)
626a6c2ba28Sakpm@osdl.org {
627a6c2ba28Sakpm@osdl.org 	i2c_del_adapter(&dev->i2c_adap);
628a6c2ba28Sakpm@osdl.org 	return 0;
629a6c2ba28Sakpm@osdl.org }
630