From f597c2089da4dd55133546b1255493579a295bff Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Thu, 14 Nov 2019 13:51:06 +0100 Subject: fbdev: Unexport unlink_framebuffer() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are no external callers of unlink_framebuffer() left. Make the function an internal interface. Signed-off-by: Thomas Zimmermann Reviewed-by: Noralf Trønnes Link: https://patchwork.freedesktop.org/patch/msgid/20191114125106.28347-4-tzimmermann@suse.de --- include/linux/fb.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/fb.h b/include/linux/fb.h index 41e0069eca0a..a6ad528990de 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -606,7 +606,6 @@ extern ssize_t fb_sys_write(struct fb_info *info, const char __user *buf, /* drivers/video/fbmem.c */ extern int register_framebuffer(struct fb_info *fb_info); extern void unregister_framebuffer(struct fb_info *fb_info); -extern void unlink_framebuffer(struct fb_info *fb_info); extern int remove_conflicting_pci_framebuffers(struct pci_dev *pdev, const char *name); extern int remove_conflicting_framebuffers(struct apertures_struct *a, -- cgit v1.2.3 From 35bd71dd1c161e6e89b21138e01e8c04c6347716 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 18 Nov 2019 11:35:23 +0100 Subject: drm/tegra: Delete host1x_bo_ops->k(un)map It doesn't have any callers anymore. Aside: The ->mmap/munmap hooks have a bit a confusing name, they don't do userspace mmaps, but a kernel vmap. I think most places use vmap for this, except ttm, which uses kmap for vmap for added confusion. mmap seems entirely for userspace mappings set up through mmap(2) syscall. Reviewed-by: Thierry Reding Tested-by: Thierry Reding Acked-by: Sumit Semwal Signed-off-by: Daniel Vetter Cc: Thierry Reding Cc: Jonathan Hunter Cc: linux-tegra@vger.kernel.org Link: https://patchwork.freedesktop.org/patch/msgid/20191118103536.17675-3-daniel.vetter@ffwll.ch --- drivers/gpu/drm/tegra/gem.c | 28 ---------------------------- include/linux/host1x.h | 13 ------------- 2 files changed, 41 deletions(-) (limited to 'include/linux') diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c index fb7667c8dd4c..0c55df20e0ef 100644 --- a/drivers/gpu/drm/tegra/gem.c +++ b/drivers/gpu/drm/tegra/gem.c @@ -65,32 +65,6 @@ static void tegra_bo_munmap(struct host1x_bo *bo, void *addr) vunmap(addr); } -static void *tegra_bo_kmap(struct host1x_bo *bo, unsigned int page) -{ - struct tegra_bo *obj = host1x_to_tegra_bo(bo); - - if (obj->vaddr) - return obj->vaddr + page * PAGE_SIZE; - else if (obj->gem.import_attach) - return dma_buf_kmap(obj->gem.import_attach->dmabuf, page); - else - return vmap(obj->pages + page, 1, VM_MAP, - pgprot_writecombine(PAGE_KERNEL)); -} - -static void tegra_bo_kunmap(struct host1x_bo *bo, unsigned int page, - void *addr) -{ - struct tegra_bo *obj = host1x_to_tegra_bo(bo); - - if (obj->vaddr) - return; - else if (obj->gem.import_attach) - dma_buf_kunmap(obj->gem.import_attach->dmabuf, page, addr); - else - vunmap(addr); -} - static struct host1x_bo *tegra_bo_get(struct host1x_bo *bo) { struct tegra_bo *obj = host1x_to_tegra_bo(bo); @@ -107,8 +81,6 @@ static const struct host1x_bo_ops tegra_bo_ops = { .unpin = tegra_bo_unpin, .mmap = tegra_bo_mmap, .munmap = tegra_bo_munmap, - .kmap = tegra_bo_kmap, - .kunmap = tegra_bo_kunmap, }; static int tegra_bo_iommu_map(struct tegra_drm *tegra, struct tegra_bo *bo) diff --git a/include/linux/host1x.h b/include/linux/host1x.h index e6eea45e1154..d11e89db7d7f 100644 --- a/include/linux/host1x.h +++ b/include/linux/host1x.h @@ -68,8 +68,6 @@ struct host1x_bo_ops { void (*unpin)(struct host1x_bo *bo, struct sg_table *sgt); void *(*mmap)(struct host1x_bo *bo); void (*munmap)(struct host1x_bo *bo, void *addr); - void *(*kmap)(struct host1x_bo *bo, unsigned int pagenum); - void (*kunmap)(struct host1x_bo *bo, unsigned int pagenum, void *addr); }; struct host1x_bo { @@ -113,17 +111,6 @@ static inline void host1x_bo_munmap(struct host1x_bo *bo, void *addr) bo->ops->munmap(bo, addr); } -static inline void *host1x_bo_kmap(struct host1x_bo *bo, unsigned int pagenum) -{ - return bo->ops->kmap(bo, pagenum); -} - -static inline void host1x_bo_kunmap(struct host1x_bo *bo, - unsigned int pagenum, void *addr) -{ - bo->ops->kunmap(bo, pagenum, addr); -} - /* * host1x syncpoints */ -- cgit v1.2.3 From 7f0de8d80816d9620e995cf98acf4b6cd2d7c230 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 18 Nov 2019 11:35:30 +0100 Subject: dma-buf: Drop dma_buf_k(un)map It's unused. 10 years ago, back when 32bit was still fairly common and trying to not exhaust vmalloc space sounded like a worthwhile goal, adding these to dma_buf made sense. Reality is that they simply never caught on, and nowadays everyone who needs plenty of buffers will run in 64bit mode anyway. Also update the docs in this area to adjust them to reality. The actual hooks in dma_buf_ops will be removed once all the implementations are gone. Acked-by: Sumit Semwal Signed-off-by: Daniel Vetter Cc: Sumit Semwal Cc: linux-media@vger.kernel.org Cc: linaro-mm-sig@lists.linaro.org Link: https://patchwork.freedesktop.org/patch/msgid/20191118103536.17675-10-daniel.vetter@ffwll.ch --- drivers/dma-buf/dma-buf.c | 63 +++-------------------------------------------- include/linux/dma-buf.h | 2 -- 2 files changed, 3 insertions(+), 62 deletions(-) (limited to 'include/linux') diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index d377b4ca66bf..97988ce1d2dc 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -880,29 +880,9 @@ EXPORT_SYMBOL_GPL(dma_buf_unmap_attachment); * with calls to dma_buf_begin_cpu_access() and dma_buf_end_cpu_access() * access. * - * To support dma_buf objects residing in highmem cpu access is page-based - * using an api similar to kmap. Accessing a dma_buf is done in aligned chunks - * of PAGE_SIZE size. Before accessing a chunk it needs to be mapped, which - * returns a pointer in kernel virtual address space. Afterwards the chunk - * needs to be unmapped again. There is no limit on how often a given chunk - * can be mapped and unmapped, i.e. the importer does not need to call - * begin_cpu_access again before mapping the same chunk again. - * - * Interfaces:: - * void \*dma_buf_kmap(struct dma_buf \*, unsigned long); - * void dma_buf_kunmap(struct dma_buf \*, unsigned long, void \*); - * - * Implementing the functions is optional for exporters and for importers all - * the restrictions of using kmap apply. - * - * dma_buf kmap calls outside of the range specified in begin_cpu_access are - * undefined. If the range is not PAGE_SIZE aligned, kmap needs to succeed on - * the partial chunks at the beginning and end but may return stale or bogus - * data outside of the range (in these partial chunks). - * - * For some cases the overhead of kmap can be too high, a vmap interface - * is introduced. This interface should be used very carefully, as vmalloc - * space is a limited resources on many architectures. + * Since for most kernel internal dma-buf accesses need the entire buffer, a + * vmap interface is introduced. Note that on very old 32-bit architectures + * vmalloc space might be limited and result in vmap calls failing. * * Interfaces:: * void \*dma_buf_vmap(struct dma_buf \*dmabuf) @@ -1052,43 +1032,6 @@ int dma_buf_end_cpu_access(struct dma_buf *dmabuf, } EXPORT_SYMBOL_GPL(dma_buf_end_cpu_access); -/** - * dma_buf_kmap - Map a page of the buffer object into kernel address space. The - * same restrictions as for kmap and friends apply. - * @dmabuf: [in] buffer to map page from. - * @page_num: [in] page in PAGE_SIZE units to map. - * - * This call must always succeed, any necessary preparations that might fail - * need to be done in begin_cpu_access. - */ -void *dma_buf_kmap(struct dma_buf *dmabuf, unsigned long page_num) -{ - WARN_ON(!dmabuf); - - if (!dmabuf->ops->map) - return NULL; - return dmabuf->ops->map(dmabuf, page_num); -} -EXPORT_SYMBOL_GPL(dma_buf_kmap); - -/** - * dma_buf_kunmap - Unmap a page obtained by dma_buf_kmap. - * @dmabuf: [in] buffer to unmap page from. - * @page_num: [in] page in PAGE_SIZE units to unmap. - * @vaddr: [in] kernel space pointer obtained from dma_buf_kmap. - * - * This call must always succeed. - */ -void dma_buf_kunmap(struct dma_buf *dmabuf, unsigned long page_num, - void *vaddr) -{ - WARN_ON(!dmabuf); - - if (dmabuf->ops->unmap) - dmabuf->ops->unmap(dmabuf, page_num, vaddr); -} -EXPORT_SYMBOL_GPL(dma_buf_kunmap); - /** * dma_buf_mmap - Setup up a userspace mmap with the given vma diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h index af73f835c51c..7feb9c3805ae 100644 --- a/include/linux/dma-buf.h +++ b/include/linux/dma-buf.h @@ -464,8 +464,6 @@ int dma_buf_begin_cpu_access(struct dma_buf *dma_buf, enum dma_data_direction dir); int dma_buf_end_cpu_access(struct dma_buf *dma_buf, enum dma_data_direction dir); -void *dma_buf_kmap(struct dma_buf *, unsigned long); -void dma_buf_kunmap(struct dma_buf *, unsigned long, void *); int dma_buf_mmap(struct dma_buf *, struct vm_area_struct *, unsigned long); -- cgit v1.2.3 From 4337ebbbbda3fb82e4fd928188a86e0bff0e9042 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 18 Nov 2019 11:35:36 +0100 Subject: dma-buf: Remove kernel map/unmap hooks All implementations are gone now. Acked-by: Sumit Semwal Signed-off-by: Daniel Vetter Cc: Sumit Semwal Cc: linux-media@vger.kernel.org Cc: linaro-mm-sig@lists.linaro.org Link: https://patchwork.freedesktop.org/patch/msgid/20191118103536.17675-16-daniel.vetter@ffwll.ch --- include/linux/dma-buf.h | 25 ------------------------- 1 file changed, 25 deletions(-) (limited to 'include/linux') diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h index 7feb9c3805ae..abf5459a5b9d 100644 --- a/include/linux/dma-buf.h +++ b/include/linux/dma-buf.h @@ -249,31 +249,6 @@ struct dma_buf_ops { */ int (*mmap)(struct dma_buf *, struct vm_area_struct *vma); - /** - * @map: - * - * Maps a page from the buffer into kernel address space. The page is - * specified by offset into the buffer in PAGE_SIZE units. - * - * This callback is optional. - * - * Returns: - * - * Virtual address pointer where requested page can be accessed. NULL - * on error or when this function is unimplemented by the exporter. - */ - void *(*map)(struct dma_buf *, unsigned long); - - /** - * @unmap: - * - * Unmaps a page from the buffer. Page offset and address pointer should - * be the same as the one passed to and returned by matching call to map. - * - * This callback is optional. - */ - void (*unmap)(struct dma_buf *, unsigned long, void *); - void *(*vmap)(struct dma_buf *); void (*vunmap)(struct dma_buf *, void *vaddr); }; -- cgit v1.2.3 From bf9e25ec12877a622857460c2f542a6c31393250 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 3 Dec 2019 18:38:47 +0200 Subject: video: fbdev: make fbops member of struct fb_info a const pointer Now that we no longer modify the fbops, or hold non-const pointers to it, we can make it const. After this, we can start making the fbops const all over the place. Cc: linux-fbdev@vger.kernel.org Reviewed-by: Daniel Vetter Signed-off-by: Jani Nikula Link: https://patchwork.freedesktop.org/patch/msgid/700c6b52c39c6e7babaa921f583eac354714d9fc.1575390740.git.jani.nikula@intel.com --- include/linux/fb.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/fb.h b/include/linux/fb.h index a6ad528990de..6557fabdea62 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -472,7 +472,7 @@ struct fb_info { struct fb_deferred_io *fbdefio; #endif - struct fb_ops *fbops; + const struct fb_ops *fbops; struct device *device; /* This is the parent */ struct device *dev; /* This is this fb device */ int class_flag; /* private sysfs flags */ -- cgit v1.2.3 From c02a81fba74fe3488ad6b08bfb5a1329005418f8 Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Tue, 3 Dec 2019 17:26:37 +0000 Subject: dma-buf: Add dma-buf heaps framework This framework allows a unified userspace interface for dma-buf exporters, allowing userland to allocate specific types of memory for use in dma-buf sharing. Each heap is given its own device node, which a user can allocate a dma-buf fd from using the DMA_HEAP_IOC_ALLOC. This code is an evoluiton of the Android ION implementation, and a big thanks is due to its authors/maintainers over time for their effort: Rebecca Schultz Zavin, Colin Cross, Benjamin Gaignard, Laura Abbott, and many other contributors! Cc: Laura Abbott Cc: Benjamin Gaignard Cc: Sumit Semwal Cc: Liam Mark Cc: Pratik Patel Cc: Brian Starkey Cc: Vincent Donnefort Cc: Sudipto Paul Cc: Andrew F. Davis Cc: Christoph Hellwig Cc: Chenbo Feng Cc: Alistair Strachan Cc: Hridya Valsaraju Cc: Sandeep Patil Cc: Hillf Danton Cc: Dave Airlie Cc: dri-devel@lists.freedesktop.org Reviewed-by: Brian Starkey Acked-by: Sandeep Patil Signed-off-by: Andrew F. Davis Signed-off-by: John Stultz Signed-off-by: Sumit Semwal Link: https://patchwork.freedesktop.org/patch/msgid/20191203172641.66642-2-john.stultz@linaro.org --- MAINTAINERS | 18 +++ drivers/dma-buf/Kconfig | 9 ++ drivers/dma-buf/Makefile | 1 + drivers/dma-buf/dma-heap.c | 297 ++++++++++++++++++++++++++++++++++++++++++ include/linux/dma-heap.h | 59 +++++++++ include/uapi/linux/dma-heap.h | 53 ++++++++ 6 files changed, 437 insertions(+) create mode 100644 drivers/dma-buf/dma-heap.c create mode 100644 include/linux/dma-heap.h create mode 100644 include/uapi/linux/dma-heap.h (limited to 'include/linux') diff --git a/MAINTAINERS b/MAINTAINERS index c2b89453805f..248938405c01 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4942,6 +4942,24 @@ F: include/linux/*fence.h F: Documentation/driver-api/dma-buf.rst T: git git://anongit.freedesktop.org/drm/drm-misc +DMA-BUF HEAPS FRAMEWORK +M: Sumit Semwal +R: Andrew F. Davis +R: Benjamin Gaignard +R: Liam Mark +R: Laura Abbott +R: Brian Starkey +R: John Stultz +S: Maintained +L: linux-media@vger.kernel.org +L: dri-devel@lists.freedesktop.org +L: linaro-mm-sig@lists.linaro.org (moderated for non-subscribers) +F: include/uapi/linux/dma-heap.h +F: include/linux/dma-heap.h +F: drivers/dma-buf/dma-heap.c +F: drivers/dma-buf/heaps/* +T: git git://anongit.freedesktop.org/drm/drm-misc + DMA GENERIC OFFLOAD ENGINE SUBSYSTEM M: Vinod Koul L: dmaengine@vger.kernel.org diff --git a/drivers/dma-buf/Kconfig b/drivers/dma-buf/Kconfig index a23b6752d11a..bffa58fc3e6e 100644 --- a/drivers/dma-buf/Kconfig +++ b/drivers/dma-buf/Kconfig @@ -44,4 +44,13 @@ config DMABUF_SELFTESTS default n depends on DMA_SHARED_BUFFER +menuconfig DMABUF_HEAPS + bool "DMA-BUF Userland Memory Heaps" + select DMA_SHARED_BUFFER + help + Choose this option to enable the DMA-BUF userland memory heaps. + This options creates per heap chardevs in /dev/dma_heap/ which + allows userspace to allocate dma-bufs that can be shared + between drivers. + endmenu diff --git a/drivers/dma-buf/Makefile b/drivers/dma-buf/Makefile index 03479da06422..caee5eb3d351 100644 --- a/drivers/dma-buf/Makefile +++ b/drivers/dma-buf/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only obj-y := dma-buf.o dma-fence.o dma-fence-array.o dma-fence-chain.o \ dma-resv.o seqno-fence.o +obj-$(CONFIG_DMABUF_HEAPS) += dma-heap.o obj-$(CONFIG_SYNC_FILE) += sync_file.o obj-$(CONFIG_SW_SYNC) += sw_sync.o sync_debug.o obj-$(CONFIG_UDMABUF) += udmabuf.o diff --git a/drivers/dma-buf/dma-heap.c b/drivers/dma-buf/dma-heap.c new file mode 100644 index 000000000000..4f04d104ae61 --- /dev/null +++ b/drivers/dma-buf/dma-heap.c @@ -0,0 +1,297 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Framework for userspace DMA-BUF allocations + * + * Copyright (C) 2011 Google, Inc. + * Copyright (C) 2019 Linaro Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEVNAME "dma_heap" + +#define NUM_HEAP_MINORS 128 + +/** + * struct dma_heap - represents a dmabuf heap in the system + * @name: used for debugging/device-node name + * @ops: ops struct for this heap + * @heap_devt heap device node + * @list list head connecting to list of heaps + * @heap_cdev heap char device + * + * Represents a heap of memory from which buffers can be made. + */ +struct dma_heap { + const char *name; + const struct dma_heap_ops *ops; + void *priv; + dev_t heap_devt; + struct list_head list; + struct cdev heap_cdev; +}; + +static LIST_HEAD(heap_list); +static DEFINE_MUTEX(heap_list_lock); +static dev_t dma_heap_devt; +static struct class *dma_heap_class; +static DEFINE_XARRAY_ALLOC(dma_heap_minors); + +static int dma_heap_buffer_alloc(struct dma_heap *heap, size_t len, + unsigned int fd_flags, + unsigned int heap_flags) +{ + /* + * Allocations from all heaps have to begin + * and end on page boundaries. + */ + len = PAGE_ALIGN(len); + if (!len) + return -EINVAL; + + return heap->ops->allocate(heap, len, fd_flags, heap_flags); +} + +static int dma_heap_open(struct inode *inode, struct file *file) +{ + struct dma_heap *heap; + + heap = xa_load(&dma_heap_minors, iminor(inode)); + if (!heap) { + pr_err("dma_heap: minor %d unknown.\n", iminor(inode)); + return -ENODEV; + } + + /* instance data as context */ + file->private_data = heap; + nonseekable_open(inode, file); + + return 0; +} + +static long dma_heap_ioctl_allocate(struct file *file, void *data) +{ + struct dma_heap_allocation_data *heap_allocation = data; + struct dma_heap *heap = file->private_data; + int fd; + + if (heap_allocation->fd) + return -EINVAL; + + if (heap_allocation->fd_flags & ~DMA_HEAP_VALID_FD_FLAGS) + return -EINVAL; + + if (heap_allocation->heap_flags & ~DMA_HEAP_VALID_HEAP_FLAGS) + return -EINVAL; + + fd = dma_heap_buffer_alloc(heap, heap_allocation->len, + heap_allocation->fd_flags, + heap_allocation->heap_flags); + if (fd < 0) + return fd; + + heap_allocation->fd = fd; + + return 0; +} + +unsigned int dma_heap_ioctl_cmds[] = { + DMA_HEAP_IOC_ALLOC, +}; + +static long dma_heap_ioctl(struct file *file, unsigned int ucmd, + unsigned long arg) +{ + char stack_kdata[128]; + char *kdata = stack_kdata; + unsigned int kcmd; + unsigned int in_size, out_size, drv_size, ksize; + int nr = _IOC_NR(ucmd); + int ret = 0; + + if (nr >= ARRAY_SIZE(dma_heap_ioctl_cmds)) + return -EINVAL; + + /* Get the kernel ioctl cmd that matches */ + kcmd = dma_heap_ioctl_cmds[nr]; + + /* Figure out the delta between user cmd size and kernel cmd size */ + drv_size = _IOC_SIZE(kcmd); + out_size = _IOC_SIZE(ucmd); + in_size = out_size; + if ((ucmd & kcmd & IOC_IN) == 0) + in_size = 0; + if ((ucmd & kcmd & IOC_OUT) == 0) + out_size = 0; + ksize = max(max(in_size, out_size), drv_size); + + /* If necessary, allocate buffer for ioctl argument */ + if (ksize > sizeof(stack_kdata)) { + kdata = kmalloc(ksize, GFP_KERNEL); + if (!kdata) + return -ENOMEM; + } + + if (copy_from_user(kdata, (void __user *)arg, in_size) != 0) { + ret = -EFAULT; + goto err; + } + + /* zero out any difference between the kernel/user structure size */ + if (ksize > in_size) + memset(kdata + in_size, 0, ksize - in_size); + + switch (kcmd) { + case DMA_HEAP_IOC_ALLOC: + ret = dma_heap_ioctl_allocate(file, kdata); + break; + default: + return -ENOTTY; + } + + if (copy_to_user((void __user *)arg, kdata, out_size) != 0) + ret = -EFAULT; +err: + if (kdata != stack_kdata) + kfree(kdata); + return ret; +} + +static const struct file_operations dma_heap_fops = { + .owner = THIS_MODULE, + .open = dma_heap_open, + .unlocked_ioctl = dma_heap_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = dma_heap_ioctl, +#endif +}; + +/** + * dma_heap_get_drvdata() - get per-subdriver data for the heap + * @heap: DMA-Heap to retrieve private data for + * + * Returns: + * The per-subdriver data for the heap. + */ +void *dma_heap_get_drvdata(struct dma_heap *heap) +{ + return heap->priv; +} + +struct dma_heap *dma_heap_add(const struct dma_heap_export_info *exp_info) +{ + struct dma_heap *heap, *h, *err_ret; + struct device *dev_ret; + unsigned int minor; + int ret; + + if (!exp_info->name || !strcmp(exp_info->name, "")) { + pr_err("dma_heap: Cannot add heap without a name\n"); + return ERR_PTR(-EINVAL); + } + + if (!exp_info->ops || !exp_info->ops->allocate) { + pr_err("dma_heap: Cannot add heap with invalid ops struct\n"); + return ERR_PTR(-EINVAL); + } + + /* check the name is unique */ + mutex_lock(&heap_list_lock); + list_for_each_entry(h, &heap_list, list) { + if (!strcmp(h->name, exp_info->name)) { + mutex_unlock(&heap_list_lock); + pr_err("dma_heap: Already registered heap named %s\n", + exp_info->name); + return ERR_PTR(-EINVAL); + } + } + mutex_unlock(&heap_list_lock); + + heap = kzalloc(sizeof(*heap), GFP_KERNEL); + if (!heap) + return ERR_PTR(-ENOMEM); + + heap->name = exp_info->name; + heap->ops = exp_info->ops; + heap->priv = exp_info->priv; + + /* Find unused minor number */ + ret = xa_alloc(&dma_heap_minors, &minor, heap, + XA_LIMIT(0, NUM_HEAP_MINORS - 1), GFP_KERNEL); + if (ret < 0) { + pr_err("dma_heap: Unable to get minor number for heap\n"); + err_ret = ERR_PTR(ret); + goto err0; + } + + /* Create device */ + heap->heap_devt = MKDEV(MAJOR(dma_heap_devt), minor); + + cdev_init(&heap->heap_cdev, &dma_heap_fops); + ret = cdev_add(&heap->heap_cdev, heap->heap_devt, 1); + if (ret < 0) { + pr_err("dma_heap: Unable to add char device\n"); + err_ret = ERR_PTR(ret); + goto err1; + } + + dev_ret = device_create(dma_heap_class, + NULL, + heap->heap_devt, + NULL, + heap->name); + if (IS_ERR(dev_ret)) { + pr_err("dma_heap: Unable to create device\n"); + err_ret = ERR_CAST(dev_ret); + goto err2; + } + /* Add heap to the list */ + mutex_lock(&heap_list_lock); + list_add(&heap->list, &heap_list); + mutex_unlock(&heap_list_lock); + + return heap; + +err2: + cdev_del(&heap->heap_cdev); +err1: + xa_erase(&dma_heap_minors, minor); +err0: + kfree(heap); + return err_ret; +} + +static char *dma_heap_devnode(struct device *dev, umode_t *mode) +{ + return kasprintf(GFP_KERNEL, "dma_heap/%s", dev_name(dev)); +} + +static int dma_heap_init(void) +{ + int ret; + + ret = alloc_chrdev_region(&dma_heap_devt, 0, NUM_HEAP_MINORS, DEVNAME); + if (ret) + return ret; + + dma_heap_class = class_create(THIS_MODULE, DEVNAME); + if (IS_ERR(dma_heap_class)) { + unregister_chrdev_region(dma_heap_devt, NUM_HEAP_MINORS); + return PTR_ERR(dma_heap_class); + } + dma_heap_class->devnode = dma_heap_devnode; + + return 0; +} +subsys_initcall(dma_heap_init); diff --git a/include/linux/dma-heap.h b/include/linux/dma-heap.h new file mode 100644 index 000000000000..454e354d1ffb --- /dev/null +++ b/include/linux/dma-heap.h @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * DMABUF Heaps Allocation Infrastructure + * + * Copyright (C) 2011 Google, Inc. + * Copyright (C) 2019 Linaro Ltd. + */ + +#ifndef _DMA_HEAPS_H +#define _DMA_HEAPS_H + +#include +#include + +struct dma_heap; + +/** + * struct dma_heap_ops - ops to operate on a given heap + * @allocate: allocate dmabuf and return fd + * + * allocate returns dmabuf fd on success, -errno on error. + */ +struct dma_heap_ops { + int (*allocate)(struct dma_heap *heap, + unsigned long len, + unsigned long fd_flags, + unsigned long heap_flags); +}; + +/** + * struct dma_heap_export_info - information needed to export a new dmabuf heap + * @name: used for debugging/device-node name + * @ops: ops struct for this heap + * @priv: heap exporter private data + * + * Information needed to export a new dmabuf heap. + */ +struct dma_heap_export_info { + const char *name; + const struct dma_heap_ops *ops; + void *priv; +}; + +/** + * dma_heap_get_drvdata() - get per-heap driver data + * @heap: DMA-Heap to retrieve private data for + * + * Returns: + * The per-heap data for the heap. + */ +void *dma_heap_get_drvdata(struct dma_heap *heap); + +/** + * dma_heap_add - adds a heap to dmabuf heaps + * @exp_info: information needed to register this heap + */ +struct dma_heap *dma_heap_add(const struct dma_heap_export_info *exp_info); + +#endif /* _DMA_HEAPS_H */ diff --git a/include/uapi/linux/dma-heap.h b/include/uapi/linux/dma-heap.h new file mode 100644 index 000000000000..73e7f66c1cae --- /dev/null +++ b/include/uapi/linux/dma-heap.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * DMABUF Heaps Userspace API + * + * Copyright (C) 2011 Google, Inc. + * Copyright (C) 2019 Linaro Ltd. + */ +#ifndef _UAPI_LINUX_DMABUF_POOL_H +#define _UAPI_LINUX_DMABUF_POOL_H + +#include +#include + +/** + * DOC: DMABUF Heaps Userspace API + */ + +/* Valid FD_FLAGS are O_CLOEXEC, O_RDONLY, O_WRONLY, O_RDWR */ +#define DMA_HEAP_VALID_FD_FLAGS (O_CLOEXEC | O_ACCMODE) + +/* Currently no heap flags */ +#define DMA_HEAP_VALID_HEAP_FLAGS (0) + +/** + * struct dma_heap_allocation_data - metadata passed from userspace for + * allocations + * @len: size of the allocation + * @fd: will be populated with a fd which provides the + * handle to the allocated dma-buf + * @fd_flags: file descriptor flags used when allocating + * @heap_flags: flags passed to heap + * + * Provided by userspace as an argument to the ioctl + */ +struct dma_heap_allocation_data { + __u64 len; + __u32 fd; + __u32 fd_flags; + __u64 heap_flags; +}; + +#define DMA_HEAP_IOC_MAGIC 'H' + +/** + * DOC: DMA_HEAP_IOC_ALLOC - allocate memory from pool + * + * Takes a dma_heap_allocation_data struct and returns it with the fd field + * populated with the dmabuf handle of the allocation. + */ +#define DMA_HEAP_IOC_ALLOC _IOWR(DMA_HEAP_IOC_MAGIC, 0x0,\ + struct dma_heap_allocation_data) + +#endif /* _UAPI_LINUX_DMABUF_POOL_H */ -- cgit v1.2.3 From cd6a1ca38698b0c8ff8afe5074fe53aff3647973 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 6 Dec 2019 10:43:01 +0100 Subject: drm/gma500: Pass GPIO for Intel MID using descriptors The GMA500 driver is using the legacy GPIO API to fetch three optional display control GPIO lines from the SFI description used by the Medfield platform. Switch this over to use GPIO descriptors and delete the custom platform data. We create three new static locals in the tc35876x bridge code but it is hardly any worse than the I2C client static local already there: I tried first to move it to the DRM driver state container but there are workarounds for probe order in the code so I just stayed off it, as the result is unpredictable. People wanting to do a more throrugh and proper cleanup of the GMA500 driver can work on top of this, I can't solve much more since I don't have access to the hardware, I can only attempt to tidy up my GPIO corner. Cc: Daniel Stone Cc: Daniel Vetter Reviewed-by: Andy Shevchenko Acked-by: Patrik Jakobsson Link: https://patchwork.freedesktop.org/patch/msgid/20191206094301.76368-1-linus.walleij@linaro.org Signed-off-by: Linus Walleij --- .../intel-mid/device_libs/platform_tc35876x.c | 26 +++++-- drivers/gpu/drm/gma500/tc35876x-dsi-lvds.c | 88 ++++++++-------------- include/linux/platform_data/tc35876x.h | 11 --- 3 files changed, 51 insertions(+), 74 deletions(-) delete mode 100644 include/linux/platform_data/tc35876x.h (limited to 'include/linux') diff --git a/arch/x86/platform/intel-mid/device_libs/platform_tc35876x.c b/arch/x86/platform/intel-mid/device_libs/platform_tc35876x.c index 44d1f884c3d3..139738bbdd36 100644 --- a/arch/x86/platform/intel-mid/device_libs/platform_tc35876x.c +++ b/arch/x86/platform/intel-mid/device_libs/platform_tc35876x.c @@ -6,21 +6,31 @@ * Author: Sathyanarayanan Kuppuswamy */ -#include -#include +#include #include +static struct gpiod_lookup_table tc35876x_gpio_table = { + .dev_id = "i2c_disp_brig", + .table = { + GPIO_LOOKUP("0000:00:0c.0", -1, "bridge-reset", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("0000:00:0c.0", -1, "bl-en", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("0000:00:0c.0", -1, "vadd", GPIO_ACTIVE_HIGH), + { }, + }, +}; + /*tc35876x DSI_LVDS bridge chip and panel platform data*/ static void *tc35876x_platform_data(void *data) { - static struct tc35876x_platform_data pdata; + struct gpiod_lookup_table *table = &tc35876x_gpio_table; + struct gpiod_lookup *lookup = table->table; - /* gpio pins set to -1 will not be used by the driver */ - pdata.gpio_bridge_reset = get_gpio_by_name("LCMB_RXEN"); - pdata.gpio_panel_bl_en = get_gpio_by_name("6S6P_BL_EN"); - pdata.gpio_panel_vadd = get_gpio_by_name("EN_VREG_LCD_V3P3"); + lookup[0].chip_hwnum = get_gpio_by_name("LCMB_RXEN"); + lookup[1].chip_hwnum = get_gpio_by_name("6S6P_BL_EN"); + lookup[2].chip_hwnum = get_gpio_by_name("EN_VREG_LCD_V3P3"); + gpiod_add_lookup_table(table); - return &pdata; + return NULL; } static const struct devs_id tc35876x_dev_id __initconst = { diff --git a/drivers/gpu/drm/gma500/tc35876x-dsi-lvds.c b/drivers/gpu/drm/gma500/tc35876x-dsi-lvds.c index 7de3ce637c7f..9e8224456ea2 100644 --- a/drivers/gpu/drm/gma500/tc35876x-dsi-lvds.c +++ b/drivers/gpu/drm/gma500/tc35876x-dsi-lvds.c @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include @@ -36,6 +36,11 @@ static struct i2c_client *tc35876x_client; static struct i2c_client *cmi_lcd_i2c_client; +/* Panel GPIOs */ +static struct gpio_desc *bridge_reset; +static struct gpio_desc *bridge_bl_enable; +static struct gpio_desc *backlight_voltage; + #define FLD_MASK(start, end) (((1 << ((start) - (end) + 1)) - 1) << (end)) #define FLD_VAL(val, start, end) (((val) << (end)) & FLD_MASK(start, end)) @@ -316,27 +321,23 @@ static int tc35876x_regr(struct i2c_client *client, u16 reg, u32 *value) void tc35876x_set_bridge_reset_state(struct drm_device *dev, int state) { - struct tc35876x_platform_data *pdata; - if (WARN(!tc35876x_client, "%s called before probe", __func__)) return; dev_dbg(&tc35876x_client->dev, "%s: state %d\n", __func__, state); - pdata = dev_get_platdata(&tc35876x_client->dev); - - if (pdata->gpio_bridge_reset == -1) + if (!bridge_reset) return; if (state) { - gpio_set_value_cansleep(pdata->gpio_bridge_reset, 0); + gpiod_set_value_cansleep(bridge_reset, 0); mdelay(10); } else { /* Pull MIPI Bridge reset pin to Low */ - gpio_set_value_cansleep(pdata->gpio_bridge_reset, 0); + gpiod_set_value_cansleep(bridge_reset, 0); mdelay(20); /* Pull MIPI Bridge reset pin to High */ - gpio_set_value_cansleep(pdata->gpio_bridge_reset, 1); + gpiod_set_value_cansleep(bridge_reset, 1); mdelay(40); } } @@ -510,25 +511,20 @@ void tc35876x_brightness_control(struct drm_device *dev, int level) void tc35876x_toshiba_bridge_panel_off(struct drm_device *dev) { - struct tc35876x_platform_data *pdata; - if (WARN(!tc35876x_client, "%s called before probe", __func__)) return; dev_dbg(&tc35876x_client->dev, "%s\n", __func__); - pdata = dev_get_platdata(&tc35876x_client->dev); - - if (pdata->gpio_panel_bl_en != -1) - gpio_set_value_cansleep(pdata->gpio_panel_bl_en, 0); + if (bridge_bl_enable) + gpiod_set_value_cansleep(bridge_bl_enable, 0); - if (pdata->gpio_panel_vadd != -1) - gpio_set_value_cansleep(pdata->gpio_panel_vadd, 0); + if (backlight_voltage) + gpiod_set_value_cansleep(backlight_voltage, 0); } void tc35876x_toshiba_bridge_panel_on(struct drm_device *dev) { - struct tc35876x_platform_data *pdata; struct drm_psb_private *dev_priv = dev->dev_private; if (WARN(!tc35876x_client, "%s called before probe", __func__)) @@ -536,10 +532,8 @@ void tc35876x_toshiba_bridge_panel_on(struct drm_device *dev) dev_dbg(&tc35876x_client->dev, "%s\n", __func__); - pdata = dev_get_platdata(&tc35876x_client->dev); - - if (pdata->gpio_panel_vadd != -1) { - gpio_set_value_cansleep(pdata->gpio_panel_vadd, 1); + if (backlight_voltage) { + gpiod_set_value_cansleep(backlight_voltage, 1); msleep(260); } @@ -571,8 +565,8 @@ void tc35876x_toshiba_bridge_panel_on(struct drm_device *dev) "i2c write failed (%d)\n", ret); } - if (pdata->gpio_panel_bl_en != -1) - gpio_set_value_cansleep(pdata->gpio_panel_bl_en, 1); + if (bridge_bl_enable) + gpiod_set_value_cansleep(bridge_bl_enable, 1); tc35876x_brightness_control(dev, dev_priv->brightness_adjusted); } @@ -635,8 +629,6 @@ static int tc35876x_get_panel_info(struct drm_device *dev, int pipe, static int tc35876x_bridge_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct tc35876x_platform_data *pdata; - dev_info(&client->dev, "%s\n", __func__); if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { @@ -645,26 +637,23 @@ static int tc35876x_bridge_probe(struct i2c_client *client, return -ENODEV; } - pdata = dev_get_platdata(&client->dev); - if (!pdata) { - dev_err(&client->dev, "%s: no platform data\n", __func__); - return -ENODEV; - } + bridge_reset = devm_gpiod_get_optional(&client->dev, "bridge-reset", GPIOD_OUT_LOW); + if (IS_ERR(bridge_reset)) + return PTR_ERR(bridge_reset); + if (bridge_reset) + gpiod_set_consumer_name(bridge_reset, "tc35876x bridge reset"); - if (pdata->gpio_bridge_reset != -1) { - gpio_request(pdata->gpio_bridge_reset, "tc35876x bridge reset"); - gpio_direction_output(pdata->gpio_bridge_reset, 0); - } - - if (pdata->gpio_panel_bl_en != -1) { - gpio_request(pdata->gpio_panel_bl_en, "tc35876x panel bl en"); - gpio_direction_output(pdata->gpio_panel_bl_en, 0); - } + bridge_bl_enable = devm_gpiod_get_optional(&client->dev, "bl-en", GPIOD_OUT_LOW); + if (IS_ERR(bridge_bl_enable)) + return PTR_ERR(bridge_bl_enable); + if (bridge_bl_enable) + gpiod_set_consumer_name(bridge_bl_enable, "tc35876x panel bl en"); - if (pdata->gpio_panel_vadd != -1) { - gpio_request(pdata->gpio_panel_vadd, "tc35876x panel vadd"); - gpio_direction_output(pdata->gpio_panel_vadd, 0); - } + backlight_voltage = devm_gpiod_get_optional(&client->dev, "vadd", GPIOD_OUT_LOW); + if (IS_ERR(backlight_voltage)) + return PTR_ERR(backlight_voltage); + if (backlight_voltage) + gpiod_set_consumer_name(backlight_voltage, "tc35876x panel vadd"); tc35876x_client = client; @@ -673,19 +662,8 @@ static int tc35876x_bridge_probe(struct i2c_client *client, static int tc35876x_bridge_remove(struct i2c_client *client) { - struct tc35876x_platform_data *pdata = dev_get_platdata(&client->dev); - dev_dbg(&client->dev, "%s\n", __func__); - if (pdata->gpio_bridge_reset != -1) - gpio_free(pdata->gpio_bridge_reset); - - if (pdata->gpio_panel_bl_en != -1) - gpio_free(pdata->gpio_panel_bl_en); - - if (pdata->gpio_panel_vadd != -1) - gpio_free(pdata->gpio_panel_vadd); - tc35876x_client = NULL; return 0; diff --git a/include/linux/platform_data/tc35876x.h b/include/linux/platform_data/tc35876x.h deleted file mode 100644 index cd6a51c71e7e..000000000000 --- a/include/linux/platform_data/tc35876x.h +++ /dev/null @@ -1,11 +0,0 @@ - -#ifndef _TC35876X_H -#define _TC35876X_H - -struct tc35876x_platform_data { - int gpio_bridge_reset; - int gpio_panel_bl_en; - int gpio_panel_vadd; -}; - -#endif /* _TC35876X_H */ -- cgit v1.2.3