4.2 ipu_init_channel函数的详细分析

在看这个函数之前,先对ipu内部的channel有所了解,先来看看ipu内部的flow的定义:


对于每个flow,代表数据从摄像头采集到显示(或者保存在内存中)的过程中的一个分步骤,每个flow都对应一个或几个channel,而每个channel的数据流通过程中,是通过DMA传输的,所以每个channel里面又包含一个或几个dma_channel。在这不太好理解,看看手册中的介绍(captureflow):


其中上面和程序中所说的channel对应图中的Taskschain,对于这个channel的理解需要根据ipu内部架构图来理解,如下所示:

以上面CSI0-->SMFC-->MEM这个channel为例,它代表数据从CSI经过SMFC到达内存MEM中,因为数据是从CSI中获得的,所以physicalDMA channel中的videoinput为空,而数据从这个channel输出的话,就需要通过DMAchannel了,从图上可以看出来,physicalDMA channel中的videooutput可以为IDMAC_CH_0~ IDMAC_CH_3

同样对于captureflow,还可以通过CSI0-->SMFC--->MEMCSI0-->VDIC-->MEM这两种方式将摄像头采集到的数据存放到内存中。


再来看手册中的Processingflows的图:

MEM-->IC-->MEM这个channel为例,根据IPU内部框架图可以看到,从内存中取出数据,经过IC处理以后再放入内存中,那么取出数据的时候,就使用到DMAchannel了,从这个图中可以看出来,使用的是IDMAC_CH12,再次放入内存中的时候就使用到IDMAC_CH20


对于其他channel就暂时不一一分析了。再来看看程序中是怎样定义channel的。

内核中使用ipu_channel_t枚举来表示一个channel

typedef enum { 
	CHAN_NONE = -1, 
	MEM_ROT_ENC_MEM = _MAKE_CHAN(1, 45, NO_DMA, NO_DMA, 48), 
	MEM_ROT_VF_MEM = _MAKE_CHAN(2, 46, NO_DMA, NO_DMA, 49), 
	MEM_ROT_PP_MEM = _MAKE_CHAN(3, 47, NO_DMA, NO_DMA, 50), 

	MEM_PRP_ENC_MEM = _MAKE_CHAN(4, 12, 14, 17, 20), 
	MEM_PRP_VF_MEM = _MAKE_CHAN(5, 12, 14, 17, 21), 
	MEM_PP_MEM = _MAKE_CHAN(6, 11, 15, 18, 22), 

	MEM_DC_SYNC = _MAKE_CHAN(7, 28, NO_DMA, NO_DMA, NO_DMA), 
	MEM_DC_ASYNC = _MAKE_CHAN(8, 41, NO_DMA, NO_DMA, NO_DMA), 
	MEM_BG_SYNC = _MAKE_CHAN(9, 23, NO_DMA, 51, NO_DMA), 
	MEM_FG_SYNC = _MAKE_CHAN(10, 27, NO_DMA, 31, NO_DMA), 

	MEM_BG_ASYNC0 = _MAKE_CHAN(11, 24, NO_DMA, 52, NO_DMA), 
	MEM_FG_ASYNC0 = _MAKE_CHAN(12, 29, NO_DMA, 33, NO_DMA), 
	MEM_BG_ASYNC1 = _MAKE_ALT_CHAN(MEM_BG_ASYNC0), 
	MEM_FG_ASYNC1 = _MAKE_ALT_CHAN(MEM_FG_ASYNC0), 

	DIRECT_ASYNC0 = _MAKE_CHAN(13, NO_DMA, NO_DMA, NO_DMA, NO_DMA), 
	DIRECT_ASYNC1 = _MAKE_CHAN(14, NO_DMA, NO_DMA, NO_DMA, NO_DMA), 

	CSI_MEM0 = _MAKE_CHAN(15, NO_DMA, NO_DMA, NO_DMA, 0), 
	CSI_MEM1 = _MAKE_CHAN(16, NO_DMA, NO_DMA, NO_DMA, 1), 
	CSI_MEM2 = _MAKE_CHAN(17, NO_DMA, NO_DMA, NO_DMA, 2), 
	CSI_MEM3 = _MAKE_CHAN(18, NO_DMA, NO_DMA, NO_DMA, 3), 

	CSI_MEM = CSI_MEM0, 

	CSI_PRP_ENC_MEM = _MAKE_CHAN(19, NO_DMA, NO_DMA, NO_DMA, 20), 
	CSI_PRP_VF_MEM = _MAKE_CHAN(20, NO_DMA, NO_DMA, NO_DMA, 21), 

	/* for vdi mem->vdi->ic->mem , add graphics plane and alpha*/ 
	MEM_VDI_PRP_VF_MEM_P = _MAKE_CHAN(21, 8, 14, 17, 21), 
	MEM_VDI_PRP_VF_MEM = _MAKE_CHAN(22, 9, 14, 17, 21), 
	MEM_VDI_PRP_VF_MEM_N = _MAKE_CHAN(23, 10, 14, 17, 21), 

	/* for vdi mem->vdi->mem */ 
	MEM_VDI_MEM_P = _MAKE_CHAN(24, 8, NO_DMA, NO_DMA, 5), 
	MEM_VDI_MEM = _MAKE_CHAN(25, 9, NO_DMA, NO_DMA, 5), 
	MEM_VDI_MEM_N = _MAKE_CHAN(26, 10, NO_DMA, NO_DMA, 5), 

	/* fake channel for vdoa to link with IPU */ 
	MEM_VDOA_MEM =  _MAKE_CHAN(27, NO_DMA, NO_DMA, NO_DMA, NO_DMA), 

	MEM_PP_ADC = CHAN_NONE, 
	ADC_SYS2 = CHAN_NONE, 
} ipu_channel_t;
#define _MAKE_CHAN(num, v_in, g_in, a_in, out) \ 
	((num << 24) | (v_in << 18) | (g_in << 12) | (a_in << 6) | out)

