| .. | .. |
|---|
| 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); |
|---|