From 4887b2b5ad0bcd7fdea8f21a94e6e3851d7b2741 Mon Sep 17 00:00:00 2001 From: tangdaoyong Date: Fri, 20 Sep 2024 17:54:03 +0800 Subject: [PATCH 182/219] feat:decode with user space buffer Changelogs: 1. decode with user space buffer. pin_user_pages -> sg_alloc_table_from_pages -> dma_map_sg Signed-off-by: tangdaoyong --- drivers/staging/media/eswin/vdec/Makefile | 3 +- drivers/staging/media/eswin/vdec/hantro_dec.c | 69 ++ drivers/staging/media/eswin/vdec/hantrodec.h | 19 +- .../staging/media/eswin/vdec/vdec_allocator.c | 887 ++++++++++++++++++ .../staging/media/eswin/vdec/vdec_allocator.h | 20 + 5 files changed, 996 insertions(+), 2 deletions(-) create mode 100644 drivers/staging/media/eswin/vdec/vdec_allocator.c create mode 100644 drivers/staging/media/eswin/vdec/vdec_allocator.h diff --git a/drivers/staging/media/eswin/vdec/Makefile b/drivers/staging/media/eswin/vdec/Makefile index 3983ec82f153..0788e1021086 100644 --- a/drivers/staging/media/eswin/vdec/Makefile +++ b/drivers/staging/media/eswin/vdec/Makefile @@ -8,6 +8,7 @@ es_vdec-objs := hantro_dec.o \ vcmdswhwregisters.o \ subsys.o \ hantro_afbc.o \ - dts_parser.o + dts_parser.o \ + vdec_allocator.o obj-$(CONFIG_VIDEO_ESWIN_VDEC) += es_vdec.o \ No newline at end of file diff --git a/drivers/staging/media/eswin/vdec/hantro_dec.c b/drivers/staging/media/eswin/vdec/hantro_dec.c index 88adf1bd86d6..cc4e04b3c5c3 100644 --- a/drivers/staging/media/eswin/vdec/hantro_dec.c +++ b/drivers/staging/media/eswin/vdec/hantro_dec.c @@ -104,6 +104,7 @@ #include "hantroaxife.h" #include "hantroafbc.h" #include "dts_parser.h" +#include "vdec_allocator.h" #define LOG_TAG DEC_DEV_NAME ":main" #include "vc_drv_log.h" @@ -547,6 +548,8 @@ static char *IoctlCmdStr(unsigned int cmd) #ifdef SUPPORT_DMA_HEAP IOCTL_CMD_STR_CASE(HANTRODEC_IOC_DMA_HEAP_GET_IOVA); #endif + IOCTL_CMD_STR_CASE(HANTRODEC_IOC_ATTACH_USER_MEM); + IOCTL_CMD_STR_CASE(HANTRODEC_IOC_DETACH_USER_MEM); default: return "Invalid ioctl cmd"; } @@ -2509,6 +2512,70 @@ static long hantrodec_ioctl(struct file *filp, unsigned int cmd, return 0; } #endif + case HANTRODEC_IOC_ATTACH_USER_MEM: { + user_memory_desc desc; + struct device *dev = NULL; + + if (copy_from_user(&desc, (void __user *)arg, sizeof(desc)) != 0) { + return -EFAULT; + } + + LOG_DBG("attach user memory: memory %px, size: %lu, nid: %u\n", desc.memory, desc.size, desc.nid); + if (!desc.memory || !desc.size) { + LOG_ERR("invalid param, addr=%px, size=%lu\n", desc.memory, desc.size); + return -EINVAL; + } + do { + if (desc.nid == 1) { + if (platformdev_d1) { + dev = &platformdev_d1->dev; + } else { + break; + } + } else if (!desc.nid) { + if (platformdev) { + dev = &platformdev->dev; + } else { + break; + } + } else { + break; + } + } while (0); + if (!dev) { + LOG_ERR("%d::invalid nid: %u\n", __LINE__, desc.nid); + return -ENODEV; + } + LOG_DBG("%d: nid: %u, dev: %px\n", __LINE__, desc.nid, dev); + + if (vdec_attach_user_memory(filp, dev, &desc)) { + return -EFAULT; + } + tmp = copy_to_user((u32 __user *)arg, &desc, sizeof(desc)); + if (tmp) { + LOG_ERR("%s %d: copy_from_user failed, returned %li\n", __func__, __LINE__, tmp); + return -EFAULT; + } + } break; + case HANTRODEC_IOC_DETACH_USER_MEM: { + user_memory_desc desc; + if (copy_from_user(&desc, (void __user *)arg, sizeof(desc)) != 0) { + return -EFAULT; + } + + if (vdec_detach_user_memory(filp, &desc)) { + return -EFAULT; + } + } break; + case HANTRODEC_IOC_SYNC_USER_MEM_CACHE: { + user_memory_desc desc; + if (copy_from_user(&desc, (void __user *)arg, sizeof(desc)) != 0) { + return -EFAULT; + } + if (vdec_sync_user_memory_cache(filp, &desc, ES_CACHE_CLEAN)) { + return -EFAULT; + } + } break; default: { if (_IOC_TYPE(cmd) == HANTRO_IOC_MMU) { volatile u8 *mmu_hwregs[MAX_SUBSYS_NUM][2]; @@ -2560,6 +2627,7 @@ static int hantrodec_open(struct inode *inode, struct file *filp) for (u32 core_id = 0; core_id < DEC_CORE_NUM; core_id ++) { atomic_set(&(fp_priv->core_tasks[core_id]), 0); } + init_bi_list((bi_list*)&fp_priv->user_memory_list); filp->private_data = (void *)fp_priv; LOG_DBG("dev opened\n"); @@ -2604,6 +2672,7 @@ static int hantrodec_release(struct inode *inode, ReleasePostProcessor(dev, n); } } + vdec_clean_user_memory(&fp_priv->user_memory_list); MMURelease(filp, hantrodec_data.hwregs[0][HW_MMU]); diff --git a/drivers/staging/media/eswin/vdec/hantrodec.h b/drivers/staging/media/eswin/vdec/hantrodec.h index 4f597b740eb2..da90216883cc 100644 --- a/drivers/staging/media/eswin/vdec/hantrodec.h +++ b/drivers/staging/media/eswin/vdec/hantrodec.h @@ -74,6 +74,7 @@ #endif #include "hantrovcmd.h" +#include "bidirect_list.h" enum CoreType { /* Decoder */ @@ -165,9 +166,16 @@ struct filp_priv { #endif void *dev; atomic_t core_tasks[DEC_CORE_NUM]; /** for task count of 4 cores*/ + bi_list user_memory_list; }; #endif +typedef struct { + void *memory; /* user space memory addr */ + size_t size; /* memory size */ + __u32 nid; /* numa id */ +} user_memory_desc; + /* Use 'k' as magic number */ #define HANTRODEC_IOC_MAGIC 'k' @@ -251,6 +259,15 @@ struct filp_priv { #define HANTRODEC_IOC_DMA_HEAP_FD_SPLIT \ _IOR(HANTRODEC_IOC_MAGIC, 35, struct dmabuf_split *) -#define HANTRODEC_IOC_MAXNR 35 +#define HANTRODEC_IOC_ATTACH_USER_MEM \ + _IOWR(HANTRODEC_IOC_MAGIC, 36, user_memory_desc *) + +#define HANTRODEC_IOC_DETACH_USER_MEM \ + _IOR(HANTRODEC_IOC_MAGIC, 37, user_memory_desc *) + +#define HANTRODEC_IOC_SYNC_USER_MEM_CACHE \ + _IOR(HANTRODEC_IOC_MAGIC, 38, user_memory_desc *) + +#define HANTRODEC_IOC_MAXNR 38 #endif /* !_HANTRODEC_H_ */ diff --git a/drivers/staging/media/eswin/vdec/vdec_allocator.c b/drivers/staging/media/eswin/vdec/vdec_allocator.c new file mode 100644 index 000000000000..f4a27d2e3894 --- /dev/null +++ b/drivers/staging/media/eswin/vdec/vdec_allocator.c @@ -0,0 +1,887 @@ +#include "vdec_allocator.h" +#include +#include + +#include +#include +#include +#include +#include +#include +#include "hantrodec.h" +#include "bidirect_list.h" + +#define LOG_TAG DEC_DEV_NAME ":alloc" +#include "vc_drv_log.h" + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(4, 20, 17) && \ + !defined(CONFIG_ARCH_NO_SG_CHAIN)) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) && \ + (defined(ARCH_HAS_SG_CHAIN) || defined(CONFIG_ARCH_HAS_SG_CHAIN))) +# define gcdUSE_LINUX_SG_TABLE_API 1 +#else +# define gcdUSE_LINUX_SG_TABLE_API 0 +#endif + +#define gcdSUPPRESS_OOM_MESSAGE 1 + +#if gcdSUPPRESS_OOM_MESSAGE +# define gcdNOWARN __GFP_NOWARN +#else +# define gcdNOWARN 0 +#endif + +#define gcdUSING_PFN_FOLLOW 0 + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) +# define current_mm_mmap_sem current->mm->mmap_lock +#else +# define current_mm_mmap_sem current->mm->mmap_sem +#endif + +#ifndef untagged_addr +# define untagged_addr(addr) (addr) +#endif + +const static int g_iommu = 1; + +enum um_desc_type { + UM_PHYSICAL_MAP, + UM_PAGE_MAP, + UM_PFN_MAP, +}; + +/* Descriptor of a user memory imported. */ +struct um_desc { + int type; + + union { + /* UM_PHYSICAL_MAP. */ + unsigned long physical; + + /* UM_PAGE_MAP. */ + struct { + struct page **pages; + }; + + /* UM_PFN_MAP. */ + struct { + unsigned long *pfns; + int *refs; + int pfns_valid; + }; + }; + + struct sg_table sgt; + + /* contiguous chunks, does not include padding pages. */ + int chunk_count; + + unsigned long vm_flags; + unsigned long user_vaddr; + size_t size; + unsigned long offset; + dma_addr_t dmaHandle; + + size_t pageCount; + size_t extraPage; + unsigned int alloc_from_res; + struct device *dev; +}; + +static int _UserMemoryCache(struct um_desc *um, ES_CACHE_OPERATION Operation); + +static void *alloc_memory(size_t bytes) +{ + if (bytes > PAGE_SIZE) + return vzalloc(bytes); + else + return kzalloc(bytes, GFP_KERNEL | __GFP_NOWARN); +} + +static void free_memory(void *ptr) +{ + if (is_vmalloc_addr(ptr)) + vfree(ptr); + else + kfree(ptr); +} + +#if 0 +static int import_physical_map(int iommu, struct device *dev, + struct um_desc *um, unsigned long phys) +{ + um->type = UM_PHYSICAL_MAP; + um->physical = phys & PAGE_MASK; + um->chunk_count = 1; + + if (iommu) { + dma_addr_t dmaHandle; + size_t size = um->size + (phys & (~PAGE_MASK)); + unsigned long pfn = phys >> PAGE_SHIFT; + + if (pfn_valid(pfn)) + dmaHandle = dma_map_page(dev, pfn_to_page(pfn), + 0, size, DMA_BIDIRECTIONAL); + else +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0) + dmaHandle = dma_map_resource(dev, pfn << PAGE_SHIFT, + size, DMA_BIDIRECTIONAL, 0); +#else + dmaHandle = dma_map_page(dev, pfn_to_page(pfn), + 0, size, DMA_BIDIRECTIONAL); +#endif + + if (dma_mapping_error(dev, dmaHandle)) + return -ENOMEM; + + um->dmaHandle = dmaHandle; + } + + return 0; +} +#endif + +static int +import_page_map(struct device *dev, struct um_desc *um, + unsigned long addr, size_t page_count, size_t size, unsigned long flags) +{ + int i; + int result; + struct page **pages; +#if 0 + if ((addr & (cache_line_size() - 1)) || (size & (cache_line_size() - 1))) { + /* Not cpu cacheline size aligned, can not support. */ + LOG_ERR("Not cpu cacheline size aligned, can not support. addr: %lx, size: %lu, cache_line_size: %d\n", + addr, size, cache_line_size()); + return -EINVAL; + } +#endif + pages = alloc_memory(page_count * sizeof(void *)); + if (!pages) + return -ENOMEM; + + down_read(¤t_mm_mmap_sem); + +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 0) + result = pin_user_pages(addr & PAGE_MASK, page_count, +#elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 6, 0) + result = get_user_pages(current, current->mm, addr & PAGE_MASK, page_count, +#else + result = get_user_pages(addr & PAGE_MASK, page_count, +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0) || defined(CONFIG_PPC) + (flags & VM_WRITE) ? FOLL_WRITE : 0, +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 1) + (flags & VM_WRITE) ? 1 : 0, 0, +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 168) + (flags & VM_WRITE) ? FOLL_WRITE : 0, +#else + (flags & VM_WRITE) ? 1 : 0, 0, +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6,4,0) + pages); +#else + pages, NULL); +#endif + + + up_read(¤t_mm_mmap_sem); + + if (result < page_count) { + for (i = 0; i < result; i++) { + if (pages[i]) +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 0) + unpin_user_page(pages[i]); +#else + put_page(pages[i]); +#endif + } + + LOG_ERR("%s:%d:: result: %d, page count: %lu, addr: %lx\n", __func__, __LINE__, result, page_count, addr); + free_memory(pages); + return -ENODEV; + } + + um->chunk_count = 1; + for (i = 1; i < page_count; i++) { + if (page_to_pfn(pages[i]) != page_to_pfn(pages[i - 1]) + 1) + ++um->chunk_count; + } + + if (!um->alloc_from_res) { +#if gcdUSE_LINUX_SG_TABLE_API + result = sg_alloc_table_from_pages(&um->sgt, pages, page_count, + addr & ~PAGE_MASK, size, GFP_KERNEL | gcdNOWARN); + +#else + result = alloc_sg_list_from_pages(&um->sgt.sgl, pages, page_count, + addr & ~PAGE_MASK, size, &um->sgt.nents); + + um->sgt.orig_nents = um->sgt.nents; +#endif + if (unlikely(result < 0)) { + LOG_WARN("%s: sg_alloc_table_from_pages failed\n", __func__); + goto error; + } + + //result = dma_map_sgtable(dev, &um->sgt, DMA_BIDIRECTIONAL, 0); + result = dma_map_sg(dev, um->sgt.sgl, um->sgt.nents, DMA_BIDIRECTIONAL); + if (g_iommu) { + um->dmaHandle = sg_dma_address(um->sgt.sgl); + } + + dma_sync_sg_for_cpu(dev, um->sgt.sgl, um->sgt.nents, DMA_FROM_DEVICE); + } + + um->type = UM_PAGE_MAP; + um->pages = pages; + + return 0; + +error: + for (i = 0; i < page_count; i++) { + if (pages[i]) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 0) + unpin_user_page(pages[i]); +#else + put_page(pages[i]); +#endif + } + } +#if gcdUSE_LINUX_SG_TABLE_API + sg_free_table(&um->sgt); +#else + kfree(um->sgt.sgl); +#endif + + free_memory(pages); + + return result; +} + + +static int +import_pfn_map(struct device *dev, struct um_desc *um, + unsigned long addr, size_t pfn_count) +{ + int i; + struct vm_area_struct *vma; + unsigned long *pfns; + int *refs; + struct page **pages = NULL; + int result = 0; + size_t pageCount = 0; + unsigned int data = 0; + + if (!current->mm) + return -ENOTTY; + + down_read(¤t_mm_mmap_sem); + vma = find_vma(current->mm, addr); +#if !gcdUSING_PFN_FOLLOW && (LINUX_VERSION_CODE < KERNEL_VERSION(6, 5, 0)) + up_read(¤t_mm_mmap_sem); +#endif + + if (!vma) + return -ENOTTY; + + pfns = (unsigned long *)alloc_memory(pfn_count * sizeof(unsigned long)); + + if (!pfns) + return -ENOMEM; + + refs = (int *)alloc_memory(pfn_count * sizeof(int)); + + if (!refs) { + free_memory(pfns); + return -ENOMEM; + } + + pages = alloc_memory(pfn_count * sizeof(void *)); + if (!pages) { + free_memory(pfns); + free_memory(refs); + return -ENOMEM; + } + + for (i = 0; i < pfn_count; i++) { +#if gcdUSING_PFN_FOLLOW || (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 5, 0)) + int ret = 0; + ret = follow_pfn(vma, addr, &pfns[i]); + if (ret < 0) { + /* Case maybe provides unmapped addr. */ + data = *(unsigned int *)addr; + ret = follow_pfn(vma, addr, &pfns[i]); + + if (ret < 0) { + up_read(¤t_mm_mmap_sem); + goto err; + } + } +#else + /* protect pfns[i] */ + spinlock_t *ptl; + pgd_t *pgd; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) + p4d_t *p4d; +#endif + pud_t *pud; + pmd_t *pmd; + pte_t *pte; + + pgd = pgd_offset(current->mm, addr); + if (pgd_none(*pgd) || pgd_bad(*pgd)) + goto err; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) + p4d = p4d_offset(pgd, addr); + if (p4d_none(READ_ONCE(*p4d))) + goto err; + + pud = pud_offset(p4d, addr); +# elif (defined(CONFIG_X86)) && LINUX_VERSION_CODE >= KERNEL_VERSION (4, 12, 0) + pud = pud_offset((p4d_t *)pgd, addr); +# elif (defined(CONFIG_CPU_CSKYV2)) && LINUX_VERSION_CODE >= KERNEL_VERSION (4, 11, 0) + pud = pud_offset((p4d_t *)pgd, addr); +# else + pud = pud_offset(pgd, addr); +# endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) */ + if (pud_none(*pud) || pud_bad(*pud)) + goto err; + + pmd = pmd_offset(pud, addr); + if (pmd_none(*pmd) || pmd_bad(*pmd)) + goto err; + + //TODO: cannot be compiled for ko + pte = pte_offset_map_lock(current->mm, pmd, addr, &ptl); + + if (!pte_present(*pte)) { + if (pte) + pte_unmap_unlock(pte, ptl); + + /* Case maybe provides unmapped addr. */ + data = *(unsigned int*)addr; + pte = pte_offset_map_lock(current->mm, pmd, addr, &ptl); + if (!pte_present(*pte)) { + pte_unmap_unlock(pte, ptl); + goto err; + } + } + + pfns[i] = pte_pfn(*pte); + pte_unmap_unlock(pte, ptl); +#endif + /* Advance to next. */ + addr += PAGE_SIZE; + } +#if gcdUSING_PFN_FOLLOW || (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 5, 0)) + up_read(¤t_mm_mmap_sem); +#endif + + for (i = 0; i < pfn_count; i++) { + if (pfn_valid(pfns[i])) { + struct page *page = pfn_to_page(pfns[i]); + + refs[i] = get_page_unless_zero(page); + pages[i] = page; + pageCount++; + } + } + + um->chunk_count = 1; + for (i = 1; i < pfn_count; i++) { + if (pfns[i] != pfns[i - 1] + 1) + ++um->chunk_count; + } + + um->pfns_valid = 0; + if (pageCount == pfn_count && !um->alloc_from_res) { +#if gcdUSE_LINUX_SG_TABLE_API + result = sg_alloc_table_from_pages(&um->sgt, pages, pfn_count, addr & ~PAGE_MASK, + pfn_count * PAGE_SIZE, GFP_KERNEL | gcdNOWARN); + +#else + result = alloc_sg_list_from_pages(&um->sgt.sgl, pages, pfn_count, addr & ~PAGE_MASK, + pfn_count * PAGE_SIZE, &um->sgt.nents); + + um->sgt.orig_nents = um->sgt.nents; +#endif + if (unlikely(result < 0)) { + LOG_WARN("%s: sg_alloc_table_from_pages failed\n", __func__); + goto err; + } + + result = dma_map_sg(dev, um->sgt.sgl, um->sgt.nents, DMA_TO_DEVICE); + + if (unlikely(result != um->sgt.nents)) { +#if gcdUSE_LINUX_SG_TABLE_API + sg_free_table(&um->sgt); +#else + kfree(um->sgt.sgl); +#endif + LOG_WARN("%s: dma_map_sg failed\n", __func__); + goto err; + } + + if (g_iommu) + um->dmaHandle = sg_dma_address(um->sgt.sgl); + + um->pfns_valid = 1; + } + + free_memory(pages); + pages = NULL; + + um->type = UM_PFN_MAP; + um->pfns = pfns; + um->refs = refs; + return 0; + +err: + free_memory(pfns); + free_memory(refs); + free_memory(pages); + + return -ENOTTY; +} + +static int +_Import(void* Memory, struct device *dev, size_t Size, struct um_desc *UserMemory) +{ + int status = 0; + unsigned long vm_flags = 0; + struct vm_area_struct *vma = NULL; + size_t start, end, memory; + int result = 0; + size_t extraPage, pageCount; + + /* Verify the arguments. */ + if (Memory == NULL || Size == 0) { + LOG_ERR("invalid param, Memory=%px, Size=%lu\n", Memory, Size); + return -EINVAL; + } + + memory = untagged_addr((size_t)Memory); + + /* Get the number of required pages. */ + end = (memory + Size + PAGE_SIZE - 1) >> PAGE_SHIFT; + start = memory >> PAGE_SHIFT; + pageCount = end - start; + + /* Allocate extra page to avoid cache overflow */ + extraPage = 2; + + LOG_DBG("%s(%d): pageCount: %lu. extraPage: %lu, Memory=%px Size=%lu\n", + __func__, __LINE__, pageCount, extraPage, Memory, Size); + + /* Overflow. */ + if ((memory + Size) < memory) { + LOG_ERR("overflow, %d", __LINE__); + return -EINVAL; + } + + if (memory) { + size_t vaddr = memory; + + down_read(¤t_mm_mmap_sem); + vma = find_vma(current->mm, memory); + up_read(¤t_mm_mmap_sem); + + if (!vma) { + /* No such memory, or across vmas. */ + status = -EINVAL; + LOG_ERR("No such memory, or across vmas\n"); + goto OnError; + } + +#ifdef CONFIG_ARM + /* coherent cache in case vivt or vipt-aliasing cache. */ + __cpuc_flush_user_range(memory, memory + Size, vma->vm_flags); +#endif + + vm_flags = vma->vm_flags; + vaddr = vma->vm_end; + + down_read(¤t_mm_mmap_sem); + while (vaddr < memory + Size) { + vma = find_vma(current->mm, vaddr); + + if (!vma) { + /* No such memory. */ + up_read(¤t_mm_mmap_sem); + status = -EINVAL; + LOG_ERR("No such memory\n"); + goto OnError; + } + + if ((vma->vm_flags & VM_PFNMAP) != (vm_flags & VM_PFNMAP)) { + /* + * Can not support different map type: + * both PFN and PAGE detected. + */ + up_read(¤t_mm_mmap_sem); + status = -1; + LOG_ERR("Can not support different map type\n"); + goto OnError; + } + + vaddr = vma->vm_end; + } + up_read(¤t_mm_mmap_sem); + } + + if (vm_flags & VM_PFNMAP) + result = import_pfn_map(dev, UserMemory, memory, pageCount); + else + result = import_page_map(dev, UserMemory, memory, pageCount, Size, vm_flags); + + + if (result < 0) { + status = result; + goto OnError; + } + + UserMemory->vm_flags = vm_flags; + UserMemory->user_vaddr = (unsigned long)Memory; + UserMemory->size = Size; + UserMemory->offset = (memory & ~PAGE_MASK); + UserMemory->pageCount = pageCount; + UserMemory->extraPage = extraPage; + UserMemory->dev = dev; + + /* Success. */ + LOG_DBG("%s: success, user memory: %px, size: %lu, dma handle: %px, dev: %px\n", + __func__, Memory, Size, (void*)UserMemory->dmaHandle, dev); + return 0; + +OnError: + LOG_ERR("%s: return %d, user memory: %px, size: %lu, dev: %px\n", + __func__, status, Memory, Size, dev); + return status; +} + +#if 0 +static int _GetUserMemroyPages(struct um_desc *userMemory) { + return userMemory->pageCount + userMemory->extraPage; +} + +static int _UserMemoryContiguous(struct um_desc *userMemory) { + return (userMemory->chunk_count == 1) ? 1 : 0; +} +#endif + +static int +_UserMemoryAttach(struct device *dev, void* memory, size_t size, struct um_desc** umd) +{ + int status; + struct um_desc *userMemory = NULL; + + /* Handle is meangless for this importer. */ + if (!memory || !size || !umd) { + LOG_ERR("%s: invalid parameters\n", __func__); + return -EINVAL; + } + + userMemory = alloc_memory(sizeof(struct um_desc)); + if (!userMemory) { + status = -ENOMEM; + LOG_ERR("alloc memory um_desc failed\n"); + goto OnError; + } + + status = _Import(memory, dev, size, userMemory); + if (status) { + goto OnError; + } + + *umd = userMemory; + return 0; + +OnError: + if (userMemory != NULL) + free_memory(userMemory); + + return status; +} + + +static void +release_physical_map(struct um_desc *um) +{ + struct device *dev = um->dev; + if (g_iommu) { + unsigned long pfn = um->physical >> PAGE_SHIFT; + size_t size = um->size + um->offset; + + if (pfn_valid(pfn)) + dma_unmap_page(dev, um->dmaHandle, size, DMA_BIDIRECTIONAL); + else +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0) + dma_unmap_resource(dev, um->dmaHandle, size, DMA_BIDIRECTIONAL, 0); +#else + dma_unmap_page(dev, um->dmaHandle, size, DMA_BIDIRECTIONAL); +#endif + + um->dmaHandle = 0; + } +} + +static void +release_page_map(struct um_desc *um) +{ + int i; + struct device *dev = um->dev; + + dma_sync_sg_for_device(dev, um->sgt.sgl, um->sgt.nents, DMA_TO_DEVICE); + dma_sync_sg_for_cpu(dev, um->sgt.sgl, um->sgt.nents, DMA_FROM_DEVICE); + dma_unmap_sg(dev, um->sgt.sgl, um->sgt.nents, DMA_FROM_DEVICE); + + um->dmaHandle = 0; + +#if gcdUSE_LINUX_SG_TABLE_API + sg_free_table(&um->sgt); +#else + kfree(um->sgt.sgl); +#endif + + for (i = 0; i < um->pageCount; i++) { + if (!PageReserved(um->pages[i])) + SetPageDirty(um->pages[i]); + +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 0) + unpin_user_page(um->pages[i]); +#else + put_page(um->pages[i]); +#endif + } + + free_memory(um->pages); +} + +static void +release_pfn_map(struct um_desc *um) +{ + int i; + struct device *dev = um->dev; + + if (um->pfns_valid) { + dma_unmap_sg(dev, um->sgt.sgl, um->sgt.nents, DMA_FROM_DEVICE); + +#if gcdUSE_LINUX_SG_TABLE_API + sg_free_table(&um->sgt); +#else + kfree(um->sgt.sgl); +#endif + } + + um->dmaHandle = 0; + + for (i = 0; i < um->pageCount; i++) { + if (pfn_valid(um->pfns[i])) { + struct page *page = pfn_to_page(um->pfns[i]); + + if (!PageReserved(page)) + SetPageDirty(page); + + if (um->refs[i]) + put_page(page); + } + } + + free_memory(um->pfns); + free_memory(um->refs); +} + +static void +_UserMemoryFree(struct um_desc* userMemory) +{ + if (userMemory) { + switch (userMemory->type) { + case UM_PHYSICAL_MAP: + release_physical_map(userMemory); + break; + case UM_PAGE_MAP: + release_page_map(userMemory); + break; + case UM_PFN_MAP: + release_pfn_map(userMemory); + break; + } + LOG_DBG("%s: dmaHandle %llx, size %lu\n", __func__, userMemory->dmaHandle, userMemory->size); + free_memory(userMemory); + } +} + +int vdec_attach_user_memory(struct file *filp, struct device *dev, user_memory_desc *desc) { + int ret = 0; + bi_list_node* node = NULL; + struct um_desc* userMemory = NULL; + struct filp_priv *fp_priv = NULL; + + if (!filp || !filp->private_data) { + LOG_ERR("%s:%d:: invalid param\n", __func__, __LINE__); + return -EINVAL; + } + + node = bi_list_create_node(); + if (!node) { + LOG_ERR("create list node failed\n"); + return -ENOMEM; + } + + ret = _UserMemoryAttach(dev, desc->memory, desc->size, &userMemory); + if (!ret) { + node->data = (void*)userMemory; + fp_priv = (struct filp_priv*)filp->private_data; + bi_list_insert_node_tail(&fp_priv->user_memory_list, node); + + LOG_DBG("attach success, user memory: %px, size: %lu; output dmaHandle: %llx, size: %lu, dev: %px\n", + desc->memory, desc->size, userMemory->dmaHandle, userMemory->size, userMemory->dev); + desc->memory = (void*)userMemory->dmaHandle; + desc->size = userMemory->size; + } else { + bi_list_free_node(node); + LOG_ERR("%s failed, ret=%d\n", __func__, ret); + } + + return ret; +} + +bi_list_node *vdec_find_node(bi_list *list, user_memory_desc *desc) { + bi_list_node *node = NULL; + struct um_desc* userMemory = NULL; + + node = list->head; + while (node) { + userMemory = (struct um_desc*)node->data; + if (userMemory->user_vaddr == (unsigned long)desc->memory) { + break; + } + + node = node->next; + } + return node; +} + +int vdec_detach_user_memory(struct file *filp, user_memory_desc *desc) { + bi_list_node* node = NULL; + struct filp_priv *fp_priv = NULL; + + if (!filp || !filp->private_data) { + LOG_ERR("%s:%d:: invalid param\n", __func__, __LINE__); + return -EINVAL; + } + + fp_priv = (struct filp_priv*)filp->private_data; + node = vdec_find_node(&fp_priv->user_memory_list, desc); + if (node) { + _UserMemoryFree((struct um_desc*)node->data); + bi_list_remove_node(&fp_priv->user_memory_list, node); + return 0; + } else { + LOG_DBG("%d: no memory node found, addr: %px, size: %lu\n", __LINE__, desc->memory, desc->size); + } + + return -EFAULT; +} + +void vdec_clean_user_memory(bi_list *list) { + bi_list_node *node = NULL; + bi_list_node *cur_node; + + if (!list) { + LOG_ERR("%s:%d:: invalid param\n", __func__, __LINE__); + return; + } + + node = list->head; + while (node) { + _UserMemoryFree((struct um_desc*)node->data); + cur_node = node; + node = node->next; + bi_list_free_node(cur_node); + } + list->head = list->tail = NULL; +} + +static inline void +_MemoryBarrier(void) +{ +#if defined(CONFIG_ARM) && (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34)) + dsb(); +#else + /* memory barrier */ + mb(); +#endif +} + +static int +_UserMemoryCache(struct um_desc *um, ES_CACHE_OPERATION Operation) +{ + enum dma_data_direction dir; + struct device *dev = um->dev; + + if (um->type == UM_PHYSICAL_MAP || um->alloc_from_res) { + _MemoryBarrier(); + return 0; + } + + if (um->type == UM_PFN_MAP && um->pfns_valid == 0) { + _MemoryBarrier(); + return 0; + } + +#ifdef CONFIG_ARM + /* coherent cache in case vivt or vipt-aliasing cache. */ + __cpuc_flush_user_range(um->user_vaddr, um->user_vaddr + um->size, um->vm_flags); +#endif + + LOG_DBG("sync cache, dev: %px, dmaHandle: %px, size: %lu, alloc: %u, opr: %d\n", + um->dev, (void*)um->dmaHandle, um->size, um->alloc_from_res, Operation); + switch (Operation) { + case ES_CACHE_CLEAN: + dir = DMA_TO_DEVICE; + dma_sync_sg_for_device(dev, um->sgt.sgl, um->sgt.nents, dir); + break; + case ES_CACHE_FLUSH: + dir = DMA_TO_DEVICE; + dma_sync_sg_for_device(dev, um->sgt.sgl, um->sgt.nents, dir); + dir = DMA_FROM_DEVICE; + dma_sync_sg_for_cpu(dev, um->sgt.sgl, um->sgt.nents, dir); + break; + case ES_CACHE_INVALIDATE: + dir = DMA_FROM_DEVICE; + dma_sync_sg_for_cpu(dev, um->sgt.sgl, um->sgt.nents, dir); + break; + default: + LOG_WARN("%d: invalid operatoin %d\n", __LINE__, Operation); + return -EINVAL; + } + + return 0; +} + +int vdec_sync_user_memory_cache(struct file *filp, user_memory_desc *desc, ES_CACHE_OPERATION opr) { + bi_list_node* node = NULL; + struct filp_priv *fp_priv = NULL; + int ret = -EFAULT; + + if (!filp || !filp->private_data) { + LOG_ERR("%s:%d:: invalid param\n", __func__, __LINE__); + return -EINVAL; + } + + LOG_DBG("%s: memory=%px, size=%lu, nid: %u\n", __func__, desc->memory, desc->size, desc->nid); + fp_priv = (struct filp_priv*)filp->private_data; + node = vdec_find_node(&fp_priv->user_memory_list, desc); + if (node) { + ret = _UserMemoryCache((struct um_desc*)node->data, opr); + } else { + LOG_DBG("%d: no memory node found, addr: %px, size: %lu\n", __LINE__, desc->memory, desc->size); + } + + return ret; +} diff --git a/drivers/staging/media/eswin/vdec/vdec_allocator.h b/drivers/staging/media/eswin/vdec/vdec_allocator.h new file mode 100644 index 000000000000..a41252b2f632 --- /dev/null +++ b/drivers/staging/media/eswin/vdec/vdec_allocator.h @@ -0,0 +1,20 @@ +#ifndef __VDEC_ALLOCATOR__H +#define __VDEC_ALLOCATOR__H + +#include +#include "hantrodec.h" + +/* CPU cache operations */ +typedef enum _ES_CACHE_OPERATION { + ES_CACHE_CLEAN = 0x01, /* Flush CPU cache to mem */ + ES_CACHE_INVALIDATE = 0x02, /* Invalidte CPU cache */ + ES_CACHE_FLUSH = ES_CACHE_CLEAN | ES_CACHE_INVALIDATE, /* Both flush & invalidate */ + ES_CACHE_MEMORY_BARRIER = 0x04, +} ES_CACHE_OPERATION; + +int vdec_attach_user_memory(struct file *filp, struct device *dev, user_memory_desc *desc); +int vdec_detach_user_memory(struct file *filp, user_memory_desc *desc); +void vdec_clean_user_memory(bi_list *list); +int vdec_sync_user_memory_cache(struct file *filp, user_memory_desc *desc, ES_CACHE_OPERATION opr); + +#endif -- 2.47.0