summaryrefslogtreecommitdiff
path: root/drivers/mxc/gpu-viv/hal/os/linux/kernel/allocator/default/gc_hal_kernel_allocator_dmabuf.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mxc/gpu-viv/hal/os/linux/kernel/allocator/default/gc_hal_kernel_allocator_dmabuf.c')
-rw-r--r--drivers/mxc/gpu-viv/hal/os/linux/kernel/allocator/default/gc_hal_kernel_allocator_dmabuf.c522
1 files changed, 522 insertions, 0 deletions
diff --git a/drivers/mxc/gpu-viv/hal/os/linux/kernel/allocator/default/gc_hal_kernel_allocator_dmabuf.c b/drivers/mxc/gpu-viv/hal/os/linux/kernel/allocator/default/gc_hal_kernel_allocator_dmabuf.c
new file mode 100644
index 000000000000..d07743be68ba
--- /dev/null
+++ b/drivers/mxc/gpu-viv/hal/os/linux/kernel/allocator/default/gc_hal_kernel_allocator_dmabuf.c
@@ -0,0 +1,522 @@
+/****************************************************************************
+*
+* The MIT License (MIT)
+*
+* Copyright (c) 2014 - 2018 Vivante Corporation
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in
+* all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+* DEALINGS IN THE SOFTWARE.
+*
+*****************************************************************************
+*
+* The GPL License (GPL)
+*
+* Copyright (C) 2014 - 2018 Vivante Corporation
+*
+* 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.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software Foundation,
+* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*
+*****************************************************************************
+*
+* Note: This software is released under dual MIT and GPL licenses. A
+* recipient may use this file under the terms of either the MIT license or
+* GPL License. If you wish to use only one license not the other, you can
+* indicate your decision by deleting one of the above license notices in your
+* version of this file.
+*
+*****************************************************************************/
+
+
+#include "gc_hal_kernel_linux.h"
+#include "gc_hal_kernel_allocator.h"
+
+#include <linux/pagemap.h>
+#include <linux/seq_file.h>
+#include <linux/mman.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <asm/atomic.h>
+#include <linux/dma-mapping.h>
+
+#include <linux/dma-buf.h>
+#include <linux/platform_device.h>
+
+#define _GC_OBJ_ZONE gcvZONE_OS
+
+/* Descriptor of a dma_buf imported. */
+typedef struct _gcsDMABUF
+{
+ struct dma_buf * dmabuf;
+ struct dma_buf_attachment * attachment;
+ struct sg_table * sgtable;
+ unsigned long * pagearray;
+
+ int npages;
+ int pid;
+ struct list_head list;
+}
+gcsDMABUF;
+
+struct allocator_priv
+{
+ struct mutex lock;
+ struct list_head buf_list;
+};
+
+/*
+* Debugfs support.
+*/
+static int dma_buf_info_show(struct seq_file* m, void* data)
+{
+ int ret;
+ gcsDMABUF *buf_desc;
+ struct dma_buf_attachment *attach_obj;
+ int count = 0;
+ size_t size = 0;
+ int npages = 0;
+ const char *exp_name;
+
+ gcsINFO_NODE *node = m->private;
+ gckALLOCATOR allocator = node->device;
+ struct allocator_priv *priv = allocator->privateData;
+
+ ret = mutex_lock_interruptible(&priv->lock);
+
+ if (ret)
+ return ret;
+
+ seq_puts(m, "Attached dma-buf objects:\n");
+ seq_puts(m, " pid fd pages size exporter attached-devices\n");
+
+ list_for_each_entry(buf_desc, &priv->buf_list, list) {
+ struct dma_buf *buf_obj = buf_desc->dmabuf;
+
+ ret = mutex_lock_interruptible(&buf_obj->lock);
+
+ if (ret) {
+ seq_puts(m,
+ "ERROR locking buffer object: skipping\n");
+ continue;
+ }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+ exp_name = buf_obj->exp_name;
+#else
+ exp_name = "unknown";
+#endif
+
+ seq_printf(m, "%6d %p %8d %8zu %10s",
+ buf_desc->pid,
+ buf_desc->dmabuf,
+ buf_desc->npages,
+ buf_obj->size,
+ exp_name);
+
+ list_for_each_entry(attach_obj, &buf_obj->attachments, node) {
+ seq_printf(m, " %s", dev_name(attach_obj->dev));
+ }
+ seq_puts(m, "\n");
+
+ count++;
+ size += buf_obj->size;
+ npages += buf_desc->npages;
+
+ mutex_unlock(&buf_obj->lock);
+ }
+
+ seq_printf(m, "\nTotal %d objects, %d pages, %zu bytes\n", count, npages, size);
+
+ mutex_unlock(&priv->lock);
+ return 0;
+}
+
+static gcsINFO _InfoList[] =
+{
+ {"bufinfo", dma_buf_info_show},
+};
+
+static void
+_DebugfsInit(
+ IN gckALLOCATOR Allocator,
+ IN gckDEBUGFS_DIR Root
+ )
+{
+ gcmkVERIFY_OK(
+ gckDEBUGFS_DIR_Init(&Allocator->debugfsDir, Root->root, "dma_buf"));
+
+ gcmkVERIFY_OK(gckDEBUGFS_DIR_CreateFiles(
+ &Allocator->debugfsDir,
+ _InfoList,
+ gcmCOUNTOF(_InfoList),
+ Allocator
+ ));
+}
+
+static void
+_DebugfsCleanup(
+ IN gckALLOCATOR Allocator
+ )
+{
+ gcmkVERIFY_OK(gckDEBUGFS_DIR_RemoveFiles(
+ &Allocator->debugfsDir,
+ _InfoList,
+ gcmCOUNTOF(_InfoList)
+ ));
+
+ gckDEBUGFS_DIR_Deinit(&Allocator->debugfsDir);
+}
+
+static gceSTATUS
+_DmabufAttach(
+ IN gckALLOCATOR Allocator,
+ IN gcsATTACH_DESC_PTR Desc,
+ IN PLINUX_MDL Mdl
+ )
+{
+ gceSTATUS status;
+
+ gckOS os = Allocator->os;
+
+ struct dma_buf *dmabuf = Desc->dmaBuf.dmabuf;
+ struct sg_table *sgt = NULL;
+ struct dma_buf_attachment *attachment = NULL;
+ int npages = 0;
+ unsigned long *pagearray = NULL;
+ int i, j, k = 0;
+ struct scatterlist *s;
+ struct allocator_priv *priv = Allocator->privateData;
+ gcsDMABUF *buf_desc = NULL;
+
+ gcmkHEADER();
+
+ gcmkVERIFY_OBJECT(os, gcvOBJ_OS);
+
+ if (!dmabuf)
+ {
+ gcmkONERROR(gcvSTATUS_NOT_SUPPORTED);
+ }
+
+ get_dma_buf(dmabuf);
+ attachment = dma_buf_attach(dmabuf, &os->device->platform->device->dev);
+
+ if (!attachment)
+ {
+ gcmkONERROR(gcvSTATUS_NOT_SUPPORTED);
+ }
+
+ sgt = dma_buf_map_attachment(attachment, DMA_BIDIRECTIONAL);
+
+ if (!sgt)
+ {
+ gcmkONERROR(gcvSTATUS_NOT_SUPPORTED);
+ }
+
+ /* Prepare page array. */
+ /* Get number of pages. */
+ for_each_sg(sgt->sgl, s, sgt->orig_nents, i)
+ {
+ npages += (sg_dma_len(s) + PAGE_SIZE - 1) / PAGE_SIZE;
+ }
+
+ /* Allocate page arrary. */
+ gcmkONERROR(gckOS_Allocate(os, npages * gcmSIZEOF(*pagearray), (gctPOINTER *)&pagearray));
+
+ /* Fill page arrary. */
+ for_each_sg(sgt->sgl, s, sgt->orig_nents, i)
+ {
+ for (j = 0; j < (sg_dma_len(s) + PAGE_SIZE - 1) / PAGE_SIZE; j++)
+ {
+ pagearray[k++] = sg_dma_address(s) + j * PAGE_SIZE;
+ }
+ }
+
+ /* Prepare descriptor. */
+ gcmkONERROR(gckOS_Allocate(os, sizeof(gcsDMABUF), (gctPOINTER *)&buf_desc));
+
+ buf_desc->dmabuf = dmabuf;
+ buf_desc->pagearray = pagearray;
+ buf_desc->attachment = attachment;
+ buf_desc->sgtable = sgt;
+
+ /* Record in buffer list to support debugfs. */
+ buf_desc->npages = npages;
+ buf_desc->pid = _GetProcessID();
+
+ mutex_lock(&priv->lock);
+ list_add(&buf_desc->list, &priv->buf_list);
+ mutex_unlock(&priv->lock);
+
+ /* Record page number. */
+ Mdl->numPages = npages;
+
+ Mdl->priv = buf_desc;
+
+ /* Need set it as true to avoid MMU mapping. */
+ Mdl->contiguous = gcvTRUE;
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ if (pagearray)
+ {
+ gcmkOS_SAFE_FREE(os, pagearray);
+ }
+
+ if (sgt)
+ {
+ dma_buf_unmap_attachment(attachment, sgt, DMA_BIDIRECTIONAL);
+ }
+
+ gcmkFOOTER();
+ return status;
+}
+
+
+static void
+_DmabufFree(
+ IN gckALLOCATOR Allocator,
+ IN PLINUX_MDL Mdl
+ )
+{
+ gcsDMABUF *buf_desc = Mdl->priv;
+ gckOS os = Allocator->os;
+ struct allocator_priv *priv = Allocator->privateData;
+
+ mutex_lock(&priv->lock);
+ list_del(&buf_desc->list);
+ mutex_unlock(&priv->lock);
+
+ dma_buf_unmap_attachment(buf_desc->attachment, buf_desc->sgtable, DMA_BIDIRECTIONAL);
+
+ dma_buf_detach(buf_desc->dmabuf, buf_desc->attachment);
+
+ dma_buf_put(buf_desc->dmabuf);
+
+ gckOS_Free(os, buf_desc->pagearray);
+
+ gckOS_Free(os, buf_desc);
+}
+
+static void
+_DmabufUnmapUser(
+ IN gckALLOCATOR Allocator,
+ IN PLINUX_MDL Mdl,
+ IN gctPOINTER Logical,
+ IN gctUINT32 Size
+ )
+{
+ gcsDMABUF *buf_desc = Mdl->priv;
+ gctINT8_PTR userLogical = Logical;
+
+ if (unlikely(current->mm == gcvNULL))
+ {
+ /* Do nothing if process is exiting. */
+ return;
+ }
+
+ userLogical -= buf_desc->sgtable->sgl->offset;
+ vm_munmap((unsigned long)userLogical, Mdl->numPages << PAGE_SHIFT);
+}
+
+static gceSTATUS
+_DmabufMapUser(
+ IN gckALLOCATOR Allocator,
+ IN PLINUX_MDL Mdl,
+ IN gctBOOL Cacheable,
+ OUT gctPOINTER * UserLogical
+ )
+{
+ gcsDMABUF *buf_desc = Mdl->priv;
+ gctINT8_PTR userLogical = gcvNULL;
+ gceSTATUS status = gcvSTATUS_OK;
+
+ userLogical = (gctINT8_PTR)vm_mmap(buf_desc->dmabuf->file,
+ 0L,
+ Mdl->numPages << PAGE_SHIFT,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_NORESERVE,
+ 0);
+
+ if (IS_ERR(userLogical))
+ {
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+ userLogical += buf_desc->sgtable->sgl->offset;
+
+ /* To make sure the mapping is created. */
+ if (access_ok(VERIFY_READ, userLogical, 4))
+ {
+ uint32_t mem;
+ get_user(mem, (uint32_t *)userLogical);
+
+ (void)mem;
+ }
+
+ *UserLogical = (gctPOINTER)userLogical;
+
+OnError:
+ if (gcmIS_ERROR(status) && userLogical)
+ {
+ _DmabufUnmapUser(Allocator, Mdl, userLogical, Mdl->numPages << PAGE_SHIFT);
+ }
+ return status;
+}
+
+static gceSTATUS
+_DmabufMapKernel(
+ IN gckALLOCATOR Allocator,
+ IN PLINUX_MDL Mdl,
+ OUT gctPOINTER *Logical
+ )
+{
+ /* Kernel doesn't acess video memory. */
+ return gcvSTATUS_NOT_SUPPORTED;
+
+}
+
+static gceSTATUS
+_DmabufUnmapKernel(
+ IN gckALLOCATOR Allocator,
+ IN PLINUX_MDL Mdl,
+ IN gctPOINTER Logical
+ )
+{
+ /* Kernel doesn't acess video memory. */
+ return gcvSTATUS_NOT_SUPPORTED;
+}
+
+static gceSTATUS
+_DmabufCache(
+ IN gckALLOCATOR Allocator,
+ IN PLINUX_MDL Mdl,
+ IN gctPOINTER Logical,
+ IN gctUINT32 Physical,
+ IN gctUINT32 Bytes,
+ IN gceCACHEOPERATION Operation
+ )
+{
+ return gcvSTATUS_OK;
+}
+
+
+static gceSTATUS
+_DmabufPhysical(
+ IN gckALLOCATOR Allocator,
+ IN PLINUX_MDL Mdl,
+ IN gctUINT32 Offset,
+ OUT gctPHYS_ADDR_T * Physical
+ )
+{
+ gcsDMABUF *buf_desc = Mdl->priv;
+ gctUINT32 offsetInPage = Offset & ~PAGE_MASK;
+ gctUINT32 index = Offset / PAGE_SIZE;
+
+ *Physical = buf_desc->pagearray[index] + offsetInPage;
+
+
+ return gcvSTATUS_OK;
+}
+
+/* Default allocator operations. */
+static gcsALLOCATOR_OPERATIONS DmabufAllocatorOperations =
+{
+ .Attach = _DmabufAttach,
+ .Free = _DmabufFree,
+ .MapUser = _DmabufMapUser,
+ .UnmapUser = _DmabufUnmapUser,
+ .MapKernel = _DmabufMapKernel,
+ .UnmapKernel = _DmabufUnmapKernel,
+ .Cache = _DmabufCache,
+ .Physical = _DmabufPhysical,
+};
+
+static void
+_DmabufAllocatorDestructor(
+ gcsALLOCATOR *Allocator
+ )
+{
+ _DebugfsCleanup(Allocator);
+
+ if (Allocator->privateData)
+ {
+ kfree(Allocator->privateData);
+ }
+
+ kfree(Allocator);
+}
+
+/* Default allocator entry. */
+gceSTATUS
+_DmabufAlloctorInit(
+ IN gckOS Os,
+ IN gcsDEBUGFS_DIR *Parent,
+ OUT gckALLOCATOR * Allocator
+ )
+{
+ gceSTATUS status;
+ gckALLOCATOR allocator;
+ struct allocator_priv *priv = NULL;
+
+ priv = kmalloc(sizeof (struct allocator_priv), GFP_KERNEL | gcdNOWARN);
+
+ if (!priv)
+ {
+ gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY);
+ }
+
+ mutex_init(&priv->lock);
+ INIT_LIST_HEAD(&priv->buf_list);
+
+ gcmkONERROR(
+ gckALLOCATOR_Construct(Os, &DmabufAllocatorOperations, &allocator));
+
+ allocator->capability = gcvALLOC_FLAG_DMABUF
+ | gcvALLOC_FLAG_DMABUF_EXPORTABLE
+ ;
+
+ /* Register private data. */
+ allocator->privateData = priv;
+ allocator->destructor = _DmabufAllocatorDestructor;
+
+ _DebugfsInit(allocator, Parent);
+
+ *Allocator = allocator;
+
+ return gcvSTATUS_OK;
+
+OnError:
+ if (priv)
+ {
+ kfree(priv);
+ }
+
+ return status;
+}
+