/*
* Copyright (c) 2012, 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 .
*/
#include
#include
#include
#include
#include
#include
struct vb2_dc_conf {
struct device *dev;
struct nvmap_client *nvmap_client;
};
struct vb2_dc_buf {
struct vb2_dc_conf *conf;
void *vaddr;
dma_addr_t paddr;
unsigned long size;
struct vm_area_struct *vma;
atomic_t refcount;
struct vb2_vmarea_handler handler;
struct nvmap_handle_ref *nvmap_ref;
};
static void vb2_dma_nvmap_put(void *buf_priv);
static void *vb2_dma_nvmap_alloc(void *alloc_ctx, unsigned long size)
{
struct vb2_dc_conf *conf = alloc_ctx;
struct vb2_dc_buf *buf;
int ret;
buf = kzalloc(sizeof *buf, GFP_KERNEL);
if (!buf) {
ret = -ENOMEM;
goto exit;
}
buf->nvmap_ref = nvmap_alloc(conf->nvmap_client, size, 32,
NVMAP_HANDLE_CACHEABLE, NVMAP_HEAP_SYSMEM);
if (IS_ERR(buf->nvmap_ref)) {
dev_err(conf->dev, "nvmap_alloc failed\n");
ret = -ENOMEM;
goto exit_free;
}
buf->paddr = nvmap_pin(conf->nvmap_client, buf->nvmap_ref);
if (IS_ERR_VALUE(buf->paddr)) {
dev_err(conf->dev, "nvmap_pin failed\n");
ret = -ENOMEM;
goto exit_dealloc;
}
buf->vaddr = nvmap_mmap(buf->nvmap_ref);
if (!buf->vaddr) {
dev_err(conf->dev, "nvmap_mmap failed\n");
ret = -ENOMEM;
goto exit_unpin;
}
buf->conf = conf;
buf->size = size;
buf->handler.refcount = &buf->refcount;
buf->handler.put = vb2_dma_nvmap_put;
buf->handler.arg = buf;
atomic_inc(&buf->refcount);
return buf;
exit_unpin:
nvmap_unpin(conf->nvmap_client, buf->nvmap_ref);
exit_dealloc:
nvmap_free(conf->nvmap_client, buf->nvmap_ref);
exit_free:
kfree(buf);
exit:
return ERR_PTR(ret);
}
static void vb2_dma_nvmap_put(void *buf_priv)
{
struct vb2_dc_buf *buf = buf_priv;
if (atomic_dec_and_test(&buf->refcount)) {
nvmap_munmap(buf->nvmap_ref, buf->vaddr);
nvmap_unpin(buf->conf->nvmap_client, buf->nvmap_ref);
nvmap_free(buf->conf->nvmap_client, buf->nvmap_ref);
kfree(buf);
}
}
static void *vb2_dma_nvmap_cookie(void *buf_priv)
{
struct vb2_dc_buf *buf = buf_priv;
return &buf->paddr;
}
static void *vb2_dma_nvmap_vaddr(void *buf_priv)
{
struct vb2_dc_buf *buf = buf_priv;
if (!buf)
return 0;
return buf->vaddr;
}
static unsigned int vb2_dma_nvmap_num_users(void *buf_priv)
{
struct vb2_dc_buf *buf = buf_priv;
return atomic_read(&buf->refcount);
}
static int vb2_dma_nvmap_mmap(void *buf_priv, struct vm_area_struct *vma)
{
struct vb2_dc_buf *buf = buf_priv;
if (!buf) {
printk(KERN_ERR "No buffer to map\n");
return -EINVAL;
}
return vb2_mmap_pfn_range(vma, buf->paddr, buf->size,
&vb2_common_vm_ops, &buf->handler);
}
static void *vb2_dma_nvmap_get_userptr(void *alloc_ctx, unsigned long vaddr,
unsigned long size, int write)
{
struct vb2_dc_buf *buf;
struct vm_area_struct *vma;
dma_addr_t paddr = 0;
int ret;
buf = kzalloc(sizeof *buf, GFP_KERNEL);
if (!buf)
return ERR_PTR(-ENOMEM);
ret = vb2_get_contig_userptr(vaddr, size, &vma, &paddr);
if (ret) {
printk(KERN_ERR "Failed acquiring VMA for vaddr 0x%08lx\n",
vaddr);
kfree(buf);
return ERR_PTR(ret);
}
buf->size = size;
buf->paddr = paddr;
buf->vma = vma;
return buf;
}
static void vb2_dma_nvmap_put_userptr(void *mem_priv)
{
struct vb2_dc_buf *buf = mem_priv;
if (!buf)
return;
vb2_put_vma(buf->vma);
kfree(buf);
}
const struct vb2_mem_ops vb2_dma_nvmap_memops = {
.alloc = vb2_dma_nvmap_alloc,
.put = vb2_dma_nvmap_put,
.cookie = vb2_dma_nvmap_cookie,
.vaddr = vb2_dma_nvmap_vaddr,
.mmap = vb2_dma_nvmap_mmap,
.get_userptr = vb2_dma_nvmap_get_userptr,
.put_userptr = vb2_dma_nvmap_put_userptr,
.num_users = vb2_dma_nvmap_num_users,
};
EXPORT_SYMBOL_GPL(vb2_dma_nvmap_memops);
void *vb2_dma_nvmap_init_ctx(struct device *dev)
{
struct vb2_dc_conf *conf;
int ret;
conf = kzalloc(sizeof *conf, GFP_KERNEL);
if (!conf) {
ret = -ENOMEM;
goto exit;
}
conf->dev = dev;
conf->nvmap_client = nvmap_create_client(nvmap_dev,
"videobuf2-dma-nvmap");
if (!conf->nvmap_client) {
ret = -ENOMEM;
goto exit_free;
}
return conf;
exit_free:
kfree(conf);
exit:
return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(vb2_dma_nvmap_init_ctx);
void vb2_dma_nvmap_cleanup_ctx(void *alloc_ctx)
{
struct vb2_dc_conf *conf = alloc_ctx;
nvmap_client_put(conf->nvmap_client);
kfree(alloc_ctx);
}
EXPORT_SYMBOL_GPL(vb2_dma_nvmap_cleanup_ctx);
MODULE_DESCRIPTION("DMA-nvmap memory handling routines for videobuf2");
MODULE_AUTHOR("Andrew Chew ");
MODULE_LICENSE("GPL");