Commit cd779808 authored by Vinod Polimera's avatar Vinod Polimera Committed by Dmitry Baryshkov
Browse files

drm/msm/dp: Add basic PSR support for eDP



Add support for basic panel self refresh (PSR) feature for eDP.
Add a new interface to set PSR state in the sink from DPU.
Program the eDP controller to issue PSR enter and exit SDP to
the sink.

Signed-off-by: default avatarSankeerth Billakanti <quic_sbillaka@quicinc.com>
Signed-off-by: default avatarVinod Polimera <quic_vpolimer@quicinc.com>
Reviewed-by: default avatarDmitry Baryshkov <dmitry.baryshkov@linaro.org>
Patchwork: https://patchwork.freedesktop.org/patch/524734/
Link: https://lore.kernel.org/r/1677774797-31063-10-git-send-email-quic_vpolimer@quicinc.com


Signed-off-by: default avatarDmitry Baryshkov <dmitry.baryshkov@linaro.org>
parent cdfd0e62
Loading
Loading
Loading
Loading
+80 −0
Original line number Diff line number Diff line
@@ -47,6 +47,14 @@
#define DP_INTERRUPT_STATUS2_MASK \
	(DP_INTERRUPT_STATUS2 << DP_INTERRUPT_STATUS_MASK_SHIFT)

#define DP_INTERRUPT_STATUS4 \
	(PSR_UPDATE_INT | PSR_CAPTURE_INT | PSR_EXIT_INT | \
	PSR_UPDATE_ERROR_INT | PSR_WAKE_ERROR_INT)

#define DP_INTERRUPT_MASK4 \
	(PSR_UPDATE_MASK | PSR_CAPTURE_MASK | PSR_EXIT_MASK | \
	PSR_UPDATE_ERROR_MASK | PSR_WAKE_ERROR_MASK)

struct dp_catalog_private {
	struct device *dev;
	struct drm_device *drm_dev;
@@ -359,6 +367,23 @@ void dp_catalog_ctrl_lane_mapping(struct dp_catalog *dp_catalog)
			ln_mapping);
}

void dp_catalog_ctrl_psr_mainlink_enable(struct dp_catalog *dp_catalog,
						bool enable)
{
	u32 val;
	struct dp_catalog_private *catalog = container_of(dp_catalog,
				struct dp_catalog_private, dp_catalog);

	val = dp_read_link(catalog, REG_DP_MAINLINK_CTRL);

	if (enable)
		val |= DP_MAINLINK_CTRL_ENABLE;
	else
		val &= ~DP_MAINLINK_CTRL_ENABLE;

	dp_write_link(catalog, REG_DP_MAINLINK_CTRL, val);
}

void dp_catalog_ctrl_mainlink_ctrl(struct dp_catalog *dp_catalog,
						bool enable)
{
@@ -610,6 +635,47 @@ void dp_catalog_ctrl_hpd_config(struct dp_catalog *dp_catalog)
	dp_write_aux(catalog, REG_DP_DP_HPD_CTRL, DP_DP_HPD_CTRL_HPD_EN);
}

static void dp_catalog_enable_sdp(struct dp_catalog_private *catalog)
{
	/* trigger sdp */
	dp_write_link(catalog, MMSS_DP_SDP_CFG3, UPDATE_SDP);
	dp_write_link(catalog, MMSS_DP_SDP_CFG3, 0x0);
}

void dp_catalog_ctrl_config_psr(struct dp_catalog *dp_catalog)
{
	struct dp_catalog_private *catalog = container_of(dp_catalog,
				struct dp_catalog_private, dp_catalog);
	u32 config;

	/* enable PSR1 function */
	config = dp_read_link(catalog, REG_PSR_CONFIG);
	config |= PSR1_SUPPORTED;
	dp_write_link(catalog, REG_PSR_CONFIG, config);

	dp_write_ahb(catalog, REG_DP_INTR_MASK4, DP_INTERRUPT_MASK4);
	dp_catalog_enable_sdp(catalog);
}

void dp_catalog_ctrl_set_psr(struct dp_catalog *dp_catalog, bool enter)
{
	struct dp_catalog_private *catalog = container_of(dp_catalog,
			struct dp_catalog_private, dp_catalog);
	u32 cmd;

	cmd = dp_read_link(catalog, REG_PSR_CMD);

	cmd &= ~(PSR_ENTER | PSR_EXIT);

	if (enter)
		cmd |= PSR_ENTER;
	else
		cmd |= PSR_EXIT;

	dp_catalog_enable_sdp(catalog);
	dp_write_link(catalog, REG_PSR_CMD, cmd);
}

