CONFIG_V4L_PLATFORM_DRIVERS=y
CONFIG_VIDEO_MXC_OUTPUT=y
CONFIG_VIDEO_MXC_CAPTURE=m
+CONFIG_VIDEO_MXC_CSI_CAMERA=m
+CONFIG_MXC_VADC=m
+CONFIG_MXC_MIPI_CSI=m
CONFIG_MXC_CAMERA_OV5640=m
+CONFIG_MXC_CAMERA_OV5640_V2=m
CONFIG_MXC_CAMERA_OV5642=m
CONFIG_MXC_CAMERA_OV5640_MIPI=m
+CONFIG_MXC_CAMERA_OV5640_MIPI_V2=m
+CONFIG_MXC_CAMERA_OV5647_MIPI=m
CONFIG_MXC_TVIN_ADV7180=m
CONFIG_MXC_IPU_DEVICE_QUEUE_SDC=m
CONFIG_VIDEO_MXC_IPU_OUTPUT=y
CONFIG_VIDEO_MXC_PXP_V4L2=y
-CONFIG_VIDEO_MXC_CSI_CAMERA=m
-CONFIG_MXC_VADC=m
-CONFIG_MXC_MIPI_CSI=m
-CONFIG_MXC_CAMERA_OV5647_MIPI=m
CONFIG_SOC_CAMERA=y
CONFIG_V4L_MEM2MEM_DRIVERS=y
CONFIG_VIDEO_CODA=y
source "drivers/media/platform/imx8/Kconfig"
source "drivers/media/platform/mxc/capture/Kconfig"
source "drivers/media/platform/mxc/output/Kconfig"
-source "drivers/media/platform/mxc/subdev/Kconfig"
source "drivers/media/platform/soc_camera/Kconfig"
source "drivers/media/platform/exynos4-is/Kconfig"
source "drivers/media/platform/am437x/Kconfig"
obj-$(CONFIG_VIDEO_MXC_OUTPUT) += mxc/output/
obj-$(CONFIG_VIDEO_MXC_CAPTURE) += mxc/capture/
-obj-$(CONFIG_VIDEO_MXC_CAPTURE) += mxc/subdev/
obj-$(CONFIG_VIDEO_MXC_OUTPUT) += mxc/output/
obj-$(CONFIG_VIDEO_MX8_CAPTURE) += imx8/
if VIDEO_MXC_CAPTURE
+
config VIDEO_V4L2_MXC_INT_DEVICE
tristate
+config VIDEO_MXC_CSI_CAMERA
+ tristate "CSI camera support"
+ depends on VIDEO_MXC_CAPTURE && VIDEO_V4L2
+ ---help---
+ This is the video4linux2 capture driver based on CSI module.
+
+config MXC_VADC
+ tristate "mxc VADC support"
+ depends on VIDEO_MXC_CAPTURE && VIDEO_V4L2
+ ---help---
+ If you plan to use the VADC with your MXC system, say Y here.
+
+config MXC_MIPI_CSI
+ tristate "mxc mipi csi driver"
+ depends on VIDEO_MXC_CAPTURE && VIDEO_V4L2
+ ---help---
+ This is a V4L2 driver for i.MX7D SoC MIPI-CSI2 receiver devices.
+
menu "MXC Camera/V4L2 PRP Features support"
config VIDEO_MXC_IPU_CAMERA
bool
---help---
If you plan to use the ov5640 Camera with your MXC system, say Y here.
+config MXC_CAMERA_OV5640_V2
+ tristate "OmniVision ov5640 camera support"
+ depends on VIDEO_MXC_CAPTURE && I2C
+ ---help---
+ If you plan to use the ov5640 Camera with your MXC system, say Y here.
+
config MXC_CAMERA_OV5642
tristate "OmniVision ov5642 camera support"
depends on !VIDEO_MXC_EMMA_CAMERA && I2C
---help---
If you plan to use the ov5640 Camera with mipi interface in your MXC system, say Y here.
+config MXC_CAMERA_OV5640_MIPI_V2
+ tristate "OmniVision ov5640 camera support using mipi"
+ depends on MXC_MIPI_CSI && I2C
+ ---help---
+ If you plan to use the ov5640 Camera with mipi interface in your MXC system, say Y here.
+
+config MXC_CAMERA_OV5647_MIPI
+ tristate "OmniVision ov5647 camera support using mipi"
+ depends on MXC_MIPI_CSI && I2C
+ ---help---
+ If you plan to use the ov5647 Camera with mipi interface in your MXC system, say Y here.
+
config MXC_TVIN_ADV7180
tristate "Analog Device adv7180 TV Decoder Input support"
depends on !VIDEO_MXC_EMMA_CAMERA && I2C
obj-$(CONFIG_MXC_IPU_CSI_ENC) += ipu_csi_enc.o ipu_still.o
endif
+obj-$(CONFIG_VIDEO_MXC_CSI_CAMERA) += mx6s_capture.o
+obj-$(CONFIG_MXC_VADC) += mxc_vadc.o
+obj-$(CONFIG_MXC_MIPI_CSI) += mxc_mipi_csi.o
+
+# Used for iMX 6QDL
ov5640_camera_int-objs := ov5640.o
obj-$(CONFIG_MXC_CAMERA_OV5640) += ov5640_camera_int.o
+# Used for iMX 6UL/ULL/SX/SL/SLL
+ov5640_camera_v2-objs := ov5640_v2.o
+obj-$(CONFIG_MXC_CAMERA_OV5640_V2) += ov5640_camera_v2.o
+
ov5642_camera-objs := ov5642.o
obj-$(CONFIG_MXC_CAMERA_OV5642) += ov5642_camera.o
+# Used for iMX 6QDL/DQSCM
ov5640_camera_mipi_int-objs := ov5640_mipi.o
obj-$(CONFIG_MXC_CAMERA_OV5640_MIPI) += ov5640_camera_mipi_int.o
+# Used for iMX 7D
+ov5640_camera_mipi_v2-objs := ov5640_mipi_v2.o
+obj-$(CONFIG_MXC_CAMERA_OV5640_MIPI_V2) += ov5640_camera_mipi_v2.o
+
+ov5647_camera_mipi-objs := ov5647_mipi.o
+obj-$(CONFIG_MXC_CAMERA_OV5647_MIPI) += ov5647_camera_mipi.o
+
adv7180_tvin-objs := adv7180.o
obj-$(CONFIG_MXC_TVIN_ADV7180) += adv7180_tvin.o
--- /dev/null
+/*
+ * Copyright (C) 2014-2016 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 mx6s_csi.c
+ *
+ * @brief mx6sx CMOS Sensor interface functions
+ *
+ * @ingroup CSI
+ */
+#include <asm/dma.h>
+#include <linux/busfreq-imx.h>
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/gcd.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/math64.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/media-bus-format.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-of.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+
+#define MX6S_CAM_DRV_NAME "mx6s-csi"
+#define MX6S_CAM_VERSION "0.0.1"
+#define MX6S_CAM_DRIVER_DESCRIPTION "i.MX6S_CSI"
+
+#define MAX_VIDEO_MEM 64
+
+/* 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
+#define SHIFT_RXFIFO_LEVEL 4
+
+/* csi status reg */
+#define BIT_ADDR_CH_ERR_INT (0x1 << 28)
+#define BIT_FIELD0_INT (0x1 << 27)
+#define BIT_FIELD1_INT (0x1 << 26)
+#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)
+
+/* csi control reg 18 */
+#define BIT_CSI_ENABLE (0x1 << 31)
+#define BIT_MIPI_DATA_FORMAT_RAW8 (0x2a << 25)
+#define BIT_MIPI_DATA_FORMAT_RAW10 (0x2b << 25)
+#define BIT_MIPI_DATA_FORMAT_YUV422_8B (0x1e << 25)
+#define BIT_MIPI_DATA_FORMAT_MASK (0x3F << 25)
+#define BIT_MIPI_DATA_FORMAT_OFFSET 25
+#define BIT_DATA_FROM_MIPI (0x1 << 22)
+#define BIT_MIPI_YU_SWAP (0x1 << 21)
+#define BIT_MIPI_DOUBLE_CMPNT (0x1 << 20)
+#define BIT_BASEADDR_CHG_ERR_EN (0x1 << 9)
+#define BIT_BASEADDR_SWITCH_SEL (0x1 << 5)
+#define BIT_BASEADDR_SWITCH_EN (0x1 << 4)
+#define BIT_PARALLEL24_EN (0x1 << 3)
+#define BIT_DEINTERLACE_EN (0x1 << 2)
+#define BIT_TVDECODER_IN_EN (0x1 << 1)
+#define BIT_NTSC_EN (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 0x0
+#define CSI_CSICR2 0x4
+#define CSI_CSICR3 0x8
+#define CSI_STATFIFO 0xC
+#define CSI_CSIRXFIFO 0x10
+#define CSI_CSIRXCNT 0x14
+#define CSI_CSISR 0x18
+
+#define CSI_CSIDBG 0x1C
+#define CSI_CSIDMASA_STATFIFO 0x20
+#define CSI_CSIDMATS_STATFIFO 0x24
+#define CSI_CSIDMASA_FB1 0x28
+#define CSI_CSIDMASA_FB2 0x2C
+#define CSI_CSIFBUF_PARA 0x30
+#define CSI_CSIIMAG_PARA 0x34
+
+#define CSI_CSICR18 0x48
+#define CSI_CSICR19 0x4c
+
+#define NUM_FORMATS ARRAY_SIZE(formats)
+#define MX6SX_MAX_SENSORS 1
+
+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;
+};
+
+/*
+ * Basic structures
+ */
+struct mx6s_fmt {
+ char name[32];
+ u32 fourcc; /* v4l2 format id */
+ u32 pixelformat;
+ u32 mbus_code;
+ int bpp;
+};
+
+static struct mx6s_fmt formats[] = {
+ {
+ .name = "UYVY-16",
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .pixelformat = V4L2_PIX_FMT_UYVY,
+ .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8,
+ .bpp = 2,
+ }, {
+ .name = "YUYV-16",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .pixelformat = V4L2_PIX_FMT_YUYV,
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
+ .bpp = 2,
+ }, {
+ .name = "YUV32 (X-Y-U-V)",
+ .fourcc = V4L2_PIX_FMT_YUV32,
+ .pixelformat = V4L2_PIX_FMT_YUV32,
+ .mbus_code = MEDIA_BUS_FMT_AYUV8_1X32,
+ .bpp = 4,
+ }, {
+ .name = "RAWRGB8 (SBGGR8)",
+ .fourcc = V4L2_PIX_FMT_SBGGR8,
+ .pixelformat = V4L2_PIX_FMT_SBGGR8,
+ .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .bpp = 1,
+ }
+};
+
+struct mx6s_buf_internal {
+ struct list_head queue;
+ int bufnum;
+ bool discard;
+};
+
+/* buffer for one video frame */
+struct mx6s_buffer {
+ /* common v4l buffer stuff -- must be first */
+ struct vb2_v4l2_buffer vb;
+ struct mx6s_buf_internal internal;
+};
+
+struct mx6s_csi_mux {
+ struct regmap *gpr;
+ u8 req_gpr;
+ u8 req_bit;
+};
+
+struct mx6s_csi_dev {
+ struct device *dev;
+ struct video_device *vdev;
+ struct v4l2_subdev *sd;
+ struct v4l2_device v4l2_dev;
+
+ struct vb2_queue vb2_vidq;
+ struct v4l2_ctrl_handler ctrl_handler;
+
+ struct mutex lock;
+ spinlock_t slock;
+
+ /* clock */
+ struct clk *clk_disp_axi;
+ struct clk *clk_disp_dcic;
+ struct clk *clk_csi_mclk;
+
+ void __iomem *regbase;
+ int irq;
+
+ u32 type;
+ u32 bytesperline;
+ v4l2_std_id std;
+ struct mx6s_fmt *fmt;
+ struct v4l2_pix_format pix;
+ u32 mbus_code;
+
+ unsigned int frame_count;
+
+ struct list_head capture;
+ struct list_head active_bufs;
+ struct list_head discard;
+
+ void *discard_buffer;
+ dma_addr_t discard_buffer_dma;
+ size_t discard_size;
+ struct mx6s_buf_internal buf_discard[2];
+
+ struct v4l2_async_subdev asd;
+ struct v4l2_async_notifier subdev_notifier;
+ struct v4l2_async_subdev *async_subdevs[2];
+
+ bool csi_mux_mipi;
+ const bool *rx_fifo_rst;
+ struct mx6s_csi_mux csi_mux;
+};
+
+static const struct of_device_id mx6s_csi_dt_ids[];
+
+static inline int csi_read(struct mx6s_csi_dev *csi, unsigned int offset)
+{
+ return __raw_readl(csi->regbase + offset);
+}
+static inline void csi_write(struct mx6s_csi_dev *csi, unsigned int value,
+ unsigned int offset)
+{
+ __raw_writel(value, csi->regbase + offset);
+}
+
+static inline struct mx6s_csi_dev
+ *notifier_to_mx6s_dev(struct v4l2_async_notifier *n)
+{
+ return container_of(n, struct mx6s_csi_dev, subdev_notifier);
+}
+
+struct mx6s_fmt *format_by_fourcc(int fourcc)
+{
+ int i;
+
+ for (i = 0; i < NUM_FORMATS; i++) {
+ if (formats[i].pixelformat == fourcc)
+ return formats + i;
+ }
+
+ pr_err("unknown pixelformat:'%4.4s'\n", (char *)&fourcc);
+ return NULL;
+}
+
+struct mx6s_fmt *format_by_mbus(u32 code)
+{
+ int i;
+
+ for (i = 0; i < NUM_FORMATS; i++) {
+ if (formats[i].mbus_code == code)
+ return formats + i;
+ }
+
+ pr_err("unknown mbus:0x%x\n", code);
+ return NULL;
+}
+
+static struct mx6s_buffer *mx6s_ibuf_to_buf(struct mx6s_buf_internal *int_buf)
+{
+ return container_of(int_buf, struct mx6s_buffer, internal);
+}
+
+void csi_clk_enable(struct mx6s_csi_dev *csi_dev)
+{
+ clk_prepare_enable(csi_dev->clk_disp_axi);
+ clk_prepare_enable(csi_dev->clk_disp_dcic);
+ clk_prepare_enable(csi_dev->clk_csi_mclk);
+}
+
+void csi_clk_disable(struct mx6s_csi_dev *csi_dev)
+{
+ clk_disable_unprepare(csi_dev->clk_csi_mclk);
+ clk_disable_unprepare(csi_dev->clk_disp_dcic);
+ clk_disable_unprepare(csi_dev->clk_disp_axi);
+}
+
+static void csihw_reset(struct mx6s_csi_dev *csi_dev)
+{
+ __raw_writel(__raw_readl(csi_dev->regbase + CSI_CSICR3)
+ | BIT_FRMCNT_RST,
+ csi_dev->regbase + CSI_CSICR3);
+
+ __raw_writel(CSICR1_RESET_VAL, csi_dev->regbase + CSI_CSICR1);
+ __raw_writel(CSICR2_RESET_VAL, csi_dev->regbase + CSI_CSICR2);
+ __raw_writel(CSICR3_RESET_VAL, csi_dev->regbase + CSI_CSICR3);
+}
+
+static void csisw_reset(struct mx6s_csi_dev *csi_dev)
+{
+ int cr1, cr3, cr18, isr;
+
+ /* Disable csi */
+ cr18 = csi_read(csi_dev, CSI_CSICR18);
+ cr18 &= ~BIT_CSI_ENABLE;
+ csi_write(csi_dev, cr18, CSI_CSICR18);
+
+ /* Clear RX FIFO */
+ cr1 = csi_read(csi_dev, CSI_CSICR1);
+ csi_write(csi_dev, cr1 & ~BIT_FCC, CSI_CSICR1);
+ cr1 = csi_read(csi_dev, CSI_CSICR1);
+ csi_write(csi_dev, cr1 | BIT_CLR_RXFIFO, CSI_CSICR1);
+
+ /* DMA reflash */
+ cr3 = csi_read(csi_dev, CSI_CSICR3);
+ cr3 |= BIT_DMA_REFLASH_RFF | BIT_FRMCNT_RST;
+ csi_write(csi_dev, cr3, CSI_CSICR3);
+
+ msleep(2);
+
+ cr1 = csi_read(csi_dev, CSI_CSICR1);
+ csi_write(csi_dev, cr1 | BIT_FCC, CSI_CSICR1);
+
+ isr = csi_read(csi_dev, CSI_CSISR);
+ csi_write(csi_dev, isr, CSI_CSISR);
+
+ /* Ensable csi */
+ cr18 |= BIT_CSI_ENABLE;
+ csi_write(csi_dev, cr18, CSI_CSICR18);
+}
+
+/*!
+ * csi_init_interface
+ * Init csi interface
+ */
+static void csi_init_interface(struct mx6s_csi_dev *csi_dev)
+{
+ 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_FCC;
+ val |= 1 << SHIFT_MCLKDIV;
+ val |= BIT_MCLKEN;
+ __raw_writel(val, csi_dev->regbase + CSI_CSICR1);
+
+ imag_para = (640 << 16) | 960;
+ __raw_writel(imag_para, csi_dev->regbase + CSI_CSIIMAG_PARA);
+
+ val = BIT_DMA_REFLASH_RFF;
+ __raw_writel(val, csi_dev->regbase + CSI_CSICR3);
+}
+
+static void csi_enable_int(struct mx6s_csi_dev *csi_dev, int arg)
+{
+ unsigned long cr1 = __raw_readl(csi_dev->regbase + CSI_CSICR1);
+
+ cr1 |= BIT_SOF_INTEN;
+ cr1 |= BIT_RFF_OR_INT;
+ if (arg == 1) {
+ /* still capture needs DMA intterrupt */
+ cr1 |= BIT_FB1_DMA_DONE_INTEN;
+ cr1 |= BIT_FB2_DMA_DONE_INTEN;
+ }
+ __raw_writel(cr1, csi_dev->regbase + CSI_CSICR1);
+}
+
+static void csi_disable_int(struct mx6s_csi_dev *csi_dev)
+{
+ unsigned long cr1 = __raw_readl(csi_dev->regbase + CSI_CSICR1);
+
+ cr1 &= ~BIT_SOF_INTEN;
+ cr1 &= ~BIT_RFF_OR_INT;
+ cr1 &= ~BIT_FB1_DMA_DONE_INTEN;
+ cr1 &= ~BIT_FB2_DMA_DONE_INTEN;
+ __raw_writel(cr1, csi_dev->regbase + CSI_CSICR1);
+}
+
+static void csi_enable(struct mx6s_csi_dev *csi_dev, int arg)
+{
+ unsigned long cr = __raw_readl(csi_dev->regbase + CSI_CSICR18);
+
+ if (arg == 1)
+ cr |= BIT_CSI_ENABLE;
+ else
+ cr &= ~BIT_CSI_ENABLE;
+ __raw_writel(cr, csi_dev->regbase + CSI_CSICR18);
+}
+
+static void csi_buf_stride_set(struct mx6s_csi_dev *csi_dev, u32 stride)
+{
+ __raw_writel(stride, csi_dev->regbase + CSI_CSIFBUF_PARA);
+}
+
+static void csi_deinterlace_enable(struct mx6s_csi_dev *csi_dev, bool enable)
+{
+ unsigned long cr18 = __raw_readl(csi_dev->regbase + CSI_CSICR18);
+
+ if (enable == true)
+ cr18 |= BIT_DEINTERLACE_EN;
+ else
+ cr18 &= ~BIT_DEINTERLACE_EN;
+
+ __raw_writel(cr18, csi_dev->regbase + CSI_CSICR18);
+}
+
+static void csi_deinterlace_mode(struct mx6s_csi_dev *csi_dev, int mode)
+{
+ unsigned long cr18 = __raw_readl(csi_dev->regbase + CSI_CSICR18);
+
+ if (mode == V4L2_STD_NTSC)
+ cr18 |= BIT_NTSC_EN;
+ else
+ cr18 &= ~BIT_NTSC_EN;
+
+ __raw_writel(cr18, csi_dev->regbase + CSI_CSICR18);
+}
+
+static void csi_tvdec_enable(struct mx6s_csi_dev *csi_dev, bool enable)
+{
+ unsigned long cr18 = __raw_readl(csi_dev->regbase + CSI_CSICR18);
+ unsigned long cr1 = __raw_readl(csi_dev->regbase + CSI_CSICR1);
+
+ if (enable == true) {
+ cr18 |= (BIT_TVDECODER_IN_EN |
+ BIT_BASEADDR_SWITCH_EN |
+ BIT_BASEADDR_SWITCH_SEL |
+ BIT_BASEADDR_CHG_ERR_EN);
+ cr1 |= BIT_CCIR_MODE;
+ cr1 &= ~(BIT_SOF_POL | BIT_REDGE);
+ } else {
+ cr18 &= ~(BIT_TVDECODER_IN_EN |
+ BIT_BASEADDR_SWITCH_EN |
+ BIT_BASEADDR_SWITCH_SEL |
+ BIT_BASEADDR_CHG_ERR_EN);
+ cr1 &= ~BIT_CCIR_MODE;
+ cr1 |= BIT_SOF_POL | BIT_REDGE;
+ }
+
+ __raw_writel(cr18, csi_dev->regbase + CSI_CSICR18);
+ __raw_writel(cr1, csi_dev->regbase + CSI_CSICR1);
+}
+
+static void csi_dmareq_rff_enable(struct mx6s_csi_dev *csi_dev)
+{
+ unsigned long cr3 = __raw_readl(csi_dev->regbase + CSI_CSICR3);
+ unsigned long cr2 = __raw_readl(csi_dev->regbase + CSI_CSICR2);
+
+ /* Burst Type of DMA Transfer from RxFIFO. INCR16 */
+ cr2 |= 0xC0000000;
+
+ cr3 |= BIT_DMA_REQ_EN_RFF;
+ cr3 |= BIT_HRESP_ERR_EN;
+ cr3 &= ~BIT_RXFF_LEVEL;
+ cr3 |= 0x2 << 4;
+
+ __raw_writel(cr3, csi_dev->regbase + CSI_CSICR3);
+ __raw_writel(cr2, csi_dev->regbase + CSI_CSICR2);
+}
+
+static void csi_dmareq_rff_disable(struct mx6s_csi_dev *csi_dev)
+{
+ unsigned long cr3 = __raw_readl(csi_dev->regbase + CSI_CSICR3);
+
+ cr3 &= ~BIT_DMA_REQ_EN_RFF;
+ cr3 &= ~BIT_HRESP_ERR_EN;
+ __raw_writel(cr3, csi_dev->regbase + CSI_CSICR3);
+}
+
+static void csi_set_imagpara(struct mx6s_csi_dev *csi,
+ int width, int height)
+{
+ int imag_para = 0;
+ unsigned long cr3 = __raw_readl(csi->regbase + CSI_CSICR3);
+
+ imag_para = (width << 16) | height;
+ __raw_writel(imag_para, csi->regbase + CSI_CSIIMAG_PARA);
+
+ /* reflash the embeded DMA controller */
+ __raw_writel(cr3 | BIT_DMA_REFLASH_RFF, csi->regbase + CSI_CSICR3);
+}
+
+static void csi_error_recovery(struct mx6s_csi_dev *csi_dev)
+{
+ u32 cr1, cr3, cr18;
+ /* software reset */
+
+ /* Disable csi */
+ cr18 = csi_read(csi_dev, CSI_CSICR18);
+ cr18 &= ~BIT_CSI_ENABLE;
+ csi_write(csi_dev, cr18, CSI_CSICR18);
+
+ /* Clear RX FIFO */
+ cr1 = csi_read(csi_dev, CSI_CSICR1);
+ csi_write(csi_dev, cr1 & ~BIT_FCC, CSI_CSICR1);
+ cr1 = csi_read(csi_dev, CSI_CSICR1);
+ csi_write(csi_dev, cr1 | BIT_CLR_RXFIFO, CSI_CSICR1);
+
+ cr1 = csi_read(csi_dev, CSI_CSICR1);
+ csi_write(csi_dev, cr1 | BIT_FCC, CSI_CSICR1);
+
+ /* DMA reflash */
+ cr3 = csi_read(csi_dev, CSI_CSICR3);
+ cr3 |= BIT_DMA_REFLASH_RFF;
+ csi_write(csi_dev, cr3, CSI_CSICR3);
+
+ /* Ensable csi */
+ cr18 |= BIT_CSI_ENABLE;
+ csi_write(csi_dev, cr18, CSI_CSICR18);
+}
+
+/*
+ * Videobuf operations
+ */
+static int mx6s_videobuf_setup(struct vb2_queue *vq,
+ unsigned int *count, unsigned int *num_planes,
+ unsigned int sizes[], struct device *alloc_devs[])
+{
+ struct mx6s_csi_dev *csi_dev = vb2_get_drv_priv(vq);
+
+ dev_dbg(csi_dev->dev, "count=%d, size=%d\n", *count, sizes[0]);
+
+ alloc_devs[0] = csi_dev->dev;
+
+ sizes[0] = csi_dev->pix.sizeimage;
+
+ pr_debug("size=%d\n", sizes[0]);
+ if (0 == *count)
+ *count = 32;
+ if (!*num_planes &&
+ sizes[0] * *count > MAX_VIDEO_MEM * 1024 * 1024)
+ *count = (MAX_VIDEO_MEM * 1024 * 1024) / sizes[0];
+
+ *num_planes = 1;
+
+ return 0;
+}
+
+static int mx6s_videobuf_prepare(struct vb2_buffer *vb)
+{
+ struct mx6s_csi_dev *csi_dev = vb2_get_drv_priv(vb->vb2_queue);
+ int ret = 0;
+
+ dev_dbg(csi_dev->dev, "%s (vb=0x%p) 0x%p %lu\n", __func__,
+ vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
+
+#ifdef DEBUG
+ /*
+ * This can be useful if you want to see if we actually fill
+ * the buffer with something
+ */
+ if (vb2_plane_vaddr(vb, 0))
+ memset((void *)vb2_plane_vaddr(vb, 0),
+ 0xaa, vb2_get_plane_payload(vb, 0));
+#endif
+
+ vb2_set_plane_payload(vb, 0, csi_dev->pix.sizeimage);
+ if (vb2_plane_vaddr(vb, 0) &&
+ vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ return 0;
+
+out:
+ return ret;
+}
+
+static void mx6s_videobuf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct mx6s_csi_dev *csi_dev = vb2_get_drv_priv(vb->vb2_queue);
+ struct mx6s_buffer *buf = container_of(vbuf, struct mx6s_buffer, vb);
+ unsigned long flags;
+
+ dev_dbg(csi_dev->dev, "%s (vb=0x%p) 0x%p %lu\n", __func__,
+ vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
+
+ spin_lock_irqsave(&csi_dev->slock, flags);
+
+ list_add_tail(&buf->internal.queue, &csi_dev->capture);
+
+ spin_unlock_irqrestore(&csi_dev->slock, flags);
+}
+
+static void mx6s_update_csi_buf(struct mx6s_csi_dev *csi_dev,
+ unsigned long phys, int bufnum)
+{
+ if (bufnum == 1)
+ csi_write(csi_dev, phys, CSI_CSIDMASA_FB2);
+ else
+ csi_write(csi_dev, phys, CSI_CSIDMASA_FB1);
+}
+
+static void mx6s_csi_init(struct mx6s_csi_dev *csi_dev)
+{
+ csi_clk_enable(csi_dev);
+ csihw_reset(csi_dev);
+ csi_init_interface(csi_dev);
+ csi_dmareq_rff_disable(csi_dev);
+}
+
+static void mx6s_csi_deinit(struct mx6s_csi_dev *csi_dev)
+{
+ csihw_reset(csi_dev);
+ csi_init_interface(csi_dev);
+ csi_dmareq_rff_disable(csi_dev);
+ csi_clk_disable(csi_dev);
+}
+
+static int mx6s_csi_enable(struct mx6s_csi_dev *csi_dev)
+{
+ struct v4l2_pix_format *pix = &csi_dev->pix;
+ unsigned long flags;
+ unsigned long val;
+ int timeout, timeout2;
+
+ csisw_reset(csi_dev);
+
+ if (pix->field == V4L2_FIELD_INTERLACED)
+ csi_tvdec_enable(csi_dev, true);
+
+ /* For mipi csi input only */
+ if (csi_dev->csi_mux_mipi == true) {
+ csi_dmareq_rff_enable(csi_dev);
+ csi_enable_int(csi_dev, 1);
+ csi_enable(csi_dev, 1);
+ return 0;
+ }
+
+ local_irq_save(flags);
+ for (timeout = 10000000; timeout > 0; timeout--) {
+ if (csi_read(csi_dev, CSI_CSISR) & BIT_SOF_INT) {
+ val = csi_read(csi_dev, CSI_CSICR3);
+ csi_write(csi_dev, val | BIT_DMA_REFLASH_RFF,
+ CSI_CSICR3);
+ /* Wait DMA reflash done */
+ for (timeout2 = 1000000; timeout2 > 0; timeout2--) {
+ if (csi_read(csi_dev, CSI_CSICR3) &
+ BIT_DMA_REFLASH_RFF)
+ cpu_relax();
+ else
+ break;
+ }
+ if (timeout2 <= 0) {
+ pr_err("timeout when wait for reflash done.\n");
+ local_irq_restore(flags);
+ return -ETIME;
+ }
+ /* For imx6sl csi, DMA FIFO will auto start when sensor ready to work,
+ * so DMA should enable right after FIFO reset, otherwise dma will lost data
+ * and image will split.
+ */
+ csi_dmareq_rff_enable(csi_dev);
+ csi_enable_int(csi_dev, 1);
+ csi_enable(csi_dev, 1);
+ break;
+ } else
+ cpu_relax();
+ }
+ if (timeout <= 0) {
+ pr_err("timeout when wait for SOF\n");
+ local_irq_restore(flags);
+ return -ETIME;
+ }
+ local_irq_restore(flags);
+
+ return 0;
+}
+
+static void mx6s_csi_disable(struct mx6s_csi_dev *csi_dev)
+{
+ struct v4l2_pix_format *pix = &csi_dev->pix;
+
+ csi_dmareq_rff_disable(csi_dev);
+ csi_disable_int(csi_dev);
+
+ /* set CSI_CSIDMASA_FB1 and CSI_CSIDMASA_FB2 to default value */
+ csi_write(csi_dev, 0, CSI_CSIDMASA_FB1);
+ csi_write(csi_dev, 0, CSI_CSIDMASA_FB2);
+
+ csi_buf_stride_set(csi_dev, 0);
+
+ if (pix->field == V4L2_FIELD_INTERLACED) {
+ csi_deinterlace_enable(csi_dev, false);
+ csi_tvdec_enable(csi_dev, false);
+ }
+
+ csi_enable(csi_dev, 0);
+}
+
+static int mx6s_configure_csi(struct mx6s_csi_dev *csi_dev)
+{
+ struct v4l2_pix_format *pix = &csi_dev->pix;
+ u32 cr1, cr18;
+ u32 width;
+
+ if (pix->field == V4L2_FIELD_INTERLACED) {
+ csi_deinterlace_enable(csi_dev, true);
+ csi_buf_stride_set(csi_dev, csi_dev->pix.width);
+ csi_deinterlace_mode(csi_dev, csi_dev->std);
+ } else {
+ csi_deinterlace_enable(csi_dev, false);
+ csi_buf_stride_set(csi_dev, 0);
+ }
+
+ switch (csi_dev->fmt->pixelformat) {
+ case V4L2_PIX_FMT_YUV32:
+ case V4L2_PIX_FMT_SBGGR8:
+ width = pix->width;
+ break;
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_YUYV:
+ if (csi_dev->csi_mux_mipi == true)
+ width = pix->width;
+ else
+ /* For parallel 8-bit sensor input */
+ width = pix->width * 2;
+ break;
+ default:
+ pr_debug(" case not supported\n");
+ return -EINVAL;
+ }
+ csi_set_imagpara(csi_dev, width, pix->height);
+
+ if (csi_dev->csi_mux_mipi == true) {
+ cr1 = csi_read(csi_dev, CSI_CSICR1);
+ cr1 &= ~BIT_GCLK_MODE;
+ csi_write(csi_dev, cr1, CSI_CSICR1);
+
+ cr18 = csi_read(csi_dev, CSI_CSICR18);
+ cr18 &= BIT_MIPI_DATA_FORMAT_MASK;
+ cr18 |= BIT_DATA_FROM_MIPI;
+
+ switch (csi_dev->fmt->pixelformat) {
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_YUYV:
+ cr18 |= BIT_MIPI_DATA_FORMAT_YUV422_8B;
+ break;
+ case V4L2_PIX_FMT_SBGGR8:
+ cr18 |= BIT_MIPI_DATA_FORMAT_RAW8;
+ break;
+ default:
+ pr_debug(" fmt not supported\n");
+ return -EINVAL;
+ }
+
+ csi_write(csi_dev, cr18, CSI_CSICR18);
+ }
+ return 0;
+}
+
+static int mx6s_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct mx6s_csi_dev *csi_dev = vb2_get_drv_priv(vq);
+ struct vb2_buffer *vb;
+ struct mx6s_buffer *buf;
+ unsigned long phys;
+ unsigned long flags;
+
+ if (count < 2)
+ return -ENOBUFS;
+
+ /*
+ * I didn't manage to properly enable/disable
+ * a per frame basis during running transfers,
+ * thus we allocate a buffer here and use it to
+ * discard frames when no buffer is available.
+ * Feel free to work on this ;)
+ */
+ csi_dev->discard_size = csi_dev->pix.sizeimage;
+ csi_dev->discard_buffer = dma_alloc_coherent(csi_dev->v4l2_dev.dev,
+ PAGE_ALIGN(csi_dev->discard_size),
+ &csi_dev->discard_buffer_dma,
+ GFP_DMA | GFP_KERNEL);
+ if (!csi_dev->discard_buffer)
+ return -ENOMEM;
+
+ spin_lock_irqsave(&csi_dev->slock, flags);
+
+ csi_dev->buf_discard[0].discard = true;
+ list_add_tail(&csi_dev->buf_discard[0].queue,
+ &csi_dev->discard);
+
+ csi_dev->buf_discard[1].discard = true;
+ list_add_tail(&csi_dev->buf_discard[1].queue,
+ &csi_dev->discard);
+
+ /* csi buf 0 */
+ buf = list_first_entry(&csi_dev->capture, struct mx6s_buffer,
+ internal.queue);
+ buf->internal.bufnum = 0;
+ vb = &buf->vb.vb2_buf;
+ vb->state = VB2_BUF_STATE_ACTIVE;
+
+ phys = vb2_dma_contig_plane_dma_addr(vb, 0);
+
+ mx6s_update_csi_buf(csi_dev, phys, buf->internal.bufnum);
+ list_move_tail(csi_dev->capture.next, &csi_dev->active_bufs);
+
+ /* csi buf 1 */
+ buf = list_first_entry(&csi_dev->capture, struct mx6s_buffer,
+ internal.queue);
+ buf->internal.bufnum = 1;
+ vb = &buf->vb.vb2_buf;
+ vb->state = VB2_BUF_STATE_ACTIVE;
+
+ phys = vb2_dma_contig_plane_dma_addr(vb, 0);
+ mx6s_update_csi_buf(csi_dev, phys, buf->internal.bufnum);
+ list_move_tail(csi_dev->capture.next, &csi_dev->active_bufs);
+
+ spin_unlock_irqrestore(&csi_dev->slock, flags);
+
+ return mx6s_csi_enable(csi_dev);
+}
+
+static void mx6s_stop_streaming(struct vb2_queue *vq)
+{
+ struct mx6s_csi_dev *csi_dev = vb2_get_drv_priv(vq);
+ unsigned long flags;
+ struct mx6s_buffer *buf, *tmp;
+ void *b;
+
+ mx6s_csi_disable(csi_dev);
+
+ spin_lock_irqsave(&csi_dev->slock, flags);
+
+ list_for_each_entry_safe(buf, tmp,
+ &csi_dev->active_bufs, internal.queue) {
+ list_del_init(&buf->internal.queue);
+ if (buf->vb.vb2_buf.state == VB2_BUF_STATE_ACTIVE)
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ }
+
+ list_for_each_entry_safe(buf, tmp,
+ &csi_dev->capture, internal.queue) {
+ list_del_init(&buf->internal.queue);
+ if (buf->vb.vb2_buf.state == VB2_BUF_STATE_ACTIVE)
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ }
+
+ INIT_LIST_HEAD(&csi_dev->capture);
+ INIT_LIST_HEAD(&csi_dev->active_bufs);
+ INIT_LIST_HEAD(&csi_dev->discard);
+
+ b = csi_dev->discard_buffer;
+ csi_dev->discard_buffer = NULL;
+
+ spin_unlock_irqrestore(&csi_dev->slock, flags);
+
+ dma_free_coherent(csi_dev->v4l2_dev.dev,
+ csi_dev->discard_size, b,
+ csi_dev->discard_buffer_dma);
+}
+
+static struct vb2_ops mx6s_videobuf_ops = {
+ .queue_setup = mx6s_videobuf_setup,
+ .buf_prepare = mx6s_videobuf_prepare,
+ .buf_queue = mx6s_videobuf_queue,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ .start_streaming = mx6s_start_streaming,
+ .stop_streaming = mx6s_stop_streaming,
+};
+
+static void mx6s_csi_frame_done(struct mx6s_csi_dev *csi_dev,
+ int bufnum, bool err)
+{
+ struct mx6s_buf_internal *ibuf;
+ struct mx6s_buffer *buf;
+ struct vb2_buffer *vb;
+ unsigned long phys;
+
+ ibuf = list_first_entry(&csi_dev->active_bufs, struct mx6s_buf_internal,
+ queue);
+
+ if (ibuf->discard) {
+ /*
+ * Discard buffer must not be returned to user space.
+ * Just return it to the discard queue.
+ */
+ list_move_tail(csi_dev->active_bufs.next, &csi_dev->discard);
+ } else {
+ buf = mx6s_ibuf_to_buf(ibuf);
+
+ vb = &buf->vb.vb2_buf;
+ phys = vb2_dma_contig_plane_dma_addr(vb, 0);
+ if (bufnum == 1) {
+ if (csi_read(csi_dev, CSI_CSIDMASA_FB2) != phys) {
+ dev_err(csi_dev->dev, "%lx != %x\n", phys,
+ csi_read(csi_dev, CSI_CSIDMASA_FB2));
+ }
+ } else {
+ if (csi_read(csi_dev, CSI_CSIDMASA_FB1) != phys) {
+ dev_err(csi_dev->dev, "%lx != %x\n", phys,
+ csi_read(csi_dev, CSI_CSIDMASA_FB1));
+ }
+ }
+ dev_dbg(csi_dev->dev, "%s (vb=0x%p) 0x%p %lu\n", __func__, vb,
+ vb2_plane_vaddr(vb, 0),
+ vb2_get_plane_payload(vb, 0));
+
+ list_del_init(&buf->internal.queue);
+ vb->timestamp =ktime_get_ns();
+ to_vb2_v4l2_buffer(vb)->sequence = csi_dev->frame_count;
+ if (err)
+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
+ else
+ vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+ }
+
+ csi_dev->frame_count++;
+
+ /* Config discard buffer to active_bufs */
+ if (list_empty(&csi_dev->capture)) {
+ if (list_empty(&csi_dev->discard)) {
+ dev_warn(csi_dev->dev,
+ "%s: trying to access empty discard list\n",
+ __func__);
+ return;
+ }
+
+ ibuf = list_first_entry(&csi_dev->discard,
+ struct mx6s_buf_internal, queue);
+ ibuf->bufnum = bufnum;
+
+ list_move_tail(csi_dev->discard.next, &csi_dev->active_bufs);
+
+ mx6s_update_csi_buf(csi_dev,
+ csi_dev->discard_buffer_dma, bufnum);
+ return;
+ }
+
+ buf = list_first_entry(&csi_dev->capture, struct mx6s_buffer,
+ internal.queue);
+
+ buf->internal.bufnum = bufnum;
+
+ list_move_tail(csi_dev->capture.next, &csi_dev->active_bufs);
+
+ vb = &buf->vb.vb2_buf;
+ vb->state = VB2_BUF_STATE_ACTIVE;
+
+ phys = vb2_dma_contig_plane_dma_addr(vb, 0);
+ mx6s_update_csi_buf(csi_dev, phys, bufnum);
+}
+
+static irqreturn_t mx6s_csi_irq_handler(int irq, void *data)
+{
+ struct mx6s_csi_dev *csi_dev = data;
+ unsigned long status;
+ u32 cr3, cr18;
+
+ spin_lock(&csi_dev->slock);
+
+ status = csi_read(csi_dev, CSI_CSISR);
+ csi_write(csi_dev, status, CSI_CSISR);
+
+ if (list_empty(&csi_dev->active_bufs)) {
+ dev_warn(csi_dev->dev,
+ "%s: called while active list is empty\n",
+ __func__);
+
+ spin_unlock(&csi_dev->slock);
+ return IRQ_HANDLED;
+ }
+
+ if (status & BIT_RFF_OR_INT) {
+ dev_warn(csi_dev->dev, "%s Rx fifo overflow\n", __func__);
+ if (*csi_dev->rx_fifo_rst)
+ csi_error_recovery(csi_dev);
+ }
+
+ if (status & BIT_HRESP_ERR_INT) {
+ dev_warn(csi_dev->dev, "%s Hresponse error detected\n",
+ __func__);
+ csi_error_recovery(csi_dev);
+ }
+
+ if (status & BIT_ADDR_CH_ERR_INT) {
+ /* Disable csi */
+ cr18 = csi_read(csi_dev, CSI_CSICR18);
+ cr18 &= ~BIT_CSI_ENABLE;
+ csi_write(csi_dev, cr18, CSI_CSICR18);
+
+ /* DMA reflash */
+ cr3 = csi_read(csi_dev, CSI_CSICR3);
+ cr3 |= BIT_DMA_REFLASH_RFF;
+ csi_write(csi_dev, cr3, CSI_CSICR3);
+
+ /* Ensable csi */
+ cr18 |= BIT_CSI_ENABLE;
+ csi_write(csi_dev, cr18, CSI_CSICR18);
+
+ pr_debug("base address switching Change Err.\n");
+ }
+
+ if ((status & BIT_DMA_TSF_DONE_FB1) &&
+ (status & BIT_DMA_TSF_DONE_FB2)) {
+ /* For both FB1 and FB2 interrupter bits set case,
+ * CSI DMA is work in one of FB1 and FB2 buffer,
+ * but software can not know the state.
+ * Skip it to avoid base address updated
+ * when csi work in field0 and field1 will write to
+ * new base address.
+ * PDM TKT230775 */
+ pr_debug("Skip two frames\n");
+ } else if (status & BIT_DMA_TSF_DONE_FB1) {
+ mx6s_csi_frame_done(csi_dev, 0, false);
+ } else if (status & BIT_DMA_TSF_DONE_FB2) {
+ mx6s_csi_frame_done(csi_dev, 1, false);
+ }
+
+ spin_unlock(&csi_dev->slock);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * File operations for the device
+ */
+static int mx6s_csi_open(struct file *file)
+{
+ struct mx6s_csi_dev *csi_dev = video_drvdata(file);
+ struct v4l2_subdev *sd = csi_dev->sd;
+ struct vb2_queue *q = &csi_dev->vb2_vidq;
+ int ret = 0;
+
+ file->private_data = csi_dev;
+
+ if (mutex_lock_interruptible(&csi_dev->lock))
+ return -ERESTARTSYS;
+
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_USERPTR;
+ q->drv_priv = csi_dev;
+ q->ops = &mx6s_videobuf_ops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct mx6s_buffer);
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->lock = &csi_dev->lock;
+
+ ret = vb2_queue_init(q);
+ if (ret < 0)
+ goto unlock;
+
+ pm_runtime_get_sync(csi_dev->dev);
+
+ request_bus_freq(BUS_FREQ_HIGH);
+
+ v4l2_subdev_call(sd, core, s_power, 1);
+ mx6s_csi_init(csi_dev);
+
+ mutex_unlock(&csi_dev->lock);
+
+ return ret;
+unlock:
+ mutex_unlock(&csi_dev->lock);
+ return ret;
+}
+
+static int mx6s_csi_close(struct file *file)
+{
+ struct mx6s_csi_dev *csi_dev = video_drvdata(file);
+ struct v4l2_subdev *sd = csi_dev->sd;
+
+ mutex_lock(&csi_dev->lock);
+
+ vb2_queue_release(&csi_dev->vb2_vidq);
+
+ mx6s_csi_deinit(csi_dev);
+ v4l2_subdev_call(sd, core, s_power, 0);
+
+ mutex_unlock(&csi_dev->lock);
+
+ file->private_data = NULL;
+
+ release_bus_freq(BUS_FREQ_HIGH);
+
+ pm_runtime_put_sync_suspend(csi_dev->dev);
+ return 0;
+}
+
+static ssize_t mx6s_csi_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct mx6s_csi_dev *csi_dev = video_drvdata(file);
+ int ret;
+
+ dev_dbg(csi_dev->dev, "read called, buf %p\n", buf);
+
+ mutex_lock(&csi_dev->lock);
+ ret = vb2_read(&csi_dev->vb2_vidq, buf, count, ppos,
+ file->f_flags & O_NONBLOCK);
+ mutex_unlock(&csi_dev->lock);
+ return ret;
+}
+
+static int mx6s_csi_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct mx6s_csi_dev *csi_dev = video_drvdata(file);
+ int ret;
+
+ if (mutex_lock_interruptible(&csi_dev->lock))
+ return -ERESTARTSYS;
+ ret = vb2_mmap(&csi_dev->vb2_vidq, vma);
+ mutex_unlock(&csi_dev->lock);
+
+ pr_debug("vma start=0x%08lx, size=%ld, ret=%d\n",
+ (unsigned long)vma->vm_start,
+ (unsigned long)vma->vm_end-(unsigned long)vma->vm_start,
+ ret);
+
+ return ret;
+}
+
+static struct v4l2_file_operations mx6s_csi_fops = {
+ .owner = THIS_MODULE,
+ .open = mx6s_csi_open,
+ .release = mx6s_csi_close,
+ .read = mx6s_csi_read,
+ .poll = vb2_fop_poll,
+ .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
+ .mmap = mx6s_csi_mmap,
+};
+
+/*
+ * Video node IOCTLs
+ */
+static int mx6s_vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *inp)
+{
+ if (inp->index != 0)
+ return -EINVAL;
+
+ /* default is camera */
+ inp->type = V4L2_INPUT_TYPE_CAMERA;
+ strcpy(inp->name, "Camera");
+
+ return 0;
+}
+
+static int mx6s_vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ *i = 0;
+
+ return 0;
+}
+
+static int mx6s_vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+ if (i > 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int mx6s_vidioc_querystd(struct file *file, void *priv, v4l2_std_id *a)
+{
+ struct mx6s_csi_dev *csi_dev = video_drvdata(file);
+ struct v4l2_subdev *sd = csi_dev->sd;
+
+ return v4l2_subdev_call(sd, video, querystd, a);
+}
+
+static int mx6s_vidioc_s_std(struct file *file, void *priv, v4l2_std_id a)
+{
+ struct mx6s_csi_dev *csi_dev = video_drvdata(file);
+ struct v4l2_subdev *sd = csi_dev->sd;
+
+ return v4l2_subdev_call(sd, video, s_std, a);
+}
+
+static int mx6s_vidioc_g_std(struct file *file, void *priv, v4l2_std_id *a)
+{
+ struct mx6s_csi_dev *csi_dev = video_drvdata(file);
+ struct v4l2_subdev *sd = csi_dev->sd;
+
+ return v4l2_subdev_call(sd, video, g_std, a);
+}
+
+static int mx6s_vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ struct mx6s_csi_dev *csi_dev = video_drvdata(file);
+
+ WARN_ON(priv != file->private_data);
+
+ return vb2_reqbufs(&csi_dev->vb2_vidq, p);
+}
+
+static int mx6s_vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct mx6s_csi_dev *csi_dev = video_drvdata(file);
+ int ret;
+
+ WARN_ON(priv != file->private_data);
+
+ ret = vb2_querybuf(&csi_dev->vb2_vidq, p);
+
+ if (!ret) {
+ /* return physical address */
+ struct vb2_buffer *vb = csi_dev->vb2_vidq.bufs[p->index];
+ if (p->flags & V4L2_BUF_FLAG_MAPPED)
+ p->m.offset = vb2_dma_contig_plane_dma_addr(vb, 0);
+ }
+ return ret;
+}
+
+static int mx6s_vidioc_qbuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct mx6s_csi_dev *csi_dev = video_drvdata(file);
+
+ WARN_ON(priv != file->private_data);
+
+ return vb2_qbuf(&csi_dev->vb2_vidq, p);
+}
+
+static int mx6s_vidioc_dqbuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct mx6s_csi_dev *csi_dev = video_drvdata(file);
+
+ WARN_ON(priv != file->private_data);
+
+ return vb2_dqbuf(&csi_dev->vb2_vidq, p, file->f_flags & O_NONBLOCK);
+}
+
+static int mx6s_vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct mx6s_csi_dev *csi_dev = video_drvdata(file);
+ struct v4l2_subdev *sd = csi_dev->sd;
+ struct v4l2_subdev_mbus_code_enum code = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ .index = f->index,
+ };
+ struct mx6s_fmt *fmt;
+ int ret;
+
+ WARN_ON(priv != file->private_data);
+
+ ret = v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code);
+ if (ret < 0) {
+ /* no more formats */
+ dev_dbg(csi_dev->dev, "No more fmt\n");
+ return -EINVAL;
+ }
+
+ fmt = format_by_mbus(code.code);
+ if (!fmt) {
+ dev_err(csi_dev->dev, "mbus (0x%08x) invalid.\n", code.code);
+ return -EINVAL;
+ }
+
+ strlcpy(f->description, fmt->name, sizeof(f->description));
+ f->pixelformat = fmt->pixelformat;
+
+ return 0;
+}
+
+static int mx6s_vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct mx6s_csi_dev *csi_dev = video_drvdata(file);
+ struct v4l2_subdev *sd = csi_dev->sd;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_subdev_format format = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+ struct mx6s_fmt *fmt;
+ int ret;
+
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ if (!fmt) {
+ dev_err(csi_dev->dev, "Fourcc format (0x%08x) invalid.",
+ f->fmt.pix.pixelformat);
+ return -EINVAL;
+ }
+
+ if (f->fmt.pix.width == 0 || f->fmt.pix.height == 0) {
+ dev_err(csi_dev->dev, "width %d, height %d is too small.\n",
+ f->fmt.pix.width, f->fmt.pix.height);
+ return -EINVAL;
+ }
+
+ v4l2_fill_mbus_format(&format.format, pix, fmt->mbus_code);
+ ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &format);
+ v4l2_fill_pix_format(pix, &format.format);
+
+ if (pix->field != V4L2_FIELD_INTERLACED)
+ pix->field = V4L2_FIELD_NONE;
+
+ pix->sizeimage = fmt->bpp * pix->height * pix->width;
+ pix->bytesperline = fmt->bpp * pix->width;
+
+ return ret;
+}
+
+/*
+ * The real work of figuring out a workable format.
+ */
+
+static int mx6s_vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct mx6s_csi_dev *csi_dev = video_drvdata(file);
+ int ret;
+
+ ret = mx6s_vidioc_try_fmt_vid_cap(file, csi_dev, f);
+ if (ret < 0)
+ return ret;
+
+ csi_dev->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ csi_dev->mbus_code = csi_dev->fmt->mbus_code;
+ csi_dev->pix.width = f->fmt.pix.width;
+ csi_dev->pix.height = f->fmt.pix.height;
+ csi_dev->pix.sizeimage = f->fmt.pix.sizeimage;
+ csi_dev->pix.field = f->fmt.pix.field;
+ csi_dev->type = f->type;
+ dev_dbg(csi_dev->dev, "set to pixelformat '%4.6s'\n",
+ (char *)&csi_dev->fmt->name);
+
+ /* Config csi */
+ mx6s_configure_csi(csi_dev);
+
+ return 0;
+}
+
+static int mx6s_vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct mx6s_csi_dev *csi_dev = video_drvdata(file);
+
+ WARN_ON(priv != file->private_data);
+
+ f->fmt.pix = csi_dev->pix;
+
+ return 0;
+}
+
+static int mx6s_vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct mx6s_csi_dev *csi_dev = video_drvdata(file);
+
+ WARN_ON(priv != file->private_data);
+
+ /* cap->name is set by the friendly caller:-> */
+ strlcpy(cap->driver, MX6S_CAM_DRV_NAME, sizeof(cap->driver));
+ strlcpy(cap->card, MX6S_CAM_DRIVER_DESCRIPTION, sizeof(cap->card));
+ snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
+ dev_name(csi_dev->dev));
+
+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+ return 0;
+}
+
+static int mx6s_vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type i)
+{
+ struct mx6s_csi_dev *csi_dev = video_drvdata(file);
+ struct v4l2_subdev *sd = csi_dev->sd;
+ int ret;
+
+ WARN_ON(priv != file->private_data);
+
+ if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ ret = vb2_streamon(&csi_dev->vb2_vidq, i);
+ if (!ret)
+ v4l2_subdev_call(sd, video, s_stream, 1);
+
+ return ret;
+}
+
+static int mx6s_vidioc_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type i)
+{
+ struct mx6s_csi_dev *csi_dev = video_drvdata(file);
+ struct v4l2_subdev *sd = csi_dev->sd;
+
+ WARN_ON(priv != file->private_data);
+
+ if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ /*
+ * This calls buf_release from host driver's videobuf_queue_ops for all
+ * remaining buffers. When the last buffer is freed, stop capture
+ */
+ vb2_streamoff(&csi_dev->vb2_vidq, i);
+
+ v4l2_subdev_call(sd, video, s_stream, 0);
+
+ return 0;
+}
+
+static int mx6s_vidioc_cropcap(struct file *file, void *fh,
+ struct v4l2_cropcap *a)
+{
+ struct mx6s_csi_dev *csi_dev = video_drvdata(file);
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ dev_dbg(csi_dev->dev, "VIDIOC_CROPCAP not implemented\n");
+
+ return 0;
+}
+
+static int mx6s_vidioc_g_crop(struct file *file, void *priv,
+ struct v4l2_crop *a)
+{
+ struct mx6s_csi_dev *csi_dev = video_drvdata(file);
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ dev_dbg(csi_dev->dev, "VIDIOC_G_CROP not implemented\n");
+
+ return 0;
+}
+
+static int mx6s_vidioc_s_crop(struct file *file, void *priv,
+ const struct v4l2_crop *a)
+{
+ struct mx6s_csi_dev *csi_dev = video_drvdata(file);
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ dev_dbg(csi_dev->dev, "VIDIOC_S_CROP not implemented\n");
+
+ return 0;
+}
+
+static int mx6s_vidioc_g_parm(struct file *file, void *priv,
+ struct v4l2_streamparm *a)
+{
+ struct mx6s_csi_dev *csi_dev = video_drvdata(file);
+ struct v4l2_subdev *sd = csi_dev->sd;
+
+ return v4l2_subdev_call(sd, video, g_parm, a);
+}
+
+static int mx6s_vidioc_s_parm(struct file *file, void *priv,
+ struct v4l2_streamparm *a)
+{
+ struct mx6s_csi_dev *csi_dev = video_drvdata(file);
+ struct v4l2_subdev *sd = csi_dev->sd;
+
+ return v4l2_subdev_call(sd, video, s_parm, a);
+}
+
+static int mx6s_vidioc_enum_framesizes(struct file *file, void *priv,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct mx6s_csi_dev *csi_dev = video_drvdata(file);
+ struct v4l2_subdev *sd = csi_dev->sd;
+ struct mx6s_fmt *fmt;
+ struct v4l2_subdev_frame_size_enum fse = {
+ .index = fsize->index,
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+ int ret;
+
+ fmt = format_by_fourcc(fsize->pixel_format);
+ if (fmt->pixelformat != fsize->pixel_format)
+ return -EINVAL;
+ fse.code = fmt->mbus_code;
+
+ ret = v4l2_subdev_call(sd, pad, enum_frame_size, NULL, &fse);
+ if (ret)
+ return ret;
+
+ if (fse.min_width == fse.max_width &&
+ fse.min_height == fse.max_height) {
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = fse.min_width;
+ fsize->discrete.height = fse.min_height;
+ return 0;
+ }
+
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->stepwise.min_width = fse.min_width;
+ fsize->stepwise.max_width = fse.max_width;
+ fsize->stepwise.min_height = fse.min_height;
+ fsize->stepwise.max_height = fse.max_height;
+ fsize->stepwise.step_width = 1;
+ fsize->stepwise.step_height = 1;
+
+ return 0;
+}
+
+static int mx6s_vidioc_enum_frameintervals(struct file *file, void *priv,
+ struct v4l2_frmivalenum *interval)
+{
+ struct mx6s_csi_dev *csi_dev = video_drvdata(file);
+ struct v4l2_subdev *sd = csi_dev->sd;
+ struct mx6s_fmt *fmt;
+ struct v4l2_subdev_frame_interval_enum fie = {
+ .index = interval->index,
+ .width = interval->width,
+ .height = interval->height,
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+ int ret;
+
+ fmt = format_by_fourcc(interval->pixel_format);
+ if (fmt->pixelformat != interval->pixel_format)
+ return -EINVAL;
+ fie.code = fmt->mbus_code;
+
+ ret = v4l2_subdev_call(sd, pad, enum_frame_interval, NULL, &fie);
+ if (ret)
+ return ret;
+ interval->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+ interval->discrete = fie.interval;
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops mx6s_csi_ioctl_ops = {
+ .vidioc_querycap = mx6s_vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = mx6s_vidioc_enum_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = mx6s_vidioc_try_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = mx6s_vidioc_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = mx6s_vidioc_s_fmt_vid_cap,
+ .vidioc_cropcap = mx6s_vidioc_cropcap,
+ .vidioc_s_crop = mx6s_vidioc_s_crop,
+ .vidioc_g_crop = mx6s_vidioc_g_crop,
+ .vidioc_reqbufs = mx6s_vidioc_reqbufs,
+ .vidioc_querybuf = mx6s_vidioc_querybuf,
+ .vidioc_qbuf = mx6s_vidioc_qbuf,
+ .vidioc_dqbuf = mx6s_vidioc_dqbuf,
+ .vidioc_g_std = mx6s_vidioc_g_std,
+ .vidioc_s_std = mx6s_vidioc_s_std,
+ .vidioc_querystd = mx6s_vidioc_querystd,
+ .vidioc_enum_input = mx6s_vidioc_enum_input,
+ .vidioc_g_input = mx6s_vidioc_g_input,
+ .vidioc_s_input = mx6s_vidioc_s_input,
+ .vidioc_streamon = mx6s_vidioc_streamon,
+ .vidioc_streamoff = mx6s_vidioc_streamoff,
+ .vidioc_g_parm = mx6s_vidioc_g_parm,
+ .vidioc_s_parm = mx6s_vidioc_s_parm,
+ .vidioc_enum_framesizes = mx6s_vidioc_enum_framesizes,
+ .vidioc_enum_frameintervals = mx6s_vidioc_enum_frameintervals,
+};
+
+static int subdev_notifier_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_subdev *asd)
+{
+ struct mx6s_csi_dev *csi_dev = notifier_to_mx6s_dev(notifier);
+
+ /* Find platform data for this sensor subdev */
+ if (csi_dev->asd.match.of.node == subdev->dev->of_node)
+ csi_dev->sd = subdev;
+
+ if (subdev == NULL)
+ return -EINVAL;
+
+ v4l2_info(&csi_dev->v4l2_dev, "Registered sensor subdevice: %s\n",
+ subdev->name);
+
+ return 0;
+}
+
+static int mx6s_csi_mux_sel(struct mx6s_csi_dev *csi_dev)
+{
+ struct device_node *np = csi_dev->dev->of_node;
+ struct device_node *node;
+ phandle phandle;
+ u32 out_val[3];
+ int ret;
+
+ ret = of_property_read_u32_array(np, "csi-mux-mipi", out_val, 3);
+ if (ret) {
+ dev_dbg(csi_dev->dev, "no csi-mux-mipi property found\n");
+ csi_dev->csi_mux_mipi = false;
+ } else {
+ phandle = *out_val;
+
+ node = of_find_node_by_phandle(phandle);
+ if (!node) {
+ dev_dbg(csi_dev->dev, "not find gpr node by phandle\n");
+ ret = PTR_ERR(node);
+ }
+ csi_dev->csi_mux.gpr = syscon_node_to_regmap(node);
+ if (IS_ERR(csi_dev->csi_mux.gpr)) {
+ dev_err(csi_dev->dev, "failed to get gpr regmap\n");
+ ret = PTR_ERR(csi_dev->csi_mux.gpr);
+ }
+ of_node_put(node);
+ if (ret < 0)
+ return ret;
+
+ csi_dev->csi_mux.req_gpr = out_val[1];
+ csi_dev->csi_mux.req_bit = out_val[2];
+
+ regmap_update_bits(csi_dev->csi_mux.gpr, csi_dev->csi_mux.req_gpr,
+ 1 << csi_dev->csi_mux.req_bit, 1 << csi_dev->csi_mux.req_bit);
+
+ csi_dev->csi_mux_mipi = true;
+ }
+ return ret;
+}
+
+static int mx6sx_register_subdevs(struct mx6s_csi_dev *csi_dev)
+{
+ struct device_node *parent = csi_dev->dev->of_node;
+ struct device_node *node, *port, *rem;
+ int ret;
+
+ /* Attach sensors linked to csi receivers */
+ for_each_available_child_of_node(parent, node) {
+ if (of_node_cmp(node->name, "port"))
+ continue;
+
+ /* The csi node can have only port subnode. */
+ port = of_get_next_child(node, NULL);
+ if (!port)
+ continue;
+ rem = of_graph_get_remote_port_parent(port);
+ of_node_put(port);
+ if (rem == NULL) {
+ v4l2_info(&csi_dev->v4l2_dev,
+ "Remote device at %s not found\n",
+ port->full_name);
+ return -1;
+ }
+
+ csi_dev->asd.match_type = V4L2_ASYNC_MATCH_OF;
+ csi_dev->asd.match.of.node = rem;
+ csi_dev->async_subdevs[0] = &csi_dev->asd;
+
+ of_node_put(rem);
+ break;
+ }
+
+ csi_dev->subdev_notifier.subdevs = csi_dev->async_subdevs;
+ csi_dev->subdev_notifier.num_subdevs = 1;
+ csi_dev->subdev_notifier.bound = subdev_notifier_bound;
+
+ ret = v4l2_async_notifier_register(&csi_dev->v4l2_dev,
+ &csi_dev->subdev_notifier);
+ if (ret)
+ dev_err(csi_dev->dev,
+ "Error register async notifier regoster\n");
+
+ return ret;
+}
+
+static int mx6s_csi_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ const struct of_device_id *of_id;
+ struct mx6s_csi_dev *csi_dev;
+ struct video_device *vdev;
+ struct resource *res;
+ int ret = 0;
+
+ dev_dbg(dev, "initialising\n");
+
+ /* Prepare our private structure */
+ csi_dev = devm_kzalloc(dev, sizeof(struct mx6s_csi_dev), GFP_ATOMIC);
+ if (!csi_dev) {
+ dev_err(dev, "Can't allocate private structure\n");
+ return -ENODEV;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ csi_dev->irq = platform_get_irq(pdev, 0);
+ if (res == NULL || csi_dev->irq < 0) {
+ dev_err(dev, "Missing platform resources data\n");
+ return -ENODEV;
+ }
+
+ csi_dev->regbase = devm_ioremap_resource(dev, res);
+ if (IS_ERR(csi_dev->regbase)) {
+ dev_err(dev, "Failed platform resources map\n");
+ return -ENODEV;
+ }
+
+ /* init video dma queues */
+ INIT_LIST_HEAD(&csi_dev->capture);
+ INIT_LIST_HEAD(&csi_dev->active_bufs);
+ INIT_LIST_HEAD(&csi_dev->discard);
+
+ csi_dev->clk_disp_axi = devm_clk_get(dev, "disp-axi");
+ if (IS_ERR(csi_dev->clk_disp_axi)) {
+ dev_err(dev, "Could not get csi axi clock\n");
+ return -ENODEV;
+ }
+
+ csi_dev->clk_disp_dcic = devm_clk_get(dev, "disp_dcic");
+ if (IS_ERR(csi_dev->clk_disp_dcic)) {
+ dev_err(dev, "Could not get disp dcic clock\n");
+ return -ENODEV;
+ }
+
+ csi_dev->clk_csi_mclk = devm_clk_get(dev, "csi_mclk");
+ if (IS_ERR(csi_dev->clk_csi_mclk)) {
+ dev_err(dev, "Could not get csi mclk clock\n");
+ return -ENODEV;
+ }
+
+ csi_dev->dev = dev;
+
+ mx6s_csi_mux_sel(csi_dev);
+
+ of_id = of_match_node(mx6s_csi_dt_ids, csi_dev->dev->of_node);
+ if (!of_id)
+ return -EINVAL;
+ csi_dev->rx_fifo_rst = of_id->data;
+
+ snprintf(csi_dev->v4l2_dev.name,
+ sizeof(csi_dev->v4l2_dev.name), "CSI");
+
+ ret = v4l2_device_register(dev, &csi_dev->v4l2_dev);
+ if (ret < 0) {
+ dev_err(dev, "v4l2_device_register() failed: %d\n", ret);
+ return -ENODEV;
+ }
+
+ /* initialize locks */
+ mutex_init(&csi_dev->lock);
+ spin_lock_init(&csi_dev->slock);
+
+ /* Allocate memory for video device */
+ vdev = video_device_alloc();
+ if (vdev == NULL) {
+ ret = -ENOMEM;
+ goto err_vdev;
+ }
+
+ snprintf(vdev->name, sizeof(vdev->name), "mx6s-csi");
+
+ vdev->v4l2_dev = &csi_dev->v4l2_dev;
+ vdev->fops = &mx6s_csi_fops;
+ vdev->ioctl_ops = &mx6s_csi_ioctl_ops;
+ vdev->release = video_device_release;
+ vdev->lock = &csi_dev->lock;
+
+ vdev->queue = &csi_dev->vb2_vidq;
+
+ csi_dev->vdev = vdev;
+
+ video_set_drvdata(csi_dev->vdev, csi_dev);
+ mutex_lock(&csi_dev->lock);
+
+ ret = video_register_device(csi_dev->vdev, VFL_TYPE_GRABBER, -1);
+ if (ret < 0) {
+ video_device_release(csi_dev->vdev);
+ mutex_unlock(&csi_dev->lock);
+ goto err_vdev;
+ }
+
+ /* install interrupt handler */
+ if (devm_request_irq(dev, csi_dev->irq, mx6s_csi_irq_handler,
+ 0, "csi", (void *)csi_dev)) {
+ mutex_unlock(&csi_dev->lock);
+ dev_err(dev, "Request CSI IRQ failed.\n");
+ ret = -ENODEV;
+ goto err_irq;
+ }
+
+ mutex_unlock(&csi_dev->lock);
+
+ ret = mx6sx_register_subdevs(csi_dev);
+ if (ret < 0)
+ goto err_irq;
+
+ pm_runtime_enable(csi_dev->dev);
+ return 0;
+
+err_irq:
+ video_unregister_device(csi_dev->vdev);
+err_vdev:
+ v4l2_device_unregister(&csi_dev->v4l2_dev);
+ return ret;
+}
+
+static int mx6s_csi_remove(struct platform_device *pdev)
+{
+ struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev);
+ struct mx6s_csi_dev *csi_dev =
+ container_of(v4l2_dev, struct mx6s_csi_dev, v4l2_dev);
+
+ v4l2_async_notifier_unregister(&csi_dev->subdev_notifier);
+
+ video_unregister_device(csi_dev->vdev);
+ v4l2_device_unregister(&csi_dev->v4l2_dev);
+
+ pm_runtime_disable(csi_dev->dev);
+ return 0;
+}
+
+static int mx6s_csi_runtime_suspend(struct device *dev)
+{
+ dev_dbg(dev, "csi v4l2 busfreq high release.\n");
+ return 0;
+}
+
+static int mx6s_csi_runtime_resume(struct device *dev)
+{
+ dev_dbg(dev, "csi v4l2 busfreq high request.\n");
+ return 0;
+}
+
+static const struct dev_pm_ops mx6s_csi_pm_ops = {
+ SET_RUNTIME_PM_OPS(mx6s_csi_runtime_suspend, mx6s_csi_runtime_resume, NULL)
+};
+
+static const u8 mx6s_fifo_rst = true;
+static const u8 mx6sl_fifo_rst = false;
+
+static const struct of_device_id mx6s_csi_dt_ids[] = {
+ { .compatible = "fsl,imx6s-csi",
+ .data = &mx6s_fifo_rst,
+ },
+ { .compatible = "fsl,imx6sl-csi",
+ .data = &mx6sl_fifo_rst,
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mx6s_csi_dt_ids);
+
+static struct platform_driver mx6s_csi_driver = {
+ .driver = {
+ .name = MX6S_CAM_DRV_NAME,
+ .of_match_table = of_match_ptr(mx6s_csi_dt_ids),
+ .pm = &mx6s_csi_pm_ops,
+ },
+ .probe = mx6s_csi_probe,
+ .remove = mx6s_csi_remove,
+};
+
+module_platform_driver(mx6s_csi_driver);
+
+MODULE_DESCRIPTION("i.MX6Sx SoC Camera Host driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(MX6S_CAM_VERSION);
--- /dev/null
+/*
+ * Freescale i.MX7 SoC series MIPI-CSI V3.3 receiver driver
+ *
+ * Copyright (C) 2015-2016 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
+ */
+/*
+ * Samsung S5P/EXYNOS SoC series MIPI-CSI receiver driver
+ *
+ * Copyright (C) 2011 - 2013 Samsung Electronics Co., Ltd.
+ * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-of.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-device.h>
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debug level (0-2)");
+
+#define CSIS_DRIVER_NAME "mxc_mipi-csi"
+#define CSIS_SUBDEV_NAME CSIS_DRIVER_NAME
+#define CSIS_MAX_ENTITIES 2
+#define CSIS0_MAX_LANES 4
+#define CSIS1_MAX_LANES 2
+
+#define MIPI_CSIS_DEF_PIX_WIDTH 640
+#define MIPI_CSIS_DEF_PIX_HEIGHT 480
+
+/* Register map definition */
+
+/* CSIS version */
+#define MIPI_CSIS_VERSION 0x00
+
+/* CSIS common control */
+#define MIPI_CSIS_CMN_CTRL 0x04
+#define MIPI_CSIS_CMN_CTRL_UPDATE_SHADOW (1 << 16)
+#define MIPI_CSIS_CMN_CTRL_INTER_MODE (1 << 10)
+#define MIPI_CSIS_CMN_CTRL_LANE_NR_OFFSET 8
+#define MIPI_CSIS_CMN_CTRL_LANE_NR_MASK (3 << 8)
+#define MIPI_CSIS_CMN_CTRL_UPDATE_SHADOW_CTRL (1 << 2)
+#define MIPI_CSIS_CMN_CTRL_RESET (1 << 1)
+#define MIPI_CSIS_CMN_CTRL_ENABLE (1 << 0)
+
+/* CSIS clock control */
+#define MIPI_CSIS_CLK_CTRL 0x08
+#define MIPI_CSIS_CLK_CTRL_CLKGATE_TRAIL_CH3(x) (x << 28)
+#define MIPI_CSIS_CLK_CTRL_CLKGATE_TRAIL_CH2(x) (x << 24)
+#define MIPI_CSIS_CLK_CTRL_CLKGATE_TRAIL_CH1(x) (x << 20)
+#define MIPI_CSIS_CLK_CTRL_CLKGATE_TRAIL_CH0(x) (x << 16)
+#define MIPI_CSIS_CLK_CTRL_CLKGATE_EN_MSK (0xf << 4)
+#define MIPI_CSIS_CLK_CTRL_WCLK_SRC (1 << 0)
+
+/* CSIS Interrupt mask */
+#define MIPI_CSIS_INTMSK 0x10
+#define MIPI_CSIS_INTMSK_EVEN_BEFORE (1 << 31)
+#define MIPI_CSIS_INTMSK_EVEN_AFTER (1 << 30)
+#define MIPI_CSIS_INTMSK_ODD_BEFORE (1 << 29)
+#define MIPI_CSIS_INTMSK_ODD_AFTER (1 << 28)
+#define MIPI_CSIS_INTMSK_FRAME_START (1 << 24)
+#define MIPI_CSIS_INTMSK_FRAME_END (1 << 20)
+#define MIPI_CSIS_INTMSK_ERR_SOT_HS (1 << 16)
+#define MIPI_CSIS_INTMSK_ERR_LOST_FS (1 << 12)
+#define MIPI_CSIS_INTMSK_ERR_LOST_FE (1 << 8)
+#define MIPI_CSIS_INTMSK_ERR_OVER (1 << 4)
+#define MIPI_CSIS_INTMSK_ERR_WRONG_CFG (1 << 3)
+#define MIPI_CSIS_INTMSK_ERR_ECC (1 << 2)
+#define MIPI_CSIS_INTMSK_ERR_CRC (1 << 1)
+#define MIPI_CSIS_INTMSK_ERR_UNKNOWN (1 << 0)
+
+/* CSIS Interrupt source */
+#define MIPI_CSIS_INTSRC 0x14
+#define MIPI_CSIS_INTSRC_EVEN_BEFORE (1 << 31)
+#define MIPI_CSIS_INTSRC_EVEN_AFTER (1 << 30)
+#define MIPI_CSIS_INTSRC_EVEN (0x3 << 30)
+#define MIPI_CSIS_INTSRC_ODD_BEFORE (1 << 29)
+#define MIPI_CSIS_INTSRC_ODD_AFTER (1 << 28)
+#define MIPI_CSIS_INTSRC_ODD (0x3 << 28)
+#define MIPI_CSIS_INTSRC_NON_IMAGE_DATA (0xf << 28)
+#define MIPI_CSIS_INTSRC_FRAME_START (1 << 24)
+#define MIPI_CSIS_INTSRC_FRAME_END (1 << 20)
+#define MIPI_CSIS_INTSRC_ERR_SOT_HS (1 << 16)
+#define MIPI_CSIS_INTSRC_ERR_LOST_FS (1 << 12)
+#define MIPI_CSIS_INTSRC_ERR_LOST_FE (1 << 8)
+#define MIPI_CSIS_INTSRC_ERR_OVER (1 << 4)
+#define MIPI_CSIS_INTSRC_ERR_WRONG_CFG (1 << 3)
+#define MIPI_CSIS_INTSRC_ERR_ECC (1 << 2)
+#define MIPI_CSIS_INTSRC_ERR_CRC (1 << 1)
+#define MIPI_CSIS_INTSRC_ERR_UNKNOWN (1 << 0)
+#define MIPI_CSIS_INTSRC_ERRORS 0xfffff
+
+/* D-PHY status control */
+#define MIPI_CSIS_DPHYSTATUS 0x20
+#define MIPI_CSIS_DPHYSTATUS_ULPS_DAT (1 << 8)
+#define MIPI_CSIS_DPHYSTATUS_STOPSTATE_DAT (1 << 4)
+#define MIPI_CSIS_DPHYSTATUS_ULPS_CLK (1 << 1)
+#define MIPI_CSIS_DPHYSTATUS_STOPSTATE_CLK (1 << 0)
+
+/* D-PHY common control */
+#define MIPI_CSIS_DPHYCTRL 0x24
+#define MIPI_CSIS_DPHYCTRL_HSS_MASK (0xff << 24)
+#define MIPI_CSIS_DPHYCTRL_HSS_OFFSET 24
+#define MIPI_CSIS_DPHYCTRL_SCLKS_MASK (0x3 << 22)
+#define MIPI_CSIS_DPHYCTRL_SCLKS_OFFSET 22
+#define MIPI_CSIS_DPHYCTRL_DPDN_SWAP_CLK (1 << 6)
+#define MIPI_CSIS_DPHYCTRL_DPDN_SWAP_DAT (1 << 5)
+#define MIPI_CSIS_DPHYCTRL_ENABLE_DAT (1 << 1)
+#define MIPI_CSIS_DPHYCTRL_ENABLE_CLK (1 << 0)
+#define MIPI_CSIS_DPHYCTRL_ENABLE (0x1f << 0)
+
+/* D-PHY Master and Slave Control register Low */
+#define MIPI_CSIS_DPHYBCTRL_L 0x30
+/* D-PHY Master and Slave Control register High */
+#define MIPI_CSIS_DPHYBCTRL_H 0x34
+/* D-PHY Slave Control register Low */
+#define MIPI_CSIS_DPHYSCTRL_L 0x38
+/* D-PHY Slave Control register High */
+#define MIPI_CSIS_DPHYSCTRL_H 0x3c
+
+
+/* ISP Configuration register */
+#define MIPI_CSIS_ISPCONFIG_CH0 0x40
+#define MIPI_CSIS_ISPCONFIG_CH1 0x50
+#define MIPI_CSIS_ISPCONFIG_CH2 0x60
+#define MIPI_CSIS_ISPCONFIG_CH3 0x70
+
+#define MIPI_CSIS_ISPCFG_MEM_FULL_GAP_MSK (0xff << 24)
+#define MIPI_CSIS_ISPCFG_MEM_FULL_GAP(x) (x << 24)
+#define MIPI_CSIS_ISPCFG_DOUBLE_CMPNT (1 << 12)
+#define MIPI_CSIS_ISPCFG_ALIGN_32BIT (1 << 11)
+#define MIPI_CSIS_ISPCFG_FMT_YCBCR422_8BIT (0x1e << 2)
+#define MIPI_CSIS_ISPCFG_FMT_RAW8 (0x2a << 2)
+#define MIPI_CSIS_ISPCFG_FMT_RAW10 (0x2b << 2)
+#define MIPI_CSIS_ISPCFG_FMT_RAW12 (0x2c << 2)
+/* User defined formats, x = 1...4 */
+#define MIPI_CSIS_ISPCFG_FMT_USER(x) ((0x30 + x - 1) << 2)
+#define MIPI_CSIS_ISPCFG_FMT_MASK (0x3f << 2)
+
+/* ISP Image Resolution register */
+#define MIPI_CSIS_ISPRESOL_CH0 0x44
+#define MIPI_CSIS_ISPRESOL_CH1 0x54
+#define MIPI_CSIS_ISPRESOL_CH2 0x64
+#define MIPI_CSIS_ISPRESOL_CH3 0x74
+#define CSIS_MAX_PIX_WIDTH 0xffff
+#define CSIS_MAX_PIX_HEIGHT 0xffff
+
+/* ISP SYNC register */
+#define MIPI_CSIS_ISPSYNC_CH0 0x48
+#define MIPI_CSIS_ISPSYNC_CH1 0x58
+#define MIPI_CSIS_ISPSYNC_CH2 0x68
+#define MIPI_CSIS_ISPSYNC_CH3 0x78
+
+#define MIPI_CSIS_ISPSYNC_HSYNC_LINTV_OFFSET 18
+#define MIPI_CSIS_ISPSYNC_VSYNC_SINTV_OFFSET 12
+#define MIPI_CSIS_ISPSYNC_VSYNC_EINTV_OFFSET 0
+
+/* Non-image packet data buffers */
+#define MIPI_CSIS_PKTDATA_ODD 0x2000
+#define MIPI_CSIS_PKTDATA_EVEN 0x3000
+#define MIPI_CSIS_PKTDATA_SIZE SZ_4K
+
+#define DEFAULT_SCLK_CSIS_FREQ 166000000UL
+
+enum {
+ ST_POWERED = 1,
+ ST_STREAMING = 2,
+ ST_SUSPENDED = 4,
+};
+
+struct mipi_csis_event {
+ u32 mask;
+ const char * const name;
+ unsigned int counter;
+};
+
+static const struct mipi_csis_event mipi_csis_events[] = {
+ /* Errors */
+ { MIPI_CSIS_INTSRC_ERR_SOT_HS, "SOT Error" },
+ { MIPI_CSIS_INTSRC_ERR_LOST_FS, "Lost Frame Start Error" },
+ { MIPI_CSIS_INTSRC_ERR_LOST_FE, "Lost Frame End Error" },
+ { MIPI_CSIS_INTSRC_ERR_OVER, "FIFO Overflow Error" },
+ { MIPI_CSIS_INTSRC_ERR_ECC, "ECC Error" },
+ { MIPI_CSIS_INTSRC_ERR_CRC, "CRC Error" },
+ { MIPI_CSIS_INTSRC_ERR_UNKNOWN, "Unknown Error" },
+ /* Non-image data receive events */
+ { MIPI_CSIS_INTSRC_EVEN_BEFORE, "Non-image data before even frame" },
+ { MIPI_CSIS_INTSRC_EVEN_AFTER, "Non-image data after even frame" },
+ { MIPI_CSIS_INTSRC_ODD_BEFORE, "Non-image data before odd frame" },
+ { MIPI_CSIS_INTSRC_ODD_AFTER, "Non-image data after odd frame" },
+ /* Frame start/end */
+ { MIPI_CSIS_INTSRC_FRAME_START, "Frame Start" },
+ { MIPI_CSIS_INTSRC_FRAME_END, "Frame End" },
+};
+#define MIPI_CSIS_NUM_EVENTS ARRAY_SIZE(mipi_csis_events)
+
+struct csis_pktbuf {
+ u32 *data;
+ unsigned int len;
+};
+
+struct csis_hw_reset {
+ struct regmap *src;
+ u8 req_src;
+ u8 rst_bit;
+};
+
+/**
+ * struct csi_state - the driver's internal state data structure
+ * @lock: mutex serializing the subdev and power management operations,
+ * protecting @format and @flags members
+ * @sd: v4l2_subdev associated with CSIS device instance
+ * @index: the hardware instance index
+ * @pdev: CSIS platform device
+ * @phy: pointer to the CSIS generic PHY
+ * @regs: mmaped I/O registers memory
+ * @supplies: CSIS regulator supplies
+ * @clock: CSIS clocks
+ * @irq: requested s5p-mipi-csis irq number
+ * @flags: the state variable for power and streaming control
+ * @clock_frequency: device bus clock frequency
+ * @hs_settle: HS-RX settle time
+ * @clk_settle: Clk settle time
+ * @num_lanes: number of MIPI-CSI data lanes used
+ * @max_num_lanes: maximum number of MIPI-CSI data lanes supported
+ * @wclk_ext: CSI wrapper clock: 0 - bus clock, 1 - external SCLK_CAM
+ * @csis_fmt: current CSIS pixel format
+ * @format: common media bus format for the source and sink pad
+ * @slock: spinlock protecting structure members below
+ * @pkt_buf: the frame embedded (non-image) data buffer
+ * @events: MIPI-CSIS event (error) counters
+ */
+struct csi_state {
+ struct mutex lock;
+ struct device *dev;
+ struct v4l2_subdev mipi_sd;
+ struct v4l2_subdev *sensor_sd;
+ struct v4l2_device v4l2_dev;
+
+ u8 index;
+ struct platform_device *pdev;
+ struct phy *phy;
+ void __iomem *regs;
+ struct clk *mipi_clk;
+ struct clk *phy_clk;
+ int irq;
+ u32 flags;
+
+ u32 clk_frequency;
+ u32 hs_settle;
+ u32 clk_settle;
+ u32 num_lanes;
+ u32 max_num_lanes;
+ u8 wclk_ext;
+
+ const struct csis_pix_format *csis_fmt;
+ struct v4l2_mbus_framefmt format;
+
+ spinlock_t slock;
+ struct csis_pktbuf pkt_buf;
+ struct mipi_csis_event events[MIPI_CSIS_NUM_EVENTS];
+
+ struct v4l2_async_subdev asd;
+ struct v4l2_async_notifier subdev_notifier;
+ struct v4l2_async_subdev *async_subdevs[2];
+
+ struct csis_hw_reset hw_reset;
+ struct regulator *mipi_phy_regulator;
+};
+
+/**
+ * struct csis_pix_format - CSIS pixel format description
+ * @pix_width_alignment: horizontal pixel alignment, width will be
+ * multiple of 2^pix_width_alignment
+ * @code: corresponding media bus code
+ * @fmt_reg: MIPI_CSIS_CONFIG register value
+ * @data_alignment: MIPI-CSI data alignment in bits
+ */
+struct csis_pix_format {
+ unsigned int pix_width_alignment;
+ u32 code;
+ u32 fmt_reg;
+ u8 data_alignment;
+};
+
+static const struct csis_pix_format mipi_csis_formats[] = {
+ {
+ .code = MEDIA_BUS_FMT_YUYV8_2X8,
+ .fmt_reg = MIPI_CSIS_ISPCFG_FMT_YCBCR422_8BIT,
+ .data_alignment = 16,
+ }, {
+ .code = MEDIA_BUS_FMT_VYUY8_2X8,
+ .fmt_reg = MIPI_CSIS_ISPCFG_FMT_YCBCR422_8BIT,
+ .data_alignment = 16,
+ }, {
+ .code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW8,
+ .data_alignment = 8,
+ }
+};
+
+#define mipi_csis_write(__csis, __r, __v) writel(__v, __csis->regs + __r)
+#define mipi_csis_read(__csis, __r) readl(__csis->regs + __r)
+
+static struct csi_state *mipi_sd_to_csi_state(struct v4l2_subdev *sdev)
+{
+ return container_of(sdev, struct csi_state, mipi_sd);
+}
+
+static inline struct csi_state
+ *notifier_to_mipi_dev(struct v4l2_async_notifier *n)
+{
+ return container_of(n, struct csi_state, subdev_notifier);
+}
+
+static const struct csis_pix_format *find_csis_format(u32 code)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mipi_csis_formats); i++)
+ if (code == mipi_csis_formats[i].code)
+ return &mipi_csis_formats[i];
+ return NULL;
+}
+
+static void mipi_csis_enable_interrupts(struct csi_state *state, bool on)
+{
+ u32 val = mipi_csis_read(state, MIPI_CSIS_INTMSK);
+ if (on)
+ val |= 0xf00fffff;
+ else
+ val &= ~0xf00fffff;
+ mipi_csis_write(state, MIPI_CSIS_INTMSK, val);
+}
+
+static void mipi_csis_sw_reset(struct csi_state *state)
+{
+ u32 val = mipi_csis_read(state, MIPI_CSIS_CMN_CTRL);
+
+ mipi_csis_write(state, MIPI_CSIS_CMN_CTRL, val | MIPI_CSIS_CMN_CTRL_RESET);
+ udelay(10);
+}
+
+static int mipi_csis_phy_init(struct csi_state *state)
+{
+ int ret;
+
+ state->mipi_phy_regulator = devm_regulator_get(state->dev,
+ "mipi-phy");
+
+ ret = regulator_set_voltage(state->mipi_phy_regulator,
+ 1000000, 1000000);
+
+ return ret;
+}
+
+static int mipi_csis_phy_reset(struct csi_state *state)
+{
+ struct device_node *np = state->dev->of_node;
+ struct device_node *node;
+ phandle phandle;
+ u32 out_val[3];
+ int ret;
+
+ ret = of_property_read_u32_array(np, "csis-phy-reset", out_val, 3);
+ if (ret) {
+ dev_dbg(state->dev, "no csis-hw-reset property found\n");
+ } else {
+ phandle = *out_val;
+
+ node = of_find_node_by_phandle(phandle);
+ if (!node) {
+ dev_dbg(state->dev, "not find src node by phandle\n");
+ ret = PTR_ERR(node);
+ }
+ state->hw_reset.src = syscon_node_to_regmap(node);
+ if (IS_ERR(state->hw_reset.src)) {
+ dev_err(state->dev, "failed to get src regmap\n");
+ ret = PTR_ERR(state->hw_reset.src);
+ }
+ of_node_put(node);
+ if (ret < 0)
+ return ret;
+
+ state->hw_reset.req_src = out_val[1];
+ state->hw_reset.rst_bit = out_val[2];
+
+ /* reset mipi phy */
+ regmap_update_bits(state->hw_reset.src, state->hw_reset.req_src,
+ 1 << state->hw_reset.rst_bit, 1 << state->hw_reset.rst_bit);
+ msleep(20);
+ regmap_update_bits(state->hw_reset.src, state->hw_reset.req_src,
+ 1 << state->hw_reset.rst_bit, 0);
+
+ }
+ return ret;
+}
+
+static void mipi_csis_system_enable(struct csi_state *state, int on)
+{
+ u32 val, mask;
+
+ val = mipi_csis_read(state, MIPI_CSIS_CMN_CTRL);
+ if (on)
+ val |= MIPI_CSIS_CMN_CTRL_ENABLE;
+ else
+ val &= ~MIPI_CSIS_CMN_CTRL_ENABLE;
+ mipi_csis_write(state, MIPI_CSIS_CMN_CTRL, val);
+
+ val = mipi_csis_read(state, MIPI_CSIS_DPHYCTRL);
+ val &= ~MIPI_CSIS_DPHYCTRL_ENABLE;
+ if (on) {
+ mask = (1 << (state->num_lanes + 1)) - 1;
+ val |= (mask & MIPI_CSIS_DPHYCTRL_ENABLE);
+ }
+ mipi_csis_write(state, MIPI_CSIS_DPHYCTRL, val);
+}
+
+/* Called with the state.lock mutex held */
+static void __mipi_csis_set_format(struct csi_state *state)
+{
+ struct v4l2_mbus_framefmt *mf = &state->format;
+ u32 val;
+
+ v4l2_dbg(1, debug, &state->mipi_sd, "fmt: %#x, %d x %d\n",
+ mf->code, mf->width, mf->height);
+
+ /* Color format */
+ val = mipi_csis_read(state, MIPI_CSIS_ISPCONFIG_CH0);
+ val = (val & ~MIPI_CSIS_ISPCFG_FMT_MASK) | state->csis_fmt->fmt_reg;
+ mipi_csis_write(state, MIPI_CSIS_ISPCONFIG_CH0, val);
+
+ /* Pixel resolution */
+ val = mf->width | (mf->height << 16);
+ mipi_csis_write(state, MIPI_CSIS_ISPRESOL_CH0, val);
+}
+
+static void mipi_csis_set_hsync_settle(struct csi_state *state,
+ int hs_settle, int clk_settle)
+{
+ u32 val = mipi_csis_read(state, MIPI_CSIS_DPHYCTRL);
+
+ val = (val & ~MIPI_CSIS_DPHYCTRL_HSS_MASK) |
+ (hs_settle << 24) | (clk_settle << 22);
+
+ mipi_csis_write(state, MIPI_CSIS_DPHYCTRL, val);
+}
+
+static void mipi_csis_set_params(struct csi_state *state)
+{
+ u32 val;
+
+ val = mipi_csis_read(state, MIPI_CSIS_CMN_CTRL);
+ val &= ~MIPI_CSIS_CMN_CTRL_LANE_NR_MASK;
+ val |= (state->num_lanes - 1) << MIPI_CSIS_CMN_CTRL_LANE_NR_OFFSET;
+ mipi_csis_write(state, MIPI_CSIS_CMN_CTRL, val);
+
+ __mipi_csis_set_format(state);
+
+ mipi_csis_set_hsync_settle(state, state->hs_settle, state->clk_settle);
+
+ val = mipi_csis_read(state, MIPI_CSIS_ISPCONFIG_CH0);
+ if (state->csis_fmt->data_alignment == 32)
+ val |= MIPI_CSIS_ISPCFG_ALIGN_32BIT;
+ else /* Normal output */
+ val &= ~MIPI_CSIS_ISPCFG_ALIGN_32BIT;
+ mipi_csis_write(state, MIPI_CSIS_ISPCONFIG_CH0, val);
+
+ val = (0 << MIPI_CSIS_ISPSYNC_HSYNC_LINTV_OFFSET) |
+ (0 << MIPI_CSIS_ISPSYNC_VSYNC_SINTV_OFFSET) |
+ (0 << MIPI_CSIS_ISPSYNC_VSYNC_EINTV_OFFSET);
+ mipi_csis_write(state, MIPI_CSIS_ISPSYNC_CH0, val);
+
+ val = mipi_csis_read(state, MIPI_CSIS_CLK_CTRL);
+ val &= ~MIPI_CSIS_CLK_CTRL_WCLK_SRC;
+ if (state->wclk_ext)
+ val |= MIPI_CSIS_CLK_CTRL_WCLK_SRC;
+ val |= MIPI_CSIS_CLK_CTRL_CLKGATE_TRAIL_CH0(15);
+ val &= ~MIPI_CSIS_CLK_CTRL_CLKGATE_EN_MSK;
+ mipi_csis_write(state, MIPI_CSIS_CLK_CTRL, val);
+
+ mipi_csis_write(state, MIPI_CSIS_DPHYBCTRL_L, 0x1f4);
+ mipi_csis_write(state, MIPI_CSIS_DPHYBCTRL_H, 0);
+
+ /* Update the shadow register. */
+ val = mipi_csis_read(state, MIPI_CSIS_CMN_CTRL);
+ mipi_csis_write(state, MIPI_CSIS_CMN_CTRL, val | MIPI_CSIS_CMN_CTRL_UPDATE_SHADOW |
+ MIPI_CSIS_CMN_CTRL_UPDATE_SHADOW_CTRL);
+}
+
+static void mipi_csis_clk_enable(struct csi_state *state)
+{
+ clk_prepare_enable(state->mipi_clk);
+ clk_prepare_enable(state->phy_clk);
+}
+
+static void mipi_csis_clk_disable(struct csi_state *state)
+{
+ clk_disable_unprepare(state->mipi_clk);
+ clk_disable_unprepare(state->phy_clk);
+}
+
+static int mipi_csis_clk_get(struct csi_state *state)
+{
+ struct device *dev = &state->pdev->dev;
+ int ret = true;
+
+ state->mipi_clk = devm_clk_get(dev, "mipi_clk");
+ if (IS_ERR(state->mipi_clk)) {
+ dev_err(dev, "Could not get mipi csi clock\n");
+ return -ENODEV;
+ }
+
+ state->phy_clk = devm_clk_get(dev, "phy_clk");
+ if (IS_ERR(state->phy_clk)) {
+ dev_err(dev, "Could not get mipi phy clock\n");
+ return -ENODEV;
+ }
+
+ /* Set clock rate */
+ if (state->clk_frequency)
+ ret = clk_set_rate(state->mipi_clk,
+ state->clk_frequency);
+ else
+ dev_WARN(dev, "No clock frequency specified!\n");
+ if (ret < 0) {
+ dev_err(dev, "set rate filed, rate=%d\n", state->clk_frequency);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static void dump_regs(struct csi_state *state, const char *label)
+{
+ struct {
+ u32 offset;
+ const char * const name;
+ } registers[] = {
+ { 0x00, "CTRL" },
+ { 0x04, "DPHYCTRL" },
+ { 0x08, "CONFIG" },
+ { 0x0c, "DPHYSTS" },
+ { 0x10, "INTMSK" },
+ { 0x2c, "RESOL" },
+ { 0x38, "SDW_CONFIG" },
+ };
+ u32 i;
+
+ v4l2_info(&state->mipi_sd, "--- %s ---\n", label);
+
+ for (i = 0; i < ARRAY_SIZE(registers); i++) {
+ u32 cfg = mipi_csis_read(state, registers[i].offset);
+ v4l2_info(&state->mipi_sd, "%10s: 0x%08x\n", registers[i].name, cfg);
+ }
+}
+
+static void mipi_csis_start_stream(struct csi_state *state)
+{
+ mipi_csis_sw_reset(state);
+ mipi_csis_set_params(state);
+ mipi_csis_system_enable(state, true);
+ mipi_csis_enable_interrupts(state, true);
+}
+
+static void mipi_csis_stop_stream(struct csi_state *state)
+{
+ mipi_csis_enable_interrupts(state, false);
+ mipi_csis_system_enable(state, false);
+}
+
+static void mipi_csis_clear_counters(struct csi_state *state)
+{
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&state->slock, flags);
+ for (i = 0; i < MIPI_CSIS_NUM_EVENTS; i++)
+ state->events[i].counter = 0;
+ spin_unlock_irqrestore(&state->slock, flags);
+}
+
+static void mipi_csis_log_counters(struct csi_state *state, bool non_errors)
+{
+ int i = non_errors ? MIPI_CSIS_NUM_EVENTS : MIPI_CSIS_NUM_EVENTS - 4;
+ unsigned long flags;
+
+ spin_lock_irqsave(&state->slock, flags);
+
+ for (i--; i >= 0; i--) {
+ if (state->events[i].counter > 0 || debug)
+ v4l2_info(&state->mipi_sd, "%s events: %d\n",
+ state->events[i].name,
+ state->events[i].counter);
+ }
+ spin_unlock_irqrestore(&state->slock, flags);
+}
+
+/*
+ * V4L2 subdev operations
+ */
+static int mipi_csis_s_power(struct v4l2_subdev *mipi_sd, int on)
+{
+ struct csi_state *state = mipi_sd_to_csi_state(mipi_sd);
+ struct device *dev = &state->pdev->dev;
+
+ v4l2_subdev_call(state->sensor_sd, core, s_power, on);
+
+ if (on)
+ return pm_runtime_get_sync(dev);
+
+ return pm_runtime_put_sync(dev);
+}
+
+static int mipi_csis_s_stream(struct v4l2_subdev *mipi_sd, int enable)
+{
+ struct csi_state *state = mipi_sd_to_csi_state(mipi_sd);
+ int ret = 0;
+
+ v4l2_dbg(1, debug, mipi_sd, "%s: %d, state: 0x%x\n",
+ __func__, enable, state->flags);
+
+ if (enable) {
+ mipi_csis_clear_counters(state);
+ ret = pm_runtime_get_sync(&state->pdev->dev);
+ if (ret && ret != 1)
+ return ret;
+ }
+
+ mutex_lock(&state->lock);
+ if (enable) {
+ if (state->flags & ST_SUSPENDED) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+ mipi_csis_start_stream(state);
+ v4l2_subdev_call(state->sensor_sd, video, s_stream, true);
+ state->flags |= ST_STREAMING;
+ } else {
+ v4l2_subdev_call(state->sensor_sd, video, s_stream, false);
+ mipi_csis_stop_stream(state);
+ state->flags &= ~ST_STREAMING;
+ if (debug > 0)
+ mipi_csis_log_counters(state, true);
+ }
+unlock:
+ mutex_unlock(&state->lock);
+ if (!enable)
+ pm_runtime_put(&state->pdev->dev);
+
+ return ret == 1 ? 0 : ret;
+}
+
+static int mipi_csis_enum_mbus_code(struct v4l2_subdev *mipi_sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct csi_state *state = mipi_sd_to_csi_state(mipi_sd);
+ struct v4l2_subdev *sensor_sd = state->sensor_sd;
+ struct csis_pix_format const *csis_fmt;
+ int ret;
+
+ ret = v4l2_subdev_call(sensor_sd, pad, enum_mbus_code, NULL, code);
+ if (ret < 0)
+ return -EINVAL;
+
+ csis_fmt = find_csis_format(code->code);
+ if (csis_fmt == NULL) {
+ dev_err(state->dev, "format not match\n");
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int mipi_csis_set_fmt(struct v4l2_subdev *mipi_sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
+{
+ struct csi_state *state = mipi_sd_to_csi_state(mipi_sd);
+ struct v4l2_subdev *sensor_sd = state->sensor_sd;
+ struct csis_pix_format const *csis_fmt;
+ struct v4l2_mbus_framefmt *mf = &format->format;
+
+ if (format->pad)
+ return -EINVAL;
+
+ csis_fmt = find_csis_format(mf->code);
+ if (csis_fmt == NULL)
+ csis_fmt = &mipi_csis_formats[0];
+
+ v4l2_subdev_call(sensor_sd, pad, set_fmt, NULL, format);
+
+ mf->code = csis_fmt->code;
+ v4l_bound_align_image(&mf->width, 1, CSIS_MAX_PIX_WIDTH,
+ csis_fmt->pix_width_alignment,
+ &mf->height, 1, CSIS_MAX_PIX_HEIGHT, 1,
+ 0);
+
+ if (format->which == V4L2_SUBDEV_FORMAT_TRY)
+ return 0;
+
+ state->format.code = mf->code;
+ state->format.width = mf->width;
+ state->format.height = mf->height;
+
+ mutex_lock(&state->lock);
+ state->csis_fmt = csis_fmt;
+ mutex_unlock(&state->lock);
+
+ return 0;
+}
+
+static int mipi_csis_get_fmt(struct v4l2_subdev *mipi_sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
+{
+ struct csi_state *state = mipi_sd_to_csi_state(mipi_sd);
+ struct v4l2_subdev *sensor_sd = state->sensor_sd;
+
+ if (format->pad)
+ return -EINVAL;
+
+ return v4l2_subdev_call(sensor_sd, pad, get_fmt, NULL, format);
+}
+
+static int mipi_csis_s_rx_buffer(struct v4l2_subdev *mipi_sd, void *buf,
+ unsigned int *size)
+{
+ struct csi_state *state = mipi_sd_to_csi_state(mipi_sd);
+ unsigned long flags;
+
+ *size = min_t(unsigned int, *size, MIPI_CSIS_PKTDATA_SIZE);
+
+ spin_lock_irqsave(&state->slock, flags);
+ state->pkt_buf.data = buf;
+ state->pkt_buf.len = *size;
+ spin_unlock_irqrestore(&state->slock, flags);
+
+ return 0;
+}
+
+static int mipi_csis_s_parm(struct v4l2_subdev *mipi_sd, struct v4l2_streamparm *a)
+{
+ struct csi_state *state = mipi_sd_to_csi_state(mipi_sd);
+ struct v4l2_subdev *sensor_sd = state->sensor_sd;
+
+ return v4l2_subdev_call(sensor_sd, video, s_parm, a);
+}
+
+static int mipi_csis_g_parm(struct v4l2_subdev *mipi_sd, struct v4l2_streamparm *a)
+{
+ struct csi_state *state = mipi_sd_to_csi_state(mipi_sd);
+ struct v4l2_subdev *sensor_sd = state->sensor_sd;
+
+ return v4l2_subdev_call(sensor_sd, video, g_parm, a);
+}
+
+static int mipi_csis_enum_framesizes(struct v4l2_subdev *mipi_sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct csi_state *state = mipi_sd_to_csi_state(mipi_sd);
+ struct v4l2_subdev *sensor_sd = state->sensor_sd;
+
+ return v4l2_subdev_call(sensor_sd, pad, enum_frame_size, NULL, fse);
+}
+
+static int mipi_csis_enum_frameintervals(struct v4l2_subdev *mipi_sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_interval_enum *fie)
+{
+ struct csi_state *state = mipi_sd_to_csi_state(mipi_sd);
+ struct v4l2_subdev *sensor_sd = state->sensor_sd;
+
+ return v4l2_subdev_call(sensor_sd, pad, enum_frame_interval, NULL, fie);
+}
+
+static int mipi_csis_log_status(struct v4l2_subdev *mipi_sd)
+{
+ struct csi_state *state = mipi_sd_to_csi_state(mipi_sd);
+
+ mutex_lock(&state->lock);
+ mipi_csis_log_counters(state, true);
+ if (debug && (state->flags & ST_POWERED))
+ dump_regs(state, __func__);
+ mutex_unlock(&state->lock);
+ return 0;
+}
+
+static struct v4l2_subdev_core_ops mipi_csis_core_ops = {
+ .s_power = mipi_csis_s_power,
+ .log_status = mipi_csis_log_status,
+};
+
+static struct v4l2_subdev_video_ops mipi_csis_video_ops = {
+ .s_rx_buffer = mipi_csis_s_rx_buffer,
+ .s_stream = mipi_csis_s_stream,
+
+ .s_parm = mipi_csis_s_parm,
+ .g_parm = mipi_csis_g_parm,
+};
+
+static const struct v4l2_subdev_pad_ops mipi_csis_pad_ops = {
+ .enum_frame_size = mipi_csis_enum_framesizes,
+ .enum_frame_interval = mipi_csis_enum_frameintervals,
+ .enum_mbus_code = mipi_csis_enum_mbus_code,
+ .get_fmt = mipi_csis_get_fmt,
+ .set_fmt = mipi_csis_set_fmt,
+};
+
+static struct v4l2_subdev_ops mipi_csis_subdev_ops = {
+ .core = &mipi_csis_core_ops,
+ .video = &mipi_csis_video_ops,
+ .pad = &mipi_csis_pad_ops,
+};
+
+static irqreturn_t mipi_csis_irq_handler(int irq, void *dev_id)
+{
+ struct csi_state *state = dev_id;
+ struct csis_pktbuf *pktbuf = &state->pkt_buf;
+ unsigned long flags;
+ u32 status;
+
+ status = mipi_csis_read(state, MIPI_CSIS_INTSRC);
+
+ spin_lock_irqsave(&state->slock, flags);
+
+ if ((status & MIPI_CSIS_INTSRC_NON_IMAGE_DATA) && pktbuf->data) {
+ u32 offset;
+
+ if (status & MIPI_CSIS_INTSRC_EVEN)
+ offset = MIPI_CSIS_PKTDATA_EVEN;
+ else
+ offset = MIPI_CSIS_PKTDATA_ODD;
+
+ memcpy(pktbuf->data, state->regs + offset, pktbuf->len);
+ pktbuf->data = NULL;
+ rmb();
+ }
+
+ /* Update the event/error counters */
+ if ((status & MIPI_CSIS_INTSRC_ERRORS) || debug) {
+ int i;
+ for (i = 0; i < MIPI_CSIS_NUM_EVENTS; i++) {
+ if (!(status & state->events[i].mask))
+ continue;
+ state->events[i].counter++;
+ v4l2_dbg(2, debug, &state->mipi_sd, "%s: %d\n",
+ state->events[i].name,
+ state->events[i].counter);
+ }
+ v4l2_dbg(2, debug, &state->mipi_sd, "status: %08x\n", status);
+ }
+ spin_unlock_irqrestore(&state->slock, flags);
+
+ mipi_csis_write(state, MIPI_CSIS_INTSRC, status);
+ return IRQ_HANDLED;
+}
+
+static int subdev_notifier_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_subdev *asd)
+{
+ struct csi_state *state = notifier_to_mipi_dev(notifier);
+
+ /* Find platform data for this sensor subdev */
+ if (state->asd.match.of.node == subdev->dev->of_node)
+ state->sensor_sd = subdev;
+
+ if (subdev == NULL)
+ return -EINVAL;
+
+ v4l2_info(&state->v4l2_dev, "Registered sensor subdevice: %s\n",
+ subdev->name);
+
+ return 0;
+}
+
+static int mipi_csis_parse_dt(struct platform_device *pdev,
+ struct csi_state *state)
+{
+ struct device_node *node = pdev->dev.of_node;
+
+ if (of_property_read_u32(node, "clock-frequency",
+ &state->clk_frequency))
+ state->clk_frequency = DEFAULT_SCLK_CSIS_FREQ;
+ if (of_property_read_u32(node, "bus-width",
+ &state->max_num_lanes))
+ return -EINVAL;
+
+ node = of_graph_get_next_endpoint(node, NULL);
+ if (!node) {
+ dev_err(&pdev->dev, "No port node at %s\n",
+ pdev->dev.of_node->full_name);
+ return -EINVAL;
+ }
+
+ /* Get MIPI CSI-2 bus configration from the endpoint node. */
+ of_property_read_u32(node, "csis-hs-settle",
+ &state->hs_settle);
+
+ of_property_read_u32(node, "csis-clk-settle",
+ &state->clk_settle);
+ state->wclk_ext = of_property_read_bool(node,
+ "csis-wclk");
+
+ of_property_read_u32(node, "data-lanes",
+ &state->num_lanes);
+ of_node_put(node);
+
+ return 0;
+}
+
+static int mipi_csis_pm_resume(struct device *dev, bool runtime);
+static const struct of_device_id mipi_csis_of_match[];
+
+/* register parent dev */
+static int mipi_csis_subdev_host(struct csi_state *state)
+{
+ struct device_node *parent = state->dev->of_node;
+ struct device_node *node, *port, *rem;
+ int ret;
+
+ /* Attach sensors linked to csi receivers */
+ for_each_available_child_of_node(parent, node) {
+ if (of_node_cmp(node->name, "port"))
+ continue;
+
+ /* The csi node can have only port subnode. */
+ port = of_get_next_child(node, NULL);
+ if (!port)
+ continue;
+ rem = of_graph_get_remote_port_parent(port);
+ of_node_put(port);
+ if (rem == NULL) {
+ v4l2_info(&state->v4l2_dev,
+ "Remote device at %s not found\n",
+ port->full_name);
+ return -1;
+ }
+
+ state->asd.match_type = V4L2_ASYNC_MATCH_OF;
+ state->asd.match.of.node = rem;
+ state->async_subdevs[0] = &state->asd;
+
+ of_node_put(rem);
+ break;
+ }
+
+ state->subdev_notifier.subdevs = state->async_subdevs;
+ state->subdev_notifier.num_subdevs = 1;
+ state->subdev_notifier.bound = subdev_notifier_bound;
+
+ ret = v4l2_async_notifier_register(&state->v4l2_dev,
+ &state->subdev_notifier);
+ if (ret)
+ dev_err(state->dev,
+ "Error register async notifier regoster\n");
+
+ return ret;
+}
+
+/* init subdev */
+static int mipi_csis_subdev_init(struct v4l2_subdev *mipi_sd,
+ struct platform_device *pdev,
+ const struct v4l2_subdev_ops *ops)
+{
+ struct csi_state *state = platform_get_drvdata(pdev);
+ int ret = 0;
+
+ v4l2_subdev_init(mipi_sd, ops);
+ mipi_sd->owner = THIS_MODULE;
+ snprintf(mipi_sd->name, sizeof(mipi_sd->name), "%s.%d",
+ CSIS_SUBDEV_NAME, state->index);
+ mipi_sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ mipi_sd->dev = &pdev->dev;
+
+ state->csis_fmt = &mipi_csis_formats[0];
+ state->format.code = mipi_csis_formats[0].code;
+ state->format.width = MIPI_CSIS_DEF_PIX_WIDTH;
+ state->format.height = MIPI_CSIS_DEF_PIX_HEIGHT;
+
+ /* This allows to retrieve the platform device id by the host driver */
+ v4l2_set_subdevdata(mipi_sd, pdev);
+
+ ret = v4l2_async_register_subdev(mipi_sd);
+ if (ret < 0)
+ dev_err(&pdev->dev, "%s--Async register faialed, ret=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int mipi_csis_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct v4l2_subdev *mipi_sd;
+ struct resource *mem_res;
+ struct csi_state *state;
+ int ret = -ENOMEM;
+
+ state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
+ if (!state)
+ return -ENOMEM;
+
+ mutex_init(&state->lock);
+ spin_lock_init(&state->slock);
+
+ state->pdev = pdev;
+ mipi_sd = &state->mipi_sd;
+ state->dev = dev;
+
+ ret = mipi_csis_parse_dt(pdev, state);
+ if (ret < 0)
+ return ret;
+
+ if (state->num_lanes == 0 || state->num_lanes > state->max_num_lanes) {
+ dev_err(dev, "Unsupported number of data lanes: %d (max. %d)\n",
+ state->num_lanes, state->max_num_lanes);
+ return -EINVAL;
+ }
+
+ mipi_csis_phy_init(state);
+ mipi_csis_phy_reset(state);
+
+ mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ state->regs = devm_ioremap_resource(dev, mem_res);
+ if (IS_ERR(state->regs))
+ return PTR_ERR(state->regs);
+
+ state->irq = platform_get_irq(pdev, 0);
+ if (state->irq < 0) {
+ dev_err(dev, "Failed to get irq\n");
+ return state->irq;
+ }
+
+ ret = mipi_csis_clk_get(state);
+ if (ret < 0)
+ return ret;
+
+ mipi_csis_clk_enable(state);
+
+ ret = devm_request_irq(dev, state->irq, mipi_csis_irq_handler,
+ 0, dev_name(dev), state);
+ if (ret) {
+ dev_err(dev, "Interrupt request failed\n");
+ goto e_clkdis;
+ }
+
+ /* First register a v4l2 device */
+ ret = v4l2_device_register(dev, &state->v4l2_dev);
+ if (ret) {
+ v4l2_err(dev->driver,
+ "Unable to register v4l2 device.\n");
+ goto e_clkdis;
+ }
+ v4l2_info(&state->v4l2_dev, "mipi csi v4l2 device registered\n");
+
+ /* .. and a pointer to the subdev. */
+ platform_set_drvdata(pdev, state);
+
+ ret = mipi_csis_subdev_init(&state->mipi_sd, pdev, &mipi_csis_subdev_ops);
+ if (ret < 0)
+ goto e_sd_mipi;
+
+ memcpy(state->events, mipi_csis_events, sizeof(state->events));
+
+ /* subdev host register */
+ ret = mipi_csis_subdev_host(state);
+ if (ret < 0)
+ goto e_sd_host;
+
+ pm_runtime_enable(dev);
+ if (!pm_runtime_enabled(dev)) {
+ ret = mipi_csis_pm_resume(dev, true);
+ if (ret < 0)
+ goto e_sd_host;
+ }
+
+ dev_info(&pdev->dev,
+ "lanes: %d, hs_settle: %d, clk_settle: %d, wclk: %d, freq: %u\n",
+ state->num_lanes, state->hs_settle, state->clk_settle,
+ state->wclk_ext, state->clk_frequency);
+ return 0;
+
+e_sd_host:
+ v4l2_async_notifier_unregister(&state->subdev_notifier);
+ v4l2_device_unregister(&state->v4l2_dev);
+e_sd_mipi:
+ v4l2_async_unregister_subdev(&state->mipi_sd);
+e_clkdis:
+ mipi_csis_clk_disable(state);
+ return ret;
+}
+
+static int mipi_csis_pm_suspend(struct device *dev, bool runtime)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct csi_state *state = platform_get_drvdata(pdev);
+ struct v4l2_subdev *mipi_sd = &state->mipi_sd;
+ int ret = 0;
+
+ v4l2_dbg(1, debug, mipi_sd, "%s: flags: 0x%x\n",
+ __func__, state->flags);
+
+ mutex_lock(&state->lock);
+ if (state->flags & ST_POWERED) {
+ mipi_csis_stop_stream(state);
+ ret = regulator_disable(state->mipi_phy_regulator);
+ if (ret)
+ goto unlock;
+ mipi_csis_clk_disable(state);
+ state->flags &= ~ST_POWERED;
+ if (!runtime)
+ state->flags |= ST_SUSPENDED;
+ }
+ unlock:
+ mutex_unlock(&state->lock);
+ return ret ? -EAGAIN : 0;
+}
+
+static int mipi_csis_pm_resume(struct device *dev, bool runtime)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct csi_state *state = platform_get_drvdata(pdev);
+ struct v4l2_subdev *mipi_sd = &state->mipi_sd;
+ int ret = 0;
+
+ v4l2_dbg(1, debug, mipi_sd, "%s: flags: 0x%x\n",
+ __func__, state->flags);
+
+ mutex_lock(&state->lock);
+ if (!runtime && !(state->flags & ST_SUSPENDED))
+ goto unlock;
+
+ if (!(state->flags & ST_POWERED)) {
+ ret = regulator_enable(state->mipi_phy_regulator);
+ if (!ret) {
+ state->flags |= ST_POWERED;
+ } else {
+ goto unlock;
+ }
+ mipi_csis_clk_enable(state);
+ }
+ if (state->flags & ST_STREAMING)
+ mipi_csis_start_stream(state);
+
+ state->flags &= ~ST_SUSPENDED;
+ unlock:
+ mutex_unlock(&state->lock);
+ return ret ? -EAGAIN : 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int mipi_csis_suspend(struct device *dev)
+{
+ return mipi_csis_pm_suspend(dev, false);
+}
+
+static int mipi_csis_resume(struct device *dev)
+{
+ return mipi_csis_pm_resume(dev, false);
+}
+#endif
+
+static int mipi_csis_runtime_suspend(struct device *dev)
+{
+ return mipi_csis_pm_suspend(dev, true);
+}
+
+static int mipi_csis_runtime_resume(struct device *dev)
+{
+ return mipi_csis_pm_resume(dev, true);
+}
+
+static int mipi_csis_remove(struct platform_device *pdev)
+{
+ struct csi_state *state = platform_get_drvdata(pdev);
+
+ v4l2_async_unregister_subdev(&state->mipi_sd);
+ v4l2_async_notifier_unregister(&state->subdev_notifier);
+ v4l2_device_unregister(&state->v4l2_dev);
+
+ pm_runtime_disable(&pdev->dev);
+ mipi_csis_pm_suspend(&pdev->dev, true);
+ mipi_csis_clk_disable(state);
+ pm_runtime_set_suspended(&pdev->dev);
+
+ return 0;
+}
+
+static const struct dev_pm_ops mipi_csis_pm_ops = {
+ SET_RUNTIME_PM_OPS(mipi_csis_runtime_suspend, mipi_csis_runtime_resume,
+ NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(mipi_csis_suspend, mipi_csis_resume)
+};
+
+static const struct of_device_id mipi_csis_of_match[] = {
+ { .compatible = "fsl,imx7d-mipi-csi",},
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, mipi_csis_of_match);
+
+static struct platform_driver mipi_csis_driver = {
+ .probe = mipi_csis_probe,
+ .remove = mipi_csis_remove,
+ .driver = {
+ .of_match_table = mipi_csis_of_match,
+ .name = CSIS_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .pm = &mipi_csis_pm_ops,
+ },
+};
+
+module_platform_driver(mipi_csis_driver);
+
+MODULE_DESCRIPTION("Freescale MIPI-CSI2 receiver driver");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * Copyright (C) 2014-2015 Freescale Semiconductor, 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/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/media-bus-format.h>
+#include <media/v4l2-ioctl.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+#include "mxc_vadc.h"
+
+/* Resource names for the VADC driver. */
+#define VAFE_REGS_ADDR_RES_NAME "vadc-vafe"
+#define VDEC_REGS_ADDR_RES_NAME "vadc-vdec"
+
+#define reg32_write(addr, val) __raw_writel(val, addr)
+#define reg32_read(addr) __raw_readl(addr)
+#define reg32setbit(addr, bitpos) \
+ reg32_write((addr), (reg32_read((addr)) | (1<<(bitpos))))
+
+#define reg32clrbit(addr, bitpos) \
+ reg32_write((addr), (reg32_read((addr)) & (0xFFFFFFFF ^ (1<<(bitpos)))))
+
+#define GPC_CNTR 0x00
+#define IMX6SX_GPC_CNTR_VADC_ANALOG_OFF_MASK BIT(17)
+#define IMX6SX_GPC_CNTR_VADC_POWER_DOWN_MASK BIT(18)
+
+void __iomem *vafe_regbase;
+void __iomem *vdec_regbase;
+
+
+/* List of input video formats supported. The video formats is corresponding
+ * with v4l2 id in video_fmt
+ */
+enum video_fmt_idx {
+ VADC_NTSC = 0, /* Locked on (M) NTSC video signal. */
+ VADC_PAL, /* (B, G, H, I, N)PAL video signal. */
+};
+
+/* Number of video standards supported (including 'not locked' signal). */
+#define VADC_STD_MAX (VADC_PAL + 1)
+
+/* Video format structure. */
+struct video_fmt{
+ v4l2_std_id v4l2_std; /* 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 framerates;
+};
+
+/*
+ * Maintains the information on the current state of the sensor.
+ */
+struct vadc_state {
+ struct v4l2_device v4l2_dev;
+ struct v4l2_subdev sd;
+ struct video_fmt *fmt;
+
+ struct clk *vadc_clk;
+ struct clk *csi_clk;
+ struct regmap *gpr;
+ void __iomem *gpc_reg;
+
+ u32 vadc_in;
+ u32 csi_id;
+};
+
+static int vadc_querystd(struct v4l2_subdev *sd, v4l2_std_id *std);
+
+/* Description of video formats supported.
+ *
+ * PAL: raw=720x625, active=720x576.
+ * NTSC: raw=720x525, active=720x480.
+ */
+static struct video_fmt video_fmts[] = {
+ /* NTSC */
+ {
+ .v4l2_std = V4L2_STD_NTSC,
+ .name = "NTSC",
+ .raw_width = 720,
+ .raw_height = 525,
+ .active_width = 720,
+ .active_height = 480,
+ .framerates = 30,
+ },
+ /* (B, G, H, I, N) PAL */
+ {
+ .v4l2_std = V4L2_STD_PAL,
+ .name = "PAL",
+ .raw_width = 720,
+ .raw_height = 625,
+ .active_width = 720,
+ .active_height = 576,
+ .framerates = 25,
+ },
+};
+
+static void afe_voltage_clampingmode(void)
+{
+ reg32_write(AFE_CLAMP, 0x07);
+ reg32_write(AFE_CLMPAMP, 0x60);
+ reg32_write(AFE_CLMPDAT, 0xF0);
+}
+
+static void afe_alwayson_clampingmode(void)
+{
+ reg32_write(AFE_CLAMP, 0x15);
+ reg32_write(AFE_CLMPDAT, 0x08);
+ reg32_write(AFE_CLMPAMP, 0x00);
+}
+
+static void afe_init(void)
+{
+ pr_debug("%s\n", __func__);
+
+ reg32_write(AFE_PDBUF, 0x1f);
+ reg32_write(AFE_PDADC, 0x0f);
+ reg32_write(AFE_PDSARH, 0x01);
+ reg32_write(AFE_PDSARL, 0xff);
+ reg32_write(AFE_PDADCRFH, 0x01);
+ reg32_write(AFE_PDADCRFL, 0xff);
+ reg32_write(AFE_ICTRL, 0x3a);
+ reg32_write(AFE_ICTLSTG, 0x1e);
+
+ reg32_write(AFE_RCTRLSTG, 0x1e);
+ reg32_write(AFE_INPBUF, 0x035);
+ reg32_write(AFE_INPFLT, 0x02);
+ reg32_write(AFE_ADCDGN, 0x40);
+ reg32_write(AFE_TSTSEL, 0x10);
+
+ reg32_write(AFE_ACCTST, 0x07);
+
+ reg32_write(AFE_BGREG, 0x08);
+
+ reg32_write(AFE_ADCGN, 0x09);
+
+ /* set current controlled clamping
+ * always on, low current */
+ reg32_write(AFE_CLAMP, 0x11);
+ reg32_write(AFE_CLMPAMP, 0x08);
+}
+
+static void vdec_mode_timing_init(int std)
+{
+ if (std == V4L2_STD_NTSC) {
+ /* NTSC 720x480 */
+ reg32_write(VDEC_HACTS, 0x66);
+ reg32_write(VDEC_HACTE, 0x24);
+
+ reg32_write(VDEC_VACTS, 0x29);
+ reg32_write(VDEC_VACTE, 0x04);
+
+ /* set V Position */
+ reg32_write(VDEC_VRTPOS, 0x2);
+ } else if (std == V4L2_STD_PAL) {
+ /* PAL 720x576 */
+ reg32_write(VDEC_HACTS, 0x66);
+ reg32_write(VDEC_HACTE, 0x24);
+
+ reg32_write(VDEC_VACTS, 0x29);
+ reg32_write(VDEC_VACTE, 0x04);
+
+ /* set V Position */
+ reg32_write(VDEC_VRTPOS, 0x6);
+ } else
+ pr_debug("Error not support video mode\n");
+
+ /* set H Position */
+ reg32_write(VDEC_HZPOS, 0x60);
+
+ /* set H ignore start */
+ reg32_write(VDEC_HSIGS, 0xf8);
+
+ /* set H ignore end */
+ reg32_write(VDEC_HSIGE, 0x18);
+}
+
+/*
+* vdec_init()
+* Initialises the VDEC registers
+* Returns: nothing
+*/
+static void vdec_init(struct vadc_state *vadc)
+{
+ v4l2_std_id std;
+
+ pr_debug("%s\n", __func__);
+
+ /* Get work mode PAL or NTSC */
+ vadc_querystd(&vadc->sd, &std);
+
+ vdec_mode_timing_init(std);
+
+ /* vcr detect threshold high, automatic detections */
+ reg32_write(VDEC_VSCON2, 0);
+
+ reg32_write(VDEC_BASE + 0x110, 0x01);
+
+ /* set the noramp mode on the Hloop PLL. */
+ reg32_write(VDEC_BASE+(0x14*4), 0x10);
+
+ /* set the YC relative delay.*/
+ reg32_write(VDEC_YCDEL, 0x90);
+
+ /* setup the Hpll */
+ reg32_write(VDEC_BASE+(0x13*4), 0x13);
+
+ /* setup the 2d comb */
+ /* set the gain of the Hdetail output to 3
+ * set the notch alpha gain to 1 */
+ reg32_write(VDEC_CFC2, 0x34);
+
+ /* setup various 2d comb bits.*/
+ reg32_write(VDEC_BASE+(0x02*4), 0x01);
+ reg32_write(VDEC_BASE+(0x03*4), 0x18);
+ reg32_write(VDEC_BASE+(0x04*4), 0x34);
+
+ /* set the start of the burst gate */
+ reg32_write(VDEC_BRSTGT, 0x30);
+
+ /* set 1f motion gain */
+ reg32_write(VDEC_BASE+(0x0f*4), 0x20);
+
+ /* set the 1F chroma motion detector thresh
+ * for colour reverse detection */
+ reg32_write(VDEC_THSH1, 0x02);
+ reg32_write(VDEC_BASE+(0x4a*4), 0x20);
+ reg32_write(VDEC_BASE+(0x4b*4), 0x08);
+
+ reg32_write(VDEC_BASE+(0x4c*4), 0x08);
+
+ /* set the threshold for the narrow/wide adaptive chroma BW */
+ reg32_write(VDEC_BASE+(0x20*4), 0x20);
+
+ /* turn up the colour with the new colour gain reg */
+ /* hue: */
+ reg32_write(VDEC_HUE, 0x00);
+
+ /* cbgain: 22 B4 */
+ reg32_write(VDEC_CBGN, 0xb4);
+ /* cr gain 80 */
+ reg32_write(VDEC_CRGN, 0x80);
+ /* luma gain (contrast) */
+ reg32_write(VDEC_CNTR, 0x80);
+
+ /* setup the signed black level register, brightness */
+ reg32_write(VDEC_BRT, 0x00);
+
+ /* filter the standard detection
+ * enable the comb for the ntsc443 */
+ reg32_write(VDEC_STDDBG, 0x20);
+
+ /* setup chroma kill thresh for no chroma */
+ reg32_write(VDEC_CHBTH, 0x0);
+
+ /* set chroma loop to wider BW
+ * no set it to normal BW. i fixed the bw problem.*/
+ reg32_write(VDEC_YCDEL, 0x00);
+
+ /* set the compensation in the chroma loop for the Hloop
+ * set the ratio for the nonarithmetic 3d comb modes.*/
+ reg32_write(VDEC_BASE + (0x1d*4), 0x90);
+
+ /* set the threshold for the nonarithmetic mode for the 2d comb
+ * the higher the value the more Fc Fh offset
+ * we will tolerate before turning off the comb. */
+ reg32_write(VDEC_BASE + (0x33*4), 0xa0);
+
+ /* setup the bluescreen output colour */
+ reg32_write(VDEC_BASE + (0x3d*4), 35);
+ reg32_write(VDEC_BLSCRCR, 114);
+ reg32_write(VDEC_BLSCRCB, 212);
+
+ /* disable the active blanking */
+ reg32_write(VDEC_BASE + (0x15*4), 0x02);
+
+ /* setup the luma agc for automatic gain. */
+ reg32_write(VDEC_LMAGC2, 0x5e);
+ reg32_write(VDEC_LMAGC1, 0x81);
+
+ /* setup chroma agc */
+ reg32_write(VDEC_CHAGC2, 0xa0);
+ reg32_write(VDEC_CHAGC1, 0x01);
+
+ /* setup the MV thresh lower nibble
+ * setup the sync top cap, upper nibble */
+ reg32_write(VDEC_BASE + (0x3a*4), 0x80);
+ reg32_write(VDEC_SHPIMP, 0x00);
+
+ /* setup the vsync block */
+ reg32_write(VDEC_VSCON1, 0x87);
+
+ /* set the nosignal threshold
+ * set the vsync threshold */
+ reg32_write(VDEC_VSSGTH, 0x35);
+
+ /* set length for min hphase filter
+ * (or saturate limit if saturate is chosen) */
+ reg32_write(VDEC_BASE + (0x45*4), 0x40);
+
+ /* enable the internal resampler,
+ * select min filter not saturate for
+ * hphase noise filter for vcr detect.
+ * enable vcr pause mode different field lengths */
+ reg32_write(VDEC_BASE + (0x46*4), 0x90);
+
+ /* disable VCR detection, lock to the Hsync rather than the Vsync */
+ reg32_write(VDEC_VSCON2, 0x04);
+
+ /* set tiplevel goal for dc clamp. */
+ reg32_write(VDEC_BASE + (0x3c*4), 0xB0);
+
+ /* override SECAM detection and force SECAM off */
+ reg32_write(VDEC_BASE + (0x2f*4), 0x20);
+
+ /* Set r3d_hardblend in 3D control2 reg */
+ reg32_write(VDEC_BASE + (0x0c*4), 0x04);
+}
+
+/* set Input selector & input pull-downs */
+static void vadc_s_routing(int vadc_in)
+{
+ switch (vadc_in) {
+ case 0:
+ reg32_write(AFE_INPFLT, 0x02);
+ reg32_write(AFE_OFFDRV, 0x00);
+ reg32_write(AFE_INPCONFIG, 0x1e);
+ break;
+ case 1:
+ reg32_write(AFE_INPFLT, 0x02);
+ reg32_write(AFE_OFFDRV, 0x00);
+ reg32_write(AFE_INPCONFIG, 0x2d);
+ break;
+ case 2:
+ reg32_write(AFE_INPFLT, 0x02);
+ reg32_write(AFE_OFFDRV, 0x00);
+ reg32_write(AFE_INPCONFIG, 0x4b);
+ break;
+ case 3:
+ reg32_write(AFE_INPFLT, 0x02);
+ reg32_write(AFE_OFFDRV, 0x00);
+ reg32_write(AFE_INPCONFIG, 0x87);
+ break;
+ default:
+ pr_debug("error video input %d\n", vadc_in);
+ }
+}
+
+static void vadc_power_up(struct vadc_state *state)
+{
+ /* Power on vadc analog */
+ reg32clrbit(state->gpc_reg + GPC_CNTR, 17);
+
+ /* Power down vadc ext power */
+ reg32clrbit(state->gpc_reg + GPC_CNTR, 18);
+
+ /* software reset afe */
+ regmap_update_bits(state->gpr, IOMUXC_GPR1,
+ IMX6SX_GPR1_VADC_SW_RST_MASK,
+ IMX6SX_GPR1_VADC_SW_RST_RESET);
+
+ msleep(10);
+
+ /* clock config for vadc */
+ reg32_write(VDEC_BASE + 0x320, 0xe3);
+ reg32_write(VDEC_BASE + 0x324, 0x38);
+ reg32_write(VDEC_BASE + 0x328, 0x8e);
+ reg32_write(VDEC_BASE + 0x32c, 0x23);
+
+ /* Release reset bit */
+ regmap_update_bits(state->gpr, IOMUXC_GPR1,
+ IMX6SX_GPR1_VADC_SW_RST_MASK,
+ IMX6SX_GPR1_VADC_SW_RST_RELEASE);
+
+ /* Power on vadc ext power */
+ reg32setbit(state->gpc_reg + GPC_CNTR, 18);
+}
+
+static void vadc_power_down(struct vadc_state *state)
+{
+ /* Power down vadc analog */
+ reg32setbit(state->gpc_reg + GPC_CNTR, 17);
+
+ /* Power down vadc ext power */
+ reg32clrbit(state->gpc_reg + GPC_CNTR, 18);
+
+}
+static void vadc_init(struct vadc_state *vadc)
+{
+ pr_debug("%s\n", __func__);
+
+ vadc_power_up(vadc);
+
+ afe_init();
+
+ /* select Video Input 0-3 */
+ vadc_s_routing(vadc->vadc_in);
+
+ afe_voltage_clampingmode();
+
+ vdec_init(vadc);
+
+ /*
+ * current control loop will move sinewave input off below
+ * the bottom of the signal range visible
+ * when the testbus is viewed as magnitude,
+ * so have to break before this point while capturing ENOB data:
+ */
+ afe_alwayson_clampingmode();
+}
+
+static inline struct vadc_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct vadc_state, sd);
+}
+
+static int vadc_g_std(struct v4l2_subdev *sd, v4l2_std_id *std)
+{
+ struct vadc_state *state = to_state(sd);
+
+ *std = state->fmt->v4l2_std;
+ return 0;
+}
+
+/*!
+ * 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 int vadc_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
+{
+ struct vadc_state *state = to_state(sd);
+ int mod;
+ int idx;
+ int i;
+
+ /* Read auto mode detected result */
+ printk(KERN_INFO"wait vadc auto detect video mode....\n");
+ for (i = 0; i < 10; i++) {
+ msleep(200);
+ mod = reg32_read(VDEC_VIDMOD);
+ /* Check video signal states */
+ if ((mod & VDEC_VIDMOD_SIGNAL_MASK)
+ == VDEC_VIDMOD_SIGNAL_DETECT)
+ break;
+ }
+ if (i == 10)
+ printk(KERN_INFO"Timeout detect video signal mod=0x%x\n", mod);
+
+ if ((mod & VDEC_VIDMOD_PAL_MASK) || (mod & VDEC_VIDMOD_M625_MASK))
+ idx = VADC_PAL;
+ else
+ idx = VADC_NTSC;
+
+ *std = video_fmts[idx].v4l2_std;
+ state->fmt = &video_fmts[idx];
+
+ printk(KERN_INFO"video mode %s\n", video_fmts[idx].name);
+ return 0;
+}
+
+static int vadc_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ /* support only one format */
+ if (code->pad || code->index >= 1)
+ return -EINVAL;
+
+ code->code = MEDIA_BUS_FMT_AYUV8_1X32;
+ return 0;
+}
+
+static int vadc_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
+{
+ struct vadc_state *state = to_state(sd);
+ struct v4l2_mbus_framefmt *fmt = &format->format;
+
+ if (format->pad)
+ return -EINVAL;
+
+ fmt->code = MEDIA_BUS_FMT_AYUV8_1X32;
+ fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ fmt->field = V4L2_FIELD_INTERLACED;
+ fmt->width = 720;
+ fmt->height = state->fmt->v4l2_std & V4L2_STD_NTSC ? 480 : 576;
+
+ return 0;
+}
+
+static int vadc_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
+{
+ return vadc_get_fmt(sd, cfg, format);
+}
+
+static int vadc_enum_framesizes(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct vadc_state *state = to_state(sd);
+ if (fse->index >= 1)
+ return -EINVAL;
+
+ fse->min_width = state->fmt->active_width;
+ fse->max_width = state->fmt->active_width;
+ fse->min_height = state->fmt->active_height;
+ fse->max_height = state->fmt->active_height;
+
+ return 0;
+}
+static int vadc_enum_frameintervals(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_interval_enum *fie)
+{
+ struct vadc_state *state = to_state(sd);
+
+ if (fie->index < 0 || fie->index >= 1)
+ return -EINVAL;
+
+ fie->interval.numerator = 1;
+
+ fie->interval.denominator = state->fmt->framerates;
+
+ return 0;
+}
+
+static int vadc_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
+{
+ struct vadc_state *state = to_state(sd);
+
+ if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (parms->parm.capture.timeperframe.denominator
+ != state->fmt->framerates)
+ parms->parm.capture.timeperframe.denominator
+ = state->fmt->framerates;
+
+ return 0;
+}
+
+static const struct v4l2_subdev_video_ops vadc_video_ops = {
+ .querystd = vadc_querystd,
+ .s_parm = vadc_s_parm,
+ .g_std = vadc_g_std,
+};
+
+
+static const struct v4l2_subdev_pad_ops vadc_pad_ops = {
+ .get_fmt = vadc_get_fmt,
+ .set_fmt = vadc_set_fmt,
+ .enum_mbus_code = vadc_enum_mbus_code,
+ .enum_frame_size = vadc_enum_framesizes,
+ .enum_frame_interval = vadc_enum_frameintervals,
+};
+
+static const struct v4l2_subdev_ops vadc_ops = {
+ .video = &vadc_video_ops,
+ .pad = &vadc_pad_ops,
+};
+
+static const struct of_device_id fsl_vadc_dt_ids[] = {
+ { .compatible = "fsl,imx6sx-vadc", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, fsl_vadc_dt_ids);
+
+static int vadc_of_init(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *gpc_np;
+ struct vadc_state *state = platform_get_drvdata(pdev);
+ int csi_id;
+ int ret;
+
+ /* Get csi_id to setting vadc to csi mux in gpr */
+ ret = of_property_read_u32(np, "csi_id", &csi_id);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to read of property csi_id\n");
+ return ret;
+ }
+
+ state->csi_id = csi_id;
+
+ /* remap GPR register */
+ state->gpr = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+ "gpr");
+ if (IS_ERR(state->gpr)) {
+ dev_dbg(&pdev->dev, "can not get gpr\n");
+ return -ENOMEM;
+ }
+
+ /* Configuration vadc-to-csi 0 or 1 */
+ if (csi_id) {
+ regmap_update_bits(state->gpr, IOMUXC_GPR5,
+ IMX6SX_GPR5_CSI2_MUX_CTRL_MASK,
+ IMX6SX_GPR5_CSI2_MUX_CTRL_CVD);
+ } else {
+ regmap_update_bits(state->gpr, IOMUXC_GPR5,
+ IMX6SX_GPR5_CSI1_MUX_CTRL_MASK,
+ IMX6SX_GPR5_CSI1_MUX_CTRL_CVD);
+ }
+
+ /* Get default vadc_in number */
+ ret = of_property_read_u32(np, "vadc_in", &state->vadc_in);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to read of property vadc_in\n");
+ return ret;
+ }
+
+ /* map GPC register */
+ gpc_np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-gpc");
+ state->gpc_reg = of_iomap(gpc_np, 0);
+ if (!state->gpc_reg) {
+ dev_err(&pdev->dev, "ioremap failed with gpc base\n");
+ goto error;
+ }
+
+ return ret;
+
+error:
+ iounmap(state->gpc_reg);
+ return ret;
+}
+
+static void vadc_v4l2_subdev_init(struct v4l2_subdev *sd,
+ struct platform_device *pdev,
+ const struct v4l2_subdev_ops *ops)
+{
+ struct vadc_state *state = platform_get_drvdata(pdev);
+ int ret = 0;
+
+ v4l2_subdev_init(sd, ops);
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ sd->owner = pdev->dev.driver->owner;
+ sd->dev = &pdev->dev;
+
+ /* initialize name */
+ snprintf(sd->name, sizeof(sd->name), "%s",
+ pdev->dev.driver->name);
+
+ v4l2_set_subdevdata(sd, state);
+
+ ret = v4l2_async_register_subdev(sd);
+ if (ret < 0)
+ dev_err(&pdev->dev, "%s--Async register faialed, ret=%d\n", __func__, ret);
+}
+
+static int vadc_probe(struct platform_device *pdev)
+{
+ struct vadc_state *state;
+ struct v4l2_subdev *sd;
+ struct resource *res;
+ int ret = 0;
+
+ state = devm_kzalloc(&pdev->dev, sizeof(struct vadc_state), GFP_KERNEL);
+ if (!state) {
+ dev_err(&pdev->dev, "Cannot allocate device data\n");
+ return -ENOMEM;
+ }
+
+ /* Set initial values for the sensor struct. */
+ state->fmt = &video_fmts[VADC_NTSC];
+
+ sd = &state->sd;
+
+ /* map vafe address */
+ res = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, VAFE_REGS_ADDR_RES_NAME);
+ if (!res) {
+ dev_err(&pdev->dev, "No vafe base address found.\n");
+ return -ENOMEM;
+ }
+ vafe_regbase = devm_ioremap_resource(&pdev->dev, res);
+ if (!vafe_regbase) {
+ dev_err(&pdev->dev, "ioremap failed with vafe base\n");
+ return -ENOMEM;
+ }
+
+ /* map vdec address */
+ res = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, VDEC_REGS_ADDR_RES_NAME);
+ if (!res) {
+ dev_err(&pdev->dev, "No vdec base address found.\n");
+ return -ENODEV;
+ }
+ vdec_regbase = devm_ioremap_resource(&pdev->dev, res);
+ if (!vdec_regbase) {
+ dev_err(&pdev->dev, "ioremap failed with vdec base\n");
+ return -ENOMEM;
+ }
+
+ /* Get clock */
+ state->vadc_clk = devm_clk_get(&pdev->dev, "vadc");
+ if (IS_ERR(state->vadc_clk)) {
+ ret = PTR_ERR(state->vadc_clk);
+ return ret;
+ }
+
+ state->csi_clk = devm_clk_get(&pdev->dev, "csi");
+ if (IS_ERR(state->csi_clk)) {
+ ret = PTR_ERR(state->csi_clk);
+ return ret;
+ }
+
+ /* clock */
+ clk_prepare_enable(state->csi_clk);
+ clk_prepare_enable(state->vadc_clk);
+
+ platform_set_drvdata(pdev, state);
+
+ vadc_v4l2_subdev_init(sd, pdev, &vadc_ops);
+
+ pm_runtime_enable(&pdev->dev);
+
+ pm_runtime_get_sync(&pdev->dev);
+ /* Init VADC */
+ ret = vadc_of_init(pdev);
+ if (ret < 0)
+ goto err;
+ vadc_init(state);
+
+ pr_info("vadc driver loaded\n");
+
+ return 0;
+err:
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ v4l2_async_unregister_subdev(&state->sd);
+ clk_disable_unprepare(state->csi_clk);
+ clk_disable_unprepare(state->vadc_clk);
+ return ret;
+}
+
+static int vadc_remove(struct platform_device *pdev)
+{
+ struct vadc_state *state = platform_get_drvdata(pdev);
+
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ v4l2_async_unregister_subdev(&state->sd);
+ clk_disable_unprepare(state->csi_clk);
+ clk_disable_unprepare(state->vadc_clk);
+
+ vadc_power_down(state);
+ return true;
+}
+
+static int vadc_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct vadc_state *state = platform_get_drvdata(pdev);
+
+ clk_disable(state->csi_clk);
+ clk_disable(state->vadc_clk);
+
+ vadc_power_down(state);
+
+ return 0;
+}
+
+static int vadc_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct vadc_state *state = platform_get_drvdata(pdev);
+
+ clk_enable(state->csi_clk);
+ clk_enable(state->vadc_clk);
+
+ vadc_init(state);
+ return 0;
+}
+
+static const struct dev_pm_ops vadc_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(vadc_suspend, vadc_resume)
+};
+
+static struct platform_driver vadc_driver = {
+ .driver = {
+ .name = "fsl_vadc",
+ .of_match_table = of_match_ptr(fsl_vadc_dt_ids),
+ .pm = &vadc_pm_ops,
+ },
+ .probe = vadc_probe,
+ .remove = vadc_remove,
+};
+
+module_platform_driver(vadc_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("fsl VADC/VDEC driver");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * Copyright (C) 2011-2015 Freescale Semiconductor, Inc.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef MXC_VDEC_H
+#define MXC_VDEC_H
+
+/*** define base address ***/
+#define VDEC_BASE vdec_regbase
+#define AFE_BASE vafe_regbase
+
+/* AFE - Register offsets */
+#define AFE_BLOCK_ID_OFFSET 0x00000000
+#define AFE_PDBUF_OFFSET 0x00000004
+#define AFE_SWRST_OFFSET 0x00000008
+#define AFE_TSTSEL_OFFSET 0x0000000c
+#define AFE_TSTMSC_OFFSET 0x00000010
+#define AFE_ENPADIO_OFFSET 0x00000014
+#define AFE_BGREG_OFFSET 0x00000018
+#define AFE_ACCESSAR_ID_OFFSET 0x00000400
+#define AFE_PDADC_OFFSET 0x00000404
+#define AFE_PDSARH_OFFSET 0x00000408
+#define AFE_PDSARL_OFFSET 0x0000040C
+#define AFE_PDADCRFH_OFFSET 0x00000410
+#define AFE_PDADCRFL_OFFSET 0x00000414
+#define AFE_ACCTST_OFFSET 0x00000418
+#define AFE_ADCGN_OFFSET 0x0000041C
+#define AFE_ICTRL_OFFSET 0x00000420
+#define AFE_ICTLSTG_OFFSET 0x00000424
+#define AFE_RCTRLSTG_OFFSET 0x00000428
+#define AFE_TCTRLSTG_OFFSET 0x0000042c
+#define AFE_REFMOD_OFFSET 0x00000430
+#define AFE_REFTRIML_OFFSET 0x00000434
+#define AFE_REFTRIMH_OFFSET 0x00000438
+#define AFE_ADCR_OFFSET 0x0000043c
+#define AFE_DUMMY0_OFFSET 0x00000440
+#define AFE_DUMMY1_OFFSET 0x00000444
+#define AFE_DUMMY2_OFFSET 0x00000448
+#define AFE_DACAMP_OFFSET 0x0000044c
+#define AFE_CLMPTST_OFFSET 0x00000450
+#define AFE_CLMPDAT_OFFSET 0x00000454
+#define AFE_CLMPAMP_OFFSET 0x00000458
+#define AFE_CLAMP_OFFSET 0x0000045c
+#define AFE_INPBUF_OFFSET 0x00000460
+#define AFE_INPFLT_OFFSET 0x00000464
+#define AFE_ADCDGN_OFFSET 0x00000468
+#define AFE_OFFDRV_OFFSET 0x0000046c
+#define AFE_INPCONFIG_OFFSET 0x00000470
+#define AFE_PROGDELAY_OFFSET 0x00000474
+#define AFE_ADCOMT_OFFSET 0x00000478
+#define AFE_ALGDELAY_OFFSET 0x0000047c
+#define AFE_ACC_ID_OFFSET 0x00000800
+#define AFE_ACCSTA_OFFSET 0x00000804
+#define AFE_ACCNOSLI_OFFSET 0x00000808
+#define AFE_ACCCALCON_OFFSET 0x0000080c
+#define AFE_BWEWRICTRL_OFFSET 0x00000810
+#define AFE_SELSLI_OFFSET 0x00000814
+#define AFE_SELBYT_OFFSET 0x00000818
+#define AFE_REDVAL_OFFSET 0x00000820
+#define AFE_WRIBYT_OFFSET 0x00000824
+
+/* AFE Register per module */
+#define AFE_BLOCK_ID (AFE_BASE + AFE_BLOCK_ID_OFFSET)
+#define AFE_PDBUF (AFE_BASE + AFE_PDBUF_OFFSET)
+#define AFE_SWRST (AFE_BASE + AFE_SWRST_OFFSET)
+#define AFE_TSTSEL (AFE_BASE + AFE_TSTSEL_OFFSET)
+#define AFE_TSTMSC (AFE_BASE + AFE_TSTMSC_OFFSET)
+#define AFE_ENPADIO (AFE_BASE + AFE_ENPADIO_OFFSET)
+#define AFE_BGREG (AFE_BASE + AFE_BGREG_OFFSET)
+#define AFE_ACCESSAR_ID (AFE_BASE + AFE_ACCESSAR_ID_OFFSET)
+#define AFE_PDADC (AFE_BASE + AFE_PDADC_OFFSET)
+#define AFE_PDSARH (AFE_BASE + AFE_PDSARH_OFFSET)
+#define AFE_PDSARL (AFE_BASE + AFE_PDSARL_OFFSET)
+#define AFE_PDADCRFH (AFE_BASE + AFE_PDADCRFH_OFFSET)
+#define AFE_PDADCRFL (AFE_BASE + AFE_PDADCRFL_OFFSET)
+#define AFE_ACCTST (AFE_BASE + AFE_ACCTST_OFFSET)
+#define AFE_ADCGN (AFE_BASE + AFE_ADCGN_OFFSET)
+#define AFE_ICTRL (AFE_BASE + AFE_ICTRL_OFFSET)
+#define AFE_ICTLSTG (AFE_BASE + AFE_ICTLSTG_OFFSET)
+#define AFE_RCTRLSTG (AFE_BASE + AFE_RCTRLSTG_OFFSET)
+#define AFE_TCTRLSTG (AFE_BASE + AFE_TCTRLSTG_OFFSET)
+#define AFE_REFMOD (AFE_BASE + AFE_REFMOD_OFFSET)
+#define AFE_REFTRIML (AFE_BASE + AFE_REFTRIML_OFFSET)
+#define AFE_REFTRIMH (AFE_BASE + AFE_REFTRIMH_OFFSET)
+#define AFE_ADCR (AFE_BASE + AFE_ADCR_OFFSET)
+#define AFE_DUMMY0 (AFE_BASE + AFE_DUMMY0_OFFSET)
+#define AFE_DUMMY1 (AFE_BASE + AFE_DUMMY1_OFFSET)
+#define AFE_DUMMY2 (AFE_BASE + AFE_DUMMY2_OFFSET)
+#define AFE_DACAMP (AFE_BASE + AFE_DACAMP_OFFSET)
+#define AFE_CLMPTST (AFE_BASE + AFE_CLMPTST_OFFSET)
+#define AFE_CLMPDAT (AFE_BASE + AFE_CLMPDAT_OFFSET)
+#define AFE_CLMPAMP (AFE_BASE + AFE_CLMPAMP_OFFSET)
+#define AFE_CLAMP (AFE_BASE + AFE_CLAMP_OFFSET)
+#define AFE_INPBUF (AFE_BASE + AFE_INPBUF_OFFSET)
+#define AFE_INPFLT (AFE_BASE + AFE_INPFLT_OFFSET)
+#define AFE_ADCDGN (AFE_BASE + AFE_ADCDGN_OFFSET)
+#define AFE_OFFDRV (AFE_BASE + AFE_OFFDRV_OFFSET)
+#define AFE_INPCONFIG (AFE_BASE + AFE_INPCONFIG_OFFSET)
+#define AFE_PROGDELAY (AFE_BASE + AFE_PROGDELAY_OFFSET)
+#define AFE_ADCOMT (AFE_BASE + AFE_ADCOMT_OFFSET)
+#define AFE_ALGDELAY (AFE_BASE + AFE_ALGDELAY_OFFSET)
+#define AFE_ACC_ID (AFE_BASE + AFE_ACC_ID_OFFSET)
+#define AFE_ACCSTA (AFE_BASE + AFE_ACCSTA_OFFSET)
+#define AFE_ACCNOSLI (AFE_BASE + AFE_ACCNOSLI_OFFSET)
+#define AFE_ACCCALCON (AFE_BASE + AFE_ACCCALCON_OFFSET)
+#define AFE_BWEWRICTRL (AFE_BASE + AFE_BWEWRICTRL_OFFSET)
+#define AFE_SELSLI (AFE_BASE + AFE_SELSLI_OFFSET)
+#define AFE_SELBYT (AFE_BASE + AFE_SELBYT_OFFSET)
+#define AFE_REDVAL (AFE_BASE + AFE_REDVAL_OFFSET)
+#define AFE_WRIBYT (AFE_BASE + AFE_WRIBYT_OFFSET)
+
+/* VDEC - Register offsets */
+#define VDEC_CFC1_OFFSET 0x00000000
+#define VDEC_CFC2_OFFSET 0x00000004
+#define VDEC_BRSTGT_OFFSET 0x00000024
+#define VDEC_HZPOS_OFFSET 0x00000040
+#define VDEC_VRTPOS_OFFSET 0x00000044
+#define VDEC_HVSHIFT_OFFSET 0x00000054
+#define VDEC_HSIGS_OFFSET 0x00000058
+#define VDEC_HSIGE_OFFSET 0x0000005C
+#define VDEC_VSCON1_OFFSET 0x00000060
+#define VDEC_VSCON2_OFFSET 0x00000064
+#define VDEC_YCDEL_OFFSET 0x0000006C
+#define VDEC_AFTCLP_OFFSET 0x00000070
+#define VDEC_DCOFF_OFFSET 0x00000078
+#define VDEC_CSID_OFFSET 0x00000084
+#define VDEC_CBGN_OFFSET 0x00000088
+#define VDEC_CRGN_OFFSET 0x0000008C
+#define VDEC_CNTR_OFFSET 0x00000090
+#define VDEC_BRT_OFFSET 0x00000094
+#define VDEC_HUE_OFFSET 0x00000098
+#define VDEC_CHBTH_OFFSET 0x0000009C
+#define VDEC_SHPIMP_OFFSET 0x000000A4
+#define VDEC_CHPLLIM_OFFSET 0x000000A8
+#define VDEC_VIDMOD_OFFSET 0x000000AC
+#define VDEC_VIDSTS_OFFSET 0x000000B0
+#define VDEC_NOISE_OFFSET 0x000000B4
+#define VDEC_STDDBG_OFFSET 0x000000B8
+#define VDEC_MANOVR_OFFSET 0x000000BC
+#define VDEC_VSSGTH_OFFSET 0x000000C8
+#define VDEC_DBGFBH_OFFSET 0x000000D0
+#define VDEC_DBGFBL_OFFSET 0x000000D4
+#define VDEC_HACTS_OFFSET 0x000000D8
+#define VDEC_HACTE_OFFSET 0x000000DC
+#define VDEC_VACTS_OFFSET 0x000000E0
+#define VDEC_VACTE_OFFSET 0x000000E4
+#define VDEC_HSTIP_OFFSET 0x000000EC
+#define VDEC_BLSCRY_OFFSET 0x000000F4
+#define VDEC_BLSCRCR_OFFSET 0x000000F8
+#define VDEC_BLSCRCB_OFFSET 0x000000FC
+#define VDEC_LMAGC1_OFFSET 0x00000100
+#define VDEC_LMAGC2_OFFSET 0x00000104
+#define VDEC_CHAGC1_OFFSET 0x00000108
+#define VDEC_CHAGC2_OFFSET 0x0000010C
+#define VDEC_MINTH_OFFSET 0x00000114
+#define VDEC_VFRQOH_OFFSET 0x0000011C
+#define VDEC_VFRQOL_OFFSET 0x00000120
+#define VDEC_THSH1_OFFSET 0x00000124
+#define VDEC_THSH2_OFFSET 0x00000128
+#define VDEC_NCHTH_OFFSET 0x0000012C
+#define VDEC_TH1F_OFFSET 0x00000130
+
+/* VDEC Register per module */
+#define VDEC_CFC1 (VDEC_BASE + VDEC_CFC1_OFFSET)
+#define VDEC_CFC2 (VDEC_BASE + VDEC_CFC2_OFFSET)
+#define VDEC_BRSTGT (VDEC_BASE + VDEC_BRSTGT_OFFSET)
+#define VDEC_HZPOS (VDEC_BASE + VDEC_HZPOS_OFFSET)
+#define VDEC_VRTPOS (VDEC_BASE + VDEC_VRTPOS_OFFSET)
+#define VDEC_HVSHIFT (VDEC_BASE + VDEC_HVSHIFT_OFFSET)
+#define VDEC_HSIGS (VDEC_BASE + VDEC_HSIGS_OFFSET)
+#define VDEC_HSIGE (VDEC_BASE + VDEC_HSIGE_OFFSET)
+#define VDEC_VSCON1 (VDEC_BASE + VDEC_VSCON1_OFFSET)
+#define VDEC_VSCON2 (VDEC_BASE + VDEC_VSCON2_OFFSET)
+#define VDEC_YCDEL (VDEC_BASE + VDEC_YCDEL_OFFSET)
+#define VDEC_AFTCLP (VDEC_BASE + VDEC_AFTCLP_OFFSET)
+#define VDEC_DCOFF (VDEC_BASE + VDEC_DCOFF_OFFSET)
+#define VDEC_CSID (VDEC_BASE + VDEC_CSID_OFFSET)
+#define VDEC_CBGN (VDEC_BASE + VDEC_CBGN_OFFSET)
+#define VDEC_CRGN (VDEC_BASE + VDEC_CRGN_OFFSET)
+#define VDEC_CNTR (VDEC_BASE + VDEC_CNTR_OFFSET)
+#define VDEC_BRT (VDEC_BASE + VDEC_BRT_OFFSET)
+#define VDEC_HUE (VDEC_BASE + VDEC_HUE_OFFSET)
+#define VDEC_CHBTH (VDEC_BASE + VDEC_CHBTH_OFFSET)
+#define VDEC_SHPIMP (VDEC_BASE + VDEC_SHPIMP_OFFSET)
+#define VDEC_CHPLLIM (VDEC_BASE + VDEC_CHPLLIM_OFFSET)
+#define VDEC_VIDMOD (VDEC_BASE + VDEC_VIDMOD_OFFSET)
+#define VDEC_VIDSTS (VDEC_BASE + VDEC_VIDSTS_OFFSET)
+#define VDEC_NOISE (VDEC_BASE + VDEC_NOISE_OFFSET)
+#define VDEC_STDDBG (VDEC_BASE + VDEC_STDDBG_OFFSET)
+#define VDEC_MANOVR (VDEC_BASE + VDEC_MANOVR_OFFSET)
+#define VDEC_VSSGTH (VDEC_BASE + VDEC_VSSGTH_OFFSET)
+#define VDEC_DBGFBH (VDEC_BASE + VDEC_DBGFBH_OFFSET)
+#define VDEC_DBGFBL (VDEC_BASE + VDEC_DBGFBL_OFFSET)
+#define VDEC_HACTS (VDEC_BASE + VDEC_HACTS_OFFSET)
+#define VDEC_HACTE (VDEC_BASE + VDEC_HACTE_OFFSET)
+#define VDEC_VACTS (VDEC_BASE + VDEC_VACTS_OFFSET)
+#define VDEC_VACTE (VDEC_BASE + VDEC_VACTE_OFFSET)
+#define VDEC_HSTIP (VDEC_BASE + VDEC_HSTIP_OFFSET)
+#define VDEC_BLSCRY (VDEC_BASE + VDEC_BLSCRY_OFFSET)
+#define VDEC_BLSCRCR (VDEC_BASE + VDEC_BLSCRCR_OFFSET)
+#define VDEC_BLSCRCB (VDEC_BASE + VDEC_BLSCRCB_OFFSET)
+#define VDEC_LMAGC1 (VDEC_BASE + VDEC_LMAGC1_OFFSET)
+#define VDEC_LMAGC2 (VDEC_BASE + VDEC_LMAGC2_OFFSET)
+#define VDEC_CHAGC1 (VDEC_BASE + VDEC_CHAGC1_OFFSET)
+#define VDEC_CHAGC2 (VDEC_BASE + VDEC_CHAGC2_OFFSET)
+#define VDEC_MINTH (VDEC_BASE + VDEC_MINTH_OFFSET)
+#define VDEC_VFRQOH (VDEC_BASE + VDEC_VFRQOH_OFFSET)
+#define VDEC_VFRQOL (VDEC_BASE + VDEC_VFRQOL_OFFSET)
+#define VDEC_THSH1 (VDEC_BASE + VDEC_THSH1_OFFSET)
+#define VDEC_THSH2 (VDEC_BASE + VDEC_THSH2_OFFSET)
+#define VDEC_NCHTH (VDEC_BASE + VDEC_NCHTH_OFFSET)
+#define VDEC_TH1F (VDEC_BASE + VDEC_TH1F_OFFSET)
+
+#define VDEC_VIDMOD_SIGNAL_MASK 0x0F
+#define VDEC_VIDMOD_SIGNAL_DETECT 0x0F
+
+#define VDEC_VIDMOD_M625_SHIFT 4
+#define VDEC_VIDMOD_M625_MASK (1 << VDEC_VIDMOD_M625_SHIFT)
+
+#define VDEC_VIDMOD_PAL_SHIFT 7
+#define VDEC_VIDMOD_PAL_MASK (1 << VDEC_VIDMOD_PAL_SHIFT)
+/*** define base address ***/
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2011-2016 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/clk.h>
+#include <linux/of_device.h>
+#include <linux/i2c.h>
+#include <linux/of_gpio.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <linux/v4l2-mediabus.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+
+#define OV5640_VOLTAGE_ANALOG 2800000
+#define OV5640_VOLTAGE_DIGITAL_CORE 1500000
+#define OV5640_VOLTAGE_DIGITAL_IO 1800000
+
+#define MIN_FPS 15
+#define MAX_FPS 30
+#define DEFAULT_FPS 30
+
+#define OV5640_XCLK_MIN 6000000
+#define OV5640_XCLK_MAX 24000000
+
+#define OV5640_CHIP_ID_HIGH_BYTE 0x300A
+#define OV5640_CHIP_ID_LOW_BYTE 0x300B
+
+enum ov5640_mode {
+ ov5640_mode_MIN = 0,
+ ov5640_mode_VGA_640_480 = 0,
+ ov5640_mode_NTSC_720_480 = 1,
+ ov5640_mode_720P_1280_720 = 2,
+ ov5640_mode_1080P_1920_1080 = 3,
+ ov5640_mode_QSXGA_2592_1944 = 4,
+ ov5640_mode_MAX = 5,
+ ov5640_mode_INIT = 0xff, /*only for sensor init*/
+};
+
+enum ov5640_frame_rate {
+ ov5640_15_fps,
+ ov5640_30_fps
+};
+
+static int ov5640_framerates[] = {
+ [ov5640_15_fps] = 15,
+ [ov5640_30_fps] = 30,
+};
+
+struct ov5640_datafmt {
+ u32 code;
+ enum v4l2_colorspace colorspace;
+};
+
+/* image size under 1280 * 960 are SUBSAMPLING
+ * image size upper 1280 * 960 are SCALING
+ */
+enum ov5640_downsize_mode {
+ SUBSAMPLING,
+ SCALING,
+};
+
+struct reg_value {
+ u16 u16RegAddr;
+ u8 u8Val;
+ u8 u8Mask;
+ u32 u32Delay_ms;
+};
+
+struct ov5640_mode_info {
+ enum ov5640_mode mode;
+ enum ov5640_downsize_mode dn_mode;
+ u32 width;
+ u32 height;
+ struct reg_value *init_data_ptr;
+ u32 init_data_size;
+};
+
+struct ov5640 {
+ struct v4l2_subdev subdev;
+ struct i2c_client *i2c_client;
+ struct v4l2_pix_format pix;
+ const struct ov5640_datafmt *fmt;
+ 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;
+ u8 mclk_source;
+ struct clk *sensor_clk;
+ int csi;
+
+ void (*io_init)(void);
+};
+/*!
+ * Maintains the information on the current state of the sesor.
+ */
+static struct ov5640 ov5640_data;
+static int pwn_gpio, rst_gpio;
+
+static struct reg_value ov5640_init_setting_30fps_VGA[] = {
+
+ {0x3103, 0x11, 0, 0}, {0x3008, 0x82, 0, 5}, {0x3008, 0x42, 0, 0},
+ {0x3103, 0x03, 0, 0}, {0x3017, 0x00, 0, 0}, {0x3018, 0x00, 0, 0},
+ {0x3034, 0x18, 0, 0}, {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0},
+ {0x3037, 0x13, 0, 0}, {0x3108, 0x01, 0, 0}, {0x3630, 0x36, 0, 0},
+ {0x3631, 0x0e, 0, 0}, {0x3632, 0xe2, 0, 0}, {0x3633, 0x12, 0, 0},
+ {0x3621, 0xe0, 0, 0}, {0x3704, 0xa0, 0, 0}, {0x3703, 0x5a, 0, 0},
+ {0x3715, 0x78, 0, 0}, {0x3717, 0x01, 0, 0}, {0x370b, 0x60, 0, 0},
+ {0x3705, 0x1a, 0, 0}, {0x3905, 0x02, 0, 0}, {0x3906, 0x10, 0, 0},
+ {0x3901, 0x0a, 0, 0}, {0x3731, 0x12, 0, 0}, {0x3600, 0x08, 0, 0},
+ {0x3601, 0x33, 0, 0}, {0x302d, 0x60, 0, 0}, {0x3620, 0x52, 0, 0},
+ {0x371b, 0x20, 0, 0}, {0x471c, 0x50, 0, 0}, {0x3a13, 0x43, 0, 0},
+ {0x3a18, 0x00, 0, 0}, {0x3a19, 0xf8, 0, 0}, {0x3635, 0x13, 0, 0},
+ {0x3636, 0x03, 0, 0}, {0x3634, 0x40, 0, 0}, {0x3622, 0x01, 0, 0},
+ {0x3c01, 0xa4, 0, 0}, {0x3c04, 0x28, 0, 0}, {0x3c05, 0x98, 0, 0},
+ {0x3c06, 0x00, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c08, 0x00, 0, 0},
+ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+ {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+ {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+ {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0},
+ {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+ {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x3000, 0x00, 0, 0},
+ {0x3002, 0x1c, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3006, 0xc3, 0, 0},
+ {0x300e, 0x45, 0, 0}, {0x302e, 0x08, 0, 0}, {0x4300, 0x30, 0, 0},
+ {0x501f, 0x00, 0, 0}, {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0},
+ {0x440e, 0x00, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+ {0x4837, 0x0a, 0, 0}, {0x4800, 0x04, 0, 0}, {0x3824, 0x02, 0, 0},
+ {0x5000, 0xa7, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x5180, 0xff, 0, 0},
+ {0x5181, 0xf2, 0, 0}, {0x5182, 0x00, 0, 0}, {0x5183, 0x14, 0, 0},
+ {0x5184, 0x25, 0, 0}, {0x5185, 0x24, 0, 0}, {0x5186, 0x09, 0, 0},
+ {0x5187, 0x09, 0, 0}, {0x5188, 0x09, 0, 0}, {0x5189, 0x88, 0, 0},
+ {0x518a, 0x54, 0, 0}, {0x518b, 0xee, 0, 0}, {0x518c, 0xb2, 0, 0},
+ {0x518d, 0x50, 0, 0}, {0x518e, 0x34, 0, 0}, {0x518f, 0x6b, 0, 0},
+ {0x5190, 0x46, 0, 0}, {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0},
+ {0x5193, 0x70, 0, 0}, {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0},
+ {0x5196, 0x03, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0},
+ {0x5199, 0x6c, 0, 0}, {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0},
+ {0x519c, 0x09, 0, 0}, {0x519d, 0x2b, 0, 0}, {0x519e, 0x38, 0, 0},
+ {0x5381, 0x1e, 0, 0}, {0x5382, 0x5b, 0, 0}, {0x5383, 0x08, 0, 0},
+ {0x5384, 0x0a, 0, 0}, {0x5385, 0x7e, 0, 0}, {0x5386, 0x88, 0, 0},
+ {0x5387, 0x7c, 0, 0}, {0x5388, 0x6c, 0, 0}, {0x5389, 0x10, 0, 0},
+ {0x538a, 0x01, 0, 0}, {0x538b, 0x98, 0, 0}, {0x5300, 0x08, 0, 0},
+ {0x5301, 0x30, 0, 0}, {0x5302, 0x10, 0, 0}, {0x5303, 0x00, 0, 0},
+ {0x5304, 0x08, 0, 0}, {0x5305, 0x30, 0, 0}, {0x5306, 0x08, 0, 0},
+ {0x5307, 0x16, 0, 0}, {0x5309, 0x08, 0, 0}, {0x530a, 0x30, 0, 0},
+ {0x530b, 0x04, 0, 0}, {0x530c, 0x06, 0, 0}, {0x5480, 0x01, 0, 0},
+ {0x5481, 0x08, 0, 0}, {0x5482, 0x14, 0, 0}, {0x5483, 0x28, 0, 0},
+ {0x5484, 0x51, 0, 0}, {0x5485, 0x65, 0, 0}, {0x5486, 0x71, 0, 0},
+ {0x5487, 0x7d, 0, 0}, {0x5488, 0x87, 0, 0}, {0x5489, 0x91, 0, 0},
+ {0x548a, 0x9a, 0, 0}, {0x548b, 0xaa, 0, 0}, {0x548c, 0xb8, 0, 0},
+ {0x548d, 0xcd, 0, 0}, {0x548e, 0xdd, 0, 0}, {0x548f, 0xea, 0, 0},
+ {0x5490, 0x1d, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5583, 0x40, 0, 0},
+ {0x5584, 0x10, 0, 0}, {0x5589, 0x10, 0, 0}, {0x558a, 0x00, 0, 0},
+ {0x558b, 0xf8, 0, 0}, {0x5800, 0x23, 0, 0}, {0x5801, 0x14, 0, 0},
+ {0x5802, 0x0f, 0, 0}, {0x5803, 0x0f, 0, 0}, {0x5804, 0x12, 0, 0},
+ {0x5805, 0x26, 0, 0}, {0x5806, 0x0c, 0, 0}, {0x5807, 0x08, 0, 0},
+ {0x5808, 0x05, 0, 0}, {0x5809, 0x05, 0, 0}, {0x580a, 0x08, 0, 0},
+ {0x580b, 0x0d, 0, 0}, {0x580c, 0x08, 0, 0}, {0x580d, 0x03, 0, 0},
+ {0x580e, 0x00, 0, 0}, {0x580f, 0x00, 0, 0}, {0x5810, 0x03, 0, 0},
+ {0x5811, 0x09, 0, 0}, {0x5812, 0x07, 0, 0}, {0x5813, 0x03, 0, 0},
+ {0x5814, 0x00, 0, 0}, {0x5815, 0x01, 0, 0}, {0x5816, 0x03, 0, 0},
+ {0x5817, 0x08, 0, 0}, {0x5818, 0x0d, 0, 0}, {0x5819, 0x08, 0, 0},
+ {0x581a, 0x05, 0, 0}, {0x581b, 0x06, 0, 0}, {0x581c, 0x08, 0, 0},
+ {0x581d, 0x0e, 0, 0}, {0x581e, 0x29, 0, 0}, {0x581f, 0x17, 0, 0},
+ {0x5820, 0x11, 0, 0}, {0x5821, 0x11, 0, 0}, {0x5822, 0x15, 0, 0},
+ {0x5823, 0x28, 0, 0}, {0x5824, 0x46, 0, 0}, {0x5825, 0x26, 0, 0},
+ {0x5826, 0x08, 0, 0}, {0x5827, 0x26, 0, 0}, {0x5828, 0x64, 0, 0},
+ {0x5829, 0x26, 0, 0}, {0x582a, 0x24, 0, 0}, {0x582b, 0x22, 0, 0},
+ {0x582c, 0x24, 0, 0}, {0x582d, 0x24, 0, 0}, {0x582e, 0x06, 0, 0},
+ {0x582f, 0x22, 0, 0}, {0x5830, 0x40, 0, 0}, {0x5831, 0x42, 0, 0},
+ {0x5832, 0x24, 0, 0}, {0x5833, 0x26, 0, 0}, {0x5834, 0x24, 0, 0},
+ {0x5835, 0x22, 0, 0}, {0x5836, 0x22, 0, 0}, {0x5837, 0x26, 0, 0},
+ {0x5838, 0x44, 0, 0}, {0x5839, 0x24, 0, 0}, {0x583a, 0x26, 0, 0},
+ {0x583b, 0x28, 0, 0}, {0x583c, 0x42, 0, 0}, {0x583d, 0xce, 0, 0},
+ {0x5025, 0x00, 0, 0}, {0x3a0f, 0x30, 0, 0}, {0x3a10, 0x28, 0, 0},
+ {0x3a1b, 0x30, 0, 0}, {0x3a1e, 0x26, 0, 0}, {0x3a11, 0x60, 0, 0},
+ {0x3a1f, 0x14, 0, 0}, {0x3008, 0x42, 0, 0}, {0x3c00, 0x04, 0, 300},
+};
+
+static struct reg_value ov5640_setting_30fps_VGA_640_480[] = {
+ {0x3008, 0x42, 0, 0},
+ {0x3035, 0x12, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+ {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+ {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+ {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0},
+ {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+ {0x380e, 0x04, 0, 0}, {0x380f, 0x38, 0, 0}, {0x3810, 0x00, 0, 0},
+ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x0e, 0, 0},
+ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
+ {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+ {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
+ {0x4005, 0x1a, 0, 0}, {0x3008, 0x02, 0, 0}, {0x3503, 0x00, 0, 0},
+};
+
+static struct reg_value ov5640_setting_30fps_NTSC_720_480[] = {
+ {0x3008, 0x42, 0, 0},
+ {0x3035, 0x12, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+ {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+ {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+ {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x01, 0, 0},
+ {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+ {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x3c, 0, 0},
+ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
+ {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+ {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
+ {0x4005, 0x1a, 0, 0}, {0x3008, 0x02, 0, 0}, {0x3503, 0, 0, 0},
+};
+
+static struct reg_value ov5640_setting_30fps_720P_1280_720[] = {
+ {0x3008, 0x42, 0, 0},
+ {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x07, 0, 0},
+ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+ {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+ {0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0},
+ {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0},
+ {0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x02, 0, 0},
+ {0x380b, 0xd0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x64, 0, 0},
+ {0x380e, 0x02, 0, 0}, {0x380f, 0xe4, 0, 0}, {0x3810, 0x00, 0, 0},
+ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
+ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x02, 0, 0},
+ {0x3a03, 0xe4, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0xbc, 0, 0},
+ {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x72, 0, 0}, {0x3a0e, 0x01, 0, 0},
+ {0x3a0d, 0x02, 0, 0}, {0x3a14, 0x02, 0, 0}, {0x3a15, 0xe4, 0, 0},
+ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x02, 0, 0},
+ {0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0},
+ {0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0}, {0x4005, 0x1a, 0, 0},
+ {0x3008, 0x02, 0, 0}, {0x3503, 0, 0, 0},
+};
+
+static struct reg_value ov5640_setting_30fps_1080P_1920_1080[] = {
+ {0x3008, 0x42, 0, 0},
+ {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x08, 0, 0},
+ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+ {0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, {0x3814, 0x11, 0, 0},
+ {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+ {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0},
+ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0},
+ {0x3808, 0x0a, 0, 0}, {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0},
+ {0x380b, 0x98, 0, 0}, {0x380c, 0x0b, 0, 0}, {0x380d, 0x1c, 0, 0},
+ {0x380e, 0x07, 0, 0}, {0x380f, 0xb0, 0, 0}, {0x3810, 0x00, 0, 0},
+ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
+ {0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0},
+ {0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0},
+ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+ {0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4713, 0x03, 0, 0},
+ {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+ {0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 0}, {0x3035, 0x11, 0, 0},
+ {0x3036, 0x54, 0, 0}, {0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0},
+ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+ {0x3800, 0x01, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3802, 0x01, 0, 0},
+ {0x3803, 0xb2, 0, 0}, {0x3804, 0x08, 0, 0}, {0x3805, 0xef, 0, 0},
+ {0x3806, 0x05, 0, 0}, {0x3807, 0xf1, 0, 0}, {0x3808, 0x07, 0, 0},
+ {0x3809, 0x80, 0, 0}, {0x380a, 0x04, 0, 0}, {0x380b, 0x38, 0, 0},
+ {0x380c, 0x09, 0, 0}, {0x380d, 0xc4, 0, 0}, {0x380e, 0x04, 0, 0},
+ {0x380f, 0x60, 0, 0}, {0x3612, 0x2b, 0, 0}, {0x3708, 0x64, 0, 0},
+ {0x3a02, 0x04, 0, 0}, {0x3a03, 0x60, 0, 0}, {0x3a08, 0x01, 0, 0},
+ {0x3a09, 0x50, 0, 0}, {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x18, 0, 0},
+ {0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x04, 0, 0},
+ {0x3a15, 0x60, 0, 0}, {0x4713, 0x02, 0, 0}, {0x4407, 0x04, 0, 0},
+ {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3824, 0x04, 0, 0},
+ {0x4005, 0x1a, 0, 0}, {0x3008, 0x02, 0, 0},
+ {0x3503, 0, 0, 0},
+};
+
+static struct reg_value ov5640_setting_15fps_QSXGA_2592_1944[] = {
+ {0x3008, 0x42, 0, 0},
+ {0x4202, 0x0f, 0, 0}, /* stream off the sensor */
+ {0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, /*disable flip*/
+ {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x08, 0, 0},
+ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+ {0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, {0x3814, 0x11, 0, 0},
+ {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+ {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0},
+ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0},
+ {0x3808, 0x0a, 0, 0}, {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0},
+ {0x380b, 0x98, 0, 0}, {0x380c, 0x0b, 0, 0}, {0x380d, 0x1c, 0, 0},
+ {0x380e, 0x07, 0, 0}, {0x380f, 0xb0, 0, 0}, {0x3810, 0x00, 0, 0},
+ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
+ {0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0},
+ {0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0},
+ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+ {0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4713, 0x03, 0, 0},
+ {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+ {0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 70}, {0x3008, 0x02, 0, 0},
+ {0x4202, 0x00, 0, 0}, /* stream on the sensor */
+};
+
+static struct ov5640_mode_info ov5640_mode_info_data[2][ov5640_mode_MAX + 1] = {
+ {
+ {ov5640_mode_VGA_640_480, -1, 0, 0, NULL, 0},
+ {ov5640_mode_NTSC_720_480, -1, 0, 0, NULL, 0},
+ {ov5640_mode_720P_1280_720, -1, 0, 0, NULL, 0},
+ {ov5640_mode_1080P_1920_1080, -1, 0, 0, NULL, 0},
+ {ov5640_mode_QSXGA_2592_1944, SCALING, 2592, 1944,
+ ov5640_setting_15fps_QSXGA_2592_1944,
+ ARRAY_SIZE(ov5640_setting_15fps_QSXGA_2592_1944)},
+ },
+ {
+ {ov5640_mode_VGA_640_480, SUBSAMPLING, 640, 480,
+ ov5640_setting_30fps_VGA_640_480,
+ ARRAY_SIZE(ov5640_setting_30fps_VGA_640_480)},
+ {ov5640_mode_NTSC_720_480, SUBSAMPLING, 720, 480,
+ ov5640_setting_30fps_NTSC_720_480,
+ ARRAY_SIZE(ov5640_setting_30fps_NTSC_720_480)},
+ {ov5640_mode_720P_1280_720, SUBSAMPLING, 1280, 720,
+ ov5640_setting_30fps_720P_1280_720,
+ ARRAY_SIZE(ov5640_setting_30fps_720P_1280_720)},
+ {ov5640_mode_1080P_1920_1080, SCALING, 1920, 1080,
+ ov5640_setting_30fps_1080P_1920_1080,
+ ARRAY_SIZE(ov5640_setting_30fps_1080P_1920_1080)},
+ {ov5640_mode_QSXGA_2592_1944, -1, 0, 0, NULL, 0},
+ },
+};
+
+static struct regulator *io_regulator;
+static struct regulator *core_regulator;
+static struct regulator *analog_regulator;
+static struct regulator *gpo_regulator;
+
+static int ov5640_probe(struct i2c_client *adapter,
+ const struct i2c_device_id *device_id);
+static int ov5640_remove(struct i2c_client *client);
+
+static s32 ov5640_read_reg(u16 reg, u8 *val);
+static s32 ov5640_write_reg(u16 reg, u8 val);
+
+static const struct i2c_device_id ov5640_id[] = {
+ {"ov5640_mipi", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, ov5640_id);
+
+static struct i2c_driver ov5640_i2c_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "ov5640_mipi",
+ },
+ .probe = ov5640_probe,
+ .remove = ov5640_remove,
+ .id_table = ov5640_id,
+};
+
+static const struct ov5640_datafmt ov5640_colour_fmts[] = {
+ {MEDIA_BUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG},
+};
+
+static struct ov5640 *to_ov5640(const struct i2c_client *client)
+{
+ return container_of(i2c_get_clientdata(client), struct ov5640, subdev);
+}
+
+/* Find a data format by a pixel code in an array */
+static const struct ov5640_datafmt
+ *ov5640_find_datafmt(u32 code)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ov5640_colour_fmts); i++)
+ if (ov5640_colour_fmts[i].code == code)
+ return ov5640_colour_fmts + i;
+
+ return NULL;
+}
+
+static inline void ov5640_power_down(int enable)
+{
+ if (pwn_gpio < 0)
+ return;
+
+ if (!enable)
+ gpio_set_value_cansleep(pwn_gpio, 0);
+ else
+ gpio_set_value_cansleep(pwn_gpio, 1);
+
+ msleep(2);
+}
+
+static void ov5640_reset(void)
+{
+ if (rst_gpio < 0 || pwn_gpio < 0)
+ return;
+
+ /* camera reset */
+ gpio_set_value(rst_gpio, 1);
+
+ /* camera power dowmn */
+ gpio_set_value(pwn_gpio, 1);
+ msleep(5);
+
+ gpio_set_value(pwn_gpio, 0);
+ msleep(5);
+
+ gpio_set_value(rst_gpio, 0);
+ msleep(1);
+
+ gpio_set_value(rst_gpio, 1);
+ msleep(5);
+
+ gpio_set_value(pwn_gpio, 1);
+}
+
+static int ov5640_regulator_enable(struct device *dev)
+{
+ int ret = 0;
+
+ io_regulator = devm_regulator_get(dev, "DOVDD");
+ if (!IS_ERR(io_regulator)) {
+ regulator_set_voltage(io_regulator,
+ OV5640_VOLTAGE_DIGITAL_IO,
+ OV5640_VOLTAGE_DIGITAL_IO);
+ ret = regulator_enable(io_regulator);
+ if (ret) {
+ pr_err("%s:io set voltage error\n", __func__);
+ return ret;
+ } else {
+ dev_dbg(dev,
+ "%s:io set voltage ok\n", __func__);
+ }
+ } else {
+ pr_err("%s: cannot get io voltage error\n", __func__);
+ io_regulator = NULL;
+ }
+
+ core_regulator = devm_regulator_get(dev, "DVDD");
+ if (!IS_ERR(core_regulator)) {
+ regulator_set_voltage(core_regulator,
+ OV5640_VOLTAGE_DIGITAL_CORE,
+ OV5640_VOLTAGE_DIGITAL_CORE);
+ ret = regulator_enable(core_regulator);
+ if (ret) {
+ pr_err("%s:core set voltage error\n", __func__);
+ return ret;
+ } else {
+ dev_dbg(dev,
+ "%s:core set voltage ok\n", __func__);
+ }
+ } else {
+ core_regulator = NULL;
+ pr_err("%s: cannot get core voltage error\n", __func__);
+ }
+
+ analog_regulator = devm_regulator_get(dev, "AVDD");
+ if (!IS_ERR(analog_regulator)) {
+ regulator_set_voltage(analog_regulator,
+ OV5640_VOLTAGE_ANALOG,
+ OV5640_VOLTAGE_ANALOG);
+ ret = regulator_enable(analog_regulator);
+ if (ret) {
+ pr_err("%s:analog set voltage error\n",
+ __func__);
+ return ret;
+ } else {
+ dev_dbg(dev,
+ "%s:analog set voltage ok\n", __func__);
+ }
+ } else {
+ analog_regulator = NULL;
+ pr_err("%s: cannot get analog voltage error\n", __func__);
+ }
+
+ return ret;
+}
+
+static s32 ov5640_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(ov5640_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 ov5640_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(ov5640_data.i2c_client, au8RegBuf, 2)) {
+ pr_err("%s:write reg error:reg=%x\n",
+ __func__, reg);
+ return -1;
+ }
+
+ if (1 != i2c_master_recv(ov5640_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 prev_sysclk, prev_HTS;
+static int AE_low, AE_high, AE_Target = 52;
+
+static void OV5640_stream_on(void)
+{
+ ov5640_write_reg(0x4202, 0x00);
+}
+
+static void OV5640_stream_off(void)
+{
+ ov5640_write_reg(0x4202, 0x0f);
+ ov5640_write_reg(0x3008, 0x42);
+}
+
+static int OV5640_get_sysclk(void)
+{
+ /* calculate sysclk */
+ int xvclk = ov5640_data.mclk / 10000;
+ int temp1, temp2;
+ int Multiplier, PreDiv, VCO, SysDiv, Pll_rdiv;
+ int Bit_div2x = 1, sclk_rdiv, sysclk;
+ u8 temp;
+
+ int sclk_rdiv_map[] = {1, 2, 4, 8};
+
+ temp1 = ov5640_read_reg(0x3034, &temp);
+ temp2 = temp1 & 0x0f;
+ if (temp2 == 8 || temp2 == 10)
+ Bit_div2x = temp2 / 2;
+
+ temp1 = ov5640_read_reg(0x3035, &temp);
+ SysDiv = temp1>>4;
+ if (SysDiv == 0)
+ SysDiv = 16;
+
+ temp1 = ov5640_read_reg(0x3036, &temp);
+ Multiplier = temp1;
+
+ temp1 = ov5640_read_reg(0x3037, &temp);
+ PreDiv = temp1 & 0x0f;
+ Pll_rdiv = ((temp1 >> 4) & 0x01) + 1;
+
+ temp1 = ov5640_read_reg(0x3108, &temp);
+ temp2 = temp1 & 0x03;
+ sclk_rdiv = sclk_rdiv_map[temp2];
+
+ VCO = xvclk * Multiplier / PreDiv;
+
+ sysclk = VCO / SysDiv / Pll_rdiv * 2 / Bit_div2x / sclk_rdiv;
+
+ return sysclk;
+}
+
+static void OV5640_set_night_mode(void)
+{
+ /* read HTS from register settings */
+ u8 mode;
+
+ ov5640_read_reg(0x3a00, &mode);
+ mode &= 0xfb;
+ ov5640_write_reg(0x3a00, mode);
+}
+
+static int OV5640_get_HTS(void)
+{
+ /* read HTS from register settings */
+ int HTS;
+ u8 temp;
+
+ HTS = ov5640_read_reg(0x380c, &temp);
+ HTS = (HTS<<8) + ov5640_read_reg(0x380d, &temp);
+
+ return HTS;
+}
+
+static int OV5640_get_VTS(void)
+{
+ /* read VTS from register settings */
+ int VTS;
+ u8 temp;
+
+ /* total vertical size[15:8] high byte */
+ VTS = ov5640_read_reg(0x380e, &temp);
+
+ VTS = (VTS<<8) + ov5640_read_reg(0x380f, &temp);
+
+ return VTS;
+}
+
+static int OV5640_set_VTS(int VTS)
+{
+ /* write VTS to registers */
+ int temp;
+
+ temp = VTS & 0xff;
+ ov5640_write_reg(0x380f, temp);
+
+ temp = VTS>>8;
+ ov5640_write_reg(0x380e, temp);
+
+ return 0;
+}
+
+static int OV5640_get_shutter(void)
+{
+ /* read shutter, in number of line period */
+ int shutter;
+ u8 temp;
+
+ shutter = (ov5640_read_reg(0x03500, &temp) & 0x0f);
+ shutter = (shutter<<8) + ov5640_read_reg(0x3501, &temp);
+ shutter = (shutter<<4) + (ov5640_read_reg(0x3502, &temp)>>4);
+
+ return shutter;
+}
+
+static int OV5640_set_shutter(int shutter)
+{
+ /* write shutter, in number of line period */
+ int temp;
+
+ shutter = shutter & 0xffff;
+
+ temp = shutter & 0x0f;
+ temp = temp<<4;
+ ov5640_write_reg(0x3502, temp);
+
+ temp = shutter & 0xfff;
+ temp = temp>>4;
+ ov5640_write_reg(0x3501, temp);
+
+ temp = shutter>>12;
+ ov5640_write_reg(0x3500, temp);
+
+ return 0;
+}
+
+static int OV5640_get_gain16(void)
+{
+ /* read gain, 16 = 1x */
+ int gain16;
+ u8 temp;
+
+ gain16 = ov5640_read_reg(0x350a, &temp) & 0x03;
+ gain16 = (gain16<<8) + ov5640_read_reg(0x350b, &temp);
+
+ return gain16;
+}
+
+static int OV5640_set_gain16(int gain16)
+{
+ /* write gain, 16 = 1x */
+ u8 temp;
+ gain16 = gain16 & 0x3ff;
+
+ temp = gain16 & 0xff;
+ ov5640_write_reg(0x350b, temp);
+
+ temp = gain16>>8;
+ ov5640_write_reg(0x350a, temp);
+
+ return 0;
+}
+
+static int OV5640_get_light_freq(void)
+{
+ /* get banding filter value */
+ int temp, temp1, light_freq = 0;
+ u8 tmp;
+
+ temp = ov5640_read_reg(0x3c01, &tmp);
+
+ if (temp & 0x80) {
+ /* manual */
+ temp1 = ov5640_read_reg(0x3c00, &tmp);
+ if (temp1 & 0x04) {
+ /* 50Hz */
+ light_freq = 50;
+ } else {
+ /* 60Hz */
+ light_freq = 60;
+ }
+ } else {
+ /* auto */
+ temp1 = ov5640_read_reg(0x3c0c, &tmp);
+ if (temp1 & 0x01) {
+ /* 50Hz */
+ light_freq = 50;
+ } else {
+ /* 60Hz */
+ }
+ }
+ return light_freq;
+}
+
+static void OV5640_set_bandingfilter(void)
+{
+ int prev_VTS;
+ int band_step60, max_band60, band_step50, max_band50;
+
+ /* read preview PCLK */
+ prev_sysclk = OV5640_get_sysclk();
+ /* read preview HTS */
+ prev_HTS = OV5640_get_HTS();
+
+ /* read preview VTS */
+ prev_VTS = OV5640_get_VTS();
+
+ /* calculate banding filter */
+ /* 60Hz */
+ band_step60 = prev_sysclk * 100/prev_HTS * 100/120;
+ ov5640_write_reg(0x3a0a, (band_step60 >> 8));
+ ov5640_write_reg(0x3a0b, (band_step60 & 0xff));
+
+ max_band60 = (int)((prev_VTS-4)/band_step60);
+ ov5640_write_reg(0x3a0d, max_band60);
+
+ /* 50Hz */
+ band_step50 = prev_sysclk * 100/prev_HTS;
+ ov5640_write_reg(0x3a08, (band_step50 >> 8));
+ ov5640_write_reg(0x3a09, (band_step50 & 0xff));
+
+ max_band50 = (int)((prev_VTS-4)/band_step50);
+ ov5640_write_reg(0x3a0e, max_band50);
+}
+
+static int OV5640_set_AE_target(int target)
+{
+ /* stable in high */
+ int fast_high, fast_low;
+ AE_low = target * 23 / 25; /* 0.92 */
+ AE_high = target * 27 / 25; /* 1.08 */
+
+ fast_high = AE_high<<1;
+ if (fast_high > 255)
+ fast_high = 255;
+
+ fast_low = AE_low >> 1;
+
+ ov5640_write_reg(0x3a0f, AE_high);
+ ov5640_write_reg(0x3a10, AE_low);
+ ov5640_write_reg(0x3a1b, AE_high);
+ ov5640_write_reg(0x3a1e, AE_low);
+ ov5640_write_reg(0x3a11, fast_high);
+ ov5640_write_reg(0x3a1f, fast_low);
+
+ return 0;
+}
+
+static void OV5640_turn_on_AE_AG(int enable)
+{
+ u8 ae_ag_ctrl;
+
+ ov5640_read_reg(0x3503, &ae_ag_ctrl);
+ if (enable) {
+ /* turn on auto AE/AG */
+ ae_ag_ctrl = ae_ag_ctrl & ~(0x03);
+ } else {
+ /* turn off AE/AG */
+ ae_ag_ctrl = ae_ag_ctrl | 0x03;
+ }
+ ov5640_write_reg(0x3503, ae_ag_ctrl);
+}
+
+static bool binning_on(void)
+{
+ u8 temp;
+ ov5640_read_reg(0x3821, &temp);
+ temp &= 0xfe;
+ if (temp)
+ return true;
+ else
+ return false;
+}
+
+static void ov5640_set_virtual_channel(int channel)
+{
+ u8 channel_id;
+
+ ov5640_read_reg(0x4814, &channel_id);
+ channel_id &= ~(3 << 6);
+ ov5640_write_reg(0x4814, channel_id | (channel << 6));
+}
+
+/* download ov5640 settings to sensor through i2c */
+static int ov5640_download_firmware(struct reg_value *pModeSetting, s32 ArySize)
+{
+ register u32 Delay_ms = 0;
+ register u16 RegAddr = 0;
+ register u8 Mask = 0;
+ register u8 Val = 0;
+ u8 RegVal = 0;
+ int i, retval = 0;
+
+ for (i = 0; i < ArySize; ++i, ++pModeSetting) {
+ Delay_ms = pModeSetting->u32Delay_ms;
+ RegAddr = pModeSetting->u16RegAddr;
+ Val = pModeSetting->u8Val;
+ Mask = pModeSetting->u8Mask;
+
+ if (Mask) {
+ retval = ov5640_read_reg(RegAddr, &RegVal);
+ if (retval < 0)
+ goto err;
+
+ RegVal &= ~(u8)Mask;
+ Val &= Mask;
+ Val |= RegVal;
+ }
+
+ retval = ov5640_write_reg(RegAddr, Val);
+ if (retval < 0)
+ goto err;
+
+ if (Delay_ms)
+ msleep(Delay_ms);
+ }
+err:
+ return retval;
+}
+
+/* sensor changes between scaling and subsampling
+ * go through exposure calcualtion
+ */
+static int ov5640_change_mode_exposure_calc(enum ov5640_frame_rate frame_rate,
+ enum ov5640_mode mode)
+{
+ struct reg_value *pModeSetting = NULL;
+ s32 ArySize = 0;
+ u8 average;
+ int prev_shutter, prev_gain16;
+ int cap_shutter, cap_gain16;
+ int cap_sysclk, cap_HTS, cap_VTS;
+ int light_freq, cap_bandfilt, cap_maxband;
+ long cap_gain16_shutter;
+ int retval = 0;
+
+ /* check if the input mode and frame rate is valid */
+ pModeSetting =
+ ov5640_mode_info_data[frame_rate][mode].init_data_ptr;
+ ArySize =
+ ov5640_mode_info_data[frame_rate][mode].init_data_size;
+
+ ov5640_data.pix.width =
+ ov5640_mode_info_data[frame_rate][mode].width;
+ ov5640_data.pix.height =
+ ov5640_mode_info_data[frame_rate][mode].height;
+
+ if (ov5640_data.pix.width == 0 || ov5640_data.pix.height == 0 ||
+ pModeSetting == NULL || ArySize == 0)
+ return -EINVAL;
+
+ /* auto focus */
+ /* OV5640_auto_focus();//if no af function, just skip it */
+
+ /* turn off AE/AG */
+ OV5640_turn_on_AE_AG(0);
+
+ /* read preview shutter */
+ prev_shutter = OV5640_get_shutter();
+ if ((binning_on()) && (mode != ov5640_mode_720P_1280_720)
+ && (mode != ov5640_mode_1080P_1920_1080))
+ prev_shutter *= 2;
+
+ /* read preview gain */
+ prev_gain16 = OV5640_get_gain16();
+
+ /* get average */
+ ov5640_read_reg(0x56a1, &average);
+
+ /* turn off night mode for capture */
+ OV5640_set_night_mode();
+
+ /* turn off overlay */
+ /* ov5640_write_reg(0x3022, 0x06);//if no af function, just skip it */
+
+ OV5640_stream_off();
+
+ /* Write capture setting */
+ retval = ov5640_download_firmware(pModeSetting, ArySize);
+ if (retval < 0)
+ goto err;
+
+ /* read capture VTS */
+ cap_VTS = OV5640_get_VTS();
+ cap_HTS = OV5640_get_HTS();
+ cap_sysclk = OV5640_get_sysclk();
+
+ /* calculate capture banding filter */
+ light_freq = OV5640_get_light_freq();
+ if (light_freq == 60) {
+ /* 60Hz */
+ cap_bandfilt = cap_sysclk * 100 / cap_HTS * 100 / 120;
+ } else {
+ /* 50Hz */
+ cap_bandfilt = cap_sysclk * 100 / cap_HTS;
+ }
+ cap_maxband = (int)((cap_VTS - 4)/cap_bandfilt);
+
+ /* calculate capture shutter/gain16 */
+ if (average > AE_low && average < AE_high) {
+ /* in stable range */
+ cap_gain16_shutter =
+ prev_gain16 * prev_shutter * cap_sysclk/prev_sysclk
+ * prev_HTS/cap_HTS * AE_Target / average;
+ } else {
+ cap_gain16_shutter =
+ prev_gain16 * prev_shutter * cap_sysclk/prev_sysclk
+ * prev_HTS/cap_HTS;
+ }
+
+ /* gain to shutter */
+ if (cap_gain16_shutter < (cap_bandfilt * 16)) {
+ /* shutter < 1/100 */
+ cap_shutter = cap_gain16_shutter/16;
+ if (cap_shutter < 1)
+ cap_shutter = 1;
+
+ cap_gain16 = cap_gain16_shutter/cap_shutter;
+ if (cap_gain16 < 16)
+ cap_gain16 = 16;
+ } else {
+ if (cap_gain16_shutter >
+ (cap_bandfilt * cap_maxband * 16)) {
+ /* exposure reach max */
+ cap_shutter = cap_bandfilt * cap_maxband;
+ cap_gain16 = cap_gain16_shutter / cap_shutter;
+ } else {
+ /* 1/100 < (cap_shutter = n/100) =< max */
+ cap_shutter =
+ ((int) (cap_gain16_shutter/16 / cap_bandfilt))
+ *cap_bandfilt;
+ cap_gain16 = cap_gain16_shutter / cap_shutter;
+ }
+ }
+
+ /* write capture gain */
+ OV5640_set_gain16(cap_gain16);
+
+ /* write capture shutter */
+ if (cap_shutter > (cap_VTS - 4)) {
+ cap_VTS = cap_shutter + 4;
+ OV5640_set_VTS(cap_VTS);
+ }
+ OV5640_set_shutter(cap_shutter);
+
+err:
+ return retval;
+}
+
+/* if sensor changes inside scaling or subsampling
+ * change mode directly
+ * */
+static int ov5640_change_mode_direct(enum ov5640_frame_rate frame_rate,
+ enum ov5640_mode mode)
+{
+ struct reg_value *pModeSetting = NULL;
+ s32 ArySize = 0;
+ int retval = 0;
+
+ /* check if the input mode and frame rate is valid */
+ pModeSetting =
+ ov5640_mode_info_data[frame_rate][mode].init_data_ptr;
+ ArySize =
+ ov5640_mode_info_data[frame_rate][mode].init_data_size;
+
+ ov5640_data.pix.width =
+ ov5640_mode_info_data[frame_rate][mode].width;
+ ov5640_data.pix.height =
+ ov5640_mode_info_data[frame_rate][mode].height;
+
+ if (ov5640_data.pix.width == 0 || ov5640_data.pix.height == 0 ||
+ pModeSetting == NULL || ArySize == 0)
+ return -EINVAL;
+
+ /* turn off AE/AG */
+ OV5640_turn_on_AE_AG(0);
+
+ OV5640_stream_off();
+
+ /* Write capture setting */
+ retval = ov5640_download_firmware(pModeSetting, ArySize);
+ if (retval < 0)
+ goto err;
+
+ OV5640_turn_on_AE_AG(1);
+
+err:
+ return retval;
+}
+
+static int ov5640_init_mode(enum ov5640_frame_rate frame_rate,
+ enum ov5640_mode mode, enum ov5640_mode orig_mode)
+{
+ struct reg_value *pModeSetting = NULL;
+ s32 ArySize = 0;
+ int retval = 0;
+ u32 msec_wait4stable = 0;
+ enum ov5640_downsize_mode dn_mode, orig_dn_mode;
+
+ if ((mode > ov5640_mode_MAX || mode < ov5640_mode_MIN)
+ && (mode != ov5640_mode_INIT)) {
+ pr_err("Wrong ov5640 mode detected!\n");
+ return -1;
+ }
+
+ dn_mode = ov5640_mode_info_data[frame_rate][mode].dn_mode;
+ orig_dn_mode = ov5640_mode_info_data[frame_rate][orig_mode].dn_mode;
+ if (mode == ov5640_mode_INIT) {
+ pModeSetting = ov5640_init_setting_30fps_VGA;
+ ArySize = ARRAY_SIZE(ov5640_init_setting_30fps_VGA);
+
+ ov5640_data.pix.width = 640;
+ ov5640_data.pix.height = 480;
+ retval = ov5640_download_firmware(pModeSetting, ArySize);
+ if (retval < 0)
+ goto err;
+
+ pModeSetting = ov5640_setting_30fps_VGA_640_480;
+ ArySize = ARRAY_SIZE(ov5640_setting_30fps_VGA_640_480);
+ retval = ov5640_download_firmware(pModeSetting, ArySize);
+ } else if ((dn_mode == SUBSAMPLING && orig_dn_mode == SCALING) ||
+ (dn_mode == SCALING && orig_dn_mode == SUBSAMPLING)) {
+ /* change between subsampling and scaling
+ * go through exposure calucation */
+ retval = ov5640_change_mode_exposure_calc(frame_rate, mode);
+ } else {
+ /* change inside subsampling or scaling
+ * download firmware directly */
+ retval = ov5640_change_mode_direct(frame_rate, mode);
+ }
+
+ if (retval < 0)
+ goto err;
+
+ OV5640_set_AE_target(AE_Target);
+ OV5640_get_light_freq();
+ OV5640_set_bandingfilter();
+ ov5640_set_virtual_channel(ov5640_data.csi);
+
+ /* add delay to wait for sensor stable */
+ if (mode == ov5640_mode_QSXGA_2592_1944) {
+ /* dump the first two frames: 1/7.5*2
+ * the frame rate of QSXGA is 7.5fps */
+ msec_wait4stable = 267;
+ } else {
+ /* dump the first eighteen frames: 1/30*18 */
+ msec_wait4stable = 600;
+ }
+ msleep(msec_wait4stable);
+
+err:
+ return retval;
+}
+
+/*!
+ * ov5640_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 ov5640_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov5640 *sensor = to_ov5640(client);
+
+ if (on && !sensor->on) {
+ 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);
+ }
+
+ sensor->on = on;
+
+ return 0;
+}
+
+/*!
+ * ov5640_g_parm - V4L2 sensor interface handler for VIDIOC_G_PARM ioctl
+ * @s: pointer to standard V4L2 sub device structure
+ * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure
+ *
+ * Returns the sensor's video CAPTURE parameters.
+ */
+static int ov5640_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov5640 *sensor = to_ov5640(client);
+ 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;
+}
+
+/*!
+ * ov5460_s_parm - V4L2 sensor interface handler for VIDIOC_S_PARM ioctl
+ * @s: pointer to standard V4L2 sub 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 ov5640_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov5640 *sensor = to_ov5640(client);
+ struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe;
+ u32 tgt_fps; /* target frames per secound */
+ enum ov5640_frame_rate frame_rate;
+ enum ov5640_mode orig_mode;
+ int ret = 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 = ov5640_15_fps;
+ else if (tgt_fps == 30)
+ frame_rate = ov5640_30_fps;
+ else {
+ pr_err(" The camera frame rate is not supported!\n");
+ return -EINVAL;
+ }
+
+ orig_mode = sensor->streamcap.capturemode;
+ ret = ov5640_init_mode(frame_rate,
+ (u32)a->parm.capture.capturemode, orig_mode);
+ if (ret < 0)
+ return ret;
+
+ sensor->streamcap.timeperframe = *timeperframe;
+ sensor->streamcap.capturemode =
+ (u32)a->parm.capture.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;
+}
+
+static int ov5640_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
+{
+ struct v4l2_mbus_framefmt *mf = &format->format;
+ const struct ov5640_datafmt *fmt = ov5640_find_datafmt(mf->code);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov5640 *sensor = to_ov5640(client);
+
+ if (!fmt) {
+ mf->code = ov5640_colour_fmts[0].code;
+ mf->colorspace = ov5640_colour_fmts[0].colorspace;
+ }
+
+ mf->field = V4L2_FIELD_NONE;
+
+ if (format->which == V4L2_SUBDEV_FORMAT_TRY)
+ return 0;
+
+ sensor->fmt = fmt;
+
+ return 0;
+}
+
+
+static int ov5640_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
+{
+ struct v4l2_mbus_framefmt *mf = &format->format;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov5640 *sensor = to_ov5640(client);
+ const struct ov5640_datafmt *fmt = sensor->fmt;
+
+ if (format->pad)
+ return -EINVAL;
+
+ mf->code = fmt->code;
+ mf->colorspace = fmt->colorspace;
+ mf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int ov5640_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->pad || code->index >= ARRAY_SIZE(ov5640_colour_fmts))
+ return -EINVAL;
+
+ code->code = ov5640_colour_fmts[code->index].code;
+ return 0;
+}
+
+/*!
+ * ov5640_enum_framesizes - V4L2 sensor interface handler for
+ * VIDIOC_ENUM_FRAMESIZES ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @fsize: standard V4L2 VIDIOC_ENUM_FRAMESIZES ioctl structure
+ *
+ * Return 0 if successful, otherwise -EINVAL.
+ */
+static int ov5640_enum_framesizes(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ if (fse->index > ov5640_mode_MAX)
+ return -EINVAL;
+
+ fse->max_width =
+ max(ov5640_mode_info_data[0][fse->index].width,
+ ov5640_mode_info_data[1][fse->index].width);
+ fse->min_width = fse->max_width;
+ fse->max_height =
+ max(ov5640_mode_info_data[0][fse->index].height,
+ ov5640_mode_info_data[1][fse->index].height);
+ fse->min_height = fse->max_height;
+ return 0;
+}
+
+/*!
+ * ov5640_enum_frameintervals - V4L2 sensor interface handler for
+ * VIDIOC_ENUM_FRAMEINTERVALS ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @fival: standard V4L2 VIDIOC_ENUM_FRAMEINTERVALS ioctl structure
+ *
+ * Return 0 if successful, otherwise -EINVAL.
+ */
+static int ov5640_enum_frameintervals(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_interval_enum *fie)
+{
+ int i, j, count = 0;
+
+ if (fie->index < 0 || fie->index > ov5640_mode_MAX)
+ return -EINVAL;
+
+ if (fie->width == 0 || fie->height == 0 ||
+ fie->code == 0) {
+ pr_warning("Please assign pixel format, width and height.\n");
+ return -EINVAL;
+ }
+
+ fie->interval.numerator = 1;
+
+ count = 0;
+ for (i = 0; i < ARRAY_SIZE(ov5640_mode_info_data); i++) {
+ for (j = 0; j < (ov5640_mode_MAX + 1); j++) {
+ if (fie->width == ov5640_mode_info_data[i][j].width
+ && fie->height == ov5640_mode_info_data[i][j].height
+ && ov5640_mode_info_data[i][j].init_data_ptr != NULL) {
+ count++;
+ }
+ if (fie->index == (count - 1)) {
+ fie->interval.denominator =
+ ov5640_framerates[i];
+ return 0;
+ }
+ }
+ }
+
+ return -EINVAL;
+}
+
+/*!
+ * dev_init - V4L2 sensor init
+ * @s: pointer to standard V4L2 device structure
+ *
+ */
+static int init_device(void)
+{
+ u32 tgt_xclk; /* target xclk */
+ u32 tgt_fps; /* target frames per secound */
+ enum ov5640_frame_rate frame_rate;
+ int ret;
+
+ ov5640_data.on = true;
+
+ /* mclk */
+ tgt_xclk = ov5640_data.mclk;
+ tgt_xclk = min(tgt_xclk, (u32)OV5640_XCLK_MAX);
+ tgt_xclk = max(tgt_xclk, (u32)OV5640_XCLK_MIN);
+ ov5640_data.mclk = tgt_xclk;
+
+ pr_debug(" Setting mclk to %d MHz\n", tgt_xclk / 1000000);
+
+ /* Default camera frame rate is set in probe */
+ tgt_fps = ov5640_data.streamcap.timeperframe.denominator /
+ ov5640_data.streamcap.timeperframe.numerator;
+
+ if (tgt_fps == 15)
+ frame_rate = ov5640_15_fps;
+ else if (tgt_fps == 30)
+ frame_rate = ov5640_30_fps;
+ else
+ return -EINVAL; /* Only support 15fps or 30fps now. */
+
+ ret = ov5640_init_mode(frame_rate, ov5640_mode_INIT, ov5640_mode_INIT);
+
+ return ret;
+}
+
+static int ov5640_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ if (enable)
+ OV5640_stream_on();
+ else
+ OV5640_stream_off();
+ return 0;
+}
+
+static struct v4l2_subdev_video_ops ov5640_subdev_video_ops = {
+ .g_parm = ov5640_g_parm,
+ .s_parm = ov5640_s_parm,
+ .s_stream = ov5640_s_stream,
+};
+
+static const struct v4l2_subdev_pad_ops ov5640_subdev_pad_ops = {
+ .enum_frame_size = ov5640_enum_framesizes,
+ .enum_frame_interval = ov5640_enum_frameintervals,
+ .enum_mbus_code = ov5640_enum_mbus_code,
+ .set_fmt = ov5640_set_fmt,
+ .get_fmt = ov5640_get_fmt,
+};
+
+static struct v4l2_subdev_core_ops ov5640_subdev_core_ops = {
+ .s_power = ov5640_s_power,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = ov5640_get_register,
+ .s_register = ov5640_set_register,
+#endif
+};
+
+static struct v4l2_subdev_ops ov5640_subdev_ops = {
+ .core = &ov5640_subdev_core_ops,
+ .video = &ov5640_subdev_video_ops,
+ .pad = &ov5640_subdev_pad_ops,
+};
+
+
+/*!
+ * ov5640 I2C probe function
+ *
+ * @param adapter struct i2c_adapter *
+ * @return Error code indicating success or failure
+ */
+static int ov5640_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct pinctrl *pinctrl;
+ struct device *dev = &client->dev;
+ int retval;
+ u8 chip_id_high, chip_id_low;
+
+ /* ov5640 pinctrl */
+ pinctrl = devm_pinctrl_get_select_default(dev);
+ if (IS_ERR(pinctrl))
+ dev_warn(dev, "no pin available\n");
+
+ /* request power down pin */
+ pwn_gpio = of_get_named_gpio(dev->of_node, "pwn-gpios", 0);
+ if (!gpio_is_valid(pwn_gpio))
+ dev_warn(dev, "no sensor pwdn pin available");
+ else {
+ retval = devm_gpio_request_one(dev, pwn_gpio, GPIOF_OUT_INIT_HIGH,
+ "ov5640_mipi_pwdn");
+ if (retval < 0) {
+ dev_warn(dev, "Failed to set power pin\n");
+ dev_warn(dev, "retval=%d\n", retval);
+ return retval;
+ }
+ }
+
+ /* request reset pin */
+ rst_gpio = of_get_named_gpio(dev->of_node, "rst-gpios", 0);
+ if (!gpio_is_valid(rst_gpio))
+ dev_warn(dev, "no sensor reset pin available");
+ else {
+ retval = devm_gpio_request_one(dev, rst_gpio, GPIOF_OUT_INIT_HIGH,
+ "ov5640_mipi_reset");
+ if (retval < 0) {
+ dev_warn(dev, "Failed to set reset pin\n");
+ return retval;
+ }
+ }
+
+ /* Set initial values for the sensor struct. */
+ memset(&ov5640_data, 0, sizeof(ov5640_data));
+ ov5640_data.sensor_clk = devm_clk_get(dev, "csi_mclk");
+ if (IS_ERR(ov5640_data.sensor_clk)) {
+ /* assuming clock enabled by default */
+ ov5640_data.sensor_clk = NULL;
+ dev_err(dev, "clock-frequency missing or invalid\n");
+ return PTR_ERR(ov5640_data.sensor_clk);
+ }
+
+ retval = of_property_read_u32(dev->of_node, "mclk",
+ &(ov5640_data.mclk));
+ if (retval) {
+ dev_err(dev, "mclk missing or invalid\n");
+ return retval;
+ }
+
+ retval = of_property_read_u32(dev->of_node, "mclk_source",
+ (u32 *) &(ov5640_data.mclk_source));
+ if (retval) {
+ dev_err(dev, "mclk_source missing or invalid\n");
+ return retval;
+ }
+
+ retval = of_property_read_u32(dev->of_node, "csi_id",
+ &(ov5640_data.csi));
+ if (retval) {
+ dev_err(dev, "csi id missing or invalid\n");
+ return retval;
+ }
+
+ clk_prepare_enable(ov5640_data.sensor_clk);
+
+ ov5640_data.io_init = ov5640_reset;
+ ov5640_data.i2c_client = client;
+ ov5640_data.pix.pixelformat = V4L2_PIX_FMT_YUYV;
+ ov5640_data.pix.width = 640;
+ ov5640_data.pix.height = 480;
+ ov5640_data.streamcap.capability = V4L2_MODE_HIGHQUALITY |
+ V4L2_CAP_TIMEPERFRAME;
+ ov5640_data.streamcap.capturemode = 0;
+ ov5640_data.streamcap.timeperframe.denominator = DEFAULT_FPS;
+ ov5640_data.streamcap.timeperframe.numerator = 1;
+
+ ov5640_regulator_enable(&client->dev);
+
+ ov5640_reset();
+
+ ov5640_power_down(0);
+
+ retval = ov5640_read_reg(OV5640_CHIP_ID_HIGH_BYTE, &chip_id_high);
+ if (retval < 0 || chip_id_high != 0x56) {
+ pr_warning("camera ov5640_mipi is not found\n");
+ clk_disable_unprepare(ov5640_data.sensor_clk);
+ return -ENODEV;
+ }
+ retval = ov5640_read_reg(OV5640_CHIP_ID_LOW_BYTE, &chip_id_low);
+ if (retval < 0 || chip_id_low != 0x40) {
+ pr_warning("camera ov5640_mipi is not found\n");
+ clk_disable_unprepare(ov5640_data.sensor_clk);
+ return -ENODEV;
+ }
+
+ retval = init_device();
+ if (retval < 0) {
+ clk_disable_unprepare(ov5640_data.sensor_clk);
+ pr_warning("camera ov5640 init failed\n");
+ ov5640_power_down(1);
+ return retval;
+ }
+
+ v4l2_i2c_subdev_init(&ov5640_data.subdev, client, &ov5640_subdev_ops);
+
+ ov5640_data.subdev.grp_id = 678;
+ retval = v4l2_async_register_subdev(&ov5640_data.subdev);
+ if (retval < 0)
+ dev_err(&client->dev,
+ "%s--Async register failed, ret=%d\n", __func__, retval);
+
+ OV5640_stream_off();
+ pr_info("camera ov5640_mipi is found\n");
+ return retval;
+}
+
+/*!
+ * ov5640 I2C detach function
+ *
+ * @param client struct i2c_client *
+ * @return Error code indicating success or failure
+ */
+static int ov5640_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_async_unregister_subdev(sd);
+
+ clk_disable_unprepare(ov5640_data.sensor_clk);
+
+ ov5640_power_down(1);
+
+ if (gpo_regulator)
+ regulator_disable(gpo_regulator);
+
+ if (analog_regulator)
+ regulator_disable(analog_regulator);
+
+ if (core_regulator)
+ regulator_disable(core_regulator);
+
+ if (io_regulator)
+ regulator_disable(io_regulator);
+
+ return 0;
+}
+
+module_i2c_driver(ov5640_i2c_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("OV5640 MIPI Camera Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("CSI");
--- /dev/null
+/*
+ * Copyright (C) 2012-2015 Freescale Semiconductor, 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/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <linux/v4l2-mediabus.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+
+#define OV5640_VOLTAGE_ANALOG 2800000
+#define OV5640_VOLTAGE_DIGITAL_CORE 1500000
+#define OV5640_VOLTAGE_DIGITAL_IO 1800000
+
+#define MIN_FPS 15
+#define MAX_FPS 30
+#define DEFAULT_FPS 30
+
+#define OV5640_XCLK_MIN 6000000
+#define OV5640_XCLK_MAX 24000000
+
+#define OV5640_CHIP_ID_HIGH_BYTE 0x300A
+#define OV5640_CHIP_ID_LOW_BYTE 0x300B
+
+enum ov5640_mode {
+ ov5640_mode_MIN = 0,
+ ov5640_mode_VGA_640_480 = 0,
+ ov5640_mode_QVGA_320_240 = 1,
+ ov5640_mode_NTSC_720_480 = 2,
+ ov5640_mode_PAL_720_576 = 3,
+ ov5640_mode_720P_1280_720 = 4,
+ ov5640_mode_1080P_1920_1080 = 5,
+ ov5640_mode_QSXGA_2592_1944 = 6,
+ ov5640_mode_QCIF_176_144 = 7,
+ ov5640_mode_XGA_1024_768 = 8,
+ ov5640_mode_MAX = 8
+};
+
+enum ov5640_frame_rate {
+ ov5640_15_fps,
+ ov5640_30_fps
+};
+
+static int ov5640_framerates[] = {
+ [ov5640_15_fps] = 15,
+ [ov5640_30_fps] = 30,
+};
+
+struct ov5640_datafmt {
+ u32 code;
+ enum v4l2_colorspace colorspace;
+};
+
+struct reg_value {
+ u16 u16RegAddr;
+ u8 u8Val;
+ u8 u8Mask;
+ u32 u32Delay_ms;
+};
+
+struct ov5640_mode_info {
+ enum ov5640_mode mode;
+ u32 width;
+ u32 height;
+ struct reg_value *init_data_ptr;
+ u32 init_data_size;
+};
+
+struct ov5640 {
+ struct v4l2_subdev subdev;
+ struct i2c_client *i2c_client;
+ struct v4l2_pix_format pix;
+ const struct ov5640_datafmt *fmt;
+ 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;
+ u8 mclk_source;
+ struct clk *sensor_clk;
+ int csi;
+
+ void (*io_init)(void);
+};
+
+/*!
+ * Maintains the information on the current state of the sesor.
+ */
+static struct ov5640 ov5640_data;
+static int pwn_gpio, rst_gpio;
+static int prev_sysclk;
+static int AE_Target = 52, night_mode;
+static int prev_HTS;
+static int AE_high, AE_low;
+
+static struct reg_value ov5640_global_init_setting[] = {
+ {0x3008, 0x42, 0, 0},
+ {0x3103, 0x03, 0, 0}, {0x3017, 0xff, 0, 0}, {0x3018, 0xff, 0, 0},
+ {0x3034, 0x1a, 0, 0}, {0x3037, 0x13, 0, 0}, {0x3108, 0x01, 0, 0},
+ {0x3630, 0x36, 0, 0}, {0x3631, 0x0e, 0, 0}, {0x3632, 0xe2, 0, 0},
+ {0x3633, 0x12, 0, 0}, {0x3621, 0xe0, 0, 0}, {0x3704, 0xa0, 0, 0},
+ {0x3703, 0x5a, 0, 0}, {0x3715, 0x78, 0, 0}, {0x3717, 0x01, 0, 0},
+ {0x370b, 0x60, 0, 0}, {0x3705, 0x1a, 0, 0}, {0x3905, 0x02, 0, 0},
+ {0x3906, 0x10, 0, 0}, {0x3901, 0x0a, 0, 0}, {0x3731, 0x12, 0, 0},
+ {0x3600, 0x08, 0, 0}, {0x3601, 0x33, 0, 0}, {0x302d, 0x60, 0, 0},
+ {0x3620, 0x52, 0, 0}, {0x371b, 0x20, 0, 0}, {0x471c, 0x50, 0, 0},
+ {0x3a13, 0x43, 0, 0}, {0x3a18, 0x00, 0, 0}, {0x3a19, 0x7c, 0, 0},
+ {0x3635, 0x13, 0, 0}, {0x3636, 0x03, 0, 0}, {0x3634, 0x40, 0, 0},
+ {0x3622, 0x01, 0, 0}, {0x3c01, 0x34, 0, 0}, {0x3c04, 0x28, 0, 0},
+ {0x3c05, 0x98, 0, 0}, {0x3c06, 0x00, 0, 0}, {0x3c07, 0x07, 0, 0},
+ {0x3c08, 0x00, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0},
+ {0x3c0b, 0x40, 0, 0}, {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0},
+ {0x3812, 0x00, 0, 0}, {0x3708, 0x64, 0, 0}, {0x4001, 0x02, 0, 0},
+ {0x4005, 0x1a, 0, 0}, {0x3000, 0x00, 0, 0}, {0x3004, 0xff, 0, 0},
+ {0x300e, 0x58, 0, 0}, {0x302e, 0x00, 0, 0}, {0x4300, 0x30, 0, 0},
+ {0x501f, 0x00, 0, 0}, {0x440e, 0x00, 0, 0}, {0x5000, 0xa7, 0, 0},
+ {0x3008, 0x02, 0, 0},
+};
+
+static struct reg_value ov5640_init_setting_30fps_VGA[] = {
+ {0x3008, 0x42, 0, 0},
+ {0x3103, 0x03, 0, 0}, {0x3017, 0xff, 0, 0}, {0x3018, 0xff, 0, 0},
+ {0x3034, 0x1a, 0, 0}, {0x3035, 0x11, 0, 0}, {0x3036, 0x46, 0, 0},
+ {0x3037, 0x13, 0, 0}, {0x3108, 0x01, 0, 0}, {0x3630, 0x36, 0, 0},
+ {0x3631, 0x0e, 0, 0}, {0x3632, 0xe2, 0, 0}, {0x3633, 0x12, 0, 0},
+ {0x3621, 0xe0, 0, 0}, {0x3704, 0xa0, 0, 0}, {0x3703, 0x5a, 0, 0},
+ {0x3715, 0x78, 0, 0}, {0x3717, 0x01, 0, 0}, {0x370b, 0x60, 0, 0},
+ {0x3705, 0x1a, 0, 0}, {0x3905, 0x02, 0, 0}, {0x3906, 0x10, 0, 0},
+ {0x3901, 0x0a, 0, 0}, {0x3731, 0x12, 0, 0}, {0x3600, 0x08, 0, 0},
+ {0x3601, 0x33, 0, 0}, {0x302d, 0x60, 0, 0}, {0x3620, 0x52, 0, 0},
+ {0x371b, 0x20, 0, 0}, {0x471c, 0x50, 0, 0}, {0x3a13, 0x43, 0, 0},
+ {0x3a18, 0x00, 0, 0}, {0x3a19, 0xf8, 0, 0}, {0x3635, 0x13, 0, 0},
+ {0x3636, 0x03, 0, 0}, {0x3634, 0x40, 0, 0}, {0x3622, 0x01, 0, 0},
+ {0x3c01, 0x34, 0, 0}, {0x3c04, 0x28, 0, 0}, {0x3c05, 0x98, 0, 0},
+ {0x3c06, 0x00, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c08, 0x00, 0, 0},
+ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+ {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+ {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+ {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0},
+ {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+ {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x3000, 0x00, 0, 0},
+ {0x3002, 0x1c, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3006, 0xc3, 0, 0},
+ {0x300e, 0x58, 0, 0}, {0x302e, 0x00, 0, 0}, {0x4300, 0x30, 0, 0},
+ {0x501f, 0x00, 0, 0}, {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0},
+ {0x440e, 0x00, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+ {0x4837, 0x22, 0, 0}, {0x3824, 0x02, 0, 0}, {0x5000, 0xa7, 0, 0},
+ {0x5001, 0xa3, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0xf2, 0, 0},
+ {0x5182, 0x00, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0},
+ {0x5185, 0x24, 0, 0}, {0x5186, 0x09, 0, 0}, {0x5187, 0x09, 0, 0},
+ {0x5188, 0x09, 0, 0}, {0x5189, 0x88, 0, 0}, {0x518a, 0x54, 0, 0},
+ {0x518b, 0xee, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x50, 0, 0},
+ {0x518e, 0x34, 0, 0}, {0x518f, 0x6b, 0, 0}, {0x5190, 0x46, 0, 0},
+ {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0},
+ {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0},
+ {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x6c, 0, 0},
+ {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x09, 0, 0},
+ {0x519d, 0x2b, 0, 0}, {0x519e, 0x38, 0, 0}, {0x5381, 0x1e, 0, 0},
+ {0x5382, 0x5b, 0, 0}, {0x5383, 0x08, 0, 0}, {0x5384, 0x0a, 0, 0},
+ {0x5385, 0x7e, 0, 0}, {0x5386, 0x88, 0, 0}, {0x5387, 0x7c, 0, 0},
+ {0x5388, 0x6c, 0, 0}, {0x5389, 0x10, 0, 0}, {0x538a, 0x01, 0, 0},
+ {0x538b, 0x98, 0, 0}, {0x5300, 0x08, 0, 0}, {0x5301, 0x30, 0, 0},
+ {0x5302, 0x10, 0, 0}, {0x5303, 0x00, 0, 0}, {0x5304, 0x08, 0, 0},
+ {0x5305, 0x30, 0, 0}, {0x5306, 0x08, 0, 0}, {0x5307, 0x16, 0, 0},
+ {0x5309, 0x08, 0, 0}, {0x530a, 0x30, 0, 0}, {0x530b, 0x04, 0, 0},
+ {0x530c, 0x06, 0, 0}, {0x5480, 0x01, 0, 0}, {0x5481, 0x08, 0, 0},
+ {0x5482, 0x14, 0, 0}, {0x5483, 0x28, 0, 0}, {0x5484, 0x51, 0, 0},
+ {0x5485, 0x65, 0, 0}, {0x5486, 0x71, 0, 0}, {0x5487, 0x7d, 0, 0},
+ {0x5488, 0x87, 0, 0}, {0x5489, 0x91, 0, 0}, {0x548a, 0x9a, 0, 0},
+ {0x548b, 0xaa, 0, 0}, {0x548c, 0xb8, 0, 0}, {0x548d, 0xcd, 0, 0},
+ {0x548e, 0xdd, 0, 0}, {0x548f, 0xea, 0, 0}, {0x5490, 0x1d, 0, 0},
+ {0x5580, 0x02, 0, 0}, {0x5583, 0x40, 0, 0}, {0x5584, 0x10, 0, 0},
+ {0x5589, 0x10, 0, 0}, {0x558a, 0x00, 0, 0}, {0x558b, 0xf8, 0, 0},
+ {0x5800, 0x23, 0, 0}, {0x5801, 0x14, 0, 0}, {0x5802, 0x0f, 0, 0},
+ {0x5803, 0x0f, 0, 0}, {0x5804, 0x12, 0, 0}, {0x5805, 0x26, 0, 0},
+ {0x5806, 0x0c, 0, 0}, {0x5807, 0x08, 0, 0}, {0x5808, 0x05, 0, 0},
+ {0x5809, 0x05, 0, 0}, {0x580a, 0x08, 0, 0}, {0x580b, 0x0d, 0, 0},
+ {0x580c, 0x08, 0, 0}, {0x580d, 0x03, 0, 0}, {0x580e, 0x00, 0, 0},
+ {0x580f, 0x00, 0, 0}, {0x5810, 0x03, 0, 0}, {0x5811, 0x09, 0, 0},
+ {0x5812, 0x07, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x00, 0, 0},
+ {0x5815, 0x01, 0, 0}, {0x5816, 0x03, 0, 0}, {0x5817, 0x08, 0, 0},
+ {0x5818, 0x0d, 0, 0}, {0x5819, 0x08, 0, 0}, {0x581a, 0x05, 0, 0},
+ {0x581b, 0x06, 0, 0}, {0x581c, 0x08, 0, 0}, {0x581d, 0x0e, 0, 0},
+ {0x581e, 0x29, 0, 0}, {0x581f, 0x17, 0, 0}, {0x5820, 0x11, 0, 0},
+ {0x5821, 0x11, 0, 0}, {0x5822, 0x15, 0, 0}, {0x5823, 0x28, 0, 0},
+ {0x5824, 0x46, 0, 0}, {0x5825, 0x26, 0, 0}, {0x5826, 0x08, 0, 0},
+ {0x5827, 0x26, 0, 0}, {0x5828, 0x64, 0, 0}, {0x5829, 0x26, 0, 0},
+ {0x582a, 0x24, 0, 0}, {0x582b, 0x22, 0, 0}, {0x582c, 0x24, 0, 0},
+ {0x582d, 0x24, 0, 0}, {0x582e, 0x06, 0, 0}, {0x582f, 0x22, 0, 0},
+ {0x5830, 0x40, 0, 0}, {0x5831, 0x42, 0, 0}, {0x5832, 0x24, 0, 0},
+ {0x5833, 0x26, 0, 0}, {0x5834, 0x24, 0, 0}, {0x5835, 0x22, 0, 0},
+ {0x5836, 0x22, 0, 0}, {0x5837, 0x26, 0, 0}, {0x5838, 0x44, 0, 0},
+ {0x5839, 0x24, 0, 0}, {0x583a, 0x26, 0, 0}, {0x583b, 0x28, 0, 0},
+ {0x583c, 0x42, 0, 0}, {0x583d, 0xce, 0, 0}, {0x5025, 0x00, 0, 0},
+ {0x3a0f, 0x30, 0, 0}, {0x3a10, 0x28, 0, 0}, {0x3a1b, 0x30, 0, 0},
+ {0x3a1e, 0x26, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x14, 0, 0},
+ {0x3008, 0x02, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x11, 0, 0},
+ {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0},
+};
+
+static struct reg_value ov5640_setting_30fps_VGA_640_480[] = {
+ {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0},
+ {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0},
+ {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0},
+ {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0},
+ {0x3807, 0x9b, 0, 0}, {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0},
+ {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0},
+ {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0},
+ {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0},
+ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0},
+ {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0},
+ {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0},
+ {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0},
+ {0x460c, 0x22, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x02, 0, 0},
+ {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x11, 0, 0},
+ {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0}, {0x3503, 0x00, 0, 0},
+};
+
+static struct reg_value ov5640_setting_15fps_VGA_640_480[] = {
+ {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0},
+ {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0},
+ {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0},
+ {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0},
+ {0x3807, 0x9b, 0, 0}, {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0},
+ {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0},
+ {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0},
+ {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0},
+ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0},
+ {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0},
+ {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0},
+ {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0},
+ {0x460c, 0x22, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x02, 0, 0},
+ {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x21, 0, 0},
+ {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0}, {0x3503, 0x00, 0, 0},
+};
+
+static struct reg_value ov5640_setting_30fps_QVGA_320_240[] = {
+ {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0},
+ {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0},
+ {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0},
+ {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0},
+ {0x3807, 0x9b, 0, 0}, {0x3808, 0x01, 0, 0}, {0x3809, 0x40, 0, 0},
+ {0x380a, 0x00, 0, 0}, {0x380b, 0xf0, 0, 0}, {0x380c, 0x07, 0, 0},
+ {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0},
+ {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0},
+ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0},
+ {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0},
+ {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0},
+ {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0},
+ {0x460c, 0x22, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x02, 0, 0},
+ {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x11, 0, 0},
+ {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0},
+};
+
+static struct reg_value ov5640_setting_15fps_QVGA_320_240[] = {
+ {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0},
+ {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0},
+ {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0},
+ {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0},
+ {0x3807, 0x9b, 0, 0}, {0x3808, 0x01, 0, 0}, {0x3809, 0x40, 0, 0},
+ {0x380a, 0x00, 0, 0}, {0x380b, 0xf0, 0, 0}, {0x380c, 0x07, 0, 0},
+ {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0},
+ {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0},
+ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0},
+ {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0},
+ {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0},
+ {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0},
+ {0x460c, 0x22, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x02, 0, 0},
+ {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x21, 0, 0},
+ {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0},
+};
+
+static struct reg_value ov5640_setting_30fps_NTSC_720_480[] = {
+ {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0},
+ {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0},
+ {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0},
+ {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0},
+ {0x3807, 0xd4, 0, 0}, {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0},
+ {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0},
+ {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0},
+ {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0},
+ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0},
+ {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0},
+ {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0},
+ {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0},
+ {0x460c, 0x22, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x02, 0, 0},
+ {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x11, 0, 0},
+ {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0},
+};
+
+static struct reg_value ov5640_setting_15fps_NTSC_720_480[] = {
+ {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0},
+ {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0},
+ {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0},
+ {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0},
+ {0x3807, 0xd4, 0, 0}, {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0},
+ {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0},
+ {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0},
+ {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0},
+ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0},
+ {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0},
+ {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0},
+ {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0},
+ {0x460c, 0x22, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x02, 0, 0},
+ {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x21, 0, 0},
+ {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0},
+};
+
+static struct reg_value ov5640_setting_30fps_PAL_720_576[] = {
+ {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0},
+ {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0},
+ {0x3801, 0x60, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0},
+ {0x3804, 0x09, 0, 0}, {0x3805, 0x7e, 0, 0}, {0x3806, 0x07, 0, 0},
+ {0x3807, 0x9b, 0, 0}, {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0},
+ {0x380a, 0x02, 0, 0}, {0x380b, 0x40, 0, 0}, {0x380c, 0x07, 0, 0},
+ {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0},
+ {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0},
+ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0},
+ {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0},
+ {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0},
+ {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0},
+ {0x460c, 0x22, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x02, 0, 0},
+ {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x11, 0, 0},
+ {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0},
+};
+
+static struct reg_value ov5640_setting_15fps_PAL_720_576[] = {
+ {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0},
+ {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0},
+ {0x3801, 0x60, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0},
+ {0x3804, 0x09, 0, 0}, {0x3805, 0x7e, 0, 0}, {0x3806, 0x07, 0, 0},
+ {0x3807, 0x9b, 0, 0}, {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0},
+ {0x380a, 0x02, 0, 0}, {0x380b, 0x40, 0, 0}, {0x380c, 0x07, 0, 0},
+ {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0},
+ {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0},
+ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0},
+ {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0},
+ {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0},
+ {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0},
+ {0x460c, 0x22, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x02, 0, 0},
+ {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x21, 0, 0},
+ {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0},
+};
+
+static struct reg_value ov5640_setting_30fps_720P_1280_720[] = {
+ {0x3035, 0x21, 0, 0}, {0x3036, 0x69, 0, 0}, {0x3c07, 0x07, 0, 0},
+ {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+ {0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0},
+ {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0},
+ {0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x02, 0, 0},
+ {0x380b, 0xd0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x64, 0, 0},
+ {0x380e, 0x02, 0, 0}, {0x380f, 0xe4, 0, 0}, {0x3813, 0x04, 0, 0},
+ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3709, 0x52, 0, 0},
+ {0x370c, 0x03, 0, 0}, {0x3a02, 0x02, 0, 0}, {0x3a03, 0xe0, 0, 0},
+ {0x3a14, 0x02, 0, 0}, {0x3a15, 0xe0, 0, 0}, {0x4004, 0x02, 0, 0},
+ {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0}, {0x4713, 0x03, 0, 0},
+ {0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0},
+ {0x4837, 0x16, 0, 0}, {0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0},
+ {0x3503, 0x00, 0, 0},
+};
+
+static struct reg_value ov5640_setting_15fps_720P_1280_720[] = {
+ {0x3035, 0x41, 0, 0}, {0x3036, 0x69, 0, 0}, {0x3c07, 0x07, 0, 0},
+ {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+ {0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0},
+ {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0},
+ {0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x02, 0, 0},
+ {0x380b, 0xd0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x64, 0, 0},
+ {0x380e, 0x02, 0, 0}, {0x380f, 0xe4, 0, 0}, {0x3813, 0x04, 0, 0},
+ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3709, 0x52, 0, 0},
+ {0x370c, 0x03, 0, 0}, {0x3a02, 0x02, 0, 0}, {0x3a03, 0xe0, 0, 0},
+ {0x3a14, 0x02, 0, 0}, {0x3a15, 0xe0, 0, 0}, {0x4004, 0x02, 0, 0},
+ {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0}, {0x4713, 0x03, 0, 0},
+ {0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0},
+ {0x4837, 0x16, 0, 0}, {0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0},
+ {0x3503, 0x00, 0, 0},
+};
+
+static struct reg_value ov5640_setting_30fps_QCIF_176_144[] = {
+ {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0},
+ {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0},
+ {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0},
+ {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0},
+ {0x3807, 0x9b, 0, 0}, {0x3808, 0x00, 0, 0}, {0x3809, 0xb0, 0, 0},
+ {0x380a, 0x00, 0, 0}, {0x380b, 0x90, 0, 0}, {0x380c, 0x07, 0, 0},
+ {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0},
+ {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0},
+ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0},
+ {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0},
+ {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0},
+ {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0},
+ {0x460c, 0x22, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x02, 0, 0},
+ {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x11, 0, 0},
+ {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0},
+};
+
+static struct reg_value ov5640_setting_15fps_QCIF_176_144[] = {
+ {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0},
+ {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0},
+ {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0},
+ {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0},
+ {0x3807, 0x9b, 0, 0}, {0x3808, 0x00, 0, 0}, {0x3809, 0xb0, 0, 0},
+ {0x380a, 0x00, 0, 0}, {0x380b, 0x90, 0, 0}, {0x380c, 0x07, 0, 0},
+ {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0},
+ {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0},
+ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0},
+ {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0},
+ {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0},
+ {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0},
+ {0x460c, 0x22, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x02, 0, 0},
+ {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x21, 0, 0},
+ {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0},
+};
+
+static struct reg_value ov5640_setting_30fps_XGA_1024_768[] = {
+ {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0},
+ {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0},
+ {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0},
+ {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0},
+ {0x3807, 0x9b, 0, 0}, {0x3808, 0x04, 0, 0}, {0x3809, 0x00, 0, 0},
+ {0x380a, 0x03, 0, 0}, {0x380b, 0x00, 0, 0}, {0x380c, 0x07, 0, 0},
+ {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0},
+ {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0},
+ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0},
+ {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0},
+ {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0},
+ {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0},
+ {0x460c, 0x20, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x01, 0, 0},
+ {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x21, 0, 0},
+ {0x3036, 0x69, 0, 0}, {0x3037, 0x13, 0, 0},
+};
+
+static struct reg_value ov5640_setting_15fps_XGA_1024_768[] = {
+ {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0},
+ {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0},
+ {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0},
+ {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0},
+ {0x3807, 0x9b, 0, 0}, {0x3808, 0x04, 0, 0}, {0x3809, 0x00, 0, 0},
+ {0x380a, 0x03, 0, 0}, {0x380b, 0x00, 0, 0}, {0x380c, 0x07, 0, 0},
+ {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0},
+ {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0},
+ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0},
+ {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0},
+ {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0},
+ {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0},
+ {0x460c, 0x20, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x01, 0, 0},
+ {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x21, 0, 0},
+ {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0},
+};
+
+
+static struct reg_value ov5640_setting_15fps_1080P_1920_1080[] = {
+ {0x3c07, 0x07, 0, 0}, {0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0},
+ {0x3814, 0x11, 0, 0}, {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0},
+ {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0xee, 0, 0},
+ {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x05, 0, 0},
+ {0x3807, 0xc3, 0, 0}, {0x3808, 0x07, 0, 0}, {0x3809, 0x80, 0, 0},
+ {0x380a, 0x04, 0, 0}, {0x380b, 0x38, 0, 0}, {0x380c, 0x0b, 0, 0},
+ {0x380d, 0x1c, 0, 0}, {0x380e, 0x07, 0, 0}, {0x380f, 0xb0, 0, 0},
+ {0x3813, 0x04, 0, 0}, {0x3618, 0x04, 0, 0}, {0x3612, 0x2b, 0, 0},
+ {0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x07, 0, 0},
+ {0x3a03, 0xae, 0, 0}, {0x3a14, 0x07, 0, 0}, {0x3a15, 0xae, 0, 0},
+ {0x4004, 0x06, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0},
+ {0x4713, 0x02, 0, 0}, {0x4407, 0x0c, 0, 0}, {0x460b, 0x37, 0, 0},
+ {0x460c, 0x20, 0, 0}, {0x4837, 0x2c, 0, 0}, {0x3824, 0x01, 0, 0},
+ {0x5001, 0x83, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x21, 0, 0},
+ {0x3036, 0x69, 0, 0}, {0x3037, 0x13, 0, 0},
+};
+
+static struct reg_value ov5640_setting_15fps_QSXGA_2592_1944[] = {
+ {0x3c07, 0x07, 0, 0}, {0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0},
+ {0x3814, 0x11, 0, 0}, {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0},
+ {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0},
+ {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0},
+ {0x3807, 0x9f, 0, 0}, {0x3808, 0x0a, 0, 0}, {0x3809, 0x20, 0, 0},
+ {0x380a, 0x07, 0, 0}, {0x380b, 0x98, 0, 0}, {0x380c, 0x0b, 0, 0},
+ {0x380d, 0x1c, 0, 0}, {0x380e, 0x07, 0, 0}, {0x380f, 0xb0, 0, 0},
+ {0x3813, 0x04, 0, 0}, {0x3618, 0x04, 0, 0}, {0x3612, 0x2b, 0, 0},
+ {0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x07, 0, 0},
+ {0x3a03, 0xae, 0, 0}, {0x3a14, 0x07, 0, 0}, {0x3a15, 0xae, 0, 0},
+ {0x4004, 0x06, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0},
+ {0x4713, 0x02, 0, 0}, {0x4407, 0x0c, 0, 0}, {0x460b, 0x37, 0, 0},
+ {0x460c, 0x20, 0, 0}, {0x4837, 0x2c, 0, 0}, {0x3824, 0x01, 0, 0},
+ {0x5001, 0x83, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x21, 0, 0},
+ {0x3036, 0x69, 0, 0}, {0x3037, 0x13, 0, 0},
+};
+
+static struct ov5640_mode_info ov5640_mode_info_data[2][ov5640_mode_MAX + 1] = {
+ {
+ {ov5640_mode_VGA_640_480, 640, 480,
+ ov5640_setting_15fps_VGA_640_480,
+ ARRAY_SIZE(ov5640_setting_15fps_VGA_640_480)},
+ {ov5640_mode_QVGA_320_240, 320, 240,
+ ov5640_setting_15fps_QVGA_320_240,
+ ARRAY_SIZE(ov5640_setting_15fps_QVGA_320_240)},
+ {ov5640_mode_NTSC_720_480, 720, 480,
+ ov5640_setting_15fps_NTSC_720_480,
+ ARRAY_SIZE(ov5640_setting_15fps_NTSC_720_480)},
+ {ov5640_mode_PAL_720_576, 720, 576,
+ ov5640_setting_15fps_PAL_720_576,
+ ARRAY_SIZE(ov5640_setting_15fps_PAL_720_576)},
+ {ov5640_mode_720P_1280_720, 1280, 720,
+ ov5640_setting_15fps_720P_1280_720,
+ ARRAY_SIZE(ov5640_setting_15fps_720P_1280_720)},
+ {ov5640_mode_1080P_1920_1080, 1920, 1080,
+ ov5640_setting_15fps_1080P_1920_1080,
+ ARRAY_SIZE(ov5640_setting_15fps_1080P_1920_1080)},
+ {ov5640_mode_QSXGA_2592_1944, 2592, 1944,
+ ov5640_setting_15fps_QSXGA_2592_1944,
+ ARRAY_SIZE(ov5640_setting_15fps_QSXGA_2592_1944)},
+ {ov5640_mode_QCIF_176_144, 176, 144,
+ ov5640_setting_15fps_QCIF_176_144,
+ ARRAY_SIZE(ov5640_setting_15fps_QCIF_176_144)},
+ {ov5640_mode_XGA_1024_768, 1024, 768,
+ ov5640_setting_15fps_XGA_1024_768,
+ ARRAY_SIZE(ov5640_setting_15fps_XGA_1024_768)},
+ },
+ {
+ {ov5640_mode_VGA_640_480, 640, 480,
+ ov5640_setting_30fps_VGA_640_480,
+ ARRAY_SIZE(ov5640_setting_30fps_VGA_640_480)},
+ {ov5640_mode_QVGA_320_240, 320, 240,
+ ov5640_setting_30fps_QVGA_320_240,
+ ARRAY_SIZE(ov5640_setting_30fps_QVGA_320_240)},
+ {ov5640_mode_NTSC_720_480, 720, 480,
+ ov5640_setting_30fps_NTSC_720_480,
+ ARRAY_SIZE(ov5640_setting_30fps_NTSC_720_480)},
+ {ov5640_mode_PAL_720_576, 720, 576,
+ ov5640_setting_30fps_PAL_720_576,
+ ARRAY_SIZE(ov5640_setting_30fps_PAL_720_576)},
+ {ov5640_mode_720P_1280_720, 1280, 720,
+ ov5640_setting_30fps_720P_1280_720,
+ ARRAY_SIZE(ov5640_setting_30fps_720P_1280_720)},
+ {ov5640_mode_1080P_1920_1080, 0, 0, NULL, 0},
+ {ov5640_mode_QSXGA_2592_1944, 0, 0, NULL, 0},
+ {ov5640_mode_QCIF_176_144, 176, 144,
+ ov5640_setting_30fps_QCIF_176_144,
+ ARRAY_SIZE(ov5640_setting_30fps_QCIF_176_144)},
+ {ov5640_mode_XGA_1024_768, 1024, 768,
+ ov5640_setting_30fps_XGA_1024_768,
+ ARRAY_SIZE(ov5640_setting_30fps_XGA_1024_768)},
+ },
+};
+
+static struct regulator *io_regulator;
+static struct regulator *core_regulator;
+static struct regulator *analog_regulator;
+
+static int ov5640_probe(struct i2c_client *adapter,
+ const struct i2c_device_id *device_id);
+static int ov5640_remove(struct i2c_client *client);
+
+static s32 ov5640_read_reg(u16 reg, u8 *val);
+static s32 ov5640_write_reg(u16 reg, u8 val);
+
+static const struct i2c_device_id ov5640_id[] = {
+ {"ov5640", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, ov5640_id);
+
+static struct i2c_driver ov5640_i2c_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "ov5640",
+ },
+ .probe = ov5640_probe,
+ .remove = ov5640_remove,
+ .id_table = ov5640_id,
+};
+
+static const struct ov5640_datafmt ov5640_colour_fmts[] = {
+ {MEDIA_BUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG},
+};
+
+static struct ov5640 *to_ov5640(const struct i2c_client *client)
+{
+ return container_of(i2c_get_clientdata(client), struct ov5640, subdev);
+}
+
+/* Find a data format by a pixel code in an array */
+static const struct ov5640_datafmt
+ *ov5640_find_datafmt(u32 code)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ov5640_colour_fmts); i++)
+ if (ov5640_colour_fmts[i].code == code)
+ return ov5640_colour_fmts + i;
+
+ return NULL;
+}
+
+static inline void ov5640_power_down(int enable)
+{
+ gpio_set_value_cansleep(pwn_gpio, enable);
+
+ msleep(2);
+}
+
+static inline void ov5640_reset(void)
+{
+ /* camera reset */
+ gpio_set_value_cansleep(rst_gpio, 1);
+
+ /* camera power down */
+ gpio_set_value_cansleep(pwn_gpio, 1);
+ msleep(5);
+ gpio_set_value_cansleep(pwn_gpio, 0);
+ msleep(5);
+ gpio_set_value_cansleep(rst_gpio, 0);
+ msleep(1);
+ gpio_set_value_cansleep(rst_gpio, 1);
+ msleep(5);
+ gpio_set_value_cansleep(pwn_gpio, 1);
+}
+
+static int ov5640_regulator_enable(struct device *dev)
+{
+ int ret = 0;
+
+ io_regulator = devm_regulator_get(dev, "DOVDD");
+ if (!IS_ERR(io_regulator)) {
+ regulator_set_voltage(io_regulator,
+ OV5640_VOLTAGE_DIGITAL_IO,
+ OV5640_VOLTAGE_DIGITAL_IO);
+ ret = regulator_enable(io_regulator);
+ if (ret) {
+ dev_err(dev, "set io voltage failed\n");
+ return ret;
+ } else {
+ dev_dbg(dev, "set io voltage ok\n");
+ }
+ } else {
+ io_regulator = NULL;
+ dev_warn(dev, "cannot get io voltage\n");
+ }
+
+ core_regulator = devm_regulator_get(dev, "DVDD");
+ if (!IS_ERR(core_regulator)) {
+ regulator_set_voltage(core_regulator,
+ OV5640_VOLTAGE_DIGITAL_CORE,
+ OV5640_VOLTAGE_DIGITAL_CORE);
+ ret = regulator_enable(core_regulator);
+ if (ret) {
+ dev_err(dev, "set core voltage failed\n");
+ return ret;
+ } else {
+ dev_dbg(dev, "set core voltage ok\n");
+ }
+ } else {
+ core_regulator = NULL;
+ dev_warn(dev, "cannot get core voltage\n");
+ }
+
+ analog_regulator = devm_regulator_get(dev, "AVDD");
+ if (!IS_ERR(analog_regulator)) {
+ regulator_set_voltage(analog_regulator,
+ OV5640_VOLTAGE_ANALOG,
+ OV5640_VOLTAGE_ANALOG);
+ ret = regulator_enable(analog_regulator);
+ if (ret) {
+ dev_err(dev, "set analog voltage failed\n");
+ return ret;
+ } else {
+ dev_dbg(dev, "set analog voltage ok\n");
+ }
+ } else {
+ analog_regulator = NULL;
+ dev_warn(dev, "cannot get analog voltage\n");
+ }
+
+ return ret;
+}
+
+static s32 ov5640_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(ov5640_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 ov5640_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(ov5640_data.i2c_client, au8RegBuf, 2)) {
+ pr_err("%s:write reg error:reg=%x\n",
+ __func__, reg);
+ return -1;
+ }
+
+ if (1 != i2c_master_recv(ov5640_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;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int ov5640_get_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+ u8 val;
+
+ if (reg->reg & ~0xffff)
+ return -EINVAL;
+
+ reg->size = 1;
+
+ ret = ov5640_read_reg(reg->reg, &val);
+ if (!ret)
+ reg->val = (__u64)val;
+
+ return ret;
+}
+
+static int ov5640_set_register(struct v4l2_subdev *sd,
+ const struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (reg->reg & ~0xffff || reg->val & ~0xff)
+ return -EINVAL;
+
+ return ov5640_write_reg(reg->reg, reg->val);
+}
+#endif
+
+static void ov5640_soft_reset(void)
+{
+ /* sysclk from pad */
+ ov5640_write_reg(0x3103, 0x11);
+
+ /* software reset */
+ ov5640_write_reg(0x3008, 0x82);
+
+ /* delay at least 5ms */
+ msleep(10);
+}
+
+/* set sensor driver capability
+ * 0x302c[7:6] - strength
+ 00 - 1x
+ 01 - 2x
+ 10 - 3x
+ 11 - 4x
+ */
+static int ov5640_driver_capability(int strength)
+{
+ u8 temp = 0;
+
+ if (strength > 4 || strength < 1) {
+ pr_err("The valid driver capability of ov5640 is 1x~4x\n");
+ return -EINVAL;
+ }
+
+ ov5640_read_reg(0x302c, &temp);
+
+ temp &= ~0xc0; /* clear [7:6] */
+ temp |= ((strength - 1) << 6); /* set [7:6] */
+
+ ov5640_write_reg(0x302c, temp);
+
+ return 0;
+}
+
+/* calculate sysclk */
+static int ov5640_get_sysclk(void)
+{
+ int xvclk = ov5640_data.mclk / 10000;
+ int sysclk;
+ int temp1, temp2;
+ int Multiplier, PreDiv, VCO, SysDiv, Pll_rdiv, Bit_div2x, sclk_rdiv;
+ int sclk_rdiv_map[] = {1, 2, 4, 8};
+ u8 regval = 0;
+
+ temp1 = ov5640_read_reg(0x3034, ®val);
+ temp2 = temp1 & 0x0f;
+ if (temp2 == 8 || temp2 == 10) {
+ Bit_div2x = temp2 / 2;
+ } else {
+ pr_err("ov5640: unsupported bit mode %d\n", temp2);
+ return -1;
+ }
+
+ temp1 = ov5640_read_reg(0x3035, ®val);
+ SysDiv = temp1 >> 4;
+ if (SysDiv == 0)
+ SysDiv = 16;
+
+ temp1 = ov5640_read_reg(0x3036, ®val);
+ Multiplier = temp1;
+ temp1 = ov5640_read_reg(0x3037, ®val);
+ PreDiv = temp1 & 0x0f;
+ Pll_rdiv = ((temp1 >> 4) & 0x01) + 1;
+
+ temp1 = ov5640_read_reg(0x3108, ®val);
+ temp2 = temp1 & 0x03;
+
+ sclk_rdiv = sclk_rdiv_map[temp2];
+ VCO = xvclk * Multiplier / PreDiv;
+ sysclk = VCO / SysDiv / Pll_rdiv * 2 / Bit_div2x / sclk_rdiv;
+
+ return sysclk;
+}
+
+/* read HTS from register settings */
+static int ov5640_get_HTS(void)
+{
+ int HTS;
+ u8 temp = 0;
+
+ HTS = ov5640_read_reg(0x380c, &temp);
+ HTS = (HTS<<8) + ov5640_read_reg(0x380d, &temp);
+ return HTS;
+}
+
+/* read VTS from register settings */
+static int ov5640_get_VTS(void)
+{
+ int VTS;
+ u8 temp = 0;
+
+ VTS = ov5640_read_reg(0x380e, &temp);
+ VTS = (VTS<<8) + ov5640_read_reg(0x380f, &temp);
+
+ return VTS;
+}
+
+/* write VTS to registers */
+static int ov5640_set_VTS(int VTS)
+{
+ int temp;
+
+ temp = VTS & 0xff;
+ ov5640_write_reg(0x380f, temp);
+
+ temp = VTS>>8;
+ ov5640_write_reg(0x380e, temp);
+ return 0;
+}
+
+/* read shutter, in number of line period */
+static int ov5640_get_shutter(void)
+{
+ int shutter;
+ u8 regval;
+
+ shutter = (ov5640_read_reg(0x03500, ®val) & 0x0f);
+
+ shutter = (shutter<<8) + ov5640_read_reg(0x3501, ®val);
+ shutter = (shutter<<4) + (ov5640_read_reg(0x3502, ®val)>>4);
+
+ return shutter;
+}
+
+/* write shutter, in number of line period */
+static int ov5640_set_shutter(int shutter)
+{
+ int temp;
+
+ shutter = shutter & 0xffff;
+ temp = shutter & 0x0f;
+ temp = temp<<4;
+ ov5640_write_reg(0x3502, temp);
+
+ temp = shutter & 0xfff;
+ temp = temp>>4;
+ ov5640_write_reg(0x3501, temp);
+
+ temp = shutter>>12;
+ ov5640_write_reg(0x3500, temp);
+
+ return 0;
+}
+
+/* read gain, 16 = 1x */
+static int ov5640_get_gain16(void)
+{
+ int gain16;
+ u8 regval;
+
+ gain16 = ov5640_read_reg(0x350a, ®val) & 0x03;
+ gain16 = (gain16<<8) + ov5640_read_reg(0x350b, ®val);
+
+ return gain16;
+}
+
+/* write gain, 16 = 1x */
+static int ov5640_set_gain16(int gain16)
+{
+ int temp;
+
+ gain16 = gain16 & 0x3ff;
+ temp = gain16 & 0xff;
+
+ ov5640_write_reg(0x350b, temp);
+ temp = gain16>>8;
+
+ ov5640_write_reg(0x350a, temp);
+ return 0;
+}
+
+/* get banding filter value */
+static int ov5640_get_light_freq(void)
+{
+ int temp, temp1, light_frequency;
+ u8 regval;
+
+ temp = ov5640_read_reg(0x3c01, ®val);
+ if (temp & 0x80) {
+ /* manual */
+ temp1 = ov5640_read_reg(0x3c00, ®val);
+ if (temp1 & 0x04) {
+ /* 50Hz */
+ light_frequency = 50;
+ } else {
+ /* 60Hz */
+ light_frequency = 60;
+ }
+ } else {
+ /* auto */
+ temp1 = ov5640_read_reg(0x3c0c, ®val);
+ if (temp1 & 0x01) {
+ /* 50Hz */
+ light_frequency = 50;
+ } else {
+ /* 60Hz */
+ light_frequency = 60;
+ }
+ }
+
+ return light_frequency;
+}
+
+static void ov5640_set_bandingfilter(void)
+{
+ int prev_VTS;
+ int band_step60, max_band60, band_step50, max_band50;
+
+ /* read preview PCLK */
+ prev_sysclk = ov5640_get_sysclk();
+
+ /* read preview HTS */
+ prev_HTS = ov5640_get_HTS();
+
+ /* read preview VTS */
+ prev_VTS = ov5640_get_VTS();
+
+ /* calculate banding filter */
+ /* 60Hz */
+ band_step60 = prev_sysclk * 100/prev_HTS * 100/120;
+ ov5640_write_reg(0x3a0a, (band_step60 >> 8));
+ ov5640_write_reg(0x3a0b, (band_step60 & 0xff));
+
+ max_band60 = (int)((prev_VTS-4)/band_step60);
+ ov5640_write_reg(0x3a0d, max_band60);
+
+ /* 50Hz */
+ band_step50 = prev_sysclk * 100/prev_HTS;
+ ov5640_write_reg(0x3a08, (band_step50 >> 8));
+ ov5640_write_reg(0x3a09, (band_step50 & 0xff));
+
+ max_band50 = (int)((prev_VTS-4)/band_step50);
+ ov5640_write_reg(0x3a0e, max_band50);
+}
+
+/* stable in high */
+static int ov5640_set_AE_target(int target)
+{
+ int fast_high, fast_low;
+
+ AE_low = target * 23 / 25; /* 0.92 */
+ AE_high = target * 27 / 25; /* 1.08 */
+ fast_high = AE_high << 1;
+
+ if (fast_high > 255)
+ fast_high = 255;
+ fast_low = AE_low >> 1;
+
+ ov5640_write_reg(0x3a0f, AE_high);
+ ov5640_write_reg(0x3a10, AE_low);
+ ov5640_write_reg(0x3a1b, AE_high);
+ ov5640_write_reg(0x3a1e, AE_low);
+ ov5640_write_reg(0x3a11, fast_high);
+ ov5640_write_reg(0x3a1f, fast_low);
+
+ return 0;
+}
+
+/* enable = 0 to turn off night mode
+ enable = 1 to turn on night mode */
+static int ov5640_set_night_mode(int enable)
+{
+ u8 mode;
+
+ ov5640_read_reg(0x3a00, &mode);
+
+ if (enable) {
+ /* night mode on */
+ mode |= 0x04;
+ ov5640_write_reg(0x3a00, mode);
+ } else {
+ /* night mode off */
+ mode &= 0xfb;
+ ov5640_write_reg(0x3a00, mode);
+ }
+
+ return 0;
+}
+
+/* enable = 0 to turn off AEC/AGC
+ enable = 1 to turn on AEC/AGC */
+static void ov5640_turn_on_AE_AG(int enable)
+{
+ u8 ae_ag_ctrl;
+
+ ov5640_read_reg(0x3503, &ae_ag_ctrl);
+ if (enable) {
+ /* turn on auto AE/AG */
+ ae_ag_ctrl = ae_ag_ctrl & ~(0x03);
+ } else {
+ /* turn off AE/AG */
+ ae_ag_ctrl = ae_ag_ctrl | 0x03;
+ }
+ ov5640_write_reg(0x3503, ae_ag_ctrl);
+}
+
+/* download ov5640 settings to sensor through i2c */
+static int ov5640_download_firmware(struct reg_value *pModeSetting, s32 ArySize)
+{
+ register u32 Delay_ms = 0;
+ register u16 RegAddr = 0;
+ register u8 Mask = 0;
+ register u8 Val = 0;
+ u8 RegVal = 0;
+ int i, retval = 0;
+
+ for (i = 0; i < ArySize; ++i, ++pModeSetting) {
+ Delay_ms = pModeSetting->u32Delay_ms;
+ RegAddr = pModeSetting->u16RegAddr;
+ Val = pModeSetting->u8Val;
+ Mask = pModeSetting->u8Mask;
+
+ if (Mask) {
+ retval = ov5640_read_reg(RegAddr, &RegVal);
+ if (retval < 0)
+ goto err;
+
+ RegVal &= ~(u8)Mask;
+ Val &= Mask;
+ Val |= RegVal;
+ }
+
+ retval = ov5640_write_reg(RegAddr, Val);
+ if (retval < 0)
+ goto err;
+
+ if (Delay_ms)
+ msleep(Delay_ms);
+ }
+err:
+ return retval;
+}
+
+static int ov5640_init_mode(void)
+{
+ struct reg_value *pModeSetting = NULL;
+ int ArySize = 0, retval = 0;
+
+ ov5640_soft_reset();
+
+ pModeSetting = ov5640_global_init_setting;
+ ArySize = ARRAY_SIZE(ov5640_global_init_setting);
+ retval = ov5640_download_firmware(pModeSetting, ArySize);
+ if (retval < 0)
+ goto err;
+
+ pModeSetting = ov5640_init_setting_30fps_VGA;
+ ArySize = ARRAY_SIZE(ov5640_init_setting_30fps_VGA);
+ retval = ov5640_download_firmware(pModeSetting, ArySize);
+ if (retval < 0)
+ goto err;
+
+ /* change driver capability to 2x according to validation board.
+ * if the image is not stable, please increase the driver strength.
+ */
+ ov5640_driver_capability(2);
+ ov5640_set_bandingfilter();
+ ov5640_set_AE_target(AE_Target);
+ ov5640_set_night_mode(night_mode);
+
+ /* skip 9 vysnc: start capture at 10th vsync */
+ msleep(300);
+
+ /* turn off night mode */
+ night_mode = 0;
+ ov5640_data.pix.width = 640;
+ ov5640_data.pix.height = 480;
+err:
+ return retval;
+}
+
+/* change to or back to subsampling mode set the mode directly
+ * image size below 1280 * 960 is subsampling mode */
+static int ov5640_change_mode_direct(enum ov5640_frame_rate frame_rate,
+ enum ov5640_mode mode)
+{
+ struct reg_value *pModeSetting = NULL;
+ s32 ArySize = 0;
+ int retval = 0;
+
+ if (mode > ov5640_mode_MAX || mode < ov5640_mode_MIN) {
+ pr_err("Wrong ov5640 mode detected!\n");
+ return -1;
+ }
+
+ pModeSetting = ov5640_mode_info_data[frame_rate][mode].init_data_ptr;
+ ArySize =
+ ov5640_mode_info_data[frame_rate][mode].init_data_size;
+
+ ov5640_data.pix.width = ov5640_mode_info_data[frame_rate][mode].width;
+ ov5640_data.pix.height = ov5640_mode_info_data[frame_rate][mode].height;
+
+ if (ov5640_data.pix.width == 0 || ov5640_data.pix.height == 0 ||
+ pModeSetting == NULL || ArySize == 0)
+ return -EINVAL;
+
+ /* set ov5640 to subsampling mode */
+ retval = ov5640_download_firmware(pModeSetting, ArySize);
+
+ /* turn on AE AG for subsampling mode, in case the firmware didn't */
+ ov5640_turn_on_AE_AG(1);
+
+ /* calculate banding filter */
+ ov5640_set_bandingfilter();
+
+ /* set AE target */
+ ov5640_set_AE_target(AE_Target);
+
+ /* update night mode setting */
+ ov5640_set_night_mode(night_mode);
+
+ /* skip 9 vysnc: start capture at 10th vsync */
+ if (mode == ov5640_mode_XGA_1024_768 && frame_rate == ov5640_30_fps) {
+ pr_warning("ov5640: actual frame rate of XGA is 22.5fps\n");
+ /* 1/22.5 * 9*/
+ msleep(400);
+ return retval;
+ }
+
+ if (frame_rate == ov5640_15_fps) {
+ /* 1/15 * 9*/
+ msleep(600);
+ } else if (frame_rate == ov5640_30_fps) {
+ /* 1/30 * 9*/
+ msleep(300);
+ }
+
+ return retval;
+}
+
+/* change to scaling mode go through exposure calucation
+ * image size above 1280 * 960 is scaling mode */
+static int ov5640_change_mode_exposure_calc(enum ov5640_frame_rate frame_rate,
+ enum ov5640_mode mode)
+{
+ int prev_shutter, prev_gain16, average;
+ int cap_shutter, cap_gain16;
+ int cap_sysclk, cap_HTS, cap_VTS;
+ int light_freq, cap_bandfilt, cap_maxband;
+ long cap_gain16_shutter;
+ u8 temp;
+ struct reg_value *pModeSetting = NULL;
+ s32 ArySize = 0;
+ int retval = 0;
+
+ /* check if the input mode and frame rate is valid */
+ pModeSetting =
+ ov5640_mode_info_data[frame_rate][mode].init_data_ptr;
+ ArySize =
+ ov5640_mode_info_data[frame_rate][mode].init_data_size;
+
+ ov5640_data.pix.width =
+ ov5640_mode_info_data[frame_rate][mode].width;
+ ov5640_data.pix.height =
+ ov5640_mode_info_data[frame_rate][mode].height;
+
+ if (ov5640_data.pix.width == 0 || ov5640_data.pix.height == 0 ||
+ pModeSetting == NULL || ArySize == 0)
+ return -EINVAL;
+
+ /* read preview shutter */
+ prev_shutter = ov5640_get_shutter();
+
+ /* read preview gain */
+ prev_gain16 = ov5640_get_gain16();
+
+ /* get average */
+ average = ov5640_read_reg(0x56a1, &temp);
+
+ /* turn off night mode for capture */
+ ov5640_set_night_mode(0);
+
+ /* turn off overlay */
+ ov5640_write_reg(0x3022, 0x06);
+
+ /* Write capture setting */
+ retval = ov5640_download_firmware(pModeSetting, ArySize);
+ if (retval < 0)
+ goto err;
+
+ /* turn off AE AG when capture image. */
+ ov5640_turn_on_AE_AG(0);
+
+ /* read capture VTS */
+ cap_VTS = ov5640_get_VTS();
+ cap_HTS = ov5640_get_HTS();
+ cap_sysclk = ov5640_get_sysclk();
+
+ /* calculate capture banding filter */
+ light_freq = ov5640_get_light_freq();
+ if (light_freq == 60) {
+ /* 60Hz */
+ cap_bandfilt = cap_sysclk * 100 / cap_HTS * 100 / 120;
+ } else {
+ /* 50Hz */
+ cap_bandfilt = cap_sysclk * 100 / cap_HTS;
+ }
+ cap_maxband = (int)((cap_VTS - 4)/cap_bandfilt);
+ /* calculate capture shutter/gain16 */
+ if (average > AE_low && average < AE_high) {
+ /* in stable range */
+ cap_gain16_shutter =
+ prev_gain16 * prev_shutter * cap_sysclk/prev_sysclk *
+ prev_HTS/cap_HTS * AE_Target / average;
+ } else {
+ cap_gain16_shutter =
+ prev_gain16 * prev_shutter * cap_sysclk/prev_sysclk *
+ prev_HTS/cap_HTS;
+ }
+
+ /* gain to shutter */
+ if (cap_gain16_shutter < (cap_bandfilt * 16)) {
+ /* shutter < 1/100 */
+ cap_shutter = cap_gain16_shutter/16;
+ if (cap_shutter < 1)
+ cap_shutter = 1;
+ cap_gain16 = cap_gain16_shutter/cap_shutter;
+ if (cap_gain16 < 16)
+ cap_gain16 = 16;
+ } else {
+ if (cap_gain16_shutter > (cap_bandfilt*cap_maxband*16)) {
+ /* exposure reach max */
+ cap_shutter = cap_bandfilt*cap_maxband;
+ cap_gain16 = cap_gain16_shutter / cap_shutter;
+ } else {
+ /* 1/100 < cap_shutter =< max, cap_shutter = n/100 */
+ cap_shutter =
+ ((int)(cap_gain16_shutter/16/cap_bandfilt))
+ * cap_bandfilt;
+ cap_gain16 = cap_gain16_shutter / cap_shutter;
+ }
+ }
+
+ /* write capture gain */
+ ov5640_set_gain16(cap_gain16);
+
+ /* write capture shutter */
+ if (cap_shutter > (cap_VTS - 4)) {
+ cap_VTS = cap_shutter + 4;
+ ov5640_set_VTS(cap_VTS);
+ }
+
+ ov5640_set_shutter(cap_shutter);
+
+ /* skip 2 vysnc: start capture at 3rd vsync
+ * frame rate of QSXGA and 1080P is 7.5fps: 1/7.5 * 2
+ */
+ pr_warning("ov5640: the actual frame rate of %s is 7.5fps\n",
+ mode == ov5640_mode_1080P_1920_1080 ? "1080P" : "QSXGA");
+ msleep(267);
+err:
+ return retval;
+}
+
+static int ov5640_change_mode(enum ov5640_frame_rate frame_rate,
+ enum ov5640_mode mode)
+{
+ int retval = 0;
+
+ if (mode > ov5640_mode_MAX || mode < ov5640_mode_MIN) {
+ pr_err("Wrong ov5640 mode detected!\n");
+ return -1;
+ }
+
+ if (mode == ov5640_mode_1080P_1920_1080 ||
+ mode == ov5640_mode_QSXGA_2592_1944) {
+ /* change to scaling mode go through exposure calucation
+ * image size above 1280 * 960 is scaling mode */
+ retval = ov5640_change_mode_exposure_calc(frame_rate, mode);
+ } else {
+ /* change back to subsampling modem download firmware directly
+ * image size below 1280 * 960 is subsampling mode */
+ retval = ov5640_change_mode_direct(frame_rate, mode);
+ }
+
+ return retval;
+}
+
+/*!
+ * ov5640_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 ov5640_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov5640 *sensor = to_ov5640(client);
+
+ if (on)
+ clk_enable(ov5640_data.sensor_clk);
+ else
+ clk_disable(ov5640_data.sensor_clk);
+
+ sensor->on = on;
+
+ return 0;
+}
+
+/*!
+ * ov5640_g_parm - V4L2 sensor interface handler for VIDIOC_G_PARM ioctl
+ * @s: pointer to standard V4L2 sub device structure
+ * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure
+ *
+ * Returns the sensor's video CAPTURE parameters.
+ */
+static int ov5640_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov5640 *sensor = to_ov5640(client);
+ 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;
+}
+
+/*!
+ * ov5460_s_parm - V4L2 sensor interface handler for VIDIOC_S_PARM ioctl
+ * @s: pointer to standard V4L2 sub 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 ov5640_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov5640 *sensor = to_ov5640(client);
+ struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe;
+ u32 tgt_fps; /* target frames per secound */
+ enum ov5640_frame_rate frame_rate;
+ int ret = 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 = ov5640_15_fps;
+ else if (tgt_fps == 30)
+ frame_rate = ov5640_30_fps;
+ else {
+ pr_err(" The camera frame rate is not supported!\n");
+ goto error;
+ }
+
+ ret = ov5640_change_mode(frame_rate,
+ a->parm.capture.capturemode);
+ if (ret < 0)
+ goto error;
+
+ sensor->streamcap.timeperframe = *timeperframe;
+ sensor->streamcap.capturemode = a->parm.capture.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;
+ }
+
+error:
+ return ret;
+}
+
+static int ov5640_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
+{
+ struct v4l2_mbus_framefmt *mf = &format->format;
+ const struct ov5640_datafmt *fmt = ov5640_find_datafmt(mf->code);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov5640 *sensor = to_ov5640(client);
+
+ if (format->pad)
+ return -EINVAL;
+
+ if (!fmt) {
+ mf->code = ov5640_colour_fmts[0].code;
+ mf->colorspace = ov5640_colour_fmts[0].colorspace;
+ }
+
+ mf->field = V4L2_FIELD_NONE;
+
+ if (format->which == V4L2_SUBDEV_FORMAT_TRY)
+ return 0;
+
+ sensor->fmt = fmt;
+
+ return 0;
+}
+
+static int ov5640_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
+{
+ struct v4l2_mbus_framefmt *mf = &format->format;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov5640 *sensor = to_ov5640(client);
+ const struct ov5640_datafmt *fmt = sensor->fmt;
+
+ if (format->pad)
+ return -EINVAL;
+
+ mf->code = fmt->code;
+ mf->colorspace = fmt->colorspace;
+ mf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int ov5640_enum_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->pad || code->index >= ARRAY_SIZE(ov5640_colour_fmts))
+ return -EINVAL;
+
+ code->code = ov5640_colour_fmts[code->index].code;
+ return 0;
+}
+
+/*!
+ * ov5640_enum_framesizes - V4L2 sensor interface handler for
+ * VIDIOC_ENUM_FRAMESIZES ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @fsize: standard V4L2 VIDIOC_ENUM_FRAMESIZES ioctl structure
+ *
+ * Return 0 if successful, otherwise -EINVAL.
+ */
+static int ov5640_enum_framesizes(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ if (fse->index > ov5640_mode_MAX)
+ return -EINVAL;
+
+ fse->max_width =
+ max(ov5640_mode_info_data[0][fse->index].width,
+ ov5640_mode_info_data[1][fse->index].width);
+ fse->min_width = fse->max_width;
+ fse->max_height =
+ max(ov5640_mode_info_data[0][fse->index].height,
+ ov5640_mode_info_data[1][fse->index].height);
+ fse->min_height = fse->max_height;
+ return 0;
+}
+
+/*!
+ * ov5640_enum_frameintervals - V4L2 sensor interface handler for
+ * VIDIOC_ENUM_FRAMEINTERVALS ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @fival: standard V4L2 VIDIOC_ENUM_FRAMEINTERVALS ioctl structure
+ *
+ * Return 0 if successful, otherwise -EINVAL.
+ */
+static int ov5640_enum_frameintervals(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_interval_enum *fie)
+{
+ int i, j, count;
+
+ if (fie->index < 0 || fie->index > ov5640_mode_MAX)
+ return -EINVAL;
+
+ if (fie->width == 0 || fie->height == 0 ||
+ fie->code == 0) {
+ pr_warning("Please assign pixel format, width and height.\n");
+ return -EINVAL;
+ }
+
+ fie->interval.numerator = 1;
+
+ count = 0;
+ for (i = 0; i < ARRAY_SIZE(ov5640_mode_info_data); i++) {
+ for (j = 0; j < (ov5640_mode_MAX + 1); j++) {
+ if (fie->width == ov5640_mode_info_data[i][j].width
+ && fie->height == ov5640_mode_info_data[i][j].height
+ && ov5640_mode_info_data[i][j].init_data_ptr != NULL) {
+ count++;
+ }
+ if (fie->index == (count - 1)) {
+ fie->interval.denominator =
+ ov5640_framerates[i];
+ return 0;
+ }
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int ov5640_set_clk_rate(void)
+{
+ u32 tgt_xclk; /* target xclk */
+ int ret;
+
+ /* mclk */
+ tgt_xclk = ov5640_data.mclk;
+ tgt_xclk = min(tgt_xclk, (u32)OV5640_XCLK_MAX);
+ tgt_xclk = max(tgt_xclk, (u32)OV5640_XCLK_MIN);
+ ov5640_data.mclk = tgt_xclk;
+
+ pr_debug(" Setting mclk to %d MHz\n", tgt_xclk / 1000000);
+ ret = clk_set_rate(ov5640_data.sensor_clk, ov5640_data.mclk);
+ if (ret < 0)
+ pr_debug("set rate filed, rate=%d\n", ov5640_data.mclk);
+ return ret;
+}
+
+/*!
+ * dev_init - V4L2 sensor init
+ * @s: pointer to standard V4L2 device structure
+ *
+ */
+static int init_device(void)
+{
+ u32 tgt_xclk; /* target xclk */
+ u32 tgt_fps; /* target frames per secound */
+ enum ov5640_frame_rate frame_rate;
+ int ret;
+
+ ov5640_data.on = true;
+
+ /* mclk */
+ tgt_xclk = ov5640_data.mclk;
+
+ /* Default camera frame rate is set in probe */
+ tgt_fps = ov5640_data.streamcap.timeperframe.denominator /
+ ov5640_data.streamcap.timeperframe.numerator;
+
+ if (tgt_fps == 15)
+ frame_rate = ov5640_15_fps;
+ else if (tgt_fps == 30)
+ frame_rate = ov5640_30_fps;
+ else
+ return -EINVAL; /* Only support 15fps or 30fps now. */
+
+ ret = ov5640_init_mode();
+
+ return ret;
+}
+
+static struct v4l2_subdev_video_ops ov5640_subdev_video_ops = {
+ .g_parm = ov5640_g_parm,
+ .s_parm = ov5640_s_parm,
+};
+
+static const struct v4l2_subdev_pad_ops ov5640_subdev_pad_ops = {
+ .enum_frame_size = ov5640_enum_framesizes,
+ .enum_frame_interval = ov5640_enum_frameintervals,
+ .enum_mbus_code = ov5640_enum_code,
+ .set_fmt = ov5640_set_fmt,
+ .get_fmt = ov5640_get_fmt,
+};
+
+static struct v4l2_subdev_core_ops ov5640_subdev_core_ops = {
+ .s_power = ov5640_s_power,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = ov5640_get_register,
+ .s_register = ov5640_set_register,
+#endif
+};
+
+static struct v4l2_subdev_ops ov5640_subdev_ops = {
+ .core = &ov5640_subdev_core_ops,
+ .video = &ov5640_subdev_video_ops,
+ .pad = &ov5640_subdev_pad_ops,
+};
+
+/*!
+ * ov5640 I2C probe function
+ *
+ * @param adapter struct i2c_adapter *
+ * @return Error code indicating success or failure
+ */
+static int ov5640_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct pinctrl *pinctrl;
+ struct device *dev = &client->dev;
+ int retval;
+ u8 chip_id_high, chip_id_low;
+
+ /* ov5640 pinctrl */
+ pinctrl = devm_pinctrl_get_select_default(dev);
+ if (IS_ERR(pinctrl)) {
+ dev_err(dev, "setup pinctrl failed\n");
+ return PTR_ERR(pinctrl);
+ }
+
+ /* request power down pin */
+ pwn_gpio = of_get_named_gpio(dev->of_node, "pwn-gpios", 0);
+ if (!gpio_is_valid(pwn_gpio)) {
+ dev_err(dev, "no sensor pwdn pin available\n");
+ return -ENODEV;
+ }
+ retval = devm_gpio_request_one(dev, pwn_gpio, GPIOF_OUT_INIT_HIGH,
+ "ov5640_pwdn");
+ if (retval < 0)
+ return retval;
+
+ /* request reset pin */
+ rst_gpio = of_get_named_gpio(dev->of_node, "rst-gpios", 0);
+ if (!gpio_is_valid(rst_gpio)) {
+ dev_err(dev, "no sensor reset pin available\n");
+ return -EINVAL;
+ }
+ retval = devm_gpio_request_one(dev, rst_gpio, GPIOF_OUT_INIT_HIGH,
+ "ov5640_reset");
+ if (retval < 0)
+ return retval;
+
+ /* Set initial values for the sensor struct. */
+ memset(&ov5640_data, 0, sizeof(ov5640_data));
+ ov5640_data.sensor_clk = devm_clk_get(dev, "csi_mclk");
+ if (IS_ERR(ov5640_data.sensor_clk)) {
+ dev_err(dev, "get mclk failed\n");
+ return PTR_ERR(ov5640_data.sensor_clk);
+ }
+
+ retval = of_property_read_u32(dev->of_node, "mclk",
+ &ov5640_data.mclk);
+ if (retval) {
+ dev_err(dev, "mclk frequency is invalid\n");
+ return retval;
+ }
+
+ retval = of_property_read_u32(dev->of_node, "mclk_source",
+ (u32 *) &(ov5640_data.mclk_source));
+ if (retval) {
+ dev_err(dev, "mclk_source invalid\n");
+ return retval;
+ }
+
+ retval = of_property_read_u32(dev->of_node, "csi_id",
+ &(ov5640_data.csi));
+ if (retval) {
+ dev_err(dev, "csi_id invalid\n");
+ return retval;
+ }
+
+ /* Set mclk rate before clk on */
+ ov5640_set_clk_rate();
+
+ clk_prepare_enable(ov5640_data.sensor_clk);
+
+ ov5640_data.io_init = ov5640_reset;
+ ov5640_data.i2c_client = client;
+ ov5640_data.pix.pixelformat = V4L2_PIX_FMT_YUYV;
+ ov5640_data.pix.width = 640;
+ ov5640_data.pix.height = 480;
+ ov5640_data.streamcap.capability = V4L2_MODE_HIGHQUALITY |
+ V4L2_CAP_TIMEPERFRAME;
+ ov5640_data.streamcap.capturemode = 0;
+ ov5640_data.streamcap.timeperframe.denominator = DEFAULT_FPS;
+ ov5640_data.streamcap.timeperframe.numerator = 1;
+
+ ov5640_regulator_enable(&client->dev);
+
+ ov5640_reset();
+
+ ov5640_power_down(0);
+
+ retval = ov5640_read_reg(OV5640_CHIP_ID_HIGH_BYTE, &chip_id_high);
+ if (retval < 0 || chip_id_high != 0x56) {
+ clk_disable_unprepare(ov5640_data.sensor_clk);
+ pr_warning("camera ov5640 is not found\n");
+ return -ENODEV;
+ }
+ retval = ov5640_read_reg(OV5640_CHIP_ID_LOW_BYTE, &chip_id_low);
+ if (retval < 0 || chip_id_low != 0x40) {
+ clk_disable_unprepare(ov5640_data.sensor_clk);
+ pr_warning("camera ov5640 is not found\n");
+ return -ENODEV;
+ }
+
+ retval = init_device();
+ if (retval < 0) {
+ clk_disable_unprepare(ov5640_data.sensor_clk);
+ pr_warning("camera ov5640 init failed\n");
+ ov5640_power_down(1);
+ return retval;
+ }
+
+ clk_disable(ov5640_data.sensor_clk);
+
+ v4l2_i2c_subdev_init(&ov5640_data.subdev, client, &ov5640_subdev_ops);
+
+ retval = v4l2_async_register_subdev(&ov5640_data.subdev);
+ if (retval < 0)
+ dev_err(&client->dev,
+ "%s--Async register failed, ret=%d\n", __func__, retval);
+
+ pr_info("camera ov5640, is found\n");
+ return retval;
+}
+
+/*!
+ * ov5640 I2C detach function
+ *
+ * @param client struct i2c_client *
+ * @return Error code indicating success or failure
+ */
+static int ov5640_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_async_unregister_subdev(sd);
+
+ clk_unprepare(ov5640_data.sensor_clk);
+
+ ov5640_power_down(1);
+
+ if (analog_regulator)
+ regulator_disable(analog_regulator);
+
+ if (core_regulator)
+ regulator_disable(core_regulator);
+
+ if (io_regulator)
+ regulator_disable(io_regulator);
+
+ return 0;
+}
+
+module_i2c_driver(ov5640_i2c_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("OV5640 Camera Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("CSI");
--- /dev/null
+/*
+ * Copyright (C) 2011-2015 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/clk.h>
+#include <linux/of_device.h>
+#include <linux/i2c.h>
+#include <linux/of_gpio.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <linux/v4l2-mediabus.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+
+#define OV5647_VOLTAGE_ANALOG 2800000
+#define OV5647_VOLTAGE_DIGITAL_CORE 1500000
+#define OV5647_VOLTAGE_DIGITAL_IO 1800000
+
+#define MIN_FPS 15
+#define MAX_FPS 30
+#define DEFAULT_FPS 30
+
+#define OV5647_XCLK_MIN 6000000
+#define OV5647_XCLK_MAX 24000000
+
+#define OV5647_CHIP_ID_HIGH_BYTE 0x300A
+#define OV5647_CHIP_ID_LOW_BYTE 0x300B
+
+enum ov5647_mode {
+ ov5647_mode_MIN = 0,
+ ov5647_mode_VGA_640_480 = 0,
+ ov5647_mode_720P_1280_720 = 1,
+ ov5647_mode_1080P_1920_1080 = 2,
+ ov5647_mode_QSXGA_2592_1944 = 3,
+ ov5647_mode_MAX = 3,
+ ov5647_mode_INIT = 0xff, /*only for sensor init*/
+};
+
+enum ov5647_frame_rate {
+ ov5647_15_fps,
+ ov5647_30_fps
+};
+
+static int ov5647_framerates[] = {
+ [ov5647_15_fps] = 15,
+ [ov5647_30_fps] = 30,
+};
+
+struct ov5647_datafmt {
+ u32 code;
+ enum v4l2_colorspace colorspace;
+};
+
+/* image size under 1280 * 960 are SUBSAMPLING
+ * image size upper 1280 * 960 are SCALING
+ */
+enum ov5647_downsize_mode {
+ SUBSAMPLING,
+ SCALING,
+};
+
+struct reg_value {
+ u16 u16RegAddr;
+ u8 u8Val;
+ u8 u8Mask;
+ u32 u32Delay_ms;
+};
+
+struct ov5647_mode_info {
+ enum ov5647_mode mode;
+ enum ov5647_downsize_mode dn_mode;
+ u32 width;
+ u32 height;
+ struct reg_value *init_data_ptr;
+ u32 init_data_size;
+};
+
+struct otp_struct {
+ int customer_id;
+ int module_integrator_id;
+ int lens_id;
+ int rg_ratio;
+ int bg_ratio;
+ int user_data[3];
+ int light_rg;
+ int light_bg;
+};
+
+struct ov5647 {
+ struct v4l2_subdev subdev;
+ struct i2c_client *i2c_client;
+ struct v4l2_pix_format pix;
+ const struct ov5647_datafmt *fmt;
+ 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;
+ u8 mclk_source;
+ struct clk *sensor_clk;
+ int csi;
+
+ void (*io_init)(void);
+};
+/*!
+ * Maintains the information on the current state of the sesor.
+ */
+static struct ov5647 ov5647_data;
+static int pwn_gpio, rst_gpio;
+static int prev_sysclk, prev_HTS;
+static int AE_low, AE_high, AE_Target = 52;
+
+/* R/G and B/G of typical camera module is defined here,
+ * the typical camera module is selected by CameraAnalyzer. */
+static int RG_Ratio_Typical = 0x70;
+static int BG_Ratio_Typical = 0x70;
+
+
+static struct reg_value ov5647_init_setting[] = {
+
+ {0x0100, 0x00, 0, 0}, {0x3035, 0x11, 0, 0},
+ {0x3036, 0x69, 0, 0}, {0x303c, 0x11, 0, 0}, {0x3821, 0x07, 0, 0},
+ {0x3820, 0x41, 0, 0}, {0x370c, 0x0f, 0, 0}, {0x3612, 0x59, 0, 0},
+ {0x3618, 0x00, 0, 0}, {0x5000, 0x06, 0, 0}, {0x5002, 0x40, 0, 0},
+ {0x5003, 0x08, 0, 0}, {0x5a00, 0x08, 0, 0}, {0x3000, 0xff, 0, 0},
+ {0x3001, 0xff, 0, 0}, {0x3002, 0xff, 0, 0}, {0x301d, 0xf0, 0, 0},
+ {0x3a18, 0x00, 0, 0}, {0x3a19, 0xf8, 0, 0}, {0x3c01, 0x80, 0, 0},
+ {0x3b07, 0x0c, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+ {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3814, 0x31, 0, 0},
+ {0x3815, 0x31, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3709, 0x52, 0, 0},
+ {0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x03, 0, 0},
+ {0x380b, 0xc0, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x18, 0, 0},
+ {0x3802, 0x00, 0, 0}, {0x3803, 0x0e, 0, 0}, {0x3804, 0x0a, 0, 0},
+ {0x3805, 0x27, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x95, 0, 0},
+ {0x3630, 0x2e, 0, 0}, {0x3632, 0xe2, 0, 0}, {0x3633, 0x23, 0, 0},
+ {0x3634, 0x44, 0, 0}, {0x3620, 0x64, 0, 0}, {0x3621, 0xe0, 0, 0},
+ {0x3600, 0x37, 0, 0}, {0x3704, 0xa0, 0, 0}, {0x3703, 0x5a, 0, 0},
+ {0x3715, 0x78, 0, 0}, {0x3717, 0x01, 0, 0}, {0x3731, 0x02, 0, 0},
+ {0x370b, 0x60, 0, 0}, {0x3705, 0x1a, 0, 0}, {0x3f05, 0x02, 0, 0},
+ {0x3f06, 0x10, 0, 0}, {0x3f01, 0x0a, 0, 0}, {0x3a08, 0x01, 0, 0},
+ {0x3a09, 0x27, 0, 0}, {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0},
+ {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, {0x3a0f, 0x58, 0, 0},
+ {0x3a10, 0x50, 0, 0}, {0x3a1b, 0x58, 0, 0}, {0x3a1e, 0x50, 0, 0},
+ {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x28, 0, 0}, {0x4001, 0x02, 0, 0},
+ {0x4004, 0x02, 0, 0}, {0x4000, 0x09, 0, 0}, {0x4050, 0x6e, 0, 0},
+ {0x4051, 0x8f, 0, 0}, {0x4837, 0x17, 0, 0}, {0x3503, 0x03, 0, 0},
+ {0x3501, 0x44, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350a, 0x00, 0, 0},
+ {0x350b, 0x7f, 0, 0}, {0x5001, 0x01, 0, 0}, {0x5002, 0x41, 0, 0},
+ {0x5180, 0x08, 0, 0}, {0x5186, 0x04, 0, 0}, {0x5187, 0x00, 0, 0},
+ {0x5188, 0x04, 0, 0}, {0x5189, 0x00, 0, 0}, {0x518a, 0x04, 0, 0},
+ {0x518b, 0x00, 0, 0}, {0x5000, 0x86, 0, 0}, {0x5800, 0x11, 0, 0},
+ {0x5801, 0x0a, 0, 0}, {0x5802, 0x09, 0, 0}, {0x5803, 0x09, 0, 0},
+ {0x5804, 0x0a, 0, 0}, {0x5805, 0x0f, 0, 0}, {0x5806, 0x07, 0, 0},
+ {0x5807, 0x05, 0, 0}, {0x5808, 0x03, 0, 0}, {0x5809, 0x03, 0, 0},
+ {0x580a, 0x05, 0, 0}, {0x580b, 0x07, 0, 0}, {0x580c, 0x05, 0, 0},
+ {0x580d, 0x02, 0, 0}, {0x580e, 0x00, 0, 0}, {0x580f, 0x00, 0, 0},
+ {0x5810, 0x02, 0, 0}, {0x5811, 0x05, 0, 0}, {0x5812, 0x05, 0, 0},
+ {0x5813, 0x02, 0, 0}, {0x5814, 0x00, 0, 0}, {0x5815, 0x00, 0, 0},
+ {0x5816, 0x01, 0, 0}, {0x5817, 0x05, 0, 0}, {0x5818, 0x08, 0, 0},
+ {0x5819, 0x05, 0, 0}, {0x581a, 0x03, 0, 0}, {0x581b, 0x03, 0, 0},
+ {0x581c, 0x04, 0, 0}, {0x581d, 0x07, 0, 0}, {0x581e, 0x10, 0, 0},
+ {0x581f, 0x0b, 0, 0}, {0x5820, 0x09, 0, 0}, {0x5821, 0x09, 0, 0},
+ {0x5822, 0x09, 0, 0}, {0x5823, 0x0e, 0, 0}, {0x5824, 0x28, 0, 0},
+ {0x5825, 0x1a, 0, 0}, {0x5826, 0x1a, 0, 0}, {0x5827, 0x1a, 0, 0},
+ {0x5828, 0x46, 0, 0}, {0x5829, 0x2a, 0, 0}, {0x582a, 0x26, 0, 0},
+ {0x582b, 0x44, 0, 0}, {0x582c, 0x26, 0, 0}, {0x582d, 0x2a, 0, 0},
+ {0x582e, 0x28, 0, 0}, {0x582f, 0x42, 0, 0}, {0x5830, 0x40, 0, 0},
+ {0x5831, 0x42, 0, 0}, {0x5832, 0x28, 0, 0}, {0x5833, 0x0a, 0, 0},
+ {0x5834, 0x16, 0, 0}, {0x5835, 0x44, 0, 0}, {0x5836, 0x26, 0, 0},
+ {0x5837, 0x2a, 0, 0}, {0x5838, 0x28, 0, 0}, {0x5839, 0x0a, 0, 0},
+ {0x583a, 0x0a, 0, 0}, {0x583b, 0x0a, 0, 0}, {0x583c, 0x26, 0, 0},
+ {0x583d, 0xbe, 0, 0}, {0x0100, 0x01, 0, 0}, {0x3000, 0x00, 0, 0},
+ {0x3001, 0x00, 0, 0}, {0x3002, 0x00, 0, 0}, {0x3017, 0xe0, 0, 0},
+ {0x301c, 0xfc, 0, 0}, {0x3636, 0x06, 0, 0}, {0x3016, 0x08, 0, 0},
+ {0x3827, 0xec, 0, 0}, {0x3018, 0x44, 0, 0}, {0x3035, 0x21, 0, 0},
+ {0x3106, 0xf5, 0, 0}, {0x3034, 0x18, 0, 0}, {0x301c, 0xf8, 0, 0},
+};
+
+static struct reg_value ov5647_setting_60fps_VGA_640_480[] = {
+ {0x0100, 0x00, 0, 0}, {0x3035, 0x11, 0, 0},
+ {0x3036, 0x46, 0, 0}, {0x303c, 0x11, 0, 0}, {0x3821, 0x07, 0, 0},
+ {0x3820, 0x41, 0, 0}, {0x370c, 0x0f, 0, 0}, {0x3612, 0x59, 0, 0},
+ {0x3618, 0x00, 0, 0}, {0x5000, 0x06, 0, 0}, {0x5002, 0x40, 0, 0},
+ {0x5003, 0x08, 0, 0}, {0x5a00, 0x08, 0, 0}, {0x3000, 0xff, 0, 0},
+ {0x3001, 0xff, 0, 0}, {0x3002, 0xff, 0, 0}, {0x301d, 0xf0, 0, 0},
+ {0x3a18, 0x00, 0, 0}, {0x3a19, 0xf8, 0, 0}, {0x3c01, 0x80, 0, 0},
+ {0x3b07, 0x0c, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x3c, 0, 0},
+ {0x380e, 0x01, 0, 0}, {0x380f, 0xf8, 0, 0}, {0x3814, 0x71, 0, 0},
+ {0x3815, 0x71, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3709, 0x52, 0, 0},
+ {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0},
+ {0x380b, 0xe0, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x10, 0, 0},
+ {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0},
+ {0x3805, 0x2f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0},
+ {0x3630, 0x2e, 0, 0}, {0x3632, 0xe2, 0, 0}, {0x3633, 0x23, 0, 0},
+ {0x3634, 0x44, 0, 0}, {0x3620, 0x64, 0, 0}, {0x3621, 0xe0, 0, 0},
+ {0x3600, 0x37, 0, 0}, {0x3704, 0xa0, 0, 0}, {0x3703, 0x5a, 0, 0},
+ {0x3715, 0x78, 0, 0}, {0x3717, 0x01, 0, 0}, {0x3731, 0x02, 0, 0},
+ {0x370b, 0x60, 0, 0}, {0x3705, 0x1a, 0, 0}, {0x3f05, 0x02, 0, 0},
+ {0x3f06, 0x10, 0, 0}, {0x3f01, 0x0a, 0, 0}, {0x3a08, 0x01, 0, 0},
+ {0x3a09, 0x2e, 0, 0}, {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xfb, 0, 0},
+ {0x3a0d, 0x02, 0, 0}, {0x3a0e, 0x01, 0, 0}, {0x3a0f, 0x58, 0, 0},
+ {0x3a10, 0x50, 0, 0}, {0x3a1b, 0x58, 0, 0}, {0x3a1e, 0x50, 0, 0},
+ {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x28, 0, 0}, {0x4001, 0x02, 0, 0},
+ {0x4004, 0x02, 0, 0}, {0x4000, 0x09, 0, 0}, {0x4050, 0x6e, 0, 0},
+ {0x4051, 0x8f, 0, 0}, {0x0100, 0x01, 0, 0}, {0x3000, 0x00, 0, 0},
+ {0x3001, 0x00, 0, 0}, {0x3002, 0x00, 0, 0}, {0x3017, 0xe0, 0, 0},
+ {0x301c, 0xfc, 0, 0}, {0x3636, 0x06, 0, 0}, {0x3016, 0x08, 0, 0},
+ {0x3827, 0xec, 0, 0}, {0x3018, 0x44, 0, 0}, {0x3035, 0x21, 0, 0},
+ {0x3106, 0xf5, 0, 0}, {0x3034, 0x18, 0, 0}, {0x301c, 0xf8, 0, 0},
+};
+
+static struct reg_value ov5647_setting_30fps_720P_1280_720[] = {
+ {0x0100, 0x00, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0},
+ {0x3612, 0x59, 0, 0}, {0x3618, 0x00, 0, 0}, {0x380c, 0x09, 0, 0},
+ {0x380d, 0xe8, 0, 0}, {0x380e, 0x04, 0, 0}, {0x380f, 0x50, 0, 0},
+ {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3709, 0x52, 0, 0},
+ {0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x02, 0, 0},
+ {0x380b, 0xd0, 0, 0}, {0x3801, 0x18, 0, 0}, {0x3802, 0x00, 0, 0},
+ {0x3803, 0xf8, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x27, 0, 0},
+ {0x3806, 0x06, 0, 0}, {0x3807, 0xa7, 0, 0}, {0x3a09, 0xbe, 0, 0},
+ {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x74, 0, 0}, {0x3a0d, 0x02, 0, 0},
+ {0x3a0e, 0x01, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4005, 0x18, 0, 0},
+ {0x0100, 0x01, 0, 0},
+};
+
+static struct reg_value ov5647_setting_30fps_1080P_1920_1080[] = {
+ {0x0100, 0x00, 0, 0}, {0x3820, 0x00, 0, 0}, {0x3821, 0x06, 0, 0},
+ {0x3612, 0x5b, 0, 0}, {0x3618, 0x04, 0, 0}, {0x380c, 0x09, 0, 0},
+ {0x380d, 0xe8, 0, 0}, {0x380e, 0x04, 0, 0}, {0x380f, 0x50, 0, 0},
+ {0x3814, 0x11, 0, 0}, {0x3815, 0x11, 0, 0}, {0x3709, 0x12, 0, 0},
+ {0x3808, 0x07, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x04, 0, 0},
+ {0x380b, 0x38, 0, 0}, {0x3801, 0x5c, 0, 0}, {0x3802, 0x01, 0, 0},
+ {0x3803, 0xb2, 0, 0}, {0x3804, 0x08, 0, 0}, {0x3805, 0xe3, 0, 0},
+ {0x3806, 0x05, 0, 0}, {0x3807, 0xf1, 0, 0}, {0x3a09, 0x4b, 0, 0},
+ {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x13, 0, 0}, {0x3a0d, 0x04, 0, 0},
+ {0x3a0e, 0x03, 0, 0}, {0x4004, 0x04, 0, 0}, {0x4005, 0x18, 0, 0},
+ {0x0100, 0x01, 0, 0},
+};
+
+static struct reg_value ov5647_setting_15fps_QSXGA_2592_1944[] = {
+ {0x0100, 0x00, 0, 0}, {0x3820, 0x00, 0, 0}, {0x3821, 0x06, 0, 0},
+ {0x3612, 0x5b, 0, 0}, {0x3618, 0x04, 0, 0}, {0x380c, 0x0b, 0, 0},
+ {0x380d, 0x10, 0, 0}, {0x380e, 0x07, 0, 0}, {0x380f, 0xb8, 0, 0},
+ {0x3814, 0x11, 0, 0}, {0x3815, 0x11, 0, 0}, {0x3709, 0x12, 0, 0},
+ {0x3808, 0x0a, 0, 0}, {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0},
+ {0x380b, 0x98, 0, 0}, {0x3801, 0x0c, 0, 0}, {0x3802, 0x00, 0, 0},
+ {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x33, 0, 0},
+ {0x3806, 0x07, 0, 0}, {0x3807, 0xa3, 0, 0}, {0x3a09, 0x28, 0, 0},
+ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0d, 0x08, 0, 0},
+ {0x3a0e, 0x06, 0, 0}, {0x4004, 0x04, 0, 0}, {0x4005, 0x1a, 0, 0},
+ {0x0100, 0x01, 0, 0},
+};
+
+static struct ov5647_mode_info ov5647_mode_info_data[2][ov5647_mode_MAX + 1] = {
+ {
+ {ov5647_mode_VGA_640_480, -1, 0, 0, NULL, 0},
+ {ov5647_mode_720P_1280_720, -1, 0, 0, NULL, 0},
+ {ov5647_mode_1080P_1920_1080, -1, 0, 0, NULL, 0},
+ {ov5647_mode_QSXGA_2592_1944, SCALING, 2592, 1944,
+ ov5647_setting_15fps_QSXGA_2592_1944,
+ ARRAY_SIZE(ov5647_setting_15fps_QSXGA_2592_1944)},
+ },
+ {
+ /* Actually VGA working in 60fps mode */
+ {ov5647_mode_VGA_640_480, SUBSAMPLING, 640, 480,
+ ov5647_setting_60fps_VGA_640_480,
+ ARRAY_SIZE(ov5647_setting_60fps_VGA_640_480)},
+ {ov5647_mode_720P_1280_720, SUBSAMPLING, 1280, 720,
+ ov5647_setting_30fps_720P_1280_720,
+ ARRAY_SIZE(ov5647_setting_30fps_720P_1280_720)},
+ {ov5647_mode_1080P_1920_1080, SCALING, 1920, 1080,
+ ov5647_setting_30fps_1080P_1920_1080,
+ ARRAY_SIZE(ov5647_setting_30fps_1080P_1920_1080)},
+ {ov5647_mode_QSXGA_2592_1944, -1, 0, 0, NULL, 0},
+ },
+};
+
+static struct regulator *io_regulator;
+static struct regulator *core_regulator;
+static struct regulator *analog_regulator;
+static struct regulator *gpo_regulator;
+
+static int ov5647_probe(struct i2c_client *adapter,
+ const struct i2c_device_id *device_id);
+static int ov5647_remove(struct i2c_client *client);
+
+static s32 ov5647_read_reg(u16 reg, u8 *val);
+static s32 ov5647_write_reg(u16 reg, u8 val);
+
+static const struct i2c_device_id ov5647_id[] = {
+ {"ov5647_mipi", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, ov5647_id);
+
+static struct i2c_driver ov5647_i2c_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "ov5647_mipi",
+ },
+ .probe = ov5647_probe,
+ .remove = ov5647_remove,
+ .id_table = ov5647_id,
+};
+
+static const struct ov5647_datafmt ov5647_colour_fmts[] = {
+ {MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_JPEG},
+};
+
+static struct ov5647 *to_ov5647(const struct i2c_client *client)
+{
+ return container_of(i2c_get_clientdata(client), struct ov5647, subdev);
+}
+
+/* Find a data format by a pixel code in an array */
+static const struct ov5647_datafmt
+ *ov5647_find_datafmt(u32 code)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ov5647_colour_fmts); i++)
+ if (ov5647_colour_fmts[i].code == code)
+ return ov5647_colour_fmts + i;
+
+ return NULL;
+}
+
+static inline void ov5647_power_down(int enable)
+{
+ if (pwn_gpio < 0)
+ return;
+
+ /* 19x19 pwdn pin invert by mipi daughter card */
+ if (!enable)
+ gpio_set_value_cansleep(pwn_gpio, 1);
+ else
+ gpio_set_value_cansleep(pwn_gpio, 0);
+
+ msleep(2);
+}
+
+static void ov5647_reset(void)
+{
+ if (rst_gpio < 0 || pwn_gpio < 0)
+ return;
+
+ /* camera reset */
+ gpio_set_value_cansleep(rst_gpio, 1);
+
+ /* camera power dowmn */
+ gpio_set_value_cansleep(pwn_gpio, 1);
+ msleep(5);
+
+ gpio_set_value_cansleep(pwn_gpio, 0);
+ msleep(5);
+
+ gpio_set_value_cansleep(rst_gpio, 0);
+ msleep(1);
+
+ gpio_set_value_cansleep(rst_gpio, 1);
+ msleep(5);
+
+ gpio_set_value_cansleep(pwn_gpio, 1);
+}
+
+static int ov5647_regulator_enable(struct device *dev)
+{
+ int ret = 0;
+
+ io_regulator = devm_regulator_get(dev, "DOVDD");
+ if (!IS_ERR(io_regulator)) {
+ regulator_set_voltage(io_regulator,
+ OV5647_VOLTAGE_DIGITAL_IO,
+ OV5647_VOLTAGE_DIGITAL_IO);
+ ret = regulator_enable(io_regulator);
+ if (ret) {
+ pr_err("%s:io set voltage error\n", __func__);
+ return ret;
+ } else {
+ dev_dbg(dev,
+ "%s:io set voltage ok\n", __func__);
+ }
+ } else {
+ pr_err("%s: cannot get io voltage error\n", __func__);
+ io_regulator = NULL;
+ }
+
+ core_regulator = devm_regulator_get(dev, "DVDD");
+ if (!IS_ERR(core_regulator)) {
+ regulator_set_voltage(core_regulator,
+ OV5647_VOLTAGE_DIGITAL_CORE,
+ OV5647_VOLTAGE_DIGITAL_CORE);
+ ret = regulator_enable(core_regulator);
+ if (ret) {
+ pr_err("%s:core set voltage error\n", __func__);
+ return ret;
+ } else {
+ dev_dbg(dev,
+ "%s:core set voltage ok\n", __func__);
+ }
+ } else {
+ core_regulator = NULL;
+ pr_err("%s: cannot get core voltage error\n", __func__);
+ }
+
+ analog_regulator = devm_regulator_get(dev, "AVDD");
+ if (!IS_ERR(analog_regulator)) {
+ regulator_set_voltage(analog_regulator,
+ OV5647_VOLTAGE_ANALOG,
+ OV5647_VOLTAGE_ANALOG);
+ ret = regulator_enable(analog_regulator);
+ if (ret) {
+ pr_err("%s:analog set voltage error\n",
+ __func__);
+ return ret;
+ } else {
+ dev_dbg(dev,
+ "%s:analog set voltage ok\n", __func__);
+ }
+ } else {
+ analog_regulator = NULL;
+ pr_err("%s: cannot get analog voltage error\n", __func__);
+ }
+
+ return ret;
+}
+
+static s32 ov5647_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(ov5647_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 ov5647_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(ov5647_data.i2c_client, au8RegBuf, 2)) {
+ pr_err("%s:write reg error:reg=%x\n",
+ __func__, reg);
+ return -1;
+ }
+
+ if (1 != i2c_master_recv(ov5647_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;
+}
+
+/* index: index of otp group. (0, 1, 2)
+ * return:
+ * 0, group index is empty
+ * 1, group index has invalid data
+ * 2, group index has valid data */
+static int ov5647_check_otp(int index)
+{
+ int i;
+ int address;
+ u8 temp;
+
+ /* read otp into buffer */
+ ov5647_write_reg(0x3d21, 0x01);
+ msleep(20);
+ address = 0x3d05 + index * 9;
+ temp = ov5647_read_reg(address, &temp);
+
+ /* disable otp read */
+ ov5647_write_reg(0x3d21, 0x00);
+
+ /* clear otp buffer */
+ for (i = 0; i < 32; i++)
+ ov5647_write_reg(0x3d00 + i, 0x00);
+
+ if (!temp)
+ return 0;
+ else if ((!(temp & 0x80)) && (temp & 0x7f))
+ return 2;
+ else
+ return 1;
+}
+
+/* index: index of otp group. (0, 1, 2)
+ * return: 0 */
+static int ov5647_read_otp(int index, struct otp_struct *otp_ptr)
+{
+ int i;
+ int address;
+ u8 temp;
+
+ /* read otp into buffer */
+ ov5647_write_reg(0x3d21, 0x01);
+ msleep(2);
+ address = 0x3d05 + index * 9;
+ temp = ov5647_read_reg(address, &temp);
+ (*otp_ptr).customer_id = temp & 0x7f;
+
+ (*otp_ptr).module_integrator_id = ov5647_read_reg(address, &temp);
+ (*otp_ptr).lens_id = ov5647_read_reg(address + 1, &temp);
+ (*otp_ptr).rg_ratio = ov5647_read_reg(address + 2, &temp);
+ (*otp_ptr).bg_ratio = ov5647_read_reg(address + 3, &temp);
+ (*otp_ptr).user_data[0] = ov5647_read_reg(address + 4, &temp);
+ (*otp_ptr).user_data[1] = ov5647_read_reg(address + 5, &temp);
+ (*otp_ptr).user_data[2] = ov5647_read_reg(address + 6, &temp);
+ (*otp_ptr).light_rg = ov5647_read_reg(address + 7, &temp);
+ (*otp_ptr).light_bg = ov5647_read_reg(address + 8, &temp);
+
+ /* disable otp read */
+ ov5647_write_reg(0x3d21, 0x00);
+
+ /* clear otp buffer */
+ for (i = 0; i < 32; i++)
+ ov5647_write_reg(0x3d00 + i, 0x00);
+
+ return 0;
+}
+
+/* R_gain, sensor red gain of AWB, 0x400 =1
+ * G_gain, sensor green gain of AWB, 0x400 =1
+ * B_gain, sensor blue gain of AWB, 0x400 =1
+ * return 0 */
+static int ov5647_update_awb_gain(int R_gain, int G_gain, int B_gain)
+{
+ if (R_gain > 0x400) {
+ ov5647_write_reg(0x5186, R_gain >> 8);
+ ov5647_write_reg(0x5187, R_gain & 0x00ff);
+ }
+ if (G_gain > 0x400) {
+ ov5647_write_reg(0x5188, G_gain >> 8);
+ ov5647_write_reg(0x5189, G_gain & 0x00ff);
+ }
+ if (B_gain > 0x400) {
+ ov5647_write_reg(0x518a, B_gain >> 8);
+ ov5647_write_reg(0x518b, B_gain & 0x00ff);
+ }
+
+ return 0;
+}
+
+/* call this function after OV5647 initialization
+ * return value:
+ * 0 update success
+ * 1 no OTP */
+static int ov5647_update_otp(void)
+{
+ struct otp_struct current_otp;
+ int i;
+ int otp_index;
+ int temp;
+ int R_gain, G_gain, B_gain, G_gain_R, G_gain_B;
+ int rg, bg;
+
+ /* R/G and B/G of current camera module is read out from sensor OTP
+ * check first OTP with valid data */
+ for (i = 0; i < 3; i++) {
+ temp = ov5647_check_otp(i);
+ if (temp == 2) {
+ otp_index = i;
+ break;
+ }
+ }
+ if (i == 3) {
+ /* no valid wb OTP data */
+ printk(KERN_WARNING "No valid wb otp data\n");
+ return 1;
+ }
+
+ ov5647_read_otp(otp_index, ¤t_otp);
+ if (current_otp.light_rg == 0)
+ rg = current_otp.rg_ratio;
+ else
+ rg = current_otp.rg_ratio * (current_otp.light_rg + 128) / 256;
+
+ if (current_otp.light_bg == 0)
+ bg = current_otp.bg_ratio;
+ else
+ bg = current_otp.bg_ratio * (current_otp.light_bg + 128) / 256;
+
+ /* calculate G gain
+ * 0x400 = 1x gain */
+ if (bg < BG_Ratio_Typical) {
+ if (rg < RG_Ratio_Typical) {
+ /* current_otp.bg_ratio < BG_Ratio_typical &&
+ * current_otp.rg_ratio < RG_Ratio_typical */
+ G_gain = 0x400;
+ B_gain = 0x400 * BG_Ratio_Typical / bg;
+ R_gain = 0x400 * RG_Ratio_Typical / rg;
+ } else {
+ /* current_otp.bg_ratio < BG_Ratio_typical &&
+ * current_otp.rg_ratio >= RG_Ratio_typical */
+ R_gain = 0x400;
+ G_gain = 0x400 * rg / RG_Ratio_Typical;
+ B_gain = G_gain * BG_Ratio_Typical / bg;
+ }
+ } else {
+ if (rg < RG_Ratio_Typical) {
+ /* current_otp.bg_ratio >= BG_Ratio_typical &&
+ * current_otp.rg_ratio < RG_Ratio_typical */
+ B_gain = 0x400;
+ G_gain = 0x400 * bg / BG_Ratio_Typical;
+ R_gain = G_gain * RG_Ratio_Typical / rg;
+ } else {
+ /* current_otp.bg_ratio >= BG_Ratio_typical &&
+ * current_otp.rg_ratio >= RG_Ratio_typical */
+ G_gain_B = 0x400 * bg / BG_Ratio_Typical;
+ G_gain_R = 0x400 * rg / RG_Ratio_Typical;
+ if (G_gain_B > G_gain_R) {
+ B_gain = 0x400;
+ G_gain = G_gain_B;
+ R_gain = G_gain * RG_Ratio_Typical / rg;
+ } else {
+ R_gain = 0x400;
+ G_gain = G_gain_R;
+ B_gain = G_gain * BG_Ratio_Typical / bg;
+ }
+ }
+ }
+ ov5647_update_awb_gain(R_gain, G_gain, B_gain);
+ return 0;
+}
+
+static void ov5647_stream_on(void)
+{
+ ov5647_write_reg(0x4202, 0x00);
+}
+
+static void ov5647_stream_off(void)
+{
+ ov5647_write_reg(0x4202, 0x0f);
+ /* both clock and data lane in LP00 */
+ ov5647_write_reg(0x0100, 0x00);
+}
+
+static int ov5647_get_sysclk(void)
+{
+ /* calculate sysclk */
+ int xvclk = ov5647_data.mclk / 10000;
+ int sysclk, temp1, temp2;
+ int pre_div02x, div_cnt7b, sdiv0, pll_rdiv, bit_div2x, sclk_div, VCO;
+ int pre_div02x_map[] = {2, 2, 4, 6, 8, 3, 12, 5, 16, 2, 2, 2, 2, 2, 2, 2};
+ int sdiv0_map[] = {16, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
+ int pll_rdiv_map[] = {1, 2};
+ int bit_div2x_map[] = {2, 2, 2, 2, 2, 2, 2, 2, 4, 2, 5, 2, 2, 2, 2, 2};
+ int sclk_div_map[] = {1, 2, 4, 1};
+ u8 temp;
+
+ temp1 = ov5647_read_reg(0x3037, &temp);
+ temp2 = temp1 & 0x0f;
+ pre_div02x = pre_div02x_map[temp2];
+ temp2 = (temp1 >> 4) & 0x01;
+ pll_rdiv = pll_rdiv_map[temp2];
+ temp1 = ov5647_read_reg(0x3036, &temp);
+
+ div_cnt7b = temp1;
+
+ VCO = xvclk * 2 / pre_div02x * div_cnt7b;
+ temp1 = ov5647_read_reg(0x3035, &temp);
+ temp2 = temp1 >> 4;
+ sdiv0 = sdiv0_map[temp2];
+ temp1 = ov5647_read_reg(0x3034, &temp);
+ temp2 = temp1 & 0x0f;
+ bit_div2x = bit_div2x_map[temp2];
+ temp1 = ov5647_read_reg(0x3106, &temp);
+ temp2 = (temp1 >> 2) & 0x03;
+ sclk_div = sclk_div_map[temp2];
+ sysclk = VCO * 2 / sdiv0 / pll_rdiv / bit_div2x / sclk_div;
+ return sysclk;
+}
+
+static void ov5647_set_night_mode(void)
+{
+ /* read HTS from register settings */
+ u8 mode;
+
+ ov5647_read_reg(0x3a00, &mode);
+ mode &= 0xfb;
+ ov5647_write_reg(0x3a00, mode);
+}
+
+static int ov5647_get_HTS(void)
+{
+ /* read HTS from register settings */
+ int HTS;
+ u8 temp;
+
+ HTS = ov5647_read_reg(0x380c, &temp);
+ HTS = (HTS << 8) + ov5647_read_reg(0x380d, &temp);
+
+ return HTS;
+}
+
+static int ov5647_soft_reset(void)
+{
+ /* soft reset ov5647 */
+
+ ov5647_write_reg(0x0103, 0x1);
+ msleep(5);
+
+ return 0;
+}
+
+static int ov5647_get_VTS(void)
+{
+ /* read VTS from register settings */
+ int VTS;
+ u8 temp;
+
+ /* total vertical size[15:8] high byte */
+ VTS = ov5647_read_reg(0x380e, &temp);
+
+ VTS = (VTS << 8) + ov5647_read_reg(0x380f, &temp);
+
+ return VTS;
+}
+
+static int ov5647_set_VTS(int VTS)
+{
+ /* write VTS to registers */
+ int temp;
+
+ temp = VTS & 0xff;
+ ov5647_write_reg(0x380f, temp);
+
+ temp = VTS >> 8;
+ ov5647_write_reg(0x380e, temp);
+
+ return 0;
+}
+
+static int ov5647_get_shutter(void)
+{
+ /* read shutter, in number of line period */
+ int shutter;
+ u8 temp;
+
+ shutter = (ov5647_read_reg(0x03500, &temp) & 0x0f);
+ shutter = (shutter << 8) + ov5647_read_reg(0x3501, &temp);
+ shutter = (shutter << 4) + (ov5647_read_reg(0x3502, &temp)>>4);
+
+ return shutter;
+}
+
+static int ov5647_set_shutter(int shutter)
+{
+ /* write shutter, in number of line period */
+ int temp;
+
+ shutter = shutter & 0xffff;
+
+ temp = shutter & 0x0f;
+ temp = temp << 4;
+ ov5647_write_reg(0x3502, temp);
+
+ temp = shutter & 0xfff;
+ temp = temp >> 4;
+ ov5647_write_reg(0x3501, temp);
+
+ temp = shutter >> 12;
+ ov5647_write_reg(0x3500, temp);
+
+ return 0;
+}
+
+static int ov5647_get_gain16(void)
+{
+ /* read gain, 16 = 1x */
+ int gain16;
+ u8 temp;
+
+ gain16 = ov5647_read_reg(0x350a, &temp) & 0x03;
+ gain16 = (gain16 << 8) + ov5647_read_reg(0x350b, &temp);
+
+ return gain16;
+}
+
+static int ov5647_set_gain16(int gain16)
+{
+ /* write gain, 16 = 1x */
+ u8 temp;
+ gain16 = gain16 & 0x3ff;
+
+ temp = gain16 & 0xff;
+ ov5647_write_reg(0x350b, temp);
+
+ temp = gain16 >> 8;
+ ov5647_write_reg(0x350a, temp);
+
+ return 0;
+}
+
+static int ov5647_get_light_freq(void)
+{
+ /* get banding filter value */
+ int temp, temp1, light_freq = 0;
+ u8 tmp;
+
+ temp = ov5647_read_reg(0x3c01, &tmp);
+
+ if (temp & 0x80) {
+ /* manual */
+ temp1 = ov5647_read_reg(0x3c00, &tmp);
+ if (temp1 & 0x04) {
+ /* 50Hz */
+ light_freq = 50;
+ } else {
+ /* 60Hz */
+ light_freq = 60;
+ }
+ } else {
+ /* auto */
+ temp1 = ov5647_read_reg(0x3c0c, &tmp);
+ if (temp1 & 0x01) {
+ /* 50Hz */
+ light_freq = 50;
+ } else {
+ /* 60Hz */
+ }
+ }
+ return light_freq;
+}
+
+static void ov5647_set_bandingfilter(void)
+{
+ int prev_VTS;
+ int band_step60, max_band60, band_step50, max_band50;
+
+ /* read preview PCLK */
+ prev_sysclk = ov5647_get_sysclk();
+ /* read preview HTS */
+ prev_HTS = ov5647_get_HTS();
+
+ /* read preview VTS */
+ prev_VTS = ov5647_get_VTS();
+
+ /* calculate banding filter */
+ /* 60Hz */
+ band_step60 = prev_sysclk * 100/prev_HTS * 100/120;
+ ov5647_write_reg(0x3a0a, (band_step60 >> 8));
+ ov5647_write_reg(0x3a0b, (band_step60 & 0xff));
+
+ max_band60 = (int)((prev_VTS-4)/band_step60);
+ ov5647_write_reg(0x3a0d, max_band60);
+
+ /* 50Hz */
+ band_step50 = prev_sysclk * 100/prev_HTS;
+ ov5647_write_reg(0x3a08, (band_step50 >> 8));
+ ov5647_write_reg(0x3a09, (band_step50 & 0xff));
+
+ max_band50 = (int)((prev_VTS-4)/band_step50);
+ ov5647_write_reg(0x3a0e, max_band50);
+}
+
+static int ov5647_set_AE_target(int target)
+{
+ /* stable in high */
+ int fast_high, fast_low;
+ AE_low = target * 23 / 25; /* 0.92 */
+ AE_high = target * 27 / 25; /* 1.08 */
+
+ fast_high = AE_high<<1;
+ if (fast_high > 255)
+ fast_high = 255;
+
+ fast_low = AE_low >> 1;
+
+ ov5647_write_reg(0x3a0f, AE_high);
+ ov5647_write_reg(0x3a10, AE_low);
+ ov5647_write_reg(0x3a1b, AE_high);
+ ov5647_write_reg(0x3a1e, AE_low);
+ ov5647_write_reg(0x3a11, fast_high);
+ ov5647_write_reg(0x3a1f, fast_low);
+
+ return 0;
+}
+
+static void ov5647_turn_on_AE_AG(int enable)
+{
+ u8 ae_ag_ctrl;
+
+ ov5647_read_reg(0x3503, &ae_ag_ctrl);
+ if (enable) {
+ /* turn on auto AE/AG */
+ ae_ag_ctrl = ae_ag_ctrl & ~(0x03);
+ } else {
+ /* turn off AE/AG */
+ ae_ag_ctrl = ae_ag_ctrl | 0x03;
+ }
+ ov5647_write_reg(0x3503, ae_ag_ctrl);
+}
+
+static void ov5647_set_virtual_channel(int channel)
+{
+ u8 channel_id;
+
+ ov5647_read_reg(0x4814, &channel_id);
+ channel_id &= ~(3 << 6);
+ ov5647_write_reg(0x4814, channel_id | (channel << 6));
+}
+
+/* download ov5647 settings to sensor through i2c */
+static int ov5647_download_firmware(struct reg_value *pModeSetting, s32 ArySize)
+{
+ register u32 Delay_ms = 0;
+ register u16 RegAddr = 0;
+ register u8 Mask = 0;
+ register u8 Val = 0;
+ u8 RegVal = 0;
+ int i, retval = 0;
+
+ for (i = 0; i < ArySize; ++i, ++pModeSetting) {
+ Delay_ms = pModeSetting->u32Delay_ms;
+ RegAddr = pModeSetting->u16RegAddr;
+ Val = pModeSetting->u8Val;
+ Mask = pModeSetting->u8Mask;
+
+ if (Mask) {
+ retval = ov5647_read_reg(RegAddr, &RegVal);
+ if (retval < 0)
+ goto err;
+
+ RegVal &= ~(u8)Mask;
+ Val &= Mask;
+ Val |= RegVal;
+ }
+
+ retval = ov5647_write_reg(RegAddr, Val);
+ if (retval < 0)
+ goto err;
+
+ if (Delay_ms)
+ msleep(Delay_ms);
+ }
+err:
+ return retval;
+}
+
+/* sensor changes between scaling and subsampling
+ * go through exposure calcualtion
+ */
+static int ov5647_change_mode_exposure_calc(enum ov5647_frame_rate frame_rate,
+ enum ov5647_mode mode)
+{
+ struct reg_value *pModeSetting = NULL;
+ s32 ArySize = 0;
+ int pre_shutter, pre_gain16;
+ int cap_shutter, cap_gain16;
+ int pre_sysclk, pre_HTS;
+ int cap_sysclk, cap_HTS, cap_VTS;
+ long cap_gain16_shutter;
+ int retval = 0;
+
+ /* check if the input mode and frame rate is valid */
+ pModeSetting =
+ ov5647_mode_info_data[frame_rate][mode].init_data_ptr;
+ ArySize =
+ ov5647_mode_info_data[frame_rate][mode].init_data_size;
+
+ ov5647_data.pix.width =
+ ov5647_mode_info_data[frame_rate][mode].width;
+ ov5647_data.pix.height =
+ ov5647_mode_info_data[frame_rate][mode].height;
+
+ if (ov5647_data.pix.width == 0 || ov5647_data.pix.height == 0 ||
+ pModeSetting == NULL || ArySize == 0)
+ return -EINVAL;
+
+ ov5647_stream_off();
+
+ /* turn off night mode for capture */
+ ov5647_set_night_mode();
+
+ pre_shutter = ov5647_get_shutter();
+ pre_gain16 = ov5647_get_gain16();
+ pre_HTS = ov5647_get_HTS();
+ pre_sysclk = ov5647_get_sysclk();
+
+ /* Write capture setting */
+ retval = ov5647_download_firmware(pModeSetting, ArySize);
+ if (retval < 0)
+ goto err;
+
+ /* read capture VTS */
+ cap_VTS = ov5647_get_VTS();
+ cap_HTS = ov5647_get_HTS();
+ cap_sysclk = ov5647_get_sysclk();
+
+ /* calculate capture shutter/gain16 */
+ cap_shutter = pre_shutter * cap_sysclk/pre_sysclk * pre_HTS / cap_HTS;
+
+ if (cap_shutter < 16) {
+ cap_gain16_shutter = pre_shutter * pre_gain16 *
+ cap_sysclk / pre_sysclk * pre_HTS / cap_HTS;
+ cap_shutter = ((int)(cap_gain16_shutter / 16));
+ if (cap_shutter < 1)
+ cap_shutter = 1;
+ cap_gain16 = ((int)(cap_gain16_shutter / cap_shutter));
+ if (cap_gain16 < 16)
+ cap_gain16 = 16;
+ } else
+ cap_gain16 = pre_gain16;
+
+ /* gain to shutter */
+ while ((cap_gain16 > 32) &&
+ (cap_shutter < ((int)((cap_VTS - 4) / 2)))) {
+ cap_gain16 = cap_gain16 / 2;
+ cap_shutter = cap_shutter * 2;
+ }
+ /* write capture gain */
+ ov5647_set_gain16(cap_gain16);
+
+ /* write capture shutter */
+ if (cap_shutter > (cap_VTS - 4)) {
+ cap_VTS = cap_shutter + 4;
+ ov5647_set_VTS(cap_VTS);
+ }
+ ov5647_set_shutter(cap_shutter);
+
+err:
+ return retval;
+}
+
+/* if sensor changes inside scaling or subsampling
+ * change mode directly
+ * */
+static int ov5647_change_mode_direct(enum ov5647_frame_rate frame_rate,
+ enum ov5647_mode mode)
+{
+ struct reg_value *pModeSetting = NULL;
+ s32 ArySize = 0;
+ int retval = 0;
+
+ /* check if the input mode and frame rate is valid */
+ pModeSetting =
+ ov5647_mode_info_data[frame_rate][mode].init_data_ptr;
+ ArySize =
+ ov5647_mode_info_data[frame_rate][mode].init_data_size;
+
+ ov5647_data.pix.width =
+ ov5647_mode_info_data[frame_rate][mode].width;
+ ov5647_data.pix.height =
+ ov5647_mode_info_data[frame_rate][mode].height;
+
+ if (ov5647_data.pix.width == 0 || ov5647_data.pix.height == 0 ||
+ pModeSetting == NULL || ArySize == 0)
+ return -EINVAL;
+
+ /* turn off AE/AG */
+ ov5647_turn_on_AE_AG(0);
+
+ ov5647_stream_off();
+
+ /* Write capture setting */
+ retval = ov5647_download_firmware(pModeSetting, ArySize);
+ if (retval < 0)
+ goto err;
+
+ ov5647_turn_on_AE_AG(1);
+
+err:
+ return retval;
+}
+
+static int ov5647_init_mode(enum ov5647_frame_rate frame_rate,
+ enum ov5647_mode mode, enum ov5647_mode orig_mode)
+{
+ struct reg_value *pModeSetting = NULL;
+ s32 ArySize = 0;
+ int retval = 0;
+ u32 msec_wait4stable = 0;
+ enum ov5647_downsize_mode dn_mode, orig_dn_mode;
+
+ if ((mode > ov5647_mode_MAX || mode < ov5647_mode_MIN)
+ && (mode != ov5647_mode_INIT)) {
+ pr_err("Wrong ov5647 mode detected!\n");
+ return -1;
+ }
+
+ dn_mode = ov5647_mode_info_data[frame_rate][mode].dn_mode;
+ orig_dn_mode = ov5647_mode_info_data[frame_rate][orig_mode].dn_mode;
+ if (mode == ov5647_mode_INIT) {
+ ov5647_soft_reset();
+ pModeSetting = ov5647_init_setting;
+ ArySize = ARRAY_SIZE(ov5647_init_setting);
+ retval = ov5647_download_firmware(pModeSetting, ArySize);
+ if (retval < 0)
+ goto err;
+ pModeSetting = ov5647_setting_60fps_VGA_640_480;
+ ArySize = ARRAY_SIZE(ov5647_setting_60fps_VGA_640_480);
+ retval = ov5647_download_firmware(pModeSetting, ArySize);
+
+ ov5647_data.pix.width = 640;
+ ov5647_data.pix.height = 480;
+ } else if ((dn_mode == SUBSAMPLING && orig_dn_mode == SCALING) ||
+ (dn_mode == SCALING && orig_dn_mode == SUBSAMPLING)) {
+ /* change between subsampling and scaling
+ * go through exposure calucation */
+ retval = ov5647_change_mode_exposure_calc(frame_rate, mode);
+ } else {
+ /* change inside subsampling or scaling
+ * download firmware directly */
+ retval = ov5647_change_mode_direct(frame_rate, mode);
+ }
+
+ if (retval < 0)
+ goto err;
+
+ ov5647_set_AE_target(AE_Target);
+ ov5647_get_light_freq();
+ ov5647_set_bandingfilter();
+ ov5647_set_virtual_channel(ov5647_data.csi);
+
+ /* add delay to wait for sensor stable */
+ if (mode == ov5647_mode_QSXGA_2592_1944) {
+ /* dump the first two frames: 1/7.5*2
+ * the frame rate of QSXGA is 7.5fps */
+ msec_wait4stable = 267;
+ } else if (frame_rate == ov5647_15_fps) {
+ /* dump the first nine frames: 1/15*9 */
+ msec_wait4stable = 600;
+ } else if (frame_rate == ov5647_30_fps) {
+ /* dump the first nine frames: 1/30*9 */
+ msec_wait4stable = 300;
+ }
+ msleep(msec_wait4stable);
+
+err:
+ return retval;
+}
+
+/*!
+ * ov5647_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 ov5647_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov5647 *sensor = to_ov5647(client);
+
+ if (on && !sensor->on) {
+ 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);
+ }
+
+ sensor->on = on;
+
+ return 0;
+}
+
+/*!
+ * ov5647_g_parm - V4L2 sensor interface handler for VIDIOC_G_PARM ioctl
+ * @s: pointer to standard V4L2 sub device structure
+ * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure
+ *
+ * Returns the sensor's video CAPTURE parameters.
+ */
+static int ov5647_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov5647 *sensor = to_ov5647(client);
+ 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;
+}
+
+/*!
+ * ov5460_s_parm - V4L2 sensor interface handler for VIDIOC_S_PARM ioctl
+ * @s: pointer to standard V4L2 sub 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 ov5647_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov5647 *sensor = to_ov5647(client);
+ struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe;
+ u32 tgt_fps; /* target frames per secound */
+ enum ov5647_frame_rate frame_rate;
+ enum ov5647_mode orig_mode;
+ int ret = 0;
+
+ /* Make sure power on */
+ ov5647_power_down(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 = ov5647_15_fps;
+ else if (tgt_fps == 30)
+ frame_rate = ov5647_30_fps;
+ else {
+ pr_err(" The camera frame rate is not supported!\n");
+ return -EINVAL;
+ }
+
+ orig_mode = sensor->streamcap.capturemode;
+ ret = ov5647_init_mode(frame_rate,
+ (u32)a->parm.capture.capturemode, orig_mode);
+ if (ret < 0)
+ return ret;
+
+ sensor->streamcap.timeperframe = *timeperframe;
+ sensor->streamcap.capturemode =
+ (u32)a->parm.capture.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;
+}
+
+static int ov5647_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
+{
+ struct v4l2_mbus_framefmt *mf = &format->format;
+ const struct ov5647_datafmt *fmt = ov5647_find_datafmt(mf->code);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov5647 *sensor = to_ov5647(client);
+
+ if (!fmt) {
+ mf->code = ov5647_colour_fmts[0].code;
+ mf->colorspace = ov5647_colour_fmts[0].colorspace;
+ }
+
+ mf->field = V4L2_FIELD_NONE;
+
+ if (format->which == V4L2_SUBDEV_FORMAT_TRY)
+ return 0;
+
+ sensor->fmt = fmt;
+
+ return 0;
+}
+
+static int ov5647_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
+{
+ struct v4l2_mbus_framefmt *mf = &format->format;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov5647 *sensor = to_ov5647(client);
+ const struct ov5647_datafmt *fmt = sensor->fmt;
+
+ if (format->pad)
+ return -EINVAL;
+
+ mf->code = fmt->code;
+ mf->colorspace = fmt->colorspace;
+ mf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int ov5647_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->pad || code->index >= ARRAY_SIZE(ov5647_colour_fmts))
+ return -EINVAL;
+
+ code->code = ov5647_colour_fmts[code->index].code;
+ return 0;
+}
+
+/*!
+ * ov5647_enum_framesizes - V4L2 sensor interface handler for
+ * VIDIOC_ENUM_FRAMESIZES ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @fsize: standard V4L2 VIDIOC_ENUM_FRAMESIZES ioctl structure
+ *
+ * Return 0 if successful, otherwise -EINVAL.
+ */
+static int ov5647_enum_framesizes(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ if (fse->index > ov5647_mode_MAX)
+ return -EINVAL;
+
+ fse->max_width =
+ max(ov5647_mode_info_data[0][fse->index].width,
+ ov5647_mode_info_data[1][fse->index].width);
+ fse->min_width = fse->max_width;
+ fse->max_height =
+ max(ov5647_mode_info_data[0][fse->index].height,
+ ov5647_mode_info_data[1][fse->index].height);
+ fse->min_height = fse->max_height;
+ return 0;
+}
+
+/*!
+ * ov5647_enum_frameintervals - V4L2 sensor interface handler for
+ * VIDIOC_ENUM_FRAMEINTERVALS ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @fival: standard V4L2 VIDIOC_ENUM_FRAMEINTERVALS ioctl structure
+ *
+ * Return 0 if successful, otherwise -EINVAL.
+ */
+static int ov5647_enum_frameintervals(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_interval_enum *fie)
+{
+ int i, j, count;
+
+ if (fie->index < 0 || fie->index > ov5647_mode_MAX)
+ return -EINVAL;
+
+ if (fie->width == 0 || fie->height == 0 ||
+ fie->code == 0) {
+ pr_warning("Please assign pixel format, width and height.\n");
+ return -EINVAL;
+ }
+
+ fie->interval.numerator = 1;
+
+ count = 0;
+ for (i = 0; i < ARRAY_SIZE(ov5647_mode_info_data); i++) {
+ for (j = 0; j < (ov5647_mode_MAX + 1); j++) {
+ if (fie->width == ov5647_mode_info_data[i][j].width
+ && fie->height == ov5647_mode_info_data[i][j].height
+ && ov5647_mode_info_data[i][j].init_data_ptr != NULL) {
+ count++;
+ }
+ if (fie->index == (count - 1)) {
+ fie->interval.denominator =
+ ov5647_framerates[i];
+ return 0;
+ }
+ }
+ }
+
+ return -EINVAL;
+}
+
+/*!
+ * dev_init - V4L2 sensor init
+ * @s: pointer to standard V4L2 device structure
+ *
+ */
+static int init_device(void)
+{
+ u32 tgt_xclk; /* target xclk */
+ u32 tgt_fps; /* target frames per secound */
+ enum ov5647_frame_rate frame_rate;
+ int ret;
+
+ ov5647_data.on = true;
+
+ /* mclk */
+ tgt_xclk = ov5647_data.mclk;
+ tgt_xclk = min(tgt_xclk, (u32)OV5647_XCLK_MAX);
+ tgt_xclk = max(tgt_xclk, (u32)OV5647_XCLK_MIN);
+ ov5647_data.mclk = tgt_xclk;
+
+ pr_debug(" Setting mclk to %d MHz\n", tgt_xclk / 1000000);
+
+ /* Default camera frame rate is set in probe */
+ tgt_fps = ov5647_data.streamcap.timeperframe.denominator /
+ ov5647_data.streamcap.timeperframe.numerator;
+
+ if (tgt_fps == 15)
+ frame_rate = ov5647_15_fps;
+ else if (tgt_fps == 30)
+ frame_rate = ov5647_30_fps;
+ else
+ return -EINVAL; /* Only support 15fps or 30fps now. */
+
+ ret = ov5647_init_mode(frame_rate, ov5647_mode_INIT, ov5647_mode_INIT);
+
+ ov5647_update_otp();
+ return ret;
+}
+
+static int ov5647_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ if (enable)
+ ov5647_stream_on();
+ else
+ ov5647_stream_off();
+ return 0;
+}
+
+static struct v4l2_subdev_video_ops ov5647_subdev_video_ops = {
+ .g_parm = ov5647_g_parm,
+ .s_parm = ov5647_s_parm,
+ .s_stream = ov5647_s_stream,
+};
+
+static const struct v4l2_subdev_pad_ops ov5647_subdev_pad_ops = {
+ .enum_frame_size = ov5647_enum_framesizes,
+ .enum_frame_interval = ov5647_enum_frameintervals,
+ .enum_mbus_code = ov5647_enum_mbus_code,
+ .set_fmt = ov5647_set_fmt,
+ .get_fmt = ov5647_get_fmt,
+};
+
+static struct v4l2_subdev_core_ops ov5647_subdev_core_ops = {
+ .s_power = ov5647_s_power,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = ov5647_get_register,
+ .s_register = ov5647_set_register,
+#endif
+};
+
+static struct v4l2_subdev_ops ov5647_subdev_ops = {
+ .core = &ov5647_subdev_core_ops,
+ .video = &ov5647_subdev_video_ops,
+ .pad = &ov5647_subdev_pad_ops,
+};
+
+
+/*!
+ * ov5647 I2C probe function
+ *
+ * @param adapter struct i2c_adapter *
+ * @return Error code indicating success or failure
+ */
+static int ov5647_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct pinctrl *pinctrl;
+ struct device *dev = &client->dev;
+ int retval;
+ u8 chip_id_high, chip_id_low;
+
+ /* ov5647 pinctrl */
+ pinctrl = devm_pinctrl_get_select_default(dev);
+ if (IS_ERR(pinctrl)) {
+ dev_warn(dev, "no pin available\n");
+ }
+
+
+ /* request power down pin */
+ pwn_gpio = of_get_named_gpio(dev->of_node, "pwn-gpios", 0);
+ if (!gpio_is_valid(pwn_gpio)) {
+ dev_warn(dev, "no sensor pwdn pin available\n");
+ pwn_gpio = -1;
+ } else {
+ retval = devm_gpio_request_one(dev, pwn_gpio, GPIOF_OUT_INIT_HIGH,
+ "ov5647_mipi_pwdn");
+ if (retval < 0) {
+ dev_warn(dev, "Failed to set power pin\n");
+ return retval;
+ }
+ }
+
+ /* request reset pin */
+ rst_gpio = of_get_named_gpio(dev->of_node, "rst-gpios", 0);
+ if (!gpio_is_valid(rst_gpio)) {
+ dev_warn(dev, "no sensor reset pin available\n");
+ rst_gpio = -1;
+ } else {
+ retval = devm_gpio_request_one(dev, rst_gpio, GPIOF_OUT_INIT_HIGH,
+ "ov5647_mipi_reset");
+ if (retval < 0) {
+ dev_warn(dev, "Failed to set reset pin\n");
+ return retval;
+ }
+ }
+
+ /* Set initial values for the sensor struct. */
+ memset(&ov5647_data, 0, sizeof(ov5647_data));
+ ov5647_data.sensor_clk = devm_clk_get(dev, "csi_mclk");
+ if (IS_ERR(ov5647_data.sensor_clk)) {
+ /* assuming clock enabled by default */
+ ov5647_data.sensor_clk = NULL;
+ dev_err(dev, "clock-frequency missing or invalid\n");
+ return PTR_ERR(ov5647_data.sensor_clk);
+ }
+
+ retval = of_property_read_u32(dev->of_node, "mclk",
+ &(ov5647_data.mclk));
+ if (retval) {
+ dev_err(dev, "mclk missing or invalid\n");
+ return retval;
+ }
+
+ retval = of_property_read_u32(dev->of_node, "mclk_source",
+ (u32 *) &(ov5647_data.mclk_source));
+ if (retval) {
+ dev_err(dev, "mclk_source missing or invalid\n");
+ return retval;
+ }
+
+ retval = of_property_read_u32(dev->of_node, "csi_id",
+ &(ov5647_data.csi));
+ if (retval) {
+ dev_err(dev, "csi id missing or invalid\n");
+ return retval;
+ }
+
+ clk_prepare_enable(ov5647_data.sensor_clk);
+
+ ov5647_data.io_init = ov5647_reset;
+ ov5647_data.i2c_client = client;
+ ov5647_data.pix.pixelformat = V4L2_PIX_FMT_SBGGR8;
+ ov5647_data.pix.width = 640;
+ ov5647_data.pix.height = 480;
+ ov5647_data.streamcap.capability = V4L2_MODE_HIGHQUALITY |
+ V4L2_CAP_TIMEPERFRAME;
+ ov5647_data.streamcap.capturemode = 0;
+ ov5647_data.streamcap.timeperframe.denominator = DEFAULT_FPS;
+ ov5647_data.streamcap.timeperframe.numerator = 1;
+
+ ov5647_regulator_enable(&client->dev);
+
+ ov5647_reset();
+
+ ov5647_power_down(0);
+
+ retval = ov5647_read_reg(OV5647_CHIP_ID_HIGH_BYTE, &chip_id_high);
+ if (retval < 0 || chip_id_high != 0x56) {
+ pr_warning("camera ov5647_mipi is not found\n");
+ clk_disable_unprepare(ov5647_data.sensor_clk);
+ return -ENODEV;
+ }
+ retval = ov5647_read_reg(OV5647_CHIP_ID_LOW_BYTE, &chip_id_low);
+ if (retval < 0 || chip_id_low != 0x47) {
+ pr_warning("camera ov5647_mipi is not found\n");
+ clk_disable_unprepare(ov5647_data.sensor_clk);
+ return -ENODEV;
+ }
+
+ retval = init_device();
+ if (retval < 0) {
+ clk_disable_unprepare(ov5647_data.sensor_clk);
+ pr_warning("camera ov5647 init failed\n");
+ ov5647_power_down(1);
+ return retval;
+ }
+
+ v4l2_i2c_subdev_init(&ov5647_data.subdev, client, &ov5647_subdev_ops);
+
+ ov5647_data.subdev.grp_id = 678;
+ retval = v4l2_async_register_subdev(&ov5647_data.subdev);
+ if (retval < 0)
+ dev_err(&client->dev,
+ "%s--Async register failed, ret=%d\n", __func__, retval);
+
+ ov5647_stream_off();
+ pr_info("camera ov5647_mipi is found\n");
+ return retval;
+}
+
+/*!
+ * ov5647 I2C detach function
+ *
+ * @param client struct i2c_client *
+ * @return Error code indicating success or failure
+ */
+static int ov5647_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_async_unregister_subdev(sd);
+
+ clk_disable_unprepare(ov5647_data.sensor_clk);
+
+ ov5647_power_down(1);
+
+ if (gpo_regulator)
+ regulator_disable(gpo_regulator);
+
+ if (analog_regulator)
+ regulator_disable(analog_regulator);
+
+ if (core_regulator)
+ regulator_disable(core_regulator);
+
+ if (io_regulator)
+ regulator_disable(io_regulator);
+
+ return 0;
+}
+
+module_i2c_driver(ov5647_i2c_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("OV5647 MIPI Camera Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("CSI");
+++ /dev/null
-if VIDEO_MXC_CAPTURE
-
-config VIDEO_MXC_CSI_CAMERA
- tristate "CSI camera support"
- depends on VIDEO_MXC_CAPTURE && VIDEO_V4L2
- ---help---
- This is the video4linux2 capture driver based on CSI module.
-
-config MXC_CAMERA_OV5640
- tristate "OmniVision ov5640 camera support"
- depends on VIDEO_MXC_CAPTURE && I2C
- ---help---
- If you plan to use the ov5640 Camera with your MXC system, say Y here.
-
-config MXC_VADC
- tristate "mxc VADC support"
- depends on VIDEO_MXC_CAPTURE && VIDEO_V4L2
- ---help---
- If you plan to use the VADC with your MXC system, say Y here.
-
-config MXC_MIPI_CSI
- tristate "mxc mipi csi driver"
- depends on VIDEO_MXC_CAPTURE && VIDEO_V4L2
- ---help---
- This is a V4L2 driver for i.MX7D SoC MIPI-CSI2 receiver devices.
-
-config MXC_CAMERA_OV5640_MIPI
- tristate "OmniVision ov5640 camera support using mipi"
- depends on MXC_MIPI_CSI && I2C
- ---help---
- If you plan to use the ov5640 Camera with mipi interface in your MXC system, say Y here.
-
-config MXC_CAMERA_OV5647_MIPI
- tristate "OmniVision ov5647 camera support using mipi"
- depends on MXC_MIPI_CSI && I2C
- ---help---
- If you plan to use the ov5647 Camera with mipi interface in your MXC system, say Y here.
-endif
+++ /dev/null
-#Makefile for mxc csi driver
-
-obj-$(CONFIG_VIDEO_MXC_CSI_CAMERA) += mx6s_capture.o
-obj-$(CONFIG_MXC_VADC) += mxc_vadc.o
-obj-$(CONFIG_MXC_MIPI_CSI) += mxc_mipi_csi.o
-
-ov5640_camera-objs := ov5640.o
-obj-$(CONFIG_MXC_CAMERA_OV5640) += ov5640_camera.o
-
-ov5640_camera_mipi-objs := ov5640_mipi.o
-obj-$(CONFIG_MXC_CAMERA_OV5640_MIPI) += ov5640_camera_mipi.o
-
-ov5647_camera_mipi-objs := ov5647_mipi.o
-obj-$(CONFIG_MXC_CAMERA_OV5647_MIPI) += ov5647_camera_mipi.o
+++ /dev/null
-/*
- * Copyright (C) 2014-2016 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 mx6s_csi.c
- *
- * @brief mx6sx CMOS Sensor interface functions
- *
- * @ingroup CSI
- */
-#include <asm/dma.h>
-#include <linux/busfreq-imx.h>
-#include <linux/clk.h>
-#include <linux/dma-mapping.h>
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/gcd.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/kernel.h>
-#include <linux/math64.h>
-#include <linux/mfd/syscon.h>
-#include <linux/mm.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
-#include <linux/regmap.h>
-#include <linux/slab.h>
-#include <linux/time.h>
-#include <linux/media-bus-format.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-dev.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-of.h>
-#include <media/videobuf2-core.h>
-#include <media/videobuf2-dma-contig.h>
-
-#define MX6S_CAM_DRV_NAME "mx6s-csi"
-#define MX6S_CAM_VERSION "0.0.1"
-#define MX6S_CAM_DRIVER_DESCRIPTION "i.MX6S_CSI"
-
-#define MAX_VIDEO_MEM 64
-
-/* 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
-#define SHIFT_RXFIFO_LEVEL 4
-
-/* csi status reg */
-#define BIT_ADDR_CH_ERR_INT (0x1 << 28)
-#define BIT_FIELD0_INT (0x1 << 27)
-#define BIT_FIELD1_INT (0x1 << 26)
-#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)
-
-/* csi control reg 18 */
-#define BIT_CSI_ENABLE (0x1 << 31)
-#define BIT_MIPI_DATA_FORMAT_RAW8 (0x2a << 25)
-#define BIT_MIPI_DATA_FORMAT_RAW10 (0x2b << 25)
-#define BIT_MIPI_DATA_FORMAT_YUV422_8B (0x1e << 25)
-#define BIT_MIPI_DATA_FORMAT_MASK (0x3F << 25)
-#define BIT_MIPI_DATA_FORMAT_OFFSET 25
-#define BIT_DATA_FROM_MIPI (0x1 << 22)
-#define BIT_MIPI_YU_SWAP (0x1 << 21)
-#define BIT_MIPI_DOUBLE_CMPNT (0x1 << 20)
-#define BIT_BASEADDR_CHG_ERR_EN (0x1 << 9)
-#define BIT_BASEADDR_SWITCH_SEL (0x1 << 5)
-#define BIT_BASEADDR_SWITCH_EN (0x1 << 4)
-#define BIT_PARALLEL24_EN (0x1 << 3)
-#define BIT_DEINTERLACE_EN (0x1 << 2)
-#define BIT_TVDECODER_IN_EN (0x1 << 1)
-#define BIT_NTSC_EN (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 0x0
-#define CSI_CSICR2 0x4
-#define CSI_CSICR3 0x8
-#define CSI_STATFIFO 0xC
-#define CSI_CSIRXFIFO 0x10
-#define CSI_CSIRXCNT 0x14
-#define CSI_CSISR 0x18
-
-#define CSI_CSIDBG 0x1C
-#define CSI_CSIDMASA_STATFIFO 0x20
-#define CSI_CSIDMATS_STATFIFO 0x24
-#define CSI_CSIDMASA_FB1 0x28
-#define CSI_CSIDMASA_FB2 0x2C
-#define CSI_CSIFBUF_PARA 0x30
-#define CSI_CSIIMAG_PARA 0x34
-
-#define CSI_CSICR18 0x48
-#define CSI_CSICR19 0x4c
-
-#define NUM_FORMATS ARRAY_SIZE(formats)
-#define MX6SX_MAX_SENSORS 1
-
-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;
-};
-
-/*
- * Basic structures
- */
-struct mx6s_fmt {
- char name[32];
- u32 fourcc; /* v4l2 format id */
- u32 pixelformat;
- u32 mbus_code;
- int bpp;
-};
-
-static struct mx6s_fmt formats[] = {
- {
- .name = "UYVY-16",
- .fourcc = V4L2_PIX_FMT_UYVY,
- .pixelformat = V4L2_PIX_FMT_UYVY,
- .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8,
- .bpp = 2,
- }, {
- .name = "YUYV-16",
- .fourcc = V4L2_PIX_FMT_YUYV,
- .pixelformat = V4L2_PIX_FMT_YUYV,
- .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
- .bpp = 2,
- }, {
- .name = "YUV32 (X-Y-U-V)",
- .fourcc = V4L2_PIX_FMT_YUV32,
- .pixelformat = V4L2_PIX_FMT_YUV32,
- .mbus_code = MEDIA_BUS_FMT_AYUV8_1X32,
- .bpp = 4,
- }, {
- .name = "RAWRGB8 (SBGGR8)",
- .fourcc = V4L2_PIX_FMT_SBGGR8,
- .pixelformat = V4L2_PIX_FMT_SBGGR8,
- .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8,
- .bpp = 1,
- }
-};
-
-struct mx6s_buf_internal {
- struct list_head queue;
- int bufnum;
- bool discard;
-};
-
-/* buffer for one video frame */
-struct mx6s_buffer {
- /* common v4l buffer stuff -- must be first */
- struct vb2_v4l2_buffer vb;
- struct mx6s_buf_internal internal;
-};
-
-struct mx6s_csi_mux {
- struct regmap *gpr;
- u8 req_gpr;
- u8 req_bit;
-};
-
-struct mx6s_csi_dev {
- struct device *dev;
- struct video_device *vdev;
- struct v4l2_subdev *sd;
- struct v4l2_device v4l2_dev;
-
- struct vb2_queue vb2_vidq;
- struct v4l2_ctrl_handler ctrl_handler;
-
- struct mutex lock;
- spinlock_t slock;
-
- /* clock */
- struct clk *clk_disp_axi;
- struct clk *clk_disp_dcic;
- struct clk *clk_csi_mclk;
-
- void __iomem *regbase;
- int irq;
-
- u32 type;
- u32 bytesperline;
- v4l2_std_id std;
- struct mx6s_fmt *fmt;
- struct v4l2_pix_format pix;
- u32 mbus_code;
-
- unsigned int frame_count;
-
- struct list_head capture;
- struct list_head active_bufs;
- struct list_head discard;
-
- void *discard_buffer;
- dma_addr_t discard_buffer_dma;
- size_t discard_size;
- struct mx6s_buf_internal buf_discard[2];
-
- struct v4l2_async_subdev asd;
- struct v4l2_async_notifier subdev_notifier;
- struct v4l2_async_subdev *async_subdevs[2];
-
- bool csi_mux_mipi;
- const bool *rx_fifo_rst;
- struct mx6s_csi_mux csi_mux;
-};
-
-static const struct of_device_id mx6s_csi_dt_ids[];
-
-static inline int csi_read(struct mx6s_csi_dev *csi, unsigned int offset)
-{
- return __raw_readl(csi->regbase + offset);
-}
-static inline void csi_write(struct mx6s_csi_dev *csi, unsigned int value,
- unsigned int offset)
-{
- __raw_writel(value, csi->regbase + offset);
-}
-
-static inline struct mx6s_csi_dev
- *notifier_to_mx6s_dev(struct v4l2_async_notifier *n)
-{
- return container_of(n, struct mx6s_csi_dev, subdev_notifier);
-}
-
-struct mx6s_fmt *format_by_fourcc(int fourcc)
-{
- int i;
-
- for (i = 0; i < NUM_FORMATS; i++) {
- if (formats[i].pixelformat == fourcc)
- return formats + i;
- }
-
- pr_err("unknown pixelformat:'%4.4s'\n", (char *)&fourcc);
- return NULL;
-}
-
-struct mx6s_fmt *format_by_mbus(u32 code)
-{
- int i;
-
- for (i = 0; i < NUM_FORMATS; i++) {
- if (formats[i].mbus_code == code)
- return formats + i;
- }
-
- pr_err("unknown mbus:0x%x\n", code);
- return NULL;
-}
-
-static struct mx6s_buffer *mx6s_ibuf_to_buf(struct mx6s_buf_internal *int_buf)
-{
- return container_of(int_buf, struct mx6s_buffer, internal);
-}
-
-void csi_clk_enable(struct mx6s_csi_dev *csi_dev)
-{
- clk_prepare_enable(csi_dev->clk_disp_axi);
- clk_prepare_enable(csi_dev->clk_disp_dcic);
- clk_prepare_enable(csi_dev->clk_csi_mclk);
-}
-
-void csi_clk_disable(struct mx6s_csi_dev *csi_dev)
-{
- clk_disable_unprepare(csi_dev->clk_csi_mclk);
- clk_disable_unprepare(csi_dev->clk_disp_dcic);
- clk_disable_unprepare(csi_dev->clk_disp_axi);
-}
-
-static void csihw_reset(struct mx6s_csi_dev *csi_dev)
-{
- __raw_writel(__raw_readl(csi_dev->regbase + CSI_CSICR3)
- | BIT_FRMCNT_RST,
- csi_dev->regbase + CSI_CSICR3);
-
- __raw_writel(CSICR1_RESET_VAL, csi_dev->regbase + CSI_CSICR1);
- __raw_writel(CSICR2_RESET_VAL, csi_dev->regbase + CSI_CSICR2);
- __raw_writel(CSICR3_RESET_VAL, csi_dev->regbase + CSI_CSICR3);
-}
-
-static void csisw_reset(struct mx6s_csi_dev *csi_dev)
-{
- int cr1, cr3, cr18, isr;
-
- /* Disable csi */
- cr18 = csi_read(csi_dev, CSI_CSICR18);
- cr18 &= ~BIT_CSI_ENABLE;
- csi_write(csi_dev, cr18, CSI_CSICR18);
-
- /* Clear RX FIFO */
- cr1 = csi_read(csi_dev, CSI_CSICR1);
- csi_write(csi_dev, cr1 & ~BIT_FCC, CSI_CSICR1);
- cr1 = csi_read(csi_dev, CSI_CSICR1);
- csi_write(csi_dev, cr1 | BIT_CLR_RXFIFO, CSI_CSICR1);
-
- /* DMA reflash */
- cr3 = csi_read(csi_dev, CSI_CSICR3);
- cr3 |= BIT_DMA_REFLASH_RFF | BIT_FRMCNT_RST;
- csi_write(csi_dev, cr3, CSI_CSICR3);
-
- msleep(2);
-
- cr1 = csi_read(csi_dev, CSI_CSICR1);
- csi_write(csi_dev, cr1 | BIT_FCC, CSI_CSICR1);
-
- isr = csi_read(csi_dev, CSI_CSISR);
- csi_write(csi_dev, isr, CSI_CSISR);
-
- /* Ensable csi */
- cr18 |= BIT_CSI_ENABLE;
- csi_write(csi_dev, cr18, CSI_CSICR18);
-}
-
-/*!
- * csi_init_interface
- * Init csi interface
- */
-static void csi_init_interface(struct mx6s_csi_dev *csi_dev)
-{
- 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_FCC;
- val |= 1 << SHIFT_MCLKDIV;
- val |= BIT_MCLKEN;
- __raw_writel(val, csi_dev->regbase + CSI_CSICR1);
-
- imag_para = (640 << 16) | 960;
- __raw_writel(imag_para, csi_dev->regbase + CSI_CSIIMAG_PARA);
-
- val = BIT_DMA_REFLASH_RFF;
- __raw_writel(val, csi_dev->regbase + CSI_CSICR3);
-}
-
-static void csi_enable_int(struct mx6s_csi_dev *csi_dev, int arg)
-{
- unsigned long cr1 = __raw_readl(csi_dev->regbase + CSI_CSICR1);
-
- cr1 |= BIT_SOF_INTEN;
- cr1 |= BIT_RFF_OR_INT;
- if (arg == 1) {
- /* still capture needs DMA intterrupt */
- cr1 |= BIT_FB1_DMA_DONE_INTEN;
- cr1 |= BIT_FB2_DMA_DONE_INTEN;
- }
- __raw_writel(cr1, csi_dev->regbase + CSI_CSICR1);
-}
-
-static void csi_disable_int(struct mx6s_csi_dev *csi_dev)
-{
- unsigned long cr1 = __raw_readl(csi_dev->regbase + CSI_CSICR1);
-
- cr1 &= ~BIT_SOF_INTEN;
- cr1 &= ~BIT_RFF_OR_INT;
- cr1 &= ~BIT_FB1_DMA_DONE_INTEN;
- cr1 &= ~BIT_FB2_DMA_DONE_INTEN;
- __raw_writel(cr1, csi_dev->regbase + CSI_CSICR1);
-}
-
-static void csi_enable(struct mx6s_csi_dev *csi_dev, int arg)
-{
- unsigned long cr = __raw_readl(csi_dev->regbase + CSI_CSICR18);
-
- if (arg == 1)
- cr |= BIT_CSI_ENABLE;
- else
- cr &= ~BIT_CSI_ENABLE;
- __raw_writel(cr, csi_dev->regbase + CSI_CSICR18);
-}
-
-static void csi_buf_stride_set(struct mx6s_csi_dev *csi_dev, u32 stride)
-{
- __raw_writel(stride, csi_dev->regbase + CSI_CSIFBUF_PARA);
-}
-
-static void csi_deinterlace_enable(struct mx6s_csi_dev *csi_dev, bool enable)
-{
- unsigned long cr18 = __raw_readl(csi_dev->regbase + CSI_CSICR18);
-
- if (enable == true)
- cr18 |= BIT_DEINTERLACE_EN;
- else
- cr18 &= ~BIT_DEINTERLACE_EN;
-
- __raw_writel(cr18, csi_dev->regbase + CSI_CSICR18);
-}
-
-static void csi_deinterlace_mode(struct mx6s_csi_dev *csi_dev, int mode)
-{
- unsigned long cr18 = __raw_readl(csi_dev->regbase + CSI_CSICR18);
-
- if (mode == V4L2_STD_NTSC)
- cr18 |= BIT_NTSC_EN;
- else
- cr18 &= ~BIT_NTSC_EN;
-
- __raw_writel(cr18, csi_dev->regbase + CSI_CSICR18);
-}
-
-static void csi_tvdec_enable(struct mx6s_csi_dev *csi_dev, bool enable)
-{
- unsigned long cr18 = __raw_readl(csi_dev->regbase + CSI_CSICR18);
- unsigned long cr1 = __raw_readl(csi_dev->regbase + CSI_CSICR1);
-
- if (enable == true) {
- cr18 |= (BIT_TVDECODER_IN_EN |
- BIT_BASEADDR_SWITCH_EN |
- BIT_BASEADDR_SWITCH_SEL |
- BIT_BASEADDR_CHG_ERR_EN);
- cr1 |= BIT_CCIR_MODE;
- cr1 &= ~(BIT_SOF_POL | BIT_REDGE);
- } else {
- cr18 &= ~(BIT_TVDECODER_IN_EN |
- BIT_BASEADDR_SWITCH_EN |
- BIT_BASEADDR_SWITCH_SEL |
- BIT_BASEADDR_CHG_ERR_EN);
- cr1 &= ~BIT_CCIR_MODE;
- cr1 |= BIT_SOF_POL | BIT_REDGE;
- }
-
- __raw_writel(cr18, csi_dev->regbase + CSI_CSICR18);
- __raw_writel(cr1, csi_dev->regbase + CSI_CSICR1);
-}
-
-static void csi_dmareq_rff_enable(struct mx6s_csi_dev *csi_dev)
-{
- unsigned long cr3 = __raw_readl(csi_dev->regbase + CSI_CSICR3);
- unsigned long cr2 = __raw_readl(csi_dev->regbase + CSI_CSICR2);
-
- /* Burst Type of DMA Transfer from RxFIFO. INCR16 */
- cr2 |= 0xC0000000;
-
- cr3 |= BIT_DMA_REQ_EN_RFF;
- cr3 |= BIT_HRESP_ERR_EN;
- cr3 &= ~BIT_RXFF_LEVEL;
- cr3 |= 0x2 << 4;
-
- __raw_writel(cr3, csi_dev->regbase + CSI_CSICR3);
- __raw_writel(cr2, csi_dev->regbase + CSI_CSICR2);
-}
-
-static void csi_dmareq_rff_disable(struct mx6s_csi_dev *csi_dev)
-{
- unsigned long cr3 = __raw_readl(csi_dev->regbase + CSI_CSICR3);
-
- cr3 &= ~BIT_DMA_REQ_EN_RFF;
- cr3 &= ~BIT_HRESP_ERR_EN;
- __raw_writel(cr3, csi_dev->regbase + CSI_CSICR3);
-}
-
-static void csi_set_imagpara(struct mx6s_csi_dev *csi,
- int width, int height)
-{
- int imag_para = 0;
- unsigned long cr3 = __raw_readl(csi->regbase + CSI_CSICR3);
-
- imag_para = (width << 16) | height;
- __raw_writel(imag_para, csi->regbase + CSI_CSIIMAG_PARA);
-
- /* reflash the embeded DMA controller */
- __raw_writel(cr3 | BIT_DMA_REFLASH_RFF, csi->regbase + CSI_CSICR3);
-}
-
-static void csi_error_recovery(struct mx6s_csi_dev *csi_dev)
-{
- u32 cr1, cr3, cr18;
- /* software reset */
-
- /* Disable csi */
- cr18 = csi_read(csi_dev, CSI_CSICR18);
- cr18 &= ~BIT_CSI_ENABLE;
- csi_write(csi_dev, cr18, CSI_CSICR18);
-
- /* Clear RX FIFO */
- cr1 = csi_read(csi_dev, CSI_CSICR1);
- csi_write(csi_dev, cr1 & ~BIT_FCC, CSI_CSICR1);
- cr1 = csi_read(csi_dev, CSI_CSICR1);
- csi_write(csi_dev, cr1 | BIT_CLR_RXFIFO, CSI_CSICR1);
-
- cr1 = csi_read(csi_dev, CSI_CSICR1);
- csi_write(csi_dev, cr1 | BIT_FCC, CSI_CSICR1);
-
- /* DMA reflash */
- cr3 = csi_read(csi_dev, CSI_CSICR3);
- cr3 |= BIT_DMA_REFLASH_RFF;
- csi_write(csi_dev, cr3, CSI_CSICR3);
-
- /* Ensable csi */
- cr18 |= BIT_CSI_ENABLE;
- csi_write(csi_dev, cr18, CSI_CSICR18);
-}
-
-/*
- * Videobuf operations
- */
-static int mx6s_videobuf_setup(struct vb2_queue *vq,
- unsigned int *count, unsigned int *num_planes,
- unsigned int sizes[], struct device *alloc_devs[])
-{
- struct mx6s_csi_dev *csi_dev = vb2_get_drv_priv(vq);
-
- dev_dbg(csi_dev->dev, "count=%d, size=%d\n", *count, sizes[0]);
-
- alloc_devs[0] = csi_dev->dev;
-
- sizes[0] = csi_dev->pix.sizeimage;
-
- pr_debug("size=%d\n", sizes[0]);
- if (0 == *count)
- *count = 32;
- if (!*num_planes &&
- sizes[0] * *count > MAX_VIDEO_MEM * 1024 * 1024)
- *count = (MAX_VIDEO_MEM * 1024 * 1024) / sizes[0];
-
- *num_planes = 1;
-
- return 0;
-}
-
-static int mx6s_videobuf_prepare(struct vb2_buffer *vb)
-{
- struct mx6s_csi_dev *csi_dev = vb2_get_drv_priv(vb->vb2_queue);
- int ret = 0;
-
- dev_dbg(csi_dev->dev, "%s (vb=0x%p) 0x%p %lu\n", __func__,
- vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
-
-#ifdef DEBUG
- /*
- * This can be useful if you want to see if we actually fill
- * the buffer with something
- */
- if (vb2_plane_vaddr(vb, 0))
- memset((void *)vb2_plane_vaddr(vb, 0),
- 0xaa, vb2_get_plane_payload(vb, 0));
-#endif
-
- vb2_set_plane_payload(vb, 0, csi_dev->pix.sizeimage);
- if (vb2_plane_vaddr(vb, 0) &&
- vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) {
- ret = -EINVAL;
- goto out;
- }
-
- return 0;
-
-out:
- return ret;
-}
-
-static void mx6s_videobuf_queue(struct vb2_buffer *vb)
-{
- struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
- struct mx6s_csi_dev *csi_dev = vb2_get_drv_priv(vb->vb2_queue);
- struct mx6s_buffer *buf = container_of(vbuf, struct mx6s_buffer, vb);
- unsigned long flags;
-
- dev_dbg(csi_dev->dev, "%s (vb=0x%p) 0x%p %lu\n", __func__,
- vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
-
- spin_lock_irqsave(&csi_dev->slock, flags);
-
- list_add_tail(&buf->internal.queue, &csi_dev->capture);
-
- spin_unlock_irqrestore(&csi_dev->slock, flags);
-}
-
-static void mx6s_update_csi_buf(struct mx6s_csi_dev *csi_dev,
- unsigned long phys, int bufnum)
-{
- if (bufnum == 1)
- csi_write(csi_dev, phys, CSI_CSIDMASA_FB2);
- else
- csi_write(csi_dev, phys, CSI_CSIDMASA_FB1);
-}
-
-static void mx6s_csi_init(struct mx6s_csi_dev *csi_dev)
-{
- csi_clk_enable(csi_dev);
- csihw_reset(csi_dev);
- csi_init_interface(csi_dev);
- csi_dmareq_rff_disable(csi_dev);
-}
-
-static void mx6s_csi_deinit(struct mx6s_csi_dev *csi_dev)
-{
- csihw_reset(csi_dev);
- csi_init_interface(csi_dev);
- csi_dmareq_rff_disable(csi_dev);
- csi_clk_disable(csi_dev);
-}
-
-static int mx6s_csi_enable(struct mx6s_csi_dev *csi_dev)
-{
- struct v4l2_pix_format *pix = &csi_dev->pix;
- unsigned long flags;
- unsigned long val;
- int timeout, timeout2;
-
- csisw_reset(csi_dev);
-
- if (pix->field == V4L2_FIELD_INTERLACED)
- csi_tvdec_enable(csi_dev, true);
-
- /* For mipi csi input only */
- if (csi_dev->csi_mux_mipi == true) {
- csi_dmareq_rff_enable(csi_dev);
- csi_enable_int(csi_dev, 1);
- csi_enable(csi_dev, 1);
- return 0;
- }
-
- local_irq_save(flags);
- for (timeout = 10000000; timeout > 0; timeout--) {
- if (csi_read(csi_dev, CSI_CSISR) & BIT_SOF_INT) {
- val = csi_read(csi_dev, CSI_CSICR3);
- csi_write(csi_dev, val | BIT_DMA_REFLASH_RFF,
- CSI_CSICR3);
- /* Wait DMA reflash done */
- for (timeout2 = 1000000; timeout2 > 0; timeout2--) {
- if (csi_read(csi_dev, CSI_CSICR3) &
- BIT_DMA_REFLASH_RFF)
- cpu_relax();
- else
- break;
- }
- if (timeout2 <= 0) {
- pr_err("timeout when wait for reflash done.\n");
- local_irq_restore(flags);
- return -ETIME;
- }
- /* For imx6sl csi, DMA FIFO will auto start when sensor ready to work,
- * so DMA should enable right after FIFO reset, otherwise dma will lost data
- * and image will split.
- */
- csi_dmareq_rff_enable(csi_dev);
- csi_enable_int(csi_dev, 1);
- csi_enable(csi_dev, 1);
- break;
- } else
- cpu_relax();
- }
- if (timeout <= 0) {
- pr_err("timeout when wait for SOF\n");
- local_irq_restore(flags);
- return -ETIME;
- }
- local_irq_restore(flags);
-
- return 0;
-}
-
-static void mx6s_csi_disable(struct mx6s_csi_dev *csi_dev)
-{
- struct v4l2_pix_format *pix = &csi_dev->pix;
-
- csi_dmareq_rff_disable(csi_dev);
- csi_disable_int(csi_dev);
-
- /* set CSI_CSIDMASA_FB1 and CSI_CSIDMASA_FB2 to default value */
- csi_write(csi_dev, 0, CSI_CSIDMASA_FB1);
- csi_write(csi_dev, 0, CSI_CSIDMASA_FB2);
-
- csi_buf_stride_set(csi_dev, 0);
-
- if (pix->field == V4L2_FIELD_INTERLACED) {
- csi_deinterlace_enable(csi_dev, false);
- csi_tvdec_enable(csi_dev, false);
- }
-
- csi_enable(csi_dev, 0);
-}
-
-static int mx6s_configure_csi(struct mx6s_csi_dev *csi_dev)
-{
- struct v4l2_pix_format *pix = &csi_dev->pix;
- u32 cr1, cr18;
- u32 width;
-
- if (pix->field == V4L2_FIELD_INTERLACED) {
- csi_deinterlace_enable(csi_dev, true);
- csi_buf_stride_set(csi_dev, csi_dev->pix.width);
- csi_deinterlace_mode(csi_dev, csi_dev->std);
- } else {
- csi_deinterlace_enable(csi_dev, false);
- csi_buf_stride_set(csi_dev, 0);
- }
-
- switch (csi_dev->fmt->pixelformat) {
- case V4L2_PIX_FMT_YUV32:
- case V4L2_PIX_FMT_SBGGR8:
- width = pix->width;
- break;
- case V4L2_PIX_FMT_UYVY:
- case V4L2_PIX_FMT_YUYV:
- if (csi_dev->csi_mux_mipi == true)
- width = pix->width;
- else
- /* For parallel 8-bit sensor input */
- width = pix->width * 2;
- break;
- default:
- pr_debug(" case not supported\n");
- return -EINVAL;
- }
- csi_set_imagpara(csi_dev, width, pix->height);
-
- if (csi_dev->csi_mux_mipi == true) {
- cr1 = csi_read(csi_dev, CSI_CSICR1);
- cr1 &= ~BIT_GCLK_MODE;
- csi_write(csi_dev, cr1, CSI_CSICR1);
-
- cr18 = csi_read(csi_dev, CSI_CSICR18);
- cr18 &= BIT_MIPI_DATA_FORMAT_MASK;
- cr18 |= BIT_DATA_FROM_MIPI;
-
- switch (csi_dev->fmt->pixelformat) {
- case V4L2_PIX_FMT_UYVY:
- case V4L2_PIX_FMT_YUYV:
- cr18 |= BIT_MIPI_DATA_FORMAT_YUV422_8B;
- break;
- case V4L2_PIX_FMT_SBGGR8:
- cr18 |= BIT_MIPI_DATA_FORMAT_RAW8;
- break;
- default:
- pr_debug(" fmt not supported\n");
- return -EINVAL;
- }
-
- csi_write(csi_dev, cr18, CSI_CSICR18);
- }
- return 0;
-}
-
-static int mx6s_start_streaming(struct vb2_queue *vq, unsigned int count)
-{
- struct mx6s_csi_dev *csi_dev = vb2_get_drv_priv(vq);
- struct vb2_buffer *vb;
- struct mx6s_buffer *buf;
- unsigned long phys;
- unsigned long flags;
-
- if (count < 2)
- return -ENOBUFS;
-
- /*
- * I didn't manage to properly enable/disable
- * a per frame basis during running transfers,
- * thus we allocate a buffer here and use it to
- * discard frames when no buffer is available.
- * Feel free to work on this ;)
- */
- csi_dev->discard_size = csi_dev->pix.sizeimage;
- csi_dev->discard_buffer = dma_alloc_coherent(csi_dev->v4l2_dev.dev,
- PAGE_ALIGN(csi_dev->discard_size),
- &csi_dev->discard_buffer_dma,
- GFP_DMA | GFP_KERNEL);
- if (!csi_dev->discard_buffer)
- return -ENOMEM;
-
- spin_lock_irqsave(&csi_dev->slock, flags);
-
- csi_dev->buf_discard[0].discard = true;
- list_add_tail(&csi_dev->buf_discard[0].queue,
- &csi_dev->discard);
-
- csi_dev->buf_discard[1].discard = true;
- list_add_tail(&csi_dev->buf_discard[1].queue,
- &csi_dev->discard);
-
- /* csi buf 0 */
- buf = list_first_entry(&csi_dev->capture, struct mx6s_buffer,
- internal.queue);
- buf->internal.bufnum = 0;
- vb = &buf->vb.vb2_buf;
- vb->state = VB2_BUF_STATE_ACTIVE;
-
- phys = vb2_dma_contig_plane_dma_addr(vb, 0);
-
- mx6s_update_csi_buf(csi_dev, phys, buf->internal.bufnum);
- list_move_tail(csi_dev->capture.next, &csi_dev->active_bufs);
-
- /* csi buf 1 */
- buf = list_first_entry(&csi_dev->capture, struct mx6s_buffer,
- internal.queue);
- buf->internal.bufnum = 1;
- vb = &buf->vb.vb2_buf;
- vb->state = VB2_BUF_STATE_ACTIVE;
-
- phys = vb2_dma_contig_plane_dma_addr(vb, 0);
- mx6s_update_csi_buf(csi_dev, phys, buf->internal.bufnum);
- list_move_tail(csi_dev->capture.next, &csi_dev->active_bufs);
-
- spin_unlock_irqrestore(&csi_dev->slock, flags);
-
- return mx6s_csi_enable(csi_dev);
-}
-
-static void mx6s_stop_streaming(struct vb2_queue *vq)
-{
- struct mx6s_csi_dev *csi_dev = vb2_get_drv_priv(vq);
- unsigned long flags;
- struct mx6s_buffer *buf, *tmp;
- void *b;
-
- mx6s_csi_disable(csi_dev);
-
- spin_lock_irqsave(&csi_dev->slock, flags);
-
- list_for_each_entry_safe(buf, tmp,
- &csi_dev->active_bufs, internal.queue) {
- list_del_init(&buf->internal.queue);
- if (buf->vb.vb2_buf.state == VB2_BUF_STATE_ACTIVE)
- vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
- }
-
- list_for_each_entry_safe(buf, tmp,
- &csi_dev->capture, internal.queue) {
- list_del_init(&buf->internal.queue);
- if (buf->vb.vb2_buf.state == VB2_BUF_STATE_ACTIVE)
- vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
- }
-
- INIT_LIST_HEAD(&csi_dev->capture);
- INIT_LIST_HEAD(&csi_dev->active_bufs);
- INIT_LIST_HEAD(&csi_dev->discard);
-
- b = csi_dev->discard_buffer;
- csi_dev->discard_buffer = NULL;
-
- spin_unlock_irqrestore(&csi_dev->slock, flags);
-
- dma_free_coherent(csi_dev->v4l2_dev.dev,
- csi_dev->discard_size, b,
- csi_dev->discard_buffer_dma);
-}
-
-static struct vb2_ops mx6s_videobuf_ops = {
- .queue_setup = mx6s_videobuf_setup,
- .buf_prepare = mx6s_videobuf_prepare,
- .buf_queue = mx6s_videobuf_queue,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
- .start_streaming = mx6s_start_streaming,
- .stop_streaming = mx6s_stop_streaming,
-};
-
-static void mx6s_csi_frame_done(struct mx6s_csi_dev *csi_dev,
- int bufnum, bool err)
-{
- struct mx6s_buf_internal *ibuf;
- struct mx6s_buffer *buf;
- struct vb2_buffer *vb;
- unsigned long phys;
-
- ibuf = list_first_entry(&csi_dev->active_bufs, struct mx6s_buf_internal,
- queue);
-
- if (ibuf->discard) {
- /*
- * Discard buffer must not be returned to user space.
- * Just return it to the discard queue.
- */
- list_move_tail(csi_dev->active_bufs.next, &csi_dev->discard);
- } else {
- buf = mx6s_ibuf_to_buf(ibuf);
-
- vb = &buf->vb.vb2_buf;
- phys = vb2_dma_contig_plane_dma_addr(vb, 0);
- if (bufnum == 1) {
- if (csi_read(csi_dev, CSI_CSIDMASA_FB2) != phys) {
- dev_err(csi_dev->dev, "%lx != %x\n", phys,
- csi_read(csi_dev, CSI_CSIDMASA_FB2));
- }
- } else {
- if (csi_read(csi_dev, CSI_CSIDMASA_FB1) != phys) {
- dev_err(csi_dev->dev, "%lx != %x\n", phys,
- csi_read(csi_dev, CSI_CSIDMASA_FB1));
- }
- }
- dev_dbg(csi_dev->dev, "%s (vb=0x%p) 0x%p %lu\n", __func__, vb,
- vb2_plane_vaddr(vb, 0),
- vb2_get_plane_payload(vb, 0));
-
- list_del_init(&buf->internal.queue);
- vb->timestamp =ktime_get_ns();
- to_vb2_v4l2_buffer(vb)->sequence = csi_dev->frame_count;
- if (err)
- vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
- else
- vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
- }
-
- csi_dev->frame_count++;
-
- /* Config discard buffer to active_bufs */
- if (list_empty(&csi_dev->capture)) {
- if (list_empty(&csi_dev->discard)) {
- dev_warn(csi_dev->dev,
- "%s: trying to access empty discard list\n",
- __func__);
- return;
- }
-
- ibuf = list_first_entry(&csi_dev->discard,
- struct mx6s_buf_internal, queue);
- ibuf->bufnum = bufnum;
-
- list_move_tail(csi_dev->discard.next, &csi_dev->active_bufs);
-
- mx6s_update_csi_buf(csi_dev,
- csi_dev->discard_buffer_dma, bufnum);
- return;
- }
-
- buf = list_first_entry(&csi_dev->capture, struct mx6s_buffer,
- internal.queue);
-
- buf->internal.bufnum = bufnum;
-
- list_move_tail(csi_dev->capture.next, &csi_dev->active_bufs);
-
- vb = &buf->vb.vb2_buf;
- vb->state = VB2_BUF_STATE_ACTIVE;
-
- phys = vb2_dma_contig_plane_dma_addr(vb, 0);
- mx6s_update_csi_buf(csi_dev, phys, bufnum);
-}
-
-static irqreturn_t mx6s_csi_irq_handler(int irq, void *data)
-{
- struct mx6s_csi_dev *csi_dev = data;
- unsigned long status;
- u32 cr3, cr18;
-
- spin_lock(&csi_dev->slock);
-
- status = csi_read(csi_dev, CSI_CSISR);
- csi_write(csi_dev, status, CSI_CSISR);
-
- if (list_empty(&csi_dev->active_bufs)) {
- dev_warn(csi_dev->dev,
- "%s: called while active list is empty\n",
- __func__);
-
- spin_unlock(&csi_dev->slock);
- return IRQ_HANDLED;
- }
-
- if (status & BIT_RFF_OR_INT) {
- dev_warn(csi_dev->dev, "%s Rx fifo overflow\n", __func__);
- if (*csi_dev->rx_fifo_rst)
- csi_error_recovery(csi_dev);
- }
-
- if (status & BIT_HRESP_ERR_INT) {
- dev_warn(csi_dev->dev, "%s Hresponse error detected\n",
- __func__);
- csi_error_recovery(csi_dev);
- }
-
- if (status & BIT_ADDR_CH_ERR_INT) {
- /* Disable csi */
- cr18 = csi_read(csi_dev, CSI_CSICR18);
- cr18 &= ~BIT_CSI_ENABLE;
- csi_write(csi_dev, cr18, CSI_CSICR18);
-
- /* DMA reflash */
- cr3 = csi_read(csi_dev, CSI_CSICR3);
- cr3 |= BIT_DMA_REFLASH_RFF;
- csi_write(csi_dev, cr3, CSI_CSICR3);
-
- /* Ensable csi */
- cr18 |= BIT_CSI_ENABLE;
- csi_write(csi_dev, cr18, CSI_CSICR18);
-
- pr_debug("base address switching Change Err.\n");
- }
-
- if ((status & BIT_DMA_TSF_DONE_FB1) &&
- (status & BIT_DMA_TSF_DONE_FB2)) {
- /* For both FB1 and FB2 interrupter bits set case,
- * CSI DMA is work in one of FB1 and FB2 buffer,
- * but software can not know the state.
- * Skip it to avoid base address updated
- * when csi work in field0 and field1 will write to
- * new base address.
- * PDM TKT230775 */
- pr_debug("Skip two frames\n");
- } else if (status & BIT_DMA_TSF_DONE_FB1) {
- mx6s_csi_frame_done(csi_dev, 0, false);
- } else if (status & BIT_DMA_TSF_DONE_FB2) {
- mx6s_csi_frame_done(csi_dev, 1, false);
- }
-
- spin_unlock(&csi_dev->slock);
-
- return IRQ_HANDLED;
-}
-
-/*
- * File operations for the device
- */
-static int mx6s_csi_open(struct file *file)
-{
- struct mx6s_csi_dev *csi_dev = video_drvdata(file);
- struct v4l2_subdev *sd = csi_dev->sd;
- struct vb2_queue *q = &csi_dev->vb2_vidq;
- int ret = 0;
-
- file->private_data = csi_dev;
-
- if (mutex_lock_interruptible(&csi_dev->lock))
- return -ERESTARTSYS;
-
- q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- q->io_modes = VB2_MMAP | VB2_USERPTR;
- q->drv_priv = csi_dev;
- q->ops = &mx6s_videobuf_ops;
- q->mem_ops = &vb2_dma_contig_memops;
- q->buf_struct_size = sizeof(struct mx6s_buffer);
- q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
- q->lock = &csi_dev->lock;
-
- ret = vb2_queue_init(q);
- if (ret < 0)
- goto unlock;
-
- pm_runtime_get_sync(csi_dev->dev);
-
- request_bus_freq(BUS_FREQ_HIGH);
-
- v4l2_subdev_call(sd, core, s_power, 1);
- mx6s_csi_init(csi_dev);
-
- mutex_unlock(&csi_dev->lock);
-
- return ret;
-unlock:
- mutex_unlock(&csi_dev->lock);
- return ret;
-}
-
-static int mx6s_csi_close(struct file *file)
-{
- struct mx6s_csi_dev *csi_dev = video_drvdata(file);
- struct v4l2_subdev *sd = csi_dev->sd;
-
- mutex_lock(&csi_dev->lock);
-
- vb2_queue_release(&csi_dev->vb2_vidq);
-
- mx6s_csi_deinit(csi_dev);
- v4l2_subdev_call(sd, core, s_power, 0);
-
- mutex_unlock(&csi_dev->lock);
-
- file->private_data = NULL;
-
- release_bus_freq(BUS_FREQ_HIGH);
-
- pm_runtime_put_sync_suspend(csi_dev->dev);
- return 0;
-}
-
-static ssize_t mx6s_csi_read(struct file *file, char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct mx6s_csi_dev *csi_dev = video_drvdata(file);
- int ret;
-
- dev_dbg(csi_dev->dev, "read called, buf %p\n", buf);
-
- mutex_lock(&csi_dev->lock);
- ret = vb2_read(&csi_dev->vb2_vidq, buf, count, ppos,
- file->f_flags & O_NONBLOCK);
- mutex_unlock(&csi_dev->lock);
- return ret;
-}
-
-static int mx6s_csi_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct mx6s_csi_dev *csi_dev = video_drvdata(file);
- int ret;
-
- if (mutex_lock_interruptible(&csi_dev->lock))
- return -ERESTARTSYS;
- ret = vb2_mmap(&csi_dev->vb2_vidq, vma);
- mutex_unlock(&csi_dev->lock);
-
- pr_debug("vma start=0x%08lx, size=%ld, ret=%d\n",
- (unsigned long)vma->vm_start,
- (unsigned long)vma->vm_end-(unsigned long)vma->vm_start,
- ret);
-
- return ret;
-}
-
-static struct v4l2_file_operations mx6s_csi_fops = {
- .owner = THIS_MODULE,
- .open = mx6s_csi_open,
- .release = mx6s_csi_close,
- .read = mx6s_csi_read,
- .poll = vb2_fop_poll,
- .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
- .mmap = mx6s_csi_mmap,
-};
-
-/*
- * Video node IOCTLs
- */
-static int mx6s_vidioc_enum_input(struct file *file, void *priv,
- struct v4l2_input *inp)
-{
- if (inp->index != 0)
- return -EINVAL;
-
- /* default is camera */
- inp->type = V4L2_INPUT_TYPE_CAMERA;
- strcpy(inp->name, "Camera");
-
- return 0;
-}
-
-static int mx6s_vidioc_g_input(struct file *file, void *priv, unsigned int *i)
-{
- *i = 0;
-
- return 0;
-}
-
-static int mx6s_vidioc_s_input(struct file *file, void *priv, unsigned int i)
-{
- if (i > 0)
- return -EINVAL;
-
- return 0;
-}
-
-static int mx6s_vidioc_querystd(struct file *file, void *priv, v4l2_std_id *a)
-{
- struct mx6s_csi_dev *csi_dev = video_drvdata(file);
- struct v4l2_subdev *sd = csi_dev->sd;
-
- return v4l2_subdev_call(sd, video, querystd, a);
-}
-
-static int mx6s_vidioc_s_std(struct file *file, void *priv, v4l2_std_id a)
-{
- struct mx6s_csi_dev *csi_dev = video_drvdata(file);
- struct v4l2_subdev *sd = csi_dev->sd;
-
- return v4l2_subdev_call(sd, video, s_std, a);
-}
-
-static int mx6s_vidioc_g_std(struct file *file, void *priv, v4l2_std_id *a)
-{
- struct mx6s_csi_dev *csi_dev = video_drvdata(file);
- struct v4l2_subdev *sd = csi_dev->sd;
-
- return v4l2_subdev_call(sd, video, g_std, a);
-}
-
-static int mx6s_vidioc_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *p)
-{
- struct mx6s_csi_dev *csi_dev = video_drvdata(file);
-
- WARN_ON(priv != file->private_data);
-
- return vb2_reqbufs(&csi_dev->vb2_vidq, p);
-}
-
-static int mx6s_vidioc_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *p)
-{
- struct mx6s_csi_dev *csi_dev = video_drvdata(file);
- int ret;
-
- WARN_ON(priv != file->private_data);
-
- ret = vb2_querybuf(&csi_dev->vb2_vidq, p);
-
- if (!ret) {
- /* return physical address */
- struct vb2_buffer *vb = csi_dev->vb2_vidq.bufs[p->index];
- if (p->flags & V4L2_BUF_FLAG_MAPPED)
- p->m.offset = vb2_dma_contig_plane_dma_addr(vb, 0);
- }
- return ret;
-}
-
-static int mx6s_vidioc_qbuf(struct file *file, void *priv,
- struct v4l2_buffer *p)
-{
- struct mx6s_csi_dev *csi_dev = video_drvdata(file);
-
- WARN_ON(priv != file->private_data);
-
- return vb2_qbuf(&csi_dev->vb2_vidq, p);
-}
-
-static int mx6s_vidioc_dqbuf(struct file *file, void *priv,
- struct v4l2_buffer *p)
-{
- struct mx6s_csi_dev *csi_dev = video_drvdata(file);
-
- WARN_ON(priv != file->private_data);
-
- return vb2_dqbuf(&csi_dev->vb2_vidq, p, file->f_flags & O_NONBLOCK);
-}
-
-static int mx6s_vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
-{
- struct mx6s_csi_dev *csi_dev = video_drvdata(file);
- struct v4l2_subdev *sd = csi_dev->sd;
- struct v4l2_subdev_mbus_code_enum code = {
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- .index = f->index,
- };
- struct mx6s_fmt *fmt;
- int ret;
-
- WARN_ON(priv != file->private_data);
-
- ret = v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code);
- if (ret < 0) {
- /* no more formats */
- dev_dbg(csi_dev->dev, "No more fmt\n");
- return -EINVAL;
- }
-
- fmt = format_by_mbus(code.code);
- if (!fmt) {
- dev_err(csi_dev->dev, "mbus (0x%08x) invalid.\n", code.code);
- return -EINVAL;
- }
-
- strlcpy(f->description, fmt->name, sizeof(f->description));
- f->pixelformat = fmt->pixelformat;
-
- return 0;
-}
-
-static int mx6s_vidioc_try_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct mx6s_csi_dev *csi_dev = video_drvdata(file);
- struct v4l2_subdev *sd = csi_dev->sd;
- struct v4l2_pix_format *pix = &f->fmt.pix;
- struct v4l2_subdev_format format = {
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- };
- struct mx6s_fmt *fmt;
- int ret;
-
- fmt = format_by_fourcc(f->fmt.pix.pixelformat);
- if (!fmt) {
- dev_err(csi_dev->dev, "Fourcc format (0x%08x) invalid.",
- f->fmt.pix.pixelformat);
- return -EINVAL;
- }
-
- if (f->fmt.pix.width == 0 || f->fmt.pix.height == 0) {
- dev_err(csi_dev->dev, "width %d, height %d is too small.\n",
- f->fmt.pix.width, f->fmt.pix.height);
- return -EINVAL;
- }
-
- v4l2_fill_mbus_format(&format.format, pix, fmt->mbus_code);
- ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &format);
- v4l2_fill_pix_format(pix, &format.format);
-
- if (pix->field != V4L2_FIELD_INTERLACED)
- pix->field = V4L2_FIELD_NONE;
-
- pix->sizeimage = fmt->bpp * pix->height * pix->width;
- pix->bytesperline = fmt->bpp * pix->width;
-
- return ret;
-}
-
-/*
- * The real work of figuring out a workable format.
- */
-
-static int mx6s_vidioc_s_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct mx6s_csi_dev *csi_dev = video_drvdata(file);
- int ret;
-
- ret = mx6s_vidioc_try_fmt_vid_cap(file, csi_dev, f);
- if (ret < 0)
- return ret;
-
- csi_dev->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
- csi_dev->mbus_code = csi_dev->fmt->mbus_code;
- csi_dev->pix.width = f->fmt.pix.width;
- csi_dev->pix.height = f->fmt.pix.height;
- csi_dev->pix.sizeimage = f->fmt.pix.sizeimage;
- csi_dev->pix.field = f->fmt.pix.field;
- csi_dev->type = f->type;
- dev_dbg(csi_dev->dev, "set to pixelformat '%4.6s'\n",
- (char *)&csi_dev->fmt->name);
-
- /* Config csi */
- mx6s_configure_csi(csi_dev);
-
- return 0;
-}
-
-static int mx6s_vidioc_g_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct mx6s_csi_dev *csi_dev = video_drvdata(file);
-
- WARN_ON(priv != file->private_data);
-
- f->fmt.pix = csi_dev->pix;
-
- return 0;
-}
-
-static int mx6s_vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- struct mx6s_csi_dev *csi_dev = video_drvdata(file);
-
- WARN_ON(priv != file->private_data);
-
- /* cap->name is set by the friendly caller:-> */
- strlcpy(cap->driver, MX6S_CAM_DRV_NAME, sizeof(cap->driver));
- strlcpy(cap->card, MX6S_CAM_DRIVER_DESCRIPTION, sizeof(cap->card));
- snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
- dev_name(csi_dev->dev));
-
- cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
- cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
- return 0;
-}
-
-static int mx6s_vidioc_streamon(struct file *file, void *priv,
- enum v4l2_buf_type i)
-{
- struct mx6s_csi_dev *csi_dev = video_drvdata(file);
- struct v4l2_subdev *sd = csi_dev->sd;
- int ret;
-
- WARN_ON(priv != file->private_data);
-
- if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- ret = vb2_streamon(&csi_dev->vb2_vidq, i);
- if (!ret)
- v4l2_subdev_call(sd, video, s_stream, 1);
-
- return ret;
-}
-
-static int mx6s_vidioc_streamoff(struct file *file, void *priv,
- enum v4l2_buf_type i)
-{
- struct mx6s_csi_dev *csi_dev = video_drvdata(file);
- struct v4l2_subdev *sd = csi_dev->sd;
-
- WARN_ON(priv != file->private_data);
-
- if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- /*
- * This calls buf_release from host driver's videobuf_queue_ops for all
- * remaining buffers. When the last buffer is freed, stop capture
- */
- vb2_streamoff(&csi_dev->vb2_vidq, i);
-
- v4l2_subdev_call(sd, video, s_stream, 0);
-
- return 0;
-}
-
-static int mx6s_vidioc_cropcap(struct file *file, void *fh,
- struct v4l2_cropcap *a)
-{
- struct mx6s_csi_dev *csi_dev = video_drvdata(file);
-
- if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- dev_dbg(csi_dev->dev, "VIDIOC_CROPCAP not implemented\n");
-
- return 0;
-}
-
-static int mx6s_vidioc_g_crop(struct file *file, void *priv,
- struct v4l2_crop *a)
-{
- struct mx6s_csi_dev *csi_dev = video_drvdata(file);
-
- if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- dev_dbg(csi_dev->dev, "VIDIOC_G_CROP not implemented\n");
-
- return 0;
-}
-
-static int mx6s_vidioc_s_crop(struct file *file, void *priv,
- const struct v4l2_crop *a)
-{
- struct mx6s_csi_dev *csi_dev = video_drvdata(file);
-
- if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- dev_dbg(csi_dev->dev, "VIDIOC_S_CROP not implemented\n");
-
- return 0;
-}
-
-static int mx6s_vidioc_g_parm(struct file *file, void *priv,
- struct v4l2_streamparm *a)
-{
- struct mx6s_csi_dev *csi_dev = video_drvdata(file);
- struct v4l2_subdev *sd = csi_dev->sd;
-
- return v4l2_subdev_call(sd, video, g_parm, a);
-}
-
-static int mx6s_vidioc_s_parm(struct file *file, void *priv,
- struct v4l2_streamparm *a)
-{
- struct mx6s_csi_dev *csi_dev = video_drvdata(file);
- struct v4l2_subdev *sd = csi_dev->sd;
-
- return v4l2_subdev_call(sd, video, s_parm, a);
-}
-
-static int mx6s_vidioc_enum_framesizes(struct file *file, void *priv,
- struct v4l2_frmsizeenum *fsize)
-{
- struct mx6s_csi_dev *csi_dev = video_drvdata(file);
- struct v4l2_subdev *sd = csi_dev->sd;
- struct mx6s_fmt *fmt;
- struct v4l2_subdev_frame_size_enum fse = {
- .index = fsize->index,
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- };
- int ret;
-
- fmt = format_by_fourcc(fsize->pixel_format);
- if (fmt->pixelformat != fsize->pixel_format)
- return -EINVAL;
- fse.code = fmt->mbus_code;
-
- ret = v4l2_subdev_call(sd, pad, enum_frame_size, NULL, &fse);
- if (ret)
- return ret;
-
- if (fse.min_width == fse.max_width &&
- fse.min_height == fse.max_height) {
- fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
- fsize->discrete.width = fse.min_width;
- fsize->discrete.height = fse.min_height;
- return 0;
- }
-
- fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
- fsize->stepwise.min_width = fse.min_width;
- fsize->stepwise.max_width = fse.max_width;
- fsize->stepwise.min_height = fse.min_height;
- fsize->stepwise.max_height = fse.max_height;
- fsize->stepwise.step_width = 1;
- fsize->stepwise.step_height = 1;
-
- return 0;
-}
-
-static int mx6s_vidioc_enum_frameintervals(struct file *file, void *priv,
- struct v4l2_frmivalenum *interval)
-{
- struct mx6s_csi_dev *csi_dev = video_drvdata(file);
- struct v4l2_subdev *sd = csi_dev->sd;
- struct mx6s_fmt *fmt;
- struct v4l2_subdev_frame_interval_enum fie = {
- .index = interval->index,
- .width = interval->width,
- .height = interval->height,
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- };
- int ret;
-
- fmt = format_by_fourcc(interval->pixel_format);
- if (fmt->pixelformat != interval->pixel_format)
- return -EINVAL;
- fie.code = fmt->mbus_code;
-
- ret = v4l2_subdev_call(sd, pad, enum_frame_interval, NULL, &fie);
- if (ret)
- return ret;
- interval->type = V4L2_FRMIVAL_TYPE_DISCRETE;
- interval->discrete = fie.interval;
- return 0;
-}
-
-static const struct v4l2_ioctl_ops mx6s_csi_ioctl_ops = {
- .vidioc_querycap = mx6s_vidioc_querycap,
- .vidioc_enum_fmt_vid_cap = mx6s_vidioc_enum_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = mx6s_vidioc_try_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = mx6s_vidioc_g_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = mx6s_vidioc_s_fmt_vid_cap,
- .vidioc_cropcap = mx6s_vidioc_cropcap,
- .vidioc_s_crop = mx6s_vidioc_s_crop,
- .vidioc_g_crop = mx6s_vidioc_g_crop,
- .vidioc_reqbufs = mx6s_vidioc_reqbufs,
- .vidioc_querybuf = mx6s_vidioc_querybuf,
- .vidioc_qbuf = mx6s_vidioc_qbuf,
- .vidioc_dqbuf = mx6s_vidioc_dqbuf,
- .vidioc_g_std = mx6s_vidioc_g_std,
- .vidioc_s_std = mx6s_vidioc_s_std,
- .vidioc_querystd = mx6s_vidioc_querystd,
- .vidioc_enum_input = mx6s_vidioc_enum_input,
- .vidioc_g_input = mx6s_vidioc_g_input,
- .vidioc_s_input = mx6s_vidioc_s_input,
- .vidioc_streamon = mx6s_vidioc_streamon,
- .vidioc_streamoff = mx6s_vidioc_streamoff,
- .vidioc_g_parm = mx6s_vidioc_g_parm,
- .vidioc_s_parm = mx6s_vidioc_s_parm,
- .vidioc_enum_framesizes = mx6s_vidioc_enum_framesizes,
- .vidioc_enum_frameintervals = mx6s_vidioc_enum_frameintervals,
-};
-
-static int subdev_notifier_bound(struct v4l2_async_notifier *notifier,
- struct v4l2_subdev *subdev,
- struct v4l2_async_subdev *asd)
-{
- struct mx6s_csi_dev *csi_dev = notifier_to_mx6s_dev(notifier);
-
- /* Find platform data for this sensor subdev */
- if (csi_dev->asd.match.of.node == subdev->dev->of_node)
- csi_dev->sd = subdev;
-
- if (subdev == NULL)
- return -EINVAL;
-
- v4l2_info(&csi_dev->v4l2_dev, "Registered sensor subdevice: %s\n",
- subdev->name);
-
- return 0;
-}
-
-static int mx6s_csi_mux_sel(struct mx6s_csi_dev *csi_dev)
-{
- struct device_node *np = csi_dev->dev->of_node;
- struct device_node *node;
- phandle phandle;
- u32 out_val[3];
- int ret;
-
- ret = of_property_read_u32_array(np, "csi-mux-mipi", out_val, 3);
- if (ret) {
- dev_dbg(csi_dev->dev, "no csi-mux-mipi property found\n");
- csi_dev->csi_mux_mipi = false;
- } else {
- phandle = *out_val;
-
- node = of_find_node_by_phandle(phandle);
- if (!node) {
- dev_dbg(csi_dev->dev, "not find gpr node by phandle\n");
- ret = PTR_ERR(node);
- }
- csi_dev->csi_mux.gpr = syscon_node_to_regmap(node);
- if (IS_ERR(csi_dev->csi_mux.gpr)) {
- dev_err(csi_dev->dev, "failed to get gpr regmap\n");
- ret = PTR_ERR(csi_dev->csi_mux.gpr);
- }
- of_node_put(node);
- if (ret < 0)
- return ret;
-
- csi_dev->csi_mux.req_gpr = out_val[1];
- csi_dev->csi_mux.req_bit = out_val[2];
-
- regmap_update_bits(csi_dev->csi_mux.gpr, csi_dev->csi_mux.req_gpr,
- 1 << csi_dev->csi_mux.req_bit, 1 << csi_dev->csi_mux.req_bit);
-
- csi_dev->csi_mux_mipi = true;
- }
- return ret;
-}
-
-static int mx6sx_register_subdevs(struct mx6s_csi_dev *csi_dev)
-{
- struct device_node *parent = csi_dev->dev->of_node;
- struct device_node *node, *port, *rem;
- int ret;
-
- /* Attach sensors linked to csi receivers */
- for_each_available_child_of_node(parent, node) {
- if (of_node_cmp(node->name, "port"))
- continue;
-
- /* The csi node can have only port subnode. */
- port = of_get_next_child(node, NULL);
- if (!port)
- continue;
- rem = of_graph_get_remote_port_parent(port);
- of_node_put(port);
- if (rem == NULL) {
- v4l2_info(&csi_dev->v4l2_dev,
- "Remote device at %s not found\n",
- port->full_name);
- return -1;
- }
-
- csi_dev->asd.match_type = V4L2_ASYNC_MATCH_OF;
- csi_dev->asd.match.of.node = rem;
- csi_dev->async_subdevs[0] = &csi_dev->asd;
-
- of_node_put(rem);
- break;
- }
-
- csi_dev->subdev_notifier.subdevs = csi_dev->async_subdevs;
- csi_dev->subdev_notifier.num_subdevs = 1;
- csi_dev->subdev_notifier.bound = subdev_notifier_bound;
-
- ret = v4l2_async_notifier_register(&csi_dev->v4l2_dev,
- &csi_dev->subdev_notifier);
- if (ret)
- dev_err(csi_dev->dev,
- "Error register async notifier regoster\n");
-
- return ret;
-}
-
-static int mx6s_csi_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- const struct of_device_id *of_id;
- struct mx6s_csi_dev *csi_dev;
- struct video_device *vdev;
- struct resource *res;
- int ret = 0;
-
- dev_dbg(dev, "initialising\n");
-
- /* Prepare our private structure */
- csi_dev = devm_kzalloc(dev, sizeof(struct mx6s_csi_dev), GFP_ATOMIC);
- if (!csi_dev) {
- dev_err(dev, "Can't allocate private structure\n");
- return -ENODEV;
- }
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- csi_dev->irq = platform_get_irq(pdev, 0);
- if (res == NULL || csi_dev->irq < 0) {
- dev_err(dev, "Missing platform resources data\n");
- return -ENODEV;
- }
-
- csi_dev->regbase = devm_ioremap_resource(dev, res);
- if (IS_ERR(csi_dev->regbase)) {
- dev_err(dev, "Failed platform resources map\n");
- return -ENODEV;
- }
-
- /* init video dma queues */
- INIT_LIST_HEAD(&csi_dev->capture);
- INIT_LIST_HEAD(&csi_dev->active_bufs);
- INIT_LIST_HEAD(&csi_dev->discard);
-
- csi_dev->clk_disp_axi = devm_clk_get(dev, "disp-axi");
- if (IS_ERR(csi_dev->clk_disp_axi)) {
- dev_err(dev, "Could not get csi axi clock\n");
- return -ENODEV;
- }
-
- csi_dev->clk_disp_dcic = devm_clk_get(dev, "disp_dcic");
- if (IS_ERR(csi_dev->clk_disp_dcic)) {
- dev_err(dev, "Could not get disp dcic clock\n");
- return -ENODEV;
- }
-
- csi_dev->clk_csi_mclk = devm_clk_get(dev, "csi_mclk");
- if (IS_ERR(csi_dev->clk_csi_mclk)) {
- dev_err(dev, "Could not get csi mclk clock\n");
- return -ENODEV;
- }
-
- csi_dev->dev = dev;
-
- mx6s_csi_mux_sel(csi_dev);
-
- of_id = of_match_node(mx6s_csi_dt_ids, csi_dev->dev->of_node);
- if (!of_id)
- return -EINVAL;
- csi_dev->rx_fifo_rst = of_id->data;
-
- snprintf(csi_dev->v4l2_dev.name,
- sizeof(csi_dev->v4l2_dev.name), "CSI");
-
- ret = v4l2_device_register(dev, &csi_dev->v4l2_dev);
- if (ret < 0) {
- dev_err(dev, "v4l2_device_register() failed: %d\n", ret);
- return -ENODEV;
- }
-
- /* initialize locks */
- mutex_init(&csi_dev->lock);
- spin_lock_init(&csi_dev->slock);
-
- /* Allocate memory for video device */
- vdev = video_device_alloc();
- if (vdev == NULL) {
- ret = -ENOMEM;
- goto err_vdev;
- }
-
- snprintf(vdev->name, sizeof(vdev->name), "mx6s-csi");
-
- vdev->v4l2_dev = &csi_dev->v4l2_dev;
- vdev->fops = &mx6s_csi_fops;
- vdev->ioctl_ops = &mx6s_csi_ioctl_ops;
- vdev->release = video_device_release;
- vdev->lock = &csi_dev->lock;
-
- vdev->queue = &csi_dev->vb2_vidq;
-
- csi_dev->vdev = vdev;
-
- video_set_drvdata(csi_dev->vdev, csi_dev);
- mutex_lock(&csi_dev->lock);
-
- ret = video_register_device(csi_dev->vdev, VFL_TYPE_GRABBER, -1);
- if (ret < 0) {
- video_device_release(csi_dev->vdev);
- mutex_unlock(&csi_dev->lock);
- goto err_vdev;
- }
-
- /* install interrupt handler */
- if (devm_request_irq(dev, csi_dev->irq, mx6s_csi_irq_handler,
- 0, "csi", (void *)csi_dev)) {
- mutex_unlock(&csi_dev->lock);
- dev_err(dev, "Request CSI IRQ failed.\n");
- ret = -ENODEV;
- goto err_irq;
- }
-
- mutex_unlock(&csi_dev->lock);
-
- ret = mx6sx_register_subdevs(csi_dev);
- if (ret < 0)
- goto err_irq;
-
- pm_runtime_enable(csi_dev->dev);
- return 0;
-
-err_irq:
- video_unregister_device(csi_dev->vdev);
-err_vdev:
- v4l2_device_unregister(&csi_dev->v4l2_dev);
- return ret;
-}
-
-static int mx6s_csi_remove(struct platform_device *pdev)
-{
- struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev);
- struct mx6s_csi_dev *csi_dev =
- container_of(v4l2_dev, struct mx6s_csi_dev, v4l2_dev);
-
- v4l2_async_notifier_unregister(&csi_dev->subdev_notifier);
-
- video_unregister_device(csi_dev->vdev);
- v4l2_device_unregister(&csi_dev->v4l2_dev);
-
- pm_runtime_disable(csi_dev->dev);
- return 0;
-}
-
-static int mx6s_csi_runtime_suspend(struct device *dev)
-{
- dev_dbg(dev, "csi v4l2 busfreq high release.\n");
- return 0;
-}
-
-static int mx6s_csi_runtime_resume(struct device *dev)
-{
- dev_dbg(dev, "csi v4l2 busfreq high request.\n");
- return 0;
-}
-
-static const struct dev_pm_ops mx6s_csi_pm_ops = {
- SET_RUNTIME_PM_OPS(mx6s_csi_runtime_suspend, mx6s_csi_runtime_resume, NULL)
-};
-
-static const u8 mx6s_fifo_rst = true;
-static const u8 mx6sl_fifo_rst = false;
-
-static const struct of_device_id mx6s_csi_dt_ids[] = {
- { .compatible = "fsl,imx6s-csi",
- .data = &mx6s_fifo_rst,
- },
- { .compatible = "fsl,imx6sl-csi",
- .data = &mx6sl_fifo_rst,
- },
- { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, mx6s_csi_dt_ids);
-
-static struct platform_driver mx6s_csi_driver = {
- .driver = {
- .name = MX6S_CAM_DRV_NAME,
- .of_match_table = of_match_ptr(mx6s_csi_dt_ids),
- .pm = &mx6s_csi_pm_ops,
- },
- .probe = mx6s_csi_probe,
- .remove = mx6s_csi_remove,
-};
-
-module_platform_driver(mx6s_csi_driver);
-
-MODULE_DESCRIPTION("i.MX6Sx SoC Camera Host driver");
-MODULE_AUTHOR("Freescale Semiconductor, Inc.");
-MODULE_LICENSE("GPL");
-MODULE_VERSION(MX6S_CAM_VERSION);
+++ /dev/null
-/*
- * Freescale i.MX7 SoC series MIPI-CSI V3.3 receiver driver
- *
- * Copyright (C) 2015-2016 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
- */
-/*
- * Samsung S5P/EXYNOS SoC series MIPI-CSI receiver driver
- *
- * Copyright (C) 2011 - 2013 Samsung Electronics Co., Ltd.
- * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/clk.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/irq.h>
-#include <linux/kernel.h>
-#include <linux/mfd/syscon.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/of_graph.h>
-#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
-#include <linux/regmap.h>
-#include <linux/regulator/consumer.h>
-#include <linux/slab.h>
-#include <linux/spinlock.h>
-#include <linux/videodev2.h>
-#include <media/v4l2-of.h>
-#include <media/v4l2-subdev.h>
-#include <media/v4l2-device.h>
-
-static int debug;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "Debug level (0-2)");
-
-#define CSIS_DRIVER_NAME "mxc_mipi-csi"
-#define CSIS_SUBDEV_NAME CSIS_DRIVER_NAME
-#define CSIS_MAX_ENTITIES 2
-#define CSIS0_MAX_LANES 4
-#define CSIS1_MAX_LANES 2
-
-#define MIPI_CSIS_DEF_PIX_WIDTH 640
-#define MIPI_CSIS_DEF_PIX_HEIGHT 480
-
-/* Register map definition */
-
-/* CSIS version */
-#define MIPI_CSIS_VERSION 0x00
-
-/* CSIS common control */
-#define MIPI_CSIS_CMN_CTRL 0x04
-#define MIPI_CSIS_CMN_CTRL_UPDATE_SHADOW (1 << 16)
-#define MIPI_CSIS_CMN_CTRL_INTER_MODE (1 << 10)
-#define MIPI_CSIS_CMN_CTRL_LANE_NR_OFFSET 8
-#define MIPI_CSIS_CMN_CTRL_LANE_NR_MASK (3 << 8)
-#define MIPI_CSIS_CMN_CTRL_UPDATE_SHADOW_CTRL (1 << 2)
-#define MIPI_CSIS_CMN_CTRL_RESET (1 << 1)
-#define MIPI_CSIS_CMN_CTRL_ENABLE (1 << 0)
-
-/* CSIS clock control */
-#define MIPI_CSIS_CLK_CTRL 0x08
-#define MIPI_CSIS_CLK_CTRL_CLKGATE_TRAIL_CH3(x) (x << 28)
-#define MIPI_CSIS_CLK_CTRL_CLKGATE_TRAIL_CH2(x) (x << 24)
-#define MIPI_CSIS_CLK_CTRL_CLKGATE_TRAIL_CH1(x) (x << 20)
-#define MIPI_CSIS_CLK_CTRL_CLKGATE_TRAIL_CH0(x) (x << 16)
-#define MIPI_CSIS_CLK_CTRL_CLKGATE_EN_MSK (0xf << 4)
-#define MIPI_CSIS_CLK_CTRL_WCLK_SRC (1 << 0)
-
-/* CSIS Interrupt mask */
-#define MIPI_CSIS_INTMSK 0x10
-#define MIPI_CSIS_INTMSK_EVEN_BEFORE (1 << 31)
-#define MIPI_CSIS_INTMSK_EVEN_AFTER (1 << 30)
-#define MIPI_CSIS_INTMSK_ODD_BEFORE (1 << 29)
-#define MIPI_CSIS_INTMSK_ODD_AFTER (1 << 28)
-#define MIPI_CSIS_INTMSK_FRAME_START (1 << 24)
-#define MIPI_CSIS_INTMSK_FRAME_END (1 << 20)
-#define MIPI_CSIS_INTMSK_ERR_SOT_HS (1 << 16)
-#define MIPI_CSIS_INTMSK_ERR_LOST_FS (1 << 12)
-#define MIPI_CSIS_INTMSK_ERR_LOST_FE (1 << 8)
-#define MIPI_CSIS_INTMSK_ERR_OVER (1 << 4)
-#define MIPI_CSIS_INTMSK_ERR_WRONG_CFG (1 << 3)
-#define MIPI_CSIS_INTMSK_ERR_ECC (1 << 2)
-#define MIPI_CSIS_INTMSK_ERR_CRC (1 << 1)
-#define MIPI_CSIS_INTMSK_ERR_UNKNOWN (1 << 0)
-
-/* CSIS Interrupt source */
-#define MIPI_CSIS_INTSRC 0x14
-#define MIPI_CSIS_INTSRC_EVEN_BEFORE (1 << 31)
-#define MIPI_CSIS_INTSRC_EVEN_AFTER (1 << 30)
-#define MIPI_CSIS_INTSRC_EVEN (0x3 << 30)
-#define MIPI_CSIS_INTSRC_ODD_BEFORE (1 << 29)
-#define MIPI_CSIS_INTSRC_ODD_AFTER (1 << 28)
-#define MIPI_CSIS_INTSRC_ODD (0x3 << 28)
-#define MIPI_CSIS_INTSRC_NON_IMAGE_DATA (0xf << 28)
-#define MIPI_CSIS_INTSRC_FRAME_START (1 << 24)
-#define MIPI_CSIS_INTSRC_FRAME_END (1 << 20)
-#define MIPI_CSIS_INTSRC_ERR_SOT_HS (1 << 16)
-#define MIPI_CSIS_INTSRC_ERR_LOST_FS (1 << 12)
-#define MIPI_CSIS_INTSRC_ERR_LOST_FE (1 << 8)
-#define MIPI_CSIS_INTSRC_ERR_OVER (1 << 4)
-#define MIPI_CSIS_INTSRC_ERR_WRONG_CFG (1 << 3)
-#define MIPI_CSIS_INTSRC_ERR_ECC (1 << 2)
-#define MIPI_CSIS_INTSRC_ERR_CRC (1 << 1)
-#define MIPI_CSIS_INTSRC_ERR_UNKNOWN (1 << 0)
-#define MIPI_CSIS_INTSRC_ERRORS 0xfffff
-
-/* D-PHY status control */
-#define MIPI_CSIS_DPHYSTATUS 0x20
-#define MIPI_CSIS_DPHYSTATUS_ULPS_DAT (1 << 8)
-#define MIPI_CSIS_DPHYSTATUS_STOPSTATE_DAT (1 << 4)
-#define MIPI_CSIS_DPHYSTATUS_ULPS_CLK (1 << 1)
-#define MIPI_CSIS_DPHYSTATUS_STOPSTATE_CLK (1 << 0)
-
-/* D-PHY common control */
-#define MIPI_CSIS_DPHYCTRL 0x24
-#define MIPI_CSIS_DPHYCTRL_HSS_MASK (0xff << 24)
-#define MIPI_CSIS_DPHYCTRL_HSS_OFFSET 24
-#define MIPI_CSIS_DPHYCTRL_SCLKS_MASK (0x3 << 22)
-#define MIPI_CSIS_DPHYCTRL_SCLKS_OFFSET 22
-#define MIPI_CSIS_DPHYCTRL_DPDN_SWAP_CLK (1 << 6)
-#define MIPI_CSIS_DPHYCTRL_DPDN_SWAP_DAT (1 << 5)
-#define MIPI_CSIS_DPHYCTRL_ENABLE_DAT (1 << 1)
-#define MIPI_CSIS_DPHYCTRL_ENABLE_CLK (1 << 0)
-#define MIPI_CSIS_DPHYCTRL_ENABLE (0x1f << 0)
-
-/* D-PHY Master and Slave Control register Low */
-#define MIPI_CSIS_DPHYBCTRL_L 0x30
-/* D-PHY Master and Slave Control register High */
-#define MIPI_CSIS_DPHYBCTRL_H 0x34
-/* D-PHY Slave Control register Low */
-#define MIPI_CSIS_DPHYSCTRL_L 0x38
-/* D-PHY Slave Control register High */
-#define MIPI_CSIS_DPHYSCTRL_H 0x3c
-
-
-/* ISP Configuration register */
-#define MIPI_CSIS_ISPCONFIG_CH0 0x40
-#define MIPI_CSIS_ISPCONFIG_CH1 0x50
-#define MIPI_CSIS_ISPCONFIG_CH2 0x60
-#define MIPI_CSIS_ISPCONFIG_CH3 0x70
-
-#define MIPI_CSIS_ISPCFG_MEM_FULL_GAP_MSK (0xff << 24)
-#define MIPI_CSIS_ISPCFG_MEM_FULL_GAP(x) (x << 24)
-#define MIPI_CSIS_ISPCFG_DOUBLE_CMPNT (1 << 12)
-#define MIPI_CSIS_ISPCFG_ALIGN_32BIT (1 << 11)
-#define MIPI_CSIS_ISPCFG_FMT_YCBCR422_8BIT (0x1e << 2)
-#define MIPI_CSIS_ISPCFG_FMT_RAW8 (0x2a << 2)
-#define MIPI_CSIS_ISPCFG_FMT_RAW10 (0x2b << 2)
-#define MIPI_CSIS_ISPCFG_FMT_RAW12 (0x2c << 2)
-/* User defined formats, x = 1...4 */
-#define MIPI_CSIS_ISPCFG_FMT_USER(x) ((0x30 + x - 1) << 2)
-#define MIPI_CSIS_ISPCFG_FMT_MASK (0x3f << 2)
-
-/* ISP Image Resolution register */
-#define MIPI_CSIS_ISPRESOL_CH0 0x44
-#define MIPI_CSIS_ISPRESOL_CH1 0x54
-#define MIPI_CSIS_ISPRESOL_CH2 0x64
-#define MIPI_CSIS_ISPRESOL_CH3 0x74
-#define CSIS_MAX_PIX_WIDTH 0xffff
-#define CSIS_MAX_PIX_HEIGHT 0xffff
-
-/* ISP SYNC register */
-#define MIPI_CSIS_ISPSYNC_CH0 0x48
-#define MIPI_CSIS_ISPSYNC_CH1 0x58
-#define MIPI_CSIS_ISPSYNC_CH2 0x68
-#define MIPI_CSIS_ISPSYNC_CH3 0x78
-
-#define MIPI_CSIS_ISPSYNC_HSYNC_LINTV_OFFSET 18
-#define MIPI_CSIS_ISPSYNC_VSYNC_SINTV_OFFSET 12
-#define MIPI_CSIS_ISPSYNC_VSYNC_EINTV_OFFSET 0
-
-/* Non-image packet data buffers */
-#define MIPI_CSIS_PKTDATA_ODD 0x2000
-#define MIPI_CSIS_PKTDATA_EVEN 0x3000
-#define MIPI_CSIS_PKTDATA_SIZE SZ_4K
-
-#define DEFAULT_SCLK_CSIS_FREQ 166000000UL
-
-enum {
- ST_POWERED = 1,
- ST_STREAMING = 2,
- ST_SUSPENDED = 4,
-};
-
-struct mipi_csis_event {
- u32 mask;
- const char * const name;
- unsigned int counter;
-};
-
-static const struct mipi_csis_event mipi_csis_events[] = {
- /* Errors */
- { MIPI_CSIS_INTSRC_ERR_SOT_HS, "SOT Error" },
- { MIPI_CSIS_INTSRC_ERR_LOST_FS, "Lost Frame Start Error" },
- { MIPI_CSIS_INTSRC_ERR_LOST_FE, "Lost Frame End Error" },
- { MIPI_CSIS_INTSRC_ERR_OVER, "FIFO Overflow Error" },
- { MIPI_CSIS_INTSRC_ERR_ECC, "ECC Error" },
- { MIPI_CSIS_INTSRC_ERR_CRC, "CRC Error" },
- { MIPI_CSIS_INTSRC_ERR_UNKNOWN, "Unknown Error" },
- /* Non-image data receive events */
- { MIPI_CSIS_INTSRC_EVEN_BEFORE, "Non-image data before even frame" },
- { MIPI_CSIS_INTSRC_EVEN_AFTER, "Non-image data after even frame" },
- { MIPI_CSIS_INTSRC_ODD_BEFORE, "Non-image data before odd frame" },
- { MIPI_CSIS_INTSRC_ODD_AFTER, "Non-image data after odd frame" },
- /* Frame start/end */
- { MIPI_CSIS_INTSRC_FRAME_START, "Frame Start" },
- { MIPI_CSIS_INTSRC_FRAME_END, "Frame End" },
-};
-#define MIPI_CSIS_NUM_EVENTS ARRAY_SIZE(mipi_csis_events)
-
-struct csis_pktbuf {
- u32 *data;
- unsigned int len;
-};
-
-struct csis_hw_reset {
- struct regmap *src;
- u8 req_src;
- u8 rst_bit;
-};
-
-/**
- * struct csi_state - the driver's internal state data structure
- * @lock: mutex serializing the subdev and power management operations,
- * protecting @format and @flags members
- * @sd: v4l2_subdev associated with CSIS device instance
- * @index: the hardware instance index
- * @pdev: CSIS platform device
- * @phy: pointer to the CSIS generic PHY
- * @regs: mmaped I/O registers memory
- * @supplies: CSIS regulator supplies
- * @clock: CSIS clocks
- * @irq: requested s5p-mipi-csis irq number
- * @flags: the state variable for power and streaming control
- * @clock_frequency: device bus clock frequency
- * @hs_settle: HS-RX settle time
- * @clk_settle: Clk settle time
- * @num_lanes: number of MIPI-CSI data lanes used
- * @max_num_lanes: maximum number of MIPI-CSI data lanes supported
- * @wclk_ext: CSI wrapper clock: 0 - bus clock, 1 - external SCLK_CAM
- * @csis_fmt: current CSIS pixel format
- * @format: common media bus format for the source and sink pad
- * @slock: spinlock protecting structure members below
- * @pkt_buf: the frame embedded (non-image) data buffer
- * @events: MIPI-CSIS event (error) counters
- */
-struct csi_state {
- struct mutex lock;
- struct device *dev;
- struct v4l2_subdev mipi_sd;
- struct v4l2_subdev *sensor_sd;
- struct v4l2_device v4l2_dev;
-
- u8 index;
- struct platform_device *pdev;
- struct phy *phy;
- void __iomem *regs;
- struct clk *mipi_clk;
- struct clk *phy_clk;
- int irq;
- u32 flags;
-
- u32 clk_frequency;
- u32 hs_settle;
- u32 clk_settle;
- u32 num_lanes;
- u32 max_num_lanes;
- u8 wclk_ext;
-
- const struct csis_pix_format *csis_fmt;
- struct v4l2_mbus_framefmt format;
-
- spinlock_t slock;
- struct csis_pktbuf pkt_buf;
- struct mipi_csis_event events[MIPI_CSIS_NUM_EVENTS];
-
- struct v4l2_async_subdev asd;
- struct v4l2_async_notifier subdev_notifier;
- struct v4l2_async_subdev *async_subdevs[2];
-
- struct csis_hw_reset hw_reset;
- struct regulator *mipi_phy_regulator;
-};
-
-/**
- * struct csis_pix_format - CSIS pixel format description
- * @pix_width_alignment: horizontal pixel alignment, width will be
- * multiple of 2^pix_width_alignment
- * @code: corresponding media bus code
- * @fmt_reg: MIPI_CSIS_CONFIG register value
- * @data_alignment: MIPI-CSI data alignment in bits
- */
-struct csis_pix_format {
- unsigned int pix_width_alignment;
- u32 code;
- u32 fmt_reg;
- u8 data_alignment;
-};
-
-static const struct csis_pix_format mipi_csis_formats[] = {
- {
- .code = MEDIA_BUS_FMT_YUYV8_2X8,
- .fmt_reg = MIPI_CSIS_ISPCFG_FMT_YCBCR422_8BIT,
- .data_alignment = 16,
- }, {
- .code = MEDIA_BUS_FMT_VYUY8_2X8,
- .fmt_reg = MIPI_CSIS_ISPCFG_FMT_YCBCR422_8BIT,
- .data_alignment = 16,
- }, {
- .code = MEDIA_BUS_FMT_SBGGR8_1X8,
- .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW8,
- .data_alignment = 8,
- }
-};
-
-#define mipi_csis_write(__csis, __r, __v) writel(__v, __csis->regs + __r)
-#define mipi_csis_read(__csis, __r) readl(__csis->regs + __r)
-
-static struct csi_state *mipi_sd_to_csi_state(struct v4l2_subdev *sdev)
-{
- return container_of(sdev, struct csi_state, mipi_sd);
-}
-
-static inline struct csi_state
- *notifier_to_mipi_dev(struct v4l2_async_notifier *n)
-{
- return container_of(n, struct csi_state, subdev_notifier);
-}
-
-static const struct csis_pix_format *find_csis_format(u32 code)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(mipi_csis_formats); i++)
- if (code == mipi_csis_formats[i].code)
- return &mipi_csis_formats[i];
- return NULL;
-}
-
-static void mipi_csis_enable_interrupts(struct csi_state *state, bool on)
-{
- u32 val = mipi_csis_read(state, MIPI_CSIS_INTMSK);
- if (on)
- val |= 0xf00fffff;
- else
- val &= ~0xf00fffff;
- mipi_csis_write(state, MIPI_CSIS_INTMSK, val);
-}
-
-static void mipi_csis_sw_reset(struct csi_state *state)
-{
- u32 val = mipi_csis_read(state, MIPI_CSIS_CMN_CTRL);
-
- mipi_csis_write(state, MIPI_CSIS_CMN_CTRL, val | MIPI_CSIS_CMN_CTRL_RESET);
- udelay(10);
-}
-
-static int mipi_csis_phy_init(struct csi_state *state)
-{
- int ret;
-
- state->mipi_phy_regulator = devm_regulator_get(state->dev,
- "mipi-phy");
-
- ret = regulator_set_voltage(state->mipi_phy_regulator,
- 1000000, 1000000);
-
- return ret;
-}
-
-static int mipi_csis_phy_reset(struct csi_state *state)
-{
- struct device_node *np = state->dev->of_node;
- struct device_node *node;
- phandle phandle;
- u32 out_val[3];
- int ret;
-
- ret = of_property_read_u32_array(np, "csis-phy-reset", out_val, 3);
- if (ret) {
- dev_dbg(state->dev, "no csis-hw-reset property found\n");
- } else {
- phandle = *out_val;
-
- node = of_find_node_by_phandle(phandle);
- if (!node) {
- dev_dbg(state->dev, "not find src node by phandle\n");
- ret = PTR_ERR(node);
- }
- state->hw_reset.src = syscon_node_to_regmap(node);
- if (IS_ERR(state->hw_reset.src)) {
- dev_err(state->dev, "failed to get src regmap\n");
- ret = PTR_ERR(state->hw_reset.src);
- }
- of_node_put(node);
- if (ret < 0)
- return ret;
-
- state->hw_reset.req_src = out_val[1];
- state->hw_reset.rst_bit = out_val[2];
-
- /* reset mipi phy */
- regmap_update_bits(state->hw_reset.src, state->hw_reset.req_src,
- 1 << state->hw_reset.rst_bit, 1 << state->hw_reset.rst_bit);
- msleep(20);
- regmap_update_bits(state->hw_reset.src, state->hw_reset.req_src,
- 1 << state->hw_reset.rst_bit, 0);
-
- }
- return ret;
-}
-
-static void mipi_csis_system_enable(struct csi_state *state, int on)
-{
- u32 val, mask;
-
- val = mipi_csis_read(state, MIPI_CSIS_CMN_CTRL);
- if (on)
- val |= MIPI_CSIS_CMN_CTRL_ENABLE;
- else
- val &= ~MIPI_CSIS_CMN_CTRL_ENABLE;
- mipi_csis_write(state, MIPI_CSIS_CMN_CTRL, val);
-
- val = mipi_csis_read(state, MIPI_CSIS_DPHYCTRL);
- val &= ~MIPI_CSIS_DPHYCTRL_ENABLE;
- if (on) {
- mask = (1 << (state->num_lanes + 1)) - 1;
- val |= (mask & MIPI_CSIS_DPHYCTRL_ENABLE);
- }
- mipi_csis_write(state, MIPI_CSIS_DPHYCTRL, val);
-}
-
-/* Called with the state.lock mutex held */
-static void __mipi_csis_set_format(struct csi_state *state)
-{
- struct v4l2_mbus_framefmt *mf = &state->format;
- u32 val;
-
- v4l2_dbg(1, debug, &state->mipi_sd, "fmt: %#x, %d x %d\n",
- mf->code, mf->width, mf->height);
-
- /* Color format */
- val = mipi_csis_read(state, MIPI_CSIS_ISPCONFIG_CH0);
- val = (val & ~MIPI_CSIS_ISPCFG_FMT_MASK) | state->csis_fmt->fmt_reg;
- mipi_csis_write(state, MIPI_CSIS_ISPCONFIG_CH0, val);
-
- /* Pixel resolution */
- val = mf->width | (mf->height << 16);
- mipi_csis_write(state, MIPI_CSIS_ISPRESOL_CH0, val);
-}
-
-static void mipi_csis_set_hsync_settle(struct csi_state *state,
- int hs_settle, int clk_settle)
-{
- u32 val = mipi_csis_read(state, MIPI_CSIS_DPHYCTRL);
-
- val = (val & ~MIPI_CSIS_DPHYCTRL_HSS_MASK) |
- (hs_settle << 24) | (clk_settle << 22);
-
- mipi_csis_write(state, MIPI_CSIS_DPHYCTRL, val);
-}
-
-static void mipi_csis_set_params(struct csi_state *state)
-{
- u32 val;
-
- val = mipi_csis_read(state, MIPI_CSIS_CMN_CTRL);
- val &= ~MIPI_CSIS_CMN_CTRL_LANE_NR_MASK;
- val |= (state->num_lanes - 1) << MIPI_CSIS_CMN_CTRL_LANE_NR_OFFSET;
- mipi_csis_write(state, MIPI_CSIS_CMN_CTRL, val);
-
- __mipi_csis_set_format(state);
-
- mipi_csis_set_hsync_settle(state, state->hs_settle, state->clk_settle);
-
- val = mipi_csis_read(state, MIPI_CSIS_ISPCONFIG_CH0);
- if (state->csis_fmt->data_alignment == 32)
- val |= MIPI_CSIS_ISPCFG_ALIGN_32BIT;
- else /* Normal output */
- val &= ~MIPI_CSIS_ISPCFG_ALIGN_32BIT;
- mipi_csis_write(state, MIPI_CSIS_ISPCONFIG_CH0, val);
-
- val = (0 << MIPI_CSIS_ISPSYNC_HSYNC_LINTV_OFFSET) |
- (0 << MIPI_CSIS_ISPSYNC_VSYNC_SINTV_OFFSET) |
- (0 << MIPI_CSIS_ISPSYNC_VSYNC_EINTV_OFFSET);
- mipi_csis_write(state, MIPI_CSIS_ISPSYNC_CH0, val);
-
- val = mipi_csis_read(state, MIPI_CSIS_CLK_CTRL);
- val &= ~MIPI_CSIS_CLK_CTRL_WCLK_SRC;
- if (state->wclk_ext)
- val |= MIPI_CSIS_CLK_CTRL_WCLK_SRC;
- val |= MIPI_CSIS_CLK_CTRL_CLKGATE_TRAIL_CH0(15);
- val &= ~MIPI_CSIS_CLK_CTRL_CLKGATE_EN_MSK;
- mipi_csis_write(state, MIPI_CSIS_CLK_CTRL, val);
-
- mipi_csis_write(state, MIPI_CSIS_DPHYBCTRL_L, 0x1f4);
- mipi_csis_write(state, MIPI_CSIS_DPHYBCTRL_H, 0);
-
- /* Update the shadow register. */
- val = mipi_csis_read(state, MIPI_CSIS_CMN_CTRL);
- mipi_csis_write(state, MIPI_CSIS_CMN_CTRL, val | MIPI_CSIS_CMN_CTRL_UPDATE_SHADOW |
- MIPI_CSIS_CMN_CTRL_UPDATE_SHADOW_CTRL);
-}
-
-static void mipi_csis_clk_enable(struct csi_state *state)
-{
- clk_prepare_enable(state->mipi_clk);
- clk_prepare_enable(state->phy_clk);
-}
-
-static void mipi_csis_clk_disable(struct csi_state *state)
-{
- clk_disable_unprepare(state->mipi_clk);
- clk_disable_unprepare(state->phy_clk);
-}
-
-static int mipi_csis_clk_get(struct csi_state *state)
-{
- struct device *dev = &state->pdev->dev;
- int ret = true;
-
- state->mipi_clk = devm_clk_get(dev, "mipi_clk");
- if (IS_ERR(state->mipi_clk)) {
- dev_err(dev, "Could not get mipi csi clock\n");
- return -ENODEV;
- }
-
- state->phy_clk = devm_clk_get(dev, "phy_clk");
- if (IS_ERR(state->phy_clk)) {
- dev_err(dev, "Could not get mipi phy clock\n");
- return -ENODEV;
- }
-
- /* Set clock rate */
- if (state->clk_frequency)
- ret = clk_set_rate(state->mipi_clk,
- state->clk_frequency);
- else
- dev_WARN(dev, "No clock frequency specified!\n");
- if (ret < 0) {
- dev_err(dev, "set rate filed, rate=%d\n", state->clk_frequency);
- return -EINVAL;
- }
-
- return ret;
-}
-
-static void dump_regs(struct csi_state *state, const char *label)
-{
- struct {
- u32 offset;
- const char * const name;
- } registers[] = {
- { 0x00, "CTRL" },
- { 0x04, "DPHYCTRL" },
- { 0x08, "CONFIG" },
- { 0x0c, "DPHYSTS" },
- { 0x10, "INTMSK" },
- { 0x2c, "RESOL" },
- { 0x38, "SDW_CONFIG" },
- };
- u32 i;
-
- v4l2_info(&state->mipi_sd, "--- %s ---\n", label);
-
- for (i = 0; i < ARRAY_SIZE(registers); i++) {
- u32 cfg = mipi_csis_read(state, registers[i].offset);
- v4l2_info(&state->mipi_sd, "%10s: 0x%08x\n", registers[i].name, cfg);
- }
-}
-
-static void mipi_csis_start_stream(struct csi_state *state)
-{
- mipi_csis_sw_reset(state);
- mipi_csis_set_params(state);
- mipi_csis_system_enable(state, true);
- mipi_csis_enable_interrupts(state, true);
-}
-
-static void mipi_csis_stop_stream(struct csi_state *state)
-{
- mipi_csis_enable_interrupts(state, false);
- mipi_csis_system_enable(state, false);
-}
-
-static void mipi_csis_clear_counters(struct csi_state *state)
-{
- unsigned long flags;
- int i;
-
- spin_lock_irqsave(&state->slock, flags);
- for (i = 0; i < MIPI_CSIS_NUM_EVENTS; i++)
- state->events[i].counter = 0;
- spin_unlock_irqrestore(&state->slock, flags);
-}
-
-static void mipi_csis_log_counters(struct csi_state *state, bool non_errors)
-{
- int i = non_errors ? MIPI_CSIS_NUM_EVENTS : MIPI_CSIS_NUM_EVENTS - 4;
- unsigned long flags;
-
- spin_lock_irqsave(&state->slock, flags);
-
- for (i--; i >= 0; i--) {
- if (state->events[i].counter > 0 || debug)
- v4l2_info(&state->mipi_sd, "%s events: %d\n",
- state->events[i].name,
- state->events[i].counter);
- }
- spin_unlock_irqrestore(&state->slock, flags);
-}
-
-/*
- * V4L2 subdev operations
- */
-static int mipi_csis_s_power(struct v4l2_subdev *mipi_sd, int on)
-{
- struct csi_state *state = mipi_sd_to_csi_state(mipi_sd);
- struct device *dev = &state->pdev->dev;
-
- v4l2_subdev_call(state->sensor_sd, core, s_power, on);
-
- if (on)
- return pm_runtime_get_sync(dev);
-
- return pm_runtime_put_sync(dev);
-}
-
-static int mipi_csis_s_stream(struct v4l2_subdev *mipi_sd, int enable)
-{
- struct csi_state *state = mipi_sd_to_csi_state(mipi_sd);
- int ret = 0;
-
- v4l2_dbg(1, debug, mipi_sd, "%s: %d, state: 0x%x\n",
- __func__, enable, state->flags);
-
- if (enable) {
- mipi_csis_clear_counters(state);
- ret = pm_runtime_get_sync(&state->pdev->dev);
- if (ret && ret != 1)
- return ret;
- }
-
- mutex_lock(&state->lock);
- if (enable) {
- if (state->flags & ST_SUSPENDED) {
- ret = -EBUSY;
- goto unlock;
- }
- mipi_csis_start_stream(state);
- v4l2_subdev_call(state->sensor_sd, video, s_stream, true);
- state->flags |= ST_STREAMING;
- } else {
- v4l2_subdev_call(state->sensor_sd, video, s_stream, false);
- mipi_csis_stop_stream(state);
- state->flags &= ~ST_STREAMING;
- if (debug > 0)
- mipi_csis_log_counters(state, true);
- }
-unlock:
- mutex_unlock(&state->lock);
- if (!enable)
- pm_runtime_put(&state->pdev->dev);
-
- return ret == 1 ? 0 : ret;
-}
-
-static int mipi_csis_enum_mbus_code(struct v4l2_subdev *mipi_sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_mbus_code_enum *code)
-{
- struct csi_state *state = mipi_sd_to_csi_state(mipi_sd);
- struct v4l2_subdev *sensor_sd = state->sensor_sd;
- struct csis_pix_format const *csis_fmt;
- int ret;
-
- ret = v4l2_subdev_call(sensor_sd, pad, enum_mbus_code, NULL, code);
- if (ret < 0)
- return -EINVAL;
-
- csis_fmt = find_csis_format(code->code);
- if (csis_fmt == NULL) {
- dev_err(state->dev, "format not match\n");
- return -EINVAL;
- }
-
- return ret;
-}
-
-static int mipi_csis_set_fmt(struct v4l2_subdev *mipi_sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_format *format)
-{
- struct csi_state *state = mipi_sd_to_csi_state(mipi_sd);
- struct v4l2_subdev *sensor_sd = state->sensor_sd;
- struct csis_pix_format const *csis_fmt;
- struct v4l2_mbus_framefmt *mf = &format->format;
-
- if (format->pad)
- return -EINVAL;
-
- csis_fmt = find_csis_format(mf->code);
- if (csis_fmt == NULL)
- csis_fmt = &mipi_csis_formats[0];
-
- v4l2_subdev_call(sensor_sd, pad, set_fmt, NULL, format);
-
- mf->code = csis_fmt->code;
- v4l_bound_align_image(&mf->width, 1, CSIS_MAX_PIX_WIDTH,
- csis_fmt->pix_width_alignment,
- &mf->height, 1, CSIS_MAX_PIX_HEIGHT, 1,
- 0);
-
- if (format->which == V4L2_SUBDEV_FORMAT_TRY)
- return 0;
-
- state->format.code = mf->code;
- state->format.width = mf->width;
- state->format.height = mf->height;
-
- mutex_lock(&state->lock);
- state->csis_fmt = csis_fmt;
- mutex_unlock(&state->lock);
-
- return 0;
-}
-
-static int mipi_csis_get_fmt(struct v4l2_subdev *mipi_sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_format *format)
-{
- struct csi_state *state = mipi_sd_to_csi_state(mipi_sd);
- struct v4l2_subdev *sensor_sd = state->sensor_sd;
-
- if (format->pad)
- return -EINVAL;
-
- return v4l2_subdev_call(sensor_sd, pad, get_fmt, NULL, format);
-}
-
-static int mipi_csis_s_rx_buffer(struct v4l2_subdev *mipi_sd, void *buf,
- unsigned int *size)
-{
- struct csi_state *state = mipi_sd_to_csi_state(mipi_sd);
- unsigned long flags;
-
- *size = min_t(unsigned int, *size, MIPI_CSIS_PKTDATA_SIZE);
-
- spin_lock_irqsave(&state->slock, flags);
- state->pkt_buf.data = buf;
- state->pkt_buf.len = *size;
- spin_unlock_irqrestore(&state->slock, flags);
-
- return 0;
-}
-
-static int mipi_csis_s_parm(struct v4l2_subdev *mipi_sd, struct v4l2_streamparm *a)
-{
- struct csi_state *state = mipi_sd_to_csi_state(mipi_sd);
- struct v4l2_subdev *sensor_sd = state->sensor_sd;
-
- return v4l2_subdev_call(sensor_sd, video, s_parm, a);
-}
-
-static int mipi_csis_g_parm(struct v4l2_subdev *mipi_sd, struct v4l2_streamparm *a)
-{
- struct csi_state *state = mipi_sd_to_csi_state(mipi_sd);
- struct v4l2_subdev *sensor_sd = state->sensor_sd;
-
- return v4l2_subdev_call(sensor_sd, video, g_parm, a);
-}
-
-static int mipi_csis_enum_framesizes(struct v4l2_subdev *mipi_sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_frame_size_enum *fse)
-{
- struct csi_state *state = mipi_sd_to_csi_state(mipi_sd);
- struct v4l2_subdev *sensor_sd = state->sensor_sd;
-
- return v4l2_subdev_call(sensor_sd, pad, enum_frame_size, NULL, fse);
-}
-
-static int mipi_csis_enum_frameintervals(struct v4l2_subdev *mipi_sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_frame_interval_enum *fie)
-{
- struct csi_state *state = mipi_sd_to_csi_state(mipi_sd);
- struct v4l2_subdev *sensor_sd = state->sensor_sd;
-
- return v4l2_subdev_call(sensor_sd, pad, enum_frame_interval, NULL, fie);
-}
-
-static int mipi_csis_log_status(struct v4l2_subdev *mipi_sd)
-{
- struct csi_state *state = mipi_sd_to_csi_state(mipi_sd);
-
- mutex_lock(&state->lock);
- mipi_csis_log_counters(state, true);
- if (debug && (state->flags & ST_POWERED))
- dump_regs(state, __func__);
- mutex_unlock(&state->lock);
- return 0;
-}
-
-static struct v4l2_subdev_core_ops mipi_csis_core_ops = {
- .s_power = mipi_csis_s_power,
- .log_status = mipi_csis_log_status,
-};
-
-static struct v4l2_subdev_video_ops mipi_csis_video_ops = {
- .s_rx_buffer = mipi_csis_s_rx_buffer,
- .s_stream = mipi_csis_s_stream,
-
- .s_parm = mipi_csis_s_parm,
- .g_parm = mipi_csis_g_parm,
-};
-
-static const struct v4l2_subdev_pad_ops mipi_csis_pad_ops = {
- .enum_frame_size = mipi_csis_enum_framesizes,
- .enum_frame_interval = mipi_csis_enum_frameintervals,
- .enum_mbus_code = mipi_csis_enum_mbus_code,
- .get_fmt = mipi_csis_get_fmt,
- .set_fmt = mipi_csis_set_fmt,
-};
-
-static struct v4l2_subdev_ops mipi_csis_subdev_ops = {
- .core = &mipi_csis_core_ops,
- .video = &mipi_csis_video_ops,
- .pad = &mipi_csis_pad_ops,
-};
-
-static irqreturn_t mipi_csis_irq_handler(int irq, void *dev_id)
-{
- struct csi_state *state = dev_id;
- struct csis_pktbuf *pktbuf = &state->pkt_buf;
- unsigned long flags;
- u32 status;
-
- status = mipi_csis_read(state, MIPI_CSIS_INTSRC);
-
- spin_lock_irqsave(&state->slock, flags);
-
- if ((status & MIPI_CSIS_INTSRC_NON_IMAGE_DATA) && pktbuf->data) {
- u32 offset;
-
- if (status & MIPI_CSIS_INTSRC_EVEN)
- offset = MIPI_CSIS_PKTDATA_EVEN;
- else
- offset = MIPI_CSIS_PKTDATA_ODD;
-
- memcpy(pktbuf->data, state->regs + offset, pktbuf->len);
- pktbuf->data = NULL;
- rmb();
- }
-
- /* Update the event/error counters */
- if ((status & MIPI_CSIS_INTSRC_ERRORS) || debug) {
- int i;
- for (i = 0; i < MIPI_CSIS_NUM_EVENTS; i++) {
- if (!(status & state->events[i].mask))
- continue;
- state->events[i].counter++;
- v4l2_dbg(2, debug, &state->mipi_sd, "%s: %d\n",
- state->events[i].name,
- state->events[i].counter);
- }
- v4l2_dbg(2, debug, &state->mipi_sd, "status: %08x\n", status);
- }
- spin_unlock_irqrestore(&state->slock, flags);
-
- mipi_csis_write(state, MIPI_CSIS_INTSRC, status);
- return IRQ_HANDLED;
-}
-
-static int subdev_notifier_bound(struct v4l2_async_notifier *notifier,
- struct v4l2_subdev *subdev,
- struct v4l2_async_subdev *asd)
-{
- struct csi_state *state = notifier_to_mipi_dev(notifier);
-
- /* Find platform data for this sensor subdev */
- if (state->asd.match.of.node == subdev->dev->of_node)
- state->sensor_sd = subdev;
-
- if (subdev == NULL)
- return -EINVAL;
-
- v4l2_info(&state->v4l2_dev, "Registered sensor subdevice: %s\n",
- subdev->name);
-
- return 0;
-}
-
-static int mipi_csis_parse_dt(struct platform_device *pdev,
- struct csi_state *state)
-{
- struct device_node *node = pdev->dev.of_node;
-
- if (of_property_read_u32(node, "clock-frequency",
- &state->clk_frequency))
- state->clk_frequency = DEFAULT_SCLK_CSIS_FREQ;
- if (of_property_read_u32(node, "bus-width",
- &state->max_num_lanes))
- return -EINVAL;
-
- node = of_graph_get_next_endpoint(node, NULL);
- if (!node) {
- dev_err(&pdev->dev, "No port node at %s\n",
- pdev->dev.of_node->full_name);
- return -EINVAL;
- }
-
- /* Get MIPI CSI-2 bus configration from the endpoint node. */
- of_property_read_u32(node, "csis-hs-settle",
- &state->hs_settle);
-
- of_property_read_u32(node, "csis-clk-settle",
- &state->clk_settle);
- state->wclk_ext = of_property_read_bool(node,
- "csis-wclk");
-
- of_property_read_u32(node, "data-lanes",
- &state->num_lanes);
- of_node_put(node);
-
- return 0;
-}
-
-static int mipi_csis_pm_resume(struct device *dev, bool runtime);
-static const struct of_device_id mipi_csis_of_match[];
-
-/* register parent dev */
-static int mipi_csis_subdev_host(struct csi_state *state)
-{
- struct device_node *parent = state->dev->of_node;
- struct device_node *node, *port, *rem;
- int ret;
-
- /* Attach sensors linked to csi receivers */
- for_each_available_child_of_node(parent, node) {
- if (of_node_cmp(node->name, "port"))
- continue;
-
- /* The csi node can have only port subnode. */
- port = of_get_next_child(node, NULL);
- if (!port)
- continue;
- rem = of_graph_get_remote_port_parent(port);
- of_node_put(port);
- if (rem == NULL) {
- v4l2_info(&state->v4l2_dev,
- "Remote device at %s not found\n",
- port->full_name);
- return -1;
- }
-
- state->asd.match_type = V4L2_ASYNC_MATCH_OF;
- state->asd.match.of.node = rem;
- state->async_subdevs[0] = &state->asd;
-
- of_node_put(rem);
- break;
- }
-
- state->subdev_notifier.subdevs = state->async_subdevs;
- state->subdev_notifier.num_subdevs = 1;
- state->subdev_notifier.bound = subdev_notifier_bound;
-
- ret = v4l2_async_notifier_register(&state->v4l2_dev,
- &state->subdev_notifier);
- if (ret)
- dev_err(state->dev,
- "Error register async notifier regoster\n");
-
- return ret;
-}
-
-/* init subdev */
-static int mipi_csis_subdev_init(struct v4l2_subdev *mipi_sd,
- struct platform_device *pdev,
- const struct v4l2_subdev_ops *ops)
-{
- struct csi_state *state = platform_get_drvdata(pdev);
- int ret = 0;
-
- v4l2_subdev_init(mipi_sd, ops);
- mipi_sd->owner = THIS_MODULE;
- snprintf(mipi_sd->name, sizeof(mipi_sd->name), "%s.%d",
- CSIS_SUBDEV_NAME, state->index);
- mipi_sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
- mipi_sd->dev = &pdev->dev;
-
- state->csis_fmt = &mipi_csis_formats[0];
- state->format.code = mipi_csis_formats[0].code;
- state->format.width = MIPI_CSIS_DEF_PIX_WIDTH;
- state->format.height = MIPI_CSIS_DEF_PIX_HEIGHT;
-
- /* This allows to retrieve the platform device id by the host driver */
- v4l2_set_subdevdata(mipi_sd, pdev);
-
- ret = v4l2_async_register_subdev(mipi_sd);
- if (ret < 0)
- dev_err(&pdev->dev, "%s--Async register faialed, ret=%d\n", __func__, ret);
-
- return ret;
-}
-
-static int mipi_csis_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct v4l2_subdev *mipi_sd;
- struct resource *mem_res;
- struct csi_state *state;
- int ret = -ENOMEM;
-
- state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
- if (!state)
- return -ENOMEM;
-
- mutex_init(&state->lock);
- spin_lock_init(&state->slock);
-
- state->pdev = pdev;
- mipi_sd = &state->mipi_sd;
- state->dev = dev;
-
- ret = mipi_csis_parse_dt(pdev, state);
- if (ret < 0)
- return ret;
-
- if (state->num_lanes == 0 || state->num_lanes > state->max_num_lanes) {
- dev_err(dev, "Unsupported number of data lanes: %d (max. %d)\n",
- state->num_lanes, state->max_num_lanes);
- return -EINVAL;
- }
-
- mipi_csis_phy_init(state);
- mipi_csis_phy_reset(state);
-
- mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- state->regs = devm_ioremap_resource(dev, mem_res);
- if (IS_ERR(state->regs))
- return PTR_ERR(state->regs);
-
- state->irq = platform_get_irq(pdev, 0);
- if (state->irq < 0) {
- dev_err(dev, "Failed to get irq\n");
- return state->irq;
- }
-
- ret = mipi_csis_clk_get(state);
- if (ret < 0)
- return ret;
-
- mipi_csis_clk_enable(state);
-
- ret = devm_request_irq(dev, state->irq, mipi_csis_irq_handler,
- 0, dev_name(dev), state);
- if (ret) {
- dev_err(dev, "Interrupt request failed\n");
- goto e_clkdis;
- }
-
- /* First register a v4l2 device */
- ret = v4l2_device_register(dev, &state->v4l2_dev);
- if (ret) {
- v4l2_err(dev->driver,
- "Unable to register v4l2 device.\n");
- goto e_clkdis;
- }
- v4l2_info(&state->v4l2_dev, "mipi csi v4l2 device registered\n");
-
- /* .. and a pointer to the subdev. */
- platform_set_drvdata(pdev, state);
-
- ret = mipi_csis_subdev_init(&state->mipi_sd, pdev, &mipi_csis_subdev_ops);
- if (ret < 0)
- goto e_sd_mipi;
-
- memcpy(state->events, mipi_csis_events, sizeof(state->events));
-
- /* subdev host register */
- ret = mipi_csis_subdev_host(state);
- if (ret < 0)
- goto e_sd_host;
-
- pm_runtime_enable(dev);
- if (!pm_runtime_enabled(dev)) {
- ret = mipi_csis_pm_resume(dev, true);
- if (ret < 0)
- goto e_sd_host;
- }
-
- dev_info(&pdev->dev,
- "lanes: %d, hs_settle: %d, clk_settle: %d, wclk: %d, freq: %u\n",
- state->num_lanes, state->hs_settle, state->clk_settle,
- state->wclk_ext, state->clk_frequency);
- return 0;
-
-e_sd_host:
- v4l2_async_notifier_unregister(&state->subdev_notifier);
- v4l2_device_unregister(&state->v4l2_dev);
-e_sd_mipi:
- v4l2_async_unregister_subdev(&state->mipi_sd);
-e_clkdis:
- mipi_csis_clk_disable(state);
- return ret;
-}
-
-static int mipi_csis_pm_suspend(struct device *dev, bool runtime)
-{
- struct platform_device *pdev = to_platform_device(dev);
- struct csi_state *state = platform_get_drvdata(pdev);
- struct v4l2_subdev *mipi_sd = &state->mipi_sd;
- int ret = 0;
-
- v4l2_dbg(1, debug, mipi_sd, "%s: flags: 0x%x\n",
- __func__, state->flags);
-
- mutex_lock(&state->lock);
- if (state->flags & ST_POWERED) {
- mipi_csis_stop_stream(state);
- ret = regulator_disable(state->mipi_phy_regulator);
- if (ret)
- goto unlock;
- mipi_csis_clk_disable(state);
- state->flags &= ~ST_POWERED;
- if (!runtime)
- state->flags |= ST_SUSPENDED;
- }
- unlock:
- mutex_unlock(&state->lock);
- return ret ? -EAGAIN : 0;
-}
-
-static int mipi_csis_pm_resume(struct device *dev, bool runtime)
-{
- struct platform_device *pdev = to_platform_device(dev);
- struct csi_state *state = platform_get_drvdata(pdev);
- struct v4l2_subdev *mipi_sd = &state->mipi_sd;
- int ret = 0;
-
- v4l2_dbg(1, debug, mipi_sd, "%s: flags: 0x%x\n",
- __func__, state->flags);
-
- mutex_lock(&state->lock);
- if (!runtime && !(state->flags & ST_SUSPENDED))
- goto unlock;
-
- if (!(state->flags & ST_POWERED)) {
- ret = regulator_enable(state->mipi_phy_regulator);
- if (!ret) {
- state->flags |= ST_POWERED;
- } else {
- goto unlock;
- }
- mipi_csis_clk_enable(state);
- }
- if (state->flags & ST_STREAMING)
- mipi_csis_start_stream(state);
-
- state->flags &= ~ST_SUSPENDED;
- unlock:
- mutex_unlock(&state->lock);
- return ret ? -EAGAIN : 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int mipi_csis_suspend(struct device *dev)
-{
- return mipi_csis_pm_suspend(dev, false);
-}
-
-static int mipi_csis_resume(struct device *dev)
-{
- return mipi_csis_pm_resume(dev, false);
-}
-#endif
-
-static int mipi_csis_runtime_suspend(struct device *dev)
-{
- return mipi_csis_pm_suspend(dev, true);
-}
-
-static int mipi_csis_runtime_resume(struct device *dev)
-{
- return mipi_csis_pm_resume(dev, true);
-}
-
-static int mipi_csis_remove(struct platform_device *pdev)
-{
- struct csi_state *state = platform_get_drvdata(pdev);
-
- v4l2_async_unregister_subdev(&state->mipi_sd);
- v4l2_async_notifier_unregister(&state->subdev_notifier);
- v4l2_device_unregister(&state->v4l2_dev);
-
- pm_runtime_disable(&pdev->dev);
- mipi_csis_pm_suspend(&pdev->dev, true);
- mipi_csis_clk_disable(state);
- pm_runtime_set_suspended(&pdev->dev);
-
- return 0;
-}
-
-static const struct dev_pm_ops mipi_csis_pm_ops = {
- SET_RUNTIME_PM_OPS(mipi_csis_runtime_suspend, mipi_csis_runtime_resume,
- NULL)
- SET_SYSTEM_SLEEP_PM_OPS(mipi_csis_suspend, mipi_csis_resume)
-};
-
-static const struct of_device_id mipi_csis_of_match[] = {
- { .compatible = "fsl,imx7d-mipi-csi",},
- { /* sentinel */ },
-};
-MODULE_DEVICE_TABLE(of, mipi_csis_of_match);
-
-static struct platform_driver mipi_csis_driver = {
- .probe = mipi_csis_probe,
- .remove = mipi_csis_remove,
- .driver = {
- .of_match_table = mipi_csis_of_match,
- .name = CSIS_DRIVER_NAME,
- .owner = THIS_MODULE,
- .pm = &mipi_csis_pm_ops,
- },
-};
-
-module_platform_driver(mipi_csis_driver);
-
-MODULE_DESCRIPTION("Freescale MIPI-CSI2 receiver driver");
-MODULE_LICENSE("GPL");
+++ /dev/null
-/*
- * Copyright (C) 2014-2015 Freescale Semiconductor, 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/clk.h>
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/init.h>
-#include <linux/io.h>
-#include <linux/module.h>
-#include <linux/mfd/syscon.h>
-#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
-#include <linux/pm_runtime.h>
-#include <linux/regmap.h>
-#include <linux/media-bus-format.h>
-#include <media/v4l2-ioctl.h>
-#include <linux/videodev2.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ctrls.h>
-#include "mxc_vadc.h"
-
-/* Resource names for the VADC driver. */
-#define VAFE_REGS_ADDR_RES_NAME "vadc-vafe"
-#define VDEC_REGS_ADDR_RES_NAME "vadc-vdec"
-
-#define reg32_write(addr, val) __raw_writel(val, addr)
-#define reg32_read(addr) __raw_readl(addr)
-#define reg32setbit(addr, bitpos) \
- reg32_write((addr), (reg32_read((addr)) | (1<<(bitpos))))
-
-#define reg32clrbit(addr, bitpos) \
- reg32_write((addr), (reg32_read((addr)) & (0xFFFFFFFF ^ (1<<(bitpos)))))
-
-#define GPC_CNTR 0x00
-#define IMX6SX_GPC_CNTR_VADC_ANALOG_OFF_MASK BIT(17)
-#define IMX6SX_GPC_CNTR_VADC_POWER_DOWN_MASK BIT(18)
-
-void __iomem *vafe_regbase;
-void __iomem *vdec_regbase;
-
-
-/* List of input video formats supported. The video formats is corresponding
- * with v4l2 id in video_fmt
- */
-enum video_fmt_idx {
- VADC_NTSC = 0, /* Locked on (M) NTSC video signal. */
- VADC_PAL, /* (B, G, H, I, N)PAL video signal. */
-};
-
-/* Number of video standards supported (including 'not locked' signal). */
-#define VADC_STD_MAX (VADC_PAL + 1)
-
-/* Video format structure. */
-struct video_fmt{
- v4l2_std_id v4l2_std; /* 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 framerates;
-};
-
-/*
- * Maintains the information on the current state of the sensor.
- */
-struct vadc_state {
- struct v4l2_device v4l2_dev;
- struct v4l2_subdev sd;
- struct video_fmt *fmt;
-
- struct clk *vadc_clk;
- struct clk *csi_clk;
- struct regmap *gpr;
- void __iomem *gpc_reg;
-
- u32 vadc_in;
- u32 csi_id;
-};
-
-static int vadc_querystd(struct v4l2_subdev *sd, v4l2_std_id *std);
-
-/* Description of video formats supported.
- *
- * PAL: raw=720x625, active=720x576.
- * NTSC: raw=720x525, active=720x480.
- */
-static struct video_fmt video_fmts[] = {
- /* NTSC */
- {
- .v4l2_std = V4L2_STD_NTSC,
- .name = "NTSC",
- .raw_width = 720,
- .raw_height = 525,
- .active_width = 720,
- .active_height = 480,
- .framerates = 30,
- },
- /* (B, G, H, I, N) PAL */
- {
- .v4l2_std = V4L2_STD_PAL,
- .name = "PAL",
- .raw_width = 720,
- .raw_height = 625,
- .active_width = 720,
- .active_height = 576,
- .framerates = 25,
- },
-};
-
-static void afe_voltage_clampingmode(void)
-{
- reg32_write(AFE_CLAMP, 0x07);
- reg32_write(AFE_CLMPAMP, 0x60);
- reg32_write(AFE_CLMPDAT, 0xF0);
-}
-
-static void afe_alwayson_clampingmode(void)
-{
- reg32_write(AFE_CLAMP, 0x15);
- reg32_write(AFE_CLMPDAT, 0x08);
- reg32_write(AFE_CLMPAMP, 0x00);
-}
-
-static void afe_init(void)
-{
- pr_debug("%s\n", __func__);
-
- reg32_write(AFE_PDBUF, 0x1f);
- reg32_write(AFE_PDADC, 0x0f);
- reg32_write(AFE_PDSARH, 0x01);
- reg32_write(AFE_PDSARL, 0xff);
- reg32_write(AFE_PDADCRFH, 0x01);
- reg32_write(AFE_PDADCRFL, 0xff);
- reg32_write(AFE_ICTRL, 0x3a);
- reg32_write(AFE_ICTLSTG, 0x1e);
-
- reg32_write(AFE_RCTRLSTG, 0x1e);
- reg32_write(AFE_INPBUF, 0x035);
- reg32_write(AFE_INPFLT, 0x02);
- reg32_write(AFE_ADCDGN, 0x40);
- reg32_write(AFE_TSTSEL, 0x10);
-
- reg32_write(AFE_ACCTST, 0x07);
-
- reg32_write(AFE_BGREG, 0x08);
-
- reg32_write(AFE_ADCGN, 0x09);
-
- /* set current controlled clamping
- * always on, low current */
- reg32_write(AFE_CLAMP, 0x11);
- reg32_write(AFE_CLMPAMP, 0x08);
-}
-
-static void vdec_mode_timing_init(int std)
-{
- if (std == V4L2_STD_NTSC) {
- /* NTSC 720x480 */
- reg32_write(VDEC_HACTS, 0x66);
- reg32_write(VDEC_HACTE, 0x24);
-
- reg32_write(VDEC_VACTS, 0x29);
- reg32_write(VDEC_VACTE, 0x04);
-
- /* set V Position */
- reg32_write(VDEC_VRTPOS, 0x2);
- } else if (std == V4L2_STD_PAL) {
- /* PAL 720x576 */
- reg32_write(VDEC_HACTS, 0x66);
- reg32_write(VDEC_HACTE, 0x24);
-
- reg32_write(VDEC_VACTS, 0x29);
- reg32_write(VDEC_VACTE, 0x04);
-
- /* set V Position */
- reg32_write(VDEC_VRTPOS, 0x6);
- } else
- pr_debug("Error not support video mode\n");
-
- /* set H Position */
- reg32_write(VDEC_HZPOS, 0x60);
-
- /* set H ignore start */
- reg32_write(VDEC_HSIGS, 0xf8);
-
- /* set H ignore end */
- reg32_write(VDEC_HSIGE, 0x18);
-}
-
-/*
-* vdec_init()
-* Initialises the VDEC registers
-* Returns: nothing
-*/
-static void vdec_init(struct vadc_state *vadc)
-{
- v4l2_std_id std;
-
- pr_debug("%s\n", __func__);
-
- /* Get work mode PAL or NTSC */
- vadc_querystd(&vadc->sd, &std);
-
- vdec_mode_timing_init(std);
-
- /* vcr detect threshold high, automatic detections */
- reg32_write(VDEC_VSCON2, 0);
-
- reg32_write(VDEC_BASE + 0x110, 0x01);
-
- /* set the noramp mode on the Hloop PLL. */
- reg32_write(VDEC_BASE+(0x14*4), 0x10);
-
- /* set the YC relative delay.*/
- reg32_write(VDEC_YCDEL, 0x90);
-
- /* setup the Hpll */
- reg32_write(VDEC_BASE+(0x13*4), 0x13);
-
- /* setup the 2d comb */
- /* set the gain of the Hdetail output to 3
- * set the notch alpha gain to 1 */
- reg32_write(VDEC_CFC2, 0x34);
-
- /* setup various 2d comb bits.*/
- reg32_write(VDEC_BASE+(0x02*4), 0x01);
- reg32_write(VDEC_BASE+(0x03*4), 0x18);
- reg32_write(VDEC_BASE+(0x04*4), 0x34);
-
- /* set the start of the burst gate */
- reg32_write(VDEC_BRSTGT, 0x30);
-
- /* set 1f motion gain */
- reg32_write(VDEC_BASE+(0x0f*4), 0x20);
-
- /* set the 1F chroma motion detector thresh
- * for colour reverse detection */
- reg32_write(VDEC_THSH1, 0x02);
- reg32_write(VDEC_BASE+(0x4a*4), 0x20);
- reg32_write(VDEC_BASE+(0x4b*4), 0x08);
-
- reg32_write(VDEC_BASE+(0x4c*4), 0x08);
-
- /* set the threshold for the narrow/wide adaptive chroma BW */
- reg32_write(VDEC_BASE+(0x20*4), 0x20);
-
- /* turn up the colour with the new colour gain reg */
- /* hue: */
- reg32_write(VDEC_HUE, 0x00);
-
- /* cbgain: 22 B4 */
- reg32_write(VDEC_CBGN, 0xb4);
- /* cr gain 80 */
- reg32_write(VDEC_CRGN, 0x80);
- /* luma gain (contrast) */
- reg32_write(VDEC_CNTR, 0x80);
-
- /* setup the signed black level register, brightness */
- reg32_write(VDEC_BRT, 0x00);
-
- /* filter the standard detection
- * enable the comb for the ntsc443 */
- reg32_write(VDEC_STDDBG, 0x20);
-
- /* setup chroma kill thresh for no chroma */
- reg32_write(VDEC_CHBTH, 0x0);
-
- /* set chroma loop to wider BW
- * no set it to normal BW. i fixed the bw problem.*/
- reg32_write(VDEC_YCDEL, 0x00);
-
- /* set the compensation in the chroma loop for the Hloop
- * set the ratio for the nonarithmetic 3d comb modes.*/
- reg32_write(VDEC_BASE + (0x1d*4), 0x90);
-
- /* set the threshold for the nonarithmetic mode for the 2d comb
- * the higher the value the more Fc Fh offset
- * we will tolerate before turning off the comb. */
- reg32_write(VDEC_BASE + (0x33*4), 0xa0);
-
- /* setup the bluescreen output colour */
- reg32_write(VDEC_BASE + (0x3d*4), 35);
- reg32_write(VDEC_BLSCRCR, 114);
- reg32_write(VDEC_BLSCRCB, 212);
-
- /* disable the active blanking */
- reg32_write(VDEC_BASE + (0x15*4), 0x02);
-
- /* setup the luma agc for automatic gain. */
- reg32_write(VDEC_LMAGC2, 0x5e);
- reg32_write(VDEC_LMAGC1, 0x81);
-
- /* setup chroma agc */
- reg32_write(VDEC_CHAGC2, 0xa0);
- reg32_write(VDEC_CHAGC1, 0x01);
-
- /* setup the MV thresh lower nibble
- * setup the sync top cap, upper nibble */
- reg32_write(VDEC_BASE + (0x3a*4), 0x80);
- reg32_write(VDEC_SHPIMP, 0x00);
-
- /* setup the vsync block */
- reg32_write(VDEC_VSCON1, 0x87);
-
- /* set the nosignal threshold
- * set the vsync threshold */
- reg32_write(VDEC_VSSGTH, 0x35);
-
- /* set length for min hphase filter
- * (or saturate limit if saturate is chosen) */
- reg32_write(VDEC_BASE + (0x45*4), 0x40);
-
- /* enable the internal resampler,
- * select min filter not saturate for
- * hphase noise filter for vcr detect.
- * enable vcr pause mode different field lengths */
- reg32_write(VDEC_BASE + (0x46*4), 0x90);
-
- /* disable VCR detection, lock to the Hsync rather than the Vsync */
- reg32_write(VDEC_VSCON2, 0x04);
-
- /* set tiplevel goal for dc clamp. */
- reg32_write(VDEC_BASE + (0x3c*4), 0xB0);
-
- /* override SECAM detection and force SECAM off */
- reg32_write(VDEC_BASE + (0x2f*4), 0x20);
-
- /* Set r3d_hardblend in 3D control2 reg */
- reg32_write(VDEC_BASE + (0x0c*4), 0x04);
-}
-
-/* set Input selector & input pull-downs */
-static void vadc_s_routing(int vadc_in)
-{
- switch (vadc_in) {
- case 0:
- reg32_write(AFE_INPFLT, 0x02);
- reg32_write(AFE_OFFDRV, 0x00);
- reg32_write(AFE_INPCONFIG, 0x1e);
- break;
- case 1:
- reg32_write(AFE_INPFLT, 0x02);
- reg32_write(AFE_OFFDRV, 0x00);
- reg32_write(AFE_INPCONFIG, 0x2d);
- break;
- case 2:
- reg32_write(AFE_INPFLT, 0x02);
- reg32_write(AFE_OFFDRV, 0x00);
- reg32_write(AFE_INPCONFIG, 0x4b);
- break;
- case 3:
- reg32_write(AFE_INPFLT, 0x02);
- reg32_write(AFE_OFFDRV, 0x00);
- reg32_write(AFE_INPCONFIG, 0x87);
- break;
- default:
- pr_debug("error video input %d\n", vadc_in);
- }
-}
-
-static void vadc_power_up(struct vadc_state *state)
-{
- /* Power on vadc analog */
- reg32clrbit(state->gpc_reg + GPC_CNTR, 17);
-
- /* Power down vadc ext power */
- reg32clrbit(state->gpc_reg + GPC_CNTR, 18);
-
- /* software reset afe */
- regmap_update_bits(state->gpr, IOMUXC_GPR1,
- IMX6SX_GPR1_VADC_SW_RST_MASK,
- IMX6SX_GPR1_VADC_SW_RST_RESET);
-
- msleep(10);
-
- /* clock config for vadc */
- reg32_write(VDEC_BASE + 0x320, 0xe3);
- reg32_write(VDEC_BASE + 0x324, 0x38);
- reg32_write(VDEC_BASE + 0x328, 0x8e);
- reg32_write(VDEC_BASE + 0x32c, 0x23);
-
- /* Release reset bit */
- regmap_update_bits(state->gpr, IOMUXC_GPR1,
- IMX6SX_GPR1_VADC_SW_RST_MASK,
- IMX6SX_GPR1_VADC_SW_RST_RELEASE);
-
- /* Power on vadc ext power */
- reg32setbit(state->gpc_reg + GPC_CNTR, 18);
-}
-
-static void vadc_power_down(struct vadc_state *state)
-{
- /* Power down vadc analog */
- reg32setbit(state->gpc_reg + GPC_CNTR, 17);
-
- /* Power down vadc ext power */
- reg32clrbit(state->gpc_reg + GPC_CNTR, 18);
-
-}
-static void vadc_init(struct vadc_state *vadc)
-{
- pr_debug("%s\n", __func__);
-
- vadc_power_up(vadc);
-
- afe_init();
-
- /* select Video Input 0-3 */
- vadc_s_routing(vadc->vadc_in);
-
- afe_voltage_clampingmode();
-
- vdec_init(vadc);
-
- /*
- * current control loop will move sinewave input off below
- * the bottom of the signal range visible
- * when the testbus is viewed as magnitude,
- * so have to break before this point while capturing ENOB data:
- */
- afe_alwayson_clampingmode();
-}
-
-static inline struct vadc_state *to_state(struct v4l2_subdev *sd)
-{
- return container_of(sd, struct vadc_state, sd);
-}
-
-static int vadc_g_std(struct v4l2_subdev *sd, v4l2_std_id *std)
-{
- struct vadc_state *state = to_state(sd);
-
- *std = state->fmt->v4l2_std;
- return 0;
-}
-
-/*!
- * 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 int vadc_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
-{
- struct vadc_state *state = to_state(sd);
- int mod;
- int idx;
- int i;
-
- /* Read auto mode detected result */
- printk(KERN_INFO"wait vadc auto detect video mode....\n");
- for (i = 0; i < 10; i++) {
- msleep(200);
- mod = reg32_read(VDEC_VIDMOD);
- /* Check video signal states */
- if ((mod & VDEC_VIDMOD_SIGNAL_MASK)
- == VDEC_VIDMOD_SIGNAL_DETECT)
- break;
- }
- if (i == 10)
- printk(KERN_INFO"Timeout detect video signal mod=0x%x\n", mod);
-
- if ((mod & VDEC_VIDMOD_PAL_MASK) || (mod & VDEC_VIDMOD_M625_MASK))
- idx = VADC_PAL;
- else
- idx = VADC_NTSC;
-
- *std = video_fmts[idx].v4l2_std;
- state->fmt = &video_fmts[idx];
-
- printk(KERN_INFO"video mode %s\n", video_fmts[idx].name);
- return 0;
-}
-
-static int vadc_enum_mbus_code(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_mbus_code_enum *code)
-{
- /* support only one format */
- if (code->pad || code->index >= 1)
- return -EINVAL;
-
- code->code = MEDIA_BUS_FMT_AYUV8_1X32;
- return 0;
-}
-
-static int vadc_get_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_format *format)
-{
- struct vadc_state *state = to_state(sd);
- struct v4l2_mbus_framefmt *fmt = &format->format;
-
- if (format->pad)
- return -EINVAL;
-
- fmt->code = MEDIA_BUS_FMT_AYUV8_1X32;
- fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
- fmt->field = V4L2_FIELD_INTERLACED;
- fmt->width = 720;
- fmt->height = state->fmt->v4l2_std & V4L2_STD_NTSC ? 480 : 576;
-
- return 0;
-}
-
-static int vadc_set_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_format *format)
-{
- return vadc_get_fmt(sd, cfg, format);
-}
-
-static int vadc_enum_framesizes(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_frame_size_enum *fse)
-{
- struct vadc_state *state = to_state(sd);
- if (fse->index >= 1)
- return -EINVAL;
-
- fse->min_width = state->fmt->active_width;
- fse->max_width = state->fmt->active_width;
- fse->min_height = state->fmt->active_height;
- fse->max_height = state->fmt->active_height;
-
- return 0;
-}
-static int vadc_enum_frameintervals(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_frame_interval_enum *fie)
-{
- struct vadc_state *state = to_state(sd);
-
- if (fie->index < 0 || fie->index >= 1)
- return -EINVAL;
-
- fie->interval.numerator = 1;
-
- fie->interval.denominator = state->fmt->framerates;
-
- return 0;
-}
-
-static int vadc_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
-{
- struct vadc_state *state = to_state(sd);
-
- if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- if (parms->parm.capture.timeperframe.denominator
- != state->fmt->framerates)
- parms->parm.capture.timeperframe.denominator
- = state->fmt->framerates;
-
- return 0;
-}
-
-static const struct v4l2_subdev_video_ops vadc_video_ops = {
- .querystd = vadc_querystd,
- .s_parm = vadc_s_parm,
- .g_std = vadc_g_std,
-};
-
-
-static const struct v4l2_subdev_pad_ops vadc_pad_ops = {
- .get_fmt = vadc_get_fmt,
- .set_fmt = vadc_set_fmt,
- .enum_mbus_code = vadc_enum_mbus_code,
- .enum_frame_size = vadc_enum_framesizes,
- .enum_frame_interval = vadc_enum_frameintervals,
-};
-
-static const struct v4l2_subdev_ops vadc_ops = {
- .video = &vadc_video_ops,
- .pad = &vadc_pad_ops,
-};
-
-static const struct of_device_id fsl_vadc_dt_ids[] = {
- { .compatible = "fsl,imx6sx-vadc", },
- { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, fsl_vadc_dt_ids);
-
-static int vadc_of_init(struct platform_device *pdev)
-{
- struct device_node *np = pdev->dev.of_node;
- struct device_node *gpc_np;
- struct vadc_state *state = platform_get_drvdata(pdev);
- int csi_id;
- int ret;
-
- /* Get csi_id to setting vadc to csi mux in gpr */
- ret = of_property_read_u32(np, "csi_id", &csi_id);
- if (ret) {
- dev_err(&pdev->dev, "failed to read of property csi_id\n");
- return ret;
- }
-
- state->csi_id = csi_id;
-
- /* remap GPR register */
- state->gpr = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
- "gpr");
- if (IS_ERR(state->gpr)) {
- dev_dbg(&pdev->dev, "can not get gpr\n");
- return -ENOMEM;
- }
-
- /* Configuration vadc-to-csi 0 or 1 */
- if (csi_id) {
- regmap_update_bits(state->gpr, IOMUXC_GPR5,
- IMX6SX_GPR5_CSI2_MUX_CTRL_MASK,
- IMX6SX_GPR5_CSI2_MUX_CTRL_CVD);
- } else {
- regmap_update_bits(state->gpr, IOMUXC_GPR5,
- IMX6SX_GPR5_CSI1_MUX_CTRL_MASK,
- IMX6SX_GPR5_CSI1_MUX_CTRL_CVD);
- }
-
- /* Get default vadc_in number */
- ret = of_property_read_u32(np, "vadc_in", &state->vadc_in);
- if (ret) {
- dev_err(&pdev->dev, "failed to read of property vadc_in\n");
- return ret;
- }
-
- /* map GPC register */
- gpc_np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-gpc");
- state->gpc_reg = of_iomap(gpc_np, 0);
- if (!state->gpc_reg) {
- dev_err(&pdev->dev, "ioremap failed with gpc base\n");
- goto error;
- }
-
- return ret;
-
-error:
- iounmap(state->gpc_reg);
- return ret;
-}
-
-static void vadc_v4l2_subdev_init(struct v4l2_subdev *sd,
- struct platform_device *pdev,
- const struct v4l2_subdev_ops *ops)
-{
- struct vadc_state *state = platform_get_drvdata(pdev);
- int ret = 0;
-
- v4l2_subdev_init(sd, ops);
- sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
- sd->owner = pdev->dev.driver->owner;
- sd->dev = &pdev->dev;
-
- /* initialize name */
- snprintf(sd->name, sizeof(sd->name), "%s",
- pdev->dev.driver->name);
-
- v4l2_set_subdevdata(sd, state);
-
- ret = v4l2_async_register_subdev(sd);
- if (ret < 0)
- dev_err(&pdev->dev, "%s--Async register faialed, ret=%d\n", __func__, ret);
-}
-
-static int vadc_probe(struct platform_device *pdev)
-{
- struct vadc_state *state;
- struct v4l2_subdev *sd;
- struct resource *res;
- int ret = 0;
-
- state = devm_kzalloc(&pdev->dev, sizeof(struct vadc_state), GFP_KERNEL);
- if (!state) {
- dev_err(&pdev->dev, "Cannot allocate device data\n");
- return -ENOMEM;
- }
-
- /* Set initial values for the sensor struct. */
- state->fmt = &video_fmts[VADC_NTSC];
-
- sd = &state->sd;
-
- /* map vafe address */
- res = platform_get_resource_byname(pdev,
- IORESOURCE_MEM, VAFE_REGS_ADDR_RES_NAME);
- if (!res) {
- dev_err(&pdev->dev, "No vafe base address found.\n");
- return -ENOMEM;
- }
- vafe_regbase = devm_ioremap_resource(&pdev->dev, res);
- if (!vafe_regbase) {
- dev_err(&pdev->dev, "ioremap failed with vafe base\n");
- return -ENOMEM;
- }
-
- /* map vdec address */
- res = platform_get_resource_byname(pdev,
- IORESOURCE_MEM, VDEC_REGS_ADDR_RES_NAME);
- if (!res) {
- dev_err(&pdev->dev, "No vdec base address found.\n");
- return -ENODEV;
- }
- vdec_regbase = devm_ioremap_resource(&pdev->dev, res);
- if (!vdec_regbase) {
- dev_err(&pdev->dev, "ioremap failed with vdec base\n");
- return -ENOMEM;
- }
-
- /* Get clock */
- state->vadc_clk = devm_clk_get(&pdev->dev, "vadc");
- if (IS_ERR(state->vadc_clk)) {
- ret = PTR_ERR(state->vadc_clk);
- return ret;
- }
-
- state->csi_clk = devm_clk_get(&pdev->dev, "csi");
- if (IS_ERR(state->csi_clk)) {
- ret = PTR_ERR(state->csi_clk);
- return ret;
- }
-
- /* clock */
- clk_prepare_enable(state->csi_clk);
- clk_prepare_enable(state->vadc_clk);
-
- platform_set_drvdata(pdev, state);
-
- vadc_v4l2_subdev_init(sd, pdev, &vadc_ops);
-
- pm_runtime_enable(&pdev->dev);
-
- pm_runtime_get_sync(&pdev->dev);
- /* Init VADC */
- ret = vadc_of_init(pdev);
- if (ret < 0)
- goto err;
- vadc_init(state);
-
- pr_info("vadc driver loaded\n");
-
- return 0;
-err:
- pm_runtime_put_sync(&pdev->dev);
- pm_runtime_disable(&pdev->dev);
- v4l2_async_unregister_subdev(&state->sd);
- clk_disable_unprepare(state->csi_clk);
- clk_disable_unprepare(state->vadc_clk);
- return ret;
-}
-
-static int vadc_remove(struct platform_device *pdev)
-{
- struct vadc_state *state = platform_get_drvdata(pdev);
-
- pm_runtime_put_sync(&pdev->dev);
- pm_runtime_disable(&pdev->dev);
- v4l2_async_unregister_subdev(&state->sd);
- clk_disable_unprepare(state->csi_clk);
- clk_disable_unprepare(state->vadc_clk);
-
- vadc_power_down(state);
- return true;
-}
-
-static int vadc_suspend(struct device *dev)
-{
- struct platform_device *pdev = to_platform_device(dev);
- struct vadc_state *state = platform_get_drvdata(pdev);
-
- clk_disable(state->csi_clk);
- clk_disable(state->vadc_clk);
-
- vadc_power_down(state);
-
- return 0;
-}
-
-static int vadc_resume(struct device *dev)
-{
- struct platform_device *pdev = to_platform_device(dev);
- struct vadc_state *state = platform_get_drvdata(pdev);
-
- clk_enable(state->csi_clk);
- clk_enable(state->vadc_clk);
-
- vadc_init(state);
- return 0;
-}
-
-static const struct dev_pm_ops vadc_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(vadc_suspend, vadc_resume)
-};
-
-static struct platform_driver vadc_driver = {
- .driver = {
- .name = "fsl_vadc",
- .of_match_table = of_match_ptr(fsl_vadc_dt_ids),
- .pm = &vadc_pm_ops,
- },
- .probe = vadc_probe,
- .remove = vadc_remove,
-};
-
-module_platform_driver(vadc_driver);
-
-MODULE_AUTHOR("Freescale Semiconductor, Inc.");
-MODULE_DESCRIPTION("fsl VADC/VDEC driver");
-MODULE_LICENSE("GPL");
+++ /dev/null
-/*
- * Copyright (C) 2011-2015 Freescale Semiconductor, Inc.
- *
- * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-#ifndef MXC_VDEC_H
-#define MXC_VDEC_H
-
-/*** define base address ***/
-#define VDEC_BASE vdec_regbase
-#define AFE_BASE vafe_regbase
-
-/* AFE - Register offsets */
-#define AFE_BLOCK_ID_OFFSET 0x00000000
-#define AFE_PDBUF_OFFSET 0x00000004
-#define AFE_SWRST_OFFSET 0x00000008
-#define AFE_TSTSEL_OFFSET 0x0000000c
-#define AFE_TSTMSC_OFFSET 0x00000010
-#define AFE_ENPADIO_OFFSET 0x00000014
-#define AFE_BGREG_OFFSET 0x00000018
-#define AFE_ACCESSAR_ID_OFFSET 0x00000400
-#define AFE_PDADC_OFFSET 0x00000404
-#define AFE_PDSARH_OFFSET 0x00000408
-#define AFE_PDSARL_OFFSET 0x0000040C
-#define AFE_PDADCRFH_OFFSET 0x00000410
-#define AFE_PDADCRFL_OFFSET 0x00000414
-#define AFE_ACCTST_OFFSET 0x00000418
-#define AFE_ADCGN_OFFSET 0x0000041C
-#define AFE_ICTRL_OFFSET 0x00000420
-#define AFE_ICTLSTG_OFFSET 0x00000424
-#define AFE_RCTRLSTG_OFFSET 0x00000428
-#define AFE_TCTRLSTG_OFFSET 0x0000042c
-#define AFE_REFMOD_OFFSET 0x00000430
-#define AFE_REFTRIML_OFFSET 0x00000434
-#define AFE_REFTRIMH_OFFSET 0x00000438
-#define AFE_ADCR_OFFSET 0x0000043c
-#define AFE_DUMMY0_OFFSET 0x00000440
-#define AFE_DUMMY1_OFFSET 0x00000444
-#define AFE_DUMMY2_OFFSET 0x00000448
-#define AFE_DACAMP_OFFSET 0x0000044c
-#define AFE_CLMPTST_OFFSET 0x00000450
-#define AFE_CLMPDAT_OFFSET 0x00000454
-#define AFE_CLMPAMP_OFFSET 0x00000458
-#define AFE_CLAMP_OFFSET 0x0000045c
-#define AFE_INPBUF_OFFSET 0x00000460
-#define AFE_INPFLT_OFFSET 0x00000464
-#define AFE_ADCDGN_OFFSET 0x00000468
-#define AFE_OFFDRV_OFFSET 0x0000046c
-#define AFE_INPCONFIG_OFFSET 0x00000470
-#define AFE_PROGDELAY_OFFSET 0x00000474
-#define AFE_ADCOMT_OFFSET 0x00000478
-#define AFE_ALGDELAY_OFFSET 0x0000047c
-#define AFE_ACC_ID_OFFSET 0x00000800
-#define AFE_ACCSTA_OFFSET 0x00000804
-#define AFE_ACCNOSLI_OFFSET 0x00000808
-#define AFE_ACCCALCON_OFFSET 0x0000080c
-#define AFE_BWEWRICTRL_OFFSET 0x00000810
-#define AFE_SELSLI_OFFSET 0x00000814
-#define AFE_SELBYT_OFFSET 0x00000818
-#define AFE_REDVAL_OFFSET 0x00000820
-#define AFE_WRIBYT_OFFSET 0x00000824
-
-/* AFE Register per module */
-#define AFE_BLOCK_ID (AFE_BASE + AFE_BLOCK_ID_OFFSET)
-#define AFE_PDBUF (AFE_BASE + AFE_PDBUF_OFFSET)
-#define AFE_SWRST (AFE_BASE + AFE_SWRST_OFFSET)
-#define AFE_TSTSEL (AFE_BASE + AFE_TSTSEL_OFFSET)
-#define AFE_TSTMSC (AFE_BASE + AFE_TSTMSC_OFFSET)
-#define AFE_ENPADIO (AFE_BASE + AFE_ENPADIO_OFFSET)
-#define AFE_BGREG (AFE_BASE + AFE_BGREG_OFFSET)
-#define AFE_ACCESSAR_ID (AFE_BASE + AFE_ACCESSAR_ID_OFFSET)
-#define AFE_PDADC (AFE_BASE + AFE_PDADC_OFFSET)
-#define AFE_PDSARH (AFE_BASE + AFE_PDSARH_OFFSET)
-#define AFE_PDSARL (AFE_BASE + AFE_PDSARL_OFFSET)
-#define AFE_PDADCRFH (AFE_BASE + AFE_PDADCRFH_OFFSET)
-#define AFE_PDADCRFL (AFE_BASE + AFE_PDADCRFL_OFFSET)
-#define AFE_ACCTST (AFE_BASE + AFE_ACCTST_OFFSET)
-#define AFE_ADCGN (AFE_BASE + AFE_ADCGN_OFFSET)
-#define AFE_ICTRL (AFE_BASE + AFE_ICTRL_OFFSET)
-#define AFE_ICTLSTG (AFE_BASE + AFE_ICTLSTG_OFFSET)
-#define AFE_RCTRLSTG (AFE_BASE + AFE_RCTRLSTG_OFFSET)
-#define AFE_TCTRLSTG (AFE_BASE + AFE_TCTRLSTG_OFFSET)
-#define AFE_REFMOD (AFE_BASE + AFE_REFMOD_OFFSET)
-#define AFE_REFTRIML (AFE_BASE + AFE_REFTRIML_OFFSET)
-#define AFE_REFTRIMH (AFE_BASE + AFE_REFTRIMH_OFFSET)
-#define AFE_ADCR (AFE_BASE + AFE_ADCR_OFFSET)
-#define AFE_DUMMY0 (AFE_BASE + AFE_DUMMY0_OFFSET)
-#define AFE_DUMMY1 (AFE_BASE + AFE_DUMMY1_OFFSET)
-#define AFE_DUMMY2 (AFE_BASE + AFE_DUMMY2_OFFSET)
-#define AFE_DACAMP (AFE_BASE + AFE_DACAMP_OFFSET)
-#define AFE_CLMPTST (AFE_BASE + AFE_CLMPTST_OFFSET)
-#define AFE_CLMPDAT (AFE_BASE + AFE_CLMPDAT_OFFSET)
-#define AFE_CLMPAMP (AFE_BASE + AFE_CLMPAMP_OFFSET)
-#define AFE_CLAMP (AFE_BASE + AFE_CLAMP_OFFSET)
-#define AFE_INPBUF (AFE_BASE + AFE_INPBUF_OFFSET)
-#define AFE_INPFLT (AFE_BASE + AFE_INPFLT_OFFSET)
-#define AFE_ADCDGN (AFE_BASE + AFE_ADCDGN_OFFSET)
-#define AFE_OFFDRV (AFE_BASE + AFE_OFFDRV_OFFSET)
-#define AFE_INPCONFIG (AFE_BASE + AFE_INPCONFIG_OFFSET)
-#define AFE_PROGDELAY (AFE_BASE + AFE_PROGDELAY_OFFSET)
-#define AFE_ADCOMT (AFE_BASE + AFE_ADCOMT_OFFSET)
-#define AFE_ALGDELAY (AFE_BASE + AFE_ALGDELAY_OFFSET)
-#define AFE_ACC_ID (AFE_BASE + AFE_ACC_ID_OFFSET)
-#define AFE_ACCSTA (AFE_BASE + AFE_ACCSTA_OFFSET)
-#define AFE_ACCNOSLI (AFE_BASE + AFE_ACCNOSLI_OFFSET)
-#define AFE_ACCCALCON (AFE_BASE + AFE_ACCCALCON_OFFSET)
-#define AFE_BWEWRICTRL (AFE_BASE + AFE_BWEWRICTRL_OFFSET)
-#define AFE_SELSLI (AFE_BASE + AFE_SELSLI_OFFSET)
-#define AFE_SELBYT (AFE_BASE + AFE_SELBYT_OFFSET)
-#define AFE_REDVAL (AFE_BASE + AFE_REDVAL_OFFSET)
-#define AFE_WRIBYT (AFE_BASE + AFE_WRIBYT_OFFSET)
-
-/* VDEC - Register offsets */
-#define VDEC_CFC1_OFFSET 0x00000000
-#define VDEC_CFC2_OFFSET 0x00000004
-#define VDEC_BRSTGT_OFFSET 0x00000024
-#define VDEC_HZPOS_OFFSET 0x00000040
-#define VDEC_VRTPOS_OFFSET 0x00000044
-#define VDEC_HVSHIFT_OFFSET 0x00000054
-#define VDEC_HSIGS_OFFSET 0x00000058
-#define VDEC_HSIGE_OFFSET 0x0000005C
-#define VDEC_VSCON1_OFFSET 0x00000060
-#define VDEC_VSCON2_OFFSET 0x00000064
-#define VDEC_YCDEL_OFFSET 0x0000006C
-#define VDEC_AFTCLP_OFFSET 0x00000070
-#define VDEC_DCOFF_OFFSET 0x00000078
-#define VDEC_CSID_OFFSET 0x00000084
-#define VDEC_CBGN_OFFSET 0x00000088
-#define VDEC_CRGN_OFFSET 0x0000008C
-#define VDEC_CNTR_OFFSET 0x00000090
-#define VDEC_BRT_OFFSET 0x00000094
-#define VDEC_HUE_OFFSET 0x00000098
-#define VDEC_CHBTH_OFFSET 0x0000009C
-#define VDEC_SHPIMP_OFFSET 0x000000A4
-#define VDEC_CHPLLIM_OFFSET 0x000000A8
-#define VDEC_VIDMOD_OFFSET 0x000000AC
-#define VDEC_VIDSTS_OFFSET 0x000000B0
-#define VDEC_NOISE_OFFSET 0x000000B4
-#define VDEC_STDDBG_OFFSET 0x000000B8
-#define VDEC_MANOVR_OFFSET 0x000000BC
-#define VDEC_VSSGTH_OFFSET 0x000000C8
-#define VDEC_DBGFBH_OFFSET 0x000000D0
-#define VDEC_DBGFBL_OFFSET 0x000000D4
-#define VDEC_HACTS_OFFSET 0x000000D8
-#define VDEC_HACTE_OFFSET 0x000000DC
-#define VDEC_VACTS_OFFSET 0x000000E0
-#define VDEC_VACTE_OFFSET 0x000000E4
-#define VDEC_HSTIP_OFFSET 0x000000EC
-#define VDEC_BLSCRY_OFFSET 0x000000F4
-#define VDEC_BLSCRCR_OFFSET 0x000000F8
-#define VDEC_BLSCRCB_OFFSET 0x000000FC
-#define VDEC_LMAGC1_OFFSET 0x00000100
-#define VDEC_LMAGC2_OFFSET 0x00000104
-#define VDEC_CHAGC1_OFFSET 0x00000108
-#define VDEC_CHAGC2_OFFSET 0x0000010C
-#define VDEC_MINTH_OFFSET 0x00000114
-#define VDEC_VFRQOH_OFFSET 0x0000011C
-#define VDEC_VFRQOL_OFFSET 0x00000120
-#define VDEC_THSH1_OFFSET 0x00000124
-#define VDEC_THSH2_OFFSET 0x00000128
-#define VDEC_NCHTH_OFFSET 0x0000012C
-#define VDEC_TH1F_OFFSET 0x00000130
-
-/* VDEC Register per module */
-#define VDEC_CFC1 (VDEC_BASE + VDEC_CFC1_OFFSET)
-#define VDEC_CFC2 (VDEC_BASE + VDEC_CFC2_OFFSET)
-#define VDEC_BRSTGT (VDEC_BASE + VDEC_BRSTGT_OFFSET)
-#define VDEC_HZPOS (VDEC_BASE + VDEC_HZPOS_OFFSET)
-#define VDEC_VRTPOS (VDEC_BASE + VDEC_VRTPOS_OFFSET)
-#define VDEC_HVSHIFT (VDEC_BASE + VDEC_HVSHIFT_OFFSET)
-#define VDEC_HSIGS (VDEC_BASE + VDEC_HSIGS_OFFSET)
-#define VDEC_HSIGE (VDEC_BASE + VDEC_HSIGE_OFFSET)
-#define VDEC_VSCON1 (VDEC_BASE + VDEC_VSCON1_OFFSET)
-#define VDEC_VSCON2 (VDEC_BASE + VDEC_VSCON2_OFFSET)
-#define VDEC_YCDEL (VDEC_BASE + VDEC_YCDEL_OFFSET)
-#define VDEC_AFTCLP (VDEC_BASE + VDEC_AFTCLP_OFFSET)
-#define VDEC_DCOFF (VDEC_BASE + VDEC_DCOFF_OFFSET)
-#define VDEC_CSID (VDEC_BASE + VDEC_CSID_OFFSET)
-#define VDEC_CBGN (VDEC_BASE + VDEC_CBGN_OFFSET)
-#define VDEC_CRGN (VDEC_BASE + VDEC_CRGN_OFFSET)
-#define VDEC_CNTR (VDEC_BASE + VDEC_CNTR_OFFSET)
-#define VDEC_BRT (VDEC_BASE + VDEC_BRT_OFFSET)
-#define VDEC_HUE (VDEC_BASE + VDEC_HUE_OFFSET)
-#define VDEC_CHBTH (VDEC_BASE + VDEC_CHBTH_OFFSET)
-#define VDEC_SHPIMP (VDEC_BASE + VDEC_SHPIMP_OFFSET)
-#define VDEC_CHPLLIM (VDEC_BASE + VDEC_CHPLLIM_OFFSET)
-#define VDEC_VIDMOD (VDEC_BASE + VDEC_VIDMOD_OFFSET)
-#define VDEC_VIDSTS (VDEC_BASE + VDEC_VIDSTS_OFFSET)
-#define VDEC_NOISE (VDEC_BASE + VDEC_NOISE_OFFSET)
-#define VDEC_STDDBG (VDEC_BASE + VDEC_STDDBG_OFFSET)
-#define VDEC_MANOVR (VDEC_BASE + VDEC_MANOVR_OFFSET)
-#define VDEC_VSSGTH (VDEC_BASE + VDEC_VSSGTH_OFFSET)
-#define VDEC_DBGFBH (VDEC_BASE + VDEC_DBGFBH_OFFSET)
-#define VDEC_DBGFBL (VDEC_BASE + VDEC_DBGFBL_OFFSET)
-#define VDEC_HACTS (VDEC_BASE + VDEC_HACTS_OFFSET)
-#define VDEC_HACTE (VDEC_BASE + VDEC_HACTE_OFFSET)
-#define VDEC_VACTS (VDEC_BASE + VDEC_VACTS_OFFSET)
-#define VDEC_VACTE (VDEC_BASE + VDEC_VACTE_OFFSET)
-#define VDEC_HSTIP (VDEC_BASE + VDEC_HSTIP_OFFSET)
-#define VDEC_BLSCRY (VDEC_BASE + VDEC_BLSCRY_OFFSET)
-#define VDEC_BLSCRCR (VDEC_BASE + VDEC_BLSCRCR_OFFSET)
-#define VDEC_BLSCRCB (VDEC_BASE + VDEC_BLSCRCB_OFFSET)
-#define VDEC_LMAGC1 (VDEC_BASE + VDEC_LMAGC1_OFFSET)
-#define VDEC_LMAGC2 (VDEC_BASE + VDEC_LMAGC2_OFFSET)
-#define VDEC_CHAGC1 (VDEC_BASE + VDEC_CHAGC1_OFFSET)
-#define VDEC_CHAGC2 (VDEC_BASE + VDEC_CHAGC2_OFFSET)
-#define VDEC_MINTH (VDEC_BASE + VDEC_MINTH_OFFSET)
-#define VDEC_VFRQOH (VDEC_BASE + VDEC_VFRQOH_OFFSET)
-#define VDEC_VFRQOL (VDEC_BASE + VDEC_VFRQOL_OFFSET)
-#define VDEC_THSH1 (VDEC_BASE + VDEC_THSH1_OFFSET)
-#define VDEC_THSH2 (VDEC_BASE + VDEC_THSH2_OFFSET)
-#define VDEC_NCHTH (VDEC_BASE + VDEC_NCHTH_OFFSET)
-#define VDEC_TH1F (VDEC_BASE + VDEC_TH1F_OFFSET)
-
-#define VDEC_VIDMOD_SIGNAL_MASK 0x0F
-#define VDEC_VIDMOD_SIGNAL_DETECT 0x0F
-
-#define VDEC_VIDMOD_M625_SHIFT 4
-#define VDEC_VIDMOD_M625_MASK (1 << VDEC_VIDMOD_M625_SHIFT)
-
-#define VDEC_VIDMOD_PAL_SHIFT 7
-#define VDEC_VIDMOD_PAL_MASK (1 << VDEC_VIDMOD_PAL_SHIFT)
-/*** define base address ***/
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) 2012-2015 Freescale Semiconductor, 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/clk.h>
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/i2c.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/of_device.h>
-#include <linux/of_gpio.h>
-#include <linux/pinctrl/consumer.h>
-#include <linux/regulator/consumer.h>
-#include <linux/v4l2-mediabus.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ctrls.h>
-
-#define OV5640_VOLTAGE_ANALOG 2800000
-#define OV5640_VOLTAGE_DIGITAL_CORE 1500000
-#define OV5640_VOLTAGE_DIGITAL_IO 1800000
-
-#define MIN_FPS 15
-#define MAX_FPS 30
-#define DEFAULT_FPS 30
-
-#define OV5640_XCLK_MIN 6000000
-#define OV5640_XCLK_MAX 24000000
-
-#define OV5640_CHIP_ID_HIGH_BYTE 0x300A
-#define OV5640_CHIP_ID_LOW_BYTE 0x300B
-
-enum ov5640_mode {
- ov5640_mode_MIN = 0,
- ov5640_mode_VGA_640_480 = 0,
- ov5640_mode_QVGA_320_240 = 1,
- ov5640_mode_NTSC_720_480 = 2,
- ov5640_mode_PAL_720_576 = 3,
- ov5640_mode_720P_1280_720 = 4,
- ov5640_mode_1080P_1920_1080 = 5,
- ov5640_mode_QSXGA_2592_1944 = 6,
- ov5640_mode_QCIF_176_144 = 7,
- ov5640_mode_XGA_1024_768 = 8,
- ov5640_mode_MAX = 8
-};
-
-enum ov5640_frame_rate {
- ov5640_15_fps,
- ov5640_30_fps
-};
-
-static int ov5640_framerates[] = {
- [ov5640_15_fps] = 15,
- [ov5640_30_fps] = 30,
-};
-
-struct ov5640_datafmt {
- u32 code;
- enum v4l2_colorspace colorspace;
-};
-
-struct reg_value {
- u16 u16RegAddr;
- u8 u8Val;
- u8 u8Mask;
- u32 u32Delay_ms;
-};
-
-struct ov5640_mode_info {
- enum ov5640_mode mode;
- u32 width;
- u32 height;
- struct reg_value *init_data_ptr;
- u32 init_data_size;
-};
-
-struct ov5640 {
- struct v4l2_subdev subdev;
- struct i2c_client *i2c_client;
- struct v4l2_pix_format pix;
- const struct ov5640_datafmt *fmt;
- 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;
- u8 mclk_source;
- struct clk *sensor_clk;
- int csi;
-
- void (*io_init)(void);
-};
-
-/*!
- * Maintains the information on the current state of the sesor.
- */
-static struct ov5640 ov5640_data;
-static int pwn_gpio, rst_gpio;
-static int prev_sysclk;
-static int AE_Target = 52, night_mode;
-static int prev_HTS;
-static int AE_high, AE_low;
-
-static struct reg_value ov5640_global_init_setting[] = {
- {0x3008, 0x42, 0, 0},
- {0x3103, 0x03, 0, 0}, {0x3017, 0xff, 0, 0}, {0x3018, 0xff, 0, 0},
- {0x3034, 0x1a, 0, 0}, {0x3037, 0x13, 0, 0}, {0x3108, 0x01, 0, 0},
- {0x3630, 0x36, 0, 0}, {0x3631, 0x0e, 0, 0}, {0x3632, 0xe2, 0, 0},
- {0x3633, 0x12, 0, 0}, {0x3621, 0xe0, 0, 0}, {0x3704, 0xa0, 0, 0},
- {0x3703, 0x5a, 0, 0}, {0x3715, 0x78, 0, 0}, {0x3717, 0x01, 0, 0},
- {0x370b, 0x60, 0, 0}, {0x3705, 0x1a, 0, 0}, {0x3905, 0x02, 0, 0},
- {0x3906, 0x10, 0, 0}, {0x3901, 0x0a, 0, 0}, {0x3731, 0x12, 0, 0},
- {0x3600, 0x08, 0, 0}, {0x3601, 0x33, 0, 0}, {0x302d, 0x60, 0, 0},
- {0x3620, 0x52, 0, 0}, {0x371b, 0x20, 0, 0}, {0x471c, 0x50, 0, 0},
- {0x3a13, 0x43, 0, 0}, {0x3a18, 0x00, 0, 0}, {0x3a19, 0x7c, 0, 0},
- {0x3635, 0x13, 0, 0}, {0x3636, 0x03, 0, 0}, {0x3634, 0x40, 0, 0},
- {0x3622, 0x01, 0, 0}, {0x3c01, 0x34, 0, 0}, {0x3c04, 0x28, 0, 0},
- {0x3c05, 0x98, 0, 0}, {0x3c06, 0x00, 0, 0}, {0x3c07, 0x07, 0, 0},
- {0x3c08, 0x00, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0},
- {0x3c0b, 0x40, 0, 0}, {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0},
- {0x3812, 0x00, 0, 0}, {0x3708, 0x64, 0, 0}, {0x4001, 0x02, 0, 0},
- {0x4005, 0x1a, 0, 0}, {0x3000, 0x00, 0, 0}, {0x3004, 0xff, 0, 0},
- {0x300e, 0x58, 0, 0}, {0x302e, 0x00, 0, 0}, {0x4300, 0x30, 0, 0},
- {0x501f, 0x00, 0, 0}, {0x440e, 0x00, 0, 0}, {0x5000, 0xa7, 0, 0},
- {0x3008, 0x02, 0, 0},
-};
-
-static struct reg_value ov5640_init_setting_30fps_VGA[] = {
- {0x3008, 0x42, 0, 0},
- {0x3103, 0x03, 0, 0}, {0x3017, 0xff, 0, 0}, {0x3018, 0xff, 0, 0},
- {0x3034, 0x1a, 0, 0}, {0x3035, 0x11, 0, 0}, {0x3036, 0x46, 0, 0},
- {0x3037, 0x13, 0, 0}, {0x3108, 0x01, 0, 0}, {0x3630, 0x36, 0, 0},
- {0x3631, 0x0e, 0, 0}, {0x3632, 0xe2, 0, 0}, {0x3633, 0x12, 0, 0},
- {0x3621, 0xe0, 0, 0}, {0x3704, 0xa0, 0, 0}, {0x3703, 0x5a, 0, 0},
- {0x3715, 0x78, 0, 0}, {0x3717, 0x01, 0, 0}, {0x370b, 0x60, 0, 0},
- {0x3705, 0x1a, 0, 0}, {0x3905, 0x02, 0, 0}, {0x3906, 0x10, 0, 0},
- {0x3901, 0x0a, 0, 0}, {0x3731, 0x12, 0, 0}, {0x3600, 0x08, 0, 0},
- {0x3601, 0x33, 0, 0}, {0x302d, 0x60, 0, 0}, {0x3620, 0x52, 0, 0},
- {0x371b, 0x20, 0, 0}, {0x471c, 0x50, 0, 0}, {0x3a13, 0x43, 0, 0},
- {0x3a18, 0x00, 0, 0}, {0x3a19, 0xf8, 0, 0}, {0x3635, 0x13, 0, 0},
- {0x3636, 0x03, 0, 0}, {0x3634, 0x40, 0, 0}, {0x3622, 0x01, 0, 0},
- {0x3c01, 0x34, 0, 0}, {0x3c04, 0x28, 0, 0}, {0x3c05, 0x98, 0, 0},
- {0x3c06, 0x00, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c08, 0x00, 0, 0},
- {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
- {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
- {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
- {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
- {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
- {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0},
- {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
- {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
- {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
- {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
- {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
- {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
- {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
- {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
- {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x3000, 0x00, 0, 0},
- {0x3002, 0x1c, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3006, 0xc3, 0, 0},
- {0x300e, 0x58, 0, 0}, {0x302e, 0x00, 0, 0}, {0x4300, 0x30, 0, 0},
- {0x501f, 0x00, 0, 0}, {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0},
- {0x440e, 0x00, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
- {0x4837, 0x22, 0, 0}, {0x3824, 0x02, 0, 0}, {0x5000, 0xa7, 0, 0},
- {0x5001, 0xa3, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0xf2, 0, 0},
- {0x5182, 0x00, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0},
- {0x5185, 0x24, 0, 0}, {0x5186, 0x09, 0, 0}, {0x5187, 0x09, 0, 0},
- {0x5188, 0x09, 0, 0}, {0x5189, 0x88, 0, 0}, {0x518a, 0x54, 0, 0},
- {0x518b, 0xee, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x50, 0, 0},
- {0x518e, 0x34, 0, 0}, {0x518f, 0x6b, 0, 0}, {0x5190, 0x46, 0, 0},
- {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0},
- {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0},
- {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x6c, 0, 0},
- {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x09, 0, 0},
- {0x519d, 0x2b, 0, 0}, {0x519e, 0x38, 0, 0}, {0x5381, 0x1e, 0, 0},
- {0x5382, 0x5b, 0, 0}, {0x5383, 0x08, 0, 0}, {0x5384, 0x0a, 0, 0},
- {0x5385, 0x7e, 0, 0}, {0x5386, 0x88, 0, 0}, {0x5387, 0x7c, 0, 0},
- {0x5388, 0x6c, 0, 0}, {0x5389, 0x10, 0, 0}, {0x538a, 0x01, 0, 0},
- {0x538b, 0x98, 0, 0}, {0x5300, 0x08, 0, 0}, {0x5301, 0x30, 0, 0},
- {0x5302, 0x10, 0, 0}, {0x5303, 0x00, 0, 0}, {0x5304, 0x08, 0, 0},
- {0x5305, 0x30, 0, 0}, {0x5306, 0x08, 0, 0}, {0x5307, 0x16, 0, 0},
- {0x5309, 0x08, 0, 0}, {0x530a, 0x30, 0, 0}, {0x530b, 0x04, 0, 0},
- {0x530c, 0x06, 0, 0}, {0x5480, 0x01, 0, 0}, {0x5481, 0x08, 0, 0},
- {0x5482, 0x14, 0, 0}, {0x5483, 0x28, 0, 0}, {0x5484, 0x51, 0, 0},
- {0x5485, 0x65, 0, 0}, {0x5486, 0x71, 0, 0}, {0x5487, 0x7d, 0, 0},
- {0x5488, 0x87, 0, 0}, {0x5489, 0x91, 0, 0}, {0x548a, 0x9a, 0, 0},
- {0x548b, 0xaa, 0, 0}, {0x548c, 0xb8, 0, 0}, {0x548d, 0xcd, 0, 0},
- {0x548e, 0xdd, 0, 0}, {0x548f, 0xea, 0, 0}, {0x5490, 0x1d, 0, 0},
- {0x5580, 0x02, 0, 0}, {0x5583, 0x40, 0, 0}, {0x5584, 0x10, 0, 0},
- {0x5589, 0x10, 0, 0}, {0x558a, 0x00, 0, 0}, {0x558b, 0xf8, 0, 0},
- {0x5800, 0x23, 0, 0}, {0x5801, 0x14, 0, 0}, {0x5802, 0x0f, 0, 0},
- {0x5803, 0x0f, 0, 0}, {0x5804, 0x12, 0, 0}, {0x5805, 0x26, 0, 0},
- {0x5806, 0x0c, 0, 0}, {0x5807, 0x08, 0, 0}, {0x5808, 0x05, 0, 0},
- {0x5809, 0x05, 0, 0}, {0x580a, 0x08, 0, 0}, {0x580b, 0x0d, 0, 0},
- {0x580c, 0x08, 0, 0}, {0x580d, 0x03, 0, 0}, {0x580e, 0x00, 0, 0},
- {0x580f, 0x00, 0, 0}, {0x5810, 0x03, 0, 0}, {0x5811, 0x09, 0, 0},
- {0x5812, 0x07, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x00, 0, 0},
- {0x5815, 0x01, 0, 0}, {0x5816, 0x03, 0, 0}, {0x5817, 0x08, 0, 0},
- {0x5818, 0x0d, 0, 0}, {0x5819, 0x08, 0, 0}, {0x581a, 0x05, 0, 0},
- {0x581b, 0x06, 0, 0}, {0x581c, 0x08, 0, 0}, {0x581d, 0x0e, 0, 0},
- {0x581e, 0x29, 0, 0}, {0x581f, 0x17, 0, 0}, {0x5820, 0x11, 0, 0},
- {0x5821, 0x11, 0, 0}, {0x5822, 0x15, 0, 0}, {0x5823, 0x28, 0, 0},
- {0x5824, 0x46, 0, 0}, {0x5825, 0x26, 0, 0}, {0x5826, 0x08, 0, 0},
- {0x5827, 0x26, 0, 0}, {0x5828, 0x64, 0, 0}, {0x5829, 0x26, 0, 0},
- {0x582a, 0x24, 0, 0}, {0x582b, 0x22, 0, 0}, {0x582c, 0x24, 0, 0},
- {0x582d, 0x24, 0, 0}, {0x582e, 0x06, 0, 0}, {0x582f, 0x22, 0, 0},
- {0x5830, 0x40, 0, 0}, {0x5831, 0x42, 0, 0}, {0x5832, 0x24, 0, 0},
- {0x5833, 0x26, 0, 0}, {0x5834, 0x24, 0, 0}, {0x5835, 0x22, 0, 0},
- {0x5836, 0x22, 0, 0}, {0x5837, 0x26, 0, 0}, {0x5838, 0x44, 0, 0},
- {0x5839, 0x24, 0, 0}, {0x583a, 0x26, 0, 0}, {0x583b, 0x28, 0, 0},
- {0x583c, 0x42, 0, 0}, {0x583d, 0xce, 0, 0}, {0x5025, 0x00, 0, 0},
- {0x3a0f, 0x30, 0, 0}, {0x3a10, 0x28, 0, 0}, {0x3a1b, 0x30, 0, 0},
- {0x3a1e, 0x26, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x14, 0, 0},
- {0x3008, 0x02, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x11, 0, 0},
- {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0},
-};
-
-static struct reg_value ov5640_setting_30fps_VGA_640_480[] = {
- {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0},
- {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0},
- {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0},
- {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0},
- {0x3807, 0x9b, 0, 0}, {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0},
- {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0},
- {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0},
- {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0},
- {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0},
- {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0},
- {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0},
- {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0},
- {0x460c, 0x22, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x02, 0, 0},
- {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x11, 0, 0},
- {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0}, {0x3503, 0x00, 0, 0},
-};
-
-static struct reg_value ov5640_setting_15fps_VGA_640_480[] = {
- {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0},
- {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0},
- {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0},
- {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0},
- {0x3807, 0x9b, 0, 0}, {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0},
- {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0},
- {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0},
- {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0},
- {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0},
- {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0},
- {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0},
- {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0},
- {0x460c, 0x22, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x02, 0, 0},
- {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x21, 0, 0},
- {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0}, {0x3503, 0x00, 0, 0},
-};
-
-static struct reg_value ov5640_setting_30fps_QVGA_320_240[] = {
- {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0},
- {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0},
- {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0},
- {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0},
- {0x3807, 0x9b, 0, 0}, {0x3808, 0x01, 0, 0}, {0x3809, 0x40, 0, 0},
- {0x380a, 0x00, 0, 0}, {0x380b, 0xf0, 0, 0}, {0x380c, 0x07, 0, 0},
- {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0},
- {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0},
- {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0},
- {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0},
- {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0},
- {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0},
- {0x460c, 0x22, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x02, 0, 0},
- {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x11, 0, 0},
- {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0},
-};
-
-static struct reg_value ov5640_setting_15fps_QVGA_320_240[] = {
- {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0},
- {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0},
- {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0},
- {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0},
- {0x3807, 0x9b, 0, 0}, {0x3808, 0x01, 0, 0}, {0x3809, 0x40, 0, 0},
- {0x380a, 0x00, 0, 0}, {0x380b, 0xf0, 0, 0}, {0x380c, 0x07, 0, 0},
- {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0},
- {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0},
- {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0},
- {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0},
- {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0},
- {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0},
- {0x460c, 0x22, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x02, 0, 0},
- {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x21, 0, 0},
- {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0},
-};
-
-static struct reg_value ov5640_setting_30fps_NTSC_720_480[] = {
- {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0},
- {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0},
- {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0},
- {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0},
- {0x3807, 0xd4, 0, 0}, {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0},
- {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0},
- {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0},
- {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0},
- {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0},
- {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0},
- {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0},
- {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0},
- {0x460c, 0x22, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x02, 0, 0},
- {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x11, 0, 0},
- {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0},
-};
-
-static struct reg_value ov5640_setting_15fps_NTSC_720_480[] = {
- {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0},
- {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0},
- {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0},
- {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0},
- {0x3807, 0xd4, 0, 0}, {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0},
- {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0},
- {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0},
- {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0},
- {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0},
- {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0},
- {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0},
- {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0},
- {0x460c, 0x22, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x02, 0, 0},
- {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x21, 0, 0},
- {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0},
-};
-
-static struct reg_value ov5640_setting_30fps_PAL_720_576[] = {
- {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0},
- {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0},
- {0x3801, 0x60, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0},
- {0x3804, 0x09, 0, 0}, {0x3805, 0x7e, 0, 0}, {0x3806, 0x07, 0, 0},
- {0x3807, 0x9b, 0, 0}, {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0},
- {0x380a, 0x02, 0, 0}, {0x380b, 0x40, 0, 0}, {0x380c, 0x07, 0, 0},
- {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0},
- {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0},
- {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0},
- {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0},
- {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0},
- {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0},
- {0x460c, 0x22, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x02, 0, 0},
- {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x11, 0, 0},
- {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0},
-};
-
-static struct reg_value ov5640_setting_15fps_PAL_720_576[] = {
- {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0},
- {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0},
- {0x3801, 0x60, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0},
- {0x3804, 0x09, 0, 0}, {0x3805, 0x7e, 0, 0}, {0x3806, 0x07, 0, 0},
- {0x3807, 0x9b, 0, 0}, {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0},
- {0x380a, 0x02, 0, 0}, {0x380b, 0x40, 0, 0}, {0x380c, 0x07, 0, 0},
- {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0},
- {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0},
- {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0},
- {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0},
- {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0},
- {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0},
- {0x460c, 0x22, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x02, 0, 0},
- {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x21, 0, 0},
- {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0},
-};
-
-static struct reg_value ov5640_setting_30fps_720P_1280_720[] = {
- {0x3035, 0x21, 0, 0}, {0x3036, 0x69, 0, 0}, {0x3c07, 0x07, 0, 0},
- {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
- {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
- {0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0},
- {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0},
- {0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x02, 0, 0},
- {0x380b, 0xd0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x64, 0, 0},
- {0x380e, 0x02, 0, 0}, {0x380f, 0xe4, 0, 0}, {0x3813, 0x04, 0, 0},
- {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3709, 0x52, 0, 0},
- {0x370c, 0x03, 0, 0}, {0x3a02, 0x02, 0, 0}, {0x3a03, 0xe0, 0, 0},
- {0x3a14, 0x02, 0, 0}, {0x3a15, 0xe0, 0, 0}, {0x4004, 0x02, 0, 0},
- {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0}, {0x4713, 0x03, 0, 0},
- {0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0},
- {0x4837, 0x16, 0, 0}, {0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0},
- {0x3503, 0x00, 0, 0},
-};
-
-static struct reg_value ov5640_setting_15fps_720P_1280_720[] = {
- {0x3035, 0x41, 0, 0}, {0x3036, 0x69, 0, 0}, {0x3c07, 0x07, 0, 0},
- {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
- {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
- {0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0},
- {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0},
- {0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x02, 0, 0},
- {0x380b, 0xd0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x64, 0, 0},
- {0x380e, 0x02, 0, 0}, {0x380f, 0xe4, 0, 0}, {0x3813, 0x04, 0, 0},
- {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3709, 0x52, 0, 0},
- {0x370c, 0x03, 0, 0}, {0x3a02, 0x02, 0, 0}, {0x3a03, 0xe0, 0, 0},
- {0x3a14, 0x02, 0, 0}, {0x3a15, 0xe0, 0, 0}, {0x4004, 0x02, 0, 0},
- {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0}, {0x4713, 0x03, 0, 0},
- {0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0},
- {0x4837, 0x16, 0, 0}, {0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0},
- {0x3503, 0x00, 0, 0},
-};
-
-static struct reg_value ov5640_setting_30fps_QCIF_176_144[] = {
- {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0},
- {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0},
- {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0},
- {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0},
- {0x3807, 0x9b, 0, 0}, {0x3808, 0x00, 0, 0}, {0x3809, 0xb0, 0, 0},
- {0x380a, 0x00, 0, 0}, {0x380b, 0x90, 0, 0}, {0x380c, 0x07, 0, 0},
- {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0},
- {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0},
- {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0},
- {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0},
- {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0},
- {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0},
- {0x460c, 0x22, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x02, 0, 0},
- {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x11, 0, 0},
- {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0},
-};
-
-static struct reg_value ov5640_setting_15fps_QCIF_176_144[] = {
- {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0},
- {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0},
- {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0},
- {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0},
- {0x3807, 0x9b, 0, 0}, {0x3808, 0x00, 0, 0}, {0x3809, 0xb0, 0, 0},
- {0x380a, 0x00, 0, 0}, {0x380b, 0x90, 0, 0}, {0x380c, 0x07, 0, 0},
- {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0},
- {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0},
- {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0},
- {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0},
- {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0},
- {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0},
- {0x460c, 0x22, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x02, 0, 0},
- {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x21, 0, 0},
- {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0},
-};
-
-static struct reg_value ov5640_setting_30fps_XGA_1024_768[] = {
- {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0},
- {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0},
- {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0},
- {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0},
- {0x3807, 0x9b, 0, 0}, {0x3808, 0x04, 0, 0}, {0x3809, 0x00, 0, 0},
- {0x380a, 0x03, 0, 0}, {0x380b, 0x00, 0, 0}, {0x380c, 0x07, 0, 0},
- {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0},
- {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0},
- {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0},
- {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0},
- {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0},
- {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0},
- {0x460c, 0x20, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x01, 0, 0},
- {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x21, 0, 0},
- {0x3036, 0x69, 0, 0}, {0x3037, 0x13, 0, 0},
-};
-
-static struct reg_value ov5640_setting_15fps_XGA_1024_768[] = {
- {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0},
- {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0},
- {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0},
- {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0},
- {0x3807, 0x9b, 0, 0}, {0x3808, 0x04, 0, 0}, {0x3809, 0x00, 0, 0},
- {0x380a, 0x03, 0, 0}, {0x380b, 0x00, 0, 0}, {0x380c, 0x07, 0, 0},
- {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0},
- {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0},
- {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0},
- {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0},
- {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0},
- {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0},
- {0x460c, 0x20, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x01, 0, 0},
- {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x21, 0, 0},
- {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0},
-};
-
-
-static struct reg_value ov5640_setting_15fps_1080P_1920_1080[] = {
- {0x3c07, 0x07, 0, 0}, {0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0},
- {0x3814, 0x11, 0, 0}, {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0},
- {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0xee, 0, 0},
- {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x05, 0, 0},
- {0x3807, 0xc3, 0, 0}, {0x3808, 0x07, 0, 0}, {0x3809, 0x80, 0, 0},
- {0x380a, 0x04, 0, 0}, {0x380b, 0x38, 0, 0}, {0x380c, 0x0b, 0, 0},
- {0x380d, 0x1c, 0, 0}, {0x380e, 0x07, 0, 0}, {0x380f, 0xb0, 0, 0},
- {0x3813, 0x04, 0, 0}, {0x3618, 0x04, 0, 0}, {0x3612, 0x2b, 0, 0},
- {0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x07, 0, 0},
- {0x3a03, 0xae, 0, 0}, {0x3a14, 0x07, 0, 0}, {0x3a15, 0xae, 0, 0},
- {0x4004, 0x06, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0},
- {0x4713, 0x02, 0, 0}, {0x4407, 0x0c, 0, 0}, {0x460b, 0x37, 0, 0},
- {0x460c, 0x20, 0, 0}, {0x4837, 0x2c, 0, 0}, {0x3824, 0x01, 0, 0},
- {0x5001, 0x83, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x21, 0, 0},
- {0x3036, 0x69, 0, 0}, {0x3037, 0x13, 0, 0},
-};
-
-static struct reg_value ov5640_setting_15fps_QSXGA_2592_1944[] = {
- {0x3c07, 0x07, 0, 0}, {0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0},
- {0x3814, 0x11, 0, 0}, {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0},
- {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0},
- {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0},
- {0x3807, 0x9f, 0, 0}, {0x3808, 0x0a, 0, 0}, {0x3809, 0x20, 0, 0},
- {0x380a, 0x07, 0, 0}, {0x380b, 0x98, 0, 0}, {0x380c, 0x0b, 0, 0},
- {0x380d, 0x1c, 0, 0}, {0x380e, 0x07, 0, 0}, {0x380f, 0xb0, 0, 0},
- {0x3813, 0x04, 0, 0}, {0x3618, 0x04, 0, 0}, {0x3612, 0x2b, 0, 0},
- {0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x07, 0, 0},
- {0x3a03, 0xae, 0, 0}, {0x3a14, 0x07, 0, 0}, {0x3a15, 0xae, 0, 0},
- {0x4004, 0x06, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0},
- {0x4713, 0x02, 0, 0}, {0x4407, 0x0c, 0, 0}, {0x460b, 0x37, 0, 0},
- {0x460c, 0x20, 0, 0}, {0x4837, 0x2c, 0, 0}, {0x3824, 0x01, 0, 0},
- {0x5001, 0x83, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x21, 0, 0},
- {0x3036, 0x69, 0, 0}, {0x3037, 0x13, 0, 0},
-};
-
-static struct ov5640_mode_info ov5640_mode_info_data[2][ov5640_mode_MAX + 1] = {
- {
- {ov5640_mode_VGA_640_480, 640, 480,
- ov5640_setting_15fps_VGA_640_480,
- ARRAY_SIZE(ov5640_setting_15fps_VGA_640_480)},
- {ov5640_mode_QVGA_320_240, 320, 240,
- ov5640_setting_15fps_QVGA_320_240,
- ARRAY_SIZE(ov5640_setting_15fps_QVGA_320_240)},
- {ov5640_mode_NTSC_720_480, 720, 480,
- ov5640_setting_15fps_NTSC_720_480,
- ARRAY_SIZE(ov5640_setting_15fps_NTSC_720_480)},
- {ov5640_mode_PAL_720_576, 720, 576,
- ov5640_setting_15fps_PAL_720_576,
- ARRAY_SIZE(ov5640_setting_15fps_PAL_720_576)},
- {ov5640_mode_720P_1280_720, 1280, 720,
- ov5640_setting_15fps_720P_1280_720,
- ARRAY_SIZE(ov5640_setting_15fps_720P_1280_720)},
- {ov5640_mode_1080P_1920_1080, 1920, 1080,
- ov5640_setting_15fps_1080P_1920_1080,
- ARRAY_SIZE(ov5640_setting_15fps_1080P_1920_1080)},
- {ov5640_mode_QSXGA_2592_1944, 2592, 1944,
- ov5640_setting_15fps_QSXGA_2592_1944,
- ARRAY_SIZE(ov5640_setting_15fps_QSXGA_2592_1944)},
- {ov5640_mode_QCIF_176_144, 176, 144,
- ov5640_setting_15fps_QCIF_176_144,
- ARRAY_SIZE(ov5640_setting_15fps_QCIF_176_144)},
- {ov5640_mode_XGA_1024_768, 1024, 768,
- ov5640_setting_15fps_XGA_1024_768,
- ARRAY_SIZE(ov5640_setting_15fps_XGA_1024_768)},
- },
- {
- {ov5640_mode_VGA_640_480, 640, 480,
- ov5640_setting_30fps_VGA_640_480,
- ARRAY_SIZE(ov5640_setting_30fps_VGA_640_480)},
- {ov5640_mode_QVGA_320_240, 320, 240,
- ov5640_setting_30fps_QVGA_320_240,
- ARRAY_SIZE(ov5640_setting_30fps_QVGA_320_240)},
- {ov5640_mode_NTSC_720_480, 720, 480,
- ov5640_setting_30fps_NTSC_720_480,
- ARRAY_SIZE(ov5640_setting_30fps_NTSC_720_480)},
- {ov5640_mode_PAL_720_576, 720, 576,
- ov5640_setting_30fps_PAL_720_576,
- ARRAY_SIZE(ov5640_setting_30fps_PAL_720_576)},
- {ov5640_mode_720P_1280_720, 1280, 720,
- ov5640_setting_30fps_720P_1280_720,
- ARRAY_SIZE(ov5640_setting_30fps_720P_1280_720)},
- {ov5640_mode_1080P_1920_1080, 0, 0, NULL, 0},
- {ov5640_mode_QSXGA_2592_1944, 0, 0, NULL, 0},
- {ov5640_mode_QCIF_176_144, 176, 144,
- ov5640_setting_30fps_QCIF_176_144,
- ARRAY_SIZE(ov5640_setting_30fps_QCIF_176_144)},
- {ov5640_mode_XGA_1024_768, 1024, 768,
- ov5640_setting_30fps_XGA_1024_768,
- ARRAY_SIZE(ov5640_setting_30fps_XGA_1024_768)},
- },
-};
-
-static struct regulator *io_regulator;
-static struct regulator *core_regulator;
-static struct regulator *analog_regulator;
-
-static int ov5640_probe(struct i2c_client *adapter,
- const struct i2c_device_id *device_id);
-static int ov5640_remove(struct i2c_client *client);
-
-static s32 ov5640_read_reg(u16 reg, u8 *val);
-static s32 ov5640_write_reg(u16 reg, u8 val);
-
-static const struct i2c_device_id ov5640_id[] = {
- {"ov5640", 0},
- {},
-};
-
-MODULE_DEVICE_TABLE(i2c, ov5640_id);
-
-static struct i2c_driver ov5640_i2c_driver = {
- .driver = {
- .owner = THIS_MODULE,
- .name = "ov5640",
- },
- .probe = ov5640_probe,
- .remove = ov5640_remove,
- .id_table = ov5640_id,
-};
-
-static const struct ov5640_datafmt ov5640_colour_fmts[] = {
- {MEDIA_BUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG},
-};
-
-static struct ov5640 *to_ov5640(const struct i2c_client *client)
-{
- return container_of(i2c_get_clientdata(client), struct ov5640, subdev);
-}
-
-/* Find a data format by a pixel code in an array */
-static const struct ov5640_datafmt
- *ov5640_find_datafmt(u32 code)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(ov5640_colour_fmts); i++)
- if (ov5640_colour_fmts[i].code == code)
- return ov5640_colour_fmts + i;
-
- return NULL;
-}
-
-static inline void ov5640_power_down(int enable)
-{
- gpio_set_value_cansleep(pwn_gpio, enable);
-
- msleep(2);
-}
-
-static inline void ov5640_reset(void)
-{
- /* camera reset */
- gpio_set_value_cansleep(rst_gpio, 1);
-
- /* camera power down */
- gpio_set_value_cansleep(pwn_gpio, 1);
- msleep(5);
- gpio_set_value_cansleep(pwn_gpio, 0);
- msleep(5);
- gpio_set_value_cansleep(rst_gpio, 0);
- msleep(1);
- gpio_set_value_cansleep(rst_gpio, 1);
- msleep(5);
- gpio_set_value_cansleep(pwn_gpio, 1);
-}
-
-static int ov5640_regulator_enable(struct device *dev)
-{
- int ret = 0;
-
- io_regulator = devm_regulator_get(dev, "DOVDD");
- if (!IS_ERR(io_regulator)) {
- regulator_set_voltage(io_regulator,
- OV5640_VOLTAGE_DIGITAL_IO,
- OV5640_VOLTAGE_DIGITAL_IO);
- ret = regulator_enable(io_regulator);
- if (ret) {
- dev_err(dev, "set io voltage failed\n");
- return ret;
- } else {
- dev_dbg(dev, "set io voltage ok\n");
- }
- } else {
- io_regulator = NULL;
- dev_warn(dev, "cannot get io voltage\n");
- }
-
- core_regulator = devm_regulator_get(dev, "DVDD");
- if (!IS_ERR(core_regulator)) {
- regulator_set_voltage(core_regulator,
- OV5640_VOLTAGE_DIGITAL_CORE,
- OV5640_VOLTAGE_DIGITAL_CORE);
- ret = regulator_enable(core_regulator);
- if (ret) {
- dev_err(dev, "set core voltage failed\n");
- return ret;
- } else {
- dev_dbg(dev, "set core voltage ok\n");
- }
- } else {
- core_regulator = NULL;
- dev_warn(dev, "cannot get core voltage\n");
- }
-
- analog_regulator = devm_regulator_get(dev, "AVDD");
- if (!IS_ERR(analog_regulator)) {
- regulator_set_voltage(analog_regulator,
- OV5640_VOLTAGE_ANALOG,
- OV5640_VOLTAGE_ANALOG);
- ret = regulator_enable(analog_regulator);
- if (ret) {
- dev_err(dev, "set analog voltage failed\n");
- return ret;
- } else {
- dev_dbg(dev, "set analog voltage ok\n");
- }
- } else {
- analog_regulator = NULL;
- dev_warn(dev, "cannot get analog voltage\n");
- }
-
- return ret;
-}
-
-static s32 ov5640_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(ov5640_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 ov5640_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(ov5640_data.i2c_client, au8RegBuf, 2)) {
- pr_err("%s:write reg error:reg=%x\n",
- __func__, reg);
- return -1;
- }
-
- if (1 != i2c_master_recv(ov5640_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;
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int ov5640_get_register(struct v4l2_subdev *sd,
- struct v4l2_dbg_register *reg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- int ret;
- u8 val;
-
- if (reg->reg & ~0xffff)
- return -EINVAL;
-
- reg->size = 1;
-
- ret = ov5640_read_reg(reg->reg, &val);
- if (!ret)
- reg->val = (__u64)val;
-
- return ret;
-}
-
-static int ov5640_set_register(struct v4l2_subdev *sd,
- const struct v4l2_dbg_register *reg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (reg->reg & ~0xffff || reg->val & ~0xff)
- return -EINVAL;
-
- return ov5640_write_reg(reg->reg, reg->val);
-}
-#endif
-
-static void ov5640_soft_reset(void)
-{
- /* sysclk from pad */
- ov5640_write_reg(0x3103, 0x11);
-
- /* software reset */
- ov5640_write_reg(0x3008, 0x82);
-
- /* delay at least 5ms */
- msleep(10);
-}
-
-/* set sensor driver capability
- * 0x302c[7:6] - strength
- 00 - 1x
- 01 - 2x
- 10 - 3x
- 11 - 4x
- */
-static int ov5640_driver_capability(int strength)
-{
- u8 temp = 0;
-
- if (strength > 4 || strength < 1) {
- pr_err("The valid driver capability of ov5640 is 1x~4x\n");
- return -EINVAL;
- }
-
- ov5640_read_reg(0x302c, &temp);
-
- temp &= ~0xc0; /* clear [7:6] */
- temp |= ((strength - 1) << 6); /* set [7:6] */
-
- ov5640_write_reg(0x302c, temp);
-
- return 0;
-}
-
-/* calculate sysclk */
-static int ov5640_get_sysclk(void)
-{
- int xvclk = ov5640_data.mclk / 10000;
- int sysclk;
- int temp1, temp2;
- int Multiplier, PreDiv, VCO, SysDiv, Pll_rdiv, Bit_div2x, sclk_rdiv;
- int sclk_rdiv_map[] = {1, 2, 4, 8};
- u8 regval = 0;
-
- temp1 = ov5640_read_reg(0x3034, ®val);
- temp2 = temp1 & 0x0f;
- if (temp2 == 8 || temp2 == 10) {
- Bit_div2x = temp2 / 2;
- } else {
- pr_err("ov5640: unsupported bit mode %d\n", temp2);
- return -1;
- }
-
- temp1 = ov5640_read_reg(0x3035, ®val);
- SysDiv = temp1 >> 4;
- if (SysDiv == 0)
- SysDiv = 16;
-
- temp1 = ov5640_read_reg(0x3036, ®val);
- Multiplier = temp1;
- temp1 = ov5640_read_reg(0x3037, ®val);
- PreDiv = temp1 & 0x0f;
- Pll_rdiv = ((temp1 >> 4) & 0x01) + 1;
-
- temp1 = ov5640_read_reg(0x3108, ®val);
- temp2 = temp1 & 0x03;
-
- sclk_rdiv = sclk_rdiv_map[temp2];
- VCO = xvclk * Multiplier / PreDiv;
- sysclk = VCO / SysDiv / Pll_rdiv * 2 / Bit_div2x / sclk_rdiv;
-
- return sysclk;
-}
-
-/* read HTS from register settings */
-static int ov5640_get_HTS(void)
-{
- int HTS;
- u8 temp = 0;
-
- HTS = ov5640_read_reg(0x380c, &temp);
- HTS = (HTS<<8) + ov5640_read_reg(0x380d, &temp);
- return HTS;
-}
-
-/* read VTS from register settings */
-static int ov5640_get_VTS(void)
-{
- int VTS;
- u8 temp = 0;
-
- VTS = ov5640_read_reg(0x380e, &temp);
- VTS = (VTS<<8) + ov5640_read_reg(0x380f, &temp);
-
- return VTS;
-}
-
-/* write VTS to registers */
-static int ov5640_set_VTS(int VTS)
-{
- int temp;
-
- temp = VTS & 0xff;
- ov5640_write_reg(0x380f, temp);
-
- temp = VTS>>8;
- ov5640_write_reg(0x380e, temp);
- return 0;
-}
-
-/* read shutter, in number of line period */
-static int ov5640_get_shutter(void)
-{
- int shutter;
- u8 regval;
-
- shutter = (ov5640_read_reg(0x03500, ®val) & 0x0f);
-
- shutter = (shutter<<8) + ov5640_read_reg(0x3501, ®val);
- shutter = (shutter<<4) + (ov5640_read_reg(0x3502, ®val)>>4);
-
- return shutter;
-}
-
-/* write shutter, in number of line period */
-static int ov5640_set_shutter(int shutter)
-{
- int temp;
-
- shutter = shutter & 0xffff;
- temp = shutter & 0x0f;
- temp = temp<<4;
- ov5640_write_reg(0x3502, temp);
-
- temp = shutter & 0xfff;
- temp = temp>>4;
- ov5640_write_reg(0x3501, temp);
-
- temp = shutter>>12;
- ov5640_write_reg(0x3500, temp);
-
- return 0;
-}
-
-/* read gain, 16 = 1x */
-static int ov5640_get_gain16(void)
-{
- int gain16;
- u8 regval;
-
- gain16 = ov5640_read_reg(0x350a, ®val) & 0x03;
- gain16 = (gain16<<8) + ov5640_read_reg(0x350b, ®val);
-
- return gain16;
-}
-
-/* write gain, 16 = 1x */
-static int ov5640_set_gain16(int gain16)
-{
- int temp;
-
- gain16 = gain16 & 0x3ff;
- temp = gain16 & 0xff;
-
- ov5640_write_reg(0x350b, temp);
- temp = gain16>>8;
-
- ov5640_write_reg(0x350a, temp);
- return 0;
-}
-
-/* get banding filter value */
-static int ov5640_get_light_freq(void)
-{
- int temp, temp1, light_frequency;
- u8 regval;
-
- temp = ov5640_read_reg(0x3c01, ®val);
- if (temp & 0x80) {
- /* manual */
- temp1 = ov5640_read_reg(0x3c00, ®val);
- if (temp1 & 0x04) {
- /* 50Hz */
- light_frequency = 50;
- } else {
- /* 60Hz */
- light_frequency = 60;
- }
- } else {
- /* auto */
- temp1 = ov5640_read_reg(0x3c0c, ®val);
- if (temp1 & 0x01) {
- /* 50Hz */
- light_frequency = 50;
- } else {
- /* 60Hz */
- light_frequency = 60;
- }
- }
-
- return light_frequency;
-}
-
-static void ov5640_set_bandingfilter(void)
-{
- int prev_VTS;
- int band_step60, max_band60, band_step50, max_band50;
-
- /* read preview PCLK */
- prev_sysclk = ov5640_get_sysclk();
-
- /* read preview HTS */
- prev_HTS = ov5640_get_HTS();
-
- /* read preview VTS */
- prev_VTS = ov5640_get_VTS();
-
- /* calculate banding filter */
- /* 60Hz */
- band_step60 = prev_sysclk * 100/prev_HTS * 100/120;
- ov5640_write_reg(0x3a0a, (band_step60 >> 8));
- ov5640_write_reg(0x3a0b, (band_step60 & 0xff));
-
- max_band60 = (int)((prev_VTS-4)/band_step60);
- ov5640_write_reg(0x3a0d, max_band60);
-
- /* 50Hz */
- band_step50 = prev_sysclk * 100/prev_HTS;
- ov5640_write_reg(0x3a08, (band_step50 >> 8));
- ov5640_write_reg(0x3a09, (band_step50 & 0xff));
-
- max_band50 = (int)((prev_VTS-4)/band_step50);
- ov5640_write_reg(0x3a0e, max_band50);
-}
-
-/* stable in high */
-static int ov5640_set_AE_target(int target)
-{
- int fast_high, fast_low;
-
- AE_low = target * 23 / 25; /* 0.92 */
- AE_high = target * 27 / 25; /* 1.08 */
- fast_high = AE_high << 1;
-
- if (fast_high > 255)
- fast_high = 255;
- fast_low = AE_low >> 1;
-
- ov5640_write_reg(0x3a0f, AE_high);
- ov5640_write_reg(0x3a10, AE_low);
- ov5640_write_reg(0x3a1b, AE_high);
- ov5640_write_reg(0x3a1e, AE_low);
- ov5640_write_reg(0x3a11, fast_high);
- ov5640_write_reg(0x3a1f, fast_low);
-
- return 0;
-}
-
-/* enable = 0 to turn off night mode
- enable = 1 to turn on night mode */
-static int ov5640_set_night_mode(int enable)
-{
- u8 mode;
-
- ov5640_read_reg(0x3a00, &mode);
-
- if (enable) {
- /* night mode on */
- mode |= 0x04;
- ov5640_write_reg(0x3a00, mode);
- } else {
- /* night mode off */
- mode &= 0xfb;
- ov5640_write_reg(0x3a00, mode);
- }
-
- return 0;
-}
-
-/* enable = 0 to turn off AEC/AGC
- enable = 1 to turn on AEC/AGC */
-static void ov5640_turn_on_AE_AG(int enable)
-{
- u8 ae_ag_ctrl;
-
- ov5640_read_reg(0x3503, &ae_ag_ctrl);
- if (enable) {
- /* turn on auto AE/AG */
- ae_ag_ctrl = ae_ag_ctrl & ~(0x03);
- } else {
- /* turn off AE/AG */
- ae_ag_ctrl = ae_ag_ctrl | 0x03;
- }
- ov5640_write_reg(0x3503, ae_ag_ctrl);
-}
-
-/* download ov5640 settings to sensor through i2c */
-static int ov5640_download_firmware(struct reg_value *pModeSetting, s32 ArySize)
-{
- register u32 Delay_ms = 0;
- register u16 RegAddr = 0;
- register u8 Mask = 0;
- register u8 Val = 0;
- u8 RegVal = 0;
- int i, retval = 0;
-
- for (i = 0; i < ArySize; ++i, ++pModeSetting) {
- Delay_ms = pModeSetting->u32Delay_ms;
- RegAddr = pModeSetting->u16RegAddr;
- Val = pModeSetting->u8Val;
- Mask = pModeSetting->u8Mask;
-
- if (Mask) {
- retval = ov5640_read_reg(RegAddr, &RegVal);
- if (retval < 0)
- goto err;
-
- RegVal &= ~(u8)Mask;
- Val &= Mask;
- Val |= RegVal;
- }
-
- retval = ov5640_write_reg(RegAddr, Val);
- if (retval < 0)
- goto err;
-
- if (Delay_ms)
- msleep(Delay_ms);
- }
-err:
- return retval;
-}
-
-static int ov5640_init_mode(void)
-{
- struct reg_value *pModeSetting = NULL;
- int ArySize = 0, retval = 0;
-
- ov5640_soft_reset();
-
- pModeSetting = ov5640_global_init_setting;
- ArySize = ARRAY_SIZE(ov5640_global_init_setting);
- retval = ov5640_download_firmware(pModeSetting, ArySize);
- if (retval < 0)
- goto err;
-
- pModeSetting = ov5640_init_setting_30fps_VGA;
- ArySize = ARRAY_SIZE(ov5640_init_setting_30fps_VGA);
- retval = ov5640_download_firmware(pModeSetting, ArySize);
- if (retval < 0)
- goto err;
-
- /* change driver capability to 2x according to validation board.
- * if the image is not stable, please increase the driver strength.
- */
- ov5640_driver_capability(2);
- ov5640_set_bandingfilter();
- ov5640_set_AE_target(AE_Target);
- ov5640_set_night_mode(night_mode);
-
- /* skip 9 vysnc: start capture at 10th vsync */
- msleep(300);
-
- /* turn off night mode */
- night_mode = 0;
- ov5640_data.pix.width = 640;
- ov5640_data.pix.height = 480;
-err:
- return retval;
-}
-
-/* change to or back to subsampling mode set the mode directly
- * image size below 1280 * 960 is subsampling mode */
-static int ov5640_change_mode_direct(enum ov5640_frame_rate frame_rate,
- enum ov5640_mode mode)
-{
- struct reg_value *pModeSetting = NULL;
- s32 ArySize = 0;
- int retval = 0;
-
- if (mode > ov5640_mode_MAX || mode < ov5640_mode_MIN) {
- pr_err("Wrong ov5640 mode detected!\n");
- return -1;
- }
-
- pModeSetting = ov5640_mode_info_data[frame_rate][mode].init_data_ptr;
- ArySize =
- ov5640_mode_info_data[frame_rate][mode].init_data_size;
-
- ov5640_data.pix.width = ov5640_mode_info_data[frame_rate][mode].width;
- ov5640_data.pix.height = ov5640_mode_info_data[frame_rate][mode].height;
-
- if (ov5640_data.pix.width == 0 || ov5640_data.pix.height == 0 ||
- pModeSetting == NULL || ArySize == 0)
- return -EINVAL;
-
- /* set ov5640 to subsampling mode */
- retval = ov5640_download_firmware(pModeSetting, ArySize);
-
- /* turn on AE AG for subsampling mode, in case the firmware didn't */
- ov5640_turn_on_AE_AG(1);
-
- /* calculate banding filter */
- ov5640_set_bandingfilter();
-
- /* set AE target */
- ov5640_set_AE_target(AE_Target);
-
- /* update night mode setting */
- ov5640_set_night_mode(night_mode);
-
- /* skip 9 vysnc: start capture at 10th vsync */
- if (mode == ov5640_mode_XGA_1024_768 && frame_rate == ov5640_30_fps) {
- pr_warning("ov5640: actual frame rate of XGA is 22.5fps\n");
- /* 1/22.5 * 9*/
- msleep(400);
- return retval;
- }
-
- if (frame_rate == ov5640_15_fps) {
- /* 1/15 * 9*/
- msleep(600);
- } else if (frame_rate == ov5640_30_fps) {
- /* 1/30 * 9*/
- msleep(300);
- }
-
- return retval;
-}
-
-/* change to scaling mode go through exposure calucation
- * image size above 1280 * 960 is scaling mode */
-static int ov5640_change_mode_exposure_calc(enum ov5640_frame_rate frame_rate,
- enum ov5640_mode mode)
-{
- int prev_shutter, prev_gain16, average;
- int cap_shutter, cap_gain16;
- int cap_sysclk, cap_HTS, cap_VTS;
- int light_freq, cap_bandfilt, cap_maxband;
- long cap_gain16_shutter;
- u8 temp;
- struct reg_value *pModeSetting = NULL;
- s32 ArySize = 0;
- int retval = 0;
-
- /* check if the input mode and frame rate is valid */
- pModeSetting =
- ov5640_mode_info_data[frame_rate][mode].init_data_ptr;
- ArySize =
- ov5640_mode_info_data[frame_rate][mode].init_data_size;
-
- ov5640_data.pix.width =
- ov5640_mode_info_data[frame_rate][mode].width;
- ov5640_data.pix.height =
- ov5640_mode_info_data[frame_rate][mode].height;
-
- if (ov5640_data.pix.width == 0 || ov5640_data.pix.height == 0 ||
- pModeSetting == NULL || ArySize == 0)
- return -EINVAL;
-
- /* read preview shutter */
- prev_shutter = ov5640_get_shutter();
-
- /* read preview gain */
- prev_gain16 = ov5640_get_gain16();
-
- /* get average */
- average = ov5640_read_reg(0x56a1, &temp);
-
- /* turn off night mode for capture */
- ov5640_set_night_mode(0);
-
- /* turn off overlay */
- ov5640_write_reg(0x3022, 0x06);
-
- /* Write capture setting */
- retval = ov5640_download_firmware(pModeSetting, ArySize);
- if (retval < 0)
- goto err;
-
- /* turn off AE AG when capture image. */
- ov5640_turn_on_AE_AG(0);
-
- /* read capture VTS */
- cap_VTS = ov5640_get_VTS();
- cap_HTS = ov5640_get_HTS();
- cap_sysclk = ov5640_get_sysclk();
-
- /* calculate capture banding filter */
- light_freq = ov5640_get_light_freq();
- if (light_freq == 60) {
- /* 60Hz */
- cap_bandfilt = cap_sysclk * 100 / cap_HTS * 100 / 120;
- } else {
- /* 50Hz */
- cap_bandfilt = cap_sysclk * 100 / cap_HTS;
- }
- cap_maxband = (int)((cap_VTS - 4)/cap_bandfilt);
- /* calculate capture shutter/gain16 */
- if (average > AE_low && average < AE_high) {
- /* in stable range */
- cap_gain16_shutter =
- prev_gain16 * prev_shutter * cap_sysclk/prev_sysclk *
- prev_HTS/cap_HTS * AE_Target / average;
- } else {
- cap_gain16_shutter =
- prev_gain16 * prev_shutter * cap_sysclk/prev_sysclk *
- prev_HTS/cap_HTS;
- }
-
- /* gain to shutter */
- if (cap_gain16_shutter < (cap_bandfilt * 16)) {
- /* shutter < 1/100 */
- cap_shutter = cap_gain16_shutter/16;
- if (cap_shutter < 1)
- cap_shutter = 1;
- cap_gain16 = cap_gain16_shutter/cap_shutter;
- if (cap_gain16 < 16)
- cap_gain16 = 16;
- } else {
- if (cap_gain16_shutter > (cap_bandfilt*cap_maxband*16)) {
- /* exposure reach max */
- cap_shutter = cap_bandfilt*cap_maxband;
- cap_gain16 = cap_gain16_shutter / cap_shutter;
- } else {
- /* 1/100 < cap_shutter =< max, cap_shutter = n/100 */
- cap_shutter =
- ((int)(cap_gain16_shutter/16/cap_bandfilt))
- * cap_bandfilt;
- cap_gain16 = cap_gain16_shutter / cap_shutter;
- }
- }
-
- /* write capture gain */
- ov5640_set_gain16(cap_gain16);
-
- /* write capture shutter */
- if (cap_shutter > (cap_VTS - 4)) {
- cap_VTS = cap_shutter + 4;
- ov5640_set_VTS(cap_VTS);
- }
-
- ov5640_set_shutter(cap_shutter);
-
- /* skip 2 vysnc: start capture at 3rd vsync
- * frame rate of QSXGA and 1080P is 7.5fps: 1/7.5 * 2
- */
- pr_warning("ov5640: the actual frame rate of %s is 7.5fps\n",
- mode == ov5640_mode_1080P_1920_1080 ? "1080P" : "QSXGA");
- msleep(267);
-err:
- return retval;
-}
-
-static int ov5640_change_mode(enum ov5640_frame_rate frame_rate,
- enum ov5640_mode mode)
-{
- int retval = 0;
-
- if (mode > ov5640_mode_MAX || mode < ov5640_mode_MIN) {
- pr_err("Wrong ov5640 mode detected!\n");
- return -1;
- }
-
- if (mode == ov5640_mode_1080P_1920_1080 ||
- mode == ov5640_mode_QSXGA_2592_1944) {
- /* change to scaling mode go through exposure calucation
- * image size above 1280 * 960 is scaling mode */
- retval = ov5640_change_mode_exposure_calc(frame_rate, mode);
- } else {
- /* change back to subsampling modem download firmware directly
- * image size below 1280 * 960 is subsampling mode */
- retval = ov5640_change_mode_direct(frame_rate, mode);
- }
-
- return retval;
-}
-
-/*!
- * ov5640_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 ov5640_s_power(struct v4l2_subdev *sd, int on)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov5640 *sensor = to_ov5640(client);
-
- if (on)
- clk_enable(ov5640_data.sensor_clk);
- else
- clk_disable(ov5640_data.sensor_clk);
-
- sensor->on = on;
-
- return 0;
-}
-
-/*!
- * ov5640_g_parm - V4L2 sensor interface handler for VIDIOC_G_PARM ioctl
- * @s: pointer to standard V4L2 sub device structure
- * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure
- *
- * Returns the sensor's video CAPTURE parameters.
- */
-static int ov5640_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov5640 *sensor = to_ov5640(client);
- 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;
-}
-
-/*!
- * ov5460_s_parm - V4L2 sensor interface handler for VIDIOC_S_PARM ioctl
- * @s: pointer to standard V4L2 sub 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 ov5640_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov5640 *sensor = to_ov5640(client);
- struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe;
- u32 tgt_fps; /* target frames per secound */
- enum ov5640_frame_rate frame_rate;
- int ret = 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 = ov5640_15_fps;
- else if (tgt_fps == 30)
- frame_rate = ov5640_30_fps;
- else {
- pr_err(" The camera frame rate is not supported!\n");
- goto error;
- }
-
- ret = ov5640_change_mode(frame_rate,
- a->parm.capture.capturemode);
- if (ret < 0)
- goto error;
-
- sensor->streamcap.timeperframe = *timeperframe;
- sensor->streamcap.capturemode = a->parm.capture.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;
- }
-
-error:
- return ret;
-}
-
-static int ov5640_set_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_format *format)
-{
- struct v4l2_mbus_framefmt *mf = &format->format;
- const struct ov5640_datafmt *fmt = ov5640_find_datafmt(mf->code);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov5640 *sensor = to_ov5640(client);
-
- if (format->pad)
- return -EINVAL;
-
- if (!fmt) {
- mf->code = ov5640_colour_fmts[0].code;
- mf->colorspace = ov5640_colour_fmts[0].colorspace;
- }
-
- mf->field = V4L2_FIELD_NONE;
-
- if (format->which == V4L2_SUBDEV_FORMAT_TRY)
- return 0;
-
- sensor->fmt = fmt;
-
- return 0;
-}
-
-static int ov5640_get_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_format *format)
-{
- struct v4l2_mbus_framefmt *mf = &format->format;
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov5640 *sensor = to_ov5640(client);
- const struct ov5640_datafmt *fmt = sensor->fmt;
-
- if (format->pad)
- return -EINVAL;
-
- mf->code = fmt->code;
- mf->colorspace = fmt->colorspace;
- mf->field = V4L2_FIELD_NONE;
-
- return 0;
-}
-
-static int ov5640_enum_code(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_mbus_code_enum *code)
-{
- if (code->pad || code->index >= ARRAY_SIZE(ov5640_colour_fmts))
- return -EINVAL;
-
- code->code = ov5640_colour_fmts[code->index].code;
- return 0;
-}
-
-/*!
- * ov5640_enum_framesizes - V4L2 sensor interface handler for
- * VIDIOC_ENUM_FRAMESIZES ioctl
- * @s: pointer to standard V4L2 device structure
- * @fsize: standard V4L2 VIDIOC_ENUM_FRAMESIZES ioctl structure
- *
- * Return 0 if successful, otherwise -EINVAL.
- */
-static int ov5640_enum_framesizes(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_frame_size_enum *fse)
-{
- if (fse->index > ov5640_mode_MAX)
- return -EINVAL;
-
- fse->max_width =
- max(ov5640_mode_info_data[0][fse->index].width,
- ov5640_mode_info_data[1][fse->index].width);
- fse->min_width = fse->max_width;
- fse->max_height =
- max(ov5640_mode_info_data[0][fse->index].height,
- ov5640_mode_info_data[1][fse->index].height);
- fse->min_height = fse->max_height;
- return 0;
-}
-
-/*!
- * ov5640_enum_frameintervals - V4L2 sensor interface handler for
- * VIDIOC_ENUM_FRAMEINTERVALS ioctl
- * @s: pointer to standard V4L2 device structure
- * @fival: standard V4L2 VIDIOC_ENUM_FRAMEINTERVALS ioctl structure
- *
- * Return 0 if successful, otherwise -EINVAL.
- */
-static int ov5640_enum_frameintervals(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_frame_interval_enum *fie)
-{
- int i, j, count;
-
- if (fie->index < 0 || fie->index > ov5640_mode_MAX)
- return -EINVAL;
-
- if (fie->width == 0 || fie->height == 0 ||
- fie->code == 0) {
- pr_warning("Please assign pixel format, width and height.\n");
- return -EINVAL;
- }
-
- fie->interval.numerator = 1;
-
- count = 0;
- for (i = 0; i < ARRAY_SIZE(ov5640_mode_info_data); i++) {
- for (j = 0; j < (ov5640_mode_MAX + 1); j++) {
- if (fie->width == ov5640_mode_info_data[i][j].width
- && fie->height == ov5640_mode_info_data[i][j].height
- && ov5640_mode_info_data[i][j].init_data_ptr != NULL) {
- count++;
- }
- if (fie->index == (count - 1)) {
- fie->interval.denominator =
- ov5640_framerates[i];
- return 0;
- }
- }
- }
-
- return -EINVAL;
-}
-
-static int ov5640_set_clk_rate(void)
-{
- u32 tgt_xclk; /* target xclk */
- int ret;
-
- /* mclk */
- tgt_xclk = ov5640_data.mclk;
- tgt_xclk = min(tgt_xclk, (u32)OV5640_XCLK_MAX);
- tgt_xclk = max(tgt_xclk, (u32)OV5640_XCLK_MIN);
- ov5640_data.mclk = tgt_xclk;
-
- pr_debug(" Setting mclk to %d MHz\n", tgt_xclk / 1000000);
- ret = clk_set_rate(ov5640_data.sensor_clk, ov5640_data.mclk);
- if (ret < 0)
- pr_debug("set rate filed, rate=%d\n", ov5640_data.mclk);
- return ret;
-}
-
-/*!
- * dev_init - V4L2 sensor init
- * @s: pointer to standard V4L2 device structure
- *
- */
-static int init_device(void)
-{
- u32 tgt_xclk; /* target xclk */
- u32 tgt_fps; /* target frames per secound */
- enum ov5640_frame_rate frame_rate;
- int ret;
-
- ov5640_data.on = true;
-
- /* mclk */
- tgt_xclk = ov5640_data.mclk;
-
- /* Default camera frame rate is set in probe */
- tgt_fps = ov5640_data.streamcap.timeperframe.denominator /
- ov5640_data.streamcap.timeperframe.numerator;
-
- if (tgt_fps == 15)
- frame_rate = ov5640_15_fps;
- else if (tgt_fps == 30)
- frame_rate = ov5640_30_fps;
- else
- return -EINVAL; /* Only support 15fps or 30fps now. */
-
- ret = ov5640_init_mode();
-
- return ret;
-}
-
-static struct v4l2_subdev_video_ops ov5640_subdev_video_ops = {
- .g_parm = ov5640_g_parm,
- .s_parm = ov5640_s_parm,
-};
-
-static const struct v4l2_subdev_pad_ops ov5640_subdev_pad_ops = {
- .enum_frame_size = ov5640_enum_framesizes,
- .enum_frame_interval = ov5640_enum_frameintervals,
- .enum_mbus_code = ov5640_enum_code,
- .set_fmt = ov5640_set_fmt,
- .get_fmt = ov5640_get_fmt,
-};
-
-static struct v4l2_subdev_core_ops ov5640_subdev_core_ops = {
- .s_power = ov5640_s_power,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .g_register = ov5640_get_register,
- .s_register = ov5640_set_register,
-#endif
-};
-
-static struct v4l2_subdev_ops ov5640_subdev_ops = {
- .core = &ov5640_subdev_core_ops,
- .video = &ov5640_subdev_video_ops,
- .pad = &ov5640_subdev_pad_ops,
-};
-
-/*!
- * ov5640 I2C probe function
- *
- * @param adapter struct i2c_adapter *
- * @return Error code indicating success or failure
- */
-static int ov5640_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- struct pinctrl *pinctrl;
- struct device *dev = &client->dev;
- int retval;
- u8 chip_id_high, chip_id_low;
-
- /* ov5640 pinctrl */
- pinctrl = devm_pinctrl_get_select_default(dev);
- if (IS_ERR(pinctrl)) {
- dev_err(dev, "setup pinctrl failed\n");
- return PTR_ERR(pinctrl);
- }
-
- /* request power down pin */
- pwn_gpio = of_get_named_gpio(dev->of_node, "pwn-gpios", 0);
- if (!gpio_is_valid(pwn_gpio)) {
- dev_err(dev, "no sensor pwdn pin available\n");
- return -ENODEV;
- }
- retval = devm_gpio_request_one(dev, pwn_gpio, GPIOF_OUT_INIT_HIGH,
- "ov5640_pwdn");
- if (retval < 0)
- return retval;
-
- /* request reset pin */
- rst_gpio = of_get_named_gpio(dev->of_node, "rst-gpios", 0);
- if (!gpio_is_valid(rst_gpio)) {
- dev_err(dev, "no sensor reset pin available\n");
- return -EINVAL;
- }
- retval = devm_gpio_request_one(dev, rst_gpio, GPIOF_OUT_INIT_HIGH,
- "ov5640_reset");
- if (retval < 0)
- return retval;
-
- /* Set initial values for the sensor struct. */
- memset(&ov5640_data, 0, sizeof(ov5640_data));
- ov5640_data.sensor_clk = devm_clk_get(dev, "csi_mclk");
- if (IS_ERR(ov5640_data.sensor_clk)) {
- dev_err(dev, "get mclk failed\n");
- return PTR_ERR(ov5640_data.sensor_clk);
- }
-
- retval = of_property_read_u32(dev->of_node, "mclk",
- &ov5640_data.mclk);
- if (retval) {
- dev_err(dev, "mclk frequency is invalid\n");
- return retval;
- }
-
- retval = of_property_read_u32(dev->of_node, "mclk_source",
- (u32 *) &(ov5640_data.mclk_source));
- if (retval) {
- dev_err(dev, "mclk_source invalid\n");
- return retval;
- }
-
- retval = of_property_read_u32(dev->of_node, "csi_id",
- &(ov5640_data.csi));
- if (retval) {
- dev_err(dev, "csi_id invalid\n");
- return retval;
- }
-
- /* Set mclk rate before clk on */
- ov5640_set_clk_rate();
-
- clk_prepare_enable(ov5640_data.sensor_clk);
-
- ov5640_data.io_init = ov5640_reset;
- ov5640_data.i2c_client = client;
- ov5640_data.pix.pixelformat = V4L2_PIX_FMT_YUYV;
- ov5640_data.pix.width = 640;
- ov5640_data.pix.height = 480;
- ov5640_data.streamcap.capability = V4L2_MODE_HIGHQUALITY |
- V4L2_CAP_TIMEPERFRAME;
- ov5640_data.streamcap.capturemode = 0;
- ov5640_data.streamcap.timeperframe.denominator = DEFAULT_FPS;
- ov5640_data.streamcap.timeperframe.numerator = 1;
-
- ov5640_regulator_enable(&client->dev);
-
- ov5640_reset();
-
- ov5640_power_down(0);
-
- retval = ov5640_read_reg(OV5640_CHIP_ID_HIGH_BYTE, &chip_id_high);
- if (retval < 0 || chip_id_high != 0x56) {
- clk_disable_unprepare(ov5640_data.sensor_clk);
- pr_warning("camera ov5640 is not found\n");
- return -ENODEV;
- }
- retval = ov5640_read_reg(OV5640_CHIP_ID_LOW_BYTE, &chip_id_low);
- if (retval < 0 || chip_id_low != 0x40) {
- clk_disable_unprepare(ov5640_data.sensor_clk);
- pr_warning("camera ov5640 is not found\n");
- return -ENODEV;
- }
-
- retval = init_device();
- if (retval < 0) {
- clk_disable_unprepare(ov5640_data.sensor_clk);
- pr_warning("camera ov5640 init failed\n");
- ov5640_power_down(1);
- return retval;
- }
-
- clk_disable(ov5640_data.sensor_clk);
-
- v4l2_i2c_subdev_init(&ov5640_data.subdev, client, &ov5640_subdev_ops);
-
- retval = v4l2_async_register_subdev(&ov5640_data.subdev);
- if (retval < 0)
- dev_err(&client->dev,
- "%s--Async register failed, ret=%d\n", __func__, retval);
-
- pr_info("camera ov5640, is found\n");
- return retval;
-}
-
-/*!
- * ov5640 I2C detach function
- *
- * @param client struct i2c_client *
- * @return Error code indicating success or failure
- */
-static int ov5640_remove(struct i2c_client *client)
-{
- struct v4l2_subdev *sd = i2c_get_clientdata(client);
-
- v4l2_async_unregister_subdev(sd);
-
- clk_unprepare(ov5640_data.sensor_clk);
-
- ov5640_power_down(1);
-
- if (analog_regulator)
- regulator_disable(analog_regulator);
-
- if (core_regulator)
- regulator_disable(core_regulator);
-
- if (io_regulator)
- regulator_disable(io_regulator);
-
- return 0;
-}
-
-module_i2c_driver(ov5640_i2c_driver);
-
-MODULE_AUTHOR("Freescale Semiconductor, Inc.");
-MODULE_DESCRIPTION("OV5640 Camera Driver");
-MODULE_LICENSE("GPL");
-MODULE_VERSION("1.0");
-MODULE_ALIAS("CSI");
+++ /dev/null
-/*
- * Copyright (C) 2011-2016 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/clk.h>
-#include <linux/of_device.h>
-#include <linux/i2c.h>
-#include <linux/of_gpio.h>
-#include <linux/pinctrl/consumer.h>
-#include <linux/regulator/consumer.h>
-#include <linux/v4l2-mediabus.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ctrls.h>
-
-#define OV5640_VOLTAGE_ANALOG 2800000
-#define OV5640_VOLTAGE_DIGITAL_CORE 1500000
-#define OV5640_VOLTAGE_DIGITAL_IO 1800000
-
-#define MIN_FPS 15
-#define MAX_FPS 30
-#define DEFAULT_FPS 30
-
-#define OV5640_XCLK_MIN 6000000
-#define OV5640_XCLK_MAX 24000000
-
-#define OV5640_CHIP_ID_HIGH_BYTE 0x300A
-#define OV5640_CHIP_ID_LOW_BYTE 0x300B
-
-enum ov5640_mode {
- ov5640_mode_MIN = 0,
- ov5640_mode_VGA_640_480 = 0,
- ov5640_mode_NTSC_720_480 = 1,
- ov5640_mode_720P_1280_720 = 2,
- ov5640_mode_1080P_1920_1080 = 3,
- ov5640_mode_QSXGA_2592_1944 = 4,
- ov5640_mode_MAX = 5,
- ov5640_mode_INIT = 0xff, /*only for sensor init*/
-};
-
-enum ov5640_frame_rate {
- ov5640_15_fps,
- ov5640_30_fps
-};
-
-static int ov5640_framerates[] = {
- [ov5640_15_fps] = 15,
- [ov5640_30_fps] = 30,
-};
-
-struct ov5640_datafmt {
- u32 code;
- enum v4l2_colorspace colorspace;
-};
-
-/* image size under 1280 * 960 are SUBSAMPLING
- * image size upper 1280 * 960 are SCALING
- */
-enum ov5640_downsize_mode {
- SUBSAMPLING,
- SCALING,
-};
-
-struct reg_value {
- u16 u16RegAddr;
- u8 u8Val;
- u8 u8Mask;
- u32 u32Delay_ms;
-};
-
-struct ov5640_mode_info {
- enum ov5640_mode mode;
- enum ov5640_downsize_mode dn_mode;
- u32 width;
- u32 height;
- struct reg_value *init_data_ptr;
- u32 init_data_size;
-};
-
-struct ov5640 {
- struct v4l2_subdev subdev;
- struct i2c_client *i2c_client;
- struct v4l2_pix_format pix;
- const struct ov5640_datafmt *fmt;
- 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;
- u8 mclk_source;
- struct clk *sensor_clk;
- int csi;
-
- void (*io_init)(void);
-};
-/*!
- * Maintains the information on the current state of the sesor.
- */
-static struct ov5640 ov5640_data;
-static int pwn_gpio, rst_gpio;
-
-static struct reg_value ov5640_init_setting_30fps_VGA[] = {
-
- {0x3103, 0x11, 0, 0}, {0x3008, 0x82, 0, 5}, {0x3008, 0x42, 0, 0},
- {0x3103, 0x03, 0, 0}, {0x3017, 0x00, 0, 0}, {0x3018, 0x00, 0, 0},
- {0x3034, 0x18, 0, 0}, {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0},
- {0x3037, 0x13, 0, 0}, {0x3108, 0x01, 0, 0}, {0x3630, 0x36, 0, 0},
- {0x3631, 0x0e, 0, 0}, {0x3632, 0xe2, 0, 0}, {0x3633, 0x12, 0, 0},
- {0x3621, 0xe0, 0, 0}, {0x3704, 0xa0, 0, 0}, {0x3703, 0x5a, 0, 0},
- {0x3715, 0x78, 0, 0}, {0x3717, 0x01, 0, 0}, {0x370b, 0x60, 0, 0},
- {0x3705, 0x1a, 0, 0}, {0x3905, 0x02, 0, 0}, {0x3906, 0x10, 0, 0},
- {0x3901, 0x0a, 0, 0}, {0x3731, 0x12, 0, 0}, {0x3600, 0x08, 0, 0},
- {0x3601, 0x33, 0, 0}, {0x302d, 0x60, 0, 0}, {0x3620, 0x52, 0, 0},
- {0x371b, 0x20, 0, 0}, {0x471c, 0x50, 0, 0}, {0x3a13, 0x43, 0, 0},
- {0x3a18, 0x00, 0, 0}, {0x3a19, 0xf8, 0, 0}, {0x3635, 0x13, 0, 0},
- {0x3636, 0x03, 0, 0}, {0x3634, 0x40, 0, 0}, {0x3622, 0x01, 0, 0},
- {0x3c01, 0xa4, 0, 0}, {0x3c04, 0x28, 0, 0}, {0x3c05, 0x98, 0, 0},
- {0x3c06, 0x00, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c08, 0x00, 0, 0},
- {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
- {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
- {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
- {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
- {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
- {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0},
- {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
- {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
- {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
- {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
- {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
- {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
- {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
- {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
- {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x3000, 0x00, 0, 0},
- {0x3002, 0x1c, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3006, 0xc3, 0, 0},
- {0x300e, 0x45, 0, 0}, {0x302e, 0x08, 0, 0}, {0x4300, 0x30, 0, 0},
- {0x501f, 0x00, 0, 0}, {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0},
- {0x440e, 0x00, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
- {0x4837, 0x0a, 0, 0}, {0x4800, 0x04, 0, 0}, {0x3824, 0x02, 0, 0},
- {0x5000, 0xa7, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x5180, 0xff, 0, 0},
- {0x5181, 0xf2, 0, 0}, {0x5182, 0x00, 0, 0}, {0x5183, 0x14, 0, 0},
- {0x5184, 0x25, 0, 0}, {0x5185, 0x24, 0, 0}, {0x5186, 0x09, 0, 0},
- {0x5187, 0x09, 0, 0}, {0x5188, 0x09, 0, 0}, {0x5189, 0x88, 0, 0},
- {0x518a, 0x54, 0, 0}, {0x518b, 0xee, 0, 0}, {0x518c, 0xb2, 0, 0},
- {0x518d, 0x50, 0, 0}, {0x518e, 0x34, 0, 0}, {0x518f, 0x6b, 0, 0},
- {0x5190, 0x46, 0, 0}, {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0},
- {0x5193, 0x70, 0, 0}, {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0},
- {0x5196, 0x03, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0},
- {0x5199, 0x6c, 0, 0}, {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0},
- {0x519c, 0x09, 0, 0}, {0x519d, 0x2b, 0, 0}, {0x519e, 0x38, 0, 0},
- {0x5381, 0x1e, 0, 0}, {0x5382, 0x5b, 0, 0}, {0x5383, 0x08, 0, 0},
- {0x5384, 0x0a, 0, 0}, {0x5385, 0x7e, 0, 0}, {0x5386, 0x88, 0, 0},
- {0x5387, 0x7c, 0, 0}, {0x5388, 0x6c, 0, 0}, {0x5389, 0x10, 0, 0},
- {0x538a, 0x01, 0, 0}, {0x538b, 0x98, 0, 0}, {0x5300, 0x08, 0, 0},
- {0x5301, 0x30, 0, 0}, {0x5302, 0x10, 0, 0}, {0x5303, 0x00, 0, 0},
- {0x5304, 0x08, 0, 0}, {0x5305, 0x30, 0, 0}, {0x5306, 0x08, 0, 0},
- {0x5307, 0x16, 0, 0}, {0x5309, 0x08, 0, 0}, {0x530a, 0x30, 0, 0},
- {0x530b, 0x04, 0, 0}, {0x530c, 0x06, 0, 0}, {0x5480, 0x01, 0, 0},
- {0x5481, 0x08, 0, 0}, {0x5482, 0x14, 0, 0}, {0x5483, 0x28, 0, 0},
- {0x5484, 0x51, 0, 0}, {0x5485, 0x65, 0, 0}, {0x5486, 0x71, 0, 0},
- {0x5487, 0x7d, 0, 0}, {0x5488, 0x87, 0, 0}, {0x5489, 0x91, 0, 0},
- {0x548a, 0x9a, 0, 0}, {0x548b, 0xaa, 0, 0}, {0x548c, 0xb8, 0, 0},
- {0x548d, 0xcd, 0, 0}, {0x548e, 0xdd, 0, 0}, {0x548f, 0xea, 0, 0},
- {0x5490, 0x1d, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5583, 0x40, 0, 0},
- {0x5584, 0x10, 0, 0}, {0x5589, 0x10, 0, 0}, {0x558a, 0x00, 0, 0},
- {0x558b, 0xf8, 0, 0}, {0x5800, 0x23, 0, 0}, {0x5801, 0x14, 0, 0},
- {0x5802, 0x0f, 0, 0}, {0x5803, 0x0f, 0, 0}, {0x5804, 0x12, 0, 0},
- {0x5805, 0x26, 0, 0}, {0x5806, 0x0c, 0, 0}, {0x5807, 0x08, 0, 0},
- {0x5808, 0x05, 0, 0}, {0x5809, 0x05, 0, 0}, {0x580a, 0x08, 0, 0},
- {0x580b, 0x0d, 0, 0}, {0x580c, 0x08, 0, 0}, {0x580d, 0x03, 0, 0},
- {0x580e, 0x00, 0, 0}, {0x580f, 0x00, 0, 0}, {0x5810, 0x03, 0, 0},
- {0x5811, 0x09, 0, 0}, {0x5812, 0x07, 0, 0}, {0x5813, 0x03, 0, 0},
- {0x5814, 0x00, 0, 0}, {0x5815, 0x01, 0, 0}, {0x5816, 0x03, 0, 0},
- {0x5817, 0x08, 0, 0}, {0x5818, 0x0d, 0, 0}, {0x5819, 0x08, 0, 0},
- {0x581a, 0x05, 0, 0}, {0x581b, 0x06, 0, 0}, {0x581c, 0x08, 0, 0},
- {0x581d, 0x0e, 0, 0}, {0x581e, 0x29, 0, 0}, {0x581f, 0x17, 0, 0},
- {0x5820, 0x11, 0, 0}, {0x5821, 0x11, 0, 0}, {0x5822, 0x15, 0, 0},
- {0x5823, 0x28, 0, 0}, {0x5824, 0x46, 0, 0}, {0x5825, 0x26, 0, 0},
- {0x5826, 0x08, 0, 0}, {0x5827, 0x26, 0, 0}, {0x5828, 0x64, 0, 0},
- {0x5829, 0x26, 0, 0}, {0x582a, 0x24, 0, 0}, {0x582b, 0x22, 0, 0},
- {0x582c, 0x24, 0, 0}, {0x582d, 0x24, 0, 0}, {0x582e, 0x06, 0, 0},
- {0x582f, 0x22, 0, 0}, {0x5830, 0x40, 0, 0}, {0x5831, 0x42, 0, 0},
- {0x5832, 0x24, 0, 0}, {0x5833, 0x26, 0, 0}, {0x5834, 0x24, 0, 0},
- {0x5835, 0x22, 0, 0}, {0x5836, 0x22, 0, 0}, {0x5837, 0x26, 0, 0},
- {0x5838, 0x44, 0, 0}, {0x5839, 0x24, 0, 0}, {0x583a, 0x26, 0, 0},
- {0x583b, 0x28, 0, 0}, {0x583c, 0x42, 0, 0}, {0x583d, 0xce, 0, 0},
- {0x5025, 0x00, 0, 0}, {0x3a0f, 0x30, 0, 0}, {0x3a10, 0x28, 0, 0},
- {0x3a1b, 0x30, 0, 0}, {0x3a1e, 0x26, 0, 0}, {0x3a11, 0x60, 0, 0},
- {0x3a1f, 0x14, 0, 0}, {0x3008, 0x42, 0, 0}, {0x3c00, 0x04, 0, 300},
-};
-
-static struct reg_value ov5640_setting_30fps_VGA_640_480[] = {
- {0x3008, 0x42, 0, 0},
- {0x3035, 0x12, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
- {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
- {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
- {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
- {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
- {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
- {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0},
- {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
- {0x380e, 0x04, 0, 0}, {0x380f, 0x38, 0, 0}, {0x3810, 0x00, 0, 0},
- {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
- {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
- {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
- {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x0e, 0, 0},
- {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
- {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
- {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
- {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
- {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
- {0x4005, 0x1a, 0, 0}, {0x3008, 0x02, 0, 0}, {0x3503, 0x00, 0, 0},
-};
-
-static struct reg_value ov5640_setting_30fps_NTSC_720_480[] = {
- {0x3008, 0x42, 0, 0},
- {0x3035, 0x12, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
- {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
- {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
- {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
- {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
- {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
- {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x01, 0, 0},
- {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
- {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
- {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x3c, 0, 0},
- {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
- {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
- {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
- {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
- {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
- {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
- {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
- {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
- {0x4005, 0x1a, 0, 0}, {0x3008, 0x02, 0, 0}, {0x3503, 0, 0, 0},
-};
-
-static struct reg_value ov5640_setting_30fps_720P_1280_720[] = {
- {0x3008, 0x42, 0, 0},
- {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x07, 0, 0},
- {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
- {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
- {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
- {0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0},
- {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0},
- {0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x02, 0, 0},
- {0x380b, 0xd0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x64, 0, 0},
- {0x380e, 0x02, 0, 0}, {0x380f, 0xe4, 0, 0}, {0x3810, 0x00, 0, 0},
- {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
- {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
- {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x02, 0, 0},
- {0x3a03, 0xe4, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0xbc, 0, 0},
- {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x72, 0, 0}, {0x3a0e, 0x01, 0, 0},
- {0x3a0d, 0x02, 0, 0}, {0x3a14, 0x02, 0, 0}, {0x3a15, 0xe4, 0, 0},
- {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x02, 0, 0},
- {0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0},
- {0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0}, {0x4005, 0x1a, 0, 0},
- {0x3008, 0x02, 0, 0}, {0x3503, 0, 0, 0},
-};
-
-static struct reg_value ov5640_setting_30fps_1080P_1920_1080[] = {
- {0x3008, 0x42, 0, 0},
- {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x08, 0, 0},
- {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
- {0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, {0x3814, 0x11, 0, 0},
- {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
- {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0},
- {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0},
- {0x3808, 0x0a, 0, 0}, {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0},
- {0x380b, 0x98, 0, 0}, {0x380c, 0x0b, 0, 0}, {0x380d, 0x1c, 0, 0},
- {0x380e, 0x07, 0, 0}, {0x380f, 0xb0, 0, 0}, {0x3810, 0x00, 0, 0},
- {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
- {0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0},
- {0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0},
- {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
- {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
- {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
- {0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4713, 0x03, 0, 0},
- {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
- {0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 0}, {0x3035, 0x11, 0, 0},
- {0x3036, 0x54, 0, 0}, {0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0},
- {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
- {0x3800, 0x01, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3802, 0x01, 0, 0},
- {0x3803, 0xb2, 0, 0}, {0x3804, 0x08, 0, 0}, {0x3805, 0xef, 0, 0},
- {0x3806, 0x05, 0, 0}, {0x3807, 0xf1, 0, 0}, {0x3808, 0x07, 0, 0},
- {0x3809, 0x80, 0, 0}, {0x380a, 0x04, 0, 0}, {0x380b, 0x38, 0, 0},
- {0x380c, 0x09, 0, 0}, {0x380d, 0xc4, 0, 0}, {0x380e, 0x04, 0, 0},
- {0x380f, 0x60, 0, 0}, {0x3612, 0x2b, 0, 0}, {0x3708, 0x64, 0, 0},
- {0x3a02, 0x04, 0, 0}, {0x3a03, 0x60, 0, 0}, {0x3a08, 0x01, 0, 0},
- {0x3a09, 0x50, 0, 0}, {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x18, 0, 0},
- {0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x04, 0, 0},
- {0x3a15, 0x60, 0, 0}, {0x4713, 0x02, 0, 0}, {0x4407, 0x04, 0, 0},
- {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3824, 0x04, 0, 0},
- {0x4005, 0x1a, 0, 0}, {0x3008, 0x02, 0, 0},
- {0x3503, 0, 0, 0},
-};
-
-static struct reg_value ov5640_setting_15fps_QSXGA_2592_1944[] = {
- {0x3008, 0x42, 0, 0},
- {0x4202, 0x0f, 0, 0}, /* stream off the sensor */
- {0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, /*disable flip*/
- {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x08, 0, 0},
- {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
- {0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, {0x3814, 0x11, 0, 0},
- {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
- {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0},
- {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0},
- {0x3808, 0x0a, 0, 0}, {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0},
- {0x380b, 0x98, 0, 0}, {0x380c, 0x0b, 0, 0}, {0x380d, 0x1c, 0, 0},
- {0x380e, 0x07, 0, 0}, {0x380f, 0xb0, 0, 0}, {0x3810, 0x00, 0, 0},
- {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
- {0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0},
- {0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0},
- {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
- {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
- {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
- {0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4713, 0x03, 0, 0},
- {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
- {0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 70}, {0x3008, 0x02, 0, 0},
- {0x4202, 0x00, 0, 0}, /* stream on the sensor */
-};
-
-static struct ov5640_mode_info ov5640_mode_info_data[2][ov5640_mode_MAX + 1] = {
- {
- {ov5640_mode_VGA_640_480, -1, 0, 0, NULL, 0},
- {ov5640_mode_NTSC_720_480, -1, 0, 0, NULL, 0},
- {ov5640_mode_720P_1280_720, -1, 0, 0, NULL, 0},
- {ov5640_mode_1080P_1920_1080, -1, 0, 0, NULL, 0},
- {ov5640_mode_QSXGA_2592_1944, SCALING, 2592, 1944,
- ov5640_setting_15fps_QSXGA_2592_1944,
- ARRAY_SIZE(ov5640_setting_15fps_QSXGA_2592_1944)},
- },
- {
- {ov5640_mode_VGA_640_480, SUBSAMPLING, 640, 480,
- ov5640_setting_30fps_VGA_640_480,
- ARRAY_SIZE(ov5640_setting_30fps_VGA_640_480)},
- {ov5640_mode_NTSC_720_480, SUBSAMPLING, 720, 480,
- ov5640_setting_30fps_NTSC_720_480,
- ARRAY_SIZE(ov5640_setting_30fps_NTSC_720_480)},
- {ov5640_mode_720P_1280_720, SUBSAMPLING, 1280, 720,
- ov5640_setting_30fps_720P_1280_720,
- ARRAY_SIZE(ov5640_setting_30fps_720P_1280_720)},
- {ov5640_mode_1080P_1920_1080, SCALING, 1920, 1080,
- ov5640_setting_30fps_1080P_1920_1080,
- ARRAY_SIZE(ov5640_setting_30fps_1080P_1920_1080)},
- {ov5640_mode_QSXGA_2592_1944, -1, 0, 0, NULL, 0},
- },
-};
-
-static struct regulator *io_regulator;
-static struct regulator *core_regulator;
-static struct regulator *analog_regulator;
-static struct regulator *gpo_regulator;
-
-static int ov5640_probe(struct i2c_client *adapter,
- const struct i2c_device_id *device_id);
-static int ov5640_remove(struct i2c_client *client);
-
-static s32 ov5640_read_reg(u16 reg, u8 *val);
-static s32 ov5640_write_reg(u16 reg, u8 val);
-
-static const struct i2c_device_id ov5640_id[] = {
- {"ov5640_mipi", 0},
- {},
-};
-
-MODULE_DEVICE_TABLE(i2c, ov5640_id);
-
-static struct i2c_driver ov5640_i2c_driver = {
- .driver = {
- .owner = THIS_MODULE,
- .name = "ov5640_mipi",
- },
- .probe = ov5640_probe,
- .remove = ov5640_remove,
- .id_table = ov5640_id,
-};
-
-static const struct ov5640_datafmt ov5640_colour_fmts[] = {
- {MEDIA_BUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG},
-};
-
-static struct ov5640 *to_ov5640(const struct i2c_client *client)
-{
- return container_of(i2c_get_clientdata(client), struct ov5640, subdev);
-}
-
-/* Find a data format by a pixel code in an array */
-static const struct ov5640_datafmt
- *ov5640_find_datafmt(u32 code)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(ov5640_colour_fmts); i++)
- if (ov5640_colour_fmts[i].code == code)
- return ov5640_colour_fmts + i;
-
- return NULL;
-}
-
-static inline void ov5640_power_down(int enable)
-{
- if (pwn_gpio < 0)
- return;
-
- if (!enable)
- gpio_set_value_cansleep(pwn_gpio, 0);
- else
- gpio_set_value_cansleep(pwn_gpio, 1);
-
- msleep(2);
-}
-
-static void ov5640_reset(void)
-{
- if (rst_gpio < 0 || pwn_gpio < 0)
- return;
-
- /* camera reset */
- gpio_set_value(rst_gpio, 1);
-
- /* camera power dowmn */
- gpio_set_value(pwn_gpio, 1);
- msleep(5);
-
- gpio_set_value(pwn_gpio, 0);
- msleep(5);
-
- gpio_set_value(rst_gpio, 0);
- msleep(1);
-
- gpio_set_value(rst_gpio, 1);
- msleep(5);
-
- gpio_set_value(pwn_gpio, 1);
-}
-
-static int ov5640_regulator_enable(struct device *dev)
-{
- int ret = 0;
-
- io_regulator = devm_regulator_get(dev, "DOVDD");
- if (!IS_ERR(io_regulator)) {
- regulator_set_voltage(io_regulator,
- OV5640_VOLTAGE_DIGITAL_IO,
- OV5640_VOLTAGE_DIGITAL_IO);
- ret = regulator_enable(io_regulator);
- if (ret) {
- pr_err("%s:io set voltage error\n", __func__);
- return ret;
- } else {
- dev_dbg(dev,
- "%s:io set voltage ok\n", __func__);
- }
- } else {
- pr_err("%s: cannot get io voltage error\n", __func__);
- io_regulator = NULL;
- }
-
- core_regulator = devm_regulator_get(dev, "DVDD");
- if (!IS_ERR(core_regulator)) {
- regulator_set_voltage(core_regulator,
- OV5640_VOLTAGE_DIGITAL_CORE,
- OV5640_VOLTAGE_DIGITAL_CORE);
- ret = regulator_enable(core_regulator);
- if (ret) {
- pr_err("%s:core set voltage error\n", __func__);
- return ret;
- } else {
- dev_dbg(dev,
- "%s:core set voltage ok\n", __func__);
- }
- } else {
- core_regulator = NULL;
- pr_err("%s: cannot get core voltage error\n", __func__);
- }
-
- analog_regulator = devm_regulator_get(dev, "AVDD");
- if (!IS_ERR(analog_regulator)) {
- regulator_set_voltage(analog_regulator,
- OV5640_VOLTAGE_ANALOG,
- OV5640_VOLTAGE_ANALOG);
- ret = regulator_enable(analog_regulator);
- if (ret) {
- pr_err("%s:analog set voltage error\n",
- __func__);
- return ret;
- } else {
- dev_dbg(dev,
- "%s:analog set voltage ok\n", __func__);
- }
- } else {
- analog_regulator = NULL;
- pr_err("%s: cannot get analog voltage error\n", __func__);
- }
-
- return ret;
-}
-
-static s32 ov5640_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(ov5640_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 ov5640_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(ov5640_data.i2c_client, au8RegBuf, 2)) {
- pr_err("%s:write reg error:reg=%x\n",
- __func__, reg);
- return -1;
- }
-
- if (1 != i2c_master_recv(ov5640_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 prev_sysclk, prev_HTS;
-static int AE_low, AE_high, AE_Target = 52;
-
-static void OV5640_stream_on(void)
-{
- ov5640_write_reg(0x4202, 0x00);
-}
-
-static void OV5640_stream_off(void)
-{
- ov5640_write_reg(0x4202, 0x0f);
- ov5640_write_reg(0x3008, 0x42);
-}
-
-static int OV5640_get_sysclk(void)
-{
- /* calculate sysclk */
- int xvclk = ov5640_data.mclk / 10000;
- int temp1, temp2;
- int Multiplier, PreDiv, VCO, SysDiv, Pll_rdiv;
- int Bit_div2x = 1, sclk_rdiv, sysclk;
- u8 temp;
-
- int sclk_rdiv_map[] = {1, 2, 4, 8};
-
- temp1 = ov5640_read_reg(0x3034, &temp);
- temp2 = temp1 & 0x0f;
- if (temp2 == 8 || temp2 == 10)
- Bit_div2x = temp2 / 2;
-
- temp1 = ov5640_read_reg(0x3035, &temp);
- SysDiv = temp1>>4;
- if (SysDiv == 0)
- SysDiv = 16;
-
- temp1 = ov5640_read_reg(0x3036, &temp);
- Multiplier = temp1;
-
- temp1 = ov5640_read_reg(0x3037, &temp);
- PreDiv = temp1 & 0x0f;
- Pll_rdiv = ((temp1 >> 4) & 0x01) + 1;
-
- temp1 = ov5640_read_reg(0x3108, &temp);
- temp2 = temp1 & 0x03;
- sclk_rdiv = sclk_rdiv_map[temp2];
-
- VCO = xvclk * Multiplier / PreDiv;
-
- sysclk = VCO / SysDiv / Pll_rdiv * 2 / Bit_div2x / sclk_rdiv;
-
- return sysclk;
-}
-
-static void OV5640_set_night_mode(void)
-{
- /* read HTS from register settings */
- u8 mode;
-
- ov5640_read_reg(0x3a00, &mode);
- mode &= 0xfb;
- ov5640_write_reg(0x3a00, mode);
-}
-
-static int OV5640_get_HTS(void)
-{
- /* read HTS from register settings */
- int HTS;
- u8 temp;
-
- HTS = ov5640_read_reg(0x380c, &temp);
- HTS = (HTS<<8) + ov5640_read_reg(0x380d, &temp);
-
- return HTS;
-}
-
-static int OV5640_get_VTS(void)
-{
- /* read VTS from register settings */
- int VTS;
- u8 temp;
-
- /* total vertical size[15:8] high byte */
- VTS = ov5640_read_reg(0x380e, &temp);
-
- VTS = (VTS<<8) + ov5640_read_reg(0x380f, &temp);
-
- return VTS;
-}
-
-static int OV5640_set_VTS(int VTS)
-{
- /* write VTS to registers */
- int temp;
-
- temp = VTS & 0xff;
- ov5640_write_reg(0x380f, temp);
-
- temp = VTS>>8;
- ov5640_write_reg(0x380e, temp);
-
- return 0;
-}
-
-static int OV5640_get_shutter(void)
-{
- /* read shutter, in number of line period */
- int shutter;
- u8 temp;
-
- shutter = (ov5640_read_reg(0x03500, &temp) & 0x0f);
- shutter = (shutter<<8) + ov5640_read_reg(0x3501, &temp);
- shutter = (shutter<<4) + (ov5640_read_reg(0x3502, &temp)>>4);
-
- return shutter;
-}
-
-static int OV5640_set_shutter(int shutter)
-{
- /* write shutter, in number of line period */
- int temp;
-
- shutter = shutter & 0xffff;
-
- temp = shutter & 0x0f;
- temp = temp<<4;
- ov5640_write_reg(0x3502, temp);
-
- temp = shutter & 0xfff;
- temp = temp>>4;
- ov5640_write_reg(0x3501, temp);
-
- temp = shutter>>12;
- ov5640_write_reg(0x3500, temp);
-
- return 0;
-}
-
-static int OV5640_get_gain16(void)
-{
- /* read gain, 16 = 1x */
- int gain16;
- u8 temp;
-
- gain16 = ov5640_read_reg(0x350a, &temp) & 0x03;
- gain16 = (gain16<<8) + ov5640_read_reg(0x350b, &temp);
-
- return gain16;
-}
-
-static int OV5640_set_gain16(int gain16)
-{
- /* write gain, 16 = 1x */
- u8 temp;
- gain16 = gain16 & 0x3ff;
-
- temp = gain16 & 0xff;
- ov5640_write_reg(0x350b, temp);
-
- temp = gain16>>8;
- ov5640_write_reg(0x350a, temp);
-
- return 0;
-}
-
-static int OV5640_get_light_freq(void)
-{
- /* get banding filter value */
- int temp, temp1, light_freq = 0;
- u8 tmp;
-
- temp = ov5640_read_reg(0x3c01, &tmp);
-
- if (temp & 0x80) {
- /* manual */
- temp1 = ov5640_read_reg(0x3c00, &tmp);
- if (temp1 & 0x04) {
- /* 50Hz */
- light_freq = 50;
- } else {
- /* 60Hz */
- light_freq = 60;
- }
- } else {
- /* auto */
- temp1 = ov5640_read_reg(0x3c0c, &tmp);
- if (temp1 & 0x01) {
- /* 50Hz */
- light_freq = 50;
- } else {
- /* 60Hz */
- }
- }
- return light_freq;
-}
-
-static void OV5640_set_bandingfilter(void)
-{
- int prev_VTS;
- int band_step60, max_band60, band_step50, max_band50;
-
- /* read preview PCLK */
- prev_sysclk = OV5640_get_sysclk();
- /* read preview HTS */
- prev_HTS = OV5640_get_HTS();
-
- /* read preview VTS */
- prev_VTS = OV5640_get_VTS();
-
- /* calculate banding filter */
- /* 60Hz */
- band_step60 = prev_sysclk * 100/prev_HTS * 100/120;
- ov5640_write_reg(0x3a0a, (band_step60 >> 8));
- ov5640_write_reg(0x3a0b, (band_step60 & 0xff));
-
- max_band60 = (int)((prev_VTS-4)/band_step60);
- ov5640_write_reg(0x3a0d, max_band60);
-
- /* 50Hz */
- band_step50 = prev_sysclk * 100/prev_HTS;
- ov5640_write_reg(0x3a08, (band_step50 >> 8));
- ov5640_write_reg(0x3a09, (band_step50 & 0xff));
-
- max_band50 = (int)((prev_VTS-4)/band_step50);
- ov5640_write_reg(0x3a0e, max_band50);
-}
-
-static int OV5640_set_AE_target(int target)
-{
- /* stable in high */
- int fast_high, fast_low;
- AE_low = target * 23 / 25; /* 0.92 */
- AE_high = target * 27 / 25; /* 1.08 */
-
- fast_high = AE_high<<1;
- if (fast_high > 255)
- fast_high = 255;
-
- fast_low = AE_low >> 1;
-
- ov5640_write_reg(0x3a0f, AE_high);
- ov5640_write_reg(0x3a10, AE_low);
- ov5640_write_reg(0x3a1b, AE_high);
- ov5640_write_reg(0x3a1e, AE_low);
- ov5640_write_reg(0x3a11, fast_high);
- ov5640_write_reg(0x3a1f, fast_low);
-
- return 0;
-}
-
-static void OV5640_turn_on_AE_AG(int enable)
-{
- u8 ae_ag_ctrl;
-
- ov5640_read_reg(0x3503, &ae_ag_ctrl);
- if (enable) {
- /* turn on auto AE/AG */
- ae_ag_ctrl = ae_ag_ctrl & ~(0x03);
- } else {
- /* turn off AE/AG */
- ae_ag_ctrl = ae_ag_ctrl | 0x03;
- }
- ov5640_write_reg(0x3503, ae_ag_ctrl);
-}
-
-static bool binning_on(void)
-{
- u8 temp;
- ov5640_read_reg(0x3821, &temp);
- temp &= 0xfe;
- if (temp)
- return true;
- else
- return false;
-}
-
-static void ov5640_set_virtual_channel(int channel)
-{
- u8 channel_id;
-
- ov5640_read_reg(0x4814, &channel_id);
- channel_id &= ~(3 << 6);
- ov5640_write_reg(0x4814, channel_id | (channel << 6));
-}
-
-/* download ov5640 settings to sensor through i2c */
-static int ov5640_download_firmware(struct reg_value *pModeSetting, s32 ArySize)
-{
- register u32 Delay_ms = 0;
- register u16 RegAddr = 0;
- register u8 Mask = 0;
- register u8 Val = 0;
- u8 RegVal = 0;
- int i, retval = 0;
-
- for (i = 0; i < ArySize; ++i, ++pModeSetting) {
- Delay_ms = pModeSetting->u32Delay_ms;
- RegAddr = pModeSetting->u16RegAddr;
- Val = pModeSetting->u8Val;
- Mask = pModeSetting->u8Mask;
-
- if (Mask) {
- retval = ov5640_read_reg(RegAddr, &RegVal);
- if (retval < 0)
- goto err;
-
- RegVal &= ~(u8)Mask;
- Val &= Mask;
- Val |= RegVal;
- }
-
- retval = ov5640_write_reg(RegAddr, Val);
- if (retval < 0)
- goto err;
-
- if (Delay_ms)
- msleep(Delay_ms);
- }
-err:
- return retval;
-}
-
-/* sensor changes between scaling and subsampling
- * go through exposure calcualtion
- */
-static int ov5640_change_mode_exposure_calc(enum ov5640_frame_rate frame_rate,
- enum ov5640_mode mode)
-{
- struct reg_value *pModeSetting = NULL;
- s32 ArySize = 0;
- u8 average;
- int prev_shutter, prev_gain16;
- int cap_shutter, cap_gain16;
- int cap_sysclk, cap_HTS, cap_VTS;
- int light_freq, cap_bandfilt, cap_maxband;
- long cap_gain16_shutter;
- int retval = 0;
-
- /* check if the input mode and frame rate is valid */
- pModeSetting =
- ov5640_mode_info_data[frame_rate][mode].init_data_ptr;
- ArySize =
- ov5640_mode_info_data[frame_rate][mode].init_data_size;
-
- ov5640_data.pix.width =
- ov5640_mode_info_data[frame_rate][mode].width;
- ov5640_data.pix.height =
- ov5640_mode_info_data[frame_rate][mode].height;
-
- if (ov5640_data.pix.width == 0 || ov5640_data.pix.height == 0 ||
- pModeSetting == NULL || ArySize == 0)
- return -EINVAL;
-
- /* auto focus */
- /* OV5640_auto_focus();//if no af function, just skip it */
-
- /* turn off AE/AG */
- OV5640_turn_on_AE_AG(0);
-
- /* read preview shutter */
- prev_shutter = OV5640_get_shutter();
- if ((binning_on()) && (mode != ov5640_mode_720P_1280_720)
- && (mode != ov5640_mode_1080P_1920_1080))
- prev_shutter *= 2;
-
- /* read preview gain */
- prev_gain16 = OV5640_get_gain16();
-
- /* get average */
- ov5640_read_reg(0x56a1, &average);
-
- /* turn off night mode for capture */
- OV5640_set_night_mode();
-
- /* turn off overlay */
- /* ov5640_write_reg(0x3022, 0x06);//if no af function, just skip it */
-
- OV5640_stream_off();
-
- /* Write capture setting */
- retval = ov5640_download_firmware(pModeSetting, ArySize);
- if (retval < 0)
- goto err;
-
- /* read capture VTS */
- cap_VTS = OV5640_get_VTS();
- cap_HTS = OV5640_get_HTS();
- cap_sysclk = OV5640_get_sysclk();
-
- /* calculate capture banding filter */
- light_freq = OV5640_get_light_freq();
- if (light_freq == 60) {
- /* 60Hz */
- cap_bandfilt = cap_sysclk * 100 / cap_HTS * 100 / 120;
- } else {
- /* 50Hz */
- cap_bandfilt = cap_sysclk * 100 / cap_HTS;
- }
- cap_maxband = (int)((cap_VTS - 4)/cap_bandfilt);
-
- /* calculate capture shutter/gain16 */
- if (average > AE_low && average < AE_high) {
- /* in stable range */
- cap_gain16_shutter =
- prev_gain16 * prev_shutter * cap_sysclk/prev_sysclk
- * prev_HTS/cap_HTS * AE_Target / average;
- } else {
- cap_gain16_shutter =
- prev_gain16 * prev_shutter * cap_sysclk/prev_sysclk
- * prev_HTS/cap_HTS;
- }
-
- /* gain to shutter */
- if (cap_gain16_shutter < (cap_bandfilt * 16)) {
- /* shutter < 1/100 */
- cap_shutter = cap_gain16_shutter/16;
- if (cap_shutter < 1)
- cap_shutter = 1;
-
- cap_gain16 = cap_gain16_shutter/cap_shutter;
- if (cap_gain16 < 16)
- cap_gain16 = 16;
- } else {
- if (cap_gain16_shutter >
- (cap_bandfilt * cap_maxband * 16)) {
- /* exposure reach max */
- cap_shutter = cap_bandfilt * cap_maxband;
- cap_gain16 = cap_gain16_shutter / cap_shutter;
- } else {
- /* 1/100 < (cap_shutter = n/100) =< max */
- cap_shutter =
- ((int) (cap_gain16_shutter/16 / cap_bandfilt))
- *cap_bandfilt;
- cap_gain16 = cap_gain16_shutter / cap_shutter;
- }
- }
-
- /* write capture gain */
- OV5640_set_gain16(cap_gain16);
-
- /* write capture shutter */
- if (cap_shutter > (cap_VTS - 4)) {
- cap_VTS = cap_shutter + 4;
- OV5640_set_VTS(cap_VTS);
- }
- OV5640_set_shutter(cap_shutter);
-
-err:
- return retval;
-}
-
-/* if sensor changes inside scaling or subsampling
- * change mode directly
- * */
-static int ov5640_change_mode_direct(enum ov5640_frame_rate frame_rate,
- enum ov5640_mode mode)
-{
- struct reg_value *pModeSetting = NULL;
- s32 ArySize = 0;
- int retval = 0;
-
- /* check if the input mode and frame rate is valid */
- pModeSetting =
- ov5640_mode_info_data[frame_rate][mode].init_data_ptr;
- ArySize =
- ov5640_mode_info_data[frame_rate][mode].init_data_size;
-
- ov5640_data.pix.width =
- ov5640_mode_info_data[frame_rate][mode].width;
- ov5640_data.pix.height =
- ov5640_mode_info_data[frame_rate][mode].height;
-
- if (ov5640_data.pix.width == 0 || ov5640_data.pix.height == 0 ||
- pModeSetting == NULL || ArySize == 0)
- return -EINVAL;
-
- /* turn off AE/AG */
- OV5640_turn_on_AE_AG(0);
-
- OV5640_stream_off();
-
- /* Write capture setting */
- retval = ov5640_download_firmware(pModeSetting, ArySize);
- if (retval < 0)
- goto err;
-
- OV5640_turn_on_AE_AG(1);
-
-err:
- return retval;
-}
-
-static int ov5640_init_mode(enum ov5640_frame_rate frame_rate,
- enum ov5640_mode mode, enum ov5640_mode orig_mode)
-{
- struct reg_value *pModeSetting = NULL;
- s32 ArySize = 0;
- int retval = 0;
- u32 msec_wait4stable = 0;
- enum ov5640_downsize_mode dn_mode, orig_dn_mode;
-
- if ((mode > ov5640_mode_MAX || mode < ov5640_mode_MIN)
- && (mode != ov5640_mode_INIT)) {
- pr_err("Wrong ov5640 mode detected!\n");
- return -1;
- }
-
- dn_mode = ov5640_mode_info_data[frame_rate][mode].dn_mode;
- orig_dn_mode = ov5640_mode_info_data[frame_rate][orig_mode].dn_mode;
- if (mode == ov5640_mode_INIT) {
- pModeSetting = ov5640_init_setting_30fps_VGA;
- ArySize = ARRAY_SIZE(ov5640_init_setting_30fps_VGA);
-
- ov5640_data.pix.width = 640;
- ov5640_data.pix.height = 480;
- retval = ov5640_download_firmware(pModeSetting, ArySize);
- if (retval < 0)
- goto err;
-
- pModeSetting = ov5640_setting_30fps_VGA_640_480;
- ArySize = ARRAY_SIZE(ov5640_setting_30fps_VGA_640_480);
- retval = ov5640_download_firmware(pModeSetting, ArySize);
- } else if ((dn_mode == SUBSAMPLING && orig_dn_mode == SCALING) ||
- (dn_mode == SCALING && orig_dn_mode == SUBSAMPLING)) {
- /* change between subsampling and scaling
- * go through exposure calucation */
- retval = ov5640_change_mode_exposure_calc(frame_rate, mode);
- } else {
- /* change inside subsampling or scaling
- * download firmware directly */
- retval = ov5640_change_mode_direct(frame_rate, mode);
- }
-
- if (retval < 0)
- goto err;
-
- OV5640_set_AE_target(AE_Target);
- OV5640_get_light_freq();
- OV5640_set_bandingfilter();
- ov5640_set_virtual_channel(ov5640_data.csi);
-
- /* add delay to wait for sensor stable */
- if (mode == ov5640_mode_QSXGA_2592_1944) {
- /* dump the first two frames: 1/7.5*2
- * the frame rate of QSXGA is 7.5fps */
- msec_wait4stable = 267;
- } else {
- /* dump the first eighteen frames: 1/30*18 */
- msec_wait4stable = 600;
- }
- msleep(msec_wait4stable);
-
-err:
- return retval;
-}
-
-/*!
- * ov5640_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 ov5640_s_power(struct v4l2_subdev *sd, int on)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov5640 *sensor = to_ov5640(client);
-
- if (on && !sensor->on) {
- 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);
- }
-
- sensor->on = on;
-
- return 0;
-}
-
-/*!
- * ov5640_g_parm - V4L2 sensor interface handler for VIDIOC_G_PARM ioctl
- * @s: pointer to standard V4L2 sub device structure
- * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure
- *
- * Returns the sensor's video CAPTURE parameters.
- */
-static int ov5640_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov5640 *sensor = to_ov5640(client);
- 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;
-}
-
-/*!
- * ov5460_s_parm - V4L2 sensor interface handler for VIDIOC_S_PARM ioctl
- * @s: pointer to standard V4L2 sub 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 ov5640_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov5640 *sensor = to_ov5640(client);
- struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe;
- u32 tgt_fps; /* target frames per secound */
- enum ov5640_frame_rate frame_rate;
- enum ov5640_mode orig_mode;
- int ret = 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 = ov5640_15_fps;
- else if (tgt_fps == 30)
- frame_rate = ov5640_30_fps;
- else {
- pr_err(" The camera frame rate is not supported!\n");
- return -EINVAL;
- }
-
- orig_mode = sensor->streamcap.capturemode;
- ret = ov5640_init_mode(frame_rate,
- (u32)a->parm.capture.capturemode, orig_mode);
- if (ret < 0)
- return ret;
-
- sensor->streamcap.timeperframe = *timeperframe;
- sensor->streamcap.capturemode =
- (u32)a->parm.capture.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;
-}
-
-static int ov5640_set_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_format *format)
-{
- struct v4l2_mbus_framefmt *mf = &format->format;
- const struct ov5640_datafmt *fmt = ov5640_find_datafmt(mf->code);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov5640 *sensor = to_ov5640(client);
-
- if (!fmt) {
- mf->code = ov5640_colour_fmts[0].code;
- mf->colorspace = ov5640_colour_fmts[0].colorspace;
- }
-
- mf->field = V4L2_FIELD_NONE;
-
- if (format->which == V4L2_SUBDEV_FORMAT_TRY)
- return 0;
-
- sensor->fmt = fmt;
-
- return 0;
-}
-
-
-static int ov5640_get_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_format *format)
-{
- struct v4l2_mbus_framefmt *mf = &format->format;
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov5640 *sensor = to_ov5640(client);
- const struct ov5640_datafmt *fmt = sensor->fmt;
-
- if (format->pad)
- return -EINVAL;
-
- mf->code = fmt->code;
- mf->colorspace = fmt->colorspace;
- mf->field = V4L2_FIELD_NONE;
-
- return 0;
-}
-
-static int ov5640_enum_mbus_code(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_mbus_code_enum *code)
-{
- if (code->pad || code->index >= ARRAY_SIZE(ov5640_colour_fmts))
- return -EINVAL;
-
- code->code = ov5640_colour_fmts[code->index].code;
- return 0;
-}
-
-/*!
- * ov5640_enum_framesizes - V4L2 sensor interface handler for
- * VIDIOC_ENUM_FRAMESIZES ioctl
- * @s: pointer to standard V4L2 device structure
- * @fsize: standard V4L2 VIDIOC_ENUM_FRAMESIZES ioctl structure
- *
- * Return 0 if successful, otherwise -EINVAL.
- */
-static int ov5640_enum_framesizes(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_frame_size_enum *fse)
-{
- if (fse->index > ov5640_mode_MAX)
- return -EINVAL;
-
- fse->max_width =
- max(ov5640_mode_info_data[0][fse->index].width,
- ov5640_mode_info_data[1][fse->index].width);
- fse->min_width = fse->max_width;
- fse->max_height =
- max(ov5640_mode_info_data[0][fse->index].height,
- ov5640_mode_info_data[1][fse->index].height);
- fse->min_height = fse->max_height;
- return 0;
-}
-
-/*!
- * ov5640_enum_frameintervals - V4L2 sensor interface handler for
- * VIDIOC_ENUM_FRAMEINTERVALS ioctl
- * @s: pointer to standard V4L2 device structure
- * @fival: standard V4L2 VIDIOC_ENUM_FRAMEINTERVALS ioctl structure
- *
- * Return 0 if successful, otherwise -EINVAL.
- */
-static int ov5640_enum_frameintervals(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_frame_interval_enum *fie)
-{
- int i, j, count = 0;
-
- if (fie->index < 0 || fie->index > ov5640_mode_MAX)
- return -EINVAL;
-
- if (fie->width == 0 || fie->height == 0 ||
- fie->code == 0) {
- pr_warning("Please assign pixel format, width and height.\n");
- return -EINVAL;
- }
-
- fie->interval.numerator = 1;
-
- count = 0;
- for (i = 0; i < ARRAY_SIZE(ov5640_mode_info_data); i++) {
- for (j = 0; j < (ov5640_mode_MAX + 1); j++) {
- if (fie->width == ov5640_mode_info_data[i][j].width
- && fie->height == ov5640_mode_info_data[i][j].height
- && ov5640_mode_info_data[i][j].init_data_ptr != NULL) {
- count++;
- }
- if (fie->index == (count - 1)) {
- fie->interval.denominator =
- ov5640_framerates[i];
- return 0;
- }
- }
- }
-
- return -EINVAL;
-}
-
-/*!
- * dev_init - V4L2 sensor init
- * @s: pointer to standard V4L2 device structure
- *
- */
-static int init_device(void)
-{
- u32 tgt_xclk; /* target xclk */
- u32 tgt_fps; /* target frames per secound */
- enum ov5640_frame_rate frame_rate;
- int ret;
-
- ov5640_data.on = true;
-
- /* mclk */
- tgt_xclk = ov5640_data.mclk;
- tgt_xclk = min(tgt_xclk, (u32)OV5640_XCLK_MAX);
- tgt_xclk = max(tgt_xclk, (u32)OV5640_XCLK_MIN);
- ov5640_data.mclk = tgt_xclk;
-
- pr_debug(" Setting mclk to %d MHz\n", tgt_xclk / 1000000);
-
- /* Default camera frame rate is set in probe */
- tgt_fps = ov5640_data.streamcap.timeperframe.denominator /
- ov5640_data.streamcap.timeperframe.numerator;
-
- if (tgt_fps == 15)
- frame_rate = ov5640_15_fps;
- else if (tgt_fps == 30)
- frame_rate = ov5640_30_fps;
- else
- return -EINVAL; /* Only support 15fps or 30fps now. */
-
- ret = ov5640_init_mode(frame_rate, ov5640_mode_INIT, ov5640_mode_INIT);
-
- return ret;
-}
-
-static int ov5640_s_stream(struct v4l2_subdev *sd, int enable)
-{
- if (enable)
- OV5640_stream_on();
- else
- OV5640_stream_off();
- return 0;
-}
-
-static struct v4l2_subdev_video_ops ov5640_subdev_video_ops = {
- .g_parm = ov5640_g_parm,
- .s_parm = ov5640_s_parm,
- .s_stream = ov5640_s_stream,
-};
-
-static const struct v4l2_subdev_pad_ops ov5640_subdev_pad_ops = {
- .enum_frame_size = ov5640_enum_framesizes,
- .enum_frame_interval = ov5640_enum_frameintervals,
- .enum_mbus_code = ov5640_enum_mbus_code,
- .set_fmt = ov5640_set_fmt,
- .get_fmt = ov5640_get_fmt,
-};
-
-static struct v4l2_subdev_core_ops ov5640_subdev_core_ops = {
- .s_power = ov5640_s_power,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .g_register = ov5640_get_register,
- .s_register = ov5640_set_register,
-#endif
-};
-
-static struct v4l2_subdev_ops ov5640_subdev_ops = {
- .core = &ov5640_subdev_core_ops,
- .video = &ov5640_subdev_video_ops,
- .pad = &ov5640_subdev_pad_ops,
-};
-
-
-/*!
- * ov5640 I2C probe function
- *
- * @param adapter struct i2c_adapter *
- * @return Error code indicating success or failure
- */
-static int ov5640_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- struct pinctrl *pinctrl;
- struct device *dev = &client->dev;
- int retval;
- u8 chip_id_high, chip_id_low;
-
- /* ov5640 pinctrl */
- pinctrl = devm_pinctrl_get_select_default(dev);
- if (IS_ERR(pinctrl))
- dev_warn(dev, "no pin available\n");
-
- /* request power down pin */
- pwn_gpio = of_get_named_gpio(dev->of_node, "pwn-gpios", 0);
- if (!gpio_is_valid(pwn_gpio))
- dev_warn(dev, "no sensor pwdn pin available");
- else {
- retval = devm_gpio_request_one(dev, pwn_gpio, GPIOF_OUT_INIT_HIGH,
- "ov5640_mipi_pwdn");
- if (retval < 0) {
- dev_warn(dev, "Failed to set power pin\n");
- dev_warn(dev, "retval=%d\n", retval);
- return retval;
- }
- }
-
- /* request reset pin */
- rst_gpio = of_get_named_gpio(dev->of_node, "rst-gpios", 0);
- if (!gpio_is_valid(rst_gpio))
- dev_warn(dev, "no sensor reset pin available");
- else {
- retval = devm_gpio_request_one(dev, rst_gpio, GPIOF_OUT_INIT_HIGH,
- "ov5640_mipi_reset");
- if (retval < 0) {
- dev_warn(dev, "Failed to set reset pin\n");
- return retval;
- }
- }
-
- /* Set initial values for the sensor struct. */
- memset(&ov5640_data, 0, sizeof(ov5640_data));
- ov5640_data.sensor_clk = devm_clk_get(dev, "csi_mclk");
- if (IS_ERR(ov5640_data.sensor_clk)) {
- /* assuming clock enabled by default */
- ov5640_data.sensor_clk = NULL;
- dev_err(dev, "clock-frequency missing or invalid\n");
- return PTR_ERR(ov5640_data.sensor_clk);
- }
-
- retval = of_property_read_u32(dev->of_node, "mclk",
- &(ov5640_data.mclk));
- if (retval) {
- dev_err(dev, "mclk missing or invalid\n");
- return retval;
- }
-
- retval = of_property_read_u32(dev->of_node, "mclk_source",
- (u32 *) &(ov5640_data.mclk_source));
- if (retval) {
- dev_err(dev, "mclk_source missing or invalid\n");
- return retval;
- }
-
- retval = of_property_read_u32(dev->of_node, "csi_id",
- &(ov5640_data.csi));
- if (retval) {
- dev_err(dev, "csi id missing or invalid\n");
- return retval;
- }
-
- clk_prepare_enable(ov5640_data.sensor_clk);
-
- ov5640_data.io_init = ov5640_reset;
- ov5640_data.i2c_client = client;
- ov5640_data.pix.pixelformat = V4L2_PIX_FMT_YUYV;
- ov5640_data.pix.width = 640;
- ov5640_data.pix.height = 480;
- ov5640_data.streamcap.capability = V4L2_MODE_HIGHQUALITY |
- V4L2_CAP_TIMEPERFRAME;
- ov5640_data.streamcap.capturemode = 0;
- ov5640_data.streamcap.timeperframe.denominator = DEFAULT_FPS;
- ov5640_data.streamcap.timeperframe.numerator = 1;
-
- ov5640_regulator_enable(&client->dev);
-
- ov5640_reset();
-
- ov5640_power_down(0);
-
- retval = ov5640_read_reg(OV5640_CHIP_ID_HIGH_BYTE, &chip_id_high);
- if (retval < 0 || chip_id_high != 0x56) {
- pr_warning("camera ov5640_mipi is not found\n");
- clk_disable_unprepare(ov5640_data.sensor_clk);
- return -ENODEV;
- }
- retval = ov5640_read_reg(OV5640_CHIP_ID_LOW_BYTE, &chip_id_low);
- if (retval < 0 || chip_id_low != 0x40) {
- pr_warning("camera ov5640_mipi is not found\n");
- clk_disable_unprepare(ov5640_data.sensor_clk);
- return -ENODEV;
- }
-
- retval = init_device();
- if (retval < 0) {
- clk_disable_unprepare(ov5640_data.sensor_clk);
- pr_warning("camera ov5640 init failed\n");
- ov5640_power_down(1);
- return retval;
- }
-
- v4l2_i2c_subdev_init(&ov5640_data.subdev, client, &ov5640_subdev_ops);
-
- ov5640_data.subdev.grp_id = 678;
- retval = v4l2_async_register_subdev(&ov5640_data.subdev);
- if (retval < 0)
- dev_err(&client->dev,
- "%s--Async register failed, ret=%d\n", __func__, retval);
-
- OV5640_stream_off();
- pr_info("camera ov5640_mipi is found\n");
- return retval;
-}
-
-/*!
- * ov5640 I2C detach function
- *
- * @param client struct i2c_client *
- * @return Error code indicating success or failure
- */
-static int ov5640_remove(struct i2c_client *client)
-{
- struct v4l2_subdev *sd = i2c_get_clientdata(client);
-
- v4l2_async_unregister_subdev(sd);
-
- clk_disable_unprepare(ov5640_data.sensor_clk);
-
- ov5640_power_down(1);
-
- if (gpo_regulator)
- regulator_disable(gpo_regulator);
-
- if (analog_regulator)
- regulator_disable(analog_regulator);
-
- if (core_regulator)
- regulator_disable(core_regulator);
-
- if (io_regulator)
- regulator_disable(io_regulator);
-
- return 0;
-}
-
-module_i2c_driver(ov5640_i2c_driver);
-
-MODULE_AUTHOR("Freescale Semiconductor, Inc.");
-MODULE_DESCRIPTION("OV5640 MIPI Camera Driver");
-MODULE_LICENSE("GPL");
-MODULE_VERSION("1.0");
-MODULE_ALIAS("CSI");
+++ /dev/null
-/*
- * Copyright (C) 2011-2015 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/clk.h>
-#include <linux/of_device.h>
-#include <linux/i2c.h>
-#include <linux/of_gpio.h>
-#include <linux/pinctrl/consumer.h>
-#include <linux/regulator/consumer.h>
-#include <linux/v4l2-mediabus.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ctrls.h>
-
-#define OV5647_VOLTAGE_ANALOG 2800000
-#define OV5647_VOLTAGE_DIGITAL_CORE 1500000
-#define OV5647_VOLTAGE_DIGITAL_IO 1800000
-
-#define MIN_FPS 15
-#define MAX_FPS 30
-#define DEFAULT_FPS 30
-
-#define OV5647_XCLK_MIN 6000000
-#define OV5647_XCLK_MAX 24000000
-
-#define OV5647_CHIP_ID_HIGH_BYTE 0x300A
-#define OV5647_CHIP_ID_LOW_BYTE 0x300B
-
-enum ov5647_mode {
- ov5647_mode_MIN = 0,
- ov5647_mode_VGA_640_480 = 0,
- ov5647_mode_720P_1280_720 = 1,
- ov5647_mode_1080P_1920_1080 = 2,
- ov5647_mode_QSXGA_2592_1944 = 3,
- ov5647_mode_MAX = 3,
- ov5647_mode_INIT = 0xff, /*only for sensor init*/
-};
-
-enum ov5647_frame_rate {
- ov5647_15_fps,
- ov5647_30_fps
-};
-
-static int ov5647_framerates[] = {
- [ov5647_15_fps] = 15,
- [ov5647_30_fps] = 30,
-};
-
-struct ov5647_datafmt {
- u32 code;
- enum v4l2_colorspace colorspace;
-};
-
-/* image size under 1280 * 960 are SUBSAMPLING
- * image size upper 1280 * 960 are SCALING
- */
-enum ov5647_downsize_mode {
- SUBSAMPLING,
- SCALING,
-};
-
-struct reg_value {
- u16 u16RegAddr;
- u8 u8Val;
- u8 u8Mask;
- u32 u32Delay_ms;
-};
-
-struct ov5647_mode_info {
- enum ov5647_mode mode;
- enum ov5647_downsize_mode dn_mode;
- u32 width;
- u32 height;
- struct reg_value *init_data_ptr;
- u32 init_data_size;
-};
-
-struct otp_struct {
- int customer_id;
- int module_integrator_id;
- int lens_id;
- int rg_ratio;
- int bg_ratio;
- int user_data[3];
- int light_rg;
- int light_bg;
-};
-
-struct ov5647 {
- struct v4l2_subdev subdev;
- struct i2c_client *i2c_client;
- struct v4l2_pix_format pix;
- const struct ov5647_datafmt *fmt;
- 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;
- u8 mclk_source;
- struct clk *sensor_clk;
- int csi;
-
- void (*io_init)(void);
-};
-/*!
- * Maintains the information on the current state of the sesor.
- */
-static struct ov5647 ov5647_data;
-static int pwn_gpio, rst_gpio;
-static int prev_sysclk, prev_HTS;
-static int AE_low, AE_high, AE_Target = 52;
-
-/* R/G and B/G of typical camera module is defined here,
- * the typical camera module is selected by CameraAnalyzer. */
-static int RG_Ratio_Typical = 0x70;
-static int BG_Ratio_Typical = 0x70;
-
-
-static struct reg_value ov5647_init_setting[] = {
-
- {0x0100, 0x00, 0, 0}, {0x3035, 0x11, 0, 0},
- {0x3036, 0x69, 0, 0}, {0x303c, 0x11, 0, 0}, {0x3821, 0x07, 0, 0},
- {0x3820, 0x41, 0, 0}, {0x370c, 0x0f, 0, 0}, {0x3612, 0x59, 0, 0},
- {0x3618, 0x00, 0, 0}, {0x5000, 0x06, 0, 0}, {0x5002, 0x40, 0, 0},
- {0x5003, 0x08, 0, 0}, {0x5a00, 0x08, 0, 0}, {0x3000, 0xff, 0, 0},
- {0x3001, 0xff, 0, 0}, {0x3002, 0xff, 0, 0}, {0x301d, 0xf0, 0, 0},
- {0x3a18, 0x00, 0, 0}, {0x3a19, 0xf8, 0, 0}, {0x3c01, 0x80, 0, 0},
- {0x3b07, 0x0c, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
- {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3814, 0x31, 0, 0},
- {0x3815, 0x31, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3709, 0x52, 0, 0},
- {0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x03, 0, 0},
- {0x380b, 0xc0, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x18, 0, 0},
- {0x3802, 0x00, 0, 0}, {0x3803, 0x0e, 0, 0}, {0x3804, 0x0a, 0, 0},
- {0x3805, 0x27, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x95, 0, 0},
- {0x3630, 0x2e, 0, 0}, {0x3632, 0xe2, 0, 0}, {0x3633, 0x23, 0, 0},
- {0x3634, 0x44, 0, 0}, {0x3620, 0x64, 0, 0}, {0x3621, 0xe0, 0, 0},
- {0x3600, 0x37, 0, 0}, {0x3704, 0xa0, 0, 0}, {0x3703, 0x5a, 0, 0},
- {0x3715, 0x78, 0, 0}, {0x3717, 0x01, 0, 0}, {0x3731, 0x02, 0, 0},
- {0x370b, 0x60, 0, 0}, {0x3705, 0x1a, 0, 0}, {0x3f05, 0x02, 0, 0},
- {0x3f06, 0x10, 0, 0}, {0x3f01, 0x0a, 0, 0}, {0x3a08, 0x01, 0, 0},
- {0x3a09, 0x27, 0, 0}, {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0},
- {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, {0x3a0f, 0x58, 0, 0},
- {0x3a10, 0x50, 0, 0}, {0x3a1b, 0x58, 0, 0}, {0x3a1e, 0x50, 0, 0},
- {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x28, 0, 0}, {0x4001, 0x02, 0, 0},
- {0x4004, 0x02, 0, 0}, {0x4000, 0x09, 0, 0}, {0x4050, 0x6e, 0, 0},
- {0x4051, 0x8f, 0, 0}, {0x4837, 0x17, 0, 0}, {0x3503, 0x03, 0, 0},
- {0x3501, 0x44, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350a, 0x00, 0, 0},
- {0x350b, 0x7f, 0, 0}, {0x5001, 0x01, 0, 0}, {0x5002, 0x41, 0, 0},
- {0x5180, 0x08, 0, 0}, {0x5186, 0x04, 0, 0}, {0x5187, 0x00, 0, 0},
- {0x5188, 0x04, 0, 0}, {0x5189, 0x00, 0, 0}, {0x518a, 0x04, 0, 0},
- {0x518b, 0x00, 0, 0}, {0x5000, 0x86, 0, 0}, {0x5800, 0x11, 0, 0},
- {0x5801, 0x0a, 0, 0}, {0x5802, 0x09, 0, 0}, {0x5803, 0x09, 0, 0},
- {0x5804, 0x0a, 0, 0}, {0x5805, 0x0f, 0, 0}, {0x5806, 0x07, 0, 0},
- {0x5807, 0x05, 0, 0}, {0x5808, 0x03, 0, 0}, {0x5809, 0x03, 0, 0},
- {0x580a, 0x05, 0, 0}, {0x580b, 0x07, 0, 0}, {0x580c, 0x05, 0, 0},
- {0x580d, 0x02, 0, 0}, {0x580e, 0x00, 0, 0}, {0x580f, 0x00, 0, 0},
- {0x5810, 0x02, 0, 0}, {0x5811, 0x05, 0, 0}, {0x5812, 0x05, 0, 0},
- {0x5813, 0x02, 0, 0}, {0x5814, 0x00, 0, 0}, {0x5815, 0x00, 0, 0},
- {0x5816, 0x01, 0, 0}, {0x5817, 0x05, 0, 0}, {0x5818, 0x08, 0, 0},
- {0x5819, 0x05, 0, 0}, {0x581a, 0x03, 0, 0}, {0x581b, 0x03, 0, 0},
- {0x581c, 0x04, 0, 0}, {0x581d, 0x07, 0, 0}, {0x581e, 0x10, 0, 0},
- {0x581f, 0x0b, 0, 0}, {0x5820, 0x09, 0, 0}, {0x5821, 0x09, 0, 0},
- {0x5822, 0x09, 0, 0}, {0x5823, 0x0e, 0, 0}, {0x5824, 0x28, 0, 0},
- {0x5825, 0x1a, 0, 0}, {0x5826, 0x1a, 0, 0}, {0x5827, 0x1a, 0, 0},
- {0x5828, 0x46, 0, 0}, {0x5829, 0x2a, 0, 0}, {0x582a, 0x26, 0, 0},
- {0x582b, 0x44, 0, 0}, {0x582c, 0x26, 0, 0}, {0x582d, 0x2a, 0, 0},
- {0x582e, 0x28, 0, 0}, {0x582f, 0x42, 0, 0}, {0x5830, 0x40, 0, 0},
- {0x5831, 0x42, 0, 0}, {0x5832, 0x28, 0, 0}, {0x5833, 0x0a, 0, 0},
- {0x5834, 0x16, 0, 0}, {0x5835, 0x44, 0, 0}, {0x5836, 0x26, 0, 0},
- {0x5837, 0x2a, 0, 0}, {0x5838, 0x28, 0, 0}, {0x5839, 0x0a, 0, 0},
- {0x583a, 0x0a, 0, 0}, {0x583b, 0x0a, 0, 0}, {0x583c, 0x26, 0, 0},
- {0x583d, 0xbe, 0, 0}, {0x0100, 0x01, 0, 0}, {0x3000, 0x00, 0, 0},
- {0x3001, 0x00, 0, 0}, {0x3002, 0x00, 0, 0}, {0x3017, 0xe0, 0, 0},
- {0x301c, 0xfc, 0, 0}, {0x3636, 0x06, 0, 0}, {0x3016, 0x08, 0, 0},
- {0x3827, 0xec, 0, 0}, {0x3018, 0x44, 0, 0}, {0x3035, 0x21, 0, 0},
- {0x3106, 0xf5, 0, 0}, {0x3034, 0x18, 0, 0}, {0x301c, 0xf8, 0, 0},
-};
-
-static struct reg_value ov5647_setting_60fps_VGA_640_480[] = {
- {0x0100, 0x00, 0, 0}, {0x3035, 0x11, 0, 0},
- {0x3036, 0x46, 0, 0}, {0x303c, 0x11, 0, 0}, {0x3821, 0x07, 0, 0},
- {0x3820, 0x41, 0, 0}, {0x370c, 0x0f, 0, 0}, {0x3612, 0x59, 0, 0},
- {0x3618, 0x00, 0, 0}, {0x5000, 0x06, 0, 0}, {0x5002, 0x40, 0, 0},
- {0x5003, 0x08, 0, 0}, {0x5a00, 0x08, 0, 0}, {0x3000, 0xff, 0, 0},
- {0x3001, 0xff, 0, 0}, {0x3002, 0xff, 0, 0}, {0x301d, 0xf0, 0, 0},
- {0x3a18, 0x00, 0, 0}, {0x3a19, 0xf8, 0, 0}, {0x3c01, 0x80, 0, 0},
- {0x3b07, 0x0c, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x3c, 0, 0},
- {0x380e, 0x01, 0, 0}, {0x380f, 0xf8, 0, 0}, {0x3814, 0x71, 0, 0},
- {0x3815, 0x71, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3709, 0x52, 0, 0},
- {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0},
- {0x380b, 0xe0, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x10, 0, 0},
- {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0},
- {0x3805, 0x2f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0},
- {0x3630, 0x2e, 0, 0}, {0x3632, 0xe2, 0, 0}, {0x3633, 0x23, 0, 0},
- {0x3634, 0x44, 0, 0}, {0x3620, 0x64, 0, 0}, {0x3621, 0xe0, 0, 0},
- {0x3600, 0x37, 0, 0}, {0x3704, 0xa0, 0, 0}, {0x3703, 0x5a, 0, 0},
- {0x3715, 0x78, 0, 0}, {0x3717, 0x01, 0, 0}, {0x3731, 0x02, 0, 0},
- {0x370b, 0x60, 0, 0}, {0x3705, 0x1a, 0, 0}, {0x3f05, 0x02, 0, 0},
- {0x3f06, 0x10, 0, 0}, {0x3f01, 0x0a, 0, 0}, {0x3a08, 0x01, 0, 0},
- {0x3a09, 0x2e, 0, 0}, {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xfb, 0, 0},
- {0x3a0d, 0x02, 0, 0}, {0x3a0e, 0x01, 0, 0}, {0x3a0f, 0x58, 0, 0},
- {0x3a10, 0x50, 0, 0}, {0x3a1b, 0x58, 0, 0}, {0x3a1e, 0x50, 0, 0},
- {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x28, 0, 0}, {0x4001, 0x02, 0, 0},
- {0x4004, 0x02, 0, 0}, {0x4000, 0x09, 0, 0}, {0x4050, 0x6e, 0, 0},
- {0x4051, 0x8f, 0, 0}, {0x0100, 0x01, 0, 0}, {0x3000, 0x00, 0, 0},
- {0x3001, 0x00, 0, 0}, {0x3002, 0x00, 0, 0}, {0x3017, 0xe0, 0, 0},
- {0x301c, 0xfc, 0, 0}, {0x3636, 0x06, 0, 0}, {0x3016, 0x08, 0, 0},
- {0x3827, 0xec, 0, 0}, {0x3018, 0x44, 0, 0}, {0x3035, 0x21, 0, 0},
- {0x3106, 0xf5, 0, 0}, {0x3034, 0x18, 0, 0}, {0x301c, 0xf8, 0, 0},
-};
-
-static struct reg_value ov5647_setting_30fps_720P_1280_720[] = {
- {0x0100, 0x00, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0},
- {0x3612, 0x59, 0, 0}, {0x3618, 0x00, 0, 0}, {0x380c, 0x09, 0, 0},
- {0x380d, 0xe8, 0, 0}, {0x380e, 0x04, 0, 0}, {0x380f, 0x50, 0, 0},
- {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3709, 0x52, 0, 0},
- {0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x02, 0, 0},
- {0x380b, 0xd0, 0, 0}, {0x3801, 0x18, 0, 0}, {0x3802, 0x00, 0, 0},
- {0x3803, 0xf8, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x27, 0, 0},
- {0x3806, 0x06, 0, 0}, {0x3807, 0xa7, 0, 0}, {0x3a09, 0xbe, 0, 0},
- {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x74, 0, 0}, {0x3a0d, 0x02, 0, 0},
- {0x3a0e, 0x01, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4005, 0x18, 0, 0},
- {0x0100, 0x01, 0, 0},
-};
-
-static struct reg_value ov5647_setting_30fps_1080P_1920_1080[] = {
- {0x0100, 0x00, 0, 0}, {0x3820, 0x00, 0, 0}, {0x3821, 0x06, 0, 0},
- {0x3612, 0x5b, 0, 0}, {0x3618, 0x04, 0, 0}, {0x380c, 0x09, 0, 0},
- {0x380d, 0xe8, 0, 0}, {0x380e, 0x04, 0, 0}, {0x380f, 0x50, 0, 0},
- {0x3814, 0x11, 0, 0}, {0x3815, 0x11, 0, 0}, {0x3709, 0x12, 0, 0},
- {0x3808, 0x07, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x04, 0, 0},
- {0x380b, 0x38, 0, 0}, {0x3801, 0x5c, 0, 0}, {0x3802, 0x01, 0, 0},
- {0x3803, 0xb2, 0, 0}, {0x3804, 0x08, 0, 0}, {0x3805, 0xe3, 0, 0},
- {0x3806, 0x05, 0, 0}, {0x3807, 0xf1, 0, 0}, {0x3a09, 0x4b, 0, 0},
- {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x13, 0, 0}, {0x3a0d, 0x04, 0, 0},
- {0x3a0e, 0x03, 0, 0}, {0x4004, 0x04, 0, 0}, {0x4005, 0x18, 0, 0},
- {0x0100, 0x01, 0, 0},
-};
-
-static struct reg_value ov5647_setting_15fps_QSXGA_2592_1944[] = {
- {0x0100, 0x00, 0, 0}, {0x3820, 0x00, 0, 0}, {0x3821, 0x06, 0, 0},
- {0x3612, 0x5b, 0, 0}, {0x3618, 0x04, 0, 0}, {0x380c, 0x0b, 0, 0},
- {0x380d, 0x10, 0, 0}, {0x380e, 0x07, 0, 0}, {0x380f, 0xb8, 0, 0},
- {0x3814, 0x11, 0, 0}, {0x3815, 0x11, 0, 0}, {0x3709, 0x12, 0, 0},
- {0x3808, 0x0a, 0, 0}, {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0},
- {0x380b, 0x98, 0, 0}, {0x3801, 0x0c, 0, 0}, {0x3802, 0x00, 0, 0},
- {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x33, 0, 0},
- {0x3806, 0x07, 0, 0}, {0x3807, 0xa3, 0, 0}, {0x3a09, 0x28, 0, 0},
- {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0d, 0x08, 0, 0},
- {0x3a0e, 0x06, 0, 0}, {0x4004, 0x04, 0, 0}, {0x4005, 0x1a, 0, 0},
- {0x0100, 0x01, 0, 0},
-};
-
-static struct ov5647_mode_info ov5647_mode_info_data[2][ov5647_mode_MAX + 1] = {
- {
- {ov5647_mode_VGA_640_480, -1, 0, 0, NULL, 0},
- {ov5647_mode_720P_1280_720, -1, 0, 0, NULL, 0},
- {ov5647_mode_1080P_1920_1080, -1, 0, 0, NULL, 0},
- {ov5647_mode_QSXGA_2592_1944, SCALING, 2592, 1944,
- ov5647_setting_15fps_QSXGA_2592_1944,
- ARRAY_SIZE(ov5647_setting_15fps_QSXGA_2592_1944)},
- },
- {
- /* Actually VGA working in 60fps mode */
- {ov5647_mode_VGA_640_480, SUBSAMPLING, 640, 480,
- ov5647_setting_60fps_VGA_640_480,
- ARRAY_SIZE(ov5647_setting_60fps_VGA_640_480)},
- {ov5647_mode_720P_1280_720, SUBSAMPLING, 1280, 720,
- ov5647_setting_30fps_720P_1280_720,
- ARRAY_SIZE(ov5647_setting_30fps_720P_1280_720)},
- {ov5647_mode_1080P_1920_1080, SCALING, 1920, 1080,
- ov5647_setting_30fps_1080P_1920_1080,
- ARRAY_SIZE(ov5647_setting_30fps_1080P_1920_1080)},
- {ov5647_mode_QSXGA_2592_1944, -1, 0, 0, NULL, 0},
- },
-};
-
-static struct regulator *io_regulator;
-static struct regulator *core_regulator;
-static struct regulator *analog_regulator;
-static struct regulator *gpo_regulator;
-
-static int ov5647_probe(struct i2c_client *adapter,
- const struct i2c_device_id *device_id);
-static int ov5647_remove(struct i2c_client *client);
-
-static s32 ov5647_read_reg(u16 reg, u8 *val);
-static s32 ov5647_write_reg(u16 reg, u8 val);
-
-static const struct i2c_device_id ov5647_id[] = {
- {"ov5647_mipi", 0},
- {},
-};
-
-MODULE_DEVICE_TABLE(i2c, ov5647_id);
-
-static struct i2c_driver ov5647_i2c_driver = {
- .driver = {
- .owner = THIS_MODULE,
- .name = "ov5647_mipi",
- },
- .probe = ov5647_probe,
- .remove = ov5647_remove,
- .id_table = ov5647_id,
-};
-
-static const struct ov5647_datafmt ov5647_colour_fmts[] = {
- {MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_JPEG},
-};
-
-static struct ov5647 *to_ov5647(const struct i2c_client *client)
-{
- return container_of(i2c_get_clientdata(client), struct ov5647, subdev);
-}
-
-/* Find a data format by a pixel code in an array */
-static const struct ov5647_datafmt
- *ov5647_find_datafmt(u32 code)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(ov5647_colour_fmts); i++)
- if (ov5647_colour_fmts[i].code == code)
- return ov5647_colour_fmts + i;
-
- return NULL;
-}
-
-static inline void ov5647_power_down(int enable)
-{
- if (pwn_gpio < 0)
- return;
-
- /* 19x19 pwdn pin invert by mipi daughter card */
- if (!enable)
- gpio_set_value_cansleep(pwn_gpio, 1);
- else
- gpio_set_value_cansleep(pwn_gpio, 0);
-
- msleep(2);
-}
-
-static void ov5647_reset(void)
-{
- if (rst_gpio < 0 || pwn_gpio < 0)
- return;
-
- /* camera reset */
- gpio_set_value_cansleep(rst_gpio, 1);
-
- /* camera power dowmn */
- gpio_set_value_cansleep(pwn_gpio, 1);
- msleep(5);
-
- gpio_set_value_cansleep(pwn_gpio, 0);
- msleep(5);
-
- gpio_set_value_cansleep(rst_gpio, 0);
- msleep(1);
-
- gpio_set_value_cansleep(rst_gpio, 1);
- msleep(5);
-
- gpio_set_value_cansleep(pwn_gpio, 1);
-}
-
-static int ov5647_regulator_enable(struct device *dev)
-{
- int ret = 0;
-
- io_regulator = devm_regulator_get(dev, "DOVDD");
- if (!IS_ERR(io_regulator)) {
- regulator_set_voltage(io_regulator,
- OV5647_VOLTAGE_DIGITAL_IO,
- OV5647_VOLTAGE_DIGITAL_IO);
- ret = regulator_enable(io_regulator);
- if (ret) {
- pr_err("%s:io set voltage error\n", __func__);
- return ret;
- } else {
- dev_dbg(dev,
- "%s:io set voltage ok\n", __func__);
- }
- } else {
- pr_err("%s: cannot get io voltage error\n", __func__);
- io_regulator = NULL;
- }
-
- core_regulator = devm_regulator_get(dev, "DVDD");
- if (!IS_ERR(core_regulator)) {
- regulator_set_voltage(core_regulator,
- OV5647_VOLTAGE_DIGITAL_CORE,
- OV5647_VOLTAGE_DIGITAL_CORE);
- ret = regulator_enable(core_regulator);
- if (ret) {
- pr_err("%s:core set voltage error\n", __func__);
- return ret;
- } else {
- dev_dbg(dev,
- "%s:core set voltage ok\n", __func__);
- }
- } else {
- core_regulator = NULL;
- pr_err("%s: cannot get core voltage error\n", __func__);
- }
-
- analog_regulator = devm_regulator_get(dev, "AVDD");
- if (!IS_ERR(analog_regulator)) {
- regulator_set_voltage(analog_regulator,
- OV5647_VOLTAGE_ANALOG,
- OV5647_VOLTAGE_ANALOG);
- ret = regulator_enable(analog_regulator);
- if (ret) {
- pr_err("%s:analog set voltage error\n",
- __func__);
- return ret;
- } else {
- dev_dbg(dev,
- "%s:analog set voltage ok\n", __func__);
- }
- } else {
- analog_regulator = NULL;
- pr_err("%s: cannot get analog voltage error\n", __func__);
- }
-
- return ret;
-}
-
-static s32 ov5647_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(ov5647_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 ov5647_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(ov5647_data.i2c_client, au8RegBuf, 2)) {
- pr_err("%s:write reg error:reg=%x\n",
- __func__, reg);
- return -1;
- }
-
- if (1 != i2c_master_recv(ov5647_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;
-}
-
-/* index: index of otp group. (0, 1, 2)
- * return:
- * 0, group index is empty
- * 1, group index has invalid data
- * 2, group index has valid data */
-static int ov5647_check_otp(int index)
-{
- int i;
- int address;
- u8 temp;
-
- /* read otp into buffer */
- ov5647_write_reg(0x3d21, 0x01);
- msleep(20);
- address = 0x3d05 + index * 9;
- temp = ov5647_read_reg(address, &temp);
-
- /* disable otp read */
- ov5647_write_reg(0x3d21, 0x00);
-
- /* clear otp buffer */
- for (i = 0; i < 32; i++)
- ov5647_write_reg(0x3d00 + i, 0x00);
-
- if (!temp)
- return 0;
- else if ((!(temp & 0x80)) && (temp & 0x7f))
- return 2;
- else
- return 1;
-}
-
-/* index: index of otp group. (0, 1, 2)
- * return: 0 */
-static int ov5647_read_otp(int index, struct otp_struct *otp_ptr)
-{
- int i;
- int address;
- u8 temp;
-
- /* read otp into buffer */
- ov5647_write_reg(0x3d21, 0x01);
- msleep(2);
- address = 0x3d05 + index * 9;
- temp = ov5647_read_reg(address, &temp);
- (*otp_ptr).customer_id = temp & 0x7f;
-
- (*otp_ptr).module_integrator_id = ov5647_read_reg(address, &temp);
- (*otp_ptr).lens_id = ov5647_read_reg(address + 1, &temp);
- (*otp_ptr).rg_ratio = ov5647_read_reg(address + 2, &temp);
- (*otp_ptr).bg_ratio = ov5647_read_reg(address + 3, &temp);
- (*otp_ptr).user_data[0] = ov5647_read_reg(address + 4, &temp);
- (*otp_ptr).user_data[1] = ov5647_read_reg(address + 5, &temp);
- (*otp_ptr).user_data[2] = ov5647_read_reg(address + 6, &temp);
- (*otp_ptr).light_rg = ov5647_read_reg(address + 7, &temp);
- (*otp_ptr).light_bg = ov5647_read_reg(address + 8, &temp);
-
- /* disable otp read */
- ov5647_write_reg(0x3d21, 0x00);
-
- /* clear otp buffer */
- for (i = 0; i < 32; i++)
- ov5647_write_reg(0x3d00 + i, 0x00);
-
- return 0;
-}
-
-/* R_gain, sensor red gain of AWB, 0x400 =1
- * G_gain, sensor green gain of AWB, 0x400 =1
- * B_gain, sensor blue gain of AWB, 0x400 =1
- * return 0 */
-static int ov5647_update_awb_gain(int R_gain, int G_gain, int B_gain)
-{
- if (R_gain > 0x400) {
- ov5647_write_reg(0x5186, R_gain >> 8);
- ov5647_write_reg(0x5187, R_gain & 0x00ff);
- }
- if (G_gain > 0x400) {
- ov5647_write_reg(0x5188, G_gain >> 8);
- ov5647_write_reg(0x5189, G_gain & 0x00ff);
- }
- if (B_gain > 0x400) {
- ov5647_write_reg(0x518a, B_gain >> 8);
- ov5647_write_reg(0x518b, B_gain & 0x00ff);
- }
-
- return 0;
-}
-
-/* call this function after OV5647 initialization
- * return value:
- * 0 update success
- * 1 no OTP */
-static int ov5647_update_otp(void)
-{
- struct otp_struct current_otp;
- int i;
- int otp_index;
- int temp;
- int R_gain, G_gain, B_gain, G_gain_R, G_gain_B;
- int rg, bg;
-
- /* R/G and B/G of current camera module is read out from sensor OTP
- * check first OTP with valid data */
- for (i = 0; i < 3; i++) {
- temp = ov5647_check_otp(i);
- if (temp == 2) {
- otp_index = i;
- break;
- }
- }
- if (i == 3) {
- /* no valid wb OTP data */
- printk(KERN_WARNING "No valid wb otp data\n");
- return 1;
- }
-
- ov5647_read_otp(otp_index, ¤t_otp);
- if (current_otp.light_rg == 0)
- rg = current_otp.rg_ratio;
- else
- rg = current_otp.rg_ratio * (current_otp.light_rg + 128) / 256;
-
- if (current_otp.light_bg == 0)
- bg = current_otp.bg_ratio;
- else
- bg = current_otp.bg_ratio * (current_otp.light_bg + 128) / 256;
-
- /* calculate G gain
- * 0x400 = 1x gain */
- if (bg < BG_Ratio_Typical) {
- if (rg < RG_Ratio_Typical) {
- /* current_otp.bg_ratio < BG_Ratio_typical &&
- * current_otp.rg_ratio < RG_Ratio_typical */
- G_gain = 0x400;
- B_gain = 0x400 * BG_Ratio_Typical / bg;
- R_gain = 0x400 * RG_Ratio_Typical / rg;
- } else {
- /* current_otp.bg_ratio < BG_Ratio_typical &&
- * current_otp.rg_ratio >= RG_Ratio_typical */
- R_gain = 0x400;
- G_gain = 0x400 * rg / RG_Ratio_Typical;
- B_gain = G_gain * BG_Ratio_Typical / bg;
- }
- } else {
- if (rg < RG_Ratio_Typical) {
- /* current_otp.bg_ratio >= BG_Ratio_typical &&
- * current_otp.rg_ratio < RG_Ratio_typical */
- B_gain = 0x400;
- G_gain = 0x400 * bg / BG_Ratio_Typical;
- R_gain = G_gain * RG_Ratio_Typical / rg;
- } else {
- /* current_otp.bg_ratio >= BG_Ratio_typical &&
- * current_otp.rg_ratio >= RG_Ratio_typical */
- G_gain_B = 0x400 * bg / BG_Ratio_Typical;
- G_gain_R = 0x400 * rg / RG_Ratio_Typical;
- if (G_gain_B > G_gain_R) {
- B_gain = 0x400;
- G_gain = G_gain_B;
- R_gain = G_gain * RG_Ratio_Typical / rg;
- } else {
- R_gain = 0x400;
- G_gain = G_gain_R;
- B_gain = G_gain * BG_Ratio_Typical / bg;
- }
- }
- }
- ov5647_update_awb_gain(R_gain, G_gain, B_gain);
- return 0;
-}
-
-static void ov5647_stream_on(void)
-{
- ov5647_write_reg(0x4202, 0x00);
-}
-
-static void ov5647_stream_off(void)
-{
- ov5647_write_reg(0x4202, 0x0f);
- /* both clock and data lane in LP00 */
- ov5647_write_reg(0x0100, 0x00);
-}
-
-static int ov5647_get_sysclk(void)
-{
- /* calculate sysclk */
- int xvclk = ov5647_data.mclk / 10000;
- int sysclk, temp1, temp2;
- int pre_div02x, div_cnt7b, sdiv0, pll_rdiv, bit_div2x, sclk_div, VCO;
- int pre_div02x_map[] = {2, 2, 4, 6, 8, 3, 12, 5, 16, 2, 2, 2, 2, 2, 2, 2};
- int sdiv0_map[] = {16, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
- int pll_rdiv_map[] = {1, 2};
- int bit_div2x_map[] = {2, 2, 2, 2, 2, 2, 2, 2, 4, 2, 5, 2, 2, 2, 2, 2};
- int sclk_div_map[] = {1, 2, 4, 1};
- u8 temp;
-
- temp1 = ov5647_read_reg(0x3037, &temp);
- temp2 = temp1 & 0x0f;
- pre_div02x = pre_div02x_map[temp2];
- temp2 = (temp1 >> 4) & 0x01;
- pll_rdiv = pll_rdiv_map[temp2];
- temp1 = ov5647_read_reg(0x3036, &temp);
-
- div_cnt7b = temp1;
-
- VCO = xvclk * 2 / pre_div02x * div_cnt7b;
- temp1 = ov5647_read_reg(0x3035, &temp);
- temp2 = temp1 >> 4;
- sdiv0 = sdiv0_map[temp2];
- temp1 = ov5647_read_reg(0x3034, &temp);
- temp2 = temp1 & 0x0f;
- bit_div2x = bit_div2x_map[temp2];
- temp1 = ov5647_read_reg(0x3106, &temp);
- temp2 = (temp1 >> 2) & 0x03;
- sclk_div = sclk_div_map[temp2];
- sysclk = VCO * 2 / sdiv0 / pll_rdiv / bit_div2x / sclk_div;
- return sysclk;
-}
-
-static void ov5647_set_night_mode(void)
-{
- /* read HTS from register settings */
- u8 mode;
-
- ov5647_read_reg(0x3a00, &mode);
- mode &= 0xfb;
- ov5647_write_reg(0x3a00, mode);
-}
-
-static int ov5647_get_HTS(void)
-{
- /* read HTS from register settings */
- int HTS;
- u8 temp;
-
- HTS = ov5647_read_reg(0x380c, &temp);
- HTS = (HTS << 8) + ov5647_read_reg(0x380d, &temp);
-
- return HTS;
-}
-
-static int ov5647_soft_reset(void)
-{
- /* soft reset ov5647 */
-
- ov5647_write_reg(0x0103, 0x1);
- msleep(5);
-
- return 0;
-}
-
-static int ov5647_get_VTS(void)
-{
- /* read VTS from register settings */
- int VTS;
- u8 temp;
-
- /* total vertical size[15:8] high byte */
- VTS = ov5647_read_reg(0x380e, &temp);
-
- VTS = (VTS << 8) + ov5647_read_reg(0x380f, &temp);
-
- return VTS;
-}
-
-static int ov5647_set_VTS(int VTS)
-{
- /* write VTS to registers */
- int temp;
-
- temp = VTS & 0xff;
- ov5647_write_reg(0x380f, temp);
-
- temp = VTS >> 8;
- ov5647_write_reg(0x380e, temp);
-
- return 0;
-}
-
-static int ov5647_get_shutter(void)
-{
- /* read shutter, in number of line period */
- int shutter;
- u8 temp;
-
- shutter = (ov5647_read_reg(0x03500, &temp) & 0x0f);
- shutter = (shutter << 8) + ov5647_read_reg(0x3501, &temp);
- shutter = (shutter << 4) + (ov5647_read_reg(0x3502, &temp)>>4);
-
- return shutter;
-}
-
-static int ov5647_set_shutter(int shutter)
-{
- /* write shutter, in number of line period */
- int temp;
-
- shutter = shutter & 0xffff;
-
- temp = shutter & 0x0f;
- temp = temp << 4;
- ov5647_write_reg(0x3502, temp);
-
- temp = shutter & 0xfff;
- temp = temp >> 4;
- ov5647_write_reg(0x3501, temp);
-
- temp = shutter >> 12;
- ov5647_write_reg(0x3500, temp);
-
- return 0;
-}
-
-static int ov5647_get_gain16(void)
-{
- /* read gain, 16 = 1x */
- int gain16;
- u8 temp;
-
- gain16 = ov5647_read_reg(0x350a, &temp) & 0x03;
- gain16 = (gain16 << 8) + ov5647_read_reg(0x350b, &temp);
-
- return gain16;
-}
-
-static int ov5647_set_gain16(int gain16)
-{
- /* write gain, 16 = 1x */
- u8 temp;
- gain16 = gain16 & 0x3ff;
-
- temp = gain16 & 0xff;
- ov5647_write_reg(0x350b, temp);
-
- temp = gain16 >> 8;
- ov5647_write_reg(0x350a, temp);
-
- return 0;
-}
-
-static int ov5647_get_light_freq(void)
-{
- /* get banding filter value */
- int temp, temp1, light_freq = 0;
- u8 tmp;
-
- temp = ov5647_read_reg(0x3c01, &tmp);
-
- if (temp & 0x80) {
- /* manual */
- temp1 = ov5647_read_reg(0x3c00, &tmp);
- if (temp1 & 0x04) {
- /* 50Hz */
- light_freq = 50;
- } else {
- /* 60Hz */
- light_freq = 60;
- }
- } else {
- /* auto */
- temp1 = ov5647_read_reg(0x3c0c, &tmp);
- if (temp1 & 0x01) {
- /* 50Hz */
- light_freq = 50;
- } else {
- /* 60Hz */
- }
- }
- return light_freq;
-}
-
-static void ov5647_set_bandingfilter(void)
-{
- int prev_VTS;
- int band_step60, max_band60, band_step50, max_band50;
-
- /* read preview PCLK */
- prev_sysclk = ov5647_get_sysclk();
- /* read preview HTS */
- prev_HTS = ov5647_get_HTS();
-
- /* read preview VTS */
- prev_VTS = ov5647_get_VTS();
-
- /* calculate banding filter */
- /* 60Hz */
- band_step60 = prev_sysclk * 100/prev_HTS * 100/120;
- ov5647_write_reg(0x3a0a, (band_step60 >> 8));
- ov5647_write_reg(0x3a0b, (band_step60 & 0xff));
-
- max_band60 = (int)((prev_VTS-4)/band_step60);
- ov5647_write_reg(0x3a0d, max_band60);
-
- /* 50Hz */
- band_step50 = prev_sysclk * 100/prev_HTS;
- ov5647_write_reg(0x3a08, (band_step50 >> 8));
- ov5647_write_reg(0x3a09, (band_step50 & 0xff));
-
- max_band50 = (int)((prev_VTS-4)/band_step50);
- ov5647_write_reg(0x3a0e, max_band50);
-}
-
-static int ov5647_set_AE_target(int target)
-{
- /* stable in high */
- int fast_high, fast_low;
- AE_low = target * 23 / 25; /* 0.92 */
- AE_high = target * 27 / 25; /* 1.08 */
-
- fast_high = AE_high<<1;
- if (fast_high > 255)
- fast_high = 255;
-
- fast_low = AE_low >> 1;
-
- ov5647_write_reg(0x3a0f, AE_high);
- ov5647_write_reg(0x3a10, AE_low);
- ov5647_write_reg(0x3a1b, AE_high);
- ov5647_write_reg(0x3a1e, AE_low);
- ov5647_write_reg(0x3a11, fast_high);
- ov5647_write_reg(0x3a1f, fast_low);
-
- return 0;
-}
-
-static void ov5647_turn_on_AE_AG(int enable)
-{
- u8 ae_ag_ctrl;
-
- ov5647_read_reg(0x3503, &ae_ag_ctrl);
- if (enable) {
- /* turn on auto AE/AG */
- ae_ag_ctrl = ae_ag_ctrl & ~(0x03);
- } else {
- /* turn off AE/AG */
- ae_ag_ctrl = ae_ag_ctrl | 0x03;
- }
- ov5647_write_reg(0x3503, ae_ag_ctrl);
-}
-
-static void ov5647_set_virtual_channel(int channel)
-{
- u8 channel_id;
-
- ov5647_read_reg(0x4814, &channel_id);
- channel_id &= ~(3 << 6);
- ov5647_write_reg(0x4814, channel_id | (channel << 6));
-}
-
-/* download ov5647 settings to sensor through i2c */
-static int ov5647_download_firmware(struct reg_value *pModeSetting, s32 ArySize)
-{
- register u32 Delay_ms = 0;
- register u16 RegAddr = 0;
- register u8 Mask = 0;
- register u8 Val = 0;
- u8 RegVal = 0;
- int i, retval = 0;
-
- for (i = 0; i < ArySize; ++i, ++pModeSetting) {
- Delay_ms = pModeSetting->u32Delay_ms;
- RegAddr = pModeSetting->u16RegAddr;
- Val = pModeSetting->u8Val;
- Mask = pModeSetting->u8Mask;
-
- if (Mask) {
- retval = ov5647_read_reg(RegAddr, &RegVal);
- if (retval < 0)
- goto err;
-
- RegVal &= ~(u8)Mask;
- Val &= Mask;
- Val |= RegVal;
- }
-
- retval = ov5647_write_reg(RegAddr, Val);
- if (retval < 0)
- goto err;
-
- if (Delay_ms)
- msleep(Delay_ms);
- }
-err:
- return retval;
-}
-
-/* sensor changes between scaling and subsampling
- * go through exposure calcualtion
- */
-static int ov5647_change_mode_exposure_calc(enum ov5647_frame_rate frame_rate,
- enum ov5647_mode mode)
-{
- struct reg_value *pModeSetting = NULL;
- s32 ArySize = 0;
- int pre_shutter, pre_gain16;
- int cap_shutter, cap_gain16;
- int pre_sysclk, pre_HTS;
- int cap_sysclk, cap_HTS, cap_VTS;
- long cap_gain16_shutter;
- int retval = 0;
-
- /* check if the input mode and frame rate is valid */
- pModeSetting =
- ov5647_mode_info_data[frame_rate][mode].init_data_ptr;
- ArySize =
- ov5647_mode_info_data[frame_rate][mode].init_data_size;
-
- ov5647_data.pix.width =
- ov5647_mode_info_data[frame_rate][mode].width;
- ov5647_data.pix.height =
- ov5647_mode_info_data[frame_rate][mode].height;
-
- if (ov5647_data.pix.width == 0 || ov5647_data.pix.height == 0 ||
- pModeSetting == NULL || ArySize == 0)
- return -EINVAL;
-
- ov5647_stream_off();
-
- /* turn off night mode for capture */
- ov5647_set_night_mode();
-
- pre_shutter = ov5647_get_shutter();
- pre_gain16 = ov5647_get_gain16();
- pre_HTS = ov5647_get_HTS();
- pre_sysclk = ov5647_get_sysclk();
-
- /* Write capture setting */
- retval = ov5647_download_firmware(pModeSetting, ArySize);
- if (retval < 0)
- goto err;
-
- /* read capture VTS */
- cap_VTS = ov5647_get_VTS();
- cap_HTS = ov5647_get_HTS();
- cap_sysclk = ov5647_get_sysclk();
-
- /* calculate capture shutter/gain16 */
- cap_shutter = pre_shutter * cap_sysclk/pre_sysclk * pre_HTS / cap_HTS;
-
- if (cap_shutter < 16) {
- cap_gain16_shutter = pre_shutter * pre_gain16 *
- cap_sysclk / pre_sysclk * pre_HTS / cap_HTS;
- cap_shutter = ((int)(cap_gain16_shutter / 16));
- if (cap_shutter < 1)
- cap_shutter = 1;
- cap_gain16 = ((int)(cap_gain16_shutter / cap_shutter));
- if (cap_gain16 < 16)
- cap_gain16 = 16;
- } else
- cap_gain16 = pre_gain16;
-
- /* gain to shutter */
- while ((cap_gain16 > 32) &&
- (cap_shutter < ((int)((cap_VTS - 4) / 2)))) {
- cap_gain16 = cap_gain16 / 2;
- cap_shutter = cap_shutter * 2;
- }
- /* write capture gain */
- ov5647_set_gain16(cap_gain16);
-
- /* write capture shutter */
- if (cap_shutter > (cap_VTS - 4)) {
- cap_VTS = cap_shutter + 4;
- ov5647_set_VTS(cap_VTS);
- }
- ov5647_set_shutter(cap_shutter);
-
-err:
- return retval;
-}
-
-/* if sensor changes inside scaling or subsampling
- * change mode directly
- * */
-static int ov5647_change_mode_direct(enum ov5647_frame_rate frame_rate,
- enum ov5647_mode mode)
-{
- struct reg_value *pModeSetting = NULL;
- s32 ArySize = 0;
- int retval = 0;
-
- /* check if the input mode and frame rate is valid */
- pModeSetting =
- ov5647_mode_info_data[frame_rate][mode].init_data_ptr;
- ArySize =
- ov5647_mode_info_data[frame_rate][mode].init_data_size;
-
- ov5647_data.pix.width =
- ov5647_mode_info_data[frame_rate][mode].width;
- ov5647_data.pix.height =
- ov5647_mode_info_data[frame_rate][mode].height;
-
- if (ov5647_data.pix.width == 0 || ov5647_data.pix.height == 0 ||
- pModeSetting == NULL || ArySize == 0)
- return -EINVAL;
-
- /* turn off AE/AG */
- ov5647_turn_on_AE_AG(0);
-
- ov5647_stream_off();
-
- /* Write capture setting */
- retval = ov5647_download_firmware(pModeSetting, ArySize);
- if (retval < 0)
- goto err;
-
- ov5647_turn_on_AE_AG(1);
-
-err:
- return retval;
-}
-
-static int ov5647_init_mode(enum ov5647_frame_rate frame_rate,
- enum ov5647_mode mode, enum ov5647_mode orig_mode)
-{
- struct reg_value *pModeSetting = NULL;
- s32 ArySize = 0;
- int retval = 0;
- u32 msec_wait4stable = 0;
- enum ov5647_downsize_mode dn_mode, orig_dn_mode;
-
- if ((mode > ov5647_mode_MAX || mode < ov5647_mode_MIN)
- && (mode != ov5647_mode_INIT)) {
- pr_err("Wrong ov5647 mode detected!\n");
- return -1;
- }
-
- dn_mode = ov5647_mode_info_data[frame_rate][mode].dn_mode;
- orig_dn_mode = ov5647_mode_info_data[frame_rate][orig_mode].dn_mode;
- if (mode == ov5647_mode_INIT) {
- ov5647_soft_reset();
- pModeSetting = ov5647_init_setting;
- ArySize = ARRAY_SIZE(ov5647_init_setting);
- retval = ov5647_download_firmware(pModeSetting, ArySize);
- if (retval < 0)
- goto err;
- pModeSetting = ov5647_setting_60fps_VGA_640_480;
- ArySize = ARRAY_SIZE(ov5647_setting_60fps_VGA_640_480);
- retval = ov5647_download_firmware(pModeSetting, ArySize);
-
- ov5647_data.pix.width = 640;
- ov5647_data.pix.height = 480;
- } else if ((dn_mode == SUBSAMPLING && orig_dn_mode == SCALING) ||
- (dn_mode == SCALING && orig_dn_mode == SUBSAMPLING)) {
- /* change between subsampling and scaling
- * go through exposure calucation */
- retval = ov5647_change_mode_exposure_calc(frame_rate, mode);
- } else {
- /* change inside subsampling or scaling
- * download firmware directly */
- retval = ov5647_change_mode_direct(frame_rate, mode);
- }
-
- if (retval < 0)
- goto err;
-
- ov5647_set_AE_target(AE_Target);
- ov5647_get_light_freq();
- ov5647_set_bandingfilter();
- ov5647_set_virtual_channel(ov5647_data.csi);
-
- /* add delay to wait for sensor stable */
- if (mode == ov5647_mode_QSXGA_2592_1944) {
- /* dump the first two frames: 1/7.5*2
- * the frame rate of QSXGA is 7.5fps */
- msec_wait4stable = 267;
- } else if (frame_rate == ov5647_15_fps) {
- /* dump the first nine frames: 1/15*9 */
- msec_wait4stable = 600;
- } else if (frame_rate == ov5647_30_fps) {
- /* dump the first nine frames: 1/30*9 */
- msec_wait4stable = 300;
- }
- msleep(msec_wait4stable);
-
-err:
- return retval;
-}
-
-/*!
- * ov5647_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 ov5647_s_power(struct v4l2_subdev *sd, int on)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov5647 *sensor = to_ov5647(client);
-
- if (on && !sensor->on) {
- 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);
- }
-
- sensor->on = on;
-
- return 0;
-}
-
-/*!
- * ov5647_g_parm - V4L2 sensor interface handler for VIDIOC_G_PARM ioctl
- * @s: pointer to standard V4L2 sub device structure
- * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure
- *
- * Returns the sensor's video CAPTURE parameters.
- */
-static int ov5647_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov5647 *sensor = to_ov5647(client);
- 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;
-}
-
-/*!
- * ov5460_s_parm - V4L2 sensor interface handler for VIDIOC_S_PARM ioctl
- * @s: pointer to standard V4L2 sub 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 ov5647_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov5647 *sensor = to_ov5647(client);
- struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe;
- u32 tgt_fps; /* target frames per secound */
- enum ov5647_frame_rate frame_rate;
- enum ov5647_mode orig_mode;
- int ret = 0;
-
- /* Make sure power on */
- ov5647_power_down(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 = ov5647_15_fps;
- else if (tgt_fps == 30)
- frame_rate = ov5647_30_fps;
- else {
- pr_err(" The camera frame rate is not supported!\n");
- return -EINVAL;
- }
-
- orig_mode = sensor->streamcap.capturemode;
- ret = ov5647_init_mode(frame_rate,
- (u32)a->parm.capture.capturemode, orig_mode);
- if (ret < 0)
- return ret;
-
- sensor->streamcap.timeperframe = *timeperframe;
- sensor->streamcap.capturemode =
- (u32)a->parm.capture.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;
-}
-
-static int ov5647_set_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_format *format)
-{
- struct v4l2_mbus_framefmt *mf = &format->format;
- const struct ov5647_datafmt *fmt = ov5647_find_datafmt(mf->code);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov5647 *sensor = to_ov5647(client);
-
- if (!fmt) {
- mf->code = ov5647_colour_fmts[0].code;
- mf->colorspace = ov5647_colour_fmts[0].colorspace;
- }
-
- mf->field = V4L2_FIELD_NONE;
-
- if (format->which == V4L2_SUBDEV_FORMAT_TRY)
- return 0;
-
- sensor->fmt = fmt;
-
- return 0;
-}
-
-static int ov5647_get_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_format *format)
-{
- struct v4l2_mbus_framefmt *mf = &format->format;
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov5647 *sensor = to_ov5647(client);
- const struct ov5647_datafmt *fmt = sensor->fmt;
-
- if (format->pad)
- return -EINVAL;
-
- mf->code = fmt->code;
- mf->colorspace = fmt->colorspace;
- mf->field = V4L2_FIELD_NONE;
-
- return 0;
-}
-
-static int ov5647_enum_mbus_code(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_mbus_code_enum *code)
-{
- if (code->pad || code->index >= ARRAY_SIZE(ov5647_colour_fmts))
- return -EINVAL;
-
- code->code = ov5647_colour_fmts[code->index].code;
- return 0;
-}
-
-/*!
- * ov5647_enum_framesizes - V4L2 sensor interface handler for
- * VIDIOC_ENUM_FRAMESIZES ioctl
- * @s: pointer to standard V4L2 device structure
- * @fsize: standard V4L2 VIDIOC_ENUM_FRAMESIZES ioctl structure
- *
- * Return 0 if successful, otherwise -EINVAL.
- */
-static int ov5647_enum_framesizes(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_frame_size_enum *fse)
-{
- if (fse->index > ov5647_mode_MAX)
- return -EINVAL;
-
- fse->max_width =
- max(ov5647_mode_info_data[0][fse->index].width,
- ov5647_mode_info_data[1][fse->index].width);
- fse->min_width = fse->max_width;
- fse->max_height =
- max(ov5647_mode_info_data[0][fse->index].height,
- ov5647_mode_info_data[1][fse->index].height);
- fse->min_height = fse->max_height;
- return 0;
-}
-
-/*!
- * ov5647_enum_frameintervals - V4L2 sensor interface handler for
- * VIDIOC_ENUM_FRAMEINTERVALS ioctl
- * @s: pointer to standard V4L2 device structure
- * @fival: standard V4L2 VIDIOC_ENUM_FRAMEINTERVALS ioctl structure
- *
- * Return 0 if successful, otherwise -EINVAL.
- */
-static int ov5647_enum_frameintervals(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_frame_interval_enum *fie)
-{
- int i, j, count;
-
- if (fie->index < 0 || fie->index > ov5647_mode_MAX)
- return -EINVAL;
-
- if (fie->width == 0 || fie->height == 0 ||
- fie->code == 0) {
- pr_warning("Please assign pixel format, width and height.\n");
- return -EINVAL;
- }
-
- fie->interval.numerator = 1;
-
- count = 0;
- for (i = 0; i < ARRAY_SIZE(ov5647_mode_info_data); i++) {
- for (j = 0; j < (ov5647_mode_MAX + 1); j++) {
- if (fie->width == ov5647_mode_info_data[i][j].width
- && fie->height == ov5647_mode_info_data[i][j].height
- && ov5647_mode_info_data[i][j].init_data_ptr != NULL) {
- count++;
- }
- if (fie->index == (count - 1)) {
- fie->interval.denominator =
- ov5647_framerates[i];
- return 0;
- }
- }
- }
-
- return -EINVAL;
-}
-
-/*!
- * dev_init - V4L2 sensor init
- * @s: pointer to standard V4L2 device structure
- *
- */
-static int init_device(void)
-{
- u32 tgt_xclk; /* target xclk */
- u32 tgt_fps; /* target frames per secound */
- enum ov5647_frame_rate frame_rate;
- int ret;
-
- ov5647_data.on = true;
-
- /* mclk */
- tgt_xclk = ov5647_data.mclk;
- tgt_xclk = min(tgt_xclk, (u32)OV5647_XCLK_MAX);
- tgt_xclk = max(tgt_xclk, (u32)OV5647_XCLK_MIN);
- ov5647_data.mclk = tgt_xclk;
-
- pr_debug(" Setting mclk to %d MHz\n", tgt_xclk / 1000000);
-
- /* Default camera frame rate is set in probe */
- tgt_fps = ov5647_data.streamcap.timeperframe.denominator /
- ov5647_data.streamcap.timeperframe.numerator;
-
- if (tgt_fps == 15)
- frame_rate = ov5647_15_fps;
- else if (tgt_fps == 30)
- frame_rate = ov5647_30_fps;
- else
- return -EINVAL; /* Only support 15fps or 30fps now. */
-
- ret = ov5647_init_mode(frame_rate, ov5647_mode_INIT, ov5647_mode_INIT);
-
- ov5647_update_otp();
- return ret;
-}
-
-static int ov5647_s_stream(struct v4l2_subdev *sd, int enable)
-{
- if (enable)
- ov5647_stream_on();
- else
- ov5647_stream_off();
- return 0;
-}
-
-static struct v4l2_subdev_video_ops ov5647_subdev_video_ops = {
- .g_parm = ov5647_g_parm,
- .s_parm = ov5647_s_parm,
- .s_stream = ov5647_s_stream,
-};
-
-static const struct v4l2_subdev_pad_ops ov5647_subdev_pad_ops = {
- .enum_frame_size = ov5647_enum_framesizes,
- .enum_frame_interval = ov5647_enum_frameintervals,
- .enum_mbus_code = ov5647_enum_mbus_code,
- .set_fmt = ov5647_set_fmt,
- .get_fmt = ov5647_get_fmt,
-};
-
-static struct v4l2_subdev_core_ops ov5647_subdev_core_ops = {
- .s_power = ov5647_s_power,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .g_register = ov5647_get_register,
- .s_register = ov5647_set_register,
-#endif
-};
-
-static struct v4l2_subdev_ops ov5647_subdev_ops = {
- .core = &ov5647_subdev_core_ops,
- .video = &ov5647_subdev_video_ops,
- .pad = &ov5647_subdev_pad_ops,
-};
-
-
-/*!
- * ov5647 I2C probe function
- *
- * @param adapter struct i2c_adapter *
- * @return Error code indicating success or failure
- */
-static int ov5647_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- struct pinctrl *pinctrl;
- struct device *dev = &client->dev;
- int retval;
- u8 chip_id_high, chip_id_low;
-
- /* ov5647 pinctrl */
- pinctrl = devm_pinctrl_get_select_default(dev);
- if (IS_ERR(pinctrl)) {
- dev_warn(dev, "no pin available\n");
- }
-
-
- /* request power down pin */
- pwn_gpio = of_get_named_gpio(dev->of_node, "pwn-gpios", 0);
- if (!gpio_is_valid(pwn_gpio)) {
- dev_warn(dev, "no sensor pwdn pin available\n");
- pwn_gpio = -1;
- } else {
- retval = devm_gpio_request_one(dev, pwn_gpio, GPIOF_OUT_INIT_HIGH,
- "ov5647_mipi_pwdn");
- if (retval < 0) {
- dev_warn(dev, "Failed to set power pin\n");
- return retval;
- }
- }
-
- /* request reset pin */
- rst_gpio = of_get_named_gpio(dev->of_node, "rst-gpios", 0);
- if (!gpio_is_valid(rst_gpio)) {
- dev_warn(dev, "no sensor reset pin available\n");
- rst_gpio = -1;
- } else {
- retval = devm_gpio_request_one(dev, rst_gpio, GPIOF_OUT_INIT_HIGH,
- "ov5647_mipi_reset");
- if (retval < 0) {
- dev_warn(dev, "Failed to set reset pin\n");
- return retval;
- }
- }
-
- /* Set initial values for the sensor struct. */
- memset(&ov5647_data, 0, sizeof(ov5647_data));
- ov5647_data.sensor_clk = devm_clk_get(dev, "csi_mclk");
- if (IS_ERR(ov5647_data.sensor_clk)) {
- /* assuming clock enabled by default */
- ov5647_data.sensor_clk = NULL;
- dev_err(dev, "clock-frequency missing or invalid\n");
- return PTR_ERR(ov5647_data.sensor_clk);
- }
-
- retval = of_property_read_u32(dev->of_node, "mclk",
- &(ov5647_data.mclk));
- if (retval) {
- dev_err(dev, "mclk missing or invalid\n");
- return retval;
- }
-
- retval = of_property_read_u32(dev->of_node, "mclk_source",
- (u32 *) &(ov5647_data.mclk_source));
- if (retval) {
- dev_err(dev, "mclk_source missing or invalid\n");
- return retval;
- }
-
- retval = of_property_read_u32(dev->of_node, "csi_id",
- &(ov5647_data.csi));
- if (retval) {
- dev_err(dev, "csi id missing or invalid\n");
- return retval;
- }
-
- clk_prepare_enable(ov5647_data.sensor_clk);
-
- ov5647_data.io_init = ov5647_reset;
- ov5647_data.i2c_client = client;
- ov5647_data.pix.pixelformat = V4L2_PIX_FMT_SBGGR8;
- ov5647_data.pix.width = 640;
- ov5647_data.pix.height = 480;
- ov5647_data.streamcap.capability = V4L2_MODE_HIGHQUALITY |
- V4L2_CAP_TIMEPERFRAME;
- ov5647_data.streamcap.capturemode = 0;
- ov5647_data.streamcap.timeperframe.denominator = DEFAULT_FPS;
- ov5647_data.streamcap.timeperframe.numerator = 1;
-
- ov5647_regulator_enable(&client->dev);
-
- ov5647_reset();
-
- ov5647_power_down(0);
-
- retval = ov5647_read_reg(OV5647_CHIP_ID_HIGH_BYTE, &chip_id_high);
- if (retval < 0 || chip_id_high != 0x56) {
- pr_warning("camera ov5647_mipi is not found\n");
- clk_disable_unprepare(ov5647_data.sensor_clk);
- return -ENODEV;
- }
- retval = ov5647_read_reg(OV5647_CHIP_ID_LOW_BYTE, &chip_id_low);
- if (retval < 0 || chip_id_low != 0x47) {
- pr_warning("camera ov5647_mipi is not found\n");
- clk_disable_unprepare(ov5647_data.sensor_clk);
- return -ENODEV;
- }
-
- retval = init_device();
- if (retval < 0) {
- clk_disable_unprepare(ov5647_data.sensor_clk);
- pr_warning("camera ov5647 init failed\n");
- ov5647_power_down(1);
- return retval;
- }
-
- v4l2_i2c_subdev_init(&ov5647_data.subdev, client, &ov5647_subdev_ops);
-
- ov5647_data.subdev.grp_id = 678;
- retval = v4l2_async_register_subdev(&ov5647_data.subdev);
- if (retval < 0)
- dev_err(&client->dev,
- "%s--Async register failed, ret=%d\n", __func__, retval);
-
- ov5647_stream_off();
- pr_info("camera ov5647_mipi is found\n");
- return retval;
-}
-
-/*!
- * ov5647 I2C detach function
- *
- * @param client struct i2c_client *
- * @return Error code indicating success or failure
- */
-static int ov5647_remove(struct i2c_client *client)
-{
- struct v4l2_subdev *sd = i2c_get_clientdata(client);
-
- v4l2_async_unregister_subdev(sd);
-
- clk_disable_unprepare(ov5647_data.sensor_clk);
-
- ov5647_power_down(1);
-
- if (gpo_regulator)
- regulator_disable(gpo_regulator);
-
- if (analog_regulator)
- regulator_disable(analog_regulator);
-
- if (core_regulator)
- regulator_disable(core_regulator);
-
- if (io_regulator)
- regulator_disable(io_regulator);
-
- return 0;
-}
-
-module_i2c_driver(ov5647_i2c_driver);
-
-MODULE_AUTHOR("Freescale Semiconductor, Inc.");
-MODULE_DESCRIPTION("OV5647 MIPI Camera Driver");
-MODULE_LICENSE("GPL");
-MODULE_VERSION("1.0");
-MODULE_ALIAS("CSI");