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
重要的部分我用红色标出了,
从这里可以看出来,它的存储方式是将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<style type="text/css">p { margin-bottom: 0.25cm; line-height: 120%; }a:link { }¶ms</style>, 0, sizeof(params));
ipu_ch_param_set_field(<span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span>¶ms<span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span>, 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(<span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span>¶ms<span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span><span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span>, 0, 138, 12, (height / 2) - 1);
ipu_ch_param_set_field(<span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span>¶<span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span>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(<span style="background: transparent"></span>¶<span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span>ms, 0, 138, 12, height - 1);
<span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span> ipu_ch_param_set_field(<span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span>¶<span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span>ms<span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span>, 1, 102, 14, stride - 1);
}
/*将params参数里面的word[0]里面的138位到150位设置为(height- 1) */
/*将params参数里面的word[1]里面的102位到116位设置为(stride- 1) */
如果channel是8/9/10的话,从注释上来看是vdoa+vdichannel,用的是bandmode,会将params那几位设置成height/2和stride*2。
/* EBA is 8-byte aligned */
ipu_ch_param_set_field(<span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span>¶<span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span>ms<span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span>, 1, 0, 29, addr0 >> 3);
ipu_ch_param_set_field(<span style="font-family:Courier 10 Pitch;">¶ms</span>, 1, 29, 29, addr1 >> 3);
if (addr0%8)
dev_warn(ipu->dev,
"IDMAC%d's EBA0 is not 8-byte aligned\n", ch);
if (addr1%8)
dev_warn(ipu->dev,
"IDMAC%d's EBA1 is not 8-byte aligned\n", ch);
/*将params参数里面的word[1]里面的0位到29位设置为(addr0>> 3),29位到58位设置成addr1>> 3。原因是EBA是8字节对齐的,后面需要分析这里。*/
switch (pixel_fmt) {
case IPU_PIX_FMT_GENERIC:
/*Represents 8-bit Generic data */
ipu_ch_param_set_field(<span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span><span style="font-family:Courier 10 Pitch;">¶ms</span><span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span>, 0, 107, 3, 5); /* bits/pixel */
ipu_ch_param_set_field(<span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span><span style="font-family:Courier 10 Pitch;">¶ms</span><span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span>, 1, 85, 4, 6); /* pix format */
ipu_ch_param_set_field(<span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span><span style="font-family:Courier 10 Pitch;">¶ms</span><span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span>, 1, 78, 7, 63); /* burst size */
break;
case IPU_PIX_FMT_GENERIC_16:
/* Represents 16-bit generic data */
ipu_ch_param_set_field(<style type="text/css">p { margin-bottom: 0.25cm; line-height: 120%; }a:link { }¶ms</style><span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span><span style="font-family:Courier 10 Pitch;">¶ms</span><span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span>, 0, 107, 3, 3); /* bits/pixel */
ipu_ch_param_set_field(<span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span><span style="font-family:Courier 10 Pitch;">¶ms</span><span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span>, 1, 85, 4, 6); /* pix format */
ipu_ch_param_set_field(<span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span><span style="font-family:Courier 10 Pitch;">¶ms</span><span style="background: transparent"><span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"><span style="background: transparent"></span></span></span></span>, 1, 78, 7, 31); /* burst size */
break;
case IPU_PIX_FMT_GENERIC_32:
/*Represents 32-bit Generic data */
break;
case IPU_PIX_FMT_RGB565:
ipu_ch_param_set_field(¶ms, 0, 107, 3, 3); /* bits/pixel */
ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */
ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */
_ipu_ch_params_set_packing(¶ms, 5, 0, 6, 5, 5, 11, 8, 16);
break;
case IPU_PIX_FMT_BGRA4444:
ipu_ch_param_set_field(¶ms, 0, 107, 3, 3); /* bits/pixel */
ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */
ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */
_ipu_ch_params_set_packing(¶ms, 4, 4, 4, 8, 4, 12, 4, 0);
break;
case IPU_PIX_FMT_BGRA5551:
ipu_ch_param_set_field(¶ms, 0, 107, 3, 3); /* bits/pixel */
ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */
ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */
_ipu_ch_params_set_packing(¶ms, 5, 1, 5, 6, 5, 11, 1, 0);
break;
case IPU_PIX_FMT_BGR24:
ipu_ch_param_set_field(¶ms, 0, 107, 3, 1); /* bits/pixel */
ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */
ipu_ch_param_set_field(¶ms, 1, 78, 7, 19); /* burst size */
_ipu_ch_params_set_packing(¶ms, 8, 0, 8, 8, 8, 16, 8, 24);
break;
case IPU_PIX_FMT_RGB24:
case IPU_PIX_FMT_YUV444:
ipu_ch_param_set_field(¶ms, 0, 107, 3, 1); /* bits/pixel */
ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */
ipu_ch_param_set_field(¶ms, 1, 78, 7, 19); /* burst size */
_ipu_ch_params_set_packing(¶ms, 8, 16, 8, 8, 8, 0, 8, 24);
break;
case IPU_PIX_FMT_VYU444:
ipu_ch_param_set_field(¶ms, 0, 107, 3, 1); /* bits/pixel */
ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */
ipu_ch_param_set_field(¶ms, 1, 78, 7, 19); /* burst size */
_ipu_ch_params_set_packing(¶ms, 8, 8, 8, 0, 8, 16, 8, 24);
break;
case IPU_PIX_FMT_AYUV:
ipu_ch_param_set_field(¶ms, 0, 107, 3, 0); /* bits/pixel */
ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */
ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */
_ipu_ch_params_set_packing(¶ms, 8, 8, 8, 16, 8, 24, 8, 0);
break;
case IPU_PIX_FMT_BGRA32:
case IPU_PIX_FMT_BGR32:
ipu_ch_param_set_field(¶ms, 0, 107, 3, 0); /* bits/pixel */
ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */
ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */
_ipu_ch_params_set_packing(¶ms, 8, 8, 8, 16, 8, 24, 8, 0);
break;
case IPU_PIX_FMT_RGBA32:
case IPU_PIX_FMT_RGB32:
ipu_ch_param_set_field(¶ms, 0, 107, 3, 0); /* bits/pixel */
ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */
ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */
_ipu_ch_params_set_packing(¶ms, 8, 24, 8, 16, 8, 8, 8, 0);
break;
case IPU_PIX_FMT_ABGR32:
ipu_ch_param_set_field(¶ms, 0, 107, 3, 0); /* bits/pixel */
ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */
ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */
_ipu_ch_params_set_packing(¶ms, 8, 0, 8, 8, 8, 16, 8, 24);
break;
case IPU_PIX_FMT_UYVY:
ipu_ch_param_set_field(¶ms, 0, 107, 3, 3); /* bits/pixel */
ipu_ch_param_set_field(¶ms, 1, 85, 4, 0xA); /* pix format */
if ((ch == 8) || (ch == 9) || (ch == 10)) {
ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */
} else {
ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */
}
break;
case IPU_PIX_FMT_YUYV:
ipu_ch_param_set_field(¶ms, 0, 107, 3, 3); /* bits/pixel */
ipu_ch_param_set_field(¶ms, 1, 85, 4, 0x8); /* pix format */
if ((ch == 8) || (ch == 9) || (ch == 10)) {
if (ipu->vdoa_en) {
ipu_ch_param_set_field(¶ms, 1, 78, 7, 31);
} else {
ipu_ch_param_set_field(¶ms, 1, 78, 7, 15);
}
} else {
ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */
}
break;
case IPU_PIX_FMT_YUV420P2:
case IPU_PIX_FMT_YUV420P:
ipu_ch_param_set_field(¶ms, 1, 85, 4, 2); /* pix format */
if (uv_stride < stride / 2)
uv_stride = stride / 2;
u_offset = stride * height;
v_offset = u_offset + (uv_stride * height / 2);
if ((ch == 8) || (ch == 9) || (ch == 10)) {
ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */
uv_stride = uv_stride*2;
} else {
if (_ipu_is_smfc_chan(ch) &&
ipu->smfc_idmac_12bit_3planar_bs_fixup)
bs = 15;
else
bs = 31;
ipu_ch_param_set_field(¶ms, 1, 78, 7, bs); /* burst size */
}
break;
case IPU_PIX_FMT_YVU420P:
ipu_ch_param_set_field(¶ms, 1, 85, 4, 2); /* pix format */
if (uv_stride < stride / 2)
uv_stride = stride / 2;
v_offset = stride * height;
u_offset = v_offset + (uv_stride * height / 2);
if ((ch == 8) || (ch == 9) || (ch == 10)) {
ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */
uv_stride = uv_stride*2;
} else {
if (_ipu_is_smfc_chan(ch) &&
ipu->smfc_idmac_12bit_3planar_bs_fixup)
bs = 15;
else
bs = 31;
ipu_ch_param_set_field(¶ms, 1, 78, 7, bs); /* burst size */
}
break;
case IPU_PIX_FMT_YVU422P:
/* BPP & pixel format */
ipu_ch_param_set_field(¶ms, 1, 85, 4, 1); /* pix format */
ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */
if (uv_stride < stride / 2)
uv_stride = stride / 2;
v_offset = (v == 0) ? stride * height : v;
u_offset = (u == 0) ? v_offset + v_offset / 2 : u;
break;
case IPU_PIX_FMT_YUV422P:
/* BPP & pixel format */
ipu_ch_param_set_field(¶ms, 1, 85, 4, 1); /* pix format */
ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */
if (uv_stride < stride / 2)
uv_stride = stride / 2;
u_offset = (u == 0) ? stride * height : u;
v_offset = (v == 0) ? u_offset + u_offset / 2 : v;
break;
case IPU_PIX_FMT_YUV444P:
/* BPP & pixel format */
ipu_ch_param_set_field(¶ms, 1, 85, 4, 0); /* pix format */
ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */
uv_stride = stride;
u_offset = (u == 0) ? stride * height : u;
v_offset = (v == 0) ? u_offset * 2 : v;
break;
case IPU_PIX_FMT_NV16:
ipu_ch_param_set_field(¶ms, 1, 85, 4, 1); /* pix format */
ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */
uv_stride = stride;
u_offset = (u == 0) ? stride * height : u;
break;
case IPU_PIX_FMT_NV12:
/* BPP & pixel format */
ipu_ch_param_set_field(¶ms, 1, 85, 4, 4); /* pix format */
uv_stride = stride;
u_offset = (u == 0) ? stride * height : u;
if ((ch == 8) || (ch == 9) || (ch == 10)) {
if (ipu->vdoa_en) {
/* one field buffer, memory width 64bits */
ipu_ch_param_set_field(¶ms, 1, 78, 7, 63);
} else {
ipu_ch_param_set_field(¶ms, 1, 78, 7, 15);
/* top/bottom field in one buffer*/
uv_stride = uv_stride*2;
}
} else {
ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */
}
break;
default:
dev_err(ipu->dev, "mxc ipu: unimplemented pixel format\n");
break;
}
/*set burst size to 16*/
/*根据函数传入的pixel_fmt参数的至来决定设置params里面的哪些位。从这个switch语句中可以看出来params参数里面word[0]里面从107位开始的几位决定bits/pixel,word[1]里面从85位开始的几位决定pixformat,word[1]里面从78位开始的几位决定burstsize。*/
if (uv_stride)
ipu_ch_param_set_field(¶ms, 1, 128, 14, uv_stride - 1);
/*如果uv_stride存在的话,就设置params参数里面的word[1]的128位到142位的值为(uv_stride- 1) */
/* Get the uv offset from user when need cropping */
if (u || v) {
u_offset = u;
v_offset = v;
}
/* UBO and VBO are 22-bit and 8-byte aligned */
if (u_offset/8 > 0x3fffff)
dev_warn(ipu->dev,
"IDMAC%d's U offset exceeds IPU limitation\n", ch);
if (v_offset/8 > 0x3fffff)
dev_warn(ipu->dev,
"IDMAC%d's V offset exceeds IPU limitation\n", ch);
if (u_offset%8)
dev_warn(ipu->dev,
"IDMAC%d's U offset is not 8-byte aligned\n", ch);
if (v_offset%8)
dev_warn(ipu->dev,
"IDMAC%d's V offset is not 8-byte aligned\n", ch);
ipu_ch_param_set_field(¶ms, 0, 46, 22, u_offset / 8);
ipu_ch_param_set_field(¶ms, 0, 68, 22, v_offset / 8);
/*上面这一段代码是设置u_offset和v_offset的值。他俩分别位于params->word[0]里面的46~68和68~90位。*/
dev_dbg(ipu->dev, "initializing idma ch %d @ %p\n", ch, ipu_ch_param_addr(ipu, ch));
fill_cpmem(ipu, ch, ¶ms);
/*辛辛苦苦设置好了params的值,肯定需要将它使用起来,就是通过这个函数来将params填充到根据ipu和ch参数找到的基址中。*/
if (addr2) { //在ipu_common.c中调用的_ipu_ch_param_init函数中有addr2这个值。
sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
if (sub_ch <= 0)
return;
/*通过__ipu_ch_get_third_buf_cpmem_num函数来找到第三个buffer的基址。*/
ipu_ch_param_set_field(¶ms, 1, 0, 29, addr2 >> 3);
ipu_ch_param_set_field(¶ms, 1, 29, 29, 0);
if (addr2%8)
dev_warn(ipu->dev,
"IDMAC%d's sub-CPMEM entry%d EBA0 is not "
"8-byte aligned\n", ch, sub_ch);
dev_dbg(ipu->dev, "initializing idma ch %d @ %p sub cpmem\n", ch,
ipu_ch_param_addr(ipu, sub_ch));
fill_cpmem(ipu, sub_ch, ¶ms);
}
};
最后这些设置是在某些情况下,比如之前分析过,可能会将几个channel一起启用。这个函数被ipu_common.c中的ipu_init_channel_buffer函数所调用。
15._ipu_ch_param_set_burst_size函数
static inline void _ipu_ch_param_set_burst_size(struct ipu_soc *ipu,
uint32_t ch,
uint16_t burst_pixels)
{
int32_t sub_ch = 0;
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 78, 7,
burst_pixels - 1);
sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
if (sub_ch <= 0)
return;
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 78, 7,
burst_pixels - 1);
};
这个函数用来设置或修改burst_size的值。因为在_ipu_ch_param_init初始化函数中,已经根据pixel_fmt的值设置了burst_size的初始值,在这里可以继续通过ipu和ch的值来修改他们的burst_size。这个函数被ipu_common.c中的ipu_init_channel_buffer函数所调用。
16._ipu_ch_param_get_burst_size函数
static inline int _ipu_ch_param_get_burst_size(struct ipu_soc *ipu, uint32_t ch)
{
return ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 78, 7) + 1;
};
通过ipu和ch参数找到对应的寄存器然后读取它的值。这个函数被ipu_common.c中的ipu_init_channel_buffer函数所调用。
17._ipu_ch_param_get_bpp函数
static inline int _ipu_ch_param_get_bpp(struct ipu_soc *ipu, uint32_t ch)
{
return ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 107, 3);
};
通过ipu和ch参数找到对应的寄存器然后读取它的值。
18._ipu_ch_param_set_buffer函数
static inline void _ipu_ch_param_set_buffer(struct ipu_soc *ipu, uint32_t ch,
int bufNum, dma_addr_t phyaddr)
{
if (bufNum == 2) {
ch = __ipu_ch_get_third_buf_cpmem_num(ch);
if (ch <= 0)
return;
bufNum = 0;
}
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 29 * bufNum, 29,
phyaddr / 8);
};
首先根据ipu和ch来获取基址,然后根据传入的bufNum参数来决定修改基址中的哪些位。这个函数被ipu_common.c中的ipu_update_channel_buffer函数调用。根据手册上面可以看出来,关于buffer的设置是在word[1]中从0~28,59~57,而且根据后面的分析,对于双buffer模式,这个bufNum的取值是0或者1.
19._ipu_ch_param_set_rotation函数
static inline void _ipu_ch_param_set_rotation(struct ipu_soc *ipu, uint32_t ch,
ipu_rotate_mode_t rot)
{
u32 temp_rot = bitrev8(rot) >> 5;
int32_t sub_ch = 0;
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 0, 119, 3, temp_rot);
sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
if (sub_ch <= 0)
return;
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 0, 119, 3, temp_rot);
};
设置rotation参数,这个函数首先根据rot的值通过bitrev8函数从byte_rev_table数组中取出对应的值,然后将那个值设置到word[0]的119~121位。这个函数被ipu_common.c中的ipu_init_channel_buffer函数调用。
20._ipu_ch_param_set_block_mode函数
static inline void _ipu_ch_param_set_block_mode(struct ipu_soc *ipu, uint32_t ch)
{
int32_t sub_ch = 0;
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 0, 117, 2, 1);
sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
if (sub_ch <= 0)
return;
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 0, 117, 2, 1);
};
设置block_mode参数,这个block_mode参数应该是位于word[0]里面的117~119位。这个函数被ipu_common.c中的ipu_init_channel_buffer函数调用。
21._ipu_ch_param_set_alpha_use_separate_channel函数
static inline void _ipu_ch_param_set_alpha_use_separate_channel(struct ipu_soc *ipu,
uint32_t ch,
bool option)
{
int32_t sub_ch = 0;
if (option) {
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 89, 1, 1);
} else {
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 89, 1, 0);
}
/*根据option这个bool类型的值来设置ch对应的基址中word[1]的89位。*/
sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
if (sub_ch <= 0)
return;
if (option) {
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 89, 1, 1);
} else {
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 89, 1, 0);
}
};
如果有第三个buffer的话,就设置sub_ch对应的基址中word[1]的89位。这个函数被ipu_common.c中的ipu_init_channel_buffer函数调用。
22._ipu_ch_param_set_alpha_condition_read函数
static inline void _ipu_ch_param_set_alpha_condition_read(struct ipu_soc *ipu, uint32_t ch)
{
int32_t sub_ch = 0;
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 149, 1, 1);
sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
if (sub_ch <= 0)
return;
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 149, 1, 1);
};
这个函数的名字里面有read,但是里面调用的是ipu_ch_param_mod_field_io函数,他用来将ch对应的基址里面的word[1]的149位修改为1。如果有sub_ch的话,就同时修改sub_ch所对应的基址。这个函数被ipu_common.c中的ipu_init_channel_buffer函数调用。
23._ipu_ch_param_set_alpha_buffer_memory函数
static inline void _ipu_ch_param_set_alpha_buffer_memory(struct ipu_soc *ipu, uint32_t ch)
{
int alp_mem_idx;
int32_t sub_ch = 0;
switch (ch) {
case 14: /* PRP graphic */
alp_mem_idx = 0;
break;
case 15: /* PP graphic */
alp_mem_idx = 1;
break;
case 23: /* DP BG SYNC graphic */
alp_mem_idx = 4;
break;
case 27: /* DP FG SYNC graphic */
alp_mem_idx = 2;
break;
default:
dev_err(ipu->dev, "unsupported correlative channel of local "
"alpha channel\n");
return;
}
/*根据ch的值设置alp_mem_idx的值。*/
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 90, 3, alp_mem_idx);
sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
if (sub_ch <= 0)
return;
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 90, 3, alp_mem_idx);
};
这个函数首先根据ch的值来确定alp_mem_idx。然后调用ipu_ch_param_mod_field_io将ch对应基址里面的word[1]里面的90~93位修改成alp_mem_idx的值。这个函数被ipu_common.c中的ipu_init_channel_buffer函数调用。
24._ipu_ch_param_set_interlaced_scan函数
static inline void _ipu_ch_param_set_interlaced_scan(struct ipu_soc *ipu, uint32_t ch)
{
u32 stride;
int32_t sub_ch = 0;
sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
ipu_ch_param_set_field_io(ipu_ch_param_addr(ipu, ch), 0, 113, 1, 1);
if (sub_ch > 0)
ipu_ch_param_set_field_io(ipu_ch_param_addr(ipu, sub_ch), 0, 113, 1, 1);
stride = ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 102, 14) + 1;
/* ILO is 20-bit and 8-byte aligned */
if (stride/8 > 0xfffff)
dev_warn(ipu->dev,
"IDMAC%d's ILO exceeds IPU limitation\n", ch);
if (stride%8)
dev_warn(ipu->dev,
"IDMAC%d's ILO is not 8-byte aligned\n", ch);
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 58, 20, stride / 8);
if (sub_ch > 0)
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 58, 20,
stride / 8);
stride *= 2;
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 102, 14, stride - 1);
if (sub_ch > 0)
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 102, 14,
stride - 1);
};
首先通过ipu_ch_param_set_field_io函数将ch对应基址里面word[0]的113位设为1。如果存在sub_ch的话,将sub_ch对应的基址里面word[0]的113位设为1。之后通过ipu_ch_param_read_field_io函数读取ch对应基址里面word[1]的102~116位的值来求出stride的值。之后通过ipu_ch_param_mod_field_io函数将ch对应基址里面的word[1]的58~78位设置为(stride/ 8),将ch对应基址里面的word[1]的102~116位设置为(stride-1),如果存在sub_ch的话需要做同样的操作。这个函数被ipu_common.c中的ipu_init_channel_buffer函数调用。
25._ipu_ch_param_set_axi_id函数
static inline void _ipu_ch_param_set_axi_id(struct ipu_soc *ipu, uint32_t ch, uint32_t id)
{
int32_t sub_ch = 0;
id %= 4;
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 93, 2, id);
sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
if (sub_ch <= 0)
return;
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 93, 2, id);
};
根据传入的id参数来设置ch对应的基址里面word[1]的93~94位,这一位是关于axi_id的。
26._ipu_ch_param_get_axi_id函数
static inline int _ipu_ch_param_get_axi_id(struct ipu_soc *ipu, uint32_t ch)
{
return ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 93, 2);
}
这个函数就是读取ch对应的基址里面word[1]的93~94位。这个函数被ipu_common.c中的ipu_ch_param_get_axi_id函数调用。
27.__ipu_ch_offset_calc函数
static inline int __ipu_ch_offset_calc(uint32_t pixel_fmt,
uint32_t width,
uint32_t height,
uint32_t stride,
uint32_t u,
uint32_t v,
uint32_t uv_stride,
uint32_t vertical_offset,
uint32_t horizontal_offset,
uint32_t *u_offset,
uint32_t *v_offset)
{
uint32_t u_fix = 0;
uint32_t v_fix = 0;
switch (pixel_fmt) {
case IPU_PIX_FMT_GENERIC:
case IPU_PIX_FMT_GENERIC_16:
case IPU_PIX_FMT_GENERIC_32:
case IPU_PIX_FMT_RGB565:
case IPU_PIX_FMT_BGR24:
case IPU_PIX_FMT_RGB24:
case IPU_PIX_FMT_YUV444:
case IPU_PIX_FMT_BGRA32:
case IPU_PIX_FMT_BGR32:
case IPU_PIX_FMT_RGBA32:
case IPU_PIX_FMT_RGB32:
case IPU_PIX_FMT_ABGR32:
case IPU_PIX_FMT_UYVY:
case IPU_PIX_FMT_YUYV:
case IPU_PIX_FMT_GPU32_SB_ST:
case IPU_PIX_FMT_GPU32_SB_SRT:
case IPU_PIX_FMT_GPU32_ST:
case IPU_PIX_FMT_GPU32_SRT:
case IPU_PIX_FMT_GPU16_SB_ST:
case IPU_PIX_FMT_GPU16_SB_SRT:
case IPU_PIX_FMT_GPU16_ST:
case IPU_PIX_FMT_GPU16_SRT:
*u_offset = 0;
*v_offset = 0;
break;
case IPU_PIX_FMT_YUV420P2:
case IPU_PIX_FMT_YUV420P:
if (uv_stride < stride / 2)
uv_stride = stride / 2;
*u_offset = stride * (height - vertical_offset - 1) +
(stride - horizontal_offset) +
(uv_stride * vertical_offset / 2) +
horizontal_offset / 2;
*v_offset = *u_offset + (uv_stride * height / 2);
u_fix = u ? (u + (uv_stride * vertical_offset / 2) +
(horizontal_offset / 2) -
(stride * vertical_offset) - (horizontal_offset)) :
*u_offset;
v_fix = v ? (v + (uv_stride * vertical_offset / 2) +
(horizontal_offset / 2) -
(stride * vertical_offset) - (horizontal_offset)) :
*v_offset;
break;
case IPU_PIX_FMT_YVU420P:
if (uv_stride < stride / 2)
uv_stride = stride / 2;
*v_offset = stride * (height - vertical_offset - 1) +
(stride - horizontal_offset) +
(uv_stride * vertical_offset / 2) +
horizontal_offset / 2;
*u_offset = *v_offset + (uv_stride * height / 2);
u_fix = u ? (u + (uv_stride * vertical_offset / 2) +
(horizontal_offset / 2) -
(stride * vertical_offset) - (horizontal_offset)) :
*u_offset;
v_fix = v ? (v + (uv_stride * vertical_offset / 2) +
(horizontal_offset / 2) -
(stride * vertical_offset) - (horizontal_offset)) :
*v_offset;
break;
case IPU_PIX_FMT_YVU422P:
if (uv_stride < stride / 2)
uv_stride = stride / 2;
*v_offset = stride * (height - vertical_offset - 1) +
(stride - horizontal_offset) +
(uv_stride * vertical_offset) +
horizontal_offset / 2;
*u_offset = *v_offset + uv_stride * height;
u_fix = u ? (u + (uv_stride * vertical_offset) +
horizontal_offset / 2 -
(stride * vertical_offset) - (horizontal_offset)) :
*u_offset;
v_fix = v ? (v + (uv_stride * vertical_offset) +
horizontal_offset / 2 -
(stride * vertical_offset) - (horizontal_offset)) :
*v_offset;
break;
case IPU_PIX_FMT_YUV422P:
if (uv_stride < stride / 2)
uv_stride = stride / 2;
*u_offset = stride * (height - vertical_offset - 1) +
(stride - horizontal_offset) +
(uv_stride * vertical_offset) +
horizontal_offset / 2;
*v_offset = *u_offset + uv_stride * height;
u_fix = u ? (u + (uv_stride * vertical_offset) +
horizontal_offset / 2 -
(stride * vertical_offset) - (horizontal_offset)) :
*u_offset;
v_fix = v ? (v + (uv_stride * vertical_offset) +
horizontal_offset / 2 -
(stride * vertical_offset) - (horizontal_offset)) :
*v_offset;
break;
case IPU_PIX_FMT_YUV444P:
uv_stride = stride;
*u_offset = stride * (height - vertical_offset - 1) +
(stride - horizontal_offset) +
(uv_stride * vertical_offset) +
horizontal_offset;
*v_offset = *u_offset + uv_stride * height;
u_fix = u ? (u + (uv_stride * vertical_offset) +
horizontal_offset -
(stride * vertical_offset) -
(horizontal_offset)) :
*u_offset;
v_fix = v ? (v + (uv_stride * vertical_offset) +
horizontal_offset -
(stride * vertical_offset) -
(horizontal_offset)) :
*v_offset;
break;
case IPU_PIX_FMT_NV12:
case IPU_PIX_FMT_NV16:
case PRE_PIX_FMT_NV21:
case PRE_PIX_FMT_NV61:
uv_stride = stride;
*u_offset = stride * (height - vertical_offset - 1) +
(stride - horizontal_offset) +
(uv_stride * vertical_offset / 2) +
horizontal_offset;
*v_offset = 0;
u_fix = u ? (u + (uv_stride * vertical_offset / 2) +
horizontal_offset -
(stride * vertical_offset) - (horizontal_offset)) :
*u_offset;
break;
default:
return -EINVAL;
}
if (u_fix > *u_offset)
*u_offset = u_fix;
if (v_fix > *v_offset)
*v_offset = v_fix;
return 0;
}
这个函数一共有11个参数,它的最终目的是根据前9个参数设置最后2个参数的值。这个函数被本文件中_ipu_ch_offset_update函数和ipu_common.c文件中ipu_get_channel_offset函数调用。
这个函数就是根据数据格式来算出u_offset和v_offset的值,以后根据各种格式来仔细算算。
28._ipu_ch_offset_update函数
/* IDMAC U/V offset changing support */
/* U and V input is not affected, */
/* the update is done by new calculation according to */
/* vertical_offset and horizontal_offset */
static inline void _ipu_ch_offset_update(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,
uint32_t vertical_offset,
uint32_t horizontal_offset)
{
uint32_t u_offset = 0;
uint32_t v_offset = 0;
uint32_t old_offset = 0;
int32_t sub_ch = 0;
int ret;
ret = __ipu_ch_offset_calc(pixel_fmt, width, height, stride,
u, v, uv_stride,
vertical_offset, horizontal_offset,
&u_offset, &v_offset);
if (ret) {
dev_err(ipu->dev, "mxc ipu: unimplemented pixel format\n");
return;
}
/* UBO and VBO are 22-bit and 8-byte aligned */
if (u_offset/8 > 0x3fffff)
dev_warn(ipu->dev,
"IDMAC%d's U offset exceeds IPU limitation\n", ch);
if (v_offset/8 > 0x3fffff)
dev_warn(ipu->dev,
"IDMAC%d's V offset exceeds IPU limitation\n", ch);
if (u_offset%8)
dev_warn(ipu->dev,
"IDMAC%d's U offset is not 8-byte aligned\n", ch);
if (v_offset%8)
dev_warn(ipu->dev,
"IDMAC%d's V offset is not 8-byte aligned\n", ch);
old_offset = ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 46, 22);
if (old_offset != u_offset / 8)
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 0, 46, 22, u_offset / 8);
old_offset = ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 68, 22);
if (old_offset != v_offset / 8)
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 0, 68, 22, v_offset / 8);
sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
if (sub_ch <= 0)
return;
old_offset = ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, sub_ch), 0, 46, 22);
if (old_offset != u_offset / 8)
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 0, 46, 22, u_offset / 8);
old_offset = ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, sub_ch), 0, 68, 22);
if (old_offset != v_offset / 8)
ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 0, 68, 22, v_offset / 8);
};
从这个函数的名字可以看出来,ipuchannel offset update:ipuchannel偏移更新,这个函数首先通过上一个__ipu_ch_offset_calc函数来计算出&u_offset和&v_offset,然后根据传入的ipu和ch参数来读取ch对应基址word[0]的46~68位中的老&u_offset偏移值和ch对应基址word[0]的68~90位中的老&v_offset偏移值,然后修改它们。重点还是u_offset和v_offset这两个值。
29._ipu_ch_params_set_alpha_width函数
static inline void _ipu_ch_params_set_alpha_width(struct ipu_soc *ipu, uint32_t ch, int alpha_width)
{
int32_t sub_ch = 0;
ipu_ch_param_set_field_io(ipu_ch_param_addr(ipu, ch), 1, 125, 3, alpha_width - 1);
sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
if (sub_ch <= 0)
return;
ipu_ch_param_set_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 125, 3, alpha_width - 1);
};
这个函数同样就是设置ch对应基址里面word[1]的125~128位,将它设置为(alpha_width– 1),这一位应该就是关于alpha_width的。
30._ipu_ch_param_set_bandmode函数
static inline void _ipu_ch_param_set_bandmode(struct ipu_soc *ipu,
uint32_t ch, uint32_t band_height)
{
int32_t sub_ch = 0;
ipu_ch_param_set_field_io(ipu_ch_param_addr(ipu, ch),
0, 114, 3, band_height - 1);
sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch);
if (sub_ch <= 0)
return;
ipu_ch_param_set_field_io(ipu_ch_param_addr(ipu, sub_ch),
0, 114, 3, band_height - 1);
dev_dbg(ipu->dev, "BNDM 0x%x, ",
ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 114, 3));
}
这个函数同样就是设置ch对应基址里面word[0]的114~117位,将它设置为(band_height– 1),这一位应该就是关于band_height的。
31._ipu_ch_param_bad_alpha_pos函数
/*
* The IPUv3 IDMAC has a bug to read 32bpp pixels from a graphics plane
* whose alpha component is at the most significant 8 bits. The bug only
* impacts on cases in which the relevant separate alpha channel is enabled.
*
* Return true on bad alpha component position, otherwise, return false.
*/
static inline bool _ipu_ch_param_bad_alpha_pos(uint32_t pixel_fmt)
{
switch (pixel_fmt) {
case IPU_PIX_FMT_BGRA32:
case IPU_PIX_FMT_BGR32:
case IPU_PIX_FMT_RGBA32:
case IPU_PIX_FMT_RGB32:
return true;
}
return false;
}
这个注释里面写的很清楚了,当IPUv3IDMAC在从一个alpha组件位于最重要8位的图像位面读取32bpp像素的时候有一个bug,所以当pixel_fmt为32位的时候,就返回true,否则返回false。