drm/nv50/disp: move remaining interrupt handling into core
authorBen Skeggs <bskeggs@redhat.com>
Fri, 9 Nov 2012 02:09:48 +0000 (12:09 +1000)
committerBen Skeggs <bskeggs@redhat.com>
Wed, 28 Nov 2012 23:57:52 +0000 (09:57 +1000)
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
drivers/gpu/drm/nouveau/core/engine/disp/nv50.c
drivers/gpu/drm/nouveau/nouveau_irq.c
drivers/gpu/drm/nouveau/nv50_display.c
drivers/gpu/drm/nouveau/nv50_display.h
drivers/gpu/drm/nouveau/nv50_sor.c

index 1b1a384..c9cfd02 100644 (file)
 #include <engine/software.h>
 #include <engine/disp.h>
 
+#include <subdev/bios.h>
+#include <subdev/bios/dcb.h>
+#include <subdev/bios/disp.h>
+#include <subdev/bios/init.h>
+#include <subdev/bios/pll.h>
 #include <subdev/timer.h>
 #include <subdev/fb.h>
 #include <subdev/bar.h>
+#include <subdev/clock.h>
 
 #include "nv50.h"
 
@@ -730,6 +736,27 @@ nv50_disp_cclass = {
  ******************************************************************************/
 
 static void
+nv50_disp_intr_error(struct nv50_disp_priv *priv)
+{
+       u32 channels = (nv_rd32(priv, 0x610020) & 0x001f0000) >> 16;
+       u32 addr, data;
+       int chid;
+
+       for (chid = 0; chid < 5; chid++) {
+               if (!(channels & (1 << chid)))
+                       continue;
+
+               nv_wr32(priv, 0x610020, 0x00010000 << chid);
+               addr = nv_rd32(priv, 0x610080 + (chid * 0x08));
+               data = nv_rd32(priv, 0x610084 + (chid * 0x08));
+               nv_wr32(priv, 0x610080 + (chid * 0x08), 0x90000000);
+
+               nv_error(priv, "chid %d mthd 0x%04x data 0x%08x 0x%08x\n",
+                        chid, addr & 0xffc, data, addr);
+       }
+}
+
+static void
 nv50_disp_intr_vblank(struct nv50_disp_priv *priv, int crtc)
 {
        struct nouveau_disp *disp = &priv->base;
@@ -766,24 +793,413 @@ nv50_disp_intr_vblank(struct nv50_disp_priv *priv, int crtc)
                disp->vblank.notify(disp->vblank.data, crtc);
 }
 
+static u16
+exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl,
+           struct dcb_output *dcb, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
+           struct nvbios_outp *info)
+{
+       struct nouveau_bios *bios = nouveau_bios(priv);
+       u16 mask, type, data;
+
+       if (outp < 4) {
+               type = DCB_OUTPUT_ANALOG;
+               mask = 0;
+       } else {
+               outp -= 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:
+                       nv_error(priv, "unknown SOR mc 0x%08x\n", ctrl);
+                       return 0x0000;
+               }
+       }
+
+       mask  = 0x00c0 & (mask << 6);
+       mask |= 0x0001 << outp;
+       mask |= 0x0100 << head;
+
+       data = dcb_outp_match(bios, type, mask, ver, hdr, dcb);
+       if (!data)
+               return 0x0000;
+
+       return nvbios_outp_match(bios, type, mask, ver, hdr, cnt, len, info);
+}
+
+static bool
+exec_script(struct nv50_disp_priv *priv, int head, int id)
+{
+       struct nouveau_bios *bios = nouveau_bios(priv);
+       struct nvbios_outp info;
+       struct dcb_output dcb;
+       u8  ver, hdr, cnt, len;
+       u16 data;
+       u32 ctrl = 0x00000000;
+       int i;
+
+       for (i = 0; !(ctrl & (1 << head)) && i < 3; i++)
+               ctrl = nv_rd32(priv, 0x610b5c + (i * 8));
+
+       if (nv_device(priv)->chipset  < 0x90 ||
+           nv_device(priv)->chipset == 0x92 ||
+           nv_device(priv)->chipset == 0xa0) {
+               for (i = 0; !(ctrl & (1 << head)) && i < 2; i++)
+                       ctrl = nv_rd32(priv, 0x610b74 + (i * 8));
+               i += 3;
+       } else {
+               for (i = 0; !(ctrl & (1 << head)) && i < 4; i++)
+                       ctrl = nv_rd32(priv, 0x610798 + (i * 8));
+               i += 3;
+       }
+
+       if (!(ctrl & (1 << head)))
+               return false;
+
+       data = exec_lookup(priv, head, i, ctrl, &dcb, &ver, &hdr, &cnt, &len, &info);
+       if (data) {
+               struct nvbios_init init = {
+                       .subdev = nv_subdev(priv),
+                       .bios = bios,
+                       .offset = info.script[id],
+                       .outp = &dcb,
+                       .crtc = head,
+                       .execute = 1,
+               };
+
+               return nvbios_exec(&init) == 0;
+       }
+
+       return false;
+}
+
+static u32
+exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk,
+           struct dcb_output *outp)
+{
+       struct nouveau_bios *bios = nouveau_bios(priv);
+       struct nvbios_outp info1;
+       struct nvbios_ocfg info2;
+       u8  ver, hdr, cnt, len;
+       u16 data, conf;
+       u32 ctrl = 0x00000000;
+       int i;
+
+       for (i = 0; !(ctrl & (1 << head)) && i < 3; i++)
+               ctrl = nv_rd32(priv, 0x610b58 + (i * 8));
+
+       if (nv_device(priv)->chipset  < 0x90 ||
+           nv_device(priv)->chipset == 0x92 ||
+           nv_device(priv)->chipset == 0xa0) {
+               for (i = 0; !(ctrl & (1 << head)) && i < 2; i++)
+                       ctrl = nv_rd32(priv, 0x610b70 + (i * 8));
+               i += 3;
+       } else {
+               for (i = 0; !(ctrl & (1 << head)) && i < 4; i++)
+                       ctrl = nv_rd32(priv, 0x610794 + (i * 8));
+               i += 3;
+       }
+
+       if (!(ctrl & (1 << head)))
+               return 0x0000;
+
+       data = exec_lookup(priv, head, i, ctrl, outp, &ver, &hdr, &cnt, &len, &info1);
+       if (!data)
+               return 0x0000;
+
+       switch (outp->type) {
+       case DCB_OUTPUT_TMDS:
+               conf = (ctrl & 0x00000f00) >> 8;
+               if (pclk >= 165000)
+                       conf |= 0x0100;
+               break;
+       case DCB_OUTPUT_LVDS:
+               conf = priv->sor.lvdsconf;
+               break;
+       case DCB_OUTPUT_DP:
+               conf = (ctrl & 0x00000f00) >> 8;
+               break;
+       case DCB_OUTPUT_ANALOG:
+       default:
+               conf = 0x00ff;
+               break;
+       }
+
+       data = nvbios_ocfg_match(bios, data, conf, &ver, &hdr, &cnt, &len, &info2);
+       if (data) {
+               data = nvbios_oclk_match(bios, info2.clkcmp[id], pclk);
+               if (data) {
+                       struct nvbios_init init = {
+                               .subdev = nv_subdev(priv),
+                               .bios = bios,
+                               .offset = data,
+                               .outp = outp,
+                               .crtc = head,
+                               .execute = 1,
+                       };
+
+                       if (nvbios_exec(&init))
+                               return 0x0000;
+                       return conf;
+               }
+       }
+
+       return 0x0000;
+}
+
+static void
+nv50_disp_intr_unk10(struct nv50_disp_priv *priv, u32 super)
+{
+       int head = ffs((super & 0x00000060) >> 5) - 1;
+       if (head >= 0) {
+               head = ffs((super & 0x00000180) >> 7) - 1;
+               if (head >= 0)
+                       exec_script(priv, head, 1);
+       }
+
+       nv_wr32(priv, 0x610024, 0x00000010);
+       nv_wr32(priv, 0x610030, 0x80000000);
+}
+
+static void
+nv50_disp_intr_unk20_dp(struct nv50_disp_priv *priv,
+                       struct dcb_output *outp, u32 pclk)
+{
+       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 = nv_rd32(priv, 0x610794 + (or * 8));
+       const u32 bits = ((ctrl & 0x000f0000) == 0x00020000) ? 18 : 24;
+       const u32 symbol = 100000;
+       u32 dpctrl = nv_rd32(priv, 0x61c10c + loff) & 0x0000f0000;
+       u32 clksor = nv_rd32(priv, 0x614300 + soff);
+       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, r;
+
+       /* calculate packed data rate for each lane */
+       if      (dpctrl > 0x00030000) link_nr = 4;
+       else if (dpctrl > 0x00010000) link_nr = 2;
+       else                          link_nr = 1;
+
+       if (clksor & 0x000c0000)
+               link_bw = 270000;
+       else
+               link_bw = 162000;
+
+       link_data_rate = (pclk * bits / 8) / link_nr;
+
+       /* calculate ratio of packed data rate to link symbol rate */
+       link_ratio = link_data_rate * symbol;
+       r = do_div(link_ratio, link_bw);
+
+       for (TU = 64; TU >= 32; TU--) {
+               /* calculate average number of valid symbols in each TU */
+               u32 tu_valid = link_ratio * TU;
+               u32 calc, diff;
+
+               /* find a hw representation for the fraction.. */
+               VTUi = tu_valid / symbol;
+               calc = VTUi * symbol;
+               diff = tu_valid - calc;
+               if (diff) {
+                       if (diff >= (symbol / 2)) {
+                               VTUf = symbol / (symbol - diff);
+                               if (symbol - (VTUf * diff))
+                                       VTUf++;
+
+                               if (VTUf <= 15) {
+                                       VTUa  = 1;
+                                       calc += symbol - (symbol / VTUf);
+                               } else {
+                                       VTUa  = 0;
+                                       VTUf  = 1;
+                                       calc += symbol;
+                               }
+                       } else {
+                               VTUa  = 0;
+                               VTUf  = min((int)(symbol / diff), 15);
+                               calc += symbol / VTUf;
+                       }
+
+                       diff = calc - tu_valid;
+               } else {
+                       /* no remainder, but the hw doesn't like the fractional
+                        * part to be zero.  decrement the integer part and
+                        * have the fraction add a whole symbol back
+                        */
+                       VTUa = 0;
+                       VTUf = 1;
+                       VTUi--;
+               }
+
+               if (diff < best_diff) {
+                       best_diff = diff;
+                       bestTU = TU;
+                       bestVTUa = VTUa;
+                       bestVTUf = VTUf;
+                       bestVTUi = VTUi;
+                       if (diff == 0)
+                               break;
+               }
+       }
+
+       if (!bestTU) {
+               nv_error(priv, "unable to find suitable dp config\n");
+               return;
+       }
+
+       /* XXX close to vbios numbers, but not right */
+       unk  = (symbol - link_ratio) * bestTU;
+       unk *= link_ratio;
+       r = do_div(unk, symbol);
+       r = do_div(unk, symbol);
+       unk += 6;
+
+       nv_mask(priv, 0x61c10c + loff, 0x000001fc, bestTU << 2);
+       nv_mask(priv, 0x61c128 + loff, 0x010f7f3f, bestVTUa << 24 |
+                                                  bestVTUf << 16 |
+                                                  bestVTUi << 8 | unk);
+}
+
+static void
+nv50_disp_intr_unk20(struct nv50_disp_priv *priv, u32 super)
+{
+       struct dcb_output outp;
+       u32 addr, mask, data;
+       int head;
+
+       /* finish detaching encoder? */
+       head = ffs((super & 0x00000180) >> 7) - 1;
+       if (head >= 0)
+               exec_script(priv, head, 2);
+
+       /* check whether a vpll change is required */
+       head = ffs((super & 0x00000600) >> 9) - 1;
+       if (head >= 0) {
+               u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff;
+               if (pclk) {
+                       struct nouveau_clock *clk = nouveau_clock(priv);
+                       clk->pll_set(clk, PLL_VPLL0 + head, pclk);
+               }
+
+               nv_mask(priv, 0x614200 + head * 0x800, 0x0000000f, 0x00000000);
+       }
+
+       /* (re)attach the relevant OR to the head */
+       head = ffs((super & 0x00000180) >> 7) - 1;
+       if (head >= 0) {
+               u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff;
+               u32 conf = exec_clkcmp(priv, head, 0, pclk, &outp);
+               if (conf) {
+                       if (outp.type == DCB_OUTPUT_ANALOG) {
+                               addr = 0x614280 + (ffs(outp.or) - 1) * 0x800;
+                               mask = 0xffffffff;
+                               data = 0x00000000;
+                       } else {
+                               if (outp.type == DCB_OUTPUT_DP)
+                                       nv50_disp_intr_unk20_dp(priv, &outp, pclk);
+                               addr = 0x614300 + (ffs(outp.or) - 1) * 0x800;
+                               mask = 0x00000707;
+                               data = (conf & 0x0100) ? 0x0101 : 0x0000;
+                       }
+
+                       nv_mask(priv, addr, mask, data);
+               }
+       }
+
+       nv_wr32(priv, 0x610024, 0x00000020);
+       nv_wr32(priv, 0x610030, 0x80000000);
+}
+
+/* If programming a TMDS output on a SOR that can also be configured for
+ * DisplayPort, make sure NV50_SOR_DP_CTRL_ENABLE is forced off.
+ *
+ * It looks like the VBIOS TMDS scripts make an attempt at this, however,
+ * the VBIOS scripts on at least one board I have only switch it off on
+ * link 0, causing a blank display if the output has previously been
+ * programmed for DisplayPort.
+ */
+static void
+nv50_disp_intr_unk40_tmds(struct nv50_disp_priv *priv, struct dcb_output *outp)
+{
+       struct nouveau_bios *bios = nouveau_bios(priv);
+       const int link = !(outp->sorconf.link & 1);
+       const int   or = ffs(outp->or) - 1;
+       const u32 loff = (or * 0x800) + (link * 0x80);
+       const u16 mask = (outp->sorconf.link << 6) | outp->or;
+       u8  ver, hdr;
+
+       if (dcb_outp_match(bios, DCB_OUTPUT_DP, mask, &ver, &hdr, outp))
+               nv_mask(priv, 0x61c10c + loff, 0x00000001, 0x00000000);
+}
+
+static void
+nv50_disp_intr_unk40(struct nv50_disp_priv *priv, u32 super)
+{
+       int head = ffs((super & 0x00000180) >> 7) - 1;
+       if (head >= 0) {
+               struct dcb_output outp;
+               u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff;
+               if (pclk && exec_clkcmp(priv, head, 1, pclk, &outp)) {
+                       if (outp.type == DCB_OUTPUT_TMDS)
+                               nv50_disp_intr_unk40_tmds(priv, &outp);
+               }
+       }
+
+       nv_wr32(priv, 0x610024, 0x00000040);
+       nv_wr32(priv, 0x610030, 0x80000000);
+}
+
+static void
+nv50_disp_intr_super(struct nv50_disp_priv *priv, u32 intr1)
+{
+       u32 super = nv_rd32(priv, 0x610030);
+
+       nv_debug(priv, "supervisor 0x%08x 0x%08x\n", intr1, super);
+
+       if (intr1 & 0x00000010)
+               nv50_disp_intr_unk10(priv, super);
+       if (intr1 & 0x00000020)
+               nv50_disp_intr_unk20(priv, super);
+       if (intr1 & 0x00000040)
+               nv50_disp_intr_unk40(priv, super);
+}
+
 void
 nv50_disp_intr(struct nouveau_subdev *subdev)
 {
        struct nv50_disp_priv *priv = (void *)subdev;
-       u32 stat1 = nv_rd32(priv, 0x610024);
+       u32 intr0 = nv_rd32(priv, 0x610020);
+       u32 intr1 = nv_rd32(priv, 0x610024);
 
-       if (stat1 & 0x00000004) {
+       if (intr0 & 0x001f0000) {
+               nv50_disp_intr_error(priv);
+               intr0 &= ~0x001f0000;
+       }
+
+       if (intr1 & 0x00000004) {
                nv50_disp_intr_vblank(priv, 0);
                nv_wr32(priv, 0x610024, 0x00000004);
-               stat1 &= ~0x00000004;
+               intr1 &= ~0x00000004;
        }
 
-       if (stat1 & 0x00000008) {
+       if (intr1 & 0x00000008) {
                nv50_disp_intr_vblank(priv, 1);
                nv_wr32(priv, 0x610024, 0x00000008);
-               stat1 &= ~0x00000008;
+               intr1 &= ~0x00000008;
        }
 
+       if (intr1 & 0x00000070) {
+               nv50_disp_intr_super(priv, intr1);
+               intr1 &= ~0x00000070;
+       }
 }
 
 static int
index 03cdc07..1303680 100644 (file)
@@ -60,14 +60,6 @@ nouveau_irq_handler(DRM_IRQ_ARGS)
                return IRQ_NONE;
 
        nv_subdev(pmc)->intr(nv_subdev(pmc));
-
-       if (dev->mode_config.num_crtc &&
-           device->card_type <= NV_C0 &&
-           device->card_type >= NV_50) {
-               if (nv_rd32(device, 0x000100) & 0x04000000)
-                       nv50_display_intr(dev);
-       }
-
        return IRQ_HANDLED;
 }
 
index 0475556..2ba0576 100644 (file)
@@ -40,8 +40,6 @@
 
 #include <subdev/timer.h>
 
-static void nv50_display_bh(unsigned long);
-
 static inline int
 nv50_sor_nr(struct drm_device *dev)
 {
@@ -312,8 +310,6 @@ nv50_display_create(struct drm_device *dev)
                }
        }
 
