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 depends on (I2C || I2C=n) && VIDEO_DEV default (I2C || I2C=n) && VIDEO_DEV +config VIDEO_V4L2_SUBDEV_API + bool "V4L2 sub-device userspace API (EXPERIMENTAL)" + depends on VIDEO_DEV && MEDIA_CONTROLLER && EXPERIMENTAL + ---help--- + Enables the V4L2 sub-device pad-level userspace API used to configure + video format, size and frame rate between hardware blocks. + + This API is mostly used by camera interfaces in embedded platforms. + # # DVB Core # 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 @@ #include <media/v4l2-fh.h> #include <media/v4l2-event.h> +static int subdev_fh_init(struct v4l2_subdev_fh *fh, struct v4l2_subdev *sd) +{ +#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) + /* Allocate try format and crop in the same memory block */ + fh->try_fmt = kzalloc((sizeof(*fh->try_fmt) + sizeof(*fh->try_crop)) + * sd->entity.num_pads, GFP_KERNEL); + if (fh->try_fmt == NULL) + return -ENOMEM; + + fh->try_crop = (struct v4l2_rect *) + (fh->try_fmt + sd->entity.num_pads); +#endif + return 0; +} + +static void subdev_fh_free(struct v4l2_subdev_fh *fh) +{ +#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) + kfree(fh->try_fmt); + fh->try_fmt = NULL; + fh->try_crop = NULL; +#endif +} + static int subdev_open(struct file *file) { struct video_device *vdev = video_devdata(file); struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); + struct v4l2_subdev_fh *subdev_fh; #if defined(CONFIG_MEDIA_CONTROLLER) struct media_entity *entity; #endif - struct v4l2_fh *vfh = NULL; int ret; - if (sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS) { - vfh = kzalloc(sizeof(*vfh), GFP_KERNEL); - if (vfh == NULL) - return -ENOMEM; + subdev_fh = kzalloc(sizeof(*subdev_fh), GFP_KERNEL); + if (subdev_fh == NULL) + return -ENOMEM; - ret = v4l2_fh_init(vfh, vdev); - if (ret) - goto err; + ret = subdev_fh_init(subdev_fh, sd); + if (ret) { + kfree(subdev_fh); + return ret; + } + + ret = v4l2_fh_init(&subdev_fh->vfh, vdev); + if (ret) + goto err; - ret = v4l2_event_init(vfh); + if (sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS) { + ret = v4l2_event_init(&subdev_fh->vfh); if (ret) goto err; - ret = v4l2_event_alloc(vfh, sd->nevents); + ret = v4l2_event_alloc(&subdev_fh->vfh, sd->nevents); if (ret) goto err; - - v4l2_fh_add(vfh); - file->private_data = vfh; } + + v4l2_fh_add(&subdev_fh->vfh); + file->private_data = &subdev_fh->vfh; #if defined(CONFIG_MEDIA_CONTROLLER) if (sd->v4l2_dev->mdev) { entity = media_entity_get(&sd->entity); @@ -70,14 +100,14 @@ static int subdev_open(struct file *file) } } #endif + return 0; err: - if (vfh != NULL) { - v4l2_fh_del(vfh); - v4l2_fh_exit(vfh); - kfree(vfh); - } + v4l2_fh_del(&subdev_fh->vfh); + v4l2_fh_exit(&subdev_fh->vfh); + subdev_fh_free(subdev_fh); + kfree(subdev_fh); return ret; } @@ -89,16 +119,17 @@ static int subdev_close(struct file *file) struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); #endif struct v4l2_fh *vfh = file->private_data; + struct v4l2_subdev_fh *subdev_fh = to_v4l2_subdev_fh(vfh); #if defined(CONFIG_MEDIA_CONTROLLER) if (sd->v4l2_dev->mdev) media_entity_put(&sd->entity); #endif - if (vfh != NULL) { - v4l2_fh_del(vfh); - v4l2_fh_exit(vfh); - kfree(vfh); - } + v4l2_fh_del(vfh); + v4l2_fh_exit(vfh); + subdev_fh_free(subdev_fh); + kfree(subdev_fh); + file->private_data = NULL; return 0; } @@ -107,7 +138,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct video_device *vdev = video_devdata(file); struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); - struct v4l2_fh *fh = file->private_data; + struct v4l2_fh *vfh = file->private_data; switch (cmd) { case VIDIOC_QUERYCTRL: @@ -135,13 +166,13 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS)) return -ENOIOCTLCMD; - return v4l2_event_dequeue(fh, arg, file->f_flags & O_NONBLOCK); + return v4l2_event_dequeue(vfh, arg, file->f_flags & O_NONBLOCK); case VIDIOC_SUBSCRIBE_EVENT: - return v4l2_subdev_call(sd, core, subscribe_event, fh, arg); + return v4l2_subdev_call(sd, core, subscribe_event, vfh, arg); case VIDIOC_UNSUBSCRIBE_EVENT: - return v4l2_subdev_call(sd, core, unsubscribe_event, fh, arg); + return v4l2_subdev_call(sd, core, unsubscribe_event, vfh, arg); default: 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 @@ #include <media/media-entity.h> #include <media/v4l2-common.h> #include <media/v4l2-dev.h> +#include <media/v4l2-fh.h> #include <media/v4l2-mediabus.h> /* generic v4l2_device notify callback notification values */ @@ -481,6 +482,34 @@ struct v4l2_subdev { #define vdev_to_v4l2_subdev(vdev) \ container_of(vdev, struct v4l2_subdev, devnode) +/* + * Used for storing subdev information per file handle + */ +struct v4l2_subdev_fh { + struct v4l2_fh vfh; +#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) + struct v4l2_mbus_framefmt *try_fmt; + struct v4l2_rect *try_crop; +#endif +}; + +#define to_v4l2_subdev_fh(fh) \ + container_of(fh, struct v4l2_subdev_fh, vfh) + +#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) +static inline struct v4l2_mbus_framefmt * +v4l2_subdev_get_try_format(struct v4l2_subdev_fh *fh, unsigned int pad) +{ + return &fh->try_fmt[pad]; +} + +static inline struct v4l2_rect * +v4l2_subdev_get_try_crop(struct v4l2_subdev_fh *fh, unsigned int pad) +{ + return &fh->try_crop[pad]; +} +#endif + extern const struct v4l2_file_operations v4l2_subdev_fops; static inline void v4l2_set_subdevdata(struct v4l2_subdev *sd, void *p) |