[media] em28xx: extract the device configuration dataset from eeproms with 16 bit address width
The new eeproms with 16 address width still have the the device config dataset (the content of the old 8 bit eeproms) embedded. Hauppauge also continues to include the tveeprom data structure inside this dataset in their devices. The start address of the dataset depends on the start address of the microcode and a variable additional offset. It should be mentioned that Camera devices seem to use a different dataset type, which is not yet supported. Tested with devices "Hauppauge HVR-930C". I've also checked the USB-log from the "MSI Digivox ATSC" and it works the same way. Signed-off-by: Frank Schäfer <fschaefer.oss@googlemail.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
Родитель
a217968f91
Коммит
510e884c1a
|
@ -409,13 +409,18 @@ static int em28xx_i2c_read_block(struct em28xx *dev, u16 addr, bool addr_w16,
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char **eedata, int len)
|
static int em28xx_i2c_eeprom(struct em28xx *dev, u8 **eedata, u16 *eedata_len)
|
||||||
{
|
{
|
||||||
u8 buf, *data;
|
const u16 len = 256;
|
||||||
struct em28xx_eeprom *em_eeprom;
|
/* FIXME common length/size for bytes to read, to display, hash
|
||||||
|
* calculation and returned device dataset. Simplifies the code a lot,
|
||||||
|
* but we might have to deal with multiple sizes in the future ! */
|
||||||
int i, err;
|
int i, err;
|
||||||
|
struct em28xx_eeprom *dev_config;
|
||||||
|
u8 buf, *data;
|
||||||
|
|
||||||
*eedata = NULL;
|
*eedata = NULL;
|
||||||
|
*eedata_len = 0;
|
||||||
|
|
||||||
dev->i2c_client.addr = 0xa0 >> 1;
|
dev->i2c_client.addr = 0xa0 >> 1;
|
||||||
|
|
||||||
|
@ -435,8 +440,7 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char **eedata, int len
|
||||||
len, data);
|
len, data);
|
||||||
if (err != len) {
|
if (err != len) {
|
||||||
em28xx_errdev("failed to read eeprom (err=%d)\n", err);
|
em28xx_errdev("failed to read eeprom (err=%d)\n", err);
|
||||||
kfree(data);
|
goto error;
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Display eeprom content */
|
/* Display eeprom content */
|
||||||
|
@ -451,15 +455,25 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char **eedata, int len
|
||||||
if (15 == (i % 16))
|
if (15 == (i % 16))
|
||||||
printk("\n");
|
printk("\n");
|
||||||
}
|
}
|
||||||
|
if (dev->eeprom_addrwidth_16bit)
|
||||||
|
em28xx_info("i2c eeprom %04x: ... (skipped)\n", i);
|
||||||
|
|
||||||
if (dev->eeprom_addrwidth_16bit &&
|
if (dev->eeprom_addrwidth_16bit &&
|
||||||
data[0] == 0x26 && data[3] == 0x00) {
|
data[0] == 0x26 && data[3] == 0x00) {
|
||||||
/* new eeprom format; size 4-64kb */
|
/* new eeprom format; size 4-64kb */
|
||||||
|
u16 mc_start;
|
||||||
|
u16 hwconf_offset;
|
||||||
|
|
||||||
dev->hash = em28xx_hash_mem(data, len, 32);
|
dev->hash = em28xx_hash_mem(data, len, 32);
|
||||||
em28xx_info("EEPROM hash = 0x%08lx\n", dev->hash);
|
mc_start = (data[1] << 8) + 4; /* usually 0x0004 */
|
||||||
em28xx_info("EEPROM info: boot page address = 0x%02x04, "
|
|
||||||
|
em28xx_info("EEPROM ID = %02x %02x %02x %02x, "
|
||||||
|
"EEPROM hash = 0x%08lx\n",
|
||||||
|
data[0], data[1], data[2], data[3], dev->hash);
|
||||||
|
em28xx_info("EEPROM info:\n");
|
||||||
|
em28xx_info("\tmicrocode start address = 0x%04x, "
|
||||||
"boot configuration = 0x%02x\n",
|
"boot configuration = 0x%02x\n",
|
||||||
data[1], data[2]);
|
mc_start, data[2]);
|
||||||
/* boot configuration (address 0x0002):
|
/* boot configuration (address 0x0002):
|
||||||
* [0] microcode download speed: 1 = 400 kHz; 0 = 100 kHz
|
* [0] microcode download speed: 1 = 400 kHz; 0 = 100 kHz
|
||||||
* [1] always selects 12 kb RAM
|
* [1] always selects 12 kb RAM
|
||||||
|
@ -469,32 +483,61 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char **eedata, int len
|
||||||
* characterization
|
* characterization
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* FIXME:
|
/* Read hardware config dataset offset from address
|
||||||
* - read more than 256 bytes / addresses above 0x00ff
|
* (microcode start + 46) */
|
||||||
* - find offset for device config dataset and extract it
|
err = em28xx_i2c_read_block(dev, mc_start + 46, 1, 2, data);
|
||||||
* - decrypt eeprom data for camera bridges (em25xx, em276x+)
|
if (err != 2) {
|
||||||
* - use separate/different eeprom hashes (not yet used)
|
em28xx_errdev("failed to read hardware configuration data from eeprom (err=%d)\n",
|
||||||
*/
|
err);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
/* Calculate hardware config dataset start address */
|
||||||
} else if (data[0] != 0x1a || data[1] != 0xeb ||
|
hwconf_offset = mc_start + data[0] + (data[1] << 8);
|
||||||
data[2] != 0x67 || data[3] != 0x95) {
|
|
||||||
|
/* Read hardware config dataset */
|
||||||
|
/* NOTE: the microcode copy can be multiple pages long, but
|
||||||
|
* we assume the hardware config dataset is the same as in
|
||||||
|
* the old eeprom and not longer than 256 bytes.
|
||||||
|
* tveeprom is currently also limited to 256 bytes.
|
||||||
|
*/
|
||||||
|
err = em28xx_i2c_read_block(dev, hwconf_offset, 1, len, data);
|
||||||
|
if (err != len) {
|
||||||
|
em28xx_errdev("failed to read hardware configuration data from eeprom (err=%d)\n",
|
||||||
|
err);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Verify hardware config dataset */
|
||||||
|
/* NOTE: not all devices provide this type of dataset */
|
||||||
|
if (data[0] != 0x1a || data[1] != 0xeb ||
|
||||||
|
data[2] != 0x67 || data[3] != 0x95) {
|
||||||
|
em28xx_info("\tno hardware configuration dataset found in eeprom\n");
|
||||||
|
kfree(data);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: decrypt eeprom data for camera bridges (em25xx, em276x+) */
|
||||||
|
|
||||||
|
} else if (!dev->eeprom_addrwidth_16bit &&
|
||||||
|
data[0] == 0x1a && data[1] == 0xeb &&
|
||||||
|
data[2] == 0x67 && data[3] == 0x95) {
|
||||||
|
dev->hash = em28xx_hash_mem(data, len, 32);
|
||||||
|
em28xx_info("EEPROM ID = %02x %02x %02x %02x, "
|
||||||
|
"EEPROM hash = 0x%08lx\n",
|
||||||
|
data[0], data[1], data[2], data[3], dev->hash);
|
||||||
|
em28xx_info("EEPROM info:\n");
|
||||||
|
} else {
|
||||||
em28xx_info("unknown eeprom format or eeprom corrupted !\n");
|
em28xx_info("unknown eeprom format or eeprom corrupted !\n");
|
||||||
return -ENODEV;
|
err = -ENODEV;
|
||||||
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
*eedata = data;
|
*eedata = data;
|
||||||
em_eeprom = (void *)eedata;
|
*eedata_len = len;
|
||||||
|
dev_config = (void *)eedata;
|
||||||
|
|
||||||
dev->hash = em28xx_hash_mem(data, len, 32);
|
switch (le16_to_cpu(dev_config->chip_conf) >> 4 & 0x3) {
|
||||||
|
|
||||||
em28xx_info("EEPROM ID = %02x %02x %02x %02x, EEPROM hash = 0x%08lx\n",
|
|
||||||
em_eeprom->id[0], em_eeprom->id[1],
|
|
||||||
em_eeprom->id[2], em_eeprom->id[3], dev->hash);
|
|
||||||
|
|
||||||
em28xx_info("EEPROM info:\n");
|
|
||||||
|
|
||||||
switch (le16_to_cpu(em_eeprom->chip_conf) >> 4 & 0x3) {
|
|
||||||
case 0:
|
case 0:
|
||||||
em28xx_info("\tNo audio on board.\n");
|
em28xx_info("\tNo audio on board.\n");
|
||||||
break;
|
break;
|
||||||
|
@ -509,13 +552,13 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char **eedata, int len
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (le16_to_cpu(em_eeprom->chip_conf) & 1 << 3)
|
if (le16_to_cpu(dev_config->chip_conf) & 1 << 3)
|
||||||
em28xx_info("\tUSB Remote wakeup capable\n");
|
em28xx_info("\tUSB Remote wakeup capable\n");
|
||||||
|
|
||||||
if (le16_to_cpu(em_eeprom->chip_conf) & 1 << 2)
|
if (le16_to_cpu(dev_config->chip_conf) & 1 << 2)
|
||||||
em28xx_info("\tUSB Self power capable\n");
|
em28xx_info("\tUSB Self power capable\n");
|
||||||
|
|
||||||
switch (le16_to_cpu(em_eeprom->chip_conf) & 0x3) {
|
switch (le16_to_cpu(dev_config->chip_conf) & 0x3) {
|
||||||
case 0:
|
case 0:
|
||||||
em28xx_info("\t500mA max power\n");
|
em28xx_info("\t500mA max power\n");
|
||||||
break;
|
break;
|
||||||
|
@ -530,12 +573,16 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char **eedata, int len
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
em28xx_info("\tTable at offset 0x%02x, strings=0x%04x, 0x%04x, 0x%04x\n",
|
em28xx_info("\tTable at offset 0x%02x, strings=0x%04x, 0x%04x, 0x%04x\n",
|
||||||
em_eeprom->string_idx_table,
|
dev_config->string_idx_table,
|
||||||
le16_to_cpu(em_eeprom->string1),
|
le16_to_cpu(dev_config->string1),
|
||||||
le16_to_cpu(em_eeprom->string2),
|
le16_to_cpu(dev_config->string2),
|
||||||
le16_to_cpu(em_eeprom->string3));
|
le16_to_cpu(dev_config->string3));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
error:
|
||||||
|
kfree(data);
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------------------------------------------------- */
|
/* ----------------------------------------------------------- */
|
||||||
|
@ -644,7 +691,7 @@ int em28xx_i2c_register(struct em28xx *dev)
|
||||||
dev->i2c_client = em28xx_client_template;
|
dev->i2c_client = em28xx_client_template;
|
||||||
dev->i2c_client.adapter = &dev->i2c_adap;
|
dev->i2c_client.adapter = &dev->i2c_adap;
|
||||||
|
|
||||||
retval = em28xx_i2c_eeprom(dev, &dev->eedata, 256);
|
retval = em28xx_i2c_eeprom(dev, &dev->eedata, &dev->eedata_len);
|
||||||
if ((retval < 0) && (retval != -ENODEV)) {
|
if ((retval < 0) && (retval != -ENODEV)) {
|
||||||
em28xx_errdev("%s: em28xx_i2_eeprom failed! retval [%d]\n",
|
em28xx_errdev("%s: em28xx_i2_eeprom failed! retval [%d]\n",
|
||||||
__func__, retval);
|
__func__, retval);
|
||||||
|
|
|
@ -562,7 +562,9 @@ struct em28xx {
|
||||||
/* resources in use */
|
/* resources in use */
|
||||||
unsigned int resources;
|
unsigned int resources;
|
||||||
|
|
||||||
u8 *eedata; /* currently always 256 bytes */
|
/* eeprom content */
|
||||||
|
u8 *eedata;
|
||||||
|
u16 eedata_len;
|
||||||
|
|
||||||
/* Isoc control struct */
|
/* Isoc control struct */
|
||||||
struct em28xx_dmaqueue vidq;
|
struct em28xx_dmaqueue vidq;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче