* [PATCH] DRM: add drm gem CMA helper
@ 2012-05-29 14:10 Sascha Hauer
2012-05-29 14:46 ` Lars-Peter Clausen
2012-05-30 15:40 ` Laurent Pinchart
0 siblings, 2 replies; 10+ messages in thread
From: Sascha Hauer @ 2012-05-29 14:10 UTC (permalink / raw)
To: dri-devel; +Cc: kernel, Rob Clark
Many embedded drm devices do not have a IOMMU and no dedicated
memory for graphics. These devices use CMA (Contiguous Memory
Allocator) backed graphics memory. This patch provides helper
functions to be able to share the code.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
Lars-Peter, please let me know if this fits your needs or if you are missing
something.
drivers/gpu/drm/Kconfig | 6 +
drivers/gpu/drm/Makefile | 1 +
drivers/gpu/drm/drm_gem_cma_helper.c | 321 ++++++++++++++++++++++++++++++++++
include/drm/drm_gem_cma_helper.h | 47 +++++
4 files changed, 375 insertions(+)
create mode 100644 drivers/gpu/drm/drm_gem_cma_helper.c
create mode 100644 include/drm/drm_gem_cma_helper.h
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index e354bc0..f62717e 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -53,6 +53,12 @@ config DRM_TTM
GPU memory types. Will be enabled automatically if a device driver
uses it.
+config DRM_GEM_CMA_HELPER
+ tristate
+ depends on DRM
+ help
+ Choose this if you need the GEM cma helper functions
+
config DRM_TDFX
tristate "3dfx Banshee/Voodoo3+"
depends on DRM && PCI
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index c20da5b..9a0d98a 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -15,6 +15,7 @@ drm-y := drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \
drm_trace_points.o drm_global.o drm_prime.o
drm-$(CONFIG_COMPAT) += drm_ioc32.o
+drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o
drm-usb-y := drm_usb.o
diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c b/drivers/gpu/drm/drm_gem_cma_helper.c
new file mode 100644
index 0000000..75534ff
--- /dev/null
+++ b/drivers/gpu/drm/drm_gem_cma_helper.c
@@ -0,0 +1,321 @@
+/*
+ * drm gem cma (contiguous memory allocator) helper functions
+ *
+ * Copyright (C) 2012 Sascha Hauer, Pengutronix
+ *
+ * Based on Samsung Exynos code
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <drm/drmP.h>
+#include <drm/drm.h>
+#include <drm/drm_gem_cma_helper.h>
+
+static unsigned int convert_to_vm_err_msg(int msg)
+{
+ unsigned int out_msg;
+
+ switch (msg) {
+ case 0:
+ case -ERESTARTSYS:
+ case -EINTR:
+ out_msg = VM_FAULT_NOPAGE;
+ break;
+
+ case -ENOMEM:
+ out_msg = VM_FAULT_OOM;
+ break;
+
+ default:
+ out_msg = VM_FAULT_SIGBUS;
+ break;
+ }
+
+ return out_msg;
+}
+
+static unsigned int get_gem_mmap_offset(struct drm_gem_object *obj)
+{
+ return (unsigned int)obj->map_list.hash.key << PAGE_SHIFT;
+}
+
+static void drm_gem_cma_buf_destroy(struct drm_device *drm,
+ struct drm_gem_cma_obj *cma_obj)
+{
+ dma_free_writecombine(drm->dev, cma_obj->base.size, cma_obj->vaddr,
+ cma_obj->paddr);
+}
+
+/*
+ * drm_gem_cma_create - allocate an object with the given size
+ *
+ * returns a struct drm_gem_cma_obj* on success or ERR_PTR values
+ * on failure.
+ */
+struct drm_gem_cma_obj *drm_gem_cma_create(struct drm_device *drm,
+ unsigned int size)
+{
+ struct drm_gem_cma_obj *cma_obj;
+ struct drm_gem_object *gem_obj;
+ int ret;
+
+ size = roundup(size, PAGE_SIZE);
+
+ cma_obj = kzalloc(sizeof(*cma_obj), GFP_KERNEL);
+ if (!cma_obj)
+ return ERR_PTR(-ENOMEM);
+
+ cma_obj->vaddr = dma_alloc_writecombine(drm->dev, size,
+ (dma_addr_t *)&cma_obj->paddr,
+ GFP_KERNEL | __GFP_NOWARN);
+ if (!cma_obj->vaddr) {
+ dev_err(drm->dev, "failed to allocate buffer with size %d\n", size);
+ ret = -ENOMEM;
+ goto err_dma_alloc;
+ }
+
+ gem_obj = &cma_obj->base;
+
+ ret = drm_gem_object_init(drm, gem_obj, size);
+ if (ret)
+ goto err_obj_init;
+
+ ret = drm_gem_create_mmap_offset(gem_obj);
+ if (ret)
+ goto err_create_mmap_offset;
+
+ return cma_obj;
+
+err_create_mmap_offset:
+ drm_gem_object_release(gem_obj);
+
+err_obj_init:
+ drm_gem_cma_buf_destroy(drm, cma_obj);
+
+err_dma_alloc:
+ kfree(cma_obj);
+
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(drm_gem_cma_create);
+
+/*
+ * drm_gem_cma_create_with_handle - allocate an object with the given
+ * size and create a gem handle on it
+ *
+ * returns a struct drm_gem_cma_obj* on success or ERR_PTR values
+ * on failure.
+ */
+struct drm_gem_cma_obj *drm_gem_cma_create_with_handle(
+ struct drm_file *file_priv,
+ struct drm_device *drm, unsigned int size,
+ unsigned int *handle)
+{
+ struct drm_gem_cma_obj *cma_obj;
+ struct drm_gem_object *gem_obj;
+ int ret;
+
+ cma_obj = drm_gem_cma_create(drm, size);
+ if (IS_ERR(cma_obj))
+ return cma_obj;
+
+ gem_obj = &cma_obj->base;
+
+ /*
+ * allocate a id of idr table where the obj is registered
+ * and handle has the id what user can see.
+ */
+ ret = drm_gem_handle_create(file_priv, gem_obj, handle);
+ if (ret)
+ goto err_handle_create;
+
+ /* drop reference from allocate - handle holds it now. */
+ drm_gem_object_unreference_unlocked(gem_obj);
+
+ return cma_obj;
+
+err_handle_create:
+ drm_gem_cma_free_object(gem_obj);
+
+ return ERR_PTR(ret);
+}
+
+static int drm_gem_cma_mmap_buffer(struct file *filp,
+ struct vm_area_struct *vma)
+{
+ struct drm_gem_object *gem_obj = filp->private_data;
+ struct drm_gem_cma_obj *cma_obj = to_dma_alloc_gem_obj(gem_obj);
+ unsigned long pfn, vm_size;
+
+ vma->vm_flags |= VM_IO | VM_RESERVED;
+
+ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+ vma->vm_file = filp;
+
+ vm_size = vma->vm_end - vma->vm_start;
+
+ /* check if user-requested size is valid. */
+ if (vm_size > gem_obj->size)
+ return -EINVAL;
+
+ /*
+ * get page frame number to physical memory to be mapped
+ * to user space.
+ */
+ pfn = cma_obj->paddr >> PAGE_SHIFT;
+
+ if (remap_pfn_range(vma, vma->vm_start, pfn, vm_size,
+ vma->vm_page_prot)) {
+ dev_err(gem_obj->dev->dev, "failed to remap pfn range.\n");
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+static const struct file_operations drm_gem_cma_fops = {
+ .mmap = drm_gem_cma_mmap_buffer,
+};
+
+/*
+ * drm_gem_cma_free_object - (struct drm_driver)->gem_free_object callback
+ * function
+ */
+void drm_gem_cma_free_object(struct drm_gem_object *gem_obj)
+{
+ struct drm_gem_cma_obj *cma_obj;
+
+ if (gem_obj->map_list.map)
+ drm_gem_free_mmap_offset(gem_obj);
+
+ drm_gem_object_release(gem_obj);
+
+ cma_obj = to_dma_alloc_gem_obj(gem_obj);
+
+ drm_gem_cma_buf_destroy(gem_obj->dev, cma_obj);
+
+ kfree(cma_obj);
+}
+EXPORT_SYMBOL_GPL(drm_gem_cma_free_object);
+
+/*
+ * drm_gem_cma_dumb_create - (struct drm_driver)->dumb_create callback
+ * function
+ *
+ * This aligns the pitch and size arguments to the minimum required. wrap
+ * this into your own function if you need bigger alignment.
+ */
+int drm_gem_cma_dumb_create(struct drm_file *file_priv,
+ struct drm_device *dev, struct drm_mode_create_dumb *args)
+{
+ struct drm_gem_cma_obj *cma_obj;
+
+ if (args->pitch < args->width * args->bpp >> 3)
+ args->pitch = args->width * args->bpp >> 3;
+
+ if (args->size < args->pitch * args->height)
+ args->size = args->pitch * args->height;
+
+ cma_obj = drm_gem_cma_create_with_handle(file_priv, dev,
+ args->size, &args->handle);
+ if (IS_ERR(cma_obj))
+ return PTR_ERR(cma_obj);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(drm_gem_cma_dumb_create);
+
+/*
+ * drm_gem_cma_dumb_map_offset - (struct drm_driver)->dumb_map_offset callback
+ * function
+ */
+int drm_gem_cma_dumb_map_offset(struct drm_file *file_priv,
+ struct drm_device *drm, uint32_t handle, uint64_t *offset)
+{
+ struct drm_gem_cma_obj *cma_obj;
+ struct drm_gem_object *gem_obj;
+
+ mutex_lock(&drm->struct_mutex);
+
+ gem_obj = drm_gem_object_lookup(drm, file_priv, handle);
+ if (!gem_obj) {
+ dev_err(drm->dev, "failed to lookup gem object\n");
+ mutex_unlock(&drm->struct_mutex);
+ return -EINVAL;
+ }
+
+ cma_obj = to_dma_alloc_gem_obj(gem_obj);
+
+ *offset = get_gem_mmap_offset(&cma_obj->base);
+
+ drm_gem_object_unreference(gem_obj);
+
+ mutex_unlock(&drm->struct_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(drm_gem_cma_dumb_map_offset);
+
+/*
+ * drm_gem_cma_fault - (struct vm_operations_struct)->fault callback function
+ */
+int drm_gem_cma_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+ struct drm_gem_object *gem_obj = vma->vm_private_data;
+ struct drm_gem_cma_obj *cma_obj = to_dma_alloc_gem_obj(gem_obj);
+ struct drm_device *dev = gem_obj->dev;
+ unsigned long pfn;
+ pgoff_t page_offset;
+ int ret;
+
+ page_offset = ((unsigned long)vmf->virtual_address -
+ vma->vm_start) >> PAGE_SHIFT;
+
+ mutex_lock(&dev->struct_mutex);
+
+ pfn = (cma_obj->paddr >> PAGE_SHIFT) + page_offset;
+
+ ret = vm_insert_mixed(vma, (unsigned long)vmf->virtual_address, pfn);
+
+ mutex_unlock(&dev->struct_mutex);
+
+ return convert_to_vm_err_msg(ret);
+}
+EXPORT_SYMBOL_GPL(drm_gem_cma_fault);
+
+/*
+ * drm_gem_cma_mmap - (struct file_operation)->mmap callback function
+ */
+int drm_gem_cma_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ int ret;
+
+ ret = drm_gem_mmap(filp, vma);
+ if (ret)
+ return ret;
+
+ vma->vm_flags &= ~VM_PFNMAP;
+ vma->vm_flags |= VM_MIXEDMAP;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(drm_gem_cma_mmap);
+
+/*
+ * drm_gem_cma_dumb_destroy - (struct drm_driver)->dumb_destroy callback function
+ */
+int drm_gem_cma_dumb_destroy(struct drm_file *file_priv,
+ struct drm_device *drm, unsigned int handle)
+{
+ return drm_gem_handle_delete(file_priv, handle);
+}
+EXPORT_SYMBOL_GPL(drm_gem_cma_dumb_destroy);
diff --git a/include/drm/drm_gem_cma_helper.h b/include/drm/drm_gem_cma_helper.h
new file mode 100644
index 0000000..53b007c
--- /dev/null
+++ b/include/drm/drm_gem_cma_helper.h
@@ -0,0 +1,47 @@
+#ifndef __DRM_GEM_DMA_ALLOC_HELPER_H__
+#define __DRM_GEM_DMA_ALLOC_HELPER_H__
+
+struct drm_gem_cma_obj {
+ struct drm_gem_object base;
+ dma_addr_t paddr;
+ void __iomem *vaddr;
+};
+
+#define to_dma_alloc_gem_obj(x) \
+ container_of(x, struct drm_gem_cma_obj, base)
+
+/* free gem object. */
+void drm_gem_cma_free_object(struct drm_gem_object *gem_obj);
+
+/* create memory region for drm framebuffer. */
+int drm_gem_cma_dumb_create(struct drm_file *file_priv,
+ struct drm_device *drm, struct drm_mode_create_dumb *args);
+
+/* map memory region for drm framebuffer to user space. */
+int drm_gem_cma_dumb_map_offset(struct drm_file *file_priv,
+ struct drm_device *drm, uint32_t handle, uint64_t *offset);
+
+/* page fault handler and mmap fault address(virtual) to physical memory. */
+int drm_gem_cma_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
+
+/* set vm_flags and we can change the vm attribute to other one at here. */
+int drm_gem_cma_mmap(struct file *filp, struct vm_area_struct *vma);
+
+/*
+ * destroy memory region allocated.
+ * - a gem handle and physical memory region pointed by a gem object
+ * would be released by drm_gem_handle_delete().
+ */
+int drm_gem_cma_dumb_destroy(struct drm_file *file_priv,
+ struct drm_device *drm, unsigned int handle);
+
+/* allocate physical memory. */
+struct drm_gem_cma_obj *drm_gem_cma_create(struct drm_device *drm,
+ unsigned int size);
+
+struct drm_gem_cma_obj *drm_gem_cma_create_with_handle(
+ struct drm_file *file_priv,
+ struct drm_device *drm, unsigned int size,
+ unsigned int *handle);
+
+#endif
--
1.7.10
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH] DRM: add drm gem CMA helper
2012-05-29 14:10 Sascha Hauer
@ 2012-05-29 14:46 ` Lars-Peter Clausen
2012-05-29 15:26 ` Lars-Peter Clausen
2012-05-29 16:06 ` Sascha Hauer
2012-05-30 15:40 ` Laurent Pinchart
1 sibling, 2 replies; 10+ messages in thread
From: Lars-Peter Clausen @ 2012-05-29 14:46 UTC (permalink / raw)
To: Sascha Hauer; +Cc: kernel, dri-devel, Rob Clark
On 05/29/2012 04:10 PM, Sascha Hauer wrote:
> Many embedded drm devices do not have a IOMMU and no dedicated
> memory for graphics. These devices use CMA (Contiguous Memory
> Allocator) backed graphics memory. This patch provides helper
> functions to be able to share the code.
>
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
>
> Lars-Peter, please let me know if this fits your needs or if you are missing
> something.
>
Awesome :) The overall structure looks basically like what I had, so I can
just use these functions as drop-in replacement. I had some minor
differences in the implementation though. Comments inline.
> drivers/gpu/drm/Kconfig | 6 +
> drivers/gpu/drm/Makefile | 1 +
> drivers/gpu/drm/drm_gem_cma_helper.c | 321 ++++++++++++++++++++++++++++++++++
> include/drm/drm_gem_cma_helper.h | 47 +++++
> 4 files changed, 375 insertions(+)
> create mode 100644 drivers/gpu/drm/drm_gem_cma_helper.c
> create mode 100644 include/drm/drm_gem_cma_helper.h
>
> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> index e354bc0..f62717e 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -53,6 +53,12 @@ config DRM_TTM
> GPU memory types. Will be enabled automatically if a device driver
> uses it.
>
> +config DRM_GEM_CMA_HELPER
> + tristate
> + depends on DRM
> + help
> + Choose this if you need the GEM cma helper functions
This shouldn't have a help text as it should be selected by the driver and
not by the user. Also the 'depends on DRM' can go away, since it becomes
meaningless if the symbol is not user-selectable.
> +
> config DRM_TDFX
> tristate "3dfx Banshee/Voodoo3+"
> depends on DRM && PCI
> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> index c20da5b..9a0d98a 100644
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -15,6 +15,7 @@ drm-y := drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \
> drm_trace_points.o drm_global.o drm_prime.o
>
> drm-$(CONFIG_COMPAT) += drm_ioc32.o
> +drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o
>
> drm-usb-y := drm_usb.o
>
> diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c b/drivers/gpu/drm/drm_gem_cma_helper.c
> new file mode 100644
> index 0000000..75534ff
> --- /dev/null
> +++ b/drivers/gpu/drm/drm_gem_cma_helper.c
> @@ -0,0 +1,321 @@
> [...]
> +/*
> + * drm_gem_cma_create - allocate an object with the given size
> + *
> + * returns a struct drm_gem_cma_obj* on success or ERR_PTR values
> + * on failure.
> + */
> +struct drm_gem_cma_obj *drm_gem_cma_create(struct drm_device *drm,
> + unsigned int size)
> +{
> + struct drm_gem_cma_obj *cma_obj;
> + struct drm_gem_object *gem_obj;
> + int ret;
> +
> + size = roundup(size, PAGE_SIZE);
> +
> + cma_obj = kzalloc(sizeof(*cma_obj), GFP_KERNEL);
> + if (!cma_obj)
> + return ERR_PTR(-ENOMEM);
> +
> + cma_obj->vaddr = dma_alloc_writecombine(drm->dev, size,
> + (dma_addr_t *)&cma_obj->paddr,
The cast shouldn't be necessary.
? [...]
> +}
> +EXPORT_SYMBOL_GPL(drm_gem_cma_create);
>[...]
> +
> +static int drm_gem_cma_mmap_buffer(struct file *filp,
> + struct vm_area_struct *vma)
> +{
> + struct drm_gem_object *gem_obj = filp->private_data;
> + struct drm_gem_cma_obj *cma_obj = to_dma_alloc_gem_obj(gem_obj);
> + unsigned long pfn, vm_size;
> +
> + vma->vm_flags |= VM_IO | VM_RESERVED;
> +
> + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
> + vma->vm_file = filp;
> +
> + vm_size = vma->vm_end - vma->vm_start;
> +
> + /* check if user-requested size is valid. */
> + if (vm_size > gem_obj->size)
> + return -EINVAL;
> +
> + /*
> + * get page frame number to physical memory to be mapped
> + * to user space.
> + */
> + pfn = cma_obj->paddr >> PAGE_SHIFT;
> +
> + if (remap_pfn_range(vma, vma->vm_start, pfn, vm_size,
> + vma->vm_page_prot)) {
> + dev_err(gem_obj->dev->dev, "failed to remap pfn range.\n");
> + return -EAGAIN;
> + }
> +
> + return 0;
> +}
> +
> +static const struct file_operations drm_gem_cma_fops = {
> + .mmap = drm_gem_cma_mmap_buffer,
> +};
This and the function above seem to be unused. I think it's a relict from
the old Exynos code.
> [...]
> +/*
> + * drm_gem_cma_dumb_create - (struct drm_driver)->dumb_create callback
> + * function
> + *
> + * This aligns the pitch and size arguments to the minimum required. wrap
> + * this into your own function if you need bigger alignment.
> + */
> +int drm_gem_cma_dumb_create(struct drm_file *file_priv,
> + struct drm_device *dev, struct drm_mode_create_dumb *args)
> +{
> + struct drm_gem_cma_obj *cma_obj;
> +
> + if (args->pitch < args->width * args->bpp >> 3)
> + args->pitch = args->width * args->bpp >> 3;
I used DIV_ROUND_UP(args->bpp, 8) here. I think it makes more sense for the
generic case.
> +
> + if (args->size < args->pitch * args->height)
> + args->size = args->pitch * args->height;
> +
> + cma_obj = drm_gem_cma_create_with_handle(file_priv, dev,
> + args->size, &args->handle);
> + if (IS_ERR(cma_obj))
> + return PTR_ERR(cma_obj);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(drm_gem_cma_dumb_create);
> +
> +/*
> + * drm_gem_cma_dumb_map_offset - (struct drm_driver)->dumb_map_offset callback
> + * function
> + */
> +int drm_gem_cma_dumb_map_offset(struct drm_file *file_priv,
> + struct drm_device *drm, uint32_t handle, uint64_t *offset)
> +{
> + struct drm_gem_cma_obj *cma_obj;
> + struct drm_gem_object *gem_obj;
> +
> + mutex_lock(&drm->struct_mutex);
> +
> + gem_obj = drm_gem_object_lookup(drm, file_priv, handle);
> + if (!gem_obj) {
> + dev_err(drm->dev, "failed to lookup gem object\n");
> + mutex_unlock(&drm->struct_mutex);
> + return -EINVAL;
> + }
> +
> + cma_obj = to_dma_alloc_gem_obj(gem_obj);
> +
> + *offset = get_gem_mmap_offset(&cma_obj->base);
Just get_gem_mmap_offset(gem_obj), also means the to_dma_alloc_gem_obj can
go away.
> +
> + drm_gem_object_unreference(gem_obj);
> +
> + mutex_unlock(&drm->struct_mutex);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(drm_gem_cma_dumb_map_offset);
> +
> +/*
> + * drm_gem_cma_fault - (struct vm_operations_struct)->fault callback function
> + */
> +int drm_gem_cma_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
> +{
> + struct drm_gem_object *gem_obj = vma->vm_private_data;
> + struct drm_gem_cma_obj *cma_obj = to_dma_alloc_gem_obj(gem_obj);
> + struct drm_device *dev = gem_obj->dev;
> + unsigned long pfn;
> + pgoff_t page_offset;
> + int ret;
> +
> + page_offset = ((unsigned long)vmf->virtual_address -
> + vma->vm_start) >> PAGE_SHIFT;
> +
> + mutex_lock(&dev->struct_mutex);
> +
> + pfn = (cma_obj->paddr >> PAGE_SHIFT) + page_offset;
> +
> + ret = vm_insert_mixed(vma, (unsigned long)vmf->virtual_address, pfn);
> +
> + mutex_unlock(&dev->struct_mutex);
> +
> + return convert_to_vm_err_msg(ret);
> +}
> +EXPORT_SYMBOL_GPL(drm_gem_cma_fault);
> +
Do you think it makes sense to have generic vm_operations struct as well, I
think it will look the same for most drivers:
struct vm_operations_struct drm_gem_cma_vm_ops = {
.fault = drm_gem_cma_fault,
.open = drm_gem_vm_open,
.close = drm_gem_vm_close,
};
> +/*
> + * drm_gem_cma_mmap - (struct file_operation)->mmap callback function
> + */
> +int drm_gem_cma_mmap(struct file *filp, struct vm_area_struct *vma)
> +{
> + int ret;
> +
> + ret = drm_gem_mmap(filp, vma);
> + if (ret)
> + return ret;
> +
> + vma->vm_flags &= ~VM_PFNMAP;
> + vma->vm_flags |= VM_MIXEDMAP;
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(drm_gem_cma_mmap);
This might be worth being made a more generic helper function, since we are
not the only ones who want a mixed mapping. But that can wait for later.
> +
> +/*
> + * drm_gem_cma_dumb_destroy - (struct drm_driver)->dumb_destroy callback function
> + */
> +int drm_gem_cma_dumb_destroy(struct drm_file *file_priv,
> + struct drm_device *drm, unsigned int handle)
> +{
> + return drm_gem_handle_delete(file_priv, handle);
> +}
> +EXPORT_SYMBOL_GPL(drm_gem_cma_dumb_destroy);
> diff --git a/include/drm/drm_gem_cma_helper.h b/include/drm/drm_gem_cma_helper.h
> new file mode 100644
> index 0000000..53b007c
> --- /dev/null
> +++ b/include/drm/drm_gem_cma_helper.h
> @@ -0,0 +1,47 @@
> +#ifndef __DRM_GEM_DMA_ALLOC_HELPER_H__
> +#define __DRM_GEM_DMA_ALLOC_HELPER_H__
> +
> +struct drm_gem_cma_obj {
> + struct drm_gem_object base;
> + dma_addr_t paddr;
> + void __iomem *vaddr;
__iomem?
> +};
> +
> +#define to_dma_alloc_gem_obj(x) \
> + container_of(x, struct drm_gem_cma_obj, base)
> +
> +/* free gem object. */
> +void drm_gem_cma_free_object(struct drm_gem_object *gem_obj);
> +
> +/* create memory region for drm framebuffer. */
> +int drm_gem_cma_dumb_create(struct drm_file *file_priv,
> + struct drm_device *drm, struct drm_mode_create_dumb *args);
> +
> +/* map memory region for drm framebuffer to user space. */
> +int drm_gem_cma_dumb_map_offset(struct drm_file *file_priv,
> + struct drm_device *drm, uint32_t handle, uint64_t *offset);
> +
> +/* page fault handler and mmap fault address(virtual) to physical memory. */
> +int drm_gem_cma_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
> +
> +/* set vm_flags and we can change the vm attribute to other one at here. */
> +int drm_gem_cma_mmap(struct file *filp, struct vm_area_struct *vma);
> +
> +/*
> + * destroy memory region allocated.
> + * - a gem handle and physical memory region pointed by a gem object
> + * would be released by drm_gem_handle_delete().
> + */
> +int drm_gem_cma_dumb_destroy(struct drm_file *file_priv,
> + struct drm_device *drm, unsigned int handle);
> +
> +/* allocate physical memory. */
> +struct drm_gem_cma_obj *drm_gem_cma_create(struct drm_device *drm,
> + unsigned int size);
> +
> +struct drm_gem_cma_obj *drm_gem_cma_create_with_handle(
> + struct drm_file *file_priv,
> + struct drm_device *drm, unsigned int size,
> + unsigned int *handle);
> +
> +#endif
Thanks for taking care of this,
- Lars
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] DRM: add drm gem CMA helper
2012-05-29 14:46 ` Lars-Peter Clausen
@ 2012-05-29 15:26 ` Lars-Peter Clausen
2012-05-29 16:06 ` Sascha Hauer
1 sibling, 0 replies; 10+ messages in thread
From: Lars-Peter Clausen @ 2012-05-29 15:26 UTC (permalink / raw)
To: Sascha Hauer; +Cc: dri-devel, kernel, Rob Clark
On 05/29/2012 04:46 PM, Lars-Peter Clausen wrote:
> On 05/29/2012 04:10 PM, Sascha Hauer wrote:
>> Many embedded drm devices do not have a IOMMU and no dedicated
>> memory for graphics. These devices use CMA (Contiguous Memory
>> Allocator) backed graphics memory. This patch provides helper
>> functions to be able to share the code.
>>
>> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
>> ---
>>
>> Lars-Peter, please let me know if this fits your needs or if you are missing
>> something.
>>
>
> Awesome :) The overall structure looks basically like what I had, so I can
> just use these functions as drop-in replacement. I had some minor
> differences in the implementation though. Comments inline.
Did some initial testing and it seems to work fine :)
>
>> drivers/gpu/drm/Kconfig | 6 +
>> drivers/gpu/drm/Makefile | 1 +
>> drivers/gpu/drm/drm_gem_cma_helper.c | 321 ++++++++++++++++++++++++++++++++++
>> include/drm/drm_gem_cma_helper.h | 47 +++++
>> 4 files changed, 375 insertions(+)
>> create mode 100644 drivers/gpu/drm/drm_gem_cma_helper.c
>> create mode 100644 include/drm/drm_gem_cma_helper.h
>>
>> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
>> index e354bc0..f62717e 100644
>> --- a/drivers/gpu/drm/Kconfig
>> +++ b/drivers/gpu/drm/Kconfig
>> @@ -53,6 +53,12 @@ config DRM_TTM
>> GPU memory types. Will be enabled automatically if a device driver
>> uses it.
>>
>> +config DRM_GEM_CMA_HELPER
>> + tristate
>> + depends on DRM
>> + help
>> + Choose this if you need the GEM cma helper functions
>
> This shouldn't have a help text as it should be selected by the driver and
> not by the user. Also the 'depends on DRM' can go away, since it becomes
> meaningless if the symbol is not user-selectable.
>
Sorry, ignore that. I though it would appear in menuconfig if it had an help
text. But it needs to have a title. The "depends on" can still go away though.
>> +
>> +#define to_dma_alloc_gem_obj(x) \
>> + container_of(x, struct drm_gem_cma_obj, base)
This looks like it has been missed during some renaming. It should probably
be "to_drm_gem_cma_obj". And maybe make it an inline function.
Thanks,
- Lars
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] DRM: add drm gem CMA helper
2012-05-29 14:46 ` Lars-Peter Clausen
2012-05-29 15:26 ` Lars-Peter Clausen
@ 2012-05-29 16:06 ` Sascha Hauer
1 sibling, 0 replies; 10+ messages in thread
From: Sascha Hauer @ 2012-05-29 16:06 UTC (permalink / raw)
To: Lars-Peter Clausen; +Cc: kernel, dri-devel, Rob Clark
Hi Lars,
Thanks for your quick comments.
On Tue, May 29, 2012 at 04:46:36PM +0200, Lars-Peter Clausen wrote:
> On 05/29/2012 04:10 PM, Sascha Hauer wrote:
> >
> > diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> > index e354bc0..f62717e 100644
> > --- a/drivers/gpu/drm/Kconfig
> > +++ b/drivers/gpu/drm/Kconfig
> > @@ -53,6 +53,12 @@ config DRM_TTM
> > GPU memory types. Will be enabled automatically if a device driver
> > uses it.
> >
> > +config DRM_GEM_CMA_HELPER
> > + tristate
> > + depends on DRM
> > + help
> > + Choose this if you need the GEM cma helper functions
>
> This shouldn't have a help text as it should be selected by the driver and
> not by the user. Also the 'depends on DRM' can go away, since it becomes
> meaningless if the symbol is not user-selectable.
The other helpers also have a depends on DRM in them. You are right,
it's quite meaningless. The only advantage I can think of is that it
would produce a 'has unmet direct dependencies' if someone outside DRM
would select this. I'll drop it.
> > +
> > +static const struct file_operations drm_gem_cma_fops = {
> > + .mmap = drm_gem_cma_mmap_buffer,
> > +};
>
> This and the function above seem to be unused. I think it's a relict from
> the old Exynos code.
Indeed. I wonder why my compiler hasn't warned me about this. Will
remove.
>
> Do you think it makes sense to have generic vm_operations struct as well, I
> think it will look the same for most drivers:
>
> struct vm_operations_struct drm_gem_cma_vm_ops = {
> .fault = drm_gem_cma_fault,
> .open = drm_gem_vm_open,
> .close = drm_gem_vm_close,
> };
As we both can make use of this I'll add it. People who need something
else can still add their own vm_operations_struct.
I integrated your other comments, will repost tomorrow unless there are
more comments on this.
Sascha
--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] DRM: add drm gem CMA helper
2012-05-29 14:10 Sascha Hauer
2012-05-29 14:46 ` Lars-Peter Clausen
@ 2012-05-30 15:40 ` Laurent Pinchart
2012-05-30 16:28 ` Sascha Hauer
1 sibling, 1 reply; 10+ messages in thread
From: Laurent Pinchart @ 2012-05-30 15:40 UTC (permalink / raw)
To: dri-devel; +Cc: kernel, Rob Clark
Hi Sascha,
Thank you for the patch. I've successfully tested the helper with the new SH
Mobile DRM driver. Just a couple of comments below in addition to Lars'
comments (this is not a full review, just details that caught my attention
when comparing the code with my implementation, and trying to use it).
On Tuesday 29 May 2012 16:10:27 Sascha Hauer wrote:
> Many embedded drm devices do not have a IOMMU and no dedicated
> memory for graphics. These devices use CMA (Contiguous Memory
> Allocator) backed graphics memory. This patch provides helper
> functions to be able to share the code.
>
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
>
> Lars-Peter, please let me know if this fits your needs or if you are missing
> something.
>
> drivers/gpu/drm/Kconfig | 6 +
> drivers/gpu/drm/Makefile | 1 +
> drivers/gpu/drm/drm_gem_cma_helper.c | 321 +++++++++++++++++++++++++++++++
> include/drm/drm_gem_cma_helper.h | 47 +++++
> 4 files changed, 375 insertions(+)
> create mode 100644 drivers/gpu/drm/drm_gem_cma_helper.c
> create mode 100644 include/drm/drm_gem_cma_helper.h
[snip]
> diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c
> b/drivers/gpu/drm/drm_gem_cma_helper.c new file mode 100644
> index 0000000..75534ff
> --- /dev/null
> +++ b/drivers/gpu/drm/drm_gem_cma_helper.c
> @@ -0,0 +1,321 @@
[snip]
> +/*
> + * drm_gem_cma_create - allocate an object with the given size
> + *
> + * returns a struct drm_gem_cma_obj* on success or ERR_PTR values
> + * on failure.
> + */
> +struct drm_gem_cma_obj *drm_gem_cma_create(struct drm_device *drm,
> + unsigned int size)
> +{
> + struct drm_gem_cma_obj *cma_obj;
> + struct drm_gem_object *gem_obj;
> + int ret;
> +
> + size = roundup(size, PAGE_SIZE);
As PAGE_SIZE is guaranteed to be a power of two, you can use round_up, which
should be faster.
> +
> + cma_obj = kzalloc(sizeof(*cma_obj), GFP_KERNEL);
> + if (!cma_obj)
> + return ERR_PTR(-ENOMEM);
> +
> + cma_obj->vaddr = dma_alloc_writecombine(drm->dev, size,
> + (dma_addr_t *)&cma_obj->paddr,
> + GFP_KERNEL | __GFP_NOWARN);
> + if (!cma_obj->vaddr) {
> + dev_err(drm->dev, "failed to allocate buffer with size %d\n", size);
> + ret = -ENOMEM;
> + goto err_dma_alloc;
> + }
> +
> + gem_obj = &cma_obj->base;
> +
> + ret = drm_gem_object_init(drm, gem_obj, size);
> + if (ret)
> + goto err_obj_init;
> +
> + ret = drm_gem_create_mmap_offset(gem_obj);
> + if (ret)
> + goto err_create_mmap_offset;
> +
> + return cma_obj;
> +
> +err_create_mmap_offset:
> + drm_gem_object_release(gem_obj);
> +
> +err_obj_init:
> + drm_gem_cma_buf_destroy(drm, cma_obj);
> +
> +err_dma_alloc:
> + kfree(cma_obj);
> +
> + return ERR_PTR(ret);
> +}
> +EXPORT_SYMBOL_GPL(drm_gem_cma_create);
[snip]
> +/*
> + * drm_gem_cma_mmap - (struct file_operation)->mmap callback function
> + */
> +int drm_gem_cma_mmap(struct file *filp, struct vm_area_struct *vma)
> +{
> + int ret;
> +
> + ret = drm_gem_mmap(filp, vma);
> + if (ret)
> + return ret;
> +
> + vma->vm_flags &= ~VM_PFNMAP;
> + vma->vm_flags |= VM_MIXEDMAP;
Why is this a mixed map ?
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(drm_gem_cma_mmap);
My implementation maps the whole buffer in one go at mmap time, not page by
page at page fault time. Isn't that more efficient when mapping frame buffer
memory ?
[snip]
> diff --git a/include/drm/drm_gem_cma_helper.h
> b/include/drm/drm_gem_cma_helper.h new file mode 100644
> index 0000000..53b007c
> --- /dev/null
> +++ b/include/drm/drm_gem_cma_helper.h
> @@ -0,0 +1,47 @@
> +#ifndef __DRM_GEM_DMA_ALLOC_HELPER_H__
> +#define __DRM_GEM_DMA_ALLOC_HELPER_H__
Should this be __DRM_GEM_CMA_HELPER_H__ ?
> +
> +struct drm_gem_cma_obj {
> + struct drm_gem_object base;
> + dma_addr_t paddr;
> + void __iomem *vaddr;
> +};
All drm objects end with _object, would it be better to rename this to struct
drm_gem_cma_object ?
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] DRM: add drm gem CMA helper
2012-05-30 15:40 ` Laurent Pinchart
@ 2012-05-30 16:28 ` Sascha Hauer
2012-05-31 9:41 ` Laurent Pinchart
0 siblings, 1 reply; 10+ messages in thread
From: Sascha Hauer @ 2012-05-30 16:28 UTC (permalink / raw)
To: Laurent Pinchart; +Cc: kernel, dri-devel, Rob Clark
On Wed, May 30, 2012 at 05:40:13PM +0200, Laurent Pinchart wrote:
> Hi Sascha,
>
> Thank you for the patch. I've successfully tested the helper with the new SH
> Mobile DRM driver. Just a couple of comments below in addition to Lars'
> comments (this is not a full review, just details that caught my attention
> when comparing the code with my implementation, and trying to use it).
>
> > +/*
> > + * drm_gem_cma_mmap - (struct file_operation)->mmap callback function
> > + */
> > +int drm_gem_cma_mmap(struct file *filp, struct vm_area_struct *vma)
> > +{
> > + int ret;
> > +
> > + ret = drm_gem_mmap(filp, vma);
> > + if (ret)
> > + return ret;
> > +
> > + vma->vm_flags &= ~VM_PFNMAP;
> > + vma->vm_flags |= VM_MIXEDMAP;
>
> Why is this a mixed map ?
Honestly, I don't know. This is copied from the exynos driver. I don't
know much about these flags, so if you think something else is better
here than you're probably right ;)
>
> > +
> > + return ret;
> > +}
> > +EXPORT_SYMBOL_GPL(drm_gem_cma_mmap);
>
> My implementation maps the whole buffer in one go at mmap time, not page by
> page at page fault time. Isn't that more efficient when mapping frame buffer
> memory ?
I remember Alan has mentioned this in an earlier review. I'll update
the patch accordingly.
I will fix this and the other things you mentioned and repost.
Thanks
Sascha
--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] DRM: add drm gem CMA helper
2012-05-30 16:28 ` Sascha Hauer
@ 2012-05-31 9:41 ` Laurent Pinchart
0 siblings, 0 replies; 10+ messages in thread
From: Laurent Pinchart @ 2012-05-31 9:41 UTC (permalink / raw)
To: Sascha Hauer; +Cc: kernel, dri-devel, Rob Clark
Hi Sascha,
On Wednesday 30 May 2012 18:28:12 Sascha Hauer wrote:
> On Wed, May 30, 2012 at 05:40:13PM +0200, Laurent Pinchart wrote:
> > Hi Sascha,
> >
> > Thank you for the patch. I've successfully tested the helper with the new
> > SH Mobile DRM driver. Just a couple of comments below in addition to
> > Lars' comments (this is not a full review, just details that caught my
> > attention when comparing the code with my implementation, and trying to
> > use it).
> > > +/*
> > > + * drm_gem_cma_mmap - (struct file_operation)->mmap callback function
> > > + */
> > > +int drm_gem_cma_mmap(struct file *filp, struct vm_area_struct *vma)
> > > +{
> > > + int ret;
> > > +
> > > + ret = drm_gem_mmap(filp, vma);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + vma->vm_flags &= ~VM_PFNMAP;
> > > + vma->vm_flags |= VM_MIXEDMAP;
> >
> > Why is this a mixed map ?
>
> Honestly, I don't know. This is copied from the exynos driver. I don't
> know much about these flags, so if you think something else is better
> here than you're probably right ;)
I wouldn't claim to be an expert on this matter :-) As far as I know, PFNMAP
means that the mapping refers to physical memory with no struct page
associated with it (that could be I/O memory, DRAM reserved at boot time, ...
basically any memory outside of the system memory resources controlled by the
kernel page allocator). MIXEDMAP means that the mapping refers to memory that
contains both PFNMAP regions and non-PFNMAP regions. I would be surprised if
dma_alloc_writecombine() returned such a mix. On the other hand the memory it
returns might be PFNMAP or non-PFNMAP depending on the underneath allocator,
maybe that's why VM_MIXEDMAP was used.
I'd appreciate an expert's opinion on this :-)
> > > +
> > > + return ret;
> > > +}
> > > +EXPORT_SYMBOL_GPL(drm_gem_cma_mmap);
> >
> > My implementation maps the whole buffer in one go at mmap time, not page
> > by page at page fault time. Isn't that more efficient when mapping frame
> > buffer memory ?
>
> I remember Alan has mentioned this in an earlier review. I'll update the
> patch accordingly.
>
> I will fix this and the other things you mentioned and repost.
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH] DRM: add drm gem CMA helper
@ 2012-06-27 13:00 Sascha Hauer
2012-06-27 13:20 ` Laurent Pinchart
0 siblings, 1 reply; 10+ messages in thread
From: Sascha Hauer @ 2012-06-27 13:00 UTC (permalink / raw)
To: dri-devel; +Cc: Laurent Pinchart
Many embedded drm devices do not have a IOMMU and no dedicated
memory for graphics. These devices use CMA (Contiguous Memory
Allocator) backed graphics memory. This patch provides helper
functions to be able to share the code. The code technically does
not depend on CMA as the backend allocator, the name has been chosen
because cma makes for a nice, short but still descriptive function
prefix.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
changes since v2:
- make drm_gem_cma_create_with_handle static
- use DIV_ROUND_UP(args->width * args->bpp, 8) to calculate the pitch
- make drm_gem_cma_vm_ops const
- add missing #include <linux/export.h>
changes since v1:
- map whole buffer at mmap time, not page by page at fault time
- several cleanups as suggested by Lars-Peter Clausen and Laurent Pinchart
drivers/gpu/drm/Kconfig | 6 +
drivers/gpu/drm/Makefile | 1 +
drivers/gpu/drm/drm_gem_cma_helper.c | 250 ++++++++++++++++++++++++++++++++++
include/drm/drm_gem_cma_helper.h | 44 ++++++
4 files changed, 301 insertions(+)
create mode 100644 drivers/gpu/drm/drm_gem_cma_helper.c
create mode 100644 include/drm/drm_gem_cma_helper.h
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index dc5df12..8f362ec 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -53,6 +53,12 @@ config DRM_TTM
GPU memory types. Will be enabled automatically if a device driver
uses it.
+config DRM_GEM_CMA_HELPER
+ tristate
+ depends on DRM
+ help
+ Choose this if you need the GEM CMA helper functions
+
config DRM_TDFX
tristate "3dfx Banshee/Voodoo3+"
depends on DRM && PCI
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 0487ff6..8759cda 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -15,6 +15,7 @@ drm-y := drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \
drm_trace_points.o drm_global.o drm_prime.o
drm-$(CONFIG_COMPAT) += drm_ioc32.o
+drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o
drm-usb-y := drm_usb.o
diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c b/drivers/gpu/drm/drm_gem_cma_helper.c
new file mode 100644
index 0000000..417f45e5
--- /dev/null
+++ b/drivers/gpu/drm/drm_gem_cma_helper.c
@@ -0,0 +1,250 @@
+/*
+ * drm gem CMA (contiguous memory allocator) helper functions
+ *
+ * Copyright (C) 2012 Sascha Hauer, Pengutronix
+ *
+ * Based on Samsung Exynos code
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/export.h>
+#include <linux/dma-mapping.h>
+
+#include <drm/drmP.h>
+#include <drm/drm.h>
+#include <drm/drm_gem_cma_helper.h>
+
+static unsigned int get_gem_mmap_offset(struct drm_gem_object *obj)
+{
+ return (unsigned int)obj->map_list.hash.key << PAGE_SHIFT;
+}
+
+static void drm_gem_cma_buf_destroy(struct drm_device *drm,
+ struct drm_gem_cma_object *cma_obj)
+{
+ dma_free_writecombine(drm->dev, cma_obj->base.size, cma_obj->vaddr,
+ cma_obj->paddr);
+}
+
+/*
+ * drm_gem_cma_create - allocate an object with the given size
+ *
+ * returns a struct drm_gem_cma_object* on success or ERR_PTR values
+ * on failure.
+ */
+struct drm_gem_cma_object *drm_gem_cma_create(struct drm_device *drm,
+ unsigned int size)
+{
+ struct drm_gem_cma_object *cma_obj;
+ struct drm_gem_object *gem_obj;
+ int ret;
+
+ size = round_up(size, PAGE_SIZE);
+
+ cma_obj = kzalloc(sizeof(*cma_obj), GFP_KERNEL);
+ if (!cma_obj)
+ return ERR_PTR(-ENOMEM);
+
+ cma_obj->vaddr = dma_alloc_writecombine(drm->dev, size,
+ &cma_obj->paddr, GFP_KERNEL | __GFP_NOWARN);
+ if (!cma_obj->vaddr) {
+ dev_err(drm->dev, "failed to allocate buffer with size %d\n", size);
+ ret = -ENOMEM;
+ goto err_dma_alloc;
+ }
+
+ gem_obj = &cma_obj->base;
+
+ ret = drm_gem_object_init(drm, gem_obj, size);
+ if (ret)
+ goto err_obj_init;
+
+ ret = drm_gem_create_mmap_offset(gem_obj);
+ if (ret)
+ goto err_create_mmap_offset;
+
+ return cma_obj;
+
+err_create_mmap_offset:
+ drm_gem_object_release(gem_obj);
+
+err_obj_init:
+ drm_gem_cma_buf_destroy(drm, cma_obj);
+
+err_dma_alloc:
+ kfree(cma_obj);
+
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(drm_gem_cma_create);
+
+/*
+ * drm_gem_cma_create_with_handle - allocate an object with the given
+ * size and create a gem handle on it
+ *
+ * returns a struct drm_gem_cma_object* on success or ERR_PTR values
+ * on failure.
+ */
+static struct drm_gem_cma_object *drm_gem_cma_create_with_handle(
+ struct drm_file *file_priv,
+ struct drm_device *drm, unsigned int size,
+ unsigned int *handle)
+{
+ struct drm_gem_cma_object *cma_obj;
+ struct drm_gem_object *gem_obj;
+ int ret;
+
+ cma_obj = drm_gem_cma_create(drm, size);
+ if (IS_ERR(cma_obj))
+ return cma_obj;
+
+ gem_obj = &cma_obj->base;
+
+ /*
+ * allocate a id of idr table where the obj is registered
+ * and handle has the id what user can see.
+ */
+ ret = drm_gem_handle_create(file_priv, gem_obj, handle);
+ if (ret)
+ goto err_handle_create;
+
+ /* drop reference from allocate - handle holds it now. */
+ drm_gem_object_unreference_unlocked(gem_obj);
+
+ return cma_obj;
+
+err_handle_create:
+ drm_gem_cma_free_object(gem_obj);
+
+ return ERR_PTR(ret);
+}
+
+/*
+ * drm_gem_cma_free_object - (struct drm_driver)->gem_free_object callback
+ * function
+ */
+void drm_gem_cma_free_object(struct drm_gem_object *gem_obj)
+{
+ struct drm_gem_cma_object *cma_obj;
+
+ if (gem_obj->map_list.map)
+ drm_gem_free_mmap_offset(gem_obj);
+
+ drm_gem_object_release(gem_obj);
+
+ cma_obj = to_drm_gem_cma_obj(gem_obj);
+
+ drm_gem_cma_buf_destroy(gem_obj->dev, cma_obj);
+
+ kfree(cma_obj);
+}
+EXPORT_SYMBOL_GPL(drm_gem_cma_free_object);
+
+/*
+ * drm_gem_cma_dumb_create - (struct drm_driver)->dumb_create callback
+ * function
+ *
+ * This aligns the pitch and size arguments to the minimum required. wrap
+ * this into your own function if you need bigger alignment.
+ */
+int drm_gem_cma_dumb_create(struct drm_file *file_priv,
+ struct drm_device *dev, struct drm_mode_create_dumb *args)
+{
+ struct drm_gem_cma_object *cma_obj;
+
+ if (args->pitch < args->width * DIV_ROUND_UP(args->bpp, 8))
+ args->pitch = args->width * DIV_ROUND_UP(args->bpp, 8);
+
+ if (args->size < args->pitch * args->height)
+ args->size = args->pitch * args->height;
+
+ cma_obj = drm_gem_cma_create_with_handle(file_priv, dev,
+ args->size, &args->handle);
+ if (IS_ERR(cma_obj))
+ return PTR_ERR(cma_obj);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(drm_gem_cma_dumb_create);
+
+/*
+ * drm_gem_cma_dumb_map_offset - (struct drm_driver)->dumb_map_offset callback
+ * function
+ */
+int drm_gem_cma_dumb_map_offset(struct drm_file *file_priv,
+ struct drm_device *drm, uint32_t handle, uint64_t *offset)
+{
+ struct drm_gem_object *gem_obj;
+
+ mutex_lock(&drm->struct_mutex);
+
+ gem_obj = drm_gem_object_lookup(drm, file_priv, handle);
+ if (!gem_obj) {
+ dev_err(drm->dev, "failed to lookup gem object\n");
+ mutex_unlock(&drm->struct_mutex);
+ return -EINVAL;
+ }
+
+ *offset = get_gem_mmap_offset(gem_obj);
+
+ drm_gem_object_unreference(gem_obj);
+
+ mutex_unlock(&drm->struct_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(drm_gem_cma_dumb_map_offset);
+
+const struct vm_operations_struct drm_gem_cma_vm_ops = {
+ .open = drm_gem_vm_open,
+ .close = drm_gem_vm_close,
+};
+EXPORT_SYMBOL_GPL(drm_gem_cma_vm_ops);
+
+/*
+ * drm_gem_cma_mmap - (struct file_operation)->mmap callback function
+ */
+int drm_gem_cma_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct drm_gem_object *gem_obj;
+ struct drm_gem_cma_object *cma_obj;
+ int ret;
+
+ ret = drm_gem_mmap(filp, vma);
+ if (ret)
+ return ret;
+
+ gem_obj = vma->vm_private_data;
+ cma_obj = to_drm_gem_cma_obj(gem_obj);
+
+ ret = remap_pfn_range(vma, vma->vm_start, cma_obj->paddr >> PAGE_SHIFT,
+ vma->vm_end - vma->vm_start, vma->vm_page_prot);
+ if (ret)
+ drm_gem_vm_close(vma);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(drm_gem_cma_mmap);
+
+/*
+ * drm_gem_cma_dumb_destroy - (struct drm_driver)->dumb_destroy callback function
+ */
+int drm_gem_cma_dumb_destroy(struct drm_file *file_priv,
+ struct drm_device *drm, unsigned int handle)
+{
+ return drm_gem_handle_delete(file_priv, handle);
+}
+EXPORT_SYMBOL_GPL(drm_gem_cma_dumb_destroy);
diff --git a/include/drm/drm_gem_cma_helper.h b/include/drm/drm_gem_cma_helper.h
new file mode 100644
index 0000000..f0f6b1a
--- /dev/null
+++ b/include/drm/drm_gem_cma_helper.h
@@ -0,0 +1,44 @@
+#ifndef __DRM_GEM_CMA_HELPER_H__
+#define __DRM_GEM_CMA_HELPER_H__
+
+struct drm_gem_cma_object {
+ struct drm_gem_object base;
+ dma_addr_t paddr;
+ void *vaddr;
+};
+
+static inline struct drm_gem_cma_object *
+to_drm_gem_cma_obj(struct drm_gem_object *gem_obj)
+{
+ return container_of(gem_obj, struct drm_gem_cma_object, base);
+}
+
+/* free gem object. */
+void drm_gem_cma_free_object(struct drm_gem_object *gem_obj);
+
+/* create memory region for drm framebuffer. */
+int drm_gem_cma_dumb_create(struct drm_file *file_priv,
+ struct drm_device *drm, struct drm_mode_create_dumb *args);
+
+/* map memory region for drm framebuffer to user space. */
+int drm_gem_cma_dumb_map_offset(struct drm_file *file_priv,
+ struct drm_device *drm, uint32_t handle, uint64_t *offset);
+
+/* set vm_flags and we can change the vm attribute to other one at here. */
+int drm_gem_cma_mmap(struct file *filp, struct vm_area_struct *vma);
+
+/*
+ * destroy memory region allocated.
+ * - a gem handle and physical memory region pointed by a gem object
+ * would be released by drm_gem_handle_delete().
+ */
+int drm_gem_cma_dumb_destroy(struct drm_file *file_priv,
+ struct drm_device *drm, unsigned int handle);
+
+/* allocate physical memory. */
+struct drm_gem_cma_object *drm_gem_cma_create(struct drm_device *drm,
+ unsigned int size);
+
+extern const struct vm_operations_struct drm_gem_cma_vm_ops;
+
+#endif /* __DRM_GEM_CMA_HELPER_H__ */
--
1.7.10
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH] DRM: add drm gem CMA helper
2012-06-27 13:00 [PATCH] DRM: add drm gem CMA helper Sascha Hauer
@ 2012-06-27 13:20 ` Laurent Pinchart
2012-06-27 13:43 ` Sascha Hauer
0 siblings, 1 reply; 10+ messages in thread
From: Laurent Pinchart @ 2012-06-27 13:20 UTC (permalink / raw)
To: dri-devel
Hi Sascha,
Thanks for the patch.
On Wednesday 27 June 2012 15:00:05 Sascha Hauer wrote:
> Many embedded drm devices do not have a IOMMU and no dedicated
> memory for graphics. These devices use CMA (Contiguous Memory
> Allocator) backed graphics memory. This patch provides helper
> functions to be able to share the code. The code technically does
> not depend on CMA as the backend allocator, the name has been chosen
> because cma makes for a nice, short but still descriptive function
> prefix.
>
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
>
> changes since v2:
> - make drm_gem_cma_create_with_handle static
> - use DIV_ROUND_UP(args->width * args->bpp, 8) to calculate the pitch
[snip]
> diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c
> b/drivers/gpu/drm/drm_gem_cma_helper.c new file mode 100644
> index 0000000..417f45e5
> --- /dev/null
> +++ b/drivers/gpu/drm/drm_gem_cma_helper.c
> @@ -0,0 +1,250 @@
[snip]
> +#include <linux/mm.h>
> +#include <linux/slab.h>
> +#include <linux/mutex.h>
> +#include <linux/export.h>
> +#include <linux/dma-mapping.h>
Nitpicking, I usually sort headers alphabetically, that helps locating
duplicates quickly. It's not mandatory though.
[snip]
> +/*
> + * drm_gem_cma_dumb_create - (struct drm_driver)->dumb_create callback
> + * function
> + *
> + * This aligns the pitch and size arguments to the minimum required. wrap
> + * this into your own function if you need bigger alignment.
> + */
> +int drm_gem_cma_dumb_create(struct drm_file *file_priv,
> + struct drm_device *dev, struct drm_mode_create_dumb *args)
> +{
> + struct drm_gem_cma_object *cma_obj;
> +
> + if (args->pitch < args->width * DIV_ROUND_UP(args->bpp, 8))
> + args->pitch = args->width * DIV_ROUND_UP(args->bpp, 8);
It's nice to mention in the changelog that you fixed the problem, but it would
be even nicer to really fix it ;-)
> +
> + if (args->size < args->pitch * args->height)
> + args->size = args->pitch * args->height;
> +
> + cma_obj = drm_gem_cma_create_with_handle(file_priv, dev,
> + args->size, &args->handle);
> + if (IS_ERR(cma_obj))
> + return PTR_ERR(cma_obj);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(drm_gem_cma_dumb_create);
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] DRM: add drm gem CMA helper
2012-06-27 13:20 ` Laurent Pinchart
@ 2012-06-27 13:43 ` Sascha Hauer
0 siblings, 0 replies; 10+ messages in thread
From: Sascha Hauer @ 2012-06-27 13:43 UTC (permalink / raw)
To: Laurent Pinchart; +Cc: dri-devel
On Wed, Jun 27, 2012 at 03:20:27PM +0200, Laurent Pinchart wrote:
> Hi Sascha,
>
> Thanks for the patch.
>
> On Wednesday 27 June 2012 15:00:05 Sascha Hauer wrote:
> > Many embedded drm devices do not have a IOMMU and no dedicated
> > memory for graphics. These devices use CMA (Contiguous Memory
> > Allocator) backed graphics memory. This patch provides helper
> > functions to be able to share the code. The code technically does
> > not depend on CMA as the backend allocator, the name has been chosen
> > because cma makes for a nice, short but still descriptive function
> > prefix.
> >
> > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> > ---
> >
> > changes since v2:
> > - make drm_gem_cma_create_with_handle static
> > - use DIV_ROUND_UP(args->width * args->bpp, 8) to calculate the pitch
>
> [snip]
>
> > diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c
> > b/drivers/gpu/drm/drm_gem_cma_helper.c new file mode 100644
> > index 0000000..417f45e5
> > --- /dev/null
> > +++ b/drivers/gpu/drm/drm_gem_cma_helper.c
> > @@ -0,0 +1,250 @@
>
> [snip]
>
> > +#include <linux/mm.h>
> > +#include <linux/slab.h>
> > +#include <linux/mutex.h>
> > +#include <linux/export.h>
> > +#include <linux/dma-mapping.h>
>
> Nitpicking, I usually sort headers alphabetically, that helps locating
> duplicates quickly. It's not mandatory though.
>
> [snip]
>
> > +/*
> > + * drm_gem_cma_dumb_create - (struct drm_driver)->dumb_create callback
> > + * function
> > + *
> > + * This aligns the pitch and size arguments to the minimum required. wrap
> > + * this into your own function if you need bigger alignment.
> > + */
> > +int drm_gem_cma_dumb_create(struct drm_file *file_priv,
> > + struct drm_device *dev, struct drm_mode_create_dumb *args)
> > +{
> > + struct drm_gem_cma_object *cma_obj;
> > +
> > + if (args->pitch < args->width * DIV_ROUND_UP(args->bpp, 8))
> > + args->pitch = args->width * DIV_ROUND_UP(args->bpp, 8);
>
> It's nice to mention in the changelog that you fixed the problem, but it would
> be even nicer to really fix it ;-)
Damn ;)
Sascha
--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2012-06-27 13:43 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-06-27 13:00 [PATCH] DRM: add drm gem CMA helper Sascha Hauer
2012-06-27 13:20 ` Laurent Pinchart
2012-06-27 13:43 ` Sascha Hauer
-- strict thread matches above, loose matches on Subject: below --
2012-05-29 14:10 Sascha Hauer
2012-05-29 14:46 ` Lars-Peter Clausen
2012-05-29 15:26 ` Lars-Peter Clausen
2012-05-29 16:06 ` Sascha Hauer
2012-05-30 15:40 ` Laurent Pinchart
2012-05-30 16:28 ` Sascha Hauer
2012-05-31 9:41 ` Laurent Pinchart
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.