1864 lines
52 KiB
C

/* 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();
}