NXP

4.4 ipu_param_mem.h头文件分析

2019-07-12 13:47发布

1.下面这两个结构体是本文件的核心结构体。 struct ipu_ch_param_word { uint32_t data[5]; uint32_t res[3]; };
struct ipu_ch_param { struct ipu_ch_param_word word[2]; };
因为CPMEM是两个160位的word,所以每个word使用5uint32_t类型来表示,同时有两个word
2.这个宏暂时不分析,在后面用到的时候再分析。 #define ipu_ch_param_addr(ipu, ch) (((struct ipu_ch_param *)ipu->cpmem_base) + (ch))
3._param_word #define _param_word(base, w) (((struct ipu_ch_param *)(base))->word[(w)].data) 这个宏有两个参数,第一个参数是一个起始地址值,它一般是一个指针,在宏中会进行强制类型转化;第二个参数是第几个word,看ipu_ch_param结构体,它的取值为01 这个宏的意思是根据base这个起始地址值取到里面的第wworddata数据段。
4.ipu_ch_param_set_field #define ipu_ch_param_set_field(base, w, bit, size, v) { int i = (bit) / 32; int off = (bit) % 32; _param_word(base, w)[i] |= (v) << off; if (((bit)+(size)-1)/32 > i) { _param_word(base, w)[i + 1] |= (v) >> (off ? (32 - off) : 0); } }
这个宏有5个参数,前两个参数与_param_word宏中的参数相同,它们也确实是给_param_word宏使用的。第三个参数bit表示某一个数据在通过_param_word宏所找到的data数据段中的准确起始bit位,因为data数据段是uint32_tdata[5]的,所以这个值的范围为0160size表示数据所占的位数。v表示传入的数据字面值。 这个宏的意思是首先通过(bit)/32来计算出这个数据的起始bitdata数组成员中的哪一个(因为data数组有5个成员)。然后off表示这个数据在data数组成员中的偏移值。然后通过_param_word(base,w)[i]来找到对应的data数组成员。同时这个宏中也给出了这个数据所占的位数:size,如果bit所在的data数组成员放不下这么多size数的话,就需要在data数组中的下一个数组成员中存储剩下的bit 注意在数据的存储过程中涉及到大端小端的问题,对大端小端的解释:http://www.cnblogs.com/wuyuegb2312/archive/2013/06/08/3126510.html#轻松记住大端小端的含义(附对大端和小端的解释)
以下面这个函数中的各个数据为例来解释一下: static inline void _ipu_ch_params_set_packing(struct ipu_ch_param *p, int red_width, int red_offset, int green_width, int green_offset, int blue_width, int blue_offset, int alpha_width, int alpha_offset) { /* Setup red width and offset */ ipu_ch_param_set_field(p, 1, 116, 3, red_width - 1); ipu_ch_param_set_field(p, 1, 128, 5, red_offset); /* Setup green width and offset */ ipu_ch_param_set_field(p, 1, 119, 3, green_width - 1); ipu_ch_param_set_field(p, 1, 133, 5, green_offset); /* Setup blue width and offset */ ipu_ch_param_set_field(p, 1, 122, 3, blue_width - 1); ipu_ch_param_set_field(p, 1, 138, 5, blue_offset); /* Setup alpha width and offset */ ipu_ch_param_set_field(p, 1, 125, 3, alpha_width - 1); ipu_ch_param_set_field(p, 1, 143, 5, alpha_offset); } 首先ipu_ch_param_set_field( p, 1, 116, 3, red_width – 1)为例: #define ipu_ch_param_set_field( base, w, bit, size, v) { int i = (bit) / 32; int off = (bit) % 32; _param_word(base, w)[i] |= (v) << off; if (((bit)+(size)-1)/32 > i) { _param_word(base, w)[i + 1] |= (v) >> (off ? (32 - off) : 0); } } 解释: i= 116/32= 3 off= 116%32 = 20 #define _param_word(base, w) (((struct ipu_ch_param *)(base))->word[(w)].data) _param_word(base,w)[i] |= (v) << off      ==>p->word[w].data[i]|= (v) << off          ==>p->word[1].data[3] |= (red_width – 1)<<20;
如下图所示:
然后继续往下执行: ipu_ch_param_set_field(p, 1, 119, 3, green_width – 1); ipu_ch_param_set_field(p, 1, 122, 3, blue_width – 1); ipu_ch_param_set_field(p, 1, 125, 3, alpha_width - 1); 同样,它最终就会在119的位置存储(green_width– 1),在122的位置存储(blue_width– 1),在125的位置存储(alpha_width– 1)。 它们详细表示如下:
同样对于 ipu_ch_param_set_field(p, 1, 128, 5, red_offset); ipu_ch_param_set_field(p, 1, 133, 5, green_offset); ipu_ch_param_set_field(p, 1, 138, 5, blue_offset); ipu_ch_param_set_field(p, 1, 143, 5, alpha_offset); 它们详细表示如下:
再分析一ipu_ch_param_set_field函数中w=1, bit = 125, size = 13 的情况,对大端小端理解的更清楚。 i= 125/32 = 3 off= 125%32 = 29 _param_word(base,w)[i] |= (v) << off      ==>p->word[w].data[i] |= (v) << off          ==>p->word[1].data[3] |= (v)<<29; if(((bit)+(size)-1)/32 > i) 125+12/32= 4 > 3成立,所以会执行if下面的语句: _param_word(base,w)[i + 1] |= (v) >> (off ? (32 - off) : 0);      ==>_param_word(base,w)[4] |= (v) >> 3          ==>p->word[1].data[4]|=(v) >> 3 重要的部分我用红 {MOD}标出了,
从这里可以看出来,它的存储方式是将v的前10位存在了data[4]中,而v的后3位存在了data[3]中,从这里可以看出来,数据的存储方式是小端模式。
5.ipu_ch_param_set_field_io #define ipu_ch_param_set_field_io(base, w, bit, size, v) { int i = (bit) / 32; int off = (bit) % 32; unsigned reg_offset; u32 temp; reg_offset = sizeof(struct ipu_ch_param_word) * w / 4; reg_offset += i; temp = readl((u32 *)base + reg_offset); temp |= (v) << off; writel(temp, (u32 *)base + reg_offset); if (((bit)+(size)-1)/32 > i) { reg_offset++; temp = readl((u32 *)base + reg_offset); temp |= (v) >> (off ? (32 - off) : 0); writel(temp, (u32 *)base + reg_offset); } } 这个宏根据basewbit的值计算出寄存器的位置,然后将v的值写进去。
但是这个ipu_ch_param_set_field_io宏与上面那个ipu_ch_param_set_field宏有什么不同呢? 往下搜索源码可以发现,虽然这两个宏的第一个参数都是base,但是他们两个不相同: ipu_ch_param_set_field(¶ms, 0, 125, 13, width – 1); ipu_ch_param_set_field_io(ipu_ch_param_addr(ipu, ch), 0, 113, 1, 1); 而这个ipu_ch_param_addr宏就是本文件开始的那个宏 #defineipu_ch_param_addr(ipu, ch) (((struct ipu_ch_param *)ipu->cpmem_base)+ (ch)) 这个宏根据ipuch两个参数来找到对应寄存器的基址,具体来说就是根据ipu_soc结构体里面存放的cpmem_base寄存器的地址,ch是一般是uint32_t类型的dmachannel,通过这两个参数来找到对应寄存器的基地址。所以这个cpmem_base寄存器是设置dmachannel的关键寄存器。 对比上面两条语句,可以发现ipu_ch_param_set_field宏用于设置structipu_ch_param结构体参数params中某些位的值。而ipu_ch_param_set_field_io宏根据传入的ipuch参数来找到某个寄存器的基址,然后修改这个寄存器中的某些位。 以下的几个宏都与这两种情况类似。
6.ipu_ch_param_mod_field #define ipu_ch_param_mod_field(base, w, bit, size, v) { int i = (bit) / 32; int off = (bit) % 32; u32 mask = (1UL << size) - 1; u32 temp = _param_word(base, w)[i]; temp &= ~(mask << off); _param_word(base, w)[i] = temp | (v) << off; if (((bit)+(size)-1)/32 > i) { temp = _param_word(base, w)[i + 1]; temp &= ~(mask >> (32 - off)); _param_word(base, w)[i + 1] = temp | ((v) >> (off ? (32 - off) : 0)); } } 这个函数首先为size大小设置掩码,比如size=7,这个mask就等于111111(二进制),然后通过temp&= ~(mask << off)将这几位都清零,最后再通过temp| (v) << offv的值写到这几位中。修改某些位值的时候,一定要先清零了再写。 7.ipu_ch_param_mod_field_io #define ipu_ch_param_mod_field_io(base, w, bit, size, v) { int i = (bit) / 32; int off = (bit) % 32; u32 mask = (1UL << size) - 1; unsigned reg_offset; u32 temp; reg_offset = sizeof(struct ipu_ch_param_word) * w / 4; reg_offset += i; temp = readl((u32 *)base + reg_offset); temp &= ~(mask << off); temp |= (v) << off; writel(temp, (u32 *)base + reg_offset); if (((bit)+(size)-1)/32 > i) { reg_offset++; temp = readl((u32 *)base + reg_offset); temp &= ~(mask >> (32 - off)); temp |= ((v) >> (off ? (32 - off) : 0)); writel(temp, (u32 *)base + reg_offset); } } 这个宏与上一个宏类似,修改寄存器中某些位的值。
8.ipu_ch_param_read_field #define ipu_ch_param_read_field(base, w, bit, size) ({ u32 temp2; int i = (bit) / 32; int off = (bit) % 32; u32 mask = (1UL << size) - 1; u32 temp1 = _param_word(base, w)[i]; temp1 = mask & (temp1 >> off); if (((bit)+(size)-1)/32 > i) { temp2 = _param_word(base, w)[i + 1]; temp2 &= mask >> (off ? (32 - off) : 0); temp1 |= temp2 << (off ? (32 - off) : 0); } temp1; }) 这个宏的意思是读取某些位的值,这个宏最后的结果是temp1的值。
9.ipu_ch_param_read_field_io #define ipu_ch_param_read_field_io(base, w, bit, size) ({ u32 temp1, temp2; int i = (bit) / 32; int off = (bit) % 32; u32 mask = (1UL << size) - 1; unsigned reg_offset; reg_offset = sizeof(struct ipu_ch_param_word) * w / 4; reg_offset += i; temp1 = readl((u32 *)base + reg_offset); temp1 = mask & (temp1 >> off); if (((bit)+(size)-1)/32 > i) { reg_offset++; temp2 = readl((u32 *)base + reg_offset); temp2 &= mask >> (off ? (32 - off) : 0); temp1 |= temp2 << (off ? (32 - off) : 0); } temp1; }) 这个宏的意思是读取某个寄存器中某些位的值,最后的结果是temp1
10.__ipu_ch_get_third_buf_cpmem_num函数 static inline int __ipu_ch_get_third_buf_cpmem_num(int ch) { switch (ch) { case 8: return 64; case 9: return 65; case 10: return 66; case 13: return 67; case 21: return 68; case 23: return 69; case 27: return 70; case 28: return 71; default: return -EINVAL; } return 0; } 这个函数的大致意思是从函数传入的参数ch中获取到第三个buffer的起始地址。
11._ipu_ch_params_set_packing函数 static inline void _ipu_ch_params_set_packing(struct ipu_ch_param *p, int red_width, int red_offset, int green_width, int green_offset, int blue_width, int blue_offset, int alpha_width, int alpha_offset) { /* Setup red width and offset */ ipu_ch_param_set_field(p, 1, 116, 3, red_width - 1); ipu_ch_param_set_field(p, 1, 128, 5, red_offset); /* Setup green width and offset */ ipu_ch_param_set_field(p, 1, 119, 3, green_width - 1); ipu_ch_param_set_field(p, 1, 133, 5, green_offset); /* Setup blue width and offset */ ipu_ch_param_set_field(p, 1, 122, 3, blue_width - 1); ipu_ch_param_set_field(p, 1, 138, 5, blue_offset); /* Setup alpha width and offset */ ipu_ch_param_set_field(p, 1, 125, 3, alpha_width - 1); ipu_ch_param_set_field(p, 1, 143, 5, alpha_offset); } 这个函数在前面分析了,它主要目的是设置第一个参数p里面的red_widthred_offsetgreen_widthgreen_offset等信息。这个函数在_ipu_ch_param_init函数中调用,比如下面这样: _ipu_ch_params_set_packing(¶ms,5, 0, 6, 5, 5, 11, 8, 16); 通过这样调用,就分别设置了params中的RGB的信息,从上面可以看出来是RGB565格式的。 _ipu_ch_params_set_packing(¶ms,4, 4, 4, 8, 4, 12, 4, 0); 这样调用设置的是RGBA4444的格式。
12._ipu_ch_param_dump函数 这个函数是输出ipu_ch_param中的一些信息,就不分析了。
13.fill_cpmem函数 static inline void fill_cpmem(struct ipu_soc *ipu, int ch, struct ipu_ch_param *params) { int i, w; void *addr = ipu_ch_param_addr(ipu, ch); /* 2 words, 5 valid data */ for (w = 0; w < 2; w++) { for (i = 0; i < 5; i++) { writel(params->word[w].data[i], addr); addr += 4; } addr += 12; } } 这个函数首先通过ipu_ch_param_addr函数根据ipuch参数取得dmachannel的基址,然后将params参数里面两个word里面的data数据填充到获得的这个基址中。这个函数被_ipu_ch_param_init函数中调用。
14._ipu_ch_param_init函数 static inline void _ipu_ch_param_init(struct ipu_soc *ipu, int ch, uint32_t pixel_fmt, uint32_t width, uint32_t height, uint32_t stride, uint32_t u, uint32_t v, uint32_t uv_stride, dma_addr_t addr0, dma_addr_t addr1, dma_addr_t addr2) { uint32_t u_offset = 0; uint32_t v_offset = 0; uint32_t bs = 0; int32_t sub_ch = 0; struct ipu_ch_param params; memset(¶ms, 0, sizeof(params)); ipu_ch_param_set_field(¶ms, 0, 125, 13, width - 1); /*params参数里面的word[0]里面的125位到138位设置为(width- 1) */ if (((ch == 8) || (ch == 9) || (ch == 10)) && !ipu->vdoa_en) { ipu_ch_param_set_field(¶ms, 0, 138, 12, (height / 2) - 1); ipu_ch_param_set_field(ms, 1, 102, 14, (stride * 2) – 1); /*params参数里面的word[0]里面的138位到150位设置为((height/ 2) - 1) */ /*params参数里面的word[1]里面的102位到116位设置为((stride* 2) - 1) */ } else { /* note: for vdoa+vdi- ch8/9/10, always use band mode */ ipu_ch_param_set_field(ms, 0, 138, 12, height - 1); ipu_ch_param_set_field(ms, 1, 102, 14, stride - 1); } /*params参数里面的word[0]里面的138位到150位设置为(height- 1) */ /*params参数里面的word[1]里面的102位到116