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/dma-buf.h>
9#include <linux/iova.h>
10#include <linux/kernel.h>
11#include <linux/list.h>
12#include <linux/sched.h>
13#include <linux/slab.h>
14#include <linux/workqueue.h>
15#include <linux/module.h>
16
17#include "vde.h"
18
19MODULE_IMPORT_NS(DMA_BUF);
20
21struct tegra_vde_cache_entry {
22	enum dma_data_direction dma_dir;
23	struct dma_buf_attachment *a;
24	struct delayed_work dwork;
25	struct tegra_vde *vde;
26	struct list_head list;
27	struct sg_table *sgt;
28	struct iova *iova;
29	unsigned int refcnt;
30};
31
32static void tegra_vde_release_entry(struct tegra_vde_cache_entry *entry)
33{
34	struct dma_buf *dmabuf = entry->a->dmabuf;
35
36	WARN_ON_ONCE(entry->refcnt);
37
38	if (entry->vde->domain)
39		tegra_vde_iommu_unmap(entry->vde, entry->iova);
40
41	dma_buf_unmap_attachment_unlocked(entry->a, entry->sgt, entry->dma_dir);
42	dma_buf_detach(dmabuf, entry->a);
43	dma_buf_put(dmabuf);
44
45	list_del(&entry->list);
46	kfree(entry);
47}
48
49static void tegra_vde_delayed_unmap(struct work_struct *work)
50{
51	struct tegra_vde_cache_entry *entry;
52	struct tegra_vde *vde;
53
54	entry = container_of(work, struct tegra_vde_cache_entry,
55			     dwork.work);
56	vde = entry->vde;
57
58	mutex_lock(&vde->map_lock);
59	tegra_vde_release_entry(entry);
60	mutex_unlock(&vde->map_lock);
61}
62
63int tegra_vde_dmabuf_cache_map(struct tegra_vde *vde,
64			       struct dma_buf *dmabuf,
65			       enum dma_data_direction dma_dir,
66			       struct dma_buf_attachment **ap,
67			       dma_addr_t *addrp)
68{
69	struct dma_buf_attachment *attachment;
70	struct tegra_vde_cache_entry *entry;
71	struct device *dev = vde->dev;
72	struct sg_table *sgt;
73	struct iova *iova;
74	int err;
75
76	mutex_lock(&vde->map_lock);
77
78	list_for_each_entry(entry, &vde->map_list, list) {
79		if (entry->a->dmabuf != dmabuf)
80			continue;
81
82		if (!cancel_delayed_work(&entry->dwork))
83			continue;
84
85		if (entry->dma_dir != dma_dir)
86			entry->dma_dir = DMA_BIDIRECTIONAL;
87
88		dma_buf_put(dmabuf);
89
90		if (vde->domain)
91			*addrp = iova_dma_addr(&vde->iova, entry->iova);
92		else
93			*addrp = sg_dma_address(entry->sgt->sgl);
94
95		goto ref;
96	}
97
98	attachment = dma_buf_attach(dmabuf, dev);
99	if (IS_ERR(attachment)) {
100		dev_err(dev, "Failed to attach dmabuf\n");
101		err = PTR_ERR(attachment);
102		goto err_unlock;
103	}
104
105	sgt = dma_buf_map_attachment_unlocked(attachment, dma_dir);
106	if (IS_ERR(sgt)) {
107		dev_err(dev, "Failed to get dmabufs sg_table\n");
108		err = PTR_ERR(sgt);
109		goto err_detach;
110	}
111
112	if (!vde->domain && sgt->nents > 1) {
113		dev_err(dev, "Sparse DMA region is unsupported, please enable IOMMU\n");
114		err = -EINVAL;
115		goto err_unmap;
116	}
117
118	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
119	if (!entry) {
120		err = -ENOMEM;
121		goto err_unmap;
122	}
123
124	if (vde->domain) {
125		err = tegra_vde_iommu_map(vde, sgt, &iova, dmabuf->size);
126		if (err)
127			goto err_free;
128
129		*addrp = iova_dma_addr(&vde->iova, iova);
130	} else {
131		*addrp = sg_dma_address(sgt->sgl);
132		iova = NULL;
133	}
134
135	INIT_DELAYED_WORK(&entry->dwork, tegra_vde_delayed_unmap);
136	list_add(&entry->list, &vde->map_list);
137
138	entry->dma_dir = dma_dir;
139	entry->iova = iova;
140	entry->vde = vde;
141	entry->sgt = sgt;
142	entry->a = attachment;
143ref:
144	entry->refcnt++;
145
146	*ap = entry->a;
147
148	mutex_unlock(&vde->map_lock);
149
150	return 0;
151
152err_free:
153	kfree(entry);
154err_unmap:
155	dma_buf_unmap_attachment_unlocked(attachment, sgt, dma_dir);
156err_detach:
157	dma_buf_detach(dmabuf, attachment);
158err_unlock:
159	mutex_unlock(&vde->map_lock);
160
161	return err;
162}
163
164void tegra_vde_dmabuf_cache_unmap(struct tegra_vde *vde,
165				  struct dma_buf_attachment *a,
166				  bool release)
167{
168	struct tegra_vde_cache_entry *entry;
169
170	mutex_lock(&vde->map_lock);
171
172	list_for_each_entry(entry, &vde->map_list, list) {
173		if (entry->a != a)
174			continue;
175
176		WARN_ON_ONCE(!entry->refcnt);
177
178		if (--entry->refcnt == 0) {
179			if (release)
180				tegra_vde_release_entry(entry);
181			else
182				schedule_delayed_work(&entry->dwork, 5 * HZ);
183		}
184		break;
185	}
186
187	mutex_unlock(&vde->map_lock);
188}
189
190void tegra_vde_dmabuf_cache_unmap_sync(struct tegra_vde *vde)
191{
192	struct tegra_vde_cache_entry *entry, *tmp;
193
194	mutex_lock(&vde->map_lock);
195
196	list_for_each_entry_safe(entry, tmp, &vde->map_list, list) {
197		if (entry->refcnt)
198			continue;
199
200		if (!cancel_delayed_work(&entry->dwork))
201			continue;
202
203		tegra_vde_release_entry(entry);
204	}
205
206	mutex_unlock(&vde->map_lock);
207}
208
209void tegra_vde_dmabuf_cache_unmap_all(struct tegra_vde *vde)
210{
211	struct tegra_vde_cache_entry *entry, *tmp;
212
213	mutex_lock(&vde->map_lock);
214
215	while (!list_empty(&vde->map_list)) {
216		list_for_each_entry_safe(entry, tmp, &vde->map_list, list) {
217			if (!cancel_delayed_work(&entry->dwork))
218				continue;
219
220			tegra_vde_release_entry(entry);
221		}
222
223		mutex_unlock(&vde->map_lock);
224		schedule();
225		mutex_lock(&vde->map_lock);
226	}
227
228	mutex_unlock(&vde->map_lock);
229}
230