diff options
Diffstat (limited to 'drivers/gpu/drm/i915/display/intel_crtc.c')
| -rw-r--r-- | drivers/gpu/drm/i915/display/intel_crtc.c | 155 | 
1 files changed, 132 insertions, 23 deletions
| diff --git a/drivers/gpu/drm/i915/display/intel_crtc.c b/drivers/gpu/drm/i915/display/intel_crtc.c index 254e67141a77..16c3ca66d9f0 100644 --- a/drivers/gpu/drm/i915/display/intel_crtc.c +++ b/drivers/gpu/drm/i915/display/intel_crtc.c @@ -3,29 +3,31 @@   * Copyright © 2020 Intel Corporation   */  #include <linux/kernel.h> +#include <linux/pm_qos.h>  #include <linux/slab.h>  #include <drm/drm_atomic_helper.h>  #include <drm/drm_fourcc.h>  #include <drm/drm_plane.h>  #include <drm/drm_plane_helper.h> +#include <drm/drm_vblank_work.h> -#include "i915_trace.h"  #include "i915_vgpu.h" - +#include "i9xx_plane.h" +#include "icl_dsi.h"  #include "intel_atomic.h"  #include "intel_atomic_plane.h"  #include "intel_color.h"  #include "intel_crtc.h"  #include "intel_cursor.h"  #include "intel_display_debugfs.h" +#include "intel_display_trace.h"  #include "intel_display_types.h"  #include "intel_dsi.h"  #include "intel_pipe_crc.h"  #include "intel_psr.h"  #include "intel_sprite.h"  #include "intel_vrr.h" -#include "i9xx_plane.h"  #include "skl_universal_plane.h"  static void assert_vblank_disabled(struct drm_crtc *crtc) @@ -34,6 +36,38 @@ static void assert_vblank_disabled(struct drm_crtc *crtc)  		drm_crtc_vblank_put(crtc);  } +struct intel_crtc *intel_first_crtc(struct drm_i915_private *i915) +{ +	return to_intel_crtc(drm_crtc_from_index(&i915->drm, 0)); +} + +struct intel_crtc *intel_crtc_for_pipe(struct drm_i915_private *i915, +				       enum pipe pipe) +{ +	struct intel_crtc *crtc; + +	for_each_intel_crtc(&i915->drm, crtc) { +		if (crtc->pipe == pipe) +			return crtc; +	} + +	return NULL; +} + +void intel_crtc_wait_for_next_vblank(struct intel_crtc *crtc) +{ +	drm_crtc_wait_one_vblank(&crtc->base); +} + +void intel_wait_for_vblank_if_active(struct drm_i915_private *i915, +				     enum pipe pipe) +{ +	struct intel_crtc *crtc = intel_crtc_for_pipe(i915, pipe); + +	if (crtc->active) +		intel_crtc_wait_for_next_vblank(crtc); +} +  u32 intel_crtc_get_vblank_counter(struct intel_crtc *crtc)  {  	struct drm_device *dev = crtc->base.dev; @@ -167,6 +201,8 @@ static void intel_crtc_destroy(struct drm_crtc *_crtc)  {  	struct intel_crtc *crtc = to_intel_crtc(_crtc); +	cpu_latency_qos_remove_request(&crtc->vblank_pm_qos); +  	drm_crtc_cleanup(&crtc->base);  	kfree(crtc);  } @@ -323,18 +359,6 @@ int intel_crtc_init(struct drm_i915_private *dev_priv, enum pipe pipe)  	if (ret)  		goto fail; -	BUG_ON(pipe >= ARRAY_SIZE(dev_priv->pipe_to_crtc_mapping) || -	       dev_priv->pipe_to_crtc_mapping[pipe] != NULL); -	dev_priv->pipe_to_crtc_mapping[pipe] = crtc; - -	if (DISPLAY_VER(dev_priv) < 9) { -		enum i9xx_plane_id i9xx_plane = primary->i9xx_plane; - -		BUG_ON(i9xx_plane >= ARRAY_SIZE(dev_priv->plane_to_crtc_mapping) || -		       dev_priv->plane_to_crtc_mapping[i9xx_plane] != NULL); -		dev_priv->plane_to_crtc_mapping[i9xx_plane] = crtc; -	} -  	if (DISPLAY_VER(dev_priv) >= 11)  		drm_crtc_create_scaling_filter_property(&crtc->base,  						BIT(DRM_SCALING_FILTER_DEFAULT) | @@ -344,6 +368,8 @@ int intel_crtc_init(struct drm_i915_private *dev_priv, enum pipe pipe)  	intel_crtc_crc_init(crtc); +	cpu_latency_qos_add_request(&crtc->vblank_pm_qos, PM_QOS_DEFAULT_VALUE); +  	drm_WARN_ON(&dev_priv->drm, drm_crtc_index(&crtc->base) != crtc->pipe);  	return 0; @@ -354,6 +380,65 @@ fail:  	return ret;  } +static bool intel_crtc_needs_vblank_work(const struct intel_crtc_state *crtc_state) +{ +	return crtc_state->hw.active && +		!intel_crtc_needs_modeset(crtc_state) && +		!crtc_state->preload_luts && +		(crtc_state->uapi.color_mgmt_changed || +		 crtc_state->update_pipe); +} + +static void intel_crtc_vblank_work(struct kthread_work *base) +{ +	struct drm_vblank_work *work = to_drm_vblank_work(base); +	struct intel_crtc_state *crtc_state = +		container_of(work, typeof(*crtc_state), vblank_work); +	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); + +	trace_intel_crtc_vblank_work_start(crtc); + +	intel_color_load_luts(crtc_state); + +	if (crtc_state->uapi.event) { +		spin_lock_irq(&crtc->base.dev->event_lock); +		drm_crtc_send_vblank_event(&crtc->base, crtc_state->uapi.event); +		crtc_state->uapi.event = NULL; +		spin_unlock_irq(&crtc->base.dev->event_lock); +	} + +	trace_intel_crtc_vblank_work_end(crtc); +} + +static void intel_crtc_vblank_work_init(struct intel_crtc_state *crtc_state) +{ +	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); + +	drm_vblank_work_init(&crtc_state->vblank_work, &crtc->base, +			     intel_crtc_vblank_work); +	/* +	 * Interrupt latency is critical for getting the vblank +	 * work executed as early as possible during the vblank. +	 */ +	cpu_latency_qos_update_request(&crtc->vblank_pm_qos, 0); +} + +void intel_wait_for_vblank_workers(struct intel_atomic_state *state) +{ +	struct intel_crtc_state *crtc_state; +	struct intel_crtc *crtc; +	int i; + +	for_each_new_intel_crtc_in_state(state, crtc, crtc_state, i) { +		if (!intel_crtc_needs_vblank_work(crtc_state)) +			continue; + +		drm_vblank_work_flush(&crtc_state->vblank_work); +		cpu_latency_qos_update_request(&crtc->vblank_pm_qos, +					       PM_QOS_DEFAULT_VALUE); +	} +} +  int intel_usecs_to_scanlines(const struct drm_display_mode *adjusted_mode,  			     int usecs)  { @@ -387,7 +472,7 @@ static int intel_mode_vblank_start(const struct drm_display_mode *mode)   * until a subsequent call to intel_pipe_update_end(). That is done to   * avoid random delays.   */ -void intel_pipe_update_start(const struct intel_crtc_state *new_crtc_state) +void intel_pipe_update_start(struct intel_crtc_state *new_crtc_state)  {  	struct intel_crtc *crtc = to_intel_crtc(new_crtc_state->uapi.crtc);  	struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); @@ -402,10 +487,17 @@ void intel_pipe_update_start(const struct intel_crtc_state *new_crtc_state)  	if (new_crtc_state->uapi.async_flip)  		return; -	if (new_crtc_state->vrr.enable) -		vblank_start = intel_vrr_vmax_vblank_start(new_crtc_state); -	else +	if (intel_crtc_needs_vblank_work(new_crtc_state)) +		intel_crtc_vblank_work_init(new_crtc_state); + +	if (new_crtc_state->vrr.enable) { +		if (intel_vrr_is_push_sent(new_crtc_state)) +			vblank_start = intel_vrr_vmin_vblank_start(new_crtc_state); +		else +			vblank_start = intel_vrr_vmax_vblank_start(new_crtc_state); +	} else {  		vblank_start = intel_mode_vblank_start(adjusted_mode); +	}  	/* FIXME needs to be calibrated sensibly */  	min = vblank_start - intel_usecs_to_scanlines(adjusted_mode, @@ -554,7 +646,11 @@ void intel_pipe_update_end(struct intel_crtc_state *new_crtc_state)  	 * Would be slightly nice to just grab the vblank count and arm the  	 * event outside of the critical section - the spinlock might spin for a  	 * while ... */ -	if (new_crtc_state->uapi.event) { +	if (intel_crtc_needs_vblank_work(new_crtc_state)) { +		drm_vblank_work_schedule(&new_crtc_state->vblank_work, +					 drm_crtc_accurate_vblank_count(&crtc->base) + 1, +					 false); +	} else if (new_crtc_state->uapi.event) {  		drm_WARN_ON(&dev_priv->drm,  			    drm_crtc_vblank_get(&crtc->base) != 0); @@ -566,11 +662,24 @@ void intel_pipe_update_end(struct intel_crtc_state *new_crtc_state)  		new_crtc_state->uapi.event = NULL;  	} -	local_irq_enable(); - -	/* Send VRR Push to terminate Vblank */ +	/* +	 * Send VRR Push to terminate Vblank. If we are already in vblank +	 * this has to be done _after_ sampling the frame counter, as +	 * otherwise the push would immediately terminate the vblank and +	 * the sampled frame counter would correspond to the next frame +	 * instead of the current frame. +	 * +	 * There is a tiny race here (iff vblank evasion failed us) where +	 * we might sample the frame counter just before vmax vblank start +	 * but the push would be sent just after it. That would cause the +	 * push to affect the next frame instead of the current frame, +	 * which would cause the next frame to terminate already at vmin +	 * vblank start instead of vmax vblank start. +	 */  	intel_vrr_send_push(new_crtc_state); +	local_irq_enable(); +  	if (intel_vgpu_active(dev_priv))  		return; |