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