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
", 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
");
return -EINVAL;
}
/*如果是IC和IRTchannel的话,函数传入的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
");
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
", dma_chan);
return -EINVAL;
}
/*这个函数比较绕,首先来看函数传入的参数里面有三个dma_addr_t类型的变量phyaddr_0,phyaddr_1和phyaddr_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
", dma_chan);
return -EINVAL;
}
/*这个判断语句是phyaddr_1存在的话就肯定不会执行dev_err语句,如果phyaddr_1和phyaddr_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的值写入ipu和sub_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);
在这个函数中,有很多有特殊含义的行参,比如说stride,u_offset,v_offset等等,在这里分析的话,也不太懂是什么含义,不如在分析应用程序的时候,每个函数都有传入的实参值,那时候再具体分析。同时这个函数中用到了很多ipu_param_mem.h头文件中的函数,来填充CPMEM中的某一位或者某几位,下面就来分析ipu_param_mem.h这个头文件。