diff mbox

[3/6] drm/i915: Add support for DRRS set property to switch RR

Message ID 1384841225-4688-4-git-send-email-vandana.kannan@intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

vandana.kannan@intel.com Nov. 19, 2013, 6:07 a.m. UTC
From: Pradeep Bhat <pradeep.bhat@intel.com>

This patch provides a set_property interface for user space to
seamlessly switch between different DRRS refresh rates. The
patch creates the property only if seamless DRRS is supported.
It implements the support for computing Data & Link M/N for any
given Refresh Rate and programs the same in 2nd M/N/TU for
switching to different refresh rate dynamically using DRM set
property IOCTL. The PIPECONF_EDP_RR_MODE_SWITCH bit helps toggle
between alternate refresh rates programmed in 2nd M/N/TU registers.
The user space should use this property to switch to any required
refresh rate based on its policy. This feature enables user space
in acheiving better power savings for certain use cases. This
feature is for PV2 and not PV1.

Signed-off-by: Pradeep Bhat <pradeep.bhat@intel.com>
Signed-off-by: Vandana Kannan <vandana.kannan@intel.com>
---
 drivers/gpu/drm/i915/i915_reg.h  |    1 +
 drivers/gpu/drm/i915/intel_dp.c  |  160 ++++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/i915/intel_drv.h |    6 ++
 3 files changed, 167 insertions(+)
diff mbox

Patch

diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 3f303ba..d1f8cc7 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -3168,6 +3168,7 @@ 
 #define   PIPECONF_INTERLACED_DBL_ILK		(4 << 21) /* ilk/snb only */
 #define   PIPECONF_PFIT_PF_INTERLACED_DBL_ILK	(5 << 21) /* ilk/snb only */
 #define   PIPECONF_INTERLACE_MODE_MASK		(7 << 21)
