3f3c5ea148d1649af89886fa962a49d31aa94c0c
[oweals/openwrt.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 */