下面就来分析这个mxc_v4l_open函数:
- static int mxc_v4l_open(struct file *file)
- {
- struct v4l2_ifparm ifparm;
- struct v4l2_format cam_fmt;
- ipu_csi_signal_cfg_t csi_param;
- struct video_device *dev = video_devdata(file); //获取video_device结构体
- cam_data *cam = video_get_drvdata(dev);
- int err = 0;
- struct sensor_data *sensor;
- pr_debug("\nIn MVC: mxc_v4l_open\n");
- pr_debug(" device name is %s\n", dev->name);
- if (!cam) { //如果没有获取到cam,就返回-EBADF。
- pr_err("ERROR: v4l2 capture: Internal error, "
- "cam_data not found!\n");
- return -EBADF;
- }
- if (cam->sensor == NULL ||
- cam->sensor->type != v4l2_int_type_slave) {
- pr_err("ERROR: v4l2 capture: slave not found!\n");
- return -EAGAIN;
- }
//如果cam->sensor不存在或者类型不是v4l2_int_type_slave的话就返回-EAGAIN。
//这个cam->sensor是在mxc_v4l2_master_attach函数中通过cam->sensor= slave设置的。
//所以cam->sensor就代表当前使用的slave设备。
- sensor = cam->sensor->priv;
- if (!sensor) {
- pr_err("%s: Internal error, sensor_data is not found!\n",
- __func__);
- return -EBADF;
- }
//这个cam->sensor->priv是在slave设备的probe函数中设置的,比如对于ov5640.c函数中,通过
//ov5640_int_device.priv= &ov5640_data;来设置的。
- down(&cam->busy_lock);
- err = 0;
- if (signal_pending(current))
- goto oops;
/*signal_pending(current)检查当前进程是否有信号处理,返回不为0表示有信号需要处理。返回-ERESTARTSYS表示信号函数处理完毕后重新执行信号函数前的某个系统调用。也就是说,如果信号函数前有发生系统调用,在调度信号处理函数之前,内核会检查系统调用的返回值,看看是不是因为这个信号而中断了系统调用.如果返回值-ERESTARTSYS,并且当前调度的信号具备-ERESTARTSYS属性,系统就会在用户信号函数返回之后再执行该系统调用。*/
- if (cam->open_count++ == 0) {
- wait_event_interruptible(cam->power_queue,
- cam->low_power == false);
/*这个power_queue等待队列是在init_camera_struct函数中初始化的,等待上电以后继续往下运行,在mxc_v4l2_resume函数中,会将low_power标志位置为false,然后唤醒这个队列。*/
- if (strcmp(mxc_capture_inputs[cam->current_input].name,
- "CSI MEM") == 0) {
- #if defined(CONFIG_MXC_IPU_CSI_ENC) || defined(CONFIG_MXC_IPU_CSI_ENC_MODULE)
- err = csi_enc_select(cam);
- #endif
- } else if (strcmp(mxc_capture_inputs[cam->current_input].name,
- "CSI IC MEM") == 0) {
- #if defined(CONFIG_MXC_IPU_PRP_ENC) || defined(CONFIG_MXC_IPU_PRP_ENC_MODULE)
- err = prp_enc_select(cam);
- #endif
- }
/*cam->current_input在init_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结构体里面那几个函数指针初始化为不同的值。这几个函数指针很重要,在后面使用到的时候再具体分析。*/
- cam->enc_counter = 0;
- INIT_LIST_HEAD(&cam->ready_q);
- INIT_LIST_HEAD(&cam->working_q);
- INIT_LIST_HEAD(&cam->done_q);
/*初始化3个队列头,这3个队列是在使用buffer过程中需要使用的。*/
- vidioc_int_g_ifparm(cam->sensor, &ifparm);
/*在《vidioc_int_*类函数的调用过程》中分析了这类函数,它最终会调用到slave设备中的ioctl_g_ifparm函数。它的作用就是填充ifparm这个结构体,以ov5640.c为例如下所示:
- static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p)
- {
- if (s == NULL) {
- pr_err(" ERROR!! no slave device set!\n");
- return -1;
- }
- memset(p, 0, sizeof(*p));
- p->u.bt656.clock_curr = ov5640_data.mclk;
- pr_debug(" clock_curr=mclk=%d\n", ov5640_data.mclk);
- p->if_type = V4L2_IF_TYPE_BT656;
- p->u.bt656.mode = V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT;
- p->u.bt656.clock_min = OV5640_XCLK_MIN;
- p->u.bt656.clock_max = OV5640_XCLK_MAX;
- p->u.bt656.bt_sync_correct = 1; /* Indicate external vsync */
- return 0;
- }
但是这个函数就是根据ov5640.c里面的一些值来设置ifparm这个结构体,跟cam->sensor没啥关系,从这个ov5640slave设备里面获取的这些值,用来进行下面的设置,这个函数可以看成是从底层的slave设备里面获取参数的函数。
*/
- csi_param.sens_clksrc = 0;
- csi_param.clk_mode = 0;
- csi_param.data_pol = 0;
- csi_param.ext_vsync = 0;
- csi_param.pack_tight = 0;
- csi_param.force_eof = 0;
- csi_param.data_en_pol = 0;
- csi_param.mclk = ifparm.u.bt656.clock_curr;
- csi_param.pixclk_pol = ifparm.u.bt656.latch_clk_inv;
- if (ifparm.u.bt656.mode
- == V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT)
- csi_param.data_width = IPU_CSI_DATA_WIDTH_8;
- else if (ifparm.u.bt656.mode
- == V4L2_IF_TYPE_BT656_MODE_NOBT_10BIT)
- csi_param.data_width = IPU_CSI_DATA_WIDTH_10;
- else
- csi_param.data_width = IPU_CSI_DATA_WIDTH_8;
- csi_param.Vsync_pol = ifparm.u.bt656.nobt_vs_inv;
- csi_param.Hsync_pol = ifparm.u.bt656.nobt_hs_inv;
- csi_param.csi = cam->csi;
/*这个csi_param是ipu_csi_signal_cfg_t类型的,根据上面vidioc_int_g_ifparm函数从底层slave设备获取的参数来对csi_param结构体进行初始化。*/
- cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- vidioc_int_g_fmt_cap(cam->sensor, &cam_fmt);
/*这个vidioc_int_g_fmt_cap函数也是《vidioc_int_*类函数的调用过程》中分析的,它最终会调用到ov5640.c中的ioctl_g_fmt_cap函数:
- static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f)
- {
- struct sensor_data *sensor = s->priv;
- f->fmt.pix = sensor->pix;
- return 0;
- }
最终会根据cam->sensor->pix里面的值来填充cam_fmt->fmt.pix的值。看这个ioctl_g_fmt_cap函数,s就是cam->sensor,s->priv就是ov5640的probe函数中指定的ov5640_data结构体。所以最终,f->fmt.pix里面存放的就是ov5640_data->pix里面的值。
*/
- /* Reset the sizes. Needed to prevent carryover of last
- * operation.*/
- cam->crop_bounds.top = cam->crop_bounds.left = 0;
- cam->crop_bounds.width = cam_fmt.fmt.pix.width;
- cam->crop_bounds.height = cam_fmt.fmt.pix.height;
- /* This also is the max crop size for this device. */
- cam->crop_defrect.top = cam->crop_defrect.left = 0;
- cam->crop_defrect.width = cam_fmt.fmt.pix.width;
- cam->crop_defrect.height = cam_fmt.fmt.pix.height;
- /* At this point, this is also the current image size. */
- cam->crop_current.top = cam->crop_current.left = 0;
- cam->crop_current.width = cam_fmt.fmt.pix.width;
- cam->crop_current.height = cam_fmt.fmt.pix.height;
/*根据上面获取到的cam_fmt参数来设置cam中crop_bounds,crop_defrect和crop_current的几个参数。*/
- pr_debug("End of %s: v2f pix widthxheight %d x %d\n",
- __func__,
- cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height);
- pr_debug("End of %s: crop_bounds widthxheight %d x %d\n",
- __func__,
- cam->crop_bounds.width, cam->crop_bounds.height);
- pr_debug("End of %s: crop_defrect widthxheight %d x %d\n",
- __func__,
- cam->crop_defrect.width, cam->crop_defrect.height);
- pr_debug("End of %s: crop_current widthxheight %d x %d\n",
- __func__,
- cam->crop_current.width, cam->crop_current.height);
- csi_param.data_fmt = cam_fmt.fmt.pix.pixelformat;
- pr_debug("On Open: Input to ipu size is %d x %d\n",
- cam_fmt.fmt.pix.width, cam_fmt.fmt.pix.height);
- ipu_csi_set_window_size(cam->ipu, cam->crop_current.width,
- cam->crop_current.height,
- cam->csi);
- ipu_csi_set_window_pos(cam->ipu, cam->crop_current.left,
- cam->crop_current.top,
- cam->csi);
/*刚才在init_camera_struct函数中进行过这两个设置,设置窗口的大小和位置。在init_camera_struct函数中,直接将窗口大小和位置设置为左上角从(0,0)开始,640* 480 。
现在根据从底层slave设备中获取的参数重新设置窗口的大小和位置。*/
- ipu_csi_init_interface(cam->ipu, cam->crop_bounds.width,
- cam->crop_bounds.height,
- cam_fmt.fmt.pix.pixelformat,
- csi_param);
/*这个函数根据这几个参数,设置底层ipu寄存器的值,它大致设置了CSI_SENS_CONF,CSI_SENS_FRM_SIZE,CSI_CCIR_CODE_1,CSI_CCIR_CODE_2,CSI_CCIR_CODE_3的值。我们在后面分析具体应用程序的执行流程的时候再具体分析它。*/
- clk_prepare_enable(sensor->sensor_clk);
/*这个函数在clk.h中定义,如下所示:
- /* clk_prepare_enable helps cases using clk_enable in non-atomic context. */
- static inline int clk_prepare_enable(struct clk *clk)
- {
- int ret;
- ret = clk_prepare(clk);
- if (ret)
- return ret;
- ret = clk_enable(clk);
- if (ret)
- clk_unprepare(clk);
- return ret;
- }
看它的注释就能理解大概意思,这个clk_prepare_enable函数是为了帮助clk_enable函数应用在非原子上下文的情况下。它的核心就是这个clk_enable函数。
*/
- vidioc_int_s_power(cam->sensor, 1);
/*这个函数同样是《vidioc_int_*类函数的调用过程》中所分析的,它最终会调用到ov5640.c中的ioctl_s_power函数:
- static int ioctl_s_power(struct v4l2_int_device *s, int on)
- {
- struct sensor_data *sensor = s->priv;
- if (on && !sensor->on) {
- if (io_regulator)
- if (regulator_enable(io_regulator) != 0)
- return -EIO;
- if (core_regulator)
- if (regulator_enable(core_regulator) != 0)
- return -EIO;
- if (gpo_regulator)
- if (regulator_enable(gpo_regulator) != 0)
- return -EIO;
- if (analog_regulator)
- if (regulator_enable(analog_regulator) != 0)
- return -EIO;
- /* Make sure power on */
- ov5640_standby(0);
- } else if (!on && sensor->on) {
- if (analog_regulator)
- regulator_disable(analog_regulator);
- if (core_regulator)
- regulator_disable(core_regulator);
- if (io_regulator)
- regulator_disable(io_regulator);
- if (gpo_regulator)
- regulator_disable(gpo_regulator);
- ov5640_standby(1);
- }
- sensor->on = on;
- return 0;
- }
如果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的值来判断是否真正的上电,断电。
*/
- vidioc_int_init(cam->sensor);
/*会调用到ioctl_init函数,但是这个函数为空函数,直接返回0.*/
- vidioc_int_dev_init(cam->sensor);
/*会调用到ioctl_dev_init函数,这个函数就是初始化ov5640设备,这个函数中主要是初始化设置ov5640摄像头本身的一些寄存器的值等。在open函数中,不只是设置ipu的一些寄存器的值,还需要设置对应的摄像头本身的数据等。这个函数在分析摄像头文件的时候再具体分析。*/
- }
- file->private_data = dev;
- oops:
- up(&cam->busy_lock);
- return err;
- }
至此就分析完这个mxc_v4l_open函数了。
上面标红色的两个函数没有分析,在这分析一下:
csi_enc_select函数在ipu_csi_enc.c中定义:
- int csi_enc_select(void *private)
- {
- cam_data *cam = (cam_data *) private;
- int err = 0;
- if (cam) {
- cam->enc_update_eba = csi_enc_eba_update;
- cam->enc_enable = csi_enc_enabling_tasks;
- cam->enc_disable = csi_enc_disabling_tasks;
- cam->enc_enable_csi = csi_enc_enable_csi;
- cam->enc_disable_csi = csi_enc_disable_csi;
- } else {
- err = -EIO;
- }
- return err;
- }
- EXPORT_SYMBOL(csi_enc_select);
它就是为cam_data结构体里面的几个函数指针赋值,这几个函数涉及到更新物理缓冲区地址,使能译码任务,使能csi等等操作,他们最终都会调用更底层的ipu操作函数来通过操作寄存器来实现这些功能。
prp_enc_select函数在ipu_prp_enc.c中定义:
- int prp_enc_select(void *private)
- {
- cam_data *cam = (cam_data *) private;
- int err = 0;
- if (cam) {
- cam->enc_update_eba = prp_enc_eba_update;
- cam->enc_enable = prp_enc_enabling_tasks;
- cam->enc_disable = prp_enc_disabling_tasks;
- cam->enc_enable_csi = prp_enc_enable_csi;
- cam->enc_disable_csi = prp_enc_disable_csi;
- } else {
- err = -EIO;
- }
- return err;
- }
- EXPORT_SYMBOL(prp_enc_select);
它同样是为cam_data中的几个函数指针赋值,这几个函数同样会调用更底层的ipu操作来实现功能。