📄 camif.c
字号:
/* * Copyright (C) 2004 Samsung Electronics * SW.LEE <hitchcar@samsung.com> * * This file is subject to the terms and conditions of the GNU General Public * License 2. See the file COPYING in the main directory of this archive * for more details. */#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/sched.h>#include <linux/irq.h>#include <linux/tqueue.h>#include <linux/locks.h>#include <linux/completion.h>#include <linux/delay.h>#include <linux/slab.h>#include <linux/vmalloc.h>#include <linux/miscdevice.h>#include <linux/wait.h>#include <linux/miscdevice.h>#include <asm/io.h>#include <asm/semaphore.h>#include <asm/hardware.h>#include <asm/uaccess.h>#ifdef CONFIG_ARCH_S3C24A0A#include <asm/arch/S3C24A0.h>#include <asm/arch/clocks.h>#else#include <asm/arch/S3C2440.h>#include <asm/arch/clocks.h>#endif#include "cam_reg.h"//#define SW_DEBUG#include "camif.h"#include "videodev.h"#include "miscdevice.h"static int camif_dma_burst(camif_cfg_t *);static int camif_scaler(camif_cfg_t *);static const char *camif_version = "$Id: camif.c,v 1.10 2004/06/04 04:24:14 swlee Exp $"; /* For SXGA Image */#define RESERVE_MEM 15*1024*1024#define YUV_MEM 10*1024*1024#define RGB_MEM (RESERVE_MEM - YUV_MEM)static int camif_malloc(camif_cfg_t *cfg){ unsigned int t_size; unsigned int daon = cfg->target_x *cfg->target_y; if(cfg->dma_type & CAMIF_CODEC) { if (cfg->fmt & CAMIF_OUT_YCBCR420) { t_size = daon * 3 / 2 ; } else { t_size = daon * 2; /* CAMIF_OUT_YCBCR422 */ } t_size = t_size *cfg->pp_num;#ifndef SAMSUNG_SXGA_CAM cfg->pp_virt_buf = consistent_alloc(GFP_KERNEL, t_size, &cfg->pp_phys_buf);#else printk(KERN_INFO "Reserving High RAM Addresses \n"); cfg->pp_phys_buf = PHYS_OFFSET + (MEM_SIZE - RESERVE_MEM); cfg->pp_virt_buf = ioremap_nocache(cfg->pp_phys_buf,YUV_MEM);#endif if ( !cfg->pp_virt_buf ) { printk(KERN_ERR"CAMERA:Failed to request YCBCR MEM\n"); return -ENOMEM; } memset(cfg->pp_virt_buf, 0, t_size); cfg->pp_totalsize = t_size; return 0; } if ( cfg->dma_type & CAMIF_PREVIEW ) { if (cfg->fmt & CAMIF_RGB16) t_size = daon * 2; /* 4byte per two pixel*/ else { assert(cfg->fmt & CAMIF_RGB24); t_size = daon * 4; /* 4byte per one pixel */ } t_size = t_size * cfg->pp_num;#ifndef SAMSUNG_SXGA_CAM cfg->pp_virt_buf = consistent_alloc(GFP_KERNEL, t_size, &cfg->pp_phys_buf);#else printk(KERN_INFO "Reserving High RAM Addresses \n"); cfg->pp_phys_buf = PHYS_OFFSET + (MEM_SIZE - RESERVE_MEM ) + YUV_MEM; cfg->pp_virt_buf = ioremap_nocache(cfg->pp_phys_buf,RGB_MEM);#endif if ( !cfg->pp_virt_buf ) { printk(KERN_ERR"CAMERA:Failed to request RGB MEM\n"); return -ENOMEM; } memset(cfg->pp_virt_buf, 0, t_size); cfg->pp_totalsize = t_size; return 0; } return 0; /* Never come. */}static int camif_demalloc(camif_cfg_t *cfg){#ifndef SAMSUNG_SXGA_CAM if ( cfg->pp_virt_buf ) { consistent_free(cfg->pp_virt_buf,cfg->pp_totalsize,cfg->pp_phys_buf); cfg->pp_virt_buf = 0; }#else iounmap(cfg->pp_virt_buf); cfg->pp_virt_buf = 0;#endif return 0;}/* * advise a person to use this func in ISR * index value indicates the next frame count to be used */int camif_g_frame_num(camif_cfg_t *cfg){ int index = 0; if (cfg->dma_type & CAMIF_CODEC ) { index = FRAME_CNT(CICOSTATUS); DPRINTK("CAMIF_CODEC frame %d \n", index); } else { assert(cfg->dma_type & CAMIF_PREVIEW ); index = FRAME_CNT(CIPRSTATUS); DPRINTK("CAMIF_PREVIEW frame %d 0x%08X \n", index, CIPRSTATUS); } cfg->now_frame_num = (index + 2) % 4; /* When 4 PingPong */ return index; /* meaningless */}static int camif_pp_codec(camif_cfg_t *cfg){ u32 i, c_size; /* Cb,Cr size */ u32 one_p_size; u32 daon = cfg->target_x * cfg->target_y; if (cfg->fmt & CAMIF_OUT_YCBCR420) { c_size = daon /4; } else { assert(cfg->fmt & CAMIF_OUT_YCBCR422); c_size = daon /2; } switch ( cfg->pp_num ) { case 1 : for ( i =0 ; i < 4; i=i+1) { 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 + daon; cfg->img_buf[i].phys_cb = cfg->pp_phys_buf + daon; cfg->img_buf[i].virt_cr = cfg->pp_virt_buf + daon + c_size; cfg->img_buf[i].phys_cr = cfg->pp_phys_buf + daon + c_size; CICOYSA(i) = cfg->img_buf[i].phys_y; CICOCBSA(i) = cfg->img_buf[i].phys_cb; CICOCRSA(i) = cfg->img_buf[i].phys_cr; } break; case 2:#define TRY (( i%2 ) ? 1 :0) one_p_size = daon + 2*c_size; for (i = 0; i < 4 ; i++) { cfg->img_buf[i].virt_y = cfg->pp_virt_buf + TRY * one_p_size; cfg->img_buf[i].phys_y = cfg->pp_phys_buf + TRY * one_p_size; cfg->img_buf[i].virt_cb = cfg->pp_virt_buf + daon + TRY * one_p_size; cfg->img_buf[i].phys_cb = cfg->pp_phys_buf + daon + TRY * one_p_size; cfg->img_buf[i].virt_cr = cfg->pp_virt_buf + daon + c_size + TRY * one_p_size; cfg->img_buf[i].phys_cr = cfg->pp_phys_buf + daon + c_size + TRY * one_p_size; CICOYSA(i) = cfg->img_buf[i].phys_y; CICOCBSA(i) = cfg->img_buf[i].phys_cb; CICOCRSA(i) = cfg->img_buf[i].phys_cr; } break; case 4: one_p_size = daon + 2*c_size; 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 + daon + i * one_p_size; cfg->img_buf[i].phys_cb = cfg->pp_phys_buf + daon + i * one_p_size; cfg->img_buf[i].virt_cr = cfg->pp_virt_buf + daon + c_size + i * one_p_size; cfg->img_buf[i].phys_cr = cfg->pp_phys_buf + daon + c_size + i * one_p_size; CICOYSA(i) = cfg->img_buf[i].phys_y; CICOCBSA(i) = cfg->img_buf[i].phys_cb; CICOCRSA(i) = cfg->img_buf[i].phys_cr; } break; default: printk("Invalid PingPong Number %d \n",cfg->pp_num); panic("halt\n"); } return 0;}/* RGB Buffer Allocation */static int camif_pp_preview(camif_cfg_t *cfg){ int i; u32 daon = cfg->target_x * cfg->target_y; if(cfg->fmt & CAMIF_RGB24) daon = daon * 4 ; else { assert (cfg->fmt & CAMIF_RGB16); daon = daon *2; } 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 ; CIPRCLRSA(i) = cfg->img_buf[i].phys_rgb; } break; case 2: for ( i = 0; i < 4 ; i++) { cfg->img_buf[i].virt_rgb = cfg->pp_virt_buf + TRY * daon; cfg->img_buf[i].phys_rgb = cfg->pp_phys_buf + TRY * daon; CIPRCLRSA(i) = cfg->img_buf[i].phys_rgb; } break; case 4: for ( i = 0; i < 4 ; i++) { cfg->img_buf[i].virt_rgb = cfg->pp_virt_buf + i * daon; cfg->img_buf[i].phys_rgb = cfg->pp_phys_buf + i * daon; CIPRCLRSA(i) = cfg->img_buf[i].phys_rgb; } break; default: printk("Invalid PingPong Number %d \n",cfg->pp_num); panic("halt\n"); } return 0;}static int camif_pingpong(camif_cfg_t *cfg){ if (cfg->dma_type & CAMIF_CODEC ) { camif_pp_codec(cfg); } if ( cfg->dma_type & CAMIF_PREVIEW) { camif_pp_preview(cfg); } return 0;}/*********** Image Convert *******************************//* Return Format * Supported by Hardware * V4L2_PIX_FMT_YUV420, * V4L2_PIX_FMT_YUV422P, * V4L2_PIX_FMT_BGR32 (BGR4) * ----------------------------------- * V4L2_PIX_FMT_RGB565(X) * Currenly 2byte --> BGR656 Format * S3C2440A,S3C24A0 supports vairants with reversed FMT_RGB565 i.e blue toward the least, red towards the most significant bit -- by SW.LEE *//* * After calling camif_g_frame_num, * this func must be called */u8 * camif_g_frame(camif_cfg_t *cfg){ u8 * ret = NULL; int cnt = cfg->now_frame_num; if(cfg->dma_type & CAMIF_PREVIEW) { ret = cfg->img_buf[cnt].virt_rgb; } if (cfg->dma_type & CAMIF_CODEC) { ret = cfg->img_buf[cnt].virt_y; } return ret;}/* This function must be called in module initial time */static int camif_source_fmt(camif_gc_t *gc) { u32 cmd = 0; /* Configure CISRCFMT --Source Format */ if (gc->itu_fmt & CAMIF_ITU601) { cmd = CAMIF_ITU601; } else { assert ( gc->itu_fmt & CAMIF_ITU656); cmd = CAMIF_ITU656; } cmd |= SOURCE_HSIZE(gc->source_x)| SOURCE_VSIZE(gc->source_y); /* Order422 */ cmd |= gc->order422; CISRCFMT = cmd; return 0 ;}/* * Codec Input YCBCR422 will be Fixed */static int camif_target_fmt(camif_cfg_t *cfg){ u32 cmd = 0; if (cfg->dma_type & CAMIF_CODEC) { /* YCBCR setting */ cmd = TARGET_HSIZE(cfg->target_x)| TARGET_VSIZE(cfg->target_y); if ( cfg->fmt & CAMIF_OUT_YCBCR420 ) { cmd |= OUT_YCBCR420|IN_YCBCR422; } else { assert(cfg->fmt & CAMIF_OUT_YCBCR422); cmd |= OUT_YCBCR422|IN_YCBCR422; } CICOTRGFMT = cmd | cfg->flip; } else { assert(cfg->dma_type & CAMIF_PREVIEW); CIPRTRGFMT = TARGET_HSIZE(cfg->target_x)|TARGET_VSIZE(cfg->target_y)|cfg->flip; } return 0;}void camif_change_flip(camif_cfg_t *cfg){ u32 cmd = 0; if (cfg->dma_type & CAMIF_CODEC ) { /* YCBCR setting */ cmd = CICOTRGFMT; cmd &= ~(BIT14|BIT15); /* Clear FLIP Mode */ cmd |= cfg->flip; CICOTRGFMT = cmd; } else { cmd = CIPRTRGFMT; cmd &= ~(BIT14|BIT15); cmd |= cfg->flip; CICOTRGFMT = cmd; }}/* Must: * Before calling this function, * you must use "camif_dynamic_open" * If you want to enable both CODEC and preview * you must do it at the same time. */int camif_capture_start(camif_cfg_t *cfg){ u32 n_cmd = 0; /* Next Command */ switch(cfg->exec) { case CAMIF_BOTH_DMA_ON: camif_reset(CAMIF_RESET,0); /* Flush Camera Core Buffer */ CIPRSCCTRL |= SCALERSTART; CICOSCCTRL |= SCALERSTART; n_cmd = CAMIF_CAP_PREVIEW_ON|CAMIF_CAP_CODEC_ON; break; case CAMIF_DMA_ON: camif_reset(CAMIF_RESET,0); /* Flush Camera Core Buffer */ if (cfg->dma_type&CAMIF_CODEC) { CICOSCCTRL |= SCALERSTART; n_cmd = CAMIF_CAP_CODEC_ON; }else { CIPRSCCTRL |= SCALERSTART; n_cmd = CAMIF_CAP_PREVIEW_ON; } /* 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(VSYNC & CICOSTATUS); break; default: break; } CIIMGCPT = n_cmd|CAMIF_CAP_ON; return 0;}int camif_capture_stop(camif_cfg_t *cfg){ u32 n_cmd = CIIMGCPT; /* Next Command */ switch(cfg->exec) { case CAMIF_BOTH_DMA_OFF: CIPRSCCTRL &= ~SCALERSTART; CICOSCCTRL &= ~SCALERSTART; n_cmd = 0; break; case CAMIF_DMA_OFF_L_IRQ: /* fall thru */ case CAMIF_DMA_OFF: if (cfg->dma_type&CAMIF_CODEC) { CICOSCCTRL &= ~SCALERSTART; n_cmd &= ~CAMIF_CAP_CODEC_ON; if (!(n_cmd & CAMIF_CAP_PREVIEW_ON)) n_cmd = 0; }else { CIPRSCCTRL &= ~SCALERSTART; n_cmd &= ~CAMIF_CAP_PREVIEW_ON; if (!(n_cmd & CAMIF_CAP_CODEC_ON)) n_cmd = 0; } break; default: panic("Unexpected \n"); } CIIMGCPT = n_cmd; if(cfg->exec == CAMIF_DMA_OFF_L_IRQ) { /* Last IRQ */ if (cfg->dma_type & CAMIF_CODEC) CICOCTRL |= LAST_IRQ_EN; else CIPRCTRL |= LAST_IRQ_EN; } #if 0 else { /* to make internal state machine of CAMERA stop */ camif_reset(CAMIF_RESET, 0); }#endif return 0;}/* LastIRQEn is autoclear */void camif_last_irq_en(camif_cfg_t *cfg){ if(cfg->exec == CAMIF_BOTH_DMA_ON) { CIPRCTRL |= LAST_IRQ_EN; CICOCTRL |= LAST_IRQ_EN; } else { if (cfg->dma_type & CAMIF_CODEC) CICOCTRL |= LAST_IRQ_EN; else CIPRCTRL |= LAST_IRQ_EN; }}static int camif_scaler_internal(u32 srcWidth, u32 dstWidth, u32 *ratio, u32 *shift){ if(srcWidth>=64*dstWidth){ printk(KERN_ERR"CAMERA:out of prescaler range: srcWidth /dstWidth = %d(< 64)\n", srcWidth/dstWidth); return 1; } else if(srcWidth>=32*dstWidth){ *ratio=32; *shift=5; } else if(srcWidth>=16*dstWidth){ *ratio=16; *shift=4; } else if(srcWidth>=8*dstWidth){ *ratio=8; *shift=3; } else if(srcWidth>=4*dstWidth){ *ratio=4; *shift=2; } else if(srcWidth>=2*dstWidth){ *ratio=2; *shift=1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -