NXP

8.3 子模块分析之SMFC

2019-07-12 13:27发布

1)概述 Sensor Multifile Controller作为CSI模块和IDMAC之间的一个缓冲设备,每个SMFC可以支持两个CSI设备。 每个CSI设备可以最多发送4帧图像到SMFC中,SMFC通过csi_id号来区分是哪个CSI发送的。每一帧图像通过SMFC映射到4dmachannel其中的一个。每个dmachannel都是一个FIFO
2SMFC功能介绍 SMFC最多支持4dmachannel,每一个dmachannel都有一个专用的FIFO控制器(FIFOcontroller)。每个buffer中都含有真正的数据和帧ID。当RR优先级机制选中其中的某一个buffer后,会将对应buffer里面保存的ID号与CH#_MAP位进行比较,然后对应的FIFO控制器将会被使能,之后这个buffer里面的真正数据就会被拷贝到RAM中。wptrbase用来计算在RAM中的位置,rptr是在dma_active信号发生后跟随wptr发出。 这个过程如下图所示: 由于这四个channels不能够同时启用,所以四个FIFO需要使用同一个RAMSMFC所使用的内存空间分为4个相同的扇区,每一个FIFO拥有一个固定的基地址————“base”Channel0 2FIFO扇区大小是固定的,并且等于一个扇区的大小。Channel1 3FIFO扇区大小会根据其他FIFO扇区的大小来决定。如下图所示:
从这个图中可以看出来,Channel0 2FIFO扇区大小只能是0或者1,Channel1 3FIFO扇区大小就是可以调整的。
下图是SMFC的时序图: (三)代码实现 3.1需要使用到SMFCchannel就是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.2ipu_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_idcsi参数都是没有作用的,因为这个函数根据传入的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-->MEMCSI-->IC-->MEM。看这个数组成员中,重要的参数有两个:indexname,应用程序中可以通过-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_MEM2CSI_MEM3,所以dma_smfc_ch2dma_smfc_ch3不会被使用到。
如果想使用dma_smfc_ch2dma_smfc_ch3的话,就需要在驱动程序中的ipu_init_channel函数之前,将channel号设置为CSI_MEM2CSI_MEM3。但是目前的代码中没有这么做