8.4 子模块分析之IC

来源:互联网 发布:查学校的软件 编辑:程序博客网 时间:2024/05/21 22:53

1)概述

IC包含三个处理模块:缩放(downsizing),主处理流程(main processing)和翻转(rotation)。这几个模块是通过外围寄存器控制的,所需的参数是ARM通过AHB总线写到Task Parameter Memory中的。这个Task Parameter Memory在后面介绍。

先来看一个整体图:

这几个模块如下所示:

2Processing tasks

这三个模块中任何一个都可以最多进行三个处理任务,这三个处理任务如下所示:

1Preprocessing task for encoding.(编码预处理)

2Preprocessing task for displaying image from sensor (viewfinder)VF显示处理)

3Postprocessing task.(后加工)

这些任务通过单个硬件来实现,ARM平台会在使能这些任务之前配置好它们。


任务切换对于ARM平台而言是透明的。任务切换到downsizing模块所花的时间单元等于处理一个8pixels突发(burst)所花的时间;任务切换到main processing模块所花的时间单元等于处理一个图像一行所花的时间;任务切换到rotation模块所花的时间单元等于处理一帧图像所花的时间。


这三个处理任务包含相似的操作控制命令,下图就是这些命令的定义:

ARM平台将这些命令写到IC_CONF寄存器中,IC_CONF寄存器如下所示:


ARM平台如果想要修改IC task参数的话,必须先关闭这个task。在关闭task的过程中,IC会继续执行完当前帧,当这一帧处理完后,IPUARM平台发送一个中断信号,通知ARM平台可以改变task的参数,ARM平台修改后使能这个task,并从下一帧开始。


3 Downsizing Section


在这个模块中,有三个内存模块,Input Buffer Memory, Downsizing Temporary Memory, Downsizing Output Memory,同时每个内存模块都有一个控制器,核心是Downsizing Arithmetic Unit模块以及与之对应的Downsizing Control控制器。下面会讲述它们分别有什么作用。


CSI中来的数据会写到Input Buffer Memory中的一个FIFO中,可以通过编程控制,这个FIFO里面的数据能够通过IDMAC发送到系统内存中或者直接发送到Downsizing Unit中。


通过IDMAC发送到系统内存中这种情况,数据是通过ARM平台处理,然后通过Input Buffer Memory中的另一个FIFO来发送到IDMAC中的。


在后加工这个task中,IDMAC将数据从系统内存中传送到第三个FIFO中,然后Downsizing Unit模块来读取这个数据。


这三个FIFO各含有128页,每一页可以保存有2或者4个字大小的突发,对应8或者16pixels。具体突发的大小在对应channelCB#_BURST_SIZE位里面定义的。其中,在这个memory中每一个字的大小是128bits,每个字里面包含相连的颜色元素或者16bytes的通用数据(例如Bayer)。这几个FIFO是通过InputBuffer Memory Control来控制的。


Downsizing Unit提供图像像素水平方向和垂直方向上的平均和采样根据下面的公式:

IPr,c代表输入像素,HPR,c代表经过水平缩放的像素值,VPR,C代表在水平缩放的基础上再次经过垂直缩放后的像素值。DS_R_HDS_R_V分别代表水平和垂直方向上的缩放比例因子。其中这两个缩放因子是在IC_PRP_ENC_RSC,IC_PRP_VF_RSCIC_PP_RSC寄存器中设置的。这三个寄存器如下所示:


仔细来看这两个公式,倒是不太难理解,例如水平方向的缩放,缩放因子是6的话,就将水平方向上面的6个像素值求和后除以6即可,垂直方向上的缩放相似。最终的计算结果向8取整。


三个缩放任务的每一个所处理的数据是一个8 pixels的突发。通常情况下,当前正在执行的任务会一直执行到FIFO里面的数据清空为止。然后如果FIFO接收到一个新任务突发的情况下,Downsizing Unit就会切换到下一个更高优先级的新任务。


水平方向上的像素平均会首先进行,每个像素的所有颜色元素并行处理,之后,会将产生的新数据按行保存到Downsizing Temporary Memory中,之后再进行垂直方向的像素平均。在这个Downsizing Temporary Memory中,会存放三行数据对应这三个任务。这个内存中每一个字的宽度是36 bits


当完成垂直方向上的像素平均以后,数据会输出到Downsizing Output Memory中,在这个内存中每一个字的宽度是48 bits(包含两个输出像素)。对于每一个任务,Downsizing Output Memory内存中一行含有两个bufferDownsizing Unit填充这两个buffer中的前背景部分,Main Processing Unit填充这两个buffer的后背景部分。


