diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
commit | fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch) | |
tree | a57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/staging/tm6000/tm6000-i2c.c | |
parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) |
Diffstat (limited to 'drivers/staging/tm6000/tm6000-i2c.c')
-rw-r--r-- | drivers/staging/tm6000/tm6000-i2c.c | 345 |
1 files changed, 345 insertions, 0 deletions
diff --git a/drivers/staging/tm6000/tm6000-i2c.c b/drivers/staging/tm6000/tm6000-i2c.c new file mode 100644 index 00000000000..5a651ea5f60 --- /dev/null +++ b/drivers/staging/tm6000/tm6000-i2c.c | |||
@@ -0,0 +1,345 @@ | |||
1 | /* | ||
2 | * tm6000-i2c.c - driver for TM5600/TM6000/TM6010 USB video capture devices | ||
3 | * | ||
4 | * Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org> | ||
5 | * | ||
6 | * Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com> | ||
7 | * - Fix SMBus Read Byte command | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License as published by | ||
11 | * the Free Software Foundation version 2 | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
21 | */ | ||
22 | |||
23 | #include <linux/module.h> | ||
24 | #include <linux/kernel.h> | ||
25 | #include <linux/usb.h> | ||
26 | #include <linux/i2c.h> | ||
27 | |||
28 | #include "tm6000.h" | ||
29 | #include "tm6000-regs.h" | ||
30 | #include <media/v4l2-common.h> | ||
31 | #include <media/tuner.h> | ||
32 | #include "tuner-xc2028.h" | ||
33 | |||
34 | |||
35 | /* ----------------------------------------------------------- */ | ||
36 | |||
37 | static unsigned int i2c_debug; | ||
38 | module_param(i2c_debug, int, 0644); | ||
39 | MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]"); | ||
40 | |||
41 | #define i2c_dprintk(lvl, fmt, args...) if (i2c_debug >= lvl) do { \ | ||
42 | printk(KERN_DEBUG "%s at %s: " fmt, \ | ||
43 | dev->name, __func__, ##args); } while (0) | ||
44 | |||
45 | static int tm6000_i2c_send_regs(struct tm6000_core *dev, unsigned char addr, | ||
46 | __u8 reg, char *buf, int len) | ||
47 | { | ||
48 | int rc; | ||
49 | unsigned int tsleep; | ||
50 | unsigned int i2c_packet_limit = 16; | ||
51 | |||
52 | if (dev->dev_type == TM6010) | ||
53 | i2c_packet_limit = 64; | ||
54 | |||
55 | if (!buf) | ||
56 | return -1; | ||
57 | |||
58 | if (len < 1 || len > i2c_packet_limit) { | ||
59 | printk(KERN_ERR "Incorrect length of i2c packet = %d, limit set to %d\n", | ||
60 | len, i2c_packet_limit); | ||
61 | return -1; | ||
62 | } | ||
63 | |||
64 | /* capture mutex */ | ||
65 | rc = tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR | | ||
66 | USB_RECIP_DEVICE, REQ_16_SET_GET_I2C_WR1_RDN, | ||
67 | addr | reg << 8, 0, buf, len); | ||
68 | |||
69 | if (rc < 0) { | ||
70 | /* release mutex */ | ||
71 | return rc; | ||
72 | } | ||
73 | |||
74 | /* Calculate delay time, 14000us for 64 bytes */ | ||
75 | tsleep = ((len * 200) + 200 + 1000) / 1000; | ||
76 | msleep(tsleep); | ||
77 | |||
78 | /* release mutex */ | ||
79 | return rc; | ||
80 | } | ||
81 | |||
82 | /* Generic read - doesn't work fine with 16bit registers */ | ||
83 | static int tm6000_i2c_recv_regs(struct tm6000_core *dev, unsigned char addr, | ||
84 | __u8 reg, char *buf, int len) | ||
85 | { | ||
86 | int rc; | ||
87 | u8 b[2]; | ||
88 | unsigned int i2c_packet_limit = 16; | ||
89 | |||
90 | if (dev->dev_type == TM6010) | ||
91 | i2c_packet_limit = 64; | ||
92 | |||
93 | if (!buf) | ||
94 | return -1; | ||
95 | |||
96 | if (len < 1 || len > i2c_packet_limit) { | ||
97 | printk(KERN_ERR "Incorrect length of i2c packet = %d, limit set to %d\n", | ||
98 | len, i2c_packet_limit); | ||
99 | return -1; | ||
100 | } | ||
101 | |||
102 | /* capture mutex */ | ||
103 | if ((dev->caps.has_zl10353) && (dev->demod_addr << 1 == addr) && (reg % 2 == 0)) { | ||
104 | /* | ||
105 | * Workaround an I2C bug when reading from zl10353 | ||
106 | */ | ||
107 | reg -= 1; | ||
108 | len += 1; | ||
109 | |||
110 | rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | ||
111 | REQ_16_SET_GET_I2C_WR1_RDN, addr | reg << 8, 0, b, len); | ||
112 | |||
113 | *buf = b[1]; | ||
114 | } else { | ||
115 | rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | ||
116 | REQ_16_SET_GET_I2C_WR1_RDN, addr | reg << 8, 0, buf, len); | ||
117 | } | ||
118 | |||
119 | /* release mutex */ | ||
120 | return rc; | ||
121 | } | ||
122 | |||
123 | /* | ||
124 | * read from a 16bit register | ||
125 | * for example xc2028, xc3028 or xc3028L | ||
126 | */ | ||
127 | static int tm6000_i2c_recv_regs16(struct tm6000_core *dev, unsigned char addr, | ||
128 | __u16 reg, char *buf, int len) | ||
129 | { | ||
130 | int rc; | ||
131 | unsigned char ureg; | ||
132 | |||
133 | if (!buf || len != 2) | ||
134 | return -1; | ||
135 | |||
136 | /* capture mutex */ | ||
137 | if (dev->dev_type == TM6010) { | ||
138 | ureg = reg & 0xFF; | ||
139 | rc = tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR | | ||
140 | USB_RECIP_DEVICE, REQ_16_SET_GET_I2C_WR1_RDN, | ||
141 | addr | (reg & 0xFF00), 0, &ureg, 1); | ||
142 | |||
143 | if (rc < 0) { | ||
144 | /* release mutex */ | ||
145 | return rc; | ||
146 | } | ||
147 | |||
148 | msleep(1400 / 1000); | ||
149 | rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR | | ||
150 | USB_RECIP_DEVICE, REQ_35_AFTEK_TUNER_READ, | ||
151 | reg, 0, buf, len); | ||
152 | } else { | ||
153 | rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR | | ||
154 | USB_RECIP_DEVICE, REQ_14_SET_GET_I2C_WR2_RDN, | ||
155 | addr, reg, buf, len); | ||
156 | } | ||
157 | |||
158 | /* release mutex */ | ||
159 | return rc; | ||
160 | } | ||
161 | |||
162 | static int tm6000_i2c_xfer(struct i2c_adapter *i2c_adap, | ||
163 | struct i2c_msg msgs[], int num) | ||
164 | { | ||
165 | struct tm6000_core *dev = i2c_adap->algo_data; | ||
166 | int addr, rc, i, byte; | ||
167 | |||
168 | if (num <= 0) | ||
169 | return 0; | ||
170 | for (i = 0; i < num; i++) { | ||
171 | addr = (msgs[i].addr << 1) & 0xff; | ||
172 | i2c_dprintk(2, "%s %s addr=0x%x len=%d:", | ||
173 | (msgs[i].flags & I2C_M_RD) ? "read" : "write", | ||
174 | i == num - 1 ? "stop" : "nonstop", addr, msgs[i].len); | ||
175 | if (msgs[i].flags & I2C_M_RD) { | ||
176 | /* read request without preceding register selection */ | ||
177 | /* | ||
178 | * The TM6000 only supports a read transaction | ||
179 | * immediately after a 1 or 2 byte write to select | ||
180 | * a register. We cannot fulfil this request. | ||
181 | */ | ||
182 | i2c_dprintk(2, " read without preceding write not" | ||
183 | " supported"); | ||
184 | rc = -EOPNOTSUPP; | ||
185 | goto err; | ||
186 | } else if (i + 1 < num && msgs[i].len <= 2 && | ||
187 | (msgs[i + 1].flags & I2C_M_RD) && | ||
188 | msgs[i].addr == msgs[i + 1].addr) { | ||
189 | /* 1 or 2 byte write followed by a read */ | ||
190 | if (i2c_debug >= 2) | ||
191 | for (byte = 0; byte < msgs[i].len; byte++) | ||
192 | printk(" %02x", msgs[i].buf[byte]); | ||
193 | i2c_dprintk(2, "; joined to read %s len=%d:", | ||
194 | i == num - 2 ? "stop" : "nonstop", | ||
195 | msgs[i + 1].len); | ||
196 | |||
197 | if (msgs[i].len == 2) { | ||
198 | rc = tm6000_i2c_recv_regs16(dev, addr, | ||
199 | msgs[i].buf[0] << 8 | msgs[i].buf[1], | ||
200 | msgs[i + 1].buf, msgs[i + 1].len); | ||
201 | } else { | ||
202 | rc = tm6000_i2c_recv_regs(dev, addr, msgs[i].buf[0], | ||
203 | msgs[i + 1].buf, msgs[i + 1].len); | ||
204 | } | ||
205 | |||
206 | i++; | ||
207 | |||
208 | if (addr == dev->tuner_addr << 1) { | ||
209 | tm6000_set_reg(dev, REQ_50_SET_START, 0, 0); | ||
210 | tm6000_set_reg(dev, REQ_51_SET_STOP, 0, 0); | ||
211 | } | ||
212 | if (i2c_debug >= 2) | ||
213 | for (byte = 0; byte < msgs[i].len; byte++) | ||
214 | printk(" %02x", msgs[i].buf[byte]); | ||
215 | } else { | ||
216 | /* write bytes */ | ||
217 | if (i2c_debug >= 2) | ||
218 | for (byte = 0; byte < msgs[i].len; byte++) | ||
219 | printk(" %02x", msgs[i].buf[byte]); | ||
220 | rc = tm6000_i2c_send_regs(dev, addr, msgs[i].buf[0], | ||
221 | msgs[i].buf + 1, msgs[i].len - 1); | ||
222 | |||
223 | if (addr == dev->tuner_addr << 1) { | ||
224 | tm6000_set_reg(dev, REQ_50_SET_START, 0, 0); | ||
225 | tm6000_set_reg(dev, REQ_51_SET_STOP, 0, 0); | ||
226 | } | ||
227 | } | ||
228 | if (i2c_debug >= 2) | ||
229 | printk("\n"); | ||
230 | if (rc < 0) | ||
231 | goto err; | ||
232 | } | ||
233 | |||
234 | return num; | ||
235 | err: | ||
236 | i2c_dprintk(2, " ERROR: %i\n", rc); | ||
237 | return rc; | ||
238 | } | ||
239 | |||
240 | static int tm6000_i2c_eeprom(struct tm6000_core *dev) | ||
241 | { | ||
242 | int i, rc; | ||
243 | unsigned char *p = dev->eedata; | ||
244 | unsigned char bytes[17]; | ||
245 | |||
246 | dev->i2c_client.addr = 0xa0 >> 1; | ||
247 | dev->eedata_size = 0; | ||
248 | |||
249 | bytes[16] = '\0'; | ||
250 | for (i = 0; i < sizeof(dev->eedata); ) { | ||
251 | *p = i; | ||
252 | rc = tm6000_i2c_recv_regs(dev, 0xa0, i, p, 1); | ||
253 | if (rc < 1) { | ||
254 | if (p == dev->eedata) | ||
255 | goto noeeprom; | ||
256 | else { | ||
257 | printk(KERN_WARNING | ||
258 | "%s: i2c eeprom read error (err=%d)\n", | ||
259 | dev->name, rc); | ||
260 | } | ||
261 | return -EINVAL; | ||
262 | } | ||
263 | dev->eedata_size++; | ||
264 | p++; | ||
265 | if (0 == (i % 16)) | ||
266 | printk(KERN_INFO "%s: i2c eeprom %02x:", dev->name, i); | ||
267 | printk(" %02x", dev->eedata[i]); | ||
268 | if ((dev->eedata[i] >= ' ') && (dev->eedata[i] <= 'z')) | ||
269 | bytes[i%16] = dev->eedata[i]; | ||
270 | else | ||
271 | bytes[i%16] = '.'; | ||
272 | |||
273 | i++; | ||
274 | |||
275 | if (0 == (i % 16)) { | ||
276 | bytes[16] = '\0'; | ||
277 | printk(" %s\n", bytes); | ||
278 | } | ||
279 | } | ||
280 | if (0 != (i%16)) { | ||
281 | bytes[i%16] = '\0'; | ||
282 | for (i %= 16; i < 16; i++) | ||
283 | printk(" "); | ||
284 | printk(" %s\n", bytes); | ||
285 | } | ||
286 | |||
287 | return 0; | ||
288 | |||
289 | noeeprom: | ||
290 | printk(KERN_INFO "%s: Huh, no eeprom present (err=%d)?\n", | ||
291 | dev->name, rc); | ||
292 | return -EINVAL; | ||
293 | } | ||
294 | |||
295 | /* ----------------------------------------------------------- */ | ||
296 | |||
297 | /* | ||
298 | * functionality() | ||
299 | */ | ||
300 | static u32 functionality(struct i2c_adapter *adap) | ||
301 | { | ||
302 | return I2C_FUNC_SMBUS_EMUL; | ||
303 | } | ||
304 | |||
305 | static const struct i2c_algorithm tm6000_algo = { | ||
306 | .master_xfer = tm6000_i2c_xfer, | ||
307 | .functionality = functionality, | ||
308 | }; | ||
309 | |||
310 | /* ----------------------------------------------------------- */ | ||
311 | |||
312 | /* | ||
313 | * tm6000_i2c_register() | ||
314 | * register i2c bus | ||
315 | */ | ||
316 | int tm6000_i2c_register(struct tm6000_core *dev) | ||
317 | { | ||
318 | int rc; | ||
319 | |||
320 | dev->i2c_adap.owner = THIS_MODULE; | ||
321 | dev->i2c_adap.algo = &tm6000_algo; | ||
322 | dev->i2c_adap.dev.parent = &dev->udev->dev; | ||
323 | strlcpy(dev->i2c_adap.name, dev->name, sizeof(dev->i2c_adap.name)); | ||
324 | dev->i2c_adap.algo_data = dev; | ||
325 | i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev); | ||
326 | rc = i2c_add_adapter(&dev->i2c_adap); | ||
327 | if (rc) | ||
328 | return rc; | ||
329 | |||
330 | dev->i2c_client.adapter = &dev->i2c_adap; | ||
331 | strlcpy(dev->i2c_client.name, "tm6000 internal", I2C_NAME_SIZE); | ||
332 | tm6000_i2c_eeprom(dev); | ||
333 | |||
334 | return 0; | ||
335 | } | ||
336 | |||
337 | /* | ||
338 | * tm6000_i2c_unregister() | ||
339 | * unregister i2c_bus | ||
340 | */ | ||
341 | int tm6000_i2c_unregister(struct tm6000_core *dev) | ||
342 | { | ||
343 | i2c_del_adapter(&dev->i2c_adap); | ||
344 | return 0; | ||
345 | } | ||