Commit 220b856a authored by Tanmay Shah's avatar Tanmay Shah Committed by Rob Clark
Browse files

drm/msm/dp: Add Display Port HPD feature



Configure HPD registers in DP controller and
enable HPD interrupt.

Add interrupt to handle HPD connect and disconnect events.

Changes in v8: None

Signed-off-by: default avatarTanmay Shah <tanmay@codeaurora.org>
Signed-off-by: default avatarRob Clark <robdclark@chromium.org>
parent a10476e4
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
@@ -774,6 +774,23 @@ static void dpu_irq_preinstall(struct msm_kms *kms)
	dpu_core_irq_preinstall(dpu_kms);
}

static int dpu_irq_postinstall(struct msm_kms *kms)
{
	struct msm_drm_private *priv;
	struct dpu_kms *dpu_kms = to_dpu_kms(kms);

	if (!dpu_kms || !dpu_kms->dev)
		return -EINVAL;

	priv = dpu_kms->dev->dev_private;
	if (!priv)
		return -EINVAL;

	msm_dp_irq_postinstall(priv->dp);

	return 0;
}

static void dpu_irq_uninstall(struct msm_kms *kms)
{
	struct dpu_kms *dpu_kms = to_dpu_kms(kms);
@@ -784,6 +801,7 @@ static void dpu_irq_uninstall(struct msm_kms *kms)
static const struct msm_kms_funcs kms_funcs = {
	.hw_init         = dpu_kms_hw_init,
	.irq_preinstall  = dpu_irq_preinstall,
	.irq_postinstall = dpu_irq_postinstall,
	.irq_uninstall   = dpu_irq_uninstall,
	.irq             = dpu_irq,
	.enable_commit   = dpu_kms_enable_commit,
+41 −26
Original line number Diff line number Diff line
@@ -17,7 +17,6 @@
#define POLLING_SLEEP_US			1000
#define POLLING_TIMEOUT_US			10000

#define REFTIMER_DEFAULT_VALUE			0x20000
#define SCRAMBLER_RESET_COUNT_VALUE		0xFC

#define DP_INTERRUPT_STATUS_ACK_SHIFT	1
@@ -746,35 +745,51 @@ void dp_catalog_ctrl_enable_irq(struct dp_catalog *dp_catalog,
	}
}

void dp_catalog_ctrl_hpd_config(struct dp_catalog *dp_catalog, bool en)
void dp_catalog_hpd_config_intr(struct dp_catalog *dp_catalog,
			u32 intr_mask, bool en)
{
	struct dp_catalog_private *catalog = container_of(dp_catalog,
				struct dp_catalog_private, dp_catalog);

	if (en) {
		u32 reftimer = dp_read_aux(catalog, REG_DP_DP_HPD_REFTIMER);
	u32 config = dp_read_aux(catalog, REG_DP_DP_HPD_INT_MASK);

	config = (en ? config | intr_mask : config & ~intr_mask);

		dp_write_aux(catalog, REG_DP_DP_HPD_INT_ACK,
				DP_DP_HPD_PLUG_INT_ACK |
				DP_DP_IRQ_HPD_INT_ACK |
				DP_DP_HPD_REPLUG_INT_ACK |
				DP_DP_HPD_UNPLUG_INT_ACK);
	dp_write_aux(catalog, REG_DP_DP_HPD_INT_MASK,
				DP_DP_HPD_PLUG_INT_MASK |
				DP_DP_IRQ_HPD_INT_MASK |
				DP_DP_HPD_REPLUG_INT_MASK |
				DP_DP_HPD_UNPLUG_INT_MASK);
				config & DP_DP_HPD_INT_MASK);
}

void dp_catalog_ctrl_hpd_config(struct dp_catalog *dp_catalog)
{
	struct dp_catalog_private *catalog = container_of(dp_catalog,
				struct dp_catalog_private, dp_catalog);

	u32 reftimer = dp_read_aux(catalog, REG_DP_DP_HPD_REFTIMER);

		/* Configure REFTIMER */
		reftimer |= REFTIMER_DEFAULT_VALUE;
	/* enable HPD interrupts */
	dp_catalog_hpd_config_intr(dp_catalog,
		DP_DP_HPD_PLUG_INT_MASK | DP_DP_IRQ_HPD_INT_MASK
		| DP_DP_HPD_UNPLUG_INT_MASK, true);

	/* Configure REFTIMER and enable it */
	reftimer |= DP_DP_HPD_REFTIMER_ENABLE;
	dp_write_aux(catalog, REG_DP_DP_HPD_REFTIMER, reftimer);

	/* Enable HPD */
		dp_write_aux(catalog, REG_DP_DP_HPD_CTRL,
				DP_DP_HPD_CTRL_HPD_EN);
	} else {
		/* Disable HPD */
		dp_write_aux(catalog, REG_DP_DP_HPD_CTRL, 0x0);
	dp_write_aux(catalog, REG_DP_DP_HPD_CTRL, DP_DP_HPD_CTRL_HPD_EN);
}

u32 dp_catalog_hpd_get_intr_status(struct dp_catalog *dp_catalog)
{
	struct dp_catalog_private *catalog = container_of(dp_catalog,
				struct dp_catalog_private, dp_catalog);
	int isr = 0;

	isr = dp_read_aux(catalog, REG_DP_DP_HPD_INT_STATUS);
	dp_write_aux(catalog, REG_DP_DP_HPD_INT_ACK,
				 (isr & DP_DP_HPD_INT_MASK));

	return isr;
}

int dp_catalog_ctrl_get_interrupt(struct dp_catalog *dp_catalog)
+4 −1
Original line number Diff line number Diff line
@@ -76,7 +76,10 @@ void dp_catalog_ctrl_reset(struct dp_catalog *dp_catalog);
void dp_catalog_ctrl_usb_reset(struct dp_catalog *dp_catalog, bool flip);
bool dp_catalog_ctrl_mainlink_ready(struct dp_catalog *dp_catalog);
void dp_catalog_ctrl_enable_irq(struct dp_catalog *dp_catalog, bool enable);
void dp_catalog_ctrl_hpd_config(struct dp_catalog *dp_catalog, bool enable);
void dp_catalog_hpd_config_intr(struct dp_catalog *dp_catalog,
			u32 intr_mask, bool en);
void dp_catalog_ctrl_hpd_config(struct dp_catalog *dp_catalog);
u32 dp_catalog_hpd_get_intr_status(struct dp_catalog *dp_catalog);
void dp_catalog_ctrl_phy_reset(struct dp_catalog *dp_catalog);
void dp_catalog_ctrl_phy_lane_cfg(struct dp_catalog *dp_catalog, bool flipped,
				u8 lane_cnt);
+0 −1
Original line number Diff line number Diff line
@@ -1563,7 +1563,6 @@ int dp_ctrl_on(struct dp_ctrl *dp_ctrl)
	rate = ctrl->panel->link_info.rate;

	dp_power_clk_enable(ctrl->power, DP_CORE_PM, true);
	dp_catalog_ctrl_hpd_config(ctrl->catalog, true);

	if (ctrl->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) {
		DRM_DEBUG_DP("using phy test link parameters\n");
+101 −7
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
#include "dp_power.h"
#include "dp_catalog.h"
#include "dp_aux.h"
#include "dp_reg.h"
#include "dp_link.h"
#include "dp_panel.h"
#include "dp_ctrl.h"
@@ -36,6 +37,7 @@ struct dp_display_private {
	bool power_on;
	bool hpd_irq_on;
	bool audio_supported;
	atomic_t hpd_isr_status;

	struct platform_device *pdev;
	struct dentry *root;
@@ -54,6 +56,8 @@ struct dp_display_private {
	struct dp_usbpd_cb usbpd_cb;
	struct dp_display_mode dp_mode;
	struct msm_dp dp_display;

	struct delayed_work config_hpd_work;
};

static const struct of_device_id dp_dt_match[] = {
@@ -64,6 +68,20 @@ static const struct of_device_id dp_dt_match[] = {
static irqreturn_t dp_display_irq(int irq, void *dev_id)
{
	struct dp_display_private *dp = dev_id;
	irqreturn_t ret = IRQ_HANDLED;
	u32 hpd_isr_status;

	if (!dp) {
		DRM_ERROR("invalid data\n");
		return IRQ_NONE;
	}

	hpd_isr_status = dp_catalog_hpd_get_intr_status(dp->catalog);

	if (hpd_isr_status & DP_DP_HPD_INT_MASK) {
		atomic_set(&dp->hpd_isr_status, hpd_isr_status);
		ret = IRQ_WAKE_THREAD;
	}

	/* DP controller isr */
	dp_ctrl_isr(dp->ctrl);
@@ -71,6 +89,54 @@ static irqreturn_t dp_display_irq(int irq, void *dev_id)
	/* DP aux isr */
	dp_aux_isr(dp->aux);

	return ret;
}

static irqreturn_t dp_display_hpd_isr_work(int irq, void *data)
{
	struct dp_display_private *dp;
	struct dp_usbpd *hpd;
	u32 isr = 0;

	dp = (struct dp_display_private *)data;
	if (!dp)
		return IRQ_NONE;

	isr = atomic_read(&dp->hpd_isr_status);

	/* reset to default */
	atomic_set(&dp->hpd_isr_status, 0);

	hpd = dp->usbpd;
	if (!hpd)
		return IRQ_NONE;

	if (isr & DP_DP_HPD_PLUG_INT_MASK &&
		isr & DP_DP_HPD_STATE_STATUS_CONNECTED) {
		hpd->hpd_high = 1;
		dp->usbpd_cb.configure(&dp->pdev->dev);
	} else if (isr & DP_DP_HPD_UNPLUG_INT_MASK &&
		(isr & DP_DP_HPD_STATE_STATUS_MASK) ==
			 DP_DP_HPD_STATE_STATUS_DISCONNECTED) {

		/* disable HPD plug interrupt until disconnect is done
		 */
		dp_catalog_hpd_config_intr(dp->catalog,
			DP_DP_HPD_PLUG_INT_MASK | DP_DP_IRQ_HPD_INT_MASK,
			false);

		hpd->hpd_high = 0;

		/* We don't need separate work for disconnect as
		 * connect/attention interrupts are disabled
		 */
		dp->usbpd_cb.disconnect(&dp->pdev->dev);

		dp_catalog_hpd_config_intr(dp->catalog,
			DP_DP_HPD_PLUG_INT_MASK | DP_DP_IRQ_HPD_INT_MASK,
			true);
	}

	return IRQ_HANDLED;
}

@@ -212,8 +278,6 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp)
	int rc = 0;
	struct edid *edid;

	dp_aux_init(dp->aux);

	if (dp->link->psm_enabled)
		goto notify;

@@ -270,10 +334,6 @@ static void dp_display_host_deinit(struct dp_display_private *dp)
		return;
	}

	dp_ctrl_host_deinit(dp->ctrl);
	dp_aux_deinit(dp->aux);
	dp_power_deinit(dp->power);
	disable_irq(dp->irq);
	dp->core_initialized = false;
}

@@ -630,7 +690,8 @@ int dp_display_request_irq(struct msm_dp *dp_display)
		return rc;
	}

	rc = devm_request_irq(&dp->pdev->dev, dp->irq, dp_display_irq,
	rc = devm_request_threaded_irq(&dp->pdev->dev, dp->irq,
		dp_display_irq, dp_display_hpd_isr_work,
		IRQF_TRIGGER_HIGH, "dp_display_isr", dp);
	if (rc < 0) {
		DRM_ERROR("failed to request IRQ%u: %d\n",
@@ -829,6 +890,39 @@ void __exit msm_dp_unregister(void)
	platform_driver_unregister(&dp_display_driver);
}

static void dp_display_config_hpd_work(struct work_struct *work)
{
	struct dp_display_private *dp;
	struct delayed_work *dw = to_delayed_work(work);

	dp = container_of(dw, struct dp_display_private, config_hpd_work);

	dp_display_host_init(dp);
	dp_catalog_ctrl_hpd_config(dp->catalog);

	/* set default to 0 */
	atomic_set(&dp->hpd_isr_status, 0);

	/* Enable interrupt first time
	 * we are leaving dp clocks on during disconnect
	 * and never disable interrupt
	 */
	enable_irq(dp->irq);
}

void msm_dp_irq_postinstall(struct msm_dp *dp_display)
{
	struct dp_display_private *dp;

	if (!dp_display)
		return;

	dp = container_of(dp_display, struct dp_display_private, dp_display);

	INIT_DELAYED_WORK(&dp->config_hpd_work, dp_display_config_hpd_work);
	queue_delayed_work(system_wq, &dp->config_hpd_work, HZ * 10);
}

int msm_dp_modeset_init(struct msm_dp *dp_display, struct drm_device *dev,
			struct drm_encoder *encoder)
{
Loading