4Main Processing Section

对于每一个任务,Main Processing Unit都会按照下面的步骤来处理:

1.将从Downsizing Output Memory里面所读取到数据进行翻转(可选操作,因为是是否翻转是根据对应dma channelCPMEM中的有关VF,HFROT参数来决定的),encoding任务使用IDMAC channel #20viewfinder任务使用IDMAC channel #21postprocessing任务使用IDMAC channel #22。这几个channel在程序中都体现了。

2.将从Downsizing Output Memory中获取到的数据进行水平方向上的大小调整根据下面的公式:

RS_C_H代表当前水平方向上的大小调整系数,这个系数的计算结果是向8bits取整的。这个系数的计算公式如下:


RS_R_H代表水平方向上的大小调整比率,这个比率是在IC_PRP_ENC_RSC,IC_PRP_VF_RSCIC_PP_RSC寄存器中设置的,这三个寄存器在上面已经显示过了,在这就不再显示。

经过这一步的计算后,结果保存在Task Parameter Memory中。

3.继续从Task Parameter Memory中取出已经经过水平方向上大小调整的数据,然后进行垂直方向上的大小调整,之后继续保存到Task Parameter Memory中。垂直方向上的大小调整是按照下面的公式:

RS_C_V代表当前垂直方向上的大小调整系数,这个系数的计算结果同样是向8 bits取整的。这个系数的计算公式如下:




RS_R_V代表垂直方向上的大小调整比率,这个比率同样是在IC_PRP_ENC_RSC,IC_PRP_VF_RSCIC_PP_RSC寄存器中设置的。


注意,在这个Main Processing Unit中进行的是ReSizing操作,而在Downsizing Section Unit中进行的是DownSizing操作,这两个相似的系数都保存在IC_PRP_ENC_RSC,IC_PRP_VF_RSCIC_PP_RSC这三个寄存器中,仔细看这几个寄存器中的各位的名字,可以区分它们。


4.第一次颜色空间转换(CSC)的时候(YUV->RGB或者RGB->YUV)需要使用到转换矩阵CSC1。转换矩阵是可以通过编程控制的,它们保存在Task Parameter Memory中。转换矩阵如下:



当在YUV->RGB情况下时:X0=Y, X1 =U, X2 =V, Z0 =R, Z1=G, Z2 =B

当在RGB->YUV情况下时:X0=R, X1 =G, X2 =B, Z0 =Y, Z1=U, Z2 =V

转换矩阵里面的所有参数都是通过ARM平台写到Task Parameter Memory中去的。

5.将图像整合起来,有三种整合方式:

local alpha blending,

global alpha blending,

use of key color.

相比alpha blending而言,color keying方式具有更高的优先级。

整合模式是可以通过在IC_CONF寄存器的28,19位中选择的:

整合公式如下所示:


IGP代表an input graphics pixel

IVP代表an input video pixelα=(A+floor(A/128))/256代表an alpha valueA代表a global or local transparency parameterglobal A写在IC_CMBP_1寄存器中,如下所示:

color keying使能并且有一个pixel匹配key color的时候,graphics pixel变得透明。

graphics data是从位于Main Processing Memory里面的FIFO里面读取出来的。这个FIFO包含32页,里面的数据格式为RGB或者RGBA或者YUV4:4:4或者YUVA。这些graphics data是通过IDMAC从系统内存中加载进去的。


以上所有的操作是通过统一的处理单元有序执行的。第一步和第二步不会被其他的任务所打断,其他步骤会被具有更高优先级的任务所打断,Preprocessing任务的优先级比postprocessing任务的优先级高。


这个统一处理单元对于三个颜色元素都有对应的三个独立部分,所以这三个颜色元素可以并行处理。


