3f3c5ea148d1649af89886fa962a49d31aa94c0c
[openwrt/staging/blocktrron.git] /
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
5 control registers
6
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.
11
12 Signed-off-by: Jonathan Bell <jonathan@raspberrypi.org>
13 ---
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
22
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.
29 +
30 +config RPIVID_MEM
31 + tristate "Character device driver for the Raspberry Pi RPIVid video decoder hardware"
32 + default n
33 + help
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
40
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
44 --- /dev/null
45 +++ b/drivers/char/broadcom/rpivid-mem.c
46 @@ -0,0 +1,286 @@
47 +/**
48 + * rpivid-mem.c - character device access to the RPiVid decoder registers
49 + *
50 + * Based on bcm2835-gpiomem.c. Provides IO memory access to the decoder
51 + * register blocks such that ffmpeg plugins can access the hardware.
52 + *
53 + * Jonathan Bell <jonathan@raspberrypi.org>
54 + * Copyright (c) 2019, Raspberry Pi (Trading) Ltd.
55 + *
56 + * Redistribution and use in source and binary forms, with or without
57 + * modification, are permitted provided that the following conditions
58 + * are met:
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.
68 + *
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.
72 + *
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.
84 + */
85 +
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>
96 +
97 +#define DRIVER_NAME "rpivid-mem"
98 +#define DEVICE_MINOR 0
99 +
100 +struct rpivid_mem_priv {
101 + dev_t devid;
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;
107 + const char *name;
108 +};
109 +
110 +static int rpivid_mem_open(struct inode *inode, struct file *file)
111 +{
112 + int dev = iminor(inode);
113 + int ret = 0;
114 + struct rpivid_mem_priv *priv;
115 + if (dev != DEVICE_MINOR && dev != DEVICE_MINOR + 1)
116 + ret = -ENXIO;
117 +
118 + priv = container_of(inode->i_cdev, struct rpivid_mem_priv,
119 + rpivid_mem_cdev);
120 + if (!priv)
121 + return -EINVAL;
122 + file->private_data = priv;
123 + return ret;
124 +}
125 +
126 +static int rpivid_mem_release(struct inode *inode, struct file *file)
127 +{
128 + int dev = iminor(inode);
129 + int ret = 0;
130 +
131 + if (dev != DEVICE_MINOR && dev != DEVICE_MINOR + 1)
132 + ret = -ENXIO;
133 +
134 + return ret;
135 +}
136 +
137 +static const struct vm_operations_struct rpivid_mem_vm_ops = {
138 +#ifdef CONFIG_HAVE_IOREMAP_PROT
139 + .access = generic_access_phys
140 +#endif
141 +};
142 +
143 +static int rpivid_mem_mmap(struct file *file, struct vm_area_struct *vma)
144 +{
145 + struct rpivid_mem_priv *priv;
146 + unsigned long pages;
147 +
148 + priv = file->private_data;
149 + pages = priv->regs_phys >> PAGE_SHIFT;
150 + /*
151 + * The address decode is far larger than the actual number of registers.
152 + * Just map the whole lot in.
153 + */
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,
159 + pages,
160 + priv->mem_window_len,
161 + vma->vm_page_prot)) {
162 + return -EAGAIN;
163 + }
164 + return 0;
165 +}
166 +
167 +static const struct file_operations
168 +rpivid_mem_fops = {
169 + .owner = THIS_MODULE,
170 + .open = rpivid_mem_open,
171 + .release = rpivid_mem_release,
172 + .mmap = rpivid_mem_mmap,
173 +};
174 +
175 +static const struct of_device_id rpivid_mem_of_match[];
176 +static int rpivid_mem_probe(struct platform_device *pdev)
177 +{
178 + int err;
179 + void *ptr_err;
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;
185 +
186 +
187 + /* Allocate buffers and instance data */
188 +
189 + priv = kzalloc(sizeof(struct rpivid_mem_priv), GFP_KERNEL);
190 +
191 + if (!priv) {
192 + err = -ENOMEM;
193 + goto failed_inst_alloc;
194 + }
195 + platform_set_drvdata(pdev, priv);
196 +
197 + priv->dev = dev;
198 + id = of_match_device(rpivid_mem_of_match, dev);
199 + if (!id)
200 + return -EINVAL;
201 + priv->name = id->data;
202 +
203 + ioresource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
204 + if (ioresource) {
205 + priv->regs_phys = ioresource->start;
206 + priv->mem_window_len = ioresource->end - ioresource->start;
207 + } else {
208 + dev_err(priv->dev, "failed to get IO resource");
209 + err = -ENOENT;
210 + goto failed_get_resource;
211 + }
212 +
213 + /* Create character device entries */
214 +
215 + err = alloc_chrdev_region(&priv->devid,
216 + DEVICE_MINOR, 2, priv->name);
217 + if (err != 0) {
218 + dev_err(priv->dev, "unable to allocate device number");
219 + goto failed_alloc_chrdev;
220 + }
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);
224 + if (err != 0) {
225 + dev_err(priv->dev, "unable to register device");
226 + goto failed_cdev_add;
227 + }
228 +
229 + /* Create sysfs entries */
230 +
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;
235 +
236 + rpivid_mem_dev = device_create(priv->class, NULL,
237 + priv->devid, NULL,
238 + priv->name);
239 + ptr_err = rpivid_mem_dev;
240 + if (IS_ERR(ptr_err))
241 + goto failed_device_create;
242 +
243 + /* Legacy alias */
244 + {
245 + char *oldname = kstrdup(priv->name, GFP_KERNEL);
246 +
247 + oldname[1] = 'a';
248 + oldname[2] = 'r';
249 + oldname[3] = 'g';
250 + oldname[4] = 'o';
251 + oldname[5] = 'n';
252 + (void)device_create(priv->class, NULL, priv->devid + 1, NULL,
253 + oldname + 1);
254 + kfree(oldname);
255 + }
256 +
257 + dev_info(priv->dev, "%s initialised: Registers at 0x%08lx length 0x%08lx",
258 + priv->name, priv->regs_phys, priv->mem_window_len);
259 +
260 + return 0;
261 +
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);
267 +failed_cdev_add:
268 + unregister_chrdev_region(priv->devid, 1);
269 +failed_alloc_chrdev:
270 +failed_get_resource:
271 + kfree(priv);
272 +failed_inst_alloc:
273 + dev_err(priv->dev, "could not load rpivid_mem");
274 + return err;
275 +}
276 +
277 +static int rpivid_mem_remove(struct platform_device *pdev)
278 +{
279 + struct device *dev = &pdev->dev;
280 + struct rpivid_mem_priv *priv = platform_get_drvdata(pdev);
281 +
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);
286 + kfree(priv);
287 +
288 + dev_info(dev, "%s driver removed - OK", priv->name);
289 + return 0;
290 +}
291 +
292 +static const struct of_device_id rpivid_mem_of_match[] = {
293 + {
294 + .compatible = "raspberrypi,rpivid-hevc-decoder",
295 + .data = "rpivid-hevcmem",
296 + },
297 + {
298 + .compatible = "raspberrypi,rpivid-h264-decoder",
299 + .data = "rpivid-h264mem",
300 + },
301 + {
302 + .compatible = "raspberrypi,rpivid-vp9-decoder",
303 + .data = "rpivid-vp9mem",
304 + },
305 + /* The "intc" is included as this block of hardware contains the
306 + * "frame done" status flags.
307 + */
308 + {
309 + .compatible = "raspberrypi,rpivid-local-intc",
310 + .data = "rpivid-intcmem",
311 + },
312 + { /* sentinel */ },
313 +};
314 +
315 +MODULE_DEVICE_TABLE(of, rpivid_mem_of_match);
316 +
317 +static struct platform_driver rpivid_mem_driver = {
318 + .probe = rpivid_mem_probe,
319 + .remove = rpivid_mem_remove,
320 + .driver = {
321 + .name = DRIVER_NAME,
322 + .owner = THIS_MODULE,
323 + .of_match_table = rpivid_mem_of_match,
324 + },
325 +};
326 +
327 +module_platform_driver(rpivid_mem_driver);
328 +
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
336 if (ret)
337 return ret;
338
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);
342 if (res) {
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);
353 }
354 }
355
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;
361
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
368 * one for now.
369 */
370 - if (pm->arg_asb) {
371 - power->asb = pm->arg_asb;
372 + if (pm->rpivid_asb) {
373 + power->asb = pm->rpivid_asb;
374 power->is_2711 = true;
375 }
376
377 --- a/include/linux/mfd/bcm2835-pm.h
378 +++ b/include/linux/mfd/bcm2835-pm.h
379 @@ -9,7 +9,7 @@ struct bcm2835_pm {
380 struct device *dev;
381 void __iomem *base;
382 void __iomem *asb;
383 - void __iomem *arg_asb;
384 + void __iomem *rpivid_asb;
385 };
386
387 #endif /* BCM2835_MFD_PM_H */