drm/nouveau/disp/nv50-: implement a common supervisor 3.0
authorBen Skeggs <bskeggs@redhat.com>
Fri, 19 May 2017 13:59:35 +0000 (23:59 +1000)
committerBen Skeggs <bskeggs@redhat.com>
Fri, 16 Jun 2017 04:05:00 +0000 (14:05 +1000)
This makes use of all the additional routing and state added in previous
commits, making it possible to deal with GM20x macro link routing, while
also sharing code between the NV50 and GF119 implementations.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/init.h
drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c
drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h
drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c
drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h
drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h
drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg94.c
drivers/gpu/drm/nouveau/nvkm/subdev/bios/init.c

index c7383e1..06ab480 100644 (file)
@@ -3,16 +3,12 @@
 
 struct nvbios_init {
        struct nvkm_subdev *subdev;
-       struct nvkm_bios *bios;
        u32 offset;
 
        struct dcb_output *outp;
        int or;
        int link;
-       union {
-               int head;
-               int crtc;
-       };
+       int head;
 
        /* internal state used during parsing */
        u8 execute;
index f4a0554..d8765b5 100644 (file)
 #include "ior.h"
 #include "rootnv50.h"
 
-#include <subdev/bios.h>
-#include <subdev/bios/disp.h>
-#include <subdev/bios/init.h>
-#include <subdev/bios/pll.h>
-#include <subdev/devinit.h>
-
-static struct nvkm_output *
-exec_lookup(struct nv50_disp *disp, int head, int or, u32 ctrl,
-           u32 *data, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
-           struct nvbios_outp *info)
-{
-       struct nvkm_subdev *subdev = &disp->base.engine.subdev;
-       struct nvkm_bios *bios = subdev->device->bios;
-       struct nvkm_output *outp;
-       u16 mask, type;
-
-       if (or < 4) {
-               type = DCB_OUTPUT_ANALOG;
-               mask = 0;
-       } else {
-               or -= 4;
-               switch (ctrl & 0x00000f00) {
-               case 0x00000000: type = DCB_OUTPUT_LVDS; mask = 1; break;
-               case 0x00000100: type = DCB_OUTPUT_TMDS; mask = 1; break;
-               case 0x00000200: type = DCB_OUTPUT_TMDS; mask = 2; break;
-               case 0x00000500: type = DCB_OUTPUT_TMDS; mask = 3; break;
-               case 0x00000800: type = DCB_OUTPUT_DP; mask = 1; break;
-               case 0x00000900: type = DCB_OUTPUT_DP; mask = 2; break;
-               default:
-                       nvkm_error(subdev, "unknown SOR mc %08x\n", ctrl);
-                       return NULL;
-               }
-       }
-
-       mask  = 0x00c0 & (mask << 6);
-       mask |= 0x0001 << or;
-       mask |= 0x0100 << head;
-
-       list_for_each_entry(outp, &disp->base.outp, head) {
-               if ((outp->info.hasht & 0xff) == type &&
-                   (outp->info.hashm & mask) == mask) {
-                       *data = nvbios_outp_match(bios, outp->info.hasht, mask,
-                                                 ver, hdr, cnt, len, info);
-                       if (!*data)
-                               return NULL;
-                       return outp;
-               }
-       }
-
-       return NULL;
-}
-
-static struct nvkm_output *
-exec_clkcmp(struct nv50_disp *disp, int head, int id, u32 pclk, u32 *conf)
-{
-       struct nvkm_subdev *subdev = &disp->base.engine.subdev;
-       struct nvkm_device *device = subdev->device;
-       struct nvkm_bios *bios = device->bios;
-       struct nvkm_output *outp;
-       struct nvbios_outp info1;
-       struct nvbios_ocfg info2;
-       u8  ver, hdr, cnt, len;
-       u32 data, ctrl = 0;
-       int or;
-
-       for (or = 0; !(ctrl & (1 << head)) && or < 8; or++) {
-               ctrl = nvkm_rd32(device, 0x660180 + (or * 0x20));
-               if (ctrl & (1 << head))
-                       break;
-       }
-
-       if (or == 8)
-               return NULL;
-
-       outp = exec_lookup(disp, head, or, ctrl, &data, &ver, &hdr, &cnt, &len, &info1);
-       if (!outp)
-               return NULL;
-
-       *conf = (ctrl & 0x00000f00) >> 8;
-       switch (outp->info.type) {
-       case DCB_OUTPUT_TMDS:
-               if (*conf == 5)
-                       *conf |= 0x0100;
-               break;
-       case DCB_OUTPUT_LVDS:
-               *conf |= disp->sor.lvdsconf;
-               break;
-       default:
-               break;
-       }
-
-       data = nvbios_ocfg_match(bios, data, *conf & 0xff, *conf >> 8,
-                                &ver, &hdr, &cnt, &len, &info2);
-       if (data && id < 0xff) {
-               data = nvbios_oclk_match(bios, info2.clkcmp[id], pclk);
-               if (data) {
-                       struct nvbios_init init = {
-                               .subdev = subdev,
-                               .bios = bios,
-                               .offset = data,
-                               .outp = &outp->info,
-                               .crtc = head,
-                               .execute = 1,
-                       };
-
-                       nvbios_exec(&init);
-               }
-       }
-
-       return outp;
-}
-
-static void
-gf119_disp_intr_unk4_0(struct nv50_disp *disp, int head)
-{
-       struct nvkm_device *device = disp->base.engine.subdev.device;
-       u32 pclk = nvkm_rd32(device, 0x660450 + (head * 0x300)) / 1000;
-       u32 conf;
-
-       exec_clkcmp(disp, head, 1, pclk, &conf);
-}
-
 void
 gf119_disp_super(struct work_struct *work)
 {
@@ -195,8 +73,7 @@ gf119_disp_super(struct work_struct *work)
                list_for_each_entry(head, &disp->base.head, head) {
                        if (!(mask[head->id] & 0x00001000))
                                continue;
-                       nvkm_debug(subdev, "supervisor 3.0 - head %d\n", head->id);
-                       gf119_disp_intr_unk4_0(disp, head->id);
+                       nv50_disp_super_3_0(disp, head);
                }
        }
 
index d6bfaa5..1e11e25 100644 (file)
@@ -52,6 +52,7 @@ struct nvkm_ior_func {
        int (*sense)(struct nvkm_ior *, u32 loadval);
        void (*clock)(struct nvkm_ior *);
        void (*war_2)(struct nvkm_ior *);
+       void (*war_3)(struct nvkm_ior *);
 
        struct {
                void (*ctrl)(struct nvkm_ior *, int head, bool enable,
index f21e0e9..0c570db 100644 (file)
@@ -238,162 +238,23 @@ nv50_disp_super_ior_arm(struct nvkm_head *head)
        return NULL;
 }
 
-static struct nvkm_output *
-exec_lookup(struct nv50_disp *disp, int head, int or, u32 ctrl,
-           u32 *data, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
-           struct nvbios_outp *info)
-{
-       struct nvkm_subdev *subdev = &disp->base.engine.subdev;
-       struct nvkm_bios *bios = subdev->device->bios;
-       struct nvkm_output *outp;
-       u16 mask, type;
-
-       if (or < 4) {
-               type = DCB_OUTPUT_ANALOG;
-               mask = 0;
-       } else
-       if (or < 8) {
-               switch (ctrl & 0x00000f00) {
-               case 0x00000000: type = DCB_OUTPUT_LVDS; mask = 1; break;
-               case 0x00000100: type = DCB_OUTPUT_TMDS; mask = 1; break;
-               case 0x00000200: type = DCB_OUTPUT_TMDS; mask = 2; break;
-               case 0x00000500: type = DCB_OUTPUT_TMDS; mask = 3; break;
-               case 0x00000800: type = DCB_OUTPUT_DP; mask = 1; break;
-               case 0x00000900: type = DCB_OUTPUT_DP; mask = 2; break;
-               default:
-                       nvkm_error(subdev, "unknown SOR mc %08x\n", ctrl);
-                       return NULL;
-               }
-               or  -= 4;
-       } else {
-               or   = or - 8;
-               type = 0x0010;
-               mask = 0;
-               switch (ctrl & 0x00000f00) {
-               case 0x00000000: type |= disp->pior.type[or]; break;
-               default:
-                       nvkm_error(subdev, "unknown PIOR mc %08x\n", ctrl);
-                       return NULL;
-               }
-       }
-
-       mask  = 0x00c0 & (mask << 6);
-       mask |= 0x0001 << or;
-       mask |= 0x0100 << head;
-
-       list_for_each_entry(outp, &disp->base.outp, head) {
-               if ((outp->info.hasht & 0xff) == type &&
-                   (outp->info.hashm & mask) == mask) {
-                       *data = nvbios_outp_match(bios, outp->info.hasht, mask,
-                                                 ver, hdr, cnt, len, info);
-                       if (!*data)
-                               return NULL;
-                       return outp;
-               }
-       }
-
-       return NULL;
-}
-
-static struct nvkm_output *
-exec_clkcmp(struct nv50_disp *disp, int head, int id, u32 pclk, u32 *conf)
-{
-       struct nvkm_subdev *subdev = &disp->base.engine.subdev;
-       struct nvkm_device *device = subdev->device;
-       struct nvkm_bios *bios = device->bios;
-       struct nvkm_output *outp;
-       struct nvbios_outp info1;
-       struct nvbios_ocfg info2;
-       u8  ver, hdr, cnt, len;
-       u32 data, ctrl = 0;
-       u32 reg;
-       int i;
-
-       /* DAC */
-       for (i = 0; !(ctrl & (1 << head)) && i < disp->func->dac.nr; i++)
-               ctrl = nvkm_rd32(device, 0x610b58 + (i * 8));
-
-       /* SOR */
-       if (!(ctrl & (1 << head))) {
-               if (device->chipset  < 0x90 ||
-                   device->chipset == 0x92 ||
-                   device->chipset == 0xa0) {
-                       reg = 0x610b70;
-               } else {
-                       reg = 0x610794;
-               }
-               for (i = 0; !(ctrl & (1 << head)) && i < disp->func->sor.nr; i++)
-                       ctrl = nvkm_rd32(device, reg + (i * 8));
-               i += 4;
-       }
-
-       /* PIOR */
-       if (!(ctrl & (1 << head))) {
-               for (i = 0; !(ctrl & (1 << head)) && i < disp->func->pior.nr; i++)
-                       ctrl = nvkm_rd32(device, 0x610b80 + (i * 8));
-               i += 8;
-       }
-
-       if (!(ctrl & (1 << head)))
-               return NULL;
-       i--;
-
-       outp = exec_lookup(disp, head, i, ctrl, &data, &ver, &hdr, &cnt, &len, &info1);
-       if (!outp)
-               return NULL;
-
-       *conf = (ctrl & 0x00000f00) >> 8;
-       if (outp->info.location == 0) {
-               switch (outp->info.type) {
-               case DCB_OUTPUT_TMDS:
-                       if (*conf == 5)
-                               *conf |= 0x0100;
-                       break;
-               case DCB_OUTPUT_LVDS:
-                       *conf |= disp->sor.lvdsconf;
-                       break;
-               default:
-                       break;
-               }
-       } else {
-               *conf = (ctrl & 0x00000f00) >> 8;
-               pclk = pclk / 2;
-       }
-
-       data = nvbios_ocfg_match(bios, data, *conf & 0xff, *conf >> 8,
-                                &ver, &hdr, &cnt, &len, &info2);
-       if (data && id < 0xff) {
-               data = nvbios_oclk_match(bios, info2.clkcmp[id], pclk);
-               if (data) {
-                       struct nvbios_init init = {
-                               .subdev = subdev,
-                               .bios = bios,
-                               .offset = data,
-                               .outp = &outp->info,
-                               .crtc = head,
-                               .execute = 1,
-                       };
-
-                       nvbios_exec(&init);
-               }
-       }
-
-       return outp;
-}
-
-static void
-nv50_disp_intr_unk40_0(struct nv50_disp *disp, int head)
+void
+nv50_disp_super_3_0(struct nv50_disp *disp, struct nvkm_head *head)
 {
-       struct nvkm_device *device = disp->base.engine.subdev.device;
-       struct nvkm_output *outp;
-       u32 pclk = nvkm_rd32(device, 0x610ad0 + (head * 0x540)) & 0x3fffff;
-       u32 conf;
+       struct nvkm_ior *ior;
 
-       outp = exec_clkcmp(disp, head, 1, pclk, &conf);
-       if (!outp)
+       /* Determine which OR, if any, we're attaching to the head. */
+       HEAD_DBG(head, "supervisor 3.0");
+       ior = nv50_disp_super_ior_asy(head);
+       if (!ior)
                return;
 
-       nv50_disp_dptmds_war_3(disp, &outp->info);
+       /* Execute OnInt3 IED script. */
+       nv50_disp_super_ied_on(head, ior, 1, head->asy.hz / 1000);
+
+       /* OR-specific handling. */
+       if (ior->func->war_3)
+               ior->func->war_3(ior);
 }
 
 static void
@@ -660,9 +521,8 @@ nv50_disp_super(struct work_struct *work)
                list_for_each_entry(head, &disp->base.head, head) {
                        if (!(super & (0x00000080 << head->id)))
                                continue;
-                       nv50_disp_intr_unk40_0(disp, head->id);
+                       nv50_disp_super_3_0(disp, head);
                }
-               nv50_disp_update_sppll1(disp);
        }
 
        nvkm_wr32(device, 0x610030, 0x80000000);
index b6092b8..19c6356 100644 (file)
@@ -30,6 +30,7 @@ void nv50_disp_super_1_0(struct nv50_disp *, struct nvkm_head *);
 void nv50_disp_super_2_0(struct nv50_disp *, struct nvkm_head *);
 void nv50_disp_super_2_1(struct nv50_disp *, struct nvkm_head *);
 void nv50_disp_super_2_2(struct nv50_disp *, struct nvkm_head *);
+void nv50_disp_super_3_0(struct nv50_disp *, struct nvkm_head *);
 
 int nv50_disp_new_(const struct nv50_disp_func *, struct nvkm_device *,
                   int index, int heads, struct nvkm_disp **);
index 7655d92..146d101 100644 (file)
@@ -43,10 +43,6 @@ struct nvkm_outp_func {
        void (*release)(struct nvkm_outp *, struct nvkm_ior *);
 };
 
-#define nvkm_output nvkm_outp
-#define nvkm_output_func nvkm_outp_func
-#define nvkm_output_new_ nvkm_outp_new_
-
 #define OUTP_MSG(o,l,f,a...) do {                                              \
        struct nvkm_outp *_outp = (o);                                         \
        nvkm_##l(&_outp->disp->engine.subdev, "outp %02x:%04x:%04x: "f"\n",    \
index bdace38..771a1ab 100644 (file)
@@ -22,7 +22,6 @@
  * Authors: Ben Skeggs
  */
 #include "ior.h"
-#include "nv50.h"
 
 #include <subdev/timer.h>
 
@@ -121,38 +120,6 @@ g94_sor_dp_links(struct nvkm_ior *sor, struct nvkm_i2c_aux *aux)
 }
 
 static bool
-nv50_disp_dptmds_war(struct nvkm_device *device)
-{
-       switch (device->chipset) {
-       case 0x94:
-       case 0x96:
-       case 0x98:
-               return true;
-       default:
-               break;
-       }
-       return false;
-}
-
-static bool
-nv50_disp_dptmds_war_needed(struct nv50_disp *disp, struct dcb_output *outp)
-{
-       struct nvkm_device *device = disp->base.engine.subdev.device;
-       const u32 soff = __ffs(outp->or) * 0x800;
-       if (nv50_disp_dptmds_war(device) && outp->type == DCB_OUTPUT_TMDS) {
-               switch (nvkm_rd32(device, 0x614300 + soff) & 0x00030000) {
-               case 0x00000000:
-               case 0x00030000:
-                       return true;
-               default:
-                       break;
-               }
-       }
-       return false;
-
-}
-
-static bool
 g94_sor_war_needed(struct nvkm_ior *sor)
 {
        struct nvkm_device *device = sor->disp->engine.subdev.device;
@@ -169,18 +136,19 @@ g94_sor_war_needed(struct nvkm_ior *sor)
        return false;
 }
 
-void
-nv50_disp_update_sppll1(struct nv50_disp *disp)
+static void
+g94_sor_war_update_sppll1(struct nvkm_disp *disp)
 {
-       struct nvkm_device *device = disp->base.engine.subdev.device;
+       struct nvkm_device *device = disp->engine.subdev.device;
+       struct nvkm_ior *ior;
        bool used = false;
-       int sor;
+       u32 clksor;
 
-       if (!nv50_disp_dptmds_war(device))
-               return;
+       list_for_each_entry(ior, &disp->ior, head) {
+               if (ior->type != SOR)
+                       continue;
 
-       for (sor = 0; sor < disp->func->sor.nr; sor++) {
-               u32 clksor = nvkm_rd32(device, 0x614300 + (sor * 0x800));
+               clksor = nvkm_rd32(device, 0x614300 + nv50_ior_base(ior));
                switch (clksor & 0x03000000) {
                case 0x02000000:
                case 0x03000000:
@@ -197,14 +165,14 @@ nv50_disp_update_sppll1(struct nv50_disp *disp)
        nvkm_mask(device, 0x00e840, 0x80000000, 0x00000000);
 }
 
-void
-nv50_disp_dptmds_war_3(struct nv50_disp *disp, struct dcb_output *outp)
+static void
+g94_sor_war_3(struct nvkm_ior *sor)
 {
-       struct nvkm_device *device = disp->base.engine.subdev.device;
-       const u32 soff = __ffs(outp->or) * 0x800;
+       struct nvkm_device *device = sor->disp->engine.subdev.device;
+       const u32 soff = nv50_ior_base(sor);
        u32 sorpwr;
 
-       if (!nv50_disp_dptmds_war_needed(disp, outp))
+       if (!g94_sor_war_needed(sor))
                return;
 
        sorpwr = nvkm_rd32(device, 0x61c004 + soff);
@@ -235,6 +203,8 @@ nv50_disp_dptmds_war_3(struct nv50_disp *disp, struct dcb_output *outp)
        if (sorpwr & 0x00000001) {
                nvkm_mask(device, 0x61c004 + soff, 0x80000001, 0x80000001);
        }
+
+       g94_sor_war_update_sppll1(sor->disp);
 }
 
 static void
@@ -293,6 +263,7 @@ g94_sor = {
        .power = nv50_sor_power,
        .clock = nv50_sor_clock,
        .war_2 = g94_sor_war_2,
+       .war_3 = g94_sor_war_3,
        .dp = {
                .lanes = { 2, 1, 0, 3},
                .links = g94_sor_dp_links,
index 2095f43..b58ee99 100644 (file)
@@ -2278,11 +2278,6 @@ nvbios_exec(struct nvbios_init *init)
 {
        struct nvkm_bios *bios = init->subdev->device->bios;
 
-       if (init->bios) {
-               init->or = init->outp ? ffs(init->outp->or) - 1 : -1;
-               init->link = init->outp ? init->outp->sorconf.link : 0;
-       }
-
        init->nested++;
        while (init->offset) {
                u8 opcode = nvbios_rd08(bios, init->offset);