imx VPU解码分析5-Linux设备驱动hantro

        vpu的使用依赖驱动,驱动名称是hantro,在设备节点中为/dev/mxc_hantro。本文看看其具体的实现。

        Linux内核驱动源文件在kernel/drivers/mxc下,在hantro库中也发现有这个驱动,文件在hantro/decoder_sw/software/linux/pcidriver。这里以内核中的驱动文件为主,后面再看二者的差别。

        驱动模块的注册与卸载:

  • module_init(hantro_init);
  • module_exit(hantro_exit);

        在init中通过platform_driver_register向平台驱动程序框架注册驱动,

static struct platform_driver mxchantro_driver = {

    .driver = {

    .name = "mxc_hantro",

    .of_match_table = hantro_of_match,

    #ifdef CONFIG_PM

    .pm = &hantro_pm_ops,

    #endif

    },

    .probe = hantro_dev_probe,

    .remove = hantro_dev_remove,

};

        可见驱动名字就是"mxc_hantro"。

        主要函数就是设备检测函数hantro_dev_probe。

static int hantro_dev_probe(struct platform_device *pdev)
{
	int err = 0;
	struct device *temp_class;
	struct resource *res;
	unsigned long reg_base;

	hantro_dev = &pdev->dev;
	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs_hantro");
	if (!res) {
		pr_err("hantro: unable to get vpu base addr\n");
		return -ENODEV;
	}
	reg_base = res->start;
	if ((ulong)reg_base != multicorebase[0]) {
		pr_err("hantrodec: regbase(0x%lX) not equal to expected value(0x%lX)\n", reg_base, multicorebase[0]);
		return -ENODEV;
	}

	hantro_clk_g1 = clk_get(&pdev->dev, "clk_hantro_g1");
	hantro_clk_g2 = clk_get(&pdev->dev, "clk_hantro_g2");
	hantro_clk_bus = clk_get(&pdev->dev, "clk_hantro_bus");
	if (IS_ERR(hantro_clk_g1) || IS_ERR(hantro_clk_g2) || IS_ERR(hantro_clk_bus)) {
		pr_err("hantro: get clock failed\n");
		return -ENODEV;
	}
	pr_debug("hantro: g1, g2, bus clock: 0x%lX, 0x%lX, 0x%lX\n", clk_get_rate(hantro_clk_g1),
				clk_get_rate(hantro_clk_g2), clk_get_rate(hantro_clk_bus));

	hantro_regulator = regulator_get(&pdev->dev, "regulator");
	if (IS_ERR(hantro_regulator)) {
		pr_err("hantro: get regulator failed\n");
		return -ENODEV;
	}
	hantro_update_voltage(&pdev->dev);

	hantro_clk_enable(&pdev->dev);
	pm_runtime_enable(&pdev->dev);
	pm_runtime_get_sync(&pdev->dev);
	hantro_ctrlblk_reset(&pdev->dev);

	err = hantrodec_init(pdev);
	if (0 != err) {
		pr_err("hantro: hantrodec_init failed\n");
		goto error;
	}

	hantro_class = class_create(THIS_MODULE, "mxc_hantro");
	if (IS_ERR(hantro_class)) {
		err = PTR_ERR(hantro_class);
		goto error;
	}
	temp_class = device_create(hantro_class, NULL, MKDEV(hantrodec_major, 0), NULL, DEVICE_NAME);
	if (IS_ERR(temp_class)) {
		err = PTR_ERR(temp_class);
		goto err_out_class;
	}

#ifdef CONFIG_DEVICE_THERMAL
	HANTRO_REG_THERMAL_NOTIFIER(&hantro_thermal_hot_notifier);
	thermal_event = 0;
	thermal_cur = 0;
	hantro_dynamic_clock = 0;
#endif
	timeout = 0;
	goto out;

err_out_class:
	device_destroy(hantro_class, MKDEV(hantrodec_major, 0));
	class_destroy(hantro_class);
error:
	pr_err("hantro probe failed\n");
out:
	pm_runtime_put_sync(&pdev->dev);
	hantro_clk_disable(&pdev->dev);
	return err;
}

        首先通过调用platform_get_resource_byname来获取设备资源,这可能包括内存,IO端口等具体得参数根据设备树描述

        linux-imx/arch/arm64/boot/dts/freescale/fsl-imx8mq.dtsi查看dts文件有如下描述:

	vpu: vpu@38300000 {
		compatible = "nxp,imx8mq-hantro";
		reg = <0x0 0x38300000 0x0 0x200000>;
		reg-names = "regs_hantro";
		interrupts = , ;
		interrupt-names = "irq_hantro_g1", "irq_hantro_g2";
		clocks = <&clk IMX8MQ_CLK_VPU_G1_ROOT>, <&clk IMX8MQ_CLK_VPU_G2_ROOT>, <&clk IMX8MQ_CLK_VPU_DEC_ROOT>;
		clock-names = "clk_hantro_g1", "clk_hantro_g2", "clk_hantro_bus";
		assigned-clocks = <&clk IMX8MQ_CLK_VPU_G1>, <&clk IMX8MQ_CLK_VPU_G2>, <&clk IMX8MQ_CLK_VPU_BUS>;
		assigned-clock-parents = <&clk IMX8MQ_VPU_PLL_OUT>, <&clk IMX8MQ_VPU_PLL_OUT>, <&clk IMX8MQ_SYS1_PLL_800M>;
		assigned-clock-rates = <600000000>, <600000000>, <800000000>;
		power-domains = <&vpu_pd>;
		regulator-supply = <&sw1c_reg>;
		status = "disabled";
	};

        这个设备树描述了VPU的信息。有些同学会说,这个status怎么是disabled,这不是没有用么,再看看,在其他文件给整ok了。

        回到init函数,然后就是获取了时钟和电压。

        接着初始化函数hantrodec_init

