15bc3cb74SMauro Carvalho Chehab /* 25bc3cb74SMauro Carvalho Chehab V4L2 device support. 35bc3cb74SMauro Carvalho Chehab 45bc3cb74SMauro Carvalho Chehab Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl> 55bc3cb74SMauro Carvalho Chehab 65bc3cb74SMauro Carvalho Chehab This program is free software; you can redistribute it and/or modify 75bc3cb74SMauro Carvalho Chehab it under the terms of the GNU General Public License as published by 85bc3cb74SMauro Carvalho Chehab the Free Software Foundation; either version 2 of the License, or 95bc3cb74SMauro Carvalho Chehab (at your option) any later version. 105bc3cb74SMauro Carvalho Chehab 115bc3cb74SMauro Carvalho Chehab This program is distributed in the hope that it will be useful, 125bc3cb74SMauro Carvalho Chehab but WITHOUT ANY WARRANTY; without even the implied warranty of 135bc3cb74SMauro Carvalho Chehab MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 145bc3cb74SMauro Carvalho Chehab GNU General Public License for more details. 155bc3cb74SMauro Carvalho Chehab 165bc3cb74SMauro Carvalho Chehab You should have received a copy of the GNU General Public License 175bc3cb74SMauro Carvalho Chehab along with this program; if not, write to the Free Software 185bc3cb74SMauro Carvalho Chehab Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 195bc3cb74SMauro Carvalho Chehab */ 205bc3cb74SMauro Carvalho Chehab 215bc3cb74SMauro Carvalho Chehab #include <linux/types.h> 225bc3cb74SMauro Carvalho Chehab #include <linux/ioctl.h> 235bc3cb74SMauro Carvalho Chehab #include <linux/module.h> 245bc3cb74SMauro Carvalho Chehab #include <linux/i2c.h> 255bc3cb74SMauro Carvalho Chehab #include <linux/slab.h> 265bc3cb74SMauro Carvalho Chehab #if defined(CONFIG_SPI) 275bc3cb74SMauro Carvalho Chehab #include <linux/spi/spi.h> 285bc3cb74SMauro Carvalho Chehab #endif 295bc3cb74SMauro Carvalho Chehab #include <linux/videodev2.h> 305bc3cb74SMauro Carvalho Chehab #include <media/v4l2-device.h> 315bc3cb74SMauro Carvalho Chehab #include <media/v4l2-ctrls.h> 325bc3cb74SMauro Carvalho Chehab 335bc3cb74SMauro Carvalho Chehab int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev) 345bc3cb74SMauro Carvalho Chehab { 355bc3cb74SMauro Carvalho Chehab if (v4l2_dev == NULL) 365bc3cb74SMauro Carvalho Chehab return -EINVAL; 375bc3cb74SMauro Carvalho Chehab 385bc3cb74SMauro Carvalho Chehab INIT_LIST_HEAD(&v4l2_dev->subdevs); 395bc3cb74SMauro Carvalho Chehab spin_lock_init(&v4l2_dev->lock); 405bc3cb74SMauro Carvalho Chehab v4l2_prio_init(&v4l2_dev->prio); 415bc3cb74SMauro Carvalho Chehab kref_init(&v4l2_dev->ref); 425bc3cb74SMauro Carvalho Chehab get_device(dev); 435bc3cb74SMauro Carvalho Chehab v4l2_dev->dev = dev; 445bc3cb74SMauro Carvalho Chehab if (dev == NULL) { 455bc3cb74SMauro Carvalho Chehab /* If dev == NULL, then name must be filled in by the caller */ 469e882e3bSHans Verkuil if (WARN_ON(!v4l2_dev->name[0])) 479e882e3bSHans Verkuil return -EINVAL; 485bc3cb74SMauro Carvalho Chehab return 0; 495bc3cb74SMauro Carvalho Chehab } 505bc3cb74SMauro Carvalho Chehab 515bc3cb74SMauro Carvalho Chehab /* Set name to driver name + device name if it is empty. */ 525bc3cb74SMauro Carvalho Chehab if (!v4l2_dev->name[0]) 535bc3cb74SMauro Carvalho Chehab snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s %s", 545bc3cb74SMauro Carvalho Chehab dev->driver->name, dev_name(dev)); 555bc3cb74SMauro Carvalho Chehab if (!dev_get_drvdata(dev)) 565bc3cb74SMauro Carvalho Chehab dev_set_drvdata(dev, v4l2_dev); 575bc3cb74SMauro Carvalho Chehab return 0; 585bc3cb74SMauro Carvalho Chehab } 595bc3cb74SMauro Carvalho Chehab EXPORT_SYMBOL_GPL(v4l2_device_register); 605bc3cb74SMauro Carvalho Chehab 615bc3cb74SMauro Carvalho Chehab static void v4l2_device_release(struct kref *ref) 625bc3cb74SMauro Carvalho Chehab { 635bc3cb74SMauro Carvalho Chehab struct v4l2_device *v4l2_dev = 645bc3cb74SMauro Carvalho Chehab container_of(ref, struct v4l2_device, ref); 655bc3cb74SMauro Carvalho Chehab 665bc3cb74SMauro Carvalho Chehab if (v4l2_dev->release) 675bc3cb74SMauro Carvalho Chehab v4l2_dev->release(v4l2_dev); 685bc3cb74SMauro Carvalho Chehab } 695bc3cb74SMauro Carvalho Chehab 705bc3cb74SMauro Carvalho Chehab int v4l2_device_put(struct v4l2_device *v4l2_dev) 715bc3cb74SMauro Carvalho Chehab { 725bc3cb74SMauro Carvalho Chehab return kref_put(&v4l2_dev->ref, v4l2_device_release); 735bc3cb74SMauro Carvalho Chehab } 745bc3cb74SMauro Carvalho Chehab EXPORT_SYMBOL_GPL(v4l2_device_put); 755bc3cb74SMauro Carvalho Chehab 765bc3cb74SMauro Carvalho Chehab int v4l2_device_set_name(struct v4l2_device *v4l2_dev, const char *basename, 775bc3cb74SMauro Carvalho Chehab atomic_t *instance) 785bc3cb74SMauro Carvalho Chehab { 795bc3cb74SMauro Carvalho Chehab int num = atomic_inc_return(instance) - 1; 805bc3cb74SMauro Carvalho Chehab int len = strlen(basename); 815bc3cb74SMauro Carvalho Chehab 825bc3cb74SMauro Carvalho Chehab if (basename[len - 1] >= '0' && basename[len - 1] <= '9') 835bc3cb74SMauro Carvalho Chehab snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), 845bc3cb74SMauro Carvalho Chehab "%s-%d", basename, num); 855bc3cb74SMauro Carvalho Chehab else 865bc3cb74SMauro Carvalho Chehab snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), 875bc3cb74SMauro Carvalho Chehab "%s%d", basename, num); 885bc3cb74SMauro Carvalho Chehab return num; 895bc3cb74SMauro Carvalho Chehab } 905bc3cb74SMauro Carvalho Chehab EXPORT_SYMBOL_GPL(v4l2_device_set_name); 915bc3cb74SMauro Carvalho Chehab 925bc3cb74SMauro Carvalho Chehab void v4l2_device_disconnect(struct v4l2_device *v4l2_dev) 935bc3cb74SMauro Carvalho Chehab { 945bc3cb74SMauro Carvalho Chehab if (v4l2_dev->dev == NULL) 955bc3cb74SMauro Carvalho Chehab return; 965bc3cb74SMauro Carvalho Chehab 975bc3cb74SMauro Carvalho Chehab if (dev_get_drvdata(v4l2_dev->dev) == v4l2_dev) 985bc3cb74SMauro Carvalho Chehab dev_set_drvdata(v4l2_dev->dev, NULL); 995bc3cb74SMauro Carvalho Chehab put_device(v4l2_dev->dev); 1005bc3cb74SMauro Carvalho Chehab v4l2_dev->dev = NULL; 1015bc3cb74SMauro Carvalho Chehab } 1025bc3cb74SMauro Carvalho Chehab EXPORT_SYMBOL_GPL(v4l2_device_disconnect); 1035bc3cb74SMauro Carvalho Chehab 1045bc3cb74SMauro Carvalho Chehab void v4l2_device_unregister(struct v4l2_device *v4l2_dev) 1055bc3cb74SMauro Carvalho Chehab { 1065bc3cb74SMauro Carvalho Chehab struct v4l2_subdev *sd, *next; 1075bc3cb74SMauro Carvalho Chehab 1089e882e3bSHans Verkuil /* Just return if v4l2_dev is NULL or if it was already 1099e882e3bSHans Verkuil * unregistered before. */ 1109e882e3bSHans Verkuil if (v4l2_dev == NULL || !v4l2_dev->name[0]) 1115bc3cb74SMauro Carvalho Chehab return; 1125bc3cb74SMauro Carvalho Chehab v4l2_device_disconnect(v4l2_dev); 1135bc3cb74SMauro Carvalho Chehab 1145bc3cb74SMauro Carvalho Chehab /* Unregister subdevs */ 1155bc3cb74SMauro Carvalho Chehab list_for_each_entry_safe(sd, next, &v4l2_dev->subdevs, list) { 1165bc3cb74SMauro Carvalho Chehab v4l2_device_unregister_subdev(sd); 1177b34be71SPeter Senna Tschudin #if IS_ENABLED(CONFIG_I2C) 1185bc3cb74SMauro Carvalho Chehab if (sd->flags & V4L2_SUBDEV_FL_IS_I2C) { 1195bc3cb74SMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(sd); 1205bc3cb74SMauro Carvalho Chehab 1210d51ebd3STommi Franttila /* 1220d51ebd3STommi Franttila * We need to unregister the i2c client 1230d51ebd3STommi Franttila * explicitly. We cannot rely on 1240d51ebd3STommi Franttila * i2c_del_adapter to always unregister 1250d51ebd3STommi Franttila * clients for us, since if the i2c bus is a 1260d51ebd3STommi Franttila * platform bus, then it is never deleted. 1270d51ebd3STommi Franttila * 1280d51ebd3STommi Franttila * Device tree or ACPI based devices must not 1290d51ebd3STommi Franttila * be unregistered as they have not been 1300d51ebd3STommi Franttila * registered by us, and would not be 1310d51ebd3STommi Franttila * re-created by just probing the V4L2 driver. 1320d51ebd3STommi Franttila */ 1330d51ebd3STommi Franttila if (client && 1340d51ebd3STommi Franttila !client->dev.of_node && !client->dev.fwnode) 1355bc3cb74SMauro Carvalho Chehab i2c_unregister_device(client); 1365bc3cb74SMauro Carvalho Chehab continue; 1375bc3cb74SMauro Carvalho Chehab } 1385bc3cb74SMauro Carvalho Chehab #endif 1395bc3cb74SMauro Carvalho Chehab #if defined(CONFIG_SPI) 1405bc3cb74SMauro Carvalho Chehab if (sd->flags & V4L2_SUBDEV_FL_IS_SPI) { 1415bc3cb74SMauro Carvalho Chehab struct spi_device *spi = v4l2_get_subdevdata(sd); 1425bc3cb74SMauro Carvalho Chehab 1430d51ebd3STommi Franttila if (spi && !spi->dev.of_node && !spi->dev.fwnode) 1445bc3cb74SMauro Carvalho Chehab spi_unregister_device(spi); 1455bc3cb74SMauro Carvalho Chehab continue; 1465bc3cb74SMauro Carvalho Chehab } 1475bc3cb74SMauro Carvalho Chehab #endif 1485bc3cb74SMauro Carvalho Chehab } 1499e882e3bSHans Verkuil /* Mark as unregistered, thus preventing duplicate unregistrations */ 1509e882e3bSHans Verkuil v4l2_dev->name[0] = '\0'; 1515bc3cb74SMauro Carvalho Chehab } 1525bc3cb74SMauro Carvalho Chehab EXPORT_SYMBOL_GPL(v4l2_device_unregister); 1535bc3cb74SMauro Carvalho Chehab 1545bc3cb74SMauro Carvalho Chehab int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, 1555bc3cb74SMauro Carvalho Chehab struct v4l2_subdev *sd) 1565bc3cb74SMauro Carvalho Chehab { 1575bc3cb74SMauro Carvalho Chehab #if defined(CONFIG_MEDIA_CONTROLLER) 1585bc3cb74SMauro Carvalho Chehab struct media_entity *entity = &sd->entity; 1595bc3cb74SMauro Carvalho Chehab #endif 1605bc3cb74SMauro Carvalho Chehab int err; 1615bc3cb74SMauro Carvalho Chehab 1625bc3cb74SMauro Carvalho Chehab /* Check for valid input */ 1635bc3cb74SMauro Carvalho Chehab if (v4l2_dev == NULL || sd == NULL || !sd->name[0]) 1645bc3cb74SMauro Carvalho Chehab return -EINVAL; 1655bc3cb74SMauro Carvalho Chehab 1665bc3cb74SMauro Carvalho Chehab /* Warn if we apparently re-register a subdev */ 1675bc3cb74SMauro Carvalho Chehab WARN_ON(sd->v4l2_dev != NULL); 1685bc3cb74SMauro Carvalho Chehab 169b2a06aecSSakari Ailus /* 170b2a06aecSSakari Ailus * The reason to acquire the module here is to avoid unloading 171b2a06aecSSakari Ailus * a module of sub-device which is registered to a media 172b2a06aecSSakari Ailus * device. To make it possible to unload modules for media 173b2a06aecSSakari Ailus * devices that also register sub-devices, do not 174b2a06aecSSakari Ailus * try_module_get() such sub-device owners. 175b2a06aecSSakari Ailus */ 176b2a06aecSSakari Ailus sd->owner_v4l2_dev = v4l2_dev->dev && v4l2_dev->dev->driver && 177b2a06aecSSakari Ailus sd->owner == v4l2_dev->dev->driver->owner; 178b2a06aecSSakari Ailus 179b2a06aecSSakari Ailus if (!sd->owner_v4l2_dev && !try_module_get(sd->owner)) 1805bc3cb74SMauro Carvalho Chehab return -ENODEV; 1815bc3cb74SMauro Carvalho Chehab 1825bc3cb74SMauro Carvalho Chehab sd->v4l2_dev = v4l2_dev; 1835bc3cb74SMauro Carvalho Chehab if (sd->internal_ops && sd->internal_ops->registered) { 1845bc3cb74SMauro Carvalho Chehab err = sd->internal_ops->registered(sd); 185317efce9SLaurent Pinchart if (err) 186317efce9SLaurent Pinchart goto error_module; 1875bc3cb74SMauro Carvalho Chehab } 1885bc3cb74SMauro Carvalho Chehab 1895bc3cb74SMauro Carvalho Chehab /* This just returns 0 if either of the two args is NULL */ 19034a6b7d0SHans Verkuil err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler, NULL); 191317efce9SLaurent Pinchart if (err) 192317efce9SLaurent Pinchart goto error_unregister; 1935bc3cb74SMauro Carvalho Chehab 1945bc3cb74SMauro Carvalho Chehab #if defined(CONFIG_MEDIA_CONTROLLER) 1955bc3cb74SMauro Carvalho Chehab /* Register the entity. */ 1965bc3cb74SMauro Carvalho Chehab if (v4l2_dev->mdev) { 1975bc3cb74SMauro Carvalho Chehab err = media_device_register_entity(v4l2_dev->mdev, entity); 198317efce9SLaurent Pinchart if (err < 0) 199317efce9SLaurent Pinchart goto error_unregister; 2005bc3cb74SMauro Carvalho Chehab } 2015bc3cb74SMauro Carvalho Chehab #endif 2025bc3cb74SMauro Carvalho Chehab 2035bc3cb74SMauro Carvalho Chehab spin_lock(&v4l2_dev->lock); 2045bc3cb74SMauro Carvalho Chehab list_add_tail(&sd->list, &v4l2_dev->subdevs); 2055bc3cb74SMauro Carvalho Chehab spin_unlock(&v4l2_dev->lock); 2065bc3cb74SMauro Carvalho Chehab 2075bc3cb74SMauro Carvalho Chehab return 0; 208317efce9SLaurent Pinchart 209317efce9SLaurent Pinchart error_unregister: 210317efce9SLaurent Pinchart if (sd->internal_ops && sd->internal_ops->unregistered) 211317efce9SLaurent Pinchart sd->internal_ops->unregistered(sd); 212317efce9SLaurent Pinchart error_module: 213b2a06aecSSakari Ailus if (!sd->owner_v4l2_dev) 214317efce9SLaurent Pinchart module_put(sd->owner); 215317efce9SLaurent Pinchart sd->v4l2_dev = NULL; 216317efce9SLaurent Pinchart return err; 2175bc3cb74SMauro Carvalho Chehab } 2185bc3cb74SMauro Carvalho Chehab EXPORT_SYMBOL_GPL(v4l2_device_register_subdev); 2195bc3cb74SMauro Carvalho Chehab 2205bc3cb74SMauro Carvalho Chehab static void v4l2_device_release_subdev_node(struct video_device *vdev) 2215bc3cb74SMauro Carvalho Chehab { 2225bc3cb74SMauro Carvalho Chehab struct v4l2_subdev *sd = video_get_drvdata(vdev); 2235bc3cb74SMauro Carvalho Chehab sd->devnode = NULL; 2245bc3cb74SMauro Carvalho Chehab kfree(vdev); 2255bc3cb74SMauro Carvalho Chehab } 2265bc3cb74SMauro Carvalho Chehab 2275bc3cb74SMauro Carvalho Chehab int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev) 2285bc3cb74SMauro Carvalho Chehab { 2295bc3cb74SMauro Carvalho Chehab struct video_device *vdev; 2305bc3cb74SMauro Carvalho Chehab struct v4l2_subdev *sd; 2315bc3cb74SMauro Carvalho Chehab int err; 2325bc3cb74SMauro Carvalho Chehab 2335bc3cb74SMauro Carvalho Chehab /* Register a device node for every subdev marked with the 2345bc3cb74SMauro Carvalho Chehab * V4L2_SUBDEV_FL_HAS_DEVNODE flag. 2355bc3cb74SMauro Carvalho Chehab */ 2365bc3cb74SMauro Carvalho Chehab list_for_each_entry(sd, &v4l2_dev->subdevs, list) { 2375bc3cb74SMauro Carvalho Chehab if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE)) 2385bc3cb74SMauro Carvalho Chehab continue; 2395bc3cb74SMauro Carvalho Chehab 2405bc3cb74SMauro Carvalho Chehab vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); 2415bc3cb74SMauro Carvalho Chehab if (!vdev) { 2425bc3cb74SMauro Carvalho Chehab err = -ENOMEM; 2435bc3cb74SMauro Carvalho Chehab goto clean_up; 2445bc3cb74SMauro Carvalho Chehab } 2455bc3cb74SMauro Carvalho Chehab 2465bc3cb74SMauro Carvalho Chehab video_set_drvdata(vdev, sd); 2475bc3cb74SMauro Carvalho Chehab strlcpy(vdev->name, sd->name, sizeof(vdev->name)); 2485bc3cb74SMauro Carvalho Chehab vdev->v4l2_dev = v4l2_dev; 2495bc3cb74SMauro Carvalho Chehab vdev->fops = &v4l2_subdev_fops; 2505bc3cb74SMauro Carvalho Chehab vdev->release = v4l2_device_release_subdev_node; 2515bc3cb74SMauro Carvalho Chehab vdev->ctrl_handler = sd->ctrl_handler; 2525bc3cb74SMauro Carvalho Chehab err = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1, 2535bc3cb74SMauro Carvalho Chehab sd->owner); 2545bc3cb74SMauro Carvalho Chehab if (err < 0) { 2555bc3cb74SMauro Carvalho Chehab kfree(vdev); 2565bc3cb74SMauro Carvalho Chehab goto clean_up; 2575bc3cb74SMauro Carvalho Chehab } 2585bc3cb74SMauro Carvalho Chehab #if defined(CONFIG_MEDIA_CONTROLLER) 259e31a0ba7SMauro Carvalho Chehab sd->entity.info.dev.major = VIDEO_MAJOR; 260e31a0ba7SMauro Carvalho Chehab sd->entity.info.dev.minor = vdev->minor; 261d9c21e3eSMauro Carvalho Chehab 262d9c21e3eSMauro Carvalho Chehab /* Interface is created by __video_register_device() */ 263d9c21e3eSMauro Carvalho Chehab if (vdev->v4l2_dev->mdev) { 264d9c21e3eSMauro Carvalho Chehab struct media_link *link; 265d9c21e3eSMauro Carvalho Chehab 266d9c21e3eSMauro Carvalho Chehab link = media_create_intf_link(&sd->entity, 267d9c21e3eSMauro Carvalho Chehab &vdev->intf_devnode->intf, 268d9c21e3eSMauro Carvalho Chehab 0); 269d9c21e3eSMauro Carvalho Chehab if (!link) 270d9c21e3eSMauro Carvalho Chehab goto clean_up; 271d9c21e3eSMauro Carvalho Chehab } 2725bc3cb74SMauro Carvalho Chehab #endif 2735bc3cb74SMauro Carvalho Chehab sd->devnode = vdev; 2745bc3cb74SMauro Carvalho Chehab } 2755bc3cb74SMauro Carvalho Chehab return 0; 2765bc3cb74SMauro Carvalho Chehab 2775bc3cb74SMauro Carvalho Chehab clean_up: 2785bc3cb74SMauro Carvalho Chehab list_for_each_entry(sd, &v4l2_dev->subdevs, list) { 2795bc3cb74SMauro Carvalho Chehab if (!sd->devnode) 2805bc3cb74SMauro Carvalho Chehab break; 2815bc3cb74SMauro Carvalho Chehab video_unregister_device(sd->devnode); 2825bc3cb74SMauro Carvalho Chehab } 2835bc3cb74SMauro Carvalho Chehab 2845bc3cb74SMauro Carvalho Chehab return err; 2855bc3cb74SMauro Carvalho Chehab } 2865bc3cb74SMauro Carvalho Chehab EXPORT_SYMBOL_GPL(v4l2_device_register_subdev_nodes); 2875bc3cb74SMauro Carvalho Chehab 2885bc3cb74SMauro Carvalho Chehab void v4l2_device_unregister_subdev(struct v4l2_subdev *sd) 2895bc3cb74SMauro Carvalho Chehab { 2905bc3cb74SMauro Carvalho Chehab struct v4l2_device *v4l2_dev; 2915bc3cb74SMauro Carvalho Chehab 2925bc3cb74SMauro Carvalho Chehab /* return if it isn't registered */ 2935bc3cb74SMauro Carvalho Chehab if (sd == NULL || sd->v4l2_dev == NULL) 2945bc3cb74SMauro Carvalho Chehab return; 2955bc3cb74SMauro Carvalho Chehab 2965bc3cb74SMauro Carvalho Chehab v4l2_dev = sd->v4l2_dev; 2975bc3cb74SMauro Carvalho Chehab 2985bc3cb74SMauro Carvalho Chehab spin_lock(&v4l2_dev->lock); 2995bc3cb74SMauro Carvalho Chehab list_del(&sd->list); 3005bc3cb74SMauro Carvalho Chehab spin_unlock(&v4l2_dev->lock); 3015bc3cb74SMauro Carvalho Chehab 3025bc3cb74SMauro Carvalho Chehab if (sd->internal_ops && sd->internal_ops->unregistered) 3035bc3cb74SMauro Carvalho Chehab sd->internal_ops->unregistered(sd); 3045bc3cb74SMauro Carvalho Chehab sd->v4l2_dev = NULL; 3055bc3cb74SMauro Carvalho Chehab 3065bc3cb74SMauro Carvalho Chehab #if defined(CONFIG_MEDIA_CONTROLLER) 307c2efd3e6SSylwester Nawrocki if (v4l2_dev->mdev) { 308d9c21e3eSMauro Carvalho Chehab /* 309d9c21e3eSMauro Carvalho Chehab * No need to explicitly remove links, as both pads and 310d9c21e3eSMauro Carvalho Chehab * links are removed by the function below, in the right order 311d9c21e3eSMauro Carvalho Chehab */ 3125bc3cb74SMauro Carvalho Chehab media_device_unregister_entity(&sd->entity); 313c2efd3e6SSylwester Nawrocki } 3145bc3cb74SMauro Carvalho Chehab #endif 3155bc3cb74SMauro Carvalho Chehab video_unregister_device(sd->devnode); 316b2a06aecSSakari Ailus if (!sd->owner_v4l2_dev) 3175bc3cb74SMauro Carvalho Chehab module_put(sd->owner); 3185bc3cb74SMauro Carvalho Chehab } 3195bc3cb74SMauro Carvalho Chehab EXPORT_SYMBOL_GPL(v4l2_device_unregister_subdev); 320