aboutsummaryrefslogtreecommitdiff
path: root/drivers/media/video/s3c_camif.c
diff options
context:
space:
mode:
authorWerner Almesberger <werner@openmoko.org>2009-03-05 14:39:41 +0000
committerAndy Green <agreen@octopus.localdomain>2009-03-05 14:39:41 +0000
commit8cc1cf3520d685d3d5b26b5d489e536c389c9058 (patch)
tree39e44a3f6da5b01099b66b797a5b2fb2a607831b /drivers/media/video/s3c_camif.c
parent9e723603e46500e2c9fbf09ac823de210bf05d31 (diff)
Add Samsung S3C camera driver for V4L
This is the original Samsung S3C camera driver code from the 2.6.21 BSP. The changes that are needed to make this work in 2.6.29 are in the next patch. Signed-off-by: Werner Almesberger <werner@openmoko.org>
Diffstat (limited to 'drivers/media/video/s3c_camif.c')
-rw-r--r--drivers/media/video/s3c_camif.c1863
1 files changed, 1863 insertions, 0 deletions
diff --git a/drivers/media/video/s3c_camif.c b/drivers/media/video/s3c_camif.c
new file mode 100644
index 00000000000..5d41ef9c0e2
--- /dev/null
+++ b/drivers/media/video/s3c_camif.c
@@ -0,0 +1,1863 @@
+/* drivers/media/video/s3c_camif.c
+ *
+ * Copyright (c) 2008 Samsung Electronics
+ *
+ * Samsung S3C Camera driver
+ *
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/wait.h>
+#include <linux/videodev.h>
+#include <asm/io.h>
+#include <asm/semaphore.h>
+#include <asm/hardware.h>
+#include <asm/uaccess.h>
+#include <asm/arch/map.h>
+#include <asm/arch/regs-camif.h>
+#include <asm/arch/regs-gpio.h>
+#include <asm/arch/regs-gpioj.h>
+#include <asm/arch/regs-lcd.h>
+
+#if defined(CONFIG_CPU_S3C2443) || defined(CONFIG_CPU_S3C2450) || defined(CONFIG_CPU_S3C2416)
+#include <asm/arch/regs-irq.h>
+#endif
+
+#include "s3c_camif.h"
+
+static unsigned int irq_old_priority;
+
+/*************************************************************************
+ * Utility part
+ ************************************************************************/
+int s3c_camif_get_frame_num(camif_cfg_t *cfg)
+{
+ int index = 0;
+
+ if (cfg->dma_type & CAMIF_CODEC)
+ index = (readl(cfg->regs + S3C_CICOSTATUS) >> 26) & 0x3;
+ else {
+ assert(cfg->dma_type & CAMIF_PREVIEW);
+ index = (readl(cfg->regs + S3C_CIPRSTATUS) >> 26) & 0x3;
+ }
+
+ cfg->cur_frame_num = (index + 2) % 4; /* When 4 PingPong */
+
+ return 0;
+}
+
+unsigned char* s3c_camif_get_frame(camif_cfg_t *cfg)
+{
+ unsigned char *ret = NULL;
+ int cnt = cfg->cur_frame_num;
+
+ if (cfg->dma_type & CAMIF_PREVIEW)
+ ret = cfg->img_buf[cnt].virt_rgb;
+
+ if (cfg->dma_type & CAMIF_CODEC) {
+ if ((cfg->dst_fmt & CAMIF_RGB16) || (cfg->dst_fmt & CAMIF_RGB24))
+ ret = cfg->img_buf[cnt].virt_rgb;
+ else
+ ret = cfg->img_buf[cnt].virt_y;
+ }
+
+ return ret;
+}
+
+int s3c_camif_get_fifo_status(camif_cfg_t *cfg)
+{
+ unsigned int reg, val, flag;
+
+ if (cfg->dma_type & CAMIF_CODEC) {
+ flag = S3C_CICOSTATUS_OVFIY_CO | S3C_CICOSTATUS_OVFICB_CO | S3C_CICOSTATUS_OVFICR_CO;
+ reg = readl(cfg->regs + S3C_CICOSTATUS);
+
+ if (reg & flag) {
+ /* FIFO Error Count ++ */
+ val = readl(cfg->regs + S3C_CIWDOFST);
+ val |= (S3C_CIWDOFST_CLROVCOFIY | S3C_CIWDOFST_CLROVCOFICB | S3C_CIWDOFST_CLROVCOFICR);
+ writel(val, cfg->regs + S3C_CIWDOFST);
+
+ val = readl(cfg->regs + S3C_CIWDOFST);
+ val &= ~(S3C_CIWDOFST_CLROVCOFIY | S3C_CIWDOFST_CLROVCOFICB | S3C_CIWDOFST_CLROVCOFICR);
+ writel(val, cfg->regs + S3C_CIWDOFST);
+
+ return 1; /* Error */
+ }
+ } else if (cfg->dma_type & CAMIF_PREVIEW) {
+ flag = S3C_CIPRSTATUS_OVFICB_PR | S3C_CIPRSTATUS_OVFICR_PR;
+ reg = readl(cfg->regs + S3C_CIPRSTATUS);
+
+ if (reg & flag) {
+ /* FIFO Error Count ++ */
+ val = readl(cfg->regs + S3C_CIWDOFST);
+ val |= (S3C_CIWDOFST_CLROVPRFICB | S3C_CIWDOFST_CLROVPRFICR);
+ writel(val, cfg->regs + S3C_CIWDOFST);
+
+ val = readl(cfg->regs + S3C_CIWDOFST);
+ val &= ~(S3C_CIWDOFST_CLROVPRFIY | S3C_CIWDOFST_CLROVPRFICB | S3C_CIWDOFST_CLROVPRFICR);
+ writel(val, cfg->regs + S3C_CIWDOFST);
+
+ return 1; /* Error */
+ }
+ }
+
+ return 0;
+}
+
+void s3c_camif_set_polarity(camif_cfg_t *cfg)
+{
+ camif_cis_t *cis = cfg->cis;
+ unsigned int val;
+ unsigned int cmd;
+
+ cmd = readl(cfg->regs + S3C_CIGCTRL);
+ cmd &= ~(0x7 << 24);
+
+ if (cis->polarity_pclk)
+ cmd |= S3C_CIGCTRL_INVPOLPCLK;
+
+ if (cis->polarity_vsync)
+ cmd |= S3C_CIGCTRL_INVPOLVSYNC;
+
+ if (cis->polarity_href)
+ cmd |= S3C_CIGCTRL_INVPOLHREF;
+
+ val = readl(cfg->regs + S3C_CIGCTRL);
+ val |= cmd;
+ writel(val, cfg->regs + S3C_CIGCTRL);
+}
+
+/*************************************************************************
+ * Memory part
+ ************************************************************************/
+static int s3c_camif_request_memory(camif_cfg_t *cfg)
+{
+ unsigned int t_size = 0, i = 0;
+ unsigned int area = 0;
+
+ area = cfg->target_x * cfg->target_y;
+
+ if (cfg->dma_type & CAMIF_CODEC) {
+ if (cfg->dst_fmt & CAMIF_YCBCR420)
+ t_size = (area * 3 / 2); /* CAMIF_YCBCR420 */
+ else if (cfg->dst_fmt & CAMIF_YCBCR422 || cfg->dst_fmt & CAMIF_YCBCR422I)
+ t_size = (area * 2); /* CAMIF_YCBCR422 */
+ else if (cfg->dst_fmt & CAMIF_RGB16)
+ t_size = (area * 2); /* 2 bytes per one pixel */
+ else if (cfg->dst_fmt & CAMIF_RGB24)
+ t_size = (area * 4); /* 4 bytes per one pixel */
+ else
+ printk(KERN_INFO "Invalid target format\n");
+
+ if ((t_size % PAGE_SIZE) != 0) {
+ i = t_size / PAGE_SIZE;
+ t_size = (i + 1) * PAGE_SIZE;
+ }
+
+ t_size = t_size * cfg->pp_num;
+ cfg->pp_totalsize = t_size;
+
+ printk(KERN_INFO "Codec memory required: 0x%08X bytes\n", t_size);
+
+ return 0;
+ }else if (cfg->dma_type & CAMIF_PREVIEW) {
+
+ if (cfg->dst_fmt & CAMIF_RGB16)
+ t_size = (area * 2); /* 2 bytes per two pixel*/
+ else if (cfg->dst_fmt & CAMIF_RGB24)
+ t_size = (area * 4); /* 4 bytes per one pixel */
+ else
+ printk(KERN_ERR "Invalid target format\n");
+
+ if ((t_size % PAGE_SIZE) != 0) {
+ i = t_size / PAGE_SIZE;
+ t_size = (i + 1) * PAGE_SIZE;
+ }
+
+ t_size = t_size * cfg->pp_num;
+ cfg->pp_totalsize = t_size;
+
+ printk(KERN_INFO "Preview memory required: 0x%08X bytes\n", t_size);
+
+ return 0;
+ }
+
+ return 0;
+}
+
+static void s3c_camif_calc_burst_length_yuv422i(unsigned int hsize, unsigned int *mburst, unsigned int *rburst)
+{
+ unsigned int tmp, wanted;
+
+ tmp = (hsize / 2) & 0xf;
+
+ switch (tmp) {
+ case 0:
+ wanted = 16;
+ break;
+
+ case 4:
+ wanted = 4;
+ break;
+
+ case 8:
+ wanted = 8;
+ break;
+
+ default:
+ wanted = 4;
+ break;
+ }
+
+ *mburst = wanted / 2;
+ *rburst = wanted / 2;
+}
+
+static void s3c_camif_calc_burst_length(unsigned int hsize, unsigned int *mburst, unsigned int *rburst)
+{
+ unsigned int tmp;
+
+ tmp = (hsize / 4) & 0xf;
+
+ switch (tmp) {
+ case 0:
+ *mburst = 16;
+ *rburst = 16;
+ break;
+
+ case 4:
+ *mburst = 16;
+ *rburst = 4;
+ break;
+
+ case 8:
+ *mburst = 16;
+ *rburst = 8;
+ break;
+
+ default:
+ tmp = (hsize / 4) % 8;
+
+ if (tmp == 0) {
+ *mburst = 8;
+ *rburst = 8;
+ } else if (tmp == 4) {
+ *mburst = 8;
+ *rburst = 4;
+ } else {
+ tmp = (hsize / 4) % 4;
+ *mburst = 4;
+ *rburst = (tmp) ? tmp : 4;
+ }
+
+ break;
+ }
+}
+
+int s3c_camif_setup_dma(camif_cfg_t *cfg)
+{
+ int width = cfg->target_x;
+ unsigned int val, yburst_m, yburst_r, cburst_m, cburst_r;
+
+ if (cfg->dma_type & CAMIF_CODEC) {
+ if ((cfg->dst_fmt == CAMIF_RGB16) || (cfg->dst_fmt == CAMIF_RGB24)) {
+ if (cfg->dst_fmt == CAMIF_RGB24) {
+ if (width % 2 != 0)
+ return BURST_ERR;
+
+ s3c_camif_calc_burst_length(width * 4, &yburst_m, &yburst_r);
+ } else {
+ if ((width / 2) % 2 != 0)
+ return BURST_ERR;
+
+ s3c_camif_calc_burst_length(width * 2, &yburst_m, &yburst_r);
+ }
+
+ val = readl(cfg->regs + S3C_CICOCTRL);
+ val &= ~(0xfffff << 4);
+
+ if (cfg->dst_fmt == CAMIF_RGB24) {
+ val = S3C_CICOCTRL_YBURST1_CO(yburst_m / 2) | \
+ S3C_CICOCTRL_YBURST2_CO(yburst_r / 4) | (4 << 9) | (2 << 4);
+ } else {
+ val = S3C_CICOCTRL_YBURST1_CO(yburst_m / 2) | \
+ S3C_CICOCTRL_YBURST2_CO(yburst_r / 2) | (4 << 9) | (2 << 4);
+ }
+
+ writel(val, cfg->regs + S3C_CICOCTRL);
+ } else {
+ /* CODEC DMA WIDHT is multiple of 16 */
+ if (width % 16 != 0)
+ return BURST_ERR;
+
+ if (cfg->dst_fmt == CAMIF_YCBCR422I) {
+ s3c_camif_calc_burst_length_yuv422i(width, &yburst_m, &yburst_r);
+ cburst_m = yburst_m / 2;
+ cburst_r = yburst_r / 2;
+ } else {
+ s3c_camif_calc_burst_length(width, &yburst_m, &yburst_r);
+ s3c_camif_calc_burst_length(width / 2, &cburst_m, &cburst_r);
+ }
+
+ val = readl(cfg->regs + S3C_CICOCTRL);
+ val &= ~(0xfffff << 4);
+ val |= (S3C_CICOCTRL_YBURST1_CO(yburst_m) | S3C_CICOCTRL_CBURST1_CO(cburst_m) | \
+ S3C_CICOCTRL_YBURST2_CO(yburst_r) | S3C_CICOCTRL_CBURST2_CO(cburst_r));
+ writel(val, cfg->regs + S3C_CICOCTRL);
+ }
+ } else if (cfg->dma_type & CAMIF_PREVIEW) {
+ if (cfg->dst_fmt == CAMIF_RGB24) {
+ if (width % 2 != 0)
+ return BURST_ERR;
+
+ s3c_camif_calc_burst_length(width * 4, &yburst_m, &yburst_r);
+ } else {
+ if ((width / 2) % 2 != 0)
+ return BURST_ERR;
+
+ s3c_camif_calc_burst_length(width * 2, &yburst_m, &yburst_r);
+ }
+
+ val = readl(cfg->regs + S3C_CIPRCTRL);
+ val &= ~(0x3ff << 14);
+ val |= (S3C_CICOCTRL_YBURST1_CO(yburst_m) | S3C_CICOCTRL_YBURST2_CO(yburst_r));
+ writel(val, cfg->regs + S3C_CIPRCTRL);
+ }
+
+ return 0;
+}
+
+/*************************************************************************
+ * Input path part
+ ************************************************************************/
+/*
+ * 2443 MSDMA (Preview Only)
+ */
+#if defined(CONFIG_CPU_S3C2443) || defined(CONFIG_CPU_S3C2450) || defined(CONFIG_CPU_S3C2416)
+int s3c_camif_input_msdma_preview(camif_cfg_t * cfg)
+{
+ int ret = 0;
+ unsigned int addr_start_Y = 0, addr_start_CB = 0, addr_start_CR = 0;
+ unsigned int addr_end_Y = 0, addr_end_CB = 0, addr_end_CR = 0;
+ unsigned int val, val_width;
+
+ val = readl(cfg->regs + S3C_CIMSCTRL);
+ val &= ~(1 << 2);
+ writel(val, cfg->regs + S3C_CIMSCTRL);
+
+ val = readl(cfg->regs + S3C_CIMSCTRL);
+ val |= (1 << 2);
+ writel(val, cfg->regs + S3C_CIMSCTRL);
+
+ if (cfg->src_fmt != CAMIF_YCBCR420 && cfg->src_fmt != CAMIF_YCBCR422 && cfg->src_fmt != CAMIF_YCBCR422I)
+ cfg->src_fmt = CAMIF_YCBCR420;
+
+ switch(cfg->src_fmt) {
+ case CAMIF_YCBCR420:
+ val = readl(cfg->regs + S3C_CIMSCTRL);
+ val = (val & ~(0x1 << 1)) | (0x1 << 1);
+ writel(val, cfg->regs + S3C_CIMSCTRL);
+
+ addr_start_Y = readl(cfg->regs + S3C_CIMSYSA);
+ addr_start_CB = addr_start_Y + (cfg->cis->source_x * cfg->cis->source_y);
+ addr_start_CR = addr_start_CB + (cfg->cis->source_x * cfg->cis->source_y / 4);
+
+ addr_end_Y = addr_start_Y + (cfg->cis->source_x * cfg->cis->source_y);
+ addr_end_CB = addr_start_CB + (cfg->cis->source_x * cfg->cis->source_y / 4);
+ addr_end_CR = addr_start_CR + (cfg->cis->source_x * cfg->cis->source_y / 4);
+ break;
+
+ case CAMIF_YCBCR422:
+ case CAMIF_YCBCR422I:
+ val = readl(cfg->regs + S3C_CIMSCTRL);
+ val = (val & ~(0x1 << 5)) | (0x1 << 5); /* Interleave_MS */
+ val &= ~(0x1 << 1);
+ val &= ~(0x3 << 3); /* YCbYCr */
+ writel(val, cfg->regs + S3C_CIMSCTRL);
+
+ addr_start_Y = readl(cfg->regs + S3C_CIMSYSA);
+ addr_start_CB = addr_start_Y + (cfg->cis->source_x * cfg->cis->source_y);
+ addr_start_CR = addr_start_CB + (cfg->cis->source_x * cfg->cis->source_y / 2);
+
+ addr_end_Y = addr_start_Y + (cfg->cis->source_x * cfg->cis->source_y);
+ addr_end_CB = addr_start_CB + (cfg->cis->source_x * cfg->cis->source_y / 2);
+ addr_end_CR = addr_start_CR + (cfg->cis->source_x * cfg->cis->source_y / 2);
+ break;
+
+ default:
+ break;
+ }
+
+ /* MSDMA memory */
+ writel(addr_start_Y, cfg->regs + S3C_CIMSYSA);
+ writel(addr_start_CB, cfg->regs + S3C_CIMSCBSA);
+ writel(addr_start_CR, cfg->regs + S3C_CIMSCRSA);
+
+ writel(addr_end_Y, cfg->regs + S3C_CIMSYEND);
+ writel(addr_end_CB, cfg->regs + S3C_CIMSCBEND);
+ writel(addr_end_CR, cfg->regs + S3C_CIMSCREND);
+
+ /* MSDMA memory offset - default : 0 */
+ writel(0, cfg->regs + S3C_CIMSYOFF);
+ writel(0, cfg->regs + S3C_CIMSCBOFF);
+ writel(0, cfg->regs + S3C_CIMSCROFF);
+
+ /* MSDMA for codec source image width */
+ val_width = readl(cfg->regs + S3C_CIMSWIDTH);
+ val_width = (val_width & ~(0x1 << 31)); /* AutoLoadDisable */
+ val_width |= (cfg->cis->source_y << 16); /* MSCOHEIGHT */
+ val_width |= cfg->cis->source_x; /* MSCOWIDTH */
+ val_width = cfg->cis->source_x;
+ writel(val_width, cfg->regs + S3C_CIMSWIDTH);
+
+ return ret;
+}
+
+static int s3c_camif_input_msdma(camif_cfg_t *cfg)
+{
+ if (cfg->input_channel == MSDMA_FROM_PREVIEW)
+ s3c_camif_input_msdma_preview(cfg);
+
+ return 0;
+}
+
+/*
+ * 6400 MSDMA (Preview & Codec)
+ */
+#elif defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410)
+int s3c_camif_input_msdma_codec(camif_cfg_t * cfg)
+{
+ int ret = 0;
+ u32 addr_start_Y = 0, addr_start_CB = 0, addr_start_CR = 0;
+ u32 addr_end_Y = 0, addr_end_CB = 0, addr_end_CR = 0;
+ u32 val, val_width;
+
+ /* Codec path input data selection */
+ val = readl(cfg->regs + S3C_MSCOCTRL);
+ val &= ~(1 << 3);
+ writel(val, cfg->regs + S3C_MSCOCTRL);
+
+ val = readl(cfg->regs + S3C_MSCOCTRL);
+ val |= (1 << 3);
+ writel(val, cfg->regs + S3C_MSCOCTRL);
+
+ if (cfg->src_fmt != CAMIF_YCBCR420 && cfg->src_fmt != CAMIF_YCBCR422 && cfg->src_fmt != CAMIF_YCBCR422I)
+ cfg->src_fmt = CAMIF_YCBCR420;
+
+ switch(cfg->src_fmt) {
+ case CAMIF_YCBCR420:
+ val = readl(cfg->regs + S3C_MSCOCTRL);
+ val &= ~(0x3 << 1);
+ writel(val, cfg->regs + S3C_MSCOCTRL);
+
+ addr_start_Y = cfg->pp_phys_buf;
+ addr_start_CB = addr_start_Y + (cfg->cis->source_x * cfg->cis->source_y);
+ addr_start_CR = addr_start_CB + (cfg->cis->source_x * cfg->cis->source_y / 4);
+
+ addr_end_Y = addr_start_Y + (cfg->cis->source_x * cfg->cis->source_y);
+ addr_end_CB = addr_start_CB + (cfg->cis->source_x * cfg->cis->source_y / 4);
+ addr_end_CR = addr_start_CR + (cfg->cis->source_x * cfg->cis->source_y / 4);
+ break;
+
+ case CAMIF_YCBCR422:
+ case CAMIF_YCBCR422I:
+ val = readl(cfg->regs + S3C_MSCOCTRL);
+ val = (val & ~(0x3 << 1)) |(0x2 << 1);
+ writel(val, cfg->regs + S3C_MSCOCTRL);
+
+ addr_start_Y = cfg->pp_phys_buf;
+ addr_start_CB = addr_start_Y + (cfg->cis->source_x * cfg->cis->source_y);
+ addr_start_CR = addr_start_CB + (cfg->cis->source_x * cfg->cis->source_y / 2);
+
+ addr_end_Y = addr_start_Y + (cfg->cis->source_x * cfg->cis->source_y);
+ addr_end_CB = addr_start_CB + (cfg->cis->source_x * cfg->cis->source_y / 2);
+ addr_end_CR = addr_start_CR + (cfg->cis->source_x * cfg->cis->source_y / 2);
+ break;
+
+ default:
+ break;
+ }
+
+ /* MSDMA memory */
+ writel(addr_start_Y, cfg->regs + S3C_MSCOY0SA);
+ writel(addr_start_CB, cfg->regs + S3C_MSCOCB0SA);
+ writel(addr_start_CR, cfg->regs + S3C_MSCOCR0SA);
+
+ writel(addr_end_Y, cfg->regs + S3C_MSCOY0END);
+ writel(addr_end_CB, cfg->regs + S3C_MSCOCB0END);
+ writel(addr_end_CR, cfg->regs + S3C_MSCOCR0END);
+
+ /* MSDMA memory offset */
+ writel(0, cfg->regs + S3C_MSCOYOFF);
+ writel(0, cfg->regs + S3C_MSCOCBOFF);
+ writel(0, cfg->regs + S3C_MSCOCROFF);
+
+ /* MSDMA for codec source image width */
+ val_width = readl(cfg->regs + S3C_MSCOWIDTH);
+ val_width = (val_width & ~(0x1 << 31))|(0x1 << 31); /* AutoLoadEnable */
+ val_width |= (cfg->cis->source_y << 16); /* MSCOHEIGHT */
+ val_width |= cfg->cis->source_x; /* MSCOWIDTH */
+ writel(val_width, cfg->regs + S3C_MSCOWIDTH);
+
+ return ret;
+}
+
+int s3c_camif_input_msdma_preview(camif_cfg_t * cfg)
+{
+ int ret = 0;
+ unsigned int addr_start_Y = 0, addr_start_CB = 0, addr_start_CR = 0;
+ unsigned int addr_end_Y = 0, addr_end_CB = 0, addr_end_CR = 0;
+ unsigned int val, val_width;
+
+ val = readl(cfg->regs + S3C_CIMSCTRL);
+ val &= ~(0x1 << 3);
+ writel(val, cfg->regs + S3C_CIMSCTRL);
+
+ val = readl(cfg->regs + S3C_CIMSCTRL);
+ val |= (0x1 << 3);
+ writel(val, cfg->regs + S3C_CIMSCTRL);
+
+ if (cfg->src_fmt != CAMIF_YCBCR420 && cfg->src_fmt != CAMIF_YCBCR422 && cfg->src_fmt != CAMIF_YCBCR422I)
+ cfg->src_fmt = CAMIF_YCBCR420;
+
+ switch(cfg->src_fmt) {
+ case CAMIF_YCBCR420:
+ val = readl(cfg->regs + S3C_CIMSCTRL);
+ val &= ~(0x3 << 1);
+ writel(val, cfg->regs + S3C_CIMSCTRL);
+
+ addr_start_Y = readl(cfg->regs + S3C_MSPRY0SA);
+ addr_start_CB = addr_start_Y + (cfg->cis->source_x * cfg->cis->source_y);
+ addr_start_CR = addr_start_CB + (cfg->cis->source_x * cfg->cis->source_y / 4);
+
+ addr_end_Y = addr_start_Y + (cfg->cis->source_x * cfg->cis->source_y);
+ addr_end_CB = addr_start_CB + (cfg->cis->source_x * cfg->cis->source_y / 4);
+ addr_end_CR = addr_start_CR + (cfg->cis->source_x * cfg->cis->source_y / 4);
+ break;
+
+ case CAMIF_YCBCR422:
+ case CAMIF_YCBCR422I:
+ val = readl(cfg->regs + S3C_CIMSCTRL);
+ val = (val & ~(0x3 << 1)) | (0x2 << 1); /* YCbCr 422 Interleave */
+ val = (val & ~(0x3 << 4)) | (0x3 << 4); /* YCbYCr */
+ writel(val, cfg->regs + S3C_CIMSCTRL);
+
+ addr_start_Y = readl(cfg->regs + S3C_MSPRY0SA);
+ addr_start_CB = addr_start_Y + (cfg->cis->source_x * cfg->cis->source_y);
+ addr_start_CR = addr_start_CB + (cfg->cis->source_x * cfg->cis->source_y / 2);
+
+ addr_end_Y = addr_start_Y + (cfg->cis->source_x * cfg->cis->source_y);
+ addr_end_CB = addr_start_CB + (cfg->cis->source_x * cfg->cis->source_y / 2);
+ addr_end_CR = addr_start_CR + (cfg->cis->source_x * cfg->cis->source_y / 2);
+ break;
+
+ default:
+ break;
+ }
+
+ /* MSDMA memory */
+ writel(addr_start_Y, cfg->regs + S3C_MSPRY0SA);
+ writel(addr_start_CB, cfg->regs + S3C_MSPRCB0SA);
+ writel(addr_start_CR, cfg->regs + S3C_MSPRCR0SA);
+
+ writel(addr_end_Y, cfg->regs + S3C_MSPRY0END);
+ writel(addr_end_CB, cfg->regs + S3C_MSPRCB0END);
+ writel(addr_end_CR, cfg->regs + S3C_MSPRCR0END);
+
+ /* MSDMA memory offset */
+ writel(0, cfg->regs + S3C_MSPRYOFF);
+ writel(0, cfg->regs + S3C_MSPRCBOFF);
+ writel(0, cfg->regs + S3C_MSPRCROFF);
+
+ /* MSDMA for codec source image width */
+ val_width = readl(cfg->regs + S3C_MSPRWIDTH);
+ val_width = (val_width & ~(0x1 << 31)); /* AutoLoadEnable */
+ val_width |= (cfg->cis->source_y << 16); /* MSCOHEIGHT */
+ val_width |= cfg->cis->source_x; /* MSCOWIDTH */
+ writel(val_width, cfg->regs + S3C_MSPRWIDTH);
+
+ return ret;
+}
+
+static int s3c_camif_input_msdma(camif_cfg_t *cfg)
+{
+ if (cfg->input_channel == MSDMA_FROM_PREVIEW)
+ s3c_camif_input_msdma_preview(cfg);
+ else if (cfg->input_channel == MSDMA_FROM_CODEC)
+ s3c_camif_input_msdma_codec(cfg);
+
+ return 0;
+}
+#endif
+
+static int s3c_camif_input_camera(camif_cfg_t *cfg)
+{
+ unsigned int val;
+
+ s3c_camif_set_offset(cfg->cis);
+
+ if (cfg->dma_type & CAMIF_CODEC) {
+#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410)
+ val = readl(cfg->regs + S3C_MSCOCTRL);
+ val &= ~(1 << 3);
+ writel(val, cfg->regs + S3C_MSCOCTRL);
+#endif
+ } else if (cfg->dma_type & CAMIF_PREVIEW) {
+#if defined(CONFIG_CPU_S3C2443) || defined(CONFIG_CPU_S3C2450)
+ val = readl(cfg->regs + S3C_CIMSCTRL);
+ val &= ~(1 << 2);
+ writel(val, cfg->regs + S3C_CIMSCTRL);
+#elif defined(CONFIG_CPU_S3C6400)
+ val = readl(cfg->regs + S3C_CIMSCTRL);
+ val &= ~(1 << 3);
+ writel(val, cfg->regs + S3C_CIMSCTRL);
+#endif
+ } else
+ printk(KERN_ERR "Invalid DMA type\n");
+
+ return 0;
+}
+
+static int s3c_camif_setup_input_path(camif_cfg_t *cfg)
+{
+ if (cfg->input_channel == CAMERA_INPUT)
+ s3c_camif_input_camera(cfg);
+ else
+ s3c_camif_input_msdma(cfg);
+
+ return 0;
+}
+
+/*************************************************************************
+ * Output path part
+ ************************************************************************/
+static int s3c_camif_output_pp_codec_rgb(camif_cfg_t *cfg)
+{
+ int i;
+ unsigned int val;
+ unsigned int area = cfg->target_x * cfg->target_y;
+
+ if (cfg->dst_fmt & CAMIF_RGB24)
+ area = area * 4;
+ else {
+ assert (cfg->dst_fmt & CAMIF_RGB16);
+ area = area * 2;
+ }
+
+ if ((area % PAGE_SIZE) != 0) {
+ i = area / PAGE_SIZE;
+ area = (i + 1) * PAGE_SIZE;
+ }
+
+ cfg->buffer_size = area;
+
+ if (cfg->input_channel == MSDMA_FROM_CODEC) {
+ val = readl(S3C_VIDW00ADD0B0);
+
+ for (i = 0; i < 4; i++)
+ writel(val, cfg->regs + S3C_CICOYSA(i));
+ } else {
+ switch ( cfg->pp_num ) {
+ case 1:
+ for (i = 0; i < 4; i++) {
+ cfg->img_buf[i].virt_rgb = cfg->pp_virt_buf;
+ cfg->img_buf[i].phys_rgb = cfg->pp_phys_buf;
+ writel(cfg->img_buf[i].phys_rgb, cfg->regs + S3C_CICOYSA(i));
+ }
+
+ break;
+
+ case 2:
+ for (i = 0; i < 4; i++) {
+ if (i == 0 || i == 2) {
+ cfg->img_buf[i].virt_rgb = cfg->pp_virt_buf;
+ cfg->img_buf[i].phys_rgb = cfg->pp_phys_buf;
+ } else {
+ cfg->img_buf[i].virt_rgb = cfg->pp_virt_buf + area;
+ cfg->img_buf[i].phys_rgb = cfg->pp_phys_buf + area;
+ }
+
+ writel(cfg->img_buf[i].phys_rgb, cfg->regs + S3C_CICOYSA(i));
+ }
+
+ break;
+
+ case 4:
+ for (i = 0; i < 4; i++) {
+ cfg->img_buf[i].virt_rgb = cfg->pp_virt_buf + i * area;
+ cfg->img_buf[i].phys_rgb = cfg->pp_phys_buf + i * area;
+ writel(cfg->img_buf[i].phys_rgb, cfg->regs + S3C_CICOYSA(i));
+ }
+
+ break;
+
+ default:
+ printk(KERN_ERR "Invalid pingpong number %d\n", cfg->pp_num);
+ panic("s3c camif halt\n");
+ }
+ }
+
+ return 0;
+}
+
+static int s3c_camif_output_pp_codec(camif_cfg_t *cfg)
+{
+ unsigned int i, cbcr_size = 0;
+ unsigned int area = cfg->target_x * cfg->target_y;
+ unsigned int one_p_size;
+
+ area = cfg->target_x * cfg->target_y;
+
+ if (cfg->dst_fmt & CAMIF_YCBCR420)
+ cbcr_size = area / 4;
+ else if (cfg->dst_fmt & CAMIF_YCBCR422 || cfg->dst_fmt & CAMIF_YCBCR422I)
+ cbcr_size = area / 2;
+ else if ((cfg->dst_fmt & CAMIF_RGB16) || (cfg->dst_fmt & CAMIF_RGB24)) {
+ s3c_camif_output_pp_codec_rgb(cfg);
+ return 0;
+ } else
+ printk(KERN_ERR "Invalid target format %d\n", cfg->dst_fmt);
+
+ one_p_size = area + (2 * cbcr_size);
+
+ if ((one_p_size % PAGE_SIZE) != 0) {
+ i = one_p_size / PAGE_SIZE;
+ one_p_size = (i + 1) * PAGE_SIZE;
+ }
+
+ cfg->buffer_size = one_p_size;
+
+ switch (cfg->pp_num) {
+ case 1 :
+ for (i = 0; i < 4; i++) {
+ cfg->img_buf[i].virt_y = cfg->pp_virt_buf;
+ cfg->img_buf[i].phys_y = cfg->pp_phys_buf;
+ cfg->img_buf[i].virt_cb = cfg->pp_virt_buf + area;
+ cfg->img_buf[i].phys_cb = cfg->pp_phys_buf + area;
+ cfg->img_buf[i].virt_cr = cfg->pp_virt_buf + area + cbcr_size;
+ cfg->img_buf[i].phys_cr = cfg->pp_phys_buf + area + cbcr_size;
+ writel(cfg->img_buf[i].phys_y, cfg->regs + S3C_CICOYSA(i));
+ writel(cfg->img_buf[i].phys_cb, cfg->regs + S3C_CICOCBSA(i));
+ writel(cfg->img_buf[i].phys_cr, cfg->regs + S3C_CICOCRSA(i));
+ }
+
+ break;
+
+ case 2:
+ for (i = 0; i < 4; i++) {
+ if (i == 0 || i == 2) {
+ cfg->img_buf[i].virt_y = cfg->pp_virt_buf;
+ cfg->img_buf[i].phys_y = cfg->pp_phys_buf;
+ cfg->img_buf[i].virt_cb = cfg->pp_virt_buf + area;
+ cfg->img_buf[i].phys_cb = cfg->pp_phys_buf + area;
+ cfg->img_buf[i].virt_cr = cfg->pp_virt_buf + area + cbcr_size;
+ cfg->img_buf[i].phys_cr = cfg->pp_phys_buf + area + cbcr_size;
+ } else {
+ cfg->img_buf[i].virt_y = cfg->pp_virt_buf + one_p_size;
+ cfg->img_buf[i].phys_y = cfg->pp_phys_buf + one_p_size;
+ cfg->img_buf[i].virt_cb = cfg->pp_virt_buf + area + one_p_size;
+ cfg->img_buf[i].phys_cb = cfg->pp_phys_buf + area + one_p_size;
+ cfg->img_buf[i].virt_cr = cfg->pp_virt_buf + area + cbcr_size + one_p_size;
+ cfg->img_buf[i].phys_cr = cfg->pp_phys_buf + area + cbcr_size + one_p_size;
+ }
+
+ writel(cfg->img_buf[i].phys_y, cfg->regs + S3C_CICOYSA(i));
+ writel(cfg->img_buf[i].phys_cb, cfg->regs + S3C_CICOCBSA(i));
+ writel(cfg->img_buf[i].phys_cr, cfg->regs + S3C_CICOCRSA(i));
+ }
+
+ break;
+
+ case 4:
+ for (i = 0; i < 4; i++) {
+ cfg->img_buf[i].virt_y = cfg->pp_virt_buf + i * one_p_size;
+ cfg->img_buf[i].phys_y = cfg->pp_phys_buf + i * one_p_size;
+ cfg->img_buf[i].virt_cb = cfg->pp_virt_buf + area + i * one_p_size;
+ cfg->img_buf[i].phys_cb = cfg->pp_phys_buf + area + i * one_p_size;
+ cfg->img_buf[i].virt_cr = cfg->pp_virt_buf + area + cbcr_size + i * one_p_size;
+ cfg->img_buf[i].phys_cr = cfg->pp_phys_buf + area + cbcr_size + i * one_p_size;
+ writel(cfg->img_buf[i].phys_y, cfg->regs + S3C_CICOYSA(i));
+ writel(cfg->img_buf[i].phys_cb, cfg->regs + S3C_CICOCBSA(i));
+ writel(cfg->img_buf[i].phys_cr, cfg->regs + S3C_CICOCRSA(i));
+ }
+
+ break;
+
+ default:
+ printk(KERN_ERR "Invalid pingpong number %d\n", cfg->pp_num);
+ }
+
+ return 0;
+}
+
+#if defined(CONFIG_CPU_S3C2443) || defined(CONFIG_CPU_S3C2450) || defined(CONFIG_CPU_S3C2416)
+static int s3c_camif_io_duplex_preview(camif_cfg_t *cfg)
+{
+ unsigned int cbcr_size = 0;
+ unsigned int area = cfg->cis->source_x * cfg->cis->source_y;
+ unsigned int val;
+ int i;
+
+ val = readl(S3C_VIDW01ADD0);
+
+ if (!((cfg->dst_fmt & CAMIF_RGB16) || (cfg->dst_fmt & CAMIF_RGB24)))
+ printk(KERN_ERR "Invalid target format\n");
+
+ for (i = 0; i < 4; i++)
+ writel(val, cfg->regs + S3C_CIPRYSA(i));
+
+ if (cfg->src_fmt & CAMIF_YCBCR420) {
+ cbcr_size = area / 4;
+ cfg->img_buf[0].virt_cb = cfg->pp_virt_buf + area;
+ cfg->img_buf[0].phys_cb = cfg->pp_phys_buf + area;
+ cfg->img_buf[0].virt_cr = cfg->pp_virt_buf + area + cbcr_size;
+ cfg->img_buf[0].phys_cr = cfg->pp_phys_buf + area + cbcr_size;
+ } else if (cfg->src_fmt & CAMIF_YCBCR422 || cfg->dst_fmt & CAMIF_YCBCR422I) {
+ area = area * 2;
+ cfg->img_buf[0].virt_cb = 0;
+ cfg->img_buf[0].phys_cb = 0;
+ cfg->img_buf[0].virt_cr = 0;
+ cfg->img_buf[0].phys_cr = 0;
+ }
+
+ cfg->img_buf[0].virt_y = cfg->pp_virt_buf;
+ cfg->img_buf[0].phys_y = cfg->pp_phys_buf;
+
+ writel(cfg->img_buf[0].phys_y, cfg->regs + S3C_CIMSYSA);
+ writel(cfg->img_buf[0].phys_y + area, cfg->regs + S3C_CIMSYEND);
+
+ writel(cfg->img_buf[0].phys_cb, cfg->regs + S3C_CIMSCBSA);
+ writel(cfg->img_buf[0].phys_cb + cbcr_size, cfg->regs + S3C_CIMSCBEND);
+
+ writel(cfg->img_buf[0].phys_cr, cfg->regs + S3C_CIMSCRSA);
+ writel(cfg->img_buf[0].phys_cr + cbcr_size, cfg->regs + S3C_CIMSCREND);
+
+ writel(cfg->cis->source_x, cfg->regs + S3C_CIMSWIDTH);
+
+ return 0;
+}
+#elif defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410)
+static int s3c_camif_io_duplex_preview(camif_cfg_t *cfg)
+{
+ unsigned int cbcr_size = 0;
+ unsigned int area = cfg->cis->source_x * cfg->cis->source_y;
+ unsigned int val;
+ int i;
+
+ val = readl(S3C_VIDW01ADD0B0);
+
+ if (!((cfg->dst_fmt & CAMIF_RGB16) || (cfg->dst_fmt & CAMIF_RGB24)))
+ printk(KERN_ERR "Invalid target format\n");
+
+ for (i = 0; i < 4; i++)
+ writel(val, cfg->regs + S3C_CIPRYSA(i));
+
+ if (cfg->src_fmt & CAMIF_YCBCR420) {
+ cbcr_size = area / 4;
+ cfg->img_buf[0].virt_cb = cfg->pp_virt_buf + area;
+ cfg->img_buf[0].phys_cb = cfg->pp_phys_buf + area;
+ cfg->img_buf[0].virt_cr = cfg->pp_virt_buf + area + cbcr_size;
+ cfg->img_buf[0].phys_cr = cfg->pp_phys_buf + area + cbcr_size;
+ } else if (cfg->src_fmt & CAMIF_YCBCR422 || cfg->dst_fmt & CAMIF_YCBCR422I){
+ area = area * 2;
+ cfg->img_buf[0].virt_cb = 0;
+ cfg->img_buf[0].phys_cb = 0;
+ cfg->img_buf[0].virt_cr = 0;
+ cfg->img_buf[0].phys_cr = 0;
+ }
+
+ cfg->img_buf[0].virt_y = cfg->pp_virt_buf;
+ cfg->img_buf[0].phys_y = cfg->pp_phys_buf;
+
+ writel(cfg->img_buf[0].phys_y, cfg->regs + S3C_MSPRY0SA);
+ writel(cfg->img_buf[0].phys_y + area, cfg->regs + S3C_MSPRY0END);
+
+ writel(cfg->img_buf[0].phys_cb, cfg->regs + S3C_MSPRCB0SA);
+ writel(cfg->img_buf[0].phys_cb + cbcr_size, cfg->regs + S3C_MSPRCB0END);
+
+ writel(cfg->img_buf[0].phys_cr, cfg->regs + S3C_MSPRCR0SA);
+ writel(cfg->img_buf[0].phys_cr + cbcr_size, cfg->regs + S3C_MSPRCR0END);
+
+ val = readl(cfg->regs + S3C_MSCOWIDTH);
+ val = (val & ~(0x1 << 31)) | (0x1 << 31);
+ val |= (cfg->cis->source_y << 16);
+ val |= cfg->cis->source_x;
+ writel(val, cfg->regs + S3C_MSPRWIDTH);
+
+ return 0;
+}
+#endif
+
+static int s3c_camif_output_pp_preview(camif_cfg_t *cfg)
+{
+ int i;
+ unsigned int cbcr_size = 0;
+ unsigned int area = cfg->target_x * cfg->target_y;
+
+ if (cfg->input_channel) {
+ s3c_camif_io_duplex_preview(cfg);
+ return 0;
+ }
+
+ if (cfg->dst_fmt & CAMIF_YCBCR420)
+ cbcr_size = area / 4;
+ else if (cfg->dst_fmt & CAMIF_YCBCR422 || cfg->dst_fmt & CAMIF_YCBCR422I)
+ cbcr_size = area / 2;
+ else if (cfg->dst_fmt & CAMIF_RGB24)
+ area = area * 4;
+ else if (cfg->dst_fmt & CAMIF_RGB16)
+ area = area * 2;
+ else
+ printk(KERN_ERR "Invalid target format %d\n", cfg->dst_fmt);
+
+ if ((area % PAGE_SIZE) != 0) {
+ i = area / PAGE_SIZE;
+ area = (i + 1) * PAGE_SIZE;
+ }
+
+ cfg->buffer_size = area;
+
+ switch (cfg->pp_num) {
+ case 1:
+ for (i = 0; i < 4; i++) {
+ cfg->img_buf[i].virt_rgb = cfg->pp_virt_buf;
+ cfg->img_buf[i].phys_rgb = cfg->pp_phys_buf;
+ writel(cfg->img_buf[i].phys_rgb, cfg->regs + S3C_CIPRYSA(i));
+ }
+
+ break;
+
+ case 2:
+ for (i = 0; i < 4; i++) {
+ if (i == 0 || i == 2) {
+ cfg->img_buf[i].virt_rgb = cfg->pp_virt_buf;
+ cfg->img_buf[i].phys_rgb = cfg->pp_phys_buf;
+ } else {
+ cfg->img_buf[i].virt_rgb = cfg->pp_virt_buf + area;
+ cfg->img_buf[i].phys_rgb = cfg->pp_phys_buf + area;
+ }
+
+ writel(cfg->img_buf[i].phys_rgb, cfg->regs + S3C_CIPRYSA(i));
+ }
+
+ break;
+
+ case 4:
+ for (i = 0; i < 4; i++) {
+ cfg->img_buf[i].virt_rgb = cfg->pp_virt_buf + i * area;
+ cfg->img_buf[i].phys_rgb = cfg->pp_phys_buf + i * area;
+ writel(cfg->img_buf[i].phys_rgb, cfg->regs + S3C_CIPRYSA(i));
+ }
+
+ break;
+
+ default:
+ printk(KERN_ERR "Invalid pingpong number %d\n", cfg->pp_num);
+ }
+
+ return 0;
+}
+
+static int s3c_camif_output_pp(camif_cfg_t *cfg)
+{
+ if (cfg->dma_type & CAMIF_CODEC)
+ s3c_camif_output_pp_codec(cfg);
+ else if ( cfg->dma_type & CAMIF_PREVIEW)
+ s3c_camif_output_pp_preview(cfg);
+
+ return 0;
+}
+
+static int s3c_camif_output_lcd(camif_cfg_t *cfg)
+{
+ /* To Be Implemented */
+ return 0;
+}
+
+static int s3c_camif_setup_output_path(camif_cfg_t *cfg)
+{
+ if (cfg->output_channel == CAMIF_OUT_FIFO)
+ s3c_camif_output_lcd(cfg);
+ else
+ s3c_camif_output_pp(cfg);
+
+ return 0;
+}
+
+/*************************************************************************
+ * Scaler part
+ ************************************************************************/
+static int s3c_camif_set_target_area(camif_cfg_t *cfg)
+{
+ unsigned int rect = cfg->target_x * cfg->target_y;
+
+ if (cfg->dma_type & CAMIF_CODEC)
+ writel(rect, cfg->regs + S3C_CICOTAREA);
+ else if (cfg->dma_type & CAMIF_PREVIEW)
+ writel(rect, cfg->regs + S3C_CIPRTAREA);
+
+ return 0;
+}
+
+#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410)
+static inline int s3c_camif_set_ratio(camif_cfg_t *cfg)
+{
+ unsigned int cmd = (S3C_CICOSCCTRL_CSCR2Y_WIDE | S3C_CICOSCCTRL_CSCY2R_WIDE);
+
+ if (cfg->dma_type & CAMIF_CODEC) {
+
+ writel(S3C_CICOSCPRERATIO_SHFACTOR_CO(cfg->sc.shfactor) | \
+ S3C_CICOSCPRERATIO_PREHORRATIO_CO(cfg->sc.prehratio) | \
+ S3C_CICOSCPRERATIO_PREVERRATIO_CO(cfg->sc.prevratio), cfg->regs + S3C_CICOSCPRERATIO);
+
+ writel(S3C_CICOSCPREDST_PREDSTWIDTH_CO(cfg->sc.predst_x) | \
+ S3C_CICOSCPREDST_PREDSTHEIGHT_CO(cfg->sc.predst_y), cfg->regs + S3C_CICOSCPREDST);
+
+ /* Differ from Preview */
+ if (cfg->sc.scalerbypass)
+ cmd |= S3C_CICOSCCTRL_SCALERBYPASS_CO;
+
+ /* Differ from Codec */
+ if (cfg->dst_fmt & CAMIF_RGB24)
+ cmd |= S3C_CICOSCCTRL_OUTRGB_FMT_RGB888;
+ else
+ cmd |= S3C_CICOSCCTRL_OUTRGB_FMT_RGB565;
+
+ if (cfg->sc.scaleup_h & cfg->sc.scaleup_v)
+ cmd |= (S3C_CICOSCCTRL_SCALEUP_H | S3C_CICOSCCTRL_SCALEUP_V);
+
+ writel(cmd | S3C_CICOSCCTRL_MAINHORRATIO_CO(cfg->sc.mainhratio) | \
+ S3C_CICOSCCTRL_MAINVERRATIO_CO(cfg->sc.mainvratio), cfg->regs + S3C_CICOSCCTRL);
+
+ } else if (cfg->dma_type & CAMIF_PREVIEW) {
+
+ writel(S3C_CIPRSCPRERATIO_SHFACTOR_PR(cfg->sc.shfactor) | \
+ S3C_CIPRSCPRERATIO_PREHORRATIO_PR(cfg->sc.prehratio) | \
+ S3C_CIPRSCPRERATIO_PREVERRATIO_PR(cfg->sc.prevratio), cfg->regs + S3C_CIPRSCPRERATIO);
+
+ writel(S3C_CIPRSCPREDST_PREDSTWIDTH_PR(cfg->sc.predst_x) | \
+ S3C_CIPRSCPREDST_PREDSTHEIGHT_PR(cfg->sc.predst_y), cfg->regs + S3C_CIPRSCPREDST);
+
+ if (cfg->dst_fmt & CAMIF_RGB24)
+ cmd |= S3C_CIPRSCCTRL_OUTRGB_FMT_PR_RGB888;
+ else
+ cmd |= S3C_CIPRSCCTRL_OUTRGB_FMT_PR_RGB565;
+
+ if (cfg->sc.scaleup_h & cfg->sc.scaleup_v)
+ cmd |= ((1 << 30) | (1 << 29));
+
+ writel(cmd | S3C_CIPRSCCTRL_MAINHORRATIO_PR(cfg->sc.mainhratio) | \
+ S3C_CIPRSCCTRL_MAINVERRATIO_PR(cfg->sc.mainvratio), cfg->regs + S3C_CIPRSCCTRL);
+
+ } else
+ printk(KERN_ERR "Invalid DMA type\n");
+
+ return 0;
+}
+#elif defined(CONFIG_CPU_S3C2443) || defined(CONFIG_CPU_S3C2450) || defined(CONFIG_CPU_S3C2416)
+static inline int s3c_camif_set_ratio(camif_cfg_t *cfg)
+{
+ u32 cmd = 0;
+
+ if (cfg->dma_type & CAMIF_CODEC) {
+
+ writel(S3C_CICOSCPRERATIO_SHFACTOR_CO(cfg->sc.shfactor) | \
+ S3C_CICOSCPRERATIO_PREHORRATIO_CO(cfg->sc.prehratio) | \
+ S3C_CICOSCPRERATIO_PREVERRATIO_CO(cfg->sc.prevratio), cfg->regs + S3C_CICOSCPRERATIO);
+
+ writel(S3C_CICOSCPREDST_PREDSTWIDTH_CO(cfg->sc.predst_x) | \
+ S3C_CICOSCPREDST_PREDSTHEIGHT_CO(cfg->sc.predst_y), cfg->regs + S3C_CICOSCPREDST);
+
+ if (cfg->sc.scalerbypass)
+ cmd |= S3C_CICOSCCTRL_SCALERBYPASS_CO;
+
+ if (cfg->sc.scaleup_h & cfg->sc.scaleup_v)
+ cmd |= (S3C_CICOSCCTRL_SCALEUP_H | S3C_CICOSCCTRL_SCALEUP_V);
+
+ writel(cmd | S3C_CICOSCCTRL_MAINHORRATIO_CO(cfg->sc.mainhratio) | \
+ S3C_CICOSCCTRL_MAINVERRATIO_CO(cfg->sc.mainvratio), cfg->regs + S3C_CICOSCCTRL);
+
+ } else if (cfg->dma_type & CAMIF_PREVIEW) {
+
+ cmd |= S3C_CIPRSCCTRL_SAMPLE_PR;
+
+ writel(S3C_CIPRSCPRERATIO_SHFACTOR_PR(cfg->sc.shfactor) | \
+ S3C_CIPRSCPRERATIO_PREHORRATIO_PR(cfg->sc.prehratio) | \
+ S3C_CIPRSCPRERATIO_PREVERRATIO_PR(cfg->sc.prevratio), cfg->regs + S3C_CIPRSCPRERATIO);
+
+ writel(S3C_CIPRSCPREDST_PREDSTWIDTH_PR(cfg->sc.predst_x) | \
+ S3C_CIPRSCPREDST_PREDSTHEIGHT_PR(cfg->sc.predst_y), cfg->regs + S3C_CIPRSCPREDST);
+
+ if (cfg->dst_fmt & CAMIF_RGB24)
+ cmd |= S3C_CIPRSCCTRL_RGBFORMAT_24;
+
+ if (cfg->sc.scaleup_h & cfg->sc.scaleup_v)
+ cmd |= ((1 << 29) | (1 << 28));
+
+ writel(cmd | S3C_CIPRSCCTRL_MAINHORRATIO_PR(cfg->sc.mainhratio) | \
+ S3C_CIPRSCCTRL_MAINVERRATIO_PR(cfg->sc.mainvratio), cfg->regs + S3C_CIPRSCCTRL);
+
+ } else
+ printk(KERN_ERR "Invalid DMA type\n");
+
+ return 0;
+}
+#endif
+
+static int s3c_camif_calc_ratio(unsigned int src_width, unsigned int dst_width, unsigned int *ratio, unsigned int *shift)
+{
+ if (src_width >= 64 * dst_width) {
+ printk(KERN_ERR "Out of pre-scaler range: src_width / dst_width = %d (< 64)\n", src_width / dst_width);
+ return 1;
+ } else if (src_width >= 32 * dst_width) {
+ *ratio = 32;
+ *shift = 5;
+ } else if (src_width >= 16 * dst_width) {
+ *ratio = 16;
+ *shift = 4;
+ } else if (src_width >= 8 * dst_width) {
+ *ratio = 8;
+ *shift = 3;
+ } else if (src_width >= 4 * dst_width) {
+ *ratio = 4;
+ *shift = 2;
+ } else if (src_width >= 2 * dst_width) {
+ *ratio = 2;
+ *shift = 1;
+ } else {
+ *ratio = 1;
+ *shift = 0;
+ }
+
+ return 0;
+}
+
+static int s3c_camif_setup_scaler(camif_cfg_t *cfg)
+{
+ int tx = cfg->target_x, ty=cfg->target_y;
+ int sx, sy;
+
+ if (tx <= 0 || ty <= 0) {
+ printk(KERN_ERR "Invalid target size\n");
+ return -1;
+ }
+
+ sx = cfg->cis->source_x - (cfg->cis->win_hor_ofst + cfg->cis->win_hor_ofst2);
+ sy = cfg->cis->source_y - (cfg->cis->win_ver_ofst + cfg->cis->win_hor_ofst2);
+
+ if (sx <= 0 || sy <= 0) {
+ printk(KERN_ERR "Invalid source size\n");
+ return -1;
+ }
+
+ cfg->sc.modified_src_x = sx;
+ cfg->sc.modified_src_y = sy;
+
+ /* Pre-scaler control register 1 */
+ s3c_camif_calc_ratio(sx, tx, &cfg->sc.prehratio, &cfg->sc.hfactor);
+ s3c_camif_calc_ratio(sy, ty, &cfg->sc.prevratio, &cfg->sc.vfactor);
+
+ if (cfg->dma_type & CAMIF_PREVIEW) {
+ if ((sx / cfg->sc.prehratio) > 640) {
+ printk(KERN_INFO "Internal preview line buffer length is 640 pixels\n");
+ printk(KERN_INFO "Decrease the resolution or adjust window offset values appropriately\n");
+ }
+ }
+
+ cfg->sc.shfactor = 10 - (cfg->sc.hfactor + cfg->sc.vfactor);
+
+ /* Pre-scaler control register 2 */
+ cfg->sc.predst_x = sx / cfg->sc.prehratio;
+ cfg->sc.predst_y = sy / cfg->sc.prevratio;
+
+ /* Main-scaler control register */
+ cfg->sc.mainhratio = (sx << 8) / (tx << cfg->sc.hfactor);
+ cfg->sc.mainvratio = (sy << 8) / (ty << cfg->sc.vfactor);
+
+ cfg->sc.scaleup_h = (sx <= tx) ? 1 : 0;
+ cfg->sc.scaleup_v = (sy <= ty) ? 1 : 0;
+
+ s3c_camif_set_ratio(cfg);
+ s3c_camif_set_target_area(cfg);
+
+ return 0;
+}
+
+/*************************************************************************
+ * Format part
+ ************************************************************************/
+int s3c_camif_set_source_format(camif_cis_t *cis)
+{
+ camif_cfg_t *cfg = s3c_camif_get_fimc_object(CODEC_MINOR);
+ unsigned int cmd = 0;
+
+ /* Configure CISRCFMT --Source Format */
+ if (cis->itu_fmt & CAMIF_ITU601)
+ cmd = CAMIF_ITU601;
+ else {
+ assert(cis->itu_fmt & CAMIF_ITU656);
+ cmd = CAMIF_ITU656;
+ }
+
+ cmd |= (S3C_CISRCFMT_SOURCEHSIZE(cis->source_x) | S3C_CISRCFMT_SOURCEVSIZE(cis->source_y));
+
+ /* Order422 */
+ cmd |= cis->order422;
+ writel(cmd, cfg->regs + S3C_CISRCFMT);
+
+#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410)
+ cmd = (cis->order422 >> 14);
+ writel((readl(cfg->regs + S3C_CICOCTRL) & ~(0x3 << 0)) | cmd, cfg->regs + S3C_CICOCTRL);
+#endif
+
+ return 0;
+}
+
+#if defined(CONFIG_CPU_S3C2443) || defined(CONFIG_CPU_S3C2450) || defined(CONFIG_CPU_S3C2416)
+static int s3c_camif_set_target_format(camif_cfg_t *cfg)
+{
+ unsigned int cmd = 0;
+
+ if (cfg->dma_type & CAMIF_CODEC) {
+ cmd |= S3C_CICOTRGFMT_TARGETHSIZE_CO(cfg->target_x) | S3C_CICOTRGFMT_TARGETVSIZE_CO(cfg->target_y);
+
+ if (cfg->dst_fmt & CAMIF_YCBCR420) {
+ cmd |= (S3C_CICOTRGFMT_OUT422_420 | S3C_CICOTRGFMT_IN422_422);
+ writel(cmd, cfg->regs + S3C_CICOTRGFMT);
+ } else if (cfg->dst_fmt & CAMIF_YCBCR422) {
+ cmd |= (S3C_CICOTRGFMT_OUT422_422 | S3C_CICOTRGFMT_IN422_422);
+ writel(cmd, cfg->regs + S3C_CICOTRGFMT);
+ } else if ((cfg->dst_fmt & CAMIF_RGB24) || (cfg->dst_fmt & CAMIF_RGB16)) {
+ cmd |= (S3C_CICOTRGFMT_OUT422_422 | S3C_CICOTRGFMT_IN422_422);
+ writel(cmd | (1 << 29), cfg->regs + S3C_CICOTRGFMT);
+ } else
+ printk(KERN_ERR "Invalid target format\n");
+ } else {
+ assert(cfg->dma_type & CAMIF_PREVIEW);
+
+ cmd = readl(cfg->regs + S3C_CIPRTRGFMT);
+ cmd &= (S3C_CIPRTRGFMT_TARGETHSIZE_PR(0) | S3C_CIPRTRGFMT_TARGETVSIZE_PR(0));
+ cmd |= (S3C_CIPRTRGFMT_TARGETHSIZE_PR(cfg->target_x) | S3C_CIPRTRGFMT_TARGETVSIZE_PR(cfg->target_y));
+
+ writel(cmd | (2 << 30), cfg->regs + S3C_CIPRTRGFMT);
+ }
+
+ return 0;
+}
+#elif defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410)
+static int s3c_camif_set_target_format(camif_cfg_t *cfg)
+{
+ unsigned int cmd = 0;
+
+ if (cfg->dma_type & CAMIF_CODEC) {
+ cmd |= (S3C_CICOTRGFMT_TARGETHSIZE_CO(cfg->target_x) | S3C_CICOTRGFMT_TARGETVSIZE_CO(cfg->target_y));
+
+ if (cfg->dst_fmt & CAMIF_YCBCR420) {
+ cmd |= S3C_CICOTRGFMT_OUTFORMAT_YCBCR420OUT;
+ writel(cmd, cfg->regs + S3C_CICOTRGFMT);
+ } else if (cfg->dst_fmt & CAMIF_YCBCR422) {
+ cmd |= S3C_CICOTRGFMT_OUTFORMAT_YCBCR422OUT;
+ writel(cmd, cfg->regs + S3C_CICOTRGFMT);
+ } else if (cfg->dst_fmt & CAMIF_YCBCR422I) {
+ cmd |= S3C_CICOTRGFMT_OUTFORMAT_YCBCR422OUTINTERLEAVE;
+ writel(cmd, cfg->regs + S3C_CICOTRGFMT);
+ } else if ((cfg->dst_fmt & CAMIF_RGB24) || (cfg->dst_fmt & CAMIF_RGB16)) {
+ cmd |= S3C_CICOTRGFMT_OUTFORMAT_RGBOUT;
+ writel(cmd, cfg->regs + S3C_CICOTRGFMT);
+ } else
+ printk(KERN_ERR "Invalid target format\n");
+ } else {
+ assert(cfg->dma_type & CAMIF_PREVIEW);
+
+ cmd = readl(cfg->regs + S3C_CIPRTRGFMT);
+ cmd &= (S3C_CIPRTRGFMT_TARGETHSIZE_PR(0) | S3C_CIPRTRGFMT_TARGETVSIZE_PR(0));
+ cmd |= (S3C_CIPRTRGFMT_TARGETHSIZE_PR(cfg->target_x) | S3C_CIPRTRGFMT_TARGETVSIZE_PR(cfg->target_y));
+
+ if (cfg->dst_fmt & CAMIF_YCBCR420)
+ cmd |= S3C_CICOTRGFMT_OUTFORMAT_YCBCR420OUT;
+ else if (cfg->dst_fmt & CAMIF_YCBCR422)
+ cmd |= S3C_CICOTRGFMT_OUTFORMAT_YCBCR422OUT;
+ else if (cfg->dst_fmt & CAMIF_YCBCR422I)
+ cmd |= S3C_CICOTRGFMT_OUTFORMAT_YCBCR422OUTINTERLEAVE;
+ else if ((cfg->dst_fmt & CAMIF_RGB24) || (cfg->dst_fmt & CAMIF_RGB16))
+ cmd |= S3C_CICOTRGFMT_OUTFORMAT_RGBOUT;
+ else
+ printk(KERN_ERR "Invalid target format\n");
+
+ writel(cmd, cfg->regs + S3C_CIPRTRGFMT);
+ }
+
+ return 0;
+}
+#endif
+
+/*************************************************************************
+ * Control part
+ ************************************************************************/
+int s3c_camif_control_fimc(camif_cfg_t *cfg)
+{
+ if (s3c_camif_request_memory(cfg)) {
+ printk(KERN_ERR "Instead of using consistent_alloc(). Let me use dedicated mem for DMA\n");
+ return -1;
+ }
+
+ s3c_camif_setup_input_path(cfg);
+
+ if (s3c_camif_setup_scaler(cfg)) {
+ printk(KERN_ERR "Preview scaler fault: change WinHorOfset or target size\n");
+ return 1;
+ }
+
+ s3c_camif_set_target_format(cfg);
+
+ if (s3c_camif_setup_dma(cfg)) {
+ printk(KERN_ERR "DMA burst length error\n");
+ return 1;
+ }
+
+ s3c_camif_setup_output_path(cfg);
+
+ return 0;
+}
+
+int s3c_camif_start_dma(camif_cfg_t *cfg)
+{
+ unsigned int n_cmd = readl(cfg->regs + S3C_CIIMGCPT);
+ unsigned int val;
+
+ switch(cfg->capture_enable) {
+ case CAMIF_BOTH_DMA_ON:
+ s3c_camif_reset(CAMIF_RESET, 0); /* Flush Camera Core Buffer */
+
+ /* For Codec */
+ val = readl(cfg->regs + S3C_CICOSCCTRL);
+ val |= S3C_CICOSCCTRL_COSCALERSTART;
+ writel(val, cfg->regs + S3C_CICOSCCTRL);
+
+ /* For Preview */
+ val = readl(cfg->regs + S3C_CIPRSCCTRL);
+ val |= S3C_CIPRSCCTRL_START;
+ writel(val, cfg->regs + S3C_CIPRSCCTRL);
+
+ n_cmd |= S3C_CIIMGCPT_IMGCPTEN_COSC | S3C_CIIMGCPT_IMGCPTEN_PRSC;
+ break;
+
+ case CAMIF_DMA_ON:
+ s3c_camif_reset(CAMIF_RESET, 0); /* Flush Camera Core Buffer */
+
+ if (cfg->dma_type & CAMIF_CODEC) {
+ val = readl(cfg->regs + S3C_CICOSCCTRL);
+ val |= S3C_CICOSCCTRL_COSCALERSTART;
+ writel(val, cfg->regs + S3C_CICOSCCTRL);
+
+ n_cmd |= S3C_CIIMGCPT_IMGCPTEN_COSC;
+
+#if defined(CONFIG_CPU_S3C2443) || defined(CONFIG_CPU_S3C2450) || defined(CONFIG_CPU_S3C2416)
+ n_cmd |= (1 << 24);
+#endif
+ } else {
+ val = readl(cfg->regs + S3C_CIPRSCCTRL);
+ val |= S3C_CIPRSCCTRL_START;
+ writel(val, cfg->regs + S3C_CIPRSCCTRL);
+ n_cmd |= S3C_CIIMGCPT_IMGCPTEN_PRSC;
+ }
+
+ /* wait until Sync Time expires */
+ /* First settting, to wait VSYNC fall */
+ /* By VESA spec,in 640x480 @60Hz
+ MAX Delay Time is around 64us which "while" has.*/
+ while (S3C_CICOSTATUS_VSYNC & readl(cfg->regs + S3C_CICOSTATUS));
+ break;
+
+ default:
+ break;
+ }
+
+#if defined(CONFIG_CPU_S3C2443)
+ if (cfg->dma_type & CAMIF_CODEC) {
+ if (cfg->dst_fmt & CAMIF_RGB24)
+ n_cmd |= (3 << 25);
+ else if (cfg->dst_fmt & CAMIF_RGB16)
+ n_cmd |= (1 << 25);
+ else if (cfg->dst_fmt & CAMIF_YCBCR420)
+ n_cmd |= (2 << 25);
+ }
+#endif
+
+ val = readl(cfg->regs + S3C_CIIMGCPT);
+ val &= ~(0x7 << 29);
+ writel(val | n_cmd | S3C_CIIMGCPT_IMGCPTEN, cfg->regs + S3C_CIIMGCPT);
+
+ return 0;
+}
+
+int s3c_camif_stop_dma(camif_cfg_t *cfg)
+{
+ unsigned int n_cmd = readl(cfg->regs + S3C_CIIMGCPT);
+ unsigned int val;
+
+ switch(cfg->capture_enable) {
+ case CAMIF_BOTH_DMA_OFF:
+ val = readl(cfg->regs + S3C_CICOSCCTRL);
+ val &= ~S3C_CICOSCCTRL_COSCALERSTART;
+ writel(val, cfg->regs + S3C_CICOSCCTRL);
+
+ val = readl(cfg->regs + S3C_CIPRSCCTRL);
+ val &= ~S3C_CIPRSCCTRL_START;
+ writel(val, cfg->regs + S3C_CIPRSCCTRL);
+
+ n_cmd = 0;
+ break;
+
+ case CAMIF_DMA_OFF_L_IRQ: /* fall thru */
+ case CAMIF_DMA_OFF:
+ if (cfg->dma_type & CAMIF_CODEC) {
+ val = readl(cfg->regs + S3C_CICOSCCTRL);
+ val &= ~S3C_CICOSCCTRL_COSCALERSTART;
+ writel(val, cfg->regs + S3C_CICOSCCTRL);
+ n_cmd &= ~S3C_CIIMGCPT_IMGCPTEN_COSC;
+
+ if (!(n_cmd & S3C_CIIMGCPT_IMGCPTEN_PRSC))
+ n_cmd = 0;
+ } else {
+ val = readl(cfg->regs + S3C_CIPRSCCTRL);
+ val &= ~S3C_CIPRSCCTRL_START;
+ writel(val, cfg->regs + S3C_CIPRSCCTRL);
+
+ n_cmd &= ~S3C_CIIMGCPT_IMGCPTEN_PRSC;
+
+ if (!(n_cmd & S3C_CIIMGCPT_IMGCPTEN_COSC))
+ n_cmd = 0;
+ }
+
+ break;
+
+ default:
+ printk(KERN_ERR "Unexpected DMA control\n");
+ }
+
+ writel(n_cmd, cfg->regs + S3C_CIIMGCPT);
+
+ if (cfg->capture_enable == CAMIF_DMA_OFF_L_IRQ) { /* Last IRQ */
+ if (cfg->dma_type & CAMIF_CODEC) {
+ val = readl(cfg->regs + S3C_CICOCTRL);
+ val |= S3C_CICOCTRL_LASTIRQEN;
+ writel(val, cfg->regs + S3C_CICOCTRL);
+ } else {
+ val = readl(cfg->regs + S3C_CIPRCTRL);
+ val |= S3C_CIPRCTRL_LASTIRQEN_ENABLE;
+ writel(val, cfg->regs + S3C_CIPRCTRL);
+ }
+ }
+
+ return 0;
+}
+
+#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410)
+int s3c_camif_start_codec_msdma(camif_cfg_t *cfg)
+{
+ int ret = 0;
+ u32 val;
+
+ val = readl(cfg->regs + S3C_MSCOCTRL);
+ val &= ~(1 << 0);
+ writel(val, cfg->regs + S3C_MSCOCTRL);
+
+ val = readl(cfg->regs + S3C_MSCOCTRL);
+ val |= (1 << 0);
+ writel(val, cfg->regs + S3C_MSCOCTRL);
+
+ return ret;
+}
+#endif
+
+int s3c_camif_start_preview_msdma(camif_cfg_t * cfg)
+{
+ unsigned int val;
+ int ret = 0;
+
+#if !defined(CONFIG_CPU_S3C6400) && !defined(CONFIG_CPU_S3C6410)
+ val = readl(cfg->regs + S3C_CIMSCTRL);
+ val &= ~(1 << 0);
+ writel(val, cfg->regs + S3C_CIMSCTRL);
+#endif
+ val = readl(cfg->regs + S3C_CIMSCTRL);
+ val |= (1 << 0);
+ writel(val, cfg->regs + S3C_CIMSCTRL);
+
+ while((readl(cfg->regs + S3C_CIMSCTRL) & (1 << 6)) == 0);
+
+ return ret;
+}
+
+void s3c_camif_change_flip(camif_cfg_t *cfg)
+{
+ unsigned int cmd = 0;
+
+ if (cfg->dma_type & CAMIF_CODEC) {
+ cmd = readl(cfg->regs + S3C_CICOTRGFMT);
+ cmd &= ~((1 << 14) | (1 << 15));
+ cmd |= cfg->flip;
+ writel(cmd, cfg->regs + S3C_CICOTRGFMT);
+ } else {
+ /* if ROT90_Pr == 1, dma burst length must be 4 */
+ if (cfg->flip == CAMIF_ROTATE_90 || cfg->flip == CAMIF_FLIP_ROTATE_270) {
+ cmd = readl(cfg->regs + S3C_CIPRCTRL);
+ cmd &= ~(0x3ff << 14);
+ cmd |= (S3C_CICOCTRL_YBURST1_CO(4) | S3C_CICOCTRL_YBURST2_CO(4));
+ writel(cmd, cfg->regs + S3C_CIPRCTRL);
+ }
+
+ cmd = readl(cfg->regs + S3C_CIPRTRGFMT);
+ cmd &= ~(0x7 << 13);
+ cmd |= cfg->flip;
+ writel(cmd, cfg->regs + S3C_CIPRTRGFMT);
+ }
+}
+
+void s3c_camif_change_effect(camif_cfg_t *cfg)
+{
+ unsigned int val = readl(cfg->regs + S3C_CIIMGEFF);
+ val &= ~((1 << 28) | (1 << 27) | (1 << 26));
+
+#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410)
+ val |= ((1 << 31) | (1 << 30));
+#endif
+
+ switch(cfg->effect) {
+ case CAMIF_SILHOUETTE:
+ val |= S3C_CIIMGEFF_FIN_SILHOUETTE;
+ break;
+
+ case CAMIF_EMBOSSING:
+ val |= S3C_CIIMGEFF_FIN_EMBOSSING;
+ break;
+
+ case CAMIF_ART_FREEZE:
+ val |= S3C_CIIMGEFF_FIN_ARTFREEZE;
+ break;
+
+ case CAMIF_NEGATIVE:
+ val |= S3C_CIIMGEFF_FIN_NEGATIVE;
+ break;
+
+ case CAMIF_ARBITRARY_CB_CR:
+ val |= S3C_CIIMGEFF_FIN_ARBITRARY;
+ break;
+
+ case CAMIF_BYPASS:
+ default:
+ break;
+ }
+
+ writel(val, cfg->regs + S3C_CIIMGEFF);
+}
+
+int s3c_camif_do_postprocess(camif_cfg_t *cfg)
+{
+ unsigned int val= readl(cfg->regs + S3C_CIMSCTRL);
+
+ if (cfg->dst_fmt & CAMIF_YCBCR420)
+ val |= (1 << 1);
+ else
+ val &= ~(1 << 1);
+
+ val &= ~(1 << 0);
+ writel(val, cfg->regs + S3C_CIMSCTRL);
+
+ val |= (1 << 0);
+ writel(val, cfg->regs + S3C_CIMSCTRL);
+
+ printk(KERN_INFO "Postprocessing started\n");
+
+ while((readl(cfg->regs + S3C_CIMSCTRL) & (1 << 6)) == 0);
+
+ printk(KERN_INFO "Postprocessing finished\n");
+
+ return 0;
+}
+
+int s3c_camif_set_offset(camif_cis_t *cis)
+{
+ camif_cfg_t *cfg = s3c_camif_get_fimc_object(CODEC_MINOR);
+ unsigned int h = cis->win_hor_ofst; /* Camera input offset ONLY */
+ unsigned int v = cis->win_ver_ofst; /* Camera input offset ONLY */
+ unsigned int h2 = cis->win_hor_ofst2; /* Camera input offset ONLY */
+ unsigned int v2 = cis->win_ver_ofst2; /* Camera input offset ONLY */
+
+ /*Clear Overflow */
+ writel(S3C_CIWDOFST_CLROVCOFIY | S3C_CIWDOFST_CLROVCOFICB | \
+ S3C_CIWDOFST_CLROVCOFICR | S3C_CIWDOFST_CLROVPRFICB | \
+ S3C_CIWDOFST_CLROVPRFICR, cfg->regs + S3C_CIWDOFST);
+
+ writel(0, cfg->regs + S3C_CIWDOFST);
+
+ if (!h && !v) {
+ writel(0, cfg->regs + S3C_CIWDOFST);
+ writel(0, cfg->regs + S3C_CIDOWSFT2);
+ return 0;
+ }
+
+ writel(S3C_CIWDOFST_WINOFSEN | S3C_CIWDOFST_WINHOROFST(h) | S3C_CIWDOFST_WINVEROFST(v), cfg->regs + S3C_CIWDOFST);
+ writel(S3C_CIDOWSFT2_WINHOROFST2(h) | S3C_CIDOWSFT2_WINVEROFST2(v), cfg->regs + S3C_CIDOWSFT2);
+ writel(S3C_CIDOWSFT2_WINHOROFST2(h2) | S3C_CIDOWSFT2_WINVEROFST2(v2), cfg->regs + S3C_CIDOWSFT2);
+
+ return 0;
+}
+
+void s3c_camif_set_priority(int flag)
+{
+ unsigned int val;
+
+ if (flag) {
+ irq_old_priority = readl(S3C_PRIORITY);
+ val = irq_old_priority;
+ val &= ~(3 << 7);
+ writel(val, S3C_PRIORITY);
+
+ /* Arbiter 1, REQ2 first */
+ val |= (1 << 7);
+ writel(val, S3C_PRIORITY);
+
+ /* Disable Priority Rotate */
+ val &= ~(1 << 1);
+ writel(val, S3C_PRIORITY);
+ } else
+ writel(irq_old_priority, S3C_PRIORITY);
+}
+
+/*************************************************************************
+ * Interrupt part
+ ************************************************************************/
+void s3c_camif_enable_lastirq(camif_cfg_t *cfg)
+{
+ unsigned int val;
+
+ if (cfg->capture_enable == CAMIF_BOTH_DMA_ON) {
+ val = readl(cfg->regs + S3C_CICOCTRL);
+ val |= S3C_CICOCTRL_LASTIRQEN;
+ writel(val, cfg->regs + S3C_CICOCTRL);
+
+ val = readl(cfg->regs + S3C_CIPRCTRL);
+ val |= S3C_CIPRCTRL_LASTIRQEN_ENABLE;
+ writel(val, cfg->regs + S3C_CIPRCTRL);
+ } else {
+ if (cfg->dma_type & CAMIF_CODEC) {
+ val = readl(cfg->regs + S3C_CICOCTRL);
+ val |= S3C_CICOCTRL_LASTIRQEN;
+ writel(val, cfg->regs + S3C_CICOCTRL);
+ } else {
+ val = readl(cfg->regs + S3C_CIPRCTRL);
+ val |= S3C_CIPRCTRL_LASTIRQEN_ENABLE;
+ writel(val, cfg->regs + S3C_CIPRCTRL);
+ }
+ }
+}
+
+void s3c_camif_disable_lastirq(camif_cfg_t *cfg)
+{
+ unsigned int val;
+
+ if (cfg->capture_enable == CAMIF_BOTH_DMA_ON) {
+ val = readl(cfg->regs + S3C_CICOCTRL);
+ val &= ~S3C_CICOCTRL_LASTIRQEN;
+ writel(val, cfg->regs + S3C_CICOCTRL);
+
+ val = readl(cfg->regs + S3C_CIPRCTRL);
+ val &= ~S3C_CIPRCTRL_LASTIRQEN_ENABLE;
+ writel(val, cfg->regs + S3C_CIPRCTRL);
+ } else {
+ if (cfg->dma_type & CAMIF_CODEC) {
+ val = readl(cfg->regs + S3C_CICOCTRL);
+ val &= ~S3C_CICOCTRL_LASTIRQEN;
+ writel(val, cfg->regs + S3C_CICOCTRL);
+ } else {
+ val = readl(cfg->regs + S3C_CIPRCTRL);
+ val &= ~S3C_CIPRCTRL_LASTIRQEN_ENABLE;
+ writel(val, cfg->regs + S3C_CIPRCTRL);
+ }
+ }
+}
+
+#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410)
+void s3c_camif_clear_irq(int irq)
+{
+ camif_cfg_t *cfg = s3c_camif_get_fimc_object(CODEC_MINOR);
+ unsigned int val = 0;
+
+ if (irq == IRQ_CAMIF_C) {
+ val = readl(cfg->regs + S3C_CIGCTRL);
+ val |= (1 << 19);
+ } else if (irq == IRQ_CAMIF_P) {
+ val = readl(cfg->regs + S3C_CIGCTRL);
+ val |= (1 << 18);
+ }
+
+ writel(val, cfg->regs + S3C_CIGCTRL);
+}
+#else
+void s3c_camif_clear_irq(int irq)
+{
+}
+#endif
+
+/*************************************************************************
+ * Initialize part
+ ************************************************************************/
+#if defined(CONFIG_CPU_S3C2443) || defined(CONFIG_CPU_S3C2450) || defined(CONFIG_CPU_S3C2416)
+static int s3c_camif_set_gpio(void)
+{
+ s3c2410_gpio_cfgpin(S3C2440_GPJ0, S3C2440_GPJ0_CAMDATA0);
+ s3c2410_gpio_cfgpin(S3C2440_GPJ1, S3C2440_GPJ1_CAMDATA1);
+ s3c2410_gpio_cfgpin(S3C2440_GPJ2, S3C2440_GPJ2_CAMDATA2);
+ s3c2410_gpio_cfgpin(S3C2440_GPJ3, S3C2440_GPJ3_CAMDATA3);
+ s3c2410_gpio_cfgpin(S3C2440_GPJ4, S3C2440_GPJ4_CAMDATA4);
+ s3c2410_gpio_cfgpin(S3C2440_GPJ5, S3C2440_GPJ5_CAMDATA5);
+ s3c2410_gpio_cfgpin(S3C2440_GPJ6, S3C2440_GPJ6_CAMDATA6);
+ s3c2410_gpio_cfgpin(S3C2440_GPJ7, S3C2440_GPJ7_CAMDATA7);
+ s3c2410_gpio_cfgpin(S3C2440_GPJ8, S3C2440_GPJ8_CAMPCLK);
+ s3c2410_gpio_cfgpin(S3C2440_GPJ9, S3C2440_GPJ9_CAMVSYNC);
+ s3c2410_gpio_cfgpin(S3C2440_GPJ10, S3C2440_GPJ10_CAMHREF);
+ s3c2410_gpio_cfgpin(S3C2440_GPJ11, S3C2440_GPJ11_CAMCLKOUT);
+ s3c2410_gpio_cfgpin(S3C2440_GPJ12, S3C2440_GPJ12_CAMRESET);
+
+ writel(0x1fff, S3C2443_GPJDN);
+
+ return 0;
+}
+#elif defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410)
+static int s3c_camif_set_gpio(void)
+{
+ s3c_gpio_cfgpin(S3C_GPF5, S3C_GPF5_CAMIF_YDATA0);
+ s3c_gpio_cfgpin(S3C_GPF6, S3C_GPF6_CAMIF_YDATA1);
+ s3c_gpio_cfgpin(S3C_GPF7, S3C_GPF7_CAMIF_YDATA2);
+ s3c_gpio_cfgpin(S3C_GPF8, S3C_GPF8_CAMIF_YDATA03);
+ s3c_gpio_cfgpin(S3C_GPF9, S3C_GPF9_CAMIF_YDATA4);
+ s3c_gpio_cfgpin(S3C_GPF10, S3C_GPF10_CAMIF_YDATA5);
+ s3c_gpio_cfgpin(S3C_GPF11, S3C_GPF11_CAMIF_YDATA06);
+ s3c_gpio_cfgpin(S3C_GPF12, S3C_GPF12_CAMIF_YDATA7);
+ s3c_gpio_cfgpin(S3C_GPF2, S3C_GPF2_CAMIF_CLK);
+ s3c_gpio_cfgpin(S3C_GPF4, S3C_GPF4_CAMIF_VSYNC);
+ s3c_gpio_cfgpin(S3C_GPF1, S3C_GPF1_CAMIF_HREF);
+ s3c_gpio_cfgpin(S3C_GPF0, S3C_GPF0_CAMIF_CLK);
+ s3c_gpio_cfgpin(S3C_GPF3, S3C_GPF3_CAMIF_RST);
+
+ writel(0, S3C_GPFPU);
+
+ return 0;
+}
+#endif
+
+void s3c_camif_reset(int is, int delay)
+{
+ camif_cfg_t *cfg = s3c_camif_get_fimc_object(CODEC_MINOR);
+ unsigned int val;
+ unsigned int tmp;
+
+ switch (is) {
+ case CAMIF_RESET:
+ tmp = readl(cfg->regs + S3C_CISRCFMT);
+
+ if (tmp &= (1 << 31)) {
+ /* ITU-R BT 601 */
+ val = readl(cfg->regs + S3C_CIGCTRL);
+ val |= S3C_CIGCTRL_SWRST;
+
+#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410)
+ val |= S3C_CIGCTRL_IRQ_LEVEL;
+#endif
+ writel(val, cfg->regs + S3C_CIGCTRL);
+ mdelay(1);
+
+ val = readl(cfg->regs + S3C_CIGCTRL);
+ val &= ~S3C_CIGCTRL_SWRST;
+ writel(val, cfg->regs + S3C_CIGCTRL);
+ } else {
+ /* ITU-R BT 656 */
+ tmp = readl(cfg->regs + S3C_CISRCFMT);
+ tmp |= (1 << 31);
+ writel(tmp, cfg->regs + S3C_CISRCFMT);
+
+ val = readl(cfg->regs + S3C_CIGCTRL);
+ val |= S3C_CIGCTRL_SWRST;
+
+#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410)
+ val |= S3C_CIGCTRL_IRQ_LEVEL;
+#endif
+ writel(val, cfg->regs + S3C_CIGCTRL);
+ mdelay(1);
+
+ val = readl(cfg->regs + S3C_CIGCTRL);
+ val &= ~S3C_CIGCTRL_SWRST;
+ writel(val, cfg->regs + S3C_CIGCTRL);
+
+ tmp = readl(cfg->regs + S3C_CISRCFMT);
+ tmp &= ~(1 << 31);
+ writel(tmp, cfg->regs + S3C_CISRCFMT);
+ }
+
+ break;
+
+ case CAMIF_EX_RESET_AH:
+ val = readl(cfg->regs + S3C_CIGCTRL);
+ val |= S3C_CIGCTRL_CAMRST;
+ writel(val, cfg->regs + S3C_CIGCTRL);
+ udelay(200);
+
+ val = readl(cfg->regs + S3C_CIGCTRL);
+ val &= ~S3C_CIGCTRL_CAMRST;
+ writel(val, cfg->regs + S3C_CIGCTRL);
+ udelay(delay);
+
+#if defined(CONFIG_VIDEO_SAMSUNG_S5K3AA)
+ val = readl(cfg->regs + S3C_CIGCTRL);
+ val |= S3C_CIGCTRL_CAMRST;
+ writel(val, cfg->regs + S3C_CIGCTRL);
+ udelay(200);
+#endif
+ break;
+
+ case CAMIF_EX_RESET_AL:
+ val = readl(cfg->regs + S3C_CIGCTRL);
+ val &= ~S3C_CIGCTRL_CAMRST;
+ writel(val, cfg->regs + S3C_CIGCTRL);
+ udelay(200);
+
+ val = readl(cfg->regs + S3C_CIGCTRL);
+ val |= S3C_CIGCTRL_CAMRST;
+ writel(val, cfg->regs + S3C_CIGCTRL);
+ udelay(delay);
+ break;
+
+ default:
+ break;
+ }
+}
+
+void s3c_camif_init(void)
+{
+ s3c_camif_reset(CAMIF_RESET, 0);
+ s3c_camif_set_gpio();
+}
+