1864 lines
52 KiB
C
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();
|
|
}
|
|
|