247 lines
5.9 KiB
C

/*
* drivers/video/tegra/host/dmabuf.c
*
* Tegra Graphics Host DMA-BUF support
*
* Copyright (c) 2012-2013, NVIDIA Corporation.
*
* 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/dma-buf.h>
#include <linux/nvhost.h>
#include <linux/nvmap.h>
#include <linux/slab.h>
#include "chip_support.h"
#include "nvhost_allocator.h"
#include "nvhost_memmgr.h"
#include "../nvmap/nvmap_ioctl.h"
struct nvhost_dmabuf_data {
struct nvhost_comptags comptags;
struct nvhost_allocator *comptag_allocator;
};
static inline struct dma_buf_attachment *to_dmabuf_att(struct mem_handle *h)
{
#ifdef CONFIG_NVMAP_USE_FD_FOR_HANDLE
struct dma_buf *dmabuf = (struct dma_buf *) h;
return nvmap_foreign_dmabuf_get_att(dmabuf);
#else
return (struct dma_buf_attachment *)h;
#endif
}
static inline struct dma_buf *to_dmabuf(struct mem_handle *h)
{
return to_dmabuf_att(h)->dmabuf;
}
struct mem_handle *nvhost_dmabuf_alloc(size_t size, size_t align, int flags)
{
/* TODO: Add allocation via DMA Mapping API */
WARN_ON(1);
return NULL;
}
void nvhost_dmabuf_put(struct mem_handle *handle)
{
#ifdef CONFIG_NVMAP_USE_FD_FOR_HANDLE
struct dma_buf_attachment *attach = to_dmabuf_att(handle);
struct dma_buf *dmabuf = attach->dmabuf;
if (nvmap_foreign_dmabuf_put(dmabuf))
dma_buf_detach(dmabuf, attach);
dma_buf_put(dmabuf);
#endif
}
/* Callback from the DMA-BUF exporter's release function */
static void delete_drvdata(void *_drvdata)
{
struct nvhost_dmabuf_data *drvdata = _drvdata;
if (WARN_ON(!drvdata))
return;
if (drvdata->comptags.lines) {
if (WARN_ON(!drvdata->comptag_allocator))
goto free;
drvdata->comptag_allocator->free(drvdata->comptag_allocator,
drvdata->comptags.offset,
drvdata->comptags.lines);
}
free:
kfree(drvdata);
}
struct sg_table *nvhost_dmabuf_pin(struct mem_handle *handle,
struct device *dev)
{
struct dma_buf_attachment *attach = to_dmabuf_att(handle);
struct dma_buf *dmabuf = attach->dmabuf;
struct nvhost_dmabuf_data *drvdata;
int err;
drvdata = dma_buf_get_drvdata(dmabuf, dev);
if (IS_ERR(drvdata))
return (void *)drvdata;
if (drvdata)
goto done;
/* Allocate drvdata struct */
drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL);
if (!drvdata)
return ERR_PTR(-ENOMEM);
err = dma_buf_set_drvdata(dmabuf, dev, drvdata, delete_drvdata);
if (err) {
kfree(drvdata);
return ERR_PTR(err);
}
/* Use attachment's priv to store device information */
attach->priv = dev;
done:
return dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
}
void nvhost_dmabuf_unpin(struct mem_handle *handle, struct sg_table *sgt)
{
struct dma_buf_attachment *attach = to_dmabuf_att(handle);
dma_buf_unmap_attachment(attach, sgt, DMA_BIDIRECTIONAL);
}
void *nvhost_dmabuf_mmap(struct mem_handle *handle)
{
return dma_buf_vmap(to_dmabuf(handle));
}
void nvhost_dmabuf_munmap(struct mem_handle *handle, void *addr)
{
dma_buf_vunmap(to_dmabuf(handle), addr);
}
void *nvhost_dmabuf_kmap(struct mem_handle *handle, unsigned int pagenum)
{
return dma_buf_kmap(to_dmabuf(handle), pagenum);
}
void nvhost_dmabuf_kunmap(struct mem_handle *handle, unsigned int pagenum,
void *addr)
{
dma_buf_kunmap(to_dmabuf(handle), pagenum, addr);
}
struct mem_handle *nvhost_dmabuf_get(ulong id, struct platform_device *dev)
{
#ifdef CONFIG_NVMAP_USE_FD_FOR_HANDLE
struct dma_buf *dmabuf;
struct mem_handle *h;
dmabuf = dma_buf_get(id);
h = (struct mem_handle *)dmabuf;
nvmap_foreign_dmabuf_get(dmabuf);
return h;
#else
WARN_ON(1);
#endif
}
int nvhost_dmabuf_get_param(struct mem_mgr *memmgr, struct mem_handle *handle,
u32 param, u64 *result)
{
struct dma_buf_attachment *attach = to_dmabuf_att(handle);
switch(param)
{
case NVMAP_HANDLE_PARAM_SIZE:
*result = attach->dmabuf->size;
return 0;
case NVMAP_HANDLE_PARAM_ALIGNMENT:
if (attach->dmabuf->size & (128 * 1024 - 1))
*result = 4096;
else
*result = 128 * 1024;
return 0;
case NVMAP_HANDLE_PARAM_BASE:
*result = 0;
return 0;
case NVMAP_HANDLE_PARAM_HEAP:
*result = NVMAP_HEAP_IOVMM;
return 0;
case NVMAP_HANDLE_PARAM_KIND:
*result = 0xfe;
return 0;
}
printk(KERN_ERR"Unimplemented parameter %d handle %p\n",param,handle);
return -EINVAL;
}
void nvhost_dmabuf_get_comptags(struct mem_handle *handle,
struct nvhost_comptags *comptags)
{
struct dma_buf_attachment *attach = to_dmabuf_att(handle);
struct dma_buf *dmabuf = attach->dmabuf;
struct device *dev = attach->priv;
struct nvhost_dmabuf_data *drvdata;
if (WARN_ON(!dev))
return;
drvdata = dma_buf_get_drvdata(dmabuf, dev);
if (WARN_ON(IS_ERR(drvdata)) || WARN_ON(!drvdata))
return;
*comptags = drvdata->comptags;
}
int nvhost_dmabuf_alloc_comptags(struct mem_handle *handle,
struct nvhost_allocator *allocator,
int lines)
{
struct dma_buf_attachment *attach = to_dmabuf_att(handle);
struct dma_buf *dmabuf = attach->dmabuf;
struct device *dev = attach->priv;
struct nvhost_dmabuf_data *drvdata;
u32 offset = 0;
int err;
if (WARN_ON(!lines))
return -EINVAL;
if (WARN_ON(!dev))
return -EINVAL;
drvdata = dma_buf_get_drvdata(dmabuf, dev);
if (WARN_ON(IS_ERR(drvdata)) || WARN_ON(!drvdata))
return -EINVAL;
/* store the allocator so we can use it when we free the ctags */
drvdata->comptag_allocator = allocator;
err = allocator->alloc(allocator, &offset, lines);
if (!err) {
drvdata->comptags.lines = lines;
drvdata->comptags.offset = offset;
}
return err;
}