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); |