// SPDX-License-Identifier: GPL-2.0+ /* * NVIDIA Tegra Video decoder driver * * Copyright (C) 2016-2019 GRATE-DRIVER project */ #include #include #include #include #if IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU) #include #endif #include "vde.h" int tegra_vde_iommu_map(struct tegra_vde *vde, struct sg_table *sgt, struct iova **iovap, size_t size) { struct iova *iova; unsigned long shift; unsigned long end; dma_addr_t addr; end = vde->domain->geometry.aperture_end; size = iova_align(&vde->iova, size); shift = iova_shift(&vde->iova); iova = alloc_iova(&vde->iova, size >> shift, end >> shift, true); if (!iova) return -ENOMEM; addr = iova_dma_addr(&vde->iova, iova); size = iommu_map_sg(vde->domain, addr, sgt->sgl, sgt->nents, IOMMU_READ | IOMMU_WRITE); if (!size) { __free_iova(&vde->iova, iova); return -ENXIO; } *iovap = iova; return 0; } void tegra_vde_iommu_unmap(struct tegra_vde *vde, struct iova *iova) { unsigned long shift = iova_shift(&vde->iova); unsigned long size = iova_size(iova) << shift; dma_addr_t addr = iova_dma_addr(&vde->iova, iova); iommu_unmap(vde->domain, addr, size); __free_iova(&vde->iova, iova); } int tegra_vde_iommu_init(struct tegra_vde *vde) { struct device *dev = vde->miscdev.parent; struct iova *iova; unsigned long order; unsigned long shift; int err; vde->group = iommu_group_get(dev); if (!vde->group) return 0; #if IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU) if (dev->archdata.mapping) { struct dma_iommu_mapping *mapping = to_dma_iommu_mapping(dev); arm_iommu_detach_device(dev); arm_iommu_release_mapping(mapping); } #endif vde->domain = iommu_domain_alloc(&platform_bus_type); if (!vde->domain) { err = -ENOMEM; goto put_group; } err = iova_cache_get(); if (err) goto free_domain; order = __ffs(vde->domain->pgsize_bitmap); init_iova_domain(&vde->iova, 1UL << order, 0); err = iommu_attach_group(vde->domain, vde->group); if (err) goto put_iova; /* * We're using some static addresses that are not accessible by VDE * to trap invalid memory accesses. */ shift = iova_shift(&vde->iova); iova = reserve_iova(&vde->iova, 0x60000000 >> shift, 0x70000000 >> shift); if (!iova) { err = -ENOMEM; goto detach_group; } vde->iova_resv_static_addresses = iova; /* * BSEV's end-address wraps around due to integer overflow during * of hardware context preparation if IOVA is allocated at the end * of address space and VDE can't handle that. Hence simply reserve * the last page to avoid the problem. */ iova = reserve_iova(&vde->iova, 0xffffffff >> shift, (0xffffffff >> shift) + 1); if (!iova) { err = -ENOMEM; goto unreserve_iova; } vde->iova_resv_last_page = iova; return 0; unreserve_iova: __free_iova(&vde->iova, vde->iova_resv_static_addresses); detach_group: iommu_detach_group(vde->domain, vde->group); put_iova: put_iova_domain(&vde->iova); iova_cache_put(); free_domain: iommu_domain_free(vde->domain); put_group: iommu_group_put(vde->group); return err; } void tegra_vde_iommu_deinit(struct tegra_vde *vde) { if (vde->domain) { __free_iova(&vde->iova, vde->iova_resv_last_page); __free_iova(&vde->iova, vde->iova_resv_static_addresses); iommu_detach_group(vde->domain, vde->group); put_iova_domain(&vde->iova); iova_cache_put(); iommu_domain_free(vde->domain); iommu_group_put(vde->group); vde->domain = NULL; } }