下面就来分析这个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操作来实现功能。