kernel-brax3-ubuntu-touch/drivers/media/platform/mtk-vcu/mtk_vcodec_mem.c
erascape f319b992b1 kernel-5.15: Initial import brax3 UT kernel
* halium configs enabled

Signed-off-by: erascape <erascape@proton.me>
2025-09-23 15:17:10 +00:00

563 lines
16 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2017 MediaTek Inc.
* Author: Yunfei Dong <yunfei.dong@mediatek.com>
*/
#include "mtk_vcodec_mem.h"
#define vcu_mem_dbg_log(fmt, arg...) do { \
if (vcu_queue->enable_vcu_dbg_log) \
pr_info(fmt, ##arg); \
} while (0)
#undef pr_debug
#define pr_debug vcu_mem_dbg_log
static void vcu_buf_remove(struct mtk_vcu_queue *vcu_queue, unsigned int buffer)
{
unsigned int last_buffer;
last_buffer = vcu_queue->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->bufs[last_buffer].dbuf = NULL;
atomic_set(&vcu_queue->bufs[last_buffer].ref_cnt, 0);
vcu_queue->num_buffers--;
}
struct mtk_vcu_queue *mtk_vcu_mem_init(struct device *dev,
struct cmdq_client *cmdq_clt)
{
struct mtk_vcu_queue *vcu_queue = NULL;
vcu_queue = kzalloc(sizeof(struct mtk_vcu_queue), GFP_KERNEL);
if (vcu_queue == NULL)
return NULL;
INIT_LIST_HEAD(&vcu_queue->pa_pages.list);
vcu_queue->mem_ops = &vb2_dma_contig_memops;
vcu_queue->dev = dev;
vcu_queue->cmdq_clt = cmdq_clt;
vcu_queue->num_buffers = 0;
vcu_queue->map_buf_pa = 0;
mutex_init(&vcu_queue->mmap_lock);
mutex_init(&vcu_queue->dev_lock);
vcu_queue->vb_queue.mem_ops = &vb2_dma_contig_memops;
vcu_queue->vb_queue.dma_dir = 0;
vcu_queue->vb_queue.dma_attrs = 0;
vcu_queue->vb_queue.gfp_flags = 0;
/*
* vcu_queue->vb_queue.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
* vcu_queue->vb_queue.io_modes = VB2_DMABUF | VB2_MMAP;
* vcu_queue->vb_queue.drv_priv = ctx;
* vcu_queue->vb_queue.buf_struct_size = sizeof(struct mtk_video_dec_buf);
* vcu_queue->vb_queue.ops = &mtk_vdec_vb2_ops;
* vcu_queue->vb_queue.mem_ops = &vb2_dma_contig_memops;
* vcu_queue->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
* vcu_queue->vb_queue.lock = &ctx->q_mutex;
* vcu_queue->vb_queue.dev = &ctx->dev->plat_dev->dev;
* vcu_queue->vb_queue.allow_zero_bytesused = 1;
* ret = vb2_queue_init(&vcu_queue->vb_queue);
* if (ret) {
* mtk_v4l2_err("Failed to initialize videobuf2 queue(capture)");
* }
*/
return vcu_queue;
}
void mtk_vcu_mem_release(struct mtk_vcu_queue *vcu_queue)
{
struct mtk_vcu_mem *vcu_buffer;
unsigned int buffer;
struct vcu_pa_pages *tmp;
struct list_head *p, *q;
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];
if (vcu_buffer->dbuf == NULL)
vcu_queue->mem_ops->put(vcu_buffer->mem_priv);
else
fput(vcu_buffer->dbuf->file);
pr_debug("Free %d dbuf = %p size = %d mem_priv = %lx ref_cnt = %d\n",
buffer, vcu_buffer->dbuf,
(unsigned int)vcu_buffer->size,
(unsigned long)vcu_buffer->mem_priv,
atomic_read(&vcu_buffer->ref_cnt));
}
}
list_for_each_safe(p, q, &vcu_queue->pa_pages.list) {
tmp = list_entry(p, struct vcu_pa_pages, list);
cmdq_mbox_buf_free(
vcu_queue->cmdq_clt,
(void *)(unsigned long)tmp->kva,
(dma_addr_t)tmp->pa);
pr_info("Free cmdq pa %llx ref_cnt = %d\n", tmp->pa,
atomic_read(&tmp->ref_cnt));
list_del(p);
kfree(tmp);
}
mutex_unlock(&vcu_queue->mmap_lock);
kfree(vcu_queue);
vcu_queue = NULL;
}
void *mtk_vcu_set_buffer(struct mtk_vcu_queue *vcu_queue,
struct mem_obj *mem_buff_data, struct vb2_buffer *src_vb,
struct vb2_buffer *dst_vb)
{
struct mtk_vcu_mem *vcu_buffer;
unsigned int num_buffers, plane;
unsigned int buffer;
dma_addr_t *dma_addr = NULL;
struct dma_buf *dbuf = NULL;
int op;
pr_debug("[%s] %d iova = %llx src_vb = %p dst_vb = %p\n",
__func__, vcu_queue->num_buffers, mem_buff_data->iova,
src_vb, dst_vb);
num_buffers = vcu_queue->num_buffers;
if (mem_buff_data->len > CODEC_ALLOCATE_MAX_BUFFER_SIZE ||
mem_buff_data->len == 0U || num_buffers >= CODEC_MAX_BUFFER) {
pr_info("Set buffer fail: buffer len = %u num_buffers = %d !!\n",
mem_buff_data->len, num_buffers);
return ERR_PTR(-EINVAL);
}
mutex_lock(&vcu_queue->mmap_lock);
for (buffer = 0; buffer < num_buffers; buffer++) {
vcu_buffer = &vcu_queue->bufs[buffer];
if (mem_buff_data->iova == (u64)vcu_buffer->iova) {
atomic_inc(&vcu_buffer->ref_cnt);
mutex_unlock(&vcu_queue->mmap_lock);
return vcu_buffer->mem_priv;
}
}
vcu_buffer = &vcu_queue->bufs[num_buffers];
if (dbuf == NULL && src_vb != NULL)
for (plane = 0; plane < src_vb->num_planes; plane++) {
dma_addr = src_vb->vb2_queue->mem_ops->cookie(src_vb,
src_vb->planes[plane].mem_priv);
if (*dma_addr == mem_buff_data->iova) {
dbuf = src_vb->planes[plane].dbuf;
vcu_buffer->size = src_vb->planes[plane].length;
vcu_buffer->mem_priv = src_vb->planes[plane].mem_priv;
op = DMA_TO_DEVICE;
pr_debug("src size = %d mem_buff_data len = %d\n",
(unsigned int)vcu_buffer->size,
(unsigned int)mem_buff_data->len);
}
}
if (dbuf == NULL && dst_vb != NULL)
for (plane = 0; plane < dst_vb->num_planes; plane++) {
dma_addr = dst_vb->vb2_queue->mem_ops->cookie(dst_vb,
dst_vb->planes[plane].mem_priv);
if (*dma_addr == mem_buff_data->iova) {
dbuf = dst_vb->planes[plane].dbuf;
vcu_buffer->size = dst_vb->planes[plane].length;
vcu_buffer->mem_priv = dst_vb->planes[plane].mem_priv;
op = DMA_FROM_DEVICE;
pr_debug("dst size = %d mem_buff_data len = %d\n",
(unsigned int)vcu_buffer->size,
(unsigned int)mem_buff_data->len);
}
}
if (dbuf == NULL) {
mutex_unlock(&vcu_queue->mmap_lock);
pr_debug("Set buffer not found: buffer len = %u iova = %llx !!\n",
mem_buff_data->len, mem_buff_data->iova);
return ERR_PTR(-ENOMEM);
}
vcu_buffer->dbuf = dbuf;
vcu_buffer->iova = *dma_addr;
get_file(dbuf->file);
vcu_queue->num_buffers++;
atomic_set(&vcu_buffer->ref_cnt, 1);
mutex_unlock(&vcu_queue->mmap_lock);
pr_debug("[%s] Num_buffers = %d iova = %llx dbuf = %p size = %d mem_priv = %lx\n",
__func__, vcu_queue->num_buffers, mem_buff_data->iova,
vcu_buffer->dbuf, (unsigned int)vcu_buffer->size,
(unsigned long)vcu_buffer->mem_priv);
return vcu_buffer->mem_priv;
}
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 > CODEC_ALLOCATE_MAX_BUFFER_SIZE ||
mem_buff_data->len == 0U || buffers >= CODEC_MAX_BUFFER) {
pr_info("Get buffer fail: buffer len = %u 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->vb.vb2_queue = &vcu_queue->vb_queue;
vcu_buffer->mem_priv = vcu_queue->mem_ops->alloc(&vcu_buffer->vb, vcu_queue->dev,
mem_buff_data->len);
vcu_buffer->size = mem_buff_data->len;
vcu_buffer->dbuf = NULL;
if (IS_ERR_OR_NULL(vcu_buffer->mem_priv)) {
mutex_unlock(&vcu_queue->mmap_lock);
return ERR_PTR(-ENOMEM);
}
cook = vcu_queue->mem_ops->vaddr(&vcu_buffer->vb, vcu_buffer->mem_priv);
dma_addr = vcu_queue->mem_ops->cookie(&vcu_buffer->vb, vcu_buffer->mem_priv);
mem_buff_data->iova = *(dma_addr_t *)dma_addr;
vcu_buffer->iova = *(dma_addr_t *)dma_addr;
mem_buff_data->va = CODEC_MSK((unsigned long)cook);
mem_buff_data->pa = 0;
vcu_queue->num_buffers++;
mutex_unlock(&vcu_queue->mmap_lock);
atomic_set(&vcu_buffer->ref_cnt, 1);
pr_debug("[%s] Num_buffers = %d iova = %llx va = %llx size = %d mem_priv = %lx\n",
__func__, vcu_queue->num_buffers, mem_buff_data->iova,
mem_buff_data->va, (unsigned int)vcu_buffer->size,
(unsigned long)vcu_buffer->mem_priv);
return vcu_buffer->mem_priv;
}
void *mtk_vcu_get_page(struct mtk_vcu_queue *vcu_queue,
struct mem_obj *mem_buff_data)
{
dma_addr_t temp_pa = 0;
void *mem_priv;
struct vcu_pa_pages *tmp;
mem_priv = cmdq_mbox_buf_alloc(vcu_queue->cmdq_clt, &temp_pa);
tmp = kmalloc(sizeof(struct vcu_pa_pages), GFP_KERNEL);
if (!tmp)
return ERR_PTR(-ENOMEM);
mutex_lock(&vcu_queue->mmap_lock);
tmp->pa = temp_pa;
mem_buff_data->pa = temp_pa;
tmp->kva = (unsigned long)mem_priv;
mem_buff_data->va = CODEC_MSK((unsigned long)mem_priv);
mem_buff_data->iova = 0;
atomic_set(&tmp->ref_cnt, 1);
list_add_tail(&tmp->list, &vcu_queue->pa_pages.list);
mutex_unlock(&vcu_queue->mmap_lock);
return mem_priv;
}
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;
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];
if (vcu_buffer->dbuf != NULL)
continue;
if (vcu_buffer->mem_priv == NULL || vcu_buffer->size == 0) {
pr_info("[VCU][Error] %s remove invalid vcu_queue bufs[%u] in num_buffers %u (mem_priv 0x%x size %d ref_cnt %d)\n",
__func__, buffer, num_buffers,
vcu_buffer->mem_priv, vcu_buffer->size,
atomic_read(&vcu_buffer->ref_cnt));
vcu_buf_remove(vcu_queue, buffer);
continue;
}
cook = vcu_queue->mem_ops->vaddr(&vcu_buffer->vb, vcu_buffer->mem_priv);
dma_addr =
vcu_queue->mem_ops->cookie(&vcu_buffer->vb, vcu_buffer->mem_priv);
if (mem_buff_data->va == CODEC_MSK((unsigned long)cook) &&
mem_buff_data->iova == *(dma_addr_t *)dma_addr &&
mem_buff_data->len == vcu_buffer->size &&
atomic_read(&vcu_buffer->ref_cnt) == 1) {
pr_debug("Free buff = %d iova = %llx 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);
atomic_dec(&vcu_buffer->ref_cnt);
vcu_buf_remove(vcu_queue, buffer);
ret = 0;
break;
}
}
}
mutex_unlock(&vcu_queue->mmap_lock);
if (ret != 0)
pr_info("Can not free memory va %llx iova %llx len %u!\n",
mem_buff_data->va, mem_buff_data->iova, mem_buff_data->len);
return ret;
}
int mtk_vcu_free_page(struct mtk_vcu_queue *vcu_queue,
struct mem_obj *mem_buff_data)
{
int ret = -EINVAL;
struct vcu_pa_pages *tmp;
struct list_head *p, *q;
mutex_lock(&vcu_queue->mmap_lock);
list_for_each_safe(p, q, &vcu_queue->pa_pages.list) {
tmp = list_entry(p, struct vcu_pa_pages, list);
if (tmp->pa == mem_buff_data->pa &&
CODEC_MSK(tmp->kva) == mem_buff_data->va &&
atomic_read(&tmp->ref_cnt) == 1) {
ret = 0;
cmdq_mbox_buf_free(
vcu_queue->cmdq_clt,
(void *)(unsigned long)
tmp->kva,
(dma_addr_t)mem_buff_data->pa);
atomic_dec(&tmp->ref_cnt);
list_del(p);
kfree(tmp);
break;
}
}
mutex_unlock(&vcu_queue->mmap_lock);
if (ret != 0)
pr_info("Can not free memory va %llx pa %llx len %u!\n",
mem_buff_data->va, mem_buff_data->pa, mem_buff_data->len);
return ret;
}
void mtk_vcu_buffer_ref_dec(struct mtk_vcu_queue *vcu_queue,
void *mem_priv)
{
struct mtk_vcu_mem *vcu_buffer;
unsigned int buffer, num_buffers;
mutex_lock(&vcu_queue->mmap_lock);
num_buffers = vcu_queue->num_buffers;
for (buffer = 0; buffer < num_buffers; buffer++) {
vcu_buffer = &vcu_queue->bufs[buffer];
if (vcu_buffer->mem_priv == mem_priv) {
if (atomic_read(&vcu_buffer->ref_cnt) > 0)
atomic_dec(&vcu_buffer->ref_cnt);
else
pr_info("[VCU][Error] %s fail\n", __func__);
if (atomic_read(&vcu_buffer->ref_cnt) == 0
&& vcu_buffer->dbuf != NULL) {
pr_debug("Free IO buff = %d iova = %llx mem_priv = %llx, queue_num = %d\n",
buffer, vcu_buffer->iova,
vcu_buffer->mem_priv, num_buffers);
fput(vcu_buffer->dbuf->file);
vcu_buf_remove(vcu_queue, buffer);
}
}
}
mutex_unlock(&vcu_queue->mmap_lock);
}
void vcu_io_buffer_cache_sync(struct device *dev,
struct dma_buf *dbuf, int op)
{
struct dma_buf_attachment *buf_att;
struct sg_table *sgt;
buf_att = dma_buf_attach(dbuf, dev);
if (IS_ERR(buf_att)) {
pr_info("failed to attach dmabuf\n");
return;
}
sgt = dma_buf_map_attachment(buf_att, op);
if (IS_ERR_OR_NULL(sgt)) {
pr_info("%s Error getting dmabuf scatterlist %d\n", __func__, sgt);
dma_buf_detach(dbuf, buf_att);
return;
}
dma_sync_sg_for_device(dev, sgt->sgl, sgt->orig_nents, op);
dma_buf_unmap_attachment(buf_att, sgt, op);
dma_buf_detach(dbuf, buf_att);
}
int vcu_buffer_flush_all(struct device *dev, struct mtk_vcu_queue *vcu_queue)
{
struct mtk_vcu_mem *vcu_buffer;
unsigned int buffer, num_buffers;
struct dma_buf *dbuf = NULL;
unsigned long flags = 0;
mutex_lock(&vcu_queue->mmap_lock);
num_buffers = vcu_queue->num_buffers;
if (num_buffers == 0U) {
mutex_unlock(&vcu_queue->mmap_lock);
return 0;
}
for (buffer = 0; buffer < num_buffers; buffer++) {
vcu_buffer = &vcu_queue->bufs[buffer];
pr_debug("Cache clean %s buffer=%d iova=%lx size=%d num=%d\n",
(vcu_buffer->dbuf == NULL) ? "working" : "io",
buffer, (unsigned int long)vcu_buffer->iova,
(unsigned int)vcu_buffer->size, num_buffers);
if (vcu_buffer->dbuf == NULL)
dbuf = vcu_queue->mem_ops->get_dmabuf(
&vcu_buffer->vb, vcu_buffer->mem_priv, flags);
else
dbuf = vcu_buffer->dbuf;
vcu_io_buffer_cache_sync(dev, dbuf, DMA_TO_DEVICE);
if (vcu_buffer->dbuf == NULL)
dma_buf_put(dbuf);
}
mutex_unlock(&vcu_queue->mmap_lock);
return 0;
}
int vcu_buffer_cache_sync(struct device *dev, struct mtk_vcu_queue *vcu_queue,
dma_addr_t dma_addr, size_t size, int op)
{
struct mtk_vcu_mem *vcu_buffer;
unsigned int num_buffers = 0;
unsigned int buffer = 0;
struct dma_buf *dbuf = NULL;
unsigned long flags = 0;
mutex_lock(&vcu_queue->mmap_lock);
num_buffers = vcu_queue->num_buffers;
if (num_buffers == 0U) {
pr_info("Cache %s buffer fail, iova = %lx, size = %d, vcu no buffers\n",
(op == DMA_TO_DEVICE) ? "flush" : "invalidate",
(unsigned long)dma_addr, (unsigned int)size);
mutex_unlock(&vcu_queue->mmap_lock);
return -1;
}
for (buffer = 0; buffer < num_buffers; buffer++) {
vcu_buffer = &vcu_queue->bufs[buffer];
if ((dma_addr + size) <= (vcu_buffer->iova + vcu_buffer->size) &&
dma_addr >= vcu_buffer->iova) {
pr_debug("Cache %s %s buffer iova=%lx range=%d (%lx %d)\n",
(op == DMA_TO_DEVICE) ? "clean" : "invalidate",
(vcu_buffer->dbuf == NULL) ? "working" : "io",
(unsigned long)dma_addr, (unsigned int)size,
(unsigned long)vcu_buffer->iova,
(unsigned int)vcu_buffer->size);
if (vcu_buffer->dbuf == NULL)
dbuf = vcu_queue->mem_ops->get_dmabuf(
&vcu_buffer->vb, vcu_buffer->mem_priv, flags);
else
dbuf = vcu_buffer->dbuf;
vcu_io_buffer_cache_sync(dev, dbuf, op);
if (vcu_buffer->dbuf == NULL)
dma_buf_put(dbuf);
mutex_unlock(&vcu_queue->mmap_lock);
return 0;
}
}
pr_info("Cache %s buffer fail, iova = %lx, size = %d\n",
(op == DMA_TO_DEVICE) ? "flush" : "invalidate",
(unsigned long)dma_addr, (unsigned int)size);
mutex_unlock(&vcu_queue->mmap_lock);
return -1;
}
static int vcu_dmabuf_get_dma_addr(struct device *dev,
struct mtk_vcu_queue *vcu_queue,
int share_fd, dma_addr_t *dma_addr, int *is_sec,
int add_fcnt)
{
struct dma_buf *buf = dma_buf_get(share_fd); /* f_cnt +1 */
struct dma_buf_attachment *attach;
struct sg_table *table;
int ret = 0, sec = 0;
const char *reason;
if (IS_ERR(buf)) {
reason = "buf_get";
ret = (int)PTR_ERR(buf);
goto out;
}
attach = dma_buf_attach(buf, dev);
if (IS_ERR(attach)) {
reason = "buf_attach";
ret = (int)PTR_ERR(attach);
goto put_buf;
}
table = dma_buf_map_attachment(attach, 0);
if (IS_ERR(table)) {
reason = "buf_map";
ret = (int)PTR_ERR(table);
goto detach_buf;
}
*dma_addr = sg_dma_address(table->sgl);
sec = sg_page(table->sgl) ? 0 : 1;
if (is_sec)
*is_sec = sec;
if (add_fcnt) {
pr_info("%s: %s, share_id %d, f_cnt +1\n", __func__,
dev_name(dev), share_fd);
get_dma_buf(buf);
}
dma_buf_unmap_attachment(attach, table, 0);
detach_buf:
dma_buf_detach(buf, attach);
put_buf:
dma_buf_put(buf);
out:
if (ret)
pr_info("%s: %s fail(%s:%d) share_id %d\n", __func__,
dev_name(dev), reason, ret, share_fd);
else
pr_debug("%s: %s, share_id %d, dma_addr 0x%lx(sec %d, add_cnt %d)\n",
__func__, dev_name(dev),
share_fd, (unsigned long)(*dma_addr),
sec, add_fcnt);
return ret;
}
int mtk_vcu_get_dma_addr(struct device *dev, struct mtk_vcu_queue *vcu_queue,
int share_fd, dma_addr_t *dma_addr, int *is_sec)
{
if (!dma_addr)
return -EINVAL;
return vcu_dmabuf_get_dma_addr(dev, vcu_queue, share_fd, dma_addr, is_sec,
0);
}