diff options
-rw-r--r-- | drivers/media/Kconfig | 9 | ||||
-rw-r--r-- | drivers/media/video/v4l2-subdev.c | 85 | ||||
-rw-r--r-- | include/media/v4l2-subdev.h | 29 |
3 files changed, 96 insertions, 27 deletions
diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index 2466f2b2ecfa..6995940b633a 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig | |||
@@ -53,6 +53,15 @@ config VIDEO_V4L2_COMMON | |||
53 | depends on (I2C || I2C=n) && VIDEO_DEV | 53 | depends on (I2C || I2C=n) && VIDEO_DEV |
54 | default (I2C || I2C=n) && VIDEO_DEV | 54 | default (I2C || I2C=n) && VIDEO_DEV |
55 | 55 | ||
56 | config VIDEO_V4L2_SUBDEV_API | ||
57 | bool "V4L2 sub-device userspace API (EXPERIMENTAL)" | ||
58 | depends on VIDEO_DEV && MEDIA_CONTROLLER && EXPERIMENTAL | ||
59 | ---help--- | ||
60 | Enables the V4L2 sub-device pad-level userspace API used to configure | ||
61 | video format, size and frame rate between hardware blocks. | ||
62 | |||
63 | This API is mostly used by camera interfaces in embedded platforms. | ||
64 | |||
56 | # | 65 | # |
57 | # DVB Core | 66 | # DVB Core |
58 | # | 67 | # |
diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c index 85573638aa1e..6cef6ad3c9ce 100644 --- a/drivers/media/video/v4l2-subdev.c +++ b/drivers/media/video/v4l2-subdev.c | |||
@@ -31,36 +31,66 @@ | |||
31 | #include <media/v4l2-fh.h> | 31 | #include <media/v4l2-fh.h> |
32 | #include <media/v4l2-event.h> | 32 | #include <media/v4l2-event.h> |
33 | 33 | ||
34 | static int subdev_fh_init(struct v4l2_subdev_fh *fh, struct v4l2_subdev *sd) | ||
35 | { | ||
36 | #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) | ||
37 | /* Allocate try format and crop in the same memory block */ | ||
38 | fh->try_fmt = kzalloc((sizeof(*fh->try_fmt) + sizeof(*fh->try_crop)) | ||
39 | * sd->entity.num_pads, GFP_KERNEL); | ||
40 | if (fh->try_fmt == NULL) | ||
41 | return -ENOMEM; | ||
42 | |||
43 | fh->try_crop = (struct v4l2_rect *) | ||
44 | (fh->try_fmt + sd->entity.num_pads); | ||
45 | #endif | ||
46 | return 0; | ||
47 | } | ||
48 | |||
49 | static void subdev_fh_free(struct v4l2_subdev_fh *fh) | ||
50 | { | ||
51 | #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) | ||
52 | kfree(fh->try_fmt); | ||
53 | fh->try_fmt = NULL; | ||
54 | fh->try_crop = NULL; | ||
55 | #endif | ||
56 | } | ||
57 | |||
34 | static int subdev_open(struct file *file) | 58 | static int subdev_open(struct file *file) |
35 | { | 59 | { |
36 | struct video_device *vdev = video_devdata(file); | 60 | struct video_device *vdev = video_devdata(file); |
37 | struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); | 61 | struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); |
62 | struct v4l2_subdev_fh *subdev_fh; | ||
38 | #if defined(CONFIG_MEDIA_CONTROLLER) | 63 | #if defined(CONFIG_MEDIA_CONTROLLER) |
39 | struct media_entity *entity; | 64 | struct media_entity *entity; |
40 | #endif | 65 | #endif |
41 | struct v4l2_fh *vfh = NULL; | ||
42 | int ret; | 66 | int ret; |
43 | 67 | ||
44 | if (sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS) { | 68 | subdev_fh = kzalloc(sizeof(*subdev_fh), GFP_KERNEL); |
45 | vfh = kzalloc(sizeof(*vfh), GFP_KERNEL); | 69 | if (subdev_fh == NULL) |
46 | if (vfh == NULL) | 70 | return -ENOMEM; |
47 | return -ENOMEM; | ||
48 | 71 | ||
49 | ret = v4l2_fh_init(vfh, vdev); | 72 | ret = subdev_fh_init(subdev_fh, sd); |
50 | if (ret) | 73 | if (ret) { |
51 | goto err; | 74 | kfree(subdev_fh); |
75 | return ret; | ||
76 | } | ||
77 | |||
78 | ret = v4l2_fh_init(&subdev_fh->vfh, vdev); | ||
79 | if (ret) | ||
80 | goto err; | ||
52 | 81 | ||
53 | ret = v4l2_event_init(vfh); | 82 | if (sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS) { |
83 | ret = v4l2_event_init(&subdev_fh->vfh); | ||
54 | if (ret) | 84 | if (ret) |
55 | goto err; | 85 | goto err; |
56 | 86 | ||
57 | ret = v4l2_event_alloc(vfh, sd->nevents); | 87 | ret = v4l2_event_alloc(&subdev_fh->vfh, sd->nevents); |
58 | if (ret) | 88 | if (ret) |
59 | goto err; | 89 | goto err; |
60 | |||
61 | v4l2_fh_add(vfh); | ||
62 | file->private_data = vfh; | ||
63 | } | 90 | } |
91 | |||
92 | v4l2_fh_add(&subdev_fh->vfh); | ||
93 | file->private_data = &subdev_fh->vfh; | ||
64 | #if defined(CONFIG_MEDIA_CONTROLLER) | 94 | #if defined(CONFIG_MEDIA_CONTROLLER) |
65 | if (sd->v4l2_dev->mdev) { | 95 | if (sd->v4l2_dev->mdev) { |
66 | entity = media_entity_get(&sd->entity); | 96 | entity = media_entity_get(&sd->entity); |
@@ -70,14 +100,14 @@ static int subdev_open(struct file *file) | |||
70 | } | 100 | } |
71 | } | 101 | } |
72 | #endif | 102 | #endif |
103 | |||
73 | return 0; | 104 | return 0; |
74 | 105 | ||
75 | err: | 106 | err: |
76 | if (vfh != NULL) { | 107 | v4l2_fh_del(&subdev_fh->vfh); |
77 | v4l2_fh_del(vfh); | 108 | v4l2_fh_exit(&subdev_fh->vfh); |
78 | v4l2_fh_exit(vfh); | 109 | subdev_fh_free(subdev_fh); |
79 | kfree(vfh); | 110 | kfree(subdev_fh); |
80 | } | ||
81 | 111 | ||
82 | return ret; | 112 | return ret; |
83 | } | 113 | } |
@@ -89,16 +119,17 @@ static int subdev_close(struct file *file) | |||
89 | struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); | 119 | struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); |
90 | #endif | 120 | #endif |
91 | struct v4l2_fh *vfh = file->private_data; | 121 | struct v4l2_fh *vfh = file->private_data; |
122 | struct v4l2_subdev_fh *subdev_fh = to_v4l2_subdev_fh(vfh); | ||
92 | 123 | ||
93 | #if defined(CONFIG_MEDIA_CONTROLLER) | 124 | #if defined(CONFIG_MEDIA_CONTROLLER) |
94 | if (sd->v4l2_dev->mdev) | 125 | if (sd->v4l2_dev->mdev) |
95 | media_entity_put(&sd->entity); | 126 | media_entity_put(&sd->entity); |
96 | #endif | 127 | #endif |
97 | if (vfh != NULL) { | 128 | v4l2_fh_del(vfh); |
98 | v4l2_fh_del(vfh); | 129 | v4l2_fh_exit(vfh); |
99 | v4l2_fh_exit(vfh); | 130 | subdev_fh_free(subdev_fh); |
100 | kfree(vfh); | 131 | kfree(subdev_fh); |
101 | } | 132 | file->private_data = NULL; |
102 | 133 | ||
103 | return 0; | 134 | return 0; |
104 | } | 135 | } |
@@ -107,7 +138,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) | |||
107 | { | 138 | { |
108 | struct video_device *vdev = video_devdata(file); | 139 | struct video_device *vdev = video_devdata(file); |
109 | struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); | 140 | struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); |
110 | struct v4l2_fh *fh = file->private_data; | 141 | struct v4l2_fh *vfh = file->private_data; |
111 | 142 | ||
112 | switch (cmd) { | 143 | switch (cmd) { |
113 | case VIDIOC_QUERYCTRL: | 144 | case VIDIOC_QUERYCTRL: |
@@ -135,13 +166,13 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) | |||
135 | if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS)) | 166 | if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS)) |
136 | return -ENOIOCTLCMD; | 167 | return -ENOIOCTLCMD; |
137 | 168 | ||
138 | return v4l2_event_dequeue(fh, arg, file->f_flags & O_NONBLOCK); | 169 | return v4l2_event_dequeue(vfh, arg, file->f_flags & O_NONBLOCK); |
139 | 170 | ||
140 | case VIDIOC_SUBSCRIBE_EVENT: | 171 | case VIDIOC_SUBSCRIBE_EVENT: |
141 | return v4l2_subdev_call(sd, core, subscribe_event, fh, arg); | 172 | return v4l2_subdev_call(sd, core, subscribe_event, vfh, arg); |
142 | 173 | ||
143 | case VIDIOC_UNSUBSCRIBE_EVENT: | 174 | case VIDIOC_UNSUBSCRIBE_EVENT: |
144 | return v4l2_subdev_call(sd, core, unsubscribe_event, fh, arg); | 175 | return v4l2_subdev_call(sd, core, unsubscribe_event, vfh, arg); |
145 | 176 | ||
146 | default: | 177 | default: |
147 | return -ENOIOCTLCMD; | 178 | return -ENOIOCTLCMD; |
diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 6f51ce4d7ee7..72f49eb3002b 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h | |||
@@ -24,6 +24,7 @@ | |||
24 | #include <media/media-entity.h> | 24 | #include <media/media-entity.h> |
25 | #include <media/v4l2-common.h> | 25 | #include <media/v4l2-common.h> |
26 | #include <media/v4l2-dev.h> | 26 | #include <media/v4l2-dev.h> |
27 | #include <media/v4l2-fh.h> | ||
27 | #include <media/v4l2-mediabus.h> | 28 | #include <media/v4l2-mediabus.h> |
28 | 29 | ||
29 | /* generic v4l2_device notify callback notification values */ | 30 | /* generic v4l2_device notify callback notification values */ |
@@ -481,6 +482,34 @@ struct v4l2_subdev { | |||
481 | #define vdev_to_v4l2_subdev(vdev) \ | 482 | #define vdev_to_v4l2_subdev(vdev) \ |
482 | container_of(vdev, struct v4l2_subdev, devnode) | 483 | container_of(vdev, struct v4l2_subdev, devnode) |
483 | 484 | ||
485 | /* | ||
486 | * Used for storing subdev information per file handle | ||
487 | */ | ||
488 | struct v4l2_subdev_fh { | ||
489 | struct v4l2_fh vfh; | ||
490 | #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) | ||
491 | struct v4l2_mbus_framefmt *try_fmt; | ||
492 | struct v4l2_rect *try_crop; | ||
493 | #endif | ||
494 | }; | ||
495 | |||
496 | #define to_v4l2_subdev_fh(fh) \ | ||
497 | container_of(fh, struct v4l2_subdev_fh, vfh) | ||
498 | |||
499 | #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) | ||
500 | static inline struct v4l2_mbus_framefmt * | ||
501 | v4l2_subdev_get_try_format(struct v4l2_subdev_fh *fh, unsigned int pad) | ||
502 | { | ||
503 | return &fh->try_fmt[pad]; | ||
504 | } | ||
505 | |||
506 | static inline struct v4l2_rect * | ||
507 | v4l2_subdev_get_try_crop(struct v4l2_subdev_fh *fh, unsigned int pad) | ||
508 | { | ||
509 | return &fh->try_crop[pad]; | ||
510 | } | ||
511 | #endif | ||
512 | |||
484 | extern const struct v4l2_file_operations v4l2_subdev_fops; | 513 | extern const struct v4l2_file_operations v4l2_subdev_fops; |
485 | 514 | ||
486 | static inline void v4l2_set_subdevdata(struct v4l2_subdev *sd, void *p) | 515 | static inline void v4l2_set_subdevdata(struct v4l2_subdev *sd, void *p) |