drm/nouveau/disp/dp: fix tmds passthrough on dp connector
authorBen Skeggs <bskeggs@redhat.com>
Wed, 11 Jun 2014 03:06:48 +0000 (13:06 +1000)
committerBen Skeggs <bskeggs@redhat.com>
Wed, 11 Jun 2014 06:11:47 +0000 (16:11 +1000)
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
drivers/gpu/drm/nouveau/core/engine/disp/nv50.c
drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c

index 90974cd..1e85f36 100644 (file)
@@ -1171,7 +1171,7 @@ exec_lookup(struct nv50_disp_priv *priv, int head, int or, u32 ctrl,
        return NULL;
 }
 
-static bool
+static struct nvkm_output *
 exec_script(struct nv50_disp_priv *priv, int head, int id)
 {
        struct nouveau_bios *bios = nouveau_bios(priv);
@@ -1208,7 +1208,7 @@ exec_script(struct nv50_disp_priv *priv, int head, int id)
        }
 
        if (!(ctrl & (1 << head)))
-               return false;
+               return NULL;
        i--;
 
        outp = exec_lookup(priv, head, i, ctrl, &data, &ver, &hdr, &cnt, &len, &info);
@@ -1222,10 +1222,10 @@ exec_script(struct nv50_disp_priv *priv, int head, int id)
                        .execute = 1,
                };
 
-               return nvbios_exec(&init) == 0;
+               nvbios_exec(&init);
        }
 
-       return false;
+       return outp;
 }
 
 static struct nvkm_output *
@@ -1325,7 +1325,35 @@ nv50_disp_intr_unk10_0(struct nv50_disp_priv *priv, int head)
 static void
 nv50_disp_intr_unk20_0(struct nv50_disp_priv *priv, int head)
 {
-       exec_script(priv, head, 2);
+       struct nvkm_output *outp = exec_script(priv, head, 2);
+
+       /* the binary driver does this outside of the supervisor handling
+        * (after the third supervisor from a detach).  we (currently?)
+        * allow both detach/attach to happen in the same set of
+        * supervisor interrupts, so it would make sense to execute this
+        * (full power down?) script after all the detach phases of the
+        * supervisor handling.  like with training if needed from the
+        * second supervisor, nvidia doesn't do this, so who knows if it's
+        * entirely safe, but it does appear to work..
+        *
+        * without this script being run, on some configurations i've
+        * seen, switching from DP to TMDS on a DP connector may result
+        * in a blank screen (SOR_PWR off/on can restore it)
+        */
+       if (outp && outp->info.type == DCB_OUTPUT_DP) {
+               struct nvkm_output_dp *outpdp = (void *)outp;
+               struct nvbios_init init = {
+                       .subdev = nv_subdev(priv),
+                       .bios = nouveau_bios(priv),
+                       .outp = &outp->info,
+                       .crtc = head,
+                       .offset = outpdp->info.script[4],
+                       .execute = 1,
+               };
+
+               nvbios_exec(&init);
+               atomic_set(&outpdp->lt.done, 0);
+       }
 }
 
 static void
index b998040..48aa38a 100644 (file)
@@ -962,7 +962,7 @@ exec_lookup(struct nv50_disp_priv *priv, int head, int or, u32 ctrl,
        return NULL;
 }
 
-static bool
+static struct nvkm_output *
 exec_script(struct nv50_disp_priv *priv, int head, int id)
 {
        struct nouveau_bios *bios = nouveau_bios(priv);
@@ -979,7 +979,7 @@ exec_script(struct nv50_disp_priv *priv, int head, int id)
        }
 
        if (or == 8)
-               return false;
+               return NULL;
 
        outp = exec_lookup(priv, head, or, ctrl, &data, &ver, &hdr, &cnt, &len, &info);
        if (outp) {
@@ -992,10 +992,10 @@ exec_script(struct nv50_disp_priv *priv, int head, int id)
                        .execute = 1,
                };
 
-               return nvbios_exec(&init) == 0;
+               nvbios_exec(&init);
        }
 
-       return false;
+       return outp;
 }
 
 static struct nvkm_output *
@@ -1069,7 +1069,23 @@ nvd0_disp_intr_unk1_0(struct nv50_disp_priv *priv, int head)
 static void
 nvd0_disp_intr_unk2_0(struct nv50_disp_priv *priv, int head)
 {
-       exec_script(priv, head, 2);
+       struct nvkm_output *outp = exec_script(priv, head, 2);
+
+       /* see note in nv50_disp_intr_unk20_0() */
+       if (outp && outp->info.type == DCB_OUTPUT_DP) {
+               struct nvkm_output_dp *outpdp = (void *)outp;
+               struct nvbios_init init = {
+                       .subdev = nv_subdev(priv),
+                       .bios = nouveau_bios(priv),
+                       .outp = &outp->info,
+                       .crtc = head,
+                       .offset = outpdp->info.script[4],
+                       .execute = 1,
+               };
+
+               nvbios_exec(&init);
+               atomic_set(&outpdp->lt.done, 0);
+       }
 }
 
 static void