drm/nouveau/bios: implement "full" BIT 'd' table (and subtable) parsing in core
authorBen Skeggs <bskeggs@redhat.com>
Tue, 6 Nov 2012 06:03:51 +0000 (16:03 +1000)
committerBen Skeggs <bskeggs@redhat.com>
Wed, 28 Nov 2012 23:57:44 +0000 (09:57 +1000)
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
drivers/gpu/drm/nouveau/core/include/subdev/bios/dcb.h
drivers/gpu/drm/nouveau/core/include/subdev/bios/dp.h
drivers/gpu/drm/nouveau/core/subdev/bios/dp.c
drivers/gpu/drm/nouveau/core/subdev/bios/init.c

index d682fb6258339e1b3f2bea8819f11e526cc5719c..0577c67612d47fcf5dbc2b15e9fc81e886fe14db 100644 (file)
@@ -58,33 +58,4 @@ u16 dcb_outp(struct nouveau_bios *, u8 idx, u8 *ver, u8 *len);
 int dcb_outp_foreach(struct nouveau_bios *, void *data, int (*exec)
                     (struct nouveau_bios *, void *, int index, u16 entry));
 
-
-/* BIT 'U'/'d' table encoder subtables have hashes matching them to
- * a particular set of encoders.
- *
- * This function returns true if a particular DCB entry matches.
- */
-static inline bool
-dcb_hash_match(struct dcb_output *dcb, u32 hash)
-{
-       if ((hash & 0x000000f0) != (dcb->location << 4))
-               return false;
-       if ((hash & 0x0000000f) != dcb->type)
-               return false;
-       if (!(hash & (dcb->or << 16)))
-               return false;
-
-       switch (dcb->type) {
-       case DCB_OUTPUT_TMDS:
-       case DCB_OUTPUT_LVDS:
-       case DCB_OUTPUT_DP:
-               if (hash & 0x00c00000) {
-                       if (!(hash & (dcb->sorconf.link << 22)))
-                               return false;
-               }
-       default:
-               return true;
-       }
-}
-
 #endif
index 73b5e5d3e75a44cf2da4c0ba0c89fe023e4099be..6e54218b55fca0f97086d6f358c260cfebea8880 100644 (file)
@@ -1,8 +1,34 @@
 #ifndef __NVBIOS_DP_H__
 #define __NVBIOS_DP_H__
 
-u16 dp_table(struct nouveau_bios *, u8 *ver, u8 *hdr, u8 *cnt, u8 *len);
-u16 dp_outp(struct nouveau_bios *, u8 idx, u8 *ver, u8 *len);
-u16 dp_outp_match(struct nouveau_bios *, struct dcb_output *, u8 *ver, u8 *len);
+struct nvbios_dpout {
+       u16 type;
+       u16 mask;
+       u8  flags;
+       u32 script[5];
+       u32 lnkcmp;
+};
+
+u16 nvbios_dpout_parse(struct nouveau_bios *, u8 idx,
+                      u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
+                      struct nvbios_dpout *);
+u16 nvbios_dpout_match(struct nouveau_bios *, u16 type, u16 mask,
+                      u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
+                      struct nvbios_dpout *);
+
+struct nvbios_dpcfg {
+       u8 drv;
+       u8 pre;
+       u8 unk;
+};
+
+u16
+nvbios_dpcfg_parse(struct nouveau_bios *, u16 outp, u8 idx,
+                  u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
+                  struct nvbios_dpcfg *);
+u16
+nvbios_dpcfg_match(struct nouveau_bios *, u16 outp, u8 un, u8 vs, u8 pe,
+                  u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
+                  struct nvbios_dpcfg *);
 
 #endif
index 3cbc0f3e8d5e32a6346ffc305185028069f87b8e..663853bcca8288f9e45f944c950b672d219ecfd0 100644 (file)
 
 #include "subdev/bios.h"
 #include "subdev/bios/bit.h"
-#include "subdev/bios/dcb.h"
 #include "subdev/bios/dp.h"
 
