drm/nouveau/bios: implement some dcb output entry parsing/matching functions
authorBen Skeggs <bskeggs@redhat.com>
Thu, 8 Nov 2012 07:41:06 +0000 (17:41 +1000)
committerBen Skeggs <bskeggs@redhat.com>
Wed, 28 Nov 2012 23:57:48 +0000 (09:57 +1000)
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c
drivers/gpu/drm/nouveau/core/engine/disp/sornv50.c
drivers/gpu/drm/nouveau/core/include/subdev/bios/dcb.h
drivers/gpu/drm/nouveau/core/subdev/bios/dcb.c

index bf07248452a6755ad1b7a94fa7a6165cf6a23d16..5ac8c429852d26885ed7007de4818c5074f26223 100644 (file)
@@ -576,8 +576,7 @@ exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl,
            struct nvbios_outp *info)
 {
        struct nouveau_bios *bios = nouveau_bios(priv);
-       u16 data, idx = 0;
-       u16 mask, type;
+       u16 mask, type, data;
 
        if (outp < 4) {
                type = DCB_OUTPUT_ANALOG;
@@ -602,30 +601,11 @@ exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl,
        mask |= 0x0001 << outp;
        mask |= 0x0100 << head;
 
-       /* this is a tad special, but for the moment its needed to get
-        * all the dcb data required by the vbios scripts.. will be cleaned
-        * up later as more bits are moved to the core..
-        */
-       while ((data = dcb_outp(bios, idx++, ver, hdr))) {
-               u32 conn = nv_ro32(bios, data + 0);
-               u32 conf = nv_ro32(bios, data + 4);
-               if ((conn & 0x00300000) ||
-                   (conn & 0x0000000f) != type ||
-                   (conn & 0x0f000000) != (0x01000000 << outp))
-                       continue;
-
-               if ( (mask & 0x00c0) && (mask & 0x00c0) !=
-                   ((mask & 0x00c0) & ((conf & 0x00000030) << 2)))
-                       continue;
-
-               dcb->type = type;
-               dcb->or = 1 << outp;
-               dcb->connector = (conn & 0x0000f000) >> 12;
-
-               return nvbios_outp_match(bios, type, mask, ver, hdr, cnt, len, info);
-       }
+       data = dcb_outp_match(bios, type, mask, ver, hdr, dcb);
+       if (!data)
+               return 0x0000;
 
-       return 0x0000;
+       return nvbios_outp_match(bios, type, mask, ver, hdr, cnt, len, info);
 }
 
 static bool
index 4a8117d33f5ac85a8ffc3d7daa69447b082f9e2c..0ca12b9230fd3515b311a637582e73c39f144ebe 100644 (file)
@@ -53,37 +53,18 @@ nv50_sor_mthd(struct nouveau_object *object, u32 mthd, void *args, u32 size)
        const u8  link = (mthd & NV50_DISP_SOR_MTHD_LINK) >> 2;
        const u8    or = (mthd & NV50_DISP_SOR_MTHD_OR);
        const u16 mask = (0x0100 << head) | (0x0040 << link) | (0x0001 << or);
-       struct dcb_output outp = {
-               .type = type,
-               .or = (1 << or),
-               .sorconf.link = (1 << link),
-       };
-       u8  ver, hdr, idx = 0;
+       struct dcb_output outp;
+       u8  ver, hdr;
        u32 data;
        int ret = -EINVAL;
 
        if (size < sizeof(u32))
                return -EINVAL;
+       data = *(u32 *)args;
 
-       while (type && (data = dcb_outp(bios, idx++, &ver, &hdr))) {
-               u32 conn = nv_ro32(bios, data + 0);
-               u32 conf = nv_ro32(bios, data + 4);
-               if ((conn & 0x00300000) ||
-                   (conn & 0x0000000f) != type ||
-                   (conn & 0x0f000000) != (0x01000000 << or))
-                       continue;
-
-               if ( (mask & 0x00c0) && (mask & 0x00c0) !=
-                   ((mask & 0x00c0) & ((conf & 0x00000030) << 2)))
-                       continue;
-
-               outp.connector = (conn & 0x0000f000) >> 12;
-       }
-
-       if (data == 0x0000)
+       if (type && !dcb_outp_match(bios, type, mask, &ver, &hdr, &outp))
                return -ENODEV;
 
-       data = *(u32 *)args;
        switch (mthd & ~0x3f) {
        case NV50_DISP_SOR_PWR:
                ret = priv->sor.power(priv, or, data);
index 0577c67612d47fcf5dbc2b15e9fc81e886fe14db..b79025da581ef5b8ea50cadb563313c70a6cd781 100644 (file)
@@ -23,6 +23,7 @@ struct dcb_output {
        uint8_t bus;
        uint8_t location;
        uint8_t or;
+       uint8_t link;
        bool duallink_possible;
        union {
                struct sor_conf {
@@ -55,6 +56,10 @@ struct dcb_output {
 
 u16 dcb_table(struct nouveau_bios *, u8 *ver, u8 *hdr, u8 *ent, u8 *len);
 u16 dcb_outp(struct nouveau_bios *, u8 idx, u8 *ver, u8 *len);
+u16 dcb_outp_parse(struct nouveau_bios *, u8 idx, u8 *, u8 *,
+                  struct dcb_output *);
+u16 dcb_outp_match(struct nouveau_bios *, u16 type, u16 mask, u8 *, u8 *,
+                  struct dcb_output *);
 int dcb_outp_foreach(struct nouveau_bios *, void *data, int (*exec)
                     (struct nouveau_bios *, void *, int index, u16 entry));
 
index 7d750382a833c33b64fbbf40c98a1d8f936399e2..bbd709fba0a9b043753bb53fad78a372831eaafe 100644 (file)
@@ -107,6 +107,69 @@ dcb_outp(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *len)
        return 0x0000;
 }
 
+u16
+dcb_outp_parse(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *len,
+              struct dcb_output *outp)
+{
+       u16 dcb = dcb_outp(bios, idx, ver, len);
+       if (dcb) {
+               if (*ver >= 0x20) {
+                       u32 conn = nv_ro32(bios, dcb + 0x00);
+                       outp->or        = (conn & 0x0f000000) >> 24;
+                       outp->location  = (conn & 0x00300000) >> 20;
+                       outp->bus       = (conn & 0x000f0000) >> 16;
+                       outp->connector = (conn & 0x0000f000) >> 12;
+                       outp->heads     = (conn & 0x00000f00) >> 8;
+                       outp->i2c_index = (conn & 0x000000f0) >> 4;
+                       outp->type      = (conn & 0x0000000f);
+                       outp->link      = 0;
+               } else {
+                       dcb = 0x0000;
+               }
+
+               if (*ver >= 0x40) {
+                       u32 conf = nv_ro32(bios, dcb + 0x04);
+                       switch (outp->type) {
+                       case DCB_OUTPUT_TMDS:
+                       case DCB_OUTPUT_LVDS:
+                       case DCB_OUTPUT_DP:
+                               outp->link = (conf & 0x00000030) >> 4;
+                               outp->sorconf.link = outp->link; /*XXX*/
+                               break;
+                       default:
+                               break;
+                       }
+               }
+       }
+       return dcb;
+}
+
+static inline u16
+dcb_outp_hasht(struct dcb_output *outp)
+{
+       return outp->type;
+}
+
+static inline u16
+dcb_outp_hashm(struct dcb_output *outp)
+{
+       return (outp->heads << 8) | (outp->link << 6) | outp->or;
+}
+
+u16
+dcb_outp_match(struct nouveau_bios *bios, u16 type, u16 mask,
+              u8 *ver, u8 *len, struct dcb_output *outp)
+{
+       u16 dcb, idx = 0;
+       while ((dcb = dcb_outp_parse(bios, idx++, ver, len, outp))) {
+               if (dcb_outp_hasht(outp) == type) {
+                       if ((dcb_outp_hashm(outp) & mask) == mask)
+                               break;
+               }
+       }
+       return dcb;
+}
+
 int
 dcb_outp_foreach(struct nouveau_bios *bios, void *data,
                 int (*exec)(struct nouveau_bios *, void *, int, u16))