-       tasklet_init(&priv->tasklet, nv50_display_bh, (unsigned long)dev);
-
        ret = nv50_evo_create(dev);
        if (ret) {
                nv50_display_destroy(dev);
@@ -464,455 +460,3 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb,
        dispc->sem.value++;
        return 0;
 }
-
-static u16
-nv50_display_script_select(struct drm_device *dev, struct dcb_output *dcb,
-                          u32 mc, int pxclk)
-{
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       struct nouveau_connector *nv_connector = NULL;
-       struct drm_encoder *encoder;
-       struct nvbios *bios = &drm->vbios;
-       u32 script = 0, or;
-
-       list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
-               struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
-
-               if (nv_encoder->dcb != dcb)
-                       continue;
-
-               nv_connector = nouveau_encoder_connector_get(nv_encoder);
-               break;
-       }
-
-       or = ffs(dcb->or) - 1;
-       switch (dcb->type) {
-       case DCB_OUTPUT_LVDS:
-               script = (mc >> 8) & 0xf;
-               if (bios->fp_no_ddc) {
-                       if (bios->fp.dual_link)
-                               script |= 0x0100;
-                       if (bios->fp.if_is_24bit)
-                               script |= 0x0200;
-               } else {
-                       /* determine number of lvds links */
-                       if (nv_connector && nv_connector->edid &&
-                           nv_connector->type == DCB_CONNECTOR_LVDS_SPWG) {
-                               /* http://www.spwg.org */
-                               if (((u8 *)nv_connector->edid)[121] == 2)
-                                       script |= 0x0100;
-                       } else
-                       if (pxclk >= bios->fp.duallink_transition_clk) {
-                               script |= 0x0100;
-                       }
-
-                       /* determine panel depth */
-                       if (script & 0x0100) {
-                               if (bios->fp.strapless_is_24bit & 2)
-                                       script |= 0x0200;
-                       } else {
-                               if (bios->fp.strapless_is_24bit & 1)
-                                       script |= 0x0200;
-                       }
-
-                       if (nv_connector && nv_connector->edid &&
-                           (nv_connector->edid->revision >= 4) &&
-                           (nv_connector->edid->input & 0x70) >= 0x20)
-                               script |= 0x0200;
-               }
-               break;
-       case DCB_OUTPUT_TMDS:
-               script = (mc >> 8) & 0xf;
-               if (pxclk >= 165000)
-                       script |= 0x0100;
-               break;
-       case DCB_OUTPUT_DP:
-               script = (mc >> 8) & 0xf;
-               break;
-       case DCB_OUTPUT_ANALOG:
-               script = 0xff;
-               break;
-       default:
-               NV_ERROR(drm, "modeset on unsupported output type!\n");
-               break;
-       }
-
-       return script;
-}
-
-static void
-nv50_display_unk10_handler(struct drm_device *dev)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       struct nv50_display *disp = nv50_display(dev);
-       u32 unk30 = nv_rd32(device, 0x610030), mc;
-       int i, crtc, or = 0, type = DCB_OUTPUT_ANY;
-
-       NV_DEBUG(drm, "0x610030: 0x%08x\n", unk30);
-       disp->irq.dcb = NULL;
-
-       nv_wr32(device, 0x619494, nv_rd32(device, 0x619494) & ~8);
-
-       /* Determine which CRTC we're dealing with, only 1 ever will be
-        * signalled at the same time with the current nouveau code.
-        */
-       crtc = ffs((unk30 & 0x00000060) >> 5) - 1;
-       if (crtc < 0)
-               goto ack;
-
-       /* Nothing needs to be done for the encoder */
-       crtc = ffs((unk30 & 0x00000180) >> 7) - 1;
-       if (crtc < 0)
-               goto ack;
-
-       /* Find which encoder was connected to the CRTC */
-       for (i = 0; type == DCB_OUTPUT_ANY && i < 3; i++) {
-               mc = nv_rd32(device, NV50_PDISPLAY_DAC_MODE_CTRL_C(i));
-               NV_DEBUG(drm, "DAC-%d mc: 0x%08x\n", i, mc);
-               if (!(mc & (1 << crtc)))
-                       continue;
-
-               switch ((mc & 0x00000f00) >> 8) {
-               case 0: type = DCB_OUTPUT_ANALOG; break;
-               case 1: type = DCB_OUTPUT_TV; break;
-               default:
-                       NV_ERROR(drm, "invalid mc, DAC-%d: 0x%08x\n", i, mc);
-                       goto ack;
-               }
-
-               or = i;
-       }
-
-       for (i = 0; type == DCB_OUTPUT_ANY && i < nv50_sor_nr(dev); i++) {
-               if (nv_device(drm->device)->chipset  < 0x90 ||
-                   nv_device(drm->device)->chipset == 0x92 ||
-                   nv_device(drm->device)->chipset == 0xa0)
-                       mc = nv_rd32(device, NV50_PDISPLAY_SOR_MODE_CTRL_C(i));
-               else
-                       mc = nv_rd32(device, NV90_PDISPLAY_SOR_MODE_CTRL_C(i));
-
-               NV_DEBUG(drm, "SOR-%d mc: 0x%08x\n", i, mc);
-               if (!(mc & (1 << crtc)))
-                       continue;
-
-               switch ((mc & 0x00000f00) >> 8) {
-               case 0: type = DCB_OUTPUT_LVDS; break;
-               case 1: type = DCB_OUTPUT_TMDS; break;
-               case 2: type = DCB_OUTPUT_TMDS; break;
-               case 5: type = DCB_OUTPUT_TMDS; break;
-               case 8: type = DCB_OUTPUT_DP; break;
-               case 9: type = DCB_OUTPUT_DP; break;
-               default:
-                       NV_ERROR(drm, "invalid mc, SOR-%d: 0x%08x\n", i, mc);
-                       goto ack;
-               }
-
-               or = i;
-       }
-
-       /* There was no encoder to disable */
-       if (type == DCB_OUTPUT_ANY)
-               goto ack;
-
-       /* Disable the encoder */
-       for (i = 0; i < drm->vbios.dcb.entries; i++) {
-               struct dcb_output *dcb = &drm->vbios.dcb.entry[i];
-
-               if (dcb->type == type && (dcb->or & (1 << or))) {
-                       nouveau_bios_run_display_table(dev, 0, -1, dcb, -1);
-                       disp->irq.dcb = dcb;
-                       goto ack;
-               }
-       }
-
-       NV_ERROR(drm, "no dcb for %d %d 0x%08x\n", or, type, mc);
-ack:
-       nv_wr32(device, NV50_PDISPLAY_INTR_1, NV50_PDISPLAY_INTR_1_CLK_UNK10);
-       nv_wr32(device, 0x610030, 0x80000000);
-}
-
-static void
-nv50_display_unk20_handler(struct drm_device *dev)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       struct nv50_display *disp = nv50_display(dev);
-       u32 unk30 = nv_rd32(device, 0x610030), tmp, pclk, script, mc = 0;
-       struct dcb_output *dcb;
-       int i, crtc, or = 0, type = DCB_OUTPUT_ANY;
-
-       NV_DEBUG(drm, "0x610030: 0x%08x\n", unk30);
-       dcb = disp->irq.dcb;
-       if (dcb) {
-               nouveau_bios_run_display_table(dev, 0, -2, dcb, -1);
-               disp->irq.dcb = NULL;
-       }
-
-       /* CRTC clock change requested? */
-       crtc = ffs((unk30 & 0x00000600) >> 9) - 1;
-       if (crtc >= 0) {
-               pclk  = nv_rd32(device, NV50_PDISPLAY_CRTC_P(crtc, CLOCK));
-               pclk &= 0x003fffff;
-               if (pclk)
-                       nv50_crtc_set_clock(dev, crtc, pclk);
-
-               tmp = nv_rd32(device, NV50_PDISPLAY_CRTC_CLK_CTRL2(crtc));
-               tmp &= ~0x000000f;
-               nv_wr32(device, NV50_PDISPLAY_CRTC_CLK_CTRL2(crtc), tmp);
-       }
-
-       /* Nothing needs to be done for the encoder */
-       crtc = ffs((unk30 & 0x00000180) >> 7) - 1;
-       if (crtc < 0)
-               goto ack;
-       pclk  = nv_rd32(device, NV50_PDISPLAY_CRTC_P(crtc, CLOCK)) & 0x003fffff;
-
-       /* Find which encoder is connected to the CRTC */
-       for (i = 0; type == DCB_OUTPUT_ANY && i < 3; i++) {
-               mc = nv_rd32(device, NV50_PDISPLAY_DAC_MODE_CTRL_P(i));
-               NV_DEBUG(drm, "DAC-%d mc: 0x%08x\n", i, mc);
-               if (!(mc & (1 << crtc)))
-                       continue;
-
-               switch ((mc & 0x00000f00) >> 8) {
-               case 0: type = DCB_OUTPUT_ANALOG; break;
-               case 1: type = DCB_OUTPUT_TV; break;
-               default:
-                       NV_ERROR(drm, "invalid mc, DAC-%d: 0x%08x\n", i, mc);
-                       goto ack;
-               }
-
-               or = i;
-       }
-
-       for (i = 0; type == DCB_OUTPUT_ANY && i < nv50_sor_nr(dev); i++) {
-               if (nv_device(drm->device)->chipset  < 0x90 ||
-                   nv_device(drm->device)->chipset == 0x92 ||
-                   nv_device(drm->device)->chipset == 0xa0)
-                       mc = nv_rd32(device, NV50_PDISPLAY_SOR_MODE_CTRL_P(i));
-               else
-                       mc = nv_rd32(device, NV90_PDISPLAY_SOR_MODE_CTRL_P(i));
-
-               NV_DEBUG(drm, "SOR-%d mc: 0x%08x\n", i, mc);
-               if (!(mc & (1 << crtc)))
-                       continue;
-
-               switch ((mc & 0x00000f00) >> 8) {
-               case 0: type = DCB_OUTPUT_LVDS; break;
-               case 1: type = DCB_OUTPUT_TMDS; break;
-               case 2: type = DCB_OUTPUT_TMDS; break;
-               case 5: type = DCB_OUTPUT_TMDS; break;
-               case 8: type = DCB_OUTPUT_DP; break;
-               case 9: type = DCB_OUTPUT_DP; break;
-               default:
-                       NV_ERROR(drm, "invalid mc, SOR-%d: 0x%08x\n", i, mc);
-                       goto ack;
-               }
-
-               or = i;
-       }
-
-       if (type == DCB_OUTPUT_ANY)
-               goto ack;
-
-       /* Enable the encoder */
-       for (i = 0; i < drm->vbios.dcb.entries; i++) {
-               dcb = &drm->vbios.dcb.entry[i];
-               if (dcb->type == type && (dcb->or & (1 << or)))
-                       break;
-       }
-
-       if (i == drm->vbios.dcb.entries) {
-               NV_ERROR(drm, "no dcb for %d %d 0x%08x\n", or, type, mc);
-               goto ack;
-       }
-
-       script = nv50_display_script_select(dev, dcb, mc, pclk);
-       nouveau_bios_run_display_table(dev, script, pclk, dcb, -1);
-
-       if (type == DCB_OUTPUT_DP) {
-               int link = !(dcb->dpconf.sor.link & 1);
-               if ((mc & 0x000f0000) == 0x00020000)
-                       nv50_sor_dp_calc_tu(dev, or, link, pclk, 18);
-               else
-                       nv50_sor_dp_calc_tu(dev, or, link, pclk, 24);
-       }
-
-       if (dcb->type != DCB_OUTPUT_ANALOG) {
-               tmp = nv_rd32(device, NV50_PDISPLAY_SOR_CLK_CTRL2(or));
-               tmp &= ~0x00000f0f;
-               if (script & 0x0100)
-                       tmp |= 0x00000101;
-               nv_wr32(device, NV50_PDISPLAY_SOR_CLK_CTRL2(or), tmp);
-       } else {
-               nv_wr32(device, NV50_PDISPLAY_DAC_CLK_CTRL2(or), 0);
-       }
-
-       disp->irq.dcb = dcb;
-       disp->irq.pclk = pclk;
-       disp->irq.script = script;
-
-ack:
-       nv_wr32(device, NV50_PDISPLAY_INTR_1, NV50_PDISPLAY_INTR_1_CLK_UNK20);
-       nv_wr32(device, 0x610030, 0x80000000);
-}
-
-/* If programming a TMDS output on a SOR that can also be configured for
- * DisplayPort, make sure NV50_SOR_DP_CTRL_ENABLE is forced off.
- *
- * It looks like the VBIOS TMDS scripts make an attempt at this, however,
- * the VBIOS scripts on at least one board I have only switch it off on
- * link 0, causing a blank display if the output has previously been
- * programmed for DisplayPort.
- */
-static void
-nv50_display_unk40_dp_set_tmds(struct drm_device *dev, struct dcb_output *dcb)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       int or = ffs(dcb->or) - 1, link = !(dcb->dpconf.sor.link & 1);
-       struct drm_encoder *encoder;
-       u32 tmp;
-
-       if (dcb->type != DCB_OUTPUT_TMDS)
-               return;
-
-       list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
-               struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
-
-               if (nv_encoder->dcb->type == DCB_OUTPUT_DP &&
-                   nv_encoder->dcb->or & (1 << or)) {
-                       tmp  = nv_rd32(device, NV50_SOR_DP_CTRL(or, link));
-                       tmp &= ~NV50_SOR_DP_CTRL_ENABLED;
-                       nv_wr32(device, NV50_SOR_DP_CTRL(or, link), tmp);
-                       break;
-               }
-       }
-}
-
-static void
-nv50_display_unk40_handler(struct drm_device *dev)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       struct nv50_display *disp = nv50_display(dev);
-       struct dcb_output *dcb = disp->irq.dcb;
-       u16 script = disp->irq.script;
-       u32 unk30 = nv_rd32(device, 0x610030), pclk = disp->irq.pclk;
-
-       NV_DEBUG(drm, "0x610030: 0x%08x\n", unk30);
-       disp->irq.dcb = NULL;
-       if (!dcb)
-               goto ack;
-
-       nouveau_bios_run_display_table(dev, script, -pclk, dcb, -1);
-       nv50_display_unk40_dp_set_tmds(dev, dcb);
-
-ack:
-       nv_wr32(device, NV50_PDISPLAY_INTR_1, NV50_PDISPLAY_INTR_1_CLK_UNK40);
-       nv_wr32(device, 0x610030, 0x80000000);
-       nv_wr32(device, 0x619494, nv_rd32(device, 0x619494) | 8);
-}
-
-static void
-nv50_display_bh(unsigned long data)
-{
-       struct drm_device *dev = (struct drm_device *)data;
-       struct nouveau_device *device = nouveau_dev(dev);
-       struct nouveau_drm *drm = nouveau_drm(dev);
-
-       for (;;) {
-               uint32_t intr0 = nv_rd32(device, NV50_PDISPLAY_INTR_0);
-               uint32_t intr1 = nv_rd32(device, NV50_PDISPLAY_INTR_1);
-
-               NV_DEBUG(drm, "PDISPLAY_INTR_BH 0x%08x 0x%08x\n", intr0, intr1);
-
-               if (intr1 & NV50_PDISPLAY_INTR_1_CLK_UNK10)
-                       nv50_display_unk10_handler(dev);
-               else
-               if (intr1 & NV50_PDISPLAY_INTR_1_CLK_UNK20)
-                       nv50_display_unk20_handler(dev);
-               else
-               if (intr1 & NV50_PDISPLAY_INTR_1_CLK_UNK40)
-                       nv50_display_unk40_handler(dev);
-               else
-                       break;
-       }
-
-       nv_wr32(device, NV03_PMC_INTR_EN_0, 1);
-}
-
-static void
-nv50_display_error_handler(struct drm_device *dev)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       u32 channels = (nv_rd32(device, NV50_PDISPLAY_INTR_0) & 0x001f0000) >> 16;
-       u32 addr, data;
-       int chid;
-
-       for (chid = 0; chid < 5; chid++) {
-               if (!(channels & (1 << chid)))
-                       continue;
-
-               nv_wr32(device, NV50_PDISPLAY_INTR_0, 0x00010000 << chid);
-               addr = nv_rd32(device, NV50_PDISPLAY_TRAPPED_ADDR(chid));
-               data = nv_rd32(device, NV50_PDISPLAY_TRAPPED_DATA(chid));
-               NV_ERROR(drm, "EvoCh %d Mthd 0x%04x Data 0x%08x "
-                             "(0x%04x 0x%02x)\n", chid,
-                        addr & 0xffc, data, addr >> 16, (addr >> 12) & 0xf);
-
-               nv_wr32(device, NV50_PDISPLAY_TRAPPED_ADDR(chid), 0x90000000);
-       }
-}
-
-void
-nv50_display_intr(struct drm_device *dev)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       struct nv50_display *disp = nv50_display(dev);
-       uint32_t delayed = 0;
-
-       while (nv_rd32(device, NV50_PMC_INTR_0) & NV50_PMC_INTR_0_DISPLAY) {
-               uint32_t intr0 = nv_rd32(device, NV50_PDISPLAY_INTR_0);
-               uint32_t intr1 = nv_rd32(device, NV50_PDISPLAY_INTR_1);
-               uint32_t clock;
-
-               NV_DEBUG(drm, "PDISPLAY_INTR 0x%08x 0x%08x\n", intr0, intr1);
-
-               if (!intr0 && !(intr1 & ~delayed))
-                       break;
-
-               if (intr0 & 0x001f0000) {
-                       nv50_display_error_handler(dev);
-                       intr0 &= ~0x001f0000;
-               }
-
-               if (intr1 & NV50_PDISPLAY_INTR_1_VBLANK_CRTC) {
-                       intr1 &= ~NV50_PDISPLAY_INTR_1_VBLANK_CRTC;
-                       delayed |= NV50_PDISPLAY_INTR_1_VBLANK_CRTC;
-               }
-
-               clock = (intr1 & (NV50_PDISPLAY_INTR_1_CLK_UNK10 |
-                                 NV50_PDISPLAY_INTR_1_CLK_UNK20 |
-                                 NV50_PDISPLAY_INTR_1_CLK_UNK40));
-               if (clock) {
-                       nv_wr32(device, NV03_PMC_INTR_EN_0, 0);
-                       tasklet_schedule(&disp->tasklet);
-                       delayed |= clock;
-                       intr1 &= ~clock;
-               }
-
-               if (intr0) {
-                       NV_ERROR(drm, "unknown PDISPLAY_INTR_0: 0x%08x\n", intr0);
-                       nv_wr32(device, NV50_PDISPLAY_INTR_0, intr0);
-               }
-
-               if (intr1) {
-                       NV_ERROR(drm,
-                                "unknown PDISPLAY_INTR_1: 0x%08x\n", intr1);
-                       nv_wr32(device, NV50_PDISPLAY_INTR_1, intr1);
-               }
-       }
-}
index 1336311..bf37038 100644 (file)
@@ -71,7 +71,6 @@ int nv50_display_create(struct drm_device *dev);
 int nv50_display_init(struct drm_device *dev);
 void nv50_display_fini(struct drm_device *dev);
 void nv50_display_destroy(struct drm_device *dev);
