blob: 367401449e3cc5c4d6e19b84f668cbb4f280c9de [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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
44MODULE_DESCRIPTION("i2c Hauppauge eeprom decoder driver");
45MODULE_AUTHOR("John Klar");
46MODULE_LICENSE("GPL");
47
48static int debug = 0;
49module_param(debug, int, 0644);
50MODULE_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
66static struct HAUPPAUGE_TUNER_FMT
67{
68 int id;
69 char *name;
70}
71hauppauge_tuner_fmt[] =
72{
73 { 0x00000000, "unknown1" },
74 { 0x00000000, "unknown2" },
75 { 0x00000007, "PAL(B/G)" },
76 { 0x00001000, "NTSC(M)" },
77 { 0x00000010, "PAL(I)" },
Mauro Carvalho Chehabac19ecc2005-06-23 22:05:09 -070078 { 0x00400000, "SECAM(L/L´)" },
Linus Torvalds1da177e2005-04-16 15:20:36 -070079 { 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. */
87static struct HAUPPAUGE_TUNER
88{
89 int id;
90 char *name;
91}
92hauppauge_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"},
Mauro Carvalho Chehab272435d2005-09-09 13:03:47 -0700155 { TUNER_PHILIPS_FM1216ME_MK3, "Philips FQ1216ME MK3"},
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156 { TUNER_ABSENT, "Philips FI1236 MK3"},
157 { TUNER_PHILIPS_FM1216ME_MK3, "Philips FM1216 ME MK3"},
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700158 { TUNER_PHILIPS_FM1236_MK3, "Philips FM1236 MK3"},
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159 { TUNER_ABSENT, "Philips FM1216MP MK3"},
160 /* 60-69 */
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700161 { TUNER_PHILIPS_FM1216ME_MK3, "LG S001D MK3"},
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162 { 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"},
Mauro Carvalho Chehab272435d2005-09-09 13:03:47 -0700170 { TUNER_LG_NTSC_TAPE, "LG TAPE H701F MK3"},
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171 /* 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"},
Mauro Carvalho Chehab793cf9e2005-09-09 13:03:37 -0700186 { TUNER_LG_PAL_NEW_TAPC, "TCL 2002MB 3"},
187 { TUNER_LG_PAL_NEW_TAPC, "TCL 2002MI 3"},
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188 { TUNER_TCL_2002N, "TCL 2002N 6A"},
189 { TUNER_ABSENT, "Philips FQ1236 MK3"},
190 { TUNER_ABSENT, "Samsung TCPN 2121P30A"},
191 { TUNER_ABSENT, "Samsung TCPE 4121P30A"},
Mauro Carvalho Chehabfd3113e2005-07-31 22:34:43 -0700192 { TUNER_PHILIPS_FM1216ME_MK3, "TCL MFPE05 2"},
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193 /* 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)"},
Mauro Carvalho Chehab272435d2005-09-09 13:03:47 -0700202 { TUNER_ABSENT, "Unspecified"},
203 { TUNER_TCL_2002N, "TCL 2002N 5H"},
204 /* 100-103 */
205 { TUNER_ABSENT, "Unspecified"},
206 { TUNER_ABSENT, "Unspecified"},
207 { TUNER_ABSENT, "Unspecified"},
208 { TUNER_PHILIPS_FM1236_MK3, "TCL MFNM05 4"},
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209};
210
211static char *sndtype[] = {
212 "None", "TEA6300", "TEA6320", "TDA9850", "MSP3400C", "MSP3410D",
213 "MSP3415", "MSP3430", "MSP3438", "CS5331", "MSP3435", "MSP3440",
214 "MSP3445", "MSP3411", "MSP3416", "MSP3425",
215
216 "Type 0x10","Type 0x11","Type 0x12","Type 0x13",
217 "Type 0x14","Type 0x15","Type 0x16","Type 0x17",
218 "Type 0x18","MSP4418","Type 0x1a","MSP4448",
219 "Type 0x1c","Type 0x1d","Type 0x1e","Type 0x1f",
220};
221
222static int hasRadioTuner(int tunerType)
223{
224 switch (tunerType) {
225 case 18: //PNPEnv_TUNER_FR1236_MK2:
226 case 23: //PNPEnv_TUNER_FM1236:
227 case 38: //PNPEnv_TUNER_FMR1236:
228 case 16: //PNPEnv_TUNER_FR1216_MK2:
229 case 19: //PNPEnv_TUNER_FR1246_MK2:
230 case 21: //PNPEnv_TUNER_FM1216:
231 case 24: //PNPEnv_TUNER_FM1246:
232 case 17: //PNPEnv_TUNER_FR1216MF_MK2:
233 case 22: //PNPEnv_TUNER_FM1216MF:
234 case 20: //PNPEnv_TUNER_FR1256_MK2:
235 case 25: //PNPEnv_TUNER_FM1256:
236 case 33: //PNPEnv_TUNER_4039FR5:
237 case 42: //PNPEnv_TUNER_4009FR5:
238 case 52: //PNPEnv_TUNER_4049FM5:
239 case 54: //PNPEnv_TUNER_4049FM5_AltI2C:
240 case 44: //PNPEnv_TUNER_4009FN5:
241 case 31: //PNPEnv_TUNER_TCPB9085P:
242 case 30: //PNPEnv_TUNER_TCPN9085D:
243 case 46: //PNPEnv_TUNER_TP18NSR01F:
244 case 47: //PNPEnv_TUNER_TP18PSB01D:
245 case 49: //PNPEnv_TUNER_TAPC_I001D:
246 case 60: //PNPEnv_TUNER_TAPE_S001D_MK3:
247 case 57: //PNPEnv_TUNER_FM1216ME_MK3:
248 case 59: //PNPEnv_TUNER_FM1216MP_MK3:
249 case 58: //PNPEnv_TUNER_FM1236_MK3:
250 case 68: //PNPEnv_TUNER_TAPE_H001F_MK3:
251 case 61: //PNPEnv_TUNER_TAPE_M001D_MK3:
252 case 78: //PNPEnv_TUNER_TDA8275C1_8290_FM:
253 case 89: //PNPEnv_TUNER_TCL_MFPE05_2:
254 case 92: //PNPEnv_TUNER_PHILIPS_FQ1236A_MK4:
255 return 1;
256 }
257 return 0;
258}
259
260void tveeprom_hauppauge_analog(struct tveeprom *tvee, unsigned char *eeprom_data)
261{
262 /* ----------------------------------------------
263 ** The hauppauge eeprom format is tagged
264 **
265 ** if packet[0] == 0x84, then packet[0..1] == length
266 ** else length = packet[0] & 3f;
267 ** if packet[0] & f8 == f8, then EOD and packet[1] == checksum
268 **
269 ** In our (ivtv) case we're interested in the following:
270 ** tuner type: tag [00].05 or [0a].01 (index into hauppauge_tuner)
271 ** tuner fmts: tag [00].04 or [0a].00 (bitmask index into hauppauge_tuner_fmt)
272 ** radio: tag [00].{last} or [0e].00 (bitmask. bit2=FM)
273 ** audio proc: tag [02].01 or [05].00 (lower nibble indexes lut?)
274
275 ** Fun info:
276 ** model: tag [00].07-08 or [06].00-01
277 ** revision: tag [00].09-0b or [06].04-06
278 ** serial#: tag [01].05-07 or [04].04-06
279
280 ** # of inputs/outputs ???
281 */
282
283 int i, j, len, done, beenhere, tag, tuner = 0, t_format = 0;
284 char *t_name = NULL, *t_fmt_name = NULL;
285
286 dprintk(1, "%s\n",__FUNCTION__);
287 tvee->revision = done = len = beenhere = 0;
288 for (i = 0; !done && i < 256; i += len) {
289 dprintk(2, "processing pos = %02x (%02x, %02x)\n",
290 i, eeprom_data[i], eeprom_data[i + 1]);
291
292 if (eeprom_data[i] == 0x84) {
293 len = eeprom_data[i + 1] + (eeprom_data[i + 2] << 8);
294 i+=3;
295 } else if ((eeprom_data[i] & 0xf0) == 0x70) {
296 if ((eeprom_data[i] & 0x08)) {
297 /* verify checksum! */
298 done = 1;
299 break;
300 }
301 len = eeprom_data[i] & 0x07;
302 ++i;
303 } else {
304 TVEEPROM_KERN_ERR("Encountered bad packet header [%02x]. "
305 "Corrupt or not a Hauppauge eeprom.\n", eeprom_data[i]);
306 return;
307 }
308
309 dprintk(1, "%3d [%02x] ", len, eeprom_data[i]);
310 for(j = 1; j < len; j++) {
311 dprintk(1, "%02x ", eeprom_data[i + j]);
312 }
313 dprintk(1, "\n");
314
315 /* process by tag */
316 tag = eeprom_data[i];
317 switch (tag) {
318 case 0x00:
319 tuner = eeprom_data[i+6];
320 t_format = eeprom_data[i+5];
321 tvee->has_radio = eeprom_data[i+len-1];
322 tvee->model =
323 eeprom_data[i+8] +
324 (eeprom_data[i+9] << 8);
325 tvee->revision = eeprom_data[i+10] +
326 (eeprom_data[i+11] << 8) +
327 (eeprom_data[i+12] << 16);
328 break;
329 case 0x01:
330 tvee->serial_number =
331 eeprom_data[i+6] +
332 (eeprom_data[i+7] << 8) +
333 (eeprom_data[i+8] << 16);
334 break;
335 case 0x02:
336 tvee->audio_processor = eeprom_data[i+2] & 0x0f;
337 break;
338 case 0x04:
339 tvee->serial_number =
340 eeprom_data[i+5] +
341 (eeprom_data[i+6] << 8) +
342 (eeprom_data[i+7] << 16);
343 break;
344 case 0x05:
345 tvee->audio_processor = eeprom_data[i+1] & 0x0f;
346 break;
347 case 0x06:
348 tvee->model =
349 eeprom_data[i+1] +
350 (eeprom_data[i+2] << 8);
351 tvee->revision = eeprom_data[i+5] +
352 (eeprom_data[i+6] << 8) +
353 (eeprom_data[i+7] << 16);
354 break;
355 case 0x0a:
356 if(beenhere == 0) {
357 tuner = eeprom_data[i+2];
358 t_format = eeprom_data[i+1];
359 beenhere = 1;
360 break;
361 } else {
362 break;
363 }
364 case 0x0e:
365 tvee->has_radio = eeprom_data[i+1];
366 break;
367 default:
368 dprintk(1, "Not sure what to do with tag [%02x]\n", tag);
369 /* dump the rest of the packet? */
370 }
371
372 }
373
374 if (!done) {
375 TVEEPROM_KERN_ERR("Ran out of data!\n");
376 return;
377 }
378
379 if (tvee->revision != 0) {
380 tvee->rev_str[0] = 32 + ((tvee->revision >> 18) & 0x3f);
381 tvee->rev_str[1] = 32 + ((tvee->revision >> 12) & 0x3f);
382 tvee->rev_str[2] = 32 + ((tvee->revision >> 6) & 0x3f);
383 tvee->rev_str[3] = 32 + ( tvee->revision & 0x3f);
384 tvee->rev_str[4] = 0;
385 }
386
387 if (hasRadioTuner(tuner) && !tvee->has_radio) {
388 TVEEPROM_KERN_INFO("The eeprom says no radio is present, but the tuner type\n");
389 TVEEPROM_KERN_INFO("indicates otherwise. I will assume that radio is present.\n");
390 tvee->has_radio = 1;
391 }
392
393 if (tuner < sizeof(hauppauge_tuner)/sizeof(struct HAUPPAUGE_TUNER)) {
394 tvee->tuner_type = hauppauge_tuner[tuner].id;
395 t_name = hauppauge_tuner[tuner].name;
396 } else {
397 t_name = "<unknown>";
398 }
399
400 tvee->tuner_formats = 0;
401 t_fmt_name = "<none>";
402 for (i = 0; i < 8; i++) {
403 if (t_format & (1<<i)) {
404 tvee->tuner_formats |= hauppauge_tuner_fmt[i].id;
405 /* yuck */
406 t_fmt_name = hauppauge_tuner_fmt[i].name;
407 }
408 }
409
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410
411 TVEEPROM_KERN_INFO("Hauppauge: model = %d, rev = %s, serial# = %d\n",
412 tvee->model,
413 tvee->rev_str,
414 tvee->serial_number);
415 TVEEPROM_KERN_INFO("tuner = %s (idx = %d, type = %d)\n",
416 t_name,
417 tuner,
418 tvee->tuner_type);
419 TVEEPROM_KERN_INFO("tuner fmt = %s (eeprom = 0x%02x, v4l2 = 0x%08x)\n",
420 t_fmt_name,
421 t_format,
422 tvee->tuner_formats);
423
424 TVEEPROM_KERN_INFO("audio_processor = %s (type = %d)\n",
425 STRM(sndtype,tvee->audio_processor),
426 tvee->audio_processor);
427
428}
429EXPORT_SYMBOL(tveeprom_hauppauge_analog);
430
431/* ----------------------------------------------------------------------- */
432/* generic helper functions */
433
434int tveeprom_read(struct i2c_client *c, unsigned char *eedata, int len)
435{
436 unsigned char buf;
437 int err;
438
439 dprintk(1, "%s\n",__FUNCTION__);
440 buf = 0;
441 if (1 != (err = i2c_master_send(c,&buf,1))) {
442 printk(KERN_INFO "tveeprom(%s): Huh, no eeprom present (err=%d)?\n",
443 c->name,err);
444 return -1;
445 }
446 if (len != (err = i2c_master_recv(c,eedata,len))) {
447 printk(KERN_WARNING "tveeprom(%s): i2c eeprom read error (err=%d)\n",
448 c->name,err);
449 return -1;
450 }
451 return 0;
452}
453EXPORT_SYMBOL(tveeprom_read);
454
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455
456/* ----------------------------------------------------------------------- */
457/* needed for ivtv.sf.net at the moment. Should go away in the long */
458/* run, just call the exported tveeprom_* directly, there is no point in */
459/* using the indirect way via i2c_driver->command() */
460
461#ifndef I2C_DRIVERID_TVEEPROM
462# define I2C_DRIVERID_TVEEPROM I2C_DRIVERID_EXP2
463#endif
464
465static unsigned short normal_i2c[] = {
466 0xa0 >> 1,
467 I2C_CLIENT_END,
468};
Mauro Carvalho Chehab833e9a12005-07-12 13:59:07 -0700469
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470I2C_CLIENT_INSMOD;
471
Adrian Bunk82ee3e62005-07-27 11:45:51 -0700472static struct i2c_driver i2c_driver_tveeprom;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473
474static int
475tveeprom_command(struct i2c_client *client,
476 unsigned int cmd,
477 void *arg)
478{
479 struct tveeprom eeprom;
480 u32 *eeprom_props = arg;
481 u8 *buf;
482
483 switch (cmd) {
484 case 0:
485 buf = kmalloc(256,GFP_KERNEL);
486 memset(buf,0,256);
487 tveeprom_read(client,buf,256);
488 tveeprom_hauppauge_analog(&eeprom,buf);
489 kfree(buf);
490 eeprom_props[0] = eeprom.tuner_type;
491 eeprom_props[1] = eeprom.tuner_formats;
492 eeprom_props[2] = eeprom.model;
493 eeprom_props[3] = eeprom.revision;
Mauro Carvalho Chehab272435d2005-09-09 13:03:47 -0700494 eeprom_props[4] = eeprom.has_radio;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495 break;
496 default:
497 return -EINVAL;
498 }
499 return 0;
500}
501
502static int
503tveeprom_detect_client(struct i2c_adapter *adapter,
504 int address,
505 int kind)
506{
507 struct i2c_client *client;
508
509 dprintk(1,"%s: id 0x%x @ 0x%x\n",__FUNCTION__,
510 adapter->id, address << 1);
511 client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
512 if (NULL == client)
513 return -ENOMEM;
514 memset(client, 0, sizeof(struct i2c_client));
515 client->addr = address;
516 client->adapter = adapter;
517 client->driver = &i2c_driver_tveeprom;
518 client->flags = I2C_CLIENT_ALLOW_USE;
519 snprintf(client->name, sizeof(client->name), "tveeprom");
520 i2c_attach_client(client);
521 return 0;
522}
523
524static int
525tveeprom_attach_adapter (struct i2c_adapter *adapter)
526{
527 dprintk(1,"%s: id 0x%x\n",__FUNCTION__,adapter->id);
Jean Delvarec7a46532005-08-11 23:41:56 +0200528 if (adapter->id != I2C_HW_B_BT848)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529 return 0;
530 return i2c_probe(adapter, &addr_data, tveeprom_detect_client);
531}
532
533static int
534tveeprom_detach_client (struct i2c_client *client)
535{
536 int err;
537
538 err = i2c_detach_client(client);
539 if (err < 0)
540 return err;
541 kfree(client);
542 return 0;
543}
544
Adrian Bunk82ee3e62005-07-27 11:45:51 -0700545static struct i2c_driver i2c_driver_tveeprom = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546 .owner = THIS_MODULE,
547 .name = "tveeprom",
548 .id = I2C_DRIVERID_TVEEPROM,
549 .flags = I2C_DF_NOTIFY,
550 .attach_adapter = tveeprom_attach_adapter,
551 .detach_client = tveeprom_detach_client,
552 .command = tveeprom_command,
553};
554
555static int __init tveeprom_init(void)
556{
557 return i2c_add_driver(&i2c_driver_tveeprom);
558}
559
560static void __exit tveeprom_exit(void)
561{
562 i2c_del_driver(&i2c_driver_tveeprom);
563}
564
565module_init(tveeprom_init);
566module_exit(tveeprom_exit);
567
568/*
569 * Local variables:
570 * c-basic-offset: 8
571 * End:
572 */