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