aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHe Yangxuan <yangxuan8282@gmail.com>2018-11-16 23:09:10 +0800
committerNatanael Copa <ncopa@alpinelinux.org>2018-11-26 15:26:48 +0000
commit8dc0e43d5a410a6f173c14caaa625adaf45f11f4 (patch)
tree8818259554accdf24310ae8aef8a0ed4a06427d5
parent9a88176c5408adb5321fa11300cb63e4dbea80ed (diff)
downloadaports-8dc0e43d5a410a6f173c14caaa625adaf45f11f4.tar.bz2
aports-8dc0e43d5a410a6f173c14caaa625adaf45f11f4.tar.xz
testing/linux-amlogic: new aport
This kernel package add support for Amlogic S9xxx device. Supported devices include many android box and some SBC like Odroid C2, Khadas Vim, Nanopi K2. This package based on mainline kernel, and apply some patches from mainline amlogic developer.
-rw-r--r--testing/linux-amlogic/0001-ARM64-dts-meson-add-vdec-entries.patch54
-rw-r--r--testing/linux-amlogic/0001-ARM64-dts-meson-gxbb-nanopi-k2-Add-HDMI-CEC-and-CVBS.patch81
-rw-r--r--testing/linux-amlogic/0001-libretech-cc-disable-CVBS-connector.patch24
-rw-r--r--testing/linux-amlogic/0002-drm-meson-Make-DMT-timings-parameters-and-pixel-cloc.patch1314
-rw-r--r--testing/linux-amlogic/0003-ARM64-defconfig-enable-CEC-support.patch44
-rw-r--r--testing/linux-amlogic/0004-clk-meson-switch-gxbb-cts-amclk-div-to-the-generic-d.patch44
-rw-r--r--testing/linux-amlogic/0005-clk-meson-remove-unused-clk-audio-divider-driver.patch48
-rw-r--r--testing/linux-amlogic/0006-ASoC-meson-add-meson-audio-core-driver.patch308
-rw-r--r--testing/linux-amlogic/0007-ASoC-meson-add-register-definitions.patch357
-rw-r--r--testing/linux-amlogic/0008-ASoC-meson-add-aiu-i2s-dma-support.patch416
-rw-r--r--testing/linux-amlogic/0009-ASoC-meson-add-initial-i2s-dai-support.patch511
-rw-r--r--testing/linux-amlogic/0010-ASoC-meson-add-aiu-spdif-dma-support.patch438
-rw-r--r--testing/linux-amlogic/0011-ASoC-meson-add-initial-spdif-dai-support.patch426
-rw-r--r--testing/linux-amlogic/0012-ARM64-defconfig-enable-audio-support-for-meson-SoCs-.patch29
-rw-r--r--testing/linux-amlogic/0013-ARM64-dts-meson-gx-add-audio-controller-nodes.patch186
-rw-r--r--testing/linux-amlogic/0014-snd-meson-activate-HDMI-audio-path.patch52
-rw-r--r--testing/linux-amlogic/0015-drm-meson-select-dw-hdmi-i2s-audio-for-meson-hdmi.patch19
-rw-r--r--testing/linux-amlogic/0016-ARM64-dts-meson-gx-add-sound-dai-cells-to-HDMI-node.patch35
-rw-r--r--testing/linux-amlogic/0017-ARM64-dts-meson-activate-hdmi-audio-HDMI-enabled-boa.patch860
-rw-r--r--testing/linux-amlogic/0018-drm-bridge-dw-hdmi-Use-AUTO-CTS-setup-mode-when-non-.patch75
-rw-r--r--testing/linux-amlogic/0019-drm-meson-Call-drm_crtc_vblank_on-drm_crtc_vblank_of.patch36
-rw-r--r--testing/linux-amlogic/0020-media-platform-meson-ao-cec-make-busy-TX-warning-sil.patch32
-rw-r--r--testing/linux-amlogic/0021-soc-amlogic-add-meson-canvas-driver.patch280
-rw-r--r--testing/linux-amlogic/0022-dt-bindings-soc-amlogic-add-meson-canvas-documentati.patch55
-rw-r--r--testing/linux-amlogic/0023-ARM64-dts-meson-gx-add-dmcbus-and-canvas-nodes.patch40
-rw-r--r--testing/linux-amlogic/0024-drm-meson-convert-to-the-new-canvas-module.patch399
-rw-r--r--testing/linux-amlogic/0025-WIP-drm-meson-Support-Overlay-plane-for-video-render.patch1141
-rw-r--r--testing/linux-amlogic/0026-media-meson-add-v4l2-m2m-video-decoder-driver.patch5906
-rw-r--r--testing/linux-amlogic/0027-ARM64-dts-meson-gx-add-vdec-entry.patch35
-rw-r--r--testing/linux-amlogic/APKBUILD313
-rw-r--r--testing/linux-amlogic/add-phicomm-n1.patch60
-rw-r--r--testing/linux-amlogic/brcmfmac-Disable-power-management.patch13
-rw-r--r--testing/linux-amlogic/bt-btbcm.patch11
-rw-r--r--testing/linux-amlogic/config-changes-amlogic.aarch6413
-rw-r--r--testing/linux-amlogic/defconfig882
-rw-r--r--testing/linux-amlogic/off_error_text_offset.patch13
-rw-r--r--testing/linux-amlogic/offset.patch13
37 files changed, 14563 insertions, 0 deletions
diff --git a/testing/linux-amlogic/0001-ARM64-dts-meson-add-vdec-entries.patch b/testing/linux-amlogic/0001-ARM64-dts-meson-add-vdec-entries.patch
new file mode 100644
index 0000000000..861623ad33
--- /dev/null
+++ b/testing/linux-amlogic/0001-ARM64-dts-meson-add-vdec-entries.patch
@@ -0,0 +1,54 @@
+From f7fd519b2188e86d212234cf3f2c8606917afd0a Mon Sep 17 00:00:00 2001
+From: Maxime Jourdan <maxi.jourdan@wanadoo.fr>
+Date: Thu, 26 Jul 2018 23:30:30 +0200
+Subject: [PATCH] ARM64: dts: meson: add vdec entries
+
+This enables the video decoder for gxbb, gxl and gxm chips
+---
+ arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi | 7 +++++++
+ arch/arm64/boot/dts/amlogic/meson-gxl.dtsi | 7 +++++++
+ arch/arm64/boot/dts/amlogic/meson-gxm.dtsi | 4 ++++
+ 3 files changed, 18 insertions(+)
+
+diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi
+index 2a4d506..c34ecca 100644
+--- a/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi
++++ b/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi
+@@ -814,3 +814,10 @@
+ power-domains = <&pwrc_vpu>;
+ };
+
++&vdec {
++ compatible = "amlogic,meson-gxbb-vdec";
++ clocks = <&clkc CLKID_DOS_PARSER>, <&clkc CLKID_DOS>, <&clkc CLKID_VDEC_1>, <&clkc CLKID_VDEC_HEVC>;
++ clock-names = "dos_parser", "dos", "vdec_1", "vdec_hevc";
++ resets = <&reset RESET_PARSER>;
++ reset-names = "esparser";
++};
+diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
+index 9f4b618..7b95db8 100644
+--- a/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
++++ b/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
+@@ -814,3 +814,10 @@
+ power-domains = <&pwrc_vpu>;
+ };
+
++&vdec {
++ compatible = "amlogic,meson-gxl-vdec";
++ clocks = <&clkc CLKID_DOS_PARSER>, <&clkc CLKID_DOS>, <&clkc CLKID_VDEC_1>, <&clkc CLKID_VDEC_HEVC>;
++ clock-names = "dos_parser", "dos", "vdec_1", "vdec_hevc";
++ resets = <&reset RESET_PARSER>;
++ reset-names = "esparser";
++};
+diff --git a/arch/arm64/boot/dts/amlogic/meson-gxm.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxm.dtsi
+index 247888d..4ce7b12 100644
+--- a/arch/arm64/boot/dts/amlogic/meson-gxm.dtsi
++++ b/arch/arm64/boot/dts/amlogic/meson-gxm.dtsi
+@@ -117,3 +117,7 @@
+ &dwc3 {
+ phys = <&usb3_phy>, <&usb2_phy0>, <&usb2_phy1>, <&usb2_phy2>;
+ };
++
++&vdec {
++ compatible = "amlogic,meson-gxm-vdec";
++};
diff --git a/testing/linux-amlogic/0001-ARM64-dts-meson-gxbb-nanopi-k2-Add-HDMI-CEC-and-CVBS.patch b/testing/linux-amlogic/0001-ARM64-dts-meson-gxbb-nanopi-k2-Add-HDMI-CEC-and-CVBS.patch
new file mode 100644
index 0000000000..aad63ba8fe
--- /dev/null
+++ b/testing/linux-amlogic/0001-ARM64-dts-meson-gxbb-nanopi-k2-Add-HDMI-CEC-and-CVBS.patch
@@ -0,0 +1,81 @@
+From 6c5aaf27886c9b308e9c4e4d613990e540f23ec8 Mon Sep 17 00:00:00 2001
+From: Neil Armstrong <narmstrong@baylibre.com>
+Date: Tue, 26 Jun 2018 09:37:39 +0200
+Subject: [PATCH] ARM64: dts: meson-gxbb-nanopi-k2: Add HDMI, CEC and CVBS
+ nodes
+
+The Amlogic Meson GXBB based Nanopi-K2 board has an HDMI connector
+with CEC and CVBS available on the 40pin header.
+This patch adds the nodes to enable HDMI, CEC and CVBS functionnalities.
+
+Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
+---
+ .../boot/dts/amlogic/meson-gxbb-nanopi-k2.dts | 48 ++++++++++++++++++++++
+ 1 file changed, 48 insertions(+)
+
+diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-nanopi-k2.dts b/arch/arm64/boot/dts/amlogic/meson-gxbb-nanopi-k2.dts
+index 7d5709c..cbe99bd 100644
+--- a/arch/arm64/boot/dts/amlogic/meson-gxbb-nanopi-k2.dts
++++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-nanopi-k2.dts
+@@ -106,6 +106,42 @@
+ compatible = "mmc-pwrseq-emmc";
+ reset-gpios = <&gpio BOOT_9 GPIO_ACTIVE_LOW>;
+ };
++
++ /* CVBS is available on CON1 pin 36, disabled by default */
++ cvbs-connector {
++ compatible = "composite-video-connector";
++ status = "disabled";
++
++ port {
++ cvbs_connector_in: endpoint {
++ remote-endpoint = <&cvbs_vdac_out>;
++ };
++ };
++ };
++
++ hdmi-connector {
++ compatible = "hdmi-connector";
++ type = "a";
++
++ port {
++ hdmi_connector_in: endpoint {
++ remote-endpoint = <&hdmi_tx_tmds_out>;
++ };
++ };
++ };
++};
++
++&cec_AO {
++ status = "okay";
++ pinctrl-0 = <&ao_cec_pins>;
++ pinctrl-names = "default";
++ hdmi-phandle = <&hdmi_tx>;
++};
++
++&cvbs_vdac_port {
++ cvbs_vdac_out: endpoint {
++ remote-endpoint = <&cvbs_connector_in>;
++ };
+ };
+
+ &ethmac {
+@@ -137,6 +173,18 @@
+ };
+ };
+
++&hdmi_tx {
++ status = "okay";
++ pinctrl-0 = <&hdmi_hpd_pins>, <&hdmi_i2c_pins>;
++ pinctrl-names = "default";
++};
++
++&hdmi_tx_tmds_port {
++ hdmi_tx_tmds_out: endpoint {
++ remote-endpoint = <&hdmi_connector_in>;
++ };
++};
++
+ &ir {
+ status = "okay";
+ pinctrl-0 = <&remote_input_ao_pins>;
diff --git a/testing/linux-amlogic/0001-libretech-cc-disable-CVBS-connector.patch b/testing/linux-amlogic/0001-libretech-cc-disable-CVBS-connector.patch
new file mode 100644
index 0000000000..7f82981189
--- /dev/null
+++ b/testing/linux-amlogic/0001-libretech-cc-disable-CVBS-connector.patch
@@ -0,0 +1,24 @@
+From baa0a8ee8b8a0a14ddab6b14c37846dfed261007 Mon Sep 17 00:00:00 2001
+From: Koen Kooi <koen@dominion.thruhere.net>
+Date: Fri, 11 May 2018 13:51:20 +0200
+Subject: [PATCH] libretech-cc: disable CVBS connector
+
+Signed-off-by: Koen Kooi <koen@dominion.thruhere.net>
+---
+ arch/arm64/boot/dts/amlogic/meson-gxl-s905x-libretech-cc.dts | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-libretech-cc.dts b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-libretech-cc.dts
+index f56969e..ac3a150 100644
+--- a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-libretech-cc.dts
++++ b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-libretech-cc.dts
+@@ -24,7 +24,8 @@
+ stdout-path = "serial0:115200n8";
+ };
+
+- cvbs-connector {
++ cvbs_connector: cvbs-connector {
++ status = "disabled";
+ compatible = "composite-video-connector";
+
+ port {
diff --git a/testing/linux-amlogic/0002-drm-meson-Make-DMT-timings-parameters-and-pixel-cloc.patch b/testing/linux-amlogic/0002-drm-meson-Make-DMT-timings-parameters-and-pixel-cloc.patch
new file mode 100644
index 0000000000..7625f11f5b
--- /dev/null
+++ b/testing/linux-amlogic/0002-drm-meson-Make-DMT-timings-parameters-and-pixel-cloc.patch
@@ -0,0 +1,1314 @@
+From 8ea8dd0432a9964e3e2642433d942c3aa9116fd3 Mon Sep 17 00:00:00 2001
+From: Neil Armstrong <narmstrong@baylibre.com>
+Date: Fri, 27 Apr 2018 17:19:46 +0200
+Subject: [PATCH] drm/meson: Make DMT timings parameters and pixel clock
+ generic
+
+Remove the modes timings tables for DMT modes and calculate the HW
+paremeters from the modes timings.
+
+Switch the DMT modes pixel clock calculation out of the static frequency
+list to a generic calculation from a range of possible PLL dividers.
+
+This setup permits setting non-CEA modes like :
+- 1600x900-60Hz
+- 1280x1024-75Hz
+- 1280x1024-60Hz
+- 1440x900-60Hz
+- 1366x768-60Hz
+- 1280x800-60Hz
+- 1152x864-75Hz
+- 1024x768-75Hz
+- 1024x768-70Hz
+- 1024x768-60Hz
+- 832x624-75Hz
+- 800x600-75Hz
+- 800x600-72Hz
+- 800x600-60Hz
+- 640x480-75Hz
+- 640x480-73Hz
+- 640x480-67Hz
+
+Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
+---
+ drivers/gpu/drm/meson/meson_dw_hdmi.c | 22 +-
+ drivers/gpu/drm/meson/meson_vclk.c | 658 +++++++++++++++-------------------
+ drivers/gpu/drm/meson/meson_vclk.h | 4 +
+ drivers/gpu/drm/meson/meson_venc.c | 379 +++-----------------
+ drivers/gpu/drm/meson/meson_venc.h | 3 +-
+ 5 files changed, 352 insertions(+), 714 deletions(-)
+
+diff --git a/drivers/gpu/drm/meson/meson_dw_hdmi.c b/drivers/gpu/drm/meson/meson_dw_hdmi.c
+index c9ad456..df7247c 100644
+--- a/drivers/gpu/drm/meson/meson_dw_hdmi.c
++++ b/drivers/gpu/drm/meson/meson_dw_hdmi.c
+@@ -329,6 +329,12 @@ static void dw_hdmi_set_vclk(struct meson_dw_hdmi *dw_hdmi,
+
+ vclk_freq = mode->clock;
+
++ if (!vic) {
++ meson_vclk_setup(priv, MESON_VCLK_TARGET_DMT, vclk_freq,
++ vclk_freq, vclk_freq, false);
++ return;
++ }
++
+ if (mode->flags & DRM_MODE_FLAG_DBLCLK)
+ vclk_freq *= 2;
+
+@@ -542,10 +548,12 @@ static enum drm_mode_status
+ dw_hdmi_mode_valid(struct drm_connector *connector,
+ const struct drm_display_mode *mode)
+ {
++ struct meson_drm *priv = connector->dev->dev_private;
+ unsigned int vclk_freq;
+ unsigned int venc_freq;
+ unsigned int hdmi_freq;
+ int vic = drm_match_cea_mode(mode);
++ enum drm_mode_status status;
+
+ DRM_DEBUG_DRIVER("Modeline %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x\n",
+ mode->base.id, mode->name, mode->vrefresh, mode->clock,
+@@ -556,8 +564,11 @@ dw_hdmi_mode_valid(struct drm_connector *connector,
+
+ /* Check against non-VIC supported modes */
+ if (!vic) {
+- if (!meson_venc_hdmi_supported_mode(mode))
+- return MODE_BAD;
++ status = meson_venc_hdmi_supported_mode(mode);
++ if (status != MODE_OK)
++ return status;
++
++ return meson_vclk_dmt_supported_freq(priv, mode->clock);
+ /* Check against supported VIC modes */
+ } else if (!meson_venc_hdmi_supported_vic(vic))
+ return MODE_BAD;
+@@ -583,16 +594,11 @@ dw_hdmi_mode_valid(struct drm_connector *connector,
+ dev_dbg(connector->dev->dev, "%s: vclk:%d venc=%d hdmi=%d\n", __func__,
+ vclk_freq, venc_freq, hdmi_freq);
+
+- /* Finally filter by configurable vclk frequencies */
++ /* Finally filter by configurable vclk frequencies for VIC modes */
+ switch (vclk_freq) {
+- case 25175:
+- case 40000:
+ case 54000:
+- case 65000:
+ case 74250:
+- case 108000:
+ case 148500:
+- case 162000:
+ case 297000:
+ case 594000:
+ return MODE_OK;
+diff --git a/drivers/gpu/drm/meson/meson_vclk.c b/drivers/gpu/drm/meson/meson_vclk.c
+index f051122..0b17788 100644
+--- a/drivers/gpu/drm/meson/meson_vclk.c
++++ b/drivers/gpu/drm/meson/meson_vclk.c
+@@ -320,32 +320,23 @@ static void meson_venci_cvbs_clock_config(struct meson_drm *priv)
+ CTS_VDAC_EN, CTS_VDAC_EN);
+ }
+
+-
++enum {
+ /* PLL O1 O2 O3 VP DV EN TX */
+ /* 4320 /4 /4 /1 /5 /1 => /2 /2 */
+-#define MESON_VCLK_HDMI_ENCI_54000 1
++ MESON_VCLK_HDMI_ENCI_54000 = 1,
+ /* 4320 /4 /4 /1 /5 /1 => /1 /2 */
+-#define MESON_VCLK_HDMI_DDR_54000 2
++ MESON_VCLK_HDMI_DDR_54000,
+ /* 2970 /4 /1 /1 /5 /1 => /1 /2 */
+-#define MESON_VCLK_HDMI_DDR_148500 3
+-/* 4028 /4 /4 /1 /5 /2 => /1 /1 */
+-#define MESON_VCLK_HDMI_25175 4
+-/* 3200 /4 /2 /1 /5 /2 => /1 /1 */
+-#define MESON_VCLK_HDMI_40000 5
+-/* 5200 /4 /2 /1 /5 /2 => /1 /1 */
+-#define MESON_VCLK_HDMI_65000 6
++ MESON_VCLK_HDMI_DDR_148500,
+ /* 2970 /2 /2 /2 /5 /1 => /1 /1 */
+-#define MESON_VCLK_HDMI_74250 7
+-/* 4320 /4 /1 /1 /5 /2 => /1 /1 */
+-#define MESON_VCLK_HDMI_108000 8
++ MESON_VCLK_HDMI_74250,
+ /* 2970 /1 /2 /2 /5 /1 => /1 /1 */
+-#define MESON_VCLK_HDMI_148500 9
+-/* 3240 /2 /1 /1 /5 /2 => /1 /1 */
+-#define MESON_VCLK_HDMI_162000 10
++ MESON_VCLK_HDMI_148500,
+ /* 2970 /1 /1 /1 /5 /2 => /1 /1 */
+-#define MESON_VCLK_HDMI_297000 11
++ MESON_VCLK_HDMI_297000,
+ /* 5940 /1 /1 /2 /5 /1 => /1 /1 */
+-#define MESON_VCLK_HDMI_594000 12
++ MESON_VCLK_HDMI_594000
++};
+
+ struct meson_vclk_params {
+ unsigned int pll_base_freq;
+@@ -411,46 +402,6 @@ struct meson_vclk_params {
+ .vid_pll_div = VID_PLL_DIV_5,
+ .vclk_div = 1,
+ },
+- [MESON_VCLK_HDMI_25175] = {
+- .pll_base_freq = 4028000,
+- .pll_od1 = 4,
+- .pll_od2 = 4,
+- .pll_od3 = 1,
+- .vid_pll_div = VID_PLL_DIV_5,
+- .vclk_div = 2,
+- },
+- [MESON_VCLK_HDMI_40000] = {
+- .pll_base_freq = 3200000,
+- .pll_od1 = 4,
+- .pll_od2 = 2,
+- .pll_od3 = 1,
+- .vid_pll_div = VID_PLL_DIV_5,
+- .vclk_div = 2,
+- },
+- [MESON_VCLK_HDMI_65000] = {
+- .pll_base_freq = 5200000,
+- .pll_od1 = 4,
+- .pll_od2 = 2,
+- .pll_od3 = 1,
+- .vid_pll_div = VID_PLL_DIV_5,
+- .vclk_div = 2,
+- },
+- [MESON_VCLK_HDMI_108000] = {
+- .pll_base_freq = 4320000,
+- .pll_od1 = 4,
+- .pll_od2 = 1,
+- .pll_od3 = 1,
+- .vid_pll_div = VID_PLL_DIV_5,
+- .vclk_div = 2,
+- },
+- [MESON_VCLK_HDMI_162000] = {
+- .pll_base_freq = 3240000,
+- .pll_od1 = 2,
+- .pll_od2 = 1,
+- .pll_od3 = 1,
+- .vid_pll_div = VID_PLL_DIV_5,
+- .vclk_div = 2,
+- },
+ };
+
+ static inline unsigned int pll_od_to_reg(unsigned int od)
+@@ -470,358 +421,217 @@ static inline unsigned int pll_od_to_reg(unsigned int od)
+ return 0;
+ }
+
+-void meson_hdmi_pll_set(struct meson_drm *priv,
+- unsigned int base,
+- unsigned int od1,
+- unsigned int od2,
+- unsigned int od3)
++void meson_hdmi_pll_set_params(struct meson_drm *priv, unsigned int m,
++ unsigned int frac, unsigned int od1,
++ unsigned int od2, unsigned int od3)
+ {
+ unsigned int val;
+
+ if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) {
+- switch (base) {
+- case 2970000:
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x5800023d);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x00000000);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x0d5c5091);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x801da72c);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x71486980);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x00000e55);
+-
+- /* Enable and unreset */
+- regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL,
+- 0x7 << 28, 0x4 << 28);
+-
+- /* Poll for lock bit */
+- regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL,
+- val, (val & HDMI_PLL_LOCK), 10, 0);
+-
+- /* div_frac */
+- regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2,
+- 0xFFFF, 0x4e00);
+- break;
+-
+- case 3200000:
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x58000242);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x00000000);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x0d5c5091);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x801da72c);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x71486980);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x00000e55);
+-
+- /* unreset */
+- regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL,
+- BIT(28), 0);
+-
+- /* Poll for lock bit */
+- regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL,
+- val, (val & HDMI_PLL_LOCK), 10, 0);
+-
+- /* div_frac */
+- regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2,
+- 0xFFFF, 0x4aab);
+- break;
+-
+- case 3240000:
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x58000243);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x00000000);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x0d5c5091);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x801da72c);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x71486980);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x00000e55);
+-
+- /* unreset */
+- regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL,
+- BIT(28), 0);
+-
+- /* Poll for lock bit */
+- regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL,
+- val, (val & HDMI_PLL_LOCK), 10, 0);
+-
+- /* div_frac */
+- regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2,
+- 0xFFFF, 0x4800);
+- break;
+-
+- case 3865000:
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x58000250);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x00000000);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x0d5c5091);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x801da72c);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x71486980);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x00000e55);
+-
+- /* unreset */
+- regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL,
+- BIT(28), 0);
+-
+- /* Poll for lock bit */
+- regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL,
+- val, (val & HDMI_PLL_LOCK), 10, 0);
+-
+- /* div_frac */
+- regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2,
+- 0xFFFF, 0x4855);
+- break;
+-
+- case 4028000:
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x58000253);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x00000000);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x0d5c5091);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x801da72c);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x71486980);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x00000e55);
+-
+- /* unreset */
+- regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL,
+- BIT(28), 0);
+-
+- /* Poll for lock bit */
+- regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL,
+- val, (val & HDMI_PLL_LOCK), 10, 0);
+-
+- /* div_frac */
+- regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2,
+- 0xFFFF, 0x4eab);
+- break;
+-
+- case 4320000:
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x5800025a);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x00000000);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x0d5c5091);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x801da72c);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x71486980);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x00000e55);
+-
+- /* unreset */
+- regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL,
+- BIT(28), 0);
+-
+- /* Poll for lock bit */
+- regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL,
+- val, (val & HDMI_PLL_LOCK), 10, 0);
+- break;
++ regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x58000200 | m);
++ if (frac)
++ regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2,
++ 0x00004000 | frac);
++ else
++ regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2,
++ 0x00000000);
++ regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x0d5c5091);
++ regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x801da72c);
++ regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x71486980);
++ regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x00000e55);
+
+- case 5940000:
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x5800027b);
+- regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2,
+- 0xFFFF, 0x4c00);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x135c5091);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x801da72c);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x71486980);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x00000e55);
+-
+- /* unreset */
+- regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL,
+- BIT(28), 0);
+-
+- /* Poll for lock bit */
+- regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL,
+- val, (val & HDMI_PLL_LOCK), 10, 0);
+- break;
++ /* Enable and unreset */
++ regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL,
++ 0x7 << 28, 0x4 << 28);
+
+- case 5200000:
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x5800026c);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x00000000);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x135c5091);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x801da72c);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x71486980);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x00000e55);
+-
+- /* unreset */
+- regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL,
+- BIT(28), 0);
+-
+- /* Poll for lock bit */
+- regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL,
+- val, (val & HDMI_PLL_LOCK), 10, 0);
+- break;
+- };
++ /* Poll for lock bit */
++ regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL,
++ val, (val & HDMI_PLL_LOCK), 10, 0);
+ } else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") ||
+- meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) {
+- switch (base) {
+- case 2970000:
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x4000027b);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x800cb300);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x860f30c4);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x0c8e0000);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x001fa729);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x01a31500);
+- break;
+-
+- case 3200000:
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x40000285);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x800cb155);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x860f30c4);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x0c8e0000);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x001fa729);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x01a31500);
+- break;
+-
+- case 3240000:
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x40000287);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x800cb000);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x860f30c4);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x0c8e0000);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x001fa729);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x01a31500);
+- break;
+-
+- case 3865000:
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x400002a1);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x800cb02b);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x860f30c4);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x0c8e0000);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x001fa729);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x01a31500);
+- break;
+-
+- case 4028000:
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x400002a7);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x800cb355);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x860f30c4);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x0c8e0000);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x001fa729);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x01a31500);
+- break;
+-
+- case 4320000:
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x400002b4);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x800cb000);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x860f30c4);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x0c8e0000);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x001fa729);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x01a31500);
+- break;
+-
+- case 5940000:
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x400002f7);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x800cb200);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x860f30c4);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x0c8e0000);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x001fa729);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x01a31500);
+- break;
+-
+- case 5200000:
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x400002d8);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x800cb2ab);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x860f30c4);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x0c8e0000);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x001fa729);
+- regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x01a31500);
+- break;
+-
+- };
++ meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) {
++ regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x40000200 | m);
++ regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x800cb000 | frac);
++ regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x860f30c4);
++ regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x0c8e0000);
++ regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x001fa729);
++ regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x01a31500);
+
+ /* Reset PLL */
+ regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL,
+- HDMI_PLL_RESET, HDMI_PLL_RESET);
++ HDMI_PLL_RESET, HDMI_PLL_RESET);
+ regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL,
+- HDMI_PLL_RESET, 0);
++ HDMI_PLL_RESET, 0);
+
+ /* Poll for lock bit */
+ regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL, val,
+ (val & HDMI_PLL_LOCK), 10, 0);
+- };
++ }
+
+ if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu"))
+ regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2,
+- 3 << 16, pll_od_to_reg(od1) << 16);
++ 3 << 16, pll_od_to_reg(od1) << 16);
+ else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") ||
+- meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu"))
++ meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu"))
+ regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL3,
+- 3 << 21, pll_od_to_reg(od1) << 21);
++ 3 << 21, pll_od_to_reg(od1) << 21);
+
+ if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu"))
+ regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2,
+- 3 << 22, pll_od_to_reg(od2) << 22);
++ 3 << 22, pll_od_to_reg(od2) << 22);
+ else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") ||
+- meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu"))
++ meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu"))
+ regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL3,
+- 3 << 23, pll_od_to_reg(od2) << 23);
++ 3 << 23, pll_od_to_reg(od2) << 23);
+
+ if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu"))
+ regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2,
+- 3 << 18, pll_od_to_reg(od3) << 18);
++ 3 << 18, pll_od_to_reg(od3) << 18);
+ else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") ||
+- meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu"))
++ meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu"))
+ regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL3,
+- 3 << 19, pll_od_to_reg(od3) << 19);
++ 3 << 19, pll_od_to_reg(od3) << 19);
++
+ }
+
+-void meson_vclk_setup(struct meson_drm *priv, unsigned int target,
+- unsigned int vclk_freq, unsigned int venc_freq,
+- unsigned int dac_freq, bool hdmi_use_enci)
++#define XTAL_FREQ 24000
++
++static unsigned int meson_hdmi_pll_get_m(struct meson_drm *priv,
++ unsigned int pll_freq)
+ {
+- unsigned int freq;
+- unsigned int hdmi_tx_div;
+- unsigned int venc_div;
++ /* The GXBB PLL has a /2 pre-multiplier */
++ if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu"))
++ pll_freq /= 2;
+
+- if (target == MESON_VCLK_TARGET_CVBS) {
+- meson_venci_cvbs_clock_config(priv);
+- return;
++ return pll_freq / XTAL_FREQ;
++}
++
++#define HDMI_FRAC_MAX_GXBB 4096
++#define HDMI_FRAC_MAX_GXL 1024
++
++static unsigned int meson_hdmi_pll_get_frac(struct meson_drm *priv,
++ unsigned int m,
++ unsigned int pll_freq)
++{
++ unsigned int parent_freq = XTAL_FREQ;
++ unsigned int frac_max = HDMI_FRAC_MAX_GXL;
++ unsigned int frac_m;
++ unsigned int frac;
++
++ /* The GXBB PLL has a /2 pre-multiplier and a larger FRAC width */
++ if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) {
++ frac_max = HDMI_FRAC_MAX_GXBB;
++ parent_freq *= 2;
+ }
+
+- hdmi_tx_div = vclk_freq / dac_freq;
++ /* We can have a perfect match !*/
++ if (pll_freq / m == parent_freq &&
++ pll_freq % m == 0)
++ return 0;
+
+- if (hdmi_tx_div == 0) {
+- pr_err("Fatal Error, invalid HDMI-TX freq %d\n",
+- dac_freq);
+- return;
++ frac = div_u64((u64)pll_freq * (u64)frac_max, parent_freq);
++ frac_m = m * frac_max;
++ if (frac_m > frac)
++ return frac_max;
++ frac -= frac_m;
++
++ return min((u16)frac, (u16)(frac_max - 1));
++}
++
++static bool meson_hdmi_pll_validate_params(struct meson_drm *priv,
++ unsigned int m,
++ unsigned int frac)
++{
++ if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) {
++ /* Empiric supported min/max dividers */
++ if (m < 53 || m > 123)
++ return false;
++ if (frac >= HDMI_FRAC_MAX_GXBB)
++ return false;
++ } else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") ||
++ meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) {
++ /* Empiric supported min/max dividers */
++ if (m < 106 || m > 247)
++ return false;
++ if (frac >= HDMI_FRAC_MAX_GXL)
++ return false;
+ }
+
+- venc_div = vclk_freq / venc_freq;
++ return true;
++}
+
+- if (venc_div == 0) {
+- pr_err("Fatal Error, invalid HDMI venc freq %d\n",
+- venc_freq);
+- return;
++static bool meson_hdmi_pll_find_params(struct meson_drm *priv,
++ unsigned int freq,
++ unsigned int *m,
++ unsigned int *frac,
++ unsigned int *od)
++{
++ /* Cycle from /16 to /2 */
++ for (*od = 16 ; *od > 1 ; *od >>= 1) {
++ *m = meson_hdmi_pll_get_m(priv, freq * *od);
++ if (!*m)
++ continue;
++ *frac = meson_hdmi_pll_get_frac(priv, *m, freq * *od);
++
++ DRM_DEBUG_DRIVER("PLL params for %dkHz: m=%x frac=%x od=%d\n",
++ freq, *m, *frac, *od);
++
++ if (meson_hdmi_pll_validate_params(priv, *m, *frac))
++ return true;
+ }
+
+- switch (vclk_freq) {
+- case 54000:
+- if (hdmi_use_enci)
+- freq = MESON_VCLK_HDMI_ENCI_54000;
+- else
+- freq = MESON_VCLK_HDMI_DDR_54000;
+- break;
+- case 25175:
+- freq = MESON_VCLK_HDMI_25175;
+- break;
+- case 40000:
+- freq = MESON_VCLK_HDMI_40000;
+- break;
+- case 65000:
+- freq = MESON_VCLK_HDMI_65000;
+- break;
+- case 74250:
+- freq = MESON_VCLK_HDMI_74250;
+- break;
+- case 108000:
+- freq = MESON_VCLK_HDMI_108000;
+- break;
+- case 148500:
+- if (dac_freq != 148500)
+- freq = MESON_VCLK_HDMI_DDR_148500;
+- else
+- freq = MESON_VCLK_HDMI_148500;
+- break;
+- case 162000:
+- freq = MESON_VCLK_HDMI_162000;
+- break;
+- case 297000:
+- freq = MESON_VCLK_HDMI_297000;
+- break;
+- case 594000:
+- freq = MESON_VCLK_HDMI_594000;
+- break;
+- default:
+- pr_err("Fatal Error, invalid HDMI vclk freq %d\n",
+- vclk_freq);
++ return false;
++}
++
++/* pll_freq is the frequency after the OD dividers */
++enum drm_mode_status
++meson_vclk_dmt_supported_freq(struct meson_drm *priv, unsigned int freq)
++{
++ unsigned int od, m, frac;
++
++ /* In DMT mode, path after PLL is always /10 */
++ freq *= 10;
++
++ if (meson_hdmi_pll_find_params(priv, freq, &m, &frac, &od))
++ return MODE_OK;
++
++ return MODE_CLOCK_RANGE;
++}
++EXPORT_SYMBOL_GPL(meson_vclk_dmt_supported_freq);
++
++/* pll_freq is the frequency after the OD dividers */
++static void meson_hdmi_pll_generic_set(struct meson_drm *priv,
++ unsigned int pll_freq)
++{
++ unsigned int od, m, frac, od1, od2, od3;
++
++ if (meson_hdmi_pll_find_params(priv, pll_freq, &m, &frac, &od)) {
++ od3 = 1;
++ if (od < 4) {
++ od1 = 2;
++ od2 = 1;
++ } else {
++ od2 = od / 4;
++ od1 = od / od2;
++ }
++
++ DRM_DEBUG_DRIVER("PLL params for %dkHz: m=%x frac=%x od=%d/%d/%d\n",
++ pll_freq, m, frac, od1, od2, od3);
++
++ meson_hdmi_pll_set_params(priv, m, frac, od1, od2, od3);
++
+ return;
+ }
+
++ DRM_ERROR("Fatal, unable to find parameters for PLL freq %d\n",
++ pll_freq);
++}
++
++static void meson_vclk_set(struct meson_drm *priv, unsigned int pll_base_freq,
++ unsigned int od1, unsigned int od2, unsigned int od3,
++ unsigned int vid_pll_div, unsigned int vclk_div,
++ unsigned int hdmi_tx_div, unsigned int venc_div,
++ bool hdmi_use_enci)
++{
+ /* Set HDMI-TX sys clock */
+ regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL,
+ CTS_HDMI_SYS_SEL_MASK, 0);
+@@ -831,19 +641,49 @@ void meson_vclk_setup(struct meson_drm *priv, unsigned int target,
+ CTS_HDMI_SYS_EN, CTS_HDMI_SYS_EN);
+
+ /* Set HDMI PLL rate */
+- meson_hdmi_pll_set(priv, params[freq].pll_base_freq,
+- params[freq].pll_od1,
+- params[freq].pll_od2,
+- params[freq].pll_od3);
++ if (!od1 && !od2 && !od3)
++ meson_hdmi_pll_generic_set(priv, pll_base_freq);
++ else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) {
++ switch (pll_base_freq) {
++ case 2970000:
++ meson_hdmi_pll_set_params(priv, 0x3d, 0xe00,
++ od1, od2, od3);
++ break;
++ case 4320000:
++ meson_hdmi_pll_set_params(priv, 0x5a, 0,
++ od1, od2, od3);
++ break;
++ case 5940000:
++ meson_hdmi_pll_set_params(priv, 0x7b, 0xc00,
++ od1, od2, od3);
++ break;
++ }
++ } else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") ||
++ meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) {
++ switch (pll_base_freq) {
++ case 2970000:
++ meson_hdmi_pll_set_params(priv, 0x7b, 0x300,
++ od1, od2, od3);
++ break;
++ case 4320000:
++ meson_hdmi_pll_set_params(priv, 0xb4, 0,
++ od1, od2, od3);
++ break;
++ case 5940000:
++ meson_hdmi_pll_set_params(priv, 0xf7, 0x200,
++ od1, od2, od3);
++ break;
++ }
++ }
+
+ /* Setup vid_pll divider */
+- meson_vid_pll_set(priv, params[freq].vid_pll_div);
++ meson_vid_pll_set(priv, vid_pll_div);
+
+ /* Set VCLK div */
+ regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL,
+ VCLK_SEL_MASK, 0);
+ regmap_update_bits(priv->hhi, HHI_VID_CLK_DIV,
+- VCLK_DIV_MASK, params[freq].vclk_div - 1);
++ VCLK_DIV_MASK, vclk_div - 1);
+
+ /* Set HDMI-TX source */
+ switch (hdmi_tx_div) {
+@@ -981,4 +821,80 @@ void meson_vclk_setup(struct meson_drm *priv, unsigned int target,
+
+ regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL, VCLK_EN, VCLK_EN);
+ }
++
++void meson_vclk_setup(struct meson_drm *priv, unsigned int target,
++ unsigned int vclk_freq, unsigned int venc_freq,
++ unsigned int dac_freq, bool hdmi_use_enci)
++{
++ unsigned int freq;
++ unsigned int hdmi_tx_div;
++ unsigned int venc_div;
++
++ if (target == MESON_VCLK_TARGET_CVBS) {
++ meson_venci_cvbs_clock_config(priv);
++ return;
++ } else if (target == MESON_VCLK_TARGET_DMT) {
++ /* The DMT clock path is fixed after the PLL:
++ * - automatic PLL freq + OD management
++ * - vid_pll_div = VID_PLL_DIV_5
++ * - vclk_div = 2
++ * - hdmi_tx_div = 1
++ * - venc_div = 1
++ * - encp encoder
++ */
++ meson_vclk_set(priv, vclk_freq * 10, 0, 0, 0,
++ VID_PLL_DIV_5, 2, 1, 1, false);
++ return;
++ }
++
++ hdmi_tx_div = vclk_freq / dac_freq;
++
++ if (hdmi_tx_div == 0) {
++ pr_err("Fatal Error, invalid HDMI-TX freq %d\n",
++ dac_freq);
++ return;
++ }
++
++ venc_div = vclk_freq / venc_freq;
++
++ if (venc_div == 0) {
++ pr_err("Fatal Error, invalid HDMI venc freq %d\n",
++ venc_freq);
++ return;
++ }
++
++ switch (vclk_freq) {
++ case 54000:
++ if (hdmi_use_enci)
++ freq = MESON_VCLK_HDMI_ENCI_54000;
++ else
++ freq = MESON_VCLK_HDMI_DDR_54000;
++ break;
++ case 74250:
++ freq = MESON_VCLK_HDMI_74250;
++ break;
++ case 148500:
++ if (dac_freq != 148500)
++ freq = MESON_VCLK_HDMI_DDR_148500;
++ else
++ freq = MESON_VCLK_HDMI_148500;
++ break;
++ case 297000:
++ freq = MESON_VCLK_HDMI_297000;
++ break;
++ case 594000:
++ freq = MESON_VCLK_HDMI_594000;
++ break;
++ default:
++ pr_err("Fatal Error, invalid HDMI vclk freq %d\n",
++ vclk_freq);
++ return;
++ }
++
++ meson_vclk_set(priv, params[freq].pll_base_freq,
++ params[freq].pll_od1, params[freq].pll_od2,
++ params[freq].pll_od3, params[freq].vid_pll_div,
++ params[freq].vclk_div, hdmi_tx_div, venc_div,
++ hdmi_use_enci);
++}
+ EXPORT_SYMBOL_GPL(meson_vclk_setup);
+diff --git a/drivers/gpu/drm/meson/meson_vclk.h b/drivers/gpu/drm/meson/meson_vclk.h
+index 0401b52..869fa3a 100644
+--- a/drivers/gpu/drm/meson/meson_vclk.h
++++ b/drivers/gpu/drm/meson/meson_vclk.h
+@@ -24,11 +24,15 @@
+ enum {
+ MESON_VCLK_TARGET_CVBS = 0,
+ MESON_VCLK_TARGET_HDMI = 1,
++ MESON_VCLK_TARGET_DMT = 2,
+ };
+
+ /* 27MHz is the CVBS Pixel Clock */
+ #define MESON_VCLK_CVBS 27000
+
++enum drm_mode_status
++meson_vclk_dmt_supported_freq(struct meson_drm *priv, unsigned int freq);
++
+ void meson_vclk_setup(struct meson_drm *priv, unsigned int target,
+ unsigned int vclk_freq, unsigned int venc_freq,
+ unsigned int dac_freq, bool hdmi_use_enci);
+diff --git a/drivers/gpu/drm/meson/meson_venc.c b/drivers/gpu/drm/meson/meson_venc.c
+index 6e27013..d9f33b0 100644
+--- a/drivers/gpu/drm/meson/meson_venc.c
++++ b/drivers/gpu/drm/meson/meson_venc.c
+@@ -697,314 +697,6 @@ union meson_hdmi_venc_mode meson_hdmi_encp_mode_1080p60 = {
+ },
+ };
+
+-union meson_hdmi_venc_mode meson_hdmi_encp_mode_640x480_60 = {
+- .encp = {
+- .dvi_settings = 0x21,
+- .video_mode = 0x4040,
+- .video_mode_adv = 0x18,
+- /* video_prog_mode */
+- /* video_sync_mode */
+- /* video_yc_dly */
+- /* video_rgb_ctrl */
+- /* video_filt_ctrl */
+- /* video_ofld_voav_ofst */
+- /* yfp1_htime */
+- /* yfp2_htime */
+- .max_pxcnt = 0x31f,
+- /* hspuls_begin */
+- /* hspuls_end */
+- /* hspuls_switch */
+- /* vspuls_begin */
+- /* vspuls_end */
+- /* vspuls_bline */
+- /* vspuls_eline */
+- .havon_begin = 0x90,
+- .havon_end = 0x30f,
+- .vavon_bline = 0x23,
+- .vavon_eline = 0x202,
+- /* eqpuls_begin */
+- /* eqpuls_end */
+- /* eqpuls_bline */
+- /* eqpuls_eline */
+- .hso_begin = 0,
+- .hso_end = 0x60,
+- .vso_begin = 0x1e,
+- .vso_end = 0x32,
+- .vso_bline = 0,
+- .vso_eline = 2,
+- .vso_eline_present = true,
+- /* sy_val */
+- /* sy2_val */
+- .max_lncnt = 0x20c,
+- },
+-};
+-
+-union meson_hdmi_venc_mode meson_hdmi_encp_mode_800x600_60 = {
+- .encp = {
+- .dvi_settings = 0x21,
+- .video_mode = 0x4040,
+- .video_mode_adv = 0x18,
+- /* video_prog_mode */
+- /* video_sync_mode */
+- /* video_yc_dly */
+- /* video_rgb_ctrl */
+- /* video_filt_ctrl */
+- /* video_ofld_voav_ofst */
+- /* yfp1_htime */
+- /* yfp2_htime */
+- .max_pxcnt = 0x41f,
+- /* hspuls_begin */
+- /* hspuls_end */
+- /* hspuls_switch */
+- /* vspuls_begin */
+- /* vspuls_end */
+- /* vspuls_bline */
+- /* vspuls_eline */
+- .havon_begin = 0xD8,
+- .havon_end = 0x3f7,
+- .vavon_bline = 0x1b,
+- .vavon_eline = 0x272,
+- /* eqpuls_begin */
+- /* eqpuls_end */
+- /* eqpuls_bline */
+- /* eqpuls_eline */
+- .hso_begin = 0,
+- .hso_end = 0x80,
+- .vso_begin = 0x1e,
+- .vso_end = 0x32,
+- .vso_bline = 0,
+- .vso_eline = 4,
+- .vso_eline_present = true,
+- /* sy_val */
+- /* sy2_val */
+- .max_lncnt = 0x273,
+- },
+-};
+-
+-union meson_hdmi_venc_mode meson_hdmi_encp_mode_1024x768_60 = {
+- .encp = {
+- .dvi_settings = 0x21,
+- .video_mode = 0x4040,
+- .video_mode_adv = 0x18,
+- /* video_prog_mode */
+- /* video_sync_mode */
+- /* video_yc_dly */
+- /* video_rgb_ctrl */
+- /* video_filt_ctrl */
+- /* video_ofld_voav_ofst */
+- /* yfp1_htime */
+- /* yfp2_htime */
+- .max_pxcnt = 1343,
+- /* hspuls_begin */
+- /* hspuls_end */
+- /* hspuls_switch */
+- /* vspuls_begin */
+- /* vspuls_end */
+- /* vspuls_bline */
+- /* vspuls_eline */
+- .havon_begin = 296,
+- .havon_end = 1319,
+- .vavon_bline = 35,
+- .vavon_eline = 802,
+- /* eqpuls_begin */
+- /* eqpuls_end */
+- /* eqpuls_bline */
+- /* eqpuls_eline */
+- .hso_begin = 0,
+- .hso_end = 136,
+- .vso_begin = 30,
+- .vso_end = 50,
+- .vso_bline = 0,
+- .vso_eline = 6,
+- .vso_eline_present = true,
+- /* sy_val */
+- /* sy2_val */
+- .max_lncnt = 805,
+- },
+-};
+-
+-union meson_hdmi_venc_mode meson_hdmi_encp_mode_1152x864_75 = {
+- .encp = {
+- .dvi_settings = 0x21,
+- .video_mode = 0x4040,
+- .video_mode_adv = 0x18,
+- /* video_prog_mode */
+- /* video_sync_mode */
+- /* video_yc_dly */
+- /* video_rgb_ctrl */
+- /* video_filt_ctrl */
+- /* video_ofld_voav_ofst */
+- /* yfp1_htime */
+- /* yfp2_htime */
+- .max_pxcnt = 0x63f,
+- /* hspuls_begin */
+- /* hspuls_end */
+- /* hspuls_switch */
+- /* vspuls_begin */
+- /* vspuls_end */
+- /* vspuls_bline */
+- /* vspuls_eline */
+- .havon_begin = 0x180,
+- .havon_end = 0x5ff,
+- .vavon_bline = 0x23,
+- .vavon_eline = 0x382,
+- /* eqpuls_begin */
+- /* eqpuls_end */
+- /* eqpuls_bline */
+- /* eqpuls_eline */
+- .hso_begin = 0,
+- .hso_end = 0x80,
+- .vso_begin = 0x1e,
+- .vso_end = 0x32,
+- .vso_bline = 0,
+- .vso_eline = 3,
+- .vso_eline_present = true,
+- /* sy_val */
+- /* sy2_val */
+- .max_lncnt = 0x383,
+- },
+-};
+-
+-union meson_hdmi_venc_mode meson_hdmi_encp_mode_1280x1024_60 = {
+- .encp = {
+- .dvi_settings = 0x21,
+- .video_mode = 0x4040,
+- .video_mode_adv = 0x18,
+- /* video_prog_mode */
+- /* video_sync_mode */
+- /* video_yc_dly */
+- /* video_rgb_ctrl */
+- /* video_filt_ctrl */
+- /* video_ofld_voav_ofst */
+- /* yfp1_htime */
+- /* yfp2_htime */
+- .max_pxcnt = 0x697,
+- /* hspuls_begin */
+- /* hspuls_end */
+- /* hspuls_switch */
+- /* vspuls_begin */
+- /* vspuls_end */
+- /* vspuls_bline */
+- /* vspuls_eline */
+- .havon_begin = 0x168,
+- .havon_end = 0x667,
+- .vavon_bline = 0x29,
+- .vavon_eline = 0x428,
+- /* eqpuls_begin */
+- /* eqpuls_end */
+- /* eqpuls_bline */
+- /* eqpuls_eline */
+- .hso_begin = 0,
+- .hso_end = 0x70,
+- .vso_begin = 0x1e,
+- .vso_end = 0x32,
+- .vso_bline = 0,
+- .vso_eline = 3,
+- .vso_eline_present = true,
+- /* sy_val */
+- /* sy2_val */
+- .max_lncnt = 0x429,
+- },
+-};
+-
+-union meson_hdmi_venc_mode meson_hdmi_encp_mode_1600x1200_60 = {
+- .encp = {
+- .dvi_settings = 0x21,
+- .video_mode = 0x4040,
+- .video_mode_adv = 0x18,
+- /* video_prog_mode */
+- /* video_sync_mode */
+- /* video_yc_dly */
+- /* video_rgb_ctrl */
+- /* video_filt_ctrl */
+- /* video_ofld_voav_ofst */
+- /* yfp1_htime */
+- /* yfp2_htime */
+- .max_pxcnt = 0x86f,
+- /* hspuls_begin */
+- /* hspuls_end */
+- /* hspuls_switch */
+- /* vspuls_begin */
+- /* vspuls_end */
+- /* vspuls_bline */
+- /* vspuls_eline */
+- .havon_begin = 0x1f0,
+- .havon_end = 0x82f,
+- .vavon_bline = 0x31,
+- .vavon_eline = 0x4e0,
+- /* eqpuls_begin */
+- /* eqpuls_end */
+- /* eqpuls_bline */
+- /* eqpuls_eline */
+- .hso_begin = 0,
+- .hso_end = 0xc0,
+- .vso_begin = 0x1e,
+- .vso_end = 0x32,
+- .vso_bline = 0,
+- .vso_eline = 3,
+- .vso_eline_present = true,
+- /* sy_val */
+- /* sy2_val */
+- .max_lncnt = 0x4e1,
+- },
+-};
+-
+-struct meson_hdmi_venc_dmt_mode {
+- struct drm_display_mode drm_mode;
+- union meson_hdmi_venc_mode *mode;
+-} meson_hdmi_venc_dmt_modes[] = {
+- /* 640x480@60Hz */
+- {
+- { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656,
+- 752, 800, 0, 480, 490, 492, 525, 0,
+- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
+- &meson_hdmi_encp_mode_640x480_60,
+- },
+- /* 800x600@60Hz */
+- {
+- { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840,
+- 968, 1056, 0, 600, 601, 605, 628, 0,
+- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+- &meson_hdmi_encp_mode_800x600_60,
+- },
+- /* 1024x768@60Hz */
+- {
+- { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024,
+- 1048, 1184, 1344, 0, 768, 771, 777, 806, 0,
+- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
+- &meson_hdmi_encp_mode_1024x768_60,
+- },
+- /* 1152x864@75Hz */
+- {
+- { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152,
+- 1216, 1344, 1600, 0, 864, 865, 868, 900, 0,
+- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+- &meson_hdmi_encp_mode_1152x864_75,
+- },
+- /* 1280x1024@60Hz */
+- {
+- { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 108000, 1280,
+- 1328, 1440, 1688, 0, 1024, 1025, 1028, 1066, 0,
+- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+- &meson_hdmi_encp_mode_1280x1024_60,
+- },
+- /* 1600x1200@60Hz */
+- {
+- { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 162000, 1600,
+- 1664, 1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
+- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+- &meson_hdmi_encp_mode_1600x1200_60,
+- },
+- /* 1920x1080@60Hz */
+- {
+- { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920,
+- 2008, 2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
+- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
+- &meson_hdmi_encp_mode_1080p60
+- },
+- { }, /* sentinel */
+-};
+-
+ struct meson_hdmi_venc_vic_mode {
+ unsigned int vic;
+ union meson_hdmi_venc_mode *mode;
+@@ -1044,17 +736,20 @@ static unsigned long modulo(unsigned long a, unsigned long b)
+ return a;
+ }
+
+-bool meson_venc_hdmi_supported_mode(const struct drm_display_mode *mode)
++enum drm_mode_status
++meson_venc_hdmi_supported_mode(const struct drm_display_mode *mode)
+ {
+- struct meson_hdmi_venc_dmt_mode *vmode = meson_hdmi_venc_dmt_modes;
++ if (mode->flags & ~(DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NHSYNC |
++ DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_NVSYNC))
++ return MODE_BAD;
+
+- while (vmode->mode) {
+- if (drm_mode_equal(&vmode->drm_mode, mode))
+- return true;
+- vmode++;
+- }
++ if (mode->hdisplay < 640 || mode->hdisplay > 1920)
++ return MODE_BAD_HVALUE;
+
+- return false;
++ if (mode->vdisplay < 480 || mode->vdisplay > 1200)
++ return MODE_BAD_VVALUE;
++
++ return MODE_OK;
+ }
+ EXPORT_SYMBOL_GPL(meson_venc_hdmi_supported_mode);
+
+@@ -1072,18 +767,29 @@ bool meson_venc_hdmi_supported_vic(int vic)
+ }
+ EXPORT_SYMBOL_GPL(meson_venc_hdmi_supported_vic);
+
+-static union meson_hdmi_venc_mode
+-*meson_venc_hdmi_get_dmt_vmode(const struct drm_display_mode *mode)
++void meson_venc_hdmi_get_dmt_vmode(const struct drm_display_mode *mode,
++ union meson_hdmi_venc_mode *dmt_mode)
+ {
+- struct meson_hdmi_venc_dmt_mode *vmode = meson_hdmi_venc_dmt_modes;
+-
+- while (vmode->mode) {
+- if (drm_mode_equal(&vmode->drm_mode, mode))
+- return vmode->mode;
+- vmode++;
+- }
+-
+- return NULL;
++ memset(dmt_mode, 0, sizeof(*dmt_mode));
++
++ dmt_mode->encp.dvi_settings = 0x21;
++ dmt_mode->encp.video_mode = 0x4040;
++ dmt_mode->encp.video_mode_adv = 0x18;
++ dmt_mode->encp.max_pxcnt = mode->htotal - 1;
++ dmt_mode->encp.havon_begin = mode->htotal - mode->hsync_start;
++ dmt_mode->encp.havon_end = dmt_mode->encp.havon_begin +
++ mode->hdisplay - 1;
++ dmt_mode->encp.vavon_bline = mode->vtotal - mode->vsync_start;
++ dmt_mode->encp.vavon_eline = dmt_mode->encp.vavon_bline +
++ mode->vdisplay - 1;
++ dmt_mode->encp.hso_begin = 0;
++ dmt_mode->encp.hso_end = mode->hsync_end - mode->hsync_start;
++ dmt_mode->encp.vso_begin = 30;
++ dmt_mode->encp.vso_end = 50;
++ dmt_mode->encp.vso_bline = 0;
++ dmt_mode->encp.vso_eline = mode->vsync_end - mode->vsync_start;
++ dmt_mode->encp.vso_eline_present = true;
++ dmt_mode->encp.max_lncnt = mode->vtotal - 1;
+ }
+
+ static union meson_hdmi_venc_mode *meson_venc_hdmi_get_vic_vmode(int vic)
+@@ -1120,6 +826,7 @@ void meson_venc_hdmi_mode_set(struct meson_drm *priv, int vic,
+ struct drm_display_mode *mode)
+ {
+ union meson_hdmi_venc_mode *vmode = NULL;
++ union meson_hdmi_venc_mode vmode_dmt;
+ bool use_enci = false;
+ bool venc_repeat = false;
+ bool hdmi_repeat = false;
+@@ -1147,14 +854,18 @@ void meson_venc_hdmi_mode_set(struct meson_drm *priv, int vic,
+ unsigned int sof_lines;
+ unsigned int vsync_lines;
+
+- if (meson_venc_hdmi_supported_vic(vic))
++ if (meson_venc_hdmi_supported_vic(vic)) {
+ vmode = meson_venc_hdmi_get_vic_vmode(vic);
+- else
+- vmode = meson_venc_hdmi_get_dmt_vmode(mode);
+- if (!vmode) {
+- dev_err(priv->dev, "%s: Fatal Error, unsupported mode "
+- DRM_MODE_FMT "\n", __func__, DRM_MODE_ARG(mode));
+- return;
++ if (!vmode) {
++ dev_err(priv->dev, "%s: Fatal Error, unsupported mode "
++ DRM_MODE_FMT "\n", __func__,
++ DRM_MODE_ARG(mode));
++ return;
++ }
++ }
++ else {
++ meson_venc_hdmi_get_dmt_vmode(mode, &vmode_dmt);
++ vmode = &vmode_dmt;
+ }
+
+ /* Use VENCI for 480i and 576i and double HDMI pixels */
+diff --git a/drivers/gpu/drm/meson/meson_venc.h b/drivers/gpu/drm/meson/meson_venc.h
+index 7c18a36..97eaebb 100644
+--- a/drivers/gpu/drm/meson/meson_venc.h
++++ b/drivers/gpu/drm/meson/meson_venc.h
+@@ -58,7 +58,8 @@ struct meson_cvbs_enci_mode {
+ };
+
+ /* HDMI Clock parameters */
+-bool meson_venc_hdmi_supported_mode(const struct drm_display_mode *mode);
++enum drm_mode_status
++meson_venc_hdmi_supported_mode(const struct drm_display_mode *mode);
+ bool meson_venc_hdmi_supported_vic(int vic);
+ bool meson_venc_hdmi_venc_repeat(int vic);
+
diff --git a/testing/linux-amlogic/0003-ARM64-defconfig-enable-CEC-support.patch b/testing/linux-amlogic/0003-ARM64-defconfig-enable-CEC-support.patch
new file mode 100644
index 0000000000..7c04717531
--- /dev/null
+++ b/testing/linux-amlogic/0003-ARM64-defconfig-enable-CEC-support.patch
@@ -0,0 +1,44 @@
+From e41c06328d1cd0989899d6a0897c6857d0cf9a4b Mon Sep 17 00:00:00 2001
+From: Jerome Brunet <jbrunet@baylibre.com>
+Date: Mon, 13 Nov 2017 12:09:40 +0100
+Subject: [PATCH] ARM64: defconfig: enable CEC support
+
+Turn on CONFIG_CEC_SUPPORT and CONFIG_CEC_PLATFORM_DRIVERS
+Turn on CONFIG_VIDEO_MESON_AO_CEC as module
+Turn on CONFIG_DRM_DW_HDMI_CEC as module
+
+Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
+Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
+---
+ arch/arm64/configs/defconfig | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
+index f9a186f..2584605 100644
+--- a/arch/arm64/configs/defconfig
++++ b/arch/arm64/configs/defconfig
+@@ -402,6 +402,7 @@ CONFIG_MEDIA_SUPPORT=m
+ CONFIG_MEDIA_CAMERA_SUPPORT=y
+ CONFIG_MEDIA_ANALOG_TV_SUPPORT=y
+ CONFIG_MEDIA_DIGITAL_TV_SUPPORT=y
++CONFIG_MEDIA_CEC_SUPPORT=y
+ CONFIG_MEDIA_CONTROLLER=y
+ CONFIG_VIDEO_V4L2_SUBDEV_API=y
+ # CONFIG_DVB_NET is not set
+@@ -411,6 +412,8 @@ CONFIG_VIDEO_SAMSUNG_S5P_MFC=m
+ CONFIG_VIDEO_SAMSUNG_EXYNOS_GSC=m
+ CONFIG_VIDEO_RENESAS_FCP=m
+ CONFIG_VIDEO_RENESAS_VSP1=m
++CONFIG_CEC_PLATFORM_DRIVERS=y
++CONFIG_VIDEO_MESON_AO_CEC=m
+ CONFIG_DRM=m
+ CONFIG_DRM_NOUVEAU=m
+ CONFIG_DRM_EXYNOS=m
+@@ -431,6 +434,7 @@ CONFIG_DRM_RCAR_LVDS=m
+ CONFIG_DRM_TEGRA=m
+ CONFIG_DRM_PANEL_SIMPLE=m
+ CONFIG_DRM_I2C_ADV7511=m
++CONFIG_DRM_DW_HDMI_CEC=m
+ CONFIG_DRM_VC4=m
+ CONFIG_DRM_HISI_HIBMC=m
+ CONFIG_DRM_HISI_KIRIN=m
diff --git a/testing/linux-amlogic/0004-clk-meson-switch-gxbb-cts-amclk-div-to-the-generic-d.patch b/testing/linux-amlogic/0004-clk-meson-switch-gxbb-cts-amclk-div-to-the-generic-d.patch
new file mode 100644
index 0000000000..e2b3860f5a
--- /dev/null
+++ b/testing/linux-amlogic/0004-clk-meson-switch-gxbb-cts-amclk-div-to-the-generic-d.patch
@@ -0,0 +1,44 @@
+From 0cecf963b9d815699fe50b7a7ee4a93ece3ed834 Mon Sep 17 00:00:00 2001
+From: Jerome Brunet <jbrunet@baylibre.com>
+Date: Tue, 19 Jun 2018 18:14:49 +0200
+Subject: [PATCH] clk: meson: switch gxbb cts-amclk div to the generic divider
+
+clk-audio-divider was a (poor) attempt to use CCF rate propagation
+while making sure the PLL rate would be high enough to work with
+audio use cases. The result is far from optimal. We can do better
+by carefully choosing the PLL rates for the audio use cases.
+Doing so, we don't need to use clk-audio-divider anymore. The
+generic will do
+
+Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
+---
+ drivers/clk/meson/gxbb.c | 12 +++++-------
+ 1 file changed, 5 insertions(+), 7 deletions(-)
+
+diff --git a/drivers/clk/meson/gxbb.c b/drivers/clk/meson/gxbb.c
+index 177fffb..69a58cb 100644
+--- a/drivers/clk/meson/gxbb.c
++++ b/drivers/clk/meson/gxbb.c
+@@ -982,17 +982,15 @@ static struct clk_regmap gxbb_cts_amclk_sel = {
+ };
+
+ static struct clk_regmap gxbb_cts_amclk_div = {
+- .data = &(struct meson_clk_audio_div_data){
+- .div = {
+- .reg_off = HHI_AUD_CLK_CNTL,
+- .shift = 0,
+- .width = 8,
+- },
++ .data = &(struct clk_regmap_div_data) {
++ .offset = HHI_AUD_CLK_CNTL,
++ .shift = 0,
++ .width = 8,
+ .flags = CLK_DIVIDER_ROUND_CLOSEST,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "cts_amclk_div",
+- .ops = &meson_clk_audio_divider_ops,
++ .ops = &clk_regmap_divider_ops,
+ .parent_names = (const char *[]){ "cts_amclk_sel" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
diff --git a/testing/linux-amlogic/0005-clk-meson-remove-unused-clk-audio-divider-driver.patch b/testing/linux-amlogic/0005-clk-meson-remove-unused-clk-audio-divider-driver.patch
new file mode 100644
index 0000000000..cd6da8cf57
--- /dev/null
+++ b/testing/linux-amlogic/0005-clk-meson-remove-unused-clk-audio-divider-driver.patch
@@ -0,0 +1,48 @@
+From e87dd7b11611e4ac5279ba4ad4f1fea4d0900273 Mon Sep 17 00:00:00 2001
+From: Jerome Brunet <jbrunet@baylibre.com>
+Date: Tue, 19 Jun 2018 18:23:28 +0200
+Subject: [PATCH] clk: meson: remove unused clk-audio-divider driver
+
+Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
+---
+ drivers/clk/meson/Makefile | 2 +-
+ drivers/clk/meson/clkc.h | 7 -------
+ 2 files changed, 1 insertion(+), 8 deletions(-)
+
+diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile
+index d0d13ae..e40fea9 100644
+--- a/drivers/clk/meson/Makefile
++++ b/drivers/clk/meson/Makefile
+@@ -2,7 +2,7 @@
+ # Makefile for Meson specific clk
+ #
+
+-obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-pll.o clk-mpll.o clk-audio-divider.o
++obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-pll.o clk-mpll.o
+ obj-$(CONFIG_COMMON_CLK_MESON_AO) += meson-aoclk.o
+ obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o
+ obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o gxbb-aoclk-32k.o
+diff --git a/drivers/clk/meson/clkc.h b/drivers/clk/meson/clkc.h
+index 2fb0843..48db024 100644
+--- a/drivers/clk/meson/clkc.h
++++ b/drivers/clk/meson/clkc.h
+@@ -91,11 +91,6 @@ struct meson_clk_mpll_data {
+
+ #define CLK_MESON_MPLL_ROUND_CLOSEST BIT(0)
+
+-struct meson_clk_audio_div_data {
+- struct parm div;
+- u8 flags;
+-};
+-
+ #define MESON_GATE(_name, _reg, _bit) \
+ struct clk_regmap _name = { \
+ .data = &(struct clk_regmap_gate_data){ \
+@@ -117,7 +112,5 @@ extern const struct clk_ops meson_clk_pll_ops;
+ extern const struct clk_ops meson_clk_cpu_ops;
+ extern const struct clk_ops meson_clk_mpll_ro_ops;
+ extern const struct clk_ops meson_clk_mpll_ops;
+-extern const struct clk_ops meson_clk_audio_divider_ro_ops;
+-extern const struct clk_ops meson_clk_audio_divider_ops;
+
+ #endif /* __CLKC_H */
diff --git a/testing/linux-amlogic/0006-ASoC-meson-add-meson-audio-core-driver.patch b/testing/linux-amlogic/0006-ASoC-meson-add-meson-audio-core-driver.patch
new file mode 100644
index 0000000000..69c0ab2995
--- /dev/null
+++ b/testing/linux-amlogic/0006-ASoC-meson-add-meson-audio-core-driver.patch
@@ -0,0 +1,308 @@
+From 878d41be386f0bcbe1475f65acd8b9fb304529a0 Mon Sep 17 00:00:00 2001
+From: Jerome Brunet <jbrunet@baylibre.com>
+Date: Thu, 30 Mar 2017 11:49:55 +0200
+Subject: [PATCH] ASoC: meson: add meson audio core driver
+
+This patch adds support for the audio core driver for the Amlogic Meson SoC
+family. The purpose of this driver is to properly reset the audio block and
+provide register access for the different devices scattered in this address
+space. This includes output and input DMAs, pcm, i2s and spdif dai, card
+level routing, internal codec for the gxl variant
+
+For more information, please refer to the section 5 of the public datasheet
+of the S905 (gxbb). This datasheet is available here: [0].
+
+[0]: http://dn.odroid.com/S905/DataSheet/S905_Public_Datasheet_V1.1.4.pdf
+
+Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
+---
+ sound/soc/Kconfig | 1 +
+ sound/soc/Makefile | 1 +
+ sound/soc/meson/Kconfig | 9 ++
+ sound/soc/meson/Makefile | 3 +
+ sound/soc/meson/audio-core.c | 190 +++++++++++++++++++++++++++++++++++++++++++
+ sound/soc/meson/audio-core.h | 28 +++++++
+ 6 files changed, 232 insertions(+)
+ create mode 100644 sound/soc/meson/Kconfig
+ create mode 100644 sound/soc/meson/Makefile
+ create mode 100644 sound/soc/meson/audio-core.c
+ create mode 100644 sound/soc/meson/audio-core.h
+
+diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
+index 41af6b9..1cf11cf 100644
+--- a/sound/soc/Kconfig
++++ b/sound/soc/Kconfig
+@@ -57,6 +57,7 @@ source "sound/soc/kirkwood/Kconfig"
+ source "sound/soc/img/Kconfig"
+ source "sound/soc/intel/Kconfig"
+ source "sound/soc/mediatek/Kconfig"
++source "sound/soc/meson/Kconfig"
+ source "sound/soc/mxs/Kconfig"
+ source "sound/soc/pxa/Kconfig"
+ source "sound/soc/qcom/Kconfig"
+diff --git a/sound/soc/Makefile b/sound/soc/Makefile
+index 06389a5..62a5f87 100644
+--- a/sound/soc/Makefile
++++ b/sound/soc/Makefile
+@@ -38,6 +38,7 @@ obj-$(CONFIG_SND_SOC) += jz4740/
+ obj-$(CONFIG_SND_SOC) += img/
+ obj-$(CONFIG_SND_SOC) += intel/
+ obj-$(CONFIG_SND_SOC) += mediatek/
++obj-$(CONFIG_SND_SOC) += meson/
+ obj-$(CONFIG_SND_SOC) += mxs/
+ obj-$(CONFIG_SND_SOC) += nuc900/
+ obj-$(CONFIG_SND_SOC) += omap/
+diff --git a/sound/soc/meson/Kconfig b/sound/soc/meson/Kconfig
+new file mode 100644
+index 0000000..ca0e3e9
+--- /dev/null
++++ b/sound/soc/meson/Kconfig
+@@ -0,0 +1,9 @@
++menuconfig SND_SOC_MESON
++ tristate "ASoC support for Amlogic Meson SoCs"
++ depends on ARCH_MESON
++ select MFD_CORE
++ select REGMAP_MMIO
++ help
++ Say Y or M if you want to add support for codecs attached to
++ the Amlogic Meson SoCs Audio interfaces. You will also need to
++ select the audio interfaces to support below.
+diff --git a/sound/soc/meson/Makefile b/sound/soc/meson/Makefile
+new file mode 100644
+index 0000000..22028ab
+--- /dev/null
++++ b/sound/soc/meson/Makefile
+@@ -0,0 +1,3 @@
++snd-soc-meson-audio-core-objs := audio-core.o
++
++obj-$(CONFIG_SND_SOC_MESON) += snd-soc-meson-audio-core.o
+diff --git a/sound/soc/meson/audio-core.c b/sound/soc/meson/audio-core.c
+new file mode 100644
+index 0000000..99993ec
+--- /dev/null
++++ b/sound/soc/meson/audio-core.c
+@@ -0,0 +1,190 @@
++/*
++ * Copyright (C) 2017 BayLibre, SAS
++ * Author: Jerome Brunet <jbrunet@baylibre.com>
++ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <linux/clk.h>
++#include <linux/mfd/core.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/regmap.h>
++#include <linux/reset.h>
++
++#include "audio-core.h"
++
++#define DRV_NAME "meson-audio-core"
++
++static const char * const acore_clock_names[] = { "aiu_top",
++ "aiu_glue",
++ "audin" };
++
++static int meson_acore_init_clocks(struct device *dev)
++{
++ struct clk *clock;
++ int i, ret;
++
++ for (i = 0; i < ARRAY_SIZE(acore_clock_names); i++) {
++ clock = devm_clk_get(dev, acore_clock_names[i]);
++ if (IS_ERR(clock)) {
++ if (PTR_ERR(clock) != -EPROBE_DEFER)
++ dev_err(dev, "Failed to get %s clock\n",
++ acore_clock_names[i]);
++ return PTR_ERR(clock);
++ }
++
++ ret = clk_prepare_enable(clock);
++ if (ret) {
++ dev_err(dev, "Failed to enable %s clock\n",
++ acore_clock_names[i]);
++ return ret;
++ }
++
++ ret = devm_add_action_or_reset(dev,
++ (void(*)(void *))clk_disable_unprepare,
++ clock);
++ if (ret)
++ return ret;
++ }
++
++ return 0;
++}
++
++static const char * const acore_reset_names[] = { "aiu",
++ "audin" };
++
++static int meson_acore_init_resets(struct device *dev)
++{
++ struct reset_control *reset;
++ int i, ret;
++
++ for (i = 0; i < ARRAY_SIZE(acore_reset_names); i++) {
++ reset = devm_reset_control_get_exclusive(dev,
++ acore_reset_names[i]);
++ if (IS_ERR(reset)) {
++ if (PTR_ERR(reset) != -EPROBE_DEFER)
++ dev_err(dev, "Failed to get %s reset\n",
++ acore_reset_names[i]);
++ return PTR_ERR(reset);
++ }
++
++ ret = reset_control_reset(reset);
++ if (ret) {
++ dev_err(dev, "Failed to pulse %s reset\n",
++ acore_reset_names[i]);
++ return ret;
++ }
++ }
++
++ return 0;
++}
++
++static const struct regmap_config meson_acore_regmap_config = {
++ .reg_bits = 32,
++ .val_bits = 32,
++ .reg_stride = 4,
++};
++
++static const struct mfd_cell meson_acore_devs[] = {
++ {
++ .name = "meson-i2s-dai",
++ .of_compatible = "amlogic,meson-i2s-dai",
++ },
++ {
++ .name = "meson-spdif-dai",
++ .of_compatible = "amlogic,meson-spdif-dai",
++ },
++ {
++ .name = "meson-aiu-i2s-dma",
++ .of_compatible = "amlogic,meson-aiu-i2s-dma",
++ },
++ {
++ .name = "meson-aiu-spdif-dma",
++ .of_compatible = "amlogic,meson-aiu-spdif-dma",
++ },
++};
++
++static int meson_acore_probe(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct meson_audio_core_data *data;
++ struct resource *res;
++ void __iomem *regs;
++ int ret;
++
++ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
++ if (!data)
++ return -ENOMEM;
++ platform_set_drvdata(pdev, data);
++
++ ret = meson_acore_init_clocks(dev);
++ if (ret)
++ return ret;
++
++ ret = meson_acore_init_resets(dev);
++ if (ret)
++ return ret;
++
++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "aiu");
++ regs = devm_ioremap_resource(dev, res);
++ if (IS_ERR(regs))
++ return PTR_ERR(regs);
++
++ data->aiu = devm_regmap_init_mmio(dev, regs,
++ &meson_acore_regmap_config);
++ if (IS_ERR(data->aiu)) {
++ dev_err(dev, "Couldn't create the AIU regmap\n");
++ return PTR_ERR(data->aiu);
++ }
++
++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "audin");
++ regs = devm_ioremap_resource(dev, res);
++ if (IS_ERR(regs))
++ return PTR_ERR(regs);
++
++ data->audin = devm_regmap_init_mmio(dev, regs,
++ &meson_acore_regmap_config);
++ if (IS_ERR(data->audin)) {
++ dev_err(dev, "Couldn't create the AUDIN regmap\n");
++ return PTR_ERR(data->audin);
++ }
++
++ return devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, meson_acore_devs,
++ ARRAY_SIZE(meson_acore_devs), NULL, 0,
++ NULL);
++}
++
++static const struct of_device_id meson_acore_of_match[] = {
++ { .compatible = "amlogic,meson-audio-core", },
++ { .compatible = "amlogic,meson-gxbb-audio-core", },
++ { .compatible = "amlogic,meson-gxl-audio-core", },
++ {}
++};
++MODULE_DEVICE_TABLE(of, meson_acore_of_match);
++
++static struct platform_driver meson_acore_pdrv = {
++ .probe = meson_acore_probe,
++ .driver = {
++ .name = DRV_NAME,
++ .of_match_table = meson_acore_of_match,
++ },
++};
++module_platform_driver(meson_acore_pdrv);
++
++MODULE_DESCRIPTION("Meson Audio Core Driver");
++MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
++MODULE_LICENSE("GPL v2");
+diff --git a/sound/soc/meson/audio-core.h b/sound/soc/meson/audio-core.h
+new file mode 100644
+index 0000000..6e7a24c
+--- /dev/null
++++ b/sound/soc/meson/audio-core.h
+@@ -0,0 +1,28 @@
++/*
++ * Copyright (C) 2017 BayLibre, SAS
++ * Author: Jerome Brunet <jbrunet@baylibre.com>
++ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, see <http://www.gnu.org/licenses/>.
++ */
++
++#ifndef _MESON_AUDIO_CORE_H_
++#define _MESON_AUDIO_CORE_H_
++
++struct meson_audio_core_data {
++ struct regmap *aiu;
++ struct regmap *audin;
++};
++
++#endif /* _MESON_AUDIO_CORE_H_ */
diff --git a/testing/linux-amlogic/0007-ASoC-meson-add-register-definitions.patch b/testing/linux-amlogic/0007-ASoC-meson-add-register-definitions.patch
new file mode 100644
index 0000000000..0a13a427cc
--- /dev/null
+++ b/testing/linux-amlogic/0007-ASoC-meson-add-register-definitions.patch
@@ -0,0 +1,357 @@
+From f6c0ce626b08f5ba85bd9bfe000044c4f9e7da24 Mon Sep 17 00:00:00 2001
+From: Jerome Brunet <jbrunet@baylibre.com>
+Date: Thu, 30 Mar 2017 12:00:10 +0200
+Subject: [PATCH] ASoC: meson: add register definitions
+
+Add the register definition for the AIU and AUDIN blocks
+
+Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
+---
+ sound/soc/meson/aiu-regs.h | 182 +++++++++++++++++++++++++++++++++++++++++++
+ sound/soc/meson/audin-regs.h | 148 +++++++++++++++++++++++++++++++++++
+ 2 files changed, 330 insertions(+)
+ create mode 100644 sound/soc/meson/aiu-regs.h
+ create mode 100644 sound/soc/meson/audin-regs.h
+
+diff --git a/sound/soc/meson/aiu-regs.h b/sound/soc/meson/aiu-regs.h
+new file mode 100644
+index 0000000..67391e6
+--- /dev/null
++++ b/sound/soc/meson/aiu-regs.h
+@@ -0,0 +1,182 @@
++/*
++ * Copyright (C) 2017 BayLibre, SAS
++ * Author: Jerome Brunet <jbrunet@baylibre.com>
++ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, see <http://www.gnu.org/licenses/>.
++ */
++
++#ifndef _AIU_REGS_H_
++#define _AIU_REGS_H_
++
++#define AIU_958_BPF 0x000
++#define AIU_958_BRST 0x004
++#define AIU_958_LENGTH 0x008
++#define AIU_958_PADDSIZE 0x00C
++#define AIU_958_MISC 0x010
++#define AIU_958_FORCE_LEFT 0x014 /* Unknown */
++#define AIU_958_DISCARD_NUM 0x018
++#define AIU_958_DCU_FF_CTRL 0x01C
++#define AIU_958_CHSTAT_L0 0x020
++#define AIU_958_CHSTAT_L1 0x024
++#define AIU_958_CTRL 0x028
++#define AIU_958_RPT 0x02C
++#define AIU_I2S_MUTE_SWAP 0x030
++#define AIU_I2S_SOURCE_DESC 0x034
++#define AIU_I2S_MED_CTRL 0x038
++#define AIU_I2S_MED_THRESH 0x03C
++#define AIU_I2S_DAC_CFG 0x040
++#define AIU_I2S_SYNC 0x044 /* Unknown */
++#define AIU_I2S_MISC 0x048
++#define AIU_I2S_OUT_CFG 0x04C
++#define AIU_I2S_FF_CTRL 0x050 /* Unknown */
++#define AIU_RST_SOFT 0x054
++#define AIU_CLK_CTRL 0x058
++#define AIU_MIX_ADCCFG 0x05C
++#define AIU_MIX_CTRL 0x060
++#define AIU_CLK_CTRL_MORE 0x064
++#define AIU_958_POP 0x068
++#define AIU_MIX_GAIN 0x06C
++#define AIU_958_SYNWORD1 0x070
++#define AIU_958_SYNWORD2 0x074
++#define AIU_958_SYNWORD3 0x078
++#define AIU_958_SYNWORD1_MASK 0x07C
++#define AIU_958_SYNWORD2_MASK 0x080
++#define AIU_958_SYNWORD3_MASK 0x084
++#define AIU_958_FFRDOUT_THD 0x088
++#define AIU_958_LENGTH_PER_PAUSE 0x08C
++#define AIU_958_PAUSE_NUM 0x090
++#define AIU_958_PAUSE_PAYLOAD 0x094
++#define AIU_958_AUTO_PAUSE 0x098
++#define AIU_958_PAUSE_PD_LENGTH 0x09C
++#define AIU_CODEC_DAC_LRCLK_CTRL 0x0A0
++#define AIU_CODEC_ADC_LRCLK_CTRL 0x0A4
++#define AIU_HDMI_CLK_DATA_CTRL 0x0A8
++#define AIU_CODEC_CLK_DATA_CTRL 0x0AC
++#define AIU_ACODEC_CTRL 0x0B0
++#define AIU_958_CHSTAT_R0 0x0C0
++#define AIU_958_CHSTAT_R1 0x0C4
++#define AIU_958_VALID_CTRL 0x0C8
++#define AIU_AUDIO_AMP_REG0 0x0F0 /* Unknown */
++#define AIU_AUDIO_AMP_REG1 0x0F4 /* Unknown */
++#define AIU_AUDIO_AMP_REG2 0x0F8 /* Unknown */
++#define AIU_AUDIO_AMP_REG3 0x0FC /* Unknown */
++#define AIU_AIFIFO2_CTRL 0x100
++#define AIU_AIFIFO2_STATUS 0x104
++#define AIU_AIFIFO2_GBIT 0x108
++#define AIU_AIFIFO2_CLB 0x10C
++#define AIU_CRC_CTRL 0x110
++#define AIU_CRC_STATUS 0x114
++#define AIU_CRC_SHIFT_REG 0x118
++#define AIU_CRC_IREG 0x11C
++#define AIU_CRC_CAL_REG1 0x120
++#define AIU_CRC_CAL_REG0 0x124
++#define AIU_CRC_POLY_COEF1 0x128
++#define AIU_CRC_POLY_COEF0 0x12C
++#define AIU_CRC_BIT_SIZE1 0x130
++#define AIU_CRC_BIT_SIZE0 0x134
++#define AIU_CRC_BIT_CNT1 0x138
++#define AIU_CRC_BIT_CNT0 0x13C
++#define AIU_AMCLK_GATE_HI 0x140
++#define AIU_AMCLK_GATE_LO 0x144
++#define AIU_AMCLK_MSR 0x148
++#define AIU_AUDAC_CTRL0 0x14C /* Unknown */
++#define AIU_DELTA_SIGMA0 0x154 /* Unknown */
++#define AIU_DELTA_SIGMA1 0x158 /* Unknown */
++#define AIU_DELTA_SIGMA2 0x15C /* Unknown */
++#define AIU_DELTA_SIGMA3 0x160 /* Unknown */
++#define AIU_DELTA_SIGMA4 0x164 /* Unknown */
++#define AIU_DELTA_SIGMA5 0x168 /* Unknown */
++#define AIU_DELTA_SIGMA6 0x16C /* Unknown */
++#define AIU_DELTA_SIGMA7 0x170 /* Unknown */
++#define AIU_DELTA_SIGMA_LCNTS 0x174 /* Unknown */
++#define AIU_DELTA_SIGMA_RCNTS 0x178 /* Unknown */
++#define AIU_MEM_I2S_START_PTR 0x180
++#define AIU_MEM_I2S_RD_PTR 0x184
++#define AIU_MEM_I2S_END_PTR 0x188
++#define AIU_MEM_I2S_MASKS 0x18C
++#define AIU_MEM_I2S_CONTROL 0x190
++#define AIU_MEM_IEC958_START_PTR 0x194
++#define AIU_MEM_IEC958_RD_PTR 0x198
++#define AIU_MEM_IEC958_END_PTR 0x19C
++#define AIU_MEM_IEC958_MASKS 0x1A0
++#define AIU_MEM_IEC958_CONTROL 0x1A4
++#define AIU_MEM_AIFIFO2_START_PTR 0x1A8
++#define AIU_MEM_AIFIFO2_CURR_PTR 0x1AC
++#define AIU_MEM_AIFIFO2_END_PTR 0x1B0
++#define AIU_MEM_AIFIFO2_BYTES_AVAIL 0x1B4
++#define AIU_MEM_AIFIFO2_CONTROL 0x1B8
++#define AIU_MEM_AIFIFO2_MAN_WP 0x1BC
++#define AIU_MEM_AIFIFO2_MAN_RP 0x1C0
++#define AIU_MEM_AIFIFO2_LEVEL 0x1C4
++#define AIU_MEM_AIFIFO2_BUF_CNTL 0x1C8
++#define AIU_MEM_I2S_MAN_WP 0x1CC
++#define AIU_MEM_I2S_MAN_RP 0x1D0
++#define AIU_MEM_I2S_LEVEL 0x1D4
++#define AIU_MEM_I2S_BUF_CNTL 0x1D8
++#define AIU_MEM_I2S_BUF_WRAP_COUNT 0x1DC
++#define AIU_MEM_I2S_MEM_CTL 0x1E0
++#define AIU_MEM_IEC958_MEM_CTL 0x1E4
++#define AIU_MEM_IEC958_WRAP_COUNT 0x1E8
++#define AIU_MEM_IEC958_IRQ_LEVEL 0x1EC
++#define AIU_MEM_IEC958_MAN_WP 0x1F0
++#define AIU_MEM_IEC958_MAN_RP 0x1F4
++#define AIU_MEM_IEC958_LEVEL 0x1F8
++#define AIU_MEM_IEC958_BUF_CNTL 0x1FC
++#define AIU_AIFIFO_CTRL 0x200
++#define AIU_AIFIFO_STATUS 0x204
++#define AIU_AIFIFO_GBIT 0x208
++#define AIU_AIFIFO_CLB 0x20C
++#define AIU_MEM_AIFIFO_START_PTR 0x210
++#define AIU_MEM_AIFIFO_CURR_PTR 0x214
++#define AIU_MEM_AIFIFO_END_PTR 0x218
++#define AIU_MEM_AIFIFO_BYTES_AVAIL 0x21C
++#define AIU_MEM_AIFIFO_CONTROL 0x220
++#define AIU_MEM_AIFIFO_MAN_WP 0x224
++#define AIU_MEM_AIFIFO_MAN_RP 0x228
++#define AIU_MEM_AIFIFO_LEVEL 0x22C
++#define AIU_MEM_AIFIFO_BUF_CNTL 0x230
++#define AIU_MEM_AIFIFO_BUF_WRAP_COUNT 0x234
++#define AIU_MEM_AIFIFO2_BUF_WRAP_COUNT 0x238
++#define AIU_MEM_AIFIFO_MEM_CTL 0x23C
++#define AIFIFO_TIME_STAMP_CNTL 0x240
++#define AIFIFO_TIME_STAMP_SYNC_0 0x244
++#define AIFIFO_TIME_STAMP_SYNC_1 0x248
++#define AIFIFO_TIME_STAMP_0 0x24C
++#define AIFIFO_TIME_STAMP_1 0x250
++#define AIFIFO_TIME_STAMP_2 0x254
++#define AIFIFO_TIME_STAMP_3 0x258
++#define AIFIFO_TIME_STAMP_LENGTH 0x25C
++#define AIFIFO2_TIME_STAMP_CNTL 0x260
++#define AIFIFO2_TIME_STAMP_SYNC_0 0x264
++#define AIFIFO2_TIME_STAMP_SYNC_1 0x268
++#define AIFIFO2_TIME_STAMP_0 0x26C
++#define AIFIFO2_TIME_STAMP_1 0x270
++#define AIFIFO2_TIME_STAMP_2 0x274
++#define AIFIFO2_TIME_STAMP_3 0x278
++#define AIFIFO2_TIME_STAMP_LENGTH 0x27C
++#define IEC958_TIME_STAMP_CNTL 0x280
++#define IEC958_TIME_STAMP_SYNC_0 0x284
++#define IEC958_TIME_STAMP_SYNC_1 0x288
++#define IEC958_TIME_STAMP_0 0x28C
++#define IEC958_TIME_STAMP_1 0x290
++#define IEC958_TIME_STAMP_2 0x294
++#define IEC958_TIME_STAMP_3 0x298
++#define IEC958_TIME_STAMP_LENGTH 0x29C
++#define AIU_MEM_AIFIFO2_MEM_CTL 0x2A0
++#define AIU_I2S_CBUS_DDR_CNTL 0x2A4
++#define AIU_I2S_CBUS_DDR_WDATA 0x2A8
++#define AIU_I2S_CBUS_DDR_ADDR 0x2AC
++
++#endif /* _AIU_REGS_H_ */
+diff --git a/sound/soc/meson/audin-regs.h b/sound/soc/meson/audin-regs.h
+new file mode 100644
+index 0000000..f224610
+--- /dev/null
++++ b/sound/soc/meson/audin-regs.h
+@@ -0,0 +1,148 @@
++/*
++ * Copyright (C) 2017 BayLibre, SAS
++ * Author: Jerome Brunet <jbrunet@baylibre.com>
++ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, see <http://www.gnu.org/licenses/>.
++ */
++
++#ifndef _AUDIN_REGS_H_
++#define _AUDIN_REGS_H_
++
++/*
++ * Note :
++ * Datasheet issue page 196
++ * AUDIN_MUTE_VAL 0x35 => impossible: Already assigned to AUDIN_FIFO1_PTR
++ * AUDIN_FIFO1_PTR is more likely to be correct here since surrounding registers
++ * also deal with AUDIN_FIFO1
++ *
++ * Clarification needed from Amlogic
++ */
++
++#define AUDIN_SPDIF_MODE 0x000
++#define AUDIN_SPDIF_FS_CLK_RLTN 0x004
++#define AUDIN_SPDIF_CHNL_STS_A 0x008
++#define AUDIN_SPDIF_CHNL_STS_B 0x00C
++#define AUDIN_SPDIF_MISC 0x010
++#define AUDIN_SPDIF_NPCM_PCPD 0x014
++#define AUDIN_SPDIF_END 0x03C /* Unknown */
++#define AUDIN_I2SIN_CTRL 0x040
++#define AUDIN_SOURCE_SEL 0x044
++#define AUDIN_DECODE_FORMAT 0x048
++#define AUDIN_DECODE_CONTROL_STATUS 0x04C
++#define AUDIN_DECODE_CHANNEL_STATUS_A_0 0x050
++#define AUDIN_DECODE_CHANNEL_STATUS_A_1 0x054
++#define AUDIN_DECODE_CHANNEL_STATUS_A_2 0x058
++#define AUDIN_DECODE_CHANNEL_STATUS_A_3 0x05C
++#define AUDIN_DECODE_CHANNEL_STATUS_A_4 0x060
++#define AUDIN_DECODE_CHANNEL_STATUS_A_5 0x064
++#define AUDIN_FIFO0_START 0x080
++#define AUDIN_FIFO0_END 0x084
++#define AUDIN_FIFO0_PTR 0x088
++#define AUDIN_FIFO0_INTR 0x08C
++#define AUDIN_FIFO0_RDPTR 0x090
++#define AUDIN_FIFO0_CTRL 0x094
++#define AUDIN_FIFO0_CTRL1 0x098
++#define AUDIN_FIFO0_LVL0 0x09C
++#define AUDIN_FIFO0_LVL1 0x0A0
++#define AUDIN_FIFO0_LVL2 0x0A4
++#define AUDIN_FIFO0_REQID 0x0C0
++#define AUDIN_FIFO0_WRAP 0x0C4
++#define AUDIN_FIFO1_START 0x0CC
++#define AUDIN_FIFO1_END 0x0D0
++#define AUDIN_FIFO1_PTR 0x0D4
++#define AUDIN_FIFO1_INTR 0x0D8
++#define AUDIN_FIFO1_RDPTR 0x0DC
++#define AUDIN_FIFO1_CTRL 0x0E0
++#define AUDIN_FIFO1_CTRL1 0x0E4
++#define AUDIN_FIFO1_LVL0 0x100
++#define AUDIN_FIFO1_LVL1 0x104
++#define AUDIN_FIFO1_LVL2 0x108
++#define AUDIN_FIFO1_REQID 0x10C
++#define AUDIN_FIFO1_WRAP 0x110
++#define AUDIN_FIFO2_START 0x114
++#define AUDIN_FIFO2_END 0x118
++#define AUDIN_FIFO2_PTR 0x11C
++#define AUDIN_FIFO2_INTR 0x120
++#define AUDIN_FIFO2_RDPTR 0x124
++#define AUDIN_FIFO2_CTRL 0x128
++#define AUDIN_FIFO2_CTRL1 0x12C
++#define AUDIN_FIFO2_LVL0 0x130
++#define AUDIN_FIFO2_LVL1 0x134
++#define AUDIN_FIFO2_LVL2 0x138
++#define AUDIN_FIFO2_REQID 0x13C
++#define AUDIN_FIFO2_WRAP 0x140
++#define AUDIN_INT_CTRL 0x144
++#define AUDIN_FIFO_INT 0x148
++#define PCMIN_CTRL0 0x180
++#define PCMIN_CTRL1 0x184
++#define PCMIN1_CTRL0 0x188
++#define PCMIN1_CTRL1 0x18C
++#define PCMOUT_CTRL0 0x1C0
++#define PCMOUT_CTRL1 0x1C4
++#define PCMOUT_CTRL2 0x1C8
++#define PCMOUT_CTRL3 0x1CC
++#define PCMOUT1_CTRL0 0x1D0
++#define PCMOUT1_CTRL1 0x1D4
++#define PCMOUT1_CTRL2 0x1D8
++#define PCMOUT1_CTRL3 0x1DC
++#define AUDOUT_CTRL 0x200
++#define AUDOUT_CTRL1 0x204
++#define AUDOUT_BUF0_STA 0x208
++#define AUDOUT_BUF0_EDA 0x20C
++#define AUDOUT_BUF0_WPTR 0x210
++#define AUDOUT_BUF1_STA 0x214
++#define AUDOUT_BUF1_EDA 0x218
++#define AUDOUT_BUF1_WPTR 0x21C
++#define AUDOUT_FIFO_RPTR 0x220
++#define AUDOUT_INTR_PTR 0x224
++#define AUDOUT_FIFO_STS 0x228
++#define AUDOUT1_CTRL 0x240
++#define AUDOUT1_CTRL1 0x244
++#define AUDOUT1_BUF0_STA 0x248
++#define AUDOUT1_BUF0_EDA 0x24C
++#define AUDOUT1_BUF0_WPTR 0x250
++#define AUDOUT1_BUF1_STA 0x254
++#define AUDOUT1_BUF1_EDA 0x258
++#define AUDOUT1_BUF1_WPTR 0x25C
++#define AUDOUT1_FIFO_RPTR 0x260
++#define AUDOUT1_INTR_PTR 0x264
++#define AUDOUT1_FIFO_STS 0x268
++#define AUDIN_HDMI_MEAS_CTRL 0x280
++#define AUDIN_HDMI_MEAS_CYCLES_M1 0x284
++#define AUDIN_HDMI_MEAS_INTR_MASKN 0x288
++#define AUDIN_HDMI_MEAS_INTR_STAT 0x28C
++#define AUDIN_HDMI_REF_CYCLES_STAT_0 0x290
++#define AUDIN_HDMI_REF_CYCLES_STAT_1 0x294
++#define AUDIN_HDMIRX_AFIFO_STAT 0x298
++#define AUDIN_FIFO0_PIO_STS 0x2C0
++#define AUDIN_FIFO0_PIO_RDL 0x2C4
++#define AUDIN_FIFO0_PIO_RDH 0x2C8
++#define AUDIN_FIFO1_PIO_STS 0x2CC
++#define AUDIN_FIFO1_PIO_RDL 0x2D0
++#define AUDIN_FIFO1_PIO_RDH 0x2D4
++#define AUDIN_FIFO2_PIO_STS 0x2D8
++#define AUDIN_FIFO2_PIO_RDL 0x2DC
++#define AUDIN_FIFO2_PIO_RDH 0x2E0
++#define AUDOUT_FIFO_PIO_STS 0x2E4
++#define AUDOUT_FIFO_PIO_WRL 0x2E8
++#define AUDOUT_FIFO_PIO_WRH 0x2EC
++#define AUDOUT1_FIFO_PIO_STS 0x2F0 /* Unknown */
++#define AUDOUT1_FIFO_PIO_WRL 0x2F4 /* Unknown */
++#define AUDOUT1_FIFO_PIO_WRH 0x2F8 /* Unknown */
++#define AUD_RESAMPLE_CTRL0 0x2FC
++#define AUD_RESAMPLE_CTRL1 0x300
++#define AUD_RESAMPLE_STATUS 0x304
++
++#endif /* _AUDIN_REGS_H_ */
diff --git a/testing/linux-amlogic/0008-ASoC-meson-add-aiu-i2s-dma-support.patch b/testing/linux-amlogic/0008-ASoC-meson-add-aiu-i2s-dma-support.patch
new file mode 100644
index 0000000000..1de0e444ad
--- /dev/null
+++ b/testing/linux-amlogic/0008-ASoC-meson-add-aiu-i2s-dma-support.patch
@@ -0,0 +1,416 @@
+From bb5102086db1579c1289440fa8aa184a70cb7c64 Mon Sep 17 00:00:00 2001
+From: Jerome Brunet <jbrunet@baylibre.com>
+Date: Thu, 30 Mar 2017 12:14:40 +0200
+Subject: [PATCH] ASoC: meson: add aiu i2s dma support
+
+Add support for the i2s output dma which is part of the AIU block
+
+Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
+---
+ sound/soc/meson/Kconfig | 7 +
+ sound/soc/meson/Makefile | 2 +
+ sound/soc/meson/aiu-i2s-dma.c | 370 ++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 379 insertions(+)
+ create mode 100644 sound/soc/meson/aiu-i2s-dma.c
+
+diff --git a/sound/soc/meson/Kconfig b/sound/soc/meson/Kconfig
+index ca0e3e9..88fbfc2 100644
+--- a/sound/soc/meson/Kconfig
++++ b/sound/soc/meson/Kconfig
+@@ -7,3 +7,10 @@ menuconfig SND_SOC_MESON
+ Say Y or M if you want to add support for codecs attached to
+ the Amlogic Meson SoCs Audio interfaces. You will also need to
+ select the audio interfaces to support below.
++
++config SND_SOC_MESON_I2S
++ tristate "Meson i2s interface"
++ depends on SND_SOC_MESON
++ help
++ Say Y or M if you want to add support for i2s dma driver for Amlogic
++ Meson SoCs.
+diff --git a/sound/soc/meson/Makefile b/sound/soc/meson/Makefile
+index 22028ab..273f275 100644
+--- a/sound/soc/meson/Makefile
++++ b/sound/soc/meson/Makefile
+@@ -1,3 +1,5 @@
+ snd-soc-meson-audio-core-objs := audio-core.o
++snd-soc-meson-aiu-i2s-dma-objs := aiu-i2s-dma.o
+
+ obj-$(CONFIG_SND_SOC_MESON) += snd-soc-meson-audio-core.o
++obj-$(CONFIG_SND_SOC_MESON_I2S) += snd-soc-meson-aiu-i2s-dma.o
+diff --git a/sound/soc/meson/aiu-i2s-dma.c b/sound/soc/meson/aiu-i2s-dma.c
+new file mode 100644
+index 0000000..2684bd0
+--- /dev/null
++++ b/sound/soc/meson/aiu-i2s-dma.c
+@@ -0,0 +1,370 @@
++/*
++ * Copyright (C) 2017 BayLibre, SAS
++ * Author: Jerome Brunet <jbrunet@baylibre.com>
++ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <linux/clk.h>
++#include <linux/mfd/syscon.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/regmap.h>
++
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++
++#include "aiu-regs.h"
++#include "audio-core.h"
++
++#define DRV_NAME "meson-aiu-i2s-dma"
++
++struct aiu_i2s_dma {
++ struct meson_audio_core_data *core;
++ struct clk *fast;
++ int irq;
++};
++
++#define AIU_MEM_I2S_BUF_CNTL_INIT BIT(0)
++#define AIU_MEM_I2S_CONTROL_INIT BIT(0)
++#define AIU_MEM_I2S_CONTROL_FILL_EN BIT(1)
++#define AIU_MEM_I2S_CONTROL_EMPTY_EN BIT(2)
++#define AIU_MEM_I2S_CONTROL_MODE_16BIT BIT(6)
++#define AIU_MEM_I2S_CONTROL_BUSY BIT(7)
++#define AIU_MEM_I2S_CONTROL_DATA_READY BIT(8)
++#define AIU_MEM_I2S_CONTROL_LEVEL_CNTL BIT(9)
++#define AIU_MEM_I2S_MASKS_IRQ_BLOCK_MASK GENMASK(31, 16)
++#define AIU_MEM_I2S_MASKS_IRQ_BLOCK(n) ((n) << 16)
++#define AIU_MEM_I2S_MASKS_CH_MEM_MASK GENMASK(15, 8)
++#define AIU_MEM_I2S_MASKS_CH_MEM(ch) ((ch) << 8)
++#define AIU_MEM_I2S_MASKS_CH_RD_MASK GENMASK(7, 0)
++#define AIU_MEM_I2S_MASKS_CH_RD(ch) ((ch) << 0)
++#define AIU_RST_SOFT_I2S_FAST_DOMAIN BIT(0)
++#define AIU_RST_SOFT_I2S_SLOW_DOMAIN BIT(1)
++
++/*
++ * The DMA works by i2s "blocks" (or DMA burst). The burst size and the memory
++ * layout expected depends on the mode of operation.
++ *
++ * - Normal mode: The channels are expected to be packed in 32 bytes groups
++ * interleaved the buffer. AIU_MEM_I2S_MASKS_CH_MEM is a bitfield representing
++ * the channels present in memory. AIU_MEM_I2S_MASKS_CH_MEM represents the
++ * channels read by the DMA. This is very flexible but the unsual memory layout
++ * makes it less easy to deal with. The burst size is 32 bytes times the number
++ * of channels read.
++ *
++ * - Split mode:
++ * Classical channel interleaved frame organisation. In this mode,
++ * AIU_MEM_I2S_MASKS_CH_MEM and AIU_MEM_I2S_MASKS_CH_MEM must be set to 0xff and
++ * the burst size is fixed to 256 bytes. The input can be either 2 or 8
++ * channels.
++ *
++ * The following driver implements the split mode.
++ */
++
++#define AIU_I2S_DMA_BURST 256
++
++static struct snd_pcm_hardware aiu_i2s_dma_hw = {
++ .info = (SNDRV_PCM_INFO_INTERLEAVED |
++ SNDRV_PCM_INFO_MMAP |
++ SNDRV_PCM_INFO_MMAP_VALID |
++ SNDRV_PCM_INFO_PAUSE),
++
++ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
++ SNDRV_PCM_FMTBIT_S24_LE |
++ SNDRV_PCM_FMTBIT_S32_LE),
++
++ /*
++ * TODO: The DMA can change the endianness, the msb position
++ * and deal with unsigned - support this later on
++ */
++
++ .rate_min = 8000,
++ .rate_max = 192000,
++ .channels_min = 2,
++ .channels_max = 8,
++ .period_bytes_min = AIU_I2S_DMA_BURST,
++ .period_bytes_max = AIU_I2S_DMA_BURST * 65535,
++ .periods_min = 2,
++ .periods_max = UINT_MAX,
++ .buffer_bytes_max = 1 * 1024 * 1024,
++ .fifo_size = 0,
++};
++
++static struct aiu_i2s_dma *aiu_i2s_dma_priv(struct snd_pcm_substream *s)
++{
++ struct snd_soc_pcm_runtime *rtd = s->private_data;
++ struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
++
++ return snd_soc_component_get_drvdata(component);
++}
++
++static snd_pcm_uframes_t
++aiu_i2s_dma_pointer(struct snd_pcm_substream *substream)
++{
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ struct aiu_i2s_dma *priv = aiu_i2s_dma_priv(substream);
++ unsigned int addr;
++ int ret;
++
++ ret = regmap_read(priv->core->aiu, AIU_MEM_I2S_RD_PTR,
++ &addr);
++ if (ret)
++ return 0;
++
++ return bytes_to_frames(runtime, addr - (unsigned int)runtime->dma_addr);
++}
++
++static void __dma_enable(struct aiu_i2s_dma *priv, bool enable)
++{
++ unsigned int en_mask = (AIU_MEM_I2S_CONTROL_FILL_EN |
++ AIU_MEM_I2S_CONTROL_EMPTY_EN);
++
++ regmap_update_bits(priv->core->aiu, AIU_MEM_I2S_CONTROL, en_mask,
++ enable ? en_mask : 0);
++
++}
++
++static int aiu_i2s_dma_trigger(struct snd_pcm_substream *substream, int cmd)
++{
++ struct aiu_i2s_dma *priv = aiu_i2s_dma_priv(substream);
++
++ switch (cmd) {
++ case SNDRV_PCM_TRIGGER_START:
++ case SNDRV_PCM_TRIGGER_RESUME:
++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
++ __dma_enable(priv, true);
++ break;
++ case SNDRV_PCM_TRIGGER_SUSPEND:
++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
++ case SNDRV_PCM_TRIGGER_STOP:
++ __dma_enable(priv, false);
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static void __dma_init_mem(struct aiu_i2s_dma *priv)
++{
++ regmap_update_bits(priv->core->aiu, AIU_MEM_I2S_CONTROL,
++ AIU_MEM_I2S_CONTROL_INIT,
++ AIU_MEM_I2S_CONTROL_INIT);
++ regmap_update_bits(priv->core->aiu, AIU_MEM_I2S_BUF_CNTL,
++ AIU_MEM_I2S_BUF_CNTL_INIT,
++ AIU_MEM_I2S_BUF_CNTL_INIT);
++
++ regmap_update_bits(priv->core->aiu, AIU_MEM_I2S_CONTROL,
++ AIU_MEM_I2S_CONTROL_INIT,
++ 0);
++ regmap_update_bits(priv->core->aiu, AIU_MEM_I2S_BUF_CNTL,
++ AIU_MEM_I2S_BUF_CNTL_INIT,
++ 0);
++}
++
++static int aiu_i2s_dma_prepare(struct snd_pcm_substream *substream)
++{
++ struct aiu_i2s_dma *priv = aiu_i2s_dma_priv(substream);
++
++ __dma_init_mem(priv);
++
++ return 0;
++}
++
++static int aiu_i2s_dma_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ struct aiu_i2s_dma *priv = aiu_i2s_dma_priv(substream);
++ int ret;
++ u32 burst_num, mem_ctl;
++ dma_addr_t end_ptr;
++
++ ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
++ if (ret < 0)
++ return ret;
++
++ /* Setup memory layout */
++ if (params_physical_width(params) == 16)
++ mem_ctl = AIU_MEM_I2S_CONTROL_MODE_16BIT;
++ else
++ mem_ctl = 0;
++
++ regmap_update_bits(priv->core->aiu, AIU_MEM_I2S_CONTROL,
++ AIU_MEM_I2S_CONTROL_MODE_16BIT,
++ mem_ctl);
++
++ /* Initialize memory pointers */
++ regmap_write(priv->core->aiu, AIU_MEM_I2S_START_PTR, runtime->dma_addr);
++ regmap_write(priv->core->aiu, AIU_MEM_I2S_RD_PTR, runtime->dma_addr);
++
++ /* The end pointer is the address of the last valid block */
++ end_ptr = runtime->dma_addr + runtime->dma_bytes - AIU_I2S_DMA_BURST;
++ regmap_write(priv->core->aiu, AIU_MEM_I2S_END_PTR, end_ptr);
++
++ /* Memory masks */
++ burst_num = params_period_bytes(params) / AIU_I2S_DMA_BURST;
++ regmap_write(priv->core->aiu, AIU_MEM_I2S_MASKS,
++ AIU_MEM_I2S_MASKS_CH_RD(0xff) |
++ AIU_MEM_I2S_MASKS_CH_MEM(0xff) |
++ AIU_MEM_I2S_MASKS_IRQ_BLOCK(burst_num));
++
++ return 0;
++}
++
++static int aiu_i2s_dma_hw_free(struct snd_pcm_substream *substream)
++{
++ return snd_pcm_lib_free_pages(substream);
++}
++
++
++static irqreturn_t aiu_i2s_dma_irq_block(int irq, void *dev_id)
++{
++ struct snd_pcm_substream *playback = dev_id;
++
++ snd_pcm_period_elapsed(playback);
++
++ return IRQ_HANDLED;
++}
++
++static int aiu_i2s_dma_open(struct snd_pcm_substream *substream)
++{
++ struct aiu_i2s_dma *priv = aiu_i2s_dma_priv(substream);
++ int ret;
++
++ snd_soc_set_runtime_hwparams(substream, &aiu_i2s_dma_hw);
++
++ /*
++ * Make sure the buffer and period size are multiple of the DMA burst
++ * size
++ */
++ ret = snd_pcm_hw_constraint_step(substream->runtime, 0,
++ SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
++ AIU_I2S_DMA_BURST);
++ if (ret)
++ return ret;
++
++ ret = snd_pcm_hw_constraint_step(substream->runtime, 0,
++ SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
++ AIU_I2S_DMA_BURST);
++ if (ret)
++ return ret;
++
++ /* Request the I2S DDR irq */
++ ret = request_irq(priv->irq, aiu_i2s_dma_irq_block, 0,
++ DRV_NAME, substream);
++ if (ret)
++ return ret;
++
++ /* Power up the i2s fast domain - can't write the registers w/o it */
++ ret = clk_prepare_enable(priv->fast);
++ if (ret)
++ return ret;
++
++ /* Make sure the dma is initially disabled */
++ __dma_enable(priv, false);
++
++ return 0;
++}
++
++static int aiu_i2s_dma_close(struct snd_pcm_substream *substream)
++{
++ struct aiu_i2s_dma *priv = aiu_i2s_dma_priv(substream);
++
++ clk_disable_unprepare(priv->fast);
++ free_irq(priv->irq, substream);
++
++ return 0;
++}
++
++static const struct snd_pcm_ops aiu_i2s_dma_ops = {
++ .open = aiu_i2s_dma_open,
++ .close = aiu_i2s_dma_close,
++ .ioctl = snd_pcm_lib_ioctl,
++ .hw_params = aiu_i2s_dma_hw_params,
++ .hw_free = aiu_i2s_dma_hw_free,
++ .prepare = aiu_i2s_dma_prepare,
++ .pointer = aiu_i2s_dma_pointer,
++ .trigger = aiu_i2s_dma_trigger,
++};
++
++static int aiu_i2s_dma_new(struct snd_soc_pcm_runtime *rtd)
++{
++ struct snd_card *card = rtd->card->snd_card;
++ size_t size = aiu_i2s_dma_hw.buffer_bytes_max;
++
++ return snd_pcm_lib_preallocate_pages_for_all(rtd->pcm,
++ SNDRV_DMA_TYPE_DEV,
++ card->dev, size, size);
++}
++
++static const struct snd_soc_component_driver aiu_i2s_platform = {
++ .ops = &aiu_i2s_dma_ops,
++ .pcm_new = aiu_i2s_dma_new,
++ .name = DRV_NAME,
++};
++
++static int aiu_i2s_dma_probe(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct aiu_i2s_dma *priv;
++
++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
++ if (!priv)
++ return -ENOMEM;
++
++ platform_set_drvdata(pdev, priv);
++ priv->core = dev_get_drvdata(dev->parent);
++
++ priv->fast = devm_clk_get(dev, "fast");
++ if (IS_ERR(priv->fast)) {
++ if (PTR_ERR(priv->fast) != -EPROBE_DEFER)
++ dev_err(dev, "Can't get i2s fast domain clock\n");
++ return PTR_ERR(priv->fast);
++ }
++
++ priv->irq = platform_get_irq(pdev, 0);
++ if (priv->irq <= 0) {
++ dev_err(dev, "Can't get i2s ddr irq\n");
++ return priv->irq;
++ }
++
++ return devm_snd_soc_register_component(dev, &aiu_i2s_platform,
++ NULL, 0);
++}
++
++static const struct of_device_id aiu_i2s_dma_of_match[] = {
++ { .compatible = "amlogic,meson-aiu-i2s-dma", },
++ { .compatible = "amlogic,meson-gxbb-aiu-i2s-dma", },
++ { .compatible = "amlogic,meson-gxl-aiu-i2s-dma", },
++ {}
++};
++MODULE_DEVICE_TABLE(of, aiu_i2s_dma_of_match);
++
++static struct platform_driver aiu_i2s_dma_pdrv = {
++ .probe = aiu_i2s_dma_probe,
++ .driver = {
++ .name = DRV_NAME,
++ .of_match_table = aiu_i2s_dma_of_match,
++ },
++};
++module_platform_driver(aiu_i2s_dma_pdrv);
++
++MODULE_DESCRIPTION("Meson AIU i2s DMA ASoC Driver");
++MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
++MODULE_LICENSE("GPL v2");
diff --git a/testing/linux-amlogic/0009-ASoC-meson-add-initial-i2s-dai-support.patch b/testing/linux-amlogic/0009-ASoC-meson-add-initial-i2s-dai-support.patch
new file mode 100644
index 0000000000..f3bac343f7
--- /dev/null
+++ b/testing/linux-amlogic/0009-ASoC-meson-add-initial-i2s-dai-support.patch
@@ -0,0 +1,511 @@
+From 6a5036e9f7dbd99023c6f9482fed3a747868b1c2 Mon Sep 17 00:00:00 2001
+From: Jerome Brunet <jbrunet@baylibre.com>
+Date: Thu, 30 Mar 2017 12:17:27 +0200
+Subject: [PATCH] ASoC: meson: add initial i2s dai support
+
+Add support for the i2s dai found on Amlogic Meson SoC family.
+With this initial implementation, only playback is supported.
+Capture will be part of furture work.
+
+Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
+---
+ sound/soc/meson/Kconfig | 2 +-
+ sound/soc/meson/Makefile | 2 +
+ sound/soc/meson/i2s-dai.c | 465 ++++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 468 insertions(+), 1 deletion(-)
+ create mode 100644 sound/soc/meson/i2s-dai.c
+
+diff --git a/sound/soc/meson/Kconfig b/sound/soc/meson/Kconfig
+index 88fbfc2..478f29a 100644
+--- a/sound/soc/meson/Kconfig
++++ b/sound/soc/meson/Kconfig
+@@ -12,5 +12,5 @@ config SND_SOC_MESON_I2S
+ tristate "Meson i2s interface"
+ depends on SND_SOC_MESON
+ help
+- Say Y or M if you want to add support for i2s dma driver for Amlogic
++ Say Y or M if you want to add support for i2s driver for Amlogic
+ Meson SoCs.
+diff --git a/sound/soc/meson/Makefile b/sound/soc/meson/Makefile
+index 273f275..ea06dde 100644
+--- a/sound/soc/meson/Makefile
++++ b/sound/soc/meson/Makefile
+@@ -1,5 +1,7 @@
+ snd-soc-meson-audio-core-objs := audio-core.o
+ snd-soc-meson-aiu-i2s-dma-objs := aiu-i2s-dma.o
++snd-soc-meson-i2s-dai-objs := i2s-dai.o
+
+ obj-$(CONFIG_SND_SOC_MESON) += snd-soc-meson-audio-core.o
+ obj-$(CONFIG_SND_SOC_MESON_I2S) += snd-soc-meson-aiu-i2s-dma.o
++obj-$(CONFIG_SND_SOC_MESON_I2S) += snd-soc-meson-i2s-dai.o
+diff --git a/sound/soc/meson/i2s-dai.c b/sound/soc/meson/i2s-dai.c
+new file mode 100644
+index 0000000..1008af8
+--- /dev/null
++++ b/sound/soc/meson/i2s-dai.c
+@@ -0,0 +1,465 @@
++/*
++ * Copyright (C) 2017 BayLibre, SAS
++ * Author: Jerome Brunet <jbrunet@baylibre.com>
++ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <linux/clk.h>
++#include <linux/mfd/syscon.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/regmap.h>
++
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++#include <sound/soc-dai.h>
++
++#include "aiu-regs.h"
++#include "audio-core.h"
++
++#define DRV_NAME "meson-i2s-dai"
++
++struct meson_i2s_dai {
++ struct meson_audio_core_data *core;
++ struct clk *mclk;
++ struct clk *bclks;
++ struct clk *iface;
++ struct clk *fast;
++ bool bclks_idle;
++};
++
++#define AIU_CLK_CTRL_I2S_DIV_EN BIT(0)
++#define AIU_CLK_CTRL_I2S_DIV_MASK GENMASK(3, 2)
++#define AIU_CLK_CTRL_AOCLK_POLARITY_MASK BIT(6)
++#define AIU_CLK_CTRL_AOCLK_POLARITY_NORMAL (0 << 6)
++#define AIU_CLK_CTRL_AOCLK_POLARITY_INVERTED (1 << 6)
++#define AIU_CLK_CTRL_ALRCLK_POLARITY_MASK BIT(7)
++#define AIU_CLK_CTRL_ALRCLK_POLARITY_NORMAL (0 << 7)
++#define AIU_CLK_CTRL_ALRCLK_POLARITY_INVERTED (1 << 7)
++#define AIU_CLK_CTRL_ALRCLK_SKEW_MASK GENMASK(9, 8)
++#define AIU_CLK_CTRL_ALRCLK_LEFT_J (0 << 8)
++#define AIU_CLK_CTRL_ALRCLK_I2S (1 << 8)
++#define AIU_CLK_CTRL_ALRCLK_RIGHT_J (2 << 8)
++#define AIU_CLK_CTRL_MORE_I2S_DIV_MASK GENMASK(5, 0)
++#define AIU_CLK_CTRL_MORE_I2S_DIV(div) (((div) - 1) << 0)
++#define AIU_CODEC_DAC_LRCLK_CTRL_DIV_MASK GENMASK(11, 0)
++#define AIU_CODEC_DAC_LRCLK_CTRL_DIV(div) (((div) - 1) << 0)
++#define AIU_I2S_DAC_CFG_PAYLOAD_SIZE_MASK GENMASK(1, 0)
++#define AIU_I2S_DAC_CFG_AOCLK_32 (0 << 0)
++#define AIU_I2S_DAC_CFG_AOCLK_48 (2 << 0)
++#define AIU_I2S_DAC_CFG_AOCLK_64 (3 << 0)
++#define AIU_I2S_MISC_HOLD_EN BIT(2)
++#define AIU_I2S_SOURCE_DESC_MODE_8CH BIT(0)
++#define AIU_I2S_SOURCE_DESC_MODE_24BIT BIT(5)
++#define AIU_I2S_SOURCE_DESC_MODE_32BIT BIT(9)
++#define AIU_I2S_SOURCE_DESC_MODE_SPLIT BIT(11)
++
++static void __hold(struct meson_i2s_dai *priv, bool enable)
++{
++ regmap_update_bits(priv->core->aiu, AIU_I2S_MISC,
++ AIU_I2S_MISC_HOLD_EN,
++ enable ? AIU_I2S_MISC_HOLD_EN : 0);
++}
++
++static void __divider_enable(struct meson_i2s_dai *priv, bool enable)
++{
++ regmap_update_bits(priv->core->aiu, AIU_CLK_CTRL,
++ AIU_CLK_CTRL_I2S_DIV_EN,
++ enable ? AIU_CLK_CTRL_I2S_DIV_EN : 0);
++}
++
++static void __playback_start(struct meson_i2s_dai *priv)
++{
++ __divider_enable(priv, true);
++ __hold(priv, false);
++}
++
++static void __playback_stop(struct meson_i2s_dai *priv, bool clk_force)
++{
++ __hold(priv, true);
++ /* Disable the bit clks if necessary */
++ if (clk_force || !priv->bclks_idle)
++ __divider_enable(priv, false);
++}
++
++static int meson_i2s_dai_trigger(struct snd_pcm_substream *substream, int cmd,
++ struct snd_soc_dai *dai)
++{
++ struct meson_i2s_dai *priv = snd_soc_dai_get_drvdata(dai);
++ bool clk_force_stop = false;
++
++ switch (cmd) {
++ case SNDRV_PCM_TRIGGER_START:
++ case SNDRV_PCM_TRIGGER_RESUME:
++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
++ __playback_start(priv);
++ return 0;
++
++ case SNDRV_PCM_TRIGGER_STOP:
++ case SNDRV_PCM_TRIGGER_SUSPEND:
++ clk_force_stop = true;
++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
++ __playback_stop(priv, clk_force_stop);
++ return 0;
++
++ default:
++ return -EINVAL;
++ }
++}
++
++static int __bclks_set_rate(struct meson_i2s_dai *priv, unsigned int srate,
++ unsigned int width)
++{
++ unsigned int fs;
++
++ /* Get the oversampling factor */
++ fs = DIV_ROUND_CLOSEST(clk_get_rate(priv->mclk), srate);
++
++ /*
++ * This DAI is usually connected to the dw-hdmi which does not support
++ * bclk being 32 * lrclk or 48 * lrclk
++ * Restrict to blck = 64 * lrclk
++ */
++ if (fs % 64)
++ return -EINVAL;
++
++ /* Set the divider between lrclk and bclk */
++ regmap_update_bits(priv->core->aiu, AIU_I2S_DAC_CFG,
++ AIU_I2S_DAC_CFG_PAYLOAD_SIZE_MASK,
++ AIU_I2S_DAC_CFG_AOCLK_64);
++
++ regmap_update_bits(priv->core->aiu, AIU_CODEC_DAC_LRCLK_CTRL,
++ AIU_CODEC_DAC_LRCLK_CTRL_DIV_MASK,
++ AIU_CODEC_DAC_LRCLK_CTRL_DIV(64));
++
++ /* Use CLK_MORE for the i2s divider */
++ regmap_update_bits(priv->core->aiu, AIU_CLK_CTRL,
++ AIU_CLK_CTRL_I2S_DIV_MASK,
++ 0);
++
++ regmap_update_bits(priv->core->aiu, AIU_CLK_CTRL_MORE,
++ AIU_CLK_CTRL_MORE_I2S_DIV_MASK,
++ AIU_CLK_CTRL_MORE_I2S_DIV(fs / 64));
++
++ return 0;
++}
++
++static int __setup_desc(struct meson_i2s_dai *priv, unsigned int width,
++ unsigned int channels)
++{
++ u32 desc = 0;
++
++ switch (width) {
++ case 24:
++ /*
++ * For some reason, 24 bits wide audio don't play well
++ * if the 32 bits mode is not set
++ */
++ desc |= (AIU_I2S_SOURCE_DESC_MODE_24BIT |
++ AIU_I2S_SOURCE_DESC_MODE_32BIT);
++ break;
++ case 16:
++ break;
++
++ default:
++ return -EINVAL;
++ }
++
++ switch (channels) {
++ case 2: /* Nothing to do */
++ break;
++ case 8:
++ /* TODO: Still requires testing ... */
++ desc |= AIU_I2S_SOURCE_DESC_MODE_8CH;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ regmap_update_bits(priv->core->aiu, AIU_I2S_SOURCE_DESC,
++ AIU_I2S_SOURCE_DESC_MODE_8CH |
++ AIU_I2S_SOURCE_DESC_MODE_24BIT |
++ AIU_I2S_SOURCE_DESC_MODE_32BIT,
++ desc);
++
++ return 0;
++}
++
++static int meson_i2s_dai_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params,
++ struct snd_soc_dai *dai)
++{
++ struct meson_i2s_dai *priv = snd_soc_dai_get_drvdata(dai);
++ unsigned int width = params_width(params);
++ unsigned int channels = params_channels(params);
++ unsigned int rate = params_rate(params);
++ int ret;
++
++ ret = __setup_desc(priv, width, channels);
++ if (ret) {
++ dev_err(dai->dev, "Unable set to set i2s description\n");
++ return ret;
++ }
++
++ ret = __bclks_set_rate(priv, rate, width);
++ if (ret) {
++ dev_err(dai->dev, "Unable set to the i2s clock rates\n");
++ return ret;
++ }
++
++ return 0;
++}
++
++static int meson_i2s_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
++{
++ struct meson_i2s_dai *priv = snd_soc_dai_get_drvdata(dai);
++ u32 val;
++
++ if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS)
++ return -EINVAL;
++
++ /* DAI output mode */
++ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
++ case SND_SOC_DAIFMT_I2S:
++ val = AIU_CLK_CTRL_ALRCLK_I2S;
++ break;
++ case SND_SOC_DAIFMT_LEFT_J:
++ val = AIU_CLK_CTRL_ALRCLK_LEFT_J;
++ break;
++ case SND_SOC_DAIFMT_RIGHT_J:
++ val = AIU_CLK_CTRL_ALRCLK_RIGHT_J;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ regmap_update_bits(priv->core->aiu, AIU_CLK_CTRL,
++ AIU_CLK_CTRL_ALRCLK_SKEW_MASK,
++ val);
++
++ /* DAI clock polarity */
++ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
++ case SND_SOC_DAIFMT_IB_IF:
++ /* Invert both clocks */
++ val = AIU_CLK_CTRL_ALRCLK_POLARITY_INVERTED |
++ AIU_CLK_CTRL_AOCLK_POLARITY_INVERTED;
++ break;
++ case SND_SOC_DAIFMT_IB_NF:
++ /* Invert bit clock */
++ val = AIU_CLK_CTRL_ALRCLK_POLARITY_NORMAL |
++ AIU_CLK_CTRL_AOCLK_POLARITY_INVERTED;
++ break;
++ case SND_SOC_DAIFMT_NB_IF:
++ /* Invert frame clock */
++ val = AIU_CLK_CTRL_ALRCLK_POLARITY_INVERTED |
++ AIU_CLK_CTRL_AOCLK_POLARITY_NORMAL;
++ break;
++ case SND_SOC_DAIFMT_NB_NF:
++ /* Normal clocks */
++ val = AIU_CLK_CTRL_ALRCLK_POLARITY_NORMAL |
++ AIU_CLK_CTRL_AOCLK_POLARITY_NORMAL;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ regmap_update_bits(priv->core->aiu, AIU_CLK_CTRL,
++ AIU_CLK_CTRL_ALRCLK_POLARITY_MASK |
++ AIU_CLK_CTRL_AOCLK_POLARITY_MASK,
++ val);
++
++ switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
++ case SND_SOC_DAIFMT_CONT:
++ priv->bclks_idle = true;
++ break;
++ case SND_SOC_DAIFMT_GATED:
++ priv->bclks_idle = false;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static int meson_i2s_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
++ unsigned int freq, int dir)
++{
++ struct meson_i2s_dai *priv = snd_soc_dai_get_drvdata(dai);
++ int ret;
++
++ if (WARN_ON(clk_id != 0))
++ return -EINVAL;
++
++ if (dir == SND_SOC_CLOCK_IN)
++ return 0;
++
++ ret = clk_set_rate(priv->mclk, freq);
++ if (ret) {
++ dev_err(dai->dev, "Failed to set sysclk to %uHz", freq);
++ return ret;
++ }
++
++ return 0;
++}
++
++static int meson_i2s_dai_startup(struct snd_pcm_substream *substream,
++ struct snd_soc_dai *dai)
++{
++ struct meson_i2s_dai *priv = snd_soc_dai_get_drvdata(dai);
++ int ret;
++
++ /* Power up the i2s fast domain - can't write the registers w/o it */
++ ret = clk_prepare_enable(priv->fast);
++ if (ret)
++ goto out_clk_fast;
++
++ /* Make sure nothing gets out of the DAI yet */
++ __hold(priv, true);
++
++ /* I2S encoder needs the mixer interface gate */
++ ret = clk_prepare_enable(priv->iface);
++ if (ret)
++ goto out_clk_iface;
++
++ /* Enable the i2s master clock */
++ ret = clk_prepare_enable(priv->mclk);
++ if (ret)
++ goto out_mclk;
++
++ /* Enable the bit clock gate */
++ ret = clk_prepare_enable(priv->bclks);
++ if (ret)
++ goto out_bclks;
++
++ /* Make sure the interface expect a memory layout we can work with */
++ regmap_update_bits(priv->core->aiu, AIU_I2S_SOURCE_DESC,
++ AIU_I2S_SOURCE_DESC_MODE_SPLIT,
++ AIU_I2S_SOURCE_DESC_MODE_SPLIT);
++
++ return 0;
++
++out_bclks:
++ clk_disable_unprepare(priv->mclk);
++out_mclk:
++ clk_disable_unprepare(priv->iface);
++out_clk_iface:
++ clk_disable_unprepare(priv->fast);
++out_clk_fast:
++ return ret;
++}
++
++static void meson_i2s_dai_shutdown(struct snd_pcm_substream *substream,
++ struct snd_soc_dai *dai)
++{
++ struct meson_i2s_dai *priv = snd_soc_dai_get_drvdata(dai);
++
++ clk_disable_unprepare(priv->bclks);
++ clk_disable_unprepare(priv->mclk);
++ clk_disable_unprepare(priv->iface);
++ clk_disable_unprepare(priv->fast);
++}
++
++static const struct snd_soc_dai_ops meson_i2s_dai_ops = {
++ .startup = meson_i2s_dai_startup,
++ .shutdown = meson_i2s_dai_shutdown,
++ .trigger = meson_i2s_dai_trigger,
++ .hw_params = meson_i2s_dai_hw_params,
++ .set_fmt = meson_i2s_dai_set_fmt,
++ .set_sysclk = meson_i2s_dai_set_sysclk,
++};
++
++static struct snd_soc_dai_driver meson_i2s_dai = {
++ .playback = {
++ .stream_name = "Playback",
++ .channels_min = 2,
++ .channels_max = 8,
++ .rates = SNDRV_PCM_RATE_8000_192000,
++ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
++ SNDRV_PCM_FMTBIT_S24_LE)
++ },
++ .ops = &meson_i2s_dai_ops,
++};
++
++static const struct snd_soc_component_driver meson_i2s_dai_component = {
++ .name = DRV_NAME,
++};
++
++static int meson_i2s_dai_probe(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct meson_i2s_dai *priv;
++
++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
++ if (!priv)
++ return -ENOMEM;
++
++ platform_set_drvdata(pdev, priv);
++ priv->core = dev_get_drvdata(dev->parent);
++
++ priv->fast = devm_clk_get(dev, "fast");
++ if (IS_ERR(priv->fast)) {
++ if (PTR_ERR(priv->fast) != -EPROBE_DEFER)
++ dev_err(dev, "Can't get the i2s fast domain clock\n");
++ return PTR_ERR(priv->fast);
++ }
++
++ priv->iface = devm_clk_get(dev, "iface");
++ if (IS_ERR(priv->iface)) {
++ if (PTR_ERR(priv->iface) != -EPROBE_DEFER)
++ dev_err(dev, "Can't get i2s dai clock gate\n");
++ return PTR_ERR(priv->iface);
++ }
++
++ priv->bclks = devm_clk_get(dev, "bclks");
++ if (IS_ERR(priv->bclks)) {
++ if (PTR_ERR(priv->bclks) != -EPROBE_DEFER)
++ dev_err(dev, "Can't get bit clocks gate\n");
++ return PTR_ERR(priv->bclks);
++ }
++
++ priv->mclk = devm_clk_get(dev, "mclk");
++ if (IS_ERR(priv->mclk)) {
++ if (PTR_ERR(priv->mclk) != -EPROBE_DEFER)
++ dev_err(dev, "failed to get the i2s master clock\n");
++ return PTR_ERR(priv->mclk);
++ }
++
++ return devm_snd_soc_register_component(dev, &meson_i2s_dai_component,
++ &meson_i2s_dai, 1);
++}
++
++static const struct of_device_id meson_i2s_dai_of_match[] = {
++ { .compatible = "amlogic,meson-i2s-dai", },
++ { .compatible = "amlogic,meson-gxbb-i2s-dai", },
++ { .compatible = "amlogic,meson-gxl-i2s-dai", },
++ {}
++};
++MODULE_DEVICE_TABLE(of, meson_i2s_dai_of_match);
++
++static struct platform_driver meson_i2s_dai_pdrv = {
++ .probe = meson_i2s_dai_probe,
++ .driver = {
++ .name = DRV_NAME,
++ .of_match_table = meson_i2s_dai_of_match,
++ },
++};
++module_platform_driver(meson_i2s_dai_pdrv);
++
++MODULE_DESCRIPTION("Meson i2s DAI ASoC Driver");
++MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
++MODULE_LICENSE("GPL v2");
diff --git a/testing/linux-amlogic/0010-ASoC-meson-add-aiu-spdif-dma-support.patch b/testing/linux-amlogic/0010-ASoC-meson-add-aiu-spdif-dma-support.patch
new file mode 100644
index 0000000000..380f5d5b2c
--- /dev/null
+++ b/testing/linux-amlogic/0010-ASoC-meson-add-aiu-spdif-dma-support.patch
@@ -0,0 +1,438 @@
+From 8dd5edaf984e4c8d6f7ca1e7709b3109cf7dd780 Mon Sep 17 00:00:00 2001
+From: Jerome Brunet <jbrunet@baylibre.com>
+Date: Thu, 30 Mar 2017 13:43:52 +0200
+Subject: [PATCH] ASoC: meson: add aiu spdif dma support
+
+Add support for the spdif output dma which is part of the AIU block
+
+Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
+---
+ sound/soc/meson/Kconfig | 7 +
+ sound/soc/meson/Makefile | 2 +
+ sound/soc/meson/aiu-spdif-dma.c | 388 ++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 397 insertions(+)
+ create mode 100644 sound/soc/meson/aiu-spdif-dma.c
+
+diff --git a/sound/soc/meson/Kconfig b/sound/soc/meson/Kconfig
+index 478f29a..3fb93b9 100644
+--- a/sound/soc/meson/Kconfig
++++ b/sound/soc/meson/Kconfig
+@@ -14,3 +14,10 @@ config SND_SOC_MESON_I2S
+ help
+ Say Y or M if you want to add support for i2s driver for Amlogic
+ Meson SoCs.
++
++config SND_SOC_MESON_SPDIF
++ tristate "Meson spdif interface"
++ depends on SND_SOC_MESON
++ help
++ Say Y or M if you want to add support for spdif dma driver for Amlogic
++ Meson SoCs.
+diff --git a/sound/soc/meson/Makefile b/sound/soc/meson/Makefile
+index ea06dde..cef9a9d 100644
+--- a/sound/soc/meson/Makefile
++++ b/sound/soc/meson/Makefile
+@@ -1,7 +1,9 @@
+ snd-soc-meson-audio-core-objs := audio-core.o
+ snd-soc-meson-aiu-i2s-dma-objs := aiu-i2s-dma.o
++snd-soc-meson-aiu-spdif-dma-objs := aiu-spdif-dma.o
+ snd-soc-meson-i2s-dai-objs := i2s-dai.o
+
+ obj-$(CONFIG_SND_SOC_MESON) += snd-soc-meson-audio-core.o
+ obj-$(CONFIG_SND_SOC_MESON_I2S) += snd-soc-meson-aiu-i2s-dma.o
+ obj-$(CONFIG_SND_SOC_MESON_I2S) += snd-soc-meson-i2s-dai.o
++obj-$(CONFIG_SND_SOC_MESON_SPDIF) += snd-soc-meson-aiu-spdif-dma.o
+diff --git a/sound/soc/meson/aiu-spdif-dma.c b/sound/soc/meson/aiu-spdif-dma.c
+new file mode 100644
+index 0000000..81c3b85
+--- /dev/null
++++ b/sound/soc/meson/aiu-spdif-dma.c
+@@ -0,0 +1,388 @@
++/*
++ * Copyright (C) 2017 BayLibre, SAS
++ * Author: Jerome Brunet <jbrunet@baylibre.com>
++ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <linux/clk.h>
++#include <linux/mfd/syscon.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/regmap.h>
++
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++
++#include "aiu-regs.h"
++#include "audio-core.h"
++
++#define DRV_NAME "meson-aiu-spdif-dma"
++
++struct aiu_spdif_dma {
++ struct meson_audio_core_data *core;
++ struct clk *fast;
++ int irq;
++};
++
++#define AIU_958_DCU_FF_CTRL_EN BIT(0)
++#define AIU_958_DCU_FF_CTRL_AUTO_DISABLE BIT(1)
++#define AIU_958_DCU_FF_CTRL_IRQ_MODE_MASK GENMASK(3, 2)
++#define AIU_958_DCU_FF_CTRL_IRQ_OUT_THD BIT(2)
++#define AIU_958_DCU_FF_CTRL_IRQ_FRAME_READ BIT(3)
++#define AIU_958_DCU_FF_CTRL_SYNC_HEAD_EN BIT(4)
++#define AIU_958_DCU_FF_CTRL_BYTE_SEEK BIT(5)
++#define AIU_958_DCU_FF_CTRL_CONTINUE BIT(6)
++#define AIU_MEM_IEC958_BUF_CNTL_INIT BIT(0)
++#define AIU_MEM_IEC958_CONTROL_INIT BIT(0)
++#define AIU_MEM_IEC958_CONTROL_FILL_EN BIT(1)
++#define AIU_MEM_IEC958_CONTROL_EMPTY_EN BIT(2)
++#define AIU_MEM_IEC958_CONTROL_ENDIAN_MASK GENMASK(5, 3)
++#define AIU_MEM_IEC958_CONTROL_RD_DDR BIT(6)
++#define AIU_MEM_IEC958_CONTROL_MODE_16BIT BIT(7)
++#define AIU_MEM_IEC958_MASKS_CH_MEM_MASK GENMASK(15, 8)
++#define AIU_MEM_IEC958_MASKS_CH_MEM(ch) ((ch) << 8)
++#define AIU_MEM_IEC958_MASKS_CH_RD_MASK GENMASK(7, 0)
++#define AIU_MEM_IEC958_MASKS_CH_RD(ch) ((ch) << 0)
++
++#define AIU_SPDIF_DMA_BURST 8
++#define AIU_SPDIF_BPF_MAX USHRT_MAX
++
++static struct snd_pcm_hardware aiu_spdif_dma_hw = {
++ .info = (SNDRV_PCM_INFO_INTERLEAVED |
++ SNDRV_PCM_INFO_MMAP |
++ SNDRV_PCM_INFO_MMAP_VALID |
++ SNDRV_PCM_INFO_PAUSE),
++
++ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
++ SNDRV_PCM_FMTBIT_S24_LE |
++ SNDRV_PCM_FMTBIT_S32_LE),
++
++ .rates = (SNDRV_PCM_RATE_32000 |
++ SNDRV_PCM_RATE_44100 |
++ SNDRV_PCM_RATE_48000 |
++ SNDRV_PCM_RATE_96000 |
++ SNDRV_PCM_RATE_192000),
++ /*
++ * TODO: The DMA can change the endianness, the msb position
++ * and deal with unsigned - support this later on
++ */
++
++ .channels_min = 2,
++ .channels_max = 2,
++ .period_bytes_min = AIU_SPDIF_DMA_BURST,
++ .period_bytes_max = AIU_SPDIF_BPF_MAX,
++ .periods_min = 2,
++ .periods_max = UINT_MAX,
++ .buffer_bytes_max = 1 * 1024 * 1024,
++ .fifo_size = 0,
++};
++
++static struct aiu_spdif_dma *aiu_spdif_dma_priv(struct snd_pcm_substream *s)
++{
++ struct snd_soc_pcm_runtime *rtd = s->private_data;
++ struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
++
++ return snd_soc_component_get_drvdata(component);
++}
++
++static snd_pcm_uframes_t
++aiu_spdif_dma_pointer(struct snd_pcm_substream *substream)
++{
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ struct aiu_spdif_dma *priv = aiu_spdif_dma_priv(substream);
++ unsigned int addr;
++ int ret;
++
++ ret = regmap_read(priv->core->aiu, AIU_MEM_IEC958_RD_PTR,
++ &addr);
++ if (ret)
++ return 0;
++
++ return bytes_to_frames(runtime, addr - (unsigned int)runtime->dma_addr);
++}
++
++static void __dma_enable(struct aiu_spdif_dma *priv, bool enable)
++{
++ unsigned int en_mask = (AIU_MEM_IEC958_CONTROL_FILL_EN |
++ AIU_MEM_IEC958_CONTROL_EMPTY_EN);
++
++ regmap_update_bits(priv->core->aiu, AIU_MEM_IEC958_CONTROL, en_mask,
++ enable ? en_mask : 0);
++}
++
++static void __dcu_fifo_enable(struct aiu_spdif_dma *priv, bool enable)
++{
++ regmap_update_bits(priv->core->aiu, AIU_958_DCU_FF_CTRL,
++ AIU_958_DCU_FF_CTRL_EN,
++ enable ? AIU_958_DCU_FF_CTRL_EN : 0);
++}
++
++static int aiu_spdif_dma_trigger(struct snd_pcm_substream *substream, int cmd)
++{
++ struct aiu_spdif_dma *priv = aiu_spdif_dma_priv(substream);
++
++ switch (cmd) {
++ case SNDRV_PCM_TRIGGER_START:
++ case SNDRV_PCM_TRIGGER_RESUME:
++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
++ __dcu_fifo_enable(priv, true);
++ __dma_enable(priv, true);
++ break;
++ case SNDRV_PCM_TRIGGER_SUSPEND:
++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
++ case SNDRV_PCM_TRIGGER_STOP:
++ __dma_enable(priv, false);
++ __dcu_fifo_enable(priv, false);
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static void __dma_init_mem(struct aiu_spdif_dma *priv)
++{
++ regmap_update_bits(priv->core->aiu, AIU_MEM_IEC958_CONTROL,
++ AIU_MEM_IEC958_CONTROL_INIT,
++ AIU_MEM_IEC958_CONTROL_INIT);
++ regmap_update_bits(priv->core->aiu, AIU_MEM_IEC958_BUF_CNTL,
++ AIU_MEM_IEC958_BUF_CNTL_INIT,
++ AIU_MEM_IEC958_BUF_CNTL_INIT);
++
++ regmap_update_bits(priv->core->aiu, AIU_MEM_IEC958_CONTROL,
++ AIU_MEM_IEC958_CONTROL_INIT,
++ 0);
++ regmap_update_bits(priv->core->aiu, AIU_MEM_IEC958_BUF_CNTL,
++ AIU_MEM_IEC958_BUF_CNTL_INIT,
++ 0);
++}
++
++static int aiu_spdif_dma_prepare(struct snd_pcm_substream *substream)
++{
++ struct aiu_spdif_dma *priv = aiu_spdif_dma_priv(substream);
++
++ __dma_init_mem(priv);
++
++ return 0;
++}
++
++static int __setup_memory_layout(struct aiu_spdif_dma *priv,
++ unsigned int width)
++{
++ u32 mem_ctl = AIU_MEM_IEC958_CONTROL_RD_DDR;
++
++ if (width == 16)
++ mem_ctl |= AIU_MEM_IEC958_CONTROL_MODE_16BIT;
++
++ regmap_update_bits(priv->core->aiu, AIU_MEM_IEC958_CONTROL,
++ AIU_MEM_IEC958_CONTROL_ENDIAN_MASK |
++ AIU_MEM_IEC958_CONTROL_MODE_16BIT |
++ AIU_MEM_IEC958_CONTROL_RD_DDR,
++ mem_ctl);
++
++ return 0;
++}
++
++static int aiu_spdif_dma_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ struct aiu_spdif_dma *priv = aiu_spdif_dma_priv(substream);
++ int ret;
++ dma_addr_t end_ptr;
++
++ ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
++ if (ret < 0)
++ return ret;
++
++ ret = __setup_memory_layout(priv, params_physical_width(params));
++ if (ret)
++ return ret;
++
++ /* Initialize memory pointers */
++ regmap_write(priv->core->aiu,
++ AIU_MEM_IEC958_START_PTR, runtime->dma_addr);
++ regmap_write(priv->core->aiu,
++ AIU_MEM_IEC958_RD_PTR, runtime->dma_addr);
++
++ /* The end pointer is the address of the last valid block */
++ end_ptr = runtime->dma_addr + runtime->dma_bytes - AIU_SPDIF_DMA_BURST;
++ regmap_write(priv->core->aiu, AIU_MEM_IEC958_END_PTR, end_ptr);
++
++ /* Memory masks */
++ regmap_write(priv->core->aiu, AIU_MEM_IEC958_MASKS,
++ AIU_MEM_IEC958_MASKS_CH_RD(0xff) |
++ AIU_MEM_IEC958_MASKS_CH_MEM(0xff));
++
++ /* Setup the number bytes read by the FIFO between each IRQ */
++ regmap_write(priv->core->aiu, AIU_958_BPF, params_period_bytes(params));
++
++ /*
++ * AUTO_DISABLE and SYNC_HEAD are enabled by default but
++ * this should be disabled in PCM (uncompressed) mode
++ */
++ regmap_update_bits(priv->core->aiu, AIU_958_DCU_FF_CTRL,
++ AIU_958_DCU_FF_CTRL_AUTO_DISABLE |
++ AIU_958_DCU_FF_CTRL_IRQ_MODE_MASK |
++ AIU_958_DCU_FF_CTRL_SYNC_HEAD_EN,
++ AIU_958_DCU_FF_CTRL_IRQ_FRAME_READ);
++
++ return 0;
++}
++
++static int aiu_spdif_dma_hw_free(struct snd_pcm_substream *substream)
++{
++ return snd_pcm_lib_free_pages(substream);
++}
++
++static irqreturn_t aiu_spdif_dma_irq(int irq, void *dev_id)
++{
++ struct snd_pcm_substream *playback = dev_id;
++
++ snd_pcm_period_elapsed(playback);
++
++ return IRQ_HANDLED;
++}
++
++static int aiu_spdif_dma_open(struct snd_pcm_substream *substream)
++{
++ struct aiu_spdif_dma *priv = aiu_spdif_dma_priv(substream);
++ int ret;
++
++ snd_soc_set_runtime_hwparams(substream, &aiu_spdif_dma_hw);
++
++ /*
++ * Make sure the buffer and period size are multiple of the DMA burst
++ * size
++ */
++ ret = snd_pcm_hw_constraint_step(substream->runtime, 0,
++ SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
++ AIU_SPDIF_DMA_BURST);
++ if (ret)
++ return ret;
++
++ ret = snd_pcm_hw_constraint_step(substream->runtime, 0,
++ SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
++ AIU_SPDIF_DMA_BURST);
++ if (ret)
++ return ret;
++
++ /* Request the SPDIF DDR irq */
++ ret = request_irq(priv->irq, aiu_spdif_dma_irq, 0,
++ DRV_NAME, substream);
++ if (ret)
++ return ret;
++
++ /* Power up the spdif fast domain - can't write the register w/o it */
++ ret = clk_prepare_enable(priv->fast);
++ if (ret)
++ return ret;
++
++ /* Make sure the dma is initially halted */
++ __dma_enable(priv, false);
++ __dcu_fifo_enable(priv, false);
++
++ return 0;
++}
++
++static int aiu_spdif_dma_close(struct snd_pcm_substream *substream)
++{
++ struct aiu_spdif_dma *priv = aiu_spdif_dma_priv(substream);
++
++ clk_disable_unprepare(priv->fast);
++ free_irq(priv->irq, substream);
++
++ return 0;
++}
++
++static const struct snd_pcm_ops aiu_spdif_dma_ops = {
++ .open = aiu_spdif_dma_open,
++ .close = aiu_spdif_dma_close,
++ .ioctl = snd_pcm_lib_ioctl,
++ .hw_params = aiu_spdif_dma_hw_params,
++ .hw_free = aiu_spdif_dma_hw_free,
++ .prepare = aiu_spdif_dma_prepare,
++ .pointer = aiu_spdif_dma_pointer,
++ .trigger = aiu_spdif_dma_trigger,
++};
++
++static int aiu_spdif_dma_new(struct snd_soc_pcm_runtime *rtd)
++{
++ struct snd_card *card = rtd->card->snd_card;
++ size_t size = aiu_spdif_dma_hw.buffer_bytes_max;
++
++ return snd_pcm_lib_preallocate_pages_for_all(rtd->pcm,
++ SNDRV_DMA_TYPE_DEV,
++ card->dev, size, size);
++}
++
++static const struct snd_soc_component_driver aiu_spdif_platform = {
++ .ops = &aiu_spdif_dma_ops,
++ .pcm_new = aiu_spdif_dma_new,
++ .name = DRV_NAME,
++};
++
++static int aiu_spdif_dma_probe(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct aiu_spdif_dma *priv;
++
++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
++ if (!priv)
++ return -ENOMEM;
++
++ platform_set_drvdata(pdev, priv);
++ priv->core = dev_get_drvdata(dev->parent);
++
++ priv->fast = devm_clk_get(dev, "fast");
++ if (IS_ERR(priv->fast)) {
++ if (PTR_ERR(priv->fast) != -EPROBE_DEFER)
++ dev_err(dev, "Can't get spdif fast domain clock\n");
++ return PTR_ERR(priv->fast);
++ }
++
++ priv->irq = platform_get_irq(pdev, 0);
++ if (priv->irq <= 0) {
++ dev_err(dev, "Can't get spdif ddr irq\n");
++ return priv->irq;
++ }
++
++ return devm_snd_soc_register_component(dev, &aiu_spdif_platform,
++ NULL, 0);
++}
++
++static const struct of_device_id aiu_spdif_dma_of_match[] = {
++ { .compatible = "amlogic,meson-aiu-spdif-dma", },
++ { .compatible = "amlogic,meson-gxbb-aiu-spdif-dma", },
++ { .compatible = "amlogic,meson-gxl-aiu-spdif-dma", },
++ {}
++};
++MODULE_DEVICE_TABLE(of, aiu_spdif_dma_of_match);
++
++static struct platform_driver aiu_spdif_dma_pdrv = {
++ .probe = aiu_spdif_dma_probe,
++ .driver = {
++ .name = DRV_NAME,
++ .of_match_table = aiu_spdif_dma_of_match,
++ },
++};
++module_platform_driver(aiu_spdif_dma_pdrv);
++
++MODULE_DESCRIPTION("Meson AIU spdif DMA ASoC Driver");
++MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
++MODULE_LICENSE("GPL");
diff --git a/testing/linux-amlogic/0011-ASoC-meson-add-initial-spdif-dai-support.patch b/testing/linux-amlogic/0011-ASoC-meson-add-initial-spdif-dai-support.patch
new file mode 100644
index 0000000000..3d36f4b064
--- /dev/null
+++ b/testing/linux-amlogic/0011-ASoC-meson-add-initial-spdif-dai-support.patch
@@ -0,0 +1,426 @@
+From e635299f76dc27b97a768f2a044d04c1917b9ad1 Mon Sep 17 00:00:00 2001
+From: Jerome Brunet <jbrunet@baylibre.com>
+Date: Thu, 30 Mar 2017 13:46:03 +0200
+Subject: [PATCH] ASoC: meson: add initial spdif dai support
+
+Add support for the spdif dai found on Amlogic Meson SoC family.
+With this initial implementation, only uncompressed pcm playback
+from the spdif dma is supported. Future work will add compressed
+support, pcm playback from i2s dma and capture.
+
+Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
+---
+ sound/soc/meson/Kconfig | 3 +-
+ sound/soc/meson/Makefile | 2 +
+ sound/soc/meson/spdif-dai.c | 374 ++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 378 insertions(+), 1 deletion(-)
+ create mode 100644 sound/soc/meson/spdif-dai.c
+
+diff --git a/sound/soc/meson/Kconfig b/sound/soc/meson/Kconfig
+index 3fb93b9..301d3a3 100644
+--- a/sound/soc/meson/Kconfig
++++ b/sound/soc/meson/Kconfig
+@@ -18,6 +18,7 @@ config SND_SOC_MESON_I2S
+ config SND_SOC_MESON_SPDIF
+ tristate "Meson spdif interface"
+ depends on SND_SOC_MESON
++ select SND_PCM_IEC958
+ help
+- Say Y or M if you want to add support for spdif dma driver for Amlogic
++ Say Y or M if you want to add support for spdif driver for Amlogic
+ Meson SoCs.
+diff --git a/sound/soc/meson/Makefile b/sound/soc/meson/Makefile
+index cef9a9d..bc4391c 100644
+--- a/sound/soc/meson/Makefile
++++ b/sound/soc/meson/Makefile
+@@ -2,8 +2,10 @@ snd-soc-meson-audio-core-objs := audio-core.o
+ snd-soc-meson-aiu-i2s-dma-objs := aiu-i2s-dma.o
+ snd-soc-meson-aiu-spdif-dma-objs := aiu-spdif-dma.o
+ snd-soc-meson-i2s-dai-objs := i2s-dai.o
++snd-soc-meson-spdif-dai-objs := spdif-dai.o
+
+ obj-$(CONFIG_SND_SOC_MESON) += snd-soc-meson-audio-core.o
+ obj-$(CONFIG_SND_SOC_MESON_I2S) += snd-soc-meson-aiu-i2s-dma.o
+ obj-$(CONFIG_SND_SOC_MESON_I2S) += snd-soc-meson-i2s-dai.o
+ obj-$(CONFIG_SND_SOC_MESON_SPDIF) += snd-soc-meson-aiu-spdif-dma.o
++obj-$(CONFIG_SND_SOC_MESON_SPDIF) += snd-soc-meson-spdif-dai.o
+diff --git a/sound/soc/meson/spdif-dai.c b/sound/soc/meson/spdif-dai.c
+new file mode 100644
+index 0000000..e763000
+--- /dev/null
++++ b/sound/soc/meson/spdif-dai.c
+@@ -0,0 +1,374 @@
++/*
++ * Copyright (C) 2017 BayLibre, SAS
++ * Author: Jerome Brunet <jbrunet@baylibre.com>
++ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <linux/clk.h>
++#include <linux/mfd/syscon.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/regmap.h>
++
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++#include <sound/soc-dai.h>
++#include <sound/pcm_iec958.h>
++
++#include "aiu-regs.h"
++#include "audio-core.h"
++
++#define DRV_NAME "meson-spdif-dai"
++
++struct meson_spdif_dai {
++ struct meson_audio_core_data *core;
++ struct clk *iface;
++ struct clk *fast;
++ struct clk *mclk_i958;
++ struct clk *mclk;
++};
++
++#define AIU_CLK_CTRL_958_DIV_EN BIT(1)
++#define AIU_CLK_CTRL_958_DIV_MASK GENMASK(5, 4)
++#define AIU_CLK_CTRL_958_DIV_MORE BIT(12)
++#define AIU_MEM_IEC958_CONTROL_MODE_LINEAR BIT(8)
++#define AIU_958_CTRL_HOLD_EN BIT(0)
++#define AIU_958_MISC_NON_PCM BIT(0)
++#define AIU_958_MISC_MODE_16BITS BIT(1)
++#define AIU_958_MISC_16BITS_ALIGN_MASK GENMASK(6, 5)
++#define AIU_958_MISC_16BITS_ALIGN(val) ((val) << 5)
++#define AIU_958_MISC_MODE_32BITS BIT(7)
++#define AIU_958_MISC_32BITS_SHIFT_MASK GENMASK(10, 8)
++#define AIU_958_MISC_32BITS_SHIFT(val) ((val) << 8)
++#define AIU_958_MISC_U_FROM_STREAM BIT(12)
++#define AIU_958_MISC_FORCE_LR BIT(13)
++
++#define AIU_CS_WORD_LEN 4
++
++static void __hold(struct meson_spdif_dai *priv, bool enable)
++{
++ regmap_update_bits(priv->core->aiu, AIU_958_CTRL,
++ AIU_958_CTRL_HOLD_EN,
++ enable ? AIU_958_CTRL_HOLD_EN : 0);
++}
++
++static void __divider_enable(struct meson_spdif_dai *priv, bool enable)
++{
++ regmap_update_bits(priv->core->aiu, AIU_CLK_CTRL,
++ AIU_CLK_CTRL_958_DIV_EN,
++ enable ? AIU_CLK_CTRL_958_DIV_EN : 0);
++}
++
++static void __playback_start(struct meson_spdif_dai *priv)
++{
++ __divider_enable(priv, true);
++ __hold(priv, false);
++}
++
++static void __playback_stop(struct meson_spdif_dai *priv)
++{
++ __hold(priv, true);
++ __divider_enable(priv, false);
++}
++
++static int meson_spdif_dai_trigger(struct snd_pcm_substream *substream, int cmd,
++ struct snd_soc_dai *dai)
++{
++ struct meson_spdif_dai *priv = snd_soc_dai_get_drvdata(dai);
++
++ switch (cmd) {
++ case SNDRV_PCM_TRIGGER_START:
++ case SNDRV_PCM_TRIGGER_RESUME:
++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
++ __playback_start(priv);
++ return 0;
++
++ case SNDRV_PCM_TRIGGER_STOP:
++ case SNDRV_PCM_TRIGGER_SUSPEND:
++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
++ __playback_stop(priv);
++ return 0;
++
++ default:
++ return -EINVAL;
++ }
++}
++
++static int __setup_spdif_clk(struct meson_spdif_dai *priv, unsigned int rate)
++{
++ unsigned int mrate;
++
++ /* Leave the internal divisor alone */
++ regmap_update_bits(priv->core->aiu, AIU_CLK_CTRL,
++ AIU_CLK_CTRL_958_DIV_MASK |
++ AIU_CLK_CTRL_958_DIV_MORE,
++ 0);
++
++ /* 2 * 32bits per subframe * 2 channels = 128 */
++ mrate = rate * 128;
++ return clk_set_rate(priv->mclk, mrate);
++}
++
++static int __setup_cs_word(struct meson_spdif_dai *priv,
++ struct snd_pcm_hw_params *params)
++{
++ u8 cs[AIU_CS_WORD_LEN];
++ u32 val;
++ int ret;
++
++ ret = snd_pcm_create_iec958_consumer_hw_params(params, cs,
++ AIU_CS_WORD_LEN);
++ if (ret < 0)
++ return -EINVAL;
++
++ /* Write the 1st half word */
++ val = cs[1] | cs[0] << 8;
++ regmap_write(priv->core->aiu, AIU_958_CHSTAT_L0, val);
++ regmap_write(priv->core->aiu, AIU_958_CHSTAT_R0, val);
++
++ /* Write the 2nd half word */
++ val = cs[3] | cs[2] << 8;
++ regmap_write(priv->core->aiu, AIU_958_CHSTAT_L1, val);
++ regmap_write(priv->core->aiu, AIU_958_CHSTAT_R1, val);
++
++ return 0;
++}
++
++static int __setup_pcm_fmt(struct meson_spdif_dai *priv,
++ unsigned int width)
++{
++ u32 val = 0;
++
++ switch (width) {
++ case 16:
++ val |= AIU_958_MISC_MODE_16BITS;
++ val |= AIU_958_MISC_16BITS_ALIGN(2);
++ break;
++ case 32:
++ case 24:
++ /*
++ * Looks like this should only be set for 32bits mode, but the
++ * vendor kernel sets it like this for 24bits as well, let's
++ * try and see
++ */
++ val |= AIU_958_MISC_MODE_32BITS;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ /* No idea what this actually does, copying the vendor kernel for now */
++ val |= AIU_958_MISC_FORCE_LR;
++ val |= AIU_958_MISC_U_FROM_STREAM;
++
++ regmap_update_bits(priv->core->aiu, AIU_958_MISC,
++ AIU_958_MISC_NON_PCM |
++ AIU_958_MISC_MODE_16BITS |
++ AIU_958_MISC_16BITS_ALIGN_MASK |
++ AIU_958_MISC_MODE_32BITS |
++ AIU_958_MISC_FORCE_LR,
++ val);
++
++ return 0;
++}
++
++static int meson_spdif_dai_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params,
++ struct snd_soc_dai *dai)
++{
++ struct meson_spdif_dai *priv = snd_soc_dai_get_drvdata(dai);
++ int ret;
++
++ ret = __setup_spdif_clk(priv, params_rate(params));
++ if (ret) {
++ dev_err(dai->dev, "Unable to set the spdif clock\n");
++ return ret;
++ }
++
++ ret = __setup_cs_word(priv, params);
++ if (ret) {
++ dev_err(dai->dev, "Unable to set the channel status word\n");
++ return ret;
++ }
++
++ ret = __setup_pcm_fmt(priv, params_width(params));
++ if (ret) {
++ dev_err(dai->dev, "Unable to set the pcm format\n");
++ return ret;
++ }
++
++ return 0;
++}
++
++static int meson_spdif_dai_startup(struct snd_pcm_substream *substream,
++ struct snd_soc_dai *dai)
++{
++ struct meson_spdif_dai *priv = snd_soc_dai_get_drvdata(dai);
++ int ret;
++
++ /* Power up the spdif fast domain - can't write the registers w/o it */
++ ret = clk_prepare_enable(priv->fast);
++ if (ret)
++ goto out_clk_fast;
++
++ /* Make sure nothing gets out of the DAI yet*/
++ __hold(priv, true);
++
++ ret = clk_set_parent(priv->mclk, priv->mclk_i958);
++ if (ret)
++ return ret;
++
++ /* Enable the clock gate */
++ ret = clk_prepare_enable(priv->iface);
++ if (ret)
++ goto out_clk_iface;
++
++ /* Enable the spdif clock */
++ ret = clk_prepare_enable(priv->mclk);
++ if (ret)
++ goto out_mclk;
++
++ /*
++ * Make sure the interface expect a memory layout we can work with
++ * MEM prefixed register usually belong to the DMA, but when the spdif
++ * DAI takes data from the i2s buffer, we need to make sure it works in
++ * split mode and not the "normal mode" (channel samples packed in
++ * 32 bytes groups)
++ */
++ regmap_update_bits(priv->core->aiu, AIU_MEM_IEC958_CONTROL,
++ AIU_MEM_IEC958_CONTROL_MODE_LINEAR,
++ AIU_MEM_IEC958_CONTROL_MODE_LINEAR);
++
++ return 0;
++
++out_mclk:
++ clk_disable_unprepare(priv->iface);
++out_clk_iface:
++ clk_disable_unprepare(priv->fast);
++out_clk_fast:
++ return ret;
++}
++
++static void meson_spdif_dai_shutdown(struct snd_pcm_substream *substream,
++ struct snd_soc_dai *dai)
++{
++ struct meson_spdif_dai *priv = snd_soc_dai_get_drvdata(dai);
++
++ clk_disable_unprepare(priv->iface);
++ clk_disable_unprepare(priv->mclk);
++ clk_disable_unprepare(priv->fast);
++}
++
++static const struct snd_soc_dai_ops meson_spdif_dai_ops = {
++ .startup = meson_spdif_dai_startup,
++ .shutdown = meson_spdif_dai_shutdown,
++ .trigger = meson_spdif_dai_trigger,
++ .hw_params = meson_spdif_dai_hw_params,
++};
++
++static struct snd_soc_dai_driver meson_spdif_dai = {
++ .playback = {
++ .stream_name = "Playback",
++ .channels_min = 2,
++ .channels_max = 2,
++ .rates = (SNDRV_PCM_RATE_32000 |
++ SNDRV_PCM_RATE_44100 |
++ SNDRV_PCM_RATE_48000 |
++ SNDRV_PCM_RATE_96000 |
++ SNDRV_PCM_RATE_192000),
++ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
++ SNDRV_PCM_FMTBIT_S24_LE)
++ },
++ .ops = &meson_spdif_dai_ops,
++};
++
++static const struct snd_soc_component_driver meson_spdif_dai_component = {
++ .name = DRV_NAME,
++};
++
++static int meson_spdif_dai_probe(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct meson_spdif_dai *priv;
++
++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
++ if (!priv)
++ return -ENOMEM;
++
++ platform_set_drvdata(pdev, priv);
++ priv->core = dev_get_drvdata(dev->parent);
++
++ priv->fast = devm_clk_get(dev, "fast");
++ if (IS_ERR(priv->fast)) {
++ if (PTR_ERR(priv->fast) != -EPROBE_DEFER)
++ dev_err(dev, "Can't get spdif fast domain clockt\n");
++ return PTR_ERR(priv->fast);
++ }
++
++ priv->iface = devm_clk_get(dev, "iface");
++ if (IS_ERR(priv->iface)) {
++ if (PTR_ERR(priv->iface) != -EPROBE_DEFER)
++ dev_err(dev,
++ "Can't get the dai clock gate\n");
++ return PTR_ERR(priv->iface);
++ }
++
++ priv->mclk_i958 = devm_clk_get(dev, "mclk_i958");
++ if (IS_ERR(priv->mclk_i958)) {
++ if (PTR_ERR(priv->mclk_i958) != -EPROBE_DEFER)
++ dev_err(dev, "Can't get the spdif master clock\n");
++ return PTR_ERR(priv->mclk_i958);
++ }
++
++ /*
++ * TODO: the spdif dai can also get its data from the i2s fifo.
++ * For this use-case, the DAI driver will need to get the i2s master
++ * clock in order to reparent the spdif clock from cts_mclk_i958 to
++ * cts_amclk
++ */
++
++ priv->mclk = devm_clk_get(dev, "mclk");
++ if (IS_ERR(priv->mclk)) {
++ if (PTR_ERR(priv->mclk) != -EPROBE_DEFER)
++ dev_err(dev, "Can't get the spdif input mux clock\n");
++ return PTR_ERR(priv->mclk);
++ }
++
++ return devm_snd_soc_register_component(dev, &meson_spdif_dai_component,
++ &meson_spdif_dai, 1);
++}
++
++static const struct of_device_id meson_spdif_dai_of_match[] = {
++ { .compatible = "amlogic,meson-spdif-dai", },
++ { .compatible = "amlogic,meson-gxbb-spdif-dai", },
++ { .compatible = "amlogic,meson-gxl-spdif-dai", },
++ {}
++};
++MODULE_DEVICE_TABLE(of, meson_spdif_dai_of_match);
++
++static struct platform_driver meson_spdif_dai_pdrv = {
++ .probe = meson_spdif_dai_probe,
++ .driver = {
++ .name = DRV_NAME,
++ .of_match_table = meson_spdif_dai_of_match,
++ },
++};
++module_platform_driver(meson_spdif_dai_pdrv);
++
++MODULE_DESCRIPTION("Meson spdif DAI ASoC Driver");
++MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
++MODULE_LICENSE("GPL v2");
diff --git a/testing/linux-amlogic/0012-ARM64-defconfig-enable-audio-support-for-meson-SoCs-.patch b/testing/linux-amlogic/0012-ARM64-defconfig-enable-audio-support-for-meson-SoCs-.patch
new file mode 100644
index 0000000000..ff2035ecc8
--- /dev/null
+++ b/testing/linux-amlogic/0012-ARM64-defconfig-enable-audio-support-for-meson-SoCs-.patch
@@ -0,0 +1,29 @@
+From 5ddca63ac5c5d81c6d6a6745670a3f136970eaef Mon Sep 17 00:00:00 2001
+From: Jerome Brunet <jbrunet@baylibre.com>
+Date: Fri, 31 Mar 2017 15:55:03 +0200
+Subject: [PATCH] ARM64: defconfig: enable audio support for meson SoCs as
+ module
+
+Add audio support for meson SoCs. This includes the audio core
+driver and the i2s and spdif output interfaces
+
+Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
+---
+ arch/arm64/configs/defconfig | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
+index 2584605..ae1f774 100644
+--- a/arch/arm64/configs/defconfig
++++ b/arch/arm64/configs/defconfig
+@@ -451,6 +451,10 @@ CONFIG_SOUND=y
+ CONFIG_SND=y
+ CONFIG_SND_SOC=y
+ CONFIG_SND_BCM2835_SOC_I2S=m
++CONFIG_SND_SOC_MESON=m
++CONFIG_SND_SOC_MESON_I2S=m
++CONFIG_SND_SOC_MESON_SPDIF=m
++CONFIG_SND_SOC_RCAR=y
+ CONFIG_SND_SOC_SAMSUNG=y
+ CONFIG_SND_SOC_RCAR=m
+ CONFIG_SND_SOC_AK4613=m
diff --git a/testing/linux-amlogic/0013-ARM64-dts-meson-gx-add-audio-controller-nodes.patch b/testing/linux-amlogic/0013-ARM64-dts-meson-gx-add-audio-controller-nodes.patch
new file mode 100644
index 0000000000..f58d3ef3ca
--- /dev/null
+++ b/testing/linux-amlogic/0013-ARM64-dts-meson-gx-add-audio-controller-nodes.patch
@@ -0,0 +1,186 @@
+From f4d7ad156ad2253d5ec3e79ea36309e27b8fabc7 Mon Sep 17 00:00:00 2001
+From: Jerome Brunet <jbrunet@baylibre.com>
+Date: Thu, 30 Mar 2017 15:19:04 +0200
+Subject: [PATCH] ARM64: dts: meson-gx: add audio controller nodes
+
+Add audio controller nodes for Amlogic meson gxbb and gxl.
+This includes the audio-core node, the i2s and spdif DAIs, i2s and spdif
+aiu DMAs.
+
+Audio on this SoC family is still a work in progress. More nodes are likely
+to be added later on (pcm DAIs, input DMAs, etc ...)
+
+Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
+---
+ arch/arm64/boot/dts/amlogic/meson-gx.dtsi | 35 ++++++++++++++++++++++++++
+ arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi | 39 +++++++++++++++++++++++++++++
+ arch/arm64/boot/dts/amlogic/meson-gxl.dtsi | 38 ++++++++++++++++++++++++++++
+ 3 files changed, 112 insertions(+)
+
+diff --git a/arch/arm64/boot/dts/amlogic/meson-gx.dtsi b/arch/arm64/boot/dts/amlogic/meson-gx.dtsi
+index b8dc4db..6b64b63 100644
+--- a/arch/arm64/boot/dts/amlogic/meson-gx.dtsi
++++ b/arch/arm64/boot/dts/amlogic/meson-gx.dtsi
+@@ -203,6 +203,41 @@
+ #reset-cells = <1>;
+ };
+
++ audio: audio@5400 {
++ compatible = "amlogic,meson-audio-core";
++ reg = <0x0 0x5400 0x0 0x2ac>,
++ <0x0 0xa000 0x0 0x304>;
++ reg-names = "aiu", "audin";
++ status = "disabled";
++
++ aiu_i2s_dma: aiu_i2s_dma {
++ #sound-dai-cells = <0>;
++ compatible = "amlogic,meson-aiu-i2s-dma";
++ interrupts = <GIC_SPI 48 IRQ_TYPE_EDGE_RISING>;
++ status = "disabled";
++ };
++
++ aiu_spdif_dma: aiu_spdif_dma {
++ #sound-dai-cells = <0>;
++ compatible = "amlogic,meson-aiu-spdif-dma";
++ interrupts = <GIC_SPI 50 IRQ_TYPE_EDGE_RISING>;
++ status = "disabled";
++ };
++
++ i2s_dai: i2s_dai {
++ #sound-dai-cells = <0>;
++ compatible = "amlogic,meson-i2s-dai";
++ status = "disabled";
++ };
++
++ spdif_dai: spdif_dai {
++ #sound-dai-cells = <0>;
++ compatible = "amlogic,meson-spdif-dai";
++ status = "disabled";
++ };
++
++ };
++
+ uart_A: serial@84c0 {
+ compatible = "amlogic,meson-gx-uart";
+ reg = <0x0 0x84c0 0x0 0x18>;
+diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi
+index 98cbba6..7913249 100644
+--- a/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi
++++ b/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi
+@@ -659,6 +659,35 @@
+ };
+ };
+
++&audio {
++ clocks = <&clkc CLKID_AIU>,
++ <&clkc CLKID_AIU_GLUE>,
++ <&clkc CLKID_I2S_SPDIF>;
++ clock-names = "aiu_top", "aiu_glue", "audin";
++ resets = <&reset RESET_AIU>,
++ <&reset RESET_AUDIN>;
++ reset-names = "aiu", "audin";
++};
++
++&aiu_i2s_dma {
++ clocks = <&clkc CLKID_I2S_OUT>;
++ clock-names = "fast";
++};
++
++&aiu_spdif_dma {
++ clocks = <&clkc CLKID_IEC958>;
++ clock-names = "fast";
++
++};
++
++&i2s_dai {
++ clocks = <&clkc CLKID_I2S_OUT>,
++ <&clkc CLKID_MIXER_IFACE>,
++ <&clkc CLKID_AOCLK_GATE>,
++ <&clkc CLKID_CTS_AMCLK>;
++ clock-names = "fast", "iface", "bclks", "mclk";
++};
++
+ &pwrc_vpu {
+ resets = <&reset RESET_VIU>,
+ <&reset RESET_VENC>,
+@@ -741,6 +770,15 @@
+ num-cs = <1>;
+ };
+
++&spdif_dai {
++ clocks = <&clkc CLKID_IEC958>,
++ <&clkc CLKID_IEC958_GATE>,
++ <&clkc CLKID_CTS_MCLK_I958>,
++ <&clkc CLKID_CTS_AMCLK>,
++ <&clkc CLKID_CTS_I958>;
++ clock-names = "fast", "iface", "mclk_i958", "mclk_i2s", "mclk";
++};
++
+ &spifc {
+ clocks = <&clkc CLKID_SPI>;
+ };
+@@ -774,3 +812,4 @@
+ compatible = "amlogic,meson-gxbb-vpu", "amlogic,meson-gx-vpu";
+ power-domains = <&pwrc_vpu>;
+ };
++
+diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
+index c87a80e..20922cd 100644
+--- a/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
++++ b/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
+@@ -660,6 +660,34 @@
+ };
+ };
+
++&audio {
++ clocks = <&clkc CLKID_AIU>,
++ <&clkc CLKID_AIU_GLUE>,
++ <&clkc CLKID_I2S_SPDIF>;
++ clock-names = "aiu_top", "aiu_glue", "audin";
++ resets = <&reset RESET_AIU>,
++ <&reset RESET_AUDIN>;
++ reset-names = "aiu", "audin";
++};
++
++&aiu_i2s_dma {
++ clocks = <&clkc CLKID_I2S_OUT>;
++ clock-names = "fast";
++};
++
++&aiu_spdif_dma {
++ clocks = <&clkc CLKID_IEC958>;
++ clock-names = "fast";
++};
++
++&i2s_dai {
++ clocks = <&clkc CLKID_I2S_OUT>,
++ <&clkc CLKID_MIXER_IFACE>,
++ <&clkc CLKID_AOCLK_GATE>,
++ <&clkc CLKID_CTS_AMCLK>;
++ clock-names = "fast", "iface", "bclks", "mclk";
++};
++
+ &pwrc_vpu {
+ resets = <&reset RESET_VIU>,
+ <&reset RESET_VENC>,
+@@ -742,6 +770,15 @@
+ num-cs = <1>;
+ };
+
++&spdif_dai {
++ clocks = <&clkc CLKID_IEC958>,
++ <&clkc CLKID_IEC958_GATE>,
++ <&clkc CLKID_CTS_MCLK_I958>,
++ <&clkc CLKID_CTS_AMCLK>,
++ <&clkc CLKID_CTS_I958>;
++ clock-names = "fast", "iface", "mclk_i958", "mclk_i2s", "mclk";
++};
++
+ &spifc {
+ clocks = <&clkc CLKID_SPI>;
+ };
+@@ -775,3 +812,4 @@
+ compatible = "amlogic,meson-gxl-vpu", "amlogic,meson-gx-vpu";
+ power-domains = <&pwrc_vpu>;
+ };
++
diff --git a/testing/linux-amlogic/0014-snd-meson-activate-HDMI-audio-path.patch b/testing/linux-amlogic/0014-snd-meson-activate-HDMI-audio-path.patch
new file mode 100644
index 0000000000..26dedf3d20
--- /dev/null
+++ b/testing/linux-amlogic/0014-snd-meson-activate-HDMI-audio-path.patch
@@ -0,0 +1,52 @@
+From 69d2f200d91fbd48e2388a8c5346f10d889a2928 Mon Sep 17 00:00:00 2001
+From: Jerome Brunet <jbrunet@baylibre.com>
+Date: Fri, 7 Jul 2017 17:39:21 +0200
+Subject: [PATCH] snd: meson: activate HDMI audio path
+
+Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
+---
+ sound/soc/meson/i2s-dai.c | 22 ++++++++++++++++++++++
+ 1 file changed, 22 insertions(+)
+
+diff --git a/sound/soc/meson/i2s-dai.c b/sound/soc/meson/i2s-dai.c
+index 1008af8..63fe098 100644
+--- a/sound/soc/meson/i2s-dai.c
++++ b/sound/soc/meson/i2s-dai.c
+@@ -56,8 +56,19 @@ struct meson_i2s_dai {
+ #define AIU_CLK_CTRL_ALRCLK_RIGHT_J (2 << 8)
+ #define AIU_CLK_CTRL_MORE_I2S_DIV_MASK GENMASK(5, 0)
+ #define AIU_CLK_CTRL_MORE_I2S_DIV(div) (((div) - 1) << 0)
++#define AIU_CLK_CTRL_MORE_HDMI_TX_SEL_MASK BIT(6)
++#define AIU_CLK_CTRL_MORE_HDMI_TX_I958_CLK (0 << 6)
++#define AIU_CLK_CTRL_MORE_HDMI_TX_INT_CLK (1 << 6)
+ #define AIU_CODEC_DAC_LRCLK_CTRL_DIV_MASK GENMASK(11, 0)
+ #define AIU_CODEC_DAC_LRCLK_CTRL_DIV(div) (((div) - 1) << 0)
++#define AIU_HDMI_CLK_DATA_CTRL_CLK_SEL_MASK GENMASK(1, 0)
++#define AIU_HDMI_CLK_DATA_CTRL_CLK_DISABLE (0 << 0)
++#define AIU_HDMI_CLK_DATA_CTRL_CLK_PCM (1 << 0)
++#define AIU_HDMI_CLK_DATA_CTRL_CLK_I2S (2 << 0)
++#define AIU_HDMI_CLK_DATA_CTRL_DATA_SEL_MASK GENMASK(5, 4)
++#define AIU_HDMI_CLK_DATA_CTRL_DATA_MUTE (0 << 4)
++#define AIU_HDMI_CLK_DATA_CTRL_DATA_PCM (1 << 4)
++#define AIU_HDMI_CLK_DATA_CTRL_DATA_I2S (2 << 4)
+ #define AIU_I2S_DAC_CFG_PAYLOAD_SIZE_MASK GENMASK(1, 0)
+ #define AIU_I2S_DAC_CFG_AOCLK_32 (0 << 0)
+ #define AIU_I2S_DAC_CFG_AOCLK_48 (2 << 0)
+@@ -221,6 +232,17 @@ static int meson_i2s_dai_hw_params(struct snd_pcm_substream *substream,
+ return ret;
+ }
+
++ /* Quick and dirty hack for HDMI */
++ regmap_update_bits(priv->core->aiu, AIU_HDMI_CLK_DATA_CTRL,
++ AIU_HDMI_CLK_DATA_CTRL_CLK_SEL_MASK |
++ AIU_HDMI_CLK_DATA_CTRL_DATA_SEL_MASK,
++ AIU_HDMI_CLK_DATA_CTRL_CLK_I2S |
++ AIU_HDMI_CLK_DATA_CTRL_DATA_I2S);
++
++ regmap_update_bits(priv->core->aiu, AIU_CLK_CTRL_MORE,
++ AIU_CLK_CTRL_MORE_HDMI_TX_SEL_MASK,
++ AIU_CLK_CTRL_MORE_HDMI_TX_INT_CLK);
++
+ return 0;
+ }
+
diff --git a/testing/linux-amlogic/0015-drm-meson-select-dw-hdmi-i2s-audio-for-meson-hdmi.patch b/testing/linux-amlogic/0015-drm-meson-select-dw-hdmi-i2s-audio-for-meson-hdmi.patch
new file mode 100644
index 0000000000..cb88ebaf11
--- /dev/null
+++ b/testing/linux-amlogic/0015-drm-meson-select-dw-hdmi-i2s-audio-for-meson-hdmi.patch
@@ -0,0 +1,19 @@
+From 223d7ef1a49981c597094e8519e150108cba9ef9 Mon Sep 17 00:00:00 2001
+From: Jerome Brunet <jbrunet@baylibre.com>
+Date: Tue, 14 Feb 2017 19:18:04 +0100
+Subject: [PATCH] drm/meson: select dw-hdmi i2s audio for meson hdmi
+
+Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
+---
+ drivers/gpu/drm/meson/Kconfig | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/drivers/gpu/drm/meson/Kconfig b/drivers/gpu/drm/meson/Kconfig
+index 3ce51d8..02d400b 100644
+--- a/drivers/gpu/drm/meson/Kconfig
++++ b/drivers/gpu/drm/meson/Kconfig
+@@ -13,3 +13,4 @@ config DRM_MESON_DW_HDMI
+ depends on DRM_MESON
+ default y if DRM_MESON
+ select DRM_DW_HDMI
++ select DRM_DW_HDMI_I2S_AUDIO
diff --git a/testing/linux-amlogic/0016-ARM64-dts-meson-gx-add-sound-dai-cells-to-HDMI-node.patch b/testing/linux-amlogic/0016-ARM64-dts-meson-gx-add-sound-dai-cells-to-HDMI-node.patch
new file mode 100644
index 0000000000..a5e4fab7a5
--- /dev/null
+++ b/testing/linux-amlogic/0016-ARM64-dts-meson-gx-add-sound-dai-cells-to-HDMI-node.patch
@@ -0,0 +1,35 @@
+From 00ce6fbb804c6aaecd3bde8f2978d091fbc0546c Mon Sep 17 00:00:00 2001
+From: Jerome Brunet <jbrunet@baylibre.com>
+Date: Wed, 20 Sep 2017 18:01:26 +0200
+Subject: [PATCH] ARM64: dts: meson-gx: add sound-dai-cells to HDMI node
+
+Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
+---
+ arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi | 1 +
+ arch/arm64/boot/dts/amlogic/meson-gxl.dtsi | 1 +
+ 2 files changed, 2 insertions(+)
+
+diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi
+index 7913249..2a4d506 100644
+--- a/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi
++++ b/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi
+@@ -305,6 +305,7 @@
+ <&clkc CLKID_CLK81>,
+ <&clkc CLKID_GCLK_VENCI_INT0>;
+ clock-names = "isfr", "iahb", "venci";
++ #sound-dai-cells = <0>;
+ };
+
+ &sysctrl {
+diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
+index 20922cd..9f4b618 100644
+--- a/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
++++ b/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
+@@ -257,6 +257,7 @@
+ <&clkc CLKID_CLK81>,
+ <&clkc CLKID_GCLK_VENCI_INT0>;
+ clock-names = "isfr", "iahb", "venci";
++ #sound-dai-cells = <0>;
+ };
+
+ &sysctrl {
diff --git a/testing/linux-amlogic/0017-ARM64-dts-meson-activate-hdmi-audio-HDMI-enabled-boa.patch b/testing/linux-amlogic/0017-ARM64-dts-meson-activate-hdmi-audio-HDMI-enabled-boa.patch
new file mode 100644
index 0000000000..e02abbfe21
--- /dev/null
+++ b/testing/linux-amlogic/0017-ARM64-dts-meson-activate-hdmi-audio-HDMI-enabled-boa.patch
@@ -0,0 +1,860 @@
+From 2df1a3a93bc1ce2d04fa0f0743c9c30195c7057a Mon Sep 17 00:00:00 2001
+From: Jerome Brunet <jbrunet@baylibre.com>
+Date: Wed, 20 Sep 2017 18:10:08 +0200
+Subject: [PATCH] ARM64: dts: meson: activate hdmi audio HDMI enabled boards
+
+This patch activate audio over HDMI on selected boards
+
+Please note that this audio support is based on WIP changes
+This should be considered as preview and it does not reflect
+the audio I expect to see merged
+
+Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
+Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
+---
+ .../arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi | 45 ++++++++++++++++++++++
+ .../boot/dts/amlogic/meson-gxbb-nanopi-k2.dts | 45 ++++++++++++++++++++++
+ .../boot/dts/amlogic/meson-gxbb-nexbox-a95x.dts | 45 ++++++++++++++++++++++
+ .../arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts | 45 ++++++++++++++++++++++
+ arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi | 45 ++++++++++++++++++++++
+ arch/arm64/boot/dts/amlogic/meson-gxbb-wetek.dtsi | 45 ++++++++++++++++++++++
+ .../dts/amlogic/meson-gxl-s905x-khadas-vim.dts | 45 ++++++++++++++++++++++
+ .../dts/amlogic/meson-gxl-s905x-libretech-cc.dts | 45 ++++++++++++++++++++++
+ .../dts/amlogic/meson-gxl-s905x-nexbox-a95x.dts | 45 ++++++++++++++++++++++
+ .../boot/dts/amlogic/meson-gxl-s905x-p212.dts | 45 ++++++++++++++++++++++
+ .../boot/dts/amlogic/meson-gxm-khadas-vim2.dts | 45 ++++++++++++++++++++++
+ .../arm64/boot/dts/amlogic/meson-gxm-nexbox-a1.dts | 45 ++++++++++++++++++++++
+ 12 files changed, 540 insertions(+)
+
+diff --git a/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi b/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi
+index 88e712e..319512e 100644
+--- a/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi
++++ b/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi
+@@ -95,6 +95,39 @@
+ };
+ };
+ };
++
++ sound {
++ compatible = "simple-audio-card";
++ simple-audio-card,name = "meson-gx-audio";
++
++ assigned-clocks = <&clkc CLKID_MPLL2>,
++ <&clkc CLKID_MPLL0>,
++ <&clkc CLKID_MPLL1>;
++ assigned-clock-parents = <0>, <0>, <0>;
++ assigned-clock-rates = <294912000>,
++ <270950400>,
++ <393216000>;
++
++ simple-audio-card,dai-link@0 {
++ /* HDMI Output */
++ format = "i2s";
++ mclk-fs = <256>;
++ bitclock-master = <&i2s_dai>;
++ frame-master = <&i2s_dai>;
++
++ plat {
++ sound-dai = <&aiu_i2s_dma>;
++ };
++
++ cpu {
++ sound-dai = <&i2s_dai>;
++ };
++
++ codec {
++ sound-dai = <&hdmi_tx>;
++ };
++ };
++ };
+ };
+
+ &cec_AO {
+@@ -104,6 +137,14 @@
+ hdmi-phandle = <&hdmi_tx>;
+ };
+
++&audio {
++ status = "okay";
++};
++
++&aiu_i2s_dma {
++ status = "okay";
++};
++
+ &cvbs_vdac_port {
+ cvbs_vdac_out: endpoint {
+ remote-endpoint = <&cvbs_connector_in>;
+@@ -126,6 +167,10 @@
+ };
+ };
+
++&i2s_dai {
++ status = "okay";
++};
++
+ &ir {
+ status = "okay";
+ pinctrl-0 = <&remote_input_ao_pins>;
+diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-nanopi-k2.dts b/arch/arm64/boot/dts/amlogic/meson-gxbb-nanopi-k2.dts
+index cbe99bd..5b10de9 100644
+--- a/arch/arm64/boot/dts/amlogic/meson-gxbb-nanopi-k2.dts
++++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-nanopi-k2.dts
+@@ -88,6 +88,39 @@
+ clock-names = "ext_clock";
+ };
+
++ sound {
++ compatible = "simple-audio-card";
++ simple-audio-card,name = "meson-gx-audio";
++
++ assigned-clocks = <&clkc CLKID_MPLL2>,
++ <&clkc CLKID_MPLL0>,
++ <&clkc CLKID_MPLL1>;
++ assigned-clock-parents = <0>, <0>, <0>;
++ assigned-clock-rates = <294912000>,
++ <270950400>,
++ <393216000>;
++
++ simple-audio-card,dai-link@0 {
++ /* HDMI Output */
++ format = "i2s";
++ mclk-fs = <256>;
++ bitclock-master = <&i2s_dai>;
++ frame-master = <&i2s_dai>;
++
++ plat {
++ sound-dai = <&aiu_i2s_dma>;
++ };
++
++ cpu {
++ sound-dai = <&i2s_dai>;
++ };
++
++ codec {
++ sound-dai = <&hdmi_tx>;
++ };
++ };
++ };
++
+ vcc1v8: regulator-vcc1v8 {
+ compatible = "regulator-fixed";
+ regulator-name = "VCC1.8V";
+@@ -131,6 +164,14 @@
+ };
+ };
+
++&audio {
++ status = "okay";
++};
++
++&aiu_i2s_dma {
++ status = "okay";
++};
++
+ &cec_AO {
+ status = "okay";
+ pinctrl-0 = <&ao_cec_pins>;
+@@ -185,6 +226,10 @@
+ };
+ };
+
++&i2s_dai {
++ status = "okay";
++};
++
+ &ir {
+ status = "okay";
+ pinctrl-0 = <&remote_input_ao_pins>;
+diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-nexbox-a95x.dts b/arch/arm64/boot/dts/amlogic/meson-gxbb-nexbox-a95x.dts
+index 4cf7f6e..ff87bdc 100644
+--- a/arch/arm64/boot/dts/amlogic/meson-gxbb-nexbox-a95x.dts
++++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-nexbox-a95x.dts
+@@ -119,6 +119,39 @@
+ clock-names = "ext_clock";
+ };
+
++ sound {
++ compatible = "simple-audio-card";
++ simple-audio-card,name = "meson-gx-audio";
++
++ assigned-clocks = <&clkc CLKID_MPLL2>,
++ <&clkc CLKID_MPLL0>,
++ <&clkc CLKID_MPLL1>;
++ assigned-clock-parents = <0>, <0>, <0>;
++ assigned-clock-rates = <294912000>,
++ <270950400>,
++ <393216000>;
++
++ simple-audio-card,dai-link@0 {
++ /* HDMI Output */
++ format = "i2s";
++ mclk-fs = <256>;
++ bitclock-master = <&i2s_dai>;
++ frame-master = <&i2s_dai>;
++
++ plat {
++ sound-dai = <&aiu_i2s_dma>;
++ };
++
++ cpu {
++ sound-dai = <&i2s_dai>;
++ };
++
++ codec {
++ sound-dai = <&hdmi_tx>;
++ };
++ };
++ };
++
+ cvbs-connector {
+ compatible = "composite-video-connector";
+
+@@ -154,6 +187,14 @@
+ hdmi-phandle = <&hdmi_tx>;
+ };
+
++&audio {
++ status = "okay";
++};
++
++&aiu_i2s_dma {
++ status = "okay";
++};
++
+ &ethmac {
+ status = "okay";
+ pinctrl-0 = <&eth_rmii_pins>;
+@@ -190,6 +231,10 @@
+ };
+ };
+
++&i2s_dai {
++ status = "okay";
++};
++
+ &ir {
+ status = "okay";
+ pinctrl-0 = <&remote_input_ao_pins>;
+diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts b/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts
+index 54954b3..3da3309 100644
+--- a/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts
++++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts
+@@ -110,6 +110,39 @@
+ };
+ };
+ };
++
++ sound {
++ compatible = "simple-audio-card";
++ simple-audio-card,name = "meson-gx-audio";
++
++ assigned-clocks = <&clkc CLKID_MPLL2>,
++ <&clkc CLKID_MPLL0>,
++ <&clkc CLKID_MPLL1>;
++ assigned-clock-parents = <0>, <0>, <0>;
++ assigned-clock-rates = <294912000>,
++ <270950400>,
++ <393216000>;
++
++ simple-audio-card,dai-link@0 {
++ /* HDMI Output */
++ format = "i2s";
++ mclk-fs = <256>;
++ bitclock-master = <&i2s_dai>;
++ frame-master = <&i2s_dai>;
++
++ plat {
++ sound-dai = <&aiu_i2s_dma>;
++ };
++
++ cpu {
++ sound-dai = <&i2s_dai>;
++ };
++
++ codec {
++ sound-dai = <&hdmi_tx>;
++ };
++ };
++ };
+ };
+
+ &cec_AO {
+@@ -119,6 +152,14 @@
+ hdmi-phandle = <&hdmi_tx>;
+ };
+
++&audio {
++ status = "okay";
++};
++
++&aiu_i2s_dma {
++ status = "okay";
++};
++
+ &ethmac {
+ status = "okay";
+ pinctrl-0 = <&eth_rgmii_pins>;
+@@ -181,6 +222,10 @@
+ pinctrl-names = "default";
+ };
+
++&i2s_dai {
++ status = "okay";
++};
++
+ &ir {
+ status = "okay";
+ pinctrl-0 = <&remote_input_ao_pins>;
+diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi
+index ce86226..84eb93b 100644
+--- a/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi
++++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi
+@@ -113,6 +113,39 @@
+ };
+ };
+ };
++
++ sound {
++ compatible = "simple-audio-card";
++ simple-audio-card,name = "meson-gx-audio";
++
++ assigned-clocks = <&clkc CLKID_MPLL2>,
++ <&clkc CLKID_MPLL0>,
++ <&clkc CLKID_MPLL1>;
++ assigned-clock-parents = <0>, <0>, <0>;
++ assigned-clock-rates = <294912000>,
++ <270950400>,
++ <393216000>;
++
++ simple-audio-card,dai-link@0 {
++ /* HDMI Output */
++ format = "i2s";
++ mclk-fs = <256>;
++ bitclock-master = <&i2s_dai>;
++ frame-master = <&i2s_dai>;
++
++ plat {
++ sound-dai = <&aiu_i2s_dma>;
++ };
++
++ cpu {
++ sound-dai = <&i2s_dai>;
++ };
++
++ codec {
++ sound-dai = <&hdmi_tx>;
++ };
++ };
++ };
+ };
+
+ &cec_AO {
+@@ -122,6 +155,14 @@
+ hdmi-phandle = <&hdmi_tx>;
+ };
+
++&audio {
++ status = "okay";
++};
++
++&aiu_i2s_dma {
++ status = "okay";
++};
++
+ &cvbs_vdac_port {
+ cvbs_vdac_out: endpoint {
+ remote-endpoint = <&cvbs_connector_in>;
+@@ -140,6 +181,10 @@
+ };
+ };
+
++&i2s_dai {
++ status = "okay";
++};
++
+ &ir {
+ status = "okay";
+ pinctrl-0 = <&remote_input_ao_pins>;
+diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-wetek.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxbb-wetek.dtsi
+index 70325b2..7d1f172 100644
+--- a/arch/arm64/boot/dts/amlogic/meson-gxbb-wetek.dtsi
++++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-wetek.dtsi
+@@ -105,6 +105,47 @@
+ };
+ };
+ };
++
++ sound {
++ compatible = "simple-audio-card";
++ simple-audio-card,name = "meson-gx-audio";
++
++ assigned-clocks = <&clkc CLKID_MPLL2>,
++ <&clkc CLKID_MPLL0>,
++ <&clkc CLKID_MPLL1>;
++ assigned-clock-parents = <0>, <0>, <0>;
++ assigned-clock-rates = <294912000>,
++ <270950400>,
++ <393216000>;
++
++ simple-audio-card,dai-link@0 {
++ /* HDMI Output */
++ format = "i2s";
++ mclk-fs = <256>;
++ bitclock-master = <&i2s_dai>;
++ frame-master = <&i2s_dai>;
++
++ plat {
++ sound-dai = <&aiu_i2s_dma>;
++ };
++
++ cpu {
++ sound-dai = <&i2s_dai>;
++ };
++
++ codec {
++ sound-dai = <&hdmi_tx>;
++ };
++ };
++ };
++};
++
++&audio {
++ status = "okay";
++};
++
++&aiu_i2s_dma {
++ status = "okay";
+ };
+
+ &cec_AO {
+@@ -159,6 +200,10 @@
+ };
+ };
+
++&i2s_dai {
++ status = "okay";
++};
++
+ &ir {
+ status = "okay";
+ pinctrl-0 = <&remote_input_ao_pins>;
+diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-khadas-vim.dts b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-khadas-vim.dts
+index d32cf38..f053595 100644
+--- a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-khadas-vim.dts
++++ b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-khadas-vim.dts
+@@ -65,6 +65,39 @@
+ };
+ };
+ };
++
++ sound {
++ compatible = "simple-audio-card";
++ simple-audio-card,name = "meson-gx-audio";
++
++ assigned-clocks = <&clkc CLKID_MPLL2>,
++ <&clkc CLKID_MPLL0>,
++ <&clkc CLKID_MPLL1>;
++ assigned-clock-parents = <0>, <0>, <0>;
++ assigned-clock-rates = <294912000>,
++ <270950400>,
++ <393216000>;
++
++ simple-audio-card,dai-link@0 {
++ /* HDMI Output */
++ format = "i2s";
++ mclk-fs = <256>;
++ bitclock-master = <&i2s_dai>;
++ frame-master = <&i2s_dai>;
++
++ plat {
++ sound-dai = <&aiu_i2s_dma>;
++ };
++
++ cpu {
++ sound-dai = <&i2s_dai>;
++ };
++
++ codec {
++ sound-dai = <&hdmi_tx>;
++ };
++ };
++ };
+ };
+
+ &cec_AO {
+@@ -74,6 +107,14 @@
+ hdmi-phandle = <&hdmi_tx>;
+ };
+
++&audio {
++ status = "okay";
++};
++
++&aiu_i2s_dma {
++ status = "okay";
++};
++
+ &hdmi_tx {
+ status = "okay";
+ pinctrl-0 = <&hdmi_hpd_pins>, <&hdmi_i2c_pins>;
+@@ -86,6 +127,10 @@
+ };
+ };
+
++&i2s_dai {
++ status = "okay";
++};
++
+ &i2c_A {
+ status = "okay";
+ pinctrl-0 = <&i2c_a_pins>;
+diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-libretech-cc.dts b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-libretech-cc.dts
+index f63bceb..f56969e 100644
+--- a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-libretech-cc.dts
++++ b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-libretech-cc.dts
+@@ -84,6 +84,39 @@
+ regulator-always-on;
+ };
+
++ sound {
++ compatible = "simple-audio-card";
++ simple-audio-card,name = "meson-gx-audio";
++
++ assigned-clocks = <&clkc CLKID_MPLL2>,
++ <&clkc CLKID_MPLL0>,
++ <&clkc CLKID_MPLL1>;
++ assigned-clock-parents = <0>, <0>, <0>;
++ assigned-clock-rates = <294912000>,
++ <270950400>,
++ <393216000>;
++
++ simple-audio-card,dai-link@0 {
++ /* HDMI Output */
++ format = "i2s";
++ mclk-fs = <256>;
++ bitclock-master = <&i2s_dai>;
++ frame-master = <&i2s_dai>;
++
++ plat {
++ sound-dai = <&aiu_i2s_dma>;
++ };
++
++ cpu {
++ sound-dai = <&i2s_dai>;
++ };
++
++ codec {
++ sound-dai = <&hdmi_tx>;
++ };
++ };
++ };
++
+ vcc_3v3: regulator-vcc_3v3 {
+ compatible = "regulator-fixed";
+ regulator-name = "VCC_3V3";
+@@ -130,6 +163,14 @@
+ hdmi-phandle = <&hdmi_tx>;
+ };
+
++&audio {
++ status = "okay";
++};
++
++&aiu_i2s_dma {
++ status = "okay";
++};
++
+ &cvbs_vdac_port {
+ cvbs_vdac_out: endpoint {
+ remote-endpoint = <&cvbs_connector_in>;
+@@ -151,6 +192,10 @@
+ pinctrl-names = "default";
+ };
+
++&i2s_dai {
++ status = "okay";
++};
++
+ &hdmi_tx {
+ status = "okay";
+ pinctrl-0 = <&hdmi_hpd_pins>, <&hdmi_i2c_pins>;
+diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-nexbox-a95x.dts b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-nexbox-a95x.dts
+index 6739697..e3e777f 100644
+--- a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-nexbox-a95x.dts
++++ b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-nexbox-a95x.dts
+@@ -102,6 +102,39 @@
+ };
+ };
+ };
++
++ sound {
++ compatible = "simple-audio-card";
++ simple-audio-card,name = "meson-gx-audio";
++
++ assigned-clocks = <&clkc CLKID_MPLL2>,
++ <&clkc CLKID_MPLL0>,
++ <&clkc CLKID_MPLL1>;
++ assigned-clock-parents = <0>, <0>, <0>;
++ assigned-clock-rates = <294912000>,
++ <270950400>,
++ <393216000>;
++
++ simple-audio-card,dai-link@0 {
++ /* HDMI Output */
++ format = "i2s";
++ mclk-fs = <256>;
++ bitclock-master = <&i2s_dai>;
++ frame-master = <&i2s_dai>;
++
++ plat {
++ sound-dai = <&aiu_i2s_dma>;
++ };
++
++ cpu {
++ sound-dai = <&i2s_dai>;
++ };
++
++ codec {
++ sound-dai = <&hdmi_tx>;
++ };
++ };
++ };
+ };
+
+ &cec_AO {
+@@ -111,6 +144,14 @@
+ hdmi-phandle = <&hdmi_tx>;
+ };
+
++&audio {
++ status = "okay";
++};
++
++&aiu_i2s_dma {
++ status = "okay";
++};
++
+ &cvbs_vdac_port {
+ cvbs_vdac_out: endpoint {
+ remote-endpoint = <&cvbs_connector_in>;
+@@ -135,6 +176,10 @@
+ };
+ };
+
++&i2s_dai {
++ status = "okay";
++};
++
+ &ir {
+ status = "okay";
+ pinctrl-0 = <&remote_input_ao_pins>;
+diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dts b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dts
+index 5896e8a..f8c66a7 100644
+--- a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dts
++++ b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dts
+@@ -32,6 +32,39 @@
+ };
+ };
+ };
++
++ sound {
++ compatible = "simple-audio-card";
++ simple-audio-card,name = "meson-gx-audio";
++
++ assigned-clocks = <&clkc CLKID_MPLL2>,
++ <&clkc CLKID_MPLL0>,
++ <&clkc CLKID_MPLL1>;
++ assigned-clock-parents = <0>, <0>, <0>;
++ assigned-clock-rates = <294912000>,
++ <270950400>,
++ <393216000>;
++
++ simple-audio-card,dai-link@0 {
++ /* HDMI Output */
++ format = "i2s";
++ mclk-fs = <256>;
++ bitclock-master = <&i2s_dai>;
++ frame-master = <&i2s_dai>;
++
++ plat {
++ sound-dai = <&aiu_i2s_dma>;
++ };
++
++ cpu {
++ sound-dai = <&i2s_dai>;
++ };
++
++ codec {
++ sound-dai = <&hdmi_tx>;
++ };
++ };
++ };
+ };
+
+ &cec_AO {
+@@ -41,12 +74,24 @@
+ hdmi-phandle = <&hdmi_tx>;
+ };
+
++&audio {
++ status = "okay";
++};
++
++&aiu_i2s_dma {
++ status = "okay";
++};
++
+ &cvbs_vdac_port {
+ cvbs_vdac_out: endpoint {
+ remote-endpoint = <&cvbs_connector_in>;
+ };
+ };
+
++&i2s_dai {
++ status = "okay";
++};
++
+ &hdmi_tx {
+ status = "okay";
+ pinctrl-0 = <&hdmi_hpd_pins>, <&hdmi_i2c_pins>;
+diff --git a/arch/arm64/boot/dts/amlogic/meson-gxm-khadas-vim2.dts b/arch/arm64/boot/dts/amlogic/meson-gxm-khadas-vim2.dts
+index 0868da4..ea71261 100644
+--- a/arch/arm64/boot/dts/amlogic/meson-gxm-khadas-vim2.dts
++++ b/arch/arm64/boot/dts/amlogic/meson-gxm-khadas-vim2.dts
+@@ -85,6 +85,39 @@
+ };
+ };
+
++ sound {
++ compatible = "simple-audio-card";
++ simple-audio-card,name = "meson-gx-audio";
++
++ assigned-clocks = <&clkc CLKID_MPLL2>,
++ <&clkc CLKID_MPLL0>,
++ <&clkc CLKID_MPLL1>;
++ assigned-clock-parents = <0>, <0>, <0>;
++ assigned-clock-rates = <294912000>,
++ <270950400>,
++ <393216000>;
++
++ simple-audio-card,dai-link@0 {
++ /* HDMI Output */
++ format = "i2s";
++ mclk-fs = <256>;
++ bitclock-master = <&i2s_dai>;
++ frame-master = <&i2s_dai>;
++
++ plat {
++ sound-dai = <&aiu_i2s_dma>;
++ };
++
++ cpu {
++ sound-dai = <&i2s_dai>;
++ };
++
++ codec {
++ sound-dai = <&hdmi_tx>;
++ };
++ };
++ };
++
+ pwmleds {
+ compatible = "pwm-leds";
+
+@@ -205,6 +238,14 @@
+ hdmi-phandle = <&hdmi_tx>;
+ };
+
++&audio {
++ status = "okay";
++};
++
++&aiu_i2s_dma {
++ status = "okay";
++};
++
+ &cpu0 {
+ #cooling-cells = <2>;
+ };
+@@ -255,6 +296,10 @@
+ };
+ };
+
++&i2s_dai {
++ status = "okay";
++};
++
+ &i2c_A {
+ status = "okay";
+ pinctrl-0 = <&i2c_a_pins>;
+diff --git a/arch/arm64/boot/dts/amlogic/meson-gxm-nexbox-a1.dts b/arch/arm64/boot/dts/amlogic/meson-gxm-nexbox-a1.dts
+index f7a1cff..b9c5e64 100644
+--- a/arch/arm64/boot/dts/amlogic/meson-gxm-nexbox-a1.dts
++++ b/arch/arm64/boot/dts/amlogic/meson-gxm-nexbox-a1.dts
+@@ -75,6 +75,39 @@
+ };
+ };
+ };
++
++ sound {
++ compatible = "simple-audio-card";
++ simple-audio-card,name = "meson-gx-audio";
++
++ assigned-clocks = <&clkc CLKID_MPLL2>,
++ <&clkc CLKID_MPLL0>,
++ <&clkc CLKID_MPLL1>;
++ assigned-clock-parents = <0>, <0>, <0>;
++ assigned-clock-rates = <294912000>,
++ <270950400>,
++ <393216000>;
++
++ simple-audio-card,dai-link@0 {
++ /* HDMI Output */
++ format = "i2s";
++ mclk-fs = <256>;
++ bitclock-master = <&i2s_dai>;
++ frame-master = <&i2s_dai>;
++
++ plat {
++ sound-dai = <&aiu_i2s_dma>;
++ };
++
++ cpu {
++ sound-dai = <&i2s_dai>;
++ };
++
++ codec {
++ sound-dai = <&hdmi_tx>;
++ };
++ };
++ };
+ };
+
+ &cec_AO {
+@@ -84,6 +117,14 @@
+ hdmi-phandle = <&hdmi_tx>;
+ };
+
++&audio {
++ status = "okay";
++};
++
++&aiu_i2s_dma {
++ status = "okay";
++};
++
+ &cvbs_vdac_port {
+ cvbs_vdac_out: endpoint {
+ remote-endpoint = <&cvbs_connector_in>;
+@@ -129,6 +170,10 @@
+ };
+ };
+
++&i2s_dai {
++ status = "okay";
++};
++
+ &ir {
+ status = "okay";
+ pinctrl-0 = <&remote_input_ao_pins>;
diff --git a/testing/linux-amlogic/0018-drm-bridge-dw-hdmi-Use-AUTO-CTS-setup-mode-when-non-.patch b/testing/linux-amlogic/0018-drm-bridge-dw-hdmi-Use-AUTO-CTS-setup-mode-when-non-.patch
new file mode 100644
index 0000000000..fbff6c8d89
--- /dev/null
+++ b/testing/linux-amlogic/0018-drm-bridge-dw-hdmi-Use-AUTO-CTS-setup-mode-when-non-.patch
@@ -0,0 +1,75 @@
+From e282ad866be628951a95d297207c9a5580f4101d Mon Sep 17 00:00:00 2001
+From: Neil Armstrong <narmstrong@baylibre.com>
+Date: Mon, 2 Jul 2018 12:21:55 +0200
+Subject: [PATCH] drm: bridge: dw-hdmi: Use AUTO CTS setup mode when non-AHB
+ audio
+
+Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
+---
+ drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 41 ++++++++++++++++++++-----------
+ 1 file changed, 26 insertions(+), 15 deletions(-)
+
+diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+index 3c136f2b..a68ffbb 100644
+--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
++++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+@@ -430,8 +430,12 @@ static void hdmi_set_cts_n(struct dw_hdmi *hdmi, unsigned int cts,
+ /* nshift factor = 0 */
+ hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_N_SHIFT_MASK, HDMI_AUD_CTS3);
+
+- hdmi_writeb(hdmi, ((cts >> 16) & HDMI_AUD_CTS3_AUDCTS19_16_MASK) |
+- HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3);
++ /* Use Auto CTS mode with CTS is unknown */
++ if (cts)
++ hdmi_writeb(hdmi, ((cts >> 16) & HDMI_AUD_CTS3_AUDCTS19_16_MASK) |
++ HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3);
++ else
++ hdmi_writeb(hdmi, 0, HDMI_AUD_CTS3);
+ hdmi_writeb(hdmi, (cts >> 8) & 0xff, HDMI_AUD_CTS2);
+ hdmi_writeb(hdmi, cts & 0xff, HDMI_AUD_CTS1);
+
+@@ -501,24 +505,31 @@ static void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi,
+ {
+ unsigned long ftdms = pixel_clk;
+ unsigned int n, cts;
++ u8 config3;
+ u64 tmp;
+
+ n = hdmi_compute_n(sample_rate, pixel_clk);
+
+- /*
+- * Compute the CTS value from the N value. Note that CTS and N
+- * can be up to 20 bits in total, so we need 64-bit math. Also
+- * note that our TDMS clock is not fully accurate; it is accurate
+- * to kHz. This can introduce an unnecessary remainder in the
+- * calculation below, so we don't try to warn about that.
+- */
+- tmp = (u64)ftdms * n;
+- do_div(tmp, 128 * sample_rate);
+- cts = tmp;
++ config3 = hdmi_readb(hdmi, HDMI_CONFIG3_ID);
+
+- dev_dbg(hdmi->dev, "%s: fs=%uHz ftdms=%lu.%03luMHz N=%d cts=%d\n",
+- __func__, sample_rate, ftdms / 1000000, (ftdms / 1000) % 1000,
+- n, cts);
++ if (config3 & HDMI_CONFIG3_AHBAUDDMA) {
++ /*
++ * Compute the CTS value from the N value. Note that CTS and N
++ * can be up to 20 bits in total, so we need 64-bit math. Also
++ * note that our TDMS clock is not fully accurate; it is
++ * accurate to kHz. This can introduce an unnecessary remainder
++ * in the calculation below, so we don't try to warn about that.
++ */
++ tmp = (u64)ftdms * n;
++ do_div(tmp, 128 * sample_rate);
++ cts = tmp;
++
++ dev_dbg(hdmi->dev, "%s: fs=%uHz ftdms=%lu.%03luMHz N=%d cts=%d\n",
++ __func__, sample_rate,
++ ftdms / 1000000, (ftdms / 1000) % 1000,
++ n, cts);
++ } else
++ cts = 0;
+
+ spin_lock_irq(&hdmi->audio_lock);
+ hdmi->audio_n = n;
diff --git a/testing/linux-amlogic/0019-drm-meson-Call-drm_crtc_vblank_on-drm_crtc_vblank_of.patch b/testing/linux-amlogic/0019-drm-meson-Call-drm_crtc_vblank_on-drm_crtc_vblank_of.patch
new file mode 100644
index 0000000000..314ff482b8
--- /dev/null
+++ b/testing/linux-amlogic/0019-drm-meson-Call-drm_crtc_vblank_on-drm_crtc_vblank_of.patch
@@ -0,0 +1,36 @@
+From 4a3a6d04d4996ff0a0acc8405fdd0b0347a62138 Mon Sep 17 00:00:00 2001
+From: Neil Armstrong <narmstrong@baylibre.com>
+Date: Wed, 28 Feb 2018 16:07:18 +0100
+Subject: [PATCH] drm/meson: Call drm_crtc_vblank_on / drm_crtc_vblank_off
+
+Make sure that the CRTC code will call the enable/disable_vblank hooks.
+
+Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
+---
+ drivers/gpu/drm/meson/meson_crtc.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/drivers/gpu/drm/meson/meson_crtc.c b/drivers/gpu/drm/meson/meson_crtc.c
+index 0552020..4dd0df0 100644
+--- a/drivers/gpu/drm/meson/meson_crtc.c
++++ b/drivers/gpu/drm/meson/meson_crtc.c
+@@ -102,6 +102,8 @@ static void meson_crtc_atomic_enable(struct drm_crtc *crtc,
+ priv->io_base + _REG(VPP_MISC));
+
+ priv->viu.osd1_enabled = true;
++
++ drm_crtc_vblank_on(crtc);
+ }
+
+ static void meson_crtc_atomic_disable(struct drm_crtc *crtc,
+@@ -110,6 +112,10 @@ static void meson_crtc_atomic_disable(struct drm_crtc *crtc,
+ struct meson_crtc *meson_crtc = to_meson_crtc(crtc);
+ struct meson_drm *priv = meson_crtc->priv;
+
++ DRM_DEBUG_DRIVER("\n");
++
++ drm_crtc_vblank_off(crtc);
++
+ priv->viu.osd1_enabled = false;
+ priv->viu.osd1_commit = false;
+
diff --git a/testing/linux-amlogic/0020-media-platform-meson-ao-cec-make-busy-TX-warning-sil.patch b/testing/linux-amlogic/0020-media-platform-meson-ao-cec-make-busy-TX-warning-sil.patch
new file mode 100644
index 0000000000..81569fc4d3
--- /dev/null
+++ b/testing/linux-amlogic/0020-media-platform-meson-ao-cec-make-busy-TX-warning-sil.patch
@@ -0,0 +1,32 @@
+From 830bb1ab9ee8999566a4d98086590ac824cdeb4e Mon Sep 17 00:00:00 2001
+From: Neil Armstrong <narmstrong@baylibre.com>
+Date: Tue, 10 Jul 2018 15:00:45 +0200
+Subject: [PATCH] media: platform: meson-ao-cec: make busy TX warning silent
+
+Switch to dev_dbg for the busy TX message to avoid having a flood of:
+[ 228.064570] meson-ao-cec c8100100.cec: meson_ao_cec_transmit: busy TX: aborting
+[ 230.368489] meson-ao-cec c8100100.cec: meson_ao_cec_transmit: busy TX: aborting
+[ 234.208655] meson-ao-cec c8100100.cec: meson_ao_cec_transmit: busy TX: aborting
+[ 236.512558] meson-ao-cec c8100100.cec: meson_ao_cec_transmit: busy TX: aborting
+
+This message is only a debug hint and not an error.
+
+Fixes: 7ec2c0f72cb1 ("media: platform: Add Amlogic Meson AO CEC Controller driver")
+Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
+---
+ drivers/media/platform/meson/ao-cec.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/media/platform/meson/ao-cec.c b/drivers/media/platform/meson/ao-cec.c
+index 8040a62..cd4be38 100644
+--- a/drivers/media/platform/meson/ao-cec.c
++++ b/drivers/media/platform/meson/ao-cec.c
+@@ -524,7 +524,7 @@ static int meson_ao_cec_transmit(struct cec_adapter *adap, u8 attempts,
+ return ret;
+
+ if (reg == TX_BUSY) {
+- dev_err(&ao_cec->pdev->dev, "%s: busy TX: aborting\n",
++ dev_dbg(&ao_cec->pdev->dev, "%s: busy TX: aborting\n",
+ __func__);
+ meson_ao_cec_write(ao_cec, CEC_TX_MSG_CMD, TX_ABORT, &ret);
+ }
diff --git a/testing/linux-amlogic/0021-soc-amlogic-add-meson-canvas-driver.patch b/testing/linux-amlogic/0021-soc-amlogic-add-meson-canvas-driver.patch
new file mode 100644
index 0000000000..226a7961a1
--- /dev/null
+++ b/testing/linux-amlogic/0021-soc-amlogic-add-meson-canvas-driver.patch
@@ -0,0 +1,280 @@
+From 8d7247813f02cad15a67980c2631d9ee37d7b76b Mon Sep 17 00:00:00 2001
+From: Maxime Jourdan <maxi.jourdan@wanadoo.fr>
+Date: Wed, 1 Aug 2018 20:51:25 +0200
+Subject: [PATCH] soc: amlogic: add meson-canvas driver
+
+Amlogic SoCs have a repository of 256 canvas which they use to
+describe pixel buffers.
+
+They contain metadata like width, height, block mode, endianness [..]
+
+Many IPs within those SoCs like vdec/vpu rely on those canvas to read/write
+pixels.
+
+Signed-off-by: Maxime Jourdan <maxi.jourdan@wanadoo.fr>
+---
+ drivers/soc/amlogic/Kconfig | 7 ++
+ drivers/soc/amlogic/Makefile | 1 +
+ drivers/soc/amlogic/meson-canvas.c | 182 +++++++++++++++++++++++++++++++
+ include/linux/soc/amlogic/meson-canvas.h | 37 +++++++
+ 4 files changed, 227 insertions(+)
+ create mode 100644 drivers/soc/amlogic/meson-canvas.c
+ create mode 100644 include/linux/soc/amlogic/meson-canvas.h
+
+diff --git a/drivers/soc/amlogic/Kconfig b/drivers/soc/amlogic/Kconfig
+index b04f6e4..5bd0498 100644
+--- a/drivers/soc/amlogic/Kconfig
++++ b/drivers/soc/amlogic/Kconfig
+@@ -1,5 +1,12 @@
+ menu "Amlogic SoC drivers"
+
++config MESON_CANVAS
++ bool "Amlogic Meson Canvas driver"
++ depends on ARCH_MESON || COMPILE_TEST
++ default ARCH_MESON
++ help
++ Say yes to support the canvas IP within Amlogic Meson Soc family.
++
+ config MESON_GX_SOCINFO
+ bool "Amlogic Meson GX SoC Information driver"
+ depends on ARCH_MESON || COMPILE_TEST
+diff --git a/drivers/soc/amlogic/Makefile b/drivers/soc/amlogic/Makefile
+index 8fa3218..0ab16d3 100644
+--- a/drivers/soc/amlogic/Makefile
++++ b/drivers/soc/amlogic/Makefile
+@@ -1,3 +1,4 @@
++obj-$(CONFIG_MESON_CANVAS) += meson-canvas.o
+ obj-$(CONFIG_MESON_GX_SOCINFO) += meson-gx-socinfo.o
+ obj-$(CONFIG_MESON_GX_PM_DOMAINS) += meson-gx-pwrc-vpu.o
+ obj-$(CONFIG_MESON_MX_SOCINFO) += meson-mx-socinfo.o
+diff --git a/drivers/soc/amlogic/meson-canvas.c b/drivers/soc/amlogic/meson-canvas.c
+new file mode 100644
+index 0000000..671eb89
+--- /dev/null
++++ b/drivers/soc/amlogic/meson-canvas.c
+@@ -0,0 +1,182 @@
++/*
++ * Copyright (C) 2018 Maxime Jourdan
++ * Copyright (C) 2016 BayLibre, SAS
++ * Author: Neil Armstrong <narmstrong@baylibre.com>
++ * Copyright (C) 2015 Amlogic, Inc. All rights reserved.
++ * Copyright (C) 2014 Endless Mobile
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <linux/of_address.h>
++#include <linux/platform_device.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/regmap.h>
++#include <linux/mfd/syscon.h>
++#include <linux/soc/amlogic/meson-canvas.h>
++#include <asm/io.h>
++
++#define NUM_CANVAS 256
++
++/* DMC Registers */
++#define DMC_CAV_LUT_DATAL 0x48 /* 0x12 offset in data sheet */
++ #define CANVAS_WIDTH_LBIT 29
++ #define CANVAS_WIDTH_LWID 3
++#define DMC_CAV_LUT_DATAH 0x4c /* 0x13 offset in data sheet */
++ #define CANVAS_WIDTH_HBIT 0
++ #define CANVAS_HEIGHT_BIT 9
++ #define CANVAS_BLKMODE_BIT 24
++#define DMC_CAV_LUT_ADDR 0x50 /* 0x14 offset in data sheet */
++ #define CANVAS_LUT_WR_EN (0x2 << 8)
++ #define CANVAS_LUT_RD_EN (0x1 << 8)
++
++struct meson_canvas {
++ struct device *dev;
++ struct regmap *regmap_dmc;
++ struct mutex lock;
++ u8 used[NUM_CANVAS];
++};
++
++static struct meson_canvas canvas = { 0 };
++
++static int meson_canvas_setup(uint8_t canvas_index, uint32_t addr,
++ uint32_t stride, uint32_t height,
++ unsigned int wrap,
++ unsigned int blkmode,
++ unsigned int endian)
++{
++ struct regmap *regmap = canvas.regmap_dmc;
++ u32 val;
++
++ mutex_lock(&canvas.lock);
++
++ if (!canvas.used[canvas_index]) {
++ dev_err(canvas.dev,
++ "Trying to setup non allocated canvas %u\n",
++ canvas_index);
++ mutex_unlock(&canvas.lock);
++ return -EINVAL;
++ }
++
++ regmap_write(regmap, DMC_CAV_LUT_DATAL,
++ ((addr + 7) >> 3) |
++ (((stride + 7) >> 3) << CANVAS_WIDTH_LBIT));
++
++ regmap_write(regmap, DMC_CAV_LUT_DATAH,
++ ((((stride + 7) >> 3) >> CANVAS_WIDTH_LWID) <<
++ CANVAS_WIDTH_HBIT) |
++ (height << CANVAS_HEIGHT_BIT) |
++ (wrap << 22) |
++ (blkmode << CANVAS_BLKMODE_BIT) |
++ (endian << 26));
++
++ regmap_write(regmap, DMC_CAV_LUT_ADDR,
++ CANVAS_LUT_WR_EN | canvas_index);
++
++ /* Force a read-back to make sure everything is flushed. */
++ regmap_read(regmap, DMC_CAV_LUT_DATAH, &val);
++ mutex_unlock(&canvas.lock);
++
++ return 0;
++}
++
++static int meson_canvas_alloc(uint8_t *canvas_index)
++{
++ int i;
++
++ mutex_lock(&canvas.lock);
++ for (i = 0; i < NUM_CANVAS; ++i) {
++ if (!canvas.used[i]) {
++ canvas.used[i] = 1;
++ mutex_unlock(&canvas.lock);
++ *canvas_index = i;
++ return 0;
++ }
++ }
++ mutex_unlock(&canvas.lock);
++ dev_err(canvas.dev, "No more canvas available\n");
++
++ return -ENODEV;
++}
++
++static int meson_canvas_free(uint8_t canvas_index)
++{
++ mutex_lock(&canvas.lock);
++ if (!canvas.used[canvas_index]) {
++ dev_err(canvas.dev,
++ "Trying to free unused canvas %u\n", canvas_index);
++ mutex_unlock(&canvas.lock);
++ return -EINVAL;
++ }
++ canvas.used[canvas_index] = 0;
++ mutex_unlock(&canvas.lock);
++
++ return 0;
++}
++
++static struct meson_canvas_platform_data canvas_platform_data = {
++ .alloc = meson_canvas_alloc,
++ .free = meson_canvas_free,
++ .setup = meson_canvas_setup,
++};
++
++static int meson_canvas_probe(struct platform_device *pdev)
++{
++ struct regmap *regmap_dmc;
++ struct device *dev;
++
++ dev = &pdev->dev;
++
++ regmap_dmc = syscon_node_to_regmap(of_get_parent(dev->of_node));
++ if (IS_ERR(regmap_dmc)) {
++ dev_err(&pdev->dev, "failed to get DMC regmap\n");
++ return PTR_ERR(regmap_dmc);
++ }
++
++ canvas.dev = dev;
++ canvas.regmap_dmc = regmap_dmc;
++ mutex_init(&canvas.lock);
++
++ dev->platform_data = &canvas_platform_data;
++
++ return 0;
++}
++
++static int meson_canvas_remove(struct platform_device *pdev)
++{
++ mutex_destroy(&canvas.lock);
++ return 0;
++}
++
++static const struct of_device_id canvas_dt_match[] = {
++ { .compatible = "amlogic,meson-canvas" },
++ {}
++};
++MODULE_DEVICE_TABLE(of, canvas_dt_match);
++
++static struct platform_driver meson_canvas_driver = {
++ .probe = meson_canvas_probe,
++ .remove = meson_canvas_remove,
++ .driver = {
++ .name = "meson-canvas",
++ .of_match_table = canvas_dt_match,
++ },
++};
++module_platform_driver(meson_canvas_driver);
++
++MODULE_ALIAS("platform:meson-canvas");
++MODULE_DESCRIPTION("AMLogic Meson Canvas driver");
++MODULE_AUTHOR("Maxime Jourdan <maxi.jourdan@wanadoo.fr>");
++MODULE_LICENSE("GPL v2");
+diff --git a/include/linux/soc/amlogic/meson-canvas.h b/include/linux/soc/amlogic/meson-canvas.h
+new file mode 100644
+index 0000000..af9e241
+--- /dev/null
++++ b/include/linux/soc/amlogic/meson-canvas.h
+@@ -0,0 +1,37 @@
++/*
++ * Copyright (c) 2018 Maxime Jourdan
++ * Author: Maxime Jourdan <maxi.jourdan@wanadoo.fr>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ */
++#ifndef MESON_CANVAS_H
++#define MESON_CANVAS_H
++
++#include <linux/kernel.h>
++
++#define MESON_CANVAS_WRAP_NONE 0x00
++#define MESON_CANVAS_WRAP_X 0x01
++#define MESON_CANVAS_WRAP_Y 0x02
++
++#define MESON_CANVAS_BLKMODE_LINEAR 0x00
++#define MESON_CANVAS_BLKMODE_32x32 0x01
++#define MESON_CANVAS_BLKMODE_64x64 0x02
++
++struct meson_canvas_platform_data {
++ int (*alloc)(uint8_t *canvas_index);
++ int (*free) (uint8_t canvas_index);
++ int (*setup)(uint8_t canvas_index, uint32_t addr,
++ uint32_t stride, uint32_t height,
++ unsigned int wrap,
++ unsigned int blkmode,
++ unsigned int endian);
++};
++
++#endif
diff --git a/testing/linux-amlogic/0022-dt-bindings-soc-amlogic-add-meson-canvas-documentati.patch b/testing/linux-amlogic/0022-dt-bindings-soc-amlogic-add-meson-canvas-documentati.patch
new file mode 100644
index 0000000000..6d449e3f9b
--- /dev/null
+++ b/testing/linux-amlogic/0022-dt-bindings-soc-amlogic-add-meson-canvas-documentati.patch
@@ -0,0 +1,55 @@
+From c50c3a3d2cb0d563757a8b8b1a3e52acdf68910e Mon Sep 17 00:00:00 2001
+From: Maxime Jourdan <maxi.jourdan@wanadoo.fr>
+Date: Wed, 1 Aug 2018 20:51:26 +0200
+Subject: [PATCH] dt-bindings: soc: amlogic: add meson-canvas documentation
+
+DT bindings doc for amlogic,meson-canvas
+
+Signed-off-by: Maxime Jourdan <maxi.jourdan@wanadoo.fr>
+---
+ .../bindings/soc/amlogic/amlogic,meson-canvas.txt | 36 ++++++++++++++++++++++
+ 1 file changed, 36 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/soc/amlogic/amlogic,meson-canvas.txt
+
+diff --git a/Documentation/devicetree/bindings/soc/amlogic/amlogic,meson-canvas.txt b/Documentation/devicetree/bindings/soc/amlogic/amlogic,meson-canvas.txt
+new file mode 100644
+index 0000000..96e1437
+--- /dev/null
++++ b/Documentation/devicetree/bindings/soc/amlogic/amlogic,meson-canvas.txt
+@@ -0,0 +1,36 @@
++Amlogic Meson Canvas
++================================
++
++A canvas is a collection of metadata that describes a pixel buffer.
++Those metadata include: width, height, phyaddr, wrapping, block mode
++and endianness.
++
++Many IPs within Amlogic SoCs rely on canvas indexes to read/write pixel data
++rather than use the phy addresses directly. For instance, this is the case for
++the video decoders and the display.
++
++Amlogic SoCs have 256 canvas.
++
++Device Tree Bindings:
++---------------------
++
++Canvas Provider
++--------------------------
++
++Required properties:
++- compatible: "amlogic,meson-canvas"
++
++Parent node should have the following properties :
++- compatible: "amlogic,meson-gx-dmc-sysctrl", "syscon", "simple-mfd"
++- reg: base address and size of the DMC system control register space.
++
++Example:
++
++sysctrl_DMC: system-controller@0 {
++ compatible = "amlogic,meson-gx-dmc-sysctrl", "syscon", "simple-mfd";
++ reg = <0x0 0x0 0x0 0x1000>;
++
++ canvas: canvas-provider@0 {
++ compatible = "amlogic,meson-canvas";
++ };
++};
diff --git a/testing/linux-amlogic/0023-ARM64-dts-meson-gx-add-dmcbus-and-canvas-nodes.patch b/testing/linux-amlogic/0023-ARM64-dts-meson-gx-add-dmcbus-and-canvas-nodes.patch
new file mode 100644
index 0000000000..6e97663fc1
--- /dev/null
+++ b/testing/linux-amlogic/0023-ARM64-dts-meson-gx-add-dmcbus-and-canvas-nodes.patch
@@ -0,0 +1,40 @@
+From 2c6aee7a14a22d1d042dc7ddafe72c44a4da501f Mon Sep 17 00:00:00 2001
+From: Maxime Jourdan <maxi.jourdan@wanadoo.fr>
+Date: Wed, 1 Aug 2018 20:51:27 +0200
+Subject: [PATCH] ARM64: dts: meson-gx: add dmcbus and canvas nodes.
+
+Wrap the canvas node in a syscon node.
+
+Signed-off-by: Maxime Jourdan <maxi.jourdan@wanadoo.fr>
+---
+ arch/arm64/boot/dts/amlogic/meson-gx.dtsi | 17 +++++++++++++++++
+ 1 file changed, 17 insertions(+)
+
+diff --git a/arch/arm64/boot/dts/amlogic/meson-gx.dtsi b/arch/arm64/boot/dts/amlogic/meson-gx.dtsi
+index 6b64b63..25e195f 100644
+--- a/arch/arm64/boot/dts/amlogic/meson-gx.dtsi
++++ b/arch/arm64/boot/dts/amlogic/meson-gx.dtsi
+@@ -458,6 +458,23 @@
+ };
+ };
+
++ dmcbus: bus@c8838000 {
++ compatible = "simple-bus";
++ reg = <0x0 0xc8838000 0x0 0x1000>;
++ #address-cells = <2>;
++ #size-cells = <2>;
++ ranges = <0x0 0x0 0x0 0xc8838000 0x0 0x1000>;
++
++ sysctrl_DMC: system-controller@0 {
++ compatible = "amlogic,meson-gx-dmc-sysctrl", "syscon", "simple-mfd";
++ reg = <0x0 0x0 0x0 0x1000>;
++
++ canvas: canvas-provider@0 {
++ compatible = "amlogic,meson-canvas";
++ };
++ };
++ };
++
+ hiubus: bus@c883c000 {
+ compatible = "simple-bus";
+ reg = <0x0 0xc883c000 0x0 0x2000>;
diff --git a/testing/linux-amlogic/0024-drm-meson-convert-to-the-new-canvas-module.patch b/testing/linux-amlogic/0024-drm-meson-convert-to-the-new-canvas-module.patch
new file mode 100644
index 0000000000..dc8fdcb181
--- /dev/null
+++ b/testing/linux-amlogic/0024-drm-meson-convert-to-the-new-canvas-module.patch
@@ -0,0 +1,399 @@
+From c71ba17b0625595a2c0268cb76b7032694550fb3 Mon Sep 17 00:00:00 2001
+From: Maxime Jourdan <maxi.jourdan@wanadoo.fr>
+Date: Wed, 1 Aug 2018 20:51:28 +0200
+Subject: [PATCH] drm/meson: convert to the new canvas module
+
+This removes the meson_canvas files within the meson/drm layer
+and makes use of the new canvas module that is referenced in the dts.
+
+Canvases can be used by different IPs and modules, and it is as such
+preferable to rely on a module that can safely dispatch canvases on
+demand.
+
+Signed-off-by: Maxime Jourdan <maxi.jourdan@wanadoo.fr>
+---
+ .../bindings/display/amlogic,meson-vpu.txt | 9 +--
+ arch/arm64/boot/dts/amlogic/meson-gx.dtsi | 7 ++-
+ drivers/gpu/drm/meson/Kconfig | 1 +
+ drivers/gpu/drm/meson/Makefile | 2 +-
+ drivers/gpu/drm/meson/meson_canvas.c | 70 ----------------------
+ drivers/gpu/drm/meson/meson_canvas.h | 42 -------------
+ drivers/gpu/drm/meson/meson_crtc.c | 5 +-
+ drivers/gpu/drm/meson/meson_drv.c | 35 ++++++-----
+ drivers/gpu/drm/meson/meson_drv.h | 5 +-
+ drivers/gpu/drm/meson/meson_plane.c | 3 +-
+ drivers/gpu/drm/meson/meson_viu.c | 1 -
+ 11 files changed, 39 insertions(+), 141 deletions(-)
+ delete mode 100644 drivers/gpu/drm/meson/meson_canvas.c
+ delete mode 100644 drivers/gpu/drm/meson/meson_canvas.h
+
+diff --git a/Documentation/devicetree/bindings/display/amlogic,meson-vpu.txt b/Documentation/devicetree/bindings/display/amlogic,meson-vpu.txt
+index 057b813..60b6e13 100644
+--- a/Documentation/devicetree/bindings/display/amlogic,meson-vpu.txt
++++ b/Documentation/devicetree/bindings/display/amlogic,meson-vpu.txt
+@@ -60,9 +60,9 @@ Required properties:
+ - reg: base address and size of he following memory-mapped regions :
+ - vpu
+ - hhi
+- - dmc
+ - reg-names: should contain the names of the previous memory regions
+ - interrupts: should contain the VENC Vsync interrupt number
++- amlogic,canvas: should point to a meson canvas provider node
+
+ Optional properties:
+ - power-domains: Optional phandle to associated power domain as described in
+@@ -98,13 +98,14 @@ tv-connector {
+ vpu: vpu@d0100000 {
+ compatible = "amlogic,meson-gxbb-vpu";
+ reg = <0x0 0xd0100000 0x0 0x100000>,
+- <0x0 0xc883c000 0x0 0x1000>,
+- <0x0 0xc8838000 0x0 0x1000>;
+- reg-names = "vpu", "hhi", "dmc";
++ <0x0 0xc883c000 0x0 0x1000>;
++ reg-names = "vpu", "hhi";
+ interrupts = <GIC_SPI 3 IRQ_TYPE_EDGE_RISING>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
++ amlogic,canvas = <&canvas>;
++
+ /* CVBS VDAC output port */
+ port@0 {
+ reg = <0>;
+diff --git a/arch/arm64/boot/dts/amlogic/meson-gx.dtsi b/arch/arm64/boot/dts/amlogic/meson-gx.dtsi
+index 25e195f..7296b4f 100644
+--- a/arch/arm64/boot/dts/amlogic/meson-gx.dtsi
++++ b/arch/arm64/boot/dts/amlogic/meson-gx.dtsi
+@@ -538,13 +538,14 @@
+ vpu: vpu@d0100000 {
+ compatible = "amlogic,meson-gx-vpu";
+ reg = <0x0 0xd0100000 0x0 0x100000>,
+- <0x0 0xc883c000 0x0 0x1000>,
+- <0x0 0xc8838000 0x0 0x1000>;
+- reg-names = "vpu", "hhi", "dmc";
++ <0x0 0xc883c000 0x0 0x1000>;
++ reg-names = "vpu", "hhi";
+ interrupts = <GIC_SPI 3 IRQ_TYPE_EDGE_RISING>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
++ amlogic,canvas = <&canvas>;
++
+ /* CVBS VDAC output port */
+ cvbs_vdac_port: port@0 {
+ reg = <0>;
+diff --git a/drivers/gpu/drm/meson/Kconfig b/drivers/gpu/drm/meson/Kconfig
+index 02d400b..8929058 100644
+--- a/drivers/gpu/drm/meson/Kconfig
++++ b/drivers/gpu/drm/meson/Kconfig
+@@ -7,6 +7,7 @@ config DRM_MESON
+ select DRM_GEM_CMA_HELPER
+ select VIDEOMODE_HELPERS
+ select REGMAP_MMIO
++ select MESON_CANVAS
+
+ config DRM_MESON_DW_HDMI
+ tristate "HDMI Synopsys Controller support for Amlogic Meson Display"
+diff --git a/drivers/gpu/drm/meson/Makefile b/drivers/gpu/drm/meson/Makefile
+index c5c4cc3..bd67429 100644
+--- a/drivers/gpu/drm/meson/Makefile
++++ b/drivers/gpu/drm/meson/Makefile
+@@ -1,5 +1,5 @@
+ meson-drm-y := meson_drv.o meson_plane.o meson_crtc.o meson_venc_cvbs.o
+-meson-drm-y += meson_viu.o meson_vpp.o meson_venc.o meson_vclk.o meson_canvas.o
++meson-drm-y += meson_viu.o meson_vpp.o meson_venc.o meson_vclk.o
+
+ obj-$(CONFIG_DRM_MESON) += meson-drm.o
+ obj-$(CONFIG_DRM_MESON_DW_HDMI) += meson_dw_hdmi.o
+diff --git a/drivers/gpu/drm/meson/meson_canvas.c b/drivers/gpu/drm/meson/meson_canvas.c
+deleted file mode 100644
+index 08f6073..0000000
+--- a/drivers/gpu/drm/meson/meson_canvas.c
++++ /dev/null
+@@ -1,70 +0,0 @@
+-/*
+- * Copyright (C) 2016 BayLibre, SAS
+- * Author: Neil Armstrong <narmstrong@baylibre.com>
+- * Copyright (C) 2015 Amlogic, Inc. All rights reserved.
+- * Copyright (C) 2014 Endless Mobile
+- *
+- * This program is free software; you can redistribute it and/or
+- * modify it under the terms of the GNU General Public License as
+- * published by the Free Software Foundation; either version 2 of the
+- * License, or (at your option) any later version.
+- *
+- * This program is distributed in the hope that it will be useful, but
+- * WITHOUT ANY WARRANTY; without even the implied warranty of
+- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+- * General Public License for more details.
+- *
+- * You should have received a copy of the GNU General Public License
+- * along with this program; if not, see <http://www.gnu.org/licenses/>.
+- */
+-
+-#include <linux/kernel.h>
+-#include <linux/module.h>
+-#include "meson_drv.h"
+-#include "meson_canvas.h"
+-#include "meson_registers.h"
+-
+-/**
+- * DOC: Canvas
+- *
+- * CANVAS is a memory zone where physical memory frames information
+- * are stored for the VIU to scanout.
+- */
+-
+-/* DMC Registers */
+-#define DMC_CAV_LUT_DATAL 0x48 /* 0x12 offset in data sheet */
+-#define CANVAS_WIDTH_LBIT 29
+-#define CANVAS_WIDTH_LWID 3
+-#define DMC_CAV_LUT_DATAH 0x4c /* 0x13 offset in data sheet */
+-#define CANVAS_WIDTH_HBIT 0
+-#define CANVAS_HEIGHT_BIT 9
+-#define CANVAS_BLKMODE_BIT 24
+-#define DMC_CAV_LUT_ADDR 0x50 /* 0x14 offset in data sheet */
+-#define CANVAS_LUT_WR_EN (0x2 << 8)
+-#define CANVAS_LUT_RD_EN (0x1 << 8)
+-
+-void meson_canvas_setup(struct meson_drm *priv,
+- uint32_t canvas_index, uint32_t addr,
+- uint32_t stride, uint32_t height,
+- unsigned int wrap,
+- unsigned int blkmode)
+-{
+- unsigned int val;
+-
+- regmap_write(priv->dmc, DMC_CAV_LUT_DATAL,
+- (((addr + 7) >> 3)) |
+- (((stride + 7) >> 3) << CANVAS_WIDTH_LBIT));
+-
+- regmap_write(priv->dmc, DMC_CAV_LUT_DATAH,
+- ((((stride + 7) >> 3) >> CANVAS_WIDTH_LWID) <<
+- CANVAS_WIDTH_HBIT) |
+- (height << CANVAS_HEIGHT_BIT) |
+- (wrap << 22) |
+- (blkmode << CANVAS_BLKMODE_BIT));
+-
+- regmap_write(priv->dmc, DMC_CAV_LUT_ADDR,
+- CANVAS_LUT_WR_EN | canvas_index);
+-
+- /* Force a read-back to make sure everything is flushed. */
+- regmap_read(priv->dmc, DMC_CAV_LUT_DATAH, &val);
+-}
+diff --git a/drivers/gpu/drm/meson/meson_canvas.h b/drivers/gpu/drm/meson/meson_canvas.h
+deleted file mode 100644
+index af1759d..0000000
+--- a/drivers/gpu/drm/meson/meson_canvas.h
++++ /dev/null
+@@ -1,42 +0,0 @@
+-/*
+- * Copyright (C) 2016 BayLibre, SAS
+- * Author: Neil Armstrong <narmstrong@baylibre.com>
+- * Copyright (C) 2014 Endless Mobile
+- *
+- * This program is free software; you can redistribute it and/or
+- * modify it under the terms of the GNU General Public License as
+- * published by the Free Software Foundation; either version 2 of the
+- * License, or (at your option) any later version.
+- *
+- * This program is distributed in the hope that it will be useful, but
+- * WITHOUT ANY WARRANTY; without even the implied warranty of
+- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+- * General Public License for more details.
+- *
+- * You should have received a copy of the GNU General Public License
+- * along with this program; if not, see <http://www.gnu.org/licenses/>.
+- */
+-
+-/* Canvas LUT Memory */
+-
+-#ifndef __MESON_CANVAS_H
+-#define __MESON_CANVAS_H
+-
+-#define MESON_CANVAS_ID_OSD1 0x4e
+-
+-/* Canvas configuration. */
+-#define MESON_CANVAS_WRAP_NONE 0x00
+-#define MESON_CANVAS_WRAP_X 0x01
+-#define MESON_CANVAS_WRAP_Y 0x02
+-
+-#define MESON_CANVAS_BLKMODE_LINEAR 0x00
+-#define MESON_CANVAS_BLKMODE_32x32 0x01
+-#define MESON_CANVAS_BLKMODE_64x64 0x02
+-
+-void meson_canvas_setup(struct meson_drm *priv,
+- uint32_t canvas_index, uint32_t addr,
+- uint32_t stride, uint32_t height,
+- unsigned int wrap,
+- unsigned int blkmode);
+-
+-#endif /* __MESON_CANVAS_H */
+diff --git a/drivers/gpu/drm/meson/meson_crtc.c b/drivers/gpu/drm/meson/meson_crtc.c
+index 4dd0df0..7c8ad06 100644
+--- a/drivers/gpu/drm/meson/meson_crtc.c
++++ b/drivers/gpu/drm/meson/meson_crtc.c
+@@ -36,7 +36,6 @@
+ #include "meson_venc.h"
+ #include "meson_vpp.h"
+ #include "meson_viu.h"
+-#include "meson_canvas.h"
+ #include "meson_registers.h"
+
+ /* CRTC definition */
+@@ -199,10 +198,10 @@ void meson_crtc_irq(struct meson_drm *priv)
+ } else
+ meson_vpp_disable_interlace_vscaler_osd1(priv);
+
+- meson_canvas_setup(priv, MESON_CANVAS_ID_OSD1,
++ priv->canvas_ops->setup(priv->canvas_id_osd1,
+ priv->viu.osd1_addr, priv->viu.osd1_stride,
+ priv->viu.osd1_height, MESON_CANVAS_WRAP_NONE,
+- MESON_CANVAS_BLKMODE_LINEAR);
++ MESON_CANVAS_BLKMODE_LINEAR, 0);
+
+ /* Enable OSD1 */
+ writel_bits_relaxed(VPP_OSD1_POSTBLEND, VPP_OSD1_POSTBLEND,
+diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c
+index d344312..de46833 100644
+--- a/drivers/gpu/drm/meson/meson_drv.c
++++ b/drivers/gpu/drm/meson/meson_drv.c
+@@ -26,6 +26,7 @@
+ #include <linux/platform_device.h>
+ #include <linux/component.h>
+ #include <linux/of_graph.h>
++#include <linux/of_platform.h>
+
+ #include <drm/drmP.h>
+ #include <drm/drm_atomic.h>
+@@ -47,7 +48,6 @@
+ #include "meson_vpp.h"
+ #include "meson_viu.h"
+ #include "meson_venc.h"
+-#include "meson_canvas.h"
+ #include "meson_registers.h"
+
+ #define DRIVER_NAME "meson"
+@@ -165,6 +165,8 @@ static int meson_drv_bind_master(struct device *dev, bool has_components)
+ struct meson_drm *priv;
+ struct drm_device *drm;
+ struct resource *res;
++ struct device_node *canvas;
++ struct platform_device *canvas_pdev;
+ void __iomem *regs;
+ int ret;
+
+@@ -211,31 +213,35 @@ static int meson_drv_bind_master(struct device *dev, bool has_components)
+ priv->hhi = devm_regmap_init_mmio(dev, regs,
+ &meson_regmap_config);
+ if (IS_ERR(priv->hhi)) {
+- dev_err(&pdev->dev, "Couldn't create the HHI regmap\n");
++ dev_err(dev, "Couldn't create the HHI regmap\n");
+ ret = PTR_ERR(priv->hhi);
+ goto free_drm;
+ }
+
+- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dmc");
+- if (!res) {
+- ret = -EINVAL;
++ canvas = of_parse_phandle(dev->of_node, "amlogic,canvas", 0);
++ if (!canvas) {
++ ret = -ENODEV;
+ goto free_drm;
+ }
+- /* Simply ioremap since it may be a shared register zone */
+- regs = devm_ioremap(dev, res->start, resource_size(res));
+- if (!regs) {
+- ret = -EADDRNOTAVAIL;
++
++ canvas_pdev = of_find_device_by_node(canvas);
++ if (!canvas_pdev) {
++ dev_err(dev, "Unable to find canvas pdev\n");
++ ret = -ENODEV;
+ goto free_drm;
+ }
+
+- priv->dmc = devm_regmap_init_mmio(dev, regs,
+- &meson_regmap_config);
+- if (IS_ERR(priv->dmc)) {
+- dev_err(&pdev->dev, "Couldn't create the DMC regmap\n");
+- ret = PTR_ERR(priv->dmc);
++ priv->canvas_ops = dev_get_platdata(&canvas_pdev->dev);
++ if (!priv->canvas_ops) {
++ dev_err(dev, "canvas pdata structure NULL\n");
++ ret = -EINVAL;
+ goto free_drm;
+ }
+
++ ret = priv->canvas_ops->alloc(&priv->canvas_id_osd1);
++ if (ret)
++ goto free_drm;
++
+ priv->vsync_irq = platform_get_irq(pdev, 0);
+
+ ret = drm_vblank_init(drm, 1);
+@@ -315,6 +321,7 @@ static void meson_drv_unbind(struct device *dev)
+ struct drm_device *drm = dev_get_drvdata(dev);
+ struct meson_drm *priv = drm->dev_private;
+
++ priv->canvas_ops->free(priv->canvas_id_osd1);
+ drm_dev_unregister(drm);
+ drm_kms_helper_poll_fini(drm);
+ drm_fbdev_cma_fini(priv->fbdev);
+diff --git a/drivers/gpu/drm/meson/meson_drv.h b/drivers/gpu/drm/meson/meson_drv.h
+index 8450d6ac..dfea959 100644
+--- a/drivers/gpu/drm/meson/meson_drv.h
++++ b/drivers/gpu/drm/meson/meson_drv.h
+@@ -22,15 +22,18 @@
+ #include <linux/platform_device.h>
+ #include <linux/regmap.h>
+ #include <linux/of.h>
++#include <linux/soc/amlogic/meson-canvas.h>
+ #include <drm/drmP.h>
+
+ struct meson_drm {
+ struct device *dev;
+ void __iomem *io_base;
+ struct regmap *hhi;
+- struct regmap *dmc;
+ int vsync_irq;
+
++ struct meson_canvas_platform_data *canvas_ops;
++ uint8_t canvas_id_osd1;
++
+ struct drm_device *drm;
+ struct drm_crtc *crtc;
+ struct drm_fbdev_cma *fbdev;
+diff --git a/drivers/gpu/drm/meson/meson_plane.c b/drivers/gpu/drm/meson/meson_plane.c
+index 12c80df..8745f92 100644
+--- a/drivers/gpu/drm/meson/meson_plane.c
++++ b/drivers/gpu/drm/meson/meson_plane.c
+@@ -36,7 +36,6 @@
+ #include "meson_plane.h"
+ #include "meson_vpp.h"
+ #include "meson_viu.h"
+-#include "meson_canvas.h"
+ #include "meson_registers.h"
+
+ struct meson_plane {
+@@ -105,7 +104,7 @@ static void meson_plane_atomic_update(struct drm_plane *plane,
+ OSD_BLK0_ENABLE;
+
+ /* Set up BLK0 to point to the right canvas */
+- priv->viu.osd1_blk0_cfg[0] = ((MESON_CANVAS_ID_OSD1 << OSD_CANVAS_SEL) |
++ priv->viu.osd1_blk0_cfg[0] = ((priv->canvas_id_osd1 << OSD_CANVAS_SEL) |
+ OSD_ENDIANNESS_LE);
+
+ /* On GXBB, Use the old non-HDR RGB2YUV converter */
+diff --git a/drivers/gpu/drm/meson/meson_viu.c b/drivers/gpu/drm/meson/meson_viu.c
+index 6bcfa52..5b48c4c 100644
+--- a/drivers/gpu/drm/meson/meson_viu.c
++++ b/drivers/gpu/drm/meson/meson_viu.c
+@@ -25,7 +25,6 @@
+ #include "meson_viu.h"
+ #include "meson_vpp.h"
+ #include "meson_venc.h"
+-#include "meson_canvas.h"
+ #include "meson_registers.h"
+
+ /**
diff --git a/testing/linux-amlogic/0025-WIP-drm-meson-Support-Overlay-plane-for-video-render.patch b/testing/linux-amlogic/0025-WIP-drm-meson-Support-Overlay-plane-for-video-render.patch
new file mode 100644
index 0000000000..6102e1cdc3
--- /dev/null
+++ b/testing/linux-amlogic/0025-WIP-drm-meson-Support-Overlay-plane-for-video-render.patch
@@ -0,0 +1,1141 @@
+From 5d0ab03232cdda74b9eb4ce283e98aa60c40b0c9 Mon Sep 17 00:00:00 2001
+From: Neil Armstrong <narmstrong@baylibre.com>
+Date: Thu, 2 Aug 2018 10:00:01 +0200
+Subject: [PATCH] [WIP] drm/meson: Support Overlay plane for video rendering
+
+The Amlogic Meson GX SoCs support an Overlay plane behind the primary
+plan for video rendering.
+This Overlay plane support various YUV layouts and a non-alpha RGB32
+layout.
+---
+ drivers/gpu/drm/meson/Makefile | 2 +-
+ drivers/gpu/drm/meson/meson_crtc.c | 170 ++++++++-
+ drivers/gpu/drm/meson/meson_drv.c | 14 +
+ drivers/gpu/drm/meson/meson_drv.h | 52 +++
+ drivers/gpu/drm/meson/meson_overlay.c | 595 ++++++++++++++++++++++++++++++++
+ drivers/gpu/drm/meson/meson_overlay.h | 14 +
+ drivers/gpu/drm/meson/meson_registers.h | 3 +
+ drivers/gpu/drm/meson/meson_viu.c | 15 +
+ drivers/gpu/drm/meson/meson_vpp.c | 44 ++-
+ drivers/soc/amlogic/meson-gx-pwrc-vpu.c | 8 +-
+ 10 files changed, 910 insertions(+), 7 deletions(-)
+ create mode 100644 drivers/gpu/drm/meson/meson_overlay.c
+ create mode 100644 drivers/gpu/drm/meson/meson_overlay.h
+
+diff --git a/drivers/gpu/drm/meson/Makefile b/drivers/gpu/drm/meson/Makefile
+index bd67429..d4ea82f 100644
+--- a/drivers/gpu/drm/meson/Makefile
++++ b/drivers/gpu/drm/meson/Makefile
+@@ -1,5 +1,5 @@
+ meson-drm-y := meson_drv.o meson_plane.o meson_crtc.o meson_venc_cvbs.o
+-meson-drm-y += meson_viu.o meson_vpp.o meson_venc.o meson_vclk.o
++meson-drm-y += meson_viu.o meson_vpp.o meson_venc.o meson_vclk.o meson_overlay.o
+
+ obj-$(CONFIG_DRM_MESON) += meson-drm.o
+ obj-$(CONFIG_DRM_MESON_DW_HDMI) += meson_dw_hdmi.o
+diff --git a/drivers/gpu/drm/meson/meson_crtc.c b/drivers/gpu/drm/meson/meson_crtc.c
+index 7c8ad06..90c826b 100644
+--- a/drivers/gpu/drm/meson/meson_crtc.c
++++ b/drivers/gpu/drm/meson/meson_crtc.c
+@@ -25,6 +25,7 @@
+ #include <linux/module.h>
+ #include <linux/mutex.h>
+ #include <linux/platform_device.h>
++#include <linux/bitfield.h>
+ #include <drm/drmP.h>
+ #include <drm/drm_atomic.h>
+ #include <drm/drm_atomic_helper.h>
+@@ -97,6 +98,10 @@ static void meson_crtc_atomic_enable(struct drm_crtc *crtc,
+ writel(crtc_state->mode.hdisplay,
+ priv->io_base + _REG(VPP_POSTBLEND_H_SIZE));
+
++ /* VD1 Preblend vertical start/end */
++ writel(FIELD_PREP(GENMASK(11, 0), 2303),
++ priv->io_base + _REG(VPP_PREBLEND_VD1_V_START_END));
++
+ writel_bits_relaxed(VPP_POSTBLEND_ENABLE, VPP_POSTBLEND_ENABLE,
+ priv->io_base + _REG(VPP_MISC));
+
+@@ -118,8 +123,12 @@ static void meson_crtc_atomic_disable(struct drm_crtc *crtc,
+ priv->viu.osd1_enabled = false;
+ priv->viu.osd1_commit = false;
+
++ priv->viu.vd1_enabled = false;
++ priv->viu.vd1_commit = false;
++
+ /* Disable VPP Postblend */
+- writel_bits_relaxed(VPP_POSTBLEND_ENABLE, 0,
++ writel_bits_relaxed(VPP_OSD1_POSTBLEND | VPP_VD1_POSTBLEND |
++ VPP_VD1_PREBLEND | VPP_POSTBLEND_ENABLE, 0,
+ priv->io_base + _REG(VPP_MISC));
+
+ if (crtc->state->event && !crtc->state->active) {
+@@ -154,6 +163,7 @@ static void meson_crtc_atomic_flush(struct drm_crtc *crtc,
+ struct meson_drm *priv = meson_crtc->priv;
+
+ priv->viu.osd1_commit = true;
++ priv->viu.vd1_commit = true;
+ }
+
+ static const struct drm_crtc_helper_funcs meson_crtc_helper_funcs = {
+@@ -210,6 +220,164 @@ void meson_crtc_irq(struct meson_drm *priv)
+ priv->viu.osd1_commit = false;
+ }
+
++ /* Update the VD1 registers */
++ if (priv->viu.vd1_enabled && priv->viu.vd1_commit) {
++
++ switch (priv->viu.vd1_planes) {
++ case 3:
++ priv->canvas_ops->setup(priv->canvas_id_vd1_2,
++ priv->viu.vd1_addr2, priv->viu.vd1_stride2,
++ priv->viu.vd1_height2, MESON_CANVAS_WRAP_NONE,
++ MESON_CANVAS_BLKMODE_LINEAR, 7);
++ case 2:
++ priv->canvas_ops->setup(priv->canvas_id_vd1_1,
++ priv->viu.vd1_addr1, priv->viu.vd1_stride1,
++ priv->viu.vd1_height1, MESON_CANVAS_WRAP_NONE,
++ MESON_CANVAS_BLKMODE_LINEAR, 7);
++ case 1:
++ priv->canvas_ops->setup(priv->canvas_id_vd1_0,
++ priv->viu.vd1_addr0, priv->viu.vd1_stride0,
++ priv->viu.vd1_height0, MESON_CANVAS_WRAP_NONE,
++ MESON_CANVAS_BLKMODE_LINEAR, 7);
++ };
++
++ writel_relaxed(priv->viu.vd1_if0_gen_reg,
++ priv->io_base + _REG(VD1_IF0_GEN_REG));
++ writel_relaxed(priv->viu.vd1_if0_gen_reg,
++ priv->io_base + _REG(VD2_IF0_GEN_REG));
++ writel_relaxed(priv->viu.vd1_if0_gen_reg2,
++ priv->io_base + _REG(VD1_IF0_GEN_REG2));
++ writel_relaxed(priv->viu.viu_vd1_fmt_ctrl,
++ priv->io_base + _REG(VIU_VD1_FMT_CTRL));
++ writel_relaxed(priv->viu.viu_vd1_fmt_ctrl,
++ priv->io_base + _REG(VIU_VD2_FMT_CTRL));
++ writel_relaxed(priv->viu.viu_vd1_fmt_w,
++ priv->io_base + _REG(VIU_VD1_FMT_W));
++ writel_relaxed(priv->viu.viu_vd1_fmt_w,
++ priv->io_base + _REG(VIU_VD2_FMT_W));
++ writel_relaxed(priv->viu.vd1_if0_canvas0,
++ priv->io_base + _REG(VD1_IF0_CANVAS0));
++ writel_relaxed(priv->viu.vd1_if0_canvas0,
++ priv->io_base + _REG(VD1_IF0_CANVAS1));
++ writel_relaxed(priv->viu.vd1_if0_canvas0,
++ priv->io_base + _REG(VD2_IF0_CANVAS0));
++ writel_relaxed(priv->viu.vd1_if0_canvas0,
++ priv->io_base + _REG(VD2_IF0_CANVAS1));
++ writel_relaxed(priv->viu.vd1_if0_luma_x0,
++ priv->io_base + _REG(VD1_IF0_LUMA_X0));
++ writel_relaxed(priv->viu.vd1_if0_luma_x0,
++ priv->io_base + _REG(VD1_IF0_LUMA_X1));
++ writel_relaxed(priv->viu.vd1_if0_luma_x0,
++ priv->io_base + _REG(VD2_IF0_LUMA_X0));
++ writel_relaxed(priv->viu.vd1_if0_luma_x0,
++ priv->io_base + _REG(VD2_IF0_LUMA_X1));
++ writel_relaxed(priv->viu.vd1_if0_luma_y0,
++ priv->io_base + _REG(VD1_IF0_LUMA_Y0));
++ writel_relaxed(priv->viu.vd1_if0_luma_y0,
++ priv->io_base + _REG(VD1_IF0_LUMA_Y1));
++ writel_relaxed(priv->viu.vd1_if0_luma_y0,
++ priv->io_base + _REG(VD2_IF0_LUMA_Y0));
++ writel_relaxed(priv->viu.vd1_if0_luma_y0,
++ priv->io_base + _REG(VD2_IF0_LUMA_Y1));
++ writel_relaxed(priv->viu.vd1_if0_chroma_x0,
++ priv->io_base + _REG(VD1_IF0_CHROMA_X0));
++ writel_relaxed(priv->viu.vd1_if0_chroma_x0,
++ priv->io_base + _REG(VD1_IF0_CHROMA_X1));
++ writel_relaxed(priv->viu.vd1_if0_chroma_x0,
++ priv->io_base + _REG(VD2_IF0_CHROMA_X0));
++ writel_relaxed(priv->viu.vd1_if0_chroma_x0,
++ priv->io_base + _REG(VD2_IF0_CHROMA_X1));
++ writel_relaxed(priv->viu.vd1_if0_chroma_y0,
++ priv->io_base + _REG(VD1_IF0_CHROMA_Y0));
++ writel_relaxed(priv->viu.vd1_if0_chroma_y0,
++ priv->io_base + _REG(VD1_IF0_CHROMA_Y1));
++ writel_relaxed(priv->viu.vd1_if0_chroma_y0,
++ priv->io_base + _REG(VD2_IF0_CHROMA_Y0));
++ writel_relaxed(priv->viu.vd1_if0_chroma_y0,
++ priv->io_base + _REG(VD2_IF0_CHROMA_Y1));
++ writel_relaxed(priv->viu.vd1_if0_repeat_loop,
++ priv->io_base + _REG(VD1_IF0_RPT_LOOP));
++ writel_relaxed(priv->viu.vd1_if0_repeat_loop,
++ priv->io_base + _REG(VD2_IF0_RPT_LOOP));
++ writel_relaxed(priv->viu.vd1_if0_luma0_rpt_pat,
++ priv->io_base + _REG(VD1_IF0_LUMA0_RPT_PAT));
++ writel_relaxed(priv->viu.vd1_if0_luma0_rpt_pat,
++ priv->io_base + _REG(VD2_IF0_LUMA0_RPT_PAT));
++ writel_relaxed(priv->viu.vd1_if0_luma0_rpt_pat,
++ priv->io_base + _REG(VD1_IF0_LUMA1_RPT_PAT));
++ writel_relaxed(priv->viu.vd1_if0_luma0_rpt_pat,
++ priv->io_base + _REG(VD2_IF0_LUMA1_RPT_PAT));
++ writel_relaxed(priv->viu.vd1_if0_chroma0_rpt_pat,
++ priv->io_base + _REG(VD1_IF0_CHROMA0_RPT_PAT));
++ writel_relaxed(priv->viu.vd1_if0_chroma0_rpt_pat,
++ priv->io_base + _REG(VD2_IF0_CHROMA0_RPT_PAT));
++ writel_relaxed(priv->viu.vd1_if0_chroma0_rpt_pat,
++ priv->io_base + _REG(VD1_IF0_CHROMA1_RPT_PAT));
++ writel_relaxed(priv->viu.vd1_if0_chroma0_rpt_pat,
++ priv->io_base + _REG(VD2_IF0_CHROMA1_RPT_PAT));
++ writel_relaxed(0, priv->io_base + _REG(VD1_IF0_LUMA_PSEL));
++ writel_relaxed(0, priv->io_base + _REG(VD1_IF0_CHROMA_PSEL));
++ writel_relaxed(0, priv->io_base + _REG(VD2_IF0_LUMA_PSEL));
++ writel_relaxed(0, priv->io_base + _REG(VD2_IF0_CHROMA_PSEL));
++ writel_relaxed(priv->viu.vd1_range_map_y,
++ priv->io_base + _REG(VD1_IF0_RANGE_MAP_Y));
++ writel_relaxed(priv->viu.vd1_range_map_cb,
++ priv->io_base + _REG(VD1_IF0_RANGE_MAP_CB));
++ writel_relaxed(priv->viu.vd1_range_map_cr,
++ priv->io_base + _REG(VD1_IF0_RANGE_MAP_CR));
++ writel_relaxed(0x78404,
++ priv->io_base + _REG(VPP_SC_MISC));
++ writel_relaxed(priv->viu.vpp_pic_in_height,
++ priv->io_base + _REG(VPP_PIC_IN_HEIGHT));
++ writel_relaxed(priv->viu.vpp_postblend_vd1_h_start_end,
++ priv->io_base + _REG(VPP_POSTBLEND_VD1_H_START_END));
++ writel_relaxed(priv->viu.vpp_blend_vd2_h_start_end,
++ priv->io_base + _REG(VPP_BLEND_VD2_H_START_END));
++ writel_relaxed(priv->viu.vpp_postblend_vd1_v_start_end,
++ priv->io_base + _REG(VPP_POSTBLEND_VD1_V_START_END));
++ writel_relaxed(priv->viu.vpp_blend_vd2_v_start_end,
++ priv->io_base + _REG(VPP_BLEND_VD2_V_START_END));
++ writel_relaxed(priv->viu.vpp_hsc_region12_startp,
++ priv->io_base + _REG(VPP_HSC_REGION12_STARTP));
++ writel_relaxed(priv->viu.vpp_hsc_region34_startp,
++ priv->io_base + _REG(VPP_HSC_REGION34_STARTP));
++ writel_relaxed(priv->viu.vpp_hsc_region4_endp,
++ priv->io_base + _REG(VPP_HSC_REGION4_ENDP));
++ writel_relaxed(priv->viu.vpp_hsc_start_phase_step,
++ priv->io_base + _REG(VPP_HSC_START_PHASE_STEP));
++ writel_relaxed(priv->viu.vpp_hsc_region1_phase_slope,
++ priv->io_base + _REG(VPP_HSC_REGION1_PHASE_SLOPE));
++ writel_relaxed(priv->viu.vpp_hsc_region3_phase_slope,
++ priv->io_base + _REG(VPP_HSC_REGION3_PHASE_SLOPE));
++ writel_relaxed(priv->viu.vpp_line_in_length,
++ priv->io_base + _REG(VPP_LINE_IN_LENGTH));
++ writel_relaxed(priv->viu.vpp_preblend_h_size,
++ priv->io_base + _REG(VPP_PREBLEND_H_SIZE));
++ writel_relaxed(priv->viu.vpp_vsc_region12_startp,
++ priv->io_base + _REG(VPP_VSC_REGION12_STARTP));
++ writel_relaxed(priv->viu.vpp_vsc_region34_startp,
++ priv->io_base + _REG(VPP_VSC_REGION34_STARTP));
++ writel_relaxed(priv->viu.vpp_vsc_region4_endp,
++ priv->io_base + _REG(VPP_VSC_REGION4_ENDP));
++ writel_relaxed(priv->viu.vpp_vsc_start_phase_step,
++ priv->io_base + _REG(VPP_VSC_START_PHASE_STEP));
++ writel_relaxed(priv->viu.vpp_vsc_ini_phase,
++ priv->io_base + _REG(VPP_VSC_INI_PHASE));
++ writel_relaxed(priv->viu.vpp_vsc_phase_ctrl,
++ priv->io_base + _REG(VPP_VSC_PHASE_CTRL));
++ writel_relaxed(priv->viu.vpp_hsc_phase_ctrl,
++ priv->io_base + _REG(VPP_HSC_PHASE_CTRL));
++ writel_relaxed(0x00000042,
++ priv->io_base + _REG(VPP_SCALE_COEF_IDX));
++
++ /* Enable VD1 */
++ writel_bits_relaxed(VPP_VD1_PREBLEND | VPP_VD1_POSTBLEND | VPP_COLOR_MNG_ENABLE,
++ VPP_VD1_PREBLEND | VPP_VD1_POSTBLEND | VPP_COLOR_MNG_ENABLE,
++ priv->io_base + _REG(VPP_MISC));
++
++ priv->viu.vd1_commit = false;
++ }
++
+ drm_crtc_handle_vblank(priv->crtc);
+
+ spin_lock_irqsave(&priv->drm->event_lock, flags);
+diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c
+index de46833..2a002cc 100644
+--- a/drivers/gpu/drm/meson/meson_drv.c
++++ b/drivers/gpu/drm/meson/meson_drv.c
+@@ -42,6 +42,7 @@
+
+ #include "meson_drv.h"
+ #include "meson_plane.h"
++#include "meson_overlay.h"
+ #include "meson_crtc.h"
+ #include "meson_venc_cvbs.h"
+
+@@ -241,6 +242,15 @@ static int meson_drv_bind_master(struct device *dev, bool has_components)
+ ret = priv->canvas_ops->alloc(&priv->canvas_id_osd1);
+ if (ret)
+ goto free_drm;
++ ret = priv->canvas_ops->alloc(&priv->canvas_id_vd1_0);
++ if (ret)
++ goto free_drm;
++ ret = priv->canvas_ops->alloc(&priv->canvas_id_vd1_1);
++ if (ret)
++ goto free_drm;
++ ret = priv->canvas_ops->alloc(&priv->canvas_id_vd1_2);
++ if (ret)
++ goto free_drm;
+
+ priv->vsync_irq = platform_get_irq(pdev, 0);
+
+@@ -278,6 +288,10 @@ static int meson_drv_bind_master(struct device *dev, bool has_components)
+ if (ret)
+ goto free_drm;
+
++ ret = meson_overlay_create(priv);
++ if (ret)
++ goto free_drm;
++
+ ret = meson_crtc_create(priv);
+ if (ret)
+ goto free_drm;
+diff --git a/drivers/gpu/drm/meson/meson_drv.h b/drivers/gpu/drm/meson/meson_drv.h
+index dfea959..e9305d7 100644
+--- a/drivers/gpu/drm/meson/meson_drv.h
++++ b/drivers/gpu/drm/meson/meson_drv.h
+@@ -33,11 +33,15 @@ struct meson_drm {
+
+ struct meson_canvas_platform_data *canvas_ops;
+ uint8_t canvas_id_osd1;
++ uint8_t canvas_id_vd1_0;
++ uint8_t canvas_id_vd1_1;
++ uint8_t canvas_id_vd1_2;
+
+ struct drm_device *drm;
+ struct drm_crtc *crtc;
+ struct drm_fbdev_cma *fbdev;
+ struct drm_plane *primary_plane;
++ struct drm_plane *overlay_plane;
+
+ /* Components Data */
+ struct {
+@@ -49,6 +53,54 @@ struct meson_drm {
+ uint32_t osd1_addr;
+ uint32_t osd1_stride;
+ uint32_t osd1_height;
++
++ bool vd1_enabled;
++ bool vd1_commit;
++ unsigned int vd1_planes;
++ uint32_t vd1_if0_gen_reg;
++ uint32_t vd1_if0_luma_x0;
++ uint32_t vd1_if0_luma_y0;
++ uint32_t vd1_if0_chroma_x0;
++ uint32_t vd1_if0_chroma_y0;
++ uint32_t vd1_if0_repeat_loop;
++ uint32_t vd1_if0_luma0_rpt_pat;
++ uint32_t vd1_if0_chroma0_rpt_pat;
++ uint32_t vd1_range_map_y;
++ uint32_t vd1_range_map_cb;
++ uint32_t vd1_range_map_cr;
++ uint32_t viu_vd1_fmt_w;
++ uint32_t vd1_if0_canvas0;
++ uint32_t vd1_if0_gen_reg2;
++ uint32_t viu_vd1_fmt_ctrl;
++ uint32_t vd1_addr0;
++ uint32_t vd1_addr1;
++ uint32_t vd1_addr2;
++ uint32_t vd1_stride0;
++ uint32_t vd1_stride1;
++ uint32_t vd1_stride2;
++ uint32_t vd1_height0;
++ uint32_t vd1_height1;
++ uint32_t vd1_height2;
++ uint32_t vpp_pic_in_height;
++ uint32_t vpp_postblend_vd1_h_start_end;
++ uint32_t vpp_postblend_vd1_v_start_end;
++ uint32_t vpp_hsc_region12_startp;
++ uint32_t vpp_hsc_region34_startp;
++ uint32_t vpp_hsc_region4_endp;
++ uint32_t vpp_hsc_start_phase_step;
++ uint32_t vpp_hsc_region1_phase_slope;
++ uint32_t vpp_hsc_region3_phase_slope;
++ uint32_t vpp_line_in_length;
++ uint32_t vpp_preblend_h_size;
++ uint32_t vpp_vsc_region12_startp;
++ uint32_t vpp_vsc_region34_startp;
++ uint32_t vpp_vsc_region4_endp;
++ uint32_t vpp_vsc_start_phase_step;
++ uint32_t vpp_vsc_ini_phase;
++ uint32_t vpp_vsc_phase_ctrl;
++ uint32_t vpp_hsc_phase_ctrl;
++ uint32_t vpp_blend_vd2_h_start_end;
++ uint32_t vpp_blend_vd2_v_start_end;
+ } viu;
+
+ struct {
+diff --git a/drivers/gpu/drm/meson/meson_overlay.c b/drivers/gpu/drm/meson/meson_overlay.c
+new file mode 100644
+index 0000000..ea7261a
+--- /dev/null
++++ b/drivers/gpu/drm/meson/meson_overlay.c
+@@ -0,0 +1,595 @@
++// SPDX-License-Identifier: GPL-2.0+
++/*
++ * Copyright (C) 2018 BayLibre, SAS
++ * Author: Neil Armstrong <narmstrong@baylibre.com>
++ * Copyright (C) 2015 Amlogic, Inc. All rights reserved.
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/mutex.h>
++#include <linux/bitfield.h>
++#include <linux/platform_device.h>
++#include <drm/drmP.h>
++#include <drm/drm_atomic.h>
++#include <drm/drm_atomic_helper.h>
++#include <drm/drm_plane_helper.h>
++#include <drm/drm_gem_cma_helper.h>
++#include <drm/drm_fb_cma_helper.h>
++#include <drm/drm_rect.h>
++
++#include "meson_overlay.h"
++#include "meson_vpp.h"
++#include "meson_viu.h"
++#include "meson_registers.h"
++
++/* VD1_IF0_GEN_REG */
++#define VD_URGENT_CHROMA BIT(28)
++#define VD_URGENT_LUMA BIT(27)
++#define VD_HOLD_LINES(lines) FIELD_PREP(GENMASK(24, 19), lines)
++#define VD_DEMUX_MODE_RGB BIT(16)
++#define VD_BYTES_PER_PIXEL(val) FIELD_PREP(GENMASK(15, 14), val)
++#define VD_CHRO_RPT_LASTL_CTRL BIT(6)
++#define VD_LITTLE_ENDIAN BIT(4)
++#define VD_SEPARATE_EN BIT(1)
++#define VD_ENABLE BIT(0)
++
++/* VD1_IF0_CANVAS0 */
++#define CANVAS_ADDR2(addr) FIELD_PREP(GENMASK(23, 16), addr)
++#define CANVAS_ADDR1(addr) FIELD_PREP(GENMASK(15, 8), addr)
++#define CANVAS_ADDR0(addr) FIELD_PREP(GENMASK(7, 0), addr)
++
++/* VD1_IF0_LUMA_X0 VD1_IF0_CHROMA_X0 */
++#define VD_X_START(value) FIELD_PREP(GENMASK(14, 0), value)
++#define VD_X_END(value) FIELD_PREP(GENMASK(30, 16), value)
++
++/* VD1_IF0_LUMA_Y0 VD1_IF0_CHROMA_Y0 */
++#define VD_Y_START(value) FIELD_PREP(GENMASK(12, 0), value)
++#define VD_Y_END(value) FIELD_PREP(GENMASK(28, 16), value)
++
++/* VD1_IF0_GEN_REG2 */
++#define VD_COLOR_MAP(value) FIELD_PREP(GENMASK(1, 0), value)
++
++/* VIU_VD1_FMT_CTRL */
++#define VD_HORZ_Y_C_RATIO(value) FIELD_PREP(GENMASK(22, 21), value)
++#define VD_HORZ_FMT_EN BIT(20)
++#define VD_VERT_RPT_LINE0 BIT(16)
++#define VD_VERT_INITIAL_PHASE(value) FIELD_PREP(GENMASK(11, 8), value)
++#define VD_VERT_PHASE_STEP(value) FIELD_PREP(GENMASK(7, 1), value)
++#define VD_VERT_FMT_EN BIT(0)
++
++/* VPP_POSTBLEND_VD1_H_START_END */
++#define VD_H_END(value) FIELD_PREP(GENMASK(11, 0), value)
++#define VD_H_START(value) FIELD_PREP(GENMASK(27, 16), value)
++
++/* VPP_POSTBLEND_VD1_V_START_END */
++#define VD_V_END(value) FIELD_PREP(GENMASK(11, 0), value)
++#define VD_V_START(value) FIELD_PREP(GENMASK(27, 16), value)
++
++/* VPP_BLEND_VD2_V_START_END */
++#define VD2_V_END(value) FIELD_PREP(GENMASK(11, 0), value)
++#define VD2_V_START(value) FIELD_PREP(GENMASK(27, 16), value)
++
++/* VIU_VD1_FMT_W */
++#define VD_V_WIDTH(value) FIELD_PREP(GENMASK(11, 0), value)
++#define VD_H_WIDTH(value) FIELD_PREP(GENMASK(27, 16), value)
++
++/* VPP_HSC_REGION12_STARTP VPP_HSC_REGION34_STARTP */
++#define VD_REGION24_START(value) FIELD_PREP(GENMASK(11, 0), value)
++#define VD_REGION13_END(value) FIELD_PREP(GENMASK(27, 16), value)
++
++struct meson_overlay {
++ struct drm_plane base;
++ struct meson_drm *priv;
++};
++#define to_meson_overlay(x) container_of(x, struct meson_overlay, base)
++
++#define FRAC_16_16(mult, div) (((mult) << 16) / (div))
++
++static int meson_overlay_atomic_check(struct drm_plane *plane,
++ struct drm_plane_state *state)
++{
++ struct drm_crtc_state *crtc_state;
++
++ if (!state->crtc)
++ return 0;
++
++ crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc);
++ if (IS_ERR(crtc_state))
++ return PTR_ERR(crtc_state);
++
++ return drm_atomic_helper_check_plane_state(state, crtc_state,
++ FRAC_16_16(1, 5),
++ FRAC_16_16(5, 1),
++ true, true);
++}
++
++/* Takes a fixed 16.16 number and converts it to integer. */
++static inline int64_t fixed16_to_int(int64_t value)
++{
++ return value >> 16;
++}
++
++static const uint8_t skip_tab[6] = {0x24, 0x04, 0x68, 0x48, 0x28, 0x08};
++
++static void meson_overlay_get_vertical_phase(unsigned ratio_y,
++ int *phase,
++ int *repeat,
++ bool interlace)
++{
++ int offset_in = 0;
++ int offset_out = 0;
++ int repeat_skip = 0;
++
++ if (!interlace && ratio_y > (1 << 18)) {
++ offset_out = (1 * ratio_y) >> 10;
++ }
++
++ while ((offset_in + (4 << 8)) <= offset_out) {
++ repeat_skip++;
++ offset_in += 4 << 8;
++ }
++
++ *phase = (offset_out - offset_in) >> 2;
++
++ if (*phase > 0x100)
++ repeat_skip++;
++
++ *phase = *phase & 0xff;
++
++ if (repeat_skip > 5)
++ repeat_skip = 5;
++
++ *repeat = skip_tab[repeat_skip];
++}
++
++static void meson_overlay_setup_scaler_params(struct meson_drm *priv,
++ struct drm_plane *plane,
++ bool interlace_mode)
++{
++ struct drm_plane_state *state = plane->state;
++ struct drm_crtc_state *crtc_state = priv->crtc->state;
++ int video_top, video_left, video_width, video_height;
++ unsigned int crop_top, crop_left;
++ unsigned int crtc_height, crtc_width;
++ unsigned int vd_start_lines, vd_end_lines;
++ unsigned int hd_start_lines, hd_end_lines;
++ unsigned int vsc_startp, vsc_endp;
++ unsigned int hsc_startp, hsc_endp;
++ unsigned int ratio_x, ratio_y;
++ unsigned int w_in, h_in;
++ int vphase, vphase_repeat_skip;
++ int temp_height, temp_width;
++ int temp, start, end;
++
++ if (!crtc_state) {
++ DRM_ERROR("Invalid crtc_state\n");
++ return;
++ }
++
++ crtc_height = crtc_state->mode.vdisplay;
++ crtc_width = crtc_state->mode.hdisplay;
++
++ w_in = fixed16_to_int(state->src_w);
++ h_in = fixed16_to_int(state->src_h);
++ crop_top = fixed16_to_int(state->src_x);
++ crop_left = fixed16_to_int(state->src_x);
++
++ video_top = state->crtc_y;
++ video_left = state->crtc_x;
++ video_width = state->crtc_w;
++ video_height = state->crtc_h;
++
++ DRM_DEBUG("crtc_width %d crtc_height %d interlace %d\n",
++ crtc_width, crtc_height, interlace_mode);
++ DRM_DEBUG("w_in %d h_in %d crop_top %d crop_left %d\n",
++ w_in, h_in, crop_top, crop_left);
++ DRM_DEBUG("video top %d left %d width %d height %d\n",
++ video_top, video_left, video_width, video_height);
++
++ ratio_x = (w_in << 18) / video_width;
++ ratio_y = (h_in << 18) / video_height;
++
++ /* TOFIX Interlace output */
++ if (interlace_mode)
++ ratio_y <<= 1;
++
++ if (ratio_x * video_width < (w_in << 18))
++ ratio_x++;
++
++ DRM_DEBUG("ratio x 0x%x y 0x%x\n", ratio_x, ratio_y);
++
++ meson_overlay_get_vertical_phase(ratio_y, &vphase, &vphase_repeat_skip,
++ interlace_mode);
++
++ DRM_DEBUG("vphase 0x%x skip %d\n", vphase, vphase_repeat_skip);
++
++ /* Vertical */
++
++ start = video_top + video_height / 2 - ((h_in << 17) / ratio_x);
++ end = (h_in << 18) / ratio_y + start - 1;
++
++ if (video_top < 0 && start < 0)
++ vd_start_lines = (-(start) * ratio_y) >> 18;
++ else if (start < video_top)
++ vd_start_lines = ((video_top - start) * ratio_y) >> 18;
++ else
++ vd_start_lines = 0;
++
++ if (video_top < 0)
++ temp_height = min_t(unsigned int, (video_top + video_height - 1),
++ (crtc_height - 1));
++ else
++ temp_height = min_t(unsigned int, (video_top + video_height - 1),
++ (crtc_height - 1)) - video_top + 1;
++
++ temp = vd_start_lines + (temp_height * ratio_y >> 18);
++ vd_end_lines = (temp <= (h_in - 1)) ? temp : (h_in - 1);
++
++ vd_start_lines += crop_left;
++ vd_end_lines += crop_left;
++
++ if (interlace_mode) {
++ start >>= 1;
++ end >>= 1;
++ }
++
++ vsc_startp = max_t(int, start,
++ max_t(int, 0, video_top));
++ vsc_endp = min_t(int, end,
++ min_t(int, crtc_height - 1,
++ video_top + video_height - 1));
++
++ DRM_DEBUG("vsc startp %d endp %d start_lines %d end_lines %d\n",
++ vsc_startp, vsc_endp, vd_start_lines, vd_end_lines);
++
++ /* Horizontal */
++
++ start = video_left + video_width / 2 - ((w_in << 17) / ratio_x);
++ end = (w_in << 18) / ratio_x + start - 1;
++
++ if (video_left < 0 && start < 0)
++ hd_start_lines = (-(start) * ratio_x) >> 18;
++ else if (start < video_left)
++ hd_start_lines = ((video_left - start) * ratio_x) >> 18;
++ else
++ hd_start_lines = 0;
++
++ if (video_left < 0)
++ temp_width = min_t(unsigned int, (video_left + video_width - 1),
++ (crtc_width - 1));
++ else
++ temp_width = min_t(unsigned int, (video_left + video_width - 1),
++ (crtc_width - 1)) - video_left + 1;
++
++ temp = hd_start_lines + (temp_width * ratio_x >> 18);
++ hd_end_lines = (temp <= (w_in - 1)) ? temp : (w_in - 1);
++
++ priv->viu.vpp_line_in_length = hd_end_lines - hd_start_lines + 1;
++ hsc_startp = max_t(int, start,
++ max_t(int, 0, video_left));
++ hsc_endp = min_t(int, end,
++ min_t(int, crtc_width - 1,
++ video_left + video_width - 1));
++
++ hd_start_lines += crop_top;
++ hd_end_lines += crop_top;
++
++ DRM_DEBUG("hsc startp %d endp %d start_lines %d end_lines %d\n",
++ hsc_startp, hsc_endp, hd_start_lines, hd_end_lines);
++
++ priv->viu.vpp_vsc_start_phase_step = ratio_y << 6;
++
++ priv->viu.vpp_vsc_ini_phase = vphase << 8;
++ priv->viu.vpp_vsc_phase_ctrl = (1 << 13) | (4 << 8) |
++ vphase_repeat_skip;
++
++ priv->viu.vd1_if0_luma_x0 = VD_X_START(hd_start_lines) |
++ VD_X_END(hd_end_lines);
++ priv->viu.vd1_if0_chroma_x0 = VD_X_START(hd_start_lines >> 1) |
++ VD_X_END(hd_end_lines >> 1);
++
++ priv->viu.viu_vd1_fmt_w = VD_H_WIDTH(hd_end_lines - hd_start_lines + 1) |
++ VD_V_WIDTH(hd_end_lines/2 - hd_start_lines/2 + 1);
++
++ priv->viu.vd1_if0_luma_y0 = VD_Y_START(vd_start_lines) |
++ VD_Y_END(vd_end_lines);
++
++ priv->viu.vd1_if0_chroma_y0 = VD_Y_START(vd_start_lines >> 1) |
++ VD_Y_END(vd_end_lines >> 1);
++
++ priv->viu.vpp_pic_in_height = h_in;
++
++ priv->viu.vpp_postblend_vd1_h_start_end = VD_H_START(hsc_startp) |
++ VD_H_END(hsc_endp);
++ priv->viu.vpp_blend_vd2_h_start_end = VD_H_START(hd_start_lines) |
++ VD_H_END(hd_end_lines);
++ priv->viu.vpp_hsc_region12_startp = VD_REGION13_END(0) |
++ VD_REGION24_START(hsc_startp);
++ priv->viu.vpp_hsc_region34_startp = VD_REGION13_END(hsc_startp) |
++ VD_REGION24_START(hsc_endp - hsc_startp);
++ priv->viu.vpp_hsc_region4_endp = hsc_endp - hsc_startp;
++ priv->viu.vpp_hsc_start_phase_step = ratio_x << 6;
++ priv->viu.vpp_hsc_region1_phase_slope = 0;
++ priv->viu.vpp_hsc_region3_phase_slope = 0;
++ priv->viu.vpp_hsc_phase_ctrl = (1 << 21) | (4 << 16);
++
++ priv->viu.vpp_line_in_length = hd_end_lines - hd_start_lines + 1;
++ priv->viu.vpp_preblend_h_size = hd_end_lines - hd_start_lines + 1;
++
++ priv->viu.vpp_postblend_vd1_v_start_end = VD_V_START(vsc_startp) |
++ VD_V_END(vsc_endp);
++ priv->viu.vpp_blend_vd2_v_start_end =
++ VD2_V_START((vd_end_lines + 1) >> 1) |
++ VD2_V_END(vd_end_lines);
++
++ priv->viu.vpp_vsc_region12_startp = 0;
++ priv->viu.vpp_vsc_region34_startp =
++ VD_REGION13_END(vsc_endp - vsc_startp) |
++ VD_REGION24_START(vsc_endp - vsc_startp);
++ priv->viu.vpp_vsc_region4_endp = vsc_endp - vsc_startp;
++ priv->viu.vpp_vsc_start_phase_step = ratio_y << 6;
++}
++
++static void meson_overlay_atomic_update(struct drm_plane *plane,
++ struct drm_plane_state *old_state)
++{
++ struct meson_overlay *meson_overlay = to_meson_overlay(plane);
++ struct drm_plane_state *state = plane->state;
++ struct drm_framebuffer *fb = state->fb;
++ struct meson_drm *priv = meson_overlay->priv;
++ struct drm_gem_cma_object *gem;
++ unsigned long flags;
++ bool interlace_mode;
++
++ DRM_DEBUG_DRIVER("\n");
++
++ interlace_mode = state->crtc->mode.flags & DRM_MODE_FLAG_INTERLACE;
++
++ /*
++ * Update Coordinates
++ * Update Formats
++ * Update Buffer
++ * Enable Plane
++ */
++ spin_lock_irqsave(&priv->drm->event_lock, flags);
++
++ priv->viu.vd1_if0_gen_reg = VD_URGENT_CHROMA |
++ VD_URGENT_LUMA |
++ VD_HOLD_LINES(9) |
++ VD_CHRO_RPT_LASTL_CTRL |
++ VD_ENABLE;
++
++ /* Setup scaler params */
++ meson_overlay_setup_scaler_params(priv, plane, interlace_mode);
++
++ //VD1_IF0_CANVAS1=0
++ //VD1_IF0_CHROMA_X1=0
++ //VD1_IF0_CHROMA_Y1=0
++ priv->viu.vd1_if0_repeat_loop = 0;
++ priv->viu.vd1_if0_luma0_rpt_pat = interlace_mode ? 8 : 0;
++ priv->viu.vd1_if0_chroma0_rpt_pat = interlace_mode ? 8 : 0;
++ //VD1_IF0_LUMA1_RPT_PAT=0
++ //VD1_IF0_CHROMA1_RPT_PAT=0
++ //VD1_IF0_LUMA_PSEL=0
++ //VD1_IF0_CHROMA_PSEL=0
++ //VD1_IF0_DUMMY_PIXEL=?
++ priv->viu.vd1_range_map_y = 0;
++ priv->viu.vd1_range_map_cb = 0;
++ priv->viu.vd1_range_map_cr = 0;
++
++ /* Default values for RGB888/YUV444 */
++ priv->viu.vd1_if0_gen_reg2 = 0;
++ priv->viu.viu_vd1_fmt_ctrl = 0;
++
++ switch (fb->format->format) {
++ case DRM_FORMAT_RGB888:
++ /* TOFIX enable RGB2YUV somewhere ! */
++ priv->viu.vd1_if0_gen_reg |= VD_DEMUX_MODE_RGB |
++ VD_BYTES_PER_PIXEL(2);
++ priv->viu.vd1_if0_canvas0 =
++ CANVAS_ADDR2(priv->canvas_id_vd1_0) |
++ CANVAS_ADDR1(priv->canvas_id_vd1_0) |
++ CANVAS_ADDR0(priv->canvas_id_vd1_0);
++ break;
++ case DRM_FORMAT_YUYV:
++ priv->viu.vd1_if0_gen_reg |= VD_BYTES_PER_PIXEL(1);
++ priv->viu.vd1_if0_canvas0 =
++ CANVAS_ADDR2(priv->canvas_id_vd1_0) |
++ CANVAS_ADDR1(priv->canvas_id_vd1_0) |
++ CANVAS_ADDR0(priv->canvas_id_vd1_0);
++ priv->viu.viu_vd1_fmt_ctrl = VD_HORZ_Y_C_RATIO(1) | /* /2 */
++ VD_HORZ_FMT_EN |
++ VD_VERT_RPT_LINE0 |
++ VD_VERT_INITIAL_PHASE(12) |
++ VD_VERT_PHASE_STEP(16) | /* /2 */
++ VD_VERT_FMT_EN;
++ break;
++ case DRM_FORMAT_NV12:
++ case DRM_FORMAT_NV21:
++ priv->viu.vd1_if0_gen_reg |= VD_SEPARATE_EN;
++ priv->viu.vd1_if0_canvas0 =
++ CANVAS_ADDR2(priv->canvas_id_vd1_1) |
++ CANVAS_ADDR1(priv->canvas_id_vd1_1) |
++ CANVAS_ADDR0(priv->canvas_id_vd1_0);
++ if (fb->format->format == DRM_FORMAT_NV12)
++ priv->viu.vd1_if0_gen_reg2 = VD_COLOR_MAP(1);
++ else
++ priv->viu.vd1_if0_gen_reg2 = VD_COLOR_MAP(2);
++ priv->viu.viu_vd1_fmt_ctrl = VD_HORZ_Y_C_RATIO(1) | /* /2 */
++ VD_HORZ_FMT_EN |
++ VD_VERT_RPT_LINE0 |
++ VD_VERT_INITIAL_PHASE(12) |
++ VD_VERT_PHASE_STEP(8) | /* /4 */
++ VD_VERT_FMT_EN;
++ break;
++ case DRM_FORMAT_YUV444:
++ case DRM_FORMAT_YUV422:
++ case DRM_FORMAT_YUV420:
++ case DRM_FORMAT_YUV411:
++ case DRM_FORMAT_YUV410:
++ priv->viu.vd1_if0_gen_reg |= VD_SEPARATE_EN;
++ priv->viu.vd1_if0_canvas0 =
++ CANVAS_ADDR2(priv->canvas_id_vd1_2) |
++ CANVAS_ADDR1(priv->canvas_id_vd1_1) |
++ CANVAS_ADDR0(priv->canvas_id_vd1_0);
++ switch (fb->format->format) {
++ case DRM_FORMAT_YUV422:
++ priv->viu.viu_vd1_fmt_ctrl =
++ VD_HORZ_Y_C_RATIO(1) | /* /2 */
++ VD_HORZ_FMT_EN |
++ VD_VERT_RPT_LINE0 |
++ VD_VERT_INITIAL_PHASE(12) |
++ VD_VERT_PHASE_STEP(16) | /* /2 */
++ VD_VERT_FMT_EN;
++ break;
++ case DRM_FORMAT_YUV420:
++ priv->viu.viu_vd1_fmt_ctrl =
++ VD_HORZ_Y_C_RATIO(1) | /* /2 */
++ VD_HORZ_FMT_EN |
++ VD_VERT_RPT_LINE0 |
++ VD_VERT_INITIAL_PHASE(12) |
++ VD_VERT_PHASE_STEP(8) | /* /4 */
++ VD_VERT_FMT_EN;
++ break;
++ case DRM_FORMAT_YUV411:
++ priv->viu.viu_vd1_fmt_ctrl =
++ VD_HORZ_Y_C_RATIO(2) | /* /4 */
++ VD_HORZ_FMT_EN |
++ VD_VERT_RPT_LINE0 |
++ VD_VERT_INITIAL_PHASE(12) |
++ VD_VERT_PHASE_STEP(16) | /* /2 */
++ VD_VERT_FMT_EN;
++ break;
++ case DRM_FORMAT_YUV410:
++ priv->viu.viu_vd1_fmt_ctrl =
++ VD_HORZ_Y_C_RATIO(2) | /* /4 */
++ VD_HORZ_FMT_EN |
++ VD_VERT_RPT_LINE0 |
++ VD_VERT_INITIAL_PHASE(12) |
++ VD_VERT_PHASE_STEP(8) | /* /4 */
++ VD_VERT_FMT_EN;
++ break;
++ }
++ break;
++ }
++
++ /* Update Canvas with buffer address */
++ priv->viu.vd1_planes = drm_format_num_planes(fb->format->format);
++
++ switch (priv->viu.vd1_planes) {
++ case 3:
++ gem = drm_fb_cma_get_gem_obj(fb, 2);
++ priv->viu.vd1_addr2 = gem->paddr + fb->offsets[2];
++ priv->viu.vd1_stride2 = fb->pitches[2];
++ priv->viu.vd1_height2 =
++ drm_format_plane_height(fb->height,
++ fb->format->format, 2);
++ DRM_DEBUG("plane 2 addr 0x%x stride %d height %d\n",
++ priv->viu.vd1_addr2,
++ priv->viu.vd1_stride2,
++ priv->viu.vd1_height2);
++ case 2:
++ gem = drm_fb_cma_get_gem_obj(fb, 1);
++ priv->viu.vd1_addr1 = gem->paddr + fb->offsets[1];
++ priv->viu.vd1_stride1 = fb->pitches[1];
++ priv->viu.vd1_height1 =
++ drm_format_plane_height(fb->height,
++ fb->format->format, 1);
++ DRM_DEBUG("plane 1 addr 0x%x stride %d height %d\n",
++ priv->viu.vd1_addr1,
++ priv->viu.vd1_stride1,
++ priv->viu.vd1_height1);
++ case 1:
++ gem = drm_fb_cma_get_gem_obj(fb, 0);
++ priv->viu.vd1_addr0 = gem->paddr + fb->offsets[0];
++ priv->viu.vd1_stride0 = fb->pitches[0];
++ priv->viu.vd1_height0 =
++ drm_format_plane_height(fb->height,
++ fb->format->format, 0);
++ DRM_DEBUG("plane 0 addr 0x%x stride %d height %d\n",
++ priv->viu.vd1_addr0,
++ priv->viu.vd1_stride0,
++ priv->viu.vd1_height0);
++ }
++
++ priv->viu.vd1_enabled = true;
++
++ spin_unlock_irqrestore(&priv->drm->event_lock, flags);
++
++ DRM_DEBUG_DRIVER("\n");
++}
++
++static void meson_overlay_atomic_disable(struct drm_plane *plane,
++ struct drm_plane_state *old_state)
++{
++ struct meson_overlay *meson_overlay = to_meson_overlay(plane);
++ struct meson_drm *priv = meson_overlay->priv;
++
++ DRM_DEBUG_DRIVER("\n");
++
++ priv->viu.vd1_enabled = false;
++
++ /* Disable VD1 */
++ writel_bits_relaxed(VPP_VD1_POSTBLEND | VPP_VD1_PREBLEND, 0,
++ priv->io_base + _REG(VPP_MISC));
++
++}
++
++static const struct drm_plane_helper_funcs meson_overlay_helper_funcs = {
++ .atomic_check = meson_overlay_atomic_check,
++ .atomic_disable = meson_overlay_atomic_disable,
++ .atomic_update = meson_overlay_atomic_update,
++};
++
++static const struct drm_plane_funcs meson_overlay_funcs = {
++ .update_plane = drm_atomic_helper_update_plane,
++ .disable_plane = drm_atomic_helper_disable_plane,
++ .destroy = drm_plane_cleanup,
++ .reset = drm_atomic_helper_plane_reset,
++ .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
++ .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
++};
++
++static const uint32_t supported_drm_formats[] = {
++ DRM_FORMAT_RGB888,
++ DRM_FORMAT_YUYV,
++ DRM_FORMAT_NV12,
++ DRM_FORMAT_NV21,
++ DRM_FORMAT_YUV444,
++ DRM_FORMAT_YUV422,
++ DRM_FORMAT_YUV420,
++ DRM_FORMAT_YUV411,
++ DRM_FORMAT_YUV410,
++};
++
++int meson_overlay_create(struct meson_drm *priv)
++{
++ struct meson_overlay *meson_overlay;
++ struct drm_plane *plane;
++
++ DRM_DEBUG_DRIVER("\n");
++
++ meson_overlay = devm_kzalloc(priv->drm->dev, sizeof(*meson_overlay),
++ GFP_KERNEL);
++ if (!meson_overlay)
++ return -ENOMEM;
++
++ meson_overlay->priv = priv;
++ plane = &meson_overlay->base;
++
++ drm_universal_plane_init(priv->drm, plane, 0xFF,
++ &meson_overlay_funcs,
++ supported_drm_formats,
++ ARRAY_SIZE(supported_drm_formats),
++ NULL,
++ DRM_PLANE_TYPE_OVERLAY, "meson_overlay_plane");
++
++ drm_plane_helper_add(plane, &meson_overlay_helper_funcs);
++
++ priv->overlay_plane = plane;
++
++ DRM_DEBUG_DRIVER("\n");
++
++ return 0;
++}
+diff --git a/drivers/gpu/drm/meson/meson_overlay.h b/drivers/gpu/drm/meson/meson_overlay.h
+new file mode 100644
+index 0000000..0fd63da
+--- /dev/null
++++ b/drivers/gpu/drm/meson/meson_overlay.h
+@@ -0,0 +1,14 @@
++// SPDX-License-Identifier: GPL-2.0+
++/*
++ * Copyright (C) 2018 BayLibre, SAS
++ * Author: Neil Armstrong <narmstrong@baylibre.com>
++ */
++
++#ifndef __MESON_OVERLAY_H
++#define __MESON_OVERLAY_H
++
++#include "meson_drv.h"
++
++int meson_overlay_create(struct meson_drm *priv);
++
++#endif /* __MESON_OVERLAY_H */
+diff --git a/drivers/gpu/drm/meson/meson_registers.h b/drivers/gpu/drm/meson/meson_registers.h
+index bca8714..5c7e02c 100644
+--- a/drivers/gpu/drm/meson/meson_registers.h
++++ b/drivers/gpu/drm/meson/meson_registers.h
+@@ -286,6 +286,7 @@
+ #define VIU_OSD1_MATRIX_COEF22_30 0x1a9d
+ #define VIU_OSD1_MATRIX_COEF31_32 0x1a9e
+ #define VIU_OSD1_MATRIX_COEF40_41 0x1a9f
++#define VD1_IF0_GEN_REG3 0x1aa7
+ #define VIU_OSD1_EOTF_CTL 0x1ad4
+ #define VIU_OSD1_EOTF_COEF00_01 0x1ad5
+ #define VIU_OSD1_EOTF_COEF02_10 0x1ad6
+@@ -297,6 +298,7 @@
+ #define VIU_OSD1_OETF_CTL 0x1adc
+ #define VIU_OSD1_OETF_LUT_ADDR_PORT 0x1add
+ #define VIU_OSD1_OETF_LUT_DATA_PORT 0x1ade
++#define AFBC_ENABLE 0x1ae0
+
+ /* vpp */
+ #define VPP_DUMMY_DATA 0x1d00
+@@ -349,6 +351,7 @@
+ #define VPP_VD2_PREBLEND BIT(15)
+ #define VPP_OSD1_PREBLEND BIT(16)
+ #define VPP_OSD2_PREBLEND BIT(17)
++#define VPP_COLOR_MNG_ENABLE BIT(28)
+ #define VPP_OFIFO_SIZE 0x1d27
+ #define VPP_FIFO_STATUS 0x1d28
+ #define VPP_SMOKE_CTRL 0x1d29
+diff --git a/drivers/gpu/drm/meson/meson_viu.c b/drivers/gpu/drm/meson/meson_viu.c
+index 5b48c4c..a423e7a 100644
+--- a/drivers/gpu/drm/meson/meson_viu.c
++++ b/drivers/gpu/drm/meson/meson_viu.c
+@@ -328,6 +328,21 @@ void meson_viu_init(struct meson_drm *priv)
+ 0xff << OSD_REPLACE_SHIFT,
+ priv->io_base + _REG(VIU_OSD2_CTRL_STAT2));
+
++ /* Disable VD1 AFBC */
++ /* di_mif0_en=0 mif0_to_vpp_en=0 di_mad_en=0 */
++ writel_bits_relaxed(0x7 << 16, 0,
++ priv->io_base + _REG(VIU_MISC_CTRL0));
++ /* afbc vd1 set=0 */
++ writel_bits_relaxed(BIT(20), 0,
++ priv->io_base + _REG(VIU_MISC_CTRL0));
++ writel_relaxed(0, priv->io_base + _REG(AFBC_ENABLE));
++
++ writel_relaxed(0x00FF00C0,
++ priv->io_base + _REG(VD1_IF0_LUMA_FIFO_SIZE));
++ writel_relaxed(0x00FF00C0,
++ priv->io_base + _REG(VD2_IF0_LUMA_FIFO_SIZE));
++
++
+ priv->viu.osd1_enabled = false;
+ priv->viu.osd1_commit = false;
+ priv->viu.osd1_interlace = false;
+diff --git a/drivers/gpu/drm/meson/meson_vpp.c b/drivers/gpu/drm/meson/meson_vpp.c
+index 27356f8..5dc24a9 100644
+--- a/drivers/gpu/drm/meson/meson_vpp.c
++++ b/drivers/gpu/drm/meson/meson_vpp.c
+@@ -122,6 +122,31 @@ static void meson_vpp_write_scaling_filter_coefs(struct meson_drm *priv,
+ priv->io_base + _REG(VPP_OSD_SCALE_COEF));
+ }
+
++static const uint32_t vpp_filter_coefs_bicubic[] = {
++ 0x00800000, 0x007f0100, 0xff7f0200, 0xfe7f0300,
++ 0xfd7e0500, 0xfc7e0600, 0xfb7d0800, 0xfb7c0900,
++ 0xfa7b0b00, 0xfa7a0dff, 0xf9790fff, 0xf97711ff,
++ 0xf87613ff, 0xf87416fe, 0xf87218fe, 0xf8701afe,
++ 0xf76f1dfd, 0xf76d1ffd, 0xf76b21fd, 0xf76824fd,
++ 0xf76627fc, 0xf76429fc, 0xf7612cfc, 0xf75f2ffb,
++ 0xf75d31fb, 0xf75a34fb, 0xf75837fa, 0xf7553afa,
++ 0xf8523cfa, 0xf8503ff9, 0xf84d42f9, 0xf84a45f9,
++ 0xf84848f8
++};
++
++static void meson_vpp_write_vd_scaling_filter_coefs(struct meson_drm *priv,
++ const unsigned int *coefs,
++ bool is_horizontal)
++{
++ int i;
++
++ writel_relaxed(is_horizontal ? BIT(8) : 0,
++ priv->io_base + _REG(VPP_SCALE_COEF_IDX));
++ for (i = 0; i < 33; i++)
++ writel_relaxed(coefs[i],
++ priv->io_base + _REG(VPP_SCALE_COEF));
++}
++
+ void meson_vpp_init(struct meson_drm *priv)
+ {
+ /* set dummy data default YUV black */
+@@ -150,17 +175,34 @@ void meson_vpp_init(struct meson_drm *priv)
+
+ /* Force all planes off */
+ writel_bits_relaxed(VPP_OSD1_POSTBLEND | VPP_OSD2_POSTBLEND |
+- VPP_VD1_POSTBLEND | VPP_VD2_POSTBLEND, 0,
++ VPP_VD1_POSTBLEND | VPP_VD2_POSTBLEND |
++ VPP_VD1_PREBLEND | VPP_VD2_PREBLEND, 0,
+ priv->io_base + _REG(VPP_MISC));
+
++ /* Setup default VD settings */
++ writel_relaxed(4096,
++ priv->io_base + _REG(VPP_PREBLEND_VD1_H_START_END));
++ writel_relaxed(4096,
++ priv->io_base + _REG(VPP_BLEND_VD2_H_START_END));
++
+ /* Disable Scalers */
+ writel_relaxed(0, priv->io_base + _REG(VPP_OSD_SC_CTRL0));
+ writel_relaxed(0, priv->io_base + _REG(VPP_OSD_VSC_CTRL0));
+ writel_relaxed(0, priv->io_base + _REG(VPP_OSD_HSC_CTRL0));
++ writel_relaxed(4 | (4 << 8) | BIT(15),
++ priv->io_base + _REG(VPP_SC_MISC));
++
++ writel_relaxed(1, priv->io_base + _REG(VPP_VADJ_CTRL));
+
+ /* Write in the proper filter coefficients. */
+ meson_vpp_write_scaling_filter_coefs(priv,
+ vpp_filter_coefs_4point_bspline, false);
+ meson_vpp_write_scaling_filter_coefs(priv,
+ vpp_filter_coefs_4point_bspline, true);
++
++ /* Write the VD proper filter coefficients. */
++ meson_vpp_write_vd_scaling_filter_coefs(priv, vpp_filter_coefs_bicubic,
++ false);
++ meson_vpp_write_vd_scaling_filter_coefs(priv, vpp_filter_coefs_bicubic,
++ true);
+ }
+diff --git a/drivers/soc/amlogic/meson-gx-pwrc-vpu.c b/drivers/soc/amlogic/meson-gx-pwrc-vpu.c
+index 6289965..05421d0 100644
+--- a/drivers/soc/amlogic/meson-gx-pwrc-vpu.c
++++ b/drivers/soc/amlogic/meson-gx-pwrc-vpu.c
+@@ -54,12 +54,12 @@ static int meson_gx_pwrc_vpu_power_off(struct generic_pm_domain *genpd)
+ /* Power Down Memories */
+ for (i = 0; i < 32; i += 2) {
+ regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0,
+- 0x2 << i, 0x3 << i);
++ 0x3 << i, 0x3 << i);
+ udelay(5);
+ }
+ for (i = 0; i < 32; i += 2) {
+ regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1,
+- 0x2 << i, 0x3 << i);
++ 0x3 << i, 0x3 << i);
+ udelay(5);
+ }
+ for (i = 8; i < 16; i++) {
+@@ -108,13 +108,13 @@ static int meson_gx_pwrc_vpu_power_on(struct generic_pm_domain *genpd)
+ /* Power Up Memories */
+ for (i = 0; i < 32; i += 2) {
+ regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0,
+- 0x2 << i, 0);
++ 0x3 << i, 0);
+ udelay(5);
+ }
+
+ for (i = 0; i < 32; i += 2) {
+ regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1,
+- 0x2 << i, 0);
++ 0x3 << i, 0);
+ udelay(5);
+ }
+
diff --git a/testing/linux-amlogic/0026-media-meson-add-v4l2-m2m-video-decoder-driver.patch b/testing/linux-amlogic/0026-media-meson-add-v4l2-m2m-video-decoder-driver.patch
new file mode 100644
index 0000000000..5967c19a07
--- /dev/null
+++ b/testing/linux-amlogic/0026-media-meson-add-v4l2-m2m-video-decoder-driver.patch
@@ -0,0 +1,5906 @@
+From ec4b3da71229c2ba3f56b62cfcab4b2d18eda4eb Mon Sep 17 00:00:00 2001
+From: Maxime Jourdan <maxi.jourdan@wanadoo.fr>
+Date: Thu, 26 Jul 2018 21:35:52 +0200
+Subject: [PATCH] media: meson: add v4l2 m2m video decoder driver
+
+Amlogic SoCs feature a powerful video decoder unit able to
+decode many formats, with a performance of usually up to 4k60.
+
+This is a driver for this IP that is based around the v4l2 m2m framework.
+
+It features decoding for:
+- MPEG 1/2/4, H.264, MJPEG, (partial) HEVC 8-bit
+
+Even though they are supported in hardware, it doesn't leverage support for:
+- HEVC 10-bit, VP9, VC1 (all those are in TODOs)
+
+The code was made in such a way to allow easy inclusion of those formats in the future.
+Supported SoCs are: GXBB (S905), GXL (S905X/W/D), GXM (S912)
+
+Specifically, two of the vdec units are enabled within this driver:
+
+- VDEC_1: can decode MPEG 1/2/4, H.264, MJPEG
+- VDEC_HEVC: can decode HEVC
+
+There is also a hardware bitstream parser (ESPARSER) that is handled here.
+---
+ drivers/media/platform/Kconfig | 10 +
+ drivers/media/platform/meson/Makefile | 1 +
+ drivers/media/platform/meson/vdec/Makefile | 7 +
+ drivers/media/platform/meson/vdec/canvas.c | 69 +
+ drivers/media/platform/meson/vdec/canvas.h | 42 +
+ drivers/media/platform/meson/vdec/codec_h264.c | 376 ++++++
+ drivers/media/platform/meson/vdec/codec_h264.h | 13 +
+ drivers/media/platform/meson/vdec/codec_helpers.c | 45 +
+ drivers/media/platform/meson/vdec/codec_helpers.h | 8 +
+ drivers/media/platform/meson/vdec/codec_hevc.c | 1383 +++++++++++++++++++++
+ drivers/media/platform/meson/vdec/codec_hevc.h | 13 +
+ drivers/media/platform/meson/vdec/codec_mjpeg.c | 203 +++
+ drivers/media/platform/meson/vdec/codec_mjpeg.h | 13 +
+ drivers/media/platform/meson/vdec/codec_mpeg12.c | 183 +++
+ drivers/media/platform/meson/vdec/codec_mpeg12.h | 13 +
+ drivers/media/platform/meson/vdec/codec_mpeg4.c | 213 ++++
+ drivers/media/platform/meson/vdec/codec_mpeg4.h | 13 +
+ drivers/media/platform/meson/vdec/esparser.c | 320 +++++
+ drivers/media/platform/meson/vdec/esparser.h | 16 +
+ drivers/media/platform/meson/vdec/hevc_regs.h | 742 +++++++++++
+ drivers/media/platform/meson/vdec/vdec.c | 1009 +++++++++++++++
+ drivers/media/platform/meson/vdec/vdec.h | 152 +++
+ drivers/media/platform/meson/vdec/vdec_1.c | 266 ++++
+ drivers/media/platform/meson/vdec/vdec_1.h | 13 +
+ drivers/media/platform/meson/vdec/vdec_hevc.c | 188 +++
+ drivers/media/platform/meson/vdec/vdec_hevc.h | 22 +
+ drivers/media/platform/meson/vdec/vdec_platform.c | 273 ++++
+ drivers/media/platform/meson/vdec/vdec_platform.h | 29 +
+ 28 files changed, 5635 insertions(+)
+ create mode 100644 drivers/media/platform/meson/vdec/Makefile
+ create mode 100644 drivers/media/platform/meson/vdec/canvas.c
+ create mode 100644 drivers/media/platform/meson/vdec/canvas.h
+ create mode 100644 drivers/media/platform/meson/vdec/codec_h264.c
+ create mode 100644 drivers/media/platform/meson/vdec/codec_h264.h
+ create mode 100644 drivers/media/platform/meson/vdec/codec_helpers.c
+ create mode 100644 drivers/media/platform/meson/vdec/codec_helpers.h
+ create mode 100644 drivers/media/platform/meson/vdec/codec_hevc.c
+ create mode 100644 drivers/media/platform/meson/vdec/codec_hevc.h
+ create mode 100644 drivers/media/platform/meson/vdec/codec_mjpeg.c
+ create mode 100644 drivers/media/platform/meson/vdec/codec_mjpeg.h
+ create mode 100644 drivers/media/platform/meson/vdec/codec_mpeg12.c
+ create mode 100644 drivers/media/platform/meson/vdec/codec_mpeg12.h
+ create mode 100644 drivers/media/platform/meson/vdec/codec_mpeg4.c
+ create mode 100644 drivers/media/platform/meson/vdec/codec_mpeg4.h
+ create mode 100644 drivers/media/platform/meson/vdec/esparser.c
+ create mode 100644 drivers/media/platform/meson/vdec/esparser.h
+ create mode 100644 drivers/media/platform/meson/vdec/hevc_regs.h
+ create mode 100644 drivers/media/platform/meson/vdec/vdec.c
+ create mode 100644 drivers/media/platform/meson/vdec/vdec.h
+ create mode 100644 drivers/media/platform/meson/vdec/vdec_1.c
+ create mode 100644 drivers/media/platform/meson/vdec/vdec_1.h
+ create mode 100644 drivers/media/platform/meson/vdec/vdec_hevc.c
+ create mode 100644 drivers/media/platform/meson/vdec/vdec_hevc.h
+ create mode 100644 drivers/media/platform/meson/vdec/vdec_platform.c
+ create mode 100644 drivers/media/platform/meson/vdec/vdec_platform.h
+
+diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
+index 2728376..42d600b 100644
+--- a/drivers/media/platform/Kconfig
++++ b/drivers/media/platform/Kconfig
+@@ -482,6 +482,16 @@ config VIDEO_QCOM_VENUS
+ on various Qualcomm SoCs.
+ To compile this driver as a module choose m here.
+
++config VIDEO_AML_MESON_VDEC
++ tristate "AMLogic video decoder driver"
++ depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA
++ depends on (ARCH_MESON) || COMPILE_TEST
++ select VIDEOBUF2_DMA_CONTIG
++ select V4L2_MEM2MEM_DEV
++ ---help---
++ Support for the video decoder found in gxbb/gxl/gxm chips.
++ Can decode MPEG 1/2/4, H.264, MJPEG, HEVC 8-bit.
++
+ endif # V4L_MEM2MEM_DRIVERS
+
+ # TI VIDEO PORT Helper Modules
+diff --git a/drivers/media/platform/meson/Makefile b/drivers/media/platform/meson/Makefile
+index 597beb8..5c935c2 100644
+--- a/drivers/media/platform/meson/Makefile
++++ b/drivers/media/platform/meson/Makefile
+@@ -1 +1,2 @@
+ obj-$(CONFIG_VIDEO_MESON_AO_CEC) += ao-cec.o
++obj-$(CONFIG_VIDEO_AML_MESON_VDEC) += vdec/
+diff --git a/drivers/media/platform/meson/vdec/Makefile b/drivers/media/platform/meson/vdec/Makefile
+new file mode 100644
+index 0000000..9f506bc
+--- /dev/null
++++ b/drivers/media/platform/meson/vdec/Makefile
+@@ -0,0 +1,7 @@
++# SPDX-License-Identifier: GPL-2.0
++# Makefile for AMLogic meson video decoder driver
++
++meson-vdec-objs += vdec.o vdec_1.o vdec_hevc.o esparser.o canvas.o codec_helpers.o codec_mpeg12.o codec_h264.o codec_hevc.o vdec_platform.o codec_mpeg4.o codec_mjpeg.o
++
++obj-$(CONFIG_VIDEO_AML_MESON_VDEC) += meson-vdec.o
++
+diff --git a/drivers/media/platform/meson/vdec/canvas.c b/drivers/media/platform/meson/vdec/canvas.c
+new file mode 100644
+index 0000000..92a8cf7
+--- /dev/null
++++ b/drivers/media/platform/meson/vdec/canvas.c
+@@ -0,0 +1,69 @@
++/*
++ * Copyright (C) 2016 BayLibre, SAS
++ * Author: Neil Armstrong <narmstrong@baylibre.com>
++ * Copyright (C) 2015 Amlogic, Inc. All rights reserved.
++ * Copyright (C) 2014 Endless Mobile
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <asm/io.h>
++#include "canvas.h"
++
++/* XXX: There is already a canvas implementation in the DRM subsystem but it's
++ * tied to it. This one is almost entirely copied from it.
++ * We should have a generic canvas provider for meson.
++ */
++
++/**
++ * DOC: Canvas
++ *
++ * CANVAS is a memory zone where physical memory frames information
++ * are stored for the VIU to scanout.
++ */
++
++/* DMC Registers */
++#define DMC_CAV_LUT_DATAL 0x48 /* 0x12 offset in data sheet */
++#define CANVAS_WIDTH_LBIT 29
++#define CANVAS_WIDTH_LWID 3
++#define DMC_CAV_LUT_DATAH 0x4c /* 0x13 offset in data sheet */
++#define CANVAS_WIDTH_HBIT 0
++#define CANVAS_HEIGHT_BIT 9
++#define CANVAS_BLKMODE_BIT 24
++#define DMC_CAV_LUT_ADDR 0x50 /* 0x14 offset in data sheet */
++#define CANVAS_LUT_WR_EN (0x2 << 8)
++#define CANVAS_LUT_RD_EN (0x1 << 8)
++
++void vdec_canvas_setup(void __iomem *dmc_base,
++ uint32_t canvas_index, uint32_t addr,
++ uint32_t stride, uint32_t height,
++ unsigned int wrap,
++ unsigned int blkmode)
++{
++ writel_relaxed((((addr + 7) >> 3)) |
++ (((stride + 7) >> 3) << CANVAS_WIDTH_LBIT), dmc_base + DMC_CAV_LUT_DATAL);
++
++ writel_relaxed(((((stride + 7) >> 3) >> CANVAS_WIDTH_LWID) <<
++ CANVAS_WIDTH_HBIT) |
++ (height << CANVAS_HEIGHT_BIT) |
++ (wrap << 22) |
++ (blkmode << CANVAS_BLKMODE_BIT) | (7 << 26), dmc_base + DMC_CAV_LUT_DATAH);
++
++ writel_relaxed(CANVAS_LUT_WR_EN | canvas_index, dmc_base + DMC_CAV_LUT_ADDR);
++
++ /* Force a read-back to make sure everything is flushed. */
++ readl_relaxed(dmc_base + DMC_CAV_LUT_DATAH);
++}
+diff --git a/drivers/media/platform/meson/vdec/canvas.h b/drivers/media/platform/meson/vdec/canvas.h
+new file mode 100644
+index 0000000..b8b5409
+--- /dev/null
++++ b/drivers/media/platform/meson/vdec/canvas.h
+@@ -0,0 +1,42 @@
++/*
++ * Copyright (C) 2016 BayLibre, SAS
++ * Author: Neil Armstrong <narmstrong@baylibre.com>
++ * Copyright (C) 2014 Endless Mobile
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, see <http://www.gnu.org/licenses/>.
++ */
++
++/* Canvas LUT Memory */
++
++#ifndef __MESON_CANVAS_H
++#define __MESON_CANVAS_H
++
++#define MESON_CANVAS_ID_OSD1 0x4e
++
++/* Canvas configuration. */
++#define MESON_CANVAS_WRAP_NONE 0x00
++#define MESON_CANVAS_WRAP_X 0x01
++#define MESON_CANVAS_WRAP_Y 0x02
++
++#define MESON_CANVAS_BLKMODE_LINEAR 0x00
++#define MESON_CANVAS_BLKMODE_32x32 0x01
++#define MESON_CANVAS_BLKMODE_64x64 0x02
++
++void vdec_canvas_setup(void __iomem *dmc_base,
++ uint32_t canvas_index, uint32_t addr,
++ uint32_t stride, uint32_t height,
++ unsigned int wrap,
++ unsigned int blkmode);
++
++#endif /* __MESON_CANVAS_H */
+diff --git a/drivers/media/platform/meson/vdec/codec_h264.c b/drivers/media/platform/meson/vdec/codec_h264.c
+new file mode 100644
+index 0000000..97621cd
+--- /dev/null
++++ b/drivers/media/platform/meson/vdec/codec_h264.c
+@@ -0,0 +1,376 @@
++// SPDX-License-Identifier: GPL-2.0+
++/*
++ * Copyright (C) 2018 Maxime Jourdan <maxi.jourdan@wanadoo.fr>
++ */
++
++#include <media/v4l2-mem2mem.h>
++#include <media/videobuf2-dma-contig.h>
++
++#include "codec_h264.h"
++#include "codec_helpers.h"
++#include "canvas.h"
++
++#define SIZE_EXT_FW (SZ_1K * 20)
++#define SIZE_WORKSPACE 0x1ee000
++#define SIZE_SEI (SZ_1K * 8)
++
++/* Offset added by the firmware which must be substracted
++ * from the workspace paddr
++ */
++#define DEF_BUF_START_ADDR 0x1000000
++
++/* DOS registers */
++#define ASSIST_MBOX1_CLR_REG 0x01d4
++#define ASSIST_MBOX1_MASK 0x01d8
++
++#define LMEM_DMA_CTRL 0x0d40
++
++#define PSCALE_CTRL 0x2444
++
++#define MDEC_PIC_DC_CTRL 0x2638
++#define ANC0_CANVAS_ADDR 0x2640
++#define MDEC_PIC_DC_THRESH 0x26e0
++
++#define AV_SCRATCH_0 0x2700
++#define AV_SCRATCH_1 0x2704
++#define AV_SCRATCH_2 0x2708
++#define AV_SCRATCH_3 0x270c
++#define AV_SCRATCH_4 0x2710
++#define AV_SCRATCH_5 0x2714
++#define AV_SCRATCH_6 0x2718
++#define AV_SCRATCH_7 0x271c
++#define AV_SCRATCH_8 0x2720
++#define AV_SCRATCH_9 0x2724
++#define AV_SCRATCH_D 0x2734
++#define AV_SCRATCH_F 0x273c
++#define AV_SCRATCH_G 0x2740
++#define AV_SCRATCH_H 0x2744
++#define AV_SCRATCH_I 0x2748
++#define AV_SCRATCH_J 0x274c
++ #define SEI_DATA_READY BIT(15)
++
++#define POWER_CTL_VLD 0x3020
++
++#define DCAC_DMA_CTRL 0x3848
++
++#define DOS_SW_RESET0 0xfc00
++
++struct codec_h264 {
++ /* H.264 decoder requires an extended firmware loaded in contiguous RAM */
++ void *ext_fw_vaddr;
++ dma_addr_t ext_fw_paddr;
++
++ /* Buffer for the H.264 Workspace */
++ void *workspace_vaddr;
++ dma_addr_t workspace_paddr;
++
++ /* Buffer for the H.264 references MV */
++ void *ref_vaddr;
++ dma_addr_t ref_paddr;
++ u32 ref_size;
++
++ /* Buffer for parsed SEI data ; > M8 ? */
++ void *sei_vaddr;
++ dma_addr_t sei_paddr;
++
++ /* Housekeeping thread for recycling buffers into the hardware */
++ struct task_struct *buffers_thread;
++};
++
++static void codec_h264_recycle_first(struct vdec_session *sess)
++{
++ struct vdec_core *core = sess->core;
++ struct vdec_buffer *tmp;
++
++ tmp = list_first_entry(&sess->bufs_recycle, struct vdec_buffer, list);
++
++ /* Tell the decoder he can recycle this buffer.
++ * AV_SCRATCH_8 serves the same purpose.
++ */
++ if (!readl_relaxed(core->dos_base + AV_SCRATCH_7))
++ writel_relaxed(tmp->index + 1, core->dos_base + AV_SCRATCH_7);
++ else
++ writel_relaxed(tmp->index + 1, core->dos_base + AV_SCRATCH_8);
++
++ dev_dbg(core->dev, "Buffer %d recycled\n", tmp->index);
++
++ list_del(&tmp->list);
++ kfree(tmp);
++}
++
++static int codec_h264_buffers_thread(void *data)
++{
++ struct vdec_session *sess = data;
++ struct vdec_core *core = sess->core;
++
++ while (!kthread_should_stop()) {
++ mutex_lock(&sess->bufs_recycle_lock);
++ while (!list_empty(&sess->bufs_recycle) &&
++ (!readl_relaxed(core->dos_base + AV_SCRATCH_7) ||
++ !readl_relaxed(core->dos_base + AV_SCRATCH_8)))
++ {
++ codec_h264_recycle_first(sess);
++ }
++ mutex_unlock(&sess->bufs_recycle_lock);
++
++ usleep_range(5000, 10000);
++ }
++
++ return 0;
++}
++
++static int codec_h264_start(struct vdec_session *sess) {
++ u32 workspace_offset;
++ struct vdec_core *core = sess->core;
++ struct codec_h264 *h264 = sess->priv;
++
++ /* Allocate some memory for the H.264 decoder's state */
++ h264->workspace_vaddr =
++ dma_alloc_coherent(core->dev, SIZE_WORKSPACE, &h264->workspace_paddr, GFP_KERNEL);
++ if (!h264->workspace_vaddr) {
++ dev_err(core->dev, "Failed to request H.264 Workspace\n");
++ return -ENOMEM;
++ }
++
++ /* Allocate some memory for the H.264 SEI dump */
++ h264->sei_vaddr =
++ dma_alloc_coherent(core->dev, SIZE_SEI, &h264->sei_paddr, GFP_KERNEL);
++ if (!h264->sei_vaddr) {
++ dev_err(core->dev, "Failed to request H.264 SEI\n");
++ return -ENOMEM;
++ }
++
++ while (readl_relaxed(core->dos_base + DCAC_DMA_CTRL) & 0x8000) { }
++ while (readl_relaxed(core->dos_base + LMEM_DMA_CTRL) & 0x8000) { }
++
++ writel_relaxed((1<<7) | (1<<6) | (1<<4), core->dos_base + DOS_SW_RESET0);
++ writel_relaxed(0, core->dos_base + DOS_SW_RESET0);
++ readl_relaxed(core->dos_base + DOS_SW_RESET0);
++
++ writel_relaxed((1<<7) | (1<<6) | (1<<4), core->dos_base + DOS_SW_RESET0);
++ writel_relaxed(0, core->dos_base + DOS_SW_RESET0);
++ writel_relaxed((1<<9) | (1<<8), core->dos_base + DOS_SW_RESET0);
++ writel_relaxed(0, core->dos_base + DOS_SW_RESET0);
++ readl_relaxed(core->dos_base + DOS_SW_RESET0);
++
++ writel_relaxed(readl_relaxed(core->dos_base + POWER_CTL_VLD) | (1 << 9) | (1 << 6), core->dos_base + POWER_CTL_VLD);
++
++ writel_relaxed(0, core->dos_base + PSCALE_CTRL);
++ writel_relaxed(0, core->dos_base + AV_SCRATCH_0);
++
++ workspace_offset = h264->workspace_paddr - DEF_BUF_START_ADDR;
++ writel_relaxed(workspace_offset, core->dos_base + AV_SCRATCH_1);
++ writel_relaxed(h264->ext_fw_paddr, core->dos_base + AV_SCRATCH_G);
++ writel_relaxed(h264->sei_paddr - workspace_offset, core->dos_base + AV_SCRATCH_I);
++
++ writel_relaxed(0, core->dos_base + AV_SCRATCH_7);
++ writel_relaxed(0, core->dos_base + AV_SCRATCH_8);
++ writel_relaxed(0, core->dos_base + AV_SCRATCH_9);
++
++ /* Enable "error correction", don't know what it means */
++ writel_relaxed((readl_relaxed(core->dos_base + AV_SCRATCH_F) & 0xffffffc3) | (1 << 4), core->dos_base + AV_SCRATCH_F);
++
++ /* Enable IRQ */
++ writel_relaxed(1, core->dos_base + ASSIST_MBOX1_CLR_REG);
++ writel_relaxed(1, core->dos_base + ASSIST_MBOX1_MASK);
++
++ /* Enable 2-plane output */
++ writel_relaxed(readl_relaxed(core->dos_base + MDEC_PIC_DC_CTRL) | (1 << 17), core->dos_base + MDEC_PIC_DC_CTRL);
++
++ writel_relaxed(0x404038aa, core->dos_base + MDEC_PIC_DC_THRESH);
++
++ writel_relaxed((1<<12)|(1<<11), core->dos_base + DOS_SW_RESET0);
++ writel_relaxed(0, core->dos_base + DOS_SW_RESET0);
++
++ readl_relaxed(core->dos_base + DOS_SW_RESET0);
++
++ h264->buffers_thread = kthread_run(codec_h264_buffers_thread, sess, "buffers_done");
++
++ return 0;
++}
++
++static int codec_h264_stop(struct vdec_session *sess)
++{
++ struct codec_h264 *h264 = sess->priv;
++ struct vdec_core *core = sess->core;
++
++ kthread_stop(h264->buffers_thread);
++
++ if (h264->ext_fw_vaddr)
++ dma_free_coherent(core->dev, SIZE_EXT_FW, h264->ext_fw_vaddr, h264->ext_fw_paddr);
++
++ if (h264->workspace_vaddr)
++ dma_free_coherent(core->dev, SIZE_WORKSPACE, h264->workspace_vaddr, h264->workspace_paddr);
++
++ if (h264->ref_vaddr)
++ dma_free_coherent(core->dev, h264->ref_size, h264->ref_vaddr, h264->ref_paddr);
++
++ if (h264->sei_vaddr)
++ dma_free_coherent(core->dev, SIZE_SEI, h264->sei_vaddr, h264->sei_paddr);
++
++ kfree(h264);
++ sess->priv = 0;
++
++ return 0;
++}
++
++static int codec_h264_load_extended_firmware(struct vdec_session *sess, const u8 *data, u32 len)
++{
++ struct codec_h264 *h264;
++ struct vdec_core *core = sess->core;
++
++ h264 = kzalloc(sizeof(*h264), GFP_KERNEL);
++ if (!h264)
++ return -ENOMEM;
++
++ sess->priv = h264;
++
++ if (len != SIZE_EXT_FW)
++ return -EINVAL;
++
++ h264->ext_fw_vaddr = dma_alloc_coherent(core->dev, SIZE_EXT_FW, &h264->ext_fw_paddr, GFP_KERNEL);
++ if (!h264->ext_fw_vaddr) {
++ dev_err(core->dev, "Couldn't allocate memory for H.264 extended firmware\n");
++ return -ENOMEM;
++ }
++
++ memcpy(h264->ext_fw_vaddr, data, SIZE_EXT_FW);
++
++ return 0;
++}
++
++/* Configure the H.264 decoder when the esparser finished parsing
++ * the first buffer.
++ */
++static void codec_h264_set_param(struct vdec_session *sess) {
++ u32 max_reference_size;
++ u32 parsed_info, mb_width, mb_height, mb_total;
++ u32 mb_mv_byte;
++ u32 actual_dpb_size = v4l2_m2m_num_dst_bufs_ready(sess->m2m_ctx);
++ u32 max_dpb_size = 4;
++ struct vdec_core *core = sess->core;
++ struct codec_h264 *h264 = sess->priv;
++
++ writel_relaxed(0, core->dos_base + AV_SCRATCH_7);
++ writel_relaxed(0, core->dos_base + AV_SCRATCH_8);
++ writel_relaxed(0, core->dos_base + AV_SCRATCH_9);
++
++ parsed_info = readl_relaxed(core->dos_base + AV_SCRATCH_1);
++
++ /* Total number of 16x16 macroblocks */
++ mb_total = (parsed_info >> 8) & 0xffff;
++
++ /* Size of Motion Vector per macroblock ? */
++ mb_mv_byte = (parsed_info & 0x80000000) ? 24 : 96;
++
++ /* Number of macroblocks per line */
++ mb_width = parsed_info & 0xff;
++
++ /* Number of macroblock lines */
++ mb_height = mb_total / mb_width;
++
++ max_reference_size = (parsed_info >> 24) & 0x7f;
++
++ /* Align to a multiple of 4 macroblocks */
++ mb_width = (mb_width + 3) & 0xfffffffc;
++ mb_height = (mb_height + 3) & 0xfffffffc;
++ mb_total = mb_width * mb_height;
++
++ codec_helper_set_canvases(sess, core->dos_base + ANC0_CANVAS_ADDR);
++
++ if (max_reference_size >= max_dpb_size)
++ max_dpb_size = max_reference_size;
++
++ max_reference_size++;
++
++ h264->ref_size = mb_total * mb_mv_byte * max_reference_size;
++ h264->ref_vaddr = dma_alloc_coherent(core->dev, h264->ref_size, &h264->ref_paddr, GFP_ATOMIC);
++
++ /* Address to store the references' MVs ? */
++ writel_relaxed(h264->ref_paddr, core->dos_base + AV_SCRATCH_1);
++
++ /* End of ref MV */
++ writel_relaxed(h264->ref_paddr + h264->ref_size, core->dos_base + AV_SCRATCH_4);
++
++ writel_relaxed((max_reference_size << 24) | (actual_dpb_size << 16) | (max_dpb_size << 8), core->dos_base + AV_SCRATCH_0);
++}
++
++static void codec_h264_frames_ready(struct vdec_session *sess, u32 status)
++{
++ struct vdec_core *core = sess->core;
++ int error_count;
++ int error;
++ int num_frames;
++ int frame_status;
++ unsigned int buffer_index;
++ int i;
++
++ error_count = readl_relaxed(core->dos_base + AV_SCRATCH_D);
++ num_frames = (status >> 8) & 0xff;
++ if (error_count) {
++ dev_warn(core->dev,
++ "decoder error(s) happened, count %d\n", error_count);
++ writel_relaxed(0, core->dos_base + AV_SCRATCH_D);
++ }
++
++ for (i = 0; i < num_frames; i++) {
++ frame_status = readl_relaxed(core->dos_base + AV_SCRATCH_1 + i*4);
++ buffer_index = frame_status & 0x1f;
++ error = frame_status & 0x200;
++
++ /* A buffer decode error means it was decoded,
++ * but part of the picture will have artifacts.
++ * Typical reason is a temporarily corrupted bitstream
++ */
++ if (error)
++ dev_dbg(core->dev, "Buffer %d decode error: %08X\n",
++ buffer_index, error);
++
++ vdec_dst_buf_done_idx(sess, buffer_index);
++ }
++}
++
++static irqreturn_t codec_h264_threaded_isr(struct vdec_session *sess)
++{
++ u32 status;
++ u8 cmd;
++ struct vdec_core *core = sess->core;
++
++ status = readl_relaxed(core->dos_base + AV_SCRATCH_0);
++ cmd = status & 0xff;
++
++ if (cmd == 1) {
++ codec_h264_set_param(sess);
++ } else if (cmd == 2) {
++ codec_h264_frames_ready(sess, status);
++ writel_relaxed(0, core->dos_base + AV_SCRATCH_0);
++ } else if (cmd != 0) {
++ dev_warn(core->dev, "Unexpected cmd: %08X\n", cmd);
++ writel_relaxed(0, core->dos_base + AV_SCRATCH_0);
++ }
++
++ /* Decoder has some SEI data for us ; ignore */
++ if (readl_relaxed(core->dos_base + AV_SCRATCH_J) & SEI_DATA_READY)
++ writel_relaxed(0, core->dos_base + AV_SCRATCH_J);
++
++ return IRQ_HANDLED;
++}
++
++static irqreturn_t codec_h264_isr(struct vdec_session *sess)
++{
++ struct vdec_core *core = sess->core;
++
++ writel_relaxed(1, core->dos_base + ASSIST_MBOX1_CLR_REG);
++
++ return IRQ_WAKE_THREAD;
++}
++
++struct vdec_codec_ops codec_h264_ops = {
++ .start = codec_h264_start,
++ .stop = codec_h264_stop,
++ .load_extended_firmware = codec_h264_load_extended_firmware,
++ .isr = codec_h264_isr,
++ .threaded_isr = codec_h264_threaded_isr,
++ .notify_dst_buffer = vdec_queue_recycle,
++};
++
+diff --git a/drivers/media/platform/meson/vdec/codec_h264.h b/drivers/media/platform/meson/vdec/codec_h264.h
+new file mode 100644
+index 0000000..1a15913
+--- /dev/null
++++ b/drivers/media/platform/meson/vdec/codec_h264.h
+@@ -0,0 +1,13 @@
++/* SPDX-License-Identifier: GPL-2.0+ */
++/*
++ * Copyright (C) 2018 Maxime Jourdan <maxi.jourdan@wanadoo.fr>
++ */
++
++#ifndef __MESON_VDEC_CODEC_H264_H_
++#define __MESON_VDEC_CODEC_H264_H_
++
++#include "vdec.h"
++
++extern struct vdec_codec_ops codec_h264_ops;
++
++#endif
+\ No newline at end of file
+diff --git a/drivers/media/platform/meson/vdec/codec_helpers.c b/drivers/media/platform/meson/vdec/codec_helpers.c
+new file mode 100644
+index 0000000..9906433
+--- /dev/null
++++ b/drivers/media/platform/meson/vdec/codec_helpers.c
+@@ -0,0 +1,45 @@
++// SPDX-License-Identifier: GPL-2.0+
++/*
++ * Copyright (C) 2018 Maxime Jourdan <maxi.jourdan@wanadoo.fr>
++ */
++
++#include <media/v4l2-mem2mem.h>
++#include <media/videobuf2-dma-contig.h>
++
++#include "codec_helpers.h"
++#include "canvas.h"
++
++void codec_helper_set_canvases(struct vdec_session *sess, void *reg_base)
++{
++ struct vdec_core *core = sess->core;
++ u32 width = ALIGN(sess->width, 64);
++ u32 height = ALIGN(sess->height, 64);
++ struct v4l2_m2m_buffer *buf;
++
++ /* Setup NV12 canvases for Decoded Picture Buffer (dpb)
++ * Map them to the user buffers' planes
++ */
++ v4l2_m2m_for_each_dst_buf(sess->m2m_ctx, buf) {
++ u32 buf_idx = buf->vb.vb2_buf.index;
++ u32 cnv_y_idx = 128 + buf_idx * 2;
++ u32 cnv_uv_idx = cnv_y_idx + 1;
++ dma_addr_t buf_y_paddr =
++ vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
++ dma_addr_t buf_uv_paddr =
++ vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 1);
++
++ /* Y plane */
++ vdec_canvas_setup(core->dmc_base, cnv_y_idx, buf_y_paddr,
++ width, height, MESON_CANVAS_WRAP_NONE,
++ MESON_CANVAS_BLKMODE_LINEAR);
++
++ /* U/V plane */
++ vdec_canvas_setup(core->dmc_base, cnv_uv_idx, buf_uv_paddr,
++ width, height / 2, MESON_CANVAS_WRAP_NONE,
++ MESON_CANVAS_BLKMODE_LINEAR);
++
++ writel_relaxed(((cnv_uv_idx) << 16) |
++ ((cnv_uv_idx) << 8) |
++ (cnv_y_idx), reg_base + buf_idx * 4);
++ }
++}
+\ No newline at end of file
+diff --git a/drivers/media/platform/meson/vdec/codec_helpers.h b/drivers/media/platform/meson/vdec/codec_helpers.h
+new file mode 100644
+index 0000000..0a778ba
+--- /dev/null
++++ b/drivers/media/platform/meson/vdec/codec_helpers.h
+@@ -0,0 +1,8 @@
++#ifndef __MESON_VDEC_CODEC_HELPERS_H_
++#define __MESON_VDEC_CODEC_HELPERS_H_
++
++#include "vdec.h"
++
++void codec_helper_set_canvases(struct vdec_session *sess, void *reg_base);
++
++#endif
+\ No newline at end of file
+diff --git a/drivers/media/platform/meson/vdec/codec_hevc.c b/drivers/media/platform/meson/vdec/codec_hevc.c
+new file mode 100644
+index 0000000..cf3e80a
+--- /dev/null
++++ b/drivers/media/platform/meson/vdec/codec_hevc.c
+@@ -0,0 +1,1383 @@
++// SPDX-License-Identifier: GPL-2.0+
++/*
++ * Copyright (C) 2018 Maxime Jourdan <maxi.jourdan@wanadoo.fr>
++ * Copyright (C) 2015 Amlogic, Inc. All rights reserved.
++ */
++
++#include <media/v4l2-mem2mem.h>
++#include <media/videobuf2-dma-contig.h>
++
++#include "codec_hevc.h"
++#include "canvas.h"
++#include "hevc_regs.h"
++
++/* DOS registers */
++#define ASSIST_MBOX1_CLR_REG 0x01d4
++#define ASSIST_MBOX1_MASK 0x01d8
++
++#define DOS_SW_RESET3 0xfcd0
++
++/* HEVC reg mapping */
++#define HEVC_DEC_STATUS_REG HEVC_ASSIST_SCRATCH_0
++ #define HEVC_ACTION_DONE 0xff
++#define HEVC_RPM_BUFFER HEVC_ASSIST_SCRATCH_1
++#define HEVC_DECODE_INFO HEVC_ASSIST_SCRATCH_1
++#define HEVC_SHORT_TERM_RPS HEVC_ASSIST_SCRATCH_2
++#define HEVC_VPS_BUFFER HEVC_ASSIST_SCRATCH_3
++#define HEVC_SPS_BUFFER HEVC_ASSIST_SCRATCH_4
++#define HEVC_PPS_BUFFER HEVC_ASSIST_SCRATCH_5
++#define HEVC_SAO_UP HEVC_ASSIST_SCRATCH_6
++#define HEVC_STREAM_SWAP_BUFFER HEVC_ASSIST_SCRATCH_7
++#define H265_MMU_MAP_BUFFER HEVC_ASSIST_SCRATCH_7
++#define HEVC_STREAM_SWAP_BUFFER2 HEVC_ASSIST_SCRATCH_8
++#define HEVC_sao_mem_unit HEVC_ASSIST_SCRATCH_9
++#define HEVC_SAO_ABV HEVC_ASSIST_SCRATCH_A
++#define HEVC_sao_vb_size HEVC_ASSIST_SCRATCH_B
++#define HEVC_SAO_VB HEVC_ASSIST_SCRATCH_C
++#define HEVC_SCALELUT HEVC_ASSIST_SCRATCH_D
++#define HEVC_WAIT_FLAG HEVC_ASSIST_SCRATCH_E
++#define RPM_CMD_REG HEVC_ASSIST_SCRATCH_F
++#define LMEM_DUMP_ADR HEVC_ASSIST_SCRATCH_F
++#define DEBUG_REG1 HEVC_ASSIST_SCRATCH_G
++#define HEVC_DECODE_MODE2 HEVC_ASSIST_SCRATCH_H
++#define NAL_SEARCH_CTL HEVC_ASSIST_SCRATCH_I
++#define HEVC_DECODE_MODE HEVC_ASSIST_SCRATCH_J
++ #define DECODE_MODE_SINGLE 0
++#define DECODE_STOP_POS HEVC_ASSIST_SCRATCH_K
++#define HEVC_AUX_ADR HEVC_ASSIST_SCRATCH_L
++#define HEVC_AUX_DATA_SIZE HEVC_ASSIST_SCRATCH_M
++#define HEVC_DECODE_SIZE HEVC_ASSIST_SCRATCH_N
++
++#define HEVCD_MPP_ANC2AXI_TBL_DATA (0x3464 * 4)
++
++#define HEVC_CM_BODY_START_ADDR (0x3626 * 4)
++#define HEVC_CM_BODY_LENGTH (0x3627 * 4)
++#define HEVC_CM_HEADER_LENGTH (0x3629 * 4)
++#define HEVC_CM_HEADER_OFFSET (0x362b * 4)
++
++#define AMRISC_MAIN_REQ 0x04
++
++/* HEVC Constants */
++#define MAX_REF_PIC_NUM 24
++#define MAX_REF_ACTIVE 16
++#define MPRED_MV_BUF_SIZE 0x120000
++#define MAX_TILE_COL_NUM 10
++#define MAX_TILE_ROW_NUM 20
++#define MAX_SLICE_NUM 800
++#define INVALID_POC 0x80000000
++
++/* HEVC Workspace layout */
++#define IPP_OFFSET 0x00
++#define SAO_ABV_OFFSET (IPP_OFFSET + 0x4000)
++#define SAO_VB_OFFSET (SAO_ABV_OFFSET + 0x30000)
++#define SH_TM_RPS_OFFSET (SAO_VB_OFFSET + 0x30000)
++#define VPS_OFFSET (SH_TM_RPS_OFFSET + 0x800)
++#define SPS_OFFSET (VPS_OFFSET + 0x800)
++#define PPS_OFFSET (SPS_OFFSET + 0x800)
++#define SAO_UP_OFFSET (PPS_OFFSET + 0x2000)
++#define SWAP_BUF_OFFSET (SAO_UP_OFFSET + 0x800)
++#define SWAP_BUF2_OFFSET (SWAP_BUF_OFFSET + 0x800)
++#define SCALELUT_OFFSET (SWAP_BUF2_OFFSET + 0x800)
++#define DBLK_PARA_OFFSET (SCALELUT_OFFSET + 0x8000)
++#define DBLK_DATA_OFFSET (DBLK_PARA_OFFSET + 0x20000)
++#define MMU_VBH_OFFSET (DBLK_DATA_OFFSET + 0x40000)
++#define MPRED_ABV_OFFSET (MMU_VBH_OFFSET + 0x5000)
++#define MPRED_MV_OFFSET (MPRED_ABV_OFFSET + 0x8000)
++#define RPM_OFFSET (MPRED_MV_OFFSET + MPRED_MV_BUF_SIZE * MAX_REF_PIC_NUM)
++#define LMEM_OFFSET (RPM_OFFSET + 0x100)
++
++/* ISR decode status */
++#define HEVC_DEC_IDLE 0x0
++#define HEVC_NAL_UNIT_VPS 0x1
++#define HEVC_NAL_UNIT_SPS 0x2
++#define HEVC_NAL_UNIT_PPS 0x3
++#define HEVC_NAL_UNIT_CODED_SLICE_SEGMENT 0x4
++#define HEVC_CODED_SLICE_SEGMENT_DAT 0x5
++#define HEVC_SLICE_DECODING 0x6
++#define HEVC_NAL_UNIT_SEI 0x7
++#define HEVC_SLICE_SEGMENT_DONE 0x8
++#define HEVC_NAL_SEARCH_DONE 0x9
++#define HEVC_DECPIC_DATA_DONE 0xa
++#define HEVC_DECPIC_DATA_ERROR 0xb
++#define HEVC_SEI_DAT 0xc
++#define HEVC_SEI_DAT_DONE 0xd
++
++/* RPM misc_flag0 */
++#define PCM_LOOP_FILTER_DISABLED_FLAG_BIT 0
++#define PCM_ENABLE_FLAG_BIT 1
++#define LOOP_FILER_ACROSS_TILES_ENABLED_FLAG_BIT 2
++#define PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG_BIT 3
++#define DEBLOCKING_FILTER_OVERRIDE_ENABLED_FLAG_BIT 4
++#define PPS_DEBLOCKING_FILTER_DISABLED_FLAG_BIT 5
++#define DEBLOCKING_FILTER_OVERRIDE_FLAG_BIT 6
++#define SLICE_DEBLOCKING_FILTER_DISABLED_FLAG_BIT 7
++#define SLICE_SAO_LUMA_FLAG_BIT 8
++#define SLICE_SAO_CHROMA_FLAG_BIT 9
++#define SLICE_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG_BIT 10
++
++/* Buffer sizes */
++#define SIZE_WORKSPACE ALIGN(LMEM_OFFSET + 0xA00, 64 * SZ_1K)
++#define SIZE_AUX (SZ_1K * 16)
++#define SIZE_FRAME_MMU (0x1200 * 4)
++#define RPM_SIZE 0x80
++#define RPS_USED_BIT 14
++
++#define PARSER_CMD_SKIP_CFG_0 0x0000090b
++#define PARSER_CMD_SKIP_CFG_1 0x1b14140f
++#define PARSER_CMD_SKIP_CFG_2 0x001b1910
++static const uint16_t parser_cmd[] = {
++ 0x0401, 0x8401, 0x0800, 0x0402,
++ 0x9002, 0x1423, 0x8CC3, 0x1423,
++ 0x8804, 0x9825, 0x0800, 0x04FE,
++ 0x8406, 0x8411, 0x1800, 0x8408,
++ 0x8409, 0x8C2A, 0x9C2B, 0x1C00,
++ 0x840F, 0x8407, 0x8000, 0x8408,
++ 0x2000, 0xA800, 0x8410, 0x04DE,
++ 0x840C, 0x840D, 0xAC00, 0xA000,
++ 0x08C0, 0x08E0, 0xA40E, 0xFC00,
++ 0x7C00
++};
++
++/* Data received from the HW in this form, do not rearrange */
++union rpm_param {
++ struct {
++ uint16_t data[RPM_SIZE];
++ } l;
++ struct {
++ uint16_t CUR_RPS[MAX_REF_ACTIVE];
++ uint16_t num_ref_idx_l0_active;
++ uint16_t num_ref_idx_l1_active;
++ uint16_t slice_type;
++ uint16_t slice_temporal_mvp_enable_flag;
++ uint16_t dependent_slice_segment_flag;
++ uint16_t slice_segment_address;
++ uint16_t num_title_rows_minus1;
++ uint16_t pic_width_in_luma_samples;
++ uint16_t pic_height_in_luma_samples;
++ uint16_t log2_min_coding_block_size_minus3;
++ uint16_t log2_diff_max_min_coding_block_size;
++ uint16_t log2_max_pic_order_cnt_lsb_minus4;
++ uint16_t POClsb;
++ uint16_t collocated_from_l0_flag;
++ uint16_t collocated_ref_idx;
++ uint16_t log2_parallel_merge_level;
++ uint16_t five_minus_max_num_merge_cand;
++ uint16_t sps_num_reorder_pics_0;
++ uint16_t modification_flag;
++ uint16_t tiles_flags;
++ uint16_t num_tile_columns_minus1;
++ uint16_t num_tile_rows_minus1;
++ uint16_t tile_width[8];
++ uint16_t tile_height[8];
++ uint16_t misc_flag0;
++ uint16_t pps_beta_offset_div2;
++ uint16_t pps_tc_offset_div2;
++ uint16_t slice_beta_offset_div2;
++ uint16_t slice_tc_offset_div2;
++ uint16_t pps_cb_qp_offset;
++ uint16_t pps_cr_qp_offset;
++ uint16_t first_slice_segment_in_pic_flag;
++ uint16_t m_temporalId;
++ uint16_t m_nalUnitType;
++ uint16_t vui_num_units_in_tick_hi;
++ uint16_t vui_num_units_in_tick_lo;
++ uint16_t vui_time_scale_hi;
++ uint16_t vui_time_scale_lo;
++ uint16_t bit_depth;
++ uint16_t profile_etc;
++ uint16_t sei_frame_field_info;
++ uint16_t video_signal_type;
++ uint16_t modification_list[0x20];
++ uint16_t conformance_window_flag;
++ uint16_t conf_win_left_offset;
++ uint16_t conf_win_right_offset;
++ uint16_t conf_win_top_offset;
++ uint16_t conf_win_bottom_offset;
++ uint16_t chroma_format_idc;
++ uint16_t color_description;
++ uint16_t aspect_ratio_idc;
++ uint16_t sar_width;
++ uint16_t sar_height;
++ } p;
++};
++
++enum nal_unit_type {
++ NAL_UNIT_CODED_SLICE_BLA = 16,
++ NAL_UNIT_CODED_SLICE_BLANT = 17,
++ NAL_UNIT_CODED_SLICE_BLA_N_LP = 18,
++ NAL_UNIT_CODED_SLICE_IDR = 19,
++ NAL_UNIT_CODED_SLICE_IDR_N_LP = 20,
++};
++
++enum slice_type {
++ B_SLICE = 0,
++ P_SLICE = 1,
++ I_SLICE = 2,
++};
++
++/* Refers to a frame being decoded */
++struct hevc_frame {
++ struct list_head list;
++ struct vb2_v4l2_buffer *vbuf;
++ u32 poc;
++
++ int referenced;
++ u32 num_reorder_pic;
++
++ u32 cur_slice_idx;
++ u32 cur_slice_type;
++
++ /* 2 lists (L0/L1) ; 800 slices ; 16 refs */
++ u32 ref_poc_list[2][MAX_SLICE_NUM][16];
++ u32 ref_num[2];
++};
++
++struct hevc_tile {
++ int width;
++ int height;
++ int start_cu_x;
++ int start_cu_y;
++
++ dma_addr_t sao_vb_start_addr;
++ dma_addr_t sao_abv_start_addr;
++};
++
++struct codec_hevc {
++ /* Current decoding status provided by the ISR */
++ u32 dec_status;
++
++ /* Buffer for the HEVC Workspace */
++ void *workspace_vaddr;
++ dma_addr_t workspace_paddr;
++
++ /* AUX buffer */
++ void *aux_vaddr;
++ dma_addr_t aux_paddr;
++
++ /* Frame MMU buffer (>= GXL) ; unused for now */
++ void *frame_mmu_vaddr;
++ dma_addr_t frame_mmu_paddr;
++
++ /* Contains many information parsed from the bitstream */
++ union rpm_param rpm_param;
++
++ /* Information computed from the RPM */
++ u32 lcu_size; // Largest Coding Unit
++ u32 lcu_x_num;
++ u32 lcu_y_num;
++ u32 lcu_total;
++
++ /* Current Frame being handled */
++ struct hevc_frame *cur_frame;
++ u32 curr_poc;
++ /* Collocated Reference Picture */
++ struct hevc_frame *col_frame;
++ u32 col_poc;
++
++ /* All ref frames used by the HW at a given time */
++ struct list_head ref_frames_list;
++ u32 frames_num;
++
++ /* Resolution reported by the hardware */
++ u32 width;
++ u32 height;
++
++ u32 iPrevTid0POC;
++ u32 slice_segment_addr;
++ u32 slice_addr;
++ u32 ldc_flag;
++
++ /* Tiles */
++ u32 num_tile_col;
++ u32 num_tile_row;
++ struct hevc_tile m_tile[MAX_TILE_ROW_NUM][MAX_TILE_COL_NUM];
++ u32 tile_start_lcu_x;
++ u32 tile_start_lcu_y;
++ u32 tile_width_lcu;
++ u32 tile_height_lcu;
++};
++
++static u32 codec_hevc_num_pending_bufs(struct vdec_session *sess)
++{
++ struct codec_hevc *hevc;
++ u32 ret;
++
++ mutex_lock(&sess->codec_lock);
++ hevc = sess->priv;
++ if (!hevc) {
++ mutex_unlock(&sess->codec_lock);
++ return 0;
++ }
++
++ ret = hevc->frames_num;
++ mutex_unlock(&sess->codec_lock);
++
++ return ret;
++}
++
++/* Update the L0 and L1 reference lists for a given frame */
++static void codec_hevc_update_frame_refs(struct vdec_session *sess, struct hevc_frame *frame)
++{
++ struct codec_hevc *hevc = sess->priv;
++ union rpm_param *params = &hevc->rpm_param;
++ int i;
++ int num_neg = 0;
++ int num_pos = 0;
++ int total_num;
++ int num_ref_idx_l0_active =
++ (params->p.num_ref_idx_l0_active > MAX_REF_ACTIVE) ?
++ MAX_REF_ACTIVE : params->p.num_ref_idx_l0_active;
++ int num_ref_idx_l1_active =
++ (params->p.num_ref_idx_l1_active > MAX_REF_ACTIVE) ?
++ MAX_REF_ACTIVE : params->p.num_ref_idx_l1_active;
++ int ref_picset0[16] = { 0 };
++ int ref_picset1[16] = { 0 };
++
++ for (i = 0; i < 16; i++) {
++ frame->ref_poc_list[0][frame->cur_slice_idx][i] = 0;
++ frame->ref_poc_list[1][frame->cur_slice_idx][i] = 0;
++ }
++
++ for (i = 0; i < 16; i++) {
++ u16 cur_rps = params->p.CUR_RPS[i];
++ int delt = cur_rps & ((1 << (RPS_USED_BIT - 1)) - 1);
++
++ if (cur_rps & 0x8000)
++ break;
++
++ if (!((cur_rps >> RPS_USED_BIT) & 1))
++ continue;
++
++ if ((cur_rps >> (RPS_USED_BIT - 1)) & 1) {
++ ref_picset0[num_neg] =
++ frame->poc - ((1 << (RPS_USED_BIT - 1)) - delt);
++ num_neg++;
++ } else {
++ ref_picset1[num_pos] = frame->poc + delt;
++ num_pos++;
++ }
++ }
++
++ total_num = num_neg + num_pos;
++
++ if (total_num <= 0)
++ goto end;
++
++ for (i = 0; i < num_ref_idx_l0_active; i++) {
++ int cIdx;
++ if (params->p.modification_flag & 0x1)
++ cIdx = params->p.modification_list[i];
++ else
++ cIdx = i % total_num;
++
++ frame->ref_poc_list[0][frame->cur_slice_idx][i] =
++ cIdx >= num_neg ? ref_picset1[cIdx - num_neg] :
++ ref_picset0[cIdx];
++ }
++
++ if (params->p.slice_type != B_SLICE)
++ goto end;
++
++ if (params->p.modification_flag & 0x2) {
++ for (i = 0; i < num_ref_idx_l1_active; i++) {
++ int cIdx;
++ if (params->p.modification_flag & 0x1)
++ cIdx =
++ params->p.modification_list[num_ref_idx_l0_active + i];
++ else
++ cIdx = params->p.modification_list[i];
++
++ frame->ref_poc_list[1][frame->cur_slice_idx][i] =
++ (cIdx >= num_pos) ? ref_picset0[cIdx - num_pos]
++ : ref_picset1[cIdx];
++ }
++ } else {
++ for (i = 0; i < num_ref_idx_l1_active; i++) {
++ int cIdx = i % total_num;
++ frame->ref_poc_list[1][frame->cur_slice_idx][i] =
++ cIdx >= num_pos ? ref_picset0[cIdx - num_pos] :
++ ref_picset1[cIdx];
++ }
++ }
++
++end:
++ frame->ref_num[0] = num_ref_idx_l0_active;
++ frame->ref_num[1] = num_ref_idx_l1_active;
++
++ dev_dbg(sess->core->dev,
++ "Frame %u; slice %u; slice_type %u; num_l0 %u; num_l1 %u\n",
++ frame->poc, frame->cur_slice_idx, params->p.slice_type,
++ frame->ref_num[0], frame->ref_num[1]);
++}
++
++static void codec_hevc_update_ldc_flag(struct codec_hevc *hevc)
++{
++ struct hevc_frame *frame = hevc->cur_frame;
++ u32 slice_type = frame->cur_slice_type;
++ int i;
++
++ hevc->ldc_flag = 0;
++
++ if (slice_type == I_SLICE)
++ return;
++
++ hevc->ldc_flag = 1;
++ for (i = 0; (i < frame->ref_num[0]) && hevc->ldc_flag; i++) {
++ if (frame->ref_poc_list[0][frame->cur_slice_idx][i] > frame->poc) {
++ hevc->ldc_flag = 0;
++ break;
++ }
++ }
++
++ if (slice_type == P_SLICE)
++ return;
++
++ for (i = 0; (i < frame->ref_num[1]) && hevc->ldc_flag; i++) {
++ if (frame->ref_poc_list[1][frame->cur_slice_idx][i] > frame->poc) {
++ hevc->ldc_flag = 0;
++ break;
++ }
++ }
++}
++
++/* Tag "old" frames that are no longer referenced */
++static void codec_hevc_update_referenced(struct codec_hevc *hevc)
++{
++ union rpm_param *param = &hevc->rpm_param;
++ struct hevc_frame *frame;
++ int i;
++ u32 curr_poc = hevc->curr_poc;
++
++ list_for_each_entry(frame, &hevc->ref_frames_list, list) {
++ int is_referenced = 0;
++ u32 poc_tmp;
++
++ if (!frame->referenced)
++ continue;
++
++ for (i = 0; i < MAX_REF_ACTIVE; i++) {
++ int delt;
++ if (param->p.CUR_RPS[i] & 0x8000)
++ break;
++
++ delt = param->p.CUR_RPS[i] & ((1 << (RPS_USED_BIT - 1)) - 1);
++ if (param->p.CUR_RPS[i] & (1 << (RPS_USED_BIT - 1))) {
++ poc_tmp = curr_poc - ((1 << (RPS_USED_BIT - 1)) - delt);
++ } else
++ poc_tmp = curr_poc + delt;
++ if (poc_tmp == frame->poc) {
++ is_referenced = 1;
++ break;
++ }
++ }
++
++ frame->referenced = is_referenced;
++ }
++}
++
++static struct hevc_frame *codec_hevc_get_lowest_poc_frame(struct codec_hevc *hevc)
++{
++ struct hevc_frame *tmp, *ret = NULL;
++ u32 poc = INT_MAX;
++
++ list_for_each_entry(tmp, &hevc->ref_frames_list, list) {
++ if (tmp->poc < poc) {
++ ret = tmp;
++ poc = tmp->poc;
++ }
++ }
++
++ return ret;
++}
++
++/* Try to output as many frames as possible */
++static void codec_hevc_output_frames(struct vdec_session *sess)
++{
++ struct hevc_frame *tmp;
++ struct codec_hevc *hevc = sess->priv;
++
++ while ((tmp = codec_hevc_get_lowest_poc_frame(hevc))) {
++ if (tmp->referenced || tmp->num_reorder_pic >= hevc->frames_num)
++ break;
++
++ dev_dbg(sess->core->dev, "DONE frame poc %u; vbuf %u\n",
++ tmp->poc, tmp->vbuf->vb2_buf.index);
++ vdec_dst_buf_done(sess, tmp->vbuf);
++ list_del(&tmp->list);
++ kfree(tmp);
++ hevc->frames_num--;
++ }
++}
++
++/* Configure part of the IP responsible for frame buffer decompression */
++static void codec_hevc_setup_decode_head(struct vdec_session *sess)
++{
++ struct vdec_core *core = sess->core;
++
++ /* TODO */
++ writel_relaxed(0, core->dos_base + HEVCD_MPP_DECOMP_CTL1);
++ writel_relaxed(0, core->dos_base + HEVCD_MPP_DECOMP_CTL2);
++ writel_relaxed(0, core->dos_base + HEVC_CM_BODY_LENGTH);
++ writel_relaxed(0, core->dos_base + HEVC_CM_HEADER_OFFSET);
++ writel_relaxed(0, core->dos_base + HEVC_CM_HEADER_LENGTH);
++}
++
++static void codec_hevc_setup_buffers_gxbb(struct vdec_session *sess)
++{
++ struct vdec_core *core = sess->core;
++ struct v4l2_m2m_buffer *buf;
++ u32 buf_size = v4l2_m2m_num_dst_bufs_ready(sess->m2m_ctx);
++ dma_addr_t buf_y_paddr = 0;
++ dma_addr_t buf_uv_paddr = 0;
++ u32 idx = 0;
++ u32 val;
++ int i;
++
++ writel_relaxed(0, core->dos_base + HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR);
++
++ v4l2_m2m_for_each_dst_buf(sess->m2m_ctx, buf) {
++ idx = buf->vb.vb2_buf.index;
++ buf_y_paddr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
++ buf_uv_paddr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 1);
++
++ val = buf_y_paddr | ((idx * 2) << 8) | 1;
++ writel_relaxed(val, core->dos_base + HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR);
++ val = buf_uv_paddr | ((idx * 2 + 1) << 8) | 1;
++ writel_relaxed(val, core->dos_base + HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR);
++ }
++
++ val = buf_y_paddr | ((idx * 2) << 8) | 1;
++ /* Fill the remaining unused slots with the last buffer's Y addr */
++ for (i = buf_size; i < MAX_REF_PIC_NUM; ++i)
++ writel_relaxed(val, core->dos_base + HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR);
++
++ writel_relaxed(1, core->dos_base + HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR);
++ writel_relaxed(1, core->dos_base + HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR);
++ for (i = 0; i < 32; ++i)
++ writel_relaxed(0, core->dos_base + HEVCD_MPP_ANC_CANVAS_DATA_ADDR);
++}
++
++static void codec_hevc_setup_buffers_gxl(struct vdec_session *sess)
++{
++ struct vdec_core *core = sess->core;
++ struct v4l2_m2m_buffer *buf;
++ u32 buf_size = v4l2_m2m_num_dst_bufs_ready(sess->m2m_ctx);
++ dma_addr_t buf_y_paddr = 0;
++ dma_addr_t buf_uv_paddr = 0;
++ int i;
++
++ writel_relaxed((1 << 2) | (1 << 1), core->dos_base + HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR);
++
++ v4l2_m2m_for_each_dst_buf(sess->m2m_ctx, buf) {
++ buf_y_paddr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
++ buf_uv_paddr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 1);
++
++ writel_relaxed(buf_y_paddr >> 5, core->dos_base + HEVCD_MPP_ANC2AXI_TBL_DATA);
++ writel_relaxed(buf_uv_paddr >> 5, core->dos_base + HEVCD_MPP_ANC2AXI_TBL_DATA);
++ }
++
++ /* Fill the remaining unused slots with the last buffer's Y addr */
++ for (i = buf_size; i < MAX_REF_PIC_NUM; ++i) {
++ writel_relaxed(buf_y_paddr >> 5, core->dos_base + HEVCD_MPP_ANC2AXI_TBL_DATA);
++ //writel_relaxed(buf_uv_paddr >> 5, core->dos_base + HEVCD_MPP_ANC2AXI_TBL_DATA);
++ }
++
++ writel_relaxed(1, core->dos_base + HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR);
++ writel_relaxed(1, core->dos_base + HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR);
++ for (i = 0; i < 32; ++i)
++ writel_relaxed(0, core->dos_base + HEVCD_MPP_ANC_CANVAS_DATA_ADDR);
++}
++
++static int codec_hevc_setup_workspace(struct vdec_session *sess)
++{
++ struct vdec_core *core = sess->core;
++ struct codec_hevc *hevc = sess->priv;
++
++ /* Allocate some memory for the HEVC decoder's state */
++ hevc->workspace_vaddr = dma_alloc_coherent(core->dev, SIZE_WORKSPACE, &hevc->workspace_paddr, GFP_KERNEL);
++ if (!hevc->workspace_vaddr) {
++ dev_err(core->dev, "Failed to allocate HEVC Workspace\n");
++ return -ENOMEM;
++ }
++
++ writel_relaxed(hevc->workspace_paddr + IPP_OFFSET, core->dos_base + HEVCD_IPP_LINEBUFF_BASE);
++ writel_relaxed(hevc->workspace_paddr + RPM_OFFSET, core->dos_base + HEVC_RPM_BUFFER);
++ writel_relaxed(hevc->workspace_paddr + SH_TM_RPS_OFFSET, core->dos_base + HEVC_SHORT_TERM_RPS);
++ writel_relaxed(hevc->workspace_paddr + VPS_OFFSET, core->dos_base + HEVC_VPS_BUFFER);
++ writel_relaxed(hevc->workspace_paddr + SPS_OFFSET, core->dos_base + HEVC_SPS_BUFFER);
++ writel_relaxed(hevc->workspace_paddr + PPS_OFFSET, core->dos_base + HEVC_PPS_BUFFER);
++ writel_relaxed(hevc->workspace_paddr + SAO_UP_OFFSET, core->dos_base + HEVC_SAO_UP);
++
++ /* MMU */
++ //writel_relaxed(hevc->frame_mmu_paddr, core->dos_base + H265_MMU_MAP_BUFFER);
++ /* No MMU */
++ writel_relaxed(hevc->workspace_paddr + SWAP_BUF_OFFSET, core->dos_base + HEVC_STREAM_SWAP_BUFFER);
++
++ writel_relaxed(hevc->workspace_paddr + SWAP_BUF2_OFFSET, core->dos_base + HEVC_STREAM_SWAP_BUFFER2);
++ writel_relaxed(hevc->workspace_paddr + SCALELUT_OFFSET, core->dos_base + HEVC_SCALELUT);
++ writel_relaxed(hevc->workspace_paddr + DBLK_PARA_OFFSET, core->dos_base + HEVC_DBLK_CFG4);
++ writel_relaxed(hevc->workspace_paddr + DBLK_DATA_OFFSET, core->dos_base + HEVC_DBLK_CFG5);
++
++ return 0;
++}
++
++static int codec_hevc_start(struct vdec_session *sess)
++{
++ struct vdec_core *core = sess->core;
++ struct codec_hevc *hevc;
++ int ret;
++ int i;
++
++ hevc = kzalloc(sizeof(*hevc), GFP_KERNEL);
++ if (!hevc)
++ return -ENOMEM;
++
++ sess->priv = hevc;
++ INIT_LIST_HEAD(&hevc->ref_frames_list);
++ hevc->curr_poc = INVALID_POC;
++
++ ret = codec_hevc_setup_workspace(sess);
++ if (ret)
++ goto free_hevc;
++
++ writel_relaxed(0x5a5a55aa, core->dos_base + HEVC_PARSER_VERSION);
++ writel_relaxed((1 << 14), core->dos_base + DOS_SW_RESET3);
++ writel_relaxed(0, core->dos_base + HEVC_CABAC_CONTROL);
++ writel_relaxed(0, core->dos_base + HEVC_PARSER_CORE_CONTROL);
++ writel_relaxed(readl_relaxed(core->dos_base + HEVC_STREAM_CONTROL) | 1, core->dos_base + HEVC_STREAM_CONTROL);
++ writel_relaxed(0x00000100, core->dos_base + HEVC_SHIFT_STARTCODE);
++ writel_relaxed(0x00000300, core->dos_base + HEVC_SHIFT_EMULATECODE);
++ writel_relaxed((readl_relaxed(core->dos_base + HEVC_PARSER_INT_CONTROL) & 0x03ffffff) |
++ (3 << 29) | (2 << 26) | (1 << 24) | (1 << 22) | (1 << 7) | (1 << 4) | 1, core->dos_base + HEVC_PARSER_INT_CONTROL);
++ writel_relaxed(readl_relaxed(core->dos_base + HEVC_SHIFT_STATUS) | (1 << 1) | 1, core->dos_base + HEVC_SHIFT_STATUS);
++ writel_relaxed((3 << 6) | (2 << 4) | (2 << 1) | 1, core->dos_base + HEVC_SHIFT_CONTROL);
++ writel_relaxed(1, core->dos_base + HEVC_CABAC_CONTROL);
++ writel_relaxed(1, core->dos_base + HEVC_PARSER_CORE_CONTROL);
++ writel_relaxed(0, core->dos_base + HEVC_DEC_STATUS_REG);
++
++ writel_relaxed(0, core->dos_base + HEVC_IQIT_SCALELUT_WR_ADDR);
++ for (i = 0; i < 1024; ++i)
++ writel_relaxed(0, core->dos_base + HEVC_IQIT_SCALELUT_DATA);
++
++ writel_relaxed(0, core->dos_base + HEVC_DECODE_SIZE);
++
++ writel_relaxed((1 << 16), core->dos_base + HEVC_PARSER_CMD_WRITE);
++ for (i = 0; i < ARRAY_SIZE(parser_cmd); ++i)
++ writel_relaxed(parser_cmd[i], core->dos_base + HEVC_PARSER_CMD_WRITE);
++
++ writel_relaxed(PARSER_CMD_SKIP_CFG_0, core->dos_base + HEVC_PARSER_CMD_SKIP_0);
++ writel_relaxed(PARSER_CMD_SKIP_CFG_1, core->dos_base + HEVC_PARSER_CMD_SKIP_1);
++ writel_relaxed(PARSER_CMD_SKIP_CFG_2, core->dos_base + HEVC_PARSER_CMD_SKIP_2);
++ writel_relaxed((1 << 5) | (1 << 2) | 1, core->dos_base + HEVC_PARSER_IF_CONTROL);
++
++ writel_relaxed(1, core->dos_base + HEVCD_IPP_TOP_CNTL);
++ writel_relaxed((1 << 1), core->dos_base + HEVCD_IPP_TOP_CNTL);
++
++ /* Enable 2-plane reference read mode for MC */
++ if (sess->fmt_cap->pixfmt == V4L2_PIX_FMT_NV12M)
++ writel_relaxed(1 << 31, core->dos_base + HEVCD_MPP_DECOMP_CTL1);
++
++ writel_relaxed(1, core->dos_base + HEVC_WAIT_FLAG);
++
++ /* clear mailbox interrupt */
++ writel_relaxed(1, core->dos_base + HEVC_ASSIST_MBOX1_CLR_REG);
++ /* enable mailbox interrupt */
++ writel_relaxed(1, core->dos_base + HEVC_ASSIST_MBOX1_MASK);
++ /* disable PSCALE for hardware sharing */
++ writel_relaxed(0, core->dos_base + HEVC_PSCALE_CTRL);
++ /* Let the uCode do all the parsing */
++ writel_relaxed(0xc, core->dos_base + NAL_SEARCH_CTL);
++
++ writel_relaxed(0, core->dos_base + DECODE_STOP_POS);
++ writel_relaxed(DECODE_MODE_SINGLE, core->dos_base + HEVC_DECODE_MODE);
++ writel_relaxed(0, core->dos_base + HEVC_DECODE_MODE2);
++
++ /* AUX buffers */
++ hevc->aux_vaddr = dma_alloc_coherent(core->dev, SIZE_AUX, &hevc->aux_paddr, GFP_KERNEL);
++ if (!hevc->aux_vaddr) {
++ dev_err(core->dev, "Failed to request HEVC AUX\n");
++ ret = -ENOMEM;
++ goto free_hevc;
++ }
++
++ writel_relaxed(hevc->aux_paddr, core->dos_base + HEVC_AUX_ADR);
++ writel_relaxed((((SIZE_AUX) >> 4) << 16) | 0, core->dos_base + HEVC_AUX_DATA_SIZE);
++
++ if (core->platform->revision == VDEC_REVISION_GXBB)
++ codec_hevc_setup_buffers_gxbb(sess);
++ else
++ codec_hevc_setup_buffers_gxl(sess);
++
++ if (sess->fmt_cap->pixfmt != V4L2_PIX_FMT_NV12M)
++ codec_hevc_setup_decode_head(sess);
++
++ return 0;
++
++free_hevc:
++ kfree(hevc);
++ return ret;
++}
++
++static void codec_hevc_flush_output(struct vdec_session *sess)
++{
++ struct codec_hevc *hevc = sess->priv;
++ struct hevc_frame *tmp, *n;
++
++ list_for_each_entry_safe(tmp, n, &hevc->ref_frames_list, list) {
++ vdec_dst_buf_done(sess, tmp->vbuf);
++ list_del(&tmp->list);
++ kfree(tmp);
++ hevc->frames_num--;
++ }
++}
++
++static int codec_hevc_stop(struct vdec_session *sess)
++{
++ struct codec_hevc *hevc = sess->priv;
++ struct vdec_core *core = sess->core;
++
++ mutex_lock(&sess->codec_lock);
++ codec_hevc_flush_output(sess);
++
++ if (hevc->workspace_vaddr) {
++ dma_free_coherent(core->dev, SIZE_WORKSPACE,
++ hevc->workspace_vaddr,
++ hevc->workspace_paddr);
++ hevc->workspace_vaddr = 0;
++ }
++
++ if (hevc->frame_mmu_vaddr) {
++ dma_free_coherent(core->dev, SIZE_FRAME_MMU,
++ hevc->frame_mmu_vaddr,
++ hevc->frame_mmu_paddr);
++ hevc->frame_mmu_vaddr = 0;
++ }
++
++ if (hevc->aux_vaddr) {
++ dma_free_coherent(core->dev, SIZE_AUX,
++ hevc->aux_vaddr, hevc->aux_paddr);
++ hevc->aux_vaddr = 0;
++ }
++
++ kfree(hevc);
++ sess->priv = 0;
++ mutex_unlock(&sess->codec_lock);
++
++ return 0;
++}
++
++static void codec_hevc_update_tiles(struct vdec_session *sess)
++{
++ struct codec_hevc *hevc = sess->priv;
++ struct vdec_core *core = sess->core;
++ u32 sao_mem_unit = (hevc->lcu_size == 16 ? 9 : hevc->lcu_size == 32 ? 14 : 24) << 4;
++ u32 pic_height_cu = (hevc->height + hevc->lcu_size - 1) / hevc->lcu_size;
++ u32 pic_width_cu = (hevc->width + hevc->lcu_size - 1) / hevc->lcu_size;
++ u32 sao_vb_size = (sao_mem_unit + (2 << 4)) * pic_height_cu;
++ u32 tiles_flags = hevc->rpm_param.p.tiles_flags;
++
++ if (tiles_flags & 1) {
++ /* TODO; The samples I'm using have tiles_flags == 0 */
++ return;
++ }
++
++ hevc->num_tile_col = 1;
++ hevc->num_tile_row = 1;
++ hevc->m_tile[0][0].width = pic_width_cu;
++ hevc->m_tile[0][0].height = pic_height_cu;
++ hevc->m_tile[0][0].start_cu_x = 0;
++ hevc->m_tile[0][0].start_cu_y = 0;
++ hevc->m_tile[0][0].sao_vb_start_addr = hevc->workspace_paddr + SAO_VB_OFFSET;
++ hevc->m_tile[0][0].sao_abv_start_addr = hevc->workspace_paddr + SAO_ABV_OFFSET;
++
++ hevc->tile_start_lcu_x = 0;
++ hevc->tile_start_lcu_y = 0;
++ hevc->tile_width_lcu = pic_width_cu;
++ hevc->tile_height_lcu = pic_height_cu;
++
++ writel_relaxed(sao_mem_unit, core->dos_base + HEVC_sao_mem_unit);
++ writel_relaxed(hevc->workspace_paddr + SAO_ABV_OFFSET, core->dos_base + HEVC_SAO_ABV);
++ writel_relaxed(sao_vb_size, core->dos_base + HEVC_sao_vb_size);
++ writel_relaxed(hevc->workspace_paddr + SAO_VB_OFFSET, core->dos_base + HEVC_SAO_VB);
++}
++
++static struct hevc_frame * codec_hevc_get_frame_by_poc(struct codec_hevc *hevc, u32 poc)
++{
++ struct hevc_frame *tmp;
++
++ list_for_each_entry(tmp, &hevc->ref_frames_list, list) {
++ if (tmp->poc == poc)
++ return tmp;
++ }
++
++ return NULL;
++}
++
++static struct hevc_frame * codec_hevc_prepare_new_frame(struct vdec_session *sess)
++{
++ struct vb2_v4l2_buffer *vbuf;
++ struct hevc_frame *new_frame = NULL;
++ struct codec_hevc *hevc = sess->priv;
++ union rpm_param *params = &hevc->rpm_param;
++
++ vbuf = v4l2_m2m_dst_buf_remove(sess->m2m_ctx);
++ if (!vbuf) {
++ dev_warn(sess->core->dev, "Couldn't remove dst buf\n");
++ return NULL;
++ }
++
++ new_frame = kzalloc(sizeof(*new_frame), GFP_KERNEL);
++ if (!new_frame)
++ return NULL;
++
++ new_frame->vbuf = vbuf;
++ new_frame->referenced = 1;
++ new_frame->poc = hevc->curr_poc;
++ new_frame->cur_slice_type = params->p.slice_type;
++ new_frame->num_reorder_pic = params->p.sps_num_reorder_pics_0;
++
++ list_add_tail(&new_frame->list, &hevc->ref_frames_list);
++ hevc->frames_num++;
++
++ return new_frame;
++}
++
++static void codec_hevc_set_sao(struct vdec_session *sess, struct hevc_frame *frame)
++{
++ struct vdec_core *core = sess->core;
++ struct codec_hevc *hevc = sess->priv;
++ union rpm_param *param = &hevc->rpm_param;
++ dma_addr_t buf_y_paddr = vb2_dma_contig_plane_dma_addr(&frame->vbuf->vb2_buf, 0);
++ dma_addr_t buf_u_v_paddr = vb2_dma_contig_plane_dma_addr(&frame->vbuf->vb2_buf, 1);
++ u32 misc_flag0 = param->p.misc_flag0;
++ u32 slice_deblocking_filter_disabled_flag;
++ u32 val, val_2;
++
++ val = (readl_relaxed(core->dos_base + HEVC_SAO_CTRL0) & ~0xf) | ilog2(hevc->lcu_size);
++ writel_relaxed(val, core->dos_base + HEVC_SAO_CTRL0);
++
++ writel_relaxed(hevc->width | (hevc->height << 16), core->dos_base + HEVC_SAO_PIC_SIZE);
++ writel_relaxed((hevc->lcu_x_num - 1) | (hevc->lcu_y_num - 1) << 16, core->dos_base + HEVC_SAO_PIC_SIZE_LCU);
++
++ writel_relaxed(buf_y_paddr, core->dos_base + HEVC_SAO_Y_START_ADDR);
++ writel_relaxed(vdec_get_output_size(sess), core->dos_base + HEVC_SAO_Y_LENGTH);
++ writel_relaxed(buf_u_v_paddr, core->dos_base + HEVC_SAO_C_START_ADDR);
++ writel_relaxed((vdec_get_output_size(sess) / 2), core->dos_base + HEVC_SAO_C_LENGTH);
++
++ writel_relaxed(buf_y_paddr, core->dos_base + HEVC_SAO_Y_WPTR);
++ writel_relaxed(buf_u_v_paddr, core->dos_base + HEVC_SAO_C_WPTR);
++
++ if (frame->cur_slice_idx == 0) {
++ writel_relaxed(hevc->width | (hevc->height << 16), core->dos_base + HEVC_DBLK_CFG2);
++
++ val = 0;
++ if ((misc_flag0 >> PCM_ENABLE_FLAG_BIT) & 0x1)
++ val |= ((misc_flag0 >> PCM_LOOP_FILTER_DISABLED_FLAG_BIT) & 0x1) << 3;
++
++ val |= (param->p.pps_cb_qp_offset & 0x1f) << 4;
++ val |= (param->p.pps_cr_qp_offset & 0x1f) << 9;
++ val |= (hevc->lcu_size == 64) ? 0 : ((hevc->lcu_size == 32) ? 1 : 2);
++ writel_relaxed(val, core->dos_base + HEVC_DBLK_CFG1);
++ }
++
++ val = readl_relaxed(core->dos_base + HEVC_SAO_CTRL1) & ~0x3ff3;
++ if (sess->fmt_cap->pixfmt == V4L2_PIX_FMT_NV12M)
++ val |= 0xff0 | /* Set endianness for 2-bytes swaps (nv12) */
++ 0x1; /* disable cm compression */
++ else
++ val |= 0x3000 | /* 64x32 block mode */
++ 0x880 | /* 64-bit Big Endian */
++ 0x2; /* Disable double write */
++
++ writel_relaxed(val, core->dos_base + HEVC_SAO_CTRL1);
++
++ /* set them all 0 for H265_NV21 (no down-scale) */
++ val = readl_relaxed(core->dos_base + HEVC_SAO_CTRL5) & ~0xff0000;
++ writel_relaxed(val, core->dos_base + HEVC_SAO_CTRL5);
++
++ val = readl_relaxed(core->dos_base + HEVCD_IPP_AXIIF_CONFIG) & ~0x30;
++ val |= 0xf;
++ if (sess->fmt_cap->pixfmt != V4L2_PIX_FMT_NV12M)
++ val |= 0x30; /* 64x32 block mode */
++
++ writel_relaxed(val, core->dos_base + HEVCD_IPP_AXIIF_CONFIG);
++
++ val = 0;
++ val_2 = readl_relaxed(core->dos_base + HEVC_SAO_CTRL0);
++ val_2 &= (~0x300);
++
++ /* TODO: handle tiles here if enabled */
++ slice_deblocking_filter_disabled_flag = (misc_flag0 >>
++ SLICE_DEBLOCKING_FILTER_DISABLED_FLAG_BIT) & 0x1;
++ if ((misc_flag0 & (1 << DEBLOCKING_FILTER_OVERRIDE_ENABLED_FLAG_BIT))
++ && (misc_flag0 & (1 << DEBLOCKING_FILTER_OVERRIDE_FLAG_BIT))) {
++ val |= slice_deblocking_filter_disabled_flag << 2;
++
++ if (!slice_deblocking_filter_disabled_flag) {
++ val |= (param->p.slice_beta_offset_div2 & 0xf) << 3;
++ val |= (param->p.slice_tc_offset_div2 & 0xf) << 7;
++ }
++ } else {
++ val |=
++ ((misc_flag0 >>
++ PPS_DEBLOCKING_FILTER_DISABLED_FLAG_BIT) & 0x1) << 2;
++
++ if (((misc_flag0 >> PPS_DEBLOCKING_FILTER_DISABLED_FLAG_BIT) &
++ 0x1) == 0) {
++ val |= (param->p.pps_beta_offset_div2 & 0xf) << 3;
++ val |= (param->p.pps_tc_offset_div2 & 0xf) << 7;
++ }
++ }
++ if ((misc_flag0 & (1 << PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG_BIT))
++ && ((misc_flag0 & (1 << SLICE_SAO_LUMA_FLAG_BIT))
++ || (misc_flag0 & (1 << SLICE_SAO_CHROMA_FLAG_BIT))
++ || (!slice_deblocking_filter_disabled_flag))) {
++ val |=
++ ((misc_flag0 >>
++ SLICE_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG_BIT)
++ & 0x1) << 1;
++ val_2 |=
++ ((misc_flag0 >>
++ SLICE_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG_BIT)
++ & 0x1) << 9;
++ } else {
++ val |=
++ ((misc_flag0 >>
++ PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG_BIT)
++ & 0x1) << 1;
++ val_2 |=
++ ((misc_flag0 >>
++ PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG_BIT)
++ & 0x1) << 9;
++ }
++
++ writel_relaxed(val, core->dos_base + HEVC_DBLK_CFG9);
++ writel_relaxed(val_2, core->dos_base + HEVC_SAO_CTRL0);
++}
++
++static dma_addr_t codec_hevc_get_frame_mv_paddr(struct codec_hevc *hevc, struct hevc_frame *frame)
++{
++ return hevc->workspace_paddr + MPRED_MV_OFFSET +
++ (frame->vbuf->vb2_buf.index * MPRED_MV_BUF_SIZE);
++}
++
++/* Update the necessary information for motion prediction with the current slice */
++static void codec_hevc_set_mpred(struct vdec_session *sess, struct hevc_frame *frame, struct hevc_frame *col_frame)
++{
++ struct vdec_core *core = sess->core;
++ struct codec_hevc *hevc = sess->priv;
++ union rpm_param *param = &hevc->rpm_param;
++ u32 *ref_num = frame->ref_num;
++ u32 *ref_poc_l0 = frame->ref_poc_list[0][frame->cur_slice_idx];
++ u32 *ref_poc_l1 = frame->ref_poc_list[1][frame->cur_slice_idx];
++ u32 lcu_size_log2 = ilog2(hevc->lcu_size);
++ u32 mv_mem_unit = lcu_size_log2 == 6 ? 0x200 : lcu_size_log2 == 5 ? 0x80 : 0x20;
++ u32 slice_segment_address = param->p.slice_segment_address;
++ u32 max_num_merge_cand = 5 - param->p.five_minus_max_num_merge_cand;
++ u32 plevel = param->p.log2_parallel_merge_level;
++ u32 col_from_l0_flag = param->p.collocated_from_l0_flag;
++ u32 tmvp_flag = param->p.slice_temporal_mvp_enable_flag;
++ u32 is_next_slice_segment = param->p.dependent_slice_segment_flag ? 1 : 0;
++ u32 slice_type = param->p.slice_type;
++ dma_addr_t col_mv_rd_start_addr, col_mv_rd_ptr, col_mv_rd_end_addr;
++ dma_addr_t mpred_mv_wr_ptr;
++ u32 mv_rd_en = 1;
++ u32 val;
++ int i;
++
++ val = readl_relaxed(core->dos_base + HEVC_MPRED_CURR_LCU);
++
++ col_mv_rd_start_addr = codec_hevc_get_frame_mv_paddr(hevc, col_frame);
++ mpred_mv_wr_ptr = codec_hevc_get_frame_mv_paddr(hevc, frame) + (hevc->slice_addr * mv_mem_unit);
++ col_mv_rd_ptr = col_mv_rd_start_addr + (hevc->slice_addr * mv_mem_unit);
++ col_mv_rd_end_addr = col_mv_rd_start_addr + ((hevc->lcu_x_num * hevc->lcu_y_num) * mv_mem_unit);
++
++ writel_relaxed(codec_hevc_get_frame_mv_paddr(hevc, frame), core->dos_base + HEVC_MPRED_MV_WR_START_ADDR);
++ writel_relaxed(col_mv_rd_start_addr, core->dos_base + HEVC_MPRED_MV_RD_START_ADDR);
++
++ val = ((hevc->lcu_x_num - hevc->tile_width_lcu) * mv_mem_unit);
++ writel_relaxed(val, core->dos_base + HEVC_MPRED_MV_WR_ROW_JUMP);
++ writel_relaxed(val, core->dos_base + HEVC_MPRED_MV_RD_ROW_JUMP);
++
++ if (slice_type == I_SLICE)
++ mv_rd_en = 0;
++
++ val = slice_type |
++ 1 << 2 | // new pic
++ 1 << 3 | // new tile
++ is_next_slice_segment << 4 |
++ tmvp_flag << 5 |
++ hevc->ldc_flag << 6 |
++ col_from_l0_flag << 7 |
++ 1 << 9 |
++ 1 << 10 |
++ mv_rd_en << 11 |
++ 1 << 13 |
++ lcu_size_log2 << 16 |
++ 3 << 20 | plevel << 24;
++ writel_relaxed(val, core->dos_base + HEVC_MPRED_CTRL0);
++
++ val = max_num_merge_cand | 2 << 4 | 3 << 8 | 5 << 12 | 36 << 16;
++ writel_relaxed(val, core->dos_base + HEVC_MPRED_CTRL1);
++
++ writel_relaxed(hevc->width | (hevc->height << 16), core->dos_base + HEVC_MPRED_PIC_SIZE);
++
++ val = ((hevc->lcu_x_num - 1) | (hevc->lcu_y_num - 1) << 16);
++ writel_relaxed(val, core->dos_base + HEVC_MPRED_PIC_SIZE_LCU);
++ val = (hevc->tile_start_lcu_x | hevc->tile_start_lcu_y << 16);
++ writel_relaxed(val, core->dos_base + HEVC_MPRED_TILE_START);
++ val = (hevc->tile_width_lcu | hevc->tile_height_lcu << 16);
++ writel_relaxed(val, core->dos_base + HEVC_MPRED_TILE_SIZE_LCU);
++
++ writel_relaxed((ref_num[1] << 8) | ref_num[0], core->dos_base + HEVC_MPRED_REF_NUM);
++ writel_relaxed((1 << ref_num[0]) - 1, core->dos_base + HEVC_MPRED_REF_EN_L0);
++ writel_relaxed((1 << ref_num[1]) - 1, core->dos_base + HEVC_MPRED_REF_EN_L1);
++
++ writel_relaxed(hevc->curr_poc, core->dos_base + HEVC_MPRED_CUR_POC);
++ writel_relaxed(hevc->col_poc, core->dos_base + HEVC_MPRED_COL_POC);
++
++ for (i = 0; i < MAX_REF_ACTIVE; ++i) {
++ writel_relaxed(ref_poc_l0[i], core->dos_base + HEVC_MPRED_L0_REF00_POC + i * 4);
++ writel_relaxed(ref_poc_l1[i], core->dos_base + HEVC_MPRED_L1_REF00_POC + i * 4);
++ }
++
++ if (slice_segment_address == 0) {
++ writel_relaxed(hevc->workspace_paddr + MPRED_ABV_OFFSET, core->dos_base + HEVC_MPRED_ABV_START_ADDR);
++ writel_relaxed(mpred_mv_wr_ptr, core->dos_base + HEVC_MPRED_MV_WPTR);
++ writel_relaxed(col_mv_rd_start_addr, core->dos_base + HEVC_MPRED_MV_RPTR);
++ } else {
++ writel_relaxed(col_mv_rd_ptr, core->dos_base + HEVC_MPRED_MV_RPTR);
++ }
++
++ writel_relaxed(col_mv_rd_end_addr, core->dos_base + HEVC_MPRED_MV_RD_END_ADDR);
++}
++
++/* motion compensation reference cache controller */
++static void codec_hevc_set_mcrcc(struct vdec_session *sess)
++{
++ struct vdec_core *core = sess->core;
++ struct codec_hevc *hevc = sess->priv;
++ u32 val, val_2;
++ int l0_cnt = hevc->cur_frame->ref_num[0];
++ int l1_cnt = hevc->cur_frame->ref_num[1];
++
++ /* reset mcrcc */
++ writel_relaxed(0x02, core->dos_base + HEVCD_MCRCC_CTL1);
++
++ if (hevc->cur_frame->cur_slice_type == I_SLICE) {
++ /* remove reset -- disables clock */
++ writel_relaxed(0, core->dos_base + HEVCD_MCRCC_CTL1);
++ return;
++ }
++
++ if (hevc->cur_frame->cur_slice_type == P_SLICE) {
++ writel_relaxed(1 << 1, core->dos_base + HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR);
++ val = readl_relaxed(core->dos_base + HEVCD_MPP_ANC_CANVAS_DATA_ADDR);
++ val &= 0xffff;
++ val |= (val << 16);
++ writel_relaxed(val, core->dos_base + HEVCD_MCRCC_CTL2);
++
++ if (l0_cnt == 1) {
++ writel_relaxed(val, core->dos_base + HEVCD_MCRCC_CTL3);
++ } else {
++ val = readl_relaxed(core->dos_base + HEVCD_MPP_ANC_CANVAS_DATA_ADDR);
++ val &= 0xffff;
++ val |= (val << 16);
++ writel_relaxed(val, core->dos_base + HEVCD_MCRCC_CTL3);
++ }
++ } else { /* B_SLICE */
++ writel_relaxed(0, core->dos_base + HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR);
++ val = readl_relaxed(core->dos_base + HEVCD_MPP_ANC_CANVAS_DATA_ADDR);
++ val &= 0xffff;
++ val |= (val << 16);
++ writel_relaxed(val, core->dos_base + HEVCD_MCRCC_CTL2);
++
++ writel_relaxed((16 << 8) | (1 << 1), core->dos_base + HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR);
++ val_2 = readl_relaxed(core->dos_base + HEVCD_MPP_ANC_CANVAS_DATA_ADDR);
++ val_2 &= 0xffff;
++ val_2 |= (val_2 << 16);
++ if (val == val_2 && l1_cnt > 1) {
++ val_2 = readl_relaxed(core->dos_base + HEVCD_MPP_ANC_CANVAS_DATA_ADDR);
++ val_2 &= 0xffff;
++ val_2 |= (val_2 << 16);
++ }
++ writel_relaxed(val, core->dos_base + HEVCD_MCRCC_CTL3);
++ }
++
++ /* enable mcrcc progressive-mode */
++ writel_relaxed(0xff0, core->dos_base + HEVCD_MCRCC_CTL1);
++}
++
++static void codec_hevc_set_ref_list(struct vdec_session *sess,
++ u32 ref_num, u32 *ref_poc_list)
++{
++ struct codec_hevc *hevc = sess->priv;
++ struct hevc_frame *ref_frame;
++ struct vdec_core *core = sess->core;
++ int i;
++ u32 ref_frame_id;
++
++ for (i = 0; i < ref_num; i++) {
++ ref_frame = codec_hevc_get_frame_by_poc(hevc, ref_poc_list[i]);
++
++ if (!ref_frame) {
++ dev_warn(core->dev, "Couldn't find ref. frame %u\n",
++ ref_poc_list[i]);
++ continue;
++ }
++
++ ref_frame_id = ref_frame->vbuf->vb2_buf.index * 2;
++ dev_dbg(core->dev, "Programming ref poc %u\n", ref_poc_list[i]);
++
++ writel_relaxed(((ref_frame_id + 1) << 16) |
++ ((ref_frame_id + 1) << 8) |
++ ref_frame_id,
++ core->dos_base + HEVCD_MPP_ANC_CANVAS_DATA_ADDR);
++ }
++}
++
++static void codec_hevc_set_mc(struct vdec_session *sess, struct hevc_frame *frame)
++{
++ struct vdec_core *core = sess->core;
++
++ if (frame->cur_slice_type == I_SLICE)
++ return;
++
++ writel_relaxed(1, core->dos_base + HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR);
++ codec_hevc_set_ref_list(sess, frame->ref_num[0],
++ frame->ref_poc_list[0][frame->cur_slice_idx]);
++
++ if (frame->cur_slice_type == P_SLICE)
++ return;
++
++ writel_relaxed((16 << 8) | 1, core->dos_base + HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR);
++ codec_hevc_set_ref_list(sess, frame->ref_num[1],
++ frame->ref_poc_list[1][frame->cur_slice_idx]);
++}
++
++static void codec_hevc_update_col_frame(struct codec_hevc *hevc)
++{
++ struct hevc_frame *cur_frame = hevc->cur_frame;
++ union rpm_param *param = &hevc->rpm_param;
++ u32 list_no = 0;
++ u32 col_ref = param->p.collocated_ref_idx;
++ u32 col_from_l0 = param->p.collocated_from_l0_flag;
++
++ if (cur_frame->cur_slice_type == B_SLICE)
++ list_no = 1 - col_from_l0;
++
++ if (col_ref >= cur_frame->ref_num[list_no])
++ hevc->col_poc = INVALID_POC;
++ else
++ hevc->col_poc = cur_frame->ref_poc_list[list_no][cur_frame->cur_slice_idx][col_ref];
++
++ if (cur_frame->cur_slice_type == I_SLICE)
++ goto end;
++
++ if (hevc->col_poc != INVALID_POC)
++ hevc->col_frame = codec_hevc_get_frame_by_poc(hevc, hevc->col_poc);
++ else
++ hevc->col_frame = hevc->cur_frame;
++
++end:
++ if (!hevc->col_frame)
++ hevc->col_frame = hevc->cur_frame;
++}
++
++static void codec_hevc_update_pocs(struct vdec_session *sess)
++{
++ struct codec_hevc *hevc = sess->priv;
++ union rpm_param *param = &hevc->rpm_param;
++ u32 nal_unit_type = param->p.m_nalUnitType;
++ u32 temporal_id = param->p.m_temporalId & 0x7;
++ int iMaxPOClsb = 1 << (param->p.log2_max_pic_order_cnt_lsb_minus4 + 4);
++ int iPrevPOClsb;
++ int iPrevPOCmsb;
++ int iPOCmsb;
++ int iPOClsb = param->p.POClsb;
++
++ if (nal_unit_type == NAL_UNIT_CODED_SLICE_IDR ||
++ nal_unit_type == NAL_UNIT_CODED_SLICE_IDR_N_LP) {
++ hevc->curr_poc = 0;
++ if ((temporal_id - 1) == 0)
++ hevc->iPrevTid0POC = hevc->curr_poc;
++
++ return;
++ }
++
++ iPrevPOClsb = hevc->iPrevTid0POC % iMaxPOClsb;
++ iPrevPOCmsb = hevc->iPrevTid0POC - iPrevPOClsb;
++
++ if ((iPOClsb < iPrevPOClsb) && ((iPrevPOClsb - iPOClsb) >= (iMaxPOClsb / 2)))
++ iPOCmsb = iPrevPOCmsb + iMaxPOClsb;
++ else if ((iPOClsb > iPrevPOClsb) && ((iPOClsb - iPrevPOClsb) > (iMaxPOClsb / 2)))
++ iPOCmsb = iPrevPOCmsb - iMaxPOClsb;
++ else
++ iPOCmsb = iPrevPOCmsb;
++
++ if (nal_unit_type == NAL_UNIT_CODED_SLICE_BLA ||
++ nal_unit_type == NAL_UNIT_CODED_SLICE_BLANT ||
++ nal_unit_type == NAL_UNIT_CODED_SLICE_BLA_N_LP)
++ iPOCmsb = 0;
++
++ hevc->curr_poc = (iPOCmsb + iPOClsb);
++ if ((temporal_id - 1) == 0)
++ hevc->iPrevTid0POC = hevc->curr_poc;
++}
++
++static int codec_hevc_process_segment_header(struct vdec_session *sess)
++{
++ struct codec_hevc *hevc = sess->priv;
++ union rpm_param *param = &hevc->rpm_param;
++ u32 slice_segment_address = param->p.slice_segment_address;
++
++ if (param->p.first_slice_segment_in_pic_flag == 0) {
++ hevc->slice_segment_addr = param->p.slice_segment_address;
++ if (!param->p.dependent_slice_segment_flag)
++ hevc->slice_addr = hevc->slice_segment_addr;
++ } else {
++ hevc->slice_segment_addr = 0;
++ hevc->slice_addr = 0;
++ }
++
++ codec_hevc_update_pocs(sess);
++
++ /* First slice: new frame */
++ if (slice_segment_address == 0) {
++ codec_hevc_update_referenced(hevc);
++ codec_hevc_output_frames(sess);
++
++ hevc->cur_frame = codec_hevc_prepare_new_frame(sess);
++ if (!hevc->cur_frame) {
++ dev_err(sess->core->dev_dec,
++ "No destination buffer available\n");
++ return -1;
++ }
++
++ codec_hevc_update_tiles(sess);
++ } else {
++ hevc->cur_frame->cur_slice_idx++;
++ }
++
++ return 0;
++}
++
++static int codec_hevc_process_rpm(struct vdec_session *sess)
++{
++ struct vdec_core *core = sess->core;
++ struct codec_hevc *hevc = sess->priv;
++ union rpm_param *rpm_param = &hevc->rpm_param;
++ u32 lcu_x_num_div, lcu_y_num_div;
++
++ if (rpm_param->p.bit_depth &&
++ sess->fmt_cap->pixfmt == V4L2_PIX_FMT_NV12M) {
++ dev_err(sess->core->dev_dec,
++ "V4L2_PIX_FMT_NV12M is only compatible with HEVC 8-bit\n");
++ return -EINVAL;
++ }
++
++ hevc->width = rpm_param->p.pic_width_in_luma_samples;
++ hevc->height = rpm_param->p.pic_height_in_luma_samples;
++
++ /*if (hevc->width != sess->width ||
++ hevc->height != sess->height) {
++ dev_err(sess->core->dev_dec,
++ "Size mismatch: bitstream %ux%u ; driver %ux%u\n",
++ hevc->width, hevc->height,
++ sess->width, sess->height);
++ return -EINVAL;
++ }*/
++
++ hevc->lcu_size = 1 << (rpm_param->p.log2_min_coding_block_size_minus3 +
++ 3 + rpm_param->p.log2_diff_max_min_coding_block_size);
++
++ lcu_x_num_div = (hevc->width / hevc->lcu_size);
++ lcu_y_num_div = (hevc->height / hevc->lcu_size);
++ hevc->lcu_x_num = ((hevc->width % hevc->lcu_size) == 0) ? lcu_x_num_div : lcu_x_num_div + 1;
++ hevc->lcu_y_num = ((hevc->height % hevc->lcu_size) == 0) ? lcu_y_num_div : lcu_y_num_div + 1;
++ hevc->lcu_total = hevc->lcu_x_num * hevc->lcu_y_num;
++
++ dev_dbg(core->dev, "lcu_size = %u ; lcu_x_num = %u; lcu_y_num = %u",
++ hevc->lcu_size, hevc->lcu_x_num, hevc->lcu_y_num);
++
++ return 0;
++}
++
++/* The RPM section within the workspace contains
++ * many information regarding the parsed bitstream
++ */
++static void codec_hevc_fetch_rpm(struct vdec_session *sess)
++{
++ struct codec_hevc *hevc = sess->priv;
++ u16 *rpm_vaddr = hevc->workspace_vaddr + RPM_OFFSET;
++ int i, j;
++
++ for (i = 0; i < RPM_SIZE; i += 4)
++ for (j = 0; j < 4; j++)
++ hevc->rpm_param.l.data[i + j] = rpm_vaddr[i + 3 - j];
++}
++
++static irqreturn_t codec_hevc_threaded_isr(struct vdec_session *sess)
++{
++ struct vdec_core *core = sess->core;
++ struct codec_hevc *hevc;
++
++ mutex_lock(&sess->codec_lock);
++ hevc = sess->priv;
++ if (!hevc)
++ goto unlock;
++
++ if (hevc->dec_status != HEVC_SLICE_SEGMENT_DONE) {
++ dev_err(core->dev_dec, "Unrecognized dec_status: %08X\n",
++ hevc->dec_status);
++ vdec_abort(sess);
++ goto unlock;
++ }
++
++ codec_hevc_fetch_rpm(sess);
++ if (codec_hevc_process_rpm(sess)) {
++ vdec_abort(sess);
++ goto unlock;
++ }
++
++ if (codec_hevc_process_segment_header(sess)) {
++ vdec_abort(sess);
++ goto unlock;
++ }
++
++ codec_hevc_update_frame_refs(sess, hevc->cur_frame);
++ codec_hevc_update_col_frame(hevc);
++ codec_hevc_update_ldc_flag(hevc);
++ codec_hevc_set_mc(sess, hevc->cur_frame);
++ codec_hevc_set_mcrcc(sess);
++ codec_hevc_set_mpred(sess, hevc->cur_frame, hevc->col_frame);
++ codec_hevc_set_sao(sess, hevc->cur_frame);
++
++ writel_relaxed(readl_relaxed(core->dos_base + HEVC_WAIT_FLAG) | 2, core->dos_base + HEVC_WAIT_FLAG);
++ writel_relaxed(HEVC_CODED_SLICE_SEGMENT_DAT, core->dos_base + HEVC_DEC_STATUS_REG);
++
++ /* Interrupt the firmware's processor */
++ writel_relaxed(AMRISC_MAIN_REQ, core->dos_base + HEVC_MCPU_INTR_REQ);
++
++unlock:
++ mutex_unlock(&sess->codec_lock);
++ return IRQ_HANDLED;
++}
++
++static irqreturn_t codec_hevc_isr(struct vdec_session *sess)
++{
++ struct vdec_core *core = sess->core;
++ struct codec_hevc *hevc = sess->priv;
++
++ hevc->dec_status = readl_relaxed(core->dos_base + HEVC_DEC_STATUS_REG);
++
++ return IRQ_WAKE_THREAD;
++}
++
++struct vdec_codec_ops codec_hevc_ops = {
++ .start = codec_hevc_start,
++ .stop = codec_hevc_stop,
++ .isr = codec_hevc_isr,
++ .threaded_isr = codec_hevc_threaded_isr,
++ .num_pending_bufs = codec_hevc_num_pending_bufs,
++};
+\ No newline at end of file
+diff --git a/drivers/media/platform/meson/vdec/codec_hevc.h b/drivers/media/platform/meson/vdec/codec_hevc.h
+new file mode 100644
+index 0000000..1cf009b
+--- /dev/null
++++ b/drivers/media/platform/meson/vdec/codec_hevc.h
+@@ -0,0 +1,13 @@
++/* SPDX-License-Identifier: GPL-2.0+ */
++/*
++ * Copyright (C) 2018 Maxime Jourdan <maxi.jourdan@wanadoo.fr>
++ */
++
++#ifndef __MESON_VDEC_CODEC_HEVC_H_
++#define __MESON_VDEC_CODEC_HEVC_H_
++
++#include "vdec.h"
++
++extern struct vdec_codec_ops codec_hevc_ops;
++
++#endif
+\ No newline at end of file
+diff --git a/drivers/media/platform/meson/vdec/codec_mjpeg.c b/drivers/media/platform/meson/vdec/codec_mjpeg.c
+new file mode 100644
+index 0000000..57b56d7
+--- /dev/null
++++ b/drivers/media/platform/meson/vdec/codec_mjpeg.c
+@@ -0,0 +1,203 @@
++// SPDX-License-Identifier: GPL-2.0+
++/*
++ * Copyright (C) 2018 Maxime Jourdan <maxi.jourdan@wanadoo.fr>
++ */
++
++#include <media/v4l2-mem2mem.h>
++#include <media/videobuf2-dma-contig.h>
++
++#include "codec_mjpeg.h"
++#include "codec_helpers.h"
++
++/* DOS registers */
++#define VDEC_ASSIST_AMR1_INT8 0x00b4
++
++#define ASSIST_MBOX1_CLR_REG 0x01d4
++#define ASSIST_MBOX1_MASK 0x01d8
++
++#define MCPU_INTR_MSK 0x0c10
++
++#define PSCALE_RST 0x2440
++#define PSCALE_CTRL 0x2444
++#define PSCALE_BMEM_ADDR 0x247c
++#define PSCALE_BMEM_DAT 0x2480
++
++#define MDEC_PIC_DC_CTRL 0x2638
++
++#define AV_SCRATCH_0 0x2700
++#define AV_SCRATCH_1 0x2704
++#define MREG_DECODE_PARAM 0x2708
++#define AV_SCRATCH_4 0x2710
++#define MREG_TO_AMRISC 0x2720
++#define MREG_FROM_AMRISC 0x2724
++
++#define DOS_SW_RESET0 0xfc00
++
++struct codec_mjpeg {
++ /* Housekeeping thread for marking buffers to DONE
++ * and recycling them into the hardware
++ */
++ struct task_struct *buffers_thread;
++};
++
++static int codec_mjpeg_buffers_thread(void *data)
++{
++ struct vdec_buffer *tmp;
++ struct vdec_session *sess = data;
++ struct vdec_core *core = sess->core;;
++
++ while (!kthread_should_stop()) {
++ mutex_lock(&sess->bufs_recycle_lock);
++ while (!list_empty(&sess->bufs_recycle) &&
++ !readl_relaxed(core->dos_base + MREG_TO_AMRISC))
++ {
++ tmp = list_first_entry(&sess->bufs_recycle, struct vdec_buffer, list);
++
++ /* Tell the decoder he can recycle this buffer */
++ writel_relaxed(tmp->index + 1, core->dos_base + MREG_TO_AMRISC);
++
++ dev_dbg(core->dev, "Buffer %d recycled\n", tmp->index);
++
++ list_del(&tmp->list);
++ kfree(tmp);
++ }
++ mutex_unlock(&sess->bufs_recycle_lock);
++
++ usleep_range(5000, 10000);
++ }
++
++ return 0;
++}
++
++/* 4 point triangle */
++static const uint32_t filt_coef[] = {
++ 0x20402000, 0x20402000, 0x1f3f2101, 0x1f3f2101,
++ 0x1e3e2202, 0x1e3e2202, 0x1d3d2303, 0x1d3d2303,
++ 0x1c3c2404, 0x1c3c2404, 0x1b3b2505, 0x1b3b2505,
++ 0x1a3a2606, 0x1a3a2606, 0x19392707, 0x19392707,
++ 0x18382808, 0x18382808, 0x17372909, 0x17372909,
++ 0x16362a0a, 0x16362a0a, 0x15352b0b, 0x15352b0b,
++ 0x14342c0c, 0x14342c0c, 0x13332d0d, 0x13332d0d,
++ 0x12322e0e, 0x12322e0e, 0x11312f0f, 0x11312f0f,
++ 0x10303010
++};
++
++static void codec_mjpeg_init_scaler(struct vdec_core *core)
++{
++ int i;
++
++ /* PSCALE cbus bmem enable */
++ writel_relaxed(0xc000, core->dos_base + PSCALE_CTRL);
++
++ writel_relaxed(0, core->dos_base + PSCALE_BMEM_ADDR);
++ for (i = 0; i < ARRAY_SIZE(filt_coef); ++i) {
++ writel_relaxed(0, core->dos_base + PSCALE_BMEM_DAT);
++ writel_relaxed(filt_coef[i], core->dos_base + PSCALE_BMEM_DAT);
++ }
++
++ writel_relaxed(74, core->dos_base + PSCALE_BMEM_ADDR);
++ writel_relaxed(0x0008, core->dos_base + PSCALE_BMEM_DAT);
++ writel_relaxed(0x60000000, core->dos_base + PSCALE_BMEM_DAT);
++
++ writel_relaxed(82, core->dos_base + PSCALE_BMEM_ADDR);
++ writel_relaxed(0x0008, core->dos_base + PSCALE_BMEM_DAT);
++ writel_relaxed(0x60000000, core->dos_base + PSCALE_BMEM_DAT);
++
++ writel_relaxed(78, core->dos_base + PSCALE_BMEM_ADDR);
++ writel_relaxed(0x0008, core->dos_base + PSCALE_BMEM_DAT);
++ writel_relaxed(0x60000000, core->dos_base + PSCALE_BMEM_DAT);
++
++ writel_relaxed(86, core->dos_base + PSCALE_BMEM_ADDR);
++ writel_relaxed(0x0008, core->dos_base + PSCALE_BMEM_DAT);
++ writel_relaxed(0x60000000, core->dos_base + PSCALE_BMEM_DAT);
++
++ writel_relaxed(73, core->dos_base + PSCALE_BMEM_ADDR);
++ writel_relaxed(0x10000, core->dos_base + PSCALE_BMEM_DAT);
++ writel_relaxed(81, core->dos_base + PSCALE_BMEM_ADDR);
++ writel_relaxed(0x10000, core->dos_base + PSCALE_BMEM_DAT);
++
++ writel_relaxed(77, core->dos_base + PSCALE_BMEM_ADDR);
++ writel_relaxed(0x10000, core->dos_base + PSCALE_BMEM_DAT);
++ writel_relaxed(85, core->dos_base + PSCALE_BMEM_ADDR);
++ writel_relaxed(0x10000, core->dos_base + PSCALE_BMEM_DAT);
++
++ writel_relaxed((1 << 10), core->dos_base + DOS_SW_RESET0);
++ writel_relaxed(0, core->dos_base + DOS_SW_RESET0);
++
++ writel_relaxed(0x7, core->dos_base + PSCALE_RST);
++ writel_relaxed(0, core->dos_base + PSCALE_RST);
++}
++
++static int codec_mjpeg_start(struct vdec_session *sess)
++{
++ struct vdec_core *core = sess->core;
++ struct codec_mjpeg *mjpeg = sess->priv;
++
++ mjpeg = kzalloc(sizeof(*mjpeg), GFP_KERNEL);
++ if (!mjpeg)
++ return -ENOMEM;
++
++ sess->priv = mjpeg;
++
++ writel_relaxed((1 << 7) | (1 << 6), core->dos_base + DOS_SW_RESET0);
++ writel_relaxed(0, core->dos_base + DOS_SW_RESET0);
++
++ writel_relaxed(12, core->dos_base + AV_SCRATCH_0);
++ writel_relaxed(0x031a, core->dos_base + AV_SCRATCH_1);
++
++ codec_helper_set_canvases(sess, core->dos_base + AV_SCRATCH_4);
++ codec_mjpeg_init_scaler(core);
++
++ writel_relaxed(0, core->dos_base + MREG_TO_AMRISC);
++ writel_relaxed(0, core->dos_base + MREG_FROM_AMRISC);
++ writel_relaxed(0xffff, core->dos_base + MCPU_INTR_MSK);
++ writel_relaxed((sess->height << 4) | 0x8000, core->dos_base + MREG_DECODE_PARAM);
++ writel_relaxed(1, core->dos_base + ASSIST_MBOX1_CLR_REG);
++ writel_relaxed(1, core->dos_base + ASSIST_MBOX1_MASK);
++ writel_relaxed(8, core->dos_base + VDEC_ASSIST_AMR1_INT8);
++
++ /* Enable 2-plane output */
++ writel_relaxed(readl_relaxed(core->dos_base + MDEC_PIC_DC_CTRL) | (1 << 17), core->dos_base + MDEC_PIC_DC_CTRL);
++
++ mjpeg->buffers_thread = kthread_run(codec_mjpeg_buffers_thread, sess, "buffers_done");
++
++ return 0;
++}
++
++static int codec_mjpeg_stop(struct vdec_session *sess)
++{
++ struct codec_mjpeg *mjpeg = sess->priv;
++
++ kthread_stop(mjpeg->buffers_thread);
++
++ kfree(mjpeg);
++ sess->priv = 0;
++
++ return 0;
++}
++
++static irqreturn_t codec_mjpeg_isr(struct vdec_session *sess)
++{
++ u32 reg;
++ u32 buffer_index;
++ struct vdec_core *core = sess->core;
++
++ writel_relaxed(1, core->dos_base + ASSIST_MBOX1_CLR_REG);
++
++ reg = readl_relaxed(core->dos_base + MREG_FROM_AMRISC);
++ if (!(reg & 0x7))
++ return IRQ_HANDLED;
++
++ buffer_index = ((reg & 0x7) - 1) & 3;
++ vdec_dst_buf_done_idx(sess, buffer_index);
++
++ writel_relaxed(0, core->dos_base + MREG_FROM_AMRISC);
++ return IRQ_HANDLED;
++}
++
++struct vdec_codec_ops codec_mjpeg_ops = {
++ .start = codec_mjpeg_start,
++ .stop = codec_mjpeg_stop,
++ .isr = codec_mjpeg_isr,
++ .notify_dst_buffer = vdec_queue_recycle,
++};
+diff --git a/drivers/media/platform/meson/vdec/codec_mjpeg.h b/drivers/media/platform/meson/vdec/codec_mjpeg.h
+new file mode 100644
+index 0000000..1164c61
+--- /dev/null
++++ b/drivers/media/platform/meson/vdec/codec_mjpeg.h
+@@ -0,0 +1,13 @@
++/* SPDX-License-Identifier: GPL-2.0+ */
++/*
++ * Copyright (C) 2018 Maxime Jourdan <maxi.jourdan@wanadoo.fr>
++ */
++
++#ifndef __MESON_VDEC_CODEC_MJPEG_H_
++#define __MESON_VDEC_CODEC_MJPEG_H_
++
++#include "vdec.h"
++
++extern struct vdec_codec_ops codec_mjpeg_ops;
++
++#endif
+\ No newline at end of file
+diff --git a/drivers/media/platform/meson/vdec/codec_mpeg12.c b/drivers/media/platform/meson/vdec/codec_mpeg12.c
+new file mode 100644
+index 0000000..8682adc
+--- /dev/null
++++ b/drivers/media/platform/meson/vdec/codec_mpeg12.c
+@@ -0,0 +1,183 @@
++// SPDX-License-Identifier: GPL-2.0+
++/*
++ * Copyright (C) 2018 Maxime Jourdan <maxi.jourdan@wanadoo.fr>
++ */
++
++#include <media/v4l2-mem2mem.h>
++#include <media/videobuf2-dma-contig.h>
++
++#include "codec_mpeg12.h"
++#include "codec_helpers.h"
++
++#define SIZE_WORKSPACE (2 * SZ_64K)
++#define SIZE_CCBUF (5 * SZ_1K)
++
++/* DOS registers */
++#define ASSIST_MBOX1_CLR_REG 0x01d4
++#define ASSIST_MBOX1_MASK 0x01d8
++
++#define PSCALE_CTRL 0x2444
++
++#define MDEC_PIC_DC_CTRL 0x2638
++
++#define AV_SCRATCH_0 0x2700
++#define MREG_SEQ_INFO 0x2710
++#define MREG_PIC_INFO 0x2714
++#define MREG_PIC_WIDTH 0x2718
++#define MREG_PIC_HEIGHT 0x271c
++#define MREG_BUFFERIN 0x2720
++#define MREG_BUFFEROUT 0x2724
++#define MREG_CMD 0x2728
++#define MREG_CO_MV_START 0x272c
++#define MREG_ERROR_COUNT 0x2730
++#define MREG_FRAME_OFFSET 0x2734
++#define MREG_WAIT_BUFFER 0x2738
++#define MREG_FATAL_ERROR 0x273c
++
++#define MPEG1_2_REG 0x3004
++#define PIC_HEAD_INFO 0x300c
++#define POWER_CTL_VLD 0x3020
++#define M4_CONTROL_REG 0x30a4
++
++#define DOS_SW_RESET0 0xfc00
++
++struct codec_mpeg12 {
++ /* Buffer for the MPEG1/2 Workspace */
++ void *workspace_vaddr;
++ dma_addr_t workspace_paddr;
++
++ /* Housekeeping thread for recycling buffers into the hardware */
++ struct task_struct *buffers_thread;
++};
++
++static int codec_mpeg12_buffers_thread(void *data)
++{
++ struct vdec_buffer *tmp;
++ struct vdec_session *sess = data;
++ struct vdec_core *core = sess->core;;
++
++ while (!kthread_should_stop()) {
++ mutex_lock(&sess->bufs_recycle_lock);
++ while (!list_empty(&sess->bufs_recycle) &&
++ !readl_relaxed(core->dos_base + MREG_BUFFERIN))
++ {
++ tmp = list_first_entry(&sess->bufs_recycle, struct vdec_buffer, list);
++
++ /* Tell the decoder he can recycle this buffer */
++ writel_relaxed(tmp->index + 1, core->dos_base + MREG_BUFFERIN);
++
++ list_del(&tmp->list);
++ kfree(tmp);
++ }
++ mutex_unlock(&sess->bufs_recycle_lock);
++
++ usleep_range(5000, 10000);
++ }
++
++ return 0;
++}
++
++static int codec_mpeg12_start(struct vdec_session *sess) {
++ struct vdec_core *core = sess->core;
++ struct codec_mpeg12 *mpeg12 = sess->priv;
++ int ret;
++
++ mpeg12 = kzalloc(sizeof(*mpeg12), GFP_KERNEL);
++ if (!mpeg12)
++ return -ENOMEM;
++
++ sess->priv = mpeg12;
++
++ /* Allocate some memory for the MPEG1/2 decoder's state */
++ mpeg12->workspace_vaddr = dma_alloc_coherent(core->dev, SIZE_WORKSPACE, &mpeg12->workspace_paddr, GFP_KERNEL);
++ if (!mpeg12->workspace_vaddr) {
++ dev_err(core->dev, "Failed to request MPEG 1/2 Workspace\n");
++ ret = -ENOMEM;
++ goto free_mpeg12;
++ }
++
++ writel_relaxed((1<<9) | (1<<8) | (1<<7) | (1<<6) | (1<<4), core->dos_base + DOS_SW_RESET0);
++ writel_relaxed(0, core->dos_base + DOS_SW_RESET0);
++ readl_relaxed(core->dos_base + DOS_SW_RESET0);
++
++ writel_relaxed((1 << 4), core->dos_base + POWER_CTL_VLD);
++
++ codec_helper_set_canvases(sess, core->dos_base + AV_SCRATCH_0);
++ writel_relaxed(mpeg12->workspace_paddr + SIZE_CCBUF, core->dos_base + MREG_CO_MV_START);
++
++ writel_relaxed(0, core->dos_base + MPEG1_2_REG);
++ writel_relaxed(0, core->dos_base + PSCALE_CTRL);
++ writel_relaxed(0x380, core->dos_base + PIC_HEAD_INFO);
++ writel_relaxed(0, core->dos_base + M4_CONTROL_REG);
++ writel_relaxed(1, core->dos_base + ASSIST_MBOX1_CLR_REG);
++ writel_relaxed(0, core->dos_base + MREG_BUFFERIN);
++ writel_relaxed(0, core->dos_base + MREG_BUFFEROUT);
++ writel_relaxed((sess->width << 16) | sess->height, core->dos_base + MREG_CMD);
++ writel_relaxed(0, core->dos_base + MREG_ERROR_COUNT);
++ writel_relaxed(0, core->dos_base + MREG_FATAL_ERROR);
++ writel_relaxed(0, core->dos_base + MREG_WAIT_BUFFER);
++
++ /* Enable NV21 */
++ writel_relaxed(readl_relaxed(core->dos_base + MDEC_PIC_DC_CTRL) | (1 << 17), core->dos_base + MDEC_PIC_DC_CTRL);
++
++ mpeg12->buffers_thread = kthread_run(codec_mpeg12_buffers_thread, sess, "buffers_done");
++
++ return 0;
++
++free_mpeg12:
++ kfree(mpeg12);
++ return ret;
++}
++
++static int codec_mpeg12_stop(struct vdec_session *sess)
++{
++ struct codec_mpeg12 *mpeg12 = sess->priv;
++ struct vdec_core *core = sess->core;
++
++ kthread_stop(mpeg12->buffers_thread);
++
++ if (mpeg12->workspace_vaddr) {
++ dma_free_coherent(core->dev, SIZE_WORKSPACE, mpeg12->workspace_vaddr, mpeg12->workspace_paddr);
++ mpeg12->workspace_vaddr = 0;
++ }
++
++ kfree(mpeg12);
++ sess->priv = 0;
++
++ return 0;
++}
++
++static irqreturn_t codec_mpeg12_isr(struct vdec_session *sess)
++{
++ u32 reg;
++ u32 buffer_index;
++ struct vdec_core *core = sess->core;
++
++ writel_relaxed(1, core->dos_base + ASSIST_MBOX1_CLR_REG);
++
++ reg = readl_relaxed(core->dos_base + MREG_FATAL_ERROR);
++ if (reg == 1)
++ dev_err(core->dev, "MPEG12 fatal error\n");
++
++ reg = readl_relaxed(core->dos_base + MREG_BUFFEROUT);
++ if (!reg)
++ return IRQ_HANDLED;
++
++ if ((reg >> 16) & 0xfe)
++ goto end;
++
++ buffer_index = ((reg & 0xf) - 1) & 7;
++ vdec_dst_buf_done_idx(sess, buffer_index);
++
++end:
++ writel_relaxed(0, core->dos_base + MREG_BUFFEROUT);
++ return IRQ_HANDLED;
++}
++
++struct vdec_codec_ops codec_mpeg12_ops = {
++ .start = codec_mpeg12_start,
++ .stop = codec_mpeg12_stop,
++ .isr = codec_mpeg12_isr,
++ .notify_dst_buffer = vdec_queue_recycle,
++};
++
+diff --git a/drivers/media/platform/meson/vdec/codec_mpeg12.h b/drivers/media/platform/meson/vdec/codec_mpeg12.h
+new file mode 100644
+index 0000000..7dc37ad
+--- /dev/null
++++ b/drivers/media/platform/meson/vdec/codec_mpeg12.h
+@@ -0,0 +1,13 @@
++/* SPDX-License-Identifier: GPL-2.0+ */
++/*
++ * Copyright (C) 2018 Maxime Jourdan <maxi.jourdan@wanadoo.fr>
++ */
++
++#ifndef __MESON_VDEC_CODEC_MPEG12_H_
++#define __MESON_VDEC_CODEC_MPEG12_H_
++
++#include "vdec.h"
++
++extern struct vdec_codec_ops codec_mpeg12_ops;
++
++#endif
+\ No newline at end of file
+diff --git a/drivers/media/platform/meson/vdec/codec_mpeg4.c b/drivers/media/platform/meson/vdec/codec_mpeg4.c
+new file mode 100644
+index 0000000..739b97e
+--- /dev/null
++++ b/drivers/media/platform/meson/vdec/codec_mpeg4.c
+@@ -0,0 +1,213 @@
++// SPDX-License-Identifier: GPL-2.0+
++/*
++ * Copyright (C) 2018 Maxime Jourdan <maxi.jourdan@wanadoo.fr>
++ */
++
++#include <media/v4l2-mem2mem.h>
++#include <media/videobuf2-dma-contig.h>
++
++#include "codec_mpeg4.h"
++#include "codec_helpers.h"
++#include "canvas.h"
++
++#define SIZE_WORKSPACE (1 * SZ_1M)
++#define DCAC_BUFF_START_IP 0x02b00000
++
++/* DOS registers */
++#define ASSIST_MBOX1_CLR_REG 0x01d4
++#define ASSIST_MBOX1_MASK 0x01d8
++
++#define PSCALE_CTRL 0x2444
++
++#define MDEC_PIC_DC_CTRL 0x2638
++#define MDEC_PIC_DC_THRESH 0x26e0
++
++#define AV_SCRATCH_0 0x2700
++#define MP4_PIC_RATIO 0x2714
++#define MP4_RATE 0x270c
++#define AV_SCRATCH_4 0x2710
++#define MP4_ERR_COUNT 0x2718
++#define MP4_PIC_WH 0x271c
++#define MREG_BUFFERIN 0x2720
++#define MREG_BUFFEROUT 0x2724
++#define MP4_NOT_CODED_CNT 0x2728
++#define MP4_VOP_TIME_INC 0x272c
++#define MP4_OFFSET_REG 0x2730
++#define MP4_SYS_RATE 0x2738
++#define MEM_OFFSET_REG 0x273c
++#define AV_SCRATCH_G 0x2740
++#define MREG_FATAL_ERROR 0x2754
++
++#define DOS_SW_RESET0 0xfc00
++
++struct codec_mpeg4 {
++ /* Buffer for the MPEG1/2 Workspace */
++ void *workspace_vaddr;
++ dma_addr_t workspace_paddr;
++
++ /* Housekeeping thread for recycling buffers into the hardware */
++ struct task_struct *buffers_thread;
++};
++
++static int codec_mpeg4_buffers_thread(void *data)
++{
++ struct vdec_buffer *tmp;
++ struct vdec_session *sess = data;
++ struct vdec_core *core = sess->core;;
++
++ while (!kthread_should_stop()) {
++ mutex_lock(&sess->bufs_recycle_lock);
++ while (!list_empty(&sess->bufs_recycle) &&
++ !readl_relaxed(core->dos_base + MREG_BUFFERIN))
++ {
++ tmp = list_first_entry(&sess->bufs_recycle, struct vdec_buffer, list);
++
++ /* Tell the decoder he can recycle this buffer */
++ writel_relaxed(~(1 << tmp->index), core->dos_base + MREG_BUFFERIN);
++
++ list_del(&tmp->list);
++ kfree(tmp);
++ }
++ mutex_unlock(&sess->bufs_recycle_lock);
++
++ usleep_range(5000, 10000);
++ }
++
++ return 0;
++}
++
++/* The MPEG4 canvas regs are not contiguous,
++ * handle it specifically instead of using the helper
++ * AV_SCRATCH_0 - AV_SCRATCH_3 ; AV_SCRATCH_G - AV_SCRATCH_J
++ */
++void codec_mpeg4_set_canvases(struct vdec_session *sess) {
++ struct v4l2_m2m_buffer *buf;
++ struct vdec_core *core = sess->core;
++ void *current_reg = core->dos_base + AV_SCRATCH_0;
++ u32 width = ALIGN(sess->width, 64);
++ u32 height = ALIGN(sess->height, 64);
++
++ /* Setup NV12 canvases for Decoded Picture Buffer (dpb)
++ * Map them to the user buffers' planes
++ */
++ v4l2_m2m_for_each_dst_buf(sess->m2m_ctx, buf) {
++ u32 buf_idx = buf->vb.vb2_buf.index;
++ u32 cnv_y_idx = buf_idx * 2;
++ u32 cnv_uv_idx = buf_idx * 2 + 1;
++ dma_addr_t buf_y_paddr =
++ vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
++ dma_addr_t buf_uv_paddr =
++ vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 1);
++
++ /* Y plane */
++ vdec_canvas_setup(core->dmc_base, cnv_y_idx, buf_y_paddr, width, height, MESON_CANVAS_WRAP_NONE, MESON_CANVAS_BLKMODE_LINEAR);
++
++ /* U/V plane */
++ vdec_canvas_setup(core->dmc_base, cnv_uv_idx, buf_uv_paddr, width, height / 2, MESON_CANVAS_WRAP_NONE, MESON_CANVAS_BLKMODE_LINEAR);
++
++ writel_relaxed(((cnv_uv_idx) << 16) |
++ ((cnv_uv_idx) << 8) |
++ (cnv_y_idx), current_reg);
++
++ current_reg += 4;
++ if (current_reg == core->dos_base + AV_SCRATCH_4)
++ current_reg = core->dos_base + AV_SCRATCH_G;
++ }
++}
++
++static int codec_mpeg4_start(struct vdec_session *sess) {
++ struct vdec_core *core = sess->core;
++ struct codec_mpeg4 *mpeg4 = sess->priv;
++ int ret;
++
++ mpeg4 = kzalloc(sizeof(*mpeg4), GFP_KERNEL);
++ if (!mpeg4)
++ return -ENOMEM;
++
++ sess->priv = mpeg4;
++
++ /* Allocate some memory for the MPEG4 decoder's state */
++ mpeg4->workspace_vaddr = dma_alloc_coherent(core->dev, SIZE_WORKSPACE, &mpeg4->workspace_paddr, GFP_KERNEL);
++ if (!mpeg4->workspace_vaddr) {
++ dev_err(core->dev, "Failed to request MPEG4 Workspace\n");
++ ret = -ENOMEM;
++ goto free_mpeg4;
++ }
++
++ writel_relaxed((1<<7) | (1<<6), core->dos_base + DOS_SW_RESET0);
++ writel_relaxed(0, core->dos_base + DOS_SW_RESET0);
++ readl_relaxed(core->dos_base + DOS_SW_RESET0);
++
++ codec_mpeg4_set_canvases(sess);
++
++ writel_relaxed(mpeg4->workspace_paddr - DCAC_BUFF_START_IP, core->dos_base + MEM_OFFSET_REG);
++ writel_relaxed(0, core->dos_base + PSCALE_CTRL);
++ writel_relaxed(0, core->dos_base + MP4_NOT_CODED_CNT);
++ writel_relaxed(0, core->dos_base + MREG_BUFFERIN);
++ writel_relaxed(0, core->dos_base + MREG_BUFFEROUT);
++ writel_relaxed(0, core->dos_base + MREG_FATAL_ERROR);
++ writel_relaxed(1, core->dos_base + ASSIST_MBOX1_CLR_REG);
++ writel_relaxed(1, core->dos_base + ASSIST_MBOX1_MASK);
++ writel_relaxed(0x404038aa, core->dos_base + MDEC_PIC_DC_THRESH);
++
++ /* Enable NV21 */
++ writel_relaxed(readl_relaxed(core->dos_base + MDEC_PIC_DC_CTRL) | (1 << 17), core->dos_base + MDEC_PIC_DC_CTRL);
++
++ mpeg4->buffers_thread = kthread_run(codec_mpeg4_buffers_thread, sess, "buffers_done");
++
++ return 0;
++
++free_mpeg4:
++ kfree(mpeg4);
++ return ret;
++}
++
++static int codec_mpeg4_stop(struct vdec_session *sess)
++{
++ struct codec_mpeg4 *mpeg4 = sess->priv;
++ struct vdec_core *core = sess->core;
++
++ kthread_stop(mpeg4->buffers_thread);
++
++ if (mpeg4->workspace_vaddr) {
++ dma_free_coherent(core->dev, SIZE_WORKSPACE, mpeg4->workspace_vaddr, mpeg4->workspace_paddr);
++ mpeg4->workspace_vaddr = 0;
++ }
++
++ kfree(mpeg4);
++ sess->priv = 0;
++
++ return 0;
++}
++
++static irqreturn_t codec_mpeg4_isr(struct vdec_session *sess)
++{
++ u32 reg;
++ u32 buffer_index;
++ struct vdec_core *core = sess->core;
++
++ reg = readl_relaxed(core->dos_base + MREG_FATAL_ERROR);
++ if (reg == 1)
++ dev_err(core->dev, "mpeg4 fatal error\n");
++
++ reg = readl_relaxed(core->dos_base + MREG_BUFFEROUT);
++ if (reg) {
++ readl_relaxed(core->dos_base + MP4_NOT_CODED_CNT);
++ readl_relaxed(core->dos_base + MP4_VOP_TIME_INC);
++ buffer_index = reg & 0x7;
++ vdec_dst_buf_done_idx(sess, buffer_index);
++ writel_relaxed(0, core->dos_base + MREG_BUFFEROUT);
++ }
++
++ writel_relaxed(1, core->dos_base + ASSIST_MBOX1_CLR_REG);
++
++ return IRQ_HANDLED;
++}
++
++struct vdec_codec_ops codec_mpeg4_ops = {
++ .start = codec_mpeg4_start,
++ .stop = codec_mpeg4_stop,
++ .isr = codec_mpeg4_isr,
++ .notify_dst_buffer = vdec_queue_recycle,
++};
++
+diff --git a/drivers/media/platform/meson/vdec/codec_mpeg4.h b/drivers/media/platform/meson/vdec/codec_mpeg4.h
+new file mode 100644
+index 0000000..a30ceed
+--- /dev/null
++++ b/drivers/media/platform/meson/vdec/codec_mpeg4.h
+@@ -0,0 +1,13 @@
++/* SPDX-License-Identifier: GPL-2.0+ */
++/*
++ * Copyright (C) 2018 Maxime Jourdan <maxi.jourdan@wanadoo.fr>
++ */
++
++#ifndef __MESON_VDEC_CODEC_MPEG4_H_
++#define __MESON_VDEC_CODEC_MPEG4_H_
++
++#include "vdec.h"
++
++extern struct vdec_codec_ops codec_mpeg4_ops;
++
++#endif
+\ No newline at end of file
+diff --git a/drivers/media/platform/meson/vdec/esparser.c b/drivers/media/platform/meson/vdec/esparser.c
+new file mode 100644
+index 0000000..400d6d5
+--- /dev/null
++++ b/drivers/media/platform/meson/vdec/esparser.c
+@@ -0,0 +1,320 @@
++// SPDX-License-Identifier: GPL-2.0+
++/*
++ * Copyright (C) 2018 Maxime Jourdan <maxi.jourdan@wanadoo.fr>
++ */
++
++#include <linux/init.h>
++#include <linux/ioctl.h>
++#include <linux/list.h>
++#include <linux/module.h>
++#include <linux/of_device.h>
++#include <linux/platform_device.h>
++#include <linux/reset.h>
++#include <media/videobuf2-dma-contig.h>
++#include <media/v4l2-mem2mem.h>
++
++#include "esparser.h"
++
++/* PARSER REGS (CBUS) */
++#define PARSER_CONTROL 0x00
++ #define ES_PACK_SIZE_BIT 8
++ #define ES_WRITE BIT(5)
++ #define ES_SEARCH BIT(1)
++ #define ES_PARSER_START BIT(0)
++#define PARSER_FETCH_ADDR 0x4
++#define PARSER_FETCH_CMD 0x8
++#define PARSER_CONFIG 0x14
++ #define PS_CFG_MAX_FETCH_CYCLE_BIT 0
++ #define PS_CFG_STARTCODE_WID_24_BIT 10
++ #define PS_CFG_MAX_ES_WR_CYCLE_BIT 12
++ #define PS_CFG_PFIFO_EMPTY_CNT_BIT 16
++#define PFIFO_WR_PTR 0x18
++#define PFIFO_RD_PTR 0x1c
++#define PARSER_SEARCH_PATTERN 0x24
++ #define ES_START_CODE_PATTERN 0x00000100
++#define PARSER_SEARCH_MASK 0x28
++ #define ES_START_CODE_MASK 0xffffff00
++ #define FETCH_ENDIAN_BIT 27
++#define PARSER_INT_ENABLE 0x2c
++ #define PARSER_INT_HOST_EN_BIT 8
++#define PARSER_INT_STATUS 0x30
++ #define PARSER_INTSTAT_SC_FOUND 1
++#define PARSER_ES_CONTROL 0x5c
++#define PARSER_VIDEO_START_PTR 0x80
++#define PARSER_VIDEO_END_PTR 0x84
++#define PARSER_VIDEO_HOLE 0x90
++
++/* STBUF regs */
++#define VLD_MEM_VIFIFO_BUF_CNTL 0x3120
++ #define MEM_BUFCTRL_MANUAL BIT(1)
++
++#define SEARCH_PATTERN_LEN 512
++
++static DECLARE_WAIT_QUEUE_HEAD(wq);
++static int search_done;
++
++/* Buffer to send to the ESPARSER to signal End Of Stream.
++ * Credits to Endless Mobile.
++ */
++#define EOS_TAIL_BUF_SIZE 1024
++static const u8 eos_tail_data[] = {
++ 0x00, 0x00, 0x00, 0x01, 0x06, 0x05, 0xff, 0xe4, 0xdc, 0x45, 0xe9, 0xbd, 0xe6, 0xd9, 0x48, 0xb7,
++ 0x96, 0x2c, 0xd8, 0x20, 0xd9, 0x23, 0xee, 0xef, 0x78, 0x32, 0x36, 0x34, 0x20, 0x2d, 0x20, 0x63,
++ 0x6f, 0x72, 0x65, 0x20, 0x36, 0x37, 0x20, 0x72, 0x31, 0x31, 0x33, 0x30, 0x20, 0x38, 0x34, 0x37,
++ 0x35, 0x39, 0x37, 0x37, 0x20, 0x2d, 0x20, 0x48, 0x2e, 0x32, 0x36, 0x34, 0x2f, 0x4d, 0x50, 0x45,
++ 0x47, 0x2d, 0x34, 0x20, 0x41, 0x56, 0x43, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x63, 0x20, 0x2d, 0x20,
++ 0x43, 0x6f, 0x70, 0x79, 0x6c, 0x65, 0x66, 0x74, 0x20, 0x32, 0x30, 0x30, 0x33, 0x2d, 0x32, 0x30,
++ 0x30, 0x39, 0x20, 0x2d, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e,
++ 0x76, 0x69, 0x64, 0x65, 0x6f, 0x6c, 0x61, 0x6e, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x78, 0x32, 0x36,
++ 0x34, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x20, 0x2d, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73,
++ 0x3a, 0x20, 0x63, 0x61, 0x62, 0x61, 0x63, 0x3d, 0x31, 0x20, 0x72, 0x65, 0x66, 0x3d, 0x31, 0x20,
++ 0x64, 0x65, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3d, 0x31, 0x3a, 0x30, 0x3a, 0x30, 0x20, 0x61, 0x6e,
++ 0x61, 0x6c, 0x79, 0x73, 0x65, 0x3d, 0x30, 0x78, 0x31, 0x3a, 0x30, 0x78, 0x31, 0x31, 0x31, 0x20,
++ 0x6d, 0x65, 0x3d, 0x68, 0x65, 0x78, 0x20, 0x73, 0x75, 0x62, 0x6d, 0x65, 0x3d, 0x36, 0x20, 0x70,
++ 0x73, 0x79, 0x5f, 0x72, 0x64, 0x3d, 0x31, 0x2e, 0x30, 0x3a, 0x30, 0x2e, 0x30, 0x20, 0x6d, 0x69,
++ 0x78, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x66, 0x3d, 0x30, 0x20, 0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e,
++ 0x67, 0x65, 0x3d, 0x31, 0x36, 0x20, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x61, 0x5f, 0x6d, 0x65, 0x3d,
++ 0x31, 0x20, 0x74, 0x72, 0x65, 0x6c, 0x6c, 0x69, 0x73, 0x3d, 0x30, 0x20, 0x38, 0x78, 0x38, 0x64,
++ 0x63, 0x74, 0x3d, 0x30, 0x20, 0x63, 0x71, 0x6d, 0x3d, 0x30, 0x20, 0x64, 0x65, 0x61, 0x64, 0x7a,
++ 0x6f, 0x6e, 0x65, 0x3d, 0x32, 0x31, 0x2c, 0x31, 0x31, 0x20, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x61,
++ 0x5f, 0x71, 0x70, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x3d, 0x2d, 0x32, 0x20, 0x74, 0x68,
++ 0x72, 0x65, 0x61, 0x64, 0x73, 0x3d, 0x31, 0x20, 0x6e, 0x72, 0x3d, 0x30, 0x20, 0x64, 0x65, 0x63,
++ 0x69, 0x6d, 0x61, 0x74, 0x65, 0x3d, 0x31, 0x20, 0x6d, 0x62, 0x61, 0x66, 0x66, 0x3d, 0x30, 0x20,
++ 0x62, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x3d, 0x30, 0x20, 0x6b, 0x65, 0x79, 0x69, 0x6e, 0x74,
++ 0x3d, 0x32, 0x35, 0x30, 0x20, 0x6b, 0x65, 0x79, 0x69, 0x6e, 0x74, 0x5f, 0x6d, 0x69, 0x6e, 0x3d,
++ 0x32, 0x35, 0x20, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x63, 0x75, 0x74, 0x3d, 0x34, 0x30, 0x20, 0x72,
++ 0x63, 0x3d, 0x61, 0x62, 0x72, 0x20, 0x62, 0x69, 0x74, 0x72, 0x61, 0x74, 0x65, 0x3d, 0x31, 0x30,
++ 0x20, 0x72, 0x61, 0x74, 0x65, 0x74, 0x6f, 0x6c, 0x3d, 0x31, 0x2e, 0x30, 0x20, 0x71, 0x63, 0x6f,
++ 0x6d, 0x70, 0x3d, 0x30, 0x2e, 0x36, 0x30, 0x20, 0x71, 0x70, 0x6d, 0x69, 0x6e, 0x3d, 0x31, 0x30,
++ 0x20, 0x71, 0x70, 0x6d, 0x61, 0x78, 0x3d, 0x35, 0x31, 0x20, 0x71, 0x70, 0x73, 0x74, 0x65, 0x70,
++ 0x3d, 0x34, 0x20, 0x69, 0x70, 0x5f, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x3d, 0x31, 0x2e, 0x34, 0x30,
++ 0x20, 0x61, 0x71, 0x3d, 0x31, 0x3a, 0x31, 0x2e, 0x30, 0x30, 0x00, 0x80, 0x00, 0x00, 0x00, 0x01,
++ 0x67, 0x4d, 0x40, 0x0a, 0x9a, 0x74, 0xf4, 0x20, 0x00, 0x00, 0x03, 0x00, 0x20, 0x00, 0x00, 0x06,
++ 0x51, 0xe2, 0x44, 0xd4, 0x00, 0x00, 0x00, 0x01, 0x68, 0xee, 0x32, 0xc8, 0x00, 0x00, 0x00, 0x01,
++ 0x65, 0x88, 0x80, 0x20, 0x00, 0x08, 0x7f, 0xea, 0x6a, 0xe2, 0x99, 0xb6, 0x57, 0xae, 0x49, 0x30,
++ 0xf5, 0xfe, 0x5e, 0x46, 0x0b, 0x72, 0x44, 0xc4, 0xe1, 0xfc, 0x62, 0xda, 0xf1, 0xfb, 0xa2, 0xdb,
++ 0xd6, 0xbe, 0x5c, 0xd7, 0x24, 0xa3, 0xf5, 0xb9, 0x2f, 0x57, 0x16, 0x49, 0x75, 0x47, 0x77, 0x09,
++ 0x5c, 0xa1, 0xb4, 0xc3, 0x4f, 0x60, 0x2b, 0xb0, 0x0c, 0xc8, 0xd6, 0x66, 0xba, 0x9b, 0x82, 0x29,
++ 0x33, 0x92, 0x26, 0x99, 0x31, 0x1c, 0x7f, 0x9b
++};
++
++static irqreturn_t esparser_isr(int irq, void *dev)
++{
++ int int_status;
++ struct vdec_core *core = dev;
++
++ int_status = readl_relaxed(core->esparser_base + PARSER_INT_STATUS);
++ writel_relaxed(int_status, core->esparser_base + PARSER_INT_STATUS);
++
++ dev_dbg(core->dev, "esparser_isr, status = %08X\n", int_status);
++
++ if (int_status & PARSER_INTSTAT_SC_FOUND) {
++ writel_relaxed(0, core->esparser_base + PFIFO_RD_PTR);
++ writel_relaxed(0, core->esparser_base + PFIFO_WR_PTR);
++ search_done = 1;
++ wake_up_interruptible(&wq);
++ }
++
++ return IRQ_HANDLED;
++}
++
++/* Add a start code at the end of the buffer
++ * to trigger the esparser interrupt
++ */
++static void esparser_append_start_code(struct vb2_buffer *vb)
++{
++ u8 *vaddr = vb2_plane_vaddr(vb, 0) + vb2_get_plane_payload(vb, 0);
++
++ vaddr[0] = 0x00;
++ vaddr[1] = 0x00;
++ vaddr[2] = 0x01;
++ vaddr[3] = 0xff;
++}
++
++static int
++esparser_write_data(struct vdec_core *core, dma_addr_t addr, u32 size)
++{
++ writel_relaxed(0, core->esparser_base + PFIFO_RD_PTR);
++ writel_relaxed(0, core->esparser_base + PFIFO_WR_PTR);
++ writel_relaxed(ES_WRITE | ES_PARSER_START | ES_SEARCH | (size << ES_PACK_SIZE_BIT), core->esparser_base + PARSER_CONTROL);
++
++ writel_relaxed(addr, core->esparser_base + PARSER_FETCH_ADDR);
++ writel_relaxed((7 << FETCH_ENDIAN_BIT) | (size + 512), core->esparser_base + PARSER_FETCH_CMD);
++ search_done = 0;
++
++ return wait_event_interruptible_timeout(wq, search_done != 0, HZ/5);
++}
++
++static u32 esparser_vififo_get_free_space(struct vdec_session *sess)
++{
++ u32 vififo_usage;
++ struct vdec_ops *vdec_ops = sess->fmt_out->vdec_ops;
++ struct vdec_core *core = sess->core;
++
++ vififo_usage = vdec_ops->vififo_level(sess);
++ vififo_usage += readl_relaxed(core->esparser_base + PARSER_VIDEO_HOLE);
++ vififo_usage += (6 * SZ_1K);
++
++ if (vififo_usage > sess->vififo_size) {
++ dev_warn(sess->core->dev,
++ "VIFIFO usage (%u) > VIFIFO size (%u)\n",
++ vififo_usage, sess->vififo_size);
++ return 0;
++ }
++
++ return sess->vififo_size - vififo_usage;
++}
++
++int esparser_queue_eos(struct vdec_session *sess)
++{
++ struct device *dev = sess->core->dev;
++ struct vdec_core *core = sess->core;
++ void *eos_vaddr;
++ dma_addr_t eos_paddr;
++ int ret;
++
++ eos_vaddr = dma_alloc_coherent(dev, EOS_TAIL_BUF_SIZE + 512, &eos_paddr, GFP_KERNEL);
++ if (!eos_vaddr)
++ return -ENOMEM;
++
++ sess->should_stop = 1;
++
++ memcpy(eos_vaddr, eos_tail_data, sizeof(eos_tail_data));
++ ret = esparser_write_data(core, eos_paddr, EOS_TAIL_BUF_SIZE);
++ dma_free_coherent(dev, EOS_TAIL_BUF_SIZE + 512,
++ eos_vaddr, eos_paddr);
++
++ return ret;
++}
++
++static int esparser_queue(struct vdec_session *sess, struct vb2_v4l2_buffer *vbuf)
++{
++ int ret;
++ struct vb2_buffer *vb = &vbuf->vb2_buf;
++ struct vdec_core *core = sess->core;
++ struct vdec_codec_ops *codec_ops = sess->fmt_out->codec_ops;
++ u32 num_dst_bufs = v4l2_m2m_num_dst_bufs_ready(sess->m2m_ctx);
++ u32 payload_size = vb2_get_plane_payload(vb, 0);
++ dma_addr_t phy = vb2_dma_contig_plane_dma_addr(vb, 0);
++
++ if (!payload_size) {
++ esparser_queue_eos(sess);
++ return 0;
++ }
++
++ if (codec_ops->num_pending_bufs)
++ num_dst_bufs += codec_ops->num_pending_bufs(sess);
++
++ if (esparser_vififo_get_free_space(sess) < payload_size ||
++ atomic_read(&sess->esparser_queued_bufs) >= num_dst_bufs)
++ return -EAGAIN;
++
++ v4l2_m2m_src_buf_remove_by_buf(sess->m2m_ctx, vbuf);
++ vdec_add_ts_reorder(sess, vb->timestamp);
++
++ esparser_append_start_code(vb);
++ ret = esparser_write_data(core, phy, payload_size);
++
++ if (ret > 0) {
++ vbuf->flags = 0;
++ vbuf->field = V4L2_FIELD_NONE;
++ v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
++ } else if (ret <= 0) {
++ printk("ESPARSER input parsing error\n");
++ vdec_remove_ts(sess, vb->timestamp);
++ v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
++ writel_relaxed(0, core->esparser_base + PARSER_FETCH_CMD);
++ }
++
++ if (vbuf->flags & V4L2_BUF_FLAG_LAST)
++ esparser_queue_eos(sess);
++
++ return 0;
++}
++
++void esparser_queue_all_src(struct work_struct *work)
++{
++ struct v4l2_m2m_buffer *buf, *n;
++ struct vdec_session *sess =
++ container_of(work, struct vdec_session, esparser_queue_work);
++
++ mutex_lock(&sess->lock);
++ v4l2_m2m_for_each_src_buf_safe(sess->m2m_ctx, buf, n) {
++ if (esparser_queue(sess, &buf->vb) < 0)
++ break;
++
++ atomic_inc(&sess->esparser_queued_bufs);
++ }
++ mutex_unlock(&sess->lock);
++}
++
++int esparser_power_up(struct vdec_session *sess)
++{
++ struct vdec_core *core = sess->core;
++ struct vdec_ops *vdec_ops = sess->fmt_out->vdec_ops;
++
++ reset_control_reset(core->esparser_reset);
++ writel_relaxed((10 << PS_CFG_PFIFO_EMPTY_CNT_BIT) |
++ (1 << PS_CFG_MAX_ES_WR_CYCLE_BIT) |
++ (16 << PS_CFG_MAX_FETCH_CYCLE_BIT),
++ core->esparser_base + PARSER_CONFIG);
++
++ writel_relaxed(0, core->esparser_base + PFIFO_RD_PTR);
++ writel_relaxed(0, core->esparser_base + PFIFO_WR_PTR);
++
++ writel_relaxed(ES_START_CODE_PATTERN, core->esparser_base + PARSER_SEARCH_PATTERN);
++ writel_relaxed(ES_START_CODE_MASK, core->esparser_base + PARSER_SEARCH_MASK);
++
++ writel_relaxed((10 << PS_CFG_PFIFO_EMPTY_CNT_BIT) |
++ (1 << PS_CFG_MAX_ES_WR_CYCLE_BIT) |
++ (16 << PS_CFG_MAX_FETCH_CYCLE_BIT) |
++ (2 << PS_CFG_STARTCODE_WID_24_BIT),
++ core->esparser_base + PARSER_CONFIG);
++
++ writel_relaxed((ES_SEARCH | ES_PARSER_START), core->esparser_base + PARSER_CONTROL);
++
++ writel_relaxed(sess->vififo_paddr, core->esparser_base + PARSER_VIDEO_START_PTR);
++ writel_relaxed(sess->vififo_paddr + sess->vififo_size - 8, core->esparser_base + PARSER_VIDEO_END_PTR);
++ writel_relaxed(readl_relaxed(core->esparser_base + PARSER_ES_CONTROL) & ~1, core->esparser_base + PARSER_ES_CONTROL);
++
++ if (vdec_ops->conf_esparser)
++ vdec_ops->conf_esparser(sess);
++
++ writel_relaxed(0xffff, core->esparser_base + PARSER_INT_STATUS);
++ writel_relaxed(1 << PARSER_INT_HOST_EN_BIT, core->esparser_base + PARSER_INT_ENABLE);
++
++ return 0;
++}
++
++int esparser_init(struct platform_device *pdev, struct vdec_core *core)
++{
++ struct device *dev = &pdev->dev;
++ int ret;
++ int irq;
++
++ /* TODO: name the IRQs */
++ irq = platform_get_irq(pdev, 1);
++ if (irq < 0) {
++ dev_err(dev, "Failed getting ESPARSER IRQ from dtb\n");
++ return irq;
++ }
++
++ printk("Requesting IRQ %d\n", irq);
++
++ ret = devm_request_irq(dev, irq, esparser_isr,
++ IRQF_SHARED,
++ "esparserirq", core);
++ if (ret) {
++ dev_err(dev, "Failed requesting ESPARSER IRQ\n");
++ return ret;
++ }
++
++ core->esparser_reset = devm_reset_control_get_exclusive(dev,
++ "esparser");
++ if (IS_ERR(core->esparser_reset)) {
++ dev_err(dev, "Failed to get esparser_reset\n");
++ return PTR_ERR(core->esparser_reset);
++ }
++
++ return 0;
++}
+\ No newline at end of file
+diff --git a/drivers/media/platform/meson/vdec/esparser.h b/drivers/media/platform/meson/vdec/esparser.h
+new file mode 100644
+index 0000000..f9c8b31
+--- /dev/null
++++ b/drivers/media/platform/meson/vdec/esparser.h
+@@ -0,0 +1,16 @@
++/* SPDX-License-Identifier: GPL-2.0+ */
++/*
++ * Copyright (C) 2018 Maxime Jourdan <maxi.jourdan@wanadoo.fr>
++ */
++
++#ifndef __MESON_VDEC_ESPARSER_H_
++#define __MESON_VDEC_ESPARSER_H_
++
++#include "vdec.h"
++
++int esparser_init(struct platform_device *pdev, struct vdec_core *core);
++int esparser_power_up(struct vdec_session *sess);
++int esparser_queue_eos(struct vdec_session *sess);
++void esparser_queue_all_src(struct work_struct *work);
++
++#endif
+\ No newline at end of file
+diff --git a/drivers/media/platform/meson/vdec/hevc_regs.h b/drivers/media/platform/meson/vdec/hevc_regs.h
+new file mode 100644
+index 0000000..ae9b38e
+--- /dev/null
++++ b/drivers/media/platform/meson/vdec/hevc_regs.h
+@@ -0,0 +1,742 @@
++/* SPDX-License-Identifier: GPL-2.0+ */
++/*
++ * Copyright (C) 2015 Amlogic, Inc. All rights reserved.
++ */
++
++#ifndef HEVC_REGS_HEADERS__
++#define HEVC_REGS_HEADERS__
++/*add from M8M2*/
++#define HEVC_ASSIST_AFIFO_CTRL (0x3001 * 4)
++#define HEVC_ASSIST_AFIFO_CTRL1 (0x3002 * 4)
++#define HEVC_ASSIST_GCLK_EN (0x3003 * 4)
++#define HEVC_ASSIST_SW_RESET (0x3004 * 4)
++#define HEVC_ASSIST_AMR1_INT0 (0x3025 * 4)
++#define HEVC_ASSIST_AMR1_INT1 (0x3026 * 4)
++#define HEVC_ASSIST_AMR1_INT2 (0x3027 * 4)
++#define HEVC_ASSIST_AMR1_INT3 (0x3028 * 4)
++#define HEVC_ASSIST_AMR1_INT4 (0x3029 * 4)
++#define HEVC_ASSIST_AMR1_INT5 (0x302a * 4)
++#define HEVC_ASSIST_AMR1_INT6 (0x302b * 4)
++#define HEVC_ASSIST_AMR1_INT7 (0x302c * 4)
++#define HEVC_ASSIST_AMR1_INT8 (0x302d * 4)
++#define HEVC_ASSIST_AMR1_INT9 (0x302e * 4)
++#define HEVC_ASSIST_AMR1_INTA (0x302f * 4)
++#define HEVC_ASSIST_AMR1_INTB (0x3030 * 4)
++#define HEVC_ASSIST_AMR1_INTC (0x3031 * 4)
++#define HEVC_ASSIST_AMR1_INTD (0x3032 * 4)
++#define HEVC_ASSIST_AMR1_INTE (0x3033 * 4)
++#define HEVC_ASSIST_AMR1_INTF (0x3034 * 4)
++#define HEVC_ASSIST_AMR2_INT0 (0x3035 * 4)
++#define HEVC_ASSIST_AMR2_INT1 (0x3036 * 4)
++#define HEVC_ASSIST_AMR2_INT2 (0x3037 * 4)
++#define HEVC_ASSIST_AMR2_INT3 (0x3038 * 4)
++#define HEVC_ASSIST_AMR2_INT4 (0x3039 * 4)
++#define HEVC_ASSIST_AMR2_INT5 (0x303a * 4)
++#define HEVC_ASSIST_AMR2_INT6 (0x303b * 4)
++#define HEVC_ASSIST_AMR2_INT7 (0x303c * 4)
++#define HEVC_ASSIST_AMR2_INT8 (0x303d * 4)
++#define HEVC_ASSIST_AMR2_INT9 (0x303e * 4)
++#define HEVC_ASSIST_AMR2_INTA (0x303f * 4)
++#define HEVC_ASSIST_AMR2_INTB (0x3040 * 4)
++#define HEVC_ASSIST_AMR2_INTC (0x3041 * 4)
++#define HEVC_ASSIST_AMR2_INTD (0x3042 * 4)
++#define HEVC_ASSIST_AMR2_INTE (0x3043 * 4)
++#define HEVC_ASSIST_AMR2_INTF (0x3044 * 4)
++#define HEVC_ASSIST_MBX_SSEL (0x3045 * 4)
++#define HEVC_ASSIST_TIMER0_LO (0x3060 * 4)
++#define HEVC_ASSIST_TIMER0_HI (0x3061 * 4)
++#define HEVC_ASSIST_TIMER1_LO (0x3062 * 4)
++#define HEVC_ASSIST_TIMER1_HI (0x3063 * 4)
++#define HEVC_ASSIST_DMA_INT (0x3064 * 4)
++#define HEVC_ASSIST_DMA_INT_MSK (0x3065 * 4)
++#define HEVC_ASSIST_DMA_INT2 (0x3066 * 4)
++#define HEVC_ASSIST_DMA_INT_MSK2 (0x3067 * 4)
++#define HEVC_ASSIST_MBOX0_IRQ_REG (0x3070 * 4)
++#define HEVC_ASSIST_MBOX0_CLR_REG (0x3071 * 4)
++#define HEVC_ASSIST_MBOX0_MASK (0x3072 * 4)
++#define HEVC_ASSIST_MBOX0_FIQ_SEL (0x3073 * 4)
++#define HEVC_ASSIST_MBOX1_IRQ_REG (0x3074 * 4)
++#define HEVC_ASSIST_MBOX1_CLR_REG (0x3075 * 4)
++#define HEVC_ASSIST_MBOX1_MASK (0x3076 * 4)
++#define HEVC_ASSIST_MBOX1_FIQ_SEL (0x3077 * 4)
++#define HEVC_ASSIST_MBOX2_IRQ_REG (0x3078 * 4)
++#define HEVC_ASSIST_MBOX2_CLR_REG (0x3079 * 4)
++#define HEVC_ASSIST_MBOX2_MASK (0x307a * 4)
++#define HEVC_ASSIST_MBOX2_FIQ_SEL (0x307b * 4)
++#define HEVC_ASSIST_AXI_CTRL (0x307c * 4)
++#define HEVC_ASSIST_AXI_STATUS (0x307d * 4)
++#define HEVC_ASSIST_SCRATCH_0 (0x30c0 * 4)
++#define HEVC_ASSIST_SCRATCH_1 (0x30c1 * 4)
++#define HEVC_ASSIST_SCRATCH_2 (0x30c2 * 4)
++#define HEVC_ASSIST_SCRATCH_3 (0x30c3 * 4)
++#define HEVC_ASSIST_SCRATCH_4 (0x30c4 * 4)
++#define HEVC_ASSIST_SCRATCH_5 (0x30c5 * 4)
++#define HEVC_ASSIST_SCRATCH_6 (0x30c6 * 4)
++#define HEVC_ASSIST_SCRATCH_7 (0x30c7 * 4)
++#define HEVC_ASSIST_SCRATCH_8 (0x30c8 * 4)
++#define HEVC_ASSIST_SCRATCH_9 (0x30c9 * 4)
++#define HEVC_ASSIST_SCRATCH_A (0x30ca * 4)
++#define HEVC_ASSIST_SCRATCH_B (0x30cb * 4)
++#define HEVC_ASSIST_SCRATCH_C (0x30cc * 4)
++#define HEVC_ASSIST_SCRATCH_D (0x30cd * 4)
++#define HEVC_ASSIST_SCRATCH_E (0x30ce * 4)
++#define HEVC_ASSIST_SCRATCH_F (0x30cf * 4)
++#define HEVC_ASSIST_SCRATCH_G (0x30d0 * 4)
++#define HEVC_ASSIST_SCRATCH_H (0x30d1 * 4)
++#define HEVC_ASSIST_SCRATCH_I (0x30d2 * 4)
++#define HEVC_ASSIST_SCRATCH_J (0x30d3 * 4)
++#define HEVC_ASSIST_SCRATCH_K (0x30d4 * 4)
++#define HEVC_ASSIST_SCRATCH_L (0x30d5 * 4)
++#define HEVC_ASSIST_SCRATCH_M (0x30d6 * 4)
++#define HEVC_ASSIST_SCRATCH_N (0x30d7 * 4)
++#define HEVC_PARSER_VERSION (0x3100 * 4)
++#define HEVC_STREAM_CONTROL (0x3101 * 4)
++#define HEVC_STREAM_START_ADDR (0x3102 * 4)
++#define HEVC_STREAM_END_ADDR (0x3103 * 4)
++#define HEVC_STREAM_WR_PTR (0x3104 * 4)
++#define HEVC_STREAM_RD_PTR (0x3105 * 4)
++#define HEVC_STREAM_LEVEL (0x3106 * 4)
++#define HEVC_STREAM_FIFO_CTL (0x3107 * 4)
++#define HEVC_SHIFT_CONTROL (0x3108 * 4)
++#define HEVC_SHIFT_STARTCODE (0x3109 * 4)
++#define HEVC_SHIFT_EMULATECODE (0x310a * 4)
++#define HEVC_SHIFT_STATUS (0x310b * 4)
++#define HEVC_SHIFTED_DATA (0x310c * 4)
++#define HEVC_SHIFT_BYTE_COUNT (0x310d * 4)
++#define HEVC_SHIFT_COMMAND (0x310e * 4)
++#define HEVC_ELEMENT_RESULT (0x310f * 4)
++#define HEVC_CABAC_CONTROL (0x3110 * 4)
++#define HEVC_PARSER_SLICE_INFO (0x3111 * 4)
++#define HEVC_PARSER_CMD_WRITE (0x3112 * 4)
++#define HEVC_PARSER_CORE_CONTROL (0x3113 * 4)
++#define HEVC_PARSER_CMD_FETCH (0x3114 * 4)
++#define HEVC_PARSER_CMD_STATUS (0x3115 * 4)
++#define HEVC_PARSER_LCU_INFO (0x3116 * 4)
++#define HEVC_PARSER_HEADER_INFO (0x3117 * 4)
++#define HEVC_PARSER_RESULT_0 (0x3118 * 4)
++#define HEVC_PARSER_RESULT_1 (0x3119 * 4)
++#define HEVC_PARSER_RESULT_2 (0x311a * 4)
++#define HEVC_PARSER_RESULT_3 (0x311b * 4)
++#define HEVC_CABAC_TOP_INFO (0x311c * 4)
++#define HEVC_CABAC_TOP_INFO_2 (0x311d * 4)
++#define HEVC_CABAC_LEFT_INFO (0x311e * 4)
++#define HEVC_CABAC_LEFT_INFO_2 (0x311f * 4)
++#define HEVC_PARSER_INT_CONTROL (0x3120 * 4)
++#define HEVC_PARSER_INT_STATUS (0x3121 * 4)
++#define HEVC_PARSER_IF_CONTROL (0x3122 * 4)
++#define HEVC_PARSER_PICTURE_SIZE (0x3123 * 4)
++#define HEVC_PARSER_LCU_START (0x3124 * 4)
++#define HEVC_PARSER_HEADER_INFO2 (0x3125 * 4)
++#define HEVC_PARSER_QUANT_READ (0x3126 * 4)
++#define HEVC_PARSER_RESERVED_27 (0x3127 * 4)
++#define HEVC_PARSER_CMD_SKIP_0 (0x3128 * 4)
++#define HEVC_PARSER_CMD_SKIP_1 (0x3129 * 4)
++#define HEVC_PARSER_CMD_SKIP_2 (0x312a * 4)
++#define HEVC_PARSER_MANUAL_CMD (0x312b * 4)
++#define HEVC_PARSER_MEM_RD_ADDR (0x312c * 4)
++#define HEVC_PARSER_MEM_WR_ADDR (0x312d * 4)
++#define HEVC_PARSER_MEM_RW_DATA (0x312e * 4)
++#define HEVC_SAO_IF_STATUS (0x3130 * 4)
++#define HEVC_SAO_IF_DATA_Y (0x3131 * 4)
++#define HEVC_SAO_IF_DATA_U (0x3132 * 4)
++#define HEVC_SAO_IF_DATA_V (0x3133 * 4)
++#define HEVC_STREAM_SWAP_ADDR (0x3134 * 4)
++#define HEVC_STREAM_SWAP_CTRL (0x3135 * 4)
++#define HEVC_IQIT_IF_WAIT_CNT (0x3136 * 4)
++#define HEVC_MPRED_IF_WAIT_CNT (0x3137 * 4)
++#define HEVC_SAO_IF_WAIT_CNT (0x3138 * 4)
++#define HEVC_PARSER_DEBUG_IDX (0x313e * 4)
++#define HEVC_PARSER_DEBUG_DAT (0x313f * 4)
++#define HEVC_MPRED_VERSION (0x3200 * 4)
++#define HEVC_MPRED_CTRL0 (0x3201 * 4)
++#define HEVC_MPRED_CTRL1 (0x3202 * 4)
++#define HEVC_MPRED_INT_EN (0x3203 * 4)
++#define HEVC_MPRED_INT_STATUS (0x3204 * 4)
++#define HEVC_MPRED_PIC_SIZE (0x3205 * 4)
++#define HEVC_MPRED_PIC_SIZE_LCU (0x3206 * 4)
++#define HEVC_MPRED_TILE_START (0x3207 * 4)
++#define HEVC_MPRED_TILE_SIZE_LCU (0x3208 * 4)
++#define HEVC_MPRED_REF_NUM (0x3209 * 4)
++#define HEVC_MPRED_LT_REF (0x320a * 4)
++#define HEVC_MPRED_LT_COLREF (0x320b * 4)
++#define HEVC_MPRED_REF_EN_L0 (0x320c * 4)
++#define HEVC_MPRED_REF_EN_L1 (0x320d * 4)
++#define HEVC_MPRED_COLREF_EN_L0 (0x320e * 4)
++#define HEVC_MPRED_COLREF_EN_L1 (0x320f * 4)
++#define HEVC_MPRED_AXI_WCTRL (0x3210 * 4)
++#define HEVC_MPRED_AXI_RCTRL (0x3211 * 4)
++#define HEVC_MPRED_ABV_START_ADDR (0x3212 * 4)
++#define HEVC_MPRED_MV_WR_START_ADDR (0x3213 * 4)
++#define HEVC_MPRED_MV_RD_START_ADDR (0x3214 * 4)
++#define HEVC_MPRED_MV_WPTR (0x3215 * 4)
++#define HEVC_MPRED_MV_RPTR (0x3216 * 4)
++#define HEVC_MPRED_MV_WR_ROW_JUMP (0x3217 * 4)
++#define HEVC_MPRED_MV_RD_ROW_JUMP (0x3218 * 4)
++#define HEVC_MPRED_CURR_LCU (0x3219 * 4)
++#define HEVC_MPRED_ABV_WPTR (0x321a * 4)
++#define HEVC_MPRED_ABV_RPTR (0x321b * 4)
++#define HEVC_MPRED_CTRL2 (0x321c * 4)
++#define HEVC_MPRED_CTRL3 (0x321d * 4)
++#define HEVC_MPRED_MV_WLCUY (0x321e * 4)
++#define HEVC_MPRED_MV_RLCUY (0x321f * 4)
++#define HEVC_MPRED_L0_REF00_POC (0x3220 * 4)
++#define HEVC_MPRED_L0_REF01_POC (0x3221 * 4)
++#define HEVC_MPRED_L0_REF02_POC (0x3222 * 4)
++#define HEVC_MPRED_L0_REF03_POC (0x3223 * 4)
++#define HEVC_MPRED_L0_REF04_POC (0x3224 * 4)
++#define HEVC_MPRED_L0_REF05_POC (0x3225 * 4)
++#define HEVC_MPRED_L0_REF06_POC (0x3226 * 4)
++#define HEVC_MPRED_L0_REF07_POC (0x3227 * 4)
++#define HEVC_MPRED_L0_REF08_POC (0x3228 * 4)
++#define HEVC_MPRED_L0_REF09_POC (0x3229 * 4)
++#define HEVC_MPRED_L0_REF10_POC (0x322a * 4)
++#define HEVC_MPRED_L0_REF11_POC (0x322b * 4)
++#define HEVC_MPRED_L0_REF12_POC (0x322c * 4)
++#define HEVC_MPRED_L0_REF13_POC (0x322d * 4)
++#define HEVC_MPRED_L0_REF14_POC (0x322e * 4)
++#define HEVC_MPRED_L0_REF15_POC (0x322f * 4)
++#define HEVC_MPRED_L1_REF00_POC (0x3230 * 4)
++#define HEVC_MPRED_L1_REF01_POC (0x3231 * 4)
++#define HEVC_MPRED_L1_REF02_POC (0x3232 * 4)
++#define HEVC_MPRED_L1_REF03_POC (0x3233 * 4)
++#define HEVC_MPRED_L1_REF04_POC (0x3234 * 4)
++#define HEVC_MPRED_L1_REF05_POC (0x3235 * 4)
++#define HEVC_MPRED_L1_REF06_POC (0x3236 * 4)
++#define HEVC_MPRED_L1_REF07_POC (0x3237 * 4)
++#define HEVC_MPRED_L1_REF08_POC (0x3238 * 4)
++#define HEVC_MPRED_L1_REF09_POC (0x3239 * 4)
++#define HEVC_MPRED_L1_REF10_POC (0x323a * 4)
++#define HEVC_MPRED_L1_REF11_POC (0x323b * 4)
++#define HEVC_MPRED_L1_REF12_POC (0x323c * 4)
++#define HEVC_MPRED_L1_REF13_POC (0x323d * 4)
++#define HEVC_MPRED_L1_REF14_POC (0x323e * 4)
++#define HEVC_MPRED_L1_REF15_POC (0x323f * 4)
++#define HEVC_MPRED_PIC_SIZE_EXT (0x3240 * 4)
++#define HEVC_MPRED_DBG_MODE0 (0x3241 * 4)
++#define HEVC_MPRED_DBG_MODE1 (0x3242 * 4)
++#define HEVC_MPRED_DBG2_MODE (0x3243 * 4)
++#define HEVC_MPRED_IMP_CMD0 (0x3244 * 4)
++#define HEVC_MPRED_IMP_CMD1 (0x3245 * 4)
++#define HEVC_MPRED_IMP_CMD2 (0x3246 * 4)
++#define HEVC_MPRED_IMP_CMD3 (0x3247 * 4)
++#define HEVC_MPRED_DBG2_DATA_0 (0x3248 * 4)
++#define HEVC_MPRED_DBG2_DATA_1 (0x3249 * 4)
++#define HEVC_MPRED_DBG2_DATA_2 (0x324a * 4)
++#define HEVC_MPRED_DBG2_DATA_3 (0x324b * 4)
++#define HEVC_MPRED_DBG_DATA_0 (0x3250 * 4)
++#define HEVC_MPRED_DBG_DATA_1 (0x3251 * 4)
++#define HEVC_MPRED_DBG_DATA_2 (0x3252 * 4)
++#define HEVC_MPRED_DBG_DATA_3 (0x3253 * 4)
++#define HEVC_MPRED_DBG_DATA_4 (0x3254 * 4)
++#define HEVC_MPRED_DBG_DATA_5 (0x3255 * 4)
++#define HEVC_MPRED_DBG_DATA_6 (0x3256 * 4)
++#define HEVC_MPRED_DBG_DATA_7 (0x3257 * 4)
++#define HEVC_MPRED_CUR_POC (0x3260 * 4)
++#define HEVC_MPRED_COL_POC (0x3261 * 4)
++#define HEVC_MPRED_MV_RD_END_ADDR (0x3262 * 4)
++#define HEVCD_IPP_TOP_CNTL (0x3400 * 4)
++#define HEVCD_IPP_TOP_STATUS (0x3401 * 4)
++#define HEVCD_IPP_TOP_FRMCONFIG (0x3402 * 4)
++#define HEVCD_IPP_TOP_TILECONFIG1 (0x3403 * 4)
++#define HEVCD_IPP_TOP_TILECONFIG2 (0x3404 * 4)
++#define HEVCD_IPP_TOP_TILECONFIG3 (0x3405 * 4)
++#define HEVCD_IPP_TOP_LCUCONFIG (0x3406 * 4)
++#define HEVCD_IPP_TOP_FRMCTL (0x3407 * 4)
++#define HEVCD_IPP_CONFIG (0x3408 * 4)
++#define HEVCD_IPP_LINEBUFF_BASE (0x3409 * 4)
++#define HEVCD_IPP_INTR_MASK (0x340a * 4)
++#define HEVCD_IPP_AXIIF_CONFIG (0x340b * 4)
++#define HEVCD_IPP_BITDEPTH_CONFIG (0x340c * 4)
++#define HEVCD_IPP_SWMPREDIF_CONFIG (0x3410 * 4)
++#define HEVCD_IPP_SWMPREDIF_STATUS (0x3411 * 4)
++#define HEVCD_IPP_SWMPREDIF_CTBINFO (0x3412 * 4)
++#define HEVCD_IPP_SWMPREDIF_PUINFO0 (0x3413 * 4)
++#define HEVCD_IPP_SWMPREDIF_PUINFO1 (0x3414 * 4)
++#define HEVCD_IPP_SWMPREDIF_PUINFO2 (0x3415 * 4)
++#define HEVCD_IPP_SWMPREDIF_PUINFO3 (0x3416 * 4)
++#define HEVCD_IPP_DYNCLKGATE_CONFIG (0x3420 * 4)
++#define HEVCD_IPP_DYNCLKGATE_STATUS (0x3421 * 4)
++#define HEVCD_IPP_DBG_SEL (0x3430 * 4)
++#define HEVCD_IPP_DBG_DATA (0x3431 * 4)
++#define HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR (0x3460 * 4)
++#define HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR (0x3461 * 4)
++#define HEVCD_MPP_ANC2AXI_TBL_WDATA_ADDR (0x3462 * 4)
++#define HEVCD_MPP_ANC2AXI_TBL_RDATA_ADDR (0x3463 * 4)
++#define HEVCD_MPP_WEIGHTPRED_CNTL_ADDR (0x347b * 4)
++#define HEVCD_MPP_L0_WEIGHT_FLAG_ADDR (0x347c * 4)
++#define HEVCD_MPP_L1_WEIGHT_FLAG_ADDR (0x347d * 4)
++#define HEVCD_MPP_YLOG2WGHTDENOM_ADDR (0x347e * 4)
++#define HEVCD_MPP_DELTACLOG2WGHTDENOM_ADDR (0x347f * 4)
++#define HEVCD_MPP_WEIGHT_ADDR (0x3480 * 4)
++#define HEVCD_MPP_WEIGHT_DATA (0x3481 * 4)
++#define HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR (0x34c0 * 4)
++#define HEVCD_MPP_ANC_CANVAS_DATA_ADDR (0x34c1 * 4)
++#define HEVCD_MPP_DECOMP_CTL1 (0x34c2 * 4)
++#define HEVCD_MPP_DECOMP_CTL2 (0x34c3 * 4)
++#define HEVCD_MCRCC_CTL1 (0x34f0 * 4)
++#define HEVCD_MCRCC_CTL2 (0x34f1 * 4)
++#define HEVCD_MCRCC_CTL3 (0x34f2 * 4)
++#define HEVCD_MCRCC_PERFMON_CTL (0x34f3 * 4)
++#define HEVCD_MCRCC_PERFMON_DATA (0x34f4 * 4)
++#define HEVC_DBLK_CFG0 (0x3500 * 4)
++#define HEVC_DBLK_CFG1 (0x3501 * 4)
++#define HEVC_DBLK_CFG2 (0x3502 * 4)
++#define HEVC_DBLK_CFG3 (0x3503 * 4)
++#define HEVC_DBLK_CFG4 (0x3504 * 4)
++#define HEVC_DBLK_CFG5 (0x3505 * 4)
++#define HEVC_DBLK_CFG6 (0x3506 * 4)
++#define HEVC_DBLK_CFG7 (0x3507 * 4)
++#define HEVC_DBLK_CFG8 (0x3508 * 4)
++#define HEVC_DBLK_CFG9 (0x3509 * 4)
++#define HEVC_DBLK_CFGA (0x350a * 4)
++#define HEVC_DBLK_STS0 (0x350b * 4)
++#define HEVC_DBLK_STS1 (0x350c * 4)
++#define HEVC_SAO_VERSION (0x3600 * 4)
++#define HEVC_SAO_CTRL0 (0x3601 * 4)
++#define HEVC_SAO_CTRL1 (0x3602 * 4)
++#define HEVC_SAO_INT_EN (0x3603 * 4)
++#define HEVC_SAO_INT_STATUS (0x3604 * 4)
++#define HEVC_SAO_PIC_SIZE (0x3605 * 4)
++#define HEVC_SAO_PIC_SIZE_LCU (0x3606 * 4)
++#define HEVC_SAO_TILE_START (0x3607 * 4)
++#define HEVC_SAO_TILE_SIZE_LCU (0x3608 * 4)
++#define HEVC_SAO_AXI_WCTRL (0x3609 * 4)
++#define HEVC_SAO_AXI_RCTRL (0x360a * 4)
++#define HEVC_SAO_Y_START_ADDR (0x360b * 4)
++#define HEVC_SAO_Y_LENGTH (0x360c * 4)
++#define HEVC_SAO_C_START_ADDR (0x360d * 4)
++#define HEVC_SAO_C_LENGTH (0x360e * 4)
++#define HEVC_SAO_Y_WPTR (0x360f * 4)
++#define HEVC_SAO_C_WPTR (0x3610 * 4)
++#define HEVC_SAO_ABV_START_ADDR (0x3611 * 4)
++#define HEVC_SAO_VB_WR_START_ADDR (0x3612 * 4)
++#define HEVC_SAO_VB_RD_START_ADDR (0x3613 * 4)
++#define HEVC_SAO_ABV_WPTR (0x3614 * 4)
++#define HEVC_SAO_ABV_RPTR (0x3615 * 4)
++#define HEVC_SAO_VB_WPTR (0x3616 * 4)
++#define HEVC_SAO_VB_RPTR (0x3617 * 4)
++#define HEVC_SAO_DBG_MODE0 (0x361e * 4)
++#define HEVC_SAO_DBG_MODE1 (0x361f * 4)
++#define HEVC_SAO_CTRL2 (0x3620 * 4)
++#define HEVC_SAO_CTRL3 (0x3621 * 4)
++#define HEVC_SAO_CTRL4 (0x3622 * 4)
++#define HEVC_SAO_CTRL5 (0x3623 * 4)
++#define HEVC_SAO_CTRL6 (0x3624 * 4)
++#define HEVC_SAO_CTRL7 (0x3625 * 4)
++#define HEVC_SAO_DBG_DATA_0 (0x3630 * 4)
++#define HEVC_SAO_DBG_DATA_1 (0x3631 * 4)
++#define HEVC_SAO_DBG_DATA_2 (0x3632 * 4)
++#define HEVC_SAO_DBG_DATA_3 (0x3633 * 4)
++#define HEVC_SAO_DBG_DATA_4 (0x3634 * 4)
++#define HEVC_SAO_DBG_DATA_5 (0x3635 * 4)
++#define HEVC_SAO_DBG_DATA_6 (0x3636 * 4)
++#define HEVC_SAO_DBG_DATA_7 (0x3637 * 4)
++#define HEVC_IQIT_CLK_RST_CTRL (0x3700 * 4)
++#define HEVC_IQIT_DEQUANT_CTRL (0x3701 * 4)
++#define HEVC_IQIT_SCALELUT_WR_ADDR (0x3702 * 4)
++#define HEVC_IQIT_SCALELUT_RD_ADDR (0x3703 * 4)
++#define HEVC_IQIT_SCALELUT_DATA (0x3704 * 4)
++#define HEVC_IQIT_SCALELUT_IDX_4 (0x3705 * 4)
++#define HEVC_IQIT_SCALELUT_IDX_8 (0x3706 * 4)
++#define HEVC_IQIT_SCALELUT_IDX_16_32 (0x3707 * 4)
++#define HEVC_IQIT_STAT_GEN0 (0x3708 * 4)
++#define HEVC_QP_WRITE (0x3709 * 4)
++#define HEVC_IQIT_STAT_GEN1 (0x370a * 4)
++/**/
++
++/*add from M8M2*/
++#define HEVC_MC_CTRL_REG (0x3900 * 4)
++#define HEVC_MC_MB_INFO (0x3901 * 4)
++#define HEVC_MC_PIC_INFO (0x3902 * 4)
++#define HEVC_MC_HALF_PEL_ONE (0x3903 * 4)
++#define HEVC_MC_HALF_PEL_TWO (0x3904 * 4)
++#define HEVC_POWER_CTL_MC (0x3905 * 4)
++#define HEVC_MC_CMD (0x3906 * 4)
++#define HEVC_MC_CTRL0 (0x3907 * 4)
++#define HEVC_MC_PIC_W_H (0x3908 * 4)
++#define HEVC_MC_STATUS0 (0x3909 * 4)
++#define HEVC_MC_STATUS1 (0x390a * 4)
++#define HEVC_MC_CTRL1 (0x390b * 4)
++#define HEVC_MC_MIX_RATIO0 (0x390c * 4)
++#define HEVC_MC_MIX_RATIO1 (0x390d * 4)
++#define HEVC_MC_DP_MB_XY (0x390e * 4)
++#define HEVC_MC_OM_MB_XY (0x390f * 4)
++#define HEVC_PSCALE_RST (0x3910 * 4)
++#define HEVC_PSCALE_CTRL (0x3911 * 4)
++#define HEVC_PSCALE_PICI_W (0x3912 * 4)
++#define HEVC_PSCALE_PICI_H (0x3913 * 4)
++#define HEVC_PSCALE_PICO_W (0x3914 * 4)
++#define HEVC_PSCALE_PICO_H (0x3915 * 4)
++#define HEVC_PSCALE_PICO_START_X (0x3916 * 4)
++#define HEVC_PSCALE_PICO_START_Y (0x3917 * 4)
++#define HEVC_PSCALE_DUMMY (0x3918 * 4)
++#define HEVC_PSCALE_FILT0_COEF0 (0x3919 * 4)
++#define HEVC_PSCALE_FILT0_COEF1 (0x391a * 4)
++#define HEVC_PSCALE_CMD_CTRL (0x391b * 4)
++#define HEVC_PSCALE_CMD_BLK_X (0x391c * 4)
++#define HEVC_PSCALE_CMD_BLK_Y (0x391d * 4)
++#define HEVC_PSCALE_STATUS (0x391e * 4)
++#define HEVC_PSCALE_BMEM_ADDR (0x391f * 4)
++#define HEVC_PSCALE_BMEM_DAT (0x3920 * 4)
++#define HEVC_PSCALE_DRAM_BUF_CTRL (0x3921 * 4)
++#define HEVC_PSCALE_MCMD_CTRL (0x3922 * 4)
++#define HEVC_PSCALE_MCMD_XSIZE (0x3923 * 4)
++#define HEVC_PSCALE_MCMD_YSIZE (0x3924 * 4)
++#define HEVC_PSCALE_RBUF_START_BLKX (0x3925 * 4)
++#define HEVC_PSCALE_RBUF_START_BLKY (0x3926 * 4)
++#define HEVC_PSCALE_PICO_SHIFT_XY (0x3928 * 4)
++#define HEVC_PSCALE_CTRL1 (0x3929 * 4)
++#define HEVC_PSCALE_SRCKEY_CTRL0 (0x392a * 4)
++#define HEVC_PSCALE_SRCKEY_CTRL1 (0x392b * 4)
++#define HEVC_PSCALE_CANVAS_RD_ADDR (0x392c * 4)
++#define HEVC_PSCALE_CANVAS_WR_ADDR (0x392d * 4)
++#define HEVC_PSCALE_CTRL2 (0x392e * 4)
++#define HEVC_HDEC_MC_OMEM_AUTO (0x3930 * 4)
++#define HEVC_HDEC_MC_MBRIGHT_IDX (0x3931 * 4)
++#define HEVC_HDEC_MC_MBRIGHT_RD (0x3932 * 4)
++#define HEVC_MC_MPORT_CTRL (0x3940 * 4)
++#define HEVC_MC_MPORT_DAT (0x3941 * 4)
++#define HEVC_MC_WT_PRED_CTRL (0x3942 * 4)
++#define HEVC_MC_MBBOT_ST_EVEN_ADDR (0x3944 * 4)
++#define HEVC_MC_MBBOT_ST_ODD_ADDR (0x3945 * 4)
++#define HEVC_MC_DPDN_MB_XY (0x3946 * 4)
++#define HEVC_MC_OMDN_MB_XY (0x3947 * 4)
++#define HEVC_MC_HCMDBUF_H (0x3948 * 4)
++#define HEVC_MC_HCMDBUF_L (0x3949 * 4)
++#define HEVC_MC_HCMD_H (0x394a * 4)
++#define HEVC_MC_HCMD_L (0x394b * 4)
++#define HEVC_MC_IDCT_DAT (0x394c * 4)
++#define HEVC_MC_CTRL_GCLK_CTRL (0x394d * 4)
++#define HEVC_MC_OTHER_GCLK_CTRL (0x394e * 4)
++#define HEVC_MC_CTRL2 (0x394f * 4)
++#define HEVC_MDEC_PIC_DC_CTRL (0x398e * 4)
++#define HEVC_MDEC_PIC_DC_STATUS (0x398f * 4)
++#define HEVC_ANC0_CANVAS_ADDR (0x3990 * 4)
++#define HEVC_ANC1_CANVAS_ADDR (0x3991 * 4)
++#define HEVC_ANC2_CANVAS_ADDR (0x3992 * 4)
++#define HEVC_ANC3_CANVAS_ADDR (0x3993 * 4)
++#define HEVC_ANC4_CANVAS_ADDR (0x3994 * 4)
++#define HEVC_ANC5_CANVAS_ADDR (0x3995 * 4)
++#define HEVC_ANC6_CANVAS_ADDR (0x3996 * 4)
++#define HEVC_ANC7_CANVAS_ADDR (0x3997 * 4)
++#define HEVC_ANC8_CANVAS_ADDR (0x3998 * 4)
++#define HEVC_ANC9_CANVAS_ADDR (0x3999 * 4)
++#define HEVC_ANC10_CANVAS_ADDR (0x399a * 4)
++#define HEVC_ANC11_CANVAS_ADDR (0x399b * 4)
++#define HEVC_ANC12_CANVAS_ADDR (0x399c * 4)
++#define HEVC_ANC13_CANVAS_ADDR (0x399d * 4)
++#define HEVC_ANC14_CANVAS_ADDR (0x399e * 4)
++#define HEVC_ANC15_CANVAS_ADDR (0x399f * 4)
++#define HEVC_ANC16_CANVAS_ADDR (0x39a0 * 4)
++#define HEVC_ANC17_CANVAS_ADDR (0x39a1 * 4)
++#define HEVC_ANC18_CANVAS_ADDR (0x39a2 * 4)
++#define HEVC_ANC19_CANVAS_ADDR (0x39a3 * 4)
++#define HEVC_ANC20_CANVAS_ADDR (0x39a4 * 4)
++#define HEVC_ANC21_CANVAS_ADDR (0x39a5 * 4)
++#define HEVC_ANC22_CANVAS_ADDR (0x39a6 * 4)
++#define HEVC_ANC23_CANVAS_ADDR (0x39a7 * 4)
++#define HEVC_ANC24_CANVAS_ADDR (0x39a8 * 4)
++#define HEVC_ANC25_CANVAS_ADDR (0x39a9 * 4)
++#define HEVC_ANC26_CANVAS_ADDR (0x39aa * 4)
++#define HEVC_ANC27_CANVAS_ADDR (0x39ab * 4)
++#define HEVC_ANC28_CANVAS_ADDR (0x39ac * 4)
++#define HEVC_ANC29_CANVAS_ADDR (0x39ad * 4)
++#define HEVC_ANC30_CANVAS_ADDR (0x39ae * 4)
++#define HEVC_ANC31_CANVAS_ADDR (0x39af * 4)
++#define HEVC_DBKR_CANVAS_ADDR (0x39b0 * 4)
++#define HEVC_DBKW_CANVAS_ADDR (0x39b1 * 4)
++#define HEVC_REC_CANVAS_ADDR (0x39b2 * 4)
++#define HEVC_CURR_CANVAS_CTRL (0x39b3 * 4)
++#define HEVC_MDEC_PIC_DC_THRESH (0x39b8 * 4)
++#define HEVC_MDEC_PICR_BUF_STATUS (0x39b9 * 4)
++#define HEVC_MDEC_PICW_BUF_STATUS (0x39ba * 4)
++#define HEVC_MCW_DBLK_WRRSP_CNT (0x39bb * 4)
++#define HEVC_MC_MBBOT_WRRSP_CNT (0x39bc * 4)
++#define HEVC_MDEC_PICW_BUF2_STATUS (0x39bd * 4)
++#define HEVC_WRRSP_FIFO_PICW_DBK (0x39be * 4)
++#define HEVC_WRRSP_FIFO_PICW_MC (0x39bf * 4)
++#define HEVC_AV_SCRATCH_0 (0x39c0 * 4)
++#define HEVC_AV_SCRATCH_1 (0x39c1 * 4)
++#define HEVC_AV_SCRATCH_2 (0x39c2 * 4)
++#define HEVC_AV_SCRATCH_3 (0x39c3 * 4)
++#define HEVC_AV_SCRATCH_4 (0x39c4 * 4)
++#define HEVC_AV_SCRATCH_5 (0x39c5 * 4)
++#define HEVC_AV_SCRATCH_6 (0x39c6 * 4)
++#define HEVC_AV_SCRATCH_7 (0x39c7 * 4)
++#define HEVC_AV_SCRATCH_8 (0x39c8 * 4)
++#define HEVC_AV_SCRATCH_9 (0x39c9 * 4)
++#define HEVC_AV_SCRATCH_A (0x39ca * 4)
++#define HEVC_AV_SCRATCH_B (0x39cb * 4)
++#define HEVC_AV_SCRATCH_C (0x39cc * 4)
++#define HEVC_AV_SCRATCH_D (0x39cd * 4)
++#define HEVC_AV_SCRATCH_E (0x39ce * 4)
++#define HEVC_AV_SCRATCH_F (0x39cf * 4)
++#define HEVC_AV_SCRATCH_G (0x39d0 * 4)
++#define HEVC_AV_SCRATCH_H (0x39d1 * 4)
++#define HEVC_AV_SCRATCH_I (0x39d2 * 4)
++#define HEVC_AV_SCRATCH_J (0x39d3 * 4)
++#define HEVC_AV_SCRATCH_K (0x39d4 * 4)
++#define HEVC_AV_SCRATCH_L (0x39d5 * 4)
++#define HEVC_AV_SCRATCH_M (0x39d6 * 4)
++#define HEVC_AV_SCRATCH_N (0x39d7 * 4)
++#define HEVC_WRRSP_CO_MB (0x39d8 * 4)
++#define HEVC_WRRSP_DCAC (0x39d9 * 4)
++#define HEVC_WRRSP_VLD (0x39da * 4)
++#define HEVC_MDEC_DOUBLEW_CFG0 (0x39db * 4)
++#define HEVC_MDEC_DOUBLEW_CFG1 (0x39dc * 4)
++#define HEVC_MDEC_DOUBLEW_CFG2 (0x39dd * 4)
++#define HEVC_MDEC_DOUBLEW_CFG3 (0x39de * 4)
++#define HEVC_MDEC_DOUBLEW_CFG4 (0x39df * 4)
++#define HEVC_MDEC_DOUBLEW_CFG5 (0x39e0 * 4)
++#define HEVC_MDEC_DOUBLEW_CFG6 (0x39e1 * 4)
++#define HEVC_MDEC_DOUBLEW_CFG7 (0x39e2 * 4)
++#define HEVC_MDEC_DOUBLEW_STATUS (0x39e3 * 4)
++#define HEVC_DBLK_RST (0x3950 * 4)
++#define HEVC_DBLK_CTRL (0x3951 * 4)
++#define HEVC_DBLK_MB_WID_HEIGHT (0x3952 * 4)
++#define HEVC_DBLK_STATUS (0x3953 * 4)
++#define HEVC_DBLK_CMD_CTRL (0x3954 * 4)
++#define HEVC_DBLK_MB_XY (0x3955 * 4)
++#define HEVC_DBLK_QP (0x3956 * 4)
++#define HEVC_DBLK_Y_BHFILT (0x3957 * 4)
++#define HEVC_DBLK_Y_BHFILT_HIGH (0x3958 * 4)
++#define HEVC_DBLK_Y_BVFILT (0x3959 * 4)
++#define HEVC_DBLK_CB_BFILT (0x395a * 4)
++#define HEVC_DBLK_CR_BFILT (0x395b * 4)
++#define HEVC_DBLK_Y_HFILT (0x395c * 4)
++#define HEVC_DBLK_Y_HFILT_HIGH (0x395d * 4)
++#define HEVC_DBLK_Y_VFILT (0x395e * 4)
++#define HEVC_DBLK_CB_FILT (0x395f * 4)
++#define HEVC_DBLK_CR_FILT (0x3960 * 4)
++#define HEVC_DBLK_BETAX_QP_SEL (0x3961 * 4)
++#define HEVC_DBLK_CLIP_CTRL0 (0x3962 * 4)
++#define HEVC_DBLK_CLIP_CTRL1 (0x3963 * 4)
++#define HEVC_DBLK_CLIP_CTRL2 (0x3964 * 4)
++#define HEVC_DBLK_CLIP_CTRL3 (0x3965 * 4)
++#define HEVC_DBLK_CLIP_CTRL4 (0x3966 * 4)
++#define HEVC_DBLK_CLIP_CTRL5 (0x3967 * 4)
++#define HEVC_DBLK_CLIP_CTRL6 (0x3968 * 4)
++#define HEVC_DBLK_CLIP_CTRL7 (0x3969 * 4)
++#define HEVC_DBLK_CLIP_CTRL8 (0x396a * 4)
++#define HEVC_DBLK_STATUS1 (0x396b * 4)
++#define HEVC_DBLK_GCLK_FREE (0x396c * 4)
++#define HEVC_DBLK_GCLK_OFF (0x396d * 4)
++#define HEVC_DBLK_AVSFLAGS (0x396e * 4)
++#define HEVC_DBLK_CBPY (0x3970 * 4)
++#define HEVC_DBLK_CBPY_ADJ (0x3971 * 4)
++#define HEVC_DBLK_CBPC (0x3972 * 4)
++#define HEVC_DBLK_CBPC_ADJ (0x3973 * 4)
++#define HEVC_DBLK_VHMVD (0x3974 * 4)
++#define HEVC_DBLK_STRONG (0x3975 * 4)
++#define HEVC_DBLK_RV8_QUANT (0x3976 * 4)
++#define HEVC_DBLK_CBUS_HCMD2 (0x3977 * 4)
++#define HEVC_DBLK_CBUS_HCMD1 (0x3978 * 4)
++#define HEVC_DBLK_CBUS_HCMD0 (0x3979 * 4)
++#define HEVC_DBLK_VLD_HCMD2 (0x397a * 4)
++#define HEVC_DBLK_VLD_HCMD1 (0x397b * 4)
++#define HEVC_DBLK_VLD_HCMD0 (0x397c * 4)
++#define HEVC_DBLK_OST_YBASE (0x397d * 4)
++#define HEVC_DBLK_OST_CBCRDIFF (0x397e * 4)
++#define HEVC_DBLK_CTRL1 (0x397f * 4)
++#define HEVC_MCRCC_CTL1 (0x3980 * 4)
++#define HEVC_MCRCC_CTL2 (0x3981 * 4)
++#define HEVC_MCRCC_CTL3 (0x3982 * 4)
++#define HEVC_GCLK_EN (0x3983 * 4)
++#define HEVC_MDEC_SW_RESET (0x3984 * 4)
++
++/*add from M8M2*/
++#define HEVC_VLD_STATUS_CTRL (0x3c00 * 4)
++#define HEVC_MPEG1_2_REG (0x3c01 * 4)
++#define HEVC_F_CODE_REG (0x3c02 * 4)
++#define HEVC_PIC_HEAD_INFO (0x3c03 * 4)
++#define HEVC_SLICE_VER_POS_PIC_TYPE (0x3c04 * 4)
++#define HEVC_QP_VALUE_REG (0x3c05 * 4)
++#define HEVC_MBA_INC (0x3c06 * 4)
++#define HEVC_MB_MOTION_MODE (0x3c07 * 4)
++#define HEVC_POWER_CTL_VLD (0x3c08 * 4)
++#define HEVC_MB_WIDTH (0x3c09 * 4)
++#define HEVC_SLICE_QP (0x3c0a * 4)
++#define HEVC_PRE_START_CODE (0x3c0b * 4)
++#define HEVC_SLICE_START_BYTE_01 (0x3c0c * 4)
++#define HEVC_SLICE_START_BYTE_23 (0x3c0d * 4)
++#define HEVC_RESYNC_MARKER_LENGTH (0x3c0e * 4)
++#define HEVC_DECODER_BUFFER_INFO (0x3c0f * 4)
++#define HEVC_FST_FOR_MV_X (0x3c10 * 4)
++#define HEVC_FST_FOR_MV_Y (0x3c11 * 4)
++#define HEVC_SCD_FOR_MV_X (0x3c12 * 4)
++#define HEVC_SCD_FOR_MV_Y (0x3c13 * 4)
++#define HEVC_FST_BAK_MV_X (0x3c14 * 4)
++#define HEVC_FST_BAK_MV_Y (0x3c15 * 4)
++#define HEVC_SCD_BAK_MV_X (0x3c16 * 4)
++#define HEVC_SCD_BAK_MV_Y (0x3c17 * 4)
++#define HEVC_VLD_DECODE_CONTROL (0x3c18 * 4)
++#define HEVC_VLD_REVERVED_19 (0x3c19 * 4)
++#define HEVC_VIFF_BIT_CNT (0x3c1a * 4)
++#define HEVC_BYTE_ALIGN_PEAK_HI (0x3c1b * 4)
++#define HEVC_BYTE_ALIGN_PEAK_LO (0x3c1c * 4)
++#define HEVC_NEXT_ALIGN_PEAK (0x3c1d * 4)
++#define HEVC_VC1_CONTROL_REG (0x3c1e * 4)
++#define HEVC_PMV1_X (0x3c20 * 4)
++#define HEVC_PMV1_Y (0x3c21 * 4)
++#define HEVC_PMV2_X (0x3c22 * 4)
++#define HEVC_PMV2_Y (0x3c23 * 4)
++#define HEVC_PMV3_X (0x3c24 * 4)
++#define HEVC_PMV3_Y (0x3c25 * 4)
++#define HEVC_PMV4_X (0x3c26 * 4)
++#define HEVC_PMV4_Y (0x3c27 * 4)
++#define HEVC_M4_TABLE_SELECT (0x3c28 * 4)
++#define HEVC_M4_CONTROL_REG (0x3c29 * 4)
++#define HEVC_BLOCK_NUM (0x3c2a * 4)
++#define HEVC_PATTERN_CODE (0x3c2b * 4)
++#define HEVC_MB_INFO (0x3c2c * 4)
++#define HEVC_VLD_DC_PRED (0x3c2d * 4)
++#define HEVC_VLD_ERROR_MASK (0x3c2e * 4)
++#define HEVC_VLD_DC_PRED_C (0x3c2f * 4)
++#define HEVC_LAST_SLICE_MV_ADDR (0x3c30 * 4)
++#define HEVC_LAST_MVX (0x3c31 * 4)
++#define HEVC_LAST_MVY (0x3c32 * 4)
++#define HEVC_VLD_C38 (0x3c38 * 4)
++#define HEVC_VLD_C39 (0x3c39 * 4)
++#define HEVC_VLD_STATUS (0x3c3a * 4)
++#define HEVC_VLD_SHIFT_STATUS (0x3c3b * 4)
++#define HEVC_VOFF_STATUS (0x3c3c * 4)
++#define HEVC_VLD_C3D (0x3c3d * 4)
++#define HEVC_VLD_DBG_INDEX (0x3c3e * 4)
++#define HEVC_VLD_DBG_DATA (0x3c3f * 4)
++#define HEVC_VLD_MEM_VIFIFO_START_PTR (0x3c40 * 4)
++#define HEVC_VLD_MEM_VIFIFO_CURR_PTR (0x3c41 * 4)
++#define HEVC_VLD_MEM_VIFIFO_END_PTR (0x3c42 * 4)
++#define HEVC_VLD_MEM_VIFIFO_BYTES_AVAIL (0x3c43 * 4)
++#define HEVC_VLD_MEM_VIFIFO_CONTROL (0x3c44 * 4)
++#define HEVC_VLD_MEM_VIFIFO_WP (0x3c45 * 4)
++#define HEVC_VLD_MEM_VIFIFO_RP (0x3c46 * 4)
++#define HEVC_VLD_MEM_VIFIFO_LEVEL (0x3c47 * 4)
++#define HEVC_VLD_MEM_VIFIFO_BUF_CNTL (0x3c48 * 4)
++#define HEVC_VLD_TIME_STAMP_CNTL (0x3c49 * 4)
++#define HEVC_VLD_TIME_STAMP_SYNC_0 (0x3c4a * 4)
++#define HEVC_VLD_TIME_STAMP_SYNC_1 (0x3c4b * 4)
++#define HEVC_VLD_TIME_STAMP_0 (0x3c4c * 4)
++#define HEVC_VLD_TIME_STAMP_1 (0x3c4d * 4)
++#define HEVC_VLD_TIME_STAMP_2 (0x3c4e * 4)
++#define HEVC_VLD_TIME_STAMP_3 (0x3c4f * 4)
++#define HEVC_VLD_TIME_STAMP_LENGTH (0x3c50 * 4)
++#define HEVC_VLD_MEM_VIFIFO_WRAP_COUNT (0x3c51 * 4)
++#define HEVC_VLD_MEM_VIFIFO_MEM_CTL (0x3c52 * 4)
++#define HEVC_VLD_MEM_VBUF_RD_PTR (0x3c53 * 4)
++#define HEVC_VLD_MEM_VBUF2_RD_PTR (0x3c54 * 4)
++#define HEVC_VLD_MEM_SWAP_ADDR (0x3c55 * 4)
++#define HEVC_VLD_MEM_SWAP_CTL (0x3c56 * 4)
++/**/
++
++/*add from M8M2*/
++#define HEVC_VCOP_CTRL_REG (0x3e00 * 4)
++#define HEVC_QP_CTRL_REG (0x3e01 * 4)
++#define HEVC_INTRA_QUANT_MATRIX (0x3e02 * 4)
++#define HEVC_NON_I_QUANT_MATRIX (0x3e03 * 4)
++#define HEVC_DC_SCALER (0x3e04 * 4)
++#define HEVC_DC_AC_CTRL (0x3e05 * 4)
++#define HEVC_DC_AC_SCALE_MUL (0x3e06 * 4)
++#define HEVC_DC_AC_SCALE_DIV (0x3e07 * 4)
++#define HEVC_POWER_CTL_IQIDCT (0x3e08 * 4)
++#define HEVC_RV_AI_Y_X (0x3e09 * 4)
++#define HEVC_RV_AI_U_X (0x3e0a * 4)
++#define HEVC_RV_AI_V_X (0x3e0b * 4)
++#define HEVC_RV_AI_MB_COUNT (0x3e0c * 4)
++#define HEVC_NEXT_INTRA_DMA_ADDRESS (0x3e0d * 4)
++#define HEVC_IQIDCT_CONTROL (0x3e0e * 4)
++#define HEVC_IQIDCT_DEBUG_INFO_0 (0x3e0f * 4)
++#define HEVC_DEBLK_CMD (0x3e10 * 4)
++#define HEVC_IQIDCT_DEBUG_IDCT (0x3e11 * 4)
++#define HEVC_DCAC_DMA_CTRL (0x3e12 * 4)
++#define HEVC_DCAC_DMA_ADDRESS (0x3e13 * 4)
++#define HEVC_DCAC_CPU_ADDRESS (0x3e14 * 4)
++#define HEVC_DCAC_CPU_DATA (0x3e15 * 4)
++#define HEVC_DCAC_MB_COUNT (0x3e16 * 4)
++#define HEVC_IQ_QUANT (0x3e17 * 4)
++#define HEVC_VC1_BITPLANE_CTL (0x3e18 * 4)
++
++
++/*add from M8M2*/
++#define HEVC_MSP (0x3300 * 4)
++#define HEVC_MPSR (0x3301 * 4)
++#define HEVC_MINT_VEC_BASE (0x3302 * 4)
++#define HEVC_MCPU_INTR_GRP (0x3303 * 4)
++#define HEVC_MCPU_INTR_MSK (0x3304 * 4)
++#define HEVC_MCPU_INTR_REQ (0x3305 * 4)
++#define HEVC_MPC_P (0x3306 * 4)
++#define HEVC_MPC_D (0x3307 * 4)
++#define HEVC_MPC_E (0x3308 * 4)
++#define HEVC_MPC_W (0x3309 * 4)
++#define HEVC_MINDEX0_REG (0x330a * 4)
++#define HEVC_MINDEX1_REG (0x330b * 4)
++#define HEVC_MINDEX2_REG (0x330c * 4)
++#define HEVC_MINDEX3_REG (0x330d * 4)
++#define HEVC_MINDEX4_REG (0x330e * 4)
++#define HEVC_MINDEX5_REG (0x330f * 4)
++#define HEVC_MINDEX6_REG (0x3310 * 4)
++#define HEVC_MINDEX7_REG (0x3311 * 4)
++#define HEVC_MMIN_REG (0x3312 * 4)
++#define HEVC_MMAX_REG (0x3313 * 4)
++#define HEVC_MBREAK0_REG (0x3314 * 4)
++#define HEVC_MBREAK1_REG (0x3315 * 4)
++#define HEVC_MBREAK2_REG (0x3316 * 4)
++#define HEVC_MBREAK3_REG (0x3317 * 4)
++#define HEVC_MBREAK_TYPE (0x3318 * 4)
++#define HEVC_MBREAK_CTRL (0x3319 * 4)
++#define HEVC_MBREAK_STAUTS (0x331a * 4)
++#define HEVC_MDB_ADDR_REG (0x331b * 4)
++#define HEVC_MDB_DATA_REG (0x331c * 4)
++#define HEVC_MDB_CTRL (0x331d * 4)
++#define HEVC_MSFTINT0 (0x331e * 4)
++#define HEVC_MSFTINT1 (0x331f * 4)
++#define HEVC_CSP (0x3320 * 4)
++#define HEVC_CPSR (0x3321 * 4)
++#define HEVC_CINT_VEC_BASE (0x3322 * 4)
++#define HEVC_CCPU_INTR_GRP (0x3323 * 4)
++#define HEVC_CCPU_INTR_MSK (0x3324 * 4)
++#define HEVC_CCPU_INTR_REQ (0x3325 * 4)
++#define HEVC_CPC_P (0x3326 * 4)
++#define HEVC_CPC_D (0x3327 * 4)
++#define HEVC_CPC_E (0x3328 * 4)
++#define HEVC_CPC_W (0x3329 * 4)
++#define HEVC_CINDEX0_REG (0x332a * 4)
++#define HEVC_CINDEX1_REG (0x332b * 4)
++#define HEVC_CINDEX2_REG (0x332c * 4)
++#define HEVC_CINDEX3_REG (0x332d * 4)
++#define HEVC_CINDEX4_REG (0x332e * 4)
++#define HEVC_CINDEX5_REG (0x332f * 4)
++#define HEVC_CINDEX6_REG (0x3330 * 4)
++#define HEVC_CINDEX7_REG (0x3331 * 4)
++#define HEVC_CMIN_REG (0x3332 * 4)
++#define HEVC_CMAX_REG (0x3333 * 4)
++#define HEVC_CBREAK0_REG (0x3334 * 4)
++#define HEVC_CBREAK1_REG (0x3335 * 4)
++#define HEVC_CBREAK2_REG (0x3336 * 4)
++#define HEVC_CBREAK3_REG (0x3337 * 4)
++#define HEVC_CBREAK_TYPE (0x3338 * 4)
++#define HEVC_CBREAK_CTRL (0x3339 * 4)
++#define HEVC_CBREAK_STAUTS (0x333a * 4)
++#define HEVC_CDB_ADDR_REG (0x333b * 4)
++#define HEVC_CDB_DATA_REG (0x333c * 4)
++#define HEVC_CDB_CTRL (0x333d * 4)
++#define HEVC_CSFTINT0 (0x333e * 4)
++#define HEVC_CSFTINT1 (0x333f * 4)
++#define HEVC_IMEM_DMA_CTRL (0x3340 * 4)
++#define HEVC_IMEM_DMA_ADR (0x3341 * 4)
++#define HEVC_IMEM_DMA_COUNT (0x3342 * 4)
++#define HEVC_WRRSP_IMEM (0x3343 * 4)
++#define HEVC_LMEM_DMA_CTRL (0x3350 * 4)
++#define HEVC_LMEM_DMA_ADR (0x3351 * 4)
++#define HEVC_LMEM_DMA_COUNT (0x3352 * 4)
++#define HEVC_WRRSP_LMEM (0x3353 * 4)
++#define HEVC_MAC_CTRL1 (0x3360 * 4)
++#define HEVC_ACC0REG1 (0x3361 * 4)
++#define HEVC_ACC1REG1 (0x3362 * 4)
++#define HEVC_MAC_CTRL2 (0x3370 * 4)
++#define HEVC_ACC0REG2 (0x3371 * 4)
++#define HEVC_ACC1REG2 (0x3372 * 4)
++#define HEVC_CPU_TRACE (0x3380 * 4)
++/**/
++
++#endif
++
+diff --git a/drivers/media/platform/meson/vdec/vdec.c b/drivers/media/platform/meson/vdec/vdec.c
+new file mode 100644
+index 0000000..b8b1edf
+--- /dev/null
++++ b/drivers/media/platform/meson/vdec/vdec.c
+@@ -0,0 +1,1009 @@
++// SPDX-License-Identifier: GPL-2.0+
++/*
++ * Copyright (C) 2018 Maxime Jourdan <maxi.jourdan@wanadoo.fr>
++ */
++
++#define DEBUG
++
++#include <linux/of_device.h>
++#include <linux/clk.h>
++#include <linux/io.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/mfd/syscon.h>
++#include <linux/slab.h>
++#include <media/v4l2-ioctl.h>
++#include <media/v4l2-event.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-mem2mem.h>
++#include <media/v4l2-dev.h>
++#include <media/videobuf2-dma-contig.h>
++
++#include "vdec.h"
++#include "esparser.h"
++#include "canvas.h"
++
++#include "vdec_1.h"
++
++/* 16 MiB for parsed bitstream swap exchange */
++#define SIZE_VIFIFO (16 * SZ_1M)
++
++void vdec_abort(struct vdec_session *sess)
++{
++ printk("Aborting decoding session!\n");
++ vb2_queue_error(&sess->m2m_ctx->cap_q_ctx.q);
++ vb2_queue_error(&sess->m2m_ctx->out_q_ctx.q);
++}
++
++static u32 get_output_size(u32 width, u32 height)
++{
++ return ALIGN(width * height, 64 * SZ_1K);
++}
++
++u32 vdec_get_output_size(struct vdec_session *sess)
++{
++ return get_output_size(sess->width, sess->height);
++}
++
++static int vdec_poweron(struct vdec_session *sess)
++{
++ int ret;
++ struct vdec_ops *vdec_ops = sess->fmt_out->vdec_ops;
++
++ ret = clk_prepare_enable(sess->core->dos_parser_clk);
++ if (ret)
++ return ret;
++
++ ret = clk_prepare_enable(sess->core->dos_clk);
++ if (ret)
++ goto disable_dos_parser;
++
++ ret = vdec_ops->start(sess);
++ if (ret)
++ goto disable_dos;
++
++ esparser_power_up(sess);
++
++ return 0;
++
++disable_dos:
++ clk_disable_unprepare(sess->core->dos_clk);
++disable_dos_parser:
++ clk_disable_unprepare(sess->core->dos_parser_clk);
++
++ return ret;
++}
++
++static void vdec_poweroff(struct vdec_session *sess) {
++ struct vdec_ops *vdec_ops = sess->fmt_out->vdec_ops;
++
++ vdec_ops->stop(sess);
++ clk_disable_unprepare(sess->core->dos_clk);
++ clk_disable_unprepare(sess->core->dos_parser_clk);
++}
++
++void vdec_queue_recycle(struct vdec_session *sess, struct vb2_buffer *vb)
++{
++ struct vdec_buffer *new_buf;
++
++ new_buf = kmalloc(sizeof(struct vdec_buffer), GFP_KERNEL);
++ new_buf->index = vb->index;
++
++ mutex_lock(&sess->bufs_recycle_lock);
++ list_add_tail(&new_buf->list, &sess->bufs_recycle);
++ mutex_unlock(&sess->bufs_recycle_lock);
++}
++
++void vdec_m2m_device_run(void *priv)
++{
++ struct vdec_session *sess = priv;
++ schedule_work(&sess->esparser_queue_work);
++}
++
++void vdec_m2m_job_abort(void *priv)
++{
++ struct vdec_session *sess = priv;
++ v4l2_m2m_job_finish(sess->m2m_dev, sess->m2m_ctx);
++}
++
++static const struct v4l2_m2m_ops vdec_m2m_ops = {
++ .device_run = vdec_m2m_device_run,
++ .job_abort = vdec_m2m_job_abort,
++};
++
++static int vdec_queue_setup(struct vb2_queue *q,
++ unsigned int *num_buffers, unsigned int *num_planes,
++ unsigned int sizes[], struct device *alloc_devs[])
++{
++ struct vdec_session *sess = vb2_get_drv_priv(q);
++ const struct vdec_format *fmt_out = sess->fmt_out;
++ const struct vdec_format *fmt_cap = sess->fmt_cap;
++
++ switch (q->type) {
++ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
++ sizes[0] = vdec_get_output_size(sess);
++ sess->num_input_bufs = *num_buffers;
++ *num_planes = fmt_out->num_planes;
++ break;
++ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
++ sizes[0] = vdec_get_output_size(sess);
++ sizes[1] = vdec_get_output_size(sess) / 2;
++ *num_planes = fmt_cap->num_planes;
++ *num_buffers = min(max(*num_buffers, fmt_out->min_buffers), fmt_out->max_buffers);
++ sess->num_output_bufs = *num_buffers;
++ break;
++ default:
++ return -EINVAL;
++ }
++ return 0;
++}
++
++static void vdec_vb2_buf_queue(struct vb2_buffer *vb)
++{
++ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
++ struct vdec_session *sess = vb2_get_drv_priv(vb->vb2_queue);
++ struct v4l2_m2m_ctx *m2m_ctx = sess->m2m_ctx;
++ struct vdec_codec_ops *codec_ops = sess->fmt_out->codec_ops;
++
++ mutex_lock(&sess->lock);
++ v4l2_m2m_buf_queue(m2m_ctx, vbuf);
++
++ if (!(sess->streamon_out & sess->streamon_cap))
++ goto unlock;
++
++ if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
++ schedule_work(&sess->esparser_queue_work);
++ } else if (codec_ops->notify_dst_buffer)
++ codec_ops->notify_dst_buffer(sess, vb);
++
++unlock:
++ mutex_unlock(&sess->lock);
++}
++
++static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
++{
++ struct vdec_session *sess = vb2_get_drv_priv(q);
++ struct vb2_v4l2_buffer *buf;
++ int ret;
++
++ mutex_lock(&sess->lock);
++
++ if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
++ sess->streamon_out = 1;
++ else
++ sess->streamon_cap = 1;
++
++ if (!(sess->streamon_out & sess->streamon_cap)) {
++ mutex_unlock(&sess->lock);
++ return 0;
++ }
++
++ sess->vififo_size = SIZE_VIFIFO;
++ sess->vififo_vaddr = dma_alloc_coherent(sess->core->dev, sess->vififo_size, &sess->vififo_paddr, GFP_KERNEL);
++ if (!sess->vififo_vaddr) {
++ printk("Failed to request VIFIFO buffer\n");
++ ret = -ENOMEM;
++ goto bufs_done;
++ }
++
++ sess->should_stop = 0;
++ ret = vdec_poweron(sess);
++ if (ret)
++ goto vififo_free;
++
++ sess->sequence_cap = 0;
++ mutex_unlock(&sess->lock);
++
++ return 0;
++
++vififo_free:
++ dma_free_coherent(sess->core->dev, sess->vififo_size, sess->vififo_vaddr, sess->vififo_paddr);
++bufs_done:
++ while ((buf = v4l2_m2m_src_buf_remove(sess->m2m_ctx)))
++ v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED);
++ while ((buf = v4l2_m2m_dst_buf_remove(sess->m2m_ctx)))
++ v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED);
++ if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
++ sess->streamon_out = 0;
++ else
++ sess->streamon_cap = 0;
++ mutex_unlock(&sess->lock);
++ return ret;
++}
++
++void vdec_stop_streaming(struct vb2_queue *q)
++{
++ struct vdec_session *sess = vb2_get_drv_priv(q);
++ struct vb2_v4l2_buffer *buf;
++
++ mutex_lock(&sess->lock);
++
++ if (sess->streamon_out & sess->streamon_cap) {
++ vdec_poweroff(sess);
++ dma_free_coherent(sess->core->dev, sess->vififo_size, sess->vififo_vaddr, sess->vififo_paddr);
++ INIT_LIST_HEAD(&sess->bufs);
++ INIT_LIST_HEAD(&sess->bufs_recycle);
++ }
++
++ if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
++ while ((buf = v4l2_m2m_src_buf_remove(sess->m2m_ctx)))
++ v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR);
++
++ sess->streamon_out = 0;
++ } else {
++ while ((buf = v4l2_m2m_dst_buf_remove(sess->m2m_ctx)))
++ v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR);
++
++ sess->streamon_cap = 0;
++ }
++
++ mutex_unlock(&sess->lock);
++}
++
++static const struct vb2_ops vdec_vb2_ops = {
++ .queue_setup = vdec_queue_setup,
++ .start_streaming = vdec_start_streaming,
++ .stop_streaming = vdec_stop_streaming,
++ .buf_queue = vdec_vb2_buf_queue,
++};
++
++static int
++vdec_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
++{
++ strlcpy(cap->driver, "meson-vdec", sizeof(cap->driver));
++ strlcpy(cap->card, "AMLogic Video Decoder", sizeof(cap->card));
++ strlcpy(cap->bus_info, "platform:meson-vdec", sizeof(cap->bus_info));
++
++ return 0;
++}
++
++static const struct vdec_format *
++find_format(const struct vdec_format *fmts, u32 size, u32 pixfmt, u32 type)
++{
++ unsigned int i;
++
++ for (i = 0; i < size; i++) {
++ if (fmts[i].pixfmt == pixfmt)
++ break;
++ }
++
++ if (i == size || fmts[i].type != type)
++ return NULL;
++
++ return &fmts[i];
++}
++
++static const struct vdec_format *
++find_format_by_index(const struct vdec_format *fmts, u32 size, u32 index, u32 type)
++{
++ unsigned int i, k = 0;
++
++ if (index > size)
++ return NULL;
++
++ for (i = 0; i < size; i++) {
++ if (fmts[i].type != type)
++ continue;
++ if (k == index)
++ break;
++ k++;
++ }
++
++ if (i == size)
++ return NULL;
++
++ return &fmts[i];
++}
++
++static const struct vdec_format *
++vdec_try_fmt_common(const struct vdec_format *fmts, u32 size, struct v4l2_format *f)
++{
++ struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp;
++ struct v4l2_plane_pix_format *pfmt = pixmp->plane_fmt;
++ const struct vdec_format *fmt;
++
++ memset(pfmt[0].reserved, 0, sizeof(pfmt[0].reserved));
++ memset(pixmp->reserved, 0, sizeof(pixmp->reserved));
++
++ fmt = find_format(fmts, size, pixmp->pixelformat, f->type);
++ if (!fmt) {
++ if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
++ pixmp->pixelformat = V4L2_PIX_FMT_NV12M;
++ else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
++ pixmp->pixelformat = V4L2_PIX_FMT_H264;
++ else
++ return NULL;
++
++ fmt = find_format(fmts, size, pixmp->pixelformat, f->type);
++ pixmp->width = 1280;
++ pixmp->height = 720;
++ }
++
++ pixmp->width = clamp(pixmp->width, (u32)256, (u32)3840);
++ pixmp->height = clamp(pixmp->height, (u32)144, (u32)2160);
++
++ if (pixmp->field == V4L2_FIELD_ANY)
++ pixmp->field = V4L2_FIELD_NONE;
++
++ pixmp->num_planes = fmt->num_planes;
++ pixmp->flags = 0;
++
++ if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
++ memset(pfmt[1].reserved, 0, sizeof(pfmt[1].reserved));
++ pfmt[0].sizeimage = get_output_size(pixmp->width, pixmp->height);
++ pfmt[0].bytesperline = ALIGN(pixmp->width, 64);
++
++ pfmt[1].sizeimage = get_output_size(pixmp->width, pixmp->height) / 2;
++ pfmt[1].bytesperline = ALIGN(pixmp->width, 64);
++ } else {
++ pfmt[0].sizeimage = get_output_size(pixmp->width, pixmp->height);
++ pfmt[0].bytesperline = 0;
++ }
++
++
++ return fmt;
++}
++
++static int vdec_try_fmt(struct file *file, void *fh, struct v4l2_format *f)
++{
++ struct vdec_session *sess = container_of(file->private_data, struct vdec_session, fh);
++
++ vdec_try_fmt_common(sess->core->platform->formats,
++ sess->core->platform->num_formats, f);
++
++ return 0;
++}
++
++static int vdec_g_fmt(struct file *file, void *fh, struct v4l2_format *f)
++{
++ struct vdec_session *sess = container_of(file->private_data, struct vdec_session, fh);
++ const struct vdec_format *fmt = NULL;
++ struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp;
++
++ if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
++ fmt = sess->fmt_cap;
++ else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
++ fmt = sess->fmt_out;
++
++ pixmp->pixelformat = fmt->pixfmt;
++
++ if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
++ pixmp->width = sess->width;
++ pixmp->height = sess->height;
++ pixmp->colorspace = sess->colorspace;
++ pixmp->ycbcr_enc = sess->ycbcr_enc;
++ pixmp->quantization = sess->quantization;
++ pixmp->xfer_func = sess->xfer_func;
++ } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
++ pixmp->width = sess->width;
++ pixmp->height = sess->height;
++ }
++
++ vdec_try_fmt_common(sess->core->platform->formats, sess->core->platform->num_formats, f);
++
++ return 0;
++}
++
++static int vdec_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
++{
++ struct vdec_session *sess = container_of(file->private_data, struct vdec_session, fh);
++ struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp;
++ const struct vdec_format *formats = sess->core->platform->formats;
++ u32 num_formats = sess->core->platform->num_formats;
++ const struct vdec_format *fmt;
++ struct v4l2_pix_format_mplane orig_pixmp;
++ struct v4l2_format format;
++ u32 pixfmt_out = 0, pixfmt_cap = 0;
++
++ orig_pixmp = *pixmp;
++
++ fmt = vdec_try_fmt_common(formats, num_formats, f);
++
++ if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
++ pixfmt_out = pixmp->pixelformat;
++ pixfmt_cap = sess->fmt_cap->pixfmt;
++ } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
++ pixfmt_cap = pixmp->pixelformat;
++ pixfmt_out = sess->fmt_out->pixfmt;
++ }
++
++ memset(&format, 0, sizeof(format));
++
++ format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
++ format.fmt.pix_mp.pixelformat = pixfmt_out;
++ format.fmt.pix_mp.width = orig_pixmp.width;
++ format.fmt.pix_mp.height = orig_pixmp.height;
++ vdec_try_fmt_common(formats, num_formats, &format);
++
++ if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
++ sess->width = format.fmt.pix_mp.width;
++ sess->height = format.fmt.pix_mp.height;
++ sess->colorspace = pixmp->colorspace;
++ sess->ycbcr_enc = pixmp->ycbcr_enc;
++ sess->quantization = pixmp->quantization;
++ sess->xfer_func = pixmp->xfer_func;
++ }
++
++ memset(&format, 0, sizeof(format));
++
++ format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
++ format.fmt.pix_mp.pixelformat = pixfmt_cap;
++ format.fmt.pix_mp.width = orig_pixmp.width;
++ format.fmt.pix_mp.height = orig_pixmp.height;
++ vdec_try_fmt_common(formats, num_formats, &format);
++
++ sess->width = format.fmt.pix_mp.width;
++ sess->height = format.fmt.pix_mp.height;
++
++ if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
++ sess->fmt_out = fmt;
++ else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
++ sess->fmt_cap = fmt;
++
++ return 0;
++}
++
++static int vdec_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f)
++{
++ struct vdec_session *sess =
++ container_of(file->private_data, struct vdec_session, fh);
++ const struct vdec_platform *platform = sess->core->platform;
++ const struct vdec_format *fmt;
++
++ memset(f->reserved, 0, sizeof(f->reserved));
++
++ fmt = find_format_by_index(platform->formats, platform->num_formats,
++ f->index, f->type);
++ if (!fmt)
++ return -EINVAL;
++
++ f->pixelformat = fmt->pixfmt;
++
++ return 0;
++}
++
++static int vdec_enum_framesizes(struct file *file, void *fh,
++ struct v4l2_frmsizeenum *fsize)
++{
++ struct vdec_session *sess =
++ container_of(file->private_data, struct vdec_session, fh);
++ const struct vdec_format *formats = sess->core->platform->formats;
++ u32 num_formats = sess->core->platform->num_formats;
++ const struct vdec_format *fmt;
++
++ fmt = find_format(formats, num_formats, fsize->pixel_format,
++ V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
++ if (!fmt) {
++ fmt = find_format(formats, num_formats, fsize->pixel_format,
++ V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
++ if (!fmt)
++ return -EINVAL;
++ }
++
++ if (fsize->index)
++ return -EINVAL;
++
++ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
++
++ /* TODO: Store these constants in vdec_format */
++ fsize->stepwise.min_width = 256;
++ fsize->stepwise.max_width = 3840;
++ fsize->stepwise.step_width = 1;
++ fsize->stepwise.min_height = 144;
++ fsize->stepwise.max_height = 2160;
++ fsize->stepwise.step_height = 1;
++
++ return 0;
++}
++
++static int
++vdec_try_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *cmd)
++{
++ switch (cmd->cmd) {
++ case V4L2_DEC_CMD_STOP:
++ if (cmd->flags & V4L2_DEC_CMD_STOP_TO_BLACK)
++ return -EINVAL;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static int
++vdec_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *cmd)
++{
++ struct vdec_session *sess =
++ container_of(file->private_data, struct vdec_session, fh);
++ int ret;
++
++ ret = vdec_try_decoder_cmd(file, fh, cmd);
++ if (ret)
++ return ret;
++
++ mutex_lock(&sess->lock);
++
++ if (!(sess->streamon_out & sess->streamon_cap))
++ goto unlock;
++
++ esparser_queue_eos(sess);
++
++unlock:
++ mutex_unlock(&sess->lock);
++ return ret;
++}
++
++static int vdec_subscribe_event(struct v4l2_fh *fh,
++ const struct v4l2_event_subscription *sub)
++{
++ switch (sub->type) {
++ case V4L2_EVENT_EOS:
++ return v4l2_event_subscribe(fh, sub, 2, NULL);
++ default:
++ return -EINVAL;
++ }
++}
++
++static const struct v4l2_ioctl_ops vdec_ioctl_ops = {
++ .vidioc_querycap = vdec_querycap,
++ .vidioc_enum_fmt_vid_cap_mplane = vdec_enum_fmt,
++ .vidioc_enum_fmt_vid_out_mplane = vdec_enum_fmt,
++ .vidioc_s_fmt_vid_cap_mplane = vdec_s_fmt,
++ .vidioc_s_fmt_vid_out_mplane = vdec_s_fmt,
++ .vidioc_g_fmt_vid_cap_mplane = vdec_g_fmt,
++ .vidioc_g_fmt_vid_out_mplane = vdec_g_fmt,
++ .vidioc_try_fmt_vid_cap_mplane = vdec_try_fmt,
++ .vidioc_try_fmt_vid_out_mplane = vdec_try_fmt,
++ .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
++ .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
++ .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
++ .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
++ .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
++ .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
++ .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
++ .vidioc_streamon = v4l2_m2m_ioctl_streamon,
++ .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
++ .vidioc_enum_framesizes = vdec_enum_framesizes,
++ .vidioc_subscribe_event = vdec_subscribe_event,
++ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
++ .vidioc_try_decoder_cmd = vdec_try_decoder_cmd,
++ .vidioc_decoder_cmd = vdec_decoder_cmd,
++};
++
++static int m2m_queue_init(void *priv, struct vb2_queue *src_vq,
++ struct vb2_queue *dst_vq)
++{
++ struct vdec_session *sess = priv;
++ int ret;
++
++ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
++ src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
++ src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
++ src_vq->ops = &vdec_vb2_ops;
++ src_vq->mem_ops = &vb2_dma_contig_memops;
++ src_vq->drv_priv = sess;
++ src_vq->buf_struct_size = sizeof(struct dummy_buf);
++ src_vq->allow_zero_bytesused = 1;
++ src_vq->min_buffers_needed = 1;
++ src_vq->dev = sess->core->dev;
++ ret = vb2_queue_init(src_vq);
++ if (ret)
++ return ret;
++
++ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
++ dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
++ dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
++ dst_vq->ops = &vdec_vb2_ops;
++ dst_vq->mem_ops = &vb2_dma_contig_memops;
++ dst_vq->drv_priv = sess;
++ dst_vq->buf_struct_size = sizeof(struct dummy_buf);
++ dst_vq->allow_zero_bytesused = 1;
++ dst_vq->min_buffers_needed = 1;
++ dst_vq->dev = sess->core->dev;
++ ret = vb2_queue_init(dst_vq);
++ if (ret) {
++ vb2_queue_release(src_vq);
++ return ret;
++ }
++
++ return 0;
++}
++
++static int vdec_open(struct file *file)
++{
++ struct vdec_core *core = video_drvdata(file);
++ struct device *dev = core->dev;
++ const struct vdec_format *formats = core->platform->formats;
++ struct vdec_session *sess;
++ int ret;
++
++ mutex_lock(&core->lock);
++ if (core->cur_sess) {
++ mutex_unlock(&core->lock);
++ return -EBUSY;
++ }
++
++ sess = kzalloc(sizeof(*sess), GFP_KERNEL);
++ if (!sess) {
++ mutex_unlock(&core->lock);
++ return -ENOMEM;
++ }
++
++ core->cur_sess = sess;
++ mutex_unlock(&core->lock);
++
++ sess->core = core;
++ sess->fmt_cap = &formats[0];
++ sess->fmt_out = &formats[1];
++ sess->width = 1280;
++ sess->height = 720;
++ INIT_LIST_HEAD(&sess->bufs);
++ INIT_LIST_HEAD(&sess->bufs_recycle);
++ INIT_WORK(&sess->esparser_queue_work, esparser_queue_all_src);
++ spin_lock_init(&sess->bufs_spinlock);
++ mutex_init(&sess->lock);
++ mutex_init(&sess->codec_lock);
++ mutex_init(&sess->bufs_recycle_lock);
++
++ sess->m2m_dev = v4l2_m2m_init(&vdec_m2m_ops);
++ if (IS_ERR(sess->m2m_dev)) {
++ dev_err(dev, "Fail to v4l2_m2m_init\n");
++ ret = PTR_ERR(sess->m2m_dev);
++ goto err_free_sess;
++ }
++
++ sess->m2m_ctx = v4l2_m2m_ctx_init(sess->m2m_dev, sess, m2m_queue_init);
++ if (IS_ERR(sess->m2m_ctx)) {
++ dev_err(dev, "Fail to v4l2_m2m_ctx_init\n");
++ ret = PTR_ERR(sess->m2m_ctx);
++ goto err_m2m_release;
++ }
++
++ v4l2_fh_init(&sess->fh, core->vdev_dec);
++ v4l2_fh_add(&sess->fh);
++ sess->fh.m2m_ctx = sess->m2m_ctx;
++ file->private_data = &sess->fh;
++
++ return 0;
++
++err_m2m_release:
++ v4l2_m2m_release(sess->m2m_dev);
++err_free_sess:
++ kfree(sess);
++ return ret;
++}
++
++static int vdec_close(struct file *file)
++{
++ struct vdec_session *sess =
++ container_of(file->private_data, struct vdec_session, fh);
++ struct vdec_core *core = sess->core;
++
++ v4l2_m2m_ctx_release(sess->m2m_ctx);
++ v4l2_m2m_release(sess->m2m_dev);
++ v4l2_fh_del(&sess->fh);
++ v4l2_fh_exit(&sess->fh);
++ mutex_destroy(&sess->lock);
++
++ kfree(sess);
++ core->cur_sess = NULL;
++
++ return 0;
++}
++
++void vdec_dst_buf_done(struct vdec_session *sess, struct vb2_v4l2_buffer *vbuf)
++{
++ unsigned long flags;
++ struct vdec_buffer *tmp;
++ struct device *dev = sess->core->dev_dec;
++
++ spin_lock_irqsave(&sess->bufs_spinlock, flags);
++ if (list_empty(&sess->bufs)) {
++ dev_err(dev, "Buffer %u done but list is empty\n",
++ vbuf->vb2_buf.index);
++
++ v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
++ vdec_abort(sess);
++ goto unlock;
++ }
++
++ tmp = list_first_entry(&sess->bufs, struct vdec_buffer, list);
++
++ vbuf->vb2_buf.planes[0].bytesused = vdec_get_output_size(sess);
++ vbuf->vb2_buf.planes[1].bytesused = vdec_get_output_size(sess) / 2;
++ vbuf->vb2_buf.timestamp = tmp->timestamp;
++ vbuf->sequence = sess->sequence_cap++;
++
++ list_del(&tmp->list);
++ kfree(tmp);
++
++ if (sess->should_stop && list_empty(&sess->bufs)) {
++ const struct v4l2_event ev = { .type = V4L2_EVENT_EOS };
++ v4l2_event_queue_fh(&sess->fh, &ev);
++ vbuf->flags |= V4L2_BUF_FLAG_LAST;
++ }
++
++ v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
++
++unlock:
++ spin_unlock_irqrestore(&sess->bufs_spinlock, flags);
++
++ atomic_dec(&sess->esparser_queued_bufs);
++ /* Buffer done probably means the vififo got freed */
++ schedule_work(&sess->esparser_queue_work);
++}
++
++static void vdec_rm_first_ts(struct vdec_session *sess)
++{
++ unsigned long flags;
++ struct vdec_buffer *tmp;
++ struct device *dev = sess->core->dev_dec;
++
++ spin_lock_irqsave(&sess->bufs_spinlock, flags);
++ if (list_empty(&sess->bufs)) {
++ dev_err(dev, "Can't rm first timestamp: list empty\n");
++ goto unlock;
++ }
++
++ tmp = list_first_entry(&sess->bufs, struct vdec_buffer, list);
++ list_del(&tmp->list);
++ kfree(tmp);
++
++unlock:
++ spin_unlock_irqrestore(&sess->bufs_spinlock, flags);
++}
++
++void vdec_dst_buf_done_idx(struct vdec_session *sess, u32 buf_idx)
++{
++ struct vb2_v4l2_buffer *vbuf;
++ struct device *dev = sess->core->dev_dec;
++
++ vbuf = v4l2_m2m_dst_buf_remove_by_idx(sess->m2m_ctx, buf_idx);
++ if (!vbuf) {
++ dev_err(dev, "Buffer %u done but it doesn't exist in m2m_ctx\n",
++ buf_idx);
++ atomic_dec(&sess->esparser_queued_bufs);
++ vdec_rm_first_ts(sess);
++ return;
++ }
++
++ vdec_dst_buf_done(sess, vbuf);
++}
++
++/* Userspace will often queue timestamps that are not
++ * in chronological order. Rearrange them here.
++ */
++void vdec_add_ts_reorder(struct vdec_session *sess, u64 ts)
++{
++ struct vdec_buffer *new_buf, *tmp;
++ unsigned long flags;
++
++ new_buf = kmalloc(sizeof(*new_buf), GFP_KERNEL);
++ new_buf->timestamp = ts;
++ new_buf->index = -1;
++
++ spin_lock_irqsave(&sess->bufs_spinlock, flags);
++ if (list_empty(&sess->bufs))
++ goto add_core;
++
++ list_for_each_entry(tmp, &sess->bufs, list) {
++ if (ts < tmp->timestamp) {
++ list_add_tail(&new_buf->list, &tmp->list);
++ goto unlock;
++ }
++ }
++
++add_core:
++ list_add_tail(&new_buf->list, &sess->bufs);
++unlock:
++ spin_unlock_irqrestore(&sess->bufs_spinlock, flags);
++}
++
++void vdec_remove_ts(struct vdec_session *sess, u64 ts)
++{
++ struct vdec_buffer *tmp;
++ unsigned long flags;
++
++ spin_lock_irqsave(&sess->bufs_spinlock, flags);
++ list_for_each_entry(tmp, &sess->bufs, list) {
++ if (tmp->timestamp == ts) {
++ list_del(&tmp->list);
++ kfree(tmp);
++ goto unlock;
++ }
++ }
++ dev_warn(sess->core->dev_dec,
++ "Couldn't remove buffer with timestamp %llu from list\n", ts);
++
++unlock:
++ spin_unlock_irqrestore(&sess->bufs_spinlock, flags);
++}
++
++static const struct v4l2_file_operations vdec_fops = {
++ .owner = THIS_MODULE,
++ .open = vdec_open,
++ .release = vdec_close,
++ .unlocked_ioctl = video_ioctl2,
++ .poll = v4l2_m2m_fop_poll,
++ .mmap = v4l2_m2m_fop_mmap,
++#ifdef CONFIG_COMPAT
++ .compat_ioctl32 = v4l2_compat_ioctl32,
++#endif
++};
++
++static irqreturn_t vdec_isr(int irq, void *data)
++{
++ struct vdec_core *core = data;
++ struct vdec_session *sess = core->cur_sess;
++
++ return sess->fmt_out->codec_ops->isr(sess);
++}
++
++static irqreturn_t vdec_threaded_isr(int irq, void *data)
++{
++ struct vdec_core *core = data;
++ struct vdec_session *sess = core->cur_sess;
++
++ return sess->fmt_out->codec_ops->threaded_isr(sess);
++}
++
++static const struct of_device_id vdec_dt_match[] = {
++ { .compatible = "amlogic,meson-gxbb-vdec",
++ .data = &vdec_platform_gxbb },
++ { .compatible = "amlogic,meson-gxm-vdec",
++ .data = &vdec_platform_gxm },
++ { .compatible = "amlogic,meson-gxl-vdec",
++ .data = &vdec_platform_gxl },
++ {}
++};
++MODULE_DEVICE_TABLE(of, vdec_dt_match);
++
++static int vdec_probe(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct video_device *vdev;
++ struct vdec_core *core;
++ struct resource *r;
++ const struct of_device_id *of_id;
++ int irq;
++ int ret;
++
++ core = devm_kzalloc(dev, sizeof(*core), GFP_KERNEL);
++ if (!core) {
++ dev_err(dev, "No memory for devm_kzalloc\n");
++ return -ENOMEM;
++ }
++
++ core->dev = dev;
++ platform_set_drvdata(pdev, core);
++
++ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dos");
++ core->dos_base = devm_ioremap_resource(dev, r);
++ if (IS_ERR(core->dos_base)) {
++ dev_err(dev, "Couldn't remap DOS memory\n");
++ return PTR_ERR(core->dos_base);
++ }
++
++ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "esparser");
++ core->esparser_base = devm_ioremap_resource(dev, r);
++ if (IS_ERR(core->esparser_base)) {
++ dev_err(dev, "Couldn't remap ESPARSER memory\n");
++ return PTR_ERR(core->esparser_base);
++ }
++
++ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dmc");
++ core->dmc_base = devm_ioremap(dev, r->start, resource_size(r));
++ if (IS_ERR(core->dmc_base)) {
++ dev_err(dev, "Couldn't remap DMC memory\n");
++ return PTR_ERR(core->dmc_base);
++ }
++
++ core->regmap_ao = syscon_regmap_lookup_by_phandle(dev->of_node,
++ "amlogic,ao-sysctrl");
++ if (IS_ERR(core->regmap_ao)) {
++ dev_err(dev, "Couldn't regmap AO sysctrl\n");
++ return PTR_ERR(core->regmap_ao);
++ }
++
++ core->dos_parser_clk = devm_clk_get(dev, "dos_parser");
++ if (IS_ERR(core->dos_parser_clk)) {
++ dev_err(dev, "dos_parser clock request failed\n");
++ return PTR_ERR(core->dos_parser_clk);
++ }
++
++ core->dos_clk = devm_clk_get(dev, "dos");
++ if (IS_ERR(core->dos_clk)) {
++ dev_err(dev, "dos clock request failed\n");
++ return PTR_ERR(core->dos_clk);
++ }
++
++ core->vdec_1_clk = devm_clk_get(dev, "vdec_1");
++ if (IS_ERR(core->vdec_1_clk)) {
++ dev_err(dev, "vdec_1 clock request failed\n");
++ return PTR_ERR(core->vdec_1_clk);
++ }
++
++ core->vdec_hevc_clk = devm_clk_get(dev, "vdec_hevc");
++ if (IS_ERR(core->vdec_hevc_clk)) {
++ dev_err(dev, "vdec_hevc clock request failed\n");
++ return PTR_ERR(core->vdec_hevc_clk);
++ }
++
++ irq = platform_get_irq(pdev, 0);
++ if (irq < 0)
++ return irq;
++
++ ret = devm_request_threaded_irq(core->dev, irq, vdec_isr,
++ vdec_threaded_isr, IRQF_ONESHOT, "vdec", core);
++ if (ret)
++ return ret;
++
++ ret = esparser_init(pdev, core);
++ if (ret)
++ return ret;
++
++ ret = v4l2_device_register(dev, &core->v4l2_dev);
++ if (ret) {
++ dev_err(dev, "Couldn't register v4l2 device\n");
++ return -ENOMEM;
++ }
++
++ vdev = video_device_alloc();
++ if (!vdev) {
++ ret = -ENOMEM;
++ goto err_vdev_release;
++ }
++
++ strlcpy(vdev->name, "meson-video-decoder", sizeof(vdev->name));
++ vdev->release = video_device_release;
++ vdev->fops = &vdec_fops;
++ vdev->ioctl_ops = &vdec_ioctl_ops;
++ vdev->vfl_dir = VFL_DIR_M2M;
++ vdev->v4l2_dev = &core->v4l2_dev;
++ vdev->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING;
++
++ ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
++ if (ret) {
++ dev_err(dev, "Failed registering video device\n");
++ goto err_vdev_release;
++ }
++
++ of_id = of_match_node(vdec_dt_match, dev->of_node);
++ core->platform = of_id->data;
++ core->vdev_dec = vdev;
++ core->dev_dec = dev;
++ mutex_init(&core->lock);
++
++ video_set_drvdata(vdev, core);
++
++ return 0;
++
++err_vdev_release:
++ video_device_release(vdev);
++ return ret;
++}
++
++static int vdec_remove(struct platform_device *pdev)
++{
++ struct vdec_core *core = platform_get_drvdata(pdev);
++
++ video_unregister_device(core->vdev_dec);
++
++ return 0;
++}
++
++static struct platform_driver meson_vdec_driver = {
++ .probe = vdec_probe,
++ .remove = vdec_remove,
++ .driver = {
++ .name = "meson-vdec",
++ .of_match_table = vdec_dt_match,
++ },
++};
++module_platform_driver(meson_vdec_driver);
++
++MODULE_ALIAS("platform:meson-video-decoder");
++MODULE_DESCRIPTION("Meson video decoder driver for GXBB/GXL/GXM");
++MODULE_AUTHOR("Maxime Jourdan <maxi.jourdan@wanadoo.fr>");
++MODULE_LICENSE("GPL v2");
+diff --git a/drivers/media/platform/meson/vdec/vdec.h b/drivers/media/platform/meson/vdec/vdec.h
+new file mode 100644
+index 0000000..b97d6b2
+--- /dev/null
++++ b/drivers/media/platform/meson/vdec/vdec.h
+@@ -0,0 +1,152 @@
++/* SPDX-License-Identifier: GPL-2.0+ */
++/*
++ * Copyright (C) 2018 Maxime Jourdan <maxi.jourdan@wanadoo.fr>
++ */
++
++#ifndef __MESON_VDEC_CORE_H_
++#define __MESON_VDEC_CORE_H_
++
++#include <linux/regmap.h>
++#include <linux/list.h>
++#include <media/videobuf2-v4l2.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-device.h>
++
++#include "vdec_platform.h"
++
++#define REG_BUF_SIZE 21
++
++struct dummy_buf {
++ struct vb2_v4l2_buffer vb;
++ struct list_head list;
++};
++
++struct vdec_buffer {
++ struct list_head list;
++ s32 index;
++ u64 timestamp;
++};
++
++struct vdec_session;
++
++struct vdec_core {
++ void __iomem *dos_base;
++ void __iomem *esparser_base;
++ void __iomem *dmc_base;
++ struct regmap *regmap_ao;
++
++ struct device *dev;
++ struct device *dev_dec;
++ const struct vdec_platform *platform;
++
++ struct clk *dos_parser_clk;
++ struct clk *dos_clk;
++ struct clk *vdec_1_clk;
++ struct clk *vdec_hevc_clk;
++
++ struct reset_control *esparser_reset;
++
++ struct video_device *vdev_dec;
++ struct v4l2_device v4l2_dev;
++
++ struct vdec_session *cur_sess;
++ struct mutex lock;
++};
++
++/* Describes one of the VDECS (VDEC_1, VDEC_2, VDEC_HCODEC, VDEC_HEVC) */
++struct vdec_ops {
++ int (*start)(struct vdec_session *sess);
++ int (*stop)(struct vdec_session *sess);
++ void (*conf_esparser)(struct vdec_session *sess);
++ u32 (*vififo_level)(struct vdec_session *sess);
++};
++
++/* Describes one of the compression standard supported (H.264, HEVC..) */
++struct vdec_codec_ops {
++ int (*start)(struct vdec_session *sess);
++ int (*stop)(struct vdec_session *sess);
++ int (*load_extended_firmware)(struct vdec_session *sess, const u8 *data, u32 len);
++ u32 (*num_pending_bufs)(struct vdec_session *sess);
++ void (*notify_dst_buffer)(struct vdec_session *sess, struct vb2_buffer *vb);
++ irqreturn_t (*isr)(struct vdec_session *sess);
++ irqreturn_t (*threaded_isr)(struct vdec_session *sess);
++};
++
++/* Describes one of the format that can be decoded/encoded */
++struct vdec_format {
++ u32 pixfmt;
++ u32 num_planes;
++ u32 type;
++ u32 min_buffers;
++ u32 max_buffers;
++
++ struct vdec_ops *vdec_ops;
++ struct vdec_codec_ops *codec_ops;
++
++ char *firmware_path;
++};
++
++struct vdec_session {
++ struct vdec_core *core;
++
++ struct v4l2_fh fh;
++ struct v4l2_m2m_dev *m2m_dev;
++ struct v4l2_m2m_ctx *m2m_ctx;
++ struct mutex lock;
++ struct mutex codec_lock;
++
++ const struct vdec_format *fmt_out;
++ const struct vdec_format *fmt_cap;
++ u32 width;
++ u32 height;
++ u32 colorspace;
++ u8 ycbcr_enc;
++ u8 quantization;
++ u8 xfer_func;
++
++ u32 num_input_bufs;
++ u32 num_output_bufs;
++
++ /* Number of buffers currently queued into ESPARSER */
++ atomic_t esparser_queued_bufs;
++
++ /* Work for the ESPARSER to process src buffers */
++ struct work_struct esparser_queue_work;
++
++ /* Whether capture/output streaming are on */
++ unsigned int streamon_cap, streamon_out;
++
++ /* Capture sequence counter */
++ unsigned int sequence_cap;
++
++ /* Whether userspace signaled EOS via command, empty buffer or
++ * V4L2_BUF_FLAG_LAST
++ */
++ unsigned int should_stop;
++
++ /* Big contiguous area for the VIFIFO */
++ void *vififo_vaddr;
++ dma_addr_t vififo_paddr;
++ u32 vififo_size;
++
++ /* Buffers that need to be recycled by the HW */
++ struct list_head bufs_recycle;
++ struct mutex bufs_recycle_lock;
++
++ /* Buffers queued into the HW */
++ struct list_head bufs;
++ spinlock_t bufs_spinlock;
++
++ /* Codec private data */
++ void *priv;
++};
++
++u32 vdec_get_output_size(struct vdec_session *sess);
++void vdec_dst_buf_done_idx(struct vdec_session *sess, u32 buf_idx);
++void vdec_dst_buf_done(struct vdec_session *sess, struct vb2_v4l2_buffer *vbuf);
++void vdec_add_ts_reorder(struct vdec_session *sess, u64 ts);
++void vdec_remove_ts(struct vdec_session *sess, u64 ts);
++void vdec_queue_recycle(struct vdec_session *sess, struct vb2_buffer *vb);
++void vdec_abort(struct vdec_session *sess);
++
++#endif
+diff --git a/drivers/media/platform/meson/vdec/vdec_1.c b/drivers/media/platform/meson/vdec/vdec_1.c
+new file mode 100644
+index 0000000..e6593a6
+--- /dev/null
++++ b/drivers/media/platform/meson/vdec/vdec_1.c
+@@ -0,0 +1,266 @@
++// SPDX-License-Identifier: GPL-2.0+
++/*
++ * Copyright (C) 2018 Maxime Jourdan <maxi.jourdan@wanadoo.fr>
++ */
++
++#include <linux/firmware.h>
++#include <linux/clk.h>
++
++#include "vdec_1.h"
++
++/* AO Registers */
++#define AO_RTI_GEN_PWR_SLEEP0 0xe8
++#define AO_RTI_GEN_PWR_ISO0 0xec
++ #define GEN_PWR_VDEC_1 (BIT(3) | BIT(2))
++
++/* DOS Registers */
++#define ASSIST_MBOX1_CLR_REG 0x01d4
++#define ASSIST_MBOX1_MASK 0x01d8
++
++#define MPSR 0x0c04
++#define CPSR 0x0c84
++
++#define IMEM_DMA_CTRL 0x0d00
++#define IMEM_DMA_ADR 0x0d04
++#define IMEM_DMA_COUNT 0x0d08
++#define LMEM_DMA_CTRL 0x0d40
++
++#define MC_STATUS0 0x2424
++#define MC_CTRL1 0x242c
++
++#define DBLK_CTRL 0x2544
++#define DBLK_STATUS 0x254c
++
++#define GCLK_EN 0x260c
++#define MDEC_PIC_DC_CTRL 0x2638
++#define MDEC_PIC_DC_STATUS 0x263c
++
++#define DCAC_DMA_CTRL 0x3848
++
++#define DOS_SW_RESET0 0xfc00
++#define DOS_GCLK_EN0 0xfc04
++#define DOS_GEN_CTRL0 0xfc08
++#define DOS_MEM_PD_VDEC 0xfcc0
++#define DOS_VDEC_MCRCC_STALL_CTRL 0xfd00
++
++/* Stream Buffer (stbuf) regs (DOS) */
++#define POWER_CTL_VLD 0x3020
++#define VLD_MEM_VIFIFO_START_PTR 0x3100
++#define VLD_MEM_VIFIFO_CURR_PTR 0x3104
++#define VLD_MEM_VIFIFO_END_PTR 0x3108
++#define VLD_MEM_VIFIFO_CONTROL 0x3110
++ #define MEM_FIFO_CNT_BIT 16
++ #define MEM_FILL_ON_LEVEL BIT(10)
++ #define MEM_CTRL_EMPTY_EN BIT(2)
++ #define MEM_CTRL_FILL_EN BIT(1)
++#define VLD_MEM_VIFIFO_WP 0x3114
++#define VLD_MEM_VIFIFO_RP 0x3118
++#define VLD_MEM_VIFIFO_LEVEL 0x311c
++#define VLD_MEM_VIFIFO_BUF_CNTL 0x3120
++ #define MEM_BUFCTRL_MANUAL BIT(1)
++#define VLD_MEM_VIFIFO_WRAP_COUNT 0x3144
++
++#define MC_SIZE (4096 * 4)
++
++static int vdec_1_load_firmware(struct vdec_session *sess, const char* fwname)
++{
++ const struct firmware *fw;
++ struct vdec_core *core = sess->core;
++ struct device *dev = core->dev_dec;
++ struct vdec_codec_ops *codec_ops = sess->fmt_out->codec_ops;
++ static void *mc_addr;
++ static dma_addr_t mc_addr_map;
++ int ret;
++ u32 i = 1000;
++
++ ret = request_firmware(&fw, fwname, dev);
++ if (ret < 0)
++ return -EINVAL;
++
++ if (fw->size < MC_SIZE) {
++ dev_err(dev, "Firmware size %zu is too small. Expected %u.\n",
++ fw->size, MC_SIZE);
++ ret = -EINVAL;
++ goto release_firmware;
++ }
++
++ mc_addr = dma_alloc_coherent(core->dev, MC_SIZE, &mc_addr_map, GFP_KERNEL);
++ if (!mc_addr) {
++ dev_err(dev, "Failed allocating memory for firmware loading\n");
++ ret = -ENOMEM;
++ goto release_firmware;
++ }
++
++ memcpy(mc_addr, fw->data, MC_SIZE);
++
++ writel_relaxed(0, core->dos_base + MPSR);
++ writel_relaxed(0, core->dos_base + CPSR);
++
++ writel_relaxed(readl_relaxed(core->dos_base + MDEC_PIC_DC_CTRL) & ~(1<<31), core->dos_base + MDEC_PIC_DC_CTRL);
++
++ writel_relaxed(mc_addr_map, core->dos_base + IMEM_DMA_ADR);
++ writel_relaxed(MC_SIZE / 4, core->dos_base + IMEM_DMA_COUNT);
++ writel_relaxed((0x8000 | (7 << 16)), core->dos_base + IMEM_DMA_CTRL);
++
++ while (--i && readl(core->dos_base + IMEM_DMA_CTRL) & 0x8000) { }
++
++ if (i == 0) {
++ dev_err(dev, "Firmware load fail (DMA hang?)\n");
++ ret = -EINVAL;
++ goto free_mc;
++ }
++
++ if (codec_ops->load_extended_firmware)
++ codec_ops->load_extended_firmware(sess, fw->data + MC_SIZE, fw->size - MC_SIZE);
++
++free_mc:
++ dma_free_coherent(core->dev, MC_SIZE, mc_addr, mc_addr_map);
++release_firmware:
++ release_firmware(fw);
++ return ret;
++}
++
++int vdec_1_stbuf_power_up(struct vdec_session *sess) {
++ struct vdec_core *core = sess->core;
++
++ writel_relaxed(0, core->dos_base + VLD_MEM_VIFIFO_CONTROL);
++ writel_relaxed(0, core->dos_base + VLD_MEM_VIFIFO_WRAP_COUNT);
++ writel_relaxed(1 << 4, core->dos_base + POWER_CTL_VLD);
++
++ writel_relaxed(sess->vififo_paddr, core->dos_base + VLD_MEM_VIFIFO_START_PTR);
++ writel_relaxed(sess->vififo_paddr, core->dos_base + VLD_MEM_VIFIFO_CURR_PTR);
++ writel_relaxed(sess->vififo_paddr + sess->vififo_size - 8, core->dos_base + VLD_MEM_VIFIFO_END_PTR);
++
++ writel_relaxed(readl_relaxed(core->dos_base + VLD_MEM_VIFIFO_CONTROL) | 1, core->dos_base + VLD_MEM_VIFIFO_CONTROL);
++ writel_relaxed(readl_relaxed(core->dos_base + VLD_MEM_VIFIFO_CONTROL) & ~1, core->dos_base + VLD_MEM_VIFIFO_CONTROL);
++
++ writel_relaxed(MEM_BUFCTRL_MANUAL, core->dos_base + VLD_MEM_VIFIFO_BUF_CNTL);
++ writel_relaxed(sess->vififo_paddr, core->dos_base + VLD_MEM_VIFIFO_WP);
++
++ writel_relaxed(readl_relaxed(core->dos_base + VLD_MEM_VIFIFO_BUF_CNTL) | 1, core->dos_base + VLD_MEM_VIFIFO_BUF_CNTL);
++ writel_relaxed(readl_relaxed(core->dos_base + VLD_MEM_VIFIFO_BUF_CNTL) & ~1, core->dos_base + VLD_MEM_VIFIFO_BUF_CNTL);
++
++ writel_relaxed(readl_relaxed(core->dos_base + VLD_MEM_VIFIFO_CONTROL) | (0x11 << MEM_FIFO_CNT_BIT) | MEM_FILL_ON_LEVEL | MEM_CTRL_FILL_EN | MEM_CTRL_EMPTY_EN, core->dos_base + VLD_MEM_VIFIFO_CONTROL);
++
++ return 0;
++}
++
++static void vdec_1_conf_esparser(struct vdec_session *sess)
++{
++ struct vdec_core *core = sess->core;
++
++ /* VDEC_1 specific ESPARSER stuff */
++ writel_relaxed(0, core->dos_base + DOS_GEN_CTRL0); // set vififo_vbuf_rp_sel=>vdec
++ writel_relaxed(1, core->dos_base + VLD_MEM_VIFIFO_BUF_CNTL);
++ writel_relaxed(readl_relaxed(core->dos_base + VLD_MEM_VIFIFO_BUF_CNTL) & ~1, core->dos_base + VLD_MEM_VIFIFO_BUF_CNTL);
++}
++
++static u32 vdec_1_vififo_level(struct vdec_session *sess)
++{
++ struct vdec_core *core = sess->core;
++
++ return readl_relaxed(core->dos_base + VLD_MEM_VIFIFO_LEVEL);
++}
++
++static int vdec_1_start(struct vdec_session *sess)
++{
++ int ret;
++ struct vdec_core *core = sess->core;
++ struct vdec_codec_ops *codec_ops = sess->fmt_out->codec_ops;
++
++ clk_set_rate(core->vdec_1_clk, 666666666);
++ ret = clk_prepare_enable(core->vdec_1_clk);
++ if (ret)
++ return ret;
++
++ regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
++ GEN_PWR_VDEC_1, 0);
++ udelay(10);
++
++ /* Reset VDEC1 */
++ writel_relaxed(0xfffffffc, core->dos_base + DOS_SW_RESET0);
++ writel_relaxed(0x00000000, core->dos_base + DOS_SW_RESET0);
++
++ writel_relaxed(0x3ff, core->dos_base + DOS_GCLK_EN0);
++
++ /* VDEC Memories */
++ writel_relaxed(0x00000000, core->dos_base + DOS_MEM_PD_VDEC);
++ /* Remove VDEC1 Isolation */
++ regmap_write(core->regmap_ao, AO_RTI_GEN_PWR_ISO0, 0x00000000);
++ /* Reset DOS top registers */
++ writel_relaxed(0x00000000, core->dos_base + DOS_VDEC_MCRCC_STALL_CTRL);
++
++ writel_relaxed(0x3ff, core->dos_base + GCLK_EN);
++ writel_relaxed(readl_relaxed(core->dos_base + MDEC_PIC_DC_CTRL) & ~(1<<31), core->dos_base + MDEC_PIC_DC_CTRL);
++
++ vdec_1_stbuf_power_up(sess);
++
++ ret = vdec_1_load_firmware(sess, sess->fmt_out->firmware_path);
++ if (ret) {
++ clk_disable_unprepare(core->vdec_1_clk);
++ regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
++ GEN_PWR_VDEC_1, GEN_PWR_VDEC_1);
++ return ret;
++ }
++
++ codec_ops->start(sess);
++
++ /* Enable firmware processor */
++ writel_relaxed(1, core->dos_base + MPSR);
++
++ /* Let the firmware settle */
++ udelay(10);
++
++ return 0;
++}
++
++static int vdec_1_stop(struct vdec_session *sess)
++{
++ struct vdec_core *core = sess->core;
++ struct vdec_codec_ops *codec_ops = sess->fmt_out->codec_ops;
++
++ writel_relaxed(0, core->dos_base + MPSR);
++ writel_relaxed(0, core->dos_base + CPSR);
++
++ codec_ops->stop(sess);
++
++ while (readl_relaxed(core->dos_base + IMEM_DMA_CTRL) & 0x8000) { }
++
++ writel_relaxed((1<<12)|(1<<11), core->dos_base + DOS_SW_RESET0);
++ writel_relaxed(0, core->dos_base + DOS_SW_RESET0);
++ readl_relaxed(core->dos_base + DOS_SW_RESET0);
++
++ writel_relaxed(0, core->dos_base + ASSIST_MBOX1_MASK);
++
++ writel_relaxed(readl_relaxed(core->dos_base + MDEC_PIC_DC_CTRL) | 1, core->dos_base + MDEC_PIC_DC_CTRL);
++ writel_relaxed(readl_relaxed(core->dos_base + MDEC_PIC_DC_CTRL) & ~1, core->dos_base + MDEC_PIC_DC_CTRL);
++ readl_relaxed(core->dos_base + MDEC_PIC_DC_STATUS);
++
++ writel_relaxed(3, core->dos_base + DBLK_CTRL);
++ writel_relaxed(0, core->dos_base + DBLK_CTRL);
++ readl_relaxed(core->dos_base + DBLK_STATUS);
++
++ writel_relaxed(readl_relaxed(core->dos_base + MC_CTRL1) | 0x9, core->dos_base + MC_CTRL1);
++ writel_relaxed(readl_relaxed(core->dos_base + MC_CTRL1) & ~0x9, core->dos_base + MC_CTRL1);
++ readl_relaxed(core->dos_base + MC_STATUS0);
++
++ while (readl_relaxed(core->dos_base + DCAC_DMA_CTRL) & 0x8000) { }
++
++ /* enable vdec1 isolation */
++ regmap_write(core->regmap_ao, AO_RTI_GEN_PWR_ISO0, 0xc0);
++ /* power off vdec1 memories */
++ writel(0xffffffffUL, core->dos_base + DOS_MEM_PD_VDEC);
++ regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
++ GEN_PWR_VDEC_1, GEN_PWR_VDEC_1);
++
++ clk_disable_unprepare(core->vdec_1_clk);
++
++ return 0;
++}
++
++struct vdec_ops vdec_1_ops = {
++ .start = vdec_1_start,
++ .stop = vdec_1_stop,
++ .conf_esparser = vdec_1_conf_esparser,
++ .vififo_level = vdec_1_vififo_level,
++};
+\ No newline at end of file
+diff --git a/drivers/media/platform/meson/vdec/vdec_1.h b/drivers/media/platform/meson/vdec/vdec_1.h
+new file mode 100644
+index 0000000..b6c8b41
+--- /dev/null
++++ b/drivers/media/platform/meson/vdec/vdec_1.h
+@@ -0,0 +1,13 @@
++/* SPDX-License-Identifier: GPL-2.0+ */
++/*
++ * Copyright (C) 2018 Maxime Jourdan <maxi.jourdan@wanadoo.fr>
++ */
++
++#ifndef __MESON_VDEC_VDEC_1_H_
++#define __MESON_VDEC_VDEC_1_H_
++
++#include "vdec.h"
++
++extern struct vdec_ops vdec_1_ops;
++
++#endif
+\ No newline at end of file
+diff --git a/drivers/media/platform/meson/vdec/vdec_hevc.c b/drivers/media/platform/meson/vdec/vdec_hevc.c
+new file mode 100644
+index 0000000..4f6e056
+--- /dev/null
++++ b/drivers/media/platform/meson/vdec/vdec_hevc.c
+@@ -0,0 +1,188 @@
++// SPDX-License-Identifier: GPL-2.0+
++/*
++ * Copyright (C) 2018 Maxime Jourdan <maxi.jourdan@wanadoo.fr>
++ */
++
++#include <linux/firmware.h>
++#include <linux/clk.h>
++
++#include "vdec_1.h"
++#include "hevc_regs.h"
++
++/* AO Registers */
++#define AO_RTI_GEN_PWR_SLEEP0 0xe8
++#define AO_RTI_GEN_PWR_ISO0 0xec
++ #define GEN_PWR_VDEC_HEVC (BIT(7) | BIT(6))
++
++/* DOS Registers */
++#define ASSIST_MBOX1_CLR_REG 0x01d4
++#define ASSIST_MBOX1_MASK 0x01d8
++
++#define DOS_GEN_CTRL0 0xfc08
++#define DOS_SW_RESET3 0xfcd0
++#define DOS_MEM_PD_HEVC 0xfccc
++#define DOS_GCLK_EN3 0xfcd4
++
++#define MC_SIZE (4096 * 4)
++
++static int vdec_hevc_load_firmware(struct vdec_session *sess, const char* fwname)
++{
++ struct vdec_core *core = sess->core;
++ struct device *dev = core->dev_dec;
++ const struct firmware *fw;
++ static void *mc_addr;
++ static dma_addr_t mc_addr_map;
++ int ret;
++ u32 i = 100;
++
++ ret = request_firmware(&fw, fwname, dev);
++ if (ret < 0) {
++ dev_err(dev, "Unable to request firmware %s\n", fwname);
++ return ret;
++ }
++
++ if (fw->size < MC_SIZE) {
++ dev_err(dev, "Firmware size %zu is too small. Expected %u.\n",
++ fw->size, MC_SIZE);
++ ret = -EINVAL;
++ goto release_firmware;
++ }
++
++ mc_addr = dma_alloc_coherent(core->dev, MC_SIZE, &mc_addr_map, GFP_KERNEL);
++ if (!mc_addr) {
++ dev_err(dev, "Failed allocating memory for firmware loading\n");
++ ret = -ENOMEM;
++ goto release_firmware;
++ }
++
++ memcpy(mc_addr, fw->data, MC_SIZE);
++
++ writel_relaxed(0, core->dos_base + HEVC_MPSR);
++ writel_relaxed(0, core->dos_base + HEVC_CPSR);
++
++ writel_relaxed(mc_addr_map, core->dos_base + HEVC_IMEM_DMA_ADR);
++ writel_relaxed(MC_SIZE / 4, core->dos_base + HEVC_IMEM_DMA_COUNT);
++ writel_relaxed((0x8000 | (7 << 16)), core->dos_base + HEVC_IMEM_DMA_CTRL);
++
++ while (--i && readl(core->dos_base + HEVC_IMEM_DMA_CTRL) & 0x8000) { }
++
++ if (i == 0) {
++ dev_err(dev, "Firmware load fail (DMA hang?)\n");
++ ret = -ENODEV;
++ }
++
++ dma_free_coherent(core->dev, MC_SIZE, mc_addr, mc_addr_map);
++release_firmware:
++ release_firmware(fw);
++ return ret;
++}
++
++static void vdec_hevc_stbuf_init(struct vdec_session *sess)
++{
++ struct vdec_core *core = sess->core;
++
++ writel_relaxed(readl_relaxed(core->dos_base + HEVC_STREAM_CONTROL) & ~1, core->dos_base + HEVC_STREAM_CONTROL);
++ writel_relaxed(sess->vififo_paddr, core->dos_base + HEVC_STREAM_START_ADDR);
++ writel_relaxed(sess->vififo_paddr + sess->vififo_size, core->dos_base + HEVC_STREAM_END_ADDR);
++ writel_relaxed(sess->vififo_paddr, core->dos_base + HEVC_STREAM_RD_PTR);
++ writel_relaxed(sess->vififo_paddr, core->dos_base + HEVC_STREAM_WR_PTR);
++}
++
++/* VDEC_HEVC specific ESPARSER configuration */
++static void vdec_hevc_conf_esparser(struct vdec_session *sess)
++{
++ struct vdec_core *core = sess->core;
++
++ /* set vififo_vbuf_rp_sel=>vdec_hevc */
++ writel_relaxed(3 << 1, core->dos_base + DOS_GEN_CTRL0);
++ writel_relaxed(readl_relaxed(core->dos_base + HEVC_STREAM_CONTROL) | (1 << 3), core->dos_base + HEVC_STREAM_CONTROL);
++ writel_relaxed(readl_relaxed(core->dos_base + HEVC_STREAM_CONTROL) | 1, core->dos_base + HEVC_STREAM_CONTROL);
++ writel_relaxed(readl_relaxed(core->dos_base + HEVC_STREAM_FIFO_CTL) | (1 << 29), core->dos_base + HEVC_STREAM_FIFO_CTL);
++}
++
++static u32 vdec_hevc_vififo_level(struct vdec_session *sess)
++{
++ return readl_relaxed(sess->core->dos_base + HEVC_STREAM_LEVEL);
++}
++
++static int vdec_hevc_stop(struct vdec_session *sess)
++{
++ struct vdec_core *core = sess->core;
++ struct vdec_codec_ops *codec_ops = sess->fmt_out->codec_ops;
++
++ /* Disable interrupt */
++ writel_relaxed(0, core->dos_base + HEVC_ASSIST_MBOX1_MASK);
++ /* Disable firmware processor */
++ writel_relaxed(0, core->dos_base + HEVC_MPSR);
++
++ codec_ops->stop(sess);
++
++ /* Enable VDEC_HEVC Isolation */
++ regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_ISO0, 0xc00, 0xc00);
++
++ /* VDEC_HEVC Memories */
++ writel_relaxed(0xffffffffUL, core->dos_base + DOS_MEM_PD_HEVC);
++
++ regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
++ GEN_PWR_VDEC_HEVC, GEN_PWR_VDEC_HEVC);
++
++ clk_disable_unprepare(core->vdec_hevc_clk);
++
++ return 0;
++}
++
++static int vdec_hevc_start(struct vdec_session *sess)
++{
++ int ret;
++ struct vdec_core *core = sess->core;
++ struct vdec_codec_ops *codec_ops = sess->fmt_out->codec_ops;
++
++ clk_set_rate(core->vdec_hevc_clk, 666666666);
++ ret = clk_prepare_enable(core->vdec_hevc_clk);
++ if (ret)
++ return ret;
++
++ regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
++ GEN_PWR_VDEC_HEVC, 0);
++ udelay(10);
++
++ /* Reset VDEC_HEVC*/
++ writel_relaxed(0xffffffff, core->dos_base + DOS_SW_RESET3);
++ writel_relaxed(0x00000000, core->dos_base + DOS_SW_RESET3);
++
++ writel_relaxed(0xffffffff, core->dos_base + DOS_GCLK_EN3);
++
++ /* VDEC_HEVC Memories */
++ writel_relaxed(0x00000000, core->dos_base + DOS_MEM_PD_HEVC);
++
++ /* Remove VDEC_HEVC Isolation */
++ regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_ISO0, 0xc00, 0);
++
++ writel_relaxed(0xffffffff, core->dos_base + DOS_SW_RESET3);
++ writel_relaxed(0x00000000, core->dos_base + DOS_SW_RESET3);
++
++ vdec_hevc_stbuf_init(sess);
++
++ ret = vdec_hevc_load_firmware(sess, sess->fmt_out->firmware_path);
++ if (ret) {
++ vdec_hevc_stop(sess);
++ return ret;
++ }
++
++ codec_ops->start(sess);
++
++ writel_relaxed((1<<12)|(1<<11), core->dos_base + DOS_SW_RESET3);
++ writel_relaxed(0, core->dos_base + DOS_SW_RESET3);
++ readl_relaxed(core->dos_base + DOS_SW_RESET3);
++
++ writel_relaxed(1, core->dos_base + HEVC_MPSR);
++
++ return 0;
++}
++
++struct vdec_ops vdec_hevc_ops = {
++ .start = vdec_hevc_start,
++ .stop = vdec_hevc_stop,
++ .conf_esparser = vdec_hevc_conf_esparser,
++ .vififo_level = vdec_hevc_vififo_level,
++};
+\ No newline at end of file
+diff --git a/drivers/media/platform/meson/vdec/vdec_hevc.h b/drivers/media/platform/meson/vdec/vdec_hevc.h
+new file mode 100644
+index 0000000..a90529c
+--- /dev/null
++++ b/drivers/media/platform/meson/vdec/vdec_hevc.h
+@@ -0,0 +1,22 @@
++/*
++ * Copyright (C) 2018 Maxime Jourdan <maxi.jourdan@wanadoo.fr>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 and
++ * only version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ */
++
++#ifndef __MESON_VDEC_VDEC_HEVC_H_
++#define __MESON_VDEC_VDEC_HEVC_H_
++
++#include "vdec.h"
++
++extern struct vdec_ops vdec_hevc_ops;
++
++#endif
+\ No newline at end of file
+diff --git a/drivers/media/platform/meson/vdec/vdec_platform.c b/drivers/media/platform/meson/vdec/vdec_platform.c
+new file mode 100644
+index 0000000..92b7e45
+--- /dev/null
++++ b/drivers/media/platform/meson/vdec/vdec_platform.c
+@@ -0,0 +1,273 @@
++// SPDX-License-Identifier: GPL-2.0+
++/*
++ * Copyright (C) 2018 Maxime Jourdan <maxi.jourdan@wanadoo.fr>
++ */
++
++#include "vdec_platform.h"
++#include "vdec.h"
++
++#include "vdec_1.h"
++#include "vdec_hevc.h"
++#include "codec_mpeg12.h"
++#include "codec_mpeg4.h"
++#include "codec_mjpeg.h"
++#include "codec_h264.h"
++#include "codec_hevc.h"
++
++static const struct vdec_format vdec_formats_gxbb[] = {
++ {
++ .pixfmt = V4L2_PIX_FMT_NV12M,
++ .num_planes = 2,
++ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
++ }, {
++ .pixfmt = V4L2_PIX_FMT_H264,
++ .num_planes = 1,
++ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
++ .min_buffers = 16,
++ .max_buffers = 32,
++ .vdec_ops = &vdec_1_ops,
++ .codec_ops = &codec_h264_ops,
++ .firmware_path = "meson/gxbb/vh264_mc",
++ }, {
++ .pixfmt = V4L2_PIX_FMT_HEVC,
++ .num_planes = 1,
++ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
++ .min_buffers = 16,
++ .max_buffers = 24,
++ .vdec_ops = &vdec_hevc_ops,
++ .codec_ops = &codec_hevc_ops,
++ .firmware_path = "meson/gx/vh265_mc",
++ }, {
++ .pixfmt = V4L2_PIX_FMT_MPEG1,
++ .num_planes = 1,
++ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
++ .min_buffers = 8,
++ .max_buffers = 8,
++ .vdec_ops = &vdec_1_ops,
++ .codec_ops = &codec_mpeg12_ops,
++ .firmware_path = "meson/gx/vmpeg12_mc",
++ }, {
++ .pixfmt = V4L2_PIX_FMT_MPEG2,
++ .num_planes = 1,
++ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
++ .min_buffers = 8,
++ .max_buffers = 8,
++ .vdec_ops = &vdec_1_ops,
++ .codec_ops = &codec_mpeg12_ops,
++ .firmware_path = "meson/gx/vmpeg12_mc",
++ }, {
++ .pixfmt = V4L2_PIX_FMT_MPEG4,
++ .num_planes = 1,
++ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
++ .min_buffers = 8,
++ .max_buffers = 8,
++ .vdec_ops = &vdec_1_ops,
++ .codec_ops = &codec_mpeg4_ops,
++ .firmware_path = "meson/gx/vmpeg4_mc_5",
++ }, {
++ .pixfmt = V4L2_PIX_FMT_H263,
++ .num_planes = 1,
++ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
++ .min_buffers = 8,
++ .max_buffers = 8,
++ .vdec_ops = &vdec_1_ops,
++ .codec_ops = &codec_mpeg4_ops,
++ .firmware_path = "meson/gx/h263_mc",
++ }, {
++ .pixfmt = V4L2_PIX_FMT_XVID,
++ .num_planes = 1,
++ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
++ .min_buffers = 8,
++ .max_buffers = 8,
++ .vdec_ops = &vdec_1_ops,
++ .codec_ops = &codec_mpeg4_ops,
++ .firmware_path = "meson/gx/vmpeg4_mc_5",
++ }, {
++ .pixfmt = V4L2_PIX_FMT_MJPEG,
++ .num_planes = 1,
++ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
++ .min_buffers = 4,
++ .max_buffers = 4,
++ .vdec_ops = &vdec_1_ops,
++ .codec_ops = &codec_mjpeg_ops,
++ .firmware_path = "meson/gx/vmjpeg_mc",
++ },
++};
++
++static const struct vdec_format vdec_formats_gxl[] = {
++ {
++ .pixfmt = V4L2_PIX_FMT_NV12M,
++ .num_planes = 2,
++ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
++ }, {
++ .pixfmt = V4L2_PIX_FMT_H264,
++ .num_planes = 1,
++ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
++ .min_buffers = 16,
++ .max_buffers = 32,
++ .vdec_ops = &vdec_1_ops,
++ .codec_ops = &codec_h264_ops,
++ .firmware_path = "meson/gxl/vh264_mc",
++ }, {
++ .pixfmt = V4L2_PIX_FMT_HEVC,
++ .num_planes = 1,
++ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
++ .min_buffers = 16,
++ .max_buffers = 24,
++ .vdec_ops = &vdec_hevc_ops,
++ .codec_ops = &codec_hevc_ops,
++ .firmware_path = "meson/gx/vh265_mc",
++ }, {
++ .pixfmt = V4L2_PIX_FMT_MPEG1,
++ .num_planes = 1,
++ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
++ .min_buffers = 8,
++ .max_buffers = 8,
++ .vdec_ops = &vdec_1_ops,
++ .codec_ops = &codec_mpeg12_ops,
++ .firmware_path = "meson/gx/vmpeg12_mc",
++ }, {
++ .pixfmt = V4L2_PIX_FMT_MPEG2,
++ .num_planes = 1,
++ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
++ .min_buffers = 8,
++ .max_buffers = 8,
++ .vdec_ops = &vdec_1_ops,
++ .codec_ops = &codec_mpeg12_ops,
++ .firmware_path = "meson/gx/vmpeg12_mc",
++ }, {
++ .pixfmt = V4L2_PIX_FMT_MPEG4,
++ .num_planes = 1,
++ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
++ .min_buffers = 8,
++ .max_buffers = 8,
++ .vdec_ops = &vdec_1_ops,
++ .codec_ops = &codec_mpeg4_ops,
++ .firmware_path = "meson/gx/vmpeg4_mc_5",
++ }, {
++ .pixfmt = V4L2_PIX_FMT_H263,
++ .num_planes = 1,
++ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
++ .min_buffers = 8,
++ .max_buffers = 8,
++ .vdec_ops = &vdec_1_ops,
++ .codec_ops = &codec_mpeg4_ops,
++ .firmware_path = "meson/gx/h263_mc",
++ }, {
++ .pixfmt = V4L2_PIX_FMT_XVID,
++ .num_planes = 1,
++ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
++ .min_buffers = 8,
++ .max_buffers = 8,
++ .vdec_ops = &vdec_1_ops,
++ .codec_ops = &codec_mpeg4_ops,
++ .firmware_path = "meson/gx/vmpeg4_mc_5",
++ }, {
++ .pixfmt = V4L2_PIX_FMT_MJPEG,
++ .num_planes = 1,
++ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
++ .min_buffers = 4,
++ .max_buffers = 4,
++ .vdec_ops = &vdec_1_ops,
++ .codec_ops = &codec_mjpeg_ops,
++ .firmware_path = "meson/gx/vmjpeg_mc",
++ },
++};
++
++static const struct vdec_format vdec_formats_gxm[] = {
++ {
++ .pixfmt = V4L2_PIX_FMT_NV12M,
++ .num_planes = 2,
++ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
++ }, {
++ .pixfmt = V4L2_PIX_FMT_H264,
++ .num_planes = 1,
++ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
++ .min_buffers = 16,
++ .max_buffers = 32,
++ .vdec_ops = &vdec_1_ops,
++ .codec_ops = &codec_h264_ops,
++ .firmware_path = "meson/gxm/vh264_mc",
++ }, {
++ .pixfmt = V4L2_PIX_FMT_HEVC,
++ .num_planes = 1,
++ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
++ .min_buffers = 16,
++ .max_buffers = 24,
++ .vdec_ops = &vdec_hevc_ops,
++ .codec_ops = &codec_hevc_ops,
++ .firmware_path = "meson/gx/vh265_mc",
++ }, {
++ .pixfmt = V4L2_PIX_FMT_MPEG1,
++ .num_planes = 1,
++ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
++ .min_buffers = 8,
++ .max_buffers = 8,
++ .vdec_ops = &vdec_1_ops,
++ .codec_ops = &codec_mpeg12_ops,
++ .firmware_path = "meson/gx/vmpeg12_mc",
++ }, {
++ .pixfmt = V4L2_PIX_FMT_MPEG2,
++ .num_planes = 1,
++ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
++ .min_buffers = 8,
++ .max_buffers = 8,
++ .vdec_ops = &vdec_1_ops,
++ .codec_ops = &codec_mpeg12_ops,
++ .firmware_path = "meson/gx/vmpeg12_mc",
++ }, {
++ .pixfmt = V4L2_PIX_FMT_MPEG4,
++ .num_planes = 1,
++ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
++ .min_buffers = 8,
++ .max_buffers = 8,
++ .vdec_ops = &vdec_1_ops,
++ .codec_ops = &codec_mpeg4_ops,
++ .firmware_path = "meson/gx/vmpeg4_mc_5",
++ }, {
++ .pixfmt = V4L2_PIX_FMT_H263,
++ .num_planes = 1,
++ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
++ .min_buffers = 8,
++ .max_buffers = 8,
++ .vdec_ops = &vdec_1_ops,
++ .codec_ops = &codec_mpeg4_ops,
++ .firmware_path = "meson/gx/h263_mc",
++ }, {
++ .pixfmt = V4L2_PIX_FMT_XVID,
++ .num_planes = 1,
++ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
++ .min_buffers = 8,
++ .max_buffers = 8,
++ .vdec_ops = &vdec_1_ops,
++ .codec_ops = &codec_mpeg4_ops,
++ .firmware_path = "meson/gx/vmpeg4_mc_5",
++ }, {
++ .pixfmt = V4L2_PIX_FMT_MJPEG,
++ .num_planes = 1,
++ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
++ .min_buffers = 4,
++ .max_buffers = 4,
++ .vdec_ops = &vdec_1_ops,
++ .codec_ops = &codec_mjpeg_ops,
++ .firmware_path = "meson/gx/vmjpeg_mc",
++ },
++};
++
++const struct vdec_platform vdec_platform_gxbb = {
++ .formats = vdec_formats_gxbb,
++ .num_formats = ARRAY_SIZE(vdec_formats_gxbb),
++ .revision = VDEC_REVISION_GXBB,
++};
++
++const struct vdec_platform vdec_platform_gxl = {
++ .formats = vdec_formats_gxl,
++ .num_formats = ARRAY_SIZE(vdec_formats_gxl),
++ .revision = VDEC_REVISION_GXL,
++};
++
++const struct vdec_platform vdec_platform_gxm = {
++ .formats = vdec_formats_gxm,
++ .num_formats = ARRAY_SIZE(vdec_formats_gxm),
++ .revision = VDEC_REVISION_GXM,
++};
+\ No newline at end of file
+diff --git a/drivers/media/platform/meson/vdec/vdec_platform.h b/drivers/media/platform/meson/vdec/vdec_platform.h
+new file mode 100644
+index 0000000..d19fad3
+--- /dev/null
++++ b/drivers/media/platform/meson/vdec/vdec_platform.h
+@@ -0,0 +1,29 @@
++/* SPDX-License-Identifier: GPL-2.0+ */
++/*
++ * Copyright (C) 2018 Maxime Jourdan <maxi.jourdan@wanadoo.fr>
++ */
++
++#ifndef __MESON_VDEC_PLATFORM_H_
++#define __MESON_VDEC_PLATFORM_H_
++
++#include "vdec.h"
++
++struct vdec_format;
++
++enum vdec_revision {
++ VDEC_REVISION_GXBB,
++ VDEC_REVISION_GXL,
++ VDEC_REVISION_GXM,
++};
++
++struct vdec_platform {
++ const struct vdec_format *formats;
++ const u32 num_formats;
++ enum vdec_revision revision;
++};
++
++extern const struct vdec_platform vdec_platform_gxbb;
++extern const struct vdec_platform vdec_platform_gxm;
++extern const struct vdec_platform vdec_platform_gxl;
++
++#endif
+\ No newline at end of file
diff --git a/testing/linux-amlogic/0027-ARM64-dts-meson-gx-add-vdec-entry.patch b/testing/linux-amlogic/0027-ARM64-dts-meson-gx-add-vdec-entry.patch
new file mode 100644
index 0000000000..b5cfc9c3bf
--- /dev/null
+++ b/testing/linux-amlogic/0027-ARM64-dts-meson-gx-add-vdec-entry.patch
@@ -0,0 +1,35 @@
+From 862bb56b65dccfa92e29d58f006f7bd22f68186e Mon Sep 17 00:00:00 2001
+From: Maxime Jourdan <maxi.jourdan@wanadoo.fr>
+Date: Thu, 26 Jul 2018 21:51:54 +0200
+Subject: [PATCH] ARM64: dts: meson-gx: add vdec entry
+
+Add the video decoder dts entry
+---
+ arch/arm64/boot/dts/amlogic/meson-gx.dtsi | 14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+
+diff --git a/arch/arm64/boot/dts/amlogic/meson-gx.dtsi b/arch/arm64/boot/dts/amlogic/meson-gx.dtsi
+index 7296b4f..739dc49 100644
+--- a/arch/arm64/boot/dts/amlogic/meson-gx.dtsi
++++ b/arch/arm64/boot/dts/amlogic/meson-gx.dtsi
+@@ -535,6 +535,20 @@
+ };
+ };
+
++ vdec: video-decoder@0xd0050000 {
++ compatible = "amlogic,meson-gx-vdec";
++ reg = <0x0 0xc8820000 0x0 0x10000
++ 0x0 0xc110a580 0x0 0xe4
++ 0x0 0xc8838000 0x0 0x60>;
++ reg-names = "dos", "esparser", "dmc";
++
++ interrupts = <GIC_SPI 44 IRQ_TYPE_EDGE_RISING
++ GIC_SPI 32 IRQ_TYPE_EDGE_RISING>;
++ interrupt-names = "vdec", "esparser";
++
++ amlogic,ao-sysctrl = <&sysctrl_AO>;
++ };
++
+ vpu: vpu@d0100000 {
+ compatible = "amlogic,meson-gx-vpu";
+ reg = <0x0 0xd0100000 0x0 0x100000>,
diff --git a/testing/linux-amlogic/APKBUILD b/testing/linux-amlogic/APKBUILD
new file mode 100644
index 0000000000..1c7e3210a7
--- /dev/null
+++ b/testing/linux-amlogic/APKBUILD
@@ -0,0 +1,313 @@
+# Maintainer: He Yangxuan <yangxuan8282@gmail.com>
+
+pkgname=linux-amlogic
+pkgver=4.18.14
+case $pkgver in
+*.*.*) _kernver=${pkgver%.*};;
+*.*) _kernver=${pkgver};;
+esac
+pkgrel=0
+pkgdesc="Linux kernel for Amlogic"
+url=https://github.com/torvalds/linux
+_commit=e7d199e92956587695510d147c8de795f944cec9
+depends="mkinitfs"
+_depends_dev="perl gmp-dev elfutils-dev bash"
+makedepends="$_depends_dev sed installkernel bc linux-headers linux-firmware openssl-dev bison flex"
+options="!strip !check"
+source="$url/archive/${_commit}.tar.gz
+ config-changes-amlogic.aarch64
+ defconfig
+ 0001-ARM64-dts-meson-gxbb-nanopi-k2-Add-HDMI-CEC-and-CVBS.patch
+ 0002-drm-meson-Make-DMT-timings-parameters-and-pixel-cloc.patch
+ 0003-ARM64-defconfig-enable-CEC-support.patch
+ 0004-clk-meson-switch-gxbb-cts-amclk-div-to-the-generic-d.patch
+ 0005-clk-meson-remove-unused-clk-audio-divider-driver.patch
+ 0006-ASoC-meson-add-meson-audio-core-driver.patch
+ 0007-ASoC-meson-add-register-definitions.patch
+ 0008-ASoC-meson-add-aiu-i2s-dma-support.patch
+ 0009-ASoC-meson-add-initial-i2s-dai-support.patch
+ 0010-ASoC-meson-add-aiu-spdif-dma-support.patch
+ 0011-ASoC-meson-add-initial-spdif-dai-support.patch
+ 0012-ARM64-defconfig-enable-audio-support-for-meson-SoCs-.patch
+ 0013-ARM64-dts-meson-gx-add-audio-controller-nodes.patch
+ 0014-snd-meson-activate-HDMI-audio-path.patch
+ 0015-drm-meson-select-dw-hdmi-i2s-audio-for-meson-hdmi.patch
+ 0016-ARM64-dts-meson-gx-add-sound-dai-cells-to-HDMI-node.patch
+ 0017-ARM64-dts-meson-activate-hdmi-audio-HDMI-enabled-boa.patch
+ 0018-drm-bridge-dw-hdmi-Use-AUTO-CTS-setup-mode-when-non-.patch
+ 0019-drm-meson-Call-drm_crtc_vblank_on-drm_crtc_vblank_of.patch
+ 0020-media-platform-meson-ao-cec-make-busy-TX-warning-sil.patch
+ 0021-soc-amlogic-add-meson-canvas-driver.patch
+ 0022-dt-bindings-soc-amlogic-add-meson-canvas-documentati.patch
+ 0023-ARM64-dts-meson-gx-add-dmcbus-and-canvas-nodes.patch
+ 0024-drm-meson-convert-to-the-new-canvas-module.patch
+ 0025-WIP-drm-meson-Support-Overlay-plane-for-video-render.patch
+ 0026-media-meson-add-v4l2-m2m-video-decoder-driver.patch
+ 0027-ARM64-dts-meson-gx-add-vdec-entry.patch
+ 0001-libretech-cc-disable-CVBS-connector.patch
+ 0001-ARM64-dts-meson-add-vdec-entries.patch
+ add-phicomm-n1.patch
+ bt-btbcm.patch
+ brcmfmac-Disable-power-management.patch
+ offset.patch
+ off_error_text_offset.patch
+ "
+subpackages=""
+arch="armhf armv7 aarch64"
+license="GPL-2.0"
+_flavors=
+for _i in $source; do
+ case $_i in
+ config-*.$CARCH)
+ _f=${_i%.$CARCH}
+ _f=${_f#config-changes-}
+ _flavors="$_flavors ${_f}"
+ [ "linux-$_f" != "$pkgname" ] && subpackages="$subpackages linux-${_f}::$CBUILD_ARCH"
+ subpackages="$subpackages linux-${_f}-dev:_dev:$CBUILD_ARCH"
+ ;;
+ esac
+done
+
+case "$CARCH" in
+ aarch64) _carch="arm64" ;;
+ arm*) _carch="arm" ;;
+esac
+
+HOSTCC="${CC:-gcc}"
+HOSTCC="${HOSTCC#${CROSS_COMPILE}}"
+
+prepare() {
+ local _patch_failed=
+ cd "$srcdir"/linux-$_commit
+
+ for i in $source; do
+ case $i in
+ *.patch)
+ msg "Applying $i..."
+ if ! patch -s -p1 -N -i "$srcdir"/${i##*/}; then
+ echo $i >>failed
+ _patch_failed=1
+ fi
+ ;;
+ esac
+ done
+
+ if ! [ -z "$_patch_failed" ]; then
+ error "The following patches failed:"
+ cat failed
+ return 1
+ fi
+
+ # remove localversion from patch if any
+ rm -f localversion*
+
+ local flavor=
+ for flavor in $_flavors; do
+ local builddir="$srcdir"/build-$flavor
+ mkdir -p "$builddir"
+ echo "-$pkgrel-$flavor" > "$builddir"/localversion-alpine
+ _genconfig $flavor
+ make -C "$srcdir"/linux-$_commit \
+ O="$builddir" \
+ ARCH="$_carch" \
+ HOSTCC="$HOSTCC" \
+ olddefconfig
+ _verifyconfig $flavor
+ done
+}
+
+# generate config from defconfig and apply local changes.
+# config-changes-$flavor.$CARCH holds a list of = delimited
+# config command and values used by kernel scripts/config script.
+_genconfig() {
+ local flavor=$1 defconfig=
+ local builddir="$srcdir"/build-$flavor
+ local defconfig=
+ case $flavor in
+ amlogic) defconfig=defconfig ;;
+ *) die "Unknown flavor: $flavor" ;;
+ esac
+
+ cp "$srcdir"/$defconfig \
+ "$builddir"/.config
+
+ while read line; do
+ [ ${line:0:1} = "#" ] && continue
+ local option=${line%%=*} str=
+ local cmd=$(echo $line | cut -d= -f2)
+ case "$cmd" in
+ enable|disable|module) str= ;;
+ set-str|set-val) str=${line##*=} ;;
+ *) die "Command $cmd not accepted" ;;
+ esac
+ msg "[$flavor] $cmd: $option $str"
+ "$srcdir"/linux-$_commit/scripts/config \
+ --file "$builddir"/.config \
+ --${cmd} "$option" "${str//\"/}"
+ done < "$srcdir"/config-changes-$flavor.${CARCH}
+}
+
+# verify if options are set to correct value
+_verifyconfig() {
+ local flavor=$1
+ local builddir="$srcdir"/build-$flavor
+ while read line; do
+ [ ${line:0:1} = "#" ] && continue
+ local option=${line%%=*} str= invert=
+ local cmd=$(echo $line | cut -d= -f2)
+ case "$cmd" in
+ enable) str="$option=y" ;;
+ disable) str="$option"; invert="-v" ;;
+ module) str="$option=m" ;;
+ set-val) str="$option=${line##*=}" ;;
+ set-str) str=${line##*=}
+ str="$option=\"${str//\"/}\"" ;;
+ esac
+ grep -q $invert "^$str" "$builddir"/.config || \
+ die "Config: $option not properly set!"
+ done < "$srcdir"/config-changes-$flavor.${CARCH}
+}
+
+build() {
+ unset LDFLAGS
+ for i in $_flavors; do
+ cd "$srcdir"/build-$i
+ make ARCH="$_carch" CC="${CC:-gcc}" \
+ KBUILD_BUILD_VERSION="$((pkgrel + 1 ))-Alpine"
+ done
+}
+
+_package() {
+ local _buildflavor="$1" _outdir="$2"
+ local _abi_release=${pkgver}-${pkgrel}-${_buildflavor}
+
+ cd "$srcdir"/build-$_buildflavor
+
+ mkdir -p "$_outdir"/boot "$_outdir"/lib/modules
+
+ local _install
+ case "$CARCH" in
+ arm*)
+ _install="zinstall dtbs_install"
+ ;;
+ aarch64)
+ _install="install dtbs_install"
+ ;;
+ *)
+ _install=install
+ ;;
+ esac
+
+ cd "$srcdir"/build-$_buildflavor
+ local INSTALL_DTBS_PATH="$_outdir"/boot/dtbs
+ make -j1 modules_install $_install \
+ ARCH="$_carch" \
+ INSTALL_MOD_PATH="$_outdir" \
+ INSTALL_PATH="$_outdir"/boot \
+ INSTALL_DTBS_PATH="$INSTALL_DTBS_PATH"
+
+ rm -f "$_outdir"/lib/modules/${_abi_release}/build \
+ "$_outdir"/lib/modules/${_abi_release}/source
+ rm -rf "$_outdir"/lib/firmware
+
+ install -D include/config/kernel.release \
+ "$_outdir"/usr/share/kernel/$_buildflavor/kernel.release
+
+ if [ "$CARCH" = "aarch64" ]; then
+ mv -f "$INSTALL_DTBS_PATH"/amlogic/*.dtb \
+ "$INSTALL_DTBS_PATH"
+ rmdir "$INSTALL_DTBS_PATH"/amlogic
+ fi
+}
+
+# main flavor installs in $pkgdir
+package() {
+ depends="$depends linux-firmware-brcm"
+ _package amlogic "$pkgdir"
+}
+
+_dev() {
+ local _flavor=$(echo $subpkgname | sed -E 's/(^linux-|-dev$)//g')
+ local _abi_release=${pkgver}-${pkgrel}-$_flavor
+ # copy the only the parts that we really need for build 3rd party
+ # kernel modules and install those as /usr/src/linux-headers,
+ # simlar to what ubuntu does
+ #
+ # this way you dont need to install the 300-400 kernel sources to
+ # build a tiny kernel module
+ #
+ pkgdesc="Headers and script for third party modules for $_flavor kernel"
+ depends="$_depends_dev"
+ local dir="$subpkgdir"/usr/src/linux-headers-${_abi_release}
+
+ # first we import config, run prepare to set up for building
+ # external modules, and create the scripts
+ mkdir -p "$dir"
+ cp "$srcdir"/build-$_flavor/.config "$dir"/.config
+ make -j1 -C "$srcdir"/linux-$_commit O="$dir" HOSTCC="${CC:-gcc}" \
+ silentoldconfig prepare modules_prepare scripts
+
+ # remove the stuff that points to real sources. we want 3rd party
+ # modules to believe this is the soruces
+ rm "$dir"/Makefile "$dir"/source
+
+ # copy the needed stuff from real sources
+ #
+ # this is taken from ubuntu kernel build script
+ # http://kernel.ubuntu.com/git/ubuntu/ubuntu-zesty.git/tree/debian/rules.d/3-binary-indep.mk
+ cd "$srcdir"/linux-$_commit
+ find . -path './include/*' -prune \
+ -o -path './scripts/*' -prune -o -type f \
+ \( -name 'Makefile*' -o -name 'Kconfig*' -o -name 'Kbuild*' -o \
+ -name '*.sh' -o -name '*.pl' -o -name '*.lds' \) \
+ -print | cpio -pdm "$dir"
+
+ cp -a scripts include "$dir"
+ find $(find arch -name include -type d -print) -type f \
+ | cpio -pdm "$dir"
+
+ install -Dm644 "$srcdir"/build-$_flavor/Module.symvers \
+ "$dir"/Module.symvers
+
+ mkdir -p "$subpkgdir"/lib/modules/${_abi_release}
+ ln -sf /usr/src/linux-headers-${_abi_release} \
+ "$subpkgdir"/lib/modules/${_abi_release}/build
+}
+
+sha512sums="b65dc14801b987b4d8ca786aa6571da12fd929ab147ed2f71f366b32313cd72d3047830e502b3fe51f9f1f3963d46b7f5b45ed937a4bdfee14dc216b32de2d3a e7d199e92956587695510d147c8de795f944cec9.tar.gz
+7186849193386fc06caab6db9ebf3d5b7efe2511f1c3836bb70d713340eb69b1f123f8372f15ee7f8dd61eeff5ce772b35db8557133ef64a2a1a5ca9e7cd30c9 config-changes-amlogic.aarch64
+2784f8c05c1c66f2bff73d0cdd415781e6529f269223607c18302803125092e7cb6c94083146deecc3226672f7f4754246cc62808f9384f697782ec55786883d defconfig
+94f944448156e435030d84d787d1489cdab4a6733b1fd27cd4ecd1284337d2b3e68bb3f74b3268a4cac01587e96019d4ad2a354c59d1422b6caae3f97849717f 0001-ARM64-dts-meson-gxbb-nanopi-k2-Add-HDMI-CEC-and-CVBS.patch
+997fee925f07735e635fdd2d2474500ca5a81a80a7817c079174e2508eec223264c97b79cfc057d6b4e428c4d9392405f79befd242dc9b21e020f544ffd84c9d 0002-drm-meson-Make-DMT-timings-parameters-and-pixel-cloc.patch
+3294e40073360e9f6daf3360022a8443bdab960ba0f261e5858e36e7b838af0304888eef9992ef93f67a5dc97f0a26c46f08da1c2f32ca1fe01b9754f55ae975 0003-ARM64-defconfig-enable-CEC-support.patch
+02c5a7b99be3a180eb40716735ced4ba5f9da1897cb9132c3376b660ba1c005df60b9da1ad7808f53027b7d37b89ef007c71aa2fdfd5ad77be4425886348c3b2 0004-clk-meson-switch-gxbb-cts-amclk-div-to-the-generic-d.patch
+8d4d3bead45b420b0920eb4eb77fc938c2bcb64a3304d5690432aec7a235f348deba4246b737742901102f256313ab34f95caa8932184099ed20baaf23913f6f 0005-clk-meson-remove-unused-clk-audio-divider-driver.patch
+7057d93953fe9bb83415ac82fe63bacc64161f295e1340dc8b56330a74655c44e9f7feb621f12b74e7d92497115abd918e7b087bc0ece7d0322e384fbd5acd95 0006-ASoC-meson-add-meson-audio-core-driver.patch
+42ba176a6198fae2146f871dc469fb94b40382ff966b167f976d5e28375f4849bda6141b01e786d37cf14ca71ba159ee0a0316ec477150040517cebf0f638334 0007-ASoC-meson-add-register-definitions.patch
+720c21f11514edba0164ecfe260b8d2409ccea5eaaf4d98013e42a7e4f160fbe30d6a25fd94ae1fae115742fff298ebab1a079ac1f54ae0dc39cfb5567eb693c 0008-ASoC-meson-add-aiu-i2s-dma-support.patch
+19d3afeae370201d513c98137bd3833ec3bb8fbd43dee5a391a6e4926a31b381746725e3abdc907a2f24f72ae6757687083d0b99ba1e27a34d90e0f06e506573 0009-ASoC-meson-add-initial-i2s-dai-support.patch
+d9da5fc3cf9d6942aa995c06e6c22bfeac89add9dc7d0dd4f58cb4d8e07ceb3679d8af1dd68ba1510bc964cc6303cdf38954b7364ccd9bf22de4c379f12cfbe3 0010-ASoC-meson-add-aiu-spdif-dma-support.patch
+9cbab78248da16985ac64c3ee58652df36ae3a8313d7723a168ed36cb23c6d488dd21a603b09ac2fc7ee88aaa4d248c49768821af065f627e8bad671a961a58a 0011-ASoC-meson-add-initial-spdif-dai-support.patch
+f7e1b85b59f4347918b626fd373cb23bc5ede17f52f7c8d0f29d6f80a22d28966060c7842363393505b8c059b11ee15297e3b429976a7e6e5f0189a14223a480 0012-ARM64-defconfig-enable-audio-support-for-meson-SoCs-.patch
+52a9eaaf61daa1923ed91a20fc7088d532060d180755c257122b6d897a07812249fefe6f5cd568551b9b8656071be3f2f6922f34d691003d8146bb5307e7522b 0013-ARM64-dts-meson-gx-add-audio-controller-nodes.patch
+5c31a60457ccc4e6fc1c6f005b9c70583a03797cd56cca3e9799d2717312fd8610ec13cd7d83eadd134379788e9655b4e95b90462076adf10ff39b71dc7c9a10 0014-snd-meson-activate-HDMI-audio-path.patch
+afd396bd828a87fb001956ce7369ac8c34e51a684f7be486995463cf4d912a39b242b51af7f3a841b6ecb42ab7e4366c5f9d163992ccaf370fedd842f7a4b2c6 0015-drm-meson-select-dw-hdmi-i2s-audio-for-meson-hdmi.patch
+b6ac2f340d57cd0514a8fbd54f3d17942e794f20bfe3402c64a98fdf7cca7ab487cc2bac884436324eda21482bebbdfe7633bd9f9708bc84adb720161a555b89 0016-ARM64-dts-meson-gx-add-sound-dai-cells-to-HDMI-node.patch
+b55f9b3370d1c7b75c52f8f75f12a5033402d3a7f0f5be591cd6268b280a39959464570c8868e94586034f48a90f50ac17e414b191e12f7725175dca6fee2a1f 0017-ARM64-dts-meson-activate-hdmi-audio-HDMI-enabled-boa.patch
+5814fcf935365b7975ae21f416122a341cef4e54dc8556a03882915d908586e42ec966b18ab5abb85b1533a1c494eeebbb237df3a4ebde28f15449bab66fa421 0018-drm-bridge-dw-hdmi-Use-AUTO-CTS-setup-mode-when-non-.patch
+95da882661e07c40d726797b570b2407c94862278a2148f8ff1d3ce7ffe00ba6f83d1f06be77a1fbc59c5f1f5620c1346bd565903b49e5d657e8d143eb247af8 0019-drm-meson-Call-drm_crtc_vblank_on-drm_crtc_vblank_of.patch
+8371d3c08ba0029e7c29a5171eb53ba175c0909f1a90a17967f2ca68c1d9a2dbc49a39d1102a62470d237a50a99307b7475a4ba243a0a0d6881684705f428e2b 0020-media-platform-meson-ao-cec-make-busy-TX-warning-sil.patch
+896a8ba7c44744e067d3d5216d28df241c6796f1aff01b9648ea2e54e5798f25436328cdb173f00de2f998903ae01b3785a37ed07aea576ccada4590925de10f 0021-soc-amlogic-add-meson-canvas-driver.patch
+ad723a68e02b2d4fd708fab3b61aba3ad888b25de9d5e73f3f00dc81bf778d1b59061aa25b840bc790903a8dc9ce180da5aca6312c9f7c8b724a18c80866e174 0022-dt-bindings-soc-amlogic-add-meson-canvas-documentati.patch
+0854014aaf150468b14a739649e1461fa66450d168f3f430756bcf6d2e34158be922824d2eb62fd867e5ac28340ef120fb13504183984da7a4bd3a6fe59724b8 0023-ARM64-dts-meson-gx-add-dmcbus-and-canvas-nodes.patch
+f01e6fd7a319c75c85803e1deb1681772385640fd10ef4a24b164af6e708d0d29e8118bf2be12a82e6587587a87a6b5b7331bd160c32ae9e3d5f4c1a4e62ce79 0024-drm-meson-convert-to-the-new-canvas-module.patch
+783a1c4c0deb352b699faa5f572d38737c1c67983a84822b900a984e9aec521f7a762d6f139d5ce7546503af4a76fd2b604ef79a2aef5105d23fc33919fa13de 0025-WIP-drm-meson-Support-Overlay-plane-for-video-render.patch
+34b404bbb4b1e2c3ae1c9fba8e297ed48d382d59208bb00340a5e9f2a86ef0f12d0fc6f347a68915cffc141e19bd4b086a1bbdc440b4bd8a1edef5a7fb79c46e 0026-media-meson-add-v4l2-m2m-video-decoder-driver.patch
+608bb5d25c94e9d8e0a1ea9d9fc2eb4436870d72e39f9446322dbdc62a5b7a3fd07d735613a425e1853cf9dd3e61fc860f4310858f9cfbea62a3ba69378b653c 0027-ARM64-dts-meson-gx-add-vdec-entry.patch
+0ab00106ab3a15ef884d0d08c4e947037007a4af7b03fc204346e9487f1f8841140dead1f6b8704b74f7fd955deaaa58cc7ce81c42a847bcdc5c13aac627f3e4 0001-libretech-cc-disable-CVBS-connector.patch
+7715e9c35072f60856d5ab6940874f053cd5e64f09ae97c79eef98bf0856f3a27643dae1154eec787c94a8c7c84b39c580324540db37d9bf2cf70b2bfa66595a 0001-ARM64-dts-meson-add-vdec-entries.patch
+6262ecd89164edcfc58ef7d50c0b027a2263e6e78f51f033b611734a8e8db0436e1b132aee65546717dd5c79635ef38c9be11cffd7808491687494df9fdcb08e add-phicomm-n1.patch
+dcc02a2e35e530490f18dd1f7e304aebd7c268ed8a46971d94e9da7dfd65b287002e6b921e46db5ce118177c9ab79fbe3640565cb2df19809b6eff514122fb43 bt-btbcm.patch
+007075ca40915f20c6c8f5e04825c3433a52621deb689a9a690a3fcc618ca9629bc089a1a2ad5141fc47a8aa0aabb302ecf75e81ef8cae466e978074156231ac brcmfmac-Disable-power-management.patch
+cb2ec80ae38c4cc46ea2a4ca6d157498632f8154ef6a1dedb898c70593c812743a69a4c0d36a4e146a71c588e30e9f9b2feb415e00166850c2cf79797048b032 offset.patch
+e5d214788f9a28d79a9ef135fbed3b3c87982941788ca74d31d919534d1627c1265b268982f67de6c14b331d1a38938b4c2772f17b3db3bddd3983f919fe73ca off_error_text_offset.patch"
diff --git a/testing/linux-amlogic/add-phicomm-n1.patch b/testing/linux-amlogic/add-phicomm-n1.patch
new file mode 100644
index 0000000000..2140d8d828
--- /dev/null
+++ b/testing/linux-amlogic/add-phicomm-n1.patch
@@ -0,0 +1,60 @@
+diff --git a/arch/arm64/boot/dts/amlogic/Makefile b/arch/arm64/boot/dts/amlogic/Makefile
+index 34dd0e9..773f415 100644
+--- a/arch/arm64/boot/dts/amlogic/Makefile
++++ b/arch/arm64/boot/dts/amlogic/Makefile
+@@ -17,6 +17,7 @@ dtb-$(CONFIG_ARCH_MESON) += meson-gxl-s905x-nexbox-a95x.dtb
+ dtb-$(CONFIG_ARCH_MESON) += meson-gxl-s905x-p212.dtb
+ dtb-$(CONFIG_ARCH_MESON) += meson-gxl-s905d-p230.dtb
+ dtb-$(CONFIG_ARCH_MESON) += meson-gxl-s905d-p231.dtb
++dtb-$(CONFIG_ARCH_MESON) += meson-gxl-s905d-phicomm-n1.dtb
+ dtb-$(CONFIG_ARCH_MESON) += meson-gxm-khadas-vim2.dtb
+ dtb-$(CONFIG_ARCH_MESON) += meson-gxm-nexbox-a1.dtb
+ dtb-$(CONFIG_ARCH_MESON) += meson-gxm-q200.dtb
+diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-phicomm-n1.dts b/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-phicomm-n1.dts
+new file mode 100644
+index 0000000..7903dad
+--- /dev/null
++++ b/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-phicomm-n1.dts
+@@ -0,0 +1,42 @@
++// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
++/*
++ * Copyright (c) 2018 He Yangxuan
++ */
++
++/dts-v1/;
++
++#include "meson-gxl-s905d-p230.dts"
++
++/ {
++ compatible = "phicomm,n1", "amlogic,s905d", "amlogic,meson-gxl";
++ model = "Phicomm N1";
++
++ aliases {
++ serial1 = &uart_A;
++ };
++
++ cvbs-connector {
++ status = "disabled";
++ };
++};
++
++&cvbs_vdac_port {
++ status = "disabled";
++};
++
++&external_phy {
++ /* Realtek RTL8211F (0x001cc916) */
++ eee-broken-1000t;
++};
++
++/* This is connected to the Bluetooth module: */
++&uart_A {
++ status = "okay";
++ pinctrl-0 = <&uart_a_pins>, <&uart_a_cts_rts_pins>;
++ pinctrl-names = "default";
++
++ bluetooth {
++ compatible = "brcm,bcm43438-bt";
++ shutdown-gpios = <&gpio GPIOX_17 GPIO_ACTIVE_HIGH>;
++ };
++};
diff --git a/testing/linux-amlogic/brcmfmac-Disable-power-management.patch b/testing/linux-amlogic/brcmfmac-Disable-power-management.patch
new file mode 100644
index 0000000000..17629f8e0c
--- /dev/null
+++ b/testing/linux-amlogic/brcmfmac-Disable-power-management.patch
@@ -0,0 +1,13 @@
+diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+index b6122aa..e2098ad 100644
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+@@ -2697,6 +2697,8 @@ brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev,
+ * preference in cfg struct to apply this to
+ * FW later while initializing the dongle
+ */
++ pr_info("power management disabled\n");
++ enabled = false;
+ cfg->pwr_save = enabled;
+ if (!check_vif_up(ifp->vif)) {
+
diff --git a/testing/linux-amlogic/bt-btbcm.patch b/testing/linux-amlogic/bt-btbcm.patch
new file mode 100644
index 0000000000..cb56ab0e66
--- /dev/null
+++ b/testing/linux-amlogic/bt-btbcm.patch
@@ -0,0 +1,11 @@
+--- a/drivers/bluetooth/btbcm.c 2018-07-09 00:34:02.000000000 +0100
++++ b/drivers/bluetooth/btbcm.c 2018-07-12 11:38:12.323462430 +0100
+@@ -326,7 +326,7 @@ static const struct bcm_subver_table bcm
+ { 0x4406, "BCM4324B3" }, /* 002.004.006 */
+ { 0x610c, "BCM4354" }, /* 003.001.012 */
+ { 0x2122, "BCM4343A0" }, /* 001.001.034 */
+- { 0x2209, "BCM43430A1" }, /* 001.002.009 */
++ { 0x2209, "BCM43438A1" }, /* 001.002.009 */
+ { 0x6119, "BCM4345C0" }, /* 003.001.025 */
+ { 0x230f, "BCM4356A2" }, /* 001.003.015 */
+ { }
diff --git a/testing/linux-amlogic/config-changes-amlogic.aarch64 b/testing/linux-amlogic/config-changes-amlogic.aarch64
new file mode 100644
index 0000000000..ef386cc05a
--- /dev/null
+++ b/testing/linux-amlogic/config-changes-amlogic.aarch64
@@ -0,0 +1,13 @@
+# format is config=command(=string/value)
+# where command can be one of:
+# enable,disable,module,set-str,set-value
+CONFIG_DRM_UDL=module
+CONFIG_FB_UDL=module
+CONFIG_SLAB_FREELIST_RANDOM=enable
+CONFIG_SLAB_FREELIST_HARDENED=enable
+CONFIG_STRICT_DEVMEM=enable
+CONFIG_IO_STRICT_DEVMEM=enable
+CONFIG_ENCRYPTED_KEYS=module
+CONFIG_KEY_DH_OPERATIONS=enable
+CONFIG_HARDENED_USERCOPY=enable
+CONFIG_IP_PNP=disable
diff --git a/testing/linux-amlogic/defconfig b/testing/linux-amlogic/defconfig
new file mode 100644
index 0000000000..e7439edb1f
--- /dev/null
+++ b/testing/linux-amlogic/defconfig
@@ -0,0 +1,882 @@
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_AUDIT=y
+CONFIG_NO_HZ_IDLE=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_BSD_PROCESS_ACCT_V3=y
+CONFIG_TASKSTATS=y
+CONFIG_TASK_DELAY_ACCT=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=19
+CONFIG_MEMCG=y
+CONFIG_MEMCG_SWAP=y
+CONFIG_BLK_CGROUP=y
+CONFIG_CGROUP_PIDS=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_HUGETLB=y
+CONFIG_CPUSETS=y
+CONFIG_CGROUP_DEVICE=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_CGROUP_PERF=y
+CONFIG_NAMESPACES=y
+CONFIG_USER_NS=y
+CONFIG_SCHED_AUTOGROUP=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_KALLSYMS_ALL=y
+CONFIG_EMBEDDED=y
+# CONFIG_COMPAT_BRK is not set
+CONFIG_PROFILING=y
+CONFIG_JUMP_LABEL=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_BLK_DEV_INTEGRITY=y
+CONFIG_BLK_DEV_THROTTLING=y
+# CONFIG_BLK_DEBUG_FS is not set
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_MAC_PARTITION=y
+CONFIG_LDM_PARTITION=y
+# CONFIG_IOSCHED_DEADLINE is not set
+CONFIG_DEFAULT_NOOP=y
+# CONFIG_MQ_IOSCHED_DEADLINE is not set
+# CONFIG_MQ_IOSCHED_KYBER is not set
+CONFIG_ARCH_MESON=y
+# CONFIG_QCOM_FALKOR_ERRATUM_1003 is not set
+# CONFIG_QCOM_FALKOR_ERRATUM_1009 is not set
+# CONFIG_QCOM_QDF2400_ERRATUM_0065 is not set
+CONFIG_ARM64_VA_BITS_48=y
+CONFIG_SCHED_MC=y
+CONFIG_PREEMPT=y
+CONFIG_KSM=y
+CONFIG_TRANSPARENT_HUGEPAGE=y
+CONFIG_CLEANCACHE=y
+CONFIG_FRONTSWAP=y
+CONFIG_CMA=y
+CONFIG_ZSMALLOC=m
+CONFIG_SECCOMP=y
+CONFIG_KEXEC=y
+CONFIG_XEN=y
+# CONFIG_ARM64_LSE_ATOMICS is not set
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_BINFMT_MISC=y
+CONFIG_COMPAT=y
+CONFIG_HIBERNATION=y
+CONFIG_CPU_IDLE=y
+CONFIG_ARM_CPUIDLE=y
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_DEFAULT_GOV_SCHEDUTIL=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+CONFIG_CPUFREQ_DT=y
+CONFIG_ARM_BIG_LITTLE_CPUFREQ=y
+CONFIG_ARM_SCPI_CPUFREQ=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_PACKET_DIAG=m
+CONFIG_UNIX=y
+CONFIG_UNIX_DIAG=m
+CONFIG_XFRM_USER=y
+CONFIG_NET_KEY=m
+CONFIG_NET_KEY_MIGRATE=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_ROUTE_MULTIPATH=y
+CONFIG_IP_ROUTE_VERBOSE=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_RARP=y
+CONFIG_NET_IPIP=y
+CONFIG_SYN_COOKIES=y
+CONFIG_INET_AH=m
+CONFIG_INET_ESP=m
+CONFIG_INET_IPCOMP=m
+CONFIG_INET_UDP_DIAG=m
+CONFIG_TCP_CONG_ADVANCED=y
+CONFIG_TCP_CONG_BBR=m
+CONFIG_IPV6_ROUTER_PREF=y
+CONFIG_IPV6_ROUTE_INFO=y
+CONFIG_IPV6_OPTIMISTIC_DAD=y
+CONFIG_INET6_AH=m
+CONFIG_INET6_ESP=m
+CONFIG_INET6_IPCOMP=m
+CONFIG_INET6_XFRM_MODE_TRANSPORT=m
+CONFIG_INET6_XFRM_MODE_TUNNEL=m
+CONFIG_INET6_XFRM_MODE_BEET=m
+CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION=m
+CONFIG_IPV6_SIT=m
+CONFIG_IPV6_TUNNEL=y
+CONFIG_IPV6_MULTIPLE_TABLES=y
+CONFIG_NETFILTER=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_CONNTRACK_ZONES=y
+CONFIG_NF_CONNTRACK_EVENTS=y
+CONFIG_NF_CONNTRACK_TIMESTAMP=y
+CONFIG_NF_CONNTRACK_AMANDA=m
+CONFIG_NF_CONNTRACK_FTP=m
+CONFIG_NF_CONNTRACK_H323=m
+CONFIG_NF_CONNTRACK_IRC=m
+CONFIG_NF_CONNTRACK_NETBIOS_NS=m
+CONFIG_NF_CONNTRACK_SNMP=m
+CONFIG_NF_CONNTRACK_PPTP=m
+CONFIG_NF_CONNTRACK_SANE=m
+CONFIG_NF_CONNTRACK_SIP=m
+CONFIG_NF_CONNTRACK_TFTP=m
+CONFIG_NF_CT_NETLINK=m
+CONFIG_NETFILTER_XT_SET=m
+CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m
+CONFIG_NETFILTER_XT_TARGET_CONNMARK=m
+CONFIG_NETFILTER_XT_TARGET_CT=m
+CONFIG_NETFILTER_XT_TARGET_DSCP=m
+CONFIG_NETFILTER_XT_TARGET_HMARK=m
+CONFIG_NETFILTER_XT_TARGET_IDLETIMER=m
+CONFIG_NETFILTER_XT_TARGET_LED=m
+CONFIG_NETFILTER_XT_TARGET_LOG=m
+CONFIG_NETFILTER_XT_TARGET_MARK=m
+CONFIG_NETFILTER_XT_TARGET_NFLOG=m
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m
+CONFIG_NETFILTER_XT_TARGET_TEE=m
+CONFIG_NETFILTER_XT_TARGET_TPROXY=m
+CONFIG_NETFILTER_XT_TARGET_TRACE=m
+CONFIG_NETFILTER_XT_TARGET_TCPMSS=m
+CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP=m
+CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=m
+CONFIG_NETFILTER_XT_MATCH_BPF=m
+CONFIG_NETFILTER_XT_MATCH_CLUSTER=m
+CONFIG_NETFILTER_XT_MATCH_COMMENT=m
+CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m
+CONFIG_NETFILTER_XT_MATCH_CONNLABEL=m
+CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=m
+CONFIG_NETFILTER_XT_MATCH_CONNMARK=m
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
+CONFIG_NETFILTER_XT_MATCH_CPU=m
+CONFIG_NETFILTER_XT_MATCH_DCCP=m
+CONFIG_NETFILTER_XT_MATCH_DEVGROUP=m
+CONFIG_NETFILTER_XT_MATCH_DSCP=m
+CONFIG_NETFILTER_XT_MATCH_ESP=m
+CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m
+CONFIG_NETFILTER_XT_MATCH_HELPER=m
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=m
+CONFIG_NETFILTER_XT_MATCH_IPVS=m
+# CONFIG_NETFILTER_XT_MATCH_L2TP is not set
+CONFIG_NETFILTER_XT_MATCH_LENGTH=m
+CONFIG_NETFILTER_XT_MATCH_LIMIT=m
+CONFIG_NETFILTER_XT_MATCH_MAC=m
+CONFIG_NETFILTER_XT_MATCH_MARK=m
+CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m
+CONFIG_NETFILTER_XT_MATCH_NFACCT=m
+CONFIG_NETFILTER_XT_MATCH_OSF=m
+CONFIG_NETFILTER_XT_MATCH_OWNER=m
+CONFIG_NETFILTER_XT_MATCH_POLICY=m
+CONFIG_NETFILTER_XT_MATCH_PHYSDEV=m
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m
+CONFIG_NETFILTER_XT_MATCH_QUOTA=m
+CONFIG_NETFILTER_XT_MATCH_RATEEST=m
+CONFIG_NETFILTER_XT_MATCH_REALM=m
+CONFIG_NETFILTER_XT_MATCH_RECENT=m
+CONFIG_NETFILTER_XT_MATCH_STATE=m
+CONFIG_NETFILTER_XT_MATCH_STATISTIC=m
+CONFIG_NETFILTER_XT_MATCH_STRING=m
+CONFIG_NETFILTER_XT_MATCH_TCPMSS=m
+CONFIG_NETFILTER_XT_MATCH_TIME=m
+CONFIG_NETFILTER_XT_MATCH_U32=m
+CONFIG_IP_SET=m
+CONFIG_IP_SET_BITMAP_IP=m
+CONFIG_IP_SET_BITMAP_IPMAC=m
+CONFIG_IP_SET_BITMAP_PORT=m
+CONFIG_IP_SET_HASH_IP=m
+CONFIG_IP_SET_HASH_IPPORT=m
+CONFIG_IP_SET_HASH_IPPORTIP=m
+CONFIG_IP_SET_HASH_IPPORTNET=m
+CONFIG_IP_SET_HASH_NET=m
+CONFIG_IP_SET_HASH_NETPORT=m
+CONFIG_IP_SET_HASH_NETIFACE=m
+CONFIG_IP_SET_LIST_SET=m
+CONFIG_IP_VS=m
+CONFIG_IP_VS_PROTO_TCP=y
+CONFIG_IP_VS_PROTO_UDP=y
+CONFIG_IP_VS_PROTO_ESP=y
+CONFIG_IP_VS_PROTO_AH=y
+CONFIG_IP_VS_PROTO_SCTP=y
+CONFIG_IP_VS_RR=m
+CONFIG_IP_VS_WRR=m
+CONFIG_IP_VS_LC=m
+CONFIG_IP_VS_WLC=m
+CONFIG_IP_VS_LBLC=m
+CONFIG_IP_VS_LBLCR=m
+CONFIG_IP_VS_DH=m
+CONFIG_IP_VS_SH=m
+CONFIG_IP_VS_SED=m
+CONFIG_IP_VS_NQ=m
+CONFIG_IP_VS_FTP=m
+CONFIG_IP_VS_PE_SIP=m
+CONFIG_NF_CONNTRACK_IPV4=y
+CONFIG_IP_NF_IPTABLES=y
+CONFIG_IP_NF_MATCH_AH=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_MATCH_RPFILTER=m
+CONFIG_IP_NF_MATCH_TTL=m
+CONFIG_IP_NF_FILTER=y
+CONFIG_IP_NF_TARGET_REJECT=y
+CONFIG_IP_NF_NAT=m
+CONFIG_IP_NF_TARGET_MASQUERADE=m
+CONFIG_IP_NF_TARGET_NETMAP=m
+CONFIG_IP_NF_TARGET_REDIRECT=m
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_TARGET_CLUSTERIP=m
+CONFIG_IP_NF_TARGET_ECN=m
+CONFIG_IP_NF_TARGET_TTL=m
+CONFIG_IP_NF_RAW=m
+CONFIG_IP_NF_ARPTABLES=m
+CONFIG_IP_NF_ARPFILTER=m
+CONFIG_IP_NF_ARP_MANGLE=m
+CONFIG_NF_CONNTRACK_IPV6=y
+CONFIG_IP6_NF_IPTABLES=y
+CONFIG_IP6_NF_MATCH_AH=m
+CONFIG_IP6_NF_MATCH_EUI64=m
+CONFIG_IP6_NF_MATCH_FRAG=m
+CONFIG_IP6_NF_MATCH_HL=m
+CONFIG_IP6_NF_MATCH_IPV6HEADER=m
+CONFIG_IP6_NF_MATCH_MH=m
+CONFIG_IP6_NF_MATCH_RPFILTER=m
+CONFIG_IP6_NF_MATCH_RT=m
+CONFIG_IP6_NF_TARGET_HL=m
+CONFIG_IP6_NF_FILTER=y
+CONFIG_IP6_NF_TARGET_REJECT=y
+CONFIG_IP6_NF_MANGLE=m
+CONFIG_IP6_NF_RAW=m
+CONFIG_IP6_NF_NAT=m
+CONFIG_IP6_NF_TARGET_MASQUERADE=m
+CONFIG_IP6_NF_TARGET_NPT=m
+CONFIG_BRIDGE_NF_EBTABLES=m
+CONFIG_BRIDGE_EBT_BROUTE=m
+CONFIG_BRIDGE_EBT_T_FILTER=m
+CONFIG_BRIDGE_EBT_T_NAT=m
+CONFIG_BRIDGE_EBT_802_3=m
+CONFIG_BRIDGE_EBT_AMONG=m
+CONFIG_BRIDGE_EBT_ARP=m
+CONFIG_BRIDGE_EBT_IP=m
+CONFIG_BRIDGE_EBT_IP6=m
+CONFIG_BRIDGE_EBT_LIMIT=m
+CONFIG_BRIDGE_EBT_MARK=m
+CONFIG_BRIDGE_EBT_PKTTYPE=m
+CONFIG_BRIDGE_EBT_STP=m
+CONFIG_BRIDGE_EBT_VLAN=m
+CONFIG_BRIDGE_EBT_ARPREPLY=m
+CONFIG_BRIDGE_EBT_DNAT=m
+CONFIG_BRIDGE_EBT_MARK_T=m
+CONFIG_BRIDGE_EBT_REDIRECT=m
+CONFIG_BRIDGE_EBT_SNAT=m
+CONFIG_BRIDGE_EBT_LOG=m
+CONFIG_BRIDGE_EBT_NFLOG=m
+CONFIG_ATM=m
+CONFIG_L2TP=y
+CONFIG_L2TP_DEBUGFS=y
+CONFIG_L2TP_V3=y
+CONFIG_L2TP_IP=y
+CONFIG_L2TP_ETH=y
+CONFIG_BRIDGE=m
+CONFIG_BRIDGE_VLAN_FILTERING=y
+CONFIG_VLAN_8021Q=m
+CONFIG_VLAN_8021Q_GVRP=y
+CONFIG_VLAN_8021Q_MVRP=y
+CONFIG_ATALK=m
+CONFIG_6LOWPAN=m
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_CBQ=m
+CONFIG_NET_SCH_HTB=m
+CONFIG_NET_SCH_HFSC=m
+CONFIG_NET_SCH_PRIO=m
+CONFIG_NET_SCH_MULTIQ=m
+CONFIG_NET_SCH_RED=m
+CONFIG_NET_SCH_SFB=m
+CONFIG_NET_SCH_SFQ=m
+CONFIG_NET_SCH_TEQL=m
+CONFIG_NET_SCH_TBF=m
+CONFIG_NET_SCH_GRED=m
+CONFIG_NET_SCH_DSMARK=m
+CONFIG_NET_SCH_NETEM=m
+CONFIG_NET_SCH_DRR=m
+CONFIG_NET_SCH_MQPRIO=m
+CONFIG_NET_SCH_CHOKE=m
+CONFIG_NET_SCH_QFQ=m
+CONFIG_NET_SCH_CODEL=m
+CONFIG_NET_SCH_FQ_CODEL=m
+CONFIG_NET_SCH_FQ=m
+CONFIG_NET_SCH_INGRESS=m
+CONFIG_NET_SCH_PLUG=m
+CONFIG_NET_CLS_BASIC=m
+CONFIG_NET_CLS_TCINDEX=m
+CONFIG_NET_CLS_ROUTE4=m
+CONFIG_NET_CLS_FW=m
+CONFIG_NET_CLS_U32=m
+CONFIG_CLS_U32_MARK=y
+CONFIG_NET_CLS_RSVP=m
+CONFIG_NET_CLS_RSVP6=m
+CONFIG_NET_CLS_FLOW=m
+CONFIG_NET_CLS_CGROUP=m
+CONFIG_NET_EMATCH=y
+CONFIG_NET_EMATCH_CMP=m
+CONFIG_NET_EMATCH_META=m
+CONFIG_NET_EMATCH_TEXT=m
+CONFIG_NET_EMATCH_IPSET=m
+CONFIG_NET_CLS_ACT=y
+CONFIG_NET_ACT_POLICE=m
+CONFIG_NET_ACT_GACT=m
+CONFIG_GACT_PROB=y
+CONFIG_NET_ACT_MIRRED=m
+CONFIG_NET_ACT_IPT=m
+CONFIG_NET_ACT_NAT=m
+CONFIG_NET_ACT_PEDIT=m
+CONFIG_NET_ACT_SIMP=m
+CONFIG_NET_ACT_SKBEDIT=m
+CONFIG_NET_ACT_CSUM=m
+CONFIG_BATMAN_ADV=m
+CONFIG_OPENVSWITCH=m
+CONFIG_BPF_JIT=y
+CONFIG_NET_PKTGEN=m
+CONFIG_HAMRADIO=y
+CONFIG_AX25=m
+CONFIG_NETROM=m
+CONFIG_ROSE=m
+CONFIG_MKISS=m
+CONFIG_6PACK=m
+CONFIG_BPQETHER=m
+CONFIG_BAYCOM_SER_FDX=m
+CONFIG_BAYCOM_SER_HDX=m
+CONFIG_YAM=m
+CONFIG_CAN=m
+CONFIG_BT=m
+CONFIG_BT_RFCOMM=m
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=m
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+CONFIG_BT_HIDP=m
+CONFIG_BT_6LOWPAN=m
+CONFIG_BT_LEDS=y
+# CONFIG_BT_DEBUGFS is not set
+CONFIG_BT_HCIBTUSB=m
+CONFIG_BT_HCIBTSDIO=m
+CONFIG_BT_HCIUART=m
+CONFIG_BT_HCIUART_BCSP=y
+CONFIG_BT_HCIUART_LL=y
+CONFIG_BT_HCIUART_3WIRE=y
+CONFIG_BT_HCIUART_BCM=y
+CONFIG_BT_HCIBCM203X=m
+CONFIG_BT_HCIBPA10X=m
+CONFIG_BT_HCIBFUSB=m
+CONFIG_BT_MRVL=m
+CONFIG_BT_MRVL_SDIO=m
+CONFIG_BT_ATH3K=m
+CONFIG_CFG80211=m
+CONFIG_MAC80211=m
+CONFIG_MAC80211_MESH=y
+CONFIG_MAC80211_LEDS=y
+CONFIG_WIMAX=m
+CONFIG_RFKILL=m
+CONFIG_RFKILL_INPUT=y
+CONFIG_NET_9P=y
+CONFIG_NET_9P_VIRTIO=y
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_DMA_CMA=y
+CONFIG_CMA_SIZE_MBYTES=256
+CONFIG_BRCMSTB_GISB_ARB=y
+CONFIG_VEXPRESS_CONFIG=y
+CONFIG_MTD=y
+CONFIG_MTD_CMDLINE_PARTS=m
+CONFIG_MTD_BLOCK=y
+CONFIG_MTD_OOPS=m
+CONFIG_MTD_M25P80=y
+CONFIG_MTD_NAND=y
+CONFIG_MTD_NAND_DENALI_DT=y
+CONFIG_MTD_SPI_NOR=y
+CONFIG_MTD_UBI=m
+CONFIG_ZRAM=m
+CONFIG_ZRAM_WRITEBACK=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_CRYPTOLOOP=m
+CONFIG_BLK_DEV_DRBD=m
+CONFIG_BLK_DEV_NBD=m
+CONFIG_BLK_DEV_RAM=m
+CONFIG_VIRTIO_BLK=y
+CONFIG_SRAM=y
+CONFIG_EEPROM_AT25=m
+CONFIG_SCSI=y
+# CONFIG_SCSI_PROC_FS is not set
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_ST=m
+CONFIG_CHR_DEV_OSST=m
+CONFIG_BLK_DEV_SR=m
+CONFIG_CHR_DEV_SG=m
+CONFIG_SCSI_SAS_LIBSAS=y
+CONFIG_ISCSI_TCP=m
+CONFIG_ISCSI_BOOT_SYSFS=m
+CONFIG_MD=y
+CONFIG_MD_LINEAR=m
+CONFIG_BLK_DEV_DM=m
+CONFIG_DM_CRYPT=m
+CONFIG_DM_SNAPSHOT=m
+CONFIG_DM_THIN_PROVISIONING=m
+CONFIG_DM_CACHE=m
+CONFIG_DM_MIRROR=m
+CONFIG_DM_LOG_USERSPACE=m
+CONFIG_DM_RAID=m
+CONFIG_DM_ZERO=m
+CONFIG_DM_DELAY=m
+CONFIG_NETDEVICES=y
+CONFIG_BONDING=m
+CONFIG_DUMMY=m
+CONFIG_IFB=m
+CONFIG_MACVLAN=m
+CONFIG_MACVTAP=m
+CONFIG_VXLAN=m
+CONFIG_NETCONSOLE=m
+CONFIG_TUN=y
+CONFIG_VETH=m
+CONFIG_VIRTIO_NET=y
+CONFIG_AMD_XGBE=y
+CONFIG_MACB=y
+CONFIG_HNS_DSAF=y
+CONFIG_HNS_ENET=y
+CONFIG_MVMDIO=y
+CONFIG_QCOM_EMAC=m
+CONFIG_SMC91X=y
+CONFIG_SMSC911X=y
+CONFIG_STMMAC_ETH=y
+# CONFIG_DWMAC_GENERIC is not set
+CONFIG_MDIO_BITBANG=y
+CONFIG_MDIO_BUS_MUX_MMIOREG=y
+CONFIG_MESON_GXL_PHY=y
+CONFIG_MICREL_PHY=y
+CONFIG_REALTEK_PHY=m
+CONFIG_PPP=m
+CONFIG_PPP_BSDCOMP=m
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPP_FILTER=y
+CONFIG_PPP_MPPE=m
+CONFIG_PPP_MULTILINK=y
+CONFIG_PPPOATM=m
+CONFIG_PPPOE=m
+CONFIG_PPPOL2TP=m
+CONFIG_PPP_ASYNC=m
+CONFIG_PPP_SYNC_TTY=m
+CONFIG_SLIP=m
+CONFIG_SLIP_COMPRESSED=y
+CONFIG_SLIP_SMART=y
+CONFIG_USB_PEGASUS=m
+CONFIG_USB_RTL8150=m
+CONFIG_USB_RTL8152=m
+CONFIG_USB_USBNET=m
+CONFIG_USB_NET_DM9601=m
+CONFIG_USB_NET_SR9800=m
+CONFIG_USB_NET_SMSC75XX=m
+CONFIG_USB_NET_SMSC95XX=m
+CONFIG_USB_NET_PLUSB=m
+CONFIG_USB_NET_MCS7830=m
+CONFIG_B43=m
+CONFIG_B43_DEBUG=y
+CONFIG_B43LEGACY=m
+CONFIG_BRCMFMAC=m
+CONFIG_BRCMFMAC_USB=y
+CONFIG_HOSTAP=m
+CONFIG_HOSTAP_FIRMWARE=y
+CONFIG_HOSTAP_FIRMWARE_NVRAM=y
+CONFIG_RTL8187=m
+CONFIG_RTL8192CU=m
+CONFIG_RTL8XXXU=m
+CONFIG_RTL8XXXU_UNTESTED=y
+CONFIG_WL18XX=m
+CONFIG_WLCORE_SDIO=m
+# CONFIG_XEN_NETDEV_FRONTEND is not set
+CONFIG_INPUT_MOUSEDEV=y
+CONFIG_INPUT_MOUSEDEV_PSAUX=y
+CONFIG_INPUT_EVDEV=y
+CONFIG_KEYBOARD_ADC=m
+CONFIG_KEYBOARD_GPIO=y
+CONFIG_KEYBOARD_CROS_EC=y
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_UINPUT=y
+# CONFIG_SERIO_SERPORT is not set
+CONFIG_SERIO_AMBAKMI=y
+CONFIG_LEGACY_PTY_COUNT=16
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_EXTENDED=y
+CONFIG_SERIAL_8250_SHARE_IRQ=y
+CONFIG_SERIAL_8250_DW=y
+CONFIG_SERIAL_OF_PLATFORM=y
+CONFIG_SERIAL_AMBA_PL011=y
+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
+CONFIG_SERIAL_MESON=y
+CONFIG_SERIAL_MESON_CONSOLE=y
+CONFIG_SERIAL_XILINX_PS_UART=y
+CONFIG_SERIAL_XILINX_PS_UART_CONSOLE=y
+CONFIG_SERIAL_DEV_BUS=y
+CONFIG_TTY_PRINTK=y
+CONFIG_VIRTIO_CONSOLE=y
+CONFIG_RAW_DRIVER=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_MUX_PCA954x=y
+CONFIG_I2C_DESIGNWARE_PLATFORM=y
+CONFIG_I2C_MESON=y
+CONFIG_I2C_RK3X=y
+CONFIG_I2C_CROS_EC_TUNNEL=y
+CONFIG_I2C_SLAVE=y
+CONFIG_SPI=y
+CONFIG_SPI_MESON_SPICC=y
+CONFIG_SPI_MESON_SPIFC=m
+CONFIG_SPI_PL022=y
+CONFIG_SPI_SPIDEV=m
+CONFIG_SPMI=y
+CONFIG_PINCTRL_SINGLE=y
+CONFIG_PINCTRL_MAX77620=y
+CONFIG_GPIO_DWAPB=y
+CONFIG_GPIO_PL061=y
+CONFIG_GPIO_XGENE=y
+CONFIG_GPIO_PCA953X=y
+CONFIG_GPIO_PCA953X_IRQ=y
+CONFIG_GPIO_MAX77620=y
+CONFIG_POWER_RESET_VEXPRESS=y
+CONFIG_POWER_RESET_XGENE=y
+CONFIG_POWER_RESET_SYSCON=y
+CONFIG_SENSORS_ARM_SCPI=y
+CONFIG_SENSORS_LM90=m
+CONFIG_SENSORS_INA2XX=m
+CONFIG_THERMAL=y
+CONFIG_THERMAL_GOV_POWER_ALLOCATOR=y
+CONFIG_CPU_THERMAL=y
+CONFIG_THERMAL_EMULATION=y
+CONFIG_WATCHDOG=y
+CONFIG_MESON_GXBB_WATCHDOG=m
+CONFIG_MESON_WATCHDOG=m
+CONFIG_SSB_SDIOHOST=y
+CONFIG_BCMA_HOST_SOC=y
+CONFIG_BCMA_DRIVER_GMAC_CMN=y
+CONFIG_BCMA_DRIVER_GPIO=y
+CONFIG_BCMA_DEBUG=y
+CONFIG_MFD_CROS_EC=y
+CONFIG_MFD_CROS_EC_I2C=y
+CONFIG_MFD_CROS_EC_SPI=y
+CONFIG_MFD_MAX77620=y
+CONFIG_MFD_RK808=y
+CONFIG_MFD_SEC_CORE=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_FAN53555=y
+CONFIG_REGULATOR_GPIO=y
+CONFIG_REGULATOR_MAX77620=y
+CONFIG_REGULATOR_PWM=y
+CONFIG_REGULATOR_QCOM_SPMI=y
+CONFIG_REGULATOR_RK808=y
+CONFIG_REGULATOR_S2MPS11=y
+CONFIG_RC_CORE=m
+CONFIG_RC_DECODERS=y
+CONFIG_IR_NEC_DECODER=m
+CONFIG_IR_RC5_DECODER=m
+CONFIG_IR_RC6_DECODER=m
+CONFIG_IR_JVC_DECODER=m
+CONFIG_IR_SONY_DECODER=m
+CONFIG_IR_SANYO_DECODER=m
+CONFIG_IR_SHARP_DECODER=m
+CONFIG_IR_MCE_KBD_DECODER=m
+CONFIG_IR_XMP_DECODER=m
+CONFIG_RC_DEVICES=y
+CONFIG_IR_MESON=m
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_MEDIA_CAMERA_SUPPORT=y
+CONFIG_MEDIA_ANALOG_TV_SUPPORT=y
+CONFIG_MEDIA_DIGITAL_TV_SUPPORT=y
+CONFIG_MEDIA_CEC_SUPPORT=y
+CONFIG_MEDIA_CONTROLLER=y
+CONFIG_VIDEO_V4L2_SUBDEV_API=y
+# CONFIG_DVB_NET is not set
+CONFIG_V4L_MEM2MEM_DRIVERS=y
+CONFIG_VIDEO_AML_MESON_VDEC=y
+CONFIG_CEC_PLATFORM_DRIVERS=y
+CONFIG_VIDEO_MESON_AO_CEC=m
+CONFIG_DRM=y
+CONFIG_DRM_FBDEV_OVERALLOC=300
+CONFIG_DRM_I2C_CH7006=m
+CONFIG_DRM_I2C_SIL164=m
+CONFIG_DRM_I2C_ADV7511=m
+CONFIG_DRM_MESON=y
+CONFIG_FB_ARMCLCD=y
+# CONFIG_LCD_CLASS_DEVICE is not set
+# CONFIG_BACKLIGHT_GENERIC is not set
+CONFIG_FRAMEBUFFER_CONSOLE=y
+CONFIG_LOGO=y
+# CONFIG_LOGO_LINUX_VGA16 is not set
+# CONFIG_LOGO_LINUX_CLUT224 is not set
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_SOC=y
+CONFIG_SND_SOC_MESON=y
+CONFIG_SND_SOC_MESON_I2S=y
+CONFIG_SND_SOC_MESON_SPDIF=y
+CONFIG_SND_SOC_AK4613=y
+CONFIG_SND_SOC_DIO2125=y
+CONFIG_SND_SOC_ES7134=y
+CONFIG_SND_SIMPLE_CARD=y
+CONFIG_HID_A4TECH=y
+CONFIG_HID_APPLE=y
+CONFIG_HID_BELKIN=y
+CONFIG_HID_CHERRY=y
+CONFIG_HID_CHICONY=y
+CONFIG_HID_CYPRESS=y
+CONFIG_HID_EZKEY=y
+CONFIG_HID_ITE=y
+CONFIG_HID_KENSINGTON=y
+CONFIG_HID_LOGITECH=y
+CONFIG_HID_MICROSOFT=y
+CONFIG_HID_MONTEREY=y
+CONFIG_HID_SONY=y
+CONFIG_SONY_FF=y
+CONFIG_USB=y
+CONFIG_USB_OTG=y
+CONFIG_USB_XHCI_HCD=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_HCD_PLATFORM=y
+CONFIG_USB_OHCI_HCD=y
+CONFIG_USB_OHCI_HCD_PLATFORM=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_DWC3=y
+CONFIG_USB_DWC2=y
+CONFIG_USB_CHIPIDEA=y
+CONFIG_USB_CHIPIDEA_UDC=y
+CONFIG_USB_CHIPIDEA_HOST=y
+CONFIG_USB_ISP1760=y
+CONFIG_USB_HSIC_USB3503=y
+CONFIG_USB_ULPI=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_SNP_UDC_PLAT=y
+CONFIG_MMC=y
+CONFIG_MMC_BLOCK_MINORS=32
+CONFIG_MMC_ARMMMCI=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_SDHCI_OF_ARASAN=y
+CONFIG_MMC_SDHCI_CADENCE=y
+CONFIG_MMC_MESON_GX=y
+CONFIG_MMC_MESON_MX_SDIO=y
+CONFIG_MMC_SPI=y
+CONFIG_MMC_DW=y
+CONFIG_MMC_DW_EXYNOS=y
+CONFIG_MMC_DW_K3=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_GPIO=y
+CONFIG_LEDS_SYSCON=y
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+CONFIG_LEDS_TRIGGER_CPU=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_MAX77686=y
+CONFIG_RTC_DRV_RK808=m
+CONFIG_RTC_DRV_S5M=y
+CONFIG_RTC_DRV_DS3232=y
+CONFIG_RTC_DRV_PL031=y
+CONFIG_DMADEVICES=y
+CONFIG_PL330_DMA=y
+CONFIG_QCOM_HIDMA_MGMT=y
+CONFIG_QCOM_HIDMA=y
+CONFIG_VIRTIO_BALLOON=y
+CONFIG_VIRTIO_MMIO=y
+CONFIG_XEN_GNTDEV=y
+CONFIG_XEN_GRANT_DEV_ALLOC=y
+CONFIG_STAGING=y
+CONFIG_RTL8723BS=m
+CONFIG_R8712U=m
+CONFIG_R8188EU=m
+CONFIG_VT6656=m
+CONFIG_MTD_SPINAND_MT29F=m
+CONFIG_COMMON_CLK_VERSATILE=y
+CONFIG_CLK_SP810=y
+CONFIG_CLK_VEXPRESS_OSC=y
+CONFIG_COMMON_CLK_RK808=y
+CONFIG_COMMON_CLK_SCPI=y
+CONFIG_COMMON_CLK_CS2000_CP=y
+CONFIG_COMMON_CLK_S2MPS11=y
+CONFIG_CLK_QORIQ=y
+CONFIG_COMMON_CLK_PWM=y
+# CONFIG_HISILICON_ERRATUM_161010101 is not set
+# CONFIG_ARM64_ERRATUM_858921 is not set
+CONFIG_ARM_TIMER_SP804=y
+CONFIG_MAILBOX=y
+CONFIG_ARM_MHU=y
+CONFIG_PLATFORM_MHU=y
+# CONFIG_IOMMU_SUPPORT is not set
+CONFIG_EXTCON_USB_GPIO=y
+CONFIG_IIO=y
+CONFIG_PWM=y
+CONFIG_PWM_MESON=m
+CONFIG_PHY_XGENE=y
+CONFIG_PHY_SAMSUNG_USB2=y
+CONFIG_MESON_EFUSE=y
+CONFIG_MESON_MX_EFUSE=y
+CONFIG_TEE=y
+CONFIG_OPTEE=y
+CONFIG_ARM_SCPI_PROTOCOL=y
+CONFIG_EXT2_FS=y
+CONFIG_EXT3_FS=y
+CONFIG_EXT4_FS_POSIX_ACL=y
+CONFIG_EXT4_FS_SECURITY=y
+CONFIG_JBD2_DEBUG=y
+CONFIG_REISERFS_FS=y
+CONFIG_JFS_FS=y
+CONFIG_JFS_POSIX_ACL=y
+CONFIG_JFS_SECURITY=y
+CONFIG_JFS_STATISTICS=y
+CONFIG_XFS_FS=m
+CONFIG_XFS_QUOTA=y
+CONFIG_XFS_POSIX_ACL=y
+CONFIG_XFS_RT=y
+CONFIG_GFS2_FS=m
+CONFIG_OCFS2_FS=m
+CONFIG_BTRFS_FS=m
+CONFIG_BTRFS_FS_POSIX_ACL=y
+CONFIG_NILFS2_FS=m
+CONFIG_F2FS_FS=y
+CONFIG_F2FS_FS_SECURITY=y
+CONFIG_F2FS_CHECK_FS=y
+CONFIG_F2FS_FS_ENCRYPTION=y
+CONFIG_FANOTIFY=y
+CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y
+CONFIG_QFMT_V1=m
+CONFIG_QFMT_V2=m
+CONFIG_AUTOFS4_FS=y
+CONFIG_FUSE_FS=m
+CONFIG_CUSE=m
+CONFIG_OVERLAY_FS=m
+CONFIG_FSCACHE=y
+CONFIG_FSCACHE_STATS=y
+CONFIG_FSCACHE_HISTOGRAM=y
+CONFIG_CACHEFILES=y
+CONFIG_ISO9660_FS=m
+CONFIG_JOLIET=y
+CONFIG_ZISOFS=y
+CONFIG_UDF_FS=m
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_FAT_DEFAULT_IOCHARSET="ascii"
+CONFIG_FAT_DEFAULT_UTF8=y
+CONFIG_NTFS_FS=m
+CONFIG_NTFS_RW=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_HUGETLBFS=y
+CONFIG_CONFIGFS_FS=y
+CONFIG_ECRYPT_FS=m
+CONFIG_HFS_FS=m
+CONFIG_HFSPLUS_FS=m
+CONFIG_JFFS2_FS=m
+CONFIG_JFFS2_SUMMARY=y
+CONFIG_UBIFS_FS=m
+CONFIG_SQUASHFS=m
+CONFIG_SQUASHFS_XATTR=y
+CONFIG_SQUASHFS_LZ4=y
+CONFIG_SQUASHFS_LZO=y
+CONFIG_SQUASHFS_XZ=y
+CONFIG_SQUASHFS_ZSTD=y
+CONFIG_PSTORE=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3_ACL=y
+CONFIG_NFS_V4=y
+CONFIG_NFS_SWAP=y
+CONFIG_NFS_V4_1=y
+CONFIG_NFS_V4_2=y
+CONFIG_NFS_V4_1_MIGRATION=y
+CONFIG_ROOT_NFS=y
+CONFIG_NFSD=m
+CONFIG_NFSD_V3_ACL=y
+CONFIG_NFSD_V4=y
+CONFIG_NFSD_BLOCKLAYOUT=y
+CONFIG_NFSD_SCSILAYOUT=y
+CONFIG_NFSD_FLEXFILELAYOUT=y
+CONFIG_CIFS=m
+CONFIG_CIFS_WEAK_PW_HASH=y
+CONFIG_CIFS_UPCALL=y
+CONFIG_CIFS_XATTR=y
+CONFIG_CIFS_POSIX=y
+CONFIG_CIFS_ACL=y
+CONFIG_CIFS_DFS_UPCALL=y
+CONFIG_CIFS_FSCACHE=y
+CONFIG_9P_FS=m
+CONFIG_9P_FS_POSIX_ACL=y
+CONFIG_NLS_DEFAULT="utf8"
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_737=m
+CONFIG_NLS_CODEPAGE_775=m
+CONFIG_NLS_CODEPAGE_850=m
+CONFIG_NLS_CODEPAGE_852=m
+CONFIG_NLS_CODEPAGE_855=m
+CONFIG_NLS_CODEPAGE_857=m
+CONFIG_NLS_CODEPAGE_860=m
+CONFIG_NLS_CODEPAGE_861=m
+CONFIG_NLS_CODEPAGE_862=m
+CONFIG_NLS_CODEPAGE_863=m
+CONFIG_NLS_CODEPAGE_864=m
+CONFIG_NLS_CODEPAGE_865=m
+CONFIG_NLS_CODEPAGE_866=m
+CONFIG_NLS_CODEPAGE_869=m
+CONFIG_NLS_CODEPAGE_936=m
+CONFIG_NLS_CODEPAGE_950=m
+CONFIG_NLS_CODEPAGE_932=m
+CONFIG_NLS_CODEPAGE_949=m
+CONFIG_NLS_CODEPAGE_874=m
+CONFIG_NLS_ISO8859_8=m
+CONFIG_NLS_CODEPAGE_1250=m
+CONFIG_NLS_CODEPAGE_1251=m
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=m
+CONFIG_NLS_ISO8859_2=m
+CONFIG_NLS_ISO8859_3=m
+CONFIG_NLS_ISO8859_4=m
+CONFIG_NLS_ISO8859_5=m
+CONFIG_NLS_ISO8859_6=m
+CONFIG_NLS_ISO8859_7=m
+CONFIG_NLS_ISO8859_9=m
+CONFIG_NLS_ISO8859_13=m
+CONFIG_NLS_ISO8859_14=m
+CONFIG_NLS_ISO8859_15=m
+CONFIG_NLS_KOI8_R=m
+CONFIG_NLS_KOI8_U=m
+CONFIG_VIRTUALIZATION=y
+CONFIG_KVM=y
+CONFIG_VHOST_NET=m
+CONFIG_VHOST_CROSS_ENDIAN_LEGACY=y
+CONFIG_PRINTK_TIME=y
+CONFIG_DYNAMIC_DEBUG=y
+CONFIG_DEBUG_FS=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DETECT_HUNG_TASK=y
+# CONFIG_SCHED_DEBUG is not set
+# CONFIG_DEBUG_PREEMPT is not set
+# CONFIG_FTRACE is not set
+CONFIG_MEMTEST=y
+# CONFIG_STRICT_DEVMEM is not set
+CONFIG_SECURITY=y
+CONFIG_CRYPTO_ECHAINIV=y
+CONFIG_CRYPTO_LZ4=m
+CONFIG_CRYPTO_LZ4HC=m
+CONFIG_CRYPTO_ANSI_CPRNG=y
+CONFIG_ARM64_CRYPTO=y
+CONFIG_CRYPTO_SHA1_ARM64_CE=y
+CONFIG_CRYPTO_SHA2_ARM64_CE=y
+CONFIG_CRYPTO_GHASH_ARM64_CE=y
+CONFIG_CRYPTO_AES_ARM64_CE_CCM=y
+CONFIG_CRYPTO_AES_ARM64_CE_BLK=y
+CONFIG_CRYPTO_CHACHA20_NEON=y
+CONFIG_CRYPTO_AES_ARM64_BS=y
+CONFIG_CORDIC=m
diff --git a/testing/linux-amlogic/off_error_text_offset.patch b/testing/linux-amlogic/off_error_text_offset.patch
new file mode 100644
index 0000000000..a5069502b6
--- /dev/null
+++ b/testing/linux-amlogic/off_error_text_offset.patch
@@ -0,0 +1,13 @@
+diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S
+index b085306..68edc91 100644
+--- a/arch/arm64/kernel/head.S
++++ b/arch/arm64/kernel/head.S
+@@ -51,7 +51,7 @@
+ #elif (PAGE_OFFSET & 0x1fffff) != 0
+ #error PAGE_OFFSET must be at least 2MB aligned
+ #elif TEXT_OFFSET > 0x1fffff
+-#error TEXT_OFFSET must be less than 2MB
++//#error TEXT_OFFSET must be less than 2MB
+ #endif
+
+ /*
diff --git a/testing/linux-amlogic/offset.patch b/testing/linux-amlogic/offset.patch
new file mode 100644
index 0000000000..644b65ee4a
--- /dev/null
+++ b/testing/linux-amlogic/offset.patch
@@ -0,0 +1,13 @@
+diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile
+index e7101b1..deabd29 100644
+--- a/arch/arm64/Makefile
++++ b/arch/arm64/Makefile
+@@ -87,7 +87,7 @@ TEXT_OFFSET := $(shell awk "BEGIN {srand(); printf \"0x%06x\n\", \
+ int(2 * 1024 * 1024 / (2 ^ $(CONFIG_ARM64_PAGE_SHIFT)) * \
+ rand()) * (2 ^ $(CONFIG_ARM64_PAGE_SHIFT))}")
+ else
+-TEXT_OFFSET := 0x00080000
++TEXT_OFFSET := 0x01080000
+ endif
+
+ # KASAN_SHADOW_OFFSET = VA_START + (1 << (VA_BITS - KASAN_SHADOW_SCALE_SHIFT))