📄 s3c2440_ov7620.c
字号:
/* * 1. Orignal : * OV7620 on S3C24A0 CAMIF * bushi@mizi.com * * 2. 2003-09-01: SW.LEE <hitchcar@sec.samsung.com> * <S3C2440 > * $\> Camera Clock Set. * make sure that your board support 24Mhz * Change OV7620 Clock from 12Mhz to 24Mhz * $\> Take care of GPEDAT (Open drain) * that is why I use write_gpio_bit_clear() * $\> if you have no image from camera, DISABLE ov7620_config() * if it works, change SCCB_DELAY * * 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. */#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 <asm/io.h>#include <asm/semaphore.h>#include <asm/hardware.h>#include <asm/uaccess.h>#include <asm/arch/cpu_s3c2440.h>#include "smdk2440_ov7620.h"#include <asm/arch/S3C2440.h>#define DEBUG_OVERLAY_TO_FB 0#define DEBUG_WORK_STANDALONE 0//#define DEBUG_PRINTK//#define DEBUG_PRINTK2#ifdef DEBUG_PRINTK#define DPRINTK(x...) printk(x)#else#define DPRINTK(x...) do {} while (0) /* !!!! */#endif#ifdef DEBUG_PRINTK2#define DPRINTK2(x...) printk(x)#else#define DPRINTK2(x...) do {} while (0) /* !!!! */#endif#define CAMIF_TYPE_RGB16 0#define CAMIF_TYPE_RGB32 1#define CAMIF_TYPE_YUV420 2#define CAM_SRC_X 640#define CAM_SRC_Y 480#define CAMIF_DST_X 240#define CAMIF_DST_Y 180static int camif_dst_x = CAMIF_DST_X;static int camif_dst_y = CAMIF_DST_Y;#define MORE_STABLE_BUT_SLOW 0#define CAMIF_DEFAULT_FMT CAMIF_TYPE_RGB16/**************** global **************/static struct s3c2440_camif_cfg_t { int src_x; int src_y; int dst_x; int dst_y; int use_vpost; int dst_type; void (*camif_frame_handler)(int frame, struct s3c2440_camif_cfg_t *cfg);} s3c2440_camif_cfg;struct img_buf_t { unsigned int phys_addr; unsigned char *buf;};static int frame_to_user = 0;static struct img_buf_t yuv_buf[4];static struct img_buf_t rgb_buf[4];#define CAPTURE_PORT_A() \ {CAM_CTRL = (1<<20) /*| (1<<13) */| (1<<18) | (1<<17) | (1<<6);}static DECLARE_WAIT_QUEUE_HEAD(camdev_wait);static unsigned long camdev_cam_status;static unsigned char *camdev_status_data = (unsigned char*)&camdev_cam_status;#define CAM_CAPTURING 1#define CAM_OPENED 2#define SET_CAM(x) set_bit(x, camdev_status_data)#define IS_CAM(x) test_bit(x, camdev_status_data)#define UNSET_CAM(x) clear_bit(x, camdev_status_data)#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_TUPLE_SIZE (640*480*2)#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;#if MORE_STABLE_BUT_SLOW#define USER_TUPLE_SIZE RGB_TUPLE_SIZEstatic unsigned char *camif_user_buf = NULL;static DECLARE_WAIT_QUEUE_HEAD(cam_wait);#endif/********* end of global **************//*********** start of OV7620 ********************//* refer to 25p of OV7620 datasheet */# define OV7620_SCCB_ID 0x42 /* CS[2:0] = 000 */# define OV7620_MAX_CLK 24000000 // 24MHz# define OV7620_PRODUCT_ID 0x7FA2# define OV7620_SCCB_DELAY 100 # define OV7620_SCCB_DELAY2 100# define SIO_C (GPIO_E14)# define SIO_D (GPIO_E15)# define MAKE_HIGH(_x) write_gpio_bit_set(_x)# define MAKE_LOW(_x) write_gpio_bit_clear(_x)# define BIT_READ(_x) read_gpio_bit(_x)# define CFG_READ(_x) set_gpio_ctrl(_x | GPIO_PULLUP_DIS | GPIO_MODE_IN)# define CFG_WRITE(_x) set_gpio_ctrl(_x | GPIO_PULLUP_DIS | GPIO_MODE_OUT)#if OV7620_SCCB_DELAY > 0# define WAIT_CYL udelay(OV7620_SCCB_DELAY)#else# define WAIT_CYL (void)(0)#endif#if OV7620_SCCB_DELAY2 > 0# define WAIT_STAB udelay(OV7620_SCCB_DELAY2)#else# define WAIT_STAB (void)(0)#endifstatic unsigned int get_camera_clk(void){ return OV7620_MAX_CLK;}/* 2-wire SCCB */void inline ov7620_sccb_start(void){ MAKE_HIGH(SIO_C); MAKE_HIGH(SIO_D); WAIT_STAB; MAKE_LOW(SIO_D); WAIT_STAB; MAKE_LOW(SIO_C); WAIT_STAB;}/* 2-wire SCCB */void inline ov7620_sccb_end(void){ MAKE_LOW(SIO_D); WAIT_STAB; MAKE_HIGH(SIO_C); WAIT_STAB; MAKE_HIGH(SIO_D); WAIT_STAB;}void inline ov7620_sccb_write_bit(unsigned char bit){ if (bit) MAKE_HIGH(SIO_D); else MAKE_LOW(SIO_D); WAIT_STAB; MAKE_HIGH(SIO_C); WAIT_CYL; MAKE_LOW(SIO_C); WAIT_STAB;}int inline ov7620_sccb_read_bit(void){ int tmp = 0; MAKE_HIGH(SIO_C); WAIT_CYL; tmp = BIT_READ(SIO_D); MAKE_LOW(SIO_C); WAIT_STAB; return tmp;} void inline ov7620_sccb_writechar(unsigned char data){ int i = 0; /* data */ for (i = 0; i < 8; i++ ) { ov7620_sccb_write_bit(data & 0x80); data <<= 1; } /* 9th bit - Don't care */ ov7620_sccb_write_bit(1);}void inline ov7620_sccb_readchar(unsigned char *val){ int i; int tmp = 0; CFG_READ(SIO_D); for (i = 7; i >= 0; i--) tmp |= ov7620_sccb_read_bit() << i; CFG_WRITE(SIO_D); /* 9th bit - N.A. */ ov7620_sccb_write_bit(1); *val = tmp & 0xff;}/* 3-phase write */static void ov7620_sccb_sendbyte(unsigned char subaddr, unsigned char data){// down(&dev.bus_lock); ov7620_sccb_start(); ov7620_sccb_writechar(OV7620_SCCB_ID); ov7620_sccb_writechar(subaddr); ov7620_sccb_writechar(data); ov7620_sccb_end(); mdelay(7);// up(&dev.bus_lock);}/* 2-phase read */static unsigned char ov7620_sccb_receivebyte(unsigned char subaddr){ unsigned char value;// down(&dev.bus_lock); /* 2-phase write */ ov7620_sccb_start(); ov7620_sccb_writechar(OV7620_SCCB_ID); ov7620_sccb_writechar(subaddr); ov7620_sccb_end(); /* 2-phase read */ ov7620_sccb_start(); ov7620_sccb_writechar(OV7620_SCCB_ID | 0x01); ov7620_sccb_readchar(&value); ov7620_sccb_end(); mdelay(7);// up(&dev.bus_lock); return value;}void inline ov7620_init(void){ CFG_WRITE(SIO_C); CFG_WRITE(SIO_D); mdelay(10);}void inline ov7620_deinit(void){ CFG_READ(SIO_C); CFG_READ(SIO_D);}void inline ov7620_config(void){ int i; for (i = 0; i < OV7620_REGS; i++) { if (ov7620_reg[i].subaddr == CHIP_DELAY) mdelay(ov7620_reg[i].value); else ov7620_sccb_sendbyte(ov7620_reg[i].subaddr & 0xff , ov7620_reg[i].value & 0xff); }}int inline check_ov7620(void){ int ret = 0; int ov7620_mid = 0; int try_count =0;try_again: ov7620_mid = (ov7620_sccb_receivebyte(0x1c) << 8); ov7620_mid |= ov7620_sccb_receivebyte(0x1d); if (ov7620_mid != OV7620_PRODUCT_ID) { if (!try_count++) goto try_again; printk("Invalid manufacture ID (0x%04X). there is no OV7620(0x%04X)\n", ov7620_mid, OV7620_PRODUCT_ID); ret = -ENODEV; } else { printk("OV7620(0x%04X) detected.\n", ov7620_mid); } return ret;}/********* end of OV7620 ********************//********** start of S/W YUV2RGB ************/#define XLATTABSIZE 256#define MulDiv(x, y, z) ((long)((int) x * (int) y) / (int) z)//#define CLIP(x) min_t(int, 255, max_t(int, 0, (x)))#define CLIP(x) {if(x<0) x=0;if(x>255) x=255;}#define RED_REGION 0xf800#define GREEN_REGION 0x07e0#define BLUE_REGION 0x001fint XlatY[XLATTABSIZE] = { 0 };int XlatV_B[XLATTABSIZE] = { 0 };int XlatV_G[XLATTABSIZE] = { 0 };int XlatU_G[XLATTABSIZE] = { 0 };int XlatU_R[XLATTABSIZE] = { 0 };#define ORIG_XLAT 1void __init init_yuvtable (void){ int i, j; for (i = 0; i < XLATTABSIZE; i++) {#if ORIG_XLAT j = min_t(int, 253, max_t(int, 16, i));#else j = (255 * i + 110) / 220; // scale up j = min_t(int, 255, max_t(int, j, 16));#endif // orig: XlatY[i] = (int ) j; XlatY[i] = j-16; } for (i = 0; i < XLATTABSIZE; i++) {#if ORIG_XLAT j = min_t(int, 240, max_t(int, 16, i)); j -= 128;#else j = i - 128; // make signed if (j < 0) j++; // noise reduction j = (127 * j + 56) / 112; // scale up j = min_t(int, 127, max_t(int, -128, j));#endif 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); }}#define MORE_QUALITY 1void 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;#if MORE_QUALITY Y = buf1;#else Y = ((buf1+buf3)/2);#endif 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);#if MORE_QUALITY 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);#else data2 = data;#endif *(unsigned short *)(rgb + 2 * cnt) = data; *(unsigned short *)(rgb + 2 * (cnt + 1))= data2; }}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;#if MORE_QUALITY Y = buf1;#else Y = ((buf1+buf3)/2);#endif 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;#if MORE_QUALITY 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);#endif *(unsigned int*)(rgb) = (red << 16) | (green << 8) | blue; rgb += 4; }}/********** end of S/W YUV2RGB ******************//************** start of CAMIF **********/static struct tq_struct cam_s_task; static void cam_to_rgb16_sw(int frame, struct s3c2440_camif_cfg_t *cfg){ int i; for (i = 0; i < cfg->dst_y; i++) yuv_convert_rgb16(yuv_buf[frame].buf + i * cfg->dst_x, yuv_buf[frame].buf + cfg->dst_x*cfg->dst_y + (i/2) * cfg->dst_x / 2, yuv_buf[frame].buf + cfg->dst_x*cfg->dst_y/2*3 + (i/2) * cfg->dst_x / 2, rgb_buf[frame].buf + i * cfg->dst_x * 2, cfg->dst_x); frame_to_user = frame;#define CAMTEST_FB_BASE 0xC0381000 #if DEBUG_OVERLAY_TO_FB /* * for TEST * overlay to 240x320 FB */ { int k; int maxy = min_t(int, 320, cfg->dst_y); int maxx = min_t(int, 240, cfg->dst_x); unsigned char *fb_base = (unsigned char *)(CAMTEST_FB_BASE); for (k = 0; k < maxy; k++) memcpy( fb_base + (240 * 2 * k), rgb_buf[frame].buf + (cfg->dst_x * 2 * k), maxx * 2);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -