(1)概述
Sensor Multifile Controller作为CSI模块和IDMAC之间的一个缓冲设备,每个SMFC可以支持两个CSI设备。
每个CSI设备可以最多发送4帧图像到SMFC中,SMFC通过csi_id号来区分是哪个CSI发送的。每一帧图像通过SMFC映射到4个dmachannel其中的一个。每个dmachannel都是一个FIFO。
(2)SMFC功能介绍
SMFC最多支持4个dmachannel,每一个dmachannel都有一个专用的FIFO控制器(FIFOcontroller)。每个buffer中都含有真正的数据和帧ID。当RR优先级机制选中其中的某一个buffer后,会将对应buffer里面保存的ID号与CH#_MAP位进行比较,然后对应的FIFO控制器将会被使能,之后这个buffer里面的真正数据就会被拷贝到RAM中。wptr和base用来计算在RAM中的位置,rptr是在dma_active信号发生后跟随wptr发出。
这个过程如下图所示:
由于这四个channels不能够同时启用,所以四个FIFO需要使用同一个RAM,SMFC所使用的内存空间分为4个相同的扇区,每一个FIFO拥有一个固定的基地址————“base”。Channel0 和2的FIFO扇区大小是固定的,并且等于一个扇区的大小。Channel1 和3的FIFO扇区大小会根据其他FIFO扇区的大小来决定。如下图所示:
从这个图中可以看出来,Channel0 和2的FIFO扇区大小只能是0或者1,而Channel1 和3的FIFO扇区大小就是可以调整的。
下图是SMFC的时序图:
(三)代码实现
3.1需要使用到SMFC的channel就是CSI-->SMFC-->MEM这个channel,在ipu_init_channel函数中对应于CSI_MEM0,CSI_MEM1, CSI_MEM2, CSI_MEM3这几个channel。所以,以ipu_init_channel这个函数为核心来分析SMFC的设置。有关CSI_MEM0,CSI_MEM1, CSI_MEM2, CSI_MEM3这几个channel的文件是ipu_csi_enc.c。
3.2在ipu_init_channel函数中,设置SMFC的就是_ipu_smfc_init函数:
if (params->csi_mem.mipi_en) {
ipu_conf |= (1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET +
params->csi_mem.csi));
_ipu_smfc_init(ipu, channel, params->csi_mem.mipi_vc,
params->csi_mem.csi);
_ipu_csi_set_mipi_di(ipu, params->csi_mem.mipi_vc,
params->csi_mem.mipi_id, params->csi_mem.csi);
} else {
ipu_conf &= ~(1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET +
params->csi_mem.csi));
_ipu_smfc_init(ipu, channel, 0, params->csi_mem.csi);
}
再来看看这个_ipu_smfc_init函数的实现:
void _ipu_smfc_init(struct ipu_soc *ipu, ipu_channel_t channel, uint32_t mipi_id, uint32_t csi)
{
uint32_t temp;
temp = ipu_smfc_read(ipu, SMFC_MAP);
switch (channel) {
case CSI_MEM0:
temp &= ~SMFC_MAP_CH0_MASK;
temp |= ((csi << 2) | mipi_id) << SMFC_MAP_CH0_SHIFT;
break;
case CSI_MEM1:
temp &= ~SMFC_MAP_CH1_MASK;
temp |= ((csi << 2) | mipi_id) << SMFC_MAP_CH1_SHIFT;
break;
case CSI_MEM2:
temp &= ~SMFC_MAP_CH2_MASK;
temp |= ((csi << 2) | mipi_id) << SMFC_MAP_CH2_SHIFT;
break;
case CSI_MEM3:
temp &= ~SMFC_MAP_CH3_MASK;
temp |= ((csi << 2) | mipi_id) << SMFC_MAP_CH3_SHIFT;
break;
default:
return;
}
ipu_smfc_write(ipu, temp, SMFC_MAP);
}
这个函数对比IPUx_SMFC_MAP寄存器来看:
...........................
..............................
从寄存器的介绍来看的话,其实_ipu_smfc_init函数中传入的mipi_id和csi参数都是没有作用的,因为这个函数根据传入的channel参数来决定执行哪一个switch分支,然后决定数据在SMFC中被映射到哪一路。
所以真正起作用的就是这个channel参数了。而这个channel参数是通过ipu_init_channel函数传进来的。所以下面简单追踪一下应用程序与驱动程序中的设置。
3.3
在mxc_v4l2_capture.c中,维护着一个全局数组,如下所示:
static struct v4l2_input mxc_capture_inputs[MXC_V4L2_CAPTURE_NUM_INPUTS] = {
{
.index = 0,
.name = "CSI IC MEM",
.type = V4L2_INPUT_TYPE_CAMERA,
.audioset = 0,
.tuner = 0,
.std = V4L2_STD_UNKNOWN,
.status = 0,
},
{
.index = 1,
.name = "CSI MEM",
.type = V4L2_INPUT_TYPE_CAMERA,
.audioset = 0,
.tuner = 0,
.std = V4L2_STD_UNKNOWN,
.status = V4L2_IN_ST_NO_POWER,
},
};
这个数组代表输入有几种形式,从这个数组可以看出来,对于摄像头采集设备来说,它有的两条通道就是:CSI-->SMFC-->MEM和CSI-->IC-->MEM。看这个数组成员中,重要的参数有两个:index和name,应用程序中可以通过-i选项来选择对应的index参数的值。在应用程序开始执行的时候,会首先执行open函数,对应到驱动中就是mxc_v4l_open函数,以及在后面,应用程序通过-i选项来指定的输入,是通过VIDIOC_S_INPUT这个ioctl调用来设置到驱动中的。先来看看mxc_v4l_open函数:
if (strcmp(mxc_capture_inputs[cam->current_input].name, "CSI MEM") == 0) {
err = csi_enc_select(cam);
} else if (strcmp(mxc_capture_inputs[cam->current_input].name,
"CSI IC MEM") == 0) {
err = prp_enc_select(cam);
}
这个cam->current_input在初始化的时候初始化为0了,mxc_capture_inputs[0].name== "CSI ICMEM",所以,默认选择的是CSI-->IC-->MEM这条通道。如果想要选择CSI-->SMFC-->MEM通路的话,就需要在应用程序中指定:-i1。而这个设置是在VIDIOC_S_INPUT中,同样会调用到上面那段代码,然后就会选择csi_enc_select函数来执行。然后在mxc_streamon函数中,就会执行cam->enc_enable函数,对应执行到ipu_csi_enc.c文件中的csi_enc_enabling_tasks函数,继续执行到csi_enc_setup函数,在csi_enc_setup函数中:
ipu_channel_t chan = (cam->csi == 0) ? CSI_MEM0 : CSI_MEM1;
err = ipu_init_channel(cam->ipu, chan, ¶ms);
_ipu_smfc_init(ipu, channel, 0, params->csi_mem.csi);
然后就在ipu_init_channel函数中,根据chan参数来选择执行哪一个case,因为会根据cam->csi号来选择chan=CSI_MEM0还是CSI_MEM1,而_ipu_smfc_init函数咱们在上面分析过了,它会根据传入的channel号来选择映射到哪一条dma_smfc_ch。而在csi_enc_setup函数中,设置了channel号的值,所以,对应的cam->csi== 0的,就会在SMFC中映射到dma_smfc_ch0;对应的cam->csi==1的,就会在SMFC中映射到dma_smfc_ch1,在驱动中没有指定CSI_MEM2和CSI_MEM3,所以dma_smfc_ch2和dma_smfc_ch3不会被使用到。
如果想使用dma_smfc_ch2和dma_smfc_ch3的话,就需要在驱动程序中的ipu_init_channel函数之前,将channel号设置为CSI_MEM2和CSI_MEM3。但是目前的代码中没有这么做。