linux/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c
Marek Szyprowski 25e73b425c [media] s5p-mfc: Add support for probe-time preallocated block based allocator
Current MFC driver depends on the fact that when IOMMU is available, the
DMA-mapping framework and its IOMMU glue will use first-fit allocator.
This was true for ARM architecture, but its not for ARM64 arch. However, in
case of MFC v6+ hardware and latest firmware, it turned out that there is
no strict requirement for ALL buffers to be allocated on higher addresses
than the firmware base. This requirement is true only for the device and
per-context buffers. All video data buffers can be allocated anywhere for
all MFC v6+ versions.

Such relaxed requirements for the memory buffers can be easily fulfilled
by allocating firmware, device and per-context buffers from the probe-time
preallocated larger buffer. This patch adds support for it. This way the
driver finally works fine on ARM64 architecture. The size of the
preallocated buffer is 8 MiB, what is enough for three instances H264
decoders or encoders (other codecs have smaller memory requirements).
If one needs more for particular use case, one can use "mem" module
parameter to force larger (or smaller) buffer (for example by adding
"s5p_mfc.mem=16M" to kernel command line).

[mchehab@s-opensource.com: fix two checkpatch warnings: don't initialize
 static to NULL; don't use S_foo permisions]

Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Reviewed-by: Javier Martinez Canillas <javier@osg.samsung.com>
Tested-by: Javier Martinez Canillas <javier@osg.samsung.com>
Acked-by: Andrzej Hajda <a.hajda@samsung.com>
Tested-by: Smitha T Murthy <smitha.t@samsung.com>
Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2017-04-05 16:01:24 -03:00

100 lines
2.6 KiB
C

/*
* drivers/media/platform/s5p-mfc/s5p_mfc_opr.c
*
* Samsung MFC (Multi Function Codec - FIMV) driver
* This file contains hw related functions.
*
* Kamil Debski, Copyright (c) 2012 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include "s5p_mfc_debug.h"
#include "s5p_mfc_opr.h"
#include "s5p_mfc_opr_v5.h"
#include "s5p_mfc_opr_v6.h"
static struct s5p_mfc_hw_ops *s5p_mfc_ops;
void s5p_mfc_init_hw_ops(struct s5p_mfc_dev *dev)
{
if (IS_MFCV6_PLUS(dev)) {
s5p_mfc_ops = s5p_mfc_init_hw_ops_v6();
dev->warn_start = S5P_FIMV_ERR_WARNINGS_START_V6;
} else {
s5p_mfc_ops = s5p_mfc_init_hw_ops_v5();
dev->warn_start = S5P_FIMV_ERR_WARNINGS_START;
}
dev->mfc_ops = s5p_mfc_ops;
}
void s5p_mfc_init_regs(struct s5p_mfc_dev *dev)
{
if (IS_MFCV6_PLUS(dev))
dev->mfc_regs = s5p_mfc_init_regs_v6_plus(dev);
}
int s5p_mfc_alloc_priv_buf(struct s5p_mfc_dev *dev, unsigned int mem_ctx,
struct s5p_mfc_priv_buf *b)
{
unsigned int bits = dev->mem_size >> PAGE_SHIFT;
unsigned int count = b->size >> PAGE_SHIFT;
unsigned int align = (SZ_64K >> PAGE_SHIFT) - 1;
unsigned int start, offset;
mfc_debug(3, "Allocating priv: %zu\n", b->size);
if (dev->mem_virt) {
start = bitmap_find_next_zero_area(dev->mem_bitmap, bits, 0, count, align);
if (start > bits)
goto no_mem;
bitmap_set(dev->mem_bitmap, start, count);
offset = start << PAGE_SHIFT;
b->virt = dev->mem_virt + offset;
b->dma = dev->mem_base + offset;
} else {
struct device *mem_dev = dev->mem_dev[mem_ctx];
dma_addr_t base = dev->dma_base[mem_ctx];
b->ctx = mem_ctx;
b->virt = dma_alloc_coherent(mem_dev, b->size, &b->dma, GFP_KERNEL);
if (!b->virt)
goto no_mem;
if (b->dma < base) {
mfc_err("Invalid memory configuration - buffer (%pad) is below base memory address(%pad)\n",
&b->dma, &base);
dma_free_coherent(mem_dev, b->size, b->virt, b->dma);
return -ENOMEM;
}
}
mfc_debug(3, "Allocated addr %p %pad\n", b->virt, &b->dma);
return 0;
no_mem:
mfc_err("Allocating private buffer of size %zu failed\n", b->size);
return -ENOMEM;
}
void s5p_mfc_release_priv_buf(struct s5p_mfc_dev *dev,
struct s5p_mfc_priv_buf *b)
{
if (dev->mem_virt) {
unsigned int start = (b->dma - dev->mem_base) >> PAGE_SHIFT;
unsigned int count = b->size >> PAGE_SHIFT;
bitmap_clear(dev->mem_bitmap, start, count);
} else {
struct device *mem_dev = dev->mem_dev[b->ctx];
dma_free_coherent(mem_dev, b->size, b->virt, b->dma);
}
b->virt = NULL;
b->dma = 0;
b->size = 0;
}