这个枚举就包含了所有的channel,如果对于上面讲解的过程理解的话,很容易根据channel的过程在这个枚举中找到对应的channel名字。同时可以看到在channel通过_MAKE_CHAN宏的构造过程中,每个channel里面都包含了输入输出dmachannel号。


分析完对channel的理解过程以后,再来看看具体的函数实现:

ipu_init_channel函数

int32_t ipu_init_channel(struct ipu_soc *ipu, ipu_channel_t channel,
 ipu_channel_params_t *params) 

/*这个函数有3个参数,第一个参数ipu代表正在使用的ipu。第二个参数是想要初始化的channel,它其实就是一个数字,可以理解为ID,第三个参数params是想要将这个channel初始化成什么样子,它里面包含channel的一些信息,会根据params参数来初始化这个channel。关于这个结构体的详细讲解可以看《ipu_channel_params_t结构体详解》*/

{ 
	int ret = 0; 
	bool bad_pixfmt; 
	uint32_t ipu_conf, reg, in_g_pixel_fmt, sec_dma; 

	dev_dbg(ipu->dev, "init channel = %d\n", IPU_CHAN_ID(channel)); 

	ret = pm_runtime_get_sync(ipu->dev); 
	if (ret < 0) { 
		dev_err(ipu->dev, "ch = %d, pm_runtime_get failed:%d!\n", 
				IPU_CHAN_ID(channel), ret); 
		dump_stack(); 
		return ret; 
	} 
	/* 
	 * Here, ret could be 1 if the device's runtime PM status was 
	 * already 'active', so clear it to be 0. 
	 */ 

/*看注释,可以看出来,上面一个函数如果执行成功的话,它的返回值是1,所以在下面需要将这个ret清零。跟踪源码,确实是这样的。*/

	ret = 0; 
 	
	_ipu_get(ipu); 

	mutex_lock(&ipu->mutex_lock); 

	/* Re-enable error interrupts every time a channel is initialized */ 
	ipu_cm_write(ipu, 0xFFFFFFFF, IPU_INT_CTRL(5)); 
	ipu_cm_write(ipu, 0xFFFFFFFF, IPU_INT_CTRL(6)); 
	ipu_cm_write(ipu, 0xFFFFFFFF, IPU_INT_CTRL(9)); 
	ipu_cm_write(ipu, 0xFFFFFFFF, IPU_INT_CTRL(10)); 

/*在初始化每一个channel的时候都需要重新使能错误中断。*/

	if (ipu->channel_init_mask & (1L << IPU_CHAN_ID(channel))) { 
		dev_warn(ipu->dev, "Warning: channel already initialized %d\n", 
			IPU_CHAN_ID(channel)); 
	} 

/*这个ipu_soc结构体里面的channel_init_mask项是uint32_t类型的,每个channel对应这个数字里面的某一位,如果这个channel进行过初始化操作的话,就将它的那一位置1.通过这个来判断其中的某一个channel是否进行过初始化。所以在这个初始化函数中肯定有将它置位的操作,我们搜索源码可以发现,在这个函数的后面确实有这样的操作。先在这粘贴一下:

ipu->channel_init_mask|= 1L << IPU_CHAN_ID(channel);

*/

	ipu_conf = ipu_cm_read(ipu, IPU_CONF); 

/*下面的switch判断语句就是根据channel的值来初始化不同的channel,所以这么多case就包括了所有的channel*/

	switch (channel) { 
	case CSI_MEM0: 
	case CSI_MEM1: 
	case CSI_MEM2: 
	case CSI_MEM3: 
		if (params->csi_mem.csi > 1) { //csi有2个,取值为0和1.
			ret = -EINVAL; 
			goto err; 
		} 

		if (params->csi_mem.interlaced) 
			ipu->chan_is_interlaced[channel_2_dma(channel, 
				IPU_OUTPUT_BUFFER)] = true; 
		else 
			ipu->chan_is_interlaced[channel_2_dma(channel, 
				IPU_OUTPUT_BUFFER)] = false; 

/*ipu->chan_is_interlaced是一个bool型的数组,根据params里面的csi_mem.interlaced参数,来设置ipu->chan_is_interlaced数组的某一项。怎么确定是数组的第几项?根据channel的值通过channel_2_dma函数为它在数组中挑选一个位置,然后将这个位置设置为truefalse*/

		ipu->smfc_use_count++; //增加smfc使用计数,这几个channel都会使用到smfc。
		ipu->csi_channel[params->csi_mem.csi] = channel;

/*根据params->csi_mem.csi来确定是ipu->csi_channel数组的第几项,然后将channel赋给它。这个ipu->csi_channel数组包含两项,意思就是每个ipu有两个csi接口,每个接口选择哪个channel,都在这里进行保存。*/

		/*SMFC setting*/ 
		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); 
		} 

