drm/nouveau: ensure we've parsed i2c table entry for INIT_*I2C* handlers
authorBen Skeggs <bskeggs@redhat.com>
Wed, 12 May 2010 04:38:25 +0000 (14:38 +1000)
committerBen Skeggs <bskeggs@redhat.com>
Wed, 19 May 2010 06:22:05 +0000 (16:22 +1000)
We may not have parsed the entry yet if the i2c_index is for an i2c bus
that's not referenced by a DCB encoder.

This could be done oh so much more nicely, except we have to care about
prehistoric DCB tables too, and they make life painful.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
drivers/gpu/drm/nouveau/nouveau_bios.c

index d8dcb362e8b5738786abf761f4ead55710949d2b..b0b98d7f4ea9da5a41b4d1618768e42a32f9a0f6 100644 (file)
@@ -715,6 +715,83 @@ static int dcb_entry_idx_from_crtchead(struct drm_device *dev)
        return dcb_entry;
 }
 
+static int
+read_dcb_i2c_entry(struct drm_device *dev, int dcb_version, uint8_t *i2ctable, int index, struct dcb_i2c_entry *i2c)
+{
+       uint8_t dcb_i2c_ver = dcb_version, headerlen = 0, entry_len = 4;
+       int i2c_entries = DCB_MAX_NUM_I2C_ENTRIES;
+       int recordoffset = 0, rdofs = 1, wrofs = 0;
+       uint8_t port_type = 0;
+
+       if (!i2ctable)
+               return -EINVAL;
+
+       if (dcb_version >= 0x30) {
+               if (i2ctable[0] != dcb_version) /* necessary? */
+                       NV_WARN(dev,
+                               "DCB I2C table version mismatch (%02X vs %02X)\n",
+                               i2ctable[0], dcb_version);
+               dcb_i2c_ver = i2ctable[0];
+               headerlen = i2ctable[1];
+               if (i2ctable[2] <= DCB_MAX_NUM_I2C_ENTRIES)
+                       i2c_entries = i2ctable[2];
+               else
+                       NV_WARN(dev,
+                               "DCB I2C table has more entries than indexable "
+                               "(%d entries, max %d)\n", i2ctable[2],
+                               DCB_MAX_NUM_I2C_ENTRIES);
+               entry_len = i2ctable[3];
+               /* [4] is i2c_default_indices, read in parse_dcb_table() */
+       }
+       /*
+        * It's your own fault if you call this function on a DCB 1.1 BIOS --
+        * the test below is for DCB 1.2
+        */
+       if (dcb_version < 0x14) {
+               recordoffset = 2;
+               rdofs = 0;
+               wrofs = 1;
+       }
+
+       if (index == 0xf)
+               return 0;
+       if (index >= i2c_entries) {
+               NV_ERROR(dev, "DCB I2C index too big (%d >= %d)\n",
+                        index, i2ctable[2]);
+               return -ENOENT;
+       }
+       if (i2ctable[headerlen + entry_len * index + 3] == 0xff) {
+               NV_ERROR(dev, "DCB I2C entry invalid\n");
+               return -EINVAL;
+       }
+
+       if (dcb_i2c_ver >= 0x30) {
+               port_type = i2ctable[headerlen + recordoffset + 3 + entry_len * index];
+
+               /*
+                * Fixup for chips using same address offset for read and
+                * write.
+                */
+               if (port_type == 4)     /* seen on C51 */
+                       rdofs = wrofs = 1;
+               if (port_type >= 5)     /* G80+ */
+                       rdofs = wrofs = 0;
+       }
+
+       if (dcb_i2c_ver >= 0x40) {
+               if (port_type != 5 && port_type != 6)
+                       NV_WARN(dev, "DCB I2C table has port type %d\n", port_type);
+
+               i2c->entry = ROM32(i2ctable[headerlen + recordoffset + entry_len * index]);
+       }
+
+       i2c->port_type = port_type;
+       i2c->read = i2ctable[headerlen + recordoffset + rdofs + entry_len * index];
+       i2c->write = i2ctable[headerlen + recordoffset + wrofs + entry_len * index];
+
+       return 0;
+}
+
 static struct nouveau_i2c_chan *
 init_i2c_device_find(struct drm_device *dev, int i2c_index)
 {
@@ -734,6 +811,17 @@ init_i2c_device_find(struct drm_device *dev, int i2c_index)
        if (i2c_index == 0x80)  /* g80+ */
                i2c_index = dcb->i2c_default_indices & 0xf;
 
+       if (i2c_index > DCB_MAX_NUM_I2C_ENTRIES) {
+               NV_ERROR(dev, "invalid i2c_index 0x%x\n", i2c_index);
+               return NULL;
+       }
+
+       /* Make sure i2c table entry has been parsed, it may not
+        * have been if this is a bus not referenced by a DCB encoder
+        */
+       read_dcb_i2c_entry(dev, dcb->version, dcb->i2c_table,
+                          i2c_index, &dcb->i2c[i2c_index]);
+
        return nouveau_i2c_find(dev, i2c_index);
 }
 
