blob: 8ae84c76d47c20f5df50da39c39c68266e8c7e89 [file] [log] [blame]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <linux/spinlock.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/genalloc.h>
#include <linux/io.h>
#include <linux/mm.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/amlogic/media/codec_mm/codec_mm.h>
#include <linux/amlogic/media/codec_mm/secmem.h>
#include <linux/ion.h>
#include "dev_ion.h"
#define ION_CODEC_MM_ALLOCATE_FAIL -1
#define CODEC_MM_ION "ION"
static phys_addr_t ion_codec_mm_allocate(struct ion_heap *heap,
unsigned long size, unsigned long flags)
{
struct ion_cma_heap *codec_heap =
container_of(heap, struct ion_cma_heap, heap);
unsigned long offset;
unsigned long allocflags = CODEC_MM_FLAGS_DMA;
if (codec_heap->alloced_size + size > codec_heap->max_can_alloc_size)
pr_debug("%s failed out size %lu,alloced %lu\n",
__func__,
size,
codec_heap->alloced_size);
if (flags & ION_FLAG_EXTEND_PROTECTED)
allocflags |= CODEC_MM_FLAGS_TVP;
offset = codec_mm_alloc_for_dma(CODEC_MM_ION,
size / PAGE_SIZE,
0,
allocflags);
if (!offset) {
pr_err("%s failed out size %d\n", __func__, (int)size);
return ION_CODEC_MM_ALLOCATE_FAIL;
}
mutex_lock(&codec_heap->mutex);
codec_heap->alloced_size += size;
mutex_unlock(&codec_heap->mutex);
return offset;
}
static void ion_codec_mm_free(struct ion_heap *heap,
phys_addr_t addr,
unsigned long size)
{
struct ion_cma_heap *codec_heap =
container_of(heap, struct ion_cma_heap, heap);
if (addr == ION_CODEC_MM_ALLOCATE_FAIL)
return;
mutex_lock(&codec_heap->mutex);
if (!codec_mm_free_for_dma(CODEC_MM_ION, addr))
codec_heap->alloced_size -= size;
mutex_unlock(&codec_heap->mutex);
}
static phys_addr_t ion_secure_allocate(struct ion_heap *heap,
unsigned long size, unsigned long flags)
{
struct ion_cma_heap *codec_heap =
container_of(heap, struct ion_cma_heap, heap);
phys_addr_t paddr;
paddr = secure_block_alloc(size, flags);
if (!paddr) {
pr_err("%s failed out size %d\n", __func__, (int)size);
return ION_CODEC_MM_ALLOCATE_FAIL;
}
codec_heap->alloced_size += size;
return paddr;
}
static void ion_secure_free(struct ion_heap *heap,
phys_addr_t addr,
unsigned long size)
{
struct ion_cma_heap *codec_heap =
container_of(heap, struct ion_cma_heap, heap);
if (addr == ION_CODEC_MM_ALLOCATE_FAIL)
return;
secure_block_free(addr, size);
codec_heap->alloced_size -= size;
}
static int ion_codec_mm_heap_allocate(struct ion_heap *heap,
struct ion_buffer *buffer,
unsigned long size,
unsigned long flags)
{
struct sg_table *table;
phys_addr_t paddr;
int ret;
if (!(flags & ION_FLAG_EXTEND_MESON_HEAP))
return -ENOMEM;
table = kzalloc(sizeof(*table), GFP_KERNEL);
if (!table)
return -ENOMEM;
ret = sg_alloc_table(table, 1, GFP_KERNEL);
if (ret)
goto err_free;
if (flags & ION_FLAG_EXTEND_MESON_SECURE_VDEC_HEAP)
paddr = ion_secure_allocate(heap, size, flags);
else
paddr = ion_codec_mm_allocate(heap, size, flags);
if (paddr == ION_CODEC_MM_ALLOCATE_FAIL) {
ret = -ENOMEM;
goto err_free_table;
}
sg_set_page(table->sgl, pfn_to_page(PFN_DOWN(paddr)), size, 0);
buffer->priv_virt = table;
buffer->sg_table = table;
ion_buffer_prep_noncached(buffer);
return 0;
err_free_table:
sg_free_table(table);
err_free:
kfree(table);
return ret;
}
static void ion_codec_mm_heap_free(struct ion_buffer *buffer)
{
struct ion_heap *heap = buffer->heap;
struct sg_table *table = buffer->priv_virt;
struct page *page = sg_page(table->sgl);
phys_addr_t paddr = PFN_PHYS(page_to_pfn(page));
struct device *ion_dev = meson_ion_get_dev();
if ((buffer->flags & ION_FLAG_EXTEND_PROTECTED) !=
ION_FLAG_EXTEND_PROTECTED)
ion_buffer_zero(buffer);
if (buffer->flags & ION_FLAG_EXTEND_MESON_SECURE_VDEC_HEAP) {
ion_secure_free(heap, paddr, buffer->size);
} else {
if (!!(buffer->flags & ION_FLAG_CACHED))
dma_sync_sg_for_device(ion_dev,
table->sgl,
table->nents,
DMA_BIDIRECTIONAL);
ion_codec_mm_free(heap, paddr, buffer->size);
}
sg_free_table(table);
kfree(table);
}
struct ion_heap_ops codec_mm_heap_ops = {
.allocate = ion_codec_mm_heap_allocate,
.free = ion_codec_mm_heap_free,
};