mirror of
https://github.com/armbian/build
synced 2025-09-24 19:47:06 +07:00
- materialized overwrites:
- `add-board-helios64.patch`
- `add-board-orangepi-r1-plus.patch`
- `add-driver-for-Motorcomm-YT85xx+PHYs.patch`
- `add-board-rk3328-roc-pc.patch`
- not touched: wifi patches, those still require work before rebase is consistent.
- `wifi-4003-uwe5622-adjust-for-rockchip.patch`
- this patch is done on top of the wifi drivers patches exclusively, and fails to apply out of tree.
- we should probably consider moving this into the wifi drivers patch harness, not in the rockchip tree.
1809 lines
55 KiB
Diff
1809 lines
55 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Paolo <paolo.sabatino@gmail.com>
|
|
Date: Thu, 9 Sep 2021 22:59:12 +0200
|
|
Subject: Rockchip IEP driver
|
|
|
|
> X-Git-Archeology: - Revision 4425589e15c3b1593731703c379df0fa1b4432fb: https://github.com/armbian/build/commit/4425589e15c3b1593731703c379df0fa1b4432fb
|
|
> X-Git-Archeology: Date: Thu, 09 Sep 2021 22:59:12 +0200
|
|
> X-Git-Archeology: From: Paolo <paolo.sabatino@gmail.com>
|
|
> X-Git-Archeology: Subject: rk322x: bump edge kernel to 5.14, u-boot to 2021.07 (#3133)
|
|
> X-Git-Archeology:
|
|
> X-Git-Archeology: - Revision a1d044de8e0bb6ca504386bc31f5615a9d169067: https://github.com/armbian/build/commit/a1d044de8e0bb6ca504386bc31f5615a9d169067
|
|
> X-Git-Archeology: Date: Tue, 12 Oct 2021 15:59:01 +0200
|
|
> X-Git-Archeology: From: Paolo Sabatino <paolo.sabatino@gmail.com>
|
|
> X-Git-Archeology: Subject: rockchip: update support for edge kernel 5.14
|
|
> X-Git-Archeology:
|
|
> X-Git-Archeology: - Revision 682e4085ab8faa278db21a41ed23a6b12f8868ea: https://github.com/armbian/build/commit/682e4085ab8faa278db21a41ed23a6b12f8868ea
|
|
> X-Git-Archeology: Date: Thu, 21 Oct 2021 22:55:25 +0200
|
|
> X-Git-Archeology: From: Paolo <paolo.sabatino@gmail.com>
|
|
> X-Git-Archeology: Subject: rockchip64: add IEP driver (#3215)
|
|
> X-Git-Archeology:
|
|
> X-Git-Archeology: - Revision dd51f9f2afcbc83a3e10b32eb6a5061d91d1558e: https://github.com/armbian/build/commit/dd51f9f2afcbc83a3e10b32eb6a5061d91d1558e
|
|
> X-Git-Archeology: Date: Tue, 09 Nov 2021 18:06:34 +0100
|
|
> X-Git-Archeology: From: Igor Pecovnik <igorpecovnik@users.noreply.github.com>
|
|
> X-Git-Archeology: Subject: Bump imx6, xu4, rockchip64 and jetson-nano to 5.15 (#3238)
|
|
> X-Git-Archeology:
|
|
> X-Git-Archeology: - Revision ac8fc4385594d59257ee9dffd9efa85e3497fa7d: https://github.com/armbian/build/commit/ac8fc4385594d59257ee9dffd9efa85e3497fa7d
|
|
> X-Git-Archeology: Date: Sat, 26 Feb 2022 07:46:44 +0100
|
|
> X-Git-Archeology: From: Piotr Szczepanik <piter75@gmail.com>
|
|
> X-Git-Archeology: Subject: Switch rockchip64 current to linux 5.15.y (#3489)
|
|
> X-Git-Archeology:
|
|
> X-Git-Archeology: - Revision 897674aa74bce0326ed7fe06f5336bf4709a8a1f: https://github.com/armbian/build/commit/897674aa74bce0326ed7fe06f5336bf4709a8a1f
|
|
> X-Git-Archeology: Date: Tue, 03 May 2022 08:27:32 +0200
|
|
> X-Git-Archeology: From: Igor Pecovnik <igorpecovnik@users.noreply.github.com>
|
|
> X-Git-Archeology: Subject: Bump and freeze kernel at last known working versions (#3736)
|
|
> X-Git-Archeology:
|
|
> X-Git-Archeology: - Revision 597d2dac11f00d9070a4e49d6bad1b2244e36cb3: https://github.com/armbian/build/commit/597d2dac11f00d9070a4e49d6bad1b2244e36cb3
|
|
> X-Git-Archeology: Date: Sat, 28 May 2022 07:56:22 +0200
|
|
> X-Git-Archeology: From: Jianfeng Liu <liujianfeng1994@gmail.com>
|
|
> X-Git-Archeology: Subject: update rockchip64-edge to 5.18 (#3814)
|
|
> X-Git-Archeology:
|
|
> X-Git-Archeology: - Revision 8c6641e7b79f0d50acdc306d140e586a4e923cf0: https://github.com/armbian/build/commit/8c6641e7b79f0d50acdc306d140e586a4e923cf0
|
|
> X-Git-Archeology: Date: Wed, 03 Aug 2022 22:22:55 +0200
|
|
> X-Git-Archeology: From: Jianfeng Liu <liujianfeng1994@gmail.com>
|
|
> X-Git-Archeology: Subject: update rockchip64 edge to 5.19 (#4039)
|
|
> X-Git-Archeology:
|
|
> X-Git-Archeology: - Revision 6765f734cc4a22aeaa9f99a3ad28c8c322de26f6: https://github.com/armbian/build/commit/6765f734cc4a22aeaa9f99a3ad28c8c322de26f6
|
|
> X-Git-Archeology: Date: Tue, 25 Oct 2022 11:26:51 +0200
|
|
> X-Git-Archeology: From: Igor Pecovnik <igorpecovnik@users.noreply.github.com>
|
|
> X-Git-Archeology: Subject: Bump rockchip64 edge to 6.0.y (#4337)
|
|
> X-Git-Archeology:
|
|
> X-Git-Archeology: - Revision 92f1a22d76b987afa7ba555d5b509adc51d689e7: https://github.com/armbian/build/commit/92f1a22d76b987afa7ba555d5b509adc51d689e7
|
|
> X-Git-Archeology: Date: Fri, 16 Dec 2022 13:38:13 +0100
|
|
> X-Git-Archeology: From: Igor Pecovnik <igorpecovnik@users.noreply.github.com>
|
|
> X-Git-Archeology: Subject: Re-add rockchip64 6.0 patches (#4575)
|
|
> X-Git-Archeology:
|
|
> X-Git-Archeology: - Revision 34ae84fac5d0b66a1ab2d1e51534b7beb13ef245: https://github.com/armbian/build/commit/34ae84fac5d0b66a1ab2d1e51534b7beb13ef245
|
|
> X-Git-Archeology: Date: Fri, 05 May 2023 14:22:00 +0200
|
|
> X-Git-Archeology: From: amazingfate <liujianfeng1994@gmail.com>
|
|
> X-Git-Archeology: Subject: bump rockchip64 edge to v6.3
|
|
> X-Git-Archeology:
|
|
---
|
|
Documentation/devicetree/bindings/media/rockchip-iep.yaml | 73 +
|
|
arch/arm/boot/dts/rk3288.dtsi | 13 +-
|
|
arch/arm64/boot/dts/rockchip/rk3328.dtsi | 22 +
|
|
arch/arm64/boot/dts/rockchip/rk3399.dtsi | 13 +
|
|
drivers/media/platform/rockchip/Kconfig | 1 +
|
|
drivers/media/platform/rockchip/Makefile | 1 +
|
|
drivers/media/platform/rockchip/iep/Kconfig | 13 +
|
|
drivers/media/platform/rockchip/iep/Makefile | 5 +
|
|
drivers/media/platform/rockchip/iep/iep-regs.h | 291 +++
|
|
drivers/media/platform/rockchip/iep/iep.c | 1089 ++++++++++
|
|
drivers/media/platform/rockchip/iep/iep.h | 112 +
|
|
11 files changed, 1632 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/Documentation/devicetree/bindings/media/rockchip-iep.yaml b/Documentation/devicetree/bindings/media/rockchip-iep.yaml
|
|
new file mode 100644
|
|
index 000000000000..a9efcda13fc1
|
|
--- /dev/null
|
|
+++ b/Documentation/devicetree/bindings/media/rockchip-iep.yaml
|
|
@@ -0,0 +1,73 @@
|
|
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
|
+%YAML 1.2
|
|
+---
|
|
+$id: http://devicetree.org/schemas/media/rockchip-iep.yaml#
|
|
+$schema: http://devicetree.org/meta-schemas/core.yaml#
|
|
+
|
|
+title: Rockchip Image Enhancement Processor (IEP)
|
|
+
|
|
+description:
|
|
+ Rockchip IEP supports various image enhancement operations for YUV and RGB domains.
|
|
+ Deinterlacing, spatial and temporal sampling noise reduction are supported by the
|
|
+ YUV block. Gamma adjustment, edge enhancement, detail enhancement are supported in
|
|
+ the RGB block. Brightness, Saturation, Contrast, Hue adjustment is supported for
|
|
+ both domains. Furthermore it supports converting RGB to YUV / YUV to RGB.
|
|
+
|
|
+maintainers:
|
|
+ - Heiko Stuebner <heiko@sntech.de>
|
|
+
|
|
+properties:
|
|
+ compatible:
|
|
+ oneOf:
|
|
+ - const: rockchip,rk3228-iep
|
|
+ - items:
|
|
+ - enum:
|
|
+ - rockchip,rk3288-iep
|
|
+ - rockchip,rk3328-iep
|
|
+ - rockchip,rk3368-iep
|
|
+ - rockchip,rk3399-iep
|
|
+ - const: rockchip,rk3228-iep
|
|
+
|
|
+ reg:
|
|
+ maxItems: 1
|
|
+
|
|
+ interrupts:
|
|
+ maxItems: 1
|
|
+
|
|
+ clocks:
|
|
+ maxItems: 2
|
|
+
|
|
+ clock-names:
|
|
+ items:
|
|
+ - const: axi
|
|
+ - const: ahb
|
|
+
|
|
+ power-domains:
|
|
+ maxItems: 1
|
|
+
|
|
+ iommus:
|
|
+ maxItems: 1
|
|
+
|
|
+required:
|
|
+ - compatible
|
|
+ - reg
|
|
+ - interrupts
|
|
+ - clocks
|
|
+ - clock-names
|
|
+
|
|
+additionalProperties: false
|
|
+
|
|
+examples:
|
|
+ - |
|
|
+ #include <dt-bindings/clock/rk3228-cru.h>
|
|
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
|
|
+ #include <dt-bindings/power/rk3228-power.h>
|
|
+ iep: iep@20070000 {
|
|
+ compatible = "rockchip,rk3228-iep";
|
|
+ reg = <0x20070000 0x800>;
|
|
+ interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
|
|
+ clocks = <&cru ACLK_IEP>, <&cru HCLK_IEP>;
|
|
+ clock-names = "axi", "ahb";
|
|
+ iommus = <&iep_mmu>;
|
|
+ power-domains = <&power RK3228_PD_VIO>;
|
|
+ };
|
|
diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi
|
|
index 511ca864c1b2..c65f9a6ddafb 100644
|
|
--- a/arch/arm/boot/dts/rk3288.dtsi
|
|
+++ b/arch/arm/boot/dts/rk3288.dtsi
|
|
@@ -984,14 +984,25 @@ crypto: crypto@ff8a0000 {
|
|
reset-names = "crypto-rst";
|
|
};
|
|
|
|
+ iep: iep@ff90000 {
|
|
+ compatible = "rockchip,rk3288-iep", "rockchip,rk3228-iep";
|
|
+ reg = <0x0 0xff900000 0x0 0x800>;
|
|
+ interrupts = <GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>;
|
|
+ interrupt-names = "iep";
|
|
+ clocks = <&cru ACLK_IEP>, <&cru HCLK_IEP>;
|
|
+ clock-names = "axi", "ahb";
|
|
+ power-domains = <&power RK3288_PD_VIO>;
|
|
+ iommus = <&iep_mmu>;
|
|
+ };
|
|
+
|
|
iep_mmu: iommu@ff900800 {
|
|
compatible = "rockchip,iommu";
|
|
reg = <0x0 0xff900800 0x0 0x40>;
|
|
interrupts = <GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>;
|
|
clocks = <&cru ACLK_IEP>, <&cru HCLK_IEP>;
|
|
clock-names = "aclk", "iface";
|
|
+ power-domains = <&power RK3288_PD_VIO>;
|
|
#iommu-cells = <0>;
|
|
- status = "disabled";
|
|
};
|
|
|
|
isp_mmu: iommu@ff914000 {
|
|
diff --git a/arch/arm64/boot/dts/rockchip/rk3328.dtsi b/arch/arm64/boot/dts/rockchip/rk3328.dtsi
|
|
index 6d7a7bf72ac7..1121c11abaa1 100644
|
|
--- a/arch/arm64/boot/dts/rockchip/rk3328.dtsi
|
|
+++ b/arch/arm64/boot/dts/rockchip/rk3328.dtsi
|
|
@@ -721,6 +721,28 @@ vop_mmu: iommu@ff373f00 {
|
|
status = "disabled";
|
|
};
|
|
|
|
+ iep: iep@ff3a0000 {
|
|
+ compatible = "rockchip,rk3328-iep", "rockchip,rk3228-iep";
|
|
+ reg = <0x0 0xff3a0000 0x0 0x800>;
|
|
+ interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
|
|
+ interrupt-names = "iep";
|
|
+ clocks = <&cru ACLK_IEP>, <&cru HCLK_IEP>;
|
|
+ clock-names = "axi", "ahb";
|
|
+ power-domains = <&power RK3328_PD_VIDEO>;
|
|
+ iommus = <&iep_mmu>;
|
|
+ };
|
|
+
|
|
+ iep_mmu: iommu@ff3a0800 {
|
|
+ compatible = "rockchip,iommu";
|
|
+ reg = <0x0 0xff3a0800 0x0 0x40>;
|
|
+ interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
|
|
+ interrupt-names = "iep_mmu";
|
|
+ clocks = <&cru ACLK_IEP>, <&cru HCLK_IEP>;
|
|
+ clock-names = "aclk", "iface";
|
|
+ power-domains = <&power RK3328_PD_VIDEO>;
|
|
+ #iommu-cells = <0>;
|
|
+ };
|
|
+
|
|
hdmi: hdmi@ff3c0000 {
|
|
compatible = "rockchip,rk3328-dw-hdmi";
|
|
reg = <0x0 0xff3c0000 0x0 0x20000>;
|
|
diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
|
|
index 40e7c4a70055..45c8da0e2168 100644
|
|
--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
|
|
+++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
|
|
@@ -1377,12 +1377,25 @@ vdec_mmu: iommu@ff660480 {
|
|
#iommu-cells = <0>;
|
|
};
|
|
|
|
+
|
|
+ iep: iep@ff670000 {
|
|
+ compatible = "rockchip,rk3399-iep", "rockchip,rk3228-iep";
|
|
+ reg = <0x0 0xff670000 0x0 0x800>;
|
|
+ interrupts = <GIC_SPI 42 IRQ_TYPE_LEVEL_HIGH 0>;
|
|
+ interrupt-names = "iep";
|
|
+ clocks = <&cru ACLK_IEP>, <&cru HCLK_IEP>;
|
|
+ clock-names = "axi", "ahb";
|
|
+ power-domains = <&power RK3399_PD_IEP>;
|
|
+ iommus = <&iep_mmu>;
|
|
+ };
|
|
+
|
|
iep_mmu: iommu@ff670800 {
|
|
compatible = "rockchip,iommu";
|
|
reg = <0x0 0xff670800 0x0 0x40>;
|
|
interrupts = <GIC_SPI 42 IRQ_TYPE_LEVEL_HIGH 0>;
|
|
clocks = <&cru ACLK_IEP>, <&cru HCLK_IEP>;
|
|
clock-names = "aclk", "iface";
|
|
+ power-domains = <&power RK3399_PD_IEP>;
|
|
#iommu-cells = <0>;
|
|
status = "disabled";
|
|
};
|
|
diff --git a/drivers/media/platform/rockchip/Kconfig b/drivers/media/platform/rockchip/Kconfig
|
|
index b41d3960c1b4..862590be7916 100644
|
|
--- a/drivers/media/platform/rockchip/Kconfig
|
|
+++ b/drivers/media/platform/rockchip/Kconfig
|
|
@@ -4,3 +4,4 @@ comment "Rockchip media platform drivers"
|
|
|
|
source "drivers/media/platform/rockchip/rga/Kconfig"
|
|
source "drivers/media/platform/rockchip/rkisp1/Kconfig"
|
|
+source "drivers/media/platform/rockchip/iep/Kconfig"
|
|
diff --git a/drivers/media/platform/rockchip/Makefile b/drivers/media/platform/rockchip/Makefile
|
|
index 4f782b876ac9..be8015c6d9e4 100644
|
|
--- a/drivers/media/platform/rockchip/Makefile
|
|
+++ b/drivers/media/platform/rockchip/Makefile
|
|
@@ -1,3 +1,4 @@
|
|
# SPDX-License-Identifier: GPL-2.0-only
|
|
obj-y += rga/
|
|
obj-y += rkisp1/
|
|
+obj-y += iep/
|
|
diff --git a/drivers/media/platform/rockchip/iep/Kconfig b/drivers/media/platform/rockchip/iep/Kconfig
|
|
new file mode 100644
|
|
index 000000000000..e513fa7f45f2
|
|
--- /dev/null
|
|
+++ b/drivers/media/platform/rockchip/iep/Kconfig
|
|
@@ -0,0 +1,13 @@
|
|
+config VIDEO_ROCKCHIP_IEP
|
|
+ tristate "Rockchip Image Enhancement Processor"
|
|
+ depends on VIDEO_DEV && VIDEO_V4L2
|
|
+ depends on ARCH_ROCKCHIP || COMPILE_TEST
|
|
+ select VIDEOBUF2_DMA_CONTIG
|
|
+ select V4L2_MEM2MEM_DEV
|
|
+ help
|
|
+ This is a v4l2 driver for Rockchip Image Enhancement Processor (IEP)
|
|
+ found in most Rockchip RK3xxx SoCs.
|
|
+ Rockchip IEP supports various enhancement operations for RGB and YUV
|
|
+ images. The driver currently implements YUV deinterlacing only.
|
|
+ To compile this driver as a module, choose M here: the module
|
|
+ will be called rockchip-iep
|
|
diff --git a/drivers/media/platform/rockchip/iep/Makefile b/drivers/media/platform/rockchip/iep/Makefile
|
|
new file mode 100644
|
|
index 000000000000..5c89b3277469
|
|
--- /dev/null
|
|
+++ b/drivers/media/platform/rockchip/iep/Makefile
|
|
@@ -0,0 +1,5 @@
|
|
+# SPDX-License-Identifier: GPL-2.0-only
|
|
+
|
|
+rockchip-iep-objs := iep.o
|
|
+
|
|
+obj-$(CONFIG_VIDEO_ROCKCHIP_IEP) += rockchip-iep.o
|
|
diff --git a/drivers/media/platform/rockchip/iep/iep-regs.h b/drivers/media/platform/rockchip/iep/iep-regs.h
|
|
new file mode 100644
|
|
index 000000000000..a68685ef3604
|
|
--- /dev/null
|
|
+++ b/drivers/media/platform/rockchip/iep/iep-regs.h
|
|
@@ -0,0 +1,291 @@
|
|
+/* SPDX-License-Identifier: GPL-2.0-only */
|
|
+/*
|
|
+ * Rockchip Image Enhancement Processor (IEP) driver
|
|
+ *
|
|
+ * Copyright (C) 2020 Alex Bee <knaerzche@gmail.com>
|
|
+ *
|
|
+ */
|
|
+
|
|
+#ifndef __IEP_REGS_H__
|
|
+#define __IEP_REGS_H__
|
|
+
|
|
+/* IEP Registers addresses */
|
|
+#define IEP_CONFIG0 0x000 /* Configuration register0 */
|
|
+#define IEP_VOP_DIRECT_PATH BIT(0)
|
|
+#define IEP_DEIN_HIGH_FREQ_SHFT 1
|
|
+#define IEP_DEIN_HIGH_FREQ_MASK (0x7f << IEP_DEIN_HIGH_FREQ_SHFT)
|
|
+#define IEP_DEIN_MODE_SHFT 8
|
|
+#define IEP_DEIN_MODE_MASK (7 << IEP_DEIN_MODE_SHFT)
|
|
+#define IEP_DEIN_HIGH_FREQ_EN BIT(11)
|
|
+#define IEP_DEIN_EDGE_INTPOL_EN BIT(12)
|
|
+#define IEP_YUV_DENOISE_EN BIT(13)
|
|
+#define IEP_YUV_ENHNC_EN BIT(14)
|
|
+#define IEP_DEIN_EDGE_INTPOL_SMTH_EN BIT(15)
|
|
+#define IEP_RGB_CLR_ENHNC_EN BIT(16)
|
|
+#define IEP_RGB_CNTRST_ENHNC_EN BIT(17)
|
|
+#define IEP_RGB_ENHNC_MODE_BYPASS (0 << 18)
|
|
+#define IEP_RGB_ENHNC_MODE_DNS BIT(18)
|
|
+#define IEP_RGB_ENHNC_MODE_DTL (2 << 18)
|
|
+#define IEP_RGB_ENHNC_MODE_EDG (3 << 18)
|
|
+#define IEP_RGB_ENHNC_MODE_MASK (3 << 18)
|
|
+#define IEP_RGB_CNTRST_ENHNC_DDE_FRST BIT(20)
|
|
+#define IEP_DEIN_EDGE_INTPOL_RADIUS_SHFT 21
|
|
+#define IEP_DEIN_EDGE_INTPOL_RADIUS_MASK (3 << IEP_DEIN_EDGE_INTPOL_RADIUS_SHFT)
|
|
+#define IEP_DEIN_EDGE_INTPOL_SELECT BIT(23)
|
|
+
|
|
+#define IEP_CONFIG1 0x004 /* Configuration register1 */
|
|
+#define IEP_SRC_FMT_SHFT 0
|
|
+#define IEP_SRC_FMT_MASK (3 << IEP_SRC_FMT_SHFT)
|
|
+#define IEP_SRC_RGB_SWP_SHFT 2
|
|
+#define IEP_SRC_RGB_SWP_MASK (2 << IEP_SRC_RGB_SWP_SHFT)
|
|
+#define IEP_SRC_YUV_SWP_SHFT 4
|
|
+#define IEP_SRC_YUV_SWP_MASK (3 << IEP_SRC_YUV_SWP_SHFT)
|
|
+#define IEP_DST_FMT_SHFT 8
|
|
+#define IEP_DST_FMT_MASK (3 << IEP_DST_FMT_SHFT)
|
|
+#define IEP_DST_RGB_SWP_SHFT 10
|
|
+#define IEP_DST_RGB_SWP_MASK (2 << IEP_DST_RGB_SWP_SHFT)
|
|
+#define IEP_DST_YUV_SWP_SHFT 12
|
|
+#define IEP_DST_YUV_SWP_MASK (3 << IEP_DST_YUV_SWP_SHFT)
|
|
+#define IEP_DTH_UP_EN BIT(14)
|
|
+#define IEP_DTH_DWN_EN BIT(15)
|
|
+#define IEP_YUV2RGB_COE_BT601_1 (0 << 16)
|
|
+#define IEP_YUV2RGB_COE_BT601_F BIT(16)
|
|
+#define IEP_YUV2RGB_COE_BT709_1 (2 << 16)
|
|
+#define IEP_YUV2RGB_COE_BT709_F (3 << 16)
|
|
+#define IEP_YUV2RGB_COE_MASK (3 << 16)
|
|
+#define IEP_RGB2YUV_COE_BT601_1 (0 << 18)
|
|
+#define IEP_RGB2YUV_COE_BT601_F BIT(18)
|
|
+#define IEP_RGB2YUV_COE_BT709_1 (2 << 18)
|
|
+#define IEP_RGB2YUV_COE_BT709_F (3 << 18)
|
|
+#define IEP_RGB2YUV_COE_MASK (3 << 18)
|
|
+#define IEP_YUV2RGB_EN BIT(20)
|
|
+#define IEP_RGB2YUV_EN BIT(21)
|
|
+#define IEP_YUV2RGB_CLIP_EN BIT(22)
|
|
+#define IEP_RGB2YUV_CLIP_EN BIT(23)
|
|
+#define IEP_GLB_ALPHA_SHFT 24
|
|
+#define IEP_GLB_ALPHA_MASK (0x7f << IEP_GLB_ALPHA_SHFT)
|
|
+
|
|
+#define IEP_STATUS 0x008 /* Status register */
|
|
+#define IEP_STATUS_YUV_DNS BIT(0)
|
|
+#define IEP_STATUS_SCL BIT(1)
|
|
+#define IEP_STATUS_DIL BIT(2)
|
|
+#define IEP_STATUS_DDE BIT(3)
|
|
+#define IEP_STATUS_DMA_WR_YUV BIT(4)
|
|
+#define IEP_STATUS_DMA_RE_YUV BIT(5)
|
|
+#define IEP_STATUS_DMA_WR_RGB BIT(6)
|
|
+#define IEP_STATUS_DMA_RE_RGB BIT(7)
|
|
+#define IEP_STATUS_VOP_DIRECT_PATH BIT(8)
|
|
+#define IEP_STATUS_DMA_IA_WR_YUV BIT(16)
|
|
+#define IEP_STATUS_DMA_IA_RE_YUV BIT(17)
|
|
+#define IEP_STATUS_DMA_IA_WR_RGB BIT(18)
|
|
+#define IEP_STATUS_DMA_IA_RE_RGB BIT(19)
|
|
+
|
|
+#define IEP_INT 0x00c /* Interrupt register*/
|
|
+#define IEP_INT_FRAME_DONE BIT(0) /* Frame process done interrupt */
|
|
+#define IEP_INT_FRAME_DONE_EN BIT(8) /* Frame process done interrupt enable */
|
|
+#define IEP_INT_FRAME_DONE_CLR BIT(16) /* Frame process done interrupt clear */
|
|
+
|
|
+#define IEP_FRM_START 0x010 /* Frame start */
|
|
+#define IEP_SRST 0x014 /* Soft reset */
|
|
+#define IEP_CONFIG_DONE 0x018 /* Configuration done */
|
|
+#define IEP_FRM_CNT 0x01c /* Frame counter */
|
|
+
|
|
+#define IEP_VIR_IMG_WIDTH 0x020 /* Image virtual width */
|
|
+#define IEP_IMG_SCL_FCT 0x024 /* Scaling factor */
|
|
+#define IEP_SRC_IMG_SIZE 0x028 /* src image width/height */
|
|
+#define IEP_DST_IMG_SIZE 0x02c /* dst image width/height */
|
|
+#define IEP_DST_IMG_WIDTH_TILE0 0x030 /* dst image tile0 width */
|
|
+#define IEP_DST_IMG_WIDTH_TILE1 0x034 /* dst image tile1 width */
|
|
+#define IEP_DST_IMG_WIDTH_TILE2 0x038 /* dst image tile2 width */
|
|
+#define IEP_DST_IMG_WIDTH_TILE3 0x03c /* dst image tile3 width */
|
|
+
|
|
+#define IEP_ENH_YUV_CNFG_0 0x040 /* Brightness, contrast, saturation adjustment */
|
|
+#define IEP_YUV_BRIGHTNESS_SHFT 0
|
|
+#define IEP_YUV_BRIGHTNESS_MASK (0x3f << IEP_YUV_BRIGHTNESS_SHFT)
|
|
+#define IEP_YUV_CONTRAST_SHFT 8
|
|
+#define IEP_YUV_CONTRAST_MASK (0xff << IEP_YUV_CONTRAST_SHFT)
|
|
+#define IEP_YUV_SATURATION_SHFT 16
|
|
+#define IEP_YUV_SATURATION_MASK (0x1ff << IEP_YUV_SATURATION_SHFT)
|
|
+
|
|
+#define IEP_ENH_YUV_CNFG_1 0x044 /* Hue configuration */
|
|
+#define IEP_YUV_COS_HUE_SHFT 0
|
|
+#define IEP_YUV_COS_HUE_MASK (0xff << IEP_YUV_COS_HUE_SHFT)
|
|
+#define IEP_YUV_SIN_HUE_SHFT 8
|
|
+#define IEP_YUV_SIN_HUE_MASK (0xff << IEP_YUV_SIN_HUE_SHFT)
|
|
+
|
|
+#define IEP_ENH_YUV_CNFG_2 0x048 /* Color bar configuration */
|
|
+#define IEP_YUV_COLOR_BAR_Y_SHFT 0
|
|
+#define IEP_YUV_COLOR_BAR_Y_MASK (0xff << IEP_YUV_COLOR_BAR_Y_SHFT)
|
|
+#define IEP_YUV_COLOR_BAR_U_SHFT 8
|
|
+#define IEP_YUV_COLOR_BAR_U_MASK (0xff << IEP_YUV_COLOR_BAR_U_SHFT)
|
|
+#define IEP_YUV_COLOR_BAR_V_SHFT 16
|
|
+#define IEP_YUV_COLOR_BAR_V_MASK (0xff << IEP_YUV_COLOR_BAR_V_SHFT)
|
|
+#define IEP_YUV_VIDEO_MODE_SHFT 24
|
|
+#define IEP_YUV_VIDEO_MODE_MASK (3 << IEP_YUV_VIDEO_MODE_SHFT)
|
|
+
|
|
+#define IEP_ENH_RGB_CNFG 0x04c /* RGB enhancement configuration */
|
|
+#define IEP_ENH_RGB_C_COE 0x050 /* RGB color enhancement coefficient */
|
|
+
|
|
+#define IEP_RAW_CONFIG0 0x058 /* Raw configuration register0 */
|
|
+#define IEP_RAW_CONFIG1 0x05c /* Raw configuration register1 */
|
|
+#define IEP_RAW_VIR_IMG_WIDTH 0x060 /* Raw image virtual width */
|
|
+#define IEP_RAW_IMG_SCL_FCT 0x064 /* Raw scaling factor */
|
|
+#define IEP_RAW_SRC_IMG_SIZE 0x068 /* Raw src image width/height */
|
|
+#define IEP_RAW_DST_IMG_SIZE 0x06c /* Raw src image width/height */
|
|
+#define IEP_RAW_ENH_YUV_CNFG_0 0x070 /* Raw brightness,contrast,saturation adjustment */
|
|
+#define IEP_RAW_ENH_YUV_CNFG_1 0x074 /* Raw hue configuration */
|
|
+#define IEP_RAW_ENH_YUV_CNFG_2 0x078 /* Raw color bar configuration */
|
|
+#define IEP_RAW_ENH_RGB_CNFG 0x07c /* Raw RGB enhancement configuration */
|
|
+
|
|
+#define IEP_SRC_ADDR_Y_RGB 0x080 /* Start addr. of src image 0 (Y/RGB) */
|
|
+#define IEP_SRC_ADDR_CBCR 0x084 /* Start addr. of src image 0 (Cb/Cr) */
|
|
+#define IEP_SRC_ADDR_CR 0x088 /* Start addr. of src image 0 (Cr) */
|
|
+#define IEP_SRC_ADDR_Y1 0x08c /* Start addr. of src image 1 (Y) */
|
|
+#define IEP_SRC_ADDR_CBCR1 0x090 /* Start addr. of src image 1 (Cb/Cr) */
|
|
+#define IEP_SRC_ADDR_CR1 0x094 /* Start addr. of src image 1 (Cr) */
|
|
+#define IEP_SRC_ADDR_Y_ITEMP 0x098 /* Start addr. of src image(Y int part) */
|
|
+#define IEP_SRC_ADDR_CBCR_ITEMP 0x09c /* Start addr. of src image(CBCR int part) */
|
|
+#define IEP_SRC_ADDR_CR_ITEMP 0x0a0 /* Start addr. of src image(CR int part) */
|
|
+#define IEP_SRC_ADDR_Y_FTEMP 0x0a4 /* Start addr. of src image(Y frac part) */
|
|
+#define IEP_SRC_ADDR_CBCR_FTEMP 0x0a8 /* Start addr. of src image(CBCR frac part) */
|
|
+#define IEP_SRC_ADDR_CR_FTEMP 0x0ac /* Start addr. of src image(CR frac part) */
|
|
+
|
|
+#define IEP_DST_ADDR_Y_RGB 0x0b0 /* Start addr. of dst image 0 (Y/RGB) */
|
|
+#define IEP_DST_ADDR_CBCR 0x0b4 /* Start addr. of dst image 0 (Cb/Cr) */
|
|
+#define IEP_DST_ADDR_CR 0x0b8 /* Start addr. of dst image 0 (Cr) */
|
|
+#define IEP_DST_ADDR_Y1 0x0bc /* Start addr. of dst image 1 (Y) */
|
|
+#define IEP_DST_ADDR_CBCR1 0x0c0 /* Start addr. of dst image 1 (Cb/Cr) */
|
|
+#define IEP_DST_ADDR_CR1 0x0c4 /* Start addr. of dst image 1 (Cr) */
|
|
+#define IEP_DST_ADDR_Y_ITEMP 0x0c8 /* Start addr. of dst image(Y int part) */
|
|
+#define IEP_DST_ADDR_CBCR_ITEMP 0x0cc /* Start addr. of dst image(CBCR int part)*/
|
|
+#define IEP_DST_ADDR_CR_ITEMP 0x0d0 /* Start addr. of dst image(CR int part) */
|
|
+#define IEP_DST_ADDR_Y_FTEMP 0x0d4 /* Start addr. of dst image(Y frac part) */
|
|
+#define IEP_DST_ADDR_CBCR_FTEMP 0x0d8 /* Start addr. of dst image(CBCR frac part) */
|
|
+#define IEP_DST_ADDR_CR_FTEMP 0x0dc /* Start addr. of dst image(CR frac part)*/
|
|
+
|
|
+#define IEP_DEIN_MTN_TAB0 0x0e0 /* Deinterlace motion table0 */
|
|
+#define IEP_DEIN_MTN_TAB1 0x0e4 /* Deinterlace motion table1 */
|
|
+#define IEP_DEIN_MTN_TAB2 0x0e8 /* Deinterlace motion table2 */
|
|
+#define IEP_DEIN_MTN_TAB3 0x0ec /* Deinterlace motion table3 */
|
|
+#define IEP_DEIN_MTN_TAB4 0x0f0 /* Deinterlace motion table4 */
|
|
+#define IEP_DEIN_MTN_TAB5 0x0f4 /* Deinterlace motion table5 */
|
|
+#define IEP_DEIN_MTN_TAB6 0x0f8 /* Deinterlace motion table6 */
|
|
+#define IEP_DEIN_MTN_TAB7 0x0fc /* Deinterlace motion table7 */
|
|
+
|
|
+#define IEP_ENH_CG_TAB 0x100 /* Contrast and gamma enhancement table */
|
|
+#define IEP_ENH_DDE_COE0 0x400 /* Denoise,detail and edge enhancement coefficient */
|
|
+#define IEP_ENH_DDE_COE1 0x500 /* Denoise,detail and edge enhancement coefficient1 */
|
|
+
|
|
+#define IEP_INT_MASK (IEP_INT_FRAME_DONE)
|
|
+
|
|
+/* IEP colorformats */
|
|
+#define IEP_COLOR_FMT_XRGB 0U
|
|
+#define IEP_COLOR_FMT_RGB565 1U
|
|
+#define IEP_COLOR_FMT_YUV422 2U
|
|
+#define IEP_COLOR_FMT_YUV420 3U
|
|
+
|
|
+/* IEP YUV color swaps */
|
|
+#define IEP_YUV_SWP_SP_UV 0U
|
|
+#define IEP_YUV_SWP_SP_VU 1U
|
|
+#define IEP_YUV_SWP_P 2U
|
|
+
|
|
+/* IEP XRGB color swaps */
|
|
+#define XRGB_SWP_XRGB 0U
|
|
+#define XRGB_SWP_XBGR 1U
|
|
+#define XRGB_SWP_BGRX 2U
|
|
+
|
|
+/* IEP RGB565 color swaps */
|
|
+#define RGB565_SWP_RGB 0U
|
|
+#define RGB565_SWP_BGR 1U
|
|
+
|
|
+#define FMT_IS_YUV(fmt) (fmt == IEP_COLOR_FMT_XRGB || fmt == IEP_COLOR_FMT_RGB565 ? 0 : 1)
|
|
+
|
|
+#define IEP_IMG_SIZE(w, h) (((w - 1) & 0x1fff) << 0 | \
|
|
+ ((h - 1) & 0x1fff) << 16)
|
|
+
|
|
+#define IEP_VIR_WIDTH(src_w, dst_w) (((src_w / 4) & 0x1fff) << 0 | \
|
|
+ ((dst_w / 4) & 0x1fff) << 16)
|
|
+
|
|
+#define IEP_Y_STRIDE(w, h) (w * h)
|
|
+#define IEP_UV_STRIDE(w, h, fac) (w * h + w * h / fac)
|
|
+
|
|
+#define IEP_SRC_FMT_SWP_MASK(f) (FMT_IS_YUV(f) ? IEP_SRC_YUV_SWP_MASK : IEP_SRC_RGB_SWP_MASK)
|
|
+#define IEP_DST_FMT_SWP_MASK(f) (FMT_IS_YUV(f) ? IEP_DST_YUV_SWP_MASK : IEP_DST_RGB_SWP_MASK)
|
|
+
|
|
+#define IEP_SRC_FMT(f, swp) (f << IEP_SRC_FMT_SHFT | \
|
|
+ (swp << (FMT_IS_YUV(f) ? IEP_SRC_YUV_SWP_SHFT : IEP_SRC_RGB_SWP_SHFT)))
|
|
+#define IEP_DST_FMT(f, swp) (f << IEP_DST_FMT_SHFT | \
|
|
+ (swp << (FMT_IS_YUV(f) ? IEP_DST_YUV_SWP_SHFT : IEP_DST_RGB_SWP_SHFT)))
|
|
+
|
|
+/* IEP DEINTERLACE MODES */
|
|
+#define IEP_DEIN_MODE_YUV 0U
|
|
+#define IEP_DEIN_MODE_I4O2 1U
|
|
+#define IEP_DEIN_MODE_I4O1B 2U
|
|
+#define IEP_DEIN_MODE_I4O1T 3U
|
|
+#define IEP_DEIN_MODE_I2O1B 4U
|
|
+#define IEP_DEIN_MODE_I2O1T 5U
|
|
+#define IEP_DEIN_MODE_BYPASS 6U
|
|
+
|
|
+#define IEP_DEIN_IN_FIELDS_2 2U
|
|
+#define IEP_DEIN_IN_FIELDS_4 4U
|
|
+
|
|
+#define IEP_DEIN_OUT_FRAMES_1 1U
|
|
+#define IEP_DEIN_OUT_FRAMES_2 2U
|
|
+
|
|
+/* values taken from BSP driver */
|
|
+static const u32 default_dein_motion_tbl[][2] = {
|
|
+ { IEP_DEIN_MTN_TAB0, 0x40404040 },
|
|
+ { IEP_DEIN_MTN_TAB1, 0x3c3e3f3f },
|
|
+ { IEP_DEIN_MTN_TAB2, 0x3336393b },
|
|
+ { IEP_DEIN_MTN_TAB3, 0x272a2d31 },
|
|
+ { IEP_DEIN_MTN_TAB4, 0x181c2023 },
|
|
+ { IEP_DEIN_MTN_TAB5, 0x0c0e1215 },
|
|
+ { IEP_DEIN_MTN_TAB6, 0x03040609 },
|
|
+ { IEP_DEIN_MTN_TAB7, 0x00000001 },
|
|
+
|
|
+};
|
|
+
|
|
+#define IEP_DEIN_IN_IMG0_Y(bff) (bff ? IEP_SRC_ADDR_Y_RGB : IEP_SRC_ADDR_Y1)
|
|
+#define IEP_DEIN_IN_IMG0_CBCR(bff) (bff ? IEP_SRC_ADDR_CBCR : IEP_SRC_ADDR_CBCR1)
|
|
+#define IEP_DEIN_IN_IMG0_CR(bff) (bff ? IEP_SRC_ADDR_CR : IEP_SRC_ADDR_CR1)
|
|
+#define IEP_DEIN_IN_IMG1_Y(bff) (IEP_DEIN_IN_IMG0_Y(!bff))
|
|
+#define IEP_DEIN_IN_IMG1_CBCR(bff) (IEP_DEIN_IN_IMG0_CBCR(!bff))
|
|
+#define IEP_DEIN_IN_IMG1_CR(bff) (IEP_DEIN_IN_IMG0_CR(!bff))
|
|
+
|
|
+#define IEP_DEIN_OUT_IMG0_Y(bff) (bff ? IEP_DST_ADDR_Y1 : IEP_DST_ADDR_Y_RGB)
|
|
+#define IEP_DEIN_OUT_IMG0_CBCR(bff) (bff ? IEP_DST_ADDR_CBCR1 : IEP_DST_ADDR_CBCR)
|
|
+#define IEP_DEIN_OUT_IMG0_CR(bff) (bff ? IEP_DST_ADDR_CR1 : IEP_DST_ADDR_CR)
|
|
+#define IEP_DEIN_OUT_IMG1_Y(bff) (IEP_DEIN_OUT_IMG0_Y(!bff))
|
|
+#define IEP_DEIN_OUT_IMG1_CBCR(bff) (IEP_DEIN_OUT_IMG0_CBCR(!bff))
|
|
+#define IEP_DEIN_OUT_IMG1_CR(bff) (IEP_DEIN_OUT_IMG0_CR(!bff))
|
|
+
|
|
+#define IEP_DEIN_MODE(m) (m << IEP_DEIN_MODE_SHFT)
|
|
+
|
|
+#define IEP_DEIN_IN_MODE_FIELDS(m) ((m == IEP_DEIN_MODE_I4O1T || m == IEP_DEIN_MODE_I4O1B \
|
|
+ || m == IEP_DEIN_MODE_I4O2) \
|
|
+ ? IEP_DEIN_IN_FIELDS_4 : IEP_DEIN_IN_FIELDS_2)
|
|
+
|
|
+#define IEP_DEIN_OUT_MODE_FRAMES(m) (m == IEP_DEIN_MODE_I4O2 \
|
|
+ ? IEP_DEIN_OUT_FRAMES_2 : IEP_DEIN_OUT_FRAMES_1)
|
|
+
|
|
+#define IEP_DEIN_OUT_MODE_1FRM_TOP_FIELD(m) (m == IEP_DEIN_MODE_I4O1T || IEP_DEIN_MODE_I2O1T \
|
|
+ ? 1 : 0)
|
|
+
|
|
+#define IEP_DEIN_EDGE_INTPOL_RADIUS(r) (r << IEP_DEIN_EDGE_INTPOL_RADIUS_SHFT)
|
|
+
|
|
+#define IEP_DEIN_HIGH_FREQ(f) (f << IEP_DEIN_HIGH_FREQ_SHFT)
|
|
+
|
|
+/* YUV Enhance video modes */
|
|
+#define VIDEO_MODE_BLACK_SCREEN 0U
|
|
+#define VIDEO_MODE_BLUE_SCREEN 1U
|
|
+#define VIDEO_MODE_COLOR_BARS 2U
|
|
+#define VIDEO_MODE_NORMAL_VIDEO 3U
|
|
+
|
|
+#define YUV_VIDEO_MODE(m) ((m << IEP_YUV_VIDEO_MODE_SHFT) & IEP_YUV_VIDEO_MODE_MASK)
|
|
+#define YUV_BRIGHTNESS(v) ((v << IEP_YUV_BRIGHTNESS_SHFT) & IEP_YUV_BRIGHTNESS_MASK)
|
|
+#define YUV_CONTRAST(v) ((v << IEP_YUV_CONTRAST_SHFT) & IEP_YUV_CONTRAST_MASK)
|
|
+#define YUV_SATURATION(v) ((v << IEP_YUV_SATURATION_SHFT) & IEP_YUV_SATURATION_MASK)
|
|
+#define YUV_COS_HUE(v) ((v << IEP_YUV_COS_HUE_SHFT) & IEP_YUV_COS_HUE_MASK)
|
|
+#define YUV_SIN_HUE(v) ((v << IEP_YUV_SIN_HUE_SHFT) & IEP_YUV_SIN_HUE_MASK)
|
|
+
|
|
+#endif
|
|
diff --git a/drivers/media/platform/rockchip/iep/iep.c b/drivers/media/platform/rockchip/iep/iep.c
|
|
new file mode 100644
|
|
index 000000000000..f4b9320733be
|
|
--- /dev/null
|
|
+++ b/drivers/media/platform/rockchip/iep/iep.c
|
|
@@ -0,0 +1,1089 @@
|
|
+// SPDX-License-Identifier: GPL-2.0-only
|
|
+/*
|
|
+ * Rockchip Image Enhancement Processor (IEP) driver
|
|
+ *
|
|
+ * Copyright (C) 2020 Alex Bee <knaerzche@gmail.com>
|
|
+ *
|
|
+ * Based on Allwinner sun8i deinterlacer with scaler driver
|
|
+ * Copyright (C) 2019 Jernej Skrabec <jernej.skrabec@siol.net>
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include <linux/clk.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/interrupt.h>
|
|
+#include <linux/io.h>
|
|
+#include <linux/iopoll.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/of.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <linux/pm_runtime.h>
|
|
+
|
|
+#include <media/v4l2-device.h>
|
|
+#include <media/v4l2-ctrls.h>
|
|
+#include <media/v4l2-ioctl.h>
|
|
+#include <media/v4l2-mem2mem.h>
|
|
+#include <media/videobuf2-v4l2.h>
|
|
+#include <media/videobuf2-core.h>
|
|
+#include <media/videobuf2-dma-contig.h>
|
|
+#include <linux/videodev2.h>
|
|
+
|
|
+#include "iep-regs.h"
|
|
+#include "iep.h"
|
|
+
|
|
+static struct iep_fmt formats[] = {
|
|
+ {
|
|
+ .fourcc = V4L2_PIX_FMT_NV12,
|
|
+ .color_swap = IEP_YUV_SWP_SP_UV,
|
|
+ .hw_format = IEP_COLOR_FMT_YUV420,
|
|
+ .depth = 12,
|
|
+ .uv_factor = 4,
|
|
+ },
|
|
+ {
|
|
+ .fourcc = V4L2_PIX_FMT_NV21,
|
|
+ .color_swap = IEP_YUV_SWP_SP_VU,
|
|
+ .hw_format = IEP_COLOR_FMT_YUV420,
|
|
+ .depth = 12,
|
|
+ .uv_factor = 4,
|
|
+ },
|
|
+ {
|
|
+ .fourcc = V4L2_PIX_FMT_NV16,
|
|
+ .color_swap = IEP_YUV_SWP_SP_UV,
|
|
+ .hw_format = IEP_COLOR_FMT_YUV422,
|
|
+ .depth = 16,
|
|
+ .uv_factor = 2,
|
|
+ },
|
|
+ {
|
|
+ .fourcc = V4L2_PIX_FMT_NV61,
|
|
+ .color_swap = IEP_YUV_SWP_SP_VU,
|
|
+ .hw_format = IEP_COLOR_FMT_YUV422,
|
|
+ .depth = 16,
|
|
+ .uv_factor = 2,
|
|
+ },
|
|
+ {
|
|
+ .fourcc = V4L2_PIX_FMT_YUV420,
|
|
+ .color_swap = IEP_YUV_SWP_P,
|
|
+ .hw_format = IEP_COLOR_FMT_YUV420,
|
|
+ .depth = 12,
|
|
+ .uv_factor = 4,
|
|
+ },
|
|
+ {
|
|
+ .fourcc = V4L2_PIX_FMT_YUV422P,
|
|
+ .color_swap = IEP_YUV_SWP_P,
|
|
+ .hw_format = IEP_COLOR_FMT_YUV422,
|
|
+ .depth = 16,
|
|
+ .uv_factor = 2,
|
|
+ },
|
|
+};
|
|
+
|
|
+static struct iep_fmt *iep_fmt_find(struct v4l2_pix_format *pix_fmt)
|
|
+{
|
|
+ unsigned int i;
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(formats); i++) {
|
|
+ if (formats[i].fourcc == pix_fmt->pixelformat)
|
|
+ return &formats[i];
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static bool iep_check_pix_format(u32 pixelformat)
|
|
+{
|
|
+ unsigned int i;
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(formats); i++)
|
|
+ if (formats[i].fourcc == pixelformat)
|
|
+ return true;
|
|
+
|
|
+ return false;
|
|
+}
|
|
+
|
|
+static struct vb2_v4l2_buffer *iep_m2m_next_dst_buf(struct iep_ctx *ctx)
|
|
+{
|
|
+ struct vb2_v4l2_buffer *dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
|
|
+
|
|
+ /* application has set a dst sequence: take it as start point */
|
|
+ if (ctx->dst_sequence == 0 && dst_buf->sequence > 0)
|
|
+ ctx->dst_sequence = dst_buf->sequence;
|
|
+
|
|
+ dst_buf->sequence = ctx->dst_sequence++;
|
|
+
|
|
+ return dst_buf;
|
|
+}
|
|
+
|
|
+static void iep_m2m_dst_bufs_done(struct iep_ctx *ctx, enum vb2_buffer_state state)
|
|
+{
|
|
+ if (ctx->dst0_buf) {
|
|
+ v4l2_m2m_buf_done(ctx->dst0_buf, state);
|
|
+ ctx->dst_buffs_done++;
|
|
+ ctx->dst0_buf = NULL;
|
|
+ }
|
|
+
|
|
+ if (ctx->dst1_buf) {
|
|
+ v4l2_m2m_buf_done(ctx->dst1_buf, state);
|
|
+ ctx->dst_buffs_done++;
|
|
+ ctx->dst1_buf = NULL;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void iep_setup_formats(struct iep_ctx *ctx)
|
|
+{
|
|
+ /* setup src dimensions */
|
|
+ iep_write(ctx->iep, IEP_SRC_IMG_SIZE,
|
|
+ IEP_IMG_SIZE(ctx->src_fmt.pix.width, ctx->src_fmt.pix.height));
|
|
+
|
|
+ /* setup dst dimensions */
|
|
+ iep_write(ctx->iep, IEP_DST_IMG_SIZE,
|
|
+ IEP_IMG_SIZE(ctx->dst_fmt.pix.width, ctx->dst_fmt.pix.height));
|
|
+
|
|
+ /* setup virtual width */
|
|
+ iep_write(ctx->iep, IEP_VIR_IMG_WIDTH,
|
|
+ IEP_VIR_WIDTH(ctx->src_fmt.pix.width, ctx->dst_fmt.pix.width));
|
|
+
|
|
+ /* setup src format */
|
|
+ iep_shadow_mod(ctx->iep, IEP_CONFIG1, IEP_RAW_CONFIG1,
|
|
+ IEP_SRC_FMT_MASK | IEP_SRC_FMT_SWP_MASK(ctx->src_fmt.hw_fmt->hw_format),
|
|
+ IEP_SRC_FMT(ctx->src_fmt.hw_fmt->hw_format,
|
|
+ ctx->src_fmt.hw_fmt->color_swap));
|
|
+ /* setup dst format */
|
|
+ iep_shadow_mod(ctx->iep, IEP_CONFIG1, IEP_RAW_CONFIG1,
|
|
+ IEP_DST_FMT_MASK | IEP_DST_FMT_SWP_MASK(ctx->dst_fmt.hw_fmt->hw_format),
|
|
+ IEP_DST_FMT(ctx->dst_fmt.hw_fmt->hw_format,
|
|
+ ctx->dst_fmt.hw_fmt->color_swap));
|
|
+
|
|
+ ctx->fmt_changed = false;
|
|
+}
|
|
+
|
|
+static void iep_dein_init(struct rockchip_iep *iep)
|
|
+{
|
|
+ unsigned int i;
|
|
+
|
|
+ /* values taken from BSP driver */
|
|
+ iep_shadow_mod(iep, IEP_CONFIG0, IEP_RAW_CONFIG0,
|
|
+ (IEP_DEIN_EDGE_INTPOL_SMTH_EN |
|
|
+ IEP_DEIN_EDGE_INTPOL_RADIUS_MASK |
|
|
+ IEP_DEIN_HIGH_FREQ_EN |
|
|
+ IEP_DEIN_HIGH_FREQ_MASK),
|
|
+ (IEP_DEIN_EDGE_INTPOL_SMTH_EN |
|
|
+ IEP_DEIN_EDGE_INTPOL_RADIUS(3) |
|
|
+ IEP_DEIN_HIGH_FREQ_EN |
|
|
+ IEP_DEIN_HIGH_FREQ(64)));
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(default_dein_motion_tbl); i++)
|
|
+ iep_write(iep, default_dein_motion_tbl[i][0],
|
|
+ default_dein_motion_tbl[i][1]);
|
|
+}
|
|
+
|
|
+static void iep_init(struct rockchip_iep *iep)
|
|
+{
|
|
+ iep_write(iep, IEP_CONFIG0,
|
|
+ IEP_DEIN_MODE(IEP_DEIN_MODE_BYPASS) // |
|
|
+ //IEP_YUV_ENHNC_EN
|
|
+ );
|
|
+
|
|
+ /* TODO: B/S/C/H works
|
|
+ * only in 1-frame-out modes
|
|
+ iep_write(iep, IEP_ENH_YUV_CNFG_0,
|
|
+ YUV_BRIGHTNESS(0) |
|
|
+ YUV_CONTRAST(128) |
|
|
+ YUV_SATURATION(128));
|
|
+
|
|
+ iep_write(iep, IEP_ENH_YUV_CNFG_1,
|
|
+ YUV_COS_HUE(255) |
|
|
+ YUV_SIN_HUE(255));
|
|
+
|
|
+ iep_write(iep, IEP_ENH_YUV_CNFG_2,
|
|
+ YUV_VIDEO_MODE(VIDEO_MODE_NORMAL_VIDEO));
|
|
+
|
|
+ */
|
|
+
|
|
+ /* reset frame counter */
|
|
+ iep_write(iep, IEP_FRM_CNT, 0);
|
|
+}
|
|
+
|
|
+static void iep_device_run(void *priv)
|
|
+{
|
|
+ struct iep_ctx *ctx = priv;
|
|
+ struct rockchip_iep *iep = ctx->iep;
|
|
+ struct vb2_v4l2_buffer *src, *dst;
|
|
+ unsigned int dein_mode;
|
|
+ dma_addr_t addr;
|
|
+
|
|
+ if (ctx->fmt_changed)
|
|
+ iep_setup_formats(ctx);
|
|
+
|
|
+ if (ctx->prev_src_buf)
|
|
+ dein_mode = IEP_DEIN_MODE_I4O2;
|
|
+ else
|
|
+ dein_mode = ctx->field_bff ? IEP_DEIN_MODE_I2O1B : IEP_DEIN_MODE_I2O1T;
|
|
+
|
|
+ iep_shadow_mod(iep, IEP_CONFIG0, IEP_RAW_CONFIG0,
|
|
+ IEP_DEIN_MODE_MASK, IEP_DEIN_MODE(dein_mode));
|
|
+
|
|
+ /* sync RAW_xxx registers with actual used */
|
|
+ iep_write(iep, IEP_CONFIG_DONE, 1);
|
|
+
|
|
+ /* setup src buff(s)/addresses */
|
|
+ src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
|
|
+ addr = vb2_dma_contig_plane_dma_addr(&src->vb2_buf, 0);
|
|
+
|
|
+ iep_write(iep, IEP_DEIN_IN_IMG0_Y(ctx->field_bff), addr);
|
|
+
|
|
+ iep_write(iep, IEP_DEIN_IN_IMG0_CBCR(ctx->field_bff),
|
|
+ addr + ctx->src_fmt.y_stride);
|
|
+
|
|
+ iep_write(iep, IEP_DEIN_IN_IMG0_CR(ctx->field_bff),
|
|
+ addr + ctx->src_fmt.uv_stride);
|
|
+
|
|
+ if (IEP_DEIN_IN_MODE_FIELDS(dein_mode) == IEP_DEIN_IN_FIELDS_4)
|
|
+ addr = vb2_dma_contig_plane_dma_addr(&ctx->prev_src_buf->vb2_buf, 0);
|
|
+
|
|
+ iep_write(iep, IEP_DEIN_IN_IMG1_Y(ctx->field_bff), addr);
|
|
+
|
|
+ iep_write(iep, IEP_DEIN_IN_IMG1_CBCR(ctx->field_bff),
|
|
+ addr + ctx->src_fmt.y_stride);
|
|
+
|
|
+ iep_write(iep, IEP_DEIN_IN_IMG1_CR(ctx->field_bff),
|
|
+ addr + ctx->src_fmt.uv_stride);
|
|
+
|
|
+ /* setup dst buff(s)/addresses */
|
|
+ dst = iep_m2m_next_dst_buf(ctx);
|
|
+ addr = vb2_dma_contig_plane_dma_addr(&dst->vb2_buf, 0);
|
|
+
|
|
+ if (IEP_DEIN_OUT_MODE_FRAMES(dein_mode) == IEP_DEIN_OUT_FRAMES_2) {
|
|
+ v4l2_m2m_buf_copy_metadata(ctx->prev_src_buf, dst, true);
|
|
+
|
|
+ iep_write(iep, IEP_DEIN_OUT_IMG0_Y(ctx->field_bff), addr);
|
|
+
|
|
+ iep_write(iep, IEP_DEIN_OUT_IMG0_CBCR(ctx->field_bff),
|
|
+ addr + ctx->dst_fmt.y_stride);
|
|
+
|
|
+ iep_write(iep, IEP_DEIN_OUT_IMG0_CR(ctx->field_bff),
|
|
+ addr + ctx->dst_fmt.uv_stride);
|
|
+
|
|
+ ctx->dst0_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
|
|
+
|
|
+ dst = iep_m2m_next_dst_buf(ctx);
|
|
+ addr = vb2_dma_contig_plane_dma_addr(&dst->vb2_buf, 0);
|
|
+ }
|
|
+
|
|
+ v4l2_m2m_buf_copy_metadata(src, dst, true);
|
|
+
|
|
+ iep_write(iep, IEP_DEIN_OUT_IMG1_Y(ctx->field_bff), addr);
|
|
+
|
|
+ iep_write(iep, IEP_DEIN_OUT_IMG1_CBCR(ctx->field_bff),
|
|
+ addr + ctx->dst_fmt.y_stride);
|
|
+
|
|
+ iep_write(iep, IEP_DEIN_OUT_IMG1_CR(ctx->field_bff),
|
|
+ addr + ctx->dst_fmt.uv_stride);
|
|
+
|
|
+ ctx->dst1_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
|
|
+
|
|
+ iep_mod(ctx->iep, IEP_INT, IEP_INT_FRAME_DONE_EN,
|
|
+ IEP_INT_FRAME_DONE_EN);
|
|
+
|
|
+ /* start HW */
|
|
+ iep_write(iep, IEP_FRM_START, 1);
|
|
+}
|
|
+
|
|
+static int iep_job_ready(void *priv)
|
|
+{
|
|
+ struct iep_ctx *ctx = priv;
|
|
+
|
|
+ return v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx) >= 2 &&
|
|
+ v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) >= 1;
|
|
+}
|
|
+
|
|
+static void iep_job_abort(void *priv)
|
|
+{
|
|
+ struct iep_ctx *ctx = priv;
|
|
+
|
|
+ /* Will cancel the transaction in the next interrupt handler */
|
|
+ ctx->job_abort = true;
|
|
+}
|
|
+
|
|
+static const struct v4l2_m2m_ops iep_m2m_ops = {
|
|
+ .device_run = iep_device_run,
|
|
+ .job_ready = iep_job_ready,
|
|
+ .job_abort = iep_job_abort,
|
|
+};
|
|
+
|
|
+static int iep_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
|
|
+ unsigned int *nplanes, unsigned int sizes[],
|
|
+ struct device *alloc_devs[])
|
|
+{
|
|
+ struct iep_ctx *ctx = vb2_get_drv_priv(vq);
|
|
+ struct v4l2_pix_format *pix_fmt;
|
|
+
|
|
+ if (V4L2_TYPE_IS_OUTPUT(vq->type))
|
|
+ pix_fmt = &ctx->src_fmt.pix;
|
|
+ else
|
|
+ pix_fmt = &ctx->dst_fmt.pix;
|
|
+
|
|
+ if (*nplanes) {
|
|
+ if (sizes[0] < pix_fmt->sizeimage)
|
|
+ return -EINVAL;
|
|
+ } else {
|
|
+ sizes[0] = pix_fmt->sizeimage;
|
|
+ *nplanes = 1;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int iep_buf_prepare(struct vb2_buffer *vb)
|
|
+{
|
|
+ struct vb2_queue *vq = vb->vb2_queue;
|
|
+ struct iep_ctx *ctx = vb2_get_drv_priv(vq);
|
|
+ struct v4l2_pix_format *pix_fmt;
|
|
+
|
|
+ if (V4L2_TYPE_IS_OUTPUT(vq->type))
|
|
+ pix_fmt = &ctx->src_fmt.pix;
|
|
+ else
|
|
+ pix_fmt = &ctx->dst_fmt.pix;
|
|
+
|
|
+ if (vb2_plane_size(vb, 0) < pix_fmt->sizeimage)
|
|
+ return -EINVAL;
|
|
+
|
|
+ /* set bytesused for capture buffers */
|
|
+ if (!V4L2_TYPE_IS_OUTPUT(vq->type))
|
|
+ vb2_set_plane_payload(vb, 0, pix_fmt->sizeimage);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void iep_buf_queue(struct vb2_buffer *vb)
|
|
+{
|
|
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
|
|
+ struct iep_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
|
|
+
|
|
+ v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
|
|
+}
|
|
+
|
|
+static void iep_queue_cleanup(struct vb2_queue *vq, u32 state)
|
|
+{
|
|
+ struct iep_ctx *ctx = vb2_get_drv_priv(vq);
|
|
+ struct vb2_v4l2_buffer *vbuf;
|
|
+
|
|
+ do {
|
|
+ if (V4L2_TYPE_IS_OUTPUT(vq->type))
|
|
+ vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
|
|
+ else
|
|
+ vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
|
|
+
|
|
+ if (vbuf)
|
|
+ v4l2_m2m_buf_done(vbuf, state);
|
|
+ } while (vbuf);
|
|
+
|
|
+ if (V4L2_TYPE_IS_OUTPUT(vq->type) && ctx->prev_src_buf)
|
|
+ v4l2_m2m_buf_done(ctx->prev_src_buf, state);
|
|
+ else
|
|
+ iep_m2m_dst_bufs_done(ctx, state);
|
|
+}
|
|
+
|
|
+static int iep_start_streaming(struct vb2_queue *vq, unsigned int count)
|
|
+{
|
|
+ struct iep_ctx *ctx = vb2_get_drv_priv(vq);
|
|
+ struct device *dev = ctx->iep->dev;
|
|
+ int ret;
|
|
+
|
|
+ if (V4L2_TYPE_IS_OUTPUT(vq->type)) {
|
|
+ ret = pm_runtime_get_sync(dev);
|
|
+ if (ret < 0) {
|
|
+ dev_err(dev, "Failed to enable module\n");
|
|
+ goto err_runtime_get;
|
|
+ }
|
|
+
|
|
+ ctx->field_order_bff =
|
|
+ ctx->src_fmt.pix.field == V4L2_FIELD_INTERLACED_BT;
|
|
+ ctx->field_bff = ctx->field_order_bff;
|
|
+
|
|
+ ctx->src_sequence = 0;
|
|
+ ctx->dst_sequence = 0;
|
|
+
|
|
+ ctx->prev_src_buf = NULL;
|
|
+
|
|
+ ctx->dst0_buf = NULL;
|
|
+ ctx->dst1_buf = NULL;
|
|
+ ctx->dst_buffs_done = 0;
|
|
+
|
|
+ ctx->job_abort = false;
|
|
+
|
|
+ iep_init(ctx->iep);
|
|
+ //if (ctx->src_fmt.pix.field != ctx->dst_fmt.pix.field)
|
|
+ iep_dein_init(ctx->iep);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+
|
|
+err_runtime_get:
|
|
+ iep_queue_cleanup(vq, VB2_BUF_STATE_QUEUED);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void iep_stop_streaming(struct vb2_queue *vq)
|
|
+{
|
|
+ struct iep_ctx *ctx = vb2_get_drv_priv(vq);
|
|
+
|
|
+ if (V4L2_TYPE_IS_OUTPUT(vq->type)) {
|
|
+ pm_runtime_mark_last_busy(ctx->iep->dev);
|
|
+ pm_runtime_put_autosuspend(ctx->iep->dev);
|
|
+ }
|
|
+
|
|
+ iep_queue_cleanup(vq, VB2_BUF_STATE_ERROR);
|
|
+}
|
|
+
|
|
+static const struct vb2_ops iep_qops = {
|
|
+ .queue_setup = iep_queue_setup,
|
|
+ .buf_prepare = iep_buf_prepare,
|
|
+ .buf_queue = iep_buf_queue,
|
|
+ .start_streaming = iep_start_streaming,
|
|
+ .stop_streaming = iep_stop_streaming,
|
|
+ .wait_prepare = vb2_ops_wait_prepare,
|
|
+ .wait_finish = vb2_ops_wait_finish,
|
|
+};
|
|
+
|
|
+static int iep_queue_init(void *priv, struct vb2_queue *src_vq,
|
|
+ struct vb2_queue *dst_vq)
|
|
+{
|
|
+ struct iep_ctx *ctx = priv;
|
|
+ int ret;
|
|
+
|
|
+ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
|
|
+ src_vq->dma_attrs = DMA_ATTR_ALLOC_SINGLE_PAGES |
|
|
+ DMA_ATTR_NO_KERNEL_MAPPING;
|
|
+ src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
|
|
+ src_vq->drv_priv = ctx;
|
|
+ src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
|
|
+ src_vq->min_buffers_needed = 1;
|
|
+ src_vq->ops = &iep_qops;
|
|
+ src_vq->mem_ops = &vb2_dma_contig_memops;
|
|
+ src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
|
|
+ src_vq->lock = &ctx->iep->mutex;
|
|
+ src_vq->dev = ctx->iep->v4l2_dev.dev;
|
|
+
|
|
+ ret = vb2_queue_init(src_vq);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ dst_vq->dma_attrs = DMA_ATTR_ALLOC_SINGLE_PAGES |
|
|
+ DMA_ATTR_NO_KERNEL_MAPPING;
|
|
+ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
+ dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
|
|
+ dst_vq->drv_priv = ctx;
|
|
+ dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
|
|
+ dst_vq->min_buffers_needed = 2;
|
|
+ dst_vq->ops = &iep_qops;
|
|
+ dst_vq->mem_ops = &vb2_dma_contig_memops;
|
|
+ dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
|
|
+ dst_vq->lock = &ctx->iep->mutex;
|
|
+ dst_vq->dev = ctx->iep->v4l2_dev.dev;
|
|
+
|
|
+ ret = vb2_queue_init(dst_vq);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void iep_prepare_format(struct v4l2_pix_format *pix_fmt)
|
|
+{
|
|
+ unsigned int height = pix_fmt->height;
|
|
+ unsigned int width = pix_fmt->width;
|
|
+ unsigned int sizeimage, bytesperline;
|
|
+
|
|
+ struct iep_fmt *hw_fmt = iep_fmt_find(pix_fmt);
|
|
+
|
|
+ if (!hw_fmt) {
|
|
+ hw_fmt = &formats[0];
|
|
+ pix_fmt->pixelformat = hw_fmt->fourcc;
|
|
+ }
|
|
+
|
|
+ width = ALIGN(clamp(width, IEP_MIN_WIDTH,
|
|
+ IEP_MAX_WIDTH), 16);
|
|
+ height = ALIGN(clamp(height, IEP_MIN_HEIGHT,
|
|
+ IEP_MAX_HEIGHT), 16);
|
|
+
|
|
+ bytesperline = FMT_IS_YUV(hw_fmt->hw_format)
|
|
+ ? width : (width * hw_fmt->depth) >> 3;
|
|
+
|
|
+ sizeimage = height * (width * hw_fmt->depth) >> 3;
|
|
+
|
|
+ pix_fmt->width = width;
|
|
+ pix_fmt->height = height;
|
|
+ pix_fmt->bytesperline = bytesperline;
|
|
+ pix_fmt->sizeimage = sizeimage;
|
|
+}
|
|
+
|
|
+static int iep_open(struct file *file)
|
|
+{
|
|
+ struct rockchip_iep *iep = video_drvdata(file);
|
|
+ struct iep_ctx *ctx = NULL;
|
|
+
|
|
+ int ret;
|
|
+
|
|
+ if (mutex_lock_interruptible(&iep->mutex))
|
|
+ return -ERESTARTSYS;
|
|
+
|
|
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
|
|
+ if (!ctx) {
|
|
+ mutex_unlock(&iep->mutex);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ /* default output format */
|
|
+ ctx->src_fmt.pix.pixelformat = formats[0].fourcc;
|
|
+ ctx->src_fmt.pix.field = V4L2_FIELD_INTERLACED;
|
|
+ ctx->src_fmt.pix.width = IEP_DEFAULT_WIDTH;
|
|
+ ctx->src_fmt.pix.height = IEP_DEFAULT_HEIGHT;
|
|
+ iep_prepare_format(&ctx->src_fmt.pix);
|
|
+ ctx->src_fmt.hw_fmt = &formats[0];
|
|
+ ctx->dst_fmt.y_stride = IEP_Y_STRIDE(ctx->src_fmt.pix.width, ctx->src_fmt.pix.height);
|
|
+ ctx->dst_fmt.uv_stride = IEP_UV_STRIDE(ctx->src_fmt.pix.width, ctx->src_fmt.pix.height,
|
|
+ ctx->src_fmt.hw_fmt->uv_factor);
|
|
+
|
|
+ /* default capture format */
|
|
+ ctx->dst_fmt.pix.pixelformat = formats[0].fourcc;
|
|
+ ctx->dst_fmt.pix.field = V4L2_FIELD_NONE;
|
|
+ ctx->dst_fmt.pix.width = IEP_DEFAULT_WIDTH;
|
|
+ ctx->dst_fmt.pix.height = IEP_DEFAULT_HEIGHT;
|
|
+ iep_prepare_format(&ctx->dst_fmt.pix);
|
|
+ ctx->dst_fmt.hw_fmt = &formats[0];
|
|
+ ctx->dst_fmt.y_stride = IEP_Y_STRIDE(ctx->dst_fmt.pix.width, ctx->dst_fmt.pix.height);
|
|
+ ctx->dst_fmt.uv_stride = IEP_UV_STRIDE(ctx->dst_fmt.pix.width, ctx->dst_fmt.pix.height,
|
|
+ ctx->dst_fmt.hw_fmt->uv_factor);
|
|
+ /* ensure fmts are written to HW */
|
|
+ ctx->fmt_changed = true;
|
|
+
|
|
+ v4l2_fh_init(&ctx->fh, video_devdata(file));
|
|
+ file->private_data = &ctx->fh;
|
|
+ ctx->iep = iep;
|
|
+
|
|
+ ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(iep->m2m_dev, ctx,
|
|
+ &iep_queue_init);
|
|
+
|
|
+ if (IS_ERR(ctx->fh.m2m_ctx)) {
|
|
+ ret = PTR_ERR(ctx->fh.m2m_ctx);
|
|
+ goto err_free;
|
|
+ }
|
|
+
|
|
+ v4l2_fh_add(&ctx->fh);
|
|
+
|
|
+ mutex_unlock(&iep->mutex);
|
|
+
|
|
+ return 0;
|
|
+
|
|
+err_free:
|
|
+ kfree(ctx);
|
|
+ mutex_unlock(&iep->mutex);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int iep_release(struct file *file)
|
|
+{
|
|
+ struct rockchip_iep *iep = video_drvdata(file);
|
|
+ struct iep_ctx *ctx = container_of(file->private_data,
|
|
+ struct iep_ctx, fh);
|
|
+
|
|
+ mutex_lock(&iep->mutex);
|
|
+
|
|
+ v4l2_fh_del(&ctx->fh);
|
|
+ v4l2_fh_exit(&ctx->fh);
|
|
+ v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
|
|
+ kfree(ctx);
|
|
+
|
|
+ mutex_unlock(&iep->mutex);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct v4l2_file_operations iep_fops = {
|
|
+ .owner = THIS_MODULE,
|
|
+ .open = iep_open,
|
|
+ .release = iep_release,
|
|
+ .poll = v4l2_m2m_fop_poll,
|
|
+ .unlocked_ioctl = video_ioctl2,
|
|
+ .mmap = v4l2_m2m_fop_mmap,
|
|
+};
|
|
+
|
|
+static int iep_querycap(struct file *file, void *priv,
|
|
+ struct v4l2_capability *cap)
|
|
+{
|
|
+ strscpy(cap->driver, IEP_NAME, sizeof(cap->driver));
|
|
+ strscpy(cap->card, IEP_NAME, sizeof(cap->card));
|
|
+ snprintf(cap->bus_info, sizeof(cap->bus_info),
|
|
+ "platform:%s", IEP_NAME);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int iep_enum_fmt(struct file *file, void *priv,
|
|
+ struct v4l2_fmtdesc *f)
|
|
+{
|
|
+ struct iep_fmt *fmt;
|
|
+
|
|
+ if (f->index < ARRAY_SIZE(formats)) {
|
|
+ fmt = &formats[f->index];
|
|
+ f->pixelformat = fmt->fourcc;
|
|
+
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ return -EINVAL;
|
|
+}
|
|
+
|
|
+static int iep_enum_framesizes(struct file *file, void *priv,
|
|
+ struct v4l2_frmsizeenum *fsize)
|
|
+{
|
|
+ if (fsize->index != 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (!iep_check_pix_format(fsize->pixel_format))
|
|
+ return -EINVAL;
|
|
+
|
|
+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
|
|
+
|
|
+ fsize->stepwise.min_width = IEP_MIN_WIDTH;
|
|
+ fsize->stepwise.max_width = IEP_MAX_WIDTH;
|
|
+ fsize->stepwise.step_width = 16;
|
|
+
|
|
+ fsize->stepwise.min_height = IEP_MIN_HEIGHT;
|
|
+ fsize->stepwise.max_height = IEP_MAX_HEIGHT;
|
|
+ fsize->stepwise.step_height = 16;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static inline struct iep_ctx *iep_file2ctx(struct file *file)
|
|
+{
|
|
+ return container_of(file->private_data, struct iep_ctx, fh);
|
|
+}
|
|
+
|
|
+static int iep_g_fmt_vid_cap(struct file *file, void *priv,
|
|
+ struct v4l2_format *f)
|
|
+{
|
|
+ struct iep_ctx *ctx = iep_file2ctx(file);
|
|
+
|
|
+ f->fmt.pix = ctx->dst_fmt.pix;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int iep_g_fmt_vid_out(struct file *file, void *priv,
|
|
+ struct v4l2_format *f)
|
|
+{
|
|
+ struct iep_ctx *ctx = iep_file2ctx(file);
|
|
+
|
|
+ f->fmt.pix = ctx->src_fmt.pix;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int iep_try_fmt_vid_cap(struct file *file, void *priv,
|
|
+ struct v4l2_format *f)
|
|
+{
|
|
+ f->fmt.pix.field = V4L2_FIELD_NONE;
|
|
+ iep_prepare_format(&f->fmt.pix);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int iep_try_fmt_vid_out(struct file *file, void *priv,
|
|
+ struct v4l2_format *f)
|
|
+{
|
|
+ if (f->fmt.pix.field != V4L2_FIELD_INTERLACED_TB &&
|
|
+ f->fmt.pix.field != V4L2_FIELD_INTERLACED_BT &&
|
|
+ f->fmt.pix.field != V4L2_FIELD_INTERLACED)
|
|
+ f->fmt.pix.field = V4L2_FIELD_INTERLACED;
|
|
+
|
|
+ iep_prepare_format(&f->fmt.pix);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int iep_s_fmt_vid_out(struct file *file, void *priv,
|
|
+ struct v4l2_format *f)
|
|
+{
|
|
+ struct iep_ctx *ctx = iep_file2ctx(file);
|
|
+ struct vb2_queue *vq;
|
|
+
|
|
+ int ret;
|
|
+
|
|
+ ret = iep_try_fmt_vid_out(file, priv, f);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
|
|
+ if (vb2_is_busy(vq))
|
|
+ return -EBUSY;
|
|
+
|
|
+ ctx->src_fmt.pix = f->fmt.pix;
|
|
+ ctx->src_fmt.hw_fmt = iep_fmt_find(&f->fmt.pix);
|
|
+ ctx->src_fmt.y_stride = IEP_Y_STRIDE(f->fmt.pix.width, f->fmt.pix.height);
|
|
+ ctx->src_fmt.uv_stride = IEP_UV_STRIDE(f->fmt.pix.width, f->fmt.pix.height,
|
|
+ ctx->src_fmt.hw_fmt->uv_factor);
|
|
+
|
|
+ /* Propagate colorspace information to capture. */
|
|
+ ctx->dst_fmt.pix.colorspace = f->fmt.pix.colorspace;
|
|
+ ctx->dst_fmt.pix.xfer_func = f->fmt.pix.xfer_func;
|
|
+ ctx->dst_fmt.pix.ycbcr_enc = f->fmt.pix.ycbcr_enc;
|
|
+ ctx->dst_fmt.pix.quantization = f->fmt.pix.quantization;
|
|
+
|
|
+ /* scaling is not supported */
|
|
+ ctx->dst_fmt.pix.width = f->fmt.pix.width;
|
|
+ ctx->dst_fmt.pix.height = f->fmt.pix.height;
|
|
+ ctx->dst_fmt.y_stride = IEP_Y_STRIDE(f->fmt.pix.width, f->fmt.pix.height);
|
|
+ ctx->dst_fmt.uv_stride = IEP_UV_STRIDE(f->fmt.pix.width, f->fmt.pix.height,
|
|
+ ctx->dst_fmt.hw_fmt->uv_factor);
|
|
+
|
|
+ ctx->fmt_changed = true;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int iep_s_fmt_vid_cap(struct file *file, void *priv,
|
|
+ struct v4l2_format *f)
|
|
+{
|
|
+ struct iep_ctx *ctx = iep_file2ctx(file);
|
|
+ struct vb2_queue *vq;
|
|
+ int ret;
|
|
+
|
|
+ ret = iep_try_fmt_vid_cap(file, priv, f);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
|
|
+ if (vb2_is_busy(vq))
|
|
+ return -EBUSY;
|
|
+
|
|
+ /* scaling is not supported */
|
|
+ f->fmt.pix.width = ctx->src_fmt.pix.width;
|
|
+ f->fmt.pix.height = ctx->src_fmt.pix.height;
|
|
+
|
|
+ ctx->dst_fmt.pix = f->fmt.pix;
|
|
+ ctx->dst_fmt.hw_fmt = iep_fmt_find(&f->fmt.pix);
|
|
+
|
|
+ ctx->dst_fmt.y_stride = IEP_Y_STRIDE(f->fmt.pix.width, f->fmt.pix.height);
|
|
+ ctx->dst_fmt.uv_stride = IEP_UV_STRIDE(f->fmt.pix.width, f->fmt.pix.height,
|
|
+ ctx->dst_fmt.hw_fmt->uv_factor);
|
|
+
|
|
+ ctx->fmt_changed = true;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct v4l2_ioctl_ops iep_ioctl_ops = {
|
|
+ .vidioc_querycap = iep_querycap,
|
|
+
|
|
+ .vidioc_enum_framesizes = iep_enum_framesizes,
|
|
+
|
|
+ .vidioc_enum_fmt_vid_cap = iep_enum_fmt,
|
|
+ .vidioc_g_fmt_vid_cap = iep_g_fmt_vid_cap,
|
|
+ .vidioc_try_fmt_vid_cap = iep_try_fmt_vid_cap,
|
|
+ .vidioc_s_fmt_vid_cap = iep_s_fmt_vid_cap,
|
|
+
|
|
+ .vidioc_enum_fmt_vid_out = iep_enum_fmt,
|
|
+ .vidioc_g_fmt_vid_out = iep_g_fmt_vid_out,
|
|
+ .vidioc_try_fmt_vid_out = iep_try_fmt_vid_out,
|
|
+ .vidioc_s_fmt_vid_out = iep_s_fmt_vid_out,
|
|
+
|
|
+ .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
|
|
+ .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
|
|
+ .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
|
|
+ .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
|
|
+ .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
|
|
+ .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
|
|
+ .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
|
|
+
|
|
+ .vidioc_streamon = v4l2_m2m_ioctl_streamon,
|
|
+ .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
|
|
+};
|
|
+
|
|
+static const struct video_device iep_video_device = {
|
|
+ .name = IEP_NAME,
|
|
+ .vfl_dir = VFL_DIR_M2M,
|
|
+ .fops = &iep_fops,
|
|
+ .ioctl_ops = &iep_ioctl_ops,
|
|
+ .minor = -1,
|
|
+ .release = video_device_release_empty,
|
|
+ .device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING,
|
|
+};
|
|
+
|
|
+static int iep_parse_dt(struct rockchip_iep *iep)
|
|
+{
|
|
+ int ret = 0;
|
|
+
|
|
+ iep->axi_clk = devm_clk_get(iep->dev, "axi");
|
|
+ if (IS_ERR(iep->axi_clk)) {
|
|
+ dev_err(iep->dev, "failed to get aclk clock\n");
|
|
+ return PTR_ERR(iep->axi_clk);
|
|
+ }
|
|
+
|
|
+ iep->ahb_clk = devm_clk_get(iep->dev, "ahb");
|
|
+ if (IS_ERR(iep->ahb_clk)) {
|
|
+ dev_err(iep->dev, "failed to get hclk clock\n");
|
|
+ return PTR_ERR(iep->ahb_clk);
|
|
+ }
|
|
+
|
|
+ ret = clk_set_rate(iep->axi_clk, 300000000);
|
|
+
|
|
+ if (ret)
|
|
+ dev_err(iep->dev, "failed to set axi clock rate to 300 MHz\n");
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static irqreturn_t iep_isr(int irq, void *prv)
|
|
+{
|
|
+ struct rockchip_iep *iep = prv;
|
|
+ struct iep_ctx *ctx;
|
|
+ u32 val;
|
|
+ enum vb2_buffer_state state = VB2_BUF_STATE_DONE;
|
|
+
|
|
+ ctx = v4l2_m2m_get_curr_priv(iep->m2m_dev);
|
|
+ if (!ctx) {
|
|
+ v4l2_err(&iep->v4l2_dev,
|
|
+ "Instance released before the end of transaction\n");
|
|
+ return IRQ_NONE;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * The irq is shared with the iommu. If the runtime-pm state of the
|
|
+ * iep-device is disabled or the interrupt status doesn't match the
|
|
+ * expeceted mask the irq has been targeted to the iommu.
|
|
+ */
|
|
+
|
|
+ if (!pm_runtime_active(iep->dev) ||
|
|
+ !(iep_read(iep, IEP_INT) & IEP_INT_MASK))
|
|
+ return IRQ_NONE;
|
|
+
|
|
+ /* disable interrupt - will be re-enabled at next iep_device_run */
|
|
+ iep_mod(ctx->iep, IEP_INT,
|
|
+ IEP_INT_FRAME_DONE_EN, 0);
|
|
+
|
|
+ iep_mod(iep, IEP_INT, IEP_INT_FRAME_DONE_CLR,
|
|
+ IEP_INT_FRAME_DONE_CLR);
|
|
+
|
|
+ /* wait for all status regs to show "idle" */
|
|
+ val = readl_poll_timeout(iep->regs + IEP_STATUS, val,
|
|
+ (val == 0), 100, IEP_TIMEOUT);
|
|
+
|
|
+ if (val) {
|
|
+ dev_err(iep->dev,
|
|
+ "Failed to wait for job to finish: status: %u\n", val);
|
|
+ state = VB2_BUF_STATE_ERROR;
|
|
+ ctx->job_abort = true;
|
|
+ }
|
|
+
|
|
+ iep_m2m_dst_bufs_done(ctx, state);
|
|
+
|
|
+ ctx->field_bff = (ctx->dst_buffs_done % 2 == 0)
|
|
+ ? ctx->field_order_bff : !ctx->field_order_bff;
|
|
+
|
|
+ if (ctx->dst_buffs_done == 2 || ctx->job_abort) {
|
|
+ if (ctx->prev_src_buf)
|
|
+ v4l2_m2m_buf_done(ctx->prev_src_buf, state);
|
|
+
|
|
+ /* current src buff will be next prev */
|
|
+ ctx->prev_src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
|
|
+
|
|
+ v4l2_m2m_job_finish(ctx->iep->m2m_dev, ctx->fh.m2m_ctx);
|
|
+ ctx->dst_buffs_done = 0;
|
|
+
|
|
+ } else {
|
|
+ iep_device_run(ctx);
|
|
+ }
|
|
+
|
|
+ return IRQ_HANDLED;
|
|
+}
|
|
+
|
|
+static int iep_probe(struct platform_device *pdev)
|
|
+{
|
|
+ struct rockchip_iep *iep;
|
|
+ struct video_device *vfd;
|
|
+ struct resource *res;
|
|
+ int ret = 0;
|
|
+ int irq;
|
|
+
|
|
+ if (!pdev->dev.of_node)
|
|
+ return -ENODEV;
|
|
+
|
|
+ iep = devm_kzalloc(&pdev->dev, sizeof(*iep), GFP_KERNEL);
|
|
+ if (!iep)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ platform_set_drvdata(pdev, iep);
|
|
+ iep->dev = &pdev->dev;
|
|
+ iep->vfd = iep_video_device;
|
|
+
|
|
+ ret = iep_parse_dt(iep);
|
|
+ if (ret)
|
|
+ dev_err(&pdev->dev, "Unable to parse OF data\n");
|
|
+
|
|
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
+
|
|
+ iep->regs = devm_ioremap_resource(iep->dev, res);
|
|
+ if (IS_ERR(iep->regs)) {
|
|
+ ret = PTR_ERR(iep->regs);
|
|
+ goto err_put_clk;
|
|
+ }
|
|
+
|
|
+ ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
|
|
+ if (ret) {
|
|
+ dev_err(&pdev->dev, "Could not set DMA coherent mask.\n");
|
|
+ goto err_put_clk;
|
|
+ }
|
|
+
|
|
+ vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32));
|
|
+
|
|
+ irq = platform_get_irq(pdev, 0);
|
|
+ if (irq < 0) {
|
|
+ ret = irq;
|
|
+ goto err_put_clk;
|
|
+ }
|
|
+
|
|
+ /* IRQ is shared with IOMMU */
|
|
+ ret = devm_request_irq(iep->dev, irq, iep_isr, IRQF_SHARED,
|
|
+ dev_name(iep->dev), iep);
|
|
+ if (ret < 0) {
|
|
+ dev_err(iep->dev, "failed to request irq\n");
|
|
+ goto err_put_clk;
|
|
+ }
|
|
+
|
|
+ mutex_init(&iep->mutex);
|
|
+
|
|
+ ret = v4l2_device_register(&pdev->dev, &iep->v4l2_dev);
|
|
+ if (ret) {
|
|
+ dev_err(iep->dev, "Failed to register V4L2 device\n");
|
|
+
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ vfd = &iep->vfd;
|
|
+ vfd->lock = &iep->mutex;
|
|
+ vfd->v4l2_dev = &iep->v4l2_dev;
|
|
+
|
|
+ snprintf(vfd->name, sizeof(vfd->name), "%s",
|
|
+ iep_video_device.name);
|
|
+
|
|
+ video_set_drvdata(vfd, iep);
|
|
+
|
|
+ ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0);
|
|
+ if (ret) {
|
|
+ v4l2_err(&iep->v4l2_dev, "Failed to register video device\n");
|
|
+
|
|
+ goto err_v4l2;
|
|
+ }
|
|
+
|
|
+ v4l2_info(&iep->v4l2_dev,
|
|
+ "Device %s registered as /dev/video%d\n", vfd->name, vfd->num);
|
|
+
|
|
+ iep->m2m_dev = v4l2_m2m_init(&iep_m2m_ops);
|
|
+ if (IS_ERR(iep->m2m_dev)) {
|
|
+ v4l2_err(&iep->v4l2_dev,
|
|
+ "Failed to initialize V4L2 M2M device\n");
|
|
+ ret = PTR_ERR(iep->m2m_dev);
|
|
+
|
|
+ goto err_video;
|
|
+ }
|
|
+
|
|
+ pm_runtime_set_autosuspend_delay(iep->dev, 100);
|
|
+ pm_runtime_use_autosuspend(iep->dev);
|
|
+ pm_runtime_enable(iep->dev);
|
|
+
|
|
+ return ret;
|
|
+
|
|
+err_video:
|
|
+ video_unregister_device(&iep->vfd);
|
|
+err_v4l2:
|
|
+ v4l2_device_unregister(&iep->v4l2_dev);
|
|
+err_put_clk:
|
|
+ pm_runtime_dont_use_autosuspend(iep->dev);
|
|
+ pm_runtime_disable(iep->dev);
|
|
+
|
|
+return ret;
|
|
+}
|
|
+
|
|
+static int iep_remove(struct platform_device *pdev)
|
|
+{
|
|
+ struct rockchip_iep *iep = platform_get_drvdata(pdev);
|
|
+
|
|
+ pm_runtime_dont_use_autosuspend(iep->dev);
|
|
+ pm_runtime_disable(iep->dev);
|
|
+
|
|
+ v4l2_m2m_release(iep->m2m_dev);
|
|
+ video_unregister_device(&iep->vfd);
|
|
+ v4l2_device_unregister(&iep->v4l2_dev);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int __maybe_unused iep_runtime_suspend(struct device *dev)
|
|
+{
|
|
+ struct rockchip_iep *iep = dev_get_drvdata(dev);
|
|
+
|
|
+ clk_disable_unprepare(iep->ahb_clk);
|
|
+ clk_disable_unprepare(iep->axi_clk);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int __maybe_unused iep_runtime_resume(struct device *dev)
|
|
+{
|
|
+ struct rockchip_iep *iep;
|
|
+ int ret = 0;
|
|
+
|
|
+ iep = dev_get_drvdata(dev);
|
|
+
|
|
+ ret = clk_prepare_enable(iep->axi_clk);
|
|
+ if (ret) {
|
|
+ dev_err(iep->dev, "Cannot enable axi clock: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = clk_prepare_enable(iep->ahb_clk);
|
|
+ if (ret) {
|
|
+ dev_err(iep->dev, "Cannot enable ahb clock: %d\n", ret);
|
|
+ goto err_disable_axi_clk;
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+
|
|
+err_disable_axi_clk:
|
|
+ clk_disable_unprepare(iep->axi_clk);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static const struct dev_pm_ops iep_pm_ops = {
|
|
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
|
+ pm_runtime_force_resume)
|
|
+ SET_RUNTIME_PM_OPS(iep_runtime_suspend,
|
|
+ iep_runtime_resume, NULL)
|
|
+};
|
|
+
|
|
+static const struct of_device_id rockchip_iep_match[] = {
|
|
+ {
|
|
+ .compatible = "rockchip,rk3228-iep",
|
|
+ },
|
|
+ {},
|
|
+};
|
|
+
|
|
+MODULE_DEVICE_TABLE(of, rockchip_iep_match);
|
|
+
|
|
+static struct platform_driver iep_pdrv = {
|
|
+ .probe = iep_probe,
|
|
+ .remove = iep_remove,
|
|
+ .driver = {
|
|
+ .name = IEP_NAME,
|
|
+ .pm = &iep_pm_ops,
|
|
+ .of_match_table = rockchip_iep_match,
|
|
+ },
|
|
+};
|
|
+
|
|
+module_platform_driver(iep_pdrv);
|
|
+
|
|
+MODULE_AUTHOR("Alex Bee <knaerzche@gmail.com>");
|
|
+MODULE_DESCRIPTION("Rockchip Image Enhancement Processor");
|
|
+MODULE_LICENSE("GPL v2");
|
|
diff --git a/drivers/media/platform/rockchip/iep/iep.h b/drivers/media/platform/rockchip/iep/iep.h
|
|
new file mode 100644
|
|
index 000000000000..7d9fc61624b6
|
|
--- /dev/null
|
|
+++ b/drivers/media/platform/rockchip/iep/iep.h
|
|
@@ -0,0 +1,112 @@
|
|
+/* SPDX-License-Identifier: GPL-2.0-only */
|
|
+/*
|
|
+ * Rockchip Image Enhancement Processor (IEP) driver
|
|
+ *
|
|
+ * Copyright (C) 2020 Alex Bee <knaerzche@gmail.com>
|
|
+ *
|
|
+ */
|
|
+#ifndef __IEP_H__
|
|
+#define __IEP_H__
|
|
+
|
|
+#include <linux/platform_device.h>
|
|
+#include <media/videobuf2-v4l2.h>
|
|
+#include <media/v4l2-ctrls.h>
|
|
+#include <media/v4l2-device.h>
|
|
+
|
|
+#define IEP_NAME "rockchip-iep"
|
|
+
|
|
+/* Hardware limits */
|
|
+#define IEP_MIN_WIDTH 320U
|
|
+#define IEP_MAX_WIDTH 1920U
|
|
+
|
|
+#define IEP_MIN_HEIGHT 240U
|
|
+#define IEP_MAX_HEIGHT 1088U
|
|
+
|
|
+/* Hardware defaults */
|
|
+#define IEP_DEFAULT_WIDTH 320U
|
|
+#define IEP_DEFAULT_HEIGHT 240U
|
|
+
|
|
+//ns
|
|
+#define IEP_TIMEOUT 250000
|
|
+
|
|
+struct iep_fmt {
|
|
+ u32 fourcc;
|
|
+ u8 depth;
|
|
+ u8 uv_factor;
|
|
+ u8 color_swap;
|
|
+ u8 hw_format;
|
|
+};
|
|
+
|
|
+struct iep_frm_fmt {
|
|
+ struct iep_fmt *hw_fmt;
|
|
+ struct v4l2_pix_format pix;
|
|
+
|
|
+ unsigned int y_stride;
|
|
+ unsigned int uv_stride;
|
|
+};
|
|
+
|
|
+struct iep_ctx {
|
|
+ struct v4l2_fh fh;
|
|
+ struct rockchip_iep *iep;
|
|
+
|
|
+ struct iep_frm_fmt src_fmt;
|
|
+ struct iep_frm_fmt dst_fmt;
|
|
+
|
|
+ struct vb2_v4l2_buffer *prev_src_buf;
|
|
+ struct vb2_v4l2_buffer *dst0_buf;
|
|
+ struct vb2_v4l2_buffer *dst1_buf;
|
|
+
|
|
+ u32 dst_sequence;
|
|
+ u32 src_sequence;
|
|
+
|
|
+ /* bff = bottom field first */
|
|
+ bool field_order_bff;
|
|
+ bool field_bff;
|
|
+
|
|
+ unsigned int dst_buffs_done;
|
|
+
|
|
+ bool fmt_changed;
|
|
+ bool job_abort;
|
|
+};
|
|
+
|
|
+struct rockchip_iep {
|
|
+ struct v4l2_device v4l2_dev;
|
|
+ struct v4l2_m2m_dev *m2m_dev;
|
|
+ struct video_device vfd;
|
|
+
|
|
+ struct device *dev;
|
|
+
|
|
+ void __iomem *regs;
|
|
+
|
|
+ struct clk *axi_clk;
|
|
+ struct clk *ahb_clk;
|
|
+
|
|
+ /* vfd lock */
|
|
+ struct mutex mutex;
|
|
+};
|
|
+
|
|
+static inline void iep_write(struct rockchip_iep *iep, u32 reg, u32 value)
|
|
+{
|
|
+ writel(value, iep->regs + reg);
|
|
+};
|
|
+
|
|
+static inline u32 iep_read(struct rockchip_iep *iep, u32 reg)
|
|
+{
|
|
+ return readl(iep->regs + reg);
|
|
+};
|
|
+
|
|
+static inline void iep_shadow_mod(struct rockchip_iep *iep, u32 reg,
|
|
+ u32 shadow_reg, u32 mask, u32 val)
|
|
+{
|
|
+ u32 temp = iep_read(iep, shadow_reg) & ~(mask);
|
|
+
|
|
+ temp |= val & mask;
|
|
+ iep_write(iep, reg, temp);
|
|
+};
|
|
+
|
|
+static inline void iep_mod(struct rockchip_iep *iep, u32 reg, u32 mask, u32 val)
|
|
+{
|
|
+ iep_shadow_mod(iep, reg, reg, mask, val);
|
|
+};
|
|
+
|
|
+#endif
|
|
--
|
|
Armbian
|
|
|