int32_t ipu_enable_channel(struct ipu_soc *ipu, ipu_channel_t channel)
{
uint32_t reg;
uint32_t ipu_conf;
uint32_t in_dma;
uint32_t out_dma;
uint32_t sec_dma;
uint32_t thrd_dma;
mutex_lock(&ipu->mutex_lock);
if (ipu->channel_enable_mask & (1L << IPU_CHAN_ID(channel))) {
dev_err(ipu->dev, "Warning: channel already enabled %d\n",
IPU_CHAN_ID(channel));
mutex_unlock(&ipu->mutex_lock);
return -EACCES;
}
/*ipu->channel_enable_mask中每一位对应一个channel是否使能了。首先检查要使能的这个channel是否已经使能了,如果已经使能了的话就会报错。*/
/* Get input and output dma channels */
out_dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER);
in_dma = channel_2_dma(channel, IPU_VIDEO_IN_BUFFER);
/*通过这两步将channel转化成dma_ch,在之前分析过这个函数,会根据type类型从channel中选取出输入或者输出的dmachannel. */
ipu_conf = ipu_cm_read(ipu, IPU_CONF);
if (ipu->di_use_count[0] > 0) {
ipu_conf |= IPU_CONF_DI0_EN;
}
if (ipu->di_use_count[1] > 0) {
ipu_conf |= IPU_CONF_DI1_EN;
}
if (ipu->dp_use_count > 0)
ipu_conf |= IPU_CONF_DP_EN;
if (ipu->dc_use_count > 0)
ipu_conf |= IPU_CONF_DC_EN;
if (ipu->dmfc_use_count > 0)
ipu_conf |= IPU_CONF_DMFC_EN;
if (ipu->ic_use_count > 0)
ipu_conf |= IPU_CONF_IC_EN;
if (ipu->vdi_use_count > 0) {
ipu_conf |= IPU_CONF_ISP_EN;
ipu_conf |= IPU_CONF_VDI_EN;
ipu_conf |= IPU_CONF_IC_INPUT;
}
if (ipu->rot_use_count > 0)
ipu_conf |= IPU_CONF_ROT_EN;
if (ipu->smfc_use_count > 0)
ipu_conf |= IPU_CONF_SMFC_EN;
ipu_cm_write(ipu, ipu_conf, IPU_CONF);
/*根据ipu参数里面的引用计数来决定将ipu_conf寄存器中的对应位置位。最终将这些值都写到这个ipu_conf寄存器中。*/
if (idma_is_valid(in_dma)) {
reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(in_dma));
ipu_idmac_write(ipu, reg | idma_mask(in_dma), IDMAC_CHA_EN(in_dma));
}
if (idma_is_valid(out_dma)) {
reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(out_dma));
ipu_idmac_write(ipu, reg | idma_mask(out_dma), IDMAC_CHA_EN(out_dma));
}
/*通过这几个函数来将in_dma和out_dma的值写到ipu里面的idmac寄存器中。*/
if ((ipu->sec_chan_en[IPU_CHAN_ID(channel)]) &&
((channel == MEM_PP_MEM) || (channel == MEM_PRP_VF_MEM) ||
(channel == MEM_VDI_PRP_VF_MEM))) {
sec_dma = channel_2_dma(channel, IPU_GRAPH_IN_BUFFER);
reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(sec_dma));
ipu_idmac_write(ipu, reg | idma_mask(sec_dma), IDMAC_CHA_EN(sec_dma));
}
/*ipu_soc结构体中sec_chan_en是一个bool类型的数组,它会根据channel通过IPU_CHAN_ID转化成的数字来从这个数组中找到对应的一项,如果使能了secondchannel的话,就是true,否则就是false。同时channel的需要是PP,PRP_VF,VDI_PRP_VF的情况,如果条件都成立的话,同样会设置idmac寄存器里面的某些位。*/
if ((ipu->thrd_chan_en[IPU_CHAN_ID(channel)]) &&
((channel == MEM_PP_MEM) || (channel == MEM_PRP_VF_MEM))) {
thrd_dma = channel_2_dma(channel, IPU_ALPHA_IN_BUFFER);
reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(thrd_dma));
ipu_idmac_write(ipu, reg | idma_mask(thrd_dma), IDMAC_CHA_EN(thrd_dma));
sec_dma = channel_2_dma(channel, IPU_GRAPH_IN_BUFFER);
reg = ipu_idmac_read(ipu, IDMAC_SEP_ALPHA);
ipu_idmac_write(ipu, reg | idma_mask(sec_dma), IDMAC_SEP_ALPHA);
}
/*这一段代码判断的是thirdchannel是否使能同时此时对应的channel是PP和PRP_VF。如果是使能了thirdchannel的话,肯定已经使能了secondchannel,所以需要同时在idmac中设置thrd_dma和sec_dma的值。*/
else if ((ipu->thrd_chan_en[IPU_CHAN_ID(channel)]) &&
((channel == MEM_BG_SYNC) || (channel == MEM_FG_SYNC))) {
thrd_dma = channel_2_dma(channel, IPU_ALPHA_IN_BUFFER);
reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(thrd_dma));
ipu_idmac_write(ipu, reg | idma_mask(thrd_dma), IDMAC_CHA_EN(thrd_dma));
reg = ipu_idmac_read(ipu, IDMAC_SEP_ALPHA);
ipu_idmac_write(ipu, reg | idma_mask(in_dma), IDMAC_SEP_ALPHA);
}
/*这一段代码判断的是thirdchannel是否使能同时此时对应的channel是BG和FG。如果是使能了thirdchannel的话,肯定已经使能了secondchannel,所以需要同时在idmac中设置thrd_dma和sec_dma的值。*/
if ((channel == MEM_DC_SYNC) || (channel == MEM_BG_SYNC) ||
(channel == MEM_FG_SYNC)) {
reg = ipu_idmac_read(ipu, IDMAC_WM_EN(in_dma));
ipu_idmac_write(ipu, reg | idma_mask(in_dma), IDMAC_WM_EN(in_dma));
_ipu_dp_dc_enable(ipu, channel);
}
/*这几个通道是关于输出显示的通道,个人理解是displaycontroll sync, background sync和foregroundsyncchannel,这时肯定不能仅仅设置idmac寄存器,同时需要调用_ipu_dp_dc_enable函数来使能显示设备,这个函数在ipu_disp.c中定义。*/
if (_ipu_is_ic_chan(in_dma) || _ipu_is_ic_chan(out_dma) ||
_ipu_is_irt_chan(in_dma) || _ipu_is_irt_chan(out_dma) ||
_ipu_is_vdi_out_chan(out_dma))
_ipu_ic_enable_task(ipu, channel);
/*如果是in_dma和out_dma使用到了ic,irt,vdi_out的话,都需要通过_ipu_ic_enable_task函数来使能ic,这个函数在ipu_ic.c中定义。*/
ipu->channel_enable_mask |= 1L << IPU_CHAN_ID(channel);
/*这个channel_enable_mask是一个uint32_t类型的掩码,如果使能了哪个channel的话,就将这个channel对应的位置1。同时也会在这个函数开始的地方通过它来判断一个channel是否已经使能过了。*/
if (ipu->prg_clk)
clk_prepare_enable(ipu->prg_clk);
mutex_unlock(&ipu->mutex_lock);
return 0;
}
EXPORT_SYMBOL(ipu_enable_channel);
总结一下,这个函数都做了哪些事情:
(1)既然是使能channel函数,设置的首要寄存器就是ipu_conf,会根据ipu_soc结构体里面的各个子模块的引用计数来将ipu_conf中对应的位使能。
(2)每一个channel都会使用到一个或者多个dmachannel,那么同样的,需要将这几个dmachannel所对应的寄存器的位使能,对应的寄存器是IPUx_IDMAC_CH_EN_1或者IPUx_IDMAC_CH_EN_2.
至此,ipu_common.c文件中的重要函数都分析完毕。