OSDN Git Service

drm/nouveau/disp: add method to control DPAUX pad power
authorBen Skeggs <bskeggs@redhat.com>
Wed, 1 Jun 2022 10:46:34 +0000 (20:46 +1000)
committerBen Skeggs <bskeggs@redhat.com>
Tue, 8 Nov 2022 22:22:02 +0000 (08:22 +1000)
This removes the need for NVKM to track DP HPD events, as the KMS
driver follows them already, and has better information available.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Reviewed-by: Lyude Paul <lyude@redhat.com>
drivers/gpu/drm/nouveau/include/nvif/if0012.h
drivers/gpu/drm/nouveau/include/nvif/outp.h
drivers/gpu/drm/nouveau/nouveau_dp.c
drivers/gpu/drm/nouveau/nvif/outp.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/outp.h
drivers/gpu/drm/nouveau/nvkm/engine/disp/uoutp.c

index c67f03d..d158327 100644 (file)
@@ -15,6 +15,7 @@ union nvif_outp_args {
 #define NVIF_OUTP_V0_RELEASE     0x02
 #define NVIF_OUTP_V0_INFOFRAME   0x03
 #define NVIF_OUTP_V0_HDA_ELD     0x04
+#define NVIF_OUTP_V0_DP_AUX_PWR  0x05
 
 union nvif_outp_load_detect_args {
        struct nvif_outp_load_detect_v0 {
@@ -91,4 +92,12 @@ union nvif_outp_hda_eld_args {
                __u8  data[];
        } v0;
 };
+
+union nvif_outp_dp_aux_pwr_args {
+       struct nvif_outp_dp_aux_pwr_v0 {
+               __u8 version;
+               __u8 state;
+               __u8 pad02[6];
+       } v0;
+};
 #endif
index 88fd2b9..1c960f6 100644 (file)
@@ -26,4 +26,5 @@ int nvif_outp_acquire_dp(struct nvif_outp *, u8 dpcd[16],
 void nvif_outp_release(struct nvif_outp *);
 int nvif_outp_infoframe(struct nvif_outp *, u8 type, struct nvif_outp_infoframe_v0 *, u32 size);
 int nvif_outp_hda_eld(struct nvif_outp *, int head, void *data, u32 size);
+int nvif_outp_dp_aux_pwr(struct nvif_outp *, bool enable);
 #endif
index 20db8ea..b7104e6 100644 (file)
@@ -140,12 +140,17 @@ nouveau_dp_detect(struct nouveau_connector *nv_connector,
         * TODO: look into checking this before probing I2C to detect DVI/HDMI
         */
        hpd = nvif_conn_hpd_status(&nv_connector->conn);
-       if (hpd == NVIF_CONN_HPD_STATUS_NOT_PRESENT)
+       if (hpd == NVIF_CONN_HPD_STATUS_NOT_PRESENT) {
+               nvif_outp_dp_aux_pwr(&nv_encoder->outp, false);
                goto out;
+       }
+       nvif_outp_dp_aux_pwr(&nv_encoder->outp, true);
 
        status = nouveau_dp_probe_dpcd(nv_connector, nv_encoder);
-       if (status == connector_status_disconnected)
+       if (status == connector_status_disconnected) {
+               nvif_outp_dp_aux_pwr(&nv_encoder->outp, false);
                goto out;
+       }
 
        /* If we're in MST mode, we're done here */
        if (mstm && mstm->can_mst && mstm->is_mst) {
@@ -193,6 +198,7 @@ nouveau_dp_detect(struct nouveau_connector *nv_connector,
                        ret = NOUVEAU_DP_MST;
                        goto out;
                } else if (ret != 0) {
+                       nvif_outp_dp_aux_pwr(&nv_encoder->outp, false);
                        goto out;
                }
        }
index 1ae5b19..9bd9cc2 100644 (file)
 #include <nvif/class.h>
 
 int
+nvif_outp_dp_aux_pwr(struct nvif_outp *outp, bool enable)
+{
+       struct nvif_outp_dp_aux_pwr_v0 args;
+       int ret;
+
+       args.version = 0;
+       args.state = enable;
+
+       ret = nvif_object_mthd(&outp->object, NVIF_OUTP_V0_DP_AUX_PWR, &args, sizeof(args));
+       NVIF_ERRON(ret, &outp->object, "[DP_AUX_PWR state:%d]", args.state);
+       return ret;
+}
+
+int
 nvif_outp_hda_eld(struct nvif_outp *outp, int head, void *data, u32 size)
 {
        struct {
index f1887b5..92c9fae 100644 (file)
@@ -612,18 +612,38 @@ nvkm_dp_enable_supported_link_rates(struct nvkm_outp *outp)
        return outp->dp.rates != 0;
 }
 
-static bool
-nvkm_dp_enable(struct nvkm_outp *outp, bool enable)
+void
+nvkm_dp_enable(struct nvkm_outp *outp, bool auxpwr)
 {
+       struct nvkm_gpio *gpio = outp->disp->engine.subdev.device->gpio;
        struct nvkm_i2c_aux *aux = outp->dp.aux;
 
-       if (enable) {
-               if (!outp->dp.present) {
-                       OUTP_DBG(outp, "aux power -> always");
-                       nvkm_i2c_aux_monitor(aux, true);
-                       outp->dp.present = true;
+       if (auxpwr && !outp->dp.aux_pwr) {
+               /* eDP panels need powering on by us (if the VBIOS doesn't default it
+                * to on) before doing any AUX channel transactions.  LVDS panel power
+                * is handled by the SOR itself, and not required for LVDS DDC.
+                */
+               if (outp->conn->info.type == DCB_CONNECTOR_eDP) {
+                       int power = nvkm_gpio_get(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff);
+                       if (power == 0) {
+                               nvkm_gpio_set(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff, 1);
+                               outp->dp.aux_pwr_pu = true;
+                       }
+
+                       /* We delay here unconditionally, even if already powered,
+                        * because some laptop panels having a significant resume
+                        * delay before the panel begins responding.
+                        *
+                        * This is likely a bit of a hack, but no better idea for
+                        * handling this at the moment.
+                        */
+                       msleep(300);
                }
 
+               OUTP_DBG(outp, "aux power -> always");
+               nvkm_i2c_aux_monitor(aux, true);
+               outp->dp.aux_pwr = true;
+
                /* Detect any LTTPRs before reading DPCD receiver caps. */
                if (!nvkm_rdaux(aux, DPCD_LTTPR_REV, outp->dp.lttpr, sizeof(outp->dp.lttpr)) &&
                    outp->dp.lttpr[0] >= 0x14 && outp->dp.lttpr[2]) {
@@ -676,19 +696,24 @@ nvkm_dp_enable(struct nvkm_outp *outp, bool enable)
                                        outp->dp.rates++;
                                }
                        }
-
-                       return true;
                }
-       }
-
-       if (outp->dp.present) {
+       } else
+       if (!auxpwr && outp->dp.aux_pwr) {
                OUTP_DBG(outp, "aux power -> demand");
                nvkm_i2c_aux_monitor(aux, false);
-               outp->dp.present = false;
-       }
+               outp->dp.aux_pwr = false;
+               atomic_set(&outp->dp.lt.done, 0);
 
-       atomic_set(&outp->dp.lt.done, 0);
-       return false;
+               /* Restore eDP panel GPIO to its prior state if we changed it, as
+                * it could potentially interfere with other outputs.
+                */
+               if (outp->conn->info.type == DCB_CONNECTOR_eDP) {
+                       if (outp->dp.aux_pwr_pu) {
+                               nvkm_gpio_set(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff, 0);
+                               outp->dp.aux_pwr_pu = false;
+                       }
+               }
+       }
 }
 
 static int
@@ -705,8 +730,6 @@ nvkm_dp_hpd(struct nvkm_notify *notify)
                if (atomic_read(&outp->dp.lt.done))
                        outp->func->acquire(outp);
                rep.mask |= NVIF_NOTIFY_CONN_V0_IRQ;
-       } else {
-               nvkm_dp_enable(outp, true);
        }
 
        if (line->mask & NVKM_I2C_UNPLUG)
@@ -728,37 +751,8 @@ nvkm_dp_fini(struct nvkm_outp *outp)
 static void
 nvkm_dp_init(struct nvkm_outp *outp)
 {
-       struct nvkm_gpio *gpio = outp->disp->engine.subdev.device->gpio;
-
+       nvkm_dp_enable(outp, outp->dp.enabled);
        nvkm_notify_put(&outp->conn->hpd);
-
-       /* eDP panels need powering on by us (if the VBIOS doesn't default it
-        * to on) before doing any AUX channel transactions.  LVDS panel power
-        * is handled by the SOR itself, and not required for LVDS DDC.
-        */
-       if (outp->conn->info.type == DCB_CONNECTOR_eDP) {
-               int power = nvkm_gpio_get(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff);
-               if (power == 0)
-                       nvkm_gpio_set(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff, 1);
-
-               /* We delay here unconditionally, even if already powered,
-                * because some laptop panels having a significant resume
-                * delay before the panel begins responding.
-                *
-                * This is likely a bit of a hack, but no better idea for
-                * handling this at the moment.
-                */
-               msleep(300);
-
-               /* If the eDP panel can't be detected, we need to restore
-                * the panel power GPIO to avoid breaking another output.
-                */
-               if (!nvkm_dp_enable(outp, true) && power == 0)
-                       nvkm_gpio_set(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff, 0);
-       } else {
-               nvkm_dp_enable(outp, true);
-       }
-
        nvkm_notify_get(&outp->dp.hpd);
 }
 
index 1d86baa..9a6be43 100644 (file)
@@ -6,6 +6,7 @@
 int nvkm_dp_new(struct nvkm_disp *, int index, struct dcb_output *,
                struct nvkm_outp **);
 void nvkm_dp_disable(struct nvkm_outp *, struct nvkm_ior *);
+void nvkm_dp_enable(struct nvkm_outp *, bool auxpwr);
 
 /* DPCD Receiver Capabilities */
 #define DPCD_RC00_DPCD_REV                                              0x00000
index d7c989e..66def8a 100644 (file)
@@ -39,7 +39,9 @@ struct nvkm_outp {
                        struct nvkm_i2c_aux *aux;
 
                        struct nvkm_notify hpd;
-                       bool present;
+                       bool enabled;
+                       bool aux_pwr;
+                       bool aux_pwr_pu;
                        u8 lttpr[6];
                        u8 lttprs;
                        u8 dpcd[16];
index 61d41b3..3bfc3e3 100644 (file)
  */
 #define nvkm_uoutp(p) container_of((p), struct nvkm_outp, object)
 #include "outp.h"
+#include "dp.h"
 #include "head.h"
 #include "ior.h"
 
 #include <nvif/if0012.h>
 
 static int
+nvkm_uoutp_mthd_dp_aux_pwr(struct nvkm_outp *outp, void *argv, u32 argc)
+{
+       union nvif_outp_dp_aux_pwr_args *args = argv;
+
+       if (argc != sizeof(args->v0) || args->v0.version != 0)
+               return -ENOSYS;
+
+       outp->dp.enabled = !!args->v0.state;
+       nvkm_dp_enable(outp, outp->dp.enabled);
+       return 0;
+}
+
+static int
 nvkm_uoutp_mthd_hda_eld(struct nvkm_outp *outp, void *argv, u32 argc)
 {
        struct nvkm_ior *ior = outp->ior;
@@ -250,6 +264,7 @@ nvkm_uoutp_mthd_noacquire(struct nvkm_outp *outp, u32 mthd, void *argv, u32 argc
        switch (mthd) {
        case NVIF_OUTP_V0_LOAD_DETECT: return nvkm_uoutp_mthd_load_detect(outp, argv, argc);
        case NVIF_OUTP_V0_ACQUIRE    : return nvkm_uoutp_mthd_acquire    (outp, argv, argc);
+       case NVIF_OUTP_V0_DP_AUX_PWR : return nvkm_uoutp_mthd_dp_aux_pwr (outp, argv, argc);
        default:
                break;
        }