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

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	<span style="color:#FF0000;">NULL_MODE</span>	0x0 
#define	<span style="color:#FF0000;">IC_MODE</span>		0x1 
#define	<span style="color:#FF0000;">ROT_MODE</span>	0x2 
#define	<span style="color:#FF0000;">VDI_MODE</span>	0x4 
#define <span style="color:#FF0000;">IPU_PREPROCESS_MODE_MASK</span>	(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	<span style="color:#FF0000;">VDOA_MODE</span>	0x8 
#define	<span style="color:#FF0000;">VDOA_BAND_MODE</span>	0x10 
	u8	mode; 
。。。。。。。。。
	struct stripe_setting sp_setting; 
};

上面红色字体是有关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占用的如下红色字体所示的任意一位的话,都可以认为是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的颜色空间colorspace。颜色空间在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的颜色空间,如果两者相同的话,就不需要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 = { 
	<span style="color:#FF0000;">.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, 
	},</span> 
	.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:\n", (void *)t); 
	dev_dbg(t->dev, "[0x%p]\tformat = 0x%x\n", (void *)t, t->input.format); 
	dev_dbg(t->dev, "[0x%p]\twidth = %d\n", (void *)t, t->input.width); 
	dev_dbg(t->dev, "[0x%p]\theight = %d\n", (void *)t, t->input.height); 
	dev_dbg(t->dev, "[0x%p]\tcrop.w = %d\n", (void *)t, t->input.crop.w); 
	dev_dbg(t->dev, "[0x%p]\tcrop.h = %d\n", (void *)t, t->input.crop.h); 
	dev_dbg(t->dev, "[0x%p]\tcrop.pos.x = %d\n", 
			(void *)t, t->input.crop.pos.x); 
	dev_dbg(t->dev, "[0x%p]\tcrop.pos.y = %d\n", 
			(void *)t, t->input.crop.pos.y); 

/*打印ipu_task_entry->input的信息。*/

	dev_dbg(t->dev, "[0x%p]input buffer:\n", (void *)t); 
	dev_dbg(t->dev, "[0x%p]\tpaddr = 0x%x\n", (void *)t, t->input.paddr); 
	dev_dbg(t->dev, "[0x%p]\ti_off = 0x%x\n", (void *)t, t->set.i_off); 
	dev_dbg(t->dev, "[0x%p]\ti_uoff = 0x%x\n", (void *)t, t->set.i_uoff); 
	dev_dbg(t->dev, "[0x%p]\ti_voff = 0x%x\n", (void *)t, t->set.i_voff); 
	dev_dbg(t->dev, "[0x%p]\tistride = %d\n", (void *)t, t->set.istride); 
	if (t->input.deinterlace.enable) { 
		dev_dbg(t->dev, "[0x%p]deinterlace enabled with:\n", (void *)t); 
		if (t->input.deinterlace.motion != HIGH_MOTION) { 
			dev_dbg(t->dev, "[0x%p]\tlow/medium motion\n", (void *)t); 
			dev_dbg(t->dev, "[0x%p]\tpaddr_n = 0x%x\n", 
				(void *)t, t->input.paddr_n); 
		} else 
			dev_dbg(t->dev, "[0x%p]\thigh motion\n", (void *)t); 
	} 

/*打印ipu_task_entry->inputbuffer的一些信息。*/

	dev_dbg(t->dev, "[0x%p]output:\n", (void *)t); 
	dev_dbg(t->dev, "[0x%p]\tformat = 0x%x\n", (void *)t, t->output.format); 
	dev_dbg(t->dev, "[0x%p]\twidth = %d\n", (void *)t, t->output.width); 
	dev_dbg(t->dev, "[0x%p]\theight = %d\n", (void *)t, t->output.height); 
	dev_dbg(t->dev, "[0x%p]\tcrop.w = %d\n", (void *)t, t->output.crop.w); 
	dev_dbg(t->dev, "[0x%p]\tcrop.h = %d\n", (void *)t, t->output.crop.h); 
	dev_dbg(t->dev, "[0x%p]\tcrop.pos.x = %d\n", 
			(void *)t, t->output.crop.pos.x); 
	dev_dbg(t->dev, "[0x%p]\tcrop.pos.y = %d\n", 
			(void *)t, t->output.crop.pos.y); 
	dev_dbg(t->dev, "[0x%p]\trotate = %d\n", (void *)t, t->output.rotate); 

/*打印ipu_task_entry->output的信息。*/

	dev_dbg(t->dev, "[0x%p]output buffer:\n", (void *)t); 
	dev_dbg(t->dev, "[0x%p]\tpaddr = 0x%x\n", (void *)t, t->output.paddr); 
	dev_dbg(t->dev, "[0x%p]\to_off = 0x%x\n", (void *)t, t->set.o_off); 
	dev_dbg(t->dev, "[0x%p]\to_uoff = 0x%x\n", (void *)t, t->set.o_uoff); 
	dev_dbg(t->dev, "[0x%p]\to_voff = 0x%x\n", (void *)t, t->set.o_voff); 
	dev_dbg(t->dev, "[0x%p]\tostride = %d\n", (void *)t, t->set.ostride); 

/*打印ipu_task_entry->outputbuffer的一些信息。*/

	if (t->overlay_en) { 
		dev_dbg(t->dev, "[0x%p]overlay:\n", (void *)t); 
		dev_dbg(t->dev, "[0x%p]\tformat = 0x%x\n", 
				(void *)t, t->overlay.format); 
		dev_dbg(t->dev, "[0x%p]\twidth = %d\n", 
				(void *)t, t->overlay.width); 
		dev_dbg(t->dev, "[0x%p]\theight = %d\n", 
				(void *)t, t->overlay.height); 
		dev_dbg(t->dev, "[0x%p]\tcrop.w = %d\n", 
				(void *)t, t->overlay.crop.w); 
		dev_dbg(t->dev, "[0x%p]\tcrop.h = %d\n", 
				(void *)t, t->overlay.crop.h); 
		dev_dbg(t->dev, "[0x%p]\tcrop.pos.x = %d\n", 
				(void *)t, t->overlay.crop.pos.x); 
		dev_dbg(t->dev, "[0x%p]\tcrop.pos.y = %d\n", 
				(void *)t, t->overlay.crop.pos.y); 

/*如果使能了overlay_en的话,打印overlay的一些信息。*/

		dev_dbg(t->dev, "[0x%p]overlay buffer:\n", (void *)t); 
		dev_dbg(t->dev, "[0x%p]\tpaddr = 0x%x\n", 
				(void *)t, t->overlay.paddr); 
		dev_dbg(t->dev, "[0x%p]\tov_off = 0x%x\n", 
				(void *)t, t->set.ov_off); 
		dev_dbg(t->dev, "[0x%p]\tov_uoff = 0x%x\n", 
				(void *)t, t->set.ov_uoff); 
		dev_dbg(t->dev, "[0x%p]\tov_voff = 0x%x\n", 
				(void *)t, t->set.ov_voff); 
		dev_dbg(t->dev, "[0x%p]\tovstride = %d\n", 
				(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:\n", 
					(void *)t); 
			dev_dbg(t->dev, "[0x%p]\tpaddr = 0x%x\n", 
					(void *)t, t->overlay.alpha.loc_alp_paddr); 
			dev_dbg(t->dev, "[0x%p]\tov_alpha_off = 0x%x\n", 
					(void *)t, t->set.ov_alpha_off); 
			dev_dbg(t->dev, "[0x%p]\tov_alpha_stride = %d\n", 
					(void *)t, t->set.ov_alpha_stride); 
		} else 
			dev_dbg(t->dev, "[0x%p]globle alpha enabled with value 0x%x\n", 
					(void *)t, t->overlay.alpha.gvalue); 
		if (t->overlay.colorkey.enable) 
			dev_dbg(t->dev, "[0x%p]colorkey enabled with value 0x%x\n", 
					(void *)t, t->overlay.colorkey.value); 
	} 

/*打印alpha通道的一些信息。*/

	dev_dbg(t->dev, "[0x%p]want task_id = %d\n", (void *)t, t->task_id); 
	dev_dbg(t->dev, "[0x%p]want task mode is 0x%x\n", 
				(void *)t, t->set.mode); 
	dev_dbg(t->dev, "[0x%p]\tIC_MODE = 0x%x\n", (void *)t, IC_MODE); 
	dev_dbg(t->dev, "[0x%p]\tROT_MODE = 0x%x\n", (void *)t, ROT_MODE); 
	dev_dbg(t->dev, "[0x%p]\tVDI_MODE = 0x%x\n", (void *)t, VDI_MODE); 
	dev_dbg(t->dev, "[0x%p]\tTask_no = 0x%x\n\n\n", (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\n"); 
		break; 
	case IPU_CHECK_ERR_OUTPUT_CROP: 
		dev_err(dev, "output crop setting error\n"); 
		break; 
	case IPU_CHECK_ERR_OVERLAY_CROP: 
		dev_err(dev, "overlay crop setting error\n"); 
		break; 
	case IPU_CHECK_ERR_INPUT_OVER_LIMIT: 
		dev_err(dev, "input over limitation\n"); 
		break; 
	case IPU_CHECK_ERR_OVERLAY_WITH_VDI: 
		dev_err(dev, "do not support overlay with deinterlace\n"); 
		break; 
	case IPU_CHECK_ERR_OV_OUT_NO_FIT: 
		dev_err(dev, 
			"width/height of overlay and ic output should be same\n"); 
		break; 
	case IPU_CHECK_ERR_PROC_NO_NEED: 
		dev_err(dev, "no ipu processing need\n"); 
		break; 
	case IPU_CHECK_ERR_SPLIT_INPUTW_OVER: 
		dev_err(dev, "split mode input width overflow\n"); 
		break; 
	case IPU_CHECK_ERR_SPLIT_INPUTH_OVER: 
		dev_err(dev, "split mode input height overflow\n"); 
		break; 
	case IPU_CHECK_ERR_SPLIT_OUTPUTW_OVER: 
		dev_err(dev, "split mode output width overflow\n"); 
		break; 
	case IPU_CHECK_ERR_SPLIT_OUTPUTH_OVER: 
		dev_err(dev, "split mode output height overflow\n"); 
		break; 
	case IPU_CHECK_ERR_SPLIT_WITH_ROT: 
		dev_err(dev, "not support split mode with rotation\n"); 
		break; 
	case IPU_CHECK_ERR_W_DOWNSIZE_OVER: 
		dev_err(dev, "horizontal downsizing ratio overflow\n"); 
		break; 
	case IPU_CHECK_ERR_H_DOWNSIZE_OVER: 
		dev_err(dev, "vertical downsizing ratio overflow\n"); 
		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\n"); 
	if (warn & IPU_CHECK_WARN_OUTPUT_OFFS_NOT8ALIGN) 
		dev_warn(dev, "output u/v offset not 8 align\n"); 
	if (warn & IPU_CHECK_WARN_OVERLAY_OFFS_NOT8ALIGN) 
		dev_warn(dev, "overlay u/v offset not 8 align\n"); 
}

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\n", 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.\n"); 
				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.\n"); 
				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.\n"); 
				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\n", 
			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参数,直接在这些变量的地址里面进行修改传回去。这个函数主要设置的是ipu_task_entry结构体里面的structtask_set set结构体里面的这些变量:

	u32 i_off; 
	u32 i_uoff; 
	u32 i_voff; 
	u32 istride; 

	u32 ov_off; 
	u32 ov_uoff; 
	u32 ov_voff; 
	u32 ovstride; 

	u32 ov_alpha_off; 
	u32 ov_alpha_stride; 

	u32 o_off; 
	u32 o_uoff; 
	u32 o_voff; 
	u32 ostride;

但是这里面的算法并不清楚(只知道off的算法),以及这个uoffvoff分别代表什么意思?这个函数都是和上一个set_crop函数绑定使用的,首先调用set_crop函数设置输入,输出或overlay里面的crop,然后调用这个update_offset函数更新偏移量。


18.update_split_setting 函数

static int update_split_setting(struct ipu_task_entry *t, bool vdi_split) 
{ 
	struct stripe_param left_stripe; 
	struct stripe_param right_stripe; 
	struct stripe_param up_stripe; 
	struct stripe_param down_stripe; 
	u32 iw, ih, ow, oh; 
	u32 max_width; 
	int ret; 

	if (t->output.rotate >= IPU_ROTATE_90_RIGHT) 
		return IPU_CHECK_ERR_SPLIT_WITH_ROT; 

	iw = t->input.crop.w; 
	ih = t->input.crop.h; 

	ow = t->output.crop.w; 
	oh = t->output.crop.h; 

	memset(&left_stripe, 0, sizeof(left_stripe)); 
	memset(&right_stripe, 0, sizeof(right_stripe)); 
	memset(&up_stripe, 0, sizeof(up_stripe)); 
	memset(&down_stripe, 0, sizeof(down_stripe)); 

	if (t->set.split_mode & RL_SPLIT) { 
		/* 
		 * We do want equal strips: initialize stripes in case 
		 * calc_stripes returns before actually doing the calculation 
		 */ 
		left_stripe.input_width = iw / 2; 
		left_stripe.output_width = ow / 2; 
		right_stripe.input_column = iw / 2; 
		right_stripe.output_column = ow / 2; 

		if (vdi_split) 
			max_width = soc_max_vdi_in_width(t->ipu); 
		else 
			max_width = soc_max_out_width(); 
		ret = ipu_calc_stripes_sizes(iw, 
				ow, 
				max_width, 
				(((unsigned long long)1) << 32), /* 32bit for fractional*/ 
				1, /* equal stripes */ 
				t->input.format, 
				t->output.format, 
				&left_stripe, 
				&right_stripe); 
		if (ret < 0) 
			return IPU_CHECK_ERR_W_DOWNSIZE_OVER; 
		else if (ret) 
			dev_dbg(t->dev, "Warn: no:0x%x,calc_stripes ret:%d\n", 
				 t->task_no, ret); 
		t->set.sp_setting.iw = left_stripe.input_width; 
		t->set.sp_setting.ow = left_stripe.output_width; 
		t->set.sp_setting.outh_resize_ratio = left_stripe.irr; 
		t->set.sp_setting.i_left_pos = left_stripe.input_column; 
		t->set.sp_setting.o_left_pos = left_stripe.output_column; 
		t->set.sp_setting.i_right_pos = right_stripe.input_column; 
		t->set.sp_setting.o_right_pos = right_stripe.output_column; 
	} else { 
		t->set.sp_setting.iw = iw; 
		t->set.sp_setting.ow = ow; 
		t->set.sp_setting.outh_resize_ratio = 0; 
		t->set.sp_setting.i_left_pos = 0; 
		t->set.sp_setting.o_left_pos = 0; 
		t->set.sp_setting.i_right_pos = 0; 
		t->set.sp_setting.o_right_pos = 0; 
	} 
	if ((t->set.sp_setting.iw + t->set.sp_setting.i_right_pos) > (iw+16)) 
		return IPU_CHECK_ERR_SPLIT_INPUTW_OVER; 
	if (((t->set.sp_setting.ow + t->set.sp_setting.o_right_pos) > ow) 
		|| (t->set.sp_setting.ow > soc_max_out_width())) 
		return IPU_CHECK_ERR_SPLIT_OUTPUTW_OVER; 
	if (rounddown(t->set.sp_setting.ow, 8) * 8 <= 
	    rounddown(t->set.sp_setting.iw, 8)) 
		return IPU_CHECK_ERR_W_DOWNSIZE_OVER; 

	if (t->set.split_mode & UD_SPLIT) { 
		/* 
		 * We do want equal strips: initialize stripes in case 
		 * calc_stripes returns before actually doing the calculation 
		 */ 
		up_stripe.input_width = ih / 2; 
		up_stripe.output_width = oh / 2; 
		down_stripe.input_column = ih / 2; 
		down_stripe.output_column = oh / 2; 
		ret = ipu_calc_stripes_sizes(ih, 
				oh, 
				soc_max_out_height(), 
				(((unsigned long long)1) << 32), /* 32bit for fractional*/ 
				0x1 | 0x2, /* equal stripes and vertical */ 
				t->input.format, 
				t->output.format, 
				&up_stripe, 
				&down_stripe); 
		if (ret < 0) 
			return IPU_CHECK_ERR_H_DOWNSIZE_OVER; 
		else if (ret) 
			dev_err(t->dev, "Warn: no:0x%x,calc_stripes ret:%d\n", 
				 t->task_no, ret); 
		t->set.sp_setting.ih = up_stripe.input_width; 
		t->set.sp_setting.oh = up_stripe.output_width; 
		t->set.sp_setting.outv_resize_ratio = up_stripe.irr; 
		t->set.sp_setting.i_top_pos = up_stripe.input_column; 
		t->set.sp_setting.o_top_pos = up_stripe.output_column; 
		t->set.sp_setting.i_bottom_pos = down_stripe.input_column; 
		t->set.sp_setting.o_bottom_pos = down_stripe.output_column; 
	} else { 
		t->set.sp_setting.ih = ih; 
		t->set.sp_setting.oh = oh; 
		t->set.sp_setting.outv_resize_ratio = 0; 
		t->set.sp_setting.i_top_pos = 0; 
		t->set.sp_setting.o_top_pos = 0; 
		t->set.sp_setting.i_bottom_pos = 0; 
		t->set.sp_setting.o_bottom_pos = 0; 
	} 

	/* downscale case: enforce limits */ 
	if (((t->set.sp_setting.ih + t->set.sp_setting.i_bottom_pos) > (ih)) 
	     && (t->set.sp_setting.ih >= t->set.sp_setting.oh)) 
		return IPU_CHECK_ERR_SPLIT_INPUTH_OVER; 
	/* upscale case: relax limits because ipu_calc_stripes_sizes() may 
	   create input stripe that falls just outside of the input window */ 
	else if ((t->set.sp_setting.ih + t->set.sp_setting.i_bottom_pos) 
		 > (ih+16)) 
		return IPU_CHECK_ERR_SPLIT_INPUTH_OVER; 
	if (((t->set.sp_setting.oh + t->set.sp_setting.o_bottom_pos) > oh) 
		|| (t->set.sp_setting.oh > soc_max_out_height())) 
		return IPU_CHECK_ERR_SPLIT_OUTPUTH_OVER; 
	if (rounddown(t->set.sp_setting.oh, 8) * 8 <= 
	    rounddown(t->set.sp_setting.ih, 8)) 
		return IPU_CHECK_ERR_H_DOWNSIZE_OVER; 

	return IPU_CHECK_OK; 
}

这个函数里面调用了ipu_calc_stripes_sizes函数,这个函数在ipu_calc_stripes_sizes.c中定义,但是看不懂这几个函数,ipu_calc_stripes_sizes.cipu_ic.c都是有关ic的操作函数。先在这作标记,往下分析。


19.check_task函数

static int check_task(struct ipu_task_entry *t) 
{ 
	int tmp; 
	int ret = IPU_CHECK_OK; 
	int timeout; 
	bool vdi_split = false; 
	int ocw, och; 

	if ((IPU_PIX_FMT_TILED_NV12 == t->overlay.format) || 
		(IPU_PIX_FMT_TILED_NV12F == t->overlay.format) || 
		(IPU_PIX_FMT_TILED_NV12 == t->output.format) || 
		(IPU_PIX_FMT_TILED_NV12F == t->output.format) || 
		((IPU_PIX_FMT_TILED_NV12F == t->input.format) && 
			!t->input.deinterlace.enable)) { 
		ret = IPU_CHECK_ERR_NOT_SUPPORT; 
		goto done; 
	} 

/*如果发生上述的情况,就说明发生了不支持的task,返回的错误信息是IPU_CHECK_ERR_NOT_SUPPORT,上述的IPU_PIX_FMT_TILED_NV12IPU_PIX_FMT_TILED_NV12F是像素格式,它们在ipu.h中定义,同样是fourcc码:

/*two planes -- 12 tiled Y/CbCr 4:2:0 */

#defineIPU_PIX_FMT_TILED_NV12 fourcc('T', 'N', 'V', 'P')

#defineIPU_PIX_FMT_TILED_NV12F fourcc('T', 'N', 'V', 'F')

*/

	/* check input */ 
	ret = set_crop(&t->input.crop, t->input.width, t->input.height, 
		t->input.format); 
	if (ret < 0) { 
		ret = IPU_CHECK_ERR_INPUT_CROP; 
		goto done; 
	} else 
		update_offset(t->input.format, t->input.width, t->input.height, 
				t->input.crop.pos.x, t->input.crop.pos.y, 
				&t->set.i_off, &t->set.i_uoff, 
				&t->set.i_voff, &t->set.istride); 

/*检查输入参数,这两个函数在上面分析了,首先调用set_crop函数根据t->input.widtht->input.heightt->input.format来填充&t->input.crop,然后通过update_offset函数来更新&t->set.i_off&t->set.i_uoff&t->set.i_voff&t->set.istride这些偏移值。*/

	/* check output */ 
	ret = set_crop(&t->output.crop, t->output.width, t->output.height, 
		t->output.format); 
	if (ret < 0) { 
		ret = IPU_CHECK_ERR_OUTPUT_CROP; 
		goto done; 
	} else 
		update_offset(t->output.format, 
				t->output.width, t->output.height, 
				t->output.crop.pos.x, t->output.crop.pos.y, 
				&t->set.o_off, &t->set.o_uoff, 
				&t->set.o_voff, &t->set.ostride); 

/*检查输出参数,首先调用set_crop函数根据t->output.widtht->output.heightt->output.format来填充&t->output.crop,然后通过update_offset函数来更新&t->set.o_off,&t->set.o_uoff, &t->set.o_voff&t->set.ostride这些偏移值。*/

	if (t->output.rotate >= IPU_ROTATE_90_RIGHT) { 
		/* 
		 * Cache output width and height and 
		 * swap them so that we may check 
		 * downsize overflow correctly. 
		 */ 
		ocw = t->output.crop.h; 
		och = t->output.crop.w; 
	} else { 
		ocw = t->output.crop.w; 
		och = t->output.crop.h; 
	} 

/*ipu_task_entry结构体里面的structipu_output有一个rotate参数,它记录了输出的图像是否发生旋转,如果旋转的角度大于90度的话,就调换widthheight。这里面的ocw应该是outputcache widthoch应该是outputcache height的意思。*/

	if (ocw * 8 <= t->input.crop.w) { 
		ret = IPU_CHECK_ERR_W_DOWNSIZE_OVER; 
		goto done; 
	} 

	if (och * 8 <= t->input.crop.h) { 
		ret = IPU_CHECK_ERR_H_DOWNSIZE_OVER; 
		goto done; 
	} 

/*因为这里面设置的是crop的值,所以它是一个缩放值。从这里可以看出来,它最多能够缩放成原来的1/8,如果比这个比例还小的话,就会报错。*/

	if ((IPU_PIX_FMT_TILED_NV12 == t->input.format) || 
		(IPU_PIX_FMT_TILED_NV12F == t->input.format)) { 
		if ((t->input.crop.w > soc_max_in_width(1)) || 
			(t->input.crop.h > soc_max_in_height())) { 
			ret = IPU_CHECK_ERR_INPUT_OVER_LIMIT; 
			goto done; 
		} 
		/* output fmt: NV12 and YUYV, now don't support resize */ 
		if (((IPU_PIX_FMT_NV12 != t->output.format) && 
				(IPU_PIX_FMT_YUYV != t->output.format)) || 
			(t->input.crop.w != t->output.crop.w) || 
			(t->input.crop.h != t->output.crop.h)) { 
			ret = IPU_CHECK_ERR_NOT_SUPPORT; 
			goto done; 
		} 
	} 

/*再次判断输入输出的参数以及t->input.crop.wt->input.crop.h等参数。*/

	/* check overlay if there is */ 
	if (t->overlay_en) { 
		if (t->input.deinterlace.enable) { 
			ret = IPU_CHECK_ERR_OVERLAY_WITH_VDI; 
			goto done; 
		} 

/*如果使能了input中的deinterlace的话,再启用overlay就会报错:IPU_CHECK_ERR_OVERLAY_WITH_VDI,从这可以看出来overlay不能和VDI共存。*/

		ret = set_crop(&t->overlay.crop, t->overlay.width, 
			t->overlay.height, t->overlay.format); 
		if (ret < 0) { 
			ret = IPU_CHECK_ERR_OVERLAY_CROP; 
			goto done; 
		} else { 
			ocw = t->output.crop.w; 
			och = t->output.crop.h; 

			if (t->output.rotate >= IPU_ROTATE_90_RIGHT) { 
				ocw = t->output.crop.h; 
				och = t->output.crop.w; 
			} 
			if ((t->overlay.crop.w != ocw) || 
			    (t->overlay.crop.h != och)) { 
				ret = IPU_CHECK_ERR_OV_OUT_NO_FIT; 
				goto done; 
			} 

			update_offset(t->overlay.format, 
					t->overlay.width, t->overlay.height, 
					t->overlay.crop.pos.x, t->overlay.crop.pos.y, 
					&t->set.ov_off, &t->set.ov_uoff, 
					&t->set.ov_voff, &t->set.ovstride); 
			if (t->overlay.alpha.mode == IPU_ALPHA_MODE_LOCAL) { 
				t->set.ov_alpha_stride = t->overlay.width; 
				t->set.ov_alpha_off = t->overlay.crop.pos.y * 
					t->overlay.width + t->overlay.crop.pos.x; 
			} 
		} 
	} 

/*与上面检查输入输出类似,在这里检查overlay同样先调用set_crop函数设置&t->overlay.crop里面的值,然后设置ocwoch的值,之后调用update_offset函数更新&t->set.ov_off,&t->set.ov_uoff,&t->set.ov_voff&t->set.ovstride的偏移值,然后根据t->overlay.alpha.mode的值设置t->set.ov_alpha_stridet->set.ov_alpha_off的值。*/

	/* input overflow? */ 
	if (!((IPU_PIX_FMT_TILED_NV12 == t->input.format) || 
		(IPU_PIX_FMT_TILED_NV12F == t->input.format))) { 
		if ((t->input.crop.w > soc_max_in_width(0)) || 
			(t->input.crop.h > soc_max_in_height())) { 
				ret = IPU_CHECK_ERR_INPUT_OVER_LIMIT; 
				goto done; 
		} 
	} 

/*检查t->input.crop.wt->input.crop.h是否合乎规范。*/

	/* check task mode */ 
	t->set.mode = NULL_MODE; 
	t->set.split_mode = NO_SPLIT; 

	if (t->output.rotate >= IPU_ROTATE_90_RIGHT) { 
		/*output swap*/ 
		tmp = t->output.crop.w; 
		t->output.crop.w = t->output.crop.h; 
		t->output.crop.h = tmp; 
	} 
 
	if (t->output.rotate >= IPU_ROTATE_90_RIGHT) 
		t->set.mode |= ROT_MODE; 

/*如果t->output.rotate>= IPU_ROTATE_90_RIGHT的话,就调换widthheight,同时将

t->set.mode中置位成ROT_MODE*/

	/*need resize or CSC?*/ 
	if ((t->input.crop.w != t->output.crop.w) || 
			(t->input.crop.h != t->output.crop.h) || 
			need_csc(t->input.format, t->output.format)) 
		t->set.mode |= IC_MODE; 

/*检查输入与输出里面crop参数是否相等,或者输入与输出里面的格式是否相同,如果不相等的话就需要将t->set.mode中置位成IC_MODE,意思是需要ImageConverte*/

	/*need cropping?*/ 
	if ((t->input.crop.w != t->input.width)       || 
		(t->input.crop.h != t->input.height)  || 
		(t->output.crop.w != t->output.width) || 
		(t->output.crop.h != t->output.height)) 
		t->set.mode |= IC_MODE; 

	/*need flip?*/ 
	if ((t->set.mode == NULL_MODE) && (t->output.rotate > IPU_ROTATE_NONE)) 
		t->set.mode |= IC_MODE; 

	/*need IDMAC do format(same color space)?*/ 
	if ((t->set.mode == NULL_MODE) && (t->input.format != t->output.format)) 
		t->set.mode |= IC_MODE; 

	/*overlay support*/ 
	if (t->overlay_en) 
		t->set.mode |=  IC_MODE; 

/*对于上面几种情况,都需要将t->set.mode置位成IC_MODE模式。*/

	/*deinterlace*/ 
	if (t->input.deinterlace.enable) { 
		t->set.mode &= ~IC_MODE; 
		t->set.mode |= VDI_MODE; 
	} 

/*如果t->input.deinterlace.enable置位的话,意思是使能了输入数据去交错功能,就需要将t->set.mode中的IC_MODE位清零,同时置位成VDI_MODE模式。*/

	if ((IPU_PIX_FMT_TILED_NV12 == t->input.format) || 
		(IPU_PIX_FMT_TILED_NV12F == t->input.format)) { 
		if (t->set.mode & ROT_MODE) { 
			ret = IPU_CHECK_ERR_NOT_SUPPORT; 
			goto done; 
		} 
		t->set.mode |= VDOA_MODE; 
		if (IPU_PIX_FMT_TILED_NV12F == t->input.format) 
			t->set.mode |= VDOA_BAND_MODE; 
		t->set.mode &= ~IC_MODE; 
	} 

/*如果t->input.format的格式为IPU_PIX_FMT_TILED_NV12IPU_PIX_FMT_TILED_NV12F的话,根据情况置位或清零t->set.mode中的某些位。*/

	if ((t->set.mode & (IC_MODE | VDI_MODE)) && 
		(IPU_PIX_FMT_TILED_NV12F != t->input.format)) { 
		if (t->output.crop.w > soc_max_out_width()) 
			t->set.split_mode |= RL_SPLIT; 
		if (t->output.crop.h > soc_max_out_height()) 
			t->set.split_mode |= UD_SPLIT; 
		if (!t->set.split_mode && (t->set.mode & VDI_MODE) && 
				(t->input.crop.w > 
				 soc_max_vdi_in_width(t->ipu))) { 
			t->set.split_mode |= RL_SPLIT; 
			vdi_split = true; 
		} 

/*根据t->output.crop里面的参数来决定是否拆分图像,这个还是比较好理解的,如果t->output.crop.w大于soc_max_out_width()的最大宽度的话,就需要左右拆分图像,同样,如果t->output.crop.h大于soc_max_out_height()的话,就需要上下拆分图像。*/

		if (t->set.split_mode) { 
			if ((t->set.split_mode == RL_SPLIT) || 
				 (t->set.split_mode == UD_SPLIT)) 
				timeout = DEF_TIMEOUT_MS * 2 + DEF_DELAY_MS; 
			else 
				timeout = DEF_TIMEOUT_MS * 4 + DEF_DELAY_MS; 
			if (t->timeout < timeout) 
				t->timeout = timeout; 

/*根据不同的拆分模式来确定超时时间,每种拆分模式需要的时间应该是不同的。*/

			ret = update_split_setting(t, vdi_split); 
			if (ret > IPU_CHECK_ERR_MIN) 
				goto done; 
		} 
	} 

/*调用update_split_setting函数来更新拆分设置。在这个函数中调用了ipu_calc_stripes_sizes函数,这时候再来看在ipu_calc_stripes_sizes函数的意义,应该就是如果设置了t->set.split_mode中的拆分模式的话(上下拆分还是左右拆分),通过这个函数来重新设置图像的大小等信息,不知道这样理解对不对。*/

	if (t->output.rotate >= IPU_ROTATE_90_RIGHT) { 
		/*output swap*/ 
		tmp = t->output.crop.w; 
		t->output.crop.w = t->output.crop.h; 
		t->output.crop.h = tmp; 
	} 

	if (t->set.mode == NULL_MODE) { 
		ret = IPU_CHECK_ERR_PROC_NO_NEED; 
		goto done; 
	} 

	if ((t->set.i_uoff % 8) || (t->set.i_voff % 8)) 
		ret |= IPU_CHECK_WARN_INPUT_OFFS_NOT8ALIGN; 
	if ((t->set.o_uoff % 8) || (t->set.o_voff % 8)) 
		ret |= IPU_CHECK_WARN_OUTPUT_OFFS_NOT8ALIGN; 
	if (t->overlay_en && ((t->set.ov_uoff % 8) || (t->set.ov_voff % 8))) 
		ret |= IPU_CHECK_WARN_OVERLAY_OFFS_NOT8ALIGN; 

done: 
	/* dump msg */ 
	if (debug) { 
		if (ret > IPU_CHECK_ERR_MIN) 
			dump_check_err(t->dev, ret); 
		else if (ret != IPU_CHECK_OK) 
			dump_check_warn(t->dev, ret); 
	} 

	return ret; 
}

