|
Signed-off-by: Sakari Ailus <sakari.ailus@xxxxxxxxx
Signed-off-by: Sergio Aguirre <saaguirre@xxxxxx
---
drivers/media/video/Kconfig | 8 +
drivers/media/video/Makefile | 2 +
drivers/media/video/omap34xxcam.c | 2017 +++++++++++++++++++++++++++++++++++++
drivers/media/video/omap34xxcam.h | 215 ++++
4 files changed, 2242 insertions(+), 0 deletions(-)
create mode 100644 drivers/media/video/omap34xxcam.c
create mode 100644 drivers/media/video/omap34xxcam.h
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 47102c2..dcd3960 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -700,6 +700,14 @@ config VIDEO_CAFE_CCIC
CMOS camera controller. This is the controller found on first-
generation OLPC systems.
+config VIDEO_OMAP3
+ tristate "OMAP 3 Camera support"
+ select VIDEOBUF_GEN
+ select VIDEOBUF_DMA_SG
+ depends on VIDEO_V4L2 && ARCH_OMAP34XX
+ ---help---
+ Driver for an OMAP 3 camera controller.
+
config SOC_CAMERA
tristate "SoC camera support"
depends on VIDEO_V4L2 && HAS_DMA
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index 16962f3..a2f73cf 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -101,6 +101,8 @@ obj-$(CONFIG_VIDEO_OV7670) += ov7670.o
obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o
+obj-$(CONFIG_VIDEO_OMAP3) += omap34xxcam.o isp/
+
obj-$(CONFIG_USB_DABUSB) += dabusb.o
obj-$(CONFIG_USB_OV511) += ov511.o
obj-$(CONFIG_USB_SE401) += se401.o
diff --git a/drivers/media/video/omap34xxcam.c b/drivers/media/video/omap34xxcam.c
new file mode 100644
index 0000000..a920bf2
--- /dev/null
+++ b/drivers/media/video/omap34xxcam.c
@@ -0,0 +1,2017 @@
+/*
+ * drivers/media/video/omap34xxcam.c
+ *
+ * Copyright (C) 2006--2008 Nokia Corporation
+ * Copyright (C) 2007, 2008 Texas Instruments
+ *
+ * Contact: Sakari Ailus <sakari.ailus@xxxxxxxxx
+ * Tuukka Toivonen <tuukka.o.toivonen@xxxxxxxxx
+ *
+ * Originally based on the OMAP 2 camera driver.
+ *
+ * Written by Sakari Ailus <sakari.ailus@xxxxxxxxx
+ * Tuukka Toivonen <tuukka.o.toivonen@xxxxxxxxx
+ * Sergio Aguirre <saaguirre@xxxxxx
+ * Mohit Jalori
+ * Sameer Venkatraman
+ * Leonides Martinez
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/io.h
+#include <linux/clk.h
+#include <linux/pci.h /* needed for videobufs */
+#include <linux/delay.h
+#include <linux/kernel.h
+#include <linux/interrupt.h
+#include <linux/videodev2.h
+#include <linux/version.h
+#include <linux/platform_device.h
+
+#include <media/v4l2-common.h
+#include <media/v4l2-ioctl.h
+
+#include "omap34xxcam.h"
+#include "isp/isp.h"
+#include "isp/ispmmu.h"
+#include "isp/ispreg.h"
+#include "isp/ispccdc.h"
+#include "isp/isph3a.h"
+#include "isp/isp_af.h"
+#include "isp/isphist.h"
+#include "isp/isppreview.h"
+#include "isp/ispresizer.h"
+
+#define OMAP34XXCAM_VERSION KERNEL_VERSION(0, 0, 0)
+
+/* global variables */
+static struct omap34xxcam_device *omap34xxcam;
+
+struct omap34xxcam_fh *camfh_saved;
+
+#define OMAP34XXCAM_POWEROFF_DELAY (2 * HZ)
+
+/*
+ *
+ * Sensor handling.
+ *
+ */
+
+/**
+ * omap34xxcam_slave_power_set - set slave power state
+ * @vdev: per-video device data structure
+ * @power: new power state
+ */
+static int omap34xxcam_slave_power_set(struct omap34xxcam_videodev *vdev,
+ enum v4l2_power power,
+ int mask)
+{
+ int rval = 0, i = 0;
+
+ BUG_ON(!mutex_is_locked(&vdev- mutex));
+
+ vdev- power_state_wish = -1;
+
+ for (i = 0; i <= OMAP34XXCAM_SLAVE_FLASH; i++) {
+ if (vdev- slave[i] == v4l2_int_device_dummy())
+ continue;
+
+ if (!(mask & (1 << i))
+ || power == vdev- power_state[i])
+ continue;
+
+ rval = vidioc_int_s_power(vdev- slave[i], power);
+
+ if (rval && power != V4L2_POWER_OFF) {
+ power = V4L2_POWER_OFF;
+ goto out;
+ }
+
+ vdev- power_state[i] = power;
+ }
+
+ return 0;
+
+out:
+ for (i--; i = 0; i--) {
+ if (vdev- slave[i] == v4l2_int_device_dummy())
+ continue;
+
+ if (!(mask & (1 << i)))
+ continue;
+
+ vidioc_int_s_power(vdev- slave[i], power);
+ vdev- power_state[i] = power;
+ }
+
+ return rval;
+}
+
+static void omap34xxcam_slave_power_work(struct work_struct *work)
+{
+ struct omap34xxcam_videodev *vdev =
+ container_of(work, struct omap34xxcam_videodev, poweroff_work);
+
+ mutex_lock(&vdev- mutex);
+
+ if (vdev- power_state_wish != -1)
+ omap34xxcam_slave_power_set(vdev, vdev- power_state_wish,
+ vdev- power_state_mask);
+
+ mutex_unlock(&vdev- mutex);
+}
+
+static void omap34xxcam_slave_power_timer(unsigned long ptr)
+{
+ struct omap34xxcam_videodev *vdev = (void *)ptr;
+
+ schedule_work(&vdev- poweroff_work);
+}
+
+/**
+ * omap34xxcam_slave_power_suggest - delayed power state change
+ *
+ * @vdev: per-video device data structure
+ * @power: new power state
+ */
+static void omap34xxcam_slave_power_suggest(struct omap34xxcam_videodev *vdev,
+ enum v4l2_power power,
+ int mask)
+{
+ BUG_ON(!mutex_is_locked(&vdev- mutex));
+
+ del_timer(&vdev- poweroff_timer);
+
+ vdev- power_state_wish = power;
+ vdev- power_state_mask = mask;
+
+ mod_timer(&vdev- poweroff_timer, jiffies + OMAP34XXCAM_POWEROFF_DELAY);
+}
+
+/**
+ * omap34xxcam_update_vbq - Updates VBQ with completed input buffer
+ * @vb: ptr. to standard V4L2 video buffer structure
+ *
+ * Updates video buffer queue with completed buffer passed as
+ * input parameter. Also updates ISP H3A timestamp and field count
+ * statistics.
+ */
+int omap34xxcam_update_vbq(struct videobuf_buffer *vb)
+{
+ struct omap34xxcam_fh *fh = camfh_saved;
+ struct omap34xxcam_videodev *vdev = fh- vdev;
+ int rval = 0;
+
+ do_gettimeofday(&vb- ts);
+ vb- field_count = atomic_add_return(2, &fh- field_count);
+ vb- state = VIDEOBUF_DONE;
+
+ if (vdev- streaming)
+ rval = 1;
+
+ wake_up(&vb- done);
+
+ return rval;
+}
+
+/**
+ * omap34xxcam_vbq_setup - Calcs size and num of buffs allowed in queue
+ * @vbq: ptr. to standard V4L2 video buffer queue structure
+ * @cnt: ptr to location to hold the count of buffers to be in the queue
+ * @size: ptr to location to hold the size of a frame
+ *
+ * Calculates the number of buffers of current image size that can be
+ * supported by the available capture memory.
+ */
+static int omap34xxcam_vbq_setup(struct videobuf_queue *vbq, unsigned int *cnt,
+ unsigned int *size)
+{
+ struct omap34xxcam_fh *fh = vbq- priv_data;
+
+ if (*cnt <= 0)
+ *cnt = VIDEO_MAX_FRAME; /* supply a default number of buffers */
+
+ if (*cnt VIDEO_MAX_FRAME)
+ *cnt = VIDEO_MAX_FRAME;
+
+ *size = fh- pix.sizeimage;
+
+ while (*size * *cnt fh- vdev- vdev_sensor_config.capture_mem)
+ (*cnt)--;
+
+ while ((*size * *cnt) ispmmu_get_mapeable_space())
+ (*cnt)--;
+
+ return 0;
+}
+
+/**
+ * omap34xxcam_vbq_release - Free resources for input VBQ and VB
+ * @vbq: ptr. to standard V4L2 video buffer queue structure
+ * @vb: ptr to standard V4L2 video buffer structure
+ *
+ * Unmap and free all memory associated with input VBQ and VB, also
+ * unmap the address in ISP MMU. Reset the VB state.
+ */
+static void omap34xxcam_vbq_release(struct videobuf_queue *vbq,
+ struct videobuf_buffer *vb)
+{
+ if (!vbq- streaming) {
+ isp_vbq_release(vbq, vb);
+ videobuf_dma_unmap(vbq, videobuf_to_dma(vb));
+ videobuf_dma_free(videobuf_to_dma(vb));
+ vb- state = VIDEOBUF_NEEDS_INIT;
+ }
+ return;
+}
+
+/**
+ * omap34xxcam_vbq_prepare - V4L2 video ops buf_prepare handler
+ * @vbq: ptr. to standard V4L2 video buffer queue structure
+ * @vb: ptr to standard V4L2 video buffer structure
+ * @field: standard V4L2 field enum
+ *
+ * Verifies there is sufficient locked memory for the requested
+ * buffer, or if there is not, allocates, locks and initializes
+ * it.
+ */
+static int omap34xxcam_vbq_prepare(struct videobuf_queue *vbq,
+ struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct omap34xxcam_fh *fh = vbq- priv_data;
+ int err = 0;
+
+ /*
+ * Accessing pix here is okay since its constant while
+ * streaming is on (and we only get called then).
+ */
+ if (vb- baddr) {
+ /* This is a userspace buffer. */
+ if (fh- pix.sizeimage vb- bsize)
+ /* The buffer isnt big enough. */
+ return -EINVAL;
+ } else {
+ if (vb- state != VIDEOBUF_NEEDS_INIT
+ && fh- pix.sizeimage vb- bsize)
+ /*
+ * We have a kernel bounce buffer that has
+ * already been allocated.
+ */
+ omap34xxcam_vbq_release(vbq, vb);
+ }
+
+ vb- size = fh- pix.bytesperline * fh- pix.height;
+ vb- width = fh- pix.width;
+ vb- height = fh- pix.height;
+ vb- field = field;
+
+ if (vb- state == VIDEOBUF_NEEDS_INIT) {
+ err = videobuf_iolock(vbq, vb, NULL);
+ if (!err) {
+ /* isp_addr will be stored locally inside isp code */
+ err = isp_vbq_prepare(vbq, vb, field);
+ }
+ }
+
+ if (!err)
+ vb- state = VIDEOBUF_PREPARED;
+ else
+ omap34xxcam_vbq_release(vbq, vb);
+
+ return err;
+
+}
+
+/**
+ * omap34xxcam_vbq_queue - V4L2 video ops buf_queue handler
+ * @vbq: ptr. to standard V4L2 video buffer queue structure
+ * @vb: ptr to standard V4L2 video buffer structure
+ *
+ * Maps the video buffer to sgdma and through the isp, sets
+ * the isp buffer done callback and sets the video buffer state
+ * to active.
+ */
+static void omap34xxcam_vbq_queue(struct videobuf_queue *vbq,
+ struct videobuf_buffer *vb)
+{
+ struct omap34xxcam_fh *fh = vbq- priv_data;
+ struct omap34xxcam_videodev *vdev = fh- vdev;
+ enum videobuf_state state = vb- state;
+ isp_vbq_callback_ptr func_ptr;
+ int err = 0;
+ camfh_saved = fh;
+
+ func_ptr = omap34xxcam_update_vbq;
+ vb- state = VIDEOBUF_ACTIVE;
+
+ err = isp_sgdma_queue(videobuf_to_dma(vb),
+ vb, 0, &vdev- cam- dma_notify, func_ptr);
+ if (err) {
+ dev_dbg(vdev- cam- dev, "vbq queue failed
");
+ vb- state = state;
+ }
+
+}
+
+static struct videobuf_queue_ops omap34xxcam_vbq_ops = {
+ .buf_setup = omap34xxcam_vbq_setup,
+ .buf_prepare = omap34xxcam_vbq_prepare,
+ .buf_queue = omap34xxcam_vbq_queue,
+ .buf_release = omap34xxcam_vbq_release,
+};
+
+/*
+ *
+ * IOCTL interface.
+ *
+ */
+
+/**
+ * vidioc_querycap - V4L2 query capabilities IOCTL handler
+ * @file: ptr. to system file structure
+ * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data)
+ * @cap: ptr to standard V4L2 capability structure
+ *
+ * Fill in the V4L2 capabliity structure for the camera device
+ */
+static int vidioc_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ struct omap34xxcam_fh *ofh = fh;
+ struct omap34xxcam_videodev *vdev = ofh- vdev;
+
+ strlcpy(cap- driver, CAM_SHORT_NAME, sizeof(cap- driver));
+ strlcpy(cap- card, vdev- vfd- name, sizeof(cap- card));
+ cap- version = OMAP34XXCAM_VERSION;
+ cap- capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+
+ return 0;
+}
+
+/**
+ * vidioc_enum_fmt_vid_cap - V4L2 enumerate format capabilities IOCTL handler
+ * @file: ptr. to system file structure
+ * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data)
+ * @f: ptr to standard V4L2 format description structure
+ *
+ * Fills in enumerate format capabilities information for sensor (if SOC
+ * sensor attached) or ISP (if raw sensor attached).
+ */
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f)
+{
+ struct omap34xxcam_fh *ofh = fh;
+ struct omap34xxcam_videodev *vdev = ofh- vdev;
+ int rval;
+
+ if (vdev- vdev_sensor_config.sensor_isp)
+ rval = vidioc_int_enum_fmt_cap(vdev- vdev_sensor, f);
+ else
+ rval = isp_enum_fmt_cap(f);
+
+ return rval;
+}
+
+/**
+ * vidioc_g_fmt_vid_cap - V4L2 get format capabilities IOCTL handler
+ * @file: ptr. to system file structure
+ * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data)
+ * @f: ptr to standard V4L2 format structure
+ *
+ * Fills in format capabilities for sensor (if SOC sensor attached) or ISP
+ * (if raw sensor attached).
+ */
+static int vidioc_g_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct omap34xxcam_fh *ofh = fh;
+ struct omap34xxcam_videodev *vdev = ofh- vdev;
+
+ mutex_lock(&vdev- mutex);
+ f- fmt.pix = ofh- pix;
+ mutex_unlock(&vdev- mutex);
+
+ return 0;
+}
+
+static int try_pix_parm(struct omap34xxcam_videodev *vdev,
+ struct v4l2_pix_format *best_pix_in,
+ struct v4l2_pix_format *wanted_pix_out,
+ struct v4l2_fract *best_ival)
+{
+ int fps;
+ int rval;
+ int size_index;
+ struct v4l2_pix_format best_pix_out;
+
+ if (best_ival- numerator == 0
+ || best_ival- denominator == 0)
+ best_ival- denominator = 30, best_ival- numerator = 1;
+
+ fps = best_ival- denominator / best_ival- numerator;
+
+ best_ival- denominator = 0;
+
+ best_pix_out.height = INT_MAX 1;
+ best_pix_out.width = best_pix_out.height;
+
+ /*
+ * Get supported resolutions.
+ */
+ for (size_index = 0; ; size_index++) {
+ struct v4l2_frmsizeenum frms;
+ struct v4l2_pix_format pix_tmp_in, pix_tmp_out;
+ int ival_index;
+
+ frms.index = size_index;
+ frms.pixel_format = best_pix_in- pixelformat;
+
+ rval = vidioc_int_enum_framesizes(vdev- vdev_sensor, &frms);
+ if (rval) {
+ rval = 0;
+ break;
+ }
+
+ pix_tmp_in.pixelformat = frms.pixel_format;
+ pix_tmp_in.width = frms.discrete.width;
+ pix_tmp_in.height = frms.discrete.height;
+ pix_tmp_out = *wanted_pix_out;
+ /* Dont do upscaling. */
+ if (pix_tmp_out.width pix_tmp_in.width)
+ pix_tmp_out.width = pix_tmp_in.width;
+ if (pix_tmp_out.height pix_tmp_in.height)
+ pix_tmp_out.height = pix_tmp_in.height;
+ rval = isp_try_fmt_cap(&pix_tmp_in, &pix_tmp_out);
+ if (rval)
+ return rval;
+
+#define IS_SMALLER_OR_EQUAL(pix1, pix2)
+ ((pix1)- width + (pix1)- height
+ < (pix2)- width + (pix2)- height)
+#define SIZE_DIFF(pix1, pix2)
+ (abs((pix1)- width - (pix2)- width)
+ + abs((pix1)- height - (pix2)- height))
+
+ /*
+ * Dont use modes that are farther from wanted size
+ * that what we already got.
+ */
+ if (SIZE_DIFF(&pix_tmp_out, wanted_pix_out)
+ SIZE_DIFF(&best_pix_out, wanted_pix_out))
+ continue;
+
+ /*
+ * Theres an input mode that can provide output
+ * closer to wanted.
+ */
+ if (SIZE_DIFF(&pix_tmp_out, wanted_pix_out)
+ < SIZE_DIFF(&best_pix_out, wanted_pix_out))
+ /* Force renegotation of fps etc. */
+ best_ival- denominator = 0;
+
+ for (ival_index = 0; ; ival_index++) {
+ struct v4l2_frmivalenum frmi;
+
+ frmi.index = ival_index;
+ frmi.pixel_format = frms.pixel_format;
+ frmi.width = frms.discrete.width;
+ frmi.height = frms.discrete.height;
+ /* FIXME: try to fix standard... */
+ frmi.reserved[0] = 0xdeafbeef;
+
+ rval = vidioc_int_enum_frameintervals(vdev- vdev_sensor,
+ &frmi);
+ if (rval) {
+ rval = 0;
+ break;
+ }
+
+ if (best_ival- denominator == 0)
+ goto do_it_now;
+
+ /*
+ * We aim to use maximum resolution from the
+ * sensor, provided that the fps is at least
+ * as close as on the current mode.
+ */
+#define FPS_ABS_DIFF(fps, ival) abs(fps - (ival).denominator / (ival).numerator)
+
+ /* Select mode with closest fps. */
+ if (FPS_ABS_DIFF(fps, frmi.discrete)
+ < FPS_ABS_DIFF(fps, *best_ival))
+ goto do_it_now;
+
+ /*
+ * Select bigger resolution if its available
+ * at same fps.
+ */
+ if (frmi.width best_pix_in- width
+ && FPS_ABS_DIFF(fps, frmi.discrete)
+ <= FPS_ABS_DIFF(fps, *best_ival))
+ goto do_it_now;
+
+ continue;
+
+do_it_now:
+ *best_ival = frmi.discrete;
+ best_pix_out = pix_tmp_out;
+ best_pix_in- width = frmi.width;
+ best_pix_in- height = frmi.height;
+ best_pix_in- pixelformat = frmi.pixel_format;
+ }
+ }
+
+ if (best_ival- denominator == 0)
+ return -EINVAL;
+
+ *wanted_pix_out = best_pix_out;
+
+ dev_info(vdev- cam- dev, "w %d, h %d - w %d, h %d
",
+ best_pix_in- width, best_pix_in- height,
+ best_pix_out.width, best_pix_out.height);
+
+ return isp_try_fmt_cap(best_pix_in, wanted_pix_out);
+}
+
+static int s_pix_parm(struct omap34xxcam_videodev *vdev,
+ struct v4l2_pix_format *best_pix,
+ struct v4l2_pix_format *pix,
+ struct v4l2_fract *best_ival)
+{
+ struct v4l2_streamparm a;
+ struct v4l2_format fmt;
+ int rval;
+ int fmtd_index;
+
+ for (fmtd_index = 0; ; fmtd_index++) {
+ struct v4l2_fmtdesc fmtd;
+
+ fmtd.index = fmtd_index;
+ fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ rval = vidioc_int_enum_fmt_cap(vdev- vdev_sensor, &fmtd);
+ if (rval)
+ return rval;
+ best_pix- pixelformat = fmtd.pixelformat;
+
+ rval = try_pix_parm(vdev, best_pix, pix, best_ival);
+ if (!rval)
+ break;
+ }
+
+ rval = isp_s_fmt_cap(best_pix, pix);
+ if (rval)
+ return rval;
+
+ fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ fmt.fmt.pix = *best_pix;
+ rval = vidioc_int_s_fmt_cap(vdev- vdev_sensor, &fmt);
+ if (rval)
+ return rval;
+
+ a.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ a.parm.capture.timeperframe = *best_ival;
+ rval = vidioc_int_s_parm(vdev- vdev_sensor, &a);
+
+ return rval;
+}
+
+/**
+ * vidioc_s_fmt_vid_cap - V4L2 set format capabilities IOCTL handler
+ * @file: ptr. to system file structure
+ * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data)
+ * @f: ptr to standard V4L2 format structure
+ *
+ * Attempts to set input format with the sensor driver (first) and then the
+ * ISP. Returns the return code from vidioc_g_fmt_vid_cap().
+ */
+static int vidioc_s_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct omap34xxcam_fh *ofh = fh;
+ struct omap34xxcam_videodev *vdev = ofh- vdev;
+ struct v4l2_pix_format pix_tmp;
+ struct v4l2_fract timeperframe;
+ int rval;
+
+ mutex_lock(&vdev- mutex);
+ if (vdev- streaming) {
+ rval = -EBUSY;
+ goto out;
+ }
+
+ vdev- want_pix = f- fmt.pix;
+
+ timeperframe = vdev- want_timeperframe;
+
+ rval = s_pix_parm(vdev, &pix_tmp, &f- fmt.pix, &timeperframe);
+ pix_tmp = f- fmt.pix;
+
+out:
+ mutex_unlock(&vdev- mutex);
+
+ if (!rval) {
+ mutex_lock(&ofh- vbq.vb_lock);
+ ofh- pix = pix_tmp;
+ mutex_unlock(&ofh- vbq.vb_lock);
+ }
+
+ return rval;
+}
+
+/**
+ * vidioc_try_fmt_vid_cap - V4L2 try format capabilities IOCTL handler
+ * @file: ptr. to system file structure
+ * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data)
+ * @f: ptr to standard V4L2 format structure
+ *
+ * Checks if the given format is supported by the sensor driver and
+ * by the ISP.
+ */
+static int vidioc_try_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct omap34xxcam_fh *ofh = fh;
+ struct omap34xxcam_videodev *vdev = ofh- vdev;
+ struct v4l2_pix_format pix_tmp;
+ struct v4l2_fract timeperframe;
+ int rval;
+ int fmtd_index;
+
+ mutex_lock(&vdev- mutex);
+
+
+ for (fmtd_index = 0; ; fmtd_index++) {
+ struct v4l2_fmtdesc fmtd;
+
+ timeperframe = vdev- want_timeperframe;
+
+ fmtd.index = fmtd_index;
+ fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ rval = vidioc_int_enum_fmt_cap(vdev- vdev_sensor, &fmtd);
+ if (rval)
+ return rval;
+ pix_tmp.pixelformat = fmtd.pixelformat;
+
+ rval = try_pix_parm(vdev, &pix_tmp, &f- fmt.pix, &timeperframe);
+ if (!rval)
+ break;
+ }
+
+ mutex_unlock(&vdev- mutex);
+
+ return rval;
+}
+
+/**
+ * vidioc_reqbufs - V4L2 request buffers IOCTL handler
+ * @file: ptr. to system file structure
+ * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data)
+ * @b: ptr to standard V4L2 request buffers structure
+ *
+ * Attempts to get a buffer from the buffer queue associated with the
+ * fh through the video buffer library API.
+ */
+static int vidioc_reqbufs(struct file *file, void *fh,
+ struct v4l2_requestbuffers *b)
+{
+ struct omap34xxcam_fh *ofh = fh;
+ struct omap34xxcam_videodev *vdev = ofh- vdev;
+ int rval;
+
+ mutex_lock(&vdev- mutex);
+ if (vdev- streaming) {
+ mutex_unlock(&vdev- mutex);
+ return -EBUSY;
+ }
+
+ mutex_unlock(&vdev- mutex);
+
+ rval = videobuf_reqbufs(&ofh- vbq, b);
+
+ /*
+ * Either videobuf_reqbufs failed or the buffers are not
+ * memory-mapped (which would need special attention).
+ */
+ if (rval < 0 || b- memory != V4L2_MEMORY_MMAP)
+ goto out;
+
+out:
+ return rval;
+}
+
+/**
+ * vidioc_querybuf - V4L2 query buffer IOCTL handler
+ * @file: ptr. to system file structure
+ * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data)
+ * @b: ptr to standard V4L2 buffer structure
+ *
+ * Attempts to fill in the v4l2_buffer structure for the buffer queue
+ * associated with the fh through the video buffer library API.
+ */
+static int vidioc_querybuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+ struct omap34xxcam_fh *ofh = fh;
+
+ return videobuf_querybuf(&ofh- vbq, b);
+}
+
+/**
+ * vidioc_qbuf - V4L2 queue buffer IOCTL handler
+ * @file: ptr. to system file structure
+ * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data)
+ * @b: ptr to standard V4L2 buffer structure
+ *
+ * Attempts to queue the v4l2_buffer on the buffer queue
+ * associated with the fh through the video buffer library API.
+ */
+static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+ struct omap34xxcam_fh *ofh = fh;
+
+ return videobuf_qbuf(&ofh- vbq, b);
+}
+
+/**
+ * vidioc_dqbuf - V4L2 dequeue buffer IOCTL handler
+ * @file: ptr. to system file structure
+ * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data)
+ * @b: ptr to standard V4L2 buffer structure
+ *
+ * Attempts to dequeue the v4l2_buffer from the buffer queue
+ * associated with the fh through the video buffer library API. If the
+ * buffer is a user space buffer, then this function will also requeue it,
+ * as user does not expect to do this.
+ */
+static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+ struct omap34xxcam_fh *ofh = fh;
+
+ return videobuf_dqbuf(&ofh- vbq, b, file- f_flags & O_NONBLOCK);
+}
+
+/**
+ * vidioc_streamon - V4L2 streamon IOCTL handler
+ * @file: ptr. to system file structure
+ * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data)
+ * @i: V4L2 buffer type
+ *
+ * Attempts to start streaming by enabling the sensor interface and turning
+ * on video buffer streaming through the video buffer library API. Upon
+ * success the function returns 0, otherwise an error code is returned.
+ */
+static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i)
+{
+ struct omap34xxcam_fh *ofh = fh;
+ struct omap34xxcam_videodev *vdev = ofh- vdev;
+ struct omap34xxcam_device *cam = vdev- cam;
+ int rval;
+
+ mutex_lock(&vdev- mutex);
+ if (vdev- streaming) {
+ rval = -EBUSY;
+ goto out;
+ }
+
+ cam- dma_notify = 1;
+ isp_sgdma_init();
+ rval = videobuf_streamon(&ofh- vbq);
+ if (rval) {
+ dev_dbg(vdev- cam- dev, "omap34xxcam_slave_power_set failed
");
+ goto out;
+ }
+
+
+ rval = omap34xxcam_slave_power_set(vdev, V4L2_POWER_ON,
+ OMAP34XXCAM_SLAVE_POWER_SENSOR_LENS);
+ if (!rval) {
+ vdev- streaming = file;
+ } else {
+ isp_stop();
+ videobuf_streamoff(&ofh- vbq);
+ }
+
+out:
+ mutex_unlock(&vdev- mutex);
+
+ return rval;
+}
+
+/**
+ * vidioc_streamoff - V4L2 streamoff IOCTL handler
+ * @file: ptr. to system file structure
+ * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data)
+ * @i: V4L2 buffer type
+ *
+ * Attempts to stop streaming by flushing all scheduled work, waiting on
+ * any queued buffers to complete and then stopping the ISP and turning
+ * off video buffer streaming through the video buffer library API. Upon
+ * success the function returns 0, otherwise an error code is returned.
+ */
+static int vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i)
+{
+ struct omap34xxcam_fh *ofh = fh;
+ struct omap34xxcam_videodev *vdev = ofh- vdev;
+ struct videobuf_queue *q = &ofh- vbq;
+ int rval;
+
+ mutex_lock(&vdev- mutex);
+
+ if (vdev- streaming == file)
+ isp_stop();
+
+ rval = videobuf_streamoff(q);
+ if (!rval)
+ vdev- streaming = NULL;
+
+ omap34xxcam_slave_power_set(vdev, V4L2_POWER_STANDBY,
+ OMAP34XXCAM_SLAVE_POWER_SENSOR);
+ omap34xxcam_slave_power_suggest(vdev, V4L2_POWER_STANDBY,
+ OMAP34XXCAM_SLAVE_POWER_LENS);
+
+ mutex_unlock(&vdev- mutex);
+
+ return rval;
+}
+
+/**
+ * vidioc_enum_input - V4L2 enumerate input IOCTL handler
+ * @file: ptr. to system file structure
+ * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data)
+ * @inp: V4L2 input type information structure
+ *
+ * Fills in v4l2_input structure. Returns 0.
+ */
+static int vidioc_enum_input(struct file *file, void *fh,
+ struct v4l2_input *inp)
+{
+ if (inp- index 0)
+ return -EINVAL;
+
+ strlcpy(inp- name, "camera", sizeof(inp- name));
+ inp- type = V4L2_INPUT_TYPE_CAMERA;
+
+ return 0;
+}
+
+/**
+ * vidioc_g_input - V4L2 get input IOCTL handler
+ * @file: ptr. to system file structure
+ * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data)
+ * @i: address to hold index of input supported
+ *
+ * Sets index to 0.
+ */
+static int vidioc_g_input(struct file *file, void *fh, unsigned int *i)
+{
+ *i = 0;
+
+ return 0;
+}
+
+/**
+ * vidioc_s_input - V4L2 set input IOCTL handler
+ * @file: ptr. to system file structure
+ * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data)
+ * @i: index of input selected
+ *
+ * 0 is only index supported.
+ */
+static int vidioc_s_input(struct file *file, void *fh, unsigned int i)
+{
+ if (i 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+/**
+ * vidioc_queryctrl - V4L2 query control IOCTL handler
+ * @file: ptr. to system file structure
+ * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data)
+ * @a: standard V4L2 query control ioctl structure
+ *
+ * If the requested control is supported, returns the control information
+ * in the v4l2_queryctrl structure. Otherwise, returns -EINVAL if the
+ * control is not supported. If the sensor being used is a "smart sensor",
+ * this request is passed to the sensor driver, otherwise the ISP is
+ * queried and if it does not support the requested control, the request
+ * is forwarded to the "raw" sensor driver to see if it supports it.
+ */
+static int vidioc_queryctrl(struct file *file, void *fh,
+ struct v4l2_queryctrl *a)
+{
+ struct omap34xxcam_fh *ofh = fh;
+ struct omap34xxcam_videodev *vdev = ofh- vdev;
+ struct v4l2_queryctrl a_tmp;
+ int best_slave = -1;
+ u32 best_ctrl = (u32)-1;
+ int i;
+
+ if (vdev- vdev_sensor_config.sensor_isp)
+ return vidioc_int_queryctrl(vdev- vdev_sensor, a);
+
+ /* No next flags: try slaves directly. */
+ if (!(a- id & V4L2_CTRL_FLAG_NEXT_CTRL)) {
+ for (i = 0; i <= OMAP34XXCAM_SLAVE_FLASH; i++) {
+ if (!vidioc_int_queryctrl(vdev- slave[i], a))
+ return 0;
+ }
+ return isp_queryctrl(a);
+ }
+
+ /* Find slave with smallest next control id. */
+ for (i = 0; i <= OMAP34XXCAM_SLAVE_FLASH; i++) {
+ a_tmp = *a;
+
+ if (vidioc_int_queryctrl(vdev- slave[i], &a_tmp))
+ continue;
+
+ if (a_tmp.id < best_ctrl) {
+ best_slave = i;
+ best_ctrl = a_tmp.id;
+ }
+ }
+
+ a_tmp = *a;
+ if (!isp_queryctrl(&a_tmp)) {
+ if (a_tmp.id < best_ctrl) {
+ *a = a_tmp;
+
+ return 0;
+ }
+ }
+
+ if (best_slave == -1)
+ return -EINVAL;
+
+ a- id = best_ctrl;
+ return vidioc_int_queryctrl(vdev- slave[best_slave], a);
+}
+
+/**
+ * vidioc_querymenu - V4L2 query menu IOCTL handler
+ * @file: ptr. to system file structure
+ * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data)
+ * @a: standard V4L2 query menu ioctl structure
+ *
+ * If the requested control is supported, returns the menu information
+ * in the v4l2_querymenu structure. Otherwise, returns -EINVAL if the
+ * control is not supported or is not a menu. If the sensor being used
+ * is a "smart sensor", this request is passed to the sensor driver,
+ * otherwise the ISP is queried and if it does not support the requested
+ * menu control, the request is forwarded to the "raw" sensor driver to
+ * see if it supports it.
+ */
+static int vidioc_querymenu(struct file *file, void *fh,
+ struct v4l2_querymenu *a)
+{
+ struct omap34xxcam_fh *ofh = fh;
+ struct omap34xxcam_videodev *vdev = ofh- vdev;
+ int i;
+
+ if (vdev- vdev_sensor_config.sensor_isp)
+ return vidioc_int_querymenu(vdev- vdev_sensor, a);
+
+ /* Try slaves directly. */
+ for (i = 0; i <= OMAP34XXCAM_SLAVE_FLASH; i++) {
+ if (!vidioc_int_querymenu(vdev- slave[i], a))
+ return 0;
+ }
+ return isp_querymenu(a);
+}
+
+static int vidioc_g_ext_ctrls(struct file *file, void *fh,
+ struct v4l2_ext_controls *a)
+{
+ struct omap34xxcam_fh *ofh = fh;
+ struct omap34xxcam_videodev *vdev = ofh- vdev;
+ int i, ctrl_idx, rval = 0;
+
+ mutex_lock(&vdev- mutex);
+
+ for (ctrl_idx = 0; ctrl_idx < a- count; ctrl_idx++) {
+ struct v4l2_control ctrl;
+
+ ctrl.id = a- controls[ctrl_idx].id;
+
+ if (vdev- vdev_sensor_config.sensor_isp) {
+ rval = vidioc_int_g_ctrl(vdev- vdev_sensor, &ctrl);
+ } else {
+ for (i = 0; i <= OMAP34XXCAM_SLAVE_FLASH; i++) {
+ rval = vidioc_int_g_ctrl(vdev- slave[i], &ctrl);
+ if (!rval)
+ break;
+ }
+ }
+
+ if (rval)
+ rval = isp_g_ctrl(&ctrl);
+
+ if (rval) {
+ a- error_idx = ctrl_idx;
+ break;
+ }
+
+ a- controls[ctrl_idx].value = ctrl.value;
+ }
+
+ mutex_unlock(&vdev- mutex);
+
+ return rval;
+}
+
+static int vidioc_s_ext_ctrls(struct file *file, void *fh,
+ struct v4l2_ext_controls *a)
+{
+ struct omap34xxcam_fh *ofh = fh;
+ struct omap34xxcam_videodev *vdev = ofh- vdev;
+ int i, ctrl_idx, rval = 0;
+
+ mutex_lock(&vdev- mutex);
+
+ for (ctrl_idx = 0; ctrl_idx < a- count; ctrl_idx++) {
+ struct v4l2_control ctrl;
+
+ ctrl.id = a- controls[ctrl_idx].id;
+ ctrl.value = a- controls[ctrl_idx].value;
+
+ if (vdev- vdev_sensor_config.sensor_isp) {
+ rval = vidioc_int_s_ctrl(vdev- vdev_sensor, &ctrl);
+ } else {
+ for (i = 0; i <= OMAP34XXCAM_SLAVE_FLASH; i++) {
+ rval = vidioc_int_s_ctrl(vdev- slave[i], &ctrl);
+ if (!rval)
+ break;
+ }
+ }
+
+ if (rval)
+ rval = isp_s_ctrl(&ctrl);
+
+ if (rval) {
+ a- error_idx = ctrl_idx;
+ break;
+ }
+
+ a- controls[ctrl_idx].value = ctrl.value;
+ }
+
+ mutex_unlock(&vdev- mutex);
+
+ return rval;
+}
+
+/**
+ * vidioc_g_parm - V4L2 get parameters IOCTL handler
+ * @file: ptr. to system file structure
+ * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data)
+ * @a: standard V4L2 stream parameters structure
+ *
+ * If request is for video capture buffer type, handles request by
+ * forwarding to sensor driver.
+ */
+static int vidioc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
+{
+ struct omap34xxcam_fh *ofh = fh;
+ struct omap34xxcam_videodev *vdev = ofh- vdev;
+ int rval;
+
+ if (a- type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ mutex_lock(&vdev- mutex);
+ rval = vidioc_int_g_parm(vdev- vdev_sensor, a);
+ mutex_unlock(&vdev- mutex);
+
+ return rval;
+}
+
+/**
+ * vidioc_s_parm - V4L2 set parameters IOCTL handler
+ * @file: ptr. to system file structure
+ * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data)
+ * @a: standard V4L2 stream parameters structure
+ *
+ * If request is for video capture buffer type, handles request by
+ * first getting current stream parameters from sensor, then forwarding
+ * request to set new parameters to sensor driver. It then attempts to
+ * enable the sensor interface with the new parameters. If this fails, it
+ * reverts back to the previous parameters.
+ */
+static int vidioc_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
+{
+ struct omap34xxcam_fh *ofh = fh;
+ struct omap34xxcam_videodev *vdev = ofh- vdev;
+ struct v4l2_pix_format pix_tmp_sensor, pix_tmp;
+ int rval;
+
+ if (a- type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ mutex_lock(&vdev- mutex);
+ if (vdev- streaming) {
+ rval = -EBUSY;
+ goto out;
+ }
+
+ vdev- want_timeperframe = a- parm.capture.timeperframe;
+
+ pix_tmp = vdev- want_pix;
+
+ rval = s_pix_parm(vdev, &pix_tmp_sensor, &pix_tmp,
+ &a- parm.capture.timeperframe);
+
+out:
+ mutex_unlock(&vdev- mutex);
+
+ return rval;
+}
+
+/**
+ * vidioc_cropcap - V4L2 crop capture IOCTL handler
+ * @file: ptr. to system file structure
+ * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data)
+ * @a: standard V4L2 crop capture structure
+ *
+ * If using a "smart" sensor, just forwards request to the sensor driver,
+ * otherwise fills in the v4l2_cropcap values locally.
+ */
+static int vidioc_cropcap(struct file *file, void *fh, struct v4l2_cropcap *a)
+{
+ struct omap34xxcam_fh *ofh = fh;
+ struct omap34xxcam_videodev *vdev = ofh- vdev;
+ struct v4l2_cropcap *cropcap = a;
+ int rval;
+
+ mutex_lock(&vdev- mutex);
+
+ if (vdev- vdev_sensor_config.sensor_isp) {
+ rval = vidioc_int_cropcap(vdev- vdev_sensor, a);
+ } else {
+ struct v4l2_format f;
+
+ rval = vidioc_int_cropcap(vdev- vdev_sensor, a);
+ if (!rval)
+ return rval;
+
+ /* cropcap failed, try to do this via g_fmt_cap */
+ rval = vidioc_int_g_fmt_cap(vdev- vdev_sensor, &f);
+ if (!rval) {
+ cropcap- bounds.top = 0;
+ cropcap- bounds.left = 0;
+ cropcap- bounds.width = f.fmt.pix.width;
+ cropcap- bounds.height = f.fmt.pix.height;
+ cropcap- defrect = cropcap- bounds;
+ cropcap- pixelaspect.numerator = 1;
+ cropcap- pixelaspect.denominator = 1;
+ }
+ }
+
+ mutex_unlock(&vdev- mutex);
+
+ return rval;
+}
+
+/**
+ * vidioc_g_crop - V4L2 get capture crop IOCTL handler
+ * @file: ptr. to system file structure
+ * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data)
+ * @a: standard V4L2 crop structure
+ *
+ * If using a "smart" sensor, just forwards request to the sensor driver,
+ * otherwise calls the isp functions to fill in current crop values.
+ */
+static int vidioc_g_crop(struct file *file, void *fh, struct v4l2_crop *a)
+{
+ struct omap34xxcam_fh *ofh = fh;
+ struct omap34xxcam_videodev *vdev = ofh- vdev;
+ int rval = 0;
+
+ mutex_lock(&vdev- mutex);
+
+ if (vdev- vdev_sensor_config.sensor_isp)
+ rval = vidioc_int_g_crop(vdev- vdev_sensor, a);
+ else
+ rval = isp_g_crop(a);
+
+ mutex_unlock(&vdev- mutex);
+
+ return rval;
+}
+
+/**
+ * vidioc_s_crop - V4L2 set capture crop IOCTL handler
+ * @file: ptr. to system file structure
+ * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data)
+ * @a: standard V4L2 crop structure
+ *
+ * If using a "smart" sensor, just forwards request to the sensor driver,
+ * otherwise calls the isp functions to set the current crop values.
+ */
+static int vidioc_s_crop(struct file *file, void *fh, struct v4l2_crop *a)
+{
+ struct omap34xxcam_fh *ofh = fh;
+ struct omap34xxcam_videodev *vdev = ofh- vdev;
+ struct v4l2_pix_format *pix = &ofh- pix;
+ int rval = 0;
+
+ mutex_lock(&vdev- mutex);
+
+ if (vdev- vdev_sensor_config.sensor_isp)
+ rval = vidioc_int_s_crop(vdev- vdev_sensor, a);
+ else
+ rval = isp_s_crop(a, pix);
+
+ mutex_unlock(&vdev- mutex);
+
+ return rval;
+}
+
+static int vidioc_enum_framesizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *frms)
+{
+ struct omap34xxcam_fh *ofh = fh;
+ struct omap34xxcam_videodev *vdev = ofh- vdev;
+ u32 pixel_format;
+ int rval;
+
+ pixel_format = frms- pixel_format;
+ frms- pixel_format = V4L2_PIX_FMT_SGRBG10; /* We can accept any. */
+
+ mutex_lock(&vdev- mutex);
+ rval = vidioc_int_enum_framesizes(vdev- vdev_sensor, frms);
+ mutex_unlock(&vdev- mutex);
+
+ frms- pixel_format = pixel_format;
+
+ return rval;
+}
+
+static int vidioc_enum_frameintervals(struct file *file, void *fh,
+ struct v4l2_frmivalenum *frmi)
+{
+ struct omap34xxcam_fh *ofh = fh;
+ struct omap34xxcam_videodev *vdev = ofh- vdev;
+ u32 pixel_format;
+ int rval;
+
+ pixel_format = frmi- pixel_format;
+ frmi- pixel_format = V4L2_PIX_FMT_SGRBG10; /* We can accept any. */
+
+ mutex_lock(&vdev- mutex);
+ rval = vidioc_int_enum_frameintervals(vdev- vdev_sensor, frmi);
+ mutex_unlock(&vdev- mutex);
+
+ frmi- pixel_format = pixel_format;
+
+ return rval;
+}
+
+/**
+ * vidioc_default - private IOCTL handler
+ * @file: ptr. to system file structure
+ * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data)
+ * @cmd: ioctl cmd value
+ * @arg: ioctl arg value
+ *
+ * If the sensor being used is a "smart sensor", this request is returned to
+ * caller with -EINVAL err code. Otherwise if the control id is the private
+ * VIDIOC_PRIVATE_ISP_AEWB_REQ to update the analog gain or exposure,
+ * then this request is forwared directly to the sensor to incorporate the
+ * feedback. The request is then passed on to the ISP private IOCTL handler,
+ * isp_handle_private()
+ */
+static int vidioc_default(struct file *file, void *fh, int cmd, void *arg)
+{
+ struct omap34xxcam_fh *ofh = file- private_data;
+ struct omap34xxcam_videodev *vdev = ofh- vdev;
+ int rval;
+
+ if (vdev- vdev_sensor_config.sensor_isp) {
+ rval = -EINVAL;
+ } else {
+ switch (cmd) {
+ case VIDIOC_ENUM_FRAMESIZES:
+ rval = vidioc_enum_framesizes(file, fh, arg);
+ goto out;
+ case VIDIOC_ENUM_FRAMEINTERVALS:
+ rval = vidioc_enum_frameintervals(file, fh, arg);
+ goto out;
+ case VIDIOC_PRIVATE_ISP_AEWB_REQ:
+ {
+ /* Need to update sensor first */
+ struct isph3a_aewb_data *data;
+ struct v4l2_control vc;
+
+ data = (struct isph3a_aewb_data *) arg;
+ if (data- update & SET_EXPOSURE) {
+ vc.id = V4L2_CID_EXPOSURE;
+ vc.value = data- shutter;
+ mutex_lock(&vdev- mutex);
+ rval = vidioc_int_s_ctrl(vdev- vdev_sensor,
+ &vc);
+ mutex_unlock(&vdev- mutex);
+ if (rval)
+ goto out;
+ }
+ if (data- update & SET_ANALOG_GAIN) {
+ vc.id = V4L2_CID_GAIN;
+ vc.value = data- gain;
+ mutex_lock(&vdev- mutex);
+ rval = vidioc_int_s_ctrl(vdev- vdev_sensor,
+ &vc);
+ mutex_unlock(&vdev- mutex);
+ if (rval)
+ goto out;
+ }
+ }
+ break;
+ case VIDIOC_PRIVATE_ISP_AF_REQ: {
+ /* Need to update lens first */
+ struct isp_af_data *data;
+ struct v4l2_control vc;
+
+ if (!vdev- vdev_lens) {
+ rval = -EINVAL;
+ goto out;
+ }
+ data = (struct isp_af_data *) arg;
+ if (data- update & LENS_DESIRED_POSITION) {
+ vc.id = V4L2_CID_FOCUS_ABSOLUTE;
+ vc.value = data- desired_lens_direction;
+ mutex_lock(&vdev- mutex);
+ rval = vidioc_int_s_ctrl(vdev- vdev_lens, &vc);
+ mutex_unlock(&vdev- mutex);
+ if (rval)
+ goto out;
+ }
+ if (data- update & REQUEST_STATISTICS) {
+ vc.id = V4L2_CID_FOCUS_ABSOLUTE;
+ mutex_lock(&vdev- mutex);
+ rval = vidioc_int_g_ctrl(vdev- vdev_lens, &vc);
+ mutex_unlock(&vdev- mutex);
+ if (rval)
+ goto out;
+ data- xtrastats.lens_position = vc.value;
+ }
+ }
+ break;
+ }
+
+ mutex_lock(&vdev- mutex);
+ rval = isp_handle_private(cmd, arg);
+ mutex_unlock(&vdev- mutex);
+ }
+out:
+ return rval;
+}
+
+/*
+ *
+ * File operations.
+ *
+ */
+
+static long omap34xxcam_unlocked_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ return (long)video_ioctl2(file- f_dentry- d_inode, file, cmd, arg);
+}
+
+/**
+ * omap34xxcam_poll - file operations poll handler
+ * @file: ptr. to system file structure
+ * @wait: system poll table structure
+ *
+ */
+static unsigned int omap34xxcam_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct omap34xxcam_fh *fh = file- private_data;
+ struct omap34xxcam_videodev *vdev = fh- vdev;
+ struct videobuf_buffer *vb;
+
+ mutex_lock(&vdev- mutex);
+ if (vdev- streaming != file) {
+ mutex_unlock(&vdev- mutex);
+ return POLLERR;
+ }
+ mutex_unlock(&vdev- mutex);
+
+ mutex_lock(&fh- vbq.vb_lock);
+ if (list_empty(&fh- vbq.stream)) {
+ mutex_unlock(&fh- vbq.vb_lock);
+ return POLLERR;
+ }
+ vb = list_entry(fh- vbq.stream.next, struct videobuf_buffer, stream);
+ mutex_unlock(&fh- vbq.vb_lock);
+
+ poll_wait(file, &vb- done, wait);
+
+ if (vb- state == VIDEOBUF_DONE || vb- state == VIDEOBUF_ERROR)
+ return POLLIN | POLLRDNORM;
+
+ return 0;
+}
+
+/**
+ * omap34xxcam_mmap - file operations mmap handler
+ * @file: ptr. to system file structure
+ * @vma: system virt. mem. area structure
+ *
+ * Maps a virtual memory area via the video buffer API
+ */
+static int omap34xxcam_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct omap34xxcam_fh *fh = file- private_data;
+ return videobuf_mmap_mapper(&fh- vbq, vma);
+}
+
+/**
+ * omap34xxcam_open - file operations open handler
+ * @inode: ptr. to system inode structure
+ * @file: ptr. to system file structure
+ *
+ * Allocates and initializes the per-filehandle data (omap34xxcam_fh),
+ * enables the sensor, opens/initializes the ISP interface and the
+ * video buffer queue. Note that this function will allow multiple
+ * file handles to be open simultaneously, however only the first
+ * handle opened will initialize the ISP. It is the application
+ * responsibility to only use one handle for streaming and the others
+ * for control only.
+ * This function returns 0 upon success and -ENODEV upon error.
+ */
+static int omap34xxcam_open(struct inode *inode, struct file *file)
+{
+ struct omap34xxcam_videodev *vdev = NULL;
+ struct omap34xxcam_device *cam = omap34xxcam;
+ struct omap34xxcam_fh *fh;
+ struct v4l2_format format;
+ int i;
+
+ for (i = 0; i < OMAP34XXCAM_VIDEODEVS; i++) {
+ if (cam- vdevs[i].vfd
+ && cam- vdevs[i].vfd- minor == iminor(inode)) {
+ vdev = &cam- vdevs[i];
+ break;
+ }
+ }
+
+ if (!vdev || !vdev- vfd)
+ return -ENODEV;
+
+ fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+ if (fh == NULL)
+ return -ENOMEM;
+
+ mutex_lock(&vdev- mutex);
+ for (i = 0; i <= OMAP34XXCAM_SLAVE_FLASH; i++) {
+ if (vdev- slave[i] != v4l2_int_device_dummy()
+ && !try_module_get(vdev- slave[i]- module)) {
+ mutex_unlock(&vdev- mutex);
+ goto out_try_module_get;
+ }
+ }
+
+ if (atomic_inc_return(&vdev- users) == 1) {
+ isp_get();
+ if (omap34xxcam_slave_power_set(vdev, V4L2_POWER_ON,
+ OMAP34XXCAM_SLAVE_POWER_ALL))
+ goto out_slave_power_set_standby;
+ omap34xxcam_slave_power_set(
+ vdev, V4L2_POWER_STANDBY,
+ OMAP34XXCAM_SLAVE_POWER_SENSOR);
+ omap34xxcam_slave_power_suggest(
+ vdev, V4L2_POWER_STANDBY,
+ OMAP34XXCAM_SLAVE_POWER_LENS);
+ }
+
+ fh- vdev = vdev;
+
+ /* FIXME: Check that we have sensor now... */
+ if (vdev- vdev_sensor_config.sensor_isp)
+ vidioc_int_g_fmt_cap(vdev- vdev_sensor, &format);
+ else
+ isp_g_fmt_cap(&format.fmt.pix);
+
+ mutex_unlock(&vdev- mutex);
+ /* FIXME: how about fh- pix when there are more users? */
+ fh- pix = format.fmt.pix;
+
+ file- private_data = fh;
+
+ spin_lock_init(&fh- vbq_lock);
+
+ videobuf_queue_sg_init(&fh- vbq, &omap34xxcam_vbq_ops, NULL,
+ &fh- vbq_lock, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_NONE,
+ sizeof(struct videobuf_buffer), fh);
+
+ return 0;
+
+out_slave_power_set_standby:
+ omap34xxcam_slave_power_set(vdev, V4L2_POWER_OFF,
+ OMAP34XXCAM_SLAVE_POWER_ALL);
+ isp_put();
+ atomic_dec(&vdev- users);
+ mutex_unlock(&vdev- mutex);
+
+out_try_module_get:
+ for (i--; i = 0; i--)
+ if (vdev- slave[i] != v4l2_int_device_dummy())
+ module_put(vdev- slave[i]- module);
+
+ kfree(fh);
+
+ return -ENODEV;
+}
+
+/**
+ * omap34xxcam_release - file operations release handler
+ * @inode: ptr. to system inode structure
+ * @file: ptr. to system file structure
+ *
+ * Complement of omap34xxcam_open. This function will flush any scheduled
+ * work, disable the sensor, close the ISP interface, stop the
+ * video buffer queue from streaming and free the per-filehandle data
+ * (omap34xxcam_fh). Note that because multiple open file handles
+ * are allowed, this function will only close the ISP and disable the
+ * sensor when the last open file handle (by count) is closed.
+ * This function returns 0.
+ */
+static int omap34xxcam_release(struct inode *inode, struct file *file)
+{
+ struct omap34xxcam_fh *fh = file- private_data;
+ struct omap34xxcam_videodev *vdev = fh- vdev;
+ int i;
+
+ mutex_lock(&vdev- mutex);
+ if (vdev- streaming == file) {
+ isp_stop();
+ videobuf_streamoff(&fh- vbq);
+ omap34xxcam_slave_power_set(
+ vdev, V4L2_POWER_STANDBY,
+ OMAP34XXCAM_SLAVE_POWER_SENSOR);
+ omap34xxcam_slave_power_suggest(
+ vdev, V4L2_POWER_STANDBY,
+ OMAP34XXCAM_SLAVE_POWER_LENS);
+ vdev- streaming = NULL;
+ }
+
+ if (atomic_dec_return(&vdev- users) == 0) {
+ omap34xxcam_slave_power_set(vdev, V4L2_POWER_OFF,
+ OMAP34XXCAM_SLAVE_POWER_ALL);
+ isp_put();
+ }
+ mutex_unlock(&vdev- mutex);
+
+ file- private_data = NULL;
+
+ for (i = 0; i <= OMAP34XXCAM_SLAVE_FLASH; i++)
+ if (vdev- slave[i] != v4l2_int_device_dummy())
+ module_put(vdev- slave[i]- module);
+
+ kfree(fh);
+
+ return 0;
+}
+
+static struct file_operations omap34xxcam_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .unlocked_ioctl = omap34xxcam_unlocked_ioctl,
+ .poll = omap34xxcam_poll,
+ .mmap = omap34xxcam_mmap,
+ .open = omap34xxcam_open,
+ .release = omap34xxcam_release,
+};
+
+static void omap34xxcam_vfd_name_update(struct omap34xxcam_videodev *vdev)
+{
+ struct video_device *vfd = vdev- vfd;
+ int i;
+
+ strlcpy(vfd- name, CAM_SHORT_NAME, sizeof(vfd- name));
+ for (i = 0; i <= OMAP34XXCAM_SLAVE_FLASH; i++) {
+ strlcat(vfd- name, "/", sizeof(vfd- name));
+ if (vdev- slave[i] == v4l2_int_device_dummy())
+ continue;
+ strlcat(vfd- name, vdev- slave[i]- name, sizeof(vfd- name));
+ }
+ dev_info(vdev- cam- dev, "video%d is now %s
", vfd- minor, vfd- name);
+}
+
+/**
+ * omap34xxcam_device_unregister - V4L2 detach handler
+ * @s: ptr. to standard V4L2 device information structure
+ *
+ * Detach sensor and unregister and release the video device.
+ */
+static void omap34xxcam_device_unregister(struct v4l2_int_device *s)
+{
+ struct omap34xxcam_videodev *vdev = s- u.slave- master- priv;
+ struct omap34xxcam_hw_config hwc;
+
+ BUG_ON(vidioc_int_g_priv(s, &hwc) < 0);
+
+ mutex_lock(&vdev- mutex);
+
+ if (vdev- slave[hwc.dev_type] != v4l2_int_device_dummy()) {
+ vdev- slave[hwc.dev_type] = v4l2_int_device_dummy();
+ vdev- slaves--;
+ omap34xxcam_vfd_name_update(vdev);
+ }
+
+ if (vdev- slaves == 0 && vdev- vfd) {
+ if (vdev- vfd- minor == -1) {
+ /*
+ * The device was never registered, so release the
+ * video_device struct directly.
+ */
+ video_device_release(vdev- vfd);
+ } else {
+ /*
+ * The unregister function will release the
+ * video_device struct as well as
+ * unregistering it.
+ */
+ video_unregister_device(vdev- vfd);
+ }
+ vdev- vfd = NULL;
+ }
+
+ mutex_unlock(&vdev- mutex);
+}
+
+static const struct v4l2_ioctl_ops omap34xxcam_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_querymenu = vidioc_querymenu,
+ .vidioc_g_ext_ctrls = vidioc_g_ext_ctrls,
+ .vidioc_s_ext_ctrls = vidioc_s_ext_ctrls,
+ .vidioc_g_parm = vidioc_g_parm,
+ .vidioc_s_parm = vidioc_s_parm,
+ .vidioc_cropcap = vidioc_cropcap,
+ .vidioc_g_crop = vidioc_g_crop,
+ .vidioc_s_crop = vidioc_s_crop,
+ .vidioc_default = vidioc_default,
+};
+
+/**
+ * omap34xxcam_device_register - V4L2 attach handler
+ * @s: ptr. to standard V4L2 device information structure
+ *
+ * Allocates and initializes the V4L2 video_device structure, initializes
+ * the sensor, and finally
+ registers the device with V4L2 based on the
+ * video_device structure.
+ *
+ * Returns 0 on success, otherwise an appropriate error code on
+ * failure.
+ */
+static int omap34xxcam_device_register(struct v4l2_int_device *s)
+{
+ struct omap34xxcam_videodev *vdev = s- u.slave- master- priv;
+ struct omap34xxcam_device *cam = vdev- cam;
+ struct omap34xxcam_hw_config hwc;
+ struct video_device *vfd;
+ int rval;
+
+ /* We need to check rval just once. The place is here. */
+ if (vidioc_int_g_priv(s, &hwc))
+ return -ENODEV;
+
+ if (vdev- index != hwc.dev_index)
+ return -ENODEV;
+
+ if (hwc.dev_type < 0 || hwc.dev_type OMAP34XXCAM_SLAVE_FLASH)
+ return -EINVAL;
+
+ if (vdev- slave[hwc.dev_type] != v4l2_int_device_dummy())
+ return -EBUSY;
+
+ mutex_lock(&vdev- mutex);
+ if (atomic_read(&vdev- users)) {
+ dev_err(cam- dev, "were open (%d), cant register
",
+ atomic_read(&vdev- users));
+ mutex_unlock(&vdev- mutex);
+ return -EBUSY;
+ }
+
+ vdev- slaves++;
+ vdev- slave[hwc.dev_type] = s;
+ vdev- slave_config[hwc.dev_type] = hwc;
+
+ if (hwc.dev_type == OMAP34XXCAM_SLAVE_SENSOR)
+ isp_get();
+ rval = omap34xxcam_slave_power_set(vdev, V4L2_POWER_ON,
+ 1 << hwc.dev_type);
+ if (!rval && hwc.dev_type == OMAP34XXCAM_SLAVE_SENSOR) {
+ struct v4l2_format format;
+ struct v4l2_streamparm a;
+
+ format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ rval = vidioc_int_g_fmt_cap(vdev- vdev_sensor, &format);
+
+ a.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ rval |= vidioc_int_g_parm(vdev- vdev_sensor, &a);
+ if (rval)
+ rval = -EBUSY;
+
+ vdev- want_pix = format.fmt.pix;
+ vdev- want_timeperframe = a.parm.capture.timeperframe;
+ }
+ omap34xxcam_slave_power_set(vdev, V4L2_POWER_OFF, 1 << hwc.dev_type);
+ if (hwc.dev_type == OMAP34XXCAM_SLAVE_SENSOR)
+ isp_put();
+
+ if (rval)
+ goto err;
+
+ /* Are we the first slave? */
+ if (vdev- slaves == 1) {
+ /* initialize the video_device struct */
+ vdev- vfd = video_device_alloc();
+ vfd = vdev- vfd;
+ if (!vfd) {
+ dev_err(cam- dev,
+ "could not allocate video device struct
");
+ return -ENOMEM;
+ }
+ vfd- release = video_device_release;
+ vfd- minor = -1;
+ vfd- fops = &omap34xxcam_fops;
+ vfd- ioctl_ops = &omap34xxcam_ioctl_ops;
+ video_set_drvdata(vfd, vdev);
+
+ if (video_register_device(vfd, VFL_TYPE_GRABBER,
+ hwc.dev_minor) < 0) {
+ dev_err(cam- dev,
+ "could not register V4L device
");
+ vfd- minor = -1;
+ rval = -EBUSY;
+ goto err;
+ }
+ } else {
+ vfd = vdev- vfd;
+ }
+
+ omap34xxcam_vfd_name_update(vdev);
+
+ mutex_unlock(&vdev- mutex);
+
+ return 0;
+
+err:
+ if (s == vdev- slave[hwc.dev_type]) {
+ vdev- slave[hwc.dev_type] = v4l2_int_device_dummy();
+ vdev- slaves--;
+ }
+
+ mutex_unlock(&vdev- mutex);
+ omap34xxcam_device_unregister(s);
+
+ return rval;
+}
+
+static struct v4l2_int_master omap34xxcam_master = {
+ .attach = omap34xxcam_device_register,
+ .detach = omap34xxcam_device_unregister,
+};
+
+/*
+ *
+ * Driver Suspend/Resume
+ *
+ */
+
+#ifdef CONFIG_PM
+/**
+ * omap34xxcam_suspend - platform driver PM suspend handler
+ * @pdev: ptr. to platform level device information structure
+ * @state: power state
+ *
+ * If applicable, stop capture and disable sensor.
+ *
+ * Returns 0 always
+ */
+static int omap34xxcam_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct omap34xxcam_videodev *vdev = platform_get_drvdata(pdev);
+
+ if (atomic_read(&vdev- users) == 0)
+ return 0;
+
+ if (vdev- streaming) {
+ isp_stop();
+ omap34xxcam_slave_power_set(vdev, V4L2_POWER_OFF,
+ OMAP34XXCAM_SLAVE_POWER_ALL);
+ }
+
+ return 0;
+}
+
+/**
+ * omap34xxcam_resume - platform driver PM resume handler
+ * @pdev: ptr. to platform level device information structure
+ *
+ * If applicable, resume capture and enable sensor.
+ *
+ * Returns 0 always
+ */
+static int omap34xxcam_resume(struct platform_device *pdev)
+{
+ struct omap34xxcam_videodev *vdev = platform_get_drvdata(pdev);
+
+ if (atomic_read(&vdev- users) == 0)
+ return 0;
+
+ if (vdev- streaming) {
+ omap34xxcam_slave_power_set(vdev, V4L2_POWER_ON,
+ |