aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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))