Commit 25e73b42 authored by Marek Szyprowski's avatar Marek Szyprowski Committed by Mauro Carvalho Chehab

[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: default avatarMarek Szyprowski <m.szyprowski@samsung.com>
Reviewed-by: default avatarJavier Martinez Canillas <javier@osg.samsung.com>
Tested-by: default avatarJavier Martinez Canillas <javier@osg.samsung.com>
Acked-by: default avatarAndrzej Hajda <a.hajda@samsung.com>
Tested-by: default avatarSmitha T Murthy <smitha.t@samsung.com>
Signed-off-by: default avatarSylwester Nawrocki <s.nawrocki@samsung.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@s-opensource.com>
parent 9ce47803
...@@ -43,6 +43,10 @@ int mfc_debug_level; ...@@ -43,6 +43,10 @@ int mfc_debug_level;
module_param_named(debug, mfc_debug_level, int, S_IRUGO | S_IWUSR); module_param_named(debug, mfc_debug_level, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Debug level - higher value produces more verbose messages"); MODULE_PARM_DESC(debug, "Debug level - higher value produces more verbose messages");
static char *mfc_mem_size;
module_param_named(mem, mfc_mem_size, charp, 0644);
MODULE_PARM_DESC(mem, "Preallocated memory size for the firmware and context buffers");
/* Helper functions for interrupt processing */ /* Helper functions for interrupt processing */
/* Remove from hw execution round robin */ /* Remove from hw execution round robin */
...@@ -1173,6 +1177,8 @@ static void s5p_mfc_unconfigure_2port_memory(struct s5p_mfc_dev *mfc_dev) ...@@ -1173,6 +1177,8 @@ static void s5p_mfc_unconfigure_2port_memory(struct s5p_mfc_dev *mfc_dev)
static int s5p_mfc_configure_common_memory(struct s5p_mfc_dev *mfc_dev) static int s5p_mfc_configure_common_memory(struct s5p_mfc_dev *mfc_dev)
{ {
struct device *dev = &mfc_dev->plat_dev->dev; struct device *dev = &mfc_dev->plat_dev->dev;
unsigned long mem_size = SZ_8M;
unsigned int bitmap_size;
/* /*
* When IOMMU is available, we cannot use the default configuration, * When IOMMU is available, we cannot use the default configuration,
* because of MFC firmware requirements: address space limited to * because of MFC firmware requirements: address space limited to
...@@ -1186,17 +1192,39 @@ static int s5p_mfc_configure_common_memory(struct s5p_mfc_dev *mfc_dev) ...@@ -1186,17 +1192,39 @@ static int s5p_mfc_configure_common_memory(struct s5p_mfc_dev *mfc_dev)
if (ret) if (ret)
return ret; return ret;
mfc_dev->mem_dev[BANK1_CTX] = mfc_dev->mem_dev[BANK2_CTX] = dev; if (mfc_mem_size)
ret = s5p_mfc_alloc_firmware(mfc_dev); mem_size = memparse(mfc_mem_size, NULL);
if (ret) {
bitmap_size = BITS_TO_LONGS(mem_size >> PAGE_SHIFT) * sizeof(long);
mfc_dev->mem_bitmap = kzalloc(bitmap_size, GFP_KERNEL);
if (!mfc_dev->mem_bitmap) {
exynos_unconfigure_iommu(dev); exynos_unconfigure_iommu(dev);
return ret; return -ENOMEM;
} }
mfc_dev->dma_base[BANK1_CTX] = mfc_dev->fw_buf.dma; mfc_dev->mem_virt = dma_alloc_coherent(dev, mem_size,
mfc_dev->dma_base[BANK2_CTX] = mfc_dev->fw_buf.dma; &mfc_dev->mem_base, GFP_KERNEL);
if (!mfc_dev->mem_virt) {
kfree(mfc_dev->mem_bitmap);
dev_err(dev, "failed to preallocate %ld MiB for the firmware and context buffers\n",
(mem_size / SZ_1M));
exynos_unconfigure_iommu(dev);
return -ENOMEM;
}
mfc_dev->mem_size = mem_size;
mfc_dev->dma_base[BANK1_CTX] = mfc_dev->mem_base;
mfc_dev->dma_base[BANK2_CTX] = mfc_dev->mem_base;
/* Firmware allocation cannot fail in this case */
s5p_mfc_alloc_firmware(mfc_dev);
mfc_dev->mem_dev[BANK1_CTX] = mfc_dev->mem_dev[BANK2_CTX] = dev;
vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32)); vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32));
dev_info(dev, "preallocated %ld MiB buffer for the firmware and context buffers\n",
(mem_size / SZ_1M));
return 0; return 0;
} }
...@@ -1205,6 +1233,9 @@ static void s5p_mfc_unconfigure_common_memory(struct s5p_mfc_dev *mfc_dev) ...@@ -1205,6 +1233,9 @@ static void s5p_mfc_unconfigure_common_memory(struct s5p_mfc_dev *mfc_dev)
struct device *dev = &mfc_dev->plat_dev->dev; struct device *dev = &mfc_dev->plat_dev->dev;
exynos_unconfigure_iommu(dev); exynos_unconfigure_iommu(dev);
dma_free_coherent(dev, mfc_dev->mem_size, mfc_dev->mem_virt,
mfc_dev->mem_base);
kfree(mfc_dev->mem_bitmap);
vb2_dma_contig_clear_max_seg_size(dev); vb2_dma_contig_clear_max_seg_size(dev);
} }
......
...@@ -315,6 +315,10 @@ struct s5p_mfc_dev { ...@@ -315,6 +315,10 @@ struct s5p_mfc_dev {
unsigned int int_err; unsigned int int_err;
wait_queue_head_t queue; wait_queue_head_t queue;
struct s5p_mfc_priv_buf fw_buf; struct s5p_mfc_priv_buf fw_buf;
size_t mem_size;
dma_addr_t mem_base;
unsigned long *mem_bitmap;
void *mem_virt;
dma_addr_t dma_base[BANK_CTX_NUM]; dma_addr_t dma_base[BANK_CTX_NUM];
unsigned long hw_lock; unsigned long hw_lock;
struct s5p_mfc_ctx *ctx[MFC_NUM_CONTEXTS]; struct s5p_mfc_ctx *ctx[MFC_NUM_CONTEXTS];
......
...@@ -40,41 +40,60 @@ void s5p_mfc_init_regs(struct s5p_mfc_dev *dev) ...@@ -40,41 +40,60 @@ void s5p_mfc_init_regs(struct s5p_mfc_dev *dev)
int s5p_mfc_alloc_priv_buf(struct s5p_mfc_dev *dev, unsigned int mem_ctx, int s5p_mfc_alloc_priv_buf(struct s5p_mfc_dev *dev, unsigned int mem_ctx,
struct s5p_mfc_priv_buf *b) struct s5p_mfc_priv_buf *b)
{ {
struct device *mem_dev = dev->mem_dev[mem_ctx]; unsigned int bits = dev->mem_size >> PAGE_SHIFT;
dma_addr_t base = dev->dma_base[mem_ctx]; 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); mfc_debug(3, "Allocating priv: %zu\n", b->size);
b->ctx = mem_ctx; if (dev->mem_virt) {
b->virt = dma_alloc_coherent(mem_dev, b->size, &b->dma, GFP_KERNEL); start = bitmap_find_next_zero_area(dev->mem_bitmap, bits, 0, count, align);
if (start > bits)
goto no_mem;
if (!b->virt) { bitmap_set(dev->mem_bitmap, start, count);
mfc_err("Allocating private buffer of size %zu failed\n", offset = start << PAGE_SHIFT;
b->size); b->virt = dev->mem_virt + offset;
return -ENOMEM; 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) { if (b->dma < base) {
mfc_err("Invalid memory configuration - buffer (%pad) is below base memory address(%pad)\n", mfc_err("Invalid memory configuration - buffer (%pad) is below base memory address(%pad)\n",
&b->dma, &base); &b->dma, &base);
dma_free_coherent(mem_dev, b->size, b->virt, b->dma); dma_free_coherent(mem_dev, b->size, b->virt, b->dma);
return -ENOMEM; return -ENOMEM;
} }
}
mfc_debug(3, "Allocated addr %p %pad\n", b->virt, &b->dma); mfc_debug(3, "Allocated addr %p %pad\n", b->virt, &b->dma);
return 0; 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, void s5p_mfc_release_priv_buf(struct s5p_mfc_dev *dev,
struct s5p_mfc_priv_buf *b) 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]; struct device *mem_dev = dev->mem_dev[b->ctx];
if (b->virt) {
dma_free_coherent(mem_dev, b->size, b->virt, b->dma); dma_free_coherent(mem_dev, b->size, b->virt, b->dma);
}
b->virt = NULL; b->virt = NULL;
b->dma = 0; b->dma = 0;
b->size = 0; b->size = 0;
}
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment