vpu的使用依赖驱动,驱动名称是hantro,在设备节点中为/dev/mxc_hantro。本文看看其具体的实现。
Linux内核驱动源文件在kernel/drivers/mxc下,在hantro库中也发现有这个驱动,文件在hantro/decoder_sw/software/linux/pcidriver。这里以内核中的驱动文件为主,后面再看二者的差别。
驱动模块的注册与卸载:
在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了。
前文提到二个驱动文件,经过对比,发现基本一致,感觉是版本升级了,内核这个比较新。