[media] em28xx: extract the device configuration dataset from eeproms with 16 bit...
[firefly-linux-kernel-4.4.55.git] / drivers / media / usb / em28xx / em28xx-i2c.c
index 8d4817747bf2164d1fee0f87d02f4eb64f614f65..6152423bef76540254a76d87df80dfb5746ae2f3 100644 (file)
@@ -409,13 +409,18 @@ static int em28xx_i2c_read_block(struct em28xx *dev, u16 addr, bool addr_w16,
        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;
-       struct em28xx_eeprom *em_eeprom;
+       const u16 len = 256;
+       /* 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;
+       struct em28xx_eeprom *dev_config;
+       u8 buf, *data;
 
        *eedata = NULL;
+       *eedata_len = 0;
 
        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);
        if (err != len) {
                em28xx_errdev("failed to read eeprom (err=%d)\n", err);
-               kfree(data);
-               return err;
+               goto error;
        }
 
        /* Display eeprom content */
@@ -451,15 +455,25 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char **eedata, int len
                if (15 == (i % 16))
                        printk("\n");
        }
+       if (dev->eeprom_addrwidth_16bit)
+               em28xx_info("i2c eeprom %04x: ... (skipped)\n", i);
 
        if (dev->eeprom_addrwidth_16bit &&
            data[0] == 0x26 && data[3] == 0x00) {
                /* new eeprom format; size 4-64kb */
+               u16 mc_start;
+               u16 hwconf_offset;
+
                dev->hash = em28xx_hash_mem(data, len, 32);
-               em28xx_info("EEPROM hash = 0x%08lx\n", dev->hash);
-               em28xx_info("EEPROM info: boot page address = 0x%02x04, "
+               mc_start = (data[1] << 8) + 4;  /* usually 0x0004 */
+
+               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",
-                           data[1], data[2]);
+                           mc_start, data[2]);
                /* boot configuration (address 0x0002):
                 * [0]   microcode download speed: 1 = 400 kHz; 0 = 100 kHz
                 * [1]   always selects 12 kb RAM
@@ -469,32 +483,61 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char **eedata, int len
                 *       characterization
                 */
 
-               /* FIXME:
-                * - read more than 256 bytes / addresses above 0x00ff
-                * - find offset for device config dataset and extract it
-                * - decrypt eeprom data for camera bridges (em25xx, em276x+)
-                * - use separate/different eeprom hashes (not yet used)
+               /* Read hardware config dataset offset from address
+                * (microcode start + 46)                           */
+               err = em28xx_i2c_read_block(dev, mc_start + 46, 1, 2, data);
+               if (err != 2) {
+                       em28xx_errdev("failed to read hardware configuration data from eeprom (err=%d)\n",
+                                     err);
+                       goto error;
+               }
+
+               /* Calculate hardware config dataset start address */
+               hwconf_offset = mc_start + data[0] + (data[1] << 8);
+
+               /* 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;
+               }
 
-               return 0;
-       } else if (data[0] != 0x1a || data[1] != 0xeb ||
-                  data[2] != 0x67 || data[3] != 0x95) {
+               /* 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");
-               return -ENODEV;
+               err = -ENODEV;
+               goto error;
        }
 
        *eedata = data;
-       em_eeprom = (void *)eedata;
+       *eedata_len = len;
+       dev_config = (void *)eedata;
 
-       dev->hash = em28xx_hash_mem(data, len, 32);
-
-       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) {
+       switch (le16_to_cpu(dev_config->chip_conf) >> 4 & 0x3) {
        case 0:
                em28xx_info("\tNo audio on board.\n");
                break;
@@ -509,13 +552,13 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char **eedata, int len
                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");
 
-       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");
 
-       switch (le16_to_cpu(em_eeprom->chip_conf) & 0x3) {
+       switch (le16_to_cpu(dev_config->chip_conf) & 0x3) {
        case 0:
                em28xx_info("\t500mA max power\n");
                break;
@@ -530,12 +573,16 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char **eedata, int len
                break;
        }
        em28xx_info("\tTable at offset 0x%02x, strings=0x%04x, 0x%04x, 0x%04x\n",
-                   em_eeprom->string_idx_table,
-                   le16_to_cpu(em_eeprom->string1),
-                   le16_to_cpu(em_eeprom->string2),
-                   le16_to_cpu(em_eeprom->string3));
+                   dev_config->string_idx_table,
+                   le16_to_cpu(dev_config->string1),
+                   le16_to_cpu(dev_config->string2),
+                   le16_to_cpu(dev_config->string3));
 
        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.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)) {
                em28xx_errdev("%s: em28xx_i2_eeprom failed! retval [%d]\n",
                        __func__, retval);