处理结果会通过一个输出FIFO一行一行地填充到Main Processing Output Memory中。最终IDMAC会将输出突发传送到系统内存中或者直接通过DMFC到显示器来显示(使用dma channel #21)。这个Main Processing Memory为每个任务准备了三个bufferthe temporary row buffer, the graphics FIFOthe output FIFO.


5Rotation Section


这个翻转模块包含:

the Rotation Memory:用来存放一个8×8像素的输入矩形块;

an output FIFO:包含四页,并且每页是8 pixels

这个Rotation Memory中字的宽度等于四个相连的像素的宽度--96bits


Rotation Unit重写每一个从输入块到输出块内像素数据的相对位置。对于这三个任务来说,旋转/左右翻转/上下翻转是分开使能的。配置这些翻转参数通过对应dma channel里面的CPMEM里面的VF,HF & ROT这几个参数。

preprocessing task for encoding使用IDMAC channel #45

preprocessing task for viewfinder使用IDMACc hannel #46

postprocessing task使用IDMAC channel #47


这些翻转如下所示:

图中FLR表示:FlippingLeft/Right

FUD表示:Flipping Up/Down

当完成这些翻转任务的时候,IDMAC将输出FIFO里面的内容返回给系统内存。


6IC Task Parameter Memory

这一节就直接看手册吧,就是一张表格,没法分析。


7IC's DMA channels

下面这个图表示IC所使用的dma channel,其中IC's channel name表示在IC层面看来的channel名字。


8)代码实现

对于IC,因为IC可以执行三个任务:

1Preprocessing task for encoding.(编码预处理)

2Preprocessing task for displaying image from sensor (viewfinder)VF显示处理)

3Postprocessing task.(后加工)

所以会有几个相似的channel使用到IC模块,如:

CSI_PRP_ENC_MEMCSI_PRP_VF_MEMMEM_PRP_VF_MEMMEM_VDI_PRP_VF_MEMMEM_PRP_ENC_MEM等等。

同时,在上面分析过,IC里面一共有三个Unitdownsizing Unitmain processing Unitrotation Unit,其中,在downsizing Unit中,会将数据进行水平和垂直方向的平均或采样(DownSizing);在main processing Unit中会对数据进行水平和垂直方向上的重新调整大小(ReSizing),然后进行颜色空间转换;在rotation Unit中,对数据进行翻转等操作。


所以,其实在代码中就很简单了,首先计算出DownSizingReSizing的比例系数,他们都保存在IC_PRP_ENC_RSC,IC_PRP_VF_RSCIC_PP_RSC这三个寄存器中,然后根据输入和输出的数据格式来进行颜色空间转换。至于翻转等操作,对于channel来说,是一个可选操作。


CSI_PRP_ENC_MEM为例:

case CSI_PRP_ENC_MEM: if (params->csi_prp_enc_mem.csi > 1) { ret = -EINVAL; goto err; } if ((ipu->using_ic_dirct_ch == MEM_VDI_PRP_VF_MEM) || (ipu->using_ic_dirct_ch == MEM_VDI_MEM)) { ret = -EINVAL; goto err; } ipu->using_ic_dirct_ch = CSI_PRP_ENC_MEM; ipu->ic_use_count++; ipu->csi_channel[params->csi_prp_enc_mem.csi] = channel; if (params->csi_prp_enc_mem.mipi_en) { ipu_conf |= (1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET + params->csi_prp_enc_mem.csi)); _ipu_csi_set_mipi_di(ipu, params->csi_prp_enc_mem.mipi_vc, params->csi_prp_enc_mem.mipi_id, params->csi_prp_enc_mem.csi); } else ipu_conf &= ~(1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET + params->csi_prp_enc_mem.csi)); <span style="color:#FF0000;">/*CSI0/1 feed into IC*/ ipu_conf &= ~IPU_CONF_IC_INPUT; if (params->csi_prp_enc_mem.csi) ipu_conf |= IPU_CONF_CSI_SEL; else ipu_conf &= ~IPU_CONF_CSI_SEL; </span>/*PRP skip buffer in memory, only valid when RWS_EN is true*/ reg = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1); ipu_cm_write(ipu, reg & ~FS_ENC_IN_VALID, IPU_FS_PROC_FLOW1); /*CSI data (include compander) dest*/ _ipu_csi_init(ipu, channel, params->csi_prp_enc_mem.csi); <span style="color:#FF0000;">_ipu_ic_init_prpenc(ipu, params, true); </span>break;与IC相关的代码就是我标红的,首先设置IPU_CONF寄存器中IC的数据来源,因为IC的输入数据来源在采集的时候只能是VDI或者CSI,所以这些代码就是来设置这些内容的:

之后有关IC的就是_ipu_ic_init_prpenc函数,如下所示:

