diff options
author | Sandor Yu <R01008@freescale.com> | 2014-11-10 15:36:49 +0800 |
---|---|---|
committer | Nitin Garg <nitin.garg@freescale.com> | 2015-04-14 14:01:01 -0500 |
commit | e562678d5358bcf7939714c93a52c6e07b5e3d60 (patch) | |
tree | 066602f9ed4a0db03c4133c878f5589c397b456c | |
parent | 3fc2e5ca16709b2cda19d0098216b0a7812f0725 (diff) |
MLK-9779-01 camera: imx6sx/sl CSI/VADC driver in subdev
CSI and VADC driver rewrite with v4l2 subdev architecture.
- mx6s_capture.c driver support imx6sx and imx6sl csi module.
- No PXP function included in csi driver, csi driver not support
csc, crop and resize function.
- Both csi and vadc driver register device tree.
- v4l2 subdev bridge device drivers register device with asynchronous.
Signed-off-by: Sandor Yu <R01008@freescale.com>
-rw-r--r-- | drivers/media/platform/Kconfig | 1 | ||||
-rw-r--r-- | drivers/media/platform/Makefile | 1 | ||||
-rw-r--r-- | drivers/media/platform/mxc/subdev/Kconfig | 21 | ||||
-rw-r--r-- | drivers/media/platform/mxc/subdev/Makefile | 4 | ||||
-rw-r--r-- | drivers/media/platform/mxc/subdev/mx6s_capture.c | 1694 | ||||
-rw-r--r-- | drivers/media/platform/mxc/subdev/mxc_vadc.c | 752 | ||||
-rw-r--r-- | drivers/media/platform/mxc/subdev/mxc_vadc.h | 236 |
7 files changed, 2709 insertions, 0 deletions
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index c8eac2e7ab08..f4994cb2c85e 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -130,6 +130,7 @@ config VIDEO_MXC_CAPTURE 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/s5p-tv/Kconfig" diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 094a575bb2f3..e934a36436ec 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -52,6 +52,7 @@ obj-y += davinci/ obj-$(CONFIG_ARCH_OMAP) += omap/ obj-$(CONFIG_VIDEO_MXC_CAPTURE) += mxc/capture/ +obj-$(CONFIG_VIDEO_MXC_CAPTURE) += mxc/subdev/ obj-$(CONFIG_VIDEO_MXC_OUTPUT) += mxc/output/ ccflags-y += -I$(srctree)/drivers/media/i2c diff --git a/drivers/media/platform/mxc/subdev/Kconfig b/drivers/media/platform/mxc/subdev/Kconfig new file mode 100644 index 000000000000..d3fc164e9f33 --- /dev/null +++ b/drivers/media/platform/mxc/subdev/Kconfig @@ -0,0 +1,21 @@ +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. + +endif diff --git a/drivers/media/platform/mxc/subdev/Makefile b/drivers/media/platform/mxc/subdev/Makefile new file mode 100644 index 000000000000..7ace6919fd3f --- /dev/null +++ b/drivers/media/platform/mxc/subdev/Makefile @@ -0,0 +1,4 @@ +#Makefile for mxc csi driver + +obj-$(CONFIG_VIDEO_MXC_CSI_CAMERA) += mx6s_capture.o +obj-$(CONFIG_MXC_VADC) += mxc_vadc.o diff --git a/drivers/media/platform/mxc/subdev/mx6s_capture.c b/drivers/media/platform/mxc/subdev/mx6s_capture.c new file mode 100644 index 000000000000..63b5e14e89b5 --- /dev/null +++ b/drivers/media/platform/mxc/subdev/mx6s_capture.c @@ -0,0 +1,1694 @@ +/* + * Copyright (C) 2014 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/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/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/slab.h> +#include <linux/time.h> +#include <linux/v4l2-mediabus.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 16 + +/* 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_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; + enum v4l2_mbus_pixelcode mbus_code; + int bpp; +}; + +static struct mx6s_fmt formats[] = { + { + .name = "UYVY-16", + .fourcc = V4L2_PIX_FMT_UYVY, + .pixelformat = V4L2_PIX_FMT_UYVY, + .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8, + .bpp = 2, + }, { + .name = "YUYV-16", + .fourcc = V4L2_PIX_FMT_YUYV, + .pixelformat = V4L2_PIX_FMT_YUYV, + .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8, + .bpp = 2, + }, { + .name = "YUV-444 (X-Y-U-V)", + .fourcc = V4L2_PIX_FMT_YUV444, + .pixelformat = V4L2_PIX_FMT_YUV444, + .mbus_code = V4L2_MBUS_FMT_AYUV8_1X32, + .bpp = 4, + } +}; + +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_buffer vb; + struct mx6s_buf_internal internal; +}; + +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 vb2_alloc_ctx *alloc_ctx; + 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; + enum v4l2_mbus_pixelcode 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]; +}; + +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; + } + + printk(KERN_INFO"unknown pixelformat:'%4.4s'\n", (char *)&fourcc); + 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; + 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_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_32bit_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_set_16bit_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 * 2); + __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); +} + +/* + * Videobuf operations + */ +static int mx6s_videobuf_setup(struct vb2_queue *vq, + const struct v4l2_format *fmt, + unsigned int *count, unsigned int *num_planes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + struct mx6s_csi_dev *csi_dev = vb2_get_drv_priv(vq); + + dev_dbg(csi_dev->dev, "count=%d, size=%d\n", *count, sizes[0]); + + /* TODO: support for VIDIOC_CREATE_BUFS not ready */ + if (fmt != NULL) + return -ENOTTY; + + alloc_ctxs[0] = csi_dev->alloc_ctx; + + 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 + */ + 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 mx6s_csi_dev *csi_dev = vb2_get_drv_priv(vb->vb2_queue); + struct mx6s_buffer *buf = container_of(vb, 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 void mx6s_csi_enable(struct mx6s_csi_dev *csi_dev) +{ + struct v4l2_pix_format *pix = &csi_dev->pix; + + csisw_reset(csi_dev); + + if (pix->field == V4L2_FIELD_INTERLACED) + csi_tvdec_enable(csi_dev, true); + + csi_dmareq_rff_enable(csi_dev); + csi_enable_int(csi_dev, 1); + csi_enable(csi_dev, 1); + +} + +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; + + 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_YUV444: + csi_set_32bit_imagpara(csi_dev, pix->width, pix->height); + break; + case V4L2_PIX_FMT_UYVY: + csi_set_16bit_imagpara(csi_dev, pix->width, pix->height); + break; + case V4L2_PIX_FMT_YUYV: + csi_set_16bit_imagpara(csi_dev, pix->width, pix->height); + break; + default: + pr_debug(" case not supported\n"); + return -EINVAL; + } + + 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; + + 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; + + 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 0; +} + +static int 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; + + 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); + vb2_buffer_done(&buf->vb, 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); + + return 0; +} + +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; + 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); + v4l2_get_timestamp(&vb->v4l2_buf.timestamp); + vb->v4l2_buf.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; + + 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 cr1, 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_HRESP_ERR_INT) { + /* 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); + + pr_warning("Hresponse error is detected.\n"); + } + + 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 vb2_queue *q = &csi_dev->vb2_vidq; + int ret = 0; + + file->private_data = csi_dev; + + if (mutex_lock_interruptible(&csi_dev->lock)) + return -ERESTARTSYS; + + csi_dev->alloc_ctx = vb2_dma_contig_init_ctx(csi_dev->dev); + if (IS_ERR(csi_dev->alloc_ctx)) + goto unlock; + + 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_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &csi_dev->lock; + + ret = vb2_queue_init(q); + if (ret < 0) + goto eallocctx; + + mx6s_csi_init(csi_dev); + + mutex_unlock(&csi_dev->lock); + + return ret; +eallocctx: + vb2_dma_contig_cleanup_ctx(csi_dev->alloc_ctx); +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); + + mutex_lock(&csi_dev->lock); + + vb2_queue_release(&csi_dev->vb2_vidq); + + mx6s_csi_deinit(csi_dev); + + vb2_dma_contig_cleanup_ctx(csi_dev->alloc_ctx); + mutex_unlock(&csi_dev->lock); + + file->private_data = NULL; + 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; + + v4l2_subdev_call(sd, video, querystd, a); + return 0; +} + +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; + + v4l2_subdev_call(sd, core, s_std, a); + + return 0; +} + +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; + + v4l2_subdev_call(sd, core, g_std, a); + + return 0; +} + +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); + + WARN_ON(priv != file->private_data); + + return vb2_querybuf(&csi_dev->vb2_vidq, p); +} + +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) +{ + int index = f->index; + + WARN_ON(priv != file->private_data); + + if (f->index > NUM_FORMATS) + return -EINVAL; + + strlcpy(f->description, formats[index].name, sizeof(f->description)); + f->pixelformat = formats[index].fourcc; + 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_mbus_framefmt mbus_fmt; + 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; + } + + v4l2_fill_mbus_format(&mbus_fmt, pix, fmt->mbus_code); + ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mbus_fmt); + v4l2_fill_pix_format(pix, &mbus_fmt); + + 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->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_DRIVER_DESCRIPTION, 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->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + 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); + + mx6s_csi_enable(csi_dev); + + 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); + + mx6s_csi_disable(csi_dev); + + 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; + + v4l2_subdev_call(sd, video, g_parm, a); + return 0; +} + +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; + + v4l2_subdev_call(sd, video, s_parm, a); + + return 0; +} + +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; + + v4l2_subdev_call(sd, video, enum_framesizes, fsize); + 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; + + v4l2_subdev_call(sd, video, enum_frameintervals, 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 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 = v4l2_of_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; + 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; + + 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; + + 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); + return 0; +} + +static const struct of_device_id mx6s_csi_dt_ids[] = { + { .compatible = "fsl,imx6s-csi", }, + { /* 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), + }, + .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); diff --git a/drivers/media/platform/mxc/subdev/mxc_vadc.c b/drivers/media/platform/mxc/subdev/mxc_vadc.c new file mode 100644 index 000000000000..666be710c2c4 --- /dev/null +++ b/drivers/media/platform/mxc/subdev/mxc_vadc.c @@ -0,0 +1,752 @@ +/* + * Copyright (C) 2014 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/regmap.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 tmp; + int idx; + + /* Read auto mode detected result */ + printk(KERN_INFO"wait vadc auto detect video mode...."); + msleep(500); + do { + tmp = reg32_read(VDEC_VIDMOD); + } while (tmp == 0); + + tmp &= (VDEC_VIDMOD_PAL_MASK | VDEC_VIDMOD_M625_MASK); + + if (tmp) + idx = VADC_PAL; + else + idx = VADC_NTSC; + + *std = video_fmts[idx].v4l2_std; + state->fmt = &video_fmts[idx]; + + pr_debug("std=%s\n", video_fmts[idx].name); + return 0; +} + +static int vadc_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, + enum v4l2_mbus_pixelcode *code) +{ + /* support only one format */ + if (index >= 1) + return -EINVAL; + + *code = V4L2_MBUS_FMT_AYUV8_1X32; + return 0; +} + +static int vadc_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + struct vadc_state *state = to_state(sd); + + fmt->code = V4L2_MBUS_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_enum_framesizes(struct v4l2_subdev *sd, + struct v4l2_frmsizeenum *fsize) +{ + struct vadc_state *state = to_state(sd); + if (fsize->index >= 1) + return -EINVAL; + + fsize->discrete.width = state->fmt->active_width; + fsize->discrete.height = state->fmt->active_height; + + return 0; +} +static int vadc_enum_frameintervals(struct v4l2_subdev *sd, + struct v4l2_frmivalenum *fival) +{ + struct vadc_state *state = to_state(sd); + + if (fival->index < 0 || fival->index >= 1) + return -EINVAL; + + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete.numerator = 1; + + fival->discrete.denominator = state->fmt->framerates; + + return 0; +} + +static const struct v4l2_subdev_video_ops vadc_video_ops = { + .querystd = vadc_querystd, + .enum_mbus_fmt = vadc_enum_mbus_fmt, + .try_mbus_fmt = vadc_mbus_fmt, + .g_mbus_fmt = vadc_mbus_fmt, + .enum_framesizes = vadc_enum_framesizes, + .enum_frameintervals = vadc_enum_frameintervals, +}; + +static const struct v4l2_subdev_core_ops vadc_core_ops = { + .g_std = vadc_g_std, +}; + +static const struct v4l2_subdev_ops vadc_ops = { + .core = &vadc_core_ops, + .video = &vadc_video_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); + + /* Init VADC */ + ret = vadc_of_init(pdev); + if (ret < 0) + goto err; + vadc_init(state); + + pr_info("vadc driver loaded\n"); + + return 0; +err: + 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); + + 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 struct platform_driver vadc_driver = { + .driver = { + .name = "fsl_vadc", + .of_match_table = of_match_ptr(fsl_vadc_dt_ids), + }, + .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"); diff --git a/drivers/media/platform/mxc/subdev/mxc_vadc.h b/drivers/media/platform/mxc/subdev/mxc_vadc.h new file mode 100644 index 000000000000..6ed5eeb982d0 --- /dev/null +++ b/drivers/media/platform/mxc/subdev/mxc_vadc.h @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2011-2014 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_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 |