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 {
#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
......@@ -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
......@@ -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;
}
}
......
......@@ -25,6 +25,20 @@
#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)
{
......
......@@ -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);
}
......
......@@ -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
......
......@@ -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];
......
......@@ -21,11 +21,25 @@
*/
#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)
{
......@@ -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;
}
......
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