int _ipu_ic_init_prpenc(struct ipu_soc *ipu, ipu_channel_params_t *params, bool src_is_csi) { uint32_t reg, ic_conf; uint32_t downsizeCoeff, resizeCoeff; ipu_color_space_t in_fmt, out_fmt; int ret = 0; /* Setup vertical resizing */ if (!params->mem_prp_enc_mem.outv_resize_ratio) { ret = _calc_resize_coeffs(ipu, params->mem_prp_enc_mem.in_height, params->mem_prp_enc_mem.out_height, &resizeCoeff, &downsizeCoeff); if (ret < 0) { dev_err(ipu->dev, "failed to calculate prpenc height " "scaling coefficients\n"); return ret; } reg = (downsizeCoeff << 30) | (resizeCoeff << 16); } else reg = (params->mem_prp_enc_mem.outv_resize_ratio) << 16; /* 上面的代码是计算垂直方向上的DownSizing和ReSizing比例系数。 *//* Setup horizontal resizing */ if (!params->mem_prp_enc_mem.outh_resize_ratio) { ret = _calc_resize_coeffs(ipu, params->mem_prp_enc_mem.in_width, params->mem_prp_enc_mem.out_width, &resizeCoeff, &downsizeCoeff); if (ret < 0) { dev_err(ipu->dev, "failed to calculate prpenc width " "scaling coefficients\n"); return ret; } reg |= (downsizeCoeff << 14) | resizeCoeff; } else reg |= params->mem_prp_enc_mem.outh_resize_ratio; /* 上面的代码是计算水平方向上的DownSizing和ReSizing比例系数。 */ipu_ic_write(ipu, reg, IC_PRP_ENC_RSC); /* 将这些比例系数写到IC_PRP_ENC_RSC寄存器中。 */ic_conf = ipu_ic_read(ipu, IC_CONF); /* Setup color space conversion */ in_fmt = format_to_colorspace(params->mem_prp_enc_mem.in_pixel_fmt); out_fmt = format_to_colorspace(params->mem_prp_enc_mem.out_pixel_fmt); if (in_fmt == RGB) { if ((out_fmt == YCbCr) || (out_fmt == YUV)) { /* Enable RGB->YCBCR CSC1 */ _init_csc(ipu, IC_TASK_ENCODER, RGB, out_fmt, 1); ic_conf |= IC_CONF_PRPENC_CSC1; } } if ((in_fmt == YCbCr) || (in_fmt == YUV)) { if (out_fmt == RGB) { /* Enable YCBCR->RGB CSC1 */ _init_csc(ipu, IC_TASK_ENCODER, YCbCr, RGB, 1); ic_conf |= IC_CONF_PRPENC_CSC1; } else { /* TODO: Support YUV<->YCbCr conversion? */ } } /* 进行颜色空间转换 */if (src_is_csi) ic_conf &= ~IC_CONF_RWS_EN; else ic_conf |= IC_CONF_RWS_EN; /* 设置IC_CONF寄存器中的RWS_EN位。 */ipu_ic_write(ipu, ic_conf, IC_CONF); return ret; }

从这一段代码中可以看出来,它完成了计算DownSizingReSizing比例系数的计算,然后将这4个系数写到IC_PRP_ENC_RSC寄存器中即可。之后完成颜色空间的转换功能,通过_init_csc函数来完成,在这个_init_csc函数中,就包含了颜色空间转换所使用到的转换矩阵CSC1,同时,由于使用到了颜色转换这个功能,所以需要将IC_CONF寄存器中的PRPENC_CSC1位置位。


在这里还有一个问题,就是在CSI_PRP_ENC_MEM这种情况下,在它的ipu_init_channel函数中,会使用到ipu_channel_params_t这个结构体联合,对应CSI_PRP_ENC_MEM,应该使用的是ipu_channel_params_t中的csi_prp_enc_mem结构体,但是在_ipu_ic_init_prpenc函数中,却使用的是mem_prp_enc_mem这个结构体来计算比例系数,但是一直找不到MEM_PRP_ENC_MEM这个channel初始化的地方或者mem_prp_enc_mem这个结构体赋值的地方,甚是不解。。。

最终,问虎哥后找到答案,这个是C语言的语法问题,大致意思是:

当多个数据需要共享内存或者多个数据每次只取其一时,可以利用联合体(union),同时它有以下几个特点:

1)联合体是一个结构;

2)它的所有成员相对于基地址的偏移量都为0

3)此结构空间要大到足够容纳最“宽”的成员;

4)其对其方式主要是要适合其中所有的成员;

也就是说,如果对ipu_channel_params_t.csi_prp_enc_mem.in_width赋值的话,ipu_channel_params_t.mem_prp_enc_mem.in_width中保存的也是同样的值。所以就可以使用mem_prp_enc_mem中的值来进行计算了。


对于其他功能如:_ipu_ic_init_prpvf_ipu_ic_init_pp等等,它们的实现是大致相似的,暂时先不分析了.

0 0
原创粉丝点击