All of lore.kernel.org
 help / color / mirror / Atom feed
* [v3 0/5] Add PSR support for eDP
@ 2022-06-21 10:53 ` Vinod Polimera
  0 siblings, 0 replies; 32+ messages in thread
From: Vinod Polimera @ 2022-06-21 10:53 UTC (permalink / raw)
  To: y, dri-devel, linux-arm-msm, freedreno, devicetree
  Cc: Vinod Polimera, linux-kernel, robdclark, dianders, swboyd,
	quic_kalyant, dmitry.baryshkov, quic_sbillaka

Changes in v2:
  - Use dp bridge to set psr entry/exit instead of dpu_enocder.
  - Don't modify whitespaces.
  - Set self refresh aware from atomic_check.
  - Set self refresh aware only if psr is supported.
  - Provide a stub for msm_dp_display_set_psr.
  - Move dp functions to bridge code.

Changes in v3:
  - Change callback names to reflect atomic interfaces.
  - Move bridge callback change to separate patch as suggested by Dmitry.
  - Remove psr function declaration from msm_drv.h.
  - Set self_refresh_aware flag only if psr is supported.
  - Modify the variable names to simpler form.
  - Define bit fields for PSR settings.
  - Add comments explaining the steps to enter/exit psr.
  - Change DRM_INFO to drm_dbg_db. 

Signed-off-by: Sankeerth Billakanti <quic_sbillaka@quicinc.com>
Signed-off-by: Kalyan Thota <quic_kalyant@quicinc.com>
Signed-off-by: Vinod Polimera <quic_vpolimer@quicinc.com>

Vinod Polimera (5):
  drm/msm/dp: Add basic PSR support for eDP
  drm/bridge: use atomic enable/disable callbacks for panel bridge
    functions
  drm/bridge: add psr support during panel bridge enable & disable
    sequence
  drm/msm/disp/dpu1: use atomic enable/disable callbacks for encoder
    functions
  drm/msm/disp/dpu1: add PSR support for eDP interface in dpu driver

 drivers/gpu/drm/bridge/panel.c              | 110 ++++++++++++++++--
 drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c    |  36 +++++-
 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c |  30 ++++-
 drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c     |   2 +-
 drivers/gpu/drm/msm/dp/dp_catalog.c         |  81 ++++++++++++++
 drivers/gpu/drm/msm/dp/dp_catalog.h         |   4 +
 drivers/gpu/drm/msm/dp/dp_ctrl.c            |  76 ++++++++++++-
 drivers/gpu/drm/msm/dp/dp_ctrl.h            |   3 +
 drivers/gpu/drm/msm/dp/dp_display.c         |  14 +++
 drivers/gpu/drm/msm/dp/dp_display.h         |   2 +
 drivers/gpu/drm/msm/dp/dp_drm.c             | 166 +++++++++++++++++++++++++++-
 drivers/gpu/drm/msm/dp/dp_link.c            |  36 ++++++
 drivers/gpu/drm/msm/dp/dp_panel.c           |  22 ++++
 drivers/gpu/drm/msm/dp/dp_panel.h           |   6 +
 drivers/gpu/drm/msm/dp/dp_reg.h             |  27 +++++
 15 files changed, 591 insertions(+), 24 deletions(-)

-- 
2.7.4


^ permalink raw reply	[flat|nested] 32+ messages in thread

* [v3 0/5] Add PSR support for eDP
@ 2022-06-21 10:53 ` Vinod Polimera
  0 siblings, 0 replies; 32+ messages in thread
From: Vinod Polimera @ 2022-06-21 10:53 UTC (permalink / raw)
  To: y, dri-devel, linux-arm-msm, freedreno, devicetree
  Cc: quic_kalyant, quic_sbillaka, dianders, linux-kernel,
	dmitry.baryshkov, swboyd, Vinod Polimera

Changes in v2:
  - Use dp bridge to set psr entry/exit instead of dpu_enocder.
  - Don't modify whitespaces.
  - Set self refresh aware from atomic_check.
  - Set self refresh aware only if psr is supported.
  - Provide a stub for msm_dp_display_set_psr.
  - Move dp functions to bridge code.

Changes in v3:
  - Change callback names to reflect atomic interfaces.
  - Move bridge callback change to separate patch as suggested by Dmitry.
  - Remove psr function declaration from msm_drv.h.
  - Set self_refresh_aware flag only if psr is supported.
  - Modify the variable names to simpler form.
  - Define bit fields for PSR settings.
  - Add comments explaining the steps to enter/exit psr.
  - Change DRM_INFO to drm_dbg_db. 

Signed-off-by: Sankeerth Billakanti <quic_sbillaka@quicinc.com>
Signed-off-by: Kalyan Thota <quic_kalyant@quicinc.com>
Signed-off-by: Vinod Polimera <quic_vpolimer@quicinc.com>

Vinod Polimera (5):
  drm/msm/dp: Add basic PSR support for eDP
  drm/bridge: use atomic enable/disable callbacks for panel bridge
    functions
  drm/bridge: add psr support during panel bridge enable & disable
    sequence
  drm/msm/disp/dpu1: use atomic enable/disable callbacks for encoder
    functions
  drm/msm/disp/dpu1: add PSR support for eDP interface in dpu driver

 drivers/gpu/drm/bridge/panel.c              | 110 ++++++++++++++++--
 drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c    |  36 +++++-
 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c |  30 ++++-
 drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c     |   2 +-
 drivers/gpu/drm/msm/dp/dp_catalog.c         |  81 ++++++++++++++
 drivers/gpu/drm/msm/dp/dp_catalog.h         |   4 +
 drivers/gpu/drm/msm/dp/dp_ctrl.c            |  76 ++++++++++++-
 drivers/gpu/drm/msm/dp/dp_ctrl.h            |   3 +
 drivers/gpu/drm/msm/dp/dp_display.c         |  14 +++
 drivers/gpu/drm/msm/dp/dp_display.h         |   2 +
 drivers/gpu/drm/msm/dp/dp_drm.c             | 166 +++++++++++++++++++++++++++-
 drivers/gpu/drm/msm/dp/dp_link.c            |  36 ++++++
 drivers/gpu/drm/msm/dp/dp_panel.c           |  22 ++++
 drivers/gpu/drm/msm/dp/dp_panel.h           |   6 +
 drivers/gpu/drm/msm/dp/dp_reg.h             |  27 +++++
 15 files changed, 591 insertions(+), 24 deletions(-)

-- 
2.7.4


^ permalink raw reply	[flat|nested] 32+ messages in thread

* [v3 1/5] drm/msm/dp: Add basic PSR support for eDP
  2022-06-21 10:53 ` Vinod Polimera
@ 2022-06-21 10:53   ` Vinod Polimera
  -1 siblings, 0 replies; 32+ messages in thread
From: Vinod Polimera @ 2022-06-21 10:53 UTC (permalink / raw)
  To: y, dri-devel, linux-arm-msm, freedreno, devicetree
  Cc: Vinod Polimera, linux-kernel, robdclark, dianders, swboyd,
	quic_kalyant, dmitry.baryshkov, quic_sbillaka

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: Sankeerth Billakanti <quic_sbillaka@quicinc.com>
Signed-off-by: Vinod Polimera <quic_vpolimer@quicinc.com>
---
 drivers/gpu/drm/msm/dp/dp_catalog.c |  81 ++++++++++++++++++
 drivers/gpu/drm/msm/dp/dp_catalog.h |   4 +
 drivers/gpu/drm/msm/dp/dp_ctrl.c    |  76 ++++++++++++++++-
 drivers/gpu/drm/msm/dp/dp_ctrl.h    |   3 +
 drivers/gpu/drm/msm/dp/dp_display.c |  14 +++
 drivers/gpu/drm/msm/dp/dp_display.h |   2 +
 drivers/gpu/drm/msm/dp/dp_drm.c     | 166 +++++++++++++++++++++++++++++++++++-
 drivers/gpu/drm/msm/dp/dp_link.c    |  36 ++++++++
 drivers/gpu/drm/msm/dp/dp_panel.c   |  22 +++++
 drivers/gpu/drm/msm/dp/dp_panel.h   |   6 ++
 drivers/gpu/drm/msm/dp/dp_reg.h     |  27 ++++++
 11 files changed, 433 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.c b/drivers/gpu/drm/msm/dp/dp_catalog.c
index 7257515..b9021ed 100644
--- a/drivers/gpu/drm/msm/dp/dp_catalog.c
+++ b/drivers/gpu/drm/msm/dp/dp_catalog.c
@@ -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,24 @@ 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);
+	val &= ~DP_MAINLINK_CTRL_ENABLE;
+
+	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 +636,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, !UPDATE_SDP);
+}
+
+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 +712,20 @@ u32 dp_catalog_hpd_get_intr_status(struct dp_catalog *dp_catalog)
 	return isr & (mask | ~DP_DP_HPD_INT_MASK);
 }
 
+int 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,
diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.h b/drivers/gpu/drm/msm/dp/dp_catalog.h
index 1f717f4..6454845 100644
--- a/drivers/gpu/drm/msm/dp/dp_catalog.h
+++ b/drivers/gpu/drm/msm/dp/dp_catalog.h
@@ -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);
+int 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);
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index d21971b..485e8f5 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -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);
 }
 
@@ -1382,11 +1387,64 @@ static int dp_ctrl_enable_stream_clocks(struct dp_ctrl_private *ctrl)
 	return ret;
 }
 
-void dp_ctrl_reset_irq_ctrl(struct dp_ctrl *dp_ctrl, bool enable)
+void dp_ctrl_config_psr(struct dp_ctrl *dp_ctrl)
 {
-	struct dp_ctrl_private *ctrl;
+	u8 cfg;
+	struct dp_ctrl_private *ctrl = container_of(dp_ctrl,
+			struct dp_ctrl_private, dp_ctrl);
 
-	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_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);
+	}
+}
+
+void dp_ctrl_reset_irq_ctrl(struct dp_ctrl *dp_ctrl, bool enable)
+{
+	struct dp_ctrl_private *ctrl = container_of(dp_ctrl,
+			struct dp_ctrl_private, dp_ctrl);
 
 	dp_catalog_ctrl_reset(ctrl->catalog);
 
@@ -1997,6 +2055,17 @@ 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 == PSR_UPDATE_INT)
+			drm_dbg_dp(ctrl->drm_dev, "PSR frame update done\n");
+		else if (isr == PSR_EXIT_INT)
+			drm_dbg_dp(ctrl->drm_dev, "PSR exit done\n");
+
+		complete(&ctrl->psr_op_comp);
+	}
+
 	isr = dp_catalog_ctrl_get_interrupt(ctrl->catalog);
 
 	if (isr & DP_CTRL_INTR_READY_FOR_VIDEO) {
@@ -2043,6 +2112,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 */
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.h b/drivers/gpu/drm/msm/dp/dp_ctrl.h
index 0745fde..be074ae 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.h
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h
@@ -38,4 +38,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_ */
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index bce7793..2e779c2 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -388,6 +388,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);
 
@@ -895,6 +897,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;
 }
 
