media: tegra: Tegra V4L2 camera
[linux-2.6.git] / drivers / media / video / v4l2-subdev.c
index 923ec8d..b7967c9 100644 (file)
 /*
-    V4L2 sub-device support.
+ * V4L2 sub-device
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ *
+ * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *         Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
 
-    Copyright (C) 2008  Hans Verkuil <hverkuil@xs4all.nl>
+#include <linux/ioctl.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
 
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
 
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
+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;
 
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
+       fh->try_crop = (struct v4l2_rect *)
+               (fh->try_fmt + sd->entity.num_pads);
+#endif
+       return 0;
+}
 
-#include <linux/types.h>
-#include <linux/ioctl.h>
-#include <linux/i2c.h>
-#include <linux/videodev2.h>
-#include <media/v4l2-subdev.h>
+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 = NULL;
+#endif
+       int ret;
+
+       subdev_fh = kzalloc(sizeof(*subdev_fh), GFP_KERNEL);
+       if (subdev_fh == NULL)
+               return -ENOMEM;
+
+       ret = subdev_fh_init(subdev_fh, sd);
+       if (ret) {
+               kfree(subdev_fh);
+               return ret;
+       }
+
+       v4l2_fh_init(&subdev_fh->vfh, vdev);
+       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);
+               if (!entity) {
+                       ret = -EBUSY;
+                       goto err;
+               }
+       }
+#endif
+
+       if (sd->internal_ops && sd->internal_ops->open) {
+               ret = sd->internal_ops->open(sd, subdev_fh);
+               if (ret < 0)
+                       goto err;
+       }
 
-int v4l2_subdev_command(struct v4l2_subdev *sd, unsigned cmd, void *arg)
+       return 0;
+
+err:
+#if defined(CONFIG_MEDIA_CONTROLLER)
+       if (entity)
+               media_entity_put(entity);
+#endif
+       v4l2_fh_del(&subdev_fh->vfh);
+       v4l2_fh_exit(&subdev_fh->vfh);
+       subdev_fh_free(subdev_fh);
+       kfree(subdev_fh);
+
+       return ret;
+}
+
+static int subdev_close(struct file *file)
 {
+       struct video_device *vdev = video_devdata(file);
+       struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+       struct v4l2_fh *vfh = file->private_data;
+       struct v4l2_subdev_fh *subdev_fh = to_v4l2_subdev_fh(vfh);
+
+       if (sd->internal_ops && sd->internal_ops->close)
+               sd->internal_ops->close(sd, subdev_fh);
+#if defined(CONFIG_MEDIA_CONTROLLER)
+       if (sd->v4l2_dev->mdev)
+               media_entity_put(&sd->entity);
+#endif
+       v4l2_fh_del(vfh);
+       v4l2_fh_exit(vfh);
+       subdev_fh_free(subdev_fh);
+       kfree(subdev_fh);
+       file->private_data = NULL;
+
+       return 0;
+}
+
+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 *vfh = file->private_data;
+#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
+       struct v4l2_subdev_fh *subdev_fh = to_v4l2_subdev_fh(vfh);
+#endif
+
        switch (cmd) {
        case VIDIOC_QUERYCTRL:
-               return v4l2_subdev_call(sd, core, queryctrl, arg);
+               return v4l2_queryctrl(vfh->ctrl_handler, arg);
+
+       case VIDIOC_QUERYMENU:
+               return v4l2_querymenu(vfh->ctrl_handler, arg);
+
        case VIDIOC_G_CTRL:
-               return v4l2_subdev_call(sd, core, g_ctrl, arg);
+               return v4l2_g_ctrl(vfh->ctrl_handler, arg);
+
        case VIDIOC_S_CTRL:
-               return v4l2_subdev_call(sd, core, s_ctrl, arg);
+               return v4l2_s_ctrl(vfh, vfh->ctrl_handler, arg);
+
        case VIDIOC_G_EXT_CTRLS:
-               return v4l2_subdev_call(sd, core, g_ext_ctrls, arg);
+               return v4l2_g_ext_ctrls(vfh->ctrl_handler, arg);
+
        case VIDIOC_S_EXT_CTRLS:
-               return v4l2_subdev_call(sd, core, s_ext_ctrls, arg);
+               return v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, arg);
+
        case VIDIOC_TRY_EXT_CTRLS:
-               return v4l2_subdev_call(sd, core, try_ext_ctrls, arg);
-       case VIDIOC_QUERYMENU:
-               return v4l2_subdev_call(sd, core, querymenu, arg);
-       case VIDIOC_LOG_STATUS:
-               return v4l2_subdev_call(sd, core, log_status);
-       case VIDIOC_DBG_G_CHIP_IDENT:
-               return v4l2_subdev_call(sd, core, g_chip_ident, arg);
-       case VIDIOC_INT_S_STANDBY:
-               return v4l2_subdev_call(sd, core, s_standby, arg ? (*(u32 *)arg) : 0);
-       case VIDIOC_INT_RESET:
-               return v4l2_subdev_call(sd, core, reset, arg ? (*(u32 *)arg) : 0);
-       case VIDIOC_INT_S_GPIO:
-               return v4l2_subdev_call(sd, core, s_gpio, arg ? (*(u32 *)arg) : 0);
-       case VIDIOC_INT_INIT:
-               return v4l2_subdev_call(sd, core, init, arg ? (*(u32 *)arg) : 0);
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-       case VIDIOC_DBG_G_REGISTER:
-               return v4l2_subdev_call(sd, core, g_register, arg);
-       case VIDIOC_DBG_S_REGISTER:
-               return v4l2_subdev_call(sd, core, s_register, arg);
-#endif
+               return v4l2_try_ext_ctrls(vfh->ctrl_handler, arg);
+
+       case VIDIOC_DQEVENT:
+               if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS))
+                       return -ENOIOCTLCMD;
+
+               return v4l2_event_dequeue(vfh, arg, file->f_flags & O_NONBLOCK);
+
+       case VIDIOC_SUBSCRIBE_EVENT:
+               return v4l2_subdev_call(sd, core, subscribe_event, vfh, arg);
 
-       case VIDIOC_INT_S_TUNER_MODE:
-               return v4l2_subdev_call(sd, tuner, s_mode, *(enum v4l2_tuner_type *)arg);
-       case AUDC_SET_RADIO:
-               return v4l2_subdev_call(sd, tuner, s_radio);
-       case VIDIOC_S_TUNER:
-               return v4l2_subdev_call(sd, tuner, s_tuner, arg);
-       case VIDIOC_G_TUNER:
-               return v4l2_subdev_call(sd, tuner, g_tuner, arg);
-       case VIDIOC_S_STD:
-               return v4l2_subdev_call(sd, tuner, s_std, *(v4l2_std_id *)arg);
-       case VIDIOC_S_FREQUENCY:
-               return v4l2_subdev_call(sd, tuner, s_frequency, arg);
-       case VIDIOC_G_FREQUENCY:
-               return v4l2_subdev_call(sd, tuner, g_frequency, arg);
-       case TUNER_SET_TYPE_ADDR:
-               return v4l2_subdev_call(sd, tuner, s_type_addr, arg);
-       case TUNER_SET_CONFIG:
-               return v4l2_subdev_call(sd, tuner, s_config, arg);
-
-       case VIDIOC_INT_AUDIO_CLOCK_FREQ:
-               return v4l2_subdev_call(sd, audio, s_clock_freq, *(u32 *)arg);
-       case VIDIOC_INT_S_AUDIO_ROUTING:
-               return v4l2_subdev_call(sd, audio, s_routing, arg);
-       case VIDIOC_INT_I2S_CLOCK_FREQ:
-               return v4l2_subdev_call(sd, audio, s_i2s_clock_freq, *(u32 *)arg);
-
-       case VIDIOC_INT_S_VIDEO_ROUTING:
-               return v4l2_subdev_call(sd, video, s_routing, arg);
-       case VIDIOC_INT_S_CRYSTAL_FREQ:
-               return v4l2_subdev_call(sd, video, s_crystal_freq, arg);
-       case VIDIOC_INT_DECODE_VBI_LINE:
-               return v4l2_subdev_call(sd, video, decode_vbi_line, arg);
-       case VIDIOC_INT_S_VBI_DATA:
-               return v4l2_subdev_call(sd, video, s_vbi_data, arg);
-       case VIDIOC_INT_G_VBI_DATA:
-               return v4l2_subdev_call(sd, video, g_vbi_data, arg);
-       case VIDIOC_G_SLICED_VBI_CAP:
-               return v4l2_subdev_call(sd, video, g_sliced_vbi_cap, arg);
-       case VIDIOC_S_FMT:
-               return v4l2_subdev_call(sd, video, s_fmt, arg);
-       case VIDIOC_G_FMT:
-               return v4l2_subdev_call(sd, video, g_fmt, arg);
-       case VIDIOC_INT_S_STD_OUTPUT:
-               return v4l2_subdev_call(sd, video, s_std_output, *(v4l2_std_id *)arg);
-       case VIDIOC_QUERYSTD:
-               return v4l2_subdev_call(sd, video, querystd, arg);
-       case VIDIOC_INT_G_INPUT_STATUS:
-               return v4l2_subdev_call(sd, video, g_input_status, arg);
-       case VIDIOC_STREAMON:
-               return v4l2_subdev_call(sd, video, s_stream, 1);
-       case VIDIOC_STREAMOFF:
-               return v4l2_subdev_call(sd, video, s_stream, 0);
+       case VIDIOC_UNSUBSCRIBE_EVENT:
+               return v4l2_subdev_call(sd, core, unsubscribe_event, vfh, arg);
+#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
+       case VIDIOC_SUBDEV_G_FMT: {
+               struct v4l2_subdev_format *format = arg;
 
+               if (format->which != V4L2_SUBDEV_FORMAT_TRY &&
+                   format->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+                       return -EINVAL;
+
+               if (format->pad >= sd->entity.num_pads)
+                       return -EINVAL;
+
+               return v4l2_subdev_call(sd, pad, get_fmt, subdev_fh, format);
+       }
+
+       case VIDIOC_SUBDEV_S_FMT: {
+               struct v4l2_subdev_format *format = arg;
+
+               if (format->which != V4L2_SUBDEV_FORMAT_TRY &&
+                   format->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+                       return -EINVAL;
+
+               if (format->pad >= sd->entity.num_pads)
+                       return -EINVAL;
+
+               return v4l2_subdev_call(sd, pad, set_fmt, subdev_fh, format);
+       }
+
+       case VIDIOC_SUBDEV_G_CROP: {
+               struct v4l2_subdev_crop *crop = arg;
+
+               if (crop->which != V4L2_SUBDEV_FORMAT_TRY &&
+                   crop->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+                       return -EINVAL;
+
+               if (crop->pad >= sd->entity.num_pads)
+                       return -EINVAL;
+
+               return v4l2_subdev_call(sd, pad, get_crop, subdev_fh, crop);
+       }
+
+       case VIDIOC_SUBDEV_S_CROP: {
+               struct v4l2_subdev_crop *crop = arg;
+
+               if (crop->which != V4L2_SUBDEV_FORMAT_TRY &&
+                   crop->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+                       return -EINVAL;
+
+               if (crop->pad >= sd->entity.num_pads)
+                       return -EINVAL;
+
+               return v4l2_subdev_call(sd, pad, set_crop, subdev_fh, crop);
+       }
+
+       case VIDIOC_SUBDEV_ENUM_MBUS_CODE: {
+               struct v4l2_subdev_mbus_code_enum *code = arg;
+
+               if (code->pad >= sd->entity.num_pads)
+                       return -EINVAL;
+
+               return v4l2_subdev_call(sd, pad, enum_mbus_code, subdev_fh,
+                                       code);
+       }
+
+       case VIDIOC_SUBDEV_ENUM_FRAME_SIZE: {
+               struct v4l2_subdev_frame_size_enum *fse = arg;
+
+               if (fse->pad >= sd->entity.num_pads)
+                       return -EINVAL;
+
+               return v4l2_subdev_call(sd, pad, enum_frame_size, subdev_fh,
+                                       fse);
+       }
+
+       case VIDIOC_SUBDEV_G_FRAME_INTERVAL:
+               return v4l2_subdev_call(sd, video, g_frame_interval, arg);
+
+       case VIDIOC_SUBDEV_S_FRAME_INTERVAL:
+               return v4l2_subdev_call(sd, video, s_frame_interval, arg);
+
+       case VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL: {
+               struct v4l2_subdev_frame_interval_enum *fie = arg;
+
+               if (fie->pad >= sd->entity.num_pads)
+                       return -EINVAL;
+
+               return v4l2_subdev_call(sd, pad, enum_frame_interval, subdev_fh,
+                                       fie);
+       }
+#endif
        default:
                return v4l2_subdev_call(sd, core, ioctl, cmd, arg);
        }
+
+       return 0;
+}
+
+static long subdev_ioctl(struct file *file, unsigned int cmd,
+       unsigned long arg)
+{
+       return video_usercopy(file, cmd, arg, subdev_do_ioctl);
+}
+
+static unsigned int subdev_poll(struct file *file, poll_table *wait)
+{
+       struct video_device *vdev = video_devdata(file);
+       struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+       struct v4l2_fh *fh = file->private_data;
+
+       if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS))
+               return POLLERR;
+
+       poll_wait(file, &fh->wait, wait);
+
+       if (v4l2_event_pending(fh))
+               return POLLPRI;
+
+       return 0;
+}
+
+const struct v4l2_file_operations v4l2_subdev_fops = {
+       .owner = THIS_MODULE,
+       .open = subdev_open,
+       .unlocked_ioctl = subdev_ioctl,
+       .release = subdev_close,
+       .poll = subdev_poll,
+};
+
+void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops)
+{
+       INIT_LIST_HEAD(&sd->list);
+       BUG_ON(!ops);
+       sd->ops = ops;
+       sd->v4l2_dev = NULL;
+       sd->flags = 0;
+       sd->name[0] = '\0';
+       sd->grp_id = 0;
+       sd->dev_priv = NULL;
+       sd->host_priv = NULL;
+#if defined(CONFIG_MEDIA_CONTROLLER)
+       sd->entity.name = sd->name;
+       sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
+#endif
 }
-EXPORT_SYMBOL_GPL(v4l2_subdev_command);
+EXPORT_SYMBOL(v4l2_subdev_init);