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

drm/nouveau/disp/dp: maintain receiver caps in response to hpd signal



Signed-off-by: default avatarBen Skeggs <bskeggs@redhat.com>
parent b8407c9e
Loading
Loading
Loading
Loading
+64 −8
Original line number Diff line number Diff line
@@ -26,21 +26,73 @@

#include "outpdp.h"
#include "conn.h"
#include "dport.h"

static int
nvkm_output_dp_service(void *data, u32 type, int index)
static void
nvkm_output_dp_enable(struct nvkm_output_dp *outp, bool present)
{
	struct nvkm_output_dp *outp = data;
	DBG("IRQ: %d\n", type);
	return NVKM_EVENT_KEEP;
	struct nouveau_i2c_port *port = outp->base.edid;
	if (present) {
		if (!outp->present) {
			nouveau_i2c(port)->acquire_pad(port, 0);
			DBG("aux power -> always\n");
			outp->present = true;
		}
	} else {
		if (outp->present) {
			nouveau_i2c(port)->release_pad(port);
			DBG("aux power -> demand\n");
			outp->present = false;
		}
	}
}

static void
nvkm_output_dp_detect(struct nvkm_output_dp *outp)
{
	struct nouveau_i2c_port *port = outp->base.edid;
	int ret = nouveau_i2c(port)->acquire_pad(port, 0);
	if (ret == 0) {
		ret = nv_rdaux(outp->base.edid, DPCD_RC00_DPCD_REV,
			       outp->dpcd, sizeof(outp->dpcd));
		nvkm_output_dp_enable(outp, ret == 0);
		nouveau_i2c(port)->release_pad(port);
	}
}

static void
nvkm_output_dp_service_work(struct work_struct *work)
{
	struct nvkm_output_dp *outp = container_of(work, typeof(*outp), work);
	struct nouveau_disp *disp = nouveau_disp(outp);
	int type = atomic_xchg(&outp->pending, 0);
	u32 send = 0;

	if (type & (NVKM_I2C_PLUG | NVKM_I2C_UNPLUG)) {
		nvkm_output_dp_detect(outp);
		if (type & NVKM_I2C_UNPLUG)
			send |= NVKM_HPD_UNPLUG;
		if (type & NVKM_I2C_PLUG)
			send |= NVKM_HPD_PLUG;
		nouveau_event_get(outp->base.conn->hpd.event);
	}

	if (type & NVKM_I2C_IRQ) {
		nouveau_event_get(outp->irq);
		send |= NVKM_HPD_IRQ;
	}

	nouveau_event_trigger(disp->hpd, send, outp->base.info.connector);
}

static int
nvkm_output_dp_hotplug(void *data, u32 type, int index)
nvkm_output_dp_service(void *data, u32 type, int index)
{
	struct nvkm_output_dp *outp = data;
	DBG("HPD: %d\n", type);
	return NVKM_EVENT_KEEP;
	atomic_or(type, &outp->pending);
	schedule_work(&outp->work);
	return NVKM_EVENT_DROP;
}

int
@@ -48,6 +100,7 @@ _nvkm_output_dp_fini(struct nouveau_object *object, bool suspend)
{
	struct nvkm_output_dp *outp = (void *)object;
	nouveau_event_put(outp->irq);
	nvkm_output_dp_enable(outp, false);
	return nvkm_output_fini(&outp->base, suspend);
}

@@ -55,6 +108,7 @@ int
_nvkm_output_dp_init(struct nouveau_object *object)
{
	struct nvkm_output_dp *outp = (void *)object;
	nvkm_output_dp_detect(outp);
	return nvkm_output_init(&outp->base);
}

@@ -113,10 +167,12 @@ nvkm_output_dp_create_(struct nouveau_object *parent,
		return ret;
	}

	INIT_WORK(&outp->work, nvkm_output_dp_service_work);

	/* hotplug detect, replaces gpio-based mechanism with aux events */
	ret = nouveau_event_new(i2c->ntfy, NVKM_I2C_PLUG | NVKM_I2C_UNPLUG,
				outp->base.edid->index,
				nvkm_output_dp_hotplug, outp,
				nvkm_output_dp_service, outp,
			       &outp->base.conn->hpd.event);
	if (ret) {
		ERR("error monitoring aux hpd events: %d\n", ret);
+4 −0
Original line number Diff line number Diff line
@@ -14,6 +14,10 @@ struct nvkm_output_dp {

	struct nouveau_eventh *irq;
	struct nouveau_eventh *hpd;
	struct work_struct work;
	atomic_t pending;
	bool present;
	u8 dpcd[16];
};

#define nvkm_output_dp_create(p,e,c,b,i,d)                                     \