@@ -5090,83 +5178,6 @@ static uint16_t findstr(uint8_t *data, int n, const uint8_t *str, int len)
        return 0;
 }
 
-static int
-read_dcb_i2c_entry(struct drm_device *dev, int dcb_version, uint8_t *i2ctable, int index, struct dcb_i2c_entry *i2c)
-{
-       uint8_t dcb_i2c_ver = dcb_version, headerlen = 0, entry_len = 4;
-       int i2c_entries = DCB_MAX_NUM_I2C_ENTRIES;
-       int recordoffset = 0, rdofs = 1, wrofs = 0;
-       uint8_t port_type = 0;
-
-       if (!i2ctable)
-               return -EINVAL;
-
-       if (dcb_version >= 0x30) {
-               if (i2ctable[0] != dcb_version) /* necessary? */
-                       NV_WARN(dev,
-                               "DCB I2C table version mismatch (%02X vs %02X)\n",
-                               i2ctable[0], dcb_version);
-               dcb_i2c_ver = i2ctable[0];
-               headerlen = i2ctable[1];
-               if (i2ctable[2] <= DCB_MAX_NUM_I2C_ENTRIES)
-                       i2c_entries = i2ctable[2];
-               else
-                       NV_WARN(dev,
-                               "DCB I2C table has more entries than indexable "
-                               "(%d entries, max %d)\n", i2ctable[2],
-                               DCB_MAX_NUM_I2C_ENTRIES);
-               entry_len = i2ctable[3];
-               /* [4] is i2c_default_indices, read in parse_dcb_table() */
-       }
-       /*
-        * It's your own fault if you call this function on a DCB 1.1 BIOS --
-        * the test below is for DCB 1.2
-        */
-       if (dcb_version < 0x14) {
-               recordoffset = 2;
-               rdofs = 0;
-               wrofs = 1;
-       }
-
-       if (index == 0xf)
-               return 0;
-       if (index >= i2c_entries) {
-               NV_ERROR(dev, "DCB I2C index too big (%d >= %d)\n",
-                        index, i2ctable[2]);
-               return -ENOENT;
-       }
-       if (i2ctable[headerlen + entry_len * index + 3] == 0xff) {
-               NV_ERROR(dev, "DCB I2C entry invalid\n");
-               return -EINVAL;
-       }
-
-       if (dcb_i2c_ver >= 0x30) {
-               port_type = i2ctable[headerlen + recordoffset + 3 + entry_len * index];
-
-               /*
-                * Fixup for chips using same address offset for read and
-                * write.
-                */
-               if (port_type == 4)     /* seen on C51 */
-                       rdofs = wrofs = 1;
-               if (port_type >= 5)     /* G80+ */
-                       rdofs = wrofs = 0;
-       }
-
-       if (dcb_i2c_ver >= 0x40) {
-               if (port_type != 5 && port_type != 6)
-                       NV_WARN(dev, "DCB I2C table has port type %d\n", port_type);
-
-               i2c->entry = ROM32(i2ctable[headerlen + recordoffset + entry_len * index]);
-       }
-
-       i2c->port_type = port_type;
-       i2c->read = i2ctable[headerlen + recordoffset + rdofs + entry_len * index];
-       i2c->write = i2ctable[headerlen + recordoffset + wrofs + entry_len * index];
-
-       return 0;
-}
-
 static struct dcb_gpio_entry *
 new_gpio_entry(struct nvbios *bios)
 {