3.4 mxc_v4l_open函数分析

下面就来分析这个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_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结构体里面那几个函数指针初始化为不同的值。这几个函数指针很重要,在后面使用到的时候再具体分析。*/

		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_paramipu_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->sensors->priv就是ov5640probe函数中指定的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参数来设置camcrop_boundscrop_defrectcrop_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函数中,直接将窗口大小和位置设置为左上角从(00)开始,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_CONFCSI_SENS_FRM_SIZECSI_CCIR_CODE_1CSI_CCIR_CODE_2CSI_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操作来实现功能。

你可能感兴趣的:(i.MX6-IPU子系统)