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