// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2021 MediaTek Inc. */ #include #include #include #include #include #include #include "mdw_cmn.h" #include "mdw_mem.h" #include "mdw_mem_rsc.h" struct mdw_mem_aram { dma_addr_t dma_addr; uint32_t dma_size; uint64_t addr; uint32_t sid; struct mdw_mem *m; struct list_head attachments; struct mutex mtx; }; struct mdw_mem_aram_attachment { struct mdw_mem_aram *am; struct mdw_mem *m; struct sg_table sgt; struct list_head node; }; static int mdw_mem_aram_attach(struct dma_buf *dbuf, struct dma_buf_attachment *attach) { struct mdw_mem_aram_attachment *am_attach = NULL; struct mdw_mem *m = dbuf->priv; struct mdw_mem_aram *am = m->priv; int ret = 0; /* check type */ if (m->type >= MDW_MEM_TYPE_MAX || m->type == MDW_MEM_TYPE_MAIN) { mdw_drv_err("invalid type(%u)\n", m->type); return -EINVAL; } /* alloc attach */ am_attach = kzalloc(sizeof(*am_attach), GFP_KERNEL); if (!am_attach) { ret = -ENOMEM; goto out; } /* alloc sg table */ ret = sg_alloc_table(&am_attach->sgt, 1, GFP_KERNEL); if (ret) goto free_attach; /* map vlm */ if (m->type == MDW_MEM_TYPE_SYSTEM_ISP) { mdw_mem_debug("s(0x%llx) bypass vlm map\n", (uint64_t)m->mpriv); am->dma_addr = am->addr; } else { ret = mdw_rvs_mem_map((uint64_t)m->mpriv, am->sid, &am->dma_addr); if (ret) { mdw_drv_err("apumem(0x%llx/%u) map fail\n", (uint64_t)m->mpriv, am->sid); goto free_table; } } sg_dma_address(am_attach->sgt.sgl) = am->dma_addr; sg_dma_len(am_attach->sgt.sgl) = am->dma_size; am_attach->am = am; am_attach->m = m; attach->priv = am_attach; mdw_mem_debug("s(0x%llx) sg(%p) m(%u/0x%llx/0x%x)\n", (uint64_t)m->mpriv, &am_attach->sgt, am->sid, sg_dma_address(am_attach->sgt.sgl), sg_dma_len(am_attach->sgt.sgl)); mutex_lock(&am->mtx); list_add_tail(&am_attach->node, &am->attachments); mutex_unlock(&am->mtx); goto out; free_table: sg_free_table(&am_attach->sgt); free_attach: kfree(am_attach); out: return ret; } static void mdw_mem_aram_detach(struct dma_buf *dbuf, struct dma_buf_attachment *attach) { struct mdw_mem_aram_attachment *am_attach = attach->priv; struct mdw_mem_aram *am = am_attach->am; struct mdw_mem *m = am->m; int ret = 0; mutex_lock(&am->mtx); list_del(&am_attach->node); mutex_unlock(&am->mtx); if (m->type == MDW_MEM_TYPE_SYSTEM_ISP) { mdw_mem_debug("s(0x%llx) bypass vlm unmap\n", (uint64_t)m->mpriv); } else { ret = mdw_rvs_mem_unmap((uint64_t)m->mpriv, am->sid); if (ret) { mdw_drv_warn("s(0x%llx) unmap sid(%u) fail\n", (uint64_t)m->mpriv, am->sid); } } sg_free_table(&am_attach->sgt); kfree(am_attach); mdw_mem_debug("\n"); } static struct sg_table *mdw_mem_aram_map_dma(struct dma_buf_attachment *attach, enum dma_data_direction dir) { struct mdw_mem_aram_attachment *am_attach = attach->priv; mdw_mem_debug("\n"); return &am_attach->sgt; } static void mdw_mem_aram_unmap_dma(struct dma_buf_attachment *attach, struct sg_table *sgt, enum dma_data_direction dir) { mdw_mem_debug("\n"); } static void mdw_mem_aram_unprepare(struct mdw_mem_aram *am) { mdw_mem_debug("type(%u)sid(%u)m(0x%llx/0x%x)\n", am->m->type, am->sid, am->dma_addr, am->dma_size); switch (am->m->type) { case MDW_MEM_TYPE_VLM: case MDW_MEM_TYPE_LOCAL: case MDW_MEM_TYPE_SYSTEM: case MDW_MEM_TYPE_SYSTEM_ISP: case MDW_MEM_TYPE_SYSTEM_APU: if (mdw_rvs_mem_free(am->sid)) mdw_mem_debug("free apumem type(%u)sid(%u)m(0x%llx/0x%x) fail\n", am->m->type, am->sid, am->dma_addr, am->dma_size); break; default: mdw_drv_warn("not support apumem(%u)\n", am->m->type); break; } } static void mdw_mem_aram_release(struct dma_buf *dbuf) { struct mdw_mem *m = dbuf->priv; struct mdw_mem_aram *am = m->priv; mdw_mem_debug("\n"); mdw_mem_aram_unprepare(am); kfree(am); m->release(m); } static struct dma_buf_ops mdw_mem_aram_ops = { .attach = mdw_mem_aram_attach, .detach = mdw_mem_aram_detach, .map_dma_buf = mdw_mem_aram_map_dma, .unmap_dma_buf = mdw_mem_aram_unmap_dma, .release = mdw_mem_aram_release, }; static int mdw_mem_aram_prepare(struct mdw_fpriv *mpriv, struct mdw_mem_aram *am) { int ret = 0; mdw_mem_debug("type(%u)size(0x%x)\n", am->m->type, am->m->size); switch (am->m->type) { case MDW_MEM_TYPE_VLM: case MDW_MEM_TYPE_LOCAL: case MDW_MEM_TYPE_SYSTEM: case MDW_MEM_TYPE_SYSTEM_ISP: case MDW_MEM_TYPE_SYSTEM_APU: ret = mdw_rvs_mem_alloc(am->m->type, am->m->size, &am->addr, &am->sid); if (ret) { mdw_drv_err("alloc apuram(%u/%u) fail(%d)\n", am->m->type, am->m->size, ret); } else { am->dma_size = am->m->size; } break; default: mdw_drv_warn("not support apumem(%u)\n", am->m->type); ret = -EINVAL; break; } return ret; } static int mdw_mem_aram_bind(void *session, struct mdw_mem *m) { struct mdw_mem_aram *am = (struct mdw_mem_aram *)m->priv; mdw_mem_debug("s(0x%llx) m(0x%llx/%u/%u)\n", (uint64_t)session, (uint64_t)m, m->type, am->sid); if (m->mpriv == session) { mdw_mem_debug("s(0x%llx) don't need bind\n"); return 0; } if (m->type == MDW_MEM_TYPE_SYSTEM_ISP) { mdw_mem_debug("s(0x%llx) bypass vlm bind\n", (uint64_t)m->mpriv); return 0; } return mdw_rvs_mem_import((uint64_t)session, am->sid); } static void mdw_mem_aram_unbind(void *session, struct mdw_mem *m) { struct mdw_mem_aram *am = (struct mdw_mem_aram *)m->priv; mdw_mem_debug("s(0x%llx) m(0x%llx/%u/%u)\n", (uint64_t)session, (uint64_t)m, m->type, am->sid); if (m->mpriv == session) { mdw_mem_debug("s(0x%llx) don't need unbind\n"); return; } if (m->type == MDW_MEM_TYPE_SYSTEM_ISP) { mdw_mem_debug("s(0x%llx) bypass vlm unbind\n", (uint64_t)m->mpriv); return; } mdw_rvs_mem_unimport((uint64_t)session, am->sid); } int mdw_mem_aram_alloc(struct mdw_mem *m) { struct mdw_mem_aram *am = NULL; DEFINE_DMA_BUF_EXPORT_INFO(exp_info); int ret = 0; /* alloc mdw dma-buf container */ am = kzalloc(sizeof(*am), GFP_KERNEL); if (!am) return -ENOMEM; mutex_init(&am->mtx); INIT_LIST_HEAD(&am->attachments); am->m = m; m->bind = mdw_mem_aram_bind; m->unbind = mdw_mem_aram_unbind; m->priv = am; ret = mdw_mem_aram_prepare(m->mpriv, am); if (ret) { mdw_drv_err("prepare apumem(%u) fail\n", m->type); goto free_aram; } /* export as dma-buf */ exp_info.ops = &mdw_mem_aram_ops; exp_info.size = am->dma_size; exp_info.flags = O_RDWR | O_CLOEXEC; exp_info.priv = m; m->dbuf = dma_buf_export(&exp_info); if (IS_ERR(m->dbuf)) { mdw_drv_err("dma_buf_export Fail\n"); ret = -ENOMEM; goto unprepare_aram; } goto out; unprepare_aram: mdw_mem_aram_unprepare(am); free_aram: kfree(am); out: return ret; }