Commit 1630fe75 authored by Chris Wilson's avatar Chris Wilson Committed by Keith Packard

drm/i915: Perform intel_enable_fbc() from a delayed task

In order to accommodate the requirements of re-enabling FBC after
page-flipping, but to avoid doing so and incurring the cost of a wait
for vblank in the middle of a page-flip sequence, we defer the actual
enablement by 50ms. If any request to disable FBC arrive within that
interval, the enablement is cancelled and we are saved from blocking on
the wait.
Signed-off-by: default avatarChris Wilson <chris@chris-wilson.co.uk>
Reviewed-by: default avatarJesse Barnes <jbarnes@virtuousgeek.org>
Signed-off-by: default avatarKeith Packard <keithp@keithp.com>
parent 7782de3b
...@@ -266,6 +266,7 @@ enum intel_pch { ...@@ -266,6 +266,7 @@ enum intel_pch {
#define QUIRK_PIPEA_FORCE (1<<0) #define QUIRK_PIPEA_FORCE (1<<0)
struct intel_fbdev; struct intel_fbdev;
struct intel_fbc_work;
typedef struct drm_i915_private { typedef struct drm_i915_private {
struct drm_device *dev; struct drm_device *dev;
...@@ -335,6 +336,7 @@ typedef struct drm_i915_private { ...@@ -335,6 +336,7 @@ typedef struct drm_i915_private {
int cfb_fence; int cfb_fence;
int cfb_plane; int cfb_plane;
int cfb_y; int cfb_y;
struct intel_fbc_work *fbc_work;
struct intel_opregion opregion; struct intel_opregion opregion;
......
...@@ -1636,20 +1636,96 @@ bool intel_fbc_enabled(struct drm_device *dev) ...@@ -1636,20 +1636,96 @@ bool intel_fbc_enabled(struct drm_device *dev)
return dev_priv->display.fbc_enabled(dev); return dev_priv->display.fbc_enabled(dev);
} }
static void intel_fbc_work_fn(struct work_struct *__work)
{
struct intel_fbc_work *work =
container_of(to_delayed_work(__work),
struct intel_fbc_work, work);
struct drm_device *dev = work->crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
mutex_lock(&dev->struct_mutex);
if (work == dev_priv->fbc_work) {
/* Double check that we haven't switched fb without cancelling
* the prior work.
*/
if (work->crtc->fb == work->fb)
dev_priv->display.enable_fbc(work->crtc,
work->interval);
dev_priv->fbc_work = NULL;
}
mutex_unlock(&dev->struct_mutex);
kfree(work);
}
static void intel_cancel_fbc_work(struct drm_i915_private *dev_priv)
{
if (dev_priv->fbc_work == NULL)
return;
DRM_DEBUG_KMS("cancelling pending FBC enable\n");
/* Synchronisation is provided by struct_mutex and checking of
* dev_priv->fbc_work, so we can perform the cancellation
* entirely asynchronously.
*/
if (cancel_delayed_work(&dev_priv->fbc_work->work))
/* tasklet was killed before being run, clean up */
kfree(dev_priv->fbc_work);
/* Mark the work as no longer wanted so that if it does
* wake-up (because the work was already running and waiting
* for our mutex), it will discover that is no longer
* necessary to run.
*/
dev_priv->fbc_work = NULL;
}
static void intel_enable_fbc(struct drm_crtc *crtc, unsigned long interval) static void intel_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
{ {
struct drm_i915_private *dev_priv = crtc->dev->dev_private; struct intel_fbc_work *work;
struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
if (!dev_priv->display.enable_fbc) if (!dev_priv->display.enable_fbc)
return; return;
intel_cancel_fbc_work(dev_priv);
work = kzalloc(sizeof *work, GFP_KERNEL);
if (work == NULL) {
dev_priv->display.enable_fbc(crtc, interval); dev_priv->display.enable_fbc(crtc, interval);
return;
}
work->crtc = crtc;
work->fb = crtc->fb;
work->interval = interval;
INIT_DELAYED_WORK(&work->work, intel_fbc_work_fn);
dev_priv->fbc_work = work;
DRM_DEBUG_KMS("scheduling delayed FBC enable\n");
/* Delay the actual enabling to let pageflipping cease and the
* display to settle before starting the compression.
*
* A more complicated solution would involve tracking vblanks
* following the termination of the page-flipping sequence
* and indeed performing the enable as a co-routine and not
* waiting synchronously upon the vblank.
*/
schedule_delayed_work(&work->work, msecs_to_jiffies(50));
} }
void intel_disable_fbc(struct drm_device *dev) void intel_disable_fbc(struct drm_device *dev)
{ {
struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_private *dev_priv = dev->dev_private;
intel_cancel_fbc_work(dev_priv);
if (!dev_priv->display.disable_fbc) if (!dev_priv->display.disable_fbc)
return; return;
...@@ -8227,6 +8303,9 @@ void intel_modeset_cleanup(struct drm_device *dev) ...@@ -8227,6 +8303,9 @@ void intel_modeset_cleanup(struct drm_device *dev)
drm_irq_uninstall(dev); drm_irq_uninstall(dev);
cancel_work_sync(&dev_priv->hotplug_work); cancel_work_sync(&dev_priv->hotplug_work);
/* flush any delayed tasks or pending work */
flush_scheduled_work();
/* Shut off idle work before the crtcs get freed. */ /* Shut off idle work before the crtcs get freed. */
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
intel_crtc = to_intel_crtc(crtc); intel_crtc = to_intel_crtc(crtc);
......
...@@ -234,6 +234,13 @@ struct intel_unpin_work { ...@@ -234,6 +234,13 @@ struct intel_unpin_work {
bool enable_stall_check; bool enable_stall_check;
}; };
struct intel_fbc_work {
struct delayed_work work;
struct drm_crtc *crtc;
struct drm_framebuffer *fb;
int interval;
};
int intel_ddc_get_modes(struct drm_connector *c, struct i2c_adapter *adapter); int intel_ddc_get_modes(struct drm_connector *c, struct i2c_adapter *adapter);
extern bool intel_ddc_probe(struct intel_encoder *intel_encoder, int ddc_bus); extern bool intel_ddc_probe(struct intel_encoder *intel_encoder, int ddc_bus);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment