]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg94.c
drm/nouveau/disp/nv50-: port OR power state control to nvkm_ior
[mirror_ubuntu-artful-kernel.git] / drivers / gpu / drm / nouveau / nvkm / engine / disp / sorg94.c
index 627b9ee1ddd275d6dafd3215dcd8222f4668efba..3175bb3031b57ecd13e73154feb5a69b05a77dbd 100644 (file)
@@ -21,8 +21,8 @@
  *
  * Authors: Ben Skeggs
  */
+#include "ior.h"
 #include "nv50.h"
-#include "outpdp.h"
 
 #include <subdev/timer.h>
 
@@ -54,6 +54,40 @@ g94_sor_dp_lane_map(struct nvkm_device *device, u8 lane)
        return g94[lane];
 }
 
+static int
+g94_sor_dp_drv_ctl(struct nvkm_output_dp *outp, int ln, int vs, int pe, int pc)
+{
+       struct nvkm_device *device = outp->base.disp->engine.subdev.device;
+       struct nvkm_bios *bios = device->bios;
+       const u32 shift = g94_sor_dp_lane_map(device, ln);
+       const u32 loff = g94_sor_loff(outp);
+       u32 addr, data[3];
+       u8  ver, hdr, cnt, len;
+       struct nvbios_dpout info;
+       struct nvbios_dpcfg ocfg;
+
+       addr = nvbios_dpout_match(bios, outp->base.info.hasht,
+                                       outp->base.info.hashm,
+                                 &ver, &hdr, &cnt, &len, &info);
+       if (!addr)
+               return -ENODEV;
+
+       addr = nvbios_dpcfg_match(bios, addr, 0, vs, pe,
+                                 &ver, &hdr, &cnt, &len, &ocfg);
+       if (!addr)
+               return -EINVAL;
+
+       data[0] = nvkm_rd32(device, 0x61c118 + loff) & ~(0x000000ff << shift);
+       data[1] = nvkm_rd32(device, 0x61c120 + loff) & ~(0x000000ff << shift);
+       data[2] = nvkm_rd32(device, 0x61c130 + loff);
+       if ((data[2] & 0x0000ff00) < (ocfg.tx_pu << 8) || ln == 0)
+               data[2] = (data[2] & ~0x0000ff00) | (ocfg.tx_pu << 8);
+       nvkm_wr32(device, 0x61c118 + loff, data[0] | (ocfg.dc << shift));
+       nvkm_wr32(device, 0x61c120 + loff, data[1] | (ocfg.pe << shift));
+       nvkm_wr32(device, 0x61c130 + loff, data[2]);
+       return 0;
+}
+
 static int
 g94_sor_dp_pattern(struct nvkm_output_dp *outp, int pattern)
 {
@@ -103,40 +137,6 @@ g94_sor_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef)
        return 0;
 }
 