至此,这个check_task函数就简单分析完毕,总结一下:它的核心就是ipu_task_entry结构体,首先通过set_crop函数来设置&t->input.crop里面的参数,然后调用update_offset函数来更新&t->set里面的一些偏移值,然后重复这个操作来设置&t->output.crop的值等等,如果使能了overlay的话,就再重复一遍。然后设置t->set.modet->set.split_mode的值。它基本就是完成了这些操作。这个函数在prepare_taskipu_check_task函数中会调用到。


20.prepare_task函数

static int prepare_task(struct ipu_task_entry *t) 
{ 
	int ret = 0; 

	ret = check_task(t); 
	if (ret > IPU_CHECK_ERR_MIN) 
		return -EINVAL; 

/*首先调用check_task函数来设置ipu_task_entry中的某些位等。*/

	if (t->set.mode & VDI_MODE) { 
		t->task_id = IPU_TASK_ID_VF; 
		t->set.task = VDI_VF; 
		if (t->set.mode & ROT_MODE) 
			t->set.task |= ROT_VF; 
	} 

/*设置ipu_task_entry中的task_idset.task*/

	if (VDOA_MODE == t->set.mode) { 
		if (t->set.task != 0) { 
			dev_err(t->dev, "ERR: vdoa only task:0x%x, [0x%p].\n", 
					t->set.task, t); 
			return -EINVAL; 
		} 
		t->set.task |= VDOA_ONLY; 
	} 

	if (VDOA_BAND_MODE & t->set.mode) { 
		/* to save band size: 1<<3 = 8 lines */ 
		t->set.band_lines = 3; 
	} 

/*如果为VDOA_BAND_MODE模式的话,需要设置t->set.band_lines位为3.*/

	dump_task_info(t); 

	return ret; 
}

可见这个prepare_task函数同样是设置ipu_task_entry结构体中的某些位。这个函数在create_split_child_taskipu_queue_task中调用。


21.ic_vf_pp_is_busy函数

static uint32_t ic_vf_pp_is_busy(struct ipu_soc *ipu, bool is_vf) 
{ 
	uint32_t	status; 
	uint32_t	status_vf; 
	uint32_t	status_rot; 

	if (is_vf) { 
		status = ipu_channel_status(ipu, MEM_VDI_PRP_VF_MEM); 
		status_vf = ipu_channel_status(ipu, MEM_PRP_VF_MEM); 
		status_rot = ipu_channel_status(ipu, MEM_ROT_VF_MEM); 
		return status || status_vf || status_rot; 
	} else { 
		status = ipu_channel_status(ipu, MEM_PP_MEM); 
		status_rot = ipu_channel_status(ipu, MEM_ROT_PP_MEM); 
		return status || status_rot; 
	} 
}

这个函数就是查看某一个channel是否被使能了,它调用了ipu_common.c中的ipu_channel_status函数,其中任意一个通道使能的话这个函数都会返回1.


22._get_vdoa_ipu_res函数

static int _get_vdoa_ipu_res(struct ipu_task_entry *t) 
{ 
	int		i; 
	struct ipu_soc	*ipu; 
	u8		*used; 
	uint32_t	found_ipu = 0; 
	uint32_t	found_vdoa = 0; 
	struct ipu_channel_tabel	*tbl = &ipu_ch_tbl; 

	mutex_lock(&tbl->lock); 
	if (t->set.mode & VDOA_MODE) { 
		if (NULL != t->vdoa_handle) 
			found_vdoa = 1; 
		else { 
			found_vdoa = tbl->vdoa_used ? 0 : 1; 
			if (found_vdoa) { 
				tbl->vdoa_used = 1; 
				vdoa_get_handle(&t->vdoa_handle); 
			} else 
				/* first get vdoa->ipu resource sequence */ 
				goto out; 
			if (t->set.task & VDOA_ONLY) 
				goto out; 
		} 
	} 

	for (i = 0; i < max_ipu_no; i++) { 
		ipu = ipu_get_soc(i); 
		if (IS_ERR(ipu)) 
			dev_err(t->dev, "no:0x%x,found_vdoa:%d, ipu:%d\n", 
				 t->task_no, found_vdoa, i); 
 
		used = &tbl->used[i][IPU_PP_CH_VF]; 
		if (t->set.mode & VDI_MODE) { 
			if (0 == *used) { 
				*used = 1; 
				found_ipu = 1; 
				break; 
			} 
		} else if ((t->set.mode & IC_MODE) || only_rot(t->set.mode)) { 
			if (0 == *used) { 
				t->task_id = IPU_TASK_ID_VF; 
				if (t->set.mode & IC_MODE) 
					t->set.task |= IC_VF; 
				if (t->set.mode & ROT_MODE) 
					t->set.task |= ROT_VF; 
				*used = 1; 
				found_ipu = 1; 
				break; 
			} 
		} else 
			dev_err(t->dev, "no:0x%x,found_vdoa:%d, mode:0x%x\n", 
				 t->task_no, found_vdoa, t->set.mode); 
	} 
	if (found_ipu) 
		goto next; 

	for (i = 0; i < max_ipu_no; i++) { 
		ipu = ipu_get_soc(i); 
		if (IS_ERR(ipu)) 
			dev_err(t->dev, "no:0x%x,found_vdoa:%d, ipu:%d\n", 
				 t->task_no, found_vdoa, i); 

		if ((t->set.mode & IC_MODE) || only_rot(t->set.mode)) { 
			used = &tbl->used[i][IPU_PP_CH_PP]; 
			if (0 == *used) { 
				t->task_id = IPU_TASK_ID_PP; 
				if (t->set.mode & IC_MODE) 
					t->set.task |= IC_PP; 
				if (t->set.mode & ROT_MODE) 
					t->set.task |= ROT_PP; 
				*used = 1; 
				found_ipu = 1; 
				break; 
			} 
		} 
	} 

next: 
	if (found_ipu) { 
		t->ipu = ipu; 
		t->ipu_id = i; 
		t->dev = ipu->dev; 
		if (atomic_inc_return(&t->res_get) == 2) 
			dev_err(t->dev, 
				"ERR no:0x%x,found_vdoa:%d,get ipu twice\n", 
				 t->task_no, found_vdoa); 
	} 
out: 
	dev_dbg(t->dev, 
		"%s:no:0x%x,found_vdoa:%d, found_ipu:%d\n", 
		 __func__, t->task_no, found_vdoa, found_ipu); 
	mutex_unlock(&tbl->lock); 
	if (t->set.task & VDOA_ONLY) 
		return found_vdoa; 
	else if (t->set.mode & VDOA_MODE) 
		return found_vdoa && found_ipu; 
	else 
		return found_ipu; 
}

在看这个函数的时候, 它直接使用了if(NULL != t->vdoa_handle),这个t->vdoa_handle是在哪初始化的?后来在搜索这个的时候发现,它是在init_tiled_buf函数中通过ret= vdoa_setup(t->vdoa_handle,&param);函数初始化的。跟踪这个vdoa_setup--->init_tiled_buf--->init_tiled_ch_bufs--->do_task_vdoa_only--->get_res_do_task;

或者vdoa_setup--->init_tiled_buf--->init_tiled_ch_bufs--->init_ic--->do_task--->get_res_do_task;(这两个流程都是后面的函数调用前面的函数)

在这个get_res_do_task函数中,get_vdoa_ipu_res--->_get_vdoa_ipu_res这两个函数是最先执行的。所以在第一次执行_get_vdoa_ipu_res函数的时候,这个t->vdoa_handle还没有通过vdoa_setup函数进行初始化。所以第一次执行这个函数的时候,可能就会走到out:这个标号这里。

然后returnget_vdoa_ipu_res函数中,get_vdoa_ipu_res函数中通过

ret= wait_event_timeout(res_waitq, _get_vdoa_ipu_res(t),

msecs_to_jiffies(t->timeout - DEF_DELAY_MS));

等待队列获取到信息。

这的流程后面再分析分析,不清楚~~~


23.put_vdoa_ipu_res函数

这个函数是get_vdoa_ipu_res函数的反函数,就不分析了。


24.get_vdoa_ipu_res

static int get_vdoa_ipu_res(struct ipu_task_entry *t) 
{ 
	int		ret; 
	uint32_t	found = 0; 

	found = _get_vdoa_ipu_res(t); 
	if (!found) { 
		t->ipu_id = -1; 
		t->ipu = NULL; 
		/* blocking to get resource */ 
		ret = atomic_inc_return(&req_cnt); 
		dev_dbg(t->dev, 
			"wait_res:no:0x%x,req_cnt:%d\n", t->task_no, ret); 
		ret = wait_event_timeout(res_waitq, _get_vdoa_ipu_res(t), 
				 msecs_to_jiffies(t->timeout - DEF_DELAY_MS)); 
		if (ret == 0) { 
			dev_err(t->dev, "ERR[0x%p,no-0x%x] wait_res timeout:%dms!\n", 
					 t, t->task_no, t->timeout - DEF_DELAY_MS); 
			ret = -ETIMEDOUT; 
			t->state = STATE_RES_TIMEOUT; 
			goto out; 
		} else { 
			if (!(t->set.task & VDOA_ONLY) && (!t->ipu)) 
				dev_err(t->dev, 
					"ERR[no-0x%x] can not get ipu!\n", 
					t->task_no); 
			ret = atomic_read(&req_cnt); 
			if (ret > 0) 
				ret = atomic_dec_return(&req_cnt); 
			else 
				dev_err(t->dev, 
					"ERR[no-0x%x] req_cnt:%d mismatch!\n", 
					t->task_no, ret); 
			dev_dbg(t->dev, "no-0x%x,[0x%p],req_cnt:%d, got_res!\n", 
						t->task_no, t, ret); 
			found = 1; 
		} 
	} 

out: 
	return found; 
}

这个函数就是首先调用_get_vdoa_ipu_res函数来获取资源,如果没有获取到的话,就在等待队列中等待资源,这个函数的返回值是:如果成功获取到资源,返回1,否则返回0.它被get_res_do_task函数调用。


25.create_task_entry函数

static struct ipu_task_entry *create_task_entry(struct ipu_task *task) 
{ 
	struct ipu_task_entry *tsk; 

	tsk = kzalloc(sizeof(struct ipu_task_entry), GFP_KERNEL); 
	if (!tsk) 
		return ERR_PTR(-ENOMEM); 
	kref_init(&tsk->refcount); 
	tsk->state = -EINVAL; 
	tsk->ipu_id = -1; 
	tsk->dev = ipu_dev; 
	tsk->input = task->input; 
	tsk->output = task->output; 
	tsk->overlay_en = task->overlay_en; 
	if (tsk->overlay_en) 
		tsk->overlay = task->overlay; 
	if (task->timeout > DEF_TIMEOUT_MS) 
		tsk->timeout = task->timeout; 
	else 
		tsk->timeout = DEF_TIMEOUT_MS; 

	return tsk; 
}

这个函数就是根据传入的structipu_task *task参数来从新分配设置一个structipu_task_entry*tsk结构体,将里面的值初始化了。它被create_split_child_taskipu_check_taskipu_queue_task函数调用。


26.task_mem_free函数

static void task_mem_free(struct kref *ref) 
{ 
	struct ipu_task_entry *tsk = 
			container_of(ref, struct ipu_task_entry, refcount); 
	kfree(tsk); 
}

根据引用计数,然后调用container_of宏来获取它的容器ipu_task_entry,然后释放掉它的内存。


27.create_split_child_task函数

int create_split_child_task(struct ipu_split_task *sp_task) 
{ 
	int ret = 0; 
	struct ipu_task_entry *tsk; 

	tsk = create_task_entry(&sp_task->task); 
	if (IS_ERR(tsk)) 
		return PTR_ERR(tsk); 

	sp_task->child_task = tsk; 
	tsk->task_no = sp_task->task_no; 

	ret = prepare_task(tsk); 
	if (ret < 0) 
		goto err; 

	tsk->parent = sp_task->parent_task; 
	tsk->set.sp_setting = sp_task->parent_task->set.sp_setting; 

	list_add(&tsk->node, &tsk->parent->split_list); 
	dev_dbg(tsk->dev, "[0x%p] sp_tsk Q list,no-0x%x\n", tsk, tsk->task_no); 
	tsk->state = STATE_QUEUE; 
	CHECK_PERF(&tsk->ts_queue); 
err: 
	return ret; 
}

这个函数还是比较好理解的,首先通过create_task_entry来根据structipu_split_task *sp_task中的task参数分配设置一个ipu_task_entry*tsk结构体,然后分配的这个*tsk结构体作为子task,而函数的实参structipu_split_task*sp_task是父task,然后调用prepare_task函数设置其他的默认值,然后将&tsk->node加入&tsk->parent->split_list链表中,设置tsk->stateSTATE_QUEUE


28.sp_task_check_done函数

static inline int sp_task_check_done(struct ipu_split_task *sp_task, 
			struct ipu_task_entry *parent, int num, int *idx) 
{ 
	int i; 
	int ret = 0; 
	struct ipu_task_entry *tsk; 
	struct mutex *lock = &parent->split_lock; 
 
	*idx = -EINVAL; 
	mutex_lock(lock); 
	for (i = 0; i < num; i++) { 
		tsk = sp_task[i].child_task; 
		if (tsk && tsk->split_done) { 
			*idx = i; 
			ret = 1; 
			goto out; 
		} 
	} 

out: 
	mutex_unlock(lock); 
	return ret; 
}

这个函数从tsk=sp_task[i].child_task;数组中找到已经完成的一项,返回它的下标。它在wait_split_task_complete函数中调用。


29.create_split_task函数

static int create_split_task( 
		int stripe, 
		struct ipu_split_task *sp_task) 
{ 
	struct ipu_task *task = &(sp_task->task); 
	struct ipu_task_entry *t = sp_task->parent_task; 
	int ret; 

	sp_task->task_no |= stripe; 

	task->input = t->input; 
	task->output = t->output; 
	task->overlay_en = t->overlay_en; 
	if (task->overlay_en) 
		task->overlay = t->overlay; 
	task->task_id = t->task_id; 
	if ((t->set.split_mode == RL_SPLIT) || 
		 (t->set.split_mode == UD_SPLIT)) 
		task->timeout = t->timeout / 2; 
	else 
		task->timeout = t->timeout / 4; 

/*设置task中的input,output,overlay,timeout*/

	task->input.crop.w = t->set.sp_setting.iw; 
	task->input.crop.h = t->set.sp_setting.ih; 
	if (task->overlay_en) { 
		task->overlay.crop.w = t->set.sp_setting.ow; 
		task->overlay.crop.h = t->set.sp_setting.oh; 
	} 
	if (t->output.rotate >= IPU_ROTATE_90_RIGHT) { 
		task->output.crop.w = t->set.sp_setting.oh; 
		task->output.crop.h = t->set.sp_setting.ow; 
		t->set.sp_setting.rl_split_line = t->set.sp_setting.o_bottom_pos; 
		t->set.sp_setting.ud_split_line = t->set.sp_setting.o_right_pos; 

	} else { 
		task->output.crop.w = t->set.sp_setting.ow; 
		task->output.crop.h = t->set.sp_setting.oh; 
		t->set.sp_setting.rl_split_line = t->set.sp_setting.o_right_pos; 
		t->set.sp_setting.ud_split_line = t->set.sp_setting.o_bottom_pos; 
	} 

/*设置taskinput,output,overlay里面的crop参数。*/

	if (stripe & LEFT_STRIPE) 
		task->input.crop.pos.x += t->set.sp_setting.i_left_pos; 
	else if (stripe & RIGHT_STRIPE) 
		task->input.crop.pos.x += t->set.sp_setting.i_right_pos; 
	if (stripe & UP_STRIPE) 
		task->input.crop.pos.y += t->set.sp_setting.i_top_pos; 
	else if (stripe & DOWN_STRIPE) 
		task->input.crop.pos.y += t->set.sp_setting.i_bottom_pos; 

	if (task->overlay_en) { 
		if (stripe & LEFT_STRIPE) 
			task->overlay.crop.pos.x += t->set.sp_setting.o_left_pos; 
		else if (stripe & RIGHT_STRIPE) 
			task->overlay.crop.pos.x += t->set.sp_setting.o_right_pos; 
		if (stripe & UP_STRIPE) 
			task->overlay.crop.pos.y += t->set.sp_setting.o_top_pos; 
		else if (stripe & DOWN_STRIPE) 
			task->overlay.crop.pos.y += t->set.sp_setting.o_bottom_pos; 
	} 

