NXP

7.2 ipu_device.c分析(二)---具体函数分析

2019-07-12 13:41发布

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有两种模式:BANDMODEnon-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_taskget_res_do_taskipu_task_threadipu_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",所以对于imx51imx53返回720imx6qimx6qp返回968 这个函数在本文件中的update_split_settingcheck_taskget_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; }
这个函数根据传入的widthheightfmt参数设置传入的structipu_crop*crop参数。它在文件中主要是用来设置ipu_task_entry结构体中的ipu_output结构体里面的structipu_crop crop。它在check_taskdo_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); }
这个函数根据传入的fmtwidthheight参数,计算出来offuoffvoffstride参数,直接在这些变量的地址