diff mbox series

[v3] drm/i915/lspcon: enter standby mode to enhance power saving

Message ID 20201118132127.28134-1-shawn.c.lee@intel.com (mailing list archive)
State New, archived
Headers show
Series [v3] drm/i915/lspcon: enter standby mode to enhance power saving | expand

Commit Message

Lee, Shawn C Nov. 18, 2020, 1:21 p.m. UTC
After system boot up, LSPCON will be configured as PCON mode.
But it never go into power saving state. Source driver can
do the following. Then LSPCON can enter standby mode
automatically to save more power.

1. At PCON mode, source driver write 0x2 to DPCD 600h.
2. At LS mode, try to disable DP_DUAL_MODE_TMDS_OEN.

v2: fix typo
v3: After turn main lin off, found particular monitor
    trigger HPD to LSPCON. Then short HPD would forward
    to source. If driver did not enable display output
    when received this HPD. Source should request LSPCON to
    enter standby mode again.

Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Jani Nikula <jani.nikula@linux.intel.com>
Cc: Uma Shankar <uma.shankar@intel.com>
Cc: Cooper Chiou <cooper.chiou@intel.com>
Cc: Khaled Almahallawy <khaled.almahallawy@intel.com>
Signed-off-by: Lee Shawn C <shawn.c.lee@intel.com>
---
 drivers/gpu/drm/i915/display/intel_dp.c     | 11 +++++++-
 drivers/gpu/drm/i915/display/intel_lspcon.c | 30 +++++++++++++++++++++
 drivers/gpu/drm/i915/display/intel_lspcon.h |  1 +
 3 files changed, 41 insertions(+), 1 deletion(-)
diff mbox series

Patch

diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c
index ec8359f03aaf..7876d785b50f 100644
--- a/drivers/gpu/drm/i915/display/intel_dp.c
+++ b/drivers/gpu/drm/i915/display/intel_dp.c
@@ -6184,6 +6184,7 @@  static bool
 intel_dp_short_pulse(struct intel_dp *intel_dp)
 {
 	struct drm_i915_private *dev_priv = dp_to_i915(intel_dp);
+	struct intel_lspcon *lspcon = dp_to_lspcon(intel_dp);
 	u8 old_sink_count = intel_dp->sink_count;
 	bool ret;
 
@@ -6211,6 +6212,9 @@  intel_dp_short_pulse(struct intel_dp *intel_dp)
 	/* Handle CEC interrupts, if any */
 	drm_dp_cec_irq(&intel_dp->aux);
 
+	if (lspcon && lspcon->active)
+		lspcon_standby(dp_to_dig_port(intel_dp));
+
 	/* defer to the hotplug work for link retraining if needed */
 	if (intel_dp_needs_link_retrain(intel_dp))
 		return false;
@@ -6536,6 +6540,7 @@  intel_dp_detect(struct drm_connector *connector,
 	struct drm_i915_private *dev_priv = to_i915(connector->dev);
 	struct intel_dp *intel_dp = intel_attached_dp(to_intel_connector(connector));
 	struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
+	struct intel_lspcon *lspcon = dp_to_lspcon(intel_dp);
 	struct intel_encoder *encoder = &dig_port->base;
 	enum drm_connector_status status;
 
@@ -6632,9 +6637,13 @@  intel_dp_detect(struct drm_connector *connector,
 	intel_dp_check_service_irq(intel_dp);
 
 out:
-	if (status != connector_status_connected && !intel_dp->is_mst)
+	if (status != connector_status_connected && !intel_dp->is_mst) {
 		intel_dp_unset_edid(intel_dp);
 
+		if (lspcon && lspcon->active)
+			lspcon_standby(dp_to_dig_port(intel_dp));
+	}
+
 	/*
 	 * Make sure the refs for power wells enabled during detect are
 	 * dropped to avoid a new detect cycle triggered by HPD polling.
diff --git a/drivers/gpu/drm/i915/display/intel_lspcon.c b/drivers/gpu/drm/i915/display/intel_lspcon.c
index e37d45e531df..700f5604d9f6 100644
--- a/drivers/gpu/drm/i915/display/intel_lspcon.c
+++ b/drivers/gpu/drm/i915/display/intel_lspcon.c
@@ -550,6 +550,36 @@  static bool lspcon_init(struct intel_digital_port *dig_port)
 	return true;
 }
 
+void lspcon_standby(struct intel_digital_port *dig_port)
+{
+	struct intel_dp *dp = &dig_port->dp;
+	u8 align_status = 0xff, training_pattern = 0xff;
+
+	if (drm_dp_dpcd_readb(&dp->aux, DP_LANE_ALIGN_STATUS_UPDATED, &align_status) <= 0) {
+		DRM_ERROR("LSPCON failed to read align status\n");
+		return;
+	}
+
+	if (drm_dp_dpcd_readb(&dp->aux, DP_TRAINING_PATTERN_SET, &training_pattern) <= 0) {
+		DRM_ERROR("LSPCON failed to read training pattern set\n");
+		return;
+	}
+
+	/*
+	 * If link trainig is ongoing. Or sink updated link align status.
+	 * Source driver should not set LSPCON power state to D3.
+	 */
+	if (align_status || training_pattern) {
+		DRM_DEBUG_KMS("LSPCON link training or display is working\n");
+		DRM_DEBUG_KMS("LSPCON DPCD register 0102h = %x, 0204h = 0x%x\n",
+			      training_pattern, align_status);
+		return;
+	}
+
+	if (drm_dp_dpcd_writeb(&dp->aux, DP_SET_POWER, DP_SET_POWER_D3) <= 0)
+		DRM_DEBUG_KMS("LSPCON failed to write power state to D3\n");
+}
+
 void lspcon_resume(struct intel_digital_port *dig_port)
 {
 	struct intel_lspcon *lspcon = &dig_port->lspcon;
diff --git a/drivers/gpu/drm/i915/display/intel_lspcon.h b/drivers/gpu/drm/i915/display/intel_lspcon.h
index b03dcb7076d8..658a2e5b22db 100644
--- a/drivers/gpu/drm/i915/display/intel_lspcon.h
+++ b/drivers/gpu/drm/i915/display/intel_lspcon.h
@@ -16,6 +16,7 @@  struct intel_encoder;
 struct intel_lspcon;
 
 void lspcon_resume(struct intel_digital_port *dig_port);
+void lspcon_standby(struct intel_digital_port *dig_port);
 void lspcon_wait_pcon_mode(struct intel_lspcon *lspcon);
 void lspcon_write_infoframe(struct intel_encoder *encoder,
 			    const struct intel_crtc_state *crtc_state,