drm/nouveau/bios: start refactoring dcb routines
authorBen Skeggs <bskeggs@redhat.com>
Thu, 10 Nov 2011 05:42:55 +0000 (15:42 +1000)
committerBen Skeggs <bskeggs@redhat.com>
Wed, 21 Dec 2011 09:01:39 +0000 (19:01 +1000)
This primary reason for this was mostly to avoid duplication of some of
this stuff by the MXM-SIS parser.  However, some other cleanups will also
follow this as a result.

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

index 61c5c3240e31dfcc9f84542c2b2aa31da5ce3e85..c7723fb54077fb5cc62663b5598e57060bae2d71 100644 (file)
@@ -6048,6 +6048,109 @@ parse_dcb_connector_table(struct nvbios *bios)
        }
 }
 
+void *
+dcb_table(struct drm_device *dev)
+{
+       struct drm_nouveau_private *dev_priv = dev->dev_private;
+       u8 *dcb = NULL;
+
+       if (dev_priv->card_type > NV_04)
+               dcb = ROMPTR(dev, dev_priv->vbios.data[0x36]);
+       if (!dcb) {
+               NV_WARNONCE(dev, "No DCB data found in VBIOS\n");
+               return NULL;
+       }
+
+       if (dcb[0] >= 0x41) {
+               NV_WARNONCE(dev, "DCB version 0x%02x unknown\n", dcb[0]);
+               return NULL;
+       } else
+       if (dcb[0] >= 0x30) {
+               if (ROM32(dcb[6]) == 0x4edcbdcb)
+                       return dcb;
+       } else
+       if (dcb[0] >= 0x20) {
+               if (ROM32(dcb[4]) == 0x4edcbdcb)
+                       return dcb;
+       } else
+       if (dcb[0] >= 0x15) {
+               if (!memcmp(&dcb[-7], "DEV_REC", 7))
+                       return dcb;
+       } else {
+               /*
+                * v1.4 (some NV15/16, NV11+) seems the same as v1.5, but
+                * always has the same single (crt) entry, even when tv-out
+                * present, so the conclusion is this version cannot really
+                * be used.
+                *
+                * v1.2 tables (some NV6/10, and NV15+) normally have the
+                * same 5 entries, which are not specific to the card and so
+                * no use.
+                *
+                * v1.2 does have an I2C table that read_dcb_i2c_table can
+                * handle, but cards exist (nv11 in #14821) with a bad i2c
+                * table pointer, so use the indices parsed in
+                * parse_bmp_structure.
+                *
+                * v1.1 (NV5+, maybe some NV4) is entirely unhelpful
+                */
+               NV_WARNONCE(dev, "No useful DCB data in VBIOS\n");
+               return NULL;
+       }
+
+       NV_WARNONCE(dev, "DCB header validation failed\n");
+       return NULL;
+}
+
+u8 *
+dcb_outp(struct drm_device *dev, u8 idx)
+{
+       u8 *dcb = dcb_table(dev);
+       if (dcb && dcb[0] >= 0x30) {
+               if (idx < dcb[2])
+                       return dcb + dcb[1] + (idx * dcb[3]);
+       } else
+       if (dcb && dcb[0] >= 0x20) {
+               u8 *i2c = ROMPTR(dev, dcb[2]);
+               u8 *ent = dcb + 8 + (idx * 8);
+               if (i2c && ent < i2c)
+                       return ent;
+       } else
+       if (dcb && dcb[0] >= 0x15) {
+               u8 *i2c = ROMPTR(dev, dcb[2]);
+               u8 *ent = dcb + 4 + (idx * 10);
+               if (i2c && ent < i2c)
+                       return ent;
+       }
+
+       return NULL;
+}
+
+int
+dcb_outp_foreach(struct drm_device *dev, void *data,
+                int (*exec)(struct drm_device *, void *, int idx, u8 *outp))
+{
+       int ret, idx = -1;
+       u8 *outp = NULL;
+       while ((outp = dcb_outp(dev, ++idx))) {
+               if (ROM32(outp[0]) == 0x00000000)
+                       break; /* seen on an NV11 with DCB v1.5 */
+               if (ROM32(outp[0]) == 0xffffffff)
+                       break; /* seen on an NV17 with DCB v2.0 */
+
+               if ((outp[0] & 0x0f) == OUTPUT_UNUSED)
+                       continue;
+               if ((outp[0] & 0x0f) == OUTPUT_EOL)
+                       break;
+
+               ret = exec(dev, data, idx, outp);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
 static struct dcb_entry *new_dcb_entry(struct dcb_table *dcb)
 {
        struct dcb_entry *entry = &dcb->entry[dcb->entries];
@@ -6251,25 +6354,6 @@ parse_dcb15_entry(struct drm_device *dev, struct dcb_table *dcb,
        return true;
 }
 
-static bool parse_dcb_entry(struct drm_device *dev, struct dcb_table *dcb,
-                           uint32_t conn, uint32_t conf)
-{
-       struct dcb_entry *entry = new_dcb_entry(dcb);
-       bool ret;
-
-       if (dcb->version >= 0x20)
-               ret = parse_dcb20_entry(dev, dcb, conn, conf, entry);
-       else
-               ret = parse_dcb15_entry(dev, dcb, conn, conf, entry);
-       if (!ret)
-               return ret;
-
-       read_dcb_i2c_entry(dev, dcb->version, dcb->i2c_table,
-                          entry->i2c_index, &dcb->i2c[entry->i2c_index]);
-
-       return true;
-}
-
 static
 void merge_like_dcb_entries(struct drm_device *dev, struct dcb_table *dcb)
 {
@@ -6446,88 +6530,62 @@ fabricate_dcb_encoder_table(struct drm_device *dev, struct nvbios *bios)
 }
 
 static int
-parse_dcb_table(struct drm_device *dev, struct nvbios *bios)
+parse_dcb_entry(struct drm_device *dev, void *data, int idx, u8 *outp)
 {
        struct drm_nouveau_private *dev_priv = dev->dev_private;
-       struct dcb_table *dcb = &bios->dcb;
-       uint16_t dcbptr = 0, i2ctabptr = 0;
-       uint8_t *dcbtable;
-       uint8_t headerlen = 0x4, entries = DCB_MAX_NUM_ENTRIES;
-       bool configblock = true;
-       int recordlength = 8, confofs = 4;
-       int i;
+       struct dcb_table *dcb = &dev_priv->vbios.dcb;
+       u32 conf = (dcb->version >= 0x20) ? ROM32(outp[4]) : ROM32(outp[6]);
+       u32 conn = ROM32(outp[0]);
+       bool ret;
 
-       /* get the offset from 0x36 */
-       if (dev_priv->card_type > NV_04) {
-               dcbptr = ROM16(bios->data[0x36]);
-               if (dcbptr == 0x0000)
-                       NV_WARN(dev, "No output data (DCB) found in BIOS\n");
-       }
+       if (apply_dcb_encoder_quirks(dev, idx, &conn, &conf)) {
+               struct dcb_entry *entry = new_dcb_entry(dcb);
 
-       /* this situation likely means a really old card, pre DCB */
-       if (dcbptr == 0x0) {
-               fabricate_dcb_encoder_table(dev, bios);
-               return 0;
-       }
+               NV_TRACEWARN(dev, "DCB entry %02d: %08x %08x\n", idx, conn, conf);
 
-       dcbtable = &bios->data[dcbptr];
+               if (dcb->version >= 0x20)
+                       ret = parse_dcb20_entry(dev, dcb, conn, conf, entry);
+               else
+                       ret = parse_dcb15_entry(dev, dcb, conn, conf, entry);
+               if (!ret)
+                       return 1; /* stop parsing */
 
-       /* get DCB version */
-       dcb->version = dcbtable[0];
-       NV_TRACE(dev, "Found Display Configuration Block version %d.%d\n",
-                dcb->version >> 4, dcb->version & 0xf);
+               read_dcb_i2c_entry(dev, dcb->version, dcb->i2c_table,
+                                  entry->i2c_index,
+                                  &dcb->i2c[entry->i2c_index]);
+       }
 
-       if (dcb->version >= 0x20) { /* NV17+ */
-               uint32_t sig;
+       return 0;
+}
 
-               if (dcb->version >= 0x30) { /* NV40+ */
-                       headerlen = dcbtable[1];
-                       entries = dcbtable[2];
-                       recordlength = dcbtable[3];
-                       i2ctabptr = ROM16(dcbtable[4]);
-                       sig = ROM32(dcbtable[6]);
-                       dcb->gpio_table_ptr = ROM16(dcbtable[10]);
-                       dcb->connector_table_ptr = ROM16(dcbtable[20]);
-               } else {
-                       i2ctabptr = ROM16(dcbtable[2]);
-                       sig = ROM32(dcbtable[4]);
-                       headerlen = 8;
+static int
+parse_dcb_table(struct drm_device *dev, struct nvbios *bios)
+{
+       struct dcb_table *dcb = &bios->dcb;
+       u16 i2ctabptr = 0x0000;
+       u8 *dcbt;
+
+       dcbt = dcb_table(dev);
+       if (!dcbt) {
+               /* handle pre-DCB boards */
+               if (bios->type == NVBIOS_BMP) {
+                       fabricate_dcb_encoder_table(dev, bios);
+                       return 0;
                }
 
-               if (sig != 0x4edcbdcb) {
-                       NV_ERROR(dev, "Bad Display Configuration Block "
-                                       "signature (%08X)\n", sig);
-                       return -EINVAL;
-               }
-       } else if (dcb->version >= 0x15) { /* some NV11 and NV20 */
-               char sig[8] = { 0 };
+               return -EINVAL;
+       }
 
-               strncpy(sig, (char *)&dcbtable[-7], 7);
-               i2ctabptr = ROM16(dcbtable[2]);
-               recordlength = 10;
-               confofs = 6;
+       NV_TRACE(dev, "DCB version %d.%d\n", dcbt[0] >> 4, dcbt[0] & 0xf);
 
-               if (strcmp(sig, "DEV_REC")) {
-                       NV_ERROR(dev, "Bad Display Configuration Block "
-                                       "signature (%s)\n", sig);
-                       return -EINVAL;
-               }
-       } else {
-               /*
-                * v1.4 (some NV15/16, NV11+) seems the same as v1.5, but always
-                * has the same single (crt) entry, even when tv-out present, so
-                * the conclusion is this version cannot really be used.
-                * v1.2 tables (some NV6/10, and NV15+) normally have the same
-                * 5 entries, which are not specific to the card and so no use.
-                * v1.2 does have an I2C table that read_dcb_i2c_table can
-                * handle, but cards exist (nv11 in #14821) with a bad i2c table
-                * pointer, so use the indices parsed in parse_bmp_structure.
-                * v1.1 (NV5+, maybe some NV4) is entirely unhelpful
-                */
-               NV_TRACEWARN(dev, "No useful information in BIOS output table; "
-                                 "adding all possible outputs\n");
-               fabricate_dcb_encoder_table(dev, bios);
-               return 0;
+       dcb->version = dcbt[0];
+       if (dcb->version >= 0x30) {
+               i2ctabptr = ROM16(dcbt[4]);
+               dcb->gpio_table_ptr = ROM16(dcbt[10]);
+               dcb->connector_table_ptr = ROM16(dcbt[20]);
+       } else
+       if (dcb->version >= 0x15) {
+               i2ctabptr = ROM16(dcbt[2]);
        }
 
        if (!i2ctabptr)
@@ -6543,44 +6601,14 @@ parse_dcb_table(struct drm_device *dev, struct nvbios *bios)
                 */
                if (dcb->version >= 0x22) {
                        int idx = (dcb->version >= 0x40 ?
-                                  dcb->i2c_default_indices & 0xf :
-                                  2);
+                                  dcb->i2c_default_indices & 0xf : 2);
 
                        read_dcb_i2c_entry(dev, dcb->version, dcb->i2c_table,
                                           idx, &dcb->i2c[idx]);
                }
        }
 
-       if (entries > DCB_MAX_NUM_ENTRIES)
-               entries = DCB_MAX_NUM_ENTRIES;
-
-       for (i = 0; i < entries; i++) {
-               uint32_t connection, config = 0;
-
-               connection = ROM32(dcbtable[headerlen + recordlength * i]);
-               if (configblock)
-                       config = ROM32(dcbtable[headerlen + confofs + recordlength * i]);
-
-               /* seen on an NV11 with DCB v1.5 */
-               if (connection == 0x00000000)
-                       break;
-
-               /* seen on an NV17 with DCB v2.0 */
-               if (connection == 0xffffffff)
-                       break;
-
-               if ((connection & 0x0000000f) == 0x0000000f)
-                       continue;
-
-               if (!apply_dcb_encoder_quirks(dev, i, &connection, &config))
-                       continue;
-
-               NV_TRACEWARN(dev, "Raw DCB entry %d: %08x %08x\n",
-                            dcb->entries, connection, config);
-
-               if (!parse_dcb_entry(dev, dcb, connection, config))
-                       break;
-       }
+       dcb_outp_foreach(dev, NULL, parse_dcb_entry);
 
        /*
         * apart for v2.1+ not being known for requiring merging, this
index 5f1258834ec15c8abe387a8ffc4860cd5a6c600e..b9f2da394f89a93c3279953ebc20c9e253f6250e 100644 (file)
@@ -117,6 +117,7 @@ enum dcb_type {
        OUTPUT_LVDS = 3,
        OUTPUT_DP = 6,
        OUTPUT_EOL = 14, /* DCB 4.0+, appears to be end-of-list */
+       OUTPUT_UNUSED = 15,
        OUTPUT_ANY = -1
 };
 
@@ -339,4 +340,9 @@ struct nvbios {
        } legacy;
 };
 
+void *dcb_table(struct drm_device *);
+u8 *dcb_outp(struct drm_device *, u8 idx);
+int dcb_outp_foreach(struct drm_device *, void *data,
+                    int (*)(struct drm_device *, void *, int idx, u8 *outp));
+
 #endif
index 1e70005d82209179d03e3f801927d4666738e29b..446caedbcff9ae4a0325cd6f71e5a97f9394851a 100644 (file)
@@ -1608,6 +1608,13 @@ extern void nv_wo32(struct nouveau_gpuobj *, u32 offset, u32 val);
 #define NV_TRACEWARN(d, fmt, arg...) NV_PRINTK(KERN_NOTICE, d, fmt, ##arg)
 #define NV_TRACE(d, fmt, arg...) NV_PRINTK(KERN_INFO, d, fmt, ##arg)
 #define NV_WARN(d, fmt, arg...) NV_PRINTK(KERN_WARNING, d, fmt, ##arg)
+#define NV_WARNONCE(d, fmt, arg...) do {                                       \
+       static int _warned = 0;                                                \
+       if (!_warned) {                                                        \
+               NV_WARN(d, fmt, ##arg);                                        \
+               _warned = 1;                                                   \
+       }                                                                      \
+} while(0)
 
 /* nouveau_reg_debug bitmask */
 enum {