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使用5个uint32_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结构体,它的取值为0和1。
这个宏的意思是根据base这个起始地址值取到里面的第w个word的data数据段。
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]的,所以这个值的范围为0~160。size表示数据所占的位数。v表示传入的数据字面值。
这个宏的意思是首先通过(bit)/32来计算出这个数据的起始bit在data数组成员中的哪一个(因为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);
}
}
这个宏根据base,w,bit的值计算出寄存器的位置,然后将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))
这个宏根据ipu和ch两个参数来找到对应寄存器的基址,具体来说就是根据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宏根据传入的ipu和ch参数来找到某个寄存器的基址,然后修改这个寄存器中的某些位。
以下的几个宏都与这两种情况类似。
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) << off将v的值写到这几位中。修改某些位值的时候,一定要先清零了再写。
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_width,red_offset,green_width,green_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函数根据ipu和ch参数取得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
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