ov9650摄像头驱动之——linux内核v4l2架构分析2

来源:互联网 发布:yolov2训练自己的数据 编辑:程序博客网 时间:2024/05/29 15:52

ov9650摄像头驱动之——linux内核v4l2架构分析2

NO.2 Camera解码器、控制器

 

1.根据camera控制器的描述,图像传输有两个DMA通道,我们用的是C通道,所以先将DMA内存初始化,因为在V4L2操作中有VIDIOC_REQBUFS中分配的数据缓存转换成物理地址的操作

所以DMA在用之前要初始化,包括实际物理地址的计算

init_image_buffer(camera_dev);// 初始化

 

 

 

复制代码
static int __inline__ init_image_buffer(struct s5pc100_camera_device *cam){unsigned long size;unsigned int order;cam->frame = img_buff; size = MAX_WIDTH * MAX_HEIGHT * formats[3].depth / 8; //sizeof image buffer is 600KBytes   printk("each image buffer is %dKBytes.\n", (int)(size/1024)); order = get_order(size); //系统函数,size应该是2的n次幂,内存按页分配img_buff[0].order = order;img_buff[0].virt_base = __get_free_pages(GFP_KERNEL|GFP_DMA, img_buff[0].order);//申请DMA空间,该函数可分配多个页并返回分配内存的首地址,分配的页数为2的order次幂,分配的页也不清零。order允许的最大值是10(即1024页)或者11(即2048页),具体依赖于硬件平台。img_buff[0].img_size = size;img_buff[0].phy_base = img_buff[0].virt_base - PAGE_OFFSET + PHYS_OFFSET;// the DMA address.申请的DMA的物理地址,怎么计算的呢?首先要减去PAGE_OFFSETwhy?因为在linux系统中,进程的4G空间被分为用户空间和内核空间两部分,用户空间的地址一般分布为0-3G(即RAGE_OFFSET),这样剩下的3-4G为内核空间,然后再加上 +PHYS_OFFSET(这个是由具体的cpu决定的,RAM的物理起始地址),这样的话phy_base就对应上了真正的物理地址printk("get pages for img_buff[0..3] done.\n");return 0;error0:return -ENOMEM;}
复制代码

 

 

 

 

2.camera控制器的初始化

 

  • 图像源的格式设置
  • window cut的设置
  • 目标图像格式的设置
  • 图像的缩放、旋转设置
  • (可选,如果是用本地LCD显示的话)将输出buffer地址定位在Framebuffer显存地址中(即内存重叠,这样的话LCD就能直接显示了),因为这里没用到LCD,所以这个就省略

 

具体代码:

 

复制代码
init_camif_config(camera_dev); static void init_camif_config(struct s5pc100_camera_device* c){struct s5pc100_camera_device*cam = c; cam->format = 3;// FIXME, C-path default format, see formats[] for detail.选择C通道  cam->srcHsize = 640;//  FIXME, the OV9650's horizontal output pixels.设置图像源的大小 cam->srcVsize = 480;// FIXME, the OV9650's verical output pixels. 设置图像源的大小     cam->wndHsize = 640; cam->wndVsize = 480; //window cut的设置 cam->targetHsize = cam->wndHsize;//目标图像格式的设置,与window图像重叠,全覆盖 cam->targetVsize = cam->wndVsize;旋转没有设置到目前为止,只是填充了cam的数据,但是camera控制器的源地址寄存器、目的地址寄存器都还没有配置这两个寄存器的配置依赖于上面初始化的参数 update_camera_config(cam, (u32)-1);//这个函数中集成了一个函数,这个函数就是配置两个寄存器的操作 } static void update_camera_config (struct s5pc100_camera_device *c, u32 cmdcode){struct s5pc100_camera_device *cam = c;update_camera_regs(cam);// config the regs directly.封装了下面的两个函数,其实没必要} static void __inline__ update_camera_regs(struct s5pc100_camera_device * cam){update_source_fmt_regs(cam);update_target_fmt_regs(cam);}
复制代码

 

 

 

初始化source寄存器

复制代码
static void __inline__ update_source_fmt_regs(struct s5pc100_camera_device *c){struct s5pc100_camera_device *cam = c;u32 cfg; cfg = (1<<31)// ITU-R BT.601 YCbCr 8-bit mode|(0<<30)// CB,Cr value offset cntrol for YCbCr|(640<<16)// target image width|(0<<14)// input order is YCbYCr|(640<<0);// source image heightwritel(cfg, cam->reg_base + S5PC100_CISRCFMT);    //0xEE20_0000 + 0000_0000 图像源地址printk("S5PC100_CIGCFMT = %x\n", readl(cam->reg_base + S5PC100_CISRCFMT)); cfg = (1<<15)|(1<<14)|(1<<30)|(1<<29); writel(cfg, cam->reg_base + S5PC100_CIWDOFST);///0xEE20_0000 + 0000_0004 清缓存fifocfg = (1<<26)|(1<<29)|(1<<16)|(1<<7)|(0<<0);writel(cfg, cam->reg_base + S5PC100_CIGCTRL);///0xEE20_0000 + 0000_0008全局变量控制寄存器,包含了使能IRQ中断等操作printk("S5PC100_CIGCTRL = %x\n", readl(cam->reg_base + S5PC100_CIGCTRL));writel(0, cam->reg_base + S5PC100_CIWDOFST2);//0xEE20_0000 + 0000_0014窗口偏移寄存器printk("OV9650_VGA mode\n");} 
复制代码

 


初始化目的寄存器
复制代码
static void __inline__ update_target_fmt_regs(struct s5pc100_camera_device * cam){u32 cfg;u32 h_shift;u32 v_shift;u32 prescaler_v_ratio;u32 prescaler_h_ratio;u32 main_v_ratio;u32 main_h_ratio;switch (formats[cam->format].pixelformat){case V4L2_PIX_FMT_RGB565:case V4L2_PIX_FMT_RGB24:case V4L2_PIX_FMT_YUV420:case V4L2_PIX_FMT_YUYV:/* YCbCr 1 plane*/printk("format V4L2_PIX_FMT_YUYV");writel(img_buff[0].phy_base, cam->reg_base + S5PC100_CIOYSA1);// 0xEE20_0000 + 0000_0018 DMAY1输出开始地址寄存器            将配置好的DMA物理开始地址赋给上述寄存器                                                                       /* CIPRTRGFMT. */cfg = (2 << 29) | (cam->targetHsize << 16)| (cam->targetVsize << 0)|(1<<13)|(1<<14)|(1<<15);将cam里已经初始化好的大小信息移位,写入对应的位置writel(cfg, cam->reg_base + S5PC100_CITRGFMT);    // 0xEE20_0000 + 0000_0048 目标格式寄存器/* CISCPRERATIO. */calculate_prescaler_ratio_shift(cam->srcHsize, cam->targetHsize, &prescaler_h_ratio, &h_shift);//将源的横坐标进行压缩,返回 压缩率和移位数calculate_prescaler_ratio_shift(cam->srcVsize, cam->targetVsize, &prescaler_v_ratio, &v_shift);//将源的纵坐标进行压缩 main_h_ratio = (cam->srcHsize << 8) / (cam->targetHsize << h_shift);main_v_ratio = (cam->srcVsize << 8) / (cam->targetVsize << v_shift); cfg = ((10 - (h_shift + v_shift)) << 28) | (prescaler_h_ratio << 16) | (prescaler_v_ratio << 0);       //移位因子,即共移位多少次writel(cfg, cam->reg_base + S5PC100_CISCPRERATIO);// 0xEE20_0000 + 0000_0050缩放比例寄存器,实现了图像的缩放处理 cfg = (cam->targetHsize << 16) | (cam->targetVsize << 0);writel(cfg, cam->reg_base + S5PC100_CISCPREDST); // 0xEE20_0000 + 0000_0054最初的目的定位寄存器 cfg = (main_h_ratio << 16) | (main_v_ratio << 0);writel(cfg, cam->reg_base + S5PC100_CISCCTRL); //main-scaler control Reg的配置 cfg = cam->targetVsize * cam->targetHsize;        //长*宽,0-27位,满足了writel(cfg, cam->reg_base + S5PC100_CITAREA);//输出目标区域大小寄存器 cfg = (cam->targetVsize << 0) | (cam->targetHsize << 16);writel(cfg, cam->reg_base + S5PC100_ORGOSIZE); //0xEE20_0000 + 0000_0184 DMA图像开始坐标寄存器break; }}下面的函数的意思是:传进来两个参数,一个是源的大小,另个是目的的大小,如果源是目标的64倍以上就错了,否则进行缩放,即源的大小是目标的32-64倍之间,就返回ratio(缩放比例)和shift(2的多少次幂),缩放比例是2的多少次幂,这样做的目的是方便移位,因为移位都是2的倍数int calculate_prescaler_ratio_shift(unsigned int SrcSize, unsigned int DstSize, unsigned int*ratio,unsigned int  *shift){if(SrcSize>=64*DstSize) {return -EINVAL;}else if(SrcSize>=32*DstSize) {*ratio=32;*shift=5;}else if(SrcSize>=16*DstSize) {*ratio=16;*shift=4;}else if(SrcSize>=8*DstSize) {*ratio=8;*shift=3;}else if(SrcSize>=4*DstSize) {*ratio=4;*shift=2;}else if(SrcSize>=2*DstSize) {*ratio=2;*shift=1;}else {*ratio=1;*shift=0;} return 0;}
复制代码
0 0
原创粉丝点击