4.1 ipu_common.c分析---入口函数及probe函数分析

这个ipu_common.c函数提供ipu底层函数调用的一些关系和函数。

(一)分析这个文件从init函数入口,发现有这个subsys_initcall,说明ipu是作为一个子系统注册到内核中的:

int32_t __init ipu_gen_init(void) 
{ 
	int32_t ret; 

	ret = platform_driver_register(&mxcipu_driver); 
	return 0; 
} 

subsys_initcall(ipu_gen_init);

(二)这个mxcipu_driver结构体注册到平台以后,如果有匹配的设备的话,就会调用其中的probe

static int ipu_probe(struct platform_device *pdev) 
{ 
	struct ipu_soc *ipu; 
	struct resource *res; 
	unsigned long ipu_base; 
	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; 
	int ret = 0, id; 
	u32 bypass_reset, reg; 

/*imx6qp为例(后面本文件中如果有涉及到板子的一些资源,都以imx6qp为例),

*iputype= of_id->data,这个of_id返回的是imx_ipuv3_dt_ids[]数组中的某一项,

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);

在本文件中就是{.compatible = "fsl,imx6qp-ipu", .data = &ipu_type_imx6qp,}这一项。所以:

*iputype= &ipu_type_imx6qp

*devtype= 下面标红的部分:

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, 
 };

*/

	dev_dbg(&pdev->dev, "<%s>\n", __func__); 

	ret = of_property_read_u32(pdev->dev.of_node, 
					"bypass_reset", &bypass_reset); 
	if (ret < 0) { 
		dev_dbg(&pdev->dev, "can not get bypass_reset\n"); 
		return ret; 
	} 

/*pdev->dev.of_node这个devicenode结构体里面读取“bypass_reset”这一项,将读出的结果存在&bypass_reset中。*/

	id = of_alias_get_id(pdev->dev.of_node, "ipu"); 
	if (id < 0) { 
		dev_dbg(&pdev->dev, "can not get alias id\n"); 
		return id; 
	} 

/*这个of_alias_get_id函数的大致意思是根据名字“ipu”获取到它的id,将这个id返回。但是有两个ipu呢,这个怎么选择??如果根据名字的话,怎么区分这两个ipu*/

	ipu = &ipu_array[id]; 
	memset(ipu, 0, sizeof(struct ipu_soc)); 
	ipu->bypass_reset = (bool)bypass_reset; 
	ipu->dev = &pdev->dev; 
	ipu->id = id; 
	ipu->devtype = devtype->type; 
	ipu->ch0123_axi = iputype->ch0123_axi; 
	ipu->ch23_axi = iputype->ch23_axi; 
	ipu->ch27_axi = iputype->ch27_axi; 
	ipu->ch28_axi = iputype->ch28_axi; 
	ipu->normal_axi = iputype->normal_axi; 
	ipu->smfc_idmac_12bit_3planar_bs_fixup = 
			iputype->smfc_idmac_12bit_3planar_bs_fixup; 
	spin_lock_init(&ipu->int_reg_spin_lock); 
	spin_lock_init(&ipu->rdy_reg_spin_lock); 
	mutex_init(&ipu->mutex_lock); 

/*这一些就是根据上面获取到的信息来填充这个structipu_soc *ipu结构体。*/

	dev_dbg(&pdev->dev, "revision is %s\n", devtype->name); 

	ipu->irq_sync = platform_get_irq(pdev, 0); 
	ipu->irq_err = platform_get_irq(pdev, 1); 
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 

	if (!res || ipu->irq_sync < 0 || ipu->irq_err < 0) { 
		dev_err(&pdev->dev, "can't get device resources\n"); 
		return -ENODEV; 
	} 

/*获取irq资源和内存资源。*/

	if (!devm_request_mem_region(&pdev->dev, res->start, 
				     resource_size(res), pdev->name)) 
		return -EBUSY; 

/*申请I/O内存资源,申请后还需要通过ioremap等函数映射后才能够使用。*/

	ret = devm_request_irq(&pdev->dev, ipu->irq_sync, 
			ipu_sync_irq_handler, 0, pdev->name, ipu); 
	if (ret) { 
		dev_err(ipu->dev, "request SYNC interrupt failed\n"); 
		return ret; 
	} 
	ret = devm_request_irq(&pdev->dev, ipu->irq_err, 
			ipu_err_irq_handler, 0, pdev->name, ipu); 
	if (ret) { 
		dev_err(ipu->dev, "request ERR interrupt failed\n"); 
		return ret; 
	} 

/*为上面获取到的irq资源注册中断服务函数。*/

	ipu_base = res->start; //ipu地址的初始值。

	ipu->cm_reg = devm_ioremap(&pdev->dev, 
				ipu_base + devtype->cm_ofs, PAGE_SIZE); 
	ipu->ic_reg = devm_ioremap(&pdev->dev, 
				ipu_base + devtype->ic_ofs, PAGE_SIZE); 
	ipu->idmac_reg = devm_ioremap(&pdev->dev, 
				ipu_base + devtype->idmac_ofs, PAGE_SIZE); 
	/* DP Registers are accessed thru the SRM */ 
	ipu->dp_reg = devm_ioremap(&pdev->dev, 
				ipu_base + devtype->srm_ofs, PAGE_SIZE); 
	ipu->dc_reg = devm_ioremap(&pdev->dev, 
				ipu_base + devtype->dc_ofs, PAGE_SIZE); 
	ipu->dmfc_reg = devm_ioremap(&pdev->dev, 
				ipu_base + devtype->dmfc_ofs, PAGE_SIZE); 
	ipu->di_reg[0] = devm_ioremap(&pdev->dev, 
				ipu_base + devtype->di0_ofs, PAGE_SIZE); 
	ipu->di_reg[1] = devm_ioremap(&pdev->dev, 
				ipu_base + devtype->di1_ofs, PAGE_SIZE); 
	ipu->smfc_reg = devm_ioremap(&pdev->dev, 
				ipu_base + devtype->smfc_ofs, PAGE_SIZE); 
	ipu->csi_reg[0] = devm_ioremap(&pdev->dev, 
				ipu_base + devtype->csi0_ofs, PAGE_SIZE); 
	ipu->csi_reg[1] = devm_ioremap(&pdev->dev, 
				ipu_base + devtype->csi1_ofs, PAGE_SIZE); 
	ipu->cpmem_base = devm_ioremap(&pdev->dev, 
				ipu_base + devtype->cpmem_ofs, SZ_128K); 
	ipu->tpmem_base = devm_ioremap(&pdev->dev, 
				ipu_base + devtype->tpm_ofs, SZ_64K); 
	ipu->dc_tmpl_reg = devm_ioremap(&pdev->dev, 
				ipu_base + devtype->dc_tmpl_ofs, SZ_128K); 
	ipu->vdi_reg = devm_ioremap(&pdev->dev, 
				ipu_base + devtype->vdi_ofs, PAGE_SIZE); 
	if (!ipu->cm_reg || !ipu->ic_reg || !ipu->idmac_reg || 
		!ipu->dp_reg || !ipu->dc_reg || !ipu->dmfc_reg || 
		!ipu->di_reg[0] || !ipu->di_reg[1] || !ipu->smfc_reg || 
		!ipu->csi_reg[0] || !ipu->csi_reg[1] || !ipu->cpmem_base || 
		!ipu->tpmem_base || !ipu->dc_tmpl_reg || !ipu->vdi_reg) 
		return -ENOMEM; 

	dev_dbg(ipu->dev, "IPU CM Regs = %p\n", ipu->cm_reg); 
	dev_dbg(ipu->dev, "IPU IC Regs = %p\n", ipu->ic_reg); 
	dev_dbg(ipu->dev, "IPU IDMAC Regs = %p\n", ipu->idmac_reg); 
	dev_dbg(ipu->dev, "IPU DP Regs = %p\n", ipu->dp_reg); 
	dev_dbg(ipu->dev, "IPU DC Regs = %p\n", ipu->dc_reg); 
	dev_dbg(ipu->dev, "IPU DMFC Regs = %p\n", ipu->dmfc_reg); 
	dev_dbg(ipu->dev, "IPU DI0 Regs = %p\n", ipu->di_reg[0]); 
	dev_dbg(ipu->dev, "IPU DI1 Regs = %p\n", ipu->di_reg[1]); 
	dev_dbg(ipu->dev, "IPU SMFC Regs = %p\n", ipu->smfc_reg); 
	dev_dbg(ipu->dev, "IPU CSI0 Regs = %p\n", ipu->csi_reg[0]); 
	dev_dbg(ipu->dev, "IPU CSI1 Regs = %p\n", ipu->csi_reg[1]); 
	dev_dbg(ipu->dev, "IPU CPMem = %p\n", ipu->cpmem_base); 
	dev_dbg(ipu->dev, "IPU TPMem = %p\n", ipu->tpmem_base); 
	dev_dbg(ipu->dev, "IPU DC Template Mem = %p\n", ipu->dc_tmpl_reg); 
	dev_dbg(ipu->dev, "IPU VDI Regs = %p\n", ipu->vdi_reg); 

/*根据获取到的资源来为每个寄存器映射内存空间。*/

	ipu->ipu_clk = devm_clk_get(ipu->dev, "bus"); 
	if (IS_ERR(ipu->ipu_clk)) { 
		dev_err(ipu->dev, "clk_get ipu failed"); 
		return PTR_ERR(ipu->ipu_clk); 
	} 

/*获取"bus"的时钟。*/

	/* ipu_clk is always prepared */ 
	ret = clk_prepare_enable(ipu->ipu_clk); 
	if (ret < 0) { 
		dev_err(ipu->dev, "ipu clk enable failed\n"); 
		return ret; 
	} 

/*这个函数是clk_prepareclk_enable两个函数的集合,其中clk_prepare函数是个预定义的函数,需要定义CONFIG_HAVE_CLK_PREPARE这个宏,然后就是调用clk_enable函数来使能时钟。*/

	ipu->prg_clk = devm_clk_get(ipu->dev, "prg"); 
	if (IS_ERR(ipu->prg_clk)) 
		ipu->prg_clk = NULL; 

/*获取"prg"的时钟。*/

	ipu->online = true; 

/*这个online是一个bool类型的变量,表示当前这个ipu是否正在使用中。*/

	platform_set_drvdata(pdev, ipu); 

/*设置私有数据。*/

/*下面这个bypass_reset参数在前面通过of_property_read_u32函数获得了,从dts文件中可以看到它等于0.同时在structipu_soc中关于这个bypass_reset有这样的注释:Bypassreset to avoid display channel being stopped by probe since it maystarts to work inbootloader.这个值是从dts文件中获得的bypass_reset,因为在开发板启动过程中,需要使能屏幕来显示,所以显示通道可能已将在bootloader中开启了,在这里设置这个值,使得显示通道在probe函数中不会关闭,也就是这个参数的含义(旁路)。*/

	if (!bypass_reset) { 
		ret = device_reset(&pdev->dev); 
		if (ret) { 
			dev_err(&pdev->dev, "failed to reset: %d\n", ret); 
			return ret; 
		} 

		ipu_mem_reset(ipu); 

		ipu_disp_init(ipu); 

		/* Set MCU_T to divide MCU access window into 2 */ 
		ipu_cm_write(ipu, 0x00400000L | (IPU_MCU_T_DEFAULT << 18), 
			     IPU_DISP_GEN); 
	} 

/*这个ipu_mem_reset函数同样在这个文件中,如下所示:

static int ipu_mem_reset(struct ipu_soc *ipu) 
{ 
	int timeout = 1000; 

	ipu_cm_write(ipu, 0x807FFFFF, IPU_MEM_RST); 

	while (ipu_cm_read(ipu, IPU_MEM_RST) & 0x80000000) { 
		if (!timeout--) 
			return -ETIME; 
		msleep(1); 
	} 

	return 0; 
}

这个ipu_disp_init函数同样在ipu_common.c文件中,如下所示:

void ipu_disp_init(struct ipu_soc *ipu) 
{ 
	ipu->fg_csc_type = ipu->bg_csc_type = CSC_NONE; 
	ipu->color_key_4rgb = true; 
	_ipu_init_dc_mappings(ipu); 
	_ipu_dmfc_init(ipu, DMFC_NORMAL, 1); 
}

最后通过ipu_cm_write函数来设置有关显示的一些寄存器。

*/

	/* setup ipu clk tree after ipu reset  */ 
	ret = ipu_clk_setup_enable(ipu); 
	if (ret < 0) { 
		dev_err(ipu->dev, "ipu clk setup failed\n"); 
		ipu->online = false; 
		return ret; 
	} 

/*然后调用ipu_clk_setup_enable函数设置时钟树,这个函数也在这个ipu_common.c文件中,在后面分析。*/

	if (devtype->idmac_used_bufs_present) { 
/* devtype->idmac_used_bufs_present = true。 */
		reg = ipu_idmac_read(ipu, IDMAC_CONF); 
		if (iputype->idmac_used_bufs_en_r) //idmac_used_bufs_en_r = true
			reg |= IDMAC_CONF_USED_BUFS_EN_R; 
		else 
			reg &= ~IDMAC_CONF_USED_BUFS_EN_R; 
		if (iputype->idmac_used_bufs_en_w) //idmac_used_bufs_en_w = true
			reg |= IDMAC_CONF_USED_BUFS_EN_W; 
		else 
			reg &= ~IDMAC_CONF_USED_BUFS_EN_W; 

		reg &= ~IDMAC_CONF_USED_BUFS_MAX_R_MASK; 
		reg |= (iputype->idmac_used_bufs_max_r << 
			IDMAC_CONF_USED_BUFS_MAX_R_OFFSET); 
		reg &= ~IDMAC_CONF_USED_BUFS_MAX_W_MASK; 
		reg |= (iputype->idmac_used_bufs_max_w << 
			IDMAC_CONF_USED_BUFS_MAX_W_OFFSET); 
/* idmac_used_bufs_max_r = 0x3,
   idmac_used_bufs_max_w = 0x3。 */
		ipu_idmac_write(ipu, reg, IDMAC_CONF); 
	} 

/*上面这段代码先通过ipu_idmac_read函数来读取IDMAC_CONF寄存器的值,然后根据iputype的一些信息设置它们在寄存器中对应的值,最后将新值通过ipu_idmac_write函数重新写入寄存器中。这个函数用来配置IDMAC*/

	/* Set sync refresh channels and CSI->mem channel as high priority */ 
	ipu_idmac_write(ipu, 0x18800003L, IDMAC_CHA_PRI(0)); 

/*通过设置IDMAC_CHA_PRI(0)寄存器,将syncrefresh channels CSI->memchannel的设为高优先级*/

	/* Enable error interrupts by default */ 
	ipu_cm_write(ipu, 0xFFFFFFFF, IPU_INT_CTRL(5)); 
	ipu_cm_write(ipu, 0xFFFFFFFF, IPU_INT_CTRL(6)); 
	ipu_cm_write(ipu, 0xFFFFFFFF, IPU_INT_CTRL(9)); 
	ipu_cm_write(ipu, 0xFFFFFFFF, IPU_INT_CTRL(10)); 

/*IPU_INT_CTRL(5)IPU_INT_CTRL(6)IPU_INT_CTRL(9)IPU_INT_CTRL(10)这几个寄存器设置为默认值0xFFFFFFFF,这几个寄存器应该是错误中断使能的寄存器。*/

	if (!bypass_reset) 
		clk_disable(ipu->ipu_clk); 

/*同样是这个bypass_reset参数,暂时不知道它是多少。*/

	register_ipu_device(ipu, id); 

/*注册ipu_device设备,这个函数在ipu_device.c文件中*/

	pm_runtime_enable(&pdev->dev); 

/*使能设备的电源管理*/

	return ret; 
}<span style="font-family:Courier 10 Pitch;"><span style="font-size:12px;"></span></span>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值