diff options
Diffstat (limited to 'drivers/media/video/s3c_camif.c')
-rw-r--r-- | drivers/media/video/s3c_camif.c | 1863 |
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(); +} + |