	switch (t->output.rotate) { 
	case IPU_ROTATE_NONE: 
		if (stripe & LEFT_STRIPE) 
			task->output.crop.pos.x += t->set.sp_setting.o_left_pos; 
		else if (stripe & RIGHT_STRIPE) 
			task->output.crop.pos.x += t->set.sp_setting.o_right_pos; 
		if (stripe & UP_STRIPE) 
			task->output.crop.pos.y += t->set.sp_setting.o_top_pos; 
		else if (stripe & DOWN_STRIPE) 
			task->output.crop.pos.y += t->set.sp_setting.o_bottom_pos; 
		break; 
	case IPU_ROTATE_VERT_FLIP: 
		if (stripe & LEFT_STRIPE) 
			task->output.crop.pos.x += t->set.sp_setting.o_left_pos; 
		else if (stripe & RIGHT_STRIPE) 
			task->output.crop.pos.x += t->set.sp_setting.o_right_pos; 
		if (stripe & UP_STRIPE) 
			task->output.crop.pos.y = 
					t->output.crop.pos.y + t->output.crop.h 
					- t->set.sp_setting.o_top_pos - t->set.sp_setting.oh; 
		else if (stripe & DOWN_STRIPE) 
			task->output.crop.pos.y = 
					t->output.crop.pos.y + t->output.crop.h 
					- t->set.sp_setting.o_bottom_pos - t->set.sp_setting.oh; 
		break; 
	case IPU_ROTATE_HORIZ_FLIP: 
		if (stripe & LEFT_STRIPE) 
			task->output.crop.pos.x = 
					t->output.crop.pos.x + t->output.crop.w 
					- t->set.sp_setting.o_left_pos - t->set.sp_setting.ow; 
		else if (stripe & RIGHT_STRIPE) 
			task->output.crop.pos.x = 
					t->output.crop.pos.x + t->output.crop.w 
					- t->set.sp_setting.o_right_pos - t->set.sp_setting.ow; 
		if (stripe & UP_STRIPE) 
			task->output.crop.pos.y += t->set.sp_setting.o_top_pos; 
		else if (stripe & DOWN_STRIPE) 
			task->output.crop.pos.y += t->set.sp_setting.o_bottom_pos; 
		break; 
	case IPU_ROTATE_180: 
		if (stripe & LEFT_STRIPE) 
			task->output.crop.pos.x = 
					t->output.crop.pos.x + t->output.crop.w 
					- t->set.sp_setting.o_left_pos - t->set.sp_setting.ow; 
		else if (stripe & RIGHT_STRIPE) 
			task->output.crop.pos.x = 
					t->output.crop.pos.x + t->output.crop.w 
					- t->set.sp_setting.o_right_pos - t->set.sp_setting.ow; 
		if (stripe & UP_STRIPE) 
			task->output.crop.pos.y = 
					t->output.crop.pos.y + t->output.crop.h 
					- t->set.sp_setting.o_top_pos - t->set.sp_setting.oh; 
		else if (stripe & DOWN_STRIPE) 
			task->output.crop.pos.y = 
					t->output.crop.pos.y + t->output.crop.h 
					- t->set.sp_setting.o_bottom_pos - t->set.sp_setting.oh; 
		break; 
	case IPU_ROTATE_90_RIGHT: 
		if (stripe & UP_STRIPE) 
			task->output.crop.pos.x = 
					t->output.crop.pos.x + t->output.crop.w 
					- t->set.sp_setting.o_top_pos - t->set.sp_setting.oh; 
		else if (stripe & DOWN_STRIPE) 
			task->output.crop.pos.x = 
					t->output.crop.pos.x + t->output.crop.w 
					- t->set.sp_setting.o_bottom_pos - t->set.sp_setting.oh; 
		if (stripe & LEFT_STRIPE) 
			task->output.crop.pos.y += t->set.sp_setting.o_left_pos; 
		else if (stripe & RIGHT_STRIPE) 
			task->output.crop.pos.y += t->set.sp_setting.o_right_pos; 
		break; 
	case IPU_ROTATE_90_RIGHT_HFLIP: 
		if (stripe & UP_STRIPE) 
			task->output.crop.pos.x += t->set.sp_setting.o_top_pos; 
		else if (stripe & DOWN_STRIPE) 
			task->output.crop.pos.x += t->set.sp_setting.o_bottom_pos; 
		if (stripe & LEFT_STRIPE) 
			task->output.crop.pos.y += t->set.sp_setting.o_left_pos; 
		else if (stripe & RIGHT_STRIPE) 
			task->output.crop.pos.y += t->set.sp_setting.o_right_pos; 
		break; 
	case IPU_ROTATE_90_RIGHT_VFLIP: 
		if (stripe & UP_STRIPE) 
			task->output.crop.pos.x = 
					t->output.crop.pos.x + t->output.crop.w 
					- t->set.sp_setting.o_top_pos - t->set.sp_setting.oh; 
		else if (stripe & DOWN_STRIPE) 
			task->output.crop.pos.x = 
					t->output.crop.pos.x + t->output.crop.w 
					- t->set.sp_setting.o_bottom_pos - t->set.sp_setting.oh; 
		if (stripe & LEFT_STRIPE) 
			task->output.crop.pos.y = 
					t->output.crop.pos.y + t->output.crop.h 
					- t->set.sp_setting.o_left_pos - t->set.sp_setting.ow; 
		else if (stripe & RIGHT_STRIPE) 
			task->output.crop.pos.y = 
					t->output.crop.pos.y + t->output.crop.h 
					- t->set.sp_setting.o_right_pos - t->set.sp_setting.ow; 
		break; 
	case IPU_ROTATE_90_LEFT: 
		if (stripe & UP_STRIPE) 
			task->output.crop.pos.x += t->set.sp_setting.o_top_pos; 
		else if (stripe & DOWN_STRIPE) 
			task->output.crop.pos.x += t->set.sp_setting.o_bottom_pos; 
		if (stripe & LEFT_STRIPE) 
			task->output.crop.pos.y = 
					t->output.crop.pos.y + t->output.crop.h 
					- t->set.sp_setting.o_left_pos - t->set.sp_setting.ow; 
		else if (stripe & RIGHT_STRIPE) 
			task->output.crop.pos.y = 
					t->output.crop.pos.y + t->output.crop.h 
					- t->set.sp_setting.o_right_pos - t->set.sp_setting.ow; 
		break; 
	default: 
		dev_err(t->dev, "ERR:should not be here\n"); 
		break; 
	} 

/*上面这个switch语句是根据t->output.rotate的值来设置task里面outputcrop值。*/

	ret = create_split_child_task(sp_task); 
	if (ret < 0) 
		dev_err(t->dev, "ERR:create_split_child_task() ret:%d\n", ret); 

/*上面设置好了sp_task中的task参数,就根据它来创建子task*/

	return ret; 
}

先来看这个structipu_split_task结构体:

struct ipu_split_task { 
	struct ipu_task task; 
	struct ipu_task_entry *parent_task; 
	struct ipu_task_entry *child_task; 
	u32 task_no; 
};

从这个函数的的开头:

structipu_task *task = &(sp_task->task);

structipu_task_entry *t = sp_task->parent_task;

而这个函数的主要操作就是设置这个task结构体。从这里就可以看出来,函数的目的就是根据传入的第二个structipu_split_task *sp_task参数里面的parent_task成员来填充第二个参数里面的task成员。设置完以后,在最后调用create_split_child_task函数(27.中讲解),根据sp_task->task来设置子task


30.queue_split_task函数

static int queue_split_task(struct ipu_task_entry *t, 
				struct ipu_split_task *sp_task, uint32_t size) 
{ 
	int err[4]; 
	int ret = 0; 
	int i, j; 
	struct ipu_task_entry *tsk = NULL; 
	struct mutex *lock = &t->split_lock; 
	struct mutex *vdic_lock = &t->vdic_lock; 

	dev_dbg(t->dev, "Split task 0x%p, no-0x%x, size:%d\n", 
			 t, t->task_no, size); 
	mutex_init(lock); 
	mutex_init(vdic_lock); 
	init_waitqueue_head(&t->split_waitq); 
	INIT_LIST_HEAD(&t->split_list); 

/*初始化锁,等待队列,链表头等。*/

	for (j = 0; j < size; j++) { 
		memset(&sp_task[j], 0, sizeof(*sp_task)); 
		sp_task[j].parent_task = t; 
		sp_task[j].task_no = t->task_no; 
	} 

/*&sp_task[j]中的每一项都置为0,然后将每一项的parent_task都指向函数的第一个参数t*/

	if (t->set.split_mode == RL_SPLIT) { 
		i = 0; 
		err[i] = create_split_task(RIGHT_STRIPE, &sp_task[i]); 
		if (err[i] < 0) 
			goto err_start; 
		i = 1; 
		err[i] = create_split_task(LEFT_STRIPE, &sp_task[i]); 
	} else if (t->set.split_mode == UD_SPLIT) { 
		i = 0; 
		err[i] = create_split_task(DOWN_STRIPE, &sp_task[i]); 
		if (err[i] < 0) 
			goto err_start; 
		i = 1; 
		err[i] = create_split_task(UP_STRIPE, &sp_task[i]); 
	} else { 
		i = 0; 
		err[i] = create_split_task(RIGHT_STRIPE | DOWN_STRIPE, &sp_task[i]); 
		if (err[i] < 0) 
			goto err_start; 
		i = 1; 
		err[i] = create_split_task(LEFT_STRIPE | DOWN_STRIPE, &sp_task[i]); 
		if (err[i] < 0) 
			goto err_start; 
		i = 2; 
		err[i] = create_split_task(RIGHT_STRIPE | UP_STRIPE, &sp_task[i]); 
		if (err[i] < 0) 
			goto err_start; 
		i = 3; 
		err[i] = create_split_task(LEFT_STRIPE | UP_STRIPE, &sp_task[i]); 
	} 

/*以上根据t->set.split_mode来选择创建什么样的子task。以RL_SPLIT为例,它分别创建了sp_task[0]stripeRIGHT_STRIPE的子tasksp_task[1]stripeLEFT_STRIPE的子task。如果tsk->set.split_mode明确表示等于RL_SPLIT或者UD_SPLIT的话,函数的第三个参数size就等于2,表示将创建2个子task,保存在sp_task数组中;除此之外的其他情况下将会创建4个子task,为什么呢?因为这个tsk->set.split_mode除了明确指明等于RL_SPLIT或者UD_SPLIT的话,第三种情况基本就能确定是RL_SPLIT|UD_SPLIT*/

err_start: 
	for (j = 0; j < (i + 1); j++) { 
		if (err[j] < 0) { 
			if (sp_task[j].child_task) 
				dev_err(t->dev, 
				 "sp_task[%d],no-0x%x fail state:%d, queue err:%d.\n", 
				j, sp_task[j].child_task->task_no, 
				sp_task[j].child_task->state, err[j]); 
			goto err_exit; 
		} 
		dev_dbg(t->dev, "[0x%p] sp_task[%d], no-0x%x state:%s, queue ret:%d.\n", 
			sp_task[j].child_task, j, sp_task[j].child_task->task_no, 
			state_msg[sp_task[j].child_task->state].msg, err[j]); 
	} 

	return ret; 

err_exit: 
	for (j = 0; j < (i + 1); j++) { 
		if (err[j] < 0 && !ret) 
			ret = err[j]; 
		tsk = sp_task[j].child_task; 
		if (!tsk) 
			continue; 
		kfree(tsk); 
	} 
	t->state = STATE_ERR; 
	return ret; 
}

这个函数直接在ipu_task_thread被调用。


31.init_tiled_buf函数

static int init_tiled_buf(struct ipu_soc *ipu, struct ipu_task_entry *t, 
				ipu_channel_t channel, uint32_t ch_type) 
{ 
	int ret = 0; 
	int i; 
	uint32_t ipu_fmt; 
	dma_addr_t inbuf_base = 0; 
	u32 field_size; 
	struct vdoa_params param; 
	struct vdoa_ipu_buf buf; 
	struct ipu_soc *ipu_idx; 
	u32 ipu_stride, obuf_size; 
	u32 height, width; 
	ipu_buffer_t type; 

	if ((IPU_PIX_FMT_YUYV != t->output.format) && 
		(IPU_PIX_FMT_NV12 != t->output.format)) { 
		dev_err(t->dev, "ERR:[0x%d] output format\n", t->task_no); 
		return -EINVAL; 
	} 

/*如果t->output.format的格式不是IPU_PIX_FMT_YUYVIPU_PIX_FMT_NV12的话就报错。*/

	memset(¶m, 0, sizeof(param)); 
	/* init channel tiled bufs */ 
	if (deinterlace_3_field(t) && 
		(IPU_PIX_FMT_TILED_NV12F == t->input.format)) { 
		field_size = tiled_filed_size(t); 
		if (INPUT_CHAN_VDI_P == ch_type) { 
			inbuf_base = t->input.paddr + field_size; 
			param.vfield_buf.prev_veba = inbuf_base + t->set.i_off; 
		} else if (INPUT_CHAN == ch_type) { 
			inbuf_base = t->input.paddr_n; 
			param.vfield_buf.cur_veba = inbuf_base + t->set.i_off; 
		} else if (INPUT_CHAN_VDI_N == ch_type) { 
			inbuf_base = t->input.paddr_n + field_size; 
			param.vfield_buf.next_veba = inbuf_base + t->set.i_off; 
		} else 
			return -EINVAL; 
		height = t->input.crop.h >> 1; /* field format for vdoa */ 
		width = t->input.crop.w; 
		param.vfield_buf.vubo = t->set.i_uoff; 
		param.interlaced = 1; 
		param.scan_order = 1; 
		type = IPU_INPUT_BUFFER; 
	} 

/*如果IPU_PIX_FMT_TILED_NV12F==t->input.format的话就会执行上面的语句。首先根据函数的第四个参数ch_type来确定inbuf_baseparam.vfield_buf.cur_veba的值。设置heightwidth,typeparam的值。*/

else if ((IPU_PIX_FMT_TILED_NV12 == t->input.format) && 
			(INPUT_CHAN == ch_type)) { 
		height = t->input.crop.h; 
		width = t->input.crop.w; 
		param.vframe_buf.veba = t->input.paddr + t->set.i_off; 
		param.vframe_buf.vubo = t->set.i_uoff; 
		type = IPU_INPUT_BUFFER; 
	} else 
		return -EINVAL; 

/*如果IPU_PIX_FMT_TILED_NV12== t->input.format的话就会执行这段代码。设置heightwidth,typeparam的值。*/

	param.band_mode = (t->set.mode & VDOA_BAND_MODE) ? 1 : 0; 
	if (param.band_mode && (t->set.band_lines != 3) && 
		 (t->set.band_lines != 4) && (t->set.band_lines != 5)) 
		return -EINVAL; 
	else if (param.band_mode) 
		param.band_lines = (1 << t->set.band_lines); 
	for (i = 0; i < max_ipu_no; i++) { 
		ipu_idx = ipu_get_soc(i); 
		if (!IS_ERR(ipu_idx) && ipu_idx == ipu) 
			break; 
	} 
	if (t->set.task & VDOA_ONLY) 
		/* dummy, didn't need ipu res */ 
		i = 0; 
	if (max_ipu_no == i) { 
		dev_err(t->dev, "ERR:[0x%p] get ipu num\n", t); 
		return -EINVAL; 
	} 

	param.ipu_num = i; 
	param.vpu_stride = t->input.width; 
	param.height = height; 
	param.width = width; 
	if (IPU_PIX_FMT_NV12 == t->output.format) 
		param.pfs = VDOA_PFS_NV12; 
	else 
		param.pfs = VDOA_PFS_YUYV; 
	ipu_fmt = (param.pfs == VDOA_PFS_YUYV) ? IPU_PIX_FMT_YUYV : 
				IPU_PIX_FMT_NV12; 
	ipu_stride = param.width * bytes_per_pixel(ipu_fmt); 
	obuf_size = PAGE_ALIGN(param.width * param.height * 
				fmt_to_bpp(ipu_fmt)/8); 
	dev_dbg(t->dev, "band_mode:%d, band_lines:%d\n", 
			param.band_mode, param.band_lines); 
	if (!param.band_mode) { 
		/* note: if only for tiled -> raster convert and 
		   no other post-processing, we don't need alloc buf 
		   and use output buffer directly. 
		*/ 
		if (t->set.task & VDOA_ONLY) 
			param.ieba0 = t->output.paddr; 
		else { 
			dev_err(t->dev, "ERR:[0x%d] vdoa task\n", t->task_no); 
			return -EINVAL; 
		} 
	} else { 
		if (IPU_PIX_FMT_TILED_NV12F != t->input.format) { 
			dev_err(t->dev, "ERR [0x%d] vdoa task\n", t->task_no); 
			return -EINVAL; 
		} 
	} 

/*上面这些设置主要是设置param的值,最终目的是调用下面的vdoa_setup函数。*/

	ret = vdoa_setup(t->vdoa_handle, ¶m); 
	if (ret) 
		goto done; 
	vdoa_get_output_buf(t->vdoa_handle, &buf); 
	if (t->set.task & VDOA_ONLY) 
		goto done; 

/*通过上面两个函数分别来设置vdoa和将vdoa中的数据读到buf中,然后为下面这个函数做准备。这两个函数在vdoa.c中,暂时没有分析。*/

	ret = ipu_init_channel_buffer(ipu, 
			channel, 
			type, 
			ipu_fmt, 
			width, 
			height, 
			ipu_stride, 
			IPU_ROTATE_NONE, 
			buf.ieba0, 
			buf.ieba1, 
			0, 
			buf.iubo, 
			0); 
	if (ret < 0) { 
		t->state = STATE_INIT_CHAN_BUF_FAIL; 
		goto done; 
	} 

/*然后调用ipu_init_channel_buffer函数来初始化channelbuffer。这个函数在ipu_common.c中。*/

	if (param.band_mode) { 
		ret = ipu_set_channel_bandmode(ipu, channel, 
				type, t->set.band_lines); 
		if (ret < 0) { 
			t->state = STATE_INIT_CHAN_BAND_FAIL; 
			goto done; 
		} 
	} 

/*如果MODEVDOA_BAND_MODE的话,就会执行这个函数。*/

done: 
	return ret; 
}


