blob: 0f1d5c6994a1de75a47776b0b592382f9303e2e2 [file] [log] [blame]
/*
* Copyright (c) 2017 MediaTek Inc.
* Author: Yunfei Dong <yunfei.dong@mediatek.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that 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.
*/
#include "mtk_vcodec_mem.h"
struct mtk_vcu_queue *mtk_vcu_dec_init(struct device *dev)
{
struct mtk_vcu_queue *vcu_queue;
pr_debug("Allocate new vcu queue !\n");
vcu_queue = kzalloc(sizeof(struct mtk_vcu_queue), GFP_KERNEL);
if (vcu_queue == NULL) {
pr_err("Allocate new vcu queue fail!\n");
return NULL;
}
vcu_queue->mem_ops = &vb2_dma_contig_memops;
vcu_queue->dev = dev;
vcu_queue->num_buffers = 0;
vcu_queue->map_buf = 0;
vcu_queue->map_type = 0;
mutex_init(&vcu_queue->mmap_lock);
return vcu_queue;
}
void mtk_vcu_dec_release(struct mtk_vcu_queue *vcu_queue)
{
struct mtk_vcu_mem *vcu_buffer;
unsigned int buffer;
mutex_lock(&vcu_queue->mmap_lock);
pr_debug("Release vcu queue !\n");
if (vcu_queue->num_buffers != 0) {
for (buffer = 0; buffer < vcu_queue->num_buffers; buffer++) {
vcu_buffer = &vcu_queue->bufs[buffer];
vcu_queue->mem_ops->put(vcu_buffer->mem_priv);
vcu_queue->bufs[buffer].mem_priv = NULL;
vcu_queue->bufs[buffer].size = 0;
}
}
mutex_unlock(&vcu_queue->mmap_lock);
kfree(vcu_queue);
vcu_queue = NULL;
}
void *mtk_vcu_get_buffer(struct mtk_vcu_queue *vcu_queue,
struct mem_obj *mem_buff_data)
{
void *cook, *dma_addr;
struct mtk_vcu_mem *vcu_buffer;
unsigned int buffers;
buffers = vcu_queue->num_buffers;
if (mem_buff_data->len > DEC_ALLOCATE_MAX_BUFFER_SIZE ||
mem_buff_data->len == 0U || buffers >= DEC_MAX_BUFFER) {
pr_err("Get buffer fail: buffer len = %ld num_buffers = %d !!\n",
mem_buff_data->len, buffers);
return ERR_PTR(-EINVAL);
}
mutex_lock(&vcu_queue->mmap_lock);
vcu_buffer = &vcu_queue->bufs[buffers];
vcu_buffer->mem_priv = vcu_queue->mem_ops->alloc(vcu_queue->dev, 0,
mem_buff_data->len, 0, 0);
vcu_buffer->size = mem_buff_data->len;
if (IS_ERR(vcu_buffer->mem_priv)) {
mutex_unlock(&vcu_queue->mmap_lock);
goto free;
}
cook = vcu_queue->mem_ops->vaddr(vcu_buffer->mem_priv);
dma_addr = vcu_queue->mem_ops->cookie(vcu_buffer->mem_priv);
mem_buff_data->iova = *(dma_addr_t *)dma_addr;
mem_buff_data->va = (unsigned long)cook;
vcu_queue->num_buffers++;
mutex_unlock(&vcu_queue->mmap_lock);
return vcu_buffer->mem_priv;
free:
vcu_queue->mem_ops->put(vcu_buffer->mem_priv);
return ERR_PTR(-ENOMEM);
}
int mtk_vcu_free_buffer(struct mtk_vcu_queue *vcu_queue,
struct mem_obj *mem_buff_data)
{
struct mtk_vcu_mem *vcu_buffer;
void *cook, *dma_addr;
unsigned int buffer, num_buffers, last_buffer;
int ret = -EINVAL;
mutex_lock(&vcu_queue->mmap_lock);
num_buffers = vcu_queue->num_buffers;
if (num_buffers != 0U) {
for (buffer = 0; buffer < num_buffers; buffer++) {
vcu_buffer = &vcu_queue->bufs[buffer];
cook = vcu_queue->mem_ops->vaddr(vcu_buffer->mem_priv);
dma_addr = vcu_queue->mem_ops->cookie(vcu_buffer->mem_priv);
if (mem_buff_data->va == (unsigned long)cook &&
mem_buff_data->iova == *(dma_addr_t *)dma_addr &&
mem_buff_data->len == vcu_buffer->size) {
pr_debug("Free buff = %d pa = %lx va = %llx, queue_num = %d\n",
buffer, mem_buff_data->iova, mem_buff_data->va,
num_buffers);
vcu_queue->mem_ops->put(vcu_buffer->mem_priv);
last_buffer = num_buffers - 1U;
if (last_buffer != buffer)
vcu_queue->bufs[buffer] =
vcu_queue->bufs[last_buffer];
vcu_queue->bufs[last_buffer].mem_priv = NULL;
vcu_queue->bufs[last_buffer].size = 0;
vcu_queue->num_buffers--;
ret = 0;
break;
}
}
}
mutex_unlock(&vcu_queue->mmap_lock);
if (ret != 0)
pr_err("Can not free memory va %llx iova %lx len %lu!\n",
mem_buff_data->va, mem_buff_data->iova, mem_buff_data->len);
return ret;
}