X-Git-Url: http://git.openwrt.org/?a=blobdiff_plain;f=target%2Flinux%2Fbrcm2708%2Fpatches-4.19%2F950-0240-staging-vc04_services-Add-new-vc-sm-cma-driver.patch;fp=target%2Flinux%2Fbrcm2708%2Fpatches-4.19%2F950-0240-staging-vc04_services-Add-new-vc-sm-cma-driver.patch;h=9e9af47a370f1385fc2fbb3bdac1363141c826e9;hb=84d555aa74434392b682fd9eb0fa701c89a046d6;hp=0000000000000000000000000000000000000000;hpb=953973c2991e8640549a55df7a0574a1abac8644;p=openwrt%2Fstaging%2Fdedeckeh.git diff --git a/target/linux/brcm2708/patches-4.19/950-0240-staging-vc04_services-Add-new-vc-sm-cma-driver.patch b/target/linux/brcm2708/patches-4.19/950-0240-staging-vc04_services-Add-new-vc-sm-cma-driver.patch new file mode 100644 index 0000000000..9e9af47a37 --- /dev/null +++ b/target/linux/brcm2708/patches-4.19/950-0240-staging-vc04_services-Add-new-vc-sm-cma-driver.patch @@ -0,0 +1,1881 @@ +From 2994fdc0a9d48be68d6e403bc8ddadecfc8d8796 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Tue, 25 Sep 2018 10:27:11 +0100 +Subject: [PATCH] staging: vc04_services: Add new vc-sm-cma driver + +This new driver allows contiguous memory blocks to be imported +into the VideoCore VPU memory map, and manages the lifetime of +those objects, only releasing the source dmabuf once the VPU has +confirmed it has finished with it. + +Signed-off-by: Dave Stevenson +--- + drivers/staging/vc04_services/Kconfig | 1 + + drivers/staging/vc04_services/Makefile | 1 + + .../staging/vc04_services/vc-sm-cma/Kconfig | 10 + + .../staging/vc04_services/vc-sm-cma/Makefile | 8 + + drivers/staging/vc04_services/vc-sm-cma/TODO | 2 + + .../staging/vc04_services/vc-sm-cma/vc_sm.c | 838 ++++++++++++++++++ + .../staging/vc04_services/vc-sm-cma/vc_sm.h | 59 ++ + .../vc04_services/vc-sm-cma/vc_sm_cma_vchi.c | 498 +++++++++++ + .../vc04_services/vc-sm-cma/vc_sm_cma_vchi.h | 59 ++ + .../vc04_services/vc-sm-cma/vc_sm_defs.h | 298 +++++++ + .../vc04_services/vc-sm-cma/vc_sm_knl.h | 28 + + 11 files changed, 1802 insertions(+) + create mode 100644 drivers/staging/vc04_services/vc-sm-cma/Kconfig + create mode 100644 drivers/staging/vc04_services/vc-sm-cma/Makefile + create mode 100644 drivers/staging/vc04_services/vc-sm-cma/TODO + create mode 100644 drivers/staging/vc04_services/vc-sm-cma/vc_sm.c + create mode 100644 drivers/staging/vc04_services/vc-sm-cma/vc_sm.h + create mode 100644 drivers/staging/vc04_services/vc-sm-cma/vc_sm_cma_vchi.c + create mode 100644 drivers/staging/vc04_services/vc-sm-cma/vc_sm_cma_vchi.h + create mode 100644 drivers/staging/vc04_services/vc-sm-cma/vc_sm_defs.h + create mode 100644 drivers/staging/vc04_services/vc-sm-cma/vc_sm_knl.h + +--- a/drivers/staging/vc04_services/Kconfig ++++ b/drivers/staging/vc04_services/Kconfig +@@ -22,6 +22,7 @@ source "drivers/staging/vc04_services/bc + + source "drivers/staging/vc04_services/bcm2835-camera/Kconfig" + source "drivers/staging/vc04_services/vchiq-mmal/Kconfig" ++source "drivers/staging/vc04_services/vc-sm-cma/Kconfig" + + endif + +--- a/drivers/staging/vc04_services/Makefile ++++ b/drivers/staging/vc04_services/Makefile +@@ -13,6 +13,7 @@ vchiq-objs := \ + obj-$(CONFIG_SND_BCM2835) += bcm2835-audio/ + obj-$(CONFIG_VIDEO_BCM2835) += bcm2835-camera/ + obj-$(CONFIG_BCM2835_VCHIQ_MMAL) += vchiq-mmal/ ++obj-$(CONFIG_BCM_VC_SM_CMA) += vc-sm-cma/ + + ccflags-y += -Idrivers/staging/vc04_services -D__VCCOREVER__=0x04000000 + +--- /dev/null ++++ b/drivers/staging/vc04_services/vc-sm-cma/Kconfig +@@ -0,0 +1,10 @@ ++config BCM_VC_SM_CMA ++ tristate "VideoCore Shared Memory (CMA) driver" ++ depends on BCM2835_VCHIQ ++ select RBTREE ++ select DMA_SHARED_BUFFER ++ help ++ Say Y here to enable the shared memory interface that ++ supports sharing dmabufs with VideoCore. ++ This operates over the VCHIQ interface to a service ++ running on VideoCore. +--- /dev/null ++++ b/drivers/staging/vc04_services/vc-sm-cma/Makefile +@@ -0,0 +1,8 @@ ++ccflags-y += -Idrivers/staging/vc04_services -Idrivers/staging/vc04_services/interface/vchi -Idrivers/staging/vc04_services/interface/vchiq_arm ++# -I"drivers/staging/android/ion/" -I"$(srctree)/fs/" ++ccflags-y += -D__VCCOREVER__=0 ++ ++vc-sm-cma-$(CONFIG_BCM_VC_SM_CMA) := \ ++ vc_sm.o vc_sm_cma_vchi.o ++ ++obj-$(CONFIG_BCM_VC_SM_CMA) += vc-sm-cma.o +--- /dev/null ++++ b/drivers/staging/vc04_services/vc-sm-cma/TODO +@@ -0,0 +1,2 @@ ++1) Convert to a platform driver. ++ +--- /dev/null ++++ b/drivers/staging/vc04_services/vc-sm-cma/vc_sm.c +@@ -0,0 +1,838 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * VideoCore Shared Memory driver using CMA. ++ * ++ * Copyright: 2018, Raspberry Pi (Trading) Ltd ++ * Dave Stevenson ++ * ++ * Based on vmcs_sm driver from Broadcom Corporation for some API, ++ * and taking some code for CMA/dmabuf handling from the Android Ion ++ * driver (Google/Linaro). ++ * ++ * This is cut down version to only support import of dma_bufs from ++ * other kernel drivers. A more complete implementation of the old ++ * vmcs_sm functionality can follow later. ++ * ++ */ ++ ++/* ---- Include Files ----------------------------------------------------- */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "vchiq_connected.h" ++#include "vc_sm_cma_vchi.h" ++ ++#include "vc_sm.h" ++#include "vc_sm_knl.h" ++ ++/* ---- Private Constants and Types --------------------------------------- */ ++ ++#define DEVICE_NAME "vcsm-cma" ++#define DEVICE_MINOR 0 ++ ++#define VC_SM_RESOURCE_NAME_DEFAULT "sm-host-resource" ++ ++#define VC_SM_DIR_ROOT_NAME "vcsm-cma" ++#define VC_SM_STATE "state" ++ ++/* Private file data associated with each opened device. */ ++struct vc_sm_privdata_t { ++ pid_t pid; /* PID of creator. */ ++ ++ int restart_sys; /* Tracks restart on interrupt. */ ++ enum vc_sm_msg_type int_action; /* Interrupted action. */ ++ u32 int_trans_id; /* Interrupted transaction. */ ++}; ++ ++typedef int (*VC_SM_SHOW) (struct seq_file *s, void *v); ++struct sm_pde_t { ++ VC_SM_SHOW show; /* Debug fs function hookup. */ ++ struct dentry *dir_entry; /* Debug fs directory entry. */ ++ void *priv_data; /* Private data */ ++}; ++ ++/* Global state information. */ ++struct sm_state_t { ++ struct platform_device *pdev; ++ ++ struct miscdevice dev; ++ struct sm_instance *sm_handle; /* Handle for videocore service. */ ++ ++ struct mutex map_lock; /* Global map lock. */ ++ struct list_head buffer_list; /* List of buffer. */ ++ ++ struct vc_sm_privdata_t *data_knl; /* Kernel internal data tracking. */ ++ struct dentry *dir_root; /* Debug fs entries root. */ ++ struct sm_pde_t dir_state; /* Debug fs entries state sub-tree. */ ++ ++ bool require_released_callback; /* VPU will send a released msg when it ++ * has finished with a resource. ++ */ ++ u32 int_trans_id; /* Interrupted transaction. */ ++}; ++ ++/* ---- Private Variables ----------------------------------------------- */ ++ ++static struct sm_state_t *sm_state; ++static int sm_inited; ++ ++/* ---- Private Function Prototypes -------------------------------------- */ ++ ++/* ---- Private Functions ------------------------------------------------ */ ++ ++static int vc_sm_cma_seq_file_show(struct seq_file *s, void *v) ++{ ++ struct sm_pde_t *sm_pde; ++ ++ sm_pde = (struct sm_pde_t *)(s->private); ++ ++ if (sm_pde && sm_pde->show) ++ sm_pde->show(s, v); ++ ++ return 0; ++} ++ ++static int vc_sm_cma_single_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, vc_sm_cma_seq_file_show, inode->i_private); ++} ++ ++static const struct file_operations vc_sm_cma_debug_fs_fops = { ++ .open = vc_sm_cma_single_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++static int vc_sm_cma_global_state_show(struct seq_file *s, void *v) ++{ ++ struct vc_sm_buffer *resource = NULL; ++ int resource_count = 0; ++ ++ if (!sm_state) ++ return 0; ++ ++ seq_printf(s, "\nVC-ServiceHandle 0x%x\n", ++ (unsigned int)sm_state->sm_handle); ++ ++ /* Log all applicable mapping(s). */ ++ ++ mutex_lock(&sm_state->map_lock); ++ seq_puts(s, "\nResources\n"); ++ if (!list_empty(&sm_state->buffer_list)) { ++ list_for_each_entry(resource, &sm_state->buffer_list, ++ global_buffer_list) { ++ resource_count++; ++ ++ seq_printf(s, "\nResource %p\n", ++ resource); ++ seq_printf(s, " NAME %s\n", ++ resource->name); ++ seq_printf(s, " SIZE %d\n", ++ resource->size); ++ seq_printf(s, " DMABUF %p\n", ++ resource->dma_buf); ++ seq_printf(s, " ATTACH %p\n", ++ resource->attach); ++ seq_printf(s, " SG_TABLE %p\n", ++ resource->sg_table); ++ seq_printf(s, " SGT %p\n", ++ resource->sgt); ++ seq_printf(s, " DMA_ADDR %pad\n", ++ &resource->dma_addr); ++ seq_printf(s, " VC_HANDLE %08x\n", ++ resource->vc_handle); ++ seq_printf(s, " VC_MAPPING %d\n", ++ resource->vpu_state); ++ } ++ } ++ seq_printf(s, "\n\nTotal resource count: %d\n\n", resource_count); ++ ++ mutex_unlock(&sm_state->map_lock); ++ ++ return 0; ++} ++ ++/* ++ * Adds a buffer to the private data list which tracks all the allocated ++ * data. ++ */ ++static void vc_sm_add_resource(struct vc_sm_privdata_t *privdata, ++ struct vc_sm_buffer *buffer) ++{ ++ mutex_lock(&sm_state->map_lock); ++ list_add(&buffer->global_buffer_list, &sm_state->buffer_list); ++ mutex_unlock(&sm_state->map_lock); ++ ++ pr_debug("[%s]: added buffer %p (name %s, size %d)\n", ++ __func__, buffer, buffer->name, buffer->size); ++} ++ ++/* ++ * Release an allocation. ++ * All refcounting is done via the dma buf object. ++ */ ++static void vc_sm_release_resource(struct vc_sm_buffer *buffer, int force) ++{ ++ mutex_lock(&sm_state->map_lock); ++ mutex_lock(&buffer->lock); ++ ++ pr_debug("[%s]: buffer %p (name %s, size %d)\n", ++ __func__, buffer, buffer->name, buffer->size); ++ ++ if (buffer->vc_handle && buffer->vpu_state == VPU_MAPPED) { ++ struct vc_sm_free_t free = { buffer->vc_handle, 0 }; ++ int status = vc_sm_cma_vchi_free(sm_state->sm_handle, &free, ++ &sm_state->int_trans_id); ++ if (status != 0 && status != -EINTR) { ++ pr_err("[%s]: failed to free memory on videocore (status: %u, trans_id: %u)\n", ++ __func__, status, sm_state->int_trans_id); ++ } ++ ++ if (sm_state->require_released_callback) { ++ /* Need to wait for the VPU to confirm the free */ ++ ++ /* Retain a reference on this until the VPU has ++ * released it ++ */ ++ buffer->vpu_state = VPU_UNMAPPING; ++ goto defer; ++ } ++ buffer->vpu_state = VPU_NOT_MAPPED; ++ buffer->vc_handle = 0; ++ } ++ if (buffer->vc_handle) { ++ /* We've sent the unmap request but not had the response. */ ++ pr_err("[%s]: Waiting for VPU unmap response on %p\n", ++ __func__, buffer); ++ goto defer; ++ } ++ if (buffer->in_use) { ++ /* Don't release dmabuf here - we await the release */ ++ pr_err("[%s]: buffer %p is still in use\n", ++ __func__, buffer); ++ goto defer; ++ } ++ ++ /* Handle cleaning up imported dmabufs */ ++ if (buffer->sgt) { ++ dma_buf_unmap_attachment(buffer->attach, buffer->sgt, ++ DMA_BIDIRECTIONAL); ++ buffer->sgt = NULL; ++ } ++ if (buffer->attach) { ++ dma_buf_detach(buffer->dma_buf, buffer->attach); ++ buffer->attach = NULL; ++ } ++ ++ /* Release the dma_buf (whether ours or imported) */ ++ if (buffer->import_dma_buf) { ++ dma_buf_put(buffer->import_dma_buf); ++ buffer->import_dma_buf = NULL; ++ buffer->dma_buf = NULL; ++ } else if (buffer->dma_buf) { ++ dma_buf_put(buffer->dma_buf); ++ buffer->dma_buf = NULL; ++ } ++ ++ if (buffer->sg_table && !buffer->import_dma_buf) { ++ /* Our own allocation that we need to dma_unmap_sg */ ++ dma_unmap_sg(&sm_state->pdev->dev, buffer->sg_table->sgl, ++ buffer->sg_table->nents, DMA_BIDIRECTIONAL); ++ } ++ ++ /* Free the local resource. Start by removing it from the list */ ++ buffer->private = NULL; ++ list_del(&buffer->global_buffer_list); ++ ++ mutex_unlock(&buffer->lock); ++ mutex_unlock(&sm_state->map_lock); ++ ++ mutex_destroy(&buffer->lock); ++ ++ kfree(buffer); ++ return; ++ ++defer: ++ mutex_unlock(&buffer->lock); ++ mutex_unlock(&sm_state->map_lock); ++} ++ ++/* Create support for private data tracking. */ ++static struct vc_sm_privdata_t *vc_sm_cma_create_priv_data(pid_t id) ++{ ++ char alloc_name[32]; ++ struct vc_sm_privdata_t *file_data = NULL; ++ ++ /* Allocate private structure. */ ++ file_data = kzalloc(sizeof(*file_data), GFP_KERNEL); ++ ++ if (!file_data) ++ return NULL; ++ ++ snprintf(alloc_name, sizeof(alloc_name), "%d", id); ++ ++ file_data->pid = id; ++ ++ return file_data; ++} ++ ++/* Dma_buf operations for chaining through to an imported dma_buf */ ++static ++int vc_sm_import_dma_buf_attach(struct dma_buf *dmabuf, ++ struct dma_buf_attachment *attachment) ++{ ++ struct vc_sm_buffer *res = dmabuf->priv; ++ ++ if (!res->import_dma_buf) ++ return -EINVAL; ++ return res->import_dma_buf->ops->attach(res->import_dma_buf, ++ attachment); ++} ++ ++static ++void vc_sm_import_dma_buf_detatch(struct dma_buf *dmabuf, ++ struct dma_buf_attachment *attachment) ++{ ++ struct vc_sm_buffer *res = dmabuf->priv; ++ ++ if (!res->import_dma_buf) ++ return; ++ res->import_dma_buf->ops->detach(res->import_dma_buf, attachment); ++} ++ ++static ++struct sg_table *vc_sm_import_map_dma_buf(struct dma_buf_attachment *attachment, ++ enum dma_data_direction direction) ++{ ++ struct vc_sm_buffer *res = attachment->dmabuf->priv; ++ ++ if (!res->import_dma_buf) ++ return NULL; ++ return res->import_dma_buf->ops->map_dma_buf(attachment, direction); ++} ++ ++static ++void vc_sm_import_unmap_dma_buf(struct dma_buf_attachment *attachment, ++ struct sg_table *table, ++ enum dma_data_direction direction) ++{ ++ struct vc_sm_buffer *res = attachment->dmabuf->priv; ++ ++ if (!res->import_dma_buf) ++ return; ++ res->import_dma_buf->ops->unmap_dma_buf(attachment, table, direction); ++} ++ ++static ++int vc_sm_import_dmabuf_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma) ++{ ++ struct vc_sm_buffer *res = dmabuf->priv; ++ ++ pr_debug("%s: mmap dma_buf %p, res %p, imported db %p\n", __func__, ++ dmabuf, res, res->import_dma_buf); ++ if (!res->import_dma_buf) { ++ pr_err("%s: mmap dma_buf %p- not an imported buffer\n", ++ __func__, dmabuf); ++ return -EINVAL; ++ } ++ return res->import_dma_buf->ops->mmap(res->import_dma_buf, vma); ++} ++ ++static ++void vc_sm_import_dma_buf_release(struct dma_buf *dmabuf) ++{ ++ struct vc_sm_buffer *res = dmabuf->priv; ++ ++ pr_debug("%s: Relasing dma_buf %p\n", __func__, dmabuf); ++ if (!res->import_dma_buf) ++ return; ++ ++ res->in_use = 0; ++ ++ vc_sm_release_resource(res, 0); ++} ++ ++static ++void *vc_sm_import_dma_buf_kmap(struct dma_buf *dmabuf, ++ unsigned long offset) ++{ ++ struct vc_sm_buffer *res = dmabuf->priv; ++ ++ if (!res->import_dma_buf) ++ return NULL; ++ return res->import_dma_buf->ops->map(res->import_dma_buf, ++ offset); ++} ++ ++static ++void vc_sm_import_dma_buf_kunmap(struct dma_buf *dmabuf, ++ unsigned long offset, void *ptr) ++{ ++ struct vc_sm_buffer *res = dmabuf->priv; ++ ++ if (!res->import_dma_buf) ++ return; ++ res->import_dma_buf->ops->unmap(res->import_dma_buf, ++ offset, ptr); ++} ++ ++static ++int vc_sm_import_dma_buf_begin_cpu_access(struct dma_buf *dmabuf, ++ enum dma_data_direction direction) ++{ ++ struct vc_sm_buffer *res = dmabuf->priv; ++ ++ if (!res->import_dma_buf) ++ return -EINVAL; ++ return res->import_dma_buf->ops->begin_cpu_access(res->import_dma_buf, ++ direction); ++} ++ ++static ++int vc_sm_import_dma_buf_end_cpu_access(struct dma_buf *dmabuf, ++ enum dma_data_direction direction) ++{ ++ struct vc_sm_buffer *res = dmabuf->priv; ++ ++ if (!res->import_dma_buf) ++ return -EINVAL; ++ return res->import_dma_buf->ops->end_cpu_access(res->import_dma_buf, ++ direction); ++} ++ ++static const struct dma_buf_ops dma_buf_import_ops = { ++ .map_dma_buf = vc_sm_import_map_dma_buf, ++ .unmap_dma_buf = vc_sm_import_unmap_dma_buf, ++ .mmap = vc_sm_import_dmabuf_mmap, ++ .release = vc_sm_import_dma_buf_release, ++ .attach = vc_sm_import_dma_buf_attach, ++ .detach = vc_sm_import_dma_buf_detatch, ++ .begin_cpu_access = vc_sm_import_dma_buf_begin_cpu_access, ++ .end_cpu_access = vc_sm_import_dma_buf_end_cpu_access, ++ .map = vc_sm_import_dma_buf_kmap, ++ .unmap = vc_sm_import_dma_buf_kunmap, ++}; ++ ++/* Import a dma_buf to be shared with VC. */ ++int ++vc_sm_cma_import_dmabuf_internal(struct vc_sm_privdata_t *private, ++ struct dma_buf *dma_buf, ++ struct dma_buf **imported_buf) ++{ ++ DEFINE_DMA_BUF_EXPORT_INFO(exp_info); ++ struct vc_sm_buffer *buffer = NULL; ++ struct vc_sm_import import = { }; ++ struct vc_sm_import_result result = { }; ++ struct dma_buf_attachment *attach = NULL; ++ struct sg_table *sgt = NULL; ++ int ret = 0; ++ int status; ++ ++ /* Setup our allocation parameters */ ++ pr_debug("%s: importing dma_buf %p\n", __func__, dma_buf); ++ ++ get_dma_buf(dma_buf); ++ dma_buf = dma_buf; ++ ++ attach = dma_buf_attach(dma_buf, &sm_state->pdev->dev); ++ if (IS_ERR(attach)) { ++ ret = PTR_ERR(attach); ++ goto error; ++ } ++ ++ sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); ++ if (IS_ERR(sgt)) { ++ ret = PTR_ERR(sgt); ++ goto error; ++ } ++ ++ /* Verify that the address block is contiguous */ ++ if (sgt->nents != 1) { ++ ret = -ENOMEM; ++ goto error; ++ } ++ ++ /* Allocate local buffer to track this allocation. */ ++ buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); ++ if (!buffer) { ++ ret = -ENOMEM; ++ goto error; ++ } ++ ++ import.type = VC_SM_ALLOC_NON_CACHED; ++ import.addr = (uint32_t)sg_dma_address(sgt->sgl); ++ if ((import.addr & 0xC0000000) != 0xC0000000) { ++ pr_err("%s: Expecting an uncached alias for dma_addr %08x\n", ++ __func__, import.addr); ++ import.addr |= 0xC0000000; ++ } ++ import.size = sg_dma_len(sgt->sgl); ++ import.allocator = current->tgid; ++ import.kernel_id = (uint32_t)buffer; //FIXME: 64 bit support needed. ++ ++ memcpy(import.name, VC_SM_RESOURCE_NAME_DEFAULT, ++ sizeof(VC_SM_RESOURCE_NAME_DEFAULT)); ++ ++ pr_debug("[%s]: attempt to import \"%s\" data - type %u, addr %p, size %u\n", ++ __func__, import.name, import.type, (void *)import.addr, ++ import.size); ++ ++ /* Allocate the videocore buffer. */ ++ status = vc_sm_cma_vchi_import(sm_state->sm_handle, &import, &result, ++ &sm_state->int_trans_id); ++ if (status == -EINTR) { ++ pr_debug("[%s]: requesting import memory action restart (trans_id: %u)\n", ++ __func__, sm_state->int_trans_id); ++ ret = -ERESTARTSYS; ++ private->restart_sys = -EINTR; ++ private->int_action = VC_SM_MSG_TYPE_IMPORT; ++ goto error; ++ } else if (status || !result.res_handle) { ++ pr_debug("[%s]: failed to import memory on videocore (status: %u, trans_id: %u)\n", ++ __func__, status, sm_state->int_trans_id); ++ ret = -ENOMEM; ++ goto error; ++ } ++ ++ mutex_init(&buffer->lock); ++ INIT_LIST_HEAD(&buffer->attachments); ++ memcpy(buffer->name, import.name, ++ min(sizeof(buffer->name), sizeof(import.name) - 1)); ++ ++ /* Keep track of the buffer we created. */ ++ buffer->private = private; ++ buffer->vc_handle = result.res_handle; ++ buffer->size = import.size; ++ buffer->vpu_state = VPU_MAPPED; ++ ++ buffer->import_dma_buf = dma_buf; ++ ++ buffer->attach = attach; ++ buffer->sgt = sgt; ++ buffer->dma_addr = sg_dma_address(sgt->sgl); ++ buffer->in_use = 1; ++ ++ /* ++ * We're done - we need to export a new dmabuf chaining through most ++ * functions, but enabling us to release our own internal references ++ * here. ++ */ ++ exp_info.ops = &dma_buf_import_ops; ++ exp_info.size = import.size; ++ exp_info.flags = O_RDWR; ++ exp_info.priv = buffer; ++ ++ buffer->dma_buf = dma_buf_export(&exp_info); ++ if (IS_ERR(buffer->dma_buf)) { ++ ret = PTR_ERR(buffer->dma_buf); ++ goto error; ++ } ++ ++ vc_sm_add_resource(private, buffer); ++ ++ *imported_buf = buffer->dma_buf; ++ ++ return 0; ++ ++error: ++ if (result.res_handle) { ++ struct vc_sm_free_t free = { result.res_handle, 0 }; ++ ++ vc_sm_cma_vchi_free(sm_state->sm_handle, &free, ++ &sm_state->int_trans_id); ++ } ++ kfree(buffer); ++ if (sgt) ++ dma_buf_unmap_attachment(attach, sgt, DMA_BIDIRECTIONAL); ++ if (attach) ++ dma_buf_detach(dma_buf, attach); ++ dma_buf_put(dma_buf); ++ return ret; ++} ++ ++/* FIXME: Pass a function pointer to this into vc_vchi_sm.c */ ++void ++vc_sm_vpu_event(struct sm_instance *instance, struct vc_sm_result_t *reply, ++ int reply_len) ++{ ++ switch (reply->trans_id & ~0x80000000) { ++ case VC_SM_MSG_TYPE_CLIENT_VERSION: ++ { ++ /* Acknowledge that the firmware supports the version command */ ++ pr_debug("%s: firmware acked version msg. Require release cb\n", ++ __func__); ++ sm_state->require_released_callback = true; ++ } ++ break; ++ case VC_SM_MSG_TYPE_RELEASED: ++ { ++ struct vc_sm_released *release = (struct vc_sm_released *)reply; ++ struct vc_sm_buffer *buffer = ++ (struct vc_sm_buffer *)release->kernel_id; ++ ++ /* ++ * FIXME: Need to check buffer is still valid and allocated ++ * before continuing ++ */ ++ pr_debug("%s: Released addr %08x, size %u, id %08x, mem_handle %08x\n", ++ __func__, release->addr, release->size, ++ release->kernel_id, release->vc_handle); ++ mutex_lock(&buffer->lock); ++ buffer->vc_handle = 0; ++ buffer->vpu_state = VPU_NOT_MAPPED; ++ mutex_unlock(&buffer->lock); ++ ++ vc_sm_release_resource(buffer, 0); ++ } ++ break; ++ default: ++ pr_err("%s: Unknown vpu cmd %x\n", __func__, reply->trans_id); ++ break; ++ } ++} ++ ++/* Videocore connected. */ ++static void vc_sm_connected_init(void) ++{ ++ int ret; ++ VCHI_INSTANCE_T vchi_instance; ++ struct vc_sm_version version; ++ struct vc_sm_result_t version_result; ++ ++ pr_info("[%s]: start\n", __func__); ++ ++ /* ++ * Initialize and create a VCHI connection for the shared memory service ++ * running on videocore. ++ */ ++ ret = vchi_initialise(&vchi_instance); ++ if (ret) { ++ pr_err("[%s]: failed to initialise VCHI instance (ret=%d)\n", ++ __func__, ret); ++ ++ ret = -EIO; ++ goto err_free_mem; ++ } ++ ++ ret = vchi_connect(vchi_instance); ++ if (ret) { ++ pr_err("[%s]: failed to connect VCHI instance (ret=%d)\n", ++ __func__, ret); ++ ++ ret = -EIO; ++ goto err_free_mem; ++ } ++ ++ /* Initialize an instance of the shared memory service. */ ++ sm_state->sm_handle = vc_sm_cma_vchi_init(vchi_instance, 1, ++ vc_sm_vpu_event); ++ if (!sm_state->sm_handle) { ++ pr_err("[%s]: failed to initialize shared memory service\n", ++ __func__); ++ ++ ret = -EPERM; ++ goto err_free_mem; ++ } ++ ++ /* Create a debug fs directory entry (root). */ ++ sm_state->dir_root = debugfs_create_dir(VC_SM_DIR_ROOT_NAME, NULL); ++ if (!sm_state->dir_root) { ++ pr_err("[%s]: failed to create \'%s\' directory entry\n", ++ __func__, VC_SM_DIR_ROOT_NAME); ++ ++ ret = -EPERM; ++ goto err_stop_sm_service; ++ } ++ ++ sm_state->dir_state.show = &vc_sm_cma_global_state_show; ++ sm_state->dir_state.dir_entry = ++ debugfs_create_file(VC_SM_STATE, 0444, sm_state->dir_root, ++ &sm_state->dir_state, ++ &vc_sm_cma_debug_fs_fops); ++ ++ INIT_LIST_HEAD(&sm_state->buffer_list); ++ ++ sm_state->data_knl = vc_sm_cma_create_priv_data(0); ++ if (!sm_state->data_knl) { ++ pr_err("[%s]: failed to create kernel private data tracker\n", ++ __func__); ++ goto err_remove_shared_memory; ++ } ++ ++ version.version = 1; ++ ret = vc_sm_cma_vchi_client_version(sm_state->sm_handle, &version, ++ &version_result, ++ &sm_state->int_trans_id); ++ if (ret) { ++ pr_err("[%s]: Failed to send version request %d\n", __func__, ++ ret); ++ } ++ ++ /* Done! */ ++ sm_inited = 1; ++ pr_info("[%s]: installed successfully\n", __func__); ++ return; ++ ++err_remove_shared_memory: ++ debugfs_remove_recursive(sm_state->dir_root); ++err_stop_sm_service: ++ vc_sm_cma_vchi_stop(&sm_state->sm_handle); ++err_free_mem: ++ kfree(sm_state); ++ pr_info("[%s]: failed, ret %d\n", __func__, ret); ++} ++ ++/* Driver loading. */ ++static int bcm2835_vc_sm_cma_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ int err; ++ ++ pr_info("%s: Videocore shared memory driver\n", __func__); ++ ++ sm_state = kzalloc(sizeof(*sm_state), GFP_KERNEL); ++ if (!sm_state) ++ return -ENOMEM; ++ sm_state->pdev = pdev; ++ mutex_init(&sm_state->map_lock); ++ ++ dev->coherent_dma_mask = DMA_BIT_MASK(32); ++ dev->dma_mask = &dev->coherent_dma_mask; ++ err = of_dma_configure(dev, NULL, true); ++ if (err) { ++ dev_err(dev, "Unable to setup DMA: %d\n", err); ++ return err; ++ } ++ ++ vchiq_add_connected_callback(vc_sm_connected_init); ++ return 0; ++} ++ ++/* Driver unloading. */ ++static int bcm2835_vc_sm_cma_remove(struct platform_device *pdev) ++{ ++ pr_debug("[%s]: start\n", __func__); ++ if (sm_inited) { ++ /* Remove shared memory device. */ ++ misc_deregister(&sm_state->dev); ++ ++ /* Remove all proc entries. */ ++ //debugfs_remove_recursive(sm_state->dir_root); ++ ++ /* Stop the videocore shared memory service. */ ++ vc_sm_cma_vchi_stop(&sm_state->sm_handle); ++ ++ /* Free the memory for the state structure. */ ++ mutex_destroy(&sm_state->map_lock); ++ kfree(sm_state); ++ } ++ ++ pr_debug("[%s]: end\n", __func__); ++ return 0; ++} ++ ++/* Get an internal resource handle mapped from the external one. */ ++int vc_sm_cma_int_handle(int handle) ++{ ++ struct dma_buf *dma_buf = (struct dma_buf *)handle; ++ struct vc_sm_buffer *res; ++ ++ /* Validate we can work with this device. */ ++ if (!sm_state || !handle) { ++ pr_err("[%s]: invalid input\n", __func__); ++ return 0; ++ } ++ ++ res = (struct vc_sm_buffer *)dma_buf->priv; ++ return res->vc_handle; ++} ++EXPORT_SYMBOL_GPL(vc_sm_cma_int_handle); ++ ++/* Free a previously allocated shared memory handle and block. */ ++int vc_sm_cma_free(int handle) ++{ ++ struct dma_buf *dma_buf = (struct dma_buf *)handle; ++ ++ /* Validate we can work with this device. */ ++ if (!sm_state || !handle) { ++ pr_err("[%s]: invalid input\n", __func__); ++ return -EPERM; ++ } ++ ++ pr_debug("%s: handle %08x/dmabuf %p\n", __func__, handle, dma_buf); ++ ++ dma_buf_put(dma_buf); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(vc_sm_cma_free); ++ ++/* Import a dmabuf to be shared with VC. */ ++int vc_sm_cma_import_dmabuf(struct dma_buf *src_dmabuf, int *handle) ++{ ++ struct dma_buf *new_dma_buf; ++ struct vc_sm_buffer *res; ++ int ret; ++ ++ /* Validate we can work with this device. */ ++ if (!sm_state || !src_dmabuf || !handle) { ++ pr_err("[%s]: invalid input\n", __func__); ++ return -EPERM; ++ } ++ ++ ret = vc_sm_cma_import_dmabuf_internal(sm_state->data_knl, src_dmabuf, ++ &new_dma_buf); ++ ++ if (!ret) { ++ pr_debug("%s: imported to ptr %p\n", __func__, new_dma_buf); ++ res = (struct vc_sm_buffer *)new_dma_buf->priv; ++ ++ /* Assign valid handle at this time.*/ ++ *handle = (int)new_dma_buf; ++ } else { ++ /* ++ * succeeded in importing the dma_buf, but then ++ * failed to look it up again. How? ++ * Release the fd again. ++ */ ++ pr_err("%s: imported vc_sm_cma_get_buffer failed %d\n", ++ __func__, ret); ++ } ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(vc_sm_cma_import_dmabuf); ++ ++static struct platform_driver bcm2835_vcsm_cma_driver = { ++ .probe = bcm2835_vc_sm_cma_probe, ++ .remove = bcm2835_vc_sm_cma_remove, ++ .driver = { ++ .name = DEVICE_NAME, ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++module_platform_driver(bcm2835_vcsm_cma_driver); ++ ++MODULE_AUTHOR("Dave Stevenson"); ++MODULE_DESCRIPTION("VideoCore CMA Shared Memory Driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:vcsm-cma"); +--- /dev/null ++++ b/drivers/staging/vc04_services/vc-sm-cma/vc_sm.h +@@ -0,0 +1,59 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++ ++/* ++ * VideoCore Shared Memory driver using CMA. ++ * ++ * Copyright: 2018, Raspberry Pi (Trading) Ltd ++ * ++ */ ++ ++#ifndef VC_SM_H ++#define VC_SM_H ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define VC_SM_MAX_NAME_LEN 32 ++ ++enum vc_sm_vpu_mapping_state { ++ VPU_NOT_MAPPED, ++ VPU_MAPPED, ++ VPU_UNMAPPING ++}; ++ ++struct vc_sm_buffer { ++ struct list_head global_buffer_list; /* Global list of buffers. */ ++ ++ size_t size; ++ ++ /* Lock over all the following state for this buffer */ ++ struct mutex lock; ++ struct sg_table *sg_table; ++ struct list_head attachments; ++ ++ char name[VC_SM_MAX_NAME_LEN]; ++ ++ int in_use:1; /* Kernel is still using this resource */ ++ ++ enum vc_sm_vpu_mapping_state vpu_state; ++ u32 vc_handle; /* VideoCore handle for this buffer */ ++ ++ /* DMABUF related fields */ ++ struct dma_buf *import_dma_buf; ++ struct dma_buf *dma_buf; ++ struct dma_buf_attachment *attach; ++ struct sg_table *sgt; ++ dma_addr_t dma_addr; ++ ++ struct vc_sm_privdata_t *private; ++}; ++ ++#endif +--- /dev/null ++++ b/drivers/staging/vc04_services/vc-sm-cma/vc_sm_cma_vchi.c +@@ -0,0 +1,498 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * VideoCore Shared Memory CMA allocator ++ * ++ * Copyright: 2018, Raspberry Pi (Trading) Ltd ++ * Copyright 2011-2012 Broadcom Corporation. All rights reserved. ++ * ++ * Based on vmcs_sm driver from Broadcom Corporation. ++ * ++ */ ++ ++/* ---- Include Files ----------------------------------------------------- */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "vc_sm_cma_vchi.h" ++ ++#define VC_SM_VER 1 ++#define VC_SM_MIN_VER 0 ++ ++/* ---- Private Constants and Types -------------------------------------- */ ++ ++/* Command blocks come from a pool */ ++#define SM_MAX_NUM_CMD_RSP_BLKS 32 ++ ++struct sm_cmd_rsp_blk { ++ struct list_head head; /* To create lists */ ++ /* To be signaled when the response is there */ ++ struct completion cmplt; ++ ++ u16 id; ++ u16 length; ++ ++ u8 msg[VC_SM_MAX_MSG_LEN]; ++ ++ uint32_t wait:1; ++ uint32_t sent:1; ++ uint32_t alloc:1; ++ ++}; ++ ++struct sm_instance { ++ u32 num_connections; ++ VCHI_SERVICE_HANDLE_T vchi_handle[VCHI_MAX_NUM_CONNECTIONS]; ++ struct task_struct *io_thread; ++ struct completion io_cmplt; ++ ++ vpu_event_cb vpu_event; ++ ++ /* Mutex over the following lists */ ++ struct mutex lock; ++ u32 trans_id; ++ struct list_head cmd_list; ++ struct list_head rsp_list; ++ struct list_head dead_list; ++ ++ struct sm_cmd_rsp_blk free_blk[SM_MAX_NUM_CMD_RSP_BLKS]; ++ ++ /* Mutex over the free_list */ ++ struct mutex free_lock; ++ struct list_head free_list; ++ ++ struct semaphore free_sema; ++ ++}; ++ ++/* ---- Private Variables ------------------------------------------------ */ ++ ++/* ---- Private Function Prototypes -------------------------------------- */ ++ ++/* ---- Private Functions ------------------------------------------------ */ ++static int ++bcm2835_vchi_msg_queue(VCHI_SERVICE_HANDLE_T handle, ++ void *data, ++ unsigned int size) ++{ ++ return vchi_queue_kernel_message(handle, ++ data, ++ size); ++} ++ ++static struct ++sm_cmd_rsp_blk *vc_vchi_cmd_create(struct sm_instance *instance, ++ enum vc_sm_msg_type id, void *msg, ++ u32 size, int wait) ++{ ++ struct sm_cmd_rsp_blk *blk; ++ struct vc_sm_msg_hdr_t *hdr; ++ ++ if (down_interruptible(&instance->free_sema)) { ++ blk = kmalloc(sizeof(*blk), GFP_KERNEL); ++ if (!blk) ++ return NULL; ++ ++ blk->alloc = 1; ++ init_completion(&blk->cmplt); ++ } else { ++ mutex_lock(&instance->free_lock); ++ blk = ++ list_first_entry(&instance->free_list, ++ struct sm_cmd_rsp_blk, head); ++ list_del(&blk->head); ++ mutex_unlock(&instance->free_lock); ++ } ++ ++ blk->sent = 0; ++ blk->wait = wait; ++ blk->length = sizeof(*hdr) + size; ++ ++ hdr = (struct vc_sm_msg_hdr_t *)blk->msg; ++ hdr->type = id; ++ mutex_lock(&instance->lock); ++ instance->trans_id++; ++ /* ++ * Retain the top bit for identifying asynchronous events, or VPU cmds. ++ */ ++ instance->trans_id &= ~0x80000000; ++ hdr->trans_id = instance->trans_id; ++ blk->id = instance->trans_id; ++ mutex_unlock(&instance->lock); ++ ++ if (size) ++ memcpy(hdr->body, msg, size); ++ ++ return blk; ++} ++ ++static void ++vc_vchi_cmd_delete(struct sm_instance *instance, struct sm_cmd_rsp_blk *blk) ++{ ++ if (blk->alloc) { ++ kfree(blk); ++ return; ++ } ++ ++ mutex_lock(&instance->free_lock); ++ list_add(&blk->head, &instance->free_list); ++ mutex_unlock(&instance->free_lock); ++ up(&instance->free_sema); ++} ++ ++static void vc_sm_cma_vchi_rx_ack(struct sm_instance *instance, ++ struct sm_cmd_rsp_blk *cmd, ++ struct vc_sm_result_t *reply, ++ u32 reply_len) ++{ ++ mutex_lock(&instance->lock); ++ list_for_each_entry(cmd, ++ &instance->rsp_list, ++ head) { ++ if (cmd->id == reply->trans_id) ++ break; ++ } ++ mutex_unlock(&instance->lock); ++ ++ if (&cmd->head == &instance->rsp_list) { ++ //pr_debug("%s: received response %u, throw away...", ++ pr_err("%s: received response %u, throw away...", ++ __func__, ++ reply->trans_id); ++ } else if (reply_len > sizeof(cmd->msg)) { ++ pr_err("%s: reply too big (%u) %u, throw away...", ++ __func__, reply_len, ++ reply->trans_id); ++ } else { ++ memcpy(cmd->msg, reply, ++ reply_len); ++ complete(&cmd->cmplt); ++ } ++} ++ ++static int vc_sm_cma_vchi_videocore_io(void *arg) ++{ ++ struct sm_instance *instance = arg; ++ struct sm_cmd_rsp_blk *cmd = NULL, *cmd_tmp; ++ struct vc_sm_result_t *reply; ++ u32 reply_len; ++ s32 status; ++ int svc_use = 1; ++ ++ while (1) { ++ if (svc_use) ++ vchi_service_release(instance->vchi_handle[0]); ++ svc_use = 0; ++ if (!wait_for_completion_interruptible(&instance->io_cmplt)) { ++ vchi_service_use(instance->vchi_handle[0]); ++ svc_use = 1; ++ ++ do { ++ /* ++ * Get new command and move it to response list ++ */ ++ mutex_lock(&instance->lock); ++ if (list_empty(&instance->cmd_list)) { ++ /* no more commands to process */ ++ mutex_unlock(&instance->lock); ++ break; ++ } ++ cmd = ++ list_first_entry(&instance->cmd_list, ++ struct sm_cmd_rsp_blk, ++ head); ++ list_move(&cmd->head, &instance->rsp_list); ++ cmd->sent = 1; ++ mutex_unlock(&instance->lock); ++ ++ /* Send the command */ ++ status = bcm2835_vchi_msg_queue( ++ instance->vchi_handle[0], ++ cmd->msg, cmd->length); ++ if (status) { ++ pr_err("%s: failed to queue message (%d)", ++ __func__, status); ++ } ++ ++ /* If no reply is needed then we're done */ ++ if (!cmd->wait) { ++ mutex_lock(&instance->lock); ++ list_del(&cmd->head); ++ mutex_unlock(&instance->lock); ++ vc_vchi_cmd_delete(instance, cmd); ++ continue; ++ } ++ ++ if (status) { ++ complete(&cmd->cmplt); ++ continue; ++ } ++ ++ } while (1); ++ ++ while (!vchi_msg_peek(instance->vchi_handle[0], ++ (void **)&reply, &reply_len, ++ VCHI_FLAGS_NONE)) { ++ if (reply->trans_id & 0x80000000) { ++ /* Async event or cmd from the VPU */ ++ if (instance->vpu_event) ++ instance->vpu_event( ++ instance, reply, ++ reply_len); ++ } else { ++ vc_sm_cma_vchi_rx_ack(instance, cmd, ++ reply, reply_len); ++ } ++ ++ vchi_msg_remove(instance->vchi_handle[0]); ++ } ++ ++ /* Go through the dead list and free them */ ++ mutex_lock(&instance->lock); ++ list_for_each_entry_safe(cmd, cmd_tmp, ++ &instance->dead_list, head) { ++ list_del(&cmd->head); ++ vc_vchi_cmd_delete(instance, cmd); ++ } ++ mutex_unlock(&instance->lock); ++ } ++ } ++ ++ return 0; ++} ++ ++static void vc_sm_cma_vchi_callback(void *param, ++ const VCHI_CALLBACK_REASON_T reason, ++ void *msg_handle) ++{ ++ struct sm_instance *instance = param; ++ ++ (void)msg_handle; ++ ++ switch (reason) { ++ case VCHI_CALLBACK_MSG_AVAILABLE: ++ complete(&instance->io_cmplt); ++ break; ++ ++ case VCHI_CALLBACK_SERVICE_CLOSED: ++ pr_info("%s: service CLOSED!!", __func__); ++ default: ++ break; ++ } ++} ++ ++struct sm_instance *vc_sm_cma_vchi_init(VCHI_INSTANCE_T vchi_instance, ++ unsigned int num_connections, ++ vpu_event_cb vpu_event) ++{ ++ u32 i; ++ struct sm_instance *instance; ++ int status; ++ ++ pr_debug("%s: start", __func__); ++ ++ if (num_connections > VCHI_MAX_NUM_CONNECTIONS) { ++ pr_err("%s: unsupported number of connections %u (max=%u)", ++ __func__, num_connections, VCHI_MAX_NUM_CONNECTIONS); ++ ++ goto err_null; ++ } ++ /* Allocate memory for this instance */ ++ instance = kzalloc(sizeof(*instance), GFP_KERNEL); ++ ++ /* Misc initialisations */ ++ mutex_init(&instance->lock); ++ init_completion(&instance->io_cmplt); ++ INIT_LIST_HEAD(&instance->cmd_list); ++ INIT_LIST_HEAD(&instance->rsp_list); ++ INIT_LIST_HEAD(&instance->dead_list); ++ INIT_LIST_HEAD(&instance->free_list); ++ sema_init(&instance->free_sema, SM_MAX_NUM_CMD_RSP_BLKS); ++ mutex_init(&instance->free_lock); ++ for (i = 0; i < SM_MAX_NUM_CMD_RSP_BLKS; i++) { ++ init_completion(&instance->free_blk[i].cmplt); ++ list_add(&instance->free_blk[i].head, &instance->free_list); ++ } ++ ++ /* Open the VCHI service connections */ ++ instance->num_connections = num_connections; ++ for (i = 0; i < num_connections; i++) { ++ SERVICE_CREATION_T params = { ++ .version = VCHI_VERSION_EX(VC_SM_VER, VC_SM_MIN_VER), ++ .service_id = VC_SM_SERVER_NAME, ++ .callback = vc_sm_cma_vchi_callback, ++ .callback_param = instance, ++ }; ++ ++ status = vchi_service_open(vchi_instance, ++ ¶ms, &instance->vchi_handle[i]); ++ if (status) { ++ pr_err("%s: failed to open VCHI service (%d)", ++ __func__, status); ++ ++ goto err_close_services; ++ } ++ } ++ ++ /* Create the thread which takes care of all io to/from videoocore. */ ++ instance->io_thread = kthread_create(&vc_sm_cma_vchi_videocore_io, ++ (void *)instance, "SMIO"); ++ if (!instance->io_thread) { ++ pr_err("%s: failed to create SMIO thread", __func__); ++ ++ goto err_close_services; ++ } ++ instance->vpu_event = vpu_event; ++ set_user_nice(instance->io_thread, -10); ++ wake_up_process(instance->io_thread); ++ ++ pr_debug("%s: success - instance 0x%x", __func__, ++ (unsigned int)instance); ++ return instance; ++ ++err_close_services: ++ for (i = 0; i < instance->num_connections; i++) { ++ if (instance->vchi_handle[i]) ++ vchi_service_close(instance->vchi_handle[i]); ++ } ++ kfree(instance); ++err_null: ++ pr_debug("%s: FAILED", __func__); ++ return NULL; ++} ++ ++int vc_sm_cma_vchi_stop(struct sm_instance **handle) ++{ ++ struct sm_instance *instance; ++ u32 i; ++ ++ if (!handle) { ++ pr_err("%s: invalid pointer to handle %p", __func__, handle); ++ goto lock; ++ } ++ ++ if (!*handle) { ++ pr_err("%s: invalid handle %p", __func__, *handle); ++ goto lock; ++ } ++ ++ instance = *handle; ++ ++ /* Close all VCHI service connections */ ++ for (i = 0; i < instance->num_connections; i++) { ++ s32 success; ++ ++ vchi_service_use(instance->vchi_handle[i]); ++ ++ success = vchi_service_close(instance->vchi_handle[i]); ++ } ++ ++ kfree(instance); ++ ++ *handle = NULL; ++ return 0; ++ ++lock: ++ return -EINVAL; ++} ++ ++static int vc_sm_cma_vchi_send_msg(struct sm_instance *handle, ++ enum vc_sm_msg_type msg_id, void *msg, ++ u32 msg_size, void *result, u32 result_size, ++ u32 *cur_trans_id, u8 wait_reply) ++{ ++ int status = 0; ++ struct sm_instance *instance = handle; ++ struct sm_cmd_rsp_blk *cmd_blk; ++ ++ if (!handle) { ++ pr_err("%s: invalid handle", __func__); ++ return -EINVAL; ++ } ++ if (!msg) { ++ pr_err("%s: invalid msg pointer", __func__); ++ return -EINVAL; ++ } ++ ++ cmd_blk = ++ vc_vchi_cmd_create(instance, msg_id, msg, msg_size, wait_reply); ++ if (!cmd_blk) { ++ pr_err("[%s]: failed to allocate global tracking resource", ++ __func__); ++ return -ENOMEM; ++ } ++ ++ if (cur_trans_id) ++ *cur_trans_id = cmd_blk->id; ++ ++ mutex_lock(&instance->lock); ++ list_add_tail(&cmd_blk->head, &instance->cmd_list); ++ mutex_unlock(&instance->lock); ++ complete(&instance->io_cmplt); ++ ++ if (!wait_reply) ++ /* We're done */ ++ return 0; ++ ++ /* Wait for the response */ ++ if (wait_for_completion_interruptible(&cmd_blk->cmplt)) { ++ mutex_lock(&instance->lock); ++ if (!cmd_blk->sent) { ++ list_del(&cmd_blk->head); ++ mutex_unlock(&instance->lock); ++ vc_vchi_cmd_delete(instance, cmd_blk); ++ return -ENXIO; ++ } ++ ++ list_move(&cmd_blk->head, &instance->dead_list); ++ mutex_unlock(&instance->lock); ++ complete(&instance->io_cmplt); ++ return -EINTR; /* We're done */ ++ } ++ ++ if (result && result_size) { ++ memcpy(result, cmd_blk->msg, result_size); ++ } else { ++ struct vc_sm_result_t *res = ++ (struct vc_sm_result_t *)cmd_blk->msg; ++ status = (res->success == 0) ? 0 : -ENXIO; ++ } ++ ++ mutex_lock(&instance->lock); ++ list_del(&cmd_blk->head); ++ mutex_unlock(&instance->lock); ++ vc_vchi_cmd_delete(instance, cmd_blk); ++ return status; ++} ++ ++int vc_sm_cma_vchi_free(struct sm_instance *handle, struct vc_sm_free_t *msg, ++ u32 *cur_trans_id) ++{ ++ return vc_sm_cma_vchi_send_msg(handle, VC_SM_MSG_TYPE_FREE, ++ msg, sizeof(*msg), 0, 0, cur_trans_id, 0); ++} ++ ++int vc_sm_cma_vchi_import(struct sm_instance *handle, struct vc_sm_import *msg, ++ struct vc_sm_import_result *result, u32 *cur_trans_id) ++{ ++ return vc_sm_cma_vchi_send_msg(handle, VC_SM_MSG_TYPE_IMPORT, ++ msg, sizeof(*msg), result, sizeof(*result), ++ cur_trans_id, 1); ++} ++ ++int vc_sm_cma_vchi_client_version(struct sm_instance *handle, ++ struct vc_sm_version *msg, ++ struct vc_sm_result_t *result, ++ u32 *cur_trans_id) ++{ ++ return vc_sm_cma_vchi_send_msg(handle, VC_SM_MSG_TYPE_CLIENT_VERSION, ++ //msg, sizeof(*msg), result, sizeof(*result), ++ //cur_trans_id, 1); ++ msg, sizeof(*msg), NULL, 0, ++ cur_trans_id, 0); ++} +--- /dev/null ++++ b/drivers/staging/vc04_services/vc-sm-cma/vc_sm_cma_vchi.h +@@ -0,0 +1,59 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++ ++/* ++ * VideoCore Shared Memory CMA allocator ++ * ++ * Copyright: 2018, Raspberry Pi (Trading) Ltd ++ * Copyright 2011-2012 Broadcom Corporation. All rights reserved. ++ * ++ * Based on vmcs_sm driver from Broadcom Corporation. ++ * ++ */ ++ ++#ifndef __VC_SM_CMA_VCHI_H__INCLUDED__ ++#define __VC_SM_CMA_VCHI_H__INCLUDED__ ++ ++#include "interface/vchi/vchi.h" ++ ++#include "vc_sm_defs.h" ++ ++/* ++ * Forward declare. ++ */ ++struct sm_instance; ++ ++typedef void (*vpu_event_cb)(struct sm_instance *instance, ++ struct vc_sm_result_t *reply, int reply_len); ++ ++/* ++ * Initialize the shared memory service, opens up vchi connection to talk to it. ++ */ ++struct sm_instance *vc_sm_cma_vchi_init(VCHI_INSTANCE_T vchi_instance, ++ unsigned int num_connections, ++ vpu_event_cb vpu_event); ++ ++/* ++ * Terminates the shared memory service. ++ */ ++int vc_sm_cma_vchi_stop(struct sm_instance **handle); ++ ++/* ++ * Ask the shared memory service to free up some memory that was previously ++ * allocated by the vc_sm_cma_vchi_alloc function call. ++ */ ++int vc_sm_cma_vchi_free(struct sm_instance *handle, struct vc_sm_free_t *msg, ++ u32 *cur_trans_id); ++ ++/* ++ * Import a contiguous block of memory and wrap it in a GPU MEM_HANDLE_T. ++ */ ++int vc_sm_cma_vchi_import(struct sm_instance *handle, struct vc_sm_import *msg, ++ struct vc_sm_import_result *result, ++ u32 *cur_trans_id); ++ ++int vc_sm_cma_vchi_client_version(struct sm_instance *handle, ++ struct vc_sm_version *msg, ++ struct vc_sm_result_t *result, ++ u32 *cur_trans_id); ++ ++#endif /* __VC_SM_CMA_VCHI_H__INCLUDED__ */ +--- /dev/null ++++ b/drivers/staging/vc04_services/vc-sm-cma/vc_sm_defs.h +@@ -0,0 +1,298 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++ ++/* ++ * VideoCore Shared Memory CMA allocator ++ * ++ * Copyright: 2018, Raspberry Pi (Trading) Ltd ++ * ++ * Based on vc_sm_defs.h from the vmcs_sm driver Copyright Broadcom Corporation. ++ * All IPC messages are copied across to this file, even if the vc-sm-cma ++ * driver is not currently using them. ++ * ++ **************************************************************************** ++ */ ++ ++#ifndef __VC_SM_DEFS_H__INCLUDED__ ++#define __VC_SM_DEFS_H__INCLUDED__ ++ ++/* FourCC code used for VCHI connection */ ++#define VC_SM_SERVER_NAME MAKE_FOURCC("SMEM") ++ ++/* Maximum message length */ ++#define VC_SM_MAX_MSG_LEN (sizeof(union vc_sm_msg_union_t) + \ ++ sizeof(struct vc_sm_msg_hdr_t)) ++#define VC_SM_MAX_RSP_LEN (sizeof(union vc_sm_msg_union_t)) ++ ++/* Resource name maximum size */ ++#define VC_SM_RESOURCE_NAME 32 ++ ++/* ++ * Version to be reported to the VPU ++ * VPU assumes 0 (aka 1) which does not require the released callback, nor ++ * expect the client to handle VC_MEM_REQUESTS. ++ * Version 2 requires the released callback, and must support VC_MEM_REQUESTS. ++ */ ++#define VC_SM_PROTOCOL_VERSION 2 ++ ++enum vc_sm_msg_type { ++ /* Message types supported for HOST->VC direction */ ++ ++ /* Allocate shared memory block */ ++ VC_SM_MSG_TYPE_ALLOC, ++ /* Lock allocated shared memory block */ ++ VC_SM_MSG_TYPE_LOCK, ++ /* Unlock allocated shared memory block */ ++ VC_SM_MSG_TYPE_UNLOCK, ++ /* Unlock allocated shared memory block, do not answer command */ ++ VC_SM_MSG_TYPE_UNLOCK_NOANS, ++ /* Free shared memory block */ ++ VC_SM_MSG_TYPE_FREE, ++ /* Resize a shared memory block */ ++ VC_SM_MSG_TYPE_RESIZE, ++ /* Walk the allocated shared memory block(s) */ ++ VC_SM_MSG_TYPE_WALK_ALLOC, ++ ++ /* A previously applied action will need to be reverted */ ++ VC_SM_MSG_TYPE_ACTION_CLEAN, ++ ++ /* ++ * Import a physical address and wrap into a MEM_HANDLE_T. ++ * Release with VC_SM_MSG_TYPE_FREE. ++ */ ++ VC_SM_MSG_TYPE_IMPORT, ++ /* ++ *Tells VC the protocol version supported by this client. ++ * 2 supports the async/cmd messages from the VPU for final release ++ * of memory, and for VC allocations. ++ */ ++ VC_SM_MSG_TYPE_CLIENT_VERSION, ++ /* Response to VC request for memory */ ++ VC_SM_MSG_TYPE_VC_MEM_REQUEST_REPLY, ++ ++ /* ++ * Asynchronous/cmd messages supported for VC->HOST direction. ++ * Signalled by setting the top bit in vc_sm_result_t trans_id. ++ */ ++ ++ /* ++ * VC has finished with an imported memory allocation. ++ * Release any Linux reference counts on the underlying block. ++ */ ++ VC_SM_MSG_TYPE_RELEASED, ++ /* VC request for memory */ ++ VC_SM_MSG_TYPE_VC_MEM_REQUEST, ++ ++ VC_SM_MSG_TYPE_MAX ++}; ++ ++/* Type of memory to be allocated */ ++enum vc_sm_alloc_type_t { ++ VC_SM_ALLOC_CACHED, ++ VC_SM_ALLOC_NON_CACHED, ++}; ++ ++/* Message header for all messages in HOST->VC direction */ ++struct vc_sm_msg_hdr_t { ++ u32 type; ++ u32 trans_id; ++ u8 body[0]; ++ ++}; ++ ++/* Request to allocate memory (HOST->VC) */ ++struct vc_sm_alloc_t { ++ /* type of memory to allocate */ ++ enum vc_sm_alloc_type_t type; ++ /* byte amount of data to allocate per unit */ ++ u32 base_unit; ++ /* number of unit to allocate */ ++ u32 num_unit; ++ /* alignment to be applied on allocation */ ++ u32 alignment; ++ /* identity of who allocated this block */ ++ u32 allocator; ++ /* resource name (for easier tracking on vc side) */ ++ char name[VC_SM_RESOURCE_NAME]; ++ ++}; ++ ++/* Result of a requested memory allocation (VC->HOST) */ ++struct vc_sm_alloc_result_t { ++ /* Transaction identifier */ ++ u32 trans_id; ++ ++ /* Resource handle */ ++ u32 res_handle; ++ /* Pointer to resource buffer */ ++ u32 res_mem; ++ /* Resource base size (bytes) */ ++ u32 res_base_size; ++ /* Resource number */ ++ u32 res_num; ++ ++}; ++ ++/* Request to free a previously allocated memory (HOST->VC) */ ++struct vc_sm_free_t { ++ /* Resource handle (returned from alloc) */ ++ u32 res_handle; ++ /* Resource buffer (returned from alloc) */ ++ u32 res_mem; ++ ++}; ++ ++/* Request to lock a previously allocated memory (HOST->VC) */ ++struct vc_sm_lock_unlock_t { ++ /* Resource handle (returned from alloc) */ ++ u32 res_handle; ++ /* Resource buffer (returned from alloc) */ ++ u32 res_mem; ++ ++}; ++ ++/* Request to resize a previously allocated memory (HOST->VC) */ ++struct vc_sm_resize_t { ++ /* Resource handle (returned from alloc) */ ++ u32 res_handle; ++ /* Resource buffer (returned from alloc) */ ++ u32 res_mem; ++ /* Resource *new* size requested (bytes) */ ++ u32 res_new_size; ++ ++}; ++ ++/* Result of a requested memory lock (VC->HOST) */ ++struct vc_sm_lock_result_t { ++ /* Transaction identifier */ ++ u32 trans_id; ++ ++ /* Resource handle */ ++ u32 res_handle; ++ /* Pointer to resource buffer */ ++ u32 res_mem; ++ /* ++ * Pointer to former resource buffer if the memory ++ * was reallocated ++ */ ++ u32 res_old_mem; ++ ++}; ++ ++/* Generic result for a request (VC->HOST) */ ++struct vc_sm_result_t { ++ /* Transaction identifier */ ++ u32 trans_id; ++ ++ s32 success; ++ ++}; ++ ++/* Request to revert a previously applied action (HOST->VC) */ ++struct vc_sm_action_clean_t { ++ /* Action of interest */ ++ enum vc_sm_msg_type res_action; ++ /* Transaction identifier for the action of interest */ ++ u32 action_trans_id; ++ ++}; ++ ++/* Request to remove all data associated with a given allocator (HOST->VC) */ ++struct vc_sm_free_all_t { ++ /* Allocator identifier */ ++ u32 allocator; ++}; ++ ++/* Request to import memory (HOST->VC) */ ++struct vc_sm_import { ++ /* type of memory to allocate */ ++ enum vc_sm_alloc_type_t type; ++ /* pointer to the VC (ie physical) address of the allocated memory */ ++ u32 addr; ++ /* size of buffer */ ++ u32 size; ++ /* opaque handle returned in RELEASED messages */ ++ u32 kernel_id; ++ /* Allocator identifier */ ++ u32 allocator; ++ /* resource name (for easier tracking on vc side) */ ++ char name[VC_SM_RESOURCE_NAME]; ++}; ++ ++/* Result of a requested memory import (VC->HOST) */ ++struct vc_sm_import_result { ++ /* Transaction identifier */ ++ u32 trans_id; ++ ++ /* Resource handle */ ++ u32 res_handle; ++}; ++ ++/* Notification that VC has finished with an allocation (VC->HOST) */ ++struct vc_sm_released { ++ /* cmd type / trans_id */ ++ u32 cmd; ++ ++ /* pointer to the VC (ie physical) address of the allocated memory */ ++ u32 addr; ++ /* size of buffer */ ++ u32 size; ++ /* opaque handle returned in RELEASED messages */ ++ u32 kernel_id; ++ u32 vc_handle; ++}; ++ ++/* ++ * Client informing VC as to the protocol version it supports. ++ * >=2 requires the released callback, and supports VC asking for memory. ++ * Failure means that the firmware doesn't support this call, and therefore the ++ * client should either fail, or NOT rely on getting the released callback. ++ */ ++struct vc_sm_version { ++ u32 version; ++}; ++ ++/* Request FROM VideoCore for some memory */ ++struct vc_sm_vc_mem_request { ++ /* cmd type */ ++ u32 cmd; ++ ++ /* trans_id (from VPU) */ ++ u32 trans_id; ++ /* size of buffer */ ++ u32 size; ++ /* alignment of buffer */ ++ u32 align; ++ /* resource name (for easier tracking) */ ++ char name[VC_SM_RESOURCE_NAME]; ++}; ++ ++/* Response from the kernel to provide the VPU with some memory */ ++struct vc_sm_vc_mem_request_result { ++ /* Transaction identifier for the VPU */ ++ u32 trans_id; ++ /* pointer to the physical address of the allocated memory */ ++ u32 addr; ++ /* opaque handle returned in RELEASED messages */ ++ u32 kernel_id; ++}; ++ ++/* Union of ALL messages */ ++union vc_sm_msg_union_t { ++ struct vc_sm_alloc_t alloc; ++ struct vc_sm_alloc_result_t alloc_result; ++ struct vc_sm_free_t free; ++ struct vc_sm_lock_unlock_t lock_unlock; ++ struct vc_sm_action_clean_t action_clean; ++ struct vc_sm_resize_t resize; ++ struct vc_sm_lock_result_t lock_result; ++ struct vc_sm_result_t result; ++ struct vc_sm_free_all_t free_all; ++ struct vc_sm_import import; ++ struct vc_sm_import_result import_result; ++ struct vc_sm_version version; ++ struct vc_sm_released released; ++ struct vc_sm_vc_mem_request vc_request; ++ struct vc_sm_vc_mem_request_result vc_request_result; ++}; ++ ++#endif /* __VC_SM_DEFS_H__INCLUDED__ */ +--- /dev/null ++++ b/drivers/staging/vc04_services/vc-sm-cma/vc_sm_knl.h +@@ -0,0 +1,28 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++ ++/* ++ * VideoCore Shared Memory CMA allocator ++ * ++ * Copyright: 2018, Raspberry Pi (Trading) Ltd ++ * ++ * Based on vc_sm_defs.h from the vmcs_sm driver Copyright Broadcom Corporation. ++ * ++ */ ++ ++#ifndef __VC_SM_KNL_H__INCLUDED__ ++#define __VC_SM_KNL_H__INCLUDED__ ++ ++#if !defined(__KERNEL__) ++#error "This interface is for kernel use only..." ++#endif ++ ++/* Free a previously allocated or imported shared memory handle and block. */ ++int vc_sm_cma_free(int handle); ++ ++/* Get an internal resource handle mapped from the external one. */ ++int vc_sm_cma_int_handle(int handle); ++ ++/* Import a block of memory into the GPU space. */ ++int vc_sm_cma_import_dmabuf(struct dma_buf *dmabuf, int *handle); ++ ++#endif /* __VC_SM_KNL_H__INCLUDED__ */