X-Git-Url: http://git.cascardo.eti.br/?a=blobdiff_plain;f=drivers%2Fgpu%2Fdrm%2Fexynos%2Fexynos_drm_crtc.c;h=3fefe8859a1c417c281e7d276f1a95b473e8093c;hb=40b4936a2ee1f5721f6b267411ba4f22764089ef;hp=b19eb75394d8836dcf549a21991e20e6b2574f03;hpb=ebfb7b7a3269640a55efcc931f2fd92467cc797e;p=cascardo%2Flinux.git diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index b19eb75394d8..3fefe8859a1c 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -47,10 +47,10 @@ static void exynos_drm_crtc_apply(struct drm_crtc *crtc) exynos_drm_encoder_crtc_commit); } -int exynos_drm_overlay_update(struct exynos_drm_overlay *overlay, - struct drm_framebuffer *fb, - struct drm_display_mode *mode, - struct exynos_drm_crtc_pos *pos) +void exynos_drm_overlay_update(struct exynos_drm_overlay *overlay, + struct drm_framebuffer *fb, + struct drm_display_mode *mode, + struct exynos_drm_crtc_pos *pos) { struct exynos_drm_gem_buf *buffer; unsigned int actual_w; @@ -60,10 +60,6 @@ int exynos_drm_overlay_update(struct exynos_drm_overlay *overlay, for (i = 0; i < nr; i++) { buffer = exynos_drm_fb_buffer(fb, i); - if (!buffer) { - DRM_LOG_KMS("buffer is null\n"); - return -EFAULT; - } overlay->dma_addr[i] = buffer->dma_addr; overlay->vaddr[i] = buffer->kvaddr; @@ -100,46 +96,15 @@ int exynos_drm_overlay_update(struct exynos_drm_overlay *overlay, DRM_DEBUG_KMS("overlay : offset_x/y(%d,%d), width/height(%d,%d)", overlay->crtc_x, overlay->crtc_y, overlay->crtc_width, overlay->crtc_height); - - return 0; -} - -static void exynos_drm_crtc_page_flip_apply(struct drm_crtc *crtc) -{ - struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); - struct exynos_drm_overlay *overlay = &exynos_crtc->overlay; - struct drm_framebuffer *fb = crtc->fb; - int nr = exynos_drm_format_num_buffers(fb->pixel_format); - int i; - - for (i = 0; i < nr; i++) { - struct exynos_drm_gem_buf *buffer; - - buffer = exynos_drm_fb_buffer(fb, i); - overlay->dma_addr[i] = buffer->dma_addr; - overlay->vaddr[i] = buffer->kvaddr; - - DRM_DEBUG_KMS("buffer: %d, vaddr = 0x%lx, dma_addr = 0x%lx\n", - i, (unsigned long)overlay->vaddr[i], - (unsigned long)overlay->dma_addr[i]); - } - - exynos_drm_fn_encoder(crtc, overlay, - exynos_drm_encoder_crtc_page_flip); - exynos_drm_fn_encoder(crtc, &exynos_crtc->pipe, - exynos_drm_encoder_crtc_commit); } -static int exynos_drm_crtc_update(struct drm_crtc *crtc) +static void exynos_drm_crtc_update(struct drm_crtc *crtc, + struct drm_framebuffer *fb) { struct exynos_drm_crtc *exynos_crtc; struct exynos_drm_overlay *overlay; struct exynos_drm_crtc_pos pos; struct drm_display_mode *mode = &crtc->mode; - struct drm_framebuffer *fb = crtc->fb; - - if (!mode || !fb) - return -EINVAL; exynos_crtc = to_exynos_crtc(crtc); overlay = &exynos_crtc->overlay; @@ -158,7 +123,7 @@ static int exynos_drm_crtc_update(struct drm_crtc *crtc) pos.crtc_w = fb->width - crtc->x; pos.crtc_h = fb->height - crtc->y; - return exynos_drm_overlay_update(overlay, crtc->fb, mode, &pos); + exynos_drm_overlay_update(overlay, fb, mode, &pos); } static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode) @@ -228,6 +193,12 @@ static void exynos_drm_crtc_commit(struct drm_crtc *crtc) if (exynos_crtc->dpms != DRM_MODE_DPMS_ON) { int mode = DRM_MODE_DPMS_ON; + /* + * TODO(seanpaul): This has the nasty habit of calling the + * underlying dpms/power callbacks twice on boot. This code + * needs to be cleaned up so this doesn't happen. + */ + /* * enable hardware(power on) to all encoders hdmi connected * to current crtc. @@ -261,31 +232,38 @@ exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode, int x, int y, struct drm_framebuffer *old_fb) { + struct drm_framebuffer *fb = crtc->fb; + DRM_DEBUG_KMS("%s\n", __FILE__); + if (!fb) + return -EINVAL; + /* * copy the mode data adjusted by mode_fixup() into crtc->mode * so that hardware can be seet to proper mode. */ memcpy(&crtc->mode, adjusted_mode, sizeof(*adjusted_mode)); - return exynos_drm_crtc_update(crtc); + exynos_drm_crtc_update(crtc, fb); + + return 0; } static int exynos_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb) { - int ret; + struct drm_framebuffer *fb = crtc->fb; DRM_DEBUG_KMS("%s\n", __FILE__); - ret = exynos_drm_crtc_update(crtc); - if (ret) - return ret; + if (!fb) + return -EINVAL; + exynos_drm_crtc_update(crtc, fb); exynos_drm_crtc_apply(crtc); - return ret; + return 0; } static void exynos_drm_crtc_load_lut(struct drm_crtc *crtc) @@ -308,23 +286,49 @@ static struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = { #ifdef CONFIG_DMA_SHARED_BUFFER_USES_KDS void exynos_drm_kds_callback(void *callback_parameter, void *callback_extra_parameter) { - struct drm_crtc *crtc = (struct drm_crtc *)callback_parameter; - struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); - struct drm_framebuffer *fb = callback_extra_parameter; + struct drm_framebuffer *fb = callback_parameter; struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb); + struct drm_crtc *crtc = exynos_fb->crtc; + struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct kds_resource_set **pkds = callback_extra_parameter; + struct kds_resource_set *prev_kds; + unsigned long flags; - if (exynos_fb->kds_res_set) { - kds_resource_set_release(&exynos_fb->kds_res_set); - exynos_fb->kds_res_set = NULL; - } + exynos_drm_crtc_update(crtc, fb); + exynos_drm_crtc_apply(crtc); - exynos_drm_crtc_page_flip_apply(crtc); + spin_lock_irqsave(&dev->event_lock, flags); + prev_kds = exynos_crtc->pending_kds; + exynos_crtc->pending_kds = *pkds; + *pkds = NULL; + if (prev_kds) + exynos_crtc->flip_in_flight--; + spin_unlock_irqrestore(&dev->event_lock, flags); - BUG_ON(atomic_read(&exynos_crtc->flip_pending)); - atomic_set(&exynos_crtc->flip_pending, 1); + if (prev_kds) { + DRM_ERROR("previous work detected\n"); + kds_resource_set_release(&prev_kds); + } else { + BUG_ON(atomic_read(&exynos_crtc->flip_pending)); + atomic_set(&exynos_crtc->flip_pending, 1); + } } #endif +static void exynos_drm_crtc_flip_complete(struct drm_pending_vblank_event *e) +{ + struct timeval now; + + do_gettimeofday(&now); + e->event.sequence = 0; + 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); + trace_exynos_fake_flip_complete(e->pipe); +} + static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, struct drm_pending_vblank_event *event) @@ -332,37 +336,22 @@ static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc, struct drm_device *dev = crtc->dev; struct exynos_drm_private *dev_priv = dev->dev_private; struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); - int ret = -EINVAL; unsigned long flags; + int ret; #ifdef CONFIG_DMA_SHARED_BUFFER_USES_KDS struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb); struct exynos_drm_gem_obj *gem_ob = (struct exynos_drm_gem_obj *)exynos_fb->exynos_gem_obj[0]; + struct kds_resource_set **pkds; + struct drm_pending_vblank_event *event_to_send; #endif DRM_DEBUG_KMS("%s\n", __FILE__); - /* Record both request and complete of page flip within the function - * since this implementation is blocking in exynos_drm_crtc_update. - */ - trace_exynos_flip_request(exynos_crtc->pipe); - /* msb: The event flag is optional but exynos does not support it. */ if (!event) { DRM_ERROR("called page_flip with empty event flag\n"); return -EINVAL; } -#ifdef CONFIG_DMA_SHARED_BUFFER_USES_KDS - spin_lock_irqsave(&dev->event_lock, flags); - if (exynos_crtc->event) { - spin_unlock_irqrestore(&dev->event_lock, flags); - - DRM_DEBUG_DRIVER("flip queue: crtc already busy\n"); - return -EBUSY; - } - exynos_crtc->event = event; - spin_unlock_irqrestore(&dev->event_lock, flags); -#endif - /* * the pipe from user always is 0 so we can set pipe number * of current owner to event. @@ -370,8 +359,34 @@ static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc, event->pipe = exynos_crtc->pipe; ret = drm_vblank_get(dev, exynos_crtc->pipe); - if (ret) - goto fail_vblank; + if (ret) { + DRM_ERROR("Unable to get vblank\n"); + return -EINVAL; + } + +#ifdef CONFIG_DMA_SHARED_BUFFER_USES_KDS + spin_lock_irqsave(&dev->event_lock, flags); + if (exynos_crtc->flip_in_flight > 1) { + spin_unlock_irqrestore(&dev->event_lock, flags); + DRM_DEBUG_DRIVER("flip queue: crtc already busy\n"); + ret = -EBUSY; + goto fail_max_in_flight; + } + /* Signal previous flip event. Or if none in flight signal current. */ + if (exynos_crtc->flip_in_flight) { + event_to_send = exynos_crtc->event; + exynos_crtc->event = event; + } else { + event_to_send = event; + exynos_crtc->event = NULL; + } + pkds = &exynos_crtc->future_kds; + if (*pkds) + pkds = &exynos_crtc->future_kds_extra; + *pkds = ERR_PTR(-EINVAL); /* Make it non-NULL */ + exynos_crtc->flip_in_flight++; + spin_unlock_irqrestore(&dev->event_lock, flags); +#endif mutex_lock(&dev->struct_mutex); @@ -380,10 +395,11 @@ static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc, mutex_unlock(&dev->struct_mutex); #ifdef CONFIG_DMA_SHARED_BUFFER_USES_KDS + exynos_fb->crtc = crtc; if (gem_ob->base.export_dma_buf) { struct dma_buf *buf = gem_ob->base.export_dma_buf; - unsigned long shared[1] = {0}; - struct kds_resource *resource_list[1] = {get_dma_buf_kds_resource(buf)}; + unsigned long shared = 0UL; + struct kds_resource *res_list = get_dma_buf_kds_resource(buf); /* * If we don't already have a reference to the dma_buf, @@ -396,22 +412,37 @@ static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc, BUG_ON(exynos_fb->dma_buf != buf); /* Waiting for the KDS resource*/ - kds_async_waitall(&exynos_fb->kds_res_set, KDS_FLAG_LOCKED_WAIT, - &dev_priv->kds_cb, crtc, fb, 1, shared, - resource_list); + ret = kds_async_waitall(pkds, KDS_FLAG_LOCKED_WAIT, + &dev_priv->kds_cb, fb, pkds, 1, + &shared, &res_list); + if (ret) { + DRM_ERROR("kds_async_waitall failed: %d\n", ret); + goto fail_kds; + } } else { + *pkds = NULL; DRM_ERROR("flipping a non-kds buffer\n"); - exynos_drm_kds_callback(crtc, fb); + exynos_drm_kds_callback(fb, pkds); } -#endif - trace_exynos_flip_complete(exynos_crtc->pipe); - return ret; -fail_vblank: -#ifdef CONFIG_DMA_SHARED_BUFFER_USES_KDS - exynos_crtc->event = NULL; + if (event_to_send) { + spin_lock_irqsave(&dev->event_lock, flags); + exynos_drm_crtc_flip_complete(event_to_send); + spin_unlock_irqrestore(&dev->event_lock, flags); + } #endif - trace_exynos_flip_complete(exynos_crtc->pipe); + + trace_exynos_flip_request(exynos_crtc->pipe); + + return 0; + +fail_kds: + *pkds = NULL; + spin_lock_irqsave(&dev->event_lock, flags); + exynos_crtc->flip_in_flight--; + spin_unlock_irqrestore(&dev->event_lock, flags); +fail_max_in_flight: + drm_vblank_put(dev, exynos_crtc->pipe); return ret; } @@ -420,8 +451,7 @@ void exynos_drm_crtc_finish_pageflip(struct drm_device *drm_dev, int crtc_idx) struct exynos_drm_private *dev_priv = drm_dev->dev_private; struct drm_crtc *crtc = dev_priv->crtc[crtc_idx]; struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); - struct drm_pending_vblank_event *e; - struct timeval now; + struct kds_resource_set *kds; unsigned long flags; /* set wait vsync event to zero and wake up queue. */ @@ -431,23 +461,23 @@ void exynos_drm_crtc_finish_pageflip(struct drm_device *drm_dev, int crtc_idx) if (!atomic_cmpxchg(&exynos_crtc->flip_pending, 1, 0)) return; + trace_exynos_flip_complete(crtc_idx); + spin_lock_irqsave(&drm_dev->event_lock, flags); if (exynos_crtc->event) { - e = exynos_crtc->event; + exynos_drm_crtc_flip_complete(exynos_crtc->event); exynos_crtc->event = NULL; - do_gettimeofday(&now); - e->event.sequence = 0; - 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); } + kds = exynos_crtc->current_kds; + exynos_crtc->current_kds = exynos_crtc->pending_kds; + exynos_crtc->pending_kds = NULL; + exynos_crtc->flip_in_flight--; spin_unlock_irqrestore(&drm_dev->event_lock, flags); - drm_vblank_put(drm_dev, crtc_idx); + if (kds) + kds_resource_set_release(&kds); + drm_vblank_put(drm_dev, crtc_idx); } static void exynos_drm_crtc_destroy(struct drm_crtc *crtc)