summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/nouveau/nvkm
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/nouveau/nvkm')
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c90
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.h1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/uoutp.c15
4 files changed, 61 insertions, 49 deletions
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c
index f1887b58f472..92c9faecffae 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c
@@ -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);
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.h
index 1d86baa6a424..9a6be43916bc 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.h
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.h
@@ -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
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h
index d7c989e1cd01..66def8ae3165 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h
@@ -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];
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/uoutp.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/uoutp.c
index 61d41b326f43..3bfc3e39778e 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/uoutp.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/uoutp.c
@@ -21,12 +21,26 @@
*/
#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;
}