kernel-brax3-ubuntu-touch/drivers/remoteproc/mtk_ccd_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

299 lines
7.4 KiB
C

// SPDX-License-Identifier: GPL-2.0
//
// Copyright (c) 2018 MediaTek Inc.
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/compiler.h>
#include <linux/dma-buf.h>
#include <linux/dma-heap.h>
#include <linux/dma-mapping.h>
#include <linux/dma-direction.h>
#include <linux/module.h>
#include <linux/fdtable.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/scatterlist.h>
#include <linux/sched/signal.h>
#include <linux/sched/task.h>
#include <linux/platform_data/mtk_ccd.h>
#include <linux/remoteproc/mtk_ccd_mem.h>
#include <linux/rpmsg/mtk_ccd_rpmsg.h>
#include <uapi/linux/mtk_ccd_controls.h>
#include <uapi/linux/dma-heap.h>
#define CCD_ALLOCATE_MAX_BUFFER_SIZE 0x20000000UL /*512MB*/
struct mtk_ccd_buf;
struct mtk_ccd_buf {
struct device *dev;
void *vaddr;
unsigned long size;
dma_addr_t dma_addr;
/* DMABUF related */
struct sg_table *dma_sgt;
struct dma_buf *dbuf;
struct dma_buf_attachment *db_attach;
struct dma_buf_map map;
};
static struct mtk_ccd_buf *mtk_ccd_buf_alloc(
struct device *dev, unsigned long size)
{
struct mtk_ccd_buf *buf;
struct dma_heap *dma_heap;
struct dma_buf_map map;
memset(&map, 0, sizeof(map));
buf = kzalloc(sizeof(*buf), GFP_KERNEL);
if (!buf)
return ERR_PTR(-ENOMEM);
dma_heap = dma_heap_find("mtk_mm-uncached");
if (!dma_heap) {
pr_info("dma_heap find fail\n");
goto fail_alloc;
}
buf->dbuf = dma_heap_buffer_alloc(dma_heap, size,
O_RDWR | O_CLOEXEC, DMA_HEAP_VALID_HEAP_FLAGS);
if (IS_ERR(buf->dbuf)) {
pr_info("dma_heap buffer alloc fail\n");
goto fail_alloc;
}
buf->db_attach = dma_buf_attach(buf->dbuf, dev);
if (IS_ERR(buf->db_attach)) {
pr_info("dma_heap attach fail\n");
goto fail_alloc;
}
buf->dma_sgt = dma_buf_map_attachment(buf->db_attach,
DMA_BIDIRECTIONAL);
if (IS_ERR(buf->dma_sgt)) {
pr_info("dma_heap map failed\n");
goto fail_map_attach;
}
if (dma_buf_vmap(buf->dbuf, &map) < 0) {
pr_info("dma_heap vmap failed\n");
goto fail_vmap;
}
buf->vaddr = map.vaddr;
buf->map = map;
buf->dma_addr = sg_dma_address(buf->dma_sgt->sgl);
buf->dev = get_device(dev);
buf->size = size;
return buf;
fail_vmap:
dma_buf_unmap_attachment(
buf->db_attach, buf->dma_sgt, DMA_BIDIRECTIONAL);
fail_map_attach:
dma_buf_detach(buf->dbuf, buf->db_attach);
fail_alloc:
kfree(buf);
return ERR_PTR(-ENOMEM);
}
static void mtk_ccd_buf_put(struct mtk_ccd_buf *buf)
{
/* free va */
if (buf->vaddr) {
dma_buf_vunmap(buf->dbuf, &buf->map);
}
/* free iova */
if (buf->db_attach && buf->dma_sgt)
dma_buf_unmap_attachment(
buf->db_attach, buf->dma_sgt, DMA_BIDIRECTIONAL);
if (buf->dbuf && buf->db_attach)
dma_buf_detach(buf->dbuf, buf->db_attach);
/* decrease file count */
dma_heap_buffer_free(buf->dbuf);
put_device(buf->dev);
kfree(buf);
}
static dma_addr_t mtk_ccd_buf_get_daddr(struct mtk_ccd_buf *buf)
{
return buf->dma_addr;
}
static void *mtk_ccd_buf_get_vaddr(struct mtk_ccd_buf *buf)
{
if (!buf->vaddr && buf->db_attach) {
if (dma_buf_vmap(buf->db_attach->dmabuf, &buf->map) < 0) {
pr_info("dma_heap vmap failed\n");
return NULL;
}
buf->vaddr = buf->map.vaddr;
}
return buf->vaddr;
}
struct dma_buf *mtk_ccd_get_buffer_dmabuf(struct mtk_ccd *ccd, void *mem_priv)
{
struct mtk_ccd_buf *buf = mem_priv;
if (ccd == NULL || buf == NULL) {
pr_info("mtk_ccd or mem_priv is NULL\n");
return NULL;
}
return buf->dbuf;
}
EXPORT_SYMBOL_GPL(mtk_ccd_get_buffer_dmabuf);
struct mtk_ccd_memory *mtk_ccd_mem_init(struct device *dev)
{
struct mtk_ccd_memory *ccd_memory;
ccd_memory = kzalloc(sizeof(*ccd_memory), GFP_KERNEL);
if (!ccd_memory)
return NULL;
ccd_memory->dev = dev;
ccd_memory->num_buffers = 0;
mutex_init(&ccd_memory->mmap_lock);
return ccd_memory;
}
EXPORT_SYMBOL_GPL(mtk_ccd_mem_init);
void mtk_ccd_mem_release(struct mtk_ccd *ccd)
{
struct mtk_ccd_memory *ccd_memory = ccd->ccd_memory;
kfree(ccd_memory);
}
EXPORT_SYMBOL_GPL(mtk_ccd_mem_release);
void *mtk_ccd_get_buffer(struct mtk_ccd *ccd,
struct mem_obj *mem_buff_data)
{
void *va;
dma_addr_t da;
unsigned int buffers;
struct mtk_ccd_buf *buf;
struct mtk_ccd_mem *ccd_buffer;
struct mtk_ccd_memory *ccd_memory = ccd->ccd_memory;
mem_buff_data->iova = 0;
mem_buff_data->va = NULL;
mutex_lock(&ccd_memory->mmap_lock);
buffers = ccd_memory->num_buffers;
if (mem_buff_data->len > CCD_ALLOCATE_MAX_BUFFER_SIZE ||
mem_buff_data->len == 0U ||
buffers >= MAX_NUMBER_OF_BUFFER) {
dev_info(ccd_memory->dev,
"%s: Failed: buffer len = %u num_buffers = %d !!\n",
__func__, mem_buff_data->len, buffers);
mutex_unlock(&ccd_memory->mmap_lock);
return ERR_PTR(-EINVAL);
}
ccd_buffer = &ccd_memory->bufs[buffers];
buf = mtk_ccd_buf_alloc(ccd_memory->dev, mem_buff_data->len);
ccd_buffer->mem_priv = buf;
ccd_buffer->size = mem_buff_data->len;
if (IS_ERR(ccd_buffer->mem_priv)) {
dev_info(ccd_memory->dev, "%s: CCD buf allocation failed\n",
__func__);
mutex_unlock(&ccd_memory->mmap_lock);
return ERR_PTR(-ENOMEM);
}
va = mtk_ccd_buf_get_vaddr(buf);
da = mtk_ccd_buf_get_daddr(buf);
mem_buff_data->iova = da;
mem_buff_data->va = va;
ccd_memory->num_buffers++;
mutex_unlock(&ccd_memory->mmap_lock);
dev_info(ccd_memory->dev,
"Num_bufs = %d iova = %pad va = %p size = %d priv = %p\n",
ccd_memory->num_buffers, &mem_buff_data->iova,
mem_buff_data->va,
(unsigned int)ccd_buffer->size,
ccd_buffer->mem_priv);
return ccd_buffer->mem_priv;
}
EXPORT_SYMBOL_GPL(mtk_ccd_get_buffer);
int mtk_ccd_put_buffer(struct mtk_ccd *ccd,
struct mem_obj *mem_buff_data)
{
struct mtk_ccd_buf *buf;
void *va;
dma_addr_t da;
int ret = -EINVAL;
struct mtk_ccd_mem *ccd_buffer;
unsigned int buffer, num_buffers, last_buffer;
struct mtk_ccd_memory *ccd_memory = ccd->ccd_memory;
mutex_lock(&ccd_memory->mmap_lock);
num_buffers = ccd_memory->num_buffers;
if (num_buffers != 0U) {
for (buffer = 0; buffer < num_buffers; buffer++) {
ccd_buffer = &ccd_memory->bufs[buffer];
buf = (struct mtk_ccd_buf *)ccd_buffer->mem_priv;
va = mtk_ccd_buf_get_vaddr(buf);
da = mtk_ccd_buf_get_daddr(buf);
if (mem_buff_data->va == va &&
mem_buff_data->len == ccd_buffer->size) {
dev_info(ccd_memory->dev,
"Free buff = %d iova = %pad va = %p, queue_num = %d, f_count = %d\n",
buffer, &mem_buff_data->iova,
mem_buff_data->va,
num_buffers, atomic_long_read(&buf->dbuf->file->f_count));
mtk_ccd_buf_put(buf);
last_buffer = num_buffers - 1U;
if (last_buffer != buffer)
ccd_memory->bufs[buffer] =
ccd_memory->bufs[last_buffer];
ccd_memory->bufs[last_buffer].mem_priv = NULL;
ccd_memory->bufs[last_buffer].size = 0;
ccd_memory->num_buffers--;
ret = 0;
break;
}
}
}
mutex_unlock(&ccd_memory->mmap_lock);
if (ret != 0)
dev_info(ccd_memory->dev,
"Can not free memory va %p iova %pad len %u!\n",
mem_buff_data->va, &mem_buff_data->iova,
mem_buff_data->len);
return ret;
}
EXPORT_SYMBOL_GPL(mtk_ccd_put_buffer);
int mtk_ccd_get_buffer_fd(struct mtk_ccd *ccd, void *mem_priv)
{
struct mtk_ccd_buf *buf = mem_priv;
int fd;
if (ccd == NULL || buf == NULL) {
pr_info("mtk_ccd or buf is NULL\n");
return -EINVAL;
}
fd = dma_buf_fd(buf->dbuf, O_RDWR | O_CLOEXEC);
if (fd < 0) {
pr_info("mtk_ccd couldn't get fd from dma_buf\n");
return -EINVAL;
}
/* increase file count, close fd in userspace driver*/
dma_buf_get(fd);
return fd;
}
EXPORT_SYMBOL_GPL(mtk_ccd_get_buffer_fd);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("MediaTek ccd memory interface");