Commit 792bc09a authored by Mauro Carvalho Chehab's avatar Mauro Carvalho Chehab

[media] tm6000: add detection based on eeprom name

On some situations, it is desired to use eeprom data to detect
the board type. This patch adds a logic for it and fixes 2 detection
issues:
	1) 10Moons UT-821 uses a generic Trident ID. Other boards
also share the same ID. So, better to use an alternative way for
it;
	2) Sometimes, HVR-900H is loaded with the default Trident
ID. This seems to be some hardware bug or race condition.

The new logic will only be enabled if the device is detected as
having a generic ID.
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent dca6b74b
...@@ -54,6 +54,11 @@ ...@@ -54,6 +54,11 @@
#define TM6010_BOARD_BEHOLD_VOYAGER_LITE 15 #define TM6010_BOARD_BEHOLD_VOYAGER_LITE 15
#define TM5600_BOARD_TERRATEC_GRABSTER 16 #define TM5600_BOARD_TERRATEC_GRABSTER 16
#define is_generic(model) ((model == TM6000_BOARD_UNKNOWN) || \
(model == TM5600_BOARD_GENERIC) || \
(model == TM6000_BOARD_GENERIC) || \
(model == TM6010_BOARD_GENERIC))
#define TM6000_MAXBOARDS 16 #define TM6000_MAXBOARDS 16
static unsigned int card[] = {[0 ... (TM6000_MAXBOARDS - 1)] = UNSET }; static unsigned int card[] = {[0 ... (TM6000_MAXBOARDS - 1)] = UNSET };
...@@ -64,6 +69,9 @@ static unsigned long tm6000_devused; ...@@ -64,6 +69,9 @@ static unsigned long tm6000_devused;
struct tm6000_board { struct tm6000_board {
char *name; char *name;
char eename[16]; /* EEPROM name */
unsigned eename_size; /* size of EEPROM name */
unsigned eename_pos; /* Position where it appears at ROM */
struct tm6000_capabilities caps; struct tm6000_capabilities caps;
enum tm6000_inaudio aradio; enum tm6000_inaudio aradio;
...@@ -139,6 +147,9 @@ struct tm6000_board tm6000_boards[] = { ...@@ -139,6 +147,9 @@ struct tm6000_board tm6000_boards[] = {
[TM5600_BOARD_10MOONS_UT821] = { [TM5600_BOARD_10MOONS_UT821] = {
.name = "10Moons UT 821", .name = "10Moons UT 821",
.tuner_type = TUNER_XC2028, .tuner_type = TUNER_XC2028,
.eename = { '1', '0', 'M', 'O', 'O', 'N', 'S', '5', '6', '0', '0', 0xff, 0x45, 0x5b},
.eename_size = 14,
.eename_pos = 0x14,
.type = TM5600, .type = TM5600,
.tuner_addr = 0xc2 >> 1, .tuner_addr = 0xc2 >> 1,
.caps = { .caps = {
...@@ -205,6 +216,9 @@ struct tm6000_board tm6000_boards[] = { ...@@ -205,6 +216,9 @@ struct tm6000_board tm6000_boards[] = {
}, },
[TM6010_BOARD_HAUPPAUGE_900H] = { [TM6010_BOARD_HAUPPAUGE_900H] = {
.name = "Hauppauge WinTV HVR-900H / WinTV USB2-Stick", .name = "Hauppauge WinTV HVR-900H / WinTV USB2-Stick",
.eename = { 'H', 0, 'V', 0, 'R', 0, '9', 0, '0', 0, '0', 0, 'H', 0 },
.eename_size = 14,
.eename_pos = 0x42,
.tuner_type = TUNER_XC2028, /* has a XC3028 */ .tuner_type = TUNER_XC2028, /* has a XC3028 */
.tuner_addr = 0xc2 >> 1, .tuner_addr = 0xc2 >> 1,
.demod_addr = 0x1e >> 1, .demod_addr = 0x1e >> 1,
...@@ -370,7 +384,7 @@ struct tm6000_board tm6000_boards[] = { ...@@ -370,7 +384,7 @@ struct tm6000_board tm6000_boards[] = {
/* table of devices that work with this driver */ /* table of devices that work with this driver */
struct usb_device_id tm6000_id_table[] = { struct usb_device_id tm6000_id_table[] = {
{ USB_DEVICE(0x6000, 0x0001), .driver_info = TM5600_BOARD_10MOONS_UT821 }, { USB_DEVICE(0x6000, 0x0001), .driver_info = TM5600_BOARD_GENERIC },
{ USB_DEVICE(0x6000, 0x0002), .driver_info = TM6010_BOARD_GENERIC }, { USB_DEVICE(0x6000, 0x0002), .driver_info = TM6010_BOARD_GENERIC },
{ USB_DEVICE(0x06e1, 0xf332), .driver_info = TM6000_BOARD_ADSTECH_DUAL_TV }, { USB_DEVICE(0x06e1, 0xf332), .driver_info = TM6000_BOARD_ADSTECH_DUAL_TV },
{ USB_DEVICE(0x14aa, 0x0620), .driver_info = TM6000_BOARD_FREECOM_AND_SIMILAR }, { USB_DEVICE(0x14aa, 0x0620), .driver_info = TM6000_BOARD_FREECOM_AND_SIMILAR },
...@@ -729,16 +743,10 @@ static void tm6000_config_tuner(struct tm6000_core *dev) ...@@ -729,16 +743,10 @@ static void tm6000_config_tuner(struct tm6000_core *dev)
} }
} }
static int tm6000_init_dev(struct tm6000_core *dev) static int fill_board_specific_data(struct tm6000_core *dev)
{ {
struct v4l2_frequency f; int rc;
int rc = 0;
mutex_init(&dev->lock);
mutex_lock(&dev->lock);
/* Initializa board-specific data */
dev->dev_type = tm6000_boards[dev->model].type; dev->dev_type = tm6000_boards[dev->model].type;
dev->tuner_type = tm6000_boards[dev->model].tuner_type; dev->tuner_type = tm6000_boards[dev->model].tuner_type;
dev->tuner_addr = tm6000_boards[dev->model].tuner_addr; dev->tuner_addr = tm6000_boards[dev->model].tuner_addr;
...@@ -756,9 +764,61 @@ static int tm6000_init_dev(struct tm6000_core *dev) ...@@ -756,9 +764,61 @@ static int tm6000_init_dev(struct tm6000_core *dev)
/* initialize hardware */ /* initialize hardware */
rc = tm6000_init(dev); rc = tm6000_init(dev);
if (rc < 0) if (rc < 0)
goto err; return rc;
rc = v4l2_device_register(&dev->udev->dev, &dev->v4l2_dev); rc = v4l2_device_register(&dev->udev->dev, &dev->v4l2_dev);
if (rc < 0)
return rc;
/* initialize hardware */
rc = tm6000_init(dev);
return rc;
}
static void use_alternative_detection_method(struct tm6000_core *dev)
{
int i, model = -1;
if (!dev->eedata_size)
return;
for (i = 0; i < ARRAY_SIZE(tm6000_boards); i++) {
if (!tm6000_boards[i].eename_size)
continue;
if (dev->eedata_size < tm6000_boards[i].eename_pos +
tm6000_boards[i].eename_size)
continue;
if (!memcmp(&dev->eedata[tm6000_boards[i].eename_pos],
tm6000_boards[i].eename,
tm6000_boards[i].eename_size)) {
model = i;
break;
}
}
if (model < 0) {
printk(KERN_INFO "Device has eeprom but is currently unknown\n");
return;
}
dev->model = model;
printk(KERN_INFO "Device identified via eeprom as %s (type = %d)\n",
tm6000_boards[model].name, model);
}
static int tm6000_init_dev(struct tm6000_core *dev)
{
struct v4l2_frequency f;
int rc = 0;
mutex_init(&dev->lock);
mutex_lock(&dev->lock);
if (!is_generic(dev->model)) {
rc = fill_board_specific_data(dev);
if (rc < 0) if (rc < 0)
goto err; goto err;
...@@ -766,6 +826,18 @@ static int tm6000_init_dev(struct tm6000_core *dev) ...@@ -766,6 +826,18 @@ static int tm6000_init_dev(struct tm6000_core *dev)
rc = tm6000_i2c_register(dev); rc = tm6000_i2c_register(dev);
if (rc < 0) if (rc < 0)
goto err; goto err;
} else {
/* register i2c bus */
rc = tm6000_i2c_register(dev);
if (rc < 0)
goto err;
use_alternative_detection_method(dev);
rc = fill_board_specific_data(dev);
if (rc < 0)
goto err;
}
/* Default values for STD and resolutions */ /* Default values for STD and resolutions */
dev->width = 720; dev->width = 720;
......
...@@ -237,35 +237,36 @@ static int tm6000_i2c_xfer(struct i2c_adapter *i2c_adap, ...@@ -237,35 +237,36 @@ static int tm6000_i2c_xfer(struct i2c_adapter *i2c_adap,
return rc; return rc;
} }
static int tm6000_i2c_eeprom(struct tm6000_core *dev, static int tm6000_i2c_eeprom(struct tm6000_core *dev)
unsigned char *eedata, int len)
{ {
int i, rc; int i, rc;
unsigned char *p = eedata; unsigned char *p = dev->eedata;
unsigned char bytes[17]; unsigned char bytes[17];
dev->i2c_client.addr = 0xa0 >> 1; dev->i2c_client.addr = 0xa0 >> 1;
dev->eedata_size = 0;
bytes[16] = '\0'; bytes[16] = '\0';
for (i = 0; i < len; ) { for (i = 0; i < sizeof(dev->eedata); ) {
*p = i; *p = i;
rc = tm6000_i2c_recv_regs(dev, 0xa0, i, p, 1); rc = tm6000_i2c_recv_regs(dev, 0xa0, i, p, 1);
if (rc < 1) { if (rc < 1) {
if (p == eedata) if (p == dev->eedata)
goto noeeprom; goto noeeprom;
else { else {
printk(KERN_WARNING printk(KERN_WARNING
"%s: i2c eeprom read error (err=%d)\n", "%s: i2c eeprom read error (err=%d)\n",
dev->name, rc); dev->name, rc);
} }
return -1; return -EINVAL;
} }
dev->eedata_size++;
p++; p++;
if (0 == (i % 16)) if (0 == (i % 16))
printk(KERN_INFO "%s: i2c eeprom %02x:", dev->name, i); printk(KERN_INFO "%s: i2c eeprom %02x:", dev->name, i);
printk(" %02x", eedata[i]); printk(" %02x", dev->eedata[i]);
if ((eedata[i] >= ' ') && (eedata[i] <= 'z')) if ((dev->eedata[i] >= ' ') && (dev->eedata[i] <= 'z'))
bytes[i%16] = eedata[i]; bytes[i%16] = dev->eedata[i];
else else
bytes[i%16] = '.'; bytes[i%16] = '.';
...@@ -280,15 +281,15 @@ static int tm6000_i2c_eeprom(struct tm6000_core *dev, ...@@ -280,15 +281,15 @@ static int tm6000_i2c_eeprom(struct tm6000_core *dev,
bytes[i%16] = '\0'; bytes[i%16] = '\0';
for (i %= 16; i < 16; i++) for (i %= 16; i < 16; i++)
printk(" "); printk(" ");
}
printk(" %s\n", bytes); printk(" %s\n", bytes);
}
return 0; return 0;
noeeprom: noeeprom:
printk(KERN_INFO "%s: Huh, no eeprom present (err=%d)?\n", printk(KERN_INFO "%s: Huh, no eeprom present (err=%d)?\n",
dev->name, rc); dev->name, rc);
return rc; return -EINVAL;
} }
/* ----------------------------------------------------------- */ /* ----------------------------------------------------------- */
...@@ -314,7 +315,6 @@ static const struct i2c_algorithm tm6000_algo = { ...@@ -314,7 +315,6 @@ static const struct i2c_algorithm tm6000_algo = {
*/ */
int tm6000_i2c_register(struct tm6000_core *dev) int tm6000_i2c_register(struct tm6000_core *dev)
{ {
unsigned char eedata[256];
int rc; int rc;
dev->i2c_adap.owner = THIS_MODULE; dev->i2c_adap.owner = THIS_MODULE;
...@@ -329,8 +329,7 @@ int tm6000_i2c_register(struct tm6000_core *dev) ...@@ -329,8 +329,7 @@ int tm6000_i2c_register(struct tm6000_core *dev)
dev->i2c_client.adapter = &dev->i2c_adap; dev->i2c_client.adapter = &dev->i2c_adap;
strlcpy(dev->i2c_client.name, "tm6000 internal", I2C_NAME_SIZE); strlcpy(dev->i2c_client.name, "tm6000 internal", I2C_NAME_SIZE);
tm6000_i2c_eeprom(dev);
tm6000_i2c_eeprom(dev, eedata, sizeof(eedata));
return 0; return 0;
} }
......
...@@ -167,6 +167,8 @@ struct tm6000_core { ...@@ -167,6 +167,8 @@ struct tm6000_core {
int model; /* index in the device_data struct */ int model; /* index in the device_data struct */
int devno; /* marks the number of this device */ int devno; /* marks the number of this device */
enum tm6000_devtype dev_type; /* type of device */ enum tm6000_devtype dev_type; /* type of device */
unsigned char eedata[256]; /* Eeprom data */
unsigned eedata_size; /* Size of the eeprom info */
v4l2_std_id norm; /* Current norm */ v4l2_std_id norm; /* Current norm */
int width, height; /* Selected resolution */ int width, height; /* Selected resolution */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment