Commit a62b7493 authored by Ben Skeggs's avatar Ben Skeggs
Browse files

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
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -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
+1 −0
Original line number Diff line number Diff line
@@ -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
+8 −2
Original line number Diff line number Diff line
@@ -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;
		}
	}
+14 −0
Original line number Diff line number Diff line
@@ -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)
{
+42 −48
Original line number Diff line number Diff line
@@ -612,17 +612,37 @@ 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) {
	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.present = 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)) &&
@@ -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);
	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);
}

Loading