/*paramscsi_mem.mipi_en是个bool类型的值,如果它为1的话,表示使用的是mipi引脚。

下面来看看ipu_conf寄存器:


这里设置的是ipu_conf寄存器的28或者29位,看看这两位的介绍:




这两位的含义是选择CSI的数据来源,我们知道,CSI接口可以接并行接口或者MIPI接口,驱动会根据mipi_en的值来选择将这两位的数据来源设置为并口或者MIPI接口。如果是MIPI接口的话,需要通过_ipu_csi_set_mipi_di函数来设置MIPI相关的寄存器。

同时,不论是哪一种接口,都需要使用SMFC,就需要通过_ipu_smfc_init函数来初始化smfcSMFC_MAP寄存器,在这个_ipu_smfc_init函数中根据不同的case选择不同的SMFC_MAP_CH;然后再调用_ipu_csi_set_mipi_di函数来初始化CSI_MIPI_DI寄存器。这个寄存器是mipidata identifier的意思。

*/

		/*CSI data (include compander) dest*/ 
		_ipu_csi_init(ipu, channel, params->csi_mem.csi); 
		break; 

/*最后通过_ipu_csi_init函数初始化csi*/

/*上面的channel流程就是下图这种模式:数据从CSI中通过SMFC直接到达IDMAC,然后通过IDMAC来进行数据的存放等操作。



*/

	case CSI_PRP_ENC_MEM: 
		if (params->csi_prp_enc_mem.csi > 1) { 
			ret = -EINVAL; 
			goto err; 
		} 
		if ((ipu->using_ic_dirct_ch == MEM_VDI_PRP_VF_MEM) || 
			(ipu->using_ic_dirct_ch == MEM_VDI_MEM)) { 
			ret = -EINVAL; 
			goto err; 
		} 
		ipu->using_ic_dirct_ch = CSI_PRP_ENC_MEM; 

