📄 s3c2440_camif.c
字号:
/* * Camera interface on S3C2440 * * bushi@mizi.com * * ToDo: * - support "V4L2_FLAG_STREAMING" * * $Revision: 1.1.1.1 $ * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive * for more details. * Oct 25 2003 SeonKon Choi <bushi@mizi.com> - initial - support "V4L2_FLAG_READ" Oct 2003 SW.LEE <hitchcar@sec.samsung.com> $\> Take care of GPEDAT (Open drain) that is why I use write_gpio_bit_clear() $\> make sure that your board support 24Mhz(or 12Mhz) Camera clock (using a internal clock) $\> make sure that S3C2440 Evalution Board version is Rev 0.17 or above $\> make sure that you jummper setting (J12 --5 voltage omnivisoin ) $\> make sure that your CAMERA modules C3188A (using a internel clock , do not use extenal clock 12MHz) have no OSC and JP1 connected. $\> s3c2440_camif.c needs the feedback from external camera modules as like the followings 1. IMGFMT ITU601,656, SWAP Order, need for Reset.. */#include <linux/config.h>#include <linux/module.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/version.h>#include <linux/wait.h>#include <linux/irq.h>#include <linux/delay.h>#include <linux/sched.h>#include <linux/locks.h>#include <linux/mm.h>#include <linux/slab.h>#include <linux/completion.h>#include "videodev.h"#include <asm/io.h>#include <asm/hardware.h>#include <asm/semaphore.h>#include <asm/uaccess.h>#include <asm/arch/cpu_s3c2440.h>#include "s3c2440_camif.h"#define OV7620_MAX_CLK 24000000 #if DEBUG#define DPRINTK(x...) printk(x)#else#define DPRINTK(x...) /* !!!! */#endif#define CAMERA_RESET() \ {CAM_CTRL |= (1<<19); \ mdelay(50); \ CAM_CTRL &= ~(1<<19); }enum { VCTRL_BRIGHTNESS = 0, VCTRL_REDBALANCE, VCTRL_BLUEBALANCE, VCTRL_EXPOSURE, VCTRL_AUTOWHITEBALANCE, NUM_OF_HW_CONTROL};static struct v4l2_queryctrl capture_control[NUM_OF_HW_CONTROL] ={ /* id, name, min, max, step, default, type, flags, category, group */ [VCTRL_BRIGHTNESS] = {V4L2_CID_BRIGHTNESS, "Brightness", 0, 255, 1, 128, V4L2_CTRL_TYPE_INTEGER, 0, V4L2_CTRL_CAT_VIDEO}, [VCTRL_REDBALANCE] = {V4L2_CID_RED_BALANCE, "RedBalance", 0, 255, 1, 128, V4L2_CTRL_TYPE_INTEGER, 0, V4L2_CTRL_CAT_VIDEO}, [VCTRL_BLUEBALANCE] = {V4L2_CID_BLUE_BALANCE, "BlueBalance", 0, 255, 1, 128, V4L2_CTRL_TYPE_INTEGER, 0, V4L2_CTRL_CAT_VIDEO}, [VCTRL_EXPOSURE] = {V4L2_CID_EXPOSURE, "Exposure", 0, 255, 1, 128, V4L2_CTRL_TYPE_INTEGER, 0, V4L2_CTRL_CAT_VIDEO}, [VCTRL_AUTOWHITEBALANCE] = {V4L2_CID_AUTO_WHITE_BALANCE, "AutoWhiteBalance", 0, 1, 1, 1, V4L2_CTRL_TYPE_BOOLEAN, 0, V4L2_CTRL_CAT_VIDEO},};typedef int (control_fn) (int in, unsigned int *out);static control_fn *hw_control_func[NUM_OF_HW_CONTROL] = {0,};static struct v4l2_fmtdesc capfmt[] = { /* index, description, pixel_format, flags, depth, reserved[2] */ { 0, {"RGB565"}, V4L2_PIX_FMT_RGB565, 0, 16, {0, 0} },#ifdef TEST_RGB32 { 1, {"RGB32(x-R-G-B)"}, V4L2_PIX_FMT_RGB32 , 0, 32, {0, 0} },#endif { 2, {"YUV 4:2:0 (planar)"}, V4L2_PIX_FMT_YUV420, V4L2_FMT_CS_601YUV, 12, {0, 0} },};#define NUM_CAPFMT ARRAY_SIZE(capfmt)static unsigned long camdev_status;static unsigned long *camdev_status_data = &camdev_status;static struct s3c2440_camif_cfg_t s3c2440_camif_cfg;static struct img_buf_t yuv_buf[4];static struct img_buf_t rgb_buf[4];#define YUV_TUPLE_SIZE (640*480*2) // maximum#define YUV_IMG_BUF_SIZE PAGE_ALIGN(YUV_TUPLE_SIZE + PAGE_SIZE)static unsigned char *camif_yuv_buf = NULL;static dma_addr_t camif_yuv_buf_dma = 0;#define RGB_TUPLE_SIZE (640*480*4) // maximum RGB32 ????#define RGB_IMG_BUF_SIZE PAGE_ALIGN(RGB_TUPLE_SIZE + PAGE_SIZE)static unsigned char *camif_rgb_buf = NULL;static dma_addr_t camif_rgb_buf_dma = 0;static struct tq_struct cam_s_task; static DECLARE_MUTEX(cam_flag);static struct s3c2440_camif camif; // v4l2 devstatic void cam_to_yuv420_sw(int frame, struct s3c2440_camif_cfg_t *cfg);static void cam_to_rgb16_sw(int frame, struct s3c2440_camif_cfg_t *cfg);static void cam_to_rgb32_sw(int frame, struct s3c2440_camif_cfg_t *cfg);static int v4l2_cam_open(struct v4l2_device *v, int flags, void **idptr);static void v4l2_cam_close(void *id);static int v4l2_cam_ioctl(void *id, unsigned int cmd, void *arg);//static int v4l2_cam_mmap(void *id, struct vm_area_struct *vma);static long v4l2_cam_read(void *id, char *buf, unsigned long count, int noblock);//static long v4l2_cam_write(void *id, const char *buf, unsigned long count, int noblock);static int v4l2_cam_poll(void *id, struct file *file, poll_table * table);static int v4l2_cam_initdone (struct v4l2_device *v);/* * $\> SW.LEE * * OV7620_CLK is 12Mhz or 24Mhz * * CAMCLK = OV7620_CLK; * CAMCLK_DIV = UPLL / ( CAMCLK * 2) - 1 ; */static unsigned int get_camera_clk(void){ return OV7620_MAX_CLK;}static void cam_gpio_init(void){ set_gpio_ctrl(GPIO_CAMDATA0); set_gpio_ctrl(GPIO_CAMDATA1); set_gpio_ctrl(GPIO_CAMDATA2); set_gpio_ctrl(GPIO_CAMDATA3); set_gpio_ctrl(GPIO_CAMDATA4); set_gpio_ctrl(GPIO_CAMDATA5); set_gpio_ctrl(GPIO_CAMDATA6); set_gpio_ctrl(GPIO_CAMDATA7); set_gpio_ctrl(GPIO_CAMPCLKIN); set_gpio_ctrl(GPIO_CAMVSYNC); set_gpio_ctrl(GPIO_CAMHREF); set_gpio_ctrl(GPIO_CAMPCLKOUT); set_gpio_ctrl(GPIO_CAMRESET);}static void inline s3c2440_camif_init(void){ unsigned int upll, uclk, camclk,camclk_div; cam_gpio_init(); camclk = get_camera_clk(); CLKCON |= CLKCON_CAMIF; /* Supposed that you must set UPLL at first */ UPLLCON = FInsrt(0x38, fPLL_MDIV) | FInsrt(0x02, fPLL_PDIV) | FInsrt(0x02, fPLL_SDIV); upll = s3c2440_get_bus_clk(GET_UPLL); uclk = (CLKDIVN & DIVN_UPLL_EN) ? upll/2 : upll; printk("CAMERA : UPLL %08d UCLK %08d CAMCLK %08d \n",upll, uclk, camclk); camclk_div = upll /( camclk * 2) -1 ; CAMDIVN = CAMCLK_SET_DIV | (camclk_div & 0xf ) ; CAMERA_RESET(); mdelay(100); }static void s3c2440_camif_deinit(void){ CLKCON &= ~CLKCON_CAMIF; CAM_CTRL = 0; mdelay(5);}static void cfg_dma_burst(int width){ int y_mburst, y_rburst; int c_mburst, c_rburst;#if 0 switch (width) { case 1024: /* XGA 1024x768 */ y_mburst = c_mburst = 16; break; case 800: /* SVGA 800x600 */ y_mburst = c_mburst = 16; break; case 640: /* VGA 640x480 */ y_mburst = c_mburst = 16; break; case 352: /* CIF 352x288 */ y_mburst = 8; c_mburst = 4; break; case 320: /* QVGA 320x240 */ y_mburst = c_mburst = 8; break; case 176: /* QCIF 176x144 */ /* fall throu */ default: y_mburst = c_mburst = 4; break; }#endif y_mburst = c_mburst = 4; y_rburst = (width / 4) % y_mburst; /* 4 means basic burst unit */ y_rburst = y_rburst ? y_rburst : y_mburst; CAM_AYBURST = CAM_BURST_M(y_mburst) | CAM_BURST_R(y_rburst); DPRINTK("YBURST = %d,%d = 0x%08lx\n", y_mburst, y_rburst , CAM_BURST_M(y_mburst) | CAM_BURST_R(y_rburst)); c_rburst = (width / 2 / 4) % c_mburst; c_rburst = c_rburst ? c_rburst : c_mburst; CAM_ACBBURST = CAM_BURST_M(c_mburst) | CAM_BURST_R(c_rburst); CAM_ACRBURST = CAM_BURST_M(c_mburst) | CAM_BURST_R(c_rburst); DPRINTK("CBBURST = %d,%d = 0x%08lx\n", c_mburst, c_rburst , CAM_BURST_M(c_mburst) | CAM_BURST_R(c_rburst));}static void s3c2440_camif_configure(struct s3c2440_camif_cfg_t *cfg){ int i; unsigned long adist, awidth; unsigned long hratio, vratio; for (i = 0; i < 4; i++) { yuv_buf[i].buf = camif_yuv_buf; yuv_buf[i].phys_addr = (unsigned int)camif_yuv_buf_dma; DPRINTK("yuv_buf[%d] : virt=0x%08lx, phys = 0x%08lx\n", i, (unsigned long)yuv_buf[i].buf, (unsigned long)yuv_buf[i].phys_addr); rgb_buf[i].buf = camif_rgb_buf; rgb_buf[i].phys_addr = (unsigned int)camif_rgb_buf_dma; DPRINTK("rgb_buf[%d] : virt=0x%08lx, phys = 0x%08lx\n", i, (unsigned long)rgb_buf[i].buf, (unsigned long)rgb_buf[i].phys_addr); CAM_STAY(i) = yuv_buf[i].phys_addr; DPRINTK("STAY%d = 0x%08x\n", i, yuv_buf[i].phys_addr); CAM_STACB(i) = yuv_buf[i].phys_addr + cfg->dev->clientfmt.width * cfg->dev->clientfmt.height; DPRINTK("STACB%d = 0x%08x\n", i, yuv_buf[i].phys_addr + cfg->dev->clientfmt.width * cfg->dev->clientfmt.height); CAM_STACR(i) = yuv_buf[i].phys_addr + cfg->dev->clientfmt.width * cfg->dev->clientfmt.height / 2 * 3; DPRINTK("STACR%d = 0x%08x\n", i, yuv_buf[i].phys_addr + cfg->dev->clientfmt.width * cfg->dev->clientfmt.height / 2 * 3); } DPRINTK("CAMIF: src = (%dx%d), dst = (%dx%d)\n", cfg->src_x, cfg->src_y, cfg->dev->clientfmt.width, cfg->dev->clientfmt.height); CAM_ASIZE = CAM_SIZE_H(cfg->dev->clientfmt.width) | CAM_SIZE_V(cfg->dev->clientfmt.height); cfg_dma_burst(cfg->dev->clientfmt.width); /* * Adist = ( MemoryBusClk/CameraClk ) * 3 * Width * * CameraClk = UPLL / [(CAMCLK_DIV + 1) * 2] */ adist = (s3c2440_get_bus_clk(GET_HCLK) * 3 * cfg->src_x) / get_camera_clk(); awidth = adist; CAM_ADISTWIDTH = CAM_DISTWIDTH_D(adist) | CAM_DISTWIDTH_W(awidth); DPRINTK("HCLK = %ld, pixel_clock = %ld, adist = %ld\n" , s3c2440_get_bus_clk(GET_HCLK) , get_camera_clk() , adist); DPRINTK("CAM_ADISTWIDTH = 0x%08lx\n" , CAM_DISTWIDTH_D(adist) | CAM_DISTWIDTH_W(awidth)); hratio = (cfg->src_x * 4096) / cfg->dev->clientfmt.width; vratio = (cfg->src_y * 4096) / cfg->dev->clientfmt.height; CAM_YRATIO = CAM_RATIO_H(hratio) | CAM_RATIO_V(vratio); DPRINTK("YRATIO = %ld,%ld = 0x%08lx\n", hratio, vratio , CAM_RATIO_H(hratio) | CAM_RATIO_V(vratio)); hratio = ((cfg->src_x/2) * 4096) / (cfg->dev->clientfmt.width/2); vratio = (cfg->src_y * 4096) / (cfg->dev->clientfmt.height/2); CAM_CRATIO = CAM_RATIO_H(hratio) | CAM_RATIO_V(vratio); DPRINTK("CRATIO = %ld,%ld = 0x%08lx\n", hratio, vratio , CAM_RATIO_H(hratio) | CAM_RATIO_V(vratio)); CAM_YORIGINAL = CAM_ORIGINAL_H(cfg->src_x) | CAM_ORIGINAL_V(cfg->src_y); DPRINTK("YORIGINAL = 0x%08lx\n", CAM_ORIGINAL_H(cfg->src_x) | CAM_ORIGINAL_V(cfg->src_y)); CAM_CORIGINAL = CAM_ORIGINAL_H((cfg->src_x/2)) | CAM_ORIGINAL_V(cfg->src_y); DPRINTK("CORIGINAL = 0x%08lx\n", CAM_ORIGINAL_H((cfg->src_x/2)) | CAM_ORIGINAL_V(cfg->src_y)); if (cfg->use_vpost) { panic("Not Supporting H/W post-processing \n"); } else { switch (cfg->dev->clientfmt.pixelformat ) { case V4L2_PIX_FMT_RGB565: cfg->camif_frame_handler = cam_to_rgb16_sw; break;#ifdef TEST_RGB32 case V4L2_PIX_FMT_RGB32: cfg->camif_frame_handler = cam_to_rgb32_sw; break;#endif case V4L2_PIX_FMT_YUV420: cfg->camif_frame_handler = cam_to_yuv420_sw; break; default: panic("unknown destination type.\n"); } } if (!cfg->camif_frame_handler) panic("\nunknown dst_type.\n");}static void cam_s_task_handler(void * data){ struct s3c2440_camif_cfg_t *cfg = (struct s3c2440_camif_cfg_t *)data; int frame = (CAM_STAT_FRAME(CAM_RDSTAT) ); // + 3) % 4;// DPRINTK("frame = %d\n", frame); cfg->camif_frame_handler(frame, cfg);}static void s3c2440_camif_isr_s(int irq, void *dev_id, struct pt_regs *regs){ schedule_task(&cam_s_task);}#define XLATTABSIZE 256#define MulDiv(x, y, z) ((long)((int) x * (int) y) / (int) z)#define CLIP(x) {if(x<0) x=0;if(x>255) x=255;}#define RED_REGION 0xf800#define GREEN_REGION 0x07e0#define BLUE_REGION 0x001fstatic int XlatY[XLATTABSIZE] = { 0 };static int XlatV_B[XLATTABSIZE] = { 0 };static int XlatV_G[XLATTABSIZE] = { 0 };static int XlatU_G[XLATTABSIZE] = { 0 };static int XlatU_R[XLATTABSIZE] = { 0 };static void __init init_yuvtable (void){ int i, j; for (i = 0; i < XLATTABSIZE; i++) { j = min_t(int, 253, max_t(int, 16, i)); // orig: XlatY[i] = (int ) j; XlatY[i] = j-16; } for (i = 0; i < XLATTABSIZE; i++) { j = min_t(int, 240, max_t(int, 16, i)); j -= 128; XlatV_B[i] = MulDiv (j, 1000, 564); /* j*219/126 */ XlatV_G[i] = MulDiv (j, 1100, 3328); XlatU_G[i] = MulDiv (j, 3100, 4207); XlatU_R[i] = MulDiv (j, 1000, 713); }}static void inline yuv_convert_rgb16(unsigned char *rawY, unsigned char *rawU, unsigned char *rawV, unsigned char *rgb, int size){ unsigned short buf1, buf3; int red; int blue; int green; unsigned long cnt; int Y, U, V; unsigned short data; unsigned short data2; for ( cnt = 0 ; cnt < size; cnt +=2){ buf1 = *(rawY+cnt) & 0xff; // Y data buf3 = *(rawY+cnt+1) & 0xff; // Y data U = *(rawV+cnt/2) & 0xff; V = *(rawU+cnt/2) & 0xff; Y = buf1; red = XlatY[Y] + XlatU_R[U]; CLIP(red); green = XlatY[Y] - XlatV_G[V] - XlatU_G[U]; CLIP(green); blue = XlatY[Y] + XlatV_B[V]; CLIP(blue); data = ((red << 8) & RED_REGION) | ((green << 3) & GREEN_REGION) | (blue >> 3); Y = buf3; red = XlatY[Y] + XlatU_R[U]; CLIP(red); green = XlatY[Y] - XlatV_G[V] - XlatU_G[U]; CLIP(green); blue = XlatY[Y] + XlatV_B[V]; CLIP(blue); data2 = ((red << 8) & RED_REGION) | ((green << 3) & GREEN_REGION) | (blue >> 3); *(unsigned short *)(rgb + 2 * cnt) = data; *(unsigned short *)(rgb + 2 * (cnt + 1))= data2; }}static void inline yuv_convert_rgb32(unsigned char *rawY, unsigned char *rawU, unsigned char *rawV, unsigned char *rgb, int size){ unsigned short buf1, buf3; int red; int blue; int green; unsigned long cnt; int Y, U, V; for ( cnt = 0 ; cnt < size; cnt +=2){ buf1 = *(rawY+cnt) & 0xff; // Y data buf3 = *(rawY+cnt+1) & 0xff; // Y data U = *(rawV+cnt/2) & 0xff; V = *(rawU+cnt/2) & 0xff; Y = buf1; red = XlatY[Y] + XlatU_R[U]; CLIP(red); green = XlatY[Y] - XlatV_G[V] - XlatU_G[U]; CLIP(green); blue = XlatY[Y] + XlatV_B[V]; CLIP(blue); *(unsigned int*)(rgb) = (red << 16) | (green << 8) | blue; rgb += 4; Y = buf3; red = XlatY[Y] + XlatU_R[U]; CLIP(red); green = XlatY[Y] - XlatV_G[V] - XlatU_G[U];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -