[PATCH v2 6/8] drm: Add vmalloc BO helper

Noralf Trønnes noralf at tronnes.org
Sun Oct 15 16:30:40 UTC 2017


Add vmalloc buffer object helper that can be useful for modesetting
drivers, particularly the framebuffer flushing kind.

Signed-off-by: Noralf Trønnes <noralf at tronnes.org>
---
 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



More information about the dri-devel mailing list