mirror of
https://github.com/LibreELEC/LibreELEC.tv
synced 2025-09-24 19:46:01 +07:00
428 lines
12 KiB
Diff
428 lines
12 KiB
Diff
From 13c2fee823cc9eeffd89249f0d1514759e29ccfe Mon Sep 17 00:00:00 2001
|
|
From: Detlev Casanova <detlev.casanova@collabora.com>
|
|
Date: Tue, 10 Jun 2025 14:53:27 -0400
|
|
Subject: [PATCH 073/110] DETLEV(v3): media: rkvdec: Add RCB and SRAM support
|
|
|
|
The RCB (Rows and Cols Buffers) are a set of buffers used by other
|
|
variations of the decoder to store temporary data.
|
|
|
|
Those variation come with a dedicated SRAM area used to store those
|
|
buffers for better performances.
|
|
|
|
The buffer sizes are either the width or height of the frame being
|
|
decoded multiplied by a documented factor and can be stored either
|
|
in SRAM or RAM.
|
|
A fallback to RAM is provided if the SRAM is full (e.g.: multiple
|
|
streams are being decoded at the same time).
|
|
|
|
To manage the different kind of allocation, an enum is added to the
|
|
rkvdec_aux_buf struct to specify how the buffer was allocated, and
|
|
so, how to free it.
|
|
|
|
This commit is in preparation of other variants support.
|
|
|
|
Signed-off-by: Detlev Casanova <detlev.casanova@collabora.com>
|
|
---
|
|
.../media/platform/rockchip/rkvdec/Makefile | 1 +
|
|
.../platform/rockchip/rkvdec/rkvdec-rcb.c | 174 ++++++++++++++++++
|
|
.../platform/rockchip/rkvdec/rkvdec-rcb.h | 29 +++
|
|
.../media/platform/rockchip/rkvdec/rkvdec.c | 29 ++-
|
|
.../media/platform/rockchip/rkvdec/rkvdec.h | 13 ++
|
|
5 files changed, 242 insertions(+), 4 deletions(-)
|
|
create mode 100644 drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.c
|
|
create mode 100644 drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.h
|
|
|
|
diff --git a/drivers/media/platform/rockchip/rkvdec/Makefile b/drivers/media/platform/rockchip/rkvdec/Makefile
|
|
index d2ba7a7c15e5..3b34f39b17d6 100644
|
|
--- a/drivers/media/platform/rockchip/rkvdec/Makefile
|
|
+++ b/drivers/media/platform/rockchip/rkvdec/Makefile
|
|
@@ -6,4 +6,5 @@ rockchip-vdec-y += \
|
|
rkvdec-h264.o \
|
|
rkvdec-h264-common.o \
|
|
rkvdec-hevc.o \
|
|
+ rkvdec-rcb.o \
|
|
rkvdec-vp9.o
|
|
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.c
|
|
new file mode 100644
|
|
index 000000000000..210e59db2c62
|
|
--- /dev/null
|
|
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.c
|
|
@@ -0,0 +1,174 @@
|
|
+// SPDX-License-Identifier: GPL-2.0
|
|
+/*
|
|
+ * Rockchip video decoder Rows and Cols Buffers manager
|
|
+ *
|
|
+ * Copyright (C) 2025 Collabora, Ltd.
|
|
+ * Detlev Casanova <detlev.casanova@collabora.com>
|
|
+ */
|
|
+
|
|
+#include "rkvdec.h"
|
|
+#include "rkvdec-rcb.h"
|
|
+
|
|
+#include <linux/types.h>
|
|
+#include <linux/iommu.h>
|
|
+#include <linux/genalloc.h>
|
|
+
|
|
+struct rkvdec_rcb_config {
|
|
+ struct rkvdec_aux_buf *rcb_bufs;
|
|
+ size_t rcb_count;
|
|
+};
|
|
+
|
|
+static size_t rkvdec_rcb_size(const struct rcb_size_info *size_info,
|
|
+ unsigned int width, unsigned int height)
|
|
+{
|
|
+ return size_info->multiplier * (size_info->axis == PIC_HEIGHT ? height : width);
|
|
+}
|
|
+
|
|
+dma_addr_t rkvdec_rcb_buf_dma_addr(struct rkvdec_ctx *ctx, int id)
|
|
+{
|
|
+ return ctx->rcb_config->rcb_bufs[id].dma;
|
|
+}
|
|
+
|
|
+size_t rkvdec_rcb_buf_size(struct rkvdec_ctx *ctx, int id)
|
|
+{
|
|
+ return ctx->rcb_config->rcb_bufs[id].size;
|
|
+}
|
|
+
|
|
+int rkvdec_rcb_buf_count(struct rkvdec_ctx *ctx)
|
|
+{
|
|
+ return ctx->rcb_config->rcb_count;
|
|
+}
|
|
+
|
|
+void rkvdec_free_rcb(struct rkvdec_ctx *ctx)
|
|
+{
|
|
+ struct rkvdec_dev *dev = ctx->dev;
|
|
+ struct rkvdec_rcb_config *cfg = ctx->rcb_config;
|
|
+ unsigned long virt_addr;
|
|
+ int i;
|
|
+
|
|
+ if (!cfg)
|
|
+ return;
|
|
+
|
|
+ for (i = 0; i < cfg->rcb_count; i++) {
|
|
+ size_t rcb_size = cfg->rcb_bufs[i].size;
|
|
+
|
|
+ if (!cfg->rcb_bufs[i].cpu)
|
|
+ continue;
|
|
+
|
|
+ switch (cfg->rcb_bufs[i].type) {
|
|
+ case RKVDEC2_ALLOC_SRAM:
|
|
+ virt_addr = (unsigned long)cfg->rcb_bufs[i].cpu;
|
|
+
|
|
+ if (dev->iommu_domain)
|
|
+ iommu_unmap(dev->iommu_domain, virt_addr, rcb_size);
|
|
+ gen_pool_free(dev->sram_pool, virt_addr, rcb_size);
|
|
+ break;
|
|
+ case RKVDEC2_ALLOC_DMA:
|
|
+ dma_free_coherent(dev->dev,
|
|
+ rcb_size,
|
|
+ cfg->rcb_bufs[i].cpu,
|
|
+ cfg->rcb_bufs[i].dma);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (cfg->rcb_bufs)
|
|
+ devm_kfree(dev->dev, cfg->rcb_bufs);
|
|
+
|
|
+ devm_kfree(dev->dev, cfg);
|
|
+}
|
|
+
|
|
+int rkvdec_allocate_rcb(struct rkvdec_ctx *ctx,
|
|
+ const struct rcb_size_info *size_info,
|
|
+ size_t rcb_count)
|
|
+{
|
|
+ int ret, i;
|
|
+ u32 width, height;
|
|
+ struct rkvdec_dev *rkvdec = ctx->dev;
|
|
+ struct rkvdec_rcb_config *cfg;
|
|
+
|
|
+ ctx->rcb_config = devm_kzalloc(rkvdec->dev, sizeof(*ctx->rcb_config), GFP_KERNEL);
|
|
+ if (!ctx->rcb_config)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ cfg = ctx->rcb_config;
|
|
+
|
|
+ cfg->rcb_bufs = devm_kzalloc(rkvdec->dev, sizeof(*cfg->rcb_bufs) * rcb_count, GFP_KERNEL);
|
|
+ if (!cfg->rcb_bufs) {
|
|
+ ret = -ENOMEM;
|
|
+ goto err_alloc;
|
|
+ }
|
|
+
|
|
+ width = ctx->decoded_fmt.fmt.pix_mp.width;
|
|
+ height = ctx->decoded_fmt.fmt.pix_mp.height;
|
|
+
|
|
+ for (i = 0; i < rcb_count; i++) {
|
|
+ void *cpu = NULL;
|
|
+ dma_addr_t dma;
|
|
+ size_t rcb_size = rkvdec_rcb_size(&size_info[i], width, height);
|
|
+ enum rkvdec_alloc_type alloc_type = RKVDEC2_ALLOC_SRAM;
|
|
+
|
|
+ /* Try allocating an SRAM buffer */
|
|
+ if (ctx->dev->sram_pool) {
|
|
+ if (rkvdec->iommu_domain)
|
|
+ rcb_size = ALIGN(rcb_size, 0x1000);
|
|
+
|
|
+ cpu = gen_pool_dma_zalloc_align(ctx->dev->sram_pool,
|
|
+ rcb_size,
|
|
+ &dma,
|
|
+ 0x1000);
|
|
+ }
|
|
+
|
|
+ /* If an IOMMU is used, map the SRAM address through it */
|
|
+ if (cpu && rkvdec->iommu_domain) {
|
|
+ unsigned long virt_addr = (unsigned long)cpu;
|
|
+ phys_addr_t phys_addr = dma;
|
|
+
|
|
+ ret = iommu_map(rkvdec->iommu_domain, virt_addr, phys_addr,
|
|
+ rcb_size, IOMMU_READ | IOMMU_WRITE, 0);
|
|
+ if (ret) {
|
|
+ gen_pool_free(ctx->dev->sram_pool,
|
|
+ (unsigned long)cpu,
|
|
+ rcb_size);
|
|
+ cpu = NULL;
|
|
+ goto ram_fallback;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * The registers will be configured with the virtual
|
|
+ * address so that it goes through the IOMMU
|
|
+ */
|
|
+ dma = virt_addr;
|
|
+ }
|
|
+
|
|
+ram_fallback:
|
|
+ /* Fallback to RAM */
|
|
+ if (!cpu) {
|
|
+ cpu = dma_alloc_coherent(ctx->dev->dev,
|
|
+ rcb_size,
|
|
+ &dma,
|
|
+ GFP_KERNEL);
|
|
+ alloc_type = RKVDEC2_ALLOC_DMA;
|
|
+ }
|
|
+
|
|
+ if (!cpu) {
|
|
+ ret = -ENOMEM;
|
|
+ goto err_alloc;
|
|
+ }
|
|
+
|
|
+ cfg->rcb_bufs[i].cpu = cpu;
|
|
+ cfg->rcb_bufs[i].dma = dma;
|
|
+ cfg->rcb_bufs[i].size = rcb_size;
|
|
+ cfg->rcb_bufs[i].type = alloc_type;
|
|
+
|
|
+ cfg->rcb_count += 1;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+
|
|
+err_alloc:
|
|
+ rkvdec_free_rcb(ctx);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.h b/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.h
|
|
new file mode 100644
|
|
index 000000000000..30e8002555c8
|
|
--- /dev/null
|
|
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.h
|
|
@@ -0,0 +1,29 @@
|
|
+/* SPDX-License-Identifier: GPL-2.0 */
|
|
+/*
|
|
+ * Rockchip video decoder Rows and Cols Buffers manager
|
|
+ *
|
|
+ * Copyright (C) 2025 Collabora, Ltd.
|
|
+ * Detlev Casanova <detlev.casanova@collabora.com>
|
|
+ */
|
|
+
|
|
+#include <linux/types.h>
|
|
+
|
|
+struct rkvdec_ctx;
|
|
+
|
|
+enum rcb_axis {
|
|
+ PIC_WIDTH = 0,
|
|
+ PIC_HEIGHT = 1
|
|
+};
|
|
+
|
|
+struct rcb_size_info {
|
|
+ u8 multiplier;
|
|
+ enum rcb_axis axis;
|
|
+};
|
|
+
|
|
+int rkvdec_allocate_rcb(struct rkvdec_ctx *ctx,
|
|
+ const struct rcb_size_info *size_info,
|
|
+ size_t rcb_count);
|
|
+dma_addr_t rkvdec_rcb_buf_dma_addr(struct rkvdec_ctx *ctx, int id);
|
|
+size_t rkvdec_rcb_buf_size(struct rkvdec_ctx *ctx, int id);
|
|
+int rkvdec_rcb_buf_count(struct rkvdec_ctx *ctx);
|
|
+void rkvdec_free_rcb(struct rkvdec_ctx *ctx);
|
|
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec.c b/drivers/media/platform/rockchip/rkvdec/rkvdec.c
|
|
index 033c03051f2e..19deb1d34a18 100644
|
|
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec.c
|
|
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec.c
|
|
@@ -10,6 +10,7 @@
|
|
*/
|
|
|
|
#include <linux/clk.h>
|
|
+#include <linux/genalloc.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/iommu.h>
|
|
#include <linux/module.h>
|
|
@@ -28,6 +29,7 @@
|
|
|
|
#include "rkvdec.h"
|
|
#include "rkvdec-regs.h"
|
|
+#include "rkvdec-rcb.h"
|
|
|
|
static bool rkvdec_image_fmt_match(enum rkvdec_image_fmt fmt1,
|
|
enum rkvdec_image_fmt fmt2)
|
|
@@ -770,6 +772,7 @@ static int rkvdec_start_streaming(struct vb2_queue *q, unsigned int count)
|
|
{
|
|
struct rkvdec_ctx *ctx = vb2_get_drv_priv(q);
|
|
const struct rkvdec_coded_fmt_desc *desc;
|
|
+ const struct rkvdec_config *cfg = ctx->dev->config;
|
|
int ret;
|
|
|
|
if (V4L2_TYPE_IS_CAPTURE(q->type))
|
|
@@ -779,13 +782,22 @@ static int rkvdec_start_streaming(struct vb2_queue *q, unsigned int count)
|
|
if (WARN_ON(!desc))
|
|
return -EINVAL;
|
|
|
|
+ ret = rkvdec_allocate_rcb(ctx, cfg->rcb_size_info, cfg->rcb_num);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
if (desc->ops->start) {
|
|
ret = desc->ops->start(ctx);
|
|
if (ret)
|
|
- return ret;
|
|
+ goto err_ops_start;
|
|
}
|
|
|
|
return 0;
|
|
+
|
|
+err_ops_start:
|
|
+ rkvdec_free_rcb(ctx);
|
|
+
|
|
+ return ret;
|
|
}
|
|
|
|
static void rkvdec_queue_cleanup(struct vb2_queue *vq, u32 state)
|
|
@@ -821,6 +833,8 @@ static void rkvdec_stop_streaming(struct vb2_queue *q)
|
|
|
|
if (desc->ops->stop)
|
|
desc->ops->stop(ctx);
|
|
+
|
|
+ rkvdec_free_rcb(ctx);
|
|
}
|
|
|
|
rkvdec_queue_cleanup(q, VB2_BUF_STATE_ERROR);
|
|
@@ -1339,8 +1353,6 @@ static int rkvdec_probe(struct platform_device *pdev)
|
|
return ret;
|
|
}
|
|
|
|
- vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32));
|
|
-
|
|
irq = platform_get_irq(pdev, 0);
|
|
if (irq <= 0)
|
|
return -ENXIO;
|
|
@@ -1353,6 +1365,10 @@ static int rkvdec_probe(struct platform_device *pdev)
|
|
return ret;
|
|
}
|
|
|
|
+ rkvdec->sram_pool = of_gen_pool_get(pdev->dev.of_node, "sram", 0);
|
|
+ if (!rkvdec->sram_pool && rkvdec->config->rcb_num > 0)
|
|
+ dev_info(&pdev->dev, "No sram node, RCB will be stored in RAM\n");
|
|
+
|
|
pm_runtime_set_autosuspend_delay(&pdev->dev, 100);
|
|
pm_runtime_use_autosuspend(&pdev->dev);
|
|
pm_runtime_enable(&pdev->dev);
|
|
@@ -1361,7 +1377,8 @@ static int rkvdec_probe(struct platform_device *pdev)
|
|
if (ret)
|
|
goto err_disable_runtime_pm;
|
|
|
|
- if (iommu_get_domain_for_dev(&pdev->dev)) {
|
|
+ rkvdec->iommu_domain = iommu_get_domain_for_dev(&pdev->dev);
|
|
+ if (rkvdec->iommu_domain) {
|
|
rkvdec->empty_domain = iommu_paging_domain_alloc(rkvdec->dev);
|
|
|
|
if (IS_ERR(rkvdec->empty_domain)) {
|
|
@@ -1375,6 +1392,10 @@ static int rkvdec_probe(struct platform_device *pdev)
|
|
err_disable_runtime_pm:
|
|
pm_runtime_dont_use_autosuspend(&pdev->dev);
|
|
pm_runtime_disable(&pdev->dev);
|
|
+
|
|
+ if (rkvdec->sram_pool)
|
|
+ gen_pool_destroy(rkvdec->sram_pool);
|
|
+
|
|
return ret;
|
|
}
|
|
|
|
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec.h b/drivers/media/platform/rockchip/rkvdec/rkvdec.h
|
|
index d8a1b3e8e0ef..86d1af5dee5e 100644
|
|
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec.h
|
|
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec.h
|
|
@@ -19,6 +19,7 @@
|
|
#include <media/v4l2-ctrls.h>
|
|
#include <media/v4l2-device.h>
|
|
#include <media/v4l2-ioctl.h>
|
|
+#include <media/v4l2-mem2mem.h>
|
|
#include <media/videobuf2-core.h>
|
|
#include <media/videobuf2-dma-contig.h>
|
|
|
|
@@ -29,6 +30,7 @@
|
|
#define RKVDEC_QUIRK_DISABLE_QOS BIT(0)
|
|
|
|
struct rkvdec_ctx;
|
|
+struct rkvdec_rcb_config;
|
|
|
|
struct rkvdec_ctrl_desc {
|
|
struct v4l2_ctrl_config cfg;
|
|
@@ -117,6 +119,8 @@ struct rkvdec_coded_fmt_desc {
|
|
struct rkvdec_config {
|
|
struct rkvdec_coded_fmt_desc *coded_fmts;
|
|
size_t coded_fmts_num;
|
|
+ struct rcb_size_info *rcb_size_info;
|
|
+ size_t rcb_num;
|
|
};
|
|
|
|
struct rkvdec_dev {
|
|
@@ -129,6 +133,8 @@ struct rkvdec_dev {
|
|
void __iomem *regs;
|
|
struct mutex vdev_lock; /* serializes ioctls */
|
|
struct delayed_work watchdog_work;
|
|
+ struct gen_pool *sram_pool;
|
|
+ struct iommu_domain *iommu_domain;
|
|
struct iommu_domain *empty_domain;
|
|
const struct rkvdec_variant *variant;
|
|
const struct rkvdec_config *config;
|
|
@@ -144,6 +150,7 @@ struct rkvdec_ctx {
|
|
struct v4l2_ctrl_handler ctrl_hdl;
|
|
struct rkvdec_dev *dev;
|
|
enum rkvdec_image_fmt image_fmt;
|
|
+ struct rkvdec_rcb_config *rcb_config;
|
|
void *priv;
|
|
};
|
|
|
|
@@ -152,10 +159,16 @@ static inline struct rkvdec_ctx *fh_to_rkvdec_ctx(struct v4l2_fh *fh)
|
|
return container_of(fh, struct rkvdec_ctx, fh);
|
|
}
|
|
|
|
+enum rkvdec_alloc_type {
|
|
+ RKVDEC2_ALLOC_DMA = 0,
|
|
+ RKVDEC2_ALLOC_SRAM = 1,
|
|
+};
|
|
+
|
|
struct rkvdec_aux_buf {
|
|
void *cpu;
|
|
dma_addr_t dma;
|
|
size_t size;
|
|
+ enum rkvdec_alloc_type type;
|
|
};
|
|
|
|
void rkvdec_run_preamble(struct rkvdec_ctx *ctx, struct rkvdec_run *run);
|
|
--
|
|
2.34.1
|
|
|