@@ -1094,6 +1100,14 @@ 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;
+
+	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;
diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
index 4f9fe4d..1feaada 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.h
+++ b/drivers/gpu/drm/msm/dp/dp_display.h
@@ -29,6 +29,7 @@ struct msm_dp {
 
 	u32 max_dp_lanes;
 	struct dp_audio *dp_audio;
+	bool psr_supported;
 };
 
 int dp_display_set_plugged_cb(struct msm_dp *dp_display,
@@ -39,5 +40,6 @@ bool dp_display_check_video_test(struct msm_dp *dp_display);
 int dp_display_get_test_bpp(struct msm_dp *dp_display);
 void dp_display_signal_audio_start(struct msm_dp *dp_display);
 void dp_display_signal_audio_complete(struct msm_dp *dp_display);
+void dp_display_set_psr(struct msm_dp *dp, bool enter);
 
 #endif /* _DP_DISPLAY_H_ */
diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c b/drivers/gpu/drm/msm/dp/dp_drm.c
index 62d58b9..cfe31ed 100644
--- a/drivers/gpu/drm/msm/dp/dp_drm.c
+++ b/drivers/gpu/drm/msm/dp/dp_drm.c
@@ -60,6 +60,169 @@ static int dp_bridge_get_modes(struct drm_bridge *bridge, struct drm_connector *
 	return rc;
 }
 
+static struct drm_crtc *dp_bridge_get_old_connector_crtc(struct msm_dp *dp,
+					  struct drm_atomic_state *state)
+{
+	struct drm_encoder *encoder = dp->encoder;
+	struct drm_connector *connector;
+	struct drm_connector_state *conn_state;
+
+	connector = drm_atomic_get_old_connector_for_encoder(state, encoder);
+	if (!connector)
+		return NULL;
+
+	conn_state = drm_atomic_get_old_connector_state(state, connector);
+	if (!conn_state)
+		return NULL;
+
+	return conn_state->crtc;
+}
+
+static struct drm_crtc *dp_bridge_get_new_connector_crtc(struct msm_dp *dp,
+					  struct drm_atomic_state *state)
+{
+	struct drm_encoder *encoder = dp->encoder;
+	struct drm_connector *connector;
+	struct drm_connector_state *conn_state;
+
+	connector = drm_atomic_get_new_connector_for_encoder(state, encoder);
+	if (!connector)
+		return NULL;
+
+	conn_state = drm_atomic_get_new_connector_state(state, connector);
+	if (!conn_state)
+		return NULL;
+
+	return conn_state->crtc;
+}
+
+static int edp_bridge_atomic_check(struct drm_bridge *drm_bridge,
+			    struct drm_bridge_state *bridge_state,
+			    struct drm_crtc_state *crtc_state,
+			    struct drm_connector_state *conn_state)
+{
+	struct msm_dp *dp;
+
+	dp = to_dp_bridge(drm_bridge)->dp_display;
+	if (WARN_ON(!conn_state))
+		return -ENODEV;
+
+	if (dp->psr_supported)
+		conn_state->self_refresh_aware = true;
+
+	if (!conn_state->crtc || !crtc_state)
+		return 0;
+
+	if (crtc_state->self_refresh_active && !dp->psr_supported)
+		return -EINVAL;
+
+	return 0;
+}
+
+static void edp_bridge_atomic_enable(struct drm_bridge *drm_bridge,
+				struct drm_bridge_state *old_bridge_state)
+{
+	struct drm_atomic_state *old_state = old_bridge_state->base.state;
+	struct drm_crtc *crtc;
+	struct drm_crtc_state *old_crtc_state;
+	struct msm_dp_bridge *dp_bridge = to_dp_bridge(drm_bridge);
+	struct msm_dp *dp_display = dp_bridge->dp_display;
+
+	crtc = dp_bridge_get_new_connector_crtc(dp_display, old_state);
+	if (!crtc)
+		return;
+
+	old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc);
+
+	/* Exit from self refresh mode */
+	if (old_crtc_state && old_crtc_state->self_refresh_active) {
+		dp_display_set_psr(dp_display, false);
+		return;
+	}
+
+	dp_bridge_enable(drm_bridge);
+}
+
+static void edp_bridge_atomic_disable(struct drm_bridge *drm_bridge,
+				struct drm_bridge_state *old_bridge_state)
+{
+	struct drm_atomic_state *old_state = old_bridge_state->base.state;
+	struct drm_crtc *crtc;
+	struct drm_crtc_state *new_crtc_state = NULL, *old_crtc_state = NULL;
+	struct msm_dp_bridge *dp_bridge = to_dp_bridge(drm_bridge);
+	struct msm_dp *dp_display = dp_bridge->dp_display;
+
+	crtc = dp_bridge_get_old_connector_crtc(dp_display, old_state);
+	if (!crtc)
+		goto out;
+
+	new_crtc_state = drm_atomic_get_new_crtc_state(old_state, crtc);
+	if (!new_crtc_state)
+		goto out;
+
+	old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc);
+	if (!old_crtc_state)
+		goto out;
+
+	/*
+	 * Set self refresh mode if current crtc state is active.
+	 * If old crtc state is active, then this is a display disable
+	 * call while the sink is in psr state. So, exit psr here.
+	 * The eDP controller will be disabled in the
+	 * edp_bridge_atomic_post_disable function.
+	 *
+	 * We observed sink is stuck in self refresh if psr exit is skipped
+	 * when display disable occurs while the sink is in psr state.
+	 */
+	if (new_crtc_state->self_refresh_active) {
+		dp_display_set_psr(dp_display, true);
+		return;
+	} else if (old_crtc_state->self_refresh_active) {
+		dp_display_set_psr(dp_display, false);
+		return;
+	}
+
+out:
+	dp_bridge_disable(drm_bridge);
+}
+
+static void edp_bridge_atomic_post_disable(struct drm_bridge *drm_bridge,
+				struct drm_bridge_state *old_bridge_state)
+{
+	struct drm_atomic_state *old_state = old_bridge_state->base.state;
+	struct drm_crtc *crtc;
+	struct drm_crtc_state *new_crtc_state = NULL;
+	struct msm_dp_bridge *dp_bridge = to_dp_bridge(drm_bridge);
+	struct msm_dp *dp_display = dp_bridge->dp_display;
+
+	crtc = dp_bridge_get_old_connector_crtc(dp_display, old_state);
+	if (!crtc)
+		return;
+
+	new_crtc_state = drm_atomic_get_new_crtc_state(old_state, crtc);
+	if (!new_crtc_state)
+		return;
+
+	/*
+	 * Self refresh mode is already set in edp_bridge_atomic_disable.
+	 */
+	if (new_crtc_state->self_refresh_active)
+		return;
+
+	dp_bridge_post_disable(drm_bridge);
+}
+
+static const struct drm_bridge_funcs edp_bridge_ops = {
+	.atomic_enable       = edp_bridge_atomic_enable,
+	.atomic_disable      = edp_bridge_atomic_disable,
+	.atomic_post_disable = edp_bridge_atomic_post_disable,
+	.mode_set     = dp_bridge_mode_set,
+	.atomic_reset = drm_atomic_helper_bridge_reset,
+	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+	.atomic_check = edp_bridge_atomic_check,
+};
+
 static const struct drm_bridge_funcs dp_bridge_ops = {
 	.enable       = dp_bridge_enable,
 	.disable      = dp_bridge_disable,
@@ -84,7 +247,7 @@ struct drm_bridge *dp_bridge_init(struct msm_dp *dp_display, struct drm_device *
 	dp_bridge->dp_display = dp_display;
 
 	bridge = &dp_bridge->bridge;
-	bridge->funcs = &dp_bridge_ops;
+	bridge->funcs = &edp_bridge_ops;
 	bridge->type = dp_display->connector_type;
 
 	/*
@@ -99,6 +262,7 @@ struct drm_bridge *dp_bridge_init(struct msm_dp *dp_display, struct drm_device *
 	 *   modes.
 	 */
 	if (!dp_display->is_edp) {
+		bridge->funcs = &dp_bridge_ops;
 		bridge->ops =
 			DRM_BRIDGE_OP_DETECT |
 			DRM_BRIDGE_OP_HPD |
diff --git a/drivers/gpu/drm/msm/dp/dp_link.c b/drivers/gpu/drm/msm/dp/dp_link.c
index 36f0af0..81ac3c1 100644
--- a/drivers/gpu/drm/msm/dp/dp_link.c
+++ b/drivers/gpu/drm/msm/dp/dp_link.c
@@ -934,6 +934,38 @@ static int dp_link_process_phy_test_pattern_request(
 	return 0;
 }
 
+static bool dp_link_read_psr_error_status(struct dp_link_private *link)
+{
+	u8 status;
+
+	drm_dp_dpcd_read(link->aux, DP_PSR_ERROR_STATUS, &status, 1);
+
+	if (status & DP_PSR_LINK_CRC_ERROR)
+		DRM_ERROR("PSR LINK CRC ERROR\n");
+	else if (status & DP_PSR_RFB_STORAGE_ERROR)
+		DRM_ERROR("PSR RFB STORAGE ERROR\n");
+	else if (status & DP_PSR_VSC_SDP_UNCORRECTABLE_ERROR)
+		DRM_ERROR("PSR VSC SDP UNCORRECTABLE ERROR\n");
+	else
+		return 0;
+
+	return 1;
+}
+
+static bool dp_link_psr_capability_changed(struct dp_link_private *link)
+{
+	u8 status;
+
+	drm_dp_dpcd_read(link->aux, DP_PSR_ESI, &status, 1);
+
+	if (status & DP_PSR_CAPS_CHANGE)
+		drm_dbg_dp(link->drm_dev, "PSR Capability Change\n");
+	else
+		return 0;
+
+	return 1;
+}
+
 static u8 get_link_status(const u8 link_status[DP_LINK_STATUS_SIZE], int r)
 {
 	return link_status[r - DP_LANE0_1_STATUS];
@@ -1053,6 +1085,10 @@ int dp_link_process_request(struct dp_link *dp_link)
 		dp_link->sink_request |= DP_TEST_LINK_TRAINING;
 	} else if (!dp_link_process_phy_test_pattern_request(link)) {
 		dp_link->sink_request |= DP_TEST_LINK_PHY_TEST_PATTERN;
+	} else if (dp_link_read_psr_error_status(link)) {
+		DRM_ERROR("PSR IRQ_HPD received\n");
+	} else if (dp_link_psr_capability_changed(link)) {
+		drm_dbg_dp(link->drm_dev, "PSR Capabiity changed");
 	} else {
 		ret = dp_link_process_link_status_update(link);
 		if (!ret) {
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c
index 5149ceb..8bf8ab4 100644
--- a/drivers/gpu/drm/msm/dp/dp_panel.c
+++ b/drivers/gpu/drm/msm/dp/dp_panel.c
@@ -20,6 +20,27 @@ struct dp_panel_private {
 	bool aux_cfg_update_done;
 };
 
+static void dp_panel_read_psr_cap(struct dp_panel_private *panel)
+{
+	ssize_t rlen;
+	struct dp_panel *dp_panel;
+
+	dp_panel = &panel->dp_panel;
+
+	/* edp sink */
+	if (dp_panel->dpcd[DP_EDP_CONFIGURATION_CAP]) {
+		rlen = drm_dp_dpcd_read(panel->aux, DP_PSR_SUPPORT,
+				&dp_panel->psr_cap, sizeof(dp_panel->psr_cap));
+		if (rlen == sizeof(dp_panel->psr_cap)) {
+			drm_dbg_dp(panel->drm_dev,
+				"psr version: 0x%x, psr_cap: 0x%x\n",
+				dp_panel->psr_cap.version,
+				dp_panel->psr_cap.capabilities);
+		} else
+			DRM_ERROR("failed to read psr info, rlen=%zd\n", rlen);
+	}
+}
+
 static int dp_panel_read_dpcd(struct dp_panel *dp_panel)
 {
 	int rc = 0;
@@ -106,6 +127,7 @@ static int dp_panel_read_dpcd(struct dp_panel *dp_panel)
 		}
 	}
 
+	dp_panel_read_psr_cap(panel);
 end:
 	return rc;
 }
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.h b/drivers/gpu/drm/msm/dp/dp_panel.h
index d861197a..2d0826a 100644
--- a/drivers/gpu/drm/msm/dp/dp_panel.h
+++ b/drivers/gpu/drm/msm/dp/dp_panel.h
@@ -34,6 +34,11 @@ struct dp_panel_in {
 	struct dp_catalog *catalog;
 };
 
+struct dp_panel_psr {
+	u8 version;
+	u8 capabilities;
+};
+
 struct dp_panel {
 	/* dpcd raw data */
 	u8 dpcd[DP_RECEIVER_CAP_SIZE + 1];
@@ -46,6 +51,7 @@ struct dp_panel {
 	struct edid *edid;
 	struct drm_connector *connector;
 	struct dp_display_mode dp_mode;
+	struct dp_panel_psr psr_cap;
 	bool video_test;
 
 	u32 vic;
diff --git a/drivers/gpu/drm/msm/dp/dp_reg.h b/drivers/gpu/drm/msm/dp/dp_reg.h
index 2686028..ea85a69 100644
--- a/drivers/gpu/drm/msm/dp/dp_reg.h
+++ b/drivers/gpu/drm/msm/dp/dp_reg.h
@@ -22,6 +22,20 @@
 #define REG_DP_INTR_STATUS2			(0x00000024)
 #define REG_DP_INTR_STATUS3			(0x00000028)
 
+#define REG_DP_INTR_STATUS4			(0x0000002C)
+#define PSR_UPDATE_INT				(0x00000001)
+#define PSR_CAPTURE_INT				(0x00000004)
+#define PSR_EXIT_INT				(0x00000010)
+#define PSR_UPDATE_ERROR_INT			(0x00000040)
+#define PSR_WAKE_ERROR_INT			(0x00000100)
+
+#define REG_DP_INTR_MASK4			(0x00000030)
+#define PSR_UPDATE_MASK				(0x00000001)
+#define PSR_CAPTURE_MASK			(0x00000002)
+#define PSR_EXIT_MASK				(0x00000004)
+#define PSR_UPDATE_ERROR_MASK			(0x00000008)
+#define PSR_WAKE_ERROR_MASK			(0x00000010)
+
 #define REG_DP_DP_HPD_CTRL			(0x00000000)
 #define DP_DP_HPD_CTRL_HPD_EN			(0x00000001)
 
@@ -164,6 +178,16 @@
 #define MMSS_DP_AUDIO_TIMING_RBR_48		(0x00000094)
 #define MMSS_DP_AUDIO_TIMING_HBR_48		(0x00000098)
 
+#define REG_PSR_CONFIG				(0x00000100)
+#define DISABLE_PSR				(0x00000000)
+#define PSR1_SUPPORTED				(0x00000001)
+#define PSR2_WITHOUT_FRAMESYNC			(0x00000002)
+#define PSR2_WITH_FRAMESYNC			(0x00000003)
+
+#define REG_PSR_CMD				(0x00000110)
+#define PSR_ENTER				(0x00000001)
+#define PSR_EXIT				(0x00000002)
+
 #define MMSS_DP_PSR_CRC_RG			(0x00000154)
 #define MMSS_DP_PSR_CRC_B			(0x00000158)
 
@@ -184,6 +208,9 @@
 #define MMSS_DP_AUDIO_STREAM_0			(0x00000240)
 #define MMSS_DP_AUDIO_STREAM_1			(0x00000244)
 
+#define MMSS_DP_SDP_CFG3			(0x0000024c)
+#define UPDATE_SDP				(0x00000001)
+
 #define MMSS_DP_EXTENSION_0			(0x00000250)
 #define MMSS_DP_EXTENSION_1			(0x00000254)
 #define MMSS_DP_EXTENSION_2			(0x00000258)
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [v3 1/5] drm/msm/dp: Add basic PSR support for eDP
@ 2022-06-21 10:53   ` Vinod Polimera
  0 siblings, 0 replies; 32+ messages in thread
From: Vinod Polimera @ 2022-06-21 10:53 UTC (permalink / raw)
  To: y, dri-devel, linux-arm-msm, freedreno, devicetree
  Cc: quic_kalyant, quic_sbillaka, dianders, linux-kernel,
	dmitry.baryshkov, swboyd, Vinod Polimera

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: Sankeerth Billakanti <quic_sbillaka@quicinc.com>
Signed-off-by: Vinod Polimera <quic_vpolimer@quicinc.com>
---
 drivers/gpu/drm/msm/dp/dp_catalog.c |  81 ++++++++++++++++++
 drivers/gpu/drm/msm/dp/dp_catalog.h |   4 +
 drivers/gpu/drm/msm/dp/dp_ctrl.c    |  76 ++++++++++++++++-
 drivers/gpu/drm/msm/dp/dp_ctrl.h    |   3 +
 drivers/gpu/drm/msm/dp/dp_display.c |  14 +++
 drivers/gpu/drm/msm/dp/dp_display.h |   2 +
 drivers/gpu/drm/msm/dp/dp_drm.c     | 166 +++++++++++++++++++++++++++++++++++-
 drivers/gpu/drm/msm/dp/dp_link.c    |  36 ++++++++
 drivers/gpu/drm/msm/dp/dp_panel.c   |  22 +++++
 drivers/gpu/drm/msm/dp/dp_panel.h   |   6 ++
 drivers/gpu/drm/msm/dp/dp_reg.h     |  27 ++++++
 11 files changed, 433 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.c b/drivers/gpu/drm/msm/dp/dp_catalog.c
index 7257515..b9021ed 100644
--- a/drivers/gpu/drm/msm/dp/dp_catalog.c
+++ b/drivers/gpu/drm/msm/dp/dp_catalog.c
@@ -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,24 @@ 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);
+	val &= ~DP_MAINLINK_CTRL_ENABLE;
+
+	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 +636,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, !UPDATE_SDP);
+}
+
+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 +712,20 @@ u32 dp_catalog_hpd_get_intr_status(struct dp_catalog *dp_catalog)
 	return isr & (mask | ~DP_DP_HPD_INT_MASK);
 }
 
+int 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,
diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.h b/drivers/gpu/drm/msm/dp/dp_catalog.h
index 1f717f4..6454845 100644
--- a/drivers/gpu/drm/msm/dp/dp_catalog.h
+++ b/drivers/gpu/drm/msm/dp/dp_catalog.h
@@ -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);
+int 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);
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index d21971b..485e8f5 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -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);
 }
 
@@ -1382,11 +1387,64 @@ static int dp_ctrl_enable_stream_clocks(struct dp_ctrl_private *ctrl)
 	return ret;
 }
 
-void dp_ctrl_reset_irq_ctrl(struct dp_ctrl *dp_ctrl, bool enable)
+void dp_ctrl_config_psr(struct dp_ctrl *dp_ctrl)
 {
-	struct dp_ctrl_private *ctrl;
+	u8 cfg;
+	struct dp_ctrl_private *ctrl = container_of(dp_ctrl,
+			struct dp_ctrl_private, dp_ctrl);
 
-	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_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);
+	}
+}
+
+void dp_ctrl_reset_irq_ctrl(struct dp_ctrl *dp_ctrl, bool enable)
+{
+	struct dp_ctrl_private *ctrl = container_of(dp_ctrl,
+			struct dp_ctrl_private, dp_ctrl);
 
 	dp_catalog_ctrl_reset(ctrl->catalog);
 
@@ -1997,6 +2055,17 @@ 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 == PSR_UPDATE_INT)
+			drm_dbg_dp(ctrl->drm_dev, "PSR frame update done\n");
+		else if (isr == PSR_EXIT_INT)
+			drm_dbg_dp(ctrl->drm_dev, "PSR exit done\n");
+
+		complete(&ctrl->psr_op_comp);
+	}
+
 	isr = dp_catalog_ctrl_get_interrupt(ctrl->catalog);
 
 	if (isr & DP_CTRL_INTR_READY_FOR_VIDEO) {
@@ -2043,6 +2112,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 */
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.h b/drivers/gpu/drm/msm/dp/dp_ctrl.h
index 0745fde..be074ae 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.h
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h
@@ -38,4 +38,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_ */
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index bce7793..2e779c2 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -388,6 +388,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);
 
@@ -895,6 +897,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;
 }
 
@@ -1094,6 +1100,14 @@ 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;
+
+	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;
diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
index 4f9fe4d..1feaada 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.h
+++ b/drivers/gpu/drm/msm/dp/dp_display.h
@@ -29,6 +29,7 @@ struct msm_dp {
 
 	u32 max_dp_lanes;
 	struct dp_audio *dp_audio;
+	bool psr_supported;
 };
 
 int dp_display_set_plugged_cb(struct msm_dp *dp_display,
@@ -39,5 +40,6 @@ bool dp_display_check_video_test(struct msm_dp *dp_display);
 int dp_display_get_test_bpp(struct msm_dp *dp_display);
 void dp_display_signal_audio_start(struct msm_dp *dp_display);
 void dp_display_signal_audio_complete(struct msm_dp *dp_display);
+void dp_display_set_psr(struct msm_dp *dp, bool enter);
 
 #endif /* _DP_DISPLAY_H_ */
diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c b/drivers/gpu/drm/msm/dp/dp_drm.c
index 62d58b9..cfe31ed 100644
--- a/drivers/gpu/drm/msm/dp/dp_drm.c
+++ b/drivers/gpu/drm/msm/dp/dp_drm.c
@@ -60,6 +60,169 @@ static int dp_bridge_get_modes(struct drm_bridge *bridge, struct drm_connector *
 	return rc;
 }
 
+static struct drm_crtc *dp_bridge_get_old_connector_crtc(struct msm_dp *dp,
+					  struct drm_atomic_state *state)
+{
+	struct drm_encoder *encoder = dp->encoder;
+	struct drm_connector *connector;
+	struct drm_connector_state *conn_state;
+
+	connector = drm_atomic_get_old_connector_for_encoder(state, encoder);
+	if (!connector)
+		return NULL;
+
+	conn_state = drm_atomic_get_old_connector_state(state, connector);
+	if (!conn_state)
+		return NULL;
+
+	return conn_state->crtc;
+}
+
+static struct drm_crtc *dp_bridge_get_new_connector_crtc(struct msm_dp *dp,
+					  struct drm_atomic_state *state)
+{
+	struct drm_encoder *encoder = dp->encoder;
+	struct drm_connector *connector;
+	struct drm_connector_state *conn_state;
+
+	connector = drm_atomic_get_new_connector_for_encoder(state, encoder);
+	if (!connector)
+		return NULL;
+
+	conn_state = drm_atomic_get_new_connector_state(state, connector);
+	if (!conn_state)
+		return NULL;
+
+	return conn_state->crtc;
+}
+
+static int edp_bridge_atomic_check(struct drm_bridge *drm_bridge,
+			    struct drm_bridge_state *bridge_state,
+			    struct drm_crtc_state *crtc_state,
+			    struct drm_connector_state *conn_state)
+{
+	struct msm_dp *dp;
+
+	dp = to_dp_bridge(drm_bridge)->dp_display;
+	if (WARN_ON(!conn_state))
+		return -ENODEV;
+
+	if (dp->psr_supported)
+		conn_state->self_refresh_aware = true;
+
+	if (!conn_state->crtc || !crtc_state)
+		return 0;
+
+	if (crtc_state->self_refresh_active && !dp->psr_supported)
+		return -EINVAL;
+
+	return 0;
+}
+
+static void edp_bridge_atomic_enable(struct drm_bridge *drm_bridge,
+				struct drm_bridge_state *old_bridge_state)
+{
+	struct drm_atomic_state *old_state = old_bridge_state->base.state;
+	struct drm_crtc *crtc;
+	struct drm_crtc_state *old_crtc_state;
+	struct msm_dp_bridge *dp_bridge = to_dp_bridge(drm_bridge);
+	struct msm_dp *dp_display = dp_bridge->dp_display;
+
+	crtc = dp_bridge_get_new_connector_crtc(dp_display, old_state);
+	if (!crtc)
+		return;
+
+	old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc);
+
+	/* Exit from self refresh mode */
+	if (old_crtc_state && old_crtc_state->self_refresh_active) {
+		dp_display_set_psr(dp_display, false);
+		return;
+	}
+
+	dp_bridge_enable(drm_bridge);
+}
+
+static void edp_bridge_atomic_disable(struct drm_bridge *drm_bridge,
+				struct drm_bridge_state *old_bridge_state)
+{
+	struct drm_atomic_state *old_state = old_bridge_state->base.state;
+	struct drm_crtc *crtc;
+	struct drm_crtc_state *new_crtc_state = NULL, *old_crtc_state = NULL;
+	struct msm_dp_bridge *dp_bridge = to_dp_bridge(drm_bridge);
+	struct msm_dp *dp_display = dp_bridge->dp_display;
+
+	crtc = dp_bridge_get_old_connector_crtc(dp_display, old_state);
+	if (!crtc)
+		goto out;
+
+	new_crtc_state = drm_atomic_get_new_crtc_state(old_state, crtc);
+	if (!new_crtc_state)
+		goto out;
+
+	old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc);
+	if (!old_crtc_state)
+		goto out;
+
+	/*
+	 * Set self refresh mode if current crtc state is active.
+	 * If old crtc state is active, then this is a display disable
+	 * call while the sink is in psr state. So, exit psr here.
+	 * The eDP controller will be disabled in the
+	 * edp_bridge_atomic_post_disable function.
+	 *
+	 * We observed sink is stuck in self refresh if psr exit is skipped
+	 * when display disable occurs while the sink is in psr state.
+	 */
+	if (new_crtc_state->self_refresh_active) {
+		dp_display_set_psr(dp_display, true);
+		return;
+	} else if (old_crtc_state->self_refresh_active) {
+		dp_display_set_psr(dp_display, false);
+		return;
+	}
+
+out:
+	dp_bridge_disable(drm_bridge);
+}
+
+static void edp_bridge_atomic_post_disable(struct drm_bridge *drm_bridge,
+				struct drm_bridge_state *old_bridge_state)
+{
+	struct drm_atomic_state *old_state = old_bridge_state->base.state;
+	struct drm_crtc *crtc;
+	struct drm_crtc_state *new_crtc_state = NULL;
+	struct msm_dp_bridge *dp_bridge = to_dp_bridge(drm_bridge);
+	struct msm_dp *dp_display = dp_bridge->dp_display;
+
+	crtc = dp_bridge_get_old_connector_crtc(dp_display, old_state);
+	if (!crtc)
+		return;
+
+	new_crtc_state = drm_atomic_get_new_crtc_state(old_state, crtc);
+	if (!new_crtc_state)
+		return;
+
+	/*
+	 * Self refresh mode is already set in edp_bridge_atomic_disable.
+	 */
+	if (new_crtc_state->self_refresh_active)
+		return;
+
+	dp_bridge_post_disable(drm_bridge);
+}
+
+static const struct drm_bridge_funcs edp_bridge_ops = {
+	.atomic_enable       = edp_bridge_atomic_enable,
+	.atomic_disable      = edp_bridge_atomic_disable,
+	.atomic_post_disable = edp_bridge_atomic_post_disable,
+	.mode_set     = dp_bridge_mode_set,
+	.atomic_reset = drm_atomic_helper_bridge_reset,
+	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+	.atomic_check = edp_bridge_atomic_check,
+};
+
 static const struct drm_bridge_funcs dp_bridge_ops = {
 	.enable       = dp_bridge_enable,
 	.disable      = dp_bridge_disable,
@@ -84,7 +247,7 @@ struct drm_bridge *dp_bridge_init(struct msm_dp *dp_display, struct drm_device *
 	dp_bridge->dp_display = dp_display;
 
 	bridge = &dp_bridge->bridge;
-	bridge->funcs = &dp_bridge_ops;
+	bridge->funcs = &edp_bridge_ops;
 	bridge->type = dp_display->connector_type;
 
 	/*
@@ -99,6 +262,7 @@ struct drm_bridge *dp_bridge_init(struct msm_dp *dp_display, struct drm_device *
 	 *   modes.
 	 */
 	if (!dp_display->is_edp) {
+		bridge->funcs = &dp_bridge_ops;
 		bridge->ops =
 			DRM_BRIDGE_OP_DETECT |
 			DRM_BRIDGE_OP_HPD |
diff --git a/drivers/gpu/drm/msm/dp/dp_link.c b/drivers/gpu/drm/msm/dp/dp_link.c
index 36f0af0..81ac3c1 100644
--- a/drivers/gpu/drm/msm/dp/dp_link.c
+++ b/drivers/gpu/drm/msm/dp/dp_link.c
@@ -934,6 +934,38 @@ static int dp_link_process_phy_test_pattern_request(
 	return 0;
 }
 
+static bool dp_link_read_psr_error_status(struct dp_link_private *link)
+{
+	u8 status;
+
+	drm_dp_dpcd_read(link->aux, DP_PSR_ERROR_STATUS, &status, 1);
+
+	if (status & DP_PSR_LINK_CRC_ERROR)
+		DRM_ERROR("PSR LINK CRC ERROR\n");
+	else if (status & DP_PSR_RFB_STORAGE_ERROR)
+		DRM_ERROR("PSR RFB STORAGE ERROR\n");
+	else if (status & DP_PSR_VSC_SDP_UNCORRECTABLE_ERROR)
+		DRM_ERROR("PSR VSC SDP UNCORRECTABLE ERROR\n");
+	else
+		return 0;
+
+	return 1;
+}
+
+static bool dp_link_psr_capability_changed(struct dp_link_private *link)
+{
+	u8 status;
+
+	drm_dp_dpcd_read(link->aux, DP_PSR_ESI, &status, 1);
+
+	if (status & DP_PSR_CAPS_CHANGE)
+		drm_dbg_dp(link->drm_dev, "PSR Capability Change\n");
+	else
+		return 0;
+
+	return 1;
+}
+
 static u8 get_link_status(const u8 link_status[DP_LINK_STATUS_SIZE], int r)
 {
 	return link_status[r - DP_LANE0_1_STATUS];
@@ -1053,6 +1085,10 @@ int dp_link_process_request(struct dp_link *dp_link)
 		dp_link->sink_request |= DP_TEST_LINK_TRAINING;
 	} else if (!dp_link_process_phy_test_pattern_request(link)) {
 		dp_link->sink_request |= DP_TEST_LINK_PHY_TEST_PATTERN;
+	} else if (dp_link_read_psr_error_status(link)) {
+		DRM_ERROR("PSR IRQ_HPD received\n");
+	} else if (dp_link_psr_capability_changed(link)) {
+		drm_dbg_dp(link->drm_dev, "PSR Capabiity changed");
 	} else {
 		ret = dp_link_process_link_status_update(link);
 		if (!ret) {
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c
index 5149ceb..8bf8ab4 100644
--- a/drivers/gpu/drm/msm/dp/dp_panel.c
+++ b/drivers/gpu/drm/msm/dp/dp_panel.c
@@ -20,6 +20,27 @@ struct dp_panel_private {
 	bool aux_cfg_update_done;
 };
 
+static void dp_panel_read_psr_cap(struct dp_panel_private *panel)
+{
+	ssize_t rlen;
+	struct dp_panel *dp_panel;
+
+	dp_panel = &panel->dp_panel;
+
+	/* edp sink */
+	if (dp_panel->dpcd[DP_EDP_CONFIGURATION_CAP]) {
+		rlen = drm_dp_dpcd_read(panel->aux, DP_PSR_SUPPORT,
+				&dp_panel->psr_cap, sizeof(dp_panel->psr_cap));
+		if (rlen == sizeof(dp_panel->psr_cap)) {
+			drm_dbg_dp(panel->drm_dev,
+				"psr version: 0x%x, psr_cap: 0x%x\n",
+				dp_panel->psr_cap.version,
+				dp_panel->psr_cap.capabilities);
+		} else
+			DRM_ERROR("failed to read psr info, rlen=%zd\n", rlen);
+	}
+}
+
 static int dp_panel_read_dpcd(struct dp_panel *dp_panel)
 {
 	int rc = 0;
@@ -106,6 +127,7 @@ static int dp_panel_read_dpcd(struct dp_panel *dp_panel)
 		}
 	}
 
+	dp_panel_read_psr_cap(panel);
 end:
 	return rc;
 }
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.h b/drivers/gpu/drm/msm/dp/dp_panel.h
index d861197a..2d0826a 100644
--- a/drivers/gpu/drm/msm/dp/dp_panel.h
+++ b/drivers/gpu/drm/msm/dp/dp_panel.h
@@ -34,6 +34,11 @@ struct dp_panel_in {
 	struct dp_catalog *catalog;
 };
 
+struct dp_panel_psr {
+	u8 version;
+	u8 capabilities;
+};
+
 struct dp_panel {
 	/* dpcd raw data */
 	u8 dpcd[DP_RECEIVER_CAP_SIZE + 1];
@@ -46,6 +51,7 @@ struct dp_panel {
 	struct edid *edid;
 	struct drm_connector *connector;
 	struct dp_display_mode dp_mode;
+	struct dp_panel_psr psr_cap;
 	bool video_test;
 
 	u32 vic;
diff --git a/drivers/gpu/drm/msm/dp/dp_reg.h b/drivers/gpu/drm/msm/dp/dp_reg.h
index 2686028..ea85a69 100644
--- a/drivers/gpu/drm/msm/dp/dp_reg.h
+++ b/drivers/gpu/drm/msm/dp/dp_reg.h
@@ -22,6 +22,20 @@
 #define REG_DP_INTR_STATUS2			(0x00000024)
 #define REG_DP_INTR_STATUS3			(0x00000028)
 
+#define REG_DP_INTR_STATUS4			(0x0000002C)
+#define PSR_UPDATE_INT				(0x00000001)
+#define PSR_CAPTURE_INT				(0x00000004)
+#define PSR_EXIT_INT				(0x00000010)
+#define PSR_UPDATE_ERROR_INT			(0x00000040)
+#define PSR_WAKE_ERROR_INT			(0x00000100)
+
+#define REG_DP_INTR_MASK4			(0x00000030)
+#define PSR_UPDATE_MASK				(0x00000001)
+#define PSR_CAPTURE_MASK			(0x00000002)
+#define PSR_EXIT_MASK				(0x00000004)
+#define PSR_UPDATE_ERROR_MASK			(0x00000008)
+#define PSR_WAKE_ERROR_MASK			(0x00000010)
+
 #define REG_DP_DP_HPD_CTRL			(0x00000000)
 #define DP_DP_HPD_CTRL_HPD_EN			(0x00000001)
 
@@ -164,6 +178,16 @@
 #define MMSS_DP_AUDIO_TIMING_RBR_48		(0x00000094)
 #define MMSS_DP_AUDIO_TIMING_HBR_48		(0x00000098)
 
+#define REG_PSR_CONFIG				(0x00000100)
+#define DISABLE_PSR				(0x00000000)
+#define PSR1_SUPPORTED				(0x00000001)
+#define PSR2_WITHOUT_FRAMESYNC			(0x00000002)
+#define PSR2_WITH_FRAMESYNC			(0x00000003)
+
+#define REG_PSR_CMD				(0x00000110)
+#define PSR_ENTER				(0x00000001)
+#define PSR_EXIT				(0x00000002)
+
 #define MMSS_DP_PSR_CRC_RG			(0x00000154)
 #define MMSS_DP_PSR_CRC_B			(0x00000158)
 
@@ -184,6 +208,9 @@
 #define MMSS_DP_AUDIO_STREAM_0			(0x00000240)
 #define MMSS_DP_AUDIO_STREAM_1			(0x00000244)
 
+#define MMSS_DP_SDP_CFG3			(0x0000024c)
+#define UPDATE_SDP				(0x00000001)
+
 #define MMSS_DP_EXTENSION_0			(0x00000250)
 #define MMSS_DP_EXTENSION_1			(0x00000254)
 #define MMSS_DP_EXTENSION_2			(0x00000258)
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [v3 2/5] drm/bridge: use atomic enable/disable callbacks for panel bridge functions
  2022-06-21 10:53 ` Vinod Polimera
@ 2022-06-21 10:53   ` Vinod Polimera
  -1 siblings, 0 replies; 32+ messages in thread
From: Vinod Polimera @ 2022-06-21 10:53 UTC (permalink / raw)
  To: y, dri-devel, linux-arm-msm, freedreno, devicetree
  Cc: Vinod Polimera, linux-kernel, robdclark, dianders, swboyd,
	quic_kalyant, dmitry.baryshkov, quic_sbillaka

Use atomic variants for panel bridge callback functions such that
certain states like self-refresh can be accessed as part of
enable/disable sequence.

Signed-off-by: Vinod Polimera <quic_vpolimer@quicinc.com>
---
 drivers/gpu/drm/bridge/panel.c | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c
index 0ee563e..59a3496 100644
--- a/drivers/gpu/drm/bridge/panel.c
+++ b/drivers/gpu/drm/bridge/panel.c
@@ -108,28 +108,28 @@ static void panel_bridge_detach(struct drm_bridge *bridge)
 		drm_connector_cleanup(connector);
 }
 
-static void panel_bridge_pre_enable(struct drm_bridge *bridge)
+static void panel_bridge_atomic_pre_enable(struct drm_bridge *bridge)
 {
 	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
 
 	drm_panel_prepare(panel_bridge->panel);
 }
 
-static void panel_bridge_enable(struct drm_bridge *bridge)
+static void panel_bridge_atomic_enable(struct drm_bridge *bridge)
 {
 	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
 
 	drm_panel_enable(panel_bridge->panel);
 }
 
-static void panel_bridge_disable(struct drm_bridge *bridge)
+static void panel_bridge_atomic_disable(struct drm_bridge *bridge)
 {
 	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
 
 	drm_panel_disable(panel_bridge->panel);
 }
 
-static void panel_bridge_post_disable(struct drm_bridge *bridge)
+static void panel_bridge_atomic_post_disable(struct drm_bridge *bridge)
 {
 	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
 
@@ -158,10 +158,10 @@ static void panel_bridge_debugfs_init(struct drm_bridge *bridge,
 static const struct drm_bridge_funcs panel_bridge_bridge_funcs = {
 	.attach = panel_bridge_attach,
 	.detach = panel_bridge_detach,
-	.pre_enable = panel_bridge_pre_enable,
-	.enable = panel_bridge_enable,
-	.disable = panel_bridge_disable,
-	.post_disable = panel_bridge_post_disable,
+	.atomic_pre_enable = panel_bridge_atomic_pre_enable,
+	.atomic_enable = panel_bridge_atomic_enable,
+	.atomic_disable = panel_bridge_atomic_disable,
+	.atomic_post_disable = panel_bridge_atomic_post_disable,
 	.get_modes = panel_bridge_get_modes,
 	.atomic_reset = drm_atomic_helper_bridge_reset,
 	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [v3 2/5] drm/bridge: use atomic enable/disable callbacks for panel bridge functions
@ 2022-06-21 10:53   ` Vinod Polimera
  0 siblings, 0 replies; 32+ messages in thread
From: Vinod Polimera @ 2022-06-21 10:53 UTC (permalink / raw)
  To: y, dri-devel, linux-arm-msm, freedreno, devicetree
  Cc: quic_kalyant, quic_sbillaka, dianders, linux-kernel,
	dmitry.baryshkov, swboyd, Vinod Polimera

Use atomic variants for panel bridge callback functions such that
certain states like self-refresh can be accessed as part of
enable/disable sequence.

Signed-off-by: Vinod Polimera <quic_vpolimer@quicinc.com>
---
 drivers/gpu/drm/bridge/panel.c | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c
index 0ee563e..59a3496 100644
--- a/drivers/gpu/drm/bridge/panel.c
+++ b/drivers/gpu/drm/bridge/panel.c
@@ -108,28 +108,28 @@ static void panel_bridge_detach(struct drm_bridge *bridge)
 		drm_connector_cleanup(connector);
 }
 
-static void panel_bridge_pre_enable(struct drm_bridge *bridge)
+static void panel_bridge_atomic_pre_enable(struct drm_bridge *bridge)
 {
 	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
 
 	drm_panel_prepare(panel_bridge->panel);
 }
 
-static void panel_bridge_enable(struct drm_bridge *bridge)
+static void panel_bridge_atomic_enable(struct drm_bridge *bridge)
 {
 	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
 
 	drm_panel_enable(panel_bridge->panel);
 }
 
-static void panel_bridge_disable(struct drm_bridge *bridge)
+static void panel_bridge_atomic_disable(struct drm_bridge *bridge)
 {
 	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
 
 	drm_panel_disable(panel_bridge->panel);
 }
 
-static void panel_bridge_post_disable(struct drm_bridge *bridge)
+static void panel_bridge_atomic_post_disable(struct drm_bridge *bridge)
 {
 	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
 
@@ -158,10 +158,10 @@ static void panel_bridge_debugfs_init(struct drm_bridge *bridge,
 static const struct drm_bridge_funcs panel_bridge_bridge_funcs = {
 	.attach = panel_bridge_attach,
 	.detach = panel_bridge_detach,
-	.pre_enable = panel_bridge_pre_enable,
-	.enable = panel_bridge_enable,
-	.disable = panel_bridge_disable,
-	.post_disable = panel_bridge_post_disable,
+	.atomic_pre_enable = panel_bridge_atomic_pre_enable,
+	.atomic_enable = panel_bridge_atomic_enable,
+	.atomic_disable = panel_bridge_atomic_disable,
+	.atomic_post_disable = panel_bridge_atomic_post_disable,
 	.get_modes = panel_bridge_get_modes,
 	.atomic_reset = drm_atomic_helper_bridge_reset,
 	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [v3 3/5] drm/bridge: add psr support during panel bridge enable & disable sequence
  2022-06-21 10:53 ` Vinod Polimera
@ 2022-06-21 10:53   ` Vinod Polimera
  -1 siblings, 0 replies; 32+ messages in thread
From: Vinod Polimera @ 2022-06-21 10:53 UTC (permalink / raw)
  To: y, dri-devel, linux-arm-msm, freedreno, devicetree
  Cc: quic_kalyant, quic_sbillaka, dianders, linux-kernel,
	dmitry.baryshkov, swboyd, Vinod Polimera

This change avoids panel prepare/unprepare based on self-refresh
state.

Signed-off-by: Sankeerth Billakanti <quic_sbillaka@quicinc.com>
Signed-off-by: Kalyan Thota <quic_kalyant@quicinc.com>
Signed-off-by: Vinod Polimera <quic_vpolimer@quicinc.com>
---
 drivers/gpu/drm/bridge/panel.c | 102 +++++++++++++++++++++++++++++++++++++++--
 1 file changed, 98 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c
index 59a3496..6b09ae0 100644
--- a/drivers/gpu/drm/bridge/panel.c
+++ b/drivers/gpu/drm/bridge/panel.c
@@ -41,6 +41,40 @@ static int panel_bridge_connector_get_modes(struct drm_connector *connector)
 	return drm_panel_get_modes(panel_bridge->panel, connector);
 }
 
+static struct drm_crtc *bridge_drm_get_old_connector_crtc(struct drm_encoder *encoder,
+							struct drm_atomic_state *state)
+{
+	struct drm_connector *connector;
+	struct drm_connector_state *conn_state;
+
+	connector = drm_atomic_get_old_connector_for_encoder(state, encoder);
+	if (!connector)
+		return NULL;
+
+	conn_state = drm_atomic_get_old_connector_state(state, connector);
+	if (!conn_state)
+		return NULL;
+
+	return conn_state->crtc;
+}
+
+static struct drm_crtc *bridge_drm_get_new_connector_crtc(struct drm_encoder *encoder,
+							struct drm_atomic_state *state)
+{
+	struct drm_connector *connector;
+	struct drm_connector_state *conn_state;
+
+	connector = drm_atomic_get_new_connector_for_encoder(state, encoder);
+	if (!connector)
+		return NULL;
+
+	conn_state = drm_atomic_get_new_connector_state(state, connector);
+	if (!conn_state)
+		return NULL;
+
+	return conn_state->crtc;
+}
+
 static const struct drm_connector_helper_funcs
 panel_bridge_connector_helper_funcs = {
 	.get_modes = panel_bridge_connector_get_modes,
@@ -108,30 +142,90 @@ static void panel_bridge_detach(struct drm_bridge *bridge)
 		drm_connector_cleanup(connector);
 }
 
-static void panel_bridge_atomic_pre_enable(struct drm_bridge *bridge)
+static void panel_bridge_atomic_pre_enable(struct drm_bridge *bridge,
+					struct drm_bridge_state *old_bridge_state)
 {
 	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
+	struct drm_atomic_state *old_state = old_bridge_state->base.state;
+	struct drm_encoder *encoder = bridge->encoder;
+	struct drm_crtc *crtc;
+	struct drm_crtc_state *old_crtc_state;
+
+	crtc = bridge_drm_get_new_connector_crtc(encoder, old_state);
+	if (!crtc)
+		return;
+
+	old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc);
+
+	/* Don't touch the panel if we're coming back from self refresh state */
+	if (old_crtc_state && old_crtc_state->self_refresh_active)
+		return;
 
 	drm_panel_prepare(panel_bridge->panel);
 }
 
-static void panel_bridge_atomic_enable(struct drm_bridge *bridge)
+static void panel_bridge_atomic_enable(struct drm_bridge *bridge,
+					struct drm_bridge_state *old_bridge_state)
 {
 	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
+	struct drm_atomic_state *old_state = old_bridge_state->base.state;
+	struct drm_encoder *encoder = bridge->encoder;
+	struct drm_crtc *crtc;
+	struct drm_crtc_state *old_crtc_state;
+
+	crtc = bridge_drm_get_new_connector_crtc(encoder, old_state);
+	if (!crtc)
+		return;
+
+	old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc);
+
+	/* Don't touch the panel if we're coming back from self refresh state */
+	if (old_crtc_state && old_crtc_state->self_refresh_active)
+		return;
 
 	drm_panel_enable(panel_bridge->panel);
 }
 
-static void panel_bridge_atomic_disable(struct drm_bridge *bridge)
+static void panel_bridge_atomic_disable(struct drm_bridge *bridge,
+					struct drm_bridge_state *old_bridge_state)
 {
 	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
+	struct drm_atomic_state *old_state = old_bridge_state->base.state;
+	struct drm_encoder *encoder = bridge->encoder;
+	struct drm_crtc *crtc;
+	struct drm_crtc_state *new_crtc_state;
+
+	crtc = bridge_drm_get_old_connector_crtc(encoder, old_state);
+	if (!crtc)
+		return;
+
+	new_crtc_state = drm_atomic_get_new_crtc_state(old_state, crtc);
+
+	/* Don't do a full disable on PSR transitions if new state is self refresh state */
+	if (new_crtc_state && new_crtc_state->self_refresh_active)
+		return;
 
 	drm_panel_disable(panel_bridge->panel);
 }
 
-static void panel_bridge_atomic_post_disable(struct drm_bridge *bridge)
+static void panel_bridge_atomic_post_disable(struct drm_bridge *bridge,
+					struct drm_bridge_state *old_bridge_state)
 {
 	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
+	struct drm_atomic_state *old_state = old_bridge_state->base.state;
+	struct drm_encoder *encoder = bridge->encoder;
+	struct drm_crtc *crtc;
+	struct drm_crtc_state *new_crtc_state;
+
+	crtc = bridge_drm_get_old_connector_crtc(encoder, old_state);
+	if (!crtc)
+		return;
+
+	new_crtc_state = drm_atomic_get_new_crtc_state(old_state, crtc);
+
+	/* Don't do unprepare on PSR transitions if new state is self refresh state */
+	if (new_crtc_state && new_crtc_state->self_refresh_active)
+		return;
 
 	drm_panel_unprepare(panel_bridge->panel);
 }
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [v3 3/5] drm/bridge: add psr support during panel bridge enable & disable sequence
@ 2022-06-21 10:53   ` Vinod Polimera
  0 siblings, 0 replies; 32+ messages in thread
From: Vinod Polimera @ 2022-06-21 10:53 UTC (permalink / raw)
  To: y, dri-devel, linux-arm-msm, freedreno, devicetree
  Cc: Vinod Polimera, linux-kernel, robdclark, dianders, swboyd,
	quic_kalyant, dmitry.baryshkov, quic_sbillaka

This change avoids panel prepare/unprepare based on self-refresh
state.

Signed-off-by: Sankeerth Billakanti <quic_sbillaka@quicinc.com>
Signed-off-by: Kalyan Thota <quic_kalyant@quicinc.com>
Signed-off-by: Vinod Polimera <quic_vpolimer@quicinc.com>
---
 drivers/gpu/drm/bridge/panel.c | 102 +++++++++++++++++++++++++++++++++++++++--
 1 file changed, 98 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c
index 59a3496..6b09ae0 100644
--- a/drivers/gpu/drm/bridge/panel.c
+++ b/drivers/gpu/drm/bridge/panel.c
@@ -41,6 +41,40 @@ static int panel_bridge_connector_get_modes(struct drm_connector *connector)
 	return drm_panel_get_modes(panel_bridge->panel, connector);
 }
 
+static struct drm_crtc *bridge_drm_get_old_connector_crtc(struct drm_encoder *encoder,
+							struct drm_atomic_state *state)
+{
+	struct drm_connector *connector;
+	struct drm_connector_state *conn_state;
+
+	connector = drm_atomic_get_old_connector_for_encoder(state, encoder);
+	if (!connector)
+		return NULL;
+
+	conn_state = drm_atomic_get_old_connector_state(state, connector);
+	if (!conn_state)
+		return NULL;
+
+	return conn_state->crtc;
+}
+
+static struct drm_crtc *bridge_drm_get_new_connector_crtc(struct drm_encoder *encoder,
+							struct drm_atomic_state *state)
+{
+	struct drm_connector *connector;
+	struct drm_connector_state *conn_state;
+
+	connector = drm_atomic_get_new_connector_for_encoder(state, encoder);
+	if (!connector)
+		return NULL;
+
+	conn_state = drm_atomic_get_new_connector_state(state, connector);
+	if (!conn_state)
+		return NULL;
+
+	return conn_state->crtc;
+}
+
 static const struct drm_connector_helper_funcs
 panel_bridge_connector_helper_funcs = {
 	.get_modes = panel_bridge_connector_get_modes,
@@ -108,30 +142,90 @@ static void panel_bridge_detach(struct drm_bridge *bridge)
 		drm_connector_cleanup(connector);
 }
 
-static void panel_bridge_atomic_pre_enable(struct drm_bridge *bridge)
+static void panel_bridge_atomic_pre_enable(struct drm_bridge *bridge,
+					struct drm_bridge_state *old_bridge_state)
 {
 	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
+	struct drm_atomic_state *old_state = old_bridge_state->base.state;
+	struct drm_encoder *encoder = bridge->encoder;
+	struct drm_crtc *crtc;
+	struct drm_crtc_state *old_crtc_state;
+
+	crtc = bridge_drm_get_new_connector_crtc(encoder, old_state);
+	if (!crtc)
+		return;
+
+	old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc);
+
+	/* Don't touch the panel if we're coming back from self refresh state */
+	if (old_crtc_state && old_crtc_state->self_refresh_active)
+		return;
 
 	drm_panel_prepare(panel_bridge->panel);
 }
 
-static void panel_bridge_atomic_enable(struct drm_bridge *bridge)
+static void panel_bridge_atomic_enable(struct drm_bridge *bridge,
+					struct drm_bridge_state *old_bridge_state)
 {
 	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
+	struct drm_atomic_state *old_state = old_bridge_state->base.state;
+	struct drm_encoder *encoder = bridge->encoder;
+	struct drm_crtc *crtc;
+	struct drm_crtc_state *old_crtc_state;
+
+	crtc = bridge_drm_get_new_connector_crtc(encoder, old_state);
+	if (!crtc)
+		return;
+
+	old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc);
+
+	/* Don't touch the panel if we're coming back from self refresh state */
+	if (old_crtc_state && old_crtc_state->self_refresh_active)
+		return;
 
 	drm_panel_enable(panel_bridge->panel);
 }
 
-static void panel_bridge_atomic_disable(struct drm_bridge *bridge)
+static void panel_bridge_atomic_disable(struct drm_bridge *bridge,
+					struct drm_bridge_state *old_bridge_state)
 {
 	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
+	struct drm_atomic_state *old_state = old_bridge_state->base.state;
+	struct drm_encoder *encoder = bridge->encoder;
+	struct drm_crtc *crtc;
+	struct drm_crtc_state *new_crtc_state;
+
+	crtc = bridge_drm_get_old_connector_crtc(encoder, old_state);
+	if (!crtc)
+		return;
+
+	new_crtc_state = drm_atomic_get_new_crtc_state(old_state, crtc);
+
+	/* Don't do a full disable on PSR transitions if new state is self refresh state */
+	if (new_crtc_state && new_crtc_state->self_refresh_active)
+		return;
 
 	drm_panel_disable(panel_bridge->panel);
 }
 
-static void panel_bridge_atomic_post_disable(struct drm_bridge *bridge)
+static void panel_bridge_atomic_post_disable(struct drm_bridge *bridge,
+					struct drm_bridge_state *old_bridge_state)
 {
 	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
+	struct drm_atomic_state *old_state = old_bridge_state->base.state;
+	struct drm_encoder *encoder = bridge->encoder;
+	struct drm_crtc *crtc;
+	struct drm_crtc_state *new_crtc_state;
+
+	crtc = bridge_drm_get_old_connector_crtc(encoder, old_state);
+	if (!crtc)
+		return;
+
+	new_crtc_state = drm_atomic_get_new_crtc_state(old_state, crtc);
+
+	/* Don't do unprepare on PSR transitions if new state is self refresh state */
+	if (new_crtc_state && new_crtc_state->self_refresh_active)
+		return;
 
 	drm_panel_unprepare(panel_bridge->panel);
 }
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [v3 4/5] drm/msm/disp/dpu1: use atomic enable/disable callbacks for encoder functions
  2022-06-21 10:53 ` Vinod Polimera
@ 2022-06-21 10:53   ` Vinod Polimera
  -1 siblings, 0 replies; 32+ messages in thread
From: Vinod Polimera @ 2022-06-21 10:53 UTC (permalink / raw)
  To: y, dri-devel, linux-arm-msm, freedreno, devicetree
  Cc: quic_kalyant, quic_sbillaka, dianders, linux-kernel,
	dmitry.baryshkov, swboyd, Vinod Polimera

Use atomic variants for encoder callback functions such that
certain states like self-refresh can be accessed as part of
enable/disable sequence.

Signed-off-by: Kalyan Thota <quic_kalyant@quicinc.com>
Signed-off-by: Vinod Polimera <quic_vpolimer@quicinc.com>
---
 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
index 52516eb..cc2809b 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
@@ -1130,7 +1130,8 @@ void dpu_encoder_virt_runtime_resume(struct drm_encoder *drm_enc)
 	mutex_unlock(&dpu_enc->enc_lock);
 }
 
-static void dpu_encoder_virt_enable(struct drm_encoder *drm_enc)
+static void dpu_encoder_virt_atomic_enable(struct drm_encoder *drm_enc,
+					struct drm_atomic_state *state)
 {
 	struct dpu_encoder_virt *dpu_enc = NULL;
 	int ret = 0;
@@ -1166,7 +1167,8 @@ static void dpu_encoder_virt_enable(struct drm_encoder *drm_enc)
 	mutex_unlock(&dpu_enc->enc_lock);
 }
 
-static void dpu_encoder_virt_disable(struct drm_encoder *drm_enc)
+static void dpu_encoder_virt_atomic_disable(struct drm_encoder *drm_enc,
+					struct drm_atomic_state *state)
 {
 	struct dpu_encoder_virt *dpu_enc = NULL;
 	int i = 0;
@@ -2332,8 +2334,8 @@ static void dpu_encoder_frame_done_timeout(struct timer_list *t)
 
 static const struct drm_encoder_helper_funcs dpu_encoder_helper_funcs = {
 	.atomic_mode_set = dpu_encoder_virt_atomic_mode_set,
-	.disable = dpu_encoder_virt_disable,
-	.enable = dpu_encoder_virt_enable,
+	.atomic_disable = dpu_encoder_virt_atomic_disable,
+	.atomic_enable = dpu_encoder_virt_atomic_enable,
 	.atomic_check = dpu_encoder_virt_atomic_check,
 };
 
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [v3 4/5] drm/msm/disp/dpu1: use atomic enable/disable callbacks for encoder functions
@ 2022-06-21 10:53   ` Vinod Polimera
  0 siblings, 0 replies; 32+ messages in thread
From: Vinod Polimera @ 2022-06-21 10:53 UTC (permalink / raw)
  To: y, dri-devel, linux-arm-msm, freedreno, devicetree
  Cc: Vinod Polimera, linux-kernel, robdclark, dianders, swboyd,
	quic_kalyant, dmitry.baryshkov, quic_sbillaka

Use atomic variants for encoder callback functions such that
certain states like self-refresh can be accessed as part of
enable/disable sequence.

Signed-off-by: Kalyan Thota <quic_kalyant@quicinc.com>
Signed-off-by: Vinod Polimera <quic_vpolimer@quicinc.com>
---
 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
index 52516eb..cc2809b 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
@@ -1130,7 +1130,8 @@ void dpu_encoder_virt_runtime_resume(struct drm_encoder *drm_enc)
 	mutex_unlock(&dpu_enc->enc_lock);
 }
 
-static void dpu_encoder_virt_enable(struct drm_encoder *drm_enc)
+static void dpu_encoder_virt_atomic_enable(struct drm_encoder *drm_enc,
+					struct drm_atomic_state *state)
 {
 	struct dpu_encoder_virt *dpu_enc = NULL;
 	int ret = 0;
@@ -1166,7 +1167,8 @@ static void dpu_encoder_virt_enable(struct drm_encoder *drm_enc)
 	mutex_unlock(&dpu_enc->enc_lock);
 }
 
-static void dpu_encoder_virt_disable(struct drm_encoder *drm_enc)
+static void dpu_encoder_virt_atomic_disable(struct drm_encoder *drm_enc,
+					struct drm_atomic_state *state)
 {
 	struct dpu_encoder_virt *dpu_enc = NULL;
 	int i = 0;
@@ -2332,8 +2334,8 @@ static void dpu_encoder_frame_done_timeout(struct timer_list *t)
 
 static const struct drm_encoder_helper_funcs dpu_encoder_helper_funcs = {
 	.atomic_mode_set = dpu_encoder_virt_atomic_mode_set,
-	.disable = dpu_encoder_virt_disable,
-	.enable = dpu_encoder_virt_enable,
+	.atomic_disable = dpu_encoder_virt_atomic_disable,
+	.atomic_enable = dpu_encoder_virt_atomic_enable,
 	.atomic_check = dpu_encoder_virt_atomic_check,
 };
 
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [v3 5/5] drm/msm/disp/dpu1: add PSR support for eDP interface in dpu driver
  2022-06-21 10:53 ` Vinod Polimera
@ 2022-06-21 10:53   ` Vinod Polimera
  -1 siblings, 0 replies; 32+ messages in thread
From: Vinod Polimera @ 2022-06-21 10:53 UTC (permalink / raw)
  To: y, dri-devel, linux-arm-msm, freedreno, devicetree
  Cc: Vinod Polimera, linux-kernel, robdclark, dianders, swboyd,
	quic_kalyant, dmitry.baryshkov, quic_sbillaka

Enable PSR on eDP interface using drm self-refresh librabry.
This patch uses a trigger from self-refresh library to enter/exit
into PSR, when there are no updates from framework.

Signed-off-by: Kalyan Thota <quic_kalyant@quicinc.com>
Signed-off-by: Vinod Polimera <quic_vpolimer@quicinc.com>
---
 drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c    | 36 ++++++++++++++++++++++++-----
 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 20 +++++++++++++++-
 drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c     |  2 +-
 3 files changed, 50 insertions(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
index b56f777..c6e4f03 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
@@ -18,6 +18,7 @@
 #include <drm/drm_probe_helper.h>
 #include <drm/drm_rect.h>
 #include <drm/drm_vblank.h>
+#include <drm/drm_self_refresh_helper.h>
 
 #include "dpu_kms.h"
 #include "dpu_hw_lm.h"
@@ -955,24 +956,39 @@ static void dpu_crtc_disable(struct drm_crtc *crtc,
 									      crtc);
 	struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc);
 	struct dpu_crtc_state *cstate = to_dpu_crtc_state(crtc->state);
-	struct drm_encoder *encoder;
+	struct drm_encoder *encoder = NULL;
 	unsigned long flags;
 	bool release_bandwidth = false;
 
 	DRM_DEBUG_KMS("crtc%d\n", crtc->base.id);
 
+	if (old_crtc_state->self_refresh_active) {
+		drm_for_each_encoder_mask(encoder, crtc->dev,
+					old_crtc_state->encoder_mask) {
+			dpu_encoder_assign_crtc(encoder, NULL);
+		}
+		return;
+	}
+
 	/* Disable/save vblank irq handling */
 	drm_crtc_vblank_off(crtc);
 
 	drm_for_each_encoder_mask(encoder, crtc->dev,
 				  old_crtc_state->encoder_mask) {
-		/* in video mode, we hold an extra bandwidth reference
+		/*
+		 * in video mode, we hold an extra bandwidth reference
 		 * as we cannot drop bandwidth at frame-done if any
 		 * crtc is being used in video mode.
 		 */
 		if (dpu_encoder_get_intf_mode(encoder) == INTF_MODE_VIDEO)
 			release_bandwidth = true;
-		dpu_encoder_assign_crtc(encoder, NULL);
+		/*
+		 * If disable is triggered during psr active(e.g: screen dim in PSR),
+		 * we will need encoder->crtc connection to process the device sleep &
+		 * preserve it during psr sequence.
+		 */
+		if (!crtc->state->self_refresh_active)
+			dpu_encoder_assign_crtc(encoder, NULL);
 	}
 
 	/* wait for frame_event_done completion */
@@ -1020,7 +1036,9 @@ static void dpu_crtc_enable(struct drm_crtc *crtc,
 	struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc);
 	struct drm_encoder *encoder;
 	bool request_bandwidth = false;
+	struct drm_crtc_state *old_crtc_state;
 
+	old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc);
 	pm_runtime_get_sync(crtc->dev->dev);
 
 	DRM_DEBUG_KMS("crtc%d\n", crtc->base.id);
@@ -1042,8 +1060,9 @@ static void dpu_crtc_enable(struct drm_crtc *crtc,
 	trace_dpu_crtc_enable(DRMID(crtc), true, dpu_crtc);
 	dpu_crtc->enabled = true;
 
-	drm_for_each_encoder_mask(encoder, crtc->dev, crtc->state->encoder_mask)
-		dpu_encoder_assign_crtc(encoder, crtc);
+	if (!old_crtc_state->self_refresh_active)
+		drm_for_each_encoder_mask(encoder, crtc->dev, crtc->state->encoder_mask)
+			dpu_encoder_assign_crtc(encoder, crtc);
 
 	/* Enable/restore vblank irq handling */
 	drm_crtc_vblank_on(crtc);
@@ -1525,7 +1544,7 @@ struct drm_crtc *dpu_crtc_init(struct drm_device *dev, struct drm_plane *plane,
 {
 	struct drm_crtc *crtc = NULL;
 	struct dpu_crtc *dpu_crtc = NULL;
-	int i;
+	int i, ret;
 
 	dpu_crtc = kzalloc(sizeof(*dpu_crtc), GFP_KERNEL);
 	if (!dpu_crtc)
@@ -1562,6 +1581,11 @@ struct drm_crtc *dpu_crtc_init(struct drm_device *dev, struct drm_plane *plane,
 	/* initialize event handling */
 	spin_lock_init(&dpu_crtc->event_lock);
 
+	ret = drm_self_refresh_helper_init(crtc);
+	if (ret)
+		DPU_ERROR("Failed to initialize %s with self-refresh helpers %d\n",
+			crtc->name, ret);
+
 	DRM_DEBUG_KMS("%s: successfully initialized crtc\n", dpu_crtc->name);
 	return crtc;
 }
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
index cc2809b..234e95d 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
@@ -225,6 +225,11 @@ bool dpu_encoder_is_widebus_enabled(const struct drm_encoder *drm_enc)
 	return dpu_enc->wide_bus_en;
 }
 
+static inline bool is_self_refresh_active(const struct drm_crtc_state *state)
+{
+	return (state && state->self_refresh_active);
+}
+
 static void _dpu_encoder_setup_dither(struct dpu_hw_pingpong *hw_pp, unsigned bpc)
 {
 	struct dpu_hw_dither_cfg dither_cfg = { 0 };
@@ -592,7 +597,8 @@ static int dpu_encoder_virt_atomic_check(
 		if (drm_atomic_crtc_needs_modeset(crtc_state)) {
 			dpu_rm_release(global_state, drm_enc);
 
-			if (!crtc_state->active_changed || crtc_state->active)
+			if (!crtc_state->active_changed || crtc_state->active ||
+					crtc_state->self_refresh_active)
 				ret = dpu_rm_reserve(&dpu_kms->rm, global_state,
 						drm_enc, crtc_state, topology);
 		}
@@ -1171,11 +1177,23 @@ static void dpu_encoder_virt_atomic_disable(struct drm_encoder *drm_enc,
 					struct drm_atomic_state *state)
 {
 	struct dpu_encoder_virt *dpu_enc = NULL;
+	struct drm_crtc *crtc;
+	struct drm_crtc_state *old_state;
 	int i = 0;
 
 	dpu_enc = to_dpu_encoder_virt(drm_enc);
 	DPU_DEBUG_ENC(dpu_enc, "\n");
 
+	crtc = dpu_enc->crtc;
+	old_state = drm_atomic_get_old_crtc_state(state, crtc);
+
+	/*
+	 * The encoder disabled already occurred when self refresh mode
+	 * was set earlier, in the old_state for the corresponding crtc.
+	 */
+	if (drm_enc->encoder_type == DRM_MODE_ENCODER_TMDS && is_self_refresh_active(old_state))
+		return;
+
 	mutex_lock(&dpu_enc->enc_lock);
 	dpu_enc->enabled = false;
 
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
index bce4764..cc0a674 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
@@ -507,7 +507,7 @@ static void dpu_kms_wait_for_commit_done(struct msm_kms *kms,
 		return;
 	}
 
-	if (!crtc->state->active) {
+	if (!drm_atomic_crtc_effectively_active(crtc->state)) {
 		DPU_DEBUG("[crtc:%d] not active\n", crtc->base.id);
 		return;
 	}
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [v3 5/5] drm/msm/disp/dpu1: add PSR support for eDP interface in dpu driver
@ 2022-06-21 10:53   ` Vinod Polimera
  0 siblings, 0 replies; 32+ messages in thread
From: Vinod Polimera @ 2022-06-21 10:53 UTC (permalink / raw)
  To: y, dri-devel, linux-arm-msm, freedreno, devicetree
  Cc: quic_kalyant, quic_sbillaka, dianders, linux-kernel,
	dmitry.baryshkov, swboyd, Vinod Polimera

Enable PSR on eDP interface using drm self-refresh librabry.
This patch uses a trigger from self-refresh library to enter/exit
into PSR, when there are no updates from framework.

Signed-off-by: Kalyan Thota <quic_kalyant@quicinc.com>
Signed-off-by: Vinod Polimera <quic_vpolimer@quicinc.com>
---
 drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c    | 36 ++++++++++++++++++++++++-----
 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 20 +++++++++++++++-
 drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c     |  2 +-
 3 files changed, 50 insertions(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
index b56f777..c6e4f03 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
@@ -18,6 +18,7 @@
 #include <drm/drm_probe_helper.h>
 #include <drm/drm_rect.h>
 #include <drm/drm_vblank.h>
+#include <drm/drm_self_refresh_helper.h>
 
 #include "dpu_kms.h"
 #include "dpu_hw_lm.h"
@@ -955,24 +956,39 @@ static void dpu_crtc_disable(struct drm_crtc *crtc,
 									      crtc);
 	struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc);
 	struct dpu_crtc_state *cstate = to_dpu_crtc_state(crtc->state);
-	struct drm_encoder *encoder;
+	struct drm_encoder *encoder = NULL;
 	unsigned long flags;
 	bool release_bandwidth = false;
 
 	DRM_DEBUG_KMS("crtc%d\n", crtc->base.id);
 
+	if (old_crtc_state->self_refresh_active) {
+		drm_for_each_encoder_mask(encoder, crtc->dev,
+					old_crtc_state->encoder_mask) {
+			dpu_encoder_assign_crtc(encoder, NULL);
+		}
+		return;
+	}
+
 	/* Disable/save vblank irq handling */
 	drm_crtc_vblank_off(crtc);
 
 	drm_for_each_encoder_mask(encoder, crtc->dev,
 				  old_crtc_state->encoder_mask) {
-		/* in video mode, we hold an extra bandwidth reference
+		/*
+		 * in video mode, we hold an extra bandwidth reference
 		 * as we cannot drop bandwidth at frame-done if any
 		 * crtc is being used in video mode.
 		 */
 		if (dpu_encoder_get_intf_mode(encoder) == INTF_MODE_VIDEO)
 			release_bandwidth = true;
-		dpu_encoder_assign_crtc(encoder, NULL);
+		/*
+		 * If disable is triggered during psr active(e.g: screen dim in PSR),
+		 * we will need encoder->crtc connection to process the device sleep &
+		 * preserve it during psr sequence.
+		 */
+		if (!crtc->state->self_refresh_active)
+			dpu_encoder_assign_crtc(encoder, NULL);
 	}
 
 	/* wait for frame_event_done completion */
@@ -1020,7 +1036,9 @@ static void dpu_crtc_enable(struct drm_crtc *crtc,
 	struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc);
 	struct drm_encoder *encoder;
 	bool request_bandwidth = false;
+	struct drm_crtc_state *old_crtc_state;
 
+	old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc);
 	pm_runtime_get_sync(crtc->dev->dev);
 
 	DRM_DEBUG_KMS("crtc%d\n", crtc->base.id);
@@ -1042,8 +1060,9 @@ static void dpu_crtc_enable(struct drm_crtc *crtc,
 	trace_dpu_crtc_enable(DRMID(crtc), true, dpu_crtc);
 	dpu_crtc->enabled = true;
 
-	drm_for_each_encoder_mask(encoder, crtc->dev, crtc->state->encoder_mask)
-		dpu_encoder_assign_crtc(encoder, crtc);
+	if (!old_crtc_state->self_refresh_active)
+		drm_for_each_encoder_mask(encoder, crtc->dev, crtc->state->encoder_mask)
+			dpu_encoder_assign_crtc(encoder, crtc);
 
 	/* Enable/restore vblank irq handling */
 	drm_crtc_vblank_on(crtc);
@@ -1525,7 +1544,7 @@ struct drm_crtc *dpu_crtc_init(struct drm_device *dev, struct drm_plane *plane,
 {
 	struct drm_crtc *crtc = NULL;
 	struct dpu_crtc *dpu_crtc = NULL;
-	int i;
+	int i, ret;
 
 	dpu_crtc = kzalloc(sizeof(*dpu_crtc), GFP_KERNEL);
 	if (!dpu_crtc)
@@ -1562,6 +1581,11 @@ struct drm_crtc *dpu_crtc_init(struct drm_device *dev, struct drm_plane *plane,
 	/* initialize event handling */
 	spin_lock_init(&dpu_crtc->event_lock);
 
+	ret = drm_self_refresh_helper_init(crtc);
+	if (ret)
+		DPU_ERROR("Failed to initialize %s with self-refresh helpers %d\n",
+			crtc->name, ret);
+
 	DRM_DEBUG_KMS("%s: successfully initialized crtc\n", dpu_crtc->name);
 	return crtc;
 }
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
index cc2809b..234e95d 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
@@ -225,6 +225,11 @@ bool dpu_encoder_is_widebus_enabled(const struct drm_encoder *drm_enc)
 	return dpu_enc->wide_bus_en;
 }
 
+static inline bool is_self_refresh_active(const struct drm_crtc_state *state)
+{
+	return (state && state->self_refresh_active);
+}
+
 static void _dpu_encoder_setup_dither(struct dpu_hw_pingpong *hw_pp, unsigned bpc)
 {
 	struct dpu_hw_dither_cfg dither_cfg = { 0 };
@@ -592,7 +597,8 @@ static int dpu_encoder_virt_atomic_check(
 		if (drm_atomic_crtc_needs_modeset(crtc_state)) {
 			dpu_rm_release(global_state, drm_enc);
 
-			if (!crtc_state->active_changed || crtc_state->active)
+			if (!crtc_state->active_changed || crtc_state->active ||
+					crtc_state->self_refresh_active)
 				ret = dpu_rm_reserve(&dpu_kms->rm, global_state,
 						drm_enc, crtc_state, topology);
 		}
@@ -1171,11 +1177,23 @@ static void dpu_encoder_virt_atomic_disable(struct drm_encoder *drm_enc,
 					struct drm_atomic_state *state)
 {
 	struct dpu_encoder_virt *dpu_enc = NULL;
+	struct drm_crtc *crtc;
+	struct drm_crtc_state *old_state;
 	int i = 0;
 
 	dpu_enc = to_dpu_encoder_virt(drm_enc);
 	DPU_DEBUG_ENC(dpu_enc, "\n");
 
+	crtc = dpu_enc->crtc;
+	old_state = drm_atomic_get_old_crtc_state(state, crtc);
+
+	/*
+	 * The encoder disabled already occurred when self refresh mode
+	 * was set earlier, in the old_state for the corresponding crtc.
+	 */
+	if (drm_enc->encoder_type == DRM_MODE_ENCODER_TMDS && is_self_refresh_active(old_state))
+		return;
+
 	mutex_lock(&dpu_enc->enc_lock);
 	dpu_enc->enabled = false;
 
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
index bce4764..cc0a674 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
@@ -507,7 +507,7 @@ static void dpu_kms_wait_for_commit_done(struct msm_kms *kms,
 		return;
 	}
 
-	if (!crtc->state->active) {
+	if (!drm_atomic_crtc_effectively_active(crtc->state)) {
 		DPU_DEBUG("[crtc:%d] not active\n", crtc->base.id);
 		return;
 	}
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* Re: [v3 0/5] Add PSR support for eDP
  2022-06-21 10:53 ` Vinod Polimera
@ 2022-06-21 18:40   ` Dmitry Baryshkov
  -1 siblings, 0 replies; 32+ messages in thread
From: Dmitry Baryshkov @ 2022-06-21 18:40 UTC (permalink / raw)
  To: Vinod Polimera, y, dri-devel, linux-arm-msm, freedreno, devicetree
  Cc: linux-kernel, robdclark, dianders, swboyd, quic_kalyant, quic_sbillaka

Please fix your mailer. You've got the following headers, which clearly 
confuse the threading:

In-Reply-To: <y>
References: <y>

Also the y@qualcomm.com address (present in To:) rejects incoming mail.


On 21/06/2022 13:53, Vinod Polimera wrote:
> Changes in v2:
>    - Use dp bridge to set psr entry/exit instead of dpu_enocder.
>    - Don't modify whitespaces.
>    - Set self refresh aware from atomic_check.
>    - Set self refresh aware only if psr is supported.
>    - Provide a stub for msm_dp_display_set_psr.
>    - Move dp functions to bridge code.
> 
> Changes in v3:
>    - Change callback names to reflect atomic interfaces.
>    - Move bridge callback change to separate patch as suggested by Dmitry.
>    - Remove psr function declaration from msm_drv.h.
>    - Set self_refresh_aware flag only if psr is supported.
>    - Modify the variable names to simpler form.
>    - Define bit fields for PSR settings.
>    - Add comments explaining the steps to enter/exit psr.
>    - Change DRM_INFO to drm_dbg_db.
> 
> Signed-off-by: Sankeerth Billakanti <quic_sbillaka@quicinc.com>
> Signed-off-by: Kalyan Thota <quic_kalyant@quicinc.com>
> Signed-off-by: Vinod Polimera <quic_vpolimer@quicinc.com>
> 
> Vinod Polimera (5):
>    drm/msm/dp: Add basic PSR support for eDP
>    drm/bridge: use atomic enable/disable callbacks for panel bridge
>      functions
>    drm/bridge: add psr support during panel bridge enable & disable
>      sequence
>    drm/msm/disp/dpu1: use atomic enable/disable callbacks for encoder
>      functions
>    drm/msm/disp/dpu1: add PSR support for eDP interface in dpu driver
> 
>   drivers/gpu/drm/bridge/panel.c              | 110 ++++++++++++++++--
>   drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c    |  36 +++++-
>   drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c |  30 ++++-
>   drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c     |   2 +-
>   drivers/gpu/drm/msm/dp/dp_catalog.c         |  81 ++++++++++++++
>   drivers/gpu/drm/msm/dp/dp_catalog.h         |   4 +
>   drivers/gpu/drm/msm/dp/dp_ctrl.c            |  76 ++++++++++++-
>   drivers/gpu/drm/msm/dp/dp_ctrl.h            |   3 +
>   drivers/gpu/drm/msm/dp/dp_display.c         |  14 +++
>   drivers/gpu/drm/msm/dp/dp_display.h         |   2 +
>   drivers/gpu/drm/msm/dp/dp_drm.c             | 166 +++++++++++++++++++++++++++-
>   drivers/gpu/drm/msm/dp/dp_link.c            |  36 ++++++
>   drivers/gpu/drm/msm/dp/dp_panel.c           |  22 ++++
>   drivers/gpu/drm/msm/dp/dp_panel.h           |   6 +
>   drivers/gpu/drm/msm/dp/dp_reg.h             |  27 +++++
>   15 files changed, 591 insertions(+), 24 deletions(-)
> 


-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [v3 0/5] Add PSR support for eDP
@ 2022-06-21 18:40   ` Dmitry Baryshkov
  0 siblings, 0 replies; 32+ messages in thread
From: Dmitry Baryshkov @ 2022-06-21 18:40 UTC (permalink / raw)
  To: Vinod Polimera, y, dri-devel, linux-arm-msm, freedreno, devicetree
  Cc: quic_kalyant, quic_sbillaka, dianders, linux-kernel, swboyd

Please fix your mailer. You've got the following headers, which clearly 
confuse the threading:

In-Reply-To: <y>
References: <y>

Also the y@qualcomm.com address (present in To:) rejects incoming mail.


On 21/06/2022 13:53, Vinod Polimera wrote:
> Changes in v2:
>    - Use dp bridge to set psr entry/exit instead of dpu_enocder.
>    - Don't modify whitespaces.
>    - Set self refresh aware from atomic_check.
>    - Set self refresh aware only if psr is supported.
>    - Provide a stub for msm_dp_display_set_psr.
>    - Move dp functions to bridge code.
> 
> Changes in v3:
>    - Change callback names to reflect atomic interfaces.
>    - Move bridge callback change to separate patch as suggested by Dmitry.
>    - Remove psr function declaration from msm_drv.h.
>    - Set self_refresh_aware flag only if psr is supported.
>    - Modify the variable names to simpler form.
>    - Define bit fields for PSR settings.
>    - Add comments explaining the steps to enter/exit psr.
>    - Change DRM_INFO to drm_dbg_db.
> 
> Signed-off-by: Sankeerth Billakanti <quic_sbillaka@quicinc.com>
> Signed-off-by: Kalyan Thota <quic_kalyant@quicinc.com>
> Signed-off-by: Vinod Polimera <quic_vpolimer@quicinc.com>
> 
> Vinod Polimera (5):
>    drm/msm/dp: Add basic PSR support for eDP
>    drm/bridge: use atomic enable/disable callbacks for panel bridge
>      functions
>    drm/bridge: add psr support during panel bridge enable & disable
>      sequence
>    drm/msm/disp/dpu1: use atomic enable/disable callbacks for encoder
>      functions
>    drm/msm/disp/dpu1: add PSR support for eDP interface in dpu driver
> 
>   drivers/gpu/drm/bridge/panel.c              | 110 ++++++++++++++++--
>   drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c    |  36 +++++-
>   drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c |  30 ++++-
>   drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c     |   2 +-
>   drivers/gpu/drm/msm/dp/dp_catalog.c         |  81 ++++++++++++++
>   drivers/gpu/drm/msm/dp/dp_catalog.h         |   4 +
>   drivers/gpu/drm/msm/dp/dp_ctrl.c            |  76 ++++++++++++-
>   drivers/gpu/drm/msm/dp/dp_ctrl.h            |   3 +
>   drivers/gpu/drm/msm/dp/dp_display.c         |  14 +++
>   drivers/gpu/drm/msm/dp/dp_display.h         |   2 +
>   drivers/gpu/drm/msm/dp/dp_drm.c             | 166 +++++++++++++++++++++++++++-
>   drivers/gpu/drm/msm/dp/dp_link.c            |  36 ++++++
>   drivers/gpu/drm/msm/dp/dp_panel.c           |  22 ++++
>   drivers/gpu/drm/msm/dp/dp_panel.h           |   6 +
>   drivers/gpu/drm/msm/dp/dp_reg.h             |  27 +++++
>   15 files changed, 591 insertions(+), 24 deletions(-)
> 


-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [v3 1/5] drm/msm/dp: Add basic PSR support for eDP
  2022-06-21 10:53   ` Vinod Polimera
@ 2022-06-21 18:53     ` Dmitry Baryshkov
  -1 siblings, 0 replies; 32+ messages in thread
From: Dmitry Baryshkov @ 2022-06-21 18:53 UTC (permalink / raw)
  To: Vinod Polimera, y, dri-devel, linux-arm-msm, freedreno, devicetree
  Cc: linux-kernel, robdclark, dianders, swboyd, quic_kalyant, quic_sbillaka

On 21/06/2022 13:53, Vinod Polimera wrote:
> 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: Sankeerth Billakanti <quic_sbillaka@quicinc.com>
> Signed-off-by: Vinod Polimera <quic_vpolimer@quicinc.com>
> ---
>   drivers/gpu/drm/msm/dp/dp_catalog.c |  81 ++++++++++++++++++
>   drivers/gpu/drm/msm/dp/dp_catalog.h |   4 +
>   drivers/gpu/drm/msm/dp/dp_ctrl.c    |  76 ++++++++++++++++-
>   drivers/gpu/drm/msm/dp/dp_ctrl.h    |   3 +
>   drivers/gpu/drm/msm/dp/dp_display.c |  14 +++
>   drivers/gpu/drm/msm/dp/dp_display.h |   2 +
>   drivers/gpu/drm/msm/dp/dp_drm.c     | 166 +++++++++++++++++++++++++++++++++++-
>   drivers/gpu/drm/msm/dp/dp_link.c    |  36 ++++++++
>   drivers/gpu/drm/msm/dp/dp_panel.c   |  22 +++++
>   drivers/gpu/drm/msm/dp/dp_panel.h   |   6 ++
>   drivers/gpu/drm/msm/dp/dp_reg.h     |  27 ++++++
>   11 files changed, 433 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.c b/drivers/gpu/drm/msm/dp/dp_catalog.c
> index 7257515..b9021ed 100644
> --- a/drivers/gpu/drm/msm/dp/dp_catalog.c
> +++ b/drivers/gpu/drm/msm/dp/dp_catalog.c
> @@ -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,24 @@ 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);
> +	val &= ~DP_MAINLINK_CTRL_ENABLE;
> +
> +	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 +636,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, !UPDATE_SDP);
> +}
> +
> +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 +712,20 @@ u32 dp_catalog_hpd_get_intr_status(struct dp_catalog *dp_catalog)
>   	return isr & (mask | ~DP_DP_HPD_INT_MASK);
>   }
>   
> +int 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,
> diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.h b/drivers/gpu/drm/msm/dp/dp_catalog.h
> index 1f717f4..6454845 100644
> --- a/drivers/gpu/drm/msm/dp/dp_catalog.h
> +++ b/drivers/gpu/drm/msm/dp/dp_catalog.h
> @@ -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);
> +int 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);
> diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
> index d21971b..485e8f5 100644
> --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
> +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
> @@ -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);
>   }
>   
> @@ -1382,11 +1387,64 @@ static int dp_ctrl_enable_stream_clocks(struct dp_ctrl_private *ctrl)
>   	return ret;
>   }
>   
> -void dp_ctrl_reset_irq_ctrl(struct dp_ctrl *dp_ctrl, bool enable)
> +void dp_ctrl_config_psr(struct dp_ctrl *dp_ctrl)
>   {
> -	struct dp_ctrl_private *ctrl;
> +	u8 cfg;
> +	struct dp_ctrl_private *ctrl = container_of(dp_ctrl,
> +			struct dp_ctrl_private, dp_ctrl);
>   
> -	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_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);
> +	}
> +}
> +
> +void dp_ctrl_reset_irq_ctrl(struct dp_ctrl *dp_ctrl, bool enable)
> +{
> +	struct dp_ctrl_private *ctrl = container_of(dp_ctrl,
> +			struct dp_ctrl_private, dp_ctrl);
>   
>   	dp_catalog_ctrl_reset(ctrl->catalog);
>   
> @@ -1997,6 +2055,17 @@ 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 == PSR_UPDATE_INT)
> +			drm_dbg_dp(ctrl->drm_dev, "PSR frame update done\n");
> +		else if (isr == PSR_EXIT_INT)
> +			drm_dbg_dp(ctrl->drm_dev, "PSR exit done\n");
> +
> +		complete(&ctrl->psr_op_comp);
> +	}
> +
>   	isr = dp_catalog_ctrl_get_interrupt(ctrl->catalog);
>   
>   	if (isr & DP_CTRL_INTR_READY_FOR_VIDEO) {
> @@ -2043,6 +2112,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 */
> diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.h b/drivers/gpu/drm/msm/dp/dp_ctrl.h
> index 0745fde..be074ae 100644
> --- a/drivers/gpu/drm/msm/dp/dp_ctrl.h
> +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h
> @@ -38,4 +38,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_ */
> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
> index bce7793..2e779c2 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.c
> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
> @@ -388,6 +388,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);
>   
> @@ -895,6 +897,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);

Dumb question. What if the first display reports psr as supported, but 
the next DP display that gets plugged doesn't support PSR? Do we need to 
do anything to clear this config?

> +
>   	return 0;
>   }
>   
> @@ -1094,6 +1100,14 @@ 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;
> +
> +	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;
> diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
> index 4f9fe4d..1feaada 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.h
> +++ b/drivers/gpu/drm/msm/dp/dp_display.h
> @@ -29,6 +29,7 @@ struct msm_dp {
>   
>   	u32 max_dp_lanes;
>   	struct dp_audio *dp_audio;
> +	bool psr_supported;
>   };
>   
>   int dp_display_set_plugged_cb(struct msm_dp *dp_display,
> @@ -39,5 +40,6 @@ bool dp_display_check_video_test(struct msm_dp *dp_display);
>   int dp_display_get_test_bpp(struct msm_dp *dp_display);
>   void dp_display_signal_audio_start(struct msm_dp *dp_display);
>   void dp_display_signal_audio_complete(struct msm_dp *dp_display);
> +void dp_display_set_psr(struct msm_dp *dp, bool enter);
>   
>   #endif /* _DP_DISPLAY_H_ */
> diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c b/drivers/gpu/drm/msm/dp/dp_drm.c
> index 62d58b9..cfe31ed 100644
> --- a/drivers/gpu/drm/msm/dp/dp_drm.c
> +++ b/drivers/gpu/drm/msm/dp/dp_drm.c
> @@ -60,6 +60,169 @@ static int dp_bridge_get_modes(struct drm_bridge *bridge, struct drm_connector *
>   	return rc;
>   }
>   
> +static struct drm_crtc *dp_bridge_get_old_connector_crtc(struct msm_dp *dp,
> +					  struct drm_atomic_state *state)
> +{
> +	struct drm_encoder *encoder = dp->encoder;
> +	struct drm_connector *connector;
> +	struct drm_connector_state *conn_state;
> +
> +	connector = drm_atomic_get_old_connector_for_encoder(state, encoder);
> +	if (!connector)
> +		return NULL;
> +
> +	conn_state = drm_atomic_get_old_connector_state(state, connector);
> +	if (!conn_state)
> +		return NULL;
> +
> +	return conn_state->crtc;
> +}
> +
> +static struct drm_crtc *dp_bridge_get_new_connector_crtc(struct msm_dp *dp,
> +					  struct drm_atomic_state *state)
> +{
> +	struct drm_encoder *encoder = dp->encoder;
> +	struct drm_connector *connector;
> +	struct drm_connector_state *conn_state;
> +
> +	connector = drm_atomic_get_new_connector_for_encoder(state, encoder);
> +	if (!connector)
> +		return NULL;
> +
> +	conn_state = drm_atomic_get_new_connector_state(state, connector);
> +	if (!conn_state)
> +		return NULL;
> +
> +	return conn_state->crtc;
> +}

These two functions should become core DRM helpers. Please move them 
accordingly to remove duplication with the bridge code.

> +
> +static int edp_bridge_atomic_check(struct drm_bridge *drm_bridge,
> +			    struct drm_bridge_state *bridge_state,
> +			    struct drm_crtc_state *crtc_state,
> +			    struct drm_connector_state *conn_state)
> +{
> +	struct msm_dp *dp;
> +
> +	dp = to_dp_bridge(drm_bridge)->dp_display;
> +	if (WARN_ON(!conn_state))
> +		return -ENODEV;
> +
> +	if (dp->psr_supported)
> +		conn_state->self_refresh_aware = true;
> +
> +	if (!conn_state->crtc || !crtc_state)
> +		return 0;
> +
> +	if (crtc_state->self_refresh_active && !dp->psr_supported)
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +static void edp_bridge_atomic_enable(struct drm_bridge *drm_bridge,
> +				struct drm_bridge_state *old_bridge_state)
> +{
> +	struct drm_atomic_state *old_state = old_bridge_state->base.state;
> +	struct drm_crtc *crtc;
> +	struct drm_crtc_state *old_crtc_state;
> +	struct msm_dp_bridge *dp_bridge = to_dp_bridge(drm_bridge);
> +	struct msm_dp *dp_display = dp_bridge->dp_display;
> +
> +	crtc = dp_bridge_get_new_connector_crtc(dp_display, old_state);
> +	if (!crtc)
> +		return;

Why?

> +
> +	old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc);
> +
> +	/* Exit from self refresh mode */
> +	if (old_crtc_state && old_crtc_state->self_refresh_active) {
> +		dp_display_set_psr(dp_display, false);
> +		return;
> +	}
> +
> +	dp_bridge_enable(drm_bridge);
> +}
> +
> +static void edp_bridge_atomic_disable(struct drm_bridge *drm_bridge,
> +				struct drm_bridge_state *old_bridge_state)
> +{
> +	struct drm_atomic_state *old_state = old_bridge_state->base.state;
> +	struct drm_crtc *crtc;
> +	struct drm_crtc_state *new_crtc_state = NULL, *old_crtc_state = NULL;
> +	struct msm_dp_bridge *dp_bridge = to_dp_bridge(drm_bridge);
> +	struct msm_dp *dp_display = dp_bridge->dp_display;
> +
> +	crtc = dp_bridge_get_old_connector_crtc(dp_display, old_state);
> +	if (!crtc)
> +		goto out;

Why? And why old crtc?

> +
> +	new_crtc_state = drm_atomic_get_new_crtc_state(old_state, crtc);
> +	if (!new_crtc_state)
> +		goto out;
> +
> +	old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc);
> +	if (!old_crtc_state)
> +		goto out;
> +
> +	/*
> +	 * Set self refresh mode if current crtc state is active.
> +	 * If old crtc state is active, then this is a display disable
> +	 * call while the sink is in psr state. So, exit psr here.
> +	 * The eDP controller will be disabled in the
> +	 * edp_bridge_atomic_post_disable function.
> +	 *
> +	 * We observed sink is stuck in self refresh if psr exit is skipped
> +	 * when display disable occurs while the sink is in psr state.
> +	 */
> +	if (new_crtc_state->self_refresh_active) {
> +		dp_display_set_psr(dp_display, true);
> +		return;
> +	} else if (old_crtc_state->self_refresh_active) {
> +		dp_display_set_psr(dp_display, false);
> +		return;
> +	}
> +
> +out:
> +	dp_bridge_disable(drm_bridge);
> +}
> +
> +static void edp_bridge_atomic_post_disable(struct drm_bridge *drm_bridge,
> +				struct drm_bridge_state *old_bridge_state)
> +{
> +	struct drm_atomic_state *old_state = old_bridge_state->base.state;
> +	struct drm_crtc *crtc;
> +	struct drm_crtc_state *new_crtc_state = NULL;
> +	struct msm_dp_bridge *dp_bridge = to_dp_bridge(drm_bridge);
> +	struct msm_dp *dp_display = dp_bridge->dp_display;
> +
> +	crtc = dp_bridge_get_old_connector_crtc(dp_display, old_state);
> +	if (!crtc)
> +		return;
> +
> +	new_crtc_state = drm_atomic_get_new_crtc_state(old_state, crtc);
> +	if (!new_crtc_state)
> +		return;
> +
> +	/*
> +	 * Self refresh mode is already set in edp_bridge_atomic_disable.
> +	 */
> +	if (new_crtc_state->self_refresh_active)
> +		return;
> +
> +	dp_bridge_post_disable(drm_bridge);
> +}
> +
> +static const struct drm_bridge_funcs edp_bridge_ops = {
> +	.atomic_enable       = edp_bridge_atomic_enable,
> +	.atomic_disable      = edp_bridge_atomic_disable,
> +	.atomic_post_disable = edp_bridge_atomic_post_disable,

If you are using the atomic ops here, please switch dp_bridge_ops to 
atomic too.

> +	.mode_set     = dp_bridge_mode_set,

.mode_valid is missing. Is this intentional?

> +	.atomic_reset = drm_atomic_helper_bridge_reset,
> +	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
> +	.atomic_check = edp_bridge_atomic_check,
> +};
> +
>   static const struct drm_bridge_funcs dp_bridge_ops = {
>   	.enable       = dp_bridge_enable,
>   	.disable      = dp_bridge_disable,
> @@ -84,7 +247,7 @@ struct drm_bridge *dp_bridge_init(struct msm_dp *dp_display, struct drm_device *
>   	dp_bridge->dp_display = dp_display;
>   
>   	bridge = &dp_bridge->bridge;
> -	bridge->funcs = &dp_bridge_ops;
> +	bridge->funcs = &edp_bridge_ops;

Please use ternary operator here.

>   	bridge->type = dp_display->connector_type;
>   
>   	/*
> @@ -99,6 +262,7 @@ struct drm_bridge *dp_bridge_init(struct msm_dp *dp_display, struct drm_device *
>   	 *   modes.
>   	 */
>   	if (!dp_display->is_edp) {
> +		bridge->funcs = &dp_bridge_ops;
>   		bridge->ops =
>   			DRM_BRIDGE_OP_DETECT |
>   			DRM_BRIDGE_OP_HPD |
> diff --git a/drivers/gpu/drm/msm/dp/dp_link.c b/drivers/gpu/drm/msm/dp/dp_link.c
> index 36f0af0..81ac3c1 100644
> --- a/drivers/gpu/drm/msm/dp/dp_link.c
> +++ b/drivers/gpu/drm/msm/dp/dp_link.c
> @@ -934,6 +934,38 @@ static int dp_link_process_phy_test_pattern_request(
>   	return 0;
>   }
>   
> +static bool dp_link_read_psr_error_status(struct dp_link_private *link)
> +{
> +	u8 status;
> +
> +	drm_dp_dpcd_read(link->aux, DP_PSR_ERROR_STATUS, &status, 1);
> +
> +	if (status & DP_PSR_LINK_CRC_ERROR)
> +		DRM_ERROR("PSR LINK CRC ERROR\n");
> +	else if (status & DP_PSR_RFB_STORAGE_ERROR)
> +		DRM_ERROR("PSR RFB STORAGE ERROR\n");
> +	else if (status & DP_PSR_VSC_SDP_UNCORRECTABLE_ERROR)
> +		DRM_ERROR("PSR VSC SDP UNCORRECTABLE ERROR\n");
> +	else
> +		return 0;
> +
> +	return 1;

If it's bool, it should return true/false and not 0/1.

> +}
> +
> +static bool dp_link_psr_capability_changed(struct dp_link_private *link)
> +{
> +	u8 status;
> +
> +	drm_dp_dpcd_read(link->aux, DP_PSR_ESI, &status, 1);
> +
> +	if (status & DP_PSR_CAPS_CHANGE)
> +		drm_dbg_dp(link->drm_dev, "PSR Capability Change\n");
> +	else
> +		return 0;
> +
> +	return 1;

if (!(satus & DP_PSR_CAPS_CHANGE))
     return false;

drm_dbg_dp(..)
return true;


Or

if (status & DP_PSR_CAPS_CHANGE) {
   drm_dbg_dp(...)
   return true;
}

return false;


> +}
> +
>   static u8 get_link_status(const u8 link_status[DP_LINK_STATUS_SIZE], int r)
>   {
>   	return link_status[r - DP_LANE0_1_STATUS];
> @@ -1053,6 +1085,10 @@ int dp_link_process_request(struct dp_link *dp_link)
>   		dp_link->sink_request |= DP_TEST_LINK_TRAINING;
>   	} else if (!dp_link_process_phy_test_pattern_request(link)) {
>   		dp_link->sink_request |= DP_TEST_LINK_PHY_TEST_PATTERN;
> +	} else if (dp_link_read_psr_error_status(link)) {
> +		DRM_ERROR("PSR IRQ_HPD received\n");
> +	} else if (dp_link_psr_capability_changed(link)) {
> +		drm_dbg_dp(link->drm_dev, "PSR Capabiity changed");
>   	} else {
>   		ret = dp_link_process_link_status_update(link);
>   		if (!ret) {
> diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c
> index 5149ceb..8bf8ab4 100644
> --- a/drivers/gpu/drm/msm/dp/dp_panel.c
> +++ b/drivers/gpu/drm/msm/dp/dp_panel.c
> @@ -20,6 +20,27 @@ struct dp_panel_private {
>   	bool aux_cfg_update_done;
>   };
>   
> +static void dp_panel_read_psr_cap(struct dp_panel_private *panel)
> +{
> +	ssize_t rlen;
> +	struct dp_panel *dp_panel;
> +
> +	dp_panel = &panel->dp_panel;
> +
> +	/* edp sink */
> +	if (dp_panel->dpcd[DP_EDP_CONFIGURATION_CAP]) {
> +		rlen = drm_dp_dpcd_read(panel->aux, DP_PSR_SUPPORT,
> +				&dp_panel->psr_cap, sizeof(dp_panel->psr_cap));
> +		if (rlen == sizeof(dp_panel->psr_cap)) {
> +			drm_dbg_dp(panel->drm_dev,
> +				"psr version: 0x%x, psr_cap: 0x%x\n",
> +				dp_panel->psr_cap.version,
> +				dp_panel->psr_cap.capabilities);
> +		} else
> +			DRM_ERROR("failed to read psr info, rlen=%zd\n", rlen);
> +	}
> +}
> +
>   static int dp_panel_read_dpcd(struct dp_panel *dp_panel)
>   {
>   	int rc = 0;
> @@ -106,6 +127,7 @@ static int dp_panel_read_dpcd(struct dp_panel *dp_panel)
>   		}
>   	}
>   
> +	dp_panel_read_psr_cap(panel);
>   end:
>   	return rc;
>   }
> diff --git a/drivers/gpu/drm/msm/dp/dp_panel.h b/drivers/gpu/drm/msm/dp/dp_panel.h
> index d861197a..2d0826a 100644
> --- a/drivers/gpu/drm/msm/dp/dp_panel.h
> +++ b/drivers/gpu/drm/msm/dp/dp_panel.h
> @@ -34,6 +34,11 @@ struct dp_panel_in {
>   	struct dp_catalog *catalog;
>   };
>   
> +struct dp_panel_psr {
> +	u8 version;
> +	u8 capabilities;
> +};
> +
>   struct dp_panel {
>   	/* dpcd raw data */
>   	u8 dpcd[DP_RECEIVER_CAP_SIZE + 1];
> @@ -46,6 +51,7 @@ struct dp_panel {
>   	struct edid *edid;
>   	struct drm_connector *connector;
>   	struct dp_display_mode dp_mode;
> +	struct dp_panel_psr psr_cap;
>   	bool video_test;
>   
>   	u32 vic;
> diff --git a/drivers/gpu/drm/msm/dp/dp_reg.h b/drivers/gpu/drm/msm/dp/dp_reg.h
> index 2686028..ea85a69 100644
> --- a/drivers/gpu/drm/msm/dp/dp_reg.h
> +++ b/drivers/gpu/drm/msm/dp/dp_reg.h
> @@ -22,6 +22,20 @@
>   #define REG_DP_INTR_STATUS2			(0x00000024)
>   #define REG_DP_INTR_STATUS3			(0x00000028)
>   
> +#define REG_DP_INTR_STATUS4			(0x0000002C)
> +#define PSR_UPDATE_INT				(0x00000001)
> +#define PSR_CAPTURE_INT				(0x00000004)
> +#define PSR_EXIT_INT				(0x00000010)
> +#define PSR_UPDATE_ERROR_INT			(0x00000040)
> +#define PSR_WAKE_ERROR_INT			(0x00000100)
> +
> +#define REG_DP_INTR_MASK4			(0x00000030)
> +#define PSR_UPDATE_MASK				(0x00000001)
> +#define PSR_CAPTURE_MASK			(0x00000002)
> +#define PSR_EXIT_MASK				(0x00000004)
> +#define PSR_UPDATE_ERROR_MASK			(0x00000008)
> +#define PSR_WAKE_ERROR_MASK			(0x00000010)
> +
>   #define REG_DP_DP_HPD_CTRL			(0x00000000)
>   #define DP_DP_HPD_CTRL_HPD_EN			(0x00000001)
>   
> @@ -164,6 +178,16 @@
>   #define MMSS_DP_AUDIO_TIMING_RBR_48		(0x00000094)
>   #define MMSS_DP_AUDIO_TIMING_HBR_48		(0x00000098)
>   
> +#define REG_PSR_CONFIG				(0x00000100)
> +#define DISABLE_PSR				(0x00000000)
> +#define PSR1_SUPPORTED				(0x00000001)
> +#define PSR2_WITHOUT_FRAMESYNC			(0x00000002)
> +#define PSR2_WITH_FRAMESYNC			(0x00000003)
> +
> +#define REG_PSR_CMD				(0x00000110)
> +#define PSR_ENTER				(0x00000001)
> +#define PSR_EXIT				(0x00000002)
> +
>   #define MMSS_DP_PSR_CRC_RG			(0x00000154)
>   #define MMSS_DP_PSR_CRC_B			(0x00000158)
>   
> @@ -184,6 +208,9 @@
>   #define MMSS_DP_AUDIO_STREAM_0			(0x00000240)
>   #define MMSS_DP_AUDIO_STREAM_1			(0x00000244)
>   
> +#define MMSS_DP_SDP_CFG3			(0x0000024c)
> +#define UPDATE_SDP				(0x00000001)
> +
>   #define MMSS_DP_EXTENSION_0			(0x00000250)
>   #define MMSS_DP_EXTENSION_1			(0x00000254)
>   #define MMSS_DP_EXTENSION_2			(0x00000258)


-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [v3 1/5] drm/msm/dp: Add basic PSR support for eDP
@ 2022-06-21 18:53     ` Dmitry Baryshkov
  0 siblings, 0 replies; 32+ messages in thread
From: Dmitry Baryshkov @ 2022-06-21 18:53 UTC (permalink / raw)
  To: Vinod Polimera, y, dri-devel, linux-arm-msm, freedreno, devicetree
  Cc: quic_kalyant, quic_sbillaka, dianders, linux-kernel, swboyd

On 21/06/2022 13:53, Vinod Polimera wrote:
> 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: Sankeerth Billakanti <quic_sbillaka@quicinc.com>
> Signed-off-by: Vinod Polimera <quic_vpolimer@quicinc.com>
> ---
>   drivers/gpu/drm/msm/dp/dp_catalog.c |  81 ++++++++++++++++++
>   drivers/gpu/drm/msm/dp/dp_catalog.h |   4 +
>   drivers/gpu/drm/msm/dp/dp_ctrl.c    |  76 ++++++++++++++++-
>   drivers/gpu/drm/msm/dp/dp_ctrl.h    |   3 +
>   drivers/gpu/drm/msm/dp/dp_display.c |  14 +++
>   drivers/gpu/drm/msm/dp/dp_display.h |   2 +
>   drivers/gpu/drm/msm/dp/dp_drm.c     | 166 +++++++++++++++++++++++++++++++++++-
>   drivers/gpu/drm/msm/dp/dp_link.c    |  36 ++++++++
>   drivers/gpu/drm/msm/dp/dp_panel.c   |  22 +++++
>   drivers/gpu/drm/msm/dp/dp_panel.h   |   6 ++
>   drivers/gpu/drm/msm/dp/dp_reg.h     |  27 ++++++
>   11 files changed, 433 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.c b/drivers/gpu/drm/msm/dp/dp_catalog.c
> index 7257515..b9021ed 100644
> --- a/drivers/gpu/drm/msm/dp/dp_catalog.c
> +++ b/drivers/gpu/drm/msm/dp/dp_catalog.c
> @@ -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,24 @@ 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);
> +	val &= ~DP_MAINLINK_CTRL_ENABLE;
> +
> +	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 +636,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, !UPDATE_SDP);
> +}
> +
> +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 +712,20 @@ u32 dp_catalog_hpd_get_intr_status(struct dp_catalog *dp_catalog)
>   	return isr & (mask | ~DP_DP_HPD_INT_MASK);
>   }
>   
> +int 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,
> diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.h b/drivers/gpu/drm/msm/dp/dp_catalog.h
> index 1f717f4..6454845 100644
> --- a/drivers/gpu/drm/msm/dp/dp_catalog.h
> +++ b/drivers/gpu/drm/msm/dp/dp_catalog.h
> @@ -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);
> +int 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);
> diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
> index d21971b..485e8f5 100644
> --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
> +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
> @@ -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);
>   }
>   
> @@ -1382,11 +1387,64 @@ static int dp_ctrl_enable_stream_clocks(struct dp_ctrl_private *ctrl)
>   	return ret;
>   }
>   
> -void dp_ctrl_reset_irq_ctrl(struct dp_ctrl *dp_ctrl, bool enable)
> +void dp_ctrl_config_psr(struct dp_ctrl *dp_ctrl)
>   {
> -	struct dp_ctrl_private *ctrl;
> +	u8 cfg;
> +	struct dp_ctrl_private *ctrl = container_of(dp_ctrl,
> +			struct dp_ctrl_private, dp_ctrl);
>   
> -	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_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);
> +	}
> +}
> +
> +void dp_ctrl_reset_irq_ctrl(struct dp_ctrl *dp_ctrl, bool enable)
> +{
> +	struct dp_ctrl_private *ctrl = container_of(dp_ctrl,
> +			struct dp_ctrl_private, dp_ctrl);
>   
>   	dp_catalog_ctrl_reset(ctrl->catalog);
>   
> @@ -1997,6 +2055,17 @@ 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 == PSR_UPDATE_INT)
> +			drm_dbg_dp(ctrl->drm_dev, "PSR frame update done\n");
> +		else if (isr == PSR_EXIT_INT)
> +			drm_dbg_dp(ctrl->drm_dev, "PSR exit done\n");
> +
> +		complete(&ctrl->psr_op_comp);
> +	}
> +
>   	isr = dp_catalog_ctrl_get_interrupt(ctrl->catalog);
>   
>   	if (isr & DP_CTRL_INTR_READY_FOR_VIDEO) {
> @@ -2043,6 +2112,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 */
> diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.h b/drivers/gpu/drm/msm/dp/dp_ctrl.h
> index 0745fde..be074ae 100644
> --- a/drivers/gpu/drm/msm/dp/dp_ctrl.h
> +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h
> @@ -38,4 +38,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_ */
> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
> index bce7793..2e779c2 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.c
> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
> @@ -388,6 +388,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);
>   
> @@ -895,6 +897,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);

Dumb question. What if the first display reports psr as supported, but 
the next DP display that gets plugged doesn't support PSR? Do we need to 
do anything to clear this config?

> +
>   	return 0;
>   }
>   
> @@ -1094,6 +1100,14 @@ 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;
> +
> +	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;
> diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
> index 4f9fe4d..1feaada 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.h
> +++ b/drivers/gpu/drm/msm/dp/dp_display.h
> @@ -29,6 +29,7 @@ struct msm_dp {
>   
>   	u32 max_dp_lanes;
>   	struct dp_audio *dp_audio;
> +	bool psr_supported;
>   };
>   
>   int dp_display_set_plugged_cb(struct msm_dp *dp_display,
> @@ -39,5 +40,6 @@ bool dp_display_check_video_test(struct msm_dp *dp_display);
>   int dp_display_get_test_bpp(struct msm_dp *dp_display);
>   void dp_display_signal_audio_start(struct msm_dp *dp_display);
>   void dp_display_signal_audio_complete(struct msm_dp *dp_display);
> +void dp_display_set_psr(struct msm_dp *dp, bool enter);
>   
>   #endif /* _DP_DISPLAY_H_ */
> diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c b/drivers/gpu/drm/msm/dp/dp_drm.c
> index 62d58b9..cfe31ed 100644
> --- a/drivers/gpu/drm/msm/dp/dp_drm.c
> +++ b/drivers/gpu/drm/msm/dp/dp_drm.c
> @@ -60,6 +60,169 @@ static int dp_bridge_get_modes(struct drm_bridge *bridge, struct drm_connector *
>   	return rc;
>   }
>   
> +static struct drm_crtc *dp_bridge_get_old_connector_crtc(struct msm_dp *dp,
> +					  struct drm_atomic_state *state)
> +{
> +	struct drm_encoder *encoder = dp->encoder;
> +	struct drm_connector *connector;
> +	struct drm_connector_state *conn_state;
> +
> +	connector = drm_atomic_get_old_connector_for_encoder(state, encoder);
> +	if (!connector)
> +		return NULL;
> +
> +	conn_state = drm_atomic_get_old_connector_state(state, connector);
> +	if (!conn_state)
> +		return NULL;
> +
> +	return conn_state->crtc;
> +}
> +
> +static struct drm_crtc *dp_bridge_get_new_connector_crtc(struct msm_dp *dp,
> +					  struct drm_atomic_state *state)
> +{
> +	struct drm_encoder *encoder = dp->encoder;
> +	struct drm_connector *connector;
> +	struct drm_connector_state *conn_state;
> +
> +	connector = drm_atomic_get_new_connector_for_encoder(state, encoder);
> +	if (!connector)
> +		return NULL;
> +
> +	conn_state = drm_atomic_get_new_connector_state(state, connector);
> +	if (!conn_state)
> +		return NULL;
> +
> +	return conn_state->crtc;
> +}

These two functions should become core DRM helpers. Please move them 
accordingly to remove duplication with the bridge code.

> +
> +static int edp_bridge_atomic_check(struct drm_bridge *drm_bridge,
> +			    struct drm_bridge_state *bridge_state,
> +			    struct drm_crtc_state *crtc_state,
> +			    struct drm_connector_state *conn_state)
> +{
> +	struct msm_dp *dp;
> +
> +	dp = to_dp_bridge(drm_bridge)->dp_display;
> +	if (WARN_ON(!conn_state))
> +		return -ENODEV;
> +
> +	if (dp->psr_supported)
> +		conn_state->self_refresh_aware = true;
> +
> +	if (!conn_state->crtc || !crtc_state)
> +		return 0;
> +
> +	if (crtc_state->self_refresh_active && !dp->psr_supported)
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +static void edp_bridge_atomic_enable(struct drm_bridge *drm_bridge,
> +				struct drm_bridge_state *old_bridge_state)
> +{
> +	struct drm_atomic_state *old_state = old_bridge_state->base.state;
> +	struct drm_crtc *crtc;
> +	struct drm_crtc_state *old_crtc_state;
> +	struct msm_dp_bridge *dp_bridge = to_dp_bridge(drm_bridge);
> +	struct msm_dp *dp_display = dp_bridge->dp_display;
> +
> +	crtc = dp_bridge_get_new_connector_crtc(dp_display, old_state);
> +	if (!crtc)
> +		return;

Why?

> +
> +	old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc);
> +
> +	/* Exit from self refresh mode */
> +	if (old_crtc_state && old_crtc_state->self_refresh_active) {
> +		dp_display_set_psr(dp_display, false);
> +		return;
> +	}
> +
> +	dp_bridge_enable(drm_bridge);
> +}
> +
> +static void edp_bridge_atomic_disable(struct drm_bridge *drm_bridge,
> +				struct drm_bridge_state *old_bridge_state)
> +{
> +	struct drm_atomic_state *old_state = old_bridge_state->base.state;
> +	struct drm_crtc *crtc;
> +	struct drm_crtc_state *new_crtc_state = NULL, *old_crtc_state = NULL;
> +	struct msm_dp_bridge *dp_bridge = to_dp_bridge(drm_bridge);
> +	struct msm_dp *dp_display = dp_bridge->dp_display;
> +
> +	crtc = dp_bridge_get_old_connector_crtc(dp_display, old_state);
> +	if (!crtc)
> +		goto out;

Why? And why old crtc?

> +
> +	new_crtc_state = drm_atomic_get_new_crtc_state(old_state, crtc);
> +	if (!new_crtc_state)
> +		goto out;
> +
> +	old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc);
> +	if (!old_crtc_state)
> +		goto out;
> +
> +	/*
> +	 * Set self refresh mode if current crtc state is active.
> +	 * If old crtc state is active, then this is a display disable
> +	 * call while the sink is in psr state. So, exit psr here.
> +	 * The eDP controller will be disabled in the
> +	 * edp_bridge_atomic_post_disable function.
> +	 *
> +	 * We observed sink is stuck in self refresh if psr exit is skipped
> +	 * when display disable occurs while the sink is in psr state.
> +	 */
> +	if (new_crtc_state->self_refresh_active) {
> +		dp_display_set_psr(dp_display, true);
> +		return;
> +	} else if (old_crtc_state->self_refresh_active) {
> +		dp_display_set_psr(dp_display, false);
> +		return;
> +	}
> +
> +out:
> +	dp_bridge_disable(drm_bridge);
> +}
> +
> +static void edp_bridge_atomic_post_disable(struct drm_bridge *drm_bridge,
> +				struct drm_bridge_state *old_bridge_state)
> +{
> +	struct drm_atomic_state *old_state = old_bridge_state->base.state;
> +	struct drm_crtc *crtc;
> +	struct drm_crtc_state *new_crtc_state = NULL;
> +	struct msm_dp_bridge *dp_bridge = to_dp_bridge(drm_bridge);
> +	struct msm_dp *dp_display = dp_bridge->dp_display;
> +
> +	crtc = dp_bridge_get_old_connector_crtc(dp_display, old_state);
> +	if (!crtc)
> +		return;
> +
> +	new_crtc_state = drm_atomic_get_new_crtc_state(old_state, crtc);
> +	if (!new_crtc_state)
> +		return;
> +
> +	/*
> +	 * Self refresh mode is already set in edp_bridge_atomic_disable.
> +	 */
> +	if (new_crtc_state->self_refresh_active)
> +		return;
> +
> +	dp_bridge_post_disable(drm_bridge);
> +}
> +
> +static const struct drm_bridge_funcs edp_bridge_ops = {
> +	.atomic_enable       = edp_bridge_atomic_enable,
> +	.atomic_disable      = edp_bridge_atomic_disable,
> +	.atomic_post_disable = edp_bridge_atomic_post_disable,

If you are using the atomic ops here, please switch dp_bridge_ops to 
atomic too.

> +	.mode_set     = dp_bridge_mode_set,

.mode_valid is missing. Is this intentional?

> +	.atomic_reset = drm_atomic_helper_bridge_reset,
> +	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
> +	.atomic_check = edp_bridge_atomic_check,
> +};
> +
>   static const struct drm_bridge_funcs dp_bridge_ops = {
>   	.enable       = dp_bridge_enable,
>   	.disable      = dp_bridge_disable,
> @@ -84,7 +247,7 @@ struct drm_bridge *dp_bridge_init(struct msm_dp *dp_display, struct drm_device *
>   	dp_bridge->dp_display = dp_display;
>   
>   	bridge = &dp_bridge->bridge;
> -	bridge->funcs = &dp_bridge_ops;
> +	bridge->funcs = &edp_bridge_ops;

Please use ternary operator here.

>   	bridge->type = dp_display->connector_type;
>   
>   	/*
> @@ -99,6 +262,7 @@ struct drm_bridge *dp_bridge_init(struct msm_dp *dp_display, struct drm_device *
>   	 *   modes.
>   	 */
>   	if (!dp_display->is_edp) {
> +		bridge->funcs = &dp_bridge_ops;
>   		bridge->ops =
>   			DRM_BRIDGE_OP_DETECT |
>   			DRM_BRIDGE_OP_HPD |
> diff --git a/drivers/gpu/drm/msm/dp/dp_link.c b/drivers/gpu/drm/msm/dp/dp_link.c
> index 36f0af0..81ac3c1 100644
> --- a/drivers/gpu/drm/msm/dp/dp_link.c
> +++ b/drivers/gpu/drm/msm/dp/dp_link.c
> @@ -934,6 +934,38 @@ static int dp_link_process_phy_test_pattern_request(
>   	return 0;
>   }
>   
> +static bool dp_link_read_psr_error_status(struct dp_link_private *link)
> +{
> +	u8 status;
> +
> +	drm_dp_dpcd_read(link->aux, DP_PSR_ERROR_STATUS, &status, 1);
> +
> +	if (status & DP_PSR_LINK_CRC_ERROR)
> +		DRM_ERROR("PSR LINK CRC ERROR\n");
> +	else if (status & DP_PSR_RFB_STORAGE_ERROR)
> +		DRM_ERROR("PSR RFB STORAGE ERROR\n");
> +	else if (status & DP_PSR_VSC_SDP_UNCORRECTABLE_ERROR)
> +		DRM_ERROR("PSR VSC SDP UNCORRECTABLE ERROR\n");
> +	else
> +		return 0;
> +
> +	return 1;

If it's bool, it should return true/false and not 0/1.

> +}
> +
> +static bool dp_link_psr_capability_changed(struct dp_link_private *link)
> +{
> +	u8 status;
> +
> +	drm_dp_dpcd_read(link->aux, DP_PSR_ESI, &status, 1);
> +
> +	if (status & DP_PSR_CAPS_CHANGE)
> +		drm_dbg_dp(link->drm_dev, "PSR Capability Change\n");
> +	else
> +		return 0;
> +
> +	return 1;

if (!(satus & DP_PSR_CAPS_CHANGE))
     return false;

drm_dbg_dp(..)
return true;


Or

if (status & DP_PSR_CAPS_CHANGE) {
   drm_dbg_dp(...)
   return true;
}

return false;


> +}
> +
>   static u8 get_link_status(const u8 link_status[DP_LINK_STATUS_SIZE], int r)
>   {
>   	return link_status[r - DP_LANE0_1_STATUS];
> @@ -1053,6 +1085,10 @@ int dp_link_process_request(struct dp_link *dp_link)
>   		dp_link->sink_request |= DP_TEST_LINK_TRAINING;
>   	} else if (!dp_link_process_phy_test_pattern_request(link)) {
>   		dp_link->sink_request |= DP_TEST_LINK_PHY_TEST_PATTERN;
> +	} else if (dp_link_read_psr_error_status(link)) {
> +		DRM_ERROR("PSR IRQ_HPD received\n");
> +	} else if (dp_link_psr_capability_changed(link)) {
> +		drm_dbg_dp(link->drm_dev, "PSR Capabiity changed");
>   	} else {
>   		ret = dp_link_process_link_status_update(link);
>   		if (!ret) {
> diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c
> index 5149ceb..8bf8ab4 100644
> --- a/drivers/gpu/drm/msm/dp/dp_panel.c
> +++ b/drivers/gpu/drm/msm/dp/dp_panel.c
> @@ -20,6 +20,27 @@ struct dp_panel_private {
>   	bool aux_cfg_update_done;
>   };
>   
> +static void dp_panel_read_psr_cap(struct dp_panel_private *panel)
> +{
> +	ssize_t rlen;
> +	struct dp_panel *dp_panel;
> +
> +	dp_panel = &panel->dp_panel;
> +
> +	/* edp sink */
> +	if (dp_panel->dpcd[DP_EDP_CONFIGURATION_CAP]) {
> +		rlen = drm_dp_dpcd_read(panel->aux, DP_PSR_SUPPORT,
> +				&dp_panel->psr_cap, sizeof(dp_panel->psr_cap));
> +		if (rlen == sizeof(dp_panel->psr_cap)) {
> +			drm_dbg_dp(panel->drm_dev,
> +				"psr version: 0x%x, psr_cap: 0x%x\n",
> +				dp_panel->psr_cap.version,
> +				dp_panel->psr_cap.capabilities);
> +		} else
> +			DRM_ERROR("failed to read psr info, rlen=%zd\n", rlen);
> +	}
> +}
> +
>   static int dp_panel_read_dpcd(struct dp_panel *dp_panel)
>   {
>   	int rc = 0;
> @@ -106,6 +127,7 @@ static int dp_panel_read_dpcd(struct dp_panel *dp_panel)
>   		}
>   	}
>   
> +	dp_panel_read_psr_cap(panel);
>   end:
>   	return rc;
>   }
> diff --git a/drivers/gpu/drm/msm/dp/dp_panel.h b/drivers/gpu/drm/msm/dp/dp_panel.h
> index d861197a..2d0826a 100644
> --- a/drivers/gpu/drm/msm/dp/dp_panel.h
> +++ b/drivers/gpu/drm/msm/dp/dp_panel.h
> @@ -34,6 +34,11 @@ struct dp_panel_in {
>   	struct dp_catalog *catalog;
>   };
>   
> +struct dp_panel_psr {
> +	u8 version;
> +	u8 capabilities;
> +};
> +
>   struct dp_panel {
>   	/* dpcd raw data */
>   	u8 dpcd[DP_RECEIVER_CAP_SIZE + 1];
> @@ -46,6 +51,7 @@ struct dp_panel {
>   	struct edid *edid;
>   	struct drm_connector *connector;
>   	struct dp_display_mode dp_mode;
> +	struct dp_panel_psr psr_cap;
>   	bool video_test;
>   
>   	u32 vic;
> diff --git a/drivers/gpu/drm/msm/dp/dp_reg.h b/drivers/gpu/drm/msm/dp/dp_reg.h
> index 2686028..ea85a69 100644
> --- a/drivers/gpu/drm/msm/dp/dp_reg.h
> +++ b/drivers/gpu/drm/msm/dp/dp_reg.h
> @@ -22,6 +22,20 @@
>   #define REG_DP_INTR_STATUS2			(0x00000024)
>   #define REG_DP_INTR_STATUS3			(0x00000028)
>   
> +#define REG_DP_INTR_STATUS4			(0x0000002C)
> +#define PSR_UPDATE_INT				(0x00000001)
> +#define PSR_CAPTURE_INT				(0x00000004)
> +#define PSR_EXIT_INT				(0x00000010)
> +#define PSR_UPDATE_ERROR_INT			(0x00000040)
> +#define PSR_WAKE_ERROR_INT			(0x00000100)
> +
> +#define REG_DP_INTR_MASK4			(0x00000030)
> +#define PSR_UPDATE_MASK				(0x00000001)
> +#define PSR_CAPTURE_MASK			(0x00000002)
> +#define PSR_EXIT_MASK				(0x00000004)
> +#define PSR_UPDATE_ERROR_MASK			(0x00000008)
> +#define PSR_WAKE_ERROR_MASK			(0x00000010)
> +
>   #define REG_DP_DP_HPD_CTRL			(0x00000000)
>   #define DP_DP_HPD_CTRL_HPD_EN			(0x00000001)
>   
> @@ -164,6 +178,16 @@
>   #define MMSS_DP_AUDIO_TIMING_RBR_48		(0x00000094)
>   #define MMSS_DP_AUDIO_TIMING_HBR_48		(0x00000098)
>   
> +#define REG_PSR_CONFIG				(0x00000100)
> +#define DISABLE_PSR				(0x00000000)
> +#define PSR1_SUPPORTED				(0x00000001)
> +#define PSR2_WITHOUT_FRAMESYNC			(0x00000002)
> +#define PSR2_WITH_FRAMESYNC			(0x00000003)
> +
> +#define REG_PSR_CMD				(0x00000110)
> +#define PSR_ENTER				(0x00000001)
> +#define PSR_EXIT				(0x00000002)
> +
>   #define MMSS_DP_PSR_CRC_RG			(0x00000154)
>   #define MMSS_DP_PSR_CRC_B			(0x00000158)
>   
> @@ -184,6 +208,9 @@
>   #define MMSS_DP_AUDIO_STREAM_0			(0x00000240)
>   #define MMSS_DP_AUDIO_STREAM_1			(0x00000244)
>   
> +#define MMSS_DP_SDP_CFG3			(0x0000024c)
> +#define UPDATE_SDP				(0x00000001)
> +
>   #define MMSS_DP_EXTENSION_0			(0x00000250)
>   #define MMSS_DP_EXTENSION_1			(0x00000254)
>   #define MMSS_DP_EXTENSION_2			(0x00000258)


-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [v3 2/5] drm/bridge: use atomic enable/disable callbacks for panel bridge functions
  2022-06-21 10:53   ` Vinod Polimera
@ 2022-06-21 18:54     ` Dmitry Baryshkov
  -1 siblings, 0 replies; 32+ messages in thread
From: Dmitry Baryshkov @ 2022-06-21 18:54 UTC (permalink / raw)
  To: Vinod Polimera, dri-devel, linux-arm-msm, freedreno, devicetree
  Cc: linux-kernel, robdclark, dianders, swboyd, quic_kalyant, quic_sbillaka

On 21/06/2022 13:53, Vinod Polimera wrote:
> Use atomic variants for panel bridge callback functions such that
> certain states like self-refresh can be accessed as part of
> enable/disable sequence.
> 
> Signed-off-by: Vinod Polimera <quic_vpolimer@quicinc.com>

Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>

> ---
>   drivers/gpu/drm/bridge/panel.c | 16 ++++++++--------
>   1 file changed, 8 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c
> index 0ee563e..59a3496 100644
> --- a/drivers/gpu/drm/bridge/panel.c
> +++ b/drivers/gpu/drm/bridge/panel.c
> @@ -108,28 +108,28 @@ static void panel_bridge_detach(struct drm_bridge *bridge)
>   		drm_connector_cleanup(connector);
>   }
>   
> -static void panel_bridge_pre_enable(struct drm_bridge *bridge)
> +static void panel_bridge_atomic_pre_enable(struct drm_bridge *bridge)
>   {
>   	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
>   
>   	drm_panel_prepare(panel_bridge->panel);
>   }
>   
> -static void panel_bridge_enable(struct drm_bridge *bridge)
> +static void panel_bridge_atomic_enable(struct drm_bridge *bridge)
>   {
>   	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
>   
>   	drm_panel_enable(panel_bridge->panel);
>   }
>   
> -static void panel_bridge_disable(struct drm_bridge *bridge)
> +static void panel_bridge_atomic_disable(struct drm_bridge *bridge)
>   {
>   	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
>   
>   	drm_panel_disable(panel_bridge->panel);
>   }
>   
> -static void panel_bridge_post_disable(struct drm_bridge *bridge)
> +static void panel_bridge_atomic_post_disable(struct drm_bridge *bridge)
>   {
>   	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
>   
> @@ -158,10 +158,10 @@ static void panel_bridge_debugfs_init(struct drm_bridge *bridge,
>   static const struct drm_bridge_funcs panel_bridge_bridge_funcs = {
>   	.attach = panel_bridge_attach,
>   	.detach = panel_bridge_detach,
> -	.pre_enable = panel_bridge_pre_enable,
> -	.enable = panel_bridge_enable,
> -	.disable = panel_bridge_disable,
> -	.post_disable = panel_bridge_post_disable,
> +	.atomic_pre_enable = panel_bridge_atomic_pre_enable,
> +	.atomic_enable = panel_bridge_atomic_enable,
> +	.atomic_disable = panel_bridge_atomic_disable,
> +	.atomic_post_disable = panel_bridge_atomic_post_disable,
>   	.get_modes = panel_bridge_get_modes,
>   	.atomic_reset = drm_atomic_helper_bridge_reset,
>   	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,


-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [v3 2/5] drm/bridge: use atomic enable/disable callbacks for panel bridge functions
@ 2022-06-21 18:54     ` Dmitry Baryshkov
  0 siblings, 0 replies; 32+ messages in thread
From: Dmitry Baryshkov @ 2022-06-21 18:54 UTC (permalink / raw)
  To: Vinod Polimera, dri-devel, linux-arm-msm, freedreno, devicetree
  Cc: quic_kalyant, quic_sbillaka, dianders, linux-kernel, swboyd

On 21/06/2022 13:53, Vinod Polimera wrote:
> Use atomic variants for panel bridge callback functions such that
> certain states like self-refresh can be accessed as part of
> enable/disable sequence.
> 
> Signed-off-by: Vinod Polimera <quic_vpolimer@quicinc.com>

Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>

> ---
>   drivers/gpu/drm/bridge/panel.c | 16 ++++++++--------
>   1 file changed, 8 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c
> index 0ee563e..59a3496 100644
> --- a/drivers/gpu/drm/bridge/panel.c
> +++ b/drivers/gpu/drm/bridge/panel.c
> @@ -108,28 +108,28 @@ static void panel_bridge_detach(struct drm_bridge *bridge)
>   		drm_connector_cleanup(connector);
>   }
>   
> -static void panel_bridge_pre_enable(struct drm_bridge *bridge)
> +static void panel_bridge_atomic_pre_enable(struct drm_bridge *bridge)
>   {
>   	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
>   
>   	drm_panel_prepare(panel_bridge->panel);
>   }
>   
> -static void panel_bridge_enable(struct drm_bridge *bridge)
> +static void panel_bridge_atomic_enable(struct drm_bridge *bridge)
>   {
>   	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
>   
>   	drm_panel_enable(panel_bridge->panel);
>   }
>   
> -static void panel_bridge_disable(struct drm_bridge *bridge)
> +static void panel_bridge_atomic_disable(struct drm_bridge *bridge)
>   {
>   	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
>   
>   	drm_panel_disable(panel_bridge->panel);
>   }
>   
> -static void panel_bridge_post_disable(struct drm_bridge *bridge)
> +static void panel_bridge_atomic_post_disable(struct drm_bridge *bridge)
>   {
>   	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
>   
> @@ -158,10 +158,10 @@ static void panel_bridge_debugfs_init(struct drm_bridge *bridge,
>   static const struct drm_bridge_funcs panel_bridge_bridge_funcs = {
>   	.attach = panel_bridge_attach,
>   	.detach = panel_bridge_detach,
> -	.pre_enable = panel_bridge_pre_enable,
> -	.enable = panel_bridge_enable,
> -	.disable = panel_bridge_disable,
> -	.post_disable = panel_bridge_post_disable,
> +	.atomic_pre_enable = panel_bridge_atomic_pre_enable,
> +	.atomic_enable = panel_bridge_atomic_enable,
> +	.atomic_disable = panel_bridge_atomic_disable,
> +	.atomic_post_disable = panel_bridge_atomic_post_disable,
>   	.get_modes = panel_bridge_get_modes,
>   	.atomic_reset = drm_atomic_helper_bridge_reset,
>   	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,


-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [v3 2/5] drm/bridge: use atomic enable/disable callbacks for panel bridge functions
  2022-06-21 18:54     ` Dmitry Baryshkov
@ 2022-06-21 18:56       ` Dmitry Baryshkov
  -1 siblings, 0 replies; 32+ messages in thread
From: Dmitry Baryshkov @ 2022-06-21 18:56 UTC (permalink / raw)
  To: Vinod Polimera, dri-devel, linux-arm-msm, freedreno, devicetree
  Cc: linux-kernel, robdclark, dianders, swboyd, quic_kalyant, quic_sbillaka

On 21/06/2022 21:54, Dmitry Baryshkov wrote:
> On 21/06/2022 13:53, Vinod Polimera wrote:
>> Use atomic variants for panel bridge callback functions such that
>> certain states like self-refresh can be accessed as part of
>> enable/disable sequence.
>>
>> Signed-off-by: Vinod Polimera <quic_vpolimer@quicinc.com>
> 
> Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>

Oh, wait. NACK. This will not compile. Please fix compilation with this 
patch.

> 
>> ---
>>   drivers/gpu/drm/bridge/panel.c | 16 ++++++++--------
>>   1 file changed, 8 insertions(+), 8 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/bridge/panel.c 
>> b/drivers/gpu/drm/bridge/panel.c
>> index 0ee563e..59a3496 100644
>> --- a/drivers/gpu/drm/bridge/panel.c
>> +++ b/drivers/gpu/drm/bridge/panel.c
>> @@ -108,28 +108,28 @@ static void panel_bridge_detach(struct 
>> drm_bridge *bridge)
>>           drm_connector_cleanup(connector);
>>   }
>> -static void panel_bridge_pre_enable(struct drm_bridge *bridge)
>> +static void panel_bridge_atomic_pre_enable(struct drm_bridge *bridge)
>>   {
>>       struct panel_bridge *panel_bridge = 
>> drm_bridge_to_panel_bridge(bridge);
>>       drm_panel_prepare(panel_bridge->panel);
>>   }
>> -static void panel_bridge_enable(struct drm_bridge *bridge)
>> +static void panel_bridge_atomic_enable(struct drm_bridge *bridge)
>>   {
>>       struct panel_bridge *panel_bridge = 
>> drm_bridge_to_panel_bridge(bridge);
>>       drm_panel_enable(panel_bridge->panel);
>>   }
>> -static void panel_bridge_disable(struct drm_bridge *bridge)
>> +static void panel_bridge_atomic_disable(struct drm_bridge *bridge)
>>   {
>>       struct panel_bridge *panel_bridge = 
>> drm_bridge_to_panel_bridge(bridge);
>>       drm_panel_disable(panel_bridge->panel);
>>   }
>> -static void panel_bridge_post_disable(struct drm_bridge *bridge)
>> +static void panel_bridge_atomic_post_disable(struct drm_bridge *bridge)
>>   {
>>       struct panel_bridge *panel_bridge = 
>> drm_bridge_to_panel_bridge(bridge);
>> @@ -158,10 +158,10 @@ static void panel_bridge_debugfs_init(struct 
>> drm_bridge *bridge,
>>   static const struct drm_bridge_funcs panel_bridge_bridge_funcs = {
>>       .attach = panel_bridge_attach,
>>       .detach = panel_bridge_detach,
>> -    .pre_enable = panel_bridge_pre_enable,
>> -    .enable = panel_bridge_enable,
>> -    .disable = panel_bridge_disable,
>> -    .post_disable = panel_bridge_post_disable,
>> +    .atomic_pre_enable = panel_bridge_atomic_pre_enable,
>> +    .atomic_enable = panel_bridge_atomic_enable,
>> +    .atomic_disable = panel_bridge_atomic_disable,
>> +    .atomic_post_disable = panel_bridge_atomic_post_disable,
>>       .get_modes = panel_bridge_get_modes,
>>       .atomic_reset = drm_atomic_helper_bridge_reset,
>>       .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
> 
> 


-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [v3 2/5] drm/bridge: use atomic enable/disable callbacks for panel bridge functions
@ 2022-06-21 18:56       ` Dmitry Baryshkov
  0 siblings, 0 replies; 32+ messages in thread
From: Dmitry Baryshkov @ 2022-06-21 18:56 UTC (permalink / raw)
  To: Vinod Polimera, dri-devel, linux-arm-msm, freedreno, devicetree
  Cc: quic_kalyant, quic_sbillaka, dianders, linux-kernel, swboyd

On 21/06/2022 21:54, Dmitry Baryshkov wrote:
> On 21/06/2022 13:53, Vinod Polimera wrote:
>> Use atomic variants for panel bridge callback functions such that
>> certain states like self-refresh can be accessed as part of
>> enable/disable sequence.
>>
>> Signed-off-by: Vinod Polimera <quic_vpolimer@quicinc.com>
> 
> Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>

Oh, wait. NACK. This will not compile. Please fix compilation with this 
patch.

> 
>> ---
>>   drivers/gpu/drm/bridge/panel.c | 16 ++++++++--------
>>   1 file changed, 8 insertions(+), 8 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/bridge/panel.c 
>> b/drivers/gpu/drm/bridge/panel.c
>> index 0ee563e..59a3496 100644
>> --- a/drivers/gpu/drm/bridge/panel.c
>> +++ b/drivers/gpu/drm/bridge/panel.c
>> @@ -108,28 +108,28 @@ static void panel_bridge_detach(struct 
>> drm_bridge *bridge)
>>           drm_connector_cleanup(connector);
>>   }
>> -static void panel_bridge_pre_enable(struct drm_bridge *bridge)
>> +static void panel_bridge_atomic_pre_enable(struct drm_bridge *bridge)
>>   {
>>       struct panel_bridge *panel_bridge = 
>> drm_bridge_to_panel_bridge(bridge);
>>       drm_panel_prepare(panel_bridge->panel);
>>   }
>> -static void panel_bridge_enable(struct drm_bridge *bridge)
>> +static void panel_bridge_atomic_enable(struct drm_bridge *bridge)
>>   {
>>       struct panel_bridge *panel_bridge = 
>> drm_bridge_to_panel_bridge(bridge);
>>       drm_panel_enable(panel_bridge->panel);
>>   }
>> -static void panel_bridge_disable(struct drm_bridge *bridge)
>> +static void panel_bridge_atomic_disable(struct drm_bridge *bridge)
>>   {
>>       struct panel_bridge *panel_bridge = 
>> drm_bridge_to_panel_bridge(bridge);
>>       drm_panel_disable(panel_bridge->panel);
>>   }
>> -static void panel_bridge_post_disable(struct drm_bridge *bridge)
>> +static void panel_bridge_atomic_post_disable(struct drm_bridge *bridge)
>>   {
>>       struct panel_bridge *panel_bridge = 
>> drm_bridge_to_panel_bridge(bridge);
>> @@ -158,10 +158,10 @@ static void panel_bridge_debugfs_init(struct 
>> drm_bridge *bridge,
>>   static const struct drm_bridge_funcs panel_bridge_bridge_funcs = {
>>       .attach = panel_bridge_attach,
>>       .detach = panel_bridge_detach,
>> -    .pre_enable = panel_bridge_pre_enable,
>> -    .enable = panel_bridge_enable,
>> -    .disable = panel_bridge_disable,
>> -    .post_disable = panel_bridge_post_disable,
>> +    .atomic_pre_enable = panel_bridge_atomic_pre_enable,
>> +    .atomic_enable = panel_bridge_atomic_enable,
>> +    .atomic_disable = panel_bridge_atomic_disable,
>> +    .atomic_post_disable = panel_bridge_atomic_post_disable,
>>       .get_modes = panel_bridge_get_modes,
>>       .atomic_reset = drm_atomic_helper_bridge_reset,
>>       .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
> 
> 


-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [v3 3/5] drm/bridge: add psr support during panel bridge enable & disable sequence
  2022-06-21 10:53   ` Vinod Polimera
@ 2022-06-21 18:59     ` Dmitry Baryshkov
  -1 siblings, 0 replies; 32+ messages in thread
From: Dmitry Baryshkov @ 2022-06-21 18:59 UTC (permalink / raw)
  To: Vinod Polimera, y, dri-devel, linux-arm-msm, freedreno, devicetree
  Cc: linux-kernel, robdclark, dianders, swboyd, quic_kalyant, quic_sbillaka

On 21/06/2022 13:53, Vinod Polimera wrote:
> This change avoids panel prepare/unprepare based on self-refresh
> state.
> 
> Signed-off-by: Sankeerth Billakanti <quic_sbillaka@quicinc.com>
> Signed-off-by: Kalyan Thota <quic_kalyant@quicinc.com>
> Signed-off-by: Vinod Polimera <quic_vpolimer@quicinc.com>
> ---
>   drivers/gpu/drm/bridge/panel.c | 102 +++++++++++++++++++++++++++++++++++++++--
>   1 file changed, 98 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c
> index 59a3496..6b09ae0 100644
> --- a/drivers/gpu/drm/bridge/panel.c
> +++ b/drivers/gpu/drm/bridge/panel.c
> @@ -41,6 +41,40 @@ static int panel_bridge_connector_get_modes(struct drm_connector *connector)
>   	return drm_panel_get_modes(panel_bridge->panel, connector);
>   }
>   
> +static struct drm_crtc *bridge_drm_get_old_connector_crtc(struct drm_encoder *encoder,
> +							struct drm_atomic_state *state)
> +{
> +	struct drm_connector *connector;
> +	struct drm_connector_state *conn_state;
> +
> +	connector = drm_atomic_get_old_connector_for_encoder(state, encoder);
> +	if (!connector)
> +		return NULL;
> +
> +	conn_state = drm_atomic_get_old_connector_state(state, connector);
> +	if (!conn_state)
> +		return NULL;
> +
> +	return conn_state->crtc;
> +}
> +
> +static struct drm_crtc *bridge_drm_get_new_connector_crtc(struct drm_encoder *encoder,
> +							struct drm_atomic_state *state)
> +{
> +	struct drm_connector *connector;
> +	struct drm_connector_state *conn_state;
> +
> +	connector = drm_atomic_get_new_connector_for_encoder(state, encoder);
> +	if (!connector)
> +		return NULL;
> +
> +	conn_state = drm_atomic_get_new_connector_state(state, connector);
> +	if (!conn_state)
> +		return NULL;
> +
> +	return conn_state->crtc;
> +}

As I wrote earlier, this should become generic drm helpers.

> +
>   static const struct drm_connector_helper_funcs
>   panel_bridge_connector_helper_funcs = {
>   	.get_modes = panel_bridge_connector_get_modes,
> @@ -108,30 +142,90 @@ static void panel_bridge_detach(struct drm_bridge *bridge)
>   		drm_connector_cleanup(connector);
>   }
>   
> -static void panel_bridge_atomic_pre_enable(struct drm_bridge *bridge)
> +static void panel_bridge_atomic_pre_enable(struct drm_bridge *bridge,
> +					struct drm_bridge_state *old_bridge_state)

This must be a part of the previous patch?

>   {
>   	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
> +	struct drm_atomic_state *old_state = old_bridge_state->base.state;
> +	struct drm_encoder *encoder = bridge->encoder;
> +	struct drm_crtc *crtc;
> +	struct drm_crtc_state *old_crtc_state;
> +
> +	crtc = bridge_drm_get_new_connector_crtc(encoder, old_state);
> +	if (!crtc)
> +		return;

Why? And why do you ask for the new crtc from the old state?

> +
> +	old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc);
> +
> +	/* Don't touch the panel if we're coming back from self refresh state */
> +	if (old_crtc_state && old_crtc_state->self_refresh_active)
> +		return;
>   
>   	drm_panel_prepare(panel_bridge->panel);
>   }
>   
> -static void panel_bridge_atomic_enable(struct drm_bridge *bridge)
> +static void panel_bridge_atomic_enable(struct drm_bridge *bridge,
> +					struct drm_bridge_state *old_bridge_state)
>   {
>   	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
> +	struct drm_atomic_state *old_state = old_bridge_state->base.state;
> +	struct drm_encoder *encoder = bridge->encoder;
> +	struct drm_crtc *crtc;
> +	struct drm_crtc_state *old_crtc_state;
> +
> +	crtc = bridge_drm_get_new_connector_crtc(encoder, old_state);
> +	if (!crtc)
> +		return;
> +
> +	old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc);
> +
> +	/* Don't touch the panel if we're coming back from self refresh state */
> +	if (old_crtc_state && old_crtc_state->self_refresh_active)
> +		return;
>   
>   	drm_panel_enable(panel_bridge->panel);
>   }
>   
> -static void panel_bridge_atomic_disable(struct drm_bridge *bridge)
> +static void panel_bridge_atomic_disable(struct drm_bridge *bridge,
> +					struct drm_bridge_state *old_bridge_state)
>   {
>   	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
> +	struct drm_atomic_state *old_state = old_bridge_state->base.state;
> +	struct drm_encoder *encoder = bridge->encoder;
> +	struct drm_crtc *crtc;
> +	struct drm_crtc_state *new_crtc_state;
> +
> +	crtc = bridge_drm_get_old_connector_crtc(encoder, old_state);
> +	if (!crtc)
> +		return;
> +
> +	new_crtc_state = drm_atomic_get_new_crtc_state(old_state, crtc);

This doesn't sound right too.

> +
> +	/* Don't do a full disable on PSR transitions if new state is self refresh state */
> +	if (new_crtc_state && new_crtc_state->self_refresh_active)
> +		return;
>   
>   	drm_panel_disable(panel_bridge->panel);
>   }
>   
> -static void panel_bridge_atomic_post_disable(struct drm_bridge *bridge)
> +static void panel_bridge_atomic_post_disable(struct drm_bridge *bridge,
> +					struct drm_bridge_state *old_bridge_state)
>   {
>   	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
> +	struct drm_atomic_state *old_state = old_bridge_state->base.state;
> +	struct drm_encoder *encoder = bridge->encoder;
> +	struct drm_crtc *crtc;
> +	struct drm_crtc_state *new_crtc_state;
> +
> +	crtc = bridge_drm_get_old_connector_crtc(encoder, old_state);
> +	if (!crtc)
> +		return;
> +
> +	new_crtc_state = drm_atomic_get_new_crtc_state(old_state, crtc);
> +
> +	/* Don't do unprepare on PSR transitions if new state is self refresh state */
> +	if (new_crtc_state && new_crtc_state->self_refresh_active)
> +		return;
>   
>   	drm_panel_unprepare(panel_bridge->panel);
>   }


-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [v3 3/5] drm/bridge: add psr support during panel bridge enable & disable sequence
@ 2022-06-21 18:59     ` Dmitry Baryshkov
  0 siblings, 0 replies; 32+ messages in thread
From: Dmitry Baryshkov @ 2022-06-21 18:59 UTC (permalink / raw)
  To: Vinod Polimera, y, dri-devel, linux-arm-msm, freedreno, devicetree
  Cc: quic_kalyant, quic_sbillaka, dianders, linux-kernel, swboyd

On 21/06/2022 13:53, Vinod Polimera wrote:
> This change avoids panel prepare/unprepare based on self-refresh
> state.
> 
> Signed-off-by: Sankeerth Billakanti <quic_sbillaka@quicinc.com>
> Signed-off-by: Kalyan Thota <quic_kalyant@quicinc.com>
> Signed-off-by: Vinod Polimera <quic_vpolimer@quicinc.com>
> ---
>   drivers/gpu/drm/bridge/panel.c | 102 +++++++++++++++++++++++++++++++++++++++--
>   1 file changed, 98 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c
> index 59a3496..6b09ae0 100644
> --- a/drivers/gpu/drm/bridge/panel.c
> +++ b/drivers/gpu/drm/bridge/panel.c
> @@ -41,6 +41,40 @@ static int panel_bridge_connector_get_modes(struct drm_connector *connector)
>   	return drm_panel_get_modes(panel_bridge->panel, connector);
>   }
>   
> +static struct drm_crtc *bridge_drm_get_old_connector_crtc(struct drm_encoder *encoder,
> +							struct drm_atomic_state *state)
> +{
> +	struct drm_connector *connector;
> +	struct drm_connector_state *conn_state;
> +
> +	connector = drm_atomic_get_old_connector_for_encoder(state, encoder);
> +	if (!connector)
> +		return NULL;
> +
> +	conn_state = drm_atomic_get_old_connector_state(state, connector);
> +	if (!conn_state)
> +		return NULL;
> +
> +	return conn_state->crtc;
> +}
> +
> +static struct drm_crtc *bridge_drm_get_new_connector_crtc(struct drm_encoder *encoder,
> +							struct drm_atomic_state *state)
> +{
> +	struct drm_connector *connector;
> +	struct drm_connector_state *conn_state;
> +
> +	connector = drm_atomic_get_new_connector_for_encoder(state, encoder);
> +	if (!connector)
> +		return NULL;
> +
> +	conn_state = drm_atomic_get_new_connector_state(state, connector);
> +	if (!conn_state)
> +		return NULL;
> +
> +	return conn_state->crtc;
> +}

As I wrote earlier, this should become generic drm helpers.

> +
>   static const struct drm_connector_helper_funcs
>   panel_bridge_connector_helper_funcs = {
>   	.get_modes = panel_bridge_connector_get_modes,
> @@ -108,30 +142,90 @@ static void panel_bridge_detach(struct drm_bridge *bridge)
>   		drm_connector_cleanup(connector);
>   }
>   
> -static void panel_bridge_atomic_pre_enable(struct drm_bridge *bridge)
> +static void panel_bridge_atomic_pre_enable(struct drm_bridge *bridge,
> +					struct drm_bridge_state *old_bridge_state)

This must be a part of the previous patch?

>   {
>   	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
> +	struct drm_atomic_state *old_state = old_bridge_state->base.state;
> +	struct drm_encoder *encoder = bridge->encoder;
> +	struct drm_crtc *crtc;
> +	struct drm_crtc_state *old_crtc_state;
> +
> +	crtc = bridge_drm_get_new_connector_crtc(encoder, old_state);
> +	if (!crtc)
> +		return;

Why? And why do you ask for the new crtc from the old state?

> +
> +	old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc);
> +
> +	/* Don't touch the panel if we're coming back from self refresh state */
> +	if (old_crtc_state && old_crtc_state->self_refresh_active)
> +		return;
>   
>   	drm_panel_prepare(panel_bridge->panel);
>   }
>   
> -static void panel_bridge_atomic_enable(struct drm_bridge *bridge)
> +static void panel_bridge_atomic_enable(struct drm_bridge *bridge,
> +					struct drm_bridge_state *old_bridge_state)
>   {
>   	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
> +	struct drm_atomic_state *old_state = old_bridge_state->base.state;
> +	struct drm_encoder *encoder = bridge->encoder;
> +	struct drm_crtc *crtc;
> +	struct drm_crtc_state *old_crtc_state;
> +
> +	crtc = bridge_drm_get_new_connector_crtc(encoder, old_state);
> +	if (!crtc)
> +		return;
> +
> +	old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc);
> +
> +	/* Don't touch the panel if we're coming back from self refresh state */
> +	if (old_crtc_state && old_crtc_state->self_refresh_active)
> +		return;
>   
>   	drm_panel_enable(panel_bridge->panel);
>   }
>   
> -static void panel_bridge_atomic_disable(struct drm_bridge *bridge)
> +static void panel_bridge_atomic_disable(struct drm_bridge *bridge,
> +					struct drm_bridge_state *old_bridge_state)
>   {
>   	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
> +	struct drm_atomic_state *old_state = old_bridge_state->base.state;
> +	struct drm_encoder *encoder = bridge->encoder;
> +	struct drm_crtc *crtc;
> +	struct drm_crtc_state *new_crtc_state;
> +
> +	crtc = bridge_drm_get_old_connector_crtc(encoder, old_state);
> +	if (!crtc)
> +		return;
> +
> +	new_crtc_state = drm_atomic_get_new_crtc_state(old_state, crtc);

This doesn't sound right too.

> +
> +	/* Don't do a full disable on PSR transitions if new state is self refresh state */
> +	if (new_crtc_state && new_crtc_state->self_refresh_active)
> +		return;
>   
>   	drm_panel_disable(panel_bridge->panel);
>   }
>   
> -static void panel_bridge_atomic_post_disable(struct drm_bridge *bridge)
> +static void panel_bridge_atomic_post_disable(struct drm_bridge *bridge,
> +					struct drm_bridge_state *old_bridge_state)
>   {
>   	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
> +	struct drm_atomic_state *old_state = old_bridge_state->base.state;
> +	struct drm_encoder *encoder = bridge->encoder;
> +	struct drm_crtc *crtc;
> +	struct drm_crtc_state *new_crtc_state;
> +
> +	crtc = bridge_drm_get_old_connector_crtc(encoder, old_state);
> +	if (!crtc)
> +		return;
> +
> +	new_crtc_state = drm_atomic_get_new_crtc_state(old_state, crtc);
> +
> +	/* Don't do unprepare on PSR transitions if new state is self refresh state */
> +	if (new_crtc_state && new_crtc_state->self_refresh_active)
> +		return;
>   
>   	drm_panel_unprepare(panel_bridge->panel);
>   }


-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [v3 4/5] drm/msm/disp/dpu1: use atomic enable/disable callbacks for encoder functions
  2022-06-21 10:53   ` Vinod Polimera
@ 2022-06-21 19:00     ` Dmitry Baryshkov
  -1 siblings, 0 replies; 32+ messages in thread
From: Dmitry Baryshkov @ 2022-06-21 19:00 UTC (permalink / raw)
  To: Vinod Polimera, y, dri-devel, linux-arm-msm, freedreno, devicetree
  Cc: linux-kernel, robdclark, dianders, swboyd, quic_kalyant, quic_sbillaka

On 21/06/2022 13:53, Vinod Polimera wrote:
> Use atomic variants for encoder callback functions such that
> certain states like self-refresh can be accessed as part of
> enable/disable sequence.
> 
> Signed-off-by: Kalyan Thota <quic_kalyant@quicinc.com>
> Signed-off-by: Vinod Polimera <quic_vpolimer@quicinc.com>

Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>


> ---
>   drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 10 ++++++----
>   1 file changed, 6 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> index 52516eb..cc2809b 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> @@ -1130,7 +1130,8 @@ void dpu_encoder_virt_runtime_resume(struct drm_encoder *drm_enc)
>   	mutex_unlock(&dpu_enc->enc_lock);
>   }
>   
> -static void dpu_encoder_virt_enable(struct drm_encoder *drm_enc)
> +static void dpu_encoder_virt_atomic_enable(struct drm_encoder *drm_enc,
> +					struct drm_atomic_state *state)
>   {
>   	struct dpu_encoder_virt *dpu_enc = NULL;
>   	int ret = 0;
> @@ -1166,7 +1167,8 @@ static void dpu_encoder_virt_enable(struct drm_encoder *drm_enc)
>   	mutex_unlock(&dpu_enc->enc_lock);
>   }
>   
> -static void dpu_encoder_virt_disable(struct drm_encoder *drm_enc)
> +static void dpu_encoder_virt_atomic_disable(struct drm_encoder *drm_enc,
> +					struct drm_atomic_state *state)
>   {
>   	struct dpu_encoder_virt *dpu_enc = NULL;
>   	int i = 0;
> @@ -2332,8 +2334,8 @@ static void dpu_encoder_frame_done_timeout(struct timer_list *t)
>   
>   static const struct drm_encoder_helper_funcs dpu_encoder_helper_funcs = {
>   	.atomic_mode_set = dpu_encoder_virt_atomic_mode_set,
> -	.disable = dpu_encoder_virt_disable,
> -	.enable = dpu_encoder_virt_enable,
> +	.atomic_disable = dpu_encoder_virt_atomic_disable,
> +	.atomic_enable = dpu_encoder_virt_atomic_enable,
>   	.atomic_check = dpu_encoder_virt_atomic_check,
>   };
>   


-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [v3 4/5] drm/msm/disp/dpu1: use atomic enable/disable callbacks for encoder functions
@ 2022-06-21 19:00     ` Dmitry Baryshkov
  0 siblings, 0 replies; 32+ messages in thread
From: Dmitry Baryshkov @ 2022-06-21 19:00 UTC (permalink / raw)
  To: Vinod Polimera, y, dri-devel, linux-arm-msm, freedreno, devicetree
  Cc: quic_kalyant, quic_sbillaka, dianders, linux-kernel, swboyd

On 21/06/2022 13:53, Vinod Polimera wrote:
> Use atomic variants for encoder callback functions such that
> certain states like self-refresh can be accessed as part of
> enable/disable sequence.
> 
> Signed-off-by: Kalyan Thota <quic_kalyant@quicinc.com>
> Signed-off-by: Vinod Polimera <quic_vpolimer@quicinc.com>

Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>


> ---
>   drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 10 ++++++----
>   1 file changed, 6 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> index 52516eb..cc2809b 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> @@ -1130,7 +1130,8 @@ void dpu_encoder_virt_runtime_resume(struct drm_encoder *drm_enc)
>   	mutex_unlock(&dpu_enc->enc_lock);
>   }
>   
> -static void dpu_encoder_virt_enable(struct drm_encoder *drm_enc)
> +static void dpu_encoder_virt_atomic_enable(struct drm_encoder *drm_enc,
> +					struct drm_atomic_state *state)
>   {
>   	struct dpu_encoder_virt *dpu_enc = NULL;
>   	int ret = 0;
> @@ -1166,7 +1167,8 @@ static void dpu_encoder_virt_enable(struct drm_encoder *drm_enc)
>   	mutex_unlock(&dpu_enc->enc_lock);
>   }
>   
> -static void dpu_encoder_virt_disable(struct drm_encoder *drm_enc)
> +static void dpu_encoder_virt_atomic_disable(struct drm_encoder *drm_enc,
> +					struct drm_atomic_state *state)
>   {
>   	struct dpu_encoder_virt *dpu_enc = NULL;
>   	int i = 0;
> @@ -2332,8 +2334,8 @@ static void dpu_encoder_frame_done_timeout(struct timer_list *t)
>   
>   static const struct drm_encoder_helper_funcs dpu_encoder_helper_funcs = {
>   	.atomic_mode_set = dpu_encoder_virt_atomic_mode_set,
> -	.disable = dpu_encoder_virt_disable,
> -	.enable = dpu_encoder_virt_enable,
> +	.atomic_disable = dpu_encoder_virt_atomic_disable,
> +	.atomic_enable = dpu_encoder_virt_atomic_enable,
>   	.atomic_check = dpu_encoder_virt_atomic_check,
>   };
>   


-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [v3 5/5] drm/msm/disp/dpu1: add PSR support for eDP interface in dpu driver
  2022-06-21 10:53   ` Vinod Polimera
@ 2022-06-21 19:09     ` Dmitry Baryshkov
  -1 siblings, 0 replies; 32+ messages in thread
From: Dmitry Baryshkov @ 2022-06-21 19:09 UTC (permalink / raw)
  To: Vinod Polimera, y, dri-devel, linux-arm-msm, freedreno, devicetree
  Cc: linux-kernel, robdclark, dianders, swboyd, quic_kalyant, quic_sbillaka

On 21/06/2022 13:53, Vinod Polimera wrote:
> Enable PSR on eDP interface using drm self-refresh librabry.
> This patch uses a trigger from self-refresh library to enter/exit
> into PSR, when there are no updates from framework.
> 
> Signed-off-by: Kalyan Thota <quic_kalyant@quicinc.com>
> Signed-off-by: Vinod Polimera <quic_vpolimer@quicinc.com>
> ---
>   drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c    | 36 ++++++++++++++++++++++++-----
>   drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 20 +++++++++++++++-
>   drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c     |  2 +-
>   3 files changed, 50 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
> index b56f777..c6e4f03 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
> @@ -18,6 +18,7 @@
>   #include <drm/drm_probe_helper.h>
>   #include <drm/drm_rect.h>
>   #include <drm/drm_vblank.h>
> +#include <drm/drm_self_refresh_helper.h>
>   
>   #include "dpu_kms.h"
>   #include "dpu_hw_lm.h"
> @@ -955,24 +956,39 @@ static void dpu_crtc_disable(struct drm_crtc *crtc,
>   									      crtc);
>   	struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc);
>   	struct dpu_crtc_state *cstate = to_dpu_crtc_state(crtc->state);
> -	struct drm_encoder *encoder;
> +	struct drm_encoder *encoder = NULL;
>   	unsigned long flags;
>   	bool release_bandwidth = false;
>   
>   	DRM_DEBUG_KMS("crtc%d\n", crtc->base.id);
>   
> +	if (old_crtc_state->self_refresh_active) {
> +		drm_for_each_encoder_mask(encoder, crtc->dev,
> +					old_crtc_state->encoder_mask) {
> +			dpu_encoder_assign_crtc(encoder, NULL);

I think we should drop dpu_encoder_assign_crtc completely and use your 
new helpers instead. Having to manually duplicate existing link sounds 
like a bad idea.

> +		}
> +		return;
> +	}
> +
>   	/* Disable/save vblank irq handling */
>   	drm_crtc_vblank_off(crtc);
>   
>   	drm_for_each_encoder_mask(encoder, crtc->dev,
>   				  old_crtc_state->encoder_mask) {
> -		/* in video mode, we hold an extra bandwidth reference
> +		/*
> +		 * in video mode, we hold an extra bandwidth reference

Unrelated to this patch.

>   		 * as we cannot drop bandwidth at frame-done if any
>   		 * crtc is being used in video mode.
>   		 */
>   		if (dpu_encoder_get_intf_mode(encoder) == INTF_MODE_VIDEO)
>   			release_bandwidth = true;
> -		dpu_encoder_assign_crtc(encoder, NULL);
> +		/*
> +		 * If disable is triggered during psr active(e.g: screen dim in PSR),
> +		 * we will need encoder->crtc connection to process the device sleep &
> +		 * preserve it during psr sequence.
> +		 */
> +		if (!crtc->state->self_refresh_active)
> +			dpu_encoder_assign_crtc(encoder, NULL);
>   	}
>   
>   	/* wait for frame_event_done completion */
> @@ -1020,7 +1036,9 @@ static void dpu_crtc_enable(struct drm_crtc *crtc,
>   	struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc);
>   	struct drm_encoder *encoder;
>   	bool request_bandwidth = false;
> +	struct drm_crtc_state *old_crtc_state;
>   
> +	old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc);
>   	pm_runtime_get_sync(crtc->dev->dev);
>   
>   	DRM_DEBUG_KMS("crtc%d\n", crtc->base.id);
> @@ -1042,8 +1060,9 @@ static void dpu_crtc_enable(struct drm_crtc *crtc,
>   	trace_dpu_crtc_enable(DRMID(crtc), true, dpu_crtc);
>   	dpu_crtc->enabled = true;
>   
> -	drm_for_each_encoder_mask(encoder, crtc->dev, crtc->state->encoder_mask)
> -		dpu_encoder_assign_crtc(encoder, crtc);
> +	if (!old_crtc_state->self_refresh_active)
> +		drm_for_each_encoder_mask(encoder, crtc->dev, crtc->state->encoder_mask)
> +			dpu_encoder_assign_crtc(encoder, crtc);
>   
>   	/* Enable/restore vblank irq handling */
>   	drm_crtc_vblank_on(crtc);
> @@ -1525,7 +1544,7 @@ struct drm_crtc *dpu_crtc_init(struct drm_device *dev, struct drm_plane *plane,
>   {
>   	struct drm_crtc *crtc = NULL;
>   	struct dpu_crtc *dpu_crtc = NULL;
> -	int i;
> +	int i, ret;
>   
>   	dpu_crtc = kzalloc(sizeof(*dpu_crtc), GFP_KERNEL);
>   	if (!dpu_crtc)
> @@ -1562,6 +1581,11 @@ struct drm_crtc *dpu_crtc_init(struct drm_device *dev, struct drm_plane *plane,
>   	/* initialize event handling */
>   	spin_lock_init(&dpu_crtc->event_lock);
>   
> +	ret = drm_self_refresh_helper_init(crtc);
> +	if (ret)
> +		DPU_ERROR("Failed to initialize %s with self-refresh helpers %d\n",
> +			crtc->name, ret);
> +
>   	DRM_DEBUG_KMS("%s: successfully initialized crtc\n", dpu_crtc->name);
>   	return crtc;
>   }
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> index cc2809b..234e95d 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> @@ -225,6 +225,11 @@ bool dpu_encoder_is_widebus_enabled(const struct drm_encoder *drm_enc)
>   	return dpu_enc->wide_bus_en;
>   }
>   
> +static inline bool is_self_refresh_active(const struct drm_crtc_state *state)
> +{
> +	return (state && state->self_refresh_active);
> +}
> +
>   static void _dpu_encoder_setup_dither(struct dpu_hw_pingpong *hw_pp, unsigned bpc)
>   {
>   	struct dpu_hw_dither_cfg dither_cfg = { 0 };
> @@ -592,7 +597,8 @@ static int dpu_encoder_virt_atomic_check(
>   		if (drm_atomic_crtc_needs_modeset(crtc_state)) {
>   			dpu_rm_release(global_state, drm_enc);
>   
> -			if (!crtc_state->active_changed || crtc_state->active)
> +			if (!crtc_state->active_changed || crtc_state->active ||
> +					crtc_state->self_refresh_active)

This condition should be changed to use enabled rather than active. 
Quoting KMS documentation: 'The driver must not release any shared 
resources if active is set to false but enable still true...'

>   				ret = dpu_rm_reserve(&dpu_kms->rm, global_state,
>   						drm_enc, crtc_state, topology);
>   		}
> @@ -1171,11 +1177,23 @@ static void dpu_encoder_virt_atomic_disable(struct drm_encoder *drm_enc,
>   					struct drm_atomic_state *state)
>   {
>   	struct dpu_encoder_virt *dpu_enc = NULL;
> +	struct drm_crtc *crtc;
> +	struct drm_crtc_state *old_state;
>   	int i = 0;
>   
>   	dpu_enc = to_dpu_encoder_virt(drm_enc);
>   	DPU_DEBUG_ENC(dpu_enc, "\n");
>   
> +	crtc = dpu_enc->crtc;
> +	old_state = drm_atomic_get_old_crtc_state(state, crtc);
> +
> +	/*
> +	 * The encoder disabled already occurred when self refresh mode
> +	 * was set earlier, in the old_state for the corresponding crtc.
> +	 */
> +	if (drm_enc->encoder_type == DRM_MODE_ENCODER_TMDS && is_self_refresh_active(old_state))
> +		return;
> +

Why do you need to check the encoder_type?

>   	mutex_lock(&dpu_enc->enc_lock);
>   	dpu_enc->enabled = false;
>   
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
> index bce4764..cc0a674 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
> @@ -507,7 +507,7 @@ static void dpu_kms_wait_for_commit_done(struct msm_kms *kms,
>   		return;
>   	}
>   
> -	if (!crtc->state->active) {
> +	if (!drm_atomic_crtc_effectively_active(crtc->state)) {
>   		DPU_DEBUG("[crtc:%d] not active\n", crtc->base.id);
>   		return;
>   	}


-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [v3 5/5] drm/msm/disp/dpu1: add PSR support for eDP interface in dpu driver
@ 2022-06-21 19:09     ` Dmitry Baryshkov
  0 siblings, 0 replies; 32+ messages in thread
From: Dmitry Baryshkov @ 2022-06-21 19:09 UTC (permalink / raw)
  To: Vinod Polimera, y, dri-devel, linux-arm-msm, freedreno, devicetree
  Cc: quic_kalyant, quic_sbillaka, dianders, linux-kernel, swboyd

On 21/06/2022 13:53, Vinod Polimera wrote:
> Enable PSR on eDP interface using drm self-refresh librabry.
> This patch uses a trigger from self-refresh library to enter/exit
> into PSR, when there are no updates from framework.
> 
> Signed-off-by: Kalyan Thota <quic_kalyant@quicinc.com>
> Signed-off-by: Vinod Polimera <quic_vpolimer@quicinc.com>
> ---
>   drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c    | 36 ++++++++++++++++++++++++-----
>   drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 20 +++++++++++++++-
>   drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c     |  2 +-
>   3 files changed, 50 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
> index b56f777..c6e4f03 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
> @@ -18,6 +18,7 @@
>   #include <drm/drm_probe_helper.h>
>   #include <drm/drm_rect.h>
>   #include <drm/drm_vblank.h>
> +#include <drm/drm_self_refresh_helper.h>
>   
>   #include "dpu_kms.h"
>   #include "dpu_hw_lm.h"
> @@ -955,24 +956,39 @@ static void dpu_crtc_disable(struct drm_crtc *crtc,
>   									      crtc);
>   	struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc);
>   	struct dpu_crtc_state *cstate = to_dpu_crtc_state(crtc->state);
> -	struct drm_encoder *encoder;
> +	struct drm_encoder *encoder = NULL;
>   	unsigned long flags;
>   	bool release_bandwidth = false;
>   
>   	DRM_DEBUG_KMS("crtc%d\n", crtc->base.id);
>   
> +	if (old_crtc_state->self_refresh_active) {
> +		drm_for_each_encoder_mask(encoder, crtc->dev,
> +					old_crtc_state->encoder_mask) {
> +			dpu_encoder_assign_crtc(encoder, NULL);

I think we should drop dpu_encoder_assign_crtc completely and use your 
new helpers instead. Having to manually duplicate existing link sounds 
like a bad idea.

> +		}
> +		return;
> +	}
> +
>   	/* Disable/save vblank irq handling */
>   	drm_crtc_vblank_off(crtc);
>   
>   	drm_for_each_encoder_mask(encoder, crtc->dev,
>   				  old_crtc_state->encoder_mask) {
> -		/* in video mode, we hold an extra bandwidth reference
> +		/*
> +		 * in video mode, we hold an extra bandwidth reference

Unrelated to this patch.

>   		 * as we cannot drop bandwidth at frame-done if any
>   		 * crtc is being used in video mode.
>   		 */
>   		if (dpu_encoder_get_intf_mode(encoder) == INTF_MODE_VIDEO)
>   			release_bandwidth = true;
> -		dpu_encoder_assign_crtc(encoder, NULL);
> +		/*
> +		 * If disable is triggered during psr active(e.g: screen dim in PSR),
> +		 * we will need encoder->crtc connection to process the device sleep &
> +		 * preserve it during psr sequence.
> +		 */
> +		if (!crtc->state->self_refresh_active)
> +			dpu_encoder_assign_crtc(encoder, NULL);
>   	}
>   
>   	/* wait for frame_event_done completion */
> @@ -1020,7 +1036,9 @@ static void dpu_crtc_enable(struct drm_crtc *crtc,
>   	struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc);
>   	struct drm_encoder *encoder;
>   	bool request_bandwidth = false;
> +	struct drm_crtc_state *old_crtc_state;
>   
> +	old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc);
>   	pm_runtime_get_sync(crtc->dev->dev);
>   
>   	DRM_DEBUG_KMS("crtc%d\n", crtc->base.id);
> @@ -1042,8 +1060,9 @@ static void dpu_crtc_enable(struct drm_crtc *crtc,
>   	trace_dpu_crtc_enable(DRMID(crtc), true, dpu_crtc);
>   	dpu_crtc->enabled = true;
>   
> -	drm_for_each_encoder_mask(encoder, crtc->dev, crtc->state->encoder_mask)
> -		dpu_encoder_assign_crtc(encoder, crtc);
> +	if (!old_crtc_state->self_refresh_active)
> +		drm_for_each_encoder_mask(encoder, crtc->dev, crtc->state->encoder_mask)
> +			dpu_encoder_assign_crtc(encoder, crtc);
>   
>   	/* Enable/restore vblank irq handling */
>   	drm_crtc_vblank_on(crtc);
> @@ -1525,7 +1544,7 @@ struct drm_crtc *dpu_crtc_init(struct drm_device *dev, struct drm_plane *plane,
>   {
>   	struct drm_crtc *crtc = NULL;
>   	struct dpu_crtc *dpu_crtc = NULL;
> -	int i;
> +	int i, ret;
>   
>   	dpu_crtc = kzalloc(sizeof(*dpu_crtc), GFP_KERNEL);
>   	if (!dpu_crtc)
> @@ -1562,6 +1581,11 @@ struct drm_crtc *dpu_crtc_init(struct drm_device *dev, struct drm_plane *plane,
>   	/* initialize event handling */
>   	spin_lock_init(&dpu_crtc->event_lock);
>   
> +	ret = drm_self_refresh_helper_init(crtc);
> +	if (ret)
> +		DPU_ERROR("Failed to initialize %s with self-refresh helpers %d\n",
> +			crtc->name, ret);
> +
>   	DRM_DEBUG_KMS("%s: successfully initialized crtc\n", dpu_crtc->name);
>   	return crtc;
>   }
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> index cc2809b..234e95d 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> @@ -225,6 +225,11 @@ bool dpu_encoder_is_widebus_enabled(const struct drm_encoder *drm_enc)
>   	return dpu_enc->wide_bus_en;
>   }
>   
> +static inline bool is_self_refresh_active(const struct drm_crtc_state *state)
> +{
> +	return (state && state->self_refresh_active);
> +}
> +
>   static void _dpu_encoder_setup_dither(struct dpu_hw_pingpong *hw_pp, unsigned bpc)
>   {
>   	struct dpu_hw_dither_cfg dither_cfg = { 0 };
> @@ -592,7 +597,8 @@ static int dpu_encoder_virt_atomic_check(
>   		if (drm_atomic_crtc_needs_modeset(crtc_state)) {
>   			dpu_rm_release(global_state, drm_enc);
>   
> -			if (!crtc_state->active_changed || crtc_state->active)
> +			if (!crtc_state->active_changed || crtc_state->active ||
> +					crtc_state->self_refresh_active)

This condition should be changed to use enabled rather than active. 
Quoting KMS documentation: 'The driver must not release any shared 
resources if active is set to false but enable still true...'

>   				ret = dpu_rm_reserve(&dpu_kms->rm, global_state,
>   						drm_enc, crtc_state, topology);
>   		}
> @@ -1171,11 +1177,23 @@ static void dpu_encoder_virt_atomic_disable(struct drm_encoder *drm_enc,
>   					struct drm_atomic_state *state)
>   {
>   	struct dpu_encoder_virt *dpu_enc = NULL;
> +	struct drm_crtc *crtc;
> +	struct drm_crtc_state *old_state;
>   	int i = 0;
>   
>   	dpu_enc = to_dpu_encoder_virt(drm_enc);
>   	DPU_DEBUG_ENC(dpu_enc, "\n");
>   
> +	crtc = dpu_enc->crtc;
> +	old_state = drm_atomic_get_old_crtc_state(state, crtc);
> +
> +	/*
> +	 * The encoder disabled already occurred when self refresh mode
> +	 * was set earlier, in the old_state for the corresponding crtc.
> +	 */
> +	if (drm_enc->encoder_type == DRM_MODE_ENCODER_TMDS && is_self_refresh_active(old_state))
> +		return;
> +

Why do you need to check the encoder_type?

>   	mutex_lock(&dpu_enc->enc_lock);
>   	dpu_enc->enabled = false;
>   
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
> index bce4764..cc0a674 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
> @@ -507,7 +507,7 @@ static void dpu_kms_wait_for_commit_done(struct msm_kms *kms,
>   		return;
>   	}
>   
> -	if (!crtc->state->active) {
> +	if (!drm_atomic_crtc_effectively_active(crtc->state)) {
>   		DPU_DEBUG("[crtc:%d] not active\n", crtc->base.id);
>   		return;
>   	}


-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [v3 5/5] drm/msm/disp/dpu1: add PSR support for eDP interface in dpu driver
  2022-06-21 10:53   ` Vinod Polimera
@ 2022-06-22 21:21     ` kernel test robot
  -1 siblings, 0 replies; 32+ messages in thread
From: kernel test robot @ 2022-06-22 21:21 UTC (permalink / raw)
  To: Vinod Polimera, y, dri-devel, linux-arm-msm, freedreno, devicetree
  Cc: llvm, kbuild-all, Vinod Polimera, linux-kernel, robdclark,
	dianders, swboyd, quic_kalyant, dmitry.baryshkov, quic_sbillaka

Hi Vinod,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on drm/drm-next]
[also build test WARNING on drm-exynos/exynos-drm-next drm-tip/drm-tip tegra-drm/drm/tegra/for-next linus/master v5.19-rc3 next-20220622]
[cannot apply to drm-intel/for-linux-next airlied/drm-next]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/intel-lab-lkp/linux/commits/Vinod-Polimera/drm-msm-dp-Add-basic-PSR-support-for-eDP/20220621-195406
base:   git://anongit.freedesktop.org/drm/drm drm-next
config: arm64-randconfig-r025-20220622 (https://download.01.org/0day-ci/archive/20220623/202206230551.H0oXeV2E-lkp@intel.com/config)
compiler: clang version 15.0.0 (https://github.com/llvm/llvm-project 8b8d126598ce7bd5243da7f94f69fa1104288bee)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # install arm64 cross compiling tool for clang build
        # apt-get install binutils-aarch64-linux-gnu
        # https://github.com/intel-lab-lkp/linux/commit/2c3c31343481a4faf2402cf513c85fb7d75ce205
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Vinod-Polimera/drm-msm-dp-Add-basic-PSR-support-for-eDP/20220621-195406
        git checkout 2c3c31343481a4faf2402cf513c85fb7d75ce205
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=arm64 SHELL=/bin/bash

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c:1064:3: warning: add explicit braces to avoid dangling else [-Wdangling-else]
                   drm_for_each_encoder_mask(encoder, crtc->dev, crtc->state->encoder_mask)
                   ^
   include/drm/drm_encoder.h:314:3: note: expanded from macro 'drm_for_each_encoder_mask'
                   for_each_if ((encoder_mask) & drm_encoder_mask(encoder))
                   ^
   include/drm/drm_util.h:63:53: note: expanded from macro 'for_each_if'
   #define for_each_if(condition) if (!(condition)) {} else
                                                       ^
   1 warning generated.


vim +1064 drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c

25fdd5933e4c0f Jeykumar Sankaran 2018-06-27  1032  
25fdd5933e4c0f Jeykumar Sankaran 2018-06-27  1033  static void dpu_crtc_enable(struct drm_crtc *crtc,
351f950db4ab28 Maxime Ripard     2020-10-08  1034  		struct drm_atomic_state *state)
25fdd5933e4c0f Jeykumar Sankaran 2018-06-27  1035  {
e12e5263bf1d8d Rob Clark         2020-09-07  1036  	struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc);
25fdd5933e4c0f Jeykumar Sankaran 2018-06-27  1037  	struct drm_encoder *encoder;
35c719da95c0d2 Rob Clark         2020-08-11  1038  	bool request_bandwidth = false;
2c3c31343481a4 Vinod Polimera    2022-06-21  1039  	struct drm_crtc_state *old_crtc_state;
25fdd5933e4c0f Jeykumar Sankaran 2018-06-27  1040  
2c3c31343481a4 Vinod Polimera    2022-06-21  1041  	old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc);
b77d0f0d4ee757 Sean Paul         2018-11-16  1042  	pm_runtime_get_sync(crtc->dev->dev);
b77d0f0d4ee757 Sean Paul         2018-11-16  1043  
25fdd5933e4c0f Jeykumar Sankaran 2018-06-27  1044  	DRM_DEBUG_KMS("crtc%d\n", crtc->base.id);
25fdd5933e4c0f Jeykumar Sankaran 2018-06-27  1045  
241b507c166fef Rob Clark         2019-08-20  1046  	drm_for_each_encoder_mask(encoder, crtc->dev, crtc->state->encoder_mask) {
241b507c166fef Rob Clark         2019-08-20  1047  		/* in video mode, we hold an extra bandwidth reference
241b507c166fef Rob Clark         2019-08-20  1048  		 * as we cannot drop bandwidth at frame-done if any
241b507c166fef Rob Clark         2019-08-20  1049  		 * crtc is being used in video mode.
241b507c166fef Rob Clark         2019-08-20  1050  		 */
241b507c166fef Rob Clark         2019-08-20  1051  		if (dpu_encoder_get_intf_mode(encoder) == INTF_MODE_VIDEO)
241b507c166fef Rob Clark         2019-08-20  1052  			request_bandwidth = true;
25fdd5933e4c0f Jeykumar Sankaran 2018-06-27  1053  		dpu_encoder_register_frame_event_callback(encoder,
25fdd5933e4c0f Jeykumar Sankaran 2018-06-27  1054  				dpu_crtc_frame_event_cb, (void *)crtc);
241b507c166fef Rob Clark         2019-08-20  1055  	}
241b507c166fef Rob Clark         2019-08-20  1056  
241b507c166fef Rob Clark         2019-08-20  1057  	if (request_bandwidth)
241b507c166fef Rob Clark         2019-08-20  1058  		atomic_inc(&_dpu_crtc_get_kms(crtc)->bandwidth_ref);
25fdd5933e4c0f Jeykumar Sankaran 2018-06-27  1059  
25fdd5933e4c0f Jeykumar Sankaran 2018-06-27  1060  	trace_dpu_crtc_enable(DRMID(crtc), true, dpu_crtc);
25fdd5933e4c0f Jeykumar Sankaran 2018-06-27  1061  	dpu_crtc->enabled = true;
25fdd5933e4c0f Jeykumar Sankaran 2018-06-27  1062  
2c3c31343481a4 Vinod Polimera    2022-06-21  1063  	if (!old_crtc_state->self_refresh_active)
a796ba2cb3dde3 Sean Paul         2018-11-16 @1064  		drm_for_each_encoder_mask(encoder, crtc->dev, crtc->state->encoder_mask)
a796ba2cb3dde3 Sean Paul         2018-11-16  1065  			dpu_encoder_assign_crtc(encoder, crtc);
25fdd5933e4c0f Jeykumar Sankaran 2018-06-27  1066  
2f2eb723b50b4d Rajesh Yadav      2018-07-13  1067  	/* Enable/restore vblank irq handling */
2f2eb723b50b4d Rajesh Yadav      2018-07-13  1068  	drm_crtc_vblank_on(crtc);
25fdd5933e4c0f Jeykumar Sankaran 2018-06-27  1069  }
25fdd5933e4c0f Jeykumar Sankaran 2018-06-27  1070  

-- 
0-DAY CI Kernel Test Service
https://01.org/lkp

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [v3 5/5] drm/msm/disp/dpu1: add PSR support for eDP interface in dpu driver
@ 2022-06-22 21:21     ` kernel test robot
  0 siblings, 0 replies; 32+ messages in thread
From: kernel test robot @ 2022-06-22 21:21 UTC (permalink / raw)
  To: Vinod Polimera, y, dri-devel, linux-arm-msm, freedreno, devicetree
  Cc: quic_kalyant, quic_sbillaka, kbuild-all, swboyd, llvm, dianders,
	linux-kernel, dmitry.baryshkov, Vinod Polimera

Hi Vinod,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on drm/drm-next]
[also build test WARNING on drm-exynos/exynos-drm-next drm-tip/drm-tip tegra-drm/drm/tegra/for-next linus/master v5.19-rc3 next-20220622]
[cannot apply to drm-intel/for-linux-next airlied/drm-next]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/intel-lab-lkp/linux/commits/Vinod-Polimera/drm-msm-dp-Add-basic-PSR-support-for-eDP/20220621-195406
base:   git://anongit.freedesktop.org/drm/drm drm-next
config: arm64-randconfig-r025-20220622 (https://download.01.org/0day-ci/archive/20220623/202206230551.H0oXeV2E-lkp@intel.com/config)
compiler: clang version 15.0.0 (https://github.com/llvm/llvm-project 8b8d126598ce7bd5243da7f94f69fa1104288bee)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # install arm64 cross compiling tool for clang build
        # apt-get install binutils-aarch64-linux-gnu
        # https://github.com/intel-lab-lkp/linux/commit/2c3c31343481a4faf2402cf513c85fb7d75ce205
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Vinod-Polimera/drm-msm-dp-Add-basic-PSR-support-for-eDP/20220621-195406
        git checkout 2c3c31343481a4faf2402cf513c85fb7d75ce205
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=arm64 SHELL=/bin/bash

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c:1064:3: warning: add explicit braces to avoid dangling else [-Wdangling-else]
                   drm_for_each_encoder_mask(encoder, crtc->dev, crtc->state->encoder_mask)
                   ^
   include/drm/drm_encoder.h:314:3: note: expanded from macro 'drm_for_each_encoder_mask'
                   for_each_if ((encoder_mask) & drm_encoder_mask(encoder))
                   ^
   include/drm/drm_util.h:63:53: note: expanded from macro 'for_each_if'
   #define for_each_if(condition) if (!(condition)) {} else
                                                       ^
   1 warning generated.


vim +1064 drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c

25fdd5933e4c0f Jeykumar Sankaran 2018-06-27  1032  
25fdd5933e4c0f Jeykumar Sankaran 2018-06-27  1033  static void dpu_crtc_enable(struct drm_crtc *crtc,
351f950db4ab28 Maxime Ripard     2020-10-08  1034  		struct drm_atomic_state *state)
25fdd5933e4c0f Jeykumar Sankaran 2018-06-27  1035  {
e12e5263bf1d8d Rob Clark         2020-09-07  1036  	struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc);
25fdd5933e4c0f Jeykumar Sankaran 2018-06-27  1037  	struct drm_encoder *encoder;
35c719da95c0d2 Rob Clark         2020-08-11  1038  	bool request_bandwidth = false;
2c3c31343481a4 Vinod Polimera    2022-06-21  1039  	struct drm_crtc_state *old_crtc_state;
25fdd5933e4c0f Jeykumar Sankaran 2018-06-27  1040  
2c3c31343481a4 Vinod Polimera    2022-06-21  1041  	old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc);
b77d0f0d4ee757 Sean Paul         2018-11-16  1042  	pm_runtime_get_sync(crtc->dev->dev);
b77d0f0d4ee757 Sean Paul         2018-11-16  1043  
25fdd5933e4c0f Jeykumar Sankaran 2018-06-27  1044  	DRM_DEBUG_KMS("crtc%d\n", crtc->base.id);
25fdd5933e4c0f Jeykumar Sankaran 2018-06-27  1045  
241b507c166fef Rob Clark         2019-08-20  1046  	drm_for_each_encoder_mask(encoder, crtc->dev, crtc->state->encoder_mask) {
241b507c166fef Rob Clark         2019-08-20  1047  		/* in video mode, we hold an extra bandwidth reference
241b507c166fef Rob Clark         2019-08-20  1048  		 * as we cannot drop bandwidth at frame-done if any
241b507c166fef Rob Clark         2019-08-20  1049  		 * crtc is being used in video mode.
241b507c166fef Rob Clark         2019-08-20  1050  		 */
241b507c166fef Rob Clark         2019-08-20  1051  		if (dpu_encoder_get_intf_mode(encoder) == INTF_MODE_VIDEO)
241b507c166fef Rob Clark         2019-08-20  1052  			request_bandwidth = true;
25fdd5933e4c0f Jeykumar Sankaran 2018-06-27  1053  		dpu_encoder_register_frame_event_callback(encoder,
25fdd5933e4c0f Jeykumar Sankaran 2018-06-27  1054  				dpu_crtc_frame_event_cb, (void *)crtc);
241b507c166fef Rob Clark         2019-08-20  1055  	}
241b507c166fef Rob Clark         2019-08-20  1056  
241b507c166fef Rob Clark         2019-08-20  1057  	if (request_bandwidth)
241b507c166fef Rob Clark         2019-08-20  1058  		atomic_inc(&_dpu_crtc_get_kms(crtc)->bandwidth_ref);
25fdd5933e4c0f Jeykumar Sankaran 2018-06-27  1059  
25fdd5933e4c0f Jeykumar Sankaran 2018-06-27  1060  	trace_dpu_crtc_enable(DRMID(crtc), true, dpu_crtc);
25fdd5933e4c0f Jeykumar Sankaran 2018-06-27  1061  	dpu_crtc->enabled = true;
25fdd5933e4c0f Jeykumar Sankaran 2018-06-27  1062  
2c3c31343481a4 Vinod Polimera    2022-06-21  1063  	if (!old_crtc_state->self_refresh_active)
a796ba2cb3dde3 Sean Paul         2018-11-16 @1064  		drm_for_each_encoder_mask(encoder, crtc->dev, crtc->state->encoder_mask)
a796ba2cb3dde3 Sean Paul         2018-11-16  1065  			dpu_encoder_assign_crtc(encoder, crtc);
25fdd5933e4c0f Jeykumar Sankaran 2018-06-27  1066  
2f2eb723b50b4d Rajesh Yadav      2018-07-13  1067  	/* Enable/restore vblank irq handling */
2f2eb723b50b4d Rajesh Yadav      2018-07-13  1068  	drm_crtc_vblank_on(crtc);
25fdd5933e4c0f Jeykumar Sankaran 2018-06-27  1069  }
25fdd5933e4c0f Jeykumar Sankaran 2018-06-27  1070  

-- 
0-DAY CI Kernel Test Service
https://01.org/lkp

^ permalink raw reply	[flat|nested] 32+ messages in thread

* RE: [v3 1/5] drm/msm/dp: Add basic PSR support for eDP
  2022-06-21 18:53     ` Dmitry Baryshkov
@ 2022-06-29 13:38       ` Sankeerth Billakanti (QUIC)
  -1 siblings, 0 replies; 32+ messages in thread
From: Sankeerth Billakanti (QUIC) @ 2022-06-29 13:38 UTC (permalink / raw)
  To: dmitry.baryshkov, Vinod Polimera (QUIC),
	y, dri-devel, linux-arm-msm, freedreno, devicetree
  Cc: linux-kernel, robdclark, dianders, swboyd, Kalyan Thota (QUIC),
	Sankeerth Billakanti (QUIC), Vishnuvardhan Prodduturi (QUIC),
	Aravind Venkateswaran (QUIC), Abhinav Kumar (QUIC),
	Kuogee Hsieh (QUIC)

Hi Dmitry,

>On 21/06/2022 13:53, Vinod Polimera wrote:
>> 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: Sankeerth Billakanti <quic_sbillaka@quicinc.com>
>> Signed-off-by: Vinod Polimera <quic_vpolimer@quicinc.com>
>> ---
>>   drivers/gpu/drm/msm/dp/dp_catalog.c |  81 ++++++++++++++++++
>>   drivers/gpu/drm/msm/dp/dp_catalog.h |   4 +
>>   drivers/gpu/drm/msm/dp/dp_ctrl.c    |  76 ++++++++++++++++-
>>   drivers/gpu/drm/msm/dp/dp_ctrl.h    |   3 +
>>   drivers/gpu/drm/msm/dp/dp_display.c |  14 +++
>>   drivers/gpu/drm/msm/dp/dp_display.h |   2 +
>>   drivers/gpu/drm/msm/dp/dp_drm.c     | 166
>+++++++++++++++++++++++++++++++++++-
>>   drivers/gpu/drm/msm/dp/dp_link.c    |  36 ++++++++
>>   drivers/gpu/drm/msm/dp/dp_panel.c   |  22 +++++
>>   drivers/gpu/drm/msm/dp/dp_panel.h   |   6 ++
>>   drivers/gpu/drm/msm/dp/dp_reg.h     |  27 ++++++
>>   11 files changed, 433 insertions(+), 4 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.c
>> b/drivers/gpu/drm/msm/dp/dp_catalog.c
>> index 7257515..b9021ed 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_catalog.c
>> +++ b/drivers/gpu/drm/msm/dp/dp_catalog.c
>> @@ -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,24 @@ 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);
>> +	val &= ~DP_MAINLINK_CTRL_ENABLE;
>> +
>> +	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 +636,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, !UPDATE_SDP); }
>> +
>> +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 +712,20 @@ u32 dp_catalog_hpd_get_intr_status(struct dp_catalog
>*dp_catalog)
>>   	return isr & (mask | ~DP_DP_HPD_INT_MASK);
>>   }
>>
>> +int 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, diff
>> --git a/drivers/gpu/drm/msm/dp/dp_catalog.h
>> b/drivers/gpu/drm/msm/dp/dp_catalog.h
>> index 1f717f4..6454845 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_catalog.h
>> +++ b/drivers/gpu/drm/msm/dp/dp_catalog.h
>> @@ -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);
>> +int 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);
>> diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c
>> b/drivers/gpu/drm/msm/dp/dp_ctrl.c
>> index d21971b..485e8f5 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
>> +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
>> @@ -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);
>>   }
>>
>> @@ -1382,11 +1387,64 @@ static int dp_ctrl_enable_stream_clocks(struct
>dp_ctrl_private *ctrl)
>>   	return ret;
>>   }
>>
>> -void dp_ctrl_reset_irq_ctrl(struct dp_ctrl *dp_ctrl, bool enable)
>> +void dp_ctrl_config_psr(struct dp_ctrl *dp_ctrl)
>>   {
>> -	struct dp_ctrl_private *ctrl;
>> +	u8 cfg;
>> +	struct dp_ctrl_private *ctrl = container_of(dp_ctrl,
>> +			struct dp_ctrl_private, dp_ctrl);
>>
>> -	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_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);
>> +	}
>> +}
>> +
>> +void dp_ctrl_reset_irq_ctrl(struct dp_ctrl *dp_ctrl, bool enable) {
>> +	struct dp_ctrl_private *ctrl = container_of(dp_ctrl,
>> +			struct dp_ctrl_private, dp_ctrl);
>>
>>   	dp_catalog_ctrl_reset(ctrl->catalog);
>>
>> @@ -1997,6 +2055,17 @@ 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 == PSR_UPDATE_INT)
>> +			drm_dbg_dp(ctrl->drm_dev, "PSR frame update
>done\n");
>> +		else if (isr == PSR_EXIT_INT)
>> +			drm_dbg_dp(ctrl->drm_dev, "PSR exit done\n");
>> +
>> +		complete(&ctrl->psr_op_comp);
>> +	}
>> +
>>   	isr = dp_catalog_ctrl_get_interrupt(ctrl->catalog);
>>
>>   	if (isr & DP_CTRL_INTR_READY_FOR_VIDEO) { @@ -2043,6 +2112,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 */
>> diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.h
>> b/drivers/gpu/drm/msm/dp/dp_ctrl.h
>> index 0745fde..be074ae 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_ctrl.h
>> +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h
>> @@ -38,4 +38,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_ */
>> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c
>> b/drivers/gpu/drm/msm/dp/dp_display.c
>> index bce7793..2e779c2 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_display.c
>> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
>> @@ -388,6 +388,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);
>>
>> @@ -895,6 +897,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);
>
>Dumb question. What if the first display reports psr as supported, but the
>next DP display that gets plugged doesn't support PSR? Do we need to do
>anything to clear this config?
>

The psr_supported flag is set after checking for psr support after HPD.

	dp->dp_display.psr_supported = !!dp->panel->psr_cap.version;
in dp_display_process_hpd_high will update it.

>> +
>>   	return 0;
>>   }
>>
>> @@ -1094,6 +1100,14 @@ 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;
>> +
>> +	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; diff --git
>> a/drivers/gpu/drm/msm/dp/dp_display.h
>> b/drivers/gpu/drm/msm/dp/dp_display.h
>> index 4f9fe4d..1feaada 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_display.h
>> +++ b/drivers/gpu/drm/msm/dp/dp_display.h
>> @@ -29,6 +29,7 @@ struct msm_dp {
>>
>>   	u32 max_dp_lanes;
>>   	struct dp_audio *dp_audio;
>> +	bool psr_supported;
>>   };
>>
>>   int dp_display_set_plugged_cb(struct msm_dp *dp_display, @@ -39,5
>> +40,6 @@ bool dp_display_check_video_test(struct msm_dp *dp_display);
>>   int dp_display_get_test_bpp(struct msm_dp *dp_display);
>>   void dp_display_signal_audio_start(struct msm_dp *dp_display);
>>   void dp_display_signal_audio_complete(struct msm_dp *dp_display);
>> +void dp_display_set_psr(struct msm_dp *dp, bool enter);
>>
>>   #endif /* _DP_DISPLAY_H_ */
>> diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c
>> b/drivers/gpu/drm/msm/dp/dp_drm.c index 62d58b9..cfe31ed 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_drm.c
>> +++ b/drivers/gpu/drm/msm/dp/dp_drm.c
>> @@ -60,6 +60,169 @@ static int dp_bridge_get_modes(struct drm_bridge
>*bridge, struct drm_connector *
>>   	return rc;
>>   }
>>
>> +static struct drm_crtc *dp_bridge_get_old_connector_crtc(struct msm_dp
>*dp,
>> +					  struct drm_atomic_state *state) {
>> +	struct drm_encoder *encoder = dp->encoder;
>> +	struct drm_connector *connector;
>> +	struct drm_connector_state *conn_state;
>> +
>> +	connector = drm_atomic_get_old_connector_for_encoder(state,
>encoder);
>> +	if (!connector)
>> +		return NULL;
>> +
>> +	conn_state = drm_atomic_get_old_connector_state(state,
>connector);
>> +	if (!conn_state)
>> +		return NULL;
>> +
>> +	return conn_state->crtc;
>> +}
>> +
>> +static struct drm_crtc *dp_bridge_get_new_connector_crtc(struct
>msm_dp *dp,
>> +					  struct drm_atomic_state *state) {
>> +	struct drm_encoder *encoder = dp->encoder;
>> +	struct drm_connector *connector;
>> +	struct drm_connector_state *conn_state;
>> +
>> +	connector = drm_atomic_get_new_connector_for_encoder(state,
>encoder);
>> +	if (!connector)
>> +		return NULL;
>> +
>> +	conn_state = drm_atomic_get_new_connector_state(state,
>connector);
>> +	if (!conn_state)
>> +		return NULL;
>> +
>> +	return conn_state->crtc;
>> +}
>
>These two functions should become core DRM helpers. Please move them
>accordingly to remove duplication with the bridge code.
>

Okay. I will add two helper functions in drm_atomic.c to retrieve the old and new crtcs from encoder and atomic state.

>> +
>> +static int edp_bridge_atomic_check(struct drm_bridge *drm_bridge,
>> +			    struct drm_bridge_state *bridge_state,
>> +			    struct drm_crtc_state *crtc_state,
>> +			    struct drm_connector_state *conn_state) {
>> +	struct msm_dp *dp;
>> +
>> +	dp = to_dp_bridge(drm_bridge)->dp_display;
>> +	if (WARN_ON(!conn_state))
>> +		return -ENODEV;
>> +
>> +	if (dp->psr_supported)
>> +		conn_state->self_refresh_aware = true;
>> +
>> +	if (!conn_state->crtc || !crtc_state)
>> +		return 0;
>> +
>> +	if (crtc_state->self_refresh_active && !dp->psr_supported)
>> +		return -EINVAL;
>> +
>> +	return 0;
>> +}
>> +
>> +static void edp_bridge_atomic_enable(struct drm_bridge *drm_bridge,
>> +				struct drm_bridge_state *old_bridge_state) {
>> +	struct drm_atomic_state *old_state = old_bridge_state->base.state;
>> +	struct drm_crtc *crtc;
>> +	struct drm_crtc_state *old_crtc_state;
>> +	struct msm_dp_bridge *dp_bridge = to_dp_bridge(drm_bridge);
>> +	struct msm_dp *dp_display = dp_bridge->dp_display;
>> +
>> +	crtc = dp_bridge_get_new_connector_crtc(dp_display, old_state);
>> +	if (!crtc)
>> +		return;
>
>Why?
>

We need to know the psr status of the old state of the crtc to decide whether
to enable the complete eDP controller or just exit psr state.

>> +
>> +	old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc);
>> +
>> +	/* Exit from self refresh mode */
>> +	if (old_crtc_state && old_crtc_state->self_refresh_active) {
>> +		dp_display_set_psr(dp_display, false);
>> +		return;
>> +	}
>> +
>> +	dp_bridge_enable(drm_bridge);
>> +}
>> +
>> +static void edp_bridge_atomic_disable(struct drm_bridge *drm_bridge,
>> +				struct drm_bridge_state *old_bridge_state) {
>> +	struct drm_atomic_state *old_state = old_bridge_state->base.state;
>> +	struct drm_crtc *crtc;
>> +	struct drm_crtc_state *new_crtc_state = NULL, *old_crtc_state =
>NULL;
>> +	struct msm_dp_bridge *dp_bridge = to_dp_bridge(drm_bridge);
>> +	struct msm_dp *dp_display = dp_bridge->dp_display;
>> +
>> +	crtc = dp_bridge_get_old_connector_crtc(dp_display, old_state);
>> +	if (!crtc)
>> +		goto out;
>
>Why? And why old crtc?
>
In disable the new crtc may be removed for screen off cases and hence could be NULL.
So, we need to retrieve the old crtc.

We need to know if psr was enabled in the old state of crtc. If it is enabled, then it
means this is a screen off case and we need to proceed to turn off the edp controller
in the post_disable.

If this is not a screen off case, then we need to check the new crtc state to know if the
OS is requesting for a psr entry and accordingly enter psr

>> +
>> +	new_crtc_state = drm_atomic_get_new_crtc_state(old_state, crtc);
>> +	if (!new_crtc_state)
>> +		goto out;
>> +
>> +	old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc);
>> +	if (!old_crtc_state)
>> +		goto out;
>> +
>> +	/*
>> +	 * Set self refresh mode if current crtc state is active.
>> +	 * If old crtc state is active, then this is a display disable
>> +	 * call while the sink is in psr state. So, exit psr here.
>> +	 * The eDP controller will be disabled in the
>> +	 * edp_bridge_atomic_post_disable function.
>> +	 *
>> +	 * We observed sink is stuck in self refresh if psr exit is skipped
>> +	 * when display disable occurs while the sink is in psr state.
>> +	 */
>> +	if (new_crtc_state->self_refresh_active) {
>> +		dp_display_set_psr(dp_display, true);
>> +		return;
>> +	} else if (old_crtc_state->self_refresh_active) {
>> +		dp_display_set_psr(dp_display, false);
>> +		return;
>> +	}
>> +
>> +out:
>> +	dp_bridge_disable(drm_bridge);
>> +}
>> +
>> +static void edp_bridge_atomic_post_disable(struct drm_bridge
>*drm_bridge,
>> +				struct drm_bridge_state *old_bridge_state) {
>> +	struct drm_atomic_state *old_state = old_bridge_state->base.state;
>> +	struct drm_crtc *crtc;
>> +	struct drm_crtc_state *new_crtc_state = NULL;
>> +	struct msm_dp_bridge *dp_bridge = to_dp_bridge(drm_bridge);
>> +	struct msm_dp *dp_display = dp_bridge->dp_display;
>> +
>> +	crtc = dp_bridge_get_old_connector_crtc(dp_display, old_state);
>> +	if (!crtc)
>> +		return;
>> +
>> +	new_crtc_state = drm_atomic_get_new_crtc_state(old_state, crtc);
>> +	if (!new_crtc_state)
>> +		return;
>> +
>> +	/*
>> +	 * Self refresh mode is already set in edp_bridge_atomic_disable.
>> +	 */
>> +	if (new_crtc_state->self_refresh_active)
>> +		return;
>> +
>> +	dp_bridge_post_disable(drm_bridge);
>> +}
>> +
>> +static const struct drm_bridge_funcs edp_bridge_ops = {
>> +	.atomic_enable       = edp_bridge_atomic_enable,
>> +	.atomic_disable      = edp_bridge_atomic_disable,
>> +	.atomic_post_disable = edp_bridge_atomic_post_disable,
>
>If you are using the atomic ops here, please switch dp_bridge_ops to atomic
>too.
>
>> +	.mode_set     = dp_bridge_mode_set,
>
>.mode_valid is missing. Is this intentional?
>

No. it was missed. Will add it

>> +	.atomic_reset = drm_atomic_helper_bridge_reset,
>> +	.atomic_duplicate_state =
>drm_atomic_helper_bridge_duplicate_state,
>> +	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
>> +	.atomic_check = edp_bridge_atomic_check, };
>> +
>>   static const struct drm_bridge_funcs dp_bridge_ops = {
>>   	.enable       = dp_bridge_enable,
>>   	.disable      = dp_bridge_disable,
>> @@ -84,7 +247,7 @@ struct drm_bridge *dp_bridge_init(struct msm_dp
>*dp_display, struct drm_device *
>>   	dp_bridge->dp_display = dp_display;
>>
>>   	bridge = &dp_bridge->bridge;
>> -	bridge->funcs = &dp_bridge_ops;
>> +	bridge->funcs = &edp_bridge_ops;
>
>Please use ternary operator here.
>

Okay

>>   	bridge->type = dp_display->connector_type;
>>
>>   	/*
>> @@ -99,6 +262,7 @@ struct drm_bridge *dp_bridge_init(struct msm_dp
>*dp_display, struct drm_device *
>>   	 *   modes.
>>   	 */
>>   	if (!dp_display->is_edp) {
>> +		bridge->funcs = &dp_bridge_ops;
>>   		bridge->ops =
>>   			DRM_BRIDGE_OP_DETECT |
>>   			DRM_BRIDGE_OP_HPD |
>> diff --git a/drivers/gpu/drm/msm/dp/dp_link.c
>> b/drivers/gpu/drm/msm/dp/dp_link.c
>> index 36f0af0..81ac3c1 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_link.c
>> +++ b/drivers/gpu/drm/msm/dp/dp_link.c
>> @@ -934,6 +934,38 @@ static int
>dp_link_process_phy_test_pattern_request(
>>   	return 0;
>>   }
>>
>> +static bool dp_link_read_psr_error_status(struct dp_link_private
>> +*link) {
>> +	u8 status;
>> +
>> +	drm_dp_dpcd_read(link->aux, DP_PSR_ERROR_STATUS, &status, 1);
>> +
>> +	if (status & DP_PSR_LINK_CRC_ERROR)
>> +		DRM_ERROR("PSR LINK CRC ERROR\n");
>> +	else if (status & DP_PSR_RFB_STORAGE_ERROR)
>> +		DRM_ERROR("PSR RFB STORAGE ERROR\n");
>> +	else if (status & DP_PSR_VSC_SDP_UNCORRECTABLE_ERROR)
>> +		DRM_ERROR("PSR VSC SDP UNCORRECTABLE ERROR\n");
>> +	else
>> +		return 0;
>> +
>> +	return 1;
>
>If it's bool, it should return true/false and not 0/1.
>

Okay. Will change it

>> +}
>> +
>> +static bool dp_link_psr_capability_changed(struct dp_link_private
>> +*link) {
>> +	u8 status;
>> +
>> +	drm_dp_dpcd_read(link->aux, DP_PSR_ESI, &status, 1);
>> +
>> +	if (status & DP_PSR_CAPS_CHANGE)
>> +		drm_dbg_dp(link->drm_dev, "PSR Capability Change\n");
>> +	else
>> +		return 0;
>> +
>> +	return 1;
>
>if (!(satus & DP_PSR_CAPS_CHANGE))
>     return false;
>
>drm_dbg_dp(..)
>return true;
>
>
>Or
>
>if (status & DP_PSR_CAPS_CHANGE) {
>   drm_dbg_dp(...)
>   return true;
>}
>
>return false;
>
>

Okay. Will change it.

>> +}
>> +
>>   static u8 get_link_status(const u8 link_status[DP_LINK_STATUS_SIZE], int r)
>>   {
>>   	return link_status[r - DP_LANE0_1_STATUS]; @@ -1053,6 +1085,10
>@@
>> int dp_link_process_request(struct dp_link *dp_link)
>>   		dp_link->sink_request |= DP_TEST_LINK_TRAINING;
>>   	} else if (!dp_link_process_phy_test_pattern_request(link)) {
>>   		dp_link->sink_request |=
>DP_TEST_LINK_PHY_TEST_PATTERN;
>> +	} else if (dp_link_read_psr_error_status(link)) {
>> +		DRM_ERROR("PSR IRQ_HPD received\n");
>> +	} else if (dp_link_psr_capability_changed(link)) {
>> +		drm_dbg_dp(link->drm_dev, "PSR Capabiity changed");
>>   	} else {
>>   		ret = dp_link_process_link_status_update(link);
>>   		if (!ret) {
>> diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c
>> b/drivers/gpu/drm/msm/dp/dp_panel.c
>> index 5149ceb..8bf8ab4 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_panel.c
>> +++ b/drivers/gpu/drm/msm/dp/dp_panel.c
>> @@ -20,6 +20,27 @@ struct dp_panel_private {
>>   	bool aux_cfg_update_done;
>>   };
>>
>> +static void dp_panel_read_psr_cap(struct dp_panel_private *panel) {
>> +	ssize_t rlen;
>> +	struct dp_panel *dp_panel;
>> +
>> +	dp_panel = &panel->dp_panel;
>> +
>> +	/* edp sink */
>> +	if (dp_panel->dpcd[DP_EDP_CONFIGURATION_CAP]) {
>> +		rlen = drm_dp_dpcd_read(panel->aux, DP_PSR_SUPPORT,
>> +				&dp_panel->psr_cap, sizeof(dp_panel-
>>psr_cap));
>> +		if (rlen == sizeof(dp_panel->psr_cap)) {
>> +			drm_dbg_dp(panel->drm_dev,
>> +				"psr version: 0x%x, psr_cap: 0x%x\n",
>> +				dp_panel->psr_cap.version,
>> +				dp_panel->psr_cap.capabilities);
>> +		} else
>> +			DRM_ERROR("failed to read psr info, rlen=%zd\n",
>rlen);
>> +	}
>> +}
>> +
>>   static int dp_panel_read_dpcd(struct dp_panel *dp_panel)
>>   {
>>   	int rc = 0;
>> @@ -106,6 +127,7 @@ static int dp_panel_read_dpcd(struct dp_panel
>*dp_panel)
>>   		}
>>   	}
>>
>> +	dp_panel_read_psr_cap(panel);
>>   end:
>>   	return rc;
>>   }
>> diff --git a/drivers/gpu/drm/msm/dp/dp_panel.h
>> b/drivers/gpu/drm/msm/dp/dp_panel.h
>> index d861197a..2d0826a 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_panel.h
>> +++ b/drivers/gpu/drm/msm/dp/dp_panel.h
>> @@ -34,6 +34,11 @@ struct dp_panel_in {
>>   	struct dp_catalog *catalog;
>>   };
>>
>> +struct dp_panel_psr {
>> +	u8 version;
>> +	u8 capabilities;
>> +};
>> +
>>   struct dp_panel {
>>   	/* dpcd raw data */
>>   	u8 dpcd[DP_RECEIVER_CAP_SIZE + 1];
>> @@ -46,6 +51,7 @@ struct dp_panel {
>>   	struct edid *edid;
>>   	struct drm_connector *connector;
>>   	struct dp_display_mode dp_mode;
>> +	struct dp_panel_psr psr_cap;
>>   	bool video_test;
>>
>>   	u32 vic;
>> diff --git a/drivers/gpu/drm/msm/dp/dp_reg.h
>> b/drivers/gpu/drm/msm/dp/dp_reg.h index 2686028..ea85a69 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_reg.h
>> +++ b/drivers/gpu/drm/msm/dp/dp_reg.h
>> @@ -22,6 +22,20 @@
>>   #define REG_DP_INTR_STATUS2			(0x00000024)
>>   #define REG_DP_INTR_STATUS3			(0x00000028)
>>
>> +#define REG_DP_INTR_STATUS4			(0x0000002C)
>> +#define PSR_UPDATE_INT				(0x00000001)
>> +#define PSR_CAPTURE_INT				(0x00000004)
>> +#define PSR_EXIT_INT				(0x00000010)
>> +#define PSR_UPDATE_ERROR_INT			(0x00000040)
>> +#define PSR_WAKE_ERROR_INT			(0x00000100)
>> +
>> +#define REG_DP_INTR_MASK4			(0x00000030)
>> +#define PSR_UPDATE_MASK				(0x00000001)
>> +#define PSR_CAPTURE_MASK			(0x00000002)
>> +#define PSR_EXIT_MASK				(0x00000004)
>> +#define PSR_UPDATE_ERROR_MASK			(0x00000008)
>> +#define PSR_WAKE_ERROR_MASK			(0x00000010)
>> +
>>   #define REG_DP_DP_HPD_CTRL			(0x00000000)
>>   #define DP_DP_HPD_CTRL_HPD_EN			(0x00000001)
>>
>> @@ -164,6 +178,16 @@
>>   #define MMSS_DP_AUDIO_TIMING_RBR_48		(0x00000094)
>>   #define MMSS_DP_AUDIO_TIMING_HBR_48		(0x00000098)
>>
>> +#define REG_PSR_CONFIG				(0x00000100)
>> +#define DISABLE_PSR				(0x00000000)
>> +#define PSR1_SUPPORTED				(0x00000001)
>> +#define PSR2_WITHOUT_FRAMESYNC			(0x00000002)
>> +#define PSR2_WITH_FRAMESYNC			(0x00000003)
>> +
>> +#define REG_PSR_CMD				(0x00000110)
>> +#define PSR_ENTER				(0x00000001)
>> +#define PSR_EXIT				(0x00000002)
>> +
>>   #define MMSS_DP_PSR_CRC_RG			(0x00000154)
>>   #define MMSS_DP_PSR_CRC_B			(0x00000158)
>>
>> @@ -184,6 +208,9 @@
>>   #define MMSS_DP_AUDIO_STREAM_0			(0x00000240)
>>   #define MMSS_DP_AUDIO_STREAM_1			(0x00000244)
>>
>> +#define MMSS_DP_SDP_CFG3			(0x0000024c)
>> +#define UPDATE_SDP				(0x00000001)
>> +
>>   #define MMSS_DP_EXTENSION_0			(0x00000250)
>>   #define MMSS_DP_EXTENSION_1			(0x00000254)
>>   #define MMSS_DP_EXTENSION_2			(0x00000258)
>
>
>--
>With best wishes
>Dmitry

Thank you,
Sankeerth

^ permalink raw reply	[flat|nested] 32+ messages in thread

* RE: [v3 1/5] drm/msm/dp: Add basic PSR support for eDP
@ 2022-06-29 13:38       ` Sankeerth Billakanti (QUIC)
  0 siblings, 0 replies; 32+ messages in thread
From: Sankeerth Billakanti (QUIC) @ 2022-06-29 13:38 UTC (permalink / raw)
  To: dmitry.baryshkov, Vinod Polimera (QUIC),
	y, dri-devel, linux-arm-msm, freedreno, devicetree
  Cc: Kalyan Thota (QUIC), Sankeerth Billakanti (QUIC),
	dianders, Abhinav Kumar (QUIC), Vishnuvardhan Prodduturi (QUIC),
	linux-kernel, swboyd, Aravind Venkateswaran (QUIC),
	Kuogee Hsieh (QUIC)

Hi Dmitry,

>On 21/06/2022 13:53, Vinod Polimera wrote:
>> 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: Sankeerth Billakanti <quic_sbillaka@quicinc.com>
>> Signed-off-by: Vinod Polimera <quic_vpolimer@quicinc.com>
>> ---
>>   drivers/gpu/drm/msm/dp/dp_catalog.c |  81 ++++++++++++++++++
>>   drivers/gpu/drm/msm/dp/dp_catalog.h |   4 +
>>   drivers/gpu/drm/msm/dp/dp_ctrl.c    |  76 ++++++++++++++++-
>>   drivers/gpu/drm/msm/dp/dp_ctrl.h    |   3 +
>>   drivers/gpu/drm/msm/dp/dp_display.c |  14 +++
>>   drivers/gpu/drm/msm/dp/dp_display.h |   2 +
>>   drivers/gpu/drm/msm/dp/dp_drm.c     | 166
>+++++++++++++++++++++++++++++++++++-
>>   drivers/gpu/drm/msm/dp/dp_link.c    |  36 ++++++++
>>   drivers/gpu/drm/msm/dp/dp_panel.c   |  22 +++++
>>   drivers/gpu/drm/msm/dp/dp_panel.h   |   6 ++
>>   drivers/gpu/drm/msm/dp/dp_reg.h     |  27 ++++++
>>   11 files changed, 433 insertions(+), 4 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.c
>> b/drivers/gpu/drm/msm/dp/dp_catalog.c
>> index 7257515..b9021ed 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_catalog.c
>> +++ b/drivers/gpu/drm/msm/dp/dp_catalog.c
>> @@ -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,24 @@ 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);
>> +	val &= ~DP_MAINLINK_CTRL_ENABLE;
>> +
>> +	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 +636,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, !UPDATE_SDP); }
>> +
>> +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 +712,20 @@ u32 dp_catalog_hpd_get_intr_status(struct dp_catalog
>*dp_catalog)
>>   	return isr & (mask | ~DP_DP_HPD_INT_MASK);
>>   }
>>
>> +int 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, diff
>> --git a/drivers/gpu/drm/msm/dp/dp_catalog.h
>> b/drivers/gpu/drm/msm/dp/dp_catalog.h
>> index 1f717f4..6454845 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_catalog.h
>> +++ b/drivers/gpu/drm/msm/dp/dp_catalog.h
>> @@ -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);
>> +int 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);
>> diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c
>> b/drivers/gpu/drm/msm/dp/dp_ctrl.c
>> index d21971b..485e8f5 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
>> +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
>> @@ -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);
>>   }
>>
>> @@ -1382,11 +1387,64 @@ static int dp_ctrl_enable_stream_clocks(struct
>dp_ctrl_private *ctrl)
>>   	return ret;
>>   }
>>
>> -void dp_ctrl_reset_irq_ctrl(struct dp_ctrl *dp_ctrl, bool enable)
>> +void dp_ctrl_config_psr(struct dp_ctrl *dp_ctrl)
>>   {
>> -	struct dp_ctrl_private *ctrl;
>> +	u8 cfg;
>> +	struct dp_ctrl_private *ctrl = container_of(dp_ctrl,
>> +			struct dp_ctrl_private, dp_ctrl);
>>
>> -	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_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);
>> +	}
>> +}
>> +
>> +void dp_ctrl_reset_irq_ctrl(struct dp_ctrl *dp_ctrl, bool enable) {
>> +	struct dp_ctrl_private *ctrl = container_of(dp_ctrl,
>> +			struct dp_ctrl_private, dp_ctrl);
>>
>>   	dp_catalog_ctrl_reset(ctrl->catalog);
>>
>> @@ -1997,6 +2055,17 @@ 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 == PSR_UPDATE_INT)
>> +			drm_dbg_dp(ctrl->drm_dev, "PSR frame update
>done\n");
>> +		else if (isr == PSR_EXIT_INT)
>> +			drm_dbg_dp(ctrl->drm_dev, "PSR exit done\n");
>> +
>> +		complete(&ctrl->psr_op_comp);
>> +	}
>> +
>>   	isr = dp_catalog_ctrl_get_interrupt(ctrl->catalog);
>>
>>   	if (isr & DP_CTRL_INTR_READY_FOR_VIDEO) { @@ -2043,6 +2112,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 */
>> diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.h
>> b/drivers/gpu/drm/msm/dp/dp_ctrl.h
>> index 0745fde..be074ae 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_ctrl.h
>> +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h
>> @@ -38,4 +38,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_ */
>> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c
>> b/drivers/gpu/drm/msm/dp/dp_display.c
>> index bce7793..2e779c2 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_display.c
>> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
>> @@ -388,6 +388,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);
>>
>> @@ -895,6 +897,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);
>
>Dumb question. What if the first display reports psr as supported, but the
>next DP display that gets plugged doesn't support PSR? Do we need to do
>anything to clear this config?
>

