Commit a62b7493 authored by Ben Skeggs's avatar Ben Skeggs

drm/nouveau/disp: add method to control DPAUX pad power

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: default avatarBen Skeggs <bskeggs@redhat.com>
Reviewed-by: default avatarLyude Paul <lyude@redhat.com>
parent 81344372
...@@ -15,6 +15,7 @@ union nvif_outp_args { ...@@ -15,6 +15,7 @@ union nvif_outp_args {
#define NVIF_OUTP_V0_RELEASE 0x02 #define NVIF_OUTP_V0_RELEASE 0x02
#define NVIF_OUTP_V0_INFOFRAME 0x03 #define NVIF_OUTP_V0_INFOFRAME 0x03
#define NVIF_OUTP_V0_HDA_ELD 0x04 #define NVIF_OUTP_V0_HDA_ELD 0x04
#define NVIF_OUTP_V0_DP_AUX_PWR 0x05
union nvif_outp_load_detect_args { union nvif_outp_load_detect_args {
struct nvif_outp_load_detect_v0 { struct nvif_outp_load_detect_v0 {
...@@ -91,4 +92,12 @@ union nvif_outp_hda_eld_args { ...@@ -91,4 +92,12 @@ union nvif_outp_hda_eld_args {
__u8 data[]; __u8 data[];
} v0; } v0;
}; };
union nvif_outp_dp_aux_pwr_args {
struct nvif_outp_dp_aux_pwr_v0 {
__u8 version;
__u8 state;
__u8 pad02[6];
} v0;
};
#endif #endif
...@@ -26,4 +26,5 @@ int nvif_outp_acquire_dp(struct nvif_outp *, u8 dpcd[16], ...@@ -26,4 +26,5 @@ int nvif_outp_acquire_dp(struct nvif_outp *, u8 dpcd[16],
void nvif_outp_release(struct nvif_outp *); 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_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_hda_eld(struct nvif_outp *, int head, void *data, u32 size);
int nvif_outp_dp_aux_pwr(struct nvif_outp *, bool enable);
#endif #endif
...@@ -140,12 +140,17 @@ nouveau_dp_detect(struct nouveau_connector *nv_connector, ...@@ -140,12 +140,17 @@ nouveau_dp_detect(struct nouveau_connector *nv_connector,
* TODO: look into checking this before probing I2C to detect DVI/HDMI * TODO: look into checking this before probing I2C to detect DVI/HDMI
*/ */
hpd = nvif_conn_hpd_status(&nv_connector->conn); 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; goto out;
}
nvif_outp_dp_aux_pwr(&nv_encoder->outp, true);
status = nouveau_dp_probe_dpcd(nv_connector, nv_encoder); 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; goto out;
}
/* If we're in MST mode, we're done here */ /* If we're in MST mode, we're done here */
if (mstm && mstm->can_mst && mstm->is_mst) { if (mstm && mstm->can_mst && mstm->is_mst) {
...@@ -193,6 +198,7 @@ nouveau_dp_detect(struct nouveau_connector *nv_connector, ...@@ -193,6 +198,7 @@ nouveau_dp_detect(struct nouveau_connector *nv_connector,
ret = NOUVEAU_DP_MST; ret = NOUVEAU_DP_MST;
goto out; goto out;
} else if (ret != 0) { } else if (ret != 0) {
nvif_outp_dp_aux_pwr(&nv_encoder->outp, false);
goto out; goto out;
} }
} }
......
...@@ -25,6 +25,20 @@ ...@@ -25,6 +25,20 @@
#include <nvif/class.h> #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 int
nvif_outp_hda_eld(struct nvif_outp *outp, int head, void *data, u32 size) nvif_outp_hda_eld(struct nvif_outp *outp, int head, void *data, u32 size)
{ {
......
...@@ -612,18 +612,38 @@ nvkm_dp_enable_supported_link_rates(struct nvkm_outp *outp) ...@@ -612,18 +612,38 @@ nvkm_dp_enable_supported_link_rates(struct nvkm_outp *outp)
return outp->dp.rates != 0; return outp->dp.rates != 0;
} }
static bool void
nvkm_dp_enable(struct nvkm_outp *outp, bool enable) 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; struct nvkm_i2c_aux *aux = outp->dp.aux;
if (enable) { if (auxpwr && !outp->dp.aux_pwr) {
if (!outp->dp.present) { /* eDP panels need powering on by us (if the VBIOS doesn't default it
OUTP_DBG(outp, "aux power -> always"); * to on) before doing any AUX channel transactions. LVDS panel power
nvkm_i2c_aux_monitor(aux, true); * is handled by the SOR itself, and not required for LVDS DDC.
outp->dp.present = true; */
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. */ /* Detect any LTTPRs before reading DPCD receiver caps. */
if (!nvkm_rdaux(aux, DPCD_LTTPR_REV, outp->dp.lttpr, sizeof(outp->dp.lttpr)) && if (!nvkm_rdaux(aux, DPCD_LTTPR_REV, outp->dp.lttpr, sizeof(outp->dp.lttpr)) &&
outp->dp.lttpr[0] >= 0x14 && outp->dp.lttpr[2]) { outp->dp.lttpr[0] >= 0x14 && outp->dp.lttpr[2]) {
...@@ -676,19 +696,24 @@ nvkm_dp_enable(struct nvkm_outp *outp, bool enable) ...@@ -676,19 +696,24 @@ nvkm_dp_enable(struct nvkm_outp *outp, bool enable)
outp->dp.rates++; outp->dp.rates++;
} }
} }
return true;
} }
} } else
if (!auxpwr && outp->dp.aux_pwr) {
if (outp->dp.present) {
OUTP_DBG(outp, "aux power -> demand"); OUTP_DBG(outp, "aux power -> demand");
nvkm_i2c_aux_monitor(aux, false); 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); /* Restore eDP panel GPIO to its prior state if we changed it, as
return false; * 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 static int
...@@ -705,8 +730,6 @@ nvkm_dp_hpd(struct nvkm_notify *notify) ...@@ -705,8 +730,6 @@ nvkm_dp_hpd(struct nvkm_notify *notify)
if (atomic_read(&outp->dp.lt.done)) if (atomic_read(&outp->dp.lt.done))
outp->func->acquire(outp); outp->func->acquire(outp);
rep.mask |= NVIF_NOTIFY_CONN_V0_IRQ; rep.mask |= NVIF_NOTIFY_CONN_V0_IRQ;
} else {
nvkm_dp_enable(outp, true);
} }
if (line->mask & NVKM_I2C_UNPLUG) if (line->mask & NVKM_I2C_UNPLUG)
...@@ -728,37 +751,8 @@ nvkm_dp_fini(struct nvkm_outp *outp) ...@@ -728,37 +751,8 @@ nvkm_dp_fini(struct nvkm_outp *outp)
static void static void
nvkm_dp_init(struct nvkm_outp *outp) 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); 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); nvkm_notify_get(&outp->dp.hpd);
} }
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
int nvkm_dp_new(struct nvkm_disp *, int index, struct dcb_output *, int nvkm_dp_new(struct nvkm_disp *, int index, struct dcb_output *,
struct nvkm_outp **); struct nvkm_outp **);
void nvkm_dp_disable(struct nvkm_outp *, struct nvkm_ior *); void nvkm_dp_disable(struct nvkm_outp *, struct nvkm_ior *);
void nvkm_dp_enable(struct nvkm_outp *, bool auxpwr);
/* DPCD Receiver Capabilities */ /* DPCD Receiver Capabilities */
#define DPCD_RC00_DPCD_REV 0x00000 #define DPCD_RC00_DPCD_REV 0x00000
......
...@@ -39,7 +39,9 @@ struct nvkm_outp { ...@@ -39,7 +39,9 @@ struct nvkm_outp {
struct nvkm_i2c_aux *aux; struct nvkm_i2c_aux *aux;
struct nvkm_notify hpd; struct nvkm_notify hpd;
bool present; bool enabled;
bool aux_pwr;
bool aux_pwr_pu;
u8 lttpr[6]; u8 lttpr[6];
u8 lttprs; u8 lttprs;
u8 dpcd[16]; u8 dpcd[16];
......
...@@ -21,11 +21,25 @@ ...@@ -21,11 +21,25 @@
*/ */
#define nvkm_uoutp(p) container_of((p), struct nvkm_outp, object) #define nvkm_uoutp(p) container_of((p), struct nvkm_outp, object)
#include "outp.h" #include "outp.h"
#include "dp.h"
#include "head.h" #include "head.h"
#include "ior.h" #include "ior.h"
#include <nvif/if0012.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 static int
nvkm_uoutp_mthd_hda_eld(struct nvkm_outp *outp, void *argv, u32 argc) nvkm_uoutp_mthd_hda_eld(struct nvkm_outp *outp, void *argv, u32 argc)
{ {
...@@ -250,6 +264,7 @@ nvkm_uoutp_mthd_noacquire(struct nvkm_outp *outp, u32 mthd, void *argv, u32 argc ...@@ -250,6 +264,7 @@ nvkm_uoutp_mthd_noacquire(struct nvkm_outp *outp, u32 mthd, void *argv, u32 argc
switch (mthd) { switch (mthd) {
case NVIF_OUTP_V0_LOAD_DETECT: return nvkm_uoutp_mthd_load_detect(outp, argv, argc); 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_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: default:
break; break;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment