4.3 ipu_init_channel_buffer函数的详细分析

ipu_init_channel_buffer函数

int32_t ipu_init_channel_buffer(struct ipu_soc *ipu, ipu_channel_t channel, 
				ipu_buffer_t type, 
				uint32_t pixel_fmt, 
				uint16_t width, uint16_t height, 
				uint32_t stride, 
				ipu_rotate_mode_t rot_mode, 
				dma_addr_t phyaddr_0, dma_addr_t phyaddr_1, 
				dma_addr_t phyaddr_2, 
				uint32_t u, uint32_t v) 
{ 
	uint32_t reg; 
	uint32_t dma_chan; 
	uint32_t burst_size; 

	dma_chan = channel_2_dma(channel, type); 
	if (!idma_is_valid(dma_chan)) 
		return -EINVAL; 

/*通过channel_2_dma函数将channel根据type提取出使用了哪个dmachannel。对于这个channel_2_dma函数,因为每个channel需要使用到dmachannel,在输入的时候可能使用,在输出的时候也可能使用,所以需要传入一个type类型来从channel中提取出所需要的dmachannel号。*/

	if (stride < width * bytes_per_pixel(pixel_fmt)) 
		stride = width * bytes_per_pixel(pixel_fmt); 

	if (stride % 4) { 
		dev_err(ipu->dev, 
			"Stride not 32-bit aligned, stride = %d\n", stride); 
		return -EINVAL; 
	} 

/*对传入的stride参数进行判断。*/

	/* IC & IRT channels' width must be multiple of 8 pixels */ 
	if ((_ipu_is_ic_chan(dma_chan) || _ipu_is_irt_chan(dma_chan)) 
		&& (width % 8)) { 
		dev_err(ipu->dev, "Width must be 8 pixel multiple\n"); 
		return -EINVAL; 
	} 

/*如果是ICIRTchannel的话,函数传入的width参数必须是8pixels的整数倍,否则就返回错误。*/

	if (_ipu_is_vdi_out_chan(dma_chan) && 
		((width < 16) || (height < 16) || (width % 2) || (height % 4))) { 
		dev_err(ipu->dev, "vdi width/height limited err\n"); 
		return -EINVAL; 
	} 

	/* IPUv3EX and IPUv3M support triple buffer */ 
	if ((!_ipu_is_trb_chan(ipu, dma_chan)) && phyaddr_2) { 
		dev_err(ipu->dev, "Chan%d doesn't support triple buffer " 
				   "mode\n", dma_chan); 
		return -EINVAL; 
	} 

/*这个函数比较绕,首先来看函数传入的参数里面有三个dma_addr_t类型的变量phyaddr_0phyaddr_1phyaddr_2。这三个变量每一个都对应一个buffer的基地址。_ipu_is_trb_chan函数是判断dma_chan是否支持3buffers,如果支持的话,就肯定不会执行下面的dev_err语句;如果不支持3buffers的话,那传入的参数里面phyaddr_2就必须为0,否则就会执行dev_err语句(都不支持3buffers了,那么第三个buffer的地址肯定为0)。*/

	if (!phyaddr_1 && phyaddr_2) { 
		dev_err(ipu->dev, "Chan%d's buf1 physical addr is NULL for " 
				   "triple buffer mode\n", dma_chan); 
		return -EINVAL; 
	} 

/*这个判断语句是phyaddr_1存在的话就肯定不会执行dev_err语句,如果phyaddr_1phyaddr_2都不存在的话,就会报错。*/

	mutex_lock(&ipu->mutex_lock); 

	/* Build parameter memory data for DMA channel */ 
	_ipu_ch_param_init(ipu, dma_chan, pixel_fmt, width, height, stride, u, v, 0, phyaddr_0, phyaddr_1, phyaddr_2); 

/*_ipu_ch_param_init函数内部,首先根据传入的pixel_fmt,width, height, stride, u, v, 0, phyaddr_0, phyaddr_1,phyaddr_2的值来设置一个structipu_ch_param类型的params变量,然后将这个变量通过fill_cpmem函数写到通过ipu,dma_chan这两个参数确定的CPMEM中。如果phyaddr_2参数不为空的话,还需要通过fill_cpmem函数将params的值写入ipusub_ch确定的寄存器的基址中。这个_ipu_ch_param_init函数很重要。*/

	/* Set correlative channel parameter of local alpha channel */ 
	if ((_ipu_is_ic_graphic_chan(dma_chan) || 
	     _ipu_is_dp_graphic_chan(dma_chan)) && 
	    (ipu->thrd_chan_en[IPU_CHAN_ID(channel)] == true)) { 
		_ipu_ch_param_set_alpha_use_separate_channel(ipu, dma_chan, true); 
		_ipu_ch_param_set_alpha_buffer_memory(ipu, dma_chan); 
		_ipu_ch_param_set_alpha_condition_read(ipu, dma_chan); 
		/* fix alpha width as 8 and burst size as 16*/ 
		_ipu_ch_params_set_alpha_width(ipu, dma_chan, 8); 
		_ipu_ch_param_set_burst_size(ipu, dma_chan, 16); 
	} else if (_ipu_is_ic_graphic_chan(dma_chan) && 
		   ipu_pixel_format_has_alpha(pixel_fmt)) 
		_ipu_ch_param_set_alpha_use_separate_channel(ipu, dma_chan, false); 

/*设置alpha通道的一些信息,上面这些子函数都分析了,他们就是设置dma_chan对应的channel的某些位。*/

	if (rot_mode) 
		_ipu_ch_param_set_rotation(ipu, dma_chan, rot_mode); 

/*设置rot_mode参数在dma_chan中对应的某些位。*/

	/* IC and ROT channels have restriction of 8 or 16 pix burst length */ 
	if (_ipu_is_ic_chan(dma_chan) || _ipu_is_vdi_out_chan(dma_chan)) { 
		if ((width % 16) == 0) 
			_ipu_ch_param_set_burst_size(ipu, dma_chan, 16); 
		else 
			_ipu_ch_param_set_burst_size(ipu, dma_chan, 8); 
	} else if (_ipu_is_irt_chan(dma_chan)) { 
		_ipu_ch_param_set_burst_size(ipu, dma_chan, 8); 
		_ipu_ch_param_set_block_mode(ipu, dma_chan); 
	} else if (_ipu_is_dmfc_chan(dma_chan)) { 
		burst_size = _ipu_ch_param_get_burst_size(ipu, dma_chan); 
		_ipu_dmfc_set_wait4eot(ipu, dma_chan, width); 
		_ipu_dmfc_set_burst_size(ipu, dma_chan, burst_size); 
	} 

/*根据不同的channel设置burstlength的不同值。*/

	if (_ipu_disp_chan_is_interlaced(ipu, channel) || 
		ipu->chan_is_interlaced[dma_chan]) 
		_ipu_ch_param_set_interlaced_scan(ipu, dma_chan); 

/*设置interlaced参数。*/

	if (_ipu_is_ic_chan(dma_chan) || _ipu_is_irt_chan(dma_chan) || 
		_ipu_is_vdi_out_chan(dma_chan)) { 
		burst_size = _ipu_ch_param_get_burst_size(ipu, dma_chan); 
		_ipu_ic_idma_init(ipu, dma_chan, width, height, burst_size, 
			rot_mode); 
	} 
/* 如果是 ic, irt或者 vdi_out channel的话,就需要调用_ipu_ic_idma_init函数来初始化ic和idma,这个函数在ipu_ic.c中定义。 */
else if (_ipu_is_smfc_chan(dma_chan)) { 
		burst_size = _ipu_ch_param_get_burst_size(ipu, dma_chan); 
		/* 
		 * This is different from IPUv3 spec, but it is confirmed 
		 * in IPUforum that SMFC burst size should be NPB[6:3] 
		 * when IDMAC works in 16-bit generic data mode. 
		 */ 
		if (pixel_fmt == IPU_PIX_FMT_GENERIC) 
			/* 8 bits per pixel */ 
			burst_size = burst_size >> 4; 
		else if (pixel_fmt == IPU_PIX_FMT_GENERIC_16) 
			/* 16 bits per pixel */ 
			burst_size = burst_size >> 3; 
		else 
			burst_size = burst_size >> 2; 
		_ipu_smfc_set_burst_size(ipu, channel, burst_size-1); 
	} 

/*如果是smfcchannel的话,就直接根据channel参数的值设置(burst_size)即可_ipu_smfc_set_burst_size这个函数在ipu_capture.c中定义。*/

	switch (dma_chan) { 
	case 0: 
	case 1: 
	case 2: 
	case 3: 
		_ipu_ch_param_set_axi_id(ipu, dma_chan, ipu->ch0123_axi); 
		break; 
	case 23: 
		_ipu_ch_param_set_axi_id(ipu, dma_chan, ipu->ch23_axi); 
		break; 
	case 27: 
		_ipu_ch_param_set_axi_id(ipu, dma_chan, ipu->ch27_axi); 
		break; 
	case 28: 
		_ipu_ch_param_set_axi_id(ipu, dma_chan, ipu->ch28_axi); 
		break; 
	default: 
		_ipu_ch_param_set_axi_id(ipu, dma_chan, ipu->normal_axi); 
		break; 
	} 

/*设置AXIprotocol id,这些AXIid是在ipu_probe函数中,从ipu_platform_type中获取到的。*/

	if (idma_is_set(ipu, IDMAC_CHA_PRI(dma_chan), dma_chan) && 
	    ipu->devtype == IPUv3H) { 
		uint32_t reg = IDMAC_CH_LOCK_EN_1(ipu->devtype); 
		uint32_t value = 0; 

		switch (dma_chan) { 
		case 5: 
			value = 0x3; 
			break; 
		case 11: 
			value = 0x3 << 2; 
			break; 
		case 12: 
			value = 0x3 << 4; 
			break; 
		case 14: 
			value = 0x3 << 6; 
			break; 
		case 15: 
			value = 0x3 << 8; 
			break; 
		case 20: 
			value = 0x3 << 10; 
			break; 
		case 21: 
			value = 0x3 << 12; 
			break; 
		case 22: 
			value = 0x3 << 14; 
			break; 
		case 23: 
			value = 0x3 << 16; 
			break; 
		case 27: 
			value = 0x3 << 18; 
			break; 
		case 28: 
			value = 0x3 << 20; 
			break; 
		case 45: 
			reg = IDMAC_CH_LOCK_EN_2(ipu->devtype); 
			value = 0x3 << 0; 
			break; 
		case 46: 
			reg = IDMAC_CH_LOCK_EN_2(ipu->devtype); 
			value = 0x3 << 2; 
			break; 
		case 47: 
			reg = IDMAC_CH_LOCK_EN_2(ipu->devtype); 
			value = 0x3 << 4; 
			break; 
		case 48: 
			reg = IDMAC_CH_LOCK_EN_2(ipu->devtype); 
			value = 0x3 << 6; 
			break; 
		case 49: 
			reg = IDMAC_CH_LOCK_EN_2(ipu->devtype); 
			value = 0x3 << 8; 
			break; 
		case 50: 
			reg = IDMAC_CH_LOCK_EN_2(ipu->devtype); 
			value = 0x3 << 10; 
			break; 
		default: 
			break; 
		} 
		value |= ipu_idmac_read(ipu, reg); 
		ipu_idmac_write(ipu, value, reg); 
	} 

/*设置idmac寄存器的一些参数。*/

	_ipu_ch_param_dump(ipu, dma_chan); 

	if (phyaddr_2 && ipu->devtype >= IPUv3EX) { 
		reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(dma_chan)); 
		reg &= ~idma_mask(dma_chan); 
		ipu_cm_write(ipu, reg, IPU_CHA_DB_MODE_SEL(dma_chan)); 

		reg = ipu_cm_read(ipu, 
				IPU_CHA_TRB_MODE_SEL(ipu->devtype, dma_chan)); 
		reg |= idma_mask(dma_chan); 
		ipu_cm_write(ipu, reg, 
				IPU_CHA_TRB_MODE_SEL(ipu->devtype, dma_chan)); 

/*上面这些步骤的意思是:如果(phyaddr_2&& ipu->devtype >=IPUv3EX)这个条件满足的话,就意味着这个channel支持triplebuffer MODE,所以先将寄存器中doublebuffer MODE的位清零,然后将triplebuffer MODE的位置位。至于是哪个寄存器。。。。通过IPU_CHA_DB_MODE_SEL这个宏可以看出来应该是CM寄存器。*/

		/* Set IDMAC third buffer's cpmem number */ 
		/* See __ipu_ch_get_third_buf_cpmem_num() for mapping */ 
		ipu_idmac_write(ipu, 0x00444047L, 
				IDMAC_SUB_ADDR_4(ipu->devtype)); 
		ipu_idmac_write(ipu, 0x46004241L, 
				IDMAC_SUB_ADDR_3(ipu->devtype)); 
		ipu_idmac_write(ipu, 0x00000045L, 
				IDMAC_SUB_ADDR_1(ipu->devtype)); 

/*设置idmac寄存器中关于triplebuffer的某些位*/

		/* Reset to buffer 0 */ 
		ipu_cm_write(ipu, tri_cur_buf_mask(dma_chan), 
				IPU_CHA_TRIPLE_CUR_BUF(ipu->devtype, dma_chan)); 
	} else { 
		reg = ipu_cm_read(ipu, 
				IPU_CHA_TRB_MODE_SEL(ipu->devtype, dma_chan)); 
		reg &= ~idma_mask(dma_chan); 
		ipu_cm_write(ipu, reg, 
				IPU_CHA_TRB_MODE_SEL(ipu->devtype, dma_chan)); 

		reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(dma_chan)); 
		if (phyaddr_1) 
			reg |= idma_mask(dma_chan); 
		else 
			reg &= ~idma_mask(dma_chan); 
		ipu_cm_write(ipu, reg, IPU_CHA_DB_MODE_SEL(dma_chan)); 

		/* Reset to buffer 0 */ 
		ipu_cm_write(ipu, idma_mask(dma_chan), 
				IPU_CHA_CUR_BUF(ipu->devtype, dma_chan)); 

	} 

	mutex_unlock(&ipu->mutex_lock); 

	return 0; 
} 
EXPORT_SYMBOL(ipu_init_channel_buffer);

在这个函数中,有很多有特殊含义的行参,比如说strideu_offset,v_offset等等,在这里分析的话,也不太懂是什么含义,不如在分析应用程序的时候,每个函数都有传入的实参值,那时候再具体分析。同时这个函数中用到了很多ipu_param_mem.h头文件中的函数,来填充CPMEM中的某一位或者某几位,下面就来分析ipu_param_mem.h这个头文件。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值