- /* 第一个参数名字用于匹配,第二个参数是摄像头地址 */
- static struct i2c_board_info cmos_ov7740_info = {
- /* 由芯片手册可知,写 -- 0x42(01000010),读 -- 0x43(01000011)
- * 将读地址或者写地址,向右移动一位即可。
- */
- I2C_BOARD_INFO("cmos_ov7740", 0x21),
- };
- static struct i2c_client *cmos_ov7740_client;
- static int cmos_ov7740_dev_init(void)
- {
- struct i2c_adapter *i2c_adap;
- /* 获得适配器,由2440原理图可知,2440只有一个iic控制器,所以iic控制器为适配器0,
- * 也就是0号iic总线上
- */
- i2c_adap = i2c_get_adapter(0);
- cmos_ov7740_client = i2c_new_device(i2c_adap, &cmos_ov7740_info);
- /* 适配器使用完后要释放他 */
- i2c_put_adapter(i2c_adap);
- return 0;
- }
- static void cmos_ov7740_dev_exit(void)
- {
- i2c_unregister_device(cmos_ov7740_client);
- }
- module_init(cmos_ov7740_dev_init);
- module_exit(cmos_ov7740_dev_exit);
- MODULE_LICENSE("GPL");
- cmos_ov7740_drv.c
- #define OV7740_INIT_REGS_SIZE (sizeof(ov7740_setting_30fps_VGA_640_480)/sizeof(ov7740_setting_30fps_VGA_640_480[0]))
- #define CAM_SRC_HSIZE (640)
- #define CAM_SRC_VSIZE (480)
- #define CAM_ORDER_YCbYCr (0)
- #define CAM_ORDER_YCrYCb (1)
- #define CAM_ORDER_CbYCrY (2)
- #define CAM_ORDER_CrYCbY (3)
- #define WinHorOfst (0)
- #define WinVerOfst (0)
- struct cmos_ov7740_scaler {
- unsigned int PreHorRatio;//水平变比
- unsigned int PreVerRatio;//垂直变比
- unsigned int H_Shift;//水平比
- unsigned int V_Shift;//垂直比
- unsigned int PreDstWidth;//目标宽度
- unsigned int PreDstHeight;//目标高度
- unsigned int MainHorRatio;//预览主缩放的水平比
- unsigned int MainVerRatio;//预览主缩放的垂直比
- unsigned int SHfactor;//缩放变比
- unsigned int ScaleUpDown;//放大缩小标志
- };
- static struct cmos_ov7740_scaler sc;
- typedef struct cmos_ov7740_i2c_value {
- unsigned char regaddr;
- unsigned char value;
- }ov7740_t;
- /* init: 640x480,30fps的,YUV422输出格式 */
- ov7740_t ov7740_setting_30fps_VGA_640_480[] =
- {
- {0x12, 0x80},
- {0x47, 0x02},
- {0x17, 0x27},
- {0x04, 0x40},
- {0x1B, 0x81},
- {0x29, 0x17},
- {0x5F, 0x03},
- {0x3A, 0x09},
- {0x33, 0x44},
- {0x68, 0x1A},
- {0x14, 0x38},
- {0x5F, 0x04},
- {0x64, 0x00},
- {0x67, 0x90},
- {0x27, 0x80},
- {0x45, 0x41},
- {0x4B, 0x40},
- {0x36, 0x2f},
- {0x11, 0x01},
- {0x36, 0x3f},
- {0x0c, 0x12},
- {0x12, 0x00},
- {0x17, 0x25},
- {0x18, 0xa0},
- {0x1a, 0xf0},
- {0x31, 0xa0},
- {0x32, 0xf0},
- {0x85, 0x08},
- {0x86, 0x02},
- {0x87, 0x01},
- {0xd5, 0x10},
- {0x0d, 0x34},
- {0x19, 0x03},
- {0x2b, 0xf8},
- {0x2c, 0x01},
- {0x53, 0x00},
- {0x89, 0x30},
- {0x8d, 0x30},
- {0x8f, 0x85},
- {0x93, 0x30},
- {0x95, 0x85},
- {0x99, 0x30},
- {0x9b, 0x85},
- {0xac, 0x6E},
- {0xbe, 0xff},
- {0xbf, 0x00},
- {0x38, 0x14},
- {0xe9, 0x00},
- {0x3D, 0x08},
- {0x3E, 0x80},
- {0x3F, 0x40},
- {0x40, 0x7F},
- {0x41, 0x6A},
- {0x42, 0x29},
- {0x49, 0x64},
- {0x4A, 0xA1},
- {0x4E, 0x13},
- {0x4D, 0x50},
- {0x44, 0x58},
- {0x4C, 0x1A},
- {0x4E, 0x14},
- {0x38, 0x11},
- {0x84, 0x70}
- };
- struct cmos_ov7740_fmt {
- char *name;
- u32 fourcc; /* v4l2 format id */
- int depth;
- };
- /* 这两个格式仅仅是预览通道支持的格式,由控制器的芯片手册可知 */
- static struct cmos_ov7740_fmt formats[] = {
- {
- .name = "RGB565",
- .fourcc = V4L2_PIX_FMT_RGB565,
- .depth = 16,
- },
- {
- .name = "PACKED_RGB_888",
- .fourcc = V4L2_PIX_FMT_RGB24,
- .depth = 24,
- },
- };
- struct camif_buffer
- {
- unsigned int order;
- unsigned long virt_base;
- unsigned long phy_base;
- };
- struct camif_buffer img_buff[] =
- {
- {
- .order = 0,
- .virt_base = (unsigned long)NULL,
- .phy_base = (unsigned long)NULL
- },
- {
- .order = 0,
- .virt_base = (unsigned long)NULL,
- .phy_base = (unsigned long)NULL
- },
- {
- .order = 0,
- .virt_base = (unsigned long)NULL,
- .phy_base = (unsigned long)NULL
- },
- {
- .order = 0,
- .virt_base = (unsigned long)NULL,
- .phy_base = (unsigned long)NULL
- }
- };
- static struct i2c_client *cmos_ov7740_client;
- // CAMIF GPIO
- static unsigned long *GPJCON;
- static unsigned long *GPJDAT;
- static unsigned long *GPJUP;
- // CAMIF
- static unsigned long *CISRCFMT;
- static unsigned long *CIWDOFST;
- static unsigned long *CIGCTRL;
- static unsigned long *CIPRCLRSA1;
- static unsigned long *CIPRCLRSA2;
- static unsigned long *CIPRCLRSA3;
- static unsigned long *CIPRCLRSA4;
- static unsigned long *CIPRTRGFMT;
- static unsigned long *CIPRCTRL;
- static unsigned long *CIPRSCPRERATIO;
- static unsigned long *CIPRSCPREDST;
- static unsigned long *CIPRSCCTRL;
- static unsigned long *CIPRTAREA;
- static unsigned long *CIIMGCPT;
- // IRQ
- static unsigned long *SRCPND;
- static unsigned long *INTPND;
- static unsigned long *SUBSRCPND;
- static unsigned int SRC_Width, SRC_Height;
- static unsigned int TargetHsize_Pr, TargetVsize_Pr;
- static unsigned long buf_size;
- static unsigned int bytesperline;
- static DECLARE_WAIT_QUEUE_HEAD(cam_wait_queue);
- /* 中断标志 */
- static volatile int ev_cam = 0;
- static irqreturn_t cmos_ov7740_camif_irq_c(int irq, void *dev_id)
- {
- return IRQ_HANDLED;
- }
- static irqreturn_t cmos_ov7740_camif_irq_p(int irq, void *dev_id)
- {
- /* 清中断,因为不是使用上升沿或者下降沿触发,所以我们要清楚中断标志位
- * 确保下次能继续产生中断。由中断控制器的手册394页可知,向相应的寄存器写入1
- * 就是清除相应的中断标识,写入0表示保持原来的标志不变。
- */
- *SRCPND = 1<<6;
- *INTPND = 1<<6;
- *SUBSRCPND = 1<<12 ev_cam="1;" wake_up_interruptible="" cam_wait_queue="" return="" irq_handled="" a2="" uvc_v4l2_do_ioctl="" static="" int="" cmos_ov7740_vidioc_querycap="" struct="" file="" file="" void="" priv="" struct="" v4l2_capability="" cap="" memset="" cap="" 0="" sizeof="" cap="" strcpy="" cap-="">driver, "cmos_ov7740");
- strcpy(cap->card, "cmos_ov7740");
- cap->version = 2;
- /* V4L2_CAP_READWRITE表示获取摄像头数据使用过读写操作,不是通过流操作
- * 所谓流操作就是通过qbuf,dqbuf,mmap,poll等函数来获得数据的
- */
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
- return 0;
- }
- /* A3 列举支持哪种格式
- * 参考: uvc_fmts 数组
- */
- static int cmos_ov7740_vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
- {
- struct cmos_ov7740_fmt *fmt;
- if (f->index >= ARRAY_SIZE(formats))
- return -EINVAL;
- fmt = &formats[f->index];
- strlcpy(f->description, fmt->name, sizeof(f->description));
- f->pixelformat = fmt->fourcc;
- return 0;
- }
- /* A4 返回当前所使用的格式 */
- static int cmos_ov7740_vidioc_g_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
- {
- return 0;
- }
- /* A5 测试驱动程序是否支持某种格式, 强制设置该格式
- * 参考: uvc_v4l2_try_format
- * myvivi_vidioc_try_fmt_vid_cap
- */
- static int cmos_ov7740_vidioc_try_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
- {
- /* 判断是否为摄像头设备 */
- if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- {
- return -EINVAL;
- }
- /* 在coms中的CICOSCCTRL寄存器中可知,在预览通道只支持RGB16/RGB24 */
- if ((f->fmt.pix.pixelformat != V4L2_PIX_FMT_RGB565) && (f->fmt.pix.pixelformat != V4L2_PIX_FMT_RGB24))
- return -EINVAL;
- return 0;
- }
- /* A6 参考 myvivi_vidioc_s_fmt_vid_cap
- * 设置摄像头数据格式
- */
- static int cmos_ov7740_vidioc_s_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
- {
- int ret = cmos_ov7740_vidioc_try_fmt_vid_cap(file, NULL, f);
- if (ret < 0)
- return ret;
- /* 目标图像的水平像素大小 */
- TargetHsize_Pr = f->fmt.pix.width;
- /* 目标图像的垂直像素大小 */
- TargetVsize_Pr = f->fmt.pix.height;
- if(f->fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565)
- {
- /* bit[30]: 设置图像输出格式是RGB16、RGB24 */
- *CIPRSCCTRL &= ~(1<<30 f-="">fmt.pix.bytesperline = (f->fmt.pix.width * 16) >> 3;
- f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
- buf_size = f->fmt.pix.sizeimage;
- bytesperline = f->fmt.pix.bytesperline;
- }
- else if(f->fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24)
- {
- /* bit[30]: 设置图像输出格式是RGB16、RGB24 */
- *CIPRSCCTRL |= (1<<30 rgb24="" 32="" s3c2440="" 515="" f-="">fmt.pix.bytesperline = (f->fmt.pix.width * 32) >> 3;
- f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
- buf_size = f->fmt.pix.sizeimage;
- bytesperline = f->fmt.pix.bytesperline;
- }
- /*
- CIPRTRGFMT:
- bit[28:16] -- 表示目标图片的水平像素大小(TargetHsize_Pr)
- bit[15:14] -- 是否旋转,我们这个驱动就不选择了
- bit[12:0] -- 表示目标图片的垂直像素大小(TargetVsize_Pr)
- */
- *CIPRTRGFMT = (TargetHsize_Pr<<16)|(0x0<<14)|(TargetVsize_Pr<<0 return="" 0="" ciprclrsa1="" static="" int="" cmos_ov7740_vidioc_reqbufs="" struct="" file="" file="" void="" priv="" struct="" v4l2_requestbuffers="" p="" unsigned="" int="" order="" __get_free_pages="" get_order="" order="get_order(buf_size);" img_buff="" 0="" order="order;" __get_free_pages="" 4m="" kmalloc="" 128k="" img_buff="" 0="" virt_base="__get_free_pages(GFP_KERNEL|__GFP_DMA," img_buff="" 0="" order="" if="" img_buff="" 0="" virt_base="=" unsigned="" long="" null="" printk="" error0="" n="" goto="" error0="" ciprclrsa1="" img_buff="" 0="" phy_base="__virt_to_phys(img_buff[0].virt_base);" img_buff="" 1="" order="order;" img_buff="" 1="" virt_base="__get_free_pages(GFP_KERNEL|__GFP_DMA," img_buff="" 1="" order="" if="" img_buff="" 1="" virt_base="=" unsigned="" long="" null="" printk="" error1="" n="" goto="" error1="" img_buff="" 1="" phy_base="__virt_to_phys(img_buff[1].virt_base);" img_buff="" 2="" order="order;" img_buff="" 2="" virt_base="__get_free_pages(GFP_KERNEL|__GFP_DMA," img_buff="" 2="" order="" if="" img_buff="" 2="" virt_base="=" unsigned="" long="" null="" printk="" error2="" n="" goto="" error2="" img_buff="" 2="" phy_base="__virt_to_phys(img_buff[2].virt_base);" img_buff="" 3="" order="order;" img_buff="" 3="" virt_base="__get_free_pages(GFP_KERNEL|__GFP_DMA," img_buff="" 3="" order="" if="" img_buff="" 3="" virt_base="=" unsigned="" long="" null="" printk="" error3="" n="" goto="" error3="" img_buff="" 3="" phy_base="__virt_to_phys(img_buff[3].virt_base);" ciprclrsa1="" ciprclrsa1="img_buff[0].phy_base;" ciprclrsa2="img_buff[1].phy_base;" ciprclrsa3="img_buff[2].phy_base;" ciprclrsa4="img_buff[3].phy_base;" return="" 0="" error3:="" free_pages="" img_buff="" 2="" virt_base="" order="" img_buff="" 2="" phy_base="(unsigned" long="" null="" error2:="" free_pages="" img_buff="" 1="" virt_base="" order="" img_buff="" 1="" phy_base="(unsigned" long="" null="" error1:="" free_pages="" img_buff="" 0="" virt_base="" order="" img_buff="" 0="" phy_base="(unsigned" long="" null="" error0:="" return="" -enomem="" 2440="" 532="" static="" void="" calculateburstsize="" unsigned="" int="" hsize="" unsigned="" int="" mainbusrtsize="" unsigned="" int="" remainedbustsize="" unsigned="" int="" tmp="" 2="" 4="" 8="" 16="" 16="" tmp="(hSize/4)%16;" switch="" tmp="" case="" 0:="" mainbusrtsize="16;" remainedbustsize="16;" break="" case="" 4:="" mainbusrtsize="16;" remainedbustsize="4;" break="" case="" 8:="" mainbusrtsize="16;" remainedbustsize="8;" break="" 16="" 8="" default:="" tmp="(hSize/4)%8;" switch="" tmp="" case="" 0:="" mainbusrtsize="8;" remainedbustsize="8;" break="" case="" 4:="" mainbusrtsize="8;" remainedbustsize="4;" break="" 8="" 4="" default:="" mainbusrtsize="4;" tmp="(hSize/4)%4;" remainedbustsize="(tmp)?tmp:4;" break="" break="" jz2440="" 527="" static="" void="" camif_get_scaler_factor="" u32="" src="" u32="" tar="" u32="" ratio="" u32="" shift="" if="" src="">= 64*tar) {return;}
- else if(src >= 32*tar) {*ratio = 32; *shift = 5;}
- else if(src >= 16*tar) {*ratio = 16; *shift = 4;}
- else if(src >= 8*tar) {*ratio = 8; *shift = 3;}
- else if(src >= 4*tar) {*ratio = 4; *shift = 2;}
- else if(src >= 2*tar) {*ratio = 2; *shift = 1;}
- else {*ratio = 1; *shift = 0;}
- }
- /* 计算公式在2440用户手册的527页 */
- static void cmos_ov7740_calculate_scaler_info(void)
- {
- unsigned int sx, sy, tx, ty;
- /* 这里的源宽度和源高度是经过窗口剪切的图像数据
- * 这里的目标宽度和高度是应用程序在cmos_ov7740_vidioc_s_fmt_vid_cap函数
- * 设置数据格式时传入的,表示应用程序想要获得的图像分辨率
- */
- sx = SRC_Width;
- sy = SRC_Height;
- tx = TargetHsize_Pr;
- ty = TargetVsize_Pr;
- printk("%s: SRC_in(%d, %d), Target_out(%d, %d)\n", __func__, sx, sy, tx, ty);
- camif_get_scaler_factor(sx, tx, &sc.PreHorRatio, &sc.H_Shift);
- camif_get_scaler_factor(sy, ty, &sc.PreVerRatio, &sc.V_Shift);
- sc.PreDstWidth = sx / sc.PreHorRatio;
- sc.PreDstHeight = sy / sc.PreVerRatio;
- sc.MainHorRatio = (sx << 8) / (tx << sc.H_Shift);
- sc.MainVerRatio = (sy << 8) / (ty << sc.V_Shift);
- sc.SHfactor = 10 - (sc.H_Shift + sc.V_Shift);
- sc.ScaleUpDown = (tx>=sx)?1:0;
- }
- /* A11 启动传输
- * 参考: uvc_video_enable(video, 1):
- * uvc_commit_video
- * uvc_init_video
- */
- static int cmos_ov7740_vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
- {
- unsigned int Main_burst, Remained_burst;
- /*
- CISRCFMT:
- bit[31] -- 选择传输方式为BT601或者BT656
- bit[30] -- 设置偏移值(0 = +0 (正常情况下) - for YCbCr)
- bit[29] -- 保留位,必须设置为0
- bit[28:16] -- 设置源图片的水平像素值(640)
- bit[15:14] -- 设置源图片的颜色顺序(0x0c --> 0x2),由摄像头地址为0xc的寄存器
- 可知,输出的顺序为UYVYUYVY------>CbYCrYCbYCrY,
- bit[12:0] -- 设置源图片的垂直像素值(480)
- 水平和垂直的像素值是摄像头模块输入的,查看摄像头模块输出水平和垂直的像素值即可
- */
- *CISRCFMT |= (0<<30)|(0<<29)|(CAM_SRC_HSIZE<<16)|(CAM_ORDER_CbYCrY<<14)|(CAM_SRC_VSIZE<<0);
- /*
- CIWDOFST:
- bit[31] -- 1 = 使能窗口功能、0 = 不使用窗口功能
- bit[30、15:12]-- 清除溢出标志位
- bit[26:16] -- 水平方向的裁剪的大小
- bit[10:0] -- 垂直方向的裁剪的大小
- */
- *CIWDOFST |=(1<<30)|(0xf<<12);
- *CIWDOFST |= (1<<31)|(WinHorOfst<<16)|(WinVerOfst<<0);
- /* 裁剪后的图像宽度 = 原始图像宽度 - 2*水平窗口宽度 */
- SRC_Width = CAM_SRC_HSIZE - 2*WinHorOfst;
- /* 裁剪后的图像高度 = 原始图像高度 - 2*水平窗口高度 */
- SRC_Height = CAM_SRC_VSIZE - 2*WinVerOfst;
- /*
- CIGCTRL:
- bit[31] -- 软件复位CAMIF控制器
- bit[30] -- 用于复位外部摄像头模块
- bit[29] -- 保留位,必须设置为1
- bit[28:27] -- 用于选择信号源(00 = 输入源来自摄像头模块)
- bit[26] -- 设置像素时钟的极性(猜0)
- bit[25] -- 设置VSYNC的极性(0)
- bit[24] -- 设置HREF的极性(0),行同步信号
- */
- *CIGCTRL |= (1<<29)|(0<<27)|(0<<26)|(0<<25)|(0<<24);
- /*
- CIPRCTRL:
- bit[23:19] -- 主突发长度(Main_burst)
- bit[18:14] -- 剩余突发长度(Remained_burst)
- bit[2] -- 是否使能LastIRQ功能(不使能)
- */
- CalculateBurstSize(bytesperline, &Main_burst, &Remained_burst);
- *CIPRCTRL = (Main_burst<<19)|(Remained_burst<<14)|(0<<2);
- /*
- CIPRSCPRERATIO:
- bit[31:28]: 预览缩放的变化系数(SHfactor_Pr)
- bit[22:16]: 预览缩放的水平比(PreHorRatio_Pr)
- bit[6:0]: 预览缩放的垂直比(PreVerRatio_Pr)
- CIPRSCPREDST:
- bit[27:16]: 预览缩放的目标宽度(PreDstWidth_Pr)
- bit[11:0]: 预览缩放的目标高度(PreDstHeight_Pr)
- CIPRSCCTRL:
- bit[29:28]: 告诉摄像头控制器(图片是缩小、放大)(ScaleUpDown_Pr)
- bit[24:16]: 预览主缩放的水平比(MainHorRatio_Pr)
- bit[8:0]: 预览主缩放的垂直比(MainVerRatio_Pr)
- bit[31]: 必须固定设置为1
- bit[30]: 设置图像输出格式是RGB16、RGB24
- bit[15]: 预览缩放开始
- //bit[30]在cmos_ov7740_vidioc_s_fmt_vid_cap函数中设置
- */
- cmos_ov7740_calculate_scaler_info();
- *CIPRSCPRERATIO = (sc.SHfactor<<28)|(sc.PreHorRatio<<16)|(sc.PreVerRatio<<0);
- *CIPRSCPREDST = (sc.PreDstWidth<<16)|(sc.PreDstHeight<<0);
- *CIPRSCCTRL |= (1<<31)|(sc.ScaleUpDown<<28)|(sc.MainHorRatio<<16)|(sc.MainVerRatio<<0);
- /*
- CIPRTAREA:
- 表示预览通道的目标区域
- */
- *CIPRTAREA = TargetHsize_Pr * TargetVsize_Pr;
- /*
- CIIMGCPT:
- bit[31]: 用来使能摄像头控制器
- bit[30]: 使能编码通道
- bit[29]: 使能预览通道
- */
- *CIIMGCPT = (1<<31)|(1<<29);
- *CIPRSCCTRL |= (1<<15);
- return 0;
- }
- /* A17 停止
- * 参考 : uvc_video_enable(video, 0)
- */
- static int cmos_ov7740_vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type t)
- {
- *CIPRSCCTRL &= ~(1<<15);
- *CIIMGCPT &= ~((1<<31)|(1<<29));
- return 0;
- }
- static const struct v4l2_ioctl_ops cmos_ov7740_ioctl_ops = {
- // 表示它是一个摄像头设备
- .vidioc_querycap = cmos_ov7740_vidioc_querycap,
- /* 用于列举、获得、测试、设置摄像头的数据的格式 */
- .vidioc_enum_fmt_vid_cap = cmos_ov7740_vidioc_enum_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = cmos_ov7740_vidioc_g_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = cmos_ov7740_vidioc_try_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = cmos_ov7740_vidioc_s_fmt_vid_cap,
- /* 缓冲区操作: 申请/查询/放入队列/取出队列 */
- .vidioc_reqbufs = cmos_ov7740_vidioc_reqbufs,
- /* 说明: 因为我们是通过读的方式来获得摄像头数据,因此查询/放入队列/取出队列这些操作函数将不在需要 */
- #if 0
- .vidioc_querybuf = myuvc_vidioc_querybuf,
- .vidioc_qbuf = myuvc_vidioc_qbuf,
- .vidioc_dqbuf = myuvc_vidioc_dqbuf,
- #endif
- // 启动/停止
- .vidioc_streamon = cmos_ov7740_vidioc_streamon,
- .vidioc_streamoff = cmos_ov7740_vidioc_streamoff,
- };
- /* A1 */
- static int cmos_ov7740_open(struct file *file)
- {
- return 0;
- }
- /* A18 关闭 */
- static int cmos_ov7740_close(struct file *file)
- {
- return 0;
- }
- /* 应用程序通过读的方式读取摄像头的数据 */
- static ssize_t cmos_ov7740_read(struct file *filep, char __user *buf, size_t count, loff_t *pos)
- {
- size_t end;
- int i;
- /* 取一帧数据的大小和用户想要读取数据的大小的最小值 */
- end = min_t(size_t, buf_size, count);
- wait_event_interruptible(cam_wait_queue, ev_cam);
- for(i=0; i<4; i++)
- {
- if(copy_to_user(buf, (void *)img_buff[i].virt_base, end))
- return -EFAULT;
- }
- ev_cam = 0;
- return end;
- }
- /* 应用程序使用read函数来获取摄像头数据,所以不再需要mmap,poll函数。 */
- static const struct v4l2_file_operations cmos_ov7740_fops = {
- .owner = THIS_MODULE,
- .open = cmos_ov7740_open,
- .release = cmos_ov7740_close,
- /* video_ioctl2是内核提供的函数,只是起到中转的作用,最后会调用video_device中的ioctl_ops */
- .unlocked_ioctl = video_ioctl2,
- .read = cmos_ov7740_read,
- };
- /*
- 注意:
- 该函数是必须的,否则在insmod的时候,会出错
- */
- static void cmos_ov7740_release(struct video_device *vdev)
- {
- unsigned int order;
- order = get_order(buf_size);
- free_pages(img_buff[0].virt_base, order);
- img_buff[0].phy_base = (unsigned long)NULL;
- free_pages(img_buff[1].virt_base, order);
- img_buff[1].phy_base = (unsigned long)NULL;
- free_pages(img_buff[2].virt_base, order);
- img_buff[2].phy_base = (unsigned long)NULL;
- free_pages(img_buff[3].virt_base, order);
- img_buff[3].phy_base = (unsigned long)NULL;
- }
- /* 2.1. 分配、设置一个video_device结构体
- * 这里采用的是静态分配结构体,使用video_device_alloc()是动态分配摄像头结构体
- */
- static struct video_device cmos_ov7740_vdev = {
- .fops = &cmos_ov7740_fops,
- .ioctl_ops = &cmos_ov7740_ioctl_ops,
- .release = cmos_ov7740_release,
- .name = "cmos_ov7740",
- };
- static void cmos_ov7740_gpio_cfg(void)
- {
- /* 设置相应的GPIO用于CAMIF */
- *GPJCON = 0x2aaaaaa;
- /* 将数据位清0 */
- *GPJDAT = 0;
- /* 为了让信号稳定,使能上拉电阻 */
- *GPJUP = 0;
- }
- static void cmos_ov7740_camif_reset(void)
- {
- /* 传输方式为BT601 */
- *CISRCFMT |= (1<<31);
- /* 复位CAMIF控制器 */
- *CIGCTRL |= (1<<31);
- /* 稍微延迟一下 */
- mdelay(10);
- /* 使摄像头恢复正常状态 */
- *CIGCTRL &= ~(1<<31 mdelay="" 10="" static="" void="" cmos_ov7740_clk_cfg="" void="" struct="" clk="" camif_clk="" struct="" clk="" camif_upll_clk="" camif="" clk_get="" soc="" source="" insight="" camif="" s3c2440="" camif_clk="clk_get(NULL," camif="" if="" camif_clk="" is_err="" camif_clk="" printk="" kern_info="" failed="" to="" get="" camif="" clock="" source="" n="" clk_enable="" camif_clk="" camclk="24MHz,设置外部摄像头的时钟" camif-upll="" usb="" soc="" usb="" soc="" camif_upll_clk="clk_get(NULL," camif-upll="" clk_set_rate="" camif_upll_clk="" 24000000="" mdelay="" 100="" :="" 1="" s3c2440="" camrst="" :0-="">1->0(0:表示正常工作的电平、1:表示复位电平)
- 但是,实验证明,该复位时序与我们的OV7740需要的复位时序(1->0->1)不符合。
- 2.因此,我们就应该结合OV7740的具体复位时序,来设置相应的寄存器。
- */
- static void cmos_ov7740_reset(void)
- {
- *CIGCTRL |= (1<<30);
- mdelay(30);
- *CIGCTRL &= ~(1<<30);
- mdelay(30);
- *CIGCTRL |= (1<<30);
- mdelay(30);
- }
- static void cmos_ov7740_init(void)
- {
- unsigned int mid;
- int i;
- /* 读
- * 0xa是摄像头模块的厂家id的高8位,0xb是厂家id的低8位
- * i2c_smbus_read_byte_data函数是内核提供的函数,也可是使用i2c_transfer函数
- */
- mid = i2c_smbus_read_byte_data(cmos_ov7740_client, 0x0a)<<8;
- mid |= i2c_smbus_read_byte_data(cmos_ov7740_client, 0x0b);
- printk("manufacture ID = 0x%4x\n", mid);
- /* 写
- * 初始化摄像头模块
- */
- for(i = 0; i < OV7740_INIT_REGS_SIZE; i++)
- {
- i2c_smbus_write_byte_data(cmos_ov7740_client, ov7740_setting_30fps_VGA_640_480[i].regaddr, ov7740_setting_30fps_VGA_640_480[i].value);
- mdelay(2);
- }
- }
- static int __devinit cmos_ov7740_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
- {
- printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
- /* 2.3 硬件相关 */
- /* 2.3.1 映射相应的寄存器 */
- GPJCON = ioremap(0x560000d0, 4);
- GPJDAT = ioremap(0x560000d4, 4);
- /* 上拉电阻相关的寄存器 */
- GPJUP = ioremap(0x560000d8, 4);
- /* 以下是摄像头控制器的寄存器 */
- CISRCFMT = ioremap(0x4F000000, 4);
- CIWDOFST = ioremap(0x4F000004, 4);
- CIGCTRL = ioremap(0x4F000008, 4);
- CIPRCLRSA1 = ioremap(0x4F00006C, 4);
- CIPRCLRSA2 = ioremap(0x4F000070, 4);
- CIPRCLRSA3 = ioremap(0x4F000074, 4);
- CIPRCLRSA4 = ioremap(0x4F000078, 4);
- CIPRTRGFMT = ioremap(0x4F00007C, 4);
- CIPRCTRL = ioremap(0x4F000080, 4);
- CIPRSCPRERATIO = ioremap(0x4F000084, 4);
- CIPRSCPREDST = ioremap(0x4F000088, 4);
- CIPRSCCTRL = ioremap(0x4F00008C, 4);
- CIPRTAREA = ioremap(0x4F000090, 4);
- CIIMGCPT = ioremap(0x4F0000A0, 4);
- SRCPND = ioremap(0X4A000000, 4);
- INTPND = ioremap(0X4A000010, 4);
- SUBSRCPND = ioremap(0X4A000018, 4);
- /* 2.3.2 设置相应的GPIO用于CAMIF */
- cmos_ov7740_gpio_cfg();
- /* 2.3.3 复位一下CAMIF控制器
- * 接下来要操作摄像头控制器,因此要先复位一下控制器
- */
- cmos_ov7740_camif_reset();
- /* 2.3.4 设置、使能时钟(使能HCLK用于摄像头控制器、使能并设置CAMCLK = 24MHz用于外部摄像头) */
- cmos_ov7740_clk_cfg();
- /* 2.3.5 复位一下摄像头模块 */
- cmos_ov7740_reset();
- /* 2.3.6 通过IIC总线,初始化摄像头模块 */
- cmos_ov7740_client = client;
- cmos_ov7740_init();
- /* 2.3.7 注册中断
- * IRQF_DISABLED - keep irqs disabled when calling the action handler
- */
- if (request_irq(IRQ_S3C2440_CAM_C, cmos_ov7740_camif_irq_c, IRQF_DISABLED , "CAM_C", NULL))
- printk("%s:request_irq failed\n", __func__);
- if (request_irq(IRQ_S3C2440_CAM_P, cmos_ov7740_camif_irq_p, IRQF_DISABLED , "CAM_P", NULL))
- printk("%s:request_irq failed\n", __func__);
- /* 3. 注册 ,以下信息是通过追踪源代码得到的结论
- * 参数-1表示分配第一个可用的号
- * which device node number (0 == /dev/video0, 1 == /dev/video1, ...
- * -1 == first free)
- */
- if(video_register_device(&cmos_ov7740_vdev, VFL_TYPE_GRABBER, -1))
- {
- printk("unable to register video device\n");
- }
- return 0;
- }
- static int __devexit cmos_ov7740_remove(struct i2c_client *client)
- {
- printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
- iounmap(GPJCON);
- iounmap(GPJDAT);
- iounmap(GPJUP);
- iounmap(CISRCFMT);
- iounmap(CIWDOFST);
- iounmap(CIGCTRL);
- iounmap(CIPRCLRSA1);
- iounmap(CIPRCLRSA2);
- iounmap(CIPRCLRSA3);
- iounmap(CIPRCLRSA4);
- iounmap(CIPRTRGFMT);
- iounmap(CIPRCTRL);
- iounmap(CIPRSCPRERATIO);
- iounmap(CIPRSCPREDST);
- iounmap(CIPRSCCTRL);
- iounmap(CIPRTAREA);
- iounmap(CIIMGCPT);
- iounmap(SRCPND);
- iounmap(INTPND);
- iounmap(SUBSRCPND);
- free_irq(IRQ_S3C2440_CAM_C, NULL);
- free_irq(IRQ_S3C2440_CAM_P, NULL);
- video_unregister_device(&cmos_ov7740_vdev);
- return 0;
- }
- static const struct i2c_device_id cmos_ov7740_id_table[] = {
- { "cmos_ov7740", 0 },
- {}
- };
- /* 1.1. 分配、设置一个i2c_driver */
- static struct i2c_driver cmos_ov7740_driver = {
- .driver = {
- /* 用于和iic设备进行匹配,必须和iic_dev中的名字一样,否则匹配不成功 */
- .name = "cmos_ov7740",
- .owner = THIS_MODULE,
- },
- .probe = cmos_ov7740_probe,
- .remove = __devexit_p(cmos_ov7740_remove),
- .id_table = cmos_ov7740_id_table,
- };
- static int cmos_ov7740_drv_init(void)
- {
- /* 1.2.注册 */
- i2c_add_driver(&cmos_ov7740_driver);
- return 0;
- }
- static void cmos_ov7740_drv_exit(void)
- {
- i2c_del_driver(&cmos_ov7740_driver);
- }
- module_init(cmos_ov7740_drv_init);
- module_exit(cmos_ov7740_drv_exit);
- MODULE_LICENSE("GPL");