/*这个ipu->using_ic_dirct_ch是个ipu_channel_t类型的变量,usingic direct channel的意思,只能使用ICchannel而不能使用VDIchannel,所以是MEM_VDI_PRP_VF_MEMMEM_VDI_MEM的话就会报错。最后再把这一位置位成CSI_PRP_ENC_MEM*/

		ipu->ic_use_count++; //增加ic的引用计数。
		ipu->csi_channel[params->csi_prp_enc_mem.csi] = channel; 

/*这个ipu_soc结构体中有一项ipu_channel_tcsi_channel[2];因为每个ipu有两个csi,所以这个数组里面保存的是每个csi选用的channel通道。在每一个需要用到csi接口的channel中都需要设置这一项,可以看到在这个switch中前几个用到csicase,里面都设置了这一项。*/

/*同时在params这个参数中,它是ipu_channel_params_t类型的联合,其中包含的两个关于csi的结构体csi_memcsi_prp_enc_mem,这两个结构体里面都含有一个uint32_tcsi,就是通过它来确定想要使用的是哪一个csi设备。 */

		if (params->csi_prp_enc_mem.mipi_en) { 
			ipu_conf |= (1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET + 
				params->csi_prp_enc_mem.csi)); 
			_ipu_csi_set_mipi_di(ipu, 
				params->csi_prp_enc_mem.mipi_vc, 
				params->csi_prp_enc_mem.mipi_id, 
				params->csi_prp_enc_mem.csi); 
		} else 
			ipu_conf &= ~(1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET + 
				params->csi_prp_enc_mem.csi)); 

/*这个channel没有用到smfc,所以不用初始化它,其他跟上面那个channel一样。设置IPU_CONF寄存器中的28,29位。如果是MIPI接口的话,还需要通过_ipu_csi_set_mipi_di函数来初始化MIPI相关的寄存器。*/

		/*CSI0/1 feed into IC*/ 
		ipu_conf &= ~IPU_CONF_IC_INPUT; 
		if (params->csi_prp_enc_mem.csi) 
			ipu_conf |= IPU_CONF_CSI_SEL; 
		else 
			ipu_conf &= ~IPU_CONF_CSI_SEL; 

/*设置ipu_conf中的IPU_CONF_IC_INPUT位和CSI_SEL位。这两位的含义如下:



在初始化这个channel的时候,我们应该首先想到这个channel使用到了IPU内部的哪些模块,在上面一个channel中,使用到了CSISMFC这两个模块,所以需要设置ipu_conf寄存器中的28,29数据来源位,选择数据是从并口来的还是从MIPI接口来的,然后分别初始化CSISMFC即可。

对于本次这个channelCSI-->IC(prpenc)-->MEM,同样的思想,首先想想这个channel使用了哪几个模块,使用了CSIIC模块,然后同样设置ipu_conf寄存器中的28,29数据来源位,然后就是设置3031位了,30位设置IC的输入数据是从哪里来的,从IPU内部框架中可以看出来,输入IC的数据只能是VDICSI,所以这里设置30位为0。但是CSI的话还有两个呢,这就需要设置31位来确定是哪一个CSI设备了。

*/

		/*PRP skip buffer in memory, only valid when RWS_EN is true*/ 
		reg = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1); 
		ipu_cm_write(ipu, reg & ~FS_ENC_IN_VALID, IPU_FS_PROC_FLOW1); 