32.init_tiled_ch_bufs函数

static int init_tiled_ch_bufs(struct ipu_soc *ipu, struct ipu_task_entry *t) 
{ 
	int ret = 0; 

	if (IPU_PIX_FMT_TILED_NV12 == t->input.format) { 
		ret = init_tiled_buf(ipu, t, t->set.ic_chan, INPUT_CHAN); 
		CHECK_RETCODE(ret < 0, "init tiled_ch", t->state, done, ret); 
	} 

/*如果t->input.format== IPU_PIX_FMT_TILED_NV12的话,就根据t->set.ic_chanINPUT_CHAN初始化一个channelbuffer*/

else if (IPU_PIX_FMT_TILED_NV12F == t->input.format) { 
		ret = init_tiled_buf(ipu, t, t->set.ic_chan, INPUT_CHAN); 
		CHECK_RETCODE(ret < 0, "init tiled_ch-c", t->state, done, ret); 
		ret = init_tiled_buf(ipu, t, t->set.vdi_ic_p_chan, 
					INPUT_CHAN_VDI_P); 
		CHECK_RETCODE(ret < 0, "init tiled_ch-p", t->state, done, ret); 
		ret = init_tiled_buf(ipu, t, t->set.vdi_ic_n_chan, 
					INPUT_CHAN_VDI_N); 
		CHECK_RETCODE(ret < 0, "init tiled_ch-n", t->state, done, ret); 
	} 

/*如果t->input.format==IPU_PIX_FMT_TILED_NV12F的话,需要初始化t->set.ic_chant->set.vdi_ic_p_chant->set.vdi_ic_n_chan三个channelbuffer。看来这个t->input.format很重要啊~~~*/

else { 
		ret = -EINVAL; 
		dev_err(t->dev, "ERR[no-0x%x] invalid fmt:0x%x!\n", 
			t->task_no, t->input.format); 
	} 

done: 
	return ret; 
}

这个函数是初始化channel的平铺buffer的,所以在init_icdo_task_vdoa_only函数中调用它。


33.init_ic函数

static int init_ic(struct ipu_soc *ipu, struct ipu_task_entry *t) 
{ 
	int ret = 0; 
	ipu_channel_params_t params; 
	dma_addr_t inbuf = 0, ovbuf = 0, ov_alp_buf = 0; 
	dma_addr_t inbuf_p = 0, inbuf_n = 0; 
	dma_addr_t outbuf = 0; 
	int out_uoff = 0, out_voff = 0, out_rot; 
	int out_w = 0, out_h = 0, out_stride; 
	int out_fmt; 
	u32 vdi_frame_idx = 0; 

	memset(¶ms, 0, sizeof(params)); 

	/* is it need link a rot channel */ 
	if (ic_and_rot(t->set.mode)) { 
		outbuf = t->set.r_paddr; 
		out_w = t->set.r_width; 
		out_h = t->set.r_height; 
		out_stride = t->set.r_stride; 
		out_fmt = t->set.r_fmt; 
		out_uoff = 0; 
		out_voff = 0; 
		out_rot = IPU_ROTATE_NONE; 
	} else { 
		outbuf = t->output.paddr + t->set.o_off; 
		out_w = t->output.crop.w; 
		out_h = t->output.crop.h; 
		out_stride = t->set.ostride; 
		out_fmt = t->output.format; 
		out_uoff = t->set.o_uoff; 
		out_voff = t->set.o_voff; 
		out_rot = t->output.rotate; 
	} 

/*通过ic_and_rot函数来判断是否需要链接上一个rotchannel,如果需要的话,就将outbufout_wout_h等等值都赋为t->set.r_xxxx的值,不需要的话就赋为t->set.o_xxxx的值。*/

	/* settings */ 
	params.mem_prp_vf_mem.in_width = t->input.crop.w; 
	params.mem_prp_vf_mem.out_width = out_w; 
	params.mem_prp_vf_mem.in_height = t->input.crop.h; 
	params.mem_prp_vf_mem.out_height = out_h; 
	params.mem_prp_vf_mem.in_pixel_fmt = t->input.format; 
	params.mem_prp_vf_mem.out_pixel_fmt = out_fmt; 
	params.mem_prp_vf_mem.motion_sel = t->input.deinterlace.motion; 

	params.mem_prp_vf_mem.outh_resize_ratio = 
			t->set.sp_setting.outh_resize_ratio; 
	params.mem_prp_vf_mem.outv_resize_ratio = 
			t->set.sp_setting.outv_resize_ratio; 

	if (t->overlay_en) { 
		params.mem_prp_vf_mem.in_g_pixel_fmt = t->overlay.format; 
		params.mem_prp_vf_mem.graphics_combine_en = 1; 
		if (t->overlay.alpha.mode == IPU_ALPHA_MODE_GLOBAL) 
			params.mem_prp_vf_mem.global_alpha_en = 1; 
		else if (t->overlay.alpha.loc_alp_paddr) 
			params.mem_prp_vf_mem.alpha_chan_en = 1; 
		/* otherwise, alpha bending per pixel is used. */ 
		params.mem_prp_vf_mem.alpha = t->overlay.alpha.gvalue; 
		if (t->overlay.colorkey.enable) { 
			params.mem_prp_vf_mem.key_color_en = 1; 
			params.mem_prp_vf_mem.key_color = t->overlay.colorkey.value; 
		} 
	} 

	if (t->input.deinterlace.enable) { 
		if (t->input.deinterlace.field_fmt & IPU_DEINTERLACE_FIELD_MASK) 
			params.mem_prp_vf_mem.field_fmt = 
				IPU_DEINTERLACE_FIELD_BOTTOM; 
		else 
			params.mem_prp_vf_mem.field_fmt = 
				IPU_DEINTERLACE_FIELD_TOP; 

		if (t->input.deinterlace.field_fmt & IPU_DEINTERLACE_RATE_EN) 
			vdi_frame_idx = t->input.deinterlace.field_fmt & 
						IPU_DEINTERLACE_RATE_FRAME1; 
	} 

/*以上的代码就是设置ipu_channel_params_tparams的各个值,然后这个params变量将要被ipu_init_channel函数所使用来初始化channel*/

	if (t->set.mode & VDOA_MODE) 
		ipu->vdoa_en = 1; 

	/* init channels */ 
	if (!(t->set.task & VDOA_ONLY)) { 
		ret = ipu_init_channel(ipu, t->set.ic_chan, ¶ms); 
		if (ret < 0) { 
			t->state = STATE_INIT_CHAN_FAIL; 
			goto done; 
		} 
	} 

/*如果t->set.task不是VDOA_ONLY的话,就根据参数初始化t->set.ic_chan这个channel。为什么不能是VDOA_ONLY呢?因为在get_res_do_task函数中分流了,如果是VDOA_ONLY的话就会调用do_task_vdoa_only函数。*/

	if (deinterlace_3_field(t)) { 
		ret = ipu_init_channel(ipu, t->set.vdi_ic_p_chan, ¶ms); 
		if (ret < 0) { 
			t->state = STATE_INIT_CHAN_FAIL; 
			goto done; 
		} 
		ret = ipu_init_channel(ipu, t->set.vdi_ic_n_chan, ¶ms); 
		if (ret < 0) { 
			t->state = STATE_INIT_CHAN_FAIL; 
			goto done; 
		} 
	} 

/*同时还需要初始化t->set.vdi_ic_p_chant->set.vdi_ic_n_chan这两个channel*/

	/* init channel bufs */ 
	if ((IPU_PIX_FMT_TILED_NV12 == t->input.format) || 
		(IPU_PIX_FMT_TILED_NV12F == t->input.format)) { 
		ret = init_tiled_ch_bufs(ipu, t); 
		if (ret < 0) 
			goto done; 
	} 

/*如果t->input.formatIPU_PIX_FMT_TILED_NV12IPU_PIX_FMT_TILED_NV12F的话,就调用init_tiled_ch_bufs函数即可。这时候就有点理解了,对于输入的这两种格式,它们不能用普通的buffer来存储,所以这的buffer叫做tiledbuffer,而对于一般的输入格式,就直接调用下面的ipu_init_channel_buffer函数来初始化。看这两个格式的名字里面也带有TILED*/

else { 
		if ((deinterlace_3_field(t)) && 
			(IPU_PIX_FMT_TILED_NV12F != t->input.format)) { 
			if (params.mem_prp_vf_mem.field_fmt == 
				IPU_DEINTERLACE_FIELD_TOP) { 
				if (vdi_frame_idx) { 
					inbuf_p = t->input.paddr + t->set.istride + 
							t->set.i_off; 
					inbuf = t->input.paddr_n + t->set.i_off; 
					inbuf_n = t->input.paddr_n + t->set.istride + 
							t->set.i_off; 
					params.mem_prp_vf_mem.field_fmt = 
						IPU_DEINTERLACE_FIELD_BOTTOM; 
				} else { 
					inbuf_p = t->input.paddr + t->set.i_off; 
					inbuf = t->input.paddr + t->set.istride + t->set.i_off; 
					inbuf_n = t->input.paddr_n + t->set.i_off; 
				} 
			} else { 
				if (vdi_frame_idx) { 
					inbuf_p = t->input.paddr + t->set.i_off; 
					inbuf = t->input.paddr_n + t->set.istride + t->set.i_off; 
					inbuf_n = t->input.paddr_n + t->set.i_off; 
					params.mem_prp_vf_mem.field_fmt = 
						IPU_DEINTERLACE_FIELD_TOP; 
				} else { 
					inbuf_p = t->input.paddr + t->set.istride + 
							t->set.i_off; 
					inbuf = t->input.paddr + t->set.i_off; 
					inbuf_n = t->input.paddr_n + t->set.istride + 
							t->set.i_off; 
				} 
			} 
		} else { 
			if (t->input.deinterlace.enable) { 
				if (params.mem_prp_vf_mem.field_fmt == 
					IPU_DEINTERLACE_FIELD_TOP) { 
					if (vdi_frame_idx) { 
						inbuf = t->input.paddr + t->set.istride + t->set.i_off; 
						params.mem_prp_vf_mem.field_fmt = 
							IPU_DEINTERLACE_FIELD_BOTTOM; 
					} else 
						inbuf = t->input.paddr + t->set.i_off; 
				} else { 
					if (vdi_frame_idx) { 
						inbuf = t->input.paddr + t->set.i_off; 
						params.mem_prp_vf_mem.field_fmt = 
							IPU_DEINTERLACE_FIELD_TOP; 
					} else 
						inbuf = t->input.paddr + t->set.istride + t->set.i_off; 
				} 
			} else 
				inbuf = t->input.paddr + t->set.i_off; 
		} 

/*上面就是设置inbuf_pinbufinbuf_n的值。这几个值分别用在不同的ipu_init_channel_buffer函数里面,具体有什么不同以后再分析。*/

		if (t->overlay_en) 
			ovbuf = t->overlay.paddr + t->set.ov_off; 

/*如果使能了overlay_en的话,设置ovbuf*/

	} 
	if (t->overlay_en && (t->overlay.alpha.mode == IPU_ALPHA_MODE_LOCAL)) 
		ov_alp_buf = t->overlay.alpha.loc_alp_paddr 
														+ t->set.ov_alpha_off; 

/*如果使能overlay_en同时设置了alpha模式的话,设置ov_alp_buf*/

	if ((IPU_PIX_FMT_TILED_NV12 != t->input.format) && 
		(IPU_PIX_FMT_TILED_NV12F != t->input.format)) { 
		ret = ipu_init_channel_buffer(ipu, 
				t->set.ic_chan, 
				IPU_INPUT_BUFFER, 
				t->input.format, 
				t->input.crop.w, 
				t->input.crop.h, 
				t->set.istride, 
				IPU_ROTATE_NONE, 
				inbuf, 
				0, 
				0, 
				t->set.i_uoff, 
				t->set.i_voff); 
		if (ret < 0) { 
			t->state = STATE_INIT_CHAN_BUF_FAIL; 
			goto done; 
		} 
	} 

/*上面通过ipu_init_channel函数初始化了3channel,所以需要初始化3个对应的buffer。这里初始化的是t->set.ic_chanchannelbuffer*/

	if (deinterlace_3_field(t) && 
		(IPU_PIX_FMT_TILED_NV12F != t->input.format)) { 
		ret = ipu_init_channel_buffer(ipu, 
				t->set.vdi_ic_p_chan, 
				IPU_INPUT_BUFFER, 
				t->input.format, 
				t->input.crop.w, 
				t->input.crop.h, 
				t->set.istride, 
				IPU_ROTATE_NONE, 
				inbuf_p, 
				0, 
				0, 
				t->set.i_uoff, 
				t->set.i_voff); 
		if (ret < 0) { 
			t->state = STATE_INIT_CHAN_BUF_FAIL; 
			goto done; 
		} 

/*这里初始化的是t->set.vdi_ic_p_chanchannel的buffer。*/

		ret = ipu_init_channel_buffer(ipu, 
				t->set.vdi_ic_n_chan, 
				IPU_INPUT_BUFFER, 
				t->input.format, 
				t->input.crop.w, 
				t->input.crop.h, 
				t->set.istride, 
				IPU_ROTATE_NONE, 
				inbuf_n, 
				0, 
				0, 
				t->set.i_uoff, 
				t->set.i_voff); 
		if (ret < 0) { 
			t->state = STATE_INIT_CHAN_BUF_FAIL; 
			goto done; 
		} 
	} 

/*这里初始化的是t->set.vdi_ic_n_chanchannel的buffer。*/

	if (t->overlay_en) { 
		ret = ipu_init_channel_buffer(ipu, 
				t->set.ic_chan, 
				IPU_GRAPH_IN_BUFFER, 
				t->overlay.format, 
				t->overlay.crop.w, 
				t->overlay.crop.h, 
				t->set.ovstride, 
				IPU_ROTATE_NONE, 
				ovbuf, 
				0, 
				0, 
				t->set.ov_uoff, 
				t->set.ov_voff); 
		if (ret < 0) { 
			t->state = STATE_INIT_CHAN_BUF_FAIL; 
			goto done; 
		} 
	} 

/*如果使能了overlay_en的话,同样需要为它初始化channelbuffer*/

	if (t->overlay.alpha.mode == IPU_ALPHA_MODE_LOCAL) { 
		ret = ipu_init_channel_buffer(ipu, 
				t->set.ic_chan, 
				IPU_ALPHA_IN_BUFFER, 
				IPU_PIX_FMT_GENERIC, 
				t->overlay.crop.w, 
				t->overlay.crop.h, 
				t->set.ov_alpha_stride, 
				IPU_ROTATE_NONE, 
				ov_alp_buf, 
				0, 
				0, 
				0, 0); 
		if (ret < 0) { 
			t->state = STATE_INIT_CHAN_BUF_FAIL; 
			goto done; 
		} 
	} 


/*如果t->overlay.alpha.mode== IPU_ALPHA_MODE_LOCAL 的话,需要为它初始化channelbuffer。*/

	if (!(t->set.task & VDOA_ONLY)) { 
		ret = ipu_init_channel_buffer(ipu, 
				t->set.ic_chan, 
				IPU_OUTPUT_BUFFER, 
				out_fmt, 
				out_w, 
				out_h, 
				out_stride, 
				out_rot, 
				outbuf, 
				0, 
				0, 
				out_uoff, 
				out_voff); 
		if (ret < 0) { 
			t->state = STATE_INIT_CHAN_BUF_FAIL; 
			goto done; 
		} 
	} 

/*初始化这个channelbuffer是为什么。。。。不理解*/

	if ((t->set.mode & VDOA_BAND_MODE) && (t->set.task & VDI_VF)) { 
		ret = ipu_link_channels(ipu, MEM_VDOA_MEM, t->set.ic_chan); 
		CHECK_RETCODE(ret < 0, "ipu_link_ch vdoa_ic", 
				STATE_LINK_CHAN_FAIL, done, ret); 
	} 

/*链接两个channel。什么情况下需要链接channel??是因为它们都是t->set.ic_chanchannel吗???这个函数在ipu_common.c中。*/

done: 
	return ret; 
}


