diff options
| author | Kristian Høgsberg <krh@bitplanet.net> | 2009-11-18 11:25:18 -0500 |
|---|---|---|
| committer | Eric Anholt <eric@anholt.net> | 2009-12-01 12:10:35 -0500 |
| commit | 6b95a207c1fd552e7d017837c5eaf1b0173a48c9 (patch) | |
| tree | 02aed1633ab954faa3eea4b2a12775c28a1c5410 | |
| parent | f40d6817a5c2bf84f5fe7b5d1a83f1e8f8669951 (diff) | |
drm/i915: Add intel implementation of the pageflip ioctl
Acked-by: Jakob Bornecrantz <jakob@vmware.com>
Acked-by: Thomas Hellström <thomas@shipmail.org>
Review-by: Chris Wilson <chris@chris-wilson.co.uk>
Signed-off-by: Jesse "Orange Smoothie" Barnes <jbarnes@virtuousgeek.org>
Signed-off-by: Kristian Høgsberg <krh@bitplanet.net>
Signed-off-by: Eric Anholt <eric@anholt.net>
| -rw-r--r-- | drivers/gpu/drm/i915/i915_drv.h | 12 | ||||
| -rw-r--r-- | drivers/gpu/drm/i915/i915_gem.c | 64 | ||||
| -rw-r--r-- | drivers/gpu/drm/i915/i915_irq.c | 19 | ||||
| -rw-r--r-- | drivers/gpu/drm/i915/i915_reg.h | 2 | ||||
| -rw-r--r-- | drivers/gpu/drm/i915/intel_display.c | 228 | ||||
| -rw-r--r-- | drivers/gpu/drm/i915/intel_drv.h | 4 |
6 files changed, 291 insertions, 38 deletions
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 450dcf0d25c8..ca1ba42af566 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 { | |||
| 536 | /* indicate whether the LVDS_BORDER should be enabled or not */ | 536 | /* indicate whether the LVDS_BORDER should be enabled or not */ |
| 537 | unsigned int lvds_border_bits; | 537 | unsigned int lvds_border_bits; |
| 538 | 538 | ||
| 539 | struct drm_crtc *plane_to_crtc_mapping[2]; | ||
| 540 | struct drm_crtc *pipe_to_crtc_mapping[2]; | ||
| 541 | wait_queue_head_t pending_flip_queue; | ||
| 542 | |||
| 539 | /* Reclocking support */ | 543 | /* Reclocking support */ |
| 540 | bool render_reclock_avail; | 544 | bool render_reclock_avail; |
| 541 | bool lvds_downclock_avail; | 545 | bool lvds_downclock_avail; |
| @@ -639,6 +643,13 @@ struct drm_i915_gem_object { | |||
| 639 | * Advice: are the backing pages purgeable? | 643 | * Advice: are the backing pages purgeable? |
| 640 | */ | 644 | */ |
| 641 | int madv; | 645 | int madv; |
| 646 | |||
| 647 | /** | ||
| 648 | * Number of crtcs where this object is currently the fb, but | ||
| 649 | * will be page flipped away on the next vblank. When it | ||
| 650 | * reaches 0, dev_priv->pending_flip_queue will be woken up. | ||
| 651 | */ | ||
| 652 | atomic_t pending_flip; | ||
| 642 | }; | 653 | }; |
| 643 | 654 | ||
| 644 | /** | 655 | /** |
| @@ -830,6 +841,7 @@ void i915_gem_free_all_phys_object(struct drm_device *dev); | |||
| 830 | int i915_gem_object_get_pages(struct drm_gem_object *obj); | 841 | int i915_gem_object_get_pages(struct drm_gem_object *obj); |
| 831 | void i915_gem_object_put_pages(struct drm_gem_object *obj); | 842 | void i915_gem_object_put_pages(struct drm_gem_object *obj); |
| 832 | void i915_gem_release(struct drm_device * dev, struct drm_file *file_priv); | 843 | void i915_gem_release(struct drm_device * dev, struct drm_file *file_priv); |
| 844 | void i915_gem_object_flush_write_domain(struct drm_gem_object *obj); | ||
| 833 | 845 | ||
| 834 | void i915_gem_shrinker_init(void); | 846 | void i915_gem_shrinker_init(void); |
| 835 | void i915_gem_shrinker_exit(void); | 847 | 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 481c0ab888c8..214fb1864710 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) | |||
| 2771 | old_write_domain); | 2771 | old_write_domain); |
| 2772 | } | 2772 | } |
| 2773 | 2773 | ||
| 2774 | void | ||
| 2775 | i915_gem_object_flush_write_domain(struct drm_gem_object *obj) | ||
| 2776 | { | ||
| 2777 | switch (obj->write_domain) { | ||
| 2778 | case I915_GEM_DOMAIN_GTT: | ||
| 2779 | i915_gem_object_flush_gtt_write_domain(obj); | ||
| 2780 | break; | ||
| 2781 | case I915_GEM_DOMAIN_CPU: | ||
| 2782 | i915_gem_object_flush_cpu_write_domain(obj); | ||
| 2783 | break; | ||
| 2784 | default: | ||
| 2785 | i915_gem_object_flush_gpu_write_domain(obj); | ||
| 2786 | break; | ||
| 2787 | } | ||
| 2788 | } | ||
| 2789 | |||
| 2774 | /** | 2790 | /** |
| 2775 | * Moves a single object to the GTT read, and possibly write domain. | 2791 | * Moves a single object to the GTT read, and possibly write domain. |
| 2776 | * | 2792 | * |
| @@ -3536,6 +3552,41 @@ i915_gem_check_execbuffer (struct drm_i915_gem_execbuffer *exec, | |||
| 3536 | return 0; | 3552 | return 0; |
| 3537 | } | 3553 | } |
| 3538 | 3554 | ||
| 3555 | static int | ||
| 3556 | i915_gem_wait_for_pending_flip(struct drm_device *dev, | ||
| 3557 | struct drm_gem_object **object_list, | ||
| 3558 | int count) | ||
| 3559 | { | ||
| 3560 | drm_i915_private_t *dev_priv = dev->dev_private; | ||
| 3561 | struct drm_i915_gem_object *obj_priv; | ||
| 3562 | DEFINE_WAIT(wait); | ||
| 3563 | int i, ret = 0; | ||
| 3564 | |||
| 3565 | for (;;) { | ||
| 3566 | prepare_to_wait(&dev_priv->pending_flip_queue, | ||
| 3567 | &wait, TASK_INTERRUPTIBLE); | ||
| 3568 | for (i = 0; i < count; i++) { | ||
| 3569 | obj_priv = object_list[i]->driver_private; | ||
| 3570 | if (atomic_read(&obj_priv->pending_flip) > 0) | ||
| 3571 | break; | ||
| 3572 | } | ||
| 3573 | if (i == count) | ||
| 3574 | break; | ||
| 3575 | |||
| 3576 | if (!signal_pending(current)) { | ||
| 3577 | mutex_unlock(&dev->struct_mutex); | ||
| 3578 | schedule(); | ||
| 3579 | mutex_lock(&dev->struct_mutex); | ||
| 3580 | continue; | ||
| 3581 | } | ||
| 3582 | ret = -ERESTARTSYS; | ||
| 3583 | break; | ||
| 3584 | } | ||
| 3585 | finish_wait(&dev_priv->pending_flip_queue, &wait); | ||
| 3586 | |||
| 3587 | return ret; | ||
| 3588 | } | ||
| 3589 | |||
| 3539 | int | 3590 | int |
| 3540 | i915_gem_execbuffer(struct drm_device *dev, void *data, | 3591 | i915_gem_execbuffer(struct drm_device *dev, void *data, |
| 3541 | struct drm_file *file_priv) | 3592 | struct drm_file *file_priv) |
| @@ -3551,7 +3602,7 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, | |||
| 3551 | int ret, ret2, i, pinned = 0; | 3602 | int ret, ret2, i, pinned = 0; |
| 3552 | uint64_t exec_offset; | 3603 | uint64_t exec_offset; |
| 3553 | uint32_t seqno, flush_domains, reloc_index; | 3604 | uint32_t seqno, flush_domains, reloc_index; |
| 3554 | int pin_tries; | 3605 | int pin_tries, flips; |
| 3555 | 3606 | ||
| 3556 | #if WATCH_EXEC | 3607 | #if WATCH_EXEC |
| 3557 | DRM_INFO("buffers_ptr %d buffer_count %d len %08x\n", | 3608 | DRM_INFO("buffers_ptr %d buffer_count %d len %08x\n", |
| @@ -3623,6 +3674,7 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, | |||
| 3623 | } | 3674 | } |
| 3624 | 3675 | ||
| 3625 | /* Look up object handles */ | 3676 | /* Look up object handles */ |
| 3677 | flips = 0; | ||
| 3626 | for (i = 0; i < args->buffer_count; i++) { | 3678 | for (i = 0; i < args->buffer_count; i++) { |
| 3627 | object_list[i] = drm_gem_object_lookup(dev, file_priv, | 3679 | object_list[i] = drm_gem_object_lookup(dev, file_priv, |
| 3628 | exec_list[i].handle); | 3680 | exec_list[i].handle); |
| @@ -3641,6 +3693,14 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, | |||
| 3641 | goto err; | 3693 | goto err; |
| 3642 | } | 3694 | } |
| 3643 | obj_priv->in_execbuffer = true; | 3695 | obj_priv->in_execbuffer = true; |
| 3696 | flips += atomic_read(&obj_priv->pending_flip); | ||
| 3697 | } | ||
| 3698 | |||
| 3699 | if (flips > 0) { | ||
| 3700 | ret = i915_gem_wait_for_pending_flip(dev, object_list, | ||
| 3701 | args->buffer_count); | ||
| 3702 | if (ret) | ||
| 3703 | goto err; | ||
| 3644 | } | 3704 | } |
| 3645 | 3705 | ||
| 3646 | /* Pin and relocate */ | 3706 | /* Pin and relocate */ |
| @@ -4625,8 +4685,8 @@ i915_gem_load(struct drm_device *dev) | |||
| 4625 | for (i = 0; i < 8; i++) | 4685 | for (i = 0; i < 8; i++) |
| 4626 | I915_WRITE(FENCE_REG_945_8 + (i * 4), 0); | 4686 | I915_WRITE(FENCE_REG_945_8 + (i * 4), 0); |
| 4627 | } | 4687 | } |
| 4628 | |||
| 4629 | i915_gem_detect_bit_6_swizzle(dev); | 4688 | i915_gem_detect_bit_6_swizzle(dev); |
| 4689 | init_waitqueue_head(&dev_priv->pending_flip_queue); | ||
| 4630 | } | 4690 | } |
| 4631 | 4691 | ||
| 4632 | /* | 4692 | /* |
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 77bc1d28f744..e2d01b3fa171 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c | |||
| @@ -43,10 +43,13 @@ | |||
| 43 | * we leave them always unmasked in IMR and then control enabling them through | 43 | * we leave them always unmasked in IMR and then control enabling them through |
| 44 | * PIPESTAT alone. | 44 | * PIPESTAT alone. |
| 45 | */ | 45 | */ |
| 46 | #define I915_INTERRUPT_ENABLE_FIX (I915_ASLE_INTERRUPT | \ | 46 | #define I915_INTERRUPT_ENABLE_FIX \ |
| 47 | I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | \ | 47 | (I915_ASLE_INTERRUPT | \ |
| 48 | I915_DISPLAY_PIPE_B_EVENT_INTERRUPT | \ | 48 | I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | \ |
| 49 | I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT) | 49 | I915_DISPLAY_PIPE_B_EVENT_INTERRUPT | \ |
| 50 | I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT | \ | ||
| 51 | I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT | \ | ||
| 52 | I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT) | ||
| 50 | 53 | ||
| 51 | /** Interrupts that we mask and unmask at runtime. */ | 54 | /** Interrupts that we mask and unmask at runtime. */ |
| 52 | #define I915_INTERRUPT_ENABLE_VAR (I915_USER_INTERRUPT) | 55 | #define I915_INTERRUPT_ENABLE_VAR (I915_USER_INTERRUPT) |
| @@ -643,14 +646,22 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) | |||
| 643 | mod_timer(&dev_priv->hangcheck_timer, jiffies + DRM_I915_HANGCHECK_PERIOD); | 646 | mod_timer(&dev_priv->hangcheck_timer, jiffies + DRM_I915_HANGCHECK_PERIOD); |
| 644 | } | 647 | } |
| 645 | 648 | ||
| 649 | if (iir & I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT) | ||
| 650 | intel_prepare_page_flip(dev, 0); | ||
| 651 | |||
| 652 | if (iir & I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT) | ||
| 653 | intel_prepare_page_flip(dev, 1); | ||
| 654 | |||
| 646 | if (pipea_stats & vblank_status) { | 655 | if (pipea_stats & vblank_status) { |
| 647 | vblank++; | 656 | vblank++; |
| 648 | drm_handle_vblank(dev, 0); | 657 | drm_handle_vblank(dev, 0); |
| 658 | intel_finish_page_flip(dev, 0); | ||
| 649 | } | 659 | } |
| 650 | 660 | ||
| 651 | if (pipeb_stats & vblank_status) { | 661 | if (pipeb_stats & vblank_status) { |
| 652 | vblank++; | 662 | vblank++; |
| 653 | drm_handle_vblank(dev, 1); | 663 | drm_handle_vblank(dev, 1); |
| 664 | intel_finish_page_flip(dev, 1); | ||
| 654 | } | 665 | } |
| 655 | 666 | ||
| 656 | if ((pipeb_stats & I915_LEGACY_BLC_EVENT_STATUS) || | 667 | 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 d58f7ad91161..120c77dabcff 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h | |||
| @@ -157,6 +157,8 @@ | |||
| 157 | #define MI_OVERLAY_ON (0x1<<21) | 157 | #define MI_OVERLAY_ON (0x1<<21) |
| 158 | #define MI_OVERLAY_OFF (0x2<<21) | 158 | #define MI_OVERLAY_OFF (0x2<<21) |
| 159 | #define MI_LOAD_SCAN_LINES_INCL MI_INSTR(0x12, 0) | 159 | #define MI_LOAD_SCAN_LINES_INCL MI_INSTR(0x12, 0) |
| 160 | #define MI_DISPLAY_FLIP MI_INSTR(0x14, 2) | ||
| 161 | #define MI_DISPLAY_FLIP_PLANE(n) ((n) << 20) | ||
| 160 | #define MI_STORE_DWORD_IMM MI_INSTR(0x20, 1) | 162 | #define MI_STORE_DWORD_IMM MI_INSTR(0x20, 1) |
| 161 | #define MI_MEM_VIRTUAL (1 << 22) /* 965+ only */ | 163 | #define MI_MEM_VIRTUAL (1 << 22) /* 965+ only */ |
| 162 | #define MI_STORE_DWORD_INDEX MI_INSTR(0x21, 1) | 164 | #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 002c07daf9b8..b63a25f0f86d 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c | |||
| @@ -1191,6 +1191,51 @@ out_disable: | |||
| 1191 | } | 1191 | } |
| 1192 | 1192 | ||
| 1193 | static int | 1193 | static int |
| 1194 | intel_pin_and_fence_fb_obj(struct drm_device *dev, struct drm_gem_object *obj) | ||
| 1195 | { | ||
| 1196 | struct drm_i915_gem_object *obj_priv = obj->driver_private; | ||
| 1197 | u32 alignment; | ||
| 1198 | int ret; | ||
| 1199 | |||
| 1200 | switch (obj_priv->tiling_mode) { | ||
| 1201 | case I915_TILING_NONE: | ||
| 1202 | alignment = 64 * 1024; | ||
| 1203 | break; | ||
| 1204 | case I915_TILING_X: | ||
| 1205 | /* pin() will align the object as required by fence */ | ||
| 1206 | alignment = 0; | ||
| 1207 | break; | ||
| 1208 | case I915_TILING_Y: | ||
| 1209 | /* FIXME: Is this true? */ | ||
| 1210 | DRM_ERROR("Y tiled not allowed for scan out buffers\n"); | ||
| 1211 | return -EINVAL; | ||
| 1212 | default: | ||
| 1213 | BUG(); | ||
| 1214 | } | ||
| 1215 | |||
| 1216 | alignment = 256 * 1024; | ||
| 1217 | ret = i915_gem_object_pin(obj, alignment); | ||
| 1218 | if (ret != 0) | ||
| 1219 | return ret; | ||
| 1220 | |||
| 1221 | /* Install a fence for tiled scan-out. Pre-i965 always needs a | ||
| 1222 | * fence, whereas 965+ only requires a fence if using | ||
| 1223 | * framebuffer compression. For simplicity, we always install | ||
| 1224 | * a fence as the cost is not that onerous. | ||
| 1225 | */ | ||
| 1226 | if (obj_priv->fence_reg == I915_FENCE_REG_NONE && | ||
| 1227 | obj_priv->tiling_mode != I915_TILING_NONE) { | ||
| 1228 | ret = i915_gem_object_get_fence_reg(obj); | ||
| 1229 | if (ret != 0) { | ||
| 1230 | i915_gem_object_unpin(obj); | ||
| 1231 | return ret; | ||
| 1232 | } | ||
| 1233 | } | ||
| 1234 | |||
| 1235 | return 0; | ||
| 1236 | } | ||
| 1237 | |||
| 1238 | static int | ||
| 1194 | intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, | 1239 | intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, |
| 1195 | struct drm_framebuffer *old_fb) | 1240 | struct drm_framebuffer *old_fb) |
| 1196 | { | 1241 | { |
| @@ -1209,7 +1254,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, | |||
| 1209 | int dspstride = (plane == 0) ? DSPASTRIDE : DSPBSTRIDE; | 1254 | int dspstride = (plane == 0) ? DSPASTRIDE : DSPBSTRIDE; |
| 1210 | int dsptileoff = (plane == 0 ? DSPATILEOFF : DSPBTILEOFF); | 1255 | int dsptileoff = (plane == 0 ? DSPATILEOFF : DSPBTILEOFF); |
| 1211 | int dspcntr_reg = (plane == 0) ? DSPACNTR : DSPBCNTR; | 1256 | int dspcntr_reg = (plane == 0) ? DSPACNTR : DSPBCNTR; |
| 1212 | u32 dspcntr, alignment; | 1257 | u32 dspcntr; |
| 1213 | int ret; | 1258 | int ret; |
| 1214 | 1259 | ||
| 1215 | /* no fb bound */ | 1260 | /* no fb bound */ |
| @@ -1231,24 +1276,8 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, | |||
| 1231 | obj = intel_fb->obj; | 1276 | obj = intel_fb->obj; |
| 1232 | obj_priv = obj->driver_private; | 1277 | obj_priv = obj->driver_private; |
| 1233 | 1278 | ||
| 1234 | switch (obj_priv->tiling_mode) { | ||
| 1235 | case I915_TILING_NONE: | ||
| 1236 | alignment = 64 * 1024; | ||
| 1237 | break; | ||
| 1238 | case I915_TILING_X: | ||
| 1239 | /* pin() will align the object as required by fence */ | ||
| 1240 | alignment = 0; | ||
| 1241 | break; | ||
| 1242 | case I915_TILING_Y: | ||
| 1243 | /* FIXME: Is this true? */ | ||
| 1244 | DRM_ERROR("Y tiled not allowed for scan out buffers\n"); | ||
| 1245 | return -EINVAL; | ||
| 1246 | default: | ||
| 1247 | BUG(); | ||
| 1248 | } | ||
| 1249 | |||
| 1250 | mutex_lock(&dev->struct_mutex); | 1279 | mutex_lock(&dev->struct_mutex); |
| 1251 | ret = i915_gem_object_pin(obj, alignment); | 1280 | ret = intel_pin_and_fence_fb_obj(dev, obj); |
| 1252 | if (ret != 0) { | 1281 | if (ret != 0) { |
| 1253 | mutex_unlock(&dev->struct_mutex); | 1282 | mutex_unlock(&dev->struct_mutex); |
| 1254 | return ret; | 1283 | return ret; |
| @@ -1261,20 +1290,6 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, | |||
| 1261 | return ret; | 1290 | return ret; |
| 1262 | } | 1291 | } |
| 1263 | 1292 | ||
| 1264 | /* Install a fence for tiled scan-out. Pre-i965 always needs a fence, | ||
| 1265 | * whereas 965+ only requires a fence if using framebuffer compression. | ||
| 1266 | * For simplicity, we always install a fence as the cost is not that onerous. | ||
| 1267 | */ | ||
| 1268 | if (obj_priv->fence_reg == I915_FENCE_REG_NONE && | ||
| 1269 | obj_priv->tiling_mode != I915_TILING_NONE) { | ||
| 1270 | ret = i915_gem_object_get_fence_reg(obj); | ||
| 1271 | if (ret != 0) { | ||
| 1272 | i915_gem_object_unpin(obj); | ||
| 1273 | mutex_unlock(&dev->struct_mutex); | ||
| 1274 | return ret; | ||
| 1275 | } | ||
| 1276 | } | ||
| 1277 | |||
| 1278 | dspcntr = I915_READ(dspcntr_reg); | 1293 | dspcntr = I915_READ(dspcntr_reg); |
| 1279 | /* Mask out pixel format bits in case we change it */ | 1294 | /* Mask out pixel format bits in case we change it */ |
| 1280 | dspcntr &= ~DISPPLANE_PIXFORMAT_MASK; | 1295 | dspcntr &= ~DISPPLANE_PIXFORMAT_MASK; |
| @@ -4068,6 +4083,153 @@ static void intel_crtc_destroy(struct drm_crtc *crtc) | |||
| 4068 | kfree(intel_crtc); | 4083 | kfree(intel_crtc); |
| 4069 | } | 4084 | } |
| 4070 | 4085 | ||
| 4086 | struct intel_unpin_work { | ||
| 4087 | struct work_struct work; | ||
| 4088 | struct drm_device *dev; | ||
| 4089 | struct drm_gem_object *obj; | ||
| 4090 | struct drm_pending_vblank_event *event; | ||
| 4091 | int pending; | ||
| 4092 | }; | ||
| 4093 | |||
| 4094 | static void intel_unpin_work_fn(struct work_struct *__work) | ||
| 4095 | { | ||
| 4096 | struct intel_unpin_work *work = | ||
| 4097 | container_of(__work, struct intel_unpin_work, work); | ||
| 4098 | |||
| 4099 | mutex_lock(&work->dev->struct_mutex); | ||
| 4100 | i915_gem_object_unpin(work->obj); | ||
| 4101 | drm_gem_object_unreference(work->obj); | ||
| 4102 | mutex_unlock(&work->dev->struct_mutex); | ||
| 4103 | kfree(work); | ||
| 4104 | } | ||
| 4105 | |||
| 4106 | void intel_finish_page_flip(struct drm_device *dev, int pipe) | ||
| 4107 | { | ||
| 4108 | drm_i915_private_t *dev_priv = dev->dev_private; | ||
| 4109 | struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe]; | ||
| 4110 | struct intel_crtc *intel_crtc = to_intel_crtc(crtc); | ||
| 4111 | struct intel_unpin_work *work; | ||
| 4112 | struct drm_i915_gem_object *obj_priv; | ||
| 4113 | struct drm_pending_vblank_event *e; | ||
| 4114 | struct timeval now; | ||
| 4115 | unsigned long flags; | ||
| 4116 | |||
| 4117 | /* Ignore early vblank irqs */ | ||
| 4118 | if (intel_crtc == NULL) | ||
| 4119 | return; | ||
| 4120 | |||
| 4121 | spin_lock_irqsave(&dev->event_lock, flags); | ||
| 4122 | work = intel_crtc->unpin_work; | ||
| 4123 | if (work == NULL || !work->pending) { | ||
| 4124 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
| 4125 | return; | ||
| 4126 | } | ||
| 4127 | |||
| 4128 | intel_crtc->unpin_work = NULL; | ||
| 4129 | drm_vblank_put(dev, intel_crtc->pipe); | ||
| 4130 | |||
| 4131 | if (work->event) { | ||
| 4132 | e = work->event; | ||
| 4133 | do_gettimeofday(&now); | ||
| 4134 | e->event.sequence = drm_vblank_count(dev, intel_crtc->pipe); | ||
| 4135 | e->event.tv_sec = now.tv_sec; | ||
| 4136 | e->event.tv_usec = now.tv_usec; | ||
| 4137 | list_add_tail(&e->base.link, | ||
| 4138 | &e->base.file_priv->event_list); | ||
| 4139 | wake_up_interruptible(&e->base.file_priv->event_wait); | ||
| 4140 | } | ||
| 4141 | |||
| 4142 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
| 4143 | |||
| 4144 | obj_priv = work->obj->driver_private; | ||
| 4145 | if (atomic_dec_and_test(&obj_priv->pending_flip)) | ||
| 4146 | DRM_WAKEUP(&dev_priv->pending_flip_queue); | ||
| 4147 | schedule_work(&work->work); | ||
| 4148 | } | ||
| 4149 | |||
| 4150 | void intel_prepare_page_flip(struct drm_device *dev, int plane) | ||
| 4151 | { | ||
| 4152 | drm_i915_private_t *dev_priv = dev->dev_private; | ||
| 4153 | struct intel_crtc *intel_crtc = | ||
| 4154 | to_intel_crtc(dev_priv->plane_to_crtc_mapping[plane]); | ||
| 4155 | unsigned long flags; | ||
| 4156 | |||
| 4157 | spin_lock_irqsave(&dev->event_lock, flags); | ||
| 4158 | if (intel_crtc->unpin_work) | ||
| 4159 | intel_crtc->unpin_work->pending = 1; | ||
| 4160 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
| 4161 | } | ||
| 4162 | |||
| 4163 | static int intel_crtc_page_flip(struct drm_crtc *crtc, | ||
| 4164 | struct drm_framebuffer *fb, | ||
| 4165 | struct drm_pending_vblank_event *event) | ||
| 4166 | { | ||
| 4167 | struct drm_device *dev = crtc->dev; | ||
| 4168 | struct drm_i915_private *dev_priv = dev->dev_private; | ||
| 4169 | struct intel_framebuffer *intel_fb; | ||
| 4170 | struct drm_i915_gem_object *obj_priv; | ||
| 4171 | struct drm_gem_object *obj; | ||
| 4172 | struct intel_crtc *intel_crtc = to_intel_crtc(crtc); | ||
| 4173 | struct intel_unpin_work *work; | ||
| 4174 | unsigned long flags; | ||
| 4175 | int ret; | ||
| 4176 | RING_LOCALS; | ||
| 4177 | |||
| 4178 | work = kzalloc(sizeof *work, GFP_KERNEL); | ||
| 4179 | if (work == NULL) | ||
| 4180 | return -ENOMEM; | ||
| 4181 | |||
| 4182 | mutex_lock(&dev->struct_mutex); | ||
| 4183 | |||
| 4184 | work->event = event; | ||
| 4185 | work->dev = crtc->dev; | ||
| 4186 | intel_fb = to_intel_framebuffer(crtc->fb); | ||
| 4187 | work->obj = intel_fb->obj; | ||
| 4188 | INIT_WORK(&work->work, intel_unpin_work_fn); | ||
| 4189 | |||
| 4190 | /* We borrow the event spin lock for protecting unpin_work */ | ||
| 4191 | spin_lock_irqsave(&dev->event_lock, flags); | ||
| 4192 | if (intel_crtc->unpin_work) { | ||
| 4193 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
| 4194 | kfree(work); | ||
| 4195 | mutex_unlock(&dev->struct_mutex); | ||
| 4196 | return -EBUSY; | ||
| 4197 | } | ||
| 4198 | intel_crtc->unpin_work = work; | ||
| 4199 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
| 4200 | |||
| 4201 | intel_fb = to_intel_framebuffer(fb); | ||
| 4202 | obj = intel_fb->obj; | ||
| 4203 | |||
| 4204 | ret = intel_pin_and_fence_fb_obj(dev, obj); | ||
| 4205 | if (ret != 0) { | ||
| 4206 | kfree(work); | ||
| 4207 | mutex_unlock(&dev->struct_mutex); | ||
| 4208 | return ret; | ||
| 4209 | } | ||
| 4210 | |||
| 4211 | /* Reference the old fb object for the scheduled work. */ | ||
| 4212 | drm_gem_object_reference(work->obj); | ||
| 4213 | |||
| 4214 | crtc->fb = fb; | ||
| 4215 | i915_gem_object_flush_write_domain(obj); | ||
| 4216 | drm_vblank_get(dev, intel_crtc->pipe); | ||
| 4217 | obj_priv = obj->driver_private; | ||
| 4218 | atomic_inc(&obj_priv->pending_flip); | ||
| 4219 | |||
| 4220 | BEGIN_LP_RING(4); | ||
| 4221 | OUT_RING(MI_DISPLAY_FLIP | | ||
| 4222 | MI_DISPLAY_FLIP_PLANE(intel_crtc->plane)); | ||
| 4223 | OUT_RING(fb->pitch); | ||
| 4224 | OUT_RING(obj_priv->gtt_offset | obj_priv->tiling_mode); | ||
| 4225 | OUT_RING((fb->width << 16) | fb->height); | ||
| 4226 | ADVANCE_LP_RING(); | ||
| 4227 | |||
| 4228 | mutex_unlock(&dev->struct_mutex); | ||
| 4229 | |||
| 4230 | return 0; | ||
| 4231 | } | ||
| 4232 | |||
| 4071 | static const struct drm_crtc_helper_funcs intel_helper_funcs = { | 4233 | static const struct drm_crtc_helper_funcs intel_helper_funcs = { |
| 4072 | .dpms = intel_crtc_dpms, | 4234 | .dpms = intel_crtc_dpms, |
| 4073 | .mode_fixup = intel_crtc_mode_fixup, | 4235 | .mode_fixup = intel_crtc_mode_fixup, |
| @@ -4084,12 +4246,14 @@ static const struct drm_crtc_funcs intel_crtc_funcs = { | |||
| 4084 | .gamma_set = intel_crtc_gamma_set, | 4246 | .gamma_set = intel_crtc_gamma_set, |
| 4085 | .set_config = drm_crtc_helper_set_config, | 4247 | .set_config = drm_crtc_helper_set_config, |
| 4086 | .destroy = intel_crtc_destroy, | 4248 | .destroy = intel_crtc_destroy, |
| 4249 | .page_flip = intel_crtc_page_flip, | ||
| 4087 | }; | 4250 | }; |
| 4088 | 4251 | ||
| 4089 | 4252 | ||
| 4090 | static void intel_crtc_init(struct drm_device *dev, int pipe) | 4253 | static void intel_crtc_init(struct drm_device *dev, int pipe) |
| 4091 | { | 4254 | { |
| 4092 | struct intel_crtc *intel_crtc; | 4255 | struct intel_crtc *intel_crtc; |
| 4256 | struct drm_i915_private *dev_priv = dev->dev_private; | ||
| 4093 | int i; | 4257 | int i; |
| 4094 | 4258 | ||
| 4095 | intel_crtc = kzalloc(sizeof(struct intel_crtc) + (INTELFB_CONN_LIMIT * sizeof(struct drm_connector *)), GFP_KERNEL); | 4259 | intel_crtc = kzalloc(sizeof(struct intel_crtc) + (INTELFB_CONN_LIMIT * sizeof(struct drm_connector *)), GFP_KERNEL); |
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 497240581c6a..8a22f2508899 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h | |||
| @@ -148,6 +148,7 @@ struct intel_crtc { | |||
| 148 | struct timer_list idle_timer; | 148 | struct timer_list idle_timer; |
| 149 | bool lowfreq_avail; | 149 | bool lowfreq_avail; |
| 150 | struct intel_overlay *overlay; | 150 | struct intel_overlay *overlay; |
| 151 | struct intel_unpin_work *unpin_work; | ||
| 151 | }; | 152 | }; |
| 152 | 153 | ||
| 153 | #define to_intel_crtc(x) container_of(x, struct intel_crtc, base) | 154 | #define to_intel_crtc(x) container_of(x, struct intel_crtc, base) |
| @@ -211,6 +212,9 @@ extern int intel_framebuffer_create(struct drm_device *dev, | |||
| 211 | struct drm_framebuffer **fb, | 212 | struct drm_framebuffer **fb, |
| 212 | struct drm_gem_object *obj); | 213 | struct drm_gem_object *obj); |
| 213 | 214 | ||
| 215 | extern void intel_prepare_page_flip(struct drm_device *dev, int plane); | ||
| 216 | extern void intel_finish_page_flip(struct drm_device *dev, int pipe); | ||
| 217 | |||
| 214 | extern void intel_setup_overlay(struct drm_device *dev); | 218 | extern void intel_setup_overlay(struct drm_device *dev); |
| 215 | extern void intel_cleanup_overlay(struct drm_device *dev); | 219 | extern void intel_cleanup_overlay(struct drm_device *dev); |
| 216 | extern int intel_overlay_switch_off(struct intel_overlay *overlay); | 220 | extern int intel_overlay_switch_off(struct intel_overlay *overlay); |
