1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * v4l2-spi - SPI helpers for Video4Linux2
4  */
5 
6 #include <linux/module.h>
7 #include <linux/spi/spi.h>
8 #include <media/v4l2-common.h>
9 #include <media/v4l2-device.h>
10 
v4l2_spi_subdev_unregister(struct v4l2_subdev * sd)11 void v4l2_spi_subdev_unregister(struct v4l2_subdev *sd)
12 {
13 	struct spi_device *spi = v4l2_get_subdevdata(sd);
14 
15 	if (spi && !spi->dev.of_node && !spi->dev.fwnode)
16 		spi_unregister_device(spi);
17 }
18 
v4l2_spi_subdev_init(struct v4l2_subdev * sd,struct spi_device * spi,const struct v4l2_subdev_ops * ops)19 void v4l2_spi_subdev_init(struct v4l2_subdev *sd, struct spi_device *spi,
20 			  const struct v4l2_subdev_ops *ops)
21 {
22 	v4l2_subdev_init(sd, ops);
23 	sd->flags |= V4L2_SUBDEV_FL_IS_SPI;
24 	/* the owner is the same as the spi_device's driver owner */
25 	sd->owner = spi->dev.driver->owner;
26 	sd->dev = &spi->dev;
27 	/* spi_device and v4l2_subdev point to one another */
28 	v4l2_set_subdevdata(sd, spi);
29 	spi_set_drvdata(spi, sd);
30 	/* initialize name */
31 	snprintf(sd->name, sizeof(sd->name), "%s %s",
32 		 spi->dev.driver->name, dev_name(&spi->dev));
33 }
34 EXPORT_SYMBOL_GPL(v4l2_spi_subdev_init);
35 
v4l2_spi_new_subdev(struct v4l2_device * v4l2_dev,struct spi_master * master,struct spi_board_info * info)36 struct v4l2_subdev *v4l2_spi_new_subdev(struct v4l2_device *v4l2_dev,
37 					struct spi_master *master,
38 					struct spi_board_info *info)
39 {
40 	struct v4l2_subdev *sd = NULL;
41 	struct spi_device *spi = NULL;
42 
43 	if (!v4l2_dev)
44 		return NULL;
45 	if (info->modalias[0])
46 		request_module(info->modalias);
47 
48 	spi = spi_new_device(master, info);
49 
50 	if (!spi || !spi->dev.driver)
51 		goto error;
52 
53 	if (!try_module_get(spi->dev.driver->owner))
54 		goto error;
55 
56 	sd = spi_get_drvdata(spi);
57 
58 	/*
59 	 * Register with the v4l2_device which increases the module's
60 	 * use count as well.
61 	 */
62 	if (v4l2_device_register_subdev(v4l2_dev, sd))
63 		sd = NULL;
64 
65 	/* Decrease the module use count to match the first try_module_get. */
66 	module_put(spi->dev.driver->owner);
67 
68 error:
69 	/*
70 	 * If we have a client but no subdev, then something went wrong and
71 	 * we must unregister the client.
72 	 */
73 	if (!sd)
74 		spi_unregister_device(spi);
75 
76 	return sd;
77 }
78 EXPORT_SYMBOL_GPL(v4l2_spi_new_subdev);
79