/*IPU_FS_PROC_FLOW1寄存器里面的FS_ENC_IN_VALID位清零。



这里设置的就是IPU_FS_PROC_FLOW1寄存器的30位,这一位的含义如上所示,它表示是否应该跳过内存中的buffer。这一位是根据RWS_EN来选择的。那么RWS_EN又是什么?在IC_CONF寄存器中找到了它,如下所示:





其中RWSrawsensor的意思,一般sensor的输出数据可以为YUVJPEGRGBRAW几种格式,其中这个RAW格式是未经处理、也未经压缩的格式记录了数码相机传感器的原始信息可以把RAW概念化为“原始图像编码数据”或更形象的称为“数字底片”。所以RWS_EN表示摄像头输出的数据是否是原始的RAW数据。

RWS_EN0时,数据直接从CSI输入到IC中,所以IPU_FS_PROC_FLOW1这个寄存器就需要设置跳过从内存中到达IC的数据IPU_FS_PROC_FLOW1寄存器的ENC_IN_VALID位清零

*/

		/*CSI data (include compander) dest*/ 
		_ipu_csi_init(ipu, channel, params->csi_prp_enc_mem.csi); 
		_ipu_ic_init_prpenc(ipu, params, true); 
		break; 

/*最后通过_ipu_csi_init函数初始化csi,同时通过_ipu_ic_init_prpenc函数预处理编码,这个函数在ipu_ic.c中。*/

/*至此,这个CSI_PRP_ENC_MEMchannel简单分析完毕,它的流程如下图所示:



*/

	case CSI_PRP_VF_MEM: 
		if (params->csi_prp_vf_mem.csi > 1) { 
			ret = -EINVAL; 
			goto err; 
		} 
		if ((ipu->using_ic_dirct_ch == MEM_VDI_PRP_VF_MEM) || 
			(ipu->using_ic_dirct_ch == MEM_VDI_MEM)) { 
			ret = -EINVAL; 
			goto err; 
		} 
		ipu->using_ic_dirct_ch = CSI_PRP_VF_MEM; 

/*与上面那个CSI_PRP_ENC_MEMchannel一样,这个ipu->using_ic_dirct_ch只能置位成当前这个CSI_PRP_VF_MEMchannel*/

		ipu->ic_use_count++; //增加ic的引用计数。
		ipu->csi_channel[params->csi_prp_vf_mem.csi] = channel; 

		if (params->csi_prp_vf_mem.mipi_en) { 
			ipu_conf |= (1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET + 
				params->csi_prp_vf_mem.csi)); 
			_ipu_csi_set_mipi_di(ipu, 
				params->csi_prp_vf_mem.mipi_vc, 
				params->csi_prp_vf_mem.mipi_id, 
				params->csi_prp_vf_mem.csi); 
		} else 
			ipu_conf &= ~(1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET + 
				params->csi_prp_vf_mem.csi)); 

		/*CSI0/1 feed into IC*/ 
		ipu_conf &= ~IPU_CONF_IC_INPUT; 
		if (params->csi_prp_vf_mem.csi) 
			ipu_conf |= IPU_CONF_CSI_SEL; 
		else 
			ipu_conf &= ~IPU_CONF_CSI_SEL; 

		/*PRP skip buffer in memory, only valid when RWS_EN is true*/ 
		reg = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1); 
		ipu_cm_write(ipu, reg & ~FS_VF_IN_VALID, IPU_FS_PROC_FLOW1); 

/*与上一个函数不同的是,设置的IPU_FS_PROC_FLOW1寄存器的VF_IN_VALID位,原理是相同的。*/

		/*CSI data (include compander) dest*/ 
		_ipu_csi_init(ipu, channel, params->csi_prp_vf_mem.csi); 
		_ipu_ic_init_prpvf(ipu, params, true); 
		break; 

/*除了最后一个函数,其他都与上一个case一样,最后一个函数_ipu_ic_init_prpvf同样在ipu_ic.c中。*/

/*这个CSI_PRP_VF_MEMchannel的流程如下所示:


*/

	case MEM_PRP_VF_MEM: 
		if (params->mem_prp_vf_mem.graphics_combine_en) { 
			sec_dma = channel_2_dma(channel, IPU_GRAPH_IN_BUFFER); 
			in_g_pixel_fmt = params->mem_prp_vf_mem.in_g_pixel_fmt; 
			bad_pixfmt = 
				_ipu_ch_param_bad_alpha_pos(in_g_pixel_fmt); 
			if (params->mem_prp_vf_mem.alpha_chan_en) { 
				if (bad_pixfmt) { 
					dev_err(ipu->dev, "bad pixel format " 
						"for graphics plane from " 
						"ch%d\n", sec_dma); 
					ret = -EINVAL; 
					goto err; 
				} 
				ipu->thrd_chan_en[IPU_CHAN_ID(channel)] = true; 
			} 
			ipu->sec_chan_en[IPU_CHAN_ID(channel)] = true; 
		} 