34.uninit_ic函数

static void uninit_ic(struct ipu_soc *ipu, struct ipu_task_entry *t) 
{ 
	int ret; 

	if ((t->set.mode & VDOA_BAND_MODE) && (t->set.task & VDI_VF)) { 
		ret = ipu_unlink_channels(ipu, MEM_VDOA_MEM, t->set.ic_chan); 
		CHECK_RETCODE_CONT(ret < 0, "ipu_unlink_ch vdoa_ic", 
				STATE_UNLINK_CHAN_FAIL, ret); 
	} 
	ipu_uninit_channel(ipu, t->set.ic_chan); 
	if (deinterlace_3_field(t)) { 
		ipu_uninit_channel(ipu, t->set.vdi_ic_p_chan); 
		ipu_uninit_channel(ipu, t->set.vdi_ic_n_chan); 
	} 
}

这个函数是上一个函数的反函数,就不分析了。


35.init_rot函数

static int init_rot(struct ipu_soc *ipu, struct ipu_task_entry *t) 
{ 
	int ret = 0; 
	dma_addr_t inbuf = 0, outbuf = 0; 
	int in_uoff = 0, in_voff = 0; 
	int in_fmt, in_width, in_height, in_stride; 

	/* init channel */ 
	ret = ipu_init_channel(ipu, t->set.rot_chan, NULL); 
	if (ret < 0) { 
		t->state = STATE_INIT_CHAN_FAIL; 
		goto done; 
	} 

	/* init channel buf */ 
	/* is it need link to a ic channel */ 
	if (ic_and_rot(t->set.mode)) { 
		in_fmt = t->set.r_fmt; 
		in_width = t->set.r_width; 
		in_height = t->set.r_height; 
		in_stride = t->set.r_stride; 
		inbuf = t->set.r_paddr; 
		in_uoff = 0; 
		in_voff = 0; 
	} else { 
		in_fmt = t->input.format; 
		in_width = t->input.crop.w; 
		in_height = t->input.crop.h; 
		in_stride = t->set.istride; 
		inbuf = t->input.paddr + t->set.i_off; 
		in_uoff = t->set.i_uoff; 
		in_voff = t->set.i_voff; 
	} 
	outbuf = t->output.paddr + t->set.o_off; 

/*设置一些值用于ipu_init_channel_buffer函数。*/

	ret = ipu_init_channel_buffer(ipu, 
			t->set.rot_chan, 
			IPU_INPUT_BUFFER, 
			in_fmt, 
			in_width, 
			in_height, 
			in_stride, 
			t->output.rotate, 
			inbuf, 
			0, 
			0, 
			in_uoff, 
			in_voff); 
	if (ret < 0) { 
		t->state = STATE_INIT_CHAN_BUF_FAIL; 
		goto done; 
	} 

	ret = ipu_init_channel_buffer(ipu, 
			t->set.rot_chan, 
			IPU_OUTPUT_BUFFER, 
			t->output.format, 
			t->output.crop.w, 
			t->output.crop.h, 
			t->set.ostride, 
			IPU_ROTATE_NONE, 
			outbuf, 
			0, 
			0, 
			t->set.o_uoff, 
			t->set.o_voff); 
	if (ret < 0) { 
		t->state = STATE_INIT_CHAN_BUF_FAIL; 
		goto done; 
	} 

done: 
	return ret; 
}

这个函数初始化了两个channelbuffer,注释中有一句话:isit need link to a icchannel。但是第二个ipu_init_channel_buffer函数的第三个参数是IPU_OUTPUT_BUFFER,同样在init_ic函数中,它同样多初始化了一个channelbuffer,第三个参数同样是IPU_OUTPUT_BUFFER。所以是不是channel中必须包含输入输出呢?

这个函数被do_task函数所调用。


36.uninit_rot函数

static void uninit_rot(struct ipu_soc *ipu, struct ipu_task_entry *t) 
{ 
	ipu_uninit_channel(ipu, t->set.rot_chan); 
}

上一个函数的反函数,不分析了。


37.get_irq函数

static int get_irq(struct ipu_task_entry *t) 
{ 
	int irq; 
	ipu_channel_t chan; 

	if (only_ic(t->set.mode)) 
		chan = t->set.ic_chan; 
	else 
		chan = t->set.rot_chan; 

	switch (chan) { 
	case MEM_ROT_VF_MEM: 
		irq = IPU_IRQ_PRP_VF_ROT_OUT_EOF; 
		break; 
	case MEM_ROT_PP_MEM: 
		irq = IPU_IRQ_PP_ROT_OUT_EOF; 
		break; 
	case MEM_VDI_PRP_VF_MEM: 
	case MEM_PRP_VF_MEM: 
		irq = IPU_IRQ_PRP_VF_OUT_EOF; 
		break; 
	case MEM_PP_MEM: 
		irq = IPU_IRQ_PP_OUT_EOF; 
		break; 
	case MEM_VDI_MEM: 
		irq = IPU_IRQ_VDIC_OUT_EOF; 
		break; 
	default: 
		irq = -EINVAL; 
	} 

	return irq; 
}

这个函数根据ipu_task_entry->setchannel来确定对应channel的中断号。


38.task_irq_handler函数

static irqreturn_t task_irq_handler(int irq, void *dev_id) 
{ 
	struct ipu_task_entry *prev_tsk = dev_id; 

	CHECK_PERF(&prev_tsk->ts_inirq); 
	complete(&prev_tsk->irq_comp); 
	dev_dbg(prev_tsk->dev, "[0x%p] no-0x%x in-irq!", 
				 prev_tsk, prev_tsk->task_no); 

	return IRQ_HANDLED; 
}

看名字基本可以确定它是一个中断函数的处理函数,搜索源码可以找到:

ret= ipu_request_irq(ipu, irq, task_irq_handler, 0, NULL, t);


39.vdi_split_process函数(这个函数只在do_task_release函数中调用了,先不想分析呢。。)


40.do_task_release函数,do_task函数的反函数,先不分析。。。


41.do_task_vdoa_only函数

static void do_task_vdoa_only(struct ipu_task_entry *t) 
{ 
	int ret; 

	ret = init_tiled_ch_bufs(NULL, t); 
	CHECK_RETCODE(ret < 0, "do_vdoa_only", STATE_ERR, out, ret); 
	ret = vdoa_start(t->vdoa_handle, VDOA_DEF_TIMEOUT_MS); 
	vdoa_stop(t->vdoa_handle); 
	CHECK_RETCODE(ret < 0, "vdoa_wait4complete, do_vdoa_only", 
			STATE_VDOA_IRQ_TIMEOUT, out, ret); 

	t->state = STATE_OK; 
out: 
	return; 
}

这个函数中调用了init_tiled_ch_bufs函数来初始化channelbuffer,就说明t->input.format只能是IPU_PIX_FMT_TILED_NV12或者IPU_PIX_FMT_TILED_NV12F,是不是VDOA就是用来处理这种输入格式的?

然后调用vdoa_start函数,在里面等待完成量,如果返回的话意味着完成量超时????,调用vdoa_stop函数来关闭vdoa


42.do_task函数