u32 dp_catalog_link_is_connected(struct dp_catalog *dp_catalog)
{
	struct dp_catalog_private *catalog = container_of(dp_catalog,
@@ -645,6 +711,20 @@ u32 dp_catalog_hpd_get_intr_status(struct dp_catalog *dp_catalog)
	return isr & (mask | ~DP_DP_HPD_INT_MASK);
}

u32 dp_catalog_ctrl_read_psr_interrupt_status(struct dp_catalog *dp_catalog)
{
	struct dp_catalog_private *catalog = container_of(dp_catalog,
				struct dp_catalog_private, dp_catalog);
	u32 intr, intr_ack;

	intr = dp_read_ahb(catalog, REG_DP_INTR_STATUS4);
	intr_ack = (intr & DP_INTERRUPT_STATUS4)
			<< DP_INTERRUPT_STATUS_ACK_SHIFT;
	dp_write_ahb(catalog, REG_DP_INTR_STATUS4, intr_ack);

	return intr;
}

int dp_catalog_ctrl_get_interrupt(struct dp_catalog *dp_catalog)
{
	struct dp_catalog_private *catalog = container_of(dp_catalog,
+4 −0
Original line number Diff line number Diff line
@@ -93,6 +93,7 @@ void dp_catalog_ctrl_state_ctrl(struct dp_catalog *dp_catalog, u32 state);
void dp_catalog_ctrl_config_ctrl(struct dp_catalog *dp_catalog, u32 config);
void dp_catalog_ctrl_lane_mapping(struct dp_catalog *dp_catalog);
void dp_catalog_ctrl_mainlink_ctrl(struct dp_catalog *dp_catalog, bool enable);
void dp_catalog_ctrl_psr_mainlink_enable(struct dp_catalog *dp_catalog, bool enable);
void dp_catalog_ctrl_config_misc(struct dp_catalog *dp_catalog, u32 cc, u32 tb);
void dp_catalog_ctrl_config_msa(struct dp_catalog *dp_catalog, u32 rate,
				u32 stream_rate_khz, bool fixed_nvid);
@@ -104,12 +105,15 @@ void dp_catalog_ctrl_enable_irq(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);
void dp_catalog_ctrl_config_psr(struct dp_catalog *dp_catalog);
void dp_catalog_ctrl_set_psr(struct dp_catalog *dp_catalog, bool enter);
u32 dp_catalog_link_is_connected(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);
int dp_catalog_ctrl_update_vx_px(struct dp_catalog *dp_catalog, u8 v_level,
				u8 p_level);
int dp_catalog_ctrl_get_interrupt(struct dp_catalog *dp_catalog);
u32 dp_catalog_ctrl_read_psr_interrupt_status(struct dp_catalog *dp_catalog);
void dp_catalog_ctrl_update_transfer_unit(struct dp_catalog *dp_catalog,
				u32 dp_tu, u32 valid_boundary,
				u32 valid_boundary2);
+80 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@

#define DP_KHZ_TO_HZ 1000
#define IDLE_PATTERN_COMPLETION_TIMEOUT_JIFFIES	(30 * HZ / 1000) /* 30 ms */
#define PSR_OPERATION_COMPLETION_TIMEOUT_JIFFIES       (300 * HZ / 1000) /* 300 ms */
#define WAIT_FOR_VIDEO_READY_TIMEOUT_JIFFIES (HZ / 2)

#define DP_CTRL_INTR_READY_FOR_VIDEO     BIT(0)
@@ -80,6 +81,7 @@ struct dp_ctrl_private {
	struct dp_catalog *catalog;

	struct completion idle_comp;
	struct completion psr_op_comp;
	struct completion video_comp;
};

@@ -153,6 +155,9 @@ static void dp_ctrl_config_ctrl(struct dp_ctrl_private *ctrl)
	config |= DP_CONFIGURATION_CTRL_STATIC_DYNAMIC_CN;
	config |= DP_CONFIGURATION_CTRL_SYNC_ASYNC_CLK;

	if (ctrl->panel->psr_cap.version)
		config |= DP_CONFIGURATION_CTRL_SEND_VSC;

	dp_catalog_ctrl_config_ctrl(ctrl->catalog, config);
}

@@ -1375,6 +1380,64 @@ void dp_ctrl_reset_irq_ctrl(struct dp_ctrl *dp_ctrl, bool enable)
	dp_catalog_ctrl_enable_irq(ctrl->catalog, enable);
}

void dp_ctrl_config_psr(struct dp_ctrl *dp_ctrl)
{
	u8 cfg;
	struct dp_ctrl_private *ctrl = container_of(dp_ctrl,
			struct dp_ctrl_private, dp_ctrl);

	if (!ctrl->panel->psr_cap.version)
		return;

	dp_catalog_ctrl_config_psr(ctrl->catalog);

	cfg = DP_PSR_ENABLE;
	drm_dp_dpcd_write(ctrl->aux, DP_PSR_EN_CFG, &cfg, 1);
}

void dp_ctrl_set_psr(struct dp_ctrl *dp_ctrl, bool enter)
{
	struct dp_ctrl_private *ctrl = container_of(dp_ctrl,
			struct dp_ctrl_private, dp_ctrl);

	if (!ctrl->panel->psr_cap.version)
		return;

	/*
	 * When entering PSR,
	 * 1. Send PSR enter SDP and wait for the PSR_UPDATE_INT
	 * 2. Turn off video
	 * 3. Disable the mainlink
	 *
	 * When exiting PSR,
	 * 1. Enable the mainlink
	 * 2. Send the PSR exit SDP
	 */
	if (enter) {
		reinit_completion(&ctrl->psr_op_comp);
		dp_catalog_ctrl_set_psr(ctrl->catalog, true);

		if (!wait_for_completion_timeout(&ctrl->psr_op_comp,
			PSR_OPERATION_COMPLETION_TIMEOUT_JIFFIES)) {
			DRM_ERROR("PSR_ENTRY timedout\n");
			dp_catalog_ctrl_set_psr(ctrl->catalog, false);
			return;
		}

		dp_ctrl_push_idle(dp_ctrl);
		dp_catalog_ctrl_state_ctrl(ctrl->catalog, 0);

		dp_catalog_ctrl_psr_mainlink_enable(ctrl->catalog, false);
	} else {
		dp_catalog_ctrl_psr_mainlink_enable(ctrl->catalog, true);

		dp_catalog_ctrl_set_psr(ctrl->catalog, false);
		dp_catalog_ctrl_state_ctrl(ctrl->catalog, DP_STATE_CTRL_SEND_VIDEO);
		dp_ctrl_wait4video_ready(ctrl);
		dp_catalog_ctrl_state_ctrl(ctrl->catalog, 0);
	}
}

void dp_ctrl_phy_init(struct dp_ctrl *dp_ctrl)
{
	struct dp_ctrl_private *ctrl;
@@ -1989,6 +2052,22 @@ void dp_ctrl_isr(struct dp_ctrl *dp_ctrl)

	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);

	if (ctrl->panel->psr_cap.version) {
		isr = dp_catalog_ctrl_read_psr_interrupt_status(ctrl->catalog);

		if (isr)
			complete(&ctrl->psr_op_comp);

		if (isr & PSR_EXIT_INT)
			drm_dbg_dp(ctrl->drm_dev, "PSR exit done\n");

		if (isr & PSR_UPDATE_INT)
			drm_dbg_dp(ctrl->drm_dev, "PSR frame update done\n");

		if (isr & PSR_CAPTURE_INT)
			drm_dbg_dp(ctrl->drm_dev, "PSR frame capture done\n");
	}

	isr = dp_catalog_ctrl_get_interrupt(ctrl->catalog);

	if (isr & DP_CTRL_INTR_READY_FOR_VIDEO) {
@@ -2035,6 +2114,7 @@ struct dp_ctrl *dp_ctrl_get(struct device *dev, struct dp_link *link,
		dev_err(dev, "failed to add DP OPP table\n");

	init_completion(&ctrl->idle_comp);
	init_completion(&ctrl->psr_op_comp);
	init_completion(&ctrl->video_comp);

	/* in parameters */
+3 −0
Original line number Diff line number Diff line
@@ -37,4 +37,7 @@ void dp_ctrl_phy_init(struct dp_ctrl *dp_ctrl);
void dp_ctrl_phy_exit(struct dp_ctrl *dp_ctrl);
void dp_ctrl_irq_phy_exit(struct dp_ctrl *dp_ctrl);

void dp_ctrl_set_psr(struct dp_ctrl *dp_ctrl, bool enable);
void dp_ctrl_config_psr(struct dp_ctrl *dp_ctrl);

#endif /* _DP_CTRL_H_ */
+19 −0
Original line number Diff line number Diff line
@@ -406,6 +406,8 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp)

	edid = dp->panel->edid;

	dp->dp_display.psr_supported = dp->panel->psr_cap.version;

	dp->audio_supported = drm_detect_monitor_audio(edid);
	dp_panel_handle_sink_request(dp->panel);

@@ -910,6 +912,10 @@ static int dp_display_post_enable(struct msm_dp *dp_display)

	/* signal the connect event late to synchronize video and display */
	dp_display_handle_plugged_change(dp_display, true);

	if (dp_display->psr_supported)
		dp_ctrl_config_psr(dp->ctrl);

	return 0;
}

@@ -1104,6 +1110,19 @@ static void dp_display_config_hpd(struct dp_display_private *dp)
	enable_irq(dp->irq);
}

void dp_display_set_psr(struct msm_dp *dp_display, bool enter)
{
	struct dp_display_private *dp;

	if (!dp_display) {
		DRM_ERROR("invalid params\n");
		return;
	}

	dp = container_of(dp_display, struct dp_display_private, dp_display);
	dp_ctrl_set_psr(dp->ctrl, enter);
}

static int hpd_event_thread(void *data)
{
	struct dp_display_private *dp_priv;
Loading