diff options
author | Chris Wilson <chris@chris-wilson.co.uk> | 2012-07-21 07:31:41 -0400 |
---|---|---|
committer | Daniel Vetter <daniel.vetter@ffwll.ch> | 2012-07-25 12:23:56 -0400 |
commit | f047e395ddc9da6c307a10629a237502e627ed85 (patch) | |
tree | 36a445f1d0bdf1bf37321d55ebfda84475c34b1c /drivers/gpu/drm/i915/intel_display.c | |
parent | a7b9761d0a2ded58170ffb4d423ff3d7228103f4 (diff) |
drm/i915: Avoid concurrent access when marking the device as idle/busy
As suggested by Daniel, rip out the independent timers for device and
crtc busyness and integrate the manual powermanagement of the display
engine into the GEM core and its request tracking. The benefits are that
the code is a lot smaller, fewer moving parts and should fit more neatly
into the overall activity tracking of the driver.
v2: Complete overhaul and removal of the racy timers and workers.
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Diffstat (limited to 'drivers/gpu/drm/i915/intel_display.c')
-rw-r--r-- | drivers/gpu/drm/i915/intel_display.c | 146 |
1 files changed, 22 insertions, 124 deletions
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 93d9934ba328..b463829b92eb 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c | |||
@@ -5860,46 +5860,6 @@ struct drm_display_mode *intel_crtc_mode_get(struct drm_device *dev, | |||
5860 | return mode; | 5860 | return mode; |
5861 | } | 5861 | } |
5862 | 5862 | ||
5863 | #define GPU_IDLE_TIMEOUT 500 /* ms */ | ||
5864 | |||
5865 | /* When this timer fires, we've been idle for awhile */ | ||
5866 | static void intel_gpu_idle_timer(unsigned long arg) | ||
5867 | { | ||
5868 | struct drm_device *dev = (struct drm_device *)arg; | ||
5869 | drm_i915_private_t *dev_priv = dev->dev_private; | ||
5870 | |||
5871 | if (!list_empty(&dev_priv->mm.active_list)) { | ||
5872 | /* Still processing requests, so just re-arm the timer. */ | ||
5873 | mod_timer(&dev_priv->idle_timer, jiffies + | ||
5874 | msecs_to_jiffies(GPU_IDLE_TIMEOUT)); | ||
5875 | return; | ||
5876 | } | ||
5877 | |||
5878 | dev_priv->busy = false; | ||
5879 | queue_work(dev_priv->wq, &dev_priv->idle_work); | ||
5880 | } | ||
5881 | |||
5882 | #define CRTC_IDLE_TIMEOUT 1000 /* ms */ | ||
5883 | |||
5884 | static void intel_crtc_idle_timer(unsigned long arg) | ||
5885 | { | ||
5886 | struct intel_crtc *intel_crtc = (struct intel_crtc *)arg; | ||
5887 | struct drm_crtc *crtc = &intel_crtc->base; | ||
5888 | drm_i915_private_t *dev_priv = crtc->dev->dev_private; | ||
5889 | struct intel_framebuffer *intel_fb; | ||
5890 | |||
5891 | intel_fb = to_intel_framebuffer(crtc->fb); | ||
5892 | if (intel_fb && intel_fb->obj->active) { | ||
5893 | /* The framebuffer is still being accessed by the GPU. */ | ||
5894 | mod_timer(&intel_crtc->idle_timer, jiffies + | ||
5895 | msecs_to_jiffies(CRTC_IDLE_TIMEOUT)); | ||
5896 | return; | ||
5897 | } | ||
5898 | |||
5899 | intel_crtc->busy = false; | ||
5900 | queue_work(dev_priv->wq, &dev_priv->idle_work); | ||
5901 | } | ||
5902 | |||
5903 | static void intel_increase_pllclock(struct drm_crtc *crtc) | 5863 | static void intel_increase_pllclock(struct drm_crtc *crtc) |
5904 | { | 5864 | { |
5905 | struct drm_device *dev = crtc->dev; | 5865 | struct drm_device *dev = crtc->dev; |
@@ -5929,10 +5889,6 @@ static void intel_increase_pllclock(struct drm_crtc *crtc) | |||
5929 | if (dpll & DISPLAY_RATE_SELECT_FPA1) | 5889 | if (dpll & DISPLAY_RATE_SELECT_FPA1) |
5930 | DRM_DEBUG_DRIVER("failed to upclock LVDS!\n"); | 5890 | DRM_DEBUG_DRIVER("failed to upclock LVDS!\n"); |
5931 | } | 5891 | } |
5932 | |||
5933 | /* Schedule downclock */ | ||
5934 | mod_timer(&intel_crtc->idle_timer, jiffies + | ||
5935 | msecs_to_jiffies(CRTC_IDLE_TIMEOUT)); | ||
5936 | } | 5892 | } |
5937 | 5893 | ||
5938 | static void intel_decrease_pllclock(struct drm_crtc *crtc) | 5894 | static void intel_decrease_pllclock(struct drm_crtc *crtc) |
@@ -5971,89 +5927,48 @@ static void intel_decrease_pllclock(struct drm_crtc *crtc) | |||
5971 | 5927 | ||
5972 | } | 5928 | } |
5973 | 5929 | ||
5974 | /** | 5930 | void intel_mark_busy(struct drm_device *dev) |
5975 | * intel_idle_update - adjust clocks for idleness | 5931 | { |
5976 | * @work: work struct | 5932 | intel_sanitize_pm(dev); |
5977 | * | 5933 | i915_update_gfx_val(dev->dev_private); |
5978 | * Either the GPU or display (or both) went idle. Check the busy status | 5934 | } |
5979 | * here and adjust the CRTC and GPU clocks as necessary. | 5935 | |
5980 | */ | 5936 | void intel_mark_idle(struct drm_device *dev) |
5981 | static void intel_idle_update(struct work_struct *work) | ||
5982 | { | 5937 | { |
5983 | drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t, | 5938 | intel_sanitize_pm(dev); |
5984 | idle_work); | 5939 | } |
5985 | struct drm_device *dev = dev_priv->dev; | 5940 | |
5941 | void intel_mark_fb_busy(struct drm_i915_gem_object *obj) | ||
5942 | { | ||
5943 | struct drm_device *dev = obj->base.dev; | ||
5986 | struct drm_crtc *crtc; | 5944 | struct drm_crtc *crtc; |
5987 | struct intel_crtc *intel_crtc; | ||
5988 | 5945 | ||
5989 | if (!i915_powersave) | 5946 | if (!i915_powersave) |
5990 | return; | 5947 | return; |
5991 | 5948 | ||
5992 | mutex_lock(&dev->struct_mutex); | ||
5993 | |||
5994 | i915_update_gfx_val(dev_priv); | ||
5995 | |||
5996 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { | 5949 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
5997 | /* Skip inactive CRTCs */ | ||
5998 | if (!crtc->fb) | 5950 | if (!crtc->fb) |
5999 | continue; | 5951 | continue; |
6000 | 5952 | ||
6001 | intel_crtc = to_intel_crtc(crtc); | 5953 | if (to_intel_framebuffer(crtc->fb)->obj == obj) |
6002 | if (!intel_crtc->busy) | 5954 | intel_increase_pllclock(crtc); |
6003 | intel_decrease_pllclock(crtc); | ||
6004 | } | 5955 | } |
6005 | |||
6006 | |||
6007 | mutex_unlock(&dev->struct_mutex); | ||
6008 | } | 5956 | } |
6009 | 5957 | ||
6010 | /** | 5958 | void intel_mark_fb_idle(struct drm_i915_gem_object *obj) |
6011 | * intel_mark_busy - mark the GPU and possibly the display busy | ||
6012 | * @dev: drm device | ||
6013 | * @obj: object we're operating on | ||
6014 | * | ||
6015 | * Callers can use this function to indicate that the GPU is busy processing | ||
6016 | * commands. If @obj matches one of the CRTC objects (i.e. it's a scanout | ||
6017 | * buffer), we'll also mark the display as busy, so we know to increase its | ||
6018 | * clock frequency. | ||
6019 | */ | ||
6020 | void intel_mark_busy(struct drm_device *dev, struct drm_i915_gem_object *obj) | ||
6021 | { | 5959 | { |
6022 | drm_i915_private_t *dev_priv = dev->dev_private; | 5960 | struct drm_device *dev = obj->base.dev; |
6023 | struct drm_crtc *crtc = NULL; | 5961 | struct drm_crtc *crtc; |
6024 | struct intel_framebuffer *intel_fb; | ||
6025 | struct intel_crtc *intel_crtc; | ||
6026 | |||
6027 | if (!drm_core_check_feature(dev, DRIVER_MODESET)) | ||
6028 | return; | ||
6029 | |||
6030 | if (!dev_priv->busy) { | ||
6031 | intel_sanitize_pm(dev); | ||
6032 | dev_priv->busy = true; | ||
6033 | } else | ||
6034 | mod_timer(&dev_priv->idle_timer, jiffies + | ||
6035 | msecs_to_jiffies(GPU_IDLE_TIMEOUT)); | ||
6036 | 5962 | ||
6037 | if (obj == NULL) | 5963 | if (!i915_powersave) |
6038 | return; | 5964 | return; |
6039 | 5965 | ||
6040 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { | 5966 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
6041 | if (!crtc->fb) | 5967 | if (!crtc->fb) |
6042 | continue; | 5968 | continue; |
6043 | 5969 | ||
6044 | intel_crtc = to_intel_crtc(crtc); | 5970 | if (to_intel_framebuffer(crtc->fb)->obj == obj) |
6045 | intel_fb = to_intel_framebuffer(crtc->fb); | 5971 | intel_decrease_pllclock(crtc); |
6046 | if (intel_fb->obj == obj) { | ||
6047 | if (!intel_crtc->busy) { | ||
6048 | /* Non-busy -> busy, upclock */ | ||
6049 | intel_increase_pllclock(crtc); | ||
6050 | intel_crtc->busy = true; | ||
6051 | } else { | ||
6052 | /* Busy -> busy, put off timer */ | ||
6053 | mod_timer(&intel_crtc->idle_timer, jiffies + | ||
6054 | msecs_to_jiffies(CRTC_IDLE_TIMEOUT)); | ||
6055 | } | ||
6056 | } | ||
6057 | } | 5972 | } |
6058 | } | 5973 | } |
6059 | 5974 | ||
@@ -6512,7 +6427,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, | |||
6512 | goto cleanup_pending; | 6427 | goto cleanup_pending; |
6513 | 6428 | ||
6514 | intel_disable_fbc(dev); | 6429 | intel_disable_fbc(dev); |
6515 | intel_mark_busy(dev, obj); | 6430 | intel_mark_fb_busy(obj); |
6516 | mutex_unlock(&dev->struct_mutex); | 6431 | mutex_unlock(&dev->struct_mutex); |
6517 | 6432 | ||
6518 | trace_i915_flip_request(intel_crtc->plane, obj); | 6433 | trace_i915_flip_request(intel_crtc->plane, obj); |
@@ -6678,11 +6593,6 @@ static void intel_crtc_init(struct drm_device *dev, int pipe) | |||
6678 | } | 6593 | } |
6679 | 6594 | ||
6680 | drm_crtc_helper_add(&intel_crtc->base, &intel_helper_funcs); | 6595 | drm_crtc_helper_add(&intel_crtc->base, &intel_helper_funcs); |
6681 | |||
6682 | intel_crtc->busy = false; | ||
6683 | |||
6684 | setup_timer(&intel_crtc->idle_timer, intel_crtc_idle_timer, | ||
6685 | (unsigned long)intel_crtc); | ||
6686 | } | 6596 | } |
6687 | 6597 | ||
6688 | int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data, | 6598 | int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data, |
@@ -7265,10 +7175,6 @@ void intel_modeset_init(struct drm_device *dev) | |||
7265 | /* Just disable it once at startup */ | 7175 | /* Just disable it once at startup */ |
7266 | i915_disable_vga(dev); | 7176 | i915_disable_vga(dev); |
7267 | intel_setup_outputs(dev); | 7177 | intel_setup_outputs(dev); |
7268 | |||
7269 | INIT_WORK(&dev_priv->idle_work, intel_idle_update); | ||
7270 | setup_timer(&dev_priv->idle_timer, intel_gpu_idle_timer, | ||
7271 | (unsigned long)dev); | ||
7272 | } | 7178 | } |
7273 | 7179 | ||
7274 | void intel_modeset_gem_init(struct drm_device *dev) | 7180 | void intel_modeset_gem_init(struct drm_device *dev) |
@@ -7319,14 +7225,6 @@ void intel_modeset_cleanup(struct drm_device *dev) | |||
7319 | /* flush any delayed tasks or pending work */ | 7225 | /* flush any delayed tasks or pending work */ |
7320 | flush_scheduled_work(); | 7226 | flush_scheduled_work(); |
7321 | 7227 | ||
7322 | /* Shut off idle work before the crtcs get freed. */ | ||
7323 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { | ||
7324 | intel_crtc = to_intel_crtc(crtc); | ||
7325 | del_timer_sync(&intel_crtc->idle_timer); | ||
7326 | } | ||
7327 | del_timer_sync(&dev_priv->idle_timer); | ||
7328 | cancel_work_sync(&dev_priv->idle_work); | ||
7329 | |||
7330 | drm_mode_config_cleanup(dev); | 7228 | drm_mode_config_cleanup(dev); |
7331 | } | 7229 | } |
7332 | 7230 | ||