-static int
-g94_sor_dp_drv_ctl(struct nvkm_output_dp *outp, int ln, int vs, int pe, int pc)
-{
-       struct nvkm_device *device = outp->base.disp->engine.subdev.device;
-       struct nvkm_bios *bios = device->bios;
-       const u32 shift = g94_sor_dp_lane_map(device, ln);
-       const u32 loff = g94_sor_loff(outp);
-       u32 addr, data[3];
-       u8  ver, hdr, cnt, len;
-       struct nvbios_dpout info;
-       struct nvbios_dpcfg ocfg;
-
-       addr = nvbios_dpout_match(bios, outp->base.info.hasht,
-                                       outp->base.info.hashm,
-                                 &ver, &hdr, &cnt, &len, &info);
-       if (!addr)
-               return -ENODEV;
-
-       addr = nvbios_dpcfg_match(bios, addr, 0, vs, pe,
-                                 &ver, &hdr, &cnt, &len, &ocfg);
-       if (!addr)
-               return -EINVAL;
-
-       data[0] = nvkm_rd32(device, 0x61c118 + loff) & ~(0x000000ff << shift);
-       data[1] = nvkm_rd32(device, 0x61c120 + loff) & ~(0x000000ff << shift);
-       data[2] = nvkm_rd32(device, 0x61c130 + loff);
-       if ((data[2] & 0x0000ff00) < (ocfg.tx_pu << 8) || ln == 0)
-               data[2] = (data[2] & ~0x0000ff00) | (ocfg.tx_pu << 8);
-       nvkm_wr32(device, 0x61c118 + loff, data[0] | (ocfg.dc << shift));
-       nvkm_wr32(device, 0x61c120 + loff, data[1] | (ocfg.pe << shift));
-       nvkm_wr32(device, 0x61c130 + loff, data[2]);
-       return 0;
-}
-
 static const struct nvkm_output_dp_func
 g94_sor_dp_func = {
        .pattern = g94_sor_dp_pattern,
@@ -151,3 +151,164 @@ g94_sor_dp_new(struct nvkm_disp *disp, int index, struct dcb_output *dcbE,
 {
        return nvkm_output_dp_new_(&g94_sor_dp_func, disp, index, dcbE, poutp);
 }
+
+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;
+
+}
+
+void
+nv50_disp_update_sppll1(struct nv50_disp *disp)
+{
+       struct nvkm_device *device = disp->base.engine.subdev.device;
+       bool used = false;
+       int sor;
+
+       if (!nv50_disp_dptmds_war(device))
+               return;
+
+       for (sor = 0; sor < disp->func->sor.nr; sor++) {
+               u32 clksor = nvkm_rd32(device, 0x614300 + (sor * 0x800));
+               switch (clksor & 0x03000000) {
+               case 0x02000000:
+               case 0x03000000:
+                       used = true;
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       if (used)
+               return;
+
+       nvkm_mask(device, 0x00e840, 0x80000000, 0x00000000);
+}
+
+void
+nv50_disp_dptmds_war_3(struct nv50_disp *disp, struct dcb_output *outp)
+{
+       struct nvkm_device *device = disp->base.engine.subdev.device;
+       const u32 soff = __ffs(outp->or) * 0x800;
+       u32 sorpwr;
+
+       if (!nv50_disp_dptmds_war_needed(disp, outp))
+               return;
+
+       sorpwr = nvkm_rd32(device, 0x61c004 + soff);
+       if (sorpwr & 0x00000001) {
+               u32 seqctl = nvkm_rd32(device, 0x61c030 + soff);
+               u32  pd_pc = (seqctl & 0x00000f00) >> 8;
+               u32  pu_pc =  seqctl & 0x0000000f;
+
+               nvkm_wr32(device, 0x61c040 + soff + pd_pc * 4, 0x1f008000);
+
+               nvkm_msec(device, 2000,
+                       if (!(nvkm_rd32(device, 0x61c030 + soff) & 0x10000000))
+                               break;
+               );
+               nvkm_mask(device, 0x61c004 + soff, 0x80000001, 0x80000000);
+               nvkm_msec(device, 2000,
+                       if (!(nvkm_rd32(device, 0x61c030 + soff) & 0x10000000))
+                               break;
+               );
+
+               nvkm_wr32(device, 0x61c040 + soff + pd_pc * 4, 0x00002000);
+               nvkm_wr32(device, 0x61c040 + soff + pu_pc * 4, 0x1f000000);
+       }
+
+       nvkm_mask(device, 0x61c10c + soff, 0x00000001, 0x00000000);
+       nvkm_mask(device, 0x614300 + soff, 0x03000000, 0x00000000);
+
+       if (sorpwr & 0x00000001) {
+               nvkm_mask(device, 0x61c004 + soff, 0x80000001, 0x80000001);
+       }
+}
+
+void
+nv50_disp_dptmds_war_2(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_needed(disp, outp))
+               return;
+
+       nvkm_mask(device, 0x00e840, 0x80000000, 0x80000000);
+       nvkm_mask(device, 0x614300 + soff, 0x03000000, 0x03000000);
+       nvkm_mask(device, 0x61c10c + soff, 0x00000001, 0x00000001);
+
+       nvkm_mask(device, 0x61c00c + soff, 0x0f000000, 0x00000000);
+       nvkm_mask(device, 0x61c008 + soff, 0xff000000, 0x14000000);
+       nvkm_usec(device, 400, NVKM_DELAY);
+       nvkm_mask(device, 0x61c008 + soff, 0xff000000, 0x00000000);
+       nvkm_mask(device, 0x61c00c + soff, 0x0f000000, 0x01000000);
+
+       if (nvkm_rd32(device, 0x61c004 + soff) & 0x00000001) {
+               u32 seqctl = nvkm_rd32(device, 0x61c030 + soff);
+               u32  pu_pc = seqctl & 0x0000000f;
+               nvkm_wr32(device, 0x61c040 + soff + pu_pc * 4, 0x1f008000);
+       }
+}
+
+void
+g94_sor_state(struct nvkm_ior *sor, struct nvkm_ior_state *state)
+{
+       struct nvkm_device *device = sor->disp->engine.subdev.device;
+       const u32 coff = sor->id * 8 + (state == &sor->arm) * 4;
+       u32 ctrl = nvkm_rd32(device, 0x610794 + coff);
+
+       state->proto_evo = (ctrl & 0x00000f00) >> 8;
+       switch (state->proto_evo) {
+       case 0: state->proto = LVDS; state->link = 1; break;
+       case 1: state->proto = TMDS; state->link = 1; break;
+       case 2: state->proto = TMDS; state->link = 2; break;
+       case 5: state->proto = TMDS; state->link = 3; break;
+       case 8: state->proto =   DP; state->link = 1; break;
+       case 9: state->proto =   DP; state->link = 2; break;
+       default:
+               state->proto = UNKNOWN;
+               break;
+       }
+
+       state->head = ctrl & 0x00000003;
+}
+
+static const struct nvkm_ior_func
+g94_sor = {
+       .state = g94_sor_state,
+       .power = nv50_sor_power,
+};
+
+int
+g94_sor_new(struct nvkm_disp *disp, int id)
+{
+       return nvkm_ior_new_(&g94_sor, disp, SOR, id);
+}