/*bad_pixfmt的意思可以在查看_ipu_ch_param_bad_alpha_pos函数的时候大致了解,在IPUv3IDMAC中对于32bpp的格式有个bug,所以对于32bpp的都返回true,对于其他返回false*/


/*下面这个涉及到alpha通道的一些知识,

http://baike.baidu.com/link?url=8p8Z8B2o_qOdahK5nfH-9ZiGfcubcPFWVxVCxCWn3XEItoG2KDmTLUpObjQMIp59VvTLBpJNzHYs4-9N4O_KOq

RGBA》里面有简单介绍。*/

/*上面的代码,会根据ipu_channel_params_t里面的mem_prp_vf_mem这一项的graphics_combine_en来决定是否使能secondchannel,根据mem_prp_vf_mem这一项的alpha_chan_en来决定是否使能thirdchannel。如果使能的话,就会设置ipu_soc中的sec_chan_en[]thrd_chan_en[]这两项,根据channelID号来置位。*/

		reg = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1); 
		ipu_cm_write(ipu, reg | FS_VF_IN_VALID, IPU_FS_PROC_FLOW1); 

		_ipu_ic_init_prpvf(ipu, params, false); 
		ipu->ic_use_count++; 
		break; 

/*其他函数大致都相似,就是细节不太相同,比如将IPU_FS_PROC_FLOW1寄存器的FS_VF_IN_VALID位设置为1_ipu_ic_init_prpvf函数的第三个参数是false等等,_ipu_ic_init_prpvf函数的第三个参数是bool类型的变量,true代表IC的数据来源是CSIfalse代表IC的数据来源不是CSI*/

/*注意这个函数中设置了ipu_soc结构体中的sec_chan_enthrd_chan_en,是两个bool型的数组,从名字上来看就是secondchannel enablethirdchannelenable的意思,我认为MEM_PRP_VF_MEM这个channel是可以和其他某些channel一起启用的(在手册的CM模块对于IPUmainflows进行了详细的讲解,在后面具体分析)。根据params参数中mem_prp_vf_mem.graphics_combine_en置位的话,就会启用第二个channel,如果params参数中mem_prp_vf_mem.alpha_chan_en继续置位的话,就继续启用第三个channel。将启用了几个channel这些信息都保存在ipu_soc结构体中。*/

	case MEM_VDI_PRP_VF_MEM: 
		if ((ipu->using_ic_dirct_ch == CSI_PRP_VF_MEM) || 
			(ipu->using_ic_dirct_ch == MEM_VDI_MEM) || 
		     (ipu->using_ic_dirct_ch == CSI_PRP_ENC_MEM)) { 
			ret = -EINVAL; 
			goto err; 
		} 
		ipu->using_ic_dirct_ch = MEM_VDI_PRP_VF_MEM; 
		ipu->ic_use_count++; 
		ipu->vdi_use_count++; 
		reg = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1); 
		reg &= ~FS_VDI_SRC_SEL_MASK; 
		ipu_cm_write(ipu, reg , IPU_FS_PROC_FLOW1); 

		if (params->mem_prp_vf_mem.graphics_combine_en) 
			ipu->sec_chan_en[IPU_CHAN_ID(channel)] = true; 
		_ipu_ic_init_prpvf(ipu, params, false); 
		_ipu_vdi_init(ipu, channel, params); 
		break; 

/*大部分设置都是相似的,在这个channel中使用到了VDIC,所以需要设置IPU_FS_PROC_FLOW1寄存器中有关VDIC的位:VDI_SRC_SEL,手册中的解释如下:




从程序中可以看到,只是将这些位清空了,所以默认的VDIC的数据来源是ARM平台。

同样,设置IC初始化为prpvf模式,然后通过_ipu_vdi_init初始化VDIC_ipu_vdi_initipu_ic.c中定义。*/

