From 7f3a4257159dea8e7ef66d1a539dc6df708b8ed3 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 7 Aug 2024 15:17:46 +0200 Subject: Adding upstream version 6.10.3. Signed-off-by: Daniel Baumann --- drivers/staging/vc04_services/Kconfig | 36 +-- drivers/staging/vc04_services/Makefile | 1 - .../staging/vc04_services/bcm2835-audio/Kconfig | 2 +- .../vc04_services/bcm2835-audio/bcm2835-vchiq.c | 5 +- .../vc04_services/bcm2835-camera/bcm2835-camera.c | 4 +- .../include/linux/raspberrypi/vchiq.h | 4 +- drivers/staging/vc04_services/interface/TODO | 15 -- .../vc04_services/interface/vchiq_arm/vchiq_arm.c | 271 +++++++++++---------- .../vc04_services/interface/vchiq_arm/vchiq_arm.h | 41 +++- .../vc04_services/interface/vchiq_arm/vchiq_bus.c | 13 + .../vc04_services/interface/vchiq_arm/vchiq_bus.h | 3 + .../interface/vchiq_arm/vchiq_connected.c | 74 ------ .../interface/vchiq_arm/vchiq_connected.h | 12 - .../vc04_services/interface/vchiq_arm/vchiq_core.c | 67 ++--- .../vc04_services/interface/vchiq_arm/vchiq_core.h | 22 +- .../interface/vchiq_arm/vchiq_debugfs.c | 11 +- .../interface/vchiq_arm/vchiq_debugfs.h | 2 +- .../vc04_services/interface/vchiq_arm/vchiq_dev.c | 40 +-- .../staging/vc04_services/vchiq-mmal/mmal-vchiq.c | 14 +- .../staging/vc04_services/vchiq-mmal/mmal-vchiq.h | 32 +-- 20 files changed, 326 insertions(+), 343 deletions(-) delete mode 100644 drivers/staging/vc04_services/interface/vchiq_arm/vchiq_connected.c delete mode 100644 drivers/staging/vc04_services/interface/vchiq_arm/vchiq_connected.h (limited to 'drivers/staging/vc04_services') diff --git a/drivers/staging/vc04_services/Kconfig b/drivers/staging/vc04_services/Kconfig index 31e58c9d1a..ccc8e15886 100644 --- a/drivers/staging/vc04_services/Kconfig +++ b/drivers/staging/vc04_services/Kconfig @@ -16,27 +16,33 @@ config BCM2835_VCHIQ depends on HAS_DMA imply VCHIQ_CDEV help - Broadcom BCM2835 and similar SoCs have a VPU called VideoCore. This config - enables the VCHIQ driver, which implements a messaging interface between - the kernel and the firmware running on VideoCore. Other drivers use this - interface to communicate to the VPU. More specifically, the VCHIQ driver is - used by audio/video and camera drivers as well as for implementing MMAL - API, which is in turn used by several multimedia services on the BCM2835 - family of SoCs. - Defaults to Y when the Broadcom Videocore services are included in - the build, N otherwise. + Broadcom BCM2835 and similar SoCs have a VPU called VideoCore. + This config enables the VCHIQ driver, which implements a + messaging interface between the kernel and the firmware running + on VideoCore. Other drivers use this interface to communicate to + the VPU. More specifically, the VCHIQ driver is used by + audio/video and camera drivers as well as for implementing MMAL + API, which is in turn used by several multimedia services on the + BCM2835 family of SoCs. + + Defaults to Y when the Broadcom Videocore services are included + in the build, N otherwise. if BCM2835_VCHIQ config VCHIQ_CDEV bool "VCHIQ Character Driver" help - Enable the creation of VCHIQ character driver. The cdev exposes ioctls used - by userspace libraries and testing tools to interact with VideoCore, via - the VCHIQ core driver (Check BCM2835_VCHIQ for more info). - This can be set to 'N' if the VideoCore communication is not needed by - userspace but only by other kernel modules (like bcm2835-audio). If not - sure, set this to 'Y'. + Enable the creation of VCHIQ character driver. The cdev exposes + ioctls used by userspace libraries and testing tools to interact + with VideoCore, via the VCHIQ core driver (Check BCM2835_VCHIQ + for more info). + + This can be set to 'N' if the VideoCore communication is not + needed by userspace but only by other kernel modules + (like bcm2835-audio). + + If not sure, set this to 'Y'. endif diff --git a/drivers/staging/vc04_services/Makefile b/drivers/staging/vc04_services/Makefile index e8b897a7b9..dad3789522 100644 --- a/drivers/staging/vc04_services/Makefile +++ b/drivers/staging/vc04_services/Makefile @@ -6,7 +6,6 @@ vchiq-objs := \ interface/vchiq_arm/vchiq_arm.o \ interface/vchiq_arm/vchiq_bus.o \ interface/vchiq_arm/vchiq_debugfs.o \ - interface/vchiq_arm/vchiq_connected.o \ ifdef CONFIG_VCHIQ_CDEV vchiq-objs += interface/vchiq_arm/vchiq_dev.o diff --git a/drivers/staging/vc04_services/bcm2835-audio/Kconfig b/drivers/staging/vc04_services/bcm2835-audio/Kconfig index 7f22f6c850..7fbb29d3c3 100644 --- a/drivers/staging/vc04_services/bcm2835-audio/Kconfig +++ b/drivers/staging/vc04_services/bcm2835-audio/Kconfig @@ -8,4 +8,4 @@ config SND_BCM2835 Say Y or M if you want to support BCM2835 built in audio. This driver handles both 3.5mm and HDMI audio, by leveraging the VCHIQ messaging interface between the kernel and the firmware - running on VideoCore. \ No newline at end of file + running on VideoCore. diff --git a/drivers/staging/vc04_services/bcm2835-audio/bcm2835-vchiq.c b/drivers/staging/vc04_services/bcm2835-audio/bcm2835-vchiq.c index d74110ca17..133ed15f3d 100644 --- a/drivers/staging/vc04_services/bcm2835-audio/bcm2835-vchiq.c +++ b/drivers/staging/vc04_services/bcm2835-audio/bcm2835-vchiq.c @@ -7,6 +7,8 @@ #include "bcm2835.h" #include "vc_vchi_audioserv_defs.h" +#include "../interface/vchiq_arm/vchiq_arm.h" + struct bcm2835_audio_instance { struct device *dev; unsigned int service_handle; @@ -175,10 +177,11 @@ static void vc_vchi_audio_deinit(struct bcm2835_audio_instance *instance) int bcm2835_new_vchi_ctx(struct device *dev, struct bcm2835_vchi_ctx *vchi_ctx) { + struct vchiq_drv_mgmt *mgmt = dev_get_drvdata(dev->parent); int ret; /* Initialize and create a VCHI connection */ - ret = vchiq_initialise(&vchi_ctx->instance); + ret = vchiq_initialise(&mgmt->state, &vchi_ctx->instance); if (ret) { dev_err(dev, "failed to initialise VCHI instance (ret=%d)\n", ret); diff --git a/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c b/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c index c3ba490e53..b3599ec629 100644 --- a/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c +++ b/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c @@ -1555,7 +1555,7 @@ static int mmal_init(struct bcm2835_mmal_dev *dev) u32 param_size; struct vchiq_mmal_component *camera; - ret = vchiq_mmal_init(&dev->instance); + ret = vchiq_mmal_init(dev->v4l2_dev.dev, &dev->instance); if (ret < 0) { v4l2_err(&dev->v4l2_dev, "%s: vchiq mmal init failed %d\n", __func__, ret); @@ -1854,7 +1854,7 @@ static int bcm2835_mmal_probe(struct vchiq_device *device) return ret; } - ret = vchiq_mmal_init(&instance); + ret = vchiq_mmal_init(&device->dev, &instance); if (ret < 0) return ret; diff --git a/drivers/staging/vc04_services/include/linux/raspberrypi/vchiq.h b/drivers/staging/vc04_services/include/linux/raspberrypi/vchiq.h index 52e106f117..6c40d8c1dd 100644 --- a/drivers/staging/vc04_services/include/linux/raspberrypi/vchiq.h +++ b/drivers/staging/vc04_services/include/linux/raspberrypi/vchiq.h @@ -48,6 +48,7 @@ struct vchiq_element { }; struct vchiq_instance; +struct vchiq_state; struct vchiq_service_base { int fourcc; @@ -78,7 +79,8 @@ struct vchiq_service_params_kernel { short version_min; /* Update for incompatible changes */ }; -extern int vchiq_initialise(struct vchiq_instance **pinstance); +extern int vchiq_initialise(struct vchiq_state *state, + struct vchiq_instance **pinstance); extern int vchiq_shutdown(struct vchiq_instance *instance); extern int vchiq_connect(struct vchiq_instance *instance); extern int vchiq_open_service(struct vchiq_instance *instance, diff --git a/drivers/staging/vc04_services/interface/TODO b/drivers/staging/vc04_services/interface/TODO index 05eb5140d0..05f129c0c2 100644 --- a/drivers/staging/vc04_services/interface/TODO +++ b/drivers/staging/vc04_services/interface/TODO @@ -28,27 +28,12 @@ variables avoided. A short top-down description of this driver's architecture (function of kthreads, userspace, limitations) could be very helpful for reviewers. -* Review and comment memory barriers - -There is a heavy use of memory barriers in this driver, it would be very -beneficial to go over all of them and, if correct, comment on their merits. -Extra points to whomever confidently reviews the remote_event_*() family of -functions. - * Reformat core code with more sane indentations The code follows the 80 characters limitation yet tends to go 3 or 4 levels of indentation deep making it very unpleasant to read. This is specially relevant in the character driver ioctl code and in the core thread functions. -* Get rid of all non essential global structures and create a proper per -device structure - -The first thing one generally sees in a probe function is a memory allocation -for all the device specific data. This structure is then passed all over the -driver. This is good practice since it makes the driver work regardless of the -number of devices probed. - * Clean up Sparse warnings from __user annotations. See vchiq_irq_queue_bulk_tx_rx(). Ensure that the address of "&waiter->bulk_waiter" is never disclosed to userspace. diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c index 1579bd4e52..5f518e5a92 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c @@ -36,7 +36,6 @@ #include "vchiq_arm.h" #include "vchiq_bus.h" #include "vchiq_debugfs.h" -#include "vchiq_connected.h" #include "vchiq_pagelist.h" #define DEVICE_NAME "vchiq" @@ -60,9 +59,6 @@ #define KEEPALIVE_VER 1 #define KEEPALIVE_VER_MIN KEEPALIVE_VER -DEFINE_SPINLOCK(msg_queue_spinlock); -struct vchiq_state g_state; - /* * The devices implemented in the VCHIQ firmware are not discoverable, * so we need to maintain a list of them in order to register them with @@ -71,16 +67,11 @@ struct vchiq_state g_state; static struct vchiq_device *bcm2835_audio; static struct vchiq_device *bcm2835_camera; -struct vchiq_drvdata { - const unsigned int cache_line_size; - struct rpi_firmware *fw; -}; - -static struct vchiq_drvdata bcm2835_drvdata = { +static const struct vchiq_platform_info bcm2835_info = { .cache_line_size = 32, }; -static struct vchiq_drvdata bcm2836_drvdata = { +static const struct vchiq_platform_info bcm2836_info = { .cache_line_size = 64, }; @@ -135,25 +126,6 @@ struct vchiq_pagelist_info { unsigned int scatterlist_mapped; }; -static void __iomem *g_regs; -/* This value is the size of the L2 cache lines as understood by the - * VPU firmware, which determines the required alignment of the - * offsets/sizes in pagelists. - * - * Modern VPU firmware looks for a DT "cache-line-size" property in - * the VCHIQ node and will overwrite it with the actual L2 cache size, - * which the kernel must then respect. That property was rejected - * upstream, so we have to use the VPU firmware's compatibility value - * of 32. - */ -static unsigned int g_cache_line_size = 32; -static unsigned int g_fragments_size; -static char *g_fragments_base; -static char *g_free_fragments; -static struct semaphore g_free_fragments_sema; - -static DEFINE_SEMAPHORE(g_free_fragments_mutex, 1); - static int vchiq_blocking_bulk_transfer(struct vchiq_instance *instance, unsigned int handle, void *data, unsigned int size, enum vchiq_bulk_dir dir); @@ -162,11 +134,14 @@ static irqreturn_t vchiq_doorbell_irq(int irq, void *dev_id) { struct vchiq_state *state = dev_id; + struct vchiq_drv_mgmt *mgmt; irqreturn_t ret = IRQ_NONE; unsigned int status; + mgmt = dev_get_drvdata(state->dev); + /* Read (and clear) the doorbell */ - status = readl(g_regs + BELL0); + status = readl(mgmt->regs + BELL0); if (status & ARM_DS_ACTIVE) { /* Was the doorbell rung? */ remote_event_pollall(state); @@ -205,6 +180,56 @@ is_adjacent_block(u32 *addrs, u32 addr, unsigned int k) return tmp == (addr & PAGE_MASK); } +/* + * This function is called by the vchiq stack once it has been connected to + * the videocore and clients can start to use the stack. + */ +static void vchiq_call_connected_callbacks(struct vchiq_drv_mgmt *drv_mgmt) +{ + int i; + + if (mutex_lock_killable(&drv_mgmt->connected_mutex)) + return; + + for (i = 0; i < drv_mgmt->num_deferred_callbacks; i++) + drv_mgmt->deferred_callback[i](); + + drv_mgmt->num_deferred_callbacks = 0; + drv_mgmt->connected = true; + mutex_unlock(&drv_mgmt->connected_mutex); +} + +/* + * This function is used to defer initialization until the vchiq stack is + * initialized. If the stack is already initialized, then the callback will + * be made immediately, otherwise it will be deferred until + * vchiq_call_connected_callbacks is called. + */ +void vchiq_add_connected_callback(struct vchiq_device *device, void (*callback)(void)) +{ + struct vchiq_drv_mgmt *drv_mgmt = device->drv_mgmt; + + if (mutex_lock_killable(&drv_mgmt->connected_mutex)) + return; + + if (drv_mgmt->connected) { + /* We're already connected. Call the callback immediately. */ + callback(); + } else { + if (drv_mgmt->num_deferred_callbacks >= VCHIQ_DRV_MAX_CALLBACKS) { + dev_err(&device->dev, + "core: deferred callbacks(%d) exceeded the maximum limit(%d)\n", + drv_mgmt->num_deferred_callbacks, VCHIQ_DRV_MAX_CALLBACKS); + } else { + drv_mgmt->deferred_callback[drv_mgmt->num_deferred_callbacks] = + callback; + drv_mgmt->num_deferred_callbacks++; + } + } + mutex_unlock(&drv_mgmt->connected_mutex); +} +EXPORT_SYMBOL(vchiq_add_connected_callback); + /* There is a potential problem with partial cache lines (pages?) * at the ends of the block when reading. If the CPU accessed anything in * the same line (page?) then it may have pulled old data into the cache, @@ -217,6 +242,7 @@ static struct vchiq_pagelist_info * create_pagelist(struct vchiq_instance *instance, char *buf, char __user *ubuf, size_t count, unsigned short type) { + struct vchiq_drv_mgmt *drv_mgmt; struct pagelist *pagelist; struct vchiq_pagelist_info *pagelistinfo; struct page **pages; @@ -231,6 +257,8 @@ create_pagelist(struct vchiq_instance *instance, char *buf, char __user *ubuf, if (count >= INT_MAX - PAGE_SIZE) return NULL; + drv_mgmt = dev_get_drvdata(instance->state->dev); + if (buf) offset = (uintptr_t)buf & (PAGE_SIZE - 1); else @@ -373,25 +401,25 @@ create_pagelist(struct vchiq_instance *instance, char *buf, char __user *ubuf, /* Partial cache lines (fragments) require special measures */ if ((type == PAGELIST_READ) && - ((pagelist->offset & (g_cache_line_size - 1)) || + ((pagelist->offset & (drv_mgmt->info->cache_line_size - 1)) || ((pagelist->offset + pagelist->length) & - (g_cache_line_size - 1)))) { + (drv_mgmt->info->cache_line_size - 1)))) { char *fragments; - if (down_interruptible(&g_free_fragments_sema)) { + if (down_interruptible(&drv_mgmt->free_fragments_sema)) { cleanup_pagelistinfo(instance, pagelistinfo); return NULL; } - WARN_ON(!g_free_fragments); + WARN_ON(!drv_mgmt->free_fragments); - down(&g_free_fragments_mutex); - fragments = g_free_fragments; + down(&drv_mgmt->free_fragments_mutex); + fragments = drv_mgmt->free_fragments; WARN_ON(!fragments); - g_free_fragments = *(char **)g_free_fragments; - up(&g_free_fragments_mutex); + drv_mgmt->free_fragments = *(char **)drv_mgmt->free_fragments; + up(&drv_mgmt->free_fragments_mutex); pagelist->type = PAGELIST_READ_WITH_FRAGMENTS + - (fragments - g_fragments_base) / g_fragments_size; + (fragments - drv_mgmt->fragments_base) / drv_mgmt->fragments_size; } return pagelistinfo; @@ -401,12 +429,15 @@ static void free_pagelist(struct vchiq_instance *instance, struct vchiq_pagelist_info *pagelistinfo, int actual) { + struct vchiq_drv_mgmt *drv_mgmt; struct pagelist *pagelist = pagelistinfo->pagelist; struct page **pages = pagelistinfo->pages; unsigned int num_pages = pagelistinfo->num_pages; dev_dbg(instance->state->dev, "arm: %pK, %d\n", pagelistinfo->pagelist, actual); + drv_mgmt = dev_get_drvdata(instance->state->dev); + /* * NOTE: dma_unmap_sg must be called before the * cpu can touch any of the data/pages. @@ -416,16 +447,16 @@ free_pagelist(struct vchiq_instance *instance, struct vchiq_pagelist_info *pagel pagelistinfo->scatterlist_mapped = 0; /* Deal with any partial cache lines (fragments) */ - if (pagelist->type >= PAGELIST_READ_WITH_FRAGMENTS && g_fragments_base) { - char *fragments = g_fragments_base + + if (pagelist->type >= PAGELIST_READ_WITH_FRAGMENTS && drv_mgmt->fragments_base) { + char *fragments = drv_mgmt->fragments_base + (pagelist->type - PAGELIST_READ_WITH_FRAGMENTS) * - g_fragments_size; + drv_mgmt->fragments_size; int head_bytes, tail_bytes; - head_bytes = (g_cache_line_size - pagelist->offset) & - (g_cache_line_size - 1); + head_bytes = (drv_mgmt->info->cache_line_size - pagelist->offset) & + (drv_mgmt->info->cache_line_size - 1); tail_bytes = (pagelist->offset + actual) & - (g_cache_line_size - 1); + (drv_mgmt->info->cache_line_size - 1); if ((actual >= 0) && (head_bytes != 0)) { if (head_bytes > actual) @@ -440,15 +471,15 @@ free_pagelist(struct vchiq_instance *instance, struct vchiq_pagelist_info *pagel (tail_bytes != 0)) memcpy_to_page(pages[num_pages - 1], (pagelist->offset + actual) & - (PAGE_SIZE - 1) & ~(g_cache_line_size - 1), - fragments + g_cache_line_size, + (PAGE_SIZE - 1) & ~(drv_mgmt->info->cache_line_size - 1), + fragments + drv_mgmt->info->cache_line_size, tail_bytes); - down(&g_free_fragments_mutex); - *(char **)fragments = g_free_fragments; - g_free_fragments = fragments; - up(&g_free_fragments_mutex); - up(&g_free_fragments_sema); + down(&drv_mgmt->free_fragments_mutex); + *(char **)fragments = drv_mgmt->free_fragments; + drv_mgmt->free_fragments = fragments; + up(&drv_mgmt->free_fragments_mutex); + up(&drv_mgmt->free_fragments_sema); } /* Need to mark all the pages dirty. */ @@ -466,8 +497,8 @@ free_pagelist(struct vchiq_instance *instance, struct vchiq_pagelist_info *pagel static int vchiq_platform_init(struct platform_device *pdev, struct vchiq_state *state) { struct device *dev = &pdev->dev; - struct vchiq_drvdata *drvdata = platform_get_drvdata(pdev); - struct rpi_firmware *fw = drvdata->fw; + struct vchiq_drv_mgmt *drv_mgmt = platform_get_drvdata(pdev); + struct rpi_firmware *fw = drv_mgmt->fw; struct vchiq_slot_zero *vchiq_slot_zero; void *slot_mem; dma_addr_t slot_phys; @@ -484,12 +515,11 @@ static int vchiq_platform_init(struct platform_device *pdev, struct vchiq_state if (err < 0) return err; - g_cache_line_size = drvdata->cache_line_size; - g_fragments_size = 2 * g_cache_line_size; + drv_mgmt->fragments_size = 2 * drv_mgmt->info->cache_line_size; /* Allocate space for the channels in coherent memory */ slot_mem_size = PAGE_ALIGN(TOTAL_SLOTS * VCHIQ_SLOT_SIZE); - frag_mem_size = PAGE_ALIGN(g_fragments_size * MAX_FRAGMENTS); + frag_mem_size = PAGE_ALIGN(drv_mgmt->fragments_size * MAX_FRAGMENTS); slot_mem = dmam_alloc_coherent(dev, slot_mem_size + frag_mem_size, &slot_phys, GFP_KERNEL); @@ -509,23 +539,24 @@ static int vchiq_platform_init(struct platform_device *pdev, struct vchiq_state vchiq_slot_zero->platform_data[VCHIQ_PLATFORM_FRAGMENTS_COUNT_IDX] = MAX_FRAGMENTS; - g_fragments_base = (char *)slot_mem + slot_mem_size; + drv_mgmt->fragments_base = (char *)slot_mem + slot_mem_size; - g_free_fragments = g_fragments_base; + drv_mgmt->free_fragments = drv_mgmt->fragments_base; for (i = 0; i < (MAX_FRAGMENTS - 1); i++) { - *(char **)&g_fragments_base[i * g_fragments_size] = - &g_fragments_base[(i + 1) * g_fragments_size]; + *(char **)&drv_mgmt->fragments_base[i * drv_mgmt->fragments_size] = + &drv_mgmt->fragments_base[(i + 1) * drv_mgmt->fragments_size]; } - *(char **)&g_fragments_base[i * g_fragments_size] = NULL; - sema_init(&g_free_fragments_sema, MAX_FRAGMENTS); + *(char **)&drv_mgmt->fragments_base[i * drv_mgmt->fragments_size] = NULL; + sema_init(&drv_mgmt->free_fragments_sema, MAX_FRAGMENTS); + sema_init(&drv_mgmt->free_fragments_mutex, 1); err = vchiq_init_state(state, vchiq_slot_zero, dev); if (err) return err; - g_regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(g_regs)) - return PTR_ERR(g_regs); + drv_mgmt->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(drv_mgmt->regs)) + return PTR_ERR(drv_mgmt->regs); irq = platform_get_irq(pdev, 0); if (irq <= 0) @@ -556,7 +587,8 @@ static int vchiq_platform_init(struct platform_device *pdev, struct vchiq_state dev_dbg(&pdev->dev, "arm: vchiq_init - done (slots %pK, phys %pad)\n", vchiq_slot_zero, &slot_phys); - vchiq_call_connected_callbacks(); + mutex_init(&drv_mgmt->connected_mutex); + vchiq_call_connected_callbacks(drv_mgmt); return 0; } @@ -607,8 +639,10 @@ static struct vchiq_arm_state *vchiq_platform_get_arm_state(struct vchiq_state * } void -remote_event_signal(struct remote_event *event) +remote_event_signal(struct vchiq_state *state, struct remote_event *event) { + struct vchiq_drv_mgmt *mgmt = dev_get_drvdata(state->dev); + /* * Ensure that all writes to shared data structures have completed * before signalling the peer. @@ -620,7 +654,7 @@ remote_event_signal(struct remote_event *event) dsb(sy); /* data barrier operation */ if (event->armed) - writel(0, g_regs + BELL2); /* trigger vc interrupt */ + writel(0, mgmt->regs + BELL2); /* trigger vc interrupt */ } int @@ -662,9 +696,8 @@ void vchiq_dump_platform_state(struct seq_file *f) } #define VCHIQ_INIT_RETRIES 10 -int vchiq_initialise(struct vchiq_instance **instance_out) +int vchiq_initialise(struct vchiq_state *state, struct vchiq_instance **instance_out) { - struct vchiq_state *state; struct vchiq_instance *instance = NULL; int i, ret; @@ -674,8 +707,7 @@ int vchiq_initialise(struct vchiq_instance **instance_out) * block forever. */ for (i = 0; i < VCHIQ_INIT_RETRIES; i++) { - state = vchiq_get_state(); - if (state) + if (vchiq_remote_initialised(state)) break; usleep_range(500, 600); } @@ -690,7 +722,6 @@ int vchiq_initialise(struct vchiq_instance **instance_out) instance = kzalloc(sizeof(*instance), GFP_KERNEL); if (!instance) { - dev_err(state->dev, "core: %s: Cannot allocate vchiq instance\n", __func__); ret = -ENOMEM; goto failed; } @@ -949,17 +980,15 @@ vchiq_blocking_bulk_transfer(struct vchiq_instance *instance, unsigned int handl * This is not a retry of the previous one. * Cancel the signal when the transfer completes. */ - spin_lock(&bulk_waiter_spinlock); + spin_lock(&service->state->bulk_waiter_spinlock); bulk->userdata = NULL; - spin_unlock(&bulk_waiter_spinlock); + spin_unlock(&service->state->bulk_waiter_spinlock); } } } else { waiter = kzalloc(sizeof(*waiter), GFP_KERNEL); - if (!waiter) { - dev_err(service->state->dev, "core: %s: - Out of memory\n", __func__); + if (!waiter) return -ENOMEM; - } } status = vchiq_bulk_transfer(instance, handle, data, NULL, size, @@ -970,9 +999,9 @@ vchiq_blocking_bulk_transfer(struct vchiq_instance *instance, unsigned int handl if (bulk) { /* Cancel the signal when the transfer completes. */ - spin_lock(&bulk_waiter_spinlock); + spin_lock(&service->state->bulk_waiter_spinlock); bulk->userdata = NULL; - spin_unlock(&bulk_waiter_spinlock); + spin_unlock(&service->state->bulk_waiter_spinlock); } kfree(waiter); } else { @@ -993,9 +1022,10 @@ add_completion(struct vchiq_instance *instance, enum vchiq_reason reason, void *bulk_userdata) { struct vchiq_completion_data_kernel *completion; + struct vchiq_drv_mgmt *mgmt = dev_get_drvdata(instance->state->dev); int insert; - DEBUG_INITIALISE(g_state.local); + DEBUG_INITIALISE(mgmt->state.local); insert = instance->completion_insert; while ((insert - instance->completion_remove) >= MAX_COMPLETIONS) { @@ -1058,11 +1088,12 @@ service_callback(struct vchiq_instance *instance, enum vchiq_reason reason, * containing the original callback and the user state structure, which * contains a circular buffer for completion records. */ + struct vchiq_drv_mgmt *mgmt = dev_get_drvdata(instance->state->dev); struct user_service *user_service; struct vchiq_service *service; bool skip_completion = false; - DEBUG_INITIALISE(g_state.local); + DEBUG_INITIALISE(mgmt->state.local); DEBUG_TRACE(SERVICE_CALLBACK_LINE); @@ -1075,7 +1106,7 @@ service_callback(struct vchiq_instance *instance, enum vchiq_reason reason, user_service = (struct user_service *)service->base.userdata; - if (!instance || instance->closing) { + if (instance->closing) { rcu_read_unlock(); return 0; } @@ -1093,10 +1124,10 @@ service_callback(struct vchiq_instance *instance, enum vchiq_reason reason, reason, header, instance, bulk_userdata); if (header && user_service->is_vchi) { - spin_lock(&msg_queue_spinlock); + spin_lock(&service->state->msg_queue_spinlock); while (user_service->msg_insert == (user_service->msg_remove + MSG_QUEUE_SIZE)) { - spin_unlock(&msg_queue_spinlock); + spin_unlock(&service->state->msg_queue_spinlock); DEBUG_TRACE(SERVICE_CALLBACK_LINE); DEBUG_COUNT(MSG_QUEUE_FULL_COUNT); dev_dbg(service->state->dev, "arm: msg queue full\n"); @@ -1133,7 +1164,7 @@ service_callback(struct vchiq_instance *instance, enum vchiq_reason reason, return -EINVAL; } DEBUG_TRACE(SERVICE_CALLBACK_LINE); - spin_lock(&msg_queue_spinlock); + spin_lock(&service->state->msg_queue_spinlock); } user_service->msg_queue[user_service->msg_insert & @@ -1152,7 +1183,7 @@ service_callback(struct vchiq_instance *instance, enum vchiq_reason reason, skip_completion = true; } - spin_unlock(&msg_queue_spinlock); + spin_unlock(&service->state->msg_queue_spinlock); complete(&user_service->insert_event); header = NULL; @@ -1167,12 +1198,11 @@ service_callback(struct vchiq_instance *instance, enum vchiq_reason reason, bulk_userdata); } -void vchiq_dump_platform_instances(struct seq_file *f) +void vchiq_dump_platform_instances(struct vchiq_state *state, struct seq_file *f) { - struct vchiq_state *state = vchiq_get_state(); int i; - if (!state) + if (!vchiq_remote_initialised(state)) return; /* @@ -1244,23 +1274,6 @@ void vchiq_dump_platform_service_state(struct seq_file *f, seq_puts(f, "\n"); } -struct vchiq_state * -vchiq_get_state(void) -{ - if (!g_state.remote) { - pr_err("%s: g_state.remote == NULL\n", __func__); - return NULL; - } - - if (g_state.remote->initialised != 1) { - pr_notice("%s: g_state.remote->initialised != 1 (%d)\n", - __func__, g_state.remote->initialised); - return NULL; - } - - return &g_state; -} - /* * Autosuspend related functionality */ @@ -1294,7 +1307,7 @@ vchiq_keepalive_thread_func(void *v) .version_min = KEEPALIVE_VER_MIN }; - ret = vchiq_initialise(&instance); + ret = vchiq_initialise(state, &instance); if (ret) { dev_err(state->dev, "suspend: %s: vchiq_initialise failed %d\n", __func__, ret); goto exit; @@ -1317,7 +1330,7 @@ vchiq_keepalive_thread_func(void *v) long rc = 0, uc = 0; if (wait_for_completion_interruptible(&arm_state->ka_evt)) { - dev_err(state->dev, "suspend: %s: interrupted\n", __func__); + dev_dbg(state->dev, "suspend: %s: interrupted\n", __func__); flush_signals(current); continue; } @@ -1706,8 +1719,8 @@ void vchiq_platform_conn_state_changed(struct vchiq_state *state, } static const struct of_device_id vchiq_of_match[] = { - { .compatible = "brcm,bcm2835-vchiq", .data = &bcm2835_drvdata }, - { .compatible = "brcm,bcm2836-vchiq", .data = &bcm2836_drvdata }, + { .compatible = "brcm,bcm2835-vchiq", .data = &bcm2835_info }, + { .compatible = "brcm,bcm2836-vchiq", .data = &bcm2836_info }, {}, }; MODULE_DEVICE_TABLE(of, vchiq_of_match); @@ -1715,13 +1728,12 @@ MODULE_DEVICE_TABLE(of, vchiq_of_match); static int vchiq_probe(struct platform_device *pdev) { struct device_node *fw_node; - const struct of_device_id *of_id; - struct vchiq_drvdata *drvdata; + const struct vchiq_platform_info *info; + struct vchiq_drv_mgmt *mgmt; int err; - of_id = of_match_node(vchiq_of_match, pdev->dev.of_node); - drvdata = (struct vchiq_drvdata *)of_id->data; - if (!drvdata) + info = of_device_get_match_data(&pdev->dev); + if (!info) return -EINVAL; fw_node = of_find_compatible_node(NULL, NULL, @@ -1731,18 +1743,23 @@ static int vchiq_probe(struct platform_device *pdev) return -ENOENT; } - drvdata->fw = devm_rpi_firmware_get(&pdev->dev, fw_node); + mgmt = kzalloc(sizeof(*mgmt), GFP_KERNEL); + if (!mgmt) + return -ENOMEM; + + mgmt->fw = devm_rpi_firmware_get(&pdev->dev, fw_node); of_node_put(fw_node); - if (!drvdata->fw) + if (!mgmt->fw) return -EPROBE_DEFER; - platform_set_drvdata(pdev, drvdata); + mgmt->info = info; + platform_set_drvdata(pdev, mgmt); - err = vchiq_platform_init(pdev, &g_state); + err = vchiq_platform_init(pdev, &mgmt->state); if (err) goto failed_platform_init; - vchiq_debugfs_init(); + vchiq_debugfs_init(&mgmt->state); dev_dbg(&pdev->dev, "arm: platform initialised - version %d (min %d)\n", VCHIQ_VERSION, VCHIQ_VERSION_MIN); @@ -1753,7 +1770,7 @@ static int vchiq_probe(struct platform_device *pdev) */ err = vchiq_register_chrdev(&pdev->dev); if (err) { - dev_warn(&pdev->dev, "arm: Failed to initialize vchiq cdev\n"); + dev_err(&pdev->dev, "arm: Failed to initialize vchiq cdev\n"); goto error_exit; } @@ -1763,17 +1780,21 @@ static int vchiq_probe(struct platform_device *pdev) return 0; failed_platform_init: - dev_warn(&pdev->dev, "arm: Could not initialize vchiq platform\n"); + dev_err(&pdev->dev, "arm: Could not initialize vchiq platform\n"); error_exit: return err; } static void vchiq_remove(struct platform_device *pdev) { + struct vchiq_drv_mgmt *mgmt = dev_get_drvdata(&pdev->dev); + vchiq_device_unregister(bcm2835_audio); vchiq_device_unregister(bcm2835_camera); vchiq_debugfs_deinit(); vchiq_deregister_chrdev(); + + kfree(mgmt); } static struct platform_driver vchiq_driver = { diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h index 7844ef765a..fd1b9d3555 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h @@ -20,11 +20,42 @@ #define MAX_ELEMENTS 8 #define MSG_QUEUE_SIZE 128 +#define VCHIQ_DRV_MAX_CALLBACKS 10 + +struct rpi_firmware; +struct vchiq_device; + enum USE_TYPE_E { USE_TYPE_SERVICE, USE_TYPE_VCHIQ }; +struct vchiq_platform_info { + unsigned int cache_line_size; +}; + +struct vchiq_drv_mgmt { + struct rpi_firmware *fw; + const struct vchiq_platform_info *info; + + bool connected; + int num_deferred_callbacks; + /* Protects connected and num_deferred_callbacks */ + struct mutex connected_mutex; + + void (*deferred_callback[VCHIQ_DRV_MAX_CALLBACKS])(void); + + struct semaphore free_fragments_sema; + struct semaphore free_fragments_mutex; + char *fragments_base; + char *free_fragments; + unsigned int fragments_size; + + void __iomem *regs; + + struct vchiq_state state; +}; + struct user_service { struct vchiq_service *service; void __user *userdata; @@ -69,12 +100,6 @@ struct vchiq_instance { struct vchiq_debugfs_node debugfs_node; }; -extern spinlock_t msg_queue_spinlock; -extern struct vchiq_state g_state; - -extern struct vchiq_state * -vchiq_get_state(void); - int vchiq_use_service(struct vchiq_instance *instance, unsigned int handle); @@ -112,6 +137,10 @@ vchiq_instance_get_trace(struct vchiq_instance *instance); extern void vchiq_instance_set_trace(struct vchiq_instance *instance, int trace); +extern void +vchiq_add_connected_callback(struct vchiq_device *device, + void (*callback)(void)); + #if IS_ENABLED(CONFIG_VCHIQ_CDEV) extern void diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_bus.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_bus.c index 68f830d755..3f87b93c65 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_bus.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_bus.c @@ -11,6 +11,7 @@ #include #include +#include "vchiq_arm.h" #include "vchiq_bus.h" static int vchiq_bus_type_match(struct device *dev, struct device_driver *drv) @@ -37,11 +38,21 @@ static int vchiq_bus_probe(struct device *dev) return driver->probe(device); } +static void vchiq_bus_remove(struct device *dev) +{ + struct vchiq_device *device = to_vchiq_device(dev); + struct vchiq_driver *driver = to_vchiq_driver(dev->driver); + + if (driver->remove) + driver->remove(device); +} + const struct bus_type vchiq_bus_type = { .name = "vchiq-bus", .match = vchiq_bus_type_match, .uevent = vchiq_bus_uevent, .probe = vchiq_bus_probe, + .remove = vchiq_bus_remove, }; static void vchiq_device_release(struct device *dev) @@ -67,6 +78,8 @@ vchiq_device_register(struct device *parent, const char *name) device->dev.dma_mask = &device->dev.coherent_dma_mask; device->dev.release = vchiq_device_release; + device->drv_mgmt = dev_get_drvdata(parent); + of_dma_configure(&device->dev, parent->of_node, true); ret = device_register(&device->dev); diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_bus.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_bus.h index 4db86e76ed..9de179b39f 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_bus.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_bus.h @@ -9,8 +9,11 @@ #include #include +struct vchiq_drv_mgmt; + struct vchiq_device { struct device dev; + struct vchiq_drv_mgmt *drv_mgmt; }; struct vchiq_driver { diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_connected.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_connected.c deleted file mode 100644 index 3cad13f09e..0000000000 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_connected.c +++ /dev/null @@ -1,74 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause -/* Copyright (c) 2010-2012 Broadcom. All rights reserved. */ - -#include "vchiq_connected.h" -#include "vchiq_core.h" -#include -#include - -#define MAX_CALLBACKS 10 - -static int g_connected; -static int g_num_deferred_callbacks; -static void (*g_deferred_callback[MAX_CALLBACKS])(void); -static int g_once_init; -static DEFINE_MUTEX(g_connected_mutex); - -/* Function to initialize our lock */ -static void connected_init(void) -{ - if (!g_once_init) - g_once_init = 1; -} - -/* - * This function is used to defer initialization until the vchiq stack is - * initialized. If the stack is already initialized, then the callback will - * be made immediately, otherwise it will be deferred until - * vchiq_call_connected_callbacks is called. - */ -void vchiq_add_connected_callback(struct vchiq_device *device, void (*callback)(void)) -{ - connected_init(); - - if (mutex_lock_killable(&g_connected_mutex)) - return; - - if (g_connected) { - /* We're already connected. Call the callback immediately. */ - callback(); - } else { - if (g_num_deferred_callbacks >= MAX_CALLBACKS) { - dev_err(&device->dev, - "core: There already %d callback registered - please increase MAX_CALLBACKS\n", - g_num_deferred_callbacks); - } else { - g_deferred_callback[g_num_deferred_callbacks] = - callback; - g_num_deferred_callbacks++; - } - } - mutex_unlock(&g_connected_mutex); -} -EXPORT_SYMBOL(vchiq_add_connected_callback); - -/* - * This function is called by the vchiq stack once it has been connected to - * the videocore and clients can start to use the stack. - */ -void vchiq_call_connected_callbacks(void) -{ - int i; - - connected_init(); - - if (mutex_lock_killable(&g_connected_mutex)) - return; - - for (i = 0; i < g_num_deferred_callbacks; i++) - g_deferred_callback[i](); - - g_num_deferred_callbacks = 0; - g_connected = 1; - mutex_unlock(&g_connected_mutex); -} diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_connected.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_connected.h deleted file mode 100644 index e4ed56446f..0000000000 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_connected.h +++ /dev/null @@ -1,12 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ -/* Copyright (c) 2010-2012 Broadcom. All rights reserved. */ - -#include "vchiq_bus.h" - -#ifndef VCHIQ_CONNECTED_H -#define VCHIQ_CONNECTED_H - -void vchiq_add_connected_callback(struct vchiq_device *device, void (*callback)(void)); -void vchiq_call_connected_callbacks(void); - -#endif /* VCHIQ_CONNECTED_H */ diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c index 76c2777815..df3af821f2 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c @@ -43,7 +43,7 @@ (((type) << TYPE_SHIFT) | ((srcport) << 12) | ((dstport) << 0)) #define VCHIQ_MSG_TYPE(msgid) ((unsigned int)(msgid) >> TYPE_SHIFT) #define VCHIQ_MSG_SRCPORT(msgid) \ - (unsigned short)(((unsigned int)(msgid) >> 12) & 0xfff) + ((unsigned short)(((unsigned int)(msgid) >> 12) & 0xfff)) #define VCHIQ_MSG_DSTPORT(msgid) \ ((unsigned short)(msgid) & 0xfff) @@ -149,9 +149,6 @@ static inline void check_sizes(void) BUILD_BUG_ON_NOT_POWER_OF_2(VCHIQ_MAX_SERVICES); } -DEFINE_SPINLOCK(bulk_waiter_spinlock); -static DEFINE_SPINLOCK(quota_spinlock); - static unsigned int handle_seq; static const char *const srvstate_names[] = { @@ -230,6 +227,7 @@ struct vchiq_service *handle_to_service(struct vchiq_instance *instance, unsigne return rcu_dereference(instance->state->services[idx]); } + struct vchiq_service * find_service_by_handle(struct vchiq_instance *instance, unsigned int handle) { @@ -691,7 +689,7 @@ reserve_space(struct vchiq_state *state, size_t space, int is_blocking) /* But first, flush through the last slot. */ state->local_tx_pos = tx_pos; local->tx_pos = tx_pos; - remote_event_signal(&state->remote->trigger); + remote_event_signal(state, &state->remote->trigger); if (!is_blocking || (wait_for_completion_interruptible(&state->slot_available_event))) @@ -700,7 +698,8 @@ reserve_space(struct vchiq_state *state, size_t space, int is_blocking) if (tx_pos == (state->slot_queue_available * VCHIQ_SLOT_SIZE)) { complete(&state->slot_available_event); - pr_warn("%s: invalid tx_pos: %d\n", __func__, tx_pos); + dev_warn(state->dev, "%s: invalid tx_pos: %d\n", + __func__, tx_pos); return NULL; } @@ -724,11 +723,11 @@ process_free_data_message(struct vchiq_state *state, u32 *service_found, struct vchiq_service_quota *quota = &state->service_quotas[port]; int count; - spin_lock("a_spinlock); + spin_lock(&state->quota_spinlock); count = quota->message_use_count; if (count > 0) quota->message_use_count = count - 1; - spin_unlock("a_spinlock); + spin_unlock(&state->quota_spinlock); if (count == quota->message_quota) { /* @@ -747,11 +746,11 @@ process_free_data_message(struct vchiq_state *state, u32 *service_found, /* Set the found bit for this service */ BITSET_SET(service_found, port); - spin_lock("a_spinlock); + spin_lock(&state->quota_spinlock); count = quota->slot_use_count; if (count > 0) quota->slot_use_count = count - 1; - spin_unlock("a_spinlock); + spin_unlock(&state->quota_spinlock); if (count > 0) { /* @@ -837,11 +836,11 @@ process_free_queue(struct vchiq_state *state, u32 *service_found, if (data_found) { int count; - spin_lock("a_spinlock); + spin_lock(&state->quota_spinlock); count = state->data_use_count; if (count > 0) state->data_use_count = count - 1; - spin_unlock("a_spinlock); + spin_unlock(&state->quota_spinlock); if (count == state->data_quota) complete(&state->data_quota_event); } @@ -940,7 +939,7 @@ queue_message(struct vchiq_state *state, struct vchiq_service *service, quota = &state->service_quotas[service->localport]; - spin_lock("a_spinlock); + spin_lock(&state->quota_spinlock); /* * Ensure this service doesn't use more than its quota of @@ -955,14 +954,14 @@ queue_message(struct vchiq_state *state, struct vchiq_service *service, while ((tx_end_index != state->previous_data_index) && (state->data_use_count == state->data_quota)) { VCHIQ_STATS_INC(state, data_stalls); - spin_unlock("a_spinlock); + spin_unlock(&state->quota_spinlock); mutex_unlock(&state->slot_mutex); if (wait_for_completion_interruptible(&state->data_quota_event)) return -EAGAIN; mutex_lock(&state->slot_mutex); - spin_lock("a_spinlock); + spin_lock(&state->quota_spinlock); tx_end_index = SLOT_QUEUE_INDEX_FROM_POS(state->local_tx_pos + stride - 1); if ((tx_end_index == state->previous_data_index) || (state->data_use_count < state->data_quota)) { @@ -975,7 +974,7 @@ queue_message(struct vchiq_state *state, struct vchiq_service *service, while ((quota->message_use_count == quota->message_quota) || ((tx_end_index != quota->previous_tx_index) && (quota->slot_use_count == quota->slot_quota))) { - spin_unlock("a_spinlock); + spin_unlock(&state->quota_spinlock); dev_dbg(state->dev, "core: %d: qm:%d %s,%zx - quota stall (msg %d, slot %d)\n", state->id, service->localport, msg_type_str(type), size, @@ -993,11 +992,11 @@ queue_message(struct vchiq_state *state, struct vchiq_service *service, mutex_unlock(&state->slot_mutex); return -EHOSTDOWN; } - spin_lock("a_spinlock); + spin_lock(&state->quota_spinlock); tx_end_index = SLOT_QUEUE_INDEX_FROM_POS(state->local_tx_pos + stride - 1); } - spin_unlock("a_spinlock); + spin_unlock(&state->quota_spinlock); } header = reserve_space(state, stride, flags & QMFLAGS_IS_BLOCKING); @@ -1040,7 +1039,7 @@ queue_message(struct vchiq_state *state, struct vchiq_service *service, header->data, min_t(size_t, 16, callback_result)); - spin_lock("a_spinlock); + spin_lock(&state->quota_spinlock); quota->message_use_count++; tx_end_index = @@ -1066,7 +1065,7 @@ queue_message(struct vchiq_state *state, struct vchiq_service *service, slot_use_count = 0; } - spin_unlock("a_spinlock); + spin_unlock(&state->quota_spinlock); if (slot_use_count) dev_dbg(state->dev, "core: %d: qm:%d %s,%zx - slot_use->%d (hdr %p)\n", @@ -1124,7 +1123,7 @@ queue_message(struct vchiq_state *state, struct vchiq_service *service, if (!(flags & QMFLAGS_NO_MUTEX_UNLOCK)) mutex_unlock(&state->slot_mutex); - remote_event_signal(&state->remote->trigger); + remote_event_signal(state, &state->remote->trigger); return 0; } @@ -1192,7 +1191,6 @@ queue_message_sync(struct vchiq_state *state, struct vchiq_service *service, header->size = size; header->msgid = msgid; - svc_fourcc = service ? service->base.fourcc : VCHIQ_MAKE_FOURCC('?', '?', '?', '?'); @@ -1202,7 +1200,7 @@ queue_message_sync(struct vchiq_state *state, struct vchiq_service *service, &svc_fourcc, VCHIQ_MSG_SRCPORT(msgid), VCHIQ_MSG_DSTPORT(msgid), size); - remote_event_signal(&state->remote->sync_trigger); + remote_event_signal(state, &state->remote->sync_trigger); if (VCHIQ_MSG_TYPE(msgid) != VCHIQ_MSG_PAUSE) mutex_unlock(&state->sync_mutex); @@ -1260,7 +1258,7 @@ release_slot(struct vchiq_state *state, struct vchiq_slot_info *slot_info, * A write barrier is necessary, but remote_event_signal * contains one. */ - remote_event_signal(&state->remote->recycle); + remote_event_signal(state, &state->remote->recycle); } mutex_unlock(&state->recycle_mutex); @@ -1322,13 +1320,13 @@ notify_bulks(struct vchiq_service *service, struct vchiq_bulk_queue *queue, if (bulk->mode == VCHIQ_BULK_MODE_BLOCKING) { struct bulk_waiter *waiter; - spin_lock(&bulk_waiter_spinlock); + spin_lock(&service->state->bulk_waiter_spinlock); waiter = bulk->userdata; if (waiter) { waiter->actual = bulk->actual; complete(&waiter->event); } - spin_unlock(&bulk_waiter_spinlock); + spin_unlock(&service->state->bulk_waiter_spinlock); } else if (bulk->mode == VCHIQ_BULK_MODE_CALLBACK) { enum vchiq_reason reason = get_bulk_reason(bulk); @@ -1618,7 +1616,6 @@ parse_message(struct vchiq_state *state, struct vchiq_header *header) break; } - svc_fourcc = service ? service->base.fourcc : VCHIQ_MAKE_FOURCC('?', '?', '?', '?'); @@ -1735,10 +1732,9 @@ parse_message(struct vchiq_state *state, struct vchiq_header *header) break; } if (queue->process != queue->remote_insert) { - pr_err("%s: p %x != ri %x\n", - __func__, - queue->process, - queue->remote_insert); + dev_err(state->dev, "%s: p %x != ri %x\n", + __func__, queue->process, + queue->remote_insert); mutex_unlock(&service->bulk_mutex); goto bail_not_ready; } @@ -2169,6 +2165,10 @@ vchiq_init_state(struct vchiq_state *state, struct vchiq_slot_zero *slot_zero, s mutex_init(&state->sync_mutex); mutex_init(&state->bulk_transfer_mutex); + spin_lock_init(&state->msg_queue_spinlock); + spin_lock_init(&state->bulk_waiter_spinlock); + spin_lock_init(&state->quota_spinlock); + init_completion(&state->slot_available_event); init_completion(&state->slot_remove_event); init_completion(&state->data_quota_event); @@ -2177,6 +2177,7 @@ vchiq_init_state(struct vchiq_state *state, struct vchiq_slot_zero *slot_zero, s for (i = 0; i < VCHIQ_MAX_SERVICES; i++) { struct vchiq_service_quota *quota = &state->service_quotas[i]; + init_completion("a->quota_event); } @@ -3240,7 +3241,7 @@ static void release_message_sync(struct vchiq_state *state, struct vchiq_header *header) { header->msgid = VCHIQ_MSGID_PADDING; - remote_event_signal(&state->remote->sync_release); + remote_event_signal(state, &state->remote->sync_release); } int @@ -3504,7 +3505,7 @@ void vchiq_dump_state(struct seq_file *f, struct vchiq_state *state) vchiq_dump_shared_state(f, state, state->remote, "Remote"); - vchiq_dump_platform_instances(f); + vchiq_dump_platform_instances(state, f); for (i = 0; i < state->unused_service; i++) { struct vchiq_service *service = find_service_by_port(state, i); diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.h index c8527551b5..382ec08f6a 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include "../../include/linux/raspberrypi/vchiq.h" @@ -348,6 +349,12 @@ struct vchiq_state { struct mutex bulk_transfer_mutex; + spinlock_t msg_queue_spinlock; + + spinlock_t bulk_waiter_spinlock; + + spinlock_t quota_spinlock; + /* * Indicates the byte position within the stream from where the next * message will be read. The least significant bits are an index into @@ -406,6 +413,11 @@ struct vchiq_state { struct opaque_platform_state *platform_state; }; +static inline bool vchiq_remote_initialised(const struct vchiq_state *state) +{ + return state->remote && state->remote->initialised; +} + struct bulk_waiter { struct vchiq_bulk *bulk; struct completion event; @@ -470,12 +482,6 @@ vchiq_bulk_transfer(struct vchiq_instance *instance, unsigned int handle, void * extern void vchiq_dump_state(struct seq_file *f, struct vchiq_state *state); -extern void -vchiq_loud_error_header(void); - -extern void -vchiq_loud_error_footer(void); - extern void request_poll(struct vchiq_state *state, struct vchiq_service *service, int poll_type); @@ -522,11 +528,11 @@ int vchiq_prepare_bulk_data(struct vchiq_instance *instance, struct vchiq_bulk * void vchiq_complete_bulk(struct vchiq_instance *instance, struct vchiq_bulk *bulk); -void remote_event_signal(struct remote_event *event); +void remote_event_signal(struct vchiq_state *state, struct remote_event *event); void vchiq_dump_platform_state(struct seq_file *f); -void vchiq_dump_platform_instances(struct seq_file *f); +void vchiq_dump_platform_instances(struct vchiq_state *state, struct seq_file *f); void vchiq_dump_platform_service_state(struct seq_file *f, struct vchiq_service *service); diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_debugfs.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_debugfs.c index d833e4e297..d5f7f61c56 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_debugfs.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_debugfs.c @@ -42,7 +42,10 @@ static int debugfs_trace_show(struct seq_file *f, void *offset) static int vchiq_dump_show(struct seq_file *f, void *offset) { - vchiq_dump_state(f, &g_state); + struct vchiq_state *state = f->private; + + vchiq_dump_state(f, state); + return 0; } DEFINE_SHOW_ATTRIBUTE(vchiq_dump); @@ -118,12 +121,12 @@ void vchiq_debugfs_remove_instance(struct vchiq_instance *instance) debugfs_remove_recursive(node->dentry); } -void vchiq_debugfs_init(void) +void vchiq_debugfs_init(struct vchiq_state *state) { vchiq_dbg_dir = debugfs_create_dir("vchiq", NULL); vchiq_dbg_clients = debugfs_create_dir("clients", vchiq_dbg_dir); - debugfs_create_file("state", S_IFREG | 0444, vchiq_dbg_dir, NULL, + debugfs_create_file("state", S_IFREG | 0444, vchiq_dbg_dir, state, &vchiq_dump_fops); } @@ -135,7 +138,7 @@ void vchiq_debugfs_deinit(void) #else /* CONFIG_DEBUG_FS */ -void vchiq_debugfs_init(void) +void vchiq_debugfs_init(struct vchiq_state *state) { } diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_debugfs.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_debugfs.h index e9bf055a4c..fabffd81b1 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_debugfs.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_debugfs.h @@ -10,7 +10,7 @@ struct vchiq_debugfs_node { struct dentry *dentry; }; -void vchiq_debugfs_init(void); +void vchiq_debugfs_init(struct vchiq_state *state); void vchiq_debugfs_deinit(void); diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_dev.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_dev.c index 4d9deeeb63..430f2ed2cc 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_dev.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_dev.c @@ -208,7 +208,7 @@ static int vchiq_ioc_dequeue_message(struct vchiq_instance *instance, struct vchiq_header *header; int ret; - DEBUG_INITIALISE(g_state.local); + DEBUG_INITIALISE(instance->state->local); DEBUG_TRACE(DEQUEUE_MESSAGE_LINE); service = find_service_for_instance(instance, args->handle); if (!service) @@ -220,10 +220,10 @@ static int vchiq_ioc_dequeue_message(struct vchiq_instance *instance, goto out; } - spin_lock(&msg_queue_spinlock); + spin_lock(&service->state->msg_queue_spinlock); if (user_service->msg_remove == user_service->msg_insert) { if (!args->blocking) { - spin_unlock(&msg_queue_spinlock); + spin_unlock(&service->state->msg_queue_spinlock); DEBUG_TRACE(DEQUEUE_MESSAGE_LINE); ret = -EWOULDBLOCK; goto out; @@ -231,14 +231,14 @@ static int vchiq_ioc_dequeue_message(struct vchiq_instance *instance, user_service->dequeue_pending = 1; ret = 0; do { - spin_unlock(&msg_queue_spinlock); + spin_unlock(&service->state->msg_queue_spinlock); DEBUG_TRACE(DEQUEUE_MESSAGE_LINE); if (wait_for_completion_interruptible(&user_service->insert_event)) { dev_dbg(service->state->dev, "arm: DEQUEUE_MESSAGE interrupted\n"); ret = -EINTR; break; } - spin_lock(&msg_queue_spinlock); + spin_lock(&service->state->msg_queue_spinlock); } while (user_service->msg_remove == user_service->msg_insert); if (ret) @@ -247,7 +247,7 @@ static int vchiq_ioc_dequeue_message(struct vchiq_instance *instance, if (WARN_ON_ONCE((int)(user_service->msg_insert - user_service->msg_remove) < 0)) { - spin_unlock(&msg_queue_spinlock); + spin_unlock(&service->state->msg_queue_spinlock); ret = -EINVAL; goto out; } @@ -255,7 +255,7 @@ static int vchiq_ioc_dequeue_message(struct vchiq_instance *instance, header = user_service->msg_queue[user_service->msg_remove & (MSG_QUEUE_SIZE - 1)]; user_service->msg_remove++; - spin_unlock(&msg_queue_spinlock); + spin_unlock(&service->state->msg_queue_spinlock); complete(&user_service->remove_event); if (!header) { @@ -340,9 +340,9 @@ static int vchiq_irq_queue_bulk_tx_rx(struct vchiq_instance *instance, !waiter->bulk_waiter.bulk) { if (waiter->bulk_waiter.bulk) { /* Cancel the signal when the transfer completes. */ - spin_lock(&bulk_waiter_spinlock); + spin_lock(&service->state->bulk_waiter_spinlock); waiter->bulk_waiter.bulk->userdata = NULL; - spin_unlock(&bulk_waiter_spinlock); + spin_unlock(&service->state->bulk_waiter_spinlock); } kfree(waiter); ret = 0; @@ -435,7 +435,7 @@ static int vchiq_ioc_await_completion(struct vchiq_instance *instance, int remove; int ret; - DEBUG_INITIALISE(g_state.local); + DEBUG_INITIALISE(instance->state->local); DEBUG_TRACE(AWAIT_COMPLETION_LINE); if (!instance->connected) @@ -1163,13 +1163,15 @@ vchiq_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) static int vchiq_open(struct inode *inode, struct file *file) { - struct vchiq_state *state = vchiq_get_state(); + struct miscdevice *vchiq_miscdev = file->private_data; + struct vchiq_drv_mgmt *mgmt = dev_get_drvdata(vchiq_miscdev->parent); + struct vchiq_state *state = &mgmt->state; struct vchiq_instance *instance; dev_dbg(state->dev, "arm: vchiq open\n"); - if (!state) { - dev_err(state->dev, "arm: vchiq has no connection to VideoCore\n"); + if (!vchiq_remote_initialised(state)) { + dev_dbg(state->dev, "arm: vchiq has no connection to VideoCore\n"); return -ENOTCONN; } @@ -1196,14 +1198,14 @@ static int vchiq_open(struct inode *inode, struct file *file) static int vchiq_release(struct inode *inode, struct file *file) { struct vchiq_instance *instance = file->private_data; - struct vchiq_state *state = vchiq_get_state(); + struct vchiq_state *state = instance->state; struct vchiq_service *service; int ret = 0; int i; dev_dbg(state->dev, "arm: instance=%p\n", instance); - if (!state) { + if (!vchiq_remote_initialised(state)) { ret = -EPERM; goto out; } @@ -1246,7 +1248,7 @@ static int vchiq_release(struct inode *inode, struct file *file) break; } - spin_lock(&msg_queue_spinlock); + spin_lock(&service->state->msg_queue_spinlock); while (user_service->msg_remove != user_service->msg_insert) { struct vchiq_header *header; @@ -1254,14 +1256,14 @@ static int vchiq_release(struct inode *inode, struct file *file) header = user_service->msg_queue[m]; user_service->msg_remove++; - spin_unlock(&msg_queue_spinlock); + spin_unlock(&service->state->msg_queue_spinlock); if (header) vchiq_release_message(instance, service->handle, header); - spin_lock(&msg_queue_spinlock); + spin_lock(&service->state->msg_queue_spinlock); } - spin_unlock(&msg_queue_spinlock); + spin_unlock(&service->state->msg_queue_spinlock); vchiq_service_put(service); } diff --git a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c index 4c3684dd90..fca920d41e 100644 --- a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c +++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c @@ -26,6 +26,7 @@ #include #include "../include/linux/raspberrypi/vchiq.h" +#include "../interface/vchiq_arm/vchiq_arm.h" #include "mmal-common.h" #include "mmal-vchiq.h" #include "mmal-msg.h" @@ -548,9 +549,9 @@ static void bulk_abort_cb(struct vchiq_mmal_instance *instance, } /* incoming event service callback */ -static int service_callback(struct vchiq_instance *vchiq_instance, - enum vchiq_reason reason, struct vchiq_header *header, - unsigned int handle, void *bulk_ctx) +static int mmal_service_callback(struct vchiq_instance *vchiq_instance, + enum vchiq_reason reason, struct vchiq_header *header, + unsigned int handle, void *bulk_ctx) { struct vchiq_mmal_instance *instance = vchiq_get_service_userdata(vchiq_instance, handle); u32 msg_len; @@ -1852,7 +1853,7 @@ int vchiq_mmal_finalise(struct vchiq_mmal_instance *instance) } EXPORT_SYMBOL_GPL(vchiq_mmal_finalise); -int vchiq_mmal_init(struct vchiq_mmal_instance **out_instance) +int vchiq_mmal_init(struct device *dev, struct vchiq_mmal_instance **out_instance) { int status; int err = -ENODEV; @@ -1862,9 +1863,10 @@ int vchiq_mmal_init(struct vchiq_mmal_instance **out_instance) .version = VC_MMAL_VER, .version_min = VC_MMAL_MIN_VER, .fourcc = VCHIQ_MAKE_FOURCC('m', 'm', 'a', 'l'), - .callback = service_callback, + .callback = mmal_service_callback, .userdata = NULL, }; + struct vchiq_drv_mgmt *mgmt = dev_get_drvdata(dev->parent); /* compile time checks to ensure structure size as they are * directly (de)serialised from memory. @@ -1880,7 +1882,7 @@ int vchiq_mmal_init(struct vchiq_mmal_instance **out_instance) BUILD_BUG_ON(sizeof(struct mmal_port) != 64); /* create a vchi instance */ - status = vchiq_initialise(&vchiq_instance); + status = vchiq_initialise(&mgmt->state, &vchiq_instance); if (status) { pr_err("Failed to initialise VCHI instance (status=%d)\n", status); diff --git a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.h b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.h index 09f030919d..97abe4bdcf 100644 --- a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.h +++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.h @@ -25,6 +25,7 @@ #define MMAL_FORMAT_EXTRADATA_MAX_SIZE 128 struct vchiq_mmal_instance; +struct device; enum vchiq_mmal_es_type { MMAL_ES_TYPE_UNKNOWN, /**< Unknown elementary stream type */ @@ -42,8 +43,7 @@ struct vchiq_mmal_port_buffer { struct vchiq_mmal_port; -typedef void (*vchiq_mmal_buffer_cb)( - struct vchiq_mmal_instance *instance, +typedef void (*vchiq_mmal_buffer_cb)(struct vchiq_mmal_instance *instance, struct vchiq_mmal_port *port, int status, struct mmal_buffer *buffer); @@ -95,37 +95,31 @@ struct vchiq_mmal_component { u32 client_component; /* Used to ref back to client struct */ }; -int vchiq_mmal_init(struct vchiq_mmal_instance **out_instance); +int vchiq_mmal_init(struct device *dev, struct vchiq_mmal_instance **out_instance); int vchiq_mmal_finalise(struct vchiq_mmal_instance *instance); /* Initialise a mmal component and its ports * */ -int vchiq_mmal_component_init( - struct vchiq_mmal_instance *instance, - const char *name, - struct vchiq_mmal_component **component_out); +int vchiq_mmal_component_init(struct vchiq_mmal_instance *instance, + const char *name, struct vchiq_mmal_component **component_out); -int vchiq_mmal_component_finalise( - struct vchiq_mmal_instance *instance, - struct vchiq_mmal_component *component); +int vchiq_mmal_component_finalise(struct vchiq_mmal_instance *instance, + struct vchiq_mmal_component *component); -int vchiq_mmal_component_enable( - struct vchiq_mmal_instance *instance, - struct vchiq_mmal_component *component); +int vchiq_mmal_component_enable(struct vchiq_mmal_instance *instance, + struct vchiq_mmal_component *component); -int vchiq_mmal_component_disable( - struct vchiq_mmal_instance *instance, - struct vchiq_mmal_component *component); +int vchiq_mmal_component_disable(struct vchiq_mmal_instance *instance, + struct vchiq_mmal_component *component); /* enable a mmal port * * enables a port and if a buffer callback provided enque buffer * headers as appropriate for the port. */ -int vchiq_mmal_port_enable( - struct vchiq_mmal_instance *instance, - struct vchiq_mmal_port *port, +int vchiq_mmal_port_enable(struct vchiq_mmal_instance *instance, + struct vchiq_mmal_port *port, vchiq_mmal_buffer_cb buffer_cb); /* disable a port -- cgit v1.2.3