diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/media/video/saa7134/saa7134-i2c.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/media/video/saa7134/saa7134-i2c.c')
-rw-r--r-- | drivers/media/video/saa7134/saa7134-i2c.c | 453 |
1 files changed, 453 insertions, 0 deletions
diff --git a/drivers/media/video/saa7134/saa7134-i2c.c b/drivers/media/video/saa7134/saa7134-i2c.c new file mode 100644 index 000000000000..702bb63d9813 --- /dev/null +++ b/drivers/media/video/saa7134/saa7134-i2c.c | |||
@@ -0,0 +1,453 @@ | |||
1 | /* | ||
2 | * $Id: saa7134-i2c.c,v 1.10 2005/01/24 17:37:23 kraxel Exp $ | ||
3 | * | ||
4 | * device driver for philips saa7134 based TV cards | ||
5 | * i2c interface support | ||
6 | * | ||
7 | * (c) 2001,02 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs] | ||
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; either version 2 of the License, or | ||
12 | * (at your option) any later version. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
22 | */ | ||
23 | |||
24 | #include <linux/init.h> | ||
25 | #include <linux/list.h> | ||
26 | #include <linux/module.h> | ||
27 | #include <linux/moduleparam.h> | ||
28 | #include <linux/kernel.h> | ||
29 | #include <linux/slab.h> | ||
30 | #include <linux/delay.h> | ||
31 | |||
32 | #include "saa7134-reg.h" | ||
33 | #include "saa7134.h" | ||
34 | |||
35 | /* ----------------------------------------------------------- */ | ||
36 | |||
37 | static unsigned int i2c_debug = 0; | ||
38 | module_param(i2c_debug, int, 0644); | ||
39 | MODULE_PARM_DESC(i2c_debug,"enable debug messages [i2c]"); | ||
40 | |||
41 | static unsigned int i2c_scan = 0; | ||
42 | module_param(i2c_scan, int, 0444); | ||
43 | MODULE_PARM_DESC(i2c_scan,"scan i2c bus at insmod time"); | ||
44 | |||
45 | #define d1printk if (1 == i2c_debug) printk | ||
46 | #define d2printk if (2 == i2c_debug) printk | ||
47 | |||
48 | #define I2C_WAIT_DELAY 32 | ||
49 | #define I2C_WAIT_RETRY 16 | ||
50 | |||
51 | /* ----------------------------------------------------------- */ | ||
52 | |||
53 | static char *str_i2c_status[] = { | ||
54 | "IDLE", "DONE_STOP", "BUSY", "TO_SCL", "TO_ARB", "DONE_WRITE", | ||
55 | "DONE_READ", "DONE_WRITE_TO", "DONE_READ_TO", "NO_DEVICE", | ||
56 | "NO_ACKN", "BUS_ERR", "ARB_LOST", "SEQ_ERR", "ST_ERR", "SW_ERR" | ||
57 | }; | ||
58 | |||
59 | enum i2c_status { | ||
60 | IDLE = 0, // no I2C command pending | ||
61 | DONE_STOP = 1, // I2C command done and STOP executed | ||
62 | BUSY = 2, // executing I2C command | ||
63 | TO_SCL = 3, // executing I2C command, time out on clock stretching | ||
64 | TO_ARB = 4, // time out on arbitration trial, still trying | ||
65 | DONE_WRITE = 5, // I2C command done and awaiting next write command | ||
66 | DONE_READ = 6, // I2C command done and awaiting next read command | ||
67 | DONE_WRITE_TO = 7, // see 5, and time out on status echo | ||
68 | DONE_READ_TO = 8, // see 6, and time out on status echo | ||
69 | NO_DEVICE = 9, // no acknowledge on device slave address | ||
70 | NO_ACKN = 10, // no acknowledge after data byte transfer | ||
71 | BUS_ERR = 11, // bus error | ||
72 | ARB_LOST = 12, // arbitration lost during transfer | ||
73 | SEQ_ERR = 13, // erroneous programming sequence | ||
74 | ST_ERR = 14, // wrong status echoing | ||
75 | SW_ERR = 15 // software error | ||
76 | }; | ||
77 | |||
78 | static char *str_i2c_attr[] = { | ||
79 | "NOP", "STOP", "CONTINUE", "START" | ||
80 | }; | ||
81 | |||
82 | enum i2c_attr { | ||
83 | NOP = 0, // no operation on I2C bus | ||
84 | STOP = 1, // stop condition, no associated byte transfer | ||
85 | CONTINUE = 2, // continue with byte transfer | ||
86 | START = 3 // start condition with byte transfer | ||
87 | }; | ||
88 | |||
89 | static inline enum i2c_status i2c_get_status(struct saa7134_dev *dev) | ||
90 | { | ||
91 | enum i2c_status status; | ||
92 | |||
93 | status = saa_readb(SAA7134_I2C_ATTR_STATUS) & 0x0f; | ||
94 | d2printk(KERN_DEBUG "%s: i2c stat <= %s\n",dev->name, | ||
95 | str_i2c_status[status]); | ||
96 | return status; | ||
97 | } | ||
98 | |||
99 | static inline void i2c_set_status(struct saa7134_dev *dev, | ||
100 | enum i2c_status status) | ||
101 | { | ||
102 | d2printk(KERN_DEBUG "%s: i2c stat => %s\n",dev->name, | ||
103 | str_i2c_status[status]); | ||
104 | saa_andorb(SAA7134_I2C_ATTR_STATUS,0x0f,status); | ||
105 | } | ||
106 | |||
107 | static inline void i2c_set_attr(struct saa7134_dev *dev, enum i2c_attr attr) | ||
108 | { | ||
109 | d2printk(KERN_DEBUG "%s: i2c attr => %s\n",dev->name, | ||
110 | str_i2c_attr[attr]); | ||
111 | saa_andorb(SAA7134_I2C_ATTR_STATUS,0xc0,attr << 6); | ||
112 | } | ||
113 | |||
114 | static inline int i2c_is_error(enum i2c_status status) | ||
115 | { | ||
116 | switch (status) { | ||
117 | case NO_DEVICE: | ||
118 | case NO_ACKN: | ||
119 | case BUS_ERR: | ||
120 | case ARB_LOST: | ||
121 | case SEQ_ERR: | ||
122 | case ST_ERR: | ||
123 | return TRUE; | ||
124 | default: | ||
125 | return FALSE; | ||
126 | } | ||
127 | } | ||
128 | |||
129 | static inline int i2c_is_idle(enum i2c_status status) | ||
130 | { | ||
131 | switch (status) { | ||
132 | case IDLE: | ||
133 | case DONE_STOP: | ||
134 | return TRUE; | ||
135 | default: | ||
136 | return FALSE; | ||
137 | } | ||
138 | } | ||
139 | |||
140 | static inline int i2c_is_busy(enum i2c_status status) | ||
141 | { | ||
142 | switch (status) { | ||
143 | case BUSY: | ||
144 | return TRUE; | ||
145 | default: | ||
146 | return FALSE; | ||
147 | } | ||
148 | } | ||
149 | |||
150 | static int i2c_is_busy_wait(struct saa7134_dev *dev) | ||
151 | { | ||
152 | enum i2c_status status; | ||
153 | int count; | ||
154 | |||
155 | for (count = 0; count < I2C_WAIT_RETRY; count++) { | ||
156 | status = i2c_get_status(dev); | ||
157 | if (!i2c_is_busy(status)) | ||
158 | break; | ||
159 | saa_wait(I2C_WAIT_DELAY); | ||
160 | } | ||
161 | if (I2C_WAIT_RETRY == count) | ||
162 | return FALSE; | ||
163 | return TRUE; | ||
164 | } | ||
165 | |||
166 | static int i2c_reset(struct saa7134_dev *dev) | ||
167 | { | ||
168 | enum i2c_status status; | ||
169 | int count; | ||
170 | |||
171 | d2printk(KERN_DEBUG "%s: i2c reset\n",dev->name); | ||
172 | status = i2c_get_status(dev); | ||
173 | if (!i2c_is_error(status)) | ||
174 | return TRUE; | ||
175 | i2c_set_status(dev,status); | ||
176 | |||
177 | for (count = 0; count < I2C_WAIT_RETRY; count++) { | ||
178 | status = i2c_get_status(dev); | ||
179 | if (!i2c_is_error(status)) | ||
180 | break; | ||
181 | udelay(I2C_WAIT_DELAY); | ||
182 | } | ||
183 | if (I2C_WAIT_RETRY == count) | ||
184 | return FALSE; | ||
185 | |||
186 | if (!i2c_is_idle(status)) | ||
187 | return FALSE; | ||
188 | |||
189 | i2c_set_attr(dev,NOP); | ||
190 | return TRUE; | ||
191 | } | ||
192 | |||
193 | static inline int i2c_send_byte(struct saa7134_dev *dev, | ||
194 | enum i2c_attr attr, | ||
195 | unsigned char data) | ||
196 | { | ||
197 | enum i2c_status status; | ||
198 | __u32 dword; | ||
199 | |||
200 | #if 0 | ||
201 | i2c_set_attr(dev,attr); | ||
202 | saa_writeb(SAA7134_I2C_DATA, data); | ||
203 | #else | ||
204 | /* have to write both attr + data in one 32bit word */ | ||
205 | dword = saa_readl(SAA7134_I2C_ATTR_STATUS >> 2); | ||
206 | dword &= 0x0f; | ||
207 | dword |= (attr << 6); | ||
208 | dword |= ((__u32)data << 8); | ||
209 | dword |= 0x00 << 16; /* 100 kHz */ | ||
210 | // dword |= 0x40 << 16; /* 400 kHz */ | ||
211 | dword |= 0xf0 << 24; | ||
212 | saa_writel(SAA7134_I2C_ATTR_STATUS >> 2, dword); | ||
213 | #endif | ||
214 | d2printk(KERN_DEBUG "%s: i2c data => 0x%x\n",dev->name,data); | ||
215 | |||
216 | if (!i2c_is_busy_wait(dev)) | ||
217 | return -EIO; | ||
218 | status = i2c_get_status(dev); | ||
219 | if (i2c_is_error(status)) | ||
220 | return -EIO; | ||
221 | return 0; | ||
222 | } | ||
223 | |||
224 | static inline int i2c_recv_byte(struct saa7134_dev *dev) | ||
225 | { | ||
226 | enum i2c_status status; | ||
227 | unsigned char data; | ||
228 | |||
229 | i2c_set_attr(dev,CONTINUE); | ||
230 | if (!i2c_is_busy_wait(dev)) | ||
231 | return -EIO; | ||
232 | status = i2c_get_status(dev); | ||
233 | if (i2c_is_error(status)) | ||
234 | return -EIO; | ||
235 | data = saa_readb(SAA7134_I2C_DATA); | ||
236 | d2printk(KERN_DEBUG "%s: i2c data <= 0x%x\n",dev->name,data); | ||
237 | return data; | ||
238 | } | ||
239 | |||
240 | static int saa7134_i2c_xfer(struct i2c_adapter *i2c_adap, | ||
241 | struct i2c_msg *msgs, int num) | ||
242 | { | ||
243 | struct saa7134_dev *dev = i2c_adap->algo_data; | ||
244 | enum i2c_status status; | ||
245 | unsigned char data; | ||
246 | int addr,rc,i,byte; | ||
247 | |||
248 | status = i2c_get_status(dev); | ||
249 | if (!i2c_is_idle(status)) | ||
250 | if (!i2c_reset(dev)) | ||
251 | return -EIO; | ||
252 | |||
253 | d2printk("start xfer\n"); | ||
254 | d1printk(KERN_DEBUG "%s: i2c xfer:",dev->name); | ||
255 | for (i = 0; i < num; i++) { | ||
256 | if (!(msgs[i].flags & I2C_M_NOSTART) || 0 == i) { | ||
257 | /* send address */ | ||
258 | d2printk("send address\n"); | ||
259 | addr = msgs[i].addr << 1; | ||
260 | if (msgs[i].flags & I2C_M_RD) | ||
261 | addr |= 1; | ||
262 | if (i > 0 && msgs[i].flags & I2C_M_RD) { | ||
263 | /* workaround for a saa7134 i2c bug | ||
264 | * needed to talk to the mt352 demux | ||
265 | * thanks to pinnacle for the hint */ | ||
266 | int quirk = 0xfd; | ||
267 | d1printk(" [%02x quirk]",quirk); | ||
268 | i2c_send_byte(dev,START,quirk); | ||
269 | i2c_recv_byte(dev); | ||
270 | } | ||
271 | d1printk(" < %02x", addr); | ||
272 | rc = i2c_send_byte(dev,START,addr); | ||
273 | if (rc < 0) | ||
274 | goto err; | ||
275 | } | ||
276 | if (msgs[i].flags & I2C_M_RD) { | ||
277 | /* read bytes */ | ||
278 | d2printk("read bytes\n"); | ||
279 | for (byte = 0; byte < msgs[i].len; byte++) { | ||
280 | d1printk(" ="); | ||
281 | rc = i2c_recv_byte(dev); | ||
282 | if (rc < 0) | ||
283 | goto err; | ||
284 | d1printk("%02x", rc); | ||
285 | msgs[i].buf[byte] = rc; | ||
286 | } | ||
287 | } else { | ||
288 | /* write bytes */ | ||
289 | d2printk("write bytes\n"); | ||
290 | for (byte = 0; byte < msgs[i].len; byte++) { | ||
291 | data = msgs[i].buf[byte]; | ||
292 | d1printk(" %02x", data); | ||
293 | rc = i2c_send_byte(dev,CONTINUE,data); | ||
294 | if (rc < 0) | ||
295 | goto err; | ||
296 | } | ||
297 | } | ||
298 | } | ||
299 | d2printk("xfer done\n"); | ||
300 | d1printk(" >"); | ||
301 | i2c_set_attr(dev,STOP); | ||
302 | rc = -EIO; | ||
303 | if (!i2c_is_busy_wait(dev)) | ||
304 | goto err; | ||
305 | status = i2c_get_status(dev); | ||
306 | if (i2c_is_error(status)) | ||
307 | goto err; | ||
308 | |||
309 | d1printk("\n"); | ||
310 | return num; | ||
311 | err: | ||
312 | if (1 == i2c_debug) { | ||
313 | status = i2c_get_status(dev); | ||
314 | printk(" ERROR: %s\n",str_i2c_status[status]); | ||
315 | } | ||
316 | return rc; | ||
317 | } | ||
318 | |||
319 | /* ----------------------------------------------------------- */ | ||
320 | |||
321 | static int algo_control(struct i2c_adapter *adapter, | ||
322 | unsigned int cmd, unsigned long arg) | ||
323 | { | ||
324 | return 0; | ||
325 | } | ||
326 | |||
327 | static u32 functionality(struct i2c_adapter *adap) | ||
328 | { | ||
329 | return I2C_FUNC_SMBUS_EMUL; | ||
330 | } | ||
331 | |||
332 | static int attach_inform(struct i2c_client *client) | ||
333 | { | ||
334 | struct saa7134_dev *dev = client->adapter->algo_data; | ||
335 | int tuner = dev->tuner_type; | ||
336 | int conf = dev->tda9887_conf; | ||
337 | |||
338 | saa7134_i2c_call_clients(dev,TUNER_SET_TYPE,&tuner); | ||
339 | saa7134_i2c_call_clients(dev,TDA9887_SET_CONFIG,&conf); | ||
340 | return 0; | ||
341 | } | ||
342 | |||
343 | static struct i2c_algorithm saa7134_algo = { | ||
344 | .name = "saa7134", | ||
345 | .id = I2C_ALGO_SAA7134, | ||
346 | .master_xfer = saa7134_i2c_xfer, | ||
347 | .algo_control = algo_control, | ||
348 | .functionality = functionality, | ||
349 | }; | ||
350 | |||
351 | static struct i2c_adapter saa7134_adap_template = { | ||
352 | .owner = THIS_MODULE, | ||
353 | #ifdef I2C_CLASS_TV_ANALOG | ||
354 | .class = I2C_CLASS_TV_ANALOG, | ||
355 | #endif | ||
356 | I2C_DEVNAME("saa7134"), | ||
357 | .id = I2C_ALGO_SAA7134, | ||
358 | .algo = &saa7134_algo, | ||
359 | .client_register = attach_inform, | ||
360 | }; | ||
361 | |||
362 | static struct i2c_client saa7134_client_template = { | ||
363 | I2C_DEVNAME("saa7134 internal"), | ||
364 | }; | ||
365 | |||
366 | /* ----------------------------------------------------------- */ | ||
367 | |||
368 | static int | ||
369 | saa7134_i2c_eeprom(struct saa7134_dev *dev, unsigned char *eedata, int len) | ||
370 | { | ||
371 | unsigned char buf; | ||
372 | int i,err; | ||
373 | |||
374 | dev->i2c_client.addr = 0xa0 >> 1; | ||
375 | buf = 0; | ||
376 | if (1 != (err = i2c_master_send(&dev->i2c_client,&buf,1))) { | ||
377 | printk(KERN_INFO "%s: Huh, no eeprom present (err=%d)?\n", | ||
378 | dev->name,err); | ||
379 | return -1; | ||
380 | } | ||
381 | if (len != (err = i2c_master_recv(&dev->i2c_client,eedata,len))) { | ||
382 | printk(KERN_WARNING "%s: i2c eeprom read error (err=%d)\n", | ||
383 | dev->name,err); | ||
384 | return -1; | ||
385 | } | ||
386 | for (i = 0; i < len; i++) { | ||
387 | if (0 == (i % 16)) | ||
388 | printk(KERN_INFO "%s: i2c eeprom %02x:",dev->name,i); | ||
389 | printk(" %02x",eedata[i]); | ||
390 | if (15 == (i % 16)) | ||
391 | printk("\n"); | ||
392 | } | ||
393 | return 0; | ||
394 | } | ||
395 | |||
396 | static char *i2c_devs[128] = { | ||
397 | [ 0x20 ] = "mpeg encoder (saa6752hs)", | ||
398 | [ 0xa0 >> 1 ] = "eeprom", | ||
399 | [ 0xc0 >> 1 ] = "tuner (analog)", | ||
400 | [ 0x86 >> 1 ] = "tda9887", | ||
401 | }; | ||
402 | |||
403 | static void do_i2c_scan(char *name, struct i2c_client *c) | ||
404 | { | ||
405 | unsigned char buf; | ||
406 | int i,rc; | ||
407 | |||
408 | for (i = 0; i < 128; i++) { | ||
409 | c->addr = i; | ||
410 | rc = i2c_master_recv(c,&buf,0); | ||
411 | if (rc < 0) | ||
412 | continue; | ||
413 | printk("%s: i2c scan: found device @ 0x%x [%s]\n", | ||
414 | name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???"); | ||
415 | } | ||
416 | } | ||
417 | |||
418 | void saa7134_i2c_call_clients(struct saa7134_dev *dev, | ||
419 | unsigned int cmd, void *arg) | ||
420 | { | ||
421 | BUG_ON(NULL == dev->i2c_adap.algo_data); | ||
422 | i2c_clients_command(&dev->i2c_adap, cmd, arg); | ||
423 | } | ||
424 | |||
425 | int saa7134_i2c_register(struct saa7134_dev *dev) | ||
426 | { | ||
427 | dev->i2c_adap = saa7134_adap_template; | ||
428 | dev->i2c_adap.dev.parent = &dev->pci->dev; | ||
429 | strcpy(dev->i2c_adap.name,dev->name); | ||
430 | dev->i2c_adap.algo_data = dev; | ||
431 | i2c_add_adapter(&dev->i2c_adap); | ||
432 | |||
433 | dev->i2c_client = saa7134_client_template; | ||
434 | dev->i2c_client.adapter = &dev->i2c_adap; | ||
435 | |||
436 | saa7134_i2c_eeprom(dev,dev->eedata,sizeof(dev->eedata)); | ||
437 | if (i2c_scan) | ||
438 | do_i2c_scan(dev->name,&dev->i2c_client); | ||
439 | return 0; | ||
440 | } | ||
441 | |||
442 | int saa7134_i2c_unregister(struct saa7134_dev *dev) | ||
443 | { | ||
444 | i2c_del_adapter(&dev->i2c_adap); | ||
445 | return 0; | ||
446 | } | ||
447 | |||
448 | /* ----------------------------------------------------------- */ | ||
449 | /* | ||
450 | * Local variables: | ||
451 | * c-basic-offset: 8 | ||
452 | * End: | ||
453 | */ | ||