diff --git a/drivers/video/tegra/host/nvdla/nvdla.c b/drivers/video/tegra/host/nvdla/nvdla.c index 640bd4a6..4d57fdb3 100644 --- a/drivers/video/tegra/host/nvdla/nvdla.c +++ b/drivers/video/tegra/host/nvdla/nvdla.c @@ -893,12 +893,14 @@ static ssize_t clk_cap_store(struct kobject *kobj, { struct nvhost_device_data *pdata = container_of(kobj, struct nvhost_device_data, clk_cap_kobj); + struct nvdla_device *nvdla = pdata->private_data; /* i is indeed 'index' here after type conversion */ int ret, i = attr - pdata->clk_cap_attrs; struct clk_bulk_data *clks = &pdata->clks[i]; struct clk *clk = clks->clk; unsigned long freq_cap; long freq_cap_signed; + u32 emc_kbps; ret = kstrtoul(buf, 0, &freq_cap); if (ret) @@ -923,6 +925,16 @@ static ssize_t clk_cap_store(struct kobject *kobj, if (ret < 0) return ret; + /* Update bandwidth requirement based on dla frequency */ + if (i == 0 && nvdla->icc_write && !pm_runtime_suspended(nvdla->dev)) { + freq_cap = clk_get_rate(clk); + emc_kbps = freq_cap * NVDLA_AXI_DBB_BW_BPC / 1024; + ret = icc_set_bw(nvdla->icc_write, kbps_to_icc(emc_kbps), 0); + if (ret) + dev_warn(&nvdla->pdev->dev, + "failed to set icc_write bw: %d\n", ret); + } + return count; } @@ -1093,6 +1105,13 @@ static int nvdla_probe(struct platform_device *pdev) goto err_alloc_nvdla; } + nvdla_dev->icc_write = devm_of_icc_get(dev, "write"); + if (IS_ERR(nvdla_dev->icc_write)) { + dev_info(dev, "failed to get icc write handle\n"); + nvdla_dev->icc_write = NULL; + } + + nvdla_dev->dev = dev; nvdla_dev->pdev = pdev; pdata->pdev = pdev; mutex_init(&pdata->lock); @@ -1315,25 +1334,58 @@ static int __exit nvdla_remove(struct platform_device *pdev) #ifdef CONFIG_PM static int nvdla_module_runtime_suspend(struct device *dev) { - if (nvhost_module_pm_ops.runtime_suspend != NULL) - return nvhost_module_pm_ops.runtime_suspend(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; + } return -EOPNOTSUPP; } static int nvdla_module_runtime_resume(struct device *dev) { - if (nvhost_module_pm_ops.runtime_resume != NULL) - return nvhost_module_pm_ops.runtime_resume(dev); + struct nvhost_device_data *pdata = dev_get_drvdata(dev); + struct nvdla_device *nvdla = pdata->private_data; + struct clk *clk = pdata->clks[0].clk; + unsigned long rate; + 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; + } return -EOPNOTSUPP; } static int nvdla_module_suspend(struct device *dev) { - int err = 0; struct nvhost_device_data *pdata = dev_get_drvdata(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); @@ -1349,6 +1401,13 @@ static int nvdla_module_suspend(struct device *dev) } } + if (nvdla_dev->icc_write) { + err = icc_set_bw(nvdla_dev->icc_write, 0, 0); + if (err) + dev_warn(&nvdla_dev->pdev->dev, + "failed to set icc_write bw: %d\n", err); + } + /* Mark module to be in suspend state. */ nvdla_dev->is_suspended = true; @@ -1358,9 +1417,12 @@ fail_nvhost_module_suspend: static int nvdla_module_resume(struct device *dev) { - int err = 0; struct nvhost_device_data *pdata = dev_get_drvdata(dev); struct nvdla_device *nvdla_dev = pdata->private_data; + struct clk *clk = pdata->clks[0].clk; + unsigned long rate; + u32 emc_kbps; + int err; /* Confirm if module is in suspend state. */ if (!nvdla_dev->is_suspended) { @@ -1382,6 +1444,15 @@ static int nvdla_module_resume(struct device *dev) } } + if (nvdla_dev->icc_write) { + rate = clk_get_rate(clk); + emc_kbps = rate * NVDLA_AXI_DBB_BW_BPC / 1024; + err = icc_set_bw(nvdla_dev->icc_write, kbps_to_icc(emc_kbps), 0); + if (err) + dev_warn(&nvdla_dev->pdev->dev, + "failed to set icc_write bw: %d\n", err); + } + return 0; fail_nvhost_module_resume: diff --git a/drivers/video/tegra/host/nvdla/nvdla.h b/drivers/video/tegra/host/nvdla/nvdla.h index 1d232753..c8139707 100644 --- a/drivers/video/tegra/host/nvdla/nvdla.h +++ b/drivers/video/tegra/host/nvdla/nvdla.h @@ -9,6 +9,7 @@ #define __NVHOST_NVDLA_H__ #include +#include #include #include #include @@ -191,6 +192,18 @@ enum { #define MAX_CMD_SIZE SZ_256 #define NVDLA_CMD_OFFSET(index) (MAX_CMD_SIZE * index) +/* + * Client connectivity bandwidth + */ +#define AXI_READ_WIDTH 512 +#define AXI_READ_OUTSTAND 512 +#define AXI_WRITE_WIDTH 512 +#define AXI_WRITE_OUTSTAND 128 +#define AXI_WORST_LATENCY 1600 +#define NVDLA_AXI_READ_BPC (2*AXI_READ_WIDTH * AXI_READ_OUTSTAND / AXI_WORST_LATENCY) +#define NVDLA_AXI_WRITE_BPC (2*AXI_WRITE_WIDTH * AXI_WRITE_OUTSTAND / AXI_WORST_LATENCY) +#define NVDLA_AXI_DBB_BW_BPC (NVDLA_AXI_READ_BPC + NVDLA_AXI_WRITE_BPC) + /** * data structure to keep command memory * @@ -242,6 +255,7 @@ enum nvdla_submit_mode { /** * data structure to keep per DLA engine device data * + * @dev pointer to device * @pdev pointer to platform device * @pool pointer to queue table * @dbg_mask debug mask for print level @@ -267,10 +281,12 @@ enum nvdla_submit_mode { * @ping_lock lock to synchronize the ping operation requests. */ struct nvdla_device { + struct device *dev; struct platform_device *pdev; struct nvdla_queue_pool *pool; struct completion cmd_completion; struct mutex cmd_lock; + struct icc_path *icc_write; int cmd_status; int waiting; u32 dbg_mask;