diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c index 90974cd087c1e0509d928b1e6c9ae27d03b6f4b3..1e85f36c705f34faba1c93d94af337b1e4d90abc 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c @@ -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 diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c b/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c index b998040b5cca6b8dbad76a8ad0c74a202f8673bd..48aa38a87e3fdb9651aea5f99586cf380099335f 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c @@ -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