drm/nouveau/disp/nv50-: implement a common supervisor 2.2
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>
25 files changed:
drivers/gpu/drm/nouveau/nvkm/engine/disp/channv50.c
drivers/gpu/drm/nouveau/nvkm/engine/disp/dacgf119.c
drivers/gpu/drm/nouveau/nvkm/engine/disp/dacnv50.c
drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c
drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.h
drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c
drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h
drivers/gpu/drm/nouveau/nvkm/engine/disp/headgf119.c
drivers/gpu/drm/nouveau/nvkm/engine/disp/headnv50.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/piornv50.c
drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv50.c
drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg84.c
drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg94.c
drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c
drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgk104.c
drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm107.c
drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm200.c
drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgt215.c
drivers/gpu/drm/nouveau/nvkm/engine/disp/sormcp77.c
drivers/gpu/drm/nouveau/nvkm/engine/disp/sormcp89.c
drivers/gpu/drm/nouveau/nvkm/engine/disp/sornv50.c

index 524a24eae1a0da5d39688dc7af255fd33da034d6..0c0310498afdb99e59459b5e70873bc7ad6a5f55 100644 (file)
@@ -25,6 +25,7 @@
 #include "rootnv50.h"
 
 #include <core/client.h>
+#include <core/notify.h>
 #include <core/ramht.h>
 #include <engine/dma.h>
 
index 7d2d929eec16bb67d3b0e4d6993c8ea71c5f9ece..8392303b77ed4e83a93050c63d8c19fdc216afc3 100644 (file)
  */
 #include "ior.h"
 
+static void
+gf119_dac_clock(struct nvkm_ior *dac)
+{
+       struct nvkm_device *device = dac->disp->engine.subdev.device;
+       const u32 doff = nv50_ior_base(dac);
+       nvkm_mask(device, 0x612280 + doff, 0x07070707, 0x00000000);
+}
+
 static void
 gf119_dac_state(struct nvkm_ior *dac, struct nvkm_ior_state *state)
 {
@@ -44,6 +52,7 @@ gf119_dac = {
        .state = gf119_dac_state,
        .power = nv50_dac_power,
        .sense = nv50_dac_sense,
+       .clock = gf119_dac_clock,
 };
 
 int
index 64de64fdc9d1bb1bb34e2245a10b3447178dcd76..119cd02a2000202e93a7c1e92d74991cb1c148ae 100644 (file)
 
 #include <subdev/timer.h>
 
+static void
+nv50_dac_clock(struct nvkm_ior *dac)
+{
+       struct nvkm_device *device = dac->disp->engine.subdev.device;
+       const u32 doff = nv50_ior_base(dac);
+       nvkm_mask(device, 0x614280 + doff, 0x07070707, 0x00000000);
+}
+
 int
 nv50_dac_sense(struct nvkm_ior *dac, u32 loadval)
 {
@@ -95,6 +103,7 @@ nv50_dac = {
        .state = nv50_dac_state,
        .power = nv50_dac_power,
        .sense = nv50_dac_sense,
+       .clock = nv50_dac_clock,
 };
 
 int
index 12e52529413cc48e4fca2aa901a10ca3e93cf7a5..7c5bed29ffef164224825a3f9e0eaac7aa786239 100644 (file)
@@ -428,8 +428,8 @@ nvkm_dp_release(struct nvkm_outp *outp, struct nvkm_ior *ior)
        );
 }
 
-int
-nvkm_output_dp_train(struct nvkm_outp *outp, u32 unused)
+static int
+nvkm_dp_acquire(struct nvkm_outp *outp)
 {
        struct nvkm_dp *dp = nvkm_dp(outp);
        struct nvkm_ior *ior = dp->outp.ior;
@@ -529,7 +529,7 @@ nvkm_dp_hpd(struct nvkm_notify *notify)
        OUTP_DBG(&dp->outp, "HPD: %d", line->mask);
        if (line->mask & NVKM_I2C_IRQ) {
                if (atomic_read(&dp->lt.done))
-                       nvkm_output_dp_train(&dp->outp, 0);
+                       dp->outp.func->acquire(&dp->outp);
                rep.mask |= NVIF_NOTIFY_CONN_V0_IRQ;
        } else {
                nvkm_dp_enable(dp, true);
@@ -574,6 +574,7 @@ nvkm_dp_func = {
        .dtor = nvkm_dp_dtor,
        .init = nvkm_dp_init,
        .fini = nvkm_dp_fini,
+       .acquire = nvkm_dp_acquire,
        .release = nvkm_dp_release,
 };
 
index 34e58b70a3949a55971f8c9e10107aeac97b7111..59173c2905253961f59fcad611b2dff37c5b0af3 100644 (file)
@@ -29,10 +29,6 @@ struct nvkm_dp {
        } lt;
 };
 
-#define nvkm_output_dp nvkm_dp
-
-int nvkm_output_dp_train(struct nvkm_output *, u32 rate);
-
 int nvkm_dp_new(struct nvkm_disp *, int index, struct dcb_output *,
                struct nvkm_outp **);
 
index d25b3b7f4fb8b92a9191030b644e4fa51aa43c4a..f4a05549f642dfdaff2585a411a289bfce8953d5 100644 (file)
@@ -138,120 +138,6 @@ exec_clkcmp(struct nv50_disp *disp, int head, int id, u32 pclk, u32 *conf)
        return outp;
 }
 