+#define   PIPECONF_EDP_RR_MODE_SWITCH		(1 << 20)
 #define   PIPECONF_CXSR_DOWNCLOCK	(1<<16)
 #define   PIPECONF_COLOR_RANGE_SELECT	(1 << 13)
 #define   PIPECONF_BPC_MASK	(0x7 << 5)
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 46f202b..ff156d2 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -3118,6 +3118,12 @@  intel_dp_set_property(struct drm_connector *connector,
 	if (ret)
 		return ret;
 
+	if (is_edp(intel_dp) && property ==
+				intel_dp->drrs_state.seamless_drrs_property) {
+		intel_dp_set_drrs_state(connector->dev, val);
+		return 0;
+	}
+
 	if (property == dev_priv->force_audio_property) {
 		int i = val;
 		bool has_audio;
@@ -3300,6 +3306,9 @@  intel_dp_add_properties(struct intel_dp *intel_dp, struct drm_connector *connect
 	intel_dp->color_range_auto = true;
 
 	if (is_edp(intel_dp)) {
+		if (intel_dp->drrs_state.is_drrs_supported !=
+							DRRS_NOT_SUPPORTED)
+			intel_dp_attach_drrs_properties(intel_dp, connector);
 		drm_mode_create_scaling_mode_property(connector->dev);
 		drm_object_attach_property(
 			&connector->base,
@@ -3820,6 +3829,7 @@  intel_dp_find_drrs_lowclk(struct intel_digital_port *intel_dig_port,
 	    dev_priv->vbt.drrs_mode == SEAMLESS_DRRS_SUPPORT) {
 		intel_dp_drrs_modelist_create(intel_dig_port, fixed_mode,
 						temp_mode);
+		mutex_init(&intel_dp->drrs_state.mutex);
 		intel_dp->drrs_state.is_drrs_supported =
 					dev_priv->vbt.drrs_mode;
 		intel_dp->drrs_state.drrs_refresh_rate_type = DRRS_HIGH_RR;
@@ -3829,3 +3839,153 @@  intel_dp_find_drrs_lowclk(struct intel_digital_port *intel_dig_port,
 	return;
 }
 
+void
+intel_dp_attach_drrs_properties(struct intel_dp *intel_dp,
+				struct drm_connector *connector)
+{
+	struct drm_device *dev = connector->dev;
+	struct drm_property *prop;
+	const struct drm_prop_enum_list seamless_drrs_names[] = {
+		{ intel_dp->drrs_state.refresh_rate_array[DRRS_HIGH_RR],
+								"high_rr"},
+		{ intel_dp->drrs_state.refresh_rate_array[DRRS_LOW_RR],
+								"low_rr"},
+		/**
+		 * add more entries if more DRRS RRs supported.
+		 * The no.of entries should be equal to
+		 * DRRS_MAX_RR.
+		 */
+	};
+
+	prop = intel_dp->drrs_state.seamless_drrs_property;
+	if (prop == NULL) {
+		prop = drm_property_create_enum(dev, 0,
+					"seamless_drrs",
+					seamless_drrs_names,
+					ARRAY_SIZE(seamless_drrs_names));
+		if (prop == NULL)
+			return;
+		intel_dp->drrs_state.seamless_drrs_property = prop;
+	}
+	drm_object_attach_property(&connector->base, prop,
+				intel_dp->drrs_state.refresh_rate_array[0]);
+}
+
+static void
+intel_dp_set_m2_n2(struct intel_crtc *crtc, struct intel_link_m_n *m_n)
+{
+	struct drm_device *dev = crtc->base.dev;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	enum transcoder transcoder = crtc->config.cpu_transcoder;
+
+	if (INTEL_INFO(dev)->gen >= 5 && INTEL_INFO(dev)->gen < 8) {
+		I915_WRITE(PIPE_DATA_M2(transcoder),
+			   TU_SIZE(m_n->tu) | m_n->gmch_m);
+		I915_WRITE(PIPE_DATA_N2(transcoder), m_n->gmch_n);
+		I915_WRITE(PIPE_LINK_M2(transcoder), m_n->link_m);
+		I915_WRITE(PIPE_LINK_N2(transcoder), m_n->link_n);
+	}
+	return;
+}
+
+void
+intel_dp_set_drrs_state(struct drm_device *dev, int refresh_rate)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct drm_mode_config *mode_config = &dev->mode_config;
+	struct intel_encoder *encoder;
+	struct intel_dp *intel_dp = NULL;
+	struct intel_crtc_config *config = NULL;
+	struct intel_crtc *intel_crtc = NULL;
+	struct intel_connector *intel_connector = NULL;
+	bool found_edp = false;
+	u32 reg, val;
+	int index = 0;
+
+	if (refresh_rate <= 0) {
+		DRM_INFO("Refresh rate should be positive non-zero.\n");
+		goto out;
+	}
+
+	list_for_each_entry(encoder, &mode_config->encoder_list, base.head) {
+		if (encoder->type == INTEL_OUTPUT_EDP) {
+			intel_dp = enc_to_intel_dp(&encoder->base);
+			intel_crtc = encoder->new_crtc;
+			if (!intel_crtc) {
+				DRM_INFO("DRRS: intel_crtc not initialized\n");
+				goto out;
+			}
+			config = &intel_crtc->config;
+			intel_connector = intel_dp->attached_connector;
+			found_edp = true;
+			break;
+		}
+	}
+
+	if (!found_edp) {
+		DRM_INFO("DRRS supported for eDP only.\n");
+		goto out;
+	}
+
+	if (intel_dp->drrs_state.is_drrs_supported < SEAMLESS_DRRS_SUPPORT) {
+		DRM_INFO("Seamless DRRS not supported.\n");
+		goto out;
+	}
+
+	for (; index < DRRS_MAX_RR; index++) {
+		if (intel_dp->drrs_state.refresh_rate_array[index] ==
+								refresh_rate)
+			break;
+	}
+
+	if (index >= DRRS_MAX_RR) {
+		DRM_INFO("Invalid refresh rate requested for DRRS.\n");
+		goto out;
+	}
+
+	if (index == intel_dp->drrs_state.drrs_refresh_rate_type) {
+		DRM_INFO("DRRS requested for previously set RR...ignoring\n");
+		goto out;
+	}
+
+	if (!intel_crtc->active) {
+		DRM_INFO("eDP encoder has been disabled. CRTC not Active\n");
+		goto out;
+	}
+
+	mutex_lock(&intel_dp->drrs_state.mutex);
+
+	intel_link_compute_m_n(config->pipe_bpp, intel_dp->lane_count,
+				intel_dp->drrs_state.pixel_clock[index],
+				config->port_clock,
+				&intel_dp->drrs_state.dp_m2_n2);
+
+	if (INTEL_INFO(dev)->gen >= 8)
+		intel_dp_set_m2_n2(intel_crtc, &intel_dp->drrs_state.dp_m2_n2);
+	else {
+		/* Haswell and below */
+		reg = PIPECONF(intel_crtc->config.cpu_transcoder);
+		val = I915_READ(reg);
+		if (index > DRRS_HIGH_RR) {
+			if ((val & PIPECONF_EDP_RR_MODE_SWITCH) != 0) {
+				val &= ~PIPECONF_EDP_RR_MODE_SWITCH;
+				I915_WRITE(reg, val);
+			}
+			intel_dp_set_m2_n2(intel_crtc,
+				&intel_dp->drrs_state.dp_m2_n2);
+			val |= PIPECONF_EDP_RR_MODE_SWITCH;
+		} else
+			val &= ~PIPECONF_EDP_RR_MODE_SWITCH;
+		I915_WRITE(reg, val);
+	}
+
+	intel_dp->drrs_state.drrs_refresh_rate_type = index;
+	DRM_INFO("eDP Refresh Rate set to : %dHz\n",
+			intel_dp->drrs_state.refresh_rate_array[index]);
+
+	mutex_unlock(&intel_dp->drrs_state.mutex);
+
+out:
+	return;
+}
+
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index bd7964d..7c22fcf 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -478,6 +478,9 @@  struct drrs_info {
 	int drrs_refresh_rate_type;
 	int refresh_rate_array[DRRS_MAX_RR];
 	int pixel_clock[DRRS_MAX_RR];
+	struct intel_link_m_n dp_m2_n2;
+	struct drm_property *seamless_drrs_property;
+	struct mutex mutex;
 };
 
 struct intel_dp {
@@ -824,6 +827,9 @@  int intel_overlay_attrs(struct drm_device *dev, void *data,
 extern void intel_dp_find_drrs_lowclk(struct intel_digital_port *intel_dig_port,
 				      struct intel_connector *intel_connector,
 				      struct drm_display_mode *fixed_mode);
+extern void intel_dp_set_drrs_state(struct drm_device *dev, int refresh_rate);
+extern void intel_dp_attach_drrs_properties(struct intel_dp *intel_dp,
+					    struct drm_connector *connector);
 
 /* intel_panel.c */
 int intel_panel_init(struct intel_panel *panel,