Den 17.10.2017 14.51, skrev Daniel Vetter:
On Sun, Oct 15, 2017 at 06:30:40PM +0200, Noralf Trønnes wrote:
Add vmalloc buffer object helper that can be useful for modesetting
drivers, particularly the framebuffer flushing kind.

Signed-off-by: Noralf Trønnes <nor...@tronnes.org>
Why can't we extend the shmem stuff to provide a simple vmalloc helper?
Doing a new flavour of gem for every special use-case seems like not a
good idea ...

This could then also be used by stuff like udl, with fewer changes.

This is the rationale (from the coverletter):

The reason I want to move away from the cma helper, is that it restricts
which drivers to PRIME import from, since cma requires the buffer to be
physically continuous. Initially I looked at udl and decided to use
shmem, but have later decided against it since shmem doesn't work with
fbdev deferred I/O, they both use page->lru and page->mapping. This
meant adding a shadow buffer for fbdev with increased complexity, so I
fell back to the KISS principle.

I'm trying to make tinydrm support drivers like simpledrm and udl, so if
using vmalloc blocks those use cases, please let me know.


Noralf.

-Daniel

---
  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 13dd237418cc..fd1ca10f6611 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 4d9f21831741..5d580440a259 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 a3fdc5a68dff..ed3eafa97a69 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 000000000000..4015b9d1d671
--- /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 000000000000..0df3d15e2e4a
--- /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.14.2


_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

Reply via email to