3.4 mxc_v4l_open函数分析

下面就来分析这个mxc_v4l_open函数:

[html]  view plain  copy
  1. static int mxc_v4l_open(struct file *file)   
  2. {   
  3.     struct v4l2_ifparm ifparm;   
  4.     struct v4l2_format cam_fmt;   
  5.     ipu_csi_signal_cfg_t csi_param;   
  6.     struct video_device *dev = video_devdata(file); //获取video_device结构体  
  7.     cam_data *cam = video_get_drvdata(dev);   
  8.     int err = 0;   
  9.     struct sensor_data *sensor;   
  10.   
  11.     pr_debug("\nIn MVC: mxc_v4l_open\n");   
  12.     pr_debug("   device name is %s\n", dev->name);   
  13.   
  14.     if (!cam) { //如果没有获取到cam,就返回-EBADF。  
  15.         pr_err("ERROR: v4l2 capture: Internal error, "   
  16.             "cam_data not found!\n");   
  17.         return -EBADF;   
  18.     }   
  19.   
  20.     if (cam->sensor == NULL ||   
  21.         cam->sensor->type != v4l2_int_type_slave) {   
  22.         pr_err("ERROR: v4l2 capture: slave not found!\n");   
  23.         return -EAGAIN;   
  24.     }   

//如果cam->sensor不存在或者类型不是v4l2_int_type_slave的话就返回-EAGAIN

//这个cam->sensor是在mxc_v4l2_master_attach函数中通过cam->sensor= slave设置的。

//所以cam->sensor就代表当前使用的slave设备。

[cpp]  view plain  copy
  1. sensor = cam->sensor->priv;   
  2. if (!sensor) {   
  3.     pr_err("%s: Internal error, sensor_data is not found!\n",   
  4.            __func__);   
  5.     return -EBADF;   
  6. }   

//这个cam->sensor->priv是在slave设备的probe函数中设置的,比如对于ov5640.c函数中,通过

//ov5640_int_device.priv= &ov5640_data;来设置的。

[cpp]  view plain  copy
  1. down(&cam->busy_lock);   
  2. err = 0;   
  3. if (signal_pending(current))   
  4.     goto oops;   

/*signal_pending(current)检查当前进程是否有信号处理,返回不为0表示有信号需要处理。返回-ERESTARTSYS表示信号函数处理完毕后重新执行信号函数前的某个系统调用。也就是说,如果信号函数前有发生系统调用,在调度信号处理函数之前,内核会检查系统调用的返回值,看看是不是因为这个信号而中断了系统调用.如果返回值-ERESTARTSYS,并且当前调度的信号具备-ERESTARTSYS属性,系统就会在用户信号函数返回之后再执行该系统调用。*/

[cpp]  view plain  copy
  1. if (cam->open_count++ == 0) {   
  2.     wait_event_interruptible(cam->power_queue,   
  3.                  cam->low_power == false);   

/*这个power_queue等待队列是在init_camera_struct函数中初始化的,等待上电以后继续往下运行,在mxc_v4l2_resume函数中,会将low_power标志位置为false,然后唤醒这个队列。*/

[cpp]  view plain  copy
  1.         if (strcmp(mxc_capture_inputs[cam->current_input].name,   
  2.                "CSI MEM") == 0) {   
  3. #if defined(CONFIG_MXC_IPU_CSI_ENC) || defined(CONFIG_MXC_IPU_CSI_ENC_MODULE)   
  4.             err = csi_enc_select(cam);   
  5. #endif   
  6.         } else if (strcmp(mxc_capture_inputs[cam->current_input].name,   
  7.                   "CSI IC MEM") == 0) {   
  8. #if defined(CONFIG_MXC_IPU_PRP_ENC) || defined(CONFIG_MXC_IPU_PRP_ENC_MODULE)   
  9.             err = prp_enc_select(cam);   
  10. #endif   
  11.         }   

/*cam->current_inputinit_camera_struct中被初始化为0,在这根据mxc_capture_inputs[]这个数组中第cam->current_input项的名字来选择执行哪个函数。在这个文件中,mxc_capture_inputs[0]的名字是“CSIIC MEM”,同时CONFIG_MXC_IPU_PRP_ENC这个宏定义了,所以执行prp_enc_select函数。这两个函数的目的就是根据不同的情况来选择为cam_data结构体里面那几个函数指针初始化为不同的值。这几个函数指针很重要,在后面使用到的时候再具体分析。*/

[cpp]  view plain  copy
  1. cam->enc_counter = 0;   
  2. INIT_LIST_HEAD(&cam->ready_q);   
  3. INIT_LIST_HEAD(&cam->working_q);   
  4. INIT_LIST_HEAD(&cam->done_q);   

/*初始化3个队列头,这3个队列是在使用buffer过程中需要使用的。*/

[cpp]  view plain  copy
  1. vidioc_int_g_ifparm(cam->sensor, &ifparm);   

/*在《vidioc_int_*类函数的调用过程》中分析了这类函数,它最终会调用到slave设备中的ioctl_g_ifparm函数。它的作用就是填充ifparm这个结构体,以ov5640.c为例如下所示:

[cpp]  view plain  copy
  1. static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p)   
  2. {   
  3.     if (s == NULL) {   
  4.         pr_err("   ERROR!! no slave device set!\n");   
  5.         return -1;   
  6.     }   
  7.   
  8.     memset(p, 0, sizeof(*p));   
  9.     p->u.bt656.clock_curr = ov5640_data.mclk;   
  10.     pr_debug("   clock_curr=mclk=%d\n", ov5640_data.mclk);   
  11.     p->if_type = V4L2_IF_TYPE_BT656;   
  12.     p->u.bt656.mode = V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT;   
  13.     p->u.bt656.clock_min = OV5640_XCLK_MIN;   
  14.     p->u.bt656.clock_max = OV5640_XCLK_MAX;   
  15.     p->u.bt656.bt_sync_correct = 1;  /* Indicate external vsync */   
  16.   
  17.     return 0;   
  18. }   

但是这个函数就是根据ov5640.c里面的一些值来设置ifparm这个结构体,跟cam->sensor没啥关系,从这个ov5640slave设备里面获取的这些值,用来进行下面的设置,这个函数可以看成是从底层的slave设备里面获取参数的函数。

*/

[cpp]  view plain  copy
  1. csi_param.sens_clksrc = 0;   
  2.   
  3. csi_param.clk_mode = 0;   
  4. csi_param.data_pol = 0;   
  5. csi_param.ext_vsync = 0;   
  6.   
  7. csi_param.pack_tight = 0;   
  8. csi_param.force_eof = 0;   
  9. csi_param.data_en_pol = 0;   
  10.   
  11. csi_param.mclk = ifparm.u.bt656.clock_curr;   
  12.   
  13. csi_param.pixclk_pol = ifparm.u.bt656.latch_clk_inv;   
  14.   
  15. if (ifparm.u.bt656.mode   
  16.         == V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT)   
  17.     csi_param.data_width = IPU_CSI_DATA_WIDTH_8;   
  18. else if (ifparm.u.bt656.mode   
  19.         == V4L2_IF_TYPE_BT656_MODE_NOBT_10BIT)   
  20.     csi_param.data_width = IPU_CSI_DATA_WIDTH_10;   
  21. else   
  22.     csi_param.data_width = IPU_CSI_DATA_WIDTH_8;   
  23.   
  24.   
  25. csi_param.Vsync_pol = ifparm.u.bt656.nobt_vs_inv;   
  26. csi_param.Hsync_pol = ifparm.u.bt656.nobt_hs_inv;   
  27.   
  28. csi_param.csi = cam->csi;   

/*这个csi_paramipu_csi_signal_cfg_t类型的,根据上面vidioc_int_g_ifparm函数从底层slave设备获取的参数来对csi_param结构体进行初始化。*/

[cpp]  view plain  copy
  1. cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;   
  2. vidioc_int_g_fmt_cap(cam->sensor, &cam_fmt);   

/*这个vidioc_int_g_fmt_cap函数也是《vidioc_int_*类函数的调用过程》中分析的,它最终会调用到ov5640.c中的ioctl_g_fmt_cap函数:

[cpp]  view plain  copy
  1. static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f)   
  2. {   
  3.     struct sensor_data *sensor = s->priv;   
  4.   
  5.     f->fmt.pix = sensor->pix;   
  6.   
  7.     return 0;   
  8. }   

最终会根据cam->sensor->pix里面的值来填充cam_fmt->fmt.pix的值。看这个ioctl_g_fmt_cap函数,s就是cam->sensors->priv就是ov5640probe函数中指定的ov5640_data结构体。所以最终,f->fmt.pix里面存放的就是ov5640_data->pix里面的值。

*/

[cpp]  view plain  copy
  1. /* Reset the sizes.  Needed to prevent carryover of last  
  2.  * operation.*/   
  3. cam->crop_bounds.top = cam->crop_bounds.left = 0;   
  4. cam->crop_bounds.width = cam_fmt.fmt.pix.width;   
  5. cam->crop_bounds.height = cam_fmt.fmt.pix.height;   
  6.   
  7. /* This also is the max crop size for this device. */   
  8. cam->crop_defrect.top = cam->crop_defrect.left = 0;   
  9. cam->crop_defrect.width = cam_fmt.fmt.pix.width;   
  10. cam->crop_defrect.height = cam_fmt.fmt.pix.height;   
  11.   
  12. /* At this point, this is also the current image size. */   
  13. cam->crop_current.top = cam->crop_current.left = 0;   
  14. cam->crop_current.width = cam_fmt.fmt.pix.width;   
  15. cam->crop_current.height = cam_fmt.fmt.pix.height;   

/*根据上面获取到的cam_fmt参数来设置camcrop_boundscrop_defrectcrop_current的几个参数。*/

[cpp]  view plain  copy
  1. pr_debug("End of %s: v2f pix widthxheight %d x %d\n",   
  2.     __func__,   
  3.     cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height);   
  4. pr_debug("End of %s: crop_bounds widthxheight %d x %d\n",   
  5.     __func__,   
  6.     cam->crop_bounds.width, cam->crop_bounds.height);   
  7. pr_debug("End of %s: crop_defrect widthxheight %d x %d\n",   
  8.     __func__,   
  9.     cam->crop_defrect.width, cam->crop_defrect.height);   
  10. pr_debug("End of %s: crop_current widthxheight %d x %d\n",   
  11.     __func__,   
  12.     cam->crop_current.width, cam->crop_current.height);   
  13.   
  14. csi_param.data_fmt = cam_fmt.fmt.pix.pixelformat;   
  15. pr_debug("On Open: Input to ipu size is %d x %d\n",   
  16.         cam_fmt.fmt.pix.width, cam_fmt.fmt.pix.height);   
  17. ipu_csi_set_window_size(cam->ipu, cam->crop_current.width,   
  18.             cam->crop_current.height,   
  19.             cam->csi);   
  20. ipu_csi_set_window_pos(cam->ipu, cam->crop_current.left,   
  21.             cam->crop_current.top,   
  22.             cam->csi);   

/*刚才在init_camera_struct函数中进行过这两个设置,设置窗口的大小和位置。在init_camera_struct函数中,直接将窗口大小和位置设置为左上角从(00)开始,640* 480 

现在根据从底层slave设备中获取的参数重新设置窗口的大小和位置。*/

[cpp]  view plain  copy
  1. ipu_csi_init_interface(cam->ipu, cam->crop_bounds.width,   
  2.             cam->crop_bounds.height,   
  3.             cam_fmt.fmt.pix.pixelformat,   
  4.             csi_param);   

/*这个函数根据这几个参数,设置底层ipu寄存器的值,它大致设置了CSI_SENS_CONFCSI_SENS_FRM_SIZECSI_CCIR_CODE_1CSI_CCIR_CODE_2CSI_CCIR_CODE_3的值。我们在后面分析具体应用程序的执行流程的时候再具体分析它。*/

[cpp]  view plain  copy
  1. clk_prepare_enable(sensor->sensor_clk);   

/*这个函数在clk.h中定义,如下所示:

[cpp]  view plain  copy
  1. /* clk_prepare_enable helps cases using clk_enable in non-atomic context. */   
  2. static inline int clk_prepare_enable(struct clk *clk)   
  3. {   
  4.     int ret;   
  5.   
  6.     ret = clk_prepare(clk);   
  7.     if (ret)   
  8.         return ret;   
  9.     ret = clk_enable(clk);   
  10.     if (ret)   
  11.         clk_unprepare(clk);   
  12.   
  13.     return ret;   
  14. }  

看它的注释就能理解大概意思,这个clk_prepare_enable函数是为了帮助clk_enable函数应用在非原子上下文的情况下。它的核心就是这个clk_enable函数。

*/

[cpp]  view plain  copy
  1. vidioc_int_s_power(cam->sensor, 1);   

/*这个函数同样是《vidioc_int_*类函数的调用过程》中所分析的,它最终会调用到ov5640.c中的ioctl_s_power函数:

[cpp]  view plain  copy
  1. static int ioctl_s_power(struct v4l2_int_device *s, int on)   
  2. {   
  3.     struct sensor_data *sensor = s->priv;   
  4.   
  5.     if (on && !sensor->on) {  
  6.         if (io_regulator)   
  7.             if (regulator_enable(io_regulator) != 0)   
  8.                 return -EIO;   
  9.         if (core_regulator)   
  10.             if (regulator_enable(core_regulator) != 0)   
  11.                 return -EIO;   
  12.         if (gpo_regulator)   
  13.             if (regulator_enable(gpo_regulator) != 0)   
  14.                 return -EIO;   
  15.         if (analog_regulator)   
  16.             if (regulator_enable(analog_regulator) != 0)   
  17.                 return -EIO;   
  18.         /* Make sure power on */   
  19.         ov5640_standby(0);   
  20.     } else if (!on && sensor->on) {   
  21.         if (analog_regulator)   
  22.             regulator_disable(analog_regulator);   
  23.         if (core_regulator)   
  24.             regulator_disable(core_regulator);   
  25.         if (io_regulator)   
  26.             regulator_disable(io_regulator);   
  27.         if (gpo_regulator)   
  28.             regulator_disable(gpo_regulator);   
  29.   
  30.         ov5640_standby(1);   
  31.     }   
  32.   
  33.     sensor->on = on;   
  34.   
  35.     return 0;   
  36. }  

