.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | V4L2 device support. |
---|
3 | 4 | |
---|
4 | 5 | Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl> |
---|
5 | 6 | |
---|
6 | | - This program is free software; you can redistribute it and/or modify |
---|
7 | | - it under the terms of the GNU General Public License as published by |
---|
8 | | - the Free Software Foundation; either version 2 of the License, or |
---|
9 | | - (at your option) any later version. |
---|
10 | | - |
---|
11 | | - This program is distributed in the hope that it will be useful, |
---|
12 | | - but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
13 | | - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
14 | | - GNU General Public License for more details. |
---|
15 | | - |
---|
16 | | - You should have received a copy of the GNU General Public License |
---|
17 | | - along with this program; if not, write to the Free Software |
---|
18 | | - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
---|
19 | 7 | */ |
---|
20 | 8 | |
---|
21 | 9 | #include <linux/types.h> |
---|
22 | 10 | #include <linux/ioctl.h> |
---|
23 | 11 | #include <linux/module.h> |
---|
24 | | -#include <linux/i2c.h> |
---|
25 | 12 | #include <linux/slab.h> |
---|
26 | | -#if defined(CONFIG_SPI) |
---|
27 | | -#include <linux/spi/spi.h> |
---|
28 | | -#endif |
---|
29 | 13 | #include <linux/videodev2.h> |
---|
30 | 14 | #include <media/v4l2-device.h> |
---|
31 | 15 | #include <media/v4l2-ctrls.h> |
---|
.. | .. |
---|
114 | 98 | /* Unregister subdevs */ |
---|
115 | 99 | list_for_each_entry_safe(sd, next, &v4l2_dev->subdevs, list) { |
---|
116 | 100 | v4l2_device_unregister_subdev(sd); |
---|
117 | | -#if IS_ENABLED(CONFIG_I2C) |
---|
118 | | - if (sd->flags & V4L2_SUBDEV_FL_IS_I2C) { |
---|
119 | | - struct i2c_client *client = v4l2_get_subdevdata(sd); |
---|
120 | | - |
---|
121 | | - /* |
---|
122 | | - * We need to unregister the i2c client |
---|
123 | | - * explicitly. We cannot rely on |
---|
124 | | - * i2c_del_adapter to always unregister |
---|
125 | | - * clients for us, since if the i2c bus is a |
---|
126 | | - * platform bus, then it is never deleted. |
---|
127 | | - * |
---|
128 | | - * Device tree or ACPI based devices must not |
---|
129 | | - * be unregistered as they have not been |
---|
130 | | - * registered by us, and would not be |
---|
131 | | - * re-created by just probing the V4L2 driver. |
---|
132 | | - */ |
---|
133 | | - if (client && |
---|
134 | | - !client->dev.of_node && !client->dev.fwnode) |
---|
135 | | - i2c_unregister_device(client); |
---|
136 | | - continue; |
---|
137 | | - } |
---|
138 | | -#endif |
---|
139 | | -#if defined(CONFIG_SPI) |
---|
140 | | - if (sd->flags & V4L2_SUBDEV_FL_IS_SPI) { |
---|
141 | | - struct spi_device *spi = v4l2_get_subdevdata(sd); |
---|
142 | | - |
---|
143 | | - if (spi && !spi->dev.of_node && !spi->dev.fwnode) |
---|
144 | | - spi_unregister_device(spi); |
---|
145 | | - continue; |
---|
146 | | - } |
---|
147 | | -#endif |
---|
| 101 | + if (sd->flags & V4L2_SUBDEV_FL_IS_I2C) |
---|
| 102 | + v4l2_i2c_subdev_unregister(sd); |
---|
| 103 | + else if (sd->flags & V4L2_SUBDEV_FL_IS_SPI) |
---|
| 104 | + v4l2_spi_subdev_unregister(sd); |
---|
148 | 105 | } |
---|
149 | 106 | /* Mark as unregistered, thus preventing duplicate unregistrations */ |
---|
150 | 107 | v4l2_dev->name[0] = '\0'; |
---|
.. | .. |
---|
154 | 111 | int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, |
---|
155 | 112 | struct v4l2_subdev *sd) |
---|
156 | 113 | { |
---|
157 | | -#if defined(CONFIG_MEDIA_CONTROLLER) |
---|
158 | | - struct media_entity *entity = &sd->entity; |
---|
159 | | -#endif |
---|
160 | 114 | int err; |
---|
161 | 115 | |
---|
162 | 116 | /* Check for valid input */ |
---|
.. | .. |
---|
178 | 132 | |
---|
179 | 133 | sd->v4l2_dev = v4l2_dev; |
---|
180 | 134 | /* This just returns 0 if either of the two args is NULL */ |
---|
181 | | - err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler, NULL); |
---|
| 135 | + err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler, |
---|
| 136 | + NULL, true); |
---|
182 | 137 | if (err) |
---|
183 | 138 | goto error_module; |
---|
184 | 139 | |
---|
185 | 140 | #if defined(CONFIG_MEDIA_CONTROLLER) |
---|
186 | 141 | /* Register the entity. */ |
---|
187 | 142 | if (v4l2_dev->mdev) { |
---|
188 | | - err = media_device_register_entity(v4l2_dev->mdev, entity); |
---|
| 143 | + err = media_device_register_entity(v4l2_dev->mdev, &sd->entity); |
---|
189 | 144 | if (err < 0) |
---|
190 | 145 | goto error_module; |
---|
191 | 146 | } |
---|
.. | .. |
---|
205 | 160 | |
---|
206 | 161 | error_unregister: |
---|
207 | 162 | #if defined(CONFIG_MEDIA_CONTROLLER) |
---|
208 | | - media_device_unregister_entity(entity); |
---|
| 163 | + media_device_unregister_entity(&sd->entity); |
---|
209 | 164 | #endif |
---|
210 | 165 | error_module: |
---|
211 | 166 | if (!sd->owner_v4l2_dev) |
---|
.. | .. |
---|
215 | 170 | } |
---|
216 | 171 | EXPORT_SYMBOL_GPL(v4l2_device_register_subdev); |
---|
217 | 172 | |
---|
| 173 | +static void v4l2_subdev_release(struct v4l2_subdev *sd) |
---|
| 174 | +{ |
---|
| 175 | + struct module *owner = !sd->owner_v4l2_dev ? sd->owner : NULL; |
---|
| 176 | + |
---|
| 177 | + if (sd->internal_ops && sd->internal_ops->release) |
---|
| 178 | + sd->internal_ops->release(sd); |
---|
| 179 | + sd->devnode = NULL; |
---|
| 180 | + module_put(owner); |
---|
| 181 | +} |
---|
| 182 | + |
---|
218 | 183 | static void v4l2_device_release_subdev_node(struct video_device *vdev) |
---|
219 | 184 | { |
---|
220 | | - struct v4l2_subdev *sd = video_get_drvdata(vdev); |
---|
221 | | - sd->devnode = NULL; |
---|
| 185 | + v4l2_subdev_release(video_get_drvdata(vdev)); |
---|
222 | 186 | kfree(vdev); |
---|
223 | 187 | } |
---|
224 | 188 | |
---|
225 | | -int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev) |
---|
| 189 | +int __v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev, |
---|
| 190 | + bool read_only) |
---|
226 | 191 | { |
---|
227 | 192 | struct video_device *vdev; |
---|
228 | 193 | struct v4l2_subdev *sd; |
---|
.. | .. |
---|
245 | 210 | } |
---|
246 | 211 | |
---|
247 | 212 | video_set_drvdata(vdev, sd); |
---|
248 | | - strlcpy(vdev->name, sd->name, sizeof(vdev->name)); |
---|
| 213 | + strscpy(vdev->name, sd->name, sizeof(vdev->name)); |
---|
| 214 | + vdev->dev_parent = sd->dev; |
---|
249 | 215 | vdev->v4l2_dev = v4l2_dev; |
---|
250 | 216 | vdev->fops = &v4l2_subdev_fops; |
---|
251 | 217 | vdev->release = v4l2_device_release_subdev_node; |
---|
252 | 218 | vdev->ctrl_handler = sd->ctrl_handler; |
---|
| 219 | + if (read_only) |
---|
| 220 | + set_bit(V4L2_FL_SUBDEV_RO_DEVNODE, &vdev->flags); |
---|
253 | 221 | err = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1, |
---|
254 | 222 | sd->owner); |
---|
255 | 223 | if (err < 0) { |
---|
.. | .. |
---|
287 | 255 | |
---|
288 | 256 | return err; |
---|
289 | 257 | } |
---|
290 | | -EXPORT_SYMBOL_GPL(v4l2_device_register_subdev_nodes); |
---|
| 258 | +EXPORT_SYMBOL_GPL(__v4l2_device_register_subdev_nodes); |
---|
291 | 259 | |
---|
292 | 260 | void v4l2_device_unregister_subdev(struct v4l2_subdev *sd) |
---|
293 | 261 | { |
---|
.. | .. |
---|
316 | 284 | media_device_unregister_entity(&sd->entity); |
---|
317 | 285 | } |
---|
318 | 286 | #endif |
---|
319 | | - video_unregister_device(sd->devnode); |
---|
320 | | - if (!sd->owner_v4l2_dev) |
---|
321 | | - module_put(sd->owner); |
---|
| 287 | + if (sd->devnode) |
---|
| 288 | + video_unregister_device(sd->devnode); |
---|
| 289 | + else |
---|
| 290 | + v4l2_subdev_release(sd); |
---|
322 | 291 | } |
---|
323 | 292 | EXPORT_SYMBOL_GPL(v4l2_device_unregister_subdev); |
---|