Merge tag 'v5.15.57' into rpi-5.15.y
[platform/kernel/linux-rpi.git] / drivers / char / broadcom / rpivid-mem.c
1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2 /**
3  * rpivid-mem.c - character device access to the RPiVid decoder registers
4  *
5  * Based on bcm2835-gpiomem.c. Provides IO memory access to the decoder
6  * register blocks such that ffmpeg plugins can access the hardware.
7  *
8  * Jonathan Bell <jonathan@raspberrypi.org>
9  * Copyright (c) 2019, Raspberry Pi (Trading) Ltd.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions, and the following disclaimer,
16  *    without modification.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. The names of the above-listed copyright holders may not be used
21  *    to endorse or promote products derived from this software without
22  *    specific prior written permission.
23  *
24  * ALTERNATIVELY, this software may be distributed under the terms of the
25  * GNU General Public License ("GPL") version 2, as published by the Free
26  * Software Foundation.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
29  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
30  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
31  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
32  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
33  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
34  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
35  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
36  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
37  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
38  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39  */
40
41 #include <linux/kernel.h>
42 #include <linux/module.h>
43 #include <linux/of.h>
44 #include <linux/of_device.h>
45 #include <linux/platform_device.h>
46 #include <linux/mm.h>
47 #include <linux/slab.h>
48 #include <linux/cdev.h>
49 #include <linux/pagemap.h>
50 #include <linux/io.h>
51
52 #define DRIVER_NAME "rpivid-mem"
53 #define DEVICE_MINOR 0
54
55 struct rpivid_mem_priv {
56         dev_t devid;
57         struct class *class;
58         struct cdev rpivid_mem_cdev;
59         unsigned long regs_phys;
60         unsigned long mem_window_len;
61         struct device *dev;
62         const char *name;
63 };
64
65 static int rpivid_mem_open(struct inode *inode, struct file *file)
66 {
67         int dev = iminor(inode);
68         int ret = 0;
69         struct rpivid_mem_priv *priv;
70
71         if (dev != DEVICE_MINOR && dev != DEVICE_MINOR + 1)
72                 ret = -ENXIO;
73
74         priv = container_of(inode->i_cdev, struct rpivid_mem_priv,
75                                 rpivid_mem_cdev);
76         if (!priv)
77                 return -EINVAL;
78         file->private_data = priv;
79         return ret;
80 }
81
82 static int rpivid_mem_release(struct inode *inode, struct file *file)
83 {
84         int dev = iminor(inode);
85         int ret = 0;
86
87         if (dev != DEVICE_MINOR && dev != DEVICE_MINOR + 1)
88                 ret = -ENXIO;
89
90         return ret;
91 }
92
93 static const struct vm_operations_struct rpivid_mem_vm_ops = {
94 #ifdef CONFIG_HAVE_IOREMAP_PROT
95         .access = generic_access_phys
96 #endif
97 };
98
99 static int rpivid_mem_mmap(struct file *file, struct vm_area_struct *vma)
100 {
101         struct rpivid_mem_priv *priv;
102         unsigned long pages;
103         unsigned long len;
104
105         priv = file->private_data;
106         pages = priv->regs_phys >> PAGE_SHIFT;
107         /*
108          * The address decode is far larger than the actual number of registers.
109          * Just map the whole lot in.
110          */
111         len = min(vma->vm_end - vma->vm_start, priv->mem_window_len);
112         vma->vm_page_prot = phys_mem_access_prot(file, pages, len,
113                                                  vma->vm_page_prot);
114         vma->vm_ops = &rpivid_mem_vm_ops;
115         if (remap_pfn_range(vma, vma->vm_start,
116                             pages, len,
117                             vma->vm_page_prot)) {
118                 return -EAGAIN;
119         }
120         return 0;
121 }
122
123 static const struct file_operations
124 rpivid_mem_fops = {
125         .owner = THIS_MODULE,
126         .open = rpivid_mem_open,
127         .release = rpivid_mem_release,
128         .mmap = rpivid_mem_mmap,
129 };
130
131 static const struct of_device_id rpivid_mem_of_match[];
132 static int rpivid_mem_probe(struct platform_device *pdev)
133 {
134         int err;
135         const struct of_device_id *id;
136         struct device *dev = &pdev->dev;
137         struct resource *ioresource;
138         struct rpivid_mem_priv *priv;
139
140         /* Allocate buffers and instance data */
141
142         priv = kzalloc(sizeof(struct rpivid_mem_priv), GFP_KERNEL);
143
144         if (!priv) {
145                 err = -ENOMEM;
146                 goto failed_inst_alloc;
147         }
148         platform_set_drvdata(pdev, priv);
149
150         priv->dev = dev;
151         id = of_match_device(rpivid_mem_of_match, dev);
152         if (!id)
153                 return -EINVAL;
154         priv->name = id->data;
155
156         ioresource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
157         if (ioresource) {
158                 priv->regs_phys = ioresource->start;
159                 priv->mem_window_len = (ioresource->end + 1) - ioresource->start;
160         } else {
161                 dev_err(priv->dev, "failed to get IO resource");
162                 err = -ENOENT;
163                 goto failed_get_resource;
164         }
165
166         /* Create character device entries */
167
168         err = alloc_chrdev_region(&priv->devid,
169                                   DEVICE_MINOR, 2, priv->name);
170         if (err != 0) {
171                 dev_err(priv->dev, "unable to allocate device number");
172                 goto failed_alloc_chrdev;
173         }
174         cdev_init(&priv->rpivid_mem_cdev, &rpivid_mem_fops);
175         priv->rpivid_mem_cdev.owner = THIS_MODULE;
176         err = cdev_add(&priv->rpivid_mem_cdev, priv->devid, 2);
177         if (err != 0) {
178                 dev_err(priv->dev, "unable to register device");
179                 goto failed_cdev_add;
180         }
181
182         /* Create sysfs entries */
183
184         priv->class = class_create(THIS_MODULE, priv->name);
185         if (IS_ERR(priv->class)) {
186                 err = PTR_ERR(priv->class);
187                 goto failed_class_create;
188         }
189
190         dev = device_create(priv->class, NULL, priv->devid, NULL, priv->name);
191         if (IS_ERR(dev)) {
192                 err = PTR_ERR(dev);
193                 goto failed_device_create;
194         }
195
196         dev_info(priv->dev, "%s initialised: Registers at 0x%08lx length 0x%08lx",
197                 priv->name, priv->regs_phys, priv->mem_window_len);
198
199         return 0;
200
201 failed_device_create:
202         class_destroy(priv->class);
203 failed_class_create:
204         cdev_del(&priv->rpivid_mem_cdev);
205 failed_cdev_add:
206         unregister_chrdev_region(priv->devid, 1);
207 failed_alloc_chrdev:
208 failed_get_resource:
209         kfree(priv);
210 failed_inst_alloc:
211         dev_err(&pdev->dev, "could not load rpivid_mem");
212         return err;
213 }
214
215 static int rpivid_mem_remove(struct platform_device *pdev)
216 {
217         struct device *dev = &pdev->dev;
218         struct rpivid_mem_priv *priv = platform_get_drvdata(pdev);
219
220         device_destroy(priv->class, priv->devid);
221         class_destroy(priv->class);
222         cdev_del(&priv->rpivid_mem_cdev);
223         unregister_chrdev_region(priv->devid, 1);
224         kfree(priv);
225
226         dev_info(dev, "%s driver removed - OK", priv->name);
227         return 0;
228 }
229
230 static const struct of_device_id rpivid_mem_of_match[] = {
231         {
232                 .compatible = "raspberrypi,rpivid-hevc-decoder",
233                 .data = "rpivid-hevcmem",
234         },
235         {
236                 .compatible = "raspberrypi,rpivid-h264-decoder",
237                 .data = "rpivid-h264mem",
238         },
239         {
240                 .compatible = "raspberrypi,rpivid-vp9-decoder",
241                 .data = "rpivid-vp9mem",
242         },
243         /* The "intc" is included as this block of hardware contains the
244          * "frame done" status flags.
245          */
246         {
247                 .compatible = "raspberrypi,rpivid-local-intc",
248                 .data = "rpivid-intcmem",
249         },
250         { /* sentinel */ },
251 };
252
253 MODULE_DEVICE_TABLE(of, rpivid_mem_of_match);
254
255 static struct platform_driver rpivid_mem_driver = {
256         .probe = rpivid_mem_probe,
257         .remove = rpivid_mem_remove,
258         .driver = {
259                    .name = DRIVER_NAME,
260                    .owner = THIS_MODULE,
261                    .of_match_table = rpivid_mem_of_match,
262                    },
263 };
264
265 module_platform_driver(rpivid_mem_driver);
266
267 MODULE_ALIAS("platform:rpivid-mem");
268 MODULE_LICENSE("GPL");
269 MODULE_DESCRIPTION("Driver for accessing RPiVid decoder registers from userspace");
270 MODULE_AUTHOR("Jonathan Bell <jonathan@raspberrypi.org>");