4.1 ipu_common.c分析---入口函数及probe函数分析
2016-08-23 16:25
351 查看
这个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[]数组中的某一项,
在本文件中就是{.compatible
= "fsl,imx6qp-ipu", .data = &ipu_type_imx6qp,}这一项。所以:
*iputype= &ipu_type_imx6qp,
*devtype=
下面标红的部分:
*/
/*从pdev->dev.of_node这个devicenode结构体里面读取“bypass_reset”这一项,将读出的结果存在&bypass_reset中。*/
/*这个of_alias_get_id函数的大致意思是根据名字“ipu”获取到它的id,将这个id返回。但是有两个ipu呢,这个怎么选择??如果根据名字的话,怎么区分这两个ipu?*/
/*这一些就是根据上面获取到的信息来填充这个structipu_soc
*ipu结构体。*/
/*获取irq资源和内存资源。*/
/*申请I/O内存资源,申请后还需要通过ioremap等函数映射后才能够使用。*/
/*为上面获取到的irq资源注册中断服务函数。*/
/*根据获取到的资源来为每个寄存器映射内存空间。*/
/*获取"bus"的时钟。*/
/*这个函数是clk_prepare和clk_enable两个函数的集合,其中clk_prepare函数是个预定义的函数,需要定义CONFIG_HAVE_CLK_PREPARE这个宏,然后就是调用clk_enable函数来使能时钟。*/
/*获取"prg"的时钟。*/
/*这个online是一个bool类型的变量,表示当前这个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函数中不会关闭,也就是这个参数的含义(旁路)。*/
/*这个ipu_mem_reset函数同样在这个文件中,如下所示:
这个ipu_disp_init函数同样在ipu_common.c文件中,如下所示:
最后通过ipu_cm_write函数来设置有关显示的一些寄存器。
*/
/*然后调用ipu_clk_setup_enable函数设置时钟树,这个函数也在这个ipu_common.c文件中,在后面分析。*/
/*上面这段代码先通过ipu_idmac_read函数来读取IDMAC_CONF寄存器的值,然后根据iputype的一些信息设置它们在寄存器中对应的值,最后将新值通过ipu_idmac_write函数重新写入寄存器中。这个函数用来配置IDMAC。*/
/*通过设置IDMAC_CHA_PRI(0)寄存器,将syncrefresh
channels 和CSI->memchannel的设为高优先级*/
/*将IPU_INT_CTRL(5),IPU_INT_CTRL(6),IPU_INT_CTRL(9),IPU_INT_CTRL(10)这几个寄存器设置为默认值0xFFFFFFFF,这几个寄存器应该是错误中断使能的寄存器。*/
/*同样是这个bypass_reset参数,暂时不知道它是多少。*/
/*注册ipu_device设备,这个函数在ipu_device.c文件中*/
/*使能设备的电源管理*/
(一)分析这个文件从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_prepare和clk_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>
相关文章推荐
- 在入口函数Hook的分析
- USB Storage Probe 函数分析
- linux触摸屏驱动开发中的s3c_ts_probe()函数的分析
- 1.2. chromium源代码分析 - chromiumframe - 入口函数
- ReactJS分析之入口函数render
- Windows内核分析之一 —— 内核入口函数
- 系统调用入口函数源码分析system_call——X86_64
- PROC预编译时加入 COMMON_PARSER=YES 选项,支持预编译分析函数(比如 ROW_NUMBER 和 OVER)
- linux-i2c驱动 之 i2c设备层的注册过程probe函数如何被调用分析
- Twemproxy源码分析之一 入口函数及启动过程
- UCHome中关于公共函数(function_common.php)页面的代码分析(一)
- freescale R10 ipu lib 分析 - ipu_common.c
- freescale R10 ipu lib 分析 - ipu_common.c
- linux启动分析---C程序入口函数start_kernel
- mmc卡驱动中host层的probe函数分析
- [转载] linux启动分析(5)---C程序入口函数start_kernel
- freescale R10 ipu lib 分析 - ipu_common.c
- 【thinkphp3.x】ThinkPHP/Common/common.php文件中某些函数的分析
- 原创:Twemproxy源码分析之一 入口函数及启动过程
- scrapy入口函数的分析