-void nv50_display_intr(struct drm_device *);
 int nv50_crtc_blank(struct nouveau_crtc *, bool blank);
 int nv50_crtc_set_clock(struct drm_device *, int head, int pclk);
 
index d2aab65..ae4f24a 100644 (file)
@@ -74,111 +74,6 @@ nv50_sor_dp_link_set(struct drm_device *dev, struct dcb_output *dcb, int crtc,
 }
 
 static void
-nv50_sor_dp_link_get(struct drm_device *dev, u32 or, u32 link, u32 *nr, u32 *bw)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       u32 dpctrl = nv_rd32(device, NV50_SOR_DP_CTRL(or, link)) & 0x000f0000;
-       u32 clksor = nv_rd32(device, 0x614300 + (or * 0x800));
-       if (clksor & 0x000c0000)
-               *bw = 270000;
-       else
-               *bw = 162000;
-
-       if      (dpctrl > 0x00030000) *nr = 4;
-       else if (dpctrl > 0x00010000) *nr = 2;
-       else                          *nr = 1;
-}
-
-void
-nv50_sor_dp_calc_tu(struct drm_device *dev, int or, int link, u32 clk, u32 bpp)
-{
-       struct nouveau_device *device = nouveau_dev(dev);
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       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, r;
-
-       /* calculate packed data rate for each lane */
-       nv50_sor_dp_link_get(dev, or, link, &link_nr, &link_bw);
-       link_data_rate = (clk * bpp / 8) / link_nr;
-
-       /* calculate ratio of packed data rate to link symbol rate */
-       link_ratio = link_data_rate * symbol;
-       r = do_div(link_ratio, link_bw);
-
-       for (TU = 64; TU >= 32; TU--) {
-               /* calculate average number of valid symbols in each TU */
-               u32 tu_valid = link_ratio * TU;
-               u32 calc, diff;
-
-               /* find a hw representation for the fraction.. */
-               VTUi = tu_valid / symbol;
-               calc = VTUi * symbol;
-               diff = tu_valid - calc;
-               if (diff) {
-                       if (diff >= (symbol / 2)) {
-                               VTUf = symbol / (symbol - diff);
-                               if (symbol - (VTUf * diff))
-                                       VTUf++;
-
-                               if (VTUf <= 15) {
-                                       VTUa  = 1;
-                                       calc += symbol - (symbol / VTUf);
-                               } else {
-                                       VTUa  = 0;
-                                       VTUf  = 1;
-                                       calc += symbol;
-                               }
-                       } else {
-                               VTUa  = 0;
-                               VTUf  = min((int)(symbol / diff), 15);
-                               calc += symbol / VTUf;
-                       }
-
-                       diff = calc - tu_valid;
-               } else {
-                       /* no remainder, but the hw doesn't like the fractional
-                        * part to be zero.  decrement the integer part and
-                        * have the fraction add a whole symbol back
-                        */
-                       VTUa = 0;
-                       VTUf = 1;
-                       VTUi--;
-               }
-
-               if (diff < best_diff) {
-                       best_diff = diff;
-                       bestTU = TU;
-                       bestVTUa = VTUa;
-                       bestVTUf = VTUf;
-                       bestVTUi = VTUi;
-                       if (diff == 0)
-                               break;
-               }
-       }
-
-       if (!bestTU) {
-               NV_ERROR(drm, "DP: unable to find suitable config\n");
-               return;
-       }
-
-       /* XXX close to vbios numbers, but not right */
-       unk  = (symbol - link_ratio) * bestTU;
-       unk *= link_ratio;
-       r = do_div(unk, symbol);
-       r = do_div(unk, symbol);
-       unk += 6;
-
-       nv_mask(device, NV50_SOR_DP_CTRL(or, link), 0x000001fc, bestTU << 2);
-       nv_mask(device, NV50_SOR_DP_SCFG(or, link), 0x010f7f3f, bestVTUa << 24 |
-                                                            bestVTUf << 16 |
-                                                            bestVTUi << 8 |
-                                                            unk);
-}
-static void
 nv50_sor_disconnect(struct drm_encoder *encoder)
 {
        struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
@@ -312,7 +207,9 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode,
        struct nouveau_drm *drm = nouveau_drm(encoder->dev);
        struct nouveau_crtc *crtc = nouveau_crtc(encoder->crtc);
        struct nouveau_connector *nv_connector;
-       uint32_t mode_ctl = 0;
+       struct nv50_display *disp = nv50_display(encoder->dev);
+       struct nvbios *bios = &drm->vbios;
+       uint32_t mode_ctl = 0, script;
        int ret;
 
        NV_DEBUG(drm, "or %d type %d -> crtc %d\n",
@@ -331,6 +228,43 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode,
 
                nouveau_hdmi_mode_set(encoder, mode);
                break;
+       case DCB_OUTPUT_LVDS:
+               script = 0x0000;
+               if (bios->fp_no_ddc) {
+                       if (bios->fp.dual_link)
+                               script |= 0x0100;
+                       if (bios->fp.if_is_24bit)
+                               script |= 0x0200;
+               } else {
+                       /* determine number of lvds links */
+                       nv_connector = nouveau_encoder_connector_get(nv_encoder);
+                       if (nv_connector && nv_connector->edid &&
+                           nv_connector->type == DCB_CONNECTOR_LVDS_SPWG) {
+                               /* http://www.spwg.org */
+                               if (((u8 *)nv_connector->edid)[121] == 2)
+                                       script |= 0x0100;
+                       } else
+                       if (mode->clock >= bios->fp.duallink_transition_clk) {
+                               script |= 0x0100;
+                       }
+
+                       /* determine panel depth */
+                       if (script & 0x0100) {
+                               if (bios->fp.strapless_is_24bit & 2)
+                                       script |= 0x0200;
+                       } else {
+                               if (bios->fp.strapless_is_24bit & 1)
+                                       script |= 0x0200;
+                       }
+
+                       if (nv_connector && nv_connector->edid &&
+                           (nv_connector->edid->revision >= 4) &&
+                           (nv_connector->edid->input & 0x70) >= 0x20)
+                               script |= 0x0200;
+               }
+
+               nv_call(disp->core, NV50_DISP_SOR_LVDS_SCRIPT + nv_encoder->or, script);
+               break;
        case DCB_OUTPUT_DP:
                nv_connector = nouveau_encoder_connector_get(nv_encoder);
                if (nv_connector && nv_connector->base.display_info.bpc == 6) {