/*这个模式的流程是下面这张图:


*/

	case MEM_VDI_PRP_VF_MEM_P: 
	case MEM_VDI_PRP_VF_MEM_N: 
	case MEM_VDI_MEM_P: 
	case MEM_VDI_MEM_N: 
		_ipu_vdi_init(ipu, channel, params); 
		break; 

/*这几个channel不再分析了。*/

	case MEM_VDI_MEM: 
		if ((ipu->using_ic_dirct_ch == CSI_PRP_VF_MEM) || 
			(ipu->using_ic_dirct_ch == MEM_VDI_PRP_VF_MEM) || 
		     (ipu->using_ic_dirct_ch == CSI_PRP_ENC_MEM)) { 
			ret = -EINVAL; 
			goto err; 
		} 
		ipu->using_ic_dirct_ch = MEM_VDI_MEM; 
		ipu->ic_use_count++; 
		ipu->vdi_use_count++; 
		_ipu_vdi_init(ipu, channel, params); 
		break; 

/*设置ipu_soc中的using_ic_dirct_ch,增加ic_use_countvdi_use_count的引用计数,最终也是调用这个_ipu_vdi_init函数。这个函数中增加了vdi_use_count引用计数,上面几个与这个channel相似的channel都没有增加这个引用计数。这个channel只使用了VDIC*/

	case MEM_ROT_VF_MEM: 
		ipu->ic_use_count++; 
		ipu->rot_use_count++; 
		_ipu_ic_init_rotate_vf(ipu, params); 
		break; 
	case MEM_PRP_ENC_MEM: 
		ipu->ic_use_count++; 
		reg = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1); 
		ipu_cm_write(ipu, reg | FS_ENC_IN_VALID, IPU_FS_PROC_FLOW1); 
		_ipu_ic_init_prpenc(ipu, params, false); 
		break; 
	case MEM_ROT_ENC_MEM: 
		ipu->ic_use_count++; 
		ipu->rot_use_count++; 
		_ipu_ic_init_rotate_enc(ipu, params); 
		break; 

/*上面几个channel程序比较简单,就暂时不分析。*/

	case MEM_PP_MEM: 
		if (params->mem_pp_mem.graphics_combine_en) { 
			sec_dma = channel_2_dma(channel, IPU_GRAPH_IN_BUFFER); 
			in_g_pixel_fmt = params->mem_pp_mem.in_g_pixel_fmt; 
			bad_pixfmt = 
				_ipu_ch_param_bad_alpha_pos(in_g_pixel_fmt); 

			if (params->mem_pp_mem.alpha_chan_en) { 
				if (bad_pixfmt) { 
					dev_err(ipu->dev, "bad pixel format " 
						"for graphics plane from " 
						"ch%d\n", sec_dma); 
					ret = -EINVAL; 
					goto err; 
				} 
				ipu->thrd_chan_en[IPU_CHAN_ID(channel)] = true; 
			} 

			ipu->sec_chan_en[IPU_CHAN_ID(channel)] = true; 
		} 

		_ipu_ic_init_pp(ipu, params); 
		ipu->ic_use_count++; 
		break; 

/*这个channelMEM_PRP_VF_MEMchannel类似,它也是根据params参数启用了sec_chan_enthrd_chan_en*/

