mirror of
https://github.com/openwrt/openwrt.git
synced 2026-10-21 09:05:28 +04:00
rockchip: add the RKVENC H.264 and HEVC encoder
Add the stateful V4L2 encoder driver for the RKVENC v1 of the RV1126 and RK3568 as patches 183: its binding, the PD_VEPU clock tree, the driver and the device tree node, followed by the frequency scaling with a cooling device and the operating points that go with it. The operating points carry the vendor voltages, which apply once a board names the supply of the encoder. The NPU thermal zone gets two passive trips and cools the encoder, which lowers its clock and voltage and then its frame rate. The zone is polled every second, since the temperature trend decides when the cooling device is released. Build the devfreq core and the simple_ondemand governor into the ARMv7 kernel for the frequency scaling, and let the 38x38 v10 board name vdd_logic_npu_vepu as the supply of the encoder. Add kmod-video-rockchip-rkvenc and install it on the Herospeed RV1126 board. Signed-off-by: Daniel Golle <daniel@makrotopia.org>
This commit is contained in:
1 parent
a59f202f75
commit
64129c4282
12 files changed
+5428
-1
No files matched your search
@@ -111,6 +111,7 @@ CONFIG_CRYPTO_LIB_UTILS=y
|
||||
CONFIG_CURRENT_POINTER_IN_TPIDRURO=y
|
||||
CONFIG_DCACHE_WORD_ACCESS=y
|
||||
CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S"
|
||||
CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND=y
|
||||
CONFIG_DMADEVICES=y
|
||||
CONFIG_DMA_CMA=y
|
||||
CONFIG_DMA_ENGINE=y
|
||||
@@ -284,6 +285,7 @@ CONFIG_PINCTRL_ROCKCHIP=y
|
||||
CONFIG_PL330_DMA=y
|
||||
CONFIG_PM=y
|
||||
CONFIG_PM_CLK=y
|
||||
CONFIG_PM_DEVFREQ=y
|
||||
CONFIG_PM_GENERIC_DOMAINS=y
|
||||
CONFIG_PM_GENERIC_DOMAINS_OF=y
|
||||
CONFIG_PM_OPP=y
|
||||
|
||||
@@ -253,6 +253,10 @@
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&venc {
|
||||
venc-supply = <&vdd_logic_npu_vepu>;
|
||||
};
|
||||
|
||||
&wdt {
|
||||
status = "okay";
|
||||
};
|
||||
@@ -47,7 +47,7 @@ define Device/herospeed_rv1126-imx415
|
||||
DEVICE_PACKAGES := kmod-rtc-pcf8563 kmod-video-core kmod-video-imx415 \
|
||||
kmod-video-rockchip-cif kmod-phy-rockchip-inno-csidphy \
|
||||
kmod-leds-pwm-flash kmod-crypto-hw-rockchip2 kmod-video-hantro \
|
||||
kmod-video-rockchip-isp2 video-support
|
||||
kmod-video-rockchip-isp2 kmod-video-rockchip-rkvenc video-support
|
||||
IMAGES := sysupgrade.bin factory-RV1126_IMX415.fls
|
||||
IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata
|
||||
IMAGE/factory-RV1126_IMX415.fls := herospeed-fls
|
||||
|
||||
@@ -282,3 +282,19 @@ define KernelPackage/video-rockchip-isp2/description
|
||||
endef
|
||||
|
||||
$(eval $(call KernelPackage,video-rockchip-isp2))
|
||||
|
||||
define KernelPackage/video-rockchip-rkvenc
|
||||
TITLE:=Rockchip RKVENC H.264/HEVC video encoder
|
||||
DEPENDS:=@TARGET_rockchip +kmod-video-mem2mem +kmod-video-dma-contig
|
||||
KCONFIG:=CONFIG_VIDEO_ROCKCHIP_RKVENC
|
||||
FILES:=$(LINUX_DIR)/drivers/media/platform/rockchip/rkvenc/rockchip-rkvenc.ko
|
||||
AUTOLOAD:=$(call AutoProbe,rockchip-rkvenc)
|
||||
$(call AddDepends/video)
|
||||
endef
|
||||
|
||||
define KernelPackage/video-rockchip-rkvenc/description
|
||||
Stateful V4L2 memory-to-memory encoder driver for the RKVENC v1 video
|
||||
encoder of the Rockchip RV1126 and RK3568 SoCs (H.264 and HEVC).
|
||||
endef
|
||||
|
||||
$(eval $(call KernelPackage,video-rockchip-rkvenc))
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
From a18e130d6caff08ebc57a665e7ff8e4aa3c7cb3e Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Mon, 12 Oct 2026 17:10:27 +0100
|
||||
Subject: [PATCH] dt-bindings: media: rockchip: Add Rockchip RKVENC v1 encoder
|
||||
binding
|
||||
|
||||
Add the device tree binding for the Rockchip RKVENC v1 video encoder
|
||||
found in the RV1126/RV1109 ("vepu541") and RK3566/RK3568 ("vepu540")
|
||||
SoCs. The block encodes H.264 (AVC) and H.265 (HEVC) from raw
|
||||
YUV/RGB frames and reaches memory through an IOMMU.
|
||||
|
||||
Document the two compatible strings, the register space, the single
|
||||
interrupt, the axi/ahb/core clock and reset triplets, the power
|
||||
domain and the IOMMU phandle.
|
||||
|
||||
Assisted-by: LLM
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
---
|
||||
.../media/rockchip,rv1126-rkvenc.yaml | 102 ++++++++++++++++++
|
||||
1 file changed, 102 insertions(+)
|
||||
create mode 100644 Documentation/devicetree/bindings/media/rockchip,rv1126-rkvenc.yaml
|
||||
|
||||
--- /dev/null
|
||||
+++ b/Documentation/devicetree/bindings/media/rockchip,rv1126-rkvenc.yaml
|
||||
@@ -0,0 +1,102 @@
|
||||
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
+%YAML 1.2
|
||||
+---
|
||||
+$id: http://devicetree.org/schemas/media/rockchip,rv1126-rkvenc.yaml#
|
||||
+$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
+
|
||||
+title: Rockchip RKVENC v1 H.264/HEVC video encoder
|
||||
+
|
||||
+maintainers:
|
||||
+ - Daniel Golle <daniel@makrotopia.org>
|
||||
+
|
||||
+description:
|
||||
+ The RKVENC v1 encoder found in the RV1109, RV1126, RK3566 and RK3568
|
||||
+ encodes H.264 and HEVC from raw YUV or RGB frames. It reads and writes
|
||||
+ memory through an IOMMU with two MMU instances.
|
||||
+
|
||||
+properties:
|
||||
+ compatible:
|
||||
+ enum:
|
||||
+ - rockchip,rk3568-rkvenc
|
||||
+ - rockchip,rv1126-rkvenc
|
||||
+
|
||||
+ reg:
|
||||
+ maxItems: 1
|
||||
+
|
||||
+ interrupts:
|
||||
+ maxItems: 1
|
||||
+
|
||||
+ clocks:
|
||||
+ items:
|
||||
+ - description: AXI master clock
|
||||
+ - description: AHB register interface clock
|
||||
+ - description: Encoder core clock
|
||||
+
|
||||
+ clock-names:
|
||||
+ items:
|
||||
+ - const: axi
|
||||
+ - const: ahb
|
||||
+ - const: core
|
||||
+
|
||||
+ assigned-clocks: true
|
||||
+
|
||||
+ assigned-clock-rates: true
|
||||
+
|
||||
+ resets:
|
||||
+ items:
|
||||
+ - description: AXI reset
|
||||
+ - description: AHB reset
|
||||
+ - description: Encoder core reset
|
||||
+
|
||||
+ reset-names:
|
||||
+ items:
|
||||
+ - const: axi
|
||||
+ - const: ahb
|
||||
+ - const: core
|
||||
+
|
||||
+ power-domains:
|
||||
+ maxItems: 1
|
||||
+
|
||||
+ iommus:
|
||||
+ maxItems: 1
|
||||
+
|
||||
+required:
|
||||
+ - compatible
|
||||
+ - reg
|
||||
+ - interrupts
|
||||
+ - clocks
|
||||
+ - clock-names
|
||||
+ - resets
|
||||
+ - reset-names
|
||||
+ - power-domains
|
||||
+ - iommus
|
||||
+
|
||||
+additionalProperties: false
|
||||
+
|
||||
+examples:
|
||||
+ - |
|
||||
+ #include <dt-bindings/clock/rockchip,rv1126-cru.h>
|
||||
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
+ #include <dt-bindings/power/rockchip,rv1126-power.h>
|
||||
+
|
||||
+ venc_mmu: iommu@ffbb0800 {
|
||||
+ compatible = "rockchip,rv1126-iommu";
|
||||
+ reg = <0xffbb0800 0x100>;
|
||||
+ #iommu-cells = <0>;
|
||||
+ };
|
||||
+
|
||||
+ video-codec@ffbb0000 {
|
||||
+ compatible = "rockchip,rv1126-rkvenc";
|
||||
+ reg = <0xffbb0000 0xf00>;
|
||||
+ interrupts = <GIC_SPI 68 IRQ_TYPE_LEVEL_HIGH>;
|
||||
+ clocks = <&cru ACLK_VENC>, <&cru HCLK_VENC>, <&cru CLK_VENC_CORE>;
|
||||
+ clock-names = "axi", "ahb", "core";
|
||||
+ assigned-clocks = <&cru ACLK_VENC>, <&cru CLK_VENC_CORE>;
|
||||
+ assigned-clock-rates = <297000000>, <396000000>;
|
||||
+ resets = <&cru SRST_VENC_A>, <&cru SRST_VENC_H>,
|
||||
+ <&cru SRST_VENC_CORE>;
|
||||
+ reset-names = "axi", "ahb", "core";
|
||||
+ iommus = <&venc_mmu>;
|
||||
+ power-domains = <&power RV1126_PD_VEPU>;
|
||||
+ };
|
||||
+...
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
From 1a3e37de23fd12f452c6939d37d0dd62a77a4bf0 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Fri, 16 Oct 2026 17:39:42 +0100
|
||||
Subject: [PATCH] clk: rockchip: rv1126: Add PD_VEPU clock tree
|
||||
|
||||
The video encoder power domain of the RV1126 contains the RKVENC
|
||||
encoder. Add its AXI and AHB bus clocks, the AXI and AHB clocks of the
|
||||
encoder and the encoder core clock, together with the two interconnect
|
||||
gates that stay enabled.
|
||||
|
||||
Assisted-by: LLM
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
---
|
||||
|
||||
--- a/drivers/clk/rockchip/clk-rv1126.c
|
||||
+++ b/drivers/clk/rockchip/clk-rv1126.c
|
||||
@@ -756,6 +756,25 @@ static struct rockchip_clk_branch rv1126
|
||||
RV1126_CLKSEL_CON(72), 8, 1, MFLAGS, 0, 7, DFLAGS,
|
||||
RV1126_CLKGATE_CON(11), 1, GFLAGS),
|
||||
|
||||
+ /* PD_VEPU */
|
||||
+ COMPOSITE(ACLK_PDVEPU, "aclk_pdvepu", mux_cpll_hpll_gpll_p, 0,
|
||||
+ RV1126_CLKSEL_CON(40), 6, 2, MFLAGS, 0, 5, DFLAGS,
|
||||
+ RV1126_CLKGATE_CON(12), 0, GFLAGS),
|
||||
+ COMPOSITE_NOMUX(HCLK_PDVEPU, "hclk_pdvepu", "aclk_pdvepu", 0,
|
||||
+ RV1126_CLKSEL_CON(41), 0, 5, DFLAGS,
|
||||
+ RV1126_CLKGATE_CON(12), 2, GFLAGS),
|
||||
+ GATE(ACLK_VENC, "aclk_venc", "aclk_pdvepu", 0,
|
||||
+ RV1126_CLKGATE_CON(12), 5, GFLAGS),
|
||||
+ GATE(HCLK_VENC, "hclk_venc", "hclk_pdvepu", 0,
|
||||
+ RV1126_CLKGATE_CON(12), 6, GFLAGS),
|
||||
+ COMPOSITE(CLK_VENC_CORE, "clk_venc_core", mux_cpll_gpll_hpll_p, 0,
|
||||
+ RV1126_CLKSEL_CON(40), 14, 2, MFLAGS, 8, 5, DFLAGS,
|
||||
+ RV1126_CLKGATE_CON(12), 1, GFLAGS),
|
||||
+ GATE(0, "aclk_pdvepu_niu", "aclk_pdvepu", CLK_IGNORE_UNUSED,
|
||||
+ RV1126_CLKGATE_CON(12), 3, GFLAGS),
|
||||
+ GATE(0, "hclk_pdvepu_niu", "hclk_pdvepu", CLK_IGNORE_UNUSED,
|
||||
+ RV1126_CLKGATE_CON(12), 4, GFLAGS),
|
||||
+
|
||||
/*
|
||||
* Clock-Architecture Diagram 8
|
||||
*/
|
||||
+4394
File diff suppressed because it is too large.
Load diff
+92
@@ -0,0 +1,92 @@
|
||||
From de2e57e70ad2eb96fa88193bd700b4a7e68ff75c Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Fri, 16 Oct 2026 17:39:58 +0100
|
||||
Subject: [PATCH] ARM: dts: rockchip: rv1126: Add the RKVENC video encoder
|
||||
|
||||
Add the encoder with its IOMMU, the VEPU power domain and the three
|
||||
QoS nodes of the encoder ports. The encoder AXI clock is set to
|
||||
297 MHz and the core clock to 396 MHz.
|
||||
|
||||
Assisted-by: LLM
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
---
|
||||
arch/arm/boot/dts/rockchip/rv1126.dtsi | 54 ++++++++++++++++++++++++++
|
||||
1 file changed, 54 insertions(+)
|
||||
|
||||
--- a/arch/arm/boot/dts/rockchip/rv1126.dtsi
|
||||
+++ b/arch/arm/boot/dts/rockchip/rv1126.dtsi
|
||||
@@ -331,6 +331,21 @@
|
||||
reg = <0xfe8d0000 0x20>;
|
||||
};
|
||||
|
||||
+ qos_venc_rd_m0: qos@fe870000 {
|
||||
+ compatible = "rockchip,rv1126-qos", "syscon";
|
||||
+ reg = <0xfe870000 0x20>;
|
||||
+ };
|
||||
+
|
||||
+ qos_venc_rd_m1: qos@fe870080 {
|
||||
+ compatible = "rockchip,rv1126-qos", "syscon";
|
||||
+ reg = <0xfe870080 0x20>;
|
||||
+ };
|
||||
+
|
||||
+ qos_venc_wr: qos@fe870100 {
|
||||
+ compatible = "rockchip,rv1126-qos", "syscon";
|
||||
+ reg = <0xfe870100 0x20>;
|
||||
+ };
|
||||
+
|
||||
qos_ispp_m0: qos@fe880000 {
|
||||
compatible = "rockchip,rv1126-qos", "syscon";
|
||||
reg = <0xfe880000 0x20>;
|
||||
@@ -472,6 +487,17 @@
|
||||
<&qos_iep>;
|
||||
#power-domain-cells = <0>;
|
||||
};
|
||||
+
|
||||
+ power-domain@RV1126_PD_VEPU {
|
||||
+ reg = <RV1126_PD_VEPU>;
|
||||
+ clocks = <&cru ACLK_VENC>,
|
||||
+ <&cru HCLK_VENC>,
|
||||
+ <&cru CLK_VENC_CORE>;
|
||||
+ pm_qos = <&qos_venc_rd_m0>,
|
||||
+ <&qos_venc_rd_m1>,
|
||||
+ <&qos_venc_wr>;
|
||||
+ #power-domain-cells = <0>;
|
||||
+ };
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1315,6 +1341,34 @@
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
+ venc: video-codec@ffbb0000 {
|
||||
+ compatible = "rockchip,rv1126-rkvenc";
|
||||
+ reg = <0xffbb0000 0xf00>;
|
||||
+ interrupts = <GIC_SPI 68 IRQ_TYPE_LEVEL_HIGH>;
|
||||
+ clocks = <&cru ACLK_VENC>, <&cru HCLK_VENC>,
|
||||
+ <&cru CLK_VENC_CORE>;
|
||||
+ clock-names = "axi", "ahb", "core";
|
||||
+ assigned-clocks = <&cru ACLK_VENC>, <&cru CLK_VENC_CORE>;
|
||||
+ assigned-clock-rates = <297000000>, <396000000>;
|
||||
+ resets = <&cru SRST_VENC_A>, <&cru SRST_VENC_H>,
|
||||
+ <&cru SRST_VENC_CORE>;
|
||||
+ reset-names = "axi", "ahb", "core";
|
||||
+ iommus = <&venc_mmu>;
|
||||
+ power-domains = <&power RV1126_PD_VEPU>;
|
||||
+ };
|
||||
+
|
||||
+ venc_mmu: iommu@ffbb0f00 {
|
||||
+ compatible = "rockchip,iommu";
|
||||
+ reg = <0xffbb0f00 0x40>, <0xffbb0f40 0x40>;
|
||||
+ interrupts = <GIC_SPI 69 IRQ_TYPE_LEVEL_HIGH>,
|
||||
+ <GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>;
|
||||
+ clocks = <&cru ACLK_VENC>, <&cru HCLK_VENC>;
|
||||
+ clock-names = "aclk", "iface";
|
||||
+ #iommu-cells = <0>;
|
||||
+ power-domains = <&power RV1126_PD_VEPU>;
|
||||
+ rockchip,disable-mmu-reset;
|
||||
+ };
|
||||
+
|
||||
gmac: ethernet@ffc40000 {
|
||||
compatible = "rockchip,rv1126-gmac", "snps,dwmac-4.20a";
|
||||
reg = <0xffc40000 0x4000>;
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
From 3abba483e809d697f09aab1bc7e1f1e9d2175fc3 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Fri, 16 Oct 2026 21:16:15 +0100
|
||||
Subject: [PATCH] dt-bindings: media: rockchip,rkvenc: Add operating points
|
||||
|
||||
The encoder core clock and its supply are scaled between operating
|
||||
points, and the encoder is a cooling device of the thermal zones.
|
||||
|
||||
Assisted-by: LLM
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
---
|
||||
.../devicetree/bindings/media/rockchip,rv1126-rkvenc.yaml | 8 ++++++++
|
||||
1 file changed, 8 insertions(+)
|
||||
|
||||
--- a/Documentation/devicetree/bindings/media/rockchip,rv1126-rkvenc.yaml
|
||||
+++ b/Documentation/devicetree/bindings/media/rockchip,rv1126-rkvenc.yaml
|
||||
@@ -42,6 +42,14 @@ properties:
|
||||
|
||||
assigned-clock-rates: true
|
||||
|
||||
+ operating-points-v2: true
|
||||
+
|
||||
+ venc-supply:
|
||||
+ description: Supply of the encoder core, scaled with the operating points.
|
||||
+
|
||||
+ "#cooling-cells":
|
||||
+ const: 2
|
||||
+
|
||||
resets:
|
||||
items:
|
||||
- description: AXI reset
|
||||
+601
@@ -0,0 +1,601 @@
|
||||
From 199c03b142bb66890d48de1685b42d54290a5394 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Fri, 16 Oct 2026 23:57:17 +0100
|
||||
Subject: [PATCH] media: rockchip: rkvenc: Add frequency scaling and cooling
|
||||
|
||||
The encoder core runs at the fixed 396 MHz of the clock tree setup,
|
||||
which is a little short of a 4K stream at 30 frames per second.
|
||||
|
||||
Register the core clock with devfreq. The simple_ondemand governor
|
||||
runs whenever a job completes and takes the busy time of the jobs as
|
||||
load, so no polling interval is involved. The clock changes only
|
||||
while no job is on the hardware: a job start waits for a clock change
|
||||
in progress and a clock change waits for the running job. A venc-supply
|
||||
in the node is scaled with the operating points.
|
||||
|
||||
Register a cooling device on top. Its first states lower the highest
|
||||
allowed operating point through a PM QoS frequency limit, one point
|
||||
per state. The remaining states stretch the period between jobs so
|
||||
that the busy time stays at 75, 50 and 25 percent of it, which limits
|
||||
the frame rate once the lowest point is reached. The cooling device,
|
||||
the QoS request and the devfreq device are removed from remove(),
|
||||
before the driver data goes away, since their callbacks use it.
|
||||
|
||||
Assisted-by: LLM
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
---
|
||||
.../media/platform/rockchip/rkvenc/Kconfig | 3 +
|
||||
.../media/platform/rockchip/rkvenc/Makefile | 1 +
|
||||
.../platform/rockchip/rkvenc/rkvenc-dvfs.c | 352 ++++++++++++++++++
|
||||
.../platform/rockchip/rkvenc/rkvenc-v4l2.c | 19 +-
|
||||
.../media/platform/rockchip/rkvenc/rkvenc.c | 10 +-
|
||||
.../media/platform/rockchip/rkvenc/rkvenc.h | 45 +++
|
||||
6 files changed, 427 insertions(+), 3 deletions(-)
|
||||
create mode 100644 drivers/media/platform/rockchip/rkvenc/rkvenc-dvfs.c
|
||||
|
||||
--- a/drivers/media/platform/rockchip/rkvenc/Kconfig
|
||||
+++ b/drivers/media/platform/rockchip/rkvenc/Kconfig
|
||||
@@ -3,7 +3,10 @@ config VIDEO_ROCKCHIP_RKVENC
|
||||
tristate "Rockchip RKVENC (vepu540/vepu541) H.264/HEVC encoder"
|
||||
depends on ARCH_ROCKCHIP || COMPILE_TEST
|
||||
depends on VIDEO_DEV
|
||||
+ select DEVFREQ_GOV_SIMPLE_ONDEMAND
|
||||
select MEDIA_CONTROLLER
|
||||
+ select PM_DEVFREQ
|
||||
+ select PM_OPP
|
||||
select VIDEOBUF2_DMA_CONTIG
|
||||
select V4L2_MEM2MEM_DEV
|
||||
help
|
||||
--- a/drivers/media/platform/rockchip/rkvenc/Makefile
|
||||
+++ b/drivers/media/platform/rockchip/rkvenc/Makefile
|
||||
@@ -1,5 +1,6 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
rockchip-rkvenc-y += rkvenc.o \
|
||||
+ rkvenc-dvfs.o \
|
||||
rkvenc-v4l2.o \
|
||||
rkvenc-gop.o \
|
||||
rkvenc-ref.o \
|
||||
--- /dev/null
|
||||
+++ b/drivers/media/platform/rockchip/rkvenc/rkvenc-dvfs.c
|
||||
@@ -0,0 +1,352 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0-only
|
||||
+/*
|
||||
+ * Rockchip RKVENC v1 frequency scaling and thermal cooling.
|
||||
+ *
|
||||
+ * The core clock follows the load through devfreq. The governor is
|
||||
+ * evaluated whenever a job completes, so there is no polling interval.
|
||||
+ * The clock only changes while no job is running.
|
||||
+ *
|
||||
+ * The cooling device lowers the highest allowed frequency state by state.
|
||||
+ * Once the lowest operating point is reached, further states stretch the
|
||||
+ * period between jobs so that the busy time stays within a share of it.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/devfreq.h>
|
||||
+#include <linux/ktime.h>
|
||||
+#include <linux/math64.h>
|
||||
+#include <linux/pm_opp.h>
|
||||
+#include <linux/pm_qos.h>
|
||||
+#include <linux/thermal.h>
|
||||
+#include <linux/units.h>
|
||||
+
|
||||
+#include "rkvenc.h"
|
||||
+
|
||||
+#define RKVENC_THROTTLE_STEPS 3
|
||||
+#define RKVENC_THROTTLE_STEP_PCT 25
|
||||
+
|
||||
+static unsigned long rkvenc_dvfs_us(ktime_t t)
|
||||
+{
|
||||
+ return min_t(u64, ktime_to_us(t), ULONG_MAX);
|
||||
+}
|
||||
+
|
||||
+static int rkvenc_dvfs_target(struct device *dev, unsigned long *freq,
|
||||
+ u32 flags)
|
||||
+{
|
||||
+ struct rkvenc_dev *rkvenc = dev_get_drvdata(dev);
|
||||
+ struct rkvenc_dvfs *dvfs = &rkvenc->dvfs;
|
||||
+ struct dev_pm_opp *opp;
|
||||
+ int ret;
|
||||
+
|
||||
+ opp = devfreq_recommended_opp(dev, freq, flags);
|
||||
+ if (IS_ERR(opp))
|
||||
+ return PTR_ERR(opp);
|
||||
+
|
||||
+ *freq = dev_pm_opp_get_freq(opp);
|
||||
+ dev_pm_opp_put(opp);
|
||||
+
|
||||
+ mutex_lock(&dvfs->freq_lock);
|
||||
+ wait_event(dvfs->idle_wq, !READ_ONCE(dvfs->busy));
|
||||
+ ret = dev_pm_opp_set_rate(dev, *freq);
|
||||
+ mutex_unlock(&dvfs->freq_lock);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int rkvenc_dvfs_cur_freq(struct device *dev, unsigned long *freq)
|
||||
+{
|
||||
+ struct rkvenc_dev *rkvenc = dev_get_drvdata(dev);
|
||||
+
|
||||
+ *freq = clk_get_rate(rkvenc->clocks[RKVENC_CLK_CORE].clk);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int rkvenc_dvfs_status(struct device *dev,
|
||||
+ struct devfreq_dev_status *stat)
|
||||
+{
|
||||
+ struct rkvenc_dev *rkvenc = dev_get_drvdata(dev);
|
||||
+ struct rkvenc_dvfs *dvfs = &rkvenc->dvfs;
|
||||
+ unsigned long flags;
|
||||
+ ktime_t now, busy;
|
||||
+
|
||||
+ spin_lock_irqsave(&dvfs->lock, flags);
|
||||
+ now = ktime_get();
|
||||
+ busy = dvfs->busy_total;
|
||||
+ if (dvfs->busy) {
|
||||
+ busy = ktime_add(busy, ktime_sub(now, dvfs->busy_since));
|
||||
+ dvfs->busy_since = now;
|
||||
+ }
|
||||
+ stat->total_time = rkvenc_dvfs_us(ktime_sub(now, dvfs->last_eval));
|
||||
+ stat->busy_time = min(rkvenc_dvfs_us(busy), stat->total_time);
|
||||
+ dvfs->busy_total = 0;
|
||||
+ dvfs->last_eval = now;
|
||||
+ spin_unlock_irqrestore(&dvfs->lock, flags);
|
||||
+
|
||||
+ rkvenc_dvfs_cur_freq(dev, &stat->current_frequency);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+void rkvenc_dvfs_job_begin(struct rkvenc_dev *rkvenc)
|
||||
+{
|
||||
+ struct rkvenc_dvfs *dvfs = &rkvenc->dvfs;
|
||||
+ unsigned long flags;
|
||||
+
|
||||
+ mutex_lock(&dvfs->freq_lock);
|
||||
+ spin_lock_irqsave(&dvfs->lock, flags);
|
||||
+ dvfs->busy = true;
|
||||
+ dvfs->job_start = ktime_get();
|
||||
+ dvfs->busy_since = dvfs->job_start;
|
||||
+ spin_unlock_irqrestore(&dvfs->lock, flags);
|
||||
+ mutex_unlock(&dvfs->freq_lock);
|
||||
+}
|
||||
+
|
||||
+bool rkvenc_dvfs_start_allowed(struct rkvenc_dev *rkvenc)
|
||||
+{
|
||||
+ struct rkvenc_dvfs *dvfs = &rkvenc->dvfs;
|
||||
+ unsigned long flags, delay;
|
||||
+ s64 delay_us;
|
||||
+ bool allowed;
|
||||
+
|
||||
+ spin_lock_irqsave(&dvfs->lock, flags);
|
||||
+ delay_us = ktime_to_us(ktime_sub(dvfs->next_start, ktime_get()));
|
||||
+ allowed = dvfs->duty_pct >= 100 || delay_us <= 0;
|
||||
+ delay = usecs_to_jiffies(clamp_t(s64, delay_us, 0, UINT_MAX));
|
||||
+ if (!allowed)
|
||||
+ queue_delayed_work(system_wq, &dvfs->throttle_work, delay);
|
||||
+ spin_unlock_irqrestore(&dvfs->lock, flags);
|
||||
+
|
||||
+ return allowed;
|
||||
+}
|
||||
+
|
||||
+void rkvenc_dvfs_job_end(struct rkvenc_dev *rkvenc)
|
||||
+{
|
||||
+ struct rkvenc_dvfs *dvfs = &rkvenc->dvfs;
|
||||
+ struct devfreq *devfreq = dvfs->devfreq;
|
||||
+ unsigned long flags;
|
||||
+ ktime_t now;
|
||||
+
|
||||
+ spin_lock_irqsave(&dvfs->lock, flags);
|
||||
+ now = ktime_get();
|
||||
+ dvfs->busy_total =
|
||||
+ ktime_add(dvfs->busy_total, ktime_sub(now, dvfs->busy_since));
|
||||
+ dvfs->next_start = ktime_add_ns(
|
||||
+ dvfs->job_start,
|
||||
+ div_u64(ktime_to_ns(ktime_sub(now, dvfs->job_start)) * 100,
|
||||
+ dvfs->duty_pct));
|
||||
+ dvfs->busy = false;
|
||||
+ spin_unlock_irqrestore(&dvfs->lock, flags);
|
||||
+ wake_up(&dvfs->idle_wq);
|
||||
+
|
||||
+ if (!devfreq)
|
||||
+ return;
|
||||
+
|
||||
+ mutex_lock(&devfreq->lock);
|
||||
+ update_devfreq(devfreq);
|
||||
+ mutex_unlock(&devfreq->lock);
|
||||
+}
|
||||
+
|
||||
+static int rkvenc_dvfs_freqs_get(struct rkvenc_dev *rkvenc)
|
||||
+{
|
||||
+ struct rkvenc_dvfs *dvfs = &rkvenc->dvfs;
|
||||
+ struct device *dev = rkvenc->dev;
|
||||
+ struct dev_pm_opp *opp;
|
||||
+ unsigned long freq = 0;
|
||||
+ int num, i;
|
||||
+
|
||||
+ num = dev_pm_opp_get_opp_count(dev);
|
||||
+ if (num <= 0)
|
||||
+ return num ? num : -ENODEV;
|
||||
+
|
||||
+ dvfs->freqs = devm_kcalloc(dev, num, sizeof(*dvfs->freqs), GFP_KERNEL);
|
||||
+ if (!dvfs->freqs)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ for (i = 0; i < num; i++, freq++) {
|
||||
+ opp = dev_pm_opp_find_freq_ceil(dev, &freq);
|
||||
+ if (IS_ERR(opp))
|
||||
+ return PTR_ERR(opp);
|
||||
+ dvfs->freqs[i] = freq;
|
||||
+ dev_pm_opp_put(opp);
|
||||
+ }
|
||||
+
|
||||
+ dvfs->num_freqs = num;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int rkvenc_cdev_max_state(struct thermal_cooling_device *cdev,
|
||||
+ unsigned long *state)
|
||||
+{
|
||||
+ struct rkvenc_dvfs *dvfs = cdev->devdata;
|
||||
+
|
||||
+ *state = dvfs->num_freqs - 1 + RKVENC_THROTTLE_STEPS;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int rkvenc_cdev_cur_state(struct thermal_cooling_device *cdev,
|
||||
+ unsigned long *state)
|
||||
+{
|
||||
+ struct rkvenc_dvfs *dvfs = cdev->devdata;
|
||||
+
|
||||
+ *state = dvfs->cooling_state;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int rkvenc_cdev_set_state(struct thermal_cooling_device *cdev,
|
||||
+ unsigned long state)
|
||||
+{
|
||||
+ struct rkvenc_dvfs *dvfs = cdev->devdata;
|
||||
+ unsigned long idx = min_t(unsigned long, state, dvfs->num_freqs - 1);
|
||||
+ unsigned long freq = dvfs->freqs[dvfs->num_freqs - 1 - idx];
|
||||
+ unsigned long flags;
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = dev_pm_qos_update_request(&dvfs->max_freq_req, freq / HZ_PER_KHZ);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ spin_lock_irqsave(&dvfs->lock, flags);
|
||||
+ dvfs->duty_pct = 100 - RKVENC_THROTTLE_STEP_PCT * (state - idx);
|
||||
+ spin_unlock_irqrestore(&dvfs->lock, flags);
|
||||
+ dvfs->cooling_state = state;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct thermal_cooling_device_ops rkvenc_cdev_ops = {
|
||||
+ .get_max_state = rkvenc_cdev_max_state,
|
||||
+ .get_cur_state = rkvenc_cdev_cur_state,
|
||||
+ .set_cur_state = rkvenc_cdev_set_state,
|
||||
+};
|
||||
+
|
||||
+static void rkvenc_dvfs_throttle_work(struct work_struct *work)
|
||||
+{
|
||||
+ struct rkvenc_dvfs *dvfs = container_of(
|
||||
+ to_delayed_work(work), struct rkvenc_dvfs, throttle_work);
|
||||
+ struct rkvenc_ctx *ctx;
|
||||
+
|
||||
+ mutex_lock(&dvfs->ctx_lock);
|
||||
+ list_for_each_entry(ctx, &dvfs->ctx_list, dvfs_node)
|
||||
+ v4l2_m2m_try_schedule(ctx->m2m_ctx);
|
||||
+ mutex_unlock(&dvfs->ctx_lock);
|
||||
+}
|
||||
+
|
||||
+void rkvenc_dvfs_ctx_add(struct rkvenc_dev *rkvenc, struct rkvenc_ctx *ctx)
|
||||
+{
|
||||
+ struct rkvenc_dvfs *dvfs = &rkvenc->dvfs;
|
||||
+
|
||||
+ mutex_lock(&dvfs->ctx_lock);
|
||||
+ list_add_tail(&ctx->dvfs_node, &dvfs->ctx_list);
|
||||
+ mutex_unlock(&dvfs->ctx_lock);
|
||||
+}
|
||||
+
|
||||
+void rkvenc_dvfs_ctx_del(struct rkvenc_dev *rkvenc, struct rkvenc_ctx *ctx)
|
||||
+{
|
||||
+ struct rkvenc_dvfs *dvfs = &rkvenc->dvfs;
|
||||
+
|
||||
+ mutex_lock(&dvfs->ctx_lock);
|
||||
+ list_del(&ctx->dvfs_node);
|
||||
+ mutex_unlock(&dvfs->ctx_lock);
|
||||
+}
|
||||
+
|
||||
+void rkvenc_dvfs_exit(struct rkvenc_dev *rkvenc)
|
||||
+{
|
||||
+ struct rkvenc_dvfs *dvfs = &rkvenc->dvfs;
|
||||
+
|
||||
+ thermal_cooling_device_unregister(dvfs->cdev);
|
||||
+ dvfs->cdev = NULL;
|
||||
+
|
||||
+ if (dvfs->qos_added)
|
||||
+ dev_pm_qos_remove_request(&dvfs->max_freq_req);
|
||||
+ dvfs->qos_added = false;
|
||||
+
|
||||
+ if (dvfs->devfreq)
|
||||
+ devfreq_remove_device(dvfs->devfreq);
|
||||
+ dvfs->devfreq = NULL;
|
||||
+
|
||||
+ cancel_delayed_work_sync(&dvfs->throttle_work);
|
||||
+}
|
||||
+
|
||||
+static int rkvenc_dvfs_cooling_init(struct rkvenc_dev *rkvenc)
|
||||
+{
|
||||
+ struct rkvenc_dvfs *dvfs = &rkvenc->dvfs;
|
||||
+ struct device *dev = rkvenc->dev;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (!of_property_present(dev->of_node, "#cooling-cells"))
|
||||
+ return 0;
|
||||
+
|
||||
+ ret = dev_pm_qos_add_request(dev, &dvfs->max_freq_req,
|
||||
+ DEV_PM_QOS_MAX_FREQUENCY,
|
||||
+ PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+ dvfs->qos_added = true;
|
||||
+
|
||||
+ dvfs->cdev = thermal_of_cooling_device_register(dev->of_node, "rkvenc",
|
||||
+ dvfs, &rkvenc_cdev_ops);
|
||||
+ if (IS_ERR(dvfs->cdev)) {
|
||||
+ ret = PTR_ERR(dvfs->cdev);
|
||||
+ dvfs->cdev = NULL;
|
||||
+ return dev_err_probe(dev, ret,
|
||||
+ "could not add the cooling device\n");
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+int rkvenc_dvfs_init(struct rkvenc_dev *rkvenc)
|
||||
+{
|
||||
+ static const char *const clk_names[] = { "core", NULL };
|
||||
+ static const char *const supply_names[] = { "venc", NULL };
|
||||
+ struct dev_pm_opp_config opp_config = { .clk_names = clk_names };
|
||||
+ struct rkvenc_dvfs *dvfs = &rkvenc->dvfs;
|
||||
+ struct device *dev = rkvenc->dev;
|
||||
+ struct devfreq *devfreq;
|
||||
+ int ret;
|
||||
+
|
||||
+ spin_lock_init(&dvfs->lock);
|
||||
+ mutex_init(&dvfs->freq_lock);
|
||||
+ mutex_init(&dvfs->ctx_lock);
|
||||
+ INIT_LIST_HEAD(&dvfs->ctx_list);
|
||||
+ INIT_DELAYED_WORK(&dvfs->throttle_work, rkvenc_dvfs_throttle_work);
|
||||
+ init_waitqueue_head(&dvfs->idle_wq);
|
||||
+ dvfs->last_eval = ktime_get();
|
||||
+ dvfs->duty_pct = 100;
|
||||
+
|
||||
+ if (!of_property_present(dev->of_node, "operating-points-v2"))
|
||||
+ return 0;
|
||||
+
|
||||
+ if (of_property_present(dev->of_node, "venc-supply"))
|
||||
+ opp_config.regulator_names = supply_names;
|
||||
+
|
||||
+ ret = devm_pm_opp_set_config(dev, &opp_config);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = devm_pm_opp_of_add_table(dev);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = rkvenc_dvfs_freqs_get(rkvenc);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ dvfs->profile.target = rkvenc_dvfs_target;
|
||||
+ dvfs->profile.get_dev_status = rkvenc_dvfs_status;
|
||||
+ dvfs->profile.get_cur_freq = rkvenc_dvfs_cur_freq;
|
||||
+ dvfs->profile.freq_table = dvfs->freqs;
|
||||
+ dvfs->profile.max_state = dvfs->num_freqs;
|
||||
+ rkvenc_dvfs_cur_freq(dev, &dvfs->profile.initial_freq);
|
||||
+
|
||||
+ devfreq = devfreq_add_device(dev, &dvfs->profile,
|
||||
+ DEVFREQ_GOV_SIMPLE_ONDEMAND, NULL);
|
||||
+ if (IS_ERR(devfreq))
|
||||
+ return dev_err_probe(dev, PTR_ERR(devfreq),
|
||||
+ "could not add the devfreq device\n");
|
||||
+
|
||||
+ dvfs->devfreq = devfreq;
|
||||
+ ret = rkvenc_dvfs_cooling_init(rkvenc);
|
||||
+ if (ret)
|
||||
+ rkvenc_dvfs_exit(rkvenc);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
--- a/drivers/media/platform/rockchip/rkvenc/rkvenc-v4l2.c
|
||||
+++ b/drivers/media/platform/rockchip/rkvenc/rkvenc-v4l2.c
|
||||
@@ -517,17 +517,22 @@ static void rkvenc_device_run(void *priv
|
||||
else
|
||||
rkvenc_hevc_build_pic(ctx, &f.hevc, idr);
|
||||
|
||||
+ rkvenc_dvfs_job_begin(dev);
|
||||
+
|
||||
ret = pm_runtime_resume_and_get(dev->dev);
|
||||
if (ret)
|
||||
- goto finish_error;
|
||||
+ goto finish_error_idle;
|
||||
|
||||
ret = rkvenc_hw_run(dev, &f);
|
||||
if (ret) {
|
||||
pm_runtime_put(dev->dev);
|
||||
- goto finish_error;
|
||||
+ goto finish_error_idle;
|
||||
}
|
||||
return;
|
||||
|
||||
+finish_error_idle:
|
||||
+ rkvenc_dvfs_job_end(dev);
|
||||
+
|
||||
finish_error:
|
||||
dev->job_running = false;
|
||||
dev->job_ctx = NULL;
|
||||
@@ -538,8 +543,16 @@ finish_error:
|
||||
v4l2_m2m_job_finish(dev->m2m_dev, ctx->m2m_ctx);
|
||||
}
|
||||
|
||||
+static int rkvenc_job_ready(void *priv)
|
||||
+{
|
||||
+ struct rkvenc_ctx *ctx = priv;
|
||||
+
|
||||
+ return rkvenc_dvfs_start_allowed(ctx->dev);
|
||||
+}
|
||||
+
|
||||
const struct v4l2_m2m_ops rkvenc_m2m_ops = {
|
||||
.device_run = rkvenc_device_run,
|
||||
+ .job_ready = rkvenc_job_ready,
|
||||
};
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
@@ -1024,6 +1037,7 @@ static int rkvenc_open(struct file *filp
|
||||
if (ret)
|
||||
goto err_m2m;
|
||||
|
||||
+ rkvenc_dvfs_ctx_add(dev, ctx);
|
||||
v4l2_fh_add(&ctx->fh, filp);
|
||||
return 0;
|
||||
|
||||
@@ -1039,6 +1053,7 @@ static int rkvenc_release(struct file *f
|
||||
{
|
||||
struct rkvenc_ctx *ctx = fh_to_rkvenc_ctx(filp->private_data);
|
||||
|
||||
+ rkvenc_dvfs_ctx_del(ctx->dev, ctx);
|
||||
v4l2_m2m_ctx_release(ctx->m2m_ctx);
|
||||
v4l2_ctrl_handler_free(&ctx->ctrl_hdl);
|
||||
v4l2_fh_del(&ctx->fh, filp);
|
||||
--- a/drivers/media/platform/rockchip/rkvenc/rkvenc.c
|
||||
+++ b/drivers/media/platform/rockchip/rkvenc/rkvenc.c
|
||||
@@ -240,6 +240,7 @@ static void rkvenc_finish_job(struct rkv
|
||||
|
||||
dev->job_ctx = NULL;
|
||||
pm_runtime_put(dev->dev);
|
||||
+ rkvenc_dvfs_job_end(dev);
|
||||
|
||||
v4l2_m2m_buf_done_and_job_finish(dev->m2m_dev, ctx->m2m_ctx, state);
|
||||
}
|
||||
@@ -466,12 +467,18 @@ static int rkvenc_probe(struct platform_
|
||||
goto err_unreg_v4l2;
|
||||
}
|
||||
|
||||
- ret = rkvenc_v4l2_init(rkvenc);
|
||||
+ ret = rkvenc_dvfs_init(rkvenc);
|
||||
if (ret)
|
||||
goto err_release_m2m;
|
||||
|
||||
+ ret = rkvenc_v4l2_init(rkvenc);
|
||||
+ if (ret)
|
||||
+ goto err_dvfs;
|
||||
+
|
||||
return 0;
|
||||
|
||||
+err_dvfs:
|
||||
+ rkvenc_dvfs_exit(rkvenc);
|
||||
err_release_m2m:
|
||||
v4l2_m2m_release(rkvenc->m2m_dev);
|
||||
err_unreg_v4l2:
|
||||
@@ -486,6 +493,7 @@ static void rkvenc_remove(struct platfor
|
||||
struct rkvenc_dev *rkvenc = platform_get_drvdata(pdev);
|
||||
|
||||
cancel_delayed_work_sync(&rkvenc->watchdog);
|
||||
+ rkvenc_dvfs_exit(rkvenc);
|
||||
rkvenc_v4l2_cleanup(rkvenc);
|
||||
v4l2_m2m_release(rkvenc->m2m_dev);
|
||||
v4l2_device_unregister(&rkvenc->v4l2_dev);
|
||||
--- a/drivers/media/platform/rockchip/rkvenc/rkvenc.h
|
||||
+++ b/drivers/media/platform/rockchip/rkvenc/rkvenc.h
|
||||
@@ -15,8 +15,14 @@
|
||||
#include <linux/clk.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/delay.h>
|
||||
+#include <linux/devfreq.h>
|
||||
+#include <linux/ktime.h>
|
||||
+#include <linux/list.h>
|
||||
#include <linux/mutex.h>
|
||||
+#include <linux/pm_qos.h>
|
||||
#include <linux/spinlock.h>
|
||||
+#include <linux/thermal.h>
|
||||
+#include <linux/wait.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
@@ -37,6 +43,7 @@
|
||||
/* Clock and reset names, in bulk-programming order (design/02 3). */
|
||||
#define RKVENC_NUM_CLKS 3
|
||||
#define RKVENC_NUM_RESETS 3
|
||||
+#define RKVENC_CLK_CORE 2
|
||||
|
||||
/* Consecutive-job failure policy (design/04 C10: "needs approval").
|
||||
* A second consecutive failed job of one context ends the stream. Until
|
||||
@@ -47,6 +54,33 @@
|
||||
|
||||
struct rkvenc_dev;
|
||||
|
||||
+struct rkvenc_dvfs {
|
||||
+ struct devfreq *devfreq;
|
||||
+ struct devfreq_dev_profile profile;
|
||||
+ unsigned long *freqs;
|
||||
+ unsigned int num_freqs;
|
||||
+
|
||||
+ struct mutex freq_lock;
|
||||
+ wait_queue_head_t idle_wq;
|
||||
+
|
||||
+ struct thermal_cooling_device *cdev;
|
||||
+ struct dev_pm_qos_request max_freq_req;
|
||||
+ bool qos_added;
|
||||
+ unsigned int cooling_state;
|
||||
+ struct delayed_work throttle_work;
|
||||
+ struct mutex ctx_lock; /* protects ctx_list */
|
||||
+ struct list_head ctx_list;
|
||||
+
|
||||
+ spinlock_t lock; /* protects the fields below */
|
||||
+ bool busy;
|
||||
+ ktime_t job_start;
|
||||
+ ktime_t busy_since;
|
||||
+ ktime_t busy_total;
|
||||
+ ktime_t last_eval;
|
||||
+ ktime_t next_start;
|
||||
+ unsigned int duty_pct;
|
||||
+};
|
||||
+
|
||||
/**
|
||||
* struct rkvenc_variant - per-SoC integration differences.
|
||||
* @vepu540: true for the RK3566/RK3568 "vepu540" (longer HEVC L2, tiles,
|
||||
@@ -78,6 +112,7 @@ struct rkvenc_dev {
|
||||
const struct rkvenc_variant *variant;
|
||||
|
||||
struct rkvenc_img img;
|
||||
+ struct rkvenc_dvfs dvfs;
|
||||
|
||||
struct mutex vdev_lock; /* serialises the video node ioctls */
|
||||
spinlock_t irq_lock;
|
||||
@@ -144,6 +179,7 @@ struct rkvenc_ctx {
|
||||
struct v4l2_m2m_ctx *m2m_ctx;
|
||||
struct v4l2_ctrl_handler ctrl_hdl;
|
||||
struct mutex vlock;
|
||||
+ struct list_head dvfs_node;
|
||||
|
||||
u32 src_fourcc;
|
||||
u32 coded_fourcc;
|
||||
@@ -215,6 +251,15 @@ void rkvenc_hw_recover(struct rkvenc_dev
|
||||
int rkvenc_v4l2_init(struct rkvenc_dev *dev);
|
||||
void rkvenc_v4l2_cleanup(struct rkvenc_dev *dev);
|
||||
|
||||
+/* rkvenc-dvfs.c */
|
||||
+int rkvenc_dvfs_init(struct rkvenc_dev *dev);
|
||||
+void rkvenc_dvfs_exit(struct rkvenc_dev *dev);
|
||||
+void rkvenc_dvfs_job_begin(struct rkvenc_dev *dev);
|
||||
+void rkvenc_dvfs_job_end(struct rkvenc_dev *dev);
|
||||
+bool rkvenc_dvfs_start_allowed(struct rkvenc_dev *dev);
|
||||
+void rkvenc_dvfs_ctx_add(struct rkvenc_dev *dev, struct rkvenc_ctx *ctx);
|
||||
+void rkvenc_dvfs_ctx_del(struct rkvenc_dev *dev, struct rkvenc_ctx *ctx);
|
||||
+
|
||||
/* rkvenc-v4l2.c */
|
||||
extern const struct v4l2_m2m_ops rkvenc_m2m_ops;
|
||||
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
From bb8ac0eccd38910345f8a60201906a40bed4c1b5 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Fri, 16 Oct 2026 21:16:15 +0100
|
||||
Subject: [PATCH] ARM: dts: rockchip: rv1126: Add the RKVENC operating points
|
||||
|
||||
Add the four core clock rates of the encoder, 297, 396, 500 and 594
|
||||
MHz, which the clock tree provides as GPLL/4, GPLL/3, CPLL and GPLL/2,
|
||||
with the voltages of 725, 725, 750 and 825 mV that the vendor device
|
||||
tree gives them. The voltages apply on boards that describe the supply
|
||||
of the encoder as venc-supply. Make the encoder a cooling device so
|
||||
that thermal zones can use it.
|
||||
|
||||
Assisted-by: LLM
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
---
|
||||
arch/arm/boot/dts/rockchip/rv1126.dtsi | 26 ++++++++++++++++++++++++++
|
||||
1 file changed, 26 insertions(+)
|
||||
|
||||
--- a/arch/arm/boot/dts/rockchip/rv1126.dtsi
|
||||
+++ b/arch/arm/boot/dts/rockchip/rv1126.dtsi
|
||||
@@ -236,6 +236,30 @@
|
||||
#clock-cells = <0>;
|
||||
};
|
||||
|
||||
+ venc_opp_table: opp-table-venc {
|
||||
+ compatible = "operating-points-v2";
|
||||
+
|
||||
+ opp-297000000 {
|
||||
+ opp-hz = /bits/ 64 <297000000>;
|
||||
+ opp-microvolt = <725000 725000 1000000>;
|
||||
+ };
|
||||
+
|
||||
+ opp-396000000 {
|
||||
+ opp-hz = /bits/ 64 <396000000>;
|
||||
+ opp-microvolt = <725000 725000 1000000>;
|
||||
+ };
|
||||
+
|
||||
+ opp-500000000 {
|
||||
+ opp-hz = /bits/ 64 <500000000>;
|
||||
+ opp-microvolt = <750000 750000 1000000>;
|
||||
+ };
|
||||
+
|
||||
+ opp-594000000 {
|
||||
+ opp-hz = /bits/ 64 <594000000>;
|
||||
+ opp-microvolt = <825000 825000 1000000>;
|
||||
+ };
|
||||
+ };
|
||||
+
|
||||
grf: syscon@fe000000 {
|
||||
compatible = "rockchip,rv1126-grf", "syscon", "simple-mfd";
|
||||
reg = <0xfe000000 0x20000>;
|
||||
@@ -1350,6 +1374,8 @@
|
||||
clock-names = "axi", "ahb", "core";
|
||||
assigned-clocks = <&cru ACLK_VENC>, <&cru CLK_VENC_CORE>;
|
||||
assigned-clock-rates = <297000000>, <396000000>;
|
||||
+ operating-points-v2 = <&venc_opp_table>;
|
||||
+ #cooling-cells = <2>;
|
||||
resets = <&cru SRST_VENC_A>, <&cru SRST_VENC_H>,
|
||||
<&cru SRST_VENC_CORE>;
|
||||
reset-names = "axi", "ahb", "core";
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Sat, 17 Oct 2026 02:00:00 +0100
|
||||
Subject: [PATCH] ARM: dts: rockchip: rv1126: bind the RKVENC to the NPU
|
||||
thermal zone
|
||||
|
||||
The encoder sits in the VEPU and NPU power region, whose temperature the
|
||||
NPU TSADC reports. Give the NPU thermal zone passive trips at 85 and 95
|
||||
degrees Celsius like the CPU zone has, and let the encoder cool it. At
|
||||
the first trip the encoder core clock and supply come down to the lowest
|
||||
operating point, which are the cooling states 0 to 3, and at the second
|
||||
trip the frame rate is limited as well. The zone is polled every second,
|
||||
since the step_wise governor releases the cooling device on a falling
|
||||
trend.
|
||||
|
||||
Assisted-by: LLM
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
---
|
||||
--- a/arch/arm/boot/dts/rockchip/rv1126.dtsi
|
||||
+++ b/arch/arm/boot/dts/rockchip/rv1126.dtsi
|
||||
@@ -148,11 +148,35 @@
|
||||
};
|
||||
|
||||
npu_thermal: npu-thermal {
|
||||
- polling-delay-passive = <0>;
|
||||
- polling-delay = <0>;
|
||||
+ polling-delay-passive = <1000>;
|
||||
+ polling-delay = <1000>;
|
||||
thermal-sensors = <&npu_tsadc 0>;
|
||||
|
||||
+ cooling-maps {
|
||||
+ map0 {
|
||||
+ trip = <&npu_alert0>;
|
||||
+ cooling-device = <&venc THERMAL_NO_LIMIT 3>;
|
||||
+ };
|
||||
+
|
||||
+ map1 {
|
||||
+ trip = <&npu_alert1>;
|
||||
+ cooling-device = <&venc THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
|
||||
+ };
|
||||
+ };
|
||||
+
|
||||
trips {
|
||||
+ npu_alert0: npu-alert0 {
|
||||
+ temperature = <85000>;
|
||||
+ hysteresis = <2000>;
|
||||
+ type = "passive";
|
||||
+ };
|
||||
+
|
||||
+ npu_alert1: npu-alert1 {
|
||||
+ temperature = <95000>;
|
||||
+ hysteresis = <2000>;
|
||||
+ type = "passive";
|
||||
+ };
|
||||
+
|
||||
npu_crit: npu-crit {
|
||||
temperature = <125000>;
|
||||
hysteresis = <2000>;
|
||||
Reference in new issue
Block a user