1 From dcf515d36ce574edd773c9c6321b6bbf9724e209 Mon Sep 17 00:00:00 2001
2 From: Jonathan Bell <jonathan@raspberrypi.org>
3 Date: Thu, 9 May 2019 14:30:37 +0100
4 Subject: [PATCH] drivers: char: add chardev for mmap'ing the RPiVid
7 Based on the gpiomem driver, allow mapping of the decoder register
8 spaces such that userspace can access control/status registers.
9 This driver is intended for use with a custom ffmpeg backend accelerator
10 prior to a v4l2 driver being written.
12 Signed-off-by: Jonathan Bell <jonathan@raspberrypi.org>
14 drivers/char/broadcom/Kconfig | 8 +
15 drivers/char/broadcom/Makefile | 1 +
16 drivers/char/broadcom/rpivid-mem.c | 286 +++++++++++++++++++++++++++++
17 drivers/mfd/bcm2835-pm.c | 12 +-
18 drivers/soc/bcm/bcm2835-power.c | 6 +-
19 include/linux/mfd/bcm2835-pm.h | 2 +-
20 6 files changed, 305 insertions(+), 10 deletions(-)
21 create mode 100644 drivers/char/broadcom/rpivid-mem.c
23 --- a/drivers/char/broadcom/Kconfig
24 +++ b/drivers/char/broadcom/Kconfig
25 @@ -49,3 +49,11 @@ config BCM2835_SMI_DEV
26 This driver provides a character device interface (ioctl + read/write) to
27 Broadcom's Secondary Memory interface. The low-level functionality is provided
28 by the SMI driver itself.
31 + tristate "Character device driver for the Raspberry Pi RPIVid video decoder hardware"
34 + This driver provides a character device interface for memory-map operations
35 + so userspace tools can access the control and status registers of the
36 + Raspberry Pi RPiVid video decoder hardware.
37 --- a/drivers/char/broadcom/Makefile
38 +++ b/drivers/char/broadcom/Makefile
39 @@ -4,3 +4,4 @@ obj-$(CONFIG_BCM_VC_SM) += vc_sm
41 obj-$(CONFIG_BCM2835_DEVGPIOMEM)+= bcm2835-gpiomem.o
42 obj-$(CONFIG_BCM2835_SMI_DEV) += bcm2835_smi_dev.o
43 +obj-$(CONFIG_RPIVID_MEM) += rpivid-mem.o
45 +++ b/drivers/char/broadcom/rpivid-mem.c
48 + * rpivid-mem.c - character device access to the RPiVid decoder registers
50 + * Based on bcm2835-gpiomem.c. Provides IO memory access to the decoder
51 + * register blocks such that ffmpeg plugins can access the hardware.
53 + * Jonathan Bell <jonathan@raspberrypi.org>
54 + * Copyright (c) 2019, Raspberry Pi (Trading) Ltd.
56 + * Redistribution and use in source and binary forms, with or without
57 + * modification, are permitted provided that the following conditions
59 + * 1. Redistributions of source code must retain the above copyright
60 + * notice, this list of conditions, and the following disclaimer,
61 + * without modification.
62 + * 2. Redistributions in binary form must reproduce the above copyright
63 + * notice, this list of conditions and the following disclaimer in the
64 + * documentation and/or other materials provided with the distribution.
65 + * 3. The names of the above-listed copyright holders may not be used
66 + * to endorse or promote products derived from this software without
67 + * specific prior written permission.
69 + * ALTERNATIVELY, this software may be distributed under the terms of the
70 + * GNU General Public License ("GPL") version 2, as published by the Free
71 + * Software Foundation.
73 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
74 + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
75 + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
76 + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
77 + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
78 + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
79 + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
80 + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
81 + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
82 + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
83 + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
86 +#include <linux/kernel.h>
87 +#include <linux/module.h>
88 +#include <linux/of.h>
89 +#include <linux/of_device.h>
90 +#include <linux/platform_device.h>
91 +#include <linux/mm.h>
92 +#include <linux/slab.h>
93 +#include <linux/cdev.h>
94 +#include <linux/pagemap.h>
95 +#include <linux/io.h>
97 +#define DRIVER_NAME "rpivid-mem"
98 +#define DEVICE_MINOR 0
100 +struct rpivid_mem_priv {
102 + struct class *class;
103 + struct cdev rpivid_mem_cdev;
104 + unsigned long regs_phys;
105 + unsigned long mem_window_len;
106 + struct device *dev;
110 +static int rpivid_mem_open(struct inode *inode, struct file *file)
112 + int dev = iminor(inode);
114 + struct rpivid_mem_priv *priv;
115 + if (dev != DEVICE_MINOR && dev != DEVICE_MINOR + 1)
118 + priv = container_of(inode->i_cdev, struct rpivid_mem_priv,
122 + file->private_data = priv;
126 +static int rpivid_mem_release(struct inode *inode, struct file *file)
128 + int dev = iminor(inode);
131 + if (dev != DEVICE_MINOR && dev != DEVICE_MINOR + 1)
137 +static const struct vm_operations_struct rpivid_mem_vm_ops = {
138 +#ifdef CONFIG_HAVE_IOREMAP_PROT
139 + .access = generic_access_phys
143 +static int rpivid_mem_mmap(struct file *file, struct vm_area_struct *vma)
145 + struct rpivid_mem_priv *priv;
146 + unsigned long pages;
148 + priv = file->private_data;
149 + pages = priv->regs_phys >> PAGE_SHIFT;
151 + * The address decode is far larger than the actual number of registers.
152 + * Just map the whole lot in.
154 + vma->vm_page_prot = phys_mem_access_prot(file, pages,
155 + priv->mem_window_len,
156 + vma->vm_page_prot);
157 + vma->vm_ops = &rpivid_mem_vm_ops;
158 + if (remap_pfn_range(vma, vma->vm_start,
160 + priv->mem_window_len,
161 + vma->vm_page_prot)) {
167 +static const struct file_operations
169 + .owner = THIS_MODULE,
170 + .open = rpivid_mem_open,
171 + .release = rpivid_mem_release,
172 + .mmap = rpivid_mem_mmap,
175 +static const struct of_device_id rpivid_mem_of_match[];
176 +static int rpivid_mem_probe(struct platform_device *pdev)
180 + const struct of_device_id *id;
181 + struct device *dev = &pdev->dev;
182 + struct device *rpivid_mem_dev;
183 + struct resource *ioresource;
184 + struct rpivid_mem_priv *priv;
187 + /* Allocate buffers and instance data */
189 + priv = kzalloc(sizeof(struct rpivid_mem_priv), GFP_KERNEL);
193 + goto failed_inst_alloc;
195 + platform_set_drvdata(pdev, priv);
198 + id = of_match_device(rpivid_mem_of_match, dev);
201 + priv->name = id->data;
203 + ioresource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
205 + priv->regs_phys = ioresource->start;
206 + priv->mem_window_len = ioresource->end - ioresource->start;
208 + dev_err(priv->dev, "failed to get IO resource");
210 + goto failed_get_resource;
213 + /* Create character device entries */
215 + err = alloc_chrdev_region(&priv->devid,
216 + DEVICE_MINOR, 2, priv->name);
218 + dev_err(priv->dev, "unable to allocate device number");
219 + goto failed_alloc_chrdev;
221 + cdev_init(&priv->rpivid_mem_cdev, &rpivid_mem_fops);
222 + priv->rpivid_mem_cdev.owner = THIS_MODULE;
223 + err = cdev_add(&priv->rpivid_mem_cdev, priv->devid, 2);
225 + dev_err(priv->dev, "unable to register device");
226 + goto failed_cdev_add;
229 + /* Create sysfs entries */
231 + priv->class = class_create(THIS_MODULE, priv->name);
232 + ptr_err = priv->class;
233 + if (IS_ERR(ptr_err))
234 + goto failed_class_create;
236 + rpivid_mem_dev = device_create(priv->class, NULL,
239 + ptr_err = rpivid_mem_dev;
240 + if (IS_ERR(ptr_err))
241 + goto failed_device_create;
245 + char *oldname = kstrdup(priv->name, GFP_KERNEL);
252 + (void)device_create(priv->class, NULL, priv->devid + 1, NULL,
257 + dev_info(priv->dev, "%s initialised: Registers at 0x%08lx length 0x%08lx",
258 + priv->name, priv->regs_phys, priv->mem_window_len);
262 +failed_device_create:
263 + class_destroy(priv->class);
264 +failed_class_create:
265 + cdev_del(&priv->rpivid_mem_cdev);
266 + err = PTR_ERR(ptr_err);
268 + unregister_chrdev_region(priv->devid, 1);
269 +failed_alloc_chrdev:
270 +failed_get_resource:
273 + dev_err(priv->dev, "could not load rpivid_mem");
277 +static int rpivid_mem_remove(struct platform_device *pdev)
279 + struct device *dev = &pdev->dev;
280 + struct rpivid_mem_priv *priv = platform_get_drvdata(pdev);
282 + device_destroy(priv->class, priv->devid);
283 + class_destroy(priv->class);
284 + cdev_del(&priv->rpivid_mem_cdev);
285 + unregister_chrdev_region(priv->devid, 1);
288 + dev_info(dev, "%s driver removed - OK", priv->name);
292 +static const struct of_device_id rpivid_mem_of_match[] = {
294 + .compatible = "raspberrypi,rpivid-hevc-decoder",
295 + .data = "rpivid-hevcmem",
298 + .compatible = "raspberrypi,rpivid-h264-decoder",
299 + .data = "rpivid-h264mem",
302 + .compatible = "raspberrypi,rpivid-vp9-decoder",
303 + .data = "rpivid-vp9mem",
305 + /* The "intc" is included as this block of hardware contains the
306 + * "frame done" status flags.
309 + .compatible = "raspberrypi,rpivid-local-intc",
310 + .data = "rpivid-intcmem",
312 + { /* sentinel */ },
315 +MODULE_DEVICE_TABLE(of, rpivid_mem_of_match);
317 +static struct platform_driver rpivid_mem_driver = {
318 + .probe = rpivid_mem_probe,
319 + .remove = rpivid_mem_remove,
321 + .name = DRIVER_NAME,
322 + .owner = THIS_MODULE,
323 + .of_match_table = rpivid_mem_of_match,
327 +module_platform_driver(rpivid_mem_driver);
329 +MODULE_ALIAS("platform:rpivid-mem");
330 +MODULE_LICENSE("GPL");
331 +MODULE_DESCRIPTION("Driver for accessing RPiVid decoder registers from userspace");
332 +MODULE_AUTHOR("Jonathan Bell <jonathan@raspberrypi.org>");
333 --- a/drivers/mfd/bcm2835-pm.c
334 +++ b/drivers/mfd/bcm2835-pm.c
335 @@ -50,14 +50,14 @@ static int bcm2835_pm_probe(struct platf
339 - /* Map the ARGON ASB regs if present. */
340 + /* Map the RPiVid ASB regs if present. */
341 res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
343 - pm->arg_asb = devm_ioremap_resource(dev, res);
344 - if (IS_ERR(pm->arg_asb)) {
345 - dev_err(dev, "Failed to map ARGON ASB: %ld\n",
346 - PTR_ERR(pm->arg_asb));
347 - return PTR_ERR(pm->arg_asb);
348 + pm->rpivid_asb = devm_ioremap_resource(dev, res);
349 + if (IS_ERR(pm->rpivid_asb)) {
350 + dev_err(dev, "Failed to map RPiVid ASB: %ld\n",
351 + PTR_ERR(pm->rpivid_asb));
352 + return PTR_ERR(pm->rpivid_asb);
356 --- a/drivers/soc/bcm/bcm2835-power.c
357 +++ b/drivers/soc/bcm/bcm2835-power.c
358 @@ -637,15 +637,15 @@ static int bcm2835_power_probe(struct pl
359 power->base = pm->base;
360 power->asb = pm->asb;
362 - /* 2711 hack: the new ARGON ASB took over V3D, which is our
363 + /* 2711 hack: the new RPiVid ASB took over V3D, which is our
364 * only consumer of this driver so far. The old ASB seems to
365 * still be present with ISP and H264 bits but no V3D, but I
366 * don't know if that's real or not. The V3D is in the same
367 * place in the new ASB as the old one, so just poke the new
371 - power->asb = pm->arg_asb;
372 + if (pm->rpivid_asb) {
373 + power->asb = pm->rpivid_asb;
374 power->is_2711 = true;
377 --- a/include/linux/mfd/bcm2835-pm.h
378 +++ b/include/linux/mfd/bcm2835-pm.h
379 @@ -9,7 +9,7 @@ struct bcm2835_pm {
383 - void __iomem *arg_asb;
384 + void __iomem *rpivid_asb;
387 #endif /* BCM2835_MFD_PM_H */