-static void
-gf119_disp_intr_unk2_2_tu(struct nv50_disp *disp, int head,
-                         struct dcb_output *outp)
-{
-       struct nvkm_device *device = disp->base.engine.subdev.device;
-       const int or = ffs(outp->or) - 1;
-       const u32 ctrl = nvkm_rd32(device, 0x660200 + (or   * 0x020));
-       const u32 conf = nvkm_rd32(device, 0x660404 + (head * 0x300));
-       const s32 vactive = nvkm_rd32(device, 0x660414 + (head * 0x300)) & 0xffff;
-       const s32 vblanke = nvkm_rd32(device, 0x66041c + (head * 0x300)) & 0xffff;
-       const s32 vblanks = nvkm_rd32(device, 0x660420 + (head * 0x300)) & 0xffff;
-       const u32 pclk = nvkm_rd32(device, 0x660450 + (head * 0x300)) / 1000;
-       const u32 link = ((ctrl & 0xf00) == 0x800) ? 0 : 1;
-       const u32 hoff = (head * 0x800);
-       const u32 soff = (  or * 0x800);
-       const u32 loff = (link * 0x080) + soff;
-       const u32 symbol = 100000;
-       const u32 TU = 64;
-       u32 dpctrl = nvkm_rd32(device, 0x61c10c + loff);
-       u32 clksor = nvkm_rd32(device, 0x612300 + soff);
-       u32 datarate, link_nr, link_bw, bits;
-       u64 ratio, value;
-
-       link_nr  = hweight32(dpctrl & 0x000f0000);
-       link_bw  = (clksor & 0x007c0000) >> 18;
-       link_bw *= 27000;
-
-       /* symbols/hblank - algorithm taken from comments in tegra driver */
-       value = vblanke + vactive - vblanks - 7;
-       value = value * link_bw;
-       do_div(value, pclk);
-       value = value - (3 * !!(dpctrl & 0x00004000)) - (12 / link_nr);
-       nvkm_mask(device, 0x616620 + hoff, 0x0000ffff, value);
-
-       /* symbols/vblank - algorithm taken from comments in tegra driver */
-       value = vblanks - vblanke - 25;
-       value = value * link_bw;
-       do_div(value, pclk);
-       value = value - ((36 / link_nr) + 3) - 1;
-       nvkm_mask(device, 0x616624 + hoff, 0x00ffffff, value);
-
-       /* watermark */
-       if      ((conf & 0x3c0) == 0x180) bits = 30;
-       else if ((conf & 0x3c0) == 0x140) bits = 24;
-       else                              bits = 18;
-       datarate = (pclk * bits) / 8;
-
-       ratio  = datarate;
-       ratio *= symbol;
-       do_div(ratio, link_nr * link_bw);
-
-       value  = (symbol - ratio) * TU;
-       value *= ratio;
-       do_div(value, symbol);
-       do_div(value, symbol);
-
-       value += 5;
-       value |= 0x08000000;
-
-       nvkm_wr32(device, 0x616610 + hoff, value);
-}
-
-static void
-gf119_disp_intr_unk2_2(struct nv50_disp *disp, int head)
-{
-       struct nvkm_device *device = disp->base.engine.subdev.device;
-       struct nvkm_output *outp;
-       u32 pclk = nvkm_rd32(device, 0x660450 + (head * 0x300)) / 1000;
-       u32 conf, addr, data;
-
-       outp = exec_clkcmp(disp, head, 0xff, pclk, &conf);
-       if (!outp)
-               return;
-
-       /* see note in nv50_disp_intr_unk20_2() */
-       if (outp->info.type == DCB_OUTPUT_DP) {
-               u32 sync = nvkm_rd32(device, 0x660404 + (head * 0x300));
-               switch ((sync & 0x000003c0) >> 6) {
-               case 6: pclk = pclk * 30; break;
-               case 5: pclk = pclk * 24; break;
-               case 2:
-               default:
-                       pclk = pclk * 18;
-                       break;
-               }
-
-               if (nvkm_output_dp_train(outp, pclk))
-                       OUTP_ERR(outp, "link not trained before attach");
-       }
-
-       exec_clkcmp(disp, head, 0, pclk, &conf);
-
-       if (outp->info.type == DCB_OUTPUT_ANALOG) {
-               addr = 0x612280 + (ffs(outp->info.or) - 1) * 0x800;
-               data = 0x00000000;
-       } else {
-               addr = 0x612300 + (ffs(outp->info.or) - 1) * 0x800;
-               data = (conf & 0x0100) ? 0x00000101 : 0x00000000;
-               switch (outp->info.type) {
-               case DCB_OUTPUT_TMDS:
-                       nvkm_mask(device, addr, 0x007c0000, 0x00280000);
-                       break;
-               case DCB_OUTPUT_DP:
-                       gf119_disp_intr_unk2_2_tu(disp, head, &outp->info);
-                       break;
-               default:
-                       break;
-               }
-       }
-
-       nvkm_mask(device, addr, 0x00000707, data);
-       nvkm_wr32(device, 0x612200 + (head * 0x800), 0x00000000);
-}
-
 static void
 gf119_disp_intr_unk4_0(struct nv50_disp *disp, int head)
 {
@@ -302,8 +188,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 2.2 - head %d\n", head->id);
-                       gf119_disp_intr_unk2_2(disp, head->id);
+                       nv50_disp_super_2_2(disp, head);
                }
        } else
        if (disp->super & 0x00000004) {
index 829a0a8cfb2e7d267fa0ab9674eb3916254482bc..b04c49d2eeeb986fec4dbc88d371bde14dbb16fa 100644 (file)
@@ -36,6 +36,7 @@ struct nvkm_head *nvkm_head_find(struct nvkm_disp *, int id);
 struct nvkm_head_func {
        void (*state)(struct nvkm_head *, struct nvkm_head_state *);
        void (*rgpos)(struct nvkm_head *, u16 *hline, u16 *vline);
+       void (*rgclk)(struct nvkm_head *, int div);
        void (*vblank_get)(struct nvkm_head *);
        void (*vblank_put)(struct nvkm_head *);
 };
index d2bd6bb4a6215455a1cb661618e6fc2cd1990f75..b335527576472c252cbdf7f6457fdebf2d928331 100644 (file)
@@ -39,6 +39,13 @@ gf119_head_vblank_get(struct nvkm_head *head)
        nvkm_mask(device, 0x6100c0 + hoff, 0x00000001, 0x00000001);
 }
 
