diff options
author | Mauro Carvalho Chehab <mchehab@infradead.org> | 2007-11-03 20:20:59 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@infradead.org> | 2008-01-25 16:01:55 -0500 |
commit | 03910cc39035d27f4c85c8ad2a236cc5c9456127 (patch) | |
tree | 04c852f7f52e9568cbc68cd6c489cc4d003958a9 /drivers/media/video/em28xx/em28xx-cards.c | |
parent | 3dbd85ba36ff74af87550e76f5765a768b108409 (diff) |
V4L/DVB (6536): Add a hint for boards without unique USB ID
This patch adds a function to allow trying to detect boards that shares
the generic IDs.
The current detection method is based at eeprom checksum.
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Diffstat (limited to 'drivers/media/video/em28xx/em28xx-cards.c')
-rw-r--r-- | drivers/media/video/em28xx/em28xx-cards.c | 198 |
1 files changed, 158 insertions, 40 deletions
diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 05264c655b5e..4cd49415eef2 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c | |||
@@ -37,6 +37,16 @@ | |||
37 | #include "em28xx.h" | 37 | #include "em28xx.h" |
38 | #include "tuner-xc2028.h" | 38 | #include "tuner-xc2028.h" |
39 | 39 | ||
40 | static int tuner = -1; | ||
41 | module_param(tuner, int, 0444); | ||
42 | MODULE_PARM_DESC(tuner, "tuner type"); | ||
43 | |||
44 | struct em28xx_hash_table { | ||
45 | unsigned long hash; | ||
46 | unsigned int model; | ||
47 | unsigned int tuner; | ||
48 | }; | ||
49 | |||
40 | struct em28xx_board em28xx_boards[] = { | 50 | struct em28xx_board em28xx_boards[] = { |
41 | [EM2800_BOARD_UNKNOWN] = { | 51 | [EM2800_BOARD_UNKNOWN] = { |
42 | .name = "Unknown EM2800 video grabber", | 52 | .name = "Unknown EM2800 video grabber", |
@@ -342,70 +352,178 @@ struct usb_device_id em28xx_id_table [] = { | |||
342 | { USB_DEVICE(0x0ccd, 0x0047), .driver_info = EM2880_BOARD_TERRATEC_PRODIGY_XS }, | 352 | { USB_DEVICE(0x0ccd, 0x0047), .driver_info = EM2880_BOARD_TERRATEC_PRODIGY_XS }, |
343 | { }, | 353 | { }, |
344 | }; | 354 | }; |
355 | MODULE_DEVICE_TABLE (usb, em28xx_id_table); | ||
356 | |||
357 | static struct em28xx_hash_table em28xx_hash [] = { | ||
358 | { 0, 0, 0 }, | ||
359 | }; | ||
345 | 360 | ||
361 | /* Since em28xx_pre_card_setup() requires a proper dev->model, | ||
362 | * this won't work for boards with generic PCI IDs | ||
363 | */ | ||
346 | void em28xx_pre_card_setup(struct em28xx *dev) | 364 | void em28xx_pre_card_setup(struct em28xx *dev) |
347 | { | 365 | { |
348 | /* request some modules */ | 366 | /* request some modules */ |
349 | switch(dev->model){ | 367 | switch(dev->model){ |
350 | case EM2880_BOARD_TERRATEC_PRODIGY_XS: | 368 | case EM2880_BOARD_TERRATEC_PRODIGY_XS: |
351 | case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: | 369 | case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: |
352 | case EM2880_BOARD_TERRATEC_HYBRID_XS: | 370 | case EM2880_BOARD_TERRATEC_HYBRID_XS: |
353 | { | 371 | /* reset through GPIO? */ |
354 | em28xx_write_regs_req(dev, 0x00, 0x08, "\x7d", 1); // reset through GPIO? | 372 | em28xx_write_regs_req(dev, 0x00, 0x08, "\x7d", 1); |
355 | break; | 373 | break; |
356 | } | 374 | } |
375 | } | ||
376 | |||
377 | static int em28xx_tuner_callback(void *ptr, int command, int arg) | ||
378 | { | ||
379 | int rc = 0; | ||
380 | struct em28xx *dev = ptr; | ||
381 | |||
382 | if (dev->tuner_type != TUNER_XC2028) | ||
383 | return 0; | ||
384 | |||
385 | switch (command) { | ||
386 | case XC2028_TUNER_RESET: | ||
387 | /* FIXME: This is device-dependent */ | ||
388 | dev->em28xx_write_regs_req(dev, 0x00, 0x48, "\x00", 1); | ||
389 | dev->em28xx_write_regs_req(dev, 0x00, 0x12, "\x67", 1); | ||
390 | |||
391 | msleep(140); | ||
392 | break; | ||
357 | } | 393 | } |
394 | return rc; | ||
358 | } | 395 | } |
359 | 396 | ||
360 | static void em28xx_config_tuner (struct em28xx *dev) | 397 | static void em28xx_config_tuner (struct em28xx *dev) |
361 | { | 398 | { |
362 | struct v4l2_priv_tun_config xc2028_cfg; | 399 | struct v4l2_priv_tun_config xc2028_cfg; |
363 | struct xc2028_ctrl ctl; | 400 | struct xc2028_ctrl ctl; |
401 | struct tuner_setup tun_setup; | ||
402 | struct v4l2_frequency f; | ||
403 | |||
404 | if (!dev->has_tuner) | ||
405 | return; | ||
406 | |||
407 | tun_setup.mode_mask = T_ANALOG_TV | T_RADIO; | ||
408 | tun_setup.type = dev->tuner_type; | ||
409 | tun_setup.addr = dev->tuner_addr; | ||
410 | tun_setup.tuner_callback = em28xx_tuner_callback; | ||
411 | |||
412 | em28xx_i2c_call_clients(dev, TUNER_SET_TYPE_ADDR, &tun_setup); | ||
413 | |||
414 | if (dev->tuner_type == TUNER_XC2028) { | ||
415 | memset (&ctl, 0, sizeof(ctl)); | ||
416 | |||
417 | ctl.fname = XC2028_DEFAULT_FIRMWARE; | ||
418 | ctl.max_len = 64; | ||
419 | |||
420 | xc2028_cfg.tuner = TUNER_XC2028; | ||
421 | xc2028_cfg.priv = &ctl; | ||
364 | 422 | ||
365 | memset (&ctl,0,sizeof(ctl)); | 423 | em28xx_i2c_call_clients(dev, TUNER_SET_CONFIG, &xc2028_cfg); |
424 | } | ||
425 | |||
426 | /* configure tuner */ | ||
427 | f.tuner = 0; | ||
428 | f.type = V4L2_TUNER_ANALOG_TV; | ||
429 | f.frequency = 9076; /* just a magic number */ | ||
430 | dev->ctl_freq = f.frequency; | ||
431 | em28xx_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, &f); | ||
432 | } | ||
433 | |||
434 | static int em28xx_hint_board(struct em28xx *dev) | ||
435 | { | ||
436 | int i; | ||
366 | 437 | ||
367 | ctl.fname = XC2028_DEFAULT_FIRMWARE; | 438 | for (i = 0; i < ARRAY_SIZE(em28xx_hash); i++) { |
368 | ctl.max_len = 64; | 439 | if (dev->hash == em28xx_hash[i].hash) { |
440 | dev->model = em28xx_hash[i].model; | ||
441 | dev->tuner_type = em28xx_hash[i].tuner; | ||
369 | 442 | ||
370 | xc2028_cfg.tuner = TUNER_XC2028; | 443 | em28xx_errdev("Your board has no unique USB ID.\n"); |
371 | xc2028_cfg.priv = &ctl; | 444 | em28xx_errdev("A hint were successfully done, " |
445 | "based on eeprom hash.\n"); | ||
446 | em28xx_errdev("This method is not 100%% failproof.\n"); | ||
447 | em28xx_errdev("If the board were missdetected, " | ||
448 | "please email this log to:\n"); | ||
449 | em28xx_errdev("\tV4L Mailing List " | ||
450 | " <video4linux-list@redhat.com>\n"); | ||
451 | em28xx_errdev("Board detected as %s\n", | ||
452 | em28xx_boards[dev->model].name); | ||
372 | 453 | ||
373 | em28xx_i2c_call_clients(dev, TUNER_SET_CONFIG, &xc2028_cfg); | 454 | return 0; |
455 | } | ||
456 | } | ||
457 | em28xx_errdev("Your board has no unique USB ID and thus need a " | ||
458 | "hint to be detected.\n"); | ||
459 | em28xx_errdev("You may try to use card=<n> insmod option to " | ||
460 | "workaround that.\n"); | ||
461 | em28xx_errdev("Please send an email with this log to:\n"); | ||
462 | em28xx_errdev("\tV4L Mailing List <video4linux-list@redhat.com>\n"); | ||
463 | em28xx_errdev("Board eeprom hash is 0x%08lx\n", dev->hash); | ||
464 | |||
465 | em28xx_errdev("Here is a list of valid choices for the card=<n>" | ||
466 | " insmod option:\n"); | ||
467 | for (i = 0; i < em28xx_bcount; i++) { | ||
468 | em28xx_errdev(" card=%d -> %s\n", | ||
469 | i, em28xx_boards[i].name); | ||
470 | } | ||
471 | return -1; | ||
374 | } | 472 | } |
375 | 473 | ||
376 | void em28xx_card_setup(struct em28xx *dev) | 474 | void em28xx_card_setup(struct em28xx *dev) |
377 | { | 475 | { |
378 | /* request some modules */ | 476 | /* request some modules */ |
379 | switch(dev->model){ | 477 | switch (dev->model) { |
380 | case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2: | 478 | case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2: |
381 | { | 479 | { |
382 | struct tveeprom tv; | 480 | struct tveeprom tv; |
383 | #ifdef CONFIG_MODULES | 481 | #ifdef CONFIG_MODULES |
384 | request_module("tveeprom"); | 482 | request_module("tveeprom"); |
385 | request_module("ir-kbd-i2c"); | 483 | request_module("ir-kbd-i2c"); |
386 | request_module("msp3400"); | ||
387 | #endif | 484 | #endif |
388 | /* Call first TVeeprom */ | 485 | /* Call first TVeeprom */ |
389 | 486 | ||
390 | dev->i2c_client.addr = 0xa0 >> 1; | 487 | dev->i2c_client.addr = 0xa0 >> 1; |
391 | tveeprom_hauppauge_analog(&dev->i2c_client, &tv, dev->eedata); | 488 | tveeprom_hauppauge_analog(&dev->i2c_client, &tv, dev->eedata); |
392 | |||
393 | dev->tuner_type= tv.tuner_type; | ||
394 | if (tv.audio_processor == AUDIO_CHIP_MSP34XX) { | ||
395 | dev->i2s_speed=2048000; | ||
396 | dev->has_msp34xx=1; | ||
397 | } else | ||
398 | dev->has_msp34xx=0; | ||
399 | break; | ||
400 | } | ||
401 | case EM2820_BOARD_KWORLD_PVRTV2800RF: | ||
402 | { | ||
403 | em28xx_write_regs_req(dev,0x00,0x08, "\xf9", 1); // GPIO enables sound on KWORLD PVR TV 2800RF | ||
404 | break; | ||
405 | } | ||
406 | 489 | ||
490 | dev->tuner_type = tv.tuner_type; | ||
491 | if (tv.audio_processor == AUDIO_CHIP_MSP34XX) { | ||
492 | dev->i2s_speed = 2048000; | ||
493 | dev->has_msp34xx = 1; | ||
494 | } | ||
495 | break; | ||
407 | } | 496 | } |
497 | case EM2820_BOARD_KWORLD_PVRTV2800RF: | ||
498 | /* GPIO enables sound on KWORLD PVR TV 2800RF */ | ||
499 | em28xx_write_regs_req(dev, 0x00, 0x08, "\xf9", 1); | ||
500 | break; | ||
501 | case EM2820_BOARD_UNKNOWN: | ||
502 | case EM2800_BOARD_UNKNOWN: | ||
503 | em28xx_hint_board(dev); | ||
504 | } | ||
505 | |||
506 | dev->is_em2800 = em28xx_boards[dev->model].is_em2800; | ||
507 | dev->has_tuner = em28xx_boards[dev->model].has_tuner; | ||
508 | dev->has_msp34xx = em28xx_boards[dev->model].has_msp34xx; | ||
509 | dev->tda9887_conf = em28xx_boards[dev->model].tda9887_conf; | ||
510 | dev->decoder = em28xx_boards[dev->model].decoder; | ||
511 | dev->video_inputs = em28xx_boards[dev->model].vchannels; | ||
512 | |||
513 | if (tuner >= 0) | ||
514 | dev->tuner_type = tuner; | ||
515 | |||
516 | #ifdef CONFIG_MODULES | ||
517 | /* request some modules */ | ||
518 | if (dev->has_msp34xx) | ||
519 | request_module("msp3400"); | ||
520 | if (dev->decoder == EM28XX_SAA7113 || dev->decoder == EM28XX_SAA7114) | ||
521 | request_module("saa7115"); | ||
522 | if (dev->decoder == EM28XX_TVP5150) | ||
523 | request_module("tvp5150"); | ||
524 | if (dev->has_tuner) | ||
525 | request_module("tuner"); | ||
526 | #endif | ||
527 | |||
408 | em28xx_config_tuner (dev); | 528 | em28xx_config_tuner (dev); |
409 | } | 529 | } |
410 | |||
411 | MODULE_DEVICE_TABLE (usb, em28xx_id_table); | ||