18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci    V4L2 device support.
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci    Copyright (C) 2008  Hans Verkuil <hverkuil@xs4all.nl>
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/types.h>
108c2ecf20Sopenharmony_ci#include <linux/ioctl.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/slab.h>
138c2ecf20Sopenharmony_ci#include <linux/videodev2.h>
148c2ecf20Sopenharmony_ci#include <media/v4l2-device.h>
158c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ciint v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev)
188c2ecf20Sopenharmony_ci{
198c2ecf20Sopenharmony_ci	if (v4l2_dev == NULL)
208c2ecf20Sopenharmony_ci		return -EINVAL;
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&v4l2_dev->subdevs);
238c2ecf20Sopenharmony_ci	spin_lock_init(&v4l2_dev->lock);
248c2ecf20Sopenharmony_ci	v4l2_prio_init(&v4l2_dev->prio);
258c2ecf20Sopenharmony_ci	kref_init(&v4l2_dev->ref);
268c2ecf20Sopenharmony_ci	get_device(dev);
278c2ecf20Sopenharmony_ci	v4l2_dev->dev = dev;
288c2ecf20Sopenharmony_ci	if (dev == NULL) {
298c2ecf20Sopenharmony_ci		/* If dev == NULL, then name must be filled in by the caller */
308c2ecf20Sopenharmony_ci		if (WARN_ON(!v4l2_dev->name[0]))
318c2ecf20Sopenharmony_ci			return -EINVAL;
328c2ecf20Sopenharmony_ci		return 0;
338c2ecf20Sopenharmony_ci	}
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	/* Set name to driver name + device name if it is empty. */
368c2ecf20Sopenharmony_ci	if (!v4l2_dev->name[0])
378c2ecf20Sopenharmony_ci		snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s %s",
388c2ecf20Sopenharmony_ci			dev->driver->name, dev_name(dev));
398c2ecf20Sopenharmony_ci	if (!dev_get_drvdata(dev))
408c2ecf20Sopenharmony_ci		dev_set_drvdata(dev, v4l2_dev);
418c2ecf20Sopenharmony_ci	return 0;
428c2ecf20Sopenharmony_ci}
438c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_device_register);
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic void v4l2_device_release(struct kref *ref)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	struct v4l2_device *v4l2_dev =
488c2ecf20Sopenharmony_ci		container_of(ref, struct v4l2_device, ref);
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	if (v4l2_dev->release)
518c2ecf20Sopenharmony_ci		v4l2_dev->release(v4l2_dev);
528c2ecf20Sopenharmony_ci}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ciint v4l2_device_put(struct v4l2_device *v4l2_dev)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	return kref_put(&v4l2_dev->ref, v4l2_device_release);
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_device_put);
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ciint v4l2_device_set_name(struct v4l2_device *v4l2_dev, const char *basename,
618c2ecf20Sopenharmony_ci						atomic_t *instance)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	int num = atomic_inc_return(instance) - 1;
648c2ecf20Sopenharmony_ci	int len = strlen(basename);
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	if (basename[len - 1] >= '0' && basename[len - 1] <= '9')
678c2ecf20Sopenharmony_ci		snprintf(v4l2_dev->name, sizeof(v4l2_dev->name),
688c2ecf20Sopenharmony_ci				"%s-%d", basename, num);
698c2ecf20Sopenharmony_ci	else
708c2ecf20Sopenharmony_ci		snprintf(v4l2_dev->name, sizeof(v4l2_dev->name),
718c2ecf20Sopenharmony_ci				"%s%d", basename, num);
728c2ecf20Sopenharmony_ci	return num;
738c2ecf20Sopenharmony_ci}
748c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_device_set_name);
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_civoid v4l2_device_disconnect(struct v4l2_device *v4l2_dev)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	if (v4l2_dev->dev == NULL)
798c2ecf20Sopenharmony_ci		return;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	if (dev_get_drvdata(v4l2_dev->dev) == v4l2_dev)
828c2ecf20Sopenharmony_ci		dev_set_drvdata(v4l2_dev->dev, NULL);
838c2ecf20Sopenharmony_ci	put_device(v4l2_dev->dev);
848c2ecf20Sopenharmony_ci	v4l2_dev->dev = NULL;
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_device_disconnect);
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_civoid v4l2_device_unregister(struct v4l2_device *v4l2_dev)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	struct v4l2_subdev *sd, *next;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	/* Just return if v4l2_dev is NULL or if it was already
938c2ecf20Sopenharmony_ci	 * unregistered before. */
948c2ecf20Sopenharmony_ci	if (v4l2_dev == NULL || !v4l2_dev->name[0])
958c2ecf20Sopenharmony_ci		return;
968c2ecf20Sopenharmony_ci	v4l2_device_disconnect(v4l2_dev);
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	/* Unregister subdevs */
998c2ecf20Sopenharmony_ci	list_for_each_entry_safe(sd, next, &v4l2_dev->subdevs, list) {
1008c2ecf20Sopenharmony_ci		v4l2_device_unregister_subdev(sd);
1018c2ecf20Sopenharmony_ci		if (sd->flags & V4L2_SUBDEV_FL_IS_I2C)
1028c2ecf20Sopenharmony_ci			v4l2_i2c_subdev_unregister(sd);
1038c2ecf20Sopenharmony_ci		else if (sd->flags & V4L2_SUBDEV_FL_IS_SPI)
1048c2ecf20Sopenharmony_ci			v4l2_spi_subdev_unregister(sd);
1058c2ecf20Sopenharmony_ci	}
1068c2ecf20Sopenharmony_ci	/* Mark as unregistered, thus preventing duplicate unregistrations */
1078c2ecf20Sopenharmony_ci	v4l2_dev->name[0] = '\0';
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_device_unregister);
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ciint v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
1128c2ecf20Sopenharmony_ci				struct v4l2_subdev *sd)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	int err;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	/* Check for valid input */
1178c2ecf20Sopenharmony_ci	if (!v4l2_dev || !sd || sd->v4l2_dev || !sd->name[0])
1188c2ecf20Sopenharmony_ci		return -EINVAL;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	/*
1218c2ecf20Sopenharmony_ci	 * The reason to acquire the module here is to avoid unloading
1228c2ecf20Sopenharmony_ci	 * a module of sub-device which is registered to a media
1238c2ecf20Sopenharmony_ci	 * device. To make it possible to unload modules for media
1248c2ecf20Sopenharmony_ci	 * devices that also register sub-devices, do not
1258c2ecf20Sopenharmony_ci	 * try_module_get() such sub-device owners.
1268c2ecf20Sopenharmony_ci	 */
1278c2ecf20Sopenharmony_ci	sd->owner_v4l2_dev = v4l2_dev->dev && v4l2_dev->dev->driver &&
1288c2ecf20Sopenharmony_ci		sd->owner == v4l2_dev->dev->driver->owner;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	if (!sd->owner_v4l2_dev && !try_module_get(sd->owner))
1318c2ecf20Sopenharmony_ci		return -ENODEV;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	sd->v4l2_dev = v4l2_dev;
1348c2ecf20Sopenharmony_ci	/* This just returns 0 if either of the two args is NULL */
1358c2ecf20Sopenharmony_ci	err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler,
1368c2ecf20Sopenharmony_ci				    NULL, true);
1378c2ecf20Sopenharmony_ci	if (err)
1388c2ecf20Sopenharmony_ci		goto error_module;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci#if defined(CONFIG_MEDIA_CONTROLLER)
1418c2ecf20Sopenharmony_ci	/* Register the entity. */
1428c2ecf20Sopenharmony_ci	if (v4l2_dev->mdev) {
1438c2ecf20Sopenharmony_ci		err = media_device_register_entity(v4l2_dev->mdev, &sd->entity);
1448c2ecf20Sopenharmony_ci		if (err < 0)
1458c2ecf20Sopenharmony_ci			goto error_module;
1468c2ecf20Sopenharmony_ci	}
1478c2ecf20Sopenharmony_ci#endif
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	if (sd->internal_ops && sd->internal_ops->registered) {
1508c2ecf20Sopenharmony_ci		err = sd->internal_ops->registered(sd);
1518c2ecf20Sopenharmony_ci		if (err)
1528c2ecf20Sopenharmony_ci			goto error_unregister;
1538c2ecf20Sopenharmony_ci	}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	spin_lock(&v4l2_dev->lock);
1568c2ecf20Sopenharmony_ci	list_add_tail(&sd->list, &v4l2_dev->subdevs);
1578c2ecf20Sopenharmony_ci	spin_unlock(&v4l2_dev->lock);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	return 0;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_cierror_unregister:
1628c2ecf20Sopenharmony_ci#if defined(CONFIG_MEDIA_CONTROLLER)
1638c2ecf20Sopenharmony_ci	media_device_unregister_entity(&sd->entity);
1648c2ecf20Sopenharmony_ci#endif
1658c2ecf20Sopenharmony_cierror_module:
1668c2ecf20Sopenharmony_ci	if (!sd->owner_v4l2_dev)
1678c2ecf20Sopenharmony_ci		module_put(sd->owner);
1688c2ecf20Sopenharmony_ci	sd->v4l2_dev = NULL;
1698c2ecf20Sopenharmony_ci	return err;
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_device_register_subdev);
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistatic void v4l2_subdev_release(struct v4l2_subdev *sd)
1748c2ecf20Sopenharmony_ci{
1758c2ecf20Sopenharmony_ci	struct module *owner = !sd->owner_v4l2_dev ? sd->owner : NULL;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	if (sd->internal_ops && sd->internal_ops->release)
1788c2ecf20Sopenharmony_ci		sd->internal_ops->release(sd);
1798c2ecf20Sopenharmony_ci	sd->devnode = NULL;
1808c2ecf20Sopenharmony_ci	module_put(owner);
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic void v4l2_device_release_subdev_node(struct video_device *vdev)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	v4l2_subdev_release(video_get_drvdata(vdev));
1868c2ecf20Sopenharmony_ci	kfree(vdev);
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ciint __v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev,
1908c2ecf20Sopenharmony_ci					bool read_only)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	struct video_device *vdev;
1938c2ecf20Sopenharmony_ci	struct v4l2_subdev *sd;
1948c2ecf20Sopenharmony_ci	int err;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	/* Register a device node for every subdev marked with the
1978c2ecf20Sopenharmony_ci	 * V4L2_SUBDEV_FL_HAS_DEVNODE flag.
1988c2ecf20Sopenharmony_ci	 */
1998c2ecf20Sopenharmony_ci	list_for_each_entry(sd, &v4l2_dev->subdevs, list) {
2008c2ecf20Sopenharmony_ci		if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE))
2018c2ecf20Sopenharmony_ci			continue;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci		if (sd->devnode)
2048c2ecf20Sopenharmony_ci			continue;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci		vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
2078c2ecf20Sopenharmony_ci		if (!vdev) {
2088c2ecf20Sopenharmony_ci			err = -ENOMEM;
2098c2ecf20Sopenharmony_ci			goto clean_up;
2108c2ecf20Sopenharmony_ci		}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci		video_set_drvdata(vdev, sd);
2138c2ecf20Sopenharmony_ci		strscpy(vdev->name, sd->name, sizeof(vdev->name));
2148c2ecf20Sopenharmony_ci		vdev->dev_parent = sd->dev;
2158c2ecf20Sopenharmony_ci		vdev->v4l2_dev = v4l2_dev;
2168c2ecf20Sopenharmony_ci		vdev->fops = &v4l2_subdev_fops;
2178c2ecf20Sopenharmony_ci		vdev->release = v4l2_device_release_subdev_node;
2188c2ecf20Sopenharmony_ci		vdev->ctrl_handler = sd->ctrl_handler;
2198c2ecf20Sopenharmony_ci		if (read_only)
2208c2ecf20Sopenharmony_ci			set_bit(V4L2_FL_SUBDEV_RO_DEVNODE, &vdev->flags);
2218c2ecf20Sopenharmony_ci		err = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1,
2228c2ecf20Sopenharmony_ci					      sd->owner);
2238c2ecf20Sopenharmony_ci		if (err < 0) {
2248c2ecf20Sopenharmony_ci			kfree(vdev);
2258c2ecf20Sopenharmony_ci			goto clean_up;
2268c2ecf20Sopenharmony_ci		}
2278c2ecf20Sopenharmony_ci		sd->devnode = vdev;
2288c2ecf20Sopenharmony_ci#if defined(CONFIG_MEDIA_CONTROLLER)
2298c2ecf20Sopenharmony_ci		sd->entity.info.dev.major = VIDEO_MAJOR;
2308c2ecf20Sopenharmony_ci		sd->entity.info.dev.minor = vdev->minor;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci		/* Interface is created by __video_register_device() */
2338c2ecf20Sopenharmony_ci		if (vdev->v4l2_dev->mdev) {
2348c2ecf20Sopenharmony_ci			struct media_link *link;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci			link = media_create_intf_link(&sd->entity,
2378c2ecf20Sopenharmony_ci						      &vdev->intf_devnode->intf,
2388c2ecf20Sopenharmony_ci						      MEDIA_LNK_FL_ENABLED |
2398c2ecf20Sopenharmony_ci						      MEDIA_LNK_FL_IMMUTABLE);
2408c2ecf20Sopenharmony_ci			if (!link) {
2418c2ecf20Sopenharmony_ci				err = -ENOMEM;
2428c2ecf20Sopenharmony_ci				goto clean_up;
2438c2ecf20Sopenharmony_ci			}
2448c2ecf20Sopenharmony_ci		}
2458c2ecf20Sopenharmony_ci#endif
2468c2ecf20Sopenharmony_ci	}
2478c2ecf20Sopenharmony_ci	return 0;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ciclean_up:
2508c2ecf20Sopenharmony_ci	list_for_each_entry(sd, &v4l2_dev->subdevs, list) {
2518c2ecf20Sopenharmony_ci		if (!sd->devnode)
2528c2ecf20Sopenharmony_ci			break;
2538c2ecf20Sopenharmony_ci		video_unregister_device(sd->devnode);
2548c2ecf20Sopenharmony_ci	}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	return err;
2578c2ecf20Sopenharmony_ci}
2588c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__v4l2_device_register_subdev_nodes);
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_civoid v4l2_device_unregister_subdev(struct v4l2_subdev *sd)
2618c2ecf20Sopenharmony_ci{
2628c2ecf20Sopenharmony_ci	struct v4l2_device *v4l2_dev;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	/* return if it isn't registered */
2658c2ecf20Sopenharmony_ci	if (sd == NULL || sd->v4l2_dev == NULL)
2668c2ecf20Sopenharmony_ci		return;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	v4l2_dev = sd->v4l2_dev;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	spin_lock(&v4l2_dev->lock);
2718c2ecf20Sopenharmony_ci	list_del(&sd->list);
2728c2ecf20Sopenharmony_ci	spin_unlock(&v4l2_dev->lock);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	if (sd->internal_ops && sd->internal_ops->unregistered)
2758c2ecf20Sopenharmony_ci		sd->internal_ops->unregistered(sd);
2768c2ecf20Sopenharmony_ci	sd->v4l2_dev = NULL;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci#if defined(CONFIG_MEDIA_CONTROLLER)
2798c2ecf20Sopenharmony_ci	if (v4l2_dev->mdev) {
2808c2ecf20Sopenharmony_ci		/*
2818c2ecf20Sopenharmony_ci		 * No need to explicitly remove links, as both pads and
2828c2ecf20Sopenharmony_ci		 * links are removed by the function below, in the right order
2838c2ecf20Sopenharmony_ci		 */
2848c2ecf20Sopenharmony_ci		media_device_unregister_entity(&sd->entity);
2858c2ecf20Sopenharmony_ci	}
2868c2ecf20Sopenharmony_ci#endif
2878c2ecf20Sopenharmony_ci	if (sd->devnode)
2888c2ecf20Sopenharmony_ci		video_unregister_device(sd->devnode);
2898c2ecf20Sopenharmony_ci	else
2908c2ecf20Sopenharmony_ci		v4l2_subdev_release(sd);
2918c2ecf20Sopenharmony_ci}
2928c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_device_unregister_subdev);
293