Add vmalloc buffer object helper that can be useful for modesetting drivers, particularly the framebuffer flushing kind. Signed-off-by: Noralf Trønnes <noralf@xxxxxxxxxxx> --- Documentation/gpu/drm-kms-helpers.rst | 12 ++ drivers/gpu/drm/Kconfig | 7 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/drm_vmalloc_bo_helper.c | 305 ++++++++++++++++++++++++++++++++ include/drm/drm_vmalloc_bo_helper.h | 88 +++++++++ 5 files changed, 413 insertions(+) create mode 100644 drivers/gpu/drm/drm_vmalloc_bo_helper.c create mode 100644 include/drm/drm_vmalloc_bo_helper.h diff --git a/Documentation/gpu/drm-kms-helpers.rst b/Documentation/gpu/drm-kms-helpers.rst index 13dd237..fd1ca10 100644 --- a/Documentation/gpu/drm-kms-helpers.rst +++ b/Documentation/gpu/drm-kms-helpers.rst @@ -305,3 +305,15 @@ Framebuffer GEM Helper Reference .. kernel-doc:: drivers/gpu/drm/drm_gem_framebuffer_helper.c :export: + +vmalloc buffer object helper +============================ + +.. kernel-doc:: drivers/gpu/drm/drm_vmalloc_bo_helper.c + :doc: overview + +.. kernel-doc:: include/drm/drm_vmalloc_bo_helper.h + :internal: + +.. kernel-doc:: drivers/gpu/drm/drm_vmalloc_bo_helper.c + :export: diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index c9f09fc..09ccaca 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -145,6 +145,13 @@ config DRM_KMS_CMA_HELPER help Choose this if you need the KMS CMA helper functions +config DRM_VMALLOC_BO_HELPER + bool + depends on DRM + select DRM_KMS_HELPER + help + Choose this if you need the vmalloc buffer object helper functions + config DRM_VM bool depends on DRM && MMU diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 0ee184f..c98bf54 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -39,6 +39,7 @@ drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_probe_helper.o \ drm_kms_helper-$(CONFIG_DRM_PANEL_BRIDGE) += bridge/panel.o drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o drm_kms_helper-$(CONFIG_DRM_KMS_CMA_HELPER) += drm_fb_cma_helper.o +drm_kms_helper-$(CONFIG_DRM_VMALLOC_BO_HELPER) += drm_vmalloc_bo_helper.o drm_kms_helper-$(CONFIG_DRM_DP_AUX_CHARDEV) += drm_dp_aux_dev.o obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o diff --git a/drivers/gpu/drm/drm_vmalloc_bo_helper.c b/drivers/gpu/drm/drm_vmalloc_bo_helper.c new file mode 100644 index 0000000..4015b9d --- /dev/null +++ b/drivers/gpu/drm/drm_vmalloc_bo_helper.c @@ -0,0 +1,305 @@ +/* + * DRM vmalloc buffer object helper functions + * + * Copyright (C) 2017 Noralf Trønnes + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/dma-buf.h> +#include <linux/export.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> + +#include <drm/drmP.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_vmalloc_bo_helper.h> + +/** + * DOC: overview + * + * This helper provides a simple GEM based buffer object with buffers allocated + * using vmalloc(). This is useful for modesetting drivers that do framebuffer + * flushing. It supports dumb buffers and PRIME import which can be setup using + * the DEFINE_DRM_VMALLOC_BO_FOPS() and DRM_VMALLOC_BO_DRIVER_OPS() macros. + * + * fbdev emulation can be setup using the drm_vmalloc_bo_fbdev_probe() function. + */ + +static struct drm_vmalloc_bo * +drm_vmalloc_bo_create(struct drm_device *dev, size_t size, bool backing) +{ + struct drm_vmalloc_bo *bo; + int ret; + + size = PAGE_ALIGN(size); + + bo = kzalloc(sizeof(*bo), GFP_KERNEL); + if (!bo) + return ERR_PTR(-ENOMEM); + + if (backing) { + bo->vaddr = vmalloc_user(size); + if (!bo->vaddr) { + ret = -ENOMEM; + goto error_free; + } + } + + drm_gem_private_object_init(dev, &bo->base, size); + + return bo; + +error_free: + kfree(bo); + + return ERR_PTR(ret); +} + +/** + * drm_vmalloc_bo_free_object() - Free resources associated with a vmalloc BO + * object + * @obj: GEM object to free + * + * This function frees the backing memory of the vmalloc BO object, cleans up + * the GEM object state and frees the memory used to store the object itself. + * Drivers using the vmalloc BO helpers should set this as their + * &drm_driver.gem_free_object callback. + */ +void drm_vmalloc_bo_free_object(struct drm_gem_object *obj) +{ + struct drm_vmalloc_bo *bo = to_drm_vmalloc_bo(obj); + + if (obj->import_attach) + dma_buf_vunmap(obj->import_attach->dmabuf, bo->vaddr); + else + vfree(bo->vaddr); + + drm_gem_object_release(obj); + kfree(bo); +} +EXPORT_SYMBOL(drm_vmalloc_bo_free_object); + +/** + * drm_vmalloc_bo_dumb_create() - Create a dumb vmalloc BO + * @file: DRM file structure to create the dumb buffer for + * @dev: DRM device + * @args: IOCTL data + * + * This function computes the pitch of the dumb buffer and rounds it up to an + * integer number of bytes per pixel. Drivers for hardware that doesn't have + * any additional restrictions on the pitch can directly use this function as + * their &drm_driver.dumb_create callback. + * + * For hardware with additional restrictions, drivers can adjust the fields + * set up by userspace before calling into this function. Also + * &drm_mode_create_dumb.pitch and &drm_mode_create_dumb.size can be set. + * + * Returns: + * Zero on success or a negative error code on failure. + */ +int drm_vmalloc_bo_dumb_create(struct drm_file *file, struct drm_device *dev, + struct drm_mode_create_dumb *args) +{ + struct drm_vmalloc_bo *bo; + int ret; + + if (!args->pitch) + args->pitch = DIV_ROUND_UP(args->width * args->bpp, 8); + else + args->pitch = max(args->pitch, + DIV_ROUND_UP(args->width * args->bpp, 8)); + if (!args->size) + args->size = args->pitch * args->height; + else + args->size = max_t(typeof(args->size), args->size, + args->pitch * args->height); + + bo = drm_vmalloc_bo_create(dev, args->size, true); + if (IS_ERR(bo)) + return PTR_ERR(bo); + + ret = drm_gem_handle_create(file, &bo->base, &args->handle); + /* drop reference from allocate - handle holds it now. */ + drm_gem_object_put_unlocked(&bo->base); + + return ret; +} +EXPORT_SYMBOL(drm_vmalloc_bo_dumb_create); + +const struct vm_operations_struct drm_vmalloc_bo_vm_ops = { + .open = drm_gem_vm_open, + .close = drm_gem_vm_close, +}; +EXPORT_SYMBOL_GPL(drm_vmalloc_bo_vm_ops); + +/** + * drm_vmalloc_bo_mmap() - Memory-map a vmalloc BO + * @filp: File object + * @vma: VMA for the area to be mapped + * + * This function implements an augmented version of the GEM DRM file mmap + * operation for vmalloc buffer objects. Drivers should use this function as + * their ->mmap handler in the DRM device file's file_operations structure. + * + * Instead of directly referencing this function, drivers should use the + * DEFINE_DRM_VMALLOC_BO_FOPS() macro. + * + * Returns: + * Zero on success or a negative error code on failure. + */ +int drm_vmalloc_bo_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct drm_vmalloc_bo *bo; + int ret; + + ret = drm_gem_mmap(filp, vma); + if (ret) + return ret; + + /* Set by drm_gem_mmap() */ + vma->vm_flags &= ~VM_IO; + vma->vm_flags &= ~VM_PFNMAP; + + bo = to_drm_vmalloc_bo(vma->vm_private_data); + + return remap_vmalloc_range(vma, bo->vaddr, 0); +} +EXPORT_SYMBOL(drm_vmalloc_bo_mmap); + +/** + * drm_vmalloc_bo_prime_import_sg_table() - Produce a vmalloc BO from a dma-buf + * @dev: DRM device to import into + * @attach: dmabuf attachment + * @sgt: Scatter/gather table of pinned pages + * + * This function creates a vmalloc buffer object using the virtual address on + * the dma-buf exported by another driver. Drivers using the vmalloc BO helpers + * should set this as their &drm_driver.gem_prime_import_sg_table callback. + * + * Returns: + * A pointer to a newly created GEM object or an ERR_PTR-encoded negative + * error code on failure. + */ +struct drm_gem_object * +drm_vmalloc_bo_prime_import_sg_table(struct drm_device *dev, + struct dma_buf_attachment *attach, + struct sg_table *sgt) +{ + struct drm_vmalloc_bo *bo; + void *vaddr; + + vaddr = dma_buf_vmap(attach->dmabuf); + if (!vaddr) { + DRM_ERROR("Failed to vmap PRIME buffer\n"); + return ERR_PTR(-ENOMEM); + } + + bo = drm_vmalloc_bo_create(dev, attach->dmabuf->size, false); + if (IS_ERR(bo)) { + dma_buf_vunmap(attach->dmabuf, vaddr); + return ERR_CAST(bo); + } + + bo->vaddr = vaddr; + + DRM_DEBUG_PRIME("size = %zu\n", attach->dmabuf->size); + + return &bo->base; +} +EXPORT_SYMBOL(drm_vmalloc_bo_prime_import_sg_table); + +static struct fb_ops drm_vmalloc_bo_fbdev_ops = { + .owner = THIS_MODULE, + DRM_FB_HELPER_DEFAULT_OPS, + .fb_read = drm_fb_helper_sys_read, + .fb_write = drm_fb_helper_sys_write, + .fb_fillrect = drm_fb_helper_sys_fillrect, + .fb_copyarea = drm_fb_helper_sys_copyarea, + .fb_imageblit = drm_fb_helper_sys_imageblit, +}; + +/** + * drm_vmalloc_bo_fbdev_probe - fbdev emulation \.fb_probe helper + * @fb_helper: fbdev emulation helper structure + * @sizes: Describes fbdev size and scanout surface size + * @fb_funcs: DRM framebuffer functions + * + * This function can be used in the &drm_fb_helper_funcs.fb_probe callback to + * setup fbdev emulation. If @fb_funcs->dirty is set, fbdev deferred I/O is + * initialized. drm_fb_helper_simple_init() can be used to initialize the fbdev + * emulation. + * + * Returns: + * Zero on success, negative error code on failure. + */ +int drm_vmalloc_bo_fbdev_probe(struct drm_fb_helper *fb_helper, + struct drm_fb_helper_surface_size *sizes, + const struct drm_framebuffer_funcs *fb_funcs) +{ + struct drm_device *dev = fb_helper->dev; + struct drm_framebuffer *fb; + struct drm_vmalloc_bo *bo; + struct fb_info *fbi; + size_t size; + int ret; + + DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d)\n", + sizes->surface_width, sizes->surface_height, + sizes->surface_bpp); + + size = sizes->surface_width * sizes->surface_height * + DIV_ROUND_UP(sizes->surface_bpp, 8); + + bo = drm_vmalloc_bo_create(dev, size, true); + if (IS_ERR(bo)) + return -ENOMEM; + + fb = drm_gem_fbdev_fb_create(dev, sizes, 0, &bo->base, fb_funcs); + if (IS_ERR(fb)) { + dev_err(dev->dev, "Failed to allocate DRM framebuffer.\n"); + ret = PTR_ERR(fb); + goto err_bo_free; + } + + fb_helper->fb = fb; + + fbi = drm_fb_helper_alloc_fbi(fb_helper); + if (IS_ERR(fbi)) { + ret = PTR_ERR(fbi); + goto err_fb_free; + } + + fbi->par = fb_helper; + fbi->flags = FBINFO_VIRTFB; + fbi->fbops = &drm_vmalloc_bo_fbdev_ops; + + drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->format->depth); + drm_fb_helper_fill_var(fbi, fb_helper, sizes->fb_width, sizes->fb_height); + + fbi->screen_base = bo->vaddr; + fbi->screen_size = size; + fbi->fix.smem_len = size; + + if (fb_funcs->dirty) { + ret = drm_fb_helper_defio_init(fb_helper); + if (ret) + goto err_fb_info_destroy; + } + + return 0; + +err_fb_info_destroy: + drm_fb_helper_fini(fb_helper); +err_fb_free: + drm_framebuffer_remove(fb); +err_bo_free: + drm_gem_object_put_unlocked(&bo->base); + + return ret; +} +EXPORT_SYMBOL(drm_vmalloc_bo_fbdev_probe); diff --git a/include/drm/drm_vmalloc_bo_helper.h b/include/drm/drm_vmalloc_bo_helper.h new file mode 100644 index 0000000..0df3d15 --- /dev/null +++ b/include/drm/drm_vmalloc_bo_helper.h @@ -0,0 +1,88 @@ +#ifndef __DRM_VMALLOC_BO_HELPER_H__ +#define __DRM_VMALLOC_BO_HELPER_H__ + +#include <drm/drmP.h> +#include <drm/drm_gem.h> + +struct drm_fb_helper_surface_size; + +/** + * struct drm_vmalloc_bo - vmalloc buffer object + */ +struct drm_vmalloc_bo { + /** + * @base: + * + * Base GEM object. + */ + struct drm_gem_object base; + + /** + * @vaddr: + * + * Kernel virtual address of the buffer. + */ + void *vaddr; +}; + +static inline struct drm_vmalloc_bo * +to_drm_vmalloc_bo(struct drm_gem_object *obj) +{ + return container_of(obj, struct drm_vmalloc_bo, base); +} + +static inline void *drm_vmalloc_bo_fb_vaddr(struct drm_framebuffer *fb) +{ + return to_drm_vmalloc_bo(fb->obj[0])->vaddr; +} + +void drm_vmalloc_bo_free_object(struct drm_gem_object *obj); +int drm_vmalloc_bo_dumb_create(struct drm_file *file, struct drm_device *dev, + struct drm_mode_create_dumb *args); +int drm_vmalloc_bo_mmap(struct file *filp, struct vm_area_struct *vma); +struct drm_gem_object * +drm_vmalloc_bo_prime_import_sg_table(struct drm_device *dev, + struct dma_buf_attachment *attach, + struct sg_table *sgt); + +int drm_vmalloc_bo_fbdev_probe(struct drm_fb_helper *fb_helper, + struct drm_fb_helper_surface_size *sizes, + const struct drm_framebuffer_funcs *fb_funcs); + +/** + * DEFINE_DRM_VMALLOC_BO_FOPS() - Macro to generate file operations for drivers + * @name: Name for the generated structure + * + * This macro autogenerates a suitable &struct file_operations which can be + * assigned to &drm_driver.fops for vmalloc BO drivers. + */ +#define DEFINE_DRM_VMALLOC_BO_FOPS(name) \ + static const struct file_operations name = {\ + .owner = THIS_MODULE,\ + .open = drm_open,\ + .release = drm_release,\ + .unlocked_ioctl = drm_ioctl,\ + .compat_ioctl = drm_compat_ioctl,\ + .poll = drm_poll,\ + .read = drm_read,\ + .llseek = noop_llseek,\ + .mmap = drm_vmalloc_bo_mmap,\ + } + +extern const struct vm_operations_struct drm_vmalloc_bo_vm_ops; + +/** + * DRM_VMALLOC_BO_DRIVER_OPS - Default GEM and PRIME operations + * + * This macro provides a shortcut for setting the GEM and PRIME operations in + * the &drm_driver structure for vmalloc BO drivers. + */ +#define DRM_VMALLOC_BO_DRIVER_OPS \ + .gem_free_object = drm_vmalloc_bo_free_object, \ + .gem_vm_ops = &drm_vmalloc_bo_vm_ops, \ + .prime_fd_to_handle = drm_gem_prime_fd_to_handle, \ + .gem_prime_import = drm_gem_prime_import, \ + .gem_prime_import_sg_table = drm_vmalloc_bo_prime_import_sg_table, \ + .dumb_create = drm_vmalloc_bo_dumb_create + +#endif /* __DRM_VMALLOC_BO_HELPER_H__ */ -- 2.7.4 _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/dri-devel