summaryrefslogtreecommitdiff
path: root/drivers/media/video
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/video')
-rw-r--r--drivers/media/video/Kconfig51
-rw-r--r--drivers/media/video/Makefile9
-rw-r--r--drivers/media/video/mxc/capture/Kconfig123
-rw-r--r--drivers/media/video/mxc/capture/Makefile39
-rw-r--r--drivers/media/video/mxc/capture/adv7180.c1001
-rw-r--r--drivers/media/video/mxc/capture/csi_v4l2_capture.c1466
-rw-r--r--drivers/media/video/mxc/capture/emma_mt9v111.c679
-rw-r--r--drivers/media/video/mxc/capture/emma_ov2640.c444
-rw-r--r--drivers/media/video/mxc/capture/emma_v4l2_capture.c2075
-rw-r--r--drivers/media/video/mxc/capture/fsl_csi.c289
-rw-r--r--drivers/media/video/mxc/capture/fsl_csi.h198
-rw-r--r--drivers/media/video/mxc/capture/ipu_csi_enc.c332
-rw-r--r--drivers/media/video/mxc/capture/ipu_prp_enc.c491
-rw-r--r--drivers/media/video/mxc/capture/ipu_prp_sw.h38
-rw-r--r--drivers/media/video/mxc/capture/ipu_prp_vf_adc.c601
-rw-r--r--drivers/media/video/mxc/capture/ipu_prp_vf_sdc.c467
-rw-r--r--drivers/media/video/mxc/capture/ipu_prp_vf_sdc_bg.c443
-rw-r--r--drivers/media/video/mxc/capture/ipu_still.c268
-rw-r--r--drivers/media/video/mxc/capture/mc521da.c648
-rw-r--r--drivers/media/video/mxc/capture/mt9v111.c1076
-rw-r--r--drivers/media/video/mxc/capture/mt9v111.h431
-rw-r--r--drivers/media/video/mxc/capture/mx27_csi.c333
-rw-r--r--drivers/media/video/mxc/capture/mx27_csi.h167
-rw-r--r--drivers/media/video/mxc/capture/mx27_prp.h310
-rw-r--r--drivers/media/video/mxc/capture/mx27_prphw.c1099
-rw-r--r--drivers/media/video/mxc/capture/mx27_prpsw.c1042
-rw-r--r--drivers/media/video/mxc/capture/mxc_v4l2_capture.c2728
-rw-r--r--drivers/media/video/mxc/capture/mxc_v4l2_capture.h206
-rw-r--r--drivers/media/video/mxc/capture/ov2640.c1081
-rw-r--r--drivers/media/video/mxc/capture/ov3640.c1432
-rw-r--r--drivers/media/video/mxc/capture/sensor_clock.c97
-rw-r--r--drivers/media/video/mxc/opl/Makefile5
-rw-r--r--drivers/media/video/mxc/opl/hmirror_rotate180_u16.c259
-rw-r--r--drivers/media/video/mxc/opl/opl.h162
-rw-r--r--drivers/media/video/mxc/opl/opl_mod.c30
-rw-r--r--drivers/media/video/mxc/opl/rotate270_u16.c285
-rw-r--r--drivers/media/video/mxc/opl/rotate270_u16_qcif.S70
-rw-r--r--drivers/media/video/mxc/opl/rotate90_u16.c220
-rw-r--r--drivers/media/video/mxc/opl/rotate90_u16_qcif.S71
-rw-r--r--drivers/media/video/mxc/opl/vmirror_u16.c46
-rw-r--r--drivers/media/video/mxc/output/Kconfig28
-rw-r--r--drivers/media/video/mxc/output/Makefile11
-rw-r--r--drivers/media/video/mxc/output/mx27_pp.c904
-rw-r--r--drivers/media/video/mxc/output/mx27_pp.h180
-rw-r--r--drivers/media/video/mxc/output/mx27_v4l2_output.c1442
-rw-r--r--drivers/media/video/mxc/output/mx31_v4l2_wvga_output.c1926
-rw-r--r--drivers/media/video/mxc/output/mxc_v4l2_output.c2614
-rw-r--r--drivers/media/video/mxc/output/mxc_v4l2_output.h154
-rw-r--r--drivers/media/video/mxs_pxp.c1413
-rw-r--r--drivers/media/video/mxs_pxp.h158
-rw-r--r--drivers/media/video/pxp.c1408
-rw-r--r--drivers/media/video/pxp.h130
-rw-r--r--drivers/media/video/videobuf-dma-contig.c4
53 files changed, 31182 insertions, 2 deletions
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index dcf9fa9264bb..c64c8d201262 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -550,6 +550,57 @@ config VIDEO_W9966
Check out <file:Documentation/video4linux/w9966.txt> for more
information.
+config VIDEO_MXC_CAMERA
+ tristate "MXC Video For Linux Camera"
+ depends on VIDEO_DEV && ARCH_MXC
+ default y
+ ---help---
+ This is the video4linux2 capture driver based on MXC IPU/eMMA module.
+
+source "drivers/media/video/mxc/capture/Kconfig"
+
+config VIDEO_MXC_OUTPUT
+ tristate "MXC Video For Linux Video Output"
+ depends on VIDEO_DEV && ARCH_MXC
+ default y
+ ---help---
+ This is the video4linux2 output driver based on MXC IPU/eMMA module.
+
+source "drivers/media/video/mxc/output/Kconfig"
+
+config VIDEO_PXP
+ tristate "STMP3XXX PxP"
+ depends on VIDEO_DEV && VIDEO_V4L2 && ARCH_STMP3XXX
+ select VIDEOBUF_DMA_CONTIG
+ ---help---
+ This is a video4linux driver for the Freescale PxP
+ (Pixel Pipeline). This module supports output overlay of
+ the STMP3xxx framebuffer on a video stream.
+
+ To compile this driver as a module, choose M here: the
+ module will be called pxp.
+
+config VIDEO_MXS_PXP
+ tristate "MXS PxP"
+ depends on VIDEO_DEV && VIDEO_V4L2 && ARCH_MXS
+ select VIDEOBUF_DMA_CONTIG
+ ---help---
+ This is a video4linux driver for the Freescale PxP
+ (Pixel Pipeline). This module supports output overlay of
+ the MXS framebuffer on a video stream.
+
+ To compile this driver as a module, choose M here: the
+ module will be called pxp.
+
+config VIDEO_MXC_OPL
+ tristate
+ depends on VIDEO_DEV && ARCH_MXC
+ default n
+ ---help---
+ This is the ARM9-optimized OPL (Open Primitives Library) software
+ rotation/mirroring implementation. It may be used by eMMA video
+ capture or output device.
+
config VIDEO_CPIA
tristate "CPiA Video For Linux"
depends on VIDEO_V4L1
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index 9f2e3214a482..49cb18802024 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -88,6 +88,15 @@ obj-$(CONFIG_VIDEO_W9966) += w9966.o
obj-$(CONFIG_VIDEO_PMS) += pms.o
obj-$(CONFIG_VIDEO_VINO) += vino.o
obj-$(CONFIG_VIDEO_STRADIS) += stradis.o
+obj-$(CONFIG_VIDEO_MXC_IPU_CAMERA) += mxc/capture/
+obj-$(CONFIG_VIDEO_MXC_EMMA_CAMERA) += mxc/capture/
+obj-$(CONFIG_VIDEO_MXC_CSI_CAMERA) += mxc/capture/
+obj-$(CONFIG_VIDEO_MXC_IPU_OUTPUT) += mxc/output/
+obj-$(CONFIG_VIDEO_MXC_IPUV1_WVGA_OUTPUT) += mxc/output/
+obj-$(CONFIG_VIDEO_MXC_EMMA_OUTPUT) += mxc/output/
+obj-$(CONFIG_VIDEO_MXC_OPL) += mxc/opl/
+obj-$(CONFIG_VIDEO_PXP) += pxp.o
+obj-$(CONFIG_VIDEO_MXS_PXP) += mxs_pxp.o
obj-$(CONFIG_VIDEO_CPIA) += cpia.o
obj-$(CONFIG_VIDEO_CPIA_PP) += cpia_pp.o
obj-$(CONFIG_VIDEO_CPIA_USB) += cpia_usb.o
diff --git a/drivers/media/video/mxc/capture/Kconfig b/drivers/media/video/mxc/capture/Kconfig
new file mode 100644
index 000000000000..1b352afebd87
--- /dev/null
+++ b/drivers/media/video/mxc/capture/Kconfig
@@ -0,0 +1,123 @@
+if VIDEO_MXC_CAMERA
+
+menu "MXC Camera/V4L2 PRP Features support"
+config VIDEO_MXC_IPU_CAMERA
+ bool
+ depends on VIDEO_MXC_CAMERA && MXC_IPU
+ default y
+
+config VIDEO_MXC_EMMA_CAMERA
+ tristate "MX27 eMMA support"
+ depends on VIDEO_MXC_CAMERA && MXC_EMMA && FB_MXC_SYNC_PANEL
+ select VIDEO_MXC_OPL
+ default y
+
+config VIDEO_MXC_CSI_CAMERA
+ tristate "MX25 CSI camera support"
+ depends on !VIDEO_MXC_EMMA_CAMERA
+
+config VIDEO_MXC_CSI_DMA
+ bool "CSI-DMA Still Image Capture support"
+ depends on VIDEO_MXC_EMMA_CAMERA
+ default n
+ ---help---
+ Use CSI-DMA method instead of CSI-PrP link to capture still image. This allows
+ to use less physical contiguous memory to capture big resolution still image. But
+ with this method the CSC (Color Space Conversion) and resize are not supported.
+ If unsure, say N.
+
+choice
+ prompt "Select Camera/TV Decoder"
+ default MXC_CAMERA_OV3640
+ depends on VIDEO_MXC_CAMERA
+
+config MXC_CAMERA_MC521DA
+ tristate "Magnachip mc521da camera support"
+ select I2C_MXC
+ depends on VIDEO_MXC_EMMA_CAMERA
+ ---help---
+ If you plan to use the mc521da Camera with your MXC system, say Y here.
+
+config MXC_EMMA_CAMERA_MICRON111
+ tristate "Micron mt9v111 camera support with eMMA"
+ select I2C_MXC
+ depends on VIDEO_MXC_EMMA_CAMERA
+ ---help---
+ If you plan to use the mt9v111 Camera with your MXC system, say Y here.
+
+config MXC_CAMERA_OV2640_EMMA
+ tristate "OmniVision ov2640 camera support with eMMA"
+ depends on VIDEO_MXC_EMMA_CAMERA
+ ---help---
+ If you plan to use the ov2640 Camera with your MXC system, say Y here.
+
+config MXC_CAMERA_MICRON111
+ tristate "Micron mt9v111 camera support"
+ select I2C_MXC
+ depends on ! VIDEO_MXC_EMMA_CAMERA
+ ---help---
+ If you plan to use the mt9v111 Camera with your MXC system, say Y here.
+
+config MXC_CAMERA_OV2640
+ tristate "OmniVision ov2640 camera support"
+ depends on !VIDEO_MXC_EMMA_CAMERA
+ ---help---
+ If you plan to use the ov2640 Camera with your MXC system, say Y here.
+
+config MXC_CAMERA_OV3640
+ tristate "OmniVision ov3640 camera support"
+ depends on !VIDEO_MXC_EMMA_CAMERA
+ ---help---
+ If you plan to use the ov3640 Camera with your MXC system, say Y here.
+
+config MXC_TVIN_ADV7180
+ tristate "Analog Device adv7180 TV Decoder Input support"
+ depends on (MACH_MX35_3DS || MACH_MX51_3DS)
+ ---help---
+ If you plan to use the adv7180 video decoder with your MXC system, say Y here.
+
+endchoice
+
+config MXC_IPU_PRP_VF_SDC
+ tristate "Pre-Processor VF SDC library"
+ depends on VIDEO_MXC_IPU_CAMERA && FB_MXC_SYNC_PANEL
+ default y
+ ---help---
+ Use case PRP_VF_SDC:
+ Preprocessing image from smart sensor for viewfinder and
+ displaying it on synchronous display with SDC use case.
+ If SDC BG is selected, Rotation will not be supported.
+ CSI -> IC (PRP VF) -> MEM
+ MEM -> IC (ROT) -> MEM
+ MEM -> SDC (FG/BG)
+
+config MXC_IPU_PRP_VF_ADC
+ tristate "Pre-Processor VF ADC library"
+ depends on VIDEO_MXC_IPU_CAMERA && FB_MXC_ASYNC_PANEL
+ default y
+ ---help---
+ Use case PRP_VF_ADC:
+ Preprocessing image from smart sensor for viewfinder and
+ displaying it on asynchronous display.
+ CSI -> IC (PRP VF) -> ADC2
+
+config MXC_IPU_PRP_ENC
+ tristate "Pre-processor Encoder library"
+ depends on VIDEO_MXC_IPU_CAMERA
+ default y
+ ---help---
+ Use case PRP_ENC:
+ Preprocessing image from smart sensor for encoder.
+ CSI -> IC (PRP ENC) -> MEM
+
+config MXC_IPU_CSI_ENC
+ tristate "IPU CSI Encoder library"
+ depends on VIDEO_MXC_IPU_CAMERA
+ default y
+ ---help---
+ Use case IPU_CSI_ENC:
+ Get raw image with CSI from smart sensor for encoder.
+ CSI -> MEM
+endmenu
+
+endif
diff --git a/drivers/media/video/mxc/capture/Makefile b/drivers/media/video/mxc/capture/Makefile
new file mode 100644
index 000000000000..03ff094171bf
--- /dev/null
+++ b/drivers/media/video/mxc/capture/Makefile
@@ -0,0 +1,39 @@
+ifeq ($(CONFIG_VIDEO_MXC_IPU_CAMERA),y)
+ obj-$(CONFIG_VIDEO_MXC_CAMERA) += mxc_v4l2_capture.o
+ obj-$(CONFIG_MXC_IPU_PRP_VF_ADC) += ipu_prp_vf_adc.o
+ obj-$(CONFIG_MXC_IPU_PRP_VF_SDC) += ipu_prp_vf_sdc.o ipu_prp_vf_sdc_bg.o
+ obj-$(CONFIG_MXC_IPU_PRP_ENC) += ipu_prp_enc.o ipu_still.o
+ obj-$(CONFIG_MXC_IPU_CSI_ENC) += ipu_csi_enc.o ipu_still.o
+endif
+
+obj-$(CONFIG_VIDEO_MXC_CSI_CAMERA) += fsl_csi.o csi_v4l2_capture.o
+
+mx27_capture-objs := mx27_prphw.o mx27_prpsw.o emma_v4l2_capture.o
+obj-$(CONFIG_VIDEO_MXC_EMMA_CAMERA) += mx27_csi.o mx27_capture.o
+
+mc521da_camera-objs := mc521da.o sensor_clock.o
+obj-$(CONFIG_MXC_CAMERA_MC521DA) += mc521da_camera.o
+
+emma_mt9v111_camera-objs := emma_mt9v111.o sensor_clock.o
+obj-$(CONFIG_MXC_EMMA_CAMERA_MICRON111) += emma_mt9v111_camera.o
+
+mt9v111_camera-objs := mt9v111.o sensor_clock.o
+obj-$(CONFIG_MXC_CAMERA_MICRON111) += mt9v111_camera.o
+
+hv7161_camera-objs := hv7161.o sensor_clock.o
+obj-$(CONFIG_MXC_CAMERA_HV7161) += hv7161_camera.o
+
+s5k3aaex_camera-objs := s5k3aaex.o sensor_clock.o
+obj-$(CONFIG_MXC_CAMERA_S5K3AAEX) += s5k3aaex_camera.o
+
+emma_ov2640_camera-objs := emma_ov2640.o sensor_clock.o
+obj-$(CONFIG_MXC_CAMERA_OV2640_EMMA) += emma_ov2640_camera.o
+
+ov2640_camera-objs := ov2640.o sensor_clock.o
+obj-$(CONFIG_MXC_CAMERA_OV2640) += ov2640_camera.o
+
+ov3640_camera-objs := ov3640.o sensor_clock.o
+obj-$(CONFIG_MXC_CAMERA_OV3640) += ov3640_camera.o
+
+adv7180_tvin-objs := adv7180.o
+obj-$(CONFIG_MXC_TVIN_ADV7180) += adv7180_tvin.o
diff --git a/drivers/media/video/mxc/capture/adv7180.c b/drivers/media/video/mxc/capture/adv7180.c
new file mode 100644
index 000000000000..527a0d1ad9fa
--- /dev/null
+++ b/drivers/media/video/mxc/capture/adv7180.c
@@ -0,0 +1,1001 @@
+/*
+ * Copyright 2005-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file adv7180.c
+ *
+ * @brief Analog Device ADV7180 video decoder functions
+ *
+ * @ingroup Camera
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/wait.h>
+#include <linux/videodev2.h>
+#include <linux/workqueue.h>
+#include <linux/regulator/consumer.h>
+#include <media/v4l2-int-device.h>
+#include <mach/hardware.h>
+#include "mxc_v4l2_capture.h"
+
+static struct regulator *dvddio_regulator;
+static struct regulator *dvdd_regulator;
+static struct regulator *avdd_regulator;
+static struct regulator *pvdd_regulator;
+static struct mxc_tvin_platform_data *tvin_plat;
+
+extern void gpio_sensor_active(void);
+extern void gpio_sensor_inactive(void);
+
+static int adv7180_probe(struct i2c_client *adapter,
+ const struct i2c_device_id *id);
+static int adv7180_detach(struct i2c_client *client);
+
+static const struct i2c_device_id adv7180_id[] = {
+ {"adv7180", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, adv7180_id);
+
+static struct i2c_driver adv7180_i2c_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "adv7180",
+ },
+ .probe = adv7180_probe,
+ .remove = adv7180_detach,
+ .id_table = adv7180_id,
+};
+
+/*!
+ * Maintains the information on the current state of the sesor.
+ */
+struct sensor {
+ struct v4l2_int_device *v4l2_int_device;
+ struct i2c_client *i2c_client;
+ struct v4l2_pix_format pix;
+ struct v4l2_captureparm streamcap;
+ bool on;
+
+ /* control settings */
+ int brightness;
+ int hue;
+ int contrast;
+ int saturation;
+ int red;
+ int green;
+ int blue;
+ int ae_mode;
+
+ v4l2_std_id std_id;
+} adv7180_data;
+
+/*! List of input video formats supported. The video formats is corresponding
+ * with v4l2 id in video_fmt_t
+ */
+typedef enum {
+ ADV7180_NTSC = 0, /*!< Locked on (M) NTSC video signal. */
+ ADV7180_PAL, /*!< (B, G, H, I, N)PAL video signal. */
+ ADV7180_NOT_LOCKED, /*!< Not locked on a signal. */
+} video_fmt_idx;
+
+/*! Number of video standards supported (including 'not locked' signal). */
+#define ADV7180_STD_MAX (ADV7180_PAL + 1)
+
+/*! Video format structure. */
+typedef struct {
+ int v4l2_id; /*!< Video for linux ID. */
+ char name[16]; /*!< Name (e.g., "NTSC", "PAL", etc.) */
+ u16 raw_width; /*!< Raw width. */
+ u16 raw_height; /*!< Raw height. */
+ u16 active_width; /*!< Active width. */
+ u16 active_height; /*!< Active height. */
+} video_fmt_t;
+
+/*! Description of video formats supported.
+ *
+ * PAL: raw=720x625, active=720x576.
+ * NTSC: raw=720x525, active=720x480.
+ */
+static video_fmt_t video_fmts[] = {
+ { /*! NTSC */
+ .v4l2_id = V4L2_STD_NTSC,
+ .name = "NTSC",
+ .raw_width = 720, /* SENS_FRM_WIDTH */
+ .raw_height = 525, /* SENS_FRM_HEIGHT */
+ .active_width = 720, /* ACT_FRM_WIDTH plus 1 */
+ .active_height = 480, /* ACT_FRM_WIDTH plus 1 */
+ },
+ { /*! (B, G, H, I, N) PAL */
+ .v4l2_id = V4L2_STD_PAL,
+ .name = "PAL",
+ .raw_width = 720,
+ .raw_height = 625,
+ .active_width = 720,
+ .active_height = 576,
+ },
+ { /*! Unlocked standard */
+ .v4l2_id = V4L2_STD_ALL,
+ .name = "Autodetect",
+ .raw_width = 720,
+ .raw_height = 625,
+ .active_width = 720,
+ .active_height = 576,
+ },
+};
+
+/*!* Standard index of ADV7180. */
+static video_fmt_idx video_idx = ADV7180_PAL;
+
+/*! @brief This mutex is used to provide mutual exclusion.
+ *
+ * Create a mutex that can be used to provide mutually exclusive
+ * read/write access to the globally accessible data structures
+ * and variables that were defined above.
+ */
+static DECLARE_MUTEX(mutex);
+
+#define IF_NAME "adv7180"
+#define ADV7180_INPUT_CTL 0x00 /* Input Control */
+#define ADV7180_STATUS_1 0x10 /* Status #1 */
+#define ADV7180_BRIGHTNESS 0x0a /* Brightness */
+#define ADV7180_IDENT 0x11 /* IDENT */
+#define ADV7180_VSYNC_FIELD_CTL_1 0x31 /* VSYNC Field Control #1 */
+#define ADV7180_MANUAL_WIN_CTL 0x3d /* Manual Window Control */
+#define ADV7180_SD_SATURATION_CB 0xe3 /* SD Saturation Cb */
+#define ADV7180_SD_SATURATION_CR 0xe4 /* SD Saturation Cr */
+#define ADV7180_PWR_MNG 0x0f /* Power Management */
+
+/* supported controls */
+/* This hasn't been fully implemented yet.
+ * This is how it should work, though. */
+static struct v4l2_queryctrl adv7180_qctrl[] = {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0, /* check this value */
+ .maximum = 255, /* check this value */
+ .step = 1, /* check this value */
+ .default_value = 127, /* check this value */
+ .flags = 0,
+ }, {
+ .id = V4L2_CID_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Saturation",
+ .minimum = 0, /* check this value */
+ .maximum = 255, /* check this value */
+ .step = 0x1, /* check this value */
+ .default_value = 127, /* check this value */
+ .flags = 0,
+ }
+};
+
+/***********************************************************************
+ * I2C transfert.
+ ***********************************************************************/
+
+/*! Read one register from a ADV7180 i2c slave device.
+ *
+ * @param *reg register in the device we wish to access.
+ *
+ * @return 0 if success, an error code otherwise.
+ */
+static inline int adv7180_read(u8 reg)
+{
+ int val;
+ val = i2c_smbus_read_byte_data(adv7180_data.i2c_client, reg);
+ if (val < 0) {
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ "%s:read reg error: reg=%2x \n", __func__, reg);
+ return -1;
+ }
+ return val;
+}
+
+/*! Write one register of a ADV7180 i2c slave device.
+ *
+ * @param *reg register in the device we wish to access.
+ *
+ * @return 0 if success, an error code otherwise.
+ */
+static int adv7180_write_reg(u8 reg, u8 val)
+{
+ if (i2c_smbus_write_byte_data(adv7180_data.i2c_client, reg, val) < 0) {
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ "%s:write reg error:reg=%2x,val=%2x\n", __func__,
+ reg, val);
+ return -1;
+ }
+ return 0;
+}
+
+/***********************************************************************
+ * mxc_v4l2_capture interface.
+ ***********************************************************************/
+
+/*!
+ * Return attributes of current video standard.
+ * Since this device autodetects the current standard, this function also
+ * sets the values that need to be changed if the standard changes.
+ * There is no set std equivalent function.
+ *
+ * @return None.
+ */
+static void adv7180_get_std(v4l2_std_id *std)
+{
+ int tmp;
+ int idx;
+
+ dev_dbg(&adv7180_data.i2c_client->dev, "In adv7180_get_std\n");
+
+ /* Make sure power on */
+ if (tvin_plat->pwdn)
+ tvin_plat->pwdn(0);
+
+ /* Read the AD_RESULT to get the detect output video standard */
+ tmp = adv7180_read(ADV7180_STATUS_1) & 0x70;
+
+ down(&mutex);
+ if (tmp == 0x40) {
+ /* PAL */
+ *std = V4L2_STD_PAL;
+ idx = ADV7180_PAL;
+ } else if (tmp == 0) {
+ /*NTSC*/
+ *std = V4L2_STD_NTSC;
+ idx = ADV7180_NTSC;
+ } else {
+ *std = V4L2_STD_ALL;
+ idx = ADV7180_NOT_LOCKED;
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ "Got invalid video standard! \n");
+ }
+ up(&mutex);
+
+ /* This assumes autodetect which this device uses. */
+ if (*std != adv7180_data.std_id) {
+ video_idx = idx;
+ adv7180_data.std_id = *std;
+ adv7180_data.pix.width = video_fmts[video_idx].raw_width;
+ adv7180_data.pix.height = video_fmts[video_idx].raw_height;
+ }
+}
+
+/***********************************************************************
+ * IOCTL Functions from v4l2_int_ioctl_desc.
+ ***********************************************************************/
+
+/*!
+ * ioctl_g_ifparm - V4L2 sensor interface handler for vidioc_int_g_ifparm_num
+ * s: pointer to standard V4L2 device structure
+ * p: pointer to standard V4L2 vidioc_int_g_ifparm_num ioctl structure
+ *
+ * Gets slave interface parameters.
+ * Calculates the required xclk value to support the requested
+ * clock parameters in p. This value is returned in the p
+ * parameter.
+ *
+ * vidioc_int_g_ifparm returns platform-specific information about the
+ * interface settings used by the sensor.
+ *
+ * Called on open.
+ */
+static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p)
+{
+ dev_dbg(&adv7180_data.i2c_client->dev, "In adv7180:ioctl_g_ifparm\n");
+
+ if (s == NULL) {
+ pr_err(" ERROR!! no slave device set!\n");
+ return -1;
+ }
+
+ /* Initialize structure to 0s then set any non-0 values. */
+ memset(p, 0, sizeof(*p));
+ p->if_type = V4L2_IF_TYPE_BT656; /* This is the only possibility. */
+ p->u.bt656.mode = V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT;
+ p->u.bt656.nobt_hs_inv = 1;
+
+ /* ADV7180 has a dedicated clock so no clock settings needed. */
+
+ return 0;
+}
+
+/*!
+ * Sets the camera power.
+ *
+ * s pointer to the camera device
+ * on if 1, power is to be turned on. 0 means power is to be turned off
+ *
+ * ioctl_s_power - V4L2 sensor interface handler for vidioc_int_s_power_num
+ * @s: pointer to standard V4L2 device structure
+ * @on: power state to which device is to be set
+ *
+ * Sets devices power state to requrested state, if possible.
+ * This is called on open, close, suspend and resume.
+ */
+static int ioctl_s_power(struct v4l2_int_device *s, int on)
+{
+ struct sensor *sensor = s->priv;
+
+ dev_dbg(&adv7180_data.i2c_client->dev, "In adv7180:ioctl_s_power\n");
+
+ if (on && !sensor->on) {
+ gpio_sensor_active();
+
+ /* Make sure pwoer on */
+ if (tvin_plat->pwdn)
+ tvin_plat->pwdn(0);
+
+ if (adv7180_write_reg(ADV7180_PWR_MNG, 0) != 0)
+ return -EIO;
+ } else if (!on && sensor->on) {
+ if (adv7180_write_reg(ADV7180_PWR_MNG, 0x24) != 0)
+ return -EIO;
+ gpio_sensor_inactive();
+ }
+
+ sensor->on = on;
+
+ return 0;
+}
+
+/*!
+ * ioctl_g_parm - V4L2 sensor interface handler for VIDIOC_G_PARM ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure
+ *
+ * Returns the sensor's video CAPTURE parameters.
+ */
+static int ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)
+{
+ struct sensor *sensor = s->priv;
+ struct v4l2_captureparm *cparm = &a->parm.capture;
+
+ dev_dbg(&adv7180_data.i2c_client->dev, "In adv7180:ioctl_g_parm\n");
+
+ switch (a->type) {
+ /* These are all the possible cases. */
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ pr_debug(" type is V4L2_BUF_TYPE_VIDEO_CAPTURE\n");
+ memset(a, 0, sizeof(*a));
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ cparm->capability = sensor->streamcap.capability;
+ cparm->timeperframe = sensor->streamcap.timeperframe;
+ cparm->capturemode = sensor->streamcap.capturemode;
+ break;
+
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ break;
+
+ default:
+ pr_debug("ioctl_g_parm:type is unknown %d\n", a->type);
+ break;
+ }
+
+ return 0;
+}
+
+/*!
+ * ioctl_s_parm - V4L2 sensor interface handler for VIDIOC_S_PARM ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure
+ *
+ * Configures the sensor to use the input parameters, if possible. If
+ * not possible, reverts to the old parameters and returns the
+ * appropriate error code.
+ *
+ * This driver cannot change these settings.
+ */
+static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)
+{
+ dev_dbg(&adv7180_data.i2c_client->dev, "In adv7180:ioctl_s_parm\n");
+
+ switch (a->type) {
+ /* These are all the possible cases. */
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ break;
+
+ default:
+ pr_debug(" type is unknown - %d\n", a->type);
+ break;
+ }
+
+ return 0;
+}
+
+/*!
+ * ioctl_g_fmt_cap - V4L2 sensor interface handler for ioctl_g_fmt_cap
+ * @s: pointer to standard V4L2 device structure
+ * @f: pointer to standard V4L2 v4l2_format structure
+ *
+ * Returns the sensor's current pixel format in the v4l2_format
+ * parameter.
+ */
+static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f)
+{
+ struct sensor *sensor = s->priv;
+
+ dev_dbg(&adv7180_data.i2c_client->dev, "In adv7180:ioctl_g_fmt_cap\n");
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ pr_debug(" Returning size of %dx%d\n",
+ sensor->pix.width, sensor->pix.height);
+ f->fmt.pix = sensor->pix;
+ break;
+
+ case V4L2_BUF_TYPE_PRIVATE: {
+ v4l2_std_id std;
+ adv7180_get_std(&std);
+ f->fmt.pix.pixelformat = (u32)std;
+ }
+ break;
+
+ default:
+ f->fmt.pix = sensor->pix;
+ break;
+ }
+
+ return 0;
+}
+
+/*!
+ * ioctl_queryctrl - V4L2 sensor interface handler for VIDIOC_QUERYCTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @qc: standard V4L2 VIDIOC_QUERYCTRL ioctl structure
+ *
+ * If the requested control is supported, returns the control information
+ * from the video_control[] array. Otherwise, returns -EINVAL if the
+ * control is not supported.
+ */
+static int ioctl_queryctrl(struct v4l2_int_device *s,
+ struct v4l2_queryctrl *qc)
+{
+ int i;
+
+ dev_dbg(&adv7180_data.i2c_client->dev, "In adv7180:ioctl_queryctrl\n");
+
+ for (i = 0; i < ARRAY_SIZE(adv7180_qctrl); i++)
+ if (qc->id && qc->id == adv7180_qctrl[i].id) {
+ memcpy(qc, &(adv7180_qctrl[i]),
+ sizeof(*qc));
+ return (0);
+ }
+
+ return -EINVAL;
+}
+
+/*!
+ * ioctl_g_ctrl - V4L2 sensor interface handler for VIDIOC_G_CTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @vc: standard V4L2 VIDIOC_G_CTRL ioctl structure
+ *
+ * If the requested control is supported, returns the control's current
+ * value from the video_control[] array. Otherwise, returns -EINVAL
+ * if the control is not supported.
+ */
+static int ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc)
+{
+ int ret = 0;
+
+ dev_dbg(&adv7180_data.i2c_client->dev, "In adv7180:ioctl_g_ctrl\n");
+
+ /* Make sure power on */
+ if (tvin_plat->pwdn)
+ tvin_plat->pwdn(0);
+
+ switch (vc->id) {
+ case V4L2_CID_BRIGHTNESS:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_BRIGHTNESS\n");
+ adv7180_data.brightness = adv7180_read(ADV7180_BRIGHTNESS);
+ vc->value = adv7180_data.brightness;
+ break;
+ case V4L2_CID_CONTRAST:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_CONTRAST\n");
+ vc->value = adv7180_data.contrast;
+ break;
+ case V4L2_CID_SATURATION:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_SATURATION\n");
+ adv7180_data.saturation = adv7180_read(ADV7180_SD_SATURATION_CB);
+ vc->value = adv7180_data.saturation;
+ break;
+ case V4L2_CID_HUE:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_HUE\n");
+ vc->value = adv7180_data.hue;
+ break;
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_AUTO_WHITE_BALANCE\n");
+ break;
+ case V4L2_CID_DO_WHITE_BALANCE:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_DO_WHITE_BALANCE\n");
+ break;
+ case V4L2_CID_RED_BALANCE:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_RED_BALANCE\n");
+ vc->value = adv7180_data.red;
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_BLUE_BALANCE\n");
+ vc->value = adv7180_data.blue;
+ break;
+ case V4L2_CID_GAMMA:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_GAMMA\n");
+ break;
+ case V4L2_CID_EXPOSURE:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_EXPOSURE\n");
+ vc->value = adv7180_data.ae_mode;
+ break;
+ case V4L2_CID_AUTOGAIN:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_AUTOGAIN\n");
+ break;
+ case V4L2_CID_GAIN:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_GAIN\n");
+ break;
+ case V4L2_CID_HFLIP:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_HFLIP\n");
+ break;
+ case V4L2_CID_VFLIP:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_VFLIP\n");
+ break;
+ default:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " Default case\n");
+ vc->value = 0;
+ ret = -EPERM;
+ break;
+ }
+
+ return ret;
+}
+
+/*!
+ * ioctl_s_ctrl - V4L2 sensor interface handler for VIDIOC_S_CTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @vc: standard V4L2 VIDIOC_S_CTRL ioctl structure
+ *
+ * If the requested control is supported, sets the control's current
+ * value in HW (and updates the video_control[] array). Otherwise,
+ * returns -EINVAL if the control is not supported.
+ */
+static int ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc)
+{
+ int retval = 0;
+ u8 tmp;
+
+ dev_dbg(&adv7180_data.i2c_client->dev, "In adv7180:ioctl_s_ctrl\n");
+
+ /* Make sure power on */
+ if (tvin_plat->pwdn)
+ tvin_plat->pwdn(0);
+
+ switch (vc->id) {
+ case V4L2_CID_BRIGHTNESS:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_BRIGHTNESS\n");
+ tmp = vc->value;
+ adv7180_write_reg(ADV7180_BRIGHTNESS, tmp);
+ adv7180_data.brightness = vc->value;
+ break;
+ case V4L2_CID_CONTRAST:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_CONTRAST\n");
+ break;
+ case V4L2_CID_SATURATION:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_SATURATION\n");
+ tmp = vc->value;
+ adv7180_write_reg(ADV7180_SD_SATURATION_CB, tmp);
+ adv7180_write_reg(ADV7180_SD_SATURATION_CR, tmp);
+ adv7180_data.saturation = vc->value;
+ break;
+ case V4L2_CID_HUE:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_HUE\n");
+ break;
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_AUTO_WHITE_BALANCE\n");
+ break;
+ case V4L2_CID_DO_WHITE_BALANCE:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_DO_WHITE_BALANCE\n");
+ break;
+ case V4L2_CID_RED_BALANCE:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_RED_BALANCE\n");
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_BLUE_BALANCE\n");
+ break;
+ case V4L2_CID_GAMMA:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_GAMMA\n");
+ break;
+ case V4L2_CID_EXPOSURE:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_EXPOSURE\n");
+ break;
+ case V4L2_CID_AUTOGAIN:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_AUTOGAIN\n");
+ break;
+ case V4L2_CID_GAIN:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_GAIN\n");
+ break;
+ case V4L2_CID_HFLIP:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_HFLIP\n");
+ break;
+ case V4L2_CID_VFLIP:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_VFLIP\n");
+ break;
+ default:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " Default case\n");
+ retval = -EPERM;
+ break;
+ }
+
+ return retval;
+}
+
+/*!
+ * ioctl_init - V4L2 sensor interface handler for VIDIOC_INT_INIT
+ * @s: pointer to standard V4L2 device structure
+ */
+static int ioctl_init(struct v4l2_int_device *s)
+{
+ dev_dbg(&adv7180_data.i2c_client->dev, "In adv7180:ioctl_init\n");
+ return 0;
+}
+
+/*!
+ * ioctl_dev_init - V4L2 sensor interface handler for vidioc_int_dev_init_num
+ * @s: pointer to standard V4L2 device structure
+ *
+ * Initialise the device when slave attaches to the master.
+ */
+static int ioctl_dev_init(struct v4l2_int_device *s)
+{
+ dev_dbg(&adv7180_data.i2c_client->dev, "In adv7180:ioctl_dev_init\n");
+ return 0;
+}
+
+/*!
+ * This structure defines all the ioctls for this module.
+ */
+static struct v4l2_int_ioctl_desc adv7180_ioctl_desc[] = {
+
+ {vidioc_int_dev_init_num, (v4l2_int_ioctl_func *)ioctl_dev_init},
+
+ /*!
+ * Delinitialise the dev. at slave detach.
+ * The complement of ioctl_dev_init.
+ */
+/* {vidioc_int_dev_exit_num, (v4l2_int_ioctl_func *)ioctl_dev_exit}, */
+
+ {vidioc_int_s_power_num, (v4l2_int_ioctl_func *)ioctl_s_power},
+ {vidioc_int_g_ifparm_num, (v4l2_int_ioctl_func *)ioctl_g_ifparm},
+/* {vidioc_int_g_needs_reset_num,
+ (v4l2_int_ioctl_func *)ioctl_g_needs_reset}, */
+/* {vidioc_int_reset_num, (v4l2_int_ioctl_func *)ioctl_reset}, */
+ {vidioc_int_init_num, (v4l2_int_ioctl_func *)ioctl_init},
+
+ /*!
+ * VIDIOC_ENUM_FMT ioctl for the CAPTURE buffer type.
+ */
+/* {vidioc_int_enum_fmt_cap_num,
+ (v4l2_int_ioctl_func *)ioctl_enum_fmt_cap}, */
+
+ /*!
+ * VIDIOC_TRY_FMT ioctl for the CAPTURE buffer type.
+ * This ioctl is used to negotiate the image capture size and
+ * pixel format without actually making it take effect.
+ */
+/* {vidioc_int_try_fmt_cap_num,
+ (v4l2_int_ioctl_func *)ioctl_try_fmt_cap}, */
+
+ {vidioc_int_g_fmt_cap_num, (v4l2_int_ioctl_func *)ioctl_g_fmt_cap},
+
+ /*!
+ * If the requested format is supported, configures the HW to use that
+ * format, returns error code if format not supported or HW can't be
+ * correctly configured.
+ */
+/* {vidioc_int_s_fmt_cap_num, (v4l2_int_ioctl_func *)ioctl_s_fmt_cap}, */
+
+ {vidioc_int_g_parm_num, (v4l2_int_ioctl_func *)ioctl_g_parm},
+ {vidioc_int_s_parm_num, (v4l2_int_ioctl_func *)ioctl_s_parm},
+ {vidioc_int_queryctrl_num, (v4l2_int_ioctl_func *)ioctl_queryctrl},
+ {vidioc_int_g_ctrl_num, (v4l2_int_ioctl_func *)ioctl_g_ctrl},
+ {vidioc_int_s_ctrl_num, (v4l2_int_ioctl_func *)ioctl_s_ctrl},
+};
+
+static struct v4l2_int_slave adv7180_slave = {
+ .ioctls = adv7180_ioctl_desc,
+ .num_ioctls = ARRAY_SIZE(adv7180_ioctl_desc),
+};
+
+static struct v4l2_int_device adv7180_int_device = {
+ .module = THIS_MODULE,
+ .name = "adv7180",
+ .type = v4l2_int_type_slave,
+ .u = {
+ .slave = &adv7180_slave,
+ },
+};
+
+
+/***********************************************************************
+ * I2C client and driver.
+ ***********************************************************************/
+
+/*! ADV7180 Reset function.
+ *
+ * @return None.
+ */
+static void adv7180_hard_reset(void)
+{
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ "In adv7180:adv7180_hard_reset\n");
+
+ /*! Driver works fine without explicit register
+ * initialization. Furthermore, initializations takes about 2 seconds
+ * at startup...
+ */
+
+ /*! Set YPbPr input on AIN1,4,5 and normal
+ * operations(autodection of all stds).
+ */
+ adv7180_write_reg(ADV7180_INPUT_CTL, 0x09);
+
+ /*! Datasheet recommends: */
+ adv7180_write_reg(ADV7180_VSYNC_FIELD_CTL_1, 0x02);
+ adv7180_write_reg(ADV7180_MANUAL_WIN_CTL, 0xa2);
+}
+
+/*! ADV7180 I2C attach function.
+ *
+ * @param *adapter struct i2c_adapter *.
+ *
+ * @return Error code indicating success or failure.
+ */
+
+/*!
+ * ADV7180 I2C probe function.
+ * Function set in i2c_driver struct.
+ * Called by insmod.
+ *
+ * @param *adapter I2C adapter descriptor.
+ *
+ * @return Error code indicating success or failure.
+ */
+static int adv7180_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int rev_id;
+ int ret = 0;
+ tvin_plat = client->dev.platform_data;
+
+ dev_dbg(&adv7180_data.i2c_client->dev, "In adv7180_probe\n");
+
+ if (tvin_plat->dvddio_reg) {
+ dvddio_regulator =
+ regulator_get(&client->dev, tvin_plat->dvddio_reg);
+ if (!IS_ERR_VALUE((unsigned long)dvddio_regulator)) {
+ regulator_set_voltage(dvddio_regulator, 3300000, 3300000);
+ if (regulator_enable(dvddio_regulator) != 0)
+ return -ENODEV;
+ }
+ }
+
+ if (tvin_plat->dvdd_reg) {
+ dvdd_regulator =
+ regulator_get(&client->dev, tvin_plat->dvdd_reg);
+ if (!IS_ERR_VALUE((unsigned long)dvdd_regulator)) {
+ regulator_set_voltage(dvdd_regulator, 1800000, 1800000);
+ if (regulator_enable(dvdd_regulator) != 0)
+ return -ENODEV;
+ }
+ }
+
+ if (tvin_plat->avdd_reg) {
+ avdd_regulator =
+ regulator_get(&client->dev, tvin_plat->avdd_reg);
+ if (!IS_ERR_VALUE((unsigned long)avdd_regulator)) {
+ regulator_set_voltage(avdd_regulator, 1800000, 1800000);
+ if (regulator_enable(avdd_regulator) != 0)
+ return -ENODEV;
+ }
+ }
+
+ if (tvin_plat->pvdd_reg) {
+ pvdd_regulator =
+ regulator_get(&client->dev, tvin_plat->pvdd_reg);
+ if (!IS_ERR_VALUE((unsigned long)pvdd_regulator)) {
+ regulator_set_voltage(pvdd_regulator, 1800000, 1800000);
+ if (regulator_enable(pvdd_regulator) != 0)
+ return -ENODEV;
+ }
+ }
+
+
+ if (tvin_plat->reset)
+ tvin_plat->reset();
+
+ if (tvin_plat->pwdn)
+ tvin_plat->pwdn(0);
+
+ msleep(1);
+
+ /* Set initial values for the sensor struct. */
+ memset(&adv7180_data, 0, sizeof(adv7180_data));
+ adv7180_data.i2c_client = client;
+ adv7180_data.streamcap.timeperframe.denominator = 30;
+ adv7180_data.streamcap.timeperframe.numerator = 1;
+ adv7180_data.std_id = V4L2_STD_ALL;
+ video_idx = ADV7180_NOT_LOCKED;
+ adv7180_data.pix.width = video_fmts[video_idx].raw_width;
+ adv7180_data.pix.height = video_fmts[video_idx].raw_height;
+ adv7180_data.pix.pixelformat = V4L2_PIX_FMT_UYVY; /* YUV422 */
+ adv7180_data.pix.priv = 1; /* 1 is used to indicate TV in */
+ adv7180_data.on = true;
+
+ gpio_sensor_active();
+
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ "%s:adv7180 probe i2c address is 0x%02X \n",
+ __func__, adv7180_data.i2c_client->addr);
+
+ /*! Read the revision ID of the tvin chip */
+ rev_id = adv7180_read(ADV7180_IDENT);
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ "%s:Analog Device adv7%2X0 detected! \n", __func__,
+ rev_id);
+
+ /*! ADV7180 initialization. */
+ adv7180_hard_reset();
+
+ pr_debug(" type is %d (expect %d)\n",
+ adv7180_int_device.type, v4l2_int_type_slave);
+ pr_debug(" num ioctls is %d\n",
+ adv7180_int_device.u.slave->num_ioctls);
+
+ /* This function attaches this structure to the /dev/video0 device.
+ * The pointer in priv points to the mt9v111_data structure here.*/
+ adv7180_int_device.priv = &adv7180_data;
+ ret = v4l2_int_device_register(&adv7180_int_device);
+
+ return ret;
+}
+
+/*!
+ * ADV7180 I2C detach function.
+ * Called on rmmod.
+ *
+ * @param *client struct i2c_client*.
+ *
+ * @return Error code indicating success or failure.
+ */
+static int adv7180_detach(struct i2c_client *client)
+{
+ struct mxc_tvin_platform_data *plat_data = client->dev.platform_data;
+
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ "%s:Removing %s video decoder @ 0x%02X from adapter %s \n",
+ __func__, IF_NAME, client->addr << 1, client->adapter->name);
+
+ if (plat_data->pwdn)
+ plat_data->pwdn(1);
+
+ if (dvddio_regulator) {
+ regulator_disable(dvddio_regulator);
+ regulator_put(dvddio_regulator);
+ }
+
+ if (dvdd_regulator) {
+ regulator_disable(dvdd_regulator);
+ regulator_put(dvdd_regulator);
+ }
+
+ if (avdd_regulator) {
+ regulator_disable(avdd_regulator);
+ regulator_put(avdd_regulator);
+ }
+
+ if (pvdd_regulator) {
+ regulator_disable(pvdd_regulator);
+ regulator_put(pvdd_regulator);
+ }
+
+ v4l2_int_device_unregister(&adv7180_int_device);
+
+ return 0;
+}
+
+/*!
+ * ADV7180 init function.
+ * Called on insmod.
+ *
+ * @return Error code indicating success or failure.
+ */
+static __init int adv7180_init(void)
+{
+ u8 err = 0;
+
+ dev_dbg(&adv7180_data.i2c_client->dev, "In adv7180_init\n");
+
+ /* Tells the i2c driver what functions to call for this driver. */
+ err = i2c_add_driver(&adv7180_i2c_driver);
+ if (err != 0)
+ pr_err("%s:driver registration failed, error=%d \n",
+ __func__, err);
+
+ return err;
+}
+
+/*!
+ * ADV7180 cleanup function.
+ * Called on rmmod.
+ *
+ * @return Error code indicating success or failure.
+ */
+static void __exit adv7180_clean(void)
+{
+ dev_dbg(&adv7180_data.i2c_client->dev, "In adv7180_clean\n");
+ i2c_del_driver(&adv7180_i2c_driver);
+ gpio_sensor_inactive();
+}
+
+module_init(adv7180_init);
+module_exit(adv7180_clean);
+
+MODULE_AUTHOR("Freescale Semiconductor");
+MODULE_DESCRIPTION("Anolog Device ADV7180 video decoder driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxc/capture/csi_v4l2_capture.c b/drivers/media/video/mxc/capture/csi_v4l2_capture.c
new file mode 100644
index 000000000000..cf224e0673f0
--- /dev/null
+++ b/drivers/media/video/mxc/capture/csi_v4l2_capture.c
@@ -0,0 +1,1466 @@
+/*
+ * Copyright 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file drivers/media/video/mxc/capture/csi_v4l2_capture.c
+ * This file is derived from mxc_v4l2_capture.c
+ *
+ * @brief MX25 Video For Linux 2 driver
+ *
+ * @ingroup MXC_V4L2_CAPTURE
+ */
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/io.h>
+#include <linux/semaphore.h>
+#include <linux/pagemap.h>
+#include <linux/vmalloc.h>
+#include <linux/types.h>
+#include <linux/fb.h>
+#include <linux/dma-mapping.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-int-device.h>
+#include <linux/mxcfb.h>
+#include "mxc_v4l2_capture.h"
+#include "fsl_csi.h"
+
+static int video_nr = -1;
+static cam_data *g_cam;
+
+static int csi_v4l2_master_attach(struct v4l2_int_device *slave);
+static void csi_v4l2_master_detach(struct v4l2_int_device *slave);
+static u8 camera_power(cam_data *cam, bool cameraOn);
+
+/*! Information about this driver. */
+static struct v4l2_int_master csi_v4l2_master = {
+ .attach = csi_v4l2_master_attach,
+ .detach = csi_v4l2_master_detach,
+};
+
+static struct v4l2_int_device csi_v4l2_int_device = {
+ .module = THIS_MODULE,
+ .name = "csi_v4l2_cap",
+ .type = v4l2_int_type_master,
+ .u = {
+ .master = &csi_v4l2_master,
+ },
+};
+
+/*!
+ * Camera V4l2 callback function.
+ *
+ * @param mask u32
+ * @param dev void device structure
+ *
+ * @return none
+ */
+static void camera_callback(u32 mask, void *dev)
+{
+ struct mxc_v4l_frame *done_frame;
+ struct mxc_v4l_frame *ready_frame;
+ cam_data *cam;
+
+ cam = (cam_data *) dev;
+ if (cam == NULL)
+ return;
+
+ if (list_empty(&cam->working_q)) {
+ pr_err("ERROR: v4l2 capture: %s: "
+ "working queue empty\n", __func__);
+ return;
+ }
+
+ done_frame =
+ list_entry(cam->working_q.next, struct mxc_v4l_frame, queue);
+ if (done_frame->buffer.flags & V4L2_BUF_FLAG_QUEUED) {
+ done_frame->buffer.flags |= V4L2_BUF_FLAG_DONE;
+ done_frame->buffer.flags &= ~V4L2_BUF_FLAG_QUEUED;
+ if (list_empty(&cam->ready_q)) {
+ cam->skip_frame++;
+ } else {
+ ready_frame = list_entry(cam->ready_q.next,
+ struct mxc_v4l_frame, queue);
+ list_del(cam->ready_q.next);
+ list_add_tail(&ready_frame->queue, &cam->working_q);
+
+ if (cam->ping_pong_csi == 1) {
+ __raw_writel(cam->frame[ready_frame->index].
+ paddress, CSI_CSIDMASA_FB1);
+ } else {
+ __raw_writel(cam->frame[ready_frame->index].
+ paddress, CSI_CSIDMASA_FB2);
+ }
+ }
+
+ /* Added to the done queue */
+ list_del(cam->working_q.next);
+ list_add_tail(&done_frame->queue, &cam->done_q);
+ cam->enc_counter++;
+ wake_up_interruptible(&cam->enc_queue);
+ } else {
+ pr_err("ERROR: v4l2 capture: %s: "
+ "buffer not queued\n", __func__);
+ }
+
+ return;
+}
+
+/*!
+ * Make csi ready for capture image.
+ *
+ * @param cam structure cam_data *
+ *
+ * @return status 0 success
+ */
+static int csi_cap_image(cam_data *cam)
+{
+ unsigned int value;
+
+ value = __raw_readl(CSI_CSICR3);
+ __raw_writel(value | BIT_DMA_REFLASH_RFF | BIT_FRMCNT_RST, CSI_CSICR3);
+ value = __raw_readl(CSI_CSISR);
+ __raw_writel(value, CSI_CSISR);
+
+ return 0;
+}
+
+/***************************************************************************
+ * Functions for handling Frame buffers.
+ **************************************************************************/
+
+/*!
+ * Free frame buffers
+ *
+ * @param cam Structure cam_data *
+ *
+ * @return status 0 success.
+ */
+static int csi_free_frame_buf(cam_data *cam)
+{
+ int i;
+
+ pr_debug("MVC: In %s\n", __func__);
+
+ for (i = 0; i < FRAME_NUM; i++) {
+ if (cam->frame[i].vaddress != 0) {
+ dma_free_coherent(0, cam->frame[i].buffer.length,
+ cam->frame[i].vaddress,
+ cam->frame[i].paddress);
+ cam->frame[i].vaddress = 0;
+ }
+ }
+
+ return 0;
+}
+
+/*!
+ * Allocate frame buffers
+ *
+ * @param cam Structure cam_data *
+ * @param count int number of buffer need to allocated
+ *
+ * @return status -0 Successfully allocated a buffer, -ENOBUFS failed.
+ */
+static int csi_allocate_frame_buf(cam_data *cam, int count)
+{
+ int i;
+
+ pr_debug("In MVC:%s- size=%d\n",
+ __func__, cam->v2f.fmt.pix.sizeimage);
+ for (i = 0; i < count; i++) {
+ cam->frame[i].vaddress = dma_alloc_coherent(0, PAGE_ALIGN
+ (cam->v2f.fmt.
+ pix.sizeimage),
+ &cam->frame[i].
+ paddress,
+ GFP_DMA |
+ GFP_KERNEL);
+ if (cam->frame[i].vaddress == 0) {
+ pr_err("ERROR: v4l2 capture: "
+ "%s failed.\n", __func__);
+ csi_free_frame_buf(cam);
+ return -ENOBUFS;
+ }
+ cam->frame[i].buffer.index = i;
+ cam->frame[i].buffer.flags = V4L2_BUF_FLAG_MAPPED;
+ cam->frame[i].buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ cam->frame[i].buffer.length = PAGE_ALIGN(cam->v2f.fmt.
+ pix.sizeimage);
+ cam->frame[i].buffer.memory = V4L2_MEMORY_MMAP;
+ cam->frame[i].buffer.m.offset = cam->frame[i].paddress;
+ cam->frame[i].index = i;
+ }
+
+ return 0;
+}
+
+/*!
+ * Free frame buffers status
+ *
+ * @param cam Structure cam_data *
+ *
+ * @return none
+ */
+static void csi_free_frames(cam_data *cam)
+{
+ int i;
+
+ pr_debug("In MVC: %s\n", __func__);
+
+ for (i = 0; i < FRAME_NUM; i++)
+ cam->frame[i].buffer.flags = V4L2_BUF_FLAG_MAPPED;
+
+ cam->skip_frame = 0;
+ INIT_LIST_HEAD(&cam->ready_q);
+ INIT_LIST_HEAD(&cam->working_q);
+ INIT_LIST_HEAD(&cam->done_q);
+
+ return;
+}
+
+/*!
+ * Return the buffer status
+ *
+ * @param cam Structure cam_data *
+ * @param buf Structure v4l2_buffer *
+ *
+ * @return status 0 success, EINVAL failed.
+ */
+static int csi_v4l2_buffer_status(cam_data *cam, struct v4l2_buffer *buf)
+{
+ pr_debug("In MVC: %s\n", __func__);
+
+ if (buf->index < 0 || buf->index >= FRAME_NUM) {
+ pr_err("ERROR: v4l2 capture: %s buffers "
+ "not allocated\n", __func__);
+ return -EINVAL;
+ }
+
+ memcpy(buf, &(cam->frame[buf->index].buffer), sizeof(*buf));
+
+ return 0;
+}
+
+/*!
+ * Indicates whether the palette is supported.
+ *
+ * @param palette V4L2_PIX_FMT_RGB565, V4L2_PIX_FMT_UYVY or V4L2_PIX_FMT_YUV420
+ *
+ * @return 0 if failed
+ */
+static inline int valid_mode(u32 palette)
+{
+ return (palette == V4L2_PIX_FMT_RGB565) ||
+ (palette == V4L2_PIX_FMT_UYVY) || (palette == V4L2_PIX_FMT_YUV420);
+}
+
+/*!
+ * Start stream I/O
+ *
+ * @param cam structure cam_data *
+ *
+ * @return status 0 Success
+ */
+static int csi_streamon(cam_data *cam)
+{
+ struct mxc_v4l_frame *frame;
+
+ pr_debug("In MVC: %s\n", __func__);
+
+ if (NULL == cam) {
+ pr_err("ERROR: v4l2 capture: %s cam parameter is NULL\n",
+ __func__);
+ return -1;
+ }
+
+ /* move the frame from readyq to workingq */
+ if (list_empty(&cam->ready_q)) {
+ pr_err("ERROR: v4l2 capture: %s: "
+ "ready_q queue empty\n", __func__);
+ return -1;
+ }
+ frame = list_entry(cam->ready_q.next, struct mxc_v4l_frame, queue);
+ list_del(cam->ready_q.next);
+ list_add_tail(&frame->queue, &cam->working_q);
+ __raw_writel(cam->frame[frame->index].paddress, CSI_CSIDMASA_FB1);
+
+ if (list_empty(&cam->ready_q)) {
+ pr_err("ERROR: v4l2 capture: %s: "
+ "ready_q queue empty\n", __func__);
+ return -1;
+ }
+ frame = list_entry(cam->ready_q.next, struct mxc_v4l_frame, queue);
+ list_del(cam->ready_q.next);
+ list_add_tail(&frame->queue, &cam->working_q);
+ __raw_writel(cam->frame[frame->index].paddress, CSI_CSIDMASA_FB2);
+
+ cam->capture_pid = current->pid;
+ cam->capture_on = true;
+ csi_cap_image(cam);
+ csi_enable_int(1);
+
+ return 0;
+}
+
+/*!
+ * Stop stream I/O
+ *
+ * @param cam structure cam_data *
+ *
+ * @return status 0 Success
+ */
+static int csi_streamoff(cam_data *cam)
+{
+ unsigned int cr3;
+
+ pr_debug("In MVC: %s\n", __func__);
+
+ if (cam->capture_on == false)
+ return 0;
+
+ csi_disable_int();
+ cam->capture_on = false;
+
+ /* set CSI_CSIDMASA_FB1 and CSI_CSIDMASA_FB2 to default value */
+ __raw_writel(0, CSI_CSIDMASA_FB1);
+ __raw_writel(0, CSI_CSIDMASA_FB2);
+ cr3 = __raw_readl(CSI_CSICR3);
+ __raw_writel(cr3 | BIT_DMA_REFLASH_RFF, CSI_CSICR3);
+
+ csi_free_frames(cam);
+ csi_free_frame_buf(cam);
+
+ return 0;
+}
+
+/*!
+ * start the viewfinder job
+ *
+ * @param cam structure cam_data *
+ *
+ * @return status 0 Success
+ */
+static int start_preview(cam_data *cam)
+{
+ unsigned long fb_addr = (unsigned long)cam->v4l2_fb.base;
+
+ __raw_writel(fb_addr, CSI_CSIDMASA_FB1);
+ __raw_writel(fb_addr, CSI_CSIDMASA_FB2);
+ __raw_writel(__raw_readl(CSI_CSICR3) | BIT_DMA_REFLASH_RFF, CSI_CSICR3);
+
+ csi_enable_int(0);
+
+ return 0;
+}
+
+/*!
+ * shut down the viewfinder job
+ *
+ * @param cam structure cam_data *
+ *
+ * @return status 0 Success
+ */
+static int stop_preview(cam_data *cam)
+{
+ csi_disable_int();
+
+ /* set CSI_CSIDMASA_FB1 and CSI_CSIDMASA_FB2 to default value */
+ __raw_writel(0, CSI_CSIDMASA_FB1);
+ __raw_writel(0, CSI_CSIDMASA_FB2);
+ __raw_writel(__raw_readl(CSI_CSICR3) | BIT_DMA_REFLASH_RFF, CSI_CSICR3);
+
+ return 0;
+}
+
+/***************************************************************************
+ * VIDIOC Functions.
+ **************************************************************************/
+
+/*!
+ *
+ * @param cam structure cam_data *
+ *
+ * @param f structure v4l2_format *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int csi_v4l2_g_fmt(cam_data *cam, struct v4l2_format *f)
+{
+ int retval = 0;
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ pr_debug(" type is V4L2_BUF_TYPE_VIDEO_CAPTURE\n");
+ f->fmt.pix = cam->v2f.fmt.pix;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ pr_debug(" type is V4L2_BUF_TYPE_VIDEO_OVERLAY\n");
+ f->fmt.win = cam->win;
+ break;
+ default:
+ pr_debug(" type is invalid\n");
+ retval = -EINVAL;
+ }
+
+ pr_debug("End of %s: v2f pix widthxheight %d x %d\n",
+ __func__, cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height);
+
+ return retval;
+}
+
+/*!
+ * V4L2 - csi_v4l2_s_fmt function
+ *
+ * @param cam structure cam_data *
+ *
+ * @param f structure v4l2_format *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int csi_v4l2_s_fmt(cam_data *cam, struct v4l2_format *f)
+{
+ int retval = 0;
+ int size = 0;
+ int bytesperline = 0;
+ int *width, *height;
+
+ pr_debug("In MVC: %s\n", __func__);
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ pr_debug(" type=V4L2_BUF_TYPE_VIDEO_CAPTURE\n");
+ if (!valid_mode(f->fmt.pix.pixelformat)) {
+ pr_err("ERROR: v4l2 capture: %s: format "
+ "not supported\n", __func__);
+ return -EINVAL;
+ }
+
+ /* Handle case where size requested is larger than cuurent
+ * camera setting. */
+ if ((f->fmt.pix.width > cam->crop_bounds.width)
+ || (f->fmt.pix.height > cam->crop_bounds.height)) {
+ /* Need the logic here, calling vidioc_s_param if
+ * camera can change. */
+ pr_debug("csi_v4l2_s_fmt size changed\n");
+ }
+ if (cam->rotation >= IPU_ROTATE_90_RIGHT) {
+ height = &f->fmt.pix.width;
+ width = &f->fmt.pix.height;
+ } else {
+ width = &f->fmt.pix.width;
+ height = &f->fmt.pix.height;
+ }
+
+ if ((cam->crop_bounds.width / *width > 8) ||
+ ((cam->crop_bounds.width / *width == 8) &&
+ (cam->crop_bounds.width % *width))) {
+ *width = cam->crop_bounds.width / 8;
+ if (*width % 8)
+ *width += 8 - *width % 8;
+ pr_err("ERROR: v4l2 capture: width exceeds limit "
+ "resize to %d.\n", *width);
+ }
+
+ if ((cam->crop_bounds.height / *height > 8) ||
+ ((cam->crop_bounds.height / *height == 8) &&
+ (cam->crop_bounds.height % *height))) {
+ *height = cam->crop_bounds.height / 8;
+ if (*height % 8)
+ *height += 8 - *height % 8;
+ pr_err("ERROR: v4l2 capture: height exceeds limit "
+ "resize to %d.\n", *height);
+ }
+
+ switch (f->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_RGB565:
+ size = f->fmt.pix.width * f->fmt.pix.height * 2;
+ csi_set_16bit_imagpara(f->fmt.pix.width,
+ f->fmt.pix.height);
+ bytesperline = f->fmt.pix.width * 2;
+ break;
+ case V4L2_PIX_FMT_UYVY:
+ size = f->fmt.pix.width * f->fmt.pix.height * 2;
+ csi_set_16bit_imagpara(f->fmt.pix.width,
+ f->fmt.pix.height);
+ bytesperline = f->fmt.pix.width * 2;
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ size = f->fmt.pix.width * f->fmt.pix.height * 3 / 2;
+ csi_set_12bit_imagpara(f->fmt.pix.width,
+ f->fmt.pix.height);
+ bytesperline = f->fmt.pix.width;
+ break;
+ case V4L2_PIX_FMT_YUV422P:
+ case V4L2_PIX_FMT_RGB24:
+ case V4L2_PIX_FMT_BGR24:
+ case V4L2_PIX_FMT_BGR32:
+ case V4L2_PIX_FMT_RGB32:
+ case V4L2_PIX_FMT_NV12:
+ default:
+ pr_debug(" case not supported\n");
+ break;
+ }
+
+ if (f->fmt.pix.bytesperline < bytesperline)
+ f->fmt.pix.bytesperline = bytesperline;
+ else
+ bytesperline = f->fmt.pix.bytesperline;
+
+ if (f->fmt.pix.sizeimage < size)
+ f->fmt.pix.sizeimage = size;
+ else
+ size = f->fmt.pix.sizeimage;
+
+ cam->v2f.fmt.pix = f->fmt.pix;
+
+ if (cam->v2f.fmt.pix.priv != 0) {
+ if (copy_from_user(&cam->offset,
+ (void *)cam->v2f.fmt.pix.priv,
+ sizeof(cam->offset))) {
+ retval = -EFAULT;
+ break;
+ }
+ }
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ pr_debug(" type=V4L2_BUF_TYPE_VIDEO_OVERLAY\n");
+ cam->win = f->fmt.win;
+ break;
+ default:
+ retval = -EINVAL;
+ }
+
+ pr_debug("End of %s: v2f pix widthxheight %d x %d\n",
+ __func__, cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height);
+
+ return retval;
+}
+
+/*!
+ * V4L2 - csi_v4l2_s_param function
+ * Allows setting of capturemode and frame rate.
+ *
+ * @param cam structure cam_data *
+ * @param parm structure v4l2_streamparm *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int csi_v4l2_s_param(cam_data *cam, struct v4l2_streamparm *parm)
+{
+ struct v4l2_ifparm ifparm;
+ struct v4l2_format cam_fmt;
+ struct v4l2_streamparm currentparm;
+ int err = 0;
+
+ pr_debug("In %s\n", __func__);
+
+ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ pr_err(KERN_ERR "%s invalid type\n", __func__);
+ return -EINVAL;
+ }
+
+ /* Stop the viewfinder */
+ if (cam->overlay_on == true)
+ stop_preview(cam);
+
+ currentparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ /* First check that this device can support the changes requested. */
+ err = vidioc_int_g_parm(cam->sensor, &currentparm);
+ if (err) {
+ pr_err("%s: vidioc_int_g_parm returned an error %d\n",
+ __func__, err);
+ goto exit;
+ }
+
+ pr_debug(" Current capabilities are %x\n",
+ currentparm.parm.capture.capability);
+ pr_debug(" Current capturemode is %d change to %d\n",
+ currentparm.parm.capture.capturemode,
+ parm->parm.capture.capturemode);
+ pr_debug(" Current framerate is %d change to %d\n",
+ currentparm.parm.capture.timeperframe.denominator,
+ parm->parm.capture.timeperframe.denominator);
+
+ err = vidioc_int_s_parm(cam->sensor, parm);
+ if (err) {
+ pr_err("%s: vidioc_int_s_parm returned an error %d\n",
+ __func__, err);
+ goto exit;
+ }
+
+ vidioc_int_g_ifparm(cam->sensor, &ifparm);
+ cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ pr_debug(" g_fmt_cap returns widthxheight of input as %d x %d\n",
+ cam_fmt.fmt.pix.width, cam_fmt.fmt.pix.height);
+
+exit:
+ return err;
+}
+
+/*!
+ * Dequeue one V4L capture buffer
+ *
+ * @param cam structure cam_data *
+ * @param buf structure v4l2_buffer *
+ *
+ * @return status 0 success, EINVAL invalid frame number
+ * ETIME timeout, ERESTARTSYS interrupted by user
+ */
+static int csi_v4l_dqueue(cam_data *cam, struct v4l2_buffer *buf)
+{
+ int retval = 0;
+ struct mxc_v4l_frame *frame;
+ unsigned long lock_flags;
+
+ if (!wait_event_interruptible_timeout(cam->enc_queue,
+ cam->enc_counter != 0, 10 * HZ)) {
+ pr_err("ERROR: v4l2 capture: mxc_v4l_dqueue timeout "
+ "enc_counter %x\n", cam->enc_counter);
+ return -ETIME;
+ } else if (signal_pending(current)) {
+ pr_err("ERROR: v4l2 capture: mxc_v4l_dqueue() "
+ "interrupt received\n");
+ return -ERESTARTSYS;
+ }
+
+ spin_lock_irqsave(&cam->dqueue_int_lock, lock_flags);
+
+ cam->enc_counter--;
+
+ frame = list_entry(cam->done_q.next, struct mxc_v4l_frame, queue);
+ list_del(cam->done_q.next);
+
+ if (frame->buffer.flags & V4L2_BUF_FLAG_DONE) {
+ frame->buffer.flags &= ~V4L2_BUF_FLAG_DONE;
+ } else if (frame->buffer.flags & V4L2_BUF_FLAG_QUEUED) {
+ pr_err("ERROR: v4l2 capture: VIDIOC_DQBUF: "
+ "Buffer not filled.\n");
+ frame->buffer.flags &= ~V4L2_BUF_FLAG_QUEUED;
+ retval = -EINVAL;
+ } else if ((frame->buffer.flags & 0x7) == V4L2_BUF_FLAG_MAPPED) {
+ pr_err("ERROR: v4l2 capture: VIDIOC_DQBUF: "
+ "Buffer not queued.\n");
+ retval = -EINVAL;
+ }
+
+ spin_unlock_irqrestore(&cam->dqueue_int_lock, lock_flags);
+
+ buf->bytesused = cam->v2f.fmt.pix.sizeimage;
+ buf->index = frame->index;
+ buf->flags = frame->buffer.flags;
+ buf->m = cam->frame[frame->index].buffer.m;
+
+ return retval;
+}
+
+/*!
+ * V4L interface - open function
+ *
+ * @param file structure file *
+ *
+ * @return status 0 success, ENODEV invalid device instance,
+ * ENODEV timeout, ERESTARTSYS interrupted by user
+ */
+static int csi_v4l_open(struct file *file)
+{
+ struct v4l2_ifparm ifparm;
+ struct v4l2_format cam_fmt;
+ struct video_device *dev = video_devdata(file);
+ cam_data *cam = video_get_drvdata(dev);
+ int err = 0;
+
+ pr_debug(" device name is %s\n", dev->name);
+
+ if (!cam) {
+ pr_err("ERROR: v4l2 capture: Internal error, "
+ "cam_data not found!\n");
+ return -EBADF;
+ }
+
+ down(&cam->busy_lock);
+ err = 0;
+ if (signal_pending(current))
+ goto oops;
+
+ if (cam->open_count++ == 0) {
+ wait_event_interruptible(cam->power_queue,
+ cam->low_power == false);
+
+ cam->enc_counter = 0;
+ cam->skip_frame = 0;
+ INIT_LIST_HEAD(&cam->ready_q);
+ INIT_LIST_HEAD(&cam->working_q);
+ INIT_LIST_HEAD(&cam->done_q);
+
+ vidioc_int_g_ifparm(cam->sensor, &ifparm);
+
+ cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ csi_enable_mclk(CSI_MCLK_I2C, true, true);
+ vidioc_int_init(cam->sensor);
+ }
+
+ file->private_data = dev;
+
+oops:
+ up(&cam->busy_lock);
+ return err;
+}
+
+/*!
+ * V4L interface - close function
+ *
+ * @param file struct file *
+ *
+ * @return 0 success
+ */
+static int csi_v4l_close(struct file *file)
+{
+ struct video_device *dev = video_devdata(file);
+ int err = 0;
+ cam_data *cam = video_get_drvdata(dev);
+
+ pr_debug("In MVC:%s\n", __func__);
+
+ if (!cam) {
+ pr_err("ERROR: v4l2 capture: Internal error, "
+ "cam_data not found!\n");
+ return -EBADF;
+ }
+
+ /* for the case somebody hit the ctrl C */
+ if (cam->overlay_pid == current->pid) {
+ err = stop_preview(cam);
+ cam->overlay_on = false;
+ }
+
+ if (--cam->open_count == 0) {
+ wait_event_interruptible(cam->power_queue,
+ cam->low_power == false);
+ file->private_data = NULL;
+ csi_enable_mclk(CSI_MCLK_I2C, false, false);
+ }
+
+ return err;
+}
+
+/*
+ * V4L interface - read function
+ *
+ * @param file struct file *
+ * @param read buf char *
+ * @param count size_t
+ * @param ppos structure loff_t *
+ *
+ * @return bytes read
+ */
+static ssize_t csi_v4l_read(struct file *file, char *buf, size_t count,
+ loff_t *ppos)
+{
+ int err = 0;
+ struct video_device *dev = video_devdata(file);
+ cam_data *cam = video_get_drvdata(dev);
+
+ if (down_interruptible(&cam->busy_lock))
+ return -EINTR;
+
+ /* Stop the viewfinder */
+ if (cam->overlay_on == true)
+ stop_preview(cam);
+
+ if (cam->still_buf_vaddr == NULL) {
+ cam->still_buf_vaddr = dma_alloc_coherent(0,
+ PAGE_ALIGN
+ (cam->v2f.fmt.
+ pix.sizeimage),
+ &cam->
+ still_buf[0],
+ GFP_DMA | GFP_KERNEL);
+ if (cam->still_buf_vaddr == NULL) {
+ pr_err("alloc dma memory failed\n");
+ return -ENOMEM;
+ }
+ cam->still_counter = 0;
+ __raw_writel(cam->still_buf[0], CSI_CSIDMASA_FB2);
+ __raw_writel(cam->still_buf[0], CSI_CSIDMASA_FB1);
+ __raw_writel(__raw_readl(CSI_CSICR3) | BIT_DMA_REFLASH_RFF,
+ CSI_CSICR3);
+ __raw_writel(__raw_readl(CSI_CSISR), CSI_CSISR);
+ __raw_writel(__raw_readl(CSI_CSICR3) | BIT_FRMCNT_RST,
+ CSI_CSICR3);
+ csi_enable_int(1);
+ }
+
+ wait_event_interruptible(cam->still_queue, cam->still_counter);
+ csi_disable_int();
+ err = copy_to_user(buf, cam->still_buf_vaddr,
+ cam->v2f.fmt.pix.sizeimage);
+
+ if (cam->still_buf_vaddr != NULL) {
+ dma_free_coherent(0, PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage),
+ cam->still_buf_vaddr, cam->still_buf[0]);
+ cam->still_buf[0] = 0;
+ cam->still_buf_vaddr = NULL;
+ }
+
+ if (cam->overlay_on == true)
+ start_preview(cam);
+
+ up(&cam->busy_lock);
+ if (err < 0)
+ return err;
+
+ return cam->v2f.fmt.pix.sizeimage - err;
+}
+
+/*!
+ * V4L interface - ioctl function
+ *
+ * @param file struct file*
+ *
+ * @param ioctlnr unsigned int
+ *
+ * @param arg void*
+ *
+ * @return 0 success, ENODEV for invalid device instance,
+ * -1 for other errors.
+ */
+static long csi_v4l_do_ioctl(struct file *file,
+ unsigned int ioctlnr, void *arg)
+{
+ struct video_device *dev = video_devdata(file);
+ cam_data *cam = video_get_drvdata(dev);
+ int retval = 0;
+ unsigned long lock_flags;
+
+ pr_debug("In MVC: %s, %x\n", __func__, ioctlnr);
+ wait_event_interruptible(cam->power_queue, cam->low_power == false);
+ /* make this _really_ smp-safe */
+ if (down_interruptible(&cam->busy_lock))
+ return -EBUSY;
+
+ switch (ioctlnr) {
+ /*!
+ * V4l2 VIDIOC_G_FMT ioctl
+ */
+ case VIDIOC_G_FMT:{
+ struct v4l2_format *gf = arg;
+ pr_debug(" case VIDIOC_G_FMT\n");
+ retval = csi_v4l2_g_fmt(cam, gf);
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_S_FMT ioctl
+ */
+ case VIDIOC_S_FMT:{
+ struct v4l2_format *sf = arg;
+ pr_debug(" case VIDIOC_S_FMT\n");
+ retval = csi_v4l2_s_fmt(cam, sf);
+ vidioc_int_s_fmt_cap(cam->sensor, sf);
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_OVERLAY ioctl
+ */
+ case VIDIOC_OVERLAY:{
+ int *on = arg;
+ pr_debug(" case VIDIOC_OVERLAY\n");
+ if (*on) {
+ cam->overlay_on = true;
+ cam->overlay_pid = current->pid;
+ start_preview(cam);
+ }
+ if (!*on) {
+ stop_preview(cam);
+ cam->overlay_on = false;
+ }
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_G_FBUF ioctl
+ */
+ case VIDIOC_G_FBUF:{
+ struct v4l2_framebuffer *fb = arg;
+ *fb = cam->v4l2_fb;
+ fb->capability = V4L2_FBUF_CAP_EXTERNOVERLAY;
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_S_FBUF ioctl
+ */
+ case VIDIOC_S_FBUF:{
+ struct v4l2_framebuffer *fb = arg;
+ cam->v4l2_fb = *fb;
+ break;
+ }
+
+ case VIDIOC_G_PARM:{
+ struct v4l2_streamparm *parm = arg;
+ pr_debug(" case VIDIOC_G_PARM\n");
+ vidioc_int_g_parm(cam->sensor, parm);
+ break;
+ }
+
+ case VIDIOC_S_PARM:{
+ struct v4l2_streamparm *parm = arg;
+ pr_debug(" case VIDIOC_S_PARM\n");
+ retval = csi_v4l2_s_param(cam, parm);
+ break;
+ }
+
+ case VIDIOC_QUERYCAP:{
+ struct v4l2_capability *cap = arg;
+ pr_debug(" case VIDIOC_QUERYCAP\n");
+ strcpy(cap->driver, "csi_v4l2");
+ cap->version = KERNEL_VERSION(0, 1, 11);
+ cap->capabilities = V4L2_CAP_VIDEO_OVERLAY |
+ V4L2_CAP_VIDEO_OUTPUT_OVERLAY | V4L2_CAP_READWRITE;
+ cap->card[0] = '\0';
+ cap->bus_info[0] = '\0';
+ break;
+ }
+
+ case VIDIOC_S_CROP:
+ pr_debug(" case not supported\n");
+ break;
+
+ case VIDIOC_REQBUFS: {
+ struct v4l2_requestbuffers *req = arg;
+ pr_debug(" case VIDIOC_REQBUFS\n");
+
+ if (req->count > FRAME_NUM) {
+ pr_err("ERROR: v4l2 capture: VIDIOC_REQBUFS: "
+ "not enough buffers\n");
+ req->count = FRAME_NUM;
+ }
+
+ if ((req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
+ (req->memory != V4L2_MEMORY_MMAP)) {
+ pr_err("ERROR: v4l2 capture: VIDIOC_REQBUFS: "
+ "wrong buffer type\n");
+ retval = -EINVAL;
+ break;
+ }
+
+ csi_streamoff(cam);
+ csi_free_frame_buf(cam);
+ cam->skip_frame = 0;
+ INIT_LIST_HEAD(&cam->ready_q);
+ INIT_LIST_HEAD(&cam->working_q);
+ INIT_LIST_HEAD(&cam->done_q);
+ retval = csi_allocate_frame_buf(cam, req->count);
+ break;
+ }
+
+ case VIDIOC_QUERYBUF: {
+ struct v4l2_buffer *buf = arg;
+ int index = buf->index;
+ pr_debug(" case VIDIOC_QUERYBUF\n");
+
+ if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ retval = -EINVAL;
+ break;
+ }
+
+ memset(buf, 0, sizeof(buf));
+ buf->index = index;
+ retval = csi_v4l2_buffer_status(cam, buf);
+ break;
+ }
+
+ case VIDIOC_QBUF: {
+ struct v4l2_buffer *buf = arg;
+ int index = buf->index;
+ pr_debug(" case VIDIOC_QBUF\n");
+
+ spin_lock_irqsave(&cam->queue_int_lock, lock_flags);
+ cam->frame[index].buffer.m.offset = buf->m.offset;
+ if ((cam->frame[index].buffer.flags & 0x7) ==
+ V4L2_BUF_FLAG_MAPPED) {
+ cam->frame[index].buffer.flags |= V4L2_BUF_FLAG_QUEUED;
+ if (cam->skip_frame > 0) {
+ list_add_tail(&cam->frame[index].queue,
+ &cam->working_q);
+ cam->skip_frame = 0;
+
+ if (cam->ping_pong_csi == 1) {
+ __raw_writel(cam->frame[index].paddress,
+ CSI_CSIDMASA_FB1);
+ } else {
+ __raw_writel(cam->frame[index].paddress,
+ CSI_CSIDMASA_FB2);
+ }
+ } else {
+ list_add_tail(&cam->frame[index].queue,
+ &cam->ready_q);
+ }
+ } else if (cam->frame[index].buffer.flags &
+ V4L2_BUF_FLAG_QUEUED) {
+ pr_err("ERROR: v4l2 capture: VIDIOC_QBUF: "
+ "buffer already queued\n");
+ retval = -EINVAL;
+ } else if (cam->frame[index].buffer.
+ flags & V4L2_BUF_FLAG_DONE) {
+ pr_err("ERROR: v4l2 capture: VIDIOC_QBUF: "
+ "overwrite done buffer.\n");
+ cam->frame[index].buffer.flags &=
+ ~V4L2_BUF_FLAG_DONE;
+ cam->frame[index].buffer.flags |=
+ V4L2_BUF_FLAG_QUEUED;
+ retval = -EINVAL;
+ }
+ buf->flags = cam->frame[index].buffer.flags;
+ spin_unlock_irqrestore(&cam->queue_int_lock, lock_flags);
+
+ break;
+ }
+
+ case VIDIOC_DQBUF: {
+ struct v4l2_buffer *buf = arg;
+ pr_debug(" case VIDIOC_DQBUF\n");
+
+ retval = csi_v4l_dqueue(cam, buf);
+
+ break;
+ }
+
+ case VIDIOC_STREAMON: {
+ pr_debug(" case VIDIOC_STREAMON\n");
+ retval = csi_streamon(cam);
+ break;
+ }
+
+ case VIDIOC_STREAMOFF: {
+ pr_debug(" case VIDIOC_STREAMOFF\n");
+ retval = csi_streamoff(cam);
+ break;
+ }
+
+ case VIDIOC_S_CTRL:
+ case VIDIOC_G_STD:
+ case VIDIOC_G_OUTPUT:
+ case VIDIOC_S_OUTPUT:
+ case VIDIOC_ENUMSTD:
+ case VIDIOC_G_CROP:
+ case VIDIOC_CROPCAP:
+ case VIDIOC_S_STD:
+ case VIDIOC_G_CTRL:
+ case VIDIOC_ENUM_FMT:
+ case VIDIOC_TRY_FMT:
+ case VIDIOC_QUERYCTRL:
+ case VIDIOC_ENUMINPUT:
+ case VIDIOC_G_INPUT:
+ case VIDIOC_S_INPUT:
+ case VIDIOC_G_TUNER:
+ case VIDIOC_S_TUNER:
+ case VIDIOC_G_FREQUENCY:
+ case VIDIOC_S_FREQUENCY:
+ case VIDIOC_ENUMOUTPUT:
+ default:
+ pr_debug(" case not supported\n");
+ retval = -EINVAL;
+ break;
+ }
+
+ up(&cam->busy_lock);
+ return retval;
+}
+
+/*
+ * V4L interface - ioctl function
+ *
+ * @return None
+ */
+static long csi_v4l_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(file, cmd, arg, csi_v4l_do_ioctl);
+}
+
+/*!
+ * V4L interface - mmap function
+ *
+ * @param file structure file *
+ *
+ * @param vma structure vm_area_struct *
+ *
+ * @return status 0 Success, EINTR busy lock error, ENOBUFS remap_page error
+ */
+static int csi_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct video_device *dev = video_devdata(file);
+ unsigned long size;
+ int res = 0;
+ cam_data *cam = video_get_drvdata(dev);
+
+ pr_debug("%s\n", __func__);
+ pr_debug("\npgoff=0x%lx, start=0x%lx, end=0x%lx\n",
+ vma->vm_pgoff, vma->vm_start, vma->vm_end);
+
+ /* make this _really_ smp-safe */
+ if (down_interruptible(&cam->busy_lock))
+ return -EINTR;
+
+ size = vma->vm_end - vma->vm_start;
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ if (remap_pfn_range(vma, vma->vm_start,
+ vma->vm_pgoff, size, vma->vm_page_prot)) {
+ pr_err("ERROR: v4l2 capture: %s : "
+ "remap_pfn_range failed\n", __func__);
+ res = -ENOBUFS;
+ goto csi_mmap_exit;
+ }
+
+ vma->vm_flags &= ~VM_IO; /* using shared anonymous pages */
+
+csi_mmap_exit:
+ up(&cam->busy_lock);
+ return res;
+}
+
+/*!
+ * This structure defines the functions to be called in this driver.
+ */
+static struct v4l2_file_operations csi_v4l_fops = {
+ .owner = THIS_MODULE,
+ .open = csi_v4l_open,
+ .release = csi_v4l_close,
+ .read = csi_v4l_read,
+ .ioctl = csi_v4l_ioctl,
+ .mmap = csi_mmap,
+};
+
+static struct video_device csi_v4l_template = {
+ .name = "Mx25 Camera",
+ .fops = &csi_v4l_fops,
+ .release = video_device_release,
+};
+
+/*!
+ * This function can be used to release any platform data on closing.
+ */
+static void camera_platform_release(struct device *device)
+{
+}
+
+/*! Device Definition for csi v4l2 device */
+static struct platform_device csi_v4l2_devices = {
+ .name = "csi_v4l2",
+ .dev = {
+ .release = camera_platform_release,
+ },
+ .id = 0,
+};
+
+/*!
+ * initialize cam_data structure
+ *
+ * @param cam structure cam_data *
+ *
+ * @return status 0 Success
+ */
+static void init_camera_struct(cam_data *cam)
+{
+ pr_debug("In MVC: %s\n", __func__);
+
+ /* Default everything to 0 */
+ memset(cam, 0, sizeof(cam_data));
+
+ init_MUTEX(&cam->param_lock);
+ init_MUTEX(&cam->busy_lock);
+
+ cam->video_dev = video_device_alloc();
+ if (cam->video_dev == NULL)
+ return;
+
+ *(cam->video_dev) = csi_v4l_template;
+
+ video_set_drvdata(cam->video_dev, cam);
+ dev_set_drvdata(&csi_v4l2_devices.dev, (void *)cam);
+ cam->video_dev->minor = -1;
+
+ init_waitqueue_head(&cam->enc_queue);
+ init_waitqueue_head(&cam->still_queue);
+
+ cam->streamparm.parm.capture.capturemode = 0;
+
+ cam->standard.index = 0;
+ cam->standard.id = V4L2_STD_UNKNOWN;
+ cam->standard.frameperiod.denominator = 30;
+ cam->standard.frameperiod.numerator = 1;
+ cam->standard.framelines = 480;
+ cam->standard_autodetect = true;
+ cam->streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ cam->streamparm.parm.capture.timeperframe = cam->standard.frameperiod;
+ cam->streamparm.parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+ cam->overlay_on = false;
+ cam->capture_on = false;
+ cam->skip_frame = 0;
+ cam->v4l2_fb.flags = V4L2_FBUF_FLAG_OVERLAY;
+
+ cam->v2f.fmt.pix.sizeimage = 480 * 640 * 2;
+ cam->v2f.fmt.pix.bytesperline = 640 * 2;
+ cam->v2f.fmt.pix.width = 640;
+ cam->v2f.fmt.pix.height = 480;
+ cam->v2f.fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
+ cam->win.w.width = 160;
+ cam->win.w.height = 160;
+ cam->win.w.left = 0;
+ cam->win.w.top = 0;
+ cam->still_counter = 0;
+
+ cam->enc_callback = camera_callback;
+ csi_start_callback(cam);
+ init_waitqueue_head(&cam->power_queue);
+ spin_lock_init(&cam->queue_int_lock);
+ spin_lock_init(&cam->dqueue_int_lock);
+}
+
+/*!
+ * camera_power function
+ * Turns Sensor power On/Off
+ *
+ * @param cam cam data struct
+ * @param cameraOn true to turn camera on, false to turn off power.
+ *
+ * @return status
+ */
+static u8 camera_power(cam_data *cam, bool cameraOn)
+{
+ pr_debug("In MVC: %s on=%d\n", __func__, cameraOn);
+
+ if (cameraOn == true) {
+ csi_enable_mclk(CSI_MCLK_I2C, true, true);
+ vidioc_int_s_power(cam->sensor, 1);
+ } else {
+ csi_enable_mclk(CSI_MCLK_I2C, false, false);
+ vidioc_int_s_power(cam->sensor, 0);
+ }
+ return 0;
+}
+
+/*!
+ * This function is called to put the sensor in a low power state.
+ * Refer to the document driver-model/driver.txt in the kernel source tree
+ * for more information.
+ *
+ * @param pdev the device structure used to give information on which I2C
+ * to suspend
+ * @param state the power state the device is entering
+ *
+ * @return The function returns 0 on success and -1 on failure.
+ */
+static int csi_v4l2_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ cam_data *cam = platform_get_drvdata(pdev);
+
+ pr_debug("In MVC: %s\n", __func__);
+
+ if (cam == NULL)
+ return -1;
+
+ cam->low_power = true;
+
+ if (cam->overlay_on == true)
+ stop_preview(cam);
+
+ camera_power(cam, false);
+
+ return 0;
+}
+
+/*!
+ * This function is called to bring the sensor back from a low power state.
+ * Refer to the document driver-model/driver.txt in the kernel source tree
+ * for more information.
+ *
+ * @param pdev the device structure
+ *
+ * @return The function returns 0 on success and -1 on failure
+ */
+static int csi_v4l2_resume(struct platform_device *pdev)
+{
+ cam_data *cam = platform_get_drvdata(pdev);
+
+ pr_debug("In MVC: %s\n", __func__);
+
+ if (cam == NULL)
+ return -1;
+
+ cam->low_power = false;
+ wake_up_interruptible(&cam->power_queue);
+ camera_power(cam, true);
+
+ if (cam->overlay_on == true)
+ start_preview(cam);
+
+ return 0;
+}
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver csi_v4l2_driver = {
+ .driver = {
+ .name = "csi_v4l2",
+ },
+ .probe = NULL,
+ .remove = NULL,
+#ifdef CONFIG_PM
+ .suspend = csi_v4l2_suspend,
+ .resume = csi_v4l2_resume,
+#endif
+ .shutdown = NULL,
+};
+
+/*!
+ * Initializes the camera driver.
+ */
+static int csi_v4l2_master_attach(struct v4l2_int_device *slave)
+{
+ cam_data *cam = slave->u.slave->master->priv;
+ struct v4l2_format cam_fmt;
+
+ pr_debug("In MVC: %s\n", __func__);
+ pr_debug(" slave.name = %s\n", slave->name);
+ pr_debug(" master.name = %s\n", slave->u.slave->master->name);
+
+ cam->sensor = slave;
+ if (slave == NULL) {
+ pr_err("ERROR: v4l2 capture: slave parameter not valid.\n");
+ return -1;
+ }
+
+ csi_enable_mclk(CSI_MCLK_I2C, true, true);
+ vidioc_int_dev_init(slave);
+ csi_enable_mclk(CSI_MCLK_I2C, false, false);
+ cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ /* Used to detect TV in (type 1) vs. camera (type 0) */
+ cam->device_type = cam_fmt.fmt.pix.priv;
+
+ pr_debug("End of %s: v2f pix widthxheight %d x %d\n",
+ __func__, cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height);
+
+ return 0;
+}
+
+/*!
+ * Disconnects the camera driver.
+ */
+static void csi_v4l2_master_detach(struct v4l2_int_device *slave)
+{
+ pr_debug("In MVC: %s\n", __func__);
+
+ vidioc_int_dev_exit(slave);
+}
+
+/*!
+ * Entry point for the V4L2
+ *
+ * @return Error code indicating success or failure
+ */
+static __init int camera_init(void)
+{
+ u8 err = 0;
+
+ /* Register the device driver structure. */
+ err = platform_driver_register(&csi_v4l2_driver);
+ if (err != 0) {
+ pr_err("ERROR: v4l2 capture:camera_init: "
+ "platform_driver_register failed.\n");
+ return err;
+ }
+
+ /* Create g_cam and initialize it. */
+ g_cam = kmalloc(sizeof(cam_data), GFP_KERNEL);
+ if (g_cam == NULL) {
+ pr_err("ERROR: v4l2 capture: failed to register camera\n");
+ platform_driver_unregister(&csi_v4l2_driver);
+ return -1;
+ }
+ init_camera_struct(g_cam);
+
+ /* Set up the v4l2 device and register it */
+ csi_v4l2_int_device.priv = g_cam;
+ /* This function contains a bug that won't let this be rmmod'd. */
+ v4l2_int_device_register(&csi_v4l2_int_device);
+
+ /* Register the platform device */
+ err = platform_device_register(&csi_v4l2_devices);
+ if (err != 0) {
+ pr_err("ERROR: v4l2 capture: camera_init: "
+ "platform_device_register failed.\n");
+ platform_driver_unregister(&csi_v4l2_driver);
+ kfree(g_cam);
+ g_cam = NULL;
+ return err;
+ }
+
+ /* register v4l video device */
+ if (video_register_device(g_cam->video_dev, VFL_TYPE_GRABBER, video_nr)
+ == -1) {
+ platform_device_unregister(&csi_v4l2_devices);
+ platform_driver_unregister(&csi_v4l2_driver);
+ kfree(g_cam);
+ g_cam = NULL;
+ pr_err("ERROR: v4l2 capture: video_register_device failed\n");
+ return -1;
+ }
+ pr_debug(" Video device registered: %s #%d\n",
+ g_cam->video_dev->name, g_cam->video_dev->minor);
+
+ return err;
+}
+
+/*!
+ * Exit and cleanup for the V4L2
+ */
+static void __exit camera_exit(void)
+{
+ pr_debug("In MVC: %s\n", __func__);
+
+ if (g_cam->open_count) {
+ pr_err("ERROR: v4l2 capture:camera open "
+ "-- setting ops to NULL\n");
+ } else {
+ pr_info("V4L2 freeing image input device\n");
+ v4l2_int_device_unregister(&csi_v4l2_int_device);
+ csi_stop_callback(g_cam);
+ video_unregister_device(g_cam->video_dev);
+ platform_driver_unregister(&csi_v4l2_driver);
+ platform_device_unregister(&csi_v4l2_devices);
+
+ kfree(g_cam);
+ g_cam = NULL;
+ }
+}
+
+module_init(camera_init);
+module_exit(camera_exit);
+
+module_param(video_nr, int, 0444);
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("V4L2 capture driver for Mx25 based cameras");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("video");
diff --git a/drivers/media/video/mxc/capture/emma_mt9v111.c b/drivers/media/video/mxc/capture/emma_mt9v111.c
new file mode 100644
index 000000000000..73e9bba36d1e
--- /dev/null
+++ b/drivers/media/video/mxc/capture/emma_mt9v111.c
@@ -0,0 +1,679 @@
+/*
+ * Copyright 2004-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file mt9v111.c
+ *
+ * @brief mt9v111 camera driver functions
+ *
+ * @ingroup Camera
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/clk.h>
+#include "mxc_v4l2_capture.h"
+#include "mt9v111.h"
+
+#ifdef MT9V111_DEBUG
+static u16 testpattern;
+#endif
+
+static sensor_interface *interface_param;
+static mt9v111_conf mt9v111_device;
+static int reset_frame_rate = 30;
+
+#define MT9V111_FRAME_RATE_NUM 20
+
+static mt9v111_image_format format[2] = {
+ {
+ .index = 0,
+ .width = 640,
+ .height = 480,
+ },
+ {
+ .index = 1,
+ .width = 352,
+ .height = 288,
+ },
+};
+
+static int mt9v111_attach(struct i2c_adapter *adapter);
+static int mt9v111_detach(struct i2c_client *client);
+
+static struct i2c_driver mt9v111_i2c_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "MT9V111 Client",
+ },
+ .attach_adapter = mt9v111_attach,
+ .detach_client = mt9v111_detach,
+};
+
+static struct i2c_client mt9v111_i2c_client = {
+ .name = "mt9v111 I2C dev",
+ .addr = MT9V111_I2C_ADDRESS,
+ .driver = &mt9v111_i2c_driver,
+};
+
+/*
+ * Function definitions
+ */
+
+#ifdef MT9V111_DEBUG
+static inline int mt9v111_read_reg(u8 reg)
+{
+ int val = i2c_smbus_read_word_data(&mt9v111_i2c_client, reg);
+ if (val != -1)
+ val = cpu_to_be16(val);
+ return val;
+}
+#endif
+
+static inline int mt9v111_write_reg(u8 reg, u16 val)
+{
+ pr_debug("write reg %x val %x.\n", reg, val);
+ return i2c_smbus_write_word_data(&mt9v111_i2c_client, reg,
+ cpu_to_be16(val));
+}
+
+/*!
+ * Initialize mt9v111_sensor_lib
+ * Libarary for Sensor configuration through I2C
+ *
+ * @param coreReg Core Registers
+ * @param ifpReg IFP Register
+ *
+ * @return status
+ */
+static u8 mt9v111_sensor_lib(mt9v111_coreReg * coreReg, mt9v111_IFPReg * ifpReg)
+{
+ u8 reg;
+ u16 data;
+ u8 error = 0;
+
+ /*
+ * setup to IFP registers
+ */
+ reg = MT9V111I_ADDR_SPACE_SEL;
+ data = ifpReg->addrSpaceSel;
+ mt9v111_write_reg(reg, data);
+
+ /* Operation Mode Control */
+ reg = MT9V111I_MODE_CONTROL;
+ data = ifpReg->modeControl;
+ mt9v111_write_reg(reg, data);
+
+ /* Output format */
+ reg = MT9V111I_FORMAT_CONTROL;
+ data = ifpReg->formatControl; /* Set bit 12 */
+ mt9v111_write_reg(reg, data);
+
+ /* AE limit 4 */
+ reg = MT9V111I_SHUTTER_WIDTH_LIMIT_AE;
+ data = ifpReg->gainLimitAE;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111I_OUTPUT_FORMAT_CTRL2;
+ data = ifpReg->outputFormatCtrl2;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111I_AE_SPEED;
+ data = ifpReg->AESpeed;
+ mt9v111_write_reg(reg, data);
+
+ /* output image size */
+ reg = MT9V111i_H_PAN;
+ data = 0x8000 | ifpReg->HPan;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111i_H_ZOOM;
+ data = 0x8000 | ifpReg->HZoom;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111i_H_SIZE;
+ data = 0x8000 | ifpReg->HSize;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111i_V_PAN;
+ data = 0x8000 | ifpReg->VPan;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111i_V_ZOOM;
+ data = 0x8000 | ifpReg->VZoom;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111i_V_SIZE;
+ data = 0x8000 | ifpReg->VSize;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111i_H_PAN;
+ data = ~0x8000 & ifpReg->HPan;
+ mt9v111_write_reg(reg, data);
+#if 0
+ reg = MT9V111I_UPPER_SHUTTER_DELAY_LIM;
+ data = ifpReg->upperShutterDelayLi;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111I_SHUTTER_60;
+ data = ifpReg->shutter_width_60;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111I_SEARCH_FLICK_60;
+ data = ifpReg->search_flicker_60;
+ mt9v111_write_reg(reg, data);
+#endif
+
+ /*
+ * setup to sensor core registers
+ */
+ reg = MT9V111I_ADDR_SPACE_SEL;
+ data = coreReg->addressSelect;
+ mt9v111_write_reg(reg, data);
+
+ /* enable changes and put the Sync bit on */
+ reg = MT9V111S_OUTPUT_CTRL;
+ data = MT9V111S_OUTCTRL_SYNC | MT9V111S_OUTCTRL_CHIP_ENABLE | 0x3000;
+ mt9v111_write_reg(reg, data);
+
+ /* min PIXCLK - Default */
+ reg = MT9V111S_PIXEL_CLOCK_SPEED;
+ data = coreReg->pixelClockSpeed;
+ mt9v111_write_reg(reg, data);
+
+ /* Setup image flipping / Dark rows / row/column skip */
+ reg = MT9V111S_READ_MODE;
+ data = coreReg->readMode;
+ mt9v111_write_reg(reg, data);
+
+ /*zoom 0 */
+ reg = MT9V111S_DIGITAL_ZOOM;
+ data = coreReg->digitalZoom;
+ mt9v111_write_reg(reg, data);
+
+ /* min H-blank */
+ reg = MT9V111S_HOR_BLANKING;
+ data = coreReg->horizontalBlanking;
+ mt9v111_write_reg(reg, data);
+
+ /* min V-blank */
+ reg = MT9V111S_VER_BLANKING;
+ data = coreReg->verticalBlanking;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111S_SHUTTER_WIDTH;
+ data = coreReg->shutterWidth;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111S_SHUTTER_DELAY;
+ data = ifpReg->upperShutterDelayLi;
+ mt9v111_write_reg(reg, data);
+
+ /* changes become effective */
+ reg = MT9V111S_OUTPUT_CTRL;
+ data = MT9V111S_OUTCTRL_CHIP_ENABLE | 0x3000;
+ mt9v111_write_reg(reg, data);
+
+ return error;
+}
+
+/*!
+ * mt9v111 sensor interface Initialization
+ * @param param sensor_interface *
+ * @param width u32
+ * @param height u32
+ * @return None
+ */
+static void mt9v111_interface(sensor_interface *param, u32 width, u32 height)
+{
+ param->Vsync_pol = 0x0;
+ param->clk_mode = 0x0; /*gated */
+ param->pixclk_pol = 0x0;
+ param->data_width = 0x1;
+ param->data_pol = 0x0;
+ param->ext_vsync = 0x0;
+ param->Vsync_pol = 0x0;
+ param->Hsync_pol = 0x0;
+ param->width = width - 1;
+ param->height = height - 1;
+ param->active_width = width;
+ param->active_height = height;
+ param->pixel_fmt = IPU_PIX_FMT_UYVY;
+ param->mclk = 27000000;
+}
+
+/*!
+ * MT9V111 frame rate calculate
+ *
+ * @param frame_rate int *
+ * @param mclk int
+ * @return None
+ */
+static void mt9v111_rate_cal(int *frame_rate, int mclk)
+{
+ int num_clock_per_row;
+ int max_rate = 0;
+
+ mt9v111_device.coreReg->horizontalBlanking = MT9V111_HORZBLANK_MIN;
+
+ num_clock_per_row = (format[0].width + 114 + MT9V111_HORZBLANK_MIN) * 2;
+ max_rate = mclk / (num_clock_per_row *
+ (format[0].height + MT9V111_VERTBLANK_DEFAULT));
+
+ if ((*frame_rate > max_rate) || (*frame_rate == 0)) {
+ *frame_rate = max_rate;
+ }
+
+ mt9v111_device.coreReg->verticalBlanking
+ = mclk / (*frame_rate * num_clock_per_row) - format[0].height;
+
+ reset_frame_rate = *frame_rate;
+}
+
+/*!
+ * MT9V111 sensor configuration
+ *
+ * @param frame_rate int *
+ * @param high_quality int
+ * @return sensor_interface *
+ */
+sensor_interface *mt9v111_config(int *frame_rate, int high_quality)
+{
+ u32 out_width, out_height;
+
+ if (interface_param == NULL)
+ return NULL;
+
+ mt9v111_device.coreReg->addressSelect = MT9V111I_SEL_SCA;
+ mt9v111_device.ifpReg->addrSpaceSel = MT9V111I_SEL_IFP;
+
+ mt9v111_device.coreReg->windowHeight = MT9V111_WINHEIGHT;
+ mt9v111_device.coreReg->windowWidth = MT9V111_WINWIDTH;
+ mt9v111_device.coreReg->zoomColStart = 0;
+ mt9v111_device.coreReg->zomRowStart = 0;
+ mt9v111_device.coreReg->digitalZoom = 0x0;
+
+ mt9v111_device.coreReg->verticalBlanking = MT9V111_VERTBLANK_DEFAULT;
+ mt9v111_device.coreReg->horizontalBlanking = MT9V111_HORZBLANK_MIN;
+ mt9v111_device.coreReg->pixelClockSpeed = 0;
+ mt9v111_device.coreReg->readMode = 0xd0a1;
+
+ mt9v111_device.ifpReg->outputFormatCtrl2 = 0;
+ mt9v111_device.ifpReg->gainLimitAE = 0x300;
+ mt9v111_device.ifpReg->AESpeed = 0x80;
+
+ /* here is the default value */
+ mt9v111_device.ifpReg->formatControl = 0xc800;
+ mt9v111_device.ifpReg->modeControl = 0x708e;
+ mt9v111_device.ifpReg->awbSpeed = 0x4514;
+ mt9v111_device.coreReg->shutterWidth = 0xf8;
+
+ out_width = 640;
+ out_height = 480;
+
+ /*output size */
+ mt9v111_device.ifpReg->HPan = 0;
+ mt9v111_device.ifpReg->HZoom = 640;
+ mt9v111_device.ifpReg->HSize = out_width;
+ mt9v111_device.ifpReg->VPan = 0;
+ mt9v111_device.ifpReg->VZoom = 480;
+ mt9v111_device.ifpReg->VSize = out_height;
+
+ mt9v111_interface(interface_param, out_width, out_height);
+ set_mclk_rate(&interface_param->mclk);
+ mt9v111_rate_cal(frame_rate, interface_param->mclk);
+ mt9v111_sensor_lib(mt9v111_device.coreReg, mt9v111_device.ifpReg);
+
+ return interface_param;
+}
+
+/*!
+ * mt9v111 sensor set color configuration
+ *
+ * @param bright int
+ * @param saturation int
+ * @param red int
+ * @param green int
+ * @param blue int
+ * @return None
+ */
+static void
+mt9v111_set_color(int bright, int saturation, int red, int green, int blue)
+{
+ u8 reg;
+ u16 data;
+
+ switch (saturation) {
+ case 100:
+ mt9v111_device.ifpReg->awbSpeed = 0x4514;
+ break;
+ case 150:
+ mt9v111_device.ifpReg->awbSpeed = 0x6D14;
+ break;
+ case 75:
+ mt9v111_device.ifpReg->awbSpeed = 0x4D14;
+ break;
+ case 50:
+ mt9v111_device.ifpReg->awbSpeed = 0x5514;
+ break;
+ case 37:
+ mt9v111_device.ifpReg->awbSpeed = 0x5D14;
+ break;
+ case 25:
+ mt9v111_device.ifpReg->awbSpeed = 0x6514;
+ break;
+ default:
+ mt9v111_device.ifpReg->awbSpeed = 0x4514;
+ break;
+ }
+
+ reg = MT9V111I_ADDR_SPACE_SEL;
+ data = mt9v111_device.ifpReg->addrSpaceSel;
+ mt9v111_write_reg(reg, data);
+
+ /* Operation Mode Control */
+ reg = MT9V111I_AWB_SPEED;
+ data = mt9v111_device.ifpReg->awbSpeed;
+ mt9v111_write_reg(reg, data);
+}
+
+/*!
+ * mt9v111 sensor get color configuration
+ *
+ * @param bright int *
+ * @param saturation int *
+ * @param red int *
+ * @param green int *
+ * @param blue int *
+ * @return None
+ */
+static void
+mt9v111_get_color(int *bright, int *saturation, int *red, int *green, int *blue)
+{
+ *saturation = (mt9v111_device.ifpReg->awbSpeed & 0x3800) >> 11;
+ switch (*saturation) {
+ case 0:
+ *saturation = 100;
+ break;
+ case 1:
+ *saturation = 75;
+ break;
+ case 2:
+ *saturation = 50;
+ break;
+ case 3:
+ *saturation = 37;
+ break;
+ case 4:
+ *saturation = 25;
+ break;
+ case 5:
+ *saturation = 150;
+ break;
+ case 6:
+ *saturation = 0;
+ break;
+ default:
+ *saturation = 0;
+ break;
+ }
+}
+
+/*!
+ * mt9v111 sensor set AE measurement window mode configuration
+ *
+ * @param ae_mode int
+ * @return None
+ */
+static void mt9v111_set_ae_mode(int ae_mode)
+{
+ u8 reg;
+ u16 data;
+
+ mt9v111_device.ifpReg->modeControl &= 0xfff3;
+ mt9v111_device.ifpReg->modeControl |= (ae_mode & 0x03) << 2;
+
+ reg = MT9V111I_ADDR_SPACE_SEL;
+ data = mt9v111_device.ifpReg->addrSpaceSel;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111I_MODE_CONTROL;
+ data = mt9v111_device.ifpReg->modeControl;
+ mt9v111_write_reg(reg, data);
+}
+
+/*!
+ * mt9v111 sensor get AE measurement window mode configuration
+ *
+ * @param ae_mode int *
+ * @return None
+ */
+static void mt9v111_get_ae_mode(int *ae_mode)
+{
+ if (ae_mode != NULL) {
+ *ae_mode = (mt9v111_device.ifpReg->modeControl & 0xc) >> 2;
+ }
+}
+
+/*!
+ * mt9v111 Reset function
+ *
+ * @return None
+ */
+static sensor_interface *mt9v111_reset(void)
+{
+ return mt9v111_config(&reset_frame_rate, 0);
+}
+
+struct camera_sensor camera_sensor_if = {
+ .set_color = mt9v111_set_color,
+ .get_color = mt9v111_get_color,
+ .set_ae_mode = mt9v111_set_ae_mode,
+ .get_ae_mode = mt9v111_get_ae_mode,
+ .config = mt9v111_config,
+ .reset = mt9v111_reset,
+};
+
+#ifdef MT9V111_DEBUG
+/*!
+ * Set sensor to test mode, which will generate test pattern.
+ *
+ * @return none
+ */
+static void mt9v111_test_pattern(bool flag)
+{
+ u16 data;
+
+ /* switch to sensor registers */
+ mt9v111_write_reg(MT9V111I_ADDR_SPACE_SEL, MT9V111I_SEL_SCA);
+
+ if (flag == true) {
+ testpattern = MT9V111S_OUTCTRL_TEST_MODE;
+
+ data = mt9v111_read_reg(MT9V111S_ROW_NOISE_CTRL) & 0xBF;
+ mt9v111_write_reg(MT9V111S_ROW_NOISE_CTRL, data);
+
+ mt9v111_write_reg(MT9V111S_TEST_DATA, 0);
+
+ /* changes take effect */
+ data = MT9V111S_OUTCTRL_CHIP_ENABLE | testpattern | 0x3000;
+ mt9v111_write_reg(MT9V111S_OUTPUT_CTRL, data);
+ } else {
+ testpattern = 0;
+
+ data = mt9v111_read_reg(MT9V111S_ROW_NOISE_CTRL) | 0x40;
+ mt9v111_write_reg(MT9V111S_ROW_NOISE_CTRL, data);
+
+ /* changes take effect */
+ data = MT9V111S_OUTCTRL_CHIP_ENABLE | testpattern | 0x3000;
+ mt9v111_write_reg(MT9V111S_OUTPUT_CTRL, data);
+ }
+}
+#endif
+
+/*!
+ * mt9v111 I2C detect_client function
+ *
+ * @param adapter struct i2c_adapter *
+ * @param address int
+ * @param kind int
+ *
+ * @return Error code indicating success or failure
+ */
+static int mt9v111_detect_client(struct i2c_adapter *adapter, int address,
+ int kind)
+{
+ mt9v111_i2c_client.adapter = adapter;
+ if (i2c_attach_client(&mt9v111_i2c_client)) {
+ mt9v111_i2c_client.adapter = NULL;
+ printk(KERN_ERR "mt9v111_attach: i2c_attach_client failed\n");
+ return -1;
+ }
+
+ interface_param = (sensor_interface *)
+ kmalloc(sizeof(sensor_interface), GFP_KERNEL);
+ if (!interface_param) {
+ printk(KERN_ERR "mt9v111_attach: kmalloc failed \n");
+ return -1;
+ }
+
+ printk(KERN_INFO "MT9V111 Detected\n");
+
+ return 0;
+}
+
+static unsigned short normal_i2c[] = { MT9V111_I2C_ADDRESS, I2C_CLIENT_END };
+
+/* Magic definition of all other variables and things */
+I2C_CLIENT_INSMOD;
+
+/*!
+ * mt9v111 I2C attach function
+ *
+ * @param adapter struct i2c_adapter *
+ * @return Error code indicating success or failure
+ */
+static int mt9v111_attach(struct i2c_adapter *adap)
+{
+ uint32_t mclk = 27000000;
+ struct clk *clk;
+ int err;
+
+ clk = clk_get(NULL, "csi_clk");
+ clk_enable(clk);
+ set_mclk_rate(&mclk);
+
+ err = i2c_probe(adap, &addr_data, &mt9v111_detect_client);
+
+ clk_disable(clk);
+ clk_put(clk);
+
+ return err;
+}
+
+/*!
+ * mt9v111 I2C detach function
+ *
+ * @param client struct i2c_client *
+ * @return Error code indicating success or failure
+ */
+static int mt9v111_detach(struct i2c_client *client)
+{
+ int err;
+
+ if (!mt9v111_i2c_client.adapter)
+ return -1;
+
+ err = i2c_detach_client(&mt9v111_i2c_client);
+ mt9v111_i2c_client.adapter = NULL;
+
+ if (interface_param)
+ kfree(interface_param);
+ interface_param = NULL;
+
+ return err;
+}
+
+extern void gpio_sensor_active(void);
+
+/*!
+ * MT9V111 init function
+ *
+ * @return Error code indicating success or failure
+ */
+static __init int mt9v111_init(void)
+{
+ u8 err;
+
+ gpio_sensor_active();
+
+ mt9v111_device.coreReg = (mt9v111_coreReg *)
+ kmalloc(sizeof(mt9v111_coreReg), GFP_KERNEL);
+ if (!mt9v111_device.coreReg)
+ return -1;
+
+ memset(mt9v111_device.coreReg, 0, sizeof(mt9v111_coreReg));
+
+ mt9v111_device.ifpReg = (mt9v111_IFPReg *)
+ kmalloc(sizeof(mt9v111_IFPReg), GFP_KERNEL);
+ if (!mt9v111_device.ifpReg) {
+ kfree(mt9v111_device.coreReg);
+ mt9v111_device.coreReg = NULL;
+ return -1;
+ }
+
+ memset(mt9v111_device.ifpReg, 0, sizeof(mt9v111_IFPReg));
+
+ err = i2c_add_driver(&mt9v111_i2c_driver);
+
+ return err;
+}
+
+extern void gpio_sensor_inactive(void);
+/*!
+ * MT9V111 cleanup function
+ *
+ * @return Error code indicating success or failure
+ */
+static void __exit mt9v111_clean(void)
+{
+ if (mt9v111_device.coreReg) {
+ kfree(mt9v111_device.coreReg);
+ mt9v111_device.coreReg = NULL;
+ }
+
+ if (mt9v111_device.ifpReg) {
+ kfree(mt9v111_device.ifpReg);
+ mt9v111_device.ifpReg = NULL;
+ }
+
+ i2c_del_driver(&mt9v111_i2c_driver);
+
+ gpio_sensor_inactive();
+}
+
+module_init(mt9v111_init);
+module_exit(mt9v111_clean);
+
+/* Exported symbols for modules. */
+EXPORT_SYMBOL(camera_sensor_if);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Mt9v111 Camera Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxc/capture/emma_ov2640.c b/drivers/media/video/mxc/capture/emma_ov2640.c
new file mode 100644
index 000000000000..ceffea4d52a9
--- /dev/null
+++ b/drivers/media/video/mxc/capture/emma_ov2640.c
@@ -0,0 +1,444 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/regulator/consumer.h>
+
+#include "mxc_v4l2_capture.h"
+
+enum ov2640_mode {
+ ov2640_mode_1600_1120,
+ ov2640_mode_800_600
+};
+
+struct reg_value {
+ u8 reg;
+ u8 value;
+ int delay_ms;
+};
+
+static struct reg_value ov2640_setting_1600_1120[] = {
+ {0xff, 0x1, 0}, {0x12, 0x80, 1}, {0xff, 0, 0}, {0x2c, 0xff, 0},
+ {0x2e, 0xdf, 0}, {0xff, 0x1, 0}, {0x3c, 0x32, 0}, {0x11, 0x01, 0},
+ {0x09, 0x00, 0}, {0x04, 0x28, 0}, {0x13, 0xe5, 0}, {0x14, 0x48, 0},
+ {0x2c, 0x0c, 0}, {0x33, 0x78, 0}, {0x3a, 0x33, 0}, {0x3b, 0xfb, 0},
+ {0x3e, 0x00, 0}, {0x43, 0x11, 0}, {0x16, 0x10, 0}, {0x39, 0x82, 0},
+ {0x35, 0x88, 0}, {0x22, 0x0a, 0}, {0x37, 0x40, 0}, {0x23, 0x00, 0},
+ {0x34, 0xa0, 0}, {0x36, 0x1a, 0}, {0x06, 0x02, 0}, {0x07, 0xc0, 0},
+ {0x0d, 0xb7, 0}, {0x0e, 0x01, 0}, {0x4c, 0x00, 0}, {0x4a, 0x81, 0},
+ {0x21, 0x99, 0}, {0x24, 0x40, 0}, {0x25, 0x38, 0}, {0x26, 0x82, 0},
+ {0x5c, 0x00, 0}, {0x63, 0x00, 0}, {0x46, 0x3f, 0}, {0x0c, 0x3c, 0},
+ {0x5d, 0x55, 0}, {0x5e, 0x7d, 0}, {0x5f, 0x7d, 0}, {0x60, 0x55, 0},
+ {0x61, 0x70, 0}, {0x62, 0x80, 0}, {0x7c, 0x05, 0}, {0x20, 0x80, 0},
+ {0x28, 0x30, 0}, {0x6c, 0x00, 0}, {0x6d, 0x80, 0}, {0x6e, 00, 0},
+ {0x70, 0x02, 0}, {0x71, 0x94, 0}, {0x73, 0xc1, 0}, {0x3d, 0x34, 0},
+ {0x5a, 0x57, 0}, {0x4f, 0xbb, 0}, {0x50, 0x9c, 0}, {0xff, 0x00, 0},
+ {0xe5, 0x7f, 0}, {0xf9, 0xc0, 0}, {0x41, 0x24, 0}, {0x44, 0x06, 0},
+ {0xe0, 0x14, 0}, {0x76, 0xff, 0}, {0x33, 0xa0, 0}, {0x42, 0x20, 0},
+ {0x43, 0x18, 0}, {0x4c, 0x00, 0}, {0x87, 0xd0, 0}, {0xd7, 0x03, 0},
+ {0xd9, 0x10, 0}, {0xd3, 0x82, 0}, {0xc8, 0x08, 0}, {0xc9, 0x80, 0},
+ {0x7c, 0x00, 0}, {0x7d, 0x00, 0}, {0x7c, 0x03, 0}, {0x7d, 0x48, 0},
+ {0x7d, 0x48, 0}, {0x7c, 0x08, 0}, {0x7d, 0x20, 0}, {0x7d, 0x10, 0},
+ {0x7d, 0x0e, 0}, {0x90, 0x00, 0}, {0x91, 0x0e, 0}, {0x91, 0x1a, 0},
+ {0x91, 0x31, 0}, {0x91, 0x5a, 0}, {0x91, 0x69, 0}, {0x91, 0x75, 0},
+ {0x91, 0x7e, 0}, {0x91, 0x88, 0}, {0x91, 0x8f, 0}, {0x91, 0x96, 0},
+ {0x91, 0xa3, 0}, {0x91, 0xaf, 0}, {0x91, 0xc4, 0}, {0x91, 0xd7, 0},
+ {0x91, 0xe8, 0}, {0x91, 0x20, 0}, {0x92, 0x00, 0}, {0x93, 0x06, 0},
+ {0x93, 0xe3, 0}, {0x93, 0x03, 0}, {0x93, 0x03, 0}, {0x93, 0x00, 0},
+ {0x93, 0x02, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0},
+ {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0},
+ {0x96, 0x00, 0}, {0x97, 0x08, 0}, {0x97, 0x19, 0}, {0x97, 0x02, 0},
+ {0x97, 0x0c, 0}, {0x97, 0x24, 0}, {0x97, 0x30, 0}, {0x97, 0x28, 0},
+ {0x97, 0x26, 0}, {0x97, 0x02, 0}, {0x97, 0x98, 0}, {0x97, 0x80, 0},
+ {0x97, 0x00, 0}, {0x97, 0x00, 0}, {0xa4, 0x00, 0}, {0xa8, 0x00, 0},
+ {0xc5, 0x11, 0}, {0xc6, 0x51, 0}, {0xbf, 0x80, 0}, {0xc7, 0x10, 0},
+ {0xb6, 0x66, 0}, {0xb8, 0xa5, 0}, {0xb7, 0x64, 0}, {0xb9, 0x7c, 0},
+ {0xb3, 0xaf, 0}, {0xb4, 0x97, 0}, {0xb5, 0xff, 0}, {0xb0, 0xc5, 0},
+ {0xb1, 0x94, 0}, {0xb2, 0x0f, 0}, {0xc4, 0x5c, 0}, {0xa6, 0x00, 0},
+ {0xa7, 0x20, 0}, {0xa7, 0xd8, 0}, {0xa7, 0x1b, 0}, {0xa7, 0x31, 0},
+ {0xa7, 0x00, 0}, {0xa7, 0x18, 0}, {0xa7, 0x20, 0}, {0xa7, 0xd8, 0},
+ {0xa7, 0x19, 0}, {0xa7, 0x31, 0}, {0xa7, 0x00, 0}, {0xa7, 0x18, 0},
+ {0xa7, 0x20, 0}, {0xa7, 0xd8, 0}, {0xa7, 0x19, 0}, {0xa7, 0x31, 0},
+ {0xa7, 0x00, 0}, {0xa7, 0x18, 0}, {0xc0, 0xc8, 0}, {0xc1, 0x96, 0},
+ {0x86, 0x3d, 0}, {0x50, 0x00, 0}, {0x51, 0x90, 0}, {0x52, 0x18, 0},
+ {0x53, 0x00, 0}, {0x54, 0x00, 0}, {0x55, 0x88, 0}, {0x57, 0x00, 0},
+ {0x5a, 0x90, 0}, {0x5b, 0x18, 0}, {0x5c, 0x05, 0}, {0xc3, 0xef, 0},
+ {0x7f, 0x00, 0}, {0xda, 0x01, 0}, {0xe5, 0x1f, 0}, {0xe1, 0x67, 0},
+ {0xe0, 0x00, 0}, {0xdd, 0x7f, 0}, {0x05, 0x00, 0}
+};
+
+static struct reg_value ov2640_setting_800_600[] = {
+ {0xff, 0, 0}, {0xff, 1, 0}, {0x12, 0x80, 1}, {0xff, 00, 0},
+ {0x2c, 0xff, 0}, {0x2e, 0xdf, 0}, {0xff, 0x1, 0}, {0x3c, 0x32, 0},
+ {0x11, 0x01, 0}, {0x09, 0x00, 0}, {0x04, 0x28, 0}, {0x13, 0xe5, 0},
+ {0x14, 0x48, 0}, {0x2c, 0x0c, 0}, {0x33, 0x78, 0}, {0x3a, 0x33, 0},
+ {0x3b, 0xfb, 0}, {0x3e, 0x00, 0}, {0x43, 0x11, 0}, {0x16, 0x10, 0},
+ {0x39, 0x92, 0}, {0x35, 0xda, 0}, {0x22, 0x1a, 0}, {0x37, 0xc3, 0},
+ {0x23, 0x00, 0}, {0x34, 0xc0, 0}, {0x36, 0x1a, 0}, {0x06, 0x88, 0},
+ {0x07, 0xc0, 0}, {0x0d, 0x87, 0}, {0x0e, 0x41, 0}, {0x4c, 0x00, 0},
+ {0x4a, 0x81, 0}, {0x21, 0x99, 0}, {0x24, 0x40, 0}, {0x25, 0x38, 0},
+ {0x26, 0x82, 0}, {0x5c, 0x00, 0}, {0x63, 0x00, 0}, {0x46, 0x22, 0},
+ {0x0c, 0x3c, 0}, {0x5d, 0x55, 0}, {0x5e, 0x7d, 0}, {0x5f, 0x7d, 0},
+ {0x60, 0x55, 0}, {0x61, 0x70, 0}, {0x62, 0x80, 0}, {0x7c, 0x05, 0},
+ {0x20, 0x80, 0}, {0x28, 0x30, 0}, {0x6c, 0x00, 0}, {0x6d, 0x80, 0},
+ {0x6e, 00, 0}, {0x70, 0x02, 0}, {0x71, 0x94, 0}, {0x73, 0xc1, 0},
+ {0x12, 0x40, 0}, {0x17, 0x11, 0}, {0x18, 0x43, 0}, {0x19, 0x00, 0},
+ {0x1a, 0x4b, 0}, {0x32, 0x09, 0}, {0x37, 0xc0, 0}, {0x4f, 0xca, 0},
+ {0x50, 0xa8, 0}, {0x6d, 0x00, 0}, {0x3d, 0x38, 0}, {0xff, 0x00, 0},
+ {0xe5, 0x7f, 0}, {0xf9, 0xc0, 0}, {0x41, 0x24, 0}, {0x44, 0x06, 0},
+ {0xe0, 0x14, 0}, {0x76, 0xff, 0}, {0x33, 0xa0, 0}, {0x42, 0x20, 0},
+ {0x43, 0x18, 0}, {0x4c, 0x00, 0}, {0x87, 0xd0, 0}, {0x88, 0x3f, 0},
+ {0xd7, 0x03, 0}, {0xd9, 0x10, 0}, {0xd3, 0x82, 0}, {0xc8, 0x08, 0},
+ {0xc9, 0x80, 0}, {0x7c, 0x00, 0}, {0x7d, 0x00, 0}, {0x7c, 0x03, 0},
+ {0x7d, 0x48, 0}, {0x7d, 0x48, 0}, {0x7c, 0x08, 0}, {0x7d, 0x20, 0},
+ {0x7d, 0x10, 0}, {0x7d, 0x0e, 0}, {0x90, 0x00, 0}, {0x91, 0x0e, 0},
+ {0x91, 0x1a, 0}, {0x91, 0x31, 0}, {0x91, 0x5a, 0}, {0x91, 0x69, 0},
+ {0x91, 0x75, 0}, {0x91, 0x7e, 0}, {0x91, 0x88, 0}, {0x91, 0x8f, 0},
+ {0x91, 0x96, 0}, {0x91, 0xa3, 0}, {0x91, 0xaf, 0}, {0x91, 0xc4, 0},
+ {0x91, 0xd7, 0}, {0x91, 0xe8, 0}, {0x91, 0x20, 0}, {0x92, 0x00, 0},
+ {0x93, 0x06, 0}, {0x93, 0xe3, 0}, {0x93, 0x03, 0}, {0x93, 0x03, 0},
+ {0x93, 0x00, 0}, {0x93, 0x02, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0},
+ {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0},
+ {0x93, 0x00, 0}, {0x96, 0x00, 0}, {0x97, 0x08, 0}, {0x97, 0x19, 0},
+ {0x97, 0x02, 0}, {0x97, 0x0c, 0}, {0x97, 0x24, 0}, {0x97, 0x30, 0},
+ {0x97, 0x28, 0}, {0x97, 0x26, 0}, {0x97, 0x02, 0}, {0x97, 0x98, 0},
+ {0x97, 0x80, 0}, {0x97, 0x00, 0}, {0x97, 0x00, 0}, {0xa4, 0x00, 0},
+ {0xa8, 0x00, 0}, {0xc5, 0x11, 0}, {0xc6, 0x51, 0}, {0xbf, 0x80, 0},
+ {0xc7, 0x10, 0}, {0xb6, 0x66, 0}, {0xb8, 0xa5, 0}, {0xb7, 0x64, 0},
+ {0xb9, 0x7c, 0}, {0xb3, 0xaf, 0}, {0xb4, 0x97, 0}, {0xb5, 0xff, 0},
+ {0xb0, 0xc5, 0}, {0xb1, 0x94, 0}, {0xb2, 0x0f, 0}, {0xc4, 0x5c, 0},
+ {0xa6, 0x00, 0}, {0xa7, 0x20, 0}, {0xa7, 0xd8, 0}, {0xa7, 0x1b, 0},
+ {0xa7, 0x31, 0}, {0xa7, 0x00, 0}, {0xa7, 0x18, 0}, {0xa7, 0x20, 0},
+ {0xa7, 0xd8, 0}, {0xa7, 0x19, 0}, {0xa7, 0x31, 0}, {0xa7, 0x00, 0},
+ {0xa7, 0x18, 0}, {0xa7, 0x20, 0}, {0xa7, 0xd8, 0}, {0xa7, 0x19, 0},
+ {0xa7, 0x31, 0}, {0xa7, 0x00, 0}, {0xa7, 0x18, 0}, {0xc0, 0x64, 0},
+ {0xc1, 0x4b, 0}, {0x86, 0x1d, 0}, {0x50, 0x00, 0}, {0x51, 0xc8, 0},
+ {0x52, 0x96, 0}, {0x53, 0x00, 0}, {0x54, 0x00, 0}, {0x55, 0x00, 0},
+ {0x57, 0x00, 0}, {0x5a, 0xc8, 0}, {0x5b, 0x96, 0}, {0x5c, 0x00, 0},
+ {0xc3, 0xef, 0}, {0x7f, 0x00, 0}, {0xda, 0x01, 0}, {0xe5, 0x1f, 0},
+ {0xe1, 0x67, 0}, {0xe0, 0x00, 0}, {0xdd, 0x7f, 0}, {0x05, 0x00, 0}
+};
+
+static struct regulator *io_regulator;
+static struct regulator *core_regulator;
+static struct regulator *analog_regulator;
+static struct regulator *gpo_regulator;
+u32 mclk = 24000000;
+
+struct i2c_client *ov2640_i2c_client;
+
+static sensor_interface *interface_param;
+static int reset_frame_rate = 30;
+static int ov2640_probe(struct i2c_client *adapter,
+ const struct i2c_device_id *id);
+static int ov2640_remove(struct i2c_client *client);
+
+static const struct i2c_device_id ov2640_id[] = {
+ {"ov2640", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, ov2640_id);
+
+static struct i2c_driver ov2640_i2c_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "ov2640",
+ },
+ .probe = ov2640_probe,
+ .remove = ov2640_remove,
+ .id_table = ov2640_id,
+};
+
+/*!
+ * ov2640 I2C attach function
+ *
+ * @param adapter struct i2c_adapter *
+ * @return Error code indicating success or failure
+ */
+static int ov2640_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct mxc_camera_platform_data *plat_data = client->dev.platform_data;
+
+ ov2640_i2c_client = client;
+ mclk = plat_data->mclk;
+
+ io_regulator = regulator_get(&client->dev, plat_data->io_regulator);
+ core_regulator = regulator_get(&client->dev, plat_data->core_regulator);
+ analog_regulator =
+ regulator_get(&client->dev, plat_data->analog_regulator);
+ gpo_regulator = regulator_get(&client->dev, plat_data->gpo_regulator);
+
+ interface_param = (sensor_interface *)
+ kmalloc(sizeof(sensor_interface), GFP_KERNEL);
+ if (!interface_param) {
+ dev_dbg(&ov2640_i2c_client->dev,
+ "ov2640_probe: kmalloc failed \n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*!
+ * ov2640 I2C detach function
+ *
+ * @param client struct i2c_client *
+ * @return Error code indicating success or failure
+ */
+static int ov2640_remove(struct i2c_client *client)
+{
+ kfree(interface_param);
+ interface_param = NULL;
+
+ if (!IS_ERR_VALUE((unsigned long)io_regulator)) {
+ regulator_disable(io_regulator);
+ regulator_put(io_regulator);
+ }
+
+ if (!IS_ERR_VALUE((unsigned long)core_regulator)) {
+ regulator_disable(core_regulator);
+ regulator_put(core_regulator);
+ }
+
+ if (!IS_ERR_VALUE((unsigned long)gpo_regulator)) {
+ regulator_disable(gpo_regulator);
+ regulator_put(gpo_regulator);
+ }
+
+ if (!IS_ERR_VALUE((unsigned long)analog_regulator)) {
+ regulator_disable(analog_regulator);
+ regulator_put(analog_regulator);
+ }
+
+ return 0;
+}
+
+static int ov2640_write_reg(u8 reg, u8 val)
+{
+ if (i2c_smbus_write_byte_data(ov2640_i2c_client, reg, val) < 0) {
+ dev_dbg(&ov2640_i2c_client->dev,
+ "%s:write reg errorr:reg=%x,val=%x\n", __func__, reg,
+ val);
+ return -1;
+ }
+ return 0;
+}
+
+static int ov2640_init_mode(enum ov2640_mode mode)
+{
+ struct reg_value *setting;
+ int i, num;
+
+ switch (mode) {
+ case ov2640_mode_1600_1120:
+ setting = ov2640_setting_1600_1120;
+ num = ARRAY_SIZE(ov2640_setting_1600_1120);
+ break;
+ case ov2640_mode_800_600:
+ setting = ov2640_setting_800_600;
+ num = ARRAY_SIZE(ov2640_setting_800_600);
+ break;
+ default:
+ return 0;
+ }
+
+ for (i = 0; i < num; i++) {
+ ov2640_write_reg(setting[i].reg, setting[i].value);
+ if (setting[i].delay_ms > 0)
+ msleep(setting[i].delay_ms);
+ }
+
+ return 0;
+}
+
+/*!
+ * ov2640 sensor interface Initialization
+ * @param param sensor_interface *
+ * @param width u32
+ * @param height u32
+ * @return None
+ */
+static void ov2640_interface(sensor_interface *param, u32 width, u32 height)
+{
+ param->Vsync_pol = 0x0;
+ param->clk_mode = 0x0; /*gated */
+ param->pixclk_pol = 0x0;
+ param->data_width = 0x1;
+ param->data_pol = 0x0;
+ param->ext_vsync = 0x0;
+ param->Vsync_pol = 0x0;
+ param->Hsync_pol = 0x0;
+ param->width = width - 1;
+ param->height = height - 1;
+ param->active_width = width;
+ param->active_height = height;
+ param->pixel_fmt = IPU_PIX_FMT_UYVY;
+ param->mclk = mclk;
+}
+
+static void ov2640_set_color(int bright, int saturation, int red, int green,
+ int blue)
+{
+
+}
+
+static void ov2640_get_color(int *bright, int *saturation, int *red, int *green,
+ int *blue)
+{
+
+}
+static void ov2640_set_ae_mode(int ae_mode)
+{
+
+}
+static void ov2640_get_ae_mode(int *ae_mode)
+{
+
+}
+
+extern void gpio_sensor_active(void);
+
+static sensor_interface *ov2640_config(int *frame_rate, int high_quality)
+{
+
+ u32 out_width, out_height;
+
+ /*set io votage */
+ if (!IS_ERR_VALUE((unsigned long)io_regulator)) {
+ regulator_set_voltage(io_regulator, 2800000, 2800000);
+ if (regulator_enable(io_regulator) != 0) {
+ dev_dbg(&ov2640_i2c_client->dev,
+ "%s:io set voltage error\n", __func__);
+ return NULL;
+ } else {
+ dev_dbg(&ov2640_i2c_client->dev,
+ "%s:io set voltage ok\n", __func__);
+ }
+ }
+
+ /*core votage */
+ if (!IS_ERR_VALUE((unsigned long)core_regulator)) {
+ regulator_set_voltage(core_regulator, 1300000, 1300000);
+ if (regulator_enable(core_regulator) != 0) {
+ dev_dbg(&ov2640_i2c_client->dev,
+ "%s:core set voltage error\n", __func__);
+ return NULL;
+ } else {
+ dev_dbg(&ov2640_i2c_client->dev,
+ "%s:core set voltage ok\n", __func__);
+ }
+ }
+
+ /*GPO 3 */
+ if (!IS_ERR_VALUE((unsigned long)gpo_regulator)) {
+ if (regulator_enable(gpo_regulator) != 0) {
+ dev_dbg(&ov2640_i2c_client->dev,
+ "%s:gpo3 enable error\n", __func__);
+ return NULL;
+ } else {
+ dev_dbg(&ov2640_i2c_client->dev, "%s:gpo3 enable ok\n",
+ __func__);
+ }
+ }
+
+ if (!IS_ERR_VALUE((unsigned long)analog_regulator)) {
+ regulator_set_voltage(analog_regulator, 2000000, 2000000);
+ if (regulator_enable(analog_regulator) != 0) {
+ dev_dbg(&ov2640_i2c_client->dev,
+ "%s:analog set voltage error\n", __func__);
+ return NULL;
+ } else {
+ dev_dbg(&ov2640_i2c_client->dev,
+ "%s:analog set voltage ok\n", __func__);
+ }
+ }
+
+ gpio_sensor_active();
+
+ if (high_quality) {
+ out_width = 1600;
+ out_height = 1120;
+ } else {
+ out_width = 800;
+ out_height = 600;
+ }
+ ov2640_interface(interface_param, out_width, out_height);
+ set_mclk_rate(&interface_param->mclk);
+
+ if (high_quality)
+ ov2640_init_mode(ov2640_mode_1600_1120);
+ else
+ ov2640_init_mode(ov2640_mode_800_600);
+
+ msleep(300);
+
+ return interface_param;
+}
+
+static sensor_interface *ov2640_reset(void)
+{
+ return ov2640_config(&reset_frame_rate, 0);
+}
+
+struct camera_sensor camera_sensor_if = {
+ .set_color = ov2640_set_color,
+ .get_color = ov2640_get_color,
+ .set_ae_mode = ov2640_set_ae_mode,
+ .get_ae_mode = ov2640_get_ae_mode,
+ .config = ov2640_config,
+ .reset = ov2640_reset,
+};
+
+EXPORT_SYMBOL(camera_sensor_if);
+
+/*!
+ * ov2640 init function
+ *
+ * @return Error code indicating success or failure
+ */
+static __init int ov2640_init(void)
+{
+ u8 err;
+
+ err = i2c_add_driver(&ov2640_i2c_driver);
+
+ return err;
+}
+
+extern void gpio_sensor_inactive(void);
+/*!
+ * OV2640 cleanup function
+ *
+ * @return Error code indicating success or failure
+ */
+static void __exit ov2640_clean(void)
+{
+ i2c_del_driver(&ov2640_i2c_driver);
+
+ gpio_sensor_inactive();
+}
+
+module_init(ov2640_init);
+module_exit(ov2640_clean);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("OV2640 Camera Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxc/capture/emma_v4l2_capture.c b/drivers/media/video/mxc/capture/emma_v4l2_capture.c
new file mode 100644
index 000000000000..170807716ec6
--- /dev/null
+++ b/drivers/media/video/mxc/capture/emma_v4l2_capture.c
@@ -0,0 +1,2075 @@
+/*
+ * Copyright 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file mx27_v4l2_capture.c
+ *
+ * @brief MX27 Video For Linux 2 driver
+ *
+ * @ingroup MXC_V4L2_CAPTURE
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/pagemap.h>
+#include <linux/vmalloc.h>
+#include <linux/types.h>
+#include <linux/fb.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/semaphore.h>
+#include <linux/version.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+
+#include "mxc_v4l2_capture.h"
+#include "mx27_prp.h"
+#include "mx27_csi.h"
+
+static int csi_mclk_flag_backup;
+static int video_nr = -1;
+static cam_data *g_cam;
+
+/*!
+ * Free frame buffers
+ *
+ * @param cam Structure cam_data *
+ *
+ * @return status 0 success.
+ */
+static int mxc_free_frame_buf(cam_data *cam)
+{
+ int i;
+
+ for (i = 0; i < FRAME_NUM; i++) {
+ if (cam->frame[i].vaddress != 0) {
+ dma_free_coherent(0,
+ cam->frame[i].buffer.length,
+ cam->frame[i].vaddress,
+ cam->frame[i].paddress);
+ cam->frame[i].vaddress = 0;
+ }
+ }
+
+ return 0;
+}
+
+/*!
+ * Allocate frame buffers
+ *
+ * @param cam Structure cam_data *
+ *
+ * @param count int number of buffer need to allocated
+ *
+ * @return status -0 Successfully allocated a buffer, -ENOBUFS failed.
+ */
+static int mxc_allocate_frame_buf(cam_data *cam, int count)
+{
+ int i;
+
+ for (i = 0; i < count; i++) {
+ cam->frame[i].vaddress =
+ dma_alloc_coherent(0,
+ PAGE_ALIGN(cam->v2f. fmt.pix.sizeimage),
+ &cam->frame[i].paddress,
+ GFP_DMA | GFP_KERNEL);
+ if (cam->frame[i].vaddress == 0) {
+ pr_debug("mxc_allocate_frame_buf failed.\n");
+ mxc_free_frame_buf(cam);
+ return -ENOBUFS;
+ }
+ cam->frame[i].buffer.index = i;
+ cam->frame[i].buffer.flags = V4L2_BUF_FLAG_MAPPED;
+ cam->frame[i].buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ cam->frame[i].buffer.length =
+ PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage);
+ cam->frame[i].buffer.memory = V4L2_MEMORY_MMAP;
+ cam->frame[i].buffer.m.offset = cam->frame[i].paddress;
+ cam->frame[i].index = i;
+ }
+
+ return 0;
+}
+
+/*!
+ * Free frame buffers status
+ *
+ * @param cam Structure cam_data *
+ *
+ * @return none
+ */
+static void mxc_free_frames(cam_data *cam)
+{
+ int i;
+
+ for (i = 0; i < FRAME_NUM; i++) {
+ cam->frame[i].buffer.flags = V4L2_BUF_FLAG_MAPPED;
+ }
+
+ cam->enc_counter = 0;
+ cam->skip_frame = 0;
+ INIT_LIST_HEAD(&cam->ready_q);
+ INIT_LIST_HEAD(&cam->working_q);
+ INIT_LIST_HEAD(&cam->done_q);
+}
+
+/*!
+ * Return the buffer status
+ *
+ * @param cam Structure cam_data *
+ * @param buf Structure v4l2_buffer *
+ *
+ * @return status 0 success, EINVAL failed.
+ */
+static int mxc_v4l2_buffer_status(cam_data *cam, struct v4l2_buffer *buf)
+{
+ /* check range */
+ if (buf->index < 0 || buf->index >= FRAME_NUM) {
+ pr_debug("mxc_v4l2_buffer_status buffers not allocated\n");
+ return -EINVAL;
+ }
+
+ memcpy(buf, &(cam->frame[buf->index].buffer), sizeof(*buf));
+ return 0;
+}
+
+/*!
+ * start the encoder job
+ *
+ * @param cam structure cam_data *
+ *
+ * @return status 0 Success
+ */
+static int mxc_streamon(cam_data *cam)
+{
+ struct mxc_v4l_frame *frame;
+ int err = 0;
+
+ if (!cam)
+ return -EIO;
+
+ if (list_empty(&cam->ready_q)) {
+ printk(KERN_ERR "mxc_streamon buffer not been queued yet\n");
+ return -EINVAL;
+ }
+
+ cam->capture_pid = current->pid;
+
+ if (cam->enc_enable) {
+ err = cam->enc_enable(cam);
+ if (err != 0) {
+ return err;
+ }
+ }
+
+ cam->ping_pong_csi = 0;
+ if (cam->enc_update_eba) {
+ frame =
+ list_entry(cam->ready_q.next, struct mxc_v4l_frame, queue);
+ list_del(cam->ready_q.next);
+ list_add_tail(&frame->queue, &cam->working_q);
+ err = cam->enc_update_eba(frame->paddress, &cam->ping_pong_csi);
+
+ frame =
+ list_entry(cam->ready_q.next, struct mxc_v4l_frame, queue);
+ list_del(cam->ready_q.next);
+ list_add_tail(&frame->queue, &cam->working_q);
+ err |=
+ cam->enc_update_eba(frame->paddress, &cam->ping_pong_csi);
+ } else {
+ return -EINVAL;
+ }
+
+ return err;
+}
+
+/*!
+ * Shut down the encoder job
+ *
+ * @param cam structure cam_data *
+ *
+ * @return status 0 Success
+ */
+static int mxc_streamoff(cam_data *cam)
+{
+ int err = 0;
+
+ if (!cam)
+ return -EIO;
+
+ if (cam->enc_disable) {
+ err = cam->enc_disable(cam);
+ }
+ mxc_free_frames(cam);
+ return err;
+}
+
+/*!
+ * Valid whether the palette is supported
+ *
+ * @param palette pixel format
+ *
+ * @return 0 if failed
+ */
+static inline int valid_mode(u32 palette)
+{
+ /*
+ * MX27 PrP channel 2 supports YUV444, but YUV444 is not
+ * defined by V4L2 :(
+ */
+ return ((palette == V4L2_PIX_FMT_YUYV) ||
+ (palette == V4L2_PIX_FMT_YUV420));
+}
+
+/*!
+ * Valid and adjust the overlay window size, position
+ *
+ * @param cam structure cam_data *
+ * @param win struct v4l2_window *
+ *
+ * @return 0
+ */
+static int verify_preview(cam_data *cam, struct v4l2_window *win)
+{
+ if (cam->output >= num_registered_fb) {
+ pr_debug("verify_preview No matched.\n");
+ return -1;
+ }
+ cam->overlay_fb = (struct fb_info *)registered_fb[cam->output];
+
+ /* TODO: suppose 16bpp, 4 bytes alignment */
+ win->w.left &= ~0x1;
+
+ if (win->w.width + win->w.left > cam->overlay_fb->var.xres)
+ win->w.width = cam->overlay_fb->var.xres - win->w.left;
+ if (win->w.height + win->w.top > cam->overlay_fb->var.yres)
+ win->w.height = cam->overlay_fb->var.yres - win->w.top;
+
+ /*
+ * TODO: suppose 16bpp. Rounded down to a multiple of 2 pixels for
+ * width according to PrP limitations.
+ */
+ if ((cam->rotation == V4L2_MXC_ROTATE_90_RIGHT)
+ || (cam->rotation == V4L2_MXC_ROTATE_90_RIGHT_VFLIP)
+ || (cam->rotation == V4L2_MXC_ROTATE_90_RIGHT_HFLIP)
+ || (cam->rotation == V4L2_MXC_ROTATE_90_LEFT))
+ win->w.height &= ~0x1;
+ else
+ win->w.width &= ~0x1;
+
+ return 0;
+}
+
+/*!
+ * start the viewfinder job
+ *
+ * @param cam structure cam_data *
+ *
+ * @return status 0 Success
+ */
+static int start_preview(cam_data *cam)
+{
+ int err = 0;
+
+ err = prp_vf_select(cam);
+ if (err != 0)
+ return err;
+
+ cam->overlay_pid = current->pid;
+ err = cam->vf_start_sdc(cam);
+
+ return err;
+}
+
+/*!
+ * shut down the viewfinder job
+ *
+ * @param cam structure cam_data *
+ *
+ * @return status 0 Success
+ */
+static int stop_preview(cam_data *cam)
+{
+ int err = 0;
+
+ err = prp_vf_deselect(cam);
+ return err;
+}
+
+/*!
+ * V4L2 - mxc_v4l2_g_fmt function
+ *
+ * @param cam structure cam_data *
+ *
+ * @param f structure v4l2_format *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_v4l2_g_fmt(cam_data *cam, struct v4l2_format *f)
+{
+ int retval = 0;
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ f->fmt.pix.width = cam->v2f.fmt.pix.width;
+ f->fmt.pix.height = cam->v2f.fmt.pix.height;
+ f->fmt.pix.sizeimage = cam->v2f.fmt.pix.sizeimage;
+ f->fmt.pix.pixelformat = cam->v2f.fmt.pix.pixelformat;
+ f->fmt.pix.bytesperline = cam->v2f.fmt.pix.bytesperline;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG;
+ retval = 0;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ f->fmt.win = cam->win;
+ break;
+ default:
+ retval = -EINVAL;
+ }
+ return retval;
+}
+
+/*!
+ * V4L2 - mxc_v4l2_s_fmt function
+ *
+ * @param cam structure cam_data *
+ *
+ * @param f structure v4l2_format *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_v4l2_s_fmt(cam_data *cam, struct v4l2_format *f)
+{
+ int retval = 0;
+ int size = 0;
+ int bytesperline = 0;
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ if (!valid_mode(f->fmt.pix.pixelformat)) {
+ pr_debug("mxc_v4l2_s_fmt: format not supported\n");
+ retval = -EINVAL;
+ }
+
+ if (cam->rotation != V4L2_MXC_ROTATE_NONE)
+ pr_debug("mxc_v4l2_s_fmt: capture rotation ignored\n");
+
+ switch (f->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_YUYV:
+ f->fmt.pix.width &= ~0x1; /* Multiple of 2 */
+ size = f->fmt.pix.width * f->fmt.pix.height * 2;
+ bytesperline = f->fmt.pix.width * 2;
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ f->fmt.pix.width &= ~0x7; /* Multiple of 8 */
+ f->fmt.pix.height &= ~0x1; /* Multiple of 2 */
+ size = f->fmt.pix.width * f->fmt.pix.height * 3 / 2;
+ bytesperline = f->fmt.pix.width * 3 / 2;
+ break;
+ default:
+ /* Suppose it's YUV444 or 32bpp */
+ size = f->fmt.pix.width * f->fmt.pix.height * 4;
+ bytesperline = f->fmt.pix.width * 4;
+ pr_info("mxc_v4l2_s_fmt: default assume"
+ " to be YUV444 interleaved.\n");
+ break;
+ }
+
+ if (f->fmt.pix.bytesperline < bytesperline) {
+ f->fmt.pix.bytesperline = bytesperline;
+ } else {
+ bytesperline = f->fmt.pix.bytesperline;
+ }
+
+ if (f->fmt.pix.sizeimage > size) {
+ pr_debug("mxc_v4l2_s_fmt: sizeimage bigger than"
+ " needed.\n");
+ size = f->fmt.pix.sizeimage;
+ }
+ f->fmt.pix.sizeimage = size;
+
+ cam->v2f.fmt.pix.sizeimage = size;
+ cam->v2f.fmt.pix.bytesperline = bytesperline;
+ cam->v2f.fmt.pix.width = f->fmt.pix.width;
+ cam->v2f.fmt.pix.height = f->fmt.pix.height;
+ cam->v2f.fmt.pix.pixelformat = f->fmt.pix.pixelformat;
+ retval = 0;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ retval = verify_preview(cam, &f->fmt.win);
+ cam->win = f->fmt.win;
+ break;
+ default:
+ retval = -EINVAL;
+ }
+ return retval;
+}
+
+/*!
+ * get control param
+ *
+ * @param cam structure cam_data *
+ *
+ * @param c structure v4l2_control *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_get_v42l_control(cam_data *cam, struct v4l2_control *c)
+{
+ int status = 0;
+
+ switch (c->id) {
+ case V4L2_CID_HFLIP:
+ c->value = cam->rotation;
+ break;
+ case V4L2_CID_VFLIP:
+ c->value = cam->rotation;
+ break;
+ case V4L2_CID_MXC_ROT:
+ c->value = cam->rotation;
+ break;
+ case V4L2_CID_BRIGHTNESS:
+ c->value = cam->bright;
+ break;
+ case V4L2_CID_HUE:
+ c->value = cam->hue;
+ break;
+ case V4L2_CID_CONTRAST:
+ c->value = cam->contrast;
+ break;
+ case V4L2_CID_SATURATION:
+ c->value = cam->saturation;
+ break;
+ case V4L2_CID_RED_BALANCE:
+ c->value = cam->red;
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ c->value = cam->blue;
+ break;
+ case V4L2_CID_BLACK_LEVEL:
+ c->value = cam->ae_mode;
+ break;
+ default:
+ status = -EINVAL;
+ }
+ return status;
+}
+
+/*!
+ * V4L2 - set_control function
+ * V4L2_CID_MXC_ROT is the extention for rotation/mirroring.
+ *
+ * @param cam structure cam_data *
+ *
+ * @param c structure v4l2_control *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_set_v42l_control(cam_data *cam, struct v4l2_control *c)
+{
+ switch (c->id) {
+ case V4L2_CID_HFLIP:
+ if (c->value == 1) {
+ if ((cam->rotation != V4L2_MXC_ROTATE_VERT_FLIP) &&
+ (cam->rotation != V4L2_MXC_ROTATE_180))
+ cam->rotation = V4L2_MXC_ROTATE_HORIZ_FLIP;
+ else
+ cam->rotation = V4L2_MXC_ROTATE_180;
+ } else {
+ if (cam->rotation == V4L2_MXC_ROTATE_HORIZ_FLIP)
+ cam->rotation = V4L2_MXC_ROTATE_NONE;
+ else if (cam->rotation == V4L2_MXC_ROTATE_180)
+ cam->rotation = V4L2_MXC_ROTATE_VERT_FLIP;
+ }
+ break;
+ case V4L2_CID_VFLIP:
+ if (c->value == 1) {
+ if ((cam->rotation != V4L2_MXC_ROTATE_HORIZ_FLIP) &&
+ (cam->rotation != V4L2_MXC_ROTATE_180))
+ cam->rotation = V4L2_MXC_ROTATE_VERT_FLIP;
+ else
+ cam->rotation = V4L2_MXC_ROTATE_180;
+ } else {
+ if (cam->rotation == V4L2_MXC_ROTATE_VERT_FLIP)
+ cam->rotation = V4L2_MXC_ROTATE_NONE;
+ if (cam->rotation == V4L2_MXC_ROTATE_180)
+ cam->rotation = V4L2_MXC_ROTATE_HORIZ_FLIP;
+ }
+ break;
+ case V4L2_CID_MXC_ROT:
+ switch (c->value) {
+ case V4L2_MXC_ROTATE_NONE:
+ case V4L2_MXC_ROTATE_VERT_FLIP:
+ case V4L2_MXC_ROTATE_HORIZ_FLIP:
+ case V4L2_MXC_ROTATE_180:
+ case V4L2_MXC_ROTATE_90_RIGHT:
+ case V4L2_MXC_ROTATE_90_RIGHT_VFLIP:
+ case V4L2_MXC_ROTATE_90_RIGHT_HFLIP:
+ case V4L2_MXC_ROTATE_90_LEFT:
+ cam->rotation = c->value;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case V4L2_CID_HUE:
+ cam->hue = c->value;
+ break;
+ case V4L2_CID_CONTRAST:
+ cam->contrast = c->value;
+ break;
+ case V4L2_CID_BRIGHTNESS:
+ cam->bright = c->value;
+ case V4L2_CID_SATURATION:
+ cam->saturation = c->value;
+ case V4L2_CID_RED_BALANCE:
+ cam->red = c->value;
+ case V4L2_CID_BLUE_BALANCE:
+ cam->blue = c->value;
+ csi_enable_mclk(CSI_MCLK_I2C, true, true);
+ cam->cam_sensor->set_color(cam->bright, cam->saturation,
+ cam->red, cam->green, cam->blue);
+ csi_enable_mclk(CSI_MCLK_I2C, false, false);
+ break;
+ case V4L2_CID_BLACK_LEVEL:
+ cam->ae_mode = c->value & 0x03;
+ csi_enable_mclk(CSI_MCLK_I2C, true, true);
+ if (cam->cam_sensor->set_ae_mode)
+ cam->cam_sensor->set_ae_mode(cam->ae_mode);
+ csi_enable_mclk(CSI_MCLK_I2C, false, false);
+ break;
+ case V4L2_CID_MXC_FLASH:
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*!
+ * V4L2 - mxc_v4l2_s_param function
+ *
+ * @param cam structure cam_data *
+ *
+ * @param parm structure v4l2_streamparm *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_v4l2_s_param(cam_data *cam, struct v4l2_streamparm *parm)
+{
+ sensor_interface *param;
+ csi_signal_cfg_t csi_param;
+
+ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ pr_debug("mxc_v4l2_s_param invalid type\n");
+ return -EINVAL;
+ }
+
+ if (parm->parm.capture.timeperframe.denominator >
+ cam->standard.frameperiod.denominator) {
+ pr_debug("mxc_v4l2_s_param frame rate %d larger "
+ "than standard supported %d\n",
+ parm->parm.capture.timeperframe.denominator,
+ cam->standard.frameperiod.denominator);
+ return -EINVAL;
+ }
+
+ cam->streamparm.parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+
+ csi_enable_mclk(CSI_MCLK_I2C, true, true);
+ param = cam->cam_sensor->config
+ (&parm->parm.capture.timeperframe.denominator,
+ parm->parm.capture.capturemode);
+ csi_enable_mclk(CSI_MCLK_I2C, false, false);
+
+ cam->streamparm.parm.capture.timeperframe =
+ parm->parm.capture.timeperframe;
+
+ if ((parm->parm.capture.capturemode != 0) &&
+ (parm->parm.capture.capturemode != V4L2_MODE_HIGHQUALITY)) {
+ pr_debug("mxc_v4l2_s_param frame un-supported capture mode\n");
+ return -EINVAL;
+ }
+
+ if (parm->parm.capture.capturemode ==
+ cam->streamparm.parm.capture.capturemode) {
+ return 0;
+ }
+
+ /* resolution changed, so need to re-program the CSI */
+ csi_param.sens_clksrc = 0;
+ csi_param.clk_mode = param->clk_mode;
+ csi_param.pixclk_pol = param->pixclk_pol;
+ csi_param.data_width = param->data_width;
+ csi_param.data_pol = param->data_pol;
+ csi_param.ext_vsync = param->ext_vsync;
+ csi_param.Vsync_pol = param->Vsync_pol;
+ csi_param.Hsync_pol = param->Hsync_pol;
+ csi_init_interface(param->width, param->height, param->pixel_fmt,
+ csi_param);
+
+ if (parm->parm.capture.capturemode != V4L2_MODE_HIGHQUALITY) {
+ cam->streamparm.parm.capture.capturemode = 0;
+ } else {
+ cam->streamparm.parm.capture.capturemode =
+ V4L2_MODE_HIGHQUALITY;
+ cam->streamparm.parm.capture.extendedmode =
+ parm->parm.capture.extendedmode;
+ cam->streamparm.parm.capture.readbuffers = 1;
+ }
+ return 0;
+}
+
+/*!
+ * Dequeue one V4L capture buffer
+ *
+ * @param cam structure cam_data *
+ * @param buf structure v4l2_buffer *
+ *
+ * @return status 0 success, EINVAL invalid frame number,
+ * ETIME timeout, ERESTARTSYS interrupted by user
+ */
+static int mxc_v4l_dqueue(cam_data *cam, struct v4l2_buffer *buf)
+{
+ int retval = 0;
+ struct mxc_v4l_frame *frame;
+
+ if (!wait_event_interruptible_timeout(cam->enc_queue,
+ cam->enc_counter != 0, 10 * HZ)) {
+ printk(KERN_ERR "mxc_v4l_dqueue timeout enc_counter %x\n",
+ cam->enc_counter);
+ return -ETIME;
+ } else if (signal_pending(current)) {
+ printk(KERN_ERR "mxc_v4l_dqueue() interrupt received\n");
+ return -ERESTARTSYS;
+ }
+
+ cam->enc_counter--;
+
+ frame = list_entry(cam->done_q.next, struct mxc_v4l_frame, queue);
+ list_del(cam->done_q.next);
+ if (frame->buffer.flags & V4L2_BUF_FLAG_DONE) {
+ frame->buffer.flags &= ~V4L2_BUF_FLAG_DONE;
+ } else if (frame->buffer.flags & V4L2_BUF_FLAG_QUEUED) {
+ printk(KERN_ERR "VIDIOC_DQBUF: Buffer not filled.\n");
+ frame->buffer.flags &= ~V4L2_BUF_FLAG_QUEUED;
+ retval = -EINVAL;
+ } else if ((frame->buffer.flags & 0x7) == V4L2_BUF_FLAG_MAPPED) {
+ printk(KERN_ERR "VIDIOC_DQBUF: Buffer not queued.\n");
+ retval = -EINVAL;
+ }
+
+ buf->bytesused = cam->v2f.fmt.pix.sizeimage;
+ buf->index = frame->index;
+ buf->flags = frame->buffer.flags;
+
+ return retval;
+}
+
+/*!
+ * V4L interface - open function
+ *
+ * @param inode structure inode *
+ * @param file structure file *
+ *
+ * @return status 0 success, ENODEV invalid device instance,
+ * ENODEV timeout, ERESTARTSYS interrupted by user
+ */
+static int mxc_v4l_open(struct inode *inode, struct file *file)
+{
+ sensor_interface *param;
+ csi_signal_cfg_t csi_param;
+ struct video_device *dev = video_devdata(file);
+ cam_data *cam = video_get_drvdata(dev);
+ int err = 0;
+
+ if (!cam) {
+ pr_info("Internal error, cam_data not found!\n");
+ return -ENODEV;
+ }
+
+ if (down_interruptible(&cam->busy_lock))
+ return -EINTR;
+
+ if (signal_pending(current))
+ goto oops;
+
+ if (cam->open_count++ == 0) {
+ wait_event_interruptible(cam->power_queue,
+ cam->low_power == false);
+
+ err = prp_enc_select(cam);
+
+ cam->enc_counter = 0;
+ cam->skip_frame = 0;
+ INIT_LIST_HEAD(&cam->ready_q);
+ INIT_LIST_HEAD(&cam->working_q);
+ INIT_LIST_HEAD(&cam->done_q);
+
+ csi_enable_mclk(CSI_MCLK_I2C, true, true);
+ param = cam->cam_sensor->reset();
+ if (param == NULL) {
+ cam->open_count--;
+ csi_enable_mclk(CSI_MCLK_I2C, false, false);
+ err = -ENODEV;
+ goto oops;
+ }
+ csi_param.sens_clksrc = 0;
+ csi_param.clk_mode = param->clk_mode;
+ csi_param.pixclk_pol = param->pixclk_pol;
+ csi_param.data_width = param->data_width;
+ csi_param.data_pol = param->data_pol;
+ csi_param.ext_vsync = param->ext_vsync;
+ csi_param.Vsync_pol = param->Vsync_pol;
+ csi_param.Hsync_pol = param->Hsync_pol;
+ csi_init_interface(param->width, param->height,
+ param->pixel_fmt, csi_param);
+ cam->cam_sensor->get_color(&cam->bright, &cam->saturation,
+ &cam->red, &cam->green, &cam->blue);
+ if (cam->cam_sensor->get_ae_mode)
+ cam->cam_sensor->get_ae_mode(&cam->ae_mode);
+ csi_enable_mclk(CSI_MCLK_I2C, false, false);
+ prp_init(cam);
+
+ }
+
+ file->private_data = dev;
+ oops:
+ up(&cam->busy_lock);
+ return err;
+}
+
+/*!
+ * V4L interface - close function
+ *
+ * @param inode struct inode *
+ * @param file struct file *
+ *
+ * @return 0 success
+ */
+static int mxc_v4l_close(struct inode *inode, struct file *file)
+{
+ struct video_device *dev = video_devdata(file);
+ int err = 0;
+ cam_data *cam = video_get_drvdata(dev);
+
+ /* for the case somebody hit the ctrl C */
+ if (cam->overlay_pid == current->pid) {
+ err = stop_preview(cam);
+ cam->overlay_on = false;
+ }
+ if (cam->capture_pid == current->pid) {
+ err |= mxc_streamoff(cam);
+ cam->capture_on = false;
+ wake_up_interruptible(&cam->enc_queue);
+ }
+
+ if (--cam->open_count == 0) {
+ wait_event_interruptible(cam->power_queue,
+ cam->low_power == false);
+ pr_debug("mxc_v4l_close: release resource\n");
+
+ err |= prp_enc_deselect(cam);
+
+ mxc_free_frame_buf(cam);
+ file->private_data = NULL;
+
+ /* capture off */
+ wake_up_interruptible(&cam->enc_queue);
+ mxc_free_frames(cam);
+ cam->enc_counter++;
+ prp_exit(cam);
+ }
+
+ return err;
+}
+
+#ifdef CONFIG_VIDEO_MXC_CSI_DMA
+#include <mach/dma.h>
+
+#define CSI_DMA_STATUS_IDLE 0 /* DMA is not started */
+#define CSI_DMA_STATUS_WORKING 1 /* DMA is transfering the data */
+#define CSI_DMA_STATUS_DONE 2 /* One frame completes successfully */
+#define CSI_DMA_STATUS_ERROR 3 /* Error occurs during the DMA */
+
+/*
+ * Sometimes the start of the DMA is not synchronized with the CSI
+ * SOF (Start of Frame) interrupt which will lead to incorrect
+ * captured image. In this case the driver will re-try capturing
+ * another frame. The following macro defines the maximum re-try
+ * times.
+ */
+#define CSI_DMA_RETRY 8
+
+/*
+ * Size of the physical contiguous memory area used to hold image data
+ * transfered by DMA. It can be less than the size of the image data.
+ */
+#define CSI_MEM_SIZE (1024 * 600)
+
+/* Number of bytes for one DMA transfer */
+#define CSI_DMA_LENGTH (1024 * 200)
+
+static int g_dma_channel;
+static int g_dma_status = CSI_DMA_STATUS_DONE;
+static volatile int g_dma_completed; /* number of completed DMA transfers */
+static volatile int g_dma_copied; /* number of copied DMA transfers */
+static struct tasklet_struct g_dma_tasklet;
+static char *g_user_buf; /* represents the buf passed by read() */
+static int g_user_count; /* represents the count passed by read() */
+
+/*!
+ * @brief setup the DMA to transfer data
+ * There may be more than one DMA to transfer the whole image. Those
+ * DMAs work like chain. This function is used to setup the DMA in
+ * case there is enough space to hold the data.
+ * @param data pointer to the cam structure
+ */
+static void mxc_csi_dma_chaining(void *data)
+{
+ cam_data *cam = (cam_data *) data;
+ int count, chained = 0;
+ int max_dma = CSI_MEM_SIZE / CSI_DMA_LENGTH;
+ mxc_dma_requestbuf_t dma_request;
+
+ while (chained * CSI_DMA_LENGTH < g_user_count) {
+ /*
+ * Calculate how many bytes the DMA should transfer. It may
+ * be less than CSI_DMA_LENGTH if the DMA is the last one.
+ */
+ if ((chained + 1) * CSI_DMA_LENGTH > g_user_count)
+ count = g_user_count - chained * CSI_DMA_LENGTH;
+ else
+ count = CSI_DMA_LENGTH;
+ pr_debug("%s() DMA chained count = %d\n", __FUNCTION__, count);
+
+ /* Config DMA */
+ memset(&dma_request, 0, sizeof(mxc_dma_requestbuf_t));
+ dma_request.dst_addr = cam->still_buf[0]
+ + (chained % max_dma) * CSI_DMA_LENGTH;
+ dma_request.src_addr = (dma_addr_t) CSI_CSIRXFIFO_PHYADDR;
+ dma_request.num_of_bytes = count;
+ mxc_dma_config(g_dma_channel, &dma_request, 1,
+ MXC_DMA_MODE_READ);
+
+ chained++;
+ }
+}
+
+/*!
+ * @brief Copy image data from physical contiguous memory to user space buffer
+ * Once the data are copied, there will be more spare space in the
+ * physical contiguous memory to receive data from DMA.
+ * @param data pointer to the cam structure
+ */
+static void mxc_csi_dma_task(unsigned long data)
+{
+ cam_data *cam = (cam_data *) data;
+ int count;
+ int max_dma = CSI_MEM_SIZE / CSI_DMA_LENGTH;
+
+ while (g_dma_copied < g_dma_completed) {
+ /*
+ * Calculate how many bytes the DMA has transfered. It may
+ * be less than CSI_DMA_LENGTH if the DMA is the last one.
+ */
+ if ((g_dma_copied + 1) * CSI_DMA_LENGTH > g_user_count)
+ count = g_user_count - g_dma_copied * CSI_DMA_LENGTH;
+ else
+ count = CSI_DMA_LENGTH;
+ if (copy_to_user(g_user_buf + g_dma_copied * CSI_DMA_LENGTH,
+ cam->still_buf_vaddr + (g_dma_copied % max_dma)
+ * CSI_DMA_LENGTH, count))
+ pr_debug("Warning: some bytes not copied\n");
+
+ g_dma_copied++;
+ }
+
+ /* If the whole image has been captured */
+ if (g_dma_copied * CSI_DMA_LENGTH >= g_user_count) {
+ cam->still_counter++;
+ wake_up_interruptible(&cam->still_queue);
+ }
+
+ pr_debug("%s() DMA completed = %d copied = %d\n",
+ __FUNCTION__, g_dma_completed, g_dma_copied);
+}
+
+/*!
+ * @brief DMA interrupt callback function
+ * @param data pointer to the cam structure
+ * @param error DMA error flag
+ * @param count number of bytes transfered by the DMA
+ */
+static void mxc_csi_dma_callback(void *data, int error, unsigned int count)
+{
+ cam_data *cam = (cam_data *) data;
+ int max_dma = CSI_MEM_SIZE / CSI_DMA_LENGTH;
+ unsigned long lock_flags;
+
+ spin_lock_irqsave(&cam->int_lock, lock_flags);
+
+ g_dma_completed++;
+
+ if (error != MXC_DMA_DONE) {
+ g_dma_status = CSI_DMA_STATUS_ERROR;
+ pr_debug("%s() DMA error\n", __FUNCTION__);
+ }
+
+ /* If the whole image has been captured */
+ if ((g_dma_status != CSI_DMA_STATUS_ERROR)
+ && (g_dma_completed * CSI_DMA_LENGTH >= g_user_count))
+ g_dma_status = CSI_DMA_STATUS_DONE;
+
+ if ((g_dma_status == CSI_DMA_STATUS_WORKING) &&
+ (g_dma_completed >= g_dma_copied + max_dma)) {
+ g_dma_status = CSI_DMA_STATUS_ERROR;
+ pr_debug("%s() Previous buffer over written\n", __FUNCTION__);
+ }
+
+ /* Schedule the tasklet */
+ tasklet_schedule(&g_dma_tasklet);
+
+ spin_unlock_irqrestore(&cam->int_lock, lock_flags);
+
+ pr_debug("%s() count = %d bytes\n", __FUNCTION__, count);
+}
+
+/*!
+ * @brief CSI interrupt callback function
+ * @param data pointer to the cam structure
+ * @param status CSI interrupt status
+ */
+static void mxc_csi_irq_callback(void *data, unsigned long status)
+{
+ cam_data *cam = (cam_data *) data;
+ unsigned long lock_flags;
+
+ spin_lock_irqsave(&cam->int_lock, lock_flags);
+
+ /* Wait for SOF (Start of Frame) interrupt to sync the image */
+ if (status & BIT_SOF_INT) {
+ if (g_dma_status == CSI_DMA_STATUS_IDLE) {
+ /* Start DMA transfer to capture image */
+ mxc_dma_enable(g_dma_channel);
+ g_dma_status = CSI_DMA_STATUS_WORKING;
+ pr_debug("%s() DMA started.\n", __FUNCTION__);
+ } else if (g_dma_status == CSI_DMA_STATUS_WORKING) {
+ /*
+ * Another SOF occurs during DMA transfer. In this
+ * case the image is not synchronized so need to
+ * report error and probably try again.
+ */
+ g_dma_status = CSI_DMA_STATUS_ERROR;
+ pr_debug("%s() Image is not synchronized with DMA - "
+ "SOF before DMA completes\n", __FUNCTION__);
+ }
+ }
+
+ spin_unlock_irqrestore(&cam->int_lock, lock_flags);
+
+ pr_debug("%s() g_dma_status = %d\n", __FUNCTION__, g_dma_status);
+}
+
+/*!
+ * V4L interface - read function
+ *
+ * @param file struct file *
+ * @param read buf char *
+ * @param count size_t
+ * @param ppos structure loff_t *
+ *
+ * @return bytes read
+ */
+static ssize_t
+mxc_v4l_read(struct file *file, char *buf, size_t count, loff_t *ppos)
+{
+ int err = 0;
+ struct video_device *dev = video_devdata(file);
+ cam_data *cam = video_get_drvdata(dev);
+ int retry = CSI_DMA_RETRY;
+
+ g_user_buf = buf;
+
+ if (down_interruptible(&cam->busy_lock))
+ return -EINTR;
+
+ /* Video capture and still image capture are exclusive */
+ if (cam->capture_on == true) {
+ err = -EBUSY;
+ goto exit0;
+ }
+
+ /* The CSI-DMA can not do CSC */
+ if (cam->v2f.fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV) {
+ pr_info("mxc_v4l_read support YUYV pixel format only\n");
+ err = -EINVAL;
+ goto exit0;
+ }
+
+ /* The CSI-DMA can not do resize or crop */
+ if ((cam->v2f.fmt.pix.width != cam->crop_bounds.width)
+ || (cam->v2f.fmt.pix.height != cam->crop_bounds.height)) {
+ pr_info("mxc_v4l_read resize is not supported\n");
+ pr_info("supported image size width = %d height = %d\n",
+ cam->crop_bounds.width, cam->crop_bounds.height);
+ err = -EINVAL;
+ goto exit0;
+ }
+ if ((cam->crop_current.left != cam->crop_bounds.left)
+ || (cam->crop_current.width != cam->crop_bounds.width)
+ || (cam->crop_current.top != cam->crop_bounds.top)
+ || (cam->crop_current.height != cam->crop_bounds.height)) {
+ pr_info("mxc_v4l_read cropping is not supported\n");
+ err = -EINVAL;
+ goto exit0;
+ }
+
+ cam->still_buf_vaddr = dma_alloc_coherent(0,
+ PAGE_ALIGN(CSI_MEM_SIZE),
+ &cam->still_buf[0],
+ GFP_DMA | GFP_KERNEL);
+
+ if (!cam->still_buf_vaddr) {
+ pr_info("mxc_v4l_read failed at allocate still_buf\n");
+ err = -ENOBUFS;
+ goto exit0;
+ }
+
+ /* Initialize DMA */
+ g_dma_channel = mxc_dma_request(MXC_DMA_CSI_RX, "CSI RX DMA");
+ if (g_dma_channel < 0) {
+ pr_debug("mxc_v4l_read failed to request DMA channel\n");
+ err = -EIO;
+ goto exit1;
+ }
+
+ err = mxc_dma_callback_set(g_dma_channel,
+ (mxc_dma_callback_t) mxc_csi_dma_callback,
+ (void *)cam);
+ if (err != 0) {
+ pr_debug("mxc_v4l_read failed to set DMA callback\n");
+ err = -EIO;
+ goto exit2;
+ }
+
+ g_user_buf = buf;
+ if (cam->v2f.fmt.pix.sizeimage < count)
+ g_user_count = cam->v2f.fmt.pix.sizeimage;
+ else
+ g_user_count = count & ~0x3;
+
+ tasklet_init(&g_dma_tasklet, mxc_csi_dma_task, (unsigned long)cam);
+ g_dma_status = CSI_DMA_STATUS_DONE;
+ csi_set_callback(mxc_csi_irq_callback, cam);
+ csi_enable_prpif(0);
+
+ /* clear current SOF first */
+ csi_clear_status(BIT_SOF_INT);
+ csi_enable_mclk(CSI_MCLK_RAW, true, true);
+
+ do {
+ g_dma_completed = g_dma_copied = 0;
+ mxc_csi_dma_chaining(cam);
+ cam->still_counter = 0;
+ g_dma_status = CSI_DMA_STATUS_IDLE;
+
+ if (!wait_event_interruptible_timeout(cam->still_queue,
+ cam->still_counter != 0,
+ 10 * HZ)) {
+ pr_info("mxc_v4l_read timeout counter %x\n",
+ cam->still_counter);
+ err = -ETIME;
+ goto exit3;
+ }
+
+ if (g_dma_status == CSI_DMA_STATUS_DONE)
+ break;
+
+ if (retry-- == 0)
+ break;
+
+ pr_debug("Now retry image capture\n");
+ } while (1);
+
+ if (g_dma_status != CSI_DMA_STATUS_DONE)
+ err = -EIO;
+
+ exit3:
+ csi_enable_prpif(1);
+ g_dma_status = CSI_DMA_STATUS_DONE;
+ csi_set_callback(0, 0);
+ csi_enable_mclk(CSI_MCLK_RAW, false, false);
+ tasklet_kill(&g_dma_tasklet);
+
+ exit2:
+ mxc_dma_free(g_dma_channel);
+
+ exit1:
+ dma_free_coherent(0, PAGE_ALIGN(CSI_MEM_SIZE),
+ cam->still_buf_vaddr, cam->still_buf[0]);
+ cam->still_buf[0] = 0;
+
+ exit0:
+ up(&cam->busy_lock);
+ if (err < 0)
+ return err;
+ else
+ return g_user_count;
+}
+#else
+/*!
+ * V4L interface - read function
+ *
+ * @param file struct file *
+ * @param read buf char *
+ * @param count size_t
+ * @param ppos structure loff_t *
+ *
+ * @return bytes read
+ */
+static ssize_t
+mxc_v4l_read(struct file *file, char *buf, size_t count, loff_t *ppos)
+{
+ int err = 0;
+ u8 *v_address;
+ struct video_device *dev = video_devdata(file);
+ cam_data *cam = video_get_drvdata(dev);
+
+ if (down_interruptible(&cam->busy_lock))
+ return -EINTR;
+
+ /* Video capture and still image capture are exclusive */
+ if (cam->capture_on == true) {
+ err = -EBUSY;
+ goto exit0;
+ }
+
+ v_address = dma_alloc_coherent(0,
+ PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage),
+ &cam->still_buf[0],
+ GFP_DMA | GFP_KERNEL);
+
+ if (!v_address) {
+ pr_info("mxc_v4l_read failed at allocate still_buf\n");
+ err = -ENOBUFS;
+ goto exit0;
+ }
+
+ if (prp_still_select(cam)) {
+ err = -EIO;
+ goto exit1;
+ }
+
+ cam->still_counter = 0;
+ if (cam->csi_start(cam)) {
+ err = -EIO;
+ goto exit2;
+ }
+
+ if (!wait_event_interruptible_timeout(cam->still_queue,
+ cam->still_counter != 0,
+ 10 * HZ)) {
+ pr_info("mxc_v4l_read timeout counter %x\n",
+ cam->still_counter);
+ err = -ETIME;
+ goto exit2;
+ }
+ err = copy_to_user(buf, v_address, cam->v2f.fmt.pix.sizeimage);
+
+ exit2:
+ prp_still_deselect(cam);
+
+ exit1:
+ dma_free_coherent(0, cam->v2f.fmt.pix.sizeimage, v_address,
+ cam->still_buf[0]);
+ cam->still_buf[0] = 0;
+
+ exit0:
+ up(&cam->busy_lock);
+ if (err < 0)
+ return err;
+ else
+ return (cam->v2f.fmt.pix.sizeimage - err);
+}
+#endif /* CONFIG_VIDEO_MXC_CSI_DMA */
+
+/*!
+ * V4L interface - ioctl function
+ *
+ * @param inode struct inode *
+ *
+ * @param file struct file *
+ *
+ * @param ioctlnr unsigned int
+ *
+ * @param arg void *
+ *
+ * @return 0 success, ENODEV for invalid device instance,
+ * -1 for other errors.
+ */
+static int
+mxc_v4l_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int ioctlnr, void *arg)
+{
+ struct video_device *dev = video_devdata(file);
+ cam_data *cam = video_get_drvdata(dev);
+ int retval = 0;
+ unsigned long lock_flags;
+
+ if (!cam)
+ return -EBADF;
+
+ wait_event_interruptible(cam->power_queue, cam->low_power == false);
+ /* make this _really_ smp-safe */
+ if (down_interruptible(&cam->busy_lock))
+ return -EBUSY;
+
+ switch (ioctlnr) {
+ /*!
+ * V4l2 VIDIOC_QUERYCAP ioctl
+ */
+ case VIDIOC_QUERYCAP:{
+ struct v4l2_capability *cap = arg;
+ strcpy(cap->driver, "mxc_v4l2");
+ cap->version = KERNEL_VERSION(0, 1, 11);
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_STREAMING
+ | V4L2_CAP_READWRITE;
+ cap->card[0] = '\0';
+ cap->bus_info[0] = '\0';
+ retval = 0;
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_G_FMT ioctl
+ */
+ case VIDIOC_G_FMT:{
+ struct v4l2_format *gf = arg;
+ retval = mxc_v4l2_g_fmt(cam, gf);
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_S_FMT ioctl
+ */
+ case VIDIOC_S_FMT:{
+ struct v4l2_format *sf = arg;
+ retval = mxc_v4l2_s_fmt(cam, sf);
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_REQBUFS ioctl
+ */
+ case VIDIOC_REQBUFS:{
+ struct v4l2_requestbuffers *req = arg;
+ if (req->count > FRAME_NUM) {
+ pr_info("VIDIOC_REQBUFS: not enough buffer\n");
+ req->count = FRAME_NUM;
+ }
+
+ if ((req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
+ (req->memory != V4L2_MEMORY_MMAP)) {
+ pr_debug("VIDIOC_REQBUFS: wrong buffer type\n");
+ retval = -EINVAL;
+ break;
+ }
+
+ mxc_streamoff(cam);
+ mxc_free_frame_buf(cam);
+
+ retval = mxc_allocate_frame_buf(cam, req->count);
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_QUERYBUF ioctl
+ */
+ case VIDIOC_QUERYBUF:{
+ struct v4l2_buffer *buf = arg;
+ int index = buf->index;
+
+ if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ pr_debug
+ ("VIDIOC_QUERYBUFS: wrong buffer type\n");
+ retval = -EINVAL;
+ break;
+ }
+
+ memset(buf, 0, sizeof(buf));
+ buf->index = index;
+
+ down(&cam->param_lock);
+ retval = mxc_v4l2_buffer_status(cam, buf);
+ up(&cam->param_lock);
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_QBUF ioctl
+ */
+ case VIDIOC_QBUF:{
+ struct v4l2_buffer *buf = arg;
+ int index = buf->index;
+
+ pr_debug("VIDIOC_QBUF: %d\n", buf->index);
+
+ spin_lock_irqsave(&cam->int_lock, lock_flags);
+ if ((cam->frame[index].buffer.flags & 0x7) ==
+ V4L2_BUF_FLAG_MAPPED) {
+ cam->frame[index].buffer.flags |=
+ V4L2_BUF_FLAG_QUEUED;
+ if (cam->skip_frame > 0) {
+ list_add_tail(&cam->frame[index].queue,
+ &cam->working_q);
+ retval =
+ cam->enc_update_eba(cam->
+ frame[index].
+ paddress,
+ &cam->
+ ping_pong_csi);
+ cam->skip_frame = 0;
+ } else {
+ list_add_tail(&cam->frame[index].queue,
+ &cam->ready_q);
+ }
+ } else if (cam->frame[index].buffer.flags &
+ V4L2_BUF_FLAG_QUEUED) {
+ pr_debug
+ ("VIDIOC_QBUF: buffer already queued\n");
+ } else if (cam->frame[index].buffer.
+ flags & V4L2_BUF_FLAG_DONE) {
+ pr_debug
+ ("VIDIOC_QBUF: overwrite done buffer.\n");
+ cam->frame[index].buffer.flags &=
+ ~V4L2_BUF_FLAG_DONE;
+ cam->frame[index].buffer.flags |=
+ V4L2_BUF_FLAG_QUEUED;
+ }
+ buf->flags = cam->frame[index].buffer.flags;
+ spin_unlock_irqrestore(&cam->int_lock, lock_flags);
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_DQBUF ioctl
+ */
+ case VIDIOC_DQBUF:{
+ struct v4l2_buffer *buf = arg;
+
+ retval = mxc_v4l_dqueue(cam, buf);
+
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_STREAMON ioctl
+ */
+ case VIDIOC_STREAMON:{
+ cam->capture_on = true;
+ retval = mxc_streamon(cam);
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_STREAMOFF ioctl
+ */
+ case VIDIOC_STREAMOFF:{
+ retval = mxc_streamoff(cam);
+ cam->capture_on = false;
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_G_CTRL ioctl
+ */
+ case VIDIOC_G_CTRL:{
+ retval = mxc_get_v42l_control(cam, arg);
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_S_CTRL ioctl
+ */
+ case VIDIOC_S_CTRL:{
+ retval = mxc_set_v42l_control(cam, arg);
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_CROPCAP ioctl
+ */
+ case VIDIOC_CROPCAP:{
+ struct v4l2_cropcap *cap = arg;
+
+ if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ cap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) {
+ retval = -EINVAL;
+ break;
+ }
+ cap->bounds = cam->crop_bounds;
+ cap->defrect = cam->crop_defrect;
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_G_CROP ioctl
+ */
+ case VIDIOC_G_CROP:{
+ struct v4l2_crop *crop = arg;
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) {
+ retval = -EINVAL;
+ break;
+ }
+ crop->c = cam->crop_current;
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_S_CROP ioctl
+ */
+ case VIDIOC_S_CROP:{
+ struct v4l2_crop *crop = arg;
+ struct v4l2_rect *b = &cam->crop_bounds;
+ int i;
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) {
+ retval = -EINVAL;
+ break;
+ }
+
+ crop->c.top = (crop->c.top < b->top) ? b->top
+ : crop->c.top;
+ if (crop->c.top > b->top + b->height)
+ crop->c.top = b->top + b->height - 1;
+ if (crop->c.height > b->top + b->height - crop->c.top)
+ crop->c.height =
+ b->top + b->height - crop->c.top;
+
+ crop->c.left = (crop->c.left < b->left) ? b->left
+ : crop->c.left;
+ if (crop->c.left > b->left + b->width)
+ crop->c.left = b->left + b->width - 1;
+ if (crop->c.width > b->left - crop->c.left + b->width)
+ crop->c.width =
+ b->left - crop->c.left + b->width;
+
+ crop->c.width &= ~0x1;
+
+ /*
+ * MX27 PrP limitation:
+ * The right spare space (CSI_FRAME_X_SIZE
+ * - SOURCE_LINE_STRIDE - PICTURE_X_SIZE)) must be
+ * multiple of 32.
+ * So we tune the crop->c.left value to the closest
+ * desired cropping value and meet the PrP requirement.
+ */
+ i = ((b->left + b->width)
+ - (crop->c.left + crop->c.width)) % 32;
+ if (i <= 16) {
+ if (crop->c.left + crop->c.width + i
+ <= b->left + b->width)
+ crop->c.left += i;
+ else if (crop->c.left - (32 - i) >= b->left)
+ crop->c.left -= 32 - i;
+ else {
+ retval = -EINVAL;
+ break;
+ }
+ } else {
+ if (crop->c.left - (32 - i) >= b->left)
+ crop->c.left -= 32 - i;
+ else if (crop->c.left + crop->c.width + i
+ <= b->left + b->width)
+ crop->c.left += i;
+ else {
+ retval = -EINVAL;
+ break;
+ }
+ }
+
+ cam->crop_current = crop->c;
+
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_OVERLAY ioctl
+ */
+ case VIDIOC_OVERLAY:{
+ int *on = arg;
+ if (*on) {
+ cam->overlay_on = true;
+ retval = start_preview(cam);
+ }
+ if (!*on) {
+ retval = stop_preview(cam);
+ cam->overlay_on = false;
+ }
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_G_FBUF ioctl
+ */
+ case VIDIOC_G_FBUF:{
+ struct v4l2_framebuffer *fb = arg;
+ struct fb_var_screeninfo *var;
+
+ if (cam->output >= num_registered_fb) {
+ retval = -EINVAL;
+ break;
+ }
+
+ var = &registered_fb[cam->output]->var;
+ cam->v4l2_fb.fmt.width = var->xres;
+ cam->v4l2_fb.fmt.height = var->yres;
+ cam->v4l2_fb.fmt.bytesperline =
+ var->xres_virtual * var->bits_per_pixel;
+ cam->v4l2_fb.fmt.colorspace = V4L2_COLORSPACE_SRGB;
+ *fb = cam->v4l2_fb;
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_S_FBUF ioctl
+ */
+ case VIDIOC_S_FBUF:{
+ struct v4l2_framebuffer *fb = arg;
+ cam->v4l2_fb.flags = fb->flags;
+ cam->v4l2_fb.fmt.pixelformat = fb->fmt.pixelformat;
+ break;
+ }
+
+ case VIDIOC_G_PARM:{
+ struct v4l2_streamparm *parm = arg;
+ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ pr_debug("VIDIOC_G_PARM invalid type\n");
+ retval = -EINVAL;
+ break;
+ }
+ parm->parm.capture = cam->streamparm.parm.capture;
+ break;
+ }
+ case VIDIOC_S_PARM:{
+ struct v4l2_streamparm *parm = arg;
+ retval = mxc_v4l2_s_param(cam, parm);
+ break;
+ }
+
+ /* linux v4l2 bug, kernel c0485619 user c0405619 */
+ case VIDIOC_ENUMSTD:{
+ struct v4l2_standard *e = arg;
+ *e = cam->standard;
+ pr_debug("VIDIOC_ENUMSTD call\n");
+ retval = 0;
+ break;
+ }
+
+ case VIDIOC_G_STD:{
+ v4l2_std_id *e = arg;
+ *e = cam->standard.id;
+ break;
+ }
+
+ case VIDIOC_S_STD:{
+ break;
+ }
+
+ case VIDIOC_ENUMOUTPUT:
+ {
+ struct v4l2_output *output = arg;
+
+ if (output->index >= num_registered_fb) {
+ retval = -EINVAL;
+ break;
+ }
+
+ strncpy(output->name,
+ registered_fb[output->index]->fix.id, 31);
+ output->type = V4L2_OUTPUT_TYPE_ANALOG;
+ output->audioset = 0;
+ output->modulator = 0;
+ output->std = V4L2_STD_UNKNOWN;
+
+ break;
+ }
+ case VIDIOC_G_OUTPUT:
+ {
+ int *p_output_num = arg;
+
+ *p_output_num = cam->output;
+ break;
+ }
+ case VIDIOC_S_OUTPUT:
+ {
+ int *p_output_num = arg;
+
+ if (*p_output_num >= num_registered_fb) {
+ retval = -EINVAL;
+ break;
+ }
+
+ cam->output = *p_output_num;
+ break;
+ }
+
+ case VIDIOC_ENUM_FMT:
+ case VIDIOC_TRY_FMT:
+ case VIDIOC_QUERYCTRL:
+ case VIDIOC_ENUMINPUT:
+ case VIDIOC_G_INPUT:
+ case VIDIOC_S_INPUT:
+ case VIDIOC_G_TUNER:
+ case VIDIOC_S_TUNER:
+ case VIDIOC_G_FREQUENCY:
+ case VIDIOC_S_FREQUENCY:
+ default:
+ retval = -EINVAL;
+ break;
+ }
+
+ up(&cam->busy_lock);
+ return retval;
+}
+
+/*
+ * V4L interface - ioctl function
+ *
+ * @return None
+ */
+static int
+mxc_v4l_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(inode, file, cmd, arg, mxc_v4l_do_ioctl);
+}
+
+/*!
+ * V4L interface - mmap function
+ *
+ * @param file structure file *
+ *
+ * @param vma structure vm_area_struct *
+ *
+ * @return status 0 Success, EINTR busy lock error, ENOBUFS remap_page error
+ */
+static int mxc_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct video_device *dev = video_devdata(file);
+ unsigned long size;
+ int res = 0;
+ cam_data *cam = video_get_drvdata(dev);
+
+ pr_debug("pgoff=0x%lx, start=0x%lx, end=0x%lx\n",
+ vma->vm_pgoff, vma->vm_start, vma->vm_end);
+
+ /* make this _really_ smp-safe */
+ if (down_interruptible(&cam->busy_lock))
+ return -EINTR;
+
+ size = vma->vm_end - vma->vm_start;
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ if (remap_pfn_range(vma, vma->vm_start,
+ vma->vm_pgoff, size, vma->vm_page_prot)) {
+ pr_debug("mxc_mmap: remap_pfn_range failed\n");
+ res = -ENOBUFS;
+ goto mxc_mmap_exit;
+ }
+
+ vma->vm_flags &= ~VM_IO; /* using shared anonymous pages */
+
+ mxc_mmap_exit:
+ up(&cam->busy_lock);
+ return res;
+}
+
+/*!
+ * V4L interface - poll function
+ *
+ * @param file structure file *
+ *
+ * @param wait structure poll_table *
+ *
+ * @return status POLLIN | POLLRDNORM
+ */
+static unsigned int mxc_poll(struct file *file, poll_table * wait)
+{
+ struct video_device *dev = video_devdata(file);
+ cam_data *cam = video_get_drvdata(dev);
+ wait_queue_head_t *queue = NULL;
+ int res = POLLIN | POLLRDNORM;
+
+ if (down_interruptible(&cam->busy_lock))
+ return -EINTR;
+
+ queue = &cam->enc_queue;
+ poll_wait(file, queue, wait);
+
+ up(&cam->busy_lock);
+ return res;
+}
+
+static struct
+file_operations mxc_v4l_fops = {
+ .owner = THIS_MODULE,
+ .open = mxc_v4l_open,
+ .release = mxc_v4l_close,
+ .read = mxc_v4l_read,
+ .ioctl = mxc_v4l_ioctl,
+ .mmap = mxc_mmap,
+ .poll = mxc_poll,
+};
+
+static struct video_device mxc_v4l_template = {
+ .name = "Mxc Camera",
+ .vfl_type = VID_TYPE_CAPTURE,
+ .fops = &mxc_v4l_fops,
+ .release = video_device_release,
+};
+
+static void camera_platform_release(struct device *device)
+{
+}
+
+/*! Device Definition for Mt9v111 devices */
+static struct platform_device mxc_v4l2_devices = {
+ .name = "mxc_v4l2",
+ .dev = {
+ .release = camera_platform_release,
+ },
+ .id = 0,
+};
+
+extern struct camera_sensor camera_sensor_if;
+
+/*!
+* Camera V4l2 callback function.
+*
+* @return status
+*/
+static void camera_callback(u32 mask, void *dev)
+{
+ struct mxc_v4l_frame *done_frame;
+ struct mxc_v4l_frame *ready_frame;
+
+ cam_data *cam = (cam_data *) dev;
+ if (cam == NULL)
+ return;
+
+ if (list_empty(&cam->working_q)) {
+ printk(KERN_ERR "camera_callback: working queue empty\n");
+ return;
+ }
+
+ done_frame =
+ list_entry(cam->working_q.next, struct mxc_v4l_frame, queue);
+ if (done_frame->buffer.flags & V4L2_BUF_FLAG_QUEUED) {
+ done_frame->buffer.flags |= V4L2_BUF_FLAG_DONE;
+ done_frame->buffer.flags &= ~V4L2_BUF_FLAG_QUEUED;
+
+ if (list_empty(&cam->ready_q)) {
+ cam->skip_frame++;
+ } else {
+ ready_frame =
+ list_entry(cam->ready_q.next, struct mxc_v4l_frame,
+ queue);
+ list_del(cam->ready_q.next);
+ list_add_tail(&ready_frame->queue, &cam->working_q);
+ cam->enc_update_eba(ready_frame->paddress,
+ &cam->ping_pong_csi);
+ }
+
+ /* Added to the done queue */
+ list_del(cam->working_q.next);
+ list_add_tail(&done_frame->queue, &cam->done_q);
+
+ /* Wake up the queue */
+ cam->enc_counter++;
+ wake_up_interruptible(&cam->enc_queue);
+ } else {
+ printk(KERN_ERR "camera_callback :buffer not queued\n");
+ }
+}
+
+/*!
+ * initialize cam_data structure
+ *
+ * @param cam structure cam_data *
+ *
+ * @return status 0 Success
+ */
+static void init_camera_struct(cam_data *cam)
+{
+ int i;
+
+ /* Default everything to 0 */
+ memset(cam, 0, sizeof(cam_data));
+
+ init_MUTEX(&cam->param_lock);
+ init_MUTEX(&cam->busy_lock);
+
+ cam->video_dev = video_device_alloc();
+ if (cam->video_dev == NULL)
+ return;
+
+ *(cam->video_dev) = mxc_v4l_template;
+
+ video_set_drvdata(cam->video_dev, cam);
+ dev_set_drvdata(&mxc_v4l2_devices.dev, (void *)cam);
+ cam->video_dev->minor = -1;
+
+ for (i = 0; i < FRAME_NUM; i++) {
+ cam->frame[i].width = 0;
+ cam->frame[i].height = 0;
+ cam->frame[i].paddress = 0;
+ }
+
+ init_waitqueue_head(&cam->enc_queue);
+ init_waitqueue_head(&cam->still_queue);
+
+ /* setup cropping */
+ cam->crop_bounds.left = 0;
+ cam->crop_bounds.width = 640;
+ cam->crop_bounds.top = 0;
+ cam->crop_bounds.height = 480;
+ cam->crop_current = cam->crop_defrect = cam->crop_bounds;
+ cam->streamparm.parm.capture.capturemode = 0;
+
+ cam->standard.index = 0;
+ cam->standard.id = V4L2_STD_UNKNOWN;
+ cam->standard.frameperiod.denominator = 30;
+ cam->standard.frameperiod.numerator = 1;
+ cam->standard.framelines = 480;
+ cam->streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ cam->streamparm.parm.capture.timeperframe = cam->standard.frameperiod;
+ cam->streamparm.parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+ cam->overlay_on = false;
+ cam->capture_on = false;
+ cam->skip_frame = 0;
+ cam->v4l2_fb.capability = V4L2_FBUF_CAP_EXTERNOVERLAY;
+ cam->v4l2_fb.flags = V4L2_FBUF_FLAG_PRIMARY;
+
+ cam->v2f.fmt.pix.sizeimage = 352 * 288 * 3 / 2;
+ cam->v2f.fmt.pix.bytesperline = 288 * 3 / 2;
+ cam->v2f.fmt.pix.width = 288;
+ cam->v2f.fmt.pix.height = 352;
+ cam->v2f.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;
+ cam->win.w.width = 160;
+ cam->win.w.height = 160;
+ cam->win.w.left = 0;
+ cam->win.w.top = 0;
+
+ cam->cam_sensor = &camera_sensor_if;
+ cam->enc_callback = camera_callback;
+
+ init_waitqueue_head(&cam->power_queue);
+ cam->int_lock = __SPIN_LOCK_UNLOCKED(cam->int_lock);
+ spin_lock_init(&cam->int_lock);
+}
+
+extern void gpio_sensor_active(void);
+extern void gpio_sensor_inactive(void);
+
+/*!
+ * camera_power function
+ * Turn Sensor power On/Off
+ *
+ * @param cameraOn true to turn camera on, otherwise shut down
+ *
+ * @return status
+ */
+static u8 camera_power(bool cameraOn)
+{
+ if (cameraOn == true) {
+ gpio_sensor_active();
+ csi_enable_mclk(csi_mclk_flag_backup, true, true);
+ } else {
+ csi_mclk_flag_backup = csi_read_mclk_flag();
+ csi_enable_mclk(csi_mclk_flag_backup, false, false);
+ gpio_sensor_inactive();
+ }
+ return 0;
+}
+
+/*!
+ * This function is called to put the sensor in a low power state. Refer to the
+ * document driver-model/driver.txt in the kernel source tree for more
+ * information.
+ *
+ * @param pdev the device structure used to give information on which I2C
+ * to suspend
+ * @param state the power state the device is entering
+ *
+ * @return The function returns 0 on success and -1 on failure.
+ */
+static int mxc_v4l2_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ cam_data *cam = platform_get_drvdata(pdev);
+
+ if (cam == NULL) {
+ return -1;
+ }
+
+ cam->low_power = true;
+
+ if (cam->overlay_on == true)
+ stop_preview(cam);
+ if ((cam->capture_on == true) && cam->enc_disable) {
+ cam->enc_disable(cam);
+ }
+ camera_power(false);
+
+ return 0;
+}
+
+/*!
+ * This function is called to bring the sensor back from a low power state.Refer
+ * to the document driver-model/driver.txt in the kernel source tree for more
+ * information.
+ *
+ * @param pdev the device structure
+ *
+ * @return The function returns 0 on success and -1 on failure
+ */
+static int mxc_v4l2_resume(struct platform_device *pdev)
+{
+ cam_data *cam = platform_get_drvdata(pdev);
+
+ if (cam == NULL) {
+ return -1;
+ }
+
+ cam->low_power = false;
+ wake_up_interruptible(&cam->power_queue);
+
+ if (cam->overlay_on == true)
+ start_preview(cam);
+ if (cam->capture_on == true)
+ mxc_streamon(cam);
+ camera_power(true);
+
+ return 0;
+}
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxc_v4l2_driver = {
+ .driver = {
+ .name = "mxc_v4l2",
+ .owner = THIS_MODULE,
+ .bus = &platform_bus_type,
+ },
+ .probe = NULL,
+ .remove = NULL,
+ .suspend = mxc_v4l2_suspend,
+ .resume = mxc_v4l2_resume,
+ .shutdown = NULL,
+};
+
+/*!
+ * Entry point for the V4L2
+ *
+ * @return Error code indicating success or failure
+ */
+static __init int camera_init(void)
+{
+ u8 err = 0;
+ cam_data *cam;
+
+ g_cam = kmalloc(sizeof(cam_data), GFP_KERNEL);
+ if (g_cam == NULL) {
+ pr_debug("failed to mxc_v4l_register_camera\n");
+ return -1;
+ }
+
+ cam = g_cam;
+ init_camera_struct(cam);
+
+ /* Register the I2C device */
+ err = platform_device_register(&mxc_v4l2_devices);
+ if (err != 0) {
+ pr_debug("camera_init: platform_device_register failed.\n");
+ video_device_release(cam->video_dev);
+ kfree(cam);
+ g_cam = NULL;
+ }
+
+ /* Register the device driver structure. */
+ err = platform_driver_register(&mxc_v4l2_driver);
+ if (err != 0) {
+ platform_device_unregister(&mxc_v4l2_devices);
+ pr_debug("camera_init: driver_register failed.\n");
+ video_device_release(cam->video_dev);
+ kfree(cam);
+ g_cam = NULL;
+ return err;
+ }
+
+ /* register v4l device */
+ if (video_register_device(cam->video_dev, VFL_TYPE_GRABBER, video_nr)
+ == -1) {
+ platform_driver_unregister(&mxc_v4l2_driver);
+ platform_device_unregister(&mxc_v4l2_devices);
+ video_device_release(cam->video_dev);
+ kfree(cam);
+ g_cam = NULL;
+ pr_debug("video_register_device failed\n");
+ return -1;
+ }
+
+ return err;
+}
+
+/*!
+ * Exit and cleanup for the V4L2
+ *
+ */
+static void __exit camera_exit(void)
+{
+ pr_debug("unregistering video\n");
+
+ video_unregister_device(g_cam->video_dev);
+
+ platform_driver_unregister(&mxc_v4l2_driver);
+ platform_device_unregister(&mxc_v4l2_devices);
+
+ if (g_cam->open_count) {
+ pr_debug("camera open -- setting ops to NULL\n");
+ } else {
+ pr_debug("freeing camera\n");
+ mxc_free_frame_buf(g_cam);
+ kfree(g_cam);
+ g_cam = NULL;
+ }
+}
+
+module_init(camera_init);
+module_exit(camera_exit);
+
+module_param(video_nr, int, 0444);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("V4L2 capture driver for Mxc based cameras");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("video");
diff --git a/drivers/media/video/mxc/capture/fsl_csi.c b/drivers/media/video/mxc/capture/fsl_csi.c
new file mode 100644
index 000000000000..6dd0cc5a59d8
--- /dev/null
+++ b/drivers/media/video/mxc/capture/fsl_csi.c
@@ -0,0 +1,289 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file fsl_csi.c, this file is derived from mx27_csi.c
+ *
+ * @brief mx25 CMOS Sensor interface functions
+ *
+ * @ingroup CSI
+ */
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <mach/clock.h>
+#include <mach/hardware.h>
+
+#include "mxc_v4l2_capture.h"
+#include "fsl_csi.h"
+
+static bool g_csi_mclk_on;
+static csi_irq_callback_t g_callback;
+static void *g_callback_data;
+static struct clk csi_mclk;
+
+static irqreturn_t csi_irq_handler(int irq, void *data)
+{
+ cam_data *cam = (cam_data *) data;
+ unsigned long status = __raw_readl(CSI_CSISR);
+ unsigned long cr3 = __raw_readl(CSI_CSICR3);
+ unsigned int frame_count = (cr3 >> 16) & 0xFFFF;
+
+ __raw_writel(status, CSI_CSISR);
+
+ if (status & BIT_SOF_INT) {
+ /* reflash the embeded DMA controller */
+ if (frame_count % 2 == 1)
+ __raw_writel(cr3 | BIT_DMA_REFLASH_RFF, CSI_CSICR3);
+ }
+
+ if (status & BIT_DMA_TSF_DONE_FB1) {
+ if (cam->capture_on) {
+ cam->ping_pong_csi = 1;
+ cam->enc_callback(0, cam);
+ } else {
+ cam->still_counter++;
+ wake_up_interruptible(&cam->still_queue);
+ }
+ }
+
+ if (status & BIT_DMA_TSF_DONE_FB2) {
+ if (cam->capture_on) {
+ cam->ping_pong_csi = 2;
+ cam->enc_callback(0, cam);
+ } else {
+ cam->still_counter++;
+ wake_up_interruptible(&cam->still_queue);
+ }
+ }
+
+ if (g_callback)
+ g_callback(g_callback_data, status);
+
+ pr_debug("CSI status = 0x%08lX\n", status);
+
+ return IRQ_HANDLED;
+}
+
+static void csihw_reset_frame_count(void)
+{
+ __raw_writel(__raw_readl(CSI_CSICR3) | BIT_FRMCNT_RST, CSI_CSICR3);
+}
+
+static void csihw_reset(void)
+{
+ csihw_reset_frame_count();
+ __raw_writel(CSICR1_RESET_VAL, CSI_CSICR1);
+ __raw_writel(CSICR2_RESET_VAL, CSI_CSICR2);
+ __raw_writel(CSICR3_RESET_VAL, CSI_CSICR3);
+}
+
+/*!
+ * csi_init_interface
+ * Init csi interface
+ */
+void csi_init_interface(void)
+{
+ unsigned int val = 0;
+ unsigned int imag_para;
+
+ val |= BIT_SOF_POL;
+ val |= BIT_REDGE;
+ val |= BIT_GCLK_MODE;
+ val |= BIT_HSYNC_POL;
+ val |= BIT_PACK_DIR;
+ val |= BIT_FCC;
+ val |= BIT_SWAP16_EN;
+ val |= 1 << SHIFT_MCLKDIV;
+ __raw_writel(val, CSI_CSICR1);
+
+ imag_para = (640 << 16) | 960;
+ __raw_writel(imag_para, CSI_CSIIMAG_PARA);
+
+ val = 0x1010;
+ val |= BIT_DMA_REFLASH_RFF;
+ __raw_writel(val, CSI_CSICR3);
+}
+EXPORT_SYMBOL(csi_init_interface);
+
+/*!
+ * csi_enable_mclk
+ *
+ * @param src enum define which source to control the clk
+ * CSI_MCLK_VF CSI_MCLK_ENC CSI_MCLK_RAW CSI_MCLK_I2C
+ * @param flag true to enable mclk, false to disable mclk
+ * @param wait true to wait 100ms make clock stable, false not wait
+ *
+ * @return 0 for success
+ */
+int32_t csi_enable_mclk(int src, bool flag, bool wait)
+{
+ if (flag == true) {
+ csi_mclk_enable();
+ if (wait == true)
+ msleep(10);
+ pr_debug("Enable csi clock from source %d\n", src);
+ g_csi_mclk_on = true;
+ } else {
+ csi_mclk_disable();
+ pr_debug("Disable csi clock from source %d\n", src);
+ g_csi_mclk_on = false;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(csi_enable_mclk);
+
+/*!
+ * csi_read_mclk_flag
+ *
+ * @return gcsi_mclk_source
+ */
+int csi_read_mclk_flag(void)
+{
+ return 0;
+}
+EXPORT_SYMBOL(csi_read_mclk_flag);
+
+void csi_start_callback(void *data)
+{
+ cam_data *cam = (cam_data *) data;
+
+ if (request_irq(MXC_INT_CSI, csi_irq_handler, 0, "csi", cam) < 0)
+ pr_debug("CSI error: irq request fail\n");
+
+}
+EXPORT_SYMBOL(csi_start_callback);
+
+void csi_stop_callback(void *data)
+{
+ cam_data *cam = (cam_data *) data;
+
+ free_irq(MXC_INT_CSI, cam);
+}
+EXPORT_SYMBOL(csi_stop_callback);
+
+void csi_enable_int(int arg)
+{
+ unsigned long cr1 = __raw_readl(CSI_CSICR1);
+
+ cr1 |= BIT_SOF_INTEN;
+ if (arg == 1) {
+ /* still capture needs DMA intterrupt */
+ cr1 |= BIT_FB1_DMA_DONE_INTEN;
+ cr1 |= BIT_FB2_DMA_DONE_INTEN;
+ }
+ __raw_writel(cr1, CSI_CSICR1);
+}
+EXPORT_SYMBOL(csi_enable_int);
+
+void csi_disable_int(void)
+{
+ unsigned long cr1 = __raw_readl(CSI_CSICR1);
+
+ cr1 &= ~BIT_SOF_INTEN;
+ cr1 &= ~BIT_FB1_DMA_DONE_INTEN;
+ cr1 &= ~BIT_FB2_DMA_DONE_INTEN;
+ __raw_writel(cr1, CSI_CSICR1);
+}
+EXPORT_SYMBOL(csi_disable_int);
+
+void csi_set_16bit_imagpara(int width, int height)
+{
+ int imag_para = 0;
+ unsigned long cr3 = __raw_readl(CSI_CSICR3);
+
+ imag_para = (width << 16) | (height * 2);
+ __raw_writel(imag_para, CSI_CSIIMAG_PARA);
+
+ /* reflash the embeded DMA controller */
+ __raw_writel(cr3 | BIT_DMA_REFLASH_RFF, CSI_CSICR3);
+}
+EXPORT_SYMBOL(csi_set_16bit_imagpara);
+
+void csi_set_12bit_imagpara(int width, int height)
+{
+ int imag_para = 0;
+ unsigned long cr3 = __raw_readl(CSI_CSICR3);
+
+ imag_para = (width << 16) | (height * 3 / 2);
+ __raw_writel(imag_para, CSI_CSIIMAG_PARA);
+
+ /* reflash the embeded DMA controller */
+ __raw_writel(cr3 | BIT_DMA_REFLASH_RFF, CSI_CSICR3);
+}
+EXPORT_SYMBOL(csi_set_12bit_imagpara);
+
+static void csi_mclk_recalc(struct clk *clk)
+{
+ u32 div;
+
+ div = (__raw_readl(CSI_CSICR1) & BIT_MCLKDIV) >> SHIFT_MCLKDIV;
+ if (div == 0)
+ div = 1;
+ else
+ div = div * 2;
+
+ clk->rate = clk->parent->rate / div;
+}
+
+void csi_mclk_enable(void)
+{
+ __raw_writel(__raw_readl(CSI_CSICR1) | BIT_MCLKEN, CSI_CSICR1);
+}
+
+void csi_mclk_disable(void)
+{
+ __raw_writel(__raw_readl(CSI_CSICR1) & ~BIT_MCLKEN, CSI_CSICR1);
+}
+
+int32_t __init csi_init_module(void)
+{
+ int ret = 0;
+ struct clk *per_clk;
+
+ csihw_reset();
+ csi_init_interface();
+
+ per_clk = clk_get(NULL, "csi_clk");
+ if (IS_ERR(per_clk))
+ return PTR_ERR(per_clk);
+
+ clk_put(per_clk);
+ csi_mclk.name = "csi_mclk";
+ csi_mclk.parent = per_clk;
+ clk_register(&csi_mclk);
+ clk_enable(per_clk);
+ csi_mclk_recalc(&csi_mclk);
+
+ return ret;
+}
+
+void __exit csi_cleanup_module(void)
+{
+ clk_disable(&csi_mclk);
+ clk_unregister(&csi_mclk);
+}
+
+module_init(csi_init_module);
+module_exit(csi_cleanup_module);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("fsl CSI driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxc/capture/fsl_csi.h b/drivers/media/video/mxc/capture/fsl_csi.h
new file mode 100644
index 000000000000..ee57597407f3
--- /dev/null
+++ b/drivers/media/video/mxc/capture/fsl_csi.h
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file fsl_csi.h
+ *
+ * @brief mx25 CMOS Sensor interface functions
+ *
+ * @ingroup CSI
+ */
+
+#ifndef MX25_CSI_H
+#define MX25_CSI_H
+
+#include <linux/io.h>
+#include <mach/hardware.h>
+
+/* reset values */
+#define CSICR1_RESET_VAL 0x40000800
+#define CSICR2_RESET_VAL 0x0
+#define CSICR3_RESET_VAL 0x0
+
+/* csi control reg 1 */
+#define BIT_SWAP16_EN (0x1 << 31)
+#define BIT_EXT_VSYNC (0x1 << 30)
+#define BIT_EOF_INT_EN (0x1 << 29)
+#define BIT_PRP_IF_EN (0x1 << 28)
+#define BIT_CCIR_MODE (0x1 << 27)
+#define BIT_COF_INT_EN (0x1 << 26)
+#define BIT_SF_OR_INTEN (0x1 << 25)
+#define BIT_RF_OR_INTEN (0x1 << 24)
+#define BIT_SFF_DMA_DONE_INTEN (0x1 << 22)
+#define BIT_STATFF_INTEN (0x1 << 21)
+#define BIT_FB2_DMA_DONE_INTEN (0x1 << 20)
+#define BIT_FB1_DMA_DONE_INTEN (0x1 << 19)
+#define BIT_RXFF_INTEN (0x1 << 18)
+#define BIT_SOF_POL (0x1 << 17)
+#define BIT_SOF_INTEN (0x1 << 16)
+#define BIT_MCLKDIV (0xF << 12)
+#define BIT_HSYNC_POL (0x1 << 11)
+#define BIT_CCIR_EN (0x1 << 10)
+#define BIT_MCLKEN (0x1 << 9)
+#define BIT_FCC (0x1 << 8)
+#define BIT_PACK_DIR (0x1 << 7)
+#define BIT_CLR_STATFIFO (0x1 << 6)
+#define BIT_CLR_RXFIFO (0x1 << 5)
+#define BIT_GCLK_MODE (0x1 << 4)
+#define BIT_INV_DATA (0x1 << 3)
+#define BIT_INV_PCLK (0x1 << 2)
+#define BIT_REDGE (0x1 << 1)
+#define BIT_PIXEL_BIT (0x1 << 0)
+
+#define SHIFT_MCLKDIV 12
+
+/* control reg 3 */
+#define BIT_FRMCNT (0xFFFF << 16)
+#define BIT_FRMCNT_RST (0x1 << 15)
+#define BIT_DMA_REFLASH_RFF (0x1 << 14)
+#define BIT_DMA_REFLASH_SFF (0x1 << 13)
+#define BIT_DMA_REQ_EN_RFF (0x1 << 12)
+#define BIT_DMA_REQ_EN_SFF (0x1 << 11)
+#define BIT_STATFF_LEVEL (0x7 << 8)
+#define BIT_HRESP_ERR_EN (0x1 << 7)
+#define BIT_RXFF_LEVEL (0x7 << 4)
+#define BIT_TWO_8BIT_SENSOR (0x1 << 3)
+#define BIT_ZERO_PACK_EN (0x1 << 2)
+#define BIT_ECC_INT_EN (0x1 << 1)
+#define BIT_ECC_AUTO_EN (0x1 << 0)
+
+#define SHIFT_FRMCNT 16
+
+/* csi status reg */
+#define BIT_SFF_OR_INT (0x1 << 25)
+#define BIT_RFF_OR_INT (0x1 << 24)
+#define BIT_DMA_TSF_DONE_SFF (0x1 << 22)
+#define BIT_STATFF_INT (0x1 << 21)
+#define BIT_DMA_TSF_DONE_FB2 (0x1 << 20)
+#define BIT_DMA_TSF_DONE_FB1 (0x1 << 19)
+#define BIT_RXFF_INT (0x1 << 18)
+#define BIT_EOF_INT (0x1 << 17)
+#define BIT_SOF_INT (0x1 << 16)
+#define BIT_F2_INT (0x1 << 15)
+#define BIT_F1_INT (0x1 << 14)
+#define BIT_COF_INT (0x1 << 13)
+#define BIT_HRESP_ERR_INT (0x1 << 7)
+#define BIT_ECC_INT (0x1 << 1)
+#define BIT_DRDY (0x1 << 0)
+
+#define CSI_MCLK_VF 1
+#define CSI_MCLK_ENC 2
+#define CSI_MCLK_RAW 4
+#define CSI_MCLK_I2C 8
+#endif
+
+#define CSI_CSICR1 (IO_ADDRESS(CSI_BASE_ADDR))
+#define CSI_CSICR2 (IO_ADDRESS(CSI_BASE_ADDR + 0x4))
+#define CSI_CSICR3 (IO_ADDRESS(CSI_BASE_ADDR + 0x8))
+#define CSI_STATFIFO (IO_ADDRESS(CSI_BASE_ADDR + 0xC))
+#define CSI_CSIRXFIFO (IO_ADDRESS(CSI_BASE_ADDR + 0x10))
+#define CSI_CSIRXCNT (IO_ADDRESS(CSI_BASE_ADDR + 0x14))
+#define CSI_CSISR (IO_ADDRESS(CSI_BASE_ADDR + 0x18))
+
+#define CSI_CSIDBG (IO_ADDRESS(CSI_BASE_ADDR + 0x1C))
+#define CSI_CSIDMASA_STATFIFO (IO_ADDRESS(CSI_BASE_ADDR + 0x20))
+#define CSI_CSIDMATS_STATFIFO (IO_ADDRESS(CSI_BASE_ADDR + 0x24))
+#define CSI_CSIDMASA_FB1 (IO_ADDRESS(CSI_BASE_ADDR + 0x28))
+#define CSI_CSIDMASA_FB2 (IO_ADDRESS(CSI_BASE_ADDR + 0x2C))
+#define CSI_CSIFBUF_PARA (IO_ADDRESS(CSI_BASE_ADDR + 0x30))
+#define CSI_CSIIMAG_PARA (IO_ADDRESS(CSI_BASE_ADDR + 0x34))
+
+#define CSI_CSIRXFIFO_PHYADDR (CSI_BASE_ADDR + 0x10)
+
+static inline void csi_clear_status(unsigned long status)
+{
+ __raw_writel(status, CSI_CSISR);
+}
+
+struct csi_signal_cfg_t {
+ unsigned data_width:3;
+ unsigned clk_mode:2;
+ unsigned ext_vsync:1;
+ unsigned Vsync_pol:1;
+ unsigned Hsync_pol:1;
+ unsigned pixclk_pol:1;
+ unsigned data_pol:1;
+ unsigned sens_clksrc:1;
+};
+
+struct csi_config_t {
+ /* control reg 1 */
+ unsigned int swap16_en:1;
+ unsigned int ext_vsync:1;
+ unsigned int eof_int_en:1;
+ unsigned int prp_if_en:1;
+ unsigned int ccir_mode:1;
+ unsigned int cof_int_en:1;
+ unsigned int sf_or_inten:1;
+ unsigned int rf_or_inten:1;
+ unsigned int sff_dma_done_inten:1;
+ unsigned int statff_inten:1;
+ unsigned int fb2_dma_done_inten:1;
+ unsigned int fb1_dma_done_inten:1;
+ unsigned int rxff_inten:1;
+ unsigned int sof_pol:1;
+ unsigned int sof_inten:1;
+ unsigned int mclkdiv:4;
+ unsigned int hsync_pol:1;
+ unsigned int ccir_en:1;
+ unsigned int mclken:1;
+ unsigned int fcc:1;
+ unsigned int pack_dir:1;
+ unsigned int gclk_mode:1;
+ unsigned int inv_data:1;
+ unsigned int inv_pclk:1;
+ unsigned int redge:1;
+ unsigned int pixel_bit:1;
+
+ /* control reg 3 */
+ unsigned int frmcnt:16;
+ unsigned int frame_reset:1;
+ unsigned int dma_reflash_rff:1;
+ unsigned int dma_reflash_sff:1;
+ unsigned int dma_req_en_rff:1;
+ unsigned int dma_req_en_sff:1;
+ unsigned int statff_level:3;
+ unsigned int hresp_err_en:1;
+ unsigned int rxff_level:3;
+ unsigned int two_8bit_sensor:1;
+ unsigned int zero_pack_en:1;
+ unsigned int ecc_int_en:1;
+ unsigned int ecc_auto_en:1;
+ /* fifo counter */
+ unsigned int rxcnt;
+};
+
+typedef void (*csi_irq_callback_t) (void *data, unsigned long status);
+
+int32_t csi_enable_mclk(int src, bool flag, bool wait);
+void csi_init_interface(void);
+void csi_set_16bit_imagpara(int width, int height);
+void csi_set_12bit_imagpara(int width, int height);
+int csi_read_mclk_flag(void);
+void csi_start_callback(void *data);
+void csi_stop_callback(void *data);
+void csi_enable_int(int arg);
+void csi_disable_int(void);
+void csi_mclk_enable(void);
+void csi_mclk_disable(void);
diff --git a/drivers/media/video/mxc/capture/ipu_csi_enc.c b/drivers/media/video/mxc/capture/ipu_csi_enc.c
new file mode 100644
index 000000000000..c0842f81ee27
--- /dev/null
+++ b/drivers/media/video/mxc/capture/ipu_csi_enc.c
@@ -0,0 +1,332 @@
+/*
+ * Copyright 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file ipu_csi_enc.c
+ *
+ * @brief CSI Use case for video capture
+ *
+ * @ingroup IPU
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/ipu.h>
+#include "mxc_v4l2_capture.h"
+#include "ipu_prp_sw.h"
+
+#ifdef CAMERA_DBG
+ #define CAMERA_TRACE(x) (printk)x
+#else
+ #define CAMERA_TRACE(x)
+#endif
+
+/*
+ * Function definitions
+ */
+
+/*!
+ * csi ENC callback function.
+ *
+ * @param irq int irq line
+ * @param dev_id void * device id
+ *
+ * @return status IRQ_HANDLED for handled
+ */
+static irqreturn_t csi_enc_callback(int irq, void *dev_id)
+{
+ cam_data *cam = (cam_data *) dev_id;
+
+ if (cam->enc_callback == NULL)
+ return IRQ_HANDLED;
+
+ cam->enc_callback(irq, dev_id);
+ return IRQ_HANDLED;
+}
+
+/*!
+ * CSI ENC enable channel setup function
+ *
+ * @param cam struct cam_data * mxc capture instance
+ *
+ * @return status
+ */
+static int csi_enc_setup(cam_data *cam)
+{
+ ipu_channel_params_t params;
+ u32 pixel_fmt;
+ int err = 0;
+ dma_addr_t dummy = cam->dummy_frame.buffer.m.offset;
+
+ CAMERA_TRACE("In csi_enc_setup\n");
+ if (!cam) {
+ printk(KERN_ERR "cam private is NULL\n");
+ return -ENXIO;
+ }
+
+ memset(&params, 0, sizeof(ipu_channel_params_t));
+ params.csi_mem.csi = cam->csi;
+
+ if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420)
+ pixel_fmt = IPU_PIX_FMT_YUV420P;
+ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV422P)
+ pixel_fmt = IPU_PIX_FMT_YUV422P;
+ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_UYVY)
+ pixel_fmt = IPU_PIX_FMT_UYVY;
+ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV)
+ pixel_fmt = IPU_PIX_FMT_YUYV;
+ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_NV12)
+ pixel_fmt = IPU_PIX_FMT_NV12;
+ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR24)
+ pixel_fmt = IPU_PIX_FMT_BGR24;
+ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24)
+ pixel_fmt = IPU_PIX_FMT_RGB24;
+ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565)
+ pixel_fmt = IPU_PIX_FMT_RGB565;
+ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR32)
+ pixel_fmt = IPU_PIX_FMT_BGR32;
+ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB32)
+ pixel_fmt = IPU_PIX_FMT_RGB32;
+ else {
+ printk(KERN_ERR "format not supported\n");
+ return -EINVAL;
+ }
+
+ ipu_csi_enable_mclk_if(CSI_MCLK_ENC, cam->csi, true, true);
+
+ err = ipu_init_channel(CSI_MEM, &params);
+ if (err != 0) {
+ printk(KERN_ERR "ipu_init_channel %d\n", err);
+ return err;
+ }
+
+ err = ipu_init_channel_buffer(CSI_MEM, IPU_OUTPUT_BUFFER,
+ pixel_fmt, cam->v2f.fmt.pix.width,
+ cam->v2f.fmt.pix.height,
+ cam->v2f.fmt.pix.width, IPU_ROTATE_NONE,
+ dummy, dummy,
+ cam->offset.u_offset,
+ cam->offset.v_offset);
+ if (err != 0) {
+ printk(KERN_ERR "CSI_MEM output buffer\n");
+ return err;
+ }
+ err = ipu_enable_channel(CSI_MEM);
+ if (err < 0) {
+ printk(KERN_ERR "ipu_enable_channel CSI_MEM\n");
+ return err;
+ }
+
+ return err;
+}
+
+/*!
+ * function to update physical buffer address for encorder IDMA channel
+ *
+ * @param eba physical buffer address for encorder IDMA channel
+ * @param buffer_num int buffer 0 or buffer 1
+ *
+ * @return status
+ */
+static int csi_enc_eba_update(dma_addr_t eba, int *buffer_num)
+{
+ int err = 0;
+
+ pr_debug("eba %x\n", eba);
+ err = ipu_update_channel_buffer(CSI_MEM, IPU_OUTPUT_BUFFER,
+ *buffer_num, eba);
+ if (err != 0) {
+ ipu_clear_buffer_ready(CSI_MEM, IPU_OUTPUT_BUFFER,
+ *buffer_num);
+ printk(KERN_ERR "err %d buffer_num %d\n", err, *buffer_num);
+ return err;
+ }
+
+ ipu_select_buffer(CSI_MEM, IPU_OUTPUT_BUFFER, *buffer_num);
+
+ *buffer_num = (*buffer_num == 0) ? 1 : 0;
+
+ return 0;
+}
+
+/*!
+ * Enable encoder task
+ * @param private struct cam_data * mxc capture instance
+ *
+ * @return status
+ */
+static int csi_enc_enabling_tasks(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+ int err = 0;
+ CAMERA_TRACE("IPU:In csi_enc_enabling_tasks\n");
+
+ cam->dummy_frame.vaddress = dma_alloc_coherent(0,
+ PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage),
+ &cam->dummy_frame.paddress,
+ GFP_DMA | GFP_KERNEL);
+ if (cam->dummy_frame.vaddress == 0) {
+ pr_err("ERROR: v4l2 capture: Allocate dummy frame "
+ "failed.\n");
+ return -ENOBUFS;
+ }
+ cam->dummy_frame.buffer.type = V4L2_BUF_TYPE_PRIVATE;
+ cam->dummy_frame.buffer.length =
+ PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage);
+ cam->dummy_frame.buffer.m.offset = cam->dummy_frame.paddress;
+
+ ipu_clear_irq(IPU_IRQ_CSI0_OUT_EOF);
+ err = ipu_request_irq(IPU_IRQ_CSI0_OUT_EOF,
+ csi_enc_callback, 0, "Mxc Camera", cam);
+ if (err != 0) {
+ printk(KERN_ERR "Error registering rot irq\n");
+ return err;
+ }
+
+ err = csi_enc_setup(cam);
+ if (err != 0) {
+ printk(KERN_ERR "csi_enc_setup %d\n", err);
+ return err;
+ }
+
+ return err;
+}
+
+/*!
+ * Disable encoder task
+ * @param private struct cam_data * mxc capture instance
+ *
+ * @return int
+ */
+static int csi_enc_disabling_tasks(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+ int err = 0;
+
+ ipu_free_irq(IPU_IRQ_CSI0_OUT_EOF, cam);
+
+ err = ipu_disable_channel(CSI_MEM, true);
+
+ ipu_uninit_channel(CSI_MEM);
+
+ if (cam->dummy_frame.vaddress != 0) {
+ dma_free_coherent(0, cam->dummy_frame.buffer.length,
+ cam->dummy_frame.vaddress,
+ cam->dummy_frame.paddress);
+ cam->dummy_frame.vaddress = 0;
+ }
+ ipu_csi_enable_mclk_if(CSI_MCLK_ENC, cam->csi, false, false);
+
+ return err;
+}
+
+/*!
+ * Enable csi
+ * @param private struct cam_data * mxc capture instance
+ *
+ * @return status
+ */
+static int csi_enc_enable_csi(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+
+ return ipu_enable_csi(cam->csi);
+}
+
+/*!
+ * Disable csi
+ * @param private struct cam_data * mxc capture instance
+ *
+ * @return status
+ */
+static int csi_enc_disable_csi(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+
+ return ipu_disable_csi(cam->csi);
+}
+
+/*!
+ * function to select CSI ENC as the working path
+ *
+ * @param private struct cam_data * mxc capture instance
+ *
+ * @return int
+ */
+int csi_enc_select(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+ int err = 0;
+
+ if (cam) {
+ cam->enc_update_eba = csi_enc_eba_update;
+ cam->enc_enable = csi_enc_enabling_tasks;
+ cam->enc_disable = csi_enc_disabling_tasks;
+ cam->enc_enable_csi = csi_enc_enable_csi;
+ cam->enc_disable_csi = csi_enc_disable_csi;
+ } else {
+ err = -EIO;
+ }
+
+ return err;
+}
+
+/*!
+ * function to de-select CSI ENC as the working path
+ *
+ * @param private struct cam_data * mxc capture instance
+ *
+ * @return int
+ */
+int csi_enc_deselect(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+ int err = 0;
+
+ if (cam) {
+ cam->enc_update_eba = NULL;
+ cam->enc_enable = NULL;
+ cam->enc_disable = NULL;
+ cam->enc_enable_csi = NULL;
+ cam->enc_disable_csi = NULL;
+ }
+
+ return err;
+}
+
+/*!
+ * Init the Encorder channels
+ *
+ * @return Error code indicating success or failure
+ */
+__init int csi_enc_init(void)
+{
+ return 0;
+}
+
+/*!
+ * Deinit the Encorder channels
+ *
+ */
+void __exit csi_enc_exit(void)
+{
+}
+
+module_init(csi_enc_init);
+module_exit(csi_enc_exit);
+
+EXPORT_SYMBOL(csi_enc_select);
+EXPORT_SYMBOL(csi_enc_deselect);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("CSI ENC Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxc/capture/ipu_prp_enc.c b/drivers/media/video/mxc/capture/ipu_prp_enc.c
new file mode 100644
index 000000000000..3a07bbfdcbaa
--- /dev/null
+++ b/drivers/media/video/mxc/capture/ipu_prp_enc.c
@@ -0,0 +1,491 @@
+/*
+ * Copyright 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file ipu_prp_enc.c
+ *
+ * @brief IPU Use case for PRP-ENC
+ *
+ * @ingroup IPU
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/ipu.h>
+#include "mxc_v4l2_capture.h"
+#include "ipu_prp_sw.h"
+
+#ifdef CAMERA_DBG
+ #define CAMERA_TRACE(x) (printk)x
+#else
+ #define CAMERA_TRACE(x)
+#endif
+
+static ipu_rotate_mode_t grotation = IPU_ROTATE_NONE;
+
+/*
+ * Function definitions
+ */
+
+/*!
+ * IPU ENC callback function.
+ *
+ * @param irq int irq line
+ * @param dev_id void * device id
+ *
+ * @return status IRQ_HANDLED for handled
+ */
+static irqreturn_t prp_enc_callback(int irq, void *dev_id)
+{
+ cam_data *cam = (cam_data *) dev_id;
+
+ if (cam->enc_callback == NULL)
+ return IRQ_HANDLED;
+
+ cam->enc_callback(irq, dev_id);
+
+ return IRQ_HANDLED;
+}
+
+/*!
+ * PrpENC enable channel setup function
+ *
+ * @param cam struct cam_data * mxc capture instance
+ *
+ * @return status
+ */
+static int prp_enc_setup(cam_data * cam)
+{
+ ipu_channel_params_t enc;
+ int err = 0;
+ dma_addr_t dummy = 0xdeadbeaf;
+
+ CAMERA_TRACE("In prp_enc_setup\n");
+ if (!cam) {
+ printk(KERN_ERR "cam private is NULL\n");
+ return -ENXIO;
+ }
+ memset(&enc, 0, sizeof(ipu_channel_params_t));
+
+ ipu_csi_get_window_size(&enc.csi_prp_enc_mem.in_width,
+ &enc.csi_prp_enc_mem.in_height, cam->csi);
+
+ enc.csi_prp_enc_mem.in_pixel_fmt = IPU_PIX_FMT_UYVY;
+ enc.csi_prp_enc_mem.out_width = cam->v2f.fmt.pix.width;
+ enc.csi_prp_enc_mem.out_height = cam->v2f.fmt.pix.height;
+ enc.csi_prp_enc_mem.csi = cam->csi;
+ if (cam->rotation >= IPU_ROTATE_90_RIGHT) {
+ enc.csi_prp_enc_mem.out_width = cam->v2f.fmt.pix.height;
+ enc.csi_prp_enc_mem.out_height = cam->v2f.fmt.pix.width;
+ }
+
+ if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) {
+ enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_YUV420P;
+ pr_info("YUV420\n");
+ } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV422P) {
+ enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_YUV422P;
+ pr_info("YUV422P\n");
+ } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) {
+ enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_YUYV;
+ pr_info("YUYV\n");
+ } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_UYVY) {
+ enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_UYVY;
+ pr_info("UYVY\n");
+ } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_NV12) {
+ enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_NV12;
+ pr_info("NV12\n");
+ } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR24) {
+ enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_BGR24;
+ pr_info("BGR24\n");
+ } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24) {
+ enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_RGB24;
+ pr_info("RGB24\n");
+ } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565) {
+ enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_RGB565;
+ pr_info("RGB565\n");
+ } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR32) {
+ enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_BGR32;
+ pr_info("BGR32\n");
+ } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB32) {
+ enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_RGB32;
+ pr_info("RGB32\n");
+ } else {
+ printk(KERN_ERR "format not supported\n");
+ return -EINVAL;
+ }
+
+ err = ipu_init_channel(CSI_PRP_ENC_MEM, &enc);
+ if (err != 0) {
+ printk(KERN_ERR "ipu_init_channel %d\n", err);
+ return err;
+ }
+
+ ipu_csi_enable_mclk_if(CSI_MCLK_ENC, cam->csi, true, true);
+
+ grotation = cam->rotation;
+ if (cam->rotation >= IPU_ROTATE_90_RIGHT) {
+ if (cam->rot_enc_bufs_vaddr[0]) {
+ dma_free_coherent(0, cam->rot_enc_buf_size[0],
+ cam->rot_enc_bufs_vaddr[0],
+ cam->rot_enc_bufs[0]);
+ }
+ if (cam->rot_enc_bufs_vaddr[1]) {
+ dma_free_coherent(0, cam->rot_enc_buf_size[1],
+ cam->rot_enc_bufs_vaddr[1],
+ cam->rot_enc_bufs[1]);
+ }
+ cam->rot_enc_buf_size[0] =
+ PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage);
+ cam->rot_enc_bufs_vaddr[0] =
+ (void *)dma_alloc_coherent(0, cam->rot_enc_buf_size[0],
+ &cam->rot_enc_bufs[0],
+ GFP_DMA | GFP_KERNEL);
+ if (!cam->rot_enc_bufs_vaddr[0]) {
+ printk(KERN_ERR "alloc enc_bufs0\n");
+ return -ENOMEM;
+ }
+ cam->rot_enc_buf_size[1] =
+ PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage);
+ cam->rot_enc_bufs_vaddr[1] =
+ (void *)dma_alloc_coherent(0, cam->rot_enc_buf_size[1],
+ &cam->rot_enc_bufs[1],
+ GFP_DMA | GFP_KERNEL);
+ if (!cam->rot_enc_bufs_vaddr[1]) {
+ dma_free_coherent(0, cam->rot_enc_buf_size[0],
+ cam->rot_enc_bufs_vaddr[0],
+ cam->rot_enc_bufs[0]);
+ cam->rot_enc_bufs_vaddr[0] = NULL;
+ cam->rot_enc_bufs[0] = 0;
+ printk(KERN_ERR "alloc enc_bufs1\n");
+ return -ENOMEM;
+ }
+
+ err = ipu_init_channel_buffer(CSI_PRP_ENC_MEM,
+ IPU_OUTPUT_BUFFER,
+ enc.csi_prp_enc_mem.out_pixel_fmt,
+ enc.csi_prp_enc_mem.out_width,
+ enc.csi_prp_enc_mem.out_height,
+ enc.csi_prp_enc_mem.out_width,
+ IPU_ROTATE_NONE,
+ cam->rot_enc_bufs[0],
+ cam->rot_enc_bufs[1], 0, 0);
+ if (err != 0) {
+ printk(KERN_ERR "CSI_PRP_ENC_MEM err\n");
+ return err;
+ }
+
+ err = ipu_init_channel(MEM_ROT_ENC_MEM, NULL);
+ if (err != 0) {
+ printk(KERN_ERR "MEM_ROT_ENC_MEM channel err\n");
+ return err;
+ }
+
+ err = ipu_init_channel_buffer(MEM_ROT_ENC_MEM, IPU_INPUT_BUFFER,
+ enc.csi_prp_enc_mem.out_pixel_fmt,
+ enc.csi_prp_enc_mem.out_width,
+ enc.csi_prp_enc_mem.out_height,
+ enc.csi_prp_enc_mem.out_width,
+ cam->rotation,
+ cam->rot_enc_bufs[0],
+ cam->rot_enc_bufs[1], 0, 0);
+ if (err != 0) {
+ printk(KERN_ERR "MEM_ROT_ENC_MEM input buffer\n");
+ return err;
+ }
+
+ err =
+ ipu_init_channel_buffer(MEM_ROT_ENC_MEM, IPU_OUTPUT_BUFFER,
+ enc.csi_prp_enc_mem.out_pixel_fmt,
+ enc.csi_prp_enc_mem.out_height,
+ enc.csi_prp_enc_mem.out_width,
+ cam->v2f.fmt.pix.bytesperline /
+ bytes_per_pixel(enc.csi_prp_enc_mem.
+ out_pixel_fmt),
+ IPU_ROTATE_NONE, dummy, dummy,
+ cam->offset.u_offset,
+ cam->offset.v_offset);
+ if (err != 0) {
+ printk(KERN_ERR "MEM_ROT_ENC_MEM output buffer\n");
+ return err;
+ }
+
+ err = ipu_link_channels(CSI_PRP_ENC_MEM, MEM_ROT_ENC_MEM);
+ if (err < 0) {
+ printk(KERN_ERR
+ "link CSI_PRP_ENC_MEM-MEM_ROT_ENC_MEM\n");
+ return err;
+ }
+
+ err = ipu_enable_channel(CSI_PRP_ENC_MEM);
+ if (err < 0) {
+ printk(KERN_ERR "ipu_enable_channel CSI_PRP_ENC_MEM\n");
+ return err;
+ }
+ err = ipu_enable_channel(MEM_ROT_ENC_MEM);
+ if (err < 0) {
+ printk(KERN_ERR "ipu_enable_channel MEM_ROT_ENC_MEM\n");
+ return err;
+ }
+
+ ipu_select_buffer(CSI_PRP_ENC_MEM, IPU_OUTPUT_BUFFER, 0);
+ ipu_select_buffer(CSI_PRP_ENC_MEM, IPU_OUTPUT_BUFFER, 1);
+ } else {
+ err =
+ ipu_init_channel_buffer(CSI_PRP_ENC_MEM, IPU_OUTPUT_BUFFER,
+ enc.csi_prp_enc_mem.out_pixel_fmt,
+ enc.csi_prp_enc_mem.out_width,
+ enc.csi_prp_enc_mem.out_height,
+ cam->v2f.fmt.pix.bytesperline /
+ bytes_per_pixel(enc.csi_prp_enc_mem.
+ out_pixel_fmt),
+ cam->rotation, dummy, dummy,
+ cam->offset.u_offset,
+ cam->offset.v_offset);
+ if (err != 0) {
+ printk(KERN_ERR "CSI_PRP_ENC_MEM output buffer\n");
+ return err;
+ }
+ err = ipu_enable_channel(CSI_PRP_ENC_MEM);
+ if (err < 0) {
+ printk(KERN_ERR "ipu_enable_channel CSI_PRP_ENC_MEM\n");
+ return err;
+ }
+ }
+
+ return err;
+}
+
+/*!
+ * function to update physical buffer address for encorder IDMA channel
+ *
+ * @param eba physical buffer address for encorder IDMA channel
+ * @param buffer_num int buffer 0 or buffer 1
+ *
+ * @return status
+ */
+static int prp_enc_eba_update(dma_addr_t eba, int *buffer_num)
+{
+ int err = 0;
+
+ pr_debug("eba %x\n", eba);
+ if (grotation >= IPU_ROTATE_90_RIGHT) {
+ err = ipu_update_channel_buffer(MEM_ROT_ENC_MEM,
+ IPU_OUTPUT_BUFFER, *buffer_num,
+ eba);
+ } else {
+ err = ipu_update_channel_buffer(CSI_PRP_ENC_MEM,
+ IPU_OUTPUT_BUFFER, *buffer_num,
+ eba);
+ }
+ if (err != 0) {
+ printk(KERN_ERR "err %d buffer_num %d\n", err, *buffer_num);
+ return err;
+ }
+
+ if (grotation >= IPU_ROTATE_90_RIGHT) {
+ ipu_select_buffer(MEM_ROT_ENC_MEM, IPU_OUTPUT_BUFFER,
+ *buffer_num);
+ } else {
+ ipu_select_buffer(CSI_PRP_ENC_MEM, IPU_OUTPUT_BUFFER,
+ *buffer_num);
+ }
+
+ *buffer_num = (*buffer_num == 0) ? 1 : 0;
+ return 0;
+}
+
+/*!
+ * Enable encoder task
+ * @param private struct cam_data * mxc capture instance
+ *
+ * @return status
+ */
+static int prp_enc_enabling_tasks(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+ int err = 0;
+ CAMERA_TRACE("IPU:In prp_enc_enabling_tasks\n");
+
+ if (cam->rotation >= IPU_ROTATE_90_RIGHT) {
+ err = ipu_request_irq(IPU_IRQ_PRP_ENC_ROT_OUT_EOF,
+ prp_enc_callback, 0, "Mxc Camera", cam);
+ } else {
+ err = ipu_request_irq(IPU_IRQ_PRP_ENC_OUT_EOF,
+ prp_enc_callback, 0, "Mxc Camera", cam);
+ }
+ if (err != 0) {
+ printk(KERN_ERR "Error registering rot irq\n");
+ return err;
+ }
+
+ err = prp_enc_setup(cam);
+ if (err != 0) {
+ printk(KERN_ERR "prp_enc_setup %d\n", err);
+ return err;
+ }
+
+ return err;
+}
+
+/*!
+ * Disable encoder task
+ * @param private struct cam_data * mxc capture instance
+ *
+ * @return int
+ */
+static int prp_enc_disabling_tasks(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+ int err = 0;
+
+ if (cam->rotation >= IPU_ROTATE_90_RIGHT) {
+ ipu_free_irq(IPU_IRQ_PRP_ENC_ROT_OUT_EOF, cam);
+ } else {
+ ipu_free_irq(IPU_IRQ_PRP_ENC_OUT_EOF, cam);
+ }
+
+ if (cam->rotation >= IPU_ROTATE_90_RIGHT) {
+ ipu_unlink_channels(CSI_PRP_ENC_MEM, MEM_ROT_ENC_MEM);
+ }
+
+ err = ipu_disable_channel(CSI_PRP_ENC_MEM, true);
+ if (cam->rotation >= IPU_ROTATE_90_RIGHT) {
+ err |= ipu_disable_channel(MEM_ROT_ENC_MEM, true);
+ }
+
+ ipu_uninit_channel(CSI_PRP_ENC_MEM);
+ if (cam->rotation >= IPU_ROTATE_90_RIGHT) {
+ ipu_uninit_channel(MEM_ROT_ENC_MEM);
+ }
+
+ ipu_csi_enable_mclk_if(CSI_MCLK_ENC, cam->csi, false, false);
+
+ return err;
+}
+
+/*!
+ * Enable csi
+ * @param private struct cam_data * mxc capture instance
+ *
+ * @return status
+ */
+static int prp_enc_enable_csi(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+
+ return ipu_enable_csi(cam->csi);
+}
+
+/*!
+ * Disable csi
+ * @param private struct cam_data * mxc capture instance
+ *
+ * @return status
+ */
+static int prp_enc_disable_csi(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+
+ return ipu_disable_csi(cam->csi);
+}
+
+/*!
+ * function to select PRP-ENC as the working path
+ *
+ * @param private struct cam_data * mxc capture instance
+ *
+ * @return int
+ */
+int prp_enc_select(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+ int err = 0;
+
+ if (cam) {
+ cam->enc_update_eba = prp_enc_eba_update;
+ cam->enc_enable = prp_enc_enabling_tasks;
+ cam->enc_disable = prp_enc_disabling_tasks;
+ cam->enc_enable_csi = prp_enc_enable_csi;
+ cam->enc_disable_csi = prp_enc_disable_csi;
+ } else {
+ err = -EIO;
+ }
+
+ return err;
+}
+
+/*!
+ * function to de-select PRP-ENC as the working path
+ *
+ * @param private struct cam_data * mxc capture instance
+ *
+ * @return int
+ */
+int prp_enc_deselect(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+ int err = 0;
+
+ //err = prp_enc_disabling_tasks(cam);
+
+ if (cam) {
+ cam->enc_update_eba = NULL;
+ cam->enc_enable = NULL;
+ cam->enc_disable = NULL;
+ cam->enc_enable_csi = NULL;
+ cam->enc_disable_csi = NULL;
+ if (cam->rot_enc_bufs_vaddr[0]) {
+ dma_free_coherent(0, cam->rot_enc_buf_size[0],
+ cam->rot_enc_bufs_vaddr[0],
+ cam->rot_enc_bufs[0]);
+ cam->rot_enc_bufs_vaddr[0] = NULL;
+ cam->rot_enc_bufs[0] = 0;
+ }
+ if (cam->rot_enc_bufs_vaddr[1]) {
+ dma_free_coherent(0, cam->rot_enc_buf_size[1],
+ cam->rot_enc_bufs_vaddr[1],
+ cam->rot_enc_bufs[1]);
+ cam->rot_enc_bufs_vaddr[1] = NULL;
+ cam->rot_enc_bufs[1] = 0;
+ }
+ }
+
+ return err;
+}
+
+/*!
+ * Init the Encorder channels
+ *
+ * @return Error code indicating success or failure
+ */
+__init int prp_enc_init(void)
+{
+ return 0;
+}
+
+/*!
+ * Deinit the Encorder channels
+ *
+ */
+void __exit prp_enc_exit(void)
+{
+}
+
+module_init(prp_enc_init);
+module_exit(prp_enc_exit);
+
+EXPORT_SYMBOL(prp_enc_select);
+EXPORT_SYMBOL(prp_enc_deselect);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("IPU PRP ENC Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxc/capture/ipu_prp_sw.h b/drivers/media/video/mxc/capture/ipu_prp_sw.h
new file mode 100644
index 000000000000..0d51e2ae8670
--- /dev/null
+++ b/drivers/media/video/mxc/capture/ipu_prp_sw.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file ipu_prp_sw.h
+ *
+ * @brief This file contains the IPU PRP use case driver header.
+ *
+ * @ingroup IPU
+ */
+
+#ifndef _INCLUDE_IPU__PRP_SW_H_
+#define _INCLUDE_IPU__PRP_SW_H_
+
+int csi_enc_select(void *private);
+int csi_enc_deselect(void *private);
+int prp_enc_select(void *private);
+int prp_enc_deselect(void *private);
+int prp_vf_adc_select(void *private);
+int prp_vf_sdc_select(void *private);
+int prp_vf_sdc_select_bg(void *private);
+int prp_vf_adc_deselect(void *private);
+int prp_vf_sdc_deselect(void *private);
+int prp_vf_sdc_deselect_bg(void *private);
+int prp_still_select(void *private);
+int prp_still_deselect(void *private);
+
+#endif
diff --git a/drivers/media/video/mxc/capture/ipu_prp_vf_adc.c b/drivers/media/video/mxc/capture/ipu_prp_vf_adc.c
new file mode 100644
index 000000000000..a7ac09ae87d4
--- /dev/null
+++ b/drivers/media/video/mxc/capture/ipu_prp_vf_adc.c
@@ -0,0 +1,601 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file ipu_prp_vf_adc.c
+ *
+ * @brief IPU Use case for PRP-VF
+ *
+ * @ingroup IPU
+ */
+
+#include "mxc_v4l2_capture.h"
+#include "ipu_prp_sw.h"
+#include <mach/mxcfb.h>
+#include <mach/ipu.h>
+#include <linux/dma-mapping.h>
+
+/*
+ * Function definitions
+ */
+
+/*!
+ * prpvf_start - start the vf task
+ *
+ * @param private cam_data * mxc v4l2 main structure
+ *
+ */
+static int prpvf_start(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+ ipu_channel_params_t vf;
+ ipu_channel_params_t params;
+ u32 format = IPU_PIX_FMT_RGB565;
+ u32 size = 2;
+ int err = 0;
+
+ if (!cam) {
+ printk(KERN_ERR "prpvf_start private is NULL\n");
+ return -ENXIO;
+ }
+
+ if (cam->overlay_active == true) {
+ printk(KERN_ERR "prpvf_start already start.\n");
+ return 0;
+ }
+
+ mxcfb_set_refresh_mode(cam->overlay_fb, MXCFB_REFRESH_OFF, 0);
+
+ memset(&vf, 0, sizeof(ipu_channel_params_t));
+ ipu_csi_get_window_size(&vf.csi_prp_vf_adc.in_width,
+ &vf.csi_prp_vf_adc.in_height);
+ vf.csi_prp_vf_adc.in_pixel_fmt = IPU_PIX_FMT_UYVY;
+ vf.csi_prp_vf_adc.out_width = cam->win.w.width;
+ vf.csi_prp_vf_adc.out_height = cam->win.w.height;
+ vf.csi_prp_vf_adc.graphics_combine_en = 0;
+ vf.csi_prp_vf_adc.out_left = cam->win.w.left;
+
+ /* hope to be removed when those offset taken cared by adc driver. */
+#ifdef CONFIG_FB_MXC_EPSON_QVGA_PANEL
+ vf.csi_prp_vf_adc.out_left += 12;
+#endif
+#ifdef CONFIG_FB_MXC_EPSON_PANEL
+ vf.csi_prp_vf_adc.out_left += 2;
+#endif
+
+ vf.csi_prp_vf_adc.out_top = cam->win.w.top;
+
+ if (cam->vf_rotation >= IPU_ROTATE_90_RIGHT) {
+ vf.csi_prp_vf_adc.out_width = cam->win.w.height;
+ vf.csi_prp_vf_adc.out_height = cam->win.w.width;
+
+ size = cam->win.w.width * cam->win.w.height * size;
+ vf.csi_prp_vf_adc.out_pixel_fmt = format;
+ err = ipu_init_channel(CSI_PRP_VF_MEM, &vf);
+ if (err != 0)
+ return err;
+
+ ipu_csi_enable_mclk_if(CSI_MCLK_VF, cam->csi, true, true);
+
+ if (cam->vf_bufs_vaddr[0]) {
+ dma_free_coherent(0, cam->vf_bufs_size[0],
+ cam->vf_bufs_vaddr[0],
+ cam->vf_bufs[0]);
+ }
+ if (cam->vf_bufs_vaddr[1]) {
+ dma_free_coherent(0, cam->vf_bufs_size[1],
+ cam->vf_bufs_vaddr[1],
+ cam->vf_bufs[1]);
+ }
+ cam->vf_bufs_size[0] = size;
+ cam->vf_bufs_vaddr[0] = (void *)dma_alloc_coherent(0,
+ cam->
+ vf_bufs_size
+ [0],
+ &cam->
+ vf_bufs[0],
+ GFP_DMA |
+ GFP_KERNEL);
+ if (cam->vf_bufs_vaddr[0] == NULL) {
+ printk(KERN_ERR
+ "prpvf_start: Error to allocate vf buffer\n");
+ err = -ENOMEM;
+ goto out_3;
+ }
+ cam->vf_bufs_size[1] = size;
+ cam->vf_bufs_vaddr[1] = (void *)dma_alloc_coherent(0,
+ cam->
+ vf_bufs_size
+ [1],
+ &cam->
+ vf_bufs[1],
+ GFP_DMA |
+ GFP_KERNEL);
+ if (cam->vf_bufs_vaddr[1] == NULL) {
+ printk(KERN_ERR
+ "prpvf_start: Error to allocate vf buffer\n");
+ err = -ENOMEM;
+ goto out_3;
+ }
+
+ err = ipu_init_channel_buffer(CSI_PRP_VF_MEM, IPU_OUTPUT_BUFFER,
+ format,
+ vf.csi_prp_vf_mem.out_width,
+ vf.csi_prp_vf_mem.out_height,
+ vf.csi_prp_vf_mem.out_width,
+ IPU_ROTATE_NONE,
+ cam->vf_bufs[0], cam->vf_bufs[1],
+ 0, 0);
+ if (err != 0)
+ goto out_3;
+
+ if (cam->rot_vf_bufs[0]) {
+ dma_free_coherent(0, cam->rot_vf_buf_size[0],
+ cam->rot_vf_bufs_vaddr[0],
+ cam->rot_vf_bufs[0]);
+ }
+ if (cam->rot_vf_bufs[1]) {
+ dma_free_coherent(0, cam->rot_vf_buf_size[1],
+ cam->rot_vf_bufs_vaddr[1],
+ cam->rot_vf_bufs[1]);
+ }
+ cam->rot_vf_buf_size[0] = PAGE_ALIGN(size);
+ cam->rot_vf_bufs_vaddr[0] = (void *)dma_alloc_coherent(0,
+ cam->
+ rot_vf_buf_size
+ [0],
+ &cam->
+ rot_vf_bufs
+ [0],
+ GFP_DMA |
+ GFP_KERNEL);
+ if (cam->rot_vf_bufs_vaddr[0] == NULL) {
+ printk(KERN_ERR
+ "prpvf_start: Error to allocate rot_vf_bufs\n");
+ err = -ENOMEM;
+ goto out_3;
+ }
+ cam->rot_vf_buf_size[1] = PAGE_ALIGN(size);
+ cam->rot_vf_bufs_vaddr[1] = (void *)dma_alloc_coherent(0,
+ cam->
+ rot_vf_buf_size
+ [1],
+ &cam->
+ rot_vf_bufs
+ [1],
+ GFP_DMA |
+ GFP_KERNEL);
+ if (cam->rot_vf_bufs_vaddr[1] == NULL) {
+ printk(KERN_ERR
+ "prpvf_start: Error to allocate rot_vf_bufs\n");
+ err = -ENOMEM;
+ goto out_3;
+ }
+ err = ipu_init_channel(MEM_ROT_VF_MEM, NULL);
+ if (err != 0) {
+ printk(KERN_ERR "prpvf_start :Error "
+ "MEM_ROT_VF_MEM channel\n");
+ goto out_3;
+ }
+
+ err = ipu_init_channel_buffer(MEM_ROT_VF_MEM, IPU_INPUT_BUFFER,
+ format,
+ vf.csi_prp_vf_mem.out_width,
+ vf.csi_prp_vf_mem.out_height,
+ vf.csi_prp_vf_mem.out_width,
+ cam->vf_rotation, cam->vf_bufs[0],
+ cam->vf_bufs[1], 0, 0);
+ if (err != 0) {
+ printk(KERN_ERR "prpvf_start: Error "
+ "MEM_ROT_VF_MEM input buffer\n");
+ goto out_2;
+ }
+
+ err = ipu_init_channel_buffer(MEM_ROT_VF_MEM, IPU_OUTPUT_BUFFER,
+ format,
+ vf.csi_prp_vf_mem.out_height,
+ vf.csi_prp_vf_mem.out_width,
+ vf.csi_prp_vf_mem.out_height,
+ IPU_ROTATE_NONE,
+ cam->rot_vf_bufs[0],
+ cam->rot_vf_bufs[1], 0, 0);
+ if (err != 0) {
+ printk(KERN_ERR "prpvf_start: Error "
+ "MEM_ROT_VF_MEM output buffer\n");
+ goto out_2;
+ }
+
+ err = ipu_link_channels(CSI_PRP_VF_MEM, MEM_ROT_VF_MEM);
+ if (err < 0) {
+ printk(KERN_ERR "prpvf_start: Error "
+ "linking CSI_PRP_VF_MEM-MEM_ROT_VF_MEM\n");
+ goto out_2;
+ }
+
+ ipu_disable_channel(ADC_SYS2, false);
+ ipu_uninit_channel(ADC_SYS2);
+
+ params.adc_sys2.disp = DISP0;
+ params.adc_sys2.ch_mode = WriteTemplateNonSeq;
+ params.adc_sys2.out_left = cam->win.w.left;
+ /* going to be removed when those offset taken cared by adc driver. */
+#ifdef CONFIG_FB_MXC_EPSON_QVGA_PANEL
+ params.adc_sys2.out_left += 12;
+#endif
+#ifdef CONFIG_FB_MXC_EPSON_PANEL
+ params.adc_sys2.out_left += 2;
+#endif
+ params.adc_sys2.out_top = cam->win.w.top;
+ err = ipu_init_channel(ADC_SYS2, &params);
+ if (err != 0) {
+ printk(KERN_ERR
+ "prpvf_start: Error initializing ADC SYS1\n");
+ goto out_2;
+ }
+
+ err = ipu_init_channel_buffer(ADC_SYS2, IPU_INPUT_BUFFER,
+ format,
+ vf.csi_prp_vf_mem.out_height,
+ vf.csi_prp_vf_mem.out_width,
+ vf.csi_prp_vf_mem.out_height,
+ IPU_ROTATE_NONE,
+ cam->rot_vf_bufs[0],
+ cam->rot_vf_bufs[1], 0, 0);
+ if (err != 0) {
+ printk(KERN_ERR "Error initializing ADC SYS1 buffer\n");
+ goto out_1;
+ }
+
+ err = ipu_link_channels(MEM_ROT_VF_MEM, ADC_SYS2);
+ if (err < 0) {
+ printk(KERN_ERR
+ "Error linking MEM_ROT_VF_MEM-ADC_SYS2\n");
+ goto out_1;
+ }
+
+ ipu_enable_channel(CSI_PRP_VF_MEM);
+ ipu_enable_channel(MEM_ROT_VF_MEM);
+ ipu_enable_channel(ADC_SYS2);
+
+ ipu_select_buffer(CSI_PRP_VF_MEM, IPU_OUTPUT_BUFFER, 0);
+ ipu_select_buffer(CSI_PRP_VF_MEM, IPU_OUTPUT_BUFFER, 1);
+ ipu_select_buffer(MEM_ROT_VF_MEM, IPU_OUTPUT_BUFFER, 0);
+ ipu_select_buffer(MEM_ROT_VF_MEM, IPU_OUTPUT_BUFFER, 1);
+ }
+#ifndef CONFIG_MXC_IPU_PRP_VF_SDC
+ else if (cam->vf_rotation == IPU_ROTATE_NONE) {
+ vf.csi_prp_vf_adc.out_pixel_fmt = IPU_PIX_FMT_BGR32;
+ err = ipu_init_channel(CSI_PRP_VF_ADC, &vf);
+ if (err != 0) {
+ printk(KERN_ERR "prpvf_start: Error "
+ "initializing CSI_PRP_VF_ADC\n");
+ return err;
+ }
+ ipu_csi_enable_mclk_if(CSI_MCLK_VF, cam->csi, true, true);
+ err = ipu_init_channel_buffer(CSI_PRP_VF_ADC, IPU_OUTPUT_BUFFER,
+ format, cam->win.w.width,
+ cam->win.w.height,
+ cam->win.w.width, IPU_ROTATE_NONE,
+ 0, 0, 0, 0);
+ if (err != 0) {
+ printk(KERN_ERR "prpvf_start: Error "
+ "initializing CSI_PRP_VF_MEM\n");
+ return err;
+ }
+ ipu_enable_channel(CSI_PRP_VF_ADC);
+ }
+#endif
+ else {
+ size = cam->win.w.width * cam->win.w.height * size;
+ vf.csi_prp_vf_adc.out_pixel_fmt = format;
+ err = ipu_init_channel(CSI_PRP_VF_MEM, &vf);
+ if (err != 0) {
+ printk(KERN_ERR "prpvf_start: Error "
+ "initializing CSI_PRP_VF_MEM\n");
+ return err;
+ }
+
+ ipu_csi_enable_mclk_if(CSI_MCLK_VF, cam->csi, true, true);
+
+ if (cam->vf_bufs[0]) {
+ dma_free_coherent(0, cam->vf_bufs_size[0],
+ cam->vf_bufs_vaddr[0],
+ cam->vf_bufs[0]);
+ }
+ if (cam->vf_bufs[1]) {
+ dma_free_coherent(0, cam->vf_bufs_size[1],
+ cam->vf_bufs_vaddr[1],
+ cam->vf_bufs[1]);
+ }
+ cam->vf_bufs_size[0] = PAGE_ALIGN(size);
+ cam->vf_bufs_vaddr[0] = (void *)dma_alloc_coherent(0,
+ cam->
+ vf_bufs_size
+ [0],
+ &cam->
+ vf_bufs[0],
+ GFP_DMA |
+ GFP_KERNEL);
+ if (cam->vf_bufs_vaddr[0] == NULL) {
+ printk(KERN_ERR
+ "prpvf_start: Error to allocate vf_bufs\n");
+ err = -ENOMEM;
+ goto out_3;
+ }
+ cam->vf_bufs_size[1] = PAGE_ALIGN(size);
+ cam->vf_bufs_vaddr[1] = (void *)dma_alloc_coherent(0,
+ cam->
+ vf_bufs_size
+ [1],
+ &cam->
+ vf_bufs[1],
+ GFP_DMA |
+ GFP_KERNEL);
+ if (cam->vf_bufs_vaddr[1] == NULL) {
+ printk(KERN_ERR
+ "prpvf_start: Error to allocate vf_bufs\n");
+ err = -ENOMEM;
+ goto out_3;
+ }
+ err = ipu_init_channel_buffer(CSI_PRP_VF_MEM, IPU_OUTPUT_BUFFER,
+ format,
+ vf.csi_prp_vf_mem.out_width,
+ vf.csi_prp_vf_mem.out_height,
+ vf.csi_prp_vf_mem.out_width,
+ cam->vf_rotation,
+ cam->vf_bufs[0], cam->vf_bufs[1],
+ 0, 0);
+ if (err != 0) {
+ printk(KERN_ERR "prpvf_start: Error "
+ "initializing CSI_PRP_VF_MEM\n");
+ goto out_3;
+ }
+
+ ipu_disable_channel(ADC_SYS2, false);
+ ipu_uninit_channel(ADC_SYS2);
+
+ params.adc_sys2.disp = DISP0;
+ params.adc_sys2.ch_mode = WriteTemplateNonSeq;
+ params.adc_sys2.out_left = cam->win.w.left;
+ // going to be removed when those offset taken cared by adc driver.
+#ifdef CONFIG_FB_MXC_EPSON_QVGA_PANEL
+ params.adc_sys2.out_left += 12;
+#endif
+#ifdef CONFIG_FB_MXC_EPSON_PANEL
+ params.adc_sys2.out_left += 2;
+#endif
+ params.adc_sys2.out_top = cam->win.w.top;
+ err = ipu_init_channel(ADC_SYS2, &params);
+ if (err != 0) {
+ printk(KERN_ERR "prpvf_start: Error "
+ "initializing ADC_SYS2\n");
+ goto out_3;
+ }
+
+ err = ipu_init_channel_buffer(ADC_SYS2, IPU_INPUT_BUFFER,
+ format,
+ vf.csi_prp_vf_mem.out_width,
+ vf.csi_prp_vf_mem.out_height,
+ vf.csi_prp_vf_mem.out_width,
+ IPU_ROTATE_NONE, cam->vf_bufs[0],
+ cam->vf_bufs[1], 0, 0);
+ if (err != 0) {
+ printk(KERN_ERR "prpvf_start: Error "
+ "initializing ADC SYS1 buffer\n");
+ goto out_1;
+ }
+
+ err = ipu_link_channels(CSI_PRP_VF_MEM, ADC_SYS2);
+ if (err < 0) {
+ printk(KERN_ERR "prpvf_start: Error "
+ "linking MEM_ROT_VF_MEM-ADC_SYS2\n");
+ goto out_1;
+ }
+
+ ipu_enable_channel(CSI_PRP_VF_MEM);
+ ipu_enable_channel(ADC_SYS2);
+
+ ipu_select_buffer(CSI_PRP_VF_MEM, IPU_OUTPUT_BUFFER, 0);
+ ipu_select_buffer(CSI_PRP_VF_MEM, IPU_OUTPUT_BUFFER, 1);
+ }
+
+ cam->overlay_active = true;
+ return err;
+
+ out_1:
+ ipu_uninit_channel(ADC_SYS2);
+ out_2:
+ if (cam->vf_rotation >= IPU_ROTATE_90_RIGHT) {
+ ipu_uninit_channel(MEM_ROT_VF_MEM);
+ }
+ out_3:
+ ipu_uninit_channel(CSI_PRP_VF_MEM);
+ if (cam->rot_vf_bufs_vaddr[0]) {
+ dma_free_coherent(0, cam->rot_vf_buf_size[0],
+ cam->rot_vf_bufs_vaddr[0],
+ cam->rot_vf_bufs[0]);
+ cam->rot_vf_bufs_vaddr[0] = NULL;
+ cam->rot_vf_bufs[0] = 0;
+ }
+ if (cam->rot_vf_bufs_vaddr[1]) {
+ dma_free_coherent(0, cam->rot_vf_buf_size[1],
+ cam->rot_vf_bufs_vaddr[1],
+ cam->rot_vf_bufs[1]);
+ cam->rot_vf_bufs_vaddr[1] = NULL;
+ cam->rot_vf_bufs[1] = 0;
+ }
+ if (cam->vf_bufs_vaddr[0]) {
+ dma_free_coherent(0, cam->vf_bufs_size[0],
+ cam->vf_bufs_vaddr[0], cam->vf_bufs[0]);
+ cam->vf_bufs_vaddr[0] = NULL;
+ cam->vf_bufs[0] = 0;
+ }
+ if (cam->vf_bufs_vaddr[1]) {
+ dma_free_coherent(0, cam->vf_bufs_size[1],
+ cam->vf_bufs_vaddr[1], cam->vf_bufs[1]);
+ cam->vf_bufs_vaddr[1] = NULL;
+ cam->vf_bufs[1] = 0;
+ }
+ return err;
+}
+
+/*!
+ * prpvf_stop - stop the vf task
+ *
+ * @param private cam_data * mxc v4l2 main structure
+ *
+ */
+static int prpvf_stop(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+ int err = 0;
+
+ if (cam->overlay_active == false)
+ return 0;
+
+ if (cam->vf_rotation >= IPU_ROTATE_90_RIGHT) {
+ ipu_unlink_channels(CSI_PRP_VF_MEM, MEM_ROT_VF_MEM);
+ ipu_unlink_channels(MEM_ROT_VF_MEM, ADC_SYS2);
+
+ ipu_disable_channel(CSI_PRP_VF_MEM, true);
+ ipu_disable_channel(MEM_ROT_VF_MEM, true);
+ ipu_disable_channel(ADC_SYS2, true);
+
+ ipu_uninit_channel(CSI_PRP_VF_MEM);
+ ipu_uninit_channel(MEM_ROT_VF_MEM);
+ ipu_uninit_channel(ADC_SYS2);
+
+ ipu_csi_enable_mclk_if(CSI_MCLK_VF, cam->csi, false, false);
+ }
+#ifndef CONFIG_MXC_IPU_PRP_VF_SDC
+ else if (cam->vf_rotation == IPU_ROTATE_NONE) {
+ ipu_disable_channel(CSI_PRP_VF_ADC, false);
+ ipu_uninit_channel(CSI_PRP_VF_ADC);
+ ipu_csi_enable_mclk_if(CSI_MCLK_VF, cam->csi, false, false);
+ }
+#endif
+ else {
+ ipu_unlink_channels(CSI_PRP_VF_MEM, ADC_SYS2);
+
+ ipu_disable_channel(CSI_PRP_VF_MEM, true);
+ ipu_disable_channel(ADC_SYS2, true);
+
+ ipu_uninit_channel(CSI_PRP_VF_MEM);
+ ipu_uninit_channel(ADC_SYS2);
+
+ ipu_csi_enable_mclk_if(CSI_MCLK_VF, cam->csi, false, false);
+ }
+
+ if (cam->vf_bufs_vaddr[0]) {
+ dma_free_coherent(0, cam->vf_bufs_size[0],
+ cam->vf_bufs_vaddr[0], cam->vf_bufs[0]);
+ cam->vf_bufs_vaddr[0] = NULL;
+ cam->vf_bufs[0] = 0;
+ }
+ if (cam->vf_bufs_vaddr[1]) {
+ dma_free_coherent(0, cam->vf_bufs_size[1],
+ cam->vf_bufs_vaddr[1], cam->vf_bufs[1]);
+ cam->vf_bufs_vaddr[1] = NULL;
+ cam->vf_bufs[1] = 0;
+ }
+ if (cam->rot_vf_bufs_vaddr[0]) {
+ dma_free_coherent(0, cam->rot_vf_buf_size[0],
+ cam->rot_vf_bufs_vaddr[0],
+ cam->rot_vf_bufs[0]);
+ cam->rot_vf_bufs_vaddr[0] = NULL;
+ cam->rot_vf_bufs[0] = 0;
+ }
+ if (cam->rot_vf_bufs_vaddr[1]) {
+ dma_free_coherent(0, cam->rot_vf_buf_size[1],
+ cam->rot_vf_bufs_vaddr[1],
+ cam->rot_vf_bufs[1]);
+ cam->rot_vf_bufs_vaddr[1] = NULL;
+ cam->rot_vf_bufs[1] = 0;
+ }
+
+ cam->overlay_active = false;
+
+ mxcfb_set_refresh_mode(cam->overlay_fb, MXCFB_REFRESH_PARTIAL, 0);
+ return err;
+}
+
+/*!
+ * function to select PRP-VF as the working path
+ *
+ * @param private cam_data * mxc v4l2 main structure
+ *
+ * @return status
+ */
+int prp_vf_adc_select(void *private)
+{
+ cam_data *cam;
+ if (private) {
+ cam = (cam_data *) private;
+ cam->vf_start_adc = prpvf_start;
+ cam->vf_stop_adc = prpvf_stop;
+ cam->overlay_active = false;
+ } else {
+ return -EIO;
+ }
+ return 0;
+}
+
+/*!
+ * function to de-select PRP-VF as the working path
+ *
+ * @param private cam_data * mxc v4l2 main structure
+ *
+ * @return status
+ */
+int prp_vf_adc_deselect(void *private)
+{
+ cam_data *cam;
+ int err = 0;
+ err = prpvf_stop(private);
+
+ if (private) {
+ cam = (cam_data *) private;
+ cam->vf_start_adc = NULL;
+ cam->vf_stop_adc = NULL;
+ }
+ return err;
+}
+
+/*!
+ * Init viewfinder task.
+ *
+ * @return Error code indicating success or failure
+ */
+__init int prp_vf_adc_init(void)
+{
+ return 0;
+}
+
+/*!
+ * Deinit viewfinder task.
+ *
+ * @return Error code indicating success or failure
+ */
+void __exit prp_vf_adc_exit(void)
+{
+}
+
+module_init(prp_vf_adc_init);
+module_exit(prp_vf_adc_exit);
+
+EXPORT_SYMBOL(prp_vf_adc_select);
+EXPORT_SYMBOL(prp_vf_adc_deselect);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("IPU PRP VF ADC Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxc/capture/ipu_prp_vf_sdc.c b/drivers/media/video/mxc/capture/ipu_prp_vf_sdc.c
new file mode 100644
index 000000000000..882bacf923b1
--- /dev/null
+++ b/drivers/media/video/mxc/capture/ipu_prp_vf_sdc.c
@@ -0,0 +1,467 @@
+/*
+ * Copyright 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file ipu_prp_vf_sdc.c
+ *
+ * @brief IPU Use case for PRP-VF
+ *
+ * @ingroup IPU
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/console.h>
+#include <linux/ipu.h>
+#include <linux/mxcfb.h>
+#include <mach/hardware.h>
+#include "mxc_v4l2_capture.h"
+#include "ipu_prp_sw.h"
+
+#define OVERLAY_FB_SUPPORT_NONSTD (cpu_is_mx51_rev(CHIP_REV_2_0) >= 1)
+
+/*
+ * Function definitions
+ */
+
+/*!
+ * prpvf_start - start the vf task
+ *
+ * @param private cam_data * mxc v4l2 main structure
+ *
+ */
+static int prpvf_start(void *private)
+{
+ struct fb_var_screeninfo fbvar;
+ struct fb_info *fbi = NULL;
+ cam_data *cam = (cam_data *) private;
+ ipu_channel_params_t vf;
+ u32 vf_out_format = 0;
+ u32 size = 2, temp = 0;
+ int err = 0, i = 0;
+
+ if (!cam) {
+ printk(KERN_ERR "private is NULL\n");
+ return -EIO;
+ }
+
+ if (cam->overlay_active == true) {
+ pr_debug("already started.\n");
+ return 0;
+ }
+
+ for (i = 0; i < num_registered_fb; i++) {
+ char *idstr = registered_fb[i]->fix.id;
+ if (strcmp(idstr, "DISP3 FG") == 0) {
+ fbi = registered_fb[i];
+ break;
+ }
+ }
+
+ if (fbi == NULL) {
+ printk(KERN_ERR "DISP3 FG fb not found\n");
+ return -EPERM;
+ }
+
+ fbvar = fbi->var;
+
+ /* Store the overlay frame buffer's original std */
+ cam->fb_origin_std = fbvar.nonstd;
+
+ if (OVERLAY_FB_SUPPORT_NONSTD) {
+ /* Use DP to do CSC so that we can get better performance */
+ vf_out_format = IPU_PIX_FMT_UYVY;
+ fbvar.nonstd = vf_out_format;
+ } else {
+ vf_out_format = IPU_PIX_FMT_RGB565;
+ fbvar.nonstd = 0;
+ }
+
+ fbvar.bits_per_pixel = 16;
+ fbvar.xres = fbvar.xres_virtual = cam->win.w.width;
+ fbvar.yres = cam->win.w.height;
+ fbvar.yres_virtual = cam->win.w.height * 2;
+ fbvar.activate |= FB_ACTIVATE_FORCE;
+ fb_set_var(fbi, &fbvar);
+
+ ipu_disp_set_window_pos(MEM_FG_SYNC, cam->win.w.left,
+ cam->win.w.top);
+
+ acquire_console_sem();
+ fb_blank(fbi, FB_BLANK_UNBLANK);
+ release_console_sem();
+
+ memset(&vf, 0, sizeof(ipu_channel_params_t));
+ ipu_csi_get_window_size(&vf.csi_prp_vf_mem.in_width,
+ &vf.csi_prp_vf_mem.in_height, cam->csi);
+ vf.csi_prp_vf_mem.in_pixel_fmt = IPU_PIX_FMT_UYVY;
+ vf.csi_prp_vf_mem.out_width = cam->win.w.width;
+ vf.csi_prp_vf_mem.out_height = cam->win.w.height;
+ vf.csi_prp_vf_mem.csi = cam->csi;
+ if (cam->vf_rotation >= IPU_ROTATE_90_RIGHT) {
+ vf.csi_prp_vf_mem.out_width = cam->win.w.height;
+ vf.csi_prp_vf_mem.out_height = cam->win.w.width;
+ }
+ vf.csi_prp_vf_mem.out_pixel_fmt = vf_out_format;
+ size = cam->win.w.width * cam->win.w.height * size;
+
+ err = ipu_init_channel(CSI_PRP_VF_MEM, &vf);
+ if (err != 0)
+ goto out_5;
+
+ ipu_csi_enable_mclk_if(CSI_MCLK_VF, cam->csi, true, true);
+
+ if (cam->vf_bufs_vaddr[0]) {
+ dma_free_coherent(0, cam->vf_bufs_size[0],
+ cam->vf_bufs_vaddr[0],
+ (dma_addr_t) cam->vf_bufs[0]);
+ }
+ if (cam->vf_bufs_vaddr[1]) {
+ dma_free_coherent(0, cam->vf_bufs_size[1],
+ cam->vf_bufs_vaddr[1],
+ (dma_addr_t) cam->vf_bufs[1]);
+ }
+ cam->vf_bufs_size[0] = PAGE_ALIGN(size);
+ cam->vf_bufs_vaddr[0] = (void *)dma_alloc_coherent(0,
+ cam->vf_bufs_size[0],
+ (dma_addr_t *) &
+ cam->vf_bufs[0],
+ GFP_DMA |
+ GFP_KERNEL);
+ if (cam->vf_bufs_vaddr[0] == NULL) {
+ printk(KERN_ERR "Error to allocate vf buffer\n");
+ err = -ENOMEM;
+ goto out_4;
+ }
+ cam->vf_bufs_size[1] = PAGE_ALIGN(size);
+ cam->vf_bufs_vaddr[1] = (void *)dma_alloc_coherent(0,
+ cam->vf_bufs_size[1],
+ (dma_addr_t *) &
+ cam->vf_bufs[1],
+ GFP_DMA |
+ GFP_KERNEL);
+ if (cam->vf_bufs_vaddr[1] == NULL) {
+ printk(KERN_ERR "Error to allocate vf buffer\n");
+ err = -ENOMEM;
+ goto out_3;
+ }
+ pr_debug("vf_bufs %x %x\n", cam->vf_bufs[0], cam->vf_bufs[1]);
+
+ if (cam->vf_rotation >= IPU_ROTATE_VERT_FLIP) {
+ err = ipu_init_channel_buffer(CSI_PRP_VF_MEM, IPU_OUTPUT_BUFFER,
+ vf_out_format,
+ vf.csi_prp_vf_mem.out_width,
+ vf.csi_prp_vf_mem.out_height,
+ vf.csi_prp_vf_mem.out_width,
+ IPU_ROTATE_NONE, cam->vf_bufs[0],
+ cam->vf_bufs[1], 0, 0);
+ if (err != 0) {
+ goto out_3;
+ }
+
+ err = ipu_init_channel(MEM_ROT_VF_MEM, NULL);
+ if (err != 0) {
+ printk(KERN_ERR "Error MEM_ROT_VF_MEM channel\n");
+ goto out_3;
+ }
+
+ err = ipu_init_channel_buffer(MEM_ROT_VF_MEM, IPU_INPUT_BUFFER,
+ vf_out_format,
+ vf.csi_prp_vf_mem.out_width,
+ vf.csi_prp_vf_mem.out_height,
+ vf.csi_prp_vf_mem.out_width,
+ cam->vf_rotation, cam->vf_bufs[0],
+ cam->vf_bufs[1], 0, 0);
+ if (err != 0) {
+ printk(KERN_ERR "Error MEM_ROT_VF_MEM input buffer\n");
+ goto out_2;
+ }
+
+ if (cam->vf_rotation < IPU_ROTATE_90_RIGHT) {
+ temp = vf.csi_prp_vf_mem.out_width;
+ vf.csi_prp_vf_mem.out_width =
+ vf.csi_prp_vf_mem.out_height;
+ vf.csi_prp_vf_mem.out_height = temp;
+ }
+
+ err = ipu_init_channel_buffer(MEM_ROT_VF_MEM, IPU_OUTPUT_BUFFER,
+ vf_out_format,
+ vf.csi_prp_vf_mem.out_height,
+ vf.csi_prp_vf_mem.out_width,
+ vf.csi_prp_vf_mem.out_height,
+ IPU_ROTATE_NONE,
+ fbi->fix.smem_start +
+ (fbi->fix.line_length *
+ fbi->var.yres),
+ fbi->fix.smem_start, 0, 0);
+
+ if (err != 0) {
+ printk(KERN_ERR "Error MEM_ROT_VF_MEM output buffer\n");
+ goto out_2;
+ }
+
+ err = ipu_link_channels(CSI_PRP_VF_MEM, MEM_ROT_VF_MEM);
+ if (err < 0) {
+ printk(KERN_ERR
+ "Error link CSI_PRP_VF_MEM-MEM_ROT_VF_MEM\n");
+ goto out_2;
+ }
+
+ err = ipu_link_channels(MEM_ROT_VF_MEM, MEM_FG_SYNC);
+ if (err < 0) {
+ printk(KERN_ERR
+ "Error link MEM_ROT_VF_MEM-MEM_FG_SYNC\n");
+ goto out_1;
+ }
+
+ ipu_enable_channel(CSI_PRP_VF_MEM);
+ ipu_enable_channel(MEM_ROT_VF_MEM);
+
+ ipu_select_buffer(CSI_PRP_VF_MEM, IPU_OUTPUT_BUFFER, 0);
+ ipu_select_buffer(CSI_PRP_VF_MEM, IPU_OUTPUT_BUFFER, 1);
+ ipu_select_buffer(MEM_ROT_VF_MEM, IPU_OUTPUT_BUFFER, 0);
+ ipu_select_buffer(MEM_ROT_VF_MEM, IPU_OUTPUT_BUFFER, 1);
+ } else {
+ err = ipu_init_channel_buffer(CSI_PRP_VF_MEM, IPU_OUTPUT_BUFFER,
+ vf_out_format, cam->win.w.width,
+ cam->win.w.height,
+ cam->win.w.width,
+ cam->vf_rotation,
+ fbi->fix.smem_start +
+ (fbi->fix.line_length *
+ fbi->var.yres),
+ fbi->fix.smem_start, 0, 0);
+ if (err != 0) {
+ printk(KERN_ERR "Error initializing CSI_PRP_VF_MEM\n");
+ goto out_4;
+ }
+
+ err = ipu_link_channels(CSI_PRP_VF_MEM, MEM_FG_SYNC);
+ if (err < 0) {
+ printk(KERN_ERR "Error linking ipu channels\n");
+ goto out_4;
+ }
+
+ ipu_enable_channel(CSI_PRP_VF_MEM);
+
+ ipu_select_buffer(CSI_PRP_VF_MEM, IPU_OUTPUT_BUFFER, 0);
+ ipu_select_buffer(CSI_PRP_VF_MEM, IPU_OUTPUT_BUFFER, 1);
+ }
+
+ cam->overlay_active = true;
+ return err;
+
+out_1:
+ ipu_unlink_channels(CSI_PRP_VF_MEM, MEM_ROT_VF_MEM);
+out_2:
+ if (cam->vf_rotation >= IPU_ROTATE_VERT_FLIP) {
+ ipu_uninit_channel(MEM_ROT_VF_MEM);
+ }
+out_3:
+ if (cam->vf_bufs_vaddr[0]) {
+ dma_free_coherent(0, cam->vf_bufs_size[0],
+ cam->vf_bufs_vaddr[0],
+ (dma_addr_t) cam->vf_bufs[0]);
+ cam->vf_bufs_vaddr[0] = NULL;
+ cam->vf_bufs[0] = 0;
+ }
+ if (cam->vf_bufs_vaddr[1]) {
+ dma_free_coherent(0, cam->vf_bufs_size[1],
+ cam->vf_bufs_vaddr[1],
+ (dma_addr_t) cam->vf_bufs[1]);
+ cam->vf_bufs_vaddr[1] = NULL;
+ cam->vf_bufs[1] = 0;
+ }
+out_4:
+ ipu_uninit_channel(CSI_PRP_VF_MEM);
+out_5:
+ return err;
+}
+
+/*!
+ * prpvf_stop - stop the vf task
+ *
+ * @param private cam_data * mxc v4l2 main structure
+ *
+ */
+static int prpvf_stop(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+ int err = 0, i = 0;
+ struct fb_info *fbi = NULL;
+ struct fb_var_screeninfo fbvar;
+
+ if (cam->overlay_active == false)
+ return 0;
+
+ for (i = 0; i < num_registered_fb; i++) {
+ char *idstr = registered_fb[i]->fix.id;
+ if (strcmp(idstr, "DISP3 FG") == 0) {
+ fbi = registered_fb[i];
+ break;
+ }
+ }
+
+ if (fbi == NULL) {
+ printk(KERN_ERR "DISP3 FG fb not found\n");
+ return -EPERM;
+ }
+
+ ipu_disp_set_window_pos(MEM_FG_SYNC, 0, 0);
+
+ if (cam->vf_rotation >= IPU_ROTATE_VERT_FLIP) {
+ ipu_unlink_channels(CSI_PRP_VF_MEM, MEM_ROT_VF_MEM);
+ ipu_unlink_channels(MEM_ROT_VF_MEM, MEM_FG_SYNC);
+ } else {
+ ipu_unlink_channels(CSI_PRP_VF_MEM, MEM_FG_SYNC);
+ }
+
+ ipu_disable_channel(CSI_PRP_VF_MEM, true);
+
+ if (cam->vf_rotation >= IPU_ROTATE_VERT_FLIP) {
+ ipu_disable_channel(MEM_ROT_VF_MEM, true);
+ ipu_uninit_channel(MEM_ROT_VF_MEM);
+ }
+ ipu_uninit_channel(CSI_PRP_VF_MEM);
+
+ acquire_console_sem();
+ fb_blank(fbi, FB_BLANK_POWERDOWN);
+ release_console_sem();
+
+ /* Set the overlay frame buffer std to what it is used to be */
+ fbvar = fbi->var;
+ fbvar.nonstd = cam->fb_origin_std;
+ fbvar.activate |= FB_ACTIVATE_FORCE;
+ fb_set_var(fbi, &fbvar);
+
+ ipu_csi_enable_mclk_if(CSI_MCLK_VF, cam->csi, false, false);
+
+ if (cam->vf_bufs_vaddr[0]) {
+ dma_free_coherent(0, cam->vf_bufs_size[0],
+ cam->vf_bufs_vaddr[0],
+ (dma_addr_t) cam->vf_bufs[0]);
+ cam->vf_bufs_vaddr[0] = NULL;
+ cam->vf_bufs[0] = 0;
+ }
+ if (cam->vf_bufs_vaddr[1]) {
+ dma_free_coherent(0, cam->vf_bufs_size[1],
+ cam->vf_bufs_vaddr[1],
+ (dma_addr_t) cam->vf_bufs[1]);
+ cam->vf_bufs_vaddr[1] = NULL;
+ cam->vf_bufs[1] = 0;
+ }
+
+ cam->overlay_active = false;
+ return err;
+}
+
+/*!
+ * Enable csi
+ * @param private struct cam_data * mxc capture instance
+ *
+ * @return status
+ */
+static int prp_vf_enable_csi(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+
+ return ipu_enable_csi(cam->csi);
+}
+
+/*!
+ * Disable csi
+ * @param private struct cam_data * mxc capture instance
+ *
+ * @return status
+ */
+static int prp_vf_disable_csi(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+
+ return ipu_disable_csi(cam->csi);
+}
+
+/*!
+ * function to select PRP-VF as the working path
+ *
+ * @param private cam_data * mxc v4l2 main structure
+ *
+ * @return status
+ */
+int prp_vf_sdc_select(void *private)
+{
+ cam_data *cam;
+ int err = 0;
+ if (private) {
+ cam = (cam_data *) private;
+ cam->vf_start_sdc = prpvf_start;
+ cam->vf_stop_sdc = prpvf_stop;
+ cam->vf_enable_csi = prp_vf_enable_csi;
+ cam->vf_disable_csi = prp_vf_disable_csi;
+ cam->overlay_active = false;
+ } else
+ err = -EIO;
+
+ return err;
+}
+
+/*!
+ * function to de-select PRP-VF as the working path
+ *
+ * @param private cam_data * mxc v4l2 main structure
+ *
+ * @return int
+ */
+int prp_vf_sdc_deselect(void *private)
+{
+ cam_data *cam;
+ int err = 0;
+ err = prpvf_stop(private);
+
+ if (private) {
+ cam = (cam_data *) private;
+ cam->vf_start_sdc = NULL;
+ cam->vf_stop_sdc = NULL;
+ cam->vf_enable_csi = NULL;
+ cam->vf_disable_csi = NULL;
+ }
+ return err;
+}
+
+/*!
+ * Init viewfinder task.
+ *
+ * @return Error code indicating success or failure
+ */
+__init int prp_vf_sdc_init(void)
+{
+ return 0;
+}
+
+/*!
+ * Deinit viewfinder task.
+ *
+ * @return Error code indicating success or failure
+ */
+void __exit prp_vf_sdc_exit(void)
+{
+}
+
+module_init(prp_vf_sdc_init);
+module_exit(prp_vf_sdc_exit);
+
+EXPORT_SYMBOL(prp_vf_sdc_select);
+EXPORT_SYMBOL(prp_vf_sdc_deselect);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("IPU PRP VF SDC Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxc/capture/ipu_prp_vf_sdc_bg.c b/drivers/media/video/mxc/capture/ipu_prp_vf_sdc_bg.c
new file mode 100644
index 000000000000..7f0984c42950
--- /dev/null
+++ b/drivers/media/video/mxc/capture/ipu_prp_vf_sdc_bg.c
@@ -0,0 +1,443 @@
+/*
+ * Copyright 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file ipu_prp_vf_sdc_bg.c
+ *
+ * @brief IPU Use case for PRP-VF back-ground
+ *
+ * @ingroup IPU
+ */
+#include <linux/dma-mapping.h>
+#include <linux/fb.h>
+#include <linux/ipu.h>
+#include "mxc_v4l2_capture.h"
+#include "ipu_prp_sw.h"
+
+static int buffer_num = 0;
+static int buffer_ready = 0;
+
+/*
+ * Function definitions
+ */
+
+/*!
+ * SDC V-Sync callback function.
+ *
+ * @param irq int irq line
+ * @param dev_id void * device id
+ *
+ * @return status IRQ_HANDLED for handled
+ */
+static irqreturn_t prpvf_sdc_vsync_callback(int irq, void *dev_id)
+{
+ pr_debug("buffer_ready %d buffer_num %d\n", buffer_ready, buffer_num);
+ if (buffer_ready > 0) {
+ ipu_select_buffer(MEM_ROT_VF_MEM, IPU_OUTPUT_BUFFER, 0);
+ buffer_ready--;
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*!
+ * VF EOF callback function.
+ *
+ * @param irq int irq line
+ * @param dev_id void * device id
+ *
+ * @return status IRQ_HANDLED for handled
+ */
+static irqreturn_t prpvf_vf_eof_callback(int irq, void *dev_id)
+{
+ pr_debug("buffer_ready %d buffer_num %d\n", buffer_ready, buffer_num);
+
+ ipu_select_buffer(MEM_ROT_VF_MEM, IPU_INPUT_BUFFER, buffer_num);
+
+ buffer_num = (buffer_num == 0) ? 1 : 0;
+
+ ipu_select_buffer(CSI_PRP_VF_MEM, IPU_OUTPUT_BUFFER, buffer_num);
+ buffer_ready++;
+ return IRQ_HANDLED;
+}
+
+/*!
+ * prpvf_start - start the vf task
+ *
+ * @param private cam_data * mxc v4l2 main structure
+ *
+ */
+static int prpvf_start(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+ ipu_channel_params_t vf;
+ u32 format;
+ u32 offset;
+ u32 bpp, size = 3;
+ int err = 0;
+
+ if (!cam) {
+ printk(KERN_ERR "private is NULL\n");
+ return -EIO;
+ }
+
+ if (cam->overlay_active == true) {
+ pr_debug("already start.\n");
+ return 0;
+ }
+
+ format = cam->v4l2_fb.fmt.pixelformat;
+ if (cam->v4l2_fb.fmt.pixelformat == IPU_PIX_FMT_BGR24) {
+ bpp = 3, size = 3;
+ pr_info("BGR24\n");
+ } else if (cam->v4l2_fb.fmt.pixelformat == IPU_PIX_FMT_RGB565) {
+ bpp = 2, size = 2;
+ pr_info("RGB565\n");
+ } else if (cam->v4l2_fb.fmt.pixelformat == IPU_PIX_FMT_BGR32) {
+ bpp = 4, size = 4;
+ pr_info("BGR32\n");
+ } else {
+ printk(KERN_ERR
+ "unsupported fix format from the framebuffer.\n");
+ return -EINVAL;
+ }
+
+ offset = cam->v4l2_fb.fmt.bytesperline * cam->win.w.top +
+ size * cam->win.w.left;
+
+ if (cam->v4l2_fb.base == 0) {
+ printk(KERN_ERR "invalid frame buffer address.\n");
+ } else {
+ offset += (u32) cam->v4l2_fb.base;
+ }
+
+ memset(&vf, 0, sizeof(ipu_channel_params_t));
+ ipu_csi_get_window_size(&vf.csi_prp_vf_mem.in_width,
+ &vf.csi_prp_vf_mem.in_height, cam->csi);
+ vf.csi_prp_vf_mem.in_pixel_fmt = IPU_PIX_FMT_UYVY;
+ vf.csi_prp_vf_mem.out_width = cam->win.w.width;
+ vf.csi_prp_vf_mem.out_height = cam->win.w.height;
+ vf.csi_prp_vf_mem.csi = cam->csi;
+ if (cam->vf_rotation >= IPU_ROTATE_90_RIGHT) {
+ vf.csi_prp_vf_mem.out_width = cam->win.w.height;
+ vf.csi_prp_vf_mem.out_height = cam->win.w.width;
+ }
+ vf.csi_prp_vf_mem.out_pixel_fmt = format;
+ size = cam->win.w.width * cam->win.w.height * size;
+
+ err = ipu_init_channel(CSI_PRP_VF_MEM, &vf);
+ if (err != 0)
+ goto out_4;
+
+ ipu_csi_enable_mclk_if(CSI_MCLK_VF, cam->csi, true, true);
+
+ if (cam->vf_bufs_vaddr[0]) {
+ dma_free_coherent(0, cam->vf_bufs_size[0],
+ cam->vf_bufs_vaddr[0], cam->vf_bufs[0]);
+ }
+ if (cam->vf_bufs_vaddr[1]) {
+ dma_free_coherent(0, cam->vf_bufs_size[1],
+ cam->vf_bufs_vaddr[1], cam->vf_bufs[1]);
+ }
+ cam->vf_bufs_size[0] = PAGE_ALIGN(size);
+ cam->vf_bufs_vaddr[0] = (void *)dma_alloc_coherent(0,
+ cam->vf_bufs_size[0],
+ &cam->vf_bufs[0],
+ GFP_DMA |
+ GFP_KERNEL);
+ if (cam->vf_bufs_vaddr[0] == NULL) {
+ printk(KERN_ERR "Error to allocate vf buffer\n");
+ err = -ENOMEM;
+ goto out_3;
+ }
+ cam->vf_bufs_size[1] = PAGE_ALIGN(size);
+ cam->vf_bufs_vaddr[1] = (void *)dma_alloc_coherent(0,
+ cam->vf_bufs_size[1],
+ &cam->vf_bufs[1],
+ GFP_DMA |
+ GFP_KERNEL);
+ if (cam->vf_bufs_vaddr[1] == NULL) {
+ printk(KERN_ERR "Error to allocate vf buffer\n");
+ err = -ENOMEM;
+ goto out_3;
+ }
+
+ err = ipu_init_channel_buffer(CSI_PRP_VF_MEM, IPU_OUTPUT_BUFFER,
+ format, vf.csi_prp_vf_mem.out_width,
+ vf.csi_prp_vf_mem.out_height,
+ vf.csi_prp_vf_mem.out_width,
+ IPU_ROTATE_NONE, cam->vf_bufs[0],
+ cam->vf_bufs[1], 0, 0);
+ if (err != 0) {
+ printk(KERN_ERR "Error initializing CSI_PRP_VF_MEM\n");
+ goto out_3;
+ }
+ err = ipu_init_channel(MEM_ROT_VF_MEM, NULL);
+ if (err != 0) {
+ printk(KERN_ERR "Error MEM_ROT_VF_MEM channel\n");
+ goto out_3;
+ }
+
+ err = ipu_init_channel_buffer(MEM_ROT_VF_MEM, IPU_INPUT_BUFFER,
+ format, vf.csi_prp_vf_mem.out_width,
+ vf.csi_prp_vf_mem.out_height,
+ vf.csi_prp_vf_mem.out_width,
+ cam->vf_rotation, cam->vf_bufs[0],
+ cam->vf_bufs[1], 0, 0);
+ if (err != 0) {
+ printk(KERN_ERR "Error MEM_ROT_VF_MEM input buffer\n");
+ goto out_2;
+ }
+
+ if (cam->vf_rotation >= IPU_ROTATE_90_RIGHT) {
+ err = ipu_init_channel_buffer(MEM_ROT_VF_MEM, IPU_OUTPUT_BUFFER,
+ format,
+ vf.csi_prp_vf_mem.out_height,
+ vf.csi_prp_vf_mem.out_width,
+ cam->overlay_fb->var.xres * bpp,
+ IPU_ROTATE_NONE, offset, 0, 0, 0);
+
+ if (err != 0) {
+ printk(KERN_ERR "Error MEM_ROT_VF_MEM output buffer\n");
+ goto out_2;
+ }
+ } else {
+ err = ipu_init_channel_buffer(MEM_ROT_VF_MEM, IPU_OUTPUT_BUFFER,
+ format,
+ vf.csi_prp_vf_mem.out_width,
+ vf.csi_prp_vf_mem.out_height,
+ cam->overlay_fb->var.xres * bpp,
+ IPU_ROTATE_NONE, offset, 0, 0, 0);
+ if (err != 0) {
+ printk(KERN_ERR "Error MEM_ROT_VF_MEM output buffer\n");
+ goto out_2;
+ }
+ }
+
+ ipu_clear_irq(IPU_IRQ_PRP_VF_OUT_EOF);
+ err = ipu_request_irq(IPU_IRQ_PRP_VF_OUT_EOF, prpvf_vf_eof_callback,
+ 0, "Mxc Camera", cam);
+ if (err != 0) {
+ printk(KERN_ERR
+ "Error registering IPU_IRQ_PRP_VF_OUT_EOF irq.\n");
+ goto out_2;
+ }
+
+ ipu_clear_irq(IPU_IRQ_BG_SF_END);
+ err = ipu_request_irq(IPU_IRQ_BG_SF_END, prpvf_sdc_vsync_callback,
+ 0, "Mxc Camera", NULL);
+ if (err != 0) {
+ printk(KERN_ERR "Error registering IPU_IRQ_BG_SF_END irq.\n");
+ goto out_1;
+ }
+
+ ipu_enable_channel(CSI_PRP_VF_MEM);
+ ipu_enable_channel(MEM_ROT_VF_MEM);
+
+ buffer_num = 0;
+ buffer_ready = 0;
+ ipu_select_buffer(CSI_PRP_VF_MEM, IPU_OUTPUT_BUFFER, 0);
+
+ cam->overlay_active = true;
+ return err;
+
+ out_1:
+ ipu_free_irq(IPU_IRQ_PRP_VF_OUT_EOF, NULL);
+ out_2:
+ ipu_uninit_channel(MEM_ROT_VF_MEM);
+ out_3:
+ ipu_uninit_channel(CSI_PRP_VF_MEM);
+ out_4:
+ if (cam->vf_bufs_vaddr[0]) {
+ dma_free_coherent(0, cam->vf_bufs_size[0],
+ cam->vf_bufs_vaddr[0], cam->vf_bufs[0]);
+ cam->vf_bufs_vaddr[0] = NULL;
+ cam->vf_bufs[0] = 0;
+ }
+ if (cam->vf_bufs_vaddr[1]) {
+ dma_free_coherent(0, cam->vf_bufs_size[1],
+ cam->vf_bufs_vaddr[1], cam->vf_bufs[1]);
+ cam->vf_bufs_vaddr[1] = NULL;
+ cam->vf_bufs[1] = 0;
+ }
+ if (cam->rot_vf_bufs_vaddr[0]) {
+ dma_free_coherent(0, cam->rot_vf_buf_size[0],
+ cam->rot_vf_bufs_vaddr[0],
+ cam->rot_vf_bufs[0]);
+ cam->rot_vf_bufs_vaddr[0] = NULL;
+ cam->rot_vf_bufs[0] = 0;
+ }
+ if (cam->rot_vf_bufs_vaddr[1]) {
+ dma_free_coherent(0, cam->rot_vf_buf_size[1],
+ cam->rot_vf_bufs_vaddr[1],
+ cam->rot_vf_bufs[1]);
+ cam->rot_vf_bufs_vaddr[1] = NULL;
+ cam->rot_vf_bufs[1] = 0;
+ }
+ return err;
+}
+
+/*!
+ * prpvf_stop - stop the vf task
+ *
+ * @param private cam_data * mxc v4l2 main structure
+ *
+ */
+static int prpvf_stop(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+
+ if (cam->overlay_active == false)
+ return 0;
+
+ ipu_free_irq(IPU_IRQ_BG_SF_END, NULL);
+
+ ipu_free_irq(IPU_IRQ_PRP_VF_OUT_EOF, cam);
+
+ ipu_disable_channel(CSI_PRP_VF_MEM, true);
+ ipu_disable_channel(MEM_ROT_VF_MEM, true);
+ ipu_uninit_channel(CSI_PRP_VF_MEM);
+ ipu_uninit_channel(MEM_ROT_VF_MEM);
+ ipu_csi_enable_mclk_if(CSI_MCLK_VF, cam->csi, false, false);
+
+ if (cam->vf_bufs_vaddr[0]) {
+ dma_free_coherent(0, cam->vf_bufs_size[0],
+ cam->vf_bufs_vaddr[0], cam->vf_bufs[0]);
+ cam->vf_bufs_vaddr[0] = NULL;
+ cam->vf_bufs[0] = 0;
+ }
+ if (cam->vf_bufs_vaddr[1]) {
+ dma_free_coherent(0, cam->vf_bufs_size[1],
+ cam->vf_bufs_vaddr[1], cam->vf_bufs[1]);
+ cam->vf_bufs_vaddr[1] = NULL;
+ cam->vf_bufs[1] = 0;
+ }
+ if (cam->rot_vf_bufs_vaddr[0]) {
+ dma_free_coherent(0, cam->rot_vf_buf_size[0],
+ cam->rot_vf_bufs_vaddr[0],
+ cam->rot_vf_bufs[0]);
+ cam->rot_vf_bufs_vaddr[0] = NULL;
+ cam->rot_vf_bufs[0] = 0;
+ }
+ if (cam->rot_vf_bufs_vaddr[1]) {
+ dma_free_coherent(0, cam->rot_vf_buf_size[1],
+ cam->rot_vf_bufs_vaddr[1],
+ cam->rot_vf_bufs[1]);
+ cam->rot_vf_bufs_vaddr[1] = NULL;
+ cam->rot_vf_bufs[1] = 0;
+ }
+
+ buffer_num = 0;
+ buffer_ready = 0;
+ cam->overlay_active = false;
+ return 0;
+}
+
+/*!
+ * Enable csi
+ * @param private struct cam_data * mxc capture instance
+ *
+ * @return status
+ */
+static int prp_vf_enable_csi(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+
+ return ipu_enable_csi(cam->csi);
+}
+
+/*!
+ * Disable csi
+ * @param private struct cam_data * mxc capture instance
+ *
+ * @return status
+ */
+static int prp_vf_disable_csi(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+
+ return ipu_disable_csi(cam->csi);
+}
+
+/*!
+ * function to select PRP-VF as the working path
+ *
+ * @param private cam_data * mxc v4l2 main structure
+ *
+ * @return status
+ */
+int prp_vf_sdc_select_bg(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+
+ if (cam) {
+ cam->vf_start_sdc = prpvf_start;
+ cam->vf_stop_sdc = prpvf_stop;
+ cam->vf_enable_csi = prp_vf_enable_csi;
+ cam->vf_disable_csi = prp_vf_disable_csi;
+ cam->overlay_active = false;
+ }
+
+ return 0;
+}
+
+/*!
+ * function to de-select PRP-VF as the working path
+ *
+ * @param private cam_data * mxc v4l2 main structure
+ *
+ * @return status
+ */
+int prp_vf_sdc_deselect_bg(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+ int err = 0;
+ err = prpvf_stop(private);
+
+ if (cam) {
+ cam->vf_start_sdc = NULL;
+ cam->vf_stop_sdc = NULL;
+ cam->vf_enable_csi = NULL;
+ cam->vf_disable_csi = NULL;
+ }
+ return err;
+}
+
+/*!
+ * Init viewfinder task.
+ *
+ * @return Error code indicating success or failure
+ */
+__init int prp_vf_sdc_init_bg(void)
+{
+ return 0;
+}
+
+/*!
+ * Deinit viewfinder task.
+ *
+ * @return Error code indicating success or failure
+ */
+void __exit prp_vf_sdc_exit_bg(void)
+{
+}
+
+module_init(prp_vf_sdc_init_bg);
+module_exit(prp_vf_sdc_exit_bg);
+
+EXPORT_SYMBOL(prp_vf_sdc_select_bg);
+EXPORT_SYMBOL(prp_vf_sdc_deselect_bg);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("IPU PRP VF SDC Backgroud Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxc/capture/ipu_still.c b/drivers/media/video/mxc/capture/ipu_still.c
new file mode 100644
index 000000000000..ded24623ce2a
--- /dev/null
+++ b/drivers/media/video/mxc/capture/ipu_still.c
@@ -0,0 +1,268 @@
+/*
+ * Copyright 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file ipu_still.c
+ *
+ * @brief IPU Use case for still image capture
+ *
+ * @ingroup IPU
+ */
+
+#include <linux/ipu.h>
+#include <linux/semaphore.h>
+#include <linux/ipu.h>
+#include "mxc_v4l2_capture.h"
+#include "ipu_prp_sw.h"
+
+static int callback_eof_flag;
+#ifndef CONFIG_MXC_IPU_V1
+static int buffer_num;
+#endif
+
+#ifdef CONFIG_MXC_IPU_V1
+static int callback_flag;
+/*
+ * Function definitions
+ */
+/*!
+ * CSI EOF callback function.
+ *
+ * @param irq int irq line
+ * @param dev_id void * device id
+ *
+ * @return status IRQ_HANDLED for handled
+ */
+static irqreturn_t prp_csi_eof_callback(int irq, void *dev_id)
+{
+ ipu_select_buffer(CSI_MEM, IPU_OUTPUT_BUFFER,
+ callback_flag%2 ? 1 : 0);
+ if (callback_flag == 0)
+ ipu_enable_channel(CSI_MEM);
+
+ callback_flag++;
+ return IRQ_HANDLED;
+}
+#endif
+
+/*!
+ * CSI callback function.
+ *
+ * @param irq int irq line
+ * @param dev_id void * device id
+ *
+ * @return status IRQ_HANDLED for handled
+ */
+static irqreturn_t prp_still_callback(int irq, void *dev_id)
+{
+ cam_data *cam = (cam_data *) dev_id;
+
+ callback_eof_flag++;
+ if (callback_eof_flag < 5) {
+#ifndef CONFIG_MXC_IPU_V1
+ buffer_num = (buffer_num == 0) ? 1 : 0;
+ ipu_select_buffer(CSI_MEM, IPU_OUTPUT_BUFFER, buffer_num);
+#endif
+ } else {
+ cam->still_counter++;
+ wake_up_interruptible(&cam->still_queue);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*!
+ * start csi->mem task
+ * @param private struct cam_data * mxc capture instance
+ *
+ * @return status
+ */
+static int prp_still_start(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+ u32 pixel_fmt;
+ int err;
+ ipu_channel_params_t params;
+
+ if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420)
+ pixel_fmt = IPU_PIX_FMT_YUV420P;
+ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_NV12)
+ pixel_fmt = IPU_PIX_FMT_NV12;
+ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV422P)
+ pixel_fmt = IPU_PIX_FMT_YUV422P;
+ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_UYVY)
+ pixel_fmt = IPU_PIX_FMT_UYVY;
+ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV)
+ pixel_fmt = IPU_PIX_FMT_YUYV;
+ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR24)
+ pixel_fmt = IPU_PIX_FMT_BGR24;
+ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24)
+ pixel_fmt = IPU_PIX_FMT_RGB24;
+ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565)
+ pixel_fmt = IPU_PIX_FMT_RGB565;
+ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR32)
+ pixel_fmt = IPU_PIX_FMT_BGR32;
+ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB32)
+ pixel_fmt = IPU_PIX_FMT_RGB32;
+ else {
+ printk(KERN_ERR "format not supported\n");
+ return -EINVAL;
+ }
+
+ ipu_csi_enable_mclk_if(CSI_MCLK_RAW, cam->csi, true, true);
+
+ memset(&params, 0, sizeof(params));
+ err = ipu_init_channel(CSI_MEM, &params);
+ if (err != 0)
+ return err;
+
+ err = ipu_init_channel_buffer(CSI_MEM, IPU_OUTPUT_BUFFER,
+ pixel_fmt, cam->v2f.fmt.pix.width,
+ cam->v2f.fmt.pix.height,
+ cam->v2f.fmt.pix.width, IPU_ROTATE_NONE,
+ cam->still_buf[0], cam->still_buf[1],
+ 0, 0);
+ if (err != 0)
+ return err;
+
+#ifdef CONFIG_MXC_IPU_V1
+ ipu_clear_irq(IPU_IRQ_SENSOR_OUT_EOF);
+ err = ipu_request_irq(IPU_IRQ_SENSOR_OUT_EOF, prp_still_callback,
+ 0, "Mxc Camera", cam);
+ if (err != 0) {
+ printk(KERN_ERR "Error registering irq.\n");
+ return err;
+ }
+ callback_flag = 0;
+ callback_eof_flag = 0;
+ ipu_clear_irq(IPU_IRQ_SENSOR_EOF);
+ err = ipu_request_irq(IPU_IRQ_SENSOR_EOF, prp_csi_eof_callback,
+ 0, "Mxc Camera", NULL);
+ if (err != 0) {
+ printk(KERN_ERR "Error IPU_IRQ_SENSOR_EOF \n");
+ return err;
+ }
+#else
+ callback_eof_flag = 0;
+ buffer_num = 0;
+
+ ipu_clear_irq(IPU_IRQ_CSI0_OUT_EOF);
+ err = ipu_request_irq(IPU_IRQ_CSI0_OUT_EOF, prp_still_callback,
+ 0, "Mxc Camera", cam);
+ if (err != 0) {
+ printk(KERN_ERR "Error registering irq.\n");
+ return err;
+ }
+
+ ipu_select_buffer(CSI_MEM, IPU_OUTPUT_BUFFER, 0);
+ ipu_enable_channel(CSI_MEM);
+ ipu_enable_csi(cam->csi);
+#endif
+
+ return err;
+}
+
+/*!
+ * stop csi->mem encoder task
+ * @param private struct cam_data * mxc capture instance
+ *
+ * @return status
+ */
+static int prp_still_stop(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+ int err = 0;
+
+#ifdef CONFIG_MXC_IPU_V1
+ ipu_free_irq(IPU_IRQ_SENSOR_EOF, NULL);
+ ipu_free_irq(IPU_IRQ_SENSOR_OUT_EOF, cam);
+#else
+ ipu_free_irq(IPU_IRQ_CSI0_OUT_EOF, cam);
+#endif
+
+ ipu_disable_csi(cam->csi);
+ ipu_disable_channel(CSI_MEM, true);
+ ipu_uninit_channel(CSI_MEM);
+ ipu_csi_enable_mclk_if(CSI_MCLK_RAW, cam->csi, false, false);
+
+ return err;
+}
+
+/*!
+ * function to select CSI_MEM as the working path
+ *
+ * @param private struct cam_data * mxc capture instance
+ *
+ * @return status
+ */
+int prp_still_select(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+
+ if (cam) {
+ cam->csi_start = prp_still_start;
+ cam->csi_stop = prp_still_stop;
+ }
+
+ return 0;
+}
+
+/*!
+ * function to de-select CSI_MEM as the working path
+ *
+ * @param private struct cam_data * mxc capture instance
+ *
+ * @return status
+ */
+int prp_still_deselect(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+ int err = 0;
+
+ err = prp_still_stop(cam);
+
+ if (cam) {
+ cam->csi_start = NULL;
+ cam->csi_stop = NULL;
+ }
+
+ return err;
+}
+
+/*!
+ * Init the Encorder channels
+ *
+ * @return Error code indicating success or failure
+ */
+__init int prp_still_init(void)
+{
+ return 0;
+}
+
+/*!
+ * Deinit the Encorder channels
+ *
+ */
+void __exit prp_still_exit(void)
+{
+}
+
+module_init(prp_still_init);
+module_exit(prp_still_exit);
+
+EXPORT_SYMBOL(prp_still_select);
+EXPORT_SYMBOL(prp_still_deselect);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("IPU PRP STILL IMAGE Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxc/capture/mc521da.c b/drivers/media/video/mxc/capture/mc521da.c
new file mode 100644
index 000000000000..a8ff84fb4c84
--- /dev/null
+++ b/drivers/media/video/mxc/capture/mc521da.c
@@ -0,0 +1,648 @@
+/*
+ * Copyright 2006-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file mc521da.c
+ *
+ * @brief MC521DA camera driver functions
+ *
+ * @ingroup Camera
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/clk.h>
+#include "mxc_v4l2_capture.h"
+
+#define MC521DA_I2C_ADDRESS 0x22
+#define MC521DA_TERM 0xFF
+
+typedef struct {
+ u16 width;
+ u16 height;
+} mc521da_image_format;
+
+struct mc521da_reg {
+ u8 reg;
+ u8 val;
+};
+
+static sensor_interface *interface_param = NULL;
+
+static mc521da_image_format format[2] = {
+ {
+ .width = 1600,
+ .height = 1200,
+ },
+ {
+ .width = 640,
+ .height = 480,
+ },
+};
+
+const static struct mc521da_reg mc521da_initial[] = {
+ /*----------------------------------------------------------
+ * Sensor Setting Start
+ *----------------------------------------------------------
+ */
+ {0xff, 0x01}, /* Sensor setting start */
+ {0x01, 0x10}, /* Wavetable script, generated by waveman */
+ {0x10, 0x64},
+ {0x03, 0x00}, {0x04, 0x06}, {0x05, 0x30}, {0x06, 0x02}, {0x08, 0x00},
+ {0x03, 0x01}, {0x04, 0x41}, {0x05, 0x70}, {0x06, 0x03}, {0x08, 0x00},
+ {0x03, 0x02}, {0x04, 0x55}, {0x05, 0x30}, {0x06, 0x03}, {0x08, 0x00},
+ {0x03, 0x03}, {0x04, 0x5A}, {0x05, 0x30}, {0x06, 0x02}, {0x08, 0x00},
+ {0x03, 0x04}, {0x04, 0x7A}, {0x05, 0x30}, {0x06, 0x06}, {0x08, 0x00},
+ {0x03, 0x05}, {0x04, 0x9C}, {0x05, 0x30}, {0x06, 0x0F}, {0x08, 0x00},
+ {0x03, 0x06}, {0x04, 0x73}, {0x05, 0x31}, {0x06, 0x06}, {0x08, 0x00},
+ {0x03, 0x07}, {0x04, 0x2D}, {0x05, 0x3B}, {0x06, 0x06}, {0x08, 0x00},
+ {0x03, 0x08}, {0x04, 0x32}, {0x05, 0x33}, {0x06, 0x06}, {0x08, 0x00},
+ {0x03, 0x09}, {0x04, 0x67}, {0x05, 0x63}, {0x06, 0x06}, {0x08, 0x00},
+ {0x03, 0x0a}, {0x04, 0x6C}, {0x05, 0x23}, {0x06, 0x0E}, {0x08, 0x00},
+ {0x03, 0x0b}, {0x04, 0x71}, {0x05, 0x23}, {0x06, 0x06}, {0x08, 0x00},
+ {0x03, 0x0c}, {0x04, 0x30}, {0x05, 0x2F}, {0x06, 0x06}, {0x08, 0x00},
+ {0x03, 0x0d}, {0x04, 0x00}, {0x05, 0x00}, {0x06, 0x06}, {0x08, 0x00},
+ {0x07, 0x0e},
+
+ /* Start Address */
+ {0x10, 0x64}, {0x14, 0x10}, {0x15, 0x00},
+
+ /* SYNC */
+ {0x18, 0x40}, {0x19, 0x00}, {0x1A, 0x03}, {0x1B, 0x00},
+
+ /* X-Y Mirror */
+ {0x11, 0x00}, {0xda, 0x00}, /* X mirror OFF, Y Mirror OFF */
+
+ /* Frame height */
+ {0x1c, 0x13}, {0x1d, 0x04}, {0x0e, 0x4b}, {0x0f, 0x05},
+ {0x9e, 0x04}, {0x9d, 0xc6}, {0xcc, 0x14}, {0xcd, 0x05},
+
+ /* Frame width */
+ {0x0c, 0x35}, {0x0d, 0x07}, {0x9b, 0x10}, {0x9c, 0x07},
+ {0x93, 0x21},
+
+ {0x01, 0x01}, {0x40, 0x00}, {0x41, 0x00}, {0x42, 0xf0},
+ {0x43, 0x03}, {0x44, 0x0a}, {0x45, 0x00}, {0x3b, 0x40},
+ {0x38, 0x18}, {0x3c, 0x00}, {0x20, 0x00}, {0x21, 0x01},
+ {0x22, 0x00}, {0x23, 0x01}, {0x24, 0x00}, {0x25, 0x01},
+ {0x26, 0x00}, {0x27, 0x01}, {0xb9, 0x04}, {0xb8, 0xc3},
+ {0xbb, 0x04}, {0xba, 0xc3}, {0xbf, 0x04}, {0xbe, 0xc3},
+
+ /* Ramp */
+ {0x57, 0x07}, {0x56, 0xd6}, {0x55, 0x03}, {0x54, 0x74},
+ {0x9f, 0x99}, {0x94, 0x80}, {0x91, 0x78}, {0x92, 0x8b},
+
+ /* Output Mode */
+ {0x52, 0x10}, {0x51, 0x00},
+
+ /* Analog Gain and Output driver */
+ {0x28, 0x00}, {0xdd, 0x82}, {0xdb, 0x00}, {0xdc, 0x00},
+
+ /* Update */
+ {0x00, 0x84},
+
+ /* PLL ADC clock = 75 MHz */
+ {0xb5, 0x60}, {0xb4, 0x02}, {0xb5, 0x20},
+
+ /*----------------------------------------------*/
+ /* ISP Setting Start */
+ /*----------------------------------------------*/
+ {0xff, 0x02},
+ {0x01, 0xbd}, {0x02, 0xf8}, {0x03, 0x3a}, {0x04, 0x00}, {0x0e, 0x00},
+
+ /* Output mode */
+ {0x88, 0x00}, {0x87, 0x11},
+
+ /* Threshold */
+ {0xb6, 0x1b}, {0x0d, 0xc0}, {0x24, 0x00}, {0x25, 0x00}, {0x26, 0x00},
+
+ /* Image Effect */
+ {0x3f, 0x80}, {0x40, 0x00}, {0x41, 0x00}, {0x42, 0x80}, {0x43, 0x00},
+ {0x44, 0x00}, {0x45, 0x00}, {0x46, 0x00}, {0x56, 0x80}, {0x57, 0x20},
+ {0x58, 0x20}, {0x59, 0x02}, {0x5a, 0x00}, {0x5b, 0x78}, {0x5c, 0x7c},
+ {0x5d, 0x84}, {0x5e, 0x85}, {0x5f, 0x78}, {0x60, 0x7e}, {0x61, 0x82},
+ {0x62, 0x85}, {0x63, 0x00}, {0x64, 0x80}, {0x65, 0x00}, {0x66, 0x80},
+ {0x67, 0x80}, {0x68, 0x80},
+
+ /* Auto Focus */
+ {0x6e, 0x02}, {0x6f, 0xe5}, {0x70, 0x08}, {0x71, 0x01}, {0x72, 0x00},
+
+ /* Decimator */
+ {0x78, 0xff}, {0x79, 0xff}, {0x7a, 0x70}, {0x7b, 0x00}, {0x7c, 0x00},
+ {0x7d, 0x00}, {0x7e, 0xc8}, {0x7f, 0xc8}, {0x80, 0x96}, {0x81, 0x96},
+ {0x82, 0x00}, {0x83, 0x00}, {0x84, 0x00}, {0x85, 0x00}, {0x86, 0x00},
+
+ /* Luminance Info */
+ {0xf9, 0x20}, {0xb7, 0x7f}, {0xb8, 0x28}, {0xb9, 0x08},
+ {0xf9, 0xa0}, {0xb7, 0x10}, {0xb9, 0x00},
+ {0xf9, 0x40}, {0xb7, 0x7f}, {0xb8, 0x28}, {0xb9, 0x08},
+ {0xf9, 0xc0}, {0xb7, 0x08}, {0xb9, 0x00},
+ {0xf9, 0x60}, {0xb7, 0x7f}, {0xb8, 0x28}, {0xb9, 0x08},
+ {0xf9, 0xe0}, {0xb7, 0x05}, {0xb9, 0x00},
+ {0xf9, 0x00}, {0xb7, 0x03}, {0xb8, 0x2d}, {0xb9, 0xcd},
+ {0xf9, 0x80}, {0xb7, 0x02}, {0xb9, 0x00},
+
+ /* AE */
+ {0x8a, 0x00}, {0x89, 0xc0}, {0x8c, 0x32}, {0x8d, 0x96}, {0x8e, 0x25},
+ {0x8f, 0x70}, {0x90, 0x12}, {0x91, 0x41}, {0x9e, 0x2e}, {0x9f, 0x2e},
+ {0xa0, 0x0b}, {0xa1, 0x71}, {0xa2, 0xb0}, {0xa3, 0x09}, {0xa4, 0x89},
+ {0xa5, 0x68}, {0xa6, 0x1a}, {0xa7, 0xb3}, {0xa8, 0xf0}, {0xa9, 0x19},
+ {0xaa, 0x6a}, {0xab, 0x6b}, {0xac, 0x01}, {0xad, 0xe8}, {0xae, 0x48},
+ {0xaf, 0x01}, {0xb0, 0x96}, {0xb1, 0xe6}, {0xb2, 0x03}, {0xb3, 0x00},
+ {0xb4, 0x10}, {0xb5, 0x00}, {0xb6, 0x04}, {0xba, 0x44}, {0xbb, 0x3a},
+ {0xbc, 0x01}, {0xbd, 0x08}, {0xbe, 0xa0}, {0xbf, 0x01}, {0xc0, 0x82},
+ {0x8a, 0xe1}, {0x8b, 0x8c},
+
+ /* AWB */
+ {0xc8, 0x00}, {0xc9, 0x00}, {0xca, 0x40}, {0xcb, 0xB0}, {0xcc, 0x40},
+ {0xcd, 0xff}, {0xce, 0x19}, {0xcf, 0x40}, {0xd0, 0x01}, {0xd1, 0x43},
+ {0xd2, 0x80}, {0xd3, 0x80}, {0xd4, 0xf1}, {0xdf, 0x00}, {0xe0, 0x8f},
+ {0xe1, 0x8f}, {0xe2, 0x53}, {0xe3, 0x97}, {0xe4, 0x1f}, {0xe5, 0x3b},
+ {0xe6, 0x9c}, {0xe7, 0x2e}, {0xe8, 0x03}, {0xe9, 0x02},
+
+ /* Neutral CCM */
+ {0xfa, 0x00}, {0xd5, 0x3f}, {0xd6, 0x8c}, {0xd7, 0x43}, {0xd8, 0x08},
+ {0xd9, 0x27}, {0xda, 0x7e}, {0xdb, 0x17}, {0xdc, 0x1a}, {0xdd, 0x47},
+ {0xde, 0xa1},
+
+ /* Blue CCM */
+ {0xfa, 0x01}, {0xd5, 0x3f}, {0xd6, 0x77}, {0xd7, 0x34}, {0xd8, 0x03},
+ {0xd9, 0x18}, {0xda, 0x6e}, {0xdb, 0x16}, {0xdc, 0x0f}, {0xdd, 0x29},
+ {0xde, 0x77},
+
+ /* Red CCM */
+ {0xfa, 0x02}, {0xd5, 0x3f}, {0xd6, 0x7d}, {0xd7, 0x2f}, {0xd8, 0x0e},
+ {0xd9, 0x1e}, {0xda, 0x76}, {0xdb, 0x18}, {0xdc, 0x29}, {0xdd, 0x51},
+ {0xde, 0xba},
+
+ /* AWB */
+ {0xea, 0x00}, {0xeb, 0x1a}, {0xc8, 0x33}, {0xc9, 0xc2},
+
+ {0xed, 0x02}, {0xee, 0x02},
+
+ /* AFD */
+ {0xf0, 0x11}, {0xf1, 0x03}, {0xf2, 0x05}, {0xf5, 0x05}, {0xf6, 0x32},
+ {0xf7, 0x32},
+
+ /* Lens Shading */
+ {0xf9, 0x00}, {0x05, 0x04}, {0x06, 0xff}, {0x07, 0xf2}, {0x08, 0x00},
+ {0x09, 0x00}, {0x0a, 0xf2}, {0x0b, 0xff}, {0x0c, 0xff},
+ {0xf9, 0x01}, {0x05, 0x04}, {0x06, 0xff}, {0x07, 0x8b}, {0x08, 0x16},
+ {0x09, 0x16}, {0x0a, 0x8b}, {0x0b, 0xff}, {0x0c, 0xe0},
+ {0xf9, 0x02}, {0x05, 0x04}, {0x06, 0xff}, {0x07, 0x8b}, {0x08, 0x16},
+ {0x09, 0x16}, {0x0a, 0x8b}, {0x0b, 0xff}, {0x0c, 0xe0},
+ {0xf9, 0x03}, {0x05, 0x04}, {0x06, 0xff}, {0x07, 0x7c}, {0x08, 0x26},
+ {0x09, 0x26}, {0x0a, 0x7c}, {0x0b, 0xd0}, {0x0c, 0xe0},
+ {0xf9, 0x04}, {0x05, 0x0d}, {0x06, 0x40}, {0x07, 0xa0}, {0x08, 0x00},
+ {0x09, 0x00}, {0x0a, 0xa0}, {0x0b, 0x40}, {0x0c, 0xe0},
+ {0xf9, 0x05}, {0x05, 0x0d}, {0x06, 0x40}, {0x07, 0xa0}, {0x08, 0x00},
+ {0x09, 0x00}, {0x0a, 0xa0}, {0x0b, 0x40}, {0x0c, 0xa0},
+ {0xf9, 0x06}, {0x05, 0x0d}, {0x06, 0x40}, {0x07, 0xa0}, {0x08, 0x00},
+ {0x09, 0x00}, {0x0a, 0xa0}, {0x0b, 0x40}, {0x0c, 0xa0},
+ {0xf9, 0x07}, {0x05, 0x0d}, {0x06, 0x40}, {0x07, 0xa0}, {0x08, 0x00},
+ {0x09, 0x00}, {0x0a, 0xa0}, {0x0b, 0x40}, {0x0c, 0xa0},
+
+ /* Edge setting */
+ {0x73, 0x68}, {0x74, 0x40}, {0x75, 0x00}, {0x76, 0xff}, {0x77, 0x80},
+ {0x4f, 0x80}, {0x50, 0x82}, {0x51, 0x82}, {0x52, 0x08},
+
+ /* Interpolation Setting */
+ {0x23, 0x7f}, {0x22, 0x08}, {0x18, 0xff}, {0x19, 0x00},
+ {0x40, 0x00}, {0x53, 0xff}, {0x54, 0x0a}, {0x55, 0xc2},
+ {0x1b, 0x18},
+
+ {0xfa, 0x00}, {0x15, 0x0c}, {0x22, 0x00}, {0x0e, 0xef}, {0x1f, 0x1d},
+ {0x20, 0x2d}, {0x1c, 0x01}, {0x1d, 0x02}, {0x1e, 0x03}, {0x0e, 0xee},
+ {0x12, 0x10}, {0x16, 0x10}, {0x17, 0x02}, {0x1a, 0x01},
+ {0xfa, 0x04}, {0x0e, 0xef}, {0x1c, 0x01}, {0x1d, 0x02}, {0x1e, 0x03},
+ {0x1f, 0x11}, {0x20, 0x11}, {0x0e, 0xee}, {0x12, 0x03}, {0x16, 0x10},
+ {0x17, 0x02}, {0x1a, 0xee},
+ {0xfa, 0x08}, {0x0e, 0xef}, {0x1c, 0x01}, {0x1d, 0x02}, {0x1e, 0x03},
+ {0x1f, 0x00}, {0x20, 0x00}, {0x0e, 0xee}, {0x12, 0x03}, {0x16, 0x10},
+ {0x17, 0x02}, {0x1a, 0x22},
+
+ /* Gamma Correction */
+ {0x27, 0x62}, {0x28, 0x00}, {0x27, 0x62}, {0x28, 0x00}, {0x29, 0x00},
+ {0x2a, 0x00}, {0x2f, 0x03}, {0x30, 0x10}, {0x31, 0x2b}, {0x32, 0x50},
+ {0x33, 0x70}, {0x34, 0x90}, {0x35, 0xB0}, {0x36, 0xD0}, {0x37, 0x00},
+ {0x38, 0x18}, {0x39, 0x57}, {0x3a, 0x89}, {0x3b, 0xac}, {0x3c, 0xc9},
+ {0x3d, 0xde}, {0x3e, 0xef}, {0x2b, 0x00}, {0x2c, 0x00}, {0x2d, 0x40},
+ {0x2e, 0xab},
+
+ /* Contrast */
+ {0x47, 0x10}, {0x48, 0x1f}, {0x49, 0xe3}, {0x4a, 0xf0}, {0x4b, 0x08},
+ {0x4c, 0x14}, {0x4d, 0xe9}, {0x4e, 0xf5}, {0x98, 0x8a},
+
+ {0xfa, 0x00},
+ {MC521DA_TERM, MC521DA_TERM}
+};
+
+static int mc521da_attach(struct i2c_adapter *adapter);
+static int mc521da_detach(struct i2c_client *client);
+
+static struct i2c_driver mc521da_i2c_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "MC521DA Client",
+ },
+ .attach_adapter = mc521da_attach,
+ .detach_client = mc521da_detach,
+};
+
+static struct i2c_client mc521da_i2c_client = {
+ .name = "MC521DA I2C dev",
+ .addr = MC521DA_I2C_ADDRESS,
+ .driver = &mc521da_i2c_driver,
+};
+
+static inline int mc521da_read_reg(u8 reg)
+{
+ return i2c_smbus_read_byte_data(&mc521da_i2c_client, reg);
+}
+
+static inline int mc521da_write_reg(u8 reg, u8 val)
+{
+ return i2c_smbus_write_byte_data(&mc521da_i2c_client, reg, val);
+}
+
+static int mc521da_write_regs(const struct mc521da_reg reglist[])
+{
+ int err;
+ const struct mc521da_reg *next = reglist;
+
+ while (!((next->reg == MC521DA_TERM) && (next->val == MC521DA_TERM))) {
+ err = mc521da_write_reg(next->reg, next->val);
+ if (err) {
+ return err;
+ }
+ next++;
+ }
+ return 0;
+}
+
+/*!
+ * mc521da sensor downscale function
+ * @param downscale bool
+ * @return Error code indicating success or failure
+ */
+static u8 mc521da_sensor_downscale(bool downscale)
+{
+ u8 data;
+ u32 i = 0;
+
+ if (downscale == true) {
+ // VGA
+ mc521da_write_reg(0xff, 0x01);
+
+ mc521da_write_reg(0x52, 0x30);
+ mc521da_write_reg(0x51, 0x00);
+
+ mc521da_write_reg(0xda, 0x01);
+ mc521da_write_reg(0x00, 0x8C);
+
+ /* Wait for changes to take effect */
+ while (i < 256) {
+ i++;
+ data = mc521da_read_reg(0x00);
+ if ((data & 0x80) == 0)
+ break;
+ msleep(5);
+ }
+
+ /* ISP */
+ mc521da_write_reg(0xff, 0x02);
+
+ mc521da_write_reg(0x03, 0x3b); /* Enable Decimator */
+
+ mc521da_write_reg(0x7a, 0x74);
+ mc521da_write_reg(0x7b, 0x01);
+ mc521da_write_reg(0x7e, 0x50);
+ mc521da_write_reg(0x7f, 0x50);
+ mc521da_write_reg(0x80, 0x3c);
+ mc521da_write_reg(0x81, 0x3c);
+ } else {
+ //UXGA
+ mc521da_write_reg(0xff, 0x01);
+ mc521da_write_reg(0x52, 0x10);
+ mc521da_write_reg(0x51, 0x00);
+ mc521da_write_reg(0xda, 0x00);
+
+ /* update */
+ mc521da_write_reg(0x00, 0x84);
+
+ /* Wait for changes to take effect */
+ while (i < 256) {
+ i++;
+ data = mc521da_read_reg(0x00);
+ if ((data & 0x80) == 0)
+ break;
+ msleep(5);
+ }
+
+ /* ISP */
+ mc521da_write_reg(0xff, 0x02);
+
+ mc521da_write_reg(0x03, 0x3a);
+
+ mc521da_write_reg(0x7a, 0x70);
+ mc521da_write_reg(0x7b, 0x00);
+ mc521da_write_reg(0x7e, 0xc8);
+ mc521da_write_reg(0x7f, 0xc8);
+ mc521da_write_reg(0x80, 0x96);
+ mc521da_write_reg(0x81, 0x96);
+ }
+
+ return 0;
+}
+
+/*!
+ * mc521da sensor interface Initialization
+ * @param param sensor_interface *
+ * @param width u32
+ * @param height u32
+ * @return None
+ */
+static void mc521da_interface(sensor_interface * param, u32 width, u32 height)
+{
+ param->clk_mode = 0x0; //gated
+ param->pixclk_pol = 0x0;
+ param->data_width = 0x1;
+ param->data_pol = 0x0;
+ param->ext_vsync = 0x0;
+ param->Vsync_pol = 0x0;
+ param->Hsync_pol = 0x0;
+ param->width = width - 1;
+ param->height = height - 1;
+ param->active_width = width;
+ param->active_height = height;
+ param->pixel_fmt = IPU_PIX_FMT_UYVY;
+}
+
+extern void gpio_sensor_reset(bool flag);
+
+/*!
+ * mc521da Reset function
+ *
+ * @return None
+ */
+static sensor_interface *mc521da_reset(void)
+{
+ if (interface_param == NULL)
+ return NULL;
+
+ mc521da_interface(interface_param, format[1].width, format[1].height);
+ set_mclk_rate(&interface_param->mclk);
+
+ gpio_sensor_reset(true);
+ msleep(10);
+ gpio_sensor_reset(false);
+ msleep(50);
+
+ return interface_param;
+}
+
+/*!
+ * mc521da sensor configuration
+ *
+ * @param frame_rate int *
+ * @param high_quality int
+ * @return sensor_interface *
+ */
+static sensor_interface *mc521da_config(int *frame_rate, int high_quality)
+{
+ int num_clock_per_row, err;
+ int max_rate = 0;
+ int index = 1;
+ u16 frame_height;
+
+ if (high_quality == 1)
+ index = 0;
+
+ err = mc521da_write_regs(mc521da_initial);
+ if (err) {
+ /* Reduce the MCLK */
+ interface_param->mclk = 20000000;
+ mc521da_reset();
+
+ printk(KERN_INFO "mc521da: mclk reduced\n");
+ mc521da_write_regs(mc521da_initial);
+ }
+
+ mc521da_interface(interface_param, format[index].width,
+ format[index].height);
+
+ if (index == 0) {
+ mc521da_sensor_downscale(false);
+ } else {
+ mc521da_sensor_downscale(true);
+ }
+
+ num_clock_per_row = 1845;
+ max_rate = interface_param->mclk * 3 * (index + 1)
+ / (2 * num_clock_per_row * 1300);
+
+ if ((*frame_rate > max_rate) || (*frame_rate == 0)) {
+ *frame_rate = max_rate;
+ }
+
+ frame_height = 1300 * max_rate / (*frame_rate);
+
+ *frame_rate = interface_param->mclk * 3 * (index + 1)
+ / (2 * num_clock_per_row * frame_height);
+
+ mc521da_write_reg(0xff, 0x01);
+ mc521da_write_reg(0xE, frame_height & 0xFF);
+ mc521da_write_reg(0xF, (frame_height & 0xFF00) >> 8);
+ mc521da_write_reg(0xCC, frame_height & 0xFF);
+ mc521da_write_reg(0xCD, (frame_height & 0xFF00) >> 8);
+
+ return interface_param;
+}
+
+/*!
+ * mc521da sensor set color configuration
+ *
+ * @param bright int
+ * @param saturation int
+ * @param red int
+ * @param green int
+ * @param blue int
+ * @return None
+ */
+static void
+mc521da_set_color(int bright, int saturation, int red, int green, int blue)
+{
+ /* Select ISP */
+ mc521da_write_reg(0xff, 0x02);
+
+ mc521da_write_reg(0x41, bright);
+ mc521da_write_reg(0xca, red);
+ mc521da_write_reg(0xcb, green);
+ mc521da_write_reg(0xcc, blue);
+}
+
+/*!
+ * mc521da sensor get color configuration
+ *
+ * @param bright int *
+ * @param saturation int *
+ * @param red int *
+ * @param green int *
+ * @param blue int *
+ * @return None
+ */
+static void
+mc521da_get_color(int *bright, int *saturation, int *red, int *green, int *blue)
+{
+ *saturation = 0;
+
+ /* Select ISP */
+ mc521da_write_reg(0xff, 0x02);
+
+ *bright = mc521da_read_reg(0x41);
+ *red = mc521da_read_reg(0xCA);
+ *green = mc521da_read_reg(0xCB);
+ *blue = mc521da_read_reg(0xCC);
+}
+
+struct camera_sensor camera_sensor_if = {
+ set_color:mc521da_set_color,
+ get_color:mc521da_get_color,
+ config:mc521da_config,
+ reset:mc521da_reset,
+};
+
+/*!
+ * mc521da I2C detect_client function
+ *
+ * @param adapter struct i2c_adapter *
+ * @param address int
+ * @param kind int
+ *
+ * @return Error code indicating success or failure
+ */
+static int mc521da_detect_client(struct i2c_adapter *adapter, int address,
+ int kind)
+{
+ mc521da_i2c_client.adapter = adapter;
+ if (i2c_attach_client(&mc521da_i2c_client)) {
+ mc521da_i2c_client.adapter = NULL;
+ printk(KERN_ERR "mc521da_attach: i2c_attach_client failed\n");
+ return -1;
+ }
+
+ interface_param = (sensor_interface *)
+ kmalloc(sizeof(sensor_interface), GFP_KERNEL);
+ if (!interface_param) {
+ printk(KERN_ERR "mc521da_attach: kmalloc failed \n");
+ return -1;
+ }
+
+ interface_param->mclk = 25000000;
+
+ printk(KERN_INFO "mc521da Detected\n");
+
+ return 0;
+}
+
+static unsigned short normal_i2c[] = { MC521DA_I2C_ADDRESS, I2C_CLIENT_END };
+
+/* Magic definition of all other variables and things */
+I2C_CLIENT_INSMOD;
+
+static int mc521da_attach(struct i2c_adapter *adap)
+{
+ uint32_t mclk = 25000000;
+ struct clk *clk;
+ int err;
+
+ clk = clk_get(NULL, "csi_clk");
+ clk_enable(clk);
+ set_mclk_rate(&mclk);
+
+ gpio_sensor_reset(true);
+ msleep(10);
+ gpio_sensor_reset(false);
+ msleep(100);
+
+ err = i2c_probe(adap, &addr_data, &mc521da_detect_client);
+
+ clk_disable(clk);
+ clk_put(clk);
+
+ return err;
+}
+
+/*!
+ * mc521da I2C detach function
+ *
+ * @param client struct i2c_client *
+ * @return Error code indicating success or failure
+ */
+static int mc521da_detach(struct i2c_client *client)
+{
+ int err;
+
+ if (!mc521da_i2c_client.adapter)
+ return -1;
+
+ err = i2c_detach_client(&mc521da_i2c_client);
+ mc521da_i2c_client.adapter = NULL;
+
+ if (interface_param)
+ kfree(interface_param);
+ interface_param = NULL;
+
+ return err;
+}
+
+extern void gpio_sensor_active(void);
+extern void gpio_sensor_inactive(void);
+
+/*!
+ * mc521da init function
+ *
+ * @return Error code indicating success or failure
+ */
+static __init int mc521da_init(void)
+{
+ gpio_sensor_active();
+ return i2c_add_driver(&mc521da_i2c_driver);
+}
+
+/*!
+ * mc521da cleanup function
+ *
+ * @return Error code indicating success or failure
+ */
+static void __exit mc521da_clean(void)
+{
+ i2c_del_driver(&mc521da_i2c_driver);
+ gpio_sensor_inactive();
+}
+
+module_init(mc521da_init);
+module_exit(mc521da_clean);
+
+/* Exported symbols for modules. */
+EXPORT_SYMBOL(camera_sensor_if);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MC521DA Camera Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxc/capture/mt9v111.c b/drivers/media/video/mxc/capture/mt9v111.c
new file mode 100644
index 000000000000..c95a20683924
--- /dev/null
+++ b/drivers/media/video/mxc/capture/mt9v111.c
@@ -0,0 +1,1076 @@
+/*
+ * Copyright 2004-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file mt9v111.c
+ *
+ * @brief mt9v111 camera driver functions
+ *
+ * @ingroup Camera
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/clk.h>
+#include <media/v4l2-int-device.h>
+#include "mxc_v4l2_capture.h"
+#include "mt9v111.h"
+
+#ifdef MT9V111_DEBUG
+static u16 testpattern = 0;
+#endif
+
+static mt9v111_conf mt9v111_device;
+
+/*!
+ * Holds the current frame rate.
+ */
+static int reset_frame_rate = MT9V111_FRAME_RATE;
+
+struct sensor {
+ const struct mt9v111_platform_data *platform_data;
+ struct v4l2_int_device *v4l2_int_device;
+ struct i2c_client *i2c_client;
+ struct v4l2_pix_format pix;
+ struct v4l2_captureparm streamcap;
+ bool on;
+
+ /* control settings */
+ int brightness;
+ int hue;
+ int contrast;
+ int saturation;
+ int red;
+ int green;
+ int blue;
+ int ae_mode;
+
+} mt9v111_data;
+
+extern void gpio_sensor_active(void);
+extern void gpio_sensor_inactive(void);
+
+static int mt9v111_probe(struct i2c_client *client,
+ const struct i2c_device_id *id);
+static int mt9v111_remove(struct i2c_client *client);
+
+static const struct i2c_device_id mt9v111_id[] = {
+ {"mt9v111", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, mt9v111_id);
+
+static struct i2c_driver mt9v111_i2c_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "mt9v111",
+ },
+ .probe = mt9v111_probe,
+ .remove = mt9v111_remove,
+ .id_table = mt9v111_id,
+/* To add power management add .suspend and .resume functions */
+};
+
+/*
+ * Function definitions
+ */
+
+#ifdef MT9V111_DEBUG
+static inline int mt9v111_read_reg(u8 reg)
+{
+ int val = i2c_smbus_read_word_data(mt9v111_data.i2c_client, reg);
+ if (val != -1)
+ val = cpu_to_be16(val);
+ return val;
+}
+#endif
+
+/*!
+ * Writes to the register via I2C.
+ */
+static inline int mt9v111_write_reg(u8 reg, u16 val)
+{
+ pr_debug("In mt9v111_write_reg (0x%x, 0x%x)\n", reg, val);
+ pr_debug(" write reg %x val %x.\n", reg, val);
+
+ return i2c_smbus_write_word_data(mt9v111_data.i2c_client,
+ reg, cpu_to_be16(val));
+}
+
+/*!
+ * Initialize mt9v111_sensor_lib
+ * Libarary for Sensor configuration through I2C
+ *
+ * @param coreReg Core Registers
+ * @param ifpReg IFP Register
+ *
+ * @return status
+ */
+static u8 mt9v111_sensor_lib(mt9v111_coreReg * coreReg, mt9v111_IFPReg * ifpReg)
+{
+ u8 reg;
+ u16 data;
+ u8 error = 0;
+
+ pr_debug("In mt9v111_sensor_lib\n");
+
+ /*
+ * setup to IFP registers
+ */
+ reg = MT9V111I_ADDR_SPACE_SEL;
+ data = ifpReg->addrSpaceSel;
+ mt9v111_write_reg(reg, data);
+
+ /* Operation Mode Control */
+ reg = MT9V111I_MODE_CONTROL;
+ data = ifpReg->modeControl;
+ mt9v111_write_reg(reg, data);
+
+ /* Output format */
+ reg = MT9V111I_FORMAT_CONTROL;
+ data = ifpReg->formatControl; /* Set bit 12 */
+ mt9v111_write_reg(reg, data);
+
+ /* AE limit 4 */
+ reg = MT9V111I_SHUTTER_WIDTH_LIMIT_AE;
+ data = ifpReg->gainLimitAE;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111I_OUTPUT_FORMAT_CTRL2;
+ data = ifpReg->outputFormatCtrl2;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111I_AE_SPEED;
+ data = ifpReg->AESpeed;
+ mt9v111_write_reg(reg, data);
+
+ /* output image size */
+ reg = MT9V111i_H_PAN;
+ data = 0x8000 | ifpReg->HPan;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111i_H_ZOOM;
+ data = 0x8000 | ifpReg->HZoom;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111i_H_SIZE;
+ data = 0x8000 | ifpReg->HSize;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111i_V_PAN;
+ data = 0x8000 | ifpReg->VPan;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111i_V_ZOOM;
+ data = 0x8000 | ifpReg->VZoom;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111i_V_SIZE;
+ data = 0x8000 | ifpReg->VSize;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111i_H_PAN;
+ data = ~0x8000 & ifpReg->HPan;
+ mt9v111_write_reg(reg, data);
+#if 0
+ reg = MT9V111I_UPPER_SHUTTER_DELAY_LIM;
+ data = ifpReg->upperShutterDelayLi;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111I_SHUTTER_60;
+ data = ifpReg->shutter_width_60;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111I_SEARCH_FLICK_60;
+ data = ifpReg->search_flicker_60;
+ mt9v111_write_reg(reg, data);
+#endif
+
+ /*
+ * setup to sensor core registers
+ */
+ reg = MT9V111I_ADDR_SPACE_SEL;
+ data = coreReg->addressSelect;
+ mt9v111_write_reg(reg, data);
+
+ /* enable changes and put the Sync bit on */
+ reg = MT9V111S_OUTPUT_CTRL;
+ data = MT9V111S_OUTCTRL_SYNC | MT9V111S_OUTCTRL_CHIP_ENABLE | 0x3000;
+ mt9v111_write_reg(reg, data);
+
+ /* min PIXCLK - Default */
+ reg = MT9V111S_PIXEL_CLOCK_SPEED;
+ data = coreReg->pixelClockSpeed;
+ mt9v111_write_reg(reg, data);
+
+ /* Setup image flipping / Dark rows / row/column skip */
+ reg = MT9V111S_READ_MODE;
+ data = coreReg->readMode;
+ mt9v111_write_reg(reg, data);
+
+ /* zoom 0 */
+ reg = MT9V111S_DIGITAL_ZOOM;
+ data = coreReg->digitalZoom;
+ mt9v111_write_reg(reg, data);
+
+ /* min H-blank */
+ reg = MT9V111S_HOR_BLANKING;
+ data = coreReg->horizontalBlanking;
+ mt9v111_write_reg(reg, data);
+
+ /* min V-blank */
+ reg = MT9V111S_VER_BLANKING;
+ data = coreReg->verticalBlanking;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111S_SHUTTER_WIDTH;
+ data = coreReg->shutterWidth;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111S_SHUTTER_DELAY;
+ data = ifpReg->upperShutterDelayLi;
+ mt9v111_write_reg(reg, data);
+
+ /* changes become effective */
+ reg = MT9V111S_OUTPUT_CTRL;
+ data = MT9V111S_OUTCTRL_CHIP_ENABLE | 0x3000;
+ mt9v111_write_reg(reg, data);
+
+ return error;
+}
+
+/*!
+ * MT9V111 frame rate calculate
+ *
+ * @param frame_rate int *
+ * @param mclk int
+ * @return None
+ */
+static void mt9v111_rate_cal(int *frame_rate, int mclk)
+{
+ int num_clock_per_row;
+ int max_rate = 0;
+
+ pr_debug("In mt9v111_rate_cal\n");
+
+ num_clock_per_row = (MT9V111_MAX_WIDTH + 114 + MT9V111_HORZBLANK_MIN)
+ * 2;
+ max_rate = mclk / (num_clock_per_row *
+ (MT9V111_MAX_HEIGHT + MT9V111_VERTBLANK_DEFAULT));
+
+ if ((*frame_rate > max_rate) || (*frame_rate == 0)) {
+ *frame_rate = max_rate;
+ }
+
+ mt9v111_device.coreReg->verticalBlanking
+ = mclk / (*frame_rate * num_clock_per_row) - MT9V111_MAX_HEIGHT;
+
+ reset_frame_rate = *frame_rate;
+}
+
+/*!
+ * MT9V111 sensor configuration
+ */
+void mt9v111_config(void)
+{
+ pr_debug("In mt9v111_config\n");
+
+ mt9v111_device.coreReg->addressSelect = MT9V111I_SEL_SCA;
+ mt9v111_device.ifpReg->addrSpaceSel = MT9V111I_SEL_IFP;
+
+ mt9v111_device.coreReg->windowHeight = MT9V111_WINHEIGHT;
+ mt9v111_device.coreReg->windowWidth = MT9V111_WINWIDTH;
+ mt9v111_device.coreReg->zoomColStart = 0;
+ mt9v111_device.coreReg->zomRowStart = 0;
+ mt9v111_device.coreReg->digitalZoom = 0x0;
+
+ mt9v111_device.coreReg->verticalBlanking = MT9V111_VERTBLANK_DEFAULT;
+ mt9v111_device.coreReg->horizontalBlanking = MT9V111_HORZBLANK_MIN;
+ mt9v111_device.coreReg->pixelClockSpeed = 0;
+ mt9v111_device.coreReg->readMode = 0xd0a1;
+
+ mt9v111_device.ifpReg->outputFormatCtrl2 = 0;
+ mt9v111_device.ifpReg->gainLimitAE = 0x300;
+ mt9v111_device.ifpReg->AESpeed = 0x80;
+
+ /* here is the default value */
+ mt9v111_device.ifpReg->formatControl = 0xc800;
+ mt9v111_device.ifpReg->modeControl = 0x708e;
+ mt9v111_device.ifpReg->awbSpeed = 0x4514;
+ mt9v111_device.coreReg->shutterWidth = 0xf8;
+
+ /* output size */
+ mt9v111_device.ifpReg->HPan = 0;
+ mt9v111_device.ifpReg->HZoom = MT9V111_MAX_WIDTH;
+ mt9v111_device.ifpReg->HSize = MT9V111_MAX_WIDTH;
+ mt9v111_device.ifpReg->VPan = 0;
+ mt9v111_device.ifpReg->VZoom = MT9V111_MAX_HEIGHT;
+ mt9v111_device.ifpReg->VSize = MT9V111_MAX_HEIGHT;
+}
+
+/*!
+ * mt9v111 sensor set saturtionn
+ *
+ * @param saturation int
+
+ * @return Error code of 0.
+ */
+static int mt9v111_set_saturation(int saturation)
+{
+ u8 reg;
+ u16 data;
+ pr_debug("In mt9v111_set_saturation(%d)\n",
+ saturation);
+
+ switch (saturation) {
+ case 150:
+ mt9v111_device.ifpReg->awbSpeed = 0x6D14;
+ break;
+ case 100:
+ mt9v111_device.ifpReg->awbSpeed = 0x4514;
+ break;
+ case 75:
+ mt9v111_device.ifpReg->awbSpeed = 0x4D14;
+ break;
+ case 50:
+ mt9v111_device.ifpReg->awbSpeed = 0x5514;
+ break;
+ case 37:
+ mt9v111_device.ifpReg->awbSpeed = 0x5D14;
+ break;
+ case 25:
+ mt9v111_device.ifpReg->awbSpeed = 0x6514;
+ break;
+ default:
+ mt9v111_device.ifpReg->awbSpeed = 0x4514;
+ break;
+ }
+
+ reg = MT9V111I_ADDR_SPACE_SEL;
+ data = mt9v111_device.ifpReg->addrSpaceSel;
+ mt9v111_write_reg(reg, data);
+
+ /* Operation Mode Control */
+ reg = MT9V111I_AWB_SPEED;
+ data = mt9v111_device.ifpReg->awbSpeed;
+ mt9v111_write_reg(reg, data);
+
+ return 0;
+}
+
+/*!
+ * mt9v111 sensor set Auto Exposure measurement window mode configuration
+ *
+ * @param ae_mode int
+ * @return Error code of 0 (no Error)
+ */
+static int mt9v111_set_ae_mode(int ae_mode)
+{
+ u8 reg;
+ u16 data;
+
+ pr_debug("In mt9v111_set_ae_mode(%d)\n",
+ ae_mode);
+
+ /* Currently this driver only supports auto and manual exposure
+ * modes. */
+ if ((ae_mode > 1) || (ae_mode << 0))
+ return -EPERM;
+
+ /*
+ * The auto exposure is set in bit 14.
+ * Other values are set for:
+ * -on the fly defect correction is on (bit 13).
+ * -aperature correction knee enabled (bit 12).
+ * -ITU_R BT656 synchronization codes are embedded in the image (bit 7)
+ * -AE measurement window is weighted sum of large and center windows
+ * (bits 2-3).
+ * -auto white balance is on (bit 1).
+ * -normal color processing (bit 4 = 0).
+ */
+ /* V4L2_EXPOSURE_AUTO = 0; needs register setting of 0x708E */
+ /* V4L2_EXPOSURE_MANUAL = 1 needs register setting of 0x308E */
+ mt9v111_device.ifpReg->modeControl &= 0x3fff;
+ mt9v111_device.ifpReg->modeControl |= (ae_mode & 0x03) << 14;
+ mt9v111_data.ae_mode = ae_mode;
+
+ reg = MT9V111I_ADDR_SPACE_SEL;
+ data = mt9v111_device.ifpReg->addrSpaceSel;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111I_MODE_CONTROL;
+ data = mt9v111_device.ifpReg->modeControl;
+ mt9v111_write_reg(reg, data);
+
+ return 0;
+}
+
+/*!
+ * mt9v111 sensor get AE measurement window mode configuration
+ *
+ * @param ae_mode int *
+ * @return None
+ */
+static void mt9v111_get_ae_mode(int *ae_mode)
+{
+ pr_debug("In mt9v111_get_ae_mode(%d)\n", *ae_mode);
+
+ if (ae_mode != NULL) {
+ *ae_mode = (mt9v111_device.ifpReg->modeControl & 0xc) >> 2;
+ }
+}
+
+#ifdef MT9V111_DEBUG
+/*!
+ * Set sensor to test mode, which will generate test pattern.
+ *
+ * @return none
+ */
+static void mt9v111_test_pattern(bool flag)
+{
+ u16 data;
+
+ /* switch to sensor registers */
+ mt9v111_write_reg(MT9V111I_ADDR_SPACE_SEL, MT9V111I_SEL_SCA);
+
+ if (flag == true) {
+ testpattern = MT9V111S_OUTCTRL_TEST_MODE;
+
+ data = mt9v111_read_reg(MT9V111S_ROW_NOISE_CTRL) & 0xBF;
+ mt9v111_write_reg(MT9V111S_ROW_NOISE_CTRL, data);
+
+ mt9v111_write_reg(MT9V111S_TEST_DATA, 0);
+
+ /* changes take effect */
+ data = MT9V111S_OUTCTRL_CHIP_ENABLE | testpattern | 0x3000;
+ mt9v111_write_reg(MT9V111S_OUTPUT_CTRL, data);
+ } else {
+ testpattern = 0;
+
+ data = mt9v111_read_reg(MT9V111S_ROW_NOISE_CTRL) | 0x40;
+ mt9v111_write_reg(MT9V111S_ROW_NOISE_CTRL, data);
+
+ /* changes take effect */
+ data = MT9V111S_OUTCTRL_CHIP_ENABLE | testpattern | 0x3000;
+ mt9v111_write_reg(MT9V111S_OUTPUT_CTRL, data);
+ }
+}
+#endif
+
+
+/* --------------- IOCTL functions from v4l2_int_ioctl_desc --------------- */
+
+/*!
+ * ioctl_g_ifparm - V4L2 sensor interface handler for vidioc_int_g_ifparm_num
+ * s: pointer to standard V4L2 device structure
+ * p: pointer to standard V4L2 vidioc_int_g_ifparm_num ioctl structure
+ *
+ * Gets slave interface parameters.
+ * Calculates the required xclk value to support the requested
+ * clock parameters in p. This value is returned in the p
+ * parameter.
+ *
+ * vidioc_int_g_ifparm returns platform-specific information about the
+ * interface settings used by the sensor.
+ *
+ * Given the image capture format in pix, the nominal frame period in
+ * timeperframe, calculate the required xclk frequency.
+ *
+ * Called on open.
+ */
+static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p)
+{
+ pr_debug("In mt9v111:ioctl_g_ifparm\n");
+
+ if (s == NULL) {
+ pr_err(" ERROR!! no slave device set!\n");
+ return -1;
+ }
+
+ memset(p, 0, sizeof(*p));
+ p->u.bt656.clock_curr = MT9V111_MCLK;
+ p->if_type = V4L2_IF_TYPE_BT656;
+ p->u.bt656.mode = V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT;
+ p->u.bt656.clock_min = MT9V111_CLK_MIN;
+ p->u.bt656.clock_max = MT9V111_CLK_MAX;
+
+ return 0;
+}
+
+/*!
+ * Sets the camera power.
+ *
+ * s pointer to the camera device
+ * on if 1, power is to be turned on. 0 means power is to be turned off
+ *
+ * ioctl_s_power - V4L2 sensor interface handler for vidioc_int_s_power_num
+ * @s: pointer to standard V4L2 device structure
+ * @on: power state to which device is to be set
+ *
+ * Sets devices power state to requrested state, if possible.
+ * This is called on suspend and resume.
+ */
+static int ioctl_s_power(struct v4l2_int_device *s, int on)
+{
+ struct sensor *sensor = s->priv;
+
+ pr_debug("In mt9v111:ioctl_s_power\n");
+
+ sensor->on = on;
+
+ if (on)
+ gpio_sensor_active();
+ else
+ gpio_sensor_inactive();
+
+ return 0;
+}
+
+/*!
+ * ioctl_g_parm - V4L2 sensor interface handler for VIDIOC_G_PARM ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure
+ *
+ * Returns the sensor's video CAPTURE parameters.
+ */
+static int ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)
+{
+ int ret = 0;
+ struct v4l2_captureparm *cparm = &a->parm.capture;
+ /* s->priv points to mt9v111_data */
+
+ pr_debug("In mt9v111:ioctl_g_parm\n");
+
+ switch (a->type) {
+ /* This is the only case currently handled. */
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ pr_debug(" type is V4L2_BUF_TYPE_VIDEO_CAPTURE\n");
+ memset(a, 0, sizeof(*a));
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ cparm->capability = mt9v111_data.streamcap.capability;
+ cparm->timeperframe =
+ mt9v111_data.streamcap.timeperframe;
+ cparm->capturemode = mt9v111_data.streamcap.capturemode;
+ ret = 0;
+ break;
+
+ /* These are all the possible cases. */
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ pr_err(" type is not V4L2_BUF_TYPE_VIDEO_CAPTURE " \
+ "but %d\n", a->type);
+ ret = -EINVAL;
+ break;
+
+ default:
+ pr_err(" type is unknown - %d\n", a->type);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+/*!
+ * ioctl_s_parm - V4L2 sensor interface handler for VIDIOC_S_PARM ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure
+ *
+ * Configures the sensor to use the input parameters, if possible. If
+ * not possible, reverts to the old parameters and returns the
+ * appropriate error code.
+ */
+static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)
+{
+ int ret = 0;
+ struct v4l2_captureparm *cparm = &a->parm.capture;
+ /* s->priv points to mt9v111_data */
+
+ pr_debug("In mt9v111:ioctl_s_parm\n");
+
+ switch (a->type) {
+ /* This is the only case currently handled. */
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ pr_debug(" type is V4L2_BUF_TYPE_VIDEO_CAPTURE\n");
+
+ /* Check that the new frame rate is allowed.
+ * Changing the frame rate is not allowed on this
+ *camera. */
+ if (cparm->timeperframe.denominator !=
+ mt9v111_data.streamcap.timeperframe.denominator) {
+ pr_err("ERROR: mt9v111: ioctl_s_parm: " \
+ "This camera does not allow frame rate "
+ "changes.\n");
+ ret = -EINVAL;
+ } else {
+ mt9v111_data.streamcap.timeperframe =
+ cparm->timeperframe;
+ /* Call any camera functions to match settings. */
+ }
+
+ /* Check that new capture mode is supported. */
+ if ((cparm->capturemode != 0) &&
+ !(cparm->capturemode & V4L2_MODE_HIGHQUALITY)) {
+ pr_err("ERROR: mt9v111: ioctl_s_parm: " \
+ "unsupported capture mode\n");
+ ret = -EINVAL;
+ } else {
+ mt9v111_data.streamcap.capturemode =
+ cparm->capturemode;
+ /* Call any camera functions to match settings. */
+ /* Right now this camera only supports 1 mode. */
+ }
+ break;
+
+ /* These are all the possible cases. */
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ pr_err(" type is not V4L2_BUF_TYPE_VIDEO_CAPTURE " \
+ "but %d\n", a->type);
+ ret = -EINVAL;
+ break;
+
+ default:
+ pr_err(" type is unknown - %d\n", a->type);
+ ret = -EINVAL;
+ break;
+ }
+
+ return 0;
+}
+
+/*!
+ * ioctl_g_fmt_cap - V4L2 sensor interface handler for ioctl_g_fmt_cap
+ * @s: pointer to standard V4L2 device structure
+ * @f: pointer to standard V4L2 v4l2_format structure
+ *
+ * Returns the sensor's current pixel format in the v4l2_format
+ * parameter.
+ */
+static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f)
+{
+ struct sensor *sensor = s->priv;
+ /* s->priv points to mt9v111_data */
+
+ pr_debug("In mt9v111:ioctl_g_fmt_cap.\n");
+ pr_debug(" Returning size of %dx%d\n",
+ sensor->pix.width, sensor->pix.height);
+
+ f->fmt.pix = sensor->pix;
+
+ return 0;
+}
+
+/*!
+ * ioctl_queryctrl - V4L2 sensor interface handler for VIDIOC_QUERYCTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @qc: standard V4L2 VIDIOC_QUERYCTRL ioctl structure
+ *
+ * If the requested control is supported, returns the control information
+ * from the video_control[] array. Otherwise, returns -EINVAL if the
+ * control is not supported.
+ */
+static int ioctl_queryctrl(struct v4l2_int_device *s, struct v4l2_queryctrl *qc)
+{
+ pr_debug("In mt9v111:ioctl_queryctrl\n");
+
+ return 0;
+}
+
+/*!
+ * ioctl_g_ctrl - V4L2 sensor interface handler for VIDIOC_G_CTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @vc: standard V4L2 VIDIOC_G_CTRL ioctl structure
+ *
+ * If the requested control is supported, returns the control's current
+ * value from the video_control[] array. Otherwise, returns -EINVAL
+ * if the control is not supported.
+ */
+static int ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc)
+{
+ pr_debug("In mt9v111:ioctl_g_ctrl\n");
+
+ switch (vc->id) {
+ case V4L2_CID_BRIGHTNESS:
+ pr_debug(" V4L2_CID_BRIGHTNESS\n");
+ vc->value = mt9v111_data.brightness;
+ break;
+ case V4L2_CID_CONTRAST:
+ pr_debug(" V4L2_CID_CONTRAST\n");
+ vc->value = mt9v111_data.contrast;
+ break;
+ case V4L2_CID_SATURATION:
+ pr_debug(" V4L2_CID_SATURATION\n");
+ vc->value = mt9v111_data.saturation;
+ break;
+ case V4L2_CID_HUE:
+ pr_debug(" V4L2_CID_HUE\n");
+ vc->value = mt9v111_data.hue;
+ break;
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ pr_debug(
+ " V4L2_CID_AUTO_WHITE_BALANCE\n");
+ vc->value = 0;
+ break;
+ case V4L2_CID_DO_WHITE_BALANCE:
+ pr_debug(
+ " V4L2_CID_DO_WHITE_BALANCE\n");
+ vc->value = 0;
+ break;
+ case V4L2_CID_RED_BALANCE:
+ pr_debug(" V4L2_CID_RED_BALANCE\n");
+ vc->value = mt9v111_data.red;
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ pr_debug(" V4L2_CID_BLUE_BALANCE\n");
+ vc->value = mt9v111_data.blue;
+ break;
+ case V4L2_CID_GAMMA:
+ pr_debug(" V4L2_CID_GAMMA\n");
+ vc->value = 0;
+ break;
+ case V4L2_CID_EXPOSURE:
+ pr_debug(" V4L2_CID_EXPOSURE\n");
+ vc->value = mt9v111_data.ae_mode;
+ break;
+ case V4L2_CID_AUTOGAIN:
+ pr_debug(" V4L2_CID_AUTOGAIN\n");
+ vc->value = 0;
+ break;
+ case V4L2_CID_GAIN:
+ pr_debug(" V4L2_CID_GAIN\n");
+ vc->value = 0;
+ break;
+ case V4L2_CID_HFLIP:
+ pr_debug(" V4L2_CID_HFLIP\n");
+ vc->value = 0;
+ break;
+ case V4L2_CID_VFLIP:
+ pr_debug(" V4L2_CID_VFLIP\n");
+ vc->value = 0;
+ break;
+ default:
+ pr_debug(" Default case\n");
+ return -EPERM;
+ break;
+ }
+
+ return 0;
+}
+
+/*!
+ * ioctl_s_ctrl - V4L2 sensor interface handler for VIDIOC_S_CTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @vc: standard V4L2 VIDIOC_S_CTRL ioctl structure
+ *
+ * If the requested control is supported, sets the control's current
+ * value in HW (and updates the video_control[] array). Otherwise,
+ * returns -EINVAL if the control is not supported.
+ */
+static int ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc)
+{
+ int retval = 0;
+
+ pr_debug("In mt9v111:ioctl_s_ctrl %d\n",
+ vc->id);
+
+ switch (vc->id) {
+ case V4L2_CID_BRIGHTNESS:
+ pr_debug(" V4L2_CID_BRIGHTNESS\n");
+ break;
+ case V4L2_CID_CONTRAST:
+ pr_debug(" V4L2_CID_CONTRAST\n");
+ break;
+ case V4L2_CID_SATURATION:
+ pr_debug(" V4L2_CID_SATURATION\n");
+ retval = mt9v111_set_saturation(vc->value);
+ break;
+ case V4L2_CID_HUE:
+ pr_debug(" V4L2_CID_HUE\n");
+ break;
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ pr_debug(
+ " V4L2_CID_AUTO_WHITE_BALANCE\n");
+ break;
+ case V4L2_CID_DO_WHITE_BALANCE:
+ pr_debug(
+ " V4L2_CID_DO_WHITE_BALANCE\n");
+ break;
+ case V4L2_CID_RED_BALANCE:
+ pr_debug(" V4L2_CID_RED_BALANCE\n");
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ pr_debug(" V4L2_CID_BLUE_BALANCE\n");
+ break;
+ case V4L2_CID_GAMMA:
+ pr_debug(" V4L2_CID_GAMMA\n");
+ break;
+ case V4L2_CID_EXPOSURE:
+ pr_debug(" V4L2_CID_EXPOSURE\n");
+ retval = mt9v111_set_ae_mode(vc->value);
+ break;
+ case V4L2_CID_AUTOGAIN:
+ pr_debug(" V4L2_CID_AUTOGAIN\n");
+ break;
+ case V4L2_CID_GAIN:
+ pr_debug(" V4L2_CID_GAIN\n");
+ break;
+ case V4L2_CID_HFLIP:
+ pr_debug(" V4L2_CID_HFLIP\n");
+ break;
+ case V4L2_CID_VFLIP:
+ pr_debug(" V4L2_CID_VFLIP\n");
+ break;
+ default:
+ pr_debug(" Default case\n");
+ retval = -EPERM;
+ break;
+ }
+
+ return retval;
+}
+
+/*!
+ * ioctl_init - V4L2 sensor interface handler for VIDIOC_INT_INIT
+ * @s: pointer to standard V4L2 device structure
+ */
+static int ioctl_init(struct v4l2_int_device *s)
+{
+ pr_debug("In mt9v111:ioctl_init\n");
+
+ return 0;
+}
+
+/*!
+ * ioctl_dev_init - V4L2 sensor interface handler for vidioc_int_dev_init_num
+ * @s: pointer to standard V4L2 device structure
+ *
+ * Initialise the device when slave attaches to the master.
+ */
+static int ioctl_dev_init(struct v4l2_int_device *s)
+{
+ uint32_t clock_rate = MT9V111_MCLK;
+
+ pr_debug("In mt9v111:ioctl_dev_init\n");
+
+ gpio_sensor_active();
+
+ set_mclk_rate(&clock_rate);
+ mt9v111_rate_cal(&reset_frame_rate, clock_rate);
+ mt9v111_sensor_lib(mt9v111_device.coreReg, mt9v111_device.ifpReg);
+
+ return 0;
+}
+
+/*!
+ * This structure defines all the ioctls for this module and links them to the
+ * enumeration.
+ */
+static struct v4l2_int_ioctl_desc mt9v111_ioctl_desc[] = {
+
+ {vidioc_int_dev_init_num, (v4l2_int_ioctl_func *)ioctl_dev_init},
+
+ /*!
+ * Delinitialise the dev. at slave detach.
+ * The complement of ioctl_dev_init.
+ */
+/* {vidioc_int_dev_exit_num, (v4l2_int_ioctl_func *) ioctl_dev_exit}, */
+
+ {vidioc_int_s_power_num, (v4l2_int_ioctl_func *) ioctl_s_power},
+ {vidioc_int_g_ifparm_num, (v4l2_int_ioctl_func *) ioctl_g_ifparm},
+/* {vidioc_int_g_needs_reset_num,
+ (v4l2_int_ioctl_func *) ioctl_g_needs_reset}, */
+/* {vidioc_int_reset_num, (v4l2_int_ioctl_func *) ioctl_reset}, */
+ {vidioc_int_init_num, (v4l2_int_ioctl_func *) ioctl_init},
+
+ /*!
+ * VIDIOC_ENUM_FMT ioctl for the CAPTURE buffer type.
+ */
+/* {vidioc_int_enum_fmt_cap_num,
+ (v4l2_int_ioctl_func *) ioctl_enum_fmt_cap}, */
+
+ /*!
+ * VIDIOC_TRY_FMT ioctl for the CAPTURE buffer type.
+ * This ioctl is used to negotiate the image capture size and
+ * pixel format without actually making it take effect.
+ */
+/* {vidioc_int_try_fmt_cap_num,
+ (v4l2_int_ioctl_func *) ioctl_try_fmt_cap}, */
+
+ {vidioc_int_g_fmt_cap_num, (v4l2_int_ioctl_func *) ioctl_g_fmt_cap},
+
+ /*!
+ * If the requested format is supported, configures the HW to use that
+ * format, returns error code if format not supported or HW can't be
+ * correctly configured.
+ */
+/* {vidioc_int_s_fmt_cap_num, (v4l2_int_ioctl_func *)ioctl_s_fmt_cap}, */
+
+ {vidioc_int_g_parm_num, (v4l2_int_ioctl_func *) ioctl_g_parm},
+ {vidioc_int_s_parm_num, (v4l2_int_ioctl_func *) ioctl_s_parm},
+/* {vidioc_int_queryctrl_num, (v4l2_int_ioctl_func *) ioctl_queryctrl}, */
+ {vidioc_int_g_ctrl_num, (v4l2_int_ioctl_func *) ioctl_g_ctrl},
+ {vidioc_int_s_ctrl_num, (v4l2_int_ioctl_func *) ioctl_s_ctrl},
+};
+
+static struct v4l2_int_slave mt9v111_slave = {
+ .ioctls = mt9v111_ioctl_desc,
+ .num_ioctls = ARRAY_SIZE(mt9v111_ioctl_desc),
+};
+
+static struct v4l2_int_device mt9v111_int_device = {
+ .module = THIS_MODULE,
+ .name = "mt9v111",
+ .type = v4l2_int_type_slave,
+ .u = {
+ .slave = &mt9v111_slave,
+ },
+};
+
+/*!
+ * mt9v111 I2C probe function
+ * Function set in i2c_driver struct.
+ * Called by insmod mt9v111_camera.ko.
+ *
+ * @return Error code indicating success or failure
+ */
+static int mt9v111_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int retval;
+
+ pr_debug("In mt9v111_probe device id is %s\n", id->name);
+
+ /* Set initial values for the sensor struct. */
+ memset(&mt9v111_data, 0, sizeof(mt9v111_data));
+ mt9v111_data.i2c_client = client;
+ pr_debug(" client name is %s\n", client->name);
+ mt9v111_data.pix.pixelformat = V4L2_PIX_FMT_UYVY;
+ mt9v111_data.pix.width = MT9V111_MAX_WIDTH;
+ mt9v111_data.pix.height = MT9V111_MAX_HEIGHT;
+ mt9v111_data.streamcap.capability = 0; /* No higher resolution or frame
+ * frame rate changes supported.
+ */
+ mt9v111_data.streamcap.timeperframe.denominator = MT9V111_FRAME_RATE;
+ mt9v111_data.streamcap.timeperframe.numerator = 1;
+
+ mt9v111_int_device.priv = &mt9v111_data;
+
+ pr_debug(" type is %d (expect %d)\n",
+ mt9v111_int_device.type, v4l2_int_type_slave);
+ pr_debug(" num ioctls is %d\n",
+ mt9v111_int_device.u.slave->num_ioctls);
+
+ /* This function attaches this structure to the /dev/video0 device.
+ * The pointer in priv points to the mt9v111_data structure here.*/
+ retval = v4l2_int_device_register(&mt9v111_int_device);
+
+ return retval;
+}
+
+/*!
+ * Function set in i2c_driver struct.
+ * Called on rmmod mt9v111_camera.ko
+ */
+static int mt9v111_remove(struct i2c_client *client)
+{
+ pr_debug("In mt9v111_remove\n");
+
+ v4l2_int_device_unregister(&mt9v111_int_device);
+ return 0;
+}
+
+/*!
+ * MT9V111 init function.
+ * Called by insmod mt9v111_camera.ko.
+ *
+ * @return Error code indicating success or failure
+ */
+static __init int mt9v111_init(void)
+{
+ u8 err;
+
+ pr_debug("In mt9v111_init\n");
+
+ /* Allocate memory for state structures. */
+ mt9v111_device.coreReg = (mt9v111_coreReg *)
+ kmalloc(sizeof(mt9v111_coreReg), GFP_KERNEL);
+ if (!mt9v111_device.coreReg)
+ return -1;
+ memset(mt9v111_device.coreReg, 0, sizeof(mt9v111_coreReg));
+
+ mt9v111_device.ifpReg = (mt9v111_IFPReg *)
+ kmalloc(sizeof(mt9v111_IFPReg), GFP_KERNEL);
+ if (!mt9v111_device.ifpReg) {
+ kfree(mt9v111_device.coreReg);
+ mt9v111_device.coreReg = NULL;
+ return -1;
+ }
+ memset(mt9v111_device.ifpReg, 0, sizeof(mt9v111_IFPReg));
+
+ /* Set contents of the just created structures. */
+ mt9v111_config();
+
+ /* Tells the i2c driver what functions to call for this driver. */
+ err = i2c_add_driver(&mt9v111_i2c_driver);
+ if (err != 0)
+ pr_err("%s:driver registration failed, error=%d \n",
+ __func__, err);
+
+ return err;
+}
+
+/*!
+ * MT9V111 cleanup function.
+ * Called on rmmod mt9v111_camera.ko
+ *
+ * @return Error code indicating success or failure
+ */
+static void __exit mt9v111_clean(void)
+{
+ pr_debug("In mt9v111_clean()\n");
+
+ i2c_del_driver(&mt9v111_i2c_driver);
+ gpio_sensor_inactive();
+
+ if (mt9v111_device.coreReg) {
+ kfree(mt9v111_device.coreReg);
+ mt9v111_device.coreReg = NULL;
+ }
+
+ if (mt9v111_device.ifpReg) {
+ kfree(mt9v111_device.ifpReg);
+ mt9v111_device.ifpReg = NULL;
+ }
+}
+
+module_init(mt9v111_init);
+module_exit(mt9v111_clean);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Mt9v111 Camera Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxc/capture/mt9v111.h b/drivers/media/video/mxc/capture/mt9v111.h
new file mode 100644
index 000000000000..cf38cec4757c
--- /dev/null
+++ b/drivers/media/video/mxc/capture/mt9v111.h
@@ -0,0 +1,431 @@
+/*
+ * Copyright 2004-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @defgroup Camera Sensor Drivers
+ */
+
+/*!
+ * @file mt9v111.h
+ *
+ * @brief MT9V111 Camera Header file
+ *
+ * This header file contains defines and structures for the iMagic mi8012
+ * aka the Micron mt9v111 camera.
+ *
+ * @ingroup Camera
+ */
+
+#ifndef MT9V111_H_
+#define MT9V111_H_
+
+/*!
+ * Basic camera values
+ */
+#define MT9V111_FRAME_RATE 30
+#define MT9V111_MCLK 27000000 /* Desired clock rate */
+#define MT9V111_CLK_MIN 12000000 /* This clock rate yields 15 fps */
+#define MT9V111_CLK_MAX 27000000
+#define MT9V111_MAX_WIDTH 640 /* Max width for this camera */
+#define MT9V111_MAX_HEIGHT 480 /* Max height for this camera */
+
+/*!
+ * mt9v111 IFP REGISTER BANK MAP
+ */
+#define MT9V111I_ADDR_SPACE_SEL 0x1
+#define MT9V111I_BASE_MAXTRIX_SIGN 0x2
+#define MT9V111I_BASE_MAXTRIX_SCALE15 0x3
+#define MT9V111I_BASE_MAXTRIX_SCALE69 0x4
+#define MT9V111I_APERTURE_GAIN 0x5
+#define MT9V111I_MODE_CONTROL 0x6
+#define MT9V111I_SOFT_RESET 0x7
+#define MT9V111I_FORMAT_CONTROL 0x8
+#define MT9V111I_BASE_MATRIX_CFK1 0x9
+#define MT9V111I_BASE_MATRIX_CFK2 0xa
+#define MT9V111I_BASE_MATRIX_CFK3 0xb
+#define MT9V111I_BASE_MATRIX_CFK4 0xc
+#define MT9V111I_BASE_MATRIX_CFK5 0xd
+#define MT9V111I_BASE_MATRIX_CFK6 0xe
+#define MT9V111I_BASE_MATRIX_CFK7 0xf
+#define MT9V111I_BASE_MATRIX_CFK8 0x10
+#define MT9V111I_BASE_MATRIX_CFK9 0x11
+#define MT9V111I_AWB_POSITION 0x12
+#define MT9V111I_AWB_RED_GAIN 0x13
+#define MT9V111I_AWB_BLUE_GAIN 0x14
+#define MT9V111I_DELTA_MATRIX_CF_SIGN 0x15
+#define MT9V111I_DELTA_MATRIX_CF_D1 0x16
+#define MT9V111I_DELTA_MATRIX_CF_D2 0x17
+#define MT9V111I_DELTA_MATRIX_CF_D3 0x18
+#define MT9V111I_DELTA_MATRIX_CF_D4 0x19
+#define MT9V111I_DELTA_MATRIX_CF_D5 0x1a
+#define MT9V111I_DELTA_MATRIX_CF_D6 0x1b
+#define MT9V111I_DELTA_MATRIX_CF_D7 0x1c
+#define MT9V111I_DELTA_MATRIX_CF_D8 0x1d
+#define MT9V111I_DELTA_MATRIX_CF_D9 0x1e
+#define MT9V111I_LUMINANCE_LIMIT_WB 0x20
+#define MT9V111I_RBG_MANUUAL_WB 0x21
+#define MT9V111I_AWB_RED_LIMIT 0x22
+#define MT9V111I_AWB_BLUE_LIMIT 0x23
+#define MT9V111I_MATRIX_ADJUST_LIMIT 0x24
+#define MT9V111I_AWB_SPEED 0x25
+#define MT9V111I_H_BOUND_AE 0x26
+#define MT9V111I_V_BOUND_AE 0x27
+#define MT9V111I_H_BOUND_AE_CEN_WIN 0x2b
+#define MT9V111I_V_BOUND_AE_CEN_WIN 0x2c
+#define MT9V111I_BOUND_AWB_WIN 0x2d
+#define MT9V111I_AE_PRECISION_TARGET 0x2e
+#define MT9V111I_AE_SPEED 0x2f
+#define MT9V111I_RED_AWB_MEASURE 0x30
+#define MT9V111I_LUMA_AWB_MEASURE 0x31
+#define MT9V111I_BLUE_AWB_MEASURE 0x32
+#define MT9V111I_LIMIT_SHARP_SATU_CTRL 0x33
+#define MT9V111I_LUMA_OFFSET 0x34
+#define MT9V111I_CLIP_LIMIT_OUTPUT_LUMI 0x35
+#define MT9V111I_GAIN_LIMIT_AE 0x36
+#define MT9V111I_SHUTTER_WIDTH_LIMIT_AE 0x37
+#define MT9V111I_UPPER_SHUTTER_DELAY_LIM 0x39
+#define MT9V111I_OUTPUT_FORMAT_CTRL2 0x3a
+#define MT9V111I_IPF_BLACK_LEVEL_SUB 0x3b
+#define MT9V111I_IPF_BLACK_LEVEL_ADD 0x3c
+#define MT9V111I_ADC_LIMIT_AE_ADJ 0x3d
+#define MT9V111I_GAIN_THRE_CCAM_ADJ 0x3e
+#define MT9V111I_LINEAR_AE 0x3f
+#define MT9V111I_THRESHOLD_EDGE_DEFECT 0x47
+#define MT9V111I_LUMA_SUM_MEASURE 0x4c
+#define MT9V111I_TIME_ADV_SUM_LUMA 0x4d
+#define MT9V111I_MOTION 0x52
+#define MT9V111I_GAMMA_KNEE_Y12 0x53
+#define MT9V111I_GAMMA_KNEE_Y34 0x54
+#define MT9V111I_GAMMA_KNEE_Y56 0x55
+#define MT9V111I_GAMMA_KNEE_Y78 0x56
+#define MT9V111I_GAMMA_KNEE_Y90 0x57
+#define MT9V111I_GAMMA_VALUE_Y0 0x58
+#define MT9V111I_SHUTTER_60 0x59
+#define MT9V111I_SEARCH_FLICK_60 0x5c
+#define MT9V111I_RATIO_IMAGE_GAIN_BASE 0x5e
+#define MT9V111I_RATIO_IMAGE_GAIN_DELTA 0x5f
+#define MT9V111I_SIGN_VALUE_REG5F 0x60
+#define MT9V111I_AE_GAIN 0x62
+#define MT9V111I_MAX_GAIN_AE 0x67
+#define MT9V111I_LENS_CORRECT_CTRL 0x80
+#define MT9V111I_SHADING_PARAMETER1 0x81
+#define MT9V111I_SHADING_PARAMETER2 0x82
+#define MT9V111I_SHADING_PARAMETER3 0x83
+#define MT9V111I_SHADING_PARAMETER4 0x84
+#define MT9V111I_SHADING_PARAMETER5 0x85
+#define MT9V111I_SHADING_PARAMETER6 0x86
+#define MT9V111I_SHADING_PARAMETER7 0x87
+#define MT9V111I_SHADING_PARAMETER8 0x88
+#define MT9V111I_SHADING_PARAMETER9 0x89
+#define MT9V111I_SHADING_PARAMETER10 0x8A
+#define MT9V111I_SHADING_PARAMETER11 0x8B
+#define MT9V111I_SHADING_PARAMETER12 0x8C
+#define MT9V111I_SHADING_PARAMETER13 0x8D
+#define MT9V111I_SHADING_PARAMETER14 0x8E
+#define MT9V111I_SHADING_PARAMETER15 0x8F
+#define MT9V111I_SHADING_PARAMETER16 0x90
+#define MT9V111I_SHADING_PARAMETER17 0x91
+#define MT9V111I_SHADING_PARAMETER18 0x92
+#define MT9V111I_SHADING_PARAMETER19 0x93
+#define MT9V111I_SHADING_PARAMETER20 0x94
+#define MT9V111I_SHADING_PARAMETER21 0x95
+#define MT9V111i_FLASH_CTRL 0x98
+#define MT9V111i_LINE_COUNTER 0x99
+#define MT9V111i_FRAME_COUNTER 0x9A
+#define MT9V111i_H_PAN 0xA5
+#define MT9V111i_H_ZOOM 0xA6
+#define MT9V111i_H_SIZE 0xA7
+#define MT9V111i_V_PAN 0xA8
+#define MT9V111i_V_ZOOM 0xA9
+#define MT9V111i_V_SIZE 0xAA
+
+#define MT9V111I_SEL_IFP 0x1
+#define MT9V111I_SEL_SCA 0x4
+#define MT9V111I_FC_RGB_OR_YUV 0x1000
+
+/*!
+ * Mt9v111 SENSOR CORE REGISTER BANK MAP
+ */
+#define MT9V111S_ADDR_SPACE_SEL 0x1
+#define MT9V111S_COLUMN_START 0x2
+#define MT9V111S_WIN_HEIGHT 0x3
+#define MT9V111S_WIN_WIDTH 0x4
+#define MT9V111S_HOR_BLANKING 0x5
+#define MT9V111S_VER_BLANKING 0x6
+#define MT9V111S_OUTPUT_CTRL 0x7
+#define MT9V111S_ROW_START 0x8
+#define MT9V111S_SHUTTER_WIDTH 0x9
+#define MT9V111S_PIXEL_CLOCK_SPEED 0xa
+#define MT9V111S_RESTART 0xb
+#define MT9V111S_SHUTTER_DELAY 0xc
+#define MT9V111S_RESET 0xd
+#define MT9V111S_COLUMN_START_IN_ZOOM 0x12
+#define MT9V111S_ROW_START_IN_ZOOM 0x13
+#define MT9V111S_DIGITAL_ZOOM 0x1e
+#define MT9V111S_READ_MODE 0x20
+#define MT9V111S_DAC_CTRL 0x27
+#define MT9V111S_GREEN1_GAIN 0x2b
+#define MT9V111S_BLUE_GAIN 0x2c
+#define MT9V111S_READ_GAIN 0x2d
+#define MT9V111S_GREEN2_GAIN 0x2e
+#define MT9V111S_ROW_NOISE_CTRL 0x30
+#define MT9V111S_DARK_TARGET_W 0x31
+#define MT9V111S_TEST_DATA 0x32
+#define MT9V111S_GLOBAL_GAIN 0x35
+#define MT9V111S_SENSOR_CORE_VERSION 0x36
+#define MT9V111S_DARK_TARGET_WO 0x37
+#define MT9V111S_VERF_DAC 0x41
+#define MT9V111S_VCM_VCL 0x42
+#define MT9V111S_DISABLE_BYPASS 0x58
+#define MT9V111S_CALIB_MEAN_TEST 0x59
+#define MT9V111S_DARK_G1_AVE 0x5B
+#define MT9V111S_DARK_G2_AVE 0x5C
+#define MT9V111S_DARK_R_AVE 0x5D
+#define MT9V111S_DARK_B_AVE 0x5E
+#define MT9V111S_CAL_THRESHOLD 0x5f
+#define MT9V111S_CAL_G1 0x60
+#define MT9V111S_CAL_G2 0x61
+#define MT9V111S_CAL_CTRL 0x62
+#define MT9V111S_CAL_R 0x63
+#define MT9V111S_CAL_B 0x64
+#define MT9V111S_CHIP_ENABLE 0xF1
+#define MT9V111S_CHIP_VERSION 0xFF
+
+/* OUTPUT_CTRL */
+#define MT9V111S_OUTCTRL_SYNC 0x1
+#define MT9V111S_OUTCTRL_CHIP_ENABLE 0x2
+#define MT9V111S_OUTCTRL_TEST_MODE 0x40
+
+/* READ_MODE */
+#define MT9V111S_RM_NOBADFRAME 0x1
+#define MT9V111S_RM_NODESTRUCT 0x2
+#define MT9V111S_RM_COLUMNSKIP 0x4
+#define MT9V111S_RM_ROWSKIP 0x8
+#define MT9V111S_RM_BOOSTEDRESET 0x1000
+#define MT9V111S_RM_COLUMN_LATE 0x10
+#define MT9V111S_RM_ROW_LATE 0x80
+#define MT9V111S_RM_RIGTH_TO_LEFT 0x4000
+#define MT9V111S_RM_BOTTOM_TO_TOP 0x8000
+
+/*! I2C Slave Address */
+#define MT9V111_I2C_ADDRESS 0x48
+
+/*!
+ * The image resolution enum for the mt9v111 sensor
+ */
+typedef enum {
+ MT9V111_OutputResolution_VGA = 0, /*!< VGA size */
+ MT9V111_OutputResolution_QVGA, /*!< QVGA size */
+ MT9V111_OutputResolution_CIF, /*!< CIF size */
+ MT9V111_OutputResolution_QCIF, /*!< QCIF size */
+ MT9V111_OutputResolution_QQVGA, /*!< QQVGA size */
+ MT9V111_OutputResolution_SXGA /*!< SXGA size */
+} MT9V111_OutputResolution;
+
+enum {
+ MT9V111_WINWIDTH = 0x287,
+ MT9V111_WINWIDTH_DEFAULT = 0x287,
+ MT9V111_WINWIDTH_MIN = 0x9,
+
+ MT9V111_WINHEIGHT = 0x1E7,
+ MT9V111_WINHEIGHT_DEFAULT = 0x1E7,
+
+ MT9V111_HORZBLANK_DEFAULT = 0x26,
+ MT9V111_HORZBLANK_MIN = 0x9,
+ MT9V111_HORZBLANK_MAX = 0x3FF,
+
+ MT9V111_VERTBLANK_DEFAULT = 0x4,
+ MT9V111_VERTBLANK_MIN = 0x3,
+ MT9V111_VERTBLANK_MAX = 0xFFF,
+};
+
+/*!
+ * Mt9v111 Core Register structure.
+ */
+typedef struct {
+ u32 addressSelect; /*!< select address bank for Core Register 0x4 */
+ u32 columnStart; /*!< Starting Column */
+ u32 windowHeight; /*!< Window Height */
+ u32 windowWidth; /*!< Window Width */
+ u32 horizontalBlanking; /*!< Horizontal Blank time, in pixels */
+ u32 verticalBlanking; /*!< Vertical Blank time, in pixels */
+ u32 outputControl; /*!< Register to control sensor output */
+ u32 rowStart; /*!< Starting Row */
+ u32 shutterWidth;
+ u32 pixelClockSpeed; /*!< pixel date rate */
+ u32 restart; /*!< Abandon the readout of current frame */
+ u32 shutterDelay;
+ u32 reset; /*!< reset the sensor to the default mode */
+ u32 zoomColStart; /*!< Column start in the Zoom mode */
+ u32 zomRowStart; /*!< Row start in the Zoom mode */
+ u32 digitalZoom; /*!< 1 means zoom by 2 */
+ u32 readMode; /*!< Readmode: aspects of the readout of the sensor */
+ u32 dACStandbyControl;
+ u32 green1Gain; /*!< Gain Settings */
+ u32 blueGain;
+ u32 redGain;
+ u32 green2Gain;
+ u32 rowNoiseControl;
+ u32 darkTargetwNC;
+ u32 testData; /*!< test mode */
+ u32 globalGain;
+ u32 chipVersion;
+ u32 darkTargetwoNC;
+ u32 vREFDACs;
+ u32 vCMandVCL;
+ u32 disableBypass;
+ u32 calibMeanTest;
+ u32 darkG1average;
+ u32 darkG2average;
+ u32 darkRaverage;
+ u32 darkBaverage;
+ u32 calibThreshold;
+ u32 calibGreen1;
+ u32 calibGreen2;
+ u32 calibControl;
+ u32 calibRed;
+ u32 calibBlue;
+ u32 chipEnable; /*!< Image core Registers written by image flow processor */
+} mt9v111_coreReg;
+
+/*!
+ * Mt9v111 IFP Register structure.
+ */
+typedef struct {
+ u32 addrSpaceSel; /*!< select address bank for Core Register 0x1 */
+ u32 baseMaxtrixSign; /*!< sign of coefficient for base color correction matrix */
+ u32 baseMaxtrixScale15; /*!< scaling of color correction coefficient K1-5 */
+ u32 baseMaxtrixScale69; /*!< scaling of color correction coefficient K6-9 */
+ u32 apertureGain; /*!< sharpening */
+ u32 modeControl; /*!< bit 7 CCIR656 sync codes are embedded in the image */
+ u32 softReset; /*!< Image processing mode: 1 reset mode, 0 operational mode */
+ u32 formatControl; /*!< bit12 1 for RGB565, 0 for YcrCb */
+ u32 baseMatrixCfk1; /*!< K1 Color correction coefficient */
+ u32 baseMatrixCfk2; /*!< K2 Color correction coefficient */
+ u32 baseMatrixCfk3; /*!< K3 Color correction coefficient */
+ u32 baseMatrixCfk4; /*!< K4 Color correction coefficient */
+ u32 baseMatrixCfk5; /*!< K5 Color correction coefficient */
+ u32 baseMatrixCfk6; /*!< K6 Color correction coefficient */
+ u32 baseMatrixCfk7; /*!< K7 Color correction coefficient */
+ u32 baseMatrixCfk8; /*!< K8 Color correction coefficient */
+ u32 baseMatrixCfk9; /*!< K9 Color correction coefficient */
+ u32 awbPosition; /*!< Current position of AWB color correction matrix */
+ u32 awbRedGain; /*!< Current value of AWB red channel gain */
+ u32 awbBlueGain; /*!< Current value of AWB blue channel gain */
+ u32 deltaMatrixCFSign; /*!< Sign of coefficients of delta color correction matrix register */
+ u32 deltaMatrixCFD1; /*!< D1 Delta coefficient */
+ u32 deltaMatrixCFD2; /*!< D2 Delta coefficient */
+ u32 deltaMatrixCFD3; /*!< D3 Delta coefficient */
+ u32 deltaMatrixCFD4; /*!< D4 Delta coefficient */
+ u32 deltaMatrixCFD5; /*!< D5 Delta coefficient */
+ u32 deltaMatrixCFD6; /*!< D6 Delta coefficient */
+ u32 deltaMatrixCFD7; /*!< D7 Delta coefficient */
+ u32 deltaMatrixCFD8; /*!< D8 Delta coefficient */
+ u32 deltaMatrixCFD9; /*!< D9 Delta coefficient */
+ u32 lumLimitWB; /*!< Luminance range of pixels considered in WB statistics */
+ u32 RBGManualWB; /*!< Red and Blue color channel gains for manual white balance */
+ u32 awbRedLimit; /*!< Limits on Red channel gain adjustment through AWB */
+ u32 awbBlueLimit; /*!< Limits on Blue channel gain adjustment through AWB */
+ u32 matrixAdjLimit; /*!< Limits on color correction matrix adjustment through AWB */
+ u32 awbSpeed; /*!< AWB speed and color saturation control */
+ u32 HBoundAE; /*!< Horizontal boundaries of AWB measurement window */
+ u32 VBoundAE; /*!< Vertical boundaries of AWB measurement window */
+ u32 HBoundAECenWin; /*!< Horizontal boundaries of AE measurement window for backlight compensation */
+ u32 VBoundAECenWin; /*!< Vertical boundaries of AE measurement window for backlight compensation */
+ u32 boundAwbWin; /*!< Boundaries of AWB measurement window */
+ u32 AEPrecisionTarget; /*!< Auto exposure target and precision control */
+ u32 AESpeed; /*!< AE speed and sensitivity control register */
+ u32 redAWBMeasure; /*!< Measure of the red channel value used by AWB */
+ u32 lumaAWBMeasure; /*!< Measure of the luminance channel value used by AWB */
+ u32 blueAWBMeasure; /*!< Measure of the blue channel value used by AWB */
+ u32 limitSharpSatuCtrl; /*!< Automatic control of sharpness and color saturation */
+ u32 lumaOffset; /*!< Luminance offset control (brightness control) */
+ u32 clipLimitOutputLumi; /*!< Clipping limits for output luminance */
+ u32 gainLimitAE; /*!< Imager gain limits for AE adjustment */
+ u32 shutterWidthLimitAE; /*!< Shutter width (exposure time) limits for AE adjustment */
+ u32 upperShutterDelayLi; /*!< Upper Shutter Delay Limit */
+ u32 outputFormatCtrl2; /*!< Output Format Control 2
+ 00 = 16-bit RGB565.
+ 01 = 15-bit RGB555.
+ 10 = 12-bit RGB444x.
+ 11 = 12-bit RGBx444. */
+ u32 ipfBlackLevelSub; /*!< IFP black level subtraction */
+ u32 ipfBlackLevelAdd; /*!< IFP black level addition */
+ u32 adcLimitAEAdj; /*!< ADC limits for AE adjustment */
+ u32 agimnThreCamAdj; /*!< Gain threshold for CCM adjustment */
+ u32 linearAE;
+ u32 thresholdEdgeDefect; /*!< Edge threshold for interpolation and defect correction */
+ u32 lumaSumMeasure; /*!< Luma measured by AE engine */
+ u32 timeAdvSumLuma; /*!< Time-averaged luminance value tracked by auto exposure */
+ u32 motion; /*!< 1 when motion is detected */
+ u32 gammaKneeY12; /*!< Gamma knee points Y1 and Y2 */
+ u32 gammaKneeY34; /*!< Gamma knee points Y3 and Y4 */
+ u32 gammaKneeY56; /*!< Gamma knee points Y5 and Y6 */
+ u32 gammaKneeY78; /*!< Gamma knee points Y7 and Y8 */
+ u32 gammaKneeY90; /*!< Gamma knee points Y9 and Y10 */
+ u32 gammaKneeY0; /*!< Gamma knee point Y0 */
+ u32 shutter_width_60;
+ u32 search_flicker_60;
+ u32 ratioImageGainBase;
+ u32 ratioImageGainDelta;
+ u32 signValueReg5F;
+ u32 aeGain;
+ u32 maxGainAE;
+ u32 lensCorrectCtrl;
+ u32 shadingParameter1; /*!< Shade Parameters */
+ u32 shadingParameter2;
+ u32 shadingParameter3;
+ u32 shadingParameter4;
+ u32 shadingParameter5;
+ u32 shadingParameter6;
+ u32 shadingParameter7;
+ u32 shadingParameter8;
+ u32 shadingParameter9;
+ u32 shadingParameter10;
+ u32 shadingParameter11;
+ u32 shadingParameter12;
+ u32 shadingParameter13;
+ u32 shadingParameter14;
+ u32 shadingParameter15;
+ u32 shadingParameter16;
+ u32 shadingParameter17;
+ u32 shadingParameter18;
+ u32 shadingParameter19;
+ u32 shadingParameter20;
+ u32 shadingParameter21;
+ u32 flashCtrl; /*!< Flash control */
+ u32 lineCounter; /*!< Line counter */
+ u32 frameCounter; /*!< Frame counter */
+ u32 HPan; /*!< Horizontal pan in decimation */
+ u32 HZoom; /*!< Horizontal zoom in decimation */
+ u32 HSize; /*!< Horizontal output size iIn decimation */
+ u32 VPan; /*!< Vertical pan in decimation */
+ u32 VZoom; /*!< Vertical zoom in decimation */
+ u32 VSize; /*!< Vertical output size in decimation */
+} mt9v111_IFPReg;
+
+/*!
+ * mt9v111 Config structure
+ */
+typedef struct {
+ mt9v111_coreReg *coreReg; /*!< Sensor Core Register Bank */
+ mt9v111_IFPReg *ifpReg; /*!< IFP Register Bank */
+} mt9v111_conf;
+
+typedef struct {
+ u8 index;
+ u16 width;
+ u16 height;
+} mt9v111_image_format;
+
+#endif /* MT9V111_H_ */
diff --git a/drivers/media/video/mxc/capture/mx27_csi.c b/drivers/media/video/mxc/capture/mx27_csi.c
new file mode 100644
index 000000000000..24fce05be110
--- /dev/null
+++ b/drivers/media/video/mxc/capture/mx27_csi.c
@@ -0,0 +1,333 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file mx27_csi.c
+ *
+ * @brief CMOS Sensor interface functions
+ *
+ * @ingroup CSI
+ */
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <mach/clock.h>
+#include <mach/hardware.h>
+
+#include "mx27_csi.h"
+
+static csi_config_t g_csi_cfg; /* csi hardware configuration */
+static bool gcsi_mclk_on = false;
+static csi_irq_callback_t g_callback = 0;
+static void *g_callback_data = 0;
+static struct clk csi_mclk;
+
+static irqreturn_t csi_irq_handler(int irq, void *data)
+{
+ unsigned long status = __raw_readl(CSI_CSISR);
+
+ __raw_writel(status, CSI_CSISR);
+ if (g_callback)
+ g_callback(g_callback_data, status);
+
+ pr_debug("CSI status = 0x%08lX\n", status);
+
+ return IRQ_HANDLED;
+}
+
+static void csihw_set_config(csi_config_t * cfg)
+{
+ unsigned val = 0;
+
+ /* control reg 1 */
+ val |= cfg->swap16_en ? BIT_SWAP16_EN : 0;
+ val |= cfg->ext_vsync ? BIT_EXT_VSYNC : 0;
+ val |= cfg->eof_int_en ? BIT_EOF_INT_EN : 0;
+ val |= cfg->prp_if_en ? BIT_PRP_IF_EN : 0;
+ val |= cfg->ccir_mode ? BIT_CCIR_MODE : 0;
+ val |= cfg->cof_int_en ? BIT_COF_INT_EN : 0;
+ val |= cfg->sf_or_inten ? BIT_SF_OR_INTEN : 0;
+ val |= cfg->rf_or_inten ? BIT_RF_OR_INTEN : 0;
+ val |= cfg->statff_level << SHIFT_STATFF_LEVEL;
+ val |= cfg->staff_inten ? BIT_STATFF_INTEN : 0;
+ val |= cfg->rxff_level << SHIFT_RXFF_LEVEL;
+ val |= cfg->rxff_inten ? BIT_RXFF_INTEN : 0;
+ val |= cfg->sof_pol ? BIT_SOF_POL : 0;
+ val |= cfg->sof_inten ? BIT_SOF_INTEN : 0;
+ val |= cfg->mclkdiv << SHIFT_MCLKDIV;
+ val |= cfg->hsync_pol ? BIT_HSYNC_POL : 0;
+ val |= cfg->ccir_en ? BIT_CCIR_EN : 0;
+ val |= cfg->mclken ? BIT_MCLKEN : 0;
+ val |= cfg->fcc ? BIT_FCC : 0;
+ val |= cfg->pack_dir ? BIT_PACK_DIR : 0;
+ val |= cfg->gclk_mode ? BIT_GCLK_MODE : 0;
+ val |= cfg->inv_data ? BIT_INV_DATA : 0;
+ val |= cfg->inv_pclk ? BIT_INV_PCLK : 0;
+ val |= cfg->redge ? BIT_REDGE : 0;
+
+ __raw_writel(val, CSI_CSICR1);
+
+ /* control reg 3 */
+ val = 0x0;
+ val |= cfg->csi_sup ? BIT_CSI_SUP : 0;
+ val |= cfg->zero_pack_en ? BIT_ZERO_PACK_EN : 0;
+ val |= cfg->ecc_int_en ? BIT_ECC_INT_EN : 0;
+ val |= cfg->ecc_auto_en ? BIT_ECC_AUTO_EN : 0;
+
+ __raw_writel(val, CSI_CSICR3);
+
+ /* rxfifo counter */
+ __raw_writel(cfg->rxcnt, CSI_CSIRXCNT);
+
+ /* update global config */
+ memcpy(&g_csi_cfg, cfg, sizeof(csi_config_t));
+}
+
+static void csihw_reset_frame_count(void)
+{
+ __raw_writel(__raw_readl(CSI_CSICR3) | BIT_FRMCNT_RST, CSI_CSICR3);
+}
+
+static void csihw_reset(void)
+{
+ csihw_reset_frame_count();
+ __raw_writel(CSICR1_RESET_VAL, CSI_CSICR1);
+ __raw_writel(CSICR2_RESET_VAL, CSI_CSICR2);
+ __raw_writel(CSICR3_RESET_VAL, CSI_CSICR3);
+}
+
+/*!
+ * csi_init_interface
+ * Sets initial values for the CSI registers.
+ * The width and height of the sensor and the actual frame size will be
+ * set to the same values.
+ * @param width Sensor width
+ * @param height Sensor height
+ * @param pixel_fmt pixel format
+ * @param sig csi_signal_cfg_t
+ *
+ * @return 0 for success, -EINVAL for error
+ */
+int32_t csi_init_interface(uint16_t width, uint16_t height,
+ uint32_t pixel_fmt, csi_signal_cfg_t sig)
+{
+ csi_config_t cfg;
+
+ /* Set the CSI_SENS_CONF register remaining fields */
+ cfg.swap16_en = 1;
+ cfg.ext_vsync = sig.ext_vsync;
+ cfg.eof_int_en = 0;
+ cfg.prp_if_en = 1;
+ cfg.ccir_mode = 0;
+ cfg.cof_int_en = 0;
+ cfg.sf_or_inten = 0;
+ cfg.rf_or_inten = 0;
+ cfg.statff_level = 0;
+ cfg.staff_inten = 0;
+ cfg.rxff_level = 2;
+ cfg.rxff_inten = 0;
+ cfg.sof_pol = 1;
+ cfg.sof_inten = 0;
+ cfg.mclkdiv = 0;
+ cfg.hsync_pol = 1;
+ cfg.ccir_en = 0;
+ cfg.mclken = gcsi_mclk_on ? 1 : 0;
+ cfg.fcc = 1;
+ cfg.pack_dir = 0;
+ cfg.gclk_mode = 1;
+ cfg.inv_data = sig.data_pol;
+ cfg.inv_pclk = sig.pixclk_pol;
+ cfg.redge = 1;
+ cfg.csicnt1_rsv = 0;
+
+ /* control reg 3 */
+ cfg.frmcnt = 0;
+ cfg.frame_reset = 0;
+ cfg.csi_sup = 0;
+ cfg.zero_pack_en = 0;
+ cfg.ecc_int_en = 0;
+ cfg.ecc_auto_en = 0;
+
+ csihw_set_config(&cfg);
+
+ return 0;
+}
+
+/*!
+ * csi_enable_prpif
+ * Enable or disable CSI-PrP interface
+ * @param enable Non-zero to enable, zero to disable
+ */
+void csi_enable_prpif(uint32_t enable)
+{
+ if (enable) {
+ g_csi_cfg.prp_if_en = 1;
+ g_csi_cfg.sof_inten = 0;
+ g_csi_cfg.pack_dir = 0;
+ } else {
+ g_csi_cfg.prp_if_en = 0;
+ g_csi_cfg.sof_inten = 1;
+ g_csi_cfg.pack_dir = 1;
+ }
+
+ csihw_set_config(&g_csi_cfg);
+}
+
+/*!
+ * csi_enable_mclk
+ *
+ * @param src enum define which source to control the clk
+ * CSI_MCLK_VF CSI_MCLK_ENC CSI_MCLK_RAW CSI_MCLK_I2C
+ * @param flag true to enable mclk, false to disable mclk
+ * @param wait true to wait 100ms make clock stable, false not wait
+ *
+ * @return 0 for success
+ */
+int32_t csi_enable_mclk(int src, bool flag, bool wait)
+{
+ if (flag == true) {
+ clk_enable(&csi_mclk);
+ if (wait == true)
+ msleep(10);
+ pr_debug("Enable csi clock from source %d\n", src);
+ gcsi_mclk_on = true;
+ } else {
+ clk_disable(&csi_mclk);
+ pr_debug("Disable csi clock from source %d\n", src);
+ gcsi_mclk_on = false;
+ }
+
+ return 0;
+}
+
+/*!
+ * csi_read_mclk_flag
+ *
+ * @return gcsi_mclk_source
+ */
+int csi_read_mclk_flag(void)
+{
+ return 0;
+}
+
+void csi_set_callback(csi_irq_callback_t callback, void *data)
+{
+ g_callback = callback;
+ g_callback_data = data;
+}
+
+static void _mclk_recalc(struct clk *clk)
+{
+ u32 div;
+
+ div = (__raw_readl(CSI_CSICR1) & BIT_MCLKDIV) >> SHIFT_MCLKDIV;
+ div = (div + 1) * 2;
+
+ clk->rate = clk->parent->rate / div;
+}
+
+static unsigned long _mclk_round_rate(struct clk *clk, unsigned long rate)
+{
+ /* Keep CSI divider and change parent clock */
+ if (clk->parent->round_rate) {
+ return clk->parent->round_rate(clk->parent, rate * 2);
+ }
+ return 0;
+}
+
+static int _mclk_set_rate(struct clk *clk, unsigned long rate)
+{
+ int ret = -EINVAL;
+
+ /* Keep CSI divider and change parent clock */
+ if (clk->parent->set_rate) {
+ ret = clk->parent->set_rate(clk->parent, rate * 2);
+ if (ret == 0) {
+ clk->rate = clk->parent->rate / 2;
+ }
+ }
+
+ return ret;
+}
+
+static int _mclk_enable(struct clk *clk)
+{
+ __raw_writel(__raw_readl(CSI_CSICR1) | BIT_MCLKEN, CSI_CSICR1);
+ return 0;
+}
+
+static void _mclk_disable(struct clk *clk)
+{
+ __raw_writel(__raw_readl(CSI_CSICR1) & ~BIT_MCLKEN, CSI_CSICR1);
+}
+
+static struct clk csi_mclk = {
+ .name = "csi_clk",
+ .recalc = _mclk_recalc,
+ .round_rate = _mclk_round_rate,
+ .set_rate = _mclk_set_rate,
+ .enable = _mclk_enable,
+ .disable = _mclk_disable,
+};
+
+int32_t __init csi_init_module(void)
+{
+ int ret = 0;
+ struct clk *per_clk;
+
+ per_clk = clk_get(NULL, "csi_perclk");
+ if (IS_ERR(per_clk))
+ return PTR_ERR(per_clk);
+ clk_put(per_clk);
+ csi_mclk.parent = per_clk;
+ clk_register(&csi_mclk);
+ clk_enable(per_clk);
+ csi_mclk.recalc(&csi_mclk);
+
+ csihw_reset();
+
+ /* interrupt enable */
+ ret = request_irq(MXC_INT_CSI, csi_irq_handler, 0, "csi", 0);
+ if (ret)
+ pr_debug("CSI error: irq request fail\n");
+
+ return ret;
+}
+
+void __exit csi_cleanup_module(void)
+{
+ /* free irq */
+ free_irq(MXC_INT_CSI, 0);
+
+ clk_disable(&csi_mclk);
+}
+
+module_init(csi_init_module);
+module_exit(csi_cleanup_module);
+
+EXPORT_SYMBOL(csi_init_interface);
+EXPORT_SYMBOL(csi_enable_mclk);
+EXPORT_SYMBOL(csi_read_mclk_flag);
+EXPORT_SYMBOL(csi_set_callback);
+EXPORT_SYMBOL(csi_enable_prpif);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MX27 CSI driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxc/capture/mx27_csi.h b/drivers/media/video/mxc/capture/mx27_csi.h
new file mode 100644
index 000000000000..9bd99781e626
--- /dev/null
+++ b/drivers/media/video/mxc/capture/mx27_csi.h
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file mx27_csi.h
+ *
+ * @brief CMOS Sensor interface functions
+ *
+ * @ingroup CSI
+ */
+
+#ifndef MX27_CSI_H
+#define MX27_CSI_H
+
+#include <linux/io.h>
+
+/* reset values */
+#define CSICR1_RESET_VAL 0x40000800
+#define CSICR2_RESET_VAL 0x0
+#define CSICR3_RESET_VAL 0x0
+
+/* csi control reg 1 */
+#define BIT_SWAP16_EN (0x1 << 31)
+#define BIT_EXT_VSYNC (0x1 << 30)
+#define BIT_EOF_INT_EN (0x1 << 29)
+#define BIT_PRP_IF_EN (0x1 << 28)
+#define BIT_CCIR_MODE (0x1 << 27)
+#define BIT_COF_INT_EN (0x1 << 26)
+#define BIT_SF_OR_INTEN (0x1 << 25)
+#define BIT_RF_OR_INTEN (0x1 << 24)
+#define BIT_STATFF_LEVEL (0x3 << 22)
+#define BIT_STATFF_INTEN (0x1 << 21)
+#define BIT_RXFF_LEVEL (0x3 << 19)
+#define BIT_RXFF_INTEN (0x1 << 18)
+#define BIT_SOF_POL (0x1 << 17)
+#define BIT_SOF_INTEN (0x1 << 16)
+#define BIT_MCLKDIV (0xF << 12)
+#define BIT_HSYNC_POL (0x1 << 11)
+#define BIT_CCIR_EN (0x1 << 10)
+#define BIT_MCLKEN (0x1 << 9)
+#define BIT_FCC (0x1 << 8)
+#define BIT_PACK_DIR (0x1 << 7)
+#define BIT_CLR_STATFIFO (0x1 << 6)
+#define BIT_CLR_RXFIFO (0x1 << 5)
+#define BIT_GCLK_MODE (0x1 << 4)
+#define BIT_INV_DATA (0x1 << 3)
+#define BIT_INV_PCLK (0x1 << 2)
+#define BIT_REDGE (0x1 << 1)
+
+#define SHIFT_STATFF_LEVEL 22
+#define SHIFT_RXFF_LEVEL 19
+#define SHIFT_MCLKDIV 12
+
+/* control reg 3 */
+#define BIT_FRMCNT (0xFFFF << 16)
+#define BIT_FRMCNT_RST (0x1 << 15)
+#define BIT_CSI_SUP (0x1 << 3)
+#define BIT_ZERO_PACK_EN (0x1 << 2)
+#define BIT_ECC_INT_EN (0x1 << 1)
+#define BIT_ECC_AUTO_EN (0x1)
+
+#define SHIFT_FRMCNT 16
+
+/* csi status reg */
+#define BIT_SFF_OR_INT (0x1 << 25)
+#define BIT_RFF_OR_INT (0x1 << 24)
+#define BIT_STATFF_INT (0x1 << 21)
+#define BIT_RXFF_INT (0x1 << 18)
+#define BIT_EOF_INT (0x1 << 17)
+#define BIT_SOF_INT (0x1 << 16)
+#define BIT_F2_INT (0x1 << 15)
+#define BIT_F1_INT (0x1 << 14)
+#define BIT_COF_INT (0x1 << 13)
+#define BIT_ECC_INT (0x1 << 1)
+#define BIT_DRDY (0x1 << 0)
+
+#define CSI_MCLK_VF 1
+#define CSI_MCLK_ENC 2
+#define CSI_MCLK_RAW 4
+#define CSI_MCLK_I2C 8
+
+#define CSI_CSICR1 (IO_ADDRESS(CSI_BASE_ADDR))
+#define CSI_CSICR2 (IO_ADDRESS(CSI_BASE_ADDR + 0x4))
+#define CSI_CSISR (IO_ADDRESS(CSI_BASE_ADDR + 0x8))
+#define CSI_STATFIFO (IO_ADDRESS(CSI_BASE_ADDR + 0xC))
+#define CSI_CSIRXFIFO (IO_ADDRESS(CSI_BASE_ADDR + 0x10))
+#define CSI_CSIRXCNT (IO_ADDRESS(CSI_BASE_ADDR + 0x14))
+#define CSI_CSICR3 (IO_ADDRESS(CSI_BASE_ADDR + 0x1C))
+
+#define CSI_CSIRXFIFO_PHYADDR (CSI_BASE_ADDR + 0x10)
+
+static __inline void csi_clear_status(unsigned long status)
+{
+ __raw_writel(status, CSI_CSISR);
+}
+
+typedef struct {
+ unsigned data_width:3;
+ unsigned clk_mode:2;
+ unsigned ext_vsync:1;
+ unsigned Vsync_pol:1;
+ unsigned Hsync_pol:1;
+ unsigned pixclk_pol:1;
+ unsigned data_pol:1;
+ unsigned sens_clksrc:1;
+} csi_signal_cfg_t;
+
+typedef struct {
+ /* control reg 1 */
+ unsigned int swap16_en:1;
+ unsigned int ext_vsync:1;
+ unsigned int eof_int_en:1;
+ unsigned int prp_if_en:1;
+ unsigned int ccir_mode:1;
+ unsigned int cof_int_en:1;
+ unsigned int sf_or_inten:1;
+ unsigned int rf_or_inten:1;
+ unsigned int statff_level:2;
+ unsigned int staff_inten:1;
+ unsigned int rxff_level:2;
+ unsigned int rxff_inten:1;
+ unsigned int sof_pol:1;
+ unsigned int sof_inten:1;
+ unsigned int mclkdiv:4;
+ unsigned int hsync_pol:1;
+ unsigned int ccir_en:1;
+ unsigned int mclken:1;
+ unsigned int fcc:1;
+ unsigned int pack_dir:1;
+ unsigned int gclk_mode:1;
+ unsigned int inv_data:1;
+ unsigned int inv_pclk:1;
+ unsigned int redge:1;
+ unsigned int csicnt1_rsv:1;
+
+ /* control reg 3 */
+ unsigned int frmcnt:16;
+ unsigned int frame_reset:1;
+ unsigned int csi_sup:1;
+ unsigned int zero_pack_en:1;
+ unsigned int ecc_int_en:1;
+ unsigned int ecc_auto_en:1;
+
+ /* fifo counter */
+ unsigned int rxcnt;
+} csi_config_t;
+
+typedef void (*csi_irq_callback_t) (void *data, unsigned long status);
+
+int32_t csi_enable_mclk(int src, bool flag, bool wait);
+int32_t csi_init_interface(uint16_t width, uint16_t height,
+ uint32_t pixel_fmt, csi_signal_cfg_t sig);
+int csi_read_mclk_flag(void);
+void csi_set_callback(csi_irq_callback_t callback, void *data);
+void csi_enable_prpif(uint32_t enable);
+
+#endif
diff --git a/drivers/media/video/mxc/capture/mx27_prp.h b/drivers/media/video/mxc/capture/mx27_prp.h
new file mode 100644
index 000000000000..e32e9029daff
--- /dev/null
+++ b/drivers/media/video/mxc/capture/mx27_prp.h
@@ -0,0 +1,310 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file mx27_prp.h
+ *
+ * @brief Header file for MX27 V4L2 capture driver
+ *
+ * @ingroup MXC_V4L2_CAPTURE
+ */
+#ifndef __MX27_PRP_H__
+#define __MX27_PRP_H__
+
+#define PRP_REG(ofs) (IO_ADDRESS(EMMA_BASE_ADDR) + ofs)
+
+/* Register definitions of PrP */
+#define PRP_CNTL PRP_REG(0x00)
+#define PRP_INTRCNTL PRP_REG(0x04)
+#define PRP_INTRSTATUS PRP_REG(0x08)
+#define PRP_SOURCE_Y_PTR PRP_REG(0x0C)
+#define PRP_SOURCE_CB_PTR PRP_REG(0x10)
+#define PRP_SOURCE_CR_PTR PRP_REG(0x14)
+#define PRP_DEST_RGB1_PTR PRP_REG(0x18)
+#define PRP_DEST_RGB2_PTR PRP_REG(0x1C)
+#define PRP_DEST_Y_PTR PRP_REG(0x20)
+#define PRP_DEST_CB_PTR PRP_REG(0x24)
+#define PRP_DEST_CR_PTR PRP_REG(0x28)
+#define PRP_SOURCE_FRAME_SIZE PRP_REG(0x2C)
+#define PRP_CH1_LINE_STRIDE PRP_REG(0x30)
+#define PRP_SRC_PIXEL_FORMAT_CNTL PRP_REG(0x34)
+#define PRP_CH1_PIXEL_FORMAT_CNTL PRP_REG(0x38)
+#define PRP_CH1_OUT_IMAGE_SIZE PRP_REG(0x3C)
+#define PRP_CH2_OUT_IMAGE_SIZE PRP_REG(0x40)
+#define PRP_SOURCE_LINE_STRIDE PRP_REG(0x44)
+#define PRP_CSC_COEF_012 PRP_REG(0x48)
+#define PRP_CSC_COEF_345 PRP_REG(0x4C)
+#define PRP_CSC_COEF_678 PRP_REG(0x50)
+#define PRP_CH1_RZ_HORI_COEF1 PRP_REG(0x54)
+#define PRP_CH1_RZ_HORI_COEF2 PRP_REG(0x58)
+#define PRP_CH1_RZ_HORI_VALID PRP_REG(0x5C)
+#define PRP_CH1_RZ_VERT_COEF1 PRP_REG(0x60)
+#define PRP_CH1_RZ_VERT_COEF2 PRP_REG(0x64)
+#define PRP_CH1_RZ_VERT_VALID PRP_REG(0x68)
+#define PRP_CH2_RZ_HORI_COEF1 PRP_REG(0x6C)
+#define PRP_CH2_RZ_HORI_COEF2 PRP_REG(0x70)
+#define PRP_CH2_RZ_HORI_VALID PRP_REG(0x74)
+#define PRP_CH2_RZ_VERT_COEF1 PRP_REG(0x78)
+#define PRP_CH2_RZ_VERT_COEF2 PRP_REG(0x7C)
+#define PRP_CH2_RZ_VERT_VALID PRP_REG(0x80)
+
+#define B_SET(b) (1 << (b))
+
+/* Bit definitions for PrP control register */
+#define PRP_CNTL_RSTVAL 0x28
+#define PRP_CNTL_CH1EN B_SET(0)
+#define PRP_CNTL_CH2EN B_SET(1)
+#define PRP_CNTL_CSI B_SET(2)
+#define PRP_CNTL_IN_32 B_SET(3)
+#define PRP_CNTL_IN_RGB B_SET(4)
+#define PRP_CNTL_IN_YUV420 0
+#define PRP_CNTL_IN_YUV422 PRP_CNTL_IN_32
+#define PRP_CNTL_IN_RGB16 PRP_CNTL_IN_RGB
+#define PRP_CNTL_IN_RGB32 (PRP_CNTL_IN_RGB | PRP_CNTL_IN_32)
+#define PRP_CNTL_CH1_RGB8 0
+#define PRP_CNTL_CH1_RGB16 B_SET(5)
+#define PRP_CNTL_CH1_RGB32 B_SET(6)
+#define PRP_CNTL_CH1_YUV422 (B_SET(5) | B_SET(6))
+#define PRP_CNTL_CH2_YUV420 0
+#define PRP_CNTL_CH2_YUV422 B_SET(7)
+#define PRP_CNTL_CH2_YUV444 B_SET(8)
+#define PRP_CNTL_CH1_LOOP B_SET(9)
+#define PRP_CNTL_CH2_LOOP B_SET(10)
+#define PRP_CNTL_AUTODROP B_SET(11)
+#define PRP_CNTL_RST B_SET(12)
+#define PRP_CNTL_CNTREN B_SET(13)
+#define PRP_CNTL_WINEN B_SET(14)
+#define PRP_CNTL_UNCHAIN B_SET(15)
+#define PRP_CNTL_IN_SKIP_NONE 0
+#define PRP_CNTL_IN_SKIP_1_2 B_SET(16)
+#define PRP_CNTL_IN_SKIP_1_3 B_SET(17)
+#define PRP_CNTL_IN_SKIP_2_3 (B_SET(16) | B_SET(17))
+#define PRP_CNTL_IN_SKIP_1_4 B_SET(18)
+#define PRP_CNTL_IN_SKIP_3_4 (B_SET(16) | B_SET(18))
+#define PRP_CNTL_IN_SKIP_2_5 (B_SET(17) | B_SET(18))
+#define PRP_CNTL_IN_SKIP_3_5 (B_SET(16) | B_SET(17) | B_SET(18))
+#define PRP_CNTL_CH1_SKIP_NONE 0
+#define PRP_CNTL_CH1_SKIP_1_2 B_SET(19)
+#define PRP_CNTL_CH1_SKIP_1_3 B_SET(20)
+#define PRP_CNTL_CH1_SKIP_2_3 (B_SET(19) | B_SET(20))
+#define PRP_CNTL_CH1_SKIP_1_4 B_SET(21)
+#define PRP_CNTL_CH1_SKIP_3_4 (B_SET(19) | B_SET(21))
+#define PRP_CNTL_CH1_SKIP_2_5 (B_SET(20) | B_SET(21))
+#define PRP_CNTL_CH1_SKIP_3_5 (B_SET(19) | B_SET(20) | B_SET(21))
+#define PRP_CNTL_CH2_SKIP_NONE 0
+#define PRP_CNTL_CH2_SKIP_1_2 B_SET(22)
+#define PRP_CNTL_CH2_SKIP_1_3 B_SET(23)
+#define PRP_CNTL_CH2_SKIP_2_3 (B_SET(22) | B_SET(23))
+#define PRP_CNTL_CH2_SKIP_1_4 B_SET(24)
+#define PRP_CNTL_CH2_SKIP_3_4 (B_SET(22) | B_SET(24))
+#define PRP_CNTL_CH2_SKIP_2_5 (B_SET(23) | B_SET(24))
+#define PRP_CNTL_CH2_SKIP_3_5 (B_SET(22) | B_SET(23) | B_SET(24))
+#define PRP_CNTL_FIFO_I128 0
+#define PRP_CNTL_FIFO_I96 B_SET(25)
+#define PRP_CNTL_FIFO_I64 B_SET(26)
+#define PRP_CNTL_FIFO_I32 (B_SET(25) | B_SET(26))
+#define PRP_CNTL_FIFO_O64 0
+#define PRP_CNTL_FIFO_O48 B_SET(27)
+#define PRP_CNTL_FIFO_O32 B_SET(28)
+#define PRP_CNTL_FIFO_O16 (B_SET(27) | B_SET(28))
+#define PRP_CNTL_CH2B1 B_SET(29)
+#define PRP_CNTL_CH2B2 B_SET(30)
+#define PRP_CNTL_CH2_FLOWEN B_SET(31)
+
+/* Bit definitions for PrP interrupt control register */
+#define PRP_INTRCNTL_RDERR B_SET(0)
+#define PRP_INTRCNTL_CH1WERR B_SET(1)
+#define PRP_INTRCNTL_CH2WERR B_SET(2)
+#define PRP_INTRCNTL_CH1FC B_SET(3)
+#define PRP_INTRCNTL_CH2FC B_SET(5)
+#define PRP_INTRCNTL_LBOVF B_SET(7)
+#define PRP_INTRCNTL_CH2OVF B_SET(8)
+
+/* Bit definitions for PrP interrupt status register */
+#define PRP_INTRSTAT_RDERR B_SET(0)
+#define PRP_INTRSTAT_CH1WERR B_SET(1)
+#define PRP_INTRSTAT_CH2WERR B_SET(2)
+#define PRP_INTRSTAT_CH2BUF2 B_SET(3)
+#define PRP_INTRSTAT_CH2BUF1 B_SET(4)
+#define PRP_INTRSTAT_CH1BUF2 B_SET(5)
+#define PRP_INTRSTAT_CH1BUF1 B_SET(6)
+#define PRP_INTRSTAT_LBOVF B_SET(7)
+#define PRP_INTRSTAT_CH2OVF B_SET(8)
+
+#define PRP_CHANNEL_1 0x1
+#define PRP_CHANNEL_2 0x2
+
+/* PRP-CSI config */
+#define PRP_CSI_EN 0x80
+#define PRP_CSI_LOOP (0x40 | PRP_CSI_EN)
+#define PRP_CSI_IRQ_FRM (0x08 | PRP_CSI_LOOP)
+#define PRP_CSI_IRQ_CH1ERR (0x10 | PRP_CSI_LOOP)
+#define PRP_CSI_IRQ_CH2ERR (0x20 | PRP_CSI_LOOP)
+#define PRP_CSI_IRQ_ALL (0x38 | PRP_CSI_LOOP)
+#define PRP_CSI_SKIP_NONE 0
+#define PRP_CSI_SKIP_1OF2 1
+#define PRP_CSI_SKIP_1OF3 2
+#define PRP_CSI_SKIP_2OF3 3
+#define PRP_CSI_SKIP_1OF4 4
+#define PRP_CSI_SKIP_3OF4 5
+#define PRP_CSI_SKIP_2OF5 6
+#define PRP_CSI_SKIP_4OF5 7
+
+#define PRP_PIXIN_RGB565 0x2CA00565
+#define PRP_PIXIN_RGB888 0x41000888
+#define PRP_PIXIN_YUV420 0
+#define PRP_PIXIN_YUYV 0x22000888
+#define PRP_PIXIN_YVYU 0x20100888
+#define PRP_PIXIN_UYVY 0x03080888
+#define PRP_PIXIN_VYUY 0x01180888
+#define PRP_PIXIN_YUV422 0x62080888
+
+#define PRP_PIX1_RGB332 0x14400322
+#define PRP_PIX1_RGB565 0x2CA00565
+#define PRP_PIX1_RGB888 0x41000888
+#define PRP_PIX1_YUYV 0x62000888
+#define PRP_PIX1_YVYU 0x60100888
+#define PRP_PIX1_UYVY 0x43080888
+#define PRP_PIX1_VYUY 0x41180888
+#define PRP_PIX1_UNUSED 0
+
+#define PRP_PIX2_YUV420 0
+#define PRP_PIX2_YUV422 1
+#define PRP_PIX2_YUV444 4
+#define PRP_PIX2_UNUSED 8
+
+#define PRP_ALGO_WIDTH_ANY 0
+#define PRP_ALGO_HEIGHT_ANY 0
+#define PRP_ALGO_WIDTH_BIL 1
+#define PRP_ALGO_WIDTH_AVG 2
+#define PRP_ALGO_HEIGHT_BIL 4
+#define PRP_ALGO_HEIGHT_AVG 8
+#define PRP_ALGO_BYPASS 0x10
+
+typedef struct _emma_prp_ratio {
+ unsigned short num;
+ unsigned short den;
+} emma_prp_ratio;
+
+/*
+ * The following definitions are for resizing. Definition values must not
+ * be changed otherwise decision logic will be wrong.
+ */
+#define SCALE_RETRY 16 /* retry times if ratio is not supported */
+
+#define BC_COEF 3
+#define MAX_TBL 20
+#define SZ_COEF (1 << BC_COEF)
+
+#define ALGO_AUTO 0
+#define ALGO_BIL 1
+#define ALGO_AVG 2
+
+typedef struct {
+ char tbl[20]; /* table entries */
+ char len; /* table length used */
+ char algo; /* ALGO_xxx */
+ char ratio[20]; /* ratios used */
+} scale_t;
+
+/*
+ * structure for prp scaling.
+ * algorithm - bilinear or averaging for each axis
+ * PRP_ALGO_WIDTH_x | PRP_ALGO_HEIGHT_x | PRP_ALGO_BYPASS
+ * PRP_ALGO_BYPASS - Ch1 will not use Ch2 scaling with this flag
+ */
+typedef struct _emma_prp_scale {
+ unsigned char algo;
+ emma_prp_ratio width;
+ emma_prp_ratio height;
+} emma_prp_scale;
+
+typedef struct emma_prp_cfg {
+ unsigned int in_pix; /* PRP_PIXIN_xxx */
+ unsigned short in_width; /* image width, 32 - 2044 */
+ unsigned short in_height; /* image height, 32 - 2044 */
+ unsigned char in_csi; /* PRP_CSI_SKIP_x | PRP_CSI_LOOP */
+ unsigned short in_line_stride; /* in_line_stride and in_line_skip */
+ unsigned short in_line_skip; /* allow cropping from CSI */
+ unsigned int in_ptr; /* bus address */
+ /*
+ * in_csc[9] = 1 -> Y-16
+ * if in_csc[1..9] == 0
+ * in_csc[0] represents YUV range 0-3 = A0,A1,B0,B1;
+ * else
+ * in_csc[0..9] represents either format
+ */
+ unsigned short in_csc[10];
+
+ unsigned char ch2_pix; /* PRP_PIX2_xxx */
+ emma_prp_scale ch2_scale; /* resizing paramters */
+ unsigned short ch2_width; /* 4-2044, 0 = scaled */
+ unsigned short ch2_height; /* 4-2044, 0 = scaled */
+ unsigned int ch2_ptr; /* bus addr */
+ unsigned int ch2_ptr2; /* bus addr for 2nd buf (loop mode) */
+ unsigned char ch2_csi; /* PRP_CSI_SKIP_x | PRP_CSI_LOOP */
+
+ unsigned int ch1_pix; /* PRP_PIX1_xxx */
+ emma_prp_scale ch1_scale; /* resizing parameters */
+ unsigned short ch1_width; /* 4-2044, 0 = scaled */
+ unsigned short ch1_height; /* 4-2044, 0 = scaled */
+ unsigned short ch1_stride; /* 4-4088, 0 = ch1_width */
+ unsigned int ch1_ptr; /* bus addr */
+ unsigned int ch1_ptr2; /* bus addr for 2nd buf (loop mode) */
+ unsigned char ch1_csi; /* PRP_CSI_SKIP_x | PRP_CSI_LOOP */
+
+ /*
+ * channel resizing coefficients
+ * scale[0] for channel 1 width
+ * scale[1] for channel 1 height
+ * scale[2] for channel 2 width
+ * scale[3] for channel 2 height
+ */
+ scale_t scale[4];
+} emma_prp_cfg;
+
+int prphw_reset(void);
+int prphw_enable(int channel);
+int prphw_disable(int channel);
+int prphw_inptr(emma_prp_cfg *);
+int prphw_ch1ptr(emma_prp_cfg *);
+int prphw_ch1ptr2(emma_prp_cfg *);
+int prphw_ch2ptr(emma_prp_cfg *);
+int prphw_ch2ptr2(emma_prp_cfg *);
+int prphw_cfg(emma_prp_cfg *);
+int prphw_isr(void);
+void prphw_init(void);
+void prphw_exit(void);
+
+/*
+ * scale out coefficient table
+ * din in scale numerator
+ * dout in scale denominator
+ * inv in pre-scale dimension
+ * vout in/out post-scale output dimension
+ * pout out post-scale internal dimension [opt]
+ * retry in retry times (round the output length) when need
+ */
+int prp_scale(scale_t * pscale, int din, int dout, int inv,
+ unsigned short *vout, unsigned short *pout, int retry);
+
+int prp_init(void *dev_id);
+void prp_exit(void *dev_id);
+int prp_enc_select(void *data);
+int prp_enc_deselect(void *data);
+int prp_vf_select(void *data);
+int prp_vf_deselect(void *data);
+int prp_still_select(void *data);
+int prp_still_deselect(void *data);
+
+#endif /* __MX27_PRP_H__ */
diff --git a/drivers/media/video/mxc/capture/mx27_prphw.c b/drivers/media/video/mxc/capture/mx27_prphw.c
new file mode 100644
index 000000000000..c56a6df1716e
--- /dev/null
+++ b/drivers/media/video/mxc/capture/mx27_prphw.c
@@ -0,0 +1,1099 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file mx27_prphw.c
+ *
+ * @brief MX27 Video For Linux 2 capture driver
+ *
+ * @ingroup MXC_V4L2_CAPTURE
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/clk.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+
+#include "mx27_prp.h"
+
+#define PRP_MIN_IN_WIDTH 32
+#define PRP_MAX_IN_WIDTH 2044
+#define PRP_MIN_IN_HEIGHT 32
+#define PRP_MAX_IN_HEIGHT 2044
+
+typedef struct _coeff_t {
+ unsigned long coeff[2];
+ unsigned long cntl;
+} coeff_t[2][2];
+
+static coeff_t *PRP_RSZ_COEFF = (coeff_t *) PRP_CH1_RZ_HORI_COEF1;
+
+static unsigned char scale_get(scale_t * t,
+ unsigned char *i, unsigned char *out);
+static int gcd(int x, int y);
+static int ratio(int x, int y, int *den);
+static int prp_scale_bilinear(scale_t * t, int coeff, int base, int nxt);
+static int prp_scale_ave(scale_t * t, unsigned char base);
+static int ave_scale(scale_t * t, int inv, int outv);
+static int scale(scale_t * t, int inv, int outv);
+
+/*!
+ * @param t table
+ * @param i table index
+ * @param out bilinear # input pixels to advance
+ * average whether result is ready for output
+ * @return coefficient
+*/
+static unsigned char scale_get(scale_t * t, unsigned char *i,
+ unsigned char *out)
+{
+ unsigned char c;
+
+ c = t->tbl[*i];
+ (*i)++;
+ *i %= t->len;
+
+ if (out) {
+ if (t->algo == ALGO_BIL) {
+ for ((*out) = 1;
+ (*i) && ((*i) < t->len) && !t->tbl[(*i)]; (*i)++) {
+ (*out)++;
+ }
+ if ((*i) == t->len)
+ (*i) = 0;
+ } else
+ *out = c >> BC_COEF;
+ }
+
+ c &= SZ_COEF - 1;
+
+ if (c == SZ_COEF - 1)
+ c = SZ_COEF;
+
+ return c;
+}
+
+/*!
+ * @brief Get maximum common divisor.
+ * @param x First input value
+ * @param y Second input value
+ * @return Maximum common divisor of x and y
+ */
+static int gcd(int x, int y)
+{
+ int k;
+
+ if (x < y) {
+ k = x;
+ x = y;
+ y = k;
+ }
+
+ while ((k = x % y)) {
+ x = y;
+ y = k;
+ }
+
+ return y;
+}
+
+/*!
+ * @brief Get ratio.
+ * @param x First input value
+ * @param y Second input value
+ * @param den Denominator of the ratio (corresponding to y)
+ * @return Numerator of the ratio (corresponding to x)
+ */
+static int ratio(int x, int y, int *den)
+{
+ int g;
+
+ if (!x || !y)
+ return 0;
+
+ g = gcd(x, y);
+ *den = y / g;
+
+ return x / g;
+}
+
+/*!
+ * @brief Build PrP coefficient entry based on bilinear algorithm
+ *
+ * @param t The pointer to scale_t structure
+ * @param coeff The weighting coefficient
+ * @param base The base of the coefficient
+ * @param nxt Number of pixels to be read
+ *
+ * @return The length of current coefficient table on success
+ * -1 on failure
+ */
+static int prp_scale_bilinear(scale_t * t, int coeff, int base, int nxt)
+{
+ int i;
+
+ if (t->len >= sizeof(t->tbl))
+ return -1;
+
+ coeff = ((coeff << BC_COEF) + (base >> 1)) / base;
+ if (coeff >= SZ_COEF - 1)
+ coeff--;
+
+ coeff |= SZ_COEF;
+ t->tbl[(int)t->len++] = (unsigned char)coeff;
+
+ for (i = 1; i < nxt; i++) {
+ if (t->len >= MAX_TBL)
+ return -1;
+
+ t->tbl[(int)t->len++] = 0;
+ }
+
+ return t->len;
+}
+
+#define _bary(name) static const unsigned char name[]
+
+_bary(c1) = {
+7};
+
+_bary(c2) = {
+4, 4};
+
+_bary(c3) = {
+2, 4, 2};
+
+_bary(c4) = {
+2, 2, 2, 2};
+
+_bary(c5) = {
+1, 2, 2, 2, 1};
+
+_bary(c6) = {
+1, 1, 2, 2, 1, 1};
+
+_bary(c7) = {
+1, 1, 1, 2, 1, 1, 1};
+
+_bary(c8) = {
+1, 1, 1, 1, 1, 1, 1, 1};
+
+_bary(c9) = {
+1, 1, 1, 1, 1, 1, 1, 1, 0};
+
+_bary(c10) = {
+0, 1, 1, 1, 1, 1, 1, 1, 1, 0};
+
+_bary(c11) = {
+0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0};
+
+_bary(c12) = {
+0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0};
+
+_bary(c13) = {
+0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0};
+
+_bary(c14) = {
+0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0};
+
+_bary(c15) = {
+0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0};
+
+_bary(c16) = {
+1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0};
+
+_bary(c17) = {
+0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0};
+
+_bary(c18) = {
+0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0};
+
+_bary(c19) = {
+0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0};
+
+_bary(c20) = {
+0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0};
+
+static const unsigned char *ave_coeff[] = {
+ c1, c2, c3, c4, c5, c6, c7, c8, c9, c10,
+ c11, c12, c13, c14, c15, c16, c17, c18, c19, c20
+};
+
+/*!
+ * @brief Build PrP coefficient table based on average algorithm
+ *
+ * @param t The pointer to scale_t structure
+ * @param base The base of the coefficient
+ *
+ * @return The length of current coefficient table on success
+ * -1 on failure
+ */
+static int prp_scale_ave(scale_t * t, unsigned char base)
+{
+ if (t->len + base > sizeof(t->tbl))
+ return -1;
+
+ memcpy(&t->tbl[(int)t->len], ave_coeff[(int)base - 1], base);
+ t->len = (unsigned char)(t->len + base);
+ t->tbl[t->len - 1] |= SZ_COEF;
+
+ return t->len;
+}
+
+/*!
+ * @brief Build PrP coefficient table based on average algorithm
+ *
+ * @param t The pointer to scale_t structure
+ * @param inv Input resolution
+ * @param outv Output resolution
+ *
+ * @return The length of current coefficient table on success
+ * -1 on failure
+ */
+static int ave_scale(scale_t * t, int inv, int outv)
+{
+ int ratio_count;
+
+ ratio_count = 0;
+ if (outv != 1) {
+ unsigned char a[20];
+ int v;
+
+ /* split n:m into multiple n[i]:1 */
+ for (v = 0; v < outv; v++)
+ a[v] = (unsigned char)(inv / outv);
+
+ inv %= outv;
+ if (inv) {
+ /* find start of next layer */
+ v = (outv - inv) >> 1;
+ inv += v;
+ for (; v < inv; v++)
+ a[v]++;
+ }
+
+ for (v = 0; v < outv; v++) {
+ if (prp_scale_ave(t, a[v]) < 0)
+ return -1;
+
+ t->ratio[ratio_count] = a[v];
+ ratio_count++;
+ }
+ } else if (prp_scale_ave(t, inv) < 0) {
+ return -1;
+ } else {
+ t->ratio[ratio_count++] = (char)inv;
+ ratio_count++;
+ }
+
+ return t->len;
+}
+
+/*!
+ * @brief Build PrP coefficient table
+ *
+ * @param t The pointer to scale_t structure
+ * @param inv input resolution reduced ratio
+ * @param outv output resolution reduced ratio
+ *
+ * @return The length of current coefficient table on success
+ * -1 on failure
+ */
+static int scale(scale_t * t, int inv, int outv)
+{
+ int v; /* overflow counter */
+ int coeff, nxt; /* table output */
+
+ t->len = 0;
+ if (t->algo == ALGO_AUTO) {
+ /* automatic choice - bilinear for shrinking less than 2:1 */
+ t->algo = ((outv != inv) && ((2 * outv) > inv)) ?
+ ALGO_BIL : ALGO_AVG;
+ }
+
+ /* 1:1 resize must use averaging, bilinear will hang */
+ if ((inv == outv) && (t->algo == ALGO_BIL)) {
+ pr_debug("Warning: 1:1 resize must use averaging algo\n");
+ t->algo = ALGO_AVG;
+ }
+
+ memset(t->tbl, 0, sizeof(t->tbl));
+ if (t->algo == ALGO_BIL) {
+ t->ratio[0] = (char)inv;
+ t->ratio[1] = (char)outv;
+ } else
+ memset(t->ratio, 0, sizeof(t->ratio));
+
+ if (inv == outv) {
+ /* force scaling */
+ t->ratio[0] = 1;
+ if (t->algo == ALGO_BIL)
+ t->ratio[1] = 1;
+
+ return prp_scale_ave(t, 1);
+ }
+
+ if (inv < outv) {
+ pr_debug("Upscaling not supported %d:%d\n", inv, outv);
+ return -1;
+ }
+
+ if (t->algo != ALGO_BIL)
+ return ave_scale(t, inv, outv);
+
+ v = 0;
+ if (inv >= 2 * outv) {
+ /* downscale: >=2:1 bilinear approximation */
+ coeff = inv - 2 * outv;
+ v = 0;
+ nxt = 0;
+ do {
+ v += coeff;
+ nxt = 2;
+ while (v >= outv) {
+ v -= outv;
+ nxt++;
+ }
+
+ if (prp_scale_bilinear(t, 1, 2, nxt) < 0)
+ return -1;
+ } while (v);
+ } else {
+ /* downscale: bilinear */
+ int in_pos_inc = 2 * outv;
+ int out_pos = inv;
+ int out_pos_inc = 2 * inv;
+ int init_carry = inv - outv;
+ int carry = init_carry;
+
+ v = outv + in_pos_inc;
+ do {
+ coeff = v - out_pos;
+ out_pos += out_pos_inc;
+ carry += out_pos_inc;
+ for (nxt = 0; v < out_pos; nxt++) {
+ v += in_pos_inc;
+ carry -= in_pos_inc;
+ }
+ if (prp_scale_bilinear(t, coeff, in_pos_inc, nxt) < 0)
+ return -1;
+ } while (carry != init_carry);
+ }
+ return t->len;
+}
+
+/*!
+ * @brief Build PrP coefficient table
+ *
+ * @param pscale The pointer to scale_t structure which holdes
+ * coefficient tables
+ * @param din Scale ratio numerator
+ * @param dout Scale ratio denominator
+ * @param inv Input resolution
+ * @param vout Output resolution
+ * @param pout Internal output resolution
+ * @param retry Retry times (round the output length) when need
+ *
+ * @return Zero on success, others on failure
+ */
+int prp_scale(scale_t * pscale, int din, int dout, int inv,
+ unsigned short *vout, unsigned short *pout, int retry)
+{
+ int num;
+ int den;
+ unsigned short outv;
+
+ /* auto-generation of values */
+ if (!(dout && din)) {
+ if (!*vout)
+ dout = din = 1;
+ else {
+ din = inv;
+ dout = *vout;
+ }
+ }
+
+ if (din < dout) {
+ pr_debug("Scale err, unsupported ratio %d : %d\n", din, dout);
+ return -1;
+ }
+
+ lp_retry:
+ num = ratio(din, dout, &den);
+ if (!num) {
+ pr_debug("Scale err, unsupported ratio %d : %d\n", din, dout);
+ return -1;
+ }
+
+ if (num > MAX_TBL || scale(pscale, num, den) < 0) {
+ dout++;
+ if (retry--)
+ goto lp_retry;
+
+ pr_debug("Scale err, unsupported ratio %d : %d\n", num, den);
+ return -1;
+ }
+
+ if (pscale->algo == ALGO_BIL) {
+ unsigned char i, j, k;
+
+ outv =
+ (unsigned short)(inv / pscale->ratio[0] * pscale->ratio[1]);
+ inv %= pscale->ratio[0];
+ for (i = j = 0; inv > 0; j++) {
+ unsigned char nxt;
+
+ k = scale_get(pscale, &i, &nxt);
+ if (inv == 1 && k < SZ_COEF) {
+ /* needs 2 pixels for this output */
+ break;
+ }
+ inv -= nxt;
+ }
+ outv = outv + j;
+ } else {
+ unsigned char i, tot;
+
+ for (tot = i = 0; pscale->ratio[i]; i++)
+ tot = tot + pscale->ratio[i];
+
+ outv = (unsigned short)(inv / tot) * i;
+ inv %= tot;
+ for (i = 0; inv > 0; i++, outv++)
+ inv -= pscale->ratio[i];
+ }
+
+ if (!(*vout) || ((*vout) > outv))
+ *vout = outv;
+
+ if (pout)
+ *pout = outv;
+
+ return 0;
+}
+
+/*!
+ * @brief Reset PrP block
+ */
+int prphw_reset(void)
+{
+ unsigned long val;
+ unsigned long flag;
+ int i;
+
+ flag = PRP_CNTL_RST;
+ val = PRP_CNTL_RSTVAL;
+
+ __raw_writel(flag, PRP_CNTL);
+
+ /* timeout */
+ for (i = 0; i < 1000; i++) {
+ if (!(__raw_readl(PRP_CNTL) & flag)) {
+ pr_debug("PrP reset over\n");
+ break;
+ }
+ msleep(1);
+ }
+
+ /* verify reset value */
+ if (__raw_readl(PRP_CNTL) != val) {
+ pr_info("PrP reset err, val = 0x%08X\n", __raw_readl(PRP_CNTL));
+ return -1;
+ }
+
+ return 0;
+}
+
+/*!
+ * @brief Enable PrP channel.
+ * @param channel Channel number to be enabled
+ * @return Zero on success, others on failure
+ */
+int prphw_enable(int channel)
+{
+ unsigned long val;
+
+ val = __raw_readl(PRP_CNTL);
+ if (channel & PRP_CHANNEL_1)
+ val |= PRP_CNTL_CH1EN;
+ if (channel & PRP_CHANNEL_2)
+ val |= (PRP_CNTL_CH2EN | PRP_CNTL_CH2_FLOWEN);
+
+ __raw_writel(val, PRP_CNTL);
+
+ return 0;
+}
+
+/*!
+ * @brief Disable PrP channel.
+ * @param channel Channel number to be disable
+ * @return Zero on success, others on failure
+ */
+int prphw_disable(int channel)
+{
+ unsigned long val;
+
+ val = __raw_readl(PRP_CNTL);
+ if (channel & PRP_CHANNEL_1)
+ val &= ~PRP_CNTL_CH1EN;
+ if (channel & PRP_CHANNEL_2)
+ val &= ~(PRP_CNTL_CH2EN | PRP_CNTL_CH2_FLOWEN);
+
+ __raw_writel(val, PRP_CNTL);
+
+ return 0;
+}
+
+/*!
+ * @brief Set PrP input buffer address.
+ * @param cfg Pointer to PrP configuration parameter
+ * @return Zero on success, others on failure
+ */
+int prphw_inptr(emma_prp_cfg * cfg)
+{
+ if (cfg->in_csi & PRP_CSI_EN)
+ return -1;
+
+ __raw_writel(cfg->in_ptr, PRP_SOURCE_Y_PTR);
+ if (cfg->in_pix == PRP_PIXIN_YUV420) {
+ u32 size;
+
+ size = cfg->in_line_stride * cfg->in_height;
+ __raw_writel(cfg->in_ptr + size, PRP_SOURCE_CB_PTR);
+ __raw_writel(cfg->in_ptr + size + (size >> 2),
+ PRP_SOURCE_CR_PTR);
+ }
+ return 0;
+}
+
+/*!
+ * @brief Set PrP channel 1 output buffer 1 address.
+ * @param cfg Pointer to PrP configuration parameter
+ * @return Zero on success, others on failure
+ */
+int prphw_ch1ptr(emma_prp_cfg * cfg)
+{
+ if (cfg->ch1_pix == PRP_PIX1_UNUSED)
+ return -1;
+
+ __raw_writel(cfg->ch1_ptr, PRP_DEST_RGB1_PTR);
+
+ /* support double buffer in loop mode only */
+ if ((cfg->in_csi & PRP_CSI_LOOP) == PRP_CSI_LOOP) {
+ if (cfg->ch1_ptr2)
+ __raw_writel(cfg->ch1_ptr2, PRP_DEST_RGB2_PTR);
+ else
+ __raw_writel(cfg->ch1_ptr, PRP_DEST_RGB2_PTR);
+ }
+
+ return 0;
+}
+
+/*!
+ * @brief Set PrP channel 1 output buffer 2 address.
+ * @param cfg Pointer to PrP configuration parameter
+ * @return Zero on success, others on failure
+ */
+int prphw_ch1ptr2(emma_prp_cfg * cfg)
+{
+ if (cfg->ch1_pix == PRP_PIX1_UNUSED ||
+ (cfg->in_csi & PRP_CSI_LOOP) != PRP_CSI_LOOP)
+ return -1;
+
+ if (cfg->ch1_ptr2)
+ __raw_writel(cfg->ch1_ptr2, PRP_DEST_RGB2_PTR);
+ else
+ return -1;
+
+ return 0;
+}
+
+/*!
+ * @brief Set PrP channel 2 output buffer 1 address.
+ * @param cfg Pointer to PrP configuration parameter
+ * @return Zero on success, others on failure
+ */
+int prphw_ch2ptr(emma_prp_cfg * cfg)
+{
+ u32 size;
+
+ if (cfg->ch2_pix == PRP_PIX2_UNUSED)
+ return -1;
+
+ __raw_writel(cfg->ch2_ptr, PRP_DEST_Y_PTR);
+
+ if (cfg->ch2_pix == PRP_PIX2_YUV420) {
+ size = cfg->ch2_width * cfg->ch2_height;
+ __raw_writel(cfg->ch2_ptr + size, PRP_DEST_CB_PTR);
+ __raw_writel(cfg->ch2_ptr + size + (size >> 2),
+ PRP_DEST_CR_PTR);
+ }
+
+ __raw_writel(__raw_readl(PRP_CNTL) | PRP_CNTL_CH2B1, PRP_CNTL);
+ return 0;
+}
+
+/*!
+ * @brief Set PrP channel 2 output buffer 2 address.
+ * @param cfg Pointer to PrP configuration parameter
+ * @return Zero on success, others on failure
+ */
+int prphw_ch2ptr2(emma_prp_cfg * cfg)
+{
+ u32 size;
+
+ if (cfg->ch2_pix == PRP_PIX2_UNUSED ||
+ (cfg->in_csi & PRP_CSI_LOOP) != PRP_CSI_LOOP)
+ return -1;
+
+ __raw_writel(cfg->ch2_ptr2, PRP_SOURCE_Y_PTR);
+ if (cfg->ch2_pix == PRP_PIX2_YUV420) {
+ size = cfg->ch2_width * cfg->ch2_height;
+ __raw_writel(cfg->ch2_ptr2 + size, PRP_SOURCE_CB_PTR);
+ __raw_writel(cfg->ch2_ptr2 + size + (size >> 2),
+ PRP_SOURCE_CR_PTR);
+ }
+
+ __raw_writel(__raw_readl(PRP_CNTL) | PRP_CNTL_CH2B2, PRP_CNTL);
+ return 0;
+}
+
+/*!
+ * @brief Build CSC table
+ * @param csc CSC table
+ * in csc[0]=index 0..3 : A.1 A.0 B.1 B.0
+ * csc[1]=direction 0 : YUV2RGB 1 : RGB2YUV
+ * out csc[0..4] are coefficients c[9] is offset
+ * csc[0..8] are coefficients c[9] is offset
+ */
+void csc_tbl(short csc[10])
+{
+ static const unsigned short _r2y[][9] = {
+ {0x4D, 0x4B, 0x3A, 0x57, 0x55, 0x40, 0x40, 0x6B, 0x29},
+ {0x42, 0x41, 0x32, 0x4C, 0x4A, 0x38, 0x38, 0x5E, 0x24},
+ {0x36, 0x5C, 0x25, 0x3B, 0x63, 0x40, 0x40, 0x74, 0x18},
+ {0x2F, 0x4F, 0x20, 0x34, 0x57, 0x38, 0x38, 0x66, 0x15},
+ };
+ static const unsigned short _y2r[][5] = {
+ {0x80, 0xb4, 0x2c, 0x5b, 0x0e4},
+ {0x95, 0xcc, 0x32, 0x68, 0x104},
+ {0x80, 0xca, 0x18, 0x3c, 0x0ec},
+ {0x95, 0xe5, 0x1b, 0x44, 0x1e0},
+ };
+ unsigned short *_csc;
+ int _csclen;
+
+ csc[9] = csc[0] & 1;
+ _csclen = csc[0] & 3;
+
+ if (csc[1]) {
+ _csc = (unsigned short *)_r2y[_csclen];
+ _csclen = sizeof(_r2y[0]);
+ } else {
+ _csc = (unsigned short *)_y2r[_csclen];
+ _csclen = sizeof(_y2r[0]);
+ memset(csc + 5, 0, sizeof(short) * 4);
+ }
+ memcpy(csc, _csc, _csclen);
+}
+
+/*!
+ * @brief Setup PrP resize coefficient registers
+ *
+ * @param ch PrP channel number
+ * @param dir Direction, 0 - horizontal, 1 - vertical
+ * @param scale The pointer to scale_t structure
+ */
+static void prp_set_scaler(int ch, int dir, scale_t * scale)
+{
+ int i;
+ unsigned int coeff[2];
+ unsigned int valid;
+
+ for (coeff[0] = coeff[1] = valid = 0, i = 19; i >= 0; i--) {
+ int j;
+
+ j = i > 9 ? 1 : 0;
+ coeff[j] = (coeff[j] << BC_COEF) |
+ (scale->tbl[i] & (SZ_COEF - 1));
+
+ if (i == 5 || i == 15)
+ coeff[j] <<= 1;
+
+ valid = (valid << 1) | (scale->tbl[i] >> BC_COEF);
+ }
+
+ valid |= (scale->len << 24) | ((2 - scale->algo) << 31);
+
+ for (i = 0; i < 2; i++)
+ (*PRP_RSZ_COEFF)[1 - ch][dir].coeff[i] = coeff[i];
+
+ (*PRP_RSZ_COEFF)[1 - ch][dir].cntl = valid;
+}
+
+/*!
+ * @brief Setup PrP registers relevant to input.
+ * @param cfg Pointer to PrP configuration parameter
+ * @param prp_cntl Holds the value for PrP control register
+ * @return Zero on success, others on failure
+ */
+static int prphw_input_cfg(emma_prp_cfg * cfg, unsigned long *prp_cntl)
+{
+ unsigned long mask;
+
+ switch (cfg->in_pix) {
+ case PRP_PIXIN_YUV420:
+ *prp_cntl |= PRP_CNTL_IN_YUV420;
+ mask = 0x7;
+ break;
+ case PRP_PIXIN_YUYV:
+ case PRP_PIXIN_YVYU:
+ case PRP_PIXIN_UYVY:
+ case PRP_PIXIN_VYUY:
+ *prp_cntl |= PRP_CNTL_IN_YUV422;
+ mask = 0x1;
+ break;
+ case PRP_PIXIN_RGB565:
+ *prp_cntl |= PRP_CNTL_IN_RGB16;
+ mask = 0x1;
+ break;
+ case PRP_PIXIN_RGB888:
+ *prp_cntl |= PRP_CNTL_IN_RGB32;
+ mask = 0;
+ break;
+ default:
+ pr_debug("Unsupported input pix format 0x%08X\n", cfg->in_pix);
+ return -1;
+ }
+
+ /* align the input image width */
+ if (cfg->in_width & mask) {
+ pr_debug("in_width misaligned. in_width=%d\n", cfg->in_width);
+ return -1;
+ }
+
+ if ((cfg->in_width < PRP_MIN_IN_WIDTH)
+ || (cfg->in_width > PRP_MAX_IN_WIDTH)) {
+ pr_debug("Unsupported input width %d\n", cfg->in_width);
+ return -1;
+ }
+
+ cfg->in_height &= ~1; /* truncate to make even */
+
+ if ((cfg->in_height < PRP_MIN_IN_HEIGHT)
+ || (cfg->in_height > PRP_MAX_IN_HEIGHT)) {
+ pr_debug("Unsupported input height %d\n", cfg->in_height);
+ return -1;
+ }
+
+ if (!(cfg->in_csi & PRP_CSI_EN))
+ if (!cfg->in_line_stride)
+ cfg->in_line_stride = cfg->in_width;
+
+ __raw_writel(cfg->in_pix, PRP_SRC_PIXEL_FORMAT_CNTL);
+ __raw_writel((cfg->in_width << 16) | cfg->in_height,
+ PRP_SOURCE_FRAME_SIZE);
+ __raw_writel((cfg->in_line_skip << 16) | cfg->in_line_stride,
+ PRP_SOURCE_LINE_STRIDE);
+
+ if (!(cfg->in_csi & PRP_CSI_EN)) {
+ __raw_writel(cfg->in_ptr, PRP_SOURCE_Y_PTR);
+ if (cfg->in_pix == PRP_PIXIN_YUV420) {
+ unsigned int size;
+
+ size = cfg->in_line_stride * cfg->in_height;
+ __raw_writel(cfg->in_ptr + size, PRP_SOURCE_CB_PTR);
+ __raw_writel(cfg->in_ptr + size + (size >> 2),
+ PRP_SOURCE_CR_PTR);
+ }
+ }
+
+ /* always cropping */
+ *prp_cntl |= PRP_CNTL_WINEN;
+
+ /* color space conversion */
+ if (!cfg->in_csc[1]) {
+ if (cfg->in_csc[0] > 3) {
+ pr_debug("in_csc invalid 0x%X\n", cfg->in_csc[0]);
+ return -1;
+ }
+ if ((cfg->in_pix == PRP_PIXIN_RGB565)
+ || (cfg->in_pix == PRP_PIXIN_RGB888))
+ cfg->in_csc[1] = 1;
+ else
+ cfg->in_csc[0] = 0;
+ csc_tbl(cfg->in_csc);
+ }
+
+ __raw_writel((cfg->in_csc[0] << 21) | (cfg->in_csc[1] << 11)
+ | cfg->in_csc[2], PRP_CSC_COEF_012);
+ __raw_writel((cfg->in_csc[3] << 21) | (cfg->in_csc[4] << 11)
+ | cfg->in_csc[5], PRP_CSC_COEF_345);
+ __raw_writel((cfg->in_csc[6] << 21) | (cfg->in_csc[7] << 11)
+ | cfg->in_csc[8] | (cfg->in_csc[9] << 31),
+ PRP_CSC_COEF_678);
+
+ if (cfg->in_csi & PRP_CSI_EN) {
+ *prp_cntl |= PRP_CNTL_CSI;
+
+ /* loop mode enable, ch1 ch2 together */
+ if ((cfg->in_csi & PRP_CSI_LOOP) == PRP_CSI_LOOP)
+ *prp_cntl |= (PRP_CNTL_CH1_LOOP | PRP_CNTL_CH2_LOOP);
+ }
+
+ return 0;
+}
+
+/*!
+ * @brief Setup PrP registers relevant to channel 2.
+ * @param cfg Pointer to PrP configuration parameter
+ * @param prp_cntl Holds the value for PrP control register
+ * @return Zero on success, others on failure
+ */
+static int prphw_ch2_cfg(emma_prp_cfg * cfg, unsigned long *prp_cntl)
+{
+ switch (cfg->ch2_pix) {
+ case PRP_PIX2_YUV420:
+ *prp_cntl |= PRP_CNTL_CH2_YUV420;
+ break;
+ case PRP_PIX2_YUV422:
+ *prp_cntl |= PRP_CNTL_CH2_YUV422;
+ break;
+ case PRP_PIX2_YUV444:
+ *prp_cntl |= PRP_CNTL_CH2_YUV444;
+ break;
+ case PRP_PIX2_UNUSED:
+ return 0;
+ default:
+ pr_debug("Unsupported channel 2 pix format 0x%08X\n",
+ cfg->ch2_pix);
+ return -1;
+ }
+
+ if (cfg->ch2_pix == PRP_PIX2_YUV420) {
+ cfg->ch2_height &= ~1; /* ensure U/V presence */
+ cfg->ch2_width &= ~7; /* ensure U/V word aligned */
+ } else if (cfg->ch2_pix == PRP_PIX2_YUV422) {
+ cfg->ch2_width &= ~1; /* word aligned */
+ }
+
+ __raw_writel((cfg->ch2_width << 16) | cfg->ch2_height,
+ PRP_CH2_OUT_IMAGE_SIZE);
+
+ if (cfg->ch2_pix == PRP_PIX2_YUV420) {
+ u32 size;
+
+ /* Luminanance band start address */
+ __raw_writel(cfg->ch2_ptr, PRP_DEST_Y_PTR);
+
+ if ((cfg->in_csi & PRP_CSI_LOOP) == PRP_CSI_LOOP) {
+ if (!cfg->ch2_ptr2)
+ __raw_writel(cfg->ch2_ptr, PRP_SOURCE_Y_PTR);
+ else
+ __raw_writel(cfg->ch2_ptr2, PRP_SOURCE_Y_PTR);
+ }
+
+ /* Cb and Cr band start address */
+ size = cfg->ch2_width * cfg->ch2_height;
+ __raw_writel(cfg->ch2_ptr + size, PRP_DEST_CB_PTR);
+ __raw_writel(cfg->ch2_ptr + size + (size >> 2),
+ PRP_DEST_CR_PTR);
+
+ if ((cfg->in_csi & PRP_CSI_LOOP) == PRP_CSI_LOOP) {
+ if (!cfg->ch2_ptr2) {
+ __raw_writel(cfg->ch2_ptr + size,
+ PRP_SOURCE_CB_PTR);
+ __raw_writel(cfg->ch2_ptr + size + (size >> 2),
+ PRP_SOURCE_CR_PTR);
+ } else {
+ __raw_writel(cfg->ch2_ptr2 + size,
+ PRP_SOURCE_CB_PTR);
+ __raw_writel(cfg->ch2_ptr2 + size + (size >> 2),
+ PRP_SOURCE_CR_PTR);
+ }
+ }
+ } else { /* Pixel interleaved YUV422 or YUV444 */
+ __raw_writel(cfg->ch2_ptr, PRP_DEST_Y_PTR);
+
+ if ((cfg->in_csi & PRP_CSI_LOOP) == PRP_CSI_LOOP) {
+ if (!cfg->ch2_ptr2)
+ __raw_writel(cfg->ch2_ptr, PRP_SOURCE_Y_PTR);
+ else
+ __raw_writel(cfg->ch2_ptr2, PRP_SOURCE_Y_PTR);
+ }
+ }
+ *prp_cntl |= PRP_CNTL_CH2B1 | PRP_CNTL_CH2B2;
+
+ return 0;
+}
+
+/*!
+ * @brief Setup PrP registers relevant to channel 1.
+ * @param cfg Pointer to PrP configuration parameter
+ * @param prp_cntl Holds the value for PrP control register
+ * @return Zero on success, others on failure
+ */
+static int prphw_ch1_cfg(emma_prp_cfg * cfg, unsigned long *prp_cntl)
+{
+ int ch1_bpp = 0;
+
+ switch (cfg->ch1_pix) {
+ case PRP_PIX1_RGB332:
+ *prp_cntl |= PRP_CNTL_CH1_RGB8;
+ ch1_bpp = 1;
+ break;
+ case PRP_PIX1_RGB565:
+ *prp_cntl |= PRP_CNTL_CH1_RGB16;
+ ch1_bpp = 2;
+ break;
+ case PRP_PIX1_RGB888:
+ *prp_cntl |= PRP_CNTL_CH1_RGB32;
+ ch1_bpp = 4;
+ break;
+ case PRP_PIX1_YUYV:
+ case PRP_PIX1_YVYU:
+ case PRP_PIX1_UYVY:
+ case PRP_PIX1_VYUY:
+ *prp_cntl |= PRP_CNTL_CH1_YUV422;
+ ch1_bpp = 2;
+ break;
+ case PRP_PIX1_UNUSED:
+ return 0;
+ default:
+ pr_debug("Unsupported channel 1 pix format 0x%08X\n",
+ cfg->ch1_pix);
+ return -1;
+ }
+
+ /* parallel or cascade resize */
+ if (cfg->ch1_scale.algo & PRP_ALGO_BYPASS)
+ *prp_cntl |= PRP_CNTL_UNCHAIN;
+
+ /* word align */
+ if (ch1_bpp == 2)
+ cfg->ch1_width &= ~1;
+ else if (ch1_bpp == 1)
+ cfg->ch1_width &= ~3;
+
+ if (!cfg->ch1_stride)
+ cfg->ch1_stride = cfg->ch1_width;
+
+ __raw_writel(cfg->ch1_pix, PRP_CH1_PIXEL_FORMAT_CNTL);
+ __raw_writel((cfg->ch1_width << 16) | cfg->ch1_height,
+ PRP_CH1_OUT_IMAGE_SIZE);
+ __raw_writel(cfg->ch1_stride * ch1_bpp, PRP_CH1_LINE_STRIDE);
+ __raw_writel(cfg->ch1_ptr, PRP_DEST_RGB1_PTR);
+
+ /* double buffer for loop mode */
+ if ((cfg->in_csi & PRP_CSI_LOOP) == PRP_CSI_LOOP) {
+ if (cfg->ch1_ptr2)
+ __raw_writel(cfg->ch1_ptr2, PRP_DEST_RGB2_PTR);
+ else
+ __raw_writel(cfg->ch1_ptr, PRP_DEST_RGB2_PTR);
+ }
+
+ return 0;
+}
+
+/*!
+ * @brief Setup PrP registers.
+ * @param cfg Pointer to PrP configuration parameter
+ * @return Zero on success, others on failure
+ */
+int prphw_cfg(emma_prp_cfg * cfg)
+{
+ unsigned long prp_cntl = 0;
+ unsigned long val;
+
+ /* input pixel format checking */
+ if (prphw_input_cfg(cfg, &prp_cntl))
+ return -1;
+
+ if (prphw_ch2_cfg(cfg, &prp_cntl))
+ return -1;
+
+ if (prphw_ch1_cfg(cfg, &prp_cntl))
+ return -1;
+
+ /* register setting */
+ __raw_writel(prp_cntl, PRP_CNTL);
+
+ /* interrupt configuration */
+ val = PRP_INTRCNTL_RDERR | PRP_INTRCNTL_LBOVF;
+ if (cfg->ch1_pix != PRP_PIX1_UNUSED)
+ val |= PRP_INTRCNTL_CH1FC | PRP_INTRCNTL_CH1WERR;
+ if (cfg->ch2_pix != PRP_PIX2_UNUSED)
+ val |=
+ PRP_INTRCNTL_CH2FC | PRP_INTRCNTL_CH2WERR |
+ PRP_INTRCNTL_CH2OVF;
+ __raw_writel(val, PRP_INTRCNTL);
+
+ prp_set_scaler(1, 0, &cfg->scale[0]); /* Channel 1 width */
+ prp_set_scaler(1, 1, &cfg->scale[1]); /* Channel 1 height */
+ prp_set_scaler(0, 0, &cfg->scale[2]); /* Channel 2 width */
+ prp_set_scaler(0, 1, &cfg->scale[3]); /* Channel 2 height */
+
+ return 0;
+}
+
+/*!
+ * @brief Check PrP interrupt status.
+ * @return PrP interrupt status
+ */
+int prphw_isr(void)
+{
+ int status;
+
+ status = __raw_readl(PRP_INTRSTATUS) & 0x1FF;
+
+ if (status & (PRP_INTRSTAT_RDERR | PRP_INTRSTAT_CH1WERR |
+ PRP_INTRSTAT_CH2WERR))
+ pr_debug("isr bus error. status= 0x%08X\n", status);
+ else if (status & PRP_INTRSTAT_CH2OVF)
+ pr_debug("isr ch 2 buffer overflow. status= 0x%08X\n", status);
+ else if (status & PRP_INTRSTAT_LBOVF)
+ pr_debug("isr line buffer overflow. status= 0x%08X\n", status);
+
+ /* silicon bug?? enable bit does not self clear? */
+ if (!(__raw_readl(PRP_CNTL) & PRP_CNTL_CH1_LOOP))
+ __raw_writel(__raw_readl(PRP_CNTL) & (~PRP_CNTL_CH1EN),
+ PRP_CNTL);
+ if (!(__raw_readl(PRP_CNTL) & PRP_CNTL_CH2_LOOP))
+ __raw_writel(__raw_readl(PRP_CNTL) & (~PRP_CNTL_CH2EN),
+ PRP_CNTL);
+
+ __raw_writel(status, PRP_INTRSTATUS); /* clr irq */
+
+ return status;
+}
+
+static struct clk *emma_clk;
+
+/*!
+ * @brief PrP module clock enable
+ */
+void prphw_init(void)
+{
+ emma_clk = clk_get(NULL, "emma_clk");
+ clk_enable(emma_clk);
+}
+
+/*!
+ * @brief PrP module clock disable
+ */
+void prphw_exit(void)
+{
+ clk_disable(emma_clk);
+ clk_put(emma_clk);
+}
diff --git a/drivers/media/video/mxc/capture/mx27_prpsw.c b/drivers/media/video/mxc/capture/mx27_prpsw.c
new file mode 100644
index 000000000000..eca200a580f2
--- /dev/null
+++ b/drivers/media/video/mxc/capture/mx27_prpsw.c
@@ -0,0 +1,1042 @@
+/*
+ * Copyright 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file mx27_prpsw.c
+ *
+ * @brief MX27 Video For Linux 2 capture driver
+ *
+ * @ingroup MXC_V4L2_CAPTURE
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/fb.h>
+#include <linux/pci.h>
+#include <asm/cacheflush.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include "mxc_v4l2_capture.h"
+#include "mx27_prp.h"
+#include "mx27_csi.h"
+#include "../drivers/video/mxc/mx2fb.h"
+#include "../opl/opl.h"
+
+#define MEAN_COEF (SZ_COEF >> 1)
+
+static char prp_dev[] = "emma_prp";
+static int g_still_on = 0;
+static emma_prp_cfg g_prp_cfg;
+static int g_vfbuf, g_rotbuf;
+static struct tasklet_struct prp_vf_tasklet;
+
+/*
+ * The following variables represents the virtual address for the cacheable
+ * buffers accessed by SW rotation/mirroring. The rotation/mirroring in
+ * cacheable buffers has significant performance improvement than it in
+ * non-cacheable buffers.
+ */
+static char *g_vaddr_vfbuf[2] = { 0, 0 };
+static char *g_vaddr_rotbuf[2] = { 0, 0 };
+static char *g_vaddr_fb = 0;
+
+static int set_ch1_addr(emma_prp_cfg * cfg, cam_data * cam);
+static int prp_v4l2_cfg(emma_prp_cfg * cfg, cam_data * cam);
+static int prp_vf_mem_alloc(cam_data * cam);
+static void prp_vf_mem_free(cam_data * cam);
+static int prp_rot_mem_alloc(cam_data * cam);
+static void prp_rot_mem_free(cam_data * cam);
+static int prp_enc_update_eba(u32 eba, int *buffer_num);
+static int prp_enc_enable(void *private);
+static int prp_enc_disable(void *private);
+static int prp_vf_start(void *private);
+static int prp_vf_stop(void *private);
+static int prp_still_start(void *private);
+static int prp_still_stop(void *private);
+static irqreturn_t prp_isr(int irq, void *dev_id);
+static void rotation(unsigned long private);
+static int prp_resize_check_ch1(emma_prp_cfg * cfg);
+static int prp_resize_check_ch2(emma_prp_cfg * cfg);
+
+#define PRP_DUMP(val) pr_debug("%s\t = 0x%08X\t%d\n", #val, val, val)
+
+/*!
+ * @brief Dump PrP configuration parameters.
+ * @param cfg The pointer to PrP configuration parameter
+ */
+static void prp_cfg_dump(emma_prp_cfg * cfg)
+{
+ PRP_DUMP(cfg->in_pix);
+ PRP_DUMP(cfg->in_width);
+ PRP_DUMP(cfg->in_height);
+ PRP_DUMP(cfg->in_csi);
+ PRP_DUMP(cfg->in_line_stride);
+ PRP_DUMP(cfg->in_line_skip);
+ PRP_DUMP(cfg->in_ptr);
+
+ PRP_DUMP(cfg->ch1_pix);
+ PRP_DUMP(cfg->ch1_width);
+ PRP_DUMP(cfg->ch1_height);
+ PRP_DUMP(cfg->ch1_scale.algo);
+ PRP_DUMP(cfg->ch1_scale.width.num);
+ PRP_DUMP(cfg->ch1_scale.width.den);
+ PRP_DUMP(cfg->ch1_scale.height.num);
+ PRP_DUMP(cfg->ch1_scale.height.den);
+ PRP_DUMP(cfg->ch1_stride);
+ PRP_DUMP(cfg->ch1_ptr);
+ PRP_DUMP(cfg->ch1_ptr2);
+ PRP_DUMP(cfg->ch1_csi);
+
+ PRP_DUMP(cfg->ch2_pix);
+ PRP_DUMP(cfg->ch2_width);
+ PRP_DUMP(cfg->ch2_height);
+ PRP_DUMP(cfg->ch2_scale.algo);
+ PRP_DUMP(cfg->ch2_scale.width.num);
+ PRP_DUMP(cfg->ch2_scale.width.den);
+ PRP_DUMP(cfg->ch2_scale.height.num);
+ PRP_DUMP(cfg->ch2_scale.height.den);
+ PRP_DUMP(cfg->ch2_ptr);
+ PRP_DUMP(cfg->ch2_ptr2);
+ PRP_DUMP(cfg->ch2_csi);
+}
+
+/*!
+ * @brief Set PrP channel 1 output address.
+ * @param cfg Pointer to emma_prp_cfg structure
+ * @param cam Pointer to cam_data structure
+ * @return Zero on success, others on failure
+ */
+static int set_ch1_addr(emma_prp_cfg * cfg, cam_data * cam)
+{
+ if (cam->rotation != V4L2_MXC_ROTATE_NONE) {
+ cfg->ch1_ptr = (unsigned int)cam->rot_vf_bufs[0];
+ cfg->ch1_ptr2 = (unsigned int)cam->rot_vf_bufs[1];
+ if ((cam->rotation == V4L2_MXC_ROTATE_90_RIGHT)
+ || (cam->rotation == V4L2_MXC_ROTATE_90_RIGHT_VFLIP)
+ || (cam->rotation == V4L2_MXC_ROTATE_90_RIGHT_HFLIP)
+ || (cam->rotation == V4L2_MXC_ROTATE_90_LEFT))
+ cfg->ch1_stride = cam->win.w.height;
+ else
+ cfg->ch1_stride = cam->win.w.width;
+
+ if (cam->v4l2_fb.flags != V4L2_FBUF_FLAG_OVERLAY) {
+ struct fb_info *fb = cam->overlay_fb;
+ if (!fb)
+ return -1;
+ if (g_vaddr_fb)
+ iounmap(g_vaddr_fb);
+ g_vaddr_fb = ioremap_cached(fb->fix.smem_start,
+ fb->fix.smem_len);
+ if (!g_vaddr_fb)
+ return -1;
+ }
+ } else if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY) {
+ cfg->ch1_ptr = (unsigned int)cam->vf_bufs[0];
+ cfg->ch1_ptr2 = (unsigned int)cam->vf_bufs[1];
+ cfg->ch1_stride = cam->win.w.width;
+ } else {
+ struct fb_info *fb = cam->overlay_fb;
+
+ if (!fb)
+ return -1;
+
+ cfg->ch1_ptr = fb->fix.smem_start;
+ cfg->ch1_ptr += cam->win.w.top * fb->var.xres_virtual
+ * (fb->var.bits_per_pixel >> 3)
+ + cam->win.w.left * (fb->var.bits_per_pixel >> 3);
+ cfg->ch1_ptr2 = cfg->ch1_ptr;
+ cfg->ch1_stride = fb->var.xres_virtual;
+ }
+
+ return 0;
+}
+
+/*!
+ * @brief Setup PrP configuration parameters.
+ * @param cfg Pointer to emma_prp_cfg structure
+ * @param cam Pointer to cam_data structure
+ * @return Zero on success, others on failure
+ */
+static int prp_v4l2_cfg(emma_prp_cfg * cfg, cam_data * cam)
+{
+ cfg->in_pix = PRP_PIXIN_YUYV;
+ cfg->in_width = cam->crop_current.width;
+ cfg->in_height = cam->crop_current.height;
+ cfg->in_line_stride = cam->crop_current.left;
+ cfg->in_line_skip = cam->crop_current.top;
+ cfg->in_ptr = 0;
+ cfg->in_csi = PRP_CSI_LOOP;
+ memset(cfg->in_csc, 0, sizeof(cfg->in_csc));
+
+ if (cam->overlay_on) {
+ /* Convert V4L2 pixel format to PrP pixel format */
+ switch (cam->v4l2_fb.fmt.pixelformat) {
+ case V4L2_PIX_FMT_RGB332:
+ cfg->ch1_pix = PRP_PIX1_RGB332;
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ case V4L2_PIX_FMT_BGR32:
+ cfg->ch1_pix = PRP_PIX1_RGB888;
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ cfg->ch1_pix = PRP_PIX1_YUYV;
+ break;
+ case V4L2_PIX_FMT_UYVY:
+ cfg->ch1_pix = PRP_PIX1_UYVY;
+ break;
+ case V4L2_PIX_FMT_RGB565:
+ default:
+ cfg->ch1_pix = PRP_PIX1_RGB565;
+ break;
+ }
+ if ((cam->rotation == V4L2_MXC_ROTATE_90_RIGHT)
+ || (cam->rotation == V4L2_MXC_ROTATE_90_RIGHT_VFLIP)
+ || (cam->rotation == V4L2_MXC_ROTATE_90_RIGHT_HFLIP)
+ || (cam->rotation == V4L2_MXC_ROTATE_90_LEFT)) {
+ cfg->ch1_width = cam->win.w.height;
+ cfg->ch1_height = cam->win.w.width;
+ } else {
+ cfg->ch1_width = cam->win.w.width;
+ cfg->ch1_height = cam->win.w.height;
+ }
+
+ if (set_ch1_addr(cfg, cam))
+ return -1;
+ } else {
+ cfg->ch1_pix = PRP_PIX1_UNUSED;
+ cfg->ch1_width = cfg->in_width;
+ cfg->ch1_height = cfg->in_height;
+ }
+ cfg->ch1_scale.algo = 0;
+ cfg->ch1_scale.width.num = cfg->in_width;
+ cfg->ch1_scale.width.den = cfg->ch1_width;
+ cfg->ch1_scale.height.num = cfg->in_height;
+ cfg->ch1_scale.height.den = cfg->ch1_height;
+ cfg->ch1_csi = PRP_CSI_EN;
+
+ if (cam->capture_on || g_still_on) {
+ switch (cam->v2f.fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_YUYV:
+ cfg->ch2_pix = PRP_PIX2_YUV422;
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ cfg->ch2_pix = PRP_PIX2_YUV420;
+ break;
+ /*
+ * YUV444 is not defined by V4L2.
+ * We support it in default case.
+ */
+ default:
+ cfg->ch2_pix = PRP_PIX2_YUV444;
+ break;
+ }
+ cfg->ch2_width = cam->v2f.fmt.pix.width;
+ cfg->ch2_height = cam->v2f.fmt.pix.height;
+ } else {
+ cfg->ch2_pix = PRP_PIX2_UNUSED;
+ cfg->ch2_width = cfg->in_width;
+ cfg->ch2_height = cfg->in_height;
+ }
+ cfg->ch2_scale.algo = 0;
+ cfg->ch2_scale.width.num = cfg->in_width;
+ cfg->ch2_scale.width.den = cfg->ch2_width;
+ cfg->ch2_scale.height.num = cfg->in_height;
+ cfg->ch2_scale.height.den = cfg->ch2_height;
+ cfg->ch2_csi = PRP_CSI_EN;
+
+ memset(cfg->scale, 0, sizeof(cfg->scale));
+ cfg->scale[0].algo = cfg->ch1_scale.algo & 3;
+ cfg->scale[1].algo = (cfg->ch1_scale.algo >> 2) & 3;
+ cfg->scale[2].algo = cfg->ch2_scale.algo & 3;
+ cfg->scale[3].algo = (cfg->ch2_scale.algo >> 2) & 3;
+
+ prp_cfg_dump(cfg);
+
+ if (prp_resize_check_ch2(cfg))
+ return -1;
+
+ if (prp_resize_check_ch1(cfg))
+ return -1;
+
+ return 0;
+}
+
+/*!
+ * @brief PrP interrupt handler
+ */
+static irqreturn_t prp_isr(int irq, void *dev_id)
+{
+ int status;
+ cam_data *cam = (cam_data *) dev_id;
+
+ status = prphw_isr();
+
+ if (g_still_on && (status & PRP_INTRSTAT_CH2BUF1)) {
+ prp_still_stop(cam);
+ cam->still_counter++;
+ wake_up_interruptible(&cam->still_queue);
+ /*
+ * Still & video capture use the same PrP channel 2.
+ * They are execlusive.
+ */
+ } else if (cam->capture_on) {
+ if (status & (PRP_INTRSTAT_CH2BUF1 | PRP_INTRSTAT_CH2BUF2)) {
+ cam->enc_callback(0, cam);
+ }
+ }
+ if (cam->overlay_on
+ && (status & (PRP_INTRSTAT_CH1BUF1 | PRP_INTRSTAT_CH1BUF2))) {
+ if (cam->rotation != V4L2_MXC_ROTATE_NONE) {
+ g_rotbuf = (status & PRP_INTRSTAT_CH1BUF1) ? 0 : 1;
+ tasklet_schedule(&prp_vf_tasklet);
+ } else if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY) {
+ struct fb_gwinfo gwinfo;
+
+ gwinfo.enabled = 1;
+ gwinfo.alpha_value = 255;
+ gwinfo.ck_enabled = 0;
+ gwinfo.xpos = cam->win.w.left;
+ gwinfo.ypos = cam->win.w.top;
+ gwinfo.xres = cam->win.w.width;
+ gwinfo.yres = cam->win.w.height;
+ gwinfo.xres_virtual = cam->win.w.width;
+ gwinfo.vs_reversed = 0;
+ if (status & PRP_INTRSTAT_CH1BUF1)
+ gwinfo.base = (unsigned long)cam->vf_bufs[0];
+ else
+ gwinfo.base = (unsigned long)cam->vf_bufs[1];
+
+ mx2_gw_set(&gwinfo);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*!
+ * @brief PrP initialization.
+ * @param dev_id Pointer to cam_data structure
+ * @return Zero on success, others on failure
+ */
+int prp_init(void *dev_id)
+{
+ enable_irq(MXC_INT_EMMAPRP);
+ if (request_irq(MXC_INT_EMMAPRP, prp_isr, 0, prp_dev, dev_id))
+ return -1;
+ prphw_init();
+
+ return 0;
+}
+
+/*!
+ * @brief PrP initialization.
+ * @param dev_id Pointer to cam_data structure
+ */
+void prp_exit(void *dev_id)
+{
+ prphw_exit();
+ disable_irq(MXC_INT_EMMAPRP);
+ free_irq(MXC_INT_EMMAPRP, dev_id);
+}
+
+/*!
+ * @brief Update PrP channel 2 output buffer address.
+ * @param eba Physical address for PrP output buffer
+ * @param buffer_num The PrP channel 2 buffer number to be updated
+ * @return Zero on success, others on failure
+ */
+static int prp_enc_update_eba(u32 eba, int *buffer_num)
+{
+ if (*buffer_num) {
+ g_prp_cfg.ch2_ptr2 = eba;
+ prphw_ch2ptr2(&g_prp_cfg);
+ *buffer_num = 0;
+ } else {
+ g_prp_cfg.ch2_ptr = eba;
+ prphw_ch2ptr(&g_prp_cfg);
+ *buffer_num = 1;
+ }
+
+ return 0;
+}
+
+/*!
+ * @brief Enable PrP for encoding.
+ * @param private Pointer to cam_data structure
+ * @return Zero on success, others on failure
+ */
+static int prp_enc_enable(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+
+ if (prp_v4l2_cfg(&g_prp_cfg, cam))
+ return -1;
+
+ csi_enable_mclk(CSI_MCLK_ENC, true, true);
+ prphw_reset();
+
+ if (prphw_cfg(&g_prp_cfg))
+ return -1;
+
+ prphw_enable(cam->overlay_on ? (PRP_CHANNEL_1 | PRP_CHANNEL_2)
+ : PRP_CHANNEL_2);
+
+ return 0;
+}
+
+/*!
+ * @brief Disable PrP for encoding.
+ * @param private Pointer to cam_data structure
+ * @return Zero on success, others on failure
+ */
+static int prp_enc_disable(void *private)
+{
+ prphw_disable(PRP_CHANNEL_2);
+ csi_enable_mclk(CSI_MCLK_ENC, false, false);
+
+ return 0;
+}
+
+/*!
+ * @brief Setup encoding functions.
+ * @param private Pointer to cam_data structure
+ * @return Zero on success, others on failure
+ */
+int prp_enc_select(void *private)
+{
+ int ret = 0;
+ cam_data *cam = (cam_data *) private;
+
+ if (cam) {
+ cam->enc_update_eba = prp_enc_update_eba;
+ cam->enc_enable = prp_enc_enable;
+ cam->enc_disable = prp_enc_disable;
+ } else
+ ret = -EIO;
+
+ return ret;
+}
+
+/*!
+ * @brief Uninstall encoding functions.
+ * @param private Pointer to cam_data structure
+ * @return Zero on success, others on failure
+ */
+int prp_enc_deselect(void *private)
+{
+ int ret = 0;
+ cam_data *cam = (cam_data *) private;
+
+ ret = prp_enc_disable(private);
+
+ if (cam) {
+ cam->enc_update_eba = NULL;
+ cam->enc_enable = NULL;
+ cam->enc_disable = NULL;
+ }
+
+ return ret;
+}
+
+/*!
+ * @brief Allocate memory for overlay.
+ * @param cam Pointer to cam_data structure
+ * @return Zero on success, others on failure
+ */
+static int prp_vf_mem_alloc(cam_data * cam)
+{
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ cam->vf_bufs_size[i] = cam->win.w.width * cam->win.w.height * 2;
+ cam->vf_bufs_vaddr[i] = dma_alloc_coherent(0,
+ cam->vf_bufs_size[i],
+ &cam->vf_bufs[i],
+ GFP_DMA |
+ GFP_KERNEL);
+ if (!cam->vf_bufs_vaddr[i]) {
+ pr_debug("Failed to alloc memory for vf.\n");
+ prp_vf_mem_free(cam);
+ return -1;
+ }
+
+ g_vaddr_vfbuf[i] =
+ ioremap_cached(cam->vf_bufs[i], cam->vf_bufs_size[i]);
+ if (!g_vaddr_vfbuf[i]) {
+ pr_debug("Failed to ioremap_cached() for vf.\n");
+ prp_vf_mem_free(cam);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/*!
+ * @brief Free memory for overlay.
+ * @param cam Pointer to cam_data structure
+ * @return Zero on success, others on failure
+ */
+static void prp_vf_mem_free(cam_data * cam)
+{
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ if (cam->vf_bufs_vaddr[i]) {
+ dma_free_coherent(0,
+ cam->vf_bufs_size[i],
+ cam->vf_bufs_vaddr[i],
+ cam->vf_bufs[i]);
+ }
+ cam->vf_bufs[i] = 0;
+ cam->vf_bufs_vaddr[i] = 0;
+ cam->vf_bufs_size[i] = 0;
+ if (g_vaddr_vfbuf[i]) {
+ iounmap(g_vaddr_vfbuf[i]);
+ g_vaddr_vfbuf[i] = 0;
+ }
+ }
+}
+
+/*!
+ * @brief Allocate intermediate memory for overlay rotation/mirroring.
+ * @param cam Pointer to cam_data structure
+ * @return Zero on success, others on failure
+ */
+static int prp_rot_mem_alloc(cam_data * cam)
+{
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ cam->rot_vf_buf_size[i] =
+ cam->win.w.width * cam->win.w.height * 2;
+ cam->rot_vf_bufs_vaddr[i] =
+ dma_alloc_coherent(0, cam->rot_vf_buf_size[i],
+ &cam->rot_vf_bufs[i],
+ GFP_DMA | GFP_KERNEL);
+ if (!cam->rot_vf_bufs_vaddr[i]) {
+ pr_debug("Failed to alloc memory for vf rotation.\n");
+ prp_rot_mem_free(cam);
+ return -1;
+ }
+
+ g_vaddr_rotbuf[i] =
+ ioremap_cached(cam->rot_vf_bufs[i],
+ cam->rot_vf_buf_size[i]);
+ if (!g_vaddr_rotbuf[i]) {
+ pr_debug
+ ("Failed to ioremap_cached() for rotation buffer.\n");
+ prp_rot_mem_free(cam);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/*!
+ * @brief Free intermedaite memory for overlay rotation/mirroring.
+ * @param cam Pointer to cam_data structure
+ * @return Zero on success, others on failure
+ */
+static void prp_rot_mem_free(cam_data * cam)
+{
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ if (cam->rot_vf_bufs_vaddr[i]) {
+ dma_free_coherent(0,
+ cam->rot_vf_buf_size[i],
+ cam->rot_vf_bufs_vaddr[i],
+ cam->rot_vf_bufs[i]);
+ }
+ cam->rot_vf_bufs[i] = 0;
+ cam->rot_vf_bufs_vaddr[i] = 0;
+ cam->rot_vf_buf_size[i] = 0;
+ if (g_vaddr_rotbuf[i]) {
+ iounmap(g_vaddr_rotbuf[i]);
+ g_vaddr_rotbuf[i] = 0;
+ }
+ }
+}
+
+/*!
+ * @brief Start overlay (view finder).
+ * @param private Pointer to cam_data structure
+ * @return Zero on success, others on failure
+ */
+static int prp_vf_start(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+
+ if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY) {
+ prp_vf_mem_free(cam);
+ if (prp_vf_mem_alloc(cam)) {
+ pr_info("Error to allocate vf buffer\n");
+ return -ENOMEM;
+ }
+ }
+
+ if (cam->rotation != V4L2_MXC_ROTATE_NONE) {
+ prp_rot_mem_free(cam);
+ if (prp_rot_mem_alloc(cam)) {
+ pr_info("Error to allocate rotation buffer\n");
+ prp_vf_mem_free(cam);
+ return -ENOMEM;
+ }
+ }
+
+ if (prp_v4l2_cfg(&g_prp_cfg, cam)) {
+ prp_vf_mem_free(cam);
+ prp_rot_mem_free(cam);
+ return -1;
+ }
+
+ csi_enable_mclk(CSI_MCLK_VF, true, true);
+ prphw_reset();
+
+ if (prphw_cfg(&g_prp_cfg)) {
+ prp_vf_mem_free(cam);
+ prp_rot_mem_free(cam);
+ return -1;
+ }
+ g_vfbuf = g_rotbuf = 0;
+ tasklet_init(&prp_vf_tasklet, rotation, (unsigned long)private);
+
+ prphw_enable(cam->capture_on ? (PRP_CHANNEL_1 | PRP_CHANNEL_2)
+ : PRP_CHANNEL_1);
+
+ return 0;
+}
+
+/*!
+ * @brief Stop overlay (view finder).
+ * @param private Pointer to cam_data structure
+ * @return Zero on success, others on failure
+ */
+static int prp_vf_stop(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+
+ prphw_disable(PRP_CHANNEL_1);
+
+ csi_enable_mclk(CSI_MCLK_VF, false, false);
+ tasklet_kill(&prp_vf_tasklet);
+
+ if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY) {
+ struct fb_gwinfo gwinfo;
+
+ /* Disable graphic window */
+ gwinfo.enabled = 0;
+ mx2_gw_set(&gwinfo);
+
+ prp_vf_mem_free(cam);
+ }
+ prp_rot_mem_free(cam);
+ if (g_vaddr_fb) {
+ iounmap(g_vaddr_fb);
+ g_vaddr_fb = 0;
+ }
+
+ return 0;
+}
+
+/*!
+ * @brief Setup overlay functions.
+ * @param private Pointer to cam_data structure
+ * @return Zero on success, others on failure
+ */
+int prp_vf_select(void *private)
+{
+ int ret = 0;
+ cam_data *cam = (cam_data *) private;
+
+ if (cam) {
+ cam->vf_start_sdc = prp_vf_start;
+ cam->vf_stop_sdc = prp_vf_stop;
+ cam->overlay_active = false;
+ } else
+ ret = -EIO;
+
+ return ret;
+}
+
+/*!
+ * @brief Uninstall overlay functions.
+ * @param private Pointer to cam_data structure
+ * @return Zero on success, others on failure
+ */
+int prp_vf_deselect(void *private)
+{
+ int ret = 0;
+ cam_data *cam = (cam_data *) private;
+
+ ret = prp_vf_stop(private);
+
+ if (cam) {
+ cam->vf_start_sdc = NULL;
+ cam->vf_stop_sdc = NULL;
+ }
+
+ return ret;
+}
+
+/*!
+ * @brief Start still picture capture.
+ * @param private Pointer to cam_data structure
+ * @return Zero on success, others on failure
+ */
+static int prp_still_start(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+
+ g_still_on = 1;
+ g_prp_cfg.ch2_ptr = (unsigned int)cam->still_buf[0];
+ g_prp_cfg.ch2_ptr2 = 0;
+
+ if (prp_v4l2_cfg(&g_prp_cfg, cam))
+ return -1;
+
+ csi_enable_mclk(CSI_MCLK_RAW, true, true);
+ prphw_reset();
+
+ if (prphw_cfg(&g_prp_cfg)) {
+ g_still_on = 0;
+ return -1;
+ }
+
+ prphw_enable(cam->overlay_on ? (PRP_CHANNEL_1 | PRP_CHANNEL_2)
+ : PRP_CHANNEL_2);
+
+ return 0;
+}
+
+/*!
+ * @brief Stop still picture capture.
+ * @param private Pointer to cam_data structure
+ * @return Zero on success, others on failure
+ */
+static int prp_still_stop(void *private)
+{
+ prphw_disable(PRP_CHANNEL_2);
+
+ csi_enable_mclk(CSI_MCLK_RAW, false, false);
+
+ g_still_on = 0;
+
+ return 0;
+}
+
+/*!
+ * @brief Setup functions for still picture capture.
+ * @param private Pointer to cam_data structure
+ * @return Zero on success, others on failure
+ */
+int prp_still_select(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+
+ if (cam) {
+ cam->csi_start = prp_still_start;
+ cam->csi_stop = prp_still_stop;
+ }
+
+ return 0;
+}
+
+/*!
+ * @brief Uninstall functions for still picture capture.
+ * @param private Pointer to cam_data structure
+ * @return Zero on success, others on failure
+ */
+int prp_still_deselect(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+ int err = 0;
+
+ err = prp_still_stop(cam);
+
+ if (cam) {
+ cam->csi_start = NULL;
+ cam->csi_stop = NULL;
+ }
+
+ return err;
+}
+
+/*!
+ * @brief Perform software rotation or mirroring
+ * @param private Argument passed to the tasklet
+ */
+static void rotation(unsigned long private)
+{
+ char *src, *dst;
+ int width, height, s_stride, d_stride;
+ int size;
+ cam_data *cam = (cam_data *) private;
+
+ src = g_vaddr_rotbuf[g_rotbuf];
+ size = cam->rot_vf_buf_size[g_rotbuf];
+
+ if ((cam->rotation == V4L2_MXC_ROTATE_90_RIGHT)
+ || (cam->rotation == V4L2_MXC_ROTATE_90_RIGHT_VFLIP)
+ || (cam->rotation == V4L2_MXC_ROTATE_90_RIGHT_HFLIP)
+ || (cam->rotation == V4L2_MXC_ROTATE_90_LEFT)) {
+ width = cam->win.w.height;
+ height = cam->win.w.width;
+ s_stride = cam->win.w.height << 1;
+ } else {
+ width = cam->win.w.width;
+ height = cam->win.w.height;
+ s_stride = cam->win.w.width << 1;
+ }
+
+ if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY) {
+ dst = g_vaddr_vfbuf[g_vfbuf];
+ d_stride = cam->win.w.width << 1;
+ } else { /* The destination is the framebuffer */
+ struct fb_info *fb = cam->overlay_fb;
+ if (!fb)
+ return;
+ dst = g_vaddr_fb;
+ dst += cam->win.w.top * fb->var.xres_virtual
+ * (fb->var.bits_per_pixel >> 3)
+ + cam->win.w.left * (fb->var.bits_per_pixel >> 3);
+ d_stride = fb->var.xres_virtual << 1;
+ }
+
+ /*
+ * Invalidate the data in cache before performing the SW rotaion
+ * or mirroring in case the image size is less than QVGA. For image
+ * larger than QVGA it is not invalidated becase the invalidation
+ * will consume much time while we don't see any artifacts on the
+ * output if we don't perform invalidation for them.
+ * Similarly we don't flush the data after SW rotation/mirroring.
+ */
+ if (size < 320 * 240 * 2)
+ dmac_inv_range(src, src + size);
+ switch (cam->rotation) {
+ case V4L2_MXC_ROTATE_VERT_FLIP:
+ opl_vmirror_u16(src, s_stride, width, height, dst, d_stride);
+ break;
+ case V4L2_MXC_ROTATE_HORIZ_FLIP:
+ opl_hmirror_u16(src, s_stride, width, height, dst, d_stride);
+ break;
+ case V4L2_MXC_ROTATE_180:
+ opl_rotate180_u16(src, s_stride, width, height, dst, d_stride);
+ break;
+ case V4L2_MXC_ROTATE_90_RIGHT:
+ opl_rotate90_u16(src, s_stride, width, height, dst, d_stride);
+ break;
+ case V4L2_MXC_ROTATE_90_RIGHT_VFLIP:
+ opl_rotate90_vmirror_u16(src, s_stride, width, height, dst,
+ d_stride);
+ break;
+ case V4L2_MXC_ROTATE_90_RIGHT_HFLIP:
+ /* ROTATE_90_RIGHT_HFLIP = ROTATE_270_RIGHT_VFLIP */
+ opl_rotate270_vmirror_u16(src, s_stride, width, height, dst,
+ d_stride);
+ break;
+ case V4L2_MXC_ROTATE_90_LEFT:
+ opl_rotate270_u16(src, s_stride, width, height, dst, d_stride);
+ break;
+ default:
+ return;
+ }
+
+ /* Config and display the graphic window */
+ if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY) {
+ struct fb_gwinfo gwinfo;
+
+ gwinfo.enabled = 1;
+ gwinfo.alpha_value = 255;
+ gwinfo.ck_enabled = 0;
+ gwinfo.xpos = cam->win.w.left;
+ gwinfo.ypos = cam->win.w.top;
+ gwinfo.xres = cam->win.w.width;
+ gwinfo.yres = cam->win.w.height;
+ gwinfo.xres_virtual = cam->win.w.width;
+ gwinfo.vs_reversed = 0;
+ gwinfo.base = (unsigned long)cam->vf_bufs[g_vfbuf];
+ mx2_gw_set(&gwinfo);
+
+ g_vfbuf = g_vfbuf ? 0 : 1;
+ }
+}
+
+/*
+ * @brief Check if the resize ratio is supported based on the input and output
+ * dimension
+ * @param input input dimension
+ * @param output output dimension
+ * @return output dimension (should equal the parameter *output*)
+ * -1 on failure
+ */
+static int check_simple(scale_t * scale, int input, int output)
+{
+ unsigned short int_out; /* PrP internel width or height */
+ unsigned short orig_out = output;
+
+ if (prp_scale(scale, input, output, input, &orig_out, &int_out, 0))
+ return -1; /* resize failed */
+ else
+ return int_out;
+}
+
+/*
+ * @brief Check if the resize ratio is supported based on the input and output
+ * dimension
+ * @param input input dimension
+ * @param output output dimension
+ * @return output dimension, may be rounded.
+ * -1 on failure
+ */
+static int check_simple_retry(scale_t * scale, int input, int output)
+{
+ unsigned short int_out; /* PrP internel width or height */
+ unsigned short orig_out = output;
+
+ if (prp_scale(scale, input, output, input, &orig_out, &int_out,
+ SCALE_RETRY))
+ return -1; /* resize failed */
+ else
+ return int_out;
+}
+
+/*!
+ * @brief Check if the resize ratio is supported by PrP channel 1
+ * @param cfg Pointer to emma_prp_cfg structure
+ * @return Zero on success, others on failure
+ */
+static int prp_resize_check_ch1(emma_prp_cfg * cfg)
+{
+ int in_w, in_h, ch1_w, ch1_h, ch2_w, ch2_h, w, h;
+ scale_t *pscale = &cfg->scale[0]; /* Ch1 width resize coeff */
+
+ if (cfg->ch1_pix == PRP_PIX1_UNUSED)
+ return 0;
+
+ in_w = cfg->in_width;
+ in_h = cfg->in_height;
+ ch1_w = cfg->ch1_width;
+ ch1_h = cfg->ch1_height;
+ ch2_w = cfg->ch2_width;
+ ch2_h = cfg->ch2_height;
+
+ /*
+ * For channel 1, try parallel resize first. If the resize
+ * ratio is not exactly supported, try cascade resize. If it
+ * still fails, use parallel resize but with rounded value.
+ */
+ w = check_simple(pscale, in_w, ch1_w);
+ h = check_simple(pscale + 1, in_h, ch1_h);
+ if ((w == ch1_w) && (h == ch1_h))
+ goto exit_parallel;
+
+ if (cfg->ch2_pix != PRP_PIX2_UNUSED) {
+ /*
+ * Channel 2 is already used. The pscale is still pointing
+ * to ch1 resize coeff for temporary use.
+ */
+ w = check_simple(pscale, in_w, ch2_w);
+ h = check_simple(pscale + 1, in_h, ch2_h);
+ if ((w == ch2_w) && (h == ch2_h)) {
+ /* Try cascade resize now */
+ w = check_simple(pscale, ch2_w, ch1_w);
+ h = check_simple(pscale + 1, ch2_h, ch1_h);
+ if ((w == ch1_w) && (h == ch1_h))
+ goto exit_cascade;
+ }
+ } else {
+ /*
+ * Try cascade resize for width, width is multiple of 2.
+ * Channel 2 is not used. So we have more values to pick
+ * for channel 2 resize.
+ */
+ for (w = in_w - 2; w > ch1_w; w -= 2) {
+ /* Ch2 width resize */
+ if (check_simple(pscale + 2, in_w, w) != w)
+ continue;
+ /* Ch1 width resize */
+ if (check_simple(pscale, w, ch1_w) != ch1_w)
+ continue;
+ break;
+ }
+ if ((ch2_w = w) > ch1_w) {
+ /* try cascade resize for height */
+ for (h = in_h - 1; h > ch1_h; h--) {
+ /* Ch2 height resize */
+ if (check_simple(pscale + 3, in_h, h) != h)
+ continue;
+ /* Ch1 height resize */
+ if (check_simple(pscale + 1, h, ch1_h) != ch1_h)
+ continue;
+ break;
+ }
+ if ((ch2_h = h) > ch1_h)
+ goto exit_cascade;
+ }
+ }
+
+ /* Have to try parallel resize again and round the dimensions */
+ w = check_simple_retry(pscale, in_w, ch1_w);
+ h = check_simple_retry(pscale + 1, in_h, ch1_h);
+ if ((w != -1) && (h != -1))
+ goto exit_parallel;
+
+ pr_debug("Ch1 resize error.\n");
+ return -1;
+
+ exit_parallel:
+ cfg->ch1_scale.algo |= PRP_ALGO_BYPASS;
+ pr_debug("ch1 parallel resize.\n");
+ pr_debug("original width = %d internel width = %d\n", ch1_w, w);
+ pr_debug("original height = %d internel height = %d\n", ch1_h, h);
+ return 0;
+
+ exit_cascade:
+ cfg->ch1_scale.algo &= ~PRP_ALGO_BYPASS;
+ pr_debug("ch1 cascade resize.\n");
+ pr_debug("[width] in : ch2 : ch1=%d : %d : %d\n", in_w, ch2_w, ch1_w);
+ pr_debug("[height] in : ch2 : ch1=%d : %d : %d\n", in_h, ch2_h, ch1_h);
+ return 0;
+}
+
+/*!
+ * @brief Check if the resize ratio is supported by PrP channel 2
+ * @param cfg Pointer to emma_prp_cfg structure
+ * @return Zero on success, others on failure
+ */
+static int prp_resize_check_ch2(emma_prp_cfg * cfg)
+{
+ int w, h;
+ scale_t *pscale = &cfg->scale[2]; /* Ch2 width resize coeff */
+
+ if (cfg->ch2_pix == PRP_PIX2_UNUSED)
+ return 0;
+
+ w = check_simple_retry(pscale, cfg->in_width, cfg->ch2_width);
+ h = check_simple_retry(pscale + 1, cfg->in_height, cfg->ch2_height);
+ if ((w != -1) && (h != -1)) {
+ pr_debug("Ch2 resize.\n");
+ pr_debug("Original width = %d internel width = %d\n",
+ cfg->ch2_width, w);
+ pr_debug("Original height = %d internel height = %d\n",
+ cfg->ch2_height, h);
+ return 0;
+ } else {
+ pr_debug("Ch2 resize error.\n");
+ return -1;
+ }
+}
diff --git a/drivers/media/video/mxc/capture/mxc_v4l2_capture.c b/drivers/media/video/mxc/capture/mxc_v4l2_capture.c
new file mode 100644
index 000000000000..f08cfe8dfcc1
--- /dev/null
+++ b/drivers/media/video/mxc/capture/mxc_v4l2_capture.c
@@ -0,0 +1,2728 @@
+/*
+ * Copyright 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file drivers/media/video/mxc/capture/mxc_v4l2_capture.c
+ *
+ * @brief Mxc Video For Linux 2 driver
+ *
+ * @ingroup MXC_V4L2_CAPTURE
+ */
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/io.h>
+#include <linux/semaphore.h>
+#include <linux/pagemap.h>
+#include <linux/vmalloc.h>
+#include <linux/types.h>
+#include <linux/fb.h>
+#include <linux/dma-mapping.h>
+#include <linux/mxcfb.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-int-device.h>
+#include "mxc_v4l2_capture.h"
+#include "ipu_prp_sw.h"
+
+static int video_nr = -1;
+static cam_data *g_cam;
+
+/*! This data is used for the output to the display. */
+#define MXC_V4L2_CAPTURE_NUM_OUTPUTS 3
+#define MXC_V4L2_CAPTURE_NUM_INPUTS 2
+static struct v4l2_output mxc_capture_outputs[MXC_V4L2_CAPTURE_NUM_OUTPUTS] = {
+ {
+ .index = 0,
+ .name = "DISP3 BG",
+ .type = V4L2_OUTPUT_TYPE_ANALOG,
+ .audioset = 0,
+ .modulator = 0,
+ .std = V4L2_STD_UNKNOWN,
+ },
+ {
+ .index = 1,
+ .name = "DISP0",
+ .type = V4L2_OUTPUT_TYPE_ANALOG,
+ .audioset = 0,
+ .modulator = 0,
+ .std = V4L2_STD_UNKNOWN,
+ },
+ {
+ .index = 2,
+ .name = "DISP3 FG",
+ .type = V4L2_OUTPUT_TYPE_ANALOG,
+ .audioset = 0,
+ .modulator = 0,
+ .std = V4L2_STD_UNKNOWN,
+ },
+};
+
+static struct v4l2_input mxc_capture_inputs[MXC_V4L2_CAPTURE_NUM_INPUTS] = {
+ {
+ .index = 0,
+ .name = "CSI IC MEM",
+ .type = V4L2_INPUT_TYPE_CAMERA,
+ .audioset = 0,
+ .tuner = 0,
+ .std = V4L2_STD_UNKNOWN,
+ .status = 0,
+ },
+ {
+ .index = 1,
+ .name = "CSI MEM",
+ .type = V4L2_INPUT_TYPE_CAMERA,
+ .audioset = 0,
+ .tuner = 0,
+ .std = V4L2_STD_UNKNOWN,
+ .status = V4L2_IN_ST_NO_POWER,
+ },
+};
+
+/*! List of TV input video formats supported. The video formats is corresponding
+ * to the v4l2_id in video_fmt_t.
+ * Currently, only PAL and NTSC is supported. Needs to be expanded in the
+ * future.
+ */
+typedef enum {
+ TV_NTSC = 0, /*!< Locked on (M) NTSC video signal. */
+ TV_PAL, /*!< (B, G, H, I, N)PAL video signal. */
+ TV_NOT_LOCKED, /*!< Not locked on a signal. */
+} video_fmt_idx;
+
+/*! Number of video standards supported (including 'not locked' signal). */
+#define TV_STD_MAX (TV_NOT_LOCKED + 1)
+
+/*! Video format structure. */
+typedef struct {
+ int v4l2_id; /*!< Video for linux ID. */
+ char name[16]; /*!< Name (e.g., "NTSC", "PAL", etc.) */
+ u16 raw_width; /*!< Raw width. */
+ u16 raw_height; /*!< Raw height. */
+ u16 active_width; /*!< Active width. */
+ u16 active_height; /*!< Active height. */
+ u16 active_top; /*!< Active top. */
+ u16 active_left; /*!< Active left. */
+} video_fmt_t;
+
+/*!
+ * Description of video formats supported.
+ *
+ * PAL: raw=720x625, active=720x576.
+ * NTSC: raw=720x525, active=720x480.
+ */
+static video_fmt_t video_fmts[] = {
+ { /*! NTSC */
+ .v4l2_id = V4L2_STD_NTSC,
+ .name = "NTSC",
+ .raw_width = 720, /* SENS_FRM_WIDTH */
+ .raw_height = 525, /* SENS_FRM_HEIGHT */
+ .active_width = 720, /* ACT_FRM_WIDTH */
+ .active_height = 240, /* ACT_FRM_HEIGHT */
+ .active_top = 0,
+ .active_left = 0,
+ },
+ { /*! (B, G, H, I, N) PAL */
+ .v4l2_id = V4L2_STD_PAL,
+ .name = "PAL",
+ .raw_width = 720,
+ .raw_height = 625,
+ .active_width = 720,
+ .active_height = 288,
+ .active_top = 0,
+ .active_left = 0,
+ },
+ { /*! Unlocked standard */
+ .v4l2_id = V4L2_STD_ALL,
+ .name = "Autodetect",
+ .raw_width = 720,
+ .raw_height = 625,
+ .active_width = 720,
+ .active_height = 288,
+ .active_top = 0,
+ .active_left = 0,
+ },
+};
+
+/*!* Standard index of TV. */
+static video_fmt_idx video_index = TV_NOT_LOCKED;
+
+static int mxc_v4l2_master_attach(struct v4l2_int_device *slave);
+static void mxc_v4l2_master_detach(struct v4l2_int_device *slave);
+static u8 camera_power(cam_data *cam, bool cameraOn);
+static int start_preview(cam_data *cam);
+static int stop_preview(cam_data *cam);
+
+/*! Information about this driver. */
+static struct v4l2_int_master mxc_v4l2_master = {
+ .attach = mxc_v4l2_master_attach,
+ .detach = mxc_v4l2_master_detach,
+};
+
+static struct v4l2_int_device mxc_v4l2_int_device = {
+ .module = THIS_MODULE,
+ .name = "mxc_v4l2_cap",
+ .type = v4l2_int_type_master,
+ .u = {
+ .master = &mxc_v4l2_master,
+ },
+};
+
+/***************************************************************************
+ * Functions for handling Frame buffers.
+ **************************************************************************/
+
+/*!
+ * Free frame buffers
+ *
+ * @param cam Structure cam_data *
+ *
+ * @return status 0 success.
+ */
+static int mxc_free_frame_buf(cam_data *cam)
+{
+ int i;
+
+ pr_debug("MVC: In mxc_free_frame_buf\n");
+
+ for (i = 0; i < FRAME_NUM; i++) {
+ if (cam->frame[i].vaddress != 0) {
+ dma_free_coherent(0, cam->frame[i].buffer.length,
+ cam->frame[i].vaddress,
+ cam->frame[i].paddress);
+ cam->frame[i].vaddress = 0;
+ }
+ }
+
+ return 0;
+}
+
+/*!
+ * Allocate frame buffers
+ *
+ * @param cam Structure cam_data*
+ * @param count int number of buffer need to allocated
+ *
+ * @return status -0 Successfully allocated a buffer, -ENOBUFS failed.
+ */
+static int mxc_allocate_frame_buf(cam_data *cam, int count)
+{
+ int i;
+
+ pr_debug("In MVC:mxc_allocate_frame_buf - size=%d\n",
+ cam->v2f.fmt.pix.sizeimage);
+
+ for (i = 0; i < count; i++) {
+ cam->frame[i].vaddress =
+ dma_alloc_coherent(0,
+ PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage),
+ &cam->frame[i].paddress,
+ GFP_DMA | GFP_KERNEL);
+ if (cam->frame[i].vaddress == 0) {
+ pr_err("ERROR: v4l2 capture: "
+ "mxc_allocate_frame_buf failed.\n");
+ mxc_free_frame_buf(cam);
+ return -ENOBUFS;
+ }
+ cam->frame[i].buffer.index = i;
+ cam->frame[i].buffer.flags = V4L2_BUF_FLAG_MAPPED;
+ cam->frame[i].buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ cam->frame[i].buffer.length =
+ PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage);
+ cam->frame[i].buffer.memory = V4L2_MEMORY_MMAP;
+ cam->frame[i].buffer.m.offset = cam->frame[i].paddress;
+ cam->frame[i].index = i;
+ }
+
+ return 0;
+}
+
+/*!
+ * Free frame buffers status
+ *
+ * @param cam Structure cam_data *
+ *
+ * @return none
+ */
+static void mxc_free_frames(cam_data *cam)
+{
+ int i;
+
+ pr_debug("In MVC:mxc_free_frames\n");
+
+ for (i = 0; i < FRAME_NUM; i++) {
+ cam->frame[i].buffer.flags = V4L2_BUF_FLAG_MAPPED;
+ }
+
+ cam->enc_counter = 0;
+ cam->skip_frame = 0;
+ INIT_LIST_HEAD(&cam->ready_q);
+ INIT_LIST_HEAD(&cam->working_q);
+ INIT_LIST_HEAD(&cam->done_q);
+}
+
+/*!
+ * Return the buffer status
+ *
+ * @param cam Structure cam_data *
+ * @param buf Structure v4l2_buffer *
+ *
+ * @return status 0 success, EINVAL failed.
+ */
+static int mxc_v4l2_buffer_status(cam_data *cam, struct v4l2_buffer *buf)
+{
+ pr_debug("In MVC:mxc_v4l2_buffer_status\n");
+
+ if (buf->index < 0 || buf->index >= FRAME_NUM) {
+ pr_err("ERROR: v4l2 capture: mxc_v4l2_buffer_status buffers "
+ "not allocated\n");
+ return -EINVAL;
+ }
+
+ memcpy(buf, &(cam->frame[buf->index].buffer), sizeof(*buf));
+ return 0;
+}
+
+/***************************************************************************
+ * Functions for handling the video stream.
+ **************************************************************************/
+
+/*!
+ * Indicates whether the palette is supported.
+ *
+ * @param palette V4L2_PIX_FMT_RGB565, V4L2_PIX_FMT_BGR24 or V4L2_PIX_FMT_BGR32
+ *
+ * @return 0 if failed
+ */
+static inline int valid_mode(u32 palette)
+{
+ return ((palette == V4L2_PIX_FMT_RGB565) ||
+ (palette == V4L2_PIX_FMT_BGR24) ||
+ (palette == V4L2_PIX_FMT_RGB24) ||
+ (palette == V4L2_PIX_FMT_BGR32) ||
+ (palette == V4L2_PIX_FMT_RGB32) ||
+ (palette == V4L2_PIX_FMT_YUV422P) ||
+ (palette == V4L2_PIX_FMT_UYVY) ||
+ (palette == V4L2_PIX_FMT_YUYV) ||
+ (palette == V4L2_PIX_FMT_YUV420) ||
+ (palette == V4L2_PIX_FMT_NV12));
+}
+
+/*!
+ * Start the encoder job
+ *
+ * @param cam structure cam_data *
+ *
+ * @return status 0 Success
+ */
+static int mxc_streamon(cam_data *cam)
+{
+ struct mxc_v4l_frame *frame;
+ int err = 0;
+
+ pr_debug("In MVC:mxc_streamon\n");
+
+ if (NULL == cam) {
+ pr_err("ERROR! cam parameter is NULL\n");
+ return -1;
+ }
+
+ if (cam->capture_on) {
+ pr_err("ERROR: v4l2 capture: Capture stream has been turned "
+ " on\n");
+ return -1;
+ }
+
+ if (list_empty(&cam->ready_q)) {
+ pr_err("ERROR: v4l2 capture: mxc_streamon buffer has not been "
+ "queued yet\n");
+ return -EINVAL;
+ }
+
+ cam->capture_pid = current->pid;
+
+ if (cam->overlay_on == true)
+ stop_preview(cam);
+
+ if (cam->enc_enable) {
+ err = cam->enc_enable(cam);
+ if (err != 0) {
+ return err;
+ }
+ }
+
+ cam->ping_pong_csi = 0;
+ if (cam->enc_update_eba) {
+ frame =
+ list_entry(cam->ready_q.next, struct mxc_v4l_frame, queue);
+ list_del(cam->ready_q.next);
+ list_add_tail(&frame->queue, &cam->working_q);
+ err = cam->enc_update_eba(frame->buffer.m.offset,
+ &cam->ping_pong_csi);
+
+ frame =
+ list_entry(cam->ready_q.next, struct mxc_v4l_frame, queue);
+ list_del(cam->ready_q.next);
+ list_add_tail(&frame->queue, &cam->working_q);
+ err |= cam->enc_update_eba(frame->buffer.m.offset,
+ &cam->ping_pong_csi);
+ } else {
+ return -EINVAL;
+ }
+
+ if (cam->overlay_on == true)
+ start_preview(cam);
+
+ if (cam->enc_enable_csi) {
+ err = cam->enc_enable_csi(cam);
+ if (err != 0)
+ return err;
+ }
+
+ cam->capture_on = true;
+
+ return err;
+}
+
+/*!
+ * Shut down the encoder job
+ *
+ * @param cam structure cam_data *
+ *
+ * @return status 0 Success
+ */
+static int mxc_streamoff(cam_data *cam)
+{
+ int err = 0;
+
+ pr_debug("In MVC:mxc_streamoff\n");
+
+ if (cam->capture_on == false)
+ return 0;
+
+ if (cam->enc_disable_csi) {
+ err = cam->enc_disable_csi(cam);
+ if (err != 0)
+ return err;
+ }
+ if (cam->enc_disable)
+ err = cam->enc_disable(cam);
+
+ mxc_free_frames(cam);
+ mxc_capture_inputs[cam->current_input].status |= V4L2_IN_ST_NO_POWER;
+ cam->capture_on = false;
+ return err;
+}
+
+/*!
+ * Valid and adjust the overlay window size, position
+ *
+ * @param cam structure cam_data *
+ * @param win struct v4l2_window *
+ *
+ * @return 0
+ */
+static int verify_preview(cam_data *cam, struct v4l2_window *win)
+{
+ int i = 0, width_bound = 0, height_bound = 0;
+ int *width, *height;
+ struct fb_info *bg_fbi = NULL;
+ bool foregound_fb;
+
+ pr_debug("In MVC: verify_preview\n");
+
+ do {
+ cam->overlay_fb = (struct fb_info *)registered_fb[i];
+ if (cam->overlay_fb == NULL) {
+ pr_err("ERROR: verify_preview frame buffer NULL.\n");
+ return -1;
+ }
+ if (strcmp(cam->overlay_fb->fix.id, "DISP3 BG") == 0)
+ bg_fbi = cam->overlay_fb;
+ if (strcmp(cam->overlay_fb->fix.id,
+ mxc_capture_outputs[cam->output].name) == 0) {
+ if (strcmp(cam->overlay_fb->fix.id, "DISP3 FG") == 0)
+ foregound_fb = true;
+ break;
+ }
+ } while (++i < FB_MAX);
+
+ if (foregound_fb) {
+ width_bound = bg_fbi->var.xres;
+ height_bound = bg_fbi->var.yres;
+
+ if (win->w.width + win->w.left > bg_fbi->var.xres ||
+ win->w.height + win->w.top > bg_fbi->var.yres) {
+ pr_err("ERROR: FG window position exceeds.\n");
+ return -1;
+ }
+ } else {
+ /* 4 bytes alignment for BG */
+ width_bound = cam->overlay_fb->var.xres;
+ height_bound = cam->overlay_fb->var.yres;
+
+ if (cam->overlay_fb->var.bits_per_pixel == 24) {
+ win->w.left -= win->w.left % 4;
+ } else if (cam->overlay_fb->var.bits_per_pixel == 16) {
+ win->w.left -= win->w.left % 2;
+ }
+
+ if (win->w.width + win->w.left > cam->overlay_fb->var.xres)
+ win->w.width = cam->overlay_fb->var.xres - win->w.left;
+ if (win->w.height + win->w.top > cam->overlay_fb->var.yres)
+ win->w.height = cam->overlay_fb->var.yres - win->w.top;
+ }
+
+ /* stride line limitation */
+ win->w.height -= win->w.height % 8;
+ win->w.width -= win->w.width % 8;
+
+ if (cam->rotation >= IPU_ROTATE_90_RIGHT) {
+ height = &win->w.width;
+ width = &win->w.height;
+ } else {
+ width = &win->w.width;
+ height = &win->w.height;
+ }
+
+ if ((cam->crop_bounds.width / *width > 8) ||
+ ((cam->crop_bounds.width / *width == 8) &&
+ (cam->crop_bounds.width % *width))) {
+ *width = cam->crop_bounds.width / 8;
+ if (*width % 8)
+ *width += 8 - *width % 8;
+ if (*width + win->w.left > width_bound) {
+ pr_err("ERROR: v4l2 capture: width exceeds "
+ "resize limit.\n");
+ return -1;
+ }
+ pr_err("ERROR: v4l2 capture: width exceeds limit. "
+ "Resize to %d.\n",
+ *width);
+ }
+
+ if ((cam->crop_bounds.height / *height > 8) ||
+ ((cam->crop_bounds.height / *height == 8) &&
+ (cam->crop_bounds.height % *height))) {
+ *height = cam->crop_bounds.height / 8;
+ if (*height % 8)
+ *height += 8 - *height % 8;
+ if (*height + win->w.top > height_bound) {
+ pr_err("ERROR: v4l2 capture: height exceeds "
+ "resize limit.\n");
+ return -1;
+ }
+ pr_err("ERROR: v4l2 capture: height exceeds limit "
+ "resize to %d.\n",
+ *height);
+ }
+
+ return 0;
+}
+
+/*!
+ * start the viewfinder job
+ *
+ * @param cam structure cam_data *
+ *
+ * @return status 0 Success
+ */
+static int start_preview(cam_data *cam)
+{
+ int err = 0;
+
+ pr_debug("MVC: start_preview\n");
+
+#if defined(CONFIG_MXC_IPU_PRP_VF_SDC) || defined(CONFIG_MXC_IPU_PRP_VF_SDC_MODULE)
+ pr_debug(" This is an SDC display\n");
+ if (cam->output == 0 || cam->output == 2) {
+ if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY)
+ err = prp_vf_sdc_select(cam);
+ else if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_PRIMARY)
+ err = prp_vf_sdc_select_bg(cam);
+ if (err != 0)
+ return err;
+
+ err = cam->vf_start_sdc(cam);
+ if (err != 0)
+ return err;
+
+ if (cam->vf_enable_csi)
+ err = cam->vf_enable_csi(cam);
+ }
+#endif
+
+#if defined(CONFIG_MXC_IPU_PRP_VF_ADC) || defined(CONFIG_MXC_IPU_PRP_VF_ADC_MODULE)
+ pr_debug(" This is an ADC display\n");
+ if (cam->output == 1) {
+ err = prp_vf_adc_select(cam);
+ if (err != 0)
+ return err;
+
+ err = cam->vf_start_adc(cam);
+ }
+#endif
+
+ pr_debug("End of %s: v2f pix widthxheight %d x %d\n",
+ __func__,
+ cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height);
+ pr_debug("End of %s: crop_bounds widthxheight %d x %d\n",
+ __func__,
+ cam->crop_bounds.width, cam->crop_bounds.height);
+ pr_debug("End of %s: crop_defrect widthxheight %d x %d\n",
+ __func__,
+ cam->crop_defrect.width, cam->crop_defrect.height);
+ pr_debug("End of %s: crop_current widthxheight %d x %d\n",
+ __func__,
+ cam->crop_current.width, cam->crop_current.height);
+
+ return err;
+}
+
+/*!
+ * shut down the viewfinder job
+ *
+ * @param cam structure cam_data *
+ *
+ * @return status 0 Success
+ */
+static int stop_preview(cam_data *cam)
+{
+ int err = 0;
+
+ pr_debug("MVC: stop preview\n");
+
+#if defined(CONFIG_MXC_IPU_PRP_VF_ADC) || defined(CONFIG_MXC_IPU_PRP_VF_ADC_MODULE)
+ if (cam->output == 1) {
+ err = prp_vf_adc_deselect(cam);
+ }
+#endif
+
+#if defined(CONFIG_MXC_IPU_PRP_VF_SDC) || defined(CONFIG_MXC_IPU_PRP_VF_SDC_MODULE)
+ if (cam->vf_disable_csi) {
+ err = cam->vf_disable_csi(cam);
+ if (err != 0)
+ return err;
+ }
+
+ if (cam->output == 0 || cam->output == 2) {
+ if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY)
+ err = prp_vf_sdc_deselect(cam);
+ else if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_PRIMARY)
+ err = prp_vf_sdc_deselect_bg(cam);
+ }
+#endif
+
+ return err;
+}
+
+/***************************************************************************
+ * VIDIOC Functions.
+ **************************************************************************/
+
+/*!
+ * V4L2 - mxc_v4l2_g_fmt function
+ *
+ * @param cam structure cam_data *
+ *
+ * @param f structure v4l2_format *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_v4l2_g_fmt(cam_data *cam, struct v4l2_format *f)
+{
+ int retval = 0;
+
+ pr_debug("In MVC: mxc_v4l2_g_fmt type=%d\n", f->type);
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ pr_debug(" type is V4L2_BUF_TYPE_VIDEO_CAPTURE\n");
+ f->fmt.pix = cam->v2f.fmt.pix;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ pr_debug(" type is V4L2_BUF_TYPE_VIDEO_OVERLAY\n");
+ f->fmt.win = cam->win;
+ break;
+ default:
+ pr_debug(" type is invalid\n");
+ retval = -EINVAL;
+ }
+
+ pr_debug("End of %s: v2f pix widthxheight %d x %d\n",
+ __func__,
+ cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height);
+ pr_debug("End of %s: crop_bounds widthxheight %d x %d\n",
+ __func__,
+ cam->crop_bounds.width, cam->crop_bounds.height);
+ pr_debug("End of %s: crop_defrect widthxheight %d x %d\n",
+ __func__,
+ cam->crop_defrect.width, cam->crop_defrect.height);
+ pr_debug("End of %s: crop_current widthxheight %d x %d\n",
+ __func__,
+ cam->crop_current.width, cam->crop_current.height);
+
+ return retval;
+}
+
+/*!
+ * V4L2 - mxc_v4l2_s_fmt function
+ *
+ * @param cam structure cam_data *
+ *
+ * @param f structure v4l2_format *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_v4l2_s_fmt(cam_data *cam, struct v4l2_format *f)
+{
+ int retval = 0;
+ int size = 0;
+ int bytesperline = 0;
+ int *width, *height;
+
+ pr_debug("In MVC: mxc_v4l2_s_fmt\n");
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ pr_debug(" type=V4L2_BUF_TYPE_VIDEO_CAPTURE\n");
+ if (!valid_mode(f->fmt.pix.pixelformat)) {
+ pr_err("ERROR: v4l2 capture: mxc_v4l2_s_fmt: format "
+ "not supported\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Force the capture window resolution to be crop bounds
+ * for CSI MEM input mode.
+ */
+ if (strcmp(mxc_capture_inputs[cam->current_input].name,
+ "CSI MEM") == 0) {
+ f->fmt.pix.width = cam->crop_current.width;
+ f->fmt.pix.height = cam->crop_current.height;
+ }
+
+ if (cam->rotation >= IPU_ROTATE_90_RIGHT) {
+ height = &f->fmt.pix.width;
+ width = &f->fmt.pix.height;
+ } else {
+ width = &f->fmt.pix.width;
+ height = &f->fmt.pix.height;
+ }
+
+ /* stride line limitation */
+ *width -= *width % 8;
+ *height -= *height % 8;
+
+ if ((cam->crop_current.width / *width > 8) ||
+ ((cam->crop_current.width / *width == 8) &&
+ (cam->crop_current.width % *width))) {
+ *width = cam->crop_current.width / 8;
+ if (*width % 8)
+ *width += 8 - *width % 8;
+ pr_err("ERROR: v4l2 capture: width exceeds limit "
+ "resize to %d.\n",
+ *width);
+ }
+
+ if ((cam->crop_current.height / *height > 8) ||
+ ((cam->crop_current.height / *height == 8) &&
+ (cam->crop_current.height % *height))) {
+ *height = cam->crop_current.height / 8;
+ if (*height % 8)
+ *height += 8 - *height % 8;
+ pr_err("ERROR: v4l2 capture: height exceeds limit "
+ "resize to %d.\n",
+ *height);
+ }
+
+ switch (f->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_RGB565:
+ size = f->fmt.pix.width * f->fmt.pix.height * 2;
+ bytesperline = f->fmt.pix.width * 2;
+ break;
+ case V4L2_PIX_FMT_BGR24:
+ size = f->fmt.pix.width * f->fmt.pix.height * 3;
+ bytesperline = f->fmt.pix.width * 3;
+ break;
+ case V4L2_PIX_FMT_RGB24:
+ size = f->fmt.pix.width * f->fmt.pix.height * 3;
+ bytesperline = f->fmt.pix.width * 3;
+ break;
+ case V4L2_PIX_FMT_BGR32:
+ size = f->fmt.pix.width * f->fmt.pix.height * 4;
+ bytesperline = f->fmt.pix.width * 4;
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ size = f->fmt.pix.width * f->fmt.pix.height * 4;
+ bytesperline = f->fmt.pix.width * 4;
+ break;
+ case V4L2_PIX_FMT_YUV422P:
+ size = f->fmt.pix.width * f->fmt.pix.height * 2;
+ bytesperline = f->fmt.pix.width;
+ break;
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_YUYV:
+ size = f->fmt.pix.width * f->fmt.pix.height * 2;
+ bytesperline = f->fmt.pix.width * 2;
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ size = f->fmt.pix.width * f->fmt.pix.height * 3 / 2;
+ bytesperline = f->fmt.pix.width;
+ break;
+ case V4L2_PIX_FMT_NV12:
+ size = f->fmt.pix.width * f->fmt.pix.height * 3 / 2;
+ bytesperline = f->fmt.pix.width;
+ break;
+ default:
+ break;
+ }
+
+ if (f->fmt.pix.bytesperline < bytesperline) {
+ f->fmt.pix.bytesperline = bytesperline;
+ } else {
+ bytesperline = f->fmt.pix.bytesperline;
+ }
+
+ if (f->fmt.pix.sizeimage < size) {
+ f->fmt.pix.sizeimage = size;
+ } else {
+ size = f->fmt.pix.sizeimage;
+ }
+
+ cam->v2f.fmt.pix = f->fmt.pix;
+
+ if (cam->v2f.fmt.pix.priv != 0) {
+ if (copy_from_user(&cam->offset,
+ (void *)cam->v2f.fmt.pix.priv,
+ sizeof(cam->offset))) {
+ retval = -EFAULT;
+ break;
+ }
+ }
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ pr_debug(" type=V4L2_BUF_TYPE_VIDEO_OVERLAY\n");
+ retval = verify_preview(cam, &f->fmt.win);
+ cam->win = f->fmt.win;
+ break;
+ default:
+ retval = -EINVAL;
+ }
+
+ pr_debug("End of %s: v2f pix widthxheight %d x %d\n",
+ __func__,
+ cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height);
+ pr_debug("End of %s: crop_bounds widthxheight %d x %d\n",
+ __func__,
+ cam->crop_bounds.width, cam->crop_bounds.height);
+ pr_debug("End of %s: crop_defrect widthxheight %d x %d\n",
+ __func__,
+ cam->crop_defrect.width, cam->crop_defrect.height);
+ pr_debug("End of %s: crop_current widthxheight %d x %d\n",
+ __func__,
+ cam->crop_current.width, cam->crop_current.height);
+
+ return retval;
+}
+
+/*!
+ * get control param
+ *
+ * @param cam structure cam_data *
+ *
+ * @param c structure v4l2_control *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_v4l2_g_ctrl(cam_data *cam, struct v4l2_control *c)
+{
+ int status = 0;
+
+ pr_debug("In MVC:mxc_v4l2_g_ctrl\n");
+
+ /* probably don't need to store the values that can be retrieved,
+ * locally, but they are for now. */
+ switch (c->id) {
+ case V4L2_CID_HFLIP:
+ /* This is handled in the ipu. */
+ if (cam->rotation == IPU_ROTATE_HORIZ_FLIP)
+ c->value = 1;
+ break;
+ case V4L2_CID_VFLIP:
+ /* This is handled in the ipu. */
+ if (cam->rotation == IPU_ROTATE_VERT_FLIP)
+ c->value = 1;
+ break;
+ case V4L2_CID_MXC_ROT:
+ /* This is handled in the ipu. */
+ c->value = cam->rotation;
+ break;
+ case V4L2_CID_BRIGHTNESS:
+ c->value = cam->bright;
+ status = vidioc_int_g_ctrl(cam->sensor, c);
+ cam->bright = c->value;
+ break;
+ case V4L2_CID_HUE:
+ c->value = cam->hue;
+ status = vidioc_int_g_ctrl(cam->sensor, c);
+ cam->hue = c->value;
+ break;
+ case V4L2_CID_CONTRAST:
+ c->value = cam->contrast;
+ status = vidioc_int_g_ctrl(cam->sensor, c);
+ cam->contrast = c->value;
+ break;
+ case V4L2_CID_SATURATION:
+ c->value = cam->saturation;
+ status = vidioc_int_g_ctrl(cam->sensor, c);
+ cam->saturation = c->value;
+ break;
+ case V4L2_CID_RED_BALANCE:
+ c->value = cam->red;
+ status = vidioc_int_g_ctrl(cam->sensor, c);
+ cam->red = c->value;
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ c->value = cam->blue;
+ status = vidioc_int_g_ctrl(cam->sensor, c);
+ cam->blue = c->value;
+ break;
+ case V4L2_CID_BLACK_LEVEL:
+ c->value = cam->ae_mode;
+ status = vidioc_int_g_ctrl(cam->sensor, c);
+ cam->ae_mode = c->value;
+ break;
+ default:
+ status = vidioc_int_g_ctrl(cam->sensor, c);
+ }
+
+ return status;
+}
+
+/*!
+ * V4L2 - set_control function
+ * V4L2_CID_PRIVATE_BASE is the extention for IPU preprocessing.
+ * 0 for normal operation
+ * 1 for vertical flip
+ * 2 for horizontal flip
+ * 3 for horizontal and vertical flip
+ * 4 for 90 degree rotation
+ * @param cam structure cam_data *
+ *
+ * @param c structure v4l2_control *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_v4l2_s_ctrl(cam_data *cam, struct v4l2_control *c)
+{
+ int ret = 0;
+ int tmp_rotation = IPU_ROTATE_NONE;
+
+ pr_debug("In MVC:mxc_v4l2_s_ctrl\n");
+
+ switch (c->id) {
+ case V4L2_CID_HFLIP:
+ /* This is done by the IPU */
+ if (c->value == 1) {
+ if ((cam->rotation != IPU_ROTATE_VERT_FLIP) &&
+ (cam->rotation != IPU_ROTATE_180))
+ cam->rotation = IPU_ROTATE_HORIZ_FLIP;
+ else
+ cam->rotation = IPU_ROTATE_180;
+ } else {
+ if (cam->rotation == IPU_ROTATE_HORIZ_FLIP)
+ cam->rotation = IPU_ROTATE_NONE;
+ if (cam->rotation == IPU_ROTATE_180)
+ cam->rotation = IPU_ROTATE_VERT_FLIP;
+ }
+ break;
+ case V4L2_CID_VFLIP:
+ /* This is done by the IPU */
+ if (c->value == 1) {
+ if ((cam->rotation != IPU_ROTATE_HORIZ_FLIP) &&
+ (cam->rotation != IPU_ROTATE_180))
+ cam->rotation = IPU_ROTATE_VERT_FLIP;
+ else
+ cam->rotation = IPU_ROTATE_180;
+ } else {
+ if (cam->rotation == IPU_ROTATE_VERT_FLIP)
+ cam->rotation = IPU_ROTATE_NONE;
+ if (cam->rotation == IPU_ROTATE_180)
+ cam->rotation = IPU_ROTATE_HORIZ_FLIP;
+ }
+ break;
+ case V4L2_CID_MXC_ROT:
+ case V4L2_CID_MXC_VF_ROT:
+ /* This is done by the IPU */
+ switch (c->value) {
+ case V4L2_MXC_ROTATE_NONE:
+ tmp_rotation = IPU_ROTATE_NONE;
+ break;
+ case V4L2_MXC_ROTATE_VERT_FLIP:
+ tmp_rotation = IPU_ROTATE_VERT_FLIP;
+ break;
+ case V4L2_MXC_ROTATE_HORIZ_FLIP:
+ tmp_rotation = IPU_ROTATE_HORIZ_FLIP;
+ break;
+ case V4L2_MXC_ROTATE_180:
+ tmp_rotation = IPU_ROTATE_180;
+ break;
+ case V4L2_MXC_ROTATE_90_RIGHT:
+ tmp_rotation = IPU_ROTATE_90_RIGHT;
+ break;
+ case V4L2_MXC_ROTATE_90_RIGHT_VFLIP:
+ tmp_rotation = IPU_ROTATE_90_RIGHT_VFLIP;
+ break;
+ case V4L2_MXC_ROTATE_90_RIGHT_HFLIP:
+ tmp_rotation = IPU_ROTATE_90_RIGHT_HFLIP;
+ break;
+ case V4L2_MXC_ROTATE_90_LEFT:
+ tmp_rotation = IPU_ROTATE_90_LEFT;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ if (c->id == V4L2_CID_MXC_VF_ROT)
+ cam->vf_rotation = tmp_rotation;
+ else
+ cam->rotation = tmp_rotation;
+
+ break;
+ case V4L2_CID_HUE:
+ cam->hue = c->value;
+ ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, true, true);
+ ret = vidioc_int_s_ctrl(cam->sensor, c);
+ ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, false, false);
+ break;
+ case V4L2_CID_CONTRAST:
+ cam->contrast = c->value;
+ ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, true, true);
+ ret = vidioc_int_s_ctrl(cam->sensor, c);
+ ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, false, false);
+ break;
+ case V4L2_CID_BRIGHTNESS:
+ cam->bright = c->value;
+ ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, true, true);
+ ret = vidioc_int_s_ctrl(cam->sensor, c);
+ ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, false, false);
+ break;
+ case V4L2_CID_SATURATION:
+ cam->saturation = c->value;
+ ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, true, true);
+ ret = vidioc_int_s_ctrl(cam->sensor, c);
+ ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, false, false);
+ break;
+ case V4L2_CID_RED_BALANCE:
+ cam->red = c->value;
+ ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, true, true);
+ ret = vidioc_int_s_ctrl(cam->sensor, c);
+ ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, false, false);
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ cam->blue = c->value;
+ ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, true, true);
+ ret = vidioc_int_s_ctrl(cam->sensor, c);
+ ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, false, false);
+ break;
+ case V4L2_CID_EXPOSURE:
+ cam->ae_mode = c->value;
+ ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, true, true);
+ ret = vidioc_int_s_ctrl(cam->sensor, c);
+ ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, false, false);
+ break;
+ case V4L2_CID_MXC_FLASH:
+#ifdef CONFIG_MXC_IPU_V1
+ ipu_csi_flash_strobe(true);
+#endif
+ break;
+ default:
+ pr_debug(" default case\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+/*!
+ * V4L2 - mxc_v4l2_s_param function
+ * Allows setting of capturemode and frame rate.
+ *
+ * @param cam structure cam_data *
+ * @param parm structure v4l2_streamparm *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_v4l2_s_param(cam_data *cam, struct v4l2_streamparm *parm)
+{
+ struct v4l2_ifparm ifparm;
+ struct v4l2_format cam_fmt;
+ struct v4l2_streamparm currentparm;
+ ipu_csi_signal_cfg_t csi_param;
+ int err = 0;
+
+ pr_debug("In mxc_v4l2_s_param\n");
+
+ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ pr_err(KERN_ERR "mxc_v4l2_s_param invalid type\n");
+ return -EINVAL;
+ }
+
+ /* Stop the viewfinder */
+ if (cam->overlay_on == true) {
+ stop_preview(cam);
+ }
+
+ currentparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ /* First check that this device can support the changes requested. */
+ err = vidioc_int_g_parm(cam->sensor, &currentparm);
+ if (err) {
+ pr_err("%s: vidioc_int_g_parm returned an error %d\n",
+ __func__, err);
+ goto exit;
+ }
+
+ pr_debug(" Current capabilities are %x\n",
+ currentparm.parm.capture.capability);
+ pr_debug(" Current capturemode is %d change to %d\n",
+ currentparm.parm.capture.capturemode,
+ parm->parm.capture.capturemode);
+ pr_debug(" Current framerate is %d change to %d\n",
+ currentparm.parm.capture.timeperframe.denominator,
+ parm->parm.capture.timeperframe.denominator);
+
+ /* This will change any camera settings needed. */
+ ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, true, true);
+ err = vidioc_int_s_parm(cam->sensor, parm);
+ ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, false, false);
+ if (err) {
+ pr_err("%s: vidioc_int_s_parm returned an error %d\n",
+ __func__, err);
+ goto exit;
+ }
+
+ /* If resolution changed, need to re-program the CSI */
+ /* Get new values. */
+ vidioc_int_g_ifparm(cam->sensor, &ifparm);
+
+ csi_param.data_width = 0;
+ csi_param.clk_mode = 0;
+ csi_param.ext_vsync = 0;
+ csi_param.Vsync_pol = 0;
+ csi_param.Hsync_pol = 0;
+ csi_param.pixclk_pol = 0;
+ csi_param.data_pol = 0;
+ csi_param.sens_clksrc = 0;
+ csi_param.pack_tight = 0;
+ csi_param.force_eof = 0;
+ csi_param.data_en_pol = 0;
+ csi_param.data_fmt = 0;
+ csi_param.csi = 0;
+ csi_param.mclk = 0;
+
+ /* This may not work on other platforms. Check when adding a new one.*/
+ pr_debug(" clock_curr=mclk=%d\n", ifparm.u.bt656.clock_curr);
+ if (ifparm.u.bt656.clock_curr == 0) {
+ csi_param.clk_mode = IPU_CSI_CLK_MODE_CCIR656_INTERLACED;
+ } else {
+ csi_param.clk_mode = IPU_CSI_CLK_MODE_GATED_CLK;
+ }
+
+ csi_param.pixclk_pol = ifparm.u.bt656.latch_clk_inv;
+
+ if (ifparm.u.bt656.mode == V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT) {
+ csi_param.data_width = IPU_CSI_DATA_WIDTH_8;
+ } else if (ifparm.u.bt656.mode
+ == V4L2_IF_TYPE_BT656_MODE_NOBT_10BIT) {
+ csi_param.data_width = IPU_CSI_DATA_WIDTH_10;
+ } else {
+ csi_param.data_width = IPU_CSI_DATA_WIDTH_8;
+ }
+
+ csi_param.Vsync_pol = ifparm.u.bt656.nobt_vs_inv;
+ csi_param.Hsync_pol = ifparm.u.bt656.nobt_hs_inv;
+ csi_param.ext_vsync = ifparm.u.bt656.bt_sync_correct;
+
+ /* if the capturemode changed, the size bounds will have changed. */
+ cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ vidioc_int_g_fmt_cap(cam->sensor, &cam_fmt);
+ pr_debug(" g_fmt_cap returns widthxheight of input as %d x %d\n",
+ cam_fmt.fmt.pix.width, cam_fmt.fmt.pix.height);
+
+ csi_param.data_fmt = cam_fmt.fmt.pix.pixelformat;
+
+ cam->crop_bounds.top = cam->crop_bounds.left = 0;
+ cam->crop_bounds.width = cam_fmt.fmt.pix.width;
+ cam->crop_bounds.height = cam_fmt.fmt.pix.height;
+
+ /*
+ * Set the default current cropped resolution to be the same with
+ * the cropping boundary(except for tvin module).
+ */
+ if (cam->device_type != 1) {
+ cam->crop_current.width = cam->crop_bounds.width;
+ cam->crop_current.height = cam->crop_bounds.height;
+ }
+
+ /* This essentially loses the data at the left and bottom of the image
+ * giving a digital zoom image, if crop_current is less than the full
+ * size of the image. */
+ ipu_csi_set_window_size(cam->crop_current.width,
+ cam->crop_current.height, cam->csi);
+ ipu_csi_set_window_pos(cam->crop_current.left,
+ cam->crop_current.top,
+ cam->csi);
+ ipu_csi_init_interface(cam->crop_bounds.width,
+ cam->crop_bounds.height,
+ cam_fmt.fmt.pix.pixelformat, csi_param);
+
+
+exit:
+ if (cam->overlay_on == true)
+ start_preview(cam);
+
+ return err;
+}
+
+/*!
+ * V4L2 - mxc_v4l2_s_std function
+ *
+ * Sets the TV standard to be used.
+ *
+ * @param cam structure cam_data *
+ * @param parm structure v4l2_streamparm *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_v4l2_s_std(cam_data *cam, v4l2_std_id e)
+{
+ bool change = false;
+
+ if (e != cam->standard.id) {
+ change = true;
+ }
+
+ pr_debug("In mxc_v4l2_s_std %Lx\n", e);
+ if (e == V4L2_STD_PAL) {
+ pr_debug(" Setting standard to PAL %Lx\n", V4L2_STD_PAL);
+ cam->standard.id = V4L2_STD_PAL;
+ video_index = TV_PAL;
+ cam->crop_current.top = 0;
+ } else if (e == V4L2_STD_NTSC) {
+ pr_debug(" Setting standard to NTSC %Lx\n",
+ V4L2_STD_NTSC);
+ /* Get rid of the white dot line in NTSC signal input */
+ cam->standard.id = V4L2_STD_NTSC;
+ video_index = TV_NTSC;
+ cam->crop_current.top = 12;
+ } else {
+ cam->standard.id = V4L2_STD_ALL;
+ video_index = TV_NOT_LOCKED;
+ cam->crop_current.top = 0;
+ pr_err("ERROR: unrecognized std! %Lx (PAL=%Lx, NTSC=%Lx\n",
+ e, V4L2_STD_PAL, V4L2_STD_NTSC);
+ }
+
+ cam->standard.index = video_index;
+ strcpy(cam->standard.name, video_fmts[video_index].name);
+ cam->crop_bounds.width = video_fmts[video_index].raw_width;
+ cam->crop_bounds.height = video_fmts[video_index].raw_height;
+ cam->crop_current.width = video_fmts[video_index].active_width;
+ cam->crop_current.height = video_fmts[video_index].active_height;
+ cam->crop_current.left = 0;
+
+ return 0;
+}
+
+/*!
+ * V4L2 - mxc_v4l2_g_std function
+ *
+ * Gets the TV standard from the TV input device.
+ *
+ * @param cam structure cam_data *
+ *
+ * @param e structure v4l2_streamparm *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_v4l2_g_std(cam_data *cam, v4l2_std_id *e)
+{
+ struct v4l2_format tv_fmt;
+
+ pr_debug("In mxc_v4l2_g_std\n");
+
+ if (cam->device_type == 1) {
+ /* Use this function to get what the TV-In device detects the
+ * format to be. pixelformat is used to return the std value
+ * since the interface has no vidioc_g_std.*/
+ tv_fmt.type = V4L2_BUF_TYPE_PRIVATE;
+ vidioc_int_g_fmt_cap(cam->sensor, &tv_fmt);
+
+ /* If the TV-in automatically detects the standard, then if it
+ * changes, the settings need to change. */
+ if (cam->standard_autodetect) {
+ if (cam->standard.id != tv_fmt.fmt.pix.pixelformat) {
+ pr_debug("MVC: mxc_v4l2_g_std: "
+ "Changing standard\n");
+ mxc_v4l2_s_std(cam, tv_fmt.fmt.pix.pixelformat);
+ }
+ }
+
+ *e = tv_fmt.fmt.pix.pixelformat;
+ }
+
+ return 0;
+}
+
+/*!
+ * Dequeue one V4L capture buffer
+ *
+ * @param cam structure cam_data *
+ * @param buf structure v4l2_buffer *
+ *
+ * @return status 0 success, EINVAL invalid frame number,
+ * ETIME timeout, ERESTARTSYS interrupted by user
+ */
+static int mxc_v4l_dqueue(cam_data *cam, struct v4l2_buffer *buf)
+{
+ int retval = 0;
+ struct mxc_v4l_frame *frame;
+ unsigned long lock_flags;
+
+ pr_debug("In MVC:mxc_v4l_dqueue\n");
+
+ if (!wait_event_interruptible_timeout(cam->enc_queue,
+ cam->enc_counter != 0, 10 * HZ)) {
+ pr_err("ERROR: v4l2 capture: mxc_v4l_dqueue timeout "
+ "enc_counter %x\n",
+ cam->enc_counter);
+ return -ETIME;
+ } else if (signal_pending(current)) {
+ pr_err("ERROR: v4l2 capture: mxc_v4l_dqueue() "
+ "interrupt received\n");
+ return -ERESTARTSYS;
+ }
+
+ spin_lock_irqsave(&cam->dqueue_int_lock, lock_flags);
+
+ cam->enc_counter--;
+
+ frame = list_entry(cam->done_q.next, struct mxc_v4l_frame, queue);
+ list_del(cam->done_q.next);
+ if (frame->buffer.flags & V4L2_BUF_FLAG_DONE) {
+ frame->buffer.flags &= ~V4L2_BUF_FLAG_DONE;
+ } else if (frame->buffer.flags & V4L2_BUF_FLAG_QUEUED) {
+ pr_err("ERROR: v4l2 capture: VIDIOC_DQBUF: "
+ "Buffer not filled.\n");
+ frame->buffer.flags &= ~V4L2_BUF_FLAG_QUEUED;
+ retval = -EINVAL;
+ } else if ((frame->buffer.flags & 0x7) == V4L2_BUF_FLAG_MAPPED) {
+ pr_err("ERROR: v4l2 capture: VIDIOC_DQBUF: "
+ "Buffer not queued.\n");
+ retval = -EINVAL;
+ }
+
+ buf->bytesused = cam->v2f.fmt.pix.sizeimage;
+ buf->index = frame->index;
+ buf->flags = frame->buffer.flags;
+ buf->m = cam->frame[frame->index].buffer.m;
+ buf->timestamp = cam->frame[frame->index].buffer.timestamp;
+
+ spin_unlock_irqrestore(&cam->dqueue_int_lock, lock_flags);
+ return retval;
+}
+
+/*!
+ * V4L interface - open function
+ *
+ * @param file structure file *
+ *
+ * @return status 0 success, ENODEV invalid device instance,
+ * ENODEV timeout, ERESTARTSYS interrupted by user
+ */
+static int mxc_v4l_open(struct file *file)
+{
+ struct v4l2_ifparm ifparm;
+ struct v4l2_format cam_fmt;
+ ipu_csi_signal_cfg_t csi_param;
+ struct video_device *dev = video_devdata(file);
+ cam_data *cam = video_get_drvdata(dev);
+ int err = 0;
+
+ pr_debug("\nIn MVC: mxc_v4l_open\n");
+ pr_debug(" device name is %s\n", dev->name);
+
+ if (!cam) {
+ pr_err("ERROR: v4l2 capture: Internal error, "
+ "cam_data not found!\n");
+ return -EBADF;
+ }
+
+ down(&cam->busy_lock);
+ err = 0;
+ if (signal_pending(current))
+ goto oops;
+
+ if (cam->open_count++ == 0) {
+ wait_event_interruptible(cam->power_queue,
+ cam->low_power == false);
+
+ if (strcmp(mxc_capture_inputs[cam->current_input].name,
+ "CSI MEM") == 0) {
+#if defined(CONFIG_MXC_IPU_CSI_ENC) || defined(CONFIG_MXC_IPU_CSI_ENC_MODULE)
+ err = csi_enc_select(cam);
+#endif
+ } else if (strcmp(mxc_capture_inputs[cam->current_input].name,
+ "CSI IC MEM") == 0) {
+#if defined(CONFIG_MXC_IPU_PRP_ENC) || defined(CONFIG_MXC_IPU_PRP_ENC_MODULE)
+ err = prp_enc_select(cam);
+#endif
+ }
+
+ cam->enc_counter = 0;
+ cam->skip_frame = 0;
+ INIT_LIST_HEAD(&cam->ready_q);
+ INIT_LIST_HEAD(&cam->working_q);
+ INIT_LIST_HEAD(&cam->done_q);
+
+ vidioc_int_g_ifparm(cam->sensor, &ifparm);
+
+ csi_param.sens_clksrc = 0;
+
+ csi_param.clk_mode = 0;
+ csi_param.data_pol = 0;
+ csi_param.ext_vsync = 0;
+
+ csi_param.pack_tight = 0;
+ csi_param.force_eof = 0;
+ csi_param.data_en_pol = 0;
+ csi_param.mclk = ifparm.u.bt656.clock_curr;
+
+ csi_param.pixclk_pol = ifparm.u.bt656.latch_clk_inv;
+
+ /* Once we handle multiple inputs this will need to change. */
+ csi_param.csi = 0;
+
+ if (ifparm.u.bt656.mode
+ == V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT)
+ csi_param.data_width = IPU_CSI_DATA_WIDTH_8;
+ else if (ifparm.u.bt656.mode
+ == V4L2_IF_TYPE_BT656_MODE_NOBT_10BIT)
+ csi_param.data_width = IPU_CSI_DATA_WIDTH_10;
+ else
+ csi_param.data_width = IPU_CSI_DATA_WIDTH_8;
+
+
+ csi_param.Vsync_pol = ifparm.u.bt656.nobt_vs_inv;
+ csi_param.Hsync_pol = ifparm.u.bt656.nobt_hs_inv;
+
+ csi_param.csi = cam->csi;
+
+ cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ vidioc_int_g_fmt_cap(cam->sensor, &cam_fmt);
+
+ /* Reset the sizes. Needed to prevent carryover of last
+ * operation.*/
+ cam->crop_bounds.top = cam->crop_bounds.left = 0;
+ cam->crop_bounds.width = cam_fmt.fmt.pix.width;
+ cam->crop_bounds.height = cam_fmt.fmt.pix.height;
+
+ /* This also is the max crop size for this device. */
+ cam->crop_defrect.top = cam->crop_defrect.left = 0;
+ cam->crop_defrect.width = cam_fmt.fmt.pix.width;
+ cam->crop_defrect.height = cam_fmt.fmt.pix.height;
+
+ /* At this point, this is also the current image size. */
+ cam->crop_current.top = cam->crop_current.left = 0;
+ cam->crop_current.width = cam_fmt.fmt.pix.width;
+ cam->crop_current.height = cam_fmt.fmt.pix.height;
+
+ pr_debug("End of %s: v2f pix widthxheight %d x %d\n",
+ __func__,
+ cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height);
+ pr_debug("End of %s: crop_bounds widthxheight %d x %d\n",
+ __func__,
+ cam->crop_bounds.width, cam->crop_bounds.height);
+ pr_debug("End of %s: crop_defrect widthxheight %d x %d\n",
+ __func__,
+ cam->crop_defrect.width, cam->crop_defrect.height);
+ pr_debug("End of %s: crop_current widthxheight %d x %d\n",
+ __func__,
+ cam->crop_current.width, cam->crop_current.height);
+
+ csi_param.data_fmt = cam_fmt.fmt.pix.pixelformat;
+ pr_debug("On Open: Input to ipu size is %d x %d\n",
+ cam_fmt.fmt.pix.width, cam_fmt.fmt.pix.height);
+ ipu_csi_set_window_size(cam->crop_current.width,
+ cam->crop_current.width,
+ cam->csi);
+ ipu_csi_set_window_pos(cam->crop_current.left,
+ cam->crop_current.top,
+ cam->csi);
+ ipu_csi_init_interface(cam->crop_bounds.width,
+ cam->crop_bounds.height,
+ cam_fmt.fmt.pix.pixelformat,
+ csi_param);
+
+ ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi,
+ true, true);
+ vidioc_int_init(cam->sensor);
+
+ ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi,
+ false, false);
+}
+
+ file->private_data = dev;
+
+ oops:
+ up(&cam->busy_lock);
+ return err;
+}
+
+/*!
+ * V4L interface - close function
+ *
+ * @param file struct file *
+ *
+ * @return 0 success
+ */
+static int mxc_v4l_close(struct file *file)
+{
+ struct video_device *dev = video_devdata(file);
+ int err = 0;
+ cam_data *cam = video_get_drvdata(dev);
+
+ pr_debug("In MVC:mxc_v4l_close\n");
+
+ if (!cam) {
+ pr_err("ERROR: v4l2 capture: Internal error, "
+ "cam_data not found!\n");
+ return -EBADF;
+ }
+
+ /* for the case somebody hit the ctrl C */
+ if (cam->overlay_pid == current->pid) {
+ err = stop_preview(cam);
+ cam->overlay_on = false;
+ }
+ if (cam->capture_pid == current->pid) {
+ err |= mxc_streamoff(cam);
+ wake_up_interruptible(&cam->enc_queue);
+ }
+
+ if (--cam->open_count == 0) {
+ wait_event_interruptible(cam->power_queue,
+ cam->low_power == false);
+ pr_info("mxc_v4l_close: release resource\n");
+
+ if (strcmp(mxc_capture_inputs[cam->current_input].name,
+ "CSI MEM") == 0) {
+#if defined(CONFIG_MXC_IPU_CSI_ENC) || defined(CONFIG_MXC_IPU_CSI_ENC_MODULE)
+ err |= csi_enc_deselect(cam);
+#endif
+ } else if (strcmp(mxc_capture_inputs[cam->current_input].name,
+ "CSI IC MEM") == 0) {
+#if defined(CONFIG_MXC_IPU_PRP_ENC) || defined(CONFIG_MXC_IPU_PRP_ENC_MODULE)
+ err |= prp_enc_deselect(cam);
+#endif
+ }
+
+ mxc_free_frame_buf(cam);
+ file->private_data = NULL;
+
+ /* capture off */
+ wake_up_interruptible(&cam->enc_queue);
+ mxc_free_frames(cam);
+ cam->enc_counter++;
+ }
+
+ return err;
+}
+
+#if defined(CONFIG_MXC_IPU_PRP_ENC) || defined(CONFIG_MXC_IPU_CSI_ENC) || \
+ defined(CONFIG_MXC_IPU_PRP_ENC_MODULE) || \
+ defined(CONFIG_MXC_IPU_CSI_ENC_MODULE)
+/*
+ * V4L interface - read function
+ *
+ * @param file struct file *
+ * @param read buf char *
+ * @param count size_t
+ * @param ppos structure loff_t *
+ *
+ * @return bytes read
+ */
+static ssize_t mxc_v4l_read(struct file *file, char *buf, size_t count,
+ loff_t *ppos)
+{
+ int err = 0;
+ u8 *v_address[2];
+ struct video_device *dev = video_devdata(file);
+ cam_data *cam = video_get_drvdata(dev);
+
+ if (down_interruptible(&cam->busy_lock))
+ return -EINTR;
+
+ /* Stop the viewfinder */
+ if (cam->overlay_on == true)
+ stop_preview(cam);
+
+ v_address[0] = dma_alloc_coherent(0,
+ PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage),
+ &cam->still_buf[0],
+ GFP_DMA | GFP_KERNEL);
+
+ v_address[1] = dma_alloc_coherent(0,
+ PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage),
+ &cam->still_buf[1],
+ GFP_DMA | GFP_KERNEL);
+
+ if (!v_address[0] || !v_address[1]) {
+ err = -ENOBUFS;
+ goto exit0;
+ }
+
+ err = prp_still_select(cam);
+ if (err != 0) {
+ err = -EIO;
+ goto exit0;
+ }
+
+ cam->still_counter = 0;
+ err = cam->csi_start(cam);
+ if (err != 0) {
+ err = -EIO;
+ goto exit1;
+ }
+
+ if (!wait_event_interruptible_timeout(cam->still_queue,
+ cam->still_counter != 0,
+ 10 * HZ)) {
+ pr_err("ERROR: v4l2 capture: mxc_v4l_read timeout counter %x\n",
+ cam->still_counter);
+ err = -ETIME;
+ goto exit1;
+ }
+ err = copy_to_user(buf, v_address[1], cam->v2f.fmt.pix.sizeimage);
+
+ exit1:
+ prp_still_deselect(cam);
+
+ exit0:
+ if (v_address[0] != 0)
+ dma_free_coherent(0, cam->v2f.fmt.pix.sizeimage, v_address[0],
+ cam->still_buf[0]);
+ if (v_address[1] != 0)
+ dma_free_coherent(0, cam->v2f.fmt.pix.sizeimage, v_address[1],
+ cam->still_buf[1]);
+
+ cam->still_buf[0] = cam->still_buf[1] = 0;
+
+ if (cam->overlay_on == true) {
+ start_preview(cam);
+ }
+
+ up(&cam->busy_lock);
+ if (err < 0)
+ return err;
+
+ return (cam->v2f.fmt.pix.sizeimage - err);
+}
+#endif
+
+/*!
+ * V4L interface - ioctl function
+ *
+ * @param file struct file*
+ *
+ * @param ioctlnr unsigned int
+ *
+ * @param arg void*
+ *
+ * @return 0 success, ENODEV for invalid device instance,
+ * -1 for other errors.
+ */
+static long mxc_v4l_do_ioctl(struct file *file,
+ unsigned int ioctlnr, void *arg)
+{
+ struct video_device *dev = video_devdata(file);
+ cam_data *cam = video_get_drvdata(dev);
+ int retval = 0;
+ unsigned long lock_flags;
+
+ pr_debug("In MVC: mxc_v4l_do_ioctl %x\n", ioctlnr);
+ wait_event_interruptible(cam->power_queue, cam->low_power == false);
+ /* make this _really_ smp-safe */
+ if (down_interruptible(&cam->busy_lock))
+ return -EBUSY;
+
+ switch (ioctlnr) {
+ /*!
+ * V4l2 VIDIOC_QUERYCAP ioctl
+ */
+ case VIDIOC_QUERYCAP: {
+ struct v4l2_capability *cap = arg;
+ pr_debug(" case VIDIOC_QUERYCAP\n");
+ strcpy(cap->driver, "mxc_v4l2");
+ cap->version = KERNEL_VERSION(0, 1, 11);
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_VIDEO_OVERLAY |
+ V4L2_CAP_STREAMING |
+ V4L2_CAP_READWRITE;
+ cap->card[0] = '\0';
+ cap->bus_info[0] = '\0';
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_G_FMT ioctl
+ */
+ case VIDIOC_G_FMT: {
+ struct v4l2_format *gf = arg;
+ pr_debug(" case VIDIOC_G_FMT\n");
+ retval = mxc_v4l2_g_fmt(cam, gf);
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_S_FMT ioctl
+ */
+ case VIDIOC_S_FMT: {
+ struct v4l2_format *sf = arg;
+ pr_debug(" case VIDIOC_S_FMT\n");
+ retval = mxc_v4l2_s_fmt(cam, sf);
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_REQBUFS ioctl
+ */
+ case VIDIOC_REQBUFS: {
+ struct v4l2_requestbuffers *req = arg;
+ pr_debug(" case VIDIOC_REQBUFS\n");
+
+ if (req->count > FRAME_NUM) {
+ pr_err("ERROR: v4l2 capture: VIDIOC_REQBUFS: "
+ "not enough buffers\n");
+ req->count = FRAME_NUM;
+ }
+
+ if ((req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
+ (req->memory != V4L2_MEMORY_MMAP)) {
+ pr_err("ERROR: v4l2 capture: VIDIOC_REQBUFS: "
+ "wrong buffer type\n");
+ retval = -EINVAL;
+ break;
+ }
+
+ mxc_streamoff(cam);
+ mxc_free_frame_buf(cam);
+ cam->enc_counter = 0;
+ cam->skip_frame = 0;
+ INIT_LIST_HEAD(&cam->ready_q);
+ INIT_LIST_HEAD(&cam->working_q);
+ INIT_LIST_HEAD(&cam->done_q);
+
+ retval = mxc_allocate_frame_buf(cam, req->count);
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_QUERYBUF ioctl
+ */
+ case VIDIOC_QUERYBUF: {
+ struct v4l2_buffer *buf = arg;
+ int index = buf->index;
+ pr_debug(" case VIDIOC_QUERYBUF\n");
+
+ if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ pr_err("ERROR: v4l2 capture: "
+ "VIDIOC_QUERYBUFS: "
+ "wrong buffer type\n");
+ retval = -EINVAL;
+ break;
+ }
+
+ memset(buf, 0, sizeof(buf));
+ buf->index = index;
+
+ down(&cam->param_lock);
+ retval = mxc_v4l2_buffer_status(cam, buf);
+ up(&cam->param_lock);
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_QBUF ioctl
+ */
+ case VIDIOC_QBUF: {
+ struct v4l2_buffer *buf = arg;
+ int index = buf->index;
+ pr_debug(" case VIDIOC_QBUF\n");
+
+ spin_lock_irqsave(&cam->queue_int_lock, lock_flags);
+ cam->frame[index].buffer.m.offset = buf->m.offset;
+ if ((cam->frame[index].buffer.flags & 0x7) ==
+ V4L2_BUF_FLAG_MAPPED) {
+ cam->frame[index].buffer.flags |=
+ V4L2_BUF_FLAG_QUEUED;
+ if (strcmp(mxc_capture_inputs[cam->current_input].name,
+ "CSI IC MEM") == 0) {
+ if (cam->skip_frame > 0) {
+ list_add_tail(&cam->frame[index].queue,
+ &cam->working_q);
+
+ retval =
+ cam->enc_update_eba(cam->
+ frame[index].
+ buffer.m.offset,
+ &cam->
+ ping_pong_csi);
+
+ cam->skip_frame = 0;
+ } else
+ list_add_tail(&cam->frame[index].queue,
+ &cam->ready_q);
+ } else if (strcmp(
+ mxc_capture_inputs[cam->current_input].
+ name, "CSI MEM") == 0) {
+ list_add_tail(&cam->frame[index].queue,
+ &cam->ready_q);
+ }
+ } else if (cam->frame[index].buffer.
+ flags & V4L2_BUF_FLAG_QUEUED) {
+ pr_err("ERROR: v4l2 capture: VIDIOC_QBUF: "
+ "buffer already queued\n");
+ retval = -EINVAL;
+ } else if (cam->frame[index].buffer.
+ flags & V4L2_BUF_FLAG_DONE) {
+ pr_err("ERROR: v4l2 capture: VIDIOC_QBUF: "
+ "overwrite done buffer.\n");
+ cam->frame[index].buffer.flags &=
+ ~V4L2_BUF_FLAG_DONE;
+ cam->frame[index].buffer.flags |=
+ V4L2_BUF_FLAG_QUEUED;
+ retval = -EINVAL;
+ }
+
+ buf->flags = cam->frame[index].buffer.flags;
+ spin_unlock_irqrestore(&cam->queue_int_lock, lock_flags);
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_DQBUF ioctl
+ */
+ case VIDIOC_DQBUF: {
+ struct v4l2_buffer *buf = arg;
+ pr_debug(" case VIDIOC_DQBUF\n");
+
+ if ((cam->enc_counter == 0) &&
+ (file->f_flags & O_NONBLOCK)) {
+ retval = -EAGAIN;
+ break;
+ }
+
+ retval = mxc_v4l_dqueue(cam, buf);
+
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_STREAMON ioctl
+ */
+ case VIDIOC_STREAMON: {
+ pr_debug(" case VIDIOC_STREAMON\n");
+ retval = mxc_streamon(cam);
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_STREAMOFF ioctl
+ */
+ case VIDIOC_STREAMOFF: {
+ pr_debug(" case VIDIOC_STREAMOFF\n");
+ retval = mxc_streamoff(cam);
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_G_CTRL ioctl
+ */
+ case VIDIOC_G_CTRL: {
+ pr_debug(" case VIDIOC_G_CTRL\n");
+ retval = mxc_v4l2_g_ctrl(cam, arg);
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_S_CTRL ioctl
+ */
+ case VIDIOC_S_CTRL: {
+ pr_debug(" case VIDIOC_S_CTRL\n");
+ retval = mxc_v4l2_s_ctrl(cam, arg);
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_CROPCAP ioctl
+ */
+ case VIDIOC_CROPCAP: {
+ struct v4l2_cropcap *cap = arg;
+ pr_debug(" case VIDIOC_CROPCAP\n");
+ if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ cap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) {
+ retval = -EINVAL;
+ break;
+ }
+ cap->bounds = cam->crop_bounds;
+ cap->defrect = cam->crop_defrect;
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_G_CROP ioctl
+ */
+ case VIDIOC_G_CROP: {
+ struct v4l2_crop *crop = arg;
+ pr_debug(" case VIDIOC_G_CROP\n");
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) {
+ retval = -EINVAL;
+ break;
+ }
+ crop->c = cam->crop_current;
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_S_CROP ioctl
+ */
+ case VIDIOC_S_CROP: {
+ struct v4l2_crop *crop = arg;
+ struct v4l2_rect *b = &cam->crop_bounds;
+ pr_debug(" case VIDIOC_S_CROP\n");
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) {
+ retval = -EINVAL;
+ break;
+ }
+
+ crop->c.top = (crop->c.top < b->top) ? b->top
+ : crop->c.top;
+ if (crop->c.top > b->top + b->height)
+ crop->c.top = b->top + b->height - 1;
+ if (crop->c.height > b->top + b->height - crop->c.top)
+ crop->c.height =
+ b->top + b->height - crop->c.top;
+
+ crop->c.left = (crop->c.left < b->left) ? b->left
+ : crop->c.left;
+ if (crop->c.left > b->left + b->width)
+ crop->c.left = b->left + b->width - 1;
+ if (crop->c.width > b->left - crop->c.left + b->width)
+ crop->c.width =
+ b->left - crop->c.left + b->width;
+
+ crop->c.width -= crop->c.width % 8;
+ crop->c.left -= crop->c.left % 4;
+ cam->crop_current = crop->c;
+
+ pr_debug(" Cropping Input to ipu size %d x %d\n",
+ cam->crop_current.width,
+ cam->crop_current.height);
+ ipu_csi_set_window_size(cam->crop_current.width,
+ cam->crop_current.height,
+ cam->csi);
+ ipu_csi_set_window_pos(cam->crop_current.left,
+ cam->crop_current.top,
+ cam->csi);
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_OVERLAY ioctl
+ */
+ case VIDIOC_OVERLAY: {
+ int *on = arg;
+ pr_debug(" VIDIOC_OVERLAY on=%d\n", *on);
+ if (*on) {
+ cam->overlay_on = true;
+ cam->overlay_pid = current->pid;
+ retval = start_preview(cam);
+ }
+ if (!*on) {
+ retval = stop_preview(cam);
+ cam->overlay_on = false;
+ }
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_G_FBUF ioctl
+ */
+ case VIDIOC_G_FBUF: {
+ struct v4l2_framebuffer *fb = arg;
+ pr_debug(" case VIDIOC_G_FBUF\n");
+ *fb = cam->v4l2_fb;
+ fb->capability = V4L2_FBUF_CAP_EXTERNOVERLAY;
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_S_FBUF ioctl
+ */
+ case VIDIOC_S_FBUF: {
+ struct v4l2_framebuffer *fb = arg;
+ pr_debug(" case VIDIOC_S_FBUF\n");
+ cam->v4l2_fb = *fb;
+ break;
+ }
+
+ case VIDIOC_G_PARM: {
+ struct v4l2_streamparm *parm = arg;
+ pr_debug(" case VIDIOC_G_PARM\n");
+ vidioc_int_g_parm(cam->sensor, parm);
+ break;
+ }
+
+ case VIDIOC_S_PARM: {
+ struct v4l2_streamparm *parm = arg;
+ pr_debug(" case VIDIOC_S_PARM\n");
+ retval = mxc_v4l2_s_param(cam, parm);
+ break;
+ }
+
+ /* linux v4l2 bug, kernel c0485619 user c0405619 */
+ case VIDIOC_ENUMSTD: {
+ struct v4l2_standard *e = arg;
+ pr_debug(" case VIDIOC_ENUMSTD\n");
+ *e = cam->standard;
+ break;
+ }
+
+ case VIDIOC_G_STD: {
+ v4l2_std_id *e = arg;
+ pr_debug(" case VIDIOC_G_STD\n");
+ retval = mxc_v4l2_g_std(cam, e);
+ break;
+ }
+
+ case VIDIOC_S_STD: {
+ v4l2_std_id *e = arg;
+ pr_debug(" case VIDIOC_S_STD\n");
+ retval = mxc_v4l2_s_std(cam, *e);
+
+ break;
+ }
+
+ case VIDIOC_ENUMOUTPUT: {
+ struct v4l2_output *output = arg;
+ pr_debug(" case VIDIOC_ENUMOUTPUT\n");
+ if (output->index >= MXC_V4L2_CAPTURE_NUM_OUTPUTS) {
+ retval = -EINVAL;
+ break;
+ }
+ *output = mxc_capture_outputs[output->index];
+
+ break;
+ }
+ case VIDIOC_G_OUTPUT: {
+ int *p_output_num = arg;
+ pr_debug(" case VIDIOC_G_OUTPUT\n");
+ *p_output_num = cam->output;
+ break;
+ }
+
+ case VIDIOC_S_OUTPUT: {
+ int *p_output_num = arg;
+ pr_debug(" case VIDIOC_S_OUTPUT\n");
+ if (*p_output_num >= MXC_V4L2_CAPTURE_NUM_OUTPUTS) {
+ retval = -EINVAL;
+ break;
+ }
+ cam->output = *p_output_num;
+ break;
+ }
+
+ case VIDIOC_ENUMINPUT: {
+ struct v4l2_input *input = arg;
+ pr_debug(" case VIDIOC_ENUMINPUT\n");
+ if (input->index >= MXC_V4L2_CAPTURE_NUM_INPUTS) {
+ retval = -EINVAL;
+ break;
+ }
+ *input = mxc_capture_inputs[input->index];
+ break;
+ }
+
+ case VIDIOC_G_INPUT: {
+ int *index = arg;
+ pr_debug(" case VIDIOC_G_INPUT\n");
+ *index = cam->current_input;
+ break;
+ }
+
+ case VIDIOC_S_INPUT: {
+ int *index = arg;
+ pr_debug(" case VIDIOC_S_INPUT\n");
+ if (*index >= MXC_V4L2_CAPTURE_NUM_INPUTS) {
+ retval = -EINVAL;
+ break;
+ }
+
+ if (*index == cam->current_input)
+ break;
+
+ if ((mxc_capture_inputs[cam->current_input].status &
+ V4L2_IN_ST_NO_POWER) == 0) {
+ retval = mxc_streamoff(cam);
+ if (retval)
+ break;
+ mxc_capture_inputs[cam->current_input].status |=
+ V4L2_IN_ST_NO_POWER;
+ }
+
+ if (strcmp(mxc_capture_inputs[*index].name, "CSI MEM") == 0) {
+#if defined(CONFIG_MXC_IPU_CSI_ENC) || defined(CONFIG_MXC_IPU_CSI_ENC_MODULE)
+ retval = csi_enc_select(cam);
+ if (retval)
+ break;
+#endif
+ } else if (strcmp(mxc_capture_inputs[*index].name,
+ "CSI IC MEM") == 0) {
+#if defined(CONFIG_MXC_IPU_PRP_ENC) || defined(CONFIG_MXC_IPU_PRP_ENC_MODULE)
+ retval = prp_enc_select(cam);
+ if (retval)
+ break;
+#endif
+ }
+
+ mxc_capture_inputs[*index].status &= ~V4L2_IN_ST_NO_POWER;
+ cam->current_input = *index;
+ break;
+ }
+
+ case VIDIOC_ENUM_FMT:
+ case VIDIOC_TRY_FMT:
+ case VIDIOC_QUERYCTRL:
+ case VIDIOC_G_TUNER:
+ case VIDIOC_S_TUNER:
+ case VIDIOC_G_FREQUENCY:
+ case VIDIOC_S_FREQUENCY:
+ default:
+ pr_debug(" case default or not supported\n");
+ retval = -EINVAL;
+ break;
+ }
+
+ up(&cam->busy_lock);
+ return retval;
+}
+
+/*
+ * V4L interface - ioctl function
+ *
+ * @return None
+ */
+static long mxc_v4l_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ pr_debug("In MVC:mxc_v4l_ioctl\n");
+ return video_usercopy(file, cmd, arg, mxc_v4l_do_ioctl);
+}
+
+/*!
+ * V4L interface - mmap function
+ *
+ * @param file structure file *
+ *
+ * @param vma structure vm_area_struct *
+ *
+ * @return status 0 Success, EINTR busy lock error, ENOBUFS remap_page error
+ */
+static int mxc_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct video_device *dev = video_devdata(file);
+ unsigned long size;
+ int res = 0;
+ cam_data *cam = video_get_drvdata(dev);
+
+ pr_debug("In MVC:mxc_mmap\n");
+ pr_debug(" pgoff=0x%lx, start=0x%lx, end=0x%lx\n",
+ vma->vm_pgoff, vma->vm_start, vma->vm_end);
+
+ /* make this _really_ smp-safe */
+ if (down_interruptible(&cam->busy_lock))
+ return -EINTR;
+
+ size = vma->vm_end - vma->vm_start;
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ if (remap_pfn_range(vma, vma->vm_start,
+ vma->vm_pgoff, size, vma->vm_page_prot)) {
+ pr_err("ERROR: v4l2 capture: mxc_mmap: "
+ "remap_pfn_range failed\n");
+ res = -ENOBUFS;
+ goto mxc_mmap_exit;
+ }
+
+ vma->vm_flags &= ~VM_IO; /* using shared anonymous pages */
+
+ mxc_mmap_exit:
+ up(&cam->busy_lock);
+ return res;
+}
+
+/*!
+ * V4L interface - poll function
+ *
+ * @param file structure file *
+ *
+ * @param wait structure poll_table_struct *
+ *
+ * @return status POLLIN | POLLRDNORM
+ */
+static unsigned int mxc_poll(struct file *file, struct poll_table_struct *wait)
+{
+ struct video_device *dev = video_devdata(file);
+ cam_data *cam = video_get_drvdata(dev);
+ wait_queue_head_t *queue = NULL;
+ int res = POLLIN | POLLRDNORM;
+
+ pr_debug("In MVC:mxc_poll\n");
+
+ if (down_interruptible(&cam->busy_lock))
+ return -EINTR;
+
+ queue = &cam->enc_queue;
+ poll_wait(file, queue, wait);
+
+ up(&cam->busy_lock);
+
+ return res;
+}
+
+/*!
+ * This structure defines the functions to be called in this driver.
+ */
+static struct v4l2_file_operations mxc_v4l_fops = {
+ .owner = THIS_MODULE,
+ .open = mxc_v4l_open,
+ .release = mxc_v4l_close,
+ .read = mxc_v4l_read,
+ .ioctl = mxc_v4l_ioctl,
+ .mmap = mxc_mmap,
+ .poll = mxc_poll,
+};
+
+static struct video_device mxc_v4l_template = {
+ .name = "Mxc Camera",
+ .fops = &mxc_v4l_fops,
+ .release = video_device_release,
+};
+
+/*!
+ * This function can be used to release any platform data on closing.
+ */
+static void camera_platform_release(struct device *device)
+{
+}
+
+/*!
+ * Camera V4l2 callback function.
+ *
+ * @param mask u32
+ *
+ * @param dev void device structure
+ *
+ * @return status
+ */
+static void camera_callback(u32 mask, void *dev)
+{
+ struct mxc_v4l_frame *done_frame;
+ struct mxc_v4l_frame *ready_frame;
+ struct timeval cur_time;
+
+ cam_data *cam = (cam_data *) dev;
+ if (cam == NULL)
+ return;
+
+ pr_debug("In MVC:camera_callback\n");
+
+ if (strcmp(mxc_capture_inputs[cam->current_input].name, "CSI IC MEM")
+ == 0) {
+ if (list_empty(&cam->working_q)) {
+ pr_err("ERROR: v4l2 capture: camera_callback: "
+ "working queue empty\n");
+ return;
+ }
+ do_gettimeofday(&cur_time);
+
+ done_frame = list_entry(cam->working_q.next,
+ struct mxc_v4l_frame,
+ queue);
+
+ /*
+ * Set the current time to done frame buffer's timestamp.
+ * Users can use this information to judge the frame's usage.
+ */
+ done_frame->buffer.timestamp = cur_time;
+ if (done_frame->buffer.flags & V4L2_BUF_FLAG_QUEUED) {
+ done_frame->buffer.flags |= V4L2_BUF_FLAG_DONE;
+ done_frame->buffer.flags &= ~V4L2_BUF_FLAG_QUEUED;
+
+ /* Added to the done queue */
+ list_del(cam->working_q.next);
+ list_add_tail(&done_frame->queue, &cam->done_q);
+
+ /* Wake up the queue */
+ cam->enc_counter++;
+ wake_up_interruptible(&cam->enc_queue);
+
+ if (list_empty(&cam->ready_q)) {
+ cam->skip_frame++;
+ } else {
+ ready_frame = list_entry(cam->ready_q.next,
+ struct mxc_v4l_frame,
+ queue);
+
+ if (cam->enc_update_eba(
+ ready_frame->buffer.m.offset,
+ &cam->ping_pong_csi) == 0) {
+ list_del(cam->ready_q.next);
+ list_add_tail(&ready_frame->queue,
+ &cam->working_q);
+ } else
+ return;
+ }
+ } else {
+ pr_err("ERROR: v4l2 capture: camera_callback: "
+ "buffer not queued\n");
+ }
+ } else if (strcmp(mxc_capture_inputs[cam->current_input].name,
+ "CSI MEM") == 0) {
+ if (!list_empty(&cam->working_q)) {
+ do_gettimeofday(&cur_time);
+
+ done_frame = list_entry(cam->working_q.next,
+ struct mxc_v4l_frame,
+ queue);
+
+ /*
+ * Set the current time to done frame buffer's
+ * timestamp. Users can use this information to judge
+ * the frame's usage.
+ */
+ done_frame->buffer.timestamp = cur_time;
+
+ if (done_frame->buffer.flags & V4L2_BUF_FLAG_QUEUED) {
+ done_frame->buffer.flags |=
+ V4L2_BUF_FLAG_DONE;
+ done_frame->buffer.flags &=
+ ~V4L2_BUF_FLAG_QUEUED;
+
+ /* Added to the done queue */
+ list_del(cam->working_q.next);
+ list_add_tail(&done_frame->queue, &cam->done_q);
+
+ /* Wake up the queue */
+ cam->enc_counter++;
+ wake_up_interruptible(&cam->enc_queue);
+ } else {
+ pr_err("ERROR: v4l2 capture: camera_callback: "
+ "buffer not queued\n");
+ }
+ }
+
+ if (!list_empty(&cam->ready_q)) {
+ ready_frame = list_entry(cam->ready_q.next,
+ struct mxc_v4l_frame,
+ queue);
+ if (cam->enc_update_eba(ready_frame->buffer.m.offset,
+ &cam->ping_pong_csi) == 0) {
+ list_del(cam->ready_q.next);
+ list_add_tail(&ready_frame->queue,
+ &cam->working_q);
+ } else
+ return;
+ } else {
+ if (cam->enc_update_eba(
+ cam->dummy_frame.buffer.m.offset,
+ &cam->ping_pong_csi) == -EACCES)
+ return;
+ }
+ }
+
+ return;
+}
+
+/*!
+ * initialize cam_data structure
+ *
+ * @param cam structure cam_data *
+ *
+ * @return status 0 Success
+ */
+static void init_camera_struct(cam_data *cam, struct platform_device *pdev)
+{
+ pr_debug("In MVC: init_camera_struct\n");
+
+ /* Default everything to 0 */
+ memset(cam, 0, sizeof(cam_data));
+
+ init_MUTEX(&cam->param_lock);
+ init_MUTEX(&cam->busy_lock);
+
+ cam->video_dev = video_device_alloc();
+ if (cam->video_dev == NULL)
+ return;
+
+ *(cam->video_dev) = mxc_v4l_template;
+
+ video_set_drvdata(cam->video_dev, cam);
+ dev_set_drvdata(&pdev->dev, (void *)cam);
+ cam->video_dev->minor = -1;
+
+ init_waitqueue_head(&cam->enc_queue);
+ init_waitqueue_head(&cam->still_queue);
+
+ /* setup cropping */
+ cam->crop_bounds.left = 0;
+ cam->crop_bounds.width = 640;
+ cam->crop_bounds.top = 0;
+ cam->crop_bounds.height = 480;
+ cam->crop_current = cam->crop_defrect = cam->crop_bounds;
+ ipu_csi_set_window_size(cam->crop_current.width,
+ cam->crop_current.height, cam->csi);
+ ipu_csi_set_window_pos(cam->crop_current.left,
+ cam->crop_current.top, cam->csi);
+ cam->streamparm.parm.capture.capturemode = 0;
+
+ cam->standard.index = 0;
+ cam->standard.id = V4L2_STD_UNKNOWN;
+ cam->standard.frameperiod.denominator = 30;
+ cam->standard.frameperiod.numerator = 1;
+ cam->standard.framelines = 480;
+ cam->standard_autodetect = true;
+ cam->streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ cam->streamparm.parm.capture.timeperframe = cam->standard.frameperiod;
+ cam->streamparm.parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+ cam->overlay_on = false;
+ cam->capture_on = false;
+ cam->skip_frame = 0;
+ cam->v4l2_fb.flags = V4L2_FBUF_FLAG_OVERLAY;
+
+ cam->v2f.fmt.pix.sizeimage = 352 * 288 * 3 / 2;
+ cam->v2f.fmt.pix.bytesperline = 288 * 3 / 2;
+ cam->v2f.fmt.pix.width = 288;
+ cam->v2f.fmt.pix.height = 352;
+ cam->v2f.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;
+ cam->win.w.width = 160;
+ cam->win.w.height = 160;
+ cam->win.w.left = 0;
+ cam->win.w.top = 0;
+
+ cam->csi = 0; /* Need to determine how to set this correctly with
+ * multiple video input devices. */
+
+ cam->enc_callback = camera_callback;
+ init_waitqueue_head(&cam->power_queue);
+ spin_lock_init(&cam->queue_int_lock);
+ spin_lock_init(&cam->dqueue_int_lock);
+}
+
+/*!
+ * camera_power function
+ * Turns Sensor power On/Off
+ *
+ * @param cam cam data struct
+ * @param cameraOn true to turn camera on, false to turn off power.
+ *
+ * @return status
+ */
+static u8 camera_power(cam_data *cam, bool cameraOn)
+{
+ pr_debug("In MVC:camera_power on=%d\n", cameraOn);
+
+ if (cameraOn == true) {
+ ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, true, true);
+ vidioc_int_s_power(cam->sensor, 1);
+ } else {
+ ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, false, false);
+ vidioc_int_s_power(cam->sensor, 0);
+ }
+ return 0;
+}
+
+/*!
+ * This function is called to probe the devices if registered.
+ *
+ * @param pdev the device structure used to give information on which device
+ * to probe
+ *
+ * @return The function returns 0 on success and -1 on failure.
+ */
+static int mxc_v4l2_probe(struct platform_device *pdev)
+{
+ /* Create g_cam and initialize it. */
+ g_cam = kmalloc(sizeof(cam_data), GFP_KERNEL);
+ if (g_cam == NULL) {
+ pr_err("ERROR: v4l2 capture: failed to register camera\n");
+ return -1;
+ }
+ init_camera_struct(g_cam, pdev);
+ pdev->dev.release = camera_platform_release;
+
+ /* Set up the v4l2 device and register it*/
+ mxc_v4l2_int_device.priv = g_cam;
+ /* This function contains a bug that won't let this be rmmod'd. */
+ v4l2_int_device_register(&mxc_v4l2_int_device);
+
+ /* register v4l video device */
+ if (video_register_device(g_cam->video_dev, VFL_TYPE_GRABBER, video_nr)
+ == -1) {
+ kfree(g_cam);
+ g_cam = NULL;
+ pr_err("ERROR: v4l2 capture: video_register_device failed\n");
+ return -1;
+ }
+ pr_debug(" Video device registered: %s #%d\n",
+ g_cam->video_dev->name, g_cam->video_dev->minor);
+
+ return 0;
+}
+
+/*!
+ * This function is called to remove the devices when device unregistered.
+ *
+ * @param pdev the device structure used to give information on which device
+ * to remove
+ *
+ * @return The function returns 0 on success and -1 on failure.
+ */
+static int mxc_v4l2_remove(struct platform_device *pdev)
+{
+
+ if (g_cam->open_count) {
+ pr_err("ERROR: v4l2 capture:camera open "
+ "-- setting ops to NULL\n");
+ return -EBUSY;
+ } else {
+ pr_info("V4L2 freeing image input device\n");
+ v4l2_int_device_unregister(&mxc_v4l2_int_device);
+ video_unregister_device(g_cam->video_dev);
+
+ mxc_free_frame_buf(g_cam);
+ kfree(g_cam);
+ g_cam = NULL;
+ }
+
+ pr_info("V4L2 unregistering video\n");
+ return 0;
+}
+
+/*!
+ * This function is called to put the sensor in a low power state.
+ * Refer to the document driver-model/driver.txt in the kernel source tree
+ * for more information.
+ *
+ * @param pdev the device structure used to give information on which I2C
+ * to suspend
+ * @param state the power state the device is entering
+ *
+ * @return The function returns 0 on success and -1 on failure.
+ */
+static int mxc_v4l2_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ cam_data *cam = platform_get_drvdata(pdev);
+
+ pr_debug("In MVC:mxc_v4l2_suspend\n");
+
+ if (cam == NULL) {
+ return -1;
+ }
+
+ cam->low_power = true;
+
+ if (cam->overlay_on == true)
+ stop_preview(cam);
+ if ((cam->capture_on == true) && cam->enc_disable) {
+ cam->enc_disable(cam);
+ }
+ camera_power(cam, false);
+
+ return 0;
+}
+
+/*!
+ * This function is called to bring the sensor back from a low power state.
+ * Refer to the document driver-model/driver.txt in the kernel source tree
+ * for more information.
+ *
+ * @param pdev the device structure
+ *
+ * @return The function returns 0 on success and -1 on failure
+ */
+static int mxc_v4l2_resume(struct platform_device *pdev)
+{
+ cam_data *cam = platform_get_drvdata(pdev);
+
+ pr_debug("In MVC:mxc_v4l2_resume\n");
+
+ if (cam == NULL) {
+ return -1;
+ }
+
+ cam->low_power = false;
+ wake_up_interruptible(&cam->power_queue);
+ camera_power(cam, true);
+
+ if (cam->overlay_on == true)
+ start_preview(cam);
+ if (cam->capture_on == true)
+ mxc_streamon(cam);
+
+ return 0;
+}
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxc_v4l2_driver = {
+ .driver = {
+ .name = "mxc_v4l2_capture",
+ },
+ .probe = mxc_v4l2_probe,
+ .remove = mxc_v4l2_remove,
+ .suspend = mxc_v4l2_suspend,
+ .resume = mxc_v4l2_resume,
+ .shutdown = NULL,
+};
+
+/*!
+ * Initializes the camera driver.
+ */
+static int mxc_v4l2_master_attach(struct v4l2_int_device *slave)
+{
+ cam_data *cam = slave->u.slave->master->priv;
+ struct v4l2_format cam_fmt;
+
+ pr_debug("In MVC: mxc_v4l2_master_attach\n");
+ pr_debug(" slave.name = %s\n", slave->name);
+ pr_debug(" master.name = %s\n", slave->u.slave->master->name);
+
+ cam->sensor = slave;
+ if (slave == NULL) {
+ pr_err("ERROR: v4l2 capture: slave parameter not valid.\n");
+ return -1;
+ }
+
+ ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, true, true);
+ vidioc_int_s_power(cam->sensor, 1);
+ vidioc_int_dev_init(slave);
+ ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, false, false);
+ cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ vidioc_int_g_fmt_cap(cam->sensor, &cam_fmt);
+
+ /* Used to detect TV in (type 1) vs. camera (type 0)*/
+ cam->device_type = cam_fmt.fmt.pix.priv;
+
+ /* Set the input size to the ipu for this device */
+ cam->crop_bounds.top = cam->crop_bounds.left = 0;
+ cam->crop_bounds.width = cam_fmt.fmt.pix.width;
+ cam->crop_bounds.height = cam_fmt.fmt.pix.height;
+
+ /* This also is the max crop size for this device. */
+ cam->crop_defrect.top = cam->crop_defrect.left = 0;
+ cam->crop_defrect.width = cam_fmt.fmt.pix.width;
+ cam->crop_defrect.height = cam_fmt.fmt.pix.height;
+
+ /* At this point, this is also the current image size. */
+ cam->crop_current.top = cam->crop_current.left = 0;
+ cam->crop_current.width = cam_fmt.fmt.pix.width;
+ cam->crop_current.height = cam_fmt.fmt.pix.height;
+
+ pr_debug("End of %s: v2f pix widthxheight %d x %d\n",
+ __func__,
+ cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height);
+ pr_debug("End of %s: crop_bounds widthxheight %d x %d\n",
+ __func__,
+ cam->crop_bounds.width, cam->crop_bounds.height);
+ pr_debug("End of %s: crop_defrect widthxheight %d x %d\n",
+ __func__,
+ cam->crop_defrect.width, cam->crop_defrect.height);
+ pr_debug("End of %s: crop_current widthxheight %d x %d\n",
+ __func__,
+ cam->crop_current.width, cam->crop_current.height);
+
+ return 0;
+}
+
+/*!
+ * Disconnects the camera driver.
+ */
+static void mxc_v4l2_master_detach(struct v4l2_int_device *slave)
+{
+ pr_debug("In MVC:mxc_v4l2_master_detach\n");
+ vidioc_int_dev_exit(slave);
+}
+
+/*!
+ * Entry point for the V4L2
+ *
+ * @return Error code indicating success or failure
+ */
+static __init int camera_init(void)
+{
+ u8 err = 0;
+
+ pr_debug("In MVC:camera_init\n");
+
+ /* Register the device driver structure. */
+ err = platform_driver_register(&mxc_v4l2_driver);
+ if (err != 0) {
+ pr_err("ERROR: v4l2 capture:camera_init: "
+ "platform_driver_register failed.\n");
+ return err;
+ }
+
+ return err;
+}
+
+/*!
+ * Exit and cleanup for the V4L2
+ */
+static void __exit camera_exit(void)
+{
+ pr_debug("In MVC: camera_exit\n");
+
+ platform_driver_unregister(&mxc_v4l2_driver);
+}
+
+module_init(camera_init);
+module_exit(camera_exit);
+
+module_param(video_nr, int, 0444);
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("V4L2 capture driver for Mxc based cameras");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("video");
diff --git a/drivers/media/video/mxc/capture/mxc_v4l2_capture.h b/drivers/media/video/mxc/capture/mxc_v4l2_capture.h
new file mode 100644
index 000000000000..50f695102095
--- /dev/null
+++ b/drivers/media/video/mxc/capture/mxc_v4l2_capture.h
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @defgroup MXC_V4L2_CAPTURE MXC V4L2 Video Capture Driver
+ */
+/*!
+ * @file mxc_v4l2_capture.h
+ *
+ * @brief mxc V4L2 capture device API Header file
+ *
+ * It include all the defines for frame operations, also three structure defines
+ * use case ops structure, common v4l2 driver structure and frame structure.
+ *
+ * @ingroup MXC_V4L2_CAPTURE
+ */
+#ifndef __MXC_V4L2_CAPTURE_H__
+#define __MXC_V4L2_CAPTURE_H__
+
+#include <asm/uaccess.h>
+#include <linux/list.h>
+#include <linux/smp_lock.h>
+#include <linux/ipu.h>
+#include <linux/mxc_v4l2.h>
+
+#include <media/v4l2-dev.h>
+
+#define FRAME_NUM 3
+
+/*!
+ * v4l2 frame structure.
+ */
+struct mxc_v4l_frame {
+ u32 paddress;
+ void *vaddress;
+ int count;
+ int width;
+ int height;
+
+ struct v4l2_buffer buffer;
+ struct list_head queue;
+ int index;
+};
+
+/* Only for old version. Will go away soon. */
+typedef struct {
+ u8 clk_mode;
+ u8 ext_vsync;
+ u8 Vsync_pol;
+ u8 Hsync_pol;
+ u8 pixclk_pol;
+ u8 data_pol;
+ u8 data_width;
+ u8 pack_tight;
+ u8 force_eof;
+ u8 data_en_pol;
+ u16 width;
+ u16 height;
+ u32 pixel_fmt;
+ u32 mclk;
+ u16 active_width;
+ u16 active_height;
+} sensor_interface;
+
+/* Sensor control function */
+/* Only for old version. Will go away soon. */
+struct camera_sensor {
+ void (*set_color) (int bright, int saturation, int red, int green,
+ int blue);
+ void (*get_color) (int *bright, int *saturation, int *red, int *green,
+ int *blue);
+ void (*set_ae_mode) (int ae_mode);
+ void (*get_ae_mode) (int *ae_mode);
+ sensor_interface *(*config) (int *frame_rate, int high_quality);
+ sensor_interface *(*reset) (void);
+ void (*get_std) (v4l2_std_id *std);
+ void (*set_std) (v4l2_std_id std);
+ unsigned int csi;
+};
+
+/*!
+ * common v4l2 driver structure.
+ */
+typedef struct _cam_data {
+ struct video_device *video_dev;
+ int device_type;
+
+ /* semaphore guard against SMP multithreading */
+ struct semaphore busy_lock;
+
+ int open_count;
+
+ /* params lock for this camera */
+ struct semaphore param_lock;
+
+ /* Encoder */
+ struct list_head ready_q;
+ struct list_head done_q;
+ struct list_head working_q;
+ int ping_pong_csi;
+ spinlock_t queue_int_lock;
+ spinlock_t dqueue_int_lock;
+ struct mxc_v4l_frame frame[FRAME_NUM];
+ struct mxc_v4l_frame dummy_frame;
+ int skip_frame;
+ wait_queue_head_t enc_queue;
+ int enc_counter;
+ dma_addr_t rot_enc_bufs[2];
+ void *rot_enc_bufs_vaddr[2];
+ int rot_enc_buf_size[2];
+ enum v4l2_buf_type type;
+
+ /* still image capture */
+ wait_queue_head_t still_queue;
+ int still_counter;
+ dma_addr_t still_buf[2];
+ void *still_buf_vaddr;
+
+ /* overlay */
+ struct v4l2_window win;
+ struct v4l2_framebuffer v4l2_fb;
+ dma_addr_t vf_bufs[2];
+ void *vf_bufs_vaddr[2];
+ int vf_bufs_size[2];
+ dma_addr_t rot_vf_bufs[2];
+ void *rot_vf_bufs_vaddr[2];
+ int rot_vf_buf_size[2];
+ bool overlay_active;
+ int output;
+ struct fb_info *overlay_fb;
+ int fb_origin_std;
+
+ /* v4l2 format */
+ struct v4l2_format v2f;
+ int rotation; /* for IPUv1 and IPUv3, this means encoder rotation */
+ int vf_rotation; /* viewfinder rotation only for IPUv1 and IPUv3 */
+ struct v4l2_mxc_offset offset;
+
+ /* V4l2 control bit */
+ int bright;
+ int hue;
+ int contrast;
+ int saturation;
+ int red;
+ int green;
+ int blue;
+ int ae_mode;
+
+ /* standard */
+ struct v4l2_streamparm streamparm;
+ struct v4l2_standard standard;
+ bool standard_autodetect;
+
+ /* crop */
+ struct v4l2_rect crop_bounds;
+ struct v4l2_rect crop_defrect;
+ struct v4l2_rect crop_current;
+
+ int (*enc_update_eba) (dma_addr_t eba, int *bufferNum);
+ int (*enc_enable) (void *private);
+ int (*enc_disable) (void *private);
+ int (*enc_enable_csi) (void *private);
+ int (*enc_disable_csi) (void *private);
+ void (*enc_callback) (u32 mask, void *dev);
+ int (*vf_start_adc) (void *private);
+ int (*vf_stop_adc) (void *private);
+ int (*vf_start_sdc) (void *private);
+ int (*vf_stop_sdc) (void *private);
+ int (*vf_enable_csi) (void *private);
+ int (*vf_disable_csi) (void *private);
+ int (*csi_start) (void *private);
+ int (*csi_stop) (void *private);
+
+ /* misc status flag */
+ bool overlay_on;
+ bool capture_on;
+ int overlay_pid;
+ int capture_pid;
+ bool low_power;
+ wait_queue_head_t power_queue;
+ unsigned int csi;
+ int current_input;
+
+ /* camera sensor interface */
+ struct camera_sensor *cam_sensor; /* old version */
+ struct v4l2_int_device *sensor;
+} cam_data;
+
+#if defined(CONFIG_MXC_IPU_V1) || defined(CONFIG_VIDEO_MXC_EMMA_CAMERA) \
+ || defined(CONFIG_VIDEO_MXC_CSI_CAMERA_MODULE) \
+ || defined(CONFIG_VIDEO_MXC_CSI_CAMERA)
+void set_mclk_rate(uint32_t *p_mclk_freq);
+#else
+void set_mclk_rate(uint32_t *p_mclk_freq, uint32_t csi);
+#endif
+#endif /* __MXC_V4L2_CAPTURE_H__ */
diff --git a/drivers/media/video/mxc/capture/ov2640.c b/drivers/media/video/mxc/capture/ov2640.c
new file mode 100644
index 000000000000..c906925eca21
--- /dev/null
+++ b/drivers/media/video/mxc/capture/ov2640.c
@@ -0,0 +1,1081 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file ov2640.c
+ *
+ * @brief ov2640 camera driver functions
+ *
+ * @ingroup Camera
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/regulator/consumer.h>
+
+#include <mach/hardware.h>
+#include <media/v4l2-int-device.h>
+#include "mxc_v4l2_capture.h"
+
+#define MIN_FPS 5
+#define MAX_FPS 30
+#define DEFAULT_FPS 30
+
+#define OV2640_XCLK_MIN 6000000
+#define OV2640_XCLK_MAX 27000000
+
+/*
+enum ov2640_mode {
+ ov2640_mode_1600_1120,
+ ov2640_mode_800_600
+};
+*/
+
+struct reg_value {
+ u8 reg;
+ u8 value;
+ int delay_ms;
+};
+
+static struct reg_value ov2640_setting_1600_1120[] = {
+#ifdef CONFIG_MACH_MX25_3DS
+ {0xff, 0x01, 0}, {0x12, 0x80, 5}, {0xff, 0x00, 0}, {0x2c, 0xff, 0},
+ {0x2e, 0xdf, 0}, {0xff, 0x01, 0}, {0x3c, 0x32, 0}, {0x11, 0x00, 0},
+ {0x09, 0x02, 0}, {0x04, 0x28, 0}, {0x13, 0xe5, 0}, {0x14, 0x48, 0},
+ {0x2c, 0x0c, 0}, {0x33, 0x78, 0}, {0x3a, 0x33, 0}, {0x3b, 0xfb, 0},
+ {0x3e, 0x00, 0}, {0x43, 0x11, 0}, {0x16, 0x10, 0}, {0x39, 0x02, 0},
+ {0x35, 0x58, 0}, {0x22, 0x0a, 0}, {0x37, 0x40, 0}, {0x23, 0x00, 0},
+ {0x34, 0xa0, 0}, {0x36, 0x1a, 0}, {0x06, 0x02, 0}, {0x07, 0xc0, 0},
+ {0x0d, 0xb7, 0}, {0x0e, 0x01, 0}, {0x4c, 0x00, 0}, {0x4a, 0x81, 0},
+ {0x21, 0x99, 0}, {0x24, 0x40, 0}, {0x25, 0x38, 0}, {0x26, 0x82, 0},
+ {0x5c, 0x00, 0}, {0x63, 0x00, 0}, {0x46, 0x3f, 0}, {0x61, 0x70, 0},
+ {0x62, 0x80, 0}, {0x7c, 0x05, 0}, {0x20, 0x80, 0}, {0x28, 0x30, 0},
+ {0x6c, 0x00, 0}, {0x6d, 0x80, 0}, {0x6e, 0x00, 0}, {0x70, 0x02, 0},
+ {0x71, 0x94, 0}, {0x73, 0xc1, 0}, {0x3d, 0x34, 0}, {0x5a, 0x57, 0},
+ {0x4f, 0xbb, 0}, {0x50, 0x9c, 0}, {0xff, 0x00, 0}, {0xe5, 0x7f, 0},
+ {0xf9, 0xc0, 0}, {0x41, 0x24, 0}, {0xe0, 0x14, 0}, {0x76, 0xff, 0},
+ {0x33, 0xa0, 0}, {0x42, 0x20, 0}, {0x43, 0x18, 0}, {0x4c, 0x00, 0},
+ {0x87, 0xd0, 0}, {0x88, 0x3f, 0}, {0xd7, 0x01, 0}, {0xd9, 0x10, 0},
+ {0xd3, 0x82, 0}, {0xc8, 0x08, 0}, {0xc9, 0x80, 0}, {0x7c, 0x00, 0},
+ {0x7d, 0x00, 0}, {0x7c, 0x03, 0}, {0x7d, 0x48, 0}, {0x7d, 0x48, 0},
+ {0x7c, 0x08, 0}, {0x7d, 0x20, 0}, {0x7d, 0x10, 0}, {0x7d, 0x0e, 0},
+ {0x90, 0x00, 0}, {0x91, 0x0e, 0}, {0x91, 0x1a, 0}, {0x91, 0x31, 0},
+ {0x91, 0x5a, 0}, {0x91, 0x69, 0}, {0x91, 0x75, 0}, {0x91, 0x7e, 0},
+ {0x91, 0x88, 0}, {0x91, 0x8f, 0}, {0x91, 0x96, 0}, {0x91, 0xa3, 0},
+ {0x91, 0xaf, 0}, {0x91, 0xc4, 0}, {0x91, 0xd7, 0}, {0x91, 0xe8, 0},
+ {0x91, 0x20, 0}, {0x92, 0x00, 0}, {0x93, 0x06, 0}, {0x93, 0xe3, 0},
+ {0x93, 0x05, 0}, {0x93, 0x05, 0}, {0x93, 0x00, 0}, {0x93, 0x04, 0},
+ {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0},
+ {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x96, 0x00, 0},
+ {0x97, 0x08, 0}, {0x97, 0x19, 0}, {0x97, 0x02, 0}, {0x97, 0x0c, 0},
+ {0x97, 0x24, 0}, {0x97, 0x30, 0}, {0x97, 0x28, 0}, {0x97, 0x26, 0},
+ {0x97, 0x02, 0}, {0x97, 0x98, 0}, {0x97, 0x80, 0}, {0x97, 0x00, 0},
+ {0x97, 0x00, 0}, {0xc3, 0xed, 0}, {0xa4, 0x00, 0}, {0xa8, 0x00, 0},
+ {0xc5, 0x11, 0}, {0xc6, 0x51, 0}, {0xbf, 0x80, 0}, {0xc7, 0x10, 0},
+ {0xb6, 0x66, 0}, {0xb8, 0xa5, 0}, {0xb7, 0x64, 0}, {0xb9, 0x7c, 0},
+ {0xb3, 0xaf, 0}, {0xb4, 0x97, 0}, {0xb5, 0xff, 0}, {0xb0, 0xc5, 0},
+ {0xb1, 0x94, 0}, {0xb2, 0x0f, 0}, {0xc4, 0x5c, 0}, {0xc0, 0xc8, 0},
+ {0xc1, 0x96, 0}, {0x86, 0x1d, 0}, {0x50, 0x00, 0}, {0x51, 0x90, 0},
+ {0x52, 0x2c, 0}, {0x53, 0x00, 0}, {0x54, 0x00, 0}, {0x55, 0x88, 0},
+ {0x57, 0x00, 0}, {0x5a, 0x90, 0}, {0x5b, 0x2c, 0}, {0x5c, 0x05, 0},
+ {0xc3, 0xed, 0}, {0x7f, 0x00, 0}, {0xda, 0x00, 0}, {0xe5, 0x1f, 0},
+ {0xe1, 0x77, 0}, {0xe0, 0x00, 0}, {0xdd, 0x7f, 0}, {0x05, 0x00, 0},
+ {0xff, 0x00, 0}, {0xe0, 0x04, 0}, {0xc0, 0xc8, 0}, {0xc1, 0x96, 0},
+ {0x86, 0x3d, 0}, {0x50, 0x00, 0}, {0x51, 0x90, 0}, {0x52, 0x2c, 0},
+ {0x53, 0x00, 0}, {0x54, 0x00, 0}, {0x55, 0x88, 0}, {0x57, 0x00, 0},
+ {0x5a, 0x40, 0}, {0x5b, 0xf0, 0}, {0x5c, 0x01, 0}, {0xd3, 0x82, 0},
+ {0xe0, 0x00, 1000}
+#else
+ {0xff, 0x1, 0}, {0x12, 0x80, 1}, {0xff, 0, 0}, {0x2c, 0xff, 0},
+ {0x2e, 0xdf, 0}, {0xff, 0x1, 0}, {0x3c, 0x32, 0}, {0x11, 0x01, 0},
+ {0x09, 0x00, 0}, {0x04, 0x28, 0}, {0x13, 0xe5, 0}, {0x14, 0x48, 0},
+ {0x2c, 0x0c, 0}, {0x33, 0x78, 0}, {0x3a, 0x33, 0}, {0x3b, 0xfb, 0},
+ {0x3e, 0x00, 0}, {0x43, 0x11, 0}, {0x16, 0x10, 0}, {0x39, 0x82, 0},
+ {0x35, 0x88, 0}, {0x22, 0x0a, 0}, {0x37, 0x40, 0}, {0x23, 0x00, 0},
+ {0x34, 0xa0, 0}, {0x36, 0x1a, 0}, {0x06, 0x02, 0}, {0x07, 0xc0, 0},
+ {0x0d, 0xb7, 0}, {0x0e, 0x01, 0}, {0x4c, 0x00, 0}, {0x4a, 0x81, 0},
+ {0x21, 0x99, 0}, {0x24, 0x40, 0}, {0x25, 0x38, 0}, {0x26, 0x82, 0},
+ {0x5c, 0x00, 0}, {0x63, 0x00, 0}, {0x46, 0x3f, 0}, {0x0c, 0x3c, 0},
+ {0x5d, 0x55, 0}, {0x5e, 0x7d, 0}, {0x5f, 0x7d, 0}, {0x60, 0x55, 0},
+ {0x61, 0x70, 0}, {0x62, 0x80, 0}, {0x7c, 0x05, 0}, {0x20, 0x80, 0},
+ {0x28, 0x30, 0}, {0x6c, 0x00, 0}, {0x6d, 0x80, 0}, {0x6e, 00, 0},
+ {0x70, 0x02, 0}, {0x71, 0x94, 0}, {0x73, 0xc1, 0}, {0x3d, 0x34, 0},
+ {0x5a, 0x57, 0}, {0x4f, 0xbb, 0}, {0x50, 0x9c, 0}, {0xff, 0x00, 0},
+ {0xe5, 0x7f, 0}, {0xf9, 0xc0, 0}, {0x41, 0x24, 0}, {0x44, 0x06, 0},
+ {0xe0, 0x14, 0}, {0x76, 0xff, 0}, {0x33, 0xa0, 0}, {0x42, 0x20, 0},
+ {0x43, 0x18, 0}, {0x4c, 0x00, 0}, {0x87, 0xd0, 0}, {0xd7, 0x03, 0},
+ {0xd9, 0x10, 0}, {0xd3, 0x82, 0}, {0xc8, 0x08, 0}, {0xc9, 0x80, 0},
+ {0x7c, 0x00, 0}, {0x7d, 0x00, 0}, {0x7c, 0x03, 0}, {0x7d, 0x48, 0},
+ {0x7d, 0x48, 0}, {0x7c, 0x08, 0}, {0x7d, 0x20, 0}, {0x7d, 0x10, 0},
+ {0x7d, 0x0e, 0}, {0x90, 0x00, 0}, {0x91, 0x0e, 0}, {0x91, 0x1a, 0},
+ {0x91, 0x31, 0}, {0x91, 0x5a, 0}, {0x91, 0x69, 0}, {0x91, 0x75, 0},
+ {0x91, 0x7e, 0}, {0x91, 0x88, 0}, {0x91, 0x8f, 0}, {0x91, 0x96, 0},
+ {0x91, 0xa3, 0}, {0x91, 0xaf, 0}, {0x91, 0xc4, 0}, {0x91, 0xd7, 0},
+ {0x91, 0xe8, 0}, {0x91, 0x20, 0}, {0x92, 0x00, 0}, {0x93, 0x06, 0},
+ {0x93, 0xe3, 0}, {0x93, 0x03, 0}, {0x93, 0x03, 0}, {0x93, 0x00, 0},
+ {0x93, 0x02, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0},
+ {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0},
+ {0x96, 0x00, 0}, {0x97, 0x08, 0}, {0x97, 0x19, 0}, {0x97, 0x02, 0},
+ {0x97, 0x0c, 0}, {0x97, 0x24, 0}, {0x97, 0x30, 0}, {0x97, 0x28, 0},
+ {0x97, 0x26, 0}, {0x97, 0x02, 0}, {0x97, 0x98, 0}, {0x97, 0x80, 0},
+ {0x97, 0x00, 0}, {0x97, 0x00, 0}, {0xa4, 0x00, 0}, {0xa8, 0x00, 0},
+ {0xc5, 0x11, 0}, {0xc6, 0x51, 0}, {0xbf, 0x80, 0}, {0xc7, 0x10, 0},
+ {0xb6, 0x66, 0}, {0xb8, 0xa5, 0}, {0xb7, 0x64, 0}, {0xb9, 0x7c, 0},
+ {0xb3, 0xaf, 0}, {0xb4, 0x97, 0}, {0xb5, 0xff, 0}, {0xb0, 0xc5, 0},
+ {0xb1, 0x94, 0}, {0xb2, 0x0f, 0}, {0xc4, 0x5c, 0}, {0xa6, 0x00, 0},
+ {0xa7, 0x20, 0}, {0xa7, 0xd8, 0}, {0xa7, 0x1b, 0}, {0xa7, 0x31, 0},
+ {0xa7, 0x00, 0}, {0xa7, 0x18, 0}, {0xa7, 0x20, 0}, {0xa7, 0xd8, 0},
+ {0xa7, 0x19, 0}, {0xa7, 0x31, 0}, {0xa7, 0x00, 0}, {0xa7, 0x18, 0},
+ {0xa7, 0x20, 0}, {0xa7, 0xd8, 0}, {0xa7, 0x19, 0}, {0xa7, 0x31, 0},
+ {0xa7, 0x00, 0}, {0xa7, 0x18, 0}, {0xc0, 0xc8, 0}, {0xc1, 0x96, 0},
+ {0x86, 0x3d, 0}, {0x50, 0x00, 0}, {0x51, 0x90, 0}, {0x52, 0x18, 0},
+ {0x53, 0x00, 0}, {0x54, 0x00, 0}, {0x55, 0x88, 0}, {0x57, 0x00, 0},
+ {0x5a, 0x90, 0}, {0x5b, 0x18, 0}, {0x5c, 0x05, 0}, {0xc3, 0xef, 0},
+ {0x7f, 0x00, 0}, {0xda, 0x01, 0}, {0xe5, 0x1f, 0}, {0xe1, 0x67, 0},
+ {0xe0, 0x00, 0}, {0xdd, 0x7f, 0}, {0x05, 0x00, 0}
+#endif
+};
+
+static struct reg_value ov2640_setting_800_600[] = {
+#ifdef CONFIG_MACH_MX25_3DS
+ {0xff, 0x01, 0}, {0x12, 0x80, 5}, {0xff, 0x00, 0}, {0x2c, 0xff, 0},
+ {0x2e, 0xdf, 0}, {0xff, 0x01, 0}, {0x3c, 0x32, 0}, {0x11, 0x00, 0},
+ {0x09, 0x02, 0}, {0x04, 0x28, 0}, {0x13, 0xe5, 0}, {0x14, 0x48, 0},
+ {0x2c, 0x0c, 0}, {0x33, 0x78, 0}, {0x3a, 0x33, 0}, {0x3b, 0xfb, 0},
+ {0x3e, 0x00, 0}, {0x43, 0x11, 0}, {0x16, 0x10, 0}, {0x39, 0x92, 0},
+ {0x35, 0xda, 0}, {0x22, 0x1a, 0}, {0x37, 0xc3, 0}, {0x23, 0x00, 0},
+ {0x34, 0xc0, 0}, {0x36, 0x1a, 0}, {0x06, 0x88, 0}, {0x07, 0xc0, 0},
+ {0x0d, 0x87, 0}, {0x0e, 0x41, 0}, {0x4c, 0x00, 0},
+ {0x48, 0x00, 0}, {0x5b, 0x00, 0}, {0x42, 0x03, 0}, {0x4a, 0x81, 0},
+ {0x21, 0x99, 0}, {0x24, 0x40, 0}, {0x25, 0x38, 0}, {0x26, 0x82, 0},
+ {0x5c, 0x00, 0}, {0x63, 0x00, 0}, {0x46, 0x22, 0}, {0x0c, 0x3c, 0},
+ {0x61, 0x70, 0}, {0x62, 0x80, 0}, {0x7c, 0x05, 0}, {0x20, 0x80, 0},
+ {0x28, 0x30, 0}, {0x6c, 0x00, 0}, {0x6d, 0x80, 0}, {0x6e, 0x00, 0},
+ {0x70, 0x02, 0}, {0x71, 0x94, 0}, {0x73, 0xc1, 0}, {0x12, 0x40, 0},
+ {0x17, 0x11, 0}, {0x18, 0x43, 0}, {0x19, 0x00, 0}, {0x1a, 0x4b, 0},
+ {0x32, 0x09, 0}, {0x37, 0xc0, 0}, {0x4f, 0xca, 0}, {0x50, 0xa8, 0},
+ {0x5a, 0x23, 0}, {0x6d, 0x00, 0}, {0x3d, 0x38, 0}, {0xff, 0x00, 0},
+ {0xe5, 0x7f, 0}, {0xf9, 0xc0, 0}, {0x41, 0x24, 0}, {0xe0, 0x14, 0},
+ {0x76, 0xff, 0}, {0x33, 0xa0, 0}, {0x42, 0x20, 0}, {0x43, 0x18, 0},
+ {0x4c, 0x00, 0}, {0x87, 0xd5, 0}, {0x88, 0x3f, 0}, {0xd7, 0x01, 0},
+ {0xd9, 0x10, 0}, {0xd3, 0x82, 0}, {0xc8, 0x08, 0}, {0xc9, 0x80, 0},
+ {0x7c, 0x00, 0}, {0x7d, 0x00, 0}, {0x7c, 0x03, 0}, {0x7d, 0x48, 0},
+ {0x7d, 0x48, 0}, {0x7c, 0x08, 0}, {0x7d, 0x20, 0}, {0x7d, 0x10, 0},
+ {0x7d, 0x0e, 0}, {0x90, 0x00, 0}, {0x91, 0x0e, 0}, {0x91, 0x1a, 0},
+ {0x91, 0x31, 0}, {0x91, 0x5a, 0}, {0x91, 0x69, 0}, {0x91, 0x75, 0},
+ {0x91, 0x7e, 0}, {0x91, 0x88, 0}, {0x91, 0x8f, 0}, {0x91, 0x96, 0},
+ {0x91, 0xa3, 0}, {0x91, 0xaf, 0}, {0x91, 0xc4, 0}, {0x91, 0xd7, 0},
+ {0x91, 0xe8, 0}, {0x91, 0x20, 0}, {0x92, 0x00, 0}, {0x93, 0x06, 0},
+ {0x93, 0xe3, 0}, {0x93, 0x05, 0}, {0x93, 0x05, 0}, {0x93, 0x00, 0},
+ {0x93, 0x04, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0},
+ {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0},
+ {0x96, 0x00, 0}, {0x97, 0x08, 0}, {0x97, 0x19, 0}, {0x97, 0x02, 0},
+ {0x97, 0x0c, 0}, {0x97, 0x24, 0}, {0x97, 0x30, 0}, {0x97, 0x28, 0},
+ {0x97, 0x26, 0}, {0x97, 0x02, 0}, {0x97, 0x98, 0}, {0x97, 0x80, 0},
+ {0x97, 0x00, 0}, {0x97, 0x00, 0}, {0xc3, 0xed, 0}, {0xa4, 0x00, 0},
+ {0xa8, 0x00, 0}, {0xc5, 0x11, 0}, {0xc6, 0x51, 0}, {0xbf, 0x80, 0},
+ {0xc7, 0x10, 0}, {0xb6, 0x66, 0}, {0xb8, 0xa5, 0}, {0xb7, 0x64, 0},
+ {0xb9, 0x7c, 0}, {0xb3, 0xaf, 0}, {0xb4, 0x97, 0}, {0xb5, 0xff, 0},
+ {0xb0, 0xc5, 0}, {0xb1, 0x94, 0}, {0xb2, 0x0f, 0}, {0xc4, 0x5c, 0},
+ {0xc0, 0x64, 0}, {0xc1, 0x4b, 0}, {0x8c, 0x00, 0}, {0x86, 0x3d, 0},
+ {0x50, 0x00, 0}, {0x51, 0xc8, 0}, {0x52, 0x96, 0}, {0x53, 0x00, 0},
+ {0x54, 0x00, 0}, {0x55, 0x00, 0}, {0x5a, 0xc8, 0}, {0x5b, 0x96, 0},
+ {0x5c, 0x00, 0}, {0xd3, 0x82, 0}, {0xc3, 0xed, 0}, {0x7f, 0x00, 0},
+ {0xda, 0x00, 0}, {0xe5, 0x1f, 0}, {0xe1, 0x67, 0}, {0xe0, 0x00, 0},
+ {0xdd, 0x7f, 0}, {0x05, 0x00, 0}, {0xff, 0x00, 0}, {0xe0, 0x04, 0},
+ {0xc0, 0x64, 0}, {0xc1, 0x4b, 0}, {0x8c, 0x00, 0}, {0x86, 0x3d, 0},
+ {0x50, 0x00, 0}, {0x51, 0xc8, 0}, {0x52, 0x96, 0}, {0x53, 0x00, 0},
+ {0x54, 0x00, 0}, {0x55, 0x00, 0}, {0x5a, 0xa0, 0}, {0x5b, 0x78, 0},
+ {0x5c, 0x00, 0}, {0xd3, 0x82, 0}, {0xe0, 0x00, 1000}
+#else
+ {0xff, 0, 0}, {0xff, 1, 0}, {0x12, 0x80, 1}, {0xff, 00, 0},
+ {0x2c, 0xff, 0}, {0x2e, 0xdf, 0}, {0xff, 0x1, 0}, {0x3c, 0x32, 0},
+ {0x11, 0x01, 0}, {0x09, 0x00, 0}, {0x04, 0x28, 0}, {0x13, 0xe5, 0},
+ {0x14, 0x48, 0}, {0x2c, 0x0c, 0}, {0x33, 0x78, 0}, {0x3a, 0x33, 0},
+ {0x3b, 0xfb, 0}, {0x3e, 0x00, 0}, {0x43, 0x11, 0}, {0x16, 0x10, 0},
+ {0x39, 0x92, 0}, {0x35, 0xda, 0}, {0x22, 0x1a, 0}, {0x37, 0xc3, 0},
+ {0x23, 0x00, 0}, {0x34, 0xc0, 0}, {0x36, 0x1a, 0}, {0x06, 0x88, 0},
+ {0x07, 0xc0, 0}, {0x0d, 0x87, 0}, {0x0e, 0x41, 0}, {0x4c, 0x00, 0},
+ {0x4a, 0x81, 0}, {0x21, 0x99, 0}, {0x24, 0x40, 0}, {0x25, 0x38, 0},
+ {0x26, 0x82, 0}, {0x5c, 0x00, 0}, {0x63, 0x00, 0}, {0x46, 0x22, 0},
+ {0x0c, 0x3c, 0}, {0x5d, 0x55, 0}, {0x5e, 0x7d, 0}, {0x5f, 0x7d, 0},
+ {0x60, 0x55, 0}, {0x61, 0x70, 0}, {0x62, 0x80, 0}, {0x7c, 0x05, 0},
+ {0x20, 0x80, 0}, {0x28, 0x30, 0}, {0x6c, 0x00, 0}, {0x6d, 0x80, 0},
+ {0x6e, 00, 0}, {0x70, 0x02, 0}, {0x71, 0x94, 0}, {0x73, 0xc1, 0},
+ {0x12, 0x40, 0}, {0x17, 0x11, 0}, {0x18, 0x43, 0}, {0x19, 0x00, 0},
+ {0x1a, 0x4b, 0}, {0x32, 0x09, 0}, {0x37, 0xc0, 0}, {0x4f, 0xca, 0},
+ {0x50, 0xa8, 0}, {0x6d, 0x00, 0}, {0x3d, 0x38, 0}, {0xff, 0x00, 0},
+ {0xe5, 0x7f, 0}, {0xf9, 0xc0, 0}, {0x41, 0x24, 0}, {0x44, 0x06, 0},
+ {0xe0, 0x14, 0}, {0x76, 0xff, 0}, {0x33, 0xa0, 0}, {0x42, 0x20, 0},
+ {0x43, 0x18, 0}, {0x4c, 0x00, 0}, {0x87, 0xd0, 0}, {0x88, 0x3f, 0},
+ {0xd7, 0x03, 0}, {0xd9, 0x10, 0}, {0xd3, 0x82, 0}, {0xc8, 0x08, 0},
+ {0xc9, 0x80, 0}, {0x7c, 0x00, 0}, {0x7d, 0x00, 0}, {0x7c, 0x03, 0},
+ {0x7d, 0x48, 0}, {0x7d, 0x48, 0}, {0x7c, 0x08, 0}, {0x7d, 0x20, 0},
+ {0x7d, 0x10, 0}, {0x7d, 0x0e, 0}, {0x90, 0x00, 0}, {0x91, 0x0e, 0},
+ {0x91, 0x1a, 0}, {0x91, 0x31, 0}, {0x91, 0x5a, 0}, {0x91, 0x69, 0},
+ {0x91, 0x75, 0}, {0x91, 0x7e, 0}, {0x91, 0x88, 0}, {0x91, 0x8f, 0},
+ {0x91, 0x96, 0}, {0x91, 0xa3, 0}, {0x91, 0xaf, 0}, {0x91, 0xc4, 0},
+ {0x91, 0xd7, 0}, {0x91, 0xe8, 0}, {0x91, 0x20, 0}, {0x92, 0x00, 0},
+ {0x93, 0x06, 0}, {0x93, 0xe3, 0}, {0x93, 0x03, 0}, {0x93, 0x03, 0},
+ {0x93, 0x00, 0}, {0x93, 0x02, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0},
+ {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0},
+ {0x93, 0x00, 0}, {0x96, 0x00, 0}, {0x97, 0x08, 0}, {0x97, 0x19, 0},
+ {0x97, 0x02, 0}, {0x97, 0x0c, 0}, {0x97, 0x24, 0}, {0x97, 0x30, 0},
+ {0x97, 0x28, 0}, {0x97, 0x26, 0}, {0x97, 0x02, 0}, {0x97, 0x98, 0},
+ {0x97, 0x80, 0}, {0x97, 0x00, 0}, {0x97, 0x00, 0}, {0xa4, 0x00, 0},
+ {0xa8, 0x00, 0}, {0xc5, 0x11, 0}, {0xc6, 0x51, 0}, {0xbf, 0x80, 0},
+ {0xc7, 0x10, 0}, {0xb6, 0x66, 0}, {0xb8, 0xa5, 0}, {0xb7, 0x64, 0},
+ {0xb9, 0x7c, 0}, {0xb3, 0xaf, 0}, {0xb4, 0x97, 0}, {0xb5, 0xff, 0},
+ {0xb0, 0xc5, 0}, {0xb1, 0x94, 0}, {0xb2, 0x0f, 0}, {0xc4, 0x5c, 0},
+ {0xa6, 0x00, 0}, {0xa7, 0x20, 0}, {0xa7, 0xd8, 0}, {0xa7, 0x1b, 0},
+ {0xa7, 0x31, 0}, {0xa7, 0x00, 0}, {0xa7, 0x18, 0}, {0xa7, 0x20, 0},
+ {0xa7, 0xd8, 0}, {0xa7, 0x19, 0}, {0xa7, 0x31, 0}, {0xa7, 0x00, 0},
+ {0xa7, 0x18, 0}, {0xa7, 0x20, 0}, {0xa7, 0xd8, 0}, {0xa7, 0x19, 0},
+ {0xa7, 0x31, 0}, {0xa7, 0x00, 0}, {0xa7, 0x18, 0}, {0xc0, 0x64, 0},
+ {0xc1, 0x4b, 0}, {0x86, 0x1d, 0}, {0x50, 0x00, 0}, {0x51, 0xc8, 0},
+ {0x52, 0x96, 0}, {0x53, 0x00, 0}, {0x54, 0x00, 0}, {0x55, 0x00, 0},
+ {0x57, 0x00, 0}, {0x5a, 0xc8, 0}, {0x5b, 0x96, 0}, {0x5c, 0x00, 0},
+ {0xc3, 0xef, 0}, {0x7f, 0x00, 0}, {0xda, 0x01, 0}, {0xe5, 0x1f, 0},
+ {0xe1, 0x67, 0}, {0xe0, 0x00, 0}, {0xdd, 0x7f, 0}, {0x05, 0x00, 0}
+#endif
+};
+
+/*!
+ * Maintains the information on the current state of the sesor.
+ */
+struct sensor {
+ const struct ov2640_platform_data *platform_data;
+ struct v4l2_int_device *v4l2_int_device;
+ struct i2c_client *i2c_client;
+ struct v4l2_pix_format pix;
+ struct v4l2_captureparm streamcap;
+ bool on;
+
+ /* control settings */
+ int brightness;
+ int hue;
+ int contrast;
+ int saturation;
+ int red;
+ int green;
+ int blue;
+ int ae_mode;
+
+ u32 csi;
+ u32 mclk;
+
+} ov2640_data;
+
+static struct regulator *io_regulator;
+static struct regulator *core_regulator;
+static struct regulator *analog_regulator;
+static struct regulator *gpo_regulator;
+
+extern void gpio_sensor_active(void);
+extern void gpio_sensor_inactive(void);
+
+/* list of image formats supported by this sensor */
+/*
+const static struct v4l2_fmtdesc ov2640_formats[] = {
+ {
+ .description = "YUYV (YUV 4:2:2), packed",
+ .pixelformat = V4L2_PIX_FMT_UYVY,
+ },
+};
+ */
+
+static int ov2640_init_mode(struct sensor *s)
+{
+ int ret = -1;
+ struct reg_value *setting;
+ int i, num;
+
+ pr_debug("In ov2640:ov2640_init_mode capturemode is %d\n",
+ s->streamcap.capturemode);
+
+ if (s->streamcap.capturemode & V4L2_MODE_HIGHQUALITY) {
+ s->pix.width = 1600;
+ s->pix.height = 1120;
+ setting = ov2640_setting_1600_1120;
+ num = ARRAY_SIZE(ov2640_setting_1600_1120);
+ } else {
+ s->pix.width = 800;
+ s->pix.height = 600;
+ setting = ov2640_setting_800_600;
+ num = ARRAY_SIZE(ov2640_setting_800_600);
+ }
+
+ for (i = 0; i < num; i++) {
+ ret = i2c_smbus_write_byte_data(s->i2c_client,
+ setting[i].reg,
+ setting[i].value);
+ if (ret < 0) {
+ pr_err("write reg error: reg=%x, val=%x\n",
+ setting[i].reg, setting[i].value);
+ return ret;
+ }
+ if (setting[i].delay_ms > 0)
+ msleep(setting[i].delay_ms);
+ }
+
+ return ret;
+}
+
+/* At present only support change to 15fps(only for SVGA mode) */
+static int ov2640_set_fps(struct sensor *s, int fps)
+{
+ int ret = 0;
+
+ if (i2c_smbus_write_byte_data(s->i2c_client, 0xff, 0x01) < 0) {
+ pr_err("in %s,change to sensor addr failed\n", __func__);
+ ret = -EPERM;
+ }
+
+ /* change the camera framerate to 15fps(only for SVGA mode) */
+ if (i2c_smbus_write_byte_data(s->i2c_client, 0x11, 0x01) < 0) {
+ pr_err("change camera to 15fps failed\n");
+ ret = -EPERM;
+ }
+
+ return ret;
+}
+
+static int ov2640_set_format(struct sensor *s, int format)
+{
+ int ret = 0;
+
+ if (i2c_smbus_write_byte_data(s->i2c_client, 0xff, 0x00) < 0)
+ ret = -EPERM;
+
+ if (format == V4L2_PIX_FMT_RGB565) {
+ /* set RGB565 format */
+ if (i2c_smbus_write_byte_data(s->i2c_client, 0xda, 0x08) < 0)
+ ret = -EPERM;
+
+ if (i2c_smbus_write_byte_data(s->i2c_client, 0xd7, 0x03) < 0)
+ ret = -EPERM;
+ } else if (format == V4L2_PIX_FMT_YUV420) {
+ /* set YUV420 format */
+ if (i2c_smbus_write_byte_data(s->i2c_client, 0xda, 0x00) < 0)
+ ret = -EPERM;
+
+ if (i2c_smbus_write_byte_data(s->i2c_client, 0xd7, 0x1b) < 0)
+ ret = -EPERM;
+ } else {
+ pr_debug("format not supported\n");
+ }
+
+ return ret;
+}
+
+/* --------------- IOCTL functions from v4l2_int_ioctl_desc --------------- */
+
+/*!
+ * ioctl_g_ifparm - V4L2 sensor interface handler for vidioc_int_g_ifparm_num
+ * s: pointer to standard V4L2 device structure
+ * p: pointer to standard V4L2 vidioc_int_g_ifparm_num ioctl structure
+ *
+ * Gets slave interface parameters.
+ * Calculates the required xclk value to support the requested
+ * clock parameters in p. This value is returned in the p
+ * parameter.
+ *
+ * vidioc_int_g_ifparm returns platform-specific information about the
+ * interface settings used by the sensor.
+ *
+ * Given the image capture format in pix, the nominal frame period in
+ * timeperframe, calculate the required xclk frequency.
+ *
+ * Called on open.
+ */
+static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p)
+{
+ pr_debug("In ov2640:ioctl_g_ifparm\n");
+
+ if (s == NULL) {
+ pr_err(" ERROR!! no slave device set!\n");
+ return -1;
+ }
+
+ memset(p, 0, sizeof(*p));
+ p->u.bt656.clock_curr = ov2640_data.mclk;
+ p->if_type = V4L2_IF_TYPE_BT656;
+ p->u.bt656.mode = V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT;
+ p->u.bt656.clock_min = OV2640_XCLK_MIN;
+ p->u.bt656.clock_max = OV2640_XCLK_MAX;
+
+ return 0;
+}
+
+/*!
+ * Sets the camera power.
+ *
+ * s pointer to the camera device
+ * on if 1, power is to be turned on. 0 means power is to be turned off
+ *
+ * ioctl_s_power - V4L2 sensor interface handler for vidioc_int_s_power_num
+ * @s: pointer to standard V4L2 device structure
+ * @on: power state to which device is to be set
+ *
+ * Sets devices power state to requrested state, if possible.
+ * This is called on open, close, suspend and resume.
+ */
+static int ioctl_s_power(struct v4l2_int_device *s, int on)
+{
+ struct sensor *sensor = s->priv;
+
+ pr_debug("In ov2640:ioctl_s_power\n");
+
+ if (on && !sensor->on) {
+ gpio_sensor_active();
+ if (io_regulator)
+ if (regulator_enable(io_regulator) != 0)
+ return -EIO;
+ if (core_regulator)
+ if (regulator_enable(core_regulator) != 0)
+ return -EIO;
+ if (gpo_regulator)
+ if (regulator_enable(gpo_regulator) != 0)
+ return -EIO;
+ if (analog_regulator)
+ if (regulator_enable(analog_regulator) != 0)
+ return -EIO;
+ } else if (!on && sensor->on) {
+ if (analog_regulator)
+ regulator_disable(analog_regulator);
+ if (core_regulator)
+ regulator_disable(core_regulator);
+ if (io_regulator)
+ regulator_disable(io_regulator);
+ if (gpo_regulator)
+ regulator_disable(gpo_regulator);
+ gpio_sensor_inactive();
+ }
+
+ sensor->on = on;
+
+ return 0;
+}
+
+/*!
+ * ioctl_g_parm - V4L2 sensor interface handler for VIDIOC_G_PARM ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure
+ *
+ * Returns the sensor's video CAPTURE parameters.
+ */
+static int ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)
+{
+ struct sensor *sensor = s->priv;
+ struct v4l2_captureparm *cparm = &a->parm.capture;
+ int ret = 0;
+
+ pr_debug("In ov2640:ioctl_g_parm\n");
+
+ switch (a->type) {
+ /* This is the only case currently handled. */
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ pr_debug(" type is V4L2_BUF_TYPE_VIDEO_CAPTURE\n");
+ memset(a, 0, sizeof(*a));
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ cparm->capability = sensor->streamcap.capability;
+ cparm->timeperframe = sensor->streamcap.timeperframe;
+ cparm->capturemode = sensor->streamcap.capturemode;
+ ret = 0;
+ break;
+
+ /* These are all the possible cases. */
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ pr_err(" type is not V4L2_BUF_TYPE_VIDEO_CAPTURE " \
+ "but %d\n", a->type);
+ ret = -EINVAL;
+ break;
+
+ default:
+ pr_err(" type is unknown - %d\n", a->type);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+/*!
+ * ioctl_s_parm - V4L2 sensor interface handler for VIDIOC_S_PARM ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure
+ *
+ * Configures the sensor to use the input parameters, if possible. If
+ * not possible, reverts to the old parameters and returns the
+ * appropriate error code.
+ */
+static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)
+{
+ struct sensor *sensor = s->priv;
+ struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe;
+ u32 tgt_fps; /* target frames per secound */
+ int ret = 0;
+
+ pr_debug("In ov2640:ioctl_s_parm\n");
+
+ switch (a->type) {
+ /* This is the only case currently handled. */
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ pr_debug(" type is V4L2_BUF_TYPE_VIDEO_CAPTURE\n");
+
+ /* Check that the new frame rate is allowed. */
+ if ((timeperframe->numerator == 0)
+ || (timeperframe->denominator == 0)) {
+ timeperframe->denominator = DEFAULT_FPS;
+ timeperframe->numerator = 1;
+ }
+ tgt_fps = timeperframe->denominator
+ / timeperframe->numerator;
+
+ if (tgt_fps > MAX_FPS) {
+ timeperframe->denominator = MAX_FPS;
+ timeperframe->numerator = 1;
+ } else if (tgt_fps < MIN_FPS) {
+ timeperframe->denominator = MIN_FPS;
+ timeperframe->numerator = 1;
+ }
+ sensor->streamcap.timeperframe = *timeperframe;
+ sensor->streamcap.capturemode =
+ (u32)a->parm.capture.capturemode;
+
+ ret = ov2640_init_mode(sensor);
+ if (tgt_fps == 15)
+ ov2640_set_fps(sensor, tgt_fps);
+ break;
+
+ /* These are all the possible cases. */
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ pr_err(" type is not V4L2_BUF_TYPE_VIDEO_CAPTURE " \
+ "but %d\n", a->type);
+ ret = -EINVAL;
+ break;
+
+ default:
+ pr_err(" type is unknown - %d\n", a->type);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+/*!
+ * ioctl_s_fmt_cap - V4L2 sensor interface handler for ioctl_s_fmt_cap
+ * set camera output format and resolution format
+ *
+ * @s: pointer to standard V4L2 device structure
+ * @arg: pointer to parameter, according this to set camera
+ *
+ * Returns 0 if set succeed, else return -1
+ */
+static int ioctl_s_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f)
+{
+ struct sensor *sensor = s->priv;
+ u32 format = f->fmt.pix.pixelformat;
+ int size = 0, ret = 0;
+
+ size = f->fmt.pix.width * f->fmt.pix.height;
+ switch (format) {
+ case V4L2_PIX_FMT_RGB565:
+ if (size > 640 * 480)
+ sensor->streamcap.capturemode = V4L2_MODE_HIGHQUALITY;
+ else
+ sensor->streamcap.capturemode = 0;
+ ret = ov2640_init_mode(sensor);
+
+ ret = ov2640_set_format(sensor, V4L2_PIX_FMT_RGB565);
+ break;
+ case V4L2_PIX_FMT_UYVY:
+ if (size > 640 * 480)
+ sensor->streamcap.capturemode = V4L2_MODE_HIGHQUALITY;
+ else
+ sensor->streamcap.capturemode = 0;
+ ret = ov2640_init_mode(sensor);
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ if (size > 640 * 480)
+ sensor->streamcap.capturemode = V4L2_MODE_HIGHQUALITY;
+ else
+ sensor->streamcap.capturemode = 0;
+ ret = ov2640_init_mode(sensor);
+
+ /* YUYV: width * 2, YY: width */
+ ret = ov2640_set_format(sensor, V4L2_PIX_FMT_YUV420);
+ break;
+ default:
+ pr_debug("case not supported\n");
+ break;
+ }
+
+ return ret;
+}
+
+/*!
+ * ioctl_g_fmt_cap - V4L2 sensor interface handler for ioctl_g_fmt_cap
+ * @s: pointer to standard V4L2 device structure
+ * @f: pointer to standard V4L2 v4l2_format structure
+ *
+ * Returns the sensor's current pixel format in the v4l2_format
+ * parameter.
+ */
+static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f)
+{
+ struct sensor *sensor = s->priv;
+
+ pr_debug("In ov2640:ioctl_g_fmt_cap.\n");
+
+ f->fmt.pix = sensor->pix;
+
+ return 0;
+}
+
+/*!
+ * ioctl_g_ctrl - V4L2 sensor interface handler for VIDIOC_G_CTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @vc: standard V4L2 VIDIOC_G_CTRL ioctl structure
+ *
+ * If the requested control is supported, returns the control's current
+ * value from the video_control[] array. Otherwise, returns -EINVAL
+ * if the control is not supported.
+ */
+static int ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc)
+{
+ int ret = 0;
+
+ pr_debug("In ov2640:ioctl_g_ctrl\n");
+
+ switch (vc->id) {
+ case V4L2_CID_BRIGHTNESS:
+ vc->value = ov2640_data.brightness;
+ break;
+ case V4L2_CID_HUE:
+ vc->value = ov2640_data.hue;
+ break;
+ case V4L2_CID_CONTRAST:
+ vc->value = ov2640_data.contrast;
+ break;
+ case V4L2_CID_SATURATION:
+ vc->value = ov2640_data.saturation;
+ break;
+ case V4L2_CID_RED_BALANCE:
+ vc->value = ov2640_data.red;
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ vc->value = ov2640_data.blue;
+ break;
+ case V4L2_CID_EXPOSURE:
+ vc->value = ov2640_data.ae_mode;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+/*!
+ * ioctl_s_ctrl - V4L2 sensor interface handler for VIDIOC_S_CTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @vc: standard V4L2 VIDIOC_S_CTRL ioctl structure
+ *
+ * If the requested control is supported, sets the control's current
+ * value in HW (and updates the video_control[] array). Otherwise,
+ * returns -EINVAL if the control is not supported.
+ */
+static int ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc)
+{
+ int retval = 0;
+
+ pr_debug("In ov2640:ioctl_s_ctrl %d\n", vc->id);
+
+ switch (vc->id) {
+ case V4L2_CID_BRIGHTNESS:
+ pr_debug(" V4L2_CID_BRIGHTNESS\n");
+ break;
+ case V4L2_CID_CONTRAST:
+ pr_debug(" V4L2_CID_CONTRAST\n");
+ break;
+ case V4L2_CID_SATURATION:
+ pr_debug(" V4L2_CID_SATURATION\n");
+ break;
+ case V4L2_CID_HUE:
+ pr_debug(" V4L2_CID_HUE\n");
+ break;
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ pr_debug(
+ " V4L2_CID_AUTO_WHITE_BALANCE\n");
+ break;
+ case V4L2_CID_DO_WHITE_BALANCE:
+ pr_debug(
+ " V4L2_CID_DO_WHITE_BALANCE\n");
+ break;
+ case V4L2_CID_RED_BALANCE:
+ pr_debug(" V4L2_CID_RED_BALANCE\n");
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ pr_debug(" V4L2_CID_BLUE_BALANCE\n");
+ break;
+ case V4L2_CID_GAMMA:
+ pr_debug(" V4L2_CID_GAMMA\n");
+ break;
+ case V4L2_CID_EXPOSURE:
+ pr_debug(" V4L2_CID_EXPOSURE\n");
+ break;
+ case V4L2_CID_AUTOGAIN:
+ pr_debug(" V4L2_CID_AUTOGAIN\n");
+ break;
+ case V4L2_CID_GAIN:
+ pr_debug(" V4L2_CID_GAIN\n");
+ break;
+ case V4L2_CID_HFLIP:
+ pr_debug(" V4L2_CID_HFLIP\n");
+ break;
+ case V4L2_CID_VFLIP:
+ pr_debug(" V4L2_CID_VFLIP\n");
+ break;
+ default:
+ pr_debug(" Default case\n");
+ retval = -EPERM;
+ break;
+ }
+
+ return retval;
+}
+
+/*!
+ * ioctl_init - V4L2 sensor interface handler for VIDIOC_INT_INIT
+ * @s: pointer to standard V4L2 device structure
+ */
+static int ioctl_init(struct v4l2_int_device *s)
+{
+ pr_debug("In ov2640:ioctl_init\n");
+
+ return 0;
+}
+
+/*!
+ * ioctl_dev_init - V4L2 sensor interface handler for vidioc_int_dev_init_num
+ * @s: pointer to standard V4L2 device structure
+ *
+ * Initialise the device when slave attaches to the master.
+ */
+static int ioctl_dev_init(struct v4l2_int_device *s)
+{
+ struct sensor *sensor = s->priv;
+ u32 tgt_xclk; /* target xclk */
+
+ pr_debug("In ov2640:ioctl_dev_init\n");
+
+ gpio_sensor_active();
+ ov2640_data.on = true;
+
+ tgt_xclk = ov2640_data.mclk;
+ tgt_xclk = min(tgt_xclk, (u32)OV2640_XCLK_MAX);
+ tgt_xclk = max(tgt_xclk, (u32)OV2640_XCLK_MIN);
+ ov2640_data.mclk = tgt_xclk;
+
+ pr_debug(" Setting mclk to %d MHz\n",
+ tgt_xclk / 1000000);
+ set_mclk_rate(&ov2640_data.mclk);
+
+ return ov2640_init_mode(sensor);
+}
+
+/*!
+ * ioctl_dev_exit - V4L2 sensor interface handler for vidioc_int_dev_exit_num
+ * @s: pointer to standard V4L2 device structure
+ *
+ * Delinitialise the device when slave detaches to the master.
+ */
+static int ioctl_dev_exit(struct v4l2_int_device *s)
+{
+ pr_debug("In ov2640:ioctl_dev_exit\n");
+
+ gpio_sensor_inactive();
+
+ return 0;
+}
+
+/*!
+ * This structure defines all the ioctls for this module and links them to the
+ * enumeration.
+ */
+static struct v4l2_int_ioctl_desc ov2640_ioctl_desc[] = {
+ {vidioc_int_dev_init_num, (v4l2_int_ioctl_func *)ioctl_dev_init},
+ {vidioc_int_dev_exit_num, (v4l2_int_ioctl_func*)ioctl_dev_exit},
+ {vidioc_int_s_power_num, (v4l2_int_ioctl_func *)ioctl_s_power},
+ {vidioc_int_g_ifparm_num, (v4l2_int_ioctl_func *)ioctl_g_ifparm},
+/* {vidioc_int_g_needs_reset_num,
+ (v4l2_int_ioctl_func *)ioctl_g_needs_reset}, */
+/* {vidioc_int_reset_num, (v4l2_int_ioctl_func *)ioctl_reset}, */
+ {vidioc_int_init_num, (v4l2_int_ioctl_func *)ioctl_init},
+/* {vidioc_int_enum_fmt_cap_num,
+ (v4l2_int_ioctl_func *)ioctl_enum_fmt_cap}, */
+/* {vidioc_int_try_fmt_cap_num,
+ (v4l2_int_ioctl_func *)ioctl_try_fmt_cap}, */
+ {vidioc_int_g_fmt_cap_num, (v4l2_int_ioctl_func *)ioctl_g_fmt_cap},
+ {vidioc_int_s_fmt_cap_num, (v4l2_int_ioctl_func*)ioctl_s_fmt_cap},
+ {vidioc_int_g_parm_num, (v4l2_int_ioctl_func *)ioctl_g_parm},
+ {vidioc_int_s_parm_num, (v4l2_int_ioctl_func *)ioctl_s_parm},
+/* {vidioc_int_queryctrl_num, (v4l2_int_ioctl_func *)ioctl_queryctrl}, */
+ {vidioc_int_g_ctrl_num, (v4l2_int_ioctl_func *)ioctl_g_ctrl},
+ {vidioc_int_s_ctrl_num, (v4l2_int_ioctl_func *)ioctl_s_ctrl},
+};
+
+static struct v4l2_int_slave ov2640_slave = {
+ .ioctls = ov2640_ioctl_desc,
+ .num_ioctls = ARRAY_SIZE(ov2640_ioctl_desc),
+};
+
+static struct v4l2_int_device ov2640_int_device = {
+ .module = THIS_MODULE,
+ .name = "ov2640",
+ .type = v4l2_int_type_slave,
+ .u = {
+ .slave = &ov2640_slave,
+ },
+};
+
+/*!
+ * ov2640 I2C attach function
+ * Function set in i2c_driver struct.
+ * Called by insmod ov2640_camera.ko.
+ *
+ * @param client struct i2c_client*
+ * @return Error code indicating success or failure
+ */
+static int ov2640_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int retval;
+ struct mxc_camera_platform_data *plat_data = client->dev.platform_data;
+
+ pr_debug("In ov2640_probe (RH_BT565)\n");
+
+ /* Set initial values for the sensor struct. */
+ memset(&ov2640_data, 0, sizeof(ov2640_data));
+ ov2640_data.i2c_client = client;
+ ov2640_data.mclk = 24000000;
+ ov2640_data.mclk = plat_data->mclk;
+ ov2640_data.pix.pixelformat = V4L2_PIX_FMT_UYVY;
+ ov2640_data.pix.width = 800;
+ ov2640_data.pix.height = 600;
+ ov2640_data.streamcap.capability = V4L2_MODE_HIGHQUALITY
+ | V4L2_CAP_TIMEPERFRAME;
+ ov2640_data.streamcap.capturemode = 0;
+ ov2640_data.streamcap.timeperframe.denominator = DEFAULT_FPS;
+ ov2640_data.streamcap.timeperframe.numerator = 1;
+
+ if (plat_data->io_regulator) {
+ io_regulator =
+ regulator_get(&client->dev, plat_data->io_regulator);
+ if (!IS_ERR(io_regulator)) {
+ regulator_set_voltage(io_regulator, 2800000, 2800000);
+ if (regulator_enable(io_regulator) != 0) {
+ pr_err("%s:io set voltage error\n", __func__);
+ goto err1;
+ } else {
+ dev_dbg(&client->dev,
+ "%s:io set voltage ok\n", __func__);
+ }
+ } else
+ io_regulator = NULL;
+ }
+
+ if (plat_data->core_regulator) {
+ core_regulator =
+ regulator_get(&client->dev, plat_data->core_regulator);
+ if (!IS_ERR(core_regulator)) {
+ regulator_set_voltage(core_regulator,
+ 1300000, 1300000);
+ if (regulator_enable(core_regulator) != 0) {
+ pr_err("%s:core set voltage error\n", __func__);
+ goto err2;
+ } else {
+ dev_dbg(&client->dev,
+ "%s:core set voltage ok\n", __func__);
+ }
+ } else
+ core_regulator = NULL;
+ }
+
+ if (plat_data->analog_regulator) {
+ analog_regulator =
+ regulator_get(&client->dev, plat_data->analog_regulator);
+ if (!IS_ERR(analog_regulator)) {
+ regulator_set_voltage(analog_regulator, 2000000, 2000000);
+ if (regulator_enable(analog_regulator) != 0) {
+ pr_err("%s:analog set voltage error\n",
+ __func__);
+ goto err3;
+ } else {
+ dev_dbg(&client->dev,
+ "%s:analog set voltage ok\n", __func__);
+ }
+ } else
+ analog_regulator = NULL;
+ }
+
+ if (plat_data->gpo_regulator) {
+ gpo_regulator =
+ regulator_get(&client->dev, plat_data->gpo_regulator);
+ if (!IS_ERR(gpo_regulator)) {
+ if (regulator_enable(gpo_regulator) != 0) {
+ pr_err("%s:gpo3 set voltage error\n", __func__);
+ goto err4;
+ } else {
+ dev_dbg(&client->dev,
+ "%s:gpo3 set voltage ok\n", __func__);
+ }
+ } else
+ gpo_regulator = NULL;
+ }
+
+ /* This function attaches this structure to the /dev/video0 device.
+ * The pointer in priv points to the ov2640_data structure here.*/
+ ov2640_int_device.priv = &ov2640_data;
+ retval = v4l2_int_device_register(&ov2640_int_device);
+
+ return retval;
+
+err4:
+ if (analog_regulator) {
+ regulator_disable(analog_regulator);
+ regulator_put(analog_regulator);
+ }
+err3:
+ if (core_regulator) {
+ regulator_disable(core_regulator);
+ regulator_put(core_regulator);
+ }
+err2:
+ if (io_regulator) {
+ regulator_disable(io_regulator);
+ regulator_put(io_regulator);
+ }
+err1:
+ return -1;
+}
+
+/*!
+ * ov2640 I2C detach function
+ * Called on rmmod ov2640_camera.ko
+ *
+ * @param client struct i2c_client*
+ * @return Error code indicating success or failure
+ */
+static int ov2640_remove(struct i2c_client *client)
+{
+ pr_debug("In ov2640_remove\n");
+
+ v4l2_int_device_unregister(&ov2640_int_device);
+
+ if (gpo_regulator) {
+ regulator_disable(gpo_regulator);
+ regulator_put(gpo_regulator);
+ }
+
+ if (analog_regulator) {
+ regulator_disable(analog_regulator);
+ regulator_put(analog_regulator);
+ }
+
+ if (core_regulator) {
+ regulator_disable(core_regulator);
+ regulator_put(core_regulator);
+ }
+
+ if (io_regulator) {
+ regulator_disable(io_regulator);
+ regulator_put(io_regulator);
+ }
+
+ return 0;
+}
+
+static const struct i2c_device_id ov2640_id[] = {
+ {"ov2640", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, ov2640_id);
+
+static struct i2c_driver ov2640_i2c_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "ov2640",
+ },
+ .probe = ov2640_probe,
+ .remove = ov2640_remove,
+ .id_table = ov2640_id,
+/* To add power management add .suspend and .resume functions */
+};
+
+/*!
+ * ov2640 init function
+ * Called by insmod ov2640_camera.ko.
+ *
+ * @return Error code indicating success or failure
+ */
+static __init int ov2640_init(void)
+{
+ u8 err;
+
+ pr_debug("In ov2640_init\n");
+
+ err = i2c_add_driver(&ov2640_i2c_driver);
+ if (err != 0)
+ pr_err("%s:driver registration failed, error=%d \n",
+ __func__, err);
+
+ return err;
+}
+
+/*!
+ * OV2640 cleanup function
+ * Called on rmmod ov2640_camera.ko
+ *
+ * @return Error code indicating success or failure
+ */
+static void __exit ov2640_clean(void)
+{
+ pr_debug("In ov2640_clean\n");
+ i2c_del_driver(&ov2640_i2c_driver);
+}
+
+module_init(ov2640_init);
+module_exit(ov2640_clean);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("OV2640 Camera Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxc/capture/ov3640.c b/drivers/media/video/mxc/capture/ov3640.c
new file mode 100644
index 000000000000..899945b7d071
--- /dev/null
+++ b/drivers/media/video/mxc/capture/ov3640.c
@@ -0,0 +1,1432 @@
+/*
+ * Copyright 2005-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/regulator/consumer.h>
+#include <mach/hardware.h>
+#include <media/v4l2-int-device.h>
+#include "mxc_v4l2_capture.h"
+
+#define OV3640_VOLTAGE_ANALOG 2800000
+#define OV3640_VOLTAGE_DIGITAL_CORE 1500000
+#define OV3640_VOLTAGE_DIGITAL_IO 1800000
+#define OV3640_VOLTAGE_DIGITAL_GPO 2800000
+
+/* Check these values! */
+#define MIN_FPS 15
+#define MAX_FPS 30
+#define DEFAULT_FPS 30
+
+#define OV3640_XCLK_MIN 6000000
+#define OV3640_XCLK_MAX 24000000
+
+enum ov3640_mode {
+ ov3640_mode_MIN = 0,
+ ov3640_mode_VGA_640_480 = 0,
+ ov3640_mode_QVGA_320_240 = 1,
+ ov3640_mode_XGA_1024_768 = 2,
+ ov3640_mode_QXGA_2048_1536 = 3,
+ ov3640_mode_NTSC_720_480 = 4,
+ ov3640_mode_PAL_720_576 = 5,
+ ov3640_mode_MAX = 5
+};
+
+enum ov3640_frame_rate {
+ ov3640_15_fps,
+ ov3640_30_fps
+};
+
+struct reg_value {
+ u16 u16RegAddr;
+ u8 u8Val;
+ u8 u8Mask;
+ u32 u32Delay_ms;
+};
+
+struct ov3640_mode_info {
+ enum ov3640_mode mode;
+ u32 width;
+ u32 height;
+ struct reg_value *init_data_ptr;
+ u32 init_data_size;
+};
+
+/*!
+ * Maintains the information on the current state of the sesor.
+ */
+struct sensor {
+ const struct ov3640_platform_data *platform_data;
+ struct v4l2_int_device *v4l2_int_device;
+ struct i2c_client *i2c_client;
+ struct v4l2_pix_format pix;
+ struct v4l2_captureparm streamcap;
+ bool on;
+
+ /* control settings */
+ int brightness;
+ int hue;
+ int contrast;
+ int saturation;
+ int red;
+ int green;
+ int blue;
+ int ae_mode;
+
+ u32 mclk;
+ int csi;
+} ov3640_data;
+
+static struct reg_value ov3640_setting_15fps_QXGA_2048_1536[] = {
+#if 0
+ /* The true 15fps QXGA setting. */
+ {0x3012, 0x80, 0, 0}, {0x304d, 0x41, 0, 0}, {0x3087, 0x16, 0, 0},
+ {0x30aa, 0x45, 0, 0}, {0x30b0, 0xff, 0, 0}, {0x30b1, 0xff, 0, 0},
+ {0x30b2, 0x13, 0, 0}, {0x30d7, 0x10, 0, 0}, {0x309e, 0x00, 0, 0},
+ {0x3602, 0x26, 0, 0}, {0x3603, 0x4D, 0, 0}, {0x364c, 0x04, 0, 0},
+ {0x360c, 0x12, 0, 0}, {0x361e, 0x00, 0, 0}, {0x361f, 0x11, 0, 0},
+ {0x3633, 0x03, 0, 0}, {0x3629, 0x3c, 0, 0}, {0x300e, 0x33, 0, 0},
+ {0x300f, 0x21, 0, 0}, {0x3010, 0x20, 0, 0}, {0x3011, 0x00, 0, 0},
+ {0x304c, 0x81, 0, 0}, {0x3029, 0x47, 0, 0}, {0x3070, 0x00, 0, 0},
+ {0x3071, 0xEC, 0, 0}, {0x301C, 0x06, 0, 0}, {0x3072, 0x00, 0, 0},
+ {0x3073, 0xC5, 0, 0}, {0x301D, 0x07, 0, 0}, {0x3018, 0x38, 0, 0},
+ {0x3019, 0x30, 0, 0}, {0x301a, 0x61, 0, 0}, {0x307d, 0x00, 0, 0},
+ {0x3087, 0x02, 0, 0}, {0x3082, 0x20, 0, 0}, {0x303c, 0x08, 0, 0},
+ {0x303d, 0x18, 0, 0}, {0x303e, 0x06, 0, 0}, {0x303F, 0x0c, 0, 0},
+ {0x3030, 0x62, 0, 0}, {0x3031, 0x26, 0, 0}, {0x3032, 0xe6, 0, 0},
+ {0x3033, 0x6e, 0, 0}, {0x3034, 0xea, 0, 0}, {0x3035, 0xae, 0, 0},
+ {0x3036, 0xa6, 0, 0}, {0x3037, 0x6a, 0, 0}, {0x3015, 0x12, 0, 0},
+ {0x3014, 0x04, 0, 0}, {0x3013, 0xf7, 0, 0}, {0x3104, 0x02, 0, 0},
+ {0x3105, 0xfd, 0, 0}, {0x3106, 0x00, 0, 0}, {0x3107, 0xff, 0, 0},
+ {0x3308, 0xa5, 0, 0}, {0x3316, 0xff, 0, 0}, {0x3317, 0x00, 0, 0},
+ {0x3087, 0x02, 0, 0}, {0x3082, 0x20, 0, 0}, {0x3300, 0x13, 0, 0},
+ {0x3301, 0xd6, 0, 0}, {0x3302, 0xef, 0, 0}, {0x30b8, 0x20, 0, 0},
+ {0x30b9, 0x17, 0, 0}, {0x30ba, 0x04, 0, 0}, {0x30bb, 0x08, 0, 0},
+ {0x3100, 0x02, 0, 0}, {0x3304, 0x00, 0, 0}, {0x3400, 0x00, 0, 0},
+ {0x3404, 0x02, 0, 0}, {0x3020, 0x01, 0, 0}, {0x3021, 0x1d, 0, 0},
+ {0x3022, 0x00, 0, 0}, {0x3023, 0x0a, 0, 0}, {0x3024, 0x08, 0, 0},
+ {0x3025, 0x18, 0, 0}, {0x3026, 0x06, 0, 0}, {0x3027, 0x0c, 0, 0},
+ {0x335f, 0x68, 0, 0}, {0x3360, 0x18, 0, 0}, {0x3361, 0x0c, 0, 0},
+ {0x3362, 0x68, 0, 0}, {0x3363, 0x08, 0, 0}, {0x3364, 0x04, 0, 0},
+ {0x3403, 0x42, 0, 0}, {0x3088, 0x08, 0, 0}, {0x3089, 0x00, 0, 0},
+ {0x308a, 0x06, 0, 0}, {0x308b, 0x00, 0, 0}, {0x3507, 0x06, 0, 0},
+ {0x350a, 0x4f, 0, 0}, {0x3600, 0xc4, 0, 0},
+#endif
+ /*
+ * Only support 7.5fps for QXGA to workaround screen tearing issue
+ * for 15fps when capturing still image.
+ */
+ {0x3012, 0x80, 0, 0}, {0x304d, 0x45, 0, 0}, {0x30a7, 0x5e, 0, 0},
+ {0x3087, 0x16, 0, 0}, {0x309c, 0x1a, 0, 0}, {0x30a2, 0xe4, 0, 0},
+ {0x30aa, 0x42, 0, 0}, {0x30b0, 0xff, 0, 0}, {0x30b1, 0xff, 0, 0},
+ {0x30b2, 0x10, 0, 0}, {0x300e, 0x32, 0, 0}, {0x300f, 0x21, 0, 0},
+ {0x3010, 0x20, 0, 0}, {0x3011, 0x00, 0, 0}, {0x304c, 0x81, 0, 0},
+ {0x30d7, 0x10, 0, 0}, {0x30d9, 0x0d, 0, 0}, {0x30db, 0x08, 0, 0},
+ {0x3016, 0x82, 0, 0}, {0x3018, 0x38, 0, 0}, {0x3019, 0x30, 0, 0},
+ {0x301a, 0x61, 0, 0}, {0x307d, 0x00, 0, 0}, {0x3087, 0x02, 0, 0},
+ {0x3082, 0x20, 0, 0}, {0x3015, 0x12, 0, 0}, {0x3014, 0x04, 0, 0},
+ {0x3013, 0xf7, 0, 0}, {0x303c, 0x08, 0, 0}, {0x303d, 0x18, 0, 0},
+ {0x303e, 0x06, 0, 0}, {0x303f, 0x0c, 0, 0}, {0x3030, 0x62, 0, 0},
+ {0x3031, 0x26, 0, 0}, {0x3032, 0xe6, 0, 0}, {0x3033, 0x6e, 0, 0},
+ {0x3034, 0xea, 0, 0}, {0x3035, 0xae, 0, 0}, {0x3036, 0xa6, 0, 0},
+ {0x3037, 0x6a, 0, 0}, {0x3104, 0x02, 0, 0}, {0x3105, 0xfd, 0, 0},
+ {0x3106, 0x00, 0, 0}, {0x3107, 0xff, 0, 0}, {0x3300, 0x12, 0, 0},
+ {0x3301, 0xde, 0, 0}, {0x3302, 0xcf, 0, 0}, {0x3312, 0x26, 0, 0},
+ {0x3314, 0x42, 0, 0}, {0x3313, 0x2b, 0, 0}, {0x3315, 0x42, 0, 0},
+ {0x3310, 0xd0, 0, 0}, {0x3311, 0xbd, 0, 0}, {0x330c, 0x18, 0, 0},
+ {0x330d, 0x18, 0, 0}, {0x330e, 0x56, 0, 0}, {0x330f, 0x5c, 0, 0},
+ {0x330b, 0x1c, 0, 0}, {0x3306, 0x5c, 0, 0}, {0x3307, 0x11, 0, 0},
+ {0x336a, 0x52, 0, 0}, {0x3370, 0x46, 0, 0}, {0x3376, 0x38, 0, 0},
+ {0x30b8, 0x20, 0, 0}, {0x30b9, 0x17, 0, 0}, {0x30ba, 0x04, 0, 0},
+ {0x30bb, 0x08, 0, 0}, {0x3507, 0x06, 0, 0}, {0x350a, 0x4f, 0, 0},
+ {0x3100, 0x02, 0, 0}, {0x3301, 0xde, 0, 0}, {0x3304, 0x00, 0, 0},
+ {0x3400, 0x00, 0, 0}, {0x3404, 0x02, 0, 0}, {0x3600, 0xc4, 0, 0},
+ {0x3088, 0x08, 0, 0}, {0x3089, 0x00, 0, 0}, {0x308a, 0x06, 0, 0},
+ {0x308b, 0x00, 0, 0}, {0x308d, 0x04, 0, 0}, {0x3086, 0x03, 0, 0},
+ {0x3086, 0x00, 0, 0}, {0x3011, 0x01, 0, 0},
+};
+
+static struct reg_value ov3640_setting_15fps_XGA_1024_768[] = {
+ {0x3012, 0x80, 0, 0}, {0x304d, 0x45, 0, 0}, {0x30a7, 0x5e, 0, 0},
+ {0x3087, 0x16, 0, 0}, {0x309c, 0x1a, 0, 0}, {0x30a2, 0xe4, 0, 0},
+ {0x30aa, 0x42, 0, 0}, {0x30b0, 0xff, 0, 0}, {0x30b1, 0xff, 0, 0},
+ {0x30b2, 0x10, 0, 0}, {0x300e, 0x32, 0, 0}, {0x300f, 0x21, 0, 0},
+ {0x3010, 0x20, 0, 0}, {0x3011, 0x00, 0, 0}, {0x304c, 0x81, 0, 0},
+ {0x3016, 0x82, 0, 0}, {0x3018, 0x38, 0, 0}, {0x3019, 0x30, 0, 0},
+ {0x301a, 0x61, 0, 0}, {0x307d, 0x00, 0, 0}, {0x3087, 0x02, 0, 0},
+ {0x3082, 0x20, 0, 0}, {0x3015, 0x12, 0, 0}, {0x3014, 0x04, 0, 0},
+ {0x3013, 0xf7, 0, 0}, {0x303c, 0x08, 0, 0}, {0x303d, 0x18, 0, 0},
+ {0x303e, 0x06, 0, 0}, {0x303f, 0x0c, 0, 0}, {0x3030, 0x62, 0, 0},
+ {0x3031, 0x26, 0, 0}, {0x3032, 0xe6, 0, 0}, {0x3033, 0x6e, 0, 0},
+ {0x3034, 0xea, 0, 0}, {0x3035, 0xae, 0, 0}, {0x3036, 0xa6, 0, 0},
+ {0x3037, 0x6a, 0, 0}, {0x3104, 0x02, 0, 0}, {0x3105, 0xfd, 0, 0},
+ {0x3106, 0x00, 0, 0}, {0x3107, 0xff, 0, 0}, {0x3300, 0x12, 0, 0},
+ {0x3301, 0xde, 0, 0}, {0x3302, 0xcf, 0, 0}, {0x3312, 0x26, 0, 0},
+ {0x3314, 0x42, 0, 0}, {0x3313, 0x2b, 0, 0}, {0x3315, 0x42, 0, 0},
+ {0x3310, 0xd0, 0, 0}, {0x3311, 0xbd, 0, 0}, {0x330c, 0x18, 0, 0},
+ {0x330d, 0x18, 0, 0}, {0x330e, 0x56, 0, 0}, {0x330f, 0x5c, 0, 0},
+ {0x330b, 0x1c, 0, 0}, {0x3306, 0x5c, 0, 0}, {0x3307, 0x11, 0, 0},
+ {0x336a, 0x52, 0, 0}, {0x3370, 0x46, 0, 0}, {0x3376, 0x38, 0, 0},
+ {0x30b8, 0x20, 0, 0}, {0x30b9, 0x17, 0, 0}, {0x30ba, 0x04, 0, 0},
+ {0x30bb, 0x08, 0, 0}, {0x3507, 0x06, 0, 0}, {0x350a, 0x4f, 0, 0},
+ {0x3100, 0x02, 0, 0}, {0x3301, 0xde, 0, 0}, {0x3304, 0x00, 0, 0},
+ {0x3400, 0x01, 0, 0}, {0x3404, 0x1d, 0, 0}, {0x3600, 0xc4, 0, 0},
+ {0x3302, 0xef, 0, 0}, {0x3020, 0x01, 0, 0}, {0x3021, 0x1d, 0, 0},
+ {0x3022, 0x00, 0, 0}, {0x3023, 0x0a, 0, 0}, {0x3024, 0x08, 0, 0},
+ {0x3025, 0x00, 0, 0}, {0x3026, 0x06, 0, 0}, {0x3027, 0x00, 0, 0},
+ {0x335f, 0x68, 0, 0}, {0x3360, 0x00, 0, 0}, {0x3361, 0x00, 0, 0},
+ {0x3362, 0x34, 0, 0}, {0x3363, 0x00, 0, 0}, {0x3364, 0x00, 0, 0},
+ {0x3403, 0x00, 0, 0}, {0x3088, 0x04, 0, 0}, {0x3089, 0x00, 0, 0},
+ {0x308a, 0x03, 0, 0}, {0x308b, 0x00, 0, 0}, {0x307c, 0x10, 0, 0},
+ {0x3090, 0xc0, 0, 0}, {0x304c, 0x84, 0, 0}, {0x308d, 0x04, 0, 0},
+ {0x3086, 0x03, 0, 0}, {0x3086, 0x00, 0, 0}, {0x3011, 0x01, 0, 0},
+};
+
+static struct reg_value ov3640_setting_30fps_XGA_1024_768[] = {
+ {0x0, 0x0, 0}
+};
+
+static struct reg_value ov3640_setting_15fps_VGA_640_480[] = {
+ {0x3012, 0x80, 0, 0}, {0x304d, 0x45, 0, 0}, {0x30a7, 0x5e, 0, 0},
+ {0x3087, 0x16, 0, 0}, {0x309c, 0x1a, 0, 0}, {0x30a2, 0xe4, 0, 0},
+ {0x30aa, 0x42, 0, 0}, {0x30b0, 0xff, 0, 0}, {0x30b1, 0xff, 0, 0},
+ {0x30b2, 0x10, 0, 0}, {0x300e, 0x32, 0, 0}, {0x300f, 0x21, 0, 0},
+ {0x3010, 0x20, 0, 0}, {0x3011, 0x00, 0, 0}, {0x304c, 0x81, 0, 0},
+ {0x30d7, 0x10, 0, 0}, {0x30d9, 0x0d, 0, 0}, {0x30db, 0x08, 0, 0},
+ {0x3016, 0x82, 0, 0}, {0x3018, 0x38, 0, 0}, {0x3019, 0x30, 0, 0},
+ {0x301a, 0x61, 0, 0}, {0x307d, 0x00, 0, 0}, {0x3087, 0x02, 0, 0},
+ {0x3082, 0x20, 0, 0}, {0x3015, 0x12, 0, 0}, {0x3014, 0x04, 0, 0},
+ {0x3013, 0xf7, 0, 0}, {0x303c, 0x08, 0, 0}, {0x303d, 0x18, 0, 0},
+ {0x303e, 0x06, 0, 0}, {0x303f, 0x0c, 0, 0}, {0x3030, 0x62, 0, 0},
+ {0x3031, 0x26, 0, 0}, {0x3032, 0xe6, 0, 0}, {0x3033, 0x6e, 0, 0},
+ {0x3034, 0xea, 0, 0}, {0x3035, 0xae, 0, 0}, {0x3036, 0xa6, 0, 0},
+ {0x3037, 0x6a, 0, 0}, {0x3104, 0x02, 0, 0}, {0x3105, 0xfd, 0, 0},
+ {0x3106, 0x00, 0, 0}, {0x3107, 0xff, 0, 0}, {0x3300, 0x12, 0, 0},
+ {0x3301, 0xde, 0, 0}, {0x3302, 0xcf, 0, 0}, {0x3312, 0x26, 0, 0},
+ {0x3314, 0x42, 0, 0}, {0x3313, 0x2b, 0, 0}, {0x3315, 0x42, 0, 0},
+ {0x3310, 0xd0, 0, 0}, {0x3311, 0xbd, 0, 0}, {0x330c, 0x18, 0, 0},
+ {0x330d, 0x18, 0, 0}, {0x330e, 0x56, 0, 0}, {0x330f, 0x5c, 0, 0},
+ {0x330b, 0x1c, 0, 0}, {0x3306, 0x5c, 0, 0}, {0x3307, 0x11, 0, 0},
+ {0x336a, 0x52, 0, 0}, {0x3370, 0x46, 0, 0}, {0x3376, 0x38, 0, 0},
+ {0x30b8, 0x20, 0, 0}, {0x30b9, 0x17, 0, 0}, {0x30ba, 0x04, 0, 0},
+ {0x30bb, 0x08, 0, 0}, {0x3507, 0x06, 0, 0}, {0x350a, 0x4f, 0, 0},
+ {0x3100, 0x02, 0, 0}, {0x3301, 0xde, 0, 0}, {0x3304, 0x00, 0, 0},
+ {0x3400, 0x00, 0, 0}, {0x3404, 0x42, 0, 0}, {0x3600, 0xc4, 0, 0},
+ {0x3302, 0xef, 0, 0}, {0x3020, 0x01, 0, 0}, {0x3021, 0x1d, 0, 0},
+ {0x3022, 0x00, 0, 0}, {0x3023, 0x0a, 0, 0}, {0x3024, 0x08, 0, 0},
+ {0x3025, 0x00, 0, 0}, {0x3026, 0x06, 0, 0}, {0x3027, 0x00, 0, 0},
+ {0x335f, 0x68, 0, 0}, {0x3360, 0x00, 0, 0}, {0x3361, 0x00, 0, 0},
+ {0x3362, 0x12, 0, 0}, {0x3363, 0x80, 0, 0}, {0x3364, 0xe0, 0, 0},
+ {0x3403, 0x00, 0, 0}, {0x3088, 0x02, 0, 0}, {0x3089, 0x80, 0, 0},
+ {0x308a, 0x01, 0, 0}, {0x308b, 0xe0, 0, 0}, {0x307c, 0x10, 0, 0},
+ {0x3090, 0xc0, 0, 0}, {0x304c, 0x84, 0, 0}, {0x308d, 0x04, 0, 0},
+ {0x3086, 0x03, 0, 0}, {0x3086, 0x00, 0, 0}, {0x3011, 0x00, 0, 0},
+};
+
+static struct reg_value ov3640_setting_30fps_VGA_640_480[] = {
+ {0x3012, 0x80, 0, 0}, {0x304d, 0x45, 0, 0}, {0x30a7, 0x5e, 0, 0},
+ {0x3087, 0x16, 0, 0}, {0x309c, 0x1a, 0, 0}, {0x30a2, 0xe4, 0, 0},
+ {0x30aa, 0x42, 0, 0}, {0x30b0, 0xff, 0, 0}, {0x30b1, 0xff, 0, 0},
+ {0x30b2, 0x10, 0, 0}, {0x300e, 0x32, 0, 0}, {0x300f, 0x21, 0, 0},
+ {0x3010, 0x20, 0, 0}, {0x3011, 0x01, 0, 0}, {0x304c, 0x82, 0, 0},
+ {0x30d7, 0x10, 0, 0}, {0x30d9, 0x0d, 0, 0}, {0x30db, 0x08, 0, 0},
+ {0x3016, 0x82, 0, 0}, {0x3018, 0x38, 0, 0}, {0x3019, 0x30, 0, 0},
+ {0x301a, 0x61, 0, 0}, {0x307d, 0x00, 0, 0}, {0x3087, 0x02, 0, 0},
+ {0x3082, 0x20, 0, 0}, {0x3015, 0x12, 0, 0}, {0x3014, 0x0c, 0, 0},
+ {0x3013, 0xf7, 0, 0}, {0x303c, 0x08, 0, 0}, {0x303d, 0x18, 0, 0},
+ {0x303e, 0x06, 0, 0}, {0x303f, 0x0c, 0, 0}, {0x3030, 0x62, 0, 0},
+ {0x3031, 0x26, 0, 0}, {0x3032, 0xe6, 0, 0}, {0x3033, 0x6e, 0, 0},
+ {0x3034, 0xea, 0, 0}, {0x3035, 0xae, 0, 0}, {0x3036, 0xa6, 0, 0},
+ {0x3037, 0x6a, 0, 0}, {0x3104, 0x02, 0, 0}, {0x3105, 0xfd, 0, 0},
+ {0x3106, 0x00, 0, 0}, {0x3107, 0xff, 0, 0}, {0x3300, 0x12, 0, 0},
+ {0x3301, 0xde, 0, 0}, {0x3302, 0xcf, 0, 0}, {0x3312, 0x26, 0, 0},
+ {0x3314, 0x42, 0, 0}, {0x3313, 0x2b, 0, 0}, {0x3315, 0x42, 0, 0},
+ {0x3310, 0xd0, 0, 0}, {0x3311, 0xbd, 0, 0}, {0x330c, 0x18, 0, 0},
+ {0x330d, 0x18, 0, 0}, {0x330e, 0x56, 0, 0}, {0x330f, 0x5c, 0, 0},
+ {0x330b, 0x1c, 0, 0}, {0x3306, 0x5c, 0, 0}, {0x3307, 0x11, 0, 0},
+ {0x336a, 0x52, 0, 0}, {0x3370, 0x46, 0, 0}, {0x3376, 0x38, 0, 0},
+ {0x3300, 0x13, 0, 0}, {0x30b8, 0x20, 0, 0}, {0x30b9, 0x17, 0, 0},
+ {0x30ba, 0x04, 0, 0}, {0x30bb, 0x08, 0, 0}, {0x3100, 0x02, 0, 0},
+ {0x3301, 0x10, 0x30, 0}, {0x3304, 0x00, 0x03, 0}, {0x3400, 0x00, 0, 0},
+ {0x3404, 0x02, 0, 0}, {0x3600, 0xc0, 0, 0}, {0x308d, 0x04, 0, 0},
+ {0x3086, 0x03, 0, 0}, {0x3086, 0x00, 0, 0}, {0x3012, 0x10, 0, 0},
+ {0x3023, 0x06, 0, 0}, {0x3026, 0x03, 0, 0}, {0x3027, 0x04, 0, 0},
+ {0x302a, 0x03, 0, 0}, {0x302b, 0x10, 0, 0}, {0x3075, 0x24, 0, 0},
+ {0x300d, 0x01, 0, 0}, {0x30d7, 0x80, 0x80, 0}, {0x3069, 0x00, 0x40, 0},
+ {0x303e, 0x00, 0, 0}, {0x303f, 0xc0, 0, 0}, {0x3302, 0x20, 0x20, 0},
+ {0x335f, 0x34, 0, 0}, {0x3360, 0x0c, 0, 0}, {0x3361, 0x04, 0, 0},
+ {0x3362, 0x12, 0, 0}, {0x3363, 0x88, 0, 0}, {0x3364, 0xe4, 0, 0},
+ {0x3403, 0x42, 0, 0}, {0x3088, 0x02, 0, 0}, {0x3089, 0x80, 0, 0},
+ {0x308a, 0x01, 0, 0}, {0x308b, 0xe0, 0, 0}, {0x3362, 0x12, 0, 0},
+ {0x3363, 0x88, 0, 0}, {0x3364, 0xe4, 0, 0}, {0x3403, 0x42, 0, 0},
+ {0x3088, 0x02, 0, 0}, {0x3089, 0x80, 0, 0}, {0x308a, 0x01, 0, 0},
+ {0x308b, 0xe0, 0, 0}, {0x300e, 0x37, 0, 0}, {0x300f, 0xe1, 0, 0},
+ {0x3010, 0x22, 0, 0}, {0x3011, 0x01, 0, 0}, {0x304c, 0x84, 0, 0},
+ {0x3014, 0x04, 0, 0}, {0x3015, 0x02, 0, 0}, {0x302e, 0x00, 0, 0},
+ {0x302d, 0x00, 0, 0},
+};
+
+static struct reg_value ov3640_setting_15fps_QVGA_320_240[] = {
+ {0x3012, 0x80, 0, 0}, {0x304d, 0x45, 0, 0}, {0x30a7, 0x5e, 0, 0},
+ {0x3087, 0x16, 0, 0}, {0x309c, 0x1a, 0, 0}, {0x30a2, 0xe4, 0, 0},
+ {0x30aa, 0x42, 0, 0}, {0x30b0, 0xff, 0, 0}, {0x30b1, 0xff, 0, 0},
+ {0x30b2, 0x10, 0, 0}, {0x300e, 0x32, 0, 0}, {0x300f, 0x21, 0, 0},
+ {0x3010, 0x20, 0, 0}, {0x3011, 0x00, 0, 0}, {0x304c, 0x81, 0, 0},
+ {0x30d7, 0x10, 0, 0}, {0x30d9, 0x0d, 0, 0}, {0x30db, 0x08, 0, 0},
+ {0x3016, 0x82, 0, 0}, {0x3018, 0x38, 0, 0}, {0x3019, 0x30, 0, 0},
+ {0x301a, 0x61, 0, 0}, {0x307d, 0x00, 0, 0}, {0x3087, 0x02, 0, 0},
+ {0x3082, 0x20, 0, 0}, {0x3015, 0x12, 0, 0}, {0x3014, 0x04, 0, 0},
+ {0x3013, 0xf7, 0, 0}, {0x303c, 0x08, 0, 0}, {0x303d, 0x18, 0, 0},
+ {0x303e, 0x06, 0, 0}, {0x303f, 0x0c, 0, 0}, {0x3030, 0x62, 0, 0},
+ {0x3031, 0x26, 0, 0}, {0x3032, 0xe6, 0, 0}, {0x3033, 0x6e, 0, 0},
+ {0x3034, 0xea, 0, 0}, {0x3035, 0xae, 0, 0}, {0x3036, 0xa6, 0, 0},
+ {0x3037, 0x6a, 0, 0}, {0x3104, 0x02, 0, 0}, {0x3105, 0xfd, 0, 0},
+ {0x3106, 0x00, 0, 0}, {0x3107, 0xff, 0, 0}, {0x3300, 0x12, 0, 0},
+ {0x3301, 0xde, 0, 0}, {0x3302, 0xcf, 0, 0}, {0x3312, 0x26, 0, 0},
+ {0x3314, 0x42, 0, 0}, {0x3313, 0x2b, 0, 0}, {0x3315, 0x42, 0, 0},
+ {0x3310, 0xd0, 0, 0}, {0x3311, 0xbd, 0, 0}, {0x330c, 0x18, 0, 0},
+ {0x330d, 0x18, 0, 0}, {0x330e, 0x56, 0, 0}, {0x330f, 0x5c, 0, 0},
+ {0x330b, 0x1c, 0, 0}, {0x3306, 0x5c, 0, 0}, {0x3307, 0x11, 0, 0},
+ {0x336a, 0x52, 0, 0}, {0x3370, 0x46, 0, 0}, {0x3376, 0x38, 0, 0},
+ {0x30b8, 0x20, 0, 0}, {0x30b9, 0x17, 0, 0}, {0x30ba, 0x04, 0, 0},
+ {0x30bb, 0x08, 0, 0}, {0x3507, 0x06, 0, 0}, {0x350a, 0x4f, 0, 0},
+ {0x3100, 0x02, 0, 0}, {0x3301, 0xde, 0, 0}, {0x3304, 0x00, 0, 0},
+ {0x3400, 0x00, 0, 0}, {0x3404, 0x42, 0, 0}, {0x3600, 0xc4, 0, 0},
+ {0x3302, 0xef, 0, 0}, {0x3020, 0x01, 0, 0}, {0x3021, 0x1d, 0, 0},
+ {0x3022, 0x00, 0, 0}, {0x3023, 0x0a, 0, 0}, {0x3024, 0x08, 0, 0},
+ {0x3025, 0x00, 0, 0}, {0x3026, 0x06, 0, 0}, {0x3027, 0x00, 0, 0},
+ {0x335f, 0x68, 0, 0}, {0x3360, 0x00, 0, 0}, {0x3361, 0x00, 0, 0},
+ {0x3362, 0x01, 0, 0}, {0x3363, 0x40, 0, 0}, {0x3364, 0xf0, 0, 0},
+ {0x3403, 0x00, 0, 0}, {0x3088, 0x01, 0, 0}, {0x3089, 0x40, 0, 0},
+ {0x308a, 0x00, 0, 0}, {0x308b, 0xf0, 0, 0}, {0x307c, 0x10, 0, 0},
+ {0x3090, 0xc0, 0, 0}, {0x304c, 0x84, 0, 0}, {0x308d, 0x04, 0, 0},
+ {0x3086, 0x03, 0, 0}, {0x3086, 0x00, 0, 0}, {0x3011, 0x01, 0, 0},
+};
+
+static struct reg_value ov3640_setting_30fps_QVGA_320_240[] = {
+ {0x3012, 0x80, 0, 0}, {0x304d, 0x45, 0, 0}, {0x30a7, 0x5e, 0, 0},
+ {0x3087, 0x16, 0, 0}, {0x309c, 0x1a, 0, 0}, {0x30a2, 0xe4, 0, 0},
+ {0x30aa, 0x42, 0, 0}, {0x30b0, 0xff, 0, 0}, {0x30b1, 0xff, 0, 0},
+ {0x30b2, 0x10, 0, 0}, {0x300e, 0x32, 0, 0}, {0x300f, 0x21, 0, 0},
+ {0x3010, 0x20, 0, 0}, {0x3011, 0x01, 0, 0}, {0x304c, 0x82, 0, 0},
+ {0x30d7, 0x10, 0, 0}, {0x30d9, 0x0d, 0, 0}, {0x30db, 0x08, 0, 0},
+ {0x3016, 0x82, 0, 0}, {0x3018, 0x38, 0, 0}, {0x3019, 0x30, 0, 0},
+ {0x301a, 0x61, 0, 0}, {0x307d, 0x00, 0, 0}, {0x3087, 0x02, 0, 0},
+ {0x3082, 0x20, 0, 0}, {0x3015, 0x12, 0, 0}, {0x3014, 0x0c, 0, 0},
+ {0x3013, 0xf7, 0, 0}, {0x303c, 0x08, 0, 0}, {0x303d, 0x18, 0, 0},
+ {0x303e, 0x06, 0, 0}, {0x303f, 0x0c, 0, 0}, {0x3030, 0x62, 0, 0},
+ {0x3031, 0x26, 0, 0}, {0x3032, 0xe6, 0, 0}, {0x3033, 0x6e, 0, 0},
+ {0x3034, 0xea, 0, 0}, {0x3035, 0xae, 0, 0}, {0x3036, 0xa6, 0, 0},
+ {0x3037, 0x6a, 0, 0}, {0x3104, 0x02, 0, 0}, {0x3105, 0xfd, 0, 0},
+ {0x3106, 0x00, 0, 0}, {0x3107, 0xff, 0, 0}, {0x3300, 0x12, 0, 0},
+ {0x3301, 0xde, 0, 0}, {0x3302, 0xcf, 0, 0}, {0x3312, 0x26, 0, 0},
+ {0x3314, 0x42, 0, 0}, {0x3313, 0x2b, 0, 0}, {0x3315, 0x42, 0, 0},
+ {0x3310, 0xd0, 0, 0}, {0x3311, 0xbd, 0, 0}, {0x330c, 0x18, 0, 0},
+ {0x330d, 0x18, 0, 0}, {0x330e, 0x56, 0, 0}, {0x330f, 0x5c, 0, 0},
+ {0x330b, 0x1c, 0, 0}, {0x3306, 0x5c, 0, 0}, {0x3307, 0x11, 0, 0},
+ {0x336a, 0x52, 0, 0}, {0x3370, 0x46, 0, 0}, {0x3376, 0x38, 0, 0},
+ {0x3300, 0x13, 0, 0}, {0x30b8, 0x20, 0, 0}, {0x30b9, 0x17, 0, 0},
+ {0x30ba, 0x04, 0, 0}, {0x30bb, 0x08, 0, 0}, {0x3100, 0x02, 0, 0},
+ {0x3301, 0x10, 0x30, 0}, {0x3304, 0x00, 0x03, 0}, {0x3400, 0x00, 0, 0},
+ {0x3404, 0x02, 0, 0}, {0x3600, 0xc0, 0, 0}, {0x308d, 0x04, 0, 0},
+ {0x3086, 0x03, 0, 0}, {0x3086, 0x00, 0, 0}, {0x3012, 0x10, 0, 0},
+ {0x3023, 0x06, 0, 0}, {0x3026, 0x03, 0, 0}, {0x3027, 0x04, 0, 0},
+ {0x302a, 0x03, 0, 0}, {0x302b, 0x10, 0, 0}, {0x3075, 0x24, 0, 0},
+ {0x300d, 0x01, 0, 0}, {0x30d7, 0x80, 0x80, 0}, {0x3069, 0x00, 0x40, 0},
+ {0x303e, 0x00, 0, 0}, {0x303f, 0xc0, 0, 0}, {0x3302, 0x20, 0x20, 0},
+ {0x335f, 0x34, 0, 0}, {0x3360, 0x0c, 0, 0}, {0x3361, 0x04, 0, 0},
+ {0x3362, 0x34, 0, 0}, {0x3363, 0x08, 0, 0}, {0x3364, 0x04, 0, 0},
+ {0x3403, 0x42, 0, 0}, {0x3088, 0x04, 0, 0}, {0x3089, 0x00, 0, 0},
+ {0x308a, 0x03, 0, 0}, {0x308b, 0x00, 0, 0}, {0x3362, 0x12, 0, 0},
+ {0x3363, 0x88, 0, 0}, {0x3364, 0xe4, 0, 0}, {0x3403, 0x42, 0, 0},
+ {0x3088, 0x02, 0, 0}, {0x3089, 0x80, 0, 0}, {0x308a, 0x01, 0, 0},
+ {0x308b, 0xe0, 0, 0}, {0x300e, 0x37, 0, 0}, {0x300f, 0xe1, 0, 0},
+ {0x3010, 0x22, 0, 0}, {0x3011, 0x01, 0, 0}, {0x304c, 0x84, 0, 0},
+};
+
+static struct reg_value ov3640_setting_15fps_NTSC_720_480[] = {
+ {0x3012, 0x80, 0, 0}, {0x304d, 0x45, 0, 0}, {0x30a7, 0x5e, 0, 0},
+ {0x3087, 0x16, 0, 0}, {0x309C, 0x1a, 0, 0}, {0x30a2, 0xe4, 0, 0},
+ {0x30aa, 0x42, 0, 0}, {0x30b0, 0xff, 0, 0}, {0x30b1, 0xff, 0, 0},
+ {0x30b2, 0x10, 0, 0}, {0x300e, 0x39, 0, 0}, {0x300f, 0x21, 0, 0},
+ {0x3010, 0x20, 0, 0}, {0x304c, 0x81, 0, 0}, {0x30d7, 0x10, 0, 0},
+ {0x30d9, 0x0d, 0, 0}, {0x30db, 0x08, 0, 0}, {0x3016, 0x82, 0, 0},
+ {0x3018, 0x48, 0, 0}, {0x3019, 0x40, 0, 0}, {0x301a, 0x82, 0, 0},
+ {0x307d, 0x00, 0, 0}, {0x3087, 0x02, 0, 0}, {0x3082, 0x20, 0, 0},
+ {0x3015, 0x12, 0, 0}, {0x3014, 0x84, 0, 0}, {0x3013, 0xf7, 0, 0},
+ {0x303c, 0x08, 0, 0}, {0x303d, 0x18, 0, 0}, {0x303e, 0x06, 0, 0},
+ {0x303F, 0x0c, 0, 0}, {0x3030, 0x62, 0, 0}, {0x3031, 0x26, 0, 0},
+ {0x3032, 0xe6, 0, 0}, {0x3033, 0x6e, 0, 0}, {0x3034, 0xea, 0, 0},
+ {0x3035, 0xae, 0, 0}, {0x3036, 0xa6, 0, 0}, {0x3037, 0x6a, 0, 0},
+ {0x3104, 0x02, 0, 0}, {0x3105, 0xfd, 0, 0}, {0x3106, 0x00, 0, 0},
+ {0x3107, 0xff, 0, 0}, {0x3300, 0x13, 0, 0}, {0x3301, 0xde, 0, 0},
+ {0x3302, 0xcf, 0, 0}, {0x3312, 0x26, 0, 0}, {0x3314, 0x42, 0, 0},
+ {0x3313, 0x2b, 0, 0}, {0x3315, 0x42, 0, 0}, {0x3310, 0xd0, 0, 0},
+ {0x3311, 0xbd, 0, 0}, {0x330c, 0x18, 0, 0}, {0x330d, 0x18, 0, 0},
+ {0x330e, 0x56, 0, 0}, {0x330f, 0x5c, 0, 0}, {0x330b, 0x1c, 0, 0},
+ {0x3306, 0x5c, 0, 0}, {0x3307, 0x11, 0, 0}, {0x336a, 0x52, 0, 0},
+ {0x3370, 0x46, 0, 0}, {0x3376, 0x38, 0, 0}, {0x30b8, 0x20, 0, 0},
+ {0x30b9, 0x17, 0, 0}, {0x30ba, 0x04, 0, 0}, {0x30bb, 0x08, 0, 0},
+ {0x3507, 0x06, 0, 0}, {0x350a, 0x4f, 0, 0}, {0x3100, 0x02, 0, 0},
+ {0x3301, 0xde, 0, 0}, {0x3304, 0xfc, 0, 0}, {0x3400, 0x00, 0, 0},
+ {0x3404, 0x00, 0, 0}, {0x3600, 0xc0, 0, 0}, {0x3088, 0x08, 0, 0},
+ {0x3089, 0x00, 0, 0}, {0x308a, 0x06, 0, 0}, {0x308b, 0x00, 0, 0},
+ {0x308d, 0x04, 0, 0}, {0x3086, 0x03, 0, 0}, {0x3086, 0x00, 0, 0},
+ {0x30a9, 0xb5, 0, 0}, {0x3317, 0x04, 0, 0}, {0x3316, 0xf8, 0, 0},
+ {0x3312, 0x17, 0, 0}, {0x3314, 0x30, 0, 0}, {0x3313, 0x23, 0, 0},
+ {0x3315, 0x3e, 0, 0}, {0x3311, 0x9e, 0, 0}, {0x3310, 0xc0, 0, 0},
+ {0x330c, 0x18, 0, 0}, {0x330d, 0x18, 0, 0}, {0x330e, 0x5e, 0, 0},
+ {0x330f, 0x6c, 0, 0}, {0x330b, 0x1c, 0, 0}, {0x3306, 0x5c, 0, 0},
+ {0x3307, 0x11, 0, 0}, {0x3308, 0x25, 0, 0}, {0x3340, 0x20, 0, 0},
+ {0x3341, 0x50, 0, 0}, {0x3342, 0x18, 0, 0}, {0x3343, 0x23, 0, 0},
+ {0x3344, 0xad, 0, 0}, {0x3345, 0xd0, 0, 0}, {0x3346, 0xb8, 0, 0},
+ {0x3347, 0xb4, 0, 0}, {0x3348, 0x04, 0, 0}, {0x3349, 0x98, 0, 0},
+ {0x3355, 0x02, 0, 0}, {0x3358, 0x44, 0, 0}, {0x3359, 0x44, 0, 0},
+ {0x3300, 0x13, 0, 0}, {0x3367, 0x23, 0, 0}, {0x3368, 0xBB, 0, 0},
+ {0x3369, 0xD6, 0, 0}, {0x336A, 0x2A, 0, 0}, {0x336B, 0x07, 0, 0},
+ {0x336C, 0x00, 0, 0}, {0x336D, 0x23, 0, 0}, {0x336E, 0xC3, 0, 0},
+ {0x336F, 0xDE, 0, 0}, {0x3370, 0x2b, 0, 0}, {0x3371, 0x07, 0, 0},
+ {0x3372, 0x00, 0, 0}, {0x3373, 0x23, 0, 0}, {0x3374, 0x9e, 0, 0},
+ {0x3375, 0xD6, 0, 0}, {0x3376, 0x29, 0, 0}, {0x3377, 0x07, 0, 0},
+ {0x3378, 0x00, 0, 0}, {0x332a, 0x1d, 0, 0}, {0x331b, 0x08, 0, 0},
+ {0x331c, 0x16, 0, 0}, {0x331d, 0x2d, 0, 0}, {0x331e, 0x54, 0, 0},
+ {0x331f, 0x66, 0, 0}, {0x3320, 0x73, 0, 0}, {0x3321, 0x80, 0, 0},
+ {0x3322, 0x8c, 0, 0}, {0x3323, 0x95, 0, 0}, {0x3324, 0x9d, 0, 0},
+ {0x3325, 0xac, 0, 0}, {0x3326, 0xb8, 0, 0}, {0x3327, 0xcc, 0, 0},
+ {0x3328, 0xdd, 0, 0}, {0x3329, 0xee, 0, 0}, {0x332e, 0x04, 0, 0},
+ {0x332f, 0x04, 0, 0}, {0x3331, 0x02, 0, 0}, {0x3100, 0x02, 0, 0},
+ {0x3301, 0xde, 0, 0}, {0x3304, 0xfc, 0, 0}, {0x3400, 0x00, 0, 0},
+ {0x3404, 0x00, 0, 0}, {0x3610, 0x40, 0, 0}, {0x304c, 0x81, 0, 0},
+ {0x307C, 0x10, 0, 0}, {0x3012, 0x10, 0, 0}, {0x3023, 0x06, 0, 0},
+ {0x3026, 0x03, 0, 0}, {0x3027, 0x04, 0, 0}, {0x302a, 0x03, 0, 0},
+ {0x302b, 0x10, 0, 0}, {0x3075, 0x24, 0, 0}, {0x300d, 0x01, 0, 0},
+ {0x30d7, 0x90, 0, 0}, {0x3069, 0x04, 0, 0}, {0x303e, 0x00, 0, 0},
+ {0x303f, 0xc0, 0, 0}, {0x3302, 0xef, 0, 0}, {0x335f, 0x34, 0, 0},
+ {0x3360, 0x0c, 0, 0}, {0x3361, 0x04, 0, 0}, {0x3362, 0x34, 0, 0},
+ {0x3363, 0x08, 0, 0}, {0x3364, 0x04, 0, 0}, {0x3403, 0x42, 0, 0},
+ {0x3088, 0x04, 0, 0}, {0x3089, 0x00, 0, 0}, {0x308a, 0x03, 0, 0},
+ {0x308b, 0x00, 0, 0}, {0x300e, 0x32, 0, 0}, {0x300f, 0x21, 0, 0},
+ {0x3010, 0x20, 0, 0}, {0x3011, 0x01, 0, 0}, {0x304c, 0x82, 0, 0},
+ {0x3302, 0xef, 0, 0}, {0x335f, 0x34, 0, 0}, {0x3360, 0x0c, 0, 0},
+ {0x3361, 0x04, 0, 0}, {0x3362, 0x23, 0, 0}, {0x3363, 0x28, 0, 0},
+ {0x3364, 0x5c, 0, 0}, {0x3403, 0x42, 0, 0}, {0x3088, 0x02, 0, 0},
+ {0x3089, 0xD0, 0, 0}, {0x308a, 0x01, 0, 0}, {0x308b, 0xe0, 0, 0},
+ {0x304c, 0x83, 0, 0}, {0x300e, 0x39, 0, 0}, {0x300f, 0x21, 0, 0},
+ {0x3011, 0x00, 0, 0}, {0x3010, 0x81, 0, 0}, {0x302e, 0x00, 0, 0},
+ {0x302d, 0x00, 0, 0}, {0x3071, 0xeb, 0, 0}, {0x301C, 0x02, 0, 0},
+ {0x3404, 0x02, 0, 0},
+};
+
+static struct reg_value ov3640_setting_15fps_PAL_720_576[] = {
+ {0x3012, 0x80, 0, 0}, {0x304d, 0x45, 0, 0}, {0x30a7, 0x5e, 0, 0},
+ {0x3087, 0x16, 0, 0}, {0x309C, 0x1a, 0, 0}, {0x30a2, 0xe4, 0, 0},
+ {0x30aa, 0x42, 0, 0}, {0x30b0, 0xff, 0, 0}, {0x30b1, 0xff, 0, 0},
+ {0x30b2, 0x10, 0, 0}, {0x300e, 0x39, 0, 0}, {0x300f, 0x21, 0, 0},
+ {0x3010, 0x20, 0, 0}, {0x304c, 0x81, 0, 0}, {0x30d7, 0x10, 0, 0},
+ {0x30d9, 0x0d, 0, 0}, {0x30db, 0x08, 0, 0}, {0x3016, 0x82, 0, 0},
+ {0x3018, 0x48, 0, 0}, {0x3019, 0x40, 0, 0}, {0x301a, 0x82, 0, 0},
+ {0x307d, 0x00, 0, 0}, {0x3087, 0x02, 0, 0}, {0x3082, 0x20, 0, 0},
+ {0x3015, 0x12, 0, 0}, {0x3014, 0x84, 0, 0}, {0x3013, 0xf7, 0, 0},
+ {0x303c, 0x08, 0, 0}, {0x303d, 0x18, 0, 0}, {0x303e, 0x06, 0, 0},
+ {0x303F, 0x0c, 0, 0}, {0x3030, 0x62, 0, 0}, {0x3031, 0x26, 0, 0},
+ {0x3032, 0xe6, 0, 0}, {0x3033, 0x6e, 0, 0}, {0x3034, 0xea, 0, 0},
+ {0x3035, 0xae, 0, 0}, {0x3036, 0xa6, 0, 0}, {0x3037, 0x6a, 0, 0},
+ {0x3104, 0x02, 0, 0}, {0x3105, 0xfd, 0, 0}, {0x3106, 0x00, 0, 0},
+ {0x3107, 0xff, 0, 0}, {0x3300, 0x13, 0, 0}, {0x3301, 0xde, 0, 0},
+ {0x3302, 0xcf, 0, 0}, {0x3312, 0x26, 0, 0}, {0x3314, 0x42, 0, 0},
+ {0x3313, 0x2b, 0, 0}, {0x3315, 0x42, 0, 0}, {0x3310, 0xd0, 0, 0},
+ {0x3311, 0xbd, 0, 0}, {0x330c, 0x18, 0, 0}, {0x330d, 0x18, 0, 0},
+ {0x330e, 0x56, 0, 0}, {0x330f, 0x5c, 0, 0}, {0x330b, 0x1c, 0, 0},
+ {0x3306, 0x5c, 0, 0}, {0x3307, 0x11, 0, 0}, {0x336a, 0x52, 0, 0},
+ {0x3370, 0x46, 0, 0}, {0x3376, 0x38, 0, 0}, {0x30b8, 0x20, 0, 0},
+ {0x30b9, 0x17, 0, 0}, {0x30ba, 0x04, 0, 0}, {0x30bb, 0x08, 0, 0},
+ {0x3507, 0x06, 0, 0}, {0x350a, 0x4f, 0, 0}, {0x3100, 0x02, 0, 0},
+ {0x3301, 0xde, 0, 0}, {0x3304, 0xfc, 0, 0}, {0x3400, 0x00, 0, 0},
+ {0x3404, 0x00, 0, 0}, {0x3600, 0xc0, 0, 0}, {0x3088, 0x08, 0, 0},
+ {0x3089, 0x00, 0, 0}, {0x308a, 0x06, 0, 0}, {0x308b, 0x00, 0, 0},
+ {0x308d, 0x04, 0, 0}, {0x3086, 0x03, 0, 0}, {0x3086, 0x00, 0, 0},
+ {0x30a9, 0xb5, 0, 0}, {0x3317, 0x04, 0, 0}, {0x3316, 0xf8, 0, 0},
+ {0x3312, 0x17, 0, 0}, {0x3314, 0x30, 0, 0}, {0x3313, 0x23, 0, 0},
+ {0x3315, 0x3e, 0, 0}, {0x3311, 0x9e, 0, 0}, {0x3310, 0xc0, 0, 0},
+ {0x330c, 0x18, 0, 0}, {0x330d, 0x18, 0, 0}, {0x330e, 0x5e, 0, 0},
+ {0x330f, 0x6c, 0, 0}, {0x330b, 0x1c, 0, 0}, {0x3306, 0x5c, 0, 0},
+ {0x3307, 0x11, 0, 0}, {0x3308, 0x25, 0, 0}, {0x3340, 0x20, 0, 0},
+ {0x3341, 0x50, 0, 0}, {0x3342, 0x18, 0, 0}, {0x3343, 0x23, 0, 0},
+ {0x3344, 0xad, 0, 0}, {0x3345, 0xd0, 0, 0}, {0x3346, 0xb8, 0, 0},
+ {0x3347, 0xb4, 0, 0}, {0x3348, 0x04, 0, 0}, {0x3349, 0x98, 0, 0},
+ {0x3355, 0x02, 0, 0}, {0x3358, 0x44, 0, 0}, {0x3359, 0x44, 0, 0},
+ {0x3300, 0x13, 0, 0}, {0x3367, 0x23, 0, 0}, {0x3368, 0xBB, 0, 0},
+ {0x3369, 0xD6, 0, 0}, {0x336A, 0x2A, 0, 0}, {0x336B, 0x07, 0, 0},
+ {0x336C, 0x00, 0, 0}, {0x336D, 0x23, 0, 0}, {0x336E, 0xC3, 0, 0},
+ {0x336F, 0xDE, 0, 0}, {0x3370, 0x2b, 0, 0}, {0x3371, 0x07, 0, 0},
+ {0x3372, 0x00, 0, 0}, {0x3373, 0x23, 0, 0}, {0x3374, 0x9e, 0, 0},
+ {0x3375, 0xD6, 0, 0}, {0x3376, 0x29, 0, 0}, {0x3377, 0x07, 0, 0},
+ {0x3378, 0x00, 0, 0}, {0x332a, 0x1d, 0, 0}, {0x331b, 0x08, 0, 0},
+ {0x331c, 0x16, 0, 0}, {0x331d, 0x2d, 0, 0}, {0x331e, 0x54, 0, 0},
+ {0x331f, 0x66, 0, 0}, {0x3320, 0x73, 0, 0}, {0x3321, 0x80, 0, 0},
+ {0x3322, 0x8c, 0, 0}, {0x3323, 0x95, 0, 0}, {0x3324, 0x9d, 0, 0},
+ {0x3325, 0xac, 0, 0}, {0x3326, 0xb8, 0, 0}, {0x3327, 0xcc, 0, 0},
+ {0x3328, 0xdd, 0, 0}, {0x3329, 0xee, 0, 0}, {0x332e, 0x04, 0, 0},
+ {0x332f, 0x04, 0, 0}, {0x3331, 0x02, 0, 0}, {0x3100, 0x02, 0, 0},
+ {0x3301, 0xde, 0, 0}, {0x3304, 0xfc, 0, 0}, {0x3400, 0x00, 0, 0},
+ {0x3404, 0x00, 0, 0}, {0x3610, 0x40, 0, 0}, {0x304c, 0x81, 0, 0},
+ {0x307C, 0x10, 0, 0}, {0x3012, 0x10, 0, 0}, {0x3023, 0x06, 0, 0},
+ {0x3026, 0x03, 0, 0}, {0x3027, 0x04, 0, 0}, {0x302a, 0x03, 0, 0},
+ {0x302b, 0x10, 0, 0}, {0x3075, 0x24, 0, 0}, {0x300d, 0x01, 0, 0},
+ {0x30d7, 0x90, 0, 0}, {0x3069, 0x04, 0, 0}, {0x303e, 0x00, 0, 0},
+ {0x303f, 0xc0, 0, 0}, {0x3302, 0xef, 0, 0}, {0x335f, 0x34, 0, 0},
+ {0x3360, 0x0c, 0, 0}, {0x3361, 0x04, 0, 0}, {0x3362, 0x34, 0, 0},
+ {0x3363, 0x08, 0, 0}, {0x3364, 0x04, 0, 0}, {0x3403, 0x42, 0, 0},
+ {0x3088, 0x04, 0, 0}, {0x3089, 0x00, 0, 0}, {0x308a, 0x03, 0, 0},
+ {0x308b, 0x00, 0, 0}, {0x300e, 0x32, 0, 0}, {0x300f, 0x21, 0, 0},
+ {0x3010, 0x20, 0, 0}, {0x3011, 0x01, 0, 0}, {0x304c, 0x82, 0, 0},
+ {0x3302, 0xef, 0, 0}, {0x335f, 0x34, 0, 0}, {0x3360, 0x0c, 0, 0},
+ {0x3361, 0x04, 0, 0}, {0x3362, 0x23, 0, 0}, {0x3363, 0x28, 0, 0},
+ {0x3364, 0x5c, 0, 0}, {0x3403, 0x42, 0, 0}, {0x3088, 0x02, 0, 0},
+ {0x3089, 0xD0, 0, 0}, {0x308a, 0x02, 0, 0}, {0x308b, 0x40, 0, 0},
+ {0x304c, 0x83, 0, 0}, {0x300e, 0x39, 0, 0}, {0x300f, 0x21, 0, 0},
+ {0x3011, 0x00, 0, 0}, {0x3010, 0x81, 0, 0}, {0x302e, 0x00, 0, 0},
+ {0x302d, 0x00, 0, 0}, {0x3071, 0xeb, 0, 0}, {0x301C, 0x02, 0, 0},
+ {0x3404, 0x02, 0, 0},
+};
+
+static struct reg_value ov3640_setting_30fps_NTSC_720_480[] = {
+ {0x3012, 0x80, 0, 0}, {0x304d, 0x45, 0, 0}, {0x30a7, 0x5e, 0, 0},
+ {0x3087, 0x16, 0, 0}, {0x309C, 0x1a, 0, 0}, {0x30a2, 0xe4, 0, 0},
+ {0x30aa, 0x42, 0, 0}, {0x30b0, 0xff, 0, 0}, {0x30b1, 0xff, 0, 0},
+ {0x30b2, 0x10, 0, 0}, {0x300e, 0x39, 0, 0}, {0x300f, 0x21, 0, 0},
+ {0x3010, 0x20, 0, 0}, {0x304c, 0x81, 0, 0}, {0x30d7, 0x10, 0, 0},
+ {0x30d9, 0x0d, 0, 0}, {0x30db, 0x08, 0, 0}, {0x3016, 0x82, 0, 0},
+ {0x3018, 0x48, 0, 0}, {0x3019, 0x40, 0, 0}, {0x301a, 0x82, 0, 0},
+ {0x307d, 0x00, 0, 0}, {0x3087, 0x02, 0, 0}, {0x3082, 0x20, 0, 0},
+ {0x3015, 0x12, 0, 0}, {0x3014, 0x84, 0, 0}, {0x3013, 0xf7, 0, 0},
+ {0x303c, 0x08, 0, 0}, {0x303d, 0x18, 0, 0}, {0x303e, 0x06, 0, 0},
+ {0x303F, 0x0c, 0, 0}, {0x3030, 0x62, 0, 0}, {0x3031, 0x26, 0, 0},
+ {0x3032, 0xe6, 0, 0}, {0x3033, 0x6e, 0, 0}, {0x3034, 0xea, 0, 0},
+ {0x3035, 0xae, 0, 0}, {0x3036, 0xa6, 0, 0}, {0x3037, 0x6a, 0, 0},
+ {0x3104, 0x02, 0, 0}, {0x3105, 0xfd, 0, 0}, {0x3106, 0x00, 0, 0},
+ {0x3107, 0xff, 0, 0}, {0x3300, 0x13, 0, 0}, {0x3301, 0xde, 0, 0},
+ {0x3302, 0xcf, 0, 0}, {0x3312, 0x26, 0, 0}, {0x3314, 0x42, 0, 0},
+ {0x3313, 0x2b, 0, 0}, {0x3315, 0x42, 0, 0}, {0x3310, 0xd0, 0, 0},
+ {0x3311, 0xbd, 0, 0}, {0x330c, 0x18, 0, 0}, {0x330d, 0x18, 0, 0},
+ {0x330e, 0x56, 0, 0}, {0x330f, 0x5c, 0, 0}, {0x330b, 0x1c, 0, 0},
+ {0x3306, 0x5c, 0, 0}, {0x3307, 0x11, 0, 0}, {0x336a, 0x52, 0, 0},
+ {0x3370, 0x46, 0, 0}, {0x3376, 0x38, 0, 0}, {0x30b8, 0x20, 0, 0},
+ {0x30b9, 0x17, 0, 0}, {0x30ba, 0x04, 0, 0}, {0x30bb, 0x08, 0, 0},
+ {0x3507, 0x06, 0, 0}, {0x350a, 0x4f, 0, 0}, {0x3100, 0x02, 0, 0},
+ {0x3301, 0xde, 0, 0}, {0x3304, 0xfc, 0, 0}, {0x3400, 0x00, 0, 0},
+ {0x3404, 0x00, 0, 0}, {0x3600, 0xc0, 0, 0}, {0x3088, 0x08, 0, 0},
+ {0x3089, 0x00, 0, 0}, {0x308a, 0x06, 0, 0}, {0x308b, 0x00, 0, 0},
+ {0x308d, 0x04, 0, 0}, {0x3086, 0x03, 0, 0}, {0x3086, 0x00, 0, 0},
+ {0x30a9, 0xb5, 0, 0}, {0x3317, 0x04, 0, 0}, {0x3316, 0xf8, 0, 0},
+ {0x3312, 0x17, 0, 0}, {0x3314, 0x30, 0, 0}, {0x3313, 0x23, 0, 0},
+ {0x3315, 0x3e, 0, 0}, {0x3311, 0x9e, 0, 0}, {0x3310, 0xc0, 0, 0},
+ {0x330c, 0x18, 0, 0}, {0x330d, 0x18, 0, 0}, {0x330e, 0x5e, 0, 0},
+ {0x330f, 0x6c, 0, 0}, {0x330b, 0x1c, 0, 0}, {0x3306, 0x5c, 0, 0},
+ {0x3307, 0x11, 0, 0}, {0x3308, 0x25, 0, 0}, {0x3340, 0x20, 0, 0},
+ {0x3341, 0x50, 0, 0}, {0x3342, 0x18, 0, 0}, {0x3343, 0x23, 0, 0},
+ {0x3344, 0xad, 0, 0}, {0x3345, 0xd0, 0, 0}, {0x3346, 0xb8, 0, 0},
+ {0x3347, 0xb4, 0, 0}, {0x3348, 0x04, 0, 0}, {0x3349, 0x98, 0, 0},
+ {0x3355, 0x02, 0, 0}, {0x3358, 0x44, 0, 0}, {0x3359, 0x44, 0, 0},
+ {0x3300, 0x13, 0, 0}, {0x3367, 0x23, 0, 0}, {0x3368, 0xBB, 0, 0},
+ {0x3369, 0xD6, 0, 0}, {0x336A, 0x2A, 0, 0}, {0x336B, 0x07, 0, 0},
+ {0x336C, 0x00, 0, 0}, {0x336D, 0x23, 0, 0}, {0x336E, 0xC3, 0, 0},
+ {0x336F, 0xDE, 0, 0}, {0x3370, 0x2b, 0, 0}, {0x3371, 0x07, 0, 0},
+ {0x3372, 0x00, 0, 0}, {0x3373, 0x23, 0, 0}, {0x3374, 0x9e, 0, 0},
+ {0x3375, 0xD6, 0, 0}, {0x3376, 0x29, 0, 0}, {0x3377, 0x07, 0, 0},
+ {0x3378, 0x00, 0, 0}, {0x332a, 0x1d, 0, 0}, {0x331b, 0x08, 0, 0},
+ {0x331c, 0x16, 0, 0}, {0x331d, 0x2d, 0, 0}, {0x331e, 0x54, 0, 0},
+ {0x331f, 0x66, 0, 0}, {0x3320, 0x73, 0, 0}, {0x3321, 0x80, 0, 0},
+ {0x3322, 0x8c, 0, 0}, {0x3323, 0x95, 0, 0}, {0x3324, 0x9d, 0, 0},
+ {0x3325, 0xac, 0, 0}, {0x3326, 0xb8, 0, 0}, {0x3327, 0xcc, 0, 0},
+ {0x3328, 0xdd, 0, 0}, {0x3329, 0xee, 0, 0}, {0x332e, 0x04, 0, 0},
+ {0x332f, 0x04, 0, 0}, {0x3331, 0x02, 0, 0}, {0x3100, 0x02, 0, 0},
+ {0x3301, 0xde, 0, 0}, {0x3304, 0xfc, 0, 0}, {0x3400, 0x00, 0, 0},
+ {0x3404, 0x00, 0, 0}, {0x3610, 0x40, 0, 0}, {0x304c, 0x81, 0, 0},
+ {0x307C, 0x10, 0, 0}, {0x3012, 0x10, 0, 0}, {0x3023, 0x06, 0, 0},
+ {0x3026, 0x03, 0, 0}, {0x3027, 0x04, 0, 0}, {0x302a, 0x03, 0, 0},
+ {0x302b, 0x10, 0, 0}, {0x3075, 0x24, 0, 0}, {0x300d, 0x01, 0, 0},
+ {0x30d7, 0x90, 0, 0}, {0x3069, 0x04, 0, 0}, {0x303e, 0x00, 0, 0},
+ {0x303f, 0xc0, 0, 0}, {0x3302, 0xef, 0, 0}, {0x335f, 0x34, 0, 0},
+ {0x3360, 0x0c, 0, 0}, {0x3361, 0x04, 0, 0}, {0x3362, 0x34, 0, 0},
+ {0x3363, 0x08, 0, 0}, {0x3364, 0x04, 0, 0}, {0x3403, 0x42, 0, 0},
+ {0x3088, 0x04, 0, 0}, {0x3089, 0x00, 0, 0}, {0x308a, 0x03, 0, 0},
+ {0x308b, 0x00, 0, 0}, {0x300e, 0x32, 0, 0}, {0x300f, 0x21, 0, 0},
+ {0x3010, 0x20, 0, 0}, {0x3011, 0x01, 0, 0}, {0x304c, 0x82, 0, 0},
+ {0x3302, 0xef, 0, 0}, {0x335f, 0x34, 0, 0}, {0x3360, 0x0c, 0, 0},
+ {0x3361, 0x04, 0, 0}, {0x3362, 0x23, 0, 0}, {0x3363, 0x28, 0, 0},
+ {0x3364, 0x5c, 0, 0}, {0x3403, 0x42, 0, 0}, {0x3088, 0x02, 0, 0},
+ {0x3089, 0xD0, 0, 0}, {0x308a, 0x01, 0, 0}, {0x308b, 0xe0, 0, 0},
+ {0x304c, 0x83, 0, 0}, {0x300e, 0x39, 0, 0}, {0x300f, 0xA1, 0, 0},
+ {0x3011, 0x00, 0, 0}, {0x3010, 0x81, 0, 0}, {0x3014, 0x84, 0, 0},
+ {0x302e, 0x00, 0, 0}, {0x302d, 0x00, 0, 0}, {0x3071, 0xeb, 0, 0},
+ {0x301C, 0x02, 0, 0}, {0x3404, 0x02, 0, 0},
+};
+
+static struct reg_value ov3640_setting_30fps_PAL_720_576[] = {
+ {0x3012, 0x80, 0, 0}, {0x304d, 0x45, 0, 0}, {0x30a7, 0x5e, 0, 0},
+ {0x3086, 0x16, 0, 0}, {0x309C, 0x1a, 0, 0}, {0x30a2, 0xe4, 0, 0},
+ {0x30aa, 0x42, 0, 0}, {0x30b0, 0xff, 0, 0}, {0x30b1, 0xff, 0, 0},
+ {0x30b2, 0x10, 0, 0}, {0x300e, 0x39, 0, 0}, {0x300f, 0x21, 0, 0},
+ {0x3010, 0x20, 0, 0}, {0x304c, 0x81, 0, 0}, {0x30d7, 0x10, 0, 0},
+ {0x30d9, 0x0d, 0, 0}, {0x30db, 0x08, 0, 0}, {0x3016, 0x82, 0, 0},
+ {0x3018, 0x48, 0, 0}, {0x3019, 0x40, 0, 0}, {0x301a, 0x82, 0, 0},
+ {0x307d, 0x00, 0, 0}, {0x3087, 0x02, 0, 0}, {0x3082, 0x20, 0, 0},
+ {0x3015, 0x12, 0, 0}, {0x3014, 0x84, 0, 0}, {0x3013, 0xf7, 0, 0},
+ {0x303c, 0x08, 0, 0}, {0x303d, 0x18, 0, 0}, {0x303e, 0x06, 0, 0},
+ {0x303F, 0x0c, 0, 0}, {0x3030, 0x62, 0, 0}, {0x3031, 0x26, 0, 0},
+ {0x3032, 0xe6, 0, 0}, {0x3033, 0x6e, 0, 0}, {0x3034, 0xea, 0, 0},
+ {0x3035, 0xae, 0, 0}, {0x3036, 0xa6, 0, 0}, {0x3037, 0x6a, 0, 0},
+ {0x3104, 0x02, 0, 0}, {0x3105, 0xfd, 0, 0}, {0x3106, 0x00, 0, 0},
+ {0x3107, 0xff, 0, 0}, {0x3300, 0x13, 0, 0}, {0x3301, 0xde, 0, 0},
+ {0x3302, 0xcf, 0, 0}, {0x3312, 0x26, 0, 0}, {0x3314, 0x42, 0, 0},
+ {0x3313, 0x2b, 0, 0}, {0x3315, 0x42, 0, 0}, {0x3310, 0xd0, 0, 0},
+ {0x3311, 0xbd, 0, 0}, {0x330c, 0x18, 0, 0}, {0x330d, 0x18, 0, 0},
+ {0x330e, 0x56, 0, 0}, {0x330f, 0x5c, 0, 0}, {0x330b, 0x1c, 0, 0},
+ {0x3306, 0x5c, 0, 0}, {0x3307, 0x11, 0, 0}, {0x336a, 0x52, 0, 0},
+ {0x3370, 0x46, 0, 0}, {0x3376, 0x38, 0, 0}, {0x30b8, 0x20, 0, 0},
+ {0x30b9, 0x17, 0, 0}, {0x30ba, 0x04, 0, 0}, {0x30bb, 0x08, 0, 0},
+ {0x3507, 0x06, 0, 0}, {0x350a, 0x4f, 0, 0}, {0x3100, 0x02, 0, 0},
+ {0x3301, 0xde, 0, 0}, {0x3304, 0xfc, 0, 0}, {0x3400, 0x00, 0, 0},
+ {0x3404, 0x00, 0, 0}, {0x3600, 0xc0, 0, 0}, {0x3088, 0x08, 0, 0},
+ {0x3089, 0x00, 0, 0}, {0x308a, 0x06, 0, 0}, {0x308b, 0x00, 0, 0},
+ {0x308d, 0x04, 0, 0}, {0x3086, 0x03, 0, 0}, {0x3086, 0x00, 0, 0},
+ {0x30a9, 0xb5, 0, 0}, {0x3317, 0x04, 0, 0}, {0x3316, 0xf8, 0, 0},
+ {0x3312, 0x17, 0, 0}, {0x3314, 0x30, 0, 0}, {0x3313, 0x23, 0, 0},
+ {0x3315, 0x3e, 0, 0}, {0x3311, 0x9e, 0, 0}, {0x3310, 0xc0, 0, 0},
+ {0x330c, 0x18, 0, 0}, {0x330d, 0x18, 0, 0}, {0x330e, 0x5e, 0, 0},
+ {0x330f, 0x6c, 0, 0}, {0x330b, 0x1c, 0, 0}, {0x3306, 0x5c, 0, 0},
+ {0x3307, 0x11, 0, 0}, {0x3308, 0x25, 0, 0}, {0x3340, 0x20, 0, 0},
+ {0x3341, 0x50, 0, 0}, {0x3342, 0x18, 0, 0}, {0x3343, 0x23, 0, 0},
+ {0x3344, 0xad, 0, 0}, {0x3345, 0xd0, 0, 0}, {0x3346, 0xb8, 0, 0},
+ {0x3347, 0xb4, 0, 0}, {0x3348, 0x04, 0, 0}, {0x3349, 0x98, 0, 0},
+ {0x3355, 0x02, 0, 0}, {0x3358, 0x44, 0, 0}, {0x3359, 0x44, 0, 0},
+ {0x3300, 0x13, 0, 0}, {0x3367, 0x23, 0, 0}, {0x3368, 0xBB, 0, 0},
+ {0x3369, 0xD6, 0, 0}, {0x336A, 0x2A, 0, 0}, {0x336B, 0x07, 0, 0},
+ {0x336C, 0x00, 0, 0}, {0x336D, 0x23, 0, 0}, {0x336E, 0xC3, 0, 0},
+ {0x336F, 0xDE, 0, 0}, {0x3370, 0x2b, 0, 0}, {0x3371, 0x07, 0, 0},
+ {0x3372, 0x00, 0, 0}, {0x3373, 0x23, 0, 0}, {0x3374, 0x9e, 0, 0},
+ {0x3375, 0xD6, 0, 0}, {0x3376, 0x29, 0, 0}, {0x3377, 0x07, 0, 0},
+ {0x3378, 0x00, 0, 0}, {0x332a, 0x1d, 0, 0}, {0x331b, 0x08, 0, 0},
+ {0x331c, 0x16, 0, 0}, {0x331d, 0x2d, 0, 0}, {0x331e, 0x54, 0, 0},
+ {0x331f, 0x66, 0, 0}, {0x3320, 0x73, 0, 0}, {0x3321, 0x80, 0, 0},
+ {0x3322, 0x8c, 0, 0}, {0x3323, 0x95, 0, 0}, {0x3324, 0x9d, 0, 0},
+ {0x3325, 0xac, 0, 0}, {0x3326, 0xb8, 0, 0}, {0x3327, 0xcc, 0, 0},
+ {0x3328, 0xdd, 0, 0}, {0x3329, 0xee, 0, 0}, {0x332e, 0x04, 0, 0},
+ {0x332f, 0x04, 0, 0}, {0x3331, 0x02, 0, 0}, {0x3100, 0x02, 0, 0},
+ {0x3301, 0xde, 0, 0}, {0x3304, 0xfc, 0, 0}, {0x3400, 0x00, 0, 0},
+ {0x3404, 0x00, 0, 0}, {0x3610, 0x40, 0, 0}, {0x304c, 0x81, 0, 0},
+ {0x307C, 0x10, 0, 0}, {0x3012, 0x10, 0, 0}, {0x3023, 0x06, 0, 0},
+ {0x3026, 0x03, 0, 0}, {0x3027, 0x04, 0, 0}, {0x302a, 0x03, 0, 0},
+ {0x302b, 0x10, 0, 0}, {0x3075, 0x24, 0, 0}, {0x300d, 0x01, 0, 0},
+ {0x30d7, 0x90, 0, 0}, {0x3069, 0x04, 0, 0}, {0x303e, 0x00, 0, 0},
+ {0x303f, 0xc0, 0, 0}, {0x3302, 0xef, 0, 0}, {0x335f, 0x34, 0, 0},
+ {0x3360, 0x0c, 0, 0}, {0x3361, 0x04, 0, 0}, {0x3362, 0x34, 0, 0},
+ {0x3363, 0x08, 0, 0}, {0x3364, 0x04, 0, 0}, {0x3403, 0x42, 0, 0},
+ {0x3088, 0x04, 0, 0}, {0x3089, 0x00, 0, 0}, {0x308a, 0x03, 0, 0},
+ {0x308b, 0x00, 0, 0}, {0x300e, 0x32, 0, 0}, {0x300f, 0x21, 0, 0},
+ {0x3010, 0x20, 0, 0}, {0x3011, 0x01, 0, 0}, {0x304c, 0x82, 0, 0},
+ {0x3302, 0xef, 0, 0}, {0x335f, 0x34, 0, 0}, {0x3360, 0x0c, 0, 0},
+ {0x3361, 0x04, 0, 0}, {0x3362, 0x23, 0, 0}, {0x3363, 0x28, 0, 0},
+ {0x3364, 0x5c, 0, 0}, {0x3403, 0x42, 0, 0}, {0x3088, 0x02, 0, 0},
+ {0x3089, 0xD0, 0, 0}, {0x308a, 0x02, 0, 0}, {0x308b, 0x40, 0, 0},
+ {0x304c, 0x83, 0, 0}, {0x300e, 0x39, 0, 0}, {0x300f, 0xA1, 0, 0},
+ {0x3011, 0x00, 0, 0}, {0x3010, 0x81, 0, 0}, {0x3014, 0x84, 0, 0},
+ {0x302e, 0x00, 0, 0}, {0x302d, 0x00, 0, 0}, {0x3071, 0xeb, 0, 0},
+ {0x301C, 0x02, 0, 0}, {0x3404, 0x02, 0, 0},
+};
+
+static struct ov3640_mode_info ov3640_mode_info_data[2][ov3640_mode_MAX + 1] = {
+ {
+ {ov3640_mode_VGA_640_480, 640, 480,
+ ov3640_setting_15fps_VGA_640_480,
+ ARRAY_SIZE(ov3640_setting_15fps_VGA_640_480)},
+ {ov3640_mode_QVGA_320_240, 320, 240,
+ ov3640_setting_15fps_QVGA_320_240,
+ ARRAY_SIZE(ov3640_setting_15fps_QVGA_320_240)},
+ {ov3640_mode_XGA_1024_768, 1024, 768,
+ ov3640_setting_15fps_XGA_1024_768,
+ ARRAY_SIZE(ov3640_setting_15fps_XGA_1024_768)},
+ {ov3640_mode_QXGA_2048_1536, 2048, 1536,
+ ov3640_setting_15fps_QXGA_2048_1536,
+ ARRAY_SIZE(ov3640_setting_15fps_QXGA_2048_1536)},
+ {ov3640_mode_NTSC_720_480, 720, 480,
+ ov3640_setting_15fps_NTSC_720_480,
+ ARRAY_SIZE(ov3640_setting_15fps_NTSC_720_480)},
+ {ov3640_mode_PAL_720_576, 720, 576,
+ ov3640_setting_15fps_PAL_720_576,
+ ARRAY_SIZE(ov3640_setting_15fps_PAL_720_576)},
+ },
+ {
+ {ov3640_mode_VGA_640_480, 640, 480,
+ ov3640_setting_30fps_VGA_640_480,
+ ARRAY_SIZE(ov3640_setting_30fps_VGA_640_480)},
+ {ov3640_mode_QVGA_320_240, 320, 240,
+ ov3640_setting_30fps_QVGA_320_240,
+ ARRAY_SIZE(ov3640_setting_30fps_QVGA_320_240)},
+ {ov3640_mode_XGA_1024_768, 1024, 768,
+ ov3640_setting_30fps_XGA_1024_768,
+ ARRAY_SIZE(ov3640_setting_30fps_XGA_1024_768)},
+ {ov3640_mode_QXGA_2048_1536, 0, 0, NULL, 0},
+ {ov3640_mode_NTSC_720_480, 720, 480,
+ ov3640_setting_30fps_NTSC_720_480,
+ ARRAY_SIZE(ov3640_setting_30fps_NTSC_720_480)},
+ {ov3640_mode_PAL_720_576, 720, 576,
+ ov3640_setting_30fps_PAL_720_576,
+ ARRAY_SIZE(ov3640_setting_30fps_PAL_720_576)},
+ },
+};
+
+static struct regulator *io_regulator;
+static struct regulator *core_regulator;
+static struct regulator *analog_regulator;
+static struct regulator *gpo_regulator;
+static struct mxc_camera_platform_data *camera_plat;
+
+static int ov3640_probe(struct i2c_client *adapter,
+ const struct i2c_device_id *device_id);
+static int ov3640_remove(struct i2c_client *client);
+
+static s32 ov3640_read_reg(u16 reg, u8 *val);
+static s32 ov3640_write_reg(u16 reg, u8 val);
+
+static const struct i2c_device_id ov3640_id[] = {
+ {"ov3640", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, ov3640_id);
+
+static struct i2c_driver ov3640_i2c_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "ov3640",
+ },
+ .probe = ov3640_probe,
+ .remove = ov3640_remove,
+ .id_table = ov3640_id,
+};
+
+extern void gpio_sensor_active(unsigned int csi_index);
+extern void gpio_sensor_inactive(unsigned int csi);
+
+static s32 ov3640_write_reg(u16 reg, u8 val)
+{
+ u8 au8Buf[3] = {0};
+
+ au8Buf[0] = reg >> 8;
+ au8Buf[1] = reg & 0xff;
+ au8Buf[2] = val;
+
+ if (i2c_master_send(ov3640_data.i2c_client, au8Buf, 3) < 0) {
+ pr_err("%s:write reg error:reg=%x,val=%x\n",
+ __func__, reg, val);
+ return -1;
+ }
+
+ return 0;
+}
+
+static s32 ov3640_read_reg(u16 reg, u8 *val)
+{
+ u8 au8RegBuf[2] = {0};
+ u8 u8RdVal = 0;
+
+ au8RegBuf[0] = reg >> 8;
+ au8RegBuf[1] = reg & 0xff;
+
+ if (2 != i2c_master_send(ov3640_data.i2c_client, au8RegBuf, 2)) {
+ pr_err("%s:write reg error:reg=%x\n",
+ __func__, reg);
+ return -1;
+ }
+
+ if (1 != i2c_master_recv(ov3640_data.i2c_client, &u8RdVal, 1)) {
+ pr_err("%s:read reg error:reg=%x,val=%x\n",
+ __func__, reg, u8RdVal);
+ return -1;
+ }
+
+ *val = u8RdVal;
+
+ return u8RdVal;
+}
+
+static int ov3640_init_mode(enum ov3640_frame_rate frame_rate,
+ enum ov3640_mode mode)
+{
+ struct reg_value *pModeSetting = NULL;
+ s32 i = 0;
+ s32 iModeSettingArySize = 0;
+ register u32 Delay_ms = 0;
+ register u16 RegAddr = 0;
+ register u8 Mask = 0;
+ register u8 Val = 0;
+ u8 RegVal = 0;
+ int retval = 0;
+
+ if (mode > ov3640_mode_MAX || mode < ov3640_mode_MIN) {
+ pr_err("Wrong ov3640 mode detected!\n");
+ return -1;
+ }
+
+ pModeSetting = ov3640_mode_info_data[frame_rate][mode].init_data_ptr;
+ iModeSettingArySize =
+ ov3640_mode_info_data[frame_rate][mode].init_data_size;
+
+ ov3640_data.pix.width = ov3640_mode_info_data[frame_rate][mode].width;
+ ov3640_data.pix.height = ov3640_mode_info_data[frame_rate][mode].height;
+
+ for (i = 0; i < iModeSettingArySize; ++i, ++pModeSetting) {
+ Delay_ms = pModeSetting->u32Delay_ms;
+ RegAddr = pModeSetting->u16RegAddr;
+ Val = pModeSetting->u8Val;
+ Mask = pModeSetting->u8Mask;
+
+ if (Mask) {
+ retval = ov3640_read_reg(RegAddr, &RegVal);
+ if (retval < 0)
+ goto err;
+
+ RegVal &= ~(u8)Mask;
+ Val &= Mask;
+ Val |= RegVal;
+ }
+
+ retval = ov3640_write_reg(RegAddr, Val);
+ if (retval < 0)
+ goto err;
+
+ if (Delay_ms)
+ msleep(Delay_ms);
+ }
+err:
+ return retval;
+}
+
+/* --------------- IOCTL functions from v4l2_int_ioctl_desc --------------- */
+
+static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p)
+{
+ if (s == NULL) {
+ pr_err(" ERROR!! no slave device set!\n");
+ return -1;
+ }
+
+ memset(p, 0, sizeof(*p));
+ p->u.bt656.clock_curr = ov3640_data.mclk;
+ pr_debug(" clock_curr=mclk=%d\n", ov3640_data.mclk);
+ p->if_type = V4L2_IF_TYPE_BT656;
+ p->u.bt656.mode = V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT;
+ p->u.bt656.clock_min = OV3640_XCLK_MIN;
+ p->u.bt656.clock_max = OV3640_XCLK_MAX;
+ p->u.bt656.bt_sync_correct = 1; /* Indicate external vsync */
+
+ return 0;
+}
+
+/*!
+ * ioctl_s_power - V4L2 sensor interface handler for VIDIOC_S_POWER ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @on: indicates power mode (on or off)
+ *
+ * Turns the power on or off, depending on the value of on and returns the
+ * appropriate error code.
+ */
+static int ioctl_s_power(struct v4l2_int_device *s, int on)
+{
+ struct sensor *sensor = s->priv;
+
+ if (on && !sensor->on) {
+ gpio_sensor_active(ov3640_data.csi);
+ if (io_regulator)
+ if (regulator_enable(io_regulator) != 0)
+ return -EIO;
+ if (core_regulator)
+ if (regulator_enable(core_regulator) != 0)
+ return -EIO;
+ if (gpo_regulator)
+ if (regulator_enable(gpo_regulator) != 0)
+ return -EIO;
+ if (analog_regulator)
+ if (regulator_enable(analog_regulator) != 0)
+ return -EIO;
+ /* Make sure power on */
+ if (camera_plat->pwdn)
+ camera_plat->pwdn(0);
+
+ } else if (!on && sensor->on) {
+ if (analog_regulator)
+ regulator_disable(analog_regulator);
+ if (core_regulator)
+ regulator_disable(core_regulator);
+ if (io_regulator)
+ regulator_disable(io_regulator);
+ if (gpo_regulator)
+ regulator_disable(gpo_regulator);
+ gpio_sensor_inactive(ov3640_data.csi);
+ }
+
+ sensor->on = on;
+
+ return 0;
+}
+
+/*!
+ * ioctl_g_parm - V4L2 sensor interface handler for VIDIOC_G_PARM ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure
+ *
+ * Returns the sensor's video CAPTURE parameters.
+ */
+static int ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)
+{
+ struct sensor *sensor = s->priv;
+ struct v4l2_captureparm *cparm = &a->parm.capture;
+ int ret = 0;
+
+ switch (a->type) {
+ /* This is the only case currently handled. */
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ memset(a, 0, sizeof(*a));
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ cparm->capability = sensor->streamcap.capability;
+ cparm->timeperframe = sensor->streamcap.timeperframe;
+ cparm->capturemode = sensor->streamcap.capturemode;
+ ret = 0;
+ break;
+
+ /* These are all the possible cases. */
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ ret = -EINVAL;
+ break;
+
+ default:
+ pr_debug(" type is unknown - %d\n", a->type);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+/*!
+ * ioctl_s_parm - V4L2 sensor interface handler for VIDIOC_S_PARM ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure
+ *
+ * Configures the sensor to use the input parameters, if possible. If
+ * not possible, reverts to the old parameters and returns the
+ * appropriate error code.
+ */
+static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)
+{
+ struct sensor *sensor = s->priv;
+ struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe;
+ u32 tgt_fps; /* target frames per secound */
+ enum ov3640_frame_rate frame_rate;
+ int ret = 0;
+
+ /* Make sure power on */
+ if (camera_plat->pwdn)
+ camera_plat->pwdn(0);
+
+ switch (a->type) {
+ /* This is the only case currently handled. */
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ /* Check that the new frame rate is allowed. */
+ if ((timeperframe->numerator == 0) ||
+ (timeperframe->denominator == 0)) {
+ timeperframe->denominator = DEFAULT_FPS;
+ timeperframe->numerator = 1;
+ }
+
+ tgt_fps = timeperframe->denominator /
+ timeperframe->numerator;
+
+ if (tgt_fps > MAX_FPS) {
+ timeperframe->denominator = MAX_FPS;
+ timeperframe->numerator = 1;
+ } else if (tgt_fps < MIN_FPS) {
+ timeperframe->denominator = MIN_FPS;
+ timeperframe->numerator = 1;
+ }
+
+ /* Actual frame rate we use */
+ tgt_fps = timeperframe->denominator /
+ timeperframe->numerator;
+
+ if (tgt_fps == 15)
+ frame_rate = ov3640_15_fps;
+ else if (tgt_fps == 30)
+ frame_rate = ov3640_30_fps;
+ else {
+ pr_err(" The camera frame rate is not supported!\n");
+ return -EINVAL;
+ }
+
+ sensor->streamcap.timeperframe = *timeperframe;
+ sensor->streamcap.capturemode =
+ (u32)a->parm.capture.capturemode;
+
+ ret = ov3640_init_mode(frame_rate,
+ sensor->streamcap.capturemode);
+ break;
+
+ /* These are all the possible cases. */
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ pr_debug(" type is not " \
+ "V4L2_BUF_TYPE_VIDEO_CAPTURE but %d\n",
+ a->type);
+ ret = -EINVAL;
+ break;
+
+ default:
+ pr_debug(" type is unknown - %d\n", a->type);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+/*!
+ * ioctl_g_fmt_cap - V4L2 sensor interface handler for ioctl_g_fmt_cap
+ * @s: pointer to standard V4L2 device structure
+ * @f: pointer to standard V4L2 v4l2_format structure
+ *
+ * Returns the sensor's current pixel format in the v4l2_format
+ * parameter.
+ */
+static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f)
+{
+ struct sensor *sensor = s->priv;
+
+ f->fmt.pix = sensor->pix;
+
+ return 0;
+}
+
+/*!
+ * ioctl_g_ctrl - V4L2 sensor interface handler for VIDIOC_G_CTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @vc: standard V4L2 VIDIOC_G_CTRL ioctl structure
+ *
+ * If the requested control is supported, returns the control's current
+ * value from the video_control[] array. Otherwise, returns -EINVAL
+ * if the control is not supported.
+ */
+static int ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc)
+{
+ int ret = 0;
+
+ switch (vc->id) {
+ case V4L2_CID_BRIGHTNESS:
+ vc->value = ov3640_data.brightness;
+ break;
+ case V4L2_CID_HUE:
+ vc->value = ov3640_data.hue;
+ break;
+ case V4L2_CID_CONTRAST:
+ vc->value = ov3640_data.contrast;
+ break;
+ case V4L2_CID_SATURATION:
+ vc->value = ov3640_data.saturation;
+ break;
+ case V4L2_CID_RED_BALANCE:
+ vc->value = ov3640_data.red;
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ vc->value = ov3640_data.blue;
+ break;
+ case V4L2_CID_EXPOSURE:
+ vc->value = ov3640_data.ae_mode;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+/*!
+ * ioctl_s_ctrl - V4L2 sensor interface handler for VIDIOC_S_CTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @vc: standard V4L2 VIDIOC_S_CTRL ioctl structure
+ *
+ * If the requested control is supported, sets the control's current
+ * value in HW (and updates the video_control[] array). Otherwise,
+ * returns -EINVAL if the control is not supported.
+ */
+static int ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc)
+{
+ int retval = 0;
+
+ pr_debug("In ov3640:ioctl_s_ctrl %d\n",
+ vc->id);
+
+ switch (vc->id) {
+ case V4L2_CID_BRIGHTNESS:
+ break;
+ case V4L2_CID_CONTRAST:
+ break;
+ case V4L2_CID_SATURATION:
+ break;
+ case V4L2_CID_HUE:
+ break;
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ break;
+ case V4L2_CID_DO_WHITE_BALANCE:
+ break;
+ case V4L2_CID_RED_BALANCE:
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ break;
+ case V4L2_CID_GAMMA:
+ break;
+ case V4L2_CID_EXPOSURE:
+ break;
+ case V4L2_CID_AUTOGAIN:
+ break;
+ case V4L2_CID_GAIN:
+ break;
+ case V4L2_CID_HFLIP:
+ break;
+ case V4L2_CID_VFLIP:
+ break;
+ default:
+ retval = -EPERM;
+ break;
+ }
+
+ return retval;
+}
+
+/*!
+ * ioctl_init - V4L2 sensor interface handler for VIDIOC_INT_INIT
+ * @s: pointer to standard V4L2 device structure
+ */
+static int ioctl_init(struct v4l2_int_device *s)
+{
+
+ return 0;
+}
+
+/*!
+ * ioctl_dev_init - V4L2 sensor interface handler for vidioc_int_dev_init_num
+ * @s: pointer to standard V4L2 device structure
+ *
+ * Initialise the device when slave attaches to the master.
+ */
+static int ioctl_dev_init(struct v4l2_int_device *s)
+{
+ struct sensor *sensor = s->priv;
+ u32 tgt_xclk; /* target xclk */
+ u32 tgt_fps; /* target frames per secound */
+ enum ov3640_frame_rate frame_rate;
+
+ gpio_sensor_active(ov3640_data.csi);
+ ov3640_data.on = true;
+
+ /* mclk */
+ tgt_xclk = ov3640_data.mclk;
+ tgt_xclk = min(tgt_xclk, (u32)OV3640_XCLK_MAX);
+ tgt_xclk = max(tgt_xclk, (u32)OV3640_XCLK_MIN);
+ ov3640_data.mclk = tgt_xclk;
+
+ pr_debug(" Setting mclk to %d MHz\n", tgt_xclk / 1000000);
+ set_mclk_rate(&ov3640_data.mclk, ov3640_data.csi);
+
+ /* Default camera frame rate is set in probe */
+ tgt_fps = sensor->streamcap.timeperframe.denominator /
+ sensor->streamcap.timeperframe.numerator;
+
+ if (tgt_fps == 15)
+ frame_rate = ov3640_15_fps;
+ else if (tgt_fps == 30)
+ frame_rate = ov3640_30_fps;
+ else
+ return -EINVAL; /* Only support 15fps or 30fps now. */
+
+ return ov3640_init_mode(frame_rate,
+ sensor->streamcap.capturemode);
+}
+
+/*!
+ * ioctl_dev_exit - V4L2 sensor interface handler for vidioc_int_dev_exit_num
+ * @s: pointer to standard V4L2 device structure
+ *
+ * Delinitialise the device when slave detaches to the master.
+ */
+static int ioctl_dev_exit(struct v4l2_int_device *s)
+{
+ gpio_sensor_inactive(ov3640_data.csi);
+
+ return 0;
+}
+
+/*!
+ * This structure defines all the ioctls for this module and links them to the
+ * enumeration.
+ */
+static struct v4l2_int_ioctl_desc ov3640_ioctl_desc[] = {
+ {vidioc_int_dev_init_num, (v4l2_int_ioctl_func *)ioctl_dev_init},
+ {vidioc_int_dev_exit_num, ioctl_dev_exit},
+ {vidioc_int_s_power_num, (v4l2_int_ioctl_func *)ioctl_s_power},
+ {vidioc_int_g_ifparm_num, (v4l2_int_ioctl_func *)ioctl_g_ifparm},
+/* {vidioc_int_g_needs_reset_num,
+ (v4l2_int_ioctl_func *)ioctl_g_needs_reset}, */
+/* {vidioc_int_reset_num, (v4l2_int_ioctl_func *)ioctl_reset}, */
+ {vidioc_int_init_num, (v4l2_int_ioctl_func *)ioctl_init},
+/* {vidioc_int_enum_fmt_cap_num,
+ (v4l2_int_ioctl_func *)ioctl_enum_fmt_cap}, */
+/* {vidioc_int_try_fmt_cap_num,
+ (v4l2_int_ioctl_func *)ioctl_try_fmt_cap}, */
+ {vidioc_int_g_fmt_cap_num, (v4l2_int_ioctl_func *)ioctl_g_fmt_cap},
+/* {vidioc_int_s_fmt_cap_num, (v4l2_int_ioctl_func *)ioctl_s_fmt_cap}, */
+ {vidioc_int_g_parm_num, (v4l2_int_ioctl_func *)ioctl_g_parm},
+ {vidioc_int_s_parm_num, (v4l2_int_ioctl_func *)ioctl_s_parm},
+/* {vidioc_int_queryctrl_num, (v4l2_int_ioctl_func *)ioctl_queryctrl}, */
+ {vidioc_int_g_ctrl_num, (v4l2_int_ioctl_func *)ioctl_g_ctrl},
+ {vidioc_int_s_ctrl_num, (v4l2_int_ioctl_func *)ioctl_s_ctrl},
+};
+
+static struct v4l2_int_slave ov3640_slave = {
+ .ioctls = ov3640_ioctl_desc,
+ .num_ioctls = ARRAY_SIZE(ov3640_ioctl_desc),
+};
+
+static struct v4l2_int_device ov3640_int_device = {
+ .module = THIS_MODULE,
+ .name = "ov3640",
+ .type = v4l2_int_type_slave,
+ .u = {
+ .slave = &ov3640_slave,
+ },
+};
+
+/*!
+ * ov3640 I2C probe function
+ *
+ * @param adapter struct i2c_adapter *
+ * @return Error code indicating success or failure
+ */
+static int ov3640_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int retval;
+ struct mxc_camera_platform_data *plat_data = client->dev.platform_data;
+
+ /* Set initial values for the sensor struct. */
+ memset(&ov3640_data, 0, sizeof(ov3640_data));
+ ov3640_data.mclk = 24000000; /* 6 - 54 MHz, typical 24MHz */
+ ov3640_data.mclk = plat_data->mclk;
+ ov3640_data.csi = plat_data->csi;
+
+ ov3640_data.i2c_client = client;
+ ov3640_data.pix.pixelformat = V4L2_PIX_FMT_UYVY;
+ ov3640_data.pix.width = 640;
+ ov3640_data.pix.height = 480;
+ ov3640_data.streamcap.capability = V4L2_MODE_HIGHQUALITY |
+ V4L2_CAP_TIMEPERFRAME;
+ ov3640_data.streamcap.capturemode = 0;
+ ov3640_data.streamcap.timeperframe.denominator = DEFAULT_FPS;
+ ov3640_data.streamcap.timeperframe.numerator = 1;
+
+ if (plat_data->io_regulator) {
+ io_regulator = regulator_get(&client->dev,
+ plat_data->io_regulator);
+ if (!IS_ERR(io_regulator)) {
+ regulator_set_voltage(io_regulator,
+ OV3640_VOLTAGE_DIGITAL_IO,
+ OV3640_VOLTAGE_DIGITAL_IO);
+ if (regulator_enable(io_regulator) != 0) {
+ pr_err("%s:io set voltage error\n", __func__);
+ goto err1;
+ } else {
+ dev_dbg(&client->dev,
+ "%s:io set voltage ok\n", __func__);
+ }
+ } else
+ io_regulator = NULL;
+ }
+
+ if (plat_data->core_regulator) {
+ core_regulator = regulator_get(&client->dev,
+ plat_data->core_regulator);
+ if (!IS_ERR(core_regulator)) {
+ regulator_set_voltage(core_regulator,
+ OV3640_VOLTAGE_DIGITAL_CORE,
+ OV3640_VOLTAGE_DIGITAL_CORE);
+ if (regulator_enable(core_regulator) != 0) {
+ pr_err("%s:core set voltage error\n", __func__);
+ goto err2;
+ } else {
+ dev_dbg(&client->dev,
+ "%s:core set voltage ok\n", __func__);
+ }
+ } else
+ core_regulator = NULL;
+ }
+
+ if (plat_data->analog_regulator) {
+ analog_regulator = regulator_get(&client->dev,
+ plat_data->analog_regulator);
+ if (!IS_ERR(analog_regulator)) {
+ regulator_set_voltage(analog_regulator,
+ OV3640_VOLTAGE_ANALOG,
+ OV3640_VOLTAGE_ANALOG);
+ if (regulator_enable(analog_regulator) != 0) {
+ pr_err("%s:analog set voltage error\n",
+ __func__);
+ goto err3;
+ } else {
+ dev_dbg(&client->dev,
+ "%s:analog set voltage ok\n", __func__);
+ }
+ } else
+ analog_regulator = NULL;
+ }
+
+ if (plat_data->gpo_regulator) {
+ gpo_regulator = regulator_get(&client->dev,
+ plat_data->gpo_regulator);
+ if (!IS_ERR(gpo_regulator)) {
+ regulator_set_voltage(gpo_regulator,
+ OV3640_VOLTAGE_DIGITAL_GPO,
+ OV3640_VOLTAGE_DIGITAL_GPO);
+ if (regulator_enable(gpo_regulator) != 0) {
+ pr_err("%s:gpo enable error\n", __func__);
+ goto err4;
+ } else {
+ dev_dbg(&client->dev,
+ "%s:gpo enable ok\n", __func__);
+ }
+ } else
+ gpo_regulator = NULL;
+ }
+
+ if (plat_data->pwdn)
+ plat_data->pwdn(0);
+
+ camera_plat = plat_data;
+
+ ov3640_int_device.priv = &ov3640_data;
+ retval = v4l2_int_device_register(&ov3640_int_device);
+
+ return retval;
+
+err4:
+ if (analog_regulator) {
+ regulator_disable(analog_regulator);
+ regulator_put(analog_regulator);
+ }
+err3:
+ if (core_regulator) {
+ regulator_disable(core_regulator);
+ regulator_put(core_regulator);
+ }
+err2:
+ if (io_regulator) {
+ regulator_disable(io_regulator);
+ regulator_put(io_regulator);
+ }
+err1:
+ return -1;
+}
+
+/*!
+ * ov3640 I2C detach function
+ *
+ * @param client struct i2c_client *
+ * @return Error code indicating success or failure
+ */
+static int ov3640_remove(struct i2c_client *client)
+{
+ v4l2_int_device_unregister(&ov3640_int_device);
+
+ if (gpo_regulator) {
+ regulator_disable(gpo_regulator);
+ regulator_put(gpo_regulator);
+ }
+
+ if (analog_regulator) {
+ regulator_disable(analog_regulator);
+ regulator_put(analog_regulator);
+ }
+
+ if (core_regulator) {
+ regulator_disable(core_regulator);
+ regulator_put(core_regulator);
+ }
+
+ if (io_regulator) {
+ regulator_disable(io_regulator);
+ regulator_put(io_regulator);
+ }
+
+ return 0;
+}
+
+/*!
+ * ov3640 init function
+ * Called by insmod ov3640_camera.ko.
+ *
+ * @return Error code indicating success or failure
+ */
+static __init int ov3640_init(void)
+{
+ u8 err;
+
+ err = i2c_add_driver(&ov3640_i2c_driver);
+ if (err != 0)
+ pr_err("%s:driver registration failed, error=%d \n",
+ __func__, err);
+
+ return err;
+}
+
+/*!
+ * OV3640 cleanup function
+ * Called on rmmod ov3640_camera.ko
+ *
+ * @return Error code indicating success or failure
+ */
+static void __exit ov3640_clean(void)
+{
+ i2c_del_driver(&ov3640_i2c_driver);
+}
+
+module_init(ov3640_init);
+module_exit(ov3640_clean);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("OV3640 Camera Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("CSI");
diff --git a/drivers/media/video/mxc/capture/sensor_clock.c b/drivers/media/video/mxc/capture/sensor_clock.c
new file mode 100644
index 000000000000..3d1bf8d71019
--- /dev/null
+++ b/drivers/media/video/mxc/capture/sensor_clock.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file sensor_clock.c
+ *
+ * @brief camera clock function
+ *
+ * @ingroup Camera
+ */
+#include <linux/init.h>
+#include <linux/ctype.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/clk.h>
+#include <mach/hardware.h>
+
+#if defined(CONFIG_MXC_IPU_V1) || defined(CONFIG_VIDEO_MXC_EMMA_CAMERA) \
+ || defined(CONFIG_VIDEO_MXC_CSI_CAMERA_MODULE) \
+ || defined(CONFIG_VIDEO_MXC_CSI_CAMERA)
+/*
+ * set_mclk_rate
+ *
+ * @param p_mclk_freq mclk frequence
+ *
+ */
+void set_mclk_rate(uint32_t * p_mclk_freq)
+{
+ struct clk *clk;
+ uint32_t freq = 0;
+
+ clk = clk_get(NULL, "csi_clk");
+
+ freq = clk_round_rate(clk, *p_mclk_freq);
+ clk_set_rate(clk, freq);
+
+ *p_mclk_freq = freq;
+
+ clk_put(clk);
+ pr_debug("mclk frequency = %d\n", *p_mclk_freq);
+}
+#else
+/*
+ * set_mclk_rate
+ *
+ * @param p_mclk_freq mclk frequence
+ * @param csi csi 0 or csi 1
+ *
+ */
+void set_mclk_rate(uint32_t *p_mclk_freq, uint32_t csi)
+{
+ struct clk *clk;
+ uint32_t freq = 0;
+ char *mclk;
+
+ if (cpu_is_mx53()) {
+ if (csi == 0)
+ mclk = "ssi_ext1_clk";
+ else {
+ pr_err("invalid csi num %d\n", csi);
+ return;
+ }
+ } else {
+ if (csi == 0) {
+ mclk = "csi_mclk1";
+ } else if (csi == 1) {
+ mclk = "csi_mclk2";
+ } else {
+ pr_err("invalid csi num %d\n", csi);
+ return;
+ }
+ }
+
+ clk = clk_get(NULL, mclk);
+
+ freq = clk_round_rate(clk, *p_mclk_freq);
+ clk_set_rate(clk, freq);
+
+ *p_mclk_freq = freq;
+
+ clk_put(clk);
+ pr_debug("%s frequency = %d\n", mclk, *p_mclk_freq);
+}
+#endif
+
+/* Exported symbols for modules. */
+EXPORT_SYMBOL(set_mclk_rate);
diff --git a/drivers/media/video/mxc/opl/Makefile b/drivers/media/video/mxc/opl/Makefile
new file mode 100644
index 000000000000..092a62c5ac4a
--- /dev/null
+++ b/drivers/media/video/mxc/opl/Makefile
@@ -0,0 +1,5 @@
+opl-objs := opl_mod.o rotate90_u16.o rotate270_u16.o \
+ rotate90_u16_qcif.o rotate270_u16_qcif.o \
+ vmirror_u16.o hmirror_rotate180_u16.o
+
+obj-$(CONFIG_VIDEO_MXC_OPL) += opl.o
diff --git a/drivers/media/video/mxc/opl/hmirror_rotate180_u16.c b/drivers/media/video/mxc/opl/hmirror_rotate180_u16.c
new file mode 100644
index 000000000000..3119a128c1f2
--- /dev/null
+++ b/drivers/media/video/mxc/opl/hmirror_rotate180_u16.c
@@ -0,0 +1,259 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/module.h>
+#include "opl.h"
+
+static inline u32 rot_left_u16(u16 x, unsigned int n)
+{
+ return (x << n) | (x >> (16 - n));
+}
+
+static inline u32 rot_left_u32(u32 x, unsigned int n)
+{
+ return (x << n) | (x >> (32 - n));
+}
+
+static inline u32 byte_swap_u32(u32 x)
+{
+ u32 t1, t2, t3;
+
+ t1 = x ^ ((x << 16) | x >> 16);
+ t2 = t1 & 0xff00ffff;
+ t3 = (x >> 8) | (x << 24);
+ return t3 ^ (t2 >> 8);
+}
+
+static int opl_hmirror_u16_by1(const u8 * src, int src_line_stride, int width,
+ int height, u8 * dst, int dst_line_stride,
+ int vmirror);
+static int opl_hmirror_u16_by2(const u8 * src, int src_line_stride, int width,
+ int height, u8 * dst, int dst_line_stride,
+ int vmirror);
+static int opl_hmirror_u16_by4(const u8 * src, int src_line_stride, int width,
+ int height, u8 * dst, int dst_line_stride,
+ int vmirror);
+static int opl_hmirror_u16_by8(const u8 * src, int src_line_stride, int width,
+ int height, u8 * dst, int dst_line_stride,
+ int vmirror);
+
+int opl_hmirror_u16(const u8 * src, int src_line_stride, int width, int height,
+ u8 * dst, int dst_line_stride)
+{
+ if (!src || !dst)
+ return OPLERR_NULL_PTR;
+
+ if (width == 0 || height == 0 || src_line_stride == 0
+ || dst_line_stride == 0)
+ return OPLERR_BAD_ARG;
+
+ if (width % 8 == 0)
+ return opl_hmirror_u16_by8(src, src_line_stride, width, height,
+ dst, dst_line_stride, 0);
+ else if (width % 4 == 0)
+ return opl_hmirror_u16_by4(src, src_line_stride, width, height,
+ dst, dst_line_stride, 0);
+ else if (width % 2 == 0)
+ return opl_hmirror_u16_by2(src, src_line_stride, width, height,
+ dst, dst_line_stride, 0);
+ else /* (width % 1) */
+ return opl_hmirror_u16_by1(src, src_line_stride, width, height,
+ dst, dst_line_stride, 0);
+}
+
+int opl_rotate180_u16(const u8 * src, int src_line_stride, int width,
+ int height, u8 * dst, int dst_line_stride)
+{
+ if (!src || !dst)
+ return OPLERR_NULL_PTR;
+
+ if (width == 0 || height == 0 || src_line_stride == 0
+ || dst_line_stride == 0)
+ return OPLERR_BAD_ARG;
+
+ if (width % 8 == 0)
+ return opl_hmirror_u16_by8(src, src_line_stride, width, height,
+ dst, dst_line_stride, 1);
+ else if (width % 4 == 0)
+ return opl_hmirror_u16_by4(src, src_line_stride, width, height,
+ dst, dst_line_stride, 1);
+ else if (width % 2 == 0)
+ return opl_hmirror_u16_by2(src, src_line_stride, width, height,
+ dst, dst_line_stride, 1);
+ else /* (width % 1) */
+ return opl_hmirror_u16_by1(src, src_line_stride, width, height,
+ dst, dst_line_stride, 1);
+}
+
+static int opl_hmirror_u16_by1(const u8 * src, int src_line_stride, int width,
+ int height, u8 * dst, int dst_line_stride,
+ int vmirror)
+{
+ const u8 *src_row_addr;
+ const u8 *psrc;
+ u8 *dst_row_addr, *pdst;
+ int i, j;
+ u16 pixel;
+
+ src_row_addr = src;
+ if (vmirror) {
+ dst_row_addr = dst + dst_line_stride * (height - 1);
+ dst_line_stride = -dst_line_stride;
+ } else
+ dst_row_addr = dst;
+
+ /* Loop over all rows */
+ for (i = 0; i < height; i++) {
+ /* Loop over each pixel */
+ psrc = src_row_addr;
+ pdst = dst_row_addr + (width - 1) * BYTES_PER_PIXEL
+ - (BYTES_PER_PIXEL - BYTES_PER_PIXEL);
+ for (j = 0; j < width; j++) {
+ pixel = *(u16 *) psrc;
+ *(u16 *) pdst = pixel;
+ psrc += BYTES_PER_PIXEL;
+ pdst -= BYTES_PER_PIXEL;
+ }
+ src_row_addr += src_line_stride;
+ dst_row_addr += dst_line_stride;
+ }
+
+ return OPLERR_SUCCESS;
+}
+
+static int opl_hmirror_u16_by2(const u8 * src, int src_line_stride, int width,
+ int height, u8 * dst, int dst_line_stride,
+ int vmirror)
+{
+ const u8 *src_row_addr;
+ const u8 *psrc;
+ u8 *dst_row_addr, *pdst;
+ int i, j;
+ u32 pixelsin, pixelsout;
+
+ src_row_addr = src;
+ if (vmirror) {
+ dst_row_addr = dst + dst_line_stride * (height - 1);
+ dst_line_stride = -dst_line_stride;
+ } else
+ dst_row_addr = dst;
+
+ /* Loop over all rows */
+ for (i = 0; i < height; i++) {
+ /* Loop over each pixel */
+ psrc = src_row_addr;
+ pdst = dst_row_addr + (width - 2) * BYTES_PER_PIXEL;
+ for (j = 0; j < (width >> 1); j++) {
+ pixelsin = *(u32 *) psrc;
+ pixelsout = rot_left_u32(pixelsin, 16);
+ *(u32 *) pdst = pixelsout;
+ psrc += BYTES_PER_2PIXEL;
+ pdst -= BYTES_PER_2PIXEL;
+ }
+ src_row_addr += src_line_stride;
+ dst_row_addr += dst_line_stride;
+ }
+
+ return OPLERR_SUCCESS;
+}
+
+static int opl_hmirror_u16_by4(const u8 * src, int src_line_stride, int width,
+ int height, u8 * dst, int dst_line_stride,
+ int vmirror)
+{
+ const u8 *src_row_addr;
+ const u8 *psrc;
+ u8 *dst_row_addr, *pdst;
+ int i, j;
+
+ union doubleword {
+ u64 dw;
+ u32 w[2];
+ };
+
+ union doubleword inbuf;
+ union doubleword outbuf;
+
+ src_row_addr = src;
+ if (vmirror) {
+ dst_row_addr = dst + dst_line_stride * (height - 1);
+ dst_line_stride = -dst_line_stride;
+ } else
+ dst_row_addr = dst;
+
+ /* Loop over all rows */
+ for (i = 0; i < height; i++) {
+ /* Loop over each pixel */
+ psrc = src_row_addr;
+ pdst = dst_row_addr + (width - 4) * BYTES_PER_PIXEL;
+ for (j = 0; j < (width >> 2); j++) {
+ inbuf.dw = *(u64 *) psrc;
+ outbuf.w[0] = rot_left_u32(inbuf.w[1], 16);
+ outbuf.w[1] = rot_left_u32(inbuf.w[0], 16);
+ *(u64 *) pdst = outbuf.dw;
+ psrc += BYTES_PER_4PIXEL;
+ pdst -= BYTES_PER_4PIXEL;
+ }
+ src_row_addr += src_line_stride;
+ dst_row_addr += dst_line_stride;
+ }
+ return OPLERR_SUCCESS;
+}
+
+static int opl_hmirror_u16_by8(const u8 * src, int src_line_stride, int width,
+ int height, u8 * dst, int dst_line_stride,
+ int vmirror)
+{
+ const u8 *src_row_addr;
+ const u8 *psrc;
+ u8 *dst_row_addr, *pdst;
+ int i, j;
+
+ src_row_addr = src;
+ if (vmirror) {
+ dst_row_addr = dst + dst_line_stride * (height - 1);
+ dst_line_stride = -dst_line_stride;
+ } else
+ dst_row_addr = dst;
+
+ /* Loop over all rows */
+ for (i = 0; i < height; i++) {
+ /* Loop over each pixel */
+ psrc = src_row_addr;
+ pdst = dst_row_addr + (width - 1) * BYTES_PER_PIXEL - 2;
+ for (j = (width >> 3); j > 0; j--) {
+ __asm__ volatile (
+ "ldmia %0!,{r2-r5}\n\t"
+ "mov r6, r2\n\t"
+ "mov r7, r3\n\t"
+ "mov r2, r5, ROR #16\n\t"
+ "mov r3, r4, ROR #16\n\t"
+ "mov r4, r7, ROR #16\n\t"
+ "mov r5, r6, ROR #16\n\t"
+ "stmda %1!,{r2-r5}\n\t"
+
+ :"+r"(psrc), "+r"(pdst)
+ :"0"(psrc), "1"(pdst)
+ :"r2", "r3", "r4", "r5", "r6", "r7",
+ "memory"
+ );
+ }
+ src_row_addr += src_line_stride;
+ dst_row_addr += dst_line_stride;
+ }
+
+ return OPLERR_SUCCESS;
+}
+
+EXPORT_SYMBOL(opl_hmirror_u16);
+EXPORT_SYMBOL(opl_rotate180_u16);
diff --git a/drivers/media/video/mxc/opl/opl.h b/drivers/media/video/mxc/opl/opl.h
new file mode 100644
index 000000000000..24644c8e78fa
--- /dev/null
+++ b/drivers/media/video/mxc/opl/opl.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @defgroup OPLIP OPL Image Processing
+ */
+/*!
+ * @file opl.h
+ *
+ * @brief The OPL (Open Primitives Library) Image Processing library defines
+ * efficient functions for rotation and mirroring.
+ *
+ * It includes ARM9-optimized rotation and mirroring functions. It is derived
+ * from the original OPL project which is found at sourceforge.freescale.net.
+ *
+ * @ingroup OPLIP
+ */
+#ifndef __OPL_H__
+#define __OPL_H__
+
+#include <linux/types.h>
+
+#define BYTES_PER_PIXEL 2
+#define CACHE_LINE_WORDS 8
+#define BYTES_PER_WORD 4
+
+#define BYTES_PER_2PIXEL (BYTES_PER_PIXEL * 2)
+#define BYTES_PER_4PIXEL (BYTES_PER_PIXEL * 4)
+#define BYTES_PER_8PIXEL (BYTES_PER_PIXEL * 8)
+
+#define QCIF_Y_WIDTH 176
+#define QCIF_Y_HEIGHT 144
+
+/*! Enumerations of opl error code */
+enum opl_error {
+ OPLERR_SUCCESS = 0,
+ OPLERR_NULL_PTR,
+ OPLERR_BAD_ARG,
+ OPLERR_DIV_BY_ZERO,
+ OPLERR_OVER_FLOW,
+ OPLERR_UNDER_FLOW,
+ OPLERR_MISALIGNED,
+};
+
+/*!
+ * @brief Rotate a 16bbp buffer 90 degrees clockwise.
+ *
+ * @param src Pointer to the input buffer
+ * @param src_line_stride Length in bytes of a raster line of the input buffer
+ * @param width Width in pixels of the region in the input buffer
+ * @param height Height in pixels of the region in the input buffer
+ * @param dst Pointer to the output buffer
+ * @param dst_line_stride Length in bytes of a raster line of the output buffer
+ *
+ * @return Standard OPL error code. See enumeration for possible result codes.
+ */
+int opl_rotate90_u16(const u8 * src, int src_line_stride, int width, int height,
+ u8 * dst, int dst_line_stride);
+
+/*!
+ * @brief Rotate a 16bbp buffer 180 degrees clockwise.
+ *
+ * @param src Pointer to the input buffer
+ * @param src_line_stride Length in bytes of a raster line of the input buffer
+ * @param width Width in pixels of the region in the input buffer
+ * @param height Height in pixels of the region in the input buffer
+ * @param dst Pointer to the output buffer
+ * @param dst_line_stride Length in bytes of a raster line of the output buffer
+ *
+ * @return Standard OPL error code. See enumeration for possible result codes.
+ */
+int opl_rotate180_u16(const u8 * src, int src_line_stride, int width,
+ int height, u8 * dst, int dst_line_stride);
+
+/*!
+ * @brief Rotate a 16bbp buffer 270 degrees clockwise
+ *
+ * @param src Pointer to the input buffer
+ * @param src_line_stride Length in bytes of a raster line of the input buffer
+ * @param width Width in pixels of the region in the input buffer
+ * @param height Height in pixels of the region in the input buffer
+ * @param dst Pointer to the output buffer
+ * @param dst_line_stride Length in bytes of a raster line of the output buffer
+ *
+ * @return Standard OPL error code. See enumeration for possible result codes.
+ */
+int opl_rotate270_u16(const u8 * src, int src_line_stride, int width,
+ int height, u8 * dst, int dst_line_stride);
+
+/*!
+ * @brief Mirror a 16bpp buffer horizontally
+ *
+ * @param src Pointer to the input buffer
+ * @param src_line_stride Length in bytes of a raster line of the input buffer
+ * @param width Width in pixels of the region in the input buffer
+ * @param height Height in pixels of the region in the input buffer
+ * @param dst Pointer to the output buffer
+ * @param dst_line_stride Length in bytes of a raster line of the output buffer
+ *
+ * @return Standard OPL error code. See enumeration for possible result codes.
+ */
+int opl_hmirror_u16(const u8 * src, int src_line_stride, int width, int height,
+ u8 * dst, int dst_line_stride);
+
+/*!
+ * @brief Mirror a 16bpp buffer vertically
+ *
+ * @param src Pointer to the input buffer
+ * @param src_line_stride Length in bytes of a raster line of the input buffer
+ * @param width Width in pixels of the region in the input buffer
+ * @param height Height in pixels of the region in the input buffer
+ * @param dst Pointer to the output buffer
+ * @param dst_line_stride Length in bytes of a raster line of the output buffer
+ *
+ * @return Standard OPL error code. See enumeration for possible result codes.
+ */
+int opl_vmirror_u16(const u8 * src, int src_line_stride, int width, int height,
+ u8 * dst, int dst_line_stride);
+
+/*!
+ * @brief Rotate a 16bbp buffer 90 degrees clockwise and mirror vertically
+ * It is equivalent to rotate 270 degree and mirror horizontally
+ *
+ * @param src Pointer to the input buffer
+ * @param src_line_stride Length in bytes of a raster line of the input buffer
+ * @param width Width in pixels of the region in the input buffer
+ * @param height Height in pixels of the region in the input buffer
+ * @param dst Pointer to the output buffer
+ * @param dst_line_stride Length in bytes of a raster line of the output buffer
+ *
+ * @return Standard OPL error code. See enumeration for possible result codes.
+ */
+int opl_rotate90_vmirror_u16(const u8 * src, int src_line_stride, int width,
+ int height, u8 * dst, int dst_line_stride);
+
+/*!
+ * @brief Rotate a 16bbp buffer 270 degrees clockwise and mirror vertically
+ * It is equivalent to rotate 90 degree and mirror horizontally
+ *
+ * @param src Pointer to the input buffer
+ * @param src_line_stride Length in bytes of a raster line of the input buffer
+ * @param width Width in pixels of the region in the input buffer
+ * @param height Height in pixels of the region in the input buffer
+ * @param dst Pointer to the output buffer
+ * @param dst_line_stride Length in bytes of a raster line of the output buffer
+ *
+ * @return Standard OPL error code. See enumeration for possible result codes.
+ */
+int opl_rotate270_vmirror_u16(const u8 * src, int src_line_stride, int width,
+ int height, u8 * dst, int dst_line_stride);
+
+#endif /* __OPL_H__ */
diff --git a/drivers/media/video/mxc/opl/opl_mod.c b/drivers/media/video/mxc/opl/opl_mod.c
new file mode 100644
index 000000000000..a581aadda252
--- /dev/null
+++ b/drivers/media/video/mxc/opl/opl_mod.c
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/module.h>
+
+static __init int opl_init(void)
+{
+ return 0;
+}
+
+static void __exit opl_exit(void)
+{
+}
+
+module_init(opl_init);
+module_exit(opl_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("OPL Software Rotation/Mirroring");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxc/opl/rotate270_u16.c b/drivers/media/video/mxc/opl/rotate270_u16.c
new file mode 100644
index 000000000000..add87f1a9e44
--- /dev/null
+++ b/drivers/media/video/mxc/opl/rotate270_u16.c
@@ -0,0 +1,285 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/module.h>
+#include "opl.h"
+
+static int opl_rotate270_u16_by16(const u8 * src, int src_line_stride,
+ int width, int height, u8 * dst,
+ int dst_line_stride, int vmirror);
+static int opl_rotate270_u16_by4(const u8 * src, int src_line_stride, int width,
+ int height, u8 * dst, int dst_line_stride,
+ int vmirror);
+static int opl_rotate270_vmirror_u16_both(const u8 * src, int src_line_stride,
+ int width, int height, u8 * dst,
+ int dst_line_stride, int vmirror);
+int opl_rotate270_u16_qcif(const u8 * src, u8 * dst);
+
+int opl_rotate270_u16(const u8 * src, int src_line_stride, int width,
+ int height, u8 * dst, int dst_line_stride)
+{
+ return opl_rotate270_vmirror_u16_both(src, src_line_stride, width,
+ height, dst, dst_line_stride, 0);
+}
+
+int opl_rotate270_vmirror_u16(const u8 * src, int src_line_stride, int width,
+ int height, u8 * dst, int dst_line_stride)
+{
+ return opl_rotate270_vmirror_u16_both(src, src_line_stride, width,
+ height, dst, dst_line_stride, 1);
+}
+
+static int opl_rotate270_vmirror_u16_both(const u8 * src, int src_line_stride,
+ int width, int height, u8 * dst,
+ int dst_line_stride, int vmirror)
+{
+ const int BLOCK_SIZE_PIXELS = CACHE_LINE_WORDS * BYTES_PER_WORD
+ / BYTES_PER_PIXEL;
+ const int BLOCK_SIZE_PIXELS_BY4 = CACHE_LINE_WORDS * BYTES_PER_WORD
+ / BYTES_PER_PIXEL / 4;
+
+ if (!src || !dst)
+ return OPLERR_NULL_PTR;
+
+ if (width == 0 || height == 0 || src_line_stride == 0
+ || dst_line_stride == 0)
+ return OPLERR_BAD_ARG;
+
+ /* The QCIF algorithm doesn't support vertical mirroring */
+ if (vmirror == 0 && width == QCIF_Y_WIDTH && height == QCIF_Y_HEIGHT
+ && src_line_stride == QCIF_Y_WIDTH * 2
+ && src_line_stride == QCIF_Y_HEIGHT * 2)
+ return opl_rotate270_u16_qcif(src, dst);
+ else if (width % BLOCK_SIZE_PIXELS == 0
+ && height % BLOCK_SIZE_PIXELS == 0)
+ return opl_rotate270_u16_by16(src, src_line_stride, width,
+ height, dst, dst_line_stride,
+ vmirror);
+ else if (width % BLOCK_SIZE_PIXELS_BY4 == 0
+ && height % BLOCK_SIZE_PIXELS_BY4 == 0)
+ return opl_rotate270_u16_by4(src, src_line_stride, width,
+ height, dst, dst_line_stride,
+ vmirror);
+ else
+ return OPLERR_BAD_ARG;
+}
+
+/*
+ * Rotate Counter Clockwise, divide RGB component into 16 row strips, read
+ * non sequentially and write sequentially. This is done in 16 line strips
+ * so that the cache is used better. Cachelines are 8 words = 32 bytes. Pixels
+ * are 2 bytes. The 16 reads will be cache misses, but the next 240 should
+ * be from cache. The writes to the output buffer will be sequential for 16
+ * writes.
+ *
+ * Example:
+ * Input data matrix: output matrix
+ *
+ * 0 | 1 | 2 | 3 | 4 | 4 | 0 | 0 | 3 |
+ * 4 | 3 | 2 | 1 | 0 | 3 | 1 | 9 | 6 |
+ * 6 | 7 | 8 | 9 | 0 | 2 | 2 | 8 | 2 |
+ * 5 | 3 | 2 | 6 | 3 | 1 | 3 | 7 | 3 |
+ * ^ 0 | 4 | 6 | 5 | < Write the input data sequentially
+ * Read first column
+ * Start at the bottom
+ * Move to next column and repeat
+ *
+ * Loop over k decreasing (blocks)
+ * in_block_ptr = src + (((RGB_HEIGHT_PIXELS / BLOCK_SIZE_PIXELS) - k)
+ * * BLOCK_SIZE_PIXELS) * (RGB_WIDTH_BYTES)
+ * out_block_ptr = dst + (((RGB_HEIGHT_PIXELS / BLOCK_SIZE_PIXELS) - k)
+ * * BLOCK_SIZE_BYTES) + (RGB_WIDTH_PIXELS - 1)
+ * * RGB_HEIGHT_PIXELS * BYTES_PER_PIXEL
+ *
+ * Loop over i decreasing (width)
+ * Each pix:
+ * in_block_ptr += RGB_WIDTH_BYTES
+ * out_block_ptr += 4
+ *
+ * Each row of block:
+ * in_block_ptr -= RGB_WIDTH_BYTES * BLOCK_SIZE_PIXELS - 2
+ * out_block_ptr -= RGB_HEIGHT_PIXELS * BYTES_PER_PIXEL + 2 * BLOCK_SIZE_PIXELS;
+ *
+ * It may perform vertical mirroring too depending on the vmirror flag.
+ */
+static int opl_rotate270_u16_by16(const u8 * src, int src_line_stride,
+ int width, int height, u8 * dst,
+ int dst_line_stride, int vmirror)
+{
+ const int BLOCK_SIZE_PIXELS = CACHE_LINE_WORDS * BYTES_PER_WORD
+ / BYTES_PER_PIXEL;
+ const int IN_INDEX = src_line_stride * BLOCK_SIZE_PIXELS
+ - BYTES_PER_PIXEL;
+ const int OUT_INDEX = vmirror ?
+ -dst_line_stride + BYTES_PER_PIXEL * BLOCK_SIZE_PIXELS
+ : dst_line_stride + BYTES_PER_PIXEL * BLOCK_SIZE_PIXELS;
+ const u8 *in_block_ptr;
+ u8 *out_block_ptr;
+ int i, k;
+
+ for (k = height / BLOCK_SIZE_PIXELS; k > 0; k--) {
+ in_block_ptr = src + (((height / BLOCK_SIZE_PIXELS) - k)
+ * BLOCK_SIZE_PIXELS) * src_line_stride;
+ out_block_ptr = dst + (((height / BLOCK_SIZE_PIXELS) - k)
+ * BLOCK_SIZE_PIXELS * BYTES_PER_PIXEL) +
+ (width - 1) * dst_line_stride;
+
+ /*
+ * For vertical mirroring the writing starts from the
+ * first line
+ */
+ if (vmirror)
+ out_block_ptr -= dst_line_stride * (width - 1);
+
+ for (i = width; i > 0; i--) {
+ __asm__ volatile (
+ "ldrh r2, [%0], %4\n\t"
+ "ldrh r3, [%0], %4\n\t"
+ "ldrh r4, [%0], %4\n\t"
+ "ldrh r5, [%0], %4\n\t"
+ "orr r2, r2, r3, lsl #16\n\t"
+ "orr r4, r4, r5, lsl #16\n\t"
+ "str r2, [%1], #4\n\t"
+ "str r4, [%1], #4\n\t"
+
+ "ldrh r2, [%0], %4\n\t"
+ "ldrh r3, [%0], %4\n\t"
+ "ldrh r4, [%0], %4\n\t"
+ "ldrh r5, [%0], %4\n\t"
+ "orr r2, r2, r3, lsl #16\n\t"
+ "orr r4, r4, r5, lsl #16\n\t"
+ "str r2, [%1], #4\n\t"
+ "str r4, [%1], #4\n\t"
+
+ "ldrh r2, [%0], %4\n\t"
+ "ldrh r3, [%0], %4\n\t"
+ "ldrh r4, [%0], %4\n\t"
+ "ldrh r5, [%0], %4\n\t"
+ "orr r2, r2, r3, lsl #16\n\t"
+ "orr r4, r4, r5, lsl #16\n\t"
+ "str r2, [%1], #4\n\t"
+ "str r4, [%1], #4\n\t"
+
+ "ldrh r2, [%0], %4\n\t"
+ "ldrh r3, [%0], %4\n\t"
+ "ldrh r4, [%0], %4\n\t"
+ "ldrh r5, [%0], %4\n\t"
+ "orr r2, r2, r3, lsl #16\n\t"
+ "orr r4, r4, r5, lsl #16\n\t"
+ "str r2, [%1], #4\n\t"
+ "str r4, [%1], #4\n\t"
+
+ :"+r" (in_block_ptr), "+r"(out_block_ptr) /* output */
+ :"0"(in_block_ptr), "1"(out_block_ptr), "r"(src_line_stride) /* input */
+ :"r2", "r3", "r4", "r5", "memory" /* modify */
+ );
+ in_block_ptr -= IN_INDEX;
+ out_block_ptr -= OUT_INDEX;
+ }
+ }
+
+ return OPLERR_SUCCESS;
+}
+
+/*
+ * Rotate Counter Clockwise, divide RGB component into 4 row strips, read
+ * non sequentially and write sequentially. This is done in 4 line strips
+ * so that the cache is used better. Cachelines are 8 words = 32 bytes. Pixels
+ * are 2 bytes. The 4 reads will be cache misses, but the next 60 should
+ * be from cache. The writes to the output buffer will be sequential for 4
+ * writes.
+ *
+ * Example:
+ * Input data matrix: output matrix
+ *
+ * 0 | 1 | 2 | 3 | 4 | 4 | 0 | 0 | 3 |
+ * 4 | 3 | 2 | 1 | 0 | 3 | 1 | 9 | 6 |
+ * 6 | 7 | 8 | 9 | 0 | 2 | 2 | 8 | 2 |
+ * 5 | 3 | 2 | 6 | 3 | 1 | 3 | 7 | 3 |
+ * ^ 0 | 4 | 6 | 5 | < Write the input data sequentially
+ * Read first column
+ * Start at the bottom
+ * Move to next column and repeat
+ *
+ * Loop over k decreasing (blocks)
+ * in_block_ptr = src + (((RGB_HEIGHT_PIXELS / BLOCK_SIZE_PIXELS) - k)
+ * * BLOCK_SIZE_PIXELS) * (RGB_WIDTH_BYTES)
+ * out_block_ptr = dst + (((RGB_HEIGHT_PIXELS / BLOCK_SIZE_PIXELS) - k)
+ * * BLOCK_SIZE_BYTES) + (RGB_WIDTH_PIXELS - 1)
+ * * RGB_HEIGHT_PIXELS * BYTES_PER_PIXEL
+ *
+ * Loop over i decreasing (width)
+ * Each pix:
+ * in_block_ptr += RGB_WIDTH_BYTES
+ * out_block_ptr += 4
+ *
+ * Each row of block:
+ * in_block_ptr -= RGB_WIDTH_BYTES * BLOCK_SIZE_PIXELS - 2
+ * out_block_ptr -= RGB_HEIGHT_PIXELS * BYTES_PER_PIXEL + 2 * BLOCK_SIZE_PIXELS;
+ *
+ * It may perform vertical mirroring too depending on the vmirror flag.
+ */
+static int opl_rotate270_u16_by4(const u8 * src, int src_line_stride, int width,
+ int height, u8 * dst, int dst_line_stride,
+ int vmirror)
+{
+ const int BLOCK_SIZE_PIXELS = CACHE_LINE_WORDS * BYTES_PER_WORD
+ / BYTES_PER_PIXEL / 4;
+ const int IN_INDEX = src_line_stride * BLOCK_SIZE_PIXELS
+ - BYTES_PER_PIXEL;
+ const int OUT_INDEX = vmirror ?
+ -dst_line_stride + BYTES_PER_PIXEL * BLOCK_SIZE_PIXELS
+ : dst_line_stride + BYTES_PER_PIXEL * BLOCK_SIZE_PIXELS;
+ const u8 *in_block_ptr;
+ u8 *out_block_ptr;
+ int i, k;
+
+ for (k = height / BLOCK_SIZE_PIXELS; k > 0; k--) {
+ in_block_ptr = src + (((height / BLOCK_SIZE_PIXELS) - k)
+ * BLOCK_SIZE_PIXELS) * src_line_stride;
+ out_block_ptr = dst + (((height / BLOCK_SIZE_PIXELS) - k)
+ * BLOCK_SIZE_PIXELS * BYTES_PER_PIXEL)
+ + (width - 1) * dst_line_stride;
+
+ /*
+ * For vertical mirroring the writing starts from the
+ * first line
+ */
+ if (vmirror)
+ out_block_ptr -= dst_line_stride * (width - 1);
+
+ for (i = width; i > 0; i--) {
+ __asm__ volatile (
+ "ldrh r2, [%0], %4\n\t"
+ "ldrh r3, [%0], %4\n\t"
+ "ldrh r4, [%0], %4\n\t"
+ "ldrh r5, [%0], %4\n\t"
+ "orr r2, r2, r3, lsl #16\n\t"
+ "orr r4, r4, r5, lsl #16\n\t"
+ "str r2, [%1], #4\n\t"
+ "str r4, [%1], #4\n\t"
+
+ :"+r" (in_block_ptr), "+r"(out_block_ptr) /* output */
+ :"0"(in_block_ptr), "1"(out_block_ptr), "r"(src_line_stride) /* input */
+ :"r2", "r3", "r4", "r5", "memory" /* modify */
+ );
+ in_block_ptr -= IN_INDEX;
+ out_block_ptr -= OUT_INDEX;
+ }
+ }
+
+ return OPLERR_SUCCESS;
+}
+
+EXPORT_SYMBOL(opl_rotate270_u16);
+EXPORT_SYMBOL(opl_rotate270_vmirror_u16);
diff --git a/drivers/media/video/mxc/opl/rotate270_u16_qcif.S b/drivers/media/video/mxc/opl/rotate270_u16_qcif.S
new file mode 100644
index 000000000000..4101eaac4554
--- /dev/null
+++ b/drivers/media/video/mxc/opl/rotate270_u16_qcif.S
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#include <linux/linkage.h>
+
+ .text
+ .align 2
+ENTRY(opl_rotate270_u16_qcif)
+ STMFD sp!,{r4-r10}
+ MOV r12,#0x160
+ MOV r10,#0x90
+ MOV r3,r10,LSR #4
+.L1.16:
+ RSB r2,r3,r10,LSR #4
+ MOV r5,r2,LSL #5
+ MOV r4,r12,LSR #1
+ SMULBB r4,r5,r4
+ ADD r2,r1,r2,LSL #5
+ ADD r5,r2,#0xc000
+ ADD r5,r5,#0x4e0
+ MOV r2,r12,LSR #1
+ ADD r4,r0,r4
+.L1.52:
+ LDRH r6,[r4],r12
+ LDRH r7,[r4],r12
+ LDRH r8,[r4],r12
+ LDRH r9,[r4],r12
+ ORR r6,r6,r7,LSL #16
+ ORR r7,r8,r9,LSL #16
+ STMIA r5!,{r6,r7}
+ SUBS r2,r2,#1
+ LDRH r6,[r4],r12
+ LDRH r7,[r4],r12
+ LDRH r8,[r4],r12
+ LDRH r9,[r4],r12
+ ORR r6,r6,r7,LSL #16
+ ORR r7,r8,r9,LSL #16
+ STMIA r5!,{r6,r7}
+ LDRH r6,[r4],r12
+ LDRH r7,[r4],r12
+ LDRH r8,[r4],r12
+ LDRH r9,[r4],r12
+ ORR r6,r6,r7,LSL #16
+ ORR r7,r8,r9,LSL #16
+ STMIA r5!,{r6,r7}
+ LDRH r6,[r4],r12
+ LDRH r7,[r4],r12
+ LDRH r8,[r4],r12
+ LDRH r9,[r4],r12
+ ORR r6,r6,r7,LSL #16
+ ORR r7,r8,r9,LSL #16
+ SUB r4,r4,#0x1500
+ STMIA r5,{r6,r7}
+ SUB r5,r5,#0x138
+ SUB r4,r4,#0xfe
+ BGT .L1.52
+ SUBS r3,r3,#1
+ BGT .L1.16
+ LDMFD sp!,{r4-r10}
+ BX lr
+ .size opl_rotate270_u16_qcif, . - opl_rotate270_u16_qcif
diff --git a/drivers/media/video/mxc/opl/rotate90_u16.c b/drivers/media/video/mxc/opl/rotate90_u16.c
new file mode 100644
index 000000000000..dd7d445aa952
--- /dev/null
+++ b/drivers/media/video/mxc/opl/rotate90_u16.c
@@ -0,0 +1,220 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/module.h>
+#include "opl.h"
+
+static int opl_rotate90_u16_by16(const u8 * src, int src_line_stride, int width,
+ int height, u8 * dst, int dst_line_stride,
+ int vmirror);
+static int opl_rotate90_u16_by4(const u8 * src, int src_line_stride, int width,
+ int height, u8 * dst, int dst_line_stride,
+ int vmirror);
+static int opl_rotate90_vmirror_u16_both(const u8 * src, int src_line_stride,
+ int width, int height, u8 * dst,
+ int dst_line_stride, int vmirror);
+int opl_rotate90_u16_qcif(const u8 * src, u8 * dst);
+
+int opl_rotate90_u16(const u8 * src, int src_line_stride, int width, int height,
+ u8 * dst, int dst_line_stride)
+{
+ return opl_rotate90_vmirror_u16_both(src, src_line_stride, width,
+ height, dst, dst_line_stride, 0);
+}
+
+int opl_rotate90_vmirror_u16(const u8 * src, int src_line_stride, int width,
+ int height, u8 * dst, int dst_line_stride)
+{
+ return opl_rotate90_vmirror_u16_both(src, src_line_stride, width,
+ height, dst, dst_line_stride, 1);
+}
+
+static int opl_rotate90_vmirror_u16_both(const u8 * src, int src_line_stride,
+ int width, int height, u8 * dst,
+ int dst_line_stride, int vmirror)
+{
+ const int BLOCK_SIZE_PIXELS = CACHE_LINE_WORDS * BYTES_PER_WORD
+ / BYTES_PER_PIXEL;
+ const int BLOCK_SIZE_PIXELS_BY4 = CACHE_LINE_WORDS * BYTES_PER_WORD
+ / BYTES_PER_PIXEL / 4;
+
+ if (!src || !dst)
+ return OPLERR_NULL_PTR;
+
+ if (width == 0 || height == 0 || src_line_stride == 0
+ || dst_line_stride == 0)
+ return OPLERR_BAD_ARG;
+
+ /* The QCIF algorithm doesn't support vertical mirroring */
+ if (vmirror == 0 && width == QCIF_Y_WIDTH && height == QCIF_Y_HEIGHT
+ && src_line_stride == QCIF_Y_WIDTH * 2
+ && src_line_stride == QCIF_Y_HEIGHT * 2)
+ return opl_rotate90_u16_qcif(src, dst);
+ else if (width % BLOCK_SIZE_PIXELS == 0
+ && height % BLOCK_SIZE_PIXELS == 0)
+ return opl_rotate90_u16_by16(src, src_line_stride, width,
+ height, dst, dst_line_stride,
+ vmirror);
+ else if (width % BLOCK_SIZE_PIXELS_BY4 == 0
+ && height % BLOCK_SIZE_PIXELS_BY4 == 0)
+ return opl_rotate90_u16_by4(src, src_line_stride, width, height,
+ dst, dst_line_stride, vmirror);
+ else
+ return OPLERR_BAD_ARG;
+}
+
+/*
+ * Performs clockwise rotation (and possibly vertical mirroring depending
+ * on the vmirror flag) using block sizes of 16x16
+ * The algorithm is similar to 270 degree clockwise rotation algorithm
+ */
+static int opl_rotate90_u16_by16(const u8 * src, int src_line_stride, int width,
+ int height, u8 * dst, int dst_line_stride,
+ int vmirror)
+{
+ const int BLOCK_SIZE_PIXELS = CACHE_LINE_WORDS * BYTES_PER_WORD
+ / BYTES_PER_PIXEL;
+ const int BLOCK_SIZE_BYTES = BYTES_PER_PIXEL * BLOCK_SIZE_PIXELS;
+ const int IN_INDEX = src_line_stride * BLOCK_SIZE_PIXELS
+ + BYTES_PER_PIXEL;
+ const int OUT_INDEX = vmirror ?
+ -dst_line_stride - BLOCK_SIZE_BYTES
+ : dst_line_stride - BLOCK_SIZE_BYTES;
+ const u8 *in_block_ptr;
+ u8 *out_block_ptr;
+ int i, k;
+
+ for (k = height / BLOCK_SIZE_PIXELS; k > 0; k--) {
+ in_block_ptr = src + src_line_stride * (height - 1)
+ - (src_line_stride * BLOCK_SIZE_PIXELS *
+ (height / BLOCK_SIZE_PIXELS - k));
+ out_block_ptr = dst + BYTES_PER_PIXEL * BLOCK_SIZE_PIXELS *
+ ((height / BLOCK_SIZE_PIXELS) - k);
+
+ /*
+ * For vertical mirroring the writing starts from the
+ * bottom line
+ */
+ if (vmirror)
+ out_block_ptr += dst_line_stride * (width - 1);
+
+ for (i = width; i > 0; i--) {
+ __asm__ volatile (
+ "ldrh r2, [%0], -%4\n\t"
+ "ldrh r3, [%0], -%4\n\t"
+ "ldrh r4, [%0], -%4\n\t"
+ "ldrh r5, [%0], -%4\n\t"
+ "orr r2, r2, r3, lsl #16\n\t"
+ "orr r4, r4, r5, lsl #16\n\t"
+ "str r2, [%1], #4\n\t"
+ "str r4, [%1], #4\n\t"
+
+ "ldrh r2, [%0], -%4\n\t"
+ "ldrh r3, [%0], -%4\n\t"
+ "ldrh r4, [%0], -%4\n\t"
+ "ldrh r5, [%0], -%4\n\t"
+ "orr r2, r2, r3, lsl #16\n\t"
+ "orr r4, r4, r5, lsl #16\n\t"
+ "str r2, [%1], #4\n\t"
+ "str r4, [%1], #4\n\t"
+
+ "ldrh r2, [%0], -%4\n\t"
+ "ldrh r3, [%0], -%4\n\t"
+ "ldrh r4, [%0], -%4\n\t"
+ "ldrh r5, [%0], -%4\n\t"
+ "orr r2, r2, r3, lsl #16\n\t"
+ "orr r4, r4, r5, lsl #16\n\t"
+ "str r2, [%1], #4\n\t"
+ "str r4, [%1], #4\n\t"
+
+ "ldrh r2, [%0], -%4\n\t"
+ "ldrh r3, [%0], -%4\n\t"
+ "ldrh r4, [%0], -%4\n\t"
+ "ldrh r5, [%0], -%4\n\t"
+ "orr r2, r2, r3, lsl #16\n\t"
+ "orr r4, r4, r5, lsl #16\n\t"
+ "str r2, [%1], #4\n\t"
+ "str r4, [%1], #4\n\t"
+
+ :"+r" (in_block_ptr), "+r"(out_block_ptr) /* output */
+ :"0"(in_block_ptr), "1"(out_block_ptr), "r"(src_line_stride) /* input */
+ :"r2", "r3", "r4", "r5", "memory" /* modify */
+ );
+ in_block_ptr += IN_INDEX;
+ out_block_ptr += OUT_INDEX;
+ }
+ }
+
+ return OPLERR_SUCCESS;
+}
+
+/*
+ * Performs clockwise rotation (and possibly vertical mirroring depending
+ * on the vmirror flag) using block sizes of 4x4
+ * The algorithm is similar to 270 degree clockwise rotation algorithm
+ */
+static int opl_rotate90_u16_by4(const u8 * src, int src_line_stride, int width,
+ int height, u8 * dst, int dst_line_stride,
+ int vmirror)
+{
+ const int BLOCK_SIZE_PIXELS = CACHE_LINE_WORDS * BYTES_PER_WORD
+ / BYTES_PER_PIXEL / 4;
+ const int BLOCK_SIZE_BYTES = BYTES_PER_PIXEL * BLOCK_SIZE_PIXELS;
+ const int IN_INDEX = src_line_stride * BLOCK_SIZE_PIXELS
+ + BYTES_PER_PIXEL;
+ const int OUT_INDEX = vmirror ?
+ -dst_line_stride - BLOCK_SIZE_BYTES
+ : dst_line_stride - BLOCK_SIZE_BYTES;
+ const u8 *in_block_ptr;
+ u8 *out_block_ptr;
+ int i, k;
+
+ for (k = height / BLOCK_SIZE_PIXELS; k > 0; k--) {
+ in_block_ptr = src + src_line_stride * (height - 1)
+ - (src_line_stride * BLOCK_SIZE_PIXELS *
+ (height / BLOCK_SIZE_PIXELS - k));
+ out_block_ptr = dst + BYTES_PER_PIXEL * BLOCK_SIZE_PIXELS
+ * ((height / BLOCK_SIZE_PIXELS) - k);
+
+ /*
+ * For horizontal mirroring the writing starts from the
+ * bottom line
+ */
+ if (vmirror)
+ out_block_ptr += dst_line_stride * (width - 1);
+
+ for (i = width; i > 0; i--) {
+ __asm__ volatile (
+ "ldrh r2, [%0], -%4\n\t"
+ "ldrh r3, [%0], -%4\n\t"
+ "ldrh r4, [%0], -%4\n\t"
+ "ldrh r5, [%0], -%4\n\t"
+ "orr r2, r2, r3, lsl #16\n\t"
+ "orr r4, r4, r5, lsl #16\n\t"
+ "str r2, [%1], #4\n\t"
+ "str r4, [%1], #4\n\t"
+
+ :"+r" (in_block_ptr), "+r"(out_block_ptr) /* output */
+ :"0"(in_block_ptr), "1"(out_block_ptr), "r"(src_line_stride) /* input */
+ :"r2", "r3", "r4", "r5", "memory" /* modify */
+ );
+ in_block_ptr += IN_INDEX;
+ out_block_ptr += OUT_INDEX;
+ }
+ }
+
+ return OPLERR_SUCCESS;
+}
+
+EXPORT_SYMBOL(opl_rotate90_u16);
+EXPORT_SYMBOL(opl_rotate90_vmirror_u16);
diff --git a/drivers/media/video/mxc/opl/rotate90_u16_qcif.S b/drivers/media/video/mxc/opl/rotate90_u16_qcif.S
new file mode 100644
index 000000000000..8568a9e629e5
--- /dev/null
+++ b/drivers/media/video/mxc/opl/rotate90_u16_qcif.S
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#include <linux/linkage.h>
+
+ .text
+ .align 2
+ENTRY(opl_rotate90_u16_qcif)
+ STMFD sp!,{r4-r10}
+ MOV r12,#0x160
+ MOV r10,#0x90
+ MOV r3,r10,LSR #4
+.L1.216:
+ RSB r2,r3,r10,LSR #4
+ MOV r4,#0x20
+ SMULBB r5,r4,r2
+ MOV r4,#0x1600
+ SMULBB r2,r4,r2
+ ADD r4,r0,#0xc000
+ ADD r4,r4,#0x4a0
+ SUB r4,r4,r2
+ MOV r2,r12,LSR #1
+ ADD r5,r1,r5
+.L1.256:
+ LDRH r6,[r4],-r12
+ LDRH r7,[r4],-r12
+ LDRH r8,[r4],-r12
+ LDRH r9,[r4],-r12
+ ORR r6,r6,r7,LSL #16
+ ORR r7,r8,r9,LSL #16
+ STMIA r5!,{r6,r7}
+ SUBS r2,r2,#1
+ LDRH r6,[r4],-r12
+ LDRH r7,[r4],-r12
+ LDRH r8,[r4],-r12
+ LDRH r9,[r4],-r12
+ ORR r6,r6,r7,LSL #16
+ ORR r7,r8,r9,LSL #16
+ STMIA r5!,{r6,r7}
+ LDRH r6,[r4],-r12
+ LDRH r7,[r4],-r12
+ LDRH r8,[r4],-r12
+ LDRH r9,[r4],-r12
+ ORR r6,r6,r7,LSL #16
+ ORR r7,r8,r9,LSL #16
+ STMIA r5!,{r6,r7}
+ LDRH r6,[r4],-r12
+ LDRH r7,[r4],-r12
+ LDRH r8,[r4],-r12
+ LDRH r9,[r4],-r12
+ ORR r6,r6,r7,LSL #16
+ ORR r7,r8,r9,LSL #16
+ ADD r4,r4,#0x1600
+ STMIA r5!,{r6,r7}
+ ADD r5,r5,#0x100
+ ADD r4,r4,#2
+ BGT .L1.256
+ SUBS r3,r3,#1
+ BGT .L1.216
+ LDMFD sp!,{r4-r10}
+ BX lr
+ .size opl_rotate90_u16_qcif, . - opl_rotate90_u16_qcif
diff --git a/drivers/media/video/mxc/opl/vmirror_u16.c b/drivers/media/video/mxc/opl/vmirror_u16.c
new file mode 100644
index 000000000000..57f805c08a81
--- /dev/null
+++ b/drivers/media/video/mxc/opl/vmirror_u16.c
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/module.h>
+#include <linux/string.h>
+#include "opl.h"
+
+int opl_vmirror_u16(const u8 * src, int src_line_stride, int width, int height,
+ u8 * dst, int dst_line_stride)
+{
+ const u8 *src_row_addr;
+ u8 *dst_row_addr;
+ int i;
+
+ if (!src || !dst)
+ return OPLERR_NULL_PTR;
+
+ if (width == 0 || height == 0 || src_line_stride == 0
+ || dst_line_stride == 0)
+ return OPLERR_BAD_ARG;
+
+ src_row_addr = src;
+ dst_row_addr = dst + (height - 1) * dst_line_stride;
+
+ /* Loop over all rows */
+ for (i = 0; i < height; i++) {
+ /* memcpy each row */
+ memcpy(dst_row_addr, src_row_addr, BYTES_PER_PIXEL * width);
+ src_row_addr += src_line_stride;
+ dst_row_addr -= dst_line_stride;
+ }
+
+ return OPLERR_SUCCESS;
+}
+
+EXPORT_SYMBOL(opl_vmirror_u16);
diff --git a/drivers/media/video/mxc/output/Kconfig b/drivers/media/video/mxc/output/Kconfig
new file mode 100644
index 000000000000..2153ad248907
--- /dev/null
+++ b/drivers/media/video/mxc/output/Kconfig
@@ -0,0 +1,28 @@
+config VIDEO_MXC_IPU_OUTPUT
+ bool "IPU v4l2 support"
+ depends on VIDEO_MXC_OUTPUT && MXC_IPU
+ default y
+ ---help---
+ This is the video4linux2 driver for IPU post processing video output.
+
+config VIDEO_MXC_IPUV1_WVGA_OUTPUT
+ bool "IPUv1 WVGA v4l2 display support"
+ depends on VIDEO_MXC_OUTPUT && MXC_IPU
+ default n
+ ---help---
+ This is the video4linux2 driver for IPUv1 WVGA post processing video output.
+
+config VIDEO_MXC_EMMA_OUTPUT
+ bool
+ depends on VIDEO_MXC_OUTPUT && MXC_EMMA && FB_MXC_SYNC_PANEL
+ default y
+ ---help---
+ This is the video4linux2 driver for EMMA post processing video output.
+
+config VIDEO_MXC_OUTPUT_FBSYNC
+ bool "Synchronize the output with LCDC refresh"
+ depends on VIDEO_MXC_EMMA_OUTPUT
+ default y
+ ---help---
+ Synchronize the post-processing with LCDC EOF (End of Frame) to
+ prevent tearing issue. If unsure, say Y.
diff --git a/drivers/media/video/mxc/output/Makefile b/drivers/media/video/mxc/output/Makefile
new file mode 100644
index 000000000000..1713fa3bf3ab
--- /dev/null
+++ b/drivers/media/video/mxc/output/Makefile
@@ -0,0 +1,11 @@
+ifeq ($(CONFIG_VIDEO_MXC_EMMA_OUTPUT),y)
+ mx27_output-objs := mx27_v4l2_output.o mx27_pp.o
+ obj-$(CONFIG_VIDEO_MXC_OUTPUT) += mx27_output.o
+endif
+
+ifeq ($(CONFIG_VIDEO_MXC_IPU_OUTPUT),y)
+ obj-$(CONFIG_VIDEO_MXC_OUTPUT) += mxc_v4l2_output.o
+endif
+ifeq ($(CONFIG_VIDEO_MXC_IPUV1_WVGA_OUTPUT),y)
+ obj-$(CONFIG_VIDEO_MXC_OUTPUT) += mx31_v4l2_wvga_output.o
+endif
diff --git a/drivers/media/video/mxc/output/mx27_pp.c b/drivers/media/video/mxc/output/mx27_pp.c
new file mode 100644
index 000000000000..a82328015fe2
--- /dev/null
+++ b/drivers/media/video/mxc/output/mx27_pp.c
@@ -0,0 +1,904 @@
+/*
+ * Copyright 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file mx27_pp.c
+ *
+ * @brief MX27 V4L2 Video Output Driver
+ *
+ * Video4Linux2 Output Device using MX27 eMMA Post-processing functionality.
+ *
+ * @ingroup MXC_V4L2_OUTPUT
+ */
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <linux/fb.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <asm/io.h>
+
+#include "mx27_pp.h"
+#include "mxc_v4l2_output.h"
+
+#define SCALE_RETRY 32 /* to be more relax, less precise */
+#define PP_SKIP 1
+#define PP_TBL_MAX 40
+
+static unsigned short scale_tbl[PP_TBL_MAX];
+static int g_hlen, g_vlen;
+
+static emma_pp_cfg g_pp_cfg;
+static int g_disp_num = 0;
+static char pp_dev[] = "emma_pp";
+
+/*!
+ * @brief PP resizing routines
+ */
+static int gcd(int x, int y);
+static int ratio(int x, int y, int *den);
+static int scale_0d(int k, int coeff, int base, int nxt);
+static int scale_1d(int inv, int outv, int k);
+static int scale_1d_smart(int *inv, int *outv, int index);
+static int scale_2d(emma_pp_scale * sz);
+
+static irqreturn_t pp_isr(int irq, void *dev_id);
+static int set_output_addr(emma_pp_cfg * cfg, vout_data * vout);
+static int pphw_reset(void);
+static int pphw_enable(int flag);
+static int pphw_ptr(emma_pp_cfg * cfg);
+static int pphw_outptr(emma_pp_cfg * cfg);
+static int pphw_cfg(emma_pp_cfg * cfg);
+static int pphw_isr(void);
+static void pphw_init(void);
+static void pphw_exit(void);
+
+#define PP_DUMP(reg) pr_debug("%s\t = 0x%08X\n", #reg, __raw_readl(reg))
+void pp_dump(void)
+{
+ PP_DUMP(PP_CNTL);
+ PP_DUMP(PP_INTRCNTL);
+ PP_DUMP(PP_INTRSTATUS);
+ PP_DUMP(PP_SOURCE_Y_PTR);
+ PP_DUMP(PP_SOURCE_CB_PTR);
+ PP_DUMP(PP_SOURCE_CR_PTR);
+ PP_DUMP(PP_DEST_RGB_PTR);
+ PP_DUMP(PP_QUANTIZER_PTR);
+ PP_DUMP(PP_PROCESS_FRAME_PARA);
+ PP_DUMP(PP_SOURCE_FRAME_WIDTH);
+ PP_DUMP(PP_DEST_DISPLAY_WIDTH);
+ PP_DUMP(PP_DEST_IMAGE_SIZE);
+ PP_DUMP(PP_DEST_FRAME_FMT_CNTL);
+ PP_DUMP(PP_RESIZE_INDEX);
+ PP_DUMP(PP_CSC_COEF_0123);
+ PP_DUMP(PP_CSC_COEF_4);
+}
+
+/*!
+ * @brief Set PP input address.
+ * @param ptr The pointer to the Y value of input
+ * @return Zero on success, others on failure
+ */
+int pp_ptr(unsigned long ptr)
+{
+ g_pp_cfg.ptr.y = ptr;
+ g_pp_cfg.ptr.u = g_pp_cfg.ptr.v = g_pp_cfg.ptr.qp = 0;
+
+ return pphw_ptr(&g_pp_cfg);
+}
+
+/*!
+ * @brief Enable or disable PP.
+ * @param flag Zero to disable PP, others to enable PP
+ * @return Zero on success, others on failure
+ */
+int pp_enable(int flag)
+{
+ return pphw_enable(flag);
+}
+
+/*!
+ * @brief Get the display No. of last completed PP frame.
+ * @return The display No. of last completed PP frame.
+ */
+int pp_num_last(void)
+{
+ return (g_disp_num ? 0 : 1);
+}
+
+/*!
+ * @brief Initialize PP.
+ * @param vout Pointer to _vout_data structure
+ * @return Zero on success, others on failure
+ */
+int pp_init(vout_data * vout)
+{
+ pphw_init();
+ pphw_enable(0);
+ enable_irq(MXC_INT_EMMAPP);
+ return request_irq(MXC_INT_EMMAPP, pp_isr, 0, pp_dev, vout);
+}
+
+/*!
+ * @brief Deinitialize PP.
+ * @param vout Pointer to _vout_data structure
+ */
+void pp_exit(vout_data * vout)
+{
+ disable_irq(MXC_INT_EMMAPP);
+ free_irq(MXC_INT_EMMAPP, vout);
+ pphw_enable(0);
+ pphw_exit();
+}
+
+/*!
+ * @brief Configure PP.
+ * @param vout Pointer to _vout_data structure
+ * @return Zero on success, others on failure
+ */
+int pp_cfg(vout_data * vout)
+{
+ if (!vout)
+ return -1;
+
+ /* PP accepts YUV420 input only */
+ if (vout->v2f.fmt.pix.pixelformat != V4L2_PIX_FMT_YUV420) {
+ pr_debug("unsupported pixel format.\n");
+ return -1;
+ }
+
+ g_pp_cfg.operation = 0;
+
+ memset(g_pp_cfg.csc_table, 0, sizeof(g_pp_cfg.csc_table));
+
+ /* Convert output pixel format to PP required format */
+ switch (vout->v4l2_fb.fmt.pixelformat) {
+ case V4L2_PIX_FMT_BGR32:
+ g_pp_cfg.red_width = 8;
+ g_pp_cfg.green_width = 8;
+ g_pp_cfg.blue_width = 8;
+ g_pp_cfg.red_offset = 8;
+ g_pp_cfg.green_offset = 16;
+ g_pp_cfg.blue_offset = 24;
+ g_pp_cfg.rgb_resolution = 32;
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ g_pp_cfg.red_width = 8;
+ g_pp_cfg.green_width = 8;
+ g_pp_cfg.blue_width = 8;
+ g_pp_cfg.red_offset = 24;
+ g_pp_cfg.green_offset = 16;
+ g_pp_cfg.blue_offset = 8;
+ g_pp_cfg.rgb_resolution = 32;
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ g_pp_cfg.red_width = 0;
+ g_pp_cfg.green_width = 0;
+ g_pp_cfg.blue_width = 0;
+ g_pp_cfg.red_offset = 0;
+ g_pp_cfg.green_offset = 0;
+ g_pp_cfg.blue_offset = PP_PIX_YUYV;
+ g_pp_cfg.rgb_resolution = 16;
+ break;
+ case V4L2_PIX_FMT_UYVY:
+ g_pp_cfg.red_width = 0;
+ g_pp_cfg.green_width = 0;
+ g_pp_cfg.blue_width = 0;
+ g_pp_cfg.red_offset = 0;
+ g_pp_cfg.green_offset = 0;
+ g_pp_cfg.blue_offset = PP_PIX_UYVY;
+ g_pp_cfg.rgb_resolution = 16;
+ break;
+ case V4L2_PIX_FMT_RGB565:
+ default:
+ g_pp_cfg.red_width = 5;
+ g_pp_cfg.green_width = 6;
+ g_pp_cfg.blue_width = 5;
+ g_pp_cfg.red_offset = 11;
+ g_pp_cfg.green_offset = 5;
+ g_pp_cfg.blue_offset = 0;
+ g_pp_cfg.rgb_resolution = 16;
+ break;
+ }
+
+ if (vout->ipu_buf[0] != -1)
+ g_pp_cfg.ptr.y =
+ (unsigned int)vout->queue_buf_paddr[vout->ipu_buf[0]];
+ else
+ g_pp_cfg.ptr.y = 0;
+
+ g_pp_cfg.ptr.u = g_pp_cfg.ptr.v = g_pp_cfg.ptr.qp = 0;
+
+ g_pp_cfg.dim.in.width = vout->v2f.fmt.pix.width;
+ g_pp_cfg.dim.in.height = vout->v2f.fmt.pix.height;
+ g_pp_cfg.dim.out.width = vout->crop_current.width;
+ g_pp_cfg.dim.out.height = vout->crop_current.height;
+ g_pp_cfg.dim.num.width = 0;
+ g_pp_cfg.dim.num.height = 0;
+ g_pp_cfg.dim.den.width = 0;
+ g_pp_cfg.dim.den.height = 0;
+
+ if (scale_2d(&g_pp_cfg.dim)) {
+ pr_debug("unsupported resize ratio.\n");
+ return -1;
+ }
+
+ g_pp_cfg.dim.out.width = vout->crop_current.width;
+ g_pp_cfg.dim.out.height = vout->crop_current.height;
+
+ g_pp_cfg.in_y_stride = 0;
+ if (set_output_addr(&g_pp_cfg, vout)) {
+ pr_debug("failed to set pp output address.\n");
+ return -1;
+ }
+
+ return pphw_cfg(&g_pp_cfg);
+}
+
+irqreturn_t mxc_v4l2out_pp_in_irq_handler(int irq, void *dev_id);
+
+/*!
+ * @brief PP IRQ handler.
+ */
+static irqreturn_t pp_isr(int irq, void *dev_id)
+{
+ int status;
+ vout_data *vout = dev_id;
+
+ status = pphw_isr();
+ if ((status & 0x1) == 0) { /* Not frame complete interrupt */
+ pr_debug("not pp frame complete interrupt\n");
+ return IRQ_HANDLED;
+ }
+
+ if (vout->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY) {
+ g_disp_num = g_disp_num ? 0 : 1;
+ g_pp_cfg.outptr = (unsigned int)vout->display_bufs[g_disp_num];
+ pphw_outptr(&g_pp_cfg);
+ }
+
+ return mxc_v4l2out_pp_in_irq_handler(irq, dev_id);
+}
+
+/*!
+ * @brief Set PP output address.
+ * @param cfg Pointer to emma_pp_cfg structure
+ * @param vout Pointer to _vout_data structure
+ * @return Zero on success, others on failure
+ */
+static int set_output_addr(emma_pp_cfg * cfg, vout_data * vout)
+{
+ if (vout->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY) {
+ g_disp_num = 0;
+ cfg->outptr = (unsigned int)vout->display_bufs[g_disp_num];
+ cfg->out_stride = vout->crop_current.width;
+ return 0;
+ } else {
+ struct fb_info *fb;
+
+ fb = registered_fb[vout->output_fb_num[vout->cur_disp_output]];
+ if (!fb)
+ return -1;
+
+ cfg->outptr = fb->fix.smem_start;
+ cfg->outptr += vout->crop_current.top * fb->var.xres_virtual
+ * (fb->var.bits_per_pixel >> 3)
+ + vout->crop_current.left * (fb->var.bits_per_pixel >> 3);
+ cfg->out_stride = fb->var.xres_virtual;
+
+ return 0;
+ }
+}
+
+/*!
+ * @brief Get maximum common divisor.
+ * @param x First input value
+ * @param y Second input value
+ * @return Maximum common divisor of x and y
+ */
+static int gcd(int x, int y)
+{
+ int k;
+
+ if (x < y) {
+ k = x;
+ x = y;
+ y = k;
+ }
+
+ while ((k = x % y)) {
+ x = y;
+ y = k;
+ }
+
+ return y;
+}
+
+/*!
+ * @brief Get ratio.
+ * @param x First input value
+ * @param y Second input value
+ * @param den Denominator of the ratio (corresponding to y)
+ * @return Numerator of the ratio (corresponding to x)
+ */
+static int ratio(int x, int y, int *den)
+{
+ int g;
+
+ if (!x || !y)
+ return 0;
+
+ g = gcd(x, y);
+ *den = y / g;
+
+ return x / g;
+}
+
+/*!
+ * @brief Build PP coefficient entry
+ * Build one or more coefficient entries for PP coefficient table based
+ * on given coefficient.
+ *
+ * @param k The index of the coefficient in coefficient table
+ * @param coeff The weighting coefficient
+ * @param base The base of the coefficient
+ * @param nxt Number of pixels to be read
+ *
+ * @return The index of the next coefficient entry on success
+ * -1 on failure
+ */
+static int scale_0d(int k, int coeff, int base, int nxt)
+{
+ if (k >= PP_TBL_MAX) {
+ /* no more space in table */
+ pr_debug("no space in scale table, k = %d\n", k);
+ return -1;
+ }
+
+ coeff = ((coeff << BC_COEF) + (base >> 1)) / base;
+
+ /*
+ * Valid values for weighting coefficient are 0, 2 to 30, and 31.
+ * A value of 31 is treated as 32 and therefore 31 is an
+ * invalid co-efficient.
+ */
+ if (coeff >= SZ_COEF - 1)
+ coeff--;
+ else if (coeff == 1)
+ coeff++;
+ coeff = coeff << BC_NXT;
+
+ if (nxt < SZ_NXT) {
+ coeff |= nxt;
+ coeff <<= 1;
+ coeff |= 1;
+ } else {
+ /*
+ * src inc field is 2 bit wide, for 4+, use special
+ * code 0:0:1 to prevent dest inc
+ */
+ coeff |= PP_SKIP;
+ coeff <<= 1;
+ coeff |= 1;
+ nxt -= PP_SKIP;
+ do {
+ pr_debug("tbl = %03X\n", coeff);
+ scale_tbl[k++] = coeff;
+ coeff = (nxt > PP_SKIP) ? PP_SKIP : nxt;
+ coeff <<= 1;
+ } while ((nxt -= PP_SKIP) > 0);
+ }
+ pr_debug("tbl = %03X\n", coeff);
+ scale_tbl[k++] = coeff;
+
+ return k;
+}
+
+/*
+ * @brief Build PP coefficient table
+ * Build PP coefficient table for one dimension (width or height)
+ * based on given input and output resolution
+ *
+ * @param inv input resolution
+ * @param outv output resolution
+ * @param k index of free table entry
+ *
+ * @return The index of the next free coefficient entry on success
+ * -1 on failure
+ */
+static int scale_1d(int inv, int outv, int k)
+{
+ int v; /* overflow counter */
+ int coeff, nxt; /* table output */
+
+ if (inv == outv)
+ return scale_0d(k, 1, 1, 1); /* force scaling */
+
+ if (inv * 4 < outv) {
+ pr_debug("upscale err: ratio should be in range 1:1 to 1:4\n");
+ return -1;
+ }
+
+ v = 0;
+ if (inv < outv) {
+ /* upscale: mix <= 2 input pixels per output pixel */
+ do {
+ coeff = outv - v;
+ v += inv;
+ if (v >= outv) {
+ v -= outv;
+ nxt = 1;
+ } else
+ nxt = 0;
+ pr_debug("upscale: coeff = %d/%d nxt = %d\n", coeff,
+ outv, nxt);
+ k = scale_0d(k, coeff, outv, nxt);
+ if (k < 0)
+ return -1;
+ } while (v);
+ } else if (inv >= 2 * outv) {
+ /* PP doesn't support resize ratio > 2:1 except 4:1. */
+ if ((inv != 2 * outv) && (inv != 4 * outv))
+ return -1;
+ /* downscale: >=2:1 bilinear approximation */
+ coeff = inv - 2 * outv;
+ v = 0;
+ nxt = 0;
+ do {
+ v += coeff;
+ nxt = 2;
+ while (v >= outv) {
+ v -= outv;
+ nxt++;
+ }
+ pr_debug("downscale: coeff = 1/2 nxt = %d\n", nxt);
+ k = scale_0d(k, 1, 2, nxt);
+ if (k < 0)
+ return -1;
+ } while (v);
+ } else {
+ /* downscale: bilinear */
+ int in_pos_inc = 2 * outv;
+ int out_pos = inv;
+ int out_pos_inc = 2 * inv;
+ int init_carry = inv - outv;
+ int carry = init_carry;
+
+ v = outv + in_pos_inc;
+ do {
+ coeff = v - out_pos;
+ out_pos += out_pos_inc;
+ carry += out_pos_inc;
+ for (nxt = 0; v < out_pos; nxt++) {
+ v += in_pos_inc;
+ carry -= in_pos_inc;
+ }
+ pr_debug("downscale: coeff = %d/%d nxt = %d\n", coeff,
+ in_pos_inc, nxt);
+ k = scale_0d(k, coeff, in_pos_inc, nxt);
+ if (k < 0)
+ return -1;
+ } while (carry != init_carry);
+ }
+ return k;
+}
+
+/*
+ * @brief Build PP coefficient table
+ * Build PP coefficient table for one dimension (width or height)
+ * based on given input and output resolution. The given input
+ * and output resolution might be not supported due to hardware
+ * limits. In this case this functin rounds the input and output
+ * to closest possible values and return them to caller.
+ *
+ * @param inv input resolution, might be modified after the call
+ * @param outv output resolution, might be modified after the call
+ * @param k index of free table entry
+ *
+ * @return The index of the next free coefficient entry on success
+ * -1 on failure
+ */
+static int scale_1d_smart(int *inv, int *outv, int index)
+{
+ int len, num, den, retry;
+ static int num1, den1;
+
+ if (!inv || !outv)
+ return -1;
+
+ /* Both should be non-zero */
+ if (!(*inv) || !(*outv))
+ return -1;
+
+ retry = SCALE_RETRY;
+
+ do {
+ num = ratio(*inv, *outv, &den);
+ pr_debug("num = %d, den = %d\n", num, den);
+ if (!num)
+ continue;
+
+ if (index != 0) {
+ /*
+ * We are now resizing height. Check to see if the
+ * resize ratio for width can be reused by height
+ */
+ if ((num == num1) && (den == den1))
+ return index;
+ }
+
+ if ((len = scale_1d(num, den, index)) < 0)
+ /* increase output dimension to try another ratio */
+ (*outv)++;
+ else {
+ if (index == 0) {
+ /*
+ * We are now resizing width. The same resize
+ * ratio may be reused by height, so save the
+ * ratio.
+ */
+ num1 = num;
+ den1 = den;
+ }
+ return len;
+ }
+ } while (retry--);
+
+ pr_debug("pp scale err\n");
+ return -1;
+}
+
+/*
+ * @brief Build PP coefficient table for both width and height
+ * Build PP coefficient table for both width and height based on
+ * given resizing ratios.
+ *
+ * @param sz Structure contains resizing ratio informations
+ *
+ * @return 0 on success, others on failure
+ */
+static int scale_2d(emma_pp_scale * sz)
+{
+ int inv, outv;
+
+ /* horizontal resizing. parameter check - must provide in size */
+ if (!sz->in.width)
+ return -1;
+
+ /* Resizing based on num:den */
+ inv = sz->num.width;
+ outv = sz->den.width;
+
+ if ((g_hlen = scale_1d_smart(&inv, &outv, 0)) > 0) {
+ /* Resizing succeeded */
+ sz->den.width = outv;
+ sz->out.width = (sz->in.width * outv) / inv;
+ } else {
+ /* Resizing based on in:out */
+ inv = sz->in.width;
+ outv = sz->out.width;
+
+ if ((g_hlen = scale_1d_smart(&inv, &outv, 0)) > 0) {
+ /* Resizing succeeded */
+ sz->out.width = outv;
+ sz->num.width = ratio(sz->in.width, sz->out.width,
+ &sz->den.width);
+ } else
+ return -1;
+ }
+
+ sz->out.width &= ~1;
+
+ /* vertical resizing. parameter check - must provide in size */
+ if (!sz->in.height)
+ return -1;
+
+ /* Resizing based on num:den */
+ inv = sz->num.height;
+ outv = sz->den.height;
+
+ if ((g_vlen = scale_1d_smart(&inv, &outv, g_hlen)) > 0) {
+ /* Resizing succeeded */
+ sz->den.height = outv;
+ sz->out.height = (sz->in.height * outv) / inv;
+ } else {
+ /* Resizing based on in:out */
+ inv = sz->in.height;
+ outv = sz->out.height;
+
+ if ((g_vlen = scale_1d_smart(&inv, &outv, g_hlen)) > 0) {
+ /* Resizing succeeded */
+ sz->out.height = outv;
+ sz->num.height = ratio(sz->in.height, sz->out.height,
+ &sz->den.height);
+ } else
+ return -1;
+ }
+
+ return 0;
+}
+
+/*!
+ * @brief Set PP resizing registers.
+ * @param sz Pointer to pp scaling structure
+ * @return Zero on success, others on failure
+ */
+static int pphw_scale(emma_pp_scale * sz)
+{
+ __raw_writel((sz->out.width << 16) | sz->out.height,
+ PP_DEST_IMAGE_SIZE);
+ __raw_writel(((g_hlen - 1) << 16) | (g_vlen ==
+ g_hlen ? 0 : (g_hlen << 8)) |
+ (g_vlen - 1), PP_RESIZE_INDEX);
+ for (g_hlen = 0; g_hlen < g_vlen; g_hlen++)
+ __raw_writel(scale_tbl[g_hlen],
+ PP_RESIZE_COEF_TBL + g_hlen * 4);
+
+ return 0;
+}
+
+/*!
+ * @brief Reset PP.
+ * @return Zero on success, others on failure
+ */
+static int pphw_reset(void)
+{
+ int i;
+
+ __raw_writel(0x100, PP_CNTL);
+
+ /* timeout */
+ for (i = 0; i < 1000; i++) {
+ if (!(__raw_readl(PP_CNTL) & 0x100)) {
+ pr_debug("pp reset over\n");
+ break;
+ }
+ }
+
+ /* check reset value */
+ if (__raw_readl(PP_CNTL) != 0x876) {
+ pr_debug("pp reset value err = 0x%08X\n", __raw_readl(PP_CNTL));
+ return -1;
+ }
+
+ return 0;
+}
+
+/*!
+ * @brief Enable or disable PP.
+ * @param flag Zero to disable PP, others to enable PP
+ * @return Zero on success, others on failure
+ */
+static int pphw_enable(int flag)
+{
+ int ret = 0;
+
+ if (flag)
+ __raw_writel(__raw_readl(PP_CNTL) | 1, PP_CNTL);
+ else
+ ret = pphw_reset();
+
+ return ret;
+}
+
+/*!
+ * @brief Set PP input address.
+ * @param cfg The pointer to PP configuration parameter
+ * @return Zero on success, others on failure
+ */
+static int pphw_ptr(emma_pp_cfg * cfg)
+{
+ if (!cfg->ptr.u) {
+ int size;
+
+ /* yuv - packed */
+ size = PP_CALC_Y_SIZE(cfg);
+ cfg->ptr.u = cfg->ptr.y + size;
+ cfg->ptr.v = cfg->ptr.u + (size >> 2);
+
+ /* yuv packed with qp appended */
+ if (!cfg->ptr.qp)
+ cfg->ptr.qp = cfg->ptr.v + (size >> 2);
+ }
+ __raw_writel(cfg->ptr.y, PP_SOURCE_Y_PTR);
+ __raw_writel(cfg->ptr.u, PP_SOURCE_CB_PTR);
+ __raw_writel(cfg->ptr.v, PP_SOURCE_CR_PTR);
+ __raw_writel(cfg->ptr.qp, PP_QUANTIZER_PTR);
+
+ return 0;
+}
+
+/*!
+ * @brief Set PP output address.
+ * @param cfg The pointer to PP configuration parameter
+ * @return Zero on success, others on failure
+ */
+static int pphw_outptr(emma_pp_cfg * cfg)
+{
+ __raw_writel(cfg->outptr, PP_DEST_RGB_PTR);
+ return 0;
+}
+
+/*!
+ * @brief Configuration PP.
+ * @param cfg The pointer to PP configuration parameter
+ * @return Zero on success, others on failure
+ */
+static int pphw_cfg(emma_pp_cfg * cfg)
+{
+ int rt;
+ register int r;
+
+ pphw_scale(&cfg->dim);
+
+ if (!cfg->in_y_stride)
+ cfg->in_y_stride = cfg->dim.in.width;
+
+ if (!cfg->out_stride)
+ cfg->out_stride = cfg->dim.out.width;
+
+ r = __raw_readl(PP_CNTL) & ~EN_MASK;
+
+ /* config parms */
+ r |= cfg->operation & EN_MASK;
+ if (cfg->operation & EN_MACROBLOCK) {
+ /* Macroblock Mode */
+ r |= 0x0200;
+ __raw_writel(0x06, PP_INTRCNTL);
+ } else {
+ /* Frame mode */
+ __raw_writel(0x05, PP_INTRCNTL);
+ }
+
+ if (cfg->red_width | cfg->green_width | cfg->blue_width) {
+ /* color conversion to be performed */
+ r |= EN_CSC;
+ if (!(cfg->red_offset | cfg->green_offset)) {
+ /* auto offset B:G:R LSb to Msb */
+ cfg->green_offset = cfg->blue_offset + cfg->blue_width;
+ cfg->red_offset = cfg->green_offset + cfg->green_width;
+ }
+ if (!cfg->rgb_resolution) {
+ /* derive minimum resolution required */
+ int w, w2;
+
+ w = cfg->red_offset + cfg->red_width;
+ w2 = cfg->blue_offset + cfg->blue_width;
+ if (w < w2)
+ w = w2;
+ w2 = cfg->green_offset + cfg->green_width;
+ if (w < w2)
+ w = w2;
+ if (w > 16)
+ w = 24;
+ else if (w > 8)
+ w = 16;
+ else
+ w = 8;
+ cfg->rgb_resolution = w;
+ }
+ /* 00,11 - 32 bpp, 10 - 16 bpp, 01 - 8 bpp */
+ r &= ~0xC00;
+ if (cfg->rgb_resolution < 32)
+ r |= (cfg->rgb_resolution << 7);
+ __raw_writel((cfg->red_offset << 26) |
+ (cfg->green_offset << 21) |
+ (cfg->blue_offset << 16) |
+ (cfg->red_width << 8) |
+ (cfg->green_width << 4) |
+ cfg->blue_width, PP_DEST_FRAME_FMT_CNTL);
+ } else {
+ /* add YUV422 formatting */
+ static const unsigned int _422[] = {
+ 0x62000888,
+ 0x60100888,
+ 0x43080888,
+ 0x41180888
+ };
+
+ __raw_writel(_422[(cfg->blue_offset >> 3) & 3],
+ PP_DEST_FRAME_FMT_CNTL);
+ cfg->rgb_resolution = 16;
+ r &= ~0xC00;
+ r |= (cfg->rgb_resolution << 7);
+ }
+
+ /* add csc formatting */
+ if (!cfg->csc_table[1]) {
+ static const unsigned short _csc[][6] = {
+ {0x80, 0xb4, 0x2c, 0x5b, 0x0e4, 0},
+ {0x95, 0xcc, 0x32, 0x68, 0x104, 1},
+ {0x80, 0xca, 0x18, 0x3c, 0x0ec, 0},
+ {0x95, 0xe5, 0x1b, 0x44, 0x10e, 1},
+ };
+ memcpy(cfg->csc_table, _csc[cfg->csc_table[0]],
+ sizeof(_csc[0]));
+ }
+ __raw_writel((cfg->csc_table[0] << 24) |
+ (cfg->csc_table[1] << 16) |
+ (cfg->csc_table[2] << 8) |
+ cfg->csc_table[3], PP_CSC_COEF_0123);
+ __raw_writel((cfg->csc_table[5] ? (1 << 9) : 0) | cfg->csc_table[4],
+ PP_CSC_COEF_4);
+
+ __raw_writel(r, PP_CNTL);
+
+ pphw_ptr(cfg);
+ pphw_outptr(cfg);
+
+ /*
+ * #MB in a row = input_width / 16pix
+ * 1 byte per QP per MB
+ * QP must be formatted to be 4-byte aligned
+ * YUV lines are to be 4-byte aligned as well
+ * So Y is 8 byte aligned, as U = V = Y/2 for 420
+ * MPEG MBs are 16x16 anyway
+ */
+ __raw_writel((cfg->dim.in.width << 16) | cfg->dim.in.height,
+ PP_PROCESS_FRAME_PARA);
+ __raw_writel(cfg->in_y_stride | (PP_CALC_QP_WIDTH(cfg) << 16),
+ PP_SOURCE_FRAME_WIDTH);
+
+ /* in bytes */
+ rt = cfg->rgb_resolution >> 3;
+ if (rt == 3)
+ rt = 4;
+ __raw_writel(cfg->out_stride * rt, PP_DEST_DISPLAY_WIDTH);
+
+ pp_dump();
+ return 0;
+}
+
+/*!
+ * @brief Check PP interrupt status.
+ * @return PP interrupt status
+ */
+static int pphw_isr(void)
+{
+ unsigned long status;
+
+ pr_debug("pp: in isr.\n");
+ status = __raw_readl(PP_INTRSTATUS) & 7;
+ if (!status) {
+ pr_debug("pp: not my isr err.\n");
+ return status;
+ }
+
+ if (status & 4)
+ pr_debug("pp: isr state error.\n");
+
+ /* clear interrupt status */
+ __raw_writel(status, PP_INTRSTATUS);
+
+ return status;
+}
+
+static struct clk *emma_clk;
+
+/*!
+ * @brief PP module clock enable
+ */
+static void pphw_init(void)
+{
+ emma_clk = clk_get(NULL, "emma_clk");
+ clk_enable(emma_clk);
+}
+
+/*!
+ * @brief PP module clock disable
+ */
+static void pphw_exit(void)
+{
+ clk_disable(emma_clk);
+ clk_put(emma_clk);
+}
diff --git a/drivers/media/video/mxc/output/mx27_pp.h b/drivers/media/video/mxc/output/mx27_pp.h
new file mode 100644
index 000000000000..7bc65ddda3a1
--- /dev/null
+++ b/drivers/media/video/mxc/output/mx27_pp.h
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file mx27_pp.h
+ *
+ * @brief Header file for MX27 V4L2 Video Output Driver
+ *
+ * @ingroup MXC_V4L2_OUTPUT
+ */
+#ifndef __MX27_PP_H__
+#define __MX27_PP_H__
+
+#include "mxc_v4l2_output.h"
+
+/* PP register definitions */
+#define PP_REG(ofs) (IO_ADDRESS(EMMA_BASE_ADDR) - 0x400 + ofs)
+
+/* Register offsets */
+#define PP_CNTL PP_REG(0x00)
+#define PP_INTRCNTL PP_REG(0x04)
+#define PP_INTRSTATUS PP_REG(0x08)
+#define PP_SOURCE_Y_PTR PP_REG(0x0C)
+#define PP_SOURCE_CB_PTR PP_REG(0x10)
+#define PP_SOURCE_CR_PTR PP_REG(0x14)
+#define PP_DEST_RGB_PTR PP_REG(0x18)
+#define PP_QUANTIZER_PTR PP_REG(0x1C)
+#define PP_PROCESS_FRAME_PARA PP_REG(0x20)
+#define PP_SOURCE_FRAME_WIDTH PP_REG(0x24)
+#define PP_DEST_DISPLAY_WIDTH PP_REG(0x28)
+#define PP_DEST_IMAGE_SIZE PP_REG(0x2C)
+#define PP_DEST_FRAME_FMT_CNTL PP_REG(0x30)
+#define PP_RESIZE_INDEX PP_REG(0x34)
+#define PP_CSC_COEF_0123 PP_REG(0x38)
+#define PP_CSC_COEF_4 PP_REG(0x3C)
+#define PP_RESIZE_COEF_TBL PP_REG(0x100)
+
+/* resize table dimensions
+ dest pixel index left/32 right/32 #src pixels to read
+ 0 [BC_COEF] [BC_COEF] [BC_NXT]
+ :
+ pp_tbl_max-1
+*/
+#define BC_NXT 2
+#define BC_COEF 5
+#define SZ_COEF (1 << BC_COEF)
+#define SZ_NXT (1 << BC_NXT)
+
+/* PP operations */
+#define EN_DEBLOCK 0x02
+#define EN_DERING 0x04
+#define EN_CSC 0x10
+#define EN_MACROBLOCK 0x20
+#define EN_DEF 0x16
+#define EN_MASK 0x36
+#define EN_BIGDATA 0x1000
+#define EN_BIGQP 0x2000
+
+/* PP CSC tables */
+#define CSC_TBL_NONE 0x80
+#define CSC_TBL_REUSE 0x81
+#define CSC_TBL_A1 0x00
+#define CSC_TBL_A0 0x20
+#define CSC_TBL_B1 0x40
+#define CSC_TBL_B0 0x60
+/* converts from 4 decimal fixed point to hw setting & vice versa */
+#define PP_CSC_FP4_2_HW(coeff) ((((coeff) << 7) + 5000) / 10000)
+#define PP_CSC_HW_2_FP4(coeff) ((((coeff) * 10000) + 64) >> 7)
+
+#define PP_PIX_YUYV 0
+#define PP_PIX_YVYU 8
+#define PP_PIX_UYVY 16
+#define PP_PIX_VYUY 24
+
+/* PP size & width calculation macros */
+#define PP_CALC_QP_WIDTH(cfg) \
+ (!((cfg)->operation & (EN_DEBLOCK | EN_DERING)) ? 0 : \
+ (((((cfg)->dim.in.width + 15) >> 4) + 3) & ~3))
+#define PP_CALC_Y_SIZE(cfg) \
+ ((cfg)->in_y_stride * (cfg)->dim.in.height)
+#define PP_CALC_CH_SIZE(cfg) (PP_CALC_Y_SIZE(cfg) >> 2)
+#define PP_CALC_BPP(cfg) \
+ ((cfg)->rgb_resolution > 16 ? 4 : ((cfg)->rgb_resolution >> 3))
+#define PP_CALC_YUV_SIZE(cfg) \
+ ((PP_CALC_Y_SIZE(cfg) * 3) >> 1)
+#define PP_CALC_QP_SIZE(cfg) \
+ (PP_CALC_QP_WIDTH(cfg) * (((cfg)->dim.in.height + 15) >> 4))
+#define PP_CALC_DEST_WIDTH(cfg) \
+ (((cfg)->out_stride & ~1) * PP_CALC_BPP(cfg))
+#define PP_CALC_DEST_SIZE(cfg) \
+ ((cfg)->dim.out.height * PP_CALC_DEST_WIDTH(cfg))
+
+/*
+ * physical addresses for bus mastering
+ * v=0 -> yuv packed
+ * v=0 & qp=0 -> yuv packed with qp appended
+ */
+typedef struct _emma_pp_ptr {
+ unsigned int y; /* Y data (line align8) */
+ unsigned int u; /* U data (line align4) */
+ unsigned int v; /* V data (line align4) */
+ unsigned int qp; /* Quantization (line align4) */
+} emma_pp_ptr;
+
+typedef struct _emma_pp_size {
+ int width;
+ int height;
+} emma_pp_size;
+
+/*
+ * if num.width != 0
+ * resize ratio = num.width : den.width
+ * else
+ * resize ratio = in.width : out.width
+ * same for height
+ */
+typedef struct _emma_pp_scale {
+ emma_pp_size num;
+ emma_pp_size den;
+ emma_pp_size in; /* clip */
+ emma_pp_size out; /* 0 -> same as in */
+} emma_pp_scale;
+
+typedef struct _emma_pp_cfg {
+ unsigned char operation; /* OR of EN_xx defines */
+
+ /*
+ * input color coeff
+ * fixed pt 8 bits, steps of 1/128
+ * csc[5] is 1 or 0 to indicate Y + 16
+ * csc[0] is matrix id 0-3 while csc[1-5]=0
+ */
+ unsigned short csc_table[6];
+
+ /*
+ * Output color (shade width, shade offset, pixel resolution)
+ * Eg. 16bpp RGB565 resolution, the values could be:
+ * red_width = 5, green_width = 6, blue_width = 6
+ * red_offset = 11, green_offset = 5, blue_offset = 0 (defaults)
+ * rgb_resolution = 16 (default)
+ * For YUV422: xxx_width=0, blue_offset=PP_PIX_xxx
+ */
+ unsigned short red_width;
+ unsigned short green_width;
+ unsigned short blue_width;
+ /* if offsets are 0, the offsets are by width LSb to MSb B:G:R */
+ unsigned short red_offset;
+ unsigned short blue_offset;
+ unsigned short green_offset;
+ /* if resolution is 0, the minimum for the sum of widths is chosen */
+ short rgb_resolution; /* 8,16,24 bpp only */
+
+ emma_pp_ptr ptr; /* dma buffer pointers */
+ unsigned int outptr; /* RGB/YUV output */
+ emma_pp_scale dim; /* in/out dimensions */
+
+ /* pixels between two adjacent input Y rows */
+ unsigned short in_y_stride; /* 0 = in_width */
+ /* PIXELS between two adjacent output rows */
+ unsigned short out_stride; /* 0 = out_width */
+} emma_pp_cfg;
+
+int pp_ptr(unsigned long ptr);
+int pp_enable(int flag);
+int pp_cfg(vout_data * vout);
+int pp_init(vout_data * vout);
+int pp_num_last(void);
+void pp_exit(vout_data * vout);
+
+#endif /* __MX27_PP_H__ */
diff --git a/drivers/media/video/mxc/output/mx27_v4l2_output.c b/drivers/media/video/mxc/output/mx27_v4l2_output.c
new file mode 100644
index 000000000000..a00f92d8e979
--- /dev/null
+++ b/drivers/media/video/mxc/output/mx27_v4l2_output.c
@@ -0,0 +1,1442 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file mx27_v4l2_output.c
+ *
+ * @brief MX27 V4L2 Video Output Driver
+ *
+ * Video4Linux2 Output Device using MX27 eMMA Post-processing functionality.
+ *
+ * @ingroup MXC_V4L2_OUTPUT
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/semaphore.h>
+#include <linux/poll.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+
+#include "mxc_v4l2_output.h"
+#include "mx27_pp.h"
+#include "../drivers/video/mxc/mx2fb.h"
+
+#define SDC_FG_FB_FORMAT V4L2_PIX_FMT_RGB565
+
+struct v4l2_output mxc_outputs[1] = {
+ {
+ .index = 0,
+ .name = "DISP0 Video Out",
+ .type = V4L2_OUTPUT_TYPE_ANALOG, /* not really correct,
+ but no other choice */
+ .audioset = 0,
+ .modulator = 0,
+ .std = V4L2_STD_UNKNOWN},
+};
+
+static int video_nr = 16;
+static spinlock_t g_lock = SPIN_LOCK_UNLOCKED;
+vout_data *g_vout;
+
+/* debug counters */
+uint32_t g_irq_cnt;
+uint32_t g_buf_output_cnt;
+uint32_t g_buf_q_cnt;
+uint32_t g_buf_dq_cnt;
+
+#ifdef CONFIG_VIDEO_MXC_OUTPUT_FBSYNC
+static uint32_t g_output_fb = -1;
+static uint32_t g_fb_enabled = 0;
+static uint32_t g_pp_ready = 0;
+
+static int fb_event_notify(struct notifier_block *self,
+ unsigned long action, void *data)
+{
+ struct fb_event *event = data;
+ struct fb_info *info = event->info;
+ unsigned long lock_flags;
+ int blank, i;
+
+ for (i = 0; i < num_registered_fb; i++)
+ if (registered_fb[i] == info)
+ break;
+
+ /*
+ * Check if the event is sent by the framebuffer in which
+ * the video is displayed.
+ */
+ if (i != g_output_fb)
+ return 0;
+
+ switch (action) {
+ case FB_EVENT_BLANK:
+ blank = *(int *)event->data;
+ spin_lock_irqsave(&g_lock, lock_flags);
+ g_fb_enabled = !blank;
+ if (blank && g_pp_ready) {
+ if (pp_enable(1))
+ pr_debug("unable to enable PP\n");
+ g_pp_ready = 0;
+ }
+ spin_unlock_irqrestore(&g_lock, lock_flags);
+ break;
+ case FB_EVENT_MXC_EOF:
+ spin_lock_irqsave(&g_lock, lock_flags);
+ g_fb_enabled = 1;
+ if (g_pp_ready) {
+ if (pp_enable(1))
+ pr_debug("unable to enable PP\n");
+ g_pp_ready = 0;
+ }
+ spin_unlock_irqrestore(&g_lock, lock_flags);
+ break;
+ }
+
+ return 0;
+}
+
+static struct notifier_block fb_event_notifier = {
+ .notifier_call = fb_event_notify,
+};
+
+static struct notifier_block mx2fb_event_notifier = {
+ .notifier_call = fb_event_notify,
+};
+#endif
+
+#define QUEUE_SIZE (MAX_FRAME_NUM + 1)
+static __inline int queue_size(v4l_queue * q)
+{
+ if (q->tail >= q->head)
+ return (q->tail - q->head);
+ else
+ return ((q->tail + QUEUE_SIZE) - q->head);
+}
+
+static __inline int queue_buf(v4l_queue * q, int idx)
+{
+ if (((q->tail + 1) % QUEUE_SIZE) == q->head)
+ return -1; /* queue full */
+ q->list[q->tail] = idx;
+ q->tail = (q->tail + 1) % QUEUE_SIZE;
+ return 0;
+}
+
+static __inline int dequeue_buf(v4l_queue * q)
+{
+ int ret;
+ if (q->tail == q->head)
+ return -1; /* queue empty */
+ ret = q->list[q->head];
+ q->head = (q->head + 1) % QUEUE_SIZE;
+ return ret;
+}
+
+static __inline int peek_next_buf(v4l_queue * q)
+{
+ if (q->tail == q->head)
+ return -1; /* queue empty */
+ return q->list[q->head];
+}
+
+static __inline unsigned long get_jiffies(struct timeval *t)
+{
+ struct timeval cur;
+
+ if (t->tv_usec >= 1000000) {
+ t->tv_sec += t->tv_usec / 1000000;
+ t->tv_usec = t->tv_usec % 1000000;
+ }
+
+ do_gettimeofday(&cur);
+ if ((t->tv_sec < cur.tv_sec)
+ || ((t->tv_sec == cur.tv_sec) && (t->tv_usec < cur.tv_usec)))
+ return jiffies;
+
+ if (t->tv_usec < cur.tv_usec) {
+ cur.tv_sec = t->tv_sec - cur.tv_sec - 1;
+ cur.tv_usec = t->tv_usec + 1000000 - cur.tv_usec;
+ } else {
+ cur.tv_sec = t->tv_sec - cur.tv_sec;
+ cur.tv_usec = t->tv_usec - cur.tv_usec;
+ }
+
+ return jiffies + timeval_to_jiffies(&cur);
+}
+
+/*!
+ * Private function to free buffers
+ *
+ * @param bufs_paddr Array of physical address of buffers to be freed
+ *
+ * @param bufs_vaddr Array of virtual address of buffers to be freed
+ *
+ * @param num_buf Number of buffers to be freed
+ *
+ * @param size Size for each buffer to be free
+ *
+ * @return status 0 success.
+ */
+static int mxc_free_buffers(dma_addr_t bufs_paddr[], void *bufs_vaddr[],
+ int num_buf, int size)
+{
+ int i;
+
+ for (i = 0; i < num_buf; i++) {
+ if (bufs_vaddr[i] != 0) {
+ dma_free_coherent(0, size, bufs_vaddr[i],
+ bufs_paddr[i]);
+ pr_debug("freed @ paddr=0x%08X\n", (u32) bufs_paddr[i]);
+ bufs_paddr[i] = 0;
+ bufs_vaddr[i] = NULL;
+ }
+ }
+ return 0;
+}
+
+/*!
+ * Private function to allocate buffers
+ *
+ * @param bufs_paddr Output array of physical address of buffers allocated
+ *
+ * @param bufs_vaddr Output array of virtual address of buffers allocated
+ *
+ * @param num_buf Input number of buffers to allocate
+ *
+ * @param size Input size for each buffer to allocate
+ *
+ * @return status -0 Successfully allocated a buffer, -ENOBUFS failed.
+ */
+static int mxc_allocate_buffers(dma_addr_t bufs_paddr[], void *bufs_vaddr[],
+ int num_buf, int size)
+{
+ int i;
+
+ for (i = 0; i < num_buf; i++) {
+ bufs_vaddr[i] = dma_alloc_coherent(0, size,
+ &bufs_paddr[i],
+ GFP_DMA | GFP_KERNEL);
+
+ if (bufs_vaddr[i] == 0) {
+ mxc_free_buffers(bufs_paddr, bufs_vaddr, i, size);
+ pr_debug("dma_alloc_coherent failed.\n");
+ return -ENOBUFS;
+ }
+ pr_debug("allocated @ paddr=0x%08X, size=%d.\n",
+ (u32) bufs_paddr[i], size);
+ }
+
+ return 0;
+}
+
+static void mxc_v4l2out_timer_handler(unsigned long arg)
+{
+ int index;
+ unsigned long timeout;
+ unsigned long lock_flags;
+ vout_data *vout = (vout_data *) arg;
+
+ pr_debug("timer handler: %lu\n", jiffies);
+
+ spin_lock_irqsave(&g_lock, lock_flags);
+
+ if ((vout->state == STATE_STREAM_OFF)
+ || (vout->state == STATE_STREAM_STOPPING)) {
+ pr_debug("stream has stopped\n");
+ goto exit0;
+ }
+
+ /*
+ * If timer occurs before PP h/w is ready, then set the state to
+ * paused and the timer will be set again when next buffer is queued
+ * or PP completes.
+ */
+ if (vout->ipu_buf[0] != -1) {
+ pr_debug("buffer is busy\n");
+ vout->state = STATE_STREAM_PAUSED;
+ goto exit0;
+ }
+
+ /* Dequeue buffer and pass to PP */
+ index = dequeue_buf(&vout->ready_q);
+ if (index == -1) { /* no buffers ready, should never occur */
+ pr_debug("mxc_v4l2out: timer - no queued buffers ready\n");
+ goto exit0;
+ }
+
+ g_buf_dq_cnt++;
+ vout->frame_count++;
+ vout->ipu_buf[0] = index;
+
+ if (pp_ptr((unsigned int)vout->queue_buf_paddr[index])) {
+ pr_debug("unable to update buffer\n");
+ goto exit0;
+ }
+#ifdef CONFIG_VIDEO_MXC_OUTPUT_FBSYNC
+ if (g_fb_enabled && (vout->v4l2_fb.flags != V4L2_FBUF_FLAG_OVERLAY))
+ g_pp_ready = 1;
+ else if (pp_enable(1)) {
+ pr_debug("unable to enable PP\n");
+ goto exit0;
+ }
+#else
+ if (pp_enable(1)) {
+ pr_debug("unable to enable PP\n");
+ goto exit0;
+ }
+#endif
+ pr_debug("enabled index %d\n", index);
+
+ /* Setup timer for next buffer */
+ index = peek_next_buf(&vout->ready_q);
+ pr_debug("next index %d\n", index);
+ if (index != -1) {
+ /* if timestamp is 0, then default to 30fps */
+ if ((vout->v4l2_bufs[index].timestamp.tv_sec == 0)
+ && (vout->v4l2_bufs[index].timestamp.tv_usec == 0))
+ timeout =
+ vout->start_jiffies + vout->frame_count * HZ / 30;
+ else
+ timeout =
+ get_jiffies(&vout->v4l2_bufs[index].timestamp);
+
+ if (jiffies >= timeout) {
+ pr_debug("warning: timer timeout already expired.\n");
+ }
+
+ if (mod_timer(&vout->output_timer, timeout))
+ pr_debug("warning: timer was already set\n");
+
+ pr_debug("timer handler next schedule: %lu\n", timeout);
+ } else {
+ vout->state = STATE_STREAM_PAUSED;
+ pr_debug("timer handler paused\n");
+ }
+
+ exit0:
+ spin_unlock_irqrestore(&g_lock, lock_flags);
+}
+
+irqreturn_t mxc_v4l2out_pp_in_irq_handler(int irq, void *dev_id)
+{
+ int last_buf;
+ int index;
+ unsigned long timeout;
+ unsigned long lock_flags;
+ vout_data *vout = dev_id;
+
+ spin_lock_irqsave(&g_lock, lock_flags);
+
+ g_irq_cnt++;
+
+ if ((vout->state == STATE_STREAM_OFF)
+ || (vout->state == STATE_STREAM_STOPPING)) {
+ spin_unlock_irqrestore(&g_lock, lock_flags);
+ return IRQ_HANDLED;
+ }
+
+ if (vout->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY) {
+ struct fb_gwinfo gwinfo;
+
+ gwinfo.enabled = 1;
+ gwinfo.alpha_value = 255;
+ gwinfo.ck_enabled = 0;
+ gwinfo.xpos = vout->crop_current.left;
+ gwinfo.ypos = vout->crop_current.top;
+ gwinfo.base = (unsigned long)vout->display_bufs[pp_num_last()];
+ gwinfo.xres = vout->crop_current.width;
+ gwinfo.yres = vout->crop_current.height;
+ gwinfo.xres_virtual = vout->crop_current.width;
+ gwinfo.vs_reversed = 0;
+
+ mx2_gw_set(&gwinfo);
+ }
+
+ /* Process previous buffer */
+ last_buf = vout->ipu_buf[0];
+ pr_debug("last_buf %d g_irq_cnt %d\n", last_buf, g_irq_cnt);
+ if (last_buf != -1) {
+ g_buf_output_cnt++;
+ vout->v4l2_bufs[last_buf].flags = V4L2_BUF_FLAG_DONE;
+ queue_buf(&vout->done_q, last_buf);
+ vout->ipu_buf[0] = -1;
+ wake_up_interruptible(&vout->v4l_bufq);
+ }
+
+ /* Setup timer for next buffer, when stream has been paused */
+ if ((vout->state == STATE_STREAM_PAUSED)
+ && ((index = peek_next_buf(&vout->ready_q)) != -1)) {
+ pr_debug("next index %d\n", index);
+ /* if timestamp is 0, then default to 30fps */
+ if ((vout->v4l2_bufs[index].timestamp.tv_sec == 0)
+ && (vout->v4l2_bufs[index].timestamp.tv_usec == 0))
+ timeout =
+ vout->start_jiffies + vout->frame_count * HZ / 30;
+ else
+ timeout =
+ get_jiffies(&vout->v4l2_bufs[index].timestamp);
+
+ if (jiffies >= timeout) {
+ pr_debug("warning: timer timeout already expired.\n");
+ }
+
+ vout->state = STATE_STREAM_ON;
+
+ if (mod_timer(&vout->output_timer, timeout))
+ pr_debug("warning: timer was already set\n");
+
+ pr_debug("timer handler next schedule: %lu\n", timeout);
+ }
+
+ spin_unlock_irqrestore(&g_lock, lock_flags);
+
+ return IRQ_HANDLED;
+}
+
+/*!
+ * Start the output stream
+ *
+ * @param vout structure vout_data *
+ *
+ * @return status 0 Success
+ */
+static int mxc_v4l2out_streamon(vout_data * vout)
+{
+ unsigned long timeout;
+ int index;
+
+ if (!vout)
+ return -EINVAL;
+
+ if (vout->state != STATE_STREAM_OFF)
+ return -EBUSY;
+
+ if (queue_size(&vout->ready_q) < 1) {
+ pr_debug("no buffers queued yet!\n");
+ return -EINVAL;
+ }
+
+ vout->ipu_buf[0] = -1;
+
+ if (vout->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY) {
+ /* Free previously allocated buffer */
+ mxc_free_buffers(vout->display_bufs, vout->display_bufs_vaddr,
+ 2, vout->display_buf_size);
+ /* Allocate buffers for foreground */
+ if (mxc_allocate_buffers(vout->display_bufs,
+ vout->display_bufs_vaddr, 2,
+ vout->display_buf_size) < 0) {
+ pr_debug("unable to allocate SDC FG buffers\n");
+ return -ENOMEM;
+ }
+ }
+
+ /* Configure PP */
+ if (pp_cfg(vout)) {
+ /* Free previously allocated buffer */
+ mxc_free_buffers(vout->display_bufs, vout->display_bufs_vaddr,
+ 2, vout->display_buf_size);
+ pr_debug("failed to config PP.\n");
+ return -EINVAL;
+ }
+#ifdef CONFIG_VIDEO_MXC_OUTPUT_FBSYNC
+ g_output_fb = vout->output_fb_num[vout->cur_disp_output];
+ g_fb_enabled = 0;
+ g_pp_ready = 0;
+ fb_register_client(&fb_event_notifier);
+ mx2fb_register_client(&mx2fb_event_notifier);
+#endif
+ vout->frame_count = 0;
+ vout->state = STATE_STREAM_ON;
+ index = peek_next_buf(&vout->ready_q);
+
+ /* if timestamp is 0, then default to 30fps */
+ if ((vout->v4l2_bufs[index].timestamp.tv_sec == 0)
+ && (vout->v4l2_bufs[index].timestamp.tv_usec == 0))
+ timeout = jiffies;
+ else
+ timeout = get_jiffies(&vout->v4l2_bufs[index].timestamp);
+
+ if (jiffies >= timeout) {
+ pr_debug("warning: timer timeout already expired.\n");
+ }
+
+ vout->start_jiffies = vout->output_timer.expires = timeout;
+ pr_debug("STREAMON:Add timer %d timeout @ %lu jiffies, current = %lu\n",
+ index, timeout, jiffies);
+ add_timer(&vout->output_timer);
+
+ return 0;
+}
+
+/*!
+ * Shut down the voutera
+ *
+ * @param vout structure vout_data *
+ *
+ * @return status 0 Success
+ */
+static int mxc_v4l2out_streamoff(vout_data * vout)
+{
+ int i, retval = 0;
+ unsigned long lock_flag = 0;
+
+ if (!vout)
+ return -EINVAL;
+
+ if (vout->state == STATE_STREAM_OFF) {
+ return 0;
+ }
+
+ spin_lock_irqsave(&g_lock, lock_flag);
+
+ del_timer(&vout->output_timer);
+ pp_enable(0); /* Disable PP */
+
+ if (vout->state == STATE_STREAM_ON) {
+ vout->state = STATE_STREAM_STOPPING;
+ }
+
+ spin_unlock_irqrestore(&g_lock, lock_flag);
+
+ vout->ready_q.head = vout->ready_q.tail = 0;
+ vout->done_q.head = vout->done_q.tail = 0;
+ for (i = 0; i < vout->buffer_cnt; i++) {
+ vout->v4l2_bufs[i].flags = 0;
+ vout->v4l2_bufs[i].timestamp.tv_sec = 0;
+ vout->v4l2_bufs[i].timestamp.tv_usec = 0;
+ }
+
+ vout->state = STATE_STREAM_OFF;
+
+ if (vout->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY) {
+ struct fb_gwinfo gwinfo;
+
+ /* Disable graphic window */
+ gwinfo.enabled = 0;
+ mx2_gw_set(&gwinfo);
+ }
+#ifdef CONFIG_VIDEO_MXC_OUTPUT_FBSYNC
+ g_output_fb = -1;
+ g_fb_enabled = 0;
+ g_pp_ready = 0;
+ fb_unregister_client(&fb_event_notifier);
+ mx2fb_unregister_client(&mx2fb_event_notifier);
+#endif
+
+ mxc_free_buffers(vout->display_bufs, vout->display_bufs_vaddr,
+ 2, vout->display_buf_size);
+
+ return retval;
+}
+
+/*
+ * Valid whether the palette is supported
+ *
+ * @param palette V4L2_PIX_FMT_RGB565, V4L2_PIX_FMT_BGR24 or V4L2_PIX_FMT_BGR32
+ *
+ * @return 1 if supported, 0 if failed
+ */
+static inline int valid_mode(u32 palette)
+{
+ return (palette == V4L2_PIX_FMT_YUV420);
+}
+
+/*
+ * Returns bits per pixel for given pixel format
+ *
+ * @param pixelformat V4L2_PIX_FMT_RGB565, V4L2_PIX_FMT_BGR24 or V4L2_PIX_FMT_BGR32
+ *
+ * @return bits per pixel of pixelformat
+ */
+static u32 fmt_to_bpp(u32 pixelformat)
+{
+ u32 bpp;
+
+ switch (pixelformat) {
+ case V4L2_PIX_FMT_RGB565:
+ bpp = 16;
+ break;
+ case V4L2_PIX_FMT_BGR24:
+ case V4L2_PIX_FMT_RGB24:
+ bpp = 24;
+ break;
+ case V4L2_PIX_FMT_BGR32:
+ case V4L2_PIX_FMT_RGB32:
+ bpp = 32;
+ break;
+ default:
+ bpp = 8;
+ break;
+ }
+ return bpp;
+}
+
+/*
+ * V4L2 - Handles VIDIOC_G_FMT Ioctl
+ *
+ * @param vout structure vout_data *
+ *
+ * @param v4l2_format structure v4l2_format *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_v4l2out_g_fmt(vout_data * vout, struct v4l2_format *f)
+{
+ if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ return -EINVAL;
+ }
+ *f = vout->v2f;
+ return 0;
+}
+
+/*
+ * V4L2 - Handles VIDIOC_S_FMT Ioctl
+ *
+ * @param vout structure vout_data *
+ *
+ * @param v4l2_format structure v4l2_format *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_v4l2out_s_fmt(vout_data * vout, struct v4l2_format *f)
+{
+ int retval = 0;
+ u32 size = 0;
+ u32 bytesperline;
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ retval = -EINVAL;
+ goto err0;
+ }
+ if (!valid_mode(f->fmt.pix.pixelformat)) {
+ pr_debug("pixel format not supported\n");
+ retval = -EINVAL;
+ goto err0;
+ }
+
+ bytesperline = (f->fmt.pix.width * fmt_to_bpp(f->fmt.pix.pixelformat)) /
+ 8;
+ if (f->fmt.pix.bytesperline < bytesperline) {
+ f->fmt.pix.bytesperline = bytesperline;
+ } else {
+ bytesperline = f->fmt.pix.bytesperline;
+ }
+
+ switch (f->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_YUV422P:
+ /* byteperline for YUV planar formats is for
+ Y plane only */
+ size = bytesperline * f->fmt.pix.height * 2;
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ size = (bytesperline * f->fmt.pix.height * 3) / 2;
+ break;
+ default:
+ size = bytesperline * f->fmt.pix.height;
+ break;
+ }
+
+ /* Return the actual size of the image to the app */
+ f->fmt.pix.sizeimage = size;
+
+ vout->v2f.fmt.pix.sizeimage = size;
+ vout->v2f.fmt.pix.width = f->fmt.pix.width;
+ vout->v2f.fmt.pix.height = f->fmt.pix.height;
+ vout->v2f.fmt.pix.pixelformat = f->fmt.pix.pixelformat;
+ vout->v2f.fmt.pix.bytesperline = f->fmt.pix.bytesperline;
+
+ retval = 0;
+ err0:
+ return retval;
+}
+
+/*
+ * V4L2 - Handles VIDIOC_G_CTRL Ioctl
+ *
+ * @param vout structure vout_data *
+ *
+ * @param c structure v4l2_control *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_get_v42lout_control(vout_data * vout, struct v4l2_control *c)
+{
+ switch (c->id) {
+ case V4L2_CID_HFLIP:
+ return (vout->rotate & IPU_ROTATE_HORIZ_FLIP) ? 1 : 0;
+ case V4L2_CID_VFLIP:
+ return (vout->rotate & IPU_ROTATE_VERT_FLIP) ? 1 : 0;
+ case (V4L2_CID_PRIVATE_BASE + 1):
+ return vout->rotate;
+ default:
+ return -EINVAL;
+ }
+}
+
+/*
+ * V4L2 - Handles VIDIOC_S_CTRL Ioctl
+ *
+ * @param vout structure vout_data *
+ *
+ * @param c structure v4l2_control *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_set_v42lout_control(vout_data * vout, struct v4l2_control *c)
+{
+ switch (c->id) {
+ case V4L2_CID_HFLIP:
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_MXC_ROT:
+ return 0;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*!
+ * V4L2 interface - open function
+ *
+ * @param inode structure inode *
+ *
+ * @param file structure file *
+ *
+ * @return status 0 success, ENODEV invalid device instance,
+ * ENODEV timeout, ERESTARTSYS interrupted by user
+ */
+static int mxc_v4l2out_open(struct inode *inode, struct file *file)
+{
+ struct video_device *dev = video_devdata(file);
+ vout_data *vout = video_get_drvdata(dev);
+ int err;
+
+ if (!vout) {
+ pr_info("Internal error, vout_data not found!\n");
+ return -ENODEV;
+ }
+
+ down(&vout->busy_lock);
+
+ err = -EINTR;
+ if (signal_pending(current))
+ goto oops;
+
+ if (vout->open_count++ == 0) {
+ pp_init(vout);
+
+ init_waitqueue_head(&vout->v4l_bufq);
+
+ init_timer(&vout->output_timer);
+ vout->output_timer.function = mxc_v4l2out_timer_handler;
+ vout->output_timer.data = (unsigned long)vout;
+
+ vout->state = STATE_STREAM_OFF;
+ g_irq_cnt = g_buf_output_cnt = g_buf_q_cnt = g_buf_dq_cnt = 0;
+
+ }
+
+ file->private_data = dev;
+ up(&vout->busy_lock);
+ return 0;
+
+ oops:
+ up(&vout->busy_lock);
+ return err;
+}
+
+/*!
+ * V4L2 interface - close function
+ *
+ * @param inode struct inode *
+ *
+ * @param file struct file *
+ *
+ * @return 0 success
+ */
+static int mxc_v4l2out_close(struct inode *inode, struct file *file)
+{
+ struct video_device *dev = file->private_data;
+ vout_data *vout = video_get_drvdata(dev);
+
+ if (--vout->open_count == 0) {
+ pr_debug("release resource\n");
+
+ pp_exit(vout);
+ if (vout->state != STATE_STREAM_OFF)
+ mxc_v4l2out_streamoff(vout);
+
+ file->private_data = NULL;
+
+ mxc_free_buffers(vout->queue_buf_paddr,
+ vout->queue_buf_vaddr,
+ vout->buffer_cnt, vout->queue_buf_size);
+ vout->buffer_cnt = 0;
+ mxc_free_buffers(vout->display_bufs,
+ vout->display_bufs_vaddr,
+ 2, vout->display_buf_size);
+
+ /* capture off */
+ wake_up_interruptible(&vout->v4l_bufq);
+ }
+
+ return 0;
+}
+
+/*!
+ * V4L2 interface - ioctl function
+ *
+ * @param inode struct inode *
+ *
+ * @param file struct file *
+ *
+ * @param ioctlnr unsigned int
+ *
+ * @param arg void *
+ *
+ * @return 0 success, ENODEV for invalid device instance,
+ * -1 for other errors.
+ */
+static int
+mxc_v4l2out_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int ioctlnr, void *arg)
+{
+ struct video_device *dev = file->private_data;
+ vout_data *vout = video_get_drvdata(dev);
+ int retval = 0;
+ int i = 0;
+
+ if (!vout)
+ return -EBADF;
+
+ /* make this _really_ smp-safe */
+ if (down_interruptible(&vout->busy_lock))
+ return -EBUSY;
+
+ switch (ioctlnr) {
+ case VIDIOC_QUERYCAP:
+ {
+ struct v4l2_capability *cap = arg;
+ strcpy(cap->driver, "mxc_v4l2_output");
+ cap->version = 0;
+ cap->capabilities =
+ V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
+ cap->card[0] = '\0';
+ cap->bus_info[0] = '\0';
+ retval = 0;
+ break;
+ }
+ case VIDIOC_G_FMT:
+ {
+ struct v4l2_format *gf = arg;
+ retval = mxc_v4l2out_g_fmt(vout, gf);
+ break;
+ }
+ case VIDIOC_S_FMT:
+ {
+ struct v4l2_format *sf = arg;
+ if (vout->state != STATE_STREAM_OFF) {
+ retval = -EBUSY;
+ break;
+ }
+ retval = mxc_v4l2out_s_fmt(vout, sf);
+ break;
+ }
+ case VIDIOC_REQBUFS:
+ {
+ struct v4l2_requestbuffers *req = arg;
+ if ((req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) ||
+ (req->memory != V4L2_MEMORY_MMAP)) {
+ pr_debug
+ ("VIDIOC_REQBUFS: incorrect buffer type\n");
+ retval = -EINVAL;
+ break;
+ }
+
+ if (req->count == 0)
+ mxc_v4l2out_streamoff(vout);
+
+ if (vout->state == STATE_STREAM_OFF) {
+ if (vout->queue_buf_paddr[0] != 0) {
+ mxc_free_buffers(vout->queue_buf_paddr,
+ vout->queue_buf_vaddr,
+ vout->buffer_cnt,
+ vout->queue_buf_size);
+ pr_debug
+ ("VIDIOC_REQBUFS: freed buffers\n");
+ }
+ vout->buffer_cnt = 0;
+ } else {
+ pr_debug("VIDIOC_REQBUFS: Buffer is in use\n");
+ retval = -EBUSY;
+ break;
+ }
+
+ if (req->count == 0)
+ break;
+
+ if (req->count < MIN_FRAME_NUM) {
+ req->count = MIN_FRAME_NUM;
+ } else if (req->count > MAX_FRAME_NUM) {
+ req->count = MAX_FRAME_NUM;
+ }
+ vout->buffer_cnt = req->count;
+ vout->queue_buf_size =
+ PAGE_ALIGN(vout->v2f.fmt.pix.sizeimage);
+
+ retval = mxc_allocate_buffers(vout->queue_buf_paddr,
+ vout->queue_buf_vaddr,
+ vout->buffer_cnt,
+ vout->queue_buf_size);
+ if (retval < 0)
+ break;
+
+ /* Init buffer queues */
+ vout->done_q.head = 0;
+ vout->done_q.tail = 0;
+ vout->ready_q.head = 0;
+ vout->ready_q.tail = 0;
+
+ for (i = 0; i < vout->buffer_cnt; i++) {
+ memset(&(vout->v4l2_bufs[i]), 0,
+ sizeof(vout->v4l2_bufs[i]));
+ vout->v4l2_bufs[i].flags = 0;
+ vout->v4l2_bufs[i].memory = V4L2_MEMORY_MMAP;
+ vout->v4l2_bufs[i].index = i;
+ vout->v4l2_bufs[i].type =
+ V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ vout->v4l2_bufs[i].length =
+ PAGE_ALIGN(vout->v2f.fmt.pix.sizeimage);
+ vout->v4l2_bufs[i].m.offset =
+ (unsigned long)vout->queue_buf_paddr[i];
+ vout->v4l2_bufs[i].timestamp.tv_sec = 0;
+ vout->v4l2_bufs[i].timestamp.tv_usec = 0;
+ }
+ break;
+ }
+ case VIDIOC_QUERYBUF:
+ {
+ struct v4l2_buffer *buf = arg;
+ u32 type = buf->type;
+ int index = buf->index;
+
+ if ((type != V4L2_BUF_TYPE_VIDEO_OUTPUT) ||
+ (index >= vout->buffer_cnt)) {
+ pr_debug
+ ("VIDIOC_QUERYBUFS: incorrect buffer type\n");
+ retval = -EINVAL;
+ break;
+ }
+ down(&vout->param_lock);
+ memcpy(buf, &(vout->v4l2_bufs[index]), sizeof(*buf));
+ up(&vout->param_lock);
+ break;
+ }
+ case VIDIOC_QBUF:
+ {
+ struct v4l2_buffer *buf = arg;
+ int index = buf->index;
+ unsigned long lock_flags;
+ unsigned long timeout;
+
+ if ((buf->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) ||
+ (index >= vout->buffer_cnt) || (buf->flags != 0)) {
+ retval = -EINVAL;
+ break;
+ }
+
+ pr_debug("VIDIOC_QBUF: %d\n", buf->index);
+
+ spin_lock_irqsave(&g_lock, lock_flags);
+
+ memcpy(&(vout->v4l2_bufs[index]), buf, sizeof(*buf));
+ vout->v4l2_bufs[index].flags |= V4L2_BUF_FLAG_QUEUED;
+
+ g_buf_q_cnt++;
+ queue_buf(&vout->ready_q, index);
+
+ if (vout->state == STATE_STREAM_PAUSED) {
+ index = peek_next_buf(&vout->ready_q);
+
+ /* if timestamp is 0, then default to 30fps */
+ if ((vout->v4l2_bufs[index].timestamp.tv_sec ==
+ 0)
+ && (vout->v4l2_bufs[index].timestamp.
+ tv_usec == 0))
+ timeout =
+ vout->start_jiffies +
+ vout->frame_count * HZ / 30;
+ else
+ timeout =
+ get_jiffies(&vout->v4l2_bufs[index].
+ timestamp);
+
+ if (jiffies >= timeout) {
+ pr_debug
+ ("warning: timer timeout already expired.\n");
+ }
+
+ vout->output_timer.expires = timeout;
+ pr_debug
+ ("QBUF:Add timer %d timeout @ %lu jiffies, "
+ "current = %lu\n", index, timeout,
+ jiffies);
+ add_timer(&vout->output_timer);
+ vout->state = STATE_STREAM_ON;
+ }
+
+ spin_unlock_irqrestore(&g_lock, lock_flags);
+ break;
+ }
+ case VIDIOC_DQBUF:
+ {
+ struct v4l2_buffer *buf = arg;
+ int idx;
+
+ pr_debug("VIDIOC_DQBUF: q size = %d\n",
+ queue_size(&vout->done_q));
+
+ if ((queue_size(&vout->done_q) == 0) &&
+ (file->f_flags & O_NONBLOCK)) {
+ retval = -EAGAIN;
+ break;
+ }
+
+ if (!wait_event_interruptible_timeout(vout->v4l_bufq,
+ queue_size(&vout->
+ done_q)
+ != 0, 10 * HZ)) {
+ pr_debug("VIDIOC_DQBUF: timeout\n");
+ retval = -ETIME;
+ break;
+ } else if (signal_pending(current)) {
+ pr_debug("VIDIOC_DQBUF: interrupt received\n");
+ retval = -ERESTARTSYS;
+ break;
+ }
+ idx = dequeue_buf(&vout->done_q);
+ if (idx == -1) { /* No frame free */
+ pr_debug
+ ("VIDIOC_DQBUF: no free buffers, returning\n");
+ retval = -EAGAIN;
+ break;
+ }
+ if ((vout->v4l2_bufs[idx].flags & V4L2_BUF_FLAG_DONE) ==
+ 0)
+ pr_debug
+ ("VIDIOC_DQBUF: buffer in done q, but not "
+ "flagged as done\n");
+
+ vout->v4l2_bufs[idx].flags = 0;
+ memcpy(buf, &(vout->v4l2_bufs[idx]), sizeof(*buf));
+ pr_debug("VIDIOC_DQBUF: %d\n", buf->index);
+ break;
+ }
+ case VIDIOC_STREAMON:
+ {
+ retval = mxc_v4l2out_streamon(vout);
+ break;
+ }
+ case VIDIOC_STREAMOFF:
+ {
+ retval = mxc_v4l2out_streamoff(vout);
+ break;
+ }
+ case VIDIOC_G_CTRL:
+ {
+ retval = mxc_get_v42lout_control(vout, arg);
+ break;
+ }
+ case VIDIOC_S_CTRL:
+ {
+ retval = mxc_set_v42lout_control(vout, arg);
+ break;
+ }
+ case VIDIOC_CROPCAP:
+ {
+ struct v4l2_cropcap *cap = arg;
+
+ if (cap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ retval = -EINVAL;
+ break;
+ }
+ cap->bounds = vout->crop_bounds[vout->cur_disp_output];
+ cap->defrect = vout->crop_bounds[vout->cur_disp_output];
+ retval = 0;
+ break;
+ }
+ case VIDIOC_G_CROP:
+ {
+ struct v4l2_crop *crop = arg;
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ retval = -EINVAL;
+ break;
+ }
+ crop->c = vout->crop_current;
+ break;
+ }
+ case VIDIOC_S_CROP:
+ {
+ struct v4l2_crop *crop = arg;
+ struct v4l2_rect *b =
+ &(vout->crop_bounds[vout->cur_disp_output]);
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ retval = -EINVAL;
+ break;
+ }
+ if (crop->c.height < 0) {
+ retval = -EINVAL;
+ break;
+ }
+ if (crop->c.width < 0) {
+ retval = -EINVAL;
+ break;
+ }
+
+ if (crop->c.top < b->top)
+ crop->c.top = b->top;
+ if (crop->c.top > b->top + b->height)
+ crop->c.top = b->top + b->height;
+ if (crop->c.height > b->top - crop->c.top + b->height)
+ crop->c.height =
+ b->top - crop->c.top + b->height;
+
+ if (crop->c.left < b->left)
+ crop->c.top = b->left;
+ if (crop->c.left > b->left + b->width)
+ crop->c.top = b->left + b->width;
+ if (crop->c.width > b->left - crop->c.left + b->width)
+ crop->c.width =
+ b->left - crop->c.left + b->width;
+
+ /* stride line limitation */
+ crop->c.height -= crop->c.height % 8;
+ crop->c.width -= crop->c.width % 8;
+
+ vout->crop_current = crop->c;
+
+ vout->display_buf_size = vout->crop_current.width *
+ vout->crop_current.height;
+ vout->display_buf_size *=
+ fmt_to_bpp(SDC_FG_FB_FORMAT) / 8;
+ break;
+ }
+ case VIDIOC_ENUMOUTPUT:
+ {
+ struct v4l2_output *output = arg;
+
+ if ((output->index >= 2) ||
+ (vout->output_enabled[output->index] == false)) {
+ retval = -EINVAL;
+ break;
+ }
+
+ *output = mxc_outputs[0];
+ output->name[4] = '0' + output->index;
+ break;
+ }
+ case VIDIOC_G_OUTPUT:
+ {
+ int *p_output_num = arg;
+
+ *p_output_num = vout->cur_disp_output;
+ break;
+ }
+ case VIDIOC_S_OUTPUT:
+ {
+ int *p_output_num = arg;
+
+ if ((*p_output_num >= 2) ||
+ (vout->output_enabled[*p_output_num] == false)) {
+ retval = -EINVAL;
+ break;
+ }
+
+ if (vout->state != STATE_STREAM_OFF) {
+ retval = -EBUSY;
+ break;
+ }
+
+ vout->cur_disp_output = *p_output_num;
+ break;
+ }
+ case VIDIOC_G_FBUF:
+ {
+ struct v4l2_framebuffer *fb = arg;
+ *fb = vout->v4l2_fb;
+ break;
+ }
+ case VIDIOC_S_FBUF:
+ {
+ struct v4l2_framebuffer *fb = arg;
+ vout->v4l2_fb = *fb;
+ vout->v4l2_fb.capability = V4L2_FBUF_CAP_EXTERNOVERLAY;
+ break;
+ }
+ case VIDIOC_ENUM_FMT:
+ case VIDIOC_TRY_FMT:
+ case VIDIOC_QUERYCTRL:
+ case VIDIOC_G_PARM:
+ case VIDIOC_ENUMSTD:
+ case VIDIOC_G_STD:
+ case VIDIOC_S_STD:
+ case VIDIOC_G_TUNER:
+ case VIDIOC_S_TUNER:
+ case VIDIOC_G_FREQUENCY:
+ case VIDIOC_S_FREQUENCY:
+ default:
+ retval = -EINVAL;
+ break;
+ }
+
+ up(&vout->busy_lock);
+ return retval;
+}
+
+/*
+ * V4L2 interface - ioctl function
+ *
+ * @return None
+ */
+static int
+mxc_v4l2out_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(inode, file, cmd, arg, mxc_v4l2out_do_ioctl);
+}
+
+/*!
+ * V4L2 interface - mmap function
+ *
+ * @param file structure file *
+ *
+ * @param vma structure vm_area_struct *
+ *
+ * @return status 0 Success, EINTR busy lock error,
+ * ENOBUFS remap_page error
+ */
+static int mxc_v4l2out_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct video_device *dev = file->private_data;
+ unsigned long start = vma->vm_start;
+ unsigned long size = vma->vm_end - vma->vm_start;
+ int res = 0;
+ vout_data *vout = video_get_drvdata(dev);
+
+ /* make this _really_ smp-safe */
+ if (down_interruptible(&vout->busy_lock))
+ return -EINTR;
+
+ /* make buffers write-thru cacheable */
+ vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) &
+ ~L_PTE_BUFFERABLE);
+
+ if (remap_pfn_range(vma, start, vma->vm_pgoff, size, vma->vm_page_prot)) {
+ pr_debug("mxc_mmap(V4L)i - remap_pfn_range failed\n");
+ res = -ENOBUFS;
+ goto mxc_mmap_exit;
+ }
+
+ mxc_mmap_exit:
+ up(&vout->busy_lock);
+ return res;
+}
+
+/*!
+ * V4L2 interface - poll function
+ *
+ * @param file structure file *
+ *
+ * @param wait structure poll_table *
+ *
+ * @return status POLLIN | POLLRDNORM
+ */
+static unsigned int mxc_v4l2out_poll(struct file *file, poll_table * wait)
+{
+ struct video_device *dev = file->private_data;
+ vout_data *vout = video_get_drvdata(dev);
+
+ wait_queue_head_t *queue = NULL;
+ int res = POLLIN | POLLRDNORM;
+
+ if (down_interruptible(&vout->busy_lock))
+ return -EINTR;
+
+ queue = &vout->v4l_bufq;
+ poll_wait(file, queue, wait);
+
+ up(&vout->busy_lock);
+ return res;
+}
+
+static struct file_operations mxc_v4l2out_fops = {
+ .owner = THIS_MODULE,
+ .open = mxc_v4l2out_open,
+ .release = mxc_v4l2out_close,
+ .ioctl = mxc_v4l2out_ioctl,
+ .mmap = mxc_v4l2out_mmap,
+ .poll = mxc_v4l2out_poll,
+};
+
+static struct video_device mxc_v4l2out_template = {
+ .name = "MXC Video Output",
+ .vfl_type = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING,
+ .fops = &mxc_v4l2out_fops,
+ .release = video_device_release,
+};
+
+/*!
+ * Probe routine for the framebuffer driver. It is called during the
+ * driver binding process. The following functions are performed in
+ * this routine: Framebuffer initialization, Memory allocation and
+ * mapping, Framebuffer registration, IPU initialization.
+ *
+ * @return Appropriate error code to the kernel common code
+ */
+static int mxc_v4l2out_probe(struct platform_device *pdev)
+{
+ int i;
+ vout_data *vout;
+
+ /*
+ * Allocate sufficient memory for the fb structure
+ */
+ g_vout = vout = kmalloc(sizeof(vout_data), GFP_KERNEL);
+
+ if (!vout)
+ return 0;
+
+ memset(vout, 0, sizeof(vout_data));
+
+ vout->video_dev = video_device_alloc();
+ if (vout->video_dev == NULL)
+ return -1;
+ vout->video_dev->minor = -1;
+
+ *(vout->video_dev) = mxc_v4l2out_template;
+
+ /* register v4l device */
+ if (video_register_device(vout->video_dev,
+ VFL_TYPE_GRABBER, video_nr) == -1) {
+ pr_debug("video_register_device failed\n");
+ return 0;
+ }
+ pr_debug("mxc_v4l2out: registered device video%d\n",
+ vout->video_dev->minor & 0x1f);
+
+ video_set_drvdata(vout->video_dev, vout);
+
+ init_MUTEX(&vout->param_lock);
+ init_MUTEX(&vout->busy_lock);
+
+ /* setup outputs and cropping */
+ vout->cur_disp_output = -1;
+ for (i = 0; i < num_registered_fb; i++) {
+ char *idstr = registered_fb[i]->fix.id;
+ if (strncmp(idstr, "DISP", 4) == 0) {
+ int disp_num = i;
+ vout->crop_bounds[disp_num].left = 0;
+ vout->crop_bounds[disp_num].top = 0;
+ vout->crop_bounds[disp_num].width =
+ registered_fb[i]->var.xres;
+ vout->crop_bounds[disp_num].height =
+ registered_fb[i]->var.yres;
+ vout->output_enabled[disp_num] = true;
+ vout->output_fb_num[disp_num] = i;
+ if (vout->cur_disp_output == -1)
+ vout->cur_disp_output = disp_num;
+ }
+
+ }
+ vout->crop_current = vout->crop_bounds[vout->cur_disp_output];
+
+ /* Setup framebuffer parameters */
+ vout->v4l2_fb.capability = V4L2_FBUF_CAP_EXTERNOVERLAY;
+ vout->v4l2_fb.flags = V4L2_FBUF_FLAG_PRIMARY;
+
+ return 0;
+}
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxc_v4l2out_driver = {
+ .driver = {
+ .name = "MXC Video Output",
+ .owner = THIS_MODULE,
+ .bus = &platform_bus_type,
+ },
+ .probe = mxc_v4l2out_probe,
+ .remove = NULL,
+};
+
+static void camera_platform_release(struct device *device)
+{
+}
+
+static struct platform_device mxc_v4l2out_device = {
+ .name = "MXC Video Output",
+ .dev = {
+ .release = camera_platform_release,
+ },
+ .id = 0,
+};
+
+/*!
+ * mxc v4l2 init function
+ *
+ */
+static int mxc_v4l2out_init(void)
+{
+ u8 err = 0;
+
+ err = platform_driver_register(&mxc_v4l2out_driver);
+ if (err == 0) {
+ platform_device_register(&mxc_v4l2out_device);
+ }
+ return err;
+}
+
+/*!
+ * mxc v4l2 cleanup function
+ *
+ */
+static void mxc_v4l2out_clean(void)
+{
+ pr_debug("unregistering video\n");
+
+ video_unregister_device(g_vout->video_dev);
+
+ platform_driver_unregister(&mxc_v4l2out_driver);
+ platform_device_unregister(&mxc_v4l2out_device);
+ kfree(g_vout);
+ g_vout = NULL;
+}
+
+module_init(mxc_v4l2out_init);
+module_exit(mxc_v4l2out_clean);
+
+module_param(video_nr, int, 0444);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("V4L2-driver for MXC video output");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("video");
diff --git a/drivers/media/video/mxc/output/mx31_v4l2_wvga_output.c b/drivers/media/video/mxc/output/mx31_v4l2_wvga_output.c
new file mode 100644
index 000000000000..4a82da2d7d00
--- /dev/null
+++ b/drivers/media/video/mxc/output/mx31_v4l2_wvga_output.c
@@ -0,0 +1,1926 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file drivers/media/video/mxc/output/mxc_v4l2_output.c
+ *
+ * @brief MXC V4L2 Video Output Driver
+ *
+ * Video4Linux2 Output Device using MXC IPU Post-processing functionality.
+ *
+ * @ingroup MXC_V4L2_OUTPUT
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <asm/cacheflush.h>
+#include <asm/io.h>
+#include <asm/semaphore.h>
+#include <linux/dma-mapping.h>
+
+#include <mach/mxcfb.h>
+#include <mach/ipu.h>
+
+#include "mxc_v4l2_output.h"
+
+vout_data *g_vout;
+#define SDC_FG_FB_FORMAT IPU_PIX_FMT_RGB565
+
+struct v4l2_output mxc_outputs[2] = {
+ {
+ .index = MXC_V4L2_OUT_2_SDC,
+ .name = "DISP3 Video Out",
+ .type = V4L2_OUTPUT_TYPE_ANALOG, /* not really correct,
+ but no other choice */
+ .audioset = 0,
+ .modulator = 0,
+ .std = V4L2_STD_UNKNOWN},
+ {
+ .index = MXC_V4L2_OUT_2_ADC,
+ .name = "DISPx Video Out",
+ .type = V4L2_OUTPUT_TYPE_ANALOG, /* not really correct,
+ but no other choice */
+ .audioset = 0,
+ .modulator = 0,
+ .std = V4L2_STD_UNKNOWN}
+};
+
+static int video_nr = 16;
+static DEFINE_SPINLOCK(g_lock);
+static unsigned int g_pp_out_number;
+static unsigned int g_pp_in_number;
+
+/* debug counters */
+uint32_t g_irq_cnt;
+uint32_t g_buf_output_cnt;
+uint32_t g_buf_q_cnt;
+uint32_t g_buf_dq_cnt;
+
+static inline uint32_t channel_2_dma(ipu_channel_t ch, ipu_buffer_t type)
+{
+ return ((type == IPU_INPUT_BUFFER) ? ((uint32_t) ch & 0xFF) :
+ ((type == IPU_OUTPUT_BUFFER) ? (((uint32_t) ch >> 8) & 0xFF)
+ : (((uint32_t) ch >> 16) & 0xFF)));
+};
+
+static inline uint32_t DMAParamAddr(uint32_t dma_ch)
+{
+ return (0x10000 | (dma_ch << 4));
+};
+
+#define QUEUE_SIZE (MAX_FRAME_NUM + 1)
+static inline int queue_size(v4l_queue * q)
+{
+ if (q->tail >= q->head)
+ return (q->tail - q->head);
+ else
+ return ((q->tail + QUEUE_SIZE) - q->head);
+}
+
+static inline int queue_buf(v4l_queue * q, int idx)
+{
+ if (((q->tail + 1) % QUEUE_SIZE) == q->head)
+ return -1; /* queue full */
+ q->list[q->tail] = idx;
+ q->tail = (q->tail + 1) % QUEUE_SIZE;
+ return 0;
+}
+
+static inline int dequeue_buf(v4l_queue * q)
+{
+ int ret;
+ if (q->tail == q->head)
+ return -1; /* queue empty */
+ ret = q->list[q->head];
+ q->head = (q->head + 1) % QUEUE_SIZE;
+ return ret;
+}
+
+static inline int peek_next_buf(v4l_queue * q)
+{
+ if (q->tail == q->head)
+ return -1; /* queue empty */
+ return q->list[q->head];
+}
+
+static inline unsigned long get_jiffies(struct timeval *t)
+{
+ struct timeval cur;
+
+ if (t->tv_usec >= 1000000) {
+ t->tv_sec += t->tv_usec / 1000000;
+ t->tv_usec = t->tv_usec % 1000000;
+ }
+
+ do_gettimeofday(&cur);
+ if ((t->tv_sec < cur.tv_sec)
+ || ((t->tv_sec == cur.tv_sec) && (t->tv_usec < cur.tv_usec)))
+ return jiffies;
+
+ if (t->tv_usec < cur.tv_usec) {
+ cur.tv_sec = t->tv_sec - cur.tv_sec - 1;
+ cur.tv_usec = t->tv_usec + 1000000 - cur.tv_usec;
+ } else {
+ cur.tv_sec = t->tv_sec - cur.tv_sec;
+ cur.tv_usec = t->tv_usec - cur.tv_usec;
+ }
+
+ return jiffies + timeval_to_jiffies(&cur);
+}
+
+/*!
+ * Private function to free buffers
+ *
+ * @param bufs_paddr Array of physical address of buffers to be freed
+ *
+ * @param bufs_vaddr Array of virtual address of buffers to be freed
+ *
+ * @param num_buf Number of buffers to be freed
+ *
+ * @param size Size for each buffer to be free
+ *
+ * @return status 0 success.
+ */
+static int mxc_free_buffers(dma_addr_t bufs_paddr[], void *bufs_vaddr[],
+ int num_buf, int size)
+{
+ int i;
+
+ for (i = 0; i < num_buf; i++) {
+ if (bufs_vaddr[i] != 0) {
+ dma_free_coherent(0, size, bufs_vaddr[i],
+ bufs_paddr[i]);
+ pr_debug("freed @ paddr=0x%08X\n", (u32) bufs_paddr[i]);
+ bufs_paddr[i] = 0;
+ bufs_vaddr[i] = NULL;
+ }
+ }
+ return 0;
+}
+
+/*!
+ * Private function to allocate buffers
+ *
+ * @param bufs_paddr Output array of physical address of buffers allocated
+ *
+ * @param bufs_vaddr Output array of virtual address of buffers allocated
+ *
+ * @param num_buf Input number of buffers to allocate
+ *
+ * @param size Input size for each buffer to allocate
+ *
+ * @return status -0 Successfully allocated a buffer, -ENOBUFS failed.
+ */
+static int mxc_allocate_buffers(dma_addr_t bufs_paddr[], void *bufs_vaddr[],
+ int num_buf, int size)
+{
+ int i;
+
+ for (i = 0; i < num_buf; i++) {
+ bufs_vaddr[i] = dma_alloc_coherent(0, size,
+ &bufs_paddr[i],
+ GFP_DMA | GFP_KERNEL);
+
+ if (bufs_vaddr[i] == 0) {
+ mxc_free_buffers(bufs_paddr, bufs_vaddr, i, size);
+ printk(KERN_ERR "dma_alloc_coherent failed.\n");
+ return -ENOBUFS;
+ }
+ pr_debug("allocated @ paddr=0x%08X, size=%d.\n",
+ (u32) bufs_paddr[i], size);
+ }
+
+ return 0;
+}
+
+/*
+ * Returns bits per pixel for given pixel format
+ *
+ * @param pixelformat V4L2_PIX_FMT_RGB565,
+ * V4L2_PIX_FMT_BGR24 or V4L2_PIX_FMT_BGR32
+ *
+ * @return bits per pixel of pixelformat
+ */
+static u32 fmt_to_bpp(u32 pixelformat)
+{
+ u32 bpp;
+
+ switch (pixelformat) {
+ case V4L2_PIX_FMT_RGB565:
+ bpp = 16;
+ break;
+ case V4L2_PIX_FMT_BGR24:
+ case V4L2_PIX_FMT_RGB24:
+ bpp = 24;
+ break;
+ case V4L2_PIX_FMT_BGR32:
+ case V4L2_PIX_FMT_RGB32:
+ bpp = 32;
+ break;
+ default:
+ bpp = 8;
+ break;
+ }
+ return bpp;
+}
+
+static u32 bpp_to_fmt(struct fb_info *fbi)
+{
+ if (fbi->var.nonstd)
+ return fbi->var.nonstd;
+
+ if (fbi->var.bits_per_pixel == 24)
+ return V4L2_PIX_FMT_BGR24;
+ else if (fbi->var.bits_per_pixel == 32)
+ return V4L2_PIX_FMT_BGR32;
+ else if (fbi->var.bits_per_pixel == 16)
+ return V4L2_PIX_FMT_RGB565;
+
+ return 0;
+}
+
+static void mxc_v4l2out_timer_handler(unsigned long arg)
+{
+ int index;
+ unsigned long timeout;
+ unsigned long lock_flags = 0;
+ vout_data *vout = (vout_data *) arg;
+
+ dev_dbg(vout->video_dev->dev, "timer handler: %lu\n", jiffies);
+
+ spin_lock_irqsave(&g_lock, lock_flags);
+
+ if ((vout->state == STATE_STREAM_STOPPING)
+ || (vout->state == STATE_STREAM_OFF))
+ goto exit0;
+ /*
+ * If timer occurs before IPU h/w is ready, then set the state to
+ * paused and the timer will be set again when next buffer is queued
+ * or PP comletes
+ */
+ if (vout->ipu_buf[0] != -1) {
+ dev_dbg(vout->video_dev->dev, "IPU buffer busy\n");
+ vout->state = STATE_STREAM_PAUSED;
+ goto exit0;
+ }
+
+ /* One frame buffer should be ready here */
+ if (vout->frame_count % 2 == 1) {
+ /* set BUF0 rdy */
+ if (ipu_select_buffer(vout->display_ch, IPU_INPUT_BUFFER, 0) <
+ 0)
+ pr_debug("error selecting display buf 0");
+ } else {
+ if (ipu_select_buffer(vout->display_ch, IPU_INPUT_BUFFER, 1) <
+ 0)
+ pr_debug("error selecting display buf 1");
+ }
+
+ /* Dequeue buffer and pass to IPU */
+ index = dequeue_buf(&vout->ready_q);
+ if (index == -1) { /* no buffers ready, should never occur */
+ dev_err(vout->video_dev->dev,
+ "mxc_v4l2out: timer - no queued buffers ready\n");
+ goto exit0;
+ }
+
+ g_buf_dq_cnt++;
+ vout->frame_count++;
+ vout->ipu_buf[1] = vout->ipu_buf[0] = index;
+
+ if (ipu_update_channel_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER,
+ 0,
+ vout->v4l2_bufs[vout->ipu_buf[0]].m.
+ offset) < 0)
+ goto exit0;
+
+ if (ipu_update_channel_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER,
+ 1,
+ vout->v4l2_bufs[vout->ipu_buf[0]].m.
+ offset + vout->v2f.fmt.pix.width / 2) < 0)
+ goto exit0;
+
+ /* All buffer should now ready in IPU out, tranfer to display buf */
+ if (ipu_update_channel_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER,
+ 0,
+ vout->
+ display_bufs[(vout->frame_count -
+ 1) % 2]) < 0) {
+ dev_err(vout->video_dev->dev,
+ "unable to update buffer %d address\n",
+ vout->next_rdy_ipu_buf);
+ goto exit0;
+ }
+ if (ipu_update_channel_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER,
+ 1,
+ vout->
+ display_bufs[(vout->frame_count -
+ 1) % 2] +
+ vout->crop_current.width / 2 *
+ bytes_per_pixel(SDC_FG_FB_FORMAT)) < 0) {
+ dev_err(vout->video_dev->dev,
+ "unable to update buffer %d address\n",
+ vout->next_rdy_ipu_buf);
+ goto exit0;
+ }
+
+ if (ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER, 0) < 0) {
+ dev_err(vout->video_dev->dev,
+ "unable to set IPU buffer ready\n");
+ goto exit0;
+ }
+
+ if (ipu_select_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER, 0) < 0) {
+ dev_err(vout->video_dev->dev,
+ "unable to set IPU buffer ready\n");
+ goto exit0;
+ }
+
+ /* Setup timer for next buffer */
+ index = peek_next_buf(&vout->ready_q);
+ if (index != -1) {
+ /* if timestamp is 0, then default to 30fps */
+ if ((vout->v4l2_bufs[index].timestamp.tv_sec == 0)
+ && (vout->v4l2_bufs[index].timestamp.tv_usec == 0))
+ timeout =
+ vout->start_jiffies + vout->frame_count * HZ / 30;
+ else
+ timeout =
+ get_jiffies(&vout->v4l2_bufs[index].timestamp);
+
+ if (jiffies >= timeout) {
+ dev_dbg(vout->video_dev->dev,
+ "warning: timer timeout already expired.\n");
+ }
+ if (mod_timer(&vout->output_timer, timeout))
+ dev_dbg(vout->video_dev->dev,
+ "warning: timer was already set\n");
+
+ dev_dbg(vout->video_dev->dev,
+ "timer handler next schedule: %lu\n", timeout);
+ } else {
+ vout->state = STATE_STREAM_PAUSED;
+ }
+
+exit0:
+ spin_unlock_irqrestore(&g_lock, lock_flags);
+}
+
+extern void _ipu_write_param_mem(uint32_t addr, uint32_t *data,
+ uint32_t numWords);
+
+static irqreturn_t mxc_v4l2out_pp_in_irq_handler(int irq, void *dev_id)
+{
+ unsigned long lock_flags = 0;
+ vout_data *vout = dev_id;
+ uint32_t u_offset;
+ uint32_t v_offset;
+ uint32_t local_params[4];
+ uint32_t width, height;
+ uint32_t dma_chan;
+
+ spin_lock_irqsave(&g_lock, lock_flags);
+ g_irq_cnt++;
+
+ dma_chan = channel_2_dma(vout->post_proc_ch, IPU_INPUT_BUFFER);
+ memset(&local_params, 0, sizeof(local_params));
+
+ if (g_pp_in_number % 2 == 1) {
+ u_offset = vout->offset.u_offset - vout->v2f.fmt.pix.width / 4;
+ v_offset = vout->offset.v_offset - vout->v2f.fmt.pix.width / 4;
+ width = vout->v2f.fmt.pix.width / 2;
+ height = vout->v2f.fmt.pix.height;
+ local_params[3] =
+ (uint32_t) ((width - 1) << 12) | ((uint32_t) (height -
+ 1) << 24);
+ local_params[1] = (1UL << (46 - 32)) | (u_offset << (53 - 32));
+ local_params[2] = u_offset >> (64 - 53);
+ local_params[2] |= v_offset << (79 - 64);
+ local_params[3] |= v_offset >> (96 - 79);
+ _ipu_write_param_mem(DMAParamAddr(dma_chan), local_params, 4);
+
+ if (ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER, 1) <
+ 0) {
+ dev_err(vout->video_dev->dev,
+ "unable to set IPU buffer ready\n");
+ }
+ } else {
+ u_offset = vout->offset.u_offset;
+ v_offset = vout->offset.v_offset;
+ width = vout->v2f.fmt.pix.width / 2;
+ height = vout->v2f.fmt.pix.height;
+ local_params[3] =
+ (uint32_t) ((width - 1) << 12) | ((uint32_t) (height -
+ 1) << 24);
+ local_params[1] = (1UL << (46 - 32)) | (u_offset << (53 - 32));
+ local_params[2] = u_offset >> (64 - 53);
+ local_params[2] |= v_offset << (79 - 64);
+ local_params[3] |= v_offset >> (96 - 79);
+ _ipu_write_param_mem(DMAParamAddr(dma_chan), local_params, 4);
+ }
+ g_pp_in_number++;
+
+ spin_unlock_irqrestore(&g_lock, lock_flags);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mxc_v4l2out_pp_out_irq_handler(int irq, void *dev_id)
+{
+ vout_data *vout = dev_id;
+ int index;
+ unsigned long timeout;
+ u32 lock_flags = 0;
+
+ spin_lock_irqsave(&g_lock, lock_flags);
+
+ if (g_pp_out_number % 2 == 1) {
+ if (ipu_select_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER, 1)
+ < 0) {
+ dev_err(vout->video_dev->dev,
+ "unable to set IPU buffer ready\n");
+ }
+ } else {
+ if (vout->ipu_buf[0] != -1) {
+ vout->v4l2_bufs[vout->ipu_buf[0]].flags =
+ V4L2_BUF_FLAG_DONE;
+ queue_buf(&vout->done_q, vout->ipu_buf[0]);
+ wake_up_interruptible(&vout->v4l_bufq);
+ vout->ipu_buf[0] = -1;
+ }
+ if (vout->state == STATE_STREAM_STOPPING) {
+ if ((vout->ipu_buf[0] == -1)
+ && (vout->ipu_buf[1] == -1))
+ vout->state = STATE_STREAM_OFF;
+ } else if ((vout->state == STATE_STREAM_PAUSED)
+ && ((index = peek_next_buf(&vout->ready_q)) != -1)) {
+ /*!
+ * Setup timer for next buffer,
+ * when stream has been paused
+ */
+ pr_debug("next index %d\n", index);
+
+ /* if timestamp is 0, then default to 30fps */
+ if ((vout->v4l2_bufs[index].timestamp.tv_sec == 0)
+ && (vout->v4l2_bufs[index].timestamp.tv_usec == 0))
+ timeout =
+ vout->start_jiffies +
+ vout->frame_count * HZ / 30;
+ else
+ timeout =
+ get_jiffies(&vout->v4l2_bufs[index].
+ timestamp);
+
+ if (jiffies >= timeout) {
+ pr_debug
+ ("warning: timer timeout"
+ "already expired.\n");
+ }
+
+ vout->state = STATE_STREAM_ON;
+
+ if (mod_timer(&vout->output_timer, timeout))
+ pr_debug("warning: timer was already set\n");
+
+ pr_debug("timer handler next schedule: %lu\n", timeout);
+ }
+ }
+ g_pp_out_number++;
+
+ spin_unlock_irqrestore(&g_lock, lock_flags);
+ return IRQ_HANDLED;
+}
+
+/*!
+ * Start the output stream
+ *
+ * @param vout structure vout_data *
+ *
+ * @return status 0 Success
+ */
+static int mxc_v4l2out_streamon(vout_data *vout)
+{
+ struct device *dev = vout->video_dev->dev;
+ ipu_channel_params_t params;
+ struct mxcfb_pos fb_pos;
+ struct fb_var_screeninfo fbvar;
+ struct fb_info *fbi =
+ registered_fb[vout->output_fb_num[vout->cur_disp_output]];
+ int pp_in_buf[2];
+ u16 out_width;
+ u16 out_height;
+ ipu_channel_t display_input_ch = MEM_PP_MEM;
+ bool use_direct_adc = false;
+ mm_segment_t old_fs;
+
+ if (!vout)
+ return -EINVAL;
+
+ if (vout->state != STATE_STREAM_OFF)
+ return -EBUSY;
+
+ if (queue_size(&vout->ready_q) < 2) {
+ dev_err(dev, "2 buffers not been queued yet!\n");
+ return -EINVAL;
+ }
+
+ out_width = vout->crop_current.width;
+ out_height = vout->crop_current.height;
+
+ vout->next_done_ipu_buf = vout->next_rdy_ipu_buf = 0;
+ vout->ipu_buf[0] = pp_in_buf[0] = dequeue_buf(&vout->ready_q);
+ vout->ipu_buf[1] = pp_in_buf[1] = vout->ipu_buf[0];
+ vout->frame_count = 1;
+ g_pp_out_number = 1;
+ g_pp_in_number = 1;
+
+ ipu_enable_irq(IPU_IRQ_PP_IN_EOF);
+ ipu_enable_irq(IPU_IRQ_PP_OUT_EOF);
+
+ /* Init Display Channel */
+#ifdef CONFIG_FB_MXC_ASYNC_PANEL
+ if (vout->cur_disp_output < DISP3) {
+ mxcfb_set_refresh_mode(fbi, MXCFB_REFRESH_OFF, 0);
+ fbi = NULL;
+ if (ipu_can_rotate_in_place(vout->rotate)) {
+ dev_dbg(dev, "Using PP direct to ADC channel\n");
+ use_direct_adc = true;
+ vout->display_ch = MEM_PP_ADC;
+ vout->post_proc_ch = MEM_PP_ADC;
+
+ memset(&params, 0, sizeof(params));
+ params.mem_pp_adc.in_width = vout->v2f.fmt.pix.width;
+ params.mem_pp_adc.in_height = vout->v2f.fmt.pix.height;
+ params.mem_pp_adc.in_pixel_fmt =
+ vout->v2f.fmt.pix.pixelformat;
+ params.mem_pp_adc.out_width = out_width;
+ params.mem_pp_adc.out_height = out_height;
+ params.mem_pp_adc.out_pixel_fmt = SDC_FG_FB_FORMAT;
+#ifdef CONFIG_FB_MXC_EPSON_PANEL
+ params.mem_pp_adc.out_left =
+ 2 + vout->crop_current.left;
+#else
+ params.mem_pp_adc.out_left =
+ 12 + vout->crop_current.left;
+#endif
+ params.mem_pp_adc.out_top = vout->crop_current.top;
+ if (ipu_init_channel(
+ vout->post_proc_ch, &params) != 0) {
+ dev_err(dev, "Error initializing PP chan\n");
+ return -EINVAL;
+ }
+
+ if (ipu_init_channel_buffer(vout->post_proc_ch,
+ IPU_INPUT_BUFFER,
+ params.mem_pp_adc.
+ in_pixel_fmt,
+ params.mem_pp_adc.in_width,
+ params.mem_pp_adc.in_height,
+ vout->v2f.fmt.pix.
+ bytesperline /
+ bytes_per_pixel(params.
+ mem_pp_adc.
+ in_pixel_fmt),
+ vout->rotate,
+ vout->
+ v4l2_bufs[pp_in_buf[0]].m.
+ offset,
+ vout->
+ v4l2_bufs[pp_in_buf[1]].m.
+ offset,
+ vout->offset.u_offset,
+ vout->offset.v_offset) !=
+ 0) {
+ dev_err(dev, "Error initializing PP in buf\n");
+ return -EINVAL;
+ }
+
+ if (ipu_init_channel_buffer(vout->post_proc_ch,
+ IPU_OUTPUT_BUFFER,
+ params.mem_pp_adc.
+ out_pixel_fmt, out_width,
+ out_height, out_width,
+ vout->rotate, 0, 0, 0,
+ 0) != 0) {
+ dev_err(dev,
+ "Error initializing PP"
+ "output buffer\n");
+ return -EINVAL;
+ }
+
+ } else {
+ dev_dbg(dev, "Using ADC SYS2 channel\n");
+ vout->display_ch = ADC_SYS2;
+ vout->post_proc_ch = MEM_PP_MEM;
+
+ if (vout->display_bufs[0]) {
+ mxc_free_buffers(vout->display_bufs,
+ vout->display_bufs_vaddr,
+ 2, vout->display_buf_size);
+ }
+
+ vout->display_buf_size = vout->crop_current.width *
+ vout->crop_current.height *
+ fmt_to_bpp(SDC_FG_FB_FORMAT) / 8;
+ mxc_allocate_buffers(vout->display_bufs,
+ vout->display_bufs_vaddr,
+ 2, vout->display_buf_size);
+
+ memset(&params, 0, sizeof(params));
+ params.adc_sys2.disp = vout->cur_disp_output;
+ params.adc_sys2.ch_mode = WriteTemplateNonSeq;
+#ifdef CONFIG_FB_MXC_EPSON_PANEL
+ params.adc_sys2.out_left = 2 + vout->crop_current.left;
+#else
+ params.adc_sys2.out_left = 12 + vout->crop_current.left;
+#endif
+ params.adc_sys2.out_top = vout->crop_current.top;
+ if (ipu_init_channel(ADC_SYS2, &params) < 0)
+ return -EINVAL;
+
+ if (ipu_init_channel_buffer(vout->display_ch,
+ IPU_INPUT_BUFFER,
+ SDC_FG_FB_FORMAT,
+ out_width, out_height,
+ out_width, IPU_ROTATE_NONE,
+ vout->display_bufs[0],
+ vout->display_bufs[1], 0,
+ 0) != 0) {
+ dev_err(dev,
+ "Error initializing SDC FG buffer\n");
+ return -EINVAL;
+ }
+ }
+ } else
+#endif
+ { /* Use SDC */
+ dev_dbg(dev, "Using SDC channel\n");
+
+ fbvar = fbi->var;
+ if (vout->cur_disp_output == 3) {
+ vout->display_ch = MEM_FG_SYNC;
+ fbvar.bits_per_pixel = 16;
+ fbvar.nonstd = IPU_PIX_FMT_UYVY;
+
+ fbvar.xres = fbvar.xres_virtual = out_width;
+ fbvar.yres = out_height;
+ fbvar.yres_virtual = out_height * 2;
+ } else if (vout->cur_disp_output == 5) {
+ vout->display_ch = MEM_DC_SYNC;
+ fbvar.bits_per_pixel = 16;
+ fbvar.nonstd = IPU_PIX_FMT_UYVY;
+
+ fbvar.xres = fbvar.xres_virtual = out_width;
+ fbvar.yres = out_height;
+ fbvar.yres_virtual = out_height * 2;
+ } else {
+ vout->display_ch = MEM_BG_SYNC;
+ }
+
+ fbvar.activate |= FB_ACTIVATE_FORCE;
+ fb_set_var(fbi, &fbvar);
+
+ fb_pos.x = vout->crop_current.left;
+ fb_pos.y = vout->crop_current.top;
+ if (fbi->fbops->fb_ioctl) {
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+ fbi->fbops->fb_ioctl(fbi, MXCFB_SET_OVERLAY_POS,
+ (unsigned long)&fb_pos);
+ set_fs(old_fs);
+ }
+
+ vout->display_bufs[1] = fbi->fix.smem_start;
+ vout->display_bufs[0] = fbi->fix.smem_start +
+ (fbi->fix.line_length * fbi->var.yres);
+ vout->display_buf_size = vout->crop_current.width *
+ vout->crop_current.height * fbi->var.bits_per_pixel / 8;
+
+ vout->post_proc_ch = MEM_PP_MEM;
+ }
+
+ /* Init PP */
+ if (use_direct_adc == false) {
+ if (vout->rotate >= IPU_ROTATE_90_RIGHT) {
+ out_width = vout->crop_current.height;
+ out_height = vout->crop_current.width;
+ }
+ memset(&params, 0, sizeof(params));
+ params.mem_pp_mem.in_width = vout->v2f.fmt.pix.width / 2;
+ params.mem_pp_mem.in_height = vout->v2f.fmt.pix.height;
+ params.mem_pp_mem.in_pixel_fmt = vout->v2f.fmt.pix.pixelformat;
+ params.mem_pp_mem.out_width = out_width / 2;
+ params.mem_pp_mem.out_height = out_height;
+ if (vout->display_ch == ADC_SYS2)
+ params.mem_pp_mem.out_pixel_fmt = SDC_FG_FB_FORMAT;
+ else
+ params.mem_pp_mem.out_pixel_fmt = bpp_to_fmt(fbi);
+ if (ipu_init_channel(vout->post_proc_ch, &params) != 0) {
+ dev_err(dev, "Error initializing PP channel\n");
+ return -EINVAL;
+ }
+
+ if (ipu_init_channel_buffer(vout->post_proc_ch,
+ IPU_INPUT_BUFFER,
+ params.mem_pp_mem.in_pixel_fmt,
+ params.mem_pp_mem.in_width,
+ params.mem_pp_mem.in_height,
+ vout->v2f.fmt.pix.bytesperline /
+ bytes_per_pixel(params.mem_pp_mem.
+ in_pixel_fmt),
+ IPU_ROTATE_NONE,
+ vout->v4l2_bufs[pp_in_buf[0]].m.
+ offset,
+ vout->v4l2_bufs[pp_in_buf[0]].m.
+ offset + params.mem_pp_mem.in_width,
+ vout->offset.u_offset,
+ vout->offset.v_offset) != 0) {
+ dev_err(dev, "Error initializing PP input buffer\n");
+ return -EINVAL;
+ }
+
+ if (!ipu_can_rotate_in_place(vout->rotate)) {
+ if (vout->rot_pp_bufs[0]) {
+ mxc_free_buffers(vout->rot_pp_bufs,
+ vout->rot_pp_bufs_vaddr, 2,
+ vout->display_buf_size);
+ }
+ if (mxc_allocate_buffers
+ (vout->rot_pp_bufs, vout->rot_pp_bufs_vaddr, 2,
+ vout->display_buf_size) < 0)
+ return -ENOBUFS;
+
+ if (ipu_init_channel_buffer(vout->post_proc_ch,
+ IPU_OUTPUT_BUFFER,
+ params.mem_pp_mem.
+ out_pixel_fmt, out_width,
+ out_height, out_width,
+ IPU_ROTATE_NONE,
+ vout->rot_pp_bufs[0],
+ vout->rot_pp_bufs[1], 0,
+ 0) != 0) {
+ dev_err(dev,
+ "Error initializing"
+ "PP output buffer\n");
+ return -EINVAL;
+ }
+
+ if (ipu_init_channel(MEM_ROT_PP_MEM, NULL) != 0) {
+ dev_err(dev,
+ "Error initializing PP ROT channel\n");
+ return -EINVAL;
+ }
+
+ if (ipu_init_channel_buffer(MEM_ROT_PP_MEM,
+ IPU_INPUT_BUFFER,
+ params.mem_pp_mem.
+ out_pixel_fmt, out_width,
+ out_height, out_width,
+ vout->rotate,
+ vout->rot_pp_bufs[0],
+ vout->rot_pp_bufs[1], 0,
+ 0) != 0) {
+ dev_err(dev,
+ "Error initializing PP ROT"
+ "input buffer\n");
+ return -EINVAL;
+ }
+
+ /* swap width and height */
+ if (vout->rotate >= IPU_ROTATE_90_RIGHT) {
+ out_width = vout->crop_current.width;
+ out_height = vout->crop_current.height;
+ }
+
+ if (ipu_init_channel_buffer(MEM_ROT_PP_MEM,
+ IPU_OUTPUT_BUFFER,
+ params.mem_pp_mem.
+ out_pixel_fmt, out_width,
+ out_height, out_width,
+ IPU_ROTATE_NONE,
+ vout->display_bufs[0],
+ vout->display_bufs[1], 0,
+ 0) != 0) {
+ dev_err(dev,
+ "Error initializing PP"
+ "output buffer\n");
+ return -EINVAL;
+ }
+
+ if (ipu_link_channels(vout->post_proc_ch,
+ MEM_ROT_PP_MEM) < 0)
+ return -EINVAL;
+
+ ipu_select_buffer(MEM_ROT_PP_MEM, IPU_OUTPUT_BUFFER, 0);
+ ipu_select_buffer(MEM_ROT_PP_MEM, IPU_OUTPUT_BUFFER, 1);
+
+ ipu_enable_channel(MEM_ROT_PP_MEM);
+
+ display_input_ch = MEM_ROT_PP_MEM;
+ } else {
+ if (ipu_init_channel_buffer(vout->post_proc_ch,
+ IPU_OUTPUT_BUFFER,
+ params.mem_pp_mem.
+ out_pixel_fmt,
+ out_width / 2,
+ out_height,
+ out_width,
+ vout->rotate,
+ vout->display_bufs[0],
+ vout->display_bufs[0]
+ +
+ out_width / 2 *
+ bytes_per_pixel
+ (SDC_FG_FB_FORMAT), 0,
+ 0) != 0) {
+ dev_err(dev,
+ "Error initializing PP"
+ "output buffer\n");
+ return -EINVAL;
+ }
+ }
+ if (ipu_unlink_channels(
+ display_input_ch, vout->display_ch) < 0) {
+ dev_err(dev, "Error linking ipu channels\n");
+ return -EINVAL;
+ }
+ }
+
+ vout->state = STATE_STREAM_PAUSED;
+
+ ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER, 0);
+
+ if (use_direct_adc == false) {
+ ipu_select_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER, 0);
+ ipu_enable_channel(vout->post_proc_ch);
+
+ if (fbi) {
+ acquire_console_sem();
+ fb_blank(fbi, FB_BLANK_UNBLANK);
+ release_console_sem();
+ } else {
+ ipu_enable_channel(vout->display_ch);
+ }
+ } else {
+ ipu_enable_channel(vout->post_proc_ch);
+ }
+
+ vout->start_jiffies = jiffies;
+ dev_dbg(dev,
+ "streamon: start time = %lu jiffies\n", vout->start_jiffies);
+
+ return 0;
+}
+
+/*!
+ * Shut down the voutera
+ *
+ * @param vout structure vout_data *
+ *
+ * @return status 0 Success
+ */
+static int mxc_v4l2out_streamoff(vout_data *vout)
+{
+ struct fb_info *fbi =
+ registered_fb[vout->output_fb_num[vout->cur_disp_output]];
+ int i, retval = 0;
+ unsigned long lockflag = 0;
+
+ if (!vout)
+ return -EINVAL;
+
+ if (vout->state == STATE_STREAM_OFF)
+ return 0;
+
+ spin_lock_irqsave(&g_lock, lockflag);
+
+ del_timer(&vout->output_timer);
+
+ if (vout->state == STATE_STREAM_ON)
+ vout->state = STATE_STREAM_STOPPING;
+
+ ipu_disable_irq(IPU_IRQ_PP_IN_EOF);
+ ipu_disable_irq(IPU_IRQ_PP_OUT_EOF);
+
+ spin_unlock_irqrestore(&g_lock, lockflag);
+
+ if (vout->post_proc_ch == MEM_PP_MEM) { /* SDC or ADC with Rotation */
+ if (!ipu_can_rotate_in_place(vout->rotate)) {
+ ipu_unlink_channels(MEM_PP_MEM, MEM_ROT_PP_MEM);
+ ipu_unlink_channels(MEM_ROT_PP_MEM, vout->display_ch);
+ ipu_disable_channel(MEM_ROT_PP_MEM, true);
+
+ if (vout->rot_pp_bufs[0]) {
+ mxc_free_buffers(vout->rot_pp_bufs,
+ vout->rot_pp_bufs_vaddr, 2,
+ vout->display_buf_size);
+ }
+ } else {
+ ipu_unlink_channels(MEM_PP_MEM, vout->display_ch);
+ }
+ ipu_disable_channel(MEM_PP_MEM, true);
+
+ if (vout->display_ch == ADC_SYS2) {
+ ipu_disable_channel(vout->display_ch, true);
+ ipu_uninit_channel(vout->display_ch);
+ } else {
+ fbi->var.activate |= FB_ACTIVATE_FORCE;
+ fb_set_var(fbi, &fbi->var);
+
+ if (vout->display_ch == MEM_FG_SYNC) {
+ acquire_console_sem();
+ fb_blank(fbi, FB_BLANK_POWERDOWN);
+ release_console_sem();
+ }
+
+ vout->display_bufs[0] = 0;
+ vout->display_bufs[1] = 0;
+ }
+
+ ipu_uninit_channel(MEM_PP_MEM);
+ if (!ipu_can_rotate_in_place(vout->rotate))
+ ipu_uninit_channel(MEM_ROT_PP_MEM);
+ } else { /* ADC Direct */
+ ipu_disable_channel(MEM_PP_ADC, true);
+ ipu_uninit_channel(MEM_PP_ADC);
+ }
+ vout->ready_q.head = vout->ready_q.tail = 0;
+ vout->done_q.head = vout->done_q.tail = 0;
+ for (i = 0; i < vout->buffer_cnt; i++) {
+ vout->v4l2_bufs[i].flags = 0;
+ vout->v4l2_bufs[i].timestamp.tv_sec = 0;
+ vout->v4l2_bufs[i].timestamp.tv_usec = 0;
+ }
+
+ vout->state = STATE_STREAM_OFF;
+
+#ifdef CONFIG_FB_MXC_ASYNC_PANEL
+ if (vout->cur_disp_output < DISP3) {
+ if (vout->display_bufs[0] != 0) {
+ mxc_free_buffers(vout->display_bufs,
+ vout->display_bufs_vaddr, 2,
+ vout->display_buf_size);
+ }
+
+ mxcfb_set_refresh_mode(registered_fb
+ [vout->
+ output_fb_num[vout->cur_disp_output]],
+ MXCFB_REFRESH_PARTIAL, 0);
+ }
+#endif
+
+ return retval;
+}
+
+/*
+ * Valid whether the palette is supported
+ *
+ * @param palette V4L2_PIX_FMT_RGB565, V4L2_PIX_FMT_BGR24 or V4L2_PIX_FMT_BGR32
+ *
+ * @return 1 if supported, 0 if failed
+ */
+static inline int valid_mode(u32 palette)
+{
+ return ((palette == V4L2_PIX_FMT_RGB565) ||
+ (palette == V4L2_PIX_FMT_BGR24) ||
+ (palette == V4L2_PIX_FMT_RGB24) ||
+ (palette == V4L2_PIX_FMT_BGR32) ||
+ (palette == V4L2_PIX_FMT_RGB32) ||
+ (palette == V4L2_PIX_FMT_NV12) ||
+ (palette == V4L2_PIX_FMT_YUV422P) ||
+ (palette == V4L2_PIX_FMT_YUV420));
+}
+
+/*
+ * V4L2 - Handles VIDIOC_G_FMT Ioctl
+ *
+ * @param vout structure vout_data *
+ *
+ * @param v4l2_format structure v4l2_format *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_v4l2out_g_fmt(vout_data *vout, struct v4l2_format *f)
+{
+ if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return -EINVAL;
+ *f = vout->v2f;
+ return 0;
+}
+
+/*
+ * V4L2 - Handles VIDIOC_S_FMT Ioctl
+ *
+ * @param vout structure vout_data *
+ *
+ * @param v4l2_format structure v4l2_format *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_v4l2out_s_fmt(vout_data *vout, struct v4l2_format *f)
+{
+ int retval = 0;
+ u32 size = 0;
+ u32 bytesperline;
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ retval = -EINVAL;
+ goto err0;
+ }
+ if (!valid_mode(f->fmt.pix.pixelformat)) {
+ dev_err(vout->video_dev->dev, "pixel format not supported\n");
+ retval = -EINVAL;
+ goto err0;
+ }
+
+ bytesperline = (f->fmt.pix.width * fmt_to_bpp(f->fmt.pix.pixelformat)) /
+ 8;
+ if (f->fmt.pix.bytesperline < bytesperline) {
+ f->fmt.pix.bytesperline = bytesperline;
+ } else {
+ bytesperline = f->fmt.pix.bytesperline;
+ }
+
+ switch (f->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_YUV422P:
+ /* byteperline for YUV planar formats is for
+ Y plane only */
+ size = bytesperline * f->fmt.pix.height * 2;
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_NV12:
+ size = (bytesperline * f->fmt.pix.height * 3) / 2;
+ break;
+ default:
+ size = bytesperline * f->fmt.pix.height;
+ break;
+ }
+
+ /* Return the actual size of the image to the app */
+ if (f->fmt.pix.sizeimage < size)
+ f->fmt.pix.sizeimage = size;
+ else
+ size = f->fmt.pix.sizeimage;
+
+ vout->v2f.fmt.pix = f->fmt.pix;
+ if (vout->v2f.fmt.pix.priv != 0) {
+ if (copy_from_user(&vout->offset,
+ (void *)vout->v2f.fmt.pix.priv,
+ sizeof(vout->offset))) {
+ retval = -EFAULT;
+ goto err0;
+ }
+ } else {
+ vout->offset.u_offset = 0;
+ vout->offset.v_offset = 0;
+ }
+
+ retval = 0;
+err0:
+ return retval;
+}
+
+/*
+ * V4L2 - Handles VIDIOC_G_CTRL Ioctl
+ *
+ * @param vout structure vout_data *
+ *
+ * @param c structure v4l2_control *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_get_v42lout_control(vout_data *vout, struct v4l2_control *c)
+{
+ switch (c->id) {
+ case V4L2_CID_HFLIP:
+ return (vout->rotate & IPU_ROTATE_HORIZ_FLIP) ? 1 : 0;
+ case V4L2_CID_VFLIP:
+ return (vout->rotate & IPU_ROTATE_VERT_FLIP) ? 1 : 0;
+ case (V4L2_CID_PRIVATE_BASE + 1):
+ return vout->rotate;
+ default:
+ return -EINVAL;
+ }
+}
+
+/*
+ * V4L2 - Handles VIDIOC_S_CTRL Ioctl
+ *
+ * @param vout structure vout_data *
+ *
+ * @param c structure v4l2_control *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_set_v42lout_control(vout_data *vout, struct v4l2_control *c)
+{
+ switch (c->id) {
+ case V4L2_CID_HFLIP:
+ vout->rotate |= c->value ? IPU_ROTATE_HORIZ_FLIP :
+ IPU_ROTATE_NONE;
+ break;
+ case V4L2_CID_VFLIP:
+ vout->rotate |= c->value ? IPU_ROTATE_VERT_FLIP :
+ IPU_ROTATE_NONE;
+ break;
+ case V4L2_CID_MXC_ROT:
+ vout->rotate = c->value;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*!
+ * V4L2 interface - open function
+ *
+ * @param inode structure inode *
+ *
+ * @param file structure file *
+ *
+ * @return status 0 success, ENODEV invalid device instance,
+ * ENODEV timeout, ERESTARTSYS interrupted by user
+ */
+static int mxc_v4l2out_open(struct inode *inode, struct file *file)
+{
+ struct video_device *dev = video_devdata(file);
+ vout_data *vout = video_get_drvdata(dev);
+ int err;
+
+ if (!vout)
+ return -ENODEV;
+
+ down(&vout->busy_lock);
+
+ err = -EINTR;
+ if (signal_pending(current))
+ goto oops;
+
+ if (vout->open_count++ == 0) {
+ ipu_request_irq(IPU_IRQ_PP_IN_EOF,
+ mxc_v4l2out_pp_in_irq_handler,
+ 0, dev->name, vout);
+ ipu_request_irq(IPU_IRQ_PP_OUT_EOF,
+ mxc_v4l2out_pp_out_irq_handler,
+ 0, dev->name, vout);
+
+ init_waitqueue_head(&vout->v4l_bufq);
+
+ init_timer(&vout->output_timer);
+ vout->output_timer.function = mxc_v4l2out_timer_handler;
+ vout->output_timer.data = (unsigned long)vout;
+
+ vout->state = STATE_STREAM_OFF;
+ vout->rotate = IPU_ROTATE_NONE;
+ g_irq_cnt = g_buf_output_cnt = g_buf_q_cnt = g_buf_dq_cnt = 0;
+
+ }
+
+ file->private_data = dev;
+
+ up(&vout->busy_lock);
+
+ return 0;
+
+oops:
+ up(&vout->busy_lock);
+ return err;
+}
+
+/*!
+ * V4L2 interface - close function
+ *
+ * @param inode struct inode *
+ *
+ * @param file struct file *
+ *
+ * @return 0 success
+ */
+static int mxc_v4l2out_close(struct inode *inode, struct file *file)
+{
+ struct video_device *dev = video_devdata(file);
+ vout_data *vout = video_get_drvdata(dev);
+
+ if (--vout->open_count == 0) {
+ if (vout->state != STATE_STREAM_OFF)
+ mxc_v4l2out_streamoff(vout);
+
+ ipu_free_irq(IPU_IRQ_PP_IN_EOF, vout);
+ ipu_free_irq(IPU_IRQ_PP_OUT_EOF, vout);
+
+ file->private_data = NULL;
+
+ mxc_free_buffers(vout->queue_buf_paddr, vout->queue_buf_vaddr,
+ vout->buffer_cnt, vout->queue_buf_size);
+ vout->buffer_cnt = 0;
+ mxc_free_buffers(vout->rot_pp_bufs, vout->rot_pp_bufs_vaddr, 2,
+ vout->display_buf_size);
+
+ /* capture off */
+ wake_up_interruptible(&vout->v4l_bufq);
+ }
+
+ return 0;
+}
+
+/*!
+ * V4L2 interface - ioctl function
+ *
+ * @param inode struct inode *
+ *
+ * @param file struct file *
+ *
+ * @param ioctlnr unsigned int
+ *
+ * @param arg void *
+ *
+ * @return 0 success, ENODEV for invalid device instance,
+ * -1 for other errors.
+ */
+static int
+mxc_v4l2out_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int ioctlnr, void *arg)
+{
+ struct video_device *vdev = file->private_data;
+ vout_data *vout = video_get_drvdata(vdev);
+ int retval = 0;
+ int i = 0;
+
+ if (!vout)
+ return -EBADF;
+
+ /* make this _really_ smp-safe */
+ if (down_interruptible(&vout->busy_lock))
+ return -EBUSY;
+
+ switch (ioctlnr) {
+ case VIDIOC_QUERYCAP:
+ {
+ struct v4l2_capability *cap = arg;
+ strcpy(cap->driver, "mxc_v4l2_output");
+ cap->version = 0;
+ cap->capabilities =
+ V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
+ cap->card[0] = '\0';
+ cap->bus_info[0] = '\0';
+ retval = 0;
+ break;
+ }
+ case VIDIOC_G_FMT:
+ {
+ struct v4l2_format *gf = arg;
+ retval = mxc_v4l2out_g_fmt(vout, gf);
+ break;
+ }
+ case VIDIOC_S_FMT:
+ {
+ struct v4l2_format *sf = arg;
+ if (vout->state != STATE_STREAM_OFF) {
+ retval = -EBUSY;
+ break;
+ }
+ retval = mxc_v4l2out_s_fmt(vout, sf);
+ break;
+ }
+ case VIDIOC_REQBUFS:
+ {
+ struct v4l2_requestbuffers *req = arg;
+ if ((req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) ||
+ (req->memory != V4L2_MEMORY_MMAP)) {
+ dev_dbg(vdev->dev,
+ "VIDIOC_REQBUFS: incorrect"
+ "buffer type\n");
+ retval = -EINVAL;
+ break;
+ }
+
+ if (req->count == 0)
+ mxc_v4l2out_streamoff(vout);
+
+ if (vout->state == STATE_STREAM_OFF) {
+ if (vout->queue_buf_paddr[0] != 0) {
+ mxc_free_buffers(vout->queue_buf_paddr,
+ vout->queue_buf_vaddr,
+ vout->buffer_cnt,
+ vout->queue_buf_size);
+ dev_dbg(vdev->dev,
+ "VIDIOC_REQBUFS:"
+ "freed buffers\n");
+ }
+ vout->buffer_cnt = 0;
+ } else {
+ dev_dbg(vdev->dev,
+ "VIDIOC_REQBUFS: Buffer is in use\n");
+ retval = -EBUSY;
+ break;
+ }
+
+ if (req->count == 0)
+ break;
+
+ if (req->count < MIN_FRAME_NUM)
+ req->count = MIN_FRAME_NUM;
+ else if (req->count > MAX_FRAME_NUM)
+ req->count = MAX_FRAME_NUM;
+ vout->buffer_cnt = req->count;
+ vout->queue_buf_size =
+ PAGE_ALIGN(vout->v2f.fmt.pix.sizeimage);
+
+ retval = mxc_allocate_buffers(vout->queue_buf_paddr,
+ vout->queue_buf_vaddr,
+ vout->buffer_cnt,
+ vout->queue_buf_size);
+ if (retval < 0)
+ break;
+
+ /* Init buffer queues */
+ vout->done_q.head = 0;
+ vout->done_q.tail = 0;
+ vout->ready_q.head = 0;
+ vout->ready_q.tail = 0;
+
+ for (i = 0; i < vout->buffer_cnt; i++) {
+ memset(&(vout->v4l2_bufs[i]), 0,
+ sizeof(vout->v4l2_bufs[i]));
+ vout->v4l2_bufs[i].flags = 0;
+ vout->v4l2_bufs[i].memory = V4L2_MEMORY_MMAP;
+ vout->v4l2_bufs[i].index = i;
+ vout->v4l2_bufs[i].type =
+ V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ vout->v4l2_bufs[i].length =
+ PAGE_ALIGN(vout->v2f.fmt.pix.sizeimage);
+ vout->v4l2_bufs[i].m.offset =
+ (unsigned long)vout->queue_buf_paddr[i];
+ vout->v4l2_bufs[i].timestamp.tv_sec = 0;
+ vout->v4l2_bufs[i].timestamp.tv_usec = 0;
+ }
+ break;
+ }
+ case VIDIOC_QUERYBUF:
+ {
+ struct v4l2_buffer *buf = arg;
+ u32 type = buf->type;
+ int index = buf->index;
+
+ if ((type != V4L2_BUF_TYPE_VIDEO_OUTPUT) ||
+ (index >= vout->buffer_cnt)) {
+ dev_dbg(vdev->dev,
+ "VIDIOC_QUERYBUFS: incorrect"
+ "buffer type\n");
+ retval = -EINVAL;
+ break;
+ }
+ down(&vout->param_lock);
+ memcpy(buf, &(vout->v4l2_bufs[index]), sizeof(*buf));
+ up(&vout->param_lock);
+ break;
+ }
+ case VIDIOC_QBUF:
+ {
+ struct v4l2_buffer *buf = arg;
+ int index = buf->index;
+ unsigned long lock_flags;
+
+ if ((buf->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) ||
+ (index >= vout->buffer_cnt)) {
+ retval = -EINVAL;
+ break;
+ }
+
+ dev_dbg(vdev->dev, "VIDIOC_QBUF: %d\n", buf->index);
+
+ /* mmapped buffers are L1 WB cached,
+ * so we need to clean them */
+ if (buf->flags & V4L2_BUF_FLAG_MAPPED)
+ flush_cache_all();
+
+ spin_lock_irqsave(&g_lock, lock_flags);
+
+ memcpy(&(vout->v4l2_bufs[index]), buf, sizeof(*buf));
+ vout->v4l2_bufs[index].flags |= V4L2_BUF_FLAG_QUEUED;
+
+ g_buf_q_cnt++;
+ queue_buf(&vout->ready_q, index);
+ if (vout->state == STATE_STREAM_PAUSED) {
+ unsigned long timeout;
+
+ index = peek_next_buf(&vout->ready_q);
+
+ /* if timestamp is 0, then default to 30fps */
+ if ((vout->v4l2_bufs[index].timestamp.tv_sec ==
+ 0)
+ && (vout->v4l2_bufs[index].timestamp.
+ tv_usec == 0))
+ timeout =
+ vout->start_jiffies +
+ vout->frame_count * HZ / 30;
+ else
+ timeout =
+ get_jiffies(&vout->v4l2_bufs[index].
+ timestamp);
+
+ if (jiffies >= timeout) {
+ dev_dbg(vout->video_dev->dev,
+ "warning: timer timeout"
+ "already expired.\n");
+ }
+ vout->output_timer.expires = timeout;
+ dev_dbg(vdev->dev,
+ "QBUF: frame #%u timeout @"
+ " %lu jiffies, current = %lu\n",
+ vout->frame_count, timeout, jiffies);
+ add_timer(&vout->output_timer);
+ vout->state = STATE_STREAM_ON;
+ }
+
+ spin_unlock_irqrestore(&g_lock, lock_flags);
+ break;
+ }
+ case VIDIOC_DQBUF:
+ {
+ struct v4l2_buffer *buf = arg;
+ int idx;
+
+ if ((queue_size(&vout->done_q) == 0) &&
+ (file->f_flags & O_NONBLOCK)) {
+ retval = -EAGAIN;
+ break;
+ }
+
+ if (!wait_event_interruptible_timeout(vout->v4l_bufq,
+ queue_size(&vout->
+ done_q)
+ != 0, 10 * HZ)) {
+ dev_dbg(vdev->dev, "VIDIOC_DQBUF: timeout\n");
+ retval = -ETIME;
+ break;
+ } else if (signal_pending(current)) {
+ dev_dbg(vdev->dev,
+ "VIDIOC_DQBUF: interrupt received\n");
+ retval = -ERESTARTSYS;
+ break;
+ }
+ idx = dequeue_buf(&vout->done_q);
+ if (idx == -1) { /* No frame free */
+ dev_dbg(vdev->dev,
+ "VIDIOC_DQBUF: no free buffers\n");
+ retval = -EAGAIN;
+ break;
+ }
+ if ((vout->v4l2_bufs[idx].flags & V4L2_BUF_FLAG_DONE) ==
+ 0)
+ dev_dbg(vdev->dev,
+ "VIDIOC_DQBUF: buffer in done q, "
+ "but not flagged as done\n");
+
+ vout->v4l2_bufs[idx].flags = 0;
+ memcpy(buf, &(vout->v4l2_bufs[idx]), sizeof(*buf));
+ dev_dbg(vdev->dev, "VIDIOC_DQBUF: %d\n", buf->index);
+ break;
+ }
+ case VIDIOC_STREAMON:
+ {
+ retval = mxc_v4l2out_streamon(vout);
+ break;
+ }
+ case VIDIOC_STREAMOFF:
+ {
+ retval = mxc_v4l2out_streamoff(vout);
+ break;
+ }
+ case VIDIOC_G_CTRL:
+ {
+ retval = mxc_get_v42lout_control(vout, arg);
+ break;
+ }
+ case VIDIOC_S_CTRL:
+ {
+ retval = mxc_set_v42lout_control(vout, arg);
+ break;
+ }
+ case VIDIOC_CROPCAP:
+ {
+ struct v4l2_cropcap *cap = arg;
+
+ if (cap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ retval = -EINVAL;
+ break;
+ }
+
+ cap->bounds = vout->crop_bounds[vout->cur_disp_output];
+ cap->defrect = vout->crop_bounds[vout->cur_disp_output];
+ retval = 0;
+ break;
+ }
+ case VIDIOC_G_CROP:
+ {
+ struct v4l2_crop *crop = arg;
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ retval = -EINVAL;
+ break;
+ }
+ crop->c = vout->crop_current;
+ break;
+ }
+ case VIDIOC_S_CROP:
+ {
+ struct v4l2_crop *crop = arg;
+ struct v4l2_rect *b =
+ &(vout->crop_bounds[vout->cur_disp_output]);
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ retval = -EINVAL;
+ break;
+ }
+ if (crop->c.height < 0) {
+ retval = -EINVAL;
+ break;
+ }
+ if (crop->c.width < 0) {
+ retval = -EINVAL;
+ break;
+ }
+
+ /* only full screen supported for SDC BG */
+ if (vout->cur_disp_output == 4) {
+ crop->c = vout->crop_current;
+ break;
+ }
+
+ if (crop->c.top < b->top)
+ crop->c.top = b->top;
+ if (crop->c.top >= b->top + b->height)
+ crop->c.top = b->top + b->height - 1;
+ if (crop->c.height > b->top - crop->c.top + b->height)
+ crop->c.height =
+ b->top - crop->c.top + b->height;
+
+ if (crop->c.left < b->left)
+ crop->c.left = b->left;
+ if (crop->c.left >= b->left + b->width)
+ crop->c.left = b->left + b->width - 1;
+ if (crop->c.width > b->left - crop->c.left + b->width)
+ crop->c.width =
+ b->left - crop->c.left + b->width;
+
+ /* stride line limitation */
+ crop->c.height -= crop->c.height % 8;
+ crop->c.width -= crop->c.width % 8;
+
+ vout->crop_current = crop->c;
+ break;
+ }
+ case VIDIOC_ENUMOUTPUT:
+ {
+ struct v4l2_output *output = arg;
+
+ if ((output->index >= 5) ||
+ (vout->output_enabled[output->index] == false)) {
+ retval = -EINVAL;
+ break;
+ }
+
+ if (output->index < 3) {
+ *output = mxc_outputs[MXC_V4L2_OUT_2_ADC];
+ output->name[4] = '0' + output->index;
+ } else {
+ *output = mxc_outputs[MXC_V4L2_OUT_2_SDC];
+ }
+ break;
+ }
+ case VIDIOC_G_OUTPUT:
+ {
+ int *p_output_num = arg;
+
+ *p_output_num = vout->cur_disp_output;
+ break;
+ }
+ case VIDIOC_S_OUTPUT:
+ {
+ int *p_output_num = arg;
+ int fbnum;
+ struct v4l2_rect *b;
+
+ if ((*p_output_num >= MXC_V4L2_OUT_NUM_OUTPUTS) ||
+ (vout->output_enabled[*p_output_num] == false)) {
+ retval = -EINVAL;
+ break;
+ }
+
+ if (vout->state != STATE_STREAM_OFF) {
+ retval = -EBUSY;
+ break;
+ }
+
+ vout->cur_disp_output = *p_output_num;
+
+ /* Update bounds in case they have changed */
+ b = &vout->crop_bounds[vout->cur_disp_output];
+
+ fbnum = vout->output_fb_num[vout->cur_disp_output];
+ if (vout->cur_disp_output == 3)
+ fbnum = vout->output_fb_num[4];
+
+ b->width = registered_fb[fbnum]->var.xres;
+ b->height = registered_fb[fbnum]->var.yres;
+
+ vout->crop_current = *b;
+ break;
+ }
+ case VIDIOC_ENUM_FMT:
+ case VIDIOC_TRY_FMT:
+ case VIDIOC_QUERYCTRL:
+ case VIDIOC_G_PARM:
+ case VIDIOC_ENUMSTD:
+ case VIDIOC_G_STD:
+ case VIDIOC_S_STD:
+ case VIDIOC_G_TUNER:
+ case VIDIOC_S_TUNER:
+ case VIDIOC_G_FREQUENCY:
+ case VIDIOC_S_FREQUENCY:
+ default:
+ retval = -EINVAL;
+ break;
+ }
+
+ up(&vout->busy_lock);
+ return retval;
+}
+
+/*
+ * V4L2 interface - ioctl function
+ *
+ * @return None
+ */
+static int
+mxc_v4l2out_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(inode, file, cmd, arg, mxc_v4l2out_do_ioctl);
+}
+
+/*!
+ * V4L2 interface - mmap function
+ *
+ * @param file structure file *
+ *
+ * @param vma structure vm_area_struct *
+ *
+ * @return status 0 Success, EINTR busy lock error,
+ * ENOBUFS remap_page error
+ */
+static int mxc_v4l2out_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct video_device *vdev = video_devdata(file);
+ unsigned long size = vma->vm_end - vma->vm_start;
+ int res = 0;
+ int i;
+ vout_data *vout = video_get_drvdata(vdev);
+
+ dev_dbg(vdev->dev, "pgoff=0x%lx, start=0x%lx, end=0x%lx\n",
+ vma->vm_pgoff, vma->vm_start, vma->vm_end);
+
+ /* make this _really_ smp-safe */
+ if (down_interruptible(&vout->busy_lock))
+ return -EINTR;
+
+ for (i = 0; i < vout->buffer_cnt; i++) {
+ if ((vout->v4l2_bufs[i].m.offset ==
+ (vma->vm_pgoff << PAGE_SHIFT)) &&
+ (vout->v4l2_bufs[i].length >= size)) {
+ vout->v4l2_bufs[i].flags |= V4L2_BUF_FLAG_MAPPED;
+ break;
+ }
+ }
+ if (i == vout->buffer_cnt) {
+ res = -ENOBUFS;
+ goto mxc_mmap_exit;
+ }
+
+ /* make buffers inner write-back, outer write-thru cacheable */
+ vma->vm_page_prot = pgprot_outer_wrthru(vma->vm_page_prot);
+
+ if (remap_pfn_range(vma, vma->vm_start,
+ vma->vm_pgoff, size, vma->vm_page_prot)) {
+ dev_dbg(vdev->dev, "mmap remap_pfn_range failed\n");
+ res = -ENOBUFS;
+ goto mxc_mmap_exit;
+ }
+
+ vma->vm_flags &= ~VM_IO; /* using shared anonymous pages */
+
+mxc_mmap_exit:
+ up(&vout->busy_lock);
+ return res;
+}
+
+/*!
+ * V4L2 interface - poll function
+ *
+ * @param file structure file *
+ *
+ * @param wait structure poll_table *
+ *
+ * @return status POLLIN | POLLRDNORM
+ */
+static unsigned int mxc_v4l2out_poll(struct file *file, poll_table * wait)
+{
+ struct video_device *dev = video_devdata(file);
+ vout_data *vout = video_get_drvdata(dev);
+
+ wait_queue_head_t *queue = NULL;
+ int res = POLLIN | POLLRDNORM;
+
+ if (down_interruptible(&vout->busy_lock))
+ return -EINTR;
+
+ queue = &vout->v4l_bufq;
+ poll_wait(file, queue, wait);
+
+ up(&vout->busy_lock);
+ return res;
+}
+
+static struct
+file_operations mxc_v4l2out_fops = {
+ .owner = THIS_MODULE,
+ .open = mxc_v4l2out_open,
+ .release = mxc_v4l2out_close,
+ .ioctl = mxc_v4l2out_ioctl,
+ .mmap = mxc_v4l2out_mmap,
+ .poll = mxc_v4l2out_poll,
+};
+
+static struct video_device mxc_v4l2out_template = {
+ .owner = THIS_MODULE,
+ .name = "MXC Video Output",
+ .type = 0,
+ .type2 = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING,
+ .fops = &mxc_v4l2out_fops,
+ .release = video_device_release,
+};
+
+/*!
+ * Probe routine for the framebuffer driver. It is called during the
+ * driver binding process. The following functions are performed in
+ * this routine: Framebuffer initialization, Memory allocation and
+ * mapping, Framebuffer registration, IPU initialization.
+ *
+ * @return Appropriate error code to the kernel common code
+ */
+static int mxc_v4l2out_probe(struct platform_device *pdev)
+{
+ int i;
+ vout_data *vout;
+
+ /*
+ * Allocate sufficient memory for the fb structure
+ */
+ g_vout = vout = kmalloc(sizeof(vout_data), GFP_KERNEL);
+
+ if (!vout)
+ return 0;
+
+ memset(vout, 0, sizeof(vout_data));
+
+ vout->video_dev = video_device_alloc();
+ if (vout->video_dev == NULL)
+ return -1;
+ vout->video_dev->dev = &pdev->dev;
+ vout->video_dev->minor = -1;
+
+ *(vout->video_dev) = mxc_v4l2out_template;
+
+ /* register v4l device */
+ if (video_register_device(vout->video_dev,
+ VFL_TYPE_GRABBER, video_nr) == -1) {
+ dev_dbg(&pdev->dev, "video_register_device failed\n");
+ return 0;
+ }
+ dev_info(&pdev->dev, "Registered device video%d\n",
+ vout->video_dev->minor & 0x1f);
+ vout->video_dev->dev = &pdev->dev;
+
+ video_set_drvdata(vout->video_dev, vout);
+
+ init_MUTEX(&vout->param_lock);
+ init_MUTEX(&vout->busy_lock);
+
+ /* setup outputs and cropping */
+ vout->cur_disp_output = -1;
+ for (i = 0; i < num_registered_fb; i++) {
+ char *idstr = registered_fb[i]->fix.id;
+ if (strncmp(idstr, "DISP", 4) == 0) {
+ int disp_num = idstr[4] - '0';
+ if (disp_num == 3) {
+ if (strcmp(idstr, "DISP3 BG - DI1") == 0)
+ disp_num = 5;
+ else if (strncmp(idstr, "DISP3 BG", 8) == 0)
+ disp_num = 4;
+ }
+ vout->crop_bounds[disp_num].left = 0;
+ vout->crop_bounds[disp_num].top = 0;
+ vout->crop_bounds[disp_num].width =
+ registered_fb[i]->var.xres;
+ vout->crop_bounds[disp_num].height =
+ registered_fb[i]->var.yres;
+ vout->output_enabled[disp_num] = true;
+ vout->output_fb_num[disp_num] = i;
+ if (vout->cur_disp_output == -1)
+ vout->cur_disp_output = disp_num;
+ }
+
+ }
+ vout->crop_current = vout->crop_bounds[vout->cur_disp_output];
+
+ platform_set_drvdata(pdev, vout);
+
+ return 0;
+}
+
+static int mxc_v4l2out_remove(struct platform_device *pdev)
+{
+ vout_data *vout = platform_get_drvdata(pdev);
+
+ if (vout->video_dev) {
+ if (-1 != vout->video_dev->minor)
+ video_unregister_device(vout->video_dev);
+ else
+ video_device_release(vout->video_dev);
+ vout->video_dev = NULL;
+ }
+
+ platform_set_drvdata(pdev, NULL);
+
+ kfree(vout);
+
+ return 0;
+}
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxc_v4l2out_driver = {
+ .driver = {
+ .name = "MXC Video Output",
+ },
+ .probe = mxc_v4l2out_probe,
+ .remove = mxc_v4l2out_remove,
+};
+
+static struct platform_device mxc_v4l2out_device = {
+ .name = "MXC Video Output",
+ .id = 0,
+};
+
+/*!
+ * mxc v4l2 init function
+ *
+ */
+static int mxc_v4l2out_init(void)
+{
+ u8 err = 0;
+
+ err = platform_driver_register(&mxc_v4l2out_driver);
+ if (err == 0)
+ platform_device_register(&mxc_v4l2out_device);
+ return err;
+}
+
+/*!
+ * mxc v4l2 cleanup function
+ *
+ */
+static void mxc_v4l2out_clean(void)
+{
+ video_unregister_device(g_vout->video_dev);
+
+ platform_driver_unregister(&mxc_v4l2out_driver);
+ platform_device_unregister(&mxc_v4l2out_device);
+ kfree(g_vout);
+ g_vout = NULL;
+}
+
+module_init(mxc_v4l2out_init);
+module_exit(mxc_v4l2out_clean);
+
+module_param(video_nr, int, 0444);
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("V4L2-driver for MXC video output");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("video");
diff --git a/drivers/media/video/mxc/output/mxc_v4l2_output.c b/drivers/media/video/mxc/output/mxc_v4l2_output.c
new file mode 100644
index 000000000000..ce577c4c27d3
--- /dev/null
+++ b/drivers/media/video/mxc/output/mxc_v4l2_output.c
@@ -0,0 +1,2614 @@
+/*
+ * Copyright 2005-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file drivers/media/video/mxc/output/mxc_v4l2_output.c
+ *
+ * @brief MXC V4L2 Video Output Driver
+ *
+ * Video4Linux2 Output Device using MXC IPU Post-processing functionality.
+ *
+ * @ingroup MXC_V4L2_OUTPUT
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/semaphore.h>
+#include <linux/console.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/mxcfb.h>
+#include <media/v4l2-ioctl.h>
+#include <asm/cacheflush.h>
+#include <mach/hardware.h>
+
+#include "mxc_v4l2_output.h"
+
+#define INTERLACED_CONTENT(vout) (((cpu_is_mx51_rev(CHIP_REV_2_0) >= 1) || \
+ cpu_is_mx53()) && \
+ (((vout)->field_fmt == V4L2_FIELD_INTERLACED_TB) || \
+ ((vout)->field_fmt == V4L2_FIELD_INTERLACED_BT)))
+#define LOAD_3FIELDS(vout) ((INTERLACED_CONTENT(vout)) && \
+ ((vout)->motion_sel != HIGH_MOTION))
+
+struct v4l2_output mxc_outputs[1] = {
+ {
+ .index = MXC_V4L2_OUT_2_SDC,
+ .name = "DISP3 Video Out",
+ .type = V4L2_OUTPUT_TYPE_ANALOG, /* not really correct,
+ but no other choice */
+ .audioset = 0,
+ .modulator = 0,
+ .std = V4L2_STD_UNKNOWN}
+};
+
+static int video_nr = 16;
+static spinlock_t g_lock = SPIN_LOCK_UNLOCKED;
+static int last_index_n;
+static unsigned int ipu_ic_out_max_width_size;
+static unsigned int ipu_ic_out_max_height_size;
+/* debug counters */
+uint32_t g_irq_cnt;
+uint32_t g_buf_output_cnt;
+uint32_t g_buf_q_cnt;
+uint32_t g_buf_dq_cnt;
+
+#define QUEUE_SIZE (MAX_FRAME_NUM + 1)
+static __inline int queue_size(v4l_queue * q)
+{
+ if (q->tail >= q->head)
+ return (q->tail - q->head);
+ else
+ return ((q->tail + QUEUE_SIZE) - q->head);
+}
+
+static __inline int queue_buf(v4l_queue * q, int idx)
+{
+ if (((q->tail + 1) % QUEUE_SIZE) == q->head)
+ return -1; /* queue full */
+ q->list[q->tail] = idx;
+ q->tail = (q->tail + 1) % QUEUE_SIZE;
+ return 0;
+}
+
+static __inline int dequeue_buf(v4l_queue * q)
+{
+ int ret;
+ if (q->tail == q->head)
+ return -1; /* queue empty */
+ ret = q->list[q->head];
+ q->head = (q->head + 1) % QUEUE_SIZE;
+ return ret;
+}
+
+static __inline int peek_next_buf(v4l_queue * q)
+{
+ if (q->tail == q->head)
+ return -1; /* queue empty */
+ return q->list[q->head];
+}
+
+static __inline unsigned long get_jiffies(struct timeval *t)
+{
+ struct timeval cur;
+
+ if (t->tv_usec >= 1000000) {
+ t->tv_sec += t->tv_usec / 1000000;
+ t->tv_usec = t->tv_usec % 1000000;
+ }
+
+ do_gettimeofday(&cur);
+ if ((t->tv_sec < cur.tv_sec)
+ || ((t->tv_sec == cur.tv_sec) && (t->tv_usec < cur.tv_usec)))
+ return jiffies;
+
+ if (t->tv_usec < cur.tv_usec) {
+ cur.tv_sec = t->tv_sec - cur.tv_sec - 1;
+ cur.tv_usec = t->tv_usec + 1000000 - cur.tv_usec;
+ } else {
+ cur.tv_sec = t->tv_sec - cur.tv_sec;
+ cur.tv_usec = t->tv_usec - cur.tv_usec;
+ }
+
+ return jiffies + timeval_to_jiffies(&cur);
+}
+
+/*!
+ * Private function to free buffers
+ *
+ * @param bufs_paddr Array of physical address of buffers to be freed
+ *
+ * @param bufs_vaddr Array of virtual address of buffers to be freed
+ *
+ * @param num_buf Number of buffers to be freed
+ *
+ * @param size Size for each buffer to be free
+ *
+ * @return status 0 success.
+ */
+static int mxc_free_buffers(dma_addr_t bufs_paddr[], void *bufs_vaddr[],
+ int num_buf, int size)
+{
+ int i;
+
+ for (i = 0; i < num_buf; i++) {
+ if (bufs_vaddr[i] != 0) {
+ dma_free_coherent(0, size, bufs_vaddr[i],
+ bufs_paddr[i]);
+ pr_debug("freed @ paddr=0x%08X\n", (u32) bufs_paddr[i]);
+ bufs_paddr[i] = 0;
+ bufs_vaddr[i] = NULL;
+ }
+ }
+ return 0;
+}
+
+/*!
+ * Private function to allocate buffers
+ *
+ * @param bufs_paddr Output array of physical address of buffers allocated
+ *
+ * @param bufs_vaddr Output array of virtual address of buffers allocated
+ *
+ * @param num_buf Input number of buffers to allocate
+ *
+ * @param size Input size for each buffer to allocate
+ *
+ * @return status -0 Successfully allocated a buffer, -ENOBUFS failed.
+ */
+static int mxc_allocate_buffers(dma_addr_t bufs_paddr[], void *bufs_vaddr[],
+ int num_buf, int size)
+{
+ int i;
+
+ for (i = 0; i < num_buf; i++) {
+ bufs_vaddr[i] = dma_alloc_coherent(0, size,
+ &bufs_paddr[i],
+ GFP_DMA | GFP_KERNEL);
+
+ if (bufs_vaddr[i] == 0) {
+ mxc_free_buffers(bufs_paddr, bufs_vaddr, i, size);
+ printk(KERN_ERR "dma_alloc_coherent failed.\n");
+ return -ENOBUFS;
+ }
+ pr_debug("allocated @ paddr=0x%08X, size=%d.\n",
+ (u32) bufs_paddr[i], size);
+ }
+
+ return 0;
+}
+
+/*
+ * Returns bits per pixel for given pixel format
+ *
+ * @param pixelformat V4L2_PIX_FMT_RGB565, V4L2_PIX_FMT_BGR24 or V4L2_PIX_FMT_BGR32
+ *
+ * @return bits per pixel of pixelformat
+ */
+static u32 fmt_to_bpp(u32 pixelformat)
+{
+ u32 bpp;
+
+ bpp = 8 * bytes_per_pixel(pixelformat);
+ return bpp;
+}
+
+static bool format_is_yuv(u32 pixelformat)
+{
+ switch (pixelformat) {
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_YUV422P:
+ case V4L2_PIX_FMT_YVU420:
+ case V4L2_PIX_FMT_NV12:
+ return true;
+ break;
+ }
+ return false;
+}
+
+static u32 bpp_to_fmt(struct fb_info *fbi)
+{
+ if (fbi->var.nonstd)
+ return fbi->var.nonstd;
+
+ if (fbi->var.bits_per_pixel == 24)
+ return V4L2_PIX_FMT_BGR24;
+ else if (fbi->var.bits_per_pixel == 32)
+ return V4L2_PIX_FMT_BGR32;
+ else if (fbi->var.bits_per_pixel == 16)
+ return V4L2_PIX_FMT_RGB565;
+
+ return 0;
+}
+
+/*
+ * we are using double buffer for video playback, ipu need make
+ * sure current buffer should not be the same buffer of next display
+ * one.
+ */
+static int select_display_buffer(vout_data *vout, int next_buf)
+{
+ int ret = 0;
+
+ if (ipu_get_cur_buffer_idx(vout->display_ch, IPU_INPUT_BUFFER)
+ != next_buf)
+ ret = ipu_select_buffer(vout->display_ch, IPU_INPUT_BUFFER,
+ next_buf);
+ else
+ dev_dbg(&vout->video_dev->dev,
+ "display buffer not ready for select\n");
+ return ret;
+}
+
+static void setup_next_buf_timer(vout_data *vout, int index)
+{
+ unsigned long timeout;
+
+ /* Setup timer for next buffer */
+ /* if timestamp is 0, then default to 30fps */
+ if ((vout->v4l2_bufs[index].timestamp.tv_sec == 0)
+ && (vout->v4l2_bufs[index].timestamp.tv_usec == 0)
+ && vout->start_jiffies)
+ timeout =
+ vout->start_jiffies + vout->frame_count * HZ / 30;
+ else
+ timeout =
+ get_jiffies(&vout->v4l2_bufs[index].timestamp);
+
+ if (jiffies >= timeout) {
+ dev_dbg(&vout->video_dev->dev,
+ "warning: timer timeout already expired.\n");
+ }
+ if (mod_timer(&vout->output_timer, timeout))
+ dev_dbg(&vout->video_dev->dev,
+ "warning: timer was already set\n");
+
+ dev_dbg(&vout->video_dev->dev,
+ "timer handler next schedule: %lu\n", timeout);
+}
+
+static int finish_previous_frame(vout_data *vout)
+{
+ struct fb_info *fbi =
+ registered_fb[vout->output_fb_num[vout->cur_disp_output]];
+ mm_segment_t old_fs;
+ int ret = 0;
+
+ /* make sure buf[next_done_ipu_buf] showed */
+ while (ipu_check_buffer_busy(vout->display_ch,
+ IPU_INPUT_BUFFER, vout->next_done_ipu_buf)) {
+ /* wait for display frame finish */
+ if (fbi->fbops->fb_ioctl) {
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+ ret = fbi->fbops->fb_ioctl(fbi, MXCFB_WAIT_FOR_VSYNC,
+ (unsigned int)NULL);
+ set_fs(old_fs);
+
+ if (ret < 0) {
+ /* ic_bypass need clear display buffer ready for next update*/
+ ipu_clear_buffer_ready(vout->display_ch, IPU_INPUT_BUFFER,
+ vout->next_done_ipu_buf);
+ }
+ }
+ }
+
+ return ret;
+}
+
+static int show_current_frame(vout_data *vout)
+{
+ struct fb_info *fbi =
+ registered_fb[vout->output_fb_num[vout->cur_disp_output]];
+ mm_segment_t old_fs;
+ int ret = 0;
+
+ /* make sure buf[next_rdy_ipu_buf] begin to show */
+ if (ipu_get_cur_buffer_idx(vout->display_ch, IPU_INPUT_BUFFER)
+ != vout->next_rdy_ipu_buf) {
+ /* wait for display frame finish */
+ if (fbi->fbops->fb_ioctl) {
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+ ret = fbi->fbops->fb_ioctl(fbi, MXCFB_WAIT_FOR_VSYNC,
+ (unsigned int)NULL);
+ set_fs(old_fs);
+ }
+ }
+
+ return ret;
+}
+
+static void timer_work_func(struct work_struct *work)
+{
+ vout_data *vout =
+ container_of(work, vout_data, timer_work);
+ int index, ret;
+ int last_buf;
+ unsigned long lock_flags = 0;
+
+ finish_previous_frame(vout);
+
+ spin_lock_irqsave(&g_lock, lock_flags);
+
+ if (g_buf_output_cnt == 0) {
+ ipu_select_buffer(vout->display_ch, IPU_INPUT_BUFFER, 1);
+ } else {
+ index = dequeue_buf(&vout->ready_q);
+ if (index == -1) { /* no buffers ready, should never occur */
+ dev_err(&vout->video_dev->dev,
+ "mxc_v4l2out: timer - no queued buffers ready\n");
+ goto exit;
+ }
+ g_buf_dq_cnt++;
+ vout->frame_count++;
+
+ vout->ipu_buf[vout->next_rdy_ipu_buf] = index;
+ ret = ipu_update_channel_buffer(vout->display_ch, IPU_INPUT_BUFFER,
+ vout->next_rdy_ipu_buf,
+ vout->v4l2_bufs[index].m.offset);
+ ret += select_display_buffer(vout, vout->next_rdy_ipu_buf);
+ if (ret < 0) {
+ dev_err(&vout->video_dev->dev,
+ "unable to update buffer %d address rc=%d\n",
+ vout->next_rdy_ipu_buf, ret);
+ goto exit;
+ }
+ spin_unlock_irqrestore(&g_lock, lock_flags);
+ show_current_frame(vout);
+ spin_lock_irqsave(&g_lock, lock_flags);
+ vout->next_rdy_ipu_buf = !vout->next_rdy_ipu_buf;
+ }
+
+ last_buf = vout->ipu_buf[vout->next_done_ipu_buf];
+ if (last_buf != -1) {
+ g_buf_output_cnt++;
+ vout->v4l2_bufs[last_buf].flags = V4L2_BUF_FLAG_DONE;
+ queue_buf(&vout->done_q, last_buf);
+ wake_up_interruptible(&vout->v4l_bufq);
+ vout->ipu_buf[vout->next_done_ipu_buf] = -1;
+ vout->next_done_ipu_buf = !vout->next_done_ipu_buf;
+ }
+
+ if (g_buf_output_cnt > 0) {
+ /* Setup timer for next buffer */
+ index = peek_next_buf(&vout->ready_q);
+ if (index != -1)
+ setup_next_buf_timer(vout, index);
+ else
+ vout->state = STATE_STREAM_PAUSED;
+
+ if (vout->state == STATE_STREAM_STOPPING) {
+ if ((vout->ipu_buf[0] == -1) && (vout->ipu_buf[1] == -1)) {
+ vout->state = STATE_STREAM_OFF;
+ }
+ }
+ }
+exit:
+ spin_unlock_irqrestore(&g_lock, lock_flags);
+}
+
+static void mxc_v4l2out_timer_handler(unsigned long arg)
+{
+ int index, ret;
+ unsigned long lock_flags = 0;
+ vout_data *vout = (vout_data *) arg;
+
+ spin_lock_irqsave(&g_lock, lock_flags);
+
+ if ((vout->state == STATE_STREAM_STOPPING)
+ || (vout->state == STATE_STREAM_OFF))
+ goto exit0;
+
+ /*
+ * If timer occurs before IPU h/w is ready, then set the state to
+ * paused and the timer will be set again when next buffer is queued
+ * or PP comletes
+ */
+ if (vout->ipu_buf[vout->next_rdy_ipu_buf] != -1) {
+ dev_dbg(&vout->video_dev->dev, "IPU buffer busy\n");
+ vout->state = STATE_STREAM_PAUSED;
+ goto exit0;
+ }
+
+ /* VDI need both buffer done before update buffer? */
+ if (INTERLACED_CONTENT(vout) &&
+ (vout->ipu_buf[!vout->next_rdy_ipu_buf] != -1)) {
+ dev_dbg(&vout->video_dev->dev, "IPU buffer busy\n");
+ vout->state = STATE_STREAM_PAUSED;
+ goto exit0;
+ }
+
+ /* Handle ic bypass mode in work queue */
+ if (vout->ic_bypass) {
+ if (queue_work(vout->v4l_wq, &vout->timer_work) == 0) {
+ dev_err(&vout->video_dev->dev, "work was in queue already!\n ");
+ vout->state = STATE_STREAM_PAUSED;
+ }
+ goto exit0;
+ }
+
+ /* Dequeue buffer and pass to IPU */
+ index = dequeue_buf(&vout->ready_q);
+ if (index == -1) { /* no buffers ready, should never occur */
+ dev_err(&vout->video_dev->dev,
+ "mxc_v4l2out: timer - no queued buffers ready\n");
+ goto exit0;
+ }
+ g_buf_dq_cnt++;
+ vout->frame_count++;
+
+ /* update next buffer */
+ if (LOAD_3FIELDS(vout)) {
+ int index_n = index;
+ int index_p = last_index_n;
+ vout->ipu_buf_p[vout->next_rdy_ipu_buf] = last_index_n;
+ vout->ipu_buf[vout->next_rdy_ipu_buf] = index;
+ vout->ipu_buf_n[vout->next_rdy_ipu_buf] = index;
+ ret = ipu_update_channel_buffer(vout->post_proc_ch,
+ IPU_INPUT_BUFFER,
+ vout->next_rdy_ipu_buf,
+ vout->v4l2_bufs[index].m.offset);
+ ret += ipu_update_channel_buffer(MEM_VDI_PRP_VF_MEM_P,
+ IPU_INPUT_BUFFER,
+ vout->next_rdy_ipu_buf,
+ vout->v4l2_bufs[index_p].m.offset + vout->bytesperline);
+ ret += ipu_update_channel_buffer(MEM_VDI_PRP_VF_MEM_N,
+ IPU_INPUT_BUFFER,
+ vout->next_rdy_ipu_buf,
+ vout->v4l2_bufs[index_n].m.offset) + vout->bytesperline;
+ last_index_n = index;
+ } else {
+ vout->ipu_buf[vout->next_rdy_ipu_buf] = index;
+ if (vout->pp_split) {
+ vout->ipu_buf[!vout->next_rdy_ipu_buf] = index;
+ /* always left stripe */
+ ret = ipu_update_channel_buffer(vout->post_proc_ch,
+ IPU_INPUT_BUFFER,
+ 0,/* vout->next_rdy_ipu_buf,*/
+ (vout->v4l2_bufs[index].m.offset) +
+ vout->pp_left_stripe.input_column +
+ vout->pp_up_stripe.input_column * vout->bytesperline);
+
+ /* the U/V offset has to be updated inside of IDMAC */
+ /* according to stripe offset */
+ ret += ipu_update_channel_offset(vout->post_proc_ch,
+ IPU_INPUT_BUFFER,
+ vout->v2f.fmt.pix.pixelformat,
+ vout->v2f.fmt.pix.width,
+ vout->v2f.fmt.pix.height,
+ vout->bytesperline,
+ vout->offset.u_offset,
+ vout->offset.v_offset,
+ vout->pp_up_stripe.input_column,
+ vout->pp_left_stripe.input_column);
+ } else
+ ret = ipu_update_channel_buffer(vout->post_proc_ch,
+ IPU_INPUT_BUFFER,
+ vout->next_rdy_ipu_buf,
+ vout->v4l2_bufs[index].m.offset);
+ }
+
+ if (ret < 0) {
+ dev_err(&vout->video_dev->dev,
+ "unable to update buffer %d address rc=%d\n",
+ vout->next_rdy_ipu_buf, ret);
+ goto exit0;
+ }
+
+ /* set next buffer ready */
+ if (LOAD_3FIELDS(vout))
+ ret = ipu_select_multi_vdi_buffer(vout->next_rdy_ipu_buf);
+ else
+ ret = ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER,
+ vout->next_rdy_ipu_buf);
+ if (ret < 0) {
+ dev_err(&vout->video_dev->dev,
+ "unable to set IPU buffer ready\n");
+ goto exit0;
+ }
+
+ /* Non IC split action */
+ if (!vout->pp_split)
+ vout->next_rdy_ipu_buf = !vout->next_rdy_ipu_buf;
+
+ /* Setup timer for next buffer */
+ index = peek_next_buf(&vout->ready_q);
+ if (index != -1)
+ setup_next_buf_timer(vout, index);
+ else
+ vout->state = STATE_STREAM_PAUSED;
+
+ if (vout->state == STATE_STREAM_STOPPING) {
+ if ((vout->ipu_buf[0] == -1) && (vout->ipu_buf[1] == -1)) {
+ vout->state = STATE_STREAM_OFF;
+ }
+ }
+
+ spin_unlock_irqrestore(&g_lock, lock_flags);
+
+ return;
+
+exit0:
+ spin_unlock_irqrestore(&g_lock, lock_flags);
+}
+
+static irqreturn_t mxc_v4l2out_work_irq_handler(int irq, void *dev_id)
+{
+ int last_buf;
+ int index;
+ unsigned long lock_flags = 0;
+ vout_data *vout = dev_id;
+ int pp_out_buf_left_right = 0;
+ int disp_buf_num = 0;
+ int disp_buf_num_next = 1;
+ int local_buffer = 0;
+ int pp_out_buf_offset = 0;
+ int pp_out_buf_up_down = 0;
+ int release_buffer = 0;
+ u32 eba_offset = 0;
+ u32 vertical_offset = 0;
+ u16 x_pos;
+ u16 y_pos;
+ int ret = -1;
+
+ spin_lock_irqsave(&g_lock, lock_flags);
+
+ g_irq_cnt++;
+
+ /* Process previous buffer */
+ if (LOAD_3FIELDS(vout))
+ last_buf = vout->ipu_buf_p[vout->next_done_ipu_buf];
+ else
+ last_buf = vout->ipu_buf[vout->next_done_ipu_buf];
+
+ if (last_buf != -1) {
+ /* If IC split mode on, update output buffer number */
+ if (vout->pp_split) {
+ pp_out_buf_up_down = vout->pp_split_buf_num & 1;/* left/right stripe */
+ pp_out_buf_left_right = (vout->pp_split_buf_num >> 1) & 1; /* up/down */
+ local_buffer = (vout->pp_split == 1) ? pp_out_buf_up_down :
+ pp_out_buf_left_right;
+ disp_buf_num = vout->pp_split_buf_num >> 2;
+ disp_buf_num_next =
+ ((vout->pp_split_buf_num + (vout->pp_split << 0x1)) & 7) >> 2;
+ if ((!pp_out_buf_left_right) ||
+ ((!pp_out_buf_up_down) && (vout->pp_split == 1))) {
+ if (vout->pp_split == 1) {
+ eba_offset = ((pp_out_buf_left_right + pp_out_buf_up_down) & 1) ?
+ vout->pp_right_stripe.input_column :
+ vout->pp_left_stripe.input_column;
+ vertical_offset = pp_out_buf_up_down ?
+ vout->pp_up_stripe.input_column :
+ vout->pp_down_stripe.input_column;
+
+ } else {
+ eba_offset = pp_out_buf_left_right ?
+ vout->pp_left_stripe.input_column :
+ vout->pp_right_stripe.input_column;
+ vertical_offset = pp_out_buf_left_right ?
+ vout->pp_up_stripe.input_column :
+ vout->pp_down_stripe.input_column;
+ }
+
+ ret = ipu_update_channel_buffer(vout->post_proc_ch,
+ IPU_INPUT_BUFFER,
+ (1 - local_buffer),
+ (vout->v4l2_bufs[vout->ipu_buf[disp_buf_num]].m.offset)
+ + eba_offset + vertical_offset * vout->bytesperline);
+ ret += ipu_update_channel_offset(vout->post_proc_ch,
+ IPU_INPUT_BUFFER,
+ vout->v2f.fmt.pix.pixelformat,
+ vout->v2f.fmt.pix.width,
+ vout->v2f.fmt.pix.height,
+ vout->bytesperline,
+ vout->offset.u_offset,
+ vout->offset.v_offset,
+ vertical_offset,
+ eba_offset);
+
+ /* select right stripe */
+ ret += ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER,
+ (1 - local_buffer));
+ if (ret < 0)
+ dev_err(&vout->video_dev->dev,
+ "unable to set IPU buffer ready\n");
+ vout->next_rdy_ipu_buf = !vout->next_rdy_ipu_buf;
+
+ } else {/* last stripe is done, run display refresh */
+ select_display_buffer(vout, disp_buf_num);
+ vout->ipu_buf[vout->next_done_ipu_buf] = -1;
+ vout->next_done_ipu_buf = !vout->next_done_ipu_buf;
+ vout->next_rdy_ipu_buf = !vout->next_rdy_ipu_buf;
+ }
+
+ /* offset for next buffer's EBA */
+ eba_offset = 0;
+ if (vout->pp_split == 1) {
+ pp_out_buf_offset = ((vout->pp_split_buf_num >> 1) & 1) ?
+ vout->pp_left_stripe.output_column :
+ vout->pp_right_stripe.output_column;
+
+ eba_offset = ((vout->pp_split_buf_num & 1) ?
+ vout->pp_down_stripe.output_column :
+ vout->pp_up_stripe.output_column);
+
+ } else {
+ pp_out_buf_offset = ((vout->pp_split_buf_num >> 1) & 1) ?
+ vout->pp_right_stripe.output_column :
+ vout->pp_left_stripe.output_column;
+ eba_offset = ((vout->pp_split_buf_num >> 1) & 1) ?
+ vout->pp_down_stripe.output_column :
+ vout->pp_up_stripe.output_column;
+ }
+
+ if (vout->cur_disp_output == 5) {
+ x_pos = (vout->crop_current.left / 8) * 8;
+ y_pos = vout->crop_current.top;
+ eba_offset += (vout->xres * y_pos + x_pos) * vout->bpp / 8;
+ }
+
+
+ /* next buffer update */
+ eba_offset = vout->display_bufs[disp_buf_num_next] +
+ pp_out_buf_offset + eba_offset;
+
+ ipu_update_channel_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER,
+ local_buffer, eba_offset);
+
+ /* next buffer ready */
+ ret = ipu_select_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER, local_buffer);
+
+ /* next stripe_buffer index 0..7 */
+ vout->pp_split_buf_num = (vout->pp_split_buf_num + vout->pp_split) & 0x7;
+
+
+ } else {
+ /* show to display */
+ select_display_buffer(vout, vout->next_done_ipu_buf);
+ ret += ipu_select_buffer(vout->display_input_ch, IPU_OUTPUT_BUFFER,
+ vout->next_done_ipu_buf);
+ }
+
+ /* release buffer. For split mode: if second stripe is done */
+ release_buffer = vout->pp_split ? (!(vout->pp_split_buf_num & 0x3)) : 1;
+ if (release_buffer) {
+ g_buf_output_cnt++;
+ vout->v4l2_bufs[last_buf].flags = V4L2_BUF_FLAG_DONE;
+ queue_buf(&vout->done_q, last_buf);
+ wake_up_interruptible(&vout->v4l_bufq);
+ vout->ipu_buf[vout->next_done_ipu_buf] = -1;
+ if (LOAD_3FIELDS(vout)) {
+ vout->ipu_buf_p[vout->next_done_ipu_buf] = -1;
+ vout->ipu_buf_n[vout->next_done_ipu_buf] = -1;
+ }
+ vout->next_done_ipu_buf = !vout->next_done_ipu_buf;
+ }
+ } /* end of last_buf != -1 */
+
+ if (vout->state == STATE_STREAM_STOPPING) {
+ if ((vout->ipu_buf[0] == -1) && (vout->ipu_buf[1] == -1)) {
+ vout->state = STATE_STREAM_OFF;
+ }
+ } else if ((vout->state == STATE_STREAM_PAUSED)
+ && ((index = peek_next_buf(&vout->ready_q)) != -1)) {
+ /* Setup timer for next buffer, when stream has been paused */
+ pr_debug("next index %d\n", index);
+ setup_next_buf_timer(vout, index);
+ vout->state = STATE_STREAM_ON;
+ }
+
+ spin_unlock_irqrestore(&g_lock, lock_flags);
+
+ return IRQ_HANDLED;
+}
+
+/*!
+ * Initialize VDI channels
+ *
+ * @param vout structure vout_data *
+ *
+ * @return status 0 Success
+ */
+static int init_VDI_channel(vout_data *vout, ipu_channel_params_t params)
+{
+ struct device *dev = &vout->video_dev->dev;
+
+ if (ipu_init_channel(MEM_VDI_PRP_VF_MEM, &params) != 0) {
+ dev_dbg(dev, "Error initializing VDI current channel\n");
+ return -EINVAL;
+ }
+ if (LOAD_3FIELDS(vout)) {
+ if (ipu_init_channel(MEM_VDI_PRP_VF_MEM_P, &params) != 0) {
+ dev_err(dev, "Error initializing VDI previous channel\n");
+ return -EINVAL;
+ }
+ if (ipu_init_channel(MEM_VDI_PRP_VF_MEM_N, &params) != 0) {
+ dev_err(dev, "Error initializing VDI next channel\n");
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+/*!
+ * Initialize VDI channel buffers
+ *
+ * @param vout structure vout_data *
+ *
+ * @return status 0 Success
+ */
+static int init_VDI_in_channel_buffer(vout_data *vout, uint32_t in_pixel_fmt,
+ uint16_t in_width, uint16_t in_height,
+ uint32_t stride,
+ uint32_t u_offset, uint32_t v_offset)
+{
+ struct device *dev = &vout->video_dev->dev;
+
+ if (ipu_init_channel_buffer(MEM_VDI_PRP_VF_MEM, IPU_INPUT_BUFFER,
+ in_pixel_fmt, in_width, in_height, stride,
+ IPU_ROTATE_NONE,
+ vout->v4l2_bufs[vout->ipu_buf[0]].m.offset,
+ vout->v4l2_bufs[vout->ipu_buf[0]].m.offset,
+ u_offset, v_offset) != 0) {
+ dev_err(dev, "Error initializing VDI current input buffer\n");
+ return -EINVAL;
+ }
+ if (LOAD_3FIELDS(vout)) {
+ if (ipu_init_channel_buffer(MEM_VDI_PRP_VF_MEM_P,
+ IPU_INPUT_BUFFER,
+ in_pixel_fmt, in_width, in_height,
+ stride, IPU_ROTATE_NONE,
+ vout->v4l2_bufs[vout->ipu_buf_p[0]].m.offset+vout->bytesperline,
+ vout->v4l2_bufs[vout->ipu_buf_p[0]].m.offset+vout->bytesperline,
+ u_offset, v_offset) != 0) {
+ dev_err(dev, "Error initializing VDI previous input buffer\n");
+ return -EINVAL;
+ }
+ if (ipu_init_channel_buffer(MEM_VDI_PRP_VF_MEM_N,
+ IPU_INPUT_BUFFER,
+ in_pixel_fmt, in_width, in_height,
+ stride, IPU_ROTATE_NONE,
+ vout->v4l2_bufs[vout->ipu_buf_n[0]].m.offset+vout->bytesperline,
+ vout->v4l2_bufs[vout->ipu_buf_n[0]].m.offset+vout->bytesperline,
+ u_offset, v_offset) != 0) {
+ dev_err(dev, "Error initializing VDI next input buffer\n");
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+/*!
+ * Initialize VDI path
+ *
+ * @param vout structure vout_data *
+ *
+ * @return status 0 Success
+ */
+static int init_VDI(ipu_channel_params_t params, vout_data *vout,
+ struct device *dev, struct fb_info *fbi,
+ u16 out_width, u16 out_height)
+{
+ params.mem_prp_vf_mem.in_width = vout->v2f.fmt.pix.width;
+ params.mem_prp_vf_mem.in_height = vout->v2f.fmt.pix.height;
+ params.mem_prp_vf_mem.motion_sel = vout->motion_sel;
+ params.mem_prp_vf_mem.field_fmt = vout->field_fmt;
+ params.mem_prp_vf_mem.in_pixel_fmt = vout->v2f.fmt.pix.pixelformat;
+ params.mem_prp_vf_mem.out_width = out_width;
+ params.mem_prp_vf_mem.out_height = out_height;
+ params.mem_prp_vf_mem.out_pixel_fmt = bpp_to_fmt(fbi);
+
+ if (init_VDI_channel(vout, params) != 0) {
+ dev_err(dev, "Error init_VDI_channel channel\n");
+ return -EINVAL;
+ }
+
+ if (init_VDI_in_channel_buffer(vout,
+ params.mem_prp_vf_mem.in_pixel_fmt,
+ params.mem_prp_vf_mem.in_width,
+ params.mem_prp_vf_mem.in_height,
+ bytes_per_pixel(params.mem_prp_vf_mem.
+ in_pixel_fmt),
+ vout->offset.u_offset,
+ vout->offset.v_offset) != 0) {
+ return -EINVAL;
+ }
+
+ if (!ipu_can_rotate_in_place(vout->rotate)) {
+ if (vout->rot_pp_bufs[0]) {
+ mxc_free_buffers(vout->rot_pp_bufs,
+ vout->rot_pp_bufs_vaddr, 2,
+ vout->display_buf_size);
+ }
+ if (mxc_allocate_buffers
+ (vout->rot_pp_bufs, vout->rot_pp_bufs_vaddr, 2,
+ vout->display_buf_size) < 0) {
+ return -ENOBUFS;
+ }
+
+ if (ipu_init_channel_buffer(vout->post_proc_ch,
+ IPU_OUTPUT_BUFFER,
+ params.mem_prp_vf_mem.
+ out_pixel_fmt, out_width,
+ out_height, out_width,
+ IPU_ROTATE_NONE,
+ vout->rot_pp_bufs[0],
+ vout->rot_pp_bufs[1], 0, 0) != 0) {
+ dev_err(dev, "Error initializing PRP output buffer\n");
+ return -EINVAL;
+ }
+
+ if (ipu_init_channel(MEM_ROT_VF_MEM, NULL) != 0) {
+ dev_err(dev, "Error initializing PP ROT channel\n");
+ return -EINVAL;
+ }
+ if (ipu_init_channel_buffer(MEM_ROT_VF_MEM,
+ IPU_INPUT_BUFFER,
+ params.mem_prp_vf_mem.
+ out_pixel_fmt, out_width,
+ out_height, out_width,
+ vout->rotate,
+ vout->rot_pp_bufs[0],
+ vout->rot_pp_bufs[1], 0, 0) != 0) {
+ dev_err(dev,
+ "Error initializing PP ROT input buffer\n");
+ return -EINVAL;
+ }
+
+ /* swap width and height */
+ if (vout->rotate >= IPU_ROTATE_90_RIGHT) {
+ out_width = vout->crop_current.width;
+ out_height = vout->crop_current.height;
+ }
+
+ if (ipu_init_channel_buffer(MEM_ROT_VF_MEM,
+ IPU_OUTPUT_BUFFER,
+ params.mem_prp_vf_mem.
+ out_pixel_fmt, out_width,
+ out_height, out_width,
+ IPU_ROTATE_NONE,
+ vout->display_bufs[0],
+ vout->display_bufs[1], 0, 0) != 0) {
+ dev_err(dev,
+ "Error initializing PP-VDI output buffer\n");
+ return -EINVAL;
+ }
+
+ if (ipu_link_channels(vout->post_proc_ch, MEM_ROT_VF_MEM) < 0)
+ return -EINVAL;
+
+ vout->display_input_ch = MEM_ROT_VF_MEM;
+ ipu_enable_channel(MEM_ROT_VF_MEM);
+ ipu_select_buffer(MEM_ROT_VF_MEM, IPU_OUTPUT_BUFFER, 0);
+ ipu_select_buffer(MEM_ROT_VF_MEM, IPU_OUTPUT_BUFFER, 1);
+ } else {
+ if (ipu_init_channel_buffer(vout->post_proc_ch,
+ IPU_OUTPUT_BUFFER,
+ params.mem_prp_vf_mem.
+ out_pixel_fmt, out_width,
+ out_height, out_width,
+ vout->rotate,
+ vout->display_bufs[0],
+ vout->display_bufs[1], 0, 0) != 0) {
+ dev_err(dev,
+ "Error initializing PP-VDI output buffer\n");
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+/*!
+ * Initialize PP path
+ *
+ * @param params structure ipu_channel_params_t
+ *
+ * @param vout structure vout_data *
+ *
+ * @return status 0 Success
+ */
+static int init_PP(ipu_channel_params_t *params, vout_data *vout,
+ struct device *dev, struct fb_info *fbi,
+ u16 out_width, u16 out_height)
+{
+ u16 in_width, out_stride; /* stride of output channel */
+ u32 eba_offset;
+ u16 x_pos;
+ u16 y_pos;
+ eba_offset = 0;
+ x_pos = 0;
+ y_pos = 0;
+
+ params->mem_pp_mem.out_pixel_fmt = bpp_to_fmt(fbi);
+
+ if (vout->cur_disp_output == 5) {
+ x_pos = (vout->crop_current.left / 8) * 8;
+ y_pos = vout->crop_current.top;
+ eba_offset = (vout->xres*y_pos + x_pos) *
+ bytes_per_pixel(params->mem_pp_mem.out_pixel_fmt);
+ }
+
+ vout->bpp = fmt_to_bpp(params->mem_pp_mem.out_pixel_fmt);
+ out_stride = vout->xres *
+ bytes_per_pixel(params->mem_pp_mem.out_pixel_fmt);
+ in_width = params->mem_pp_mem.in_width = vout->v2f.fmt.pix.width;
+ params->mem_pp_mem.in_height = vout->v2f.fmt.pix.height;
+ params->mem_pp_mem.in_pixel_fmt = vout->v2f.fmt.pix.pixelformat;
+ params->mem_pp_mem.out_width = out_width;
+ params->mem_pp_mem.out_height = out_height;
+ params->mem_pp_mem.outh_resize_ratio = 0; /* 0 means unused */
+ params->mem_pp_mem.outv_resize_ratio = 0; /* 0 means unused */
+ /* split IC by two stripes, the by pass is impossible*/
+ if (vout->pp_split) {
+ vout->pp_left_stripe.input_column = 0;
+ vout->pp_left_stripe.output_column = 0;
+ vout->pp_right_stripe.input_column = 0;
+ vout->pp_right_stripe.output_column = 0;
+ vout->pp_up_stripe.input_column = 0;
+ vout->pp_up_stripe.output_column = 0;
+ vout->pp_down_stripe.input_column = 0;
+ vout->pp_down_stripe.output_column = 0;
+ if (vout->pp_split != 3) {
+ ipu_calc_stripes_sizes(
+ params->mem_pp_mem.in_width, /* input frame width;>1 */
+ params->mem_pp_mem.out_width, /* output frame width; >1 */
+ ipu_ic_out_max_width_size,
+ (((unsigned long long)1) << 32), /* 32bit for fractional*/
+ 1, /* equal stripes */
+ params->mem_pp_mem.in_pixel_fmt,
+ params->mem_pp_mem.out_pixel_fmt,
+ &(vout->pp_left_stripe),
+ &(vout->pp_right_stripe));
+
+ vout->pp_left_stripe.input_column = vout->pp_left_stripe.input_column *
+ fmt_to_bpp(vout->v2f.fmt.pix.pixelformat) / 8;
+ vout->pp_left_stripe.output_column = vout->pp_left_stripe.output_column *
+ fmt_to_bpp(params->mem_pp_mem.out_pixel_fmt) / 8;
+ vout->pp_right_stripe.input_column = vout->pp_right_stripe.input_column *
+ fmt_to_bpp(vout->v2f.fmt.pix.pixelformat) / 8;
+ vout->pp_right_stripe.output_column = vout->pp_right_stripe.output_column *
+ fmt_to_bpp(params->mem_pp_mem.out_pixel_fmt) / 8;
+
+
+ /* updare parameters */
+ params->mem_pp_mem.in_width = vout->pp_left_stripe.input_width;
+ params->mem_pp_mem.out_width = vout->pp_left_stripe.output_width;
+ out_width = vout->pp_left_stripe.output_width;
+ /* for using in ic_init*/
+ params->mem_pp_mem.outh_resize_ratio = vout->pp_left_stripe.irr;
+ }
+ if (vout->pp_split != 2) {
+ ipu_calc_stripes_sizes(
+ params->mem_pp_mem.in_height, /* input frame width;>1 */
+ params->mem_pp_mem.out_height, /* output frame width; >1 */
+ ipu_ic_out_max_height_size,
+ (((unsigned long long)1) << 32),/* 32bit for fractional */
+ 1, /* equal stripes */
+ params->mem_pp_mem.in_pixel_fmt,
+ params->mem_pp_mem.out_pixel_fmt,
+ &(vout->pp_up_stripe),
+ &(vout->pp_down_stripe));
+ vout->pp_down_stripe.output_column = vout->pp_down_stripe.output_column * out_stride;
+ vout->pp_up_stripe.output_column = vout->pp_up_stripe.output_column * out_stride;
+ params->mem_pp_mem.outv_resize_ratio = vout->pp_up_stripe.irr;
+ params->mem_pp_mem.in_height = vout->pp_up_stripe.input_width;/*height*/
+ out_height = vout->pp_up_stripe.output_width;/*height*/
+ if (vout->pp_split == 3)
+ vout->pp_split = 2;/*2 vertical stripe as two horizontal stripes */
+ }
+ vout->pp_split_buf_num = 0;
+ }
+
+ if (ipu_init_channel(vout->post_proc_ch, params) != 0) {
+ dev_err(dev, "Error initializing PP channel\n");
+ return -EINVAL;
+ }
+
+ if (ipu_init_channel_buffer(vout->post_proc_ch,
+ IPU_INPUT_BUFFER,
+ params->mem_pp_mem.in_pixel_fmt,
+ params->mem_pp_mem.in_width,
+ params->mem_pp_mem.in_height,
+ vout->v2f.fmt.pix.bytesperline /
+ bytes_per_pixel(params->mem_pp_mem.
+ in_pixel_fmt),
+ IPU_ROTATE_NONE,
+ vout->v4l2_bufs[vout->ipu_buf[0]].m.offset,
+ vout->v4l2_bufs[vout->ipu_buf[1]].m.offset,
+ vout->offset.u_offset,
+ vout->offset.v_offset) != 0) {
+ dev_err(dev, "Error initializing PP input buffer\n");
+ return -EINVAL;
+ }
+
+ if (!ipu_can_rotate_in_place(vout->rotate)) {
+ if (vout->rot_pp_bufs[0]) {
+ mxc_free_buffers(vout->rot_pp_bufs,
+ vout->rot_pp_bufs_vaddr, 2,
+ vout->display_buf_size);
+ }
+ if (mxc_allocate_buffers
+ (vout->rot_pp_bufs, vout->rot_pp_bufs_vaddr, 2,
+ vout->display_buf_size) < 0) {
+ return -ENOBUFS;
+ }
+
+ if (ipu_init_channel_buffer(vout->post_proc_ch,
+ IPU_OUTPUT_BUFFER,
+ params->mem_pp_mem.
+ out_pixel_fmt, out_width,
+ out_height, out_stride,
+ IPU_ROTATE_NONE,
+ vout->rot_pp_bufs[0] + eba_offset,
+ vout->rot_pp_bufs[1] + eba_offset, 0, 0) != 0) {
+ dev_err(dev, "Error initializing PP output buffer\n");
+ return -EINVAL;
+ }
+
+ if (ipu_init_channel(MEM_ROT_PP_MEM, NULL) != 0) {
+ dev_err(dev, "Error initializing PP ROT channel\n");
+ return -EINVAL;
+ }
+ if (ipu_init_channel_buffer(MEM_ROT_PP_MEM,
+ IPU_INPUT_BUFFER,
+ params->mem_pp_mem.
+ out_pixel_fmt, out_width,
+ out_height, out_stride,
+ vout->rotate,
+ vout->rot_pp_bufs[0],
+ vout->rot_pp_bufs[1], 0, 0) != 0) {
+ dev_err(dev,
+ "Error initializing PP ROT input buffer\n");
+ return -EINVAL;
+ }
+
+ /* swap width and height */
+ if (vout->rotate >= IPU_ROTATE_90_RIGHT) {
+ out_width = vout->crop_current.width;
+ out_height = vout->crop_current.height;
+ }
+
+ if (ipu_init_channel_buffer(MEM_ROT_PP_MEM,
+ IPU_OUTPUT_BUFFER,
+ params->mem_pp_mem.
+ out_pixel_fmt, out_width,
+ out_height, out_stride,
+ IPU_ROTATE_NONE,
+ vout->display_bufs[0] + eba_offset,
+ vout->display_bufs[1] + eba_offset, 0, 0) != 0) {
+ dev_err(dev, "Error initializing PP output buffer\n");
+ return -EINVAL;
+ }
+
+ if (ipu_link_channels(vout->post_proc_ch, MEM_ROT_PP_MEM) < 0)
+ return -EINVAL;
+
+ vout->display_input_ch = MEM_ROT_PP_MEM;
+ ipu_enable_channel(MEM_ROT_PP_MEM);
+ ipu_select_buffer(MEM_ROT_PP_MEM, IPU_OUTPUT_BUFFER, 0);
+ ipu_select_buffer(MEM_ROT_PP_MEM, IPU_OUTPUT_BUFFER, 1);
+ } else {
+ if (ipu_init_channel_buffer(vout->post_proc_ch,
+ IPU_OUTPUT_BUFFER,
+ params->mem_pp_mem.
+ out_pixel_fmt, out_width,
+ out_height, out_stride,
+ vout->rotate,
+ vout->display_bufs[0] + eba_offset,
+ vout->display_bufs[1] + eba_offset, 0, 0) != 0) {
+ dev_err(dev, "Error initializing PP output buffer\n");
+ return -EINVAL;
+ }
+ }
+
+ /* fix EBAs for IDMAC channels */
+ if (vout->pp_split) {
+ ipu_update_channel_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER,
+ 0,
+ vout->v4l2_bufs[vout->ipu_buf[0]].m.offset +
+ vout->pp_left_stripe.input_column +
+ vout->pp_up_stripe.input_column * vout->bytesperline);
+
+
+ ipu_update_channel_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER,
+ 1,
+ vout->v4l2_bufs[vout->ipu_buf[0]].m.offset +
+ vout->pp_right_stripe.input_column +
+ vout->pp_up_stripe.input_column * vout->bytesperline);
+
+ ipu_update_channel_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER,
+ 0,
+ vout->display_bufs[0] + eba_offset +
+ vout->pp_left_stripe.output_column +
+ vout->pp_up_stripe.output_column);
+
+ ipu_update_channel_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER,
+ 1,
+ vout->display_bufs[0] + eba_offset +
+ vout->pp_right_stripe.output_column +
+ vout->pp_up_stripe.output_column);
+ }
+
+ return 0;
+}
+
+/*!
+ * Start the output stream
+ *
+ * @param vout structure vout_data *
+ *
+ * @return status 0 Success
+ */
+static int mxc_v4l2out_streamon(vout_data * vout)
+{
+ struct device *dev = &vout->video_dev->dev;
+ ipu_channel_params_t params;
+ struct mxcfb_pos fb_pos;
+ struct fb_var_screeninfo fbvar;
+ struct fb_info *fbi =
+ registered_fb[vout->output_fb_num[vout->cur_disp_output]];
+ u16 out_width;
+ u16 out_height;
+ mm_segment_t old_fs;
+ unsigned int ipu_ch = CHAN_NONE;
+ int rc = 0;
+
+ dev_dbg(dev, "mxc_v4l2out_streamon: field format=%d\n",
+ vout->field_fmt);
+
+ if (!vout)
+ return -EINVAL;
+
+ if (vout->state != STATE_STREAM_OFF)
+ return -EBUSY;
+
+ if (queue_size(&vout->ready_q) < 2) {
+ dev_err(dev, "2 buffers not been queued yet!\n");
+ return -EINVAL;
+ }
+
+ if ((vout->field_fmt == V4L2_FIELD_BOTTOM) || (vout->field_fmt == V4L2_FIELD_TOP)) {
+ dev_err(dev, "4 queued buffers need, not supported yet!\n");
+ return -EINVAL;
+ }
+
+ /*
+ * params init, check whether operation exceed the IC limitation:
+ * whether split mode used ( ipu version >= ipuv3 only)
+ */
+ g_irq_cnt = g_buf_output_cnt = g_buf_q_cnt = g_buf_dq_cnt = 0;
+ out_width = vout->crop_current.width;
+ out_height = vout->crop_current.height;
+ vout->next_done_ipu_buf = 0;
+ vout->next_rdy_ipu_buf = 1;
+ vout->pp_split = 0;
+ ipu_ic_out_max_height_size = 1024;
+#ifdef CONFIG_MXC_IPU_V1
+ if (cpu_is_mx35())
+ ipu_ic_out_max_width_size = 800;
+ else
+ ipu_ic_out_max_width_size = 720;
+#else
+ ipu_ic_out_max_width_size = 1024;
+#endif
+ if ((out_width > ipu_ic_out_max_width_size) ||
+ (out_height > ipu_ic_out_max_height_size))
+ vout->pp_split = 4;
+ if (!INTERLACED_CONTENT(vout)) {
+ vout->next_done_ipu_buf = vout->next_rdy_ipu_buf = 0;
+ vout->ipu_buf[0] = dequeue_buf(&vout->ready_q);
+ /* split IC by two stripes, the by pass is impossible*/
+ if ((out_width != vout->v2f.fmt.pix.width ||
+ out_height != vout->v2f.fmt.pix.height) &&
+ vout->pp_split) {
+ vout->ipu_buf[1] = vout->ipu_buf[0];
+ vout->frame_count = 1;
+ if ((out_width > ipu_ic_out_max_width_size) &&
+ (out_height > ipu_ic_out_max_height_size))
+ vout->pp_split = 1; /*4 stripes*/
+ else if (!(out_height > ipu_ic_out_max_height_size))
+ vout->pp_split = 2; /*two horizontal stripes */
+ else
+ vout->pp_split = 3; /*2 vertical stripes*/
+ } else {
+ vout->ipu_buf[1] = dequeue_buf(&vout->ready_q);
+ vout->frame_count = 2;
+ }
+ } else if (!LOAD_3FIELDS(vout)) {
+ vout->ipu_buf[0] = dequeue_buf(&vout->ready_q);
+ vout->ipu_buf[1] = -1;
+ vout->frame_count = 1;
+ } else {
+ vout->ipu_buf_p[0] = dequeue_buf(&vout->ready_q);
+ vout->ipu_buf[0] = dequeue_buf(&vout->ready_q);
+ vout->ipu_buf_n[0] = vout->ipu_buf[0];
+ vout->ipu_buf_p[1] = -1;
+ vout->ipu_buf[1] = -1;
+ vout->ipu_buf_n[1] = -1;
+ last_index_n = vout->ipu_buf_n[0];
+ vout->frame_count = 2;
+ }
+
+ /*
+ * Bypass IC if resizing and rotation are not needed
+ * Meanwhile, apply IC bypass to SDC only
+ */
+ fbvar = fbi->var;
+ vout->xres = fbvar.xres;
+ vout->yres = fbvar.yres;
+
+ if (vout->cur_disp_output == 3 || vout->cur_disp_output == 5) {
+ fbvar.bits_per_pixel = 16;
+ if (format_is_yuv(vout->v2f.fmt.pix.pixelformat))
+ fbvar.nonstd = IPU_PIX_FMT_UYVY;
+ else
+ fbvar.nonstd = 0;
+ if (vout->cur_disp_output == 3) {
+ fbvar.xres = out_width;
+ fbvar.yres = out_height;
+ vout->xres = fbvar.xres;
+ vout->yres = fbvar.yres;
+ }
+
+ fbvar.xres_virtual = fbvar.xres;
+ fbvar.yres_virtual = fbvar.yres * 2;
+ }
+
+ if (out_width == vout->v2f.fmt.pix.width &&
+ out_height == vout->v2f.fmt.pix.height &&
+ vout->xres == out_width &&
+ vout->yres == out_height &&
+ ipu_can_rotate_in_place(vout->rotate) &&
+ (vout->bytesperline ==
+ bytes_per_pixel(vout->v2f.fmt.pix.pixelformat) * out_width) &&
+ !INTERLACED_CONTENT(vout)) {
+ vout->ic_bypass = 1;
+ } else {
+ vout->ic_bypass = 0;
+ }
+
+#ifdef CONFIG_MXC_IPU_V1
+ /* IPUv1 needs IC to do CSC */
+ if (format_is_yuv(vout->v2f.fmt.pix.pixelformat) !=
+ format_is_yuv(bpp_to_fmt(fbi)))
+ vout->ic_bypass = 0;
+#endif
+
+ if (fbi->fbops->fb_ioctl) {
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+ fbi->fbops->fb_ioctl(fbi, MXCFB_GET_FB_IPU_CHAN,
+ (unsigned long)&ipu_ch);
+ set_fs(old_fs);
+ }
+
+ if (ipu_ch == CHAN_NONE) {
+ dev_err(dev, "Can not get display ipu channel\n");
+ return -EINVAL;
+ }
+
+ vout->display_ch = ipu_ch;
+
+ if (vout->ic_bypass) {
+ pr_debug("Bypassing IC\n");
+ vout->pp_split = 0;
+ switch (vout->v2f.fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
+ case V4L2_PIX_FMT_NV12:
+ fbvar.bits_per_pixel = 12;
+ break;
+ case V4L2_PIX_FMT_YUV422P:
+ fbvar.bits_per_pixel = 16;
+ break;
+ default:
+ fbvar.bits_per_pixel = 8*
+ bytes_per_pixel(vout->v2f.fmt.pix.pixelformat);
+ }
+ fbvar.nonstd = vout->v2f.fmt.pix.pixelformat;
+ }
+
+ /* Init display channel through fb API */
+ fbvar.activate |= FB_ACTIVATE_FORCE;
+ fb_set_var(fbi, &fbvar);
+
+ if (fbi->fbops->fb_ioctl && vout->display_ch == MEM_FG_SYNC) {
+ fb_pos.x = vout->crop_current.left;
+ fb_pos.y = vout->crop_current.top;
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+ fbi->fbops->fb_ioctl(fbi, MXCFB_SET_OVERLAY_POS,
+ (unsigned long)&fb_pos);
+ set_fs(old_fs);
+ }
+
+ vout->display_bufs[1] = fbi->fix.smem_start;
+ vout->display_bufs[0] = fbi->fix.smem_start +
+ (fbi->fix.line_length * vout->yres);
+ vout->display_buf_size = vout->xres *
+ vout->yres * fbi->var.bits_per_pixel / 8;
+
+ /* fill black color for init fb, we assume fb has double buffer*/
+ if (format_is_yuv(vout->v2f.fmt.pix.pixelformat)) {
+ int i;
+
+ if ((vout->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_UYVY) ||
+ (vout->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) ||
+ (!vout->ic_bypass)) {
+ short * tmp = (short *) fbi->screen_base;
+ short color;
+ if (vout->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV)
+ color = 0x8000;
+ else
+ color = 0x80;
+ for (i = 0; i < (fbi->fix.line_length * fbi->var.yres_virtual)/2;
+ i++, tmp++)
+ *tmp = color;
+ } else if ((vout->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) ||
+ (vout->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YVU420) ||
+ (vout->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_NV12)) {
+ char * base = (char *)fbi->screen_base;
+ int j, screen_size = fbi->var.xres * fbi->var.yres;
+
+ for (j = 0; j < 2; j++) {
+ memset(base, 0, screen_size);
+ base += screen_size;
+ for (i = 0; i < screen_size/2; i++, base++)
+ *base = 0x80;
+ }
+ } else if (vout->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV422P) {
+ char * base = (char *)fbi->screen_base;
+ int j, screen_size = fbi->var.xres * fbi->var.yres;
+
+ for (j = 0; j < 2; j++) {
+ memset(base, 0, screen_size);
+ base += screen_size;
+ for (i = 0; i < screen_size; i++, base++)
+ *base = 0x80;
+ }
+ }
+ } else
+ memset(fbi->screen_base, 0x0,
+ fbi->fix.line_length * fbi->var.yres_virtual);
+
+ if (INTERLACED_CONTENT(vout))
+ vout->post_proc_ch = MEM_VDI_PRP_VF_MEM;
+ else if (!vout->ic_bypass)
+ vout->post_proc_ch = MEM_PP_MEM;
+
+ /* Init IC channel */
+ if (!vout->ic_bypass) {
+ if (vout->rotate >= IPU_ROTATE_90_RIGHT) {
+ out_width = vout->crop_current.height;
+ out_height = vout->crop_current.width;
+ }
+ vout->display_input_ch = vout->post_proc_ch;
+ memset(&params, 0, sizeof(params));
+ if (INTERLACED_CONTENT(vout)) {
+ if (vout->pp_split) {
+ dev_err(&vout->video_dev->dev, "VDI split has not supported yet.\n");
+ return -1;
+ } else
+ rc = init_VDI(params, vout, dev, fbi, out_width, out_height);
+ } else {
+ rc = init_PP(&params, vout, dev, fbi, out_width, out_height);
+ }
+ if (rc < 0)
+ return rc;
+ }
+
+ if (!vout->ic_bypass) {
+ switch (vout->display_input_ch) {
+ case MEM_PP_MEM:
+ vout->work_irq = IPU_IRQ_PP_OUT_EOF;
+ break;
+ case MEM_VDI_PRP_VF_MEM:
+ vout->work_irq = IPU_IRQ_PRP_VF_OUT_EOF;
+ break;
+ case MEM_ROT_VF_MEM:
+ vout->work_irq = IPU_IRQ_PRP_VF_ROT_OUT_EOF;
+ break;
+ case MEM_ROT_PP_MEM:
+ vout->work_irq = IPU_IRQ_PP_ROT_OUT_EOF;
+ break;
+ default:
+ dev_err(&vout->video_dev->dev,
+ "not support channel, should not be here\n");
+ }
+ } else
+ vout->work_irq = -1;
+
+ if (!vout->ic_bypass && (vout->work_irq > 0)) {
+ ipu_clear_irq(vout->work_irq);
+ ipu_request_irq(vout->work_irq,
+ mxc_v4l2out_work_irq_handler,
+ 0, vout->video_dev->name, vout);
+ }
+
+ vout->state = STATE_STREAM_PAUSED;
+
+ /* Enable display and IC channels */
+ if (fbi) {
+ acquire_console_sem();
+ fb_blank(fbi, FB_BLANK_UNBLANK);
+ release_console_sem();
+ } else {
+ ipu_enable_channel(vout->display_ch);
+ }
+ if (!vout->ic_bypass) {
+#ifndef CONFIG_MXC_IPU_V1
+ ipu_enable_channel(vout->post_proc_ch);
+#endif
+ if (LOAD_3FIELDS(vout)) {
+ ipu_enable_channel(MEM_VDI_PRP_VF_MEM_P);
+ ipu_enable_channel(MEM_VDI_PRP_VF_MEM_N);
+ ipu_select_multi_vdi_buffer(0);
+ } else if (INTERLACED_CONTENT(vout)) {
+ ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER, 0);
+ } else {
+ ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER, 0);
+ if (!vout->pp_split)
+ ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER, 1);
+ }
+ ipu_select_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER, 0);
+ ipu_select_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER, 1);
+#ifdef CONFIG_MXC_IPU_V1
+ ipu_enable_channel(vout->post_proc_ch);
+#endif
+ } else {
+ ipu_update_channel_buffer(vout->display_ch,
+ IPU_INPUT_BUFFER,
+ 0, vout->v4l2_bufs[vout->ipu_buf[0]].m.offset);
+ ipu_update_channel_buffer(vout->display_ch,
+ IPU_INPUT_BUFFER,
+ 1, vout->v4l2_bufs[vout->ipu_buf[1]].m.offset);
+ if (vout->offset.u_offset || vout->offset.v_offset)
+ /* only update u/v offset */
+ ipu_update_channel_offset(vout->display_ch,
+ IPU_INPUT_BUFFER,
+ vout->v2f.fmt.pix.pixelformat,
+ vout->v2f.fmt.pix.width,
+ vout->v2f.fmt.pix.height,
+ vout->bytesperline,
+ vout->offset.u_offset,
+ vout->offset.v_offset,
+ 0,
+ 0);
+ ipu_select_buffer(vout->display_ch, IPU_INPUT_BUFFER, 0);
+ queue_work(vout->v4l_wq, &vout->timer_work);
+ }
+
+ vout->start_jiffies = jiffies;
+
+ msleep(1);
+
+ dev_dbg(dev,
+ "streamon: start time = %lu jiffies\n", vout->start_jiffies);
+
+ return 0;
+}
+
+/*!
+ * Shut down the voutera
+ *
+ * @param vout structure vout_data *
+ *
+ * @return status 0 Success
+ */
+static int mxc_v4l2out_streamoff(vout_data * vout)
+{
+ struct fb_info *fbi =
+ registered_fb[vout->output_fb_num[vout->cur_disp_output]];
+ int i, retval = 0;
+ unsigned long lockflag = 0;
+
+ if (!vout)
+ return -EINVAL;
+
+ if (vout->state == STATE_STREAM_OFF) {
+ return 0;
+ }
+
+ if (!vout->ic_bypass)
+ ipu_free_irq(vout->work_irq, vout);
+
+ if (vout->ic_bypass)
+ cancel_work_sync(&vout->timer_work);
+
+ spin_lock_irqsave(&g_lock, lockflag);
+
+ del_timer(&vout->output_timer);
+
+ if (vout->state == STATE_STREAM_ON) {
+ vout->state = STATE_STREAM_STOPPING;
+ }
+
+ spin_unlock_irqrestore(&g_lock, lockflag);
+
+ if (vout->display_ch == MEM_FG_SYNC) {
+ struct mxcfb_pos fb_pos;
+ mm_segment_t old_fs;
+
+ fb_pos.x = 0;
+ fb_pos.y = 0;
+ if (fbi->fbops->fb_ioctl) {
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+ fbi->fbops->fb_ioctl(fbi, MXCFB_SET_OVERLAY_POS,
+ (unsigned long)&fb_pos);
+ set_fs(old_fs);
+ }
+ }
+
+ if (vout->ic_bypass) {
+ fbi->var.activate |= FB_ACTIVATE_FORCE;
+ fb_set_var(fbi, &fbi->var);
+
+ if (vout->display_ch == MEM_FG_SYNC) {
+ acquire_console_sem();
+ fb_blank(fbi, FB_BLANK_POWERDOWN);
+ release_console_sem();
+ }
+
+ vout->display_bufs[0] = 0;
+ vout->display_bufs[1] = 0;
+ } else if (vout->post_proc_ch == MEM_PP_MEM ||
+ vout->post_proc_ch == MEM_PRP_VF_MEM) {
+ /* SDC with Rotation */
+ if (!ipu_can_rotate_in_place(vout->rotate)) {
+ ipu_unlink_channels(MEM_PP_MEM, MEM_ROT_PP_MEM);
+ ipu_disable_channel(MEM_ROT_PP_MEM, true);
+
+ if (vout->rot_pp_bufs[0]) {
+ mxc_free_buffers(vout->rot_pp_bufs,
+ vout->rot_pp_bufs_vaddr, 2,
+ vout->display_buf_size);
+ }
+ }
+ ipu_disable_channel(MEM_PP_MEM, true);
+
+ fbi->var.activate |= FB_ACTIVATE_FORCE;
+ fb_set_var(fbi, &fbi->var);
+
+ if (vout->display_ch == MEM_FG_SYNC) {
+ acquire_console_sem();
+ fb_blank(fbi, FB_BLANK_POWERDOWN);
+ release_console_sem();
+ }
+
+ vout->display_bufs[0] = 0;
+ vout->display_bufs[1] = 0;
+
+ ipu_uninit_channel(MEM_PP_MEM);
+ if (!ipu_can_rotate_in_place(vout->rotate))
+ ipu_uninit_channel(MEM_ROT_PP_MEM);
+ } else if (INTERLACED_CONTENT(vout) &&
+ (vout->post_proc_ch == MEM_VDI_PRP_VF_MEM)) {
+ if (!ipu_can_rotate_in_place(vout->rotate)) {
+ ipu_unlink_channels(MEM_VDI_PRP_VF_MEM,
+ MEM_ROT_VF_MEM);
+ ipu_disable_channel(MEM_ROT_VF_MEM, true);
+
+ if (vout->rot_pp_bufs[0]) {
+ mxc_free_buffers(vout->rot_pp_bufs,
+ vout->rot_pp_bufs_vaddr, 2,
+ vout->display_buf_size);
+ }
+ }
+
+ ipu_disable_channel(MEM_VDI_PRP_VF_MEM, true);
+ if (LOAD_3FIELDS(vout)) {
+ ipu_disable_channel(MEM_VDI_PRP_VF_MEM_P, true);
+ ipu_disable_channel(MEM_VDI_PRP_VF_MEM_N, true);
+ }
+
+ fbi->var.activate |= FB_ACTIVATE_FORCE;
+ fb_set_var(fbi, &fbi->var);
+
+ if (vout->display_ch == MEM_FG_SYNC) {
+ acquire_console_sem();
+ fb_blank(fbi, FB_BLANK_POWERDOWN);
+ release_console_sem();
+ }
+
+ vout->display_bufs[0] = 0;
+ vout->display_bufs[1] = 0;
+
+ ipu_uninit_channel(MEM_VDI_PRP_VF_MEM);
+ if (LOAD_3FIELDS(vout)) {
+ ipu_uninit_channel(MEM_VDI_PRP_VF_MEM_P);
+ ipu_uninit_channel(MEM_VDI_PRP_VF_MEM_N);
+ }
+ if (!ipu_can_rotate_in_place(vout->rotate))
+ ipu_uninit_channel(MEM_ROT_VF_MEM);
+ }
+
+ vout->ready_q.head = vout->ready_q.tail = 0;
+ vout->done_q.head = vout->done_q.tail = 0;
+ for (i = 0; i < vout->buffer_cnt; i++) {
+ vout->v4l2_bufs[i].flags = 0;
+ vout->v4l2_bufs[i].timestamp.tv_sec = 0;
+ vout->v4l2_bufs[i].timestamp.tv_usec = 0;
+ }
+
+ vout->post_proc_ch = CHAN_NONE;
+ vout->state = STATE_STREAM_OFF;
+
+ return retval;
+}
+
+/*
+ * Valid whether the palette is supported
+ *
+ * @param palette V4L2_PIX_FMT_RGB565, V4L2_PIX_FMT_BGR24 or V4L2_PIX_FMT_BGR32
+ *
+ * @return 1 if supported, 0 if failed
+ */
+static inline int valid_mode(u32 palette)
+{
+ return ((palette == V4L2_PIX_FMT_RGB565) ||
+ (palette == V4L2_PIX_FMT_BGR24) ||
+ (palette == V4L2_PIX_FMT_RGB24) ||
+ (palette == V4L2_PIX_FMT_BGR32) ||
+ (palette == V4L2_PIX_FMT_RGB32) ||
+ (palette == V4L2_PIX_FMT_NV12) ||
+ (palette == V4L2_PIX_FMT_UYVY) ||
+ (palette == V4L2_PIX_FMT_YUYV) ||
+ (palette == V4L2_PIX_FMT_YUV422P) ||
+ (palette == V4L2_PIX_FMT_YUV420));
+}
+
+/*
+ * V4L2 - Handles VIDIOC_G_FMT Ioctl
+ *
+ * @param vout structure vout_data *
+ *
+ * @param v4l2_format structure v4l2_format *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_v4l2out_g_fmt(vout_data * vout, struct v4l2_format *f)
+{
+ if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ return -EINVAL;
+ }
+ *f = vout->v2f;
+ return 0;
+}
+
+/*
+ * V4L2 - Handles VIDIOC_S_FMT Ioctl
+ *
+ * @param vout structure vout_data *
+ *
+ * @param v4l2_format structure v4l2_format *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_v4l2out_s_fmt(vout_data * vout, struct v4l2_format *f)
+{
+ int retval = 0;
+ u32 size = 0;
+ u32 bytesperline;
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ retval = -EINVAL;
+ goto err0;
+ }
+ if (!valid_mode(f->fmt.pix.pixelformat)) {
+ dev_err(&vout->video_dev->dev, "pixel format not supported\n");
+ retval = -EINVAL;
+ goto err0;
+ }
+
+ bytesperline = (f->fmt.pix.width * fmt_to_bpp(f->fmt.pix.pixelformat)) /
+ 8;
+ if (f->fmt.pix.bytesperline < bytesperline) {
+ f->fmt.pix.bytesperline = bytesperline;
+ } else {
+ bytesperline = f->fmt.pix.bytesperline;
+ }
+ vout->bytesperline = bytesperline;
+
+ /* Based on http://v4l2spec.bytesex.org/spec/x6386.htm#V4L2-FIELD */
+ vout->field_fmt = f->fmt.pix.field;
+ switch (vout->field_fmt) {
+ /* Images are in progressive format, not interlaced */
+ case V4L2_FIELD_NONE:
+ break;
+ /* The two fields of a frame are passed in separate buffers,
+ in temporal order, i. e. the older one first. */
+ case V4L2_FIELD_ALTERNATE:
+ dev_err(&vout->video_dev->dev,
+ "V4L2_FIELD_ALTERNATE field format not supported yet!\n");
+ break;
+ case V4L2_FIELD_INTERLACED_TB:
+ if (cpu_is_mx51() || cpu_is_mx53())
+ break;
+ dev_err(&vout->video_dev->dev,
+ "De-interlacing not supported in this device!\n");
+ vout->field_fmt = V4L2_FIELD_NONE;
+ case V4L2_FIELD_INTERLACED_BT:
+ dev_err(&vout->video_dev->dev,
+ "V4L2_FIELD_INTERLACED_BT field format not supported yet!\n");
+ default:
+ vout->field_fmt = V4L2_FIELD_NONE;
+ break;
+ }
+
+ switch (f->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_YUV422P:
+ /* byteperline for YUV planar formats is for
+ Y plane only */
+ size = bytesperline * f->fmt.pix.height * 2;
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_NV12:
+ size = (bytesperline * f->fmt.pix.height * 3) / 2;
+ break;
+ default:
+ size = bytesperline * f->fmt.pix.height;
+ break;
+ }
+
+ /* Return the actual size of the image to the app */
+ if (f->fmt.pix.sizeimage < size) {
+ f->fmt.pix.sizeimage = size;
+ } else {
+ size = f->fmt.pix.sizeimage;
+ }
+
+ vout->v2f.fmt.pix = f->fmt.pix;
+ if (vout->v2f.fmt.pix.priv != 0) {
+ if (copy_from_user(&vout->offset,
+ (void *)vout->v2f.fmt.pix.priv,
+ sizeof(vout->offset))) {
+ retval = -EFAULT;
+ goto err0;
+ }
+ } else {
+ vout->offset.u_offset = 0;
+ vout->offset.v_offset = 0;
+ }
+
+ retval = 0;
+ err0:
+ return retval;
+}
+
+/*
+ * V4L2 - Handles VIDIOC_G_CTRL Ioctl
+ *
+ * @param vout structure vout_data *
+ *
+ * @param c structure v4l2_control *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_get_v42lout_control(vout_data * vout, struct v4l2_control *c)
+{
+ switch (c->id) {
+ case V4L2_CID_HFLIP:
+ return (vout->rotate & IPU_ROTATE_HORIZ_FLIP) ? 1 : 0;
+ case V4L2_CID_VFLIP:
+ return (vout->rotate & IPU_ROTATE_VERT_FLIP) ? 1 : 0;
+ case (V4L2_CID_PRIVATE_BASE + 1):
+ return vout->rotate;
+ default:
+ return -EINVAL;
+ }
+}
+
+/*
+ * V4L2 - Handles VIDIOC_S_CTRL Ioctl
+ *
+ * @param vout structure vout_data *
+ *
+ * @param c structure v4l2_control *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_set_v42lout_control(vout_data * vout, struct v4l2_control *c)
+{
+ switch (c->id) {
+ case V4L2_CID_HFLIP:
+ vout->rotate |= c->value ? IPU_ROTATE_HORIZ_FLIP :
+ IPU_ROTATE_NONE;
+ break;
+ case V4L2_CID_VFLIP:
+ vout->rotate |= c->value ? IPU_ROTATE_VERT_FLIP :
+ IPU_ROTATE_NONE;
+ break;
+ case V4L2_CID_MXC_ROT:
+ vout->rotate = c->value;
+ break;
+ case V4L2_CID_MXC_MOTION:
+ vout->motion_sel = c->value;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*!
+ * V4L2 interface - open function
+ *
+ * @param file structure file *
+ *
+ * @return status 0 success, ENODEV invalid device instance,
+ * ENODEV timeout, ERESTARTSYS interrupted by user
+ */
+static int mxc_v4l2out_open(struct file *file)
+{
+ struct video_device *dev = video_devdata(file);
+ vout_data *vout = video_get_drvdata(dev);
+ int err;
+
+ if (!vout) {
+ return -ENODEV;
+ }
+
+ down(&vout->busy_lock);
+
+ err = -EINTR;
+ if (signal_pending(current))
+ goto oops;
+
+
+ if (vout->open_count++ == 0) {
+ init_waitqueue_head(&vout->v4l_bufq);
+
+ init_timer(&vout->output_timer);
+ vout->output_timer.function = mxc_v4l2out_timer_handler;
+ vout->output_timer.data = (unsigned long)vout;
+
+ vout->state = STATE_STREAM_OFF;
+ vout->rotate = IPU_ROTATE_NONE;
+
+ vout->v4l_wq = create_singlethread_workqueue("v4l2q");
+ if (!vout->v4l_wq) {
+ dev_dbg(&dev->dev,
+ "Could not create work queue\n");
+ err = -ENOMEM;
+ goto oops;
+ }
+
+ INIT_WORK(&vout->timer_work, timer_work_func);
+ }
+
+ file->private_data = dev;
+
+ up(&vout->busy_lock);
+
+ return 0;
+
+ oops:
+ up(&vout->busy_lock);
+ return err;
+}
+
+/*!
+ * V4L2 interface - close function
+ *
+ * @param file struct file *
+ *
+ * @return 0 success
+ */
+static int mxc_v4l2out_close(struct file *file)
+{
+ struct video_device *dev = video_devdata(file);
+ vout_data *vout = video_get_drvdata(dev);
+
+ if (--vout->open_count == 0) {
+ if (vout->state != STATE_STREAM_OFF)
+ mxc_v4l2out_streamoff(vout);
+
+ file->private_data = NULL;
+
+ mxc_free_buffers(vout->queue_buf_paddr, vout->queue_buf_vaddr,
+ vout->buffer_cnt, vout->queue_buf_size);
+ vout->buffer_cnt = 0;
+ mxc_free_buffers(vout->rot_pp_bufs, vout->rot_pp_bufs_vaddr, 2,
+ vout->display_buf_size);
+
+ /* capture off */
+ wake_up_interruptible(&vout->v4l_bufq);
+
+ flush_workqueue(vout->v4l_wq);
+ destroy_workqueue(vout->v4l_wq);
+ }
+
+ return 0;
+}
+
+/*!
+ * V4L2 interface - ioctl function
+ *
+ * @param file struct file *
+ *
+ * @param ioctlnr unsigned int
+ *
+ * @param arg void *
+ *
+ * @return 0 success, ENODEV for invalid device instance,
+ * -1 for other errors.
+ */
+static long
+mxc_v4l2out_do_ioctl(struct file *file,
+ unsigned int ioctlnr, void *arg)
+{
+ struct video_device *vdev = file->private_data;
+ vout_data *vout = video_get_drvdata(vdev);
+ int retval = 0;
+ int i = 0;
+
+ if (!vout)
+ return -EBADF;
+
+ /* make this _really_ smp-safe */
+ if (down_interruptible(&vout->busy_lock))
+ return -EBUSY;
+
+ switch (ioctlnr) {
+ case VIDIOC_QUERYCAP:
+ {
+ struct v4l2_capability *cap = arg;
+ strcpy(cap->driver, "mxc_v4l2_output");
+ cap->version = 0;
+ cap->capabilities =
+ V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
+ cap->card[0] = '\0';
+ cap->bus_info[0] = '\0';
+ retval = 0;
+ break;
+ }
+ case VIDIOC_G_FMT:
+ {
+ struct v4l2_format *gf = arg;
+ retval = mxc_v4l2out_g_fmt(vout, gf);
+ break;
+ }
+ case VIDIOC_S_FMT:
+ {
+ struct v4l2_format *sf = arg;
+ if (vout->state != STATE_STREAM_OFF) {
+ retval = -EBUSY;
+ break;
+ }
+ retval = mxc_v4l2out_s_fmt(vout, sf);
+ break;
+ }
+ case VIDIOC_REQBUFS:
+ {
+ struct v4l2_requestbuffers *req = arg;
+ if ((req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) ||
+ (req->memory != V4L2_MEMORY_MMAP)) {
+ dev_dbg(&vdev->dev,
+ "VIDIOC_REQBUFS: incorrect buffer type\n");
+ retval = -EINVAL;
+ break;
+ }
+
+ if (req->count == 0)
+ mxc_v4l2out_streamoff(vout);
+
+ if (vout->state == STATE_STREAM_OFF) {
+ if (vout->queue_buf_paddr[0] != 0) {
+ mxc_free_buffers(vout->queue_buf_paddr,
+ vout->queue_buf_vaddr,
+ vout->buffer_cnt,
+ vout->queue_buf_size);
+ dev_dbg(&vdev->dev,
+ "VIDIOC_REQBUFS: freed buffers\n");
+ }
+ vout->buffer_cnt = 0;
+ } else {
+ dev_dbg(&vdev->dev,
+ "VIDIOC_REQBUFS: Buffer is in use\n");
+ retval = -EBUSY;
+ break;
+ }
+
+ if (req->count == 0)
+ break;
+
+ if (req->count < MIN_FRAME_NUM) {
+ req->count = MIN_FRAME_NUM;
+ } else if (req->count > MAX_FRAME_NUM) {
+ req->count = MAX_FRAME_NUM;
+ }
+ vout->buffer_cnt = req->count;
+ vout->queue_buf_size =
+ PAGE_ALIGN(vout->v2f.fmt.pix.sizeimage);
+
+ retval = mxc_allocate_buffers(vout->queue_buf_paddr,
+ vout->queue_buf_vaddr,
+ vout->buffer_cnt,
+ vout->queue_buf_size);
+ if (retval < 0)
+ break;
+
+ /* Init buffer queues */
+ vout->done_q.head = 0;
+ vout->done_q.tail = 0;
+ vout->ready_q.head = 0;
+ vout->ready_q.tail = 0;
+
+ for (i = 0; i < vout->buffer_cnt; i++) {
+ memset(&(vout->v4l2_bufs[i]), 0,
+ sizeof(vout->v4l2_bufs[i]));
+ vout->v4l2_bufs[i].flags = 0;
+ vout->v4l2_bufs[i].memory = V4L2_MEMORY_MMAP;
+ vout->v4l2_bufs[i].index = i;
+ vout->v4l2_bufs[i].type =
+ V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ vout->v4l2_bufs[i].length =
+ PAGE_ALIGN(vout->v2f.fmt.pix.sizeimage);
+ vout->v4l2_bufs[i].m.offset =
+ (unsigned long)vout->queue_buf_paddr[i];
+ vout->v4l2_bufs[i].timestamp.tv_sec = 0;
+ vout->v4l2_bufs[i].timestamp.tv_usec = 0;
+ }
+ break;
+ }
+ case VIDIOC_QUERYBUF:
+ {
+ struct v4l2_buffer *buf = arg;
+ u32 type = buf->type;
+ int index = buf->index;
+
+ if ((type != V4L2_BUF_TYPE_VIDEO_OUTPUT) ||
+ (index >= vout->buffer_cnt)) {
+ dev_dbg(&vdev->dev,
+ "VIDIOC_QUERYBUFS: incorrect buffer type\n");
+ retval = -EINVAL;
+ break;
+ }
+ down(&vout->param_lock);
+ memcpy(buf, &(vout->v4l2_bufs[index]), sizeof(*buf));
+ up(&vout->param_lock);
+ break;
+ }
+ case VIDIOC_QBUF:
+ {
+ struct v4l2_buffer *buf = arg;
+ int index = buf->index;
+ unsigned long lock_flags;
+ int param[5][3];
+
+ if ((buf->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) ||
+ (index >= vout->buffer_cnt)) {
+ retval = -EINVAL;
+ break;
+ }
+
+ dev_dbg(&vdev->dev, "VIDIOC_QBUF: %d field = %d\n", buf->index, buf->field);
+
+ /* mmapped buffers are L1 WB cached,
+ * so we need to clean them */
+ if (buf->memory & V4L2_MEMORY_MMAP) {
+ flush_cache_all();
+ }
+
+ spin_lock_irqsave(&g_lock, lock_flags);
+
+ memcpy(&(vout->v4l2_bufs[index]), buf, sizeof(*buf));
+ vout->v4l2_bufs[index].flags |= V4L2_BUF_FLAG_QUEUED;
+
+ g_buf_q_cnt++;
+ if (vout->v4l2_bufs[index].reserved)
+ if (!copy_from_user(&param[0][0],
+ (void *)vout->
+ v4l2_bufs[index]
+ .reserved, sizeof(param)))
+ ipu_set_csc_coefficients(vout->
+ display_ch,
+ param);
+ queue_buf(&vout->ready_q, index);
+ if (vout->state == STATE_STREAM_PAUSED) {
+ index = peek_next_buf(&vout->ready_q);
+ setup_next_buf_timer(vout, index);
+ vout->state = STATE_STREAM_ON;
+ }
+
+ spin_unlock_irqrestore(&g_lock, lock_flags);
+ break;
+ }
+ case VIDIOC_DQBUF:
+ {
+ struct v4l2_buffer *buf = arg;
+ int idx;
+
+ if ((queue_size(&vout->done_q) == 0) &&
+ (file->f_flags & O_NONBLOCK)) {
+ retval = -EAGAIN;
+ break;
+ }
+
+ if (!wait_event_interruptible_timeout(vout->v4l_bufq,
+ queue_size(&vout->
+ done_q)
+ != 0, 10 * HZ)) {
+ dev_dbg(&vdev->dev, "VIDIOC_DQBUF: timeout\n");
+ retval = -ETIME;
+ break;
+ } else if (signal_pending(current)) {
+ dev_dbg(&vdev->dev,
+ "VIDIOC_DQBUF: interrupt received\n");
+ retval = -ERESTARTSYS;
+ break;
+ }
+ idx = dequeue_buf(&vout->done_q);
+ if (idx == -1) { /* No frame free */
+ dev_dbg(&vdev->dev,
+ "VIDIOC_DQBUF: no free buffers, returning\n");
+ retval = -EAGAIN;
+ break;
+ }
+ if ((vout->v4l2_bufs[idx].flags & V4L2_BUF_FLAG_DONE) ==
+ 0)
+ dev_dbg(&vdev->dev,
+ "VIDIOC_DQBUF: buffer in done q, but not "
+ "flagged as done\n");
+
+ vout->v4l2_bufs[idx].flags = 0;
+ memcpy(buf, &(vout->v4l2_bufs[idx]), sizeof(*buf));
+ dev_dbg(&vdev->dev, "VIDIOC_DQBUF: %d\n", buf->index);
+ break;
+ }
+ case VIDIOC_STREAMON:
+ {
+ retval = mxc_v4l2out_streamon(vout);
+ break;
+ }
+ case VIDIOC_STREAMOFF:
+ {
+ retval = mxc_v4l2out_streamoff(vout);
+ break;
+ }
+ case VIDIOC_G_CTRL:
+ {
+ retval = mxc_get_v42lout_control(vout, arg);
+ break;
+ }
+ case VIDIOC_S_CTRL:
+ {
+ retval = mxc_set_v42lout_control(vout, arg);
+ break;
+ }
+ case VIDIOC_CROPCAP:
+ {
+ struct v4l2_cropcap *cap = arg;
+
+ if (cap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ retval = -EINVAL;
+ break;
+ }
+
+ cap->bounds = vout->crop_bounds[vout->cur_disp_output];
+ cap->defrect = vout->crop_bounds[vout->cur_disp_output];
+ retval = 0;
+ break;
+ }
+ case VIDIOC_G_CROP:
+ {
+ struct v4l2_crop *crop = arg;
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ retval = -EINVAL;
+ break;
+ }
+ crop->c = vout->crop_current;
+ break;
+ }
+ case VIDIOC_S_CROP:
+ {
+ struct v4l2_crop *crop = arg;
+ struct v4l2_rect *b =
+ &(vout->crop_bounds[vout->cur_disp_output]);
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ retval = -EINVAL;
+ break;
+ }
+ if (crop->c.height < 0) {
+ retval = -EINVAL;
+ break;
+ }
+ if (crop->c.width < 0) {
+ retval = -EINVAL;
+ break;
+ }
+
+ /* only full screen supported for SDC BG and SDC DC */
+ if (vout->cur_disp_output == 4) {
+ crop->c = vout->crop_current;
+ break;
+ }
+
+ if (crop->c.top < b->top)
+ crop->c.top = b->top;
+ if (crop->c.top >= b->top + b->height)
+ crop->c.top = b->top + b->height - 1;
+ if (crop->c.height > b->top - crop->c.top + b->height)
+ crop->c.height =
+ b->top - crop->c.top + b->height;
+
+ if (crop->c.left < b->left)
+ crop->c.left = b->left;
+ if (crop->c.left >= b->left + b->width)
+ crop->c.left = b->left + b->width - 1;
+ if (crop->c.width > b->left - crop->c.left + b->width)
+ crop->c.width =
+ b->left - crop->c.left + b->width;
+
+ /* stride line limitation */
+ crop->c.height -= crop->c.height % 8;
+ crop->c.width -= crop->c.width % 8;
+
+ vout->crop_current = crop->c;
+ break;
+ }
+ case VIDIOC_ENUMOUTPUT:
+ {
+ struct v4l2_output *output = arg;
+
+ if ((output->index >= 5) ||
+ (vout->output_enabled[output->index] == false)) {
+ retval = -EINVAL;
+ break;
+ }
+
+ if (output->index >= 3)
+ *output = mxc_outputs[MXC_V4L2_OUT_2_SDC];
+ break;
+ }
+ case VIDIOC_G_OUTPUT:
+ {
+ int *p_output_num = arg;
+
+ *p_output_num = vout->cur_disp_output;
+ break;
+ }
+ case VIDIOC_S_OUTPUT:
+ {
+ int *p_output_num = arg;
+ int fbnum;
+ struct v4l2_rect *b;
+
+ if ((*p_output_num >= MXC_V4L2_OUT_NUM_OUTPUTS) ||
+ (vout->output_enabled[*p_output_num] == false)) {
+ retval = -EINVAL;
+ break;
+ }
+
+ if (vout->state != STATE_STREAM_OFF) {
+ retval = -EBUSY;
+ break;
+ }
+
+ vout->cur_disp_output = *p_output_num;
+
+ /* Update bounds in case they have changed */
+ b = &vout->crop_bounds[vout->cur_disp_output];
+
+ fbnum = vout->output_fb_num[vout->cur_disp_output];
+
+ /*
+ * For FG overlay, it uses BG window parameter as
+ * limitation reference; and BG must be enabled to
+ * support FG.
+ */
+ if (vout->cur_disp_output == 3) {
+ unsigned int i, ipu_ch = CHAN_NONE;
+ struct fb_info *fbi;
+ mm_segment_t old_fs;
+
+ for (i = 0; i < num_registered_fb; i++) {
+ fbi = registered_fb[i];
+ if (fbi->fbops->fb_ioctl) {
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+ fbi->fbops->fb_ioctl(fbi,
+ MXCFB_GET_FB_IPU_CHAN,
+ (unsigned long)&ipu_ch);
+ set_fs(old_fs);
+ }
+ if (ipu_ch == CHAN_NONE) {
+ dev_err(&vdev->dev,
+ "Can't get disp ipu channel\n");
+ retval = -EINVAL;
+ break;
+ }
+
+ if (ipu_ch == MEM_BG_SYNC) {
+ fbnum = i;
+ break;
+ }
+ }
+ }
+
+ b->width = registered_fb[fbnum]->var.xres;
+ b->height = registered_fb[fbnum]->var.yres;
+
+ vout->crop_current = *b;
+ break;
+ }
+ case VIDIOC_ENUM_FMT:
+ case VIDIOC_TRY_FMT:
+ case VIDIOC_QUERYCTRL:
+ case VIDIOC_G_PARM:
+ case VIDIOC_ENUMSTD:
+ case VIDIOC_G_STD:
+ case VIDIOC_S_STD:
+ case VIDIOC_G_TUNER:
+ case VIDIOC_S_TUNER:
+ case VIDIOC_G_FREQUENCY:
+ case VIDIOC_S_FREQUENCY:
+ default:
+ retval = -EINVAL;
+ break;
+ }
+
+ up(&vout->busy_lock);
+ return retval;
+}
+
+/*
+ * V4L2 interface - ioctl function
+ *
+ * @return None
+ */
+static long
+mxc_v4l2out_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(file, cmd, arg, mxc_v4l2out_do_ioctl);
+}
+
+/*!
+ * V4L2 interface - mmap function
+ *
+ * @param file structure file *
+ *
+ * @param vma structure vm_area_struct *
+ *
+ * @return status 0 Success, EINTR busy lock error,
+ * ENOBUFS remap_page error
+ */
+static int mxc_v4l2out_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct video_device *vdev = video_devdata(file);
+ unsigned long size = vma->vm_end - vma->vm_start;
+ int res = 0;
+ int i;
+ vout_data *vout = video_get_drvdata(vdev);
+
+ dev_dbg(&vdev->dev, "pgoff=0x%lx, start=0x%lx, end=0x%lx\n",
+ vma->vm_pgoff, vma->vm_start, vma->vm_end);
+
+ /* make this _really_ smp-safe */
+ if (down_interruptible(&vout->busy_lock))
+ return -EINTR;
+
+ for (i = 0; i < vout->buffer_cnt; i++) {
+ if ((vout->v4l2_bufs[i].m.offset ==
+ (vma->vm_pgoff << PAGE_SHIFT)) &&
+ (vout->v4l2_bufs[i].length >= size)) {
+ vout->v4l2_bufs[i].flags |= V4L2_BUF_FLAG_MAPPED;
+ break;
+ }
+ }
+ if (i == vout->buffer_cnt) {
+ res = -ENOBUFS;
+ goto mxc_mmap_exit;
+ }
+
+ /* make buffers inner write-back, outer write-thru cacheable */
+ /* vma->vm_page_prot = pgprot_outer_wrthru(vma->vm_page_prot);*/
+
+ if (remap_pfn_range(vma, vma->vm_start,
+ vma->vm_pgoff, size, vma->vm_page_prot)) {
+ dev_dbg(&vdev->dev, "mmap remap_pfn_range failed\n");
+ res = -ENOBUFS;
+ goto mxc_mmap_exit;
+ }
+
+ vma->vm_flags &= ~VM_IO; /* using shared anonymous pages */
+
+ mxc_mmap_exit:
+ up(&vout->busy_lock);
+ return res;
+}
+
+/*!
+ * V4L2 interface - poll function
+ *
+ * @param file structure file *
+ *
+ * @param wait structure poll_table_struct *
+ *
+ * @return status POLLIN | POLLRDNORM
+ */
+static unsigned int mxc_v4l2out_poll(struct file *file, struct poll_table_struct * wait)
+{
+ struct video_device *dev = video_devdata(file);
+ vout_data *vout = video_get_drvdata(dev);
+
+ wait_queue_head_t *queue = NULL;
+ int res = POLLIN | POLLRDNORM;
+
+ if (down_interruptible(&vout->busy_lock))
+ return -EINTR;
+
+ queue = &vout->v4l_bufq;
+ poll_wait(file, queue, wait);
+
+ up(&vout->busy_lock);
+ return res;
+}
+
+static struct
+v4l2_file_operations mxc_v4l2out_fops = {
+ .owner = THIS_MODULE,
+ .open = mxc_v4l2out_open,
+ .release = mxc_v4l2out_close,
+ .ioctl = mxc_v4l2out_ioctl,
+ .mmap = mxc_v4l2out_mmap,
+ .poll = mxc_v4l2out_poll,
+};
+
+static struct video_device mxc_v4l2out_template = {
+ .name = "MXC Video Output",
+ .vfl_type = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING,
+ .fops = &mxc_v4l2out_fops,
+ .release = video_device_release,
+};
+
+/*!
+ * Probe routine for the framebuffer driver. It is called during the
+ * driver binding process. The following functions are performed in
+ * this routine: Framebuffer initialization, Memory allocation and
+ * mapping, Framebuffer registration, IPU initialization.
+ *
+ * @return Appropriate error code to the kernel common code
+ */
+static int mxc_v4l2out_probe(struct platform_device *pdev)
+{
+ int i;
+ vout_data *vout;
+
+ /*
+ * Allocate sufficient memory for the fb structure
+ */
+ vout = kmalloc(sizeof(vout_data), GFP_KERNEL);
+
+ if (!vout)
+ return 0;
+
+ memset(vout, 0, sizeof(vout_data));
+
+ vout->video_dev = video_device_alloc();
+ if (vout->video_dev == NULL)
+ return -1;
+ vout->video_dev->minor = -1;
+
+ *(vout->video_dev) = mxc_v4l2out_template;
+
+ /* register v4l device */
+ if (video_register_device(vout->video_dev,
+ VFL_TYPE_GRABBER, video_nr) == -1) {
+ dev_dbg(&pdev->dev, "video_register_device failed\n");
+ return 0;
+ }
+ dev_info(&pdev->dev, "Registered device video%d\n",
+ vout->video_dev->minor & 0x1f);
+ /*vout->video_dev->dev = &pdev->dev;*/
+
+ video_set_drvdata(vout->video_dev, vout);
+
+ init_MUTEX(&vout->param_lock);
+ init_MUTEX(&vout->busy_lock);
+
+ /* setup outputs and cropping */
+ vout->cur_disp_output = -1;
+ for (i = 0; i < num_registered_fb; i++) {
+ char *idstr = registered_fb[i]->fix.id;
+ if (strncmp(idstr, "DISP", 4) == 0) {
+ int disp_num = idstr[4] - '0';
+ if (disp_num == 3) {
+ if (strcmp(idstr, "DISP3 BG - DI1") == 0)
+ disp_num = 5;
+ else if (strncmp(idstr, "DISP3 BG", 8) == 0)
+ disp_num = 4;
+ }
+ vout->crop_bounds[disp_num].left = 0;
+ vout->crop_bounds[disp_num].top = 0;
+ vout->crop_bounds[disp_num].width =
+ registered_fb[i]->var.xres;
+ vout->crop_bounds[disp_num].height =
+ registered_fb[i]->var.yres;
+ vout->output_enabled[disp_num] = true;
+ vout->output_fb_num[disp_num] = i;
+ if (vout->cur_disp_output == -1) {
+ vout->cur_disp_output = disp_num;
+ }
+ }
+
+ }
+ vout->crop_current = vout->crop_bounds[vout->cur_disp_output];
+
+ platform_set_drvdata(pdev, vout);
+
+ return 0;
+}
+
+static int mxc_v4l2out_remove(struct platform_device *pdev)
+{
+ vout_data *vout = platform_get_drvdata(pdev);
+
+ if (vout->video_dev) {
+ video_unregister_device(vout->video_dev);
+ vout->video_dev = NULL;
+ }
+
+ platform_set_drvdata(pdev, NULL);
+
+ kfree(vout);
+
+ return 0;
+}
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxc_v4l2out_driver = {
+ .driver = {
+ .name = "mxc_v4l2_output",
+ },
+ .probe = mxc_v4l2out_probe,
+ .remove = mxc_v4l2out_remove,
+};
+
+/*!
+ * mxc v4l2 init function
+ *
+ */
+static int mxc_v4l2out_init(void)
+{
+ return platform_driver_register(&mxc_v4l2out_driver);
+}
+
+/*!
+ * mxc v4l2 cleanup function
+ *
+ */
+static void mxc_v4l2out_clean(void)
+{
+ platform_driver_unregister(&mxc_v4l2out_driver);
+}
+
+module_init(mxc_v4l2out_init);
+module_exit(mxc_v4l2out_clean);
+
+module_param(video_nr, int, 0444);
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("V4L2-driver for MXC video output");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("video");
diff --git a/drivers/media/video/mxc/output/mxc_v4l2_output.h b/drivers/media/video/mxc/output/mxc_v4l2_output.h
new file mode 100644
index 000000000000..096dc3b17a06
--- /dev/null
+++ b/drivers/media/video/mxc/output/mxc_v4l2_output.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2005-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @defgroup MXC_V4L2_OUTPUT MXC V4L2 Video Output Driver
+ */
+/*!
+ * @file mxc_v4l2_output.h
+ *
+ * @brief MXC V4L2 Video Output Driver Header file
+ *
+ * Video4Linux2 Output Device using MXC IPU Post-processing functionality.
+ *
+ * @ingroup MXC_V4L2_OUTPUT
+ */
+#ifndef __MXC_V4L2_OUTPUT_H__
+#define __MXC_V4L2_OUTPUT_H__
+
+#include <media/v4l2-dev.h>
+
+#ifdef __KERNEL__
+
+#include <linux/ipu.h>
+#include <linux/mxc_v4l2.h>
+#include <linux/videodev2.h>
+
+#define MIN_FRAME_NUM 2
+#define MAX_FRAME_NUM 30
+
+#define MXC_V4L2_OUT_NUM_OUTPUTS 6
+#define MXC_V4L2_OUT_2_SDC 0
+
+
+typedef struct {
+ int list[MAX_FRAME_NUM + 1];
+ int head;
+ int tail;
+} v4l_queue;
+
+/*!
+ * States for the video stream
+ */
+typedef enum {
+ STATE_STREAM_OFF,
+ STATE_STREAM_ON,
+ STATE_STREAM_PAUSED,
+ STATE_STREAM_STOPPING,
+} v4lout_state;
+
+/*!
+ * common v4l2 driver structure.
+ */
+typedef struct _vout_data {
+ struct video_device *video_dev;
+ /*!
+ * semaphore guard against SMP multithreading
+ */
+ struct semaphore busy_lock;
+
+ /*!
+ * number of process that have device open
+ */
+ int open_count;
+
+ /*!
+ * params lock for this camera
+ */
+ struct semaphore param_lock;
+
+ struct timer_list output_timer;
+ struct workqueue_struct *v4l_wq;
+ struct work_struct timer_work;
+ unsigned long start_jiffies;
+ u32 frame_count;
+
+ v4l_queue ready_q;
+ v4l_queue done_q;
+
+ s8 next_rdy_ipu_buf;
+ s8 next_done_ipu_buf;
+ s8 ipu_buf[2];
+ s8 ipu_buf_p[2];
+ s8 ipu_buf_n[2];
+ volatile v4lout_state state;
+
+ int cur_disp_output;
+ int output_fb_num[MXC_V4L2_OUT_NUM_OUTPUTS];
+ int output_enabled[MXC_V4L2_OUT_NUM_OUTPUTS];
+ struct v4l2_framebuffer v4l2_fb;
+ int ic_bypass;
+ u32 work_irq;
+ ipu_channel_t display_ch;
+ ipu_channel_t post_proc_ch;
+ ipu_channel_t display_input_ch;
+
+ /*!
+ * FRAME_NUM-buffering, so we need a array
+ */
+ int buffer_cnt;
+ dma_addr_t queue_buf_paddr[MAX_FRAME_NUM];
+ void *queue_buf_vaddr[MAX_FRAME_NUM];
+ u32 queue_buf_size;
+ struct v4l2_buffer v4l2_bufs[MAX_FRAME_NUM];
+ u32 display_buf_size;
+ dma_addr_t display_bufs[2];
+ void *display_bufs_vaddr[2];
+ dma_addr_t rot_pp_bufs[2];
+ void *rot_pp_bufs_vaddr[2];
+
+ /*!
+ * Poll wait queue
+ */
+ wait_queue_head_t v4l_bufq;
+
+ /*!
+ * v4l2 format
+ */
+ struct v4l2_format v2f;
+ struct v4l2_mxc_offset offset;
+ ipu_rotate_mode_t rotate;
+
+ /* crop */
+ struct v4l2_rect crop_bounds[MXC_V4L2_OUT_NUM_OUTPUTS];
+ struct v4l2_rect crop_current;
+ u32 bytesperline;
+ enum v4l2_field field_fmt;
+ ipu_motion_sel motion_sel;
+
+ /* PP split fot two stripes*/
+ int pp_split; /* 0,1 */
+ struct stripe_param pp_left_stripe;
+ struct stripe_param pp_right_stripe; /* struct for split parameters */
+ struct stripe_param pp_up_stripe;
+ struct stripe_param pp_down_stripe;
+ /* IC ouput buffer number. Counting from 0 to 7 */
+ int pp_split_buf_num; /* 0..7 */
+ u16 bpp ; /* bit per pixel */
+ u16 xres; /* width of physical frame (BGs) */
+ u16 yres; /* heigth of physical frame (BGs)*/
+
+} vout_data;
+
+#endif
+#endif /* __MXC_V4L2_OUTPUT_H__ */
diff --git a/drivers/media/video/mxs_pxp.c b/drivers/media/video/mxs_pxp.c
new file mode 100644
index 000000000000..017d22458a22
--- /dev/null
+++ b/drivers/media/video/mxs_pxp.c
@@ -0,0 +1,1413 @@
+/*
+ * Freescale MXS PxP driver
+ *
+ * Author: Matt Porter <mporter@embeddedalley.com>
+ *
+ * Copyright 2008-2010 Freescale Semiconductor, Inc.
+ * Copyright 2008-2009 Embedded Alley Solutions, Inc All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/vmalloc.h>
+#include <linux/videodev2.h>
+#include <linux/delay.h>
+
+#include <media/videobuf-dma-contig.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+
+#include <mach/hardware.h>
+#include <mach/regs-pxp.h>
+
+#include "mxs_pxp.h"
+
+#define PXP_BASE_ADDR IO_ADDRESS(PXP_PHYS_ADDR)
+
+#define PXP_DRIVER_NAME "mxs-pxp"
+#define PXP_DRIVER_MAJOR 1
+#define PXP_DRIVER_MINOR 0
+
+#define PXP_DEF_BUFS 2
+#define PXP_MIN_PIX 8
+
+#define V4L2_OUTPUT_TYPE_INTERNAL 4
+
+#define PXP_WAITCON ((__raw_readl(PXP_BASE_ADDR + HW_PXP_NEXT) & \
+ BM_PXP_NEXT_ENABLED) != BM_PXP_NEXT_ENABLED)
+
+#define REG_OFFSET 0x10
+#define REGS1_NUMS 16
+#define REGS2_NUMS 5
+#define REGS3_NUMS 32
+static u32 regs1[REGS1_NUMS];
+static u32 regs2[REGS2_NUMS];
+static u32 regs3[REGS3_NUMS];
+
+static struct pxp_data_format pxp_s0_formats[] = {
+ {
+ .name = "24-bit RGB",
+ .bpp = 4,
+ .fourcc = V4L2_PIX_FMT_RGB24,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .ctrl_s0_fmt = BV_PXP_CTRL_S0_FORMAT__RGB888,
+ },
+ {
+ .name = "16-bit RGB 5:6:5",
+ .bpp = 2,
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .ctrl_s0_fmt = BV_PXP_CTRL_S0_FORMAT__RGB565,
+ },
+ {
+ .name = "16-bit RGB 5:5:5",
+ .bpp = 2,
+ .fourcc = V4L2_PIX_FMT_RGB555,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .ctrl_s0_fmt = BV_PXP_CTRL_S0_FORMAT__RGB555,
+ },
+ {
+ .name = "YUV 4:2:0 Planar",
+ .bpp = 2,
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .ctrl_s0_fmt = BV_PXP_CTRL_S0_FORMAT__YUV420,
+ },
+ {
+ .name = "YUV 4:2:2 Planar",
+ .bpp = 2,
+ .fourcc = V4L2_PIX_FMT_YUV422P,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .ctrl_s0_fmt = BV_PXP_CTRL_S0_FORMAT__YUV422,
+ },
+};
+
+struct v4l2_queryctrl pxp_controls[] = {
+ {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Horizontal Flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Vertical Flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_PRIVATE_BASE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Rotation",
+ .minimum = 0,
+ .maximum = 270,
+ .step = 90,
+ .default_value = 0,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_PRIVATE_BASE + 1,
+ .name = "Background Color",
+ .minimum = 0,
+ .maximum = 0xFFFFFF,
+ .step = 1,
+ .default_value = 0,
+ .flags = 0,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ {
+ .id = V4L2_CID_PRIVATE_BASE + 2,
+ .name = "Set S0 Chromakey",
+ .minimum = -1,
+ .maximum = 0xFFFFFF,
+ .step = 1,
+ .default_value = -1,
+ .flags = 0,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ {
+ .id = V4L2_CID_PRIVATE_BASE + 3,
+ .name = "YUV Colorspace",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ .flags = 0,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },
+};
+
+static void pxp_set_ctrl(struct pxps *pxp)
+{
+ u32 ctrl;
+
+ ctrl = BF_PXP_CTRL_S0_FORMAT(pxp->s0_fmt->ctrl_s0_fmt);
+ ctrl |= BF_PXP_CTRL_OUTBUF_FORMAT(BV_PXP_CTRL_OUTBUF_FORMAT__RGB888);
+ ctrl |= BM_PXP_CTRL_CROP;
+
+ if (pxp->scaling)
+ ctrl |= BM_PXP_CTRL_SCALE;
+ if (pxp->vflip)
+ ctrl |= BM_PXP_CTRL_VFLIP;
+ if (pxp->hflip)
+ ctrl |= BM_PXP_CTRL_HFLIP;
+ if (pxp->rotate)
+ ctrl |= BF_PXP_CTRL_ROTATE(pxp->rotate / 90);
+
+ ctrl |= BM_PXP_CTRL_IRQ_ENABLE;
+ if (pxp->active)
+ ctrl |= BM_PXP_CTRL_ENABLE;
+
+ __raw_writel(ctrl, PXP_BASE_ADDR + HW_PXP_CTRL);
+ pxp->regs_virt->ctrl = ctrl;
+}
+
+static void pxp_set_outbuf(struct pxps *pxp)
+{
+ pxp->regs_virt->outbuf = pxp->outb_phys;
+ /* Always equal to the FB size */
+ if (pxp->rotate % 180)
+ pxp->regs_virt->outsize =
+ BF_PXP_OUTSIZE_WIDTH(pxp->fb.fmt.height) |
+ BF_PXP_OUTSIZE_HEIGHT(pxp->fb.fmt.width);
+ else
+ pxp->regs_virt->outsize =
+ BF_PXP_OUTSIZE_WIDTH(pxp->fb.fmt.width) |
+ BF_PXP_OUTSIZE_HEIGHT(pxp->fb.fmt.height);
+}
+
+static void pxp_set_s0colorkey(struct pxps *pxp)
+{
+ /* Low and high are set equal. V4L does not allow a chromakey range */
+ if (pxp->s0_chromakey == -1) {
+ /* disable color key */
+ pxp->regs_virt->s0colorkeylow = 0xFFFFFF;
+ pxp->regs_virt->s0colorkeyhigh = 0;
+ } else {
+ pxp->regs_virt->s0colorkeylow = pxp->s0_chromakey;
+ pxp->regs_virt->s0colorkeyhigh = pxp->s0_chromakey;
+ }
+}
+
+static void pxp_set_s1colorkey(struct pxps *pxp)
+{
+ /* Low and high are set equal. V4L does not allow a chromakey range */
+ if (pxp->s1_chromakey_state != 0 && pxp->s1_chromakey != -1) {
+ pxp->regs_virt->olcolorkeylow = pxp->s1_chromakey;
+ pxp->regs_virt->olcolorkeyhigh = pxp->s1_chromakey;
+ } else {
+ /* disable color key */
+ pxp->regs_virt->olcolorkeylow = 0xFFFFFF;
+ pxp->regs_virt->olcolorkeyhigh = 0;
+ }
+}
+
+static void pxp_set_oln(struct pxps *pxp)
+{
+ pxp->regs_virt->ol0.ol = (u32) pxp->fb.base;
+ if (pxp->rotate % 180)
+ pxp->regs_virt->ol0.olsize =
+ BF_PXP_OLnSIZE_WIDTH(pxp->fb.fmt.height >> 3) |
+ BF_PXP_OLnSIZE_HEIGHT(pxp->fb.fmt.width >> 3);
+ else
+ pxp->regs_virt->ol0.olsize =
+ BF_PXP_OLnSIZE_WIDTH(pxp->fb.fmt.width >> 3) |
+ BF_PXP_OLnSIZE_HEIGHT(pxp->fb.fmt.height >> 3);
+}
+
+static void pxp_set_olparam(struct pxps *pxp)
+{
+ u32 olparam;
+ struct v4l2_pix_format *fmt = &pxp->fb.fmt;
+
+ olparam = BF_PXP_OLnPARAM_ALPHA(pxp->global_alpha);
+ if (fmt->pixelformat == V4L2_PIX_FMT_RGB24)
+ olparam |=
+ BF_PXP_OLnPARAM_FORMAT(BV_PXP_OLnPARAM_FORMAT__RGB888);
+ else
+ olparam |=
+ BF_PXP_OLnPARAM_FORMAT(BV_PXP_OLnPARAM_FORMAT__RGB565);
+ if (pxp->global_alpha_state)
+ olparam |=
+ BF_PXP_OLnPARAM_ALPHA_CNTL
+ (BV_PXP_OLnPARAM_ALPHA_CNTL__Override);
+ if (pxp->s1_chromakey_state)
+ olparam |= BM_PXP_OLnPARAM_ENABLE_COLORKEY;
+ if (pxp->overlay_state)
+ olparam |= BM_PXP_OLnPARAM_ENABLE;
+
+ pxp->regs_virt->ol0.olparam = olparam;
+}
+
+static void pxp_set_s0param(struct pxps *pxp)
+{
+ u32 s0param;
+
+ s0param = BF_PXP_S0PARAM_XBASE(pxp->drect.left >> 3);
+ s0param |= BF_PXP_S0PARAM_YBASE(pxp->drect.top >> 3);
+ s0param |= BF_PXP_S0PARAM_WIDTH(pxp->s0_width >> 3);
+ s0param |= BF_PXP_S0PARAM_HEIGHT(pxp->s0_height >> 3);
+ pxp->regs_virt->s0param = s0param;
+}
+
+static void pxp_set_s0crop(struct pxps *pxp)
+{
+ u32 s0crop;
+
+ s0crop = BF_PXP_S0CROP_XBASE(pxp->srect.left >> 3);
+ s0crop |= BF_PXP_S0CROP_YBASE(pxp->srect.top >> 3);
+ s0crop |= BF_PXP_S0CROP_WIDTH(pxp->drect.width >> 3);
+ s0crop |= BF_PXP_S0CROP_HEIGHT(pxp->drect.height >> 3);
+ pxp->regs_virt->s0crop = s0crop;
+}
+
+static int pxp_set_scaling(struct pxps *pxp)
+{
+ int ret = 0;
+ u32 xscale, yscale, s0scale;
+
+ if ((pxp->s0_fmt->fourcc != V4L2_PIX_FMT_YUV420) &&
+ (pxp->s0_fmt->fourcc != V4L2_PIX_FMT_YUV422P)) {
+ pxp->scaling = 0;
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if ((pxp->srect.width == pxp->drect.width) &&
+ (pxp->srect.height == pxp->drect.height)) {
+ pxp->regs_virt->s0scale = 0x10001000;
+ pxp->scaling = 0;
+ goto out;
+ }
+
+ pxp->scaling = 1;
+ xscale = pxp->srect.width * 0x1000 / pxp->drect.width;
+ yscale = pxp->srect.height * 0x1000 / pxp->drect.height;
+ if (xscale > PXP_DOWNSCALE_THRESHOLD)
+ xscale = PXP_DOWNSCALE_THRESHOLD;
+ if (yscale > PXP_DOWNSCALE_THRESHOLD)
+ yscale = PXP_DOWNSCALE_THRESHOLD;
+ s0scale = BF_PXP_S0SCALE_YSCALE(yscale) | BF_PXP_S0SCALE_XSCALE(xscale);
+ pxp->regs_virt->s0scale = s0scale;
+
+out:
+ pxp_set_ctrl(pxp);
+
+ return ret;
+}
+
+static int pxp_set_fbinfo(struct pxps *pxp)
+{
+ struct fb_var_screeninfo var;
+ struct fb_fix_screeninfo fix;
+ struct v4l2_framebuffer *fb = &pxp->fb;
+ int err;
+
+ err = mxsfb_get_info(&var, &fix);
+
+ fb->fmt.width = var.xres;
+ fb->fmt.height = var.yres;
+ if (var.bits_per_pixel == 16)
+ fb->fmt.pixelformat = V4L2_PIX_FMT_RGB565;
+ else
+ fb->fmt.pixelformat = V4L2_PIX_FMT_RGB24;
+ fb->base = (void *)fix.smem_start;
+ return err;
+}
+
+static void pxp_set_s0bg(struct pxps *pxp)
+{
+ pxp->regs_virt->s0background = pxp->s0_bgcolor;
+}
+
+static void pxp_set_csc(struct pxps *pxp)
+{
+ if (pxp->yuv) {
+ /* YUV colorspace */
+ __raw_writel(0x04030000, PXP_BASE_ADDR + HW_PXP_CSCCOEFF0);
+ __raw_writel(0x01230208, PXP_BASE_ADDR + HW_PXP_CSCCOEFF1);
+ __raw_writel(0x076b079b, PXP_BASE_ADDR + HW_PXP_CSCCOEFF2);
+ } else {
+ /* YCrCb colorspace */
+ __raw_writel(0x84ab01f0, PXP_BASE_ADDR + HW_PXP_CSCCOEFF0);
+ __raw_writel(0x01230204, PXP_BASE_ADDR + HW_PXP_CSCCOEFF1);
+ __raw_writel(0x0730079c, PXP_BASE_ADDR + HW_PXP_CSCCOEFF2);
+ }
+}
+
+static int pxp_set_cstate(struct pxps *pxp, struct v4l2_control *vc)
+{
+
+ if (vc->id == V4L2_CID_HFLIP)
+ pxp->hflip = vc->value;
+ else if (vc->id == V4L2_CID_VFLIP)
+ pxp->vflip = vc->value;
+ else if (vc->id == V4L2_CID_PRIVATE_BASE) {
+ if (vc->value % 90)
+ return -ERANGE;
+ pxp->rotate = vc->value;
+ } else if (vc->id == V4L2_CID_PRIVATE_BASE + 1) {
+ pxp->s0_bgcolor = vc->value;
+ pxp_set_s0bg(pxp);
+ } else if (vc->id == V4L2_CID_PRIVATE_BASE + 2) {
+ pxp->s0_chromakey = vc->value;
+ pxp_set_s0colorkey(pxp);
+ } else if (vc->id == V4L2_CID_PRIVATE_BASE + 3) {
+ pxp->yuv = vc->value;
+ pxp_set_csc(pxp);
+ }
+
+ pxp_set_ctrl(pxp);
+
+ return 0;
+}
+
+static int pxp_get_cstate(struct pxps *pxp, struct v4l2_control *vc)
+{
+ if (vc->id == V4L2_CID_HFLIP)
+ vc->value = pxp->hflip;
+ else if (vc->id == V4L2_CID_VFLIP)
+ vc->value = pxp->vflip;
+ else if (vc->id == V4L2_CID_PRIVATE_BASE)
+ vc->value = pxp->rotate;
+ else if (vc->id == V4L2_CID_PRIVATE_BASE + 1)
+ vc->value = pxp->s0_bgcolor;
+ else if (vc->id == V4L2_CID_PRIVATE_BASE + 2)
+ vc->value = pxp->s0_chromakey;
+ else if (vc->id == V4L2_CID_PRIVATE_BASE + 3)
+ vc->value = pxp->yuv;
+
+ return 0;
+}
+
+static int pxp_enumoutput(struct file *file, void *fh, struct v4l2_output *o)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+ if ((o->index < 0) || (o->index > 1))
+ return -EINVAL;
+
+ memset(o, 0, sizeof(struct v4l2_output));
+ if (o->index == 0) {
+ strcpy(o->name, "PxP Display Output");
+ pxp->output = 0;
+ } else {
+ strcpy(o->name, "PxP Virtual Output");
+ pxp->output = 1;
+ }
+ o->type = V4L2_OUTPUT_TYPE_INTERNAL;
+ o->std = 0;
+ o->reserved[0] = pxp->outb_phys;
+
+ return 0;
+}
+
+static int pxp_g_output(struct file *file, void *fh, unsigned int *i)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+ *i = pxp->output;
+
+ return 0;
+}
+
+static int pxp_s_output(struct file *file, void *fh, unsigned int i)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+ struct v4l2_pix_format *fmt = &pxp->fb.fmt;
+ int bpp;
+
+ if ((i < 0) || (i > 1))
+ return -EINVAL;
+
+ if (pxp->outb)
+ goto out;
+
+ /* Output buffer is same format as fbdev */
+ if (fmt->pixelformat == V4L2_PIX_FMT_RGB24)
+ bpp = 4;
+ else
+ bpp = 2;
+
+ pxp->outb = kmalloc(fmt->width * fmt->height * bpp, GFP_KERNEL);
+ pxp->outb_phys = virt_to_phys(pxp->outb);
+ dma_map_single(NULL, pxp->outb,
+ fmt->width * fmt->height * bpp, DMA_TO_DEVICE);
+
+out:
+ pxp_set_outbuf(pxp);
+
+ return 0;
+}
+
+static int pxp_enum_fmt_video_output(struct file *file, void *fh,
+ struct v4l2_fmtdesc *fmt)
+{
+ enum v4l2_buf_type type = fmt->type;
+ int index = fmt->index;
+
+ if ((fmt->index < 0) || (fmt->index >= ARRAY_SIZE(pxp_s0_formats)))
+ return -EINVAL;
+
+ memset(fmt, 0, sizeof(struct v4l2_fmtdesc));
+ fmt->index = index;
+ fmt->type = type;
+ fmt->pixelformat = pxp_s0_formats[index].fourcc;
+ strcpy(fmt->description, pxp_s0_formats[index].name);
+
+ return 0;
+}
+
+static int pxp_g_fmt_video_output(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct v4l2_pix_format *pf = &f->fmt.pix;
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+ struct pxp_data_format *fmt = pxp->s0_fmt;
+
+ pf->width = pxp->s0_width;
+ pf->height = pxp->s0_height;
+ pf->pixelformat = fmt->fourcc;
+ pf->field = V4L2_FIELD_NONE;
+ pf->bytesperline = fmt->bpp * pf->width;
+ pf->sizeimage = pf->bytesperline * pf->height;
+ pf->colorspace = fmt->colorspace;
+ pf->priv = 0;
+
+ return 0;
+}
+
+static struct pxp_data_format *pxp_get_format(struct v4l2_format *f)
+{
+ struct pxp_data_format *fmt;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(pxp_s0_formats); i++) {
+ fmt = &pxp_s0_formats[i];
+ if (fmt->fourcc == f->fmt.pix.pixelformat)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(pxp_s0_formats))
+ return NULL;
+
+ return &pxp_s0_formats[i];
+}
+
+static int pxp_try_fmt_video_output(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ int w = f->fmt.pix.width;
+ int h = f->fmt.pix.height;
+ struct pxp_data_format *fmt = pxp_get_format(f);
+
+ if (!fmt)
+ return -EINVAL;
+
+ w = min(w, 2040);
+ w = max(w, 8);
+ h = min(h, 2040);
+ h = max(h, 8);
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ f->fmt.pix.width = w;
+ f->fmt.pix.height = h;
+ f->fmt.pix.pixelformat = fmt->fourcc;
+
+ return 0;
+}
+
+static int pxp_s_fmt_video_output(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+ struct v4l2_pix_format *pf = &f->fmt.pix;
+ int ret = pxp_try_fmt_video_output(file, fh, f);
+
+ if (ret == 0) {
+ pxp->s0_fmt = pxp_get_format(f);
+ pxp->s0_width = pf->width;
+ pxp->s0_height = pf->height;
+ pxp_set_ctrl(pxp);
+ pxp_set_s0param(pxp);
+ }
+
+ return ret;
+}
+
+static int pxp_g_fmt_output_overlay(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+ struct v4l2_window *wf = &f->fmt.win;
+
+ memset(wf, 0, sizeof(struct v4l2_window));
+ wf->chromakey = pxp->s1_chromakey;
+ wf->global_alpha = pxp->global_alpha;
+ wf->field = V4L2_FIELD_NONE;
+ wf->clips = NULL;
+ wf->clipcount = 0;
+ wf->bitmap = NULL;
+ wf->w.left = pxp->srect.left;
+ wf->w.top = pxp->srect.top;
+ wf->w.width = pxp->srect.width;
+ wf->w.height = pxp->srect.height;
+
+ return 0;
+}
+
+static int pxp_try_fmt_output_overlay(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+ struct v4l2_window *wf = &f->fmt.win;
+ struct v4l2_rect srect;
+ u32 s1_chromakey = wf->chromakey;
+ u8 global_alpha = wf->global_alpha;
+
+ memcpy(&srect, &(wf->w), sizeof(struct v4l2_rect));
+
+ pxp_g_fmt_output_overlay(file, fh, f);
+
+ wf->chromakey = s1_chromakey;
+ wf->global_alpha = global_alpha;
+
+ /* Constrain parameters to the input buffer */
+ wf->w.left = srect.left;
+ wf->w.top = srect.top;
+ wf->w.width = min(srect.width, ((__s32) pxp->s0_width - wf->w.left));
+ wf->w.height = min(srect.height, ((__s32) pxp->s0_height - wf->w.top));
+
+ return 0;
+}
+
+static int pxp_s_fmt_output_overlay(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+ struct v4l2_window *wf = &f->fmt.win;
+ int ret = pxp_try_fmt_output_overlay(file, fh, f);
+
+ if (ret == 0) {
+ pxp->srect.left = wf->w.left;
+ pxp->srect.top = wf->w.top;
+ pxp->srect.width = wf->w.width;
+ pxp->srect.height = wf->w.height;
+ pxp->global_alpha = wf->global_alpha;
+ pxp->s1_chromakey = wf->chromakey;
+ pxp_set_s0param(pxp);
+ pxp_set_s0crop(pxp);
+ pxp_set_scaling(pxp);
+ pxp_set_olparam(pxp);
+ pxp_set_s1colorkey(pxp);
+ }
+
+ return ret;
+}
+
+static int pxp_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *r)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+ return videobuf_reqbufs(&pxp->s0_vbq, r);
+}
+
+static int pxp_querybuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+ return videobuf_querybuf(&pxp->s0_vbq, b);
+}
+
+static int pxp_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+ return videobuf_qbuf(&pxp->s0_vbq, b);
+}
+
+static int pxp_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+ return videobuf_dqbuf(&pxp->s0_vbq, b, file->f_flags & O_NONBLOCK);
+}
+
+static int pxp_streamon(struct file *file, void *priv, enum v4l2_buf_type t)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+ int ret = 0;
+
+ if ((t != V4L2_BUF_TYPE_VIDEO_OUTPUT))
+ return -EINVAL;
+
+ pxp_set_outbuf(pxp);
+ ret = videobuf_streamon(&pxp->s0_vbq);
+ msleep(20);
+
+ if (!ret && (pxp->output == 0))
+ mxsfb_cfg_pxp(1, pxp->outb_phys);
+
+ return ret;
+}
+
+static int pxp_streamoff(struct file *file, void *priv, enum v4l2_buf_type t)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+ int ret = 0;
+
+ if ((t != V4L2_BUF_TYPE_VIDEO_OUTPUT))
+ return -EINVAL;
+
+ cancel_work_sync(&pxp->work);
+ ret = videobuf_streamoff(&pxp->s0_vbq);
+ msleep(20);
+
+ if (!ret)
+ mxsfb_cfg_pxp(0, 0);
+
+ return ret;
+}
+
+static int pxp_buf_setup(struct videobuf_queue *q,
+ unsigned int *count, unsigned *size)
+{
+ struct pxps *pxp = q->priv_data;
+
+ *size = pxp->s0_width * pxp->s0_height * pxp->s0_fmt->bpp;
+
+ if (0 == *count)
+ *count = PXP_DEF_BUFS;
+
+ return 0;
+}
+
+static void pxp_buf_free(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ if (in_interrupt())
+ BUG();
+
+ videobuf_dma_contig_free(q, vb);
+
+ vb->state = VIDEOBUF_NEEDS_INIT;
+}
+
+static int pxp_buf_prepare(struct videobuf_queue *q,
+ struct videobuf_buffer *vb, enum v4l2_field field)
+{
+ struct pxps *pxp = q->priv_data;
+ int ret = 0;
+
+ vb->width = pxp->s0_width;
+ vb->height = pxp->s0_height;
+ vb->size = vb->width * vb->height * pxp->s0_fmt->bpp;
+ vb->field = V4L2_FIELD_NONE;
+ vb->state = VIDEOBUF_NEEDS_INIT;
+
+ ret = videobuf_iolock(q, vb, NULL);
+ if (ret)
+ goto fail;
+ vb->state = VIDEOBUF_PREPARED;
+
+ return 0;
+
+fail:
+ pxp_buf_free(q, vb);
+ return ret;
+}
+
+static void pxp_buf_next(struct pxps *pxp)
+{
+ dma_addr_t Y, U, V;
+
+ if (pxp->active) {
+ pxp->active->state = VIDEOBUF_ACTIVE;
+ Y = videobuf_to_dma_contig(pxp->active);
+ pxp->regs_virt->s0buf = Y;
+ if ((pxp->s0_fmt->fourcc == V4L2_PIX_FMT_YUV420) ||
+ (pxp->s0_fmt->fourcc == V4L2_PIX_FMT_YUV422P)) {
+ int s = 1; /* default to YUV 4:2:2 */
+ if (pxp->s0_fmt->fourcc == V4L2_PIX_FMT_YUV420)
+ s = 2;
+ U = Y + (pxp->s0_width * pxp->s0_height);
+ V = U + ((pxp->s0_width * pxp->s0_height) >> s);
+ pxp->regs_virt->s0ubuf = U;
+ pxp->regs_virt->s0vbuf = V;
+ }
+ pxp->regs_virt->ctrl =
+ __raw_readl(PXP_BASE_ADDR +
+ HW_PXP_CTRL) | BM_PXP_CTRL_ENABLE;
+ }
+
+ __raw_writel(pxp->regs_phys, PXP_BASE_ADDR + HW_PXP_NEXT);
+}
+
+static void pxp_next_handle(struct work_struct *w)
+{
+ struct pxps *pxp = container_of(w, struct pxps, work);
+ struct pxp_buffer *buf, *next;
+ unsigned long flags;
+
+ if (pxp->next_queue_ended == 1)
+ return;
+
+ spin_lock_irqsave(&pxp->lock, flags);
+
+ while (!list_empty(&pxp->nextq)) {
+ spin_unlock_irqrestore(&pxp->lock, flags);
+
+ if (!wait_event_interruptible_timeout(pxp->done, PXP_WAITCON,
+ 5 * HZ)
+ || signal_pending(current)) {
+ spin_lock_irqsave(&pxp->lock, flags);
+ list_for_each_entry_safe(buf, next, &pxp->nextq, queue)
+ list_del(&buf->queue);
+ spin_unlock_irqrestore(&pxp->lock, flags);
+ pxp->next_queue_ended = 1;
+ return;
+ }
+
+ spin_lock_irqsave(&pxp->lock, flags);
+ buf = list_entry(pxp->nextq.next, struct pxp_buffer, queue);
+ list_del_init(&buf->queue);
+ pxp->active = &buf->vb;
+ pxp->active->state = VIDEOBUF_QUEUED;
+ pxp_buf_next(pxp);
+ }
+
+ spin_unlock_irqrestore(&pxp->lock, flags);
+}
+
+static void pxp_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct pxps *pxp = q->priv_data;
+ struct pxp_buffer *buf;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pxp->lock, flags);
+
+ if (list_empty(&pxp->outq)) {
+ list_add_tail(&vb->queue, &pxp->outq);
+ vb->state = VIDEOBUF_QUEUED;
+
+ pxp->active = vb;
+ pxp_buf_next(pxp);
+ } else {
+ list_add_tail(&vb->queue, &pxp->outq);
+
+ buf = container_of(vb, struct pxp_buffer, vb);
+ list_add_tail(&buf->queue, &pxp->nextq);
+ queue_work(pxp->workqueue, &pxp->work);
+ }
+
+ spin_unlock_irqrestore(&pxp->lock, flags);
+}
+
+static void pxp_buf_release(struct videobuf_queue *q,
+ struct videobuf_buffer *vb)
+{
+ pxp_buf_free(q, vb);
+}
+
+static struct videobuf_queue_ops pxp_vbq_ops = {
+ .buf_setup = pxp_buf_setup,
+ .buf_prepare = pxp_buf_prepare,
+ .buf_queue = pxp_buf_queue,
+ .buf_release = pxp_buf_release,
+};
+
+static int pxp_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+ memset(cap, 0, sizeof(*cap));
+ strcpy(cap->driver, "pxp");
+ strcpy(cap->card, "pxp");
+ strlcpy(cap->bus_info, dev_name(&pxp->pdev->dev),
+ sizeof(cap->bus_info));
+
+ cap->version = (PXP_DRIVER_MAJOR << 8) + PXP_DRIVER_MINOR;
+
+ cap->capabilities = V4L2_CAP_VIDEO_OUTPUT |
+ V4L2_CAP_VIDEO_OUTPUT_OVERLAY | V4L2_CAP_STREAMING;
+
+ return 0;
+}
+
+static int pxp_g_fbuf(struct file *file, void *priv,
+ struct v4l2_framebuffer *fb)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+ memset(fb, 0, sizeof(*fb));
+
+ fb->capability = V4L2_FBUF_CAP_EXTERNOVERLAY |
+ V4L2_FBUF_CAP_CHROMAKEY |
+ V4L2_FBUF_CAP_LOCAL_ALPHA | V4L2_FBUF_CAP_GLOBAL_ALPHA;
+
+ if (pxp->global_alpha_state)
+ fb->flags |= V4L2_FBUF_FLAG_GLOBAL_ALPHA;
+ if (pxp->local_alpha_state)
+ fb->flags |= V4L2_FBUF_FLAG_LOCAL_ALPHA;
+ if (pxp->s1_chromakey_state)
+ fb->flags |= V4L2_FBUF_FLAG_CHROMAKEY;
+
+ return 0;
+}
+
+static int pxp_s_fbuf(struct file *file, void *priv,
+ struct v4l2_framebuffer *fb)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+ pxp->overlay_state = (fb->flags & V4L2_FBUF_FLAG_OVERLAY) != 0;
+ pxp->global_alpha_state =
+ (fb->flags & V4L2_FBUF_FLAG_GLOBAL_ALPHA) != 0;
+ pxp->local_alpha_state = (fb->flags & V4L2_FBUF_FLAG_LOCAL_ALPHA) != 0;
+ /* Global alpha overrides local alpha if both are requested */
+ if (pxp->global_alpha_state && pxp->local_alpha_state)
+ pxp->local_alpha_state = 0;
+ pxp->s1_chromakey_state = (fb->flags & V4L2_FBUF_FLAG_CHROMAKEY) != 0;
+
+ pxp_set_olparam(pxp);
+ pxp_set_s0crop(pxp);
+ pxp_set_scaling(pxp);
+
+ return 0;
+}
+
+static int pxp_g_crop(struct file *file, void *fh, struct v4l2_crop *c)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+ if (c->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY)
+ return -EINVAL;
+
+ c->c.left = pxp->drect.left;
+ c->c.top = pxp->drect.top;
+ c->c.width = pxp->drect.width;
+ c->c.height = pxp->drect.height;
+
+ return 0;
+}
+
+static int pxp_s_crop(struct file *file, void *fh, struct v4l2_crop *c)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+ int l = c->c.left;
+ int t = c->c.top;
+ int w = c->c.width;
+ int h = c->c.height;
+ int fbw = pxp->fb.fmt.width;
+ int fbh = pxp->fb.fmt.height;
+
+ if (c->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY)
+ return -EINVAL;
+
+ /* Constrain parameters to FB limits */
+ w = min(w, fbw);
+ w = max(w, PXP_MIN_PIX);
+ h = min(h, fbh);
+ h = max(h, PXP_MIN_PIX);
+ if ((l + w) > fbw)
+ l = 0;
+ if ((t + h) > fbh)
+ t = 0;
+
+ /* Round up values to PxP pixel block */
+ l = roundup(l, PXP_MIN_PIX);
+ t = roundup(t, PXP_MIN_PIX);
+ w = roundup(w, PXP_MIN_PIX);
+ h = roundup(h, PXP_MIN_PIX);
+
+ pxp->drect.left = l;
+ pxp->drect.top = t;
+ pxp->drect.width = w;
+ pxp->drect.height = h;
+
+ pxp_set_s0param(pxp);
+ pxp_set_s0crop(pxp);
+ pxp_set_scaling(pxp);
+
+ return 0;
+}
+
+static int pxp_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(pxp_controls); i++)
+ if (qc->id && qc->id == pxp_controls[i].id) {
+ memcpy(qc, &(pxp_controls[i]), sizeof(*qc));
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int pxp_g_ctrl(struct file *file, void *priv, struct v4l2_control *vc)
+{
+ int i;
+
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+ for (i = 0; i < ARRAY_SIZE(pxp_controls); i++)
+ if (vc->id == pxp_controls[i].id)
+ return pxp_get_cstate(pxp, vc);
+
+ return -EINVAL;
+}
+
+static int pxp_s_ctrl(struct file *file, void *priv, struct v4l2_control *vc)
+{
+ int i;
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+ for (i = 0; i < ARRAY_SIZE(pxp_controls); i++)
+ if (vc->id == pxp_controls[i].id) {
+ if (vc->value < pxp_controls[i].minimum ||
+ vc->value > pxp_controls[i].maximum)
+ return -ERANGE;
+ return pxp_set_cstate(pxp, vc);
+ }
+
+ return -EINVAL;
+}
+
+void pxp_release(struct video_device *vfd)
+{
+ struct pxps *pxp = video_get_drvdata(vfd);
+
+ spin_lock(&pxp->lock);
+ video_device_release(vfd);
+ spin_unlock(&pxp->lock);
+}
+
+static int pxp_hw_init(struct pxps *pxp)
+{
+ struct fb_var_screeninfo var;
+ struct fb_fix_screeninfo fix;
+ int err;
+
+ err = mxsfb_get_info(&var, &fix);
+ if (err)
+ return err;
+
+ /* Pull PxP out of reset */
+ __raw_writel(0, PXP_BASE_ADDR + HW_PXP_CTRL);
+
+ /* Config defaults */
+ pxp->active = NULL;
+
+ pxp->s0_fmt = &pxp_s0_formats[0];
+ pxp->drect.left = pxp->srect.left = 0;
+ pxp->drect.top = pxp->srect.top = 0;
+ pxp->drect.width = pxp->srect.width = pxp->s0_width = var.xres;
+ pxp->drect.height = pxp->srect.height = pxp->s0_height = var.yres;
+ pxp->s0_bgcolor = 0;
+
+ pxp->output = 0;
+ err = pxp_set_fbinfo(pxp);
+ if (err)
+ return err;
+
+ pxp->scaling = 0;
+ pxp->hflip = 0;
+ pxp->vflip = 0;
+ pxp->rotate = 0;
+ pxp->yuv = 0;
+
+ pxp->overlay_state = 0;
+ pxp->global_alpha_state = 0;
+ pxp->global_alpha = 0;
+ pxp->local_alpha_state = 0;
+ pxp->s1_chromakey_state = 0;
+ pxp->s1_chromakey = -1;
+ pxp->s0_chromakey = -1;
+
+ /* Write default h/w config */
+ pxp_set_ctrl(pxp);
+ pxp_set_s0param(pxp);
+ pxp_set_s0crop(pxp);
+ pxp_set_oln(pxp);
+ pxp_set_olparam(pxp);
+ pxp_set_s0colorkey(pxp);
+ pxp_set_s1colorkey(pxp);
+ pxp_set_csc(pxp);
+
+ return 0;
+}
+
+static int pxp_open(struct file *file)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+ int ret = 0;
+
+ mutex_lock(&pxp->mutex);
+ pxp->users++;
+
+ if (pxp->users > 1) {
+ pxp->users--;
+ ret = -EBUSY;
+ goto out;
+ }
+out:
+ mutex_unlock(&pxp->mutex);
+ if (ret)
+ return ret;
+
+ pxp->next_queue_ended = 0;
+ pxp->workqueue = create_singlethread_workqueue("pxp");
+
+ videobuf_queue_dma_contig_init(&pxp->s0_vbq,
+ &pxp_vbq_ops,
+ &pxp->pdev->dev,
+ &pxp->lock,
+ V4L2_BUF_TYPE_VIDEO_OUTPUT,
+ V4L2_FIELD_NONE,
+ sizeof(struct pxp_buffer), pxp);
+
+ return 0;
+}
+
+static int pxp_close(struct file *file)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+ if (pxp->workqueue) {
+ flush_workqueue(pxp->workqueue);
+ destroy_workqueue(pxp->workqueue);
+ }
+
+ videobuf_stop(&pxp->s0_vbq);
+ videobuf_mmap_free(&pxp->s0_vbq);
+ pxp->active = NULL;
+
+ mutex_lock(&pxp->mutex);
+ pxp->users--;
+ mutex_unlock(&pxp->mutex);
+
+ return 0;
+}
+
+static int pxp_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+ int ret;
+
+ ret = videobuf_mmap_mapper(&pxp->s0_vbq, vma);
+
+ return ret;
+}
+
+static const struct v4l2_file_operations pxp_fops = {
+ .owner = THIS_MODULE,
+ .open = pxp_open,
+ .release = pxp_close,
+ .ioctl = video_ioctl2,
+ .mmap = pxp_mmap,
+};
+
+static const struct v4l2_ioctl_ops pxp_ioctl_ops = {
+ .vidioc_querycap = pxp_querycap,
+
+ .vidioc_reqbufs = pxp_reqbufs,
+ .vidioc_querybuf = pxp_querybuf,
+ .vidioc_qbuf = pxp_qbuf,
+ .vidioc_dqbuf = pxp_dqbuf,
+
+ .vidioc_streamon = pxp_streamon,
+ .vidioc_streamoff = pxp_streamoff,
+
+ .vidioc_enum_output = pxp_enumoutput,
+ .vidioc_g_output = pxp_g_output,
+ .vidioc_s_output = pxp_s_output,
+
+ .vidioc_enum_fmt_vid_out = pxp_enum_fmt_video_output,
+ .vidioc_try_fmt_vid_out = pxp_try_fmt_video_output,
+ .vidioc_g_fmt_vid_out = pxp_g_fmt_video_output,
+ .vidioc_s_fmt_vid_out = pxp_s_fmt_video_output,
+
+ .vidioc_try_fmt_vid_out_overlay = pxp_try_fmt_output_overlay,
+ .vidioc_g_fmt_vid_out_overlay = pxp_g_fmt_output_overlay,
+ .vidioc_s_fmt_vid_out_overlay = pxp_s_fmt_output_overlay,
+
+ .vidioc_g_fbuf = pxp_g_fbuf,
+ .vidioc_s_fbuf = pxp_s_fbuf,
+
+ .vidioc_g_crop = pxp_g_crop,
+ .vidioc_s_crop = pxp_s_crop,
+
+ .vidioc_queryctrl = pxp_queryctrl,
+ .vidioc_g_ctrl = pxp_g_ctrl,
+ .vidioc_s_ctrl = pxp_s_ctrl,
+};
+
+static const struct video_device pxp_template = {
+ .name = "PxP",
+ .vfl_type = V4L2_CAP_VIDEO_OUTPUT |
+ V4L2_CAP_VIDEO_OVERLAY |
+ V4L2_CAP_STREAMING,
+ .fops = &pxp_fops,
+ .release = pxp_release,
+ .minor = -1,
+ .ioctl_ops = &pxp_ioctl_ops,
+};
+
+static irqreturn_t pxp_irq(int irq, void *dev_id)
+{
+ struct pxps *pxp = (struct pxps *)dev_id;
+ struct videobuf_buffer *vb;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pxp->lock, flags);
+
+ __raw_writel(BM_PXP_STAT_IRQ, PXP_BASE_ADDR + HW_PXP_STAT_CLR);
+
+ if (list_empty(&pxp->outq)) {
+ pr_warning("irq: outq empty!!!\n");
+ goto out;
+ }
+
+ vb = list_entry(pxp->outq.next, struct videobuf_buffer, queue);
+ list_del_init(&vb->queue);
+
+ vb->state = VIDEOBUF_DONE;
+ do_gettimeofday(&vb->ts);
+ vb->field_count++;
+
+ wake_up(&vb->done);
+ wake_up(&pxp->done);
+out:
+ spin_unlock_irqrestore(&pxp->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static int pxp_probe(struct platform_device *pdev)
+{
+ struct pxps *pxp;
+ struct resource *res;
+ int irq;
+ int err = 0;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ irq = platform_get_irq(pdev, 0);
+ if (!res || irq < 0) {
+ err = -ENODEV;
+ goto exit;
+ }
+
+ pxp = kzalloc(sizeof(*pxp), GFP_KERNEL);
+ if (!pxp) {
+ dev_err(&pdev->dev, "failed to allocate control object\n");
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ dev_set_drvdata(&pdev->dev, pxp);
+ pxp->res = res;
+ pxp->irq = irq;
+
+ pxp->regs_virt = dma_alloc_coherent(NULL,
+ PAGE_ALIGN(sizeof
+ (struct pxp_registers)),
+ &pxp->regs_phys, GFP_KERNEL);
+ if (pxp->regs_virt == NULL) {
+ dev_err(&pdev->dev, "failed to allocate pxp_register object\n");
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ init_waitqueue_head(&pxp->done);
+
+ INIT_WORK(&pxp->work, pxp_next_handle);
+ INIT_LIST_HEAD(&pxp->outq);
+ INIT_LIST_HEAD(&pxp->nextq);
+ spin_lock_init(&pxp->lock);
+ mutex_init(&pxp->mutex);
+
+ if (!request_mem_region(res->start, res->end - res->start + 1,
+ PXP_DRIVER_NAME)) {
+ err = -EBUSY;
+ goto freepxp;
+ }
+
+ pxp->regs = (void __iomem *)res->start; /* it is already ioremapped */
+ pxp->pdev = pdev;
+
+ err = request_irq(pxp->irq, pxp_irq, 0, PXP_DRIVER_NAME, pxp);
+
+ if (err) {
+ dev_err(&pdev->dev, "interrupt register failed\n");
+ goto release;
+ }
+
+ pxp->vdev = video_device_alloc();
+ if (!pxp->vdev) {
+ dev_err(&pdev->dev, "video_device_alloc() failed\n");
+ err = -ENOMEM;
+ goto freeirq;
+ }
+
+ memcpy(pxp->vdev, &pxp_template, sizeof(pxp_template));
+ video_set_drvdata(pxp->vdev, pxp);
+
+ err = video_register_device(pxp->vdev, VFL_TYPE_GRABBER, 0);
+ if (err) {
+ dev_err(&pdev->dev, "failed to register video device\n");
+ goto freevdev;
+ }
+
+ err = pxp_hw_init(pxp);
+ if (err) {
+ dev_err(&pdev->dev, "failed to initialize hardware\n");
+ goto freevdev;
+ }
+
+ dev_info(&pdev->dev, "initialized\n");
+
+exit:
+ return err;
+
+freevdev:
+ video_device_release(pxp->vdev);
+
+freeirq:
+ free_irq(pxp->irq, pxp);
+
+release:
+ release_mem_region(res->start, res->end - res->start + 1);
+
+freepxp:
+ kfree(pxp);
+
+ return err;
+}
+
+static int __devexit pxp_remove(struct platform_device *pdev)
+{
+ struct pxps *pxp = platform_get_drvdata(pdev);
+
+ video_unregister_device(pxp->vdev);
+ video_device_release(pxp->vdev);
+
+ if (pxp->regs_virt)
+ dma_free_coherent(0, PAGE_ALIGN(sizeof(struct pxp_registers)),
+ pxp->regs_virt, pxp->regs_phys);
+ kfree(pxp->outb);
+ kfree(pxp);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int pxp_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ int i;
+
+ while (__raw_readl(PXP_BASE_ADDR + HW_PXP_CTRL) & BM_PXP_CTRL_ENABLE)
+ ;
+
+ for (i = 0; i < REGS1_NUMS; i++)
+ regs1[i] =
+ __raw_readl(PXP_BASE_ADDR + HW_PXP_CTRL + REG_OFFSET * i);
+
+ for (i = 0; i < REGS2_NUMS; i++)
+ regs2[i] =
+ __raw_readl(PXP_BASE_ADDR + HW_PXP_S0COLORKEYLOW +
+ REG_OFFSET * i);
+
+ for (i = 0; i < REGS3_NUMS; i++)
+ regs3[i] =
+ __raw_readl(PXP_BASE_ADDR + HW_PXP_OLn(0) + REG_OFFSET * i);
+
+ __raw_writel(BM_PXP_CTRL_SFTRST, PXP_BASE_ADDR + HW_PXP_CTRL);
+
+ return 0;
+}
+
+static int pxp_resume(struct platform_device *pdev)
+{
+ int i;
+
+ /* Pull PxP out of reset */
+ __raw_writel(0, PXP_BASE_ADDR + HW_PXP_CTRL);
+
+ for (i = 0; i < REGS1_NUMS; i++)
+ __raw_writel(regs1[i],
+ PXP_BASE_ADDR + HW_PXP_CTRL + REG_OFFSET * i);
+
+ for (i = 0; i < REGS2_NUMS; i++)
+ __raw_writel(regs2[i],
+ PXP_BASE_ADDR + HW_PXP_S0COLORKEYLOW +
+ REG_OFFSET * i);
+
+ for (i = 0; i < REGS3_NUMS; i++)
+ __raw_writel(regs3[i],
+ PXP_BASE_ADDR + HW_PXP_OLn(0) + REG_OFFSET * i);
+
+ return 0;
+}
+#else
+#define pxp_suspend NULL
+#define pxp_resume NULL
+#endif
+
+static struct platform_driver pxp_driver = {
+ .driver = {
+ .name = PXP_DRIVER_NAME,
+ },
+ .probe = pxp_probe,
+ .remove = __exit_p(pxp_remove),
+ .suspend = pxp_suspend,
+ .resume = pxp_resume,
+};
+
+static int __devinit pxp_init(void)
+{
+ return platform_driver_register(&pxp_driver);
+}
+
+static void __exit pxp_exit(void)
+{
+ platform_driver_unregister(&pxp_driver);
+}
+
+module_init(pxp_init);
+module_exit(pxp_exit);
+
+MODULE_DESCRIPTION("STMP37xx PxP driver");
+MODULE_AUTHOR("Matt Porter <mporter@embeddedalley.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxs_pxp.h b/drivers/media/video/mxs_pxp.h
new file mode 100644
index 000000000000..6538388fa4cd
--- /dev/null
+++ b/drivers/media/video/mxs_pxp.h
@@ -0,0 +1,158 @@
+/*
+ * Freescale MXS PxP driver
+ *
+ * Author: Matt Porter <mporter@embeddedalley.com>
+ *
+ * Copyright 2008-2010 Freescale Semiconductor, Inc.
+ * Copyright 2008-2009 Embedded Alley Solutions, Inc All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef CONFIG_ARCH_MX23
+#define BF_PXP_CTRL_OUTBUF_FORMAT(v) BF_PXP_CTRL_OUTPUT_RGB_FORMAT(v)
+
+#define BV_PXP_CTRL_OUTBUF_FORMAT__ARGB8888 \
+ BV_PXP_CTRL_OUTPUT_RGB_FORMAT__ARGB8888
+#define BV_PXP_CTRL_OUTBUF_FORMAT__RGB888 \
+ BV_PXP_CTRL_OUTPUT_RGB_FORMAT__RGB888
+#define BV_PXP_CTRL_OUTBUF_FORMAT__RGB888P \
+ BV_PXP_CTRL_OUTPUT_RGB_FORMAT__RGB888P
+#define BV_PXP_CTRL_OUTBUF_FORMAT__ARGB1555 \
+ BV_PXP_CTRL_OUTPUT_RGB_FORMAT__ARGB1555
+#define BV_PXP_CTRL_OUTBUF_FORMAT__RGB565 \
+ BV_PXP_CTRL_OUTPUT_RGB_FORMAT__RGB565
+#define BV_PXP_CTRL_OUTBUF_FORMAT__RGB555 \
+ BV_PXP_CTRL_OUTPUT_RGB_FORMAT__RGB555
+
+#define BF_PXP_OUTSIZE_WIDTH(v) BF_PXP_RGBSIZE_WIDTH(v)
+#define BF_PXP_OUTSIZE_HEIGHT(v) BF_PXP_RGBSIZE_HEIGHT(v)
+
+/* The maximum down scaling factor is 1/2 */
+#define PXP_DOWNSCALE_THRESHOLD 0x2000
+#else
+/* The maximum down scaling factor is 1/4 */
+#define PXP_DOWNSCALE_THRESHOLD 0x4000
+#endif
+
+struct pxp_overlay_registers {
+ u32 ol;
+ u32 olsize;
+ u32 olparam;
+ u32 olparam2;
+};
+
+/* Registers feed for PXP_NEXT */
+struct pxp_registers {
+ u32 ctrl;
+ u32 outbuf;
+ u32 outbuf2;
+ u32 outsize;
+ u32 s0buf;
+ u32 s0ubuf;
+ u32 s0vbuf;
+ u32 s0param;
+ u32 s0background;
+ u32 s0crop;
+ u32 s0scale;
+ u32 s0offset;
+ u32 s0colorkeylow;
+ u32 s0colorkeyhigh;
+ u32 olcolorkeylow;
+ u32 olcolorkeyhigh;
+
+ struct pxp_overlay_registers ol0;
+ struct pxp_overlay_registers ol1;
+ struct pxp_overlay_registers ol2;
+ struct pxp_overlay_registers ol3;
+ struct pxp_overlay_registers ol4;
+ struct pxp_overlay_registers ol5;
+ struct pxp_overlay_registers ol6;
+ struct pxp_overlay_registers ol7;
+};
+
+struct pxp_buffer {
+ /* Must be first! */
+ struct videobuf_buffer vb;
+ struct list_head queue;
+};
+
+struct pxps {
+ struct platform_device *pdev;
+ struct resource *res;
+ int irq;
+ void __iomem *regs;
+
+ struct work_struct work;
+ struct workqueue_struct *workqueue;
+ spinlock_t lock;
+ struct mutex mutex;
+ int users;
+
+ struct video_device *vdev;
+
+ struct videobuf_queue s0_vbq;
+ struct videobuf_buffer *active;
+ struct list_head outq;
+ struct list_head nextq;
+
+ int output;
+ u32 *outb;
+ dma_addr_t outb_phys;
+
+ /* Current S0 configuration */
+ struct pxp_data_format *s0_fmt;
+ u32 s0_width;
+ u32 s0_height;
+ u32 s0_bgcolor;
+ u32 s0_chromakey;
+
+ struct v4l2_framebuffer fb;
+ struct v4l2_rect drect;
+ struct v4l2_rect srect;
+
+ /* Transformation support */
+ int scaling;
+ int hflip;
+ int vflip;
+ int rotate;
+ int yuv;
+
+ /* Output overlay support */
+ int overlay_state;
+ int global_alpha_state;
+ u8 global_alpha;
+ int local_alpha_state;
+ int s1_chromakey_state;
+ u32 s1_chromakey;
+
+ /* PXP_NEXT */
+ u32 regs_phys;
+ struct pxp_registers *regs_virt;
+ wait_queue_head_t done;
+ int next_queue_ended;
+};
+
+struct pxp_data_format {
+ char *name;
+ unsigned int bpp;
+ u32 fourcc;
+ enum v4l2_colorspace colorspace;
+ u32 ctrl_s0_fmt;
+};
+
+extern int mxsfb_get_info(struct fb_var_screeninfo *var,
+ struct fb_fix_screeninfo *fix);
+extern void mxsfb_cfg_pxp(int enable, dma_addr_t pxp_phys);
diff --git a/drivers/media/video/pxp.c b/drivers/media/video/pxp.c
new file mode 100644
index 000000000000..0f0e89cd6806
--- /dev/null
+++ b/drivers/media/video/pxp.c
@@ -0,0 +1,1408 @@
+/*
+ * Freescale STMP378X PxP driver
+ *
+ * Author: Matt Porter <mporter@embeddedalley.com>
+ *
+ * Copyright 2008-2010 Freescale Semiconductor, Inc.
+ * Copyright 2008-2009 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/vmalloc.h>
+#include <linux/videodev2.h>
+
+#include <media/videobuf-dma-contig.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+
+#include <mach/platform.h>
+#include <mach/regs-pxp.h>
+#include <mach/lcdif.h>
+
+#include "pxp.h"
+
+#define PXP_DRIVER_NAME "stmp3xxx-pxp"
+#define PXP_DRIVER_MAJOR 1
+#define PXP_DRIVER_MINOR 0
+
+#define PXP_DEF_BUFS 2
+#define PXP_MIN_PIX 8
+
+#define V4L2_OUTPUT_TYPE_INTERNAL 4
+
+#define PXP_WAITCON ((__raw_readl(HW_PXP_NEXT_ADDR) & BM_PXP_NEXT_ENABLED) \
+ != BM_PXP_NEXT_ENABLED)
+
+#define REG_OFFSET 0x10
+#define REGS1_NUMS 16
+#define REGS2_NUMS 5
+#define REGS3_NUMS 32
+static u32 regs1[REGS1_NUMS];
+static u32 regs2[REGS2_NUMS];
+static u32 regs3[REGS3_NUMS];
+
+static struct pxp_data_format pxp_s0_formats[] = {
+ {
+ .name = "24-bit RGB",
+ .bpp = 4,
+ .fourcc = V4L2_PIX_FMT_RGB24,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .ctrl_s0_fmt = BV_PXP_CTRL_S0_FORMAT__RGB888,
+ }, {
+ .name = "16-bit RGB 5:6:5",
+ .bpp = 2,
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .ctrl_s0_fmt = BV_PXP_CTRL_S0_FORMAT__RGB565,
+ }, {
+ .name = "16-bit RGB 5:5:5",
+ .bpp = 2,
+ .fourcc = V4L2_PIX_FMT_RGB555,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .ctrl_s0_fmt = BV_PXP_CTRL_S0_FORMAT__RGB555,
+ }, {
+ .name = "YUV 4:2:0 Planar",
+ .bpp = 2,
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .ctrl_s0_fmt = BV_PXP_CTRL_S0_FORMAT__YUV420,
+ }, {
+ .name = "YUV 4:2:2 Planar",
+ .bpp = 2,
+ .fourcc = V4L2_PIX_FMT_YUV422P,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .ctrl_s0_fmt = BV_PXP_CTRL_S0_FORMAT__YUV422,
+ },
+};
+
+struct v4l2_queryctrl pxp_controls[] = {
+ {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Horizontal Flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ .flags = 0,
+ }, {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Vertical Flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ .flags = 0,
+ }, {
+ .id = V4L2_CID_PRIVATE_BASE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Rotation",
+ .minimum = 0,
+ .maximum = 270,
+ .step = 90,
+ .default_value = 0,
+ .flags = 0,
+ }, {
+ .id = V4L2_CID_PRIVATE_BASE + 1,
+ .name = "Background Color",
+ .minimum = 0,
+ .maximum = 0xFFFFFF,
+ .step = 1,
+ .default_value = 0,
+ .flags = 0,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ }, {
+ .id = V4L2_CID_PRIVATE_BASE + 2,
+ .name = "Set S0 Chromakey",
+ .minimum = -1,
+ .maximum = 0xFFFFFF,
+ .step = 1,
+ .default_value = -1,
+ .flags = 0,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ }, {
+ .id = V4L2_CID_PRIVATE_BASE + 3,
+ .name = "YUV Colorspace",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ .flags = 0,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },
+};
+
+static void pxp_set_ctrl(struct pxps *pxp)
+{
+ u32 ctrl;
+
+ ctrl = BF(pxp->s0_fmt->ctrl_s0_fmt, PXP_CTRL_S0_FORMAT);
+ ctrl |=
+ BF(BV_PXP_CTRL_OUTPUT_RGB_FORMAT__RGB888, PXP_CTRL_OUTPUT_RGB_FORMAT);
+ ctrl |= BM_PXP_CTRL_CROP;
+
+ if (pxp->scaling)
+ ctrl |= BM_PXP_CTRL_SCALE;
+ if (pxp->vflip)
+ ctrl |= BM_PXP_CTRL_VFLIP;
+ if (pxp->hflip)
+ ctrl |= BM_PXP_CTRL_HFLIP;
+ if (pxp->rotate)
+ ctrl |= BF(pxp->rotate/90, PXP_CTRL_ROTATE);
+
+ ctrl |= BM_PXP_CTRL_IRQ_ENABLE;
+ if (pxp->active)
+ ctrl |= BM_PXP_CTRL_ENABLE;
+
+ __raw_writel(ctrl, HW_PXP_CTRL_ADDR);
+ pxp->regs_virt->ctrl = ctrl;
+}
+
+static void pxp_set_rgbbuf(struct pxps *pxp)
+{
+ pxp->regs_virt->rgbbuf = pxp->outb_phys;
+ /* Always equal to the FB size */
+ pxp->regs_virt->rgbsize = BF(pxp->fb.fmt.width, PXP_RGBSIZE_WIDTH) |
+ BF(pxp->fb.fmt.height, PXP_RGBSIZE_HEIGHT);
+}
+
+static void pxp_set_s0colorkey(struct pxps *pxp)
+{
+ /* Low and high are set equal. V4L does not allow a chromakey range */
+ if (pxp->s0_chromakey == -1) {
+ /* disable color key */
+ pxp->regs_virt->s0colorkeylow = 0xFFFFFF;
+ pxp->regs_virt->s0colorkeyhigh = 0;
+ } else {
+ pxp->regs_virt->s0colorkeylow = pxp->s0_chromakey;
+ pxp->regs_virt->s0colorkeyhigh = pxp->s0_chromakey;
+ }
+}
+
+static void pxp_set_s1colorkey(struct pxps *pxp)
+{
+ /* Low and high are set equal. V4L does not allow a chromakey range */
+ if (pxp->s1_chromakey_state != 0 && pxp->s1_chromakey != -1) {
+ pxp->regs_virt->olcolorkeylow = pxp->s1_chromakey;
+ pxp->regs_virt->olcolorkeyhigh = pxp->s1_chromakey;
+ } else {
+ /* disable color key */
+ pxp->regs_virt->olcolorkeylow = 0xFFFFFF;
+ pxp->regs_virt->olcolorkeyhigh = 0;
+ }
+}
+
+static void pxp_set_oln(struct pxps *pxp)
+{
+ pxp->regs_virt->ol0.ol = (u32)pxp->fb.base;
+ pxp->regs_virt->ol0.olsize =
+ BF(pxp->fb.fmt.width >> 3, PXP_OLnSIZE_WIDTH) |
+ BF(pxp->fb.fmt.height >> 3, PXP_OLnSIZE_HEIGHT);
+}
+
+static void pxp_set_olparam(struct pxps *pxp)
+{
+ u32 olparam;
+ struct v4l2_pix_format *fmt = &pxp->fb.fmt;
+
+ olparam = BF(pxp->global_alpha, PXP_OLnPARAM_ALPHA);
+ if (fmt->pixelformat == V4L2_PIX_FMT_RGB24)
+ olparam |=
+ BF(BV_PXP_OLnPARAM_FORMAT__RGB888, PXP_OLnPARAM_FORMAT);
+ else
+ olparam |=
+ BF(BV_PXP_OLnPARAM_FORMAT__RGB565, PXP_OLnPARAM_FORMAT);
+ if (pxp->global_alpha_state)
+ olparam |= BF(BV_PXP_OLnPARAM_ALPHA_CNTL__Override,
+ PXP_OLnPARAM_ALPHA_CNTL);
+ if (pxp->s1_chromakey_state)
+ olparam |= BM_PXP_OLnPARAM_ENABLE_COLORKEY;
+ if (pxp->overlay_state)
+ olparam |= BM_PXP_OLnPARAM_ENABLE;
+
+ pxp->regs_virt->ol0.olparam = olparam;
+}
+
+static void pxp_set_s0param(struct pxps *pxp)
+{
+ u32 s0param;
+
+ s0param = BF(pxp->drect.left >> 3, PXP_S0PARAM_XBASE);
+ s0param |= BF(pxp->drect.top >> 3, PXP_S0PARAM_YBASE);
+ s0param |= BF(pxp->s0_width >> 3, PXP_S0PARAM_WIDTH);
+ s0param |= BF(pxp->s0_height >> 3, PXP_S0PARAM_HEIGHT);
+ pxp->regs_virt->s0param = s0param;
+}
+
+static void pxp_set_s0crop(struct pxps *pxp)
+{
+ u32 s0crop;
+
+ s0crop = BF(pxp->srect.left >> 3, PXP_S0CROP_XBASE);
+ s0crop |= BF(pxp->srect.top >> 3, PXP_S0CROP_YBASE);
+ s0crop |= BF(pxp->drect.width >> 3, PXP_S0CROP_WIDTH);
+ s0crop |= BF(pxp->drect.height >> 3, PXP_S0CROP_HEIGHT);
+ pxp->regs_virt->s0crop = s0crop;
+}
+
+static int pxp_set_scaling(struct pxps *pxp)
+{
+ int ret = 0;
+ u32 xscale, yscale, s0scale;
+
+ if ((pxp->s0_fmt->fourcc != V4L2_PIX_FMT_YUV420) &&
+ (pxp->s0_fmt->fourcc != V4L2_PIX_FMT_YUV422P)) {
+ pxp->scaling = 0;
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if ((pxp->srect.width == pxp->drect.width) &&
+ (pxp->srect.height == pxp->drect.height)) {
+ pxp->scaling = 0;
+ goto out;
+ }
+
+ pxp->scaling = 1;
+ xscale = pxp->srect.width * 0x1000 / pxp->drect.width;
+ yscale = pxp->srect.height * 0x1000 / pxp->drect.height;
+ s0scale = BF(yscale, PXP_S0SCALE_YSCALE) |
+ BF(xscale, PXP_S0SCALE_XSCALE);
+ pxp->regs_virt->s0scale = s0scale;
+
+out:
+ pxp_set_ctrl(pxp);
+
+ return ret;
+}
+
+static int pxp_set_fbinfo(struct pxps *pxp)
+{
+ struct fb_var_screeninfo var;
+ struct fb_fix_screeninfo fix;
+ struct v4l2_framebuffer *fb = &pxp->fb;
+ int err;
+
+ err = stmp3xxxfb_get_info(&var, &fix);
+
+ fb->fmt.width = var.xres;
+ fb->fmt.height = var.yres;
+ if (var.bits_per_pixel == 16)
+ fb->fmt.pixelformat = V4L2_PIX_FMT_RGB565;
+ else
+ fb->fmt.pixelformat = V4L2_PIX_FMT_RGB24;
+ fb->base = (void *)fix.smem_start;
+ return err;
+}
+
+static void pxp_set_s0bg(struct pxps *pxp)
+{
+ pxp->regs_virt->s0background = pxp->s0_bgcolor;
+}
+
+static void pxp_set_csc(struct pxps *pxp)
+{
+ if (pxp->yuv) {
+ /* YUV colorspace */
+ __raw_writel(0x04030000, HW_PXP_CSCCOEFF0_ADDR);
+ __raw_writel(0x01230208, HW_PXP_CSCCOEFF1_ADDR);
+ __raw_writel(0x076b079c, HW_PXP_CSCCOEFF2_ADDR);
+ } else {
+ /* YCrCb colorspace */
+ __raw_writel(0x84ab01f0, HW_PXP_CSCCOEFF0_ADDR);
+ __raw_writel(0x01230204, HW_PXP_CSCCOEFF1_ADDR);
+ __raw_writel(0x0730079c, HW_PXP_CSCCOEFF2_ADDR);
+ }
+}
+
+static int pxp_set_cstate(struct pxps *pxp, struct v4l2_control *vc)
+{
+
+ if (vc->id == V4L2_CID_HFLIP)
+ pxp->hflip = vc->value;
+ else if (vc->id == V4L2_CID_VFLIP)
+ pxp->vflip = vc->value;
+ else if (vc->id == V4L2_CID_PRIVATE_BASE) {
+ if (vc->value % 90)
+ return -ERANGE;
+ pxp->rotate = vc->value;
+ } else if (vc->id == V4L2_CID_PRIVATE_BASE + 1) {
+ pxp->s0_bgcolor = vc->value;
+ pxp_set_s0bg(pxp);
+ } else if (vc->id == V4L2_CID_PRIVATE_BASE + 2) {
+ pxp->s0_chromakey = vc->value;
+ pxp_set_s0colorkey(pxp);
+ } else if (vc->id == V4L2_CID_PRIVATE_BASE + 3) {
+ pxp->yuv = vc->value;
+ pxp_set_csc(pxp);
+ }
+
+ pxp_set_ctrl(pxp);
+
+ return 0;
+}
+
+static int pxp_get_cstate(struct pxps *pxp, struct v4l2_control *vc)
+{
+ if (vc->id == V4L2_CID_HFLIP)
+ vc->value = pxp->hflip;
+ else if (vc->id == V4L2_CID_VFLIP)
+ vc->value = pxp->vflip;
+ else if (vc->id == V4L2_CID_PRIVATE_BASE)
+ vc->value = pxp->rotate;
+ else if (vc->id == V4L2_CID_PRIVATE_BASE + 1)
+ vc->value = pxp->s0_bgcolor;
+ else if (vc->id == V4L2_CID_PRIVATE_BASE + 2)
+ vc->value = pxp->s0_chromakey;
+ else if (vc->id == V4L2_CID_PRIVATE_BASE + 3)
+ vc->value = pxp->yuv;
+
+ return 0;
+}
+
+static int pxp_enumoutput(struct file *file, void *fh,
+ struct v4l2_output *o)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+ if ((o->index < 0) || (o->index > 1))
+ return -EINVAL;
+
+ memset(o, 0, sizeof(struct v4l2_output));
+ if (o->index == 0) {
+ strcpy(o->name, "PxP Display Output");
+ pxp->output = 0;
+ } else {
+ strcpy(o->name, "PxP Virtual Output");
+ pxp->output = 1;
+ }
+ o->type = V4L2_OUTPUT_TYPE_INTERNAL;
+ o->std = 0;
+ o->reserved[0] = pxp->outb_phys;
+
+ return 0;
+}
+
+static int pxp_g_output(struct file *file, void *fh,
+ unsigned int *i)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+ *i = pxp->output;
+
+ return 0;
+}
+
+static int pxp_s_output(struct file *file, void *fh,
+ unsigned int i)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+ struct v4l2_pix_format *fmt = &pxp->fb.fmt;
+ int bpp;
+
+ if ((i < 0) || (i > 1))
+ return -EINVAL;
+
+ if (pxp->outb)
+ goto out;
+
+ /* Output buffer is same format as fbdev */
+ if (fmt->pixelformat == V4L2_PIX_FMT_RGB24)
+ bpp = 4;
+ else
+ bpp = 2;
+
+ pxp->outb = kmalloc(fmt->width * fmt->height * bpp, GFP_KERNEL);
+ pxp->outb_phys = virt_to_phys(pxp->outb);
+ dma_map_single(NULL, pxp->outb,
+ fmt->width * fmt->height * bpp, DMA_TO_DEVICE);
+
+out:
+ pxp_set_rgbbuf(pxp);
+
+ return 0;
+}
+
+static int pxp_enum_fmt_video_output(struct file *file, void *fh,
+ struct v4l2_fmtdesc *fmt)
+{
+ enum v4l2_buf_type type = fmt->type;
+ int index = fmt->index;
+
+ if ((fmt->index < 0) || (fmt->index >= ARRAY_SIZE(pxp_s0_formats)))
+ return -EINVAL;
+
+ memset(fmt, 0, sizeof(struct v4l2_fmtdesc));
+ fmt->index = index;
+ fmt->type = type;
+ fmt->pixelformat = pxp_s0_formats[index].fourcc;
+ strcpy(fmt->description, pxp_s0_formats[index].name);
+
+ return 0;
+}
+
+static int pxp_g_fmt_video_output(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct v4l2_pix_format *pf = &f->fmt.pix;
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+ struct pxp_data_format *fmt = pxp->s0_fmt;
+
+ pf->width = pxp->s0_width;
+ pf->height = pxp->s0_height;
+ pf->pixelformat = fmt->fourcc;
+ pf->field = V4L2_FIELD_NONE;
+ pf->bytesperline = fmt->bpp * pf->width;
+ pf->sizeimage = pf->bytesperline * pf->height;
+ pf->colorspace = fmt->colorspace;
+ pf->priv = 0;
+
+ return 0;
+}
+
+static struct pxp_data_format *pxp_get_format(struct v4l2_format *f)
+{
+ struct pxp_data_format *fmt;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(pxp_s0_formats); i++) {
+ fmt = &pxp_s0_formats[i];
+ if (fmt->fourcc == f->fmt.pix.pixelformat)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(pxp_s0_formats))
+ return NULL;
+
+ return &pxp_s0_formats[i];
+}
+
+static int pxp_try_fmt_video_output(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ int w = f->fmt.pix.width;
+ int h = f->fmt.pix.height;
+ struct pxp_data_format *fmt = pxp_get_format(f);
+
+ if (!fmt)
+ return -EINVAL;
+
+ w = min(w, 2040);
+ w = max(w, 8);
+ h = min(h, 2040);
+ h = max(h, 8);
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ f->fmt.pix.width = w;
+ f->fmt.pix.height = h;
+ f->fmt.pix.pixelformat = fmt->fourcc;
+
+ return 0;
+}
+
+static int pxp_s_fmt_video_output(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+ struct v4l2_pix_format *pf = &f->fmt.pix;
+ int ret = pxp_try_fmt_video_output(file, fh, f);
+
+ if (ret == 0) {
+ pxp->s0_fmt = pxp_get_format(f);
+ pxp->s0_width = pf->width;
+ pxp->s0_height = pf->height;
+ pxp_set_ctrl(pxp);
+ pxp_set_s0param(pxp);
+ }
+
+ return ret;
+}
+
+static int pxp_g_fmt_output_overlay(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+ struct v4l2_window *wf = &f->fmt.win;
+
+ memset(wf, 0, sizeof(struct v4l2_window));
+ wf->chromakey = pxp->s1_chromakey;
+ wf->global_alpha = pxp->global_alpha;
+ wf->field = V4L2_FIELD_NONE;
+ wf->clips = NULL;
+ wf->clipcount = 0;
+ wf->bitmap = NULL;
+ wf->w.left = pxp->srect.left;
+ wf->w.top = pxp->srect.top;
+ wf->w.width = pxp->srect.width;
+ wf->w.height = pxp->srect.height;
+
+ return 0;
+}
+
+static int pxp_try_fmt_output_overlay(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+ struct v4l2_window *wf = &f->fmt.win;
+ struct v4l2_rect srect;
+ u32 s1_chromakey = wf->chromakey;
+ u8 global_alpha = wf->global_alpha;
+
+ memcpy(&srect, &(wf->w), sizeof(struct v4l2_rect));
+
+ pxp_g_fmt_output_overlay(file, fh, f);
+
+ wf->chromakey = s1_chromakey;
+ wf->global_alpha = global_alpha;
+
+ /* Constrain parameters to the input buffer */
+ wf->w.left = srect.left;
+ wf->w.top = srect.top;
+ wf->w.width = min(srect.width, ((__s32)pxp->s0_width - wf->w.left));
+ wf->w.height = min(srect.height, ((__s32)pxp->s0_height - wf->w.top));
+
+ return 0;
+}
+
+static int pxp_s_fmt_output_overlay(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+ struct v4l2_window *wf = &f->fmt.win;
+ int ret = pxp_try_fmt_output_overlay(file, fh, f);
+
+ if (ret == 0) {
+ pxp->srect.left = wf->w.left;
+ pxp->srect.top = wf->w.top;
+ pxp->srect.width = wf->w.width;
+ pxp->srect.height = wf->w.height;
+ pxp->global_alpha = wf->global_alpha;
+ pxp->s1_chromakey = wf->chromakey;
+ pxp_set_s0param(pxp);
+ pxp_set_s0crop(pxp);
+ pxp_set_scaling(pxp);
+ pxp_set_olparam(pxp);
+ pxp_set_s1colorkey(pxp);
+ }
+
+ return ret;
+}
+
+static int pxp_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *r)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+ return videobuf_reqbufs(&pxp->s0_vbq, r);
+}
+
+static int pxp_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *b)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+ return videobuf_querybuf(&pxp->s0_vbq, b);
+}
+
+static int pxp_qbuf(struct file *file, void *priv,
+ struct v4l2_buffer *b)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+ return videobuf_qbuf(&pxp->s0_vbq, b);
+}
+
+static int pxp_dqbuf(struct file *file, void *priv,
+ struct v4l2_buffer *b)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+ return videobuf_dqbuf(&pxp->s0_vbq, b, file->f_flags & O_NONBLOCK);
+}
+
+static int pxp_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type t)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+ int ret = 0;
+
+ if ((t != V4L2_BUF_TYPE_VIDEO_OUTPUT))
+ return -EINVAL;
+
+ ret = videobuf_streamon(&pxp->s0_vbq);
+
+ if (!ret && (pxp->output == 0))
+ stmp3xxxfb_cfg_pxp(1, pxp->outb_phys);
+
+ return ret;
+}
+
+static int pxp_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type t)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+ int ret = 0;
+
+ if ((t != V4L2_BUF_TYPE_VIDEO_OUTPUT))
+ return -EINVAL;
+
+ ret = videobuf_streamoff(&pxp->s0_vbq);
+
+ if (!ret)
+ stmp3xxxfb_cfg_pxp(0, 0);
+
+ return ret;
+}
+
+static int pxp_buf_setup(struct videobuf_queue *q,
+ unsigned int *count, unsigned *size)
+{
+ struct pxps *pxp = q->priv_data;
+
+ *size = pxp->s0_width * pxp->s0_height * pxp->s0_fmt->bpp;
+
+ if (0 == *count)
+ *count = PXP_DEF_BUFS;
+
+ return 0;
+}
+
+static void pxp_buf_free(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ if (in_interrupt())
+ BUG();
+
+ videobuf_dma_contig_free(q, vb);
+
+ vb->state = VIDEOBUF_NEEDS_INIT;
+}
+
+static int pxp_buf_prepare(struct videobuf_queue *q,
+ struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct pxps *pxp = q->priv_data;
+ int ret = 0;
+
+ vb->width = pxp->s0_width;
+ vb->height = pxp->s0_height;
+ vb->size = vb->width * vb->height * pxp->s0_fmt->bpp;
+ vb->field = V4L2_FIELD_NONE;
+ vb->state = VIDEOBUF_NEEDS_INIT;
+
+ ret = videobuf_iolock(q, vb, NULL);
+ if (ret)
+ goto fail;
+ vb->state = VIDEOBUF_PREPARED;
+
+ return 0;
+
+fail:
+ pxp_buf_free(q, vb);
+ return ret;
+}
+
+static void pxp_buf_next(struct pxps *pxp)
+{
+ dma_addr_t Y, U, V;
+
+ if (pxp->active) {
+ pxp->active->state = VIDEOBUF_ACTIVE;
+ Y = videobuf_to_dma_contig(pxp->active);
+ pxp->regs_virt->s0buf = Y;
+ if ((pxp->s0_fmt->fourcc == V4L2_PIX_FMT_YUV420) ||
+ (pxp->s0_fmt->fourcc == V4L2_PIX_FMT_YUV422P)) {
+ int s = 1; /* default to YUV 4:2:2 */
+ if (pxp->s0_fmt->fourcc == V4L2_PIX_FMT_YUV420)
+ s = 2;
+ U = Y + (pxp->s0_width * pxp->s0_height);
+ V = U + ((pxp->s0_width * pxp->s0_height) >> s);
+ pxp->regs_virt->s0ubuf = U;
+ pxp->regs_virt->s0vbuf = V;
+ }
+ pxp->regs_virt->ctrl =
+ __raw_readl(HW_PXP_CTRL_ADDR) | BM_PXP_CTRL_ENABLE;
+ }
+
+ __raw_writel(pxp->regs_phys, HW_PXP_NEXT_ADDR);
+}
+
+static void pxp_next_handle(struct work_struct *w)
+{
+ struct pxps *pxp = container_of(w, struct pxps, work);
+ struct pxp_buffer *buf, *next;
+ unsigned long flags;
+
+ if (pxp->next_queue_ended == 1)
+ return;
+
+ spin_lock_irqsave(&pxp->lock, flags);
+
+ while (!list_empty(&pxp->nextq)) {
+ spin_unlock_irqrestore(&pxp->lock, flags);
+
+ if (!wait_event_interruptible_timeout(pxp->done, PXP_WAITCON,
+ 5 * HZ) || signal_pending(current)) {
+ spin_lock_irqsave(&pxp->lock, flags);
+ list_for_each_entry_safe(buf, next, &pxp->nextq, queue)
+ list_del(&buf->queue);
+ spin_unlock_irqrestore(&pxp->lock, flags);
+ pxp->next_queue_ended = 1;
+ return;
+ }
+
+ spin_lock_irqsave(&pxp->lock, flags);
+ buf = list_entry(pxp->nextq.next,
+ struct pxp_buffer,
+ queue);
+ list_del_init(&buf->queue);
+ pxp->active = &buf->vb;
+ pxp->active->state = VIDEOBUF_QUEUED;
+ pxp_buf_next(pxp);
+ }
+
+ spin_unlock_irqrestore(&pxp->lock, flags);
+}
+
+static void pxp_buf_queue(struct videobuf_queue *q,
+ struct videobuf_buffer *vb)
+{
+ struct pxps *pxp = q->priv_data;
+ struct pxp_buffer *buf;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pxp->lock, flags);
+
+ if (list_empty(&pxp->outq)) {
+ list_add_tail(&vb->queue, &pxp->outq);
+ vb->state = VIDEOBUF_QUEUED;
+
+ pxp->active = vb;
+ pxp_buf_next(pxp);
+ } else {
+ list_add_tail(&vb->queue, &pxp->outq);
+
+ buf = container_of(vb, struct pxp_buffer, vb);
+ list_add_tail(&buf->queue, &pxp->nextq);
+ queue_work(pxp->workqueue, &pxp->work);
+ }
+
+ spin_unlock_irqrestore(&pxp->lock, flags);
+}
+
+static void pxp_buf_release(struct videobuf_queue *q,
+ struct videobuf_buffer *vb)
+{
+ pxp_buf_free(q, vb);
+}
+
+static struct videobuf_queue_ops pxp_vbq_ops = {
+ .buf_setup = pxp_buf_setup,
+ .buf_prepare = pxp_buf_prepare,
+ .buf_queue = pxp_buf_queue,
+ .buf_release = pxp_buf_release,
+};
+
+static int pxp_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+ memset(cap, 0, sizeof(*cap));
+ strcpy(cap->driver, "pxp");
+ strcpy(cap->card, "pxp");
+ strlcpy(cap->bus_info, dev_name(&pxp->pdev->dev), sizeof(cap->bus_info));
+
+ cap->version = (PXP_DRIVER_MAJOR << 8) + PXP_DRIVER_MINOR;
+
+ cap->capabilities = V4L2_CAP_VIDEO_OUTPUT |
+ V4L2_CAP_VIDEO_OUTPUT_OVERLAY |
+ V4L2_CAP_STREAMING;
+
+ return 0;
+}
+
+static int pxp_g_fbuf(struct file *file, void *priv,
+ struct v4l2_framebuffer *fb)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+ memset(fb, 0, sizeof(*fb));
+
+ fb->capability = V4L2_FBUF_CAP_EXTERNOVERLAY |
+ V4L2_FBUF_CAP_CHROMAKEY |
+ V4L2_FBUF_CAP_LOCAL_ALPHA |
+ V4L2_FBUF_CAP_GLOBAL_ALPHA;
+
+ if (pxp->global_alpha_state)
+ fb->flags |= V4L2_FBUF_FLAG_GLOBAL_ALPHA;
+ if (pxp->local_alpha_state)
+ fb->flags |= V4L2_FBUF_FLAG_LOCAL_ALPHA;
+ if (pxp->s1_chromakey_state)
+ fb->flags |= V4L2_FBUF_FLAG_CHROMAKEY;
+
+ return 0;
+}
+
+static int pxp_s_fbuf(struct file *file, void *priv,
+ struct v4l2_framebuffer *fb)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+ pxp->overlay_state =
+ (fb->flags & V4L2_FBUF_FLAG_OVERLAY) != 0;
+ pxp->global_alpha_state =
+ (fb->flags & V4L2_FBUF_FLAG_GLOBAL_ALPHA) != 0;
+ pxp->local_alpha_state =
+ (fb->flags & V4L2_FBUF_FLAG_LOCAL_ALPHA) != 0;
+ /* Global alpha overrides local alpha if both are requested */
+ if (pxp->global_alpha_state && pxp->local_alpha_state)
+ pxp->local_alpha_state = 0;
+ pxp->s1_chromakey_state =
+ (fb->flags & V4L2_FBUF_FLAG_CHROMAKEY) != 0;
+
+ pxp_set_olparam(pxp);
+ pxp_set_s0crop(pxp);
+ pxp_set_scaling(pxp);
+
+ return 0;
+}
+
+static int pxp_g_crop(struct file *file, void *fh,
+ struct v4l2_crop *c)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+ if (c->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY)
+ return -EINVAL;
+
+ c->c.left = pxp->drect.left;
+ c->c.top = pxp->drect.top;
+ c->c.width = pxp->drect.width;
+ c->c.height = pxp->drect.height;
+
+ return 0;
+}
+
+static int pxp_s_crop(struct file *file, void *fh,
+ struct v4l2_crop *c)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+ int l = c->c.left;
+ int t = c->c.top;
+ int w = c->c.width;
+ int h = c->c.height;
+ int fbw = pxp->fb.fmt.width;
+ int fbh = pxp->fb.fmt.height;
+
+ if (c->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY)
+ return -EINVAL;
+
+ /* Constrain parameters to FB limits */
+ w = min(w, fbw);
+ w = max(w, PXP_MIN_PIX);
+ h = min(h, fbh);
+ h = max(h, PXP_MIN_PIX);
+ if ((l + w) > fbw)
+ l = 0;
+ if ((t + h) > fbh)
+ t = 0;
+
+ /* Round up values to PxP pixel block */
+ l = roundup(l, PXP_MIN_PIX);
+ t = roundup(t, PXP_MIN_PIX);
+ w = roundup(w, PXP_MIN_PIX);
+ h = roundup(h, PXP_MIN_PIX);
+
+ pxp->drect.left = l;
+ pxp->drect.top = t;
+ pxp->drect.width = w;
+ pxp->drect.height = h;
+
+ pxp_set_s0param(pxp);
+ pxp_set_s0crop(pxp);
+ pxp_set_scaling(pxp);
+
+ return 0;
+}
+
+static int pxp_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(pxp_controls); i++)
+ if (qc->id && qc->id == pxp_controls[i].id) {
+ memcpy(qc, &(pxp_controls[i]), sizeof(*qc));
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int pxp_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *vc)
+{
+ int i;
+
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+ for (i = 0; i < ARRAY_SIZE(pxp_controls); i++)
+ if (vc->id == pxp_controls[i].id)
+ return pxp_get_cstate(pxp, vc);
+
+ return -EINVAL;
+}
+
+static int pxp_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *vc)
+{
+ int i;
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+ for (i = 0; i < ARRAY_SIZE(pxp_controls); i++)
+ if (vc->id == pxp_controls[i].id) {
+ if (vc->value < pxp_controls[i].minimum ||
+ vc->value > pxp_controls[i].maximum)
+ return -ERANGE;
+ return pxp_set_cstate(pxp, vc);
+ }
+
+ return -EINVAL;
+}
+
+void pxp_release(struct video_device *vfd)
+{
+ struct pxps *pxp = video_get_drvdata(vfd);
+
+ spin_lock(&pxp->lock);
+ video_device_release(vfd);
+ spin_unlock(&pxp->lock);
+}
+
+static int pxp_hw_init(struct pxps *pxp)
+{
+ struct fb_var_screeninfo var;
+ struct fb_fix_screeninfo fix;
+ int err;
+
+ err = stmp3xxxfb_get_info(&var, &fix);
+ if (err)
+ return err;
+
+ /* Pull PxP out of reset */
+ __raw_writel(0, HW_PXP_CTRL_ADDR);
+
+ /* Config defaults */
+ pxp->active = NULL;
+
+ pxp->s0_fmt = &pxp_s0_formats[0];
+ pxp->drect.left = pxp->srect.left = 0;
+ pxp->drect.top = pxp->srect.top = 0;
+ pxp->drect.width = pxp->srect.width = pxp->s0_width = var.xres;
+ pxp->drect.height = pxp->srect.height = pxp->s0_height = var.yres;
+ pxp->s0_bgcolor = 0;
+
+ pxp->output = 0;
+ err = pxp_set_fbinfo(pxp);
+ if (err)
+ return err;
+
+ pxp->scaling = 0;
+ pxp->hflip = 0;
+ pxp->vflip = 0;
+ pxp->rotate = 0;
+ pxp->yuv = 0;
+
+ pxp->overlay_state = 0;
+ pxp->global_alpha_state = 0;
+ pxp->global_alpha = 0;
+ pxp->local_alpha_state = 0;
+ pxp->s1_chromakey_state = 0;
+ pxp->s1_chromakey = -1;
+ pxp->s0_chromakey = -1;
+
+ /* Write default h/w config */
+ pxp_set_ctrl(pxp);
+ pxp_set_s0param(pxp);
+ pxp_set_s0crop(pxp);
+ pxp_set_oln(pxp);
+ pxp_set_olparam(pxp);
+ pxp_set_s0colorkey(pxp);
+ pxp_set_s1colorkey(pxp);
+ pxp_set_csc(pxp);
+
+ return 0;
+}
+
+static int pxp_open(struct file *file)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+ int ret = 0;
+
+ mutex_lock(&pxp->mutex);
+ pxp->users++;
+
+ if (pxp->users > 1) {
+ pxp->users--;
+ ret = -EBUSY;
+ goto out;
+ }
+out:
+ mutex_unlock(&pxp->mutex);
+ if (ret)
+ return ret;
+
+ pxp->next_queue_ended = 0;
+ pxp->workqueue = create_singlethread_workqueue("pxp");
+
+ videobuf_queue_dma_contig_init(&pxp->s0_vbq,
+ &pxp_vbq_ops,
+ &pxp->pdev->dev,
+ &pxp->lock,
+ V4L2_BUF_TYPE_VIDEO_OUTPUT,
+ V4L2_FIELD_NONE,
+ sizeof(struct pxp_buffer),
+ pxp);
+
+ return 0;
+}
+
+static int pxp_close(struct file *file)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+ if (pxp->workqueue)
+ destroy_workqueue(pxp->workqueue);
+
+ videobuf_stop(&pxp->s0_vbq);
+ videobuf_mmap_free(&pxp->s0_vbq);
+ pxp->active = NULL;
+
+ mutex_lock(&pxp->mutex);
+ pxp->users--;
+ mutex_unlock(&pxp->mutex);
+
+ return 0;
+}
+
+static int pxp_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+ int ret;
+
+ ret = videobuf_mmap_mapper(&pxp->s0_vbq, vma);
+
+ return ret;
+}
+
+static const struct v4l2_file_operations pxp_fops = {
+ .owner = THIS_MODULE,
+ .open = pxp_open,
+ .release = pxp_close,
+ .ioctl = video_ioctl2,
+ .mmap = pxp_mmap,
+};
+
+static const struct v4l2_ioctl_ops pxp_ioctl_ops = {
+ .vidioc_querycap = pxp_querycap,
+
+ .vidioc_reqbufs = pxp_reqbufs,
+ .vidioc_querybuf = pxp_querybuf,
+ .vidioc_qbuf = pxp_qbuf,
+ .vidioc_dqbuf = pxp_dqbuf,
+
+ .vidioc_streamon = pxp_streamon,
+ .vidioc_streamoff = pxp_streamoff,
+
+ .vidioc_enum_output = pxp_enumoutput,
+ .vidioc_g_output = pxp_g_output,
+ .vidioc_s_output = pxp_s_output,
+
+ .vidioc_enum_fmt_vid_out = pxp_enum_fmt_video_output,
+ .vidioc_try_fmt_vid_out = pxp_try_fmt_video_output,
+ .vidioc_g_fmt_vid_out = pxp_g_fmt_video_output,
+ .vidioc_s_fmt_vid_out = pxp_s_fmt_video_output,
+
+ .vidioc_try_fmt_vid_out_overlay = pxp_try_fmt_output_overlay,
+ .vidioc_g_fmt_vid_out_overlay = pxp_g_fmt_output_overlay,
+ .vidioc_s_fmt_vid_out_overlay = pxp_s_fmt_output_overlay,
+
+ .vidioc_g_fbuf = pxp_g_fbuf,
+ .vidioc_s_fbuf = pxp_s_fbuf,
+
+ .vidioc_g_crop = pxp_g_crop,
+ .vidioc_s_crop = pxp_s_crop,
+
+ .vidioc_queryctrl = pxp_queryctrl,
+ .vidioc_g_ctrl = pxp_g_ctrl,
+ .vidioc_s_ctrl = pxp_s_ctrl,
+};
+
+static const struct video_device pxp_template = {
+ .name = "PxP",
+ .vfl_type = V4L2_CAP_VIDEO_OUTPUT |
+ V4L2_CAP_VIDEO_OVERLAY |
+ V4L2_CAP_STREAMING,
+ .fops = &pxp_fops,
+ .release = pxp_release,
+ .minor = -1,
+ .ioctl_ops = &pxp_ioctl_ops,
+};
+
+static irqreturn_t pxp_irq(int irq, void *dev_id)
+{
+ struct pxps *pxp = (struct pxps *)dev_id;
+ struct videobuf_buffer *vb;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pxp->lock, flags);
+
+ __raw_writel(BM_PXP_STAT_IRQ, HW_PXP_STAT_CLR_ADDR);
+
+ if (list_empty(&pxp->outq)) {
+ pr_warning("irq: outq empty!!!\n");
+ goto out;
+ }
+
+ vb = list_entry(pxp->outq.next,
+ struct videobuf_buffer,
+ queue);
+ list_del_init(&vb->queue);
+
+ vb->state = VIDEOBUF_DONE;
+ do_gettimeofday(&vb->ts);
+ vb->field_count++;
+
+ wake_up(&vb->done);
+ wake_up(&pxp->done);
+out:
+ spin_unlock_irqrestore(&pxp->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static int pxp_notifier_callback(struct notifier_block *self,
+ unsigned long event, void *data)
+{
+ struct pxps *pxp = container_of(self, struct pxps, nb);
+
+ switch (event) {
+ case STMP3XXX_LCDIF_PANEL_INIT:
+ pxp_set_fbinfo(pxp);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int pxp_probe(struct platform_device *pdev)
+{
+ struct pxps *pxp;
+ struct resource *res;
+ int irq;
+ int err = 0;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ irq = platform_get_irq(pdev, 0);
+ if (!res || irq < 0) {
+ err = -ENODEV;
+ goto exit;
+ }
+
+ pxp = kzalloc(sizeof(*pxp), GFP_KERNEL);
+ if (!pxp) {
+ dev_err(&pdev->dev, "failed to allocate control object\n");
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ dev_set_drvdata(&pdev->dev, pxp);
+ pxp->res = res;
+ pxp->irq = irq;
+
+ pxp->regs_virt = dma_alloc_coherent(NULL,
+ PAGE_ALIGN(sizeof(struct pxp_registers)),
+ &pxp->regs_phys, GFP_KERNEL);
+ if (pxp->regs_virt == NULL) {
+ dev_err(&pdev->dev, "failed to allocate pxp_register object\n");
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ init_waitqueue_head(&pxp->done);
+
+ INIT_WORK(&pxp->work, pxp_next_handle);
+ INIT_LIST_HEAD(&pxp->outq);
+ INIT_LIST_HEAD(&pxp->nextq);
+ spin_lock_init(&pxp->lock);
+ mutex_init(&pxp->mutex);
+
+ if (!request_mem_region(res->start, res->end - res->start + 1,
+ PXP_DRIVER_NAME)) {
+ err = -EBUSY;
+ goto freepxp;
+ }
+
+ pxp->regs = (void __iomem *)res->start; /* it is already ioremapped */
+ pxp->pdev = pdev;
+
+ err = request_irq(pxp->irq, pxp_irq, 0, PXP_DRIVER_NAME, pxp);
+
+ if (err) {
+ dev_err(&pdev->dev, "interrupt register failed\n");
+ goto release;
+ }
+
+ pxp->vdev = video_device_alloc();
+ if (!pxp->vdev) {
+ dev_err(&pdev->dev, "video_device_alloc() failed\n");
+ err = -ENOMEM;
+ goto freeirq;
+ }
+
+ memcpy(pxp->vdev, &pxp_template, sizeof(pxp_template));
+ video_set_drvdata(pxp->vdev, pxp);
+
+ err = video_register_device(pxp->vdev, VFL_TYPE_GRABBER, 0);
+ if (err) {
+ dev_err(&pdev->dev, "failed to register video device\n");
+ goto freevdev;
+ }
+
+ err = pxp_hw_init(pxp);
+ if (err) {
+ dev_err(&pdev->dev, "failed to initialize hardware\n");
+ goto freevdev;
+ }
+
+ pxp->nb.notifier_call = pxp_notifier_callback,
+ stmp3xxx_lcdif_register_client(&pxp->nb);
+ dev_info(&pdev->dev, "initialized\n");
+
+exit:
+ return err;
+
+freevdev:
+ video_device_release(pxp->vdev);
+
+freeirq:
+ free_irq(pxp->irq, pxp);
+
+release:
+ release_mem_region(res->start, res->end - res->start + 1);
+
+freepxp:
+ kfree(pxp);
+
+ return err;
+}
+
+static int __devexit pxp_remove(struct platform_device *pdev)
+{
+ struct pxps *pxp = platform_get_drvdata(pdev);
+
+ stmp3xxx_lcdif_unregister_client(&pxp->nb);
+ video_unregister_device(pxp->vdev);
+ video_device_release(pxp->vdev);
+
+ if (pxp->regs_virt)
+ dma_free_coherent(0, PAGE_ALIGN(sizeof(struct pxp_registers)),
+ pxp->regs_virt, pxp->regs_phys);
+ kfree(pxp->outb);
+ kfree(pxp);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int pxp_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ int i;
+
+ while (__raw_readl(HW_PXP_CTRL_ADDR) & BM_PXP_CTRL_ENABLE)
+ ;
+
+ for (i = 0; i < REGS1_NUMS; i++)
+ regs1[i] = __raw_readl(HW_PXP_CTRL_ADDR + REG_OFFSET * i);
+
+ for (i = 0; i < REGS2_NUMS; i++)
+ regs2[i] = __raw_readl(HW_PXP_PAGETABLE_ADDR + REG_OFFSET * i);
+
+ for (i = 0; i < REGS3_NUMS; i++)
+ regs3[i] = __raw_readl(HW_PXP_OLn_ADDR(0) + REG_OFFSET * i);
+
+ __raw_writel(BM_PXP_CTRL_SFTRST, HW_PXP_CTRL_ADDR);
+
+ return 0;
+}
+
+static int pxp_resume(struct platform_device *pdev)
+{
+ int i;
+
+ /* Pull PxP out of reset */
+ __raw_writel(0, HW_PXP_CTRL_ADDR);
+
+ for (i = 0; i < REGS1_NUMS; i++)
+ __raw_writel(regs1[i], HW_PXP_CTRL_ADDR + REG_OFFSET * i);
+
+ for (i = 0; i < REGS2_NUMS; i++)
+ __raw_writel(regs2[i], HW_PXP_PAGETABLE_ADDR + REG_OFFSET * i);
+
+ for (i = 0; i < REGS3_NUMS; i++)
+ __raw_writel(regs3[i], HW_PXP_OLn_ADDR(0) + REG_OFFSET * i);
+
+ return 0;
+}
+#else
+#define pxp_suspend NULL
+#define pxp_resume NULL
+#endif
+
+static struct platform_driver pxp_driver = {
+ .driver = {
+ .name = PXP_DRIVER_NAME,
+ },
+ .probe = pxp_probe,
+ .remove = __exit_p(pxp_remove),
+ .suspend = pxp_suspend,
+ .resume = pxp_resume,
+};
+
+
+static int __devinit pxp_init(void)
+{
+ return platform_driver_register(&pxp_driver);
+}
+
+static void __exit pxp_exit(void)
+{
+ platform_driver_unregister(&pxp_driver);
+}
+
+module_init(pxp_init);
+module_exit(pxp_exit);
+
+MODULE_DESCRIPTION("STMP37xx PxP driver");
+MODULE_AUTHOR("Matt Porter <mporter@embeddedalley.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/pxp.h b/drivers/media/video/pxp.h
new file mode 100644
index 000000000000..11b41dcc6ade
--- /dev/null
+++ b/drivers/media/video/pxp.h
@@ -0,0 +1,130 @@
+/*
+ * Freescale STMP378X PxP driver
+ *
+ * Author: Matt Porter <mporter@embeddedalley.com>
+ *
+ * Copyright 2008-2010 Freescale Semiconductor, Inc.
+ * Copyright 2008-2009 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+struct pxp_overlay_registers {
+ u32 ol;
+ u32 olsize;
+ u32 olparam;
+ u32 olparam2;
+};
+
+/* Registers feed for PXP_NEXT */
+struct pxp_registers {
+ u32 ctrl;
+ u32 rgbbuf;
+ u32 rgbbuf2;
+ u32 rgbsize;
+ u32 s0buf;
+ u32 s0ubuf;
+ u32 s0vbuf;
+ u32 s0param;
+ u32 s0background;
+ u32 s0crop;
+ u32 s0scale;
+ u32 s0offset;
+ u32 s0colorkeylow;
+ u32 s0colorkeyhigh;
+ u32 olcolorkeylow;
+ u32 olcolorkeyhigh;
+
+ struct pxp_overlay_registers ol0;
+ struct pxp_overlay_registers ol1;
+ struct pxp_overlay_registers ol2;
+ struct pxp_overlay_registers ol3;
+ struct pxp_overlay_registers ol4;
+ struct pxp_overlay_registers ol5;
+ struct pxp_overlay_registers ol6;
+ struct pxp_overlay_registers ol7;
+};
+
+struct pxp_buffer {
+ /* Must be first! */
+ struct videobuf_buffer vb;
+ struct list_head queue;
+};
+
+struct pxps {
+ struct platform_device *pdev;
+ struct resource *res;
+ int irq;
+ void __iomem *regs;
+
+ struct work_struct work;
+ struct workqueue_struct *workqueue;
+ spinlock_t lock;
+ struct mutex mutex;
+ int users;
+
+ struct video_device *vdev;
+
+ struct videobuf_queue s0_vbq;
+ struct videobuf_buffer *active;
+ struct list_head outq;
+ struct list_head nextq;
+
+ int output;
+ u32 *outb;
+ dma_addr_t outb_phys;
+
+ /* Current S0 configuration */
+ struct pxp_data_format *s0_fmt;
+ u32 s0_width;
+ u32 s0_height;
+ u32 s0_bgcolor;
+ u32 s0_chromakey;
+
+ struct v4l2_framebuffer fb;
+ struct v4l2_rect drect;
+ struct v4l2_rect srect;
+
+ /* Transformation support */
+ int scaling;
+ int hflip;
+ int vflip;
+ int rotate;
+ int yuv;
+
+ /* Output overlay support */
+ int overlay_state;
+ int global_alpha_state;
+ u8 global_alpha;
+ int local_alpha_state;
+ int s1_chromakey_state;
+ u32 s1_chromakey;
+
+ /* PXP_NEXT */
+ u32 regs_phys;
+ struct pxp_registers *regs_virt;
+ wait_queue_head_t done;
+ int next_queue_ended;
+
+ /* notifier for PXP when fb changed */
+ struct notifier_block nb;
+};
+
+struct pxp_data_format {
+ char *name;
+ unsigned int bpp;
+ u32 fourcc;
+ enum v4l2_colorspace colorspace;
+ u32 ctrl_s0_fmt;
+};
+
+extern int stmp3xxxfb_get_info(struct fb_var_screeninfo *var,
+ struct fb_fix_screeninfo *fix);
+extern void stmp3xxxfb_cfg_pxp(int enable, dma_addr_t pxp_phys);
diff --git a/drivers/media/video/videobuf-dma-contig.c b/drivers/media/video/videobuf-dma-contig.c
index d09ce83a9429..dc4f32bbd83d 100644
--- a/drivers/media/video/videobuf-dma-contig.c
+++ b/drivers/media/video/videobuf-dma-contig.c
@@ -319,7 +319,7 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q,
mem->size = PAGE_ALIGN(q->bufs[first]->bsize);
mem->vaddr = dma_alloc_coherent(q->dev, mem->size,
- &mem->dma_handle, GFP_KERNEL);
+ &mem->dma_handle, GFP_DMA);
if (!mem->vaddr) {
dev_err(q->dev, "dma_alloc_coherent size %ld failed\n",
mem->size);
@@ -333,7 +333,7 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q,
size = vma->vm_end - vma->vm_start;
size = (size < mem->size) ? size : mem->size;
- vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ vma->vm_page_prot = pgprot_writethru(vma->vm_page_prot);
retval = remap_pfn_range(vma, vma->vm_start,
mem->dma_handle >> PAGE_SHIFT,
size, vma->vm_page_prot);