NXP

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

2019-07-12 13:47发布

这个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 = { .devtype = { .name = "IPUv3H", .cm_ofs = 0x00200000, .idmac_ofs = 0x00208000, .ic_ofs = 0x00220000, .csi0_ofs = 0x00230000, .csi1_ofs = 0x00238000, .di0_ofs = 0x00240000, .di1_ofs = 0x00248000, .smfc_ofs = 0x00250000, .dc_ofs = 0x00258000, .dmfc_ofs = 0x00260000, .vdi_ofs = 0x00268000, .cpmem_ofs = 0x00300000, .srm_ofs = 0x00340000, .tpm_ofs = 0x00360000, .dc_tmpl_ofs = 0x00380000, .type = IPUv3H, .idmac_used_bufs_present = true, }, .ch0123_axi = 0, .ch23_axi = 0, .ch27_axi = 2, .ch28_axi = 3, .normal_axi = 1, .idmac_used_bufs_en_r = true, .idmac_used_bufs_en_w = true, .idmac_used_bufs_max_r = 0x3, .idmac_used_bufs_max_w = 0x3, .smfc_idmac_12bit_3planar_bs_fixup = true, }; */ dev_dbg(&pdev->dev, "<%s> ", __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 "); 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 "); 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 ", 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 "); 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 "); 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 "); 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 ", ipu->cm_reg); dev_dbg(ipu->dev, "IPU IC Regs = %p ", ipu->ic_reg); dev_dbg(ipu->dev, "IPU IDMAC Regs = %p ", ipu->idmac_reg); dev_dbg(ipu->dev, "IPU DP Regs = %p ", ipu->dp_reg); dev_dbg(ipu->dev, "IPU DC Regs = %p ", ipu->dc_reg); dev_dbg(ipu->dev, "IPU DMFC Regs = %p ", ipu->dmfc_reg); dev_dbg(ipu->dev, "IPU DI0 Regs = %p ", ipu->di_reg[0]); dev_dbg(ipu->dev, "IPU DI1 Regs = %p ", ipu->di_reg[1]); dev_dbg(ipu->dev, "IPU SMFC Regs = %p ", ipu->smfc_reg); dev_dbg(ipu->dev, "IPU CSI0 Regs = %p ", ipu->csi_reg[0]); dev_dbg(ipu->dev, "IPU CSI1 Regs = %p ", ipu->csi_reg[1]); dev_dbg(ipu->dev, "IPU CPMem = %p ", ipu->cpmem_base); dev_dbg(ipu->dev, "IPU TPMem = %p ", ipu->tpmem_base); dev_dbg(ipu->dev, "IPU DC Template Mem = %p ", ipu->dc_tmpl_reg); dev_dbg(ipu->dev, "IPU VDI Regs = %p ", 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 "); 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 ", 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 "); 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; }