+static void
+gf119_head_rgclk(struct nvkm_head *head, int div)
+{
+       struct nvkm_device *device = head->disp->engine.subdev.device;
+       nvkm_mask(device, 0x612200 + (head->id * 0x800), 0x0000000f, div);
+}
+
 static void
 gf119_head_state(struct nvkm_head *head, struct nvkm_head_state *state)
 {
@@ -77,6 +84,7 @@ static const struct nvkm_head_func
 gf119_head = {
        .state = gf119_head_state,
        .rgpos = nv50_head_rgpos,
+       .rgclk = gf119_head_rgclk,
        .vblank_get = gf119_head_vblank_get,
        .vblank_put = gf119_head_vblank_put,
 };
index d4a9879f0d0a3dc71beb5166fd6e727494621517..c80d06d5168f5b01a528486897036a0d549923ba 100644 (file)
@@ -37,6 +37,13 @@ nv50_head_vblank_get(struct nvkm_head *head)
        nvkm_mask(device, 0x61002c, (4 << head->id), (4 << head->id));
 }
 
+static void
+nv50_head_rgclk(struct nvkm_head *head, int div)
+{
+       struct nvkm_device *device = head->disp->engine.subdev.device;
+       nvkm_mask(device, 0x614200 + (head->id * 0x800), 0x0000000f, div);
+}
+
 void
 nv50_head_rgpos(struct nvkm_head *head, u16 *hline, u16 *vline)
 {
@@ -73,6 +80,7 @@ static const struct nvkm_head_func
 nv50_head = {
        .state = nv50_head_state,
        .rgpos = nv50_head_rgpos,
+       .rgclk = nv50_head_rgclk,
        .vblank_get = nv50_head_vblank_get,
        .vblank_put = nv50_head_vblank_put,
 };
index 3a6e01f5a0e5f9a36ef2a3ce99ea96abca030ed9..d6bfaa53f96b237a810788ddfc526a327c98330d 100644 (file)
@@ -50,6 +50,8 @@ struct nvkm_ior_func {
        void (*power)(struct nvkm_ior *, bool normal, bool pu,
                      bool data, bool vsync, bool hsync);
        int (*sense)(struct nvkm_ior *, u32 loadval);
+       void (*clock)(struct nvkm_ior *);
+       void (*war_2)(struct nvkm_ior *);
 
        struct {
                void (*ctrl)(struct nvkm_ior *, int head, bool enable,
@@ -67,6 +69,10 @@ struct nvkm_ior_func {
                void (*vcpi)(struct nvkm_ior *, int head, u8 slot,
                             u8 slot_nr, u16 pbn, u16 aligned);
                void (*audio)(struct nvkm_ior *, int head, bool enable);
+               void (*audio_sym)(struct nvkm_ior *, int head, u16 h, u32 v);
+               void (*activesym)(struct nvkm_ior *, int head,
+                                 u8 TU, u8 VTUa, u8 VTUf, u8 VTUi);
+               void (*watermark)(struct nvkm_ior *, int head, u8 watermark);
        } dp;
 
        struct {
@@ -99,21 +105,28 @@ nv50_sor_link(struct nvkm_ior *ior)
 
 void nv50_sor_state(struct nvkm_ior *, struct nvkm_ior_state *);
 void nv50_sor_power(struct nvkm_ior *, bool, bool, bool, bool, bool);
+void nv50_sor_clock(struct nvkm_ior *);
 
 void g94_sor_state(struct nvkm_ior *, struct nvkm_ior_state *);
 int g94_sor_dp_links(struct nvkm_ior *, struct nvkm_i2c_aux *);
 void g94_sor_dp_power(struct nvkm_ior *, int);
 void g94_sor_dp_pattern(struct nvkm_ior *, int);
 void g94_sor_dp_drive(struct nvkm_ior *, int, int, int, int, int);
+void g94_sor_dp_audio_sym(struct nvkm_ior *, int, u16, u32);
+void g94_sor_dp_activesym(struct nvkm_ior *, int, u8, u8, u8, u8);
+void g94_sor_dp_watermark(struct nvkm_ior *, int, u8);
 
 void gt215_sor_dp_audio(struct nvkm_ior *, int, bool);
 
 void gf119_sor_state(struct nvkm_ior *, struct nvkm_ior_state *);
+void gf119_sor_clock(struct nvkm_ior *);
 int gf119_sor_dp_links(struct nvkm_ior *, struct nvkm_i2c_aux *);
 void gf119_sor_dp_pattern(struct nvkm_ior *, int);
 void gf119_sor_dp_drive(struct nvkm_ior *, int, int, int, int, int);
 void gf119_sor_dp_vcpi(struct nvkm_ior *, int, u8, u8, u16, u16);
 void gf119_sor_dp_audio(struct nvkm_ior *, int, bool);
+void gf119_sor_dp_audio_sym(struct nvkm_ior *, int, u16, u32);
+void gf119_sor_dp_watermark(struct nvkm_ior *, int, u8);
 
 void gm107_sor_dp_pattern(struct nvkm_ior *, int);
 
index ebe7657bf2afdfabd0007abd32bf5fd604039075..f21e0e92d6f1a65acb46e4a651c1ca8f84d87378 100644 (file)
@@ -129,6 +129,62 @@ nv50_disp_super_iedt(struct nvkm_head *head, struct nvkm_outp *outp,
        return data;
 }
 
+static void
+nv50_disp_super_ied_on(struct nvkm_head *head,
+                      struct nvkm_ior *ior, int id, u32 khz)
+{
+       struct nvkm_subdev *subdev = &head->disp->engine.subdev;
+       struct nvkm_bios *bios = subdev->device->bios;
+       struct nvkm_outp *outp = ior->asy.outp;
+       struct nvbios_ocfg iedtrs;
+       struct nvbios_outp iedt;
+       u8  ver, hdr, cnt, len, flags = 0x00;
+       u32 data;
+
+       if (!outp) {
+               IOR_DBG(ior, "nothing to attach");
+               return;
+       }
+
+       /* Lookup IED table for the device. */
+       data = nv50_disp_super_iedt(head, outp, &ver, &hdr, &cnt, &len, &iedt);
+       if (!data)
+               return;
+
+       /* Lookup IEDT runtime settings for the current configuration. */
+       if (ior->type == SOR) {
+               if (ior->asy.proto == LVDS) {
+                       if (head->asy.or.depth == 24)
+                               flags |= 0x02;
+               }
+               if (ior->asy.link == 3)
+                       flags |= 0x01;
+       }
+
+       data = nvbios_ocfg_match(bios, data, ior->asy.proto_evo, flags,
+                                &ver, &hdr, &cnt, &len, &iedtrs);
+       if (!data) {
+               OUTP_DBG(outp, "missing IEDT RS for %02x:%02x",
+                        ior->asy.proto_evo, flags);
+               return;
+       }
+
+       /* Execute the OnInt[23] script for the current frequency. */
+       data = nvbios_oclk_match(bios, iedtrs.clkcmp[id], khz);
+       if (!data) {
+               OUTP_DBG(outp, "missing IEDT RSS %d for %02x:%02x %d khz",
+                        id, ior->asy.proto_evo, flags, khz);
+               return;
+       }
+
+       nvbios_init(subdev, data,
+               init.outp = &outp->info;
+               init.or   = ior->id;
+               init.link = ior->asy.link;
+               init.head = head->id;
+       );
+}
+
 static void
 nv50_disp_super_ied_off(struct nvkm_head *head, struct nvkm_ior *ior, int id)
 {
@@ -154,6 +210,20 @@ nv50_disp_super_ied_off(struct nvkm_head *head, struct nvkm_ior *ior, int id)
        );
 }
 
+static struct nvkm_ior *
+nv50_disp_super_ior_asy(struct nvkm_head *head)
+{
+       struct nvkm_ior *ior;
+       list_for_each_entry(ior, &head->disp->ior, head) {
+               if (ior->asy.head & (1 << head->id)) {
+                       HEAD_DBG(head, "to %s", ior->name);
+                       return ior;
+               }
+       }
+       HEAD_DBG(head, "nothing to attach");
+       return NULL;
+}
+
 static struct nvkm_ior *
 nv50_disp_super_ior_arm(struct nvkm_head *head)
 {
@@ -327,58 +397,40 @@ nv50_disp_intr_unk40_0(struct nv50_disp *disp, int head)
 }
 
 static void
-nv50_disp_intr_unk20_2_dp(struct nv50_disp *disp, int head,
-                         struct dcb_output *outp, u32 pclk)
+nv50_disp_super_2_2_dp(struct nvkm_head *head, struct nvkm_ior *ior)
 {
-       struct nvkm_subdev *subdev = &disp->base.engine.subdev;
-       struct nvkm_device *device = subdev->device;
-       const int link = !(outp->sorconf.link & 1);
-       const int   or = ffs(outp->or) - 1;
-       const u32 soff = (  or * 0x800);
-       const u32 loff = (link * 0x080) + soff;
-       const u32 ctrl = nvkm_rd32(device, 0x610794 + (or * 8));
-       const u32 symbol = 100000;
-       const s32 vactive = nvkm_rd32(device, 0x610af8 + (head * 0x540)) & 0xffff;
-       const s32 vblanke = nvkm_rd32(device, 0x610ae8 + (head * 0x540)) & 0xffff;
-       const s32 vblanks = nvkm_rd32(device, 0x610af0 + (head * 0x540)) & 0xffff;
-       u32 dpctrl = nvkm_rd32(device, 0x61c10c + loff);
-       u32 clksor = nvkm_rd32(device, 0x614300 + soff);
+       struct nvkm_subdev *subdev = &head->disp->engine.subdev;
+       const u32      khz = head->asy.hz / 1000;
+       const u32 linkKBps = ior->dp.bw * 27000;
+       const u32   symbol = 100000;
        int bestTU = 0, bestVTUi = 0, bestVTUf = 0, bestVTUa = 0;
        int TU, VTUi, VTUf, VTUa;
        u64 link_data_rate, link_ratio, unk;
        u32 best_diff = 64 * symbol;
-       u32 link_nr, link_bw, bits;
-       u64 value;
-
-       link_bw = (clksor & 0x000c0000) ? 270000 : 162000;
-       link_nr = hweight32(dpctrl & 0x000f0000);
+       u64 h, v;
 
        /* symbols/hblank - algorithm taken from comments in tegra driver */
-       value = vblanke + vactive - vblanks - 7;
-       value = value * link_bw;
-       do_div(value, pclk);
-       value = value - (3 * !!(dpctrl & 0x00004000)) - (12 / link_nr);
-       nvkm_mask(device, 0x61c1e8 + soff, 0x0000ffff, value);
+       h = head->asy.hblanke + head->asy.htotal - head->asy.hblanks - 7;
+       h = h * linkKBps;
+       do_div(h, khz);
+       h = h - (3 * ior->dp.ef) - (12 / ior->dp.nr);
 
        /* symbols/vblank - algorithm taken from comments in tegra driver */
-       value = vblanks - vblanke - 25;
-       value = value * link_bw;
-       do_div(value, pclk);
-       value = value - ((36 / link_nr) + 3) - 1;
-       nvkm_mask(device, 0x61c1ec + soff, 0x00ffffff, value);
+       v = head->asy.vblanks - head->asy.vblanke - 25;
+       v = v * linkKBps;
+       do_div(v, khz);
+       v = v - ((36 / ior->dp.nr) + 3) - 1;
 
-       /* watermark / activesym */
-       if      ((ctrl & 0xf0000) == 0x60000) bits = 30;
-       else if ((ctrl & 0xf0000) == 0x50000) bits = 24;
-       else                                  bits = 18;
+       ior->func->dp.audio_sym(ior, head->id, h, v);
 
-       link_data_rate = (pclk * bits / 8) / link_nr;
+       /* watermark / activesym */
+       link_data_rate = (khz * head->asy.or.depth / 8) / ior->dp.nr;
 
        /* calculate ratio of packed data rate to link symbol rate */
        link_ratio = link_data_rate * symbol;
-       do_div(link_ratio, link_bw);
+       do_div(link_ratio, linkKBps);
 
-       for (TU = 64; TU >= 32; TU--) {
+       for (TU = 64; ior->func->dp.activesym && TU >= 32; TU--) {
                /* calculate average number of valid symbols in each TU */
                u32 tu_valid = link_ratio * TU;
                u32 calc, diff;
@@ -429,9 +481,15 @@ nv50_disp_intr_unk20_2_dp(struct nv50_disp *disp, int head,
                }
        }
 
-       if (!bestTU) {
-               nvkm_error(subdev, "unable to find suitable dp config\n");
-               return;
+       if (ior->func->dp.activesym) {
+               if (!bestTU) {
+                       nvkm_error(subdev, "unable to determine dp config\n");
+                       return;
+               }
+               ior->func->dp.activesym(ior, head->id, bestTU,
+                                       bestVTUa, bestVTUf, bestVTUi);
+       } else {
+               bestTU = 64;
        }
 
        /* XXX close to vbios numbers, but not right */
@@ -441,102 +499,61 @@ nv50_disp_intr_unk20_2_dp(struct nv50_disp *disp, int head,
        do_div(unk, symbol);
        unk += 6;
 
-       nvkm_mask(device, 0x61c10c + loff, 0x000001fc, bestTU << 2);
-       nvkm_mask(device, 0x61c128 + loff, 0x010f7f3f, bestVTUa << 24 |
-                                                  bestVTUf << 16 |
-                                                  bestVTUi << 8 | unk);
+       ior->func->dp.watermark(ior, head->id, unk);
 }
 
-static void
-nv50_disp_intr_unk20_2(struct nv50_disp *disp, int head)
+void
+nv50_disp_super_2_2(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 hval, hreg = 0x614200 + (head * 0x800);
-       u32 oval, oreg;
-       u32 mask, conf;
+       const u32 khz = head->asy.hz / 1000;
+       struct nvkm_outp *outp;
+       struct nvkm_ior *ior;
 
-       outp = exec_clkcmp(disp, head, 0xff, pclk, &conf);
-       if (!outp)
+       /* Determine which OR, if any, we're attaching from the head. */
+       HEAD_DBG(head, "supervisor 2.2");
+       ior = nv50_disp_super_ior_asy(head);
+       if (!ior)
                return;
 
-       /* we allow both encoder attach and detach operations to occur
-        * within a single supervisor (ie. modeset) sequence.  the
-        * encoder detach scripts quite often switch off power to the
-        * lanes, which requires the link to be re-trained.
-        *
-        * this is not generally an issue as the sink "must" (heh)
-        * signal an irq when it's lost sync so the driver can
-        * re-train.
+       /* For some reason, NVIDIA decided not to:
         *
-        * however, on some boards, if one does not configure at least
-        * the gpu side of the link *before* attaching, then various
-        * things can go horribly wrong (PDISP disappearing from mmio,
-        * third supervisor never happens, etc).
+        * A) Give dual-link LVDS a separate EVO protocol, like for TMDS.
+        *  and
+        * B) Use SetControlOutputResource.PixelDepth on LVDS.
         *
-        * the solution is simply to retrain here, if necessary.  last
-        * i checked, the binary driver userspace does not appear to
-        * trigger this situation (it forces an UPDATE between steps).
+        * Override the values we usually read from HW with the same
+        * data we pass though an ioctl instead.
         */
-       if (outp->info.type == DCB_OUTPUT_DP) {
-               u32 soff = (ffs(outp->info.or) - 1) * 0x08;
-               u32 ctrl, datarate;
-
-               if (outp->info.location == 0) {
-                       ctrl = nvkm_rd32(device, 0x610794 + soff);
-                       soff = 1;
-               } else {
-                       ctrl = nvkm_rd32(device, 0x610b80 + soff);
-                       soff = 2;
-               }
-
-               switch ((ctrl & 0x000f0000) >> 16) {
-               case 6: datarate = pclk * 30; break;
-               case 5: datarate = pclk * 24; break;
-               case 2:
-               default:
-                       datarate = pclk * 18;
-                       break;
-               }
-
-               if (nvkm_output_dp_train(outp, datarate / soff))
-                       OUTP_ERR(outp, "link not trained before attach");
+       if (ior->type == SOR && ior->asy.proto == LVDS) {
+               head->asy.or.depth = (disp->sor.lvdsconf & 0x0200) ? 24 : 18;
+               ior->asy.link      = (disp->sor.lvdsconf & 0x0100) ? 3  : 1;
        }
 
-       exec_clkcmp(disp, head, 0, pclk, &conf);
+       /* Handle any link training, etc. */
+       if ((outp = ior->asy.outp) && outp->func->acquire)
+               outp->func->acquire(outp);
 
-       if (!outp->info.location && outp->info.type == DCB_OUTPUT_ANALOG) {
-               oreg = 0x614280 + (ffs(outp->info.or) - 1) * 0x800;
-               oval = 0x00000000;
-               hval = 0x00000000;
-               mask = 0xffffffff;
-       } else
-       if (!outp->info.location) {
-               if (outp->info.type == DCB_OUTPUT_DP)
-                       nv50_disp_intr_unk20_2_dp(disp, head, &outp->info, pclk);
-               oreg = 0x614300 + (ffs(outp->info.or) - 1) * 0x800;
-               oval = (conf & 0x0100) ? 0x00000101 : 0x00000000;
-               hval = 0x00000000;
-               mask = 0x00000707;
-       } else {
-               oreg = 0x614380 + (ffs(outp->info.or) - 1) * 0x800;
-               oval = 0x00000001;
-               hval = 0x00000001;
-               mask = 0x00000707;
-       }
+       /* Execute OnInt2 IED script. */
+       nv50_disp_super_ied_on(head, ior, 0, khz);
+
+       /* Program RG clock divider. */
+       head->func->rgclk(head, ior->asy.rgdiv);
 
-       nvkm_mask(device, hreg, 0x0000000f, hval);
-       nvkm_mask(device, oreg, mask, oval);
+       /* Mode-specific internal DP configuration. */
+       if (ior->type == SOR && ior->asy.proto == DP)
+               nv50_disp_super_2_2_dp(head, ior);
 
-       nv50_disp_dptmds_war_2(disp, &outp->info);
+       /* OR-specific handling. */
+       ior->func->clock(ior);
+       if (ior->func->war_2)
+               ior->func->war_2(ior);
 }
 
 void
 nv50_disp_super_2_1(struct nv50_disp *disp, struct nvkm_head *head)
 {
        struct nvkm_devinit *devinit = disp->base.engine.subdev.device->devinit;
-       u32 khz = head->asy.hz / 1000;
+       const u32 khz = head->asy.hz / 1000;
        HEAD_DBG(head, "supervisor 2.1 - %d khz", khz);
        if (khz)
                nvkm_devinit_pll_set(devinit, PLL_VPLL0 + head->id, khz);
@@ -636,7 +653,7 @@ 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_unk20_2(disp, head->id);
+                       nv50_disp_super_2_2(disp, head);
                }
        } else
        if (disp->super & 0x00000040) {
index 310a568c3fdb34cc12a09afbcb0687d8bd97294d..b6092b8b528a5f3c1ab58666c727daee26ed2502 100644 (file)
@@ -2,7 +2,6 @@
 #define __NV50_DISP_H__
 #define nv50_disp(p) container_of((p), struct nv50_disp, base)
 #include "priv.h"
-#include "dp.h"
 struct nvkm_head;
 
 struct nv50_disp {
@@ -30,6 +29,7 @@ void nv50_disp_super_1(struct nv50_disp *);
 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 *);
 
 int nv50_disp_new_(const struct nv50_disp_func *, struct nvkm_device *,
                   int index, int heads, struct nvkm_disp **);
index da4347e27e6568842f772c23f2dbdcaf37d603d1..7655d9293911ea509c8924694f9016175a5da584 100644 (file)
@@ -39,6 +39,7 @@ struct nvkm_outp_func {
        void *(*dtor)(struct nvkm_outp *);
        void (*init)(struct nvkm_outp *);
        void (*fini)(struct nvkm_outp *);
+       int (*acquire)(struct nvkm_outp *);
        void (*release)(struct nvkm_outp *, struct nvkm_ior *);
 };
 
index dc59c319377e806c39589658342e989822e1f3de..a1f24ef222c13c1b7b3784487c77b29058c14420 100644 (file)
 #include <subdev/i2c.h>
 #include <subdev/timer.h>
 
+static void
+nv50_pior_clock(struct nvkm_ior *pior)
+{
+       struct nvkm_device *device = pior->disp->engine.subdev.device;
+       const u32 poff = nv50_ior_base(pior);
+       nvkm_mask(device, 0x614380 + poff, 0x00000707, 0x00000001);
+}
+
 static int
 nv50_pior_dp_links(struct nvkm_ior *pior, struct nvkm_i2c_aux *aux)
 {
@@ -110,6 +118,7 @@ static const struct nvkm_ior_func
 nv50_pior = {
        .state = nv50_pior_state,
        .power = nv50_pior_power,
+       .clock = nv50_pior_clock,
        .dp = {
                .links = nv50_pior_dp_links,
        },
index 4c7b7090b1a065b0d14a40aceabf5aca27a89a4c..1208524aae14e7f8c16b064406851c2b2c33f2c2 100644 (file)
@@ -23,6 +23,7 @@
  */
 #include "rootnv50.h"
 #include "dmacnv50.h"
+#include "dp.h"
 #include "head.h"
 #include "ior.h"
 
index 8913b5f58103ff4d7e4ba5e730aa7c8d30fa1452..ec3a7db081188d6b46d206995ac08695d43af712 100644 (file)
@@ -25,6 +25,7 @@ static const struct nvkm_ior_func
 g84_sor = {
        .state = nv50_sor_state,
        .power = nv50_sor_power,
+       .clock = nv50_sor_clock,
        .hdmi = {
                .ctrl = g84_hdmi_ctrl,
        },
index 5abf563b0e84bdb5db28206480a2a8b821bbc36e..bdace3852b37399189a1318524b30a215956b8de 100644 (file)
 
 #include <subdev/timer.h>
 
+void
+g94_sor_dp_watermark(struct nvkm_ior *sor, int head, u8 watermark)
+{
+       struct nvkm_device *device = sor->disp->engine.subdev.device;
+       const u32 loff = nv50_sor_link(sor);
+       nvkm_mask(device, 0x61c128 + loff, 0x0000003f, watermark);
+}
+
+void
+g94_sor_dp_activesym(struct nvkm_ior *sor, int head,
+                    u8 TU, u8 VTUa, u8 VTUf, u8 VTUi)
+{
+       struct nvkm_device *device = sor->disp->engine.subdev.device;
+       const u32 loff = nv50_sor_link(sor);
+       nvkm_mask(device, 0x61c10c + loff, 0x000001fc, TU << 2);
+       nvkm_mask(device, 0x61c128 + loff, 0x010f7f00, VTUa << 24 |
+                                                      VTUf << 16 |
+                                                      VTUi << 8);
+}
+
+void
+g94_sor_dp_audio_sym(struct nvkm_ior *sor, int head, u16 h, u32 v)
+{
+       struct nvkm_device *device = sor->disp->engine.subdev.device;
+       const u32 soff = nv50_ior_base(sor);
+       nvkm_mask(device, 0x61c1e8 + soff, 0x0000ffff, h);
+       nvkm_mask(device, 0x61c1ec + soff, 0x00ffffff, v);
+}
+
 void
 g94_sor_dp_drive(struct nvkm_ior *sor, int ln, int pc, int dc, int pe, int pu)
 {
@@ -123,6 +152,23 @@ nv50_disp_dptmds_war_needed(struct nv50_disp *disp, struct dcb_output *outp)
 
 }
 
+static bool
+g94_sor_war_needed(struct nvkm_ior *sor)
+{
+       struct nvkm_device *device = sor->disp->engine.subdev.device;
+       const u32 soff = nv50_ior_base(sor);
+       if (sor->asy.proto == TMDS) {
+               switch (nvkm_rd32(device, 0x614300 + soff) & 0x00030000) {
+               case 0x00000000:
+               case 0x00030000:
+                       return true;
+               default:
+                       break;
+               }
+       }
+       return false;
+}
+
 void
 nv50_disp_update_sppll1(struct nv50_disp *disp)
 {
@@ -191,13 +237,13 @@ nv50_disp_dptmds_war_3(struct nv50_disp *disp, struct dcb_output *outp)
        }
 }
 
-void
-nv50_disp_dptmds_war_2(struct nv50_disp *disp, struct dcb_output *outp)
+static void
+g94_sor_war_2(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);
 
-       if (!nv50_disp_dptmds_war_needed(disp, outp))
+       if (!g94_sor_war_needed(sor))
                return;
 
        nvkm_mask(device, 0x00e840, 0x80000000, 0x80000000);
@@ -245,12 +291,17 @@ static const struct nvkm_ior_func
 g94_sor = {
        .state = g94_sor_state,
        .power = nv50_sor_power,
+       .clock = nv50_sor_clock,
+       .war_2 = g94_sor_war_2,
        .dp = {
                .lanes = { 2, 1, 0, 3},
                .links = g94_sor_dp_links,
                .power = g94_sor_dp_power,
                .pattern = g94_sor_dp_pattern,
                .drive = g94_sor_dp_drive,
+               .audio_sym = g94_sor_dp_audio_sym,
+               .activesym = g94_sor_dp_activesym,
+               .watermark = g94_sor_dp_watermark,
        },
 };
 
index d37cd037ee031276a3476d086c518f7e81254163..5f8306b00f715c259538327b34837c726e4dadbc 100644 (file)
 
 #include <subdev/timer.h>
 
+void
+gf119_sor_dp_watermark(struct nvkm_ior *sor, int head, u8 watermark)
+{
+       struct nvkm_device *device = sor->disp->engine.subdev.device;
+       const u32 hoff = head * 0x800;
+       nvkm_mask(device, 0x616610 + hoff, 0x0800003f, 0x08000000 | watermark);
+}
+
+void
+gf119_sor_dp_audio_sym(struct nvkm_ior *sor, int head, u16 h, u32 v)
+{
+       struct nvkm_device *device = sor->disp->engine.subdev.device;
+       const u32 hoff = head * 0x800;
+       nvkm_mask(device, 0x616620 + hoff, 0x0000ffff, h);
+       nvkm_mask(device, 0x616624 + hoff, 0x00ffffff, v);
+}
+
 void
 gf119_sor_dp_audio(struct nvkm_ior *sor, int head, bool enable)
 {
@@ -99,6 +116,19 @@ gf119_sor_dp_links(struct nvkm_ior *sor, struct nvkm_i2c_aux *aux)
        return 0;
 }
 
+void
+gf119_sor_clock(struct nvkm_ior *sor)
+{
+       struct nvkm_device *device = sor->disp->engine.subdev.device;
+       const int  div = sor->asy.link == 3;
+       const u32 soff = nv50_ior_base(sor);
+       if (sor->asy.proto == TMDS) {
+               /* NFI why, but this sets DP_LINK_BW_2_7 when using TMDS. */
+               nvkm_mask(device, 0x612300 + soff, 0x007c0000, 0x0a << 18);
+       }
+       nvkm_mask(device, 0x612300 + soff, 0x00000707, (div << 8) | div);
+}
+
 void
 gf119_sor_state(struct nvkm_ior *sor, struct nvkm_ior_state *state)
 {
@@ -126,6 +156,7 @@ static const struct nvkm_ior_func
 gf119_sor = {
        .state = gf119_sor_state,
        .power = nv50_sor_power,
+       .clock = gf119_sor_clock,
        .hdmi = {
                .ctrl = gf119_hdmi_ctrl,
        },
@@ -136,6 +167,8 @@ gf119_sor = {
                .pattern = gf119_sor_dp_pattern,
                .vcpi = gf119_sor_dp_vcpi,
                .audio = gf119_sor_dp_audio,
+               .audio_sym = gf119_sor_dp_audio_sym,
+               .watermark = gf119_sor_dp_watermark,
        },
        .hda = {
                .hpd = gf119_hda_hpd,
index 24fc026cd18d546ccda6fda9a160723adc18a8ee..b94090edaebff2b7462499c18339790d01dbde91 100644 (file)
@@ -25,6 +25,7 @@ static const struct nvkm_ior_func
 gk104_sor = {
        .state = gf119_sor_state,
        .power = nv50_sor_power,
+       .clock = gf119_sor_clock,
        .hdmi = {
                .ctrl = gk104_hdmi_ctrl,
        },
@@ -36,6 +37,8 @@ gk104_sor = {
                .drive = gf119_sor_dp_drive,
                .vcpi = gf119_sor_dp_vcpi,
                .audio = gf119_sor_dp_audio,
+               .audio_sym = gf119_sor_dp_audio_sym,
+               .watermark = gf119_sor_dp_watermark,
        },
        .hda = {
                .hpd = gf119_hda_hpd,
index e467f8eaf2eef95002f4b523494f0426026359f9..e6965dec09c968f6257649990c0eb78abec02b02 100644 (file)
@@ -39,6 +39,7 @@ static const struct nvkm_ior_func
 gm107_sor = {
        .state = gf119_sor_state,
        .power = nv50_sor_power,
+       .clock = gf119_sor_clock,
        .hdmi = {
                .ctrl = gk104_hdmi_ctrl,
        },
@@ -50,6 +51,8 @@ gm107_sor = {
                .drive = gf119_sor_dp_drive,
                .vcpi = gf119_sor_dp_vcpi,
                .audio = gf119_sor_dp_audio,
+               .audio_sym = gf119_sor_dp_audio_sym,
+               .watermark = gf119_sor_dp_watermark,
        },
        .hda = {
                .hpd = gf119_hda_hpd,
index 25528a15516b30c3042d628e96aa523843cf587d..8bc019b6ffabaedd67126d0d120e528bedeb65bb 100644 (file)
@@ -96,6 +96,7 @@ gm200_sor = {
        },
        .state = gf119_sor_state,
        .power = nv50_sor_power,
+       .clock = gf119_sor_clock,
        .hdmi = {
                .ctrl = gk104_hdmi_ctrl,
        },
@@ -107,6 +108,8 @@ gm200_sor = {
                .drive = gm200_sor_dp_drive,
                .vcpi = gf119_sor_dp_vcpi,
                .audio = gf119_sor_dp_audio,
+               .audio_sym = gf119_sor_dp_audio_sym,
+               .watermark = gf119_sor_dp_watermark,
        },
        .hda = {
                .hpd = gf119_hda_hpd,
index 5e9126e832ffefdfa5d262769d92d40f8097dc17..54d134d4ca1d129e8035006a192e1916545b2509 100644 (file)
@@ -41,6 +41,7 @@ static const struct nvkm_ior_func
 gt215_sor = {
        .state = g94_sor_state,
        .power = nv50_sor_power,
+       .clock = nv50_sor_clock,
        .hdmi = {
                .ctrl = gt215_hdmi_ctrl,
        },
@@ -51,6 +52,9 @@ gt215_sor = {
                .pattern = g94_sor_dp_pattern,
                .drive = g94_sor_dp_drive,
                .audio = gt215_sor_dp_audio,
+               .audio_sym = g94_sor_dp_audio_sym,
+               .activesym = g94_sor_dp_activesym,
+               .watermark = g94_sor_dp_watermark,
        },
        .hda = {
                .hpd = gt215_hda_hpd,
index 50d6c0d98e2005e7b04606ba7ab6335e24465e7a..8a70dd25b13a9f1ddbeeb8de570dcd26579f43e0 100644 (file)
@@ -25,6 +25,7 @@ static const struct nvkm_ior_func
 mcp77_sor = {
        .state = g94_sor_state,
        .power = nv50_sor_power,
+       .clock = nv50_sor_clock,
        .hdmi = {
                .ctrl = g84_hdmi_ctrl,
        },
@@ -34,6 +35,9 @@ mcp77_sor = {
                .power = g94_sor_dp_power,
                .pattern = g94_sor_dp_pattern,
                .drive = g94_sor_dp_drive,
+               .audio_sym = g94_sor_dp_audio_sym,
+               .activesym = g94_sor_dp_activesym,
+               .watermark = g94_sor_dp_watermark,
        },
 };
 
index dd76351d2ccb4bc1f90df663fabc4d77b51379cb..eac9c5be9166dd7e41d1cb9cf7cb3440567a884c 100644 (file)
@@ -25,6 +25,7 @@ static const struct nvkm_ior_func
 mcp89_sor = {
        .state = g94_sor_state,
        .power = nv50_sor_power,
+       .clock = nv50_sor_clock,
        .hdmi = {
                .ctrl = gt215_hdmi_ctrl,
        },
@@ -35,6 +36,9 @@ mcp89_sor = {
                .pattern = g94_sor_dp_pattern,
                .drive = g94_sor_dp_drive,
                .audio = gt215_sor_dp_audio,
+               .audio_sym = g94_sor_dp_audio_sym,
+               .activesym = g94_sor_dp_activesym,
+               .watermark = g94_sor_dp_watermark,
        },
        .hda = {
                .hpd = gt215_hda_hpd,
index 2ca600c744cd5528fa8d0be992ae051df7c15f88..dbb303db3178253f595bda542d18f9e012d28c0b 100644 (file)
 
 #include <subdev/timer.h>
 
+void
+nv50_sor_clock(struct nvkm_ior *sor)
+{
+       struct nvkm_device *device = sor->disp->engine.subdev.device;
+       const int  div = sor->asy.link == 3;
+       const u32 soff = nv50_ior_base(sor);
+       nvkm_mask(device, 0x614300 + soff, 0x00000707, (div << 8) | div);
+}
+
 static void
 nv50_sor_power_wait(struct nvkm_device *device, u32 soff)
 {
@@ -79,6 +88,7 @@ static const struct nvkm_ior_func
 nv50_sor = {
        .state = nv50_sor_state,
        .power = nv50_sor_power,
+       .clock = nv50_sor_clock,
 };
 
 int