/*


*/

	case MEM_ROT_PP_MEM: 
		_ipu_ic_init_rotate_pp(ipu, params); 
		ipu->ic_use_count++; 
		ipu->rot_use_count++; 
		break; 
	case MEM_DC_SYNC: 
		if (params->mem_dc_sync.di > 1) { 
			ret = -EINVAL; 
			goto err; 
		} 

		ipu->dc_di_assignment[1] = params->mem_dc_sync.di; 
		_ipu_dc_init(ipu, 1, params->mem_dc_sync.di, 
			     params->mem_dc_sync.interlaced, 
			     params->mem_dc_sync.out_pixel_fmt); 
		ipu->di_use_count[params->mem_dc_sync.di]++; 
		ipu->dc_use_count++; 
		ipu->dmfc_use_count++; 
		break; 
	case MEM_BG_SYNC: 
		if (params->mem_dp_bg_sync.di > 1) { 
			ret = -EINVAL; 
			goto err; 
		} 

		if (params->mem_dp_bg_sync.alpha_chan_en) 
			ipu->thrd_chan_en[IPU_CHAN_ID(channel)] = true; 

		ipu->dc_di_assignment[5] = params->mem_dp_bg_sync.di; 
		_ipu_dp_init(ipu, channel, params->mem_dp_bg_sync.in_pixel_fmt, 
			     params->mem_dp_bg_sync.out_pixel_fmt); 
		_ipu_dc_init(ipu, 5, params->mem_dp_bg_sync.di, 
			     params->mem_dp_bg_sync.interlaced, 
			     params->mem_dp_bg_sync.out_pixel_fmt); 
		ipu->di_use_count[params->mem_dp_bg_sync.di]++; 
		ipu->dc_use_count++; 
		ipu->dp_use_count++; 
		ipu->dmfc_use_count++; 
		break; 
	case MEM_FG_SYNC: 
		_ipu_dp_init(ipu, channel, params->mem_dp_fg_sync.in_pixel_fmt, 
			     params->mem_dp_fg_sync.out_pixel_fmt); 

		if (params->mem_dp_fg_sync.alpha_chan_en) 
			ipu->thrd_chan_en[IPU_CHAN_ID(channel)] = true; 

		ipu->dc_use_count++; 
		ipu->dp_use_count++; 
		ipu->dmfc_use_count++; 
		break; 
	case DIRECT_ASYNC0: 
		if (params->direct_async.di > 1) { 
			ret = -EINVAL; 
			goto err; 
		} 

		ipu->dc_di_assignment[8] = params->direct_async.di; 
		_ipu_dc_init(ipu, 8, params->direct_async.di, false, IPU_PIX_FMT_GENERIC); 
		ipu->di_use_count[params->direct_async.di]++; 
		ipu->dc_use_count++; 
		break; 
	case DIRECT_ASYNC1: 
		if (params->direct_async.di > 1) { 
			ret = -EINVAL; 
			goto err; 
		} 

		ipu->dc_di_assignment[9] = params->direct_async.di; 
		_ipu_dc_init(ipu, 9, params->direct_async.di, false, IPU_PIX_FMT_GENERIC); 
		ipu->di_use_count[params->direct_async.di]++; 
		ipu->dc_use_count++; 
		break; 
	default: 
		dev_err(ipu->dev, "Missing channel initialization\n"); 
		break; 
	} 

	ipu->channel_init_mask |= 1L << IPU_CHAN_ID(channel); 

	ipu_cm_write(ipu, ipu_conf, IPU_CONF); 

err: 
	mutex_unlock(&ipu->mutex_lock); 
	return ret; 
} 
EXPORT_SYMBOL(ipu_init_channel);

/*后面几个channel是关于显示的channel,他们与前面几个case的步骤是相似的,暂时不分析,用到的时候再分析。*/

小总结:

可以看出来,在ipu_init_channel函数中,会根据对应的channel来决定使用哪个模块。

比如对于CSI_MEM0CSI_MEM3,就会使用SMFC_ipu_smfc_init函数),然后通过_ipu_csi_init函数来指定数据的目的地址。


对于CSI_PRP_ENC_MEMchannel,就会使用IC,使用的是ICprpenc功能,就需要使用_ipu_ic_init_prpenc函数,同样需要使用通过_ipu_csi_init函数来指定数据的目的地址。


对于CSI_PRP_VF_MEMchannel,需要使用到IC,但是使用的是ICprpvf功能,就需要使用_ipu_ic_init_prpvf函数。同样需要使用通过_ipu_csi_init函数来指定数据的目的地址。


上面几个函数都使用到了CSI设备,所以都使用了_ipu_csi_init函数来指定数据的目的地址。对于有些channel没有使用CSI设备,肯定就不用使用_ipu_csi_init这个函数了。


比如对于MEM_VDI_MEMchannel,它使用到VDI模块,只需要通过_ipu_vdi_init函数来设置即可。

比如MEM_ROT_VF_MEMchannel,它需要使用到ICrotateviewfinder功能,就需要通过_ipu_ic_init_rotate_vf函数来设置了。


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值