162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * NVIDIA Tegra Video decoder driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2016-2017 Dmitry Osipenko <digetx@gmail.com>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/clk.h>
1062306a36Sopenharmony_ci#include <linux/dma-buf.h>
1162306a36Sopenharmony_ci#include <linux/genalloc.h>
1262306a36Sopenharmony_ci#include <linux/interrupt.h>
1362306a36Sopenharmony_ci#include <linux/list.h>
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/of.h>
1662306a36Sopenharmony_ci#include <linux/platform_device.h>
1762306a36Sopenharmony_ci#include <linux/pm_runtime.h>
1862306a36Sopenharmony_ci#include <linux/reset.h>
1962306a36Sopenharmony_ci#include <linux/slab.h>
2062306a36Sopenharmony_ci#include <linux/uaccess.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include <soc/tegra/common.h>
2362306a36Sopenharmony_ci#include <soc/tegra/pmc.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include "vde.h"
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define CREATE_TRACE_POINTS
2862306a36Sopenharmony_ci#include "trace.h"
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_civoid tegra_vde_writel(struct tegra_vde *vde, u32 value,
3162306a36Sopenharmony_ci		      void __iomem *base, u32 offset)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	trace_vde_writel(vde, base, offset, value);
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	writel_relaxed(value, base + offset);
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ciu32 tegra_vde_readl(struct tegra_vde *vde, void __iomem *base, u32 offset)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	u32 value = readl_relaxed(base + offset);
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	trace_vde_readl(vde, base, offset, value);
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	return value;
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_civoid tegra_vde_set_bits(struct tegra_vde *vde, u32 mask,
4862306a36Sopenharmony_ci			void __iomem *base, u32 offset)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	u32 value = tegra_vde_readl(vde, base, offset);
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	tegra_vde_writel(vde, value | mask, base, offset);
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ciint tegra_vde_alloc_bo(struct tegra_vde *vde,
5662306a36Sopenharmony_ci		       struct tegra_vde_bo **ret_bo,
5762306a36Sopenharmony_ci		       enum dma_data_direction dma_dir,
5862306a36Sopenharmony_ci		       size_t size)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	struct device *dev = vde->dev;
6162306a36Sopenharmony_ci	struct tegra_vde_bo *bo;
6262306a36Sopenharmony_ci	int err;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	bo = kzalloc(sizeof(*bo), GFP_KERNEL);
6562306a36Sopenharmony_ci	if (!bo)
6662306a36Sopenharmony_ci		return -ENOMEM;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	bo->vde = vde;
6962306a36Sopenharmony_ci	bo->size = size;
7062306a36Sopenharmony_ci	bo->dma_dir = dma_dir;
7162306a36Sopenharmony_ci	bo->dma_attrs = DMA_ATTR_WRITE_COMBINE |
7262306a36Sopenharmony_ci			DMA_ATTR_NO_KERNEL_MAPPING;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	if (!vde->domain)
7562306a36Sopenharmony_ci		bo->dma_attrs |= DMA_ATTR_FORCE_CONTIGUOUS;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	bo->dma_cookie = dma_alloc_attrs(dev, bo->size, &bo->dma_handle,
7862306a36Sopenharmony_ci					 GFP_KERNEL, bo->dma_attrs);
7962306a36Sopenharmony_ci	if (!bo->dma_cookie) {
8062306a36Sopenharmony_ci		dev_err(dev, "Failed to allocate DMA buffer of size: %zu\n",
8162306a36Sopenharmony_ci			bo->size);
8262306a36Sopenharmony_ci		err = -ENOMEM;
8362306a36Sopenharmony_ci		goto free_bo;
8462306a36Sopenharmony_ci	}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	err = dma_get_sgtable_attrs(dev, &bo->sgt, bo->dma_cookie,
8762306a36Sopenharmony_ci				    bo->dma_handle, bo->size, bo->dma_attrs);
8862306a36Sopenharmony_ci	if (err) {
8962306a36Sopenharmony_ci		dev_err(dev, "Failed to get DMA buffer SG table: %d\n", err);
9062306a36Sopenharmony_ci		goto free_attrs;
9162306a36Sopenharmony_ci	}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	err = dma_map_sgtable(dev, &bo->sgt, bo->dma_dir, bo->dma_attrs);
9462306a36Sopenharmony_ci	if (err) {
9562306a36Sopenharmony_ci		dev_err(dev, "Failed to map DMA buffer SG table: %d\n", err);
9662306a36Sopenharmony_ci		goto free_table;
9762306a36Sopenharmony_ci	}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	if (vde->domain) {
10062306a36Sopenharmony_ci		err = tegra_vde_iommu_map(vde, &bo->sgt, &bo->iova, bo->size);
10162306a36Sopenharmony_ci		if (err) {
10262306a36Sopenharmony_ci			dev_err(dev, "Failed to map DMA buffer IOVA: %d\n", err);
10362306a36Sopenharmony_ci			goto unmap_sgtable;
10462306a36Sopenharmony_ci		}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci		bo->dma_addr = iova_dma_addr(&vde->iova, bo->iova);
10762306a36Sopenharmony_ci	} else {
10862306a36Sopenharmony_ci		bo->dma_addr = sg_dma_address(bo->sgt.sgl);
10962306a36Sopenharmony_ci	}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	*ret_bo = bo;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	return 0;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ciunmap_sgtable:
11662306a36Sopenharmony_ci	dma_unmap_sgtable(dev, &bo->sgt, bo->dma_dir, bo->dma_attrs);
11762306a36Sopenharmony_cifree_table:
11862306a36Sopenharmony_ci	sg_free_table(&bo->sgt);
11962306a36Sopenharmony_cifree_attrs:
12062306a36Sopenharmony_ci	dma_free_attrs(dev, bo->size, bo->dma_cookie, bo->dma_handle,
12162306a36Sopenharmony_ci		       bo->dma_attrs);
12262306a36Sopenharmony_cifree_bo:
12362306a36Sopenharmony_ci	kfree(bo);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	return err;
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_civoid tegra_vde_free_bo(struct tegra_vde_bo *bo)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	struct tegra_vde *vde = bo->vde;
13162306a36Sopenharmony_ci	struct device *dev = vde->dev;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	if (vde->domain)
13462306a36Sopenharmony_ci		tegra_vde_iommu_unmap(vde, bo->iova);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	dma_unmap_sgtable(dev, &bo->sgt, bo->dma_dir, bo->dma_attrs);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	sg_free_table(&bo->sgt);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	dma_free_attrs(dev, bo->size, bo->dma_cookie, bo->dma_handle,
14162306a36Sopenharmony_ci		       bo->dma_attrs);
14262306a36Sopenharmony_ci	kfree(bo);
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic irqreturn_t tegra_vde_isr(int irq, void *data)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	struct tegra_vde *vde = data;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	if (completion_done(&vde->decode_completion))
15062306a36Sopenharmony_ci		return IRQ_NONE;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	tegra_vde_set_bits(vde, 0, vde->frameid, 0x208);
15362306a36Sopenharmony_ci	complete(&vde->decode_completion);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	return IRQ_HANDLED;
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_cistatic __maybe_unused int tegra_vde_runtime_suspend(struct device *dev)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	struct tegra_vde *vde = dev_get_drvdata(dev);
16162306a36Sopenharmony_ci	int err;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	if (!dev->pm_domain) {
16462306a36Sopenharmony_ci		err = tegra_powergate_power_off(TEGRA_POWERGATE_VDEC);
16562306a36Sopenharmony_ci		if (err) {
16662306a36Sopenharmony_ci			dev_err(dev, "Failed to power down HW: %d\n", err);
16762306a36Sopenharmony_ci			return err;
16862306a36Sopenharmony_ci		}
16962306a36Sopenharmony_ci	}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	clk_disable_unprepare(vde->clk);
17262306a36Sopenharmony_ci	reset_control_release(vde->rst);
17362306a36Sopenharmony_ci	reset_control_release(vde->rst_mc);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	return 0;
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cistatic __maybe_unused int tegra_vde_runtime_resume(struct device *dev)
17962306a36Sopenharmony_ci{
18062306a36Sopenharmony_ci	struct tegra_vde *vde = dev_get_drvdata(dev);
18162306a36Sopenharmony_ci	int err;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	err = reset_control_acquire(vde->rst_mc);
18462306a36Sopenharmony_ci	if (err) {
18562306a36Sopenharmony_ci		dev_err(dev, "Failed to acquire mc reset: %d\n", err);
18662306a36Sopenharmony_ci		return err;
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	err = reset_control_acquire(vde->rst);
19062306a36Sopenharmony_ci	if (err) {
19162306a36Sopenharmony_ci		dev_err(dev, "Failed to acquire reset: %d\n", err);
19262306a36Sopenharmony_ci		goto release_mc_reset;
19362306a36Sopenharmony_ci	}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	if (!dev->pm_domain) {
19662306a36Sopenharmony_ci		err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_VDEC,
19762306a36Sopenharmony_ci							vde->clk, vde->rst);
19862306a36Sopenharmony_ci		if (err) {
19962306a36Sopenharmony_ci			dev_err(dev, "Failed to power up HW : %d\n", err);
20062306a36Sopenharmony_ci			goto release_reset;
20162306a36Sopenharmony_ci		}
20262306a36Sopenharmony_ci	} else {
20362306a36Sopenharmony_ci		/*
20462306a36Sopenharmony_ci		 * tegra_powergate_sequence_power_up() leaves clocks enabled,
20562306a36Sopenharmony_ci		 * while GENPD not.
20662306a36Sopenharmony_ci		 */
20762306a36Sopenharmony_ci		err = clk_prepare_enable(vde->clk);
20862306a36Sopenharmony_ci		if (err) {
20962306a36Sopenharmony_ci			dev_err(dev, "Failed to enable clock: %d\n", err);
21062306a36Sopenharmony_ci			goto release_reset;
21162306a36Sopenharmony_ci		}
21262306a36Sopenharmony_ci	}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	return 0;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_cirelease_reset:
21762306a36Sopenharmony_ci	reset_control_release(vde->rst);
21862306a36Sopenharmony_cirelease_mc_reset:
21962306a36Sopenharmony_ci	reset_control_release(vde->rst_mc);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	return err;
22262306a36Sopenharmony_ci}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_cistatic int tegra_vde_probe(struct platform_device *pdev)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
22762306a36Sopenharmony_ci	struct tegra_vde *vde;
22862306a36Sopenharmony_ci	int irq, err;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	vde = devm_kzalloc(dev, sizeof(*vde), GFP_KERNEL);
23162306a36Sopenharmony_ci	if (!vde)
23262306a36Sopenharmony_ci		return -ENOMEM;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	platform_set_drvdata(pdev, vde);
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	vde->soc = of_device_get_match_data(&pdev->dev);
23762306a36Sopenharmony_ci	vde->dev = dev;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	vde->sxe = devm_platform_ioremap_resource_byname(pdev, "sxe");
24062306a36Sopenharmony_ci	if (IS_ERR(vde->sxe))
24162306a36Sopenharmony_ci		return PTR_ERR(vde->sxe);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	vde->bsev = devm_platform_ioremap_resource_byname(pdev, "bsev");
24462306a36Sopenharmony_ci	if (IS_ERR(vde->bsev))
24562306a36Sopenharmony_ci		return PTR_ERR(vde->bsev);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	vde->mbe = devm_platform_ioremap_resource_byname(pdev, "mbe");
24862306a36Sopenharmony_ci	if (IS_ERR(vde->mbe))
24962306a36Sopenharmony_ci		return PTR_ERR(vde->mbe);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	vde->ppe = devm_platform_ioremap_resource_byname(pdev, "ppe");
25262306a36Sopenharmony_ci	if (IS_ERR(vde->ppe))
25362306a36Sopenharmony_ci		return PTR_ERR(vde->ppe);
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	vde->mce = devm_platform_ioremap_resource_byname(pdev, "mce");
25662306a36Sopenharmony_ci	if (IS_ERR(vde->mce))
25762306a36Sopenharmony_ci		return PTR_ERR(vde->mce);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	vde->tfe = devm_platform_ioremap_resource_byname(pdev, "tfe");
26062306a36Sopenharmony_ci	if (IS_ERR(vde->tfe))
26162306a36Sopenharmony_ci		return PTR_ERR(vde->tfe);
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	vde->ppb = devm_platform_ioremap_resource_byname(pdev, "ppb");
26462306a36Sopenharmony_ci	if (IS_ERR(vde->ppb))
26562306a36Sopenharmony_ci		return PTR_ERR(vde->ppb);
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	vde->vdma = devm_platform_ioremap_resource_byname(pdev, "vdma");
26862306a36Sopenharmony_ci	if (IS_ERR(vde->vdma))
26962306a36Sopenharmony_ci		return PTR_ERR(vde->vdma);
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	vde->frameid = devm_platform_ioremap_resource_byname(pdev, "frameid");
27262306a36Sopenharmony_ci	if (IS_ERR(vde->frameid))
27362306a36Sopenharmony_ci		return PTR_ERR(vde->frameid);
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	vde->clk = devm_clk_get(dev, NULL);
27662306a36Sopenharmony_ci	if (IS_ERR(vde->clk)) {
27762306a36Sopenharmony_ci		err = PTR_ERR(vde->clk);
27862306a36Sopenharmony_ci		dev_err(dev, "Could not get VDE clk %d\n", err);
27962306a36Sopenharmony_ci		return err;
28062306a36Sopenharmony_ci	}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	vde->rst = devm_reset_control_get_exclusive_released(dev, NULL);
28362306a36Sopenharmony_ci	if (IS_ERR(vde->rst)) {
28462306a36Sopenharmony_ci		err = PTR_ERR(vde->rst);
28562306a36Sopenharmony_ci		dev_err(dev, "Could not get VDE reset %d\n", err);
28662306a36Sopenharmony_ci		return err;
28762306a36Sopenharmony_ci	}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	vde->rst_mc = devm_reset_control_get_optional_exclusive_released(dev, "mc");
29062306a36Sopenharmony_ci	if (IS_ERR(vde->rst_mc)) {
29162306a36Sopenharmony_ci		err = PTR_ERR(vde->rst_mc);
29262306a36Sopenharmony_ci		dev_err(dev, "Could not get MC reset %d\n", err);
29362306a36Sopenharmony_ci		return err;
29462306a36Sopenharmony_ci	}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	irq = platform_get_irq_byname(pdev, "sync-token");
29762306a36Sopenharmony_ci	if (irq < 0)
29862306a36Sopenharmony_ci		return irq;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	err = devm_request_irq(dev, irq, tegra_vde_isr, 0,
30162306a36Sopenharmony_ci			       dev_name(dev), vde);
30262306a36Sopenharmony_ci	if (err) {
30362306a36Sopenharmony_ci		dev_err(dev, "Could not request IRQ %d\n", err);
30462306a36Sopenharmony_ci		return err;
30562306a36Sopenharmony_ci	}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	err = devm_tegra_core_dev_init_opp_table_common(dev);
30862306a36Sopenharmony_ci	if (err) {
30962306a36Sopenharmony_ci		dev_err(dev, "Could initialize OPP table %d\n", err);
31062306a36Sopenharmony_ci		return err;
31162306a36Sopenharmony_ci	}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	vde->iram_pool = of_gen_pool_get(dev->of_node, "iram", 0);
31462306a36Sopenharmony_ci	if (!vde->iram_pool) {
31562306a36Sopenharmony_ci		dev_err(dev, "Could not get IRAM pool\n");
31662306a36Sopenharmony_ci		return -EPROBE_DEFER;
31762306a36Sopenharmony_ci	}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	vde->iram = gen_pool_dma_alloc(vde->iram_pool,
32062306a36Sopenharmony_ci				       gen_pool_size(vde->iram_pool),
32162306a36Sopenharmony_ci				       &vde->iram_lists_addr);
32262306a36Sopenharmony_ci	if (!vde->iram) {
32362306a36Sopenharmony_ci		dev_err(dev, "Could not reserve IRAM\n");
32462306a36Sopenharmony_ci		return -ENOMEM;
32562306a36Sopenharmony_ci	}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	INIT_LIST_HEAD(&vde->map_list);
32862306a36Sopenharmony_ci	mutex_init(&vde->map_lock);
32962306a36Sopenharmony_ci	mutex_init(&vde->lock);
33062306a36Sopenharmony_ci	init_completion(&vde->decode_completion);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	err = tegra_vde_iommu_init(vde);
33362306a36Sopenharmony_ci	if (err) {
33462306a36Sopenharmony_ci		dev_err(dev, "Failed to initialize IOMMU: %d\n", err);
33562306a36Sopenharmony_ci		goto err_gen_free;
33662306a36Sopenharmony_ci	}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	pm_runtime_enable(dev);
33962306a36Sopenharmony_ci	pm_runtime_use_autosuspend(dev);
34062306a36Sopenharmony_ci	pm_runtime_set_autosuspend_delay(dev, 300);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	/*
34362306a36Sopenharmony_ci	 * VDE partition may be left ON after bootloader, hence let's
34462306a36Sopenharmony_ci	 * power-cycle it in order to put hardware into a predictable lower
34562306a36Sopenharmony_ci	 * power state.
34662306a36Sopenharmony_ci	 */
34762306a36Sopenharmony_ci	err = pm_runtime_resume_and_get(dev);
34862306a36Sopenharmony_ci	if (err)
34962306a36Sopenharmony_ci		goto err_pm_runtime;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	pm_runtime_put(dev);
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	err = tegra_vde_alloc_bo(vde, &vde->secure_bo, DMA_FROM_DEVICE, 4096);
35462306a36Sopenharmony_ci	if (err) {
35562306a36Sopenharmony_ci		dev_err(dev, "Failed to allocate secure BO: %d\n", err);
35662306a36Sopenharmony_ci		goto err_pm_runtime;
35762306a36Sopenharmony_ci	}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	err = tegra_vde_v4l2_init(vde);
36062306a36Sopenharmony_ci	if (err) {
36162306a36Sopenharmony_ci		dev_err(dev, "Failed to initialize V4L2: %d\n", err);
36262306a36Sopenharmony_ci		goto err_free_secure_bo;
36362306a36Sopenharmony_ci	}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	return 0;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_cierr_free_secure_bo:
36862306a36Sopenharmony_ci	tegra_vde_free_bo(vde->secure_bo);
36962306a36Sopenharmony_cierr_pm_runtime:
37062306a36Sopenharmony_ci	pm_runtime_dont_use_autosuspend(dev);
37162306a36Sopenharmony_ci	pm_runtime_disable(dev);
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	tegra_vde_iommu_deinit(vde);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_cierr_gen_free:
37662306a36Sopenharmony_ci	gen_pool_free(vde->iram_pool, (unsigned long)vde->iram,
37762306a36Sopenharmony_ci		      gen_pool_size(vde->iram_pool));
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	return err;
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_cistatic void tegra_vde_remove(struct platform_device *pdev)
38362306a36Sopenharmony_ci{
38462306a36Sopenharmony_ci	struct tegra_vde *vde = platform_get_drvdata(pdev);
38562306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	tegra_vde_v4l2_deinit(vde);
38862306a36Sopenharmony_ci	tegra_vde_free_bo(vde->secure_bo);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	/*
39162306a36Sopenharmony_ci	 * As it increments RPM usage_count even on errors, we don't need to
39262306a36Sopenharmony_ci	 * check the returned code here.
39362306a36Sopenharmony_ci	 */
39462306a36Sopenharmony_ci	pm_runtime_get_sync(dev);
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	pm_runtime_dont_use_autosuspend(dev);
39762306a36Sopenharmony_ci	pm_runtime_disable(dev);
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	/*
40062306a36Sopenharmony_ci	 * Balance RPM state, the VDE power domain is left ON and hardware
40162306a36Sopenharmony_ci	 * is clock-gated. It's safe to reboot machine now.
40262306a36Sopenharmony_ci	 */
40362306a36Sopenharmony_ci	pm_runtime_put_noidle(dev);
40462306a36Sopenharmony_ci	clk_disable_unprepare(vde->clk);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	tegra_vde_dmabuf_cache_unmap_all(vde);
40762306a36Sopenharmony_ci	tegra_vde_iommu_deinit(vde);
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	gen_pool_free(vde->iram_pool, (unsigned long)vde->iram,
41062306a36Sopenharmony_ci		      gen_pool_size(vde->iram_pool));
41162306a36Sopenharmony_ci}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_cistatic void tegra_vde_shutdown(struct platform_device *pdev)
41462306a36Sopenharmony_ci{
41562306a36Sopenharmony_ci	/*
41662306a36Sopenharmony_ci	 * On some devices bootloader isn't ready to a power-gated VDE on
41762306a36Sopenharmony_ci	 * a warm-reboot, machine will hang in that case.
41862306a36Sopenharmony_ci	 */
41962306a36Sopenharmony_ci	pm_runtime_get_sync(&pdev->dev);
42062306a36Sopenharmony_ci}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_cistatic __maybe_unused int tegra_vde_pm_suspend(struct device *dev)
42362306a36Sopenharmony_ci{
42462306a36Sopenharmony_ci	struct tegra_vde *vde = dev_get_drvdata(dev);
42562306a36Sopenharmony_ci	int err;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	mutex_lock(&vde->lock);
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	err = pm_runtime_force_suspend(dev);
43062306a36Sopenharmony_ci	if (err < 0)
43162306a36Sopenharmony_ci		return err;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	return 0;
43462306a36Sopenharmony_ci}
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_cistatic __maybe_unused int tegra_vde_pm_resume(struct device *dev)
43762306a36Sopenharmony_ci{
43862306a36Sopenharmony_ci	struct tegra_vde *vde = dev_get_drvdata(dev);
43962306a36Sopenharmony_ci	int err;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	err = pm_runtime_force_resume(dev);
44262306a36Sopenharmony_ci	if (err < 0)
44362306a36Sopenharmony_ci		return err;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	mutex_unlock(&vde->lock);
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	return 0;
44862306a36Sopenharmony_ci}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_cistatic const struct dev_pm_ops tegra_vde_pm_ops = {
45162306a36Sopenharmony_ci	SET_RUNTIME_PM_OPS(tegra_vde_runtime_suspend,
45262306a36Sopenharmony_ci			   tegra_vde_runtime_resume,
45362306a36Sopenharmony_ci			   NULL)
45462306a36Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(tegra_vde_pm_suspend,
45562306a36Sopenharmony_ci				tegra_vde_pm_resume)
45662306a36Sopenharmony_ci};
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_cistatic const u32 tegra124_decoded_fmts[] = {
45962306a36Sopenharmony_ci	/* TBD: T124 supports only a non-standard Tegra tiled format */
46062306a36Sopenharmony_ci};
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_cistatic const struct tegra_coded_fmt_desc tegra124_coded_fmts[] = {
46362306a36Sopenharmony_ci	{
46462306a36Sopenharmony_ci		.fourcc = V4L2_PIX_FMT_H264_SLICE,
46562306a36Sopenharmony_ci		.frmsize = {
46662306a36Sopenharmony_ci			.min_width = 16,
46762306a36Sopenharmony_ci			.max_width = 1920,
46862306a36Sopenharmony_ci			.step_width = 16,
46962306a36Sopenharmony_ci			.min_height = 16,
47062306a36Sopenharmony_ci			.max_height = 2032,
47162306a36Sopenharmony_ci			.step_height = 16,
47262306a36Sopenharmony_ci		},
47362306a36Sopenharmony_ci		.num_decoded_fmts = ARRAY_SIZE(tegra124_decoded_fmts),
47462306a36Sopenharmony_ci		.decoded_fmts = tegra124_decoded_fmts,
47562306a36Sopenharmony_ci		.decode_run = tegra_vde_h264_decode_run,
47662306a36Sopenharmony_ci		.decode_wait = tegra_vde_h264_decode_wait,
47762306a36Sopenharmony_ci	},
47862306a36Sopenharmony_ci};
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_cistatic const u32 tegra20_decoded_fmts[] = {
48162306a36Sopenharmony_ci	V4L2_PIX_FMT_YUV420M,
48262306a36Sopenharmony_ci	V4L2_PIX_FMT_YVU420M,
48362306a36Sopenharmony_ci};
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_cistatic const struct tegra_coded_fmt_desc tegra20_coded_fmts[] = {
48662306a36Sopenharmony_ci	{
48762306a36Sopenharmony_ci		.fourcc = V4L2_PIX_FMT_H264_SLICE,
48862306a36Sopenharmony_ci		.frmsize = {
48962306a36Sopenharmony_ci			.min_width = 16,
49062306a36Sopenharmony_ci			.max_width = 1920,
49162306a36Sopenharmony_ci			.step_width = 16,
49262306a36Sopenharmony_ci			.min_height = 16,
49362306a36Sopenharmony_ci			.max_height = 2032,
49462306a36Sopenharmony_ci			.step_height = 16,
49562306a36Sopenharmony_ci		},
49662306a36Sopenharmony_ci		.num_decoded_fmts = ARRAY_SIZE(tegra20_decoded_fmts),
49762306a36Sopenharmony_ci		.decoded_fmts = tegra20_decoded_fmts,
49862306a36Sopenharmony_ci		.decode_run = tegra_vde_h264_decode_run,
49962306a36Sopenharmony_ci		.decode_wait = tegra_vde_h264_decode_wait,
50062306a36Sopenharmony_ci	},
50162306a36Sopenharmony_ci};
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_cistatic const struct tegra_vde_soc tegra124_vde_soc = {
50462306a36Sopenharmony_ci	.supports_ref_pic_marking = true,
50562306a36Sopenharmony_ci	.coded_fmts = tegra124_coded_fmts,
50662306a36Sopenharmony_ci	.num_coded_fmts = ARRAY_SIZE(tegra124_coded_fmts),
50762306a36Sopenharmony_ci};
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_cistatic const struct tegra_vde_soc tegra114_vde_soc = {
51062306a36Sopenharmony_ci	.supports_ref_pic_marking = true,
51162306a36Sopenharmony_ci	.coded_fmts = tegra20_coded_fmts,
51262306a36Sopenharmony_ci	.num_coded_fmts = ARRAY_SIZE(tegra20_coded_fmts),
51362306a36Sopenharmony_ci};
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_cistatic const struct tegra_vde_soc tegra30_vde_soc = {
51662306a36Sopenharmony_ci	.supports_ref_pic_marking = false,
51762306a36Sopenharmony_ci	.coded_fmts = tegra20_coded_fmts,
51862306a36Sopenharmony_ci	.num_coded_fmts = ARRAY_SIZE(tegra20_coded_fmts),
51962306a36Sopenharmony_ci};
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_cistatic const struct tegra_vde_soc tegra20_vde_soc = {
52262306a36Sopenharmony_ci	.supports_ref_pic_marking = false,
52362306a36Sopenharmony_ci	.coded_fmts = tegra20_coded_fmts,
52462306a36Sopenharmony_ci	.num_coded_fmts = ARRAY_SIZE(tegra20_coded_fmts),
52562306a36Sopenharmony_ci};
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_cistatic const struct of_device_id tegra_vde_of_match[] = {
52862306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra124-vde", .data = &tegra124_vde_soc },
52962306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra114-vde", .data = &tegra114_vde_soc },
53062306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra30-vde", .data = &tegra30_vde_soc },
53162306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra20-vde", .data = &tegra20_vde_soc },
53262306a36Sopenharmony_ci	{ },
53362306a36Sopenharmony_ci};
53462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, tegra_vde_of_match);
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_cistatic struct platform_driver tegra_vde_driver = {
53762306a36Sopenharmony_ci	.probe		= tegra_vde_probe,
53862306a36Sopenharmony_ci	.remove_new	= tegra_vde_remove,
53962306a36Sopenharmony_ci	.shutdown	= tegra_vde_shutdown,
54062306a36Sopenharmony_ci	.driver		= {
54162306a36Sopenharmony_ci		.name		= "tegra-vde",
54262306a36Sopenharmony_ci		.of_match_table = tegra_vde_of_match,
54362306a36Sopenharmony_ci		.pm		= &tegra_vde_pm_ops,
54462306a36Sopenharmony_ci	},
54562306a36Sopenharmony_ci};
54662306a36Sopenharmony_cimodule_platform_driver(tegra_vde_driver);
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ciMODULE_DESCRIPTION("NVIDIA Tegra Video Decoder driver");
54962306a36Sopenharmony_ciMODULE_AUTHOR("Dmitry Osipenko <digetx@gmail.com>");
55062306a36Sopenharmony_ciMODULE_LICENSE("GPL");
551