File Index
Files and directories
Sign in
FILE

dmabuf-cache.c

File preview with syntax highlighting.

linux-6.19.14/drivers/media/platform/nvidia/tegra-vde/dmabuf-cache.c C
  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}
~/ Read-only file preview.