static void do_task(struct ipu_task_entry *t) 
{ 
	int r_size; 
	int irq; 
	int ret; 
	uint32_t busy; 
	struct ipu_soc *ipu = t->ipu; 

	CHECK_PERF(&t->ts_dotask); 

	if (!ipu) { 
		t->state = STATE_NO_IPU; 
		return; 
	} 

	init_completion(&t->irq_comp); //初始化完成量
	dev_dbg(ipu->dev, "[0x%p]Do task no:0x%x: id %d\n", (void *)t, 
		 t->task_no, t->task_id); 
	dump_task_info(t); 

	if (t->set.task & IC_PP) { 
		t->set.ic_chan = MEM_PP_MEM; 
		dev_dbg(ipu->dev, "[0x%p]ic channel MEM_PP_MEM\n", (void *)t); 
	} else if (t->set.task & IC_VF) { 
		t->set.ic_chan = MEM_PRP_VF_MEM; 
		dev_dbg(ipu->dev, "[0x%p]ic channel MEM_PRP_VF_MEM\n", (void *)t); 
	} else if (t->set.task & VDI_VF) { 
		if (t->set.mode & VDOA_BAND_MODE) { 
			t->set.ic_chan = MEM_VDI_MEM; 
			if (deinterlace_3_field(t)) { 
				t->set.vdi_ic_p_chan = MEM_VDI_MEM_P; 
				t->set.vdi_ic_n_chan = MEM_VDI_MEM_N; 
			} 
			dev_dbg(ipu->dev, "[0x%p]ic ch MEM_VDI_MEM\n", 
					 (void *)t); 
		} else { 
			t->set.ic_chan = MEM_VDI_PRP_VF_MEM; 
			if (deinterlace_3_field(t)) { 
				t->set.vdi_ic_p_chan = MEM_VDI_PRP_VF_MEM_P; 
				t->set.vdi_ic_n_chan = MEM_VDI_PRP_VF_MEM_N; 
			} 
			dev_dbg(ipu->dev, 
				"[0x%p]ic ch MEM_VDI_PRP_VF_MEM\n", t); 
		} 
	} 

/*根据t->set.task里面的值设置t->set.ic_chan。在t->set.taskVDI_VF的情况下,还要根据t->set.mode来设置t->set.vdi_ic_p_chant->set.vdi_ic_n_chan*/

/*上面从整体来说是设置ICchannel的。*/

	if (t->set.task & ROT_PP) { 
		t->set.rot_chan = MEM_ROT_PP_MEM; 
		dev_dbg(ipu->dev, "[0x%p]rot channel MEM_ROT_PP_MEM\n", (void *)t); 
	} else if (t->set.task & ROT_VF) { 
		t->set.rot_chan = MEM_ROT_VF_MEM; 
		dev_dbg(ipu->dev, "[0x%p]rot channel MEM_ROT_VF_MEM\n", (void *)t); 
	} 

/*设置t->set.rot_chan,从整体上来说是设置ROTchannel*/

	if (t->task_id == IPU_TASK_ID_VF) 
		busy = ic_vf_pp_is_busy(ipu, true); 
	else if (t->task_id == IPU_TASK_ID_PP) 
		busy = ic_vf_pp_is_busy(ipu, false); 
	else { 
		dev_err(ipu->dev, "ERR[no:0x%x]ipu task_id:%d invalid!\n", 
				t->task_no, t->task_id); 
		return; 
	} 
	if (busy) { 
		dev_err(ipu->dev, "ERR[0x%p-no:0x%x]ipu task_id:%d busy!\n", 
				(void *)t, t->task_no, t->task_id); 
		t->state = STATE_IPU_BUSY; 
		return; 
	} 

/*判断这两个通道是否正在占用。*/

	irq = get_irq(t); 
	if (irq < 0) { 
		t->state = STATE_NO_IRQ; 
		return; 
	} 
	t->irq = irq; 

/*申请中断,这个get_irq函数是在前面讲过的。申请成功的话将t->irq指向这个irq*/

	/* channel setup */ 
	if (only_ic(t->set.mode)) { 
		dev_dbg(t->dev, "[0x%p]only ic mode\n", (void *)t); 
		ret = init_ic(ipu, t); 
		CHECK_RETCODE(ret < 0, "init_ic only_ic", 
				t->state, chan_setup, ret); 
	} else if (only_rot(t->set.mode)) { 
		dev_dbg(t->dev, "[0x%p]only rot mode\n", (void *)t); 
		ret = init_rot(ipu, t); 
		CHECK_RETCODE(ret < 0, "init_rot only_rot", 
				t->state, chan_setup, ret); 
	} else if (ic_and_rot(t->set.mode)) { 
		int rot_idx = (t->task_id == IPU_TASK_ID_VF) ? 0 : 1; 

		dev_dbg(t->dev, "[0x%p]ic + rot mode\n", (void *)t); 
		t->set.r_fmt = t->output.format; 
		if (t->output.rotate >= IPU_ROTATE_90_RIGHT) { 
			t->set.r_width = t->output.crop.h; 
			t->set.r_height = t->output.crop.w; 
		} else { 
			t->set.r_width = t->output.crop.w; 
			t->set.r_height = t->output.crop.h; 
		} 
		t->set.r_stride = t->set.r_width * 
			bytes_per_pixel(t->set.r_fmt); 
		r_size = PAGE_ALIGN(t->set.r_width * t->set.r_height 
			* fmt_to_bpp(t->set.r_fmt)/8); 

		if (r_size > ipu->rot_dma[rot_idx].size) { 
			dev_dbg(t->dev, "[0x%p]realloc rot buffer\n", (void *)t); 

			if (ipu->rot_dma[rot_idx].vaddr) 
				dma_free_coherent(t->dev, 
					ipu->rot_dma[rot_idx].size, 
					ipu->rot_dma[rot_idx].vaddr, 
					ipu->rot_dma[rot_idx].paddr); 

			ipu->rot_dma[rot_idx].size = r_size; 
			ipu->rot_dma[rot_idx].vaddr = dma_alloc_coherent(t->dev, 
						r_size, 
						&ipu->rot_dma[rot_idx].paddr, 
						GFP_DMA | GFP_KERNEL); 
			CHECK_RETCODE(ipu->rot_dma[rot_idx].vaddr == NULL, 
					"ic_and_rot", STATE_SYS_NO_MEM, 
					chan_setup, -ENOMEM); 
		} 
		t->set.r_paddr = ipu->rot_dma[rot_idx].paddr; 

		dev_dbg(t->dev, "[0x%p]rotation:\n", (void *)t); 
		dev_dbg(t->dev, "[0x%p]\tformat = 0x%x\n", (void *)t, t->set.r_fmt); 
		dev_dbg(t->dev, "[0x%p]\twidth = %d\n", (void *)t, t->set.r_width); 
		dev_dbg(t->dev, "[0x%p]\theight = %d\n", (void *)t, t->set.r_height); 
		dev_dbg(t->dev, "[0x%p]\tpaddr = 0x%x\n", (void *)t, t->set.r_paddr); 
		dev_dbg(t->dev, "[0x%p]\trstride = %d\n", (void *)t, t->set.r_stride); 

		ret = init_ic(ipu, t); 
		CHECK_RETCODE(ret < 0, "init_ic ic_and_rot", 
				t->state, chan_setup, ret); 
		ret = init_rot(ipu, t); 
		CHECK_RETCODE(ret < 0, "init_rot ic_and_rot", 
				t->state, chan_setup, ret); 
		ret = ipu_link_channels(ipu, t->set.ic_chan, 
				t->set.rot_chan); 
		CHECK_RETCODE(ret < 0, "ipu_link_ch ic_and_rot", 
				STATE_LINK_CHAN_FAIL, chan_setup, ret); 
	} else { 
		dev_err(t->dev, "ERR [0x%p]do task: should not be here\n", t); 
		t->state = STATE_ERR; 
		return; 
	} 

/*上面这一段代码就是根据t->set.mode来决定执行哪个mode模式:如果只是ICMODE 模式的话,就调用init_ic函数即可,如果只是ROTMODE的话,就只调用init_rot函数即可。如果是ic_and_rot模式的话,就需要同时调用这两个函数,同时还需要调用ipu_link_channels函数将两个channel链接起来。*/

	ret = ipu_request_irq(ipu, irq, task_irq_handler, 0, NULL, t); 
	CHECK_RETCODE(ret < 0, "ipu_req_irq", 
			STATE_IRQ_FAIL, chan_setup, ret); 

/*调用ipu_request_irq函数来为ipu申请中断,中断函数为task_irq_handler。这个ipu_request_irq函数在ipu_common.c中定义。*/

	/* enable/start channel */ 
	if (only_ic(t->set.mode)) { 
		ret = ipu_enable_channel(ipu, t->set.ic_chan); 
		CHECK_RETCODE(ret < 0, "ipu_enable_ch only_ic", 
				STATE_ENABLE_CHAN_FAIL, chan_en, ret); 
		if (deinterlace_3_field(t)) { 
			ret = ipu_enable_channel(ipu, t->set.vdi_ic_p_chan); 
			CHECK_RETCODE(ret < 0, "ipu_enable_ch only_ic_p", 
					STATE_ENABLE_CHAN_FAIL, chan_en, ret); 
			ret = ipu_enable_channel(ipu, t->set.vdi_ic_n_chan); 
			CHECK_RETCODE(ret < 0, "ipu_enable_ch only_ic_n", 
					STATE_ENABLE_CHAN_FAIL, chan_en, ret); 
		} 

		ret = ipu_select_buffer(ipu, t->set.ic_chan, IPU_OUTPUT_BUFFER, 
					0); 
		CHECK_RETCODE(ret < 0, "ipu_sel_buf only_ic", 
				STATE_SEL_BUF_FAIL, chan_buf, ret); 
		if (t->overlay_en) { 
			ret = ipu_select_buffer(ipu, t->set.ic_chan, 
						IPU_GRAPH_IN_BUFFER, 0); 
			CHECK_RETCODE(ret < 0, "ipu_sel_buf only_ic_g", 
					STATE_SEL_BUF_FAIL, chan_buf, ret); 
			if (t->overlay.alpha.mode == IPU_ALPHA_MODE_LOCAL) { 
				ret = ipu_select_buffer(ipu, t->set.ic_chan, 
							IPU_ALPHA_IN_BUFFER, 0); 
				CHECK_RETCODE(ret < 0, "ipu_sel_buf only_ic_a", 
						STATE_SEL_BUF_FAIL, chan_buf, 
						ret); 
			} 
		} 
		if (!(t->set.mode & VDOA_BAND_MODE)) { 
			if (deinterlace_3_field(t)) 
				ipu_select_multi_vdi_buffer(ipu, 0); 
			else { 
				ret = ipu_select_buffer(ipu, t->set.ic_chan, 
							IPU_INPUT_BUFFER, 0); 
				CHECK_RETCODE(ret < 0, "ipu_sel_buf only_ic_i", 
					STATE_SEL_BUF_FAIL, chan_buf, ret); 
			} 
		} 
	} 

/*使能/开启channel,同样分为3种情况,根据t->set.mode来判断,如果是only_ic模式,(1)就调用ipu_enable_channel函数来使能t->set.ic_chanchannel,然后如果有需要的话同时使能t->set.vdi_ic_p_chanchannelt->set.vdi_ic_n_chanchannel。(2)调用ipu_select_buffer函数来为t->set.ic_chanchannel选择IPU_OUTPUT_BUFFERbuffer。如果使能ovverlay的话,还需要为t->set.ic_chanchannel选择IPU_GRAPH_IN_BUFFERbuffer;如果启用了alpha通道的话,也需要为t->set.ic_chanchannel选择IPU_ALPHA_IN_BUFFERbuffer*/

else if (only_rot(t->set.mode)) { 
		ret = ipu_enable_channel(ipu, t->set.rot_chan); 
		CHECK_RETCODE(ret < 0, "ipu_enable_ch only_rot", 
				STATE_ENABLE_CHAN_FAIL, chan_en, ret); 
		ret = ipu_select_buffer(ipu, t->set.rot_chan, 
						IPU_OUTPUT_BUFFER, 0); 
		CHECK_RETCODE(ret < 0, "ipu_sel_buf only_rot_o", 
				STATE_SEL_BUF_FAIL, chan_buf, ret); 
		ret = ipu_select_buffer(ipu, t->set.rot_chan, 
						IPU_INPUT_BUFFER, 0); 
		CHECK_RETCODE(ret < 0, "ipu_sel_buf only_rot_i", 
				STATE_SEL_BUF_FAIL, chan_buf, ret); 
	} 

/*如果是only_rot模式,调用ipu_enable_channel函数使能t->set.rot_chan通道,还需要为t->set.rot_chanchannel选择对应的buffer,这个channel需要同时选择IPU_OUTPUT_BUFFERIPU_INPUT_BUFFER两个buffer*/

else if (ic_and_rot(t->set.mode)) { 
		ret = ipu_enable_channel(ipu, t->set.rot_chan); 
		CHECK_RETCODE(ret < 0, "ipu_enable_ch ic_and_rot-rot", 
				STATE_ENABLE_CHAN_FAIL, chan_en, ret); 
		ret = ipu_enable_channel(ipu, t->set.ic_chan); 
		CHECK_RETCODE(ret < 0, "ipu_enable_ch ic_and_rot-ic", 
				STATE_ENABLE_CHAN_FAIL, chan_en, ret); 
		if (deinterlace_3_field(t)) { 
			ret = ipu_enable_channel(ipu, t->set.vdi_ic_p_chan); 
			CHECK_RETCODE(ret < 0, "ipu_enable_ch ic_and_rot-p", 
					STATE_ENABLE_CHAN_FAIL, chan_en, ret); 
			ret = ipu_enable_channel(ipu, t->set.vdi_ic_n_chan); 
			CHECK_RETCODE(ret < 0, "ipu_enable_ch ic_and_rot-n", 
					STATE_ENABLE_CHAN_FAIL, chan_en, ret); 
		} 
 
		ret = ipu_select_buffer(ipu, t->set.rot_chan, 
						IPU_OUTPUT_BUFFER, 0); 
		CHECK_RETCODE(ret < 0, "ipu_sel_buf ic_and_rot-rot-o", 
				STATE_SEL_BUF_FAIL, chan_buf, ret); 
		if (t->overlay_en) { 
			ret = ipu_select_buffer(ipu, t->set.ic_chan, 
							IPU_GRAPH_IN_BUFFER, 0); 
			CHECK_RETCODE(ret < 0, "ipu_sel_buf ic_and_rot-ic-g", 
					STATE_SEL_BUF_FAIL, chan_buf, ret); 
			if (t->overlay.alpha.mode == IPU_ALPHA_MODE_LOCAL) { 
				ret = ipu_select_buffer(ipu, t->set.ic_chan, 
							IPU_ALPHA_IN_BUFFER, 0); 
				CHECK_RETCODE(ret < 0, "ipu_sel_buf icrot-ic-a", 
						STATE_SEL_BUF_FAIL, 
						chan_buf, ret); 
			} 
		} 
		ret = ipu_select_buffer(ipu, t->set.ic_chan, 
						IPU_OUTPUT_BUFFER, 0); 
		CHECK_RETCODE(ret < 0, "ipu_sel_buf ic_and_rot-ic-o", 
				STATE_SEL_BUF_FAIL, chan_buf, ret); 
		if (deinterlace_3_field(t)) 
			ipu_select_multi_vdi_buffer(ipu, 0); 
		else { 
			ret = ipu_select_buffer(ipu, t->set.ic_chan, 
							IPU_INPUT_BUFFER, 0); 
			CHECK_RETCODE(ret < 0, "ipu_sel_buf ic_and_rot-ic-i", 
					STATE_SEL_BUF_FAIL, chan_buf, ret); 
		} 
	} 

/*ic_and_rot模式下,很显然需要使能t->set.rot_chant->set.ic_chan两个channel,同时根据deinterlace_3_field(t)这个函数决定是否使能t->set.vdi_ic_p_chant->set.vdi_ic_n_chan这两个channel。之后调用ipu_select_buffer函数为t->set.rot_chanchannel选择IPU_OUTPUT_BUFFERbuffer,同样需要为t->set.ic_chanchannel选择IPU_OUTPUT_BUFFERbuffer,根据deinterlace_3_field(t)函数来决定是否调用ipu_select_multi_vdi_buffer函数。如果使能了channel的话,就需要为这个channel选择buffer*/

	if (need_split(t)) 
		t->state = STATE_IN_PROGRESS; 

/*如果需要拆分的话,就将t->state置位。*/

	if (t->set.mode & VDOA_BAND_MODE) { 
		ret = vdoa_start(t->vdoa_handle, VDOA_DEF_TIMEOUT_MS); 
		CHECK_RETCODE(ret < 0, "vdoa_wait4complete, do_vdoa_band", 
				STATE_VDOA_IRQ_TIMEOUT, chan_rel, ret); 
	} 

/*如果是VDOA_BAND_MODE模式的话,就需要调用vdoa_start函数来启用vdoa.*/

	CHECK_PERF(&t->ts_waitirq); 
	ret = wait_for_completion_timeout(&t->irq_comp, 
				 msecs_to_jiffies(t->timeout - DEF_DELAY_MS)); 
	CHECK_PERF(&t->ts_wakeup); 
	CHECK_RETCODE(ret == 0, "wait_for_comp_timeout", 
			STATE_IRQ_TIMEOUT, chan_rel, ret); 
	dev_dbg(t->dev, "[0x%p] no-0x%x ipu irq done!", t, t->task_no); 

/*等待完成量超时。对完成量不清楚,以后再查查这一块。*/

chan_rel: 
chan_buf: 
chan_en: 
chan_setup: 
	if (t->set.mode & VDOA_BAND_MODE) 
		vdoa_stop(t->vdoa_handle); 
	do_task_release(t, t->state >= STATE_ERR); 
	return; 
}

这个do_task函数是一个比较重要的函数,它在get_res_do_task和下一个do_task_vdoa_vdi函数中调用。


43.do_task_vdoa_vdi函数

static void do_task_vdoa_vdi(struct ipu_task_entry *t) 
{ 
	int i; 
	int ret; 
	u32 stripe_width; 

	/* FIXME: crop mode not support now */ 
	stripe_width = t->input.width >> 1; 
	t->input.crop.pos.x = 0; 
	t->input.crop.pos.y = 0; 
	t->input.crop.w = stripe_width; 
	t->input.crop.h = t->input.height; 
	t->output.crop.w = stripe_width; 
	t->output.crop.h = t->input.height; 

	for (i = 0; i < 2; i++) { 
		t->input.crop.pos.x = t->input.crop.pos.x + i * stripe_width; 
		t->output.crop.pos.x = t->output.crop.pos.x + i * stripe_width; 
		/* check input */ 
		ret = set_crop(&t->input.crop, t->input.width, t->input.height, 
			t->input.format); 
		if (ret < 0) { 
			ret = STATE_ERR; 
			goto done; 
		} else 
			update_offset(t->input.format, 
					t->input.width, t->input.height, 
					t->input.crop.pos.x, 
					t->input.crop.pos.y, 
					&t->set.i_off, &t->set.i_uoff, 
					&t->set.i_voff, &t->set.istride); 
		dev_dbg(t->dev, "i_off:0x%x, i_uoff:0x%x, istride:%d.\n", 
			t->set.i_off, t->set.i_uoff, t->set.istride); 
		/* check output */ 
		ret = set_crop(&t->output.crop, t->input.width, 
					t->output.height, t->output.format); 
		if (ret < 0) { 
			ret = STATE_ERR; 
			goto done; 
		} else 
			update_offset(t->output.format, 
					t->output.width, t->output.height, 
					t->output.crop.pos.x, 
					t->output.crop.pos.y, 
					&t->set.o_off, &t->set.o_uoff, 
					&t->set.o_voff, &t->set.ostride); 

		dev_dbg(t->dev, "o_off:0x%x, o_uoff:0x%x, ostride:%d.\n", 
				t->set.o_off, t->set.o_uoff, t->set.ostride); 

		do_task(t); 
	} 

	return; 
done: 
	dev_err(t->dev, "ERR %s set_crop.\n", __func__); 
	t->state = ret; 
	return; 
}

这个函数里面有一条注释:/*FIXME: crop mode not support now */crop模式在这里不支持,所以这个函数需要重新设置crop里面的参数,为什么不支持??这个函数中有个for循环,也不太理解。

首先设置inputcrop,调用set_crop函数,然后继续调用update_offset函数来更新偏移量,之后设置outputcrop,是同样的步骤。最后就会调用到上一个里面讲的do_task函数。这个函数只被get_res_do_task调用了。


44.get_res_do_task函数

static void get_res_do_task(struct ipu_task_entry *t) 
{ 
	uint32_t	found; 
	uint32_t	split_child; 
	struct mutex	*lock; 

	found = get_vdoa_ipu_res(t); 
	if (!found) { 
		dev_err(t->dev, "ERR:[0x%p] no-0x%x can not get res\n", 
			t, t->task_no); 
		return; 
	} else { 
		if (t->set.task & VDOA_ONLY) 
			do_task_vdoa_only(t); 
		else if ((IPU_PIX_FMT_TILED_NV12F == t->input.format) && 
				(t->set.mode & VDOA_BAND_MODE) && 
				(t->input.crop.w > 
				 soc_max_vdi_in_width(t->ipu))) 
			do_task_vdoa_vdi(t); 
		else 
			do_task(t); 
		put_vdoa_ipu_res(t, 0); 
	} 
	if (t->state != STATE_OK) { 
		dev_err(t->dev, "ERR:[0x%p] no-0x%x state: %s\n", 
			t, t->task_no, state_msg[t->state].msg); 
	} 

	split_child = need_split(t) && t->parent; 
	if (split_child) { 
		lock = &t->parent->split_lock; 
		mutex_lock(lock); 
		t->split_done = 1; 
		mutex_unlock(lock); 
		wake_up(&t->parent->split_waitq); 
	} 

	return; 
}

这个函数首先调用get_vdoa_ipu_res函数来获取资源,然后根据t->set.task中的task模式决定调用do_task_vdoa_onlydo_task_vdoa_vdido_task函数中的哪一个。这个函数在ipu_task_thread函数中直接使用。


45.wait_split_task_complete函数

static void wait_split_task_complete(struct ipu_task_entry *parent, 
				struct ipu_split_task *sp_task, uint32_t size) 
{ 
	struct ipu_task_entry *tsk = NULL; 
	int ret = 0, rc; 
	int j, idx = -1; 
	unsigned long flags; 
	struct mutex *lock = &parent->split_lock; 
	int k, busy_vf, busy_pp; 
	struct ipu_soc *ipu; 
	DECLARE_PERF_VAR; 

