]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/media/media-device.c
ARM: dts: tx6: add enet_out clock for FEC
[karo-tx-linux.git] / drivers / media / media-device.c
1 /*
2  * Media device
3  *
4  * Copyright (C) 2010 Nokia Corporation
5  *
6  * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
7  *           Sakari Ailus <sakari.ailus@iki.fi>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License version 2 as
11  * published by the Free Software Foundation.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 #include <linux/compat.h>
24 #include <linux/export.h>
25 #include <linux/ioctl.h>
26 #include <linux/media.h>
27 #include <linux/types.h>
28
29 #include <media/media-device.h>
30 #include <media/media-devnode.h>
31 #include <media/media-entity.h>
32
33 /* -----------------------------------------------------------------------------
34  * Userspace API
35  */
36
37 static int media_device_open(struct file *filp)
38 {
39         return 0;
40 }
41
42 static int media_device_close(struct file *filp)
43 {
44         return 0;
45 }
46
47 static int media_device_get_info(struct media_device *dev,
48                                  struct media_device_info __user *__info)
49 {
50         struct media_device_info info;
51
52         memset(&info, 0, sizeof(info));
53
54         strlcpy(info.driver, dev->dev->driver->name, sizeof(info.driver));
55         strlcpy(info.model, dev->model, sizeof(info.model));
56         strlcpy(info.serial, dev->serial, sizeof(info.serial));
57         strlcpy(info.bus_info, dev->bus_info, sizeof(info.bus_info));
58
59         info.media_version = MEDIA_API_VERSION;
60         info.hw_revision = dev->hw_revision;
61         info.driver_version = dev->driver_version;
62
63         if (copy_to_user(__info, &info, sizeof(*__info)))
64                 return -EFAULT;
65         return 0;
66 }
67
68 static struct media_entity *find_entity(struct media_device *mdev, u32 id)
69 {
70         struct media_entity *entity;
71         int next = id & MEDIA_ENT_ID_FLAG_NEXT;
72
73         id &= ~MEDIA_ENT_ID_FLAG_NEXT;
74
75         spin_lock(&mdev->lock);
76
77         media_device_for_each_entity(entity, mdev) {
78                 if ((entity->id == id && !next) ||
79                     (entity->id > id && next)) {
80                         spin_unlock(&mdev->lock);
81                         return entity;
82                 }
83         }
84
85         spin_unlock(&mdev->lock);
86
87         return NULL;
88 }
89
90 static long media_device_enum_entities(struct media_device *mdev,
91                                        struct media_entity_desc __user *uent)
92 {
93         struct media_entity *ent;
94         struct media_entity_desc u_ent;
95
96         memset(&u_ent, 0, sizeof(u_ent));
97         if (copy_from_user(&u_ent.id, &uent->id, sizeof(u_ent.id)))
98                 return -EFAULT;
99
100         ent = find_entity(mdev, u_ent.id);
101
102         if (ent == NULL)
103                 return -EINVAL;
104
105         u_ent.id = ent->id;
106         if (ent->name) {
107                 strncpy(u_ent.name, ent->name, sizeof(u_ent.name));
108                 u_ent.name[sizeof(u_ent.name) - 1] = '\0';
109         } else {
110                 memset(u_ent.name, 0, sizeof(u_ent.name));
111         }
112         u_ent.type = ent->type;
113         u_ent.revision = ent->revision;
114         u_ent.flags = ent->flags;
115         u_ent.group_id = ent->group_id;
116         u_ent.pads = ent->num_pads;
117         u_ent.links = ent->num_links - ent->num_backlinks;
118         memcpy(&u_ent.raw, &ent->info, sizeof(ent->info));
119         if (copy_to_user(uent, &u_ent, sizeof(u_ent)))
120                 return -EFAULT;
121         return 0;
122 }
123
124 static void media_device_kpad_to_upad(const struct media_pad *kpad,
125                                       struct media_pad_desc *upad)
126 {
127         upad->entity = kpad->entity->id;
128         upad->index = kpad->index;
129         upad->flags = kpad->flags;
130 }
131
132 static long __media_device_enum_links(struct media_device *mdev,
133                                       struct media_links_enum *links)
134 {
135         struct media_entity *entity;
136
137         entity = find_entity(mdev, links->entity);
138         if (entity == NULL)
139                 return -EINVAL;
140
141         if (links->pads) {
142                 unsigned int p;
143
144                 for (p = 0; p < entity->num_pads; p++) {
145                         struct media_pad_desc pad;
146
147                         memset(&pad, 0, sizeof(pad));
148                         media_device_kpad_to_upad(&entity->pads[p], &pad);
149                         if (copy_to_user(&links->pads[p], &pad, sizeof(pad)))
150                                 return -EFAULT;
151                 }
152         }
153
154         if (links->links) {
155                 struct media_link_desc __user *ulink;
156                 unsigned int l;
157
158                 for (l = 0, ulink = links->links; l < entity->num_links; l++) {
159                         struct media_link_desc link;
160
161                         /* Ignore backlinks. */
162                         if (entity->links[l].source->entity != entity)
163                                 continue;
164
165                         memset(&link, 0, sizeof(link));
166                         media_device_kpad_to_upad(entity->links[l].source,
167                                                   &link.source);
168                         media_device_kpad_to_upad(entity->links[l].sink,
169                                                   &link.sink);
170                         link.flags = entity->links[l].flags;
171                         if (copy_to_user(ulink, &link, sizeof(*ulink)))
172                                 return -EFAULT;
173                         ulink++;
174                 }
175         }
176
177         return 0;
178 }
179
180 static long media_device_enum_links(struct media_device *mdev,
181                                     struct media_links_enum __user *ulinks)
182 {
183         struct media_links_enum links;
184         int rval;
185
186         if (copy_from_user(&links, ulinks, sizeof(links)))
187                 return -EFAULT;
188
189         rval = __media_device_enum_links(mdev, &links);
190         if (rval < 0)
191                 return rval;
192
193         if (copy_to_user(ulinks, &links, sizeof(*ulinks)))
194                 return -EFAULT;
195
196         return 0;
197 }
198
199 static long media_device_setup_link(struct media_device *mdev,
200                                     struct media_link_desc __user *_ulink)
201 {
202         struct media_link *link = NULL;
203         struct media_link_desc ulink;
204         struct media_entity *source;
205         struct media_entity *sink;
206         int ret;
207
208         if (copy_from_user(&ulink, _ulink, sizeof(ulink)))
209                 return -EFAULT;
210
211         /* Find the source and sink entities and link.
212          */
213         source = find_entity(mdev, ulink.source.entity);
214         sink = find_entity(mdev, ulink.sink.entity);
215
216         if (source == NULL || sink == NULL)
217                 return -EINVAL;
218
219         if (ulink.source.index >= source->num_pads ||
220             ulink.sink.index >= sink->num_pads)
221                 return -EINVAL;
222
223         link = media_entity_find_link(&source->pads[ulink.source.index],
224                                       &sink->pads[ulink.sink.index]);
225         if (link == NULL)
226                 return -EINVAL;
227
228         /* Setup the link on both entities. */
229         ret = __media_entity_setup_link(link, ulink.flags);
230
231         if (copy_to_user(_ulink, &ulink, sizeof(ulink)))
232                 return -EFAULT;
233
234         return ret;
235 }
236
237 static long media_device_ioctl(struct file *filp, unsigned int cmd,
238                                unsigned long arg)
239 {
240         struct media_devnode *devnode = media_devnode_data(filp);
241         struct media_device *dev = to_media_device(devnode);
242         long ret;
243
244         switch (cmd) {
245         case MEDIA_IOC_DEVICE_INFO:
246                 ret = media_device_get_info(dev,
247                                 (struct media_device_info __user *)arg);
248                 break;
249
250         case MEDIA_IOC_ENUM_ENTITIES:
251                 ret = media_device_enum_entities(dev,
252                                 (struct media_entity_desc __user *)arg);
253                 break;
254
255         case MEDIA_IOC_ENUM_LINKS:
256                 mutex_lock(&dev->graph_mutex);
257                 ret = media_device_enum_links(dev,
258                                 (struct media_links_enum __user *)arg);
259                 mutex_unlock(&dev->graph_mutex);
260                 break;
261
262         case MEDIA_IOC_SETUP_LINK:
263                 mutex_lock(&dev->graph_mutex);
264                 ret = media_device_setup_link(dev,
265                                 (struct media_link_desc __user *)arg);
266                 mutex_unlock(&dev->graph_mutex);
267                 break;
268
269         default:
270                 ret = -ENOIOCTLCMD;
271         }
272
273         return ret;
274 }
275
276 #ifdef CONFIG_COMPAT
277
278 struct media_links_enum32 {
279         __u32 entity;
280         compat_uptr_t pads; /* struct media_pad_desc * */
281         compat_uptr_t links; /* struct media_link_desc * */
282         __u32 reserved[4];
283 };
284
285 static long media_device_enum_links32(struct media_device *mdev,
286                                       struct media_links_enum32 __user *ulinks)
287 {
288         struct media_links_enum links;
289         compat_uptr_t pads_ptr, links_ptr;
290
291         memset(&links, 0, sizeof(links));
292
293         if (get_user(links.entity, &ulinks->entity)
294             || get_user(pads_ptr, &ulinks->pads)
295             || get_user(links_ptr, &ulinks->links))
296                 return -EFAULT;
297
298         links.pads = compat_ptr(pads_ptr);
299         links.links = compat_ptr(links_ptr);
300
301         return __media_device_enum_links(mdev, &links);
302 }
303
304 #define MEDIA_IOC_ENUM_LINKS32          _IOWR('|', 0x02, struct media_links_enum32)
305
306 static long media_device_compat_ioctl(struct file *filp, unsigned int cmd,
307                                       unsigned long arg)
308 {
309         struct media_devnode *devnode = media_devnode_data(filp);
310         struct media_device *dev = to_media_device(devnode);
311         long ret;
312
313         switch (cmd) {
314         case MEDIA_IOC_DEVICE_INFO:
315         case MEDIA_IOC_ENUM_ENTITIES:
316         case MEDIA_IOC_SETUP_LINK:
317                 return media_device_ioctl(filp, cmd, arg);
318
319         case MEDIA_IOC_ENUM_LINKS32:
320                 mutex_lock(&dev->graph_mutex);
321                 ret = media_device_enum_links32(dev,
322                                 (struct media_links_enum32 __user *)arg);
323                 mutex_unlock(&dev->graph_mutex);
324                 break;
325
326         default:
327                 ret = -ENOIOCTLCMD;
328         }
329
330         return ret;
331 }
332 #endif /* CONFIG_COMPAT */
333
334 static const struct media_file_operations media_device_fops = {
335         .owner = THIS_MODULE,
336         .open = media_device_open,
337         .ioctl = media_device_ioctl,
338 #ifdef CONFIG_COMPAT
339         .compat_ioctl = media_device_compat_ioctl,
340 #endif /* CONFIG_COMPAT */
341         .release = media_device_close,
342 };
343
344 /* -----------------------------------------------------------------------------
345  * sysfs
346  */
347
348 static ssize_t show_model(struct device *cd,
349                           struct device_attribute *attr, char *buf)
350 {
351         struct media_device *mdev = to_media_device(to_media_devnode(cd));
352
353         return sprintf(buf, "%.*s\n", (int)sizeof(mdev->model), mdev->model);
354 }
355
356 static DEVICE_ATTR(model, S_IRUGO, show_model, NULL);
357
358 /* -----------------------------------------------------------------------------
359  * Registration/unregistration
360  */
361
362 static void media_device_release(struct media_devnode *mdev)
363 {
364 }
365
366 /**
367  * media_device_register - register a media device
368  * @mdev:       The media device
369  *
370  * The caller is responsible for initializing the media device before
371  * registration. The following fields must be set:
372  *
373  * - dev must point to the parent device
374  * - model must be filled with the device model name
375  */
376 int __must_check __media_device_register(struct media_device *mdev,
377                                          struct module *owner)
378 {
379         int ret;
380
381         if (WARN_ON(mdev->dev == NULL || mdev->model[0] == 0))
382                 return -EINVAL;
383
384         mdev->entity_id = 1;
385         INIT_LIST_HEAD(&mdev->entities);
386         spin_lock_init(&mdev->lock);
387         mutex_init(&mdev->graph_mutex);
388
389         /* Register the device node. */
390         mdev->devnode.fops = &media_device_fops;
391         mdev->devnode.parent = mdev->dev;
392         mdev->devnode.release = media_device_release;
393         ret = media_devnode_register(&mdev->devnode, owner);
394         if (ret < 0)
395                 return ret;
396
397         ret = device_create_file(&mdev->devnode.dev, &dev_attr_model);
398         if (ret < 0) {
399                 media_devnode_unregister(&mdev->devnode);
400                 return ret;
401         }
402
403         return 0;
404 }
405 EXPORT_SYMBOL_GPL(__media_device_register);
406
407 /**
408  * media_device_unregister - unregister a media device
409  * @mdev:       The media device
410  *
411  */
412 void media_device_unregister(struct media_device *mdev)
413 {
414         struct media_entity *entity;
415         struct media_entity *next;
416
417         list_for_each_entry_safe(entity, next, &mdev->entities, list)
418                 media_device_unregister_entity(entity);
419
420         device_remove_file(&mdev->devnode.dev, &dev_attr_model);
421         media_devnode_unregister(&mdev->devnode);
422 }
423 EXPORT_SYMBOL_GPL(media_device_unregister);
424
425 /**
426  * media_device_register_entity - Register an entity with a media device
427  * @mdev:       The media device
428  * @entity:     The entity
429  */
430 int __must_check media_device_register_entity(struct media_device *mdev,
431                                               struct media_entity *entity)
432 {
433         /* Warn if we apparently re-register an entity */
434         WARN_ON(entity->parent != NULL);
435         entity->parent = mdev;
436
437         spin_lock(&mdev->lock);
438         if (entity->id == 0)
439                 entity->id = mdev->entity_id++;
440         else
441                 mdev->entity_id = max(entity->id + 1, mdev->entity_id);
442         list_add_tail(&entity->list, &mdev->entities);
443         spin_unlock(&mdev->lock);
444
445         return 0;
446 }
447 EXPORT_SYMBOL_GPL(media_device_register_entity);
448
449 /**
450  * media_device_unregister_entity - Unregister an entity
451  * @entity:     The entity
452  *
453  * If the entity has never been registered this function will return
454  * immediately.
455  */
456 void media_device_unregister_entity(struct media_entity *entity)
457 {
458         struct media_device *mdev = entity->parent;
459
460         if (mdev == NULL)
461                 return;
462
463         spin_lock(&mdev->lock);
464         list_del(&entity->list);
465         spin_unlock(&mdev->lock);
466         entity->parent = NULL;
467 }
468 EXPORT_SYMBOL_GPL(media_device_unregister_entity);