[Intel-gfx] [PATCH] drm/i915: Synchronize userspace palette LUT (i.e. gamma) changes to vblank
Chris Wilson
chris at chris-wilson.co.uk
Tue Feb 28 20:09:52 CET 2012
Adam Jackson was watching the screensaver fade out and expressed a
desire for the gamma updates to be synchronized to vblank to avoid the
unsightly tears.
Reported-by: Adam Jackson <ajax at redhat.com>
Cc: Adam Jackson <ajax at redhat.com>
Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
---
drivers/gpu/drm/i915/intel_display.c | 108 +++++++++++++++++++++++++++++-----
drivers/gpu/drm/i915/intel_drv.h | 9 +++
2 files changed, 102 insertions(+), 15 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 66b19d3..6c2101e 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -47,6 +47,7 @@ bool intel_pipe_has_type(struct drm_crtc *crtc, int type);
static void intel_update_watermarks(struct drm_device *dev);
static void intel_increase_pllclock(struct drm_crtc *crtc);
static void intel_crtc_update_cursor(struct drm_crtc *crtc, bool on);
+static void __intel_crtc_load_lut(struct intel_crtc *crtc);
typedef struct {
/* given values */
@@ -3094,7 +3095,7 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
* On ILK+ LUT must be loaded before the pipe is running but with
* clocks enabled
*/
- intel_crtc_load_lut(crtc);
+ __intel_crtc_load_lut(to_intel_crtc(crtc));
intel_enable_pipe(dev_priv, pipe, is_pch_port);
intel_enable_plane(dev_priv, plane, pipe);
@@ -6258,29 +6259,100 @@ void intel_write_eld(struct drm_encoder *encoder,
dev_priv->display.write_eld(connector, crtc);
}
-/** Loads the palette/gamma unit for the CRTC with the prepared values */
-void intel_crtc_load_lut(struct drm_crtc *crtc)
+static void __intel_crtc_load_lut(struct intel_crtc *crtc)
{
- struct drm_device *dev = crtc->dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
- int palreg = PALETTE(intel_crtc->pipe);
+ struct drm_i915_private *dev_priv = crtc->base.dev->dev_private;
+ int reg;
int i;
- /* The clocks have to be on to load the palette. */
- if (!crtc->enabled || !intel_crtc->active)
+ if (!crtc->base.enabled || !crtc->active)
return;
/* use legacy palette for Ironlake */
- if (HAS_PCH_SPLIT(dev))
- palreg = LGC_PALETTE(intel_crtc->pipe);
+ reg = PALETTE(crtc->pipe);
+ if (HAS_PCH_SPLIT(crtc->base.dev))
+ reg = LGC_PALETTE(crtc->pipe);
for (i = 0; i < 256; i++) {
- I915_WRITE(palreg + 4 * i,
- (intel_crtc->lut_r[i] << 16) |
- (intel_crtc->lut_g[i] << 8) |
- intel_crtc->lut_b[i]);
+ I915_WRITE(reg + 4 * i,
+ crtc->lut_r[i] << 16 |
+ crtc->lut_g[i] << 8 |
+ crtc->lut_b[i]);
+ }
+}
+
+struct intel_crtc_vblank_task {
+ struct list_head list;
+ void (*func)(struct intel_crtc *);
+};
+
+static void intel_crtc_vblank_work_fn(struct work_struct *_work)
+{
+ struct intel_crtc_vblank_work *work =
+ container_of(_work, struct intel_crtc_vblank_work, work);
+ struct intel_crtc *crtc = work->crtc;
+
+ intel_wait_for_vblank(crtc->base.dev, crtc->pipe);
+
+ mutex_lock(&crtc->vblank_mutex);
+ while (!list_empty(&work->tasks)) {
+ struct intel_crtc_vblank_task *task
+ = list_first_entry(&work->tasks, struct intel_crtc_vblank_task, list);
+
+ task->func(crtc);
+ list_del(&task->list);
+ kfree(task);
}
+ crtc->vblank_work = NULL;
+ mutex_unlock(&crtc->vblank_mutex);
+
+ kfree(work);
+}
+
+static int intel_crtc_add_vblank_task(struct intel_crtc *crtc,
+ void (*func)(struct intel_crtc *))
+{
+ struct intel_crtc_vblank_task *task;
+ struct intel_crtc_vblank_work *work;
+
+ task = kzalloc(sizeof *task, GFP_KERNEL);
+ if (task == NULL)
+ return -ENOMEM;
+ task->func = func;
+
+ mutex_lock(&crtc->vblank_mutex);
+ work = crtc->vblank_work;
+ if (work == NULL) {
+ work = kzalloc(sizeof *work, GFP_KERNEL);
+ if (work == NULL) {
+ mutex_unlock(&crtc->vblank_mutex);
+ kfree(task);
+ return -ENOMEM;
+ }
+
+ work->crtc = crtc;
+ INIT_LIST_HEAD(&work->tasks);
+ INIT_WORK(&work->work, intel_crtc_vblank_work_fn);
+ schedule_work(&work->work);
+ crtc->vblank_work = work;
+ }
+ list_add(&task->list, &work->tasks);
+ mutex_unlock(&crtc->vblank_mutex);
+
+ return 0;
+}
+
+/** Loads the palette/gamma unit for the CRTC with the prepared values */
+void intel_crtc_load_lut(struct drm_crtc *crtc)
+{
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+
+ /* The clocks have to be on to load the palette. */
+ if (!crtc->enabled || !intel_crtc->active)
+ return;
+
+ if (intel_crtc_add_vblank_task(intel_crtc, __intel_crtc_load_lut))
+ __intel_crtc_load_lut(intel_crtc);
}
static void i845_update_cursor(struct drm_crtc *crtc, u32 base)
@@ -6570,6 +6642,7 @@ static void intel_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green,
int end = (start + size > 256) ? 256 : start + size, i;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ /* We race here with setting the lut and reading it during vblank. */
for (i = start; i < end; i++) {
intel_crtc->lut_r[i] = red[i] >> 8;
intel_crtc->lut_g[i] = green[i] >> 8;
@@ -7647,6 +7720,8 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
if (intel_crtc == NULL)
return;
+ mutex_init(&intel_crtc->vblank_mutex);
+
drm_crtc_init(dev, &intel_crtc->base, &intel_crtc_funcs);
drm_mode_crtc_set_gamma_size(&intel_crtc->base, 256);
@@ -9161,6 +9236,9 @@ void intel_modeset_cleanup(struct drm_device *dev)
struct drm_crtc *crtc;
struct intel_crtc *intel_crtc;
+ /* Clear the vblank worker prior to taking any locks */
+ flush_scheduled_work();
+
drm_kms_helper_poll_fini(dev);
mutex_lock(&dev->struct_mutex);
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 9cec6c3..1e3d8a9 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -160,6 +160,9 @@ struct intel_crtc {
struct intel_unpin_work *unpin_work;
int fdi_lanes;
+ struct mutex vblank_mutex; /* protects *vblank_work */
+ struct intel_crtc_vblank_work *vblank_work;
+
struct drm_i915_gem_object *cursor_bo;
uint32_t cursor_addr;
int16_t cursor_x, cursor_y;
@@ -267,6 +270,12 @@ intel_get_crtc_for_plane(struct drm_device *dev, int plane)
return dev_priv->plane_to_crtc_mapping[plane];
}
+struct intel_crtc_vblank_work {
+ struct work_struct work;
+ struct intel_crtc *crtc;
+ struct list_head tasks;
+};
+
struct intel_unpin_work {
struct work_struct work;
struct drm_device *dev;
--
1.7.9.1
More information about the Intel-gfx
mailing list