This also limits the maximum frequency of page flips by the vrefresh rate. Signed-off-by: Haixia Shi <hshi@xxxxxxxxxxxx> Reviewed-by: Stéphane Marchesin <marcheu@xxxxxxxxxxxx> Tested-by: Haixia Shi <hshi@xxxxxxxxxxxx> --- drivers/gpu/drm/udl/udl_drv.h | 3 + drivers/gpu/drm/udl/udl_modeset.c | 129 ++++++++++++++++++++++++++++++++++---- 2 files changed, 119 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/udl/udl_drv.h b/drivers/gpu/drm/udl/udl_drv.h index 80adbac..b1e9fae 100644 --- a/drivers/gpu/drm/udl/udl_drv.h +++ b/drivers/gpu/drm/udl/udl_drv.h @@ -47,6 +47,7 @@ struct urb_list { }; struct udl_fbdev; +struct udl_flip_queue; struct udl_device { struct device *dev; @@ -65,6 +66,8 @@ struct udl_device { atomic_t bytes_identical; /* saved effort with backbuffer comparison */ atomic_t bytes_sent; /* to usb, after compression including overhead */ atomic_t cpu_kcycles_used; /* transpired during pixel processing */ + + struct udl_flip_queue *flip_queue; }; struct udl_gem_object { diff --git a/drivers/gpu/drm/udl/udl_modeset.c b/drivers/gpu/drm/udl/udl_modeset.c index 677190a6..f3804b4 100644 --- a/drivers/gpu/drm/udl/udl_modeset.c +++ b/drivers/gpu/drm/udl/udl_modeset.c @@ -303,6 +303,16 @@ udl_pipe_set_base(struct drm_crtc *crtc, int x, int y, } #endif +struct udl_flip_queue { + struct mutex lock; + struct workqueue_struct *wq; + struct delayed_work work; + struct drm_crtc *crtc; + struct drm_pending_vblank_event *event; + u64 flip_time; /* in jiffies */ + u64 vblank_interval; /* in jiffies */ +}; + static int udl_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode, @@ -313,6 +323,7 @@ static int udl_crtc_mode_set(struct drm_crtc *crtc, struct drm_device *dev = crtc->dev; struct udl_framebuffer *ufb = to_udl_fb(crtc->primary->fb); struct udl_device *udl = dev->dev_private; + struct udl_flip_queue *flip_queue = udl->flip_queue; char *buf; char *wrptr; int color_depth = 0; @@ -347,6 +358,13 @@ static int udl_crtc_mode_set(struct drm_crtc *crtc, ufb->active_16 = true; udl->mode_buf_len = wrptr - buf; + /* update flip queue vblank interval */ + if (flip_queue) { + mutex_lock(&flip_queue->lock); + flip_queue->vblank_interval = HZ / mode->vrefresh; + mutex_unlock(&flip_queue->lock); + } + /* damage all of it */ udl_handle_damage(ufb, 0, 0, ufb->base.width, ufb->base.height); return 0; @@ -364,29 +382,82 @@ static void udl_crtc_destroy(struct drm_crtc *crtc) kfree(crtc); } +static void udl_sched_page_flip(struct work_struct *work) +{ + struct udl_flip_queue *flip_queue = + container_of(container_of(work, struct delayed_work, work), + struct udl_flip_queue, work); + struct drm_crtc *crtc; + struct drm_device *dev; + struct drm_pending_vblank_event *event; + struct drm_framebuffer *fb; + + mutex_lock(&flip_queue->lock); + crtc = flip_queue->crtc; + dev = crtc->dev; + event = flip_queue->event; + fb = crtc->primary->fb; + flip_queue->event = NULL; + mutex_unlock(&flip_queue->lock); + + if (fb) + udl_handle_damage(to_udl_fb(fb), 0, 0, fb->width, fb->height); + if (event) { + unsigned long flags; + spin_lock_irqsave(&dev->event_lock, flags); + drm_send_vblank_event(dev, 0, event); + spin_unlock_irqrestore(&dev->event_lock, flags); + } +} + static int udl_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, struct drm_pending_vblank_event *event, uint32_t page_flip_flags) { - struct udl_framebuffer *ufb = to_udl_fb(fb); struct drm_device *dev = crtc->dev; - unsigned long flags; + struct udl_device *udl = dev->dev_private; + struct udl_flip_queue *flip_queue = udl->flip_queue; - struct drm_framebuffer *old_fb = crtc->primary->fb; - if (old_fb) { - struct udl_framebuffer *uold_fb = to_udl_fb(old_fb); - uold_fb->active_16 = false; + if (!flip_queue || !flip_queue->wq) { + DRM_ERROR("Uninitialized page flip queue\n"); + return -ENOMEM; } - ufb->active_16 = true; - udl_handle_damage(ufb, 0, 0, fb->width, fb->height); + mutex_lock(&flip_queue->lock); + + flip_queue->crtc = crtc; + if (fb) { + struct drm_framebuffer *old_fb; + struct udl_framebuffer *ufb; + ufb = to_udl_fb(fb); + old_fb = crtc->primary->fb; + if (old_fb) { + struct udl_framebuffer *uold_fb = to_udl_fb(old_fb); + uold_fb->active_16 = false; + } + ufb->active_16 = true; + crtc->primary->fb = fb; + } + if (event) { + if (flip_queue->event) { + unsigned long flags; + spin_lock_irqsave(&dev->event_lock, flags); + drm_send_vblank_event(dev, 0, flip_queue->event); + spin_unlock_irqrestore(&dev->event_lock, flags); + } + flip_queue->event = event; + } + if (!delayed_work_pending(&flip_queue->work)) { + u64 now = jiffies; + u64 next_flip = + flip_queue->flip_time + flip_queue->vblank_interval; + flip_queue->flip_time = (next_flip < now) ? now : next_flip; + queue_delayed_work(flip_queue->wq, &flip_queue->work, + flip_queue->flip_time - now); + } - spin_lock_irqsave(&dev->event_lock, flags); - if (event) - drm_send_vblank_event(dev, 0, event); - spin_unlock_irqrestore(&dev->event_lock, flags); - crtc->primary->fb = fb; + mutex_unlock(&flip_queue->lock); return 0; } @@ -429,6 +500,35 @@ static int udl_crtc_init(struct drm_device *dev) return 0; } +static void udl_flip_workqueue_init(struct drm_device *dev) +{ + struct udl_device *udl = dev->dev_private; + struct udl_flip_queue *flip_queue = + kzalloc(sizeof(struct udl_flip_queue), GFP_KERNEL); + BUG_ON(!flip_queue); + mutex_init(&flip_queue->lock); + flip_queue->wq = create_singlethread_workqueue("flip"); + BUG_ON(!flip_queue->wq); + INIT_DELAYED_WORK(&flip_queue->work, udl_sched_page_flip); + flip_queue->flip_time = jiffies; + flip_queue->vblank_interval = HZ / 60; + udl->flip_queue = flip_queue; +} + +static void udl_flip_workqueue_cleanup(struct drm_device *dev) +{ + struct udl_device *udl = dev->dev_private; + struct udl_flip_queue *flip_queue = udl->flip_queue; + if (!flip_queue) + return; + if (flip_queue->wq) { + flush_workqueue(flip_queue->wq); + destroy_workqueue(flip_queue->wq); + } + mutex_destroy(&flip_queue->lock); + kfree(flip_queue); +} + static const struct drm_mode_config_funcs udl_mode_funcs = { .fb_create = udl_fb_user_fb_create, .output_poll_changed = NULL, @@ -458,10 +558,13 @@ int udl_modeset_init(struct drm_device *dev) udl_connector_init(dev, encoder); + udl_flip_workqueue_init(dev); + return 0; } void udl_modeset_cleanup(struct drm_device *dev) { + udl_flip_workqueue_cleanup(dev); drm_mode_config_cleanup(dev); } -- 2.2.0.rc0.207.ga3a616c _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/dri-devel