aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/vc04_services/interface/vchiq_arm
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/vc04_services/interface/vchiq_arm')
-rw-r--r--drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c146
-rw-r--r--drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c27
-rw-r--r--drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h1
-rw-r--r--drivers/staging/vc04_services/interface/vchiq_arm/vchiq_connected.c1
-rw-r--r--drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c1
-rw-r--r--drivers/staging/vc04_services/interface/vchiq_arm/vchiq_killable.h55
-rw-r--r--drivers/staging/vc04_services/interface/vchiq_arm/vchiq_util.c1
7 files changed, 202 insertions, 30 deletions
diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c
index 38a13e4618a8..406de35eead5 100644
--- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c
+++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c
@@ -7,6 +7,7 @@
#include <linux/interrupt.h>
#include <linux/pagemap.h>
#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/uaccess.h>
@@ -18,6 +19,7 @@
#include "vchiq_arm.h"
#include "vchiq_connected.h"
+#include "vchiq_killable.h"
#include "vchiq_pagelist.h"
#define MAX_FRAGMENTS (VCHIQ_NUM_CURRENT_BULKS * 2)
@@ -28,6 +30,8 @@
#define BELL0 0x00
#define BELL2 0x08
+#define VCHIQ_DMA_POOL_SIZE PAGE_SIZE
+
struct vchiq_2835_state {
int inited;
struct vchiq_arm_state arm_state;
@@ -37,6 +41,7 @@ struct vchiq_pagelist_info {
struct pagelist *pagelist;
size_t pagelist_buffer_size;
dma_addr_t dma_addr;
+ bool is_from_pool;
enum dma_data_direction dma_dir;
unsigned int num_pages;
unsigned int pages_need_release;
@@ -57,11 +62,14 @@ static void __iomem *g_regs;
* of 32.
*/
static unsigned int g_cache_line_size = 32;
+static struct dma_pool *g_dma_pool;
+static unsigned int g_use_36bit_addrs = 0;
static unsigned int g_fragments_size;
static char *g_fragments_base;
static char *g_free_fragments;
static struct semaphore g_free_fragments_sema;
static struct device *g_dev;
+static struct device *g_dma_dev;
static DEFINE_SEMAPHORE(g_free_fragments_mutex);
@@ -78,6 +86,7 @@ free_pagelist(struct vchiq_pagelist_info *pagelistinfo,
int vchiq_platform_init(struct platform_device *pdev, struct vchiq_state *state)
{
struct device *dev = &pdev->dev;
+ struct device *dma_dev = NULL;
struct vchiq_drvdata *drvdata = platform_get_drvdata(pdev);
struct rpi_firmware *fw = drvdata->fw;
struct vchiq_slot_zero *vchiq_slot_zero;
@@ -99,6 +108,24 @@ int vchiq_platform_init(struct platform_device *pdev, struct vchiq_state *state)
g_cache_line_size = drvdata->cache_line_size;
g_fragments_size = 2 * g_cache_line_size;
+ if (drvdata->use_36bit_addrs) {
+ struct device_node *dma_node =
+ of_find_compatible_node(NULL, NULL, "brcm,bcm2711-dma");
+
+ if (dma_node) {
+ struct platform_device *pdev;
+
+ pdev = of_find_device_by_node(dma_node);
+ if (pdev)
+ dma_dev = &pdev->dev;
+ of_node_put(dma_node);
+ g_use_36bit_addrs = true;
+ } else {
+ dev_err(dev, "40-bit DMA controller not found\n");
+ return -EINVAL;
+ }
+ }
+
/* 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);
@@ -111,13 +138,14 @@ int vchiq_platform_init(struct platform_device *pdev, struct vchiq_state *state)
}
WARN_ON(((unsigned long)slot_mem & (PAGE_SIZE - 1)) != 0);
+ channelbase = slot_phys;
vchiq_slot_zero = vchiq_init_slots(slot_mem, slot_mem_size);
if (!vchiq_slot_zero)
return -EINVAL;
vchiq_slot_zero->platform_data[VCHIQ_PLATFORM_FRAGMENTS_OFFSET_IDX] =
- (int)slot_phys + slot_mem_size;
+ channelbase + slot_mem_size;
vchiq_slot_zero->platform_data[VCHIQ_PLATFORM_FRAGMENTS_COUNT_IDX] =
MAX_FRAGMENTS;
@@ -150,7 +178,6 @@ int vchiq_platform_init(struct platform_device *pdev, struct vchiq_state *state)
}
/* Send the base address of the slots to VideoCore */
- channelbase = slot_phys;
err = rpi_firmware_property(fw, RPI_FIRMWARE_VCHIQ_INIT,
&channelbase, sizeof(channelbase));
if (err || channelbase) {
@@ -159,6 +186,15 @@ int vchiq_platform_init(struct platform_device *pdev, struct vchiq_state *state)
}
g_dev = dev;
+ g_dma_dev = dma_dev ?: dev;
+ g_dma_pool = dmam_pool_create("vchiq_scatter_pool", dev,
+ VCHIQ_DMA_POOL_SIZE, g_cache_line_size,
+ 0);
+ if (!g_dma_pool) {
+ dev_err(dev, "failed to create dma pool");
+ return -ENOMEM;
+ }
+
vchiq_log_info(vchiq_arm_log_level,
"vchiq_init - done (slots %pK, phys %pad)",
vchiq_slot_zero, &slot_phys);
@@ -228,7 +264,7 @@ vchiq_prepare_bulk_data(struct vchiq_bulk *bulk, void *offset, int size,
if (!pagelistinfo)
return VCHIQ_ERROR;
- bulk->data = (void *)(unsigned long)pagelistinfo->dma_addr;
+ bulk->data = (void *)(uintptr_t)pagelistinfo->dma_addr;
/*
* Store the pagelistinfo address in remote_data,
@@ -283,7 +319,7 @@ static void
cleanup_pagelistinfo(struct vchiq_pagelist_info *pagelistinfo)
{
if (pagelistinfo->scatterlist_mapped) {
- dma_unmap_sg(g_dev, pagelistinfo->scatterlist,
+ dma_unmap_sg(g_dma_dev, pagelistinfo->scatterlist,
pagelistinfo->num_pages, pagelistinfo->dma_dir);
}
@@ -293,9 +329,14 @@ cleanup_pagelistinfo(struct vchiq_pagelist_info *pagelistinfo)
for (i = 0; i < pagelistinfo->num_pages; i++)
put_page(pagelistinfo->pages[i]);
}
-
- dma_free_coherent(g_dev, pagelistinfo->pagelist_buffer_size,
- pagelistinfo->pagelist, pagelistinfo->dma_addr);
+ if (pagelistinfo->is_from_pool) {
+ dma_pool_free(g_dma_pool, pagelistinfo->pagelist,
+ pagelistinfo->dma_addr);
+ } else {
+ dma_free_coherent(g_dev, pagelistinfo->pagelist_buffer_size,
+ pagelistinfo->pagelist,
+ pagelistinfo->dma_addr);
+ }
}
/* There is a potential problem with partial cache lines (pages?)
@@ -315,6 +356,7 @@ create_pagelist(char __user *buf, size_t count, unsigned short type)
u32 *addrs;
unsigned int num_pages, offset, i, k;
int actual_pages;
+ bool is_from_pool;
size_t pagelist_size;
struct scatterlist *scatterlist, *sg;
int dma_buffers;
@@ -341,8 +383,16 @@ create_pagelist(char __user *buf, size_t count, unsigned short type)
/* Allocate enough storage to hold the page pointers and the page
* list
*/
- pagelist = dma_alloc_coherent(g_dev, pagelist_size, &dma_addr,
- GFP_KERNEL);
+ if (pagelist_size > VCHIQ_DMA_POOL_SIZE) {
+ pagelist = dma_alloc_coherent(g_dev,
+ pagelist_size,
+ &dma_addr,
+ GFP_KERNEL);
+ is_from_pool = false;
+ } else {
+ pagelist = dma_pool_alloc(g_dma_pool, GFP_KERNEL, &dma_addr);
+ is_from_pool = true;
+ }
vchiq_log_trace(vchiq_arm_log_level, "%s - %pK", __func__, pagelist);
@@ -363,6 +413,7 @@ create_pagelist(char __user *buf, size_t count, unsigned short type)
pagelistinfo->pagelist = pagelist;
pagelistinfo->pagelist_buffer_size = pagelist_size;
pagelistinfo->dma_addr = dma_addr;
+ pagelistinfo->is_from_pool = is_from_pool;
pagelistinfo->dma_dir = (type == PAGELIST_WRITE) ?
DMA_TO_DEVICE : DMA_FROM_DEVICE;
pagelistinfo->num_pages = num_pages;
@@ -434,7 +485,7 @@ create_pagelist(char __user *buf, size_t count, unsigned short type)
count -= len;
}
- dma_buffers = dma_map_sg(g_dev,
+ dma_buffers = dma_map_sg(g_dma_dev,
scatterlist,
num_pages,
pagelistinfo->dma_dir);
@@ -448,25 +499,60 @@ create_pagelist(char __user *buf, size_t count, unsigned short type)
/* Combine adjacent blocks for performance */
k = 0;
- for_each_sg(scatterlist, sg, dma_buffers, i) {
- u32 len = sg_dma_len(sg);
- u32 addr = sg_dma_address(sg);
-
- /* Note: addrs is the address + page_count - 1
- * The firmware expects blocks after the first to be page-
- * aligned and a multiple of the page size
- */
- WARN_ON(len == 0);
- WARN_ON(i && (i != (dma_buffers - 1)) && (len & ~PAGE_MASK));
- WARN_ON(i && (addr & ~PAGE_MASK));
- if (k > 0 &&
- ((addrs[k - 1] & PAGE_MASK) +
- (((addrs[k - 1] & ~PAGE_MASK) + 1) << PAGE_SHIFT))
- == (addr & PAGE_MASK))
- addrs[k - 1] += ((len + PAGE_SIZE - 1) >> PAGE_SHIFT);
- else
- addrs[k++] = (addr & PAGE_MASK) |
- (((len + PAGE_SIZE - 1) >> PAGE_SHIFT) - 1);
+ if (g_use_36bit_addrs) {
+ for_each_sg(scatterlist, sg, dma_buffers, i) {
+ u32 len = sg_dma_len(sg);
+ u64 addr = sg_dma_address(sg);
+ u32 page_id = (u32)((addr >> 4) & ~0xff);
+ u32 sg_pages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT;
+
+ /* Note: addrs is the address + page_count - 1
+ * The firmware expects blocks after the first to be page-
+ * aligned and a multiple of the page size
+ */
+ WARN_ON(len == 0);
+ WARN_ON(i &&
+ (i != (dma_buffers - 1)) && (len & ~PAGE_MASK));
+ WARN_ON(i && (addr & ~PAGE_MASK));
+ WARN_ON(upper_32_bits(addr) > 0xf);
+ if (k > 0 &&
+ ((addrs[k - 1] & ~0xff) +
+ (((addrs[k - 1] & 0xff) + 1) << 8)
+ == page_id)) {
+ u32 inc_pages = min(sg_pages,
+ 0xff - (addrs[k - 1] & 0xff));
+ addrs[k - 1] += inc_pages;
+ page_id += inc_pages << 8;
+ sg_pages -= inc_pages;
+ }
+ while (sg_pages) {
+ u32 inc_pages = min(sg_pages, 0x100u);
+ addrs[k++] = page_id | (inc_pages - 1);
+ page_id += inc_pages << 8;
+ sg_pages -= inc_pages;
+ }
+ }
+ } else {
+ for_each_sg(scatterlist, sg, dma_buffers, i) {
+ u32 len = sg_dma_len(sg);
+ u32 addr = sg_dma_address(sg);
+ u32 new_pages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT;
+
+ /* Note: addrs is the address + page_count - 1
+ * The firmware expects blocks after the first to be page-
+ * aligned and a multiple of the page size
+ */
+ WARN_ON(len == 0);
+ WARN_ON(i && (i != (dma_buffers - 1)) && (len & ~PAGE_MASK));
+ WARN_ON(i && (addr & ~PAGE_MASK));
+ if (k > 0 &&
+ ((addrs[k - 1] & PAGE_MASK) +
+ (((addrs[k - 1] & ~PAGE_MASK) + 1) << PAGE_SHIFT))
+ == (addr & PAGE_MASK))
+ addrs[k - 1] += new_pages;
+ else
+ addrs[k++] = (addr & PAGE_MASK) | (new_pages - 1);
+ }
}
/* Partial cache lines (fragments) require special measures */
@@ -510,7 +596,7 @@ free_pagelist(struct vchiq_pagelist_info *pagelistinfo,
* NOTE: dma_unmap_sg must be called before the
* cpu can touch any of the data/pages.
*/
- dma_unmap_sg(g_dev, pagelistinfo->scatterlist,
+ dma_unmap_sg(g_dma_dev, pagelistinfo->scatterlist,
pagelistinfo->num_pages, pagelistinfo->dma_dir);
pagelistinfo->scatterlist_mapped = 0;
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 355590f1e130..8cdad336dd5b 100644
--- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
+++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
@@ -29,6 +29,7 @@
#include "vchiq_ioctl.h"
#include "vchiq_arm.h"
#include "vchiq_debugfs.h"
+#include "vchiq_killable.h"
#define DEVICE_NAME "vchiq"
@@ -107,6 +108,9 @@ static struct class *vchiq_class;
static DEFINE_SPINLOCK(msg_queue_spinlock);
static struct platform_device *bcm2835_camera;
static struct platform_device *bcm2835_audio;
+static struct platform_device *bcm2835_codec;
+static struct platform_device *vcsm_cma;
+static struct platform_device *bcm2835_isp;
static struct vchiq_drvdata bcm2835_drvdata = {
.cache_line_size = 32,
@@ -116,6 +120,11 @@ static struct vchiq_drvdata bcm2836_drvdata = {
.cache_line_size = 64,
};
+static struct vchiq_drvdata bcm2711_drvdata = {
+ .cache_line_size = 64,
+ .use_36bit_addrs = true,
+};
+
static const char *const ioctl_names[] = {
"CONNECT",
"SHUTDOWN",
@@ -2711,6 +2720,7 @@ 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,bcm2711-vchiq", .data = &bcm2711_drvdata },
{},
};
MODULE_DEVICE_TABLE(of, vchiq_of_match);
@@ -2720,6 +2730,7 @@ vchiq_register_child(struct platform_device *pdev, const char *name)
{
struct platform_device_info pdevinfo;
struct platform_device *child;
+ struct device_node *np;
memset(&pdevinfo, 0, sizeof(pdevinfo));
@@ -2728,12 +2739,22 @@ vchiq_register_child(struct platform_device *pdev, const char *name)
pdevinfo.id = PLATFORM_DEVID_NONE;
pdevinfo.dma_mask = DMA_BIT_MASK(32);
+ np = of_get_child_by_name(pdev->dev.of_node, name);
+
+ /* Skip the child if it is explicitly disabled */
+ if (np && !of_device_is_available(np))
+ return NULL;
+
child = platform_device_register_full(&pdevinfo);
if (IS_ERR(child)) {
dev_warn(&pdev->dev, "%s not registered\n", name);
child = NULL;
}
+ child->dev.of_node = np;
+
+ of_dma_configure(&child->dev, np, true);
+
return child;
}
@@ -2791,8 +2812,11 @@ static int vchiq_probe(struct platform_device *pdev)
VCHIQ_VERSION, VCHIQ_VERSION_MIN,
MAJOR(vchiq_devid), MINOR(vchiq_devid));
+ vcsm_cma = vchiq_register_child(pdev, "vcsm-cma");
+ bcm2835_codec = vchiq_register_child(pdev, "bcm2835-codec");
bcm2835_camera = vchiq_register_child(pdev, "bcm2835-camera");
bcm2835_audio = vchiq_register_child(pdev, "bcm2835_audio");
+ bcm2835_isp = vchiq_register_child(pdev, "bcm2835-isp");
return 0;
@@ -2805,8 +2829,11 @@ failed_platform_init:
static int vchiq_remove(struct platform_device *pdev)
{
+ platform_device_unregister(bcm2835_isp);
platform_device_unregister(bcm2835_audio);
platform_device_unregister(bcm2835_camera);
+ platform_device_unregister(bcm2835_codec);
+ platform_device_unregister(vcsm_cma);
vchiq_debugfs_deinit();
device_destroy(vchiq_class, vchiq_devid);
cdev_del(&vchiq_cdev);
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 0784c5002417..f8b1c005af62 100644
--- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h
+++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h
@@ -52,6 +52,7 @@ struct vchiq_arm_state {
struct vchiq_drvdata {
const unsigned int cache_line_size;
+ const bool use_36bit_addrs;
struct rpi_firmware *fw;
};
diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_connected.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_connected.c
index 79b75efa6868..ee8ea71a2b3b 100644
--- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_connected.c
+++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_connected.c
@@ -3,6 +3,7 @@
#include "vchiq_connected.h"
#include "vchiq_core.h"
+#include "vchiq_killable.h"
#include <linux/module.h>
#include <linux/mutex.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 ae9183db44ee..37927456a2be 100644
--- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c
+++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c
@@ -5,6 +5,7 @@
#include <linux/rcupdate.h>
#include "vchiq_core.h"
+#include "vchiq_killable.h"
#define VCHIQ_SLOT_HANDLER_STACK 8192
diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_killable.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_killable.h
new file mode 100644
index 000000000000..778063ba312a
--- /dev/null
+++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_killable.h
@@ -0,0 +1,55 @@
+/**
+ * Copyright (c) 2010-2012 Broadcom. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The names of the above-listed copyright holders may not be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2, as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef VCHIQ_KILLABLE_H
+#define VCHIQ_KILLABLE_H
+
+#include <linux/mutex.h>
+#include <linux/semaphore.h>
+
+#define SHUTDOWN_SIGS (sigmask(SIGKILL) | sigmask(SIGINT) | sigmask(SIGQUIT) | sigmask(SIGTRAP) | sigmask(SIGSTOP) | sigmask(SIGCONT))
+
+static inline int __must_check down_interruptible_killable(struct semaphore *sem)
+{
+ /* Allow interception of killable signals only. We don't want to be interrupted by harmless signals like SIGALRM */
+ int ret;
+ sigset_t blocked, oldset;
+ siginitsetinv(&blocked, SHUTDOWN_SIGS);
+ sigprocmask(SIG_SETMASK, &blocked, &oldset);
+ ret = down_interruptible(sem);
+ sigprocmask(SIG_SETMASK, &oldset, NULL);
+ return ret;
+}
+#define down_interruptible down_interruptible_killable
+
+#endif
diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_util.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_util.c
index 644844d88fed..694a22749869 100644
--- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_util.c
+++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_util.c
@@ -2,6 +2,7 @@
/* Copyright (c) 2010-2012 Broadcom. All rights reserved. */
#include "vchiq_util.h"
+#include "vchiq_killable.h"
static inline int is_pow2(int i)
{