1. deinterlace_3_field函数
static bool deinterlace_3_field(struct ipu_task_entry *t)
{
return ((t->set.mode & VDI_MODE) &&
(t->input.deinterlace.motion != HIGH_MOTION));
}
2. tiled_filed_size函数
u32 field_size;
static u32 tiled_filed_size(struct ipu_task_entry *t)
{
/* note: page_align is required by VPU hw ouput buffer */
field_size = TILED_NV12_FRAME_SIZE(t->input.width, t->input.height/2);
return field_size;
}
3. MODE模式
关于这个VDOA模式,在下面这个task_set结构体里面的注释有说明:VDOA_MODE意味着这个task任务使用vdoa,并且VDOA有两种模式:BANDMODE和non-bandMODE模式。non-bandMODE模式将要转化数据到内存中。而BANDMODE模式需要IPU硬件的同步信号,这种模式是连上VDIC的默认模式。
先来看这个task_set结构体:
struct task_set {
#define NULL_MODE 0x0
#define IC_MODE 0x1
#define ROT_MODE 0x2
#define VDI_MODE 0x4
#define IPU_PREPROCESS_MODE_MASK (IC_MODE | ROT_MODE | VDI_MODE)
/* VDOA_MODE means this task use vdoa, and VDOA has two modes:
* BAND MODE and non-BAND MODE. Non-band mode will do transfer data
* to memory. BAND mode needs hareware sync with IPU, it is used default
* if connected to VDIC.
*/
#define VDOA_MODE 0x8
#define VDOA_BAND_MODE 0x10
u8 mode;
。。。。。。。。。
struct stripe_setting sp_setting;
};
上面红 {MOD}字体是有关MODE模式的变量,可以看到IC_MODE,ROT_MODE,VDA_MODE分别占用u8的一位:
下面就来看下面三个函数:
3.1 only_ic函数
static bool only_ic(u8 mode)
{
mode = mode & IPU_PREPROCESS_MODE_MASK;
return ((mode == IC_MODE) || (mode == VDI_MODE));
}
可以看出来,如果mode占用的如下红 {MOD}字体所示的任意一位的话,都可以认为是only_ic模式。
3.2 only_rot函数
static bool only_rot(u8 mode)
{
mode = mode & IPU_PREPROCESS_MODE_MASK;
return (mode == ROT_MODE);
}
这个only_rot模式,则只有mode占用ROT位的话,才能够认为是only_rot。
3.3ic_and_rot函数
static bool ic_and_rot(u8 mode)
{
mode = mode & IPU_PREPROCESS_MODE_MASK;
return ((mode == (IC_MODE | ROT_MODE)) ||
(mode == (VDI_MODE | ROT_MODE)));
}
这种模式如下所示:
4.need_split函数
static bool need_split(struct ipu_task_entry *t)
{
return ((t->set.split_mode != NO_SPLIT) || (t->task_no & SPLIT_MASK));
}
struct task_set {
。。。。。。。。。。
#define NO_SPLIT 0x0
#define RL_SPLIT 0x1
#define UD_SPLIT 0x2
#define LEFT_STRIPE 0x1
#define RIGHT_STRIPE 0x2
#define UP_STRIPE 0x4
#define DOWN_STRIPE 0x8
#define SPLIT_MASK 0xF
u8 split_mode;
。。。。。。。。。
struct stripe_setting sp_setting;
};
这个函数判断的同样是ipu_task_entry结构体中的task_set里面的split_mode,如果这一位不等于0的话,就返回true。这个函数的意思应该是判断一个结构体是否需要拆分,do_task,get_res_do_task,ipu_task_thread,ipu_queue_task函数中都调用了这个函数。
5.fmt_to_bpp函数
unsigned int fmt_to_bpp(unsigned int pixelformat)
{
u32 bpp;
switch (pixelformat) {
case IPU_PIX_FMT_RGB565:
/*interleaved 422*/
case IPU_PIX_FMT_YUYV:
case IPU_PIX_FMT_UYVY:
/*non-interleaved 422*/
case IPU_PIX_FMT_YUV422P:
case IPU_PIX_FMT_YVU422P:
bpp = 16;
break;
case IPU_PIX_FMT_BGR24:
case IPU_PIX_FMT_RGB24:
case IPU_PIX_FMT_YUV444:
case IPU_PIX_FMT_YUV444P:
bpp = 24;
break;
case IPU_PIX_FMT_BGR32:
case IPU_PIX_FMT_BGRA32:
case IPU_PIX_FMT_RGB32:
case IPU_PIX_FMT_RGBA32:
case IPU_PIX_FMT_ABGR32:
bpp = 32;
break;
/*non-interleaved 420*/
case IPU_PIX_FMT_YUV420P:
case IPU_PIX_FMT_YVU420P:
case IPU_PIX_FMT_YUV420P2:
case IPU_PIX_FMT_NV12:
bpp = 12;
break;
default:
bpp = 8;
break;
}
return bpp;
}
EXPORT_SYMBOL_GPL(fmt_to_bpp);
这个函数就很简单了,它根据pixelformat格式返回它所占用的bit数,bitsper
pixel。
6.colorspaceofpixel函数
cs_t colorspaceofpixel(int fmt)
{
switch (fmt) {
case IPU_PIX_FMT_RGB565:
case IPU_PIX_FMT_RGB666:
case IPU_PIX_FMT_BGR24:
case IPU_PIX_FMT_RGB24:
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:
return RGB_CS;
break;
case IPU_PIX_FMT_UYVY:
case IPU_PIX_FMT_YUYV:
case IPU_PIX_FMT_YUV420P2:
case IPU_PIX_FMT_YUV420P:
case IPU_PIX_FMT_YVU420P:
case IPU_PIX_FMT_YVU422P:
case IPU_PIX_FMT_YUV422P:
case IPU_PIX_FMT_YUV444:
case IPU_PIX_FMT_YUV444P:
case IPU_PIX_FMT_NV12:
case IPU_PIX_FMT_TILED_NV12:
case IPU_PIX_FMT_TILED_NV12F:
return YUV_CS;
break;
default:
return NULL_CS;
}
}
EXPORT_SYMBOL_GPL(colorspaceofpixel);
这个函数根据format的值返回这个format的颜 {MOD}空间colorspace。颜 {MOD}空间在ipu.h中定义:
typedef enum {
RGB_CS,
YUV_CS,
NULL_CS
} cs_t;
这个函数主要用在下面要介绍的need_csc函数。
7.need_csc函数
int need_csc(int ifmt, int ofmt)
{
cs_t ics, ocs;
ics = colorspaceofpixel(ifmt);
ocs = colorspaceofpixel(ofmt);
if ((ics == NULL_CS) || (ocs == NULL_CS))
return -1;
else if (ics != ocs)
return 1;
return 0;
}
EXPORT_SYMBOL_GPL(need_csc);
这个函数通过colorspaceofpixel函数分别获取输入format和输出format的颜 {MOD}空间,如果两者相同的话,就不需要csc转化了,如果不同的话就需要csc转化。这个函数在check_task中调用。
8.soc_max_in_width函数
static int soc_max_in_width(u32 is_vdoa)
{
return is_vdoa ? 8192 : 4096;
}
这个函数返回soc最大的宽度,如果传入的参数为1的话,就返回8192,否则返回4096。这个函数在check_task函数中就是直接这样用的。参数为1的话代表是vdoa。
9.soc_max_vdi_in_width函数
static int soc_max_vdi_in_width(struct ipu_soc *ipu)
{
int i;
if (!ipu) {
for (i = 0; i < max_ipu_no; i++) {
ipu = ipu_get_soc(i);
if (!IS_ERR_OR_NULL(ipu))
break;
}
if (i == max_ipu_no)
return 720;
}
return IPU_MAX_VDI_IN_WIDTH(ipu->devtype);
}
#define IPU_MAX_VDI_IN_WIDTH(type) ({ (type) >= IPUv3M ? 968 : 720; })
这个函数根据传入的ipu_soc结构体里面的devtype来返回vdi的最大宽度。
在ipu_common.c中的ipu_probe函数里面,以下代码是摘取出来的:(在ipu_common.c中详细分析)
const struct of_device_id *of_id =
of_match_device(imx_ipuv3_dt_ids, &pdev->dev);
const struct ipu_platform_type *iputype = of_id->data;
const struct ipu_devtype *devtype = &iputype->devtype;
ipu = &ipu_array[id];
ipu->devtype = devtype->type;
最终ipu->devtype就调用到imx_ipuv3_dt_ids[x]里面的data数据段:
static const struct of_device_id imx_ipuv3_dt_ids[] = {
{ .compatible = "fsl,imx51-ipu", .data = &ipu_type_imx51, },
{ .compatible = "fsl,imx53-ipu", .data = &ipu_type_imx53, },
{ .compatible = "fsl,imx6q-ipu", .data = &ipu_type_imx6q, },
{ .compatible = "fsl,imx6qp-ipu", .data = &ipu_type_imx6qp, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, imx_ipuv3_dt_ids);
以ipu_type_imx6qp为例,如下所示:
static struct ipu_platform_type ipu_type_imx6qp = {
.devtype = {
.name = "IPUv3H",
.cm_ofs = 0x00200000,
.idmac_ofs = 0x00208000,
.ic_ofs = 0x00220000,
.csi0_ofs = 0x00230000,
.csi1_ofs = 0x00238000,
.di0_ofs = 0x00240000,
.di1_ofs = 0x00248000,
.smfc_ofs = 0x00250000,
.dc_ofs = 0x00258000,
.dmfc_ofs = 0x00260000,
.vdi_ofs = 0x00268000,
.cpmem_ofs = 0x00300000,
.srm_ofs = 0x00340000,
.tpm_ofs = 0x00360000,
.dc_tmpl_ofs = 0x00380000,
.type = IPUv3H,
.idmac_used_bufs_present = true,
},
.ch0123_axi = 0,
.ch23_axi = 0,
.ch27_axi = 2,
.ch28_axi = 3,
.normal_axi = 1,
.idmac_used_bufs_en_r = true,
.idmac_used_bufs_en_w = true,
.idmac_used_bufs_max_r = 0x3,
.idmac_used_bufs_max_w = 0x3,
.smfc_idmac_12bit_3planar_bs_fixup = true,
};
这个soc_max_vdi_in_width函数就会比对ipu->devtype即上面标红的部分,将它于IPUv3M比较,大于就返回968,否则返回720。可以查看ipu_common.c中可以看出来ipu_type_imx51为"IPUv3EX",ipu_type_imx53为"IPUv3M",ipu_type_imx6q为"IPUv3H",ipu_type_imx6qp为"IPUv3H",所以对于imx51和imx53返回720,imx6q和imx6qp返回968。
这个函数在本文件中的update_split_setting,check_task,get_res_do_task函数中调用。
10.soc_max_in_height函数
static int soc_max_in_height(void)
{
return 4096;
}
11.soc_max_out_width函数
static int soc_max_out_width(void)
{
/* mx51/mx53/mx6q is 1024*/
return 1024;
}
12.soc_max_out_height函数
static int soc_max_out_height(void)
{
/* mx51/mx53/mx6q is 1024*/
return 1024;
}
13.dump_task_info函数
static void dump_task_info(struct ipu_task_entry *t)
{
if (!debug)
return;
dev_dbg(t->dev, "[0x%p]input:
", (void *)t);
dev_dbg(t->dev, "[0x%p] format = 0x%x
", (void *)t, t->input.format);
dev_dbg(t->dev, "[0x%p] width = %d
", (void *)t, t->input.width);
dev_dbg(t->dev, "[0x%p] height = %d
", (void *)t, t->input.height);
dev_dbg(t->dev, "[0x%p] crop.w = %d
", (void *)t, t->input.crop.w);
dev_dbg(t->dev, "[0x%p] crop.h = %d
", (void *)t, t->input.crop.h);
dev_dbg(t->dev, "[0x%p] crop.pos.x = %d
",
(void *)t, t->input.crop.pos.x);
dev_dbg(t->dev, "[0x%p] crop.pos.y = %d
",
(void *)t, t->input.crop.pos.y);
/*打印ipu_task_entry->input的信息。*/
dev_dbg(t->dev, "[0x%p]input buffer:
", (void *)t);
dev_dbg(t->dev, "[0x%p] paddr = 0x%x
", (void *)t, t->input.paddr);
dev_dbg(t->dev, "[0x%p] i_off = 0x%x
", (void *)t, t->set.i_off);
dev_dbg(t->dev, "[0x%p] i_uoff = 0x%x
", (void *)t, t->set.i_uoff);
dev_dbg(t->dev, "[0x%p] i_voff = 0x%x
", (void *)t, t->set.i_voff);
dev_dbg(t->dev, "[0x%p] istride = %d
", (void *)t, t->set.istride);
if (t->input.deinterlace.enable) {
dev_dbg(t->dev, "[0x%p]deinterlace enabled with:
", (void *)t);
if (t->input.deinterlace.motion != HIGH_MOTION) {
dev_dbg(t->dev, "[0x%p] low/medium motion
", (void *)t);
dev_dbg(t->dev, "[0x%p] paddr_n = 0x%x
",
(void *)t, t->input.paddr_n);
} else
dev_dbg(t->dev, "[0x%p] high motion
", (void *)t);
}
/*打印ipu_task_entry->inputbuffer的一些信息。*/
dev_dbg(t->dev, "[0x%p]output:
", (void *)t);
dev_dbg(t->dev, "[0x%p] format = 0x%x
", (void *)t, t->output.format);
dev_dbg(t->dev, "[0x%p] width = %d
", (void *)t, t->output.width);
dev_dbg(t->dev, "[0x%p] height = %d
", (void *)t, t->output.height);
dev_dbg(t->dev, "[0x%p] crop.w = %d
", (void *)t, t->output.crop.w);
dev_dbg(t->dev, "[0x%p] crop.h = %d
", (void *)t, t->output.crop.h);
dev_dbg(t->dev, "[0x%p] crop.pos.x = %d
",
(void *)t, t->output.crop.pos.x);
dev_dbg(t->dev, "[0x%p] crop.pos.y = %d
",
(void *)t, t->output.crop.pos.y);
dev_dbg(t->dev, "[0x%p] rotate = %d
", (void *)t, t->output.rotate);
/*打印ipu_task_entry->output的信息。*/
dev_dbg(t->dev, "[0x%p]output buffer:
", (void *)t);
dev_dbg(t->dev, "[0x%p] paddr = 0x%x
", (void *)t, t->output.paddr);
dev_dbg(t->dev, "[0x%p] o_off = 0x%x
", (void *)t, t->set.o_off);
dev_dbg(t->dev, "[0x%p] o_uoff = 0x%x
", (void *)t, t->set.o_uoff);
dev_dbg(t->dev, "[0x%p] o_voff = 0x%x
", (void *)t, t->set.o_voff);
dev_dbg(t->dev, "[0x%p] ostride = %d
", (void *)t, t->set.ostride);
/*打印ipu_task_entry->outputbuffer的一些信息。*/
if (t->overlay_en) {
dev_dbg(t->dev, "[0x%p]overlay:
", (void *)t);
dev_dbg(t->dev, "[0x%p] format = 0x%x
",
(void *)t, t->overlay.format);
dev_dbg(t->dev, "[0x%p] width = %d
",
(void *)t, t->overlay.width);
dev_dbg(t->dev, "[0x%p] height = %d
",
(void *)t, t->overlay.height);
dev_dbg(t->dev, "[0x%p] crop.w = %d
",
(void *)t, t->overlay.crop.w);
dev_dbg(t->dev, "[0x%p] crop.h = %d
",
(void *)t, t->overlay.crop.h);
dev_dbg(t->dev, "[0x%p] crop.pos.x = %d
",
(void *)t, t->overlay.crop.pos.x);
dev_dbg(t->dev, "[0x%p] crop.pos.y = %d
",
(void *)t, t->overlay.crop.pos.y);
/*如果使能了overlay_en的话,打印overlay的一些信息。*/
dev_dbg(t->dev, "[0x%p]overlay buffer:
", (void *)t);
dev_dbg(t->dev, "[0x%p] paddr = 0x%x
",
(void *)t, t->overlay.paddr);
dev_dbg(t->dev, "[0x%p] ov_off = 0x%x
",
(void *)t, t->set.ov_off);
dev_dbg(t->dev, "[0x%p] ov_uoff = 0x%x
",
(void *)t, t->set.ov_uoff);
dev_dbg(t->dev, "[0x%p] ov_voff = 0x%x
",
(void *)t, t->set.ov_voff);
dev_dbg(t->dev, "[0x%p] ovstride = %d
",
(void *)t, t->set.ovstride);
/*打印overlaybuffer的一些信息。*/
if (t->overlay.alpha.mode == IPU_ALPHA_MODE_LOCAL) {
dev_dbg(t->dev, "[0x%p]local alpha enabled with:
",
(void *)t);
dev_dbg(t->dev, "[0x%p] paddr = 0x%x
",
(void *)t, t->overlay.alpha.loc_alp_paddr);
dev_dbg(t->dev, "[0x%p] ov_alpha_off = 0x%x
",
(void *)t, t->set.ov_alpha_off);
dev_dbg(t->dev, "[0x%p] ov_alpha_stride = %d
",
(void *)t, t->set.ov_alpha_stride);
} else
dev_dbg(t->dev, "[0x%p]globle alpha enabled with value 0x%x
",
(void *)t, t->overlay.alpha.gvalue);
if (t->overlay.colorkey.enable)
dev_dbg(t->dev, "[0x%p]colorkey enabled with value 0x%x
",
(void *)t, t->overlay.colorkey.value);
}
/*打印alpha通道的一些信息。*/
dev_dbg(t->dev, "[0x%p]want task_id = %d
", (void *)t, t->task_id);
dev_dbg(t->dev, "[0x%p]want task mode is 0x%x
",
(void *)t, t->set.mode);
dev_dbg(t->dev, "[0x%p] IC_MODE = 0x%x
", (void *)t, IC_MODE);
dev_dbg(t->dev, "[0x%p] ROT_MODE = 0x%x
", (void *)t, ROT_MODE);
dev_dbg(t->dev, "[0x%p] VDI_MODE = 0x%x
", (void *)t, VDI_MODE);
dev_dbg(t->dev, "[0x%p] Task_no = 0x%x
", (void *)t, t->task_no);
}
/*打印这个task的一些信息。*/
这个函数里面就是一些打印信息,可以在合适的地方添加这些打印信息来辅助调试。
14.dump_check_err函数
static void dump_check_err(struct device *dev, int err)
{
switch (err) {
case IPU_CHECK_ERR_INPUT_CROP:
dev_err(dev, "input crop setting error
");
break;
case IPU_CHECK_ERR_OUTPUT_CROP:
dev_err(dev, "output crop setting error
");
break;
case IPU_CHECK_ERR_OVERLAY_CROP:
dev_err(dev, "overlay crop setting error
");
break;
case IPU_CHECK_ERR_INPUT_OVER_LIMIT:
dev_err(dev, "input over limitation
");
break;
case IPU_CHECK_ERR_OVERLAY_WITH_VDI:
dev_err(dev, "do not support overlay with deinterlace
");
break;
case IPU_CHECK_ERR_OV_OUT_NO_FIT:
dev_err(dev,
"width/height of overlay and ic output should be same
");
break;
case IPU_CHECK_ERR_PROC_NO_NEED:
dev_err(dev, "no ipu processing need
");
break;
case IPU_CHECK_ERR_SPLIT_INPUTW_OVER:
dev_err(dev, "split mode input width overflow
");
break;
case IPU_CHECK_ERR_SPLIT_INPUTH_OVER:
dev_err(dev, "split mode input height overflow
");
break;
case IPU_CHECK_ERR_SPLIT_OUTPUTW_OVER:
dev_err(dev, "split mode output width overflow
");
break;
case IPU_CHECK_ERR_SPLIT_OUTPUTH_OVER:
dev_err(dev, "split mode output height overflow
");
break;
case IPU_CHECK_ERR_SPLIT_WITH_ROT:
dev_err(dev, "not support split mode with rotation
");
break;
case IPU_CHECK_ERR_W_DOWNSIZE_OVER:
dev_err(dev, "horizontal downsizing ratio overflow
");
break;
case IPU_CHECK_ERR_H_DOWNSIZE_OVER:
dev_err(dev, "vertical downsizing ratio overflow
");
break;
default:
break;
}
}
这个函数在check_task里面调用。在上一个ipu_device.c分析(一)中分析的框架中,
staticstruct file_operations mxc_ipu_fops = {
。。。
.unlocked_ioctl= mxc_ipu_ioctl,
};
在这个mxc_ipu_ioctl函数中,它为应用程序提供了这个IPU_CHECK_TASKioctl宏,这个宏的核心是ipu_check_task函数,在这个ipu_check_task函数中调用了check_task函数来检查task是否出错,这个函数会生成各个错误信息,然后就会调用这个dump_check_err函数将错误信息打印出来。同时也会调用dump_check_warn函数来打印警告信息,这个dump_check_warn函数在下面贴出来。
15.dump_check_warn函数
static void dump_check_warn(struct device *dev, int warn)
{
if (warn & IPU_CHECK_WARN_INPUT_OFFS_NOT8ALIGN)
dev_warn(dev, "input u/v offset not 8 align
");
if (warn & IPU_CHECK_WARN_OUTPUT_OFFS_NOT8ALIGN)
dev_warn(dev, "output u/v offset not 8 align
");
if (warn & IPU_CHECK_WARN_OVERLAY_OFFS_NOT8ALIGN)
dev_warn(dev, "overlay u/v offset not 8 align
");
}
16.set_crop函数
static int set_crop(struct ipu_crop *crop, int width, int height, int fmt)
{
if ((width == 0) || (height == 0)) {
pr_err("Invalid param: width=%d, height=%d
", width, height);
return -EINVAL;
}
if ((IPU_PIX_FMT_TILED_NV12 == fmt) ||
(IPU_PIX_FMT_TILED_NV12F == fmt)) {
if (crop->w || crop->h) {
if (((crop->w + crop->pos.x) > width)
|| ((crop->h + crop->pos.y) > height)
|| (0 != (crop->w % IPU_PIX_FMT_TILED_NV12_MBALIGN))
|| (0 != (crop->h % IPU_PIX_FMT_TILED_NV12_MBALIGN))
|| (0 != (crop->pos.x % IPU_PIX_FMT_TILED_NV12_MBALIGN))
|| (0 != (crop->pos.y % IPU_PIX_FMT_TILED_NV12_MBALIGN))
) {
pr_err("set_crop error MB align.
");
return -EINVAL;
}
} else {
crop->pos.x = 0;
crop->pos.y = 0;
crop->w = width;
crop->h = height;
if ((0 != (crop->w % IPU_PIX_FMT_TILED_NV12_MBALIGN))
|| (0 != (crop->h % IPU_PIX_FMT_TILED_NV12_MBALIGN))) {
pr_err("set_crop error w/h MB align.
");
return -EINVAL;
}
}
} else {
if (crop->w || crop->h) {
if (((crop->w + crop->pos.x) > (width + 16))
|| ((crop->h + crop->pos.y) > height + 16)) {
pr_err("set_crop error exceeds width/height.
");
return -EINVAL;
}
} else {
crop->pos.x = 0;
crop->pos.y = 0;
crop->w = width;
crop->h = height;
}
crop->w -= crop->w%8;
crop->h -= crop->h%8;
}
if ((crop->w == 0) || (crop->h == 0)) {
pr_err("Invalid crop param: crop.w=%d, crop.h=%d
",
crop->w, crop->h);
return -EINVAL;
}
return 0;
}
这个函数根据传入的width,height和fmt参数设置传入的structipu_crop*crop参数。它在文件中主要是用来设置ipu_task_entry结构体中的ipu_output结构体里面的structipu_crop
crop。它在check_task,do_task_vdoa_vdi函数中都有调用。
17.update_offset函数
static void update_offset(unsigned int fmt,
unsigned int width, unsigned int height,
unsigned int pos_x, unsigned int pos_y,
int *off, int *uoff, int *voff, int *stride)
{
/* NOTE: u v offset should based on start point of off*/
switch (fmt) {
case IPU_PIX_FMT_YUV420P2:
case IPU_PIX_FMT_YUV420P:
*off = pos_y * width + pos_x;
*uoff = (width * (height - pos_y) - pos_x)
+ (width/2) * (pos_y/2) + pos_x/2;
/* In case height is odd, round up to even */
*voff = *uoff + (width/2) * ((height+1)/2);
break;
case IPU_PIX_FMT_YVU420P:
*off = pos_y * width + pos_x;
*voff = (width * (height - pos_y) - pos_x)
+ (width/2) * (pos_y/2) + pos_x/2;
/* In case height is odd, round up to even */
*uoff = *voff + (width/2) * ((height+1)/2);
break;
case IPU_PIX_FMT_YVU422P:
*off = pos_y * width + pos_x;
*voff = (width * (height - pos_y) - pos_x)
+ (width/2) * pos_y + pos_x/2;
*uoff = *voff + (width/2) * height;
break;
case IPU_PIX_FMT_YUV422P:
*off = pos_y * width + pos_x;
*uoff = (width * (height - pos_y) - pos_x)
+ (width/2) * pos_y + pos_x/2;
*voff = *uoff + (width/2) * height;
break;
case IPU_PIX_FMT_YUV444P:
*off = pos_y * width + pos_x;
*uoff = width * height;
*voff = width * height * 2;
break;
case IPU_PIX_FMT_NV12:
*off = pos_y * width + pos_x;
*uoff = (width * (height - pos_y) - pos_x)
+ width * (pos_y/2) + pos_x;
break;
case IPU_PIX_FMT_TILED_NV12:
/*
* tiled format, progressive:
* assuming that line is aligned with MB height (aligned to 16)
* offset = line * stride + (pixel / MB_width) * pixels_in_MB
* = line * stride + (pixel / 16) * 256
* = line * stride + pixel * 16
*/
*off = pos_y * width + (pos_x << 4);
*uoff = ALIGN(width * height, SZ_4K) + (*off >> 1) - *off;
break;
case IPU_PIX_FMT_TILED_NV12F:
/*
* tiled format, interlaced:
* same as above, only number of pixels in MB is 128,
* instead of 256
*/
*off = (pos_y >> 1) * width + (pos_x << 3);
*uoff = ALIGN(width * height/2, SZ_4K) + (*off >> 1) - *off;
break;
default:
*off = (pos_y * width + pos_x) * fmt_to_bpp(fmt)/8;
break;
}
*stride = width * bytes_per_pixel(fmt);
}
这个函数根据传入的fmt,width和height参数,计算出来off,uoff,voff和stride参数,直接在这些变量的地址