[Intel-gfx] [PATCH] drm/i915: Add intel implementation of the pageflip ioctl
Kristian Høgsberg
krh at bitplanet.net
Mon Nov 30 20:25:09 CET 2009
2009/11/19 Jesse Barnes <jbarnes at virtuousgeek.org>:
> Dave, here's an updated version that fixes the checkpatch warnings, removes a
> stray line (the forced alignment hack) and fixes pre-965 support. I have
> some related followup patches, but I think this one is ready.
>
> Thanks,
> Jesse
>
> --
>
> From 2bec6039e7e6180a981971665be712f5a5b9b0e0 Mon Sep 17 00:00:00 2001
> From: =?utf-8?q?Kristian=20H=C3=B8gsberg?= <krh at bitplanet.net>
> Date: Tue, 17 Nov 2009 12:43:56 -0500
> Subject: [PATCH 1/4] Add intel implementation of the pageflip ioctl
> MIME-Version: 1.0
> Content-Type: text/plain; charset=utf-8
> Content-Transfer-Encoding: 8bit
>
> Acked-by: Jakob Bornecrantz <jakob at vmware.com>
> Acked-by: Thomas Hellström <thomas at shipmail.org>
> Review-by: Chris Wilson <chris at chris-wilson.co.uk>
> Signed-off-by: Jesse "Orange Smoothie" Barnes <jbarnes at virtuousgeek.org>
> Signed-off-by: Kristian Høgsberg <krh at bitplanet.net>
Hmm, I'm already the author of this patch, can I ack it? *confused*
Anyway, with Jesse's fixes, the ioctl is actually useful, so:
Acked-by: Kristian Høgsberg <krh at bitplanet.net>
> drivers/gpu/drm/i915/i915_drv.h | 12 ++
> drivers/gpu/drm/i915/i915_gem.c | 64 +++++++++-
> drivers/gpu/drm/i915/i915_irq.c | 10 ++
> drivers/gpu/drm/i915/i915_reg.h | 2 +
> drivers/gpu/drm/i915/intel_display.c | 237 +++++++++++++++++++++++++++++-----
> drivers/gpu/drm/i915/intel_drv.h | 3 +
> 6 files changed, 294 insertions(+), 34 deletions(-)
>
> diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
> index 835625b..75acb5d 100644
> --- a/drivers/gpu/drm/i915/i915_drv.h
> +++ b/drivers/gpu/drm/i915/i915_drv.h
> @@ -536,6 +536,10 @@ typedef struct drm_i915_private {
> /* indicate whether the LVDS_BORDER should be enabled or not */
> unsigned int lvds_border_bits;
>
> + struct drm_crtc *plane_to_crtc_mapping[2];
> + struct drm_crtc *pipe_to_crtc_mapping[2];
> + wait_queue_head_t pending_flip_queue;
> +
> /* Reclocking support */
> bool render_reclock_avail;
> bool lvds_downclock_avail;
> @@ -635,6 +639,13 @@ struct drm_i915_gem_object {
> * Advice: are the backing pages purgeable?
> */
> int madv;
> +
> + /**
> + * Number of crtcs where this object is currently the fb, but
> + * will be page flipped away on the next vblank. When it
> + * reaches 0, dev_priv->pending_flip_queue will be woken up.
> + */
> + atomic_t pending_flip;
> };
>
> /**
> @@ -826,6 +837,7 @@ void i915_gem_free_all_phys_object(struct drm_device *dev);
> int i915_gem_object_get_pages(struct drm_gem_object *obj);
> void i915_gem_object_put_pages(struct drm_gem_object *obj);
> void i915_gem_release(struct drm_device * dev, struct drm_file *file_priv);
> +void i915_gem_object_flush_write_domain(struct drm_gem_object *obj);
>
> void i915_gem_shrinker_init(void);
> void i915_gem_shrinker_exit(void);
> diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
> index 2065b8f..55ed06f 100644
> --- a/drivers/gpu/drm/i915/i915_gem.c
> +++ b/drivers/gpu/drm/i915/i915_gem.c
> @@ -2771,6 +2771,22 @@ i915_gem_object_flush_cpu_write_domain(struct drm_gem_object *obj)
> old_write_domain);
> }
>
> +void
> +i915_gem_object_flush_write_domain(struct drm_gem_object *obj)
> +{
> + switch (obj->write_domain) {
> + case I915_GEM_DOMAIN_GTT:
> + i915_gem_object_flush_gtt_write_domain(obj);
> + break;
> + case I915_GEM_DOMAIN_CPU:
> + i915_gem_object_flush_cpu_write_domain(obj);
> + break;
> + default:
> + i915_gem_object_flush_gpu_write_domain(obj);
> + break;
> + }
> +}
> +
> /**
> * Moves a single object to the GTT read, and possibly write domain.
> *
> @@ -3536,6 +3552,41 @@ i915_gem_check_execbuffer (struct drm_i915_gem_execbuffer *exec,
> return 0;
> }
>
> +static int
> +i915_gem_wait_for_pending_flip(struct drm_device *dev,
> + struct drm_gem_object **object_list,
> + int count)
> +{
> + drm_i915_private_t *dev_priv = dev->dev_private;
> + struct drm_i915_gem_object *obj_priv;
> + DEFINE_WAIT(wait);
> + int i, ret = 0;
> +
> + for (;;) {
> + prepare_to_wait(&dev_priv->pending_flip_queue,
> + &wait, TASK_INTERRUPTIBLE);
> + for (i = 0; i < count; i++) {
> + obj_priv = object_list[i]->driver_private;
> + if (atomic_read(&obj_priv->pending_flip) > 0)
> + break;
> + }
> + if (i == count)
> + break;
> +
> + if (!signal_pending(current)) {
> + mutex_unlock(&dev->struct_mutex);
> + schedule();
> + mutex_lock(&dev->struct_mutex);
> + continue;
> + }
> + ret = -ERESTARTSYS;
> + break;
> + }
> + finish_wait(&dev_priv->pending_flip_queue, &wait);
> +
> + return ret;
> +}
> +
> int
> i915_gem_execbuffer(struct drm_device *dev, void *data,
> struct drm_file *file_priv)
> @@ -3551,7 +3602,7 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
> int ret, ret2, i, pinned = 0;
> uint64_t exec_offset;
> uint32_t seqno, flush_domains, reloc_index;
> - int pin_tries;
> + int pin_tries, flips;
>
> #if WATCH_EXEC
> DRM_INFO("buffers_ptr %d buffer_count %d len %08x\n",
> @@ -3623,6 +3674,7 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
> }
>
> /* Look up object handles */
> + flips = 0;
> for (i = 0; i < args->buffer_count; i++) {
> object_list[i] = drm_gem_object_lookup(dev, file_priv,
> exec_list[i].handle);
> @@ -3641,6 +3693,14 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
> goto err;
> }
> obj_priv->in_execbuffer = true;
> + flips += atomic_read(&obj_priv->pending_flip);
> + }
> +
> + if (flips > 0) {
> + ret = i915_gem_wait_for_pending_flip(dev, object_list,
> + args->buffer_count);
> + if (ret)
> + goto err;
> }
>
> /* Pin and relocate */
> @@ -4625,8 +4685,8 @@ i915_gem_load(struct drm_device *dev)
> for (i = 0; i < 8; i++)
> I915_WRITE(FENCE_REG_945_8 + (i * 4), 0);
> }
> -
> i915_gem_detect_bit_6_swizzle(dev);
> + init_waitqueue_head(&dev_priv->pending_flip_queue);
> }
>
> /*
> diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
> index 024fb95..86abd6b 100644
> --- a/drivers/gpu/drm/i915/i915_irq.c
> +++ b/drivers/gpu/drm/i915/i915_irq.c
> @@ -46,6 +46,8 @@
> #define I915_INTERRUPT_ENABLE_FIX (I915_ASLE_INTERRUPT | \
> I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | \
> I915_DISPLAY_PIPE_B_EVENT_INTERRUPT | \
> + I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT | \
> + I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT | \
> I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT)
>
> /** Interrupts that we mask and unmask at runtime. */
> @@ -664,14 +666,22 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
> mod_timer(&dev_priv->hangcheck_timer, jiffies + DRM_I915_HANGCHECK_PERIOD);
> }
>
> + if (iir & I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT)
> + intel_prepare_page_flip(dev, 0);
> +
> + if (iir & I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT)
> + intel_prepare_page_flip(dev, 1);
> +
> if (pipea_stats & vblank_status) {
> vblank++;
> drm_handle_vblank(dev, 0);
> + intel_finish_page_flip(dev, 0);
> }
>
> if (pipeb_stats & vblank_status) {
> vblank++;
> drm_handle_vblank(dev, 1);
> + intel_finish_page_flip(dev, 1);
> }
>
> if ((pipeb_stats & I915_LEGACY_BLC_EVENT_STATUS) ||
> diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
> index b11a682..210fe85 100644
> --- a/drivers/gpu/drm/i915/i915_reg.h
> +++ b/drivers/gpu/drm/i915/i915_reg.h
> @@ -157,6 +157,8 @@
> #define MI_OVERLAY_ON (0x1<<21)
> #define MI_OVERLAY_OFF (0x2<<21)
> #define MI_LOAD_SCAN_LINES_INCL MI_INSTR(0x12, 0)
> +#define MI_DISPLAY_FLIP MI_INSTR(0x14, 2)
> +#define MI_DISPLAY_FLIP_PLANE(n) ((n) << 20)
> #define MI_STORE_DWORD_IMM MI_INSTR(0x20, 1)
> #define MI_MEM_VIRTUAL (1 << 22) /* 965+ only */
> #define MI_STORE_DWORD_INDEX MI_INSTR(0x21, 1)
> diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
> index 33113c7..4cb3294 100644
> --- a/drivers/gpu/drm/i915/intel_display.c
> +++ b/drivers/gpu/drm/i915/intel_display.c
> @@ -1190,6 +1190,50 @@ out_disable:
> }
>
> static int
> +intel_pin_and_fence_fb_obj(struct drm_device *dev, struct drm_gem_object *obj)
> +{
> + struct drm_i915_gem_object *obj_priv = obj->driver_private;
> + u32 alignment;
> + int ret;
> +
> + switch (obj_priv->tiling_mode) {
> + case I915_TILING_NONE:
> + alignment = 64 * 1024;
> + break;
> + case I915_TILING_X:
> + /* pin() will align the object as required by fence */
> + alignment = 0;
> + break;
> + case I915_TILING_Y:
> + /* FIXME: Is this true? */
> + DRM_ERROR("Y tiled not allowed for scan out buffers\n");
> + return -EINVAL;
> + default:
> + BUG();
> + }
> +
> + ret = i915_gem_object_pin(obj, alignment);
> + if (ret != 0)
> + return ret;
> +
> + /* Install a fence for tiled scan-out. Pre-i965 always needs a
> + * fence, whereas 965+ only requires a fence if using
> + * framebuffer compression. For simplicity, we always install
> + * a fence as the cost is not that onerous.
> + */
> + if (obj_priv->fence_reg == I915_FENCE_REG_NONE &&
> + obj_priv->tiling_mode != I915_TILING_NONE) {
> + ret = i915_gem_object_get_fence_reg(obj);
> + if (ret != 0) {
> + i915_gem_object_unpin(obj);
> + return ret;
> + }
> + }
> +
> + return 0;
> +}
> +
> +static int
> intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
> struct drm_framebuffer *old_fb)
> {
> @@ -1208,7 +1252,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
> int dspstride = (plane == 0) ? DSPASTRIDE : DSPBSTRIDE;
> int dsptileoff = (plane == 0 ? DSPATILEOFF : DSPBTILEOFF);
> int dspcntr_reg = (plane == 0) ? DSPACNTR : DSPBCNTR;
> - u32 dspcntr, alignment;
> + u32 dspcntr;
> int ret;
>
> /* no fb bound */
> @@ -1230,24 +1274,8 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
> obj = intel_fb->obj;
> obj_priv = obj->driver_private;
>
> - switch (obj_priv->tiling_mode) {
> - case I915_TILING_NONE:
> - alignment = 64 * 1024;
> - break;
> - case I915_TILING_X:
> - /* pin() will align the object as required by fence */
> - alignment = 0;
> - break;
> - case I915_TILING_Y:
> - /* FIXME: Is this true? */
> - DRM_ERROR("Y tiled not allowed for scan out buffers\n");
> - return -EINVAL;
> - default:
> - BUG();
> - }
> -
> mutex_lock(&dev->struct_mutex);
> - ret = i915_gem_object_pin(obj, alignment);
> + ret = intel_pin_and_fence_fb_obj(dev, obj);
> if (ret != 0) {
> mutex_unlock(&dev->struct_mutex);
> return ret;
> @@ -1260,20 +1288,6 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
> return ret;
> }
>
> - /* Install a fence for tiled scan-out. Pre-i965 always needs a fence,
> - * whereas 965+ only requires a fence if using framebuffer compression.
> - * For simplicity, we always install a fence as the cost is not that onerous.
> - */
> - if (obj_priv->fence_reg == I915_FENCE_REG_NONE &&
> - obj_priv->tiling_mode != I915_TILING_NONE) {
> - ret = i915_gem_object_get_fence_reg(obj);
> - if (ret != 0) {
> - i915_gem_object_unpin(obj);
> - mutex_unlock(&dev->struct_mutex);
> - return ret;
> - }
> - }
> -
> dspcntr = I915_READ(dspcntr_reg);
> /* Mask out pixel format bits in case we change it */
> dspcntr &= ~DISPPLANE_PIXFORMAT_MASK;
> @@ -4034,6 +4048,158 @@ static void intel_crtc_destroy(struct drm_crtc *crtc)
> kfree(intel_crtc);
> }
>
> +struct intel_unpin_work {
> + struct work_struct work;
> + struct drm_device *dev;
> + struct drm_gem_object *obj;
> + struct drm_pending_vblank_event *event;
> + int pending;
> +};
> +
> +static void intel_unpin_work_fn(struct work_struct *__work)
> +{
> + struct intel_unpin_work *work =
> + container_of(__work, struct intel_unpin_work, work);
> +
> + mutex_lock(&work->dev->struct_mutex);
> + i915_gem_object_unpin(work->obj);
> + drm_gem_object_unreference(work->obj);
> + mutex_unlock(&work->dev->struct_mutex);
> + kfree(work);
> +}
> +
> +void intel_finish_page_flip(struct drm_device *dev, int pipe)
> +{
> + drm_i915_private_t *dev_priv = dev->dev_private;
> + struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe];
> + struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
> + struct intel_unpin_work *work;
> + struct drm_i915_gem_object *obj_priv;
> + struct drm_pending_vblank_event *e;
> + struct timeval now;
> + unsigned long flags;
> +
> + /* Ignore early vblank irqs */
> + if (intel_crtc == NULL)
> + return;
> +
> + spin_lock_irqsave(&dev->event_lock, flags);
> + work = intel_crtc->unpin_work;
> + if (work == NULL || !work->pending) {
> + spin_unlock_irqrestore(&dev->event_lock, flags);
> + return;
> + }
> +
> + intel_crtc->unpin_work = NULL;
> + drm_vblank_put(dev, intel_crtc->pipe);
> +
> + if (work->event) {
> + e = work->event;
> + do_gettimeofday(&now);
> + e->event.sequence = drm_vblank_count(dev, intel_crtc->pipe);
> + e->event.tv_sec = now.tv_sec;
> + e->event.tv_usec = now.tv_usec;
> + list_add_tail(&e->base.link,
> + &e->base.file_priv->event_list);
> + wake_up_interruptible(&e->base.file_priv->event_wait);
> + }
> +
> + spin_unlock_irqrestore(&dev->event_lock, flags);
> +
> + obj_priv = work->obj->driver_private;
> + if (atomic_dec_and_test(&obj_priv->pending_flip))
> + DRM_WAKEUP(&dev_priv->pending_flip_queue);
> + schedule_work(&work->work);
> +}
> +
> +void intel_prepare_page_flip(struct drm_device *dev, int plane)
> +{
> + drm_i915_private_t *dev_priv = dev->dev_private;
> + struct intel_crtc *intel_crtc =
> + to_intel_crtc(dev_priv->plane_to_crtc_mapping[plane]);
> + unsigned long flags;
> +
> + spin_lock_irqsave(&dev->event_lock, flags);
> + if (intel_crtc->unpin_work)
> + intel_crtc->unpin_work->pending = 1;
> + spin_unlock_irqrestore(&dev->event_lock, flags);
> +}
> +
> +static int intel_crtc_page_flip(struct drm_crtc *crtc,
> + struct drm_framebuffer *fb,
> + struct drm_pending_vblank_event *event)
> +{
> + struct drm_device *dev = crtc->dev;
> + struct drm_i915_private *dev_priv = dev->dev_private;
> + struct intel_framebuffer *intel_fb;
> + struct drm_i915_gem_object *obj_priv;
> + struct drm_gem_object *obj;
> + struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
> + struct intel_unpin_work *work;
> + unsigned long flags;
> + int ret;
> + RING_LOCALS;
> +
> + work = kzalloc(sizeof *work, GFP_KERNEL);
> + if (work == NULL)
> + return -ENOMEM;
> +
> + mutex_lock(&dev->struct_mutex);
> +
> + work->event = event;
> + work->dev = crtc->dev;
> + intel_fb = to_intel_framebuffer(crtc->fb);
> + work->obj = intel_fb->obj;
> + INIT_WORK(&work->work, intel_unpin_work_fn);
> +
> + /* We borrow the event spin lock for protecting unpin_work */
> + spin_lock_irqsave(&dev->event_lock, flags);
> + if (intel_crtc->unpin_work) {
> + spin_unlock_irqrestore(&dev->event_lock, flags);
> + kfree(work);
> + mutex_unlock(&dev->struct_mutex);
> + return -EBUSY;
> + }
> + intel_crtc->unpin_work = work;
> + spin_unlock_irqrestore(&dev->event_lock, flags);
> +
> + intel_fb = to_intel_framebuffer(fb);
> + obj = intel_fb->obj;
> +
> + ret = intel_pin_and_fence_fb_obj(dev, obj);
> + if (ret != 0) {
> + kfree(work);
> + mutex_unlock(&dev->struct_mutex);
> + return ret;
> + }
> +
> + /* Reference the old fb object for the scheduled work. */
> + drm_gem_object_reference(work->obj);
> +
> + crtc->fb = fb;
> + i915_gem_object_flush_write_domain(obj);
> + drm_vblank_get(dev, intel_crtc->pipe);
> + obj_priv = obj->driver_private;
> + atomic_inc(&obj_priv->pending_flip);
> +
> + BEGIN_LP_RING(4);
> + OUT_RING(MI_DISPLAY_FLIP |
> + MI_DISPLAY_FLIP_PLANE(intel_crtc->plane));
> + OUT_RING(fb->pitch);
> + if (IS_I965G(dev)) {
> + OUT_RING(obj_priv->gtt_offset | obj_priv->tiling_mode);
> + OUT_RING((fb->width << 16) | fb->height);
> + } else {
> + OUT_RING(obj_priv->gtt_offset);
> + OUT_RING(MI_NOOP);
> + }
> + ADVANCE_LP_RING();
> +
> + mutex_unlock(&dev->struct_mutex);
> +
> + return 0;
> +}
> +
> static const struct drm_crtc_helper_funcs intel_helper_funcs = {
> .dpms = intel_crtc_dpms,
> .mode_fixup = intel_crtc_mode_fixup,
> @@ -4050,12 +4216,14 @@ static const struct drm_crtc_funcs intel_crtc_funcs = {
> .gamma_set = intel_crtc_gamma_set,
> .set_config = drm_crtc_helper_set_config,
> .destroy = intel_crtc_destroy,
> + .page_flip = intel_crtc_page_flip,
> };
>
>
> static void intel_crtc_init(struct drm_device *dev, int pipe)
> {
> struct intel_crtc *intel_crtc;
> + struct drm_i915_private *dev_priv = dev->dev_private;
> int i;
>
> intel_crtc = kzalloc(sizeof(struct intel_crtc) + (INTELFB_CONN_LIMIT * sizeof(struct drm_connector *)), GFP_KERNEL);
> @@ -4081,6 +4249,11 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
> intel_crtc->plane = ((pipe == 0) ? 1 : 0);
> }
>
> + BUG_ON(pipe >= ARRAY_SIZE(dev_priv->plane_to_crtc_mapping) ||
> + dev_priv->plane_to_crtc_mapping[intel_crtc->plane] != NULL);
> + dev_priv->plane_to_crtc_mapping[intel_crtc->plane] = &intel_crtc->base;
> + dev_priv->pipe_to_crtc_mapping[intel_crtc->pipe] = &intel_crtc->base;
> +
> intel_crtc->cursor_addr = 0;
> intel_crtc->dpms_mode = DRM_MODE_DPMS_OFF;
> drm_crtc_helper_add(&intel_crtc->base, &intel_helper_funcs);
> diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
> index 4972405..3547bba 100644
> --- a/drivers/gpu/drm/i915/intel_drv.h
> +++ b/drivers/gpu/drm/i915/intel_drv.h
> @@ -148,6 +148,7 @@ struct intel_crtc {
> struct timer_list idle_timer;
> bool lowfreq_avail;
> struct intel_overlay *overlay;
> + struct intel_unpin_work *unpin_work;
> };
>
> #define to_intel_crtc(x) container_of(x, struct intel_crtc, base)
> @@ -211,6 +212,8 @@ extern int intel_framebuffer_create(struct drm_device *dev,
> struct drm_framebuffer **fb,
> struct drm_gem_object *obj);
>
> +extern void intel_prepare_page_flip(struct drm_device *dev, int plane);
> +extern void intel_finish_page_flip(struct drm_device *dev, int pipe);
> extern void intel_setup_overlay(struct drm_device *dev);
> extern void intel_cleanup_overlay(struct drm_device *dev);
> extern int intel_overlay_switch_off(struct intel_overlay *overlay);
> --
> 1.6.1.3
>
>
> ------------------------------------------------------------------------------
> Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day
> trial. Simplify your report design, integration and deployment - and focus on
> what you do best, core application coding. Discover what's new with
> Crystal Reports now. http://p.sf.net/sfu/bobj-july
> --
> _______________________________________________
> Dri-devel mailing list
> Dri-devel at lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/dri-devel
>
More information about the Intel-gfx
mailing list