	for (j = 0; j < size; j++) { 
		rc = wait_event_timeout( 
			parent->split_waitq, 
			<span style="color:#FF0000;">sp_task_check_done(sp_task, parent, size, &idx), </span>
			msecs_to_jiffies(parent->timeout - DEF_DELAY_MS)); 
		if (!rc) { 
			dev_err(parent->dev, 
				"ERR:[0x%p] no-0x%x, split_task timeout,j:%d," 
				"size:%d.\n", 
				 parent, parent->task_no, j, size); 
			ret = -ETIMEDOUT; 
			goto out; 
		} else { 
			if (idx < 0) { 
				dev_err(parent->dev, 
				"ERR:[0x%p] no-0x%x, invalid task idx:%d\n", 
				 parent, parent->task_no, idx); 
				continue; 
			} 
			tsk = sp_task[idx].child_task; 
			mutex_lock(lock); 
			if (!tsk->split_done || !tsk->ipu) 
				dev_err(tsk->dev, 
				"ERR:no-0x%x,split not done:%d/null ipu:0x%p\n", 
				 tsk->task_no, tsk->split_done, tsk->ipu); 
			tsk->split_done = 0; 
			mutex_unlock(lock); 

			dev_dbg(tsk->dev, 
				"[0x%p] no-0x%x sp_tsk[%d] done,state:%d.\n", 
				 tsk, tsk->task_no, idx, tsk->state); 
			#ifdef DBG_IPU_PERF 
				CHECK_PERF(&tsk->ts_rel); 
				PRINT_TASK_STATISTICS; 
			#endif 
		} 
	} 

out: 
	if (ret == -ETIMEDOUT) { 
		/* debug */ 
		for (k = 0; k < max_ipu_no; k++) { 
			ipu = ipu_get_soc(k); 
			if (IS_ERR(ipu)) { 
				dev_err(parent->dev, "no:0x%x, null ipu:%d\n", 
				 parent->task_no, k); 
			} else { 
				busy_vf = ic_vf_pp_is_busy(ipu, true); 
				busy_pp = ic_vf_pp_is_busy(ipu, false); 
				dev_err(parent->dev, 
					"ERR:ipu[%d] busy_vf:%d, busy_pp:%d.\n", 
					k, busy_vf, busy_pp); 
			} 
		} 
		for (k = 0; k < size; k++) { 
			tsk = sp_task[k].child_task; 
			if (!tsk) 
				continue; 
			dev_err(parent->dev, 
				"ERR: sp_task[%d][0x%p] no-0x%x done:%d," 
				 "state:%s,on_list:%d, ipu:0x%p,timeout!\n", 
				 k, tsk, tsk->task_no, tsk->split_done, 
				 state_msg[tsk->state].msg, tsk->task_in_list, 
				 tsk->ipu); 
		} 
	} 

	for (j = 0; j < size; j++) { 
		tsk = sp_task[j].child_task; 
		if (!tsk) 
			continue; 
		spin_lock_irqsave(&ipu_task_list_lock, flags); 
		if (tsk->task_in_list) { 
			list_del(&tsk->node); 
			tsk->task_in_list = 0; 
			dev_dbg(tsk->dev, 
				"[0x%p] no-0x%x,id:%d sp_tsk timeout list_del.\n", 
				 tsk, tsk->task_no, tsk->task_id); 
		} 
		spin_unlock_irqrestore(&ipu_task_list_lock, flags); 
		if (!tsk->ipu) 
			continue; 
		if (tsk->state != STATE_OK) { 
			dev_err(tsk->dev, 
				"ERR:[0x%p] no-0x%x,id:%d, sp_tsk state: %s\n", 
					tsk, tsk->task_no, tsk->task_id, 
					state_msg[tsk->state].msg); 
		} 
		kref_put(&tsk->refcount, task_mem_free); 
	} 

	kfree(parent->vditmpbuf[0]); 
	kfree(parent->vditmpbuf[1]); 

	if (ret < 0) 
		parent->state = STATE_TIMEOUT; 
	else 
		parent->state = STATE_OK; 
	return; 
}

这个函数的核心就是sp_task_check_done函数,我在里面标红了,它根据数组中的子task,检验每个子task是否执行完毕。


46.find_task函数

static inline int find_task(struct ipu_task_entry **t, int thread_id) 
{ 
	int found; 
	unsigned long flags; 
	struct ipu_task_entry *tsk; 
	struct list_head *task_list = &ipu_task_list; 

/*这个ipu_task_list是一个全局链表,在ipu_queue_task函数中调用list_add_tail(&tsk->node,&ipu_task_list)往这个链表中添加成员。*/

	*t = NULL; 
	spin_lock_irqsave(&ipu_task_list_lock, flags); 
	found = !list_empty(task_list); 
	if (found) { 
		tsk = list_first_entry(task_list, struct ipu_task_entry, node); 
		if (tsk->task_in_list) { 
			list_del(&tsk->node); 
			tsk->task_in_list = 0; 
			*t = tsk; 
			kref_get(&tsk->refcount); 
			dev_dbg(tsk->dev, 
			"thread_id:%d,[0x%p] task_no:0x%x,mode:0x%x list_del\n", 
			thread_id, tsk, tsk->task_no, tsk->set.mode); 
		} else 
			dev_err(tsk->dev, 
			"thread_id:%d,task_no:0x%x,mode:0x%x not on list_del\n", 
			thread_id, tsk->task_no, tsk->set.mode); 
	} 
	spin_unlock_irqrestore(&ipu_task_list_lock, flags); 

	return found; 
} 

可以看出来,这个函数就是从ipu_task_list这个全局链表中取出第一项,如果不为空的话,就返回找到的这一项,将找到的这一项保存在函数的第一个参数t中。


47.ipu_task_thread函数

static int ipu_task_thread(void *argv) 
{ 
	struct ipu_task_entry *tsk; 
	struct ipu_task_entry *sp_tsk0; 
	struct ipu_split_task sp_task[4]; 
	/* priority lower than irq_thread */ 
	const struct sched_param param = { 
		.sched_priority = MAX_USER_RT_PRIO/2 - 1, 
	}; 
	int ret; 
	int curr_thread_id; 
	uint32_t size; 
	unsigned long flags; 
	unsigned int cpu; 
	struct cpumask cpu_mask; 
	struct ipu_thread_data *data = (struct ipu_thread_data *)argv; 

	thread_id++; 
	curr_thread_id = thread_id; 
	sched_setscheduler(current, SCHED_FIFO, ¶m); 

/*这个函数的用法可以查看http://blog.csdn.net/allwtg/article/details/5254306里面的解释。它的目的是将current这个进程的policy设置成SCHED_FIFO,优先级设置成param->sched_priority。*/

	if (!data->is_vdoa) { 
		cpu = cpumask_first(cpu_online_mask); 
		cpumask_set_cpu(cpu, &cpu_mask); 
		ret = sched_setaffinity(data->ipu->thread[data->id]->pid, 
			&cpu_mask); 
		if (ret < 0) { 
			pr_err("%s: sched_setaffinity fail:%d.\n", __func__, ret); 
		} 
		pr_debug("%s: sched_setaffinity cpu:%d.\n", __func__, cpu); 
	} 

	while (!kthread_should_stop()) { 
		int split_fail = 0; 
		int split_parent; 
		int split_child; 

		wait_event_interruptible(thread_waitq, find_task(&tsk, curr_thread_id)); 

/*如果找到当前的task的话,就让thread_waitq这个等待队列进入等待状态,会在本函数和ipu_queue_task函数中唤醒它的。*/

		if (!tsk) { 
			pr_err("thread:%d can not find task.\n", 
				curr_thread_id); 
			continue; 
		} 

		/* note: other threads run split child task */ 
		split_parent = need_split(tsk) && !tsk->parent; 
		split_child = need_split(tsk) && tsk->parent; 

/*找到split_parentsplit_child的方法很简单,它就是根据tskset.split_modetask_no来决定task是否需要拆分。如果需要拆分的话,同时这个ipu_task_entry结构体中的parent没有置位的话,就说明它本身就是parent*/

		if (split_parent) { 
			if ((tsk->set.split_mode == RL_SPLIT) || 
				 (tsk->set.split_mode == UD_SPLIT)) 
				size = 2; 
			else 
				size = 4; 
			ret = queue_split_task(tsk, sp_task, size); 

/*设置queue_split_task函数中的size值,然后根据size的值调用queue_split_task函数来创建拆分的子task,并将它们都保存在sp_task数组中。*/

			if (ret < 0) { 
				split_fail = 1; 
			} else { 
				struct list_head *pos; 

				spin_lock_irqsave(&ipu_task_list_lock, flags); 

				sp_tsk0 = list_first_entry(&tsk->split_list, 
						struct ipu_task_entry, node); 
				list_del(&sp_tsk0->node); 

				list_for_each(pos, &tsk->split_list) { 
					struct ipu_task_entry *tmp; 

					tmp = list_entry(pos, 
						struct ipu_task_entry, node); 
					tmp->task_in_list = 1; 
					dev_dbg(tmp->dev, 
						"[0x%p] no-0x%x,id:%d sp_tsk " 
						"add_to_list.\n", tmp, 
						tmp->task_no, tmp->task_id); 
				} 
				/* add to global list */ 
				list_splice(&tsk->split_list, &ipu_task_list); 

				spin_unlock_irqrestore(&ipu_task_list_lock, 
									flags); 
				/* let the parent thread do the first sp_task */ 
				/* FIXME: ensure the correct sequence for split 
					4size: 5/6->9/a*/ 
				if (!sp_tsk0) 
					dev_err(tsk->dev, 
					"ERR: no-0x%x,can not get split_tsk0\n", 
					tsk->task_no); 
				wake_up_interruptible(&thread_waitq); 
				get_res_do_task(sp_tsk0); 
				dev_dbg(sp_tsk0->dev, 
					"thread:%d complete tsk no:0x%x.\n", 
					curr_thread_id, sp_tsk0->task_no); 
				ret = atomic_read(&req_cnt); 
				if (ret > 0) { 
					wake_up(&res_waitq); 
					dev_dbg(sp_tsk0->dev, 
					"sp_tsk0 sche thread:%d no:0x%x," 
					"req_cnt:%d\n", curr_thread_id, 
					sp_tsk0->task_no, ret); 
					/* For other threads to get_res */ 
					schedule(); 
				} 
			} 
		} else 
			get_res_do_task(tsk); 

/*这一块的逻辑比较乱,但是整体可以理解的是无论哪种情况,最终都会调用到get_res_do_task函数。应该是如果创建了子task的话,每个子task都会执行一个get_res_do_task函数。看下面的注释。*/

		/* wait for all 4 sp_task finished here or timeout 
			and then release all resources */ 
		if (split_parent && !split_fail) 
			wait_split_task_complete(tsk, sp_task, size); 

/*等待子task都执行完。*/

		if (!split_child) { 
			atomic_inc(&tsk->done); 
			wake_up(&tsk->task_waitq); 
		} 

		dev_dbg(tsk->dev, "thread:%d complete tsk no:0x%x-[0x%p].\n", 
				curr_thread_id, tsk->task_no, tsk); 
		ret = atomic_read(&req_cnt); 
		if (ret > 0) { 
			wake_up(&res_waitq); 
			dev_dbg(tsk->dev, "sche thread:%d no:0x%x,req_cnt:%d\n", 
				curr_thread_id, tsk->task_no, ret); 
			/* note: give cpu to other threads to get_res */ 
			schedule(); 
		} 

		kref_put(&tsk->refcount, task_mem_free); 


48.ipu_check_task函数

int ipu_check_task(struct ipu_task *task) 
{ 
	struct ipu_task_entry *tsk; 
	int ret = 0; 

	tsk = create_task_entry(task); 
	if (IS_ERR(tsk)) 
		return PTR_ERR(tsk); 

	ret = check_task(tsk); 

	task->input = tsk->input; 
	task->output = tsk->output; 
	task->overlay = tsk->overlay; 
	dump_task_info(tsk); 

	kref_put(&tsk->refcount, task_mem_free); 
	if (ret != 0) 
		pr_debug("%s ret:%d.\n", __func__, ret); 
	return ret; 
} 
EXPORT_SYMBOL_GPL(ipu_check_task);

这个函数里面没做什么内容,只是调用create_task_entry创建了一个ipu_task_entry实体,然后调用check_task函数来检验这个结构体里面的设置是否正确。这个ipu_check_task函数在mxc_ipu_ioctlIPU_CHECK_TASK中调用。


49.ipu_queue_task函数

int ipu_queue_task(struct ipu_task *task) 
{ 
	struct ipu_task_entry *tsk; 
	unsigned long flags; 
	int ret; 
	u32 tmp_task_no; 
	DECLARE_PERF_VAR; 

	tsk = create_task_entry(task); 
	if (IS_ERR(tsk)) 
		return PTR_ERR(tsk); 

	CHECK_PERF(&tsk->ts_queue); 
	ret = prepare_task(tsk); 
	if (ret < 0) 
		goto done; 

/*首先调用create_task_entry函数来创建一个ipu_task_entry实体,然后调用prepare_task函数来继续设置这个结构体中的某些位。*/

	if (need_split(tsk)) { 
		CHECK_PERF(&tsk->ts_dotask); 
		CHECK_PERF(&tsk->ts_waitirq); 
		CHECK_PERF(&tsk->ts_inirq); 
		CHECK_PERF(&tsk->ts_wakeup); 
	} 

	/* task_no last four bits for split task type*/ 
	tmp_task_no = atomic_inc_return(&frame_no); 
	tsk->task_no = tmp_task_no << 4; 
	init_waitqueue_head(&tsk->task_waitq); 

	spin_lock_irqsave(&ipu_task_list_lock, flags); 
	list_add_tail(&tsk->node, &ipu_task_list); 
	tsk->task_in_list = 1; 
	dev_dbg(tsk->dev, "[0x%p,no-0x%x] list_add_tail\n", tsk, tsk->task_no); 
	spin_unlock_irqrestore(&ipu_task_list_lock, flags); 
	wake_up_interruptible(&thread_waitq); 

	ret = wait_event_timeout(tsk->task_waitq, atomic_read(&tsk->done), 
						msecs_to_jiffies(tsk->timeout)); 
	if (0 == ret) { 
		/* note: the timeout should larger than the internal timeout!*/ 
		ret = -ETIMEDOUT; 
		dev_err(tsk->dev, "ERR: [0x%p] no-0x%x, timeout:%dms!\n", 
				tsk, tsk->task_no, tsk->timeout); 
	} else { 
		if (STATE_OK != tsk->state) { 
			dev_err(tsk->dev, "ERR: [0x%p] no-0x%x,state %d: %s\n", 
				tsk, tsk->task_no, tsk->state, 
				state_msg[tsk->state].msg); 
			ret = -ECANCELED; 
		} else 
			ret = 0; 
	} 

	spin_lock_irqsave(&ipu_task_list_lock, flags); 
	if (tsk->task_in_list) { 
		list_del(&tsk->node); 
		tsk->task_in_list = 0; 
		dev_dbg(tsk->dev, "[0x%p] no:0x%x list_del\n", 
				tsk, tsk->task_no); 
	} 
	spin_unlock_irqrestore(&ipu_task_list_lock, flags); 

#ifdef DBG_IPU_PERF 
	CHECK_PERF(&tsk->ts_rel); 
	PRINT_TASK_STATISTICS; 
	if (ts_frame_avg == 0) 
		ts_frame_avg = ts_frame.tv_nsec / NSEC_PER_USEC + 
				ts_frame.tv_sec * USEC_PER_SEC; 
	else 
		ts_frame_avg = (ts_frame_avg + ts_frame.tv_nsec / NSEC_PER_USEC 
				+ ts_frame.tv_sec * USEC_PER_SEC)/2; 
	if (timespec_compare(&ts_frame, &ts_frame_max) > 0) 
		ts_frame_max = ts_frame; 

	atomic_inc(&frame_cnt); 

	if ((atomic_read(&frame_cnt) %  1000) == 0) 
		pr_debug("ipu_dev: max frame time:%ldus, avg frame time:%dus," 
			"frame_cnt:%d\n", ts_frame_max.tv_nsec / NSEC_PER_USEC 
			+ ts_frame_max.tv_sec * USEC_PER_SEC, 
			ts_frame_avg, atomic_read(&frame_cnt)); 
#endif 
done: 
	if (ret < 0) 
		dev_err(tsk->dev, "ERR: no-0x%x,ipu_queue_task err:%d\n", 
				tsk->task_no, ret); 

	kref_put(&tsk->refcount, task_mem_free); 

	return ret; 
} 
EXPORT_SYMBOL_GPL(ipu_queue_task);

对于这个函数后面的部分,个人感觉重要的就是wake_up_interruptible(&thread_waitq)函数,它唤醒在ipu_task_thread函数中睡眠的thread_waitq队列,让这个函数继续往下执行,就能执行到get_res_do_task函数来处理task了。所以在应用程序中,首先调用IPU_CHECK_TASKioctl宏以后还需要调用IPU_QUEUE_TASKtask宏,这样程序才能往下面执行下去。


后面的几个函数就是围绕file_operations结构体来构建的,以及register_ipu_deviceunregister_ipu_device两个函数,这些都在(一)中分析过了,在这就不再分析。至此,这个文件分析完毕。


  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值