1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * NVIDIA Tegra Video decoder driver
4 *
5 * Copyright (C) 2016-2019 GRATE-DRIVER project
6 */
7
8#include <linux/iommu.h>
9#include <linux/iova.h>
10#include <linux/kernel.h>
11#include <linux/platform_device.h>
12
13#if IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)
14#include <asm/dma-iommu.h>
15#endif
16
17#include "vde.h"
18
19int tegra_vde_iommu_map(struct tegra_vde *vde,
20			struct sg_table *sgt,
21			struct iova **iovap,
22			size_t size)
23{
24	struct iova *iova;
25	unsigned long shift;
26	unsigned long end;
27	dma_addr_t addr;
28
29	end = vde->domain->geometry.aperture_end;
30	size = iova_align(&vde->iova, size);
31	shift = iova_shift(&vde->iova);
32
33	iova = alloc_iova(&vde->iova, size >> shift, end >> shift, true);
34	if (!iova)
35		return -ENOMEM;
36
37	addr = iova_dma_addr(&vde->iova, iova);
38
39	size = iommu_map_sgtable(vde->domain, addr, sgt,
40				 IOMMU_READ | IOMMU_WRITE);
41	if (!size) {
42		__free_iova(&vde->iova, iova);
43		return -ENXIO;
44	}
45
46	*iovap = iova;
47
48	return 0;
49}
50
51void tegra_vde_iommu_unmap(struct tegra_vde *vde, struct iova *iova)
52{
53	unsigned long shift = iova_shift(&vde->iova);
54	unsigned long size = iova_size(iova) << shift;
55	dma_addr_t addr = iova_dma_addr(&vde->iova, iova);
56
57	iommu_unmap(vde->domain, addr, size);
58	__free_iova(&vde->iova, iova);
59}
60
61int tegra_vde_iommu_init(struct tegra_vde *vde)
62{
63	struct device *dev = vde->dev;
64	struct iova *iova;
65	unsigned long order;
66	unsigned long shift;
67	int err;
68
69	vde->group = iommu_group_get(dev);
70	if (!vde->group)
71		return 0;
72
73#if IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)
74	if (dev->archdata.mapping) {
75		struct dma_iommu_mapping *mapping = to_dma_iommu_mapping(dev);
76
77		arm_iommu_detach_device(dev);
78		arm_iommu_release_mapping(mapping);
79	}
80#endif
81	vde->domain = iommu_domain_alloc(&platform_bus_type);
82	if (!vde->domain) {
83		err = -ENOMEM;
84		goto put_group;
85	}
86
87	err = iova_cache_get();
88	if (err)
89		goto free_domain;
90
91	order = __ffs(vde->domain->pgsize_bitmap);
92	init_iova_domain(&vde->iova, 1UL << order, 0);
93
94	err = iommu_attach_group(vde->domain, vde->group);
95	if (err)
96		goto put_iova;
97
98	/*
99	 * We're using some static addresses that are not accessible by VDE
100	 * to trap invalid memory accesses.
101	 */
102	shift = iova_shift(&vde->iova);
103	iova = reserve_iova(&vde->iova, 0x60000000 >> shift,
104			    0x70000000 >> shift);
105	if (!iova) {
106		err = -ENOMEM;
107		goto detach_group;
108	}
109
110	vde->iova_resv_static_addresses = iova;
111
112	/*
113	 * BSEV's end-address wraps around due to integer overflow during
114	 * of hardware context preparation if IOVA is allocated at the end
115	 * of address space and VDE can't handle that. Hence simply reserve
116	 * the last page to avoid the problem.
117	 */
118	iova = reserve_iova(&vde->iova, 0xffffffff >> shift,
119			    (0xffffffff >> shift) + 1);
120	if (!iova) {
121		err = -ENOMEM;
122		goto unreserve_iova;
123	}
124
125	vde->iova_resv_last_page = iova;
126
127	return 0;
128
129unreserve_iova:
130	__free_iova(&vde->iova, vde->iova_resv_static_addresses);
131detach_group:
132	iommu_detach_group(vde->domain, vde->group);
133put_iova:
134	put_iova_domain(&vde->iova);
135	iova_cache_put();
136free_domain:
137	iommu_domain_free(vde->domain);
138put_group:
139	iommu_group_put(vde->group);
140
141	return err;
142}
143
144void tegra_vde_iommu_deinit(struct tegra_vde *vde)
145{
146	if (vde->domain) {
147		__free_iova(&vde->iova, vde->iova_resv_last_page);
148		__free_iova(&vde->iova, vde->iova_resv_static_addresses);
149		iommu_detach_group(vde->domain, vde->group);
150		put_iova_domain(&vde->iova);
151		iova_cache_put();
152		iommu_domain_free(vde->domain);
153		iommu_group_put(vde->group);
154
155		vde->domain = NULL;
156	}
157}
158