diff options
Diffstat (limited to 'drivers/media/video/v4l2-device.c')
-rw-r--r-- | drivers/media/video/v4l2-device.c | 253 |
1 files changed, 253 insertions, 0 deletions
diff --git a/drivers/media/video/v4l2-device.c b/drivers/media/video/v4l2-device.c new file mode 100644 index 00000000000..e6a2c3b302d --- /dev/null +++ b/drivers/media/video/v4l2-device.c | |||
@@ -0,0 +1,253 @@ | |||
1 | /* | ||
2 | V4L2 device support. | ||
3 | |||
4 | Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl> | ||
5 | |||
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 | */ | ||
20 | |||
21 | #include <linux/types.h> | ||
22 | #include <linux/ioctl.h> | ||
23 | #include <linux/i2c.h> | ||
24 | #if defined(CONFIG_SPI) | ||
25 | #include <linux/spi/spi.h> | ||
26 | #endif | ||
27 | #include <linux/videodev2.h> | ||
28 | #include <media/v4l2-device.h> | ||
29 | #include <media/v4l2-ctrls.h> | ||
30 | |||
31 | int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev) | ||
32 | { | ||
33 | if (v4l2_dev == NULL) | ||
34 | return -EINVAL; | ||
35 | |||
36 | INIT_LIST_HEAD(&v4l2_dev->subdevs); | ||
37 | spin_lock_init(&v4l2_dev->lock); | ||
38 | mutex_init(&v4l2_dev->ioctl_lock); | ||
39 | v4l2_prio_init(&v4l2_dev->prio); | ||
40 | kref_init(&v4l2_dev->ref); | ||
41 | get_device(dev); | ||
42 | v4l2_dev->dev = dev; | ||
43 | if (dev == NULL) { | ||
44 | /* If dev == NULL, then name must be filled in by the caller */ | ||
45 | WARN_ON(!v4l2_dev->name[0]); | ||
46 | return 0; | ||
47 | } | ||
48 | |||
49 | /* Set name to driver name + device name if it is empty. */ | ||
50 | if (!v4l2_dev->name[0]) | ||
51 | snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s %s", | ||
52 | dev->driver->name, dev_name(dev)); | ||
53 | if (!dev_get_drvdata(dev)) | ||
54 | dev_set_drvdata(dev, v4l2_dev); | ||
55 | return 0; | ||
56 | } | ||
57 | EXPORT_SYMBOL_GPL(v4l2_device_register); | ||
58 | |||
59 | static void v4l2_device_release(struct kref *ref) | ||
60 | { | ||
61 | struct v4l2_device *v4l2_dev = | ||
62 | container_of(ref, struct v4l2_device, ref); | ||
63 | |||
64 | if (v4l2_dev->release) | ||
65 | v4l2_dev->release(v4l2_dev); | ||
66 | } | ||
67 | |||
68 | int v4l2_device_put(struct v4l2_device *v4l2_dev) | ||
69 | { | ||
70 | return kref_put(&v4l2_dev->ref, v4l2_device_release); | ||
71 | } | ||
72 | EXPORT_SYMBOL_GPL(v4l2_device_put); | ||
73 | |||
74 | int v4l2_device_set_name(struct v4l2_device *v4l2_dev, const char *basename, | ||
75 | atomic_t *instance) | ||
76 | { | ||
77 | int num = atomic_inc_return(instance) - 1; | ||
78 | int len = strlen(basename); | ||
79 | |||
80 | if (basename[len - 1] >= '0' && basename[len - 1] <= '9') | ||
81 | snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), | ||
82 | "%s-%d", basename, num); | ||
83 | else | ||
84 | snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), | ||
85 | "%s%d", basename, num); | ||
86 | return num; | ||
87 | } | ||
88 | EXPORT_SYMBOL_GPL(v4l2_device_set_name); | ||
89 | |||
90 | void v4l2_device_disconnect(struct v4l2_device *v4l2_dev) | ||
91 | { | ||
92 | if (v4l2_dev->dev == NULL) | ||
93 | return; | ||
94 | |||
95 | if (dev_get_drvdata(v4l2_dev->dev) == v4l2_dev) | ||
96 | dev_set_drvdata(v4l2_dev->dev, NULL); | ||
97 | put_device(v4l2_dev->dev); | ||
98 | v4l2_dev->dev = NULL; | ||
99 | } | ||
100 | EXPORT_SYMBOL_GPL(v4l2_device_disconnect); | ||
101 | |||
102 | void v4l2_device_unregister(struct v4l2_device *v4l2_dev) | ||
103 | { | ||
104 | struct v4l2_subdev *sd, *next; | ||
105 | |||
106 | if (v4l2_dev == NULL) | ||
107 | return; | ||
108 | v4l2_device_disconnect(v4l2_dev); | ||
109 | |||
110 | /* Unregister subdevs */ | ||
111 | list_for_each_entry_safe(sd, next, &v4l2_dev->subdevs, list) { | ||
112 | v4l2_device_unregister_subdev(sd); | ||
113 | #if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) | ||
114 | if (sd->flags & V4L2_SUBDEV_FL_IS_I2C) { | ||
115 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
116 | |||
117 | /* We need to unregister the i2c client explicitly. | ||
118 | We cannot rely on i2c_del_adapter to always | ||
119 | unregister clients for us, since if the i2c bus | ||
120 | is a platform bus, then it is never deleted. */ | ||
121 | if (client) | ||
122 | i2c_unregister_device(client); | ||
123 | continue; | ||
124 | } | ||
125 | #endif | ||
126 | #if defined(CONFIG_SPI) | ||
127 | if (sd->flags & V4L2_SUBDEV_FL_IS_SPI) { | ||
128 | struct spi_device *spi = v4l2_get_subdevdata(sd); | ||
129 | |||
130 | if (spi) | ||
131 | spi_unregister_device(spi); | ||
132 | continue; | ||
133 | } | ||
134 | #endif | ||
135 | } | ||
136 | } | ||
137 | EXPORT_SYMBOL_GPL(v4l2_device_unregister); | ||
138 | |||
139 | int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, | ||
140 | struct v4l2_subdev *sd) | ||
141 | { | ||
142 | #if defined(CONFIG_MEDIA_CONTROLLER) | ||
143 | struct media_entity *entity = &sd->entity; | ||
144 | #endif | ||
145 | int err; | ||
146 | |||
147 | /* Check for valid input */ | ||
148 | if (v4l2_dev == NULL || sd == NULL || !sd->name[0]) | ||
149 | return -EINVAL; | ||
150 | |||
151 | /* Warn if we apparently re-register a subdev */ | ||
152 | WARN_ON(sd->v4l2_dev != NULL); | ||
153 | |||
154 | if (!try_module_get(sd->owner)) | ||
155 | return -ENODEV; | ||
156 | |||
157 | sd->v4l2_dev = v4l2_dev; | ||
158 | if (sd->internal_ops && sd->internal_ops->registered) { | ||
159 | err = sd->internal_ops->registered(sd); | ||
160 | if (err) { | ||
161 | module_put(sd->owner); | ||
162 | return err; | ||
163 | } | ||
164 | } | ||
165 | |||
166 | /* This just returns 0 if either of the two args is NULL */ | ||
167 | err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler); | ||
168 | if (err) { | ||
169 | if (sd->internal_ops && sd->internal_ops->unregistered) | ||
170 | sd->internal_ops->unregistered(sd); | ||
171 | module_put(sd->owner); | ||
172 | return err; | ||
173 | } | ||
174 | |||
175 | #if defined(CONFIG_MEDIA_CONTROLLER) | ||
176 | /* Register the entity. */ | ||
177 | if (v4l2_dev->mdev) { | ||
178 | err = media_device_register_entity(v4l2_dev->mdev, entity); | ||
179 | if (err < 0) { | ||
180 | if (sd->internal_ops && sd->internal_ops->unregistered) | ||
181 | sd->internal_ops->unregistered(sd); | ||
182 | module_put(sd->owner); | ||
183 | return err; | ||
184 | } | ||
185 | } | ||
186 | #endif | ||
187 | |||
188 | spin_lock(&v4l2_dev->lock); | ||
189 | list_add_tail(&sd->list, &v4l2_dev->subdevs); | ||
190 | spin_unlock(&v4l2_dev->lock); | ||
191 | |||
192 | return 0; | ||
193 | } | ||
194 | EXPORT_SYMBOL_GPL(v4l2_device_register_subdev); | ||
195 | |||
196 | int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev) | ||
197 | { | ||
198 | struct video_device *vdev; | ||
199 | struct v4l2_subdev *sd; | ||
200 | int err; | ||
201 | |||
202 | /* Register a device node for every subdev marked with the | ||
203 | * V4L2_SUBDEV_FL_HAS_DEVNODE flag. | ||
204 | */ | ||
205 | list_for_each_entry(sd, &v4l2_dev->subdevs, list) { | ||
206 | if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE)) | ||
207 | continue; | ||
208 | |||
209 | vdev = &sd->devnode; | ||
210 | strlcpy(vdev->name, sd->name, sizeof(vdev->name)); | ||
211 | vdev->v4l2_dev = v4l2_dev; | ||
212 | vdev->fops = &v4l2_subdev_fops; | ||
213 | vdev->release = video_device_release_empty; | ||
214 | vdev->ctrl_handler = sd->ctrl_handler; | ||
215 | err = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1, | ||
216 | sd->owner); | ||
217 | if (err < 0) | ||
218 | return err; | ||
219 | #if defined(CONFIG_MEDIA_CONTROLLER) | ||
220 | sd->entity.v4l.major = VIDEO_MAJOR; | ||
221 | sd->entity.v4l.minor = vdev->minor; | ||
222 | #endif | ||
223 | } | ||
224 | return 0; | ||
225 | } | ||
226 | EXPORT_SYMBOL_GPL(v4l2_device_register_subdev_nodes); | ||
227 | |||
228 | void v4l2_device_unregister_subdev(struct v4l2_subdev *sd) | ||
229 | { | ||
230 | struct v4l2_device *v4l2_dev; | ||
231 | |||
232 | /* return if it isn't registered */ | ||
233 | if (sd == NULL || sd->v4l2_dev == NULL) | ||
234 | return; | ||
235 | |||
236 | v4l2_dev = sd->v4l2_dev; | ||
237 | |||
238 | spin_lock(&v4l2_dev->lock); | ||
239 | list_del(&sd->list); | ||
240 | spin_unlock(&v4l2_dev->lock); | ||
241 | |||
242 | if (sd->internal_ops && sd->internal_ops->unregistered) | ||
243 | sd->internal_ops->unregistered(sd); | ||
244 | sd->v4l2_dev = NULL; | ||
245 | |||
246 | #if defined(CONFIG_MEDIA_CONTROLLER) | ||
247 | if (v4l2_dev->mdev) | ||
248 | media_device_unregister_entity(&sd->entity); | ||
249 | #endif | ||
250 | video_unregister_device(&sd->devnode); | ||
251 | module_put(sd->owner); | ||
252 | } | ||
253 | EXPORT_SYMBOL_GPL(v4l2_device_unregister_subdev); | ||