NXP

4.3 ipu_init_channel_buffer函数的详细分析

2019-07-12 13:48发布

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; } /*如果是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 "); 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_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 ", 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这个头文件。