From cdf96fa81ba48e97a138f653c02e62f0b8d8c601 Mon Sep 17 00:00:00 2001 From: Mainak Sen Date: Tue, 18 Mar 2025 04:38:05 +0000 Subject: [PATCH] nvdla: Replace deprecated nvhost_module* APIs Remove dependencies on the following deprecated nvhost API functions by implementing their functionality directly in the NVDLA driver: 1. nvhost_module_init: Replaced with direct implementation in nvdla_module_init: - Get clock resources and set clock rates - Initialize reset control - Set up power management runtime - Create debugfs entries 2. nvhost_module_deinit: Replaced with direct implementation in nvdla_module_deinit: - Disable power management runtime - Free falcon firmware if present - Clean up debugfs entries 3. nvhost_module_reset: Replaced with direct implementation in nvdla_module_reset: - Handle prepare_poweroff callback - Acquire and release reset control - Load clock gating registers - Handle finalize_poweron callback 4. nvhost_module_busy: Replaced with direct implementation in nvdla_module_busy: - Get PM runtime sync - Handle PM runtime errors 5. nvhost_module_idle and nvhost_module_idle_mult: Replaced with direct implementations: - Mark last busy for PM runtime - Handle autosuspend or regular PM runtime put 6. nvhost_module_pm_ops: Replaced with custom nvdla_pm_ops structure: - Implement nvdla_runtime_suspend and nvdla_runtime_resume - Implement nvdla_suspend and nvdla_resume - Configure SET_RUNTIME_PM_OPS and SET_SYSTEM_SLEEP_PM_OPS 7. nvhost_module_load_regs: Replaced with nvdla_module_load_regs: - Implement register manipulation with direct memory mapped I/O - Use writel instead of host1x_writel Bug 4921620 Jira HOSTX-5963 Change-Id: I3006a92a05966da138da31730e97f74f786c9d0c Signed-off-by: Mainak Sen Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3321246 Reviewed-by: Arvind M Reviewed-by: Mitch Harwell GVS: buildbot_gerritrpt --- .../nvdla/port/device/nvdla_device_host1x.c | 328 +++++++++++++----- .../tegra/host/nvdla/port/fw/nvdla_falcon.c | 13 + .../tegra/host/nvdla/port/fw/nvdla_falcon.h | 1 + 3 files changed, 258 insertions(+), 84 deletions(-) diff --git a/drivers/video/tegra/host/nvdla/port/device/nvdla_device_host1x.c b/drivers/video/tegra/host/nvdla/port/device/nvdla_device_host1x.c index c7f9d88a..ea7d4545 100644 --- a/drivers/video/tegra/host/nvdla/port/device/nvdla_device_host1x.c +++ b/drivers/video/tegra/host/nvdla/port/device/nvdla_device_host1x.c @@ -10,6 +10,7 @@ #include "../../dla_queue.h" #include "../../nvdla_debug.h" +#include "../fw/nvdla_falcon.h" #include #include @@ -19,6 +20,7 @@ #include #include #include +#include uint32_t nvdla_device_register_read(struct platform_device *pdev, uint32_t reg) @@ -126,6 +128,7 @@ int32_t nvdla_module_init(struct platform_device *pdev) int32_t err; struct nvhost_device_data *pdata = platform_get_drvdata(pdev); int i; + unsigned int num_clks; pdata->host1x = dev_get_drvdata(pdev->dev.parent); if (!pdata->host1x) { @@ -153,16 +156,62 @@ int32_t nvdla_module_init(struct platform_device *pdev) pdata->aperture[i] = regs; } - err = nvhost_module_init(pdev); - if (err) { - nvdla_dbg_err(pdev, "Failed to init module (err: %x)", err); + err = devm_clk_bulk_get_all(&pdev->dev, &pdata->clks); + if (err < 0) { + dev_err(&pdev->dev, "failed to get clocks %d\n", err); return err; } + pdata->num_clks = err; + num_clks = err; + + for (i = 0; i < num_clks; i++) { + err = clk_set_rate(pdata->clks[i].clk, ULONG_MAX); + if (err < 0) { + dev_err(&pdev->dev, "failed to set clock rate!\n"); + return err; + } + } + + pdata->reset_control = devm_reset_control_get_exclusive_released(&pdev->dev, NULL); + if (IS_ERR(pdata->reset_control)) { + dev_err(&pdev->dev, "failed to get reset\n"); + return PTR_ERR(pdata->reset_control); + } + + err = reset_control_acquire(pdata->reset_control); + if (err < 0) { + dev_err(&pdev->dev, "failed to acquire reset: %d\n", err); + return err; + } + + err = clk_bulk_prepare_enable(num_clks, pdata->clks); + if (err < 0) { + reset_control_release(pdata->reset_control); + dev_err(&pdev->dev, "failed to enabled clocks: %d\n", err); + return err; + } + + reset_control_reset(pdata->reset_control); + clk_bulk_disable_unprepare(num_clks, pdata->clks); + reset_control_release(pdata->reset_control); + + if (pdata->autosuspend_delay) { + pm_runtime_set_autosuspend_delay(&pdev->dev, pdata->autosuspend_delay); + pm_runtime_use_autosuspend(&pdev->dev); + } + + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) + return -EOPNOTSUPP; + + pdata->debugfs = debugfs_create_dir(pdev->dev.of_node->name, NULL); err = nvdla_client_device_init(pdev); if (err) { nvdla_dbg_err(pdev, "Failed to client device (err: %x)", err); - nvhost_module_deinit(pdev); + /* Clean up in case of error */ + pm_runtime_disable(&pdev->dev); + debugfs_remove_recursive(pdata->debugfs); return err; } @@ -171,8 +220,19 @@ int32_t nvdla_module_init(struct platform_device *pdev) void nvdla_module_deinit(struct platform_device *pdev) { + struct nvhost_device_data *pdata = platform_get_drvdata(pdev); + struct falcon *falcon = pdata->falcon_data; + nvdla_client_device_release(pdev); - nvhost_module_deinit(pdev); + pm_runtime_disable(&pdev->dev); + + if (falcon) { + dma_free_coherent(&pdev->dev, falcon->firmware.size, + falcon->firmware.virt, falcon->firmware.iova); + falcon_exit(falcon); + } + + debugfs_remove_recursive(pdata->debugfs); } int32_t nvdla_module_client_register(struct platform_device *pdev, @@ -189,47 +249,182 @@ void nvdla_module_client_unregister(struct platform_device *pdev, int32_t nvdla_module_busy(struct platform_device *pdev) { - return nvhost_module_busy(pdev); + int err; + + err = pm_runtime_get_sync(&pdev->dev); + if (err < 0) { + pm_runtime_put_noidle(&pdev->dev); + return err; + } + + return 0; } void nvdla_module_idle(struct platform_device *pdev) { - nvhost_module_idle(pdev); + nvdla_module_idle_mult(pdev, 1); } void nvdla_module_idle_mult(struct platform_device *pdev, int32_t refs) { - nvhost_module_idle_mult(pdev, refs); + struct nvhost_device_data *pdata = platform_get_drvdata(pdev); + + while (refs--) { + pm_runtime_mark_last_busy(&pdev->dev); + if (pdata->autosuspend_delay) + pm_runtime_put_autosuspend(&pdev->dev); + else + pm_runtime_put(&pdev->dev); + } +} + +static void nvdla_module_load_regs(struct platform_device *pdev, bool prod) +{ + struct nvhost_device_data *pdata = platform_get_drvdata(pdev); + struct nvhost_gating_register *regs = pdata->engine_cg_regs; + + if (!regs) + return; + + while (regs->addr) { + if (prod) { + void __iomem *addr = pdata->aperture[0] + regs->addr; + + writel(regs->prod, addr); + } else { + void __iomem *addr = pdata->aperture[0] + regs->addr; + + writel(regs->disable, addr); + } + regs++; + } } void nvdla_module_reset(struct platform_device *pdev, bool reboot) { - nvhost_module_reset(pdev, reboot); + struct nvhost_device_data *pdata = platform_get_drvdata(pdev); + int err; + + if (reboot) + if (pdata->prepare_poweroff) + pdata->prepare_poweroff(pdev); + + mutex_lock(&pdata->lock); + err = reset_control_acquire(pdata->reset_control); + if (err < 0) { + dev_err(&pdev->dev, "failed to acquire reset: %d\n", err); + } else { + reset_control_reset(pdata->reset_control); + reset_control_release(pdata->reset_control); + } + mutex_unlock(&pdata->lock); + + if (reboot) { + /* Load clockgating registers */ + nvdla_module_load_regs(pdev, pdata->engine_can_cg); + + /* ..and execute engine specific operations (i.e. boot) */ + if (pdata->finalize_poweron) + pdata->finalize_poweron(pdev); + } } +/* Define our own runtime_suspend function for nvdla */ +static int nvdla_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct nvhost_device_data *pdata = dev_get_drvdata(dev); + int err; + + if (pdata->prepare_poweroff) { + err = pdata->prepare_poweroff(pdev); + if (err) + return err; + } + + clk_bulk_disable_unprepare(pdata->num_clks, pdata->clks); + + return 0; +} + +/* Define our own runtime_resume function for nvdla */ +static int nvdla_runtime_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct nvhost_device_data *pdata = dev_get_drvdata(dev); + int err; + + err = clk_bulk_prepare_enable(pdata->num_clks, pdata->clks); + if (err < 0) { + dev_err(&pdev->dev, "failed to enabled clocks: %d\n", err); + return err; + } + + if (pdata->poweron_reset) + nvdla_module_reset(pdev, false); + + /* Load clockgating registers */ + nvdla_module_load_regs(pdev, pdata->engine_can_cg); + + if (pdata->finalize_poweron) + err = pdata->finalize_poweron(pdev); + + return err; +} + +/* Define our own suspend function for nvdla */ +static int nvdla_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct nvhost_device_data *pdata = dev_get_drvdata(dev); + int err = 0; + + if (pdata->prepare_poweroff) { + err = pdata->prepare_poweroff(pdev); + if (err) + return err; + } + + clk_bulk_disable_unprepare(pdata->num_clks, pdata->clks); + + return 0; +} + +/* Define our own resume function for nvdla */ +static int nvdla_resume(struct device *dev) +{ + return nvdla_runtime_resume(dev); +} + +/* Create our own PM operations structure */ +static const struct dev_pm_ops nvdla_pm_ops = { + .runtime_suspend = nvdla_runtime_suspend, + .runtime_resume = nvdla_runtime_resume, + .suspend = nvdla_suspend, + .resume = nvdla_resume, +}; + +/* Module runtime suspend implementation */ int nvdla_module_runtime_suspend(struct device *dev) { struct nvhost_device_data *pdata = dev_get_drvdata(dev); struct nvdla_device *nvdla = pdata->private_data; int err; - if (nvhost_module_pm_ops.runtime_suspend != NULL) { - err = nvhost_module_pm_ops.runtime_suspend(dev); - if (!err && nvdla->icc_write) { - err = icc_set_bw(nvdla->icc_write, 0, 0); - if (err) - dev_warn(&nvdla->pdev->dev, - "failed to set icc_write bw: %d\n", - err); - - return 0; - } - return err; + /* Call directly to our own runtime_suspend */ + err = nvdla_runtime_suspend(dev); + if (!err && nvdla->icc_write) { + err = icc_set_bw(nvdla->icc_write, 0, 0); + if (err) + dev_warn(&nvdla->pdev->dev, + "failed to set icc_write bw: %d\n", + err); } - return -EOPNOTSUPP; + return err; } +/* Module runtime resume implementation */ int nvdla_module_runtime_resume(struct device *dev) { struct nvhost_device_data *pdata = dev_get_drvdata(dev); @@ -239,24 +434,20 @@ int nvdla_module_runtime_resume(struct device *dev) u32 emc_kbps; int err; - if (nvhost_module_pm_ops.runtime_resume != NULL) { - err = nvhost_module_pm_ops.runtime_resume(dev); - if (!err && nvdla->icc_write) { - rate = clk_get_rate(clk); - emc_kbps = rate * NVDLA_AXI_DBB_BW_BPC / 1024; - err = icc_set_bw(nvdla->icc_write, kbps_to_icc(emc_kbps), - 0); - if (err) - dev_warn(&nvdla->pdev->dev, - "failed to set icc_write bw: %d\n", - err); - - return 0; - } - return err; + /* Call directly to our own runtime_resume */ + err = nvdla_runtime_resume(dev); + if (!err && nvdla->icc_write) { + rate = clk_get_rate(clk); + emc_kbps = rate * NVDLA_AXI_DBB_BW_BPC / 1024; + err = icc_set_bw(nvdla->icc_write, kbps_to_icc(emc_kbps), + 0); + if (err) + dev_warn(&nvdla->pdev->dev, + "failed to set icc_write bw: %d\n", + err); } - return -EOPNOTSUPP; + return err; } int nvdla_module_suspend(struct device *dev) @@ -265,18 +456,11 @@ int nvdla_module_suspend(struct device *dev) struct nvdla_device *nvdla_dev = pdata->private_data; int err = 0; - if (nvhost_module_pm_ops.suspend != NULL) { - err = nvhost_module_pm_ops.suspend(dev); - if (err != 0) { - dev_err(dev, "(FAIL) NvHost suspend\n"); - goto fail_nvhost_module_suspend; - } - } else { - err = pm_runtime_force_suspend(dev); - if (err != 0) { - dev_err(dev, "(FAIL) PM suspend\n"); - goto fail_nvhost_module_suspend; - } + /* Call directly to our own suspend */ + err = nvdla_suspend(dev); + if (err != 0) { + dev_err(dev, "(FAIL) NVDLA suspend\n"); + goto fail_nvhost_module_suspend; } if (nvdla_dev->icc_write) { @@ -305,18 +489,11 @@ int nvdla_module_resume(struct device *dev) goto fail_not_in_suspend; } - if (nvhost_module_pm_ops.resume != NULL) { - err = nvhost_module_pm_ops.resume(dev); - if (err != 0) { - dev_err(dev, "(FAIL) NvHost resume\n"); - goto fail_nvhost_module_resume; - } - } else { - err = pm_runtime_force_resume(dev); - if (err != 0) { - dev_err(dev, "(FAIL) PM resume\n"); - goto fail_nvhost_module_resume; - } + /* Call directly to our own resume */ + err = nvdla_resume(dev); + if (err != 0) { + dev_err(dev, "(FAIL) NVDLA resume\n"); + goto fail_nvhost_module_resume; } return 0; @@ -345,25 +522,11 @@ int nvdla_module_prepare_suspend(struct device *dev) goto fail_nvdla_queue_pool_prepare_suspend; } - /* NvHost prepare suspend - callback */ - if (nvhost_module_pm_ops.prepare != NULL) { - err = nvhost_module_pm_ops.prepare(dev); - if (err != 0) { - dev_err(dev, "(FAIL) NvHost prepare suspend\n"); - goto fail_nvhost_module_prepare_suspend; - } - } else { - /* If we took an extra reference, drop it now to prevent - * the device from automatically resuming upon system - * resume. - */ - pm_runtime_put_sync(dev); - } - + /* Drop runtime reference */ + pm_runtime_put_sync(dev); return 0; -fail_nvhost_module_prepare_suspend: fail_nvdla_queue_pool_prepare_suspend: fail_already_in_suspend: return err; @@ -374,12 +537,8 @@ void nvdla_module_complete_resume(struct device *dev) struct nvhost_device_data *pdata = dev_get_drvdata(dev); struct nvdla_device *nvdla_dev = pdata->private_data; - if (nvhost_module_pm_ops.complete != NULL) { - nvhost_module_pm_ops.complete(dev); - } else { - /* Retake reference dropped above */ - pm_runtime_get_noresume(dev); - } + /* Retake reference dropped in prepare */ + pm_runtime_get_noresume(dev); /* Module is no longer in suspend and has resumed successfully */ nvdla_dev->is_suspended = false; @@ -388,6 +547,7 @@ void nvdla_module_complete_resume(struct device *dev) static struct host1x_driver host1x_nvdla_driver = { .driver = { .name = "host1x-nvdla", + .pm = &nvdla_pm_ops, }, }; diff --git a/drivers/video/tegra/host/nvdla/port/fw/nvdla_falcon.c b/drivers/video/tegra/host/nvdla/port/fw/nvdla_falcon.c index 86b5d2dc..19f89b06 100644 --- a/drivers/video/tegra/host/nvdla/port/fw/nvdla_falcon.c +++ b/drivers/video/tegra/host/nvdla/port/fw/nvdla_falcon.c @@ -152,6 +152,19 @@ static int falcon_init(struct falcon *falcon) return 0; } +/** + * falcon_exit() - Release resources used by falcon + * + * @falcon: Pointer to falcon structure + * + * This function releases firmware resources for falcon + */ +void falcon_exit(struct falcon *falcon) +{ + if (falcon->firmware.firmware) + release_firmware(falcon->firmware.firmware); +} + static int falcon_boot(struct falcon *falcon) { unsigned long offset; diff --git a/drivers/video/tegra/host/nvdla/port/fw/nvdla_falcon.h b/drivers/video/tegra/host/nvdla/port/fw/nvdla_falcon.h index 2520fa89..f0758d4d 100644 --- a/drivers/video/tegra/host/nvdla/port/fw/nvdla_falcon.h +++ b/drivers/video/tegra/host/nvdla/port/fw/nvdla_falcon.h @@ -116,5 +116,6 @@ int nvdla_flcn_intr_init(struct platform_device *pdev); int nvdla_flcn_reload_fw(struct platform_device *pdev); int nvdla_flcn_prepare_poweroff(struct platform_device *pdev); int nvdla_flcn_finalize_poweron(struct platform_device *pdev); +void falcon_exit(struct falcon *falcon); #endif /* _NVDLA_PORT_FW_FALCON_H_ */ \ No newline at end of file