vserver 2.0 rc7
[linux-2.6.git] / drivers / media / video / tveeprom.c
1 /*
2  * tveeprom - eeprom decoder for tvcard configuration eeproms
3  *
4  * Data and decoding routines shamelessly borrowed from bttv-cards.c
5  * eeprom access routine shamelessly borrowed from bttv-if.c
6  * which are:
7
8     Copyright (C) 1996,97,98 Ralph  Metzler (rjkm@thp.uni-koeln.de)
9                            & Marcus Metzler (mocm@thp.uni-koeln.de)
10     (c) 1999-2001 Gerd Knorr <kraxel@goldbach.in-berlin.de>
11
12  * Adjustments to fit a more general model and all bugs:
13
14         Copyright (C) 2003 John Klar <linpvr at projectplasma.com>
15
16  * This program is free software; you can redistribute it and/or modify
17  * it under the terms of the GNU General Public License as published by
18  * the Free Software Foundation; either version 2 of the License, or
19  * (at your option) any later version.
20  *
21  * This program is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  * GNU General Public License for more details.
25  *
26  * You should have received a copy of the GNU General Public License
27  * along with this program; if not, write to the Free Software
28  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29  */
30
31
32 #include <linux/module.h>
33 #include <linux/moduleparam.h>
34 #include <linux/errno.h>
35 #include <linux/kernel.h>
36 #include <linux/init.h>
37 #include <linux/types.h>
38 #include <linux/videodev.h>
39 #include <linux/i2c.h>
40
41 #include <media/tuner.h>
42 #include <media/tveeprom.h>
43
44 MODULE_DESCRIPTION("i2c Hauppauge eeprom decoder driver");
45 MODULE_AUTHOR("John Klar");
46 MODULE_LICENSE("GPL");
47
48 static int debug = 0;
49 module_param(debug, int, 0644);
50 MODULE_PARM_DESC(debug, "Debug level (0-2)");
51
52 #define STRM(array,i) (i < sizeof(array)/sizeof(char*) ? array[i] : "unknown")
53
54 #define dprintk(num, args...) \
55         do { \
56                 if (debug >= num) \
57                         printk(KERN_INFO "tveeprom: " args); \
58         } while (0)
59
60 #define TVEEPROM_KERN_ERR(args...) printk(KERN_ERR "tveeprom: " args);
61 #define TVEEPROM_KERN_INFO(args...) printk(KERN_INFO "tveeprom: " args);
62
63 /* ----------------------------------------------------------------------- */
64 /* some hauppauge specific stuff                                           */
65
66 static struct HAUPPAUGE_TUNER_FMT
67 {
68         int     id;
69         char *name;
70 }
71 hauppauge_tuner_fmt[] =
72 {
73         { 0x00000000, "unknown1" },
74         { 0x00000000, "unknown2" },
75         { 0x00000007, "PAL(B/G)" },
76         { 0x00001000, "NTSC(M)" },
77         { 0x00000010, "PAL(I)" },
78         { 0x00400000, "SECAM(L/L�)" },
79         { 0x00000e00, "PAL(D/K)" },
80         { 0x03000000, "ATSC Digital" },
81 };
82
83 /* This is the full list of possible tuners. Many thanks to Hauppauge for
84    supplying this information. Note that many tuners where only used for
85    testing and never made it to the outside world. So you will only see
86    a subset in actual produced cards. */
87 static struct HAUPPAUGE_TUNER
88 {
89         int  id;
90         char *name;
91 }
92 hauppauge_tuner[] =
93 {
94         /* 0-9 */
95         { TUNER_ABSENT,        "None" },
96         { TUNER_ABSENT,        "External" },
97         { TUNER_ABSENT,        "Unspecified" },
98         { TUNER_PHILIPS_PAL,   "Philips FI1216" },
99         { TUNER_PHILIPS_SECAM, "Philips FI1216MF" },
100         { TUNER_PHILIPS_NTSC,  "Philips FI1236" },
101         { TUNER_PHILIPS_PAL_I, "Philips FI1246" },
102         { TUNER_PHILIPS_PAL_DK,"Philips FI1256" },
103         { TUNER_PHILIPS_PAL,   "Philips FI1216 MK2" },
104         { TUNER_PHILIPS_SECAM, "Philips FI1216MF MK2" },
105         /* 10-19 */
106         { TUNER_PHILIPS_NTSC,  "Philips FI1236 MK2" },
107         { TUNER_PHILIPS_PAL_I, "Philips FI1246 MK2" },
108         { TUNER_PHILIPS_PAL_DK,"Philips FI1256 MK2" },
109         { TUNER_TEMIC_NTSC,    "Temic 4032FY5" },
110         { TUNER_TEMIC_PAL,     "Temic 4002FH5" },
111         { TUNER_TEMIC_PAL_I,   "Temic 4062FY5" },
112         { TUNER_PHILIPS_PAL,   "Philips FR1216 MK2" },
113         { TUNER_PHILIPS_SECAM, "Philips FR1216MF MK2" },
114         { TUNER_PHILIPS_NTSC,  "Philips FR1236 MK2" },
115         { TUNER_PHILIPS_PAL_I, "Philips FR1246 MK2" },
116         /* 20-29 */
117         { TUNER_PHILIPS_PAL_DK,"Philips FR1256 MK2" },
118         { TUNER_PHILIPS_PAL,   "Philips FM1216" },
119         { TUNER_PHILIPS_SECAM, "Philips FM1216MF" },
120         { TUNER_PHILIPS_NTSC,  "Philips FM1236" },
121         { TUNER_PHILIPS_PAL_I, "Philips FM1246" },
122         { TUNER_PHILIPS_PAL_DK,"Philips FM1256" },
123         { TUNER_TEMIC_4036FY5_NTSC, "Temic 4036FY5" },
124         { TUNER_ABSENT,        "Samsung TCPN9082D" },
125         { TUNER_ABSENT,        "Samsung TCPM9092P" },
126         { TUNER_TEMIC_4006FH5_PAL, "Temic 4006FH5" },
127         /* 30-39 */
128         { TUNER_ABSENT,        "Samsung TCPN9085D" },
129         { TUNER_ABSENT,        "Samsung TCPB9085P" },
130         { TUNER_ABSENT,        "Samsung TCPL9091P" },
131         { TUNER_TEMIC_4039FR5_NTSC, "Temic 4039FR5" },
132         { TUNER_PHILIPS_FQ1216ME,   "Philips FQ1216 ME" },
133         { TUNER_TEMIC_4066FY5_PAL_I, "Temic 4066FY5" },
134         { TUNER_PHILIPS_NTSC,        "Philips TD1536" },
135         { TUNER_PHILIPS_NTSC,        "Philips TD1536D" },
136         { TUNER_PHILIPS_NTSC,  "Philips FMR1236" }, /* mono radio */
137         { TUNER_ABSENT,        "Philips FI1256MP" },
138         /* 40-49 */
139         { TUNER_ABSENT,        "Samsung TCPQ9091P" },
140         { TUNER_TEMIC_4006FN5_MULTI_PAL, "Temic 4006FN5" },
141         { TUNER_TEMIC_4009FR5_PAL, "Temic 4009FR5" },
142         { TUNER_TEMIC_4046FM5,     "Temic 4046FM5" },
143         { TUNER_TEMIC_4009FN5_MULTI_PAL_FM, "Temic 4009FN5" },
144         { TUNER_ABSENT,        "Philips TD1536D FH 44"},
145         { TUNER_LG_NTSC_FM,    "LG TP18NSR01F"},
146         { TUNER_LG_PAL_FM,     "LG TP18PSB01D"},
147         { TUNER_LG_PAL,        "LG TP18PSB11D"},
148         { TUNER_LG_PAL_I_FM,   "LG TAPC-I001D"},
149         /* 50-59 */
150         { TUNER_LG_PAL_I,      "LG TAPC-I701D"},
151         { TUNER_ABSENT,        "Temic 4042FI5"},
152         { TUNER_MICROTUNE_4049FM5, "Microtune 4049 FM5"},
153         { TUNER_ABSENT,        "LG TPI8NSR11F"},
154         { TUNER_ABSENT,        "Microtune 4049 FM5 Alt I2C"},
155         { TUNER_ABSENT,        "Philips FQ1216ME MK3"},
156         { TUNER_ABSENT,        "Philips FI1236 MK3"},
157         { TUNER_PHILIPS_FM1216ME_MK3, "Philips FM1216 ME MK3"},
158         { TUNER_ABSENT,        "Philips FM1236 MK3"},
159         { TUNER_ABSENT,        "Philips FM1216MP MK3"},
160         /* 60-69 */
161         { TUNER_ABSENT,        "LG S001D MK3"},
162         { TUNER_ABSENT,        "LG M001D MK3"},
163         { TUNER_ABSENT,        "LG S701D MK3"},
164         { TUNER_ABSENT,        "LG M701D MK3"},
165         { TUNER_ABSENT,        "Temic 4146FM5"},
166         { TUNER_ABSENT,        "Temic 4136FY5"},
167         { TUNER_ABSENT,        "Temic 4106FH5"},
168         { TUNER_ABSENT,        "Philips FQ1216LMP MK3"},
169         { TUNER_LG_NTSC_TAPE,  "LG TAPE H001F MK3"},
170         { TUNER_ABSENT,        "LG TAPE H701F MK3"},
171         /* 70-79 */
172         { TUNER_ABSENT,        "LG TALN H200T"},
173         { TUNER_ABSENT,        "LG TALN H250T"},
174         { TUNER_ABSENT,        "LG TALN M200T"},
175         { TUNER_ABSENT,        "LG TALN Z200T"},
176         { TUNER_ABSENT,        "LG TALN S200T"},
177         { TUNER_ABSENT,        "Thompson DTT7595"},
178         { TUNER_ABSENT,        "Thompson DTT7592"},
179         { TUNER_ABSENT,        "Silicon TDA8275C1 8290"},
180         { TUNER_ABSENT,        "Silicon TDA8275C1 8290 FM"},
181         { TUNER_ABSENT,        "Thompson DTT757"},
182         /* 80-89 */
183         { TUNER_ABSENT,        "Philips FQ1216LME MK3"},
184         { TUNER_ABSENT,        "LG TAPC G701D"},
185         { TUNER_LG_NTSC_NEW_TAPC, "LG TAPC H791F"},
186         { TUNER_ABSENT,        "TCL 2002MB 3"},
187         { TUNER_ABSENT,        "TCL 2002MI 3"},
188         { TUNER_TCL_2002N,     "TCL 2002N 6A"},
189         { TUNER_ABSENT,        "Philips FQ1236 MK3"},
190         { TUNER_ABSENT,        "Samsung TCPN 2121P30A"},
191         { TUNER_ABSENT,        "Samsung TCPE 4121P30A"},
192         { TUNER_ABSENT,        "TCL MFPE05 2"},
193         /* 90-99 */
194         { TUNER_ABSENT,        "LG TALN H202T"},
195         { TUNER_PHILIPS_FQ1216AME_MK4, "Philips FQ1216AME MK4"},
196         { TUNER_PHILIPS_FQ1236A_MK4, "Philips FQ1236A MK4"},
197         { TUNER_ABSENT,        "Philips FQ1286A MK4"},
198         { TUNER_ABSENT,        "Philips FQ1216ME MK5"},
199         { TUNER_ABSENT,        "Philips FQ1236 MK5"},
200         { TUNER_ABSENT,        "Unspecified"},
201         { TUNER_LG_PAL_TAPE,   "LG PAL (TAPE Series)"},
202 };
203
204 static char *sndtype[] = {
205         "None", "TEA6300", "TEA6320", "TDA9850", "MSP3400C", "MSP3410D",
206         "MSP3415", "MSP3430", "MSP3438", "CS5331", "MSP3435", "MSP3440",
207         "MSP3445", "MSP3411", "MSP3416", "MSP3425",
208
209         "Type 0x10","Type 0x11","Type 0x12","Type 0x13",
210         "Type 0x14","Type 0x15","Type 0x16","Type 0x17",
211         "Type 0x18","MSP4418","Type 0x1a","MSP4448",
212         "Type 0x1c","Type 0x1d","Type 0x1e","Type 0x1f",
213 };
214
215 static int hasRadioTuner(int tunerType)
216 {
217         switch (tunerType) {
218                 case 18: //PNPEnv_TUNER_FR1236_MK2:
219                 case 23: //PNPEnv_TUNER_FM1236:
220                 case 38: //PNPEnv_TUNER_FMR1236:
221                 case 16: //PNPEnv_TUNER_FR1216_MK2:
222                 case 19: //PNPEnv_TUNER_FR1246_MK2:
223                 case 21: //PNPEnv_TUNER_FM1216:
224                 case 24: //PNPEnv_TUNER_FM1246:
225                 case 17: //PNPEnv_TUNER_FR1216MF_MK2:
226                 case 22: //PNPEnv_TUNER_FM1216MF:
227                 case 20: //PNPEnv_TUNER_FR1256_MK2:
228                 case 25: //PNPEnv_TUNER_FM1256:
229                 case 33: //PNPEnv_TUNER_4039FR5:
230                 case 42: //PNPEnv_TUNER_4009FR5:
231                 case 52: //PNPEnv_TUNER_4049FM5:
232                 case 54: //PNPEnv_TUNER_4049FM5_AltI2C:
233                 case 44: //PNPEnv_TUNER_4009FN5:
234                 case 31: //PNPEnv_TUNER_TCPB9085P:
235                 case 30: //PNPEnv_TUNER_TCPN9085D:
236                 case 46: //PNPEnv_TUNER_TP18NSR01F:
237                 case 47: //PNPEnv_TUNER_TP18PSB01D:
238                 case 49: //PNPEnv_TUNER_TAPC_I001D:
239                 case 60: //PNPEnv_TUNER_TAPE_S001D_MK3:
240                 case 57: //PNPEnv_TUNER_FM1216ME_MK3:
241                 case 59: //PNPEnv_TUNER_FM1216MP_MK3:
242                 case 58: //PNPEnv_TUNER_FM1236_MK3:
243                 case 68: //PNPEnv_TUNER_TAPE_H001F_MK3:
244                 case 61: //PNPEnv_TUNER_TAPE_M001D_MK3:
245                 case 78: //PNPEnv_TUNER_TDA8275C1_8290_FM:
246                 case 89: //PNPEnv_TUNER_TCL_MFPE05_2:
247                 case 92: //PNPEnv_TUNER_PHILIPS_FQ1236A_MK4:
248                     return 1;
249         }
250         return 0;
251 }
252
253 void tveeprom_hauppauge_analog(struct tveeprom *tvee, unsigned char *eeprom_data)
254 {
255         /* ----------------------------------------------
256         ** The hauppauge eeprom format is tagged
257         **
258         ** if packet[0] == 0x84, then packet[0..1] == length
259         ** else length = packet[0] & 3f;
260         ** if packet[0] & f8 == f8, then EOD and packet[1] == checksum
261         **
262         ** In our (ivtv) case we're interested in the following:
263         ** tuner type: tag [00].05 or [0a].01 (index into hauppauge_tuner)
264         ** tuner fmts: tag [00].04 or [0a].00 (bitmask index into hauppauge_tuner_fmt)
265         ** radio:      tag [00].{last} or [0e].00  (bitmask.  bit2=FM)
266         ** audio proc: tag [02].01 or [05].00 (lower nibble indexes lut?)
267
268         ** Fun info:
269         ** model:      tag [00].07-08 or [06].00-01
270         ** revision:   tag [00].09-0b or [06].04-06
271         ** serial#:    tag [01].05-07 or [04].04-06
272
273         ** # of inputs/outputs ???
274         */
275
276         int i, j, len, done, beenhere, tag, tuner = 0, t_format = 0;
277         char *t_name = NULL, *t_fmt_name = NULL;
278
279         dprintk(1, "%s\n",__FUNCTION__);
280         tvee->revision = done = len = beenhere = 0;
281         for (i = 0; !done && i < 256; i += len) {
282                 dprintk(2, "processing pos = %02x (%02x, %02x)\n",
283                         i, eeprom_data[i], eeprom_data[i + 1]);
284
285                 if (eeprom_data[i] == 0x84) {
286                         len = eeprom_data[i + 1] + (eeprom_data[i + 2] << 8);
287                         i+=3;
288                 } else if ((eeprom_data[i] & 0xf0) == 0x70) {
289                         if ((eeprom_data[i] & 0x08)) {
290                                 /* verify checksum! */
291                                 done = 1;
292                                 break;
293                         }
294                         len = eeprom_data[i] & 0x07;
295                         ++i;
296                 } else {
297                         TVEEPROM_KERN_ERR("Encountered bad packet header [%02x]. "
298                                    "Corrupt or not a Hauppauge eeprom.\n", eeprom_data[i]);
299                         return;
300                 }
301
302                 dprintk(1, "%3d [%02x] ", len, eeprom_data[i]);
303                 for(j = 1; j < len; j++) {
304                         dprintk(1, "%02x ", eeprom_data[i + j]);
305                 }
306                 dprintk(1, "\n");
307
308                 /* process by tag */
309                 tag = eeprom_data[i];
310                 switch (tag) {
311                 case 0x00:
312                         tuner = eeprom_data[i+6];
313                         t_format = eeprom_data[i+5];
314                         tvee->has_radio = eeprom_data[i+len-1];
315                         tvee->model =
316                                 eeprom_data[i+8] +
317                                 (eeprom_data[i+9] << 8);
318                         tvee->revision = eeprom_data[i+10] +
319                                 (eeprom_data[i+11] << 8) +
320                                 (eeprom_data[i+12] << 16);
321                         break;
322                 case 0x01:
323                         tvee->serial_number =
324                                 eeprom_data[i+6] +
325                                 (eeprom_data[i+7] << 8) +
326                                 (eeprom_data[i+8] << 16);
327                         break;
328                 case 0x02:
329                         tvee->audio_processor = eeprom_data[i+2] & 0x0f;
330                         break;
331                 case 0x04:
332                         tvee->serial_number =
333                                 eeprom_data[i+5] +
334                                 (eeprom_data[i+6] << 8) +
335                                 (eeprom_data[i+7] << 16);
336                         break;
337                 case 0x05:
338                         tvee->audio_processor = eeprom_data[i+1] & 0x0f;
339                         break;
340                 case 0x06:
341                         tvee->model =
342                                 eeprom_data[i+1] +
343                                 (eeprom_data[i+2] << 8);
344                         tvee->revision = eeprom_data[i+5] +
345                                 (eeprom_data[i+6] << 8) +
346                                 (eeprom_data[i+7] << 16);
347                         break;
348                 case 0x0a:
349                         if(beenhere == 0) {
350                                 tuner = eeprom_data[i+2];
351                                 t_format = eeprom_data[i+1];
352                                 beenhere = 1;
353                                 break;
354                         } else {
355                                 break;
356                         }
357                 case 0x0e:
358                         tvee->has_radio = eeprom_data[i+1];
359                         break;
360                 default:
361                         dprintk(1, "Not sure what to do with tag [%02x]\n", tag);
362                         /* dump the rest of the packet? */
363                 }
364
365         }
366
367         if (!done) {
368                 TVEEPROM_KERN_ERR("Ran out of data!\n");
369                 return;
370         }
371
372         if (tvee->revision != 0) {
373                 tvee->rev_str[0] = 32 + ((tvee->revision >> 18) & 0x3f);
374                 tvee->rev_str[1] = 32 + ((tvee->revision >> 12) & 0x3f);
375                 tvee->rev_str[2] = 32 + ((tvee->revision >>  6) & 0x3f);
376                 tvee->rev_str[3] = 32 + ( tvee->revision        & 0x3f);
377                 tvee->rev_str[4] = 0;
378         }
379
380         if (hasRadioTuner(tuner) && !tvee->has_radio) {
381             TVEEPROM_KERN_INFO("The eeprom says no radio is present, but the tuner type\n");
382             TVEEPROM_KERN_INFO("indicates otherwise. I will assume that radio is present.\n");
383             tvee->has_radio = 1;
384         }
385
386         if (tuner < sizeof(hauppauge_tuner)/sizeof(struct HAUPPAUGE_TUNER)) {
387                 tvee->tuner_type = hauppauge_tuner[tuner].id;
388                 t_name = hauppauge_tuner[tuner].name;
389         } else {
390                 t_name = "<unknown>";
391         }
392
393         tvee->tuner_formats = 0;
394         t_fmt_name = "<none>";
395         for (i = 0; i < 8; i++) {
396                 if (t_format & (1<<i)) {
397                         tvee->tuner_formats |= hauppauge_tuner_fmt[i].id;
398                         /* yuck */
399                         t_fmt_name = hauppauge_tuner_fmt[i].name;
400                 }
401         }
402
403 #if 0
404         if (t_format < sizeof(hauppauge_tuner_fmt)/sizeof(struct HAUPPAUGE_TUNER_FMT)) {
405                 tvee->tuner_formats = hauppauge_tuner_fmt[t_format].id;
406                 t_fmt_name = hauppauge_tuner_fmt[t_format].name;
407         } else {
408                 t_fmt_name = "<unknown>";
409         }
410 #endif
411
412         TVEEPROM_KERN_INFO("Hauppauge: model = %d, rev = %s, serial# = %d\n",
413                    tvee->model,
414                    tvee->rev_str,
415                    tvee->serial_number);
416         TVEEPROM_KERN_INFO("tuner = %s (idx = %d, type = %d)\n",
417                    t_name,
418                    tuner,
419                    tvee->tuner_type);
420         TVEEPROM_KERN_INFO("tuner fmt = %s (eeprom = 0x%02x, v4l2 = 0x%08x)\n",
421                    t_fmt_name,
422                    t_format,
423                    tvee->tuner_formats);
424
425         TVEEPROM_KERN_INFO("audio_processor = %s (type = %d)\n",
426                    STRM(sndtype,tvee->audio_processor),
427                    tvee->audio_processor);
428
429 }
430 EXPORT_SYMBOL(tveeprom_hauppauge_analog);
431
432 /* ----------------------------------------------------------------------- */
433 /* generic helper functions                                                */
434
435 int tveeprom_read(struct i2c_client *c, unsigned char *eedata, int len)
436 {
437         unsigned char buf;
438         int err;
439
440         dprintk(1, "%s\n",__FUNCTION__);
441         buf = 0;
442         if (1 != (err = i2c_master_send(c,&buf,1))) {
443                 printk(KERN_INFO "tveeprom(%s): Huh, no eeprom present (err=%d)?\n",
444                        c->name,err);
445                 return -1;
446         }
447         if (len != (err = i2c_master_recv(c,eedata,len))) {
448                 printk(KERN_WARNING "tveeprom(%s): i2c eeprom read error (err=%d)\n",
449                        c->name,err);
450                 return -1;
451         }
452         return 0;
453 }
454 EXPORT_SYMBOL(tveeprom_read);
455
456 int tveeprom_dump(unsigned char *eedata, int len)
457 {
458         int i;
459
460         dprintk(1, "%s\n",__FUNCTION__);
461         for (i = 0; i < len; i++) {
462                 if (0 == (i % 16))
463                         printk(KERN_INFO "tveeprom: %02x:",i);
464                 printk(" %02x",eedata[i]);
465                 if (15 == (i % 16))
466                         printk("\n");
467         }
468         return 0;
469 }
470 EXPORT_SYMBOL(tveeprom_dump);
471
472 /* ----------------------------------------------------------------------- */
473 /* needed for ivtv.sf.net at the moment.  Should go away in the long       */
474 /* run, just call the exported tveeprom_* directly, there is no point in   */
475 /* using the indirect way via i2c_driver->command()                        */
476
477 #ifndef I2C_DRIVERID_TVEEPROM
478 # define I2C_DRIVERID_TVEEPROM I2C_DRIVERID_EXP2
479 #endif
480
481 static unsigned short normal_i2c[] = {
482         0xa0 >> 1,
483         I2C_CLIENT_END,
484 };
485 static unsigned short normal_i2c_range[] = { I2C_CLIENT_END };
486 I2C_CLIENT_INSMOD;
487
488 struct i2c_driver i2c_driver_tveeprom;
489
490 static int
491 tveeprom_command(struct i2c_client *client,
492                  unsigned int       cmd,
493                  void              *arg)
494 {
495         struct tveeprom eeprom;
496         u32 *eeprom_props = arg;
497         u8 *buf;
498
499         switch (cmd) {
500         case 0:
501                 buf = kmalloc(256,GFP_KERNEL);
502                 memset(buf,0,256);
503                 tveeprom_read(client,buf,256);
504                 tveeprom_hauppauge_analog(&eeprom,buf);
505                 kfree(buf);
506                 eeprom_props[0] = eeprom.tuner_type;
507                 eeprom_props[1] = eeprom.tuner_formats;
508                 eeprom_props[2] = eeprom.model;
509                 eeprom_props[3] = eeprom.revision;
510                 break;
511         default:
512                 return -EINVAL;
513         }
514         return 0;
515 }
516
517 static int
518 tveeprom_detect_client(struct i2c_adapter *adapter,
519                        int                 address,
520                        int                 kind)
521 {
522         struct i2c_client *client;
523
524         dprintk(1,"%s: id 0x%x @ 0x%x\n",__FUNCTION__,
525                adapter->id, address << 1);
526         client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
527         if (NULL == client)
528                 return -ENOMEM;
529         memset(client, 0, sizeof(struct i2c_client));
530         client->addr = address;
531         client->adapter = adapter;
532         client->driver = &i2c_driver_tveeprom;
533         client->flags = I2C_CLIENT_ALLOW_USE;
534         snprintf(client->name, sizeof(client->name), "tveeprom");
535         i2c_attach_client(client);
536         return 0;
537 }
538
539 static int
540 tveeprom_attach_adapter (struct i2c_adapter *adapter)
541 {
542         dprintk(1,"%s: id 0x%x\n",__FUNCTION__,adapter->id);
543         if (adapter->id != (I2C_ALGO_BIT | I2C_HW_B_BT848))
544                 return 0;
545         return i2c_probe(adapter, &addr_data, tveeprom_detect_client);
546 }
547
548 static int
549 tveeprom_detach_client (struct i2c_client *client)
550 {
551         int err;
552
553         err = i2c_detach_client(client);
554         if (err < 0)
555                 return err;
556         kfree(client);
557         return 0;
558 }
559
560 struct i2c_driver i2c_driver_tveeprom = {
561         .owner          = THIS_MODULE,
562         .name           = "tveeprom",
563         .id             = I2C_DRIVERID_TVEEPROM,
564         .flags          = I2C_DF_NOTIFY,
565         .attach_adapter = tveeprom_attach_adapter,
566         .detach_client  = tveeprom_detach_client,
567         .command        = tveeprom_command,
568 };
569
570 static int __init tveeprom_init(void)
571 {
572         return i2c_add_driver(&i2c_driver_tveeprom);
573 }
574
575 static void __exit tveeprom_exit(void)
576 {
577         i2c_del_driver(&i2c_driver_tveeprom);
578 }
579
580 module_init(tveeprom_init);
581 module_exit(tveeprom_exit);
582
583 /*
584  * Local variables:
585  * c-basic-offset: 8
586  * End:
587  */