-u16
-dp_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
+static u16
+nvbios_dp_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
 {
-       struct bit_entry bit_d;
+       struct bit_entry d;
 
-       if (!bit_entry(bios, 'd', &bit_d)) {
-               if (bit_d.version == 1) {
-                       u16 data = nv_ro16(bios, bit_d.offset);
+       if (!bit_entry(bios, 'd', &d)) {
+               if (d.version == 1 && d.length >= 2) {
+                       u16 data = nv_ro16(bios, d.offset);
                        if (data) {
-                               *ver = nv_ro08(bios, data + 0);
-                               *hdr = nv_ro08(bios, data + 1);
-                               *len = nv_ro08(bios, data + 2);
-                               *cnt = nv_ro08(bios, data + 3);
-                               return data;
+                               *ver = nv_ro08(bios, data + 0x00);
+                               switch (*ver) {
+                               case 0x21:
+                               case 0x30:
+                               case 0x40:
+                                       *hdr = nv_ro08(bios, data + 0x01);
+                                       *len = nv_ro08(bios, data + 0x02);
+                                       *cnt = nv_ro08(bios, data + 0x03);
+                                       return data;
+                               default:
+                                       break;
+                               }
                        }
                }
        }
@@ -49,28 +55,150 @@ dp_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
        return 0x0000;
 }
 
+static u16
+nvbios_dpout_entry(struct nouveau_bios *bios, u8 idx,
+                  u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
+{
+       u16 data = nvbios_dp_table(bios, ver, hdr, cnt, len);
+       if (data && idx < *cnt) {
+               u16 outp = nv_ro16(bios, data + *hdr + idx * *len);
+               switch (*ver * !!outp) {
+               case 0x21:
+               case 0x30:
+                       *hdr = nv_ro08(bios, data + 0x04);
+                       *len = nv_ro08(bios, data + 0x05);
+                       *cnt = nv_ro08(bios, outp + 0x04);
+                       break;
+               case 0x40:
+                       *hdr = nv_ro08(bios, data + 0x04);
+                       *cnt = 0;
+                       *len = 0;
+                       break;
+               default:
+                       break;
+               }
+               return outp;
+       }
+       *ver = 0x00;
+       return 0x0000;
+}
+
 u16
-dp_outp(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *len)
+nvbios_dpout_parse(struct nouveau_bios *bios, u8 idx,
+                  u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
+                  struct nvbios_dpout *info)
 {
-       u8  hdr, cnt;
-       u16 table = dp_table(bios, ver, &hdr, &cnt, len);
-       if (table && idx < cnt)
-               return nv_ro16(bios, table + hdr + (idx * *len));
-       return 0xffff;
+       u16 data = nvbios_dpout_entry(bios, idx, ver, hdr, cnt, len);
+       if (data && *ver) {
+               info->type = nv_ro16(bios, data + 0x00);
+               info->mask = nv_ro16(bios, data + 0x02);
+               switch (*ver) {
+               case 0x21:
+               case 0x30:
+                       info->flags     = nv_ro08(bios, data + 0x05);
+                       info->script[0] = nv_ro16(bios, data + 0x06);
+                       info->script[1] = nv_ro16(bios, data + 0x08);
+                       info->lnkcmp    = nv_ro16(bios, data + 0x0a);
+                       info->script[2] = nv_ro16(bios, data + 0x0c);
+                       info->script[3] = nv_ro16(bios, data + 0x0e);
+                       info->script[4] = nv_ro16(bios, data + 0x10);
+                       break;
+               case 0x40:
+                       info->flags     = nv_ro08(bios, data + 0x04);
+                       info->script[0] = nv_ro16(bios, data + 0x05);
+                       info->script[1] = nv_ro16(bios, data + 0x07);
+                       info->lnkcmp    = nv_ro16(bios, data + 0x09);
+                       info->script[2] = nv_ro16(bios, data + 0x0b);
+                       info->script[3] = nv_ro16(bios, data + 0x0d);
+                       info->script[4] = nv_ro16(bios, data + 0x0f);
+                       break;
+               default:
+                       data = 0x0000;
+                       break;
+               }
+       }
+       return data;
 }
 
 u16
-dp_outp_match(struct nouveau_bios *bios, struct dcb_output *outp,
-             u8 *ver, u8 *len)
+nvbios_dpout_match(struct nouveau_bios *bios, u16 type, u16 mask,
+                  u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
+                  struct nvbios_dpout *info)
 {
-       u8  idx = 0;
-       u16 data;
-       while ((data = dp_outp(bios, idx++, ver, len)) != 0xffff) {
-               if (data) {
-                       u32 hash = nv_ro32(bios, data);
-                       if (dcb_hash_match(outp, hash))
-                               return data;
+       u16 data, idx = 0;
+       while ((data = nvbios_dpout_parse(bios, idx++, ver, hdr, cnt, len, info)) || *ver) {
+               if (data && info->type == type) {
+                       if ((info->mask & mask) == mask)
+                               break;
                }
        }
+       return data;
+}
+
+static u16
+nvbios_dpcfg_entry(struct nouveau_bios *bios, u16 outp, u8 idx,
+                  u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
+{
+       if (*ver >= 0x40) {
+               outp = nvbios_dp_table(bios, ver, hdr, cnt, len);
+               *hdr = *hdr + (*len * * cnt);
+               *len = nv_ro08(bios, outp + 0x06);
+               *cnt = nv_ro08(bios, outp + 0x07);
+       }
+
+       if (idx < *cnt)
+               return outp + *hdr + (idx * *len);
+
        return 0x0000;
 }
+
+u16
+nvbios_dpcfg_parse(struct nouveau_bios *bios, u16 outp, u8 idx,
+                  u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
+                  struct nvbios_dpcfg *info)
+{
+       u16 data = nvbios_dpcfg_entry(bios, outp, idx, ver, hdr, cnt, len);
+       if (data) {
+               switch (*ver) {
+               case 0x21:
+                       info->drv = nv_ro08(bios, data + 0x02);
+                       info->pre = nv_ro08(bios, data + 0x03);
+                       info->unk = nv_ro08(bios, data + 0x04);
+                       break;
+               case 0x30:
+               case 0x40:
+                       info->drv = nv_ro08(bios, data + 0x01);
+                       info->pre = nv_ro08(bios, data + 0x02);
+                       info->unk = nv_ro08(bios, data + 0x03);
+                       break;
+               default:
+                       data = 0x0000;
+                       break;
+               }
+       }
+       return data;
+}
+
+u16
+nvbios_dpcfg_match(struct nouveau_bios *bios, u16 outp, u8 un, u8 vs, u8 pe,
+                  u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
+                  struct nvbios_dpcfg *info)
+{
+       u8 idx = 0xff;
+       u16 data;
+
+       if (*ver >= 0x30) {
+               const u8 vsoff[] = { 0, 4, 7, 9 };
+               idx = (un * 10) + vsoff[vs] + pe;
+       } else {
+               while ((data = nvbios_dpcfg_entry(bios, outp, idx,
+                                                 ver, hdr, cnt, len))) {
+                       if (nv_ro08(bios, data + 0x00) == vs &&
+                           nv_ro08(bios, data + 0x01) == pe)
+                               break;
+                       idx++;
+               }
+       }
+
+       return nvbios_dpcfg_parse(bios, outp, pe, ver, hdr, cnt, len, info);
+}
index 6be8c32f6e4cc146e9c2d887cfc0989b579c3164..ae168bbb86d851a0bd1a81a532106281bae91a83 100644 (file)
@@ -743,9 +743,10 @@ static void
 init_dp_condition(struct nvbios_init *init)
 {
        struct nouveau_bios *bios = init->bios;
+       struct nvbios_dpout info;
        u8  cond = nv_ro08(bios, init->offset + 1);
        u8  unkn = nv_ro08(bios, init->offset + 2);
-       u8  ver, len;
+       u8  ver, hdr, cnt, len;
        u16 data;
 
        trace("DP_CONDITION\t0x%02x 0x%02x\n", cond, unkn);
@@ -759,10 +760,12 @@ init_dp_condition(struct nvbios_init *init)
        case 1:
        case 2:
                if ( init->outp &&
-                   (data = dp_outp_match(bios, init->outp, &ver, &len))) {
-                       if (ver <= 0x40 && !(nv_ro08(bios, data + 5) & cond))
-                               init_exec_set(init, false);
-                       if (ver == 0x40 && !(nv_ro08(bios, data + 4) & cond))
+                   (data = nvbios_dpout_match(bios, DCB_OUTPUT_DP,
+                                              (init->outp->or << 0) |
+                                              (init->outp->sorconf.link << 6),
+                                              &ver, &hdr, &cnt, &len, &info)))
+               {
+                       if (!(info.flags & cond))
                                init_exec_set(init, false);
                        break;
                }