On Fri, Apr 08, 2016 at 07:05:03PM +0200, Noralf Trønnes wrote: > This adds deferred io support if CONFIG_FB_DEFERRED_IO is enabled. > Accumulated fbdev framebuffer changes are signaled using the callback > (struct drm_framebuffer_funcs *)->dirty() > > The drm_fb_helper_sys_*() functions will accumulate changes and > schedule fb_info.deferred_work _if_ fb_info.fbdefio is set. > This worker is used by the deferred io mmap code to signal that it > has been collecting page faults. The page faults and/or other changes > are then merged into a drm_clip_rect and passed to the framebuffer > dirty() function. > > The driver is responsible for setting up the fb_info.fbdefio structure > and calling fb_deferred_io_init() using the provided callback: > (struct fb_info *)->fbdefio->deferred_io = drm_fb_helper_deferred_io; > > Signed-off-by: Noralf Trønnes <noralf@xxxxxxxxxxx> For this one it'd be awesome to throw patches for qxl/udl on top to remove their own hand-rolled implementations. Just to maximize the testing coverage of this new code. Should be doable to set up a qxl virtual machine quickly, but even without that we should be able to pull it in (since it's mostly just about removing code from these two drivers). -Daniel > --- > drivers/gpu/drm/drm_fb_helper.c | 189 +++++++++++++++++++++++++++++++++++++++- > include/drm/drm_fb_helper.h | 15 ++++ > 2 files changed, 203 insertions(+), 1 deletion(-) > > diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c > index 1e103c4..30f3dfd 100644 > --- a/drivers/gpu/drm/drm_fb_helper.c > +++ b/drivers/gpu/drm/drm_fb_helper.c > @@ -48,6 +48,100 @@ MODULE_PARM_DESC(fbdev_emulation, > > static LIST_HEAD(kernel_fb_helper_list); > > +/* > + * Where should I put these drm_clip_rect functions? > + */ > + > +/** > + * drm_clip_rect_reset - Reset clip > + * @clip: clip rectangle > + * > + * Sets clip to {0,0,0,0}. > + */ > +static inline void drm_clip_rect_reset(struct drm_clip_rect *clip) > +{ > + clip->x1 = 0; > + clip->x2 = 0; > + clip->y1 = 0; > + clip->y2 = 0; > +} > + > +/** > + * drm_clip_rect_is_empty - Is clip empty? > + * @clip: clip rectangle > + * > + * Returns true if clip is {0,0,0,0}. > + */ > +static inline bool drm_clip_rect_is_empty(struct drm_clip_rect *clip) > +{ > + return (!clip->x1 && !clip->x2 && !clip->y1 && !clip->y2); > +} > + > +/** > + * drm_clip_rect_sanetize - Make sure clip rectangle has sane values > + * @clip: clip rectangle > + * @width: maximum width of rectangle > + * @height: maximum height of rectangle > + * > + * Makes sure the clip doesn't exceed the specified width and height and that > + * x1 <= x2 and y1 <= y2. > + */ > +void drm_clip_rect_sanetize(struct drm_clip_rect *clip, u32 width, u32 height) > +{ > + if (clip->x1 > clip->x2) > + swap(clip->x1, clip->x2); > + if (clip->y1 > clip->y2) > + swap(clip->y1, clip->y2); > + > + clip->x1 = min_t(u32, clip->x1, width - 1); > + clip->x2 = min_t(u32, clip->x2, width - 1); > + clip->y1 = min_t(u32, clip->y1, height - 1); > + clip->y2 = min_t(u32, clip->y2, height - 1); > +} > +EXPORT_SYMBOL(drm_clip_rect_sanetize); > + > +/** > + * drm_clip_rect_merge - Merge clip rectangles > + * @dst: destination clip rectangle > + * @src: source clip rectangle(s), can be NULL > + * @num_clips: number of source clip rectangles > + * @width: width of rectangle if @src is NULL > + * @height: height of rectangle if @src is NULL > + * > + * The dirtyfb ioctl allows for a NULL clip to be passed in, > + * so if @src is NULL, width and height is used to set a full clip. > + * @dst takes part in the merge unless it is empty {0,0,0,0}. > + */ > +void drm_clip_rect_merge(struct drm_clip_rect *dst, > + struct drm_clip_rect *src, unsigned num_clips, > + unsigned flags, u32 width, u32 height) > +{ > + int i; > + > + if (!src || !num_clips) { > + dst->x1 = 0; > + dst->x2 = width - 1; > + dst->y1 = 0; > + dst->y2 = height - 1; > + return; > + } > + > + if (drm_clip_rect_is_empty(dst)) { > + dst->x1 = ~0; > + dst->y1 = ~0; > + } > + > + for (i = 0; i < num_clips; i++) { > + if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY) > + i++; > + dst->x1 = min(dst->x1, src[i].x1); > + dst->x2 = max(dst->x2, src[i].x2); > + dst->y1 = min(dst->y1, src[i].y1); > + dst->y2 = max(dst->y2, src[i].y2); > + } > +} > +EXPORT_SYMBOL(drm_clip_rect_merge); > + > /** > * DOC: fbdev helpers > * > @@ -410,6 +504,13 @@ static int restore_fbdev_mode(struct drm_fb_helper *fb_helper) > > drm_warn_on_modeset_not_all_locked(dev); > > +#ifdef CONFIG_FB_DEFERRED_IO > + spin_lock(&fb_helper->dirty_lock); > + drm_clip_rect_merge(&fb_helper->dirty_clip, NULL, 0, 0, > + fb_helper->fbdev->var.xres, > + fb_helper->fbdev->var.yres); > + spin_unlock(&fb_helper->dirty_lock); > +#endif > if (fb_helper->atomic) > return restore_fbdev_mode_atomic(fb_helper); > > @@ -654,6 +755,9 @@ void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper, > const struct drm_fb_helper_funcs *funcs) > { > INIT_LIST_HEAD(&helper->kernel_fb_list); > +#ifdef CONFIG_FB_DEFERRED_IO > + spin_lock_init(&helper->dirty_lock); > +#endif > helper->funcs = funcs; > helper->dev = dev; > } > @@ -838,6 +942,76 @@ void drm_fb_helper_unlink_fbi(struct drm_fb_helper *fb_helper) > } > EXPORT_SYMBOL(drm_fb_helper_unlink_fbi); > > +#ifdef CONFIG_FB_DEFERRED_IO > +void drm_fb_helper_deferred_io(struct fb_info *info, > + struct list_head *pagelist) > +{ > + struct drm_fb_helper *helper = info->par; > + unsigned long start, end, min, max; > + struct drm_clip_rect clip; > + struct page *page; > + > + if (!helper->fb->funcs->dirty) > + return; > + > + spin_lock(&helper->dirty_lock); > + clip = helper->dirty_clip; > + drm_clip_rect_reset(&helper->dirty_clip); > + spin_unlock(&helper->dirty_lock); > + > + min = ULONG_MAX; > + max = 0; > + list_for_each_entry(page, pagelist, lru) { > + start = page->index << PAGE_SHIFT; > + end = start + PAGE_SIZE - 1; > + min = min(min, start); > + max = max(max, end); > + } > + > + if (min < max) { > + clip.x1 = 0; > + clip.x2 = info->var.xres - 1; > + clip.y1 = min / info->fix.line_length; > + clip.y2 = min_t(u32, max / info->fix.line_length, > + info->var.yres - 1); > + } else if (drm_clip_rect_is_empty(&clip)) { > + clip.x1 = 0; > + clip.x2 = info->var.xres - 1; > + clip.y1 = 0; > + clip.y2 = info->var.yres - 1; > + } > + > + helper->fb->funcs->dirty(helper->fb, NULL, 0, 0, &clip, 1); > +} > +EXPORT_SYMBOL(drm_fb_helper_deferred_io); > + > +static void drm_fb_helper_sys_deferred(struct fb_info *info, u32 x, u32 y, > + u32 width, u32 height) > +{ > + struct drm_fb_helper *helper = info->par; > + struct drm_clip_rect clip; > + > + if (!info->fbdefio) > + return; > + > + clip.x1 = x; > + clip.x2 = x + width - 1; > + clip.y1 = y; > + clip.y2 = y + height - 1; > + > + spin_lock(&helper->dirty_lock); > + drm_clip_rect_merge(&helper->dirty_clip, &clip, 1, 0, 0, 0); > + spin_unlock(&helper->dirty_lock); > + > + schedule_delayed_work(&info->deferred_work, info->fbdefio->delay); > +} > +#else > +static inline void drm_fb_helper_sys_deferred(struct fb_info *info, u32 x, u32 y, > + u32 width, u32 height) > +{ > +} > +#endif > + > /** > * drm_fb_helper_sys_read - wrapper around fb_sys_read > * @info: fb_info struct pointer > @@ -866,7 +1040,14 @@ EXPORT_SYMBOL(drm_fb_helper_sys_read); > ssize_t drm_fb_helper_sys_write(struct fb_info *info, const char __user *buf, > size_t count, loff_t *ppos) > { > - return fb_sys_write(info, buf, count, ppos); > + ssize_t ret; > + > + ret = fb_sys_write(info, buf, count, ppos); > + if (ret > 0) > + drm_fb_helper_sys_deferred(info, 0, 0, > + info->var.xres, info->var.yres); > + > + return ret; > } > EXPORT_SYMBOL(drm_fb_helper_sys_write); > > @@ -881,6 +1062,8 @@ void drm_fb_helper_sys_fillrect(struct fb_info *info, > const struct fb_fillrect *rect) > { > sys_fillrect(info, rect); > + drm_fb_helper_sys_deferred(info, rect->dx, rect->dy, > + rect->width, rect->height); > } > EXPORT_SYMBOL(drm_fb_helper_sys_fillrect); > > @@ -895,6 +1078,8 @@ void drm_fb_helper_sys_copyarea(struct fb_info *info, > const struct fb_copyarea *area) > { > sys_copyarea(info, area); > + drm_fb_helper_sys_deferred(info, area->dx, area->dy, > + area->width, area->height); > } > EXPORT_SYMBOL(drm_fb_helper_sys_copyarea); > > @@ -909,6 +1094,8 @@ void drm_fb_helper_sys_imageblit(struct fb_info *info, > const struct fb_image *image) > { > sys_imageblit(info, image); > + drm_fb_helper_sys_deferred(info, image->dx, image->dy, > + image->width, image->height); > } > EXPORT_SYMBOL(drm_fb_helper_sys_imageblit); > > diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h > index d8a40df..1daadc7 100644 > --- a/include/drm/drm_fb_helper.h > +++ b/include/drm/drm_fb_helper.h > @@ -172,6 +172,9 @@ struct drm_fb_helper_connector { > * @funcs: driver callbacks for fb helper > * @fbdev: emulated fbdev device info struct > * @pseudo_palette: fake palette of 16 colors > + * @dirty_clip: clip rectangle used with deferred_io to accumulate damage to > + * the screen buffer > + * @dirty_lock: spinlock protecting @dirty_clip > * > * This is the main structure used by the fbdev helpers. Drivers supporting > * fbdev emulation should embedded this into their overall driver structure. > @@ -189,6 +192,10 @@ struct drm_fb_helper { > const struct drm_fb_helper_funcs *funcs; > struct fb_info *fbdev; > u32 pseudo_palette[17]; > +#ifdef CONFIG_FB_DEFERRED_IO > + struct drm_clip_rect dirty_clip; > + spinlock_t dirty_lock; > +#endif > > /** > * @kernel_fb_list: > @@ -244,6 +251,9 @@ void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, > > void drm_fb_helper_unlink_fbi(struct drm_fb_helper *fb_helper); > > +void drm_fb_helper_deferred_io(struct fb_info *info, > + struct list_head *pagelist); > + > ssize_t drm_fb_helper_sys_read(struct fb_info *info, char __user *buf, > size_t count, loff_t *ppos); > ssize_t drm_fb_helper_sys_write(struct fb_info *info, const char __user *buf, > @@ -362,6 +372,11 @@ static inline void drm_fb_helper_unlink_fbi(struct drm_fb_helper *fb_helper) > { > } > > +static inline void drm_fb_helper_deferred_io(struct fb_info *info, > + struct list_head *pagelist) > +{ > +} > + > static inline ssize_t drm_fb_helper_sys_read(struct fb_info *info, > char __user *buf, size_t count, > loff_t *ppos) > -- > 2.2.2 > > _______________________________________________ > dri-devel mailing list > dri-devel@xxxxxxxxxxxxxxxxxxxxx > https://lists.freedesktop.org/mailman/listinfo/dri-devel -- Daniel Vetter Software Engineer, Intel Corporation http://blog.ffwll.ch _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/dri-devel