From mboxrd@z Thu Jan 1 00:00:00 1970 From: Dawid Kurek Subject: [PATCH] drm/udl: Make page_flip asynchronous Date: Fri, 7 Jul 2017 07:48:49 +0200 Message-ID: <20170707054849.GA16959@displaylink.com> Mime-Version: 1.0 Content-Type: text/plain; charset=WINDOWS-1252 Content-Transfer-Encoding: quoted-printable Return-path: Content-Disposition: inline Sender: linux-kernel-owner@vger.kernel.org To: Dave Airlie , David Airlie , dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org List-Id: dri-devel@lists.freedesktop.org In page_flip vblank is sent with no delay. Driver does not know when the actual update is present on the display and has no means for getting this information from a device. It is practically impossible to say exactly *when* as there is also i.e. a usb delay. When we are unable to determine when the vblank actually happens we may assume it will behave accordingly, i.e. it will present frames with proper timing. In the worst case scenario it should take up to duration of one frame (we may get new frame in the device just after presenting current one so we would need to wait for the whole frame). Because of the asynchronous nature of the delay we need to synchronize: * read/write vrefresh/page_flip data when changing mode and preparing/executing vblank * USB requests to prevent interleaved access to URBs for two different frame buffers All those changes are backports from ChromeOS: 1. https://chromium-review.googlesource.com/250622 2. https://chromium-review.googlesource.com/249450 partially, only change in udl_modeset.c for 'udl_flip_queue' 3. https://chromium-review.googlesource.com/321378 4. https://chromium-review.googlesource.com/324119 + fixes for checkpatch and latest drm changes Cc: hshi@chromium.org Cc: marcheu@chromium.org Cc: zachr@chromium.org Cc: dbehr@google.com Signed-off-by: Dawid Kurek --- drivers/gpu/drm/udl/udl_drv.h | 4 + drivers/gpu/drm/udl/udl_fb.c | 28 ++++--- drivers/gpu/drm/udl/udl_main.c | 3 + drivers/gpu/drm/udl/udl_modeset.c | 160 ++++++++++++++++++++++++++++++++++= ---- 4 files changed, 171 insertions(+), 24 deletions(-) diff --git a/drivers/gpu/drm/udl/udl_drv.h b/drivers/gpu/drm/udl/udl_drv.h index 2a75ab8..b93fb8d 100644 --- a/drivers/gpu/drm/udl/udl_drv.h +++ b/drivers/gpu/drm/udl/udl_drv.h @@ -47,6 +47,7 @@ struct urb_list { }; =20 struct udl_fbdev; +struct udl_flip_queue; =20 struct udl_device { =09struct device *dev; @@ -66,6 +67,9 @@ struct udl_device { =09atomic_t bytes_identical; /* saved effort with backbuffer comparison */ =09atomic_t bytes_sent; /* to usb, after compression including overhead */ =09atomic_t cpu_kcycles_used; /* transpired during pixel processing */ + +=09struct udl_flip_queue *flip_queue; +=09struct mutex transfer_lock; /* to serialize transfers */ }; =20 struct udl_gem_object { diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c index 4a65003..6dd9a49 100644 --- a/drivers/gpu/drm/udl/udl_fb.c +++ b/drivers/gpu/drm/udl/udl_fb.c @@ -82,7 +82,7 @@ int udl_handle_damage(struct udl_framebuffer *fb, int x, = int y, { =09struct drm_device *dev =3D fb->base.dev; =09struct udl_device *udl =3D dev->dev_private; -=09int i, ret; +=09int i, ret =3D 0; =09char *cmd; =09cycles_t start_cycles, end_cycles; =09int bytes_sent =3D 0; @@ -91,18 +91,22 @@ int udl_handle_damage(struct udl_framebuffer *fb, int x= , int y, =09int aligned_x; =09int bpp =3D fb->base.format->cpp[0]; =20 +=09mutex_lock(&udl->transfer_lock); + =09if (!fb->active_16) -=09=09return 0; +=09=09goto out; =20 =09if (!fb->obj->vmapping) { =09=09ret =3D udl_gem_vmap(fb->obj); =09=09if (ret =3D=3D -ENOMEM) { =09=09=09DRM_ERROR("failed to vmap fb\n"); -=09=09=09return 0; +=09=09=09ret =3D 0; +=09=09=09goto out; =09=09} =09=09if (!fb->obj->vmapping) { =09=09=09DRM_ERROR("failed to vmapping\n"); -=09=09=09return 0; +=09=09=09ret =3D 0; +=09=09=09goto out; =09=09} =09} =20 @@ -112,14 +116,18 @@ int udl_handle_damage(struct udl_framebuffer *fb, int= x, int y, =20 =09if ((width <=3D 0) || =09 (x + width > fb->base.width) || -=09 (y + height > fb->base.height)) -=09=09return -EINVAL; +=09 (y + height > fb->base.height)) { +=09=09ret =3D -EINVAL; +=09=09goto out; +=09} =20 =09start_cycles =3D get_cycles(); =20 =09urb =3D udl_get_urb(dev); -=09if (!urb) -=09=09return 0; +=09if (!urb) { +=09=09ret =3D 0; +=09=09goto out; +=09} =09cmd =3D urb->transfer_buffer; =20 =09for (i =3D y; i < y + height ; i++) { @@ -151,7 +159,9 @@ int udl_handle_damage(struct udl_framebuffer *fb, int x= , int y, =09=09 >> 10)), /* Kcycles */ =09=09 &udl->cpu_kcycles_used); =20 -=09return 0; +out: +=09mutex_unlock(&udl->transfer_lock); +=09return ret; } =20 static int udl_fb_mmap(struct fb_info *info, struct vm_area_struct *vma) diff --git a/drivers/gpu/drm/udl/udl_main.c b/drivers/gpu/drm/udl/udl_main.= c index a9d93b8..d376233 100644 --- a/drivers/gpu/drm/udl/udl_main.c +++ b/drivers/gpu/drm/udl/udl_main.c @@ -319,6 +319,8 @@ int udl_driver_load(struct drm_device *dev, unsigned lo= ng flags) =09if (!udl) =09=09return -ENOMEM; =20 +=09mutex_init(&udl->transfer_lock); + =09udl->udev =3D udev; =09udl->ddev =3D dev; =09dev->dev_private =3D udl; @@ -378,5 +380,6 @@ void udl_driver_unload(struct drm_device *dev) =20 =09udl_fbdev_cleanup(dev); =09udl_modeset_cleanup(dev); +=09mutex_destroy(&udl->transfer_lock); =09kfree(udl); } diff --git a/drivers/gpu/drm/udl/udl_modeset.c b/drivers/gpu/drm/udl/udl_mo= deset.c index 5bcae76..9a0c84d 100644 --- a/drivers/gpu/drm/udl/udl_modeset.c +++ b/drivers/gpu/drm/udl/udl_modeset.c @@ -235,15 +235,21 @@ static int udl_crtc_write_mode_to_hw(struct drm_crtc = *crtc) =09char *buf; =09int retval; =20 +=09mutex_lock(&udl->transfer_lock); + =09urb =3D udl_get_urb(dev); -=09if (!urb) +=09if (!urb) { +=09=09mutex_unlock(&udl->transfer_lock); =09=09return -ENOMEM; +=09} =20 =09buf =3D (char *)urb->transfer_buffer; =20 =09memcpy(buf, udl->mode_buf, udl->mode_buf_len); =09retval =3D udl_submit_urb(dev, urb, udl->mode_buf_len); =09DRM_INFO("write mode info %d\n", udl->mode_buf_len); +=09mutex_unlock(&udl->transfer_lock); + =09return retval; } =20 @@ -257,9 +263,14 @@ static void udl_crtc_dpms(struct drm_crtc *crtc, int m= ode) =09if (mode =3D=3D DRM_MODE_DPMS_OFF) { =09=09char *buf; =09=09struct urb *urb; + +=09=09mutex_lock(&udl->transfer_lock); + =09=09urb =3D udl_get_urb(dev); -=09=09if (!urb) +=09=09if (!urb) { +=09=09=09mutex_unlock(&udl->transfer_lock); =09=09=09return; +=09=09} =20 =09=09buf =3D (char *)urb->transfer_buffer; =09=09buf =3D udl_vidreg_lock(buf); @@ -269,6 +280,8 @@ static void udl_crtc_dpms(struct drm_crtc *crtc, int mo= de) =09=09buf =3D udl_dummy_render(buf); =09=09retval =3D udl_submit_urb(dev, urb, buf - (char *) =09=09=09=09=09urb->transfer_buffer); + +=09=09mutex_unlock(&udl->transfer_lock); =09} else { =09=09if (udl->mode_buf_len =3D=3D 0) { =09=09=09DRM_ERROR("Trying to enable DPMS with no mode\n"); @@ -295,6 +308,16 @@ udl_pipe_set_base(struct drm_crtc *crtc, int x, int y, } #endif =20 +struct udl_flip_queue { +=09struct mutex lock; +=09struct workqueue_struct *wq; +=09struct delayed_work work; +=09struct drm_crtc *crtc; +=09struct drm_pending_vblank_event *event; +=09u64 flip_time; /*in jiffies */ +=09u64 vblank_interval; /*in jiffies */ +}; + static int udl_crtc_mode_set(struct drm_crtc *crtc, =09=09=09 struct drm_display_mode *mode, =09=09=09 struct drm_display_mode *adjusted_mode, @@ -305,6 +328,7 @@ static int udl_crtc_mode_set(struct drm_crtc *crtc, =09struct drm_device *dev =3D crtc->dev; =09struct udl_framebuffer *ufb =3D to_udl_fb(crtc->primary->fb); =09struct udl_device *udl =3D dev->dev_private; +=09struct udl_flip_queue *flip_queue =3D udl->flip_queue; =09char *buf; =09char *wrptr; =09int color_depth =3D 0; @@ -336,11 +360,20 @@ static int udl_crtc_mode_set(struct drm_crtc *crtc, =20 =09if (old_fb) { =09=09struct udl_framebuffer *uold_fb =3D to_udl_fb(old_fb); + =09=09uold_fb->active_16 =3D false; =09} =09ufb->active_16 =3D true; =09udl->mode_buf_len =3D wrptr - buf; =20 +=09if (flip_queue) { +=09=09int vrefresh =3D drm_mode_vrefresh(mode); + +=09=09mutex_lock(&flip_queue->lock); +=09=09flip_queue->vblank_interval =3D HZ / vrefresh; +=09=09mutex_unlock(&flip_queue->lock); +=09} + =09/* damage all of it */ =09udl_handle_damage(ufb, 0, 0, ufb->base.width, ufb->base.height); =09return 0; @@ -358,30 +391,91 @@ static void udl_crtc_destroy(struct drm_crtc *crtc) =09kfree(crtc); } =20 +static void udl_sched_page_flip(struct work_struct *work) +{ +=09struct delayed_work *delayed_work +=09=09=3D container_of(work, struct delayed_work, work); +=09struct udl_flip_queue *flip_queue =3D +=09=09container_of(delayed_work, struct udl_flip_queue, work); +=09struct drm_crtc *crtc; +=09struct drm_device *dev; +=09struct drm_pending_vblank_event *event; +=09struct drm_framebuffer *fb; + +=09if (!flip_queue) +=09=09return; + +=09mutex_lock(&flip_queue->lock); +=09crtc =3D flip_queue->crtc; +=09dev =3D crtc->dev; +=09fb =3D crtc->primary->fb; +=09event =3D flip_queue->event; +=09flip_queue->event =3D NULL; +=09mutex_unlock(&flip_queue->lock); + +=09if (fb) +=09=09udl_handle_damage(to_udl_fb(fb), 0, 0, fb->width, fb->height); +=09if (event) { +=09=09unsigned long flags; + +=09=09spin_lock_irqsave(&dev->event_lock, flags); +=09=09drm_crtc_send_vblank_event(crtc, event); +=09=09spin_unlock_irqrestore(&dev->event_lock, flags); +=09} +} + static int udl_crtc_page_flip(struct drm_crtc *crtc, =09=09=09 struct drm_framebuffer *fb, =09=09=09 struct drm_pending_vblank_event *event, =09=09=09 uint32_t page_flip_flags, =09=09=09 struct drm_modeset_acquire_ctx *ctx) { -=09struct udl_framebuffer *ufb =3D to_udl_fb(fb); =09struct drm_device *dev =3D crtc->dev; -=09unsigned long flags; +=09struct udl_device *udl =3D dev->dev_private; +=09struct udl_flip_queue *flip_queue =3D udl->flip_queue; =20 -=09struct drm_framebuffer *old_fb =3D crtc->primary->fb; -=09if (old_fb) { -=09=09struct udl_framebuffer *uold_fb =3D to_udl_fb(old_fb); -=09=09uold_fb->active_16 =3D false; +=09if (!flip_queue || !flip_queue->wq) { +=09=09DRM_ERROR("Uninitialized page flip queue\n"); +=09=09return -ENOMEM; =09} -=09ufb->active_16 =3D true; =20 -=09udl_handle_damage(ufb, 0, 0, fb->width, fb->height); +=09mutex_lock(&flip_queue->lock); =20 -=09spin_lock_irqsave(&dev->event_lock, flags); -=09if (event) -=09=09drm_crtc_send_vblank_event(crtc, event); -=09spin_unlock_irqrestore(&dev->event_lock, flags); -=09crtc->primary->fb =3D fb; +=09flip_queue->crtc =3D crtc; +=09if (fb) { +=09=09struct udl_framebuffer *ufb =3D to_udl_fb(fb); +=09=09struct drm_framebuffer *old_fb; + +=09=09old_fb =3D crtc->primary->fb; +=09=09if (old_fb) { +=09=09=09struct udl_framebuffer *uold_fb =3D to_udl_fb(old_fb); + +=09=09=09uold_fb->active_16 =3D false; +=09=09} +=09=09ufb->active_16 =3D true; +=09=09crtc->primary->fb =3D fb; +=09} +=09if (event) { +=09=09if (flip_queue->event) { +=09=09=09unsigned long flags; + +=09=09=09spin_lock_irqsave(&dev->event_lock, flags); +=09=09=09drm_crtc_send_vblank_event(crtc, flip_queue->event); +=09=09=09spin_unlock_irqrestore(&dev->event_lock, flags); +=09=09} +=09=09flip_queue->event =3D event; +=09} +=09if (!delayed_work_pending(&flip_queue->work)) { +=09=09u64 now =3D jiffies; +=09=09u64 next_flip =3D +=09=09=09flip_queue->flip_time + flip_queue->vblank_interval; + +=09=09flip_queue->flip_time =3D (next_flip < now) ? now : next_flip; +=09=09queue_delayed_work(flip_queue->wq, &flip_queue->work, +=09=09=09flip_queue->flip_time - now); +=09} + +=09mutex_unlock(&flip_queue->lock); =20 =09return 0; } @@ -423,6 +517,39 @@ static int udl_crtc_init(struct drm_device *dev) =09return 0; } =20 +static void udl_flip_workqueue_init(struct drm_device *dev) +{ +=09struct udl_device *udl =3D dev->dev_private; +=09struct udl_flip_queue *flip_queue; + +=09flip_queue =3D kzalloc(sizeof(*flip_queue), GFP_KERNEL); +=09if (WARN_ON(!flip_queue)) +=09=09return; + +=09mutex_init(&flip_queue->lock); +=09flip_queue->wq =3D create_singlethread_workqueue("flip"); +=09WARN_ON(!flip_queue->wq); +=09INIT_DELAYED_WORK(&flip_queue->work, udl_sched_page_flip); +=09flip_queue->flip_time =3D jiffies; +=09flip_queue->vblank_interval =3D HZ / 60; +=09udl->flip_queue =3D flip_queue; +} + +static void udl_flip_workqueue_cleanup(struct drm_device *dev) +{ +=09struct udl_device *udl =3D dev->dev_private; +=09struct udl_flip_queue *flip_queue =3D udl->flip_queue; + +=09if (!flip_queue) +=09=09return; +=09if (flip_queue->wq) { +=09=09flush_workqueue(flip_queue->wq); +=09=09destroy_workqueue(flip_queue->wq); +=09} +=09mutex_destroy(&flip_queue->lock); +=09kfree(flip_queue); +} + static const struct drm_mode_config_funcs udl_mode_funcs =3D { =09.fb_create =3D udl_fb_user_fb_create, =09.output_poll_changed =3D NULL, @@ -450,6 +577,8 @@ int udl_modeset_init(struct drm_device *dev) =20 =09udl_connector_init(dev, encoder); =20 +=09udl_flip_workqueue_init(dev); + =09return 0; } =20 @@ -467,5 +596,6 @@ void udl_modeset_restore(struct drm_device *dev) =20 void udl_modeset_cleanup(struct drm_device *dev) { +=09udl_flip_workqueue_cleanup(dev); =09drm_mode_config_cleanup(dev); } --=20 2.7.4