311 lines
7.8 KiB
C
311 lines
7.8 KiB
C
/*
|
|
* drivers/video/tegra/host/nvmap.c
|
|
*
|
|
* Tegra Graphics Host Nvmap support
|
|
*
|
|
* Copyright (c) 2012-2013, NVIDIA CORPORATION. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms and conditions of the GNU General Public License,
|
|
* version 2, as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include <linux/nvmap.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/scatterlist.h>
|
|
#include <linux/err.h>
|
|
#include <linux/platform_data/tegra_smmu.h>
|
|
#include <trace/events/nvhost.h>
|
|
#include "nvmap.h"
|
|
#include "nvhost_job.h"
|
|
#include "chip_support.h"
|
|
#include "nvhost_allocator.h"
|
|
|
|
#define FLAG_NVHOST_MAPPED 1
|
|
#define FLAG_NVMAP_MAPPED 2
|
|
#define FLAG_CARVEOUT 3
|
|
|
|
struct nvhost_nvmap_as_data {
|
|
struct device *dev;
|
|
size_t len;
|
|
struct sg_table *sgt;
|
|
int pin_count;
|
|
int flags;
|
|
struct dma_buf_attachment *attach;
|
|
};
|
|
|
|
struct nvhost_nvmap_data {
|
|
struct mutex lock;
|
|
struct nvhost_nvmap_as_data *as[TEGRA_IOMMU_NUM_ASIDS];
|
|
struct nvhost_comptags comptags;
|
|
struct nvhost_allocator *comptag_allocator;
|
|
};
|
|
|
|
struct mem_mgr *nvhost_nvmap_alloc_mgr(void)
|
|
{
|
|
return (struct mem_mgr *)0x1;
|
|
}
|
|
|
|
void nvhost_nvmap_put_mgr(struct mem_mgr *mgr)
|
|
{
|
|
if ((ulong)mgr != 0x1)
|
|
nvmap_client_put((struct nvmap_client *)mgr);
|
|
}
|
|
|
|
struct mem_mgr *nvhost_nvmap_get_mgr(struct mem_mgr *mgr)
|
|
{
|
|
if ((ulong)mgr == 0x1)
|
|
return (struct mem_mgr *)0x1;
|
|
return (struct mem_mgr *)nvmap_client_get((struct nvmap_client *)mgr);
|
|
}
|
|
|
|
struct mem_mgr *nvhost_nvmap_get_mgr_file(int fd)
|
|
{
|
|
return (struct mem_mgr *)nvmap_client_get_file(fd);
|
|
}
|
|
|
|
struct mem_handle *nvhost_nvmap_alloc(struct mem_mgr *mgr,
|
|
size_t size, size_t align, int flags, unsigned int heap_mask)
|
|
{
|
|
return (struct mem_handle *)nvmap_alloc_dmabuf(
|
|
size, align, flags, heap_mask);
|
|
}
|
|
|
|
void nvhost_nvmap_put(struct mem_mgr *mgr, struct mem_handle *handle)
|
|
{
|
|
if (!handle)
|
|
return;
|
|
dma_buf_put((struct dma_buf *)handle);
|
|
}
|
|
|
|
void delete_priv(void *_priv)
|
|
{
|
|
struct nvhost_nvmap_data *priv = _priv;
|
|
int i;
|
|
for (i = 0; i < ARRAY_SIZE(priv->as); i++) {
|
|
struct nvhost_nvmap_as_data *as = priv->as[i];
|
|
if (!as)
|
|
continue;
|
|
if (as->sgt && as->flags & BIT(FLAG_CARVEOUT))
|
|
nvmap_dmabuf_free_sg_table(NULL, as->sgt);
|
|
kfree(as);
|
|
}
|
|
if (priv->comptags.lines) {
|
|
BUG_ON(!priv->comptag_allocator);
|
|
priv->comptag_allocator->free(priv->comptag_allocator,
|
|
priv->comptags.offset,
|
|
priv->comptags.lines);
|
|
}
|
|
kfree(priv);
|
|
}
|
|
|
|
struct sg_table *nvhost_nvmap_pin(struct mem_mgr *mgr,
|
|
struct mem_handle *handle,
|
|
struct device *dev,
|
|
int rw_flag)
|
|
{
|
|
struct nvhost_nvmap_data *priv;
|
|
struct nvhost_nvmap_as_data *as_priv;
|
|
struct sg_table *sgt;
|
|
int ret;
|
|
struct dma_buf *dmabuf = (struct dma_buf *)handle;
|
|
static DEFINE_MUTEX(priv_lock);
|
|
|
|
/* create the nvhost priv if needed */
|
|
priv = nvmap_get_dmabuf_private(dmabuf);
|
|
if (!priv) {
|
|
mutex_lock(&priv_lock);
|
|
priv = nvmap_get_dmabuf_private(dmabuf);
|
|
if (priv)
|
|
goto priv_exist_or_err;
|
|
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
|
if (!priv) {
|
|
priv = ERR_PTR(-ENOMEM);
|
|
goto priv_exist_or_err;
|
|
}
|
|
mutex_init(&priv->lock);
|
|
nvmap_set_dmabuf_private(dmabuf, priv, delete_priv);
|
|
priv_exist_or_err:
|
|
mutex_unlock(&priv_lock);
|
|
}
|
|
if (IS_ERR(priv))
|
|
return (struct sg_table *)priv;
|
|
|
|
mutex_lock(&priv->lock);
|
|
/* create the per-as part of the priv if needed */
|
|
as_priv = priv->as[tegra_smmu_get_asid(dev)];
|
|
if (!as_priv) {
|
|
u64 size = 0, heap = 0;
|
|
|
|
ret = nvmap_get_dmabuf_param(dmabuf,
|
|
NVMAP_HANDLE_PARAM_SIZE, &size);
|
|
if (ret) {
|
|
mutex_unlock(&priv->lock);
|
|
return ERR_PTR(ret);
|
|
}
|
|
|
|
ret = nvmap_get_dmabuf_param(dmabuf,
|
|
NVMAP_HANDLE_PARAM_HEAP, &heap);
|
|
if (ret) {
|
|
mutex_unlock(&priv->lock);
|
|
return ERR_PTR(ret);
|
|
}
|
|
|
|
as_priv = kzalloc(sizeof(*as_priv), GFP_KERNEL);
|
|
if (!as_priv) {
|
|
mutex_unlock(&priv->lock);
|
|
return ERR_PTR(-ENOMEM);
|
|
}
|
|
if (heap & NVMAP_HEAP_CARVEOUT_MASK)
|
|
as_priv->flags |= BIT(FLAG_CARVEOUT);
|
|
as_priv->len = size;
|
|
as_priv->dev = dev;
|
|
priv->as[tegra_smmu_get_asid(dev)] = as_priv;
|
|
}
|
|
|
|
if (as_priv->flags & BIT(FLAG_CARVEOUT)) {
|
|
if (!as_priv->sgt) {
|
|
as_priv->sgt = nvmap_dmabuf_sg_table(dmabuf);
|
|
if (IS_ERR(as_priv->sgt)) {
|
|
sgt = as_priv->sgt;
|
|
as_priv->sgt = NULL;
|
|
mutex_unlock(&priv->lock);
|
|
return sgt;
|
|
}
|
|
}
|
|
} else if (as_priv->pin_count == 0) {
|
|
as_priv->attach = dma_buf_attach(dmabuf, dev);
|
|
if (IS_ERR(as_priv->attach)) {
|
|
mutex_unlock(&priv->lock);
|
|
return (struct sg_table *)as_priv->attach;
|
|
}
|
|
|
|
as_priv->sgt = dma_buf_map_attachment(as_priv->attach,
|
|
DMA_BIDIRECTIONAL);
|
|
if (IS_ERR(as_priv->sgt)) {
|
|
dma_buf_detach(dmabuf, as_priv->attach);
|
|
as_priv->attach = NULL;
|
|
mutex_unlock(&priv->lock);
|
|
return as_priv->sgt;
|
|
}
|
|
}
|
|
|
|
trace_nvhost_nvmap_pin(dev_name(dev), dmabuf, as_priv->len,
|
|
sg_dma_address(as_priv->sgt->sgl));
|
|
sgt = as_priv->sgt;
|
|
as_priv->pin_count++;
|
|
mutex_unlock(&priv->lock);
|
|
return sgt;
|
|
}
|
|
|
|
void nvhost_nvmap_unpin(struct mem_mgr *mgr, struct mem_handle *handle,
|
|
struct device *dev, struct sg_table *sgt)
|
|
{
|
|
struct dma_buf *dmabuf = (struct dma_buf *)handle;
|
|
struct nvhost_nvmap_data *priv = nvmap_get_dmabuf_private(dmabuf);
|
|
struct nvhost_nvmap_as_data *as_priv;
|
|
dma_addr_t dma_addr;
|
|
|
|
if (IS_ERR(priv) || !priv)
|
|
return;
|
|
|
|
mutex_lock(&priv->lock);
|
|
as_priv = priv->as[tegra_smmu_get_asid(dev)];
|
|
if (as_priv) {
|
|
WARN_ON(as_priv->sgt != sgt);
|
|
as_priv->pin_count--;
|
|
WARN_ON(as_priv->pin_count < 0);
|
|
dma_addr = sg_dma_address(as_priv->sgt->sgl);
|
|
if (as_priv->pin_count == 0 &&
|
|
as_priv->flags & BIT(FLAG_CARVEOUT)) {
|
|
/* do nothing. sgt free is deferred to delete_priv */
|
|
} else if (as_priv->pin_count == 0) {
|
|
dma_buf_unmap_attachment(as_priv->attach,
|
|
as_priv->sgt, DMA_BIDIRECTIONAL);
|
|
dma_buf_detach(dmabuf, as_priv->attach);
|
|
as_priv->attach = NULL;
|
|
}
|
|
trace_nvhost_nvmap_unpin(dev_name(dev),
|
|
dmabuf, as_priv->len, dma_addr);
|
|
}
|
|
mutex_unlock(&priv->lock);
|
|
}
|
|
|
|
void *nvhost_nvmap_mmap(struct mem_handle *handle)
|
|
{
|
|
return dma_buf_vmap((struct dma_buf *)handle);
|
|
}
|
|
|
|
void nvhost_nvmap_munmap(struct mem_handle *handle, void *addr)
|
|
{
|
|
dma_buf_vunmap((struct dma_buf *)handle, addr);
|
|
}
|
|
|
|
void *nvhost_nvmap_kmap(struct mem_handle *handle, unsigned int pagenum)
|
|
{
|
|
return dma_buf_kmap((struct dma_buf *)handle, pagenum);
|
|
}
|
|
|
|
void nvhost_nvmap_kunmap(struct mem_handle *handle, unsigned int pagenum,
|
|
void *addr)
|
|
{
|
|
dma_buf_kunmap((struct dma_buf *)handle, pagenum, addr);
|
|
}
|
|
|
|
struct mem_handle *nvhost_nvmap_get(struct mem_mgr *mgr,
|
|
ulong id, struct platform_device *dev)
|
|
{
|
|
return (struct mem_handle *)
|
|
nvmap_dmabuf_export((struct nvmap_client *)mgr, id);
|
|
}
|
|
|
|
int nvhost_nvmap_get_param(struct mem_mgr *mgr, struct mem_handle *handle,
|
|
u32 param, u64 *result)
|
|
{
|
|
return nvmap_get_dmabuf_param(
|
|
(struct dma_buf *)handle,
|
|
param, result);
|
|
}
|
|
|
|
void nvhost_nvmap_get_comptags(struct mem_handle *mem,
|
|
struct nvhost_comptags *comptags)
|
|
{
|
|
struct nvhost_nvmap_data *priv;
|
|
|
|
priv = nvmap_get_dmabuf_private((struct dma_buf *)mem);
|
|
|
|
BUG_ON(!priv || !comptags);
|
|
|
|
*comptags = priv->comptags;
|
|
}
|
|
|
|
int nvhost_nvmap_alloc_comptags(struct mem_handle *mem,
|
|
struct nvhost_allocator *allocator,
|
|
int lines)
|
|
{
|
|
int err;
|
|
u32 offset = 0;
|
|
struct nvhost_nvmap_data *priv;
|
|
|
|
priv = nvmap_get_dmabuf_private((struct dma_buf *)mem);
|
|
|
|
BUG_ON(!priv);
|
|
BUG_ON(!lines);
|
|
|
|
/* store the allocator so we can use it when we free the ctags */
|
|
priv->comptag_allocator = allocator;
|
|
err = allocator->alloc(allocator, &offset, lines);
|
|
if (!err) {
|
|
priv->comptags.lines = lines;
|
|
priv->comptags.offset = offset;
|
|
}
|
|
return err;
|
|
}
|