/*---------------------------------------------------------------------------
 *Function name   : hantrodec_init
 *Description     : Initialize the driver
 *
 *Return type     : int
 *---------------------------------------------------------------------------
 */
int hantrodec_init(struct platform_device *pdev)
{
	int result;
	int irq_0, irq_1;

	dec_irq = 0;
	pp_irq = 0;
	pr_debug("hantrodec: Init multi Core[0] at 0x%16lx\n"
			"                     Core[1] at 0x%16lx\n", multicorebase[0], multicorebase[1]);

	hantrodec_data.cores = 0;
	hantrodec_data.iosize[0] = DEC_IO_SIZE_0;
	hantrodec_data.iosize[1] = DEC_IO_SIZE_1;

	hantrodec_data.async_queue_dec = NULL;
	hantrodec_data.async_queue_pp = NULL;

	result = register_chrdev(hantrodec_major, "hantrodec", &hantrodec_fops);
	if (result < 0) {
		pr_err("hantrodec: unable to get major %d\n", hantrodec_major);
		goto err;
	} else if (result != 0) { /* this is for dynamic major */
		hantrodec_major = result;
	}

	result = ReserveIO();
	if (result < 0)
		goto err;

	memset(dec_owner, 0, sizeof(dec_owner));
	memset(pp_owner, 0, sizeof(pp_owner));

	sema_init(&dec_core_sem, hantrodec_data.cores-1);
	sema_init(&pp_core_sem, 1);

	/* read configuration fo all cores */
	ReadCoreConfig(&hantrodec_data);

	/* reset hardware */
	ResetAsic(&hantrodec_data);

	/* register irq for each core*/
	irq_0 = platform_get_irq_byname(pdev, "irq_hantro_g1");
	if (irq_0 > 0) {
		hantrodec_data.irq[0] = irq_0;
		result = request_irq(irq_0, hantrodec_isr, IRQF_SHARED,
				"hantrodec", (void *) &hantrodec_data);

		if (result != 0) {
			if (result == -EINVAL)
				pr_err("hantrodec: Bad irq number or handler\n");
			else if (result == -EBUSY) {
				pr_err("hantrodec: IRQ <%d> busy, change your config\n",
				hantrodec_data.irq[0]);
			}
			ReleaseIO();
			goto err;
		}
	}	else {
		pr_err("hantrodec: IRQ0 not in use!\n");
		goto err;
	}

	irq_1 = platform_get_irq_byname(pdev, "irq_hantro_g2");
	if (irq_1 > 0) {
		hantrodec_data.irq[1] = irq_1;
		result = request_irq(irq_1, hantrodec_isr, IRQF_SHARED,
				"hantrodec", (void *) &hantrodec_data);

		if (result != 0) {
			if (result == -EINVAL)
				pr_err("hantrodec: Bad irq number or handler\n");
			else if (result == -EBUSY) {
				pr_err("hantrodec: IRQ <%d> busy, change your config\n",
					hantrodec_data.irq[1]);
			}

			ReleaseIO();
			goto err;
		}
	}	else {
		pr_err("hantrodec: IRQ1 not in use!\n");
		goto err;
	}
	pr_info("hantrodec: module inserted. Major = %d\n", hantrodec_major);

	return 0;

err:
	pr_err("hantrodec: module not inserted\n");
	unregister_chrdev(hantrodec_major, "hantrodec");
	return result;
}

        主要进行了注册字符设备、预留I/O、初始化信号量、读取核心配置和重置ASIC还有中断

        最后就是class_create和device_create了。

        前文提到二个驱动文件,经过对比,发现基本一致,感觉是版本升级了,内核这个比较新。

你可能感兴趣的:(音视频,嵌入式,Linux,fpga开发)