The psr_supported flag is set after checking for psr support after HPD.

	dp->dp_display.psr_supported = !!dp->panel->psr_cap.version;
in dp_display_process_hpd_high will update it.

>> +
>>   	return 0;
>>   }
>>
>> @@ -1094,6 +1100,14 @@ 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;
>> +
>> +	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; diff --git
>> a/drivers/gpu/drm/msm/dp/dp_display.h
>> b/drivers/gpu/drm/msm/dp/dp_display.h
>> index 4f9fe4d..1feaada 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_display.h
>> +++ b/drivers/gpu/drm/msm/dp/dp_display.h
>> @@ -29,6 +29,7 @@ struct msm_dp {
>>
>>   	u32 max_dp_lanes;
>>   	struct dp_audio *dp_audio;
>> +	bool psr_supported;
>>   };
>>
>>   int dp_display_set_plugged_cb(struct msm_dp *dp_display, @@ -39,5
>> +40,6 @@ bool dp_display_check_video_test(struct msm_dp *dp_display);
>>   int dp_display_get_test_bpp(struct msm_dp *dp_display);
>>   void dp_display_signal_audio_start(struct msm_dp *dp_display);
>>   void dp_display_signal_audio_complete(struct msm_dp *dp_display);
>> +void dp_display_set_psr(struct msm_dp *dp, bool enter);
>>
>>   #endif /* _DP_DISPLAY_H_ */
>> diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c
>> b/drivers/gpu/drm/msm/dp/dp_drm.c index 62d58b9..cfe31ed 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_drm.c
>> +++ b/drivers/gpu/drm/msm/dp/dp_drm.c
>> @@ -60,6 +60,169 @@ static int dp_bridge_get_modes(struct drm_bridge
>*bridge, struct drm_connector *
>>   	return rc;
>>   }
>>
>> +static struct drm_crtc *dp_bridge_get_old_connector_crtc(struct msm_dp
>*dp,
>> +					  struct drm_atomic_state *state) {
>> +	struct drm_encoder *encoder = dp->encoder;
>> +	struct drm_connector *connector;
>> +	struct drm_connector_state *conn_state;
>> +
>> +	connector = drm_atomic_get_old_connector_for_encoder(state,
>encoder);
>> +	if (!connector)
>> +		return NULL;
>> +
>> +	conn_state = drm_atomic_get_old_connector_state(state,
>connector);
>> +	if (!conn_state)
>> +		return NULL;
>> +
>> +	return conn_state->crtc;
>> +}
>> +
>> +static struct drm_crtc *dp_bridge_get_new_connector_crtc(struct
>msm_dp *dp,
>> +					  struct drm_atomic_state *state) {
>> +	struct drm_encoder *encoder = dp->encoder;
>> +	struct drm_connector *connector;
>> +	struct drm_connector_state *conn_state;
>> +
>> +	connector = drm_atomic_get_new_connector_for_encoder(state,
>encoder);
>> +	if (!connector)
>> +		return NULL;
>> +
>> +	conn_state = drm_atomic_get_new_connector_state(state,
>connector);
>> +	if (!conn_state)
>> +		return NULL;
>> +
>> +	return conn_state->crtc;
>> +}
>
>These two functions should become core DRM helpers. Please move them
>accordingly to remove duplication with the bridge code.
>

Okay. I will add two helper functions in drm_atomic.c to retrieve the old and new crtcs from encoder and atomic state.

>> +
>> +static int edp_bridge_atomic_check(struct drm_bridge *drm_bridge,
>> +			    struct drm_bridge_state *bridge_state,
>> +			    struct drm_crtc_state *crtc_state,
>> +			    struct drm_connector_state *conn_state) {
>> +	struct msm_dp *dp;
>> +
>> +	dp = to_dp_bridge(drm_bridge)->dp_display;
>> +	if (WARN_ON(!conn_state))
>> +		return -ENODEV;
>> +
>> +	if (dp->psr_supported)
>> +		conn_state->self_refresh_aware = true;
>> +
>> +	if (!conn_state->crtc || !crtc_state)
>> +		return 0;
>> +
>> +	if (crtc_state->self_refresh_active && !dp->psr_supported)
>> +		return -EINVAL;
>> +
>> +	return 0;
>> +}
>> +
>> +static void edp_bridge_atomic_enable(struct drm_bridge *drm_bridge,
>> +				struct drm_bridge_state *old_bridge_state) {
>> +	struct drm_atomic_state *old_state = old_bridge_state->base.state;
>> +	struct drm_crtc *crtc;
>> +	struct drm_crtc_state *old_crtc_state;
>> +	struct msm_dp_bridge *dp_bridge = to_dp_bridge(drm_bridge);
>> +	struct msm_dp *dp_display = dp_bridge->dp_display;
>> +
>> +	crtc = dp_bridge_get_new_connector_crtc(dp_display, old_state);
>> +	if (!crtc)
>> +		return;
>
>Why?
>

We need to know the psr status of the old state of the crtc to decide whether
to enable the complete eDP controller or just exit psr state.

>> +
>> +	old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc);
>> +
>> +	/* Exit from self refresh mode */
>> +	if (old_crtc_state && old_crtc_state->self_refresh_active) {
>> +		dp_display_set_psr(dp_display, false);
>> +		return;
>> +	}
>> +
>> +	dp_bridge_enable(drm_bridge);
>> +}
>> +
>> +static void edp_bridge_atomic_disable(struct drm_bridge *drm_bridge,
>> +				struct drm_bridge_state *old_bridge_state) {
>> +	struct drm_atomic_state *old_state = old_bridge_state->base.state;
>> +	struct drm_crtc *crtc;
>> +	struct drm_crtc_state *new_crtc_state = NULL, *old_crtc_state =
>NULL;
>> +	struct msm_dp_bridge *dp_bridge = to_dp_bridge(drm_bridge);
>> +	struct msm_dp *dp_display = dp_bridge->dp_display;
>> +
>> +	crtc = dp_bridge_get_old_connector_crtc(dp_display, old_state);
>> +	if (!crtc)
>> +		goto out;
>
>Why? And why old crtc?
>
In disable the new crtc may be removed for screen off cases and hence could be NULL.
So, we need to retrieve the old crtc.

We need to know if psr was enabled in the old state of crtc. If it is enabled, then it
means this is a screen off case and we need to proceed to turn off the edp controller
in the post_disable.

If this is not a screen off case, then we need to check the new crtc state to know if the
OS is requesting for a psr entry and accordingly enter psr

>> +
>> +	new_crtc_state = drm_atomic_get_new_crtc_state(old_state, crtc);
>> +	if (!new_crtc_state)
>> +		goto out;
>> +
>> +	old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc);
>> +	if (!old_crtc_state)
>> +		goto out;
>> +
>> +	/*
>> +	 * Set self refresh mode if current crtc state is active.
>> +	 * If old crtc state is active, then this is a display disable
>> +	 * call while the sink is in psr state. So, exit psr here.
>> +	 * The eDP controller will be disabled in the
>> +	 * edp_bridge_atomic_post_disable function.
>> +	 *
>> +	 * We observed sink is stuck in self refresh if psr exit is skipped
>> +	 * when display disable occurs while the sink is in psr state.
>> +	 */
>> +	if (new_crtc_state->self_refresh_active) {
>> +		dp_display_set_psr(dp_display, true);
>> +		return;
>> +	} else if (old_crtc_state->self_refresh_active) {
>> +		dp_display_set_psr(dp_display, false);
>> +		return;
>> +	}
>> +
>> +out:
>> +	dp_bridge_disable(drm_bridge);
>> +}
>> +
>> +static void edp_bridge_atomic_post_disable(struct drm_bridge
>*drm_bridge,
>> +				struct drm_bridge_state *old_bridge_state) {
>> +	struct drm_atomic_state *old_state = old_bridge_state->base.state;
>> +	struct drm_crtc *crtc;
>> +	struct drm_crtc_state *new_crtc_state = NULL;
>> +	struct msm_dp_bridge *dp_bridge = to_dp_bridge(drm_bridge);
>> +	struct msm_dp *dp_display = dp_bridge->dp_display;
>> +
>> +	crtc = dp_bridge_get_old_connector_crtc(dp_display, old_state);
>> +	if (!crtc)
>> +		return;
>> +
>> +	new_crtc_state = drm_atomic_get_new_crtc_state(old_state, crtc);
>> +	if (!new_crtc_state)
>> +		return;
>> +
>> +	/*
>> +	 * Self refresh mode is already set in edp_bridge_atomic_disable.
>> +	 */
>> +	if (new_crtc_state->self_refresh_active)
>> +		return;
>> +
>> +	dp_bridge_post_disable(drm_bridge);
>> +}
>> +
>> +static const struct drm_bridge_funcs edp_bridge_ops = {
>> +	.atomic_enable       = edp_bridge_atomic_enable,
>> +	.atomic_disable      = edp_bridge_atomic_disable,
>> +	.atomic_post_disable = edp_bridge_atomic_post_disable,
>
>If you are using the atomic ops here, please switch dp_bridge_ops to atomic
>too.
>
>> +	.mode_set     = dp_bridge_mode_set,
>
>.mode_valid is missing. Is this intentional?
>

No. it was missed. Will add it

>> +	.atomic_reset = drm_atomic_helper_bridge_reset,
>> +	.atomic_duplicate_state =
>drm_atomic_helper_bridge_duplicate_state,
>> +	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
>> +	.atomic_check = edp_bridge_atomic_check, };
>> +
>>   static const struct drm_bridge_funcs dp_bridge_ops = {
>>   	.enable       = dp_bridge_enable,
>>   	.disable      = dp_bridge_disable,
>> @@ -84,7 +247,7 @@ struct drm_bridge *dp_bridge_init(struct msm_dp
>*dp_display, struct drm_device *
>>   	dp_bridge->dp_display = dp_display;
>>
>>   	bridge = &dp_bridge->bridge;
>> -	bridge->funcs = &dp_bridge_ops;
>> +	bridge->funcs = &edp_bridge_ops;
>
>Please use ternary operator here.
>

Okay

>>   	bridge->type = dp_display->connector_type;
>>
>>   	/*
>> @@ -99,6 +262,7 @@ struct drm_bridge *dp_bridge_init(struct msm_dp
>*dp_display, struct drm_device *
>>   	 *   modes.
>>   	 */
>>   	if (!dp_display->is_edp) {
>> +		bridge->funcs = &dp_bridge_ops;
>>   		bridge->ops =
>>   			DRM_BRIDGE_OP_DETECT |
>>   			DRM_BRIDGE_OP_HPD |
>> diff --git a/drivers/gpu/drm/msm/dp/dp_link.c
>> b/drivers/gpu/drm/msm/dp/dp_link.c
>> index 36f0af0..81ac3c1 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_link.c
>> +++ b/drivers/gpu/drm/msm/dp/dp_link.c
>> @@ -934,6 +934,38 @@ static int
>dp_link_process_phy_test_pattern_request(
>>   	return 0;
>>   }
>>
>> +static bool dp_link_read_psr_error_status(struct dp_link_private
>> +*link) {
>> +	u8 status;
>> +
>> +	drm_dp_dpcd_read(link->aux, DP_PSR_ERROR_STATUS, &status, 1);
>> +
>> +	if (status & DP_PSR_LINK_CRC_ERROR)
>> +		DRM_ERROR("PSR LINK CRC ERROR\n");
>> +	else if (status & DP_PSR_RFB_STORAGE_ERROR)
>> +		DRM_ERROR("PSR RFB STORAGE ERROR\n");
>> +	else if (status & DP_PSR_VSC_SDP_UNCORRECTABLE_ERROR)
>> +		DRM_ERROR("PSR VSC SDP UNCORRECTABLE ERROR\n");
>> +	else
>> +		return 0;
>> +
>> +	return 1;
>
>If it's bool, it should return true/false and not 0/1.
>

Okay. Will change it

>> +}
>> +
>> +static bool dp_link_psr_capability_changed(struct dp_link_private
>> +*link) {
>> +	u8 status;
>> +
>> +	drm_dp_dpcd_read(link->aux, DP_PSR_ESI, &status, 1);
>> +
>> +	if (status & DP_PSR_CAPS_CHANGE)
>> +		drm_dbg_dp(link->drm_dev, "PSR Capability Change\n");
>> +	else
>> +		return 0;
>> +
>> +	return 1;
>
>if (!(satus & DP_PSR_CAPS_CHANGE))
>     return false;
>
>drm_dbg_dp(..)
>return true;
>
>
>Or
>
>if (status & DP_PSR_CAPS_CHANGE) {
>   drm_dbg_dp(...)
>   return true;
>}
>
>return false;
>
>

Okay. Will change it.

>> +}
>> +
>>   static u8 get_link_status(const u8 link_status[DP_LINK_STATUS_SIZE], int r)
>>   {
>>   	return link_status[r - DP_LANE0_1_STATUS]; @@ -1053,6 +1085,10
>@@
>> int dp_link_process_request(struct dp_link *dp_link)
>>   		dp_link->sink_request |= DP_TEST_LINK_TRAINING;
>>   	} else if (!dp_link_process_phy_test_pattern_request(link)) {
>>   		dp_link->sink_request |=
>DP_TEST_LINK_PHY_TEST_PATTERN;
>> +	} else if (dp_link_read_psr_error_status(link)) {
>> +		DRM_ERROR("PSR IRQ_HPD received\n");
>> +	} else if (dp_link_psr_capability_changed(link)) {
>> +		drm_dbg_dp(link->drm_dev, "PSR Capabiity changed");
>>   	} else {
>>   		ret = dp_link_process_link_status_update(link);
>>   		if (!ret) {
>> diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c
>> b/drivers/gpu/drm/msm/dp/dp_panel.c
>> index 5149ceb..8bf8ab4 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_panel.c
>> +++ b/drivers/gpu/drm/msm/dp/dp_panel.c
>> @@ -20,6 +20,27 @@ struct dp_panel_private {
>>   	bool aux_cfg_update_done;
>>   };
>>
>> +static void dp_panel_read_psr_cap(struct dp_panel_private *panel) {
>> +	ssize_t rlen;
>> +	struct dp_panel *dp_panel;
>> +
>> +	dp_panel = &panel->dp_panel;
>> +
>> +	/* edp sink */
>> +	if (dp_panel->dpcd[DP_EDP_CONFIGURATION_CAP]) {
>> +		rlen = drm_dp_dpcd_read(panel->aux, DP_PSR_SUPPORT,
>> +				&dp_panel->psr_cap, sizeof(dp_panel-
>>psr_cap));
>> +		if (rlen == sizeof(dp_panel->psr_cap)) {
>> +			drm_dbg_dp(panel->drm_dev,
>> +				"psr version: 0x%x, psr_cap: 0x%x\n",
>> +				dp_panel->psr_cap.version,
>> +				dp_panel->psr_cap.capabilities);
>> +		} else
>> +			DRM_ERROR("failed to read psr info, rlen=%zd\n",
>rlen);
>> +	}
>> +}
>> +
>>   static int dp_panel_read_dpcd(struct dp_panel *dp_panel)
>>   {
>>   	int rc = 0;
>> @@ -106,6 +127,7 @@ static int dp_panel_read_dpcd(struct dp_panel
>*dp_panel)
>>   		}
>>   	}
>>
>> +	dp_panel_read_psr_cap(panel);
>>   end:
>>   	return rc;
>>   }
>> diff --git a/drivers/gpu/drm/msm/dp/dp_panel.h
>> b/drivers/gpu/drm/msm/dp/dp_panel.h
>> index d861197a..2d0826a 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_panel.h
>> +++ b/drivers/gpu/drm/msm/dp/dp_panel.h
>> @@ -34,6 +34,11 @@ struct dp_panel_in {
>>   	struct dp_catalog *catalog;
>>   };
>>
>> +struct dp_panel_psr {
>> +	u8 version;
>> +	u8 capabilities;
>> +};
>> +
>>   struct dp_panel {
>>   	/* dpcd raw data */
>>   	u8 dpcd[DP_RECEIVER_CAP_SIZE + 1];
>> @@ -46,6 +51,7 @@ struct dp_panel {
>>   	struct edid *edid;
>>   	struct drm_connector *connector;
>>   	struct dp_display_mode dp_mode;
>> +	struct dp_panel_psr psr_cap;
>>   	bool video_test;
>>
>>   	u32 vic;
>> diff --git a/drivers/gpu/drm/msm/dp/dp_reg.h
>> b/drivers/gpu/drm/msm/dp/dp_reg.h index 2686028..ea85a69 100644
>> --- a/drivers/gpu/drm/msm/dp/dp_reg.h
>> +++ b/drivers/gpu/drm/msm/dp/dp_reg.h
>> @@ -22,6 +22,20 @@
>>   #define REG_DP_INTR_STATUS2			(0x00000024)
>>   #define REG_DP_INTR_STATUS3			(0x00000028)
>>
>> +#define REG_DP_INTR_STATUS4			(0x0000002C)
>> +#define PSR_UPDATE_INT				(0x00000001)
>> +#define PSR_CAPTURE_INT				(0x00000004)
>> +#define PSR_EXIT_INT				(0x00000010)
>> +#define PSR_UPDATE_ERROR_INT			(0x00000040)
>> +#define PSR_WAKE_ERROR_INT			(0x00000100)
>> +
>> +#define REG_DP_INTR_MASK4			(0x00000030)
>> +#define PSR_UPDATE_MASK				(0x00000001)
>> +#define PSR_CAPTURE_MASK			(0x00000002)
>> +#define PSR_EXIT_MASK				(0x00000004)
>> +#define PSR_UPDATE_ERROR_MASK			(0x00000008)
>> +#define PSR_WAKE_ERROR_MASK			(0x00000010)
>> +
>>   #define REG_DP_DP_HPD_CTRL			(0x00000000)
>>   #define DP_DP_HPD_CTRL_HPD_EN			(0x00000001)
>>
>> @@ -164,6 +178,16 @@
>>   #define MMSS_DP_AUDIO_TIMING_RBR_48		(0x00000094)
>>   #define MMSS_DP_AUDIO_TIMING_HBR_48		(0x00000098)
>>
>> +#define REG_PSR_CONFIG				(0x00000100)
>> +#define DISABLE_PSR				(0x00000000)
>> +#define PSR1_SUPPORTED				(0x00000001)
>> +#define PSR2_WITHOUT_FRAMESYNC			(0x00000002)
>> +#define PSR2_WITH_FRAMESYNC			(0x00000003)
>> +
>> +#define REG_PSR_CMD				(0x00000110)
>> +#define PSR_ENTER				(0x00000001)
>> +#define PSR_EXIT				(0x00000002)
>> +
>>   #define MMSS_DP_PSR_CRC_RG			(0x00000154)
>>   #define MMSS_DP_PSR_CRC_B			(0x00000158)
>>
>> @@ -184,6 +208,9 @@
>>   #define MMSS_DP_AUDIO_STREAM_0			(0x00000240)
>>   #define MMSS_DP_AUDIO_STREAM_1			(0x00000244)
>>
>> +#define MMSS_DP_SDP_CFG3			(0x0000024c)
>> +#define UPDATE_SDP				(0x00000001)
>> +
>>   #define MMSS_DP_EXTENSION_0			(0x00000250)
>>   #define MMSS_DP_EXTENSION_1			(0x00000254)
>>   #define MMSS_DP_EXTENSION_2			(0x00000258)
>
>
>--
>With best wishes
>Dmitry

Thank you,
Sankeerth

^ permalink raw reply	[flat|nested] 32+ messages in thread

* RE: [v3 3/5] drm/bridge: add psr support during panel bridge enable & disable sequence
  2022-06-21 18:59     ` Dmitry Baryshkov
@ 2022-06-29 13:48       ` Sankeerth Billakanti (QUIC)
  -1 siblings, 0 replies; 32+ messages in thread
From: Sankeerth Billakanti (QUIC) @ 2022-06-29 13:48 UTC (permalink / raw)
  To: dmitry.baryshkov, Vinod Polimera (QUIC),
	dri-devel, linux-arm-msm, freedreno, devicetree
  Cc: linux-kernel, robdclark, dianders, swboyd, Kalyan Thota (QUIC),
	Sankeerth Billakanti (QUIC), Vishnuvardhan Prodduturi (QUIC),
	Aravind Venkateswaran (QUIC), Abhinav Kumar (QUIC),
	Kuogee Hsieh (QUIC)

Hi Dmitry,

>On 21/06/2022 13:53, Vinod Polimera wrote:
>> This change avoids panel prepare/unprepare based on self-refresh
>> state.
>>
>> Signed-off-by: Sankeerth Billakanti <quic_sbillaka@quicinc.com>
>> Signed-off-by: Kalyan Thota <quic_kalyant@quicinc.com>
>> Signed-off-by: Vinod Polimera <quic_vpolimer@quicinc.com>
>> ---
>>   drivers/gpu/drm/bridge/panel.c | 102
>+++++++++++++++++++++++++++++++++++++++--
>>   1 file changed, 98 insertions(+), 4 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/bridge/panel.c
>> b/drivers/gpu/drm/bridge/panel.c index 59a3496..6b09ae0 100644
>> --- a/drivers/gpu/drm/bridge/panel.c
>> +++ b/drivers/gpu/drm/bridge/panel.c
>> @@ -41,6 +41,40 @@ static int panel_bridge_connector_get_modes(struct
>drm_connector *connector)
>>   	return drm_panel_get_modes(panel_bridge->panel, connector);
>>   }
>>
>> +static struct drm_crtc *bridge_drm_get_old_connector_crtc(struct
>drm_encoder *encoder,
>> +							struct
>drm_atomic_state *state) {
>> +	struct drm_connector *connector;
>> +	struct drm_connector_state *conn_state;
>> +
>> +	connector = drm_atomic_get_old_connector_for_encoder(state,
>encoder);
>> +	if (!connector)
>> +		return NULL;
>> +
>> +	conn_state = drm_atomic_get_old_connector_state(state,
>connector);
>> +	if (!conn_state)
>> +		return NULL;
>> +
>> +	return conn_state->crtc;
>> +}
>> +
>> +static struct drm_crtc *bridge_drm_get_new_connector_crtc(struct
>drm_encoder *encoder,
>> +							struct
>drm_atomic_state *state) {
>> +	struct drm_connector *connector;
>> +	struct drm_connector_state *conn_state;
>> +
>> +	connector = drm_atomic_get_new_connector_for_encoder(state,
>encoder);
>> +	if (!connector)
>> +		return NULL;
>> +
>> +	conn_state = drm_atomic_get_new_connector_state(state,
>connector);
>> +	if (!conn_state)
>> +		return NULL;
>> +
>> +	return conn_state->crtc;
>> +}
>
>As I wrote earlier, this should become generic drm helpers.
>

Yes, will move it.

>> +
>>   static const struct drm_connector_helper_funcs
>>   panel_bridge_connector_helper_funcs = {
>>   	.get_modes = panel_bridge_connector_get_modes, @@ -108,30
>+142,90
>> @@ static void panel_bridge_detach(struct drm_bridge *bridge)
>>   		drm_connector_cleanup(connector);
>>   }
>>
>> -static void panel_bridge_atomic_pre_enable(struct drm_bridge *bridge)
>> +static void panel_bridge_atomic_pre_enable(struct drm_bridge *bridge,
>> +					struct drm_bridge_state
>*old_bridge_state)
>
>This must be a part of the previous patch?
>

Yes, it should be moved to that patch.

>>   {
>>   	struct panel_bridge *panel_bridge =
>> drm_bridge_to_panel_bridge(bridge);
>> +	struct drm_atomic_state *old_state = old_bridge_state->base.state;
>> +	struct drm_encoder *encoder = bridge->encoder;
>> +	struct drm_crtc *crtc;
>> +	struct drm_crtc_state *old_crtc_state;
>> +
>> +	crtc = bridge_drm_get_new_connector_crtc(encoder, old_state);
>> +	if (!crtc)
>> +		return;
>
>Why? And why do you ask for the new crtc from the old state?
>

If the previous bridge disable and post_disable calls were issued just to enter psr,
then the panel power and backlight will still be on.

We need to know the psr status of the old state of the crtc to decide whether to
enable the panel power or just early return.

old_state is the atomic_state object. Will change the variable name to atomic_state.

>> +
>> +	old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc);
>> +
>> +	/* Don't touch the panel if we're coming back from self refresh state
>*/
>> +	if (old_crtc_state && old_crtc_state->self_refresh_active)
>> +		return;
>>
>>   	drm_panel_prepare(panel_bridge->panel);
>>   }
>>
>> -static void panel_bridge_atomic_enable(struct drm_bridge *bridge)
>> +static void panel_bridge_atomic_enable(struct drm_bridge *bridge,
>> +					struct drm_bridge_state
>*old_bridge_state)
>>   {
>>   	struct panel_bridge *panel_bridge =
>> drm_bridge_to_panel_bridge(bridge);
>> +	struct drm_atomic_state *old_state = old_bridge_state->base.state;
>> +	struct drm_encoder *encoder = bridge->encoder;
>> +	struct drm_crtc *crtc;
>> +	struct drm_crtc_state *old_crtc_state;
>> +
>> +	crtc = bridge_drm_get_new_connector_crtc(encoder, old_state);
>> +	if (!crtc)
>> +		return;
>> +
>> +	old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc);
>> +
>> +	/* Don't touch the panel if we're coming back from self refresh state
>*/
>> +	if (old_crtc_state && old_crtc_state->self_refresh_active)
>> +		return;
>>
>>   	drm_panel_enable(panel_bridge->panel);
>>   }
>>
>> -static void panel_bridge_atomic_disable(struct drm_bridge *bridge)
>> +static void panel_bridge_atomic_disable(struct drm_bridge *bridge,
>> +					struct drm_bridge_state
>*old_bridge_state)
>>   {
>>   	struct panel_bridge *panel_bridge =
>> drm_bridge_to_panel_bridge(bridge);
>> +	struct drm_atomic_state *old_state = old_bridge_state->base.state;
>> +	struct drm_encoder *encoder = bridge->encoder;
>> +	struct drm_crtc *crtc;
>> +	struct drm_crtc_state *new_crtc_state;
>> +
>> +	crtc = bridge_drm_get_old_connector_crtc(encoder, old_state);
>> +	if (!crtc)
>> +		return;
>> +
>> +	new_crtc_state = drm_atomic_get_new_crtc_state(old_state, crtc);
>
>This doesn't sound right too.
>

There is a risk of crtc being deallocated if the disable call came during screen off.
To be on safer side, we are getting the old crtc and check for the appropriate crtc state.
I believe the old_state variable name is causing a confusion. I will change the name to
atomic_state.

>> +
>> +	/* Don't do a full disable on PSR transitions if new state is self refresh
>state */
>> +	if (new_crtc_state && new_crtc_state->self_refresh_active)
>> +		return;
>>
>>   	drm_panel_disable(panel_bridge->panel);
>>   }
>>
>> -static void panel_bridge_atomic_post_disable(struct drm_bridge
>> *bridge)
>> +static void panel_bridge_atomic_post_disable(struct drm_bridge *bridge,
>> +					struct drm_bridge_state
>*old_bridge_state)
>>   {
>>   	struct panel_bridge *panel_bridge =
>> drm_bridge_to_panel_bridge(bridge);
>> +	struct drm_atomic_state *old_state = old_bridge_state->base.state;
>> +	struct drm_encoder *encoder = bridge->encoder;
>> +	struct drm_crtc *crtc;
>> +	struct drm_crtc_state *new_crtc_state;
>> +
>> +	crtc = bridge_drm_get_old_connector_crtc(encoder, old_state);
>> +	if (!crtc)
>> +		return;
>> +
>> +	new_crtc_state = drm_atomic_get_new_crtc_state(old_state, crtc);
>> +
>> +	/* Don't do unprepare on PSR transitions if new state is self refresh
>state */
>> +	if (new_crtc_state && new_crtc_state->self_refresh_active)
>> +		return;
>>
>>   	drm_panel_unprepare(panel_bridge->panel);
>>   }
>
>
>--
>With best wishes
>Dmitry

Thank you,
Sankeerth

^ permalink raw reply	[flat|nested] 32+ messages in thread

* RE: [v3 3/5] drm/bridge: add psr support during panel bridge enable & disable sequence
@ 2022-06-29 13:48       ` Sankeerth Billakanti (QUIC)
  0 siblings, 0 replies; 32+ messages in thread
From: Sankeerth Billakanti (QUIC) @ 2022-06-29 13:48 UTC (permalink / raw)
  To: dmitry.baryshkov, Vinod Polimera (QUIC),
	dri-devel, linux-arm-msm, freedreno, devicetree
  Cc: Kalyan Thota (QUIC), Sankeerth Billakanti (QUIC),
	dianders, Abhinav Kumar (QUIC), Vishnuvardhan Prodduturi (QUIC),
	linux-kernel, swboyd, Aravind Venkateswaran (QUIC),
	Kuogee Hsieh (QUIC)

Hi Dmitry,

>On 21/06/2022 13:53, Vinod Polimera wrote:
>> This change avoids panel prepare/unprepare based on self-refresh
>> state.
>>
>> Signed-off-by: Sankeerth Billakanti <quic_sbillaka@quicinc.com>
>> Signed-off-by: Kalyan Thota <quic_kalyant@quicinc.com>
>> Signed-off-by: Vinod Polimera <quic_vpolimer@quicinc.com>
>> ---
>>   drivers/gpu/drm/bridge/panel.c | 102
>+++++++++++++++++++++++++++++++++++++++--
>>   1 file changed, 98 insertions(+), 4 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/bridge/panel.c
>> b/drivers/gpu/drm/bridge/panel.c index 59a3496..6b09ae0 100644
>> --- a/drivers/gpu/drm/bridge/panel.c
>> +++ b/drivers/gpu/drm/bridge/panel.c
>> @@ -41,6 +41,40 @@ static int panel_bridge_connector_get_modes(struct
>drm_connector *connector)
>>   	return drm_panel_get_modes(panel_bridge->panel, connector);
>>   }
>>
>> +static struct drm_crtc *bridge_drm_get_old_connector_crtc(struct
>drm_encoder *encoder,
>> +							struct
>drm_atomic_state *state) {
>> +	struct drm_connector *connector;
>> +	struct drm_connector_state *conn_state;
>> +
>> +	connector = drm_atomic_get_old_connector_for_encoder(state,
>encoder);
>> +	if (!connector)
>> +		return NULL;
>> +
>> +	conn_state = drm_atomic_get_old_connector_state(state,
>connector);
>> +	if (!conn_state)
>> +		return NULL;
>> +
>> +	return conn_state->crtc;
>> +}
>> +
>> +static struct drm_crtc *bridge_drm_get_new_connector_crtc(struct
>drm_encoder *encoder,
>> +							struct
>drm_atomic_state *state) {
>> +	struct drm_connector *connector;
>> +	struct drm_connector_state *conn_state;
>> +
>> +	connector = drm_atomic_get_new_connector_for_encoder(state,
>encoder);
>> +	if (!connector)
>> +		return NULL;
>> +
>> +	conn_state = drm_atomic_get_new_connector_state(state,
>connector);
>> +	if (!conn_state)
>> +		return NULL;
>> +
>> +	return conn_state->crtc;
>> +}
>
>As I wrote earlier, this should become generic drm helpers.
>

Yes, will move it.

>> +
>>   static const struct drm_connector_helper_funcs
>>   panel_bridge_connector_helper_funcs = {
>>   	.get_modes = panel_bridge_connector_get_modes, @@ -108,30
>+142,90
>> @@ static void panel_bridge_detach(struct drm_bridge *bridge)
>>   		drm_connector_cleanup(connector);
>>   }
>>
>> -static void panel_bridge_atomic_pre_enable(struct drm_bridge *bridge)
>> +static void panel_bridge_atomic_pre_enable(struct drm_bridge *bridge,
>> +					struct drm_bridge_state
>*old_bridge_state)
>
>This must be a part of the previous patch?
>

Yes, it should be moved to that patch.

>>   {
>>   	struct panel_bridge *panel_bridge =
>> drm_bridge_to_panel_bridge(bridge);
>> +	struct drm_atomic_state *old_state = old_bridge_state->base.state;
>> +	struct drm_encoder *encoder = bridge->encoder;
>> +	struct drm_crtc *crtc;
>> +	struct drm_crtc_state *old_crtc_state;
>> +
>> +	crtc = bridge_drm_get_new_connector_crtc(encoder, old_state);
>> +	if (!crtc)
>> +		return;
>
>Why? And why do you ask for the new crtc from the old state?
>

If the previous bridge disable and post_disable calls were issued just to enter psr,
then the panel power and backlight will still be on.

We need to know the psr status of the old state of the crtc to decide whether to
enable the panel power or just early return.

old_state is the atomic_state object. Will change the variable name to atomic_state.

>> +
>> +	old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc);
>> +
>> +	/* Don't touch the panel if we're coming back from self refresh state
>*/
>> +	if (old_crtc_state && old_crtc_state->self_refresh_active)
>> +		return;
>>
>>   	drm_panel_prepare(panel_bridge->panel);
>>   }
>>
>> -static void panel_bridge_atomic_enable(struct drm_bridge *bridge)
>> +static void panel_bridge_atomic_enable(struct drm_bridge *bridge,
>> +					struct drm_bridge_state
>*old_bridge_state)
>>   {
>>   	struct panel_bridge *panel_bridge =
>> drm_bridge_to_panel_bridge(bridge);
>> +	struct drm_atomic_state *old_state = old_bridge_state->base.state;
>> +	struct drm_encoder *encoder = bridge->encoder;
>> +	struct drm_crtc *crtc;
>> +	struct drm_crtc_state *old_crtc_state;
>> +
>> +	crtc = bridge_drm_get_new_connector_crtc(encoder, old_state);
>> +	if (!crtc)
>> +		return;
>> +
>> +	old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc);
>> +
>> +	/* Don't touch the panel if we're coming back from self refresh state
>*/
>> +	if (old_crtc_state && old_crtc_state->self_refresh_active)
>> +		return;
>>
>>   	drm_panel_enable(panel_bridge->panel);
>>   }
>>
>> -static void panel_bridge_atomic_disable(struct drm_bridge *bridge)
>> +static void panel_bridge_atomic_disable(struct drm_bridge *bridge,
>> +					struct drm_bridge_state
>*old_bridge_state)
>>   {
>>   	struct panel_bridge *panel_bridge =
>> drm_bridge_to_panel_bridge(bridge);
>> +	struct drm_atomic_state *old_state = old_bridge_state->base.state;
>> +	struct drm_encoder *encoder = bridge->encoder;
>> +	struct drm_crtc *crtc;
>> +	struct drm_crtc_state *new_crtc_state;
>> +
>> +	crtc = bridge_drm_get_old_connector_crtc(encoder, old_state);
>> +	if (!crtc)
>> +		return;
>> +
>> +	new_crtc_state = drm_atomic_get_new_crtc_state(old_state, crtc);
>
>This doesn't sound right too.
>

There is a risk of crtc being deallocated if the disable call came during screen off.
To be on safer side, we are getting the old crtc and check for the appropriate crtc state.
I believe the old_state variable name is causing a confusion. I will change the name to
atomic_state.

>> +
>> +	/* Don't do a full disable on PSR transitions if new state is self refresh
>state */
>> +	if (new_crtc_state && new_crtc_state->self_refresh_active)
>> +		return;
>>
>>   	drm_panel_disable(panel_bridge->panel);
>>   }
>>
>> -static void panel_bridge_atomic_post_disable(struct drm_bridge
>> *bridge)
>> +static void panel_bridge_atomic_post_disable(struct drm_bridge *bridge,
>> +					struct drm_bridge_state
>*old_bridge_state)
>>   {
>>   	struct panel_bridge *panel_bridge =
>> drm_bridge_to_panel_bridge(bridge);
>> +	struct drm_atomic_state *old_state = old_bridge_state->base.state;
>> +	struct drm_encoder *encoder = bridge->encoder;
>> +	struct drm_crtc *crtc;
>> +	struct drm_crtc_state *new_crtc_state;
>> +
>> +	crtc = bridge_drm_get_old_connector_crtc(encoder, old_state);
>> +	if (!crtc)
>> +		return;
>> +
>> +	new_crtc_state = drm_atomic_get_new_crtc_state(old_state, crtc);
>> +
>> +	/* Don't do unprepare on PSR transitions if new state is self refresh
>state */
>> +	if (new_crtc_state && new_crtc_state->self_refresh_active)
>> +		return;
>>
>>   	drm_panel_unprepare(panel_bridge->panel);
>>   }
>
>
>--
>With best wishes
>Dmitry

Thank you,
Sankeerth

^ permalink raw reply	[flat|nested] 32+ messages in thread

end of thread, other threads:[~2022-06-29 13:48 UTC | newest]

Thread overview: 32+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-06-21 10:53 [v3 0/5] Add PSR support for eDP Vinod Polimera
2022-06-21 10:53 ` Vinod Polimera
2022-06-21 10:53 ` [v3 1/5] drm/msm/dp: Add basic " Vinod Polimera
2022-06-21 10:53   ` Vinod Polimera
2022-06-21 18:53   ` Dmitry Baryshkov
2022-06-21 18:53     ` Dmitry Baryshkov
2022-06-29 13:38     ` Sankeerth Billakanti (QUIC)
2022-06-29 13:38       ` Sankeerth Billakanti (QUIC)
2022-06-21 10:53 ` [v3 2/5] drm/bridge: use atomic enable/disable callbacks for panel bridge functions Vinod Polimera
2022-06-21 10:53   ` Vinod Polimera
2022-06-21 18:54   ` Dmitry Baryshkov
2022-06-21 18:54     ` Dmitry Baryshkov
2022-06-21 18:56     ` Dmitry Baryshkov
2022-06-21 18:56       ` Dmitry Baryshkov
2022-06-21 10:53 ` [v3 3/5] drm/bridge: add psr support during panel bridge enable & disable sequence Vinod Polimera
2022-06-21 10:53   ` Vinod Polimera
2022-06-21 18:59   ` Dmitry Baryshkov
2022-06-21 18:59     ` Dmitry Baryshkov
2022-06-29 13:48     ` Sankeerth Billakanti (QUIC)
2022-06-29 13:48       ` Sankeerth Billakanti (QUIC)
2022-06-21 10:53 ` [v3 4/5] drm/msm/disp/dpu1: use atomic enable/disable callbacks for encoder functions Vinod Polimera
2022-06-21 10:53   ` Vinod Polimera
2022-06-21 19:00   ` Dmitry Baryshkov
2022-06-21 19:00     ` Dmitry Baryshkov
2022-06-21 10:53 ` [v3 5/5] drm/msm/disp/dpu1: add PSR support for eDP interface in dpu driver Vinod Polimera
2022-06-21 10:53   ` Vinod Polimera
2022-06-21 19:09   ` Dmitry Baryshkov
2022-06-21 19:09     ` Dmitry Baryshkov
2022-06-22 21:21   ` kernel test robot
2022-06-22 21:21     ` kernel test robot
2022-06-21 18:40 ` [v3 0/5] Add PSR support for eDP Dmitry Baryshkov
2022-06-21 18:40   ` Dmitry Baryshkov

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.