2009/11/19 Jesse Barnes <jbar...@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?= <k...@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 <ja...@vmware.com> > Acked-by: Thomas Hellström <tho...@shipmail.org> > Review-by: Chris Wilson <ch...@chris-wilson.co.uk> > Signed-off-by: Jesse "Orange Smoothie" Barnes <jbar...@virtuousgeek.org> > Signed-off-by: Kristian Høgsberg <k...@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 <k...@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@lists.sourceforge.net > https://lists.sourceforge.net/lists/listinfo/dri-devel > ------------------------------------------------------------------------------ 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@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/dri-devel