Commit b301f8de authored by Dmitry Osipenko's avatar Dmitry Osipenko Committed by Mauro Carvalho Chehab

media: staging: media: tegra-vde: Add IOMMU support

All Tegra's could provide memory isolation for the video decoder
hardware using IOMMU, it is also required for Tegra30+ in order
to handle sparse dmabuf's which GPU exports in a default kernel
configuration.
Signed-off-by: default avatarDmitry Osipenko <digetx@gmail.com>
Signed-off-by: default avatarHans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab+samsung@kernel.org>
parent 6bc5a4a1
......@@ -3,6 +3,7 @@ config TEGRA_VDE
tristate "NVIDIA Tegra Video Decoder Engine driver"
depends on ARCH_TEGRA || COMPILE_TEST
select DMA_SHARED_BUFFER
select IOMMU_IOVA if IOMMU_SUPPORT
select SRAM
help
Say Y here to enable support for the NVIDIA Tegra video decoder
......
# SPDX-License-Identifier: GPL-2.0
tegra-vde-y := vde.o iommu.o
obj-$(CONFIG_TEGRA_VDE) += tegra-vde.o
// SPDX-License-Identifier: GPL-2.0+
/*
* NVIDIA Tegra Video decoder driver
*
* Copyright (C) 2016-2019 GRATE-DRIVER project
*/
#include <linux/iommu.h>
#include <linux/iova.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#if IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)
#include <asm/dma-iommu.h>
#endif
#include "vde.h"
int tegra_vde_iommu_map(struct tegra_vde *vde,
struct sg_table *sgt,
struct iova **iovap,
dma_addr_t *addrp,
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;
*addrp = addr;
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;
}
}
......@@ -8,6 +8,8 @@
#include <linux/tracepoint.h>
#include "vde.h"
DECLARE_EVENT_CLASS(register_access,
TP_PROTO(struct tegra_vde *vde, void __iomem *base,
u32 offset, u32 value),
......
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* NVIDIA Tegra Video decoder driver
*
* Copyright (C) 2016-2019 GRATE-DRIVER project
*/
#ifndef TEGRA_VDE_H
#define TEGRA_VDE_H
#include <linux/completion.h>
#include <linux/miscdevice.h>
#include <linux/mutex.h>
#include <linux/types.h>
#include <linux/iova.h>
struct clk;
struct gen_pool;
struct iommu_group;
struct iommu_domain;
struct reset_control;
struct tegra_vde {
void __iomem *sxe;
void __iomem *bsev;
void __iomem *mbe;
void __iomem *ppe;
void __iomem *mce;
void __iomem *tfe;
void __iomem *ppb;
void __iomem *vdma;
void __iomem *frameid;
struct mutex lock;
struct miscdevice miscdev;
struct reset_control *rst;
struct reset_control *rst_mc;
struct gen_pool *iram_pool;
struct completion decode_completion;
struct clk *clk;
struct iommu_domain *domain;
struct iommu_group *group;
struct iova_domain iova;
struct iova *iova_resv_static_addresses;
struct iova *iova_resv_last_page;
dma_addr_t iram_lists_addr;
u32 *iram;
};
int tegra_vde_iommu_init(struct tegra_vde *vde);
void tegra_vde_iommu_deinit(struct tegra_vde *vde);
int tegra_vde_iommu_map(struct tegra_vde *vde,
struct sg_table *sgt,
struct iova **iovap,
dma_addr_t *addrp,
size_t size);
void tegra_vde_iommu_unmap(struct tegra_vde *vde, struct iova *iova);
static __maybe_unused char const *
tegra_vde_reg_base_name(struct tegra_vde *vde, void __iomem *base)
{
if (vde->sxe == base)
return "SXE";
if (vde->bsev == base)
return "BSEV";
if (vde->mbe == base)
return "MBE";
if (vde->ppe == base)
return "PPE";
if (vde->mce == base)
return "MCE";
if (vde->tfe == base)
return "TFE";
if (vde->ppb == base)
return "PPB";
if (vde->vdma == base)
return "VDMA";
if (vde->frameid == base)
return "FRAMEID";
return "???";
}
#endif /* TEGRA_VDE_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment