brintos

brintos / linux-shallow public Read only

0
0
Text · 4.7 KiB · 1c5b949 Raw
230 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * NVIDIA Tegra Video decoder driver4 *5 * Copyright (C) 2016-2019 GRATE-DRIVER project6 */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		else93			*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			else182				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