如果on=1并且sensor->on==0的话,这表示什么意思呢?sensor->on==0表明这个slave设备本身是没有上电的,然后调用vidioc_int_s_power(cam->sensor,1)函数为它上电,如果设备本身是上电状态,想要把它关闭就就调用调用vidioc_int_s_power(cam->sensor,0)即可。内部调用的是regulator_enable函数,它在/include/linux/regulator/consumer.h中定义,跳转查看可以发现这个函数和regulator_disable函数都是空函数,直接返回0.所以有用的函数就是ov5640_standby一个了,大致意思是根据pwn_gpio的值来判断是否真正的上电,断电。

*/

[cpp]  view plain  copy
  1. vidioc_int_init(cam->sensor);   

/*会调用到ioctl_init函数,但是这个函数为空函数,直接返回0.*/

[cpp]  view plain  copy
  1. vidioc_int_dev_init(cam->sensor);   

/*会调用到ioctl_dev_init函数,这个函数就是初始化ov5640设备,这个函数中主要是初始化设置ov5640摄像头本身的一些寄存器的值等。在open函数中,不只是设置ipu的一些寄存器的值,还需要设置对应的摄像头本身的数据等。这个函数在分析摄像头文件的时候再具体分析。*/

[cpp]  view plain  copy
  1.     }   
  2.   
  3.     file->private_data = dev;   
  4.   
  5. oops:   
  6.     up(&cam->busy_lock);   
  7.     return err;   
  8. }  

至此就分析完这个mxc_v4l_open函数了。


上面标红色的两个函数没有分析,在这分析一下:

csi_enc_select函数在ipu_csi_enc.c中定义:

[cpp]  view plain  copy
  1. int csi_enc_select(void *private)   
  2. {   
  3.     cam_data *cam = (cam_data *) private;   
  4.     int err = 0;   
  5.   
  6.     if (cam) {   
  7.         cam->enc_update_eba = csi_enc_eba_update;   
  8.         cam->enc_enable = csi_enc_enabling_tasks;   
  9.         cam->enc_disable = csi_enc_disabling_tasks;   
  10.         cam->enc_enable_csi = csi_enc_enable_csi;   
  11.         cam->enc_disable_csi = csi_enc_disable_csi;   
  12.     } else {   
  13.         err = -EIO;   
  14.     }   
  15.   
  16.     return err;   
  17. }   
  18. EXPORT_SYMBOL(csi_enc_select);  

它就是为cam_data结构体里面的几个函数指针赋值,这几个函数涉及到更新物理缓冲区地址,使能译码任务,使能csi等等操作,他们最终都会调用更底层的ipu操作函数来通过操作寄存器来实现这些功能。

prp_enc_select函数在ipu_prp_enc.c中定义:

[cpp]  view plain  copy
  1. int prp_enc_select(void *private)   
  2. {   
  3.     cam_data *cam = (cam_data *) private;   
  4.     int err = 0;   
  5.   
  6.     if (cam) {   
  7.         cam->enc_update_eba = prp_enc_eba_update;   
  8.         cam->enc_enable = prp_enc_enabling_tasks;   
  9.         cam->enc_disable = prp_enc_disabling_tasks;   
  10.         cam->enc_enable_csi = prp_enc_enable_csi;   
  11.         cam->enc_disable_csi = prp_enc_disable_csi;   
  12.     } else {   
  13.         err = -EIO;   
  14.     }   
  15.   
  16.     return err;   
  17. }   
  18. EXPORT_SYMBOL(prp_enc_select);  

它同样是为cam_data中的几个函数指针赋值,这几个函数同样会调用更底层的ipu操作来实现功能。

猜你喜欢

转载自blog.csdn.net/dragon101788/article/details/80660862
3.4