[RFC PATCH 13/18] drm/i915: MEDIA_RR support in general DRRS state machine

Ramalingam C
2015-06-26T14:00:31Z

Based on the minimum requirement of the vrefresh to support a
content's playback, we can place a drrs request from userspace
to kernel. For example 24FPS video content can be played at
48 vrefresh without any quality degradation.

This patch implements required changes in generic DRRS state machine.

Signed-off-by: Ramalingam C <ramalingam.c@intel.com>
---
drivers/gpu/drm/i915/i915_drv.h | 7 ++
drivers/gpu/drm/i915/intel_drrs.c | 161 +++++++++++++++++++++++++++++++++++--
drivers/gpu/drm/i915/intel_drrs.h | 5 +-
3 files changed, 166 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 677293d..dbdba44 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -948,10 +948,12 @@ enum drrs_support_type {
* Different DRRS States:
* DRRS_HIGH_RR : Refreshrate of Fixed mode. [Maximum Vrefresh]
* DRRS_LOW_RR : Refreshrate of Downclock mode. [Minimum vrefresh]
+ * DRRS_MEDIA_RR: Refreshrate of requested from userspace. [<Max && >= Min]
*/
enum drrs_refresh_rate_type {
DRRS_HIGH_RR,
DRRS_LOW_RR,
+ DRRS_MEDIA_RR,
DRRS_MAX_RR,
};

@@ -983,6 +985,8 @@ struct drrs_encoder_ops {
void (*exit)(struct i915_drrs *);
void (*set_drrs_state)(struct i915_drrs *);
bool (*is_drrs_hr_state_pending)(struct i915_drrs *);
+ bool (*is_mp_drrs_req)(struct i915_drrs *,
+ struct drm_display_mode *);
};

struct i915_drrs {
@@ -992,6 +996,9 @@ struct i915_drrs {
/* Whether DRRS is supported on this Panel */
bool has_drrs;

+ /* Flag to resume the Idleness DRRS from Media Playback */
+ bool resume_idleness_detection;
+
/* Holds the DRRS state machine states */
struct drrs_info drrs_state;

diff --git a/drivers/gpu/drm/i915/intel_drrs.c b/drivers/gpu/drm/i915/intel_drrs.c
index 2901832..7f66be4 100644
--- a/drivers/gpu/drm/i915/intel_drrs.c
+++ b/drivers/gpu/drm/i915/intel_drrs.c
@@ -111,6 +111,13 @@ bool is_cloned_mode_active(struct drm_device *dev)
return false;
}

+static inline bool
+is_media_playback_drrs_in_progress(struct drrs_info *drrs_state)
+{
+ return drrs_state->current_rr_type == DRRS_MEDIA_RR ||
+ drrs_state->target_rr_type == DRRS_MEDIA_RR;
+}
+
void intel_set_drrs_state(struct i915_drrs *drrs)
{
struct drrs_info *drrs_state;
@@ -157,7 +164,8 @@ void intel_set_drrs_state(struct i915_drrs *drrs)
return;
}

- if (drrs_state->target_rr_type == drrs_state->current_rr_type) {
+ if (drrs_state->target_rr_type == drrs_state->current_rr_type &&
+ drrs_state->current_rr_type != DRRS_MEDIA_RR) {
DRM_INFO("Requested for previously set RR. Ignoring\n");
return;
}
@@ -170,19 +178,24 @@ void intel_set_drrs_state(struct i915_drrs *drrs)
* As only DSI has SEAMLESS_DRRS_SUPPORT_SW.
*/
/*
- * TODO: Protect the access to the crtc mode with corresponding
+ * Protect the access to the crtc mode with corresponding
* mutex in case of Idleness DRRS. As media playback update
* will happen under crtc modeset lock protection
*/
- drm_modeset_lock(&intel_crtc->base.mutex, NULL);
+ if (!is_media_playback_drrs_in_progress(drrs_state))
+ drm_modeset_lock(&intel_crtc->base.mutex, NULL);
intel_crtc->base.mode.clock = target_mode->clock;
intel_crtc->base.mode.vrefresh = target_mode->vrefresh;
intel_crtc->config->base.adjusted_mode.clock =
target_mode->clock;
intel_crtc->config->base.adjusted_mode.vrefresh =
target_mode->vrefresh;
- drm_modeset_unlock(&intel_crtc->base.mutex);
+ if (!is_media_playback_drrs_in_progress(drrs_state))
+ drm_modeset_unlock(&intel_crtc->base.mutex);

+ if (drrs_state->current_rr_type == DRRS_MEDIA_RR &&
+ drrs_state->target_rr_type == DRRS_HIGH_RR)
+ drrs->resume_idleness_detection = true;
drrs_state->current_rr_type = drrs_state->target_rr_type;
DRM_INFO("Refresh Rate set to : %dHz\n", refresh_rate);
}
@@ -203,6 +216,11 @@ static void intel_idleness_drrs_work_fn(struct work_struct *__work)
return;

mutex_lock(&drrs->drrs_mutex);
+ if (is_media_playback_drrs_in_progress(&drrs->drrs_state)) {
+ mutex_unlock(&drrs->drrs_mutex);
+ return;
+ }
+
if (panel->target_mode != NULL)
DRM_ERROR("FIXME: We shouldn't be here\n");

@@ -229,7 +247,7 @@ static void intel_cancel_idleness_drrs_work(struct i915_drrs *drrs, bool sync)
drrs->connector->panel.target_mode = NULL;
}

-static void intel_enable_idleness_drrs(struct i915_drrs *drrs)
+void intel_enable_idleness_drrs(struct i915_drrs *drrs)
{
bool force_enable_drrs = false;

@@ -240,6 +258,11 @@ static void intel_enable_idleness_drrs(struct i915_drrs *drrs)
if (drrs->is_clone)
return;

+ if (is_media_playback_drrs_in_progress(&drrs->drrs_state)) {
+ mutex_unlock(&drrs->drrs_mutex);
+ return;
+ }
+
/* Capturing the deferred request for disable_drrs */
if (drrs->drrs_state.type == SEAMLESS_DRRS_SUPPORT_SW &&
drrs->encoder_ops->is_drrs_hr_state_pending) {
@@ -267,6 +290,12 @@ void intel_disable_idleness_drrs(struct i915_drrs *drrs)
struct intel_panel *panel;

mutex_lock(&drrs->drrs_mutex);
+
+ if (is_media_playback_drrs_in_progress(&drrs->drrs_state)) {
+ mutex_unlock(&drrs->drrs_mutex);
+ return;
+ }
+
panel = &drrs->connector->panel;

/* as part of disable DRRS, reset refresh rate to HIGH_RR */
@@ -301,7 +330,6 @@ void intel_drrs_invalidate(struct drm_device *dev,
int pipe;
int index;

-
for (pipe = 0; pipe < 4; pipe++) {
if (frontbuffer_bits & (0xf << (16 * pipe))) {
index = get_drrs_struct_index_for_pipe(dev_priv, pipe);
@@ -375,6 +403,126 @@ void intel_drrs_flush(struct drm_device *dev,
}
}

+/*
+ * Handles the userspace request for MEDIA_RR.
+ */
+int intel_media_playback_drrs_configure(struct intel_crtc *crtc,
+ struct drm_display_mode *mode)
+{
+ struct drm_device *dev = crtc->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct i915_drrs *drrs;
+ struct drrs_info *drrs_state;
+ struct intel_panel *panel;
+ struct drm_crtc *tmp_crtc = NULL;
+ int refresh_rate = mode->vrefresh;
+ int index, crtc_cnt = 0;
+
+ index = get_drrs_struct_index_for_crtc(dev_priv, crtc);
+ if (index < 0)
+ return -EINVAL;
+
+ drrs = dev_priv->drrs[index];
+ if (!drrs || !drrs->has_drrs) {
+ DRM_ERROR("DRRS is not supported\n");
+ return -EPERM;
+ }
+
+ drrs_state = &drrs->drrs_state;
+ panel = &drrs->connector->panel;
+
+ if (refresh_rate < panel->downclock_mode->vrefresh &&
+ refresh_rate > panel->fixed_mode->vrefresh) {
+ DRM_ERROR("Invalid refresh_rate\n");
+ return -EINVAL;
+ }
+
+ intel_cancel_idleness_drrs_work(drrs, false);
+
+ mutex_lock(&drrs->drrs_mutex);
+
+ if (refresh_rate == panel->fixed_mode->vrefresh) {
+ if (drrs_state->current_rr_type == DRRS_MEDIA_RR) {
+
+ /* DRRS_MEDIA_RR -> DRRS_HIGH_RR */
+ if (panel->target_mode)
+ drm_mode_destroy(dev, panel->target_mode);
+ panel->target_mode = panel->fixed_mode;
+ drrs_state->target_rr_type = DRRS_HIGH_RR;
+ } else {
+
+ /*
+ * Invalid Media Playback DRRS request.
+ * Resume the Idleness Detection
+ */
+ DRM_ERROR("Invalid Entry req for mode DRRS_MEDIA_RR\n");
+ mutex_unlock(&drrs->drrs_mutex);
+ intel_disable_idleness_drrs(drrs);
+ intel_enable_idleness_drrs(drrs);
+ return 0;
+ }
+ } else {
+ drrs->is_clone = false;
+ list_for_each_entry(tmp_crtc, &dev->mode_config.crtc_list,
+ head) {
+ if (intel_crtc_active(tmp_crtc)) {
+ if (crtc_cnt) {
+ DRM_DEBUG_KMS(
+ "more than one pipe active\n");
+ drrs->is_clone = true;
+ break;
+ }
+ crtc_cnt++;
+ }
+ }
+
+ if (drrs->is_clone) {
+ if (panel->target_mode &&
+ drrs_state->current_rr_type == DRRS_MEDIA_RR)
+ drm_mode_destroy(dev, panel->target_mode);
+ panel->target_mode = panel->fixed_mode;
+ drrs_state->target_rr_type = DRRS_HIGH_RR;
+ goto set_state;
+ }
+
+ drrs_state->target_rr_type = DRRS_MEDIA_RR;
+
+ if (drrs_state->current_rr_type == DRRS_MEDIA_RR) {
+
+ /* Refresh rate change in Media playback DRRS */
+ if (refresh_rate == panel->target_mode->vrefresh) {
+ DRM_DEBUG("Request for current RR.<%d>\n",
+ panel->target_mode->vrefresh);
+ mutex_unlock(&drrs->drrs_mutex);
+ return 0;
+ }
+ panel->target_mode->vrefresh = refresh_rate;
+ } else {
+
+ /* Entering MEDIA Playback DRRS state */
+ panel->target_mode = drm_mode_duplicate(dev, mode);
+ }
+
+ panel->target_mode->clock = mode->vrefresh * mode->vtotal *
+ mode->htotal / 1000;
+ }
+
+set_state:
+ DRM_DEBUG("cur_rr_type: %d, target_rr_type: %d, target_rr: %d\n",
+ drrs_state->current_rr_type,
+ drrs_state->target_rr_type,
+ panel->target_mode->vrefresh);
+
+ intel_set_drrs_state(drrs);
+ mutex_unlock(&drrs->drrs_mutex);
+
+ if (drrs->resume_idleness_detection) {
+ intel_disable_idleness_drrs(drrs);
+ intel_enable_idleness_drrs(drrs);
+ }
+ return 0;
+}
+
/* Idleness detection logic is initialized */
int intel_drrs_idleness_detection_init(struct i915_drrs *drrs)
{
@@ -480,6 +628,7 @@ int intel_drrs_init(struct drm_device *dev,
drrs->has_drrs = true;
to_intel_crtc(intel_encoder->base.crtc)->config->has_drrs = true;
mutex_init(&drrs->drrs_mutex);
+ drrs->resume_idleness_detection = false;
drrs->drrs_state.current_rr_type = DRRS_HIGH_RR;
DRM_INFO("SEAMLESS DRRS supported on this panel.\n");

diff --git a/drivers/gpu/drm/i915/intel_drrs.h b/drivers/gpu/drm/i915/intel_drrs.h
index a07f023..7f9985c0 100644
--- a/drivers/gpu/drm/i915/intel_drrs.h
+++ b/drivers/gpu/drm/i915/intel_drrs.h
@@ -26,7 +26,10 @@ int get_drrs_struct_index_for_crtc(struct drm_i915_private *dev_priv,
int get_drrs_struct_index_for_connector(struct drm_i915_private *dev_priv,
struct intel_connector *intel_connector);
int get_free_drrs_struct_index(struct drm_i915_private *dev_priv);
-
+void intel_disable_idleness_drrs(struct i915_drrs *drrs);
+void intel_enable_idleness_drrs(struct i915_drrs *drrs);
+int intel_media_playback_drrs_configure(struct intel_crtc *crtc,
+ struct drm_display_mode *mode);
int intel_drrs_init(struct drm_device *dev,
struct intel_connector *intel_connector,
struct drm_display_mode *fixed_mode);