diff options
Diffstat (limited to 'recipes/linux/linux-omap-2.6.29/isp/omap3camera/0009-omap34xxcam-Add-camera-driver.patch')
-rw-r--r-- | recipes/linux/linux-omap-2.6.29/isp/omap3camera/0009-omap34xxcam-Add-camera-driver.patch | 2249 |
1 files changed, 2249 insertions, 0 deletions
diff --git a/recipes/linux/linux-omap-2.6.29/isp/omap3camera/0009-omap34xxcam-Add-camera-driver.patch b/recipes/linux/linux-omap-2.6.29/isp/omap3camera/0009-omap34xxcam-Add-camera-driver.patch new file mode 100644 index 0000000000..22074be148 --- /dev/null +++ b/recipes/linux/linux-omap-2.6.29/isp/omap3camera/0009-omap34xxcam-Add-camera-driver.patch @@ -0,0 +1,2249 @@ +From 0edf5a50dc0164db5bc71b1a5d1aa8bb1838262c Mon Sep 17 00:00:00 2001 +From: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> +Date: Tue, 10 Mar 2009 10:49:03 +0200 +Subject: [PATCH] omap34xxcam: Add camera driver + +This is the camera driver for the OMAP 3 camera ISP and v4l2-int-device +sensors, lenses and (led) flashes. There are a few connections to OMAP +3 left but after those have been broken this is hardware independent. +Namely, the OMAP 3 ISP must offer a standard interface through +v4l2_subdev (or v4l2-int-device) first. + +This driver has originated from the omap24xxcam camera driver written +specifically for OMAP 2. + +TODO: + +- Convert to use v4l2_subdev instead of v4l2-int-device. + +Signed-off-by: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> +--- + drivers/media/video/Kconfig | 9 + + drivers/media/video/Makefile | 2 + + drivers/media/video/omap34xxcam.c | 1966 +++++++++++++++++++++++++++++++++++++ + drivers/media/video/omap34xxcam.h | 207 ++++ + 4 files changed, 2184 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 19cf3b8..3cdb5a4 100644 +--- a/drivers/media/video/Kconfig ++++ b/drivers/media/video/Kconfig +@@ -711,6 +711,15 @@ 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 ++ select OMAP_IOMMU ++ 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 e654270..74a684e 100644 +--- a/drivers/media/video/Makefile ++++ b/drivers/media/video/Makefile +@@ -108,6 +108,8 @@ obj-$(CONFIG_VIDEO_OV7670) += ov7670.o + + obj-y += isp/ + ++obj-$(CONFIG_VIDEO_OMAP3) += omap34xxcam.o ++ + obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o + + obj-$(CONFIG_USB_DABUSB) += dabusb.o +diff --git a/drivers/media/video/omap34xxcam.c b/drivers/media/video/omap34xxcam.c +new file mode 100644 +index 0000000..00fdbf2 +--- /dev/null ++++ b/drivers/media/video/omap34xxcam.c +@@ -0,0 +1,1966 @@ ++/* ++ * omap34xxcam.c ++ * ++ * Copyright (C) 2006--2009 Nokia Corporation ++ * Copyright (C) 2007--2009 Texas Instruments ++ * ++ * Contact: Sakari Ailus <sakari.ailus@nokia.com> ++ * Tuukka Toivonen <tuukka.o.toivonen@nokia.com> ++ * ++ * Originally based on the OMAP 2 camera driver. ++ * ++ * Written by Sakari Ailus <sakari.ailus@nokia.com> ++ * Tuukka Toivonen <tuukka.o.toivonen@nokia.com> ++ * Sergio Aguirre <saaguirre@ti.com> ++ * 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; ++ ++/* ++ * ++ * 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)); ++ ++#ifdef OMAP34XXCAM_POWEROFF_DELAY ++ vdev->power_state_wish = -1; ++#endif ++ ++ 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; ++} ++ ++#ifdef OMAP34XXCAM_POWEROFF_DELAY ++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); ++} ++#else /* OMAP34XXCAM_POWEROFF_DELAY */ ++#define omap34xxcam_slave_power_suggest(a, b, c) do {} while (0) ++#endif /* 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. ++ */ ++void omap34xxcam_vbq_complete(struct videobuf_buffer *vb, void *priv) ++{ ++ struct omap34xxcam_fh *fh = priv; ++ ++ do_gettimeofday(&vb->ts); ++ vb->field_count = atomic_add_return(2, &fh->field_count); ++ ++ wake_up(&vb->done); ++} ++ ++/** ++ * 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; ++ struct omap34xxcam_videodev *vdev = fh->vdev; ++ ++ if (*cnt <= 0) ++ *cnt = VIDEO_MAX_FRAME; /* supply a default number of buffers */ ++ ++ if (*cnt > VIDEO_MAX_FRAME) ++ *cnt = VIDEO_MAX_FRAME; ++ ++ *size = vdev->pix.sizeimage; ++ ++ while (*size * *cnt > fh->vdev->vdev_sensor_config.capture_mem) ++ (*cnt)--; ++ ++ return isp_vbq_setup(vbq, cnt, size); ++} ++ ++/** ++ * 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; ++ struct omap34xxcam_videodev *vdev = fh->vdev; ++ int err = 0; ++ ++ /* ++ * Accessing pix here is okay since it's constant while ++ * streaming is on (and we only get called then). ++ */ ++ if (vb->baddr) { ++ /* This is a userspace buffer. */ ++ if (vdev->pix.sizeimage > vb->bsize) ++ /* The buffer isn't big enough. */ ++ return -EINVAL; ++ } else { ++ if (vb->state != VIDEOBUF_NEEDS_INIT ++ && vdev->pix.sizeimage > vb->bsize) ++ /* ++ * We have a kernel bounce buffer that has ++ * already been allocated. ++ */ ++ omap34xxcam_vbq_release(vbq, vb); ++ } ++ ++ vb->size = vdev->pix.bytesperline * vdev->pix.height; ++ vb->width = vdev->pix.width; ++ vb->height = vdev->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; ++ ++ vb->state = VIDEOBUF_ACTIVE; ++ ++ isp_buf_queue(vb, omap34xxcam_vbq_complete, (void *)fh); ++} ++ ++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; ++ if (vdev->vdev_sensor != v4l2_int_device_dummy()) ++ 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 == v4l2_int_device_dummy()) ++ return -EINVAL; ++ ++ 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; ++ ++ if (vdev->vdev_sensor == v4l2_int_device_dummy()) ++ return -EINVAL; ++ ++ mutex_lock(&vdev->mutex); ++ f->fmt.pix = vdev->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 fmtd_index; ++ int rval; ++ struct v4l2_pix_format best_pix_out; ++ ++ if (best_ival->numerator == 0 ++ || best_ival->denominator == 0) ++ *best_ival = vdev->vdev_sensor_config.ival_default; ++ ++ 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; ++ ++ for (fmtd_index = 0; ; fmtd_index++) { ++ int size_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) ++ break; ++ dev_info(&vdev->vfd->dev, "trying fmt %8.8x (%d)\n", ++ fmtd.pixelformat, fmtd_index); ++ /* ++ * 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 = fmtd.pixelformat; ++ ++ rval = vidioc_int_enum_framesizes(vdev->vdev_sensor, ++ &frms); ++ if (rval) ++ 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; ++ /* Don't 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; ++ ++ dev_info(&vdev->vfd->dev, "this w %d\th %d\tfmt %8.8x\t" ++ "-> w %d\th %d\t fmt %8.8x" ++ "\twanted w %d\th %d\t fmt %8.8x\n", ++ pix_tmp_in.width, pix_tmp_in.height, ++ pix_tmp_in.pixelformat, ++ pix_tmp_out.width, pix_tmp_out.height, ++ pix_tmp_out.pixelformat, ++ wanted_pix_out->width, wanted_pix_out->height, ++ wanted_pix_out->pixelformat); ++ ++#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)) ++ ++ /* ++ * Don't 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)) { ++ dev_info(&vdev->vfd->dev, "size diff bigger: " ++ "w %d\th %d\tw %d\th %d\n", ++ pix_tmp_out.width, pix_tmp_out.height, ++ best_pix_out.width, ++ best_pix_out.height); ++ continue; ++ } ++ ++ /* ++ * There's 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; ++ dev_info(&vdev->vfd->dev, "renegotiate: " ++ "w %d\th %d\tw %d\th %d\n", ++ pix_tmp_out.width, pix_tmp_out.height, ++ best_pix_out.width, ++ best_pix_out.height); ++ } ++ ++ 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) ++ break; ++ ++ dev_info(&vdev->vfd->dev, "fps %d\n", ++ frmi.discrete.denominator ++ / frmi.discrete.numerator); ++ ++ 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)) { ++ dev_info(&vdev->vfd->dev, "closer fps: " ++ "fps %d\t fps %d\n", ++ FPS_ABS_DIFF(fps, ++ frmi.discrete), ++ FPS_ABS_DIFF(fps, *best_ival)); ++ goto do_it_now; ++ } ++ ++ /* ++ * Select bigger resolution if it's available ++ * at same fps. ++ */ ++ if (frmi.width + frmi.height ++ > best_pix_in->width + best_pix_in->height ++ && FPS_ABS_DIFF(fps, frmi.discrete) ++ <= FPS_ABS_DIFF(fps, *best_ival)) { ++ dev_info(&vdev->vfd->dev, "bigger res, " ++ "same fps: " ++ "w %d\th %d\tw %d\th %d\n", ++ frmi.width, frmi.height, ++ best_pix_in->width, ++ best_pix_in->height); ++ goto do_it_now; ++ } ++ ++ dev_info(&vdev->vfd->dev, "falling through\n"); ++ ++ 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; ++ ++ dev_info(&vdev->vfd->dev, ++ "best_pix_in: w %d\th %d\tfmt %8.8x" ++ "\tival %d/%d\n", ++ best_pix_in->width, ++ best_pix_in->height, ++ best_pix_in->pixelformat, ++ best_ival->numerator, ++ best_ival->denominator); ++ } ++ } ++ } ++ ++ if (best_ival->denominator == 0) ++ return -EINVAL; ++ ++ *wanted_pix_out = best_pix_out; ++ ++ dev_info(&vdev->vfd->dev, "w %d, h %d, fmt %8.8x -> w %d, h %d\n", ++ best_pix_in->width, best_pix_in->height, ++ best_pix_in->pixelformat, ++ 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; ++ ++ rval = try_pix_parm(vdev, best_pix, pix, best_ival); ++ if (rval) ++ return rval; ++ ++ 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; ++ ++ if (vdev->vdev_sensor == v4l2_int_device_dummy()) ++ return -EINVAL; ++ ++ 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); ++ if (!rval) ++ vdev->pix = f->fmt.pix; ++ ++out: ++ mutex_unlock(&vdev->mutex); ++ ++ 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; ++ ++ if (vdev->vdev_sensor == v4l2_int_device_dummy()) ++ return -EINVAL; ++ ++ mutex_lock(&vdev->mutex); ++ ++ timeperframe = vdev->want_timeperframe; ++ ++ rval = try_pix_parm(vdev, &pix_tmp, &f->fmt.pix, &timeperframe); ++ ++ 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; ++ ++ if (vdev->vdev_sensor == v4l2_int_device_dummy()) ++ return -EINVAL; ++ ++ mutex_lock(&vdev->mutex); ++ if (vdev->streaming) { ++ mutex_unlock(&vdev->mutex); ++ return -EBUSY; ++ } ++ ++ rval = videobuf_reqbufs(&ofh->vbq, b); ++ ++ mutex_unlock(&vdev->mutex); ++ ++ /* ++ * 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; ++ int rval; ++ ++videobuf_dqbuf_again: ++ rval = videobuf_dqbuf(&ofh->vbq, b, file->f_flags & O_NONBLOCK); ++ ++ /* ++ * This is a hack. We don't want to show -EIO to the user ++ * space. Requeue the buffer and try again if we're not doing ++ * this in non-blocking mode. ++ */ ++ if (rval == -EIO) { ++ videobuf_qbuf(&ofh->vbq, b); ++ if (!(file->f_flags & O_NONBLOCK)) ++ goto videobuf_dqbuf_again; ++ /* ++ * We don't have a videobuf_buffer now --- maybe next ++ * time... ++ */ ++ rval = -EAGAIN; ++ } ++ ++ return rval; ++} ++ ++/** ++ * 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; ++ int rval; ++ ++ if (vdev->vdev_sensor == v4l2_int_device_dummy()) ++ return -EINVAL; ++ ++ mutex_lock(&vdev->mutex); ++ if (vdev->streaming) { ++ rval = -EBUSY; ++ goto out; ++ } ++ ++ rval = omap34xxcam_slave_power_set(vdev, V4L2_POWER_ON, ++ OMAP34XXCAM_SLAVE_POWER_SENSOR_LENS); ++ if (rval) { ++ dev_dbg(&vdev->vfd->dev, ++ "omap34xxcam_slave_power_set failed\n"); ++ goto out; ++ } ++ ++ rval = videobuf_streamon(&ofh->vbq); ++ if (rval) ++ omap34xxcam_slave_power_set( ++ vdev, V4L2_POWER_OFF, ++ OMAP34XXCAM_SLAVE_POWER_SENSOR_LENS); ++ else ++ vdev->streaming = file; ++ ++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; ++ ++ if (vdev->vdev_sensor == v4l2_int_device_dummy()) ++ 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; ++ ++ if (vdev->vdev_sensor == v4l2_int_device_dummy()) ++ return -EINVAL; ++ ++ mutex_lock(&vdev->mutex); ++ ++ rval = vidioc_int_cropcap(vdev->vdev_sensor, a); ++ ++ if (rval && !vdev->vdev_sensor_config.sensor_isp) { ++ struct v4l2_format f; ++ ++ /* 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; ++ ++ if (vdev->vdev_sensor == v4l2_int_device_dummy()) ++ return -EINVAL; ++ ++ 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; ++ int rval = 0; ++ ++ if (vdev->vdev_sensor == v4l2_int_device_dummy()) ++ return -EINVAL; ++ ++ 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, &vdev->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; ++ ++ mutex_lock(&vdev->mutex); ++ ++ if (vdev->vdev_sensor_config.sensor_isp) { ++ rval = vidioc_int_enum_framesizes(vdev->vdev_sensor, frms); ++ } else { ++ pixel_format = frms->pixel_format; ++ frms->pixel_format = -1; /* ISP does format conversion */ ++ rval = vidioc_int_enum_framesizes(vdev->vdev_sensor, frms); ++ frms->pixel_format = pixel_format; ++ } ++ ++ mutex_unlock(&vdev->mutex); ++ 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; ++ ++ mutex_lock(&vdev->mutex); ++ ++ if (vdev->vdev_sensor_config.sensor_isp) { ++ rval = vidioc_int_enum_frameintervals(vdev->vdev_sensor, frmi); ++ } else { ++ pixel_format = frmi->pixel_format; ++ frmi->pixel_format = -1; /* ISP does format conversion */ ++ rval = vidioc_int_enum_frameintervals(vdev->vdev_sensor, frmi); ++ frmi->pixel_format = pixel_format; ++ } ++ ++ mutex_unlock(&vdev->mutex); ++ 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 long 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_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) { ++ dev_info(&vdev->vfd->dev, "using " ++ "VIDIOC_PRIVATE_ISP_AEWB_REQ to set " ++ "exposure is deprecated!\n"); ++ 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) { ++ dev_info(&vdev->vfd->dev, "using " ++ "VIDIOC_PRIVATE_ISP_AEWB_REQ to set " ++ "gain is deprecated!\n"); ++ 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) { ++ dev_info(&vdev->vfd->dev, "using " ++ "VIDIOC_PRIVATE_ISP_AF_REQ to set " ++ "lens position is deprecated!\n"); ++ 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; ++ } ++ } ++ break; ++ } ++ ++ mutex_lock(&vdev->mutex); ++ rval = isp_handle_private(cmd, arg); ++ mutex_unlock(&vdev->mutex); ++ } ++out: ++ return rval; ++} ++ ++/* ++ * ++ * File operations. ++ * ++ */ ++ ++/** ++ * 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 file *file) ++{ ++ int rval = 0; ++ 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(file->f_dentry->d_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); ++ dev_err(&vdev->vfd->dev, "can't try_module_get %s\n", ++ vdev->slave[i]->name); ++ rval = -ENODEV; ++ goto out_try_module_get; ++ } ++ } ++ ++ if (atomic_inc_return(&vdev->users) == 1) { ++ rval = isp_get(); ++ if (rval < 0) { ++ dev_err(&vdev->vfd->dev, "can't get isp\n"); ++ goto out_isp_get; ++ } ++ if (omap34xxcam_slave_power_set(vdev, V4L2_POWER_ON, ++ OMAP34XXCAM_SLAVE_POWER_ALL)) { ++ dev_err(&vdev->vfd->dev, "can't power up slaves\n"); ++ rval = -EBUSY; ++ 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; ++ ++ if (!vdev->pix.width ++ && vdev->vdev_sensor != v4l2_int_device_dummy()) { ++ memset(&format, 0, sizeof(format)); ++ if (vidioc_int_g_fmt_cap(vdev->vdev_sensor, &format)) { ++ dev_err(&vdev->vfd->dev, ++ "can't get current pix from sensor!\n"); ++ goto out_vidioc_int_g_fmt_cap; ++ } ++ if (!vdev->vdev_sensor_config.sensor_isp) { ++ struct v4l2_pix_format pix = format.fmt.pix; ++ if (isp_s_fmt_cap(&pix, &format.fmt.pix)) { ++ dev_err(&vdev->vfd->dev, ++ "isp doesn't like the sensor!\n"); ++ goto out_isp_s_fmt_cap; ++ } ++ } ++ vdev->pix = format.fmt.pix; ++ } ++ ++ mutex_unlock(&vdev->mutex); ++ ++ 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_isp_s_fmt_cap: ++out_vidioc_int_g_fmt_cap: ++ omap34xxcam_slave_power_set(vdev, V4L2_POWER_OFF, ++ OMAP34XXCAM_SLAVE_POWER_ALL); ++out_slave_power_set_standby: ++ isp_put(); ++ ++out_isp_get: ++ 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 rval; ++} ++ ++/** ++ * 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 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 v4l2_file_operations omap34xxcam_fops = { ++ .owner = THIS_MODULE, ++ .unlocked_ioctl = video_ioctl2, ++ .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->vfd->dev, "video%d is now %s\n", vfd->num, 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_enum_framesizes = vidioc_enum_framesizes, ++ .vidioc_enum_frameintervals = vidioc_enum_frameintervals, ++ .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_hw_config hwc; ++ 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)) { ++ printk(KERN_ERR "%s: we're open (%d), can't register\n", ++ __func__, 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) { ++ rval = isp_get(); ++ if (rval < 0) { ++ printk(KERN_ERR "%s: can't get ISP, " ++ "sensor init failed\n", __func__); ++ goto err; ++ } ++ } ++ rval = omap34xxcam_slave_power_set(vdev, V4L2_POWER_ON, ++ 1 << hwc.dev_type); ++ if (rval) ++ goto err_omap34xxcam_slave_power_set; ++ if (hwc.dev_type == OMAP34XXCAM_SLAVE_SENSOR) { ++ struct v4l2_format format; ++ ++ format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ rval = vidioc_int_g_fmt_cap(vdev->vdev_sensor, &format); ++ if (rval) ++ rval = -EBUSY; ++ ++ vdev->want_pix = format.fmt.pix; ++ } ++ 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(); ++ if (!vdev->vfd) { ++ printk(KERN_ERR "%s: could not allocate " ++ "video device struct\n", __func__); ++ rval = -ENOMEM; ++ goto err; ++ } ++ vdev->vfd->release = video_device_release; ++ vdev->vfd->minor = -1; ++ vdev->vfd->fops = &omap34xxcam_fops; ++ vdev->vfd->ioctl_ops = &omap34xxcam_ioctl_ops; ++ video_set_drvdata(vdev->vfd, vdev); ++ ++ if (video_register_device(vdev->vfd, VFL_TYPE_GRABBER, ++ hwc.dev_minor) < 0) { ++ printk(KERN_ERR "%s: could not register V4L device\n", ++ __func__); ++ vdev->vfd->minor = -1; ++ rval = -EBUSY; ++ goto err; ++ } ++ } ++ ++ omap34xxcam_vfd_name_update(vdev); ++ ++ mutex_unlock(&vdev->mutex); ++ ++ return 0; ++ ++err_omap34xxcam_slave_power_set: ++ if (hwc.dev_type == OMAP34XXCAM_SLAVE_SENSOR) ++ isp_put(); ++ ++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, ++}; ++ ++/* ++ * ++ * Module initialisation and deinitialisation ++ * ++ */ ++ ++static void omap34xxcam_exit(void) ++{ ++ struct omap34xxcam_device *cam = omap34xxcam; ++ int i; ++ ++ if (!cam) ++ return; ++ ++ for (i = 0; i < OMAP34XXCAM_VIDEODEVS; i++) { ++ if (cam->vdevs[i].cam == NULL) ++ continue; ++ ++ v4l2_int_device_unregister(&cam->vdevs[i].master); ++ cam->vdevs[i].cam = NULL; ++ } ++ ++ omap34xxcam = NULL; ++ ++ kfree(cam); ++} ++ ++static int __init omap34xxcam_init(void) ++{ ++ struct omap34xxcam_device *cam; ++ int i; ++ ++ cam = kzalloc(sizeof(*cam), GFP_KERNEL); ++ if (!cam) { ++ printk(KERN_ERR "%s: could not allocate memory\n", __func__); ++ return -ENOMEM; ++ } ++ ++ omap34xxcam = cam; ++ ++ for (i = 0; i < OMAP34XXCAM_VIDEODEVS; i++) { ++ struct omap34xxcam_videodev *vdev = &cam->vdevs[i]; ++ struct v4l2_int_device *m = &vdev->master; ++ ++ m->module = THIS_MODULE; ++ strlcpy(m->name, CAM_NAME, sizeof(m->name)); ++ m->type = v4l2_int_type_master; ++ m->u.master = &omap34xxcam_master; ++ m->priv = vdev; ++ ++ mutex_init(&vdev->mutex); ++ vdev->index = i; ++ vdev->cam = cam; ++ vdev->vdev_sensor = ++ vdev->vdev_lens = ++ vdev->vdev_flash = v4l2_int_device_dummy(); ++#ifdef OMAP34XXCAM_POWEROFF_DELAY ++ setup_timer(&vdev->poweroff_timer, ++ omap34xxcam_slave_power_timer, (unsigned long)vdev); ++ INIT_WORK(&vdev->poweroff_work, omap34xxcam_slave_power_work); ++#endif /* OMAP34XXCAM_POWEROFF_DELAY */ ++ ++ if (v4l2_int_device_register(m)) ++ goto err; ++ } ++ ++ return 0; ++ ++err: ++ omap34xxcam_exit(); ++ return -ENODEV; ++} ++ ++MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>"); ++MODULE_DESCRIPTION("OMAP34xx Video for Linux camera driver"); ++MODULE_LICENSE("GPL"); ++ ++late_initcall(omap34xxcam_init); ++module_exit(omap34xxcam_exit); +diff --git a/drivers/media/video/omap34xxcam.h b/drivers/media/video/omap34xxcam.h +new file mode 100644 +index 0000000..9859d15 +--- /dev/null ++++ b/drivers/media/video/omap34xxcam.h +@@ -0,0 +1,207 @@ ++/* ++ * omap34xxcam.h ++ * ++ * Copyright (C) 2006--2009 Nokia Corporation ++ * Copyright (C) 2007--2009 Texas Instruments ++ * ++ * Contact: Sakari Ailus <sakari.ailus@nokia.com> ++ * Tuukka Toivonen <tuukka.o.toivonen@nokia.com> ++ * ++ * Originally based on the OMAP 2 camera driver. ++ * ++ * Written by Sakari Ailus <sakari.ailus@nokia.com> ++ * Tuukka Toivonen <tuukka.o.toivonen@nokia.com> ++ * Sergio Aguirre <saaguirre@ti.com> ++ * 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 ++ * ++ */ ++ ++#ifndef OMAP34XXCAM_H ++#define OMAP34XXCAM_H ++ ++#include <media/v4l2-int-device.h> ++#include "isp/isp.h" ++ ++#define CAM_NAME "omap34xxcam" ++#define CAM_SHORT_NAME "omap3" ++ ++#define OMAP_ISP_AF (1 << 4) ++#define OMAP_ISP_HIST (1 << 5) ++#define OMAP34XXCAM_XCLK_NONE -1 ++#define OMAP34XXCAM_XCLK_A 0 ++#define OMAP34XXCAM_XCLK_B 1 ++ ++#define OMAP34XXCAM_SLAVE_SENSOR 0 ++#define OMAP34XXCAM_SLAVE_LENS 1 ++#define OMAP34XXCAM_SLAVE_FLASH 2 /* This is the last slave! */ ++ ++/* mask for omap34xxcam_slave_power_set */ ++#define OMAP34XXCAM_SLAVE_POWER_SENSOR (1 << OMAP34XXCAM_SLAVE_SENSOR) ++#define OMAP34XXCAM_SLAVE_POWER_LENS (1 << OMAP34XXCAM_SLAVE_LENS) ++#define OMAP34XXCAM_SLAVE_POWER_SENSOR_LENS \ ++ (OMAP34XXCAM_SLAVE_POWER_SENSOR | OMAP34XXCAM_SLAVE_POWER_LENS) ++#define OMAP34XXCAM_SLAVE_POWER_FLASH (1 << OMAP34XXCAM_SLAVE_FLASH) ++#define OMAP34XXCAM_SLAVE_POWER_ALL -1 ++ ++#define OMAP34XXCAM_VIDEODEVS 4 ++ ++/* #define OMAP34XXCAM_POWEROFF_DELAY (2 * HZ) */ ++ ++struct omap34xxcam_device; ++struct omap34xxcam_videodev; ++ ++struct omap34xxcam_sensor_config { ++ int xclk; ++ int sensor_isp; ++ u32 capture_mem; ++ struct v4l2_fract ival_default; ++}; ++ ++struct omap34xxcam_lens_config { ++}; ++ ++struct omap34xxcam_flash_config { ++}; ++ ++/** ++ * struct omap34xxcam_hw_config - struct for vidioc_int_g_priv ioctl ++ * @xclk: OMAP34XXCAM_XCLK_A or OMAP34XXCAM_XCLK_B ++ * @sensor_isp: Is sensor smart/SOC or raw ++ * @s_pix_sparm: Access function to set pix and sparm. ++ * Pix will override sparm ++ */ ++struct omap34xxcam_hw_config { ++ int dev_index; /* Index in omap34xxcam_sensors */ ++ int dev_minor; /* Video device minor number */ ++ int dev_type; /* OMAP34XXCAM_SLAVE_* */ ++ union { ++ struct omap34xxcam_sensor_config sensor; ++ struct omap34xxcam_lens_config lens; ++ struct omap34xxcam_flash_config flash; ++ } u; ++}; ++ ++/** ++ * struct omap34xxcam_videodev - per /dev/video* structure ++ * @mutex: serialises access to this structure ++ * @cam: pointer to cam hw structure ++ * @master: we are v4l2_int_device master ++ * @sensor: sensor device ++ * @lens: lens device ++ * @flash: flash device ++ * @slaves: how many slaves we have at the moment ++ * @vfd: our video device ++ * @capture_mem: maximum kernel-allocated capture memory ++ * @if_u: sensor interface stuff ++ * @index: index of this structure in cam->vdevs ++ * @users: how many users we have ++ * @power_state: Current power state ++ * @power_state_wish: New power state when poweroff_timer expires ++ * @power_state_mask: Bitmask of devices to set the new power state ++ * @poweroff_timer: Timer for dispatching poweroff_work ++ * @poweroff_work: Work for slave power state change ++ * @sensor_config: ISP-speicific sensor configuration ++ * @lens_config: ISP-speicific lens configuration ++ * @flash_config: ISP-speicific flash configuration ++ * @want_timeperframe: Desired timeperframe ++ * @want_pix: Desired pix ++ * @pix: Current pix ++ * @streaming: streaming file handle, if streaming is enabled ++ */ ++struct omap34xxcam_videodev { ++ struct mutex mutex; /* serialises access to this structure */ ++ ++ struct omap34xxcam_device *cam; ++ struct v4l2_int_device master; ++ ++#define vdev_sensor slave[OMAP34XXCAM_SLAVE_SENSOR] ++#define vdev_lens slave[OMAP34XXCAM_SLAVE_LENS] ++#define vdev_flash slave[OMAP34XXCAM_SLAVE_FLASH] ++ struct v4l2_int_device *slave[OMAP34XXCAM_SLAVE_FLASH + 1]; ++ ++ /* number of slaves attached */ ++ int slaves; ++ ++ /*** video device parameters ***/ ++ struct video_device *vfd; ++ int capture_mem; ++ ++ /*** general driver state information ***/ ++ int index; ++ atomic_t users; ++ enum v4l2_power power_state[OMAP34XXCAM_SLAVE_FLASH + 1]; ++#ifdef OMAP34XXCAM_POWEROFF_DELAY ++ enum v4l2_power power_state_wish; ++ int power_state_mask; ++ struct timer_list poweroff_timer; ++ struct work_struct poweroff_work; ++#endif /* OMAP34XXCAM_POWEROFF_DELAY */ ++ ++#define vdev_sensor_config slave_config[OMAP34XXCAM_SLAVE_SENSOR].u.sensor ++#define vdev_lens_config slave_config[OMAP34XXCAM_SLAVE_LENS].u.lens ++#define vdev_flash_config slave_config[OMAP34XXCAM_SLAVE_FLASH].u.flash ++ struct omap34xxcam_hw_config slave_config[OMAP34XXCAM_SLAVE_FLASH + 1]; ++ ++ /*** capture data ***/ ++ struct file *streaming; ++ struct v4l2_fract want_timeperframe; ++ struct v4l2_pix_format want_pix; ++ spinlock_t pix_lock; ++ struct v4l2_pix_format pix; ++}; ++ ++/** ++ * struct omap34xxcam_device - per-device data structure ++ * @mutex: mutex serialises access to this structure ++ * @sgdma_in_queue: Number or sgdma requests in scatter-gather queue, ++ * protected by the lock above. ++ * @sgdma: ISP sgdma subsystem information structure ++ * @dma_notify: DMA notify flag ++ * @dev: device structure ++ * @vdevs: /dev/video specific structures ++ * @fck: camera module fck clock information ++ * @ick: camera module ick clock information ++ */ ++struct omap34xxcam_device { ++ struct mutex mutex; /* serialises access to this structure */ ++ ++ /*** interfaces and device ***/ ++ struct omap34xxcam_videodev vdevs[OMAP34XXCAM_VIDEODEVS]; ++ ++ /*** camera module clocks ***/ ++ struct clk *fck; ++ struct clk *ick; ++ bool sensor_if_enabled; ++}; ++ ++/** ++ * struct omap34xxcam_fh - per-filehandle data structure ++ * @vbq_lock: spinlock for the videobuf queue ++ * @vbq: V4L2 video buffer queue structure ++ * @field_count: field counter for videobuf_buffer ++ * @vdev: our /dev/video specific structure ++ */ ++struct omap34xxcam_fh { ++ spinlock_t vbq_lock; /* spinlock for the videobuf queue */ ++ struct videobuf_queue vbq; ++ atomic_t field_count; ++ struct omap34xxcam_videodev *vdev; ++}; ++ ++#endif /* ifndef OMAP34XXCAM_H */ +-- +1.5.6.5 + |