diff options
Diffstat (limited to 'testing')
75 files changed, 8854 insertions, 8704 deletions
diff --git a/testing/linux-amlogic/0003-ARM64-defconfig-enable-CEC-support.patch b/testing/linux-amlogic/0001-ARM64-defconfig-enable-CEC-support.patch index 7c04717531..41791a5c25 100644 --- a/testing/linux-amlogic/0003-ARM64-defconfig-enable-CEC-support.patch +++ b/testing/linux-amlogic/0001-ARM64-defconfig-enable-CEC-support.patch @@ -1,4 +1,4 @@ -From e41c06328d1cd0989899d6a0897c6857d0cf9a4b Mon Sep 17 00:00:00 2001 +From 0c815d66dbaa54b53e4b54105992ce44dfe8c36f 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 @@ -9,15 +9,16 @@ 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 +index db8d364..ab1cb51 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig -@@ -402,6 +402,7 @@ CONFIG_MEDIA_SUPPORT=m +@@ -413,6 +413,7 @@ CONFIG_MEDIA_SUPPORT=m CONFIG_MEDIA_CAMERA_SUPPORT=y CONFIG_MEDIA_ANALOG_TV_SUPPORT=y CONFIG_MEDIA_DIGITAL_TV_SUPPORT=y @@ -25,7 +26,7 @@ index f9a186f..2584605 100644 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 +@@ -424,6 +425,8 @@ CONFIG_VIDEO_SAMSUNG_S5P_MFC=m CONFIG_VIDEO_SAMSUNG_EXYNOS_GSC=m CONFIG_VIDEO_RENESAS_FCP=m CONFIG_VIDEO_RENESAS_VSP1=m @@ -34,7 +35,7 @@ index f9a186f..2584605 100644 CONFIG_DRM=m CONFIG_DRM_NOUVEAU=m CONFIG_DRM_EXYNOS=m -@@ -431,6 +434,7 @@ CONFIG_DRM_RCAR_LVDS=m +@@ -444,6 +447,7 @@ CONFIG_DRM_RCAR_LVDS=m CONFIG_DRM_TEGRA=m CONFIG_DRM_PANEL_SIMPLE=m CONFIG_DRM_I2C_ADV7511=m 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 deleted file mode 100644 index aad63ba8fe..0000000000 --- a/testing/linux-amlogic/0001-ARM64-dts-meson-gxbb-nanopi-k2-Add-HDMI-CEC-and-CVBS.patch +++ /dev/null @@ -1,81 +0,0 @@ -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>; -+ }; - }; - - ðmac { -@@ -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-arm64-dts-meson-Fix-IRQ-trigger-type-for-macirq.patch b/testing/linux-amlogic/0001-arm64-dts-meson-Fix-IRQ-trigger-type-for-macirq.patch new file mode 100644 index 0000000000..5bcec06be2 --- /dev/null +++ b/testing/linux-amlogic/0001-arm64-dts-meson-Fix-IRQ-trigger-type-for-macirq.patch @@ -0,0 +1,62 @@ +From e2c70445f3533e8fa62f8c645713508148f8e4f6 Mon Sep 17 00:00:00 2001 +From: Carlo Caione <ccaione@baylibre.com> +Date: Tue, 4 Dec 2018 16:04:46 +0000 +Subject: [PATCH] arm64: dts: meson: Fix IRQ trigger type for macirq + +A long running stress test on a custom board shipping an AXG SoCs and a +Realtek RTL8211F PHY revealed that after a few hours the connection +speed would drop drastically, from ~1000Mbps to ~3Mbps. At the same time +the 'macirq' (eth0) IRQ would stop being triggered at all and as +consequence the GMAC IRQs never ACKed. + +After a painful investigation the problem seemed to be due to a wrong +defined IRQ type for the GMAC IRQ that should be LEVEL_HIGH instead of +EDGE_RISING. + +Signed-off-by: Carlo Caione <ccaione@baylibre.com> +Acked-by: Neil Armstrong <narmstrong@baylibre.com> +--- + arch/arm/boot/dts/meson.dtsi | 2 +- + arch/arm64/boot/dts/amlogic/meson-axg.dtsi | 2 +- + arch/arm64/boot/dts/amlogic/meson-gx.dtsi | 2 +- + 3 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/arch/arm/boot/dts/meson.dtsi b/arch/arm/boot/dts/meson.dtsi +index 0d9faf1a51ea..a86b89086334 100644 +--- a/arch/arm/boot/dts/meson.dtsi ++++ b/arch/arm/boot/dts/meson.dtsi +@@ -263,7 +263,7 @@ + compatible = "amlogic,meson6-dwmac", "snps,dwmac"; + reg = <0xc9410000 0x10000 + 0xc1108108 0x4>; +- interrupts = <GIC_SPI 8 IRQ_TYPE_EDGE_RISING>; ++ interrupts = <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "macirq"; + status = "disabled"; + }; +diff --git a/arch/arm64/boot/dts/amlogic/meson-axg.dtsi b/arch/arm64/boot/dts/amlogic/meson-axg.dtsi +index c518130e5ce7..81dcbde9e674 100644 +--- a/arch/arm64/boot/dts/amlogic/meson-axg.dtsi ++++ b/arch/arm64/boot/dts/amlogic/meson-axg.dtsi +@@ -461,7 +461,7 @@ + compatible = "amlogic,meson-gxbb-dwmac", "snps,dwmac"; + reg = <0x0 0xff3f0000 0x0 0x10000 + 0x0 0xff634540 0x0 0x8>; +- interrupts = <GIC_SPI 8 IRQ_TYPE_EDGE_RISING>; ++ interrupts = <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "macirq"; + clocks = <&clkc CLKID_ETH>, + <&clkc CLKID_FCLK_DIV2>, +diff --git a/arch/arm64/boot/dts/amlogic/meson-gx.dtsi b/arch/arm64/boot/dts/amlogic/meson-gx.dtsi +index 5d2820ef9a88..d03737acbae1 100644 +--- a/arch/arm64/boot/dts/amlogic/meson-gx.dtsi ++++ b/arch/arm64/boot/dts/amlogic/meson-gx.dtsi +@@ -511,7 +511,7 @@ + compatible = "amlogic,meson-gx-dwmac", "amlogic,meson-gxbb-dwmac", "snps,dwmac"; + reg = <0x0 0xc9410000 0x0 0x10000 + 0x0 0xc8834540 0x0 0x4>; +- interrupts = <GIC_SPI 8 IRQ_TYPE_EDGE_RISING>; ++ interrupts = <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "macirq"; + status = "disabled"; + }; diff --git a/testing/linux-amlogic/0001-drm-meson-fix-max-mode_config-height-width.patch b/testing/linux-amlogic/0001-drm-meson-fix-max-mode_config-height-width.patch new file mode 100644 index 0000000000..b9c5c2ca50 --- /dev/null +++ b/testing/linux-amlogic/0001-drm-meson-fix-max-mode_config-height-width.patch @@ -0,0 +1,33 @@ +From a2b8a766c2fb57096c065e539d93f9f4fbd7ace4 Mon Sep 17 00:00:00 2001 +From: Neil Armstrong <narmstrong@baylibre.com> +Date: Thu, 4 Oct 2018 10:42:43 +0200 +Subject: [PATCH] drm/meson: fix max mode_config height/width + +The mode_config max_width/max_height determines the maximum framebuffer +size the pixel reader can handle. But the values were set thinking they +were determining the maximum screen dimensions. + +This patch changes the values to the maximum height/width the CANVAS block +can handle rounded to some coherent values. + +Fixes: a41e82e6c457 ("drm/meson: Add support for components") +Signed-off-by: Neil Armstrong <narmstrong@baylibre.com> +--- + drivers/gpu/drm/meson/meson_drv.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c +index 3ee4d4a4ecba..b3c04ecc75f3 100644 +--- a/drivers/gpu/drm/meson/meson_drv.c ++++ b/drivers/gpu/drm/meson/meson_drv.c +@@ -263,8 +263,8 @@ static int meson_drv_bind_master(struct device *dev, bool has_components) + goto free_drm; + + drm_mode_config_init(drm); +- drm->mode_config.max_width = 3840; +- drm->mode_config.max_height = 2160; ++ drm->mode_config.max_width = 16384; ++ drm->mode_config.max_height = 8192; + drm->mode_config.funcs = &meson_mode_config_funcs; + + /* Hardware Initialization */ diff --git a/testing/linux-amlogic/0001-libretech-cc-disable-CVBS-connector.patch b/testing/linux-amlogic/0001-libretech-cc-disable-CVBS-connector.patch deleted file mode 100644 index 7f82981189..0000000000 --- a/testing/linux-amlogic/0001-libretech-cc-disable-CVBS-connector.patch +++ /dev/null @@ -1,24 +0,0 @@ -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/0006-ASoC-meson-add-meson-audio-core-driver.patch b/testing/linux-amlogic/0002-ASoC-meson-add-meson-audio-core-driver.patch index 69c0ab2995..6acbf80c86 100644 --- a/testing/linux-amlogic/0006-ASoC-meson-add-meson-audio-core-driver.patch +++ b/testing/linux-amlogic/0002-ASoC-meson-add-meson-audio-core-driver.patch @@ -1,4 +1,4 @@ -From 878d41be386f0bcbe1475f65acd8b9fb304529a0 Mon Sep 17 00:00:00 2001 +From 4770a464de7b87bc849e4e110f197ef9fa7bccf6 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 @@ -15,49 +15,25 @@ 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/Kconfig | 10 +++ + sound/soc/meson/Makefile | 4 + 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 + 4 files changed, 232 insertions(+) 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 +index 8af8bc3..ed432d4 100644 +--- a/sound/soc/meson/Kconfig +++ b/sound/soc/meson/Kconfig -@@ -0,0 +1,9 @@ +@@ -63,3 +63,13 @@ config SND_MESON_AXG_SPDIFOUT + in the Amlogic AXG SoC family + + endmenu ++ +menuconfig SND_SOC_MESON + tristate "ASoC support for Amlogic Meson SoCs" + depends on ARCH_MESON @@ -68,14 +44,18 @@ index 0000000..ca0e3e9 + 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 +index c5e003b..768d7c4 100644 +--- a/sound/soc/meson/Makefile +++ b/sound/soc/meson/Makefile -@@ -0,0 +1,3 @@ +@@ -19,3 +19,7 @@ obj-$(CONFIG_SND_MESON_AXG_TDMIN) += snd-soc-meson-axg-tdmin.o + obj-$(CONFIG_SND_MESON_AXG_TDMOUT) += snd-soc-meson-axg-tdmout.o + obj-$(CONFIG_SND_MESON_AXG_SOUND_CARD) += snd-soc-meson-axg-sound-card.o + obj-$(CONFIG_SND_MESON_AXG_SPDIFOUT) += snd-soc-meson-axg-spdifout.o ++ +snd-soc-meson-audio-core-objs := audio-core.o + -+obj-$(CONFIG_SND_SOC_MESON) += snd-soc-meson-audio-core.o ++obj-$(CONFIG_SND_SOC_MESON) += snd-soc-meson-audio-core.o +\ No newline at end of file diff --git a/sound/soc/meson/audio-core.c b/sound/soc/meson/audio-core.c new file mode 100644 index 0000000..99993ec 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 deleted file mode 100644 index 7625f11f5b..0000000000 --- a/testing/linux-amlogic/0002-drm-meson-Make-DMT-timings-parameters-and-pixel-cloc.patch +++ /dev/null @@ -1,1314 +0,0 @@ -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/0007-ASoC-meson-add-register-definitions.patch b/testing/linux-amlogic/0003-ASoC-meson-add-register-definitions.patch index 0a13a427cc..cb4027a2c4 100644 --- a/testing/linux-amlogic/0007-ASoC-meson-add-register-definitions.patch +++ b/testing/linux-amlogic/0003-ASoC-meson-add-register-definitions.patch @@ -1,4 +1,4 @@ -From f6c0ce626b08f5ba85bd9bfe000044c4f9e7da24 Mon Sep 17 00:00:00 2001 +From dc1d93ea8dc61686364e2659c1ac1392681b64b4 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 @@ -6,6 +6,7 @@ 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 +++++++++++++++++++++++++++++++++++ diff --git a/testing/linux-amlogic/0008-ASoC-meson-add-aiu-i2s-dma-support.patch b/testing/linux-amlogic/0004-ASoC-meson-add-aiu-i2s-dma-support.patch index 1de0e444ad..900659845e 100644 --- a/testing/linux-amlogic/0008-ASoC-meson-add-aiu-i2s-dma-support.patch +++ b/testing/linux-amlogic/0004-ASoC-meson-add-aiu-i2s-dma-support.patch @@ -1,4 +1,4 @@ -From bb5102086db1579c1289440fa8aa184a70cb7c64 Mon Sep 17 00:00:00 2001 +From ef53207463b1ffa58dbc8b994cb470f35bf12420 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 @@ -6,18 +6,19 @@ 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/Makefile | 4 +- sound/soc/meson/aiu-i2s-dma.c | 370 ++++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 379 insertions(+) + 3 files changed, 380 insertions(+), 1 deletion(-) 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 +index ed432d4..6e030b5 100644 --- a/sound/soc/meson/Kconfig +++ b/sound/soc/meson/Kconfig -@@ -7,3 +7,10 @@ menuconfig SND_SOC_MESON +@@ -73,3 +73,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. @@ -29,15 +30,20 @@ index ca0e3e9..88fbfc2 100644 + 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 +index 768d7c4..5796007 100644 --- a/sound/soc/meson/Makefile +++ b/sound/soc/meson/Makefile -@@ -1,3 +1,5 @@ +@@ -21,5 +21,7 @@ obj-$(CONFIG_SND_MESON_AXG_SOUND_CARD) += snd-soc-meson-axg-sound-card.o + obj-$(CONFIG_SND_MESON_AXG_SPDIFOUT) += snd-soc-meson-axg-spdifout.o + 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 +-obj-$(CONFIG_SND_SOC_MESON) += snd-soc-meson-audio-core.o +\ No newline at end of file ++obj-$(CONFIG_SND_SOC_MESON) += snd-soc-meson-audio-core.o ++obj-$(CONFIG_SND_SOC_MESON_I2S) += snd-soc-meson-aiu-i2s-dma.o +\ No newline at end of file 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 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 deleted file mode 100644 index e2b3860f5a..0000000000 --- a/testing/linux-amlogic/0004-clk-meson-switch-gxbb-cts-amclk-div-to-the-generic-d.patch +++ /dev/null @@ -1,44 +0,0 @@ -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/0009-ASoC-meson-add-initial-i2s-dai-support.patch b/testing/linux-amlogic/0005-ASoC-meson-add-initial-i2s-dai-support.patch index f3bac343f7..e1be6fab36 100644 --- a/testing/linux-amlogic/0009-ASoC-meson-add-initial-i2s-dai-support.patch +++ b/testing/linux-amlogic/0005-ASoC-meson-add-initial-i2s-dai-support.patch @@ -1,4 +1,4 @@ -From 6a5036e9f7dbd99023c6f9482fed3a747868b1c2 Mon Sep 17 00:00:00 2001 +From 9031b415030a316ec4ca513185e2d2c0fbb894c4 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 @@ -8,18 +8,19 @@ 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/Makefile | 4 +- sound/soc/meson/i2s-dai.c | 465 ++++++++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 468 insertions(+), 1 deletion(-) + 3 files changed, 469 insertions(+), 2 deletions(-) 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 +index 6e030b5..5904e9e 100644 --- a/sound/soc/meson/Kconfig +++ b/sound/soc/meson/Kconfig -@@ -12,5 +12,5 @@ config SND_SOC_MESON_I2S +@@ -78,5 +78,5 @@ config SND_SOC_MESON_I2S tristate "Meson i2s interface" depends on SND_SOC_MESON help @@ -27,17 +28,21 @@ index 88fbfc2..478f29a 100644 + 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 +index 5796007..b8641f9 100644 --- a/sound/soc/meson/Makefile +++ b/sound/soc/meson/Makefile -@@ -1,5 +1,7 @@ +@@ -22,6 +22,8 @@ obj-$(CONFIG_SND_MESON_AXG_SPDIFOUT) += snd-soc-meson-axg-spdifout.o + 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 + obj-$(CONFIG_SND_SOC_MESON) += snd-soc-meson-audio-core.o +-obj-$(CONFIG_SND_SOC_MESON_I2S) += snd-soc-meson-aiu-i2s-dma.o +\ No newline at end of file ++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 +\ No newline at end of file diff --git a/sound/soc/meson/i2s-dai.c b/sound/soc/meson/i2s-dai.c new file mode 100644 index 0000000..1008af8 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 deleted file mode 100644 index cd6da8cf57..0000000000 --- a/testing/linux-amlogic/0005-clk-meson-remove-unused-clk-audio-divider-driver.patch +++ /dev/null @@ -1,48 +0,0 @@ -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/0010-ASoC-meson-add-aiu-spdif-dma-support.patch b/testing/linux-amlogic/0006-ASoC-meson-add-aiu-spdif-dma-support.patch index 380f5d5b2c..31cef2e53d 100644 --- a/testing/linux-amlogic/0010-ASoC-meson-add-aiu-spdif-dma-support.patch +++ b/testing/linux-amlogic/0006-ASoC-meson-add-aiu-spdif-dma-support.patch @@ -1,4 +1,4 @@ -From 8dd5edaf984e4c8d6f7ca1e7709b3109cf7dd780 Mon Sep 17 00:00:00 2001 +From 32a55958cc2d89e2feee831ca4e6ceae8458e950 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 @@ -6,18 +6,19 @@ 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/Makefile | 4 +- sound/soc/meson/aiu-spdif-dma.c | 388 ++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 397 insertions(+) + 3 files changed, 398 insertions(+), 1 deletion(-) 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 +index 5904e9e..712303f 100644 --- a/sound/soc/meson/Kconfig +++ b/sound/soc/meson/Kconfig -@@ -14,3 +14,10 @@ config SND_SOC_MESON_I2S +@@ -80,3 +80,10 @@ config SND_SOC_MESON_I2S help Say Y or M if you want to add support for i2s driver for Amlogic Meson SoCs. @@ -29,19 +30,23 @@ index 478f29a..3fb93b9 100644 + 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 +index b8641f9..dc5164a7 100644 --- a/sound/soc/meson/Makefile +++ b/sound/soc/meson/Makefile -@@ -1,7 +1,9 @@ +@@ -22,8 +22,10 @@ obj-$(CONFIG_SND_MESON_AXG_SPDIFOUT) += snd-soc-meson-axg-spdifout.o + 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 + 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 +\ No newline at end of file ++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 +\ No newline at end of file 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 diff --git a/testing/linux-amlogic/0011-ASoC-meson-add-initial-spdif-dai-support.patch b/testing/linux-amlogic/0007-ASoC-meson-add-initial-spdif-dai-support.patch index 3d36f4b064..c60779b465 100644 --- a/testing/linux-amlogic/0011-ASoC-meson-add-initial-spdif-dai-support.patch +++ b/testing/linux-amlogic/0007-ASoC-meson-add-initial-spdif-dai-support.patch @@ -1,4 +1,4 @@ -From e635299f76dc27b97a768f2a044d04c1917b9ad1 Mon Sep 17 00:00:00 2001 +From 67e2a1601f80648f5c318728218b788c51081fa3 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 @@ -9,18 +9,19 @@ 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/Makefile | 4 +- sound/soc/meson/spdif-dai.c | 374 ++++++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 378 insertions(+), 1 deletion(-) + 3 files changed, 379 insertions(+), 2 deletions(-) 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 +index 712303f..bc3d6f2 100644 --- a/sound/soc/meson/Kconfig +++ b/sound/soc/meson/Kconfig -@@ -18,6 +18,7 @@ config SND_SOC_MESON_I2S +@@ -84,6 +84,7 @@ config SND_SOC_MESON_I2S config SND_SOC_MESON_SPDIF tristate "Meson spdif interface" depends on SND_SOC_MESON @@ -30,20 +31,23 @@ index 3fb93b9..301d3a3 100644 + 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 +index dc5164a7..44f79d8 100644 --- a/sound/soc/meson/Makefile +++ b/sound/soc/meson/Makefile -@@ -2,8 +2,10 @@ snd-soc-meson-audio-core-objs := audio-core.o +@@ -24,8 +24,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 + 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 +\ No newline at end of file ++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 +\ No newline at end of file diff --git a/sound/soc/meson/spdif-dai.c b/sound/soc/meson/spdif-dai.c new file mode 100644 index 0000000..e763000 diff --git a/testing/linux-amlogic/0012-ARM64-defconfig-enable-audio-support-for-meson-SoCs-.patch b/testing/linux-amlogic/0008-ARM64-defconfig-enable-audio-support-for-meson-SoCs-.patch index ff2035ecc8..2f8854bf36 100644 --- a/testing/linux-amlogic/0012-ARM64-defconfig-enable-audio-support-for-meson-SoCs-.patch +++ b/testing/linux-amlogic/0008-ARM64-defconfig-enable-audio-support-for-meson-SoCs-.patch @@ -1,4 +1,4 @@ -From 5ddca63ac5c5d81c6d6a6745670a3f136970eaef Mon Sep 17 00:00:00 2001 +From 93621178cb2f756c8ca8901801c2ce914ac7dc6b 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 @@ -8,22 +8,22 @@ 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(+) + arch/arm64/configs/defconfig | 3 +++ + 1 file changed, 3 insertions(+) diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig -index 2584605..ae1f774 100644 +index ab1cb51..a4bf54b 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig -@@ -451,6 +451,10 @@ CONFIG_SOUND=y +@@ -464,6 +464,9 @@ 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 + CONFIG_SND_SOC_ROCKCHIP=m + CONFIG_SND_SOC_ROCKCHIP_I2S=m + CONFIG_SND_SOC_ROCKCHIP_SPDIF=m diff --git a/testing/linux-amlogic/0008-drm-meson-Add-HDMI-1.4-4k-modes.patch b/testing/linux-amlogic/0008-drm-meson-Add-HDMI-1.4-4k-modes.patch new file mode 100644 index 0000000000..194ba6e2de --- /dev/null +++ b/testing/linux-amlogic/0008-drm-meson-Add-HDMI-1.4-4k-modes.patch @@ -0,0 +1,165 @@ +From 24b38ca3bd2579d4bac18f57526c93bc63354959 Mon Sep 17 00:00:00 2001 +From: Neil Armstrong <narmstrong@baylibre.com> +Date: Fri, 20 Jul 2018 15:29:18 +0200 +Subject: [PATCH] drm/meson: Add HDMI 1.4 4k modes + +Add the timings for the HDMI 1.4 4K modes support : +- 3840x2160@30 +- 3840x2160@25 +- 3840x2160@24 + +Since the 297000Hz pixel clock is already managed and the modes are +compatible with the HDMI 1.4 current HDMI PHY+Controller support, only +the missing timings values needs to be added. + +--- + drivers/gpu/drm/meson/meson_venc.c | 129 +++++++++++++++++++++++++++++++++++++ + 1 file changed, 129 insertions(+) + +diff --git a/drivers/gpu/drm/meson/meson_venc.c b/drivers/gpu/drm/meson/meson_venc.c +index 14aac66..d68ccbf 100644 +--- a/drivers/gpu/drm/meson/meson_venc.c ++++ b/drivers/gpu/drm/meson/meson_venc.c +@@ -698,6 +698,132 @@ union meson_hdmi_venc_mode meson_hdmi_encp_mode_1080p60 = { + }, + }; + ++union meson_hdmi_venc_mode meson_hdmi_encp_mode_2160p24 = { ++ .encp = { ++ .dvi_settings = 0x1, ++ .video_mode = 0x4040, ++ .video_mode_adv = 0x8, ++ /* video_sync_mode */ ++ /* video_yc_dly */ ++ /* video_rgb_ctrl */ ++ .video_filt_ctrl = 0x1000, ++ .video_filt_ctrl_present = true, ++ /* video_ofld_voav_ofst */ ++ .yfp1_htime = 140, ++ .yfp2_htime = 140+3840, ++ .max_pxcnt = 3840+1660-1, ++ .hspuls_begin = 2156+1920, ++ .hspuls_end = 44, ++ .hspuls_switch = 44, ++ .vspuls_begin = 140, ++ .vspuls_end = 2059+1920, ++ .vspuls_bline = 0, ++ .vspuls_eline = 4, ++ .havon_begin = 148, ++ .havon_end = 3987, ++ .vavon_bline = 89, ++ .vavon_eline = 2248, ++ /* eqpuls_begin */ ++ /* eqpuls_end */ ++ /* eqpuls_bline */ ++ /* eqpuls_eline */ ++ .hso_begin = 44, ++ .hso_end = 2156+1920, ++ .vso_begin = 2100+1920, ++ .vso_end = 2164+1920, ++ .vso_bline = 51, ++ .vso_eline = 53, ++ .vso_eline_present = true, ++ /* sy_val */ ++ /* sy2_val */ ++ .max_lncnt = 2249, ++ }, ++}; ++ ++union meson_hdmi_venc_mode meson_hdmi_encp_mode_2160p25 = { ++ .encp = { ++ .dvi_settings = 0x1, ++ .video_mode = 0x4040, ++ .video_mode_adv = 0x8, ++ /* video_sync_mode */ ++ /* video_yc_dly */ ++ /* video_rgb_ctrl */ ++ .video_filt_ctrl = 0x1000, ++ .video_filt_ctrl_present = true, ++ /* video_ofld_voav_ofst */ ++ .yfp1_htime = 140, ++ .yfp2_htime = 140+3840, ++ .max_pxcnt = 3840+1440-1, ++ .hspuls_begin = 2156+1920, ++ .hspuls_end = 44, ++ .hspuls_switch = 44, ++ .vspuls_begin = 140, ++ .vspuls_end = 2059+1920, ++ .vspuls_bline = 0, ++ .vspuls_eline = 4, ++ .havon_begin = 148, ++ .havon_end = 3987, ++ .vavon_bline = 89, ++ .vavon_eline = 2248, ++ /* eqpuls_begin */ ++ /* eqpuls_end */ ++ /* eqpuls_bline */ ++ /* eqpuls_eline */ ++ .hso_begin = 44, ++ .hso_end = 2156+1920, ++ .vso_begin = 2100+1920, ++ .vso_end = 2164+1920, ++ .vso_bline = 51, ++ .vso_eline = 53, ++ .vso_eline_present = true, ++ /* sy_val */ ++ /* sy2_val */ ++ .max_lncnt = 2249, ++ }, ++}; ++ ++union meson_hdmi_venc_mode meson_hdmi_encp_mode_2160p30 = { ++ .encp = { ++ .dvi_settings = 0x1, ++ .video_mode = 0x4040, ++ .video_mode_adv = 0x8, ++ /* video_sync_mode */ ++ /* video_yc_dly */ ++ /* video_rgb_ctrl */ ++ .video_filt_ctrl = 0x1000, ++ .video_filt_ctrl_present = true, ++ /* video_ofld_voav_ofst */ ++ .yfp1_htime = 140, ++ .yfp2_htime = 140+3840, ++ .max_pxcnt = 3840+560-1, ++ .hspuls_begin = 2156+1920, ++ .hspuls_end = 44, ++ .hspuls_switch = 44, ++ .vspuls_begin = 140, ++ .vspuls_end = 2059+1920, ++ .vspuls_bline = 0, ++ .vspuls_eline = 4, ++ .havon_begin = 148, ++ .havon_end = 3987, ++ .vavon_bline = 89, ++ .vavon_eline = 2248, ++ /* eqpuls_begin */ ++ /* eqpuls_end */ ++ /* eqpuls_bline */ ++ /* eqpuls_eline */ ++ .hso_begin = 44, ++ .hso_end = 2156+1920, ++ .vso_begin = 2100+1920, ++ .vso_end = 2164+1920, ++ .vso_bline = 51, ++ .vso_eline = 53, ++ .vso_eline_present = true, ++ /* sy_val */ ++ /* sy2_val */ ++ .max_lncnt = 2249, ++ }, ++}; ++ + struct meson_hdmi_venc_vic_mode { + unsigned int vic; + union meson_hdmi_venc_mode *mode; +@@ -718,6 +844,9 @@ struct meson_hdmi_venc_vic_mode { + { 34, &meson_hdmi_encp_mode_1080p30 }, + { 31, &meson_hdmi_encp_mode_1080p50 }, + { 16, &meson_hdmi_encp_mode_1080p60 }, ++ { 93, &meson_hdmi_encp_mode_2160p24 }, ++ { 94, &meson_hdmi_encp_mode_2160p25 }, ++ { 95, &meson_hdmi_encp_mode_2160p30 }, + { 0, NULL}, /* sentinel */ + }; + diff --git a/testing/linux-amlogic/0013-ARM64-dts-meson-gx-add-audio-controller-nodes.patch b/testing/linux-amlogic/0009-ARM64-dts-meson-gx-add-audio-controller-nodes.patch index f58d3ef3ca..f16edd0d1b 100644 --- a/testing/linux-amlogic/0013-ARM64-dts-meson-gx-add-audio-controller-nodes.patch +++ b/testing/linux-amlogic/0009-ARM64-dts-meson-gx-add-audio-controller-nodes.patch @@ -1,4 +1,4 @@ -From f4d7ad156ad2253d5ec3e79ea36309e27b8fabc7 Mon Sep 17 00:00:00 2001 +From 7ecd280bd317cff3c608b32b2a185929b2ec17ca 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 @@ -11,6 +11,7 @@ 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 +++++++++++++++++++++++++++++ diff --git a/testing/linux-amlogic/0009-drm-meson-Use-drm_fbdev_generic_setup.patch b/testing/linux-amlogic/0009-drm-meson-Use-drm_fbdev_generic_setup.patch new file mode 100644 index 0000000000..fc3511a111 --- /dev/null +++ b/testing/linux-amlogic/0009-drm-meson-Use-drm_fbdev_generic_setup.patch @@ -0,0 +1,100 @@ +From a753ec94679503680fcf86ffad3f3d3eb817c6b9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= <noralf@tronnes.org> +Date: Sat, 8 Sep 2018 15:46:33 +0200 +Subject: [PATCH] drm/meson: Use drm_fbdev_generic_setup() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The CMA helper is already using the drm_fb_helper_generic_probe part of +the generic fbdev emulation. This patch makes full use of the generic +fbdev emulation by using its drm_client callbacks. This means that +drm_mode_config_funcs->output_poll_changed and drm_driver->lastclose are +now handled by the emulation code. Additionally fbdev unregister happens +automatically on drm_dev_unregister(). + +The drm_fbdev_generic_setup() call is put after drm_dev_register() in the +driver. This is done to highlight the fact that fbdev emulation is an +internal client that makes use of the driver, it is not part of the +driver as such. If fbdev setup fails, an error is printed, but the driver +succeeds probing. + +Cc: Neil Armstrong <narmstrong@baylibre.com> +Signed-off-by: Noralf Trønnes <noralf@tronnes.org> + +--- + drivers/gpu/drm/meson/meson_drv.c | 19 ++----------------- + drivers/gpu/drm/meson/meson_drv.h | 1 - + 2 files changed, 2 insertions(+), 18 deletions(-) + +diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c +index b55e03d..3997e3e 100644 +--- a/drivers/gpu/drm/meson/meson_drv.c ++++ b/drivers/gpu/drm/meson/meson_drv.c +@@ -69,15 +69,7 @@ + * - Powering Up HDMI controller and PHY + */ + +-static void meson_fb_output_poll_changed(struct drm_device *dev) +-{ +- struct meson_drm *priv = dev->dev_private; +- +- drm_fbdev_cma_hotplug_event(priv->fbdev); +-} +- + static const struct drm_mode_config_funcs meson_mode_config_funcs = { +- .output_poll_changed = meson_fb_output_poll_changed, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, + .fb_create = drm_gem_fb_create, +@@ -314,13 +306,6 @@ static int meson_drv_bind_master(struct device *dev, bool has_components) + + drm_mode_config_reset(drm); + +- priv->fbdev = drm_fbdev_cma_init(drm, 32, +- drm->mode_config.num_connector); +- if (IS_ERR(priv->fbdev)) { +- ret = PTR_ERR(priv->fbdev); +- goto free_drm; +- } +- + drm_kms_helper_poll_init(drm); + + platform_set_drvdata(pdev, priv); +@@ -329,6 +314,8 @@ static int meson_drv_bind_master(struct device *dev, bool has_components) + if (ret) + goto free_drm; + ++ drm_fbdev_generic_setup(drm, 32); ++ + return 0; + + free_drm: +@@ -345,7 +332,6 @@ static int meson_drv_bind(struct device *dev) + static void meson_drv_unbind(struct device *dev) + { + struct drm_device *drm = dev_get_drvdata(dev); +- struct meson_drm *priv = drm->dev_private; + + if (priv->canvas) { + meson_canvas_free(priv->canvas, priv->canvas_id_osd1); +@@ -356,7 +342,6 @@ static void meson_drv_unbind(struct device *dev) + + drm_dev_unregister(drm); + drm_kms_helper_poll_fini(drm); +- drm_fbdev_cma_fini(priv->fbdev); + drm_mode_config_cleanup(drm); + drm_dev_put(drm); + +diff --git a/drivers/gpu/drm/meson/meson_drv.h b/drivers/gpu/drm/meson/meson_drv.h +index a955354..4dccf4c 100644 +--- a/drivers/gpu/drm/meson/meson_drv.h ++++ b/drivers/gpu/drm/meson/meson_drv.h +@@ -40,7 +40,6 @@ struct meson_drm { + + struct drm_device *drm; + struct drm_crtc *crtc; +- struct drm_fbdev_cma *fbdev; + struct drm_plane *primary_plane; + struct drm_plane *overlay_plane; + diff --git a/testing/linux-amlogic/0010-fixup-drm-meson-Use-optional-canvas-provider.patch b/testing/linux-amlogic/0010-fixup-drm-meson-Use-optional-canvas-provider.patch new file mode 100644 index 0000000000..9ea9352c9b --- /dev/null +++ b/testing/linux-amlogic/0010-fixup-drm-meson-Use-optional-canvas-provider.patch @@ -0,0 +1,21 @@ +From 07145ad0791209d3782eca4800406e7028e40a24 Mon Sep 17 00:00:00 2001 +From: Neil Armstrong <narmstrong@baylibre.com> +Date: Wed, 7 Nov 2018 11:24:43 +0100 +Subject: [PATCH] fixup! drm/meson: Use optional canvas provider + +--- + drivers/gpu/drm/meson/meson_drv.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c +index 3997e3e..3ee4d4a4e 100644 +--- a/drivers/gpu/drm/meson/meson_drv.c ++++ b/drivers/gpu/drm/meson/meson_drv.c +@@ -332,6 +332,7 @@ static int meson_drv_bind(struct device *dev) + static void meson_drv_unbind(struct device *dev) + { + struct drm_device *drm = dev_get_drvdata(dev); ++ struct meson_drm *priv = drm->dev_private; + + if (priv->canvas) { + meson_canvas_free(priv->canvas, priv->canvas_id_osd1); diff --git a/testing/linux-amlogic/0014-snd-meson-activate-HDMI-audio-path.patch b/testing/linux-amlogic/0010-snd-meson-activate-HDMI-audio-path.patch index 26dedf3d20..8a3ea861f2 100644 --- a/testing/linux-amlogic/0014-snd-meson-activate-HDMI-audio-path.patch +++ b/testing/linux-amlogic/0010-snd-meson-activate-HDMI-audio-path.patch @@ -1,9 +1,10 @@ -From 69d2f200d91fbd48e2388a8c5346f10d889a2928 Mon Sep 17 00:00:00 2001 +From c89e4a9d376b72bb00c1c71795b86fe81914a3ea 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/testing/linux-amlogic/0015-drm-meson-select-dw-hdmi-i2s-audio-for-meson-hdmi.patch b/testing/linux-amlogic/0011-drm-meson-select-dw-hdmi-i2s-audio-for-meson-hdmi.patch index cb88ebaf11..25c5a7f968 100644 --- a/testing/linux-amlogic/0015-drm-meson-select-dw-hdmi-i2s-audio-for-meson-hdmi.patch +++ b/testing/linux-amlogic/0011-drm-meson-select-dw-hdmi-i2s-audio-for-meson-hdmi.patch @@ -1,9 +1,10 @@ -From 223d7ef1a49981c597094e8519e150108cba9ef9 Mon Sep 17 00:00:00 2001 +From d46542e83265b476b32b94729e609a9a7767deac 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/testing/linux-amlogic/0016-ARM64-dts-meson-gx-add-sound-dai-cells-to-HDMI-node.patch b/testing/linux-amlogic/0012-ARM64-dts-meson-gx-add-sound-dai-cells-to-HDMI-node.patch index a5e4fab7a5..0b84218aa9 100644 --- a/testing/linux-amlogic/0016-ARM64-dts-meson-gx-add-sound-dai-cells-to-HDMI-node.patch +++ b/testing/linux-amlogic/0012-ARM64-dts-meson-gx-add-sound-dai-cells-to-HDMI-node.patch @@ -1,9 +1,10 @@ -From 00ce6fbb804c6aaecd3bde8f2978d091fbc0546c Mon Sep 17 00:00:00 2001 +From 0eb1a7bb3fd9e1ff2f368bb20490c7a032fc96e6 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 + diff --git a/testing/linux-amlogic/0012-drm-meson-add-support-for-1080p25-mode.patch b/testing/linux-amlogic/0012-drm-meson-add-support-for-1080p25-mode.patch new file mode 100644 index 0000000000..bd267a5c03 --- /dev/null +++ b/testing/linux-amlogic/0012-drm-meson-add-support-for-1080p25-mode.patch @@ -0,0 +1,21 @@ +From 1f929f36f48f0f957f6c73cec309235243bd42f9 Mon Sep 17 00:00:00 2001 +From: Neil Armstrong <narmstrong@baylibre.com> +Date: Mon, 12 Nov 2018 16:10:31 +0100 +Subject: [PATCH] drm/meson: add support for 1080p25 mode + +--- + drivers/gpu/drm/meson/meson_venc.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/gpu/drm/meson/meson_venc.c b/drivers/gpu/drm/meson/meson_venc.c +index d68ccbf..0fbe525 100644 +--- a/drivers/gpu/drm/meson/meson_venc.c ++++ b/drivers/gpu/drm/meson/meson_venc.c +@@ -841,6 +841,7 @@ struct meson_hdmi_venc_vic_mode { + { 5, &meson_hdmi_encp_mode_1080i60 }, + { 20, &meson_hdmi_encp_mode_1080i50 }, + { 32, &meson_hdmi_encp_mode_1080p24 }, ++ { 33, &meson_hdmi_encp_mode_1080p50 }, + { 34, &meson_hdmi_encp_mode_1080p30 }, + { 31, &meson_hdmi_encp_mode_1080p50 }, + { 16, &meson_hdmi_encp_mode_1080p60 }, diff --git a/testing/linux-amlogic/0017-ARM64-dts-meson-activate-hdmi-audio-HDMI-enabled-boa.patch b/testing/linux-amlogic/0013-ARM64-dts-meson-activate-hdmi-audio-HDMI-enabled-boa.patch index e02abbfe21..3ad65fc473 100644 --- a/testing/linux-amlogic/0017-ARM64-dts-meson-activate-hdmi-audio-HDMI-enabled-boa.patch +++ b/testing/linux-amlogic/0013-ARM64-dts-meson-activate-hdmi-audio-HDMI-enabled-boa.patch @@ -1,4 +1,4 @@ -From 2df1a3a93bc1ce2d04fa0f0743c9c30195c7057a Mon Sep 17 00:00:00 2001 +From f0417a0c309fb02a5896abb868a52a1a3e23d610 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 @@ -11,6 +11,7 @@ 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 ++++++++++++++++++++++ @@ -27,10 +28,10 @@ Signed-off-by: Neil Armstrong <narmstrong@baylibre.com> 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 +index 765247b..fb9ad6f 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 @@ +@@ -102,6 +102,39 @@ }; }; }; @@ -70,7 +71,7 @@ index 88e712e..319512e 100644 }; &cec_AO { -@@ -104,6 +137,14 @@ +@@ -111,6 +144,14 @@ hdmi-phandle = <&hdmi_tx>; }; @@ -85,7 +86,7 @@ index 88e712e..319512e 100644 &cvbs_vdac_port { cvbs_vdac_out: endpoint { remote-endpoint = <&cvbs_connector_in>; -@@ -126,6 +167,10 @@ +@@ -133,6 +174,10 @@ }; }; @@ -719,7 +720,7 @@ index 5896e8a..f8c66a7 100644 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 +index 313f88f..4fbfa5a 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 @@ @@ -777,7 +778,7 @@ index 0868da4..ea71261 100644 &cpu0 { #cooling-cells = <2>; }; -@@ -255,6 +296,10 @@ +@@ -279,6 +320,10 @@ }; }; diff --git a/testing/linux-amlogic/0014-drm-bridge-dw-hdmi-Add-SCDC-and-TMDS-Scrambling-supp.patch b/testing/linux-amlogic/0014-drm-bridge-dw-hdmi-Add-SCDC-and-TMDS-Scrambling-supp.patch new file mode 100644 index 0000000000..832af82c2a --- /dev/null +++ b/testing/linux-amlogic/0014-drm-bridge-dw-hdmi-Add-SCDC-and-TMDS-Scrambling-supp.patch @@ -0,0 +1,147 @@ +From 2ffa6ba6e67706f195b1938c5f7e8a385252bd8e Mon Sep 17 00:00:00 2001 +From: Neil Armstrong <narmstrong@baylibre.com> +Date: Wed, 14 Nov 2018 16:48:50 +0100 +Subject: [PATCH] drm/bridge: dw-hdmi: Add SCDC and TMDS Scrambling support + +Add support for SCDC Setup for TMDS Clock > 3.4GHz and enable TMDS +Scrambling when supported or mandatory. + +This patch also adds an helper to setup the control bit to support +the hight TMDS Bit Period/TMDS Clock-Period Ratio as required with +TMDS Clock > 3.4GHz for HDMI2.0 3840x2160@60/50 modes. + +These changes were based on work done by Huicong Xu <xhc@rock-chips.com> +and Nickey Yang <nickey.yang@rock-chips.com> to support HDMI2.0 modes +on the Rockchip 4.4 BSP kernel at [1] + +[1] https://github.com/rockchip-linux/kernel/tree/release-4.4 + +Cc: Nickey Yang <nickey.yang@rock-chips.com> +Cc: Huicong Xu <xhc@rock-chips.com> +Signed-off-by: Neil Armstrong <narmstrong@baylibre.com> + +--- + drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 45 ++++++++++++++++++++++++++++--- + drivers/gpu/drm/bridge/synopsys/dw-hdmi.h | 1 + + include/drm/bridge/dw_hdmi.h | 1 + + 3 files changed, 44 insertions(+), 3 deletions(-) + +diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +index 1fc1270..2a30d83 100644 +--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c ++++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +@@ -28,6 +28,7 @@ + #include <drm/drm_crtc_helper.h> + #include <drm/drm_edid.h> + #include <drm/drm_encoder_slave.h> ++#include <drm/drm_scdc_helper.h> + #include <drm/bridge/dw_hdmi.h> + + #include <uapi/linux/media-bus-format.h> +@@ -1026,6 +1027,20 @@ void dw_hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data, + } + EXPORT_SYMBOL_GPL(dw_hdmi_phy_i2c_write); + ++void dw_hdmi_set_high_tmds_clock_ratio(struct dw_hdmi *hdmi) ++{ ++ unsigned long mtmdsclock = hdmi->hdmi_data.video_mode.mpixelclock; ++ ++ /* Control for TMDS Bit Period/TMDS Clock-Period Ratio */ ++ if (hdmi->connector.display_info.hdmi.scdc.supported) { ++ if (mtmdsclock > 340000000) ++ drm_scdc_set_high_tmds_clock_ratio(hdmi->ddc, 1); ++ else ++ drm_scdc_set_high_tmds_clock_ratio(hdmi->ddc, 0); ++ } ++} ++EXPORT_SYMBOL_GPL(dw_hdmi_set_high_tmds_clock_ratio); ++ + static void dw_hdmi_phy_enable_powerdown(struct dw_hdmi *hdmi, bool enable) + { + hdmi_mask_writeb(hdmi, !enable, HDMI_PHY_CONF0, +@@ -1351,11 +1366,12 @@ static void hdmi_tx_hdcp_config(struct dw_hdmi *hdmi) + + static void hdmi_config_AVI(struct dw_hdmi *hdmi, struct drm_display_mode *mode) + { ++ bool is_hdmi2_sink = hdmi->connector.display_info.hdmi.scdc.supported; + struct hdmi_avi_infoframe frame; + u8 val; + + /* Initialise info frame from DRM mode */ +- drm_hdmi_avi_infoframe_from_display_mode(&frame, mode, false); ++ drm_hdmi_avi_infoframe_from_display_mode(&frame, mode, is_hdmi2_sink); + + if (hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format)) + frame.colorspace = HDMI_COLORSPACE_YUV444; +@@ -1514,7 +1530,8 @@ static void hdmi_config_vendor_specific_infoframe(struct dw_hdmi *hdmi, + static void hdmi_av_composer(struct dw_hdmi *hdmi, + const struct drm_display_mode *mode) + { +- u8 inv_val; ++ u8 inv_val, bytes; ++ struct drm_hdmi_info *hdmi_info = &hdmi->connector.display_info.hdmi; + struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode; + int hblank, vblank, h_de_hs, v_de_vs, hsync_len, vsync_len; + unsigned int vdisplay; +@@ -1524,7 +1541,9 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi, + dev_dbg(hdmi->dev, "final pixclk = %d\n", vmode->mpixelclock); + + /* Set up HDMI_FC_INVIDCONF */ +- inv_val = (hdmi->hdmi_data.hdcp_enable ? ++ inv_val = (hdmi->hdmi_data.hdcp_enable || ++ vmode->mpixelclock > 340000000 || ++ hdmi_info->scdc.scrambling.low_rates ? + HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE : + HDMI_FC_INVIDCONF_HDCP_KEEPOUT_INACTIVE); + +@@ -1573,6 +1592,26 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi, + vsync_len /= 2; + } + ++ /* Scrambling Control */ ++ if (hdmi_info->scdc.supported) { ++ if (vmode->mpixelclock > 340000000 || ++ hdmi_info->scdc.scrambling.low_rates) { ++ drm_scdc_readb(&hdmi->i2c->adap, SCDC_SINK_VERSION, ++ &bytes); ++ drm_scdc_writeb(&hdmi->i2c->adap, SCDC_SOURCE_VERSION, ++ bytes); ++ drm_scdc_set_scrambling(&hdmi->i2c->adap, 1); ++ hdmi_writeb(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ, ++ HDMI_MC_SWRSTZ); ++ hdmi_writeb(hdmi, 1, HDMI_FC_SCRAMBLER_CTRL); ++ } else { ++ hdmi_writeb(hdmi, 0, HDMI_FC_SCRAMBLER_CTRL); ++ hdmi_writeb(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ, ++ HDMI_MC_SWRSTZ); ++ drm_scdc_set_scrambling(&hdmi->i2c->adap, 0); ++ } ++ } ++ + /* Set up horizontal active pixel width */ + hdmi_writeb(hdmi, mode->hdisplay >> 8, HDMI_FC_INHACTV1); + hdmi_writeb(hdmi, mode->hdisplay, HDMI_FC_INHACTV0); +diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h +index 9d90eb9..3f3c616 100644 +--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h ++++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h +@@ -255,6 +255,7 @@ + #define HDMI_FC_MASK2 0x10DA + #define HDMI_FC_POL2 0x10DB + #define HDMI_FC_PRCONF 0x10E0 ++#define HDMI_FC_SCRAMBLER_CTRL 0x10E1 + + #define HDMI_FC_GMD_STAT 0x1100 + #define HDMI_FC_GMD_EN 0x1101 +diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h +index ccb5aa8..d7cc5d0 100644 +--- a/include/drm/bridge/dw_hdmi.h ++++ b/include/drm/bridge/dw_hdmi.h +@@ -156,6 +156,7 @@ void dw_hdmi_setup_rx_sense(struct dw_hdmi *hdmi, bool hpd, bool rx_sense); + void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate); + void dw_hdmi_audio_enable(struct dw_hdmi *hdmi); + void dw_hdmi_audio_disable(struct dw_hdmi *hdmi); ++void dw_hdmi_set_high_tmds_clock_ratio(struct dw_hdmi *hdmi); + + /* PHY configuration */ + void dw_hdmi_phy_i2c_set_addr(struct dw_hdmi *hdmi, u8 address); diff --git a/testing/linux-amlogic/0018-drm-bridge-dw-hdmi-Use-AUTO-CTS-setup-mode-when-non-.patch b/testing/linux-amlogic/0014-drm-bridge-dw-hdmi-Use-AUTO-CTS-setup-mode-when-non-.patch index fbff6c8d89..702f0c7141 100644 --- a/testing/linux-amlogic/0018-drm-bridge-dw-hdmi-Use-AUTO-CTS-setup-mode-when-non-.patch +++ b/testing/linux-amlogic/0014-drm-bridge-dw-hdmi-Use-AUTO-CTS-setup-mode-when-non-.patch @@ -1,16 +1,17 @@ -From e282ad866be628951a95d297207c9a5580f4101d Mon Sep 17 00:00:00 2001 +From 68854b3c7771ad5754ad46d42f45f626ca87b4ac 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 +index 5971976..1fc1270 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, diff --git a/testing/linux-amlogic/0015-drm-meson-Call-drm_crtc_vblank_on-drm_crtc_vblank_of.patch b/testing/linux-amlogic/0015-drm-meson-Call-drm_crtc_vblank_on-drm_crtc_vblank_of.patch new file mode 100644 index 0000000000..7dc23b5b47 --- /dev/null +++ b/testing/linux-amlogic/0015-drm-meson-Call-drm_crtc_vblank_on-drm_crtc_vblank_of.patch @@ -0,0 +1,61 @@ +From 40bfd476f6bb44f9bf88bbbc0bd4cfd9591bedc9 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 | 4 ++++ + drivers/gpu/drm/meson/meson_venc.c | 3 +++ + 2 files changed, 7 insertions(+) + +diff --git a/drivers/gpu/drm/meson/meson_crtc.c b/drivers/gpu/drm/meson/meson_crtc.c +index 0552020..7c0bdc8 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,8 @@ 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_crtc_vblank_off(crtc); ++ + priv->viu.osd1_enabled = false; + priv->viu.osd1_commit = false; + +diff --git a/drivers/gpu/drm/meson/meson_venc.c b/drivers/gpu/drm/meson/meson_venc.c +index 514245e..14aac66 100644 +--- a/drivers/gpu/drm/meson/meson_venc.c ++++ b/drivers/gpu/drm/meson/meson_venc.c +@@ -71,6 +71,7 @@ + */ + + /* HHI Registers */ ++#define HHI_GCLK_MPEG2 0x148 /* 0x52 offset in data sheet */ + #define HHI_VDAC_CNTL0 0x2F4 /* 0xbd offset in data sheet */ + #define HHI_VDAC_CNTL1 0x2F8 /* 0xbe offset in data sheet */ + #define HHI_HDMI_PHY_CNTL0 0x3a0 /* 0xe8 offset in data sheet */ +@@ -1529,10 +1530,12 @@ unsigned int meson_venci_get_field(struct meson_drm *priv) + void meson_venc_enable_vsync(struct meson_drm *priv) + { + writel_relaxed(2, priv->io_base + _REG(VENC_INTCTRL)); ++ regmap_update_bits(priv->hhi, HHI_GCLK_MPEG2, BIT(25), BIT(25)); + } + + void meson_venc_disable_vsync(struct meson_drm *priv) + { ++ regmap_update_bits(priv->hhi, HHI_GCLK_MPEG2, BIT(25), 0); + writel_relaxed(0, priv->io_base + _REG(VENC_INTCTRL)); + } + diff --git a/testing/linux-amlogic/0015-drm-meson-add-HDMI-div40-TMDS-mode.patch b/testing/linux-amlogic/0015-drm-meson-add-HDMI-div40-TMDS-mode.patch new file mode 100644 index 0000000000..3af3749c72 --- /dev/null +++ b/testing/linux-amlogic/0015-drm-meson-add-HDMI-div40-TMDS-mode.patch @@ -0,0 +1,69 @@ +From cd02f4b3e7ad491111dbd6e1eccf3db9bbc1bc81 Mon Sep 17 00:00:00 2001 +From: Neil Armstrong <narmstrong@baylibre.com> +Date: Mon, 12 Nov 2018 16:08:13 +0100 +Subject: [PATCH] drm/meson: add HDMI div40 TMDS mode + +Add support for TMDS Clock > 3.4GHz for HDMI2.0 display modes. + +Signed-off-by: Neil Armstrong <narmstrong@baylibre.com> + +--- + drivers/gpu/drm/meson/meson_dw_hdmi.c | 24 ++++++++++++++++++++---- + 1 file changed, 20 insertions(+), 4 deletions(-) + +diff --git a/drivers/gpu/drm/meson/meson_dw_hdmi.c b/drivers/gpu/drm/meson/meson_dw_hdmi.c +index d8c5cc3..118c49e 100644 +--- a/drivers/gpu/drm/meson/meson_dw_hdmi.c ++++ b/drivers/gpu/drm/meson/meson_dw_hdmi.c +@@ -365,7 +365,8 @@ static int dw_hdmi_phy_init(struct dw_hdmi *hdmi, void *data, + unsigned int wr_clk = + readl_relaxed(priv->io_base + _REG(VPU_HDMI_SETTING)); + +- DRM_DEBUG_DRIVER("%d:\"%s\"\n", mode->base.id, mode->name); ++ DRM_DEBUG_DRIVER("%d:\"%s\" div%d\n", mode->base.id, mode->name, ++ mode->clock > 340000 ? 40 : 10); + + /* Enable clocks */ + regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL, 0xffff, 0x100); +@@ -385,9 +386,17 @@ static int dw_hdmi_phy_init(struct dw_hdmi *hdmi, void *data, + /* Enable normal output to PHY */ + dw_hdmi_top_write(dw_hdmi, HDMITX_TOP_BIST_CNTL, BIT(12)); + +- /* TMDS pattern setup (TOFIX pattern for 4k2k scrambling) */ +- dw_hdmi_top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_01, 0x001f001f); +- dw_hdmi_top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_23, 0x001f001f); ++ /* TMDS pattern setup (TOFIX Handle the YUV420 case) */ ++ if (mode->clock > 340000) { ++ dw_hdmi_top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_01, 0); ++ dw_hdmi_top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_23, ++ 0x03ff03ff); ++ } else { ++ dw_hdmi_top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_01, ++ 0x001f001f); ++ dw_hdmi_top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_23, ++ 0x001f001f); ++ } + + /* Load TMDS pattern */ + dw_hdmi_top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_CNTL, 0x1); +@@ -413,6 +422,8 @@ static int dw_hdmi_phy_init(struct dw_hdmi *hdmi, void *data, + /* Disable clock, fifo, fifo_wr */ + regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1, 0xf, 0); + ++ dw_hdmi_set_high_tmds_clock_ratio(hdmi); ++ + msleep(100); + + /* Reset PHY 3 times in a row */ +@@ -562,6 +573,11 @@ dw_hdmi_mode_valid(struct drm_connector *connector, + mode->vdisplay, mode->vsync_start, + mode->vsync_end, mode->vtotal, mode->type, mode->flags); + ++ /* If sink max TMDS clock < 340MHz, we reject the HDMI2.0 modes */ ++ if (mode->clock > 340000 && ++ connector->display_info.max_tmds_clock < 340000) ++ return MODE_BAD; ++ + /* Check against non-VIC supported modes */ + if (!vic) { + status = meson_venc_hdmi_supported_mode(mode); diff --git a/testing/linux-amlogic/0016-drm-meson-add-support-for-HDMI2.0-2160p-modes.patch b/testing/linux-amlogic/0016-drm-meson-add-support-for-HDMI2.0-2160p-modes.patch new file mode 100644 index 0000000000..4a860acdbc --- /dev/null +++ b/testing/linux-amlogic/0016-drm-meson-add-support-for-HDMI2.0-2160p-modes.patch @@ -0,0 +1,28 @@ +From b48d4a78b2c3dd2db65ac391be3e12e323b6759e Mon Sep 17 00:00:00 2001 +From: Neil Armstrong <narmstrong@baylibre.com> +Date: Mon, 12 Nov 2018 16:10:07 +0100 +Subject: [PATCH] drm/meson: add support for HDMI2.0 2160p modes + +Now we support the TMDS Clock > 3.4GHz and support the SCDC Control +operation in the DW-HDMI Controller, we can enable support for the +HDMI2.0 3840x2160@60/50 RGB444 display modes. + +Signed-off-by: Neil Armstrong <narmstrong@baylibre.com> + +--- + drivers/gpu/drm/meson/meson_venc.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/gpu/drm/meson/meson_venc.c b/drivers/gpu/drm/meson/meson_venc.c +index 0fbe525..1bcd642 100644 +--- a/drivers/gpu/drm/meson/meson_venc.c ++++ b/drivers/gpu/drm/meson/meson_venc.c +@@ -848,6 +848,8 @@ struct meson_hdmi_venc_vic_mode { + { 93, &meson_hdmi_encp_mode_2160p24 }, + { 94, &meson_hdmi_encp_mode_2160p25 }, + { 95, &meson_hdmi_encp_mode_2160p30 }, ++ { 96, &meson_hdmi_encp_mode_2160p25 }, ++ { 97, &meson_hdmi_encp_mode_2160p30 }, + { 0, NULL}, /* sentinel */ + }; + diff --git a/testing/linux-amlogic/0017-drm-bridge-dw-hdmi-add-support-for-YUV420-output.patch b/testing/linux-amlogic/0017-drm-bridge-dw-hdmi-add-support-for-YUV420-output.patch new file mode 100644 index 0000000000..9c36ea2b48 --- /dev/null +++ b/testing/linux-amlogic/0017-drm-bridge-dw-hdmi-add-support-for-YUV420-output.patch @@ -0,0 +1,198 @@ +From 3e7f3ec3de8753faefdeb02ed6d00cc580e6ad52 Mon Sep 17 00:00:00 2001 +From: Neil Armstrong <narmstrong@baylibre.com> +Date: Wed, 14 Nov 2018 17:19:36 +0100 +Subject: [PATCH] drm/bridge: dw-hdmi: add support for YUV420 output + +In order to support the HDMI2.0 YUV420 display modes, this patch +adds support for the YUV420 TMDS Clock divided by 2 and the controller +passthrough mode. + +This patch is based on work from Zheng Yang <zhengyang@rock-chips.com> in +the Rockchip Linux 4.4 BSP at [1] + +[1] https://github.com/rockchip-linux/kernel/tree/release-4.4 + +Cc: Zheng Yang <zhengyang@rock-chips.com> +Signed-off-by: Neil Armstrong <narmstrong@baylibre.com> + +--- + drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 63 ++++++++++++++++++++++++------- + 1 file changed, 50 insertions(+), 13 deletions(-) + +diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +index 2a30d83..c3e4ed1 100644 +--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c ++++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +@@ -94,6 +94,7 @@ struct hdmi_vmode { + unsigned int mpixelclock; + unsigned int mpixelrepetitioninput; + unsigned int mpixelrepetitionoutput; ++ unsigned int mtmdsclock; + }; + + struct hdmi_data_info { +@@ -549,7 +550,7 @@ static void hdmi_init_clk_regenerator(struct dw_hdmi *hdmi) + static void hdmi_clk_regenerator_update_pixel_clock(struct dw_hdmi *hdmi) + { + mutex_lock(&hdmi->audio_mutex); +- hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mpixelclock, ++ hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mtmdsclock, + hdmi->sample_rate); + mutex_unlock(&hdmi->audio_mutex); + } +@@ -558,7 +559,7 @@ void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate) + { + mutex_lock(&hdmi->audio_mutex); + hdmi->sample_rate = rate; +- hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mpixelclock, ++ hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mtmdsclock, + hdmi->sample_rate); + mutex_unlock(&hdmi->audio_mutex); + } +@@ -659,6 +660,20 @@ static bool hdmi_bus_fmt_is_yuv422(unsigned int bus_format) + } + } + ++static bool hdmi_bus_fmt_is_yuv420(unsigned int bus_format) ++{ ++ switch (bus_format) { ++ case MEDIA_BUS_FMT_UYYVYY8_0_5X24: ++ case MEDIA_BUS_FMT_UYYVYY10_0_5X30: ++ case MEDIA_BUS_FMT_UYYVYY12_0_5X36: ++ case MEDIA_BUS_FMT_UYYVYY16_0_5X48: ++ return true; ++ ++ default: ++ return false; ++ } ++} ++ + static int hdmi_bus_fmt_color_depth(unsigned int bus_format) + { + switch (bus_format) { +@@ -888,7 +903,8 @@ static void hdmi_video_packetize(struct dw_hdmi *hdmi) + u8 val, vp_conf; + + if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format) || +- hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format)) { ++ hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format) || ++ hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) { + switch (hdmi_bus_fmt_color_depth( + hdmi->hdmi_data.enc_out_bus_format)) { + case 8: +@@ -1029,7 +1045,7 @@ EXPORT_SYMBOL_GPL(dw_hdmi_phy_i2c_write); + + void dw_hdmi_set_high_tmds_clock_ratio(struct dw_hdmi *hdmi) + { +- unsigned long mtmdsclock = hdmi->hdmi_data.video_mode.mpixelclock; ++ unsigned long mtmdsclock = hdmi->hdmi_data.video_mode.mtmdsclock; + + /* Control for TMDS Bit Period/TMDS Clock-Period Ratio */ + if (hdmi->connector.display_info.hdmi.scdc.supported) { +@@ -1370,6 +1386,9 @@ static void hdmi_config_AVI(struct dw_hdmi *hdmi, struct drm_display_mode *mode) + struct hdmi_avi_infoframe frame; + u8 val; + ++ if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) ++ is_hdmi2_sink = true; ++ + /* Initialise info frame from DRM mode */ + drm_hdmi_avi_infoframe_from_display_mode(&frame, mode, is_hdmi2_sink); + +@@ -1377,6 +1396,8 @@ static void hdmi_config_AVI(struct dw_hdmi *hdmi, struct drm_display_mode *mode) + frame.colorspace = HDMI_COLORSPACE_YUV444; + else if (hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) + frame.colorspace = HDMI_COLORSPACE_YUV422; ++ else if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) ++ frame.colorspace = HDMI_COLORSPACE_YUV420; + else + frame.colorspace = HDMI_COLORSPACE_RGB; + +@@ -1534,15 +1555,18 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi, + struct drm_hdmi_info *hdmi_info = &hdmi->connector.display_info.hdmi; + struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode; + int hblank, vblank, h_de_hs, v_de_vs, hsync_len, vsync_len; +- unsigned int vdisplay; ++ unsigned int vdisplay, hdisplay; + +- vmode->mpixelclock = mode->clock * 1000; ++ vmode->mtmdsclock = vmode->mpixelclock = mode->clock * 1000; + + dev_dbg(hdmi->dev, "final pixclk = %d\n", vmode->mpixelclock); + ++ if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) ++ vmode->mtmdsclock /= 2; ++ + /* Set up HDMI_FC_INVIDCONF */ + inv_val = (hdmi->hdmi_data.hdcp_enable || +- vmode->mpixelclock > 340000000 || ++ vmode->mtmdsclock > 340000000 || + hdmi_info->scdc.scrambling.low_rates ? + HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE : + HDMI_FC_INVIDCONF_HDCP_KEEPOUT_INACTIVE); +@@ -1576,6 +1600,22 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi, + + hdmi_writeb(hdmi, inv_val, HDMI_FC_INVIDCONF); + ++ hdisplay = mode->hdisplay; ++ hblank = mode->htotal - mode->hdisplay; ++ h_de_hs = mode->hsync_start - mode->hdisplay; ++ hsync_len = mode->hsync_end - mode->hsync_start; ++ ++ /* ++ * When we're setting a YCbCr420 mode, we need ++ * to adjust the horizontal timing to suit. ++ */ ++ if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) { ++ hdisplay /= 2; ++ hblank /= 2; ++ h_de_hs /= 2; ++ hsync_len /= 2; ++ } ++ + vdisplay = mode->vdisplay; + vblank = mode->vtotal - mode->vdisplay; + v_de_vs = mode->vsync_start - mode->vdisplay; +@@ -1594,7 +1634,7 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi, + + /* Scrambling Control */ + if (hdmi_info->scdc.supported) { +- if (vmode->mpixelclock > 340000000 || ++ if (vmode->mtmdsclock > 340000000 || + hdmi_info->scdc.scrambling.low_rates) { + drm_scdc_readb(&hdmi->i2c->adap, SCDC_SINK_VERSION, + &bytes); +@@ -1613,15 +1653,14 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi, + } + + /* Set up horizontal active pixel width */ +- hdmi_writeb(hdmi, mode->hdisplay >> 8, HDMI_FC_INHACTV1); +- hdmi_writeb(hdmi, mode->hdisplay, HDMI_FC_INHACTV0); ++ hdmi_writeb(hdmi, hdisplay >> 8, HDMI_FC_INHACTV1); ++ hdmi_writeb(hdmi, hdisplay, HDMI_FC_INHACTV0); + + /* Set up vertical active lines */ + hdmi_writeb(hdmi, vdisplay >> 8, HDMI_FC_INVACTV1); + hdmi_writeb(hdmi, vdisplay, HDMI_FC_INVACTV0); + + /* Set up horizontal blanking pixel region width */ +- hblank = mode->htotal - mode->hdisplay; + hdmi_writeb(hdmi, hblank >> 8, HDMI_FC_INHBLANK1); + hdmi_writeb(hdmi, hblank, HDMI_FC_INHBLANK0); + +@@ -1629,7 +1668,6 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi, + hdmi_writeb(hdmi, vblank, HDMI_FC_INVBLANK); + + /* Set up HSYNC active edge delay width (in pixel clks) */ +- h_de_hs = mode->hsync_start - mode->hdisplay; + hdmi_writeb(hdmi, h_de_hs >> 8, HDMI_FC_HSYNCINDELAY1); + hdmi_writeb(hdmi, h_de_hs, HDMI_FC_HSYNCINDELAY0); + +@@ -1637,7 +1675,6 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi, + hdmi_writeb(hdmi, v_de_vs, HDMI_FC_VSYNCINDELAY); + + /* Set up HSYNC active pulse width (in pixel clks) */ +- hsync_len = mode->hsync_end - mode->hsync_start; + hdmi_writeb(hdmi, hsync_len >> 8, HDMI_FC_HSYNCINWIDTH1); + hdmi_writeb(hdmi, hsync_len, HDMI_FC_HSYNCINWIDTH0); + diff --git a/testing/linux-amlogic/0017-soc-amlogic-add-meson-canvas-driver.patch b/testing/linux-amlogic/0017-soc-amlogic-add-meson-canvas-driver.patch new file mode 100644 index 0000000000..4bb5731f30 --- /dev/null +++ b/testing/linux-amlogic/0017-soc-amlogic-add-meson-canvas-driver.patch @@ -0,0 +1,314 @@ +From 47756c823298bef3895fa2837c4b3e97062e9842 Mon Sep 17 00:00:00 2001 +From: Maxime Jourdan <maxi.jourdan@wanadoo.fr> +Date: Fri, 20 Apr 2018 13:17:07 +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. + +Reviewed-by: Jerome Brunet <jbrunet@baylibre.com> +Tested-by: Neil Armstrong <narmstrong@baylibre.com> +Signed-off-by: Maxime Jourdan <mjourdan@baylibre.com> + +--- + drivers/soc/amlogic/Kconfig | 7 ++ + drivers/soc/amlogic/Makefile | 1 + + drivers/soc/amlogic/meson-canvas.c | 185 +++++++++++++++++++++++++++++++ + include/linux/soc/amlogic/meson-canvas.h | 65 +++++++++++ + 4 files changed, 258 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..2f282b4 100644 +--- a/drivers/soc/amlogic/Kconfig ++++ b/drivers/soc/amlogic/Kconfig +@@ -1,5 +1,12 @@ + menu "Amlogic SoC drivers" + ++config MESON_CANVAS ++ tristate "Amlogic Meson Canvas driver" ++ depends on ARCH_MESON || COMPILE_TEST ++ default n ++ help ++ Say yes to support the canvas IP for Amlogic SoCs. ++ + 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..fce33ca +--- /dev/null ++++ b/drivers/soc/amlogic/meson-canvas.c +@@ -0,0 +1,185 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Copyright (C) 2018 BayLibre, SAS ++ * Copyright (C) 2015 Amlogic, Inc. All rights reserved. ++ * Copyright (C) 2014 Endless Mobile ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/mfd/syscon.h> ++#include <linux/module.h> ++#include <linux/regmap.h> ++#include <linux/soc/amlogic/meson-canvas.h> ++#include <linux/of_address.h> ++#include <linux/of_platform.h> ++#include <linux/io.h> ++ ++#define NUM_CANVAS 256 ++ ++/* DMC Registers */ ++#define DMC_CAV_LUT_DATAL 0x00 ++ #define CANVAS_WIDTH_LBIT 29 ++ #define CANVAS_WIDTH_LWID 3 ++#define DMC_CAV_LUT_DATAH 0x04 ++ #define CANVAS_WIDTH_HBIT 0 ++ #define CANVAS_HEIGHT_BIT 9 ++ #define CANVAS_WRAP_BIT 22 ++ #define CANVAS_BLKMODE_BIT 24 ++ #define CANVAS_ENDIAN_BIT 26 ++#define DMC_CAV_LUT_ADDR 0x08 ++ #define CANVAS_LUT_WR_EN BIT(9) ++ #define CANVAS_LUT_RD_EN BIT(8) ++ ++struct meson_canvas { ++ struct device *dev; ++ void __iomem *reg_base; ++ spinlock_t lock; /* canvas device lock */ ++ u8 used[NUM_CANVAS]; ++}; ++ ++static void canvas_write(struct meson_canvas *canvas, u32 reg, u32 val) ++{ ++ writel_relaxed(val, canvas->reg_base + reg); ++} ++ ++static u32 canvas_read(struct meson_canvas *canvas, u32 reg) ++{ ++ return readl_relaxed(canvas->reg_base + reg); ++} ++ ++struct meson_canvas *meson_canvas_get(struct device *dev) ++{ ++ struct device_node *canvas_node; ++ struct platform_device *canvas_pdev; ++ ++ canvas_node = of_parse_phandle(dev->of_node, "amlogic,canvas", 0); ++ if (!canvas_node) ++ return ERR_PTR(-ENODEV); ++ ++ canvas_pdev = of_find_device_by_node(canvas_node); ++ if (!canvas_pdev) ++ return ERR_PTR(-EPROBE_DEFER); ++ ++ return dev_get_drvdata(&canvas_pdev->dev); ++} ++EXPORT_SYMBOL_GPL(meson_canvas_get); ++ ++int meson_canvas_config(struct meson_canvas *canvas, u8 canvas_index, ++ u32 addr, u32 stride, u32 height, ++ unsigned int wrap, ++ unsigned int blkmode, ++ unsigned int endian) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&canvas->lock, flags); ++ if (!canvas->used[canvas_index]) { ++ dev_err(canvas->dev, ++ "Trying to setup non allocated canvas %u\n", ++ canvas_index); ++ spin_unlock_irqrestore(&canvas->lock, flags); ++ return -EINVAL; ++ } ++ ++ canvas_write(canvas, DMC_CAV_LUT_DATAL, ++ ((addr + 7) >> 3) | ++ (((stride + 7) >> 3) << CANVAS_WIDTH_LBIT)); ++ ++ canvas_write(canvas, DMC_CAV_LUT_DATAH, ++ ((((stride + 7) >> 3) >> CANVAS_WIDTH_LWID) << ++ CANVAS_WIDTH_HBIT) | ++ (height << CANVAS_HEIGHT_BIT) | ++ (wrap << CANVAS_WRAP_BIT) | ++ (blkmode << CANVAS_BLKMODE_BIT) | ++ (endian << CANVAS_ENDIAN_BIT)); ++ ++ canvas_write(canvas, DMC_CAV_LUT_ADDR, ++ CANVAS_LUT_WR_EN | canvas_index); ++ ++ /* Force a read-back to make sure everything is flushed. */ ++ canvas_read(canvas, DMC_CAV_LUT_DATAH); ++ spin_unlock_irqrestore(&canvas->lock, flags); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(meson_canvas_config); ++ ++int meson_canvas_alloc(struct meson_canvas *canvas, u8 *canvas_index) ++{ ++ int i; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&canvas->lock, flags); ++ for (i = 0; i < NUM_CANVAS; ++i) { ++ if (!canvas->used[i]) { ++ canvas->used[i] = 1; ++ spin_unlock_irqrestore(&canvas->lock, flags); ++ *canvas_index = i; ++ return 0; ++ } ++ } ++ spin_unlock_irqrestore(&canvas->lock, flags); ++ ++ dev_err(canvas->dev, "No more canvas available\n"); ++ return -ENODEV; ++} ++EXPORT_SYMBOL_GPL(meson_canvas_alloc); ++ ++int meson_canvas_free(struct meson_canvas *canvas, u8 canvas_index) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&canvas->lock, flags); ++ if (!canvas->used[canvas_index]) { ++ dev_err(canvas->dev, ++ "Trying to free unused canvas %u\n", canvas_index); ++ spin_unlock_irqrestore(&canvas->lock, flags); ++ return -EINVAL; ++ } ++ canvas->used[canvas_index] = 0; ++ spin_unlock_irqrestore(&canvas->lock, flags); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(meson_canvas_free); ++ ++static int meson_canvas_probe(struct platform_device *pdev) ++{ ++ struct resource *res; ++ struct meson_canvas *canvas; ++ struct device *dev = &pdev->dev; ++ ++ canvas = devm_kzalloc(dev, sizeof(*canvas), GFP_KERNEL); ++ if (!canvas) ++ return -ENOMEM; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ canvas->reg_base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(canvas->reg_base)) ++ return PTR_ERR(canvas->reg_base); ++ ++ canvas->dev = dev; ++ spin_lock_init(&canvas->lock); ++ dev_set_drvdata(dev, canvas); ++ ++ return 0; ++} ++ ++static const struct of_device_id canvas_dt_match[] = { ++ { .compatible = "amlogic,canvas" }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, canvas_dt_match); ++ ++static struct platform_driver meson_canvas_driver = { ++ .probe = meson_canvas_probe, ++ .driver = { ++ .name = "amlogic-canvas", ++ .of_match_table = canvas_dt_match, ++ }, ++}; ++module_platform_driver(meson_canvas_driver); ++ ++MODULE_DESCRIPTION("Amlogic Canvas driver"); ++MODULE_AUTHOR("Maxime Jourdan <mjourdan@baylibre.com>"); ++MODULE_LICENSE("GPL"); +diff --git a/include/linux/soc/amlogic/meson-canvas.h b/include/linux/soc/amlogic/meson-canvas.h +new file mode 100644 +index 0000000..b4dde2f +--- /dev/null ++++ b/include/linux/soc/amlogic/meson-canvas.h +@@ -0,0 +1,65 @@ ++/* SPDX-License-Identifier: GPL-2.0+ */ ++/* ++ * Copyright (C) 2018 BayLibre, SAS ++ */ ++#ifndef __SOC_MESON_CANVAS_H ++#define __SOC_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 ++ ++#define MESON_CANVAS_ENDIAN_SWAP16 0x1 ++#define MESON_CANVAS_ENDIAN_SWAP32 0x3 ++#define MESON_CANVAS_ENDIAN_SWAP64 0x7 ++#define MESON_CANVAS_ENDIAN_SWAP128 0xf ++ ++struct meson_canvas; ++ ++/** ++ * meson_canvas_get() - get a canvas provider instance ++ * ++ * @dev: consumer device pointer ++ */ ++struct meson_canvas *meson_canvas_get(struct device *dev); ++ ++/** ++ * meson_canvas_alloc() - take ownership of a canvas ++ * ++ * @canvas: canvas provider instance retrieved from meson_canvas_get() ++ * @canvas_index: will be filled with the canvas ID ++ */ ++int meson_canvas_alloc(struct meson_canvas *canvas, u8 *canvas_index); ++ ++/** ++ * meson_canvas_free() - remove ownership from a canvas ++ * ++ * @canvas: canvas provider instance retrieved from meson_canvas_get() ++ * @canvas_index: canvas ID that was obtained via meson_canvas_alloc() ++ */ ++int meson_canvas_free(struct meson_canvas *canvas, u8 canvas_index); ++ ++/** ++ * meson_canvas_config() - configure a canvas ++ * ++ * @canvas: canvas provider instance retrieved from meson_canvas_get() ++ * @canvas_index: canvas ID that was obtained via meson_canvas_alloc() ++ * @addr: physical address to the pixel buffer ++ * @stride: width of the buffer ++ * @height: height of the buffer ++ * @wrap: undocumented ++ * @blkmode: block mode (linear, 32x32, 64x64) ++ * @endian: byte swapping (swap16, swap32, swap64, swap128) ++ */ ++int meson_canvas_config(struct meson_canvas *canvas, u8 canvas_index, ++ u32 addr, u32 stride, u32 height, ++ unsigned int wrap, unsigned int blkmode, ++ unsigned int endian); ++ ++#endif diff --git a/testing/linux-amlogic/0018-ARM64-dts-meson-gx-add-dmcbus-and-canvas-nodes.patch b/testing/linux-amlogic/0018-ARM64-dts-meson-gx-add-dmcbus-and-canvas-nodes.patch new file mode 100644 index 0000000000..b09c7c8488 --- /dev/null +++ b/testing/linux-amlogic/0018-ARM64-dts-meson-gx-add-dmcbus-and-canvas-nodes.patch @@ -0,0 +1,39 @@ +From b2b84d801a2e8145b2a08e0d310682fc5e88b0ad Mon Sep 17 00:00:00 2001 +From: Maxime Jourdan <maxi.jourdan@wanadoo.fr> +Date: Fri, 20 Apr 2018 16:09:09 +0200 +Subject: [PATCH] ARM64: dts: meson-gx: add dmcbus and canvas nodes. + +DMC is a small memory region with various registers, +including the ones needed for the canvas module. + +Reviewed-by: Jerome Brunet <jbrunet@baylibre.com> +Signed-off-by: Maxime Jourdan <mjourdan@baylibre.com> + +--- + arch/arm64/boot/dts/amlogic/meson-gx.dtsi | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +diff --git a/arch/arm64/boot/dts/amlogic/meson-gx.dtsi b/arch/arm64/boot/dts/amlogic/meson-gx.dtsi +index 6b64b63..fb64354 100644 +--- a/arch/arm64/boot/dts/amlogic/meson-gx.dtsi ++++ b/arch/arm64/boot/dts/amlogic/meson-gx.dtsi +@@ -458,6 +458,19 @@ + }; + }; + ++ dmcbus: bus@c8838000 { ++ compatible = "simple-bus"; ++ reg = <0x0 0xc8838000 0x0 0x400>; ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges = <0x0 0x0 0x0 0xc8838000 0x0 0x400>; ++ ++ canvas: video-lut@48 { ++ compatible = "amlogic,canvas"; ++ reg = <0x0 0x48 0x0 0x14>; ++ }; ++ }; ++ + hiubus: bus@c883c000 { + compatible = "simple-bus"; + reg = <0x0 0xc883c000 0x0 0x2000>; diff --git a/testing/linux-amlogic/0018-drm-bridge-dw-hdmi-support-dynamically-get-input-out.patch b/testing/linux-amlogic/0018-drm-bridge-dw-hdmi-support-dynamically-get-input-out.patch new file mode 100644 index 0000000000..bc86f57989 --- /dev/null +++ b/testing/linux-amlogic/0018-drm-bridge-dw-hdmi-support-dynamically-get-input-out.patch @@ -0,0 +1,102 @@ +From 2dcf2d31652207dfe20d7606804ca3b763b7f094 Mon Sep 17 00:00:00 2001 +From: Zheng Yang <zhengyang@rock-chips.com> +Date: Tue, 27 Jun 2017 16:22:01 +0800 +Subject: [PATCH] drm/bridge: dw-hdmi: support dynamically get input/out color + info + +To get input/output bus_format/enc_format dynamically, this patch +introduce following funstion in plat_data: + - get_input_bus_format + - get_output_bus_format + - get_enc_in_encoding + - get_enc_out_encoding + +Signed-off-by: Zheng Yang <zhengyang@rock-chips.com> +Signed-off-by: Neil Armstrong <narmstrong@baylibre.com> + +--- + drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 28 +++++++++++++++++++++------- + include/drm/bridge/dw_hdmi.h | 5 +++++ + 2 files changed, 26 insertions(+), 7 deletions(-) + +diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +index c3e4ed1..6473df3 100644 +--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c ++++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +@@ -1774,6 +1774,7 @@ static void hdmi_disable_overflow_interrupts(struct dw_hdmi *hdmi) + static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode) + { + int ret; ++ void *data = hdmi->plat_data->phy_data; + + hdmi_disable_overflow_interrupts(hdmi); + +@@ -1785,10 +1786,13 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode) + dev_dbg(hdmi->dev, "CEA mode used vic=%d\n", hdmi->vic); + } + +- if ((hdmi->vic == 6) || (hdmi->vic == 7) || +- (hdmi->vic == 21) || (hdmi->vic == 22) || +- (hdmi->vic == 2) || (hdmi->vic == 3) || +- (hdmi->vic == 17) || (hdmi->vic == 18)) ++ if (hdmi->plat_data->get_enc_out_encoding) ++ hdmi->hdmi_data.enc_out_encoding = ++ hdmi->plat_data->get_enc_out_encoding(data); ++ else if ((hdmi->vic == 6) || (hdmi->vic == 7) || ++ (hdmi->vic == 21) || (hdmi->vic == 22) || ++ (hdmi->vic == 2) || (hdmi->vic == 3) || ++ (hdmi->vic == 17) || (hdmi->vic == 18)) + hdmi->hdmi_data.enc_out_encoding = V4L2_YCBCR_ENC_601; + else + hdmi->hdmi_data.enc_out_encoding = V4L2_YCBCR_ENC_709; +@@ -1797,21 +1801,31 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode) + hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 0; + + /* TOFIX: Get input format from plat data or fallback to RGB888 */ +- if (hdmi->plat_data->input_bus_format) ++ if (hdmi->plat_data->get_input_bus_format) ++ hdmi->hdmi_data.enc_in_bus_format = ++ hdmi->plat_data->get_input_bus_format(data); ++ else if (hdmi->plat_data->input_bus_format) + hdmi->hdmi_data.enc_in_bus_format = + hdmi->plat_data->input_bus_format; + else + hdmi->hdmi_data.enc_in_bus_format = MEDIA_BUS_FMT_RGB888_1X24; + + /* TOFIX: Get input encoding from plat data or fallback to none */ +- if (hdmi->plat_data->input_bus_encoding) ++ if (hdmi->plat_data->get_enc_in_encoding) ++ hdmi->hdmi_data.enc_in_encoding = ++ hdmi->plat_data->get_enc_in_encoding(data); ++ else if (hdmi->plat_data->input_bus_encoding) + hdmi->hdmi_data.enc_in_encoding = + hdmi->plat_data->input_bus_encoding; + else + hdmi->hdmi_data.enc_in_encoding = V4L2_YCBCR_ENC_DEFAULT; + + /* TOFIX: Default to RGB888 output format */ +- hdmi->hdmi_data.enc_out_bus_format = MEDIA_BUS_FMT_RGB888_1X24; ++ if (hdmi->plat_data->get_output_bus_format) ++ hdmi->hdmi_data.enc_out_bus_format = ++ hdmi->plat_data->get_output_bus_format(data); ++ else ++ hdmi->hdmi_data.enc_out_bus_format = MEDIA_BUS_FMT_RGB888_1X24; + + hdmi->hdmi_data.pix_repet_factor = 0; + hdmi->hdmi_data.hdcp_enable = 0; +diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h +index d7cc5d0..27f9cce 100644 +--- a/include/drm/bridge/dw_hdmi.h ++++ b/include/drm/bridge/dw_hdmi.h +@@ -141,6 +141,11 @@ struct dw_hdmi_plat_data { + int (*configure_phy)(struct dw_hdmi *hdmi, + const struct dw_hdmi_plat_data *pdata, + unsigned long mpixelclock); ++ ++ unsigned long (*get_input_bus_format)(void *data); ++ unsigned long (*get_output_bus_format)(void *data); ++ unsigned long (*get_enc_in_encoding)(void *data); ++ unsigned long (*get_enc_out_encoding)(void *data); + }; + + struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev, diff --git a/testing/linux-amlogic/0019-drm-bridge-dw-hdmi-allow-ycbcr420-modes-for-0x200a.patch b/testing/linux-amlogic/0019-drm-bridge-dw-hdmi-allow-ycbcr420-modes-for-0x200a.patch new file mode 100644 index 0000000000..54939c84e9 --- /dev/null +++ b/testing/linux-amlogic/0019-drm-bridge-dw-hdmi-allow-ycbcr420-modes-for-0x200a.patch @@ -0,0 +1,46 @@ +From 94d815707144fb76f2e6f718a864f10a8d3f6306 Mon Sep 17 00:00:00 2001 +From: Neil Armstrong <narmstrong@baylibre.com> +Date: Wed, 14 Nov 2018 17:39:46 +0100 +Subject: [PATCH] drm/bridge: dw-hdmi: allow ycbcr420 modes for >= 0x200a + +Now the DW-HDMI Controller supports the HDMI2.0 modes, enable support +for these modes in the connector if the platform supports them. +We limit these modes to DW-HDMI IP version >= 0x200a which +are designed to support HDMI2.0 display modes. + +Signed-off-by: Neil Armstrong <narmstrong@baylibre.com> + +--- + drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 6 ++++++ + include/drm/bridge/dw_hdmi.h | 1 + + 2 files changed, 7 insertions(+) + +diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +index 6473df3..d10277f 100644 +--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c ++++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +@@ -2575,6 +2575,12 @@ __dw_hdmi_probe(struct platform_device *pdev, + if (hdmi->phy.ops->setup_hpd) + hdmi->phy.ops->setup_hpd(hdmi, hdmi->phy.data); + ++ if (hdmi->version >= 0x200a) ++ hdmi->connector.ycbcr_420_allowed = ++ hdmi->plat_data->ycbcr_420_allowed; ++ else ++ hdmi->connector.ycbcr_420_allowed = false; ++ + memset(&pdevinfo, 0, sizeof(pdevinfo)); + pdevinfo.parent = dev; + pdevinfo.id = PLATFORM_DEVID_AUTO; +diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h +index 27f9cce..c04f497a 100644 +--- a/include/drm/bridge/dw_hdmi.h ++++ b/include/drm/bridge/dw_hdmi.h +@@ -128,6 +128,7 @@ struct dw_hdmi_plat_data { + const struct drm_display_mode *mode); + unsigned long input_bus_format; + unsigned long input_bus_encoding; ++ bool ycbcr_420_allowed; + + /* Vendor PHY support */ + const struct dw_hdmi_phy_ops *phy_ops; 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 deleted file mode 100644 index 314ff482b8..0000000000 --- a/testing/linux-amlogic/0019-drm-meson-Call-drm_crtc_vblank_on-drm_crtc_vblank_of.patch +++ /dev/null @@ -1,36 +0,0 @@ -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-drm-meson-Add-YUV420-output-support.patch b/testing/linux-amlogic/0020-drm-meson-Add-YUV420-output-support.patch new file mode 100644 index 0000000000..ab69cd67c9 --- /dev/null +++ b/testing/linux-amlogic/0020-drm-meson-Add-YUV420-output-support.patch @@ -0,0 +1,582 @@ +From fb1abdc9ba8015b1a5c2a6c53ffc08fa0676db04 Mon Sep 17 00:00:00 2001 +From: Neil Armstrong <narmstrong@baylibre.com> +Date: Thu, 15 Nov 2018 16:41:23 +0100 +Subject: [PATCH] drm/meson: Add YUV420 output support + +This patch adds support for the YUV420 output from the Amlogic Meson SoCs +Video Processing Unit to the HDMI Controller. + +The YUV420 is obtained by generating a YUV444 pixel stream like +the classic HDMI display modes, but then the Video Encoder output +can be configured to down-sample the YUV444 pixel stream to a YUV420 +stream. +In addition if pixel stream down-sampling, the Y Cb Cr components must +also be mapped differently to align with the HDMI2.0 specifications. + +This mode needs a different clock generation scheme since the TMDS PHY +clock must match the 10x ration with the YUV420 pixel clock, but +the video encoder must run at 2x the pixel clock. + +This patch adds the TMDS PHY clock value in all the video clock setup +in order to better support these specific uses cases and switch +to the Common Clock framework for clocks handling in the future. + +Signed-off-by: Neil Armstrong <narmstrong@baylibre.com> + +--- + drivers/gpu/drm/meson/meson_dw_hdmi.c | 108 +++++++++++++++++++++++++++----- + drivers/gpu/drm/meson/meson_vclk.c | 95 +++++++++++++++++++++------- + drivers/gpu/drm/meson/meson_vclk.h | 7 ++- + drivers/gpu/drm/meson/meson_venc.c | 6 +- + drivers/gpu/drm/meson/meson_venc.h | 11 ++++ + drivers/gpu/drm/meson/meson_venc_cvbs.c | 3 +- + 6 files changed, 184 insertions(+), 46 deletions(-) + +diff --git a/drivers/gpu/drm/meson/meson_dw_hdmi.c b/drivers/gpu/drm/meson/meson_dw_hdmi.c +index 118c49e..0b9ecbf 100644 +--- a/drivers/gpu/drm/meson/meson_dw_hdmi.c ++++ b/drivers/gpu/drm/meson/meson_dw_hdmi.c +@@ -141,6 +141,8 @@ struct meson_dw_hdmi { + struct regulator *hdmi_supply; + u32 irq_stat; + struct dw_hdmi *hdmi; ++ unsigned long input_bus_format; ++ unsigned long output_bus_format; + }; + #define encoder_to_meson_dw_hdmi(x) \ + container_of(x, struct meson_dw_hdmi, encoder) +@@ -323,25 +325,36 @@ static void dw_hdmi_set_vclk(struct meson_dw_hdmi *dw_hdmi, + { + struct meson_drm *priv = dw_hdmi->priv; + int vic = drm_match_cea_mode(mode); ++ unsigned int phy_freq; + unsigned int vclk_freq; + unsigned int venc_freq; + unsigned int hdmi_freq; + + vclk_freq = mode->clock; + ++ /* For 420, pixel clock is half unlike venc clock */ ++ if (dw_hdmi->input_bus_format == MEDIA_BUS_FMT_UYYVYY8_0_5X24) ++ vclk_freq /= 2; ++ ++ /* TMDS clock is pixel_clock * 10 */ ++ phy_freq = vclk_freq * 10; ++ + if (!vic) { +- meson_vclk_setup(priv, MESON_VCLK_TARGET_DMT, vclk_freq, +- vclk_freq, vclk_freq, false); ++ meson_vclk_setup(priv, MESON_VCLK_TARGET_DMT, phy_freq, ++ vclk_freq, vclk_freq, vclk_freq, false); + return; + } + ++ /* 480i/576i needs global pixel doubling */ + if (mode->flags & DRM_MODE_FLAG_DBLCLK) + vclk_freq *= 2; + + venc_freq = vclk_freq; + hdmi_freq = vclk_freq; + +- if (meson_venc_hdmi_venc_repeat(vic)) ++ /* VENC double pixels for 1080i, 720p and YUV420 modes */ ++ if (meson_venc_hdmi_venc_repeat(vic) || ++ dw_hdmi->input_bus_format == MEDIA_BUS_FMT_UYYVYY8_0_5X24) + venc_freq *= 2; + + vclk_freq = max(venc_freq, hdmi_freq); +@@ -349,11 +362,11 @@ static void dw_hdmi_set_vclk(struct meson_dw_hdmi *dw_hdmi, + if (mode->flags & DRM_MODE_FLAG_DBLCLK) + venc_freq /= 2; + +- DRM_DEBUG_DRIVER("vclk:%d venc=%d hdmi=%d enci=%d\n", +- vclk_freq, venc_freq, hdmi_freq, ++ DRM_DEBUG_DRIVER("vclk:%d phy=%d venc=%d hdmi=%d enci=%d\n", ++ phy_freq, vclk_freq, venc_freq, hdmi_freq, + priv->venc.hdmi_use_enci); + +- meson_vclk_setup(priv, MESON_VCLK_TARGET_HDMI, vclk_freq, ++ meson_vclk_setup(priv, MESON_VCLK_TARGET_HDMI, phy_freq, vclk_freq, + venc_freq, hdmi_freq, priv->venc.hdmi_use_enci); + } + +@@ -387,7 +400,8 @@ static int dw_hdmi_phy_init(struct dw_hdmi *hdmi, void *data, + dw_hdmi_top_write(dw_hdmi, HDMITX_TOP_BIST_CNTL, BIT(12)); + + /* TMDS pattern setup (TOFIX Handle the YUV420 case) */ +- if (mode->clock > 340000) { ++ if (mode->clock > 340000 && ++ dw_hdmi->input_bus_format == MEDIA_BUS_FMT_YUV8_1X24) { + dw_hdmi_top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_01, 0); + dw_hdmi_top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_23, + 0x03ff03ff); +@@ -560,6 +574,8 @@ dw_hdmi_mode_valid(struct drm_connector *connector, + const struct drm_display_mode *mode) + { + struct meson_drm *priv = connector->dev->dev_private; ++ bool is_hdmi2_sink = connector->display_info.hdmi.scdc.supported; ++ unsigned int phy_freq; + unsigned int vclk_freq; + unsigned int venc_freq; + unsigned int hdmi_freq; +@@ -573,9 +589,11 @@ dw_hdmi_mode_valid(struct drm_connector *connector, + mode->vdisplay, mode->vsync_start, + mode->vsync_end, mode->vtotal, mode->type, mode->flags); + +- /* If sink max TMDS clock < 340MHz, we reject the HDMI2.0 modes */ ++ /* If sink does not support 540MHz, reject the non-420 HDMI2 modes */ + if (mode->clock > 340000 && +- connector->display_info.max_tmds_clock < 340000) ++ connector->display_info.max_tmds_clock < 340000 && ++ !drm_mode_is_420_only(&connector->display_info, mode) && ++ !drm_mode_is_420_also(&connector->display_info, mode)) + return MODE_BAD; + + /* Check against non-VIC supported modes */ +@@ -591,6 +609,15 @@ dw_hdmi_mode_valid(struct drm_connector *connector, + + vclk_freq = mode->clock; + ++ /* For 420, pixel clock is half unlike venc clock */ ++ if (drm_mode_is_420_only(&connector->display_info, mode) || ++ (!is_hdmi2_sink && ++ drm_mode_is_420_also(&connector->display_info, mode))) ++ vclk_freq /= 2; ++ ++ /* TMDS clock is pixel_clock * 10 */ ++ phy_freq = vclk_freq * 10; ++ + /* 480i/576i needs global pixel doubling */ + if (mode->flags & DRM_MODE_FLAG_DBLCLK) + vclk_freq *= 2; +@@ -598,8 +625,11 @@ dw_hdmi_mode_valid(struct drm_connector *connector, + venc_freq = vclk_freq; + hdmi_freq = vclk_freq; + +- /* VENC double pixels for 1080i and 720p modes */ +- if (meson_venc_hdmi_venc_repeat(vic)) ++ /* VENC double pixels for 1080i, 720p and YUV420 modes */ ++ if (meson_venc_hdmi_venc_repeat(vic) || ++ drm_mode_is_420_only(&connector->display_info, mode) || ++ (!is_hdmi2_sink && ++ drm_mode_is_420_also(&connector->display_info, mode))) + venc_freq *= 2; + + vclk_freq = max(venc_freq, hdmi_freq); +@@ -607,10 +637,10 @@ dw_hdmi_mode_valid(struct drm_connector *connector, + if (mode->flags & DRM_MODE_FLAG_DBLCLK) + venc_freq /= 2; + +- dev_dbg(connector->dev->dev, "%s: vclk:%d venc=%d hdmi=%d\n", __func__, +- vclk_freq, venc_freq, hdmi_freq); ++ dev_dbg(connector->dev->dev, "%s: vclk:%d phy=%d venc=%d hdmi=%d\n", ++ __func__, phy_freq, vclk_freq, venc_freq, hdmi_freq); + +- return meson_vclk_vic_supported_freq(vclk_freq); ++ return meson_vclk_vic_supported_freq(phy_freq, vclk_freq); + } + + /* Encoder */ +@@ -628,6 +658,21 @@ static int meson_venc_hdmi_encoder_atomic_check(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) + { ++ struct meson_dw_hdmi *dw_hdmi = encoder_to_meson_dw_hdmi(encoder); ++ struct drm_display_info *info = &conn_state->connector->display_info; ++ struct drm_display_mode *mode = &crtc_state->mode; ++ bool is_hdmi2_sink = ++ conn_state->connector->display_info.hdmi.scdc.supported; ++ ++ if (drm_mode_is_420_only(info, mode) || ++ (!is_hdmi2_sink && drm_mode_is_420_also(info, mode))) { ++ dw_hdmi->input_bus_format = MEDIA_BUS_FMT_UYYVYY8_0_5X24; ++ dw_hdmi->output_bus_format = MEDIA_BUS_FMT_UYYVYY8_0_5X24; ++ } else { ++ dw_hdmi->input_bus_format = MEDIA_BUS_FMT_YUV8_1X24; ++ dw_hdmi->output_bus_format = MEDIA_BUS_FMT_RGB888_1X24; ++ } ++ + return 0; + } + +@@ -665,18 +710,30 @@ static void meson_venc_hdmi_encoder_mode_set(struct drm_encoder *encoder, + struct meson_dw_hdmi *dw_hdmi = encoder_to_meson_dw_hdmi(encoder); + struct meson_drm *priv = dw_hdmi->priv; + int vic = drm_match_cea_mode(mode); ++ unsigned int ycrcb_map = MESON_VENC_MAP_CB_Y_CR; ++ bool yuv420_mode = false; + + DRM_DEBUG_DRIVER("%d:\"%s\" vic %d\n", + mode->base.id, mode->name, vic); + ++ if (dw_hdmi->input_bus_format == MEDIA_BUS_FMT_UYYVYY8_0_5X24) { ++ ycrcb_map = MESON_VENC_MAP_CR_Y_CB; ++ yuv420_mode = true; ++ } ++ + /* VENC + VENC-DVI Mode setup */ +- meson_venc_hdmi_mode_set(priv, vic, mode); ++ meson_venc_hdmi_mode_set(priv, vic, ycrcb_map, yuv420_mode, mode); + + /* VCLK Set clock */ + dw_hdmi_set_vclk(dw_hdmi, mode); + +- /* Setup YUV444 to HDMI-TX, no 10bit diphering */ +- writel_relaxed(0, priv->io_base + _REG(VPU_HDMI_FMT_CTRL)); ++ if (dw_hdmi->input_bus_format == MEDIA_BUS_FMT_UYYVYY8_0_5X24) ++ /* Setup YUV420 to HDMI-TX, no 10bit diphering */ ++ writel_relaxed(2 | (2 << 2), ++ priv->io_base + _REG(VPU_HDMI_FMT_CTRL)); ++ else ++ /* Setup YUV444 to HDMI-TX, no 10bit diphering */ ++ writel_relaxed(0, priv->io_base + _REG(VPU_HDMI_FMT_CTRL)); + } + + static const struct drm_encoder_helper_funcs +@@ -714,6 +771,20 @@ static const struct regmap_config meson_dw_hdmi_regmap_config = { + .max_register = 0x10000, + }; + ++static unsigned long meson_dw_hdmi_get_in_bus_format(void *data) ++{ ++ struct meson_dw_hdmi *dw_hdmi = (struct meson_dw_hdmi *)data; ++ ++ return dw_hdmi->input_bus_format; ++} ++ ++static unsigned long meson_dw_hdmi_get_out_bus_format(void *data) ++{ ++ struct meson_dw_hdmi *dw_hdmi = (struct meson_dw_hdmi *)data; ++ ++ return dw_hdmi->output_bus_format; ++} ++ + static bool meson_hdmi_connector_is_available(struct device *dev) + { + struct device_node *ep, *remote; +@@ -890,6 +961,9 @@ static int meson_dw_hdmi_bind(struct device *dev, struct device *master, + dw_plat_data->phy_data = meson_dw_hdmi; + dw_plat_data->input_bus_format = MEDIA_BUS_FMT_YUV8_1X24; + dw_plat_data->input_bus_encoding = V4L2_YCBCR_ENC_709; ++ dw_plat_data->get_input_bus_format = meson_dw_hdmi_get_in_bus_format; ++ dw_plat_data->get_output_bus_format = meson_dw_hdmi_get_out_bus_format; ++ dw_plat_data->ycbcr_420_allowed = true; + + platform_set_drvdata(pdev, meson_dw_hdmi); + +diff --git a/drivers/gpu/drm/meson/meson_vclk.c b/drivers/gpu/drm/meson/meson_vclk.c +index 5accceb..27c9c5e 100644 +--- a/drivers/gpu/drm/meson/meson_vclk.c ++++ b/drivers/gpu/drm/meson/meson_vclk.c +@@ -337,12 +337,17 @@ enum { + /* 2970 /1 /1 /1 /5 /2 => /1 /1 */ + MESON_VCLK_HDMI_297000, + /* 5940 /1 /1 /2 /5 /1 => /1 /1 */ +- MESON_VCLK_HDMI_594000 ++ MESON_VCLK_HDMI_594000, ++/* 2970 /1 /1 /1 /5 /1 => /1 /2 */ ++ MESON_VCLK_HDMI_594000_YUV420, + }; + + struct meson_vclk_params { ++ unsigned int pll_freq; ++ unsigned int phy_freq; ++ unsigned int vclk_freq; ++ unsigned int venc_freq; + unsigned int pixel_freq; +- unsigned int pll_base_freq; + unsigned int pll_od1; + unsigned int pll_od2; + unsigned int pll_od3; +@@ -350,8 +355,11 @@ struct meson_vclk_params { + unsigned int vclk_div; + } params[] = { + [MESON_VCLK_HDMI_ENCI_54000] = { ++ .pll_freq = 4320000, ++ .phy_freq = 270000, ++ .vclk_freq = 54000, ++ .venc_freq = 54000, + .pixel_freq = 54000, +- .pll_base_freq = 4320000, + .pll_od1 = 4, + .pll_od2 = 4, + .pll_od3 = 1, +@@ -359,8 +367,11 @@ struct meson_vclk_params { + .vclk_div = 1, + }, + [MESON_VCLK_HDMI_DDR_54000] = { +- .pixel_freq = 54000, +- .pll_base_freq = 4320000, ++ .pll_freq = 4320000, ++ .phy_freq = 270000, ++ .vclk_freq = 54000, ++ .venc_freq = 54000, ++ .pixel_freq = 27000, + .pll_od1 = 4, + .pll_od2 = 4, + .pll_od3 = 1, +@@ -368,8 +379,11 @@ struct meson_vclk_params { + .vclk_div = 1, + }, + [MESON_VCLK_HDMI_DDR_148500] = { +- .pixel_freq = 148500, +- .pll_base_freq = 2970000, ++ .pll_freq = 2970000, ++ .phy_freq = 742500, ++ .vclk_freq = 148500, ++ .venc_freq = 148500, ++ .pixel_freq = 74250, + .pll_od1 = 4, + .pll_od2 = 1, + .pll_od3 = 1, +@@ -377,8 +391,11 @@ struct meson_vclk_params { + .vclk_div = 1, + }, + [MESON_VCLK_HDMI_74250] = { ++ .pll_freq = 2970000, ++ .phy_freq = 742500, ++ .vclk_freq = 74250, ++ .venc_freq = 74250, + .pixel_freq = 74250, +- .pll_base_freq = 2970000, + .pll_od1 = 2, + .pll_od2 = 2, + .pll_od3 = 2, +@@ -386,8 +403,11 @@ struct meson_vclk_params { + .vclk_div = 1, + }, + [MESON_VCLK_HDMI_148500] = { ++ .pll_freq = 2970000, ++ .phy_freq = 1485000, ++ .vclk_freq = 148500, ++ .venc_freq = 148500, + .pixel_freq = 148500, +- .pll_base_freq = 2970000, + .pll_od1 = 1, + .pll_od2 = 2, + .pll_od3 = 2, +@@ -395,8 +415,11 @@ struct meson_vclk_params { + .vclk_div = 1, + }, + [MESON_VCLK_HDMI_297000] = { ++ .pll_freq = 2970000, ++ .phy_freq = 2970000, ++ .venc_freq = 297000, ++ .vclk_freq = 297000, + .pixel_freq = 297000, +- .pll_base_freq = 2970000, + .pll_od1 = 1, + .pll_od2 = 1, + .pll_od3 = 1, +@@ -404,14 +427,29 @@ struct meson_vclk_params { + .vclk_div = 2, + }, + [MESON_VCLK_HDMI_594000] = { ++ .pll_freq = 5940000, ++ .phy_freq = 5940000, ++ .venc_freq = 594000, ++ .vclk_freq = 594000, + .pixel_freq = 594000, +- .pll_base_freq = 5940000, + .pll_od1 = 1, + .pll_od2 = 1, + .pll_od3 = 2, + .vid_pll_div = VID_PLL_DIV_5, + .vclk_div = 1, + }, ++ [MESON_VCLK_HDMI_594000_YUV420] = { ++ .pll_freq = 2970000, ++ .phy_freq = 2970000, ++ .venc_freq = 594000, ++ .vclk_freq = 594000, ++ .pixel_freq = 297000, ++ .pll_od1 = 1, ++ .pll_od2 = 1, ++ .pll_od3 = 1, ++ .vid_pll_div = VID_PLL_DIV_5, ++ .vclk_div = 1, ++ }, + { /* sentinel */ }, + }; + +@@ -616,6 +654,7 @@ static void meson_hdmi_pll_generic_set(struct meson_drm *priv, + unsigned int od, m, frac, od1, od2, od3; + + if (meson_hdmi_pll_find_params(priv, pll_freq, &m, &frac, &od)) { ++ /* OD2 goes to the PHY, and needs to be *10, so keep OD3=1 */ + od3 = 1; + if (od < 4) { + od1 = 2; +@@ -638,21 +677,28 @@ static void meson_hdmi_pll_generic_set(struct meson_drm *priv, + } + + enum drm_mode_status +-meson_vclk_vic_supported_freq(unsigned int freq) ++meson_vclk_vic_supported_freq(unsigned int phy_freq, ++ unsigned int vclk_freq) + { + int i; + +- DRM_DEBUG_DRIVER("freq = %d\n", freq); ++ DRM_DEBUG_DRIVER("phy_freq = %d vclk_freq = %d\n", ++ phy_freq, vclk_freq); + + for (i = 0 ; params[i].pixel_freq ; ++i) { + DRM_DEBUG_DRIVER("i = %d pixel_freq = %d alt = %d\n", + i, params[i].pixel_freq, + FREQ_1000_1001(params[i].pixel_freq)); ++ DRM_DEBUG_DRIVER("i = %d phy_freq = %d alt = %d\n", ++ i, params[i].phy_freq, ++ FREQ_1000_1001(params[i].phy_freq/10)*10); + /* Match strict frequency */ +- if (freq == params[i].pixel_freq) ++ if (phy_freq == params[i].phy_freq && ++ vclk_freq == params[i].vclk_freq) + return MODE_OK; + /* Match 1000/1001 variant */ +- if (freq == FREQ_1000_1001(params[i].pixel_freq)) ++ if (phy_freq == (FREQ_1000_1001(params[i].phy_freq/10)*10) && ++ vclk_freq == FREQ_1000_1001(params[i].vclk_freq)) + return MODE_OK; + } + +@@ -666,7 +712,7 @@ static void meson_vclk_set(struct meson_drm *priv, unsigned int pll_base_freq, + unsigned int hdmi_tx_div, unsigned int venc_div, + bool hdmi_use_enci, bool vic_alternate_clock) + { +- unsigned int m, frac; ++ unsigned int m = 0, frac = 0; + + /* Set HDMI-TX sys clock */ + regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL, +@@ -863,8 +909,9 @@ static void meson_vclk_set(struct meson_drm *priv, unsigned int pll_base_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) ++ unsigned int phy_freq, unsigned int vclk_freq, ++ unsigned int venc_freq, unsigned int dac_freq, ++ bool hdmi_use_enci) + { + bool vic_alternate_clock = false; + unsigned int freq; +@@ -883,7 +930,7 @@ void meson_vclk_setup(struct meson_drm *priv, unsigned int target, + * - venc_div = 1 + * - encp encoder + */ +- meson_vclk_set(priv, vclk_freq * 10, 0, 0, 0, ++ meson_vclk_set(priv, phy_freq, 0, 0, 0, + VID_PLL_DIV_5, 2, 1, 1, false, false); + return; + } +@@ -905,9 +952,11 @@ void meson_vclk_setup(struct meson_drm *priv, unsigned int target, + } + + for (freq = 0 ; params[freq].pixel_freq ; ++freq) { +- if (vclk_freq == params[freq].pixel_freq || +- vclk_freq == FREQ_1000_1001(params[freq].pixel_freq)) { +- if (vclk_freq != params[freq].pixel_freq) ++ if ((phy_freq == params[freq].phy_freq || ++ phy_freq == FREQ_1000_1001(params[freq].phy_freq/10)*10) && ++ (vclk_freq == params[freq].vclk_freq || ++ vclk_freq == FREQ_1000_1001(params[freq].vclk_freq))) { ++ if (vclk_freq != params[freq].vclk_freq) + vic_alternate_clock = true; + else + vic_alternate_clock = false; +@@ -936,7 +985,7 @@ void meson_vclk_setup(struct meson_drm *priv, unsigned int target, + return; + } + +- meson_vclk_set(priv, params[freq].pll_base_freq, ++ meson_vclk_set(priv, params[freq].pll_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, +diff --git a/drivers/gpu/drm/meson/meson_vclk.h b/drivers/gpu/drm/meson/meson_vclk.h +index 4bd8752..c4d19dd 100644 +--- a/drivers/gpu/drm/meson/meson_vclk.h ++++ b/drivers/gpu/drm/meson/meson_vclk.h +@@ -33,10 +33,11 @@ enum { + enum drm_mode_status + meson_vclk_dmt_supported_freq(struct meson_drm *priv, unsigned int freq); + enum drm_mode_status +-meson_vclk_vic_supported_freq(unsigned int freq); ++meson_vclk_vic_supported_freq(unsigned int phy_freq, unsigned int vclk_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); ++ unsigned int phy_freq, unsigned int vclk_freq, ++ unsigned int venc_freq, unsigned int dac_freq, ++ bool hdmi_use_enci); + + #endif /* __MESON_VCLK_H */ +diff --git a/drivers/gpu/drm/meson/meson_venc.c b/drivers/gpu/drm/meson/meson_venc.c +index 1bcd642..ab72ddd 100644 +--- a/drivers/gpu/drm/meson/meson_venc.c ++++ b/drivers/gpu/drm/meson/meson_venc.c +@@ -956,6 +956,8 @@ bool meson_venc_hdmi_venc_repeat(int vic) + EXPORT_SYMBOL_GPL(meson_venc_hdmi_venc_repeat); + + void meson_venc_hdmi_mode_set(struct meson_drm *priv, int vic, ++ unsigned int ycrcb_map, ++ bool yuv420_mode, + struct drm_display_mode *mode) + { + union meson_hdmi_venc_mode *vmode = NULL; +@@ -1505,8 +1507,8 @@ void meson_venc_hdmi_mode_set(struct meson_drm *priv, int vic, + writel_relaxed((use_enci ? 1 : 2) | + (mode->flags & DRM_MODE_FLAG_PHSYNC ? 1 << 2 : 0) | + (mode->flags & DRM_MODE_FLAG_PVSYNC ? 1 << 3 : 0) | +- 4 << 5 | +- (venc_repeat ? 1 << 8 : 0) | ++ (ycrcb_map << 5) | ++ (venc_repeat || yuv420_mode ? 1 << 8 : 0) | + (hdmi_repeat ? 1 << 12 : 0), + priv->io_base + _REG(VPU_HDMI_SETTING)); + +diff --git a/drivers/gpu/drm/meson/meson_venc.h b/drivers/gpu/drm/meson/meson_venc.h +index 97eaebb..5580bf3 100644 +--- a/drivers/gpu/drm/meson/meson_venc.h ++++ b/drivers/gpu/drm/meson/meson_venc.h +@@ -33,6 +33,15 @@ enum { + MESON_VENC_MODE_HDMI, + }; + ++enum { ++ MESON_VENC_MAP_CR_Y_CB = 0, ++ MESON_VENC_MAP_Y_CB_CR, ++ MESON_VENC_MAP_Y_CR_CB, ++ MESON_VENC_MAP_CB_CR_Y, ++ MESON_VENC_MAP_CB_Y_CR, ++ MESON_VENC_MAP_CR_CB_Y, ++}; ++ + struct meson_cvbs_enci_mode { + unsigned int mode_tag; + unsigned int hso_begin; /* HSO begin position */ +@@ -70,6 +79,8 @@ extern struct meson_cvbs_enci_mode meson_cvbs_enci_ntsc; + void meson_venci_cvbs_mode_set(struct meson_drm *priv, + struct meson_cvbs_enci_mode *mode); + void meson_venc_hdmi_mode_set(struct meson_drm *priv, int vic, ++ unsigned int ycrcb_map, ++ bool yuv420_mode, + struct drm_display_mode *mode); + unsigned int meson_venci_get_field(struct meson_drm *priv); + +diff --git a/drivers/gpu/drm/meson/meson_venc_cvbs.c b/drivers/gpu/drm/meson/meson_venc_cvbs.c +index f7945ba..38a1117 100644 +--- a/drivers/gpu/drm/meson/meson_venc_cvbs.c ++++ b/drivers/gpu/drm/meson/meson_venc_cvbs.c +@@ -207,7 +207,8 @@ static void meson_venc_cvbs_encoder_mode_set(struct drm_encoder *encoder, + /* Setup 27MHz vclk2 for ENCI and VDAC */ + meson_vclk_setup(priv, MESON_VCLK_TARGET_CVBS, + MESON_VCLK_CVBS, MESON_VCLK_CVBS, +- MESON_VCLK_CVBS, true); ++ MESON_VCLK_CVBS, MESON_VCLK_CVBS, ++ true); + break; + } + } diff --git a/testing/linux-amlogic/0020-drm-meson-Use-optional-canvas-provider.patch b/testing/linux-amlogic/0020-drm-meson-Use-optional-canvas-provider.patch new file mode 100644 index 0000000000..3988b69855 --- /dev/null +++ b/testing/linux-amlogic/0020-drm-meson-Use-optional-canvas-provider.patch @@ -0,0 +1,172 @@ +From 21a926d5d6a2973c1a1665482accac7548c1a67d Mon Sep 17 00:00:00 2001 +From: Maxime Jourdan <mjourdan@baylibre.com> +Date: Mon, 15 Oct 2018 14:37:18 +0200 +Subject: [PATCH] drm/meson: Use optional canvas provider + +This is the first step into converting the meson/drm driver to use +the canvas module. + +If a canvas provider node is detected in DT, use it. Otherwise, +fall back to what is currently being done. + +Signed-off-by: Maxime Jourdan <mjourdan@baylibre.com> + +--- + drivers/gpu/drm/meson/Kconfig | 1 + + drivers/gpu/drm/meson/meson_crtc.c | 14 +++++++---- + drivers/gpu/drm/meson/meson_drv.c | 46 +++++++++++++++++++++++-------------- + drivers/gpu/drm/meson/meson_drv.h | 4 ++++ + drivers/gpu/drm/meson/meson_plane.c | 8 ++++++- + 5 files changed, 51 insertions(+), 22 deletions(-) + +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/meson_crtc.c b/drivers/gpu/drm/meson/meson_crtc.c +index 7c0bdc8..8744244 100644 +--- a/drivers/gpu/drm/meson/meson_crtc.c ++++ b/drivers/gpu/drm/meson/meson_crtc.c +@@ -197,10 +197,16 @@ 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->viu.osd1_addr, priv->viu.osd1_stride, +- priv->viu.osd1_height, MESON_CANVAS_WRAP_NONE, +- MESON_CANVAS_BLKMODE_LINEAR); ++ if (priv->canvas) ++ meson_canvas_config(priv->canvas, priv->canvas_id_osd1, ++ priv->viu.osd1_addr, priv->viu.osd1_stride, ++ priv->viu.osd1_height, MESON_CANVAS_WRAP_NONE, ++ MESON_CANVAS_BLKMODE_LINEAR, 0); ++ else ++ meson_canvas_setup(priv, MESON_CANVAS_ID_OSD1, ++ priv->viu.osd1_addr, priv->viu.osd1_stride, ++ priv->viu.osd1_height, MESON_CANVAS_WRAP_NONE, ++ MESON_CANVAS_BLKMODE_LINEAR); + + /* 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..b39c38c 100644 +--- a/drivers/gpu/drm/meson/meson_drv.c ++++ b/drivers/gpu/drm/meson/meson_drv.c +@@ -216,24 +216,33 @@ static int meson_drv_bind_master(struct device *dev, bool has_components) + goto free_drm; + } + +- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dmc"); +- if (!res) { +- ret = -EINVAL; +- 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; +- goto free_drm; +- } ++ priv->canvas = meson_canvas_get(dev); ++ if (!IS_ERR(priv->canvas)) { ++ ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_osd1); ++ if (ret) ++ goto free_drm; ++ } else { ++ priv->canvas = NULL; + +- 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); +- goto free_drm; ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dmc"); ++ if (!res) { ++ ret = -EINVAL; ++ 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; ++ 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); ++ goto free_drm; ++ } + } + + priv->vsync_irq = platform_get_irq(pdev, 0); +@@ -315,6 +324,9 @@ static void meson_drv_unbind(struct device *dev) + struct drm_device *drm = dev_get_drvdata(dev); + struct meson_drm *priv = drm->dev_private; + ++ if (priv->canvas) ++ meson_canvas_free(priv->canvas, 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..728d0ca 100644 +--- a/drivers/gpu/drm/meson/meson_drv.h ++++ b/drivers/gpu/drm/meson/meson_drv.h +@@ -22,6 +22,7 @@ + #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 { +@@ -31,6 +32,9 @@ struct meson_drm { + struct regmap *dmc; + int vsync_irq; + ++ struct meson_canvas *canvas; ++ u8 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..51bec8e 100644 +--- a/drivers/gpu/drm/meson/meson_plane.c ++++ b/drivers/gpu/drm/meson/meson_plane.c +@@ -90,6 +90,7 @@ static void meson_plane_atomic_update(struct drm_plane *plane, + .y2 = state->crtc_y + state->crtc_h, + }; + unsigned long flags; ++ u8 canvas_id_osd1; + + /* + * Update Coordinates +@@ -104,8 +105,13 @@ static void meson_plane_atomic_update(struct drm_plane *plane, + (0xFF << OSD_GLOBAL_ALPHA_SHIFT) | + OSD_BLK0_ENABLE; + ++ if (priv->canvas) ++ canvas_id_osd1 = priv->canvas_id_osd1; ++ else ++ canvas_id_osd1 = MESON_CANVAS_ID_OSD1; ++ + /* 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] = ((canvas_id_osd1 << OSD_CANVAS_SEL) | + OSD_ENDIANNESS_LE); + + /* On GXBB, Use the old non-HDR RGB2YUV converter */ 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 deleted file mode 100644 index 81569fc4d3..0000000000 --- a/testing/linux-amlogic/0020-media-platform-meson-ao-cec-make-busy-TX-warning-sil.patch +++ /dev/null @@ -1,32 +0,0 @@ -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-arm64-dts-meson-gx-Add-canvas-provider-node-to-the-v.patch b/testing/linux-amlogic/0021-arm64-dts-meson-gx-Add-canvas-provider-node-to-the-v.patch new file mode 100644 index 0000000000..fa25f46426 --- /dev/null +++ b/testing/linux-amlogic/0021-arm64-dts-meson-gx-Add-canvas-provider-node-to-the-v.patch @@ -0,0 +1,25 @@ +From 7d3414ae548dd7d6e7caad845322b0dedcf05cb1 Mon Sep 17 00:00:00 2001 +From: Maxime Jourdan <mjourdan@baylibre.com> +Date: Mon, 15 Oct 2018 14:38:24 +0200 +Subject: [PATCH] arm64: dts: meson-gx: Add canvas provider node to the vpu + +Allows the vpu driver to optionally use a canvas provider node. + +Signed-off-by: Maxime Jourdan <mjourdan@baylibre.com> + +--- + arch/arm64/boot/dts/amlogic/meson-gx.dtsi | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/arch/arm64/boot/dts/amlogic/meson-gx.dtsi b/arch/arm64/boot/dts/amlogic/meson-gx.dtsi +index fb64354..5012607 100644 +--- a/arch/arm64/boot/dts/amlogic/meson-gx.dtsi ++++ b/arch/arm64/boot/dts/amlogic/meson-gx.dtsi +@@ -540,6 +540,7 @@ + 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 { diff --git a/testing/linux-amlogic/0021-drm-meson-Output-in-YUV444-if-sink-supports-it.patch b/testing/linux-amlogic/0021-drm-meson-Output-in-YUV444-if-sink-supports-it.patch new file mode 100644 index 0000000000..50aa112db2 --- /dev/null +++ b/testing/linux-amlogic/0021-drm-meson-Output-in-YUV444-if-sink-supports-it.patch @@ -0,0 +1,31 @@ +From c9cf1e80259276c3da76bc03ab0aaa9dfac481ae Mon Sep 17 00:00:00 2001 +From: Neil Armstrong <narmstrong@baylibre.com> +Date: Sun, 18 Nov 2018 14:06:11 +0100 +Subject: [PATCH] drm/meson: Output in YUV444 if sink supports it + +With the YUV420 handling, we can no dynamically setup the HDMI output +pixel format depending on the mode and connector info. +So now, we can output in YUV444, which is the native video pipeline +format, directly the the HDMI Sink it it's supported, without +involving the HDMI Controller CSC. + +--- + drivers/gpu/drm/meson/meson_dw_hdmi.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/meson/meson_dw_hdmi.c b/drivers/gpu/drm/meson/meson_dw_hdmi.c +index 0b9ecbf..6df124c 100644 +--- a/drivers/gpu/drm/meson/meson_dw_hdmi.c ++++ b/drivers/gpu/drm/meson/meson_dw_hdmi.c +@@ -670,7 +670,10 @@ static int meson_venc_hdmi_encoder_atomic_check(struct drm_encoder *encoder, + dw_hdmi->output_bus_format = MEDIA_BUS_FMT_UYYVYY8_0_5X24; + } else { + dw_hdmi->input_bus_format = MEDIA_BUS_FMT_YUV8_1X24; +- dw_hdmi->output_bus_format = MEDIA_BUS_FMT_RGB888_1X24; ++ if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444) ++ dw_hdmi->output_bus_format = MEDIA_BUS_FMT_YUV8_1X24; ++ else ++ dw_hdmi->output_bus_format = MEDIA_BUS_FMT_RGB888_1X24; + } + + return 0; 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 deleted file mode 100644 index 226a7961a1..0000000000 --- a/testing/linux-amlogic/0021-soc-amlogic-add-meson-canvas-driver.patch +++ /dev/null @@ -1,280 +0,0 @@ -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-drm-meson-Fixes-for-drm_crtc_vblank_on-off-support.patch b/testing/linux-amlogic/0022-drm-meson-Fixes-for-drm_crtc_vblank_on-off-support.patch new file mode 100644 index 0000000000..a17be6bc38 --- /dev/null +++ b/testing/linux-amlogic/0022-drm-meson-Fixes-for-drm_crtc_vblank_on-off-support.patch @@ -0,0 +1,130 @@ +From 5a258bd31dab2dad8afcced8a7a85fa92e04edab 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: Fixes for drm_crtc_vblank_on/off support + +Since Linux 4.17, calls to drm_crtc_vblank_on/off are mandatory, and we get +a warning when ctrc is disabled : + driver forgot to call drm_crtc_vblank_off() + +But, the vsync IRQ was not totally disabled due the transient hardware +state, thus adding proper IRQ masking from the HHI system control registers. + +The last change fixes a race condition introduced by calling the added +drm_crtc_vblank_on/off when an HPD event occurs from the HDMI connector, +triggering a WARN_ON() in the _atomic_bebin() callback when the CRTC +is disabled, thus also triggering a WARN_ON() in drm_vblank_put() : + +WARNING: CPU: 0 PID: 1185 at drivers/gpu/drm/meson/meson_crtc.c:157 meson_crtc_atomic_begin+0x78/0x80 +[...] +Call trace: + meson_crtc_atomic_begin+0x78/0x80 + drm_atomic_helper_commit_planes+0x140/0x218 + drm_atomic_helper_commit_tail+0x38/0x80 + commit_tail+0x7c/0x80 + drm_atomic_helper_commit+0xdc/0x150 + drm_atomic_commit+0x54/0x60 + restore_fbdev_mode_atomic+0x198/0x238 + restore_fbdev_mode+0x6c/0x1c0 + drm_fb_helper_restore_fbdev_mode_unlocked+0x7c/0xf0 + drm_fb_helper_set_par+0x34/0x60 + drm_fb_helper_hotplug_event.part.28+0xb8/0xc8 + drm_fbdev_client_hotplug+0xa4/0xe0 + drm_client_dev_hotplug+0x90/0xe0 + drm_kms_helper_hotplug_event+0x3c/0x48 + drm_helper_hpd_irq_event+0x134/0x168 + dw_hdmi_top_thread_irq+0x3c/0x50 +[...] +WARNING: CPU: 0 PID: 1185 at drivers/gpu/drm/drm_vblank.c:1026 drm_vblank_put+0xb4/0xc8 +[...] + Call trace: + drm_vblank_put+0xb4/0xc8 + drm_crtc_vblank_put+0x24/0x30 + drm_atomic_helper_wait_for_vblanks.part.9+0x130/0x2b8 + drm_atomic_helper_commit_tail+0x68/0x80 +[...] + +The issue is the vblank need to be enabled in any occurence of : +- atomic_enable() +- atomic_begin() and state->enable == true, which was not the case + +Moving the CRTC enable code to a common function and calling in one +of these occurence solves this race condition and makes sure vblank +is enabled in each call to _atomic_begin() from the HPD event leading +to drm_atomic_helper_commit_planes(). + +To Summarize : +- Make sure that the CRTC code will calls the drm_crtc_vblank_on/off +- *Really* mask the Vsync IRQ +- Initialize and enable vblank at the first _atomic_begin()/_atomic_enable() + +Signed-off-by: Neil Armstrong <narmstrong@baylibre.com> + +--- + drivers/gpu/drm/meson/meson_crtc.c | 25 +++++++++++++++++++++++-- + 1 file changed, 23 insertions(+), 2 deletions(-) + +diff --git a/drivers/gpu/drm/meson/meson_crtc.c b/drivers/gpu/drm/meson/meson_crtc.c +index 6099997..f8e260b 100644 +--- a/drivers/gpu/drm/meson/meson_crtc.c ++++ b/drivers/gpu/drm/meson/meson_crtc.c +@@ -46,6 +46,7 @@ struct meson_crtc { + struct drm_crtc base; + struct drm_pending_vblank_event *event; + struct meson_drm *priv; ++ bool enabled; + }; + #define to_meson_crtc(x) container_of(x, struct meson_crtc, base) + +@@ -81,8 +82,7 @@ static const struct drm_crtc_funcs meson_crtc_funcs = { + + }; + +-static void meson_crtc_atomic_enable(struct drm_crtc *crtc, +- struct drm_crtc_state *old_state) ++static void meson_crtc_enable(struct drm_crtc *crtc) + { + struct meson_crtc *meson_crtc = to_meson_crtc(crtc); + struct drm_crtc_state *crtc_state = crtc->state; +@@ -106,6 +106,22 @@ static void meson_crtc_atomic_enable(struct drm_crtc *crtc, + writel_bits_relaxed(VPP_POSTBLEND_ENABLE, VPP_POSTBLEND_ENABLE, + priv->io_base + _REG(VPP_MISC)); + ++ drm_crtc_vblank_on(crtc); ++ ++ meson_crtc->enabled = true; ++} ++ ++static void meson_crtc_atomic_enable(struct drm_crtc *crtc, ++ struct drm_crtc_state *old_state) ++{ ++ struct meson_crtc *meson_crtc = to_meson_crtc(crtc); ++ struct meson_drm *priv = meson_crtc->priv; ++ ++ DRM_DEBUG_DRIVER("\n"); ++ ++ if (!meson_crtc->enabled) ++ meson_crtc_enable(crtc); ++ + priv->viu.osd1_enabled = true; + + drm_crtc_vblank_on(crtc); +@@ -139,6 +155,8 @@ static void meson_crtc_atomic_disable(struct drm_crtc *crtc, + + crtc->state->event = NULL; + } ++ ++ meson_crtc->enabled = false; + } + + static void meson_crtc_atomic_begin(struct drm_crtc *crtc, +@@ -147,6 +165,9 @@ static void meson_crtc_atomic_begin(struct drm_crtc *crtc, + struct meson_crtc *meson_crtc = to_meson_crtc(crtc); + unsigned long flags; + ++ if (crtc->state->enable && !meson_crtc->enabled) ++ meson_crtc_enable(crtc); ++ + if (crtc->state->event) { + WARN_ON(drm_crtc_vblank_get(crtc) != 0); + diff --git a/testing/linux-amlogic/0025-WIP-drm-meson-Support-Overlay-plane-for-video-render.patch b/testing/linux-amlogic/0022-drm-meson-Support-Overlay-plane-for-video-rendering.patch index 6102e1cdc3..704b26c878 100644 --- a/testing/linux-amlogic/0025-WIP-drm-meson-Support-Overlay-plane-for-video-render.patch +++ b/testing/linux-amlogic/0022-drm-meson-Support-Overlay-plane-for-video-rendering.patch @@ -1,40 +1,122 @@ -From 5d0ab03232cdda74b9eb4ce283e98aa60c40b0c9 Mon Sep 17 00:00:00 2001 +From cfcbd82639be569a7ef6f71e991d7506b5e372ab 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 +Subject: [PATCH] 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. +plane for video rendering. + +This Overlay plane support various YUV layouts : +- YUYV +- NV12 / NV21 +- YUV444 / 422 / 420 / 411 / 410 + +The scaler supports a wide range of scaling ratios, but for simplicity, +plane atomic check limits the scaling from x5 to /5 in vertical and +horizontal scaling. + +The z-order is fixed and always behind the primary plane and cannot be changed. + +The scaling parameter algorithm was taken from the Amlogic vendor kernel +code and rewritten to match the atomic universal plane requirements. + +The video rendering using this overlay plane support has been tested using +the new Kodi DRM-KMS Prime rendering path along the in-review V4L2 Mem2Mem +Hardware Video Decoder up to 3840x2160 NV12 frames on various display modes. + --- 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_canvas.c | 7 +- + drivers/gpu/drm/meson/meson_canvas.h | 11 +- + drivers/gpu/drm/meson/meson_crtc.c | 216 +++++++++++- + drivers/gpu/drm/meson/meson_drv.c | 29 +- drivers/gpu/drm/meson/meson_drv.h | 52 +++ - drivers/gpu/drm/meson/meson_overlay.c | 595 ++++++++++++++++++++++++++++++++ + drivers/gpu/drm/meson/meson_overlay.c | 586 ++++++++++++++++++++++++++++++++ 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(-) + 11 files changed, 971 insertions(+), 8 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 +index c5c4cc3..7709f2f 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 +-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 meson_canvas.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_canvas.c b/drivers/gpu/drm/meson/meson_canvas.c +index 08f6073..5de11aa 100644 +--- a/drivers/gpu/drm/meson/meson_canvas.c ++++ b/drivers/gpu/drm/meson/meson_canvas.c +@@ -39,6 +39,7 @@ + #define CANVAS_WIDTH_HBIT 0 + #define CANVAS_HEIGHT_BIT 9 + #define CANVAS_BLKMODE_BIT 24 ++#define CANVAS_ENDIAN_BIT 26 + #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) +@@ -47,7 +48,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 blkmode, ++ unsigned int endian) + { + unsigned int val; + +@@ -60,7 +62,8 @@ void meson_canvas_setup(struct meson_drm *priv, + CANVAS_WIDTH_HBIT) | + (height << CANVAS_HEIGHT_BIT) | + (wrap << 22) | +- (blkmode << CANVAS_BLKMODE_BIT)); ++ (blkmode << CANVAS_BLKMODE_BIT) | ++ (endian << CANVAS_ENDIAN_BIT)); + + regmap_write(priv->dmc, DMC_CAV_LUT_ADDR, + CANVAS_LUT_WR_EN | canvas_index); +diff --git a/drivers/gpu/drm/meson/meson_canvas.h b/drivers/gpu/drm/meson/meson_canvas.h +index af1759d..85dbf26 100644 +--- a/drivers/gpu/drm/meson/meson_canvas.h ++++ b/drivers/gpu/drm/meson/meson_canvas.h +@@ -23,6 +23,9 @@ + #define __MESON_CANVAS_H + + #define MESON_CANVAS_ID_OSD1 0x4e ++#define MESON_CANVAS_ID_VD1_0 0x60 ++#define MESON_CANVAS_ID_VD1_1 0x61 ++#define MESON_CANVAS_ID_VD1_2 0x62 + + /* Canvas configuration. */ + #define MESON_CANVAS_WRAP_NONE 0x00 +@@ -33,10 +36,16 @@ + #define MESON_CANVAS_BLKMODE_32x32 0x01 + #define MESON_CANVAS_BLKMODE_64x64 0x02 + ++#define MESON_CANVAS_ENDIAN_SWAP16 0x1 ++#define MESON_CANVAS_ENDIAN_SWAP32 0x3 ++#define MESON_CANVAS_ENDIAN_SWAP64 0x7 ++#define MESON_CANVAS_ENDIAN_SWAP128 0xf ++ + 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 blkmode, ++ unsigned int endian); + + #endif /* __MESON_CANVAS_H */ diff --git a/drivers/gpu/drm/meson/meson_crtc.c b/drivers/gpu/drm/meson/meson_crtc.c -index 7c8ad06..90c826b 100644 +index 8744244..1d9d22c 100644 --- a/drivers/gpu/drm/meson/meson_crtc.c +++ b/drivers/gpu/drm/meson/meson_crtc.c @@ -25,6 +25,7 @@ @@ -45,7 +127,7 @@ index 7c8ad06..90c826b 100644 #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, +@@ -98,6 +99,10 @@ static void meson_crtc_atomic_enable(struct drm_crtc *crtc, writel(crtc_state->mode.hdisplay, priv->io_base + _REG(VPP_POSTBLEND_H_SIZE)); @@ -56,7 +138,12 @@ index 7c8ad06..90c826b 100644 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, +@@ -114,11 +119,17 @@ static void meson_crtc_atomic_disable(struct drm_crtc *crtc, + + drm_crtc_vblank_off(crtc); + ++ DRM_DEBUG_DRIVER("\n"); ++ priv->viu.osd1_enabled = false; priv->viu.osd1_commit = false; @@ -70,7 +157,7 @@ index 7c8ad06..90c826b 100644 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, +@@ -153,6 +164,7 @@ static void meson_crtc_atomic_flush(struct drm_crtc *crtc, struct meson_drm *priv = meson_crtc->priv; priv->viu.osd1_commit = true; @@ -78,7 +165,16 @@ index 7c8ad06..90c826b 100644 } static const struct drm_crtc_helper_funcs meson_crtc_helper_funcs = { -@@ -210,6 +220,164 @@ void meson_crtc_irq(struct meson_drm *priv) +@@ -206,7 +218,7 @@ void meson_crtc_irq(struct meson_drm *priv) + meson_canvas_setup(priv, MESON_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, +@@ -215,6 +227,206 @@ void meson_crtc_irq(struct meson_drm *priv) priv->viu.osd1_commit = false; } @@ -87,20 +183,61 @@ index 7c8ad06..90c826b 100644 + + 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); ++ if (priv->canvas) ++ meson_canvas_config(priv->canvas, ++ 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, ++ MESON_CANVAS_ENDIAN_SWAP64); ++ else ++ meson_canvas_setup(priv, MESON_CANVAS_ID_VD1_2, ++ priv->viu.vd1_addr2, ++ priv->viu.vd1_stride2, ++ priv->viu.vd1_height2, ++ MESON_CANVAS_WRAP_NONE, ++ MESON_CANVAS_BLKMODE_LINEAR, ++ MESON_CANVAS_ENDIAN_SWAP64); ++ /* fallthrough */ + 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); ++ if (priv->canvas) ++ meson_canvas_config(priv->canvas, ++ 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, ++ MESON_CANVAS_ENDIAN_SWAP64); ++ else ++ meson_canvas_setup(priv, MESON_CANVAS_ID_VD1_1, ++ priv->viu.vd1_addr2, ++ priv->viu.vd1_stride2, ++ priv->viu.vd1_height2, ++ MESON_CANVAS_WRAP_NONE, ++ MESON_CANVAS_BLKMODE_LINEAR, ++ MESON_CANVAS_ENDIAN_SWAP64); ++ /* fallthrough */ + 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); ++ if (priv->canvas) ++ meson_canvas_config(priv->canvas, ++ 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, ++ MESON_CANVAS_ENDIAN_SWAP64); ++ else ++ meson_canvas_setup(priv, MESON_CANVAS_ID_VD1_0, ++ priv->viu.vd1_addr2, ++ priv->viu.vd1_stride2, ++ priv->viu.vd1_height2, ++ MESON_CANVAS_WRAP_NONE, ++ MESON_CANVAS_BLKMODE_LINEAR, ++ MESON_CANVAS_ENDIAN_SWAP64); + }; + + writel_relaxed(priv->viu.vd1_if0_gen_reg, @@ -192,13 +329,13 @@ index 7c8ad06..90c826b 100644 + 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)); ++ 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)); ++ 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)); ++ 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)); ++ 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, @@ -208,9 +345,9 @@ index 7c8ad06..90c826b 100644 + 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)); ++ 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)); ++ 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, @@ -229,12 +366,13 @@ index 7c8ad06..90c826b 100644 + 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)); ++ writel_relaxed(0x42, 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, ++ 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; @@ -244,10 +382,10 @@ index 7c8ad06..90c826b 100644 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 +index b39c38c..b55e03d 100644 --- a/drivers/gpu/drm/meson/meson_drv.c +++ b/drivers/gpu/drm/meson/meson_drv.c -@@ -42,6 +42,7 @@ +@@ -41,6 +41,7 @@ #include "meson_drv.h" #include "meson_plane.h" @@ -255,23 +393,32 @@ index de46833..2a002cc 100644 #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); +@@ -221,6 +222,24 @@ static int meson_drv_bind_master(struct device *dev, bool has_components) + ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_osd1); + if (ret) + goto free_drm; ++ ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_0); ++ if (ret) { ++ meson_canvas_free(priv->canvas, priv->canvas_id_osd1); ++ goto free_drm; ++ } ++ ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_1); ++ if (ret) { ++ meson_canvas_free(priv->canvas, priv->canvas_id_osd1); ++ meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0); ++ goto free_drm; ++ } ++ ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_2); ++ if (ret) { ++ meson_canvas_free(priv->canvas, priv->canvas_id_osd1); ++ meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0); ++ meson_canvas_free(priv->canvas, priv->canvas_id_vd1_1); ++ goto free_drm; ++ } + } else { + priv->canvas = NULL; -@@ -278,6 +288,10 @@ static int meson_drv_bind_master(struct device *dev, bool has_components) +@@ -281,6 +300,10 @@ static int meson_drv_bind_master(struct device *dev, bool has_components) if (ret) goto free_drm; @@ -282,17 +429,31 @@ index de46833..2a002cc 100644 ret = meson_crtc_create(priv); if (ret) goto free_drm; +@@ -324,8 +347,12 @@ static void meson_drv_unbind(struct device *dev) + struct drm_device *drm = dev_get_drvdata(dev); + struct meson_drm *priv = drm->dev_private; + +- if (priv->canvas) ++ if (priv->canvas) { + meson_canvas_free(priv->canvas, priv->canvas_id_osd1); ++ meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0); ++ meson_canvas_free(priv->canvas, priv->canvas_id_vd1_1); ++ meson_canvas_free(priv->canvas, priv->canvas_id_vd1_2); ++ } + + drm_dev_unregister(drm); + drm_kms_helper_poll_fini(drm); diff --git a/drivers/gpu/drm/meson/meson_drv.h b/drivers/gpu/drm/meson/meson_drv.h -index dfea959..e9305d7 100644 +index 728d0ca..c971557 100644 --- a/drivers/gpu/drm/meson/meson_drv.h +++ b/drivers/gpu/drm/meson/meson_drv.h -@@ -33,11 +33,15 @@ struct meson_drm { +@@ -34,11 +34,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 meson_canvas *canvas; + u8 canvas_id_osd1; ++ u8 canvas_id_vd1_0; ++ u8 canvas_id_vd1_1; ++ u8 canvas_id_vd1_2; struct drm_device *drm; struct drm_crtc *crtc; @@ -302,7 +463,7 @@ index dfea959..e9305d7 100644 /* Components Data */ struct { -@@ -49,6 +53,54 @@ struct meson_drm { +@@ -50,6 +54,54 @@ struct meson_drm { uint32_t osd1_addr; uint32_t osd1_stride; uint32_t osd1_height; @@ -359,10 +520,10 @@ index dfea959..e9305d7 100644 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 +index 0000000..9aebc5e --- /dev/null +++ b/drivers/gpu/drm/meson/meson_overlay.c -@@ -0,0 +1,595 @@ +@@ -0,0 +1,586 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018 BayLibre, SAS @@ -386,6 +547,7 @@ index 0000000..ea7261a +#include "meson_overlay.h" +#include "meson_vpp.h" +#include "meson_viu.h" ++#include "meson_canvas.h" +#include "meson_registers.h" + +/* VD1_IF0_GEN_REG */ @@ -452,7 +614,7 @@ index 0000000..ea7261a +#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_plane_state *state) +{ + struct drm_crtc_state *crtc_state; + @@ -475,20 +637,19 @@ index 0000000..ea7261a + return value >> 16; +} + -+static const uint8_t skip_tab[6] = {0x24, 0x04, 0x68, 0x48, 0x28, 0x08}; ++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) ++static void meson_overlay_get_vertical_phase(unsigned int 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)) { ++ if (!interlace && ratio_y > (1 << 18)) + offset_out = (1 * ratio_y) >> 10; -+ } + + while ((offset_in + (4 << 8)) <= offset_out) { + repeat_skip++; @@ -512,19 +673,19 @@ index 0000000..ea7261a + 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; ++ struct drm_plane_state *state = plane->state; + unsigned int vd_start_lines, vd_end_lines; + unsigned int hd_start_lines, hd_end_lines; ++ unsigned int crtc_height, crtc_width; + unsigned int vsc_startp, vsc_endp; + unsigned int hsc_startp, hsc_endp; -+ unsigned int ratio_x, ratio_y; -+ unsigned int w_in, h_in; ++ unsigned int crop_top, crop_left; + int vphase, vphase_repeat_skip; ++ unsigned int ratio_x, ratio_y; + int temp_height, temp_width; ++ unsigned int w_in, h_in; + int temp, start, end; + + if (!crtc_state) { @@ -546,19 +707,15 @@ index 0000000..ea7261a + video_height = state->crtc_h; + + DRM_DEBUG("crtc_width %d crtc_height %d interlace %d\n", -+ crtc_width, crtc_height, interlace_mode); ++ 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); ++ 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); ++ 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++; + @@ -571,7 +728,7 @@ index 0000000..ea7261a + + /* Vertical */ + -+ start = video_top + video_height / 2 - ((h_in << 17) / ratio_x); ++ start = video_top + video_height / 2 - ((h_in << 17) / ratio_y); + end = (h_in << 18) / ratio_y + start - 1; + + if (video_top < 0 && start < 0) @@ -582,11 +739,13 @@ index 0000000..ea7261a + vd_start_lines = 0; + + if (video_top < 0) -+ temp_height = min_t(unsigned int, (video_top + video_height - 1), -+ (crtc_height - 1)); ++ 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_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); @@ -594,6 +753,11 @@ index 0000000..ea7261a + vd_start_lines += crop_left; + vd_end_lines += crop_left; + ++ /* ++ * TOFIX: Input frames are handled and scaled like progressive frames, ++ * proper handling of interlaced field input frames need to be figured ++ * out using the proper framebuffer flags set by userspace. ++ */ + if (interlace_mode) { + start >>= 1; + end >>= 1; @@ -621,21 +785,21 @@ index 0000000..ea7261a + hd_start_lines = 0; + + if (video_left < 0) -+ temp_width = min_t(unsigned int, (video_left + video_width - 1), -+ (crtc_width - 1)); ++ 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_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)); ++ 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; @@ -654,8 +818,9 @@ index 0000000..ea7261a + 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.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); @@ -671,7 +836,8 @@ index 0000000..ea7261a + 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) | ++ 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; @@ -690,8 +856,8 @@ index 0000000..ea7261a + + 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); ++ 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; +} @@ -709,14 +875,15 @@ index 0000000..ea7261a + + DRM_DEBUG_DRIVER("\n"); + ++ /* Fallback is canvas provider is not available */ ++ if (!priv->canvas) { ++ priv->canvas_id_vd1_0 = MESON_CANVAS_ID_VD1_0; ++ priv->canvas_id_vd1_1 = MESON_CANVAS_ID_VD1_1; ++ priv->canvas_id_vd1_2 = MESON_CANVAS_ID_VD1_2; ++ } ++ + 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 | @@ -728,17 +895,9 @@ index 0000000..ea7261a + /* 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; @@ -748,15 +907,7 @@ index 0000000..ea7261a + 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; ++ /* TOFIX DRM_FORMAT_RGB888 should be supported */ + case DRM_FORMAT_YUYV: + priv->viu.vd1_if0_gen_reg |= VD_BYTES_PER_PIXEL(1); + priv->viu.vd1_if0_canvas0 = @@ -854,6 +1005,7 @@ index 0000000..ea7261a + priv->viu.vd1_addr2, + priv->viu.vd1_stride2, + priv->viu.vd1_height2); ++ /* fallthrough */ + case 2: + gem = drm_fb_cma_get_gem_obj(fb, 1); + priv->viu.vd1_addr1 = gem->paddr + fb->offsets[1]; @@ -865,6 +1017,7 @@ index 0000000..ea7261a + priv->viu.vd1_addr1, + priv->viu.vd1_stride1, + priv->viu.vd1_height1); ++ /* fallthrough */ + case 1: + gem = drm_fb_cma_get_gem_obj(fb, 0); + priv->viu.vd1_addr0 = gem->paddr + fb->offsets[0]; @@ -917,7 +1070,6 @@ index 0000000..ea7261a +}; + +static const uint32_t supported_drm_formats[] = { -+ DRM_FORMAT_RGB888, + DRM_FORMAT_YUYV, + DRM_FORMAT_NV12, + DRM_FORMAT_NV21, @@ -960,11 +1112,11 @@ index 0000000..ea7261a +} 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 +index 0000000..dae24f5 --- /dev/null +++ b/drivers/gpu/drm/meson/meson_overlay.h @@ -0,0 +1,14 @@ -+// SPDX-License-Identifier: GPL-2.0+ ++/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2018 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> @@ -1007,10 +1159,10 @@ index bca8714..5c7e02c 100644 #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 +index 6bcfa52..2dffb98 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) +@@ -329,6 +329,21 @@ void meson_viu_init(struct meson_drm *priv) 0xff << OSD_REPLACE_SHIFT, priv->io_base + _REG(VIU_OSD2_CTRL_STAT2)); @@ -1104,38 +1256,3 @@ index 27356f8..5dc24a9 100644 + 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/0022-dt-bindings-soc-amlogic-add-meson-canvas-documentati.patch b/testing/linux-amlogic/0022-dt-bindings-soc-amlogic-add-meson-canvas-documentati.patch deleted file mode 100644 index 6d449e3f9b..0000000000 --- a/testing/linux-amlogic/0022-dt-bindings-soc-amlogic-add-meson-canvas-documentati.patch +++ /dev/null @@ -1,55 +0,0 @@ -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 deleted file mode 100644 index 6e97663fc1..0000000000 --- a/testing/linux-amlogic/0023-ARM64-dts-meson-gx-add-dmcbus-and-canvas-nodes.patch +++ /dev/null @@ -1,40 +0,0 @@ -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/0023-drm-meson-Fix-an-Alpha-Primary-Plane-bug-on-Meson-GX.patch b/testing/linux-amlogic/0023-drm-meson-Fix-an-Alpha-Primary-Plane-bug-on-Meson-GX.patch new file mode 100644 index 0000000000..23d0bc366f --- /dev/null +++ b/testing/linux-amlogic/0023-drm-meson-Fix-an-Alpha-Primary-Plane-bug-on-Meson-GX.patch @@ -0,0 +1,124 @@ +From 90c2e40067b0591a1419472da186463f3f84613b Mon Sep 17 00:00:00 2001 +From: Neil Armstrong <narmstrong@baylibre.com> +Date: Thu, 22 Nov 2018 17:27:20 +0100 +Subject: [PATCH] drm/meson: Fix an Alpha Primary Plane bug on Meson GXL/GXM + SoCs + +On the Amlogic GXL & GXM SoCs, a bug occurs in the OSD1 plane when +alpha is used where the alpha is not aligned with the pixel content. + +The woraround Amlogic implemented is the reset the OSD1 plane hardware +block each time the plane is updated, solving the issue. + +In the reset, we still need to save the content of 2 registers which +depends on the status of the plane, in addition to reload the scaler +conversion matrix in the same time. + +Signed-off-by: Neil Armstrong <narmstrong@baylibre.com> + +--- + drivers/gpu/drm/meson/meson_crtc.c | 1 + + drivers/gpu/drm/meson/meson_plane.c | 12 ++++++++++++ + drivers/gpu/drm/meson/meson_viu.c | 27 +++++++++++++++++++++++++++ + drivers/gpu/drm/meson/meson_viu.h | 1 + + 4 files changed, 41 insertions(+) + +diff --git a/drivers/gpu/drm/meson/meson_crtc.c b/drivers/gpu/drm/meson/meson_crtc.c +index f8e260b..5312cce 100644 +--- a/drivers/gpu/drm/meson/meson_crtc.c ++++ b/drivers/gpu/drm/meson/meson_crtc.c +@@ -202,6 +202,7 @@ void meson_crtc_irq(struct meson_drm *priv) + + /* Update the OSD registers */ + if (priv->viu.osd1_enabled && priv->viu.osd1_commit) { ++ + writel_relaxed(priv->viu.osd1_ctrl_stat, + priv->io_base + _REG(VIU_OSD1_CTRL_STAT)); + writel_relaxed(priv->viu.osd1_blk0_cfg[0], +diff --git a/drivers/gpu/drm/meson/meson_plane.c b/drivers/gpu/drm/meson/meson_plane.c +index 12a47b4..8372288 100644 +--- a/drivers/gpu/drm/meson/meson_plane.c ++++ b/drivers/gpu/drm/meson/meson_plane.c +@@ -79,6 +79,7 @@ + struct meson_plane { + struct drm_plane base; + struct meson_drm *priv; ++ bool enabled; + }; + #define to_meson_plane(x) container_of(x, struct meson_plane, base) + +@@ -303,6 +304,15 @@ static void meson_plane_atomic_update(struct drm_plane *plane, + priv->viu.osd1_stride = fb->pitches[0]; + priv->viu.osd1_height = fb->height; + ++ if (!meson_plane->enabled) { ++ /* Reset OSD1 at updates on GXL+ SoCs */ ++ if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") || ++ meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) ++ meson_viu_reset(priv); ++ ++ meson_plane->enabled = true; ++ } ++ + spin_unlock_irqrestore(&priv->drm->event_lock, flags); + } + +@@ -316,6 +326,8 @@ static void meson_plane_atomic_disable(struct drm_plane *plane, + writel_bits_relaxed(VPP_OSD1_POSTBLEND, 0, + priv->io_base + _REG(VPP_MISC)); + ++ meson_plane->enabled = false; ++ + } + + static const struct drm_plane_helper_funcs meson_plane_helper_funcs = { +diff --git a/drivers/gpu/drm/meson/meson_viu.c b/drivers/gpu/drm/meson/meson_viu.c +index 2dffb98..a41dd6c 100644 +--- a/drivers/gpu/drm/meson/meson_viu.c ++++ b/drivers/gpu/drm/meson/meson_viu.c +@@ -296,6 +296,33 @@ static void meson_viu_load_matrix(struct meson_drm *priv) + true); + } + ++/* VIU OSD1 Reset as workaround for GXL+ Alpha OSD Bug */ ++void meson_viu_reset(struct meson_drm *priv) ++{ ++ uint32_t osd1_fifo_ctrl_stat, osd1_ctrl_stat2; ++ ++ /* Save these 2 registers state */ ++ osd1_fifo_ctrl_stat = readl_relaxed( ++ priv->io_base + _REG(VIU_OSD1_FIFO_CTRL_STAT)); ++ osd1_ctrl_stat2 = readl_relaxed( ++ priv->io_base + _REG(VIU_OSD1_CTRL_STAT2)); ++ ++ /* Reset OSD1 */ ++ writel_bits_relaxed(BIT(0), BIT(0), ++ priv->io_base + _REG(VIU_SW_RESET)); ++ writel_bits_relaxed(BIT(0), 0, ++ priv->io_base + _REG(VIU_SW_RESET)); ++ ++ /* Rewrite these registers state lost in the reset */ ++ writel_relaxed(osd1_fifo_ctrl_stat, ++ priv->io_base + _REG(VIU_OSD1_FIFO_CTRL_STAT)); ++ writel_relaxed(osd1_ctrl_stat2, ++ priv->io_base + _REG(VIU_OSD1_CTRL_STAT2)); ++ ++ /* Reload the conversion matrix */ ++ meson_viu_load_matrix(priv); ++} ++ + void meson_viu_init(struct meson_drm *priv) + { + uint32_t reg; +diff --git a/drivers/gpu/drm/meson/meson_viu.h b/drivers/gpu/drm/meson/meson_viu.h +index 073b191..e4a6e2f 100644 +--- a/drivers/gpu/drm/meson/meson_viu.h ++++ b/drivers/gpu/drm/meson/meson_viu.h +@@ -59,6 +59,7 @@ + #define OSD_REPLACE_EN BIT(14) + #define OSD_REPLACE_SHIFT 6 + ++void meson_viu_reset(struct meson_drm *priv); + void meson_viu_init(struct meson_drm *priv); + + #endif /* __MESON_VIU_H */ diff --git a/testing/linux-amlogic/0023-drm-meson-move-OSD-scaler-management-into-plane-atom.patch b/testing/linux-amlogic/0023-drm-meson-move-OSD-scaler-management-into-plane-atom.patch new file mode 100644 index 0000000000..54b356de64 --- /dev/null +++ b/testing/linux-amlogic/0023-drm-meson-move-OSD-scaler-management-into-plane-atom.patch @@ -0,0 +1,198 @@ +From b92e7773bdb2d5c86091cbb2d03cc55ec6365115 Mon Sep 17 00:00:00 2001 +From: Neil Armstrong <narmstrong@baylibre.com> +Date: Mon, 29 Oct 2018 17:04:05 +0100 +Subject: [PATCH] drm/meson: move OSD scaler management into plane atomic + update + +In preparation to support the Primary Plane scaling, move the basic +OSD Interlace-Only scaler setup code into the primary plane atomic +update callback and handle the vsync scaler update like the overlay +plane scaling registers update. + +--- + drivers/gpu/drm/meson/meson_crtc.c | 35 ++++++++++++++++------------ + drivers/gpu/drm/meson/meson_drv.h | 10 ++++++++ + drivers/gpu/drm/meson/meson_plane.c | 39 ++++++++++++++++++++++++++++++- + drivers/gpu/drm/meson/meson_vpp.c | 46 ------------------------------------- + 4 files changed, 68 insertions(+), 62 deletions(-) + +diff --git a/drivers/gpu/drm/meson/meson_crtc.c b/drivers/gpu/drm/meson/meson_crtc.c +index 1d9d22c..6099997 100644 +--- a/drivers/gpu/drm/meson/meson_crtc.c ++++ b/drivers/gpu/drm/meson/meson_crtc.c +@@ -193,21 +193,26 @@ void meson_crtc_irq(struct meson_drm *priv) + priv->io_base + _REG(VIU_OSD1_BLK0_CFG_W3)); + writel_relaxed(priv->viu.osd1_blk0_cfg[4], + priv->io_base + _REG(VIU_OSD1_BLK0_CFG_W4)); +- +- /* If output is interlace, make use of the Scaler */ +- if (priv->viu.osd1_interlace) { +- struct drm_plane *plane = priv->primary_plane; +- struct drm_plane_state *state = plane->state; +- struct drm_rect dest = { +- .x1 = state->crtc_x, +- .y1 = state->crtc_y, +- .x2 = state->crtc_x + state->crtc_w, +- .y2 = state->crtc_y + state->crtc_h, +- }; +- +- meson_vpp_setup_interlace_vscaler_osd1(priv, &dest); +- } else +- meson_vpp_disable_interlace_vscaler_osd1(priv); ++ writel_relaxed(priv->viu.osd_sc_ctrl0, ++ priv->io_base + _REG(VPP_OSD_SC_CTRL0)); ++ writel_relaxed(priv->viu.osd_sc_i_wh_m1, ++ priv->io_base + _REG(VPP_OSD_SCI_WH_M1)); ++ writel_relaxed(priv->viu.osd_sc_o_h_start_end, ++ priv->io_base + _REG(VPP_OSD_SCO_H_START_END)); ++ writel_relaxed(priv->viu.osd_sc_o_v_start_end, ++ priv->io_base + _REG(VPP_OSD_SCO_V_START_END)); ++ writel_relaxed(priv->viu.osd_sc_v_ini_phase, ++ priv->io_base + _REG(VPP_OSD_VSC_INI_PHASE)); ++ writel_relaxed(priv->viu.osd_sc_v_phase_step, ++ priv->io_base + _REG(VPP_OSD_VSC_PHASE_STEP)); ++ writel_relaxed(priv->viu.osd_sc_h_ini_phase, ++ priv->io_base + _REG(VPP_OSD_HSC_INI_PHASE)); ++ writel_relaxed(priv->viu.osd_sc_h_phase_step, ++ priv->io_base + _REG(VPP_OSD_HSC_PHASE_STEP)); ++ writel_relaxed(priv->viu.osd_sc_h_ctrl0, ++ priv->io_base + _REG(VPP_OSD_HSC_CTRL0)); ++ writel_relaxed(priv->viu.osd_sc_v_ctrl0, ++ priv->io_base + _REG(VPP_OSD_VSC_CTRL0)); + + if (priv->canvas) + meson_canvas_config(priv->canvas, priv->canvas_id_osd1, +diff --git a/drivers/gpu/drm/meson/meson_drv.h b/drivers/gpu/drm/meson/meson_drv.h +index c971557..a955354 100644 +--- a/drivers/gpu/drm/meson/meson_drv.h ++++ b/drivers/gpu/drm/meson/meson_drv.h +@@ -54,6 +54,16 @@ struct meson_drm { + uint32_t osd1_addr; + uint32_t osd1_stride; + uint32_t osd1_height; ++ uint32_t osd_sc_ctrl0; ++ uint32_t osd_sc_i_wh_m1; ++ uint32_t osd_sc_o_h_start_end; ++ uint32_t osd_sc_o_v_start_end; ++ uint32_t osd_sc_v_ini_phase; ++ uint32_t osd_sc_v_phase_step; ++ uint32_t osd_sc_h_ini_phase; ++ uint32_t osd_sc_h_phase_step; ++ uint32_t osd_sc_h_ctrl0; ++ uint32_t osd_sc_v_ctrl0; + + bool vd1_enabled; + bool vd1_commit; +diff --git a/drivers/gpu/drm/meson/meson_plane.c b/drivers/gpu/drm/meson/meson_plane.c +index 51bec8e..f915a79 100644 +--- a/drivers/gpu/drm/meson/meson_plane.c ++++ b/drivers/gpu/drm/meson/meson_plane.c +@@ -143,13 +143,50 @@ static void meson_plane_atomic_update(struct drm_plane *plane, + break; + }; + ++ /* ++ * When the output is interlaced, the OSD must switch between ++ * each field using the INTERLACE_SEL_ODD (0) of VIU_OSD1_BLK0_CFG_W0 ++ * at each vsync. ++ * But the vertical scaler can provide such funtionnality if ++ * is configured for 2:1 scaling with interlace options enabled. ++ */ + if (state->crtc->mode.flags & DRM_MODE_FLAG_INTERLACE) { + priv->viu.osd1_interlace = true; + + dest.y1 /= 2; + dest.y2 /= 2; +- } else ++ ++ priv->viu.osd_sc_ctrl0 = BIT(3)| /* Enable scaler */ ++ BIT(2); /* Select OSD1 */ ++ ++ /* 2:1 scaling */ ++ priv->viu.osd_sc_i_wh_m1 = ((drm_rect_width(&dest) - 1) << 16) | ++ (drm_rect_height(&dest) - 1); ++ priv->viu.osd_sc_o_h_start_end = (dest.x1 << 16) | dest.x2; ++ priv->viu.osd_sc_o_v_start_end = (dest.y1 << 16) | dest.y2; ++ ++ /* 2:1 vertical scaling values */ ++ priv->viu.osd_sc_v_ini_phase = BIT(16); ++ priv->viu.osd_sc_v_phase_step = BIT(25); ++ priv->viu.osd_sc_v_ctrl0 = ++ (4 << 0) | /* osd_vsc_bank_length */ ++ (4 << 3) | /* osd_vsc_top_ini_rcv_num0 */ ++ (1 << 8) | /* osd_vsc_top_rpt_p0_num0 */ ++ (6 << 11) | /* osd_vsc_bot_ini_rcv_num0 */ ++ (2 << 16) | /* osd_vsc_bot_rpt_p0_num0 */ ++ BIT(23) | /* osd_prog_interlace */ ++ BIT(24); /* Enable vertical scaler */ ++ ++ /* No horizontal scaling */ ++ priv->viu.osd_sc_h_ini_phase = 0; ++ priv->viu.osd_sc_h_phase_step = 0; ++ priv->viu.osd_sc_h_ctrl0 = 0; ++ } else { + priv->viu.osd1_interlace = false; ++ priv->viu.osd_sc_ctrl0 = 0; ++ priv->viu.osd_sc_h_ctrl0 = 0; ++ priv->viu.osd_sc_v_ctrl0 = 0; ++ } + + /* + * The format of these registers is (x2 << 16 | x1), +diff --git a/drivers/gpu/drm/meson/meson_vpp.c b/drivers/gpu/drm/meson/meson_vpp.c +index 5dc24a9..f9efb43 100644 +--- a/drivers/gpu/drm/meson/meson_vpp.c ++++ b/drivers/gpu/drm/meson/meson_vpp.c +@@ -51,52 +51,6 @@ void meson_vpp_setup_mux(struct meson_drm *priv, unsigned int mux) + writel(mux, priv->io_base + _REG(VPU_VIU_VENC_MUX_CTRL)); + } + +-/* +- * When the output is interlaced, the OSD must switch between +- * each field using the INTERLACE_SEL_ODD (0) of VIU_OSD1_BLK0_CFG_W0 +- * at each vsync. +- * But the vertical scaler can provide such funtionnality if +- * is configured for 2:1 scaling with interlace options enabled. +- */ +-void meson_vpp_setup_interlace_vscaler_osd1(struct meson_drm *priv, +- struct drm_rect *input) +-{ +- writel_relaxed(BIT(3) /* Enable scaler */ | +- BIT(2), /* Select OSD1 */ +- priv->io_base + _REG(VPP_OSD_SC_CTRL0)); +- +- writel_relaxed(((drm_rect_width(input) - 1) << 16) | +- (drm_rect_height(input) - 1), +- priv->io_base + _REG(VPP_OSD_SCI_WH_M1)); +- /* 2:1 scaling */ +- writel_relaxed(((input->x1) << 16) | (input->x2), +- priv->io_base + _REG(VPP_OSD_SCO_H_START_END)); +- writel_relaxed(((input->y1 >> 1) << 16) | (input->y2 >> 1), +- priv->io_base + _REG(VPP_OSD_SCO_V_START_END)); +- +- /* 2:1 scaling values */ +- writel_relaxed(BIT(16), priv->io_base + _REG(VPP_OSD_VSC_INI_PHASE)); +- writel_relaxed(BIT(25), priv->io_base + _REG(VPP_OSD_VSC_PHASE_STEP)); +- +- writel_relaxed(0, priv->io_base + _REG(VPP_OSD_HSC_CTRL0)); +- +- writel_relaxed((4 << 0) /* osd_vsc_bank_length */ | +- (4 << 3) /* osd_vsc_top_ini_rcv_num0 */ | +- (1 << 8) /* osd_vsc_top_rpt_p0_num0 */ | +- (6 << 11) /* osd_vsc_bot_ini_rcv_num0 */ | +- (2 << 16) /* osd_vsc_bot_rpt_p0_num0 */ | +- BIT(23) /* osd_prog_interlace */ | +- BIT(24), /* Enable vertical scaler */ +- priv->io_base + _REG(VPP_OSD_VSC_CTRL0)); +-} +- +-void meson_vpp_disable_interlace_vscaler_osd1(struct meson_drm *priv) +-{ +- 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)); +-} +- + static unsigned int vpp_filter_coefs_4point_bspline[] = { + 0x15561500, 0x14561600, 0x13561700, 0x12561800, + 0x11551a00, 0x11541b00, 0x10541c00, 0x0f541d00, diff --git a/testing/linux-amlogic/0024-drm-meson-Add-primary-plane-scaling.patch b/testing/linux-amlogic/0024-drm-meson-Add-primary-plane-scaling.patch new file mode 100644 index 0000000000..472355a105 --- /dev/null +++ b/testing/linux-amlogic/0024-drm-meson-Add-primary-plane-scaling.patch @@ -0,0 +1,285 @@ +From 7489078416fdb581625f99ec87b828c2e22794ee Mon Sep 17 00:00:00 2001 +From: Neil Armstrong <narmstrong@baylibre.com> +Date: Tue, 30 Oct 2018 14:29:10 +0100 +Subject: [PATCH] drm/meson: Add primary plane scaling + +This patch adds support for the Primary Plane scaling. + +On the Amlogic GX SoCs, the primary plane is used as On-Screen-Display +layer on top of video, and it's needed to keep the OSD layer to a lower +size as the physical display size to : +- lower the memory bandwidth +- lower the OSD rendering +- lower the memory usage + +This use-case is used when setting the display mode to 3840x2160 and the +OSD layer is rendered using the GPU. In this case, the GXBB & GXL cannot +work on more than 2000x2000 buffer, thus needing the OSD layer to be kept +at 1920x1080 and upscaled to 3840x2160 in hardware. + +The primary plane atomic check still allow 1:1 scaling, allowing native +3840x2160 if needed by user-space applications. + +--- + drivers/gpu/drm/meson/meson_plane.c | 186 +++++++++++++++++++++++++++--------- + 1 file changed, 141 insertions(+), 45 deletions(-) + +diff --git a/drivers/gpu/drm/meson/meson_plane.c b/drivers/gpu/drm/meson/meson_plane.c +index f915a79..12a47b4 100644 +--- a/drivers/gpu/drm/meson/meson_plane.c ++++ b/drivers/gpu/drm/meson/meson_plane.c +@@ -24,6 +24,7 @@ + #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> +@@ -39,12 +40,50 @@ + #include "meson_canvas.h" + #include "meson_registers.h" + ++/* OSD_SCI_WH_M1 */ ++#define SCI_WH_M1_W(w) FIELD_PREP(GENMASK(28, 16), w) ++#define SCI_WH_M1_H(h) FIELD_PREP(GENMASK(12, 0), h) ++ ++/* OSD_SCO_H_START_END */ ++/* OSD_SCO_V_START_END */ ++#define SCO_HV_START(start) FIELD_PREP(GENMASK(27, 16), start) ++#define SCO_HV_END(end) FIELD_PREP(GENMASK(11, 0), end) ++ ++/* OSD_SC_CTRL0 */ ++#define SC_CTRL0_PATH_EN BIT(3) ++#define SC_CTRL0_SEL_OSD1 BIT(2) ++ ++/* OSD_VSC_CTRL0 */ ++#define VSC_BANK_LEN(value) FIELD_PREP(GENMASK(2, 0), value) ++#define VSC_TOP_INI_RCV_NUM(value) FIELD_PREP(GENMASK(6, 3), value) ++#define VSC_TOP_RPT_L0_NUM(value) FIELD_PREP(GENMASK(9, 8), value) ++#define VSC_BOT_INI_RCV_NUM(value) FIELD_PREP(GENMASK(14, 11), value) ++#define VSC_BOT_RPT_L0_NUM(value) FIELD_PREP(GENMASK(17, 16), value) ++#define VSC_PROG_INTERLACE BIT(23) ++#define VSC_VERTICAL_SCALER_EN BIT(24) ++ ++/* OSD_VSC_INI_PHASE */ ++#define VSC_INI_PHASE_BOT(bottom) FIELD_PREP(GENMASK(31, 16), bottom) ++#define VSC_INI_PHASE_TOP(top) FIELD_PREP(GENMASK(15, 0), top) ++ ++/* OSD_HSC_CTRL0 */ ++#define HSC_BANK_LENGTH(value) FIELD_PREP(GENMASK(2, 0), value) ++#define HSC_INI_RCV_NUM0(value) FIELD_PREP(GENMASK(6, 3), value) ++#define HSC_RPT_P0_NUM0(value) FIELD_PREP(GENMASK(9, 8), value) ++#define HSC_HORIZ_SCALER_EN BIT(22) ++ ++/* VPP_OSD_VSC_PHASE_STEP */ ++/* VPP_OSD_HSC_PHASE_STEP */ ++#define SC_PHASE_STEP(value) FIELD_PREP(GENMASK(27, 0), value) ++ + struct meson_plane { + struct drm_plane base; + struct meson_drm *priv; + }; + #define to_meson_plane(x) container_of(x, struct meson_plane, base) + ++#define FRAC_16_16(mult, div) (((mult) << 16) / (div)) ++ + static int meson_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) + { +@@ -57,10 +96,15 @@ static int meson_plane_atomic_check(struct drm_plane *plane, + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); + ++ /* ++ * Only allow : ++ * - Upscaling up to 5x, vertical and horizontal ++ * - Final coordinates must match crtc size ++ */ + return drm_atomic_helper_check_plane_state(state, crtc_state, ++ FRAC_16_16(1, 5), + DRM_PLANE_HELPER_NO_SCALING, +- DRM_PLANE_HELPER_NO_SCALING, +- true, true); ++ false, true); + } + + /* Takes a fixed 16.16 number and converts it to integer. */ +@@ -74,22 +118,19 @@ static void meson_plane_atomic_update(struct drm_plane *plane, + { + struct meson_plane *meson_plane = to_meson_plane(plane); + struct drm_plane_state *state = plane->state; +- struct drm_framebuffer *fb = state->fb; ++ struct drm_rect dest = drm_plane_state_dest(state); + struct meson_drm *priv = meson_plane->priv; ++ struct drm_framebuffer *fb = state->fb; + struct drm_gem_cma_object *gem; +- struct drm_rect src = { +- .x1 = (state->src_x), +- .y1 = (state->src_y), +- .x2 = (state->src_x + state->src_w), +- .y2 = (state->src_y + state->src_h), +- }; +- struct drm_rect dest = { +- .x1 = state->crtc_x, +- .y1 = state->crtc_y, +- .x2 = state->crtc_x + state->crtc_w, +- .y2 = state->crtc_y + state->crtc_h, +- }; + unsigned long flags; ++ int vsc_ini_rcv_num, vsc_ini_rpt_p0_num; ++ int vsc_bot_rcv_num, vsc_bot_rpt_p0_num; ++ int hsc_ini_rcv_num, hsc_ini_rpt_p0_num; ++ int hf_phase_step, vf_phase_step; ++ int src_w, src_h, dst_w, dst_h; ++ int bot_ini_phase; ++ int hf_bank_len; ++ int vf_bank_len; + u8 canvas_id_osd1; + + /* +@@ -143,6 +184,27 @@ static void meson_plane_atomic_update(struct drm_plane *plane, + break; + }; + ++ /* Default scaler parameters */ ++ vsc_bot_rcv_num = 0; ++ vsc_bot_rpt_p0_num = 0; ++ hf_bank_len = 4; ++ vf_bank_len = 4; ++ ++ if (state->crtc->mode.flags & DRM_MODE_FLAG_INTERLACE) { ++ vsc_bot_rcv_num = 6; ++ vsc_bot_rpt_p0_num = 2; ++ } ++ ++ hsc_ini_rcv_num = hf_bank_len; ++ vsc_ini_rcv_num = vf_bank_len; ++ hsc_ini_rpt_p0_num = (hf_bank_len / 2) - 1; ++ vsc_ini_rpt_p0_num = (vf_bank_len / 2) - 1; ++ ++ src_w = fixed16_to_int(state->src_w); ++ src_h = fixed16_to_int(state->src_h); ++ dst_w = state->crtc_w; ++ dst_h = state->crtc_h; ++ + /* + * When the output is interlaced, the OSD must switch between + * each field using the INTERLACE_SEL_ODD (0) of VIU_OSD1_BLK0_CFG_W0 +@@ -151,41 +213,73 @@ static void meson_plane_atomic_update(struct drm_plane *plane, + * is configured for 2:1 scaling with interlace options enabled. + */ + if (state->crtc->mode.flags & DRM_MODE_FLAG_INTERLACE) { +- priv->viu.osd1_interlace = true; +- + dest.y1 /= 2; + dest.y2 /= 2; ++ dst_h /= 2; ++ } + +- priv->viu.osd_sc_ctrl0 = BIT(3)| /* Enable scaler */ +- BIT(2); /* Select OSD1 */ ++ hf_phase_step = ((src_w << 18) / dst_w) << 6; ++ vf_phase_step = (src_h << 20) / dst_h; + +- /* 2:1 scaling */ +- priv->viu.osd_sc_i_wh_m1 = ((drm_rect_width(&dest) - 1) << 16) | +- (drm_rect_height(&dest) - 1); +- priv->viu.osd_sc_o_h_start_end = (dest.x1 << 16) | dest.x2; +- priv->viu.osd_sc_o_v_start_end = (dest.y1 << 16) | dest.y2; ++ if (state->crtc->mode.flags & DRM_MODE_FLAG_INTERLACE) ++ bot_ini_phase = ((vf_phase_step / 2) >> 4); ++ else ++ bot_ini_phase = 0; ++ ++ vf_phase_step = (vf_phase_step << 4); ++ ++ /* In interlaced mode, scaler is always active */ ++ if (src_h != dst_h || src_w != dst_w) { ++ priv->viu.osd_sc_i_wh_m1 = SCI_WH_M1_W(src_w - 1) | ++ SCI_WH_M1_H(src_h - 1); ++ priv->viu.osd_sc_o_h_start_end = SCO_HV_START(dest.x1) | ++ SCO_HV_END(dest.x2 - 1); ++ priv->viu.osd_sc_o_v_start_end = SCO_HV_START(dest.y1) | ++ SCO_HV_END(dest.y2 - 1); ++ /* Enable OSD Scaler */ ++ priv->viu.osd_sc_ctrl0 = SC_CTRL0_PATH_EN | SC_CTRL0_SEL_OSD1; ++ } else { ++ priv->viu.osd_sc_i_wh_m1 = 0; ++ priv->viu.osd_sc_o_h_start_end = 0; ++ priv->viu.osd_sc_o_v_start_end = 0; ++ priv->viu.osd_sc_ctrl0 = 0; ++ } + +- /* 2:1 vertical scaling values */ +- priv->viu.osd_sc_v_ini_phase = BIT(16); +- priv->viu.osd_sc_v_phase_step = BIT(25); ++ /* In interlaced mode, vertical scaler is always active */ ++ if (src_h != dst_h) { + priv->viu.osd_sc_v_ctrl0 = +- (4 << 0) | /* osd_vsc_bank_length */ +- (4 << 3) | /* osd_vsc_top_ini_rcv_num0 */ +- (1 << 8) | /* osd_vsc_top_rpt_p0_num0 */ +- (6 << 11) | /* osd_vsc_bot_ini_rcv_num0 */ +- (2 << 16) | /* osd_vsc_bot_rpt_p0_num0 */ +- BIT(23) | /* osd_prog_interlace */ +- BIT(24); /* Enable vertical scaler */ +- +- /* No horizontal scaling */ ++ VSC_BANK_LEN(vf_bank_len) | ++ VSC_TOP_INI_RCV_NUM(vsc_ini_rcv_num) | ++ VSC_TOP_RPT_L0_NUM(vsc_ini_rpt_p0_num) | ++ VSC_VERTICAL_SCALER_EN; ++ ++ if (state->crtc->mode.flags & DRM_MODE_FLAG_INTERLACE) ++ priv->viu.osd_sc_v_ctrl0 |= ++ VSC_BOT_INI_RCV_NUM(vsc_bot_rcv_num) | ++ VSC_BOT_RPT_L0_NUM(vsc_bot_rpt_p0_num) | ++ VSC_PROG_INTERLACE; ++ ++ priv->viu.osd_sc_v_phase_step = SC_PHASE_STEP(vf_phase_step); ++ priv->viu.osd_sc_v_ini_phase = VSC_INI_PHASE_BOT(bot_ini_phase); ++ } else { ++ priv->viu.osd_sc_v_ctrl0 = 0; ++ priv->viu.osd_sc_v_phase_step = 0; ++ priv->viu.osd_sc_v_ini_phase = 0; ++ } ++ ++ /* Horizontal scaler is only used if width does not match */ ++ if (src_w != dst_w) { ++ priv->viu.osd_sc_h_ctrl0 = ++ HSC_BANK_LENGTH(hf_bank_len) | ++ HSC_INI_RCV_NUM0(hsc_ini_rcv_num) | ++ HSC_RPT_P0_NUM0(hsc_ini_rpt_p0_num) | ++ HSC_HORIZ_SCALER_EN; ++ priv->viu.osd_sc_h_phase_step = SC_PHASE_STEP(hf_phase_step); + priv->viu.osd_sc_h_ini_phase = 0; +- priv->viu.osd_sc_h_phase_step = 0; +- priv->viu.osd_sc_h_ctrl0 = 0; + } else { +- priv->viu.osd1_interlace = false; +- priv->viu.osd_sc_ctrl0 = 0; + priv->viu.osd_sc_h_ctrl0 = 0; +- priv->viu.osd_sc_v_ctrl0 = 0; ++ priv->viu.osd_sc_h_phase_step = 0; ++ priv->viu.osd_sc_h_ini_phase = 0; + } + + /* +@@ -193,10 +287,12 @@ static void meson_plane_atomic_update(struct drm_plane *plane, + * where x2 is exclusive. + * e.g. +30x1920 would be (1919 << 16) | 30 + */ +- priv->viu.osd1_blk0_cfg[1] = ((fixed16_to_int(src.x2) - 1) << 16) | +- fixed16_to_int(src.x1); +- priv->viu.osd1_blk0_cfg[2] = ((fixed16_to_int(src.y2) - 1) << 16) | +- fixed16_to_int(src.y1); ++ priv->viu.osd1_blk0_cfg[1] = ++ ((fixed16_to_int(state->src.x2) - 1) << 16) | ++ fixed16_to_int(state->src.x1); ++ priv->viu.osd1_blk0_cfg[2] = ++ ((fixed16_to_int(state->src.y2) - 1) << 16) | ++ fixed16_to_int(state->src.y1); + priv->viu.osd1_blk0_cfg[3] = ((dest.x2 - 1) << 16) | dest.x1; + priv->viu.osd1_blk0_cfg[4] = ((dest.y2 - 1) << 16) | dest.y1; + 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 deleted file mode 100644 index dc8fdcb181..0000000000 --- a/testing/linux-amlogic/0024-drm-meson-convert-to-the-new-canvas-module.patch +++ /dev/null @@ -1,399 +0,0 @@ -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/0026-media-meson-add-v4l2-m2m-video-decoder-driver.patch b/testing/linux-amlogic/0026-media-meson-add-v4l2-m2m-video-decoder-driver.patch deleted file mode 100644 index 5967c19a07..0000000000 --- a/testing/linux-amlogic/0026-media-meson-add-v4l2-m2m-video-decoder-driver.patch +++ /dev/null @@ -1,5906 +0,0 @@ -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/0026-pinctrl-meson-gxl-remove-invalid-GPIOX-tsin_a-pins.patch b/testing/linux-amlogic/0026-pinctrl-meson-gxl-remove-invalid-GPIOX-tsin_a-pins.patch new file mode 100644 index 0000000000..d07847edac --- /dev/null +++ b/testing/linux-amlogic/0026-pinctrl-meson-gxl-remove-invalid-GPIOX-tsin_a-pins.patch @@ -0,0 +1,55 @@ +From 90af79faeb239f3a32974d99a2b58d4ce37cd0c1 Mon Sep 17 00:00:00 2001 +From: Neil Armstrong <narmstrong@baylibre.com> +Date: Wed, 7 Nov 2018 11:34:47 +0100 +Subject: [PATCH] pinctrl: meson-gxl: remove invalid GPIOX tsin_a pins + +The GPIOX tsin_a pins wrongly uses the SDCard pinctrl bits, this +patch completely removes these pins entries until we find out what +are the correct bits and registers to be used instead. + +Fixes: 5a6ae9b80139 ("pinctrl: meson-gxl: add tsin_a pins") + +--- + drivers/pinctrl/meson/pinctrl-meson-gxl.c | 12 ++---------- + 1 file changed, 2 insertions(+), 10 deletions(-) + +diff --git a/drivers/pinctrl/meson/pinctrl-meson-gxl.c b/drivers/pinctrl/meson/pinctrl-meson-gxl.c +index 158f618..0c0a501 100644 +--- a/drivers/pinctrl/meson/pinctrl-meson-gxl.c ++++ b/drivers/pinctrl/meson/pinctrl-meson-gxl.c +@@ -239,13 +239,9 @@ static const unsigned int eth_link_led_pins[] = { GPIOZ_14 }; + static const unsigned int eth_act_led_pins[] = { GPIOZ_15 }; + + static const unsigned int tsin_a_d0_pins[] = { GPIODV_0 }; +-static const unsigned int tsin_a_d0_x_pins[] = { GPIOX_10 }; + static const unsigned int tsin_a_clk_pins[] = { GPIODV_8 }; +-static const unsigned int tsin_a_clk_x_pins[] = { GPIOX_11 }; + static const unsigned int tsin_a_sop_pins[] = { GPIODV_9 }; +-static const unsigned int tsin_a_sop_x_pins[] = { GPIOX_8 }; + static const unsigned int tsin_a_d_valid_pins[] = { GPIODV_10 }; +-static const unsigned int tsin_a_d_valid_x_pins[] = { GPIOX_9 }; + static const unsigned int tsin_a_fail_pins[] = { GPIODV_11 }; + static const unsigned int tsin_a_dp_pins[] = { + GPIODV_1, GPIODV_2, GPIODV_3, GPIODV_4, GPIODV_5, GPIODV_6, GPIODV_7, +@@ -432,10 +428,6 @@ static struct meson_pmx_group meson_gxl_periphs_groups[] = { + GROUP(spi_miso, 5, 2), + GROUP(spi_ss0, 5, 1), + GROUP(spi_sclk, 5, 0), +- GROUP(tsin_a_sop_x, 6, 3), +- GROUP(tsin_a_d_valid_x, 6, 2), +- GROUP(tsin_a_d0_x, 6, 1), +- GROUP(tsin_a_clk_x, 6, 0), + + /* Bank Z */ + GROUP(eth_mdio, 4, 23), +@@ -698,8 +690,8 @@ static const char * const eth_led_groups[] = { + }; + + static const char * const tsin_a_groups[] = { +- "tsin_a_clk", "tsin_a_clk_x", "tsin_a_sop", "tsin_a_sop_x", +- "tsin_a_d_valid", "tsin_a_d_valid_x", "tsin_a_d0", "tsin_a_d0_x", ++ "tsin_a_clk", "tsin_a_sop", ++ "tsin_a_d_valid", "tsin_a_d0", + "tsin_a_dp", "tsin_a_fail", + }; + 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 deleted file mode 100644 index b5cfc9c3bf..0000000000 --- a/testing/linux-amlogic/0027-ARM64-dts-meson-gx-add-vdec-entry.patch +++ /dev/null @@ -1,35 +0,0 @@ -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/0027-arm64-dts-meson-gx-Add-hdmi_5v-regulator-as-hdmi-tx-.patch b/testing/linux-amlogic/0027-arm64-dts-meson-gx-Add-hdmi_5v-regulator-as-hdmi-tx-.patch new file mode 100644 index 0000000000..f1ee60638e --- /dev/null +++ b/testing/linux-amlogic/0027-arm64-dts-meson-gx-Add-hdmi_5v-regulator-as-hdmi-tx-.patch @@ -0,0 +1,79 @@ +From 6c4496753e3bea38dd4b42266c8402d048bde635 Mon Sep 17 00:00:00 2001 +From: Neil Armstrong <narmstrong@baylibre.com> +Date: Thu, 8 Nov 2018 14:24:38 +0100 +Subject: [PATCH] arm64: dts: meson-gx: Add hdmi_5v regulator as hdmi tx supply + +The hdmi_5v regulator must be enabled to provide power to the physical HDMI +PHY and enables the HDMI 5V presence loopback for the monitor. + +Fixes: b409f625a6d5 ("ARM64: dts: meson-gx: Add HDMI_5V regulator on selected boards") +Signed-off-by: Neil Armstrong <narmstrong@baylibre.com> + +--- + arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi | 1 + + arch/arm64/boot/dts/amlogic/meson-gxl-s905x-khadas-vim.dts | 1 + + arch/arm64/boot/dts/amlogic/meson-gxl-s905x-libretech-cc.dts | 1 + + arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dts | 1 + + arch/arm64/boot/dts/amlogic/meson-gxm-khadas-vim2.dts | 1 + + 5 files changed, 5 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 fb9ad6f..774f8af 100644 +--- a/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi ++++ b/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi +@@ -166,6 +166,7 @@ + status = "okay"; + pinctrl-0 = <&hdmi_hpd_pins>, <&hdmi_i2c_pins>; + pinctrl-names = "default"; ++ hdmi-supply = <&hdmi_5v>; + }; + + &hdmi_tx_tmds_port { +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 f053595..e5ef9b0 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 +@@ -119,6 +119,7 @@ + status = "okay"; + pinctrl-0 = <&hdmi_hpd_pins>, <&hdmi_i2c_pins>; + pinctrl-names = "default"; ++ hdmi-supply = <&hdmi_5v>; + }; + + &hdmi_tx_tmds_port { +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..ca0228e 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 +@@ -200,6 +200,7 @@ + status = "okay"; + pinctrl-0 = <&hdmi_hpd_pins>, <&hdmi_i2c_pins>; + pinctrl-names = "default"; ++ hdmi-supply = <&hdmi_5v>; + }; + + &hdmi_tx_tmds_port { +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 f8c66a7..29c9837 100644 +--- a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dts ++++ b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dts +@@ -96,6 +96,7 @@ + status = "okay"; + pinctrl-0 = <&hdmi_hpd_pins>, <&hdmi_i2c_pins>; + pinctrl-names = "default"; ++ hdmi-supply = <&hdmi_5v>; + }; + + &hdmi_tx_tmds_port { +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 4fbfa5a..fe8e726 100644 +--- a/arch/arm64/boot/dts/amlogic/meson-gxm-khadas-vim2.dts ++++ b/arch/arm64/boot/dts/amlogic/meson-gxm-khadas-vim2.dts +@@ -312,6 +312,7 @@ + status = "okay"; + pinctrl-0 = <&hdmi_hpd_pins>, <&hdmi_i2c_pins>; + pinctrl-names = "default"; ++ hdmi-supply = <&hdmi_5v>; + }; + + &hdmi_tx_tmds_port { diff --git a/testing/linux-amlogic/0028-arm64-dts-meson-gxl-libretech-cc-fix-GPIO-lines-name.patch b/testing/linux-amlogic/0028-arm64-dts-meson-gxl-libretech-cc-fix-GPIO-lines-name.patch new file mode 100644 index 0000000000..ca8d27efb3 --- /dev/null +++ b/testing/linux-amlogic/0028-arm64-dts-meson-gxl-libretech-cc-fix-GPIO-lines-name.patch @@ -0,0 +1,38 @@ +From a0d84363005399bb41b3eee556aa813a129bbcdf Mon Sep 17 00:00:00 2001 +From: Neil Armstrong <narmstrong@baylibre.com> +Date: Wed, 7 Nov 2018 11:45:47 +0100 +Subject: [PATCH] arm64: dts: meson-gxl-libretech-cc: fix GPIO lines names + +The gpio line names were set in the pinctrl node instead of the gpio node, +at the time it was merged, it worked, but was obviously wrong. +This patch moves the properties to the gpio nodes. + +Fixes: 47884c5c746e ("ARM64: dts: meson-gxl-libretech-cc: Add GPIO lines names") +Signed-off-by: Neil Armstrong <narmstrong@baylibre.com> + +--- + arch/arm64/boot/dts/amlogic/meson-gxl-s905x-libretech-cc.dts | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +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 ca0228e..bb2a8c7 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 +@@ -209,7 +209,7 @@ + }; + }; + +-&pinctrl_aobus { ++&gpio_ao { + gpio-line-names = "UART TX", + "UART RX", + "Blue LED", +@@ -224,7 +224,7 @@ + "7J1 Header Pin15"; + }; + +-&pinctrl_periphs { ++&gpio { + gpio-line-names = /* Bank GPIOZ */ + "", "", "", "", "", "", "", + "", "", "", "", "", "", "", diff --git a/testing/linux-amlogic/0029-arm64-dts-meson-gxbb-nanopi-k2-fix-GPIO-lines-names.patch b/testing/linux-amlogic/0029-arm64-dts-meson-gxbb-nanopi-k2-fix-GPIO-lines-names.patch new file mode 100644 index 0000000000..4ee05f01f1 --- /dev/null +++ b/testing/linux-amlogic/0029-arm64-dts-meson-gxbb-nanopi-k2-fix-GPIO-lines-names.patch @@ -0,0 +1,38 @@ +From 29cf4c3c228fd4cea4f46db9fa5e9386b3e794e6 Mon Sep 17 00:00:00 2001 +From: Neil Armstrong <narmstrong@baylibre.com> +Date: Wed, 7 Nov 2018 11:45:48 +0100 +Subject: [PATCH] arm64: dts: meson-gxbb-nanopi-k2: fix GPIO lines names + +The gpio line names were set in the pinctrl node instead of the gpio node, +at the time it was merged, it worked, but was obviously wrong. +This patch moves the properties to the gpio nodes. + +Fixes: 12ada0513d7a ("ARM64: dts: meson-gxbb-nanopi-k2: Add GPIO lines names") +Signed-off-by: Neil Armstrong <narmstrong@baylibre.com> + +--- + arch/arm64/boot/dts/amlogic/meson-gxbb-nanopi-k2.dts | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +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 5b10de9..8ea5ed5 100644 +--- a/arch/arm64/boot/dts/amlogic/meson-gxbb-nanopi-k2.dts ++++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-nanopi-k2.dts +@@ -236,7 +236,7 @@ + pinctrl-names = "default"; + }; + +-&pinctrl_aobus { ++&gpio_ao { + gpio-line-names = "UART TX", "UART RX", "Power Control", "Power Key In", + "VCCK En", "CON1 Header Pin31", + "I2S Header Pin6", "IR In", "I2S Header Pin7", +@@ -246,7 +246,7 @@ + ""; + }; + +-&pinctrl_periphs { ++&gpio { + gpio-line-names = /* Bank GPIOZ */ + "Eth MDIO", "Eth MDC", "Eth RGMII RX Clk", + "Eth RX DV", "Eth RX D0", "Eth RX D1", "Eth RX D2", diff --git a/testing/linux-amlogic/0030-arm64-dts-meson-gxbb-odroidc2-fix-GPIO-lines-names.patch b/testing/linux-amlogic/0030-arm64-dts-meson-gxbb-odroidc2-fix-GPIO-lines-names.patch new file mode 100644 index 0000000000..57e37258f5 --- /dev/null +++ b/testing/linux-amlogic/0030-arm64-dts-meson-gxbb-odroidc2-fix-GPIO-lines-names.patch @@ -0,0 +1,38 @@ +From fe156961d17219329389eee52306a731b8151b49 Mon Sep 17 00:00:00 2001 +From: Neil Armstrong <narmstrong@baylibre.com> +Date: Wed, 7 Nov 2018 11:45:49 +0100 +Subject: [PATCH] arm64: dts: meson-gxbb-odroidc2: fix GPIO lines names + +The gpio line names were set in the pinctrl node instead of the gpio node, +at the time it was merged, it worked, but was obviously wrong. +This patch moves the properties to the gpio nodes. + +Fixes: b03c7d6438bb ("ARM64: dts: meson-gxbb-odroidc2: Add GPIO lines names") +Signed-off-by: Neil Armstrong <narmstrong@baylibre.com> + +--- + arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts b/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts +index 3da3309..73cc801 100644 +--- a/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts ++++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts +@@ -232,7 +232,7 @@ + pinctrl-names = "default"; + }; + +-&pinctrl_aobus { ++&gpio_ao { + gpio-line-names = "UART TX", "UART RX", "VCCK En", "TF 3V3/1V8 En", + "USB HUB nRESET", "USB OTG Power En", + "J7 Header Pin2", "IR In", "J7 Header Pin4", +@@ -242,7 +242,7 @@ + ""; + }; + +-&pinctrl_periphs { ++&gpio { + gpio-line-names = /* Bank GPIOZ */ + "Eth MDIO", "Eth MDC", "Eth RGMII RX Clk", + "Eth RX DV", "Eth RX D0", "Eth RX D1", "Eth RX D2", diff --git a/testing/linux-amlogic/0031-arm64-dts-meson-gxl-khadas-vim-fix-GPIO-lines-names.patch b/testing/linux-amlogic/0031-arm64-dts-meson-gxl-khadas-vim-fix-GPIO-lines-names.patch new file mode 100644 index 0000000000..3e686149e6 --- /dev/null +++ b/testing/linux-amlogic/0031-arm64-dts-meson-gxl-khadas-vim-fix-GPIO-lines-names.patch @@ -0,0 +1,38 @@ +From 06df453970ccfbd1affb8ecd6ea50808fa132774 Mon Sep 17 00:00:00 2001 +From: Neil Armstrong <narmstrong@baylibre.com> +Date: Wed, 7 Nov 2018 11:45:50 +0100 +Subject: [PATCH] arm64: dts: meson-gxl-khadas-vim: fix GPIO lines names + +The gpio line names were set in the pinctrl node instead of the gpio node, +at the time it was merged, it worked, but was obviously wrong. +This patch moves the properties to the gpio nodes. + +Fixes: 60795933b709 ("ARM64: dts: meson-gxl-khadas-vim: Add GPIO lines names") +Signed-off-by: Neil Armstrong <narmstrong@baylibre.com> + +--- + arch/arm64/boot/dts/amlogic/meson-gxl-s905x-khadas-vim.dts | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +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 e5ef9b0..1a4b3f3 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 +@@ -158,7 +158,7 @@ + linux,rc-map-name = "rc-geekbox"; + }; + +-&pinctrl_aobus { ++&gpio_ao { + gpio-line-names = "UART TX", + "UART RX", + "Power Key In", +@@ -173,7 +173,7 @@ + ""; + }; + +-&pinctrl_periphs { ++&gpio { + gpio-line-names = /* Bank GPIOZ */ + "", "", "", "", "", "", "", + "", "", "", "", "", "", "", diff --git a/testing/linux-amlogic/0032-drm-meson-Add-support-for-VIC-alternate-timings.patch b/testing/linux-amlogic/0032-drm-meson-Add-support-for-VIC-alternate-timings.patch new file mode 100644 index 0000000000..42673f4bc2 --- /dev/null +++ b/testing/linux-amlogic/0032-drm-meson-Add-support-for-VIC-alternate-timings.patch @@ -0,0 +1,328 @@ +From 96c3cc9c6943cb27ee519cafe17cb563c3fcc28d Mon Sep 17 00:00:00 2001 +From: Neil Armstrong <narmstrong@baylibre.com> +Date: Tue, 6 Nov 2018 11:54:35 +0100 +Subject: [PATCH] drm/meson: Add support for VIC alternate timings + +This change is an attempt to handle the alternate clock for the CEA mode. +60Hz vs. 59.94Hz, 30Hz vs 29.97Hz or 24Hz vs 23.97Hz on the Amlogic Meson SoC +DRM Driver pixel clock generation. + +The actual clock generation will be moved to the Common Clock framework once +all the video clock are handled by the Amlogic Meson SoC clock driver, +then these alternate timings will be handled in the same time in a cleaner +fashion. + +Signed-off-by: Neil Armstrong <narmstrong@baylibre.com> + +--- + drivers/gpu/drm/meson/meson_dw_hdmi.c | 12 +--- + drivers/gpu/drm/meson/meson_vclk.c | 127 +++++++++++++++++++++++----------- + drivers/gpu/drm/meson/meson_vclk.h | 2 + + 3 files changed, 89 insertions(+), 52 deletions(-) + +diff --git a/drivers/gpu/drm/meson/meson_dw_hdmi.c b/drivers/gpu/drm/meson/meson_dw_hdmi.c +index df7247c..d8c5cc3 100644 +--- a/drivers/gpu/drm/meson/meson_dw_hdmi.c ++++ b/drivers/gpu/drm/meson/meson_dw_hdmi.c +@@ -594,17 +594,7 @@ 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 for VIC modes */ +- switch (vclk_freq) { +- case 54000: +- case 74250: +- case 148500: +- case 297000: +- case 594000: +- return MODE_OK; +- } +- +- return MODE_CLOCK_RANGE; ++ return meson_vclk_vic_supported_freq(vclk_freq); + } + + /* Encoder */ +diff --git a/drivers/gpu/drm/meson/meson_vclk.c b/drivers/gpu/drm/meson/meson_vclk.c +index ae54732..5accceb 100644 +--- a/drivers/gpu/drm/meson/meson_vclk.c ++++ b/drivers/gpu/drm/meson/meson_vclk.c +@@ -117,6 +117,8 @@ + #define HDMI_PLL_RESET BIT(28) + #define HDMI_PLL_LOCK BIT(31) + ++#define FREQ_1000_1001(_freq) DIV_ROUND_CLOSEST(_freq * 1000, 1001) ++ + /* VID PLL Dividers */ + enum { + VID_PLL_DIV_1 = 0, +@@ -323,7 +325,7 @@ static void meson_venci_cvbs_clock_config(struct meson_drm *priv) + enum { + /* PLL O1 O2 O3 VP DV EN TX */ + /* 4320 /4 /4 /1 /5 /1 => /2 /2 */ +- MESON_VCLK_HDMI_ENCI_54000 = 1, ++ MESON_VCLK_HDMI_ENCI_54000 = 0, + /* 4320 /4 /4 /1 /5 /1 => /1 /2 */ + MESON_VCLK_HDMI_DDR_54000, + /* 2970 /4 /1 /1 /5 /1 => /1 /2 */ +@@ -339,6 +341,7 @@ enum { + }; + + struct meson_vclk_params { ++ unsigned int pixel_freq; + unsigned int pll_base_freq; + unsigned int pll_od1; + unsigned int pll_od2; +@@ -347,6 +350,7 @@ struct meson_vclk_params { + unsigned int vclk_div; + } params[] = { + [MESON_VCLK_HDMI_ENCI_54000] = { ++ .pixel_freq = 54000, + .pll_base_freq = 4320000, + .pll_od1 = 4, + .pll_od2 = 4, +@@ -355,6 +359,7 @@ struct meson_vclk_params { + .vclk_div = 1, + }, + [MESON_VCLK_HDMI_DDR_54000] = { ++ .pixel_freq = 54000, + .pll_base_freq = 4320000, + .pll_od1 = 4, + .pll_od2 = 4, +@@ -363,6 +368,7 @@ struct meson_vclk_params { + .vclk_div = 1, + }, + [MESON_VCLK_HDMI_DDR_148500] = { ++ .pixel_freq = 148500, + .pll_base_freq = 2970000, + .pll_od1 = 4, + .pll_od2 = 1, +@@ -371,6 +377,7 @@ struct meson_vclk_params { + .vclk_div = 1, + }, + [MESON_VCLK_HDMI_74250] = { ++ .pixel_freq = 74250, + .pll_base_freq = 2970000, + .pll_od1 = 2, + .pll_od2 = 2, +@@ -379,6 +386,7 @@ struct meson_vclk_params { + .vclk_div = 1, + }, + [MESON_VCLK_HDMI_148500] = { ++ .pixel_freq = 148500, + .pll_base_freq = 2970000, + .pll_od1 = 1, + .pll_od2 = 2, +@@ -387,6 +395,7 @@ struct meson_vclk_params { + .vclk_div = 1, + }, + [MESON_VCLK_HDMI_297000] = { ++ .pixel_freq = 297000, + .pll_base_freq = 2970000, + .pll_od1 = 1, + .pll_od2 = 1, +@@ -395,6 +404,7 @@ struct meson_vclk_params { + .vclk_div = 2, + }, + [MESON_VCLK_HDMI_594000] = { ++ .pixel_freq = 594000, + .pll_base_freq = 5940000, + .pll_od1 = 1, + .pll_od2 = 1, +@@ -402,6 +412,7 @@ struct meson_vclk_params { + .vid_pll_div = VID_PLL_DIV_5, + .vclk_div = 1, + }, ++ { /* sentinel */ }, + }; + + static inline unsigned int pll_od_to_reg(unsigned int od) +@@ -626,12 +637,37 @@ static void meson_hdmi_pll_generic_set(struct meson_drm *priv, + pll_freq); + } + ++enum drm_mode_status ++meson_vclk_vic_supported_freq(unsigned int freq) ++{ ++ int i; ++ ++ DRM_DEBUG_DRIVER("freq = %d\n", freq); ++ ++ for (i = 0 ; params[i].pixel_freq ; ++i) { ++ DRM_DEBUG_DRIVER("i = %d pixel_freq = %d alt = %d\n", ++ i, params[i].pixel_freq, ++ FREQ_1000_1001(params[i].pixel_freq)); ++ /* Match strict frequency */ ++ if (freq == params[i].pixel_freq) ++ return MODE_OK; ++ /* Match 1000/1001 variant */ ++ if (freq == FREQ_1000_1001(params[i].pixel_freq)) ++ return MODE_OK; ++ } ++ ++ return MODE_CLOCK_RANGE; ++} ++EXPORT_SYMBOL_GPL(meson_vclk_vic_supported_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) ++ bool hdmi_use_enci, bool vic_alternate_clock) + { ++ unsigned int m, frac; ++ + /* Set HDMI-TX sys clock */ + regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL, + CTS_HDMI_SYS_SEL_MASK, 0); +@@ -646,34 +682,38 @@ static void meson_vclk_set(struct meson_drm *priv, unsigned int 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); ++ m = 0x3d; ++ frac = vic_alternate_clock ? 0xd02 : 0xe00; + break; + case 4320000: +- meson_hdmi_pll_set_params(priv, 0x5a, 0, +- od1, od2, od3); ++ m = vic_alternate_clock ? 0x59 : 0x5a; ++ frac = vic_alternate_clock ? 0xe8f : 0; + break; + case 5940000: +- meson_hdmi_pll_set_params(priv, 0x7b, 0xc00, +- od1, od2, od3); ++ m = 0x7b; ++ frac = vic_alternate_clock ? 0xa05 : 0xc00; + break; + } ++ ++ meson_hdmi_pll_set_params(priv, m, frac, od1, od2, od3); + } 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); ++ m = 0x7b; ++ frac = vic_alternate_clock ? 0x281 : 0x300; + break; + case 4320000: +- meson_hdmi_pll_set_params(priv, 0xb4, 0, +- od1, od2, od3); ++ m = vic_alternate_clock ? 0xb3 : 0xb4; ++ frac = vic_alternate_clock ? 0x347 : 0; + break; + case 5940000: +- meson_hdmi_pll_set_params(priv, 0xf7, 0x200, +- od1, od2, od3); ++ m = 0xf7; ++ frac = vic_alternate_clock ? 0x102 : 0x200; + break; + } ++ ++ meson_hdmi_pll_set_params(priv, m, frac, od1, od2, od3); + } + + /* Setup vid_pll divider */ +@@ -826,6 +866,7 @@ 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) + { ++ bool vic_alternate_clock = false; + unsigned int freq; + unsigned int hdmi_tx_div; + unsigned int venc_div; +@@ -843,7 +884,7 @@ void meson_vclk_setup(struct meson_drm *priv, unsigned int target, + * - encp encoder + */ + meson_vclk_set(priv, vclk_freq * 10, 0, 0, 0, +- VID_PLL_DIV_5, 2, 1, 1, false); ++ VID_PLL_DIV_5, 2, 1, 1, false, false); + return; + } + +@@ -863,31 +904,35 @@ void meson_vclk_setup(struct meson_drm *priv, unsigned int target, + 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); ++ for (freq = 0 ; params[freq].pixel_freq ; ++freq) { ++ if (vclk_freq == params[freq].pixel_freq || ++ vclk_freq == FREQ_1000_1001(params[freq].pixel_freq)) { ++ if (vclk_freq != params[freq].pixel_freq) ++ vic_alternate_clock = true; ++ else ++ vic_alternate_clock = false; ++ ++ if (freq == MESON_VCLK_HDMI_ENCI_54000 && ++ !hdmi_use_enci) ++ continue; ++ ++ if (freq == MESON_VCLK_HDMI_DDR_54000 && ++ hdmi_use_enci) ++ continue; ++ ++ if (freq == MESON_VCLK_HDMI_DDR_148500 && ++ dac_freq == vclk_freq) ++ continue; ++ ++ if (freq == MESON_VCLK_HDMI_148500 && ++ dac_freq != vclk_freq) ++ continue; ++ break; ++ } ++ } ++ ++ if (!params[freq].pixel_freq) { ++ pr_err("Fatal Error, invalid HDMI vclk freq %d\n", vclk_freq); + return; + } + +@@ -895,6 +940,6 @@ void meson_vclk_setup(struct meson_drm *priv, unsigned int target, + 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); ++ hdmi_use_enci, vic_alternate_clock); + } + 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 869fa3a..4bd8752 100644 +--- a/drivers/gpu/drm/meson/meson_vclk.h ++++ b/drivers/gpu/drm/meson/meson_vclk.h +@@ -32,6 +32,8 @@ enum { + + enum drm_mode_status + meson_vclk_dmt_supported_freq(struct meson_drm *priv, unsigned int freq); ++enum drm_mode_status ++meson_vclk_vic_supported_freq(unsigned int freq); + + void meson_vclk_setup(struct meson_drm *priv, unsigned int target, + unsigned int vclk_freq, unsigned int venc_freq, diff --git a/testing/linux-amlogic/0033-media-meson-add-v4l2-m2m-video-decoder-driver.patch b/testing/linux-amlogic/0033-media-meson-add-v4l2-m2m-video-decoder-driver.patch new file mode 100644 index 0000000000..3604a6e456 --- /dev/null +++ b/testing/linux-amlogic/0033-media-meson-add-v4l2-m2m-video-decoder-driver.patch @@ -0,0 +1,2969 @@ +From 2c28b1d1f1487bf4aecb36986c2d7b73eb8ac94d Mon Sep 17 00:00:00 2001 +From: Maxime Jourdan <mjourdan@baylibre.com> +Date: Wed, 29 Aug 2018 15:17:22 +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 +- MPEG 2 + +Supported SoCs are: GXBB (S905), GXL (S905X/W/D), GXM (S912) + +There is also a hardware bitstream parser (ESPARSER) that is handled here. + +Signed-off-by: Maxime Jourdan <mjourdan@baylibre.com> + +--- + drivers/media/platform/Kconfig | 10 + + drivers/media/platform/meson/Makefile | 1 + + drivers/media/platform/meson/vdec/Makefile | 8 + + drivers/media/platform/meson/vdec/codec_mpeg12.c | 209 +++++ + drivers/media/platform/meson/vdec/codec_mpeg12.h | 14 + + drivers/media/platform/meson/vdec/dos_regs.h | 98 ++ + drivers/media/platform/meson/vdec/esparser.c | 322 +++++++ + drivers/media/platform/meson/vdec/esparser.h | 32 + + drivers/media/platform/meson/vdec/vdec.c | 1034 +++++++++++++++++++++ + drivers/media/platform/meson/vdec/vdec.h | 251 +++++ + drivers/media/platform/meson/vdec/vdec_1.c | 231 +++++ + drivers/media/platform/meson/vdec/vdec_1.h | 14 + + drivers/media/platform/meson/vdec/vdec_helpers.c | 412 ++++++++ + drivers/media/platform/meson/vdec/vdec_helpers.h | 48 + + drivers/media/platform/meson/vdec/vdec_platform.c | 101 ++ + drivers/media/platform/meson/vdec/vdec_platform.h | 30 + + 16 files changed, 2815 insertions(+) + create mode 100644 drivers/media/platform/meson/vdec/Makefile + 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/dos_regs.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/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_helpers.c + create mode 100644 drivers/media/platform/meson/vdec/vdec_helpers.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 54fe90a..6bffb0c 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_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 ++ select MESON_CANVAS ++ help ++ Support for the video decoder found in gxbb/gxl/gxm chips. ++ + 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..f7c6e10 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_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..6bea129 +--- /dev/null ++++ b/drivers/media/platform/meson/vdec/Makefile +@@ -0,0 +1,8 @@ ++# SPDX-License-Identifier: GPL-2.0 ++# Makefile for Amlogic meson video decoder driver ++ ++meson-vdec-objs = esparser.o vdec.o vdec_helpers.o vdec_platform.o ++meson-vdec-objs += vdec_1.o ++meson-vdec-objs += codec_mpeg12.o ++ ++obj-$(CONFIG_VIDEO_MESON_VDEC) += meson-vdec.o +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..1bd6fb7 +--- /dev/null ++++ b/drivers/media/platform/meson/vdec/codec_mpeg12.c +@@ -0,0 +1,209 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Copyright (C) 2018 BayLibre, SAS ++ * Author: Maxime Jourdan <mjourdan@baylibre.com> ++ */ ++ ++#include <media/v4l2-mem2mem.h> ++#include <media/videobuf2-dma-contig.h> ++ ++#include "vdec_helpers.h" ++#include "dos_regs.h" ++ ++#define SIZE_WORKSPACE SZ_128K ++/* Offset substracted by the firmware from the workspace paddr */ ++#define WORKSPACE_OFFSET (5 * SZ_1K) ++ ++/* map firmware registers to known MPEG1/2 functions */ ++#define MREG_SEQ_INFO AV_SCRATCH_4 ++ #define MPEG2_SEQ_DAR_MASK GENMASK(3, 0) ++ #define MPEG2_DAR_4_3 2 ++ #define MPEG2_DAR_16_9 3 ++ #define MPEG2_DAR_221_100 4 ++#define MREG_PIC_INFO AV_SCRATCH_5 ++#define MREG_PIC_WIDTH AV_SCRATCH_6 ++#define MREG_PIC_HEIGHT AV_SCRATCH_7 ++#define MREG_BUFFERIN AV_SCRATCH_8 ++#define MREG_BUFFEROUT AV_SCRATCH_9 ++#define MREG_CMD AV_SCRATCH_A ++#define MREG_CO_MV_START AV_SCRATCH_B ++#define MREG_ERROR_COUNT AV_SCRATCH_C ++#define MREG_FRAME_OFFSET AV_SCRATCH_D ++#define MREG_WAIT_BUFFER AV_SCRATCH_E ++#define MREG_FATAL_ERROR AV_SCRATCH_F ++ ++#define PICINFO_PROG 0x00008000 ++#define PICINFO_TOP_FIRST 0x00002000 ++ ++struct codec_mpeg12 { ++ /* Buffer for the MPEG1/2 Workspace */ ++ void *workspace_vaddr; ++ dma_addr_t workspace_paddr; ++}; ++ ++static const u8 eos_sequence[SZ_1K] = { 0x00, 0x00, 0x01, 0xB7 }; ++ ++static const u8 *codec_mpeg12_eos_sequence(u32 *len) ++{ ++ *len = ARRAY_SIZE(eos_sequence); ++ return eos_sequence; ++} ++ ++static int codec_mpeg12_can_recycle(struct amvdec_core *core) ++{ ++ return !amvdec_read_dos(core, MREG_BUFFERIN); ++} ++ ++static void codec_mpeg12_recycle(struct amvdec_core *core, u32 buf_idx) ++{ ++ amvdec_write_dos(core, MREG_BUFFERIN, buf_idx + 1); ++} ++ ++static int codec_mpeg12_start(struct amvdec_session *sess) ++{ ++ struct amvdec_core *core = sess->core; ++ struct codec_mpeg12 *mpeg12 = sess->priv; ++ int ret; ++ ++ mpeg12 = kzalloc(sizeof(*mpeg12), GFP_KERNEL); ++ if (!mpeg12) ++ return -ENOMEM; ++ ++ /* 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; ++ } ++ ++ ret = amvdec_set_canvases(sess, (u32[]){ AV_SCRATCH_0, 0 }, ++ (u32[]){ 8, 0 }); ++ if (ret) ++ goto free_workspace; ++ ++ amvdec_write_dos(core, POWER_CTL_VLD, BIT(4)); ++ amvdec_write_dos(core, MREG_CO_MV_START, ++ mpeg12->workspace_paddr + WORKSPACE_OFFSET); ++ ++ amvdec_write_dos(core, MPEG1_2_REG, 0); ++ amvdec_write_dos(core, PSCALE_CTRL, 0); ++ amvdec_write_dos(core, PIC_HEAD_INFO, 0x380); ++ amvdec_write_dos(core, M4_CONTROL_REG, 0); ++ amvdec_write_dos(core, MREG_BUFFERIN, 0); ++ amvdec_write_dos(core, MREG_BUFFEROUT, 0); ++ amvdec_write_dos(core, MREG_CMD, (sess->width << 16) | sess->height); ++ amvdec_write_dos(core, MREG_ERROR_COUNT, 0); ++ amvdec_write_dos(core, MREG_FATAL_ERROR, 0); ++ amvdec_write_dos(core, MREG_WAIT_BUFFER, 0); ++ ++ sess->keyframe_found = 1; ++ sess->priv = mpeg12; ++ ++ return 0; ++ ++free_workspace: ++ dma_free_coherent(core->dev, SIZE_WORKSPACE, mpeg12->workspace_vaddr, ++ mpeg12->workspace_paddr); ++free_mpeg12: ++ kfree(mpeg12); ++ ++ return ret; ++} ++ ++static int codec_mpeg12_stop(struct amvdec_session *sess) ++{ ++ struct codec_mpeg12 *mpeg12 = sess->priv; ++ struct amvdec_core *core = sess->core; ++ ++ if (mpeg12->workspace_vaddr) ++ dma_free_coherent(core->dev, SIZE_WORKSPACE, ++ mpeg12->workspace_vaddr, ++ mpeg12->workspace_paddr); ++ ++ return 0; ++} ++ ++static void codec_mpeg12_update_dar(struct amvdec_session *sess) ++{ ++ struct amvdec_core *core = sess->core; ++ u32 seq = amvdec_read_dos(core, MREG_SEQ_INFO); ++ u32 ar = seq & MPEG2_SEQ_DAR_MASK; ++ ++ switch (ar) { ++ case MPEG2_DAR_4_3: ++ amvdec_set_par_from_dar(sess, 4, 3); ++ break; ++ case MPEG2_DAR_16_9: ++ amvdec_set_par_from_dar(sess, 16, 9); ++ break; ++ case MPEG2_DAR_221_100: ++ amvdec_set_par_from_dar(sess, 221, 100); ++ break; ++ default: ++ sess->pixelaspect.numerator = 1; ++ sess->pixelaspect.denominator = 1; ++ break; ++ }; ++} ++ ++static irqreturn_t codec_mpeg12_threaded_isr(struct amvdec_session *sess) ++{ ++ struct amvdec_core *core = sess->core; ++ u32 reg; ++ u32 pic_info; ++ u32 is_progressive; ++ u32 buffer_index; ++ u32 field = V4L2_FIELD_NONE; ++ u32 offset; ++ ++ amvdec_write_dos(core, ASSIST_MBOX1_CLR_REG, 1); ++ reg = amvdec_read_dos(core, MREG_FATAL_ERROR); ++ if (reg == 1) { ++ dev_err(core->dev, "MPEG1/2 fatal error\n"); ++ amvdec_abort(sess); ++ return IRQ_HANDLED; ++ } ++ ++ reg = amvdec_read_dos(core, MREG_BUFFEROUT); ++ if (!reg) ++ return IRQ_HANDLED; ++ ++ /* Unclear what this means */ ++ if ((reg & GENMASK(23, 17)) == GENMASK(23, 17)) ++ goto end; ++ ++ pic_info = amvdec_read_dos(core, MREG_PIC_INFO); ++ is_progressive = pic_info & PICINFO_PROG; ++ ++ if (!is_progressive) ++ field = (pic_info & PICINFO_TOP_FIRST) ? ++ V4L2_FIELD_INTERLACED_TB : ++ V4L2_FIELD_INTERLACED_BT; ++ ++ codec_mpeg12_update_dar(sess); ++ buffer_index = ((reg & 0xf) - 1) & 7; ++ offset = amvdec_read_dos(core, MREG_FRAME_OFFSET); ++ amvdec_dst_buf_done_idx(sess, buffer_index, offset, field); ++ ++end: ++ amvdec_write_dos(core, MREG_BUFFEROUT, 0); ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t codec_mpeg12_isr(struct amvdec_session *sess) ++{ ++ return IRQ_WAKE_THREAD; ++} ++ ++struct amvdec_codec_ops codec_mpeg12_ops = { ++ .start = codec_mpeg12_start, ++ .stop = codec_mpeg12_stop, ++ .isr = codec_mpeg12_isr, ++ .threaded_isr = codec_mpeg12_threaded_isr, ++ .can_recycle = codec_mpeg12_can_recycle, ++ .recycle = codec_mpeg12_recycle, ++ .eos_sequence = codec_mpeg12_eos_sequence, ++}; +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..43cab5f +--- /dev/null ++++ b/drivers/media/platform/meson/vdec/codec_mpeg12.h +@@ -0,0 +1,14 @@ ++/* SPDX-License-Identifier: GPL-2.0+ */ ++/* ++ * Copyright (C) 2018 BayLibre, SAS ++ * Author: Maxime Jourdan <mjourdan@baylibre.com> ++ */ ++ ++#ifndef __MESON_VDEC_CODEC_MPEG12_H_ ++#define __MESON_VDEC_CODEC_MPEG12_H_ ++ ++#include "vdec.h" ++ ++extern struct amvdec_codec_ops codec_mpeg12_ops; ++ ++#endif +diff --git a/drivers/media/platform/meson/vdec/dos_regs.h b/drivers/media/platform/meson/vdec/dos_regs.h +new file mode 100644 +index 0000000..abd8105 +--- /dev/null ++++ b/drivers/media/platform/meson/vdec/dos_regs.h +@@ -0,0 +1,98 @@ ++/* SPDX-License-Identifier: GPL-2.0+ */ ++/* ++ * Copyright (C) 2018 BayLibre, SAS ++ * Author: Maxime Jourdan <mjourdan@baylibre.com> ++ */ ++ ++#ifndef __MESON_VDEC_DOS_REGS_H_ ++#define __MESON_VDEC_DOS_REGS_H_ ++ ++/* DOS registers */ ++#define VDEC_ASSIST_AMR1_INT8 0x00b4 ++ ++#define ASSIST_MBOX1_CLR_REG 0x01d4 ++#define ASSIST_MBOX1_MASK 0x01d8 ++ ++#define MPSR 0x0c04 ++#define MCPU_INTR_MSK 0x0c10 ++#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 PSCALE_RST 0x2440 ++#define PSCALE_CTRL 0x2444 ++#define PSCALE_BMEM_ADDR 0x247c ++#define PSCALE_BMEM_DAT 0x2480 ++ ++#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 ANC0_CANVAS_ADDR 0x2640 ++#define MDEC_PIC_DC_THRESH 0x26e0 ++ ++/* Firmware interface registers */ ++#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_A 0x2728 ++#define AV_SCRATCH_B 0x272c ++#define AV_SCRATCH_C 0x2730 ++#define AV_SCRATCH_D 0x2734 ++#define AV_SCRATCH_E 0x2738 ++#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 AV_SCRATCH_K 0x2750 ++#define AV_SCRATCH_L 0x2754 ++ ++#define MPEG1_2_REG 0x3004 ++#define PIC_HEAD_INFO 0x300c ++#define POWER_CTL_VLD 0x3020 ++#define M4_CONTROL_REG 0x30a4 ++ ++/* Stream Buffer (stbuf) regs */ ++#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 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_MEM_PD_HEVC 0xfccc ++#define DOS_SW_RESET3 0xfcd0 ++#define DOS_GCLK_EN3 0xfcd4 ++#define DOS_VDEC_MCRCC_STALL_CTRL 0xfd00 ++ ++#endif +diff --git a/drivers/media/platform/meson/vdec/esparser.c b/drivers/media/platform/meson/vdec/esparser.c +new file mode 100644 +index 0000000..9498812 +--- /dev/null ++++ b/drivers/media/platform/meson/vdec/esparser.c +@@ -0,0 +1,322 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Copyright (C) 2018 BayLibre, SAS ++ * Author: Maxime Jourdan <mjourdan@baylibre.com> ++ * ++ * The Elementary Stream Parser is a HW bitstream parser. ++ * It reads bitstream buffers and feeds them to the VIFIFO ++ */ ++ ++#include <linux/init.h> ++#include <linux/ioctl.h> ++#include <linux/list.h> ++#include <linux/module.h> ++#include <linux/of_device.h> ++#include <linux/reset.h> ++#include <media/videobuf2-dma-contig.h> ++#include <media/v4l2-mem2mem.h> ++ ++#include "dos_regs.h" ++#include "esparser.h" ++#include "vdec_helpers.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_WP 0x88 ++#define PARSER_VIDEO_HOLE 0x90 ++ ++#define SEARCH_PATTERN_LEN 512 ++ ++static DECLARE_WAIT_QUEUE_HEAD(wq); ++static int search_done; ++ ++static irqreturn_t esparser_isr(int irq, void *dev) ++{ ++ int int_status; ++ struct amvdec_core *core = dev; ++ ++ int_status = amvdec_read_parser(core, PARSER_INT_STATUS); ++ amvdec_write_parser(core, PARSER_INT_STATUS, int_status); ++ ++ if (int_status & PARSER_INTSTAT_SC_FOUND) { ++ amvdec_write_parser(core, PFIFO_RD_PTR, 0); ++ amvdec_write_parser(core, PFIFO_WR_PTR, 0); ++ search_done = 1; ++ wake_up_interruptible(&wq); ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++/* Pad the packet to at least 4KiB bytes otherwise the VDEC unit won't trigger ++ * ISRs. ++ * Also append a start code 000001ff at the end to trigger ++ * the ESPARSER interrupt. ++ */ ++static u32 esparser_pad_start_code(struct vb2_buffer *vb) ++{ ++ u32 payload_size = vb2_get_plane_payload(vb, 0); ++ u32 pad_size = 0; ++ u8 *vaddr = vb2_plane_vaddr(vb, 0) + payload_size; ++ ++ if (payload_size < ESPARSER_MIN_PACKET_SIZE) { ++ pad_size = ESPARSER_MIN_PACKET_SIZE - payload_size; ++ memset(vaddr, 0, pad_size); ++ } ++ ++ memset(vaddr + pad_size, 0, SEARCH_PATTERN_LEN); ++ vaddr[pad_size] = 0x00; ++ vaddr[pad_size + 1] = 0x00; ++ vaddr[pad_size + 2] = 0x01; ++ vaddr[pad_size + 3] = 0xff; ++ ++ return pad_size; ++} ++ ++static int ++esparser_write_data(struct amvdec_core *core, dma_addr_t addr, u32 size) ++{ ++ amvdec_write_parser(core, PFIFO_RD_PTR, 0); ++ amvdec_write_parser(core, PFIFO_WR_PTR, 0); ++ amvdec_write_parser(core, PARSER_CONTROL, ++ ES_WRITE | ++ ES_PARSER_START | ++ ES_SEARCH | ++ (size << ES_PACK_SIZE_BIT)); ++ ++ amvdec_write_parser(core, PARSER_FETCH_ADDR, addr); ++ amvdec_write_parser(core, PARSER_FETCH_CMD, ++ (7 << FETCH_ENDIAN_BIT) | ++ (size + SEARCH_PATTERN_LEN)); ++ ++ search_done = 0; ++ return wait_event_interruptible_timeout(wq, search_done, (HZ / 5)); ++} ++ ++static u32 esparser_vififo_get_free_space(struct amvdec_session *sess) ++{ ++ u32 vififo_usage; ++ struct amvdec_ops *vdec_ops = sess->fmt_out->vdec_ops; ++ struct amvdec_core *core = sess->core; ++ ++ vififo_usage = vdec_ops->vififo_level(sess); ++ vififo_usage += amvdec_read_parser(core, PARSER_VIDEO_HOLE); ++ vififo_usage += (6 * SZ_1K); // 6 KiB internal fifo ++ ++ 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 amvdec_core *core, const u8 *data, u32 len) ++{ ++ struct device *dev = core->dev; ++ void *eos_vaddr; ++ dma_addr_t eos_paddr; ++ int ret; ++ ++ eos_vaddr = dma_alloc_coherent(dev, ++ len + SEARCH_PATTERN_LEN, ++ &eos_paddr, GFP_KERNEL); ++ if (!eos_vaddr) ++ return -ENOMEM; ++ ++ memset(eos_vaddr, 0, len + SEARCH_PATTERN_LEN); ++ memcpy(eos_vaddr, data, len); ++ ret = esparser_write_data(core, eos_paddr, len); ++ dma_free_coherent(dev, len + SEARCH_PATTERN_LEN, ++ eos_vaddr, eos_paddr); ++ ++ return ret; ++} ++ ++static u32 esparser_get_offset(struct amvdec_session *sess) ++{ ++ struct amvdec_core *core = sess->core; ++ u32 offset = amvdec_read_parser(core, PARSER_VIDEO_WP) - ++ sess->vififo_paddr; ++ ++ if (offset < sess->last_offset) ++ sess->wrap_count++; ++ ++ sess->last_offset = offset; ++ offset += (sess->wrap_count * sess->vififo_size); ++ ++ return offset; ++} ++ ++static int ++esparser_queue(struct amvdec_session *sess, struct vb2_v4l2_buffer *vbuf) ++{ ++ int ret; ++ struct vb2_buffer *vb = &vbuf->vb2_buf; ++ struct amvdec_core *core = sess->core; ++ struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops; ++ u32 num_dst_bufs = 0; ++ u32 payload_size = vb2_get_plane_payload(vb, 0); ++ dma_addr_t phy = vb2_dma_contig_plane_dma_addr(vb, 0); ++ u32 offset; ++ u32 pad_size; ++ ++ if (codec_ops->num_pending_bufs) ++ num_dst_bufs = codec_ops->num_pending_bufs(sess); ++ ++ num_dst_bufs += v4l2_m2m_num_dst_bufs_ready(sess->m2m_ctx); ++ ++ 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); ++ ++ offset = esparser_get_offset(sess); ++ ++ amvdec_add_ts_reorder(sess, vb->timestamp, offset); ++ dev_dbg(core->dev, "esparser: ts = %llu pld_size = %u offset = %08X\n", ++ vb->timestamp, payload_size, offset); ++ ++ pad_size = esparser_pad_start_code(vb); ++ ret = esparser_write_data(core, phy, payload_size + pad_size); ++ ++ if (ret <= 0) { ++ dev_warn(core->dev, "esparser: input parsing error\n"); ++ amvdec_remove_ts(sess, vb->timestamp); ++ v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR); ++ amvdec_write_parser(core, PARSER_FETCH_CMD, 0); ++ ++ return 0; ++ } ++ ++ /* We need to wait until we parse the first keyframe. ++ * All buffers prior to the first keyframe must be dropped. ++ */ ++ if (!sess->keyframe_found) ++ usleep_range(1000, 2000); ++ ++ if (sess->keyframe_found) ++ atomic_inc(&sess->esparser_queued_bufs); ++ else ++ amvdec_remove_ts(sess, vb->timestamp); ++ ++ vbuf->flags = 0; ++ vbuf->field = V4L2_FIELD_NONE; ++ v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE); ++ ++ return 0; ++} ++ ++void esparser_queue_all_src(struct work_struct *work) ++{ ++ struct v4l2_m2m_buffer *buf, *n; ++ struct amvdec_session *sess = ++ container_of(work, struct amvdec_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; ++ } ++ mutex_unlock(&sess->lock); ++} ++ ++int esparser_power_up(struct amvdec_session *sess) ++{ ++ struct amvdec_core *core = sess->core; ++ struct amvdec_ops *vdec_ops = sess->fmt_out->vdec_ops; ++ ++ reset_control_reset(core->esparser_reset); ++ amvdec_write_parser(core, PARSER_CONFIG, ++ (10 << PS_CFG_PFIFO_EMPTY_CNT_BIT) | ++ (1 << PS_CFG_MAX_ES_WR_CYCLE_BIT) | ++ (16 << PS_CFG_MAX_FETCH_CYCLE_BIT)); ++ ++ amvdec_write_parser(core, PFIFO_RD_PTR, 0); ++ amvdec_write_parser(core, PFIFO_WR_PTR, 0); ++ ++ amvdec_write_parser(core, PARSER_SEARCH_PATTERN, ++ ES_START_CODE_PATTERN); ++ amvdec_write_parser(core, PARSER_SEARCH_MASK, ES_START_CODE_MASK); ++ ++ amvdec_write_parser(core, PARSER_CONFIG, ++ (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)); ++ ++ amvdec_write_parser(core, PARSER_CONTROL, ++ (ES_SEARCH | ES_PARSER_START)); ++ ++ amvdec_write_parser(core, PARSER_VIDEO_START_PTR, sess->vififo_paddr); ++ amvdec_write_parser(core, PARSER_VIDEO_END_PTR, ++ sess->vififo_paddr + sess->vififo_size - 8); ++ amvdec_write_parser(core, PARSER_ES_CONTROL, ++ amvdec_read_parser(core, PARSER_ES_CONTROL) & ~1); ++ ++ if (vdec_ops->conf_esparser) ++ vdec_ops->conf_esparser(sess); ++ ++ amvdec_write_parser(core, PARSER_INT_STATUS, 0xffff); ++ amvdec_write_parser(core, PARSER_INT_ENABLE, ++ BIT(PARSER_INT_HOST_EN_BIT)); ++ ++ return 0; ++} ++ ++int esparser_init(struct platform_device *pdev, struct amvdec_core *core) ++{ ++ struct device *dev = &pdev->dev; ++ int ret; ++ int irq; ++ ++ irq = platform_get_irq_byname(pdev, "esparser"); ++ if (irq < 0) { ++ dev_err(dev, "Failed getting ESPARSER IRQ from dtb\n"); ++ return 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; ++} +diff --git a/drivers/media/platform/meson/vdec/esparser.h b/drivers/media/platform/meson/vdec/esparser.h +new file mode 100644 +index 0000000..ff51fe7 +--- /dev/null ++++ b/drivers/media/platform/meson/vdec/esparser.h +@@ -0,0 +1,32 @@ ++/* SPDX-License-Identifier: GPL-2.0+ */ ++/* ++ * Copyright (C) 2018 BayLibre, SAS ++ * Author: Maxime Jourdan <mjourdan@baylibre.com> ++ */ ++ ++#ifndef __MESON_VDEC_ESPARSER_H_ ++#define __MESON_VDEC_ESPARSER_H_ ++ ++#include <linux/platform_device.h> ++ ++#include "vdec.h" ++ ++int esparser_init(struct platform_device *pdev, struct amvdec_core *core); ++int esparser_power_up(struct amvdec_session *sess); ++ ++/** ++ * esparser_queue_eos() - write End Of Stream sequence to the ESPARSER ++ * ++ * @core vdec core struct ++ */ ++int esparser_queue_eos(struct amvdec_core *core, const u8 *data, u32 len); ++ ++/** ++ * esparser_queue_all_src() - work handler that writes as many src buffers ++ * as possible to the ESPARSER ++ */ ++void esparser_queue_all_src(struct work_struct *work); ++ ++#define ESPARSER_MIN_PACKET_SIZE SZ_4K ++ ++#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..d8db52c +--- /dev/null ++++ b/drivers/media/platform/meson/vdec/vdec.c +@@ -0,0 +1,1034 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Copyright (C) 2018 BayLibre, SAS ++ * Author: Maxime Jourdan <mjourdan@baylibre.com> ++ */ ++ ++#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 "vdec_helpers.h" ++ ++struct dummy_buf { ++ struct vb2_v4l2_buffer vb; ++ struct list_head list; ++}; ++ ++/* 16 MiB for parsed bitstream swap exchange */ ++#define SIZE_VIFIFO SZ_16M ++ ++static u32 get_output_size(u32 width, u32 height) ++{ ++ return ALIGN(width * height, SZ_64K); ++} ++ ++u32 amvdec_get_output_size(struct amvdec_session *sess) ++{ ++ return get_output_size(sess->width, sess->height); ++} ++EXPORT_SYMBOL_GPL(amvdec_get_output_size); ++ ++static int vdec_codec_needs_recycle(struct amvdec_session *sess) ++{ ++ struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops; ++ ++ return codec_ops->can_recycle && codec_ops->recycle; ++} ++ ++static int vdec_recycle_thread(void *data) ++{ ++ struct amvdec_session *sess = data; ++ struct amvdec_core *core = sess->core; ++ struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops; ++ struct amvdec_buffer *tmp, *n; ++ ++ while (!kthread_should_stop()) { ++ mutex_lock(&sess->bufs_recycle_lock); ++ list_for_each_entry_safe(tmp, n, &sess->bufs_recycle, list) { ++ if (!codec_ops->can_recycle(core)) ++ break; ++ ++ codec_ops->recycle(core, tmp->vb->index); ++ list_del(&tmp->list); ++ kfree(tmp); ++ } ++ mutex_unlock(&sess->bufs_recycle_lock); ++ ++ usleep_range(5000, 10000); ++ } ++ ++ return 0; ++} ++ ++static int vdec_poweron(struct amvdec_session *sess) ++{ ++ int ret; ++ struct amvdec_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_wait_inactive(struct amvdec_session *sess) ++{ ++ /* We consider 50ms with no IRQ to be inactive. */ ++ while (time_is_after_jiffies64(sess->last_irq_jiffies + ++ msecs_to_jiffies(50))) ++ msleep(25); ++} ++ ++static void vdec_poweroff(struct amvdec_session *sess) ++{ ++ struct amvdec_ops *vdec_ops = sess->fmt_out->vdec_ops; ++ struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops; ++ ++ vdec_wait_inactive(sess); ++ if (codec_ops->drain) ++ codec_ops->drain(sess); ++ ++ vdec_ops->stop(sess); ++ clk_disable_unprepare(sess->core->dos_clk); ++ clk_disable_unprepare(sess->core->dos_parser_clk); ++} ++ ++static void ++vdec_queue_recycle(struct amvdec_session *sess, struct vb2_buffer *vb) ++{ ++ struct amvdec_buffer *new_buf; ++ ++ new_buf = kmalloc(sizeof(*new_buf), GFP_KERNEL); ++ new_buf->vb = vb; ++ ++ mutex_lock(&sess->bufs_recycle_lock); ++ list_add_tail(&new_buf->list, &sess->bufs_recycle); ++ mutex_unlock(&sess->bufs_recycle_lock); ++} ++ ++static void vdec_m2m_device_run(void *priv) ++{ ++ struct amvdec_session *sess = priv; ++ ++ schedule_work(&sess->esparser_queue_work); ++} ++ ++static void vdec_m2m_job_abort(void *priv) ++{ ++ struct amvdec_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 amvdec_session *sess = vb2_get_drv_priv(q); ++ const struct amvdec_format *fmt_out = sess->fmt_out; ++ u32 output_size = amvdec_get_output_size(sess); ++ u32 buffers_total; ++ ++ if (*num_planes) { ++ switch (q->type) { ++ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: ++ if (*num_planes != 1 || sizes[0] < output_size) ++ return -EINVAL; ++ break; ++ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: ++ switch (sess->pixfmt_cap) { ++ case V4L2_PIX_FMT_NV12M: ++ if (*num_planes != 2 || ++ sizes[0] < output_size || ++ sizes[1] < output_size / 2) ++ return -EINVAL; ++ break; ++ case V4L2_PIX_FMT_YUV420M: ++ if (*num_planes != 3 || ++ sizes[0] < output_size || ++ sizes[1] < output_size / 4 || ++ sizes[2] < output_size / 4) ++ return -EINVAL; ++ break; ++ default: ++ return -EINVAL; ++ } ++ break; ++ } ++ ++ return 0; ++ } ++ ++ switch (q->type) { ++ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: ++ sizes[0] = amvdec_get_output_size(sess); ++ *num_planes = 1; ++ break; ++ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: ++ switch (sess->pixfmt_cap) { ++ case V4L2_PIX_FMT_NV12M: ++ sizes[0] = output_size; ++ sizes[1] = output_size / 2; ++ *num_planes = 2; ++ break; ++ case V4L2_PIX_FMT_YUV420M: ++ sizes[0] = output_size; ++ sizes[1] = output_size / 4; ++ sizes[2] = output_size / 4; ++ *num_planes = 3; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ buffers_total = q->num_buffers + *num_buffers; ++ ++ if (buffers_total < fmt_out->min_buffers) ++ *num_buffers = fmt_out->min_buffers - q->num_buffers; ++ if (buffers_total > fmt_out->max_buffers) ++ *num_buffers = fmt_out->max_buffers - q->num_buffers; ++ ++ /* We need to program the complete CAPTURE buffer list ++ * in registers during start_streaming, and the firmwares ++ * are free to choose any of them to write frames to. As such, ++ * we need all of them to be queued into the driver ++ */ ++ q->min_buffers_needed = q->num_buffers + *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 amvdec_session *sess = vb2_get_drv_priv(vb->vb2_queue); ++ struct v4l2_m2m_ctx *m2m_ctx = sess->m2m_ctx; ++ ++ v4l2_m2m_buf_queue(m2m_ctx, vbuf); ++ ++ if (!sess->streamon_out || !sess->streamon_cap) ++ return; ++ ++ if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && ++ vdec_codec_needs_recycle(sess)) ++ vdec_queue_recycle(sess, vb); ++ ++ schedule_work(&sess->esparser_queue_work); ++} ++ ++static int vdec_start_streaming(struct vb2_queue *q, unsigned int count) ++{ ++ struct amvdec_session *sess = vb2_get_drv_priv(q); ++ struct amvdec_core *core = sess->core; ++ struct vb2_v4l2_buffer *buf; ++ int ret; ++ ++ if (core->cur_sess && core->cur_sess != sess) { ++ ret = -EBUSY; ++ goto bufs_done; ++ } ++ ++ 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) ++ 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) { ++ dev_err(sess->core->dev, "Failed to request VIFIFO buffer\n"); ++ ret = -ENOMEM; ++ goto bufs_done; ++ } ++ ++ sess->should_stop = 0; ++ sess->keyframe_found = 0; ++ sess->last_offset = 0; ++ sess->wrap_count = 0; ++ sess->pixelaspect.numerator = 1; ++ sess->pixelaspect.denominator = 1; ++ atomic_set(&sess->esparser_queued_bufs, 0); ++ ++ ret = vdec_poweron(sess); ++ if (ret) ++ goto vififo_free; ++ ++ sess->sequence_cap = 0; ++ if (vdec_codec_needs_recycle(sess)) ++ sess->recycle_thread = kthread_run(vdec_recycle_thread, sess, ++ "vdec_recycle"); ++ ++ core->cur_sess = sess; ++ ++ 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; ++ ++ return ret; ++} ++ ++static void vdec_free_canvas(struct amvdec_session *sess) ++{ ++ int i; ++ ++ for (i = 0; i < sess->canvas_num; ++i) ++ meson_canvas_free(sess->core->canvas, sess->canvas_alloc[i]); ++ ++ sess->canvas_num = 0; ++} ++ ++static void vdec_reset_timestamps(struct amvdec_session *sess) ++{ ++ struct amvdec_timestamp *tmp, *n; ++ ++ list_for_each_entry_safe(tmp, n, &sess->timestamps, list) { ++ list_del(&tmp->list); ++ kfree(tmp); ++ } ++} ++ ++static void vdec_reset_bufs_recycle(struct amvdec_session *sess) ++{ ++ struct amvdec_buffer *tmp, *n; ++ ++ list_for_each_entry_safe(tmp, n, &sess->bufs_recycle, list) { ++ list_del(&tmp->list); ++ kfree(tmp); ++ } ++} ++ ++static void vdec_stop_streaming(struct vb2_queue *q) ++{ ++ struct amvdec_session *sess = vb2_get_drv_priv(q); ++ struct amvdec_core *core = sess->core; ++ struct vb2_v4l2_buffer *buf; ++ ++ if (sess->streamon_out && sess->streamon_cap) { ++ if (vdec_codec_needs_recycle(sess)) ++ kthread_stop(sess->recycle_thread); ++ ++ vdec_poweroff(sess); ++ vdec_free_canvas(sess); ++ dma_free_coherent(sess->core->dev, sess->vififo_size, ++ sess->vififo_vaddr, sess->vififo_paddr); ++ vdec_reset_timestamps(sess); ++ vdec_reset_bufs_recycle(sess); ++ kfree(sess->priv); ++ sess->priv = NULL; ++ core->cur_sess = NULL; ++ } ++ ++ 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; ++ } ++} ++ ++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, ++ .wait_prepare = vb2_ops_wait_prepare, ++ .wait_finish = vb2_ops_wait_finish, ++}; ++ ++static int ++vdec_querycap(struct file *file, void *fh, struct v4l2_capability *cap) ++{ ++ strscpy(cap->driver, "meson-vdec", sizeof(cap->driver)); ++ strscpy(cap->card, "Amlogic Video Decoder", sizeof(cap->card)); ++ strscpy(cap->bus_info, "platform:meson-vdec", sizeof(cap->bus_info)); ++ ++ return 0; ++} ++ ++static const struct amvdec_format * ++find_format(const struct amvdec_format *fmts, u32 size, u32 pixfmt) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < size; i++) { ++ if (fmts[i].pixfmt == pixfmt) ++ return &fmts[i]; ++ } ++ ++ return NULL; ++} ++ ++static unsigned int ++vdec_supports_pixfmt_cap(const struct amvdec_format *fmt_out, u32 pixfmt_cap) ++{ ++ int i; ++ ++ for (i = 0; fmt_out->pixfmts_cap[i]; i++) ++ if (fmt_out->pixfmts_cap[i] == pixfmt_cap) ++ return 1; ++ ++ return 0; ++} ++ ++static const struct amvdec_format * ++vdec_try_fmt_common(struct amvdec_session *sess, 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 amvdec_format *fmts = sess->core->platform->formats; ++ const struct amvdec_format *fmt_out; ++ ++ memset(pfmt[0].reserved, 0, sizeof(pfmt[0].reserved)); ++ memset(pixmp->reserved, 0, sizeof(pixmp->reserved)); ++ ++ if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { ++ fmt_out = find_format(fmts, size, pixmp->pixelformat); ++ if (!fmt_out) { ++ pixmp->pixelformat = V4L2_PIX_FMT_MPEG2; ++ fmt_out = find_format(fmts, size, pixmp->pixelformat); ++ } ++ ++ pfmt[0].sizeimage = ++ get_output_size(pixmp->width, pixmp->height); ++ pfmt[0].bytesperline = 0; ++ pixmp->num_planes = 1; ++ } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { ++ fmt_out = sess->fmt_out; ++ if (!vdec_supports_pixfmt_cap(fmt_out, pixmp->pixelformat)) ++ pixmp->pixelformat = fmt_out->pixfmts_cap[0]; ++ ++ memset(pfmt[1].reserved, 0, sizeof(pfmt[1].reserved)); ++ if (pixmp->pixelformat == V4L2_PIX_FMT_NV12M) { ++ 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); ++ pixmp->num_planes = 2; ++ } else if (pixmp->pixelformat == V4L2_PIX_FMT_YUV420M) { ++ 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) / 4; ++ pfmt[1].bytesperline = ALIGN(pixmp->width, 64) / 2; ++ ++ pfmt[2].sizeimage = ++ get_output_size(pixmp->width, pixmp->height) / 4; ++ pfmt[2].bytesperline = ALIGN(pixmp->width, 64) / 2; ++ pixmp->num_planes = 3; ++ } ++ } else { ++ return NULL; ++ } ++ ++ pixmp->width = clamp(pixmp->width, (u32)256, fmt_out->max_width); ++ pixmp->height = clamp(pixmp->height, (u32)144, fmt_out->max_height); ++ ++ if (pixmp->field == V4L2_FIELD_ANY) ++ pixmp->field = V4L2_FIELD_NONE; ++ ++ return fmt_out; ++} ++ ++static int vdec_try_fmt(struct file *file, void *fh, struct v4l2_format *f) ++{ ++ struct amvdec_session *sess = ++ container_of(file->private_data, struct amvdec_session, fh); ++ ++ vdec_try_fmt_common(sess, sess->core->platform->num_formats, f); ++ ++ return 0; ++} ++ ++static int vdec_g_fmt(struct file *file, void *fh, struct v4l2_format *f) ++{ ++ struct amvdec_session *sess = ++ container_of(file->private_data, struct amvdec_session, fh); ++ struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp; ++ ++ if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) ++ pixmp->pixelformat = sess->pixfmt_cap; ++ else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ++ pixmp->pixelformat = sess->fmt_out->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, sess->core->platform->num_formats, f); ++ ++ return 0; ++} ++ ++static int vdec_s_fmt(struct file *file, void *fh, struct v4l2_format *f) ++{ ++ struct amvdec_session *sess = ++ container_of(file->private_data, struct amvdec_session, fh); ++ struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp; ++ u32 num_formats = sess->core->platform->num_formats; ++ const struct amvdec_format *fmt_out; ++ struct v4l2_pix_format_mplane orig_pixmp; ++ struct v4l2_format format; ++ u32 pixfmt_out = 0, pixfmt_cap = 0; ++ ++ orig_pixmp = *pixmp; ++ ++ fmt_out = vdec_try_fmt_common(sess, num_formats, f); ++ ++ if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { ++ pixfmt_out = pixmp->pixelformat; ++ pixfmt_cap = sess->pixfmt_cap; ++ } 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(sess, 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(sess, 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_out; ++ else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) ++ sess->pixfmt_cap = format.fmt.pix_mp.pixelformat; ++ ++ return 0; ++} ++ ++static int vdec_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f) ++{ ++ struct amvdec_session *sess = ++ container_of(file->private_data, struct amvdec_session, fh); ++ const struct vdec_platform *platform = sess->core->platform; ++ const struct amvdec_format *fmt_out; ++ ++ memset(f->reserved, 0, sizeof(f->reserved)); ++ ++ if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { ++ if (f->index >= platform->num_formats) ++ return -EINVAL; ++ ++ fmt_out = &platform->formats[f->index]; ++ f->pixelformat = fmt_out->pixfmt; ++ } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { ++ fmt_out = sess->fmt_out; ++ if (f->index >= 4 || !fmt_out->pixfmts_cap[f->index]) ++ return -EINVAL; ++ ++ f->pixelformat = fmt_out->pixfmts_cap[f->index]; ++ } else { ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int vdec_enum_framesizes(struct file *file, void *fh, ++ struct v4l2_frmsizeenum *fsize) ++{ ++ struct amvdec_session *sess = ++ container_of(file->private_data, struct amvdec_session, fh); ++ const struct amvdec_format *formats = sess->core->platform->formats; ++ const struct amvdec_format *fmt; ++ u32 num_formats = sess->core->platform->num_formats; ++ ++ fmt = find_format(formats, num_formats, fsize->pixel_format); ++ if (!fmt || fsize->index) ++ return -EINVAL; ++ ++ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; ++ ++ fsize->stepwise.min_width = 256; ++ fsize->stepwise.max_width = fmt->max_width; ++ fsize->stepwise.step_width = 1; ++ fsize->stepwise.min_height = 144; ++ fsize->stepwise.max_height = fmt->max_height; ++ 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 amvdec_session *sess = ++ container_of(file->private_data, struct amvdec_session, fh); ++ struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops; ++ struct device *dev = sess->core->dev; ++ int ret; ++ ++ ret = vdec_try_decoder_cmd(file, fh, cmd); ++ if (ret) ++ return ret; ++ ++ if (!(sess->streamon_out & sess->streamon_cap)) ++ return 0; ++ ++ dev_dbg(dev, "Received V4L2_DEC_CMD_STOP\n"); ++ sess->should_stop = 1; ++ ++ vdec_wait_inactive(sess); ++ ++ if (codec_ops->drain) { ++ codec_ops->drain(sess); ++ } else if (codec_ops->eos_sequence) { ++ u32 len; ++ const u8 *data = codec_ops->eos_sequence(&len); ++ ++ esparser_queue_eos(sess->core, data, len); ++ } ++ ++ 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 int vdec_cropcap(struct file *file, void *fh, ++ struct v4l2_cropcap *crop) ++{ ++ struct amvdec_session *sess = ++ container_of(file->private_data, struct amvdec_session, fh); ++ ++ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) ++ return -EINVAL; ++ ++ crop->pixelaspect = sess->pixelaspect; ++ return 0; ++} ++ ++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_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, ++ .vidioc_cropcap = vdec_cropcap, ++}; ++ ++static int m2m_queue_init(void *priv, struct vb2_queue *src_vq, ++ struct vb2_queue *dst_vq) ++{ ++ struct amvdec_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->min_buffers_needed = 1; ++ src_vq->dev = sess->core->dev; ++ src_vq->lock = &sess->lock; ++ 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->min_buffers_needed = 1; ++ dst_vq->dev = sess->core->dev; ++ dst_vq->lock = &sess->lock; ++ 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 amvdec_core *core = video_drvdata(file); ++ struct device *dev = core->dev; ++ const struct amvdec_format *formats = core->platform->formats; ++ struct amvdec_session *sess; ++ int ret; ++ ++ sess = kzalloc(sizeof(*sess), GFP_KERNEL); ++ if (!sess) ++ return -ENOMEM; ++ ++ sess->core = core; ++ ++ 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; ++ } ++ ++ sess->pixfmt_cap = formats[0].pixfmts_cap[0]; ++ sess->fmt_out = &formats[0]; ++ sess->width = 1280; ++ sess->height = 720; ++ sess->pixelaspect.numerator = 1; ++ sess->pixelaspect.denominator = 1; ++ ++ INIT_LIST_HEAD(&sess->timestamps); ++ INIT_LIST_HEAD(&sess->bufs_recycle); ++ INIT_WORK(&sess->esparser_queue_work, esparser_queue_all_src); ++ mutex_init(&sess->lock); ++ mutex_init(&sess->bufs_recycle_lock); ++ spin_lock_init(&sess->ts_spinlock); ++ ++ 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 amvdec_session *sess = ++ container_of(file->private_data, struct amvdec_session, fh); ++ ++ 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); ++ mutex_destroy(&sess->bufs_recycle_lock); ++ ++ kfree(sess); ++ ++ return 0; ++} ++ ++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, ++}; ++ ++static irqreturn_t vdec_isr(int irq, void *data) ++{ ++ struct amvdec_core *core = data; ++ struct amvdec_session *sess = core->cur_sess; ++ ++ sess->last_irq_jiffies = get_jiffies_64(); ++ ++ return sess->fmt_out->codec_ops->isr(sess); ++} ++ ++static irqreturn_t vdec_threaded_isr(int irq, void *data) ++{ ++ struct amvdec_core *core = data; ++ struct amvdec_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,gxbb-vdec", ++ .data = &vdec_platform_gxbb }, ++ { .compatible = "amlogic,gxm-vdec", ++ .data = &vdec_platform_gxm }, ++ { .compatible = "amlogic,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 amvdec_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) ++ 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); ++ } ++ ++ 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->canvas = meson_canvas_get(dev); ++ if (!core->canvas) ++ return PTR_ERR(core->canvas); ++ ++ core->dos_parser_clk = devm_clk_get(dev, "dos_parser"); ++ if (IS_ERR(core->dos_parser_clk)) ++ return -EPROBE_DEFER; ++ ++ core->dos_clk = devm_clk_get(dev, "dos"); ++ if (IS_ERR(core->dos_clk)) ++ return -EPROBE_DEFER; ++ ++ core->vdec_1_clk = devm_clk_get(dev, "vdec_1"); ++ if (IS_ERR(core->vdec_1_clk)) ++ return -EPROBE_DEFER; ++ ++ core->vdec_hevc_clk = devm_clk_get(dev, "vdec_hevc"); ++ if (IS_ERR(core->vdec_hevc_clk)) ++ return -EPROBE_DEFER; ++ ++ irq = platform_get_irq_byname(pdev, "vdec"); ++ 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; ++ } ++ ++ 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); ++ ++ strscpy(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->lock = &core->lock; ++ vdev->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; ++ ++ video_set_drvdata(vdev, core); ++ ++ ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); ++ if (ret) { ++ dev_err(dev, "Failed registering video device\n"); ++ goto err_vdev_release; ++ } ++ ++ return 0; ++ ++err_vdev_release: ++ video_device_release(vdev); ++ return ret; ++} ++ ++static int vdec_remove(struct platform_device *pdev) ++{ ++ struct amvdec_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_DESCRIPTION("Meson video decoder driver for GXBB/GXL/GXM"); ++MODULE_AUTHOR("Maxime Jourdan <mjourdan@baylibre.com>"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/platform/meson/vdec/vdec.h b/drivers/media/platform/meson/vdec/vdec.h +new file mode 100644 +index 0000000..4e8c3f1 +--- /dev/null ++++ b/drivers/media/platform/meson/vdec/vdec.h +@@ -0,0 +1,251 @@ ++/* SPDX-License-Identifier: GPL-2.0+ */ ++/* ++ * Copyright (C) 2018 BayLibre, SAS ++ * Author: Maxime Jourdan <mjourdan@baylibre.com> ++ */ ++ ++#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 <linux/soc/amlogic/meson-canvas.h> ++ ++#include "vdec_platform.h" ++ ++/* 32 buffers in 3-plane YUV420 */ ++#define MAX_CANVAS (32 * 3) ++ ++struct amvdec_buffer { ++ struct list_head list; ++ struct vb2_buffer *vb; ++}; ++ ++/** ++ * struct amvdec_timestamp - stores a src timestamp along with a VIFIFO offset ++ * ++ * @list: used to make lists out of this struct ++ * @ts: timestamp ++ * @offset: offset in the VIFIFO where the associated packet was written ++ */ ++struct amvdec_timestamp { ++ struct list_head list; ++ u64 ts; ++ u32 offset; ++}; ++ ++struct amvdec_session; ++ ++/** ++ * struct amvdec_core - device parameters, singleton ++ * ++ * @dos_base: DOS memory base address ++ * @esparser_base: PARSER memory base address ++ * @regmap_ao: regmap for the AO bus ++ * @dev: core device ++ * @dev_dec: decoder device ++ * @platform: platform-specific data ++ * @canvas: canvas provider reference ++ * @dos_parser_clk: DOS_PARSER clock ++ * @dos_clk: DOS clock ++ * @vdec_1_clk: VDEC_1 clock ++ * @vdec_hevc_clk: VDEC_HEVC clock ++ * @esparser_reset: RESET for the PARSER ++ * @vdec_dec: video device for the decoder ++ * @v4l2_dev: v4l2 device ++ * @cur_sess: current decoding session ++ * @lock: lock for this structure ++ */ ++struct amvdec_core { ++ void __iomem *dos_base; ++ void __iomem *esparser_base; ++ struct regmap *regmap_ao; ++ ++ struct device *dev; ++ struct device *dev_dec; ++ const struct vdec_platform *platform; ++ ++ struct meson_canvas *canvas; ++ ++ 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 amvdec_session *cur_sess; ++ struct mutex lock; ++}; ++ ++/** ++ * struct amvdec_ops - vdec operations ++ * ++ * @start: mandatory call when the vdec needs to initialize ++ * @stop: mandatory call when the vdec needs to stop ++ * @conf_esparser: mandatory call to let the vdec configure the ESPARSER ++ * @vififo_level: mandatory call to get the current amount of data ++ * in the VIFIFO ++ * @use_offsets: mandatory call. Returns 1 if the VDEC supports vififo offsets ++ */ ++struct amvdec_ops { ++ int (*start)(struct amvdec_session *sess); ++ int (*stop)(struct amvdec_session *sess); ++ void (*conf_esparser)(struct amvdec_session *sess); ++ u32 (*vififo_level)(struct amvdec_session *sess); ++}; ++ ++/** ++ * struct amvdec_codec_ops - codec operations ++ * ++ * @start: mandatory call when the codec needs to initialize ++ * @stop: mandatory call when the codec needs to stop ++ * @load_extended_firmware: optional call to load additional firmware bits ++ * @num_pending_bufs: optional call to get the number of dst buffers on hold ++ * @can_recycle: optional call to know if the codec is ready to recycle ++ * a dst buffer ++ * @recycle: optional call to tell the codec to recycle a dst buffer. Must go ++ * in pair with @can_recycle ++ * @drain: optional call if the codec has a custom way of draining ++ * @eos_sequence: optional call to get an end sequence to send to esparser ++ * for flush. Mutually exclusive with @drain. ++ * @isr: mandatory call when the ISR triggers ++ * @threaded_isr: mandatory call for the threaded ISR ++ */ ++struct amvdec_codec_ops { ++ int (*start)(struct amvdec_session *sess); ++ int (*stop)(struct amvdec_session *sess); ++ int (*load_extended_firmware)(struct amvdec_session *sess, ++ const u8 *data, u32 len); ++ u32 (*num_pending_bufs)(struct amvdec_session *sess); ++ int (*can_recycle)(struct amvdec_core *core); ++ void (*recycle)(struct amvdec_core *core, u32 buf_idx); ++ void (*drain)(struct amvdec_session *sess); ++ const u8 * (*eos_sequence)(u32 *len); ++ irqreturn_t (*isr)(struct amvdec_session *sess); ++ irqreturn_t (*threaded_isr)(struct amvdec_session *sess); ++}; ++ ++/** ++ * struct amvdec_format - describes one of the OUTPUT (src) format supported ++ * ++ * @pixfmt: V4L2 pixel format ++ * @min_buffers: minimum amount of CAPTURE (dst) buffers ++ * @max_buffers: maximum amount of CAPTURE (dst) buffers ++ * @max_width: maximum picture width supported ++ * @max_height: maximum picture height supported ++ * @vdec_ops: the VDEC operations that support this format ++ * @codec_ops: the codec operations that support this format ++ * @firmware_path: Path to the firmware that supports this format ++ * @pixfmts_cap: list of CAPTURE pixel formats available with pixfmt ++ */ ++struct amvdec_format { ++ u32 pixfmt; ++ u32 min_buffers; ++ u32 max_buffers; ++ u32 max_width; ++ u32 max_height; ++ ++ struct amvdec_ops *vdec_ops; ++ struct amvdec_codec_ops *codec_ops; ++ ++ char *firmware_path; ++ u32 pixfmts_cap[4]; ++}; ++ ++/** ++ * struct amvdec_session - decoding session parameters ++ * ++ * @core: reference to the vdec core struct ++ * @fh: v4l2 file handle ++ * @m2m_dev: v4l2 m2m device ++ * @m2m_ctx: v4l2 m2m context ++ * @lock: session lock ++ * @fmt_out: vdec pixel format for the OUTPUT queue ++ * @pixfmt_cap: V4L2 pixel format for the CAPTURE queue ++ * @width: current picture width ++ * @height: current picture height ++ * @colorspace: current colorspace ++ * @ycbcr_enc: current ycbcr_enc ++ * @quantization: current quantization ++ * @xfer_func: current transfer function ++ * @pixelaspect: Pixel Aspect Ratio reported by the decoder ++ * @esparser_queued_bufs: number of buffers currently queued into ESPARSER ++ * @esparser_queue_work: work struct for the ESPARSER to process src buffers ++ * @streamon_cap: stream on flag for capture queue ++ * @streamon_out: stream on flag for output queue ++ * @sequence_cap: capture sequence counter ++ * @should_stop: flag set if userspace signaled EOS via command ++ * or empty buffer ++ * @keyframe_found: flag set once a keyframe has been parsed ++ * @canvas_alloc: array of all the canvas IDs allocated ++ * @canvas_num: number of canvas IDs allocated ++ * @vififo_vaddr: virtual address for the VIFIFO ++ * @vififo_paddr: physical address for the VIFIFO ++ * @vififo_size: size of the VIFIFO dma alloc ++ * @bufs_recycle: list of buffers that need to be recycled ++ * @bufs_recycle_lock: lock for the bufs_recycle list ++ * @recycle_thread: task struct for the recycling thread ++ * @timestamps: chronological list of src timestamps ++ * @ts_spinlock: spinlock for the timestamps list ++ * @last_irq_jiffies: tracks last time the vdec triggered an IRQ ++ * @priv: codec private data ++ */ ++struct amvdec_session { ++ struct amvdec_core *core; ++ ++ struct v4l2_fh fh; ++ struct v4l2_m2m_dev *m2m_dev; ++ struct v4l2_m2m_ctx *m2m_ctx; ++ struct mutex lock; ++ ++ const struct amvdec_format *fmt_out; ++ u32 pixfmt_cap; ++ ++ u32 width; ++ u32 height; ++ u32 colorspace; ++ u8 ycbcr_enc; ++ u8 quantization; ++ u8 xfer_func; ++ ++ struct v4l2_fract pixelaspect; ++ ++ atomic_t esparser_queued_bufs; ++ struct work_struct esparser_queue_work; ++ ++ unsigned int streamon_cap, streamon_out; ++ unsigned int sequence_cap; ++ unsigned int should_stop; ++ unsigned int keyframe_found; ++ ++ u8 canvas_alloc[MAX_CANVAS]; ++ u32 canvas_num; ++ ++ void *vififo_vaddr; ++ dma_addr_t vififo_paddr; ++ u32 vififo_size; ++ ++ struct list_head bufs_recycle; ++ struct mutex bufs_recycle_lock; ++ struct task_struct *recycle_thread; ++ ++ struct list_head timestamps; ++ spinlock_t ts_spinlock; ++ ++ u64 last_irq_jiffies; ++ u32 last_offset; ++ u32 wrap_count; ++ ++ void *priv; ++}; ++ ++u32 amvdec_get_output_size(struct amvdec_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..88b8bed +--- /dev/null ++++ b/drivers/media/platform/meson/vdec/vdec_1.c +@@ -0,0 +1,231 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Copyright (C) 2018 BayLibre, SAS ++ * Author: Maxime Jourdan <mjourdan@baylibre.com> ++ * ++ * VDEC_1 is a video decoding block that allows decoding of ++ * MPEG 1/2/4, H.263, H.264, MJPEG, VC1 ++ */ ++ ++#include <linux/firmware.h> ++#include <linux/clk.h> ++ ++#include "vdec_1.h" ++#include "vdec_helpers.h" ++#include "dos_regs.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)) ++ ++#define MC_SIZE (4096 * 4) ++ ++static int ++vdec_1_load_firmware(struct amvdec_session *sess, const char *fwname) ++{ ++ const struct firmware *fw; ++ struct amvdec_core *core = sess->core; ++ struct device *dev = core->dev_dec; ++ struct amvdec_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); ++ ++ amvdec_write_dos(core, MPSR, 0); ++ amvdec_write_dos(core, CPSR, 0); ++ ++ amvdec_clear_dos_bits(core, MDEC_PIC_DC_CTRL, BIT(31)); ++ ++ amvdec_write_dos(core, IMEM_DMA_ADR, mc_addr_map); ++ amvdec_write_dos(core, IMEM_DMA_COUNT, MC_SIZE / 4); ++ amvdec_write_dos(core, IMEM_DMA_CTRL, (0x8000 | (7 << 16))); ++ ++ while (--i && amvdec_read_dos(core, 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) ++ ret = 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 amvdec_session *sess) ++{ ++ struct amvdec_core *core = sess->core; ++ ++ amvdec_write_dos(core, VLD_MEM_VIFIFO_CONTROL, 0); ++ amvdec_write_dos(core, VLD_MEM_VIFIFO_WRAP_COUNT, 0); ++ amvdec_write_dos(core, POWER_CTL_VLD, BIT(4)); ++ ++ amvdec_write_dos(core, VLD_MEM_VIFIFO_START_PTR, sess->vififo_paddr); ++ amvdec_write_dos(core, VLD_MEM_VIFIFO_CURR_PTR, sess->vififo_paddr); ++ amvdec_write_dos(core, VLD_MEM_VIFIFO_END_PTR, ++ sess->vififo_paddr + sess->vififo_size - 8); ++ ++ amvdec_write_dos_bits(core, VLD_MEM_VIFIFO_CONTROL, 1); ++ amvdec_clear_dos_bits(core, VLD_MEM_VIFIFO_CONTROL, 1); ++ ++ amvdec_write_dos(core, VLD_MEM_VIFIFO_BUF_CNTL, MEM_BUFCTRL_MANUAL); ++ amvdec_write_dos(core, VLD_MEM_VIFIFO_WP, sess->vififo_paddr); ++ ++ amvdec_write_dos_bits(core, VLD_MEM_VIFIFO_BUF_CNTL, 1); ++ amvdec_clear_dos_bits(core, VLD_MEM_VIFIFO_BUF_CNTL, 1); ++ ++ amvdec_write_dos_bits(core, VLD_MEM_VIFIFO_CONTROL, ++ (0x11 << MEM_FIFO_CNT_BIT) | MEM_FILL_ON_LEVEL | ++ MEM_CTRL_FILL_EN | MEM_CTRL_EMPTY_EN); ++ ++ return 0; ++} ++ ++static void vdec_1_conf_esparser(struct amvdec_session *sess) ++{ ++ struct amvdec_core *core = sess->core; ++ ++ /* VDEC_1 specific ESPARSER stuff */ ++ amvdec_write_dos(core, DOS_GEN_CTRL0, 0); ++ amvdec_write_dos(core, VLD_MEM_VIFIFO_BUF_CNTL, 1); ++ amvdec_clear_dos_bits(core, VLD_MEM_VIFIFO_BUF_CNTL, 1); ++} ++ ++static u32 vdec_1_vififo_level(struct amvdec_session *sess) ++{ ++ struct amvdec_core *core = sess->core; ++ ++ return amvdec_read_dos(core, VLD_MEM_VIFIFO_LEVEL); ++} ++ ++static int vdec_1_stop(struct amvdec_session *sess) ++{ ++ struct amvdec_core *core = sess->core; ++ struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops; ++ ++ amvdec_write_dos(core, MPSR, 0); ++ amvdec_write_dos(core, CPSR, 0); ++ amvdec_write_dos(core, ASSIST_MBOX1_MASK, 0); ++ ++ amvdec_write_dos(core, DOS_SW_RESET0, BIT(12) | BIT(11)); ++ amvdec_write_dos(core, DOS_SW_RESET0, 0); ++ amvdec_read_dos(core, DOS_SW_RESET0); ++ ++ /* enable vdec1 isolation */ ++ regmap_write(core->regmap_ao, AO_RTI_GEN_PWR_ISO0, 0xc0); ++ /* power off vdec1 memories */ ++ amvdec_write_dos(core, DOS_MEM_PD_VDEC, 0xffffffff); ++ /* power off vdec1 */ ++ 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); ++ ++ if (sess->priv) ++ codec_ops->stop(sess); ++ ++ return 0; ++} ++ ++static int vdec_1_start(struct amvdec_session *sess) ++{ ++ int ret; ++ struct amvdec_core *core = sess->core; ++ struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops; ++ ++ /* Configure the vdec clk to the maximum available */ ++ 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 */ ++ amvdec_write_dos(core, DOS_SW_RESET0, 0xfffffffc); ++ amvdec_write_dos(core, DOS_SW_RESET0, 0x00000000); ++ ++ amvdec_write_dos(core, DOS_GCLK_EN0, 0x3ff); ++ ++ /* enable VDEC Memories */ ++ amvdec_write_dos(core, DOS_MEM_PD_VDEC, 0); ++ /* Remove VDEC1 Isolation */ ++ regmap_write(core->regmap_ao, AO_RTI_GEN_PWR_ISO0, 0); ++ /* Reset DOS top registers */ ++ amvdec_write_dos(core, DOS_VDEC_MCRCC_STALL_CTRL, 0); ++ ++ amvdec_write_dos(core, GCLK_EN, 0x3ff); ++ amvdec_clear_dos_bits(core, MDEC_PIC_DC_CTRL, BIT(31)); ++ ++ vdec_1_stbuf_power_up(sess); ++ ++ ret = vdec_1_load_firmware(sess, sess->fmt_out->firmware_path); ++ if (ret) ++ goto stop; ++ ++ ret = codec_ops->start(sess); ++ if (ret) ++ goto stop; ++ ++ /* Enable IRQ */ ++ amvdec_write_dos(core, ASSIST_MBOX1_CLR_REG, 1); ++ amvdec_write_dos(core, ASSIST_MBOX1_MASK, 1); ++ ++ /* Enable 2-plane output */ ++ if (sess->pixfmt_cap == V4L2_PIX_FMT_NV12M) ++ amvdec_write_dos_bits(core, MDEC_PIC_DC_CTRL, BIT(17)); ++ else ++ amvdec_clear_dos_bits(core, MDEC_PIC_DC_CTRL, BIT(17)); ++ ++ /* Enable firmware processor */ ++ amvdec_write_dos(core, MPSR, 1); ++ /* Let the firmware settle */ ++ udelay(10); ++ ++ return 0; ++ ++stop: ++ vdec_1_stop(sess); ++ return ret; ++} ++ ++struct amvdec_ops vdec_1_ops = { ++ .start = vdec_1_start, ++ .stop = vdec_1_stop, ++ .conf_esparser = vdec_1_conf_esparser, ++ .vififo_level = vdec_1_vififo_level, ++}; +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..042d930 +--- /dev/null ++++ b/drivers/media/platform/meson/vdec/vdec_1.h +@@ -0,0 +1,14 @@ ++/* SPDX-License-Identifier: GPL-2.0+ */ ++/* ++ * Copyright (C) 2018 BayLibre, SAS ++ * Author: Maxime Jourdan <mjourdan@baylibre.com> ++ */ ++ ++#ifndef __MESON_VDEC_VDEC_1_H_ ++#define __MESON_VDEC_VDEC_1_H_ ++ ++#include "vdec.h" ++ ++extern struct amvdec_ops vdec_1_ops; ++ ++#endif +diff --git a/drivers/media/platform/meson/vdec/vdec_helpers.c b/drivers/media/platform/meson/vdec/vdec_helpers.c +new file mode 100644 +index 0000000..02090c5 +--- /dev/null ++++ b/drivers/media/platform/meson/vdec/vdec_helpers.c +@@ -0,0 +1,412 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Copyright (C) 2018 BayLibre, SAS ++ * Author: Maxime Jourdan <mjourdan@baylibre.com> ++ */ ++ ++#include <linux/gcd.h> ++#include <media/v4l2-mem2mem.h> ++#include <media/v4l2-event.h> ++#include <media/videobuf2-dma-contig.h> ++ ++#include "vdec_helpers.h" ++ ++#define NUM_CANVAS_NV12 2 ++#define NUM_CANVAS_YUV420 3 ++ ++u32 amvdec_read_dos(struct amvdec_core *core, u32 reg) ++{ ++ return readl_relaxed(core->dos_base + reg); ++} ++EXPORT_SYMBOL_GPL(amvdec_read_dos); ++ ++void amvdec_write_dos(struct amvdec_core *core, u32 reg, u32 val) ++{ ++ writel_relaxed(val, core->dos_base + reg); ++} ++EXPORT_SYMBOL_GPL(amvdec_write_dos); ++ ++void amvdec_write_dos_bits(struct amvdec_core *core, u32 reg, u32 val) ++{ ++ amvdec_write_dos(core, reg, amvdec_read_dos(core, reg) | val); ++} ++EXPORT_SYMBOL_GPL(amvdec_write_dos_bits); ++ ++void amvdec_clear_dos_bits(struct amvdec_core *core, u32 reg, u32 val) ++{ ++ amvdec_write_dos(core, reg, amvdec_read_dos(core, reg) & ~val); ++} ++EXPORT_SYMBOL_GPL(amvdec_clear_dos_bits); ++ ++u32 amvdec_read_parser(struct amvdec_core *core, u32 reg) ++{ ++ return readl_relaxed(core->esparser_base + reg); ++} ++EXPORT_SYMBOL_GPL(amvdec_read_parser); ++ ++void amvdec_write_parser(struct amvdec_core *core, u32 reg, u32 val) ++{ ++ writel_relaxed(val, core->esparser_base + reg); ++} ++EXPORT_SYMBOL_GPL(amvdec_write_parser); ++ ++static int canvas_alloc(struct amvdec_session *sess, u8 *canvas_id) ++{ ++ int ret; ++ ++ if (sess->canvas_num >= MAX_CANVAS) { ++ dev_err(sess->core->dev, "Reached max number of canvas\n"); ++ return -ENOMEM; ++ } ++ ++ ret = meson_canvas_alloc(sess->core->canvas, canvas_id); ++ if (ret) ++ return ret; ++ ++ sess->canvas_alloc[sess->canvas_num++] = *canvas_id; ++ return 0; ++} ++ ++static int set_canvas_yuv420m(struct amvdec_session *sess, ++ struct vb2_buffer *vb, u32 width, ++ u32 height, u32 reg) ++{ ++ struct amvdec_core *core = sess->core; ++ u8 canvas_id[NUM_CANVAS_YUV420]; /* Y U V */ ++ dma_addr_t buf_paddr[NUM_CANVAS_YUV420]; /* Y U V */ ++ int ret, i; ++ ++ for (i = 0; i < NUM_CANVAS_YUV420; ++i) { ++ ret = canvas_alloc(sess, &canvas_id[i]); ++ if (ret) ++ return ret; ++ ++ buf_paddr[i] = ++ vb2_dma_contig_plane_dma_addr(vb, i); ++ } ++ ++ /* Y plane */ ++ meson_canvas_config(core->canvas, canvas_id[0], buf_paddr[0], ++ width, height, MESON_CANVAS_WRAP_NONE, ++ MESON_CANVAS_BLKMODE_LINEAR, ++ MESON_CANVAS_ENDIAN_SWAP64); ++ ++ /* U plane */ ++ meson_canvas_config(core->canvas, canvas_id[1], buf_paddr[1], ++ width / 2, height / 2, MESON_CANVAS_WRAP_NONE, ++ MESON_CANVAS_BLKMODE_LINEAR, ++ MESON_CANVAS_ENDIAN_SWAP64); ++ ++ /* V plane */ ++ meson_canvas_config(core->canvas, canvas_id[2], buf_paddr[2], ++ width / 2, height / 2, MESON_CANVAS_WRAP_NONE, ++ MESON_CANVAS_BLKMODE_LINEAR, ++ MESON_CANVAS_ENDIAN_SWAP64); ++ ++ amvdec_write_dos(core, reg, ++ ((canvas_id[2]) << 16) | ++ ((canvas_id[1]) << 8) | ++ (canvas_id[0])); ++ ++ return 0; ++} ++ ++static int set_canvas_nv12m(struct amvdec_session *sess, ++ struct vb2_buffer *vb, u32 width, ++ u32 height, u32 reg) ++{ ++ struct amvdec_core *core = sess->core; ++ u8 canvas_id[NUM_CANVAS_NV12]; /* Y U/V */ ++ dma_addr_t buf_paddr[NUM_CANVAS_NV12]; /* Y U/V */ ++ int ret, i; ++ ++ for (i = 0; i < NUM_CANVAS_NV12; ++i) { ++ ret = canvas_alloc(sess, &canvas_id[i]); ++ if (ret) ++ return ret; ++ ++ buf_paddr[i] = ++ vb2_dma_contig_plane_dma_addr(vb, i); ++ } ++ ++ /* Y plane */ ++ meson_canvas_config(core->canvas, canvas_id[0], buf_paddr[0], ++ width, height, MESON_CANVAS_WRAP_NONE, ++ MESON_CANVAS_BLKMODE_LINEAR, ++ MESON_CANVAS_ENDIAN_SWAP64); ++ ++ /* U/V plane */ ++ meson_canvas_config(core->canvas, canvas_id[1], buf_paddr[1], ++ width, height / 2, MESON_CANVAS_WRAP_NONE, ++ MESON_CANVAS_BLKMODE_LINEAR, ++ MESON_CANVAS_ENDIAN_SWAP64); ++ ++ amvdec_write_dos(core, reg, ++ ((canvas_id[1]) << 16) | ++ ((canvas_id[1]) << 8) | ++ (canvas_id[0])); ++ ++ return 0; ++} ++ ++int amvdec_set_canvases(struct amvdec_session *sess, ++ u32 reg_base[], u32 reg_num[]) ++{ ++ struct v4l2_m2m_buffer *buf; ++ u32 pixfmt = sess->pixfmt_cap; ++ u32 width = ALIGN(sess->width, 64); ++ u32 height = ALIGN(sess->height, 64); ++ u32 reg_cur = reg_base[0]; ++ u32 reg_num_cur = 0; ++ u32 reg_base_cur = 0; ++ int ret; ++ ++ v4l2_m2m_for_each_dst_buf(sess->m2m_ctx, buf) { ++ if (!reg_base[reg_base_cur]) ++ return -EINVAL; ++ ++ reg_cur = reg_base[reg_base_cur] + reg_num_cur * 4; ++ ++ switch (pixfmt) { ++ case V4L2_PIX_FMT_NV12M: ++ ret = set_canvas_nv12m(sess, &buf->vb.vb2_buf, width, ++ height, reg_cur); ++ if (ret) ++ return ret; ++ break; ++ case V4L2_PIX_FMT_YUV420M: ++ ret = set_canvas_yuv420m(sess, &buf->vb.vb2_buf, width, ++ height, reg_cur); ++ if (ret) ++ return ret; ++ break; ++ default: ++ dev_err(sess->core->dev, "Unsupported pixfmt %08X\n", ++ pixfmt); ++ return -EINVAL; ++ }; ++ ++ reg_num_cur++; ++ if (reg_num_cur >= reg_num[reg_base_cur]) { ++ reg_base_cur++; ++ reg_num_cur = 0; ++ } ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(amvdec_set_canvases); ++ ++void amvdec_add_ts_reorder(struct amvdec_session *sess, u64 ts, u32 offset) ++{ ++ struct amvdec_timestamp *new_ts, *tmp; ++ unsigned long flags; ++ ++ new_ts = kmalloc(sizeof(*new_ts), GFP_KERNEL); ++ new_ts->ts = ts; ++ new_ts->offset = offset; ++ ++ spin_lock_irqsave(&sess->ts_spinlock, flags); ++ ++ if (list_empty(&sess->timestamps)) ++ goto add_tail; ++ ++ list_for_each_entry(tmp, &sess->timestamps, list) { ++ if (ts <= tmp->ts) { ++ list_add_tail(&new_ts->list, &tmp->list); ++ goto unlock; ++ } ++ } ++ ++add_tail: ++ list_add_tail(&new_ts->list, &sess->timestamps); ++unlock: ++ spin_unlock_irqrestore(&sess->ts_spinlock, flags); ++} ++EXPORT_SYMBOL_GPL(amvdec_add_ts_reorder); ++ ++void amvdec_remove_ts(struct amvdec_session *sess, u64 ts) ++{ ++ struct amvdec_timestamp *tmp; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&sess->ts_spinlock, flags); ++ list_for_each_entry(tmp, &sess->timestamps, list) { ++ if (tmp->ts == 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->ts_spinlock, flags); ++} ++EXPORT_SYMBOL_GPL(amvdec_remove_ts); ++ ++static void dst_buf_done(struct amvdec_session *sess, ++ struct vb2_v4l2_buffer *vbuf, ++ u32 field, ++ u64 timestamp) ++{ ++ struct device *dev = sess->core->dev_dec; ++ u32 output_size = amvdec_get_output_size(sess); ++ ++ switch (sess->pixfmt_cap) { ++ case V4L2_PIX_FMT_NV12M: ++ vbuf->vb2_buf.planes[0].bytesused = output_size; ++ vbuf->vb2_buf.planes[1].bytesused = output_size / 2; ++ break; ++ case V4L2_PIX_FMT_YUV420M: ++ vbuf->vb2_buf.planes[0].bytesused = output_size; ++ vbuf->vb2_buf.planes[1].bytesused = output_size / 4; ++ vbuf->vb2_buf.planes[2].bytesused = output_size / 4; ++ break; ++ } ++ ++ vbuf->vb2_buf.timestamp = timestamp; ++ vbuf->sequence = sess->sequence_cap++; ++ ++ if (sess->should_stop && ++ atomic_read(&sess->esparser_queued_bufs) <= 2) { ++ const struct v4l2_event ev = { .type = V4L2_EVENT_EOS }; ++ ++ dev_dbg(dev, "Signaling EOS\n"); ++ v4l2_event_queue_fh(&sess->fh, &ev); ++ vbuf->flags |= V4L2_BUF_FLAG_LAST; ++ } else if (sess->should_stop) ++ dev_dbg(dev, "should_stop, %u bufs remain\n", ++ atomic_read(&sess->esparser_queued_bufs)); ++ ++ dev_dbg(dev, "Buffer %u done\n", vbuf->vb2_buf.index); ++ vbuf->field = field; ++ v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE); ++ ++ /* Buffer done probably means the vififo got freed */ ++ schedule_work(&sess->esparser_queue_work); ++} ++ ++void amvdec_dst_buf_done(struct amvdec_session *sess, ++ struct vb2_v4l2_buffer *vbuf, u32 field) ++{ ++ struct device *dev = sess->core->dev_dec; ++ struct amvdec_timestamp *tmp; ++ struct list_head *timestamps = &sess->timestamps; ++ u64 timestamp; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&sess->ts_spinlock, flags); ++ if (list_empty(timestamps)) { ++ dev_err(dev, "Buffer %u done but list is empty\n", ++ vbuf->vb2_buf.index); ++ ++ v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR); ++ spin_unlock_irqrestore(&sess->ts_spinlock, flags); ++ return; ++ } ++ ++ tmp = list_first_entry(timestamps, struct amvdec_timestamp, list); ++ timestamp = tmp->ts; ++ list_del(&tmp->list); ++ kfree(tmp); ++ spin_unlock_irqrestore(&sess->ts_spinlock, flags); ++ ++ dst_buf_done(sess, vbuf, field, timestamp); ++ atomic_dec(&sess->esparser_queued_bufs); ++} ++EXPORT_SYMBOL_GPL(amvdec_dst_buf_done); ++ ++static void amvdec_dst_buf_done_offset(struct amvdec_session *sess, ++ struct vb2_v4l2_buffer *vbuf, ++ u32 offset, ++ u32 field) ++{ ++ struct device *dev = sess->core->dev_dec; ++ struct amvdec_timestamp *match = NULL; ++ struct amvdec_timestamp *tmp, *n; ++ u64 timestamp = 0; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&sess->ts_spinlock, flags); ++ ++ /* Look for our vififo offset to get the corresponding timestamp. */ ++ list_for_each_entry_safe(tmp, n, &sess->timestamps, list) { ++ s64 delta = (s64)offset - tmp->offset; ++ ++ /* Offsets reported by codecs usually differ slightly, ++ * so we need some wiggle room. ++ * 4KiB being the minimum packet size, there is no risk here. ++ */ ++ if (delta > (-1 * (s32)SZ_4K) && delta < SZ_4K) { ++ match = tmp; ++ break; ++ } ++ ++ /* Delete any timestamp entry that appears before our target ++ * (not all src packets/timestamps lead to a frame) ++ */ ++ if (delta > 0 || delta < -1 * (s32)sess->vififo_size) { ++ atomic_dec(&sess->esparser_queued_bufs); ++ list_del(&tmp->list); ++ kfree(tmp); ++ } ++ } ++ ++ if (!match) { ++ dev_dbg(dev, "Buffer %u done but can't match offset (%08X)\n", ++ vbuf->vb2_buf.index, offset); ++ } else { ++ timestamp = match->ts; ++ list_del(&match->list); ++ kfree(match); ++ } ++ spin_unlock_irqrestore(&sess->ts_spinlock, flags); ++ ++ dst_buf_done(sess, vbuf, field, timestamp); ++ if (match) ++ atomic_dec(&sess->esparser_queued_bufs); ++} ++ ++void amvdec_dst_buf_done_idx(struct amvdec_session *sess, ++ u32 buf_idx, u32 offset, u32 field) ++{ ++ 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); ++ return; ++ } ++ ++ if (offset != -1) ++ amvdec_dst_buf_done_offset(sess, vbuf, offset, field); ++ else ++ amvdec_dst_buf_done(sess, vbuf, field); ++} ++EXPORT_SYMBOL_GPL(amvdec_dst_buf_done_idx); ++ ++void amvdec_set_par_from_dar(struct amvdec_session *sess, ++ u32 dar_num, u32 dar_den) ++{ ++ u32 div; ++ ++ sess->pixelaspect.numerator = sess->height * dar_num; ++ sess->pixelaspect.denominator = sess->width * dar_den; ++ div = gcd(sess->pixelaspect.numerator, sess->pixelaspect.denominator); ++ sess->pixelaspect.numerator /= div; ++ sess->pixelaspect.denominator /= div; ++} ++EXPORT_SYMBOL_GPL(amvdec_set_par_from_dar); ++ ++void amvdec_abort(struct amvdec_session *sess) ++{ ++ dev_info(sess->core->dev, "Aborting decoding session!\n"); ++ vb2_queue_error(&sess->m2m_ctx->cap_q_ctx.q); ++ vb2_queue_error(&sess->m2m_ctx->out_q_ctx.q); ++} ++EXPORT_SYMBOL_GPL(amvdec_abort); +diff --git a/drivers/media/platform/meson/vdec/vdec_helpers.h b/drivers/media/platform/meson/vdec/vdec_helpers.h +new file mode 100644 +index 0000000..b9250a8 +--- /dev/null ++++ b/drivers/media/platform/meson/vdec/vdec_helpers.h +@@ -0,0 +1,48 @@ ++/* SPDX-License-Identifier: GPL-2.0+ */ ++/* ++ * Copyright (C) 2018 BayLibre, SAS ++ * Author: Maxime Jourdan <mjourdan@baylibre.com> ++ */ ++ ++#ifndef __MESON_VDEC_HELPERS_H_ ++#define __MESON_VDEC_HELPERS_H_ ++ ++#include "vdec.h" ++ ++/** ++ * amvdec_set_canvases() - Map VB2 buffers to canvases ++ * ++ * @sess: current session ++ * @reg_base: Registry bases of where to write the canvas indexes ++ * @reg_num: number of contiguous registers after each reg_base (including it) ++ */ ++int amvdec_set_canvases(struct amvdec_session *sess, ++ u32 reg_base[], u32 reg_num[]); ++ ++u32 amvdec_read_dos(struct amvdec_core *core, u32 reg); ++void amvdec_write_dos(struct amvdec_core *core, u32 reg, u32 val); ++void amvdec_write_dos_bits(struct amvdec_core *core, u32 reg, u32 val); ++void amvdec_clear_dos_bits(struct amvdec_core *core, u32 reg, u32 val); ++u32 amvdec_read_parser(struct amvdec_core *core, u32 reg); ++void amvdec_write_parser(struct amvdec_core *core, u32 reg, u32 val); ++ ++void amvdec_dst_buf_done_idx(struct amvdec_session *sess, u32 buf_idx, ++ u32 offset, u32 field); ++void amvdec_dst_buf_done(struct amvdec_session *sess, ++ struct vb2_v4l2_buffer *vbuf, u32 field); ++ ++/** ++ * amvdec_add_ts_reorder() - Add a timestamp to the list in chronological order ++ * ++ * @sess: current session ++ * @ts: timestamp to add ++ * @offset: offset in the VIFIFO where the associated packet was written ++ */ ++void amvdec_add_ts_reorder(struct amvdec_session *sess, u64 ts, u32 offset); ++void amvdec_remove_ts(struct amvdec_session *sess, u64 ts); ++ ++void amvdec_set_par_from_dar(struct amvdec_session *sess, ++ u32 dar_num, u32 dar_den); ++ ++void amvdec_abort(struct amvdec_session *sess); ++#endif +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..46eeb74 +--- /dev/null ++++ b/drivers/media/platform/meson/vdec/vdec_platform.c +@@ -0,0 +1,101 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Copyright (C) 2018 BayLibre, SAS ++ * Author: Maxime Jourdan <mjourdan@baylibre.com> ++ */ ++ ++#include "vdec_platform.h" ++#include "vdec.h" ++ ++#include "vdec_1.h" ++#include "codec_mpeg12.h" ++ ++static const struct amvdec_format vdec_formats_gxbb[] = { ++ { ++ .pixfmt = V4L2_PIX_FMT_MPEG1, ++ .min_buffers = 8, ++ .max_buffers = 8, ++ .max_width = 1920, ++ .max_height = 1080, ++ .vdec_ops = &vdec_1_ops, ++ .codec_ops = &codec_mpeg12_ops, ++ .firmware_path = "meson/gx/vmpeg12_mc", ++ .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, ++ }, { ++ .pixfmt = V4L2_PIX_FMT_MPEG2, ++ .min_buffers = 8, ++ .max_buffers = 8, ++ .max_width = 1920, ++ .max_height = 1080, ++ .vdec_ops = &vdec_1_ops, ++ .codec_ops = &codec_mpeg12_ops, ++ .firmware_path = "meson/gx/vmpeg12_mc", ++ .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, ++ }, ++}; ++ ++static const struct amvdec_format vdec_formats_gxl[] = { ++ { ++ .pixfmt = V4L2_PIX_FMT_MPEG1, ++ .min_buffers = 8, ++ .max_buffers = 8, ++ .max_width = 1920, ++ .max_height = 1080, ++ .vdec_ops = &vdec_1_ops, ++ .codec_ops = &codec_mpeg12_ops, ++ .firmware_path = "meson/gx/vmpeg12_mc", ++ .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, ++ }, { ++ .pixfmt = V4L2_PIX_FMT_MPEG2, ++ .min_buffers = 8, ++ .max_buffers = 8, ++ .max_width = 1920, ++ .max_height = 1080, ++ .vdec_ops = &vdec_1_ops, ++ .codec_ops = &codec_mpeg12_ops, ++ .firmware_path = "meson/gx/vmpeg12_mc", ++ .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, ++ }, ++}; ++ ++static const struct amvdec_format vdec_formats_gxm[] = { ++ { ++ .pixfmt = V4L2_PIX_FMT_MPEG1, ++ .min_buffers = 8, ++ .max_buffers = 8, ++ .max_width = 1920, ++ .max_height = 1080, ++ .vdec_ops = &vdec_1_ops, ++ .codec_ops = &codec_mpeg12_ops, ++ .firmware_path = "meson/gx/vmpeg12_mc", ++ .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, ++ }, { ++ .pixfmt = V4L2_PIX_FMT_MPEG2, ++ .min_buffers = 8, ++ .max_buffers = 8, ++ .max_width = 1920, ++ .max_height = 1080, ++ .vdec_ops = &vdec_1_ops, ++ .codec_ops = &codec_mpeg12_ops, ++ .firmware_path = "meson/gx/vmpeg12_mc", ++ .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, ++ }, ++}; ++ ++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, ++}; +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..f602532 +--- /dev/null ++++ b/drivers/media/platform/meson/vdec/vdec_platform.h +@@ -0,0 +1,30 @@ ++/* SPDX-License-Identifier: GPL-2.0+ */ ++/* ++ * Copyright (C) 2018 BayLibre, SAS ++ * Author: Maxime Jourdan <mjourdan@baylibre.com> ++ */ ++ ++#ifndef __MESON_VDEC_PLATFORM_H_ ++#define __MESON_VDEC_PLATFORM_H_ ++ ++#include "vdec.h" ++ ++struct amvdec_format; ++ ++enum vdec_revision { ++ VDEC_REVISION_GXBB, ++ VDEC_REVISION_GXL, ++ VDEC_REVISION_GXM, ++}; ++ ++struct vdec_platform { ++ const struct amvdec_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 diff --git a/testing/linux-amlogic/0034-MAINTAINERS-Add-meson-video-decoder.patch b/testing/linux-amlogic/0034-MAINTAINERS-Add-meson-video-decoder.patch new file mode 100644 index 0000000000..76a6a3826b --- /dev/null +++ b/testing/linux-amlogic/0034-MAINTAINERS-Add-meson-video-decoder.patch @@ -0,0 +1,32 @@ +From a4089c61b05e61349ff10539fc20ee480d44b303 Mon Sep 17 00:00:00 2001 +From: Maxime Jourdan <mjourdan@baylibre.com> +Date: Tue, 4 Sep 2018 10:07:08 +0200 +Subject: [PATCH] MAINTAINERS: Add meson video decoder + +Add an entry for the meson video decoder for amlogic SoCs. + +Signed-off-by: Maxime Jourdan <mjourdan@baylibre.com> + +--- + MAINTAINERS | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/MAINTAINERS b/MAINTAINERS +index 9e9b19e..ede389b 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -9520,6 +9520,14 @@ F: drivers/media/platform/meson/ao-cec.c + F: Documentation/devicetree/bindings/media/meson-ao-cec.txt + T: git git://linuxtv.org/media_tree.git + ++MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS ++M: Maxime Jourdan <mjourdan@baylibre.com> ++L: linux-media@lists.freedesktop.org ++L: linux-amlogic@lists.infradead.org ++S: Supported ++F: drivers/media/platform/meson/vdec/ ++T: git git://linuxtv.org/media_tree.git ++ + MICROBLAZE ARCHITECTURE + M: Michal Simek <monstr@monstr.eu> + W: http://www.monstr.eu/fdt/ diff --git a/testing/linux-amlogic/0035-arm64-dts-meson-gx-add-vdec-entry.patch b/testing/linux-amlogic/0035-arm64-dts-meson-gx-add-vdec-entry.patch new file mode 100644 index 0000000000..4b1f168f7e --- /dev/null +++ b/testing/linux-amlogic/0035-arm64-dts-meson-gx-add-vdec-entry.patch @@ -0,0 +1,38 @@ +From 771e9830bdd1361594a874ded57fe497f443d7dd Mon Sep 17 00:00:00 2001 +From: Maxime Jourdan <mjourdan@baylibre.com> +Date: Wed, 29 Aug 2018 15:24:02 +0200 +Subject: [PATCH] arm64: dts: meson-gx: add vdec entry + +Add the video decoder dts entry + +Signed-off-by: Maxime Jourdan <mjourdan@baylibre.com> + +--- + 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 5012607..5d2820e 100644 +--- a/arch/arm64/boot/dts/amlogic/meson-gx.dtsi ++++ b/arch/arm64/boot/dts/amlogic/meson-gx.dtsi +@@ -445,6 +445,20 @@ + }; + }; + ++ vdec: video-decoder@c8820000 { ++ compatible = "amlogic,gx-vdec"; ++ reg = <0x0 0xc8820000 0x0 0x10000>, ++ <0x0 0xc110a580 0x0 0xe4>; ++ reg-names = "dos", "esparser"; ++ ++ interrupts = <GIC_SPI 44 IRQ_TYPE_EDGE_RISING>, ++ <GIC_SPI 32 IRQ_TYPE_EDGE_RISING>; ++ interrupt-names = "vdec", "esparser"; ++ ++ amlogic,ao-sysctrl = <&sysctrl_AO>; ++ amlogic,canvas = <&canvas>; ++ }; ++ + periphs: periphs@c8834000 { + compatible = "simple-bus"; + reg = <0x0 0xc8834000 0x0 0x2000>; diff --git a/testing/linux-amlogic/0001-ARM64-dts-meson-add-vdec-entries.patch b/testing/linux-amlogic/0036-arm64-dts-meson-add-vdec-entries.patch index 861623ad33..0770272005 100644 --- a/testing/linux-amlogic/0001-ARM64-dts-meson-add-vdec-entries.patch +++ b/testing/linux-amlogic/0036-arm64-dts-meson-add-vdec-entries.patch @@ -1,47 +1,56 @@ -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 +From 75863ab0baf29e4b0212a34f9ab7ef2763a38824 Mon Sep 17 00:00:00 2001 +From: Maxime Jourdan <mjourdan@baylibre.com> +Date: Wed, 29 Aug 2018 15:24:22 +0200 +Subject: [PATCH] arm64: dts: meson: add vdec entries This enables the video decoder for gxbb, gxl and gxm chips + +Signed-off-by: Maxime Jourdan <mjourdan@baylibre.com> + --- - 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(+) + arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi | 10 ++++++++++ + arch/arm64/boot/dts/amlogic/meson-gxl.dtsi | 10 ++++++++++ + arch/arm64/boot/dts/amlogic/meson-gxm.dtsi | 4 ++++ + 3 files changed, 24 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 +index 2a4d506..96145e4 100644 --- a/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi +++ b/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi -@@ -814,3 +814,10 @@ +@@ -814,3 +814,13 @@ 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>; ++ compatible = "amlogic,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 +index 9f4b618..6ca93ae 100644 --- a/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi +++ b/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi -@@ -814,3 +814,10 @@ +@@ -814,3 +814,13 @@ 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>; ++ compatible = "amlogic,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 +index 247888d..2f35649 100644 --- a/arch/arm64/boot/dts/amlogic/meson-gxm.dtsi +++ b/arch/arm64/boot/dts/amlogic/meson-gxm.dtsi @@ -117,3 +117,7 @@ @@ -50,5 +59,5 @@ index 247888d..4ce7b12 100644 }; + +&vdec { -+ compatible = "amlogic,meson-gxm-vdec"; ++ compatible = "amlogic,gxm-vdec"; +}; diff --git a/testing/linux-amlogic/0037-meson-vdec-introduce-controls-and-V4L2_CID_MIN_BUFFE.patch b/testing/linux-amlogic/0037-meson-vdec-introduce-controls-and-V4L2_CID_MIN_BUFFE.patch new file mode 100644 index 0000000000..3c120117b1 --- /dev/null +++ b/testing/linux-amlogic/0037-meson-vdec-introduce-controls-and-V4L2_CID_MIN_BUFFE.patch @@ -0,0 +1,153 @@ +From e36802c6297adb0c560f3a6c1672546eb380c458 Mon Sep 17 00:00:00 2001 +From: Maxime Jourdan <mjourdan@baylibre.com> +Date: Wed, 10 Oct 2018 17:22:27 +0200 +Subject: [PATCH] meson: vdec: introduce controls and + V4L2_CID_MIN_BUFFERS_FOR_CAPTURE + +--- + drivers/media/platform/meson/vdec/Makefile | 2 +- + drivers/media/platform/meson/vdec/vdec.c | 7 ++++ + drivers/media/platform/meson/vdec/vdec.h | 2 ++ + drivers/media/platform/meson/vdec/vdec_ctrls.c | 45 ++++++++++++++++++++++++++ + drivers/media/platform/meson/vdec/vdec_ctrls.h | 8 +++++ + 5 files changed, 63 insertions(+), 1 deletion(-) + create mode 100644 drivers/media/platform/meson/vdec/vdec_ctrls.c + create mode 100644 drivers/media/platform/meson/vdec/vdec_ctrls.h + +diff --git a/drivers/media/platform/meson/vdec/Makefile b/drivers/media/platform/meson/vdec/Makefile +index 6bea129..eba8608 100644 +--- a/drivers/media/platform/meson/vdec/Makefile ++++ b/drivers/media/platform/meson/vdec/Makefile +@@ -1,7 +1,7 @@ + # SPDX-License-Identifier: GPL-2.0 + # Makefile for Amlogic meson video decoder driver + +-meson-vdec-objs = esparser.o vdec.o vdec_helpers.o vdec_platform.o ++meson-vdec-objs = esparser.o vdec.o vdec_ctrls.o vdec_helpers.o vdec_platform.o + meson-vdec-objs += vdec_1.o + meson-vdec-objs += codec_mpeg12.o + +diff --git a/drivers/media/platform/meson/vdec/vdec.c b/drivers/media/platform/meson/vdec/vdec.c +index d8db52c..1c5d3e9 100644 +--- a/drivers/media/platform/meson/vdec/vdec.c ++++ b/drivers/media/platform/meson/vdec/vdec.c +@@ -21,6 +21,7 @@ + #include "vdec.h" + #include "esparser.h" + #include "vdec_helpers.h" ++#include "vdec_ctrls.h" + + struct dummy_buf { + struct vb2_v4l2_buffer vb; +@@ -290,6 +291,7 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count) + sess->keyframe_found = 0; + sess->last_offset = 0; + sess->wrap_count = 0; ++ sess->dpb_size = 0; + sess->pixelaspect.numerator = 1; + sess->pixelaspect.denominator = 1; + atomic_set(&sess->esparser_queued_bufs, 0); +@@ -812,6 +814,10 @@ static int vdec_open(struct file *file) + goto err_m2m_release; + } + ++ ret = amvdec_init_ctrls(&sess->ctrl_handler); ++ if (ret) ++ goto err_m2m_release; ++ + sess->pixfmt_cap = formats[0].pixfmts_cap[0]; + sess->fmt_out = &formats[0]; + sess->width = 1280; +@@ -827,6 +833,7 @@ static int vdec_open(struct file *file) + spin_lock_init(&sess->ts_spinlock); + + v4l2_fh_init(&sess->fh, core->vdev_dec); ++ sess->fh.ctrl_handler = &sess->ctrl_handler; + v4l2_fh_add(&sess->fh); + sess->fh.m2m_ctx = sess->m2m_ctx; + file->private_data = &sess->fh; +diff --git a/drivers/media/platform/meson/vdec/vdec.h b/drivers/media/platform/meson/vdec/vdec.h +index 4e8c3f1..6be7de2 100644 +--- a/drivers/media/platform/meson/vdec/vdec.h ++++ b/drivers/media/platform/meson/vdec/vdec.h +@@ -203,6 +203,7 @@ struct amvdec_session { + struct v4l2_fh fh; + struct v4l2_m2m_dev *m2m_dev; + struct v4l2_m2m_ctx *m2m_ctx; ++ struct v4l2_ctrl_handler ctrl_handler; + struct mutex lock; + + const struct amvdec_format *fmt_out; +@@ -242,6 +243,7 @@ struct amvdec_session { + u64 last_irq_jiffies; + u32 last_offset; + u32 wrap_count; ++ u32 dpb_size; + + void *priv; + }; +diff --git a/drivers/media/platform/meson/vdec/vdec_ctrls.c b/drivers/media/platform/meson/vdec/vdec_ctrls.c +new file mode 100644 +index 0000000..cd6dd6d +--- /dev/null ++++ b/drivers/media/platform/meson/vdec/vdec_ctrls.c +@@ -0,0 +1,45 @@ ++#include "vdec_ctrls.h" ++ ++static int vdec_op_g_volatile_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct amvdec_session *sess = ++ container_of(ctrl->handler, struct amvdec_session, ctrl_handler); ++ ++ switch (ctrl->id) { ++ case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE: ++ ctrl->val = sess->dpb_size; ++ break; ++ default: ++ return -EINVAL; ++ }; ++ ++ return 0; ++} ++ ++static const struct v4l2_ctrl_ops vdec_ctrl_ops = { ++ .g_volatile_ctrl = vdec_op_g_volatile_ctrl, ++}; ++ ++int amvdec_init_ctrls(struct v4l2_ctrl_handler *ctrl_handler) ++{ ++ int ret; ++ struct v4l2_ctrl *ctrl; ++ ++ ret = v4l2_ctrl_handler_init(ctrl_handler, 1); ++ if (ret) ++ return ret; ++ ++ ctrl = v4l2_ctrl_new_std(ctrl_handler, &vdec_ctrl_ops, ++ V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, 1, 32, 1, 1); ++ if (ctrl) ++ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; ++ ++ ret = ctrl_handler->error; ++ if (ret) { ++ v4l2_ctrl_handler_free(ctrl_handler); ++ return ret; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(amvdec_init_ctrls); +diff --git a/drivers/media/platform/meson/vdec/vdec_ctrls.h b/drivers/media/platform/meson/vdec/vdec_ctrls.h +new file mode 100644 +index 0000000..4bcc5e6 +--- /dev/null ++++ b/drivers/media/platform/meson/vdec/vdec_ctrls.h +@@ -0,0 +1,8 @@ ++#ifndef __MESON_VDEC_CTRLS_H_ ++#define __MESON_VDEC_CTRLS_H_ ++ ++#include "vdec.h" ++ ++int amvdec_init_ctrls(struct v4l2_ctrl_handler *ctrl_handler); ++ ++#endif diff --git a/testing/linux-amlogic/0038-media-videodev2-add-V4L2_FMT_FLAG_NO_SOURCE_CHANGE.patch b/testing/linux-amlogic/0038-media-videodev2-add-V4L2_FMT_FLAG_NO_SOURCE_CHANGE.patch new file mode 100644 index 0000000000..7229bb2ec6 --- /dev/null +++ b/testing/linux-amlogic/0038-media-videodev2-add-V4L2_FMT_FLAG_NO_SOURCE_CHANGE.patch @@ -0,0 +1,49 @@ +From 80f9e75b2fce22552fdf56ade990f3f98567e29e Mon Sep 17 00:00:00 2001 +From: Maxime Jourdan <mjourdan@baylibre.com> +Date: Thu, 4 Oct 2018 15:37:39 +0200 +Subject: [PATCH] media: videodev2: add V4L2_FMT_FLAG_NO_SOURCE_CHANGE + +When a v4l2 driver exposes V4L2_EVENT_SOURCE_CHANGE, some (usually +OUTPUT) formats may not be able to trigger this event. + +Add a enum_fmt format flag to tag those specific formats. + +Signed-off-by: Maxime Jourdan <mjourdan@baylibre.com> + +--- + Documentation/media/uapi/v4l/vidioc-enum-fmt.rst | 5 +++++ + include/uapi/linux/videodev2.h | 5 +++-- + 2 files changed, 8 insertions(+), 2 deletions(-) + +diff --git a/Documentation/media/uapi/v4l/vidioc-enum-fmt.rst b/Documentation/media/uapi/v4l/vidioc-enum-fmt.rst +index 019c513..e0040b3 100644 +--- a/Documentation/media/uapi/v4l/vidioc-enum-fmt.rst ++++ b/Documentation/media/uapi/v4l/vidioc-enum-fmt.rst +@@ -116,6 +116,11 @@ one until ``EINVAL`` is returned. + - This format is not native to the device but emulated through + software (usually libv4l2), where possible try to use a native + format instead for better performance. ++ * - ``V4L2_FMT_FLAG_NO_SOURCE_CHANGE`` ++ - 0x0004 ++ - The event ``V4L2_EVENT_SOURCE_CHANGE`` is not supported ++ for this format. ++ + + + Return Value +diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h +index 1aae2e4..f44bdef 100644 +--- a/include/uapi/linux/videodev2.h ++++ b/include/uapi/linux/videodev2.h +@@ -733,8 +733,9 @@ struct v4l2_fmtdesc { + __u32 reserved[4]; + }; + +-#define V4L2_FMT_FLAG_COMPRESSED 0x0001 +-#define V4L2_FMT_FLAG_EMULATED 0x0002 ++#define V4L2_FMT_FLAG_COMPRESSED 0x0001 ++#define V4L2_FMT_FLAG_EMULATED 0x0002 ++#define V4L2_FMT_FLAG_NO_SOURCE_CHANGE 0x0004 + + /* Frame Size and frame rate enumeration */ + /* diff --git a/testing/linux-amlogic/0039-meson-vdec-allow-subscribing-to-V4L2_EVENT_SOURCE_CH.patch b/testing/linux-amlogic/0039-meson-vdec-allow-subscribing-to-V4L2_EVENT_SOURCE_CH.patch new file mode 100644 index 0000000000..4a29d88391 --- /dev/null +++ b/testing/linux-amlogic/0039-meson-vdec-allow-subscribing-to-V4L2_EVENT_SOURCE_CH.patch @@ -0,0 +1,270 @@ +From 4ba289cf4940b6b8ddf1e332fc7248a27f54cfc8 Mon Sep 17 00:00:00 2001 +From: Maxime Jourdan <mjourdan@baylibre.com> +Date: Wed, 10 Oct 2018 15:44:56 +0200 +Subject: [PATCH] meson: vdec: allow subscribing to V4L2_EVENT_SOURCE_CHANGE + +Flag MPEG1/MPEG2 as NO_SOURCE_CHANGE. + +--- + drivers/media/platform/meson/vdec/vdec.c | 20 ++++++++++++++-- + drivers/media/platform/meson/vdec/vdec.h | 13 +++++++++++ + drivers/media/platform/meson/vdec/vdec_helpers.c | 28 +++++++++++++++++++++++ + drivers/media/platform/meson/vdec/vdec_helpers.h | 1 + + drivers/media/platform/meson/vdec/vdec_platform.c | 6 +++++ + 5 files changed, 66 insertions(+), 2 deletions(-) + +diff --git a/drivers/media/platform/meson/vdec/vdec.c b/drivers/media/platform/meson/vdec/vdec.c +index 1c5d3e9..ca64045 100644 +--- a/drivers/media/platform/meson/vdec/vdec.c ++++ b/drivers/media/platform/meson/vdec/vdec.c +@@ -230,7 +230,8 @@ static int vdec_queue_setup(struct vb2_queue *q, + * are free to choose any of them to write frames to. As such, + * we need all of them to be queued into the driver + */ +- q->min_buffers_needed = q->num_buffers + *num_buffers; ++ sess->num_dst_bufs = q->num_buffers + *num_buffers; ++ q->min_buffers_needed = sess->num_dst_bufs; + break; + default: + return -EINVAL; +@@ -260,6 +261,7 @@ static void vdec_vb2_buf_queue(struct vb2_buffer *vb) + static int vdec_start_streaming(struct vb2_queue *q, unsigned int count) + { + struct amvdec_session *sess = vb2_get_drv_priv(q); ++ struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops; + struct amvdec_core *core = sess->core; + struct vb2_v4l2_buffer *buf; + int ret; +@@ -277,6 +279,13 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count) + if (!sess->streamon_out || !sess->streamon_cap) + return 0; + ++ if (sess->status == STATUS_NEEDS_RESUME && ++ q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { ++ codec_ops->resume(sess); ++ sess->status = STATUS_RUNNING; ++ return 0; ++ } ++ + sess->vififo_size = SIZE_VIFIFO; + sess->vififo_vaddr = + dma_alloc_coherent(sess->core->dev, sess->vififo_size, +@@ -305,6 +314,7 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count) + sess->recycle_thread = kthread_run(vdec_recycle_thread, sess, + "vdec_recycle"); + ++ sess->status = STATUS_RUNNING; + core->cur_sess = sess; + + return 0; +@@ -362,7 +372,9 @@ static void vdec_stop_streaming(struct vb2_queue *q) + struct amvdec_core *core = sess->core; + struct vb2_v4l2_buffer *buf; + +- if (sess->streamon_out && sess->streamon_cap) { ++ if (sess->status == STATUS_RUNNING || ++ (sess->status == STATUS_NEEDS_RESUME && ++ (!sess->streamon_out || !sess->streamon_cap))) { + if (vdec_codec_needs_recycle(sess)) + kthread_stop(sess->recycle_thread); + +@@ -375,6 +387,7 @@ static void vdec_stop_streaming(struct vb2_queue *q) + kfree(sess->priv); + sess->priv = NULL; + core->cur_sess = NULL; ++ sess->status = STATUS_STOPPED; + } + + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { +@@ -611,6 +624,7 @@ static int vdec_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f) + + fmt_out = &platform->formats[f->index]; + f->pixelformat = fmt_out->pixfmt; ++ f->flags = fmt_out->flags; + } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + fmt_out = sess->fmt_out; + if (f->index >= 4 || !fmt_out->pixfmts_cap[f->index]) +@@ -703,6 +717,8 @@ static int vdec_subscribe_event(struct v4l2_fh *fh, + switch (sub->type) { + case V4L2_EVENT_EOS: + return v4l2_event_subscribe(fh, sub, 2, NULL); ++ case V4L2_EVENT_SOURCE_CHANGE: ++ return v4l2_src_change_event_subscribe(fh, sub); + default: + return -EINVAL; + } +diff --git a/drivers/media/platform/meson/vdec/vdec.h b/drivers/media/platform/meson/vdec/vdec.h +index 6be7de2..8f8ce62 100644 +--- a/drivers/media/platform/meson/vdec/vdec.h ++++ b/drivers/media/platform/meson/vdec/vdec.h +@@ -101,6 +101,7 @@ struct amvdec_ops { + u32 (*vififo_level)(struct amvdec_session *sess); + }; + ++ + /** + * struct amvdec_codec_ops - codec operations + * +@@ -127,6 +128,7 @@ struct amvdec_codec_ops { + int (*can_recycle)(struct amvdec_core *core); + void (*recycle)(struct amvdec_core *core, u32 buf_idx); + void (*drain)(struct amvdec_session *sess); ++ void (*resume)(struct amvdec_session *sess); + const u8 * (*eos_sequence)(u32 *len); + irqreturn_t (*isr)(struct amvdec_session *sess); + irqreturn_t (*threaded_isr)(struct amvdec_session *sess); +@@ -140,6 +142,7 @@ struct amvdec_codec_ops { + * @max_buffers: maximum amount of CAPTURE (dst) buffers + * @max_width: maximum picture width supported + * @max_height: maximum picture height supported ++ * @flags: enum flags associated with this pixfmt + * @vdec_ops: the VDEC operations that support this format + * @codec_ops: the codec operations that support this format + * @firmware_path: Path to the firmware that supports this format +@@ -151,6 +154,7 @@ struct amvdec_format { + u32 max_buffers; + u32 max_width; + u32 max_height; ++ u32 flags; + + struct amvdec_ops *vdec_ops; + struct amvdec_codec_ops *codec_ops; +@@ -159,6 +163,12 @@ struct amvdec_format { + u32 pixfmts_cap[4]; + }; + ++enum amvdec_status { ++ STATUS_STOPPED, ++ STATUS_RUNNING, ++ STATUS_NEEDS_RESUME, ++}; ++ + /** + * struct amvdec_session - decoding session parameters + * +@@ -195,6 +205,7 @@ struct amvdec_format { + * @timestamps: chronological list of src timestamps + * @ts_spinlock: spinlock for the timestamps list + * @last_irq_jiffies: tracks last time the vdec triggered an IRQ ++ * @status: current decoding status + * @priv: codec private data + */ + struct amvdec_session { +@@ -225,6 +236,7 @@ struct amvdec_session { + unsigned int sequence_cap; + unsigned int should_stop; + unsigned int keyframe_found; ++ unsigned int num_dst_bufs; + + u8 canvas_alloc[MAX_CANVAS]; + u32 canvas_num; +@@ -245,6 +257,7 @@ struct amvdec_session { + u32 wrap_count; + u32 dpb_size; + ++ enum amvdec_status status; + void *priv; + }; + +diff --git a/drivers/media/platform/meson/vdec/vdec_helpers.c b/drivers/media/platform/meson/vdec/vdec_helpers.c +index 02090c5..b982b28 100644 +--- a/drivers/media/platform/meson/vdec/vdec_helpers.c ++++ b/drivers/media/platform/meson/vdec/vdec_helpers.c +@@ -403,6 +403,34 @@ void amvdec_set_par_from_dar(struct amvdec_session *sess, + } + EXPORT_SYMBOL_GPL(amvdec_set_par_from_dar); + ++void amvdec_src_change(struct amvdec_session *sess, u32 width, u32 height, u32 dpb_size) ++{ ++ static const struct v4l2_event ev = { ++ .type = V4L2_EVENT_SOURCE_CHANGE, ++ .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION }; ++ ++ sess->dpb_size = dpb_size; ++ ++ /* Check if the capture queue is already configured well for our ++ * usecase. If so, keep decoding with it and do not send the event ++ */ ++ if (sess->width == width && ++ sess->height == height && ++ dpb_size <= sess->num_dst_bufs) { ++ sess->fmt_out->codec_ops->resume(sess); ++ return; ++ } ++ ++ sess->width = width; ++ sess->height = height; ++ sess->status = STATUS_NEEDS_RESUME; ++ ++ dev_dbg(sess->core->dev, "Res. changed (%ux%u), DPB size %u\n", ++ width, height, dpb_size); ++ v4l2_event_queue_fh(&sess->fh, &ev); ++} ++EXPORT_SYMBOL_GPL(amvdec_src_change); ++ + void amvdec_abort(struct amvdec_session *sess) + { + dev_info(sess->core->dev, "Aborting decoding session!\n"); +diff --git a/drivers/media/platform/meson/vdec/vdec_helpers.h b/drivers/media/platform/meson/vdec/vdec_helpers.h +index b9250a8..060799b 100644 +--- a/drivers/media/platform/meson/vdec/vdec_helpers.h ++++ b/drivers/media/platform/meson/vdec/vdec_helpers.h +@@ -44,5 +44,6 @@ void amvdec_remove_ts(struct amvdec_session *sess, u64 ts); + void amvdec_set_par_from_dar(struct amvdec_session *sess, + u32 dar_num, u32 dar_den); + ++void amvdec_src_change(struct amvdec_session *sess, u32 width, u32 height, u32 dpb_size); + void amvdec_abort(struct amvdec_session *sess); + #endif +diff --git a/drivers/media/platform/meson/vdec/vdec_platform.c b/drivers/media/platform/meson/vdec/vdec_platform.c +index 46eeb74..291f1ee 100644 +--- a/drivers/media/platform/meson/vdec/vdec_platform.c ++++ b/drivers/media/platform/meson/vdec/vdec_platform.c +@@ -17,6 +17,7 @@ static const struct amvdec_format vdec_formats_gxbb[] = { + .max_buffers = 8, + .max_width = 1920, + .max_height = 1080, ++ .flags = V4L2_FMT_FLAG_NO_SOURCE_CHANGE, + .vdec_ops = &vdec_1_ops, + .codec_ops = &codec_mpeg12_ops, + .firmware_path = "meson/gx/vmpeg12_mc", +@@ -27,6 +28,7 @@ static const struct amvdec_format vdec_formats_gxbb[] = { + .max_buffers = 8, + .max_width = 1920, + .max_height = 1080, ++ .flags = V4L2_FMT_FLAG_NO_SOURCE_CHANGE, + .vdec_ops = &vdec_1_ops, + .codec_ops = &codec_mpeg12_ops, + .firmware_path = "meson/gx/vmpeg12_mc", +@@ -41,6 +43,7 @@ static const struct amvdec_format vdec_formats_gxl[] = { + .max_buffers = 8, + .max_width = 1920, + .max_height = 1080, ++ .flags = V4L2_FMT_FLAG_NO_SOURCE_CHANGE, + .vdec_ops = &vdec_1_ops, + .codec_ops = &codec_mpeg12_ops, + .firmware_path = "meson/gx/vmpeg12_mc", +@@ -51,6 +54,7 @@ static const struct amvdec_format vdec_formats_gxl[] = { + .max_buffers = 8, + .max_width = 1920, + .max_height = 1080, ++ .flags = V4L2_FMT_FLAG_NO_SOURCE_CHANGE, + .vdec_ops = &vdec_1_ops, + .codec_ops = &codec_mpeg12_ops, + .firmware_path = "meson/gx/vmpeg12_mc", +@@ -65,6 +69,7 @@ static const struct amvdec_format vdec_formats_gxm[] = { + .max_buffers = 8, + .max_width = 1920, + .max_height = 1080, ++ .flags = V4L2_FMT_FLAG_NO_SOURCE_CHANGE, + .vdec_ops = &vdec_1_ops, + .codec_ops = &codec_mpeg12_ops, + .firmware_path = "meson/gx/vmpeg12_mc", +@@ -75,6 +80,7 @@ static const struct amvdec_format vdec_formats_gxm[] = { + .max_buffers = 8, + .max_width = 1920, + .max_height = 1080, ++ .flags = V4L2_FMT_FLAG_NO_SOURCE_CHANGE, + .vdec_ops = &vdec_1_ops, + .codec_ops = &codec_mpeg12_ops, + .firmware_path = "meson/gx/vmpeg12_mc", diff --git a/testing/linux-amlogic/0040-media-meson-vdec-add-H.264-decoding-support.patch b/testing/linux-amlogic/0040-media-meson-vdec-add-H.264-decoding-support.patch new file mode 100644 index 0000000000..aa66ec743e --- /dev/null +++ b/testing/linux-amlogic/0040-media-meson-vdec-add-H.264-decoding-support.patch @@ -0,0 +1,591 @@ +From a41f52edc3681c11517c4d7ceb374f3189ea3310 Mon Sep 17 00:00:00 2001 +From: Maxime Jourdan <mjourdan@baylibre.com> +Date: Wed, 29 Aug 2018 15:42:56 +0200 +Subject: [PATCH] media: meson: vdec: add H.264 decoding support + +Add support for V4L2_PIX_FMT_H264 + +--- + drivers/media/platform/meson/vdec/Makefile | 2 +- + drivers/media/platform/meson/vdec/codec_h264.c | 478 ++++++++++++++++++++++ + drivers/media/platform/meson/vdec/codec_h264.h | 13 + + drivers/media/platform/meson/vdec/vdec_platform.c | 31 ++ + 4 files changed, 523 insertions(+), 1 deletion(-) + create mode 100644 drivers/media/platform/meson/vdec/codec_h264.c + create mode 100644 drivers/media/platform/meson/vdec/codec_h264.h + +diff --git a/drivers/media/platform/meson/vdec/Makefile b/drivers/media/platform/meson/vdec/Makefile +index eba8608..01dc960 100644 +--- a/drivers/media/platform/meson/vdec/Makefile ++++ b/drivers/media/platform/meson/vdec/Makefile +@@ -3,6 +3,6 @@ + + meson-vdec-objs = esparser.o vdec.o vdec_ctrls.o vdec_helpers.o vdec_platform.o + meson-vdec-objs += vdec_1.o +-meson-vdec-objs += codec_mpeg12.o ++meson-vdec-objs += codec_mpeg12.o codec_h264.o + + obj-$(CONFIG_VIDEO_MESON_VDEC) += meson-vdec.o +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..6ac0115 +--- /dev/null ++++ b/drivers/media/platform/meson/vdec/codec_h264.c +@@ -0,0 +1,478 @@ ++// 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 "vdec_helpers.h" ++#include "dos_regs.h" ++ ++#define SIZE_EXT_FW (20 * SZ_1K) ++#define SIZE_WORKSPACE 0x1ee000 ++#define SIZE_SEI (8 * SZ_1K) ++ ++/* Offset added by the firmware which must be substracted ++ * from the workspace phyaddr ++ */ ++#define WORKSPACE_BUF_OFFSET 0x1000000 ++ ++/* ISR status */ ++#define CMD_MASK GENMASK(7, 0) ++#define CMD_SRC_CHANGE 1 ++#define CMD_FRAMES_READY 2 ++#define CMD_FATAL_ERROR 6 ++#define CMD_BAD_WIDTH 7 ++#define CMD_BAD_HEIGHT 8 ++ ++#define SEI_DATA_READY BIT(15) ++ ++/* Picture type */ ++#define PIC_TOP_BOT 5 ++#define PIC_BOT_TOP 6 ++ ++/* Size of Motion Vector per macroblock */ ++#define MB_MV_SIZE 96 ++ ++/* Frame status data */ ++#define PIC_STRUCT_BIT 5 ++#define PIC_STRUCT_MASK GENMASK(2, 0) ++#define BUF_IDX_MASK GENMASK(4, 0) ++#define ERROR_FLAG BIT(9) ++#define OFFSET_BIT 16 ++#define OFFSET_MASK GENMASK(15, 0) ++ ++/* Bitstream parsed data */ ++#define MB_TOTAL_BIT 8 ++#define MB_TOTAL_MASK GENMASK(15, 0) ++#define MB_WIDTH_MASK GENMASK(7, 0) ++#define MAX_REF_BIT 24 ++#define MAX_REF_MASK GENMASK(6, 0) ++#define AR_IDC_BIT 16 ++#define AR_IDC_MASK GENMASK(7, 0) ++#define AR_PRESENT_FLAG BIT(0) ++#define AR_EXTEND 0xff ++ ++/* Buffer to send to the ESPARSER to signal End Of Stream for H.264. ++ * This is a 16x16 encoded picture that will trigger drain firmware-side. ++ * There is no known alternative. ++ */ ++static const u8 eos_sequence[SZ_1K] = { ++ 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 const u8 *codec_h264_eos_sequence(u32 *len) ++{ ++ *len = ARRAY_SIZE(eos_sequence); ++ return eos_sequence; ++} ++ ++struct codec_h264 { ++ /* H.264 decoder requires an extended firmware */ ++ 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 */ ++ void *sei_vaddr; ++ dma_addr_t sei_paddr; ++ ++ u32 mb_width; ++ u32 mb_height; ++ u32 max_refs; ++}; ++ ++static int codec_h264_can_recycle(struct amvdec_core *core) ++{ ++ return !amvdec_read_dos(core, AV_SCRATCH_7) || ++ !amvdec_read_dos(core, AV_SCRATCH_8); ++} ++ ++static void codec_h264_recycle(struct amvdec_core *core, u32 buf_idx) ++{ ++ /* Tell the decoder he can recycle this buffer. ++ * AV_SCRATCH_8 serves the same purpose. ++ */ ++ if (!amvdec_read_dos(core, AV_SCRATCH_7)) ++ amvdec_write_dos(core, AV_SCRATCH_7, buf_idx + 1); ++ else ++ amvdec_write_dos(core, AV_SCRATCH_8, buf_idx + 1); ++} ++ ++static int codec_h264_start(struct amvdec_session *sess) { ++ u32 workspace_offset; ++ struct amvdec_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 alloc 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 alloc H.264 SEI\n"); ++ return -ENOMEM; ++ } ++ ++ amvdec_write_dos_bits(core, POWER_CTL_VLD, BIT(9) | BIT(6)); ++ ++ workspace_offset = h264->workspace_paddr - WORKSPACE_BUF_OFFSET; ++ amvdec_write_dos(core, AV_SCRATCH_1, workspace_offset); ++ amvdec_write_dos(core, AV_SCRATCH_G, h264->ext_fw_paddr); ++ amvdec_write_dos(core, AV_SCRATCH_I, h264->sei_paddr - workspace_offset); ++ ++ /* Enable "error correction" */ ++ amvdec_write_dos(core, AV_SCRATCH_F, ++ (amvdec_read_dos(core, AV_SCRATCH_F) & 0xffffffc3) | ++ BIT(4) | BIT(7)); ++ ++ amvdec_write_dos(core, MDEC_PIC_DC_THRESH, 0x404038aa); ++ ++ return 0; ++} ++ ++static int codec_h264_stop(struct amvdec_session *sess) ++{ ++ struct codec_h264 *h264 = sess->priv; ++ struct amvdec_core *core = sess->core; ++ ++ 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); ++ ++ return 0; ++} ++ ++static int codec_h264_load_extended_firmware(struct amvdec_session *sess, ++ const u8 *data, u32 len) ++{ ++ struct codec_h264 *h264; ++ struct amvdec_core *core = sess->core; ++ ++ if (len < SIZE_EXT_FW) ++ return -EINVAL; ++ ++ h264 = kzalloc(sizeof(*h264), GFP_KERNEL); ++ if (!h264) ++ return -ENOMEM; ++ ++ 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, "Failed to alloc H.264 extended fw\n"); ++ kfree(h264); ++ return -ENOMEM; ++ } ++ ++ memcpy(h264->ext_fw_vaddr, data, SIZE_EXT_FW); ++ sess->priv = h264; ++ ++ return 0; ++} ++ ++static const struct v4l2_fract par_table[] = { ++ { 1, 1 }, { 1, 1 }, { 12, 11 }, { 10, 11 }, ++ { 16, 11 }, { 40, 33 }, { 24, 11 }, { 20, 11 }, ++ { 32, 11 }, { 80, 33 }, { 18, 11 }, { 15, 11 }, ++ { 64, 33 }, { 160, 99 }, { 4, 3 }, { 3, 2 }, ++ { 2, 1 } ++}; ++ ++static void codec_h264_set_par(struct amvdec_session *sess) ++{ ++ struct amvdec_core *core = sess->core; ++ u32 seq_info = amvdec_read_dos(core, AV_SCRATCH_2); ++ u32 ar_idc = (seq_info >> AR_IDC_BIT) & AR_IDC_MASK; ++ ++ if (!(seq_info & AR_PRESENT_FLAG)) ++ return; ++ ++ if (ar_idc == AR_EXTEND) { ++ u32 ar_info = amvdec_read_dos(core, AV_SCRATCH_3); ++ sess->pixelaspect.numerator = ar_info & 0xffff; ++ sess->pixelaspect.denominator = (ar_info >> 16) & 0xffff; ++ return; ++ } ++ ++ if (ar_idc >= ARRAY_SIZE(par_table)) ++ return; ++ ++ sess->pixelaspect = par_table[ar_idc]; ++} ++ ++static void codec_h264_resume(struct amvdec_session *sess) ++{ ++ struct amvdec_core *core = sess->core; ++ struct codec_h264 *h264 = sess->priv; ++ u32 mb_width, mb_height, mb_total; ++ ++ amvdec_set_canvases(sess, (u32[]){ ANC0_CANVAS_ADDR, 0 }, ++ (u32[]){ 24, 0 }); ++ ++ dev_dbg(core->dev, ++ "max_refs = %u; actual_dpb_size = %u\n", ++ h264->max_refs, sess->num_dst_bufs); ++ ++ /* Align to a multiple of 4 macroblocks */ ++ mb_width = ALIGN(h264->mb_width, 4); ++ mb_height = ALIGN(h264->mb_height, 4); ++ mb_total = mb_width * mb_height; ++ ++ h264->ref_size = mb_total * MB_MV_SIZE * h264->max_refs; ++ h264->ref_vaddr = dma_alloc_coherent(core->dev, h264->ref_size, ++ &h264->ref_paddr, GFP_KERNEL); ++ if (!h264->ref_vaddr) { ++ dev_err(core->dev, "Failed to alloc refs (%u)\n", ++ h264->ref_size); ++ amvdec_abort(sess); ++ return; ++ } ++ ++ /* Address to store the references' MVs */ ++ amvdec_write_dos(core, AV_SCRATCH_1, h264->ref_paddr); ++ /* End of ref MV */ ++ amvdec_write_dos(core, AV_SCRATCH_4, h264->ref_paddr + h264->ref_size); ++ ++ amvdec_write_dos(core, AV_SCRATCH_0, (h264->max_refs << 24) | ++ (sess->num_dst_bufs << 16) | ++ ((h264->max_refs - 1) << 8)); ++} ++ ++/* Configure the H.264 decoder when the parser detected a parameter set change ++ */ ++static void codec_h264_src_change(struct amvdec_session *sess) ++{ ++ struct amvdec_core *core = sess->core; ++ struct codec_h264 *h264 = sess->priv; ++ u32 parsed_info, mb_total; ++ u32 crop_infor, crop_bottom, crop_right; ++ u32 frame_width, frame_height; ++ ++ sess->keyframe_found = 1; ++ ++ parsed_info = amvdec_read_dos(core, AV_SCRATCH_1); ++ ++ /* Total number of 16x16 macroblocks */ ++ mb_total = (parsed_info >> MB_TOTAL_BIT) & MB_TOTAL_MASK; ++ /* Number of macroblocks per line */ ++ h264->mb_width = parsed_info & MB_WIDTH_MASK; ++ /* Number of macroblock lines */ ++ h264->mb_height = mb_total / h264->mb_width; ++ ++ h264->max_refs = ((parsed_info >> MAX_REF_BIT) & MAX_REF_MASK) + 1; ++ ++ crop_infor = amvdec_read_dos(core, AV_SCRATCH_6); ++ crop_bottom = (crop_infor & 0xff); ++ crop_right = (crop_infor >> 16) & 0xff; ++ ++ frame_width = h264->mb_width * 16 - crop_right; ++ frame_height = h264->mb_height * 16 - crop_bottom; ++ ++ dev_info(core->dev, "frame: %ux%u; crop: %u %u\n", ++ frame_width, frame_height, crop_right, crop_bottom); ++ ++ codec_h264_set_par(sess); ++ amvdec_src_change(sess, frame_width, frame_height, h264->max_refs + 5); ++} ++ ++/** ++ * The offset is split in half in 2 different registers ++ */ ++static u32 get_offset_msb(struct amvdec_core *core, int frame_num) ++{ ++ int take_msb = frame_num % 2; ++ int reg_offset = (frame_num / 2) * 4; ++ u32 offset_msb = amvdec_read_dos(core, AV_SCRATCH_A + reg_offset); ++ ++ if (take_msb) ++ return offset_msb & 0xffff0000; ++ ++ return (offset_msb & 0x0000ffff) << 16; ++} ++ ++static void codec_h264_frames_ready(struct amvdec_session *sess, u32 status) ++{ ++ struct amvdec_core *core = sess->core; ++ int error_count; ++ int num_frames; ++ int i; ++ ++ error_count = amvdec_read_dos(core, AV_SCRATCH_D); ++ num_frames = (status >> 8) & 0xff; ++ if (error_count) { ++ dev_warn(core->dev, ++ "decoder error(s) happened, count %d\n", error_count); ++ amvdec_write_dos(core, AV_SCRATCH_D, 0); ++ } ++ ++ for (i = 0; i < num_frames; i++) { ++ u32 frame_status = amvdec_read_dos(core, AV_SCRATCH_1 + i * 4); ++ u32 buffer_index = frame_status & BUF_IDX_MASK; ++ u32 pic_struct = (frame_status >> PIC_STRUCT_BIT) & ++ PIC_STRUCT_MASK; ++ u32 offset = (frame_status >> OFFSET_BIT) & OFFSET_MASK; ++ u32 field = V4L2_FIELD_NONE; ++ ++ /* A buffer decode error means it was decoded, ++ * but part of the picture will have artifacts. ++ * Typical reason is a temporarily corrupted bitstream ++ */ ++ if (frame_status & ERROR_FLAG) ++ dev_dbg(core->dev, "Buffer %d decode error\n", ++ buffer_index); ++ ++ if (pic_struct == PIC_TOP_BOT) ++ field = V4L2_FIELD_INTERLACED_TB; ++ else if (pic_struct == PIC_BOT_TOP) ++ field = V4L2_FIELD_INTERLACED_BT; ++ ++ offset |= get_offset_msb(core, i); ++ amvdec_dst_buf_done_idx(sess, buffer_index, offset, field); ++ } ++} ++ ++static irqreturn_t codec_h264_threaded_isr(struct amvdec_session *sess) ++{ ++ struct amvdec_core *core = sess->core; ++ u32 status; ++ u32 size; ++ u8 cmd; ++ ++ status = amvdec_read_dos(core, AV_SCRATCH_0); ++ cmd = status & CMD_MASK; ++ ++ switch (cmd) { ++ case CMD_SRC_CHANGE: ++ codec_h264_src_change(sess); ++ break; ++ case CMD_FRAMES_READY: ++ codec_h264_frames_ready(sess, status); ++ break; ++ case CMD_FATAL_ERROR: ++ dev_err(core->dev, "H.264 decoder fatal error\n"); ++ goto abort; ++ case CMD_BAD_WIDTH: ++ size = (amvdec_read_dos(core, AV_SCRATCH_1) + 1) * 16; ++ dev_err(core->dev, "Unsupported video width: %u\n", size); ++ goto abort; ++ case CMD_BAD_HEIGHT: ++ size = (amvdec_read_dos(core, AV_SCRATCH_1) + 1) * 16; ++ dev_err(core->dev, "Unsupported video height: %u\n", size); ++ goto abort; ++ case 0: /* Unused but not worth printing for */ ++ case 9: ++ break; ++ default: ++ dev_info(core->dev, "Unexpected H264 ISR: %08X\n", cmd); ++ break; ++ } ++ ++ if (cmd && cmd != CMD_SRC_CHANGE) ++ amvdec_write_dos(core, AV_SCRATCH_0, 0); ++ ++ /* Decoder has some SEI data for us ; ignore */ ++ if (amvdec_read_dos(core, AV_SCRATCH_J) & SEI_DATA_READY) ++ amvdec_write_dos(core, AV_SCRATCH_J, 0); ++ ++ return IRQ_HANDLED; ++abort: ++ amvdec_abort(sess); ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t codec_h264_isr(struct amvdec_session *sess) ++{ ++ struct amvdec_core *core = sess->core; ++ ++ amvdec_write_dos(core, ASSIST_MBOX1_CLR_REG, 1); ++ ++ return IRQ_WAKE_THREAD; ++} ++ ++struct amvdec_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, ++ .can_recycle = codec_h264_can_recycle, ++ .recycle = codec_h264_recycle, ++ .eos_sequence = codec_h264_eos_sequence, ++ .resume = codec_h264_resume, ++}; +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..7a15976 +--- /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 amvdec_codec_ops codec_h264_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 +index 291f1ee..baecf59 100644 +--- a/drivers/media/platform/meson/vdec/vdec_platform.c ++++ b/drivers/media/platform/meson/vdec/vdec_platform.c +@@ -9,9 +9,20 @@ + + #include "vdec_1.h" + #include "codec_mpeg12.h" ++#include "codec_h264.h" + + static const struct amvdec_format vdec_formats_gxbb[] = { + { ++ .pixfmt = V4L2_PIX_FMT_H264, ++ .min_buffers = 2, ++ .max_buffers = 24, ++ .max_width = 1920, ++ .max_height = 1080, ++ .vdec_ops = &vdec_1_ops, ++ .codec_ops = &codec_h264_ops, ++ .firmware_path = "meson/gxbb/vh264_mc", ++ .pixfmts_cap = { V4L2_PIX_FMT_NV12M, 0 }, ++ }, { + .pixfmt = V4L2_PIX_FMT_MPEG1, + .min_buffers = 8, + .max_buffers = 8, +@@ -38,6 +49,16 @@ static const struct amvdec_format vdec_formats_gxbb[] = { + + static const struct amvdec_format vdec_formats_gxl[] = { + { ++ .pixfmt = V4L2_PIX_FMT_H264, ++ .min_buffers = 2, ++ .max_buffers = 24, ++ .max_width = 3840, ++ .max_height = 2160, ++ .vdec_ops = &vdec_1_ops, ++ .codec_ops = &codec_h264_ops, ++ .firmware_path = "meson/gxl/vh264_mc", ++ .pixfmts_cap = { V4L2_PIX_FMT_NV12M, 0 }, ++ }, { + .pixfmt = V4L2_PIX_FMT_MPEG1, + .min_buffers = 8, + .max_buffers = 8, +@@ -64,6 +85,16 @@ static const struct amvdec_format vdec_formats_gxl[] = { + + static const struct amvdec_format vdec_formats_gxm[] = { + { ++ .pixfmt = V4L2_PIX_FMT_H264, ++ .min_buffers = 2, ++ .max_buffers = 24, ++ .max_width = 3840, ++ .max_height = 2160, ++ .vdec_ops = &vdec_1_ops, ++ .codec_ops = &codec_h264_ops, ++ .firmware_path = "meson/gxm/vh264_mc", ++ .pixfmts_cap = { V4L2_PIX_FMT_NV12M, 0 }, ++ }, { + .pixfmt = V4L2_PIX_FMT_MPEG1, + .min_buffers = 8, + .max_buffers = 8, diff --git a/testing/linux-amlogic/0041-media-meson-vdec-add-MPEG4-decoding-support.patch b/testing/linux-amlogic/0041-media-meson-vdec-add-MPEG4-decoding-support.patch new file mode 100644 index 0000000000..01fd5ae030 --- /dev/null +++ b/testing/linux-amlogic/0041-media-meson-vdec-add-MPEG4-decoding-support.patch @@ -0,0 +1,313 @@ +From e222a1cf4bd62745407a9404565d76b8a5770f12 Mon Sep 17 00:00:00 2001 +From: Maxime Jourdan <mjourdan@baylibre.com> +Date: Wed, 29 Aug 2018 16:01:55 +0200 +Subject: [PATCH] media: meson: vdec: add MPEG4 decoding support + +Add support for V4L2_PIX_FMT_MPEG4, V4L2_PIX_FMT_XVID and +V4L2_PIX_FMT_H.263 + +--- + drivers/media/platform/meson/vdec/Makefile | 2 +- + drivers/media/platform/meson/vdec/codec_mpeg4.c | 139 ++++++++++++++++++++++ + drivers/media/platform/meson/vdec/codec_mpeg4.h | 13 ++ + drivers/media/platform/meson/vdec/vdec_platform.c | 91 ++++++++++++++ + 4 files changed, 244 insertions(+), 1 deletion(-) + create mode 100644 drivers/media/platform/meson/vdec/codec_mpeg4.c + create mode 100644 drivers/media/platform/meson/vdec/codec_mpeg4.h + +diff --git a/drivers/media/platform/meson/vdec/Makefile b/drivers/media/platform/meson/vdec/Makefile +index 01dc960..bb7a134 100644 +--- a/drivers/media/platform/meson/vdec/Makefile ++++ b/drivers/media/platform/meson/vdec/Makefile +@@ -3,6 +3,6 @@ + + meson-vdec-objs = esparser.o vdec.o vdec_ctrls.o vdec_helpers.o vdec_platform.o + meson-vdec-objs += vdec_1.o +-meson-vdec-objs += codec_mpeg12.o codec_h264.o ++meson-vdec-objs += codec_mpeg12.o codec_h264.o codec_mpeg4.o + + obj-$(CONFIG_VIDEO_MESON_VDEC) += meson-vdec.o +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..1d574e5 +--- /dev/null ++++ b/drivers/media/platform/meson/vdec/codec_mpeg4.c +@@ -0,0 +1,139 @@ ++// 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 "vdec_helpers.h" ++#include "dos_regs.h" ++ ++#define SIZE_WORKSPACE SZ_1M ++/* Offset added by firmware, to substract from workspace paddr */ ++#define DCAC_BUFF_START_IP 0x02b00000 ++ ++/* map firmware registers to known MPEG4 functions */ ++#define MREG_BUFFERIN AV_SCRATCH_8 ++#define MREG_BUFFEROUT AV_SCRATCH_9 ++#define MP4_NOT_CODED_CNT AV_SCRATCH_A ++#define MP4_OFFSET_REG AV_SCRATCH_C ++#define MEM_OFFSET_REG AV_SCRATCH_F ++#define MREG_FATAL_ERROR AV_SCRATCH_L ++ ++#define BUF_IDX_MASK GENMASK(2, 0) ++#define INTERLACE_FLAG BIT(7) ++#define TOP_FIELD_FIRST_FLAG BIT(6) ++ ++struct codec_mpeg4 { ++ /* Buffer for the MPEG4 Workspace */ ++ void *workspace_vaddr; ++ dma_addr_t workspace_paddr; ++}; ++ ++static int codec_mpeg4_can_recycle(struct amvdec_core *core) ++{ ++ return !amvdec_read_dos(core, MREG_BUFFERIN); ++} ++ ++static void codec_mpeg4_recycle(struct amvdec_core *core, u32 buf_idx) ++{ ++ amvdec_write_dos(core, MREG_BUFFERIN, ~BIT(buf_idx)); ++} ++ ++static int codec_mpeg4_start(struct amvdec_session *sess) { ++ struct amvdec_core *core = sess->core; ++ struct codec_mpeg4 *mpeg4 = sess->priv; ++ int ret; ++ ++ mpeg4 = kzalloc(sizeof(*mpeg4), GFP_KERNEL); ++ if (!mpeg4) ++ return -ENOMEM; ++ ++ /* 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; ++ } ++ ++ /* Canvas regs: AV_SCRATCH_0-AV_SCRATCH_4;AV_SCRATCH_G-AV_SCRATCH_J */ ++ amvdec_set_canvases(sess, (u32[]){ AV_SCRATCH_0, AV_SCRATCH_G, 0 }, ++ (u32[]){ 4, 4, 0 }); ++ ++ amvdec_write_dos(core, MEM_OFFSET_REG, ++ mpeg4->workspace_paddr - DCAC_BUFF_START_IP); ++ amvdec_write_dos(core, PSCALE_CTRL, 0); ++ amvdec_write_dos(core, MP4_NOT_CODED_CNT, 0); ++ amvdec_write_dos(core, MREG_BUFFERIN, 0); ++ amvdec_write_dos(core, MREG_BUFFEROUT, 0); ++ amvdec_write_dos(core, MREG_FATAL_ERROR, 0); ++ amvdec_write_dos(core, MDEC_PIC_DC_THRESH, 0x404038aa); ++ ++ sess->keyframe_found = 1; ++ sess->priv = mpeg4; ++ ++ return 0; ++ ++free_mpeg4: ++ kfree(mpeg4); ++ return ret; ++} ++ ++static int codec_mpeg4_stop(struct amvdec_session *sess) ++{ ++ struct codec_mpeg4 *mpeg4 = sess->priv; ++ struct amvdec_core *core = sess->core; ++ ++ if (mpeg4->workspace_vaddr) { ++ dma_free_coherent(core->dev, SIZE_WORKSPACE, ++ mpeg4->workspace_vaddr, ++ mpeg4->workspace_paddr); ++ mpeg4->workspace_vaddr = 0; ++ } ++ ++ return 0; ++} ++ ++static irqreturn_t codec_mpeg4_isr(struct amvdec_session *sess) ++{ ++ struct amvdec_core *core = sess->core; ++ u32 reg; ++ u32 buffer_index; ++ u32 field = V4L2_FIELD_NONE; ++ ++ reg = amvdec_read_dos(core, MREG_FATAL_ERROR); ++ if (reg == 1) { ++ dev_err(core->dev, "mpeg4 fatal error\n"); ++ amvdec_abort(sess); ++ return IRQ_HANDLED; ++ } ++ ++ reg = amvdec_read_dos(core, MREG_BUFFEROUT); ++ if (!reg) ++ goto end; ++ ++ buffer_index = reg & BUF_IDX_MASK; ++ if (reg & INTERLACE_FLAG) ++ field = (reg & TOP_FIELD_FIRST_FLAG) ? ++ V4L2_FIELD_INTERLACED_TB : ++ V4L2_FIELD_INTERLACED_BT; ++ ++ amvdec_dst_buf_done_idx(sess, buffer_index, -1, field); ++ amvdec_write_dos(core, MREG_BUFFEROUT, 0); ++ ++end: ++ amvdec_write_dos(core, ASSIST_MBOX1_CLR_REG, 1); ++ return IRQ_HANDLED; ++} ++ ++struct amvdec_codec_ops codec_mpeg4_ops = { ++ .start = codec_mpeg4_start, ++ .stop = codec_mpeg4_stop, ++ .isr = codec_mpeg4_isr, ++ .can_recycle = codec_mpeg4_can_recycle, ++ .recycle = codec_mpeg4_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..b91b26413 +--- /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 amvdec_codec_ops codec_mpeg4_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 +index baecf59..80b43fd 100644 +--- a/drivers/media/platform/meson/vdec/vdec_platform.c ++++ b/drivers/media/platform/meson/vdec/vdec_platform.c +@@ -10,9 +10,40 @@ + #include "vdec_1.h" + #include "codec_mpeg12.h" + #include "codec_h264.h" ++#include "codec_mpeg4.h" + + static const struct amvdec_format vdec_formats_gxbb[] = { + { ++ .pixfmt = V4L2_PIX_FMT_MPEG4, ++ .min_buffers = 8, ++ .max_buffers = 8, ++ .max_width = 1920, ++ .max_height = 1080, ++ .vdec_ops = &vdec_1_ops, ++ .codec_ops = &codec_mpeg4_ops, ++ .firmware_path = "meson/gx/vmpeg4_mc_5", ++ .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, ++ }, { ++ .pixfmt = V4L2_PIX_FMT_H263, ++ .min_buffers = 8, ++ .max_buffers = 8, ++ .max_width = 1920, ++ .max_height = 1080, ++ .vdec_ops = &vdec_1_ops, ++ .codec_ops = &codec_mpeg4_ops, ++ .firmware_path = "meson/gx/h263_mc", ++ .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, ++ }, { ++ .pixfmt = V4L2_PIX_FMT_XVID, ++ .min_buffers = 8, ++ .max_buffers = 8, ++ .max_width = 1920, ++ .max_height = 1080, ++ .vdec_ops = &vdec_1_ops, ++ .codec_ops = &codec_mpeg4_ops, ++ .firmware_path = "meson/gx/vmpeg4_mc_5", ++ .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, ++ }, { + .pixfmt = V4L2_PIX_FMT_H264, + .min_buffers = 2, + .max_buffers = 24, +@@ -49,6 +80,36 @@ static const struct amvdec_format vdec_formats_gxbb[] = { + + static const struct amvdec_format vdec_formats_gxl[] = { + { ++ .pixfmt = V4L2_PIX_FMT_MPEG4, ++ .min_buffers = 8, ++ .max_buffers = 8, ++ .max_width = 1920, ++ .max_height = 1080, ++ .vdec_ops = &vdec_1_ops, ++ .codec_ops = &codec_mpeg4_ops, ++ .firmware_path = "meson/gx/vmpeg4_mc_5", ++ .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, ++ }, { ++ .pixfmt = V4L2_PIX_FMT_H263, ++ .min_buffers = 8, ++ .max_buffers = 8, ++ .max_width = 1920, ++ .max_height = 1080, ++ .vdec_ops = &vdec_1_ops, ++ .codec_ops = &codec_mpeg4_ops, ++ .firmware_path = "meson/gx/h263_mc", ++ .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, ++ }, { ++ .pixfmt = V4L2_PIX_FMT_XVID, ++ .min_buffers = 8, ++ .max_buffers = 8, ++ .max_width = 1920, ++ .max_height = 1080, ++ .vdec_ops = &vdec_1_ops, ++ .codec_ops = &codec_mpeg4_ops, ++ .firmware_path = "meson/gx/vmpeg4_mc_5", ++ .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, ++ }, { + .pixfmt = V4L2_PIX_FMT_H264, + .min_buffers = 2, + .max_buffers = 24, +@@ -85,6 +146,36 @@ static const struct amvdec_format vdec_formats_gxl[] = { + + static const struct amvdec_format vdec_formats_gxm[] = { + { ++ .pixfmt = V4L2_PIX_FMT_MPEG4, ++ .min_buffers = 8, ++ .max_buffers = 8, ++ .max_width = 1920, ++ .max_height = 1080, ++ .vdec_ops = &vdec_1_ops, ++ .codec_ops = &codec_mpeg4_ops, ++ .firmware_path = "meson/gx/vmpeg4_mc_5", ++ .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, ++ }, { ++ .pixfmt = V4L2_PIX_FMT_H263, ++ .min_buffers = 8, ++ .max_buffers = 8, ++ .max_width = 1920, ++ .max_height = 1080, ++ .vdec_ops = &vdec_1_ops, ++ .codec_ops = &codec_mpeg4_ops, ++ .firmware_path = "meson/gx/h263_mc", ++ .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, ++ }, { ++ .pixfmt = V4L2_PIX_FMT_XVID, ++ .min_buffers = 8, ++ .max_buffers = 8, ++ .max_width = 1920, ++ .max_height = 1080, ++ .vdec_ops = &vdec_1_ops, ++ .codec_ops = &codec_mpeg4_ops, ++ .firmware_path = "meson/gx/vmpeg4_mc_5", ++ .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, ++ }, { + .pixfmt = V4L2_PIX_FMT_H264, + .min_buffers = 2, + .max_buffers = 24, diff --git a/testing/linux-amlogic/0042-media-meson-vdec-add-MJPEG-decoding-support.patch b/testing/linux-amlogic/0042-media-meson-vdec-add-MJPEG-decoding-support.patch new file mode 100644 index 0000000000..b72f817fe6 --- /dev/null +++ b/testing/linux-amlogic/0042-media-meson-vdec-add-MJPEG-decoding-support.patch @@ -0,0 +1,253 @@ +From 9a662159440cf5e0f55275ef63588e35c91c581e Mon Sep 17 00:00:00 2001 +From: Maxime Jourdan <mjourdan@baylibre.com> +Date: Sun, 21 Oct 2018 15:14:27 +0200 +Subject: [PATCH] media: meson: vdec: add MJPEG decoding support + +Add support for V4L2_PIX_FMT_MJPEG + +--- + drivers/media/platform/meson/vdec/Makefile | 2 +- + drivers/media/platform/meson/vdec/codec_mjpeg.c | 140 ++++++++++++++++++++++ + drivers/media/platform/meson/vdec/codec_mjpeg.h | 13 ++ + drivers/media/platform/meson/vdec/vdec_platform.c | 31 +++++ + 4 files changed, 185 insertions(+), 1 deletion(-) + create mode 100644 drivers/media/platform/meson/vdec/codec_mjpeg.c + create mode 100644 drivers/media/platform/meson/vdec/codec_mjpeg.h + +diff --git a/drivers/media/platform/meson/vdec/Makefile b/drivers/media/platform/meson/vdec/Makefile +index bb7a134..acf07f3 100644 +--- a/drivers/media/platform/meson/vdec/Makefile ++++ b/drivers/media/platform/meson/vdec/Makefile +@@ -3,6 +3,6 @@ + + meson-vdec-objs = esparser.o vdec.o vdec_ctrls.o vdec_helpers.o vdec_platform.o + meson-vdec-objs += vdec_1.o +-meson-vdec-objs += codec_mpeg12.o codec_h264.o codec_mpeg4.o ++meson-vdec-objs += codec_mpeg12.o codec_h264.o codec_mpeg4.o codec_mjpeg.o + + obj-$(CONFIG_VIDEO_MESON_VDEC) += meson-vdec.o +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..abea9e3 +--- /dev/null ++++ b/drivers/media/platform/meson/vdec/codec_mjpeg.c +@@ -0,0 +1,140 @@ ++// 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 "vdec_helpers.h" ++#include "dos_regs.h" ++ ++/* map FW registers to known MJPEG functions */ ++#define MREG_DECODE_PARAM AV_SCRATCH_2 ++#define MREG_TO_AMRISC AV_SCRATCH_8 ++#define MREG_FROM_AMRISC AV_SCRATCH_9 ++#define MREG_FRAME_OFFSET AV_SCRATCH_A ++ ++static int codec_mjpeg_can_recycle(struct amvdec_core *core) ++{ ++ return !amvdec_read_dos(core, MREG_TO_AMRISC); ++} ++ ++static void codec_mjpeg_recycle(struct amvdec_core *core, u32 buf_idx) ++{ ++ amvdec_write_dos(core, MREG_TO_AMRISC, buf_idx + 1); ++} ++ ++/* 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 amvdec_core *core) ++{ ++ int i; ++ ++ /* PSCALE cbus bmem enable */ ++ amvdec_write_dos(core, PSCALE_CTRL, 0xc000); ++ ++ amvdec_write_dos(core, PSCALE_BMEM_ADDR, 0); ++ for (i = 0; i < ARRAY_SIZE(filt_coef); ++i) { ++ amvdec_write_dos(core, PSCALE_BMEM_DAT, 0); ++ amvdec_write_dos(core, PSCALE_BMEM_DAT, filt_coef[i]); ++ } ++ ++ amvdec_write_dos(core, PSCALE_BMEM_ADDR, 74); ++ amvdec_write_dos(core, PSCALE_BMEM_DAT, 0x0008); ++ amvdec_write_dos(core, PSCALE_BMEM_DAT, 0x60000000); ++ ++ amvdec_write_dos(core, PSCALE_BMEM_ADDR, 82); ++ amvdec_write_dos(core, PSCALE_BMEM_DAT, 0x0008); ++ amvdec_write_dos(core, PSCALE_BMEM_DAT, 0x60000000); ++ ++ amvdec_write_dos(core, PSCALE_BMEM_ADDR, 78); ++ amvdec_write_dos(core, PSCALE_BMEM_DAT, 0x0008); ++ amvdec_write_dos(core, PSCALE_BMEM_DAT, 0x60000000); ++ ++ amvdec_write_dos(core, PSCALE_BMEM_ADDR, 86); ++ amvdec_write_dos(core, PSCALE_BMEM_DAT, 0x0008); ++ amvdec_write_dos(core, PSCALE_BMEM_DAT, 0x60000000); ++ ++ amvdec_write_dos(core, PSCALE_BMEM_ADDR, 73); ++ amvdec_write_dos(core, PSCALE_BMEM_DAT, 0x10000); ++ amvdec_write_dos(core, PSCALE_BMEM_ADDR, 81); ++ amvdec_write_dos(core, PSCALE_BMEM_DAT, 0x10000); ++ ++ amvdec_write_dos(core, PSCALE_BMEM_ADDR, 77); ++ amvdec_write_dos(core, PSCALE_BMEM_DAT, 0x10000); ++ amvdec_write_dos(core, PSCALE_BMEM_ADDR, 85); ++ amvdec_write_dos(core, PSCALE_BMEM_DAT, 0x10000); ++ ++ amvdec_write_dos(core, PSCALE_RST, 0x7); ++ amvdec_write_dos(core, PSCALE_RST, 0); ++} ++ ++static int codec_mjpeg_start(struct amvdec_session *sess) ++{ ++ struct amvdec_core *core = sess->core; ++ ++ amvdec_write_dos(core, AV_SCRATCH_0, 12); ++ amvdec_write_dos(core, AV_SCRATCH_1, 0x031a); ++ ++ amvdec_set_canvases(sess, (u32[]){ AV_SCRATCH_4, 0 }, ++ (u32[]){ 4, 0 }); ++ codec_mjpeg_init_scaler(core); ++ ++ amvdec_write_dos(core, MREG_TO_AMRISC, 0); ++ amvdec_write_dos(core, MREG_FROM_AMRISC, 0); ++ amvdec_write_dos(core, MCPU_INTR_MSK, 0xffff); ++ amvdec_write_dos(core, MREG_DECODE_PARAM, ++ (sess->height << 4) | 0x8000); ++ amvdec_write_dos(core, VDEC_ASSIST_AMR1_INT8, 8); ++ ++ /* Intra-only codec */ ++ sess->keyframe_found = 1; ++ ++ return 0; ++} ++ ++static int codec_mjpeg_stop(struct amvdec_session *sess) ++{ ++ return 0; ++} ++ ++static irqreturn_t codec_mjpeg_isr(struct amvdec_session *sess) ++{ ++ struct amvdec_core *core = sess->core; ++ u32 reg; ++ u32 buffer_index; ++ u32 offset; ++ ++ amvdec_write_dos(core, ASSIST_MBOX1_CLR_REG, 1); ++ ++ reg = amvdec_read_dos(core, MREG_FROM_AMRISC); ++ if (!(reg & 0x7)) ++ return IRQ_HANDLED; ++ ++ buffer_index = ((reg & 0x7) - 1) & 3; ++ offset = amvdec_read_dos(core, MREG_FRAME_OFFSET); ++ amvdec_dst_buf_done_idx(sess, buffer_index, offset, V4L2_FIELD_NONE); ++ ++ amvdec_write_dos(core, MREG_FROM_AMRISC, 0); ++ return IRQ_HANDLED; ++} ++ ++struct amvdec_codec_ops codec_mjpeg_ops = { ++ .start = codec_mjpeg_start, ++ .stop = codec_mjpeg_stop, ++ .isr = codec_mjpeg_isr, ++ .can_recycle = codec_mjpeg_can_recycle, ++ .recycle = codec_mjpeg_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..cc1cf73 +--- /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 amvdec_codec_ops codec_mjpeg_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 +index 80b43fd..61def15 100644 +--- a/drivers/media/platform/meson/vdec/vdec_platform.c ++++ b/drivers/media/platform/meson/vdec/vdec_platform.c +@@ -11,9 +11,20 @@ + #include "codec_mpeg12.h" + #include "codec_h264.h" + #include "codec_mpeg4.h" ++#include "codec_mjpeg.h" + + static const struct amvdec_format vdec_formats_gxbb[] = { + { ++ .pixfmt = V4L2_PIX_FMT_MJPEG, ++ .min_buffers = 4, ++ .max_buffers = 4, ++ .max_width = 1920, ++ .max_height = 1080, ++ .vdec_ops = &vdec_1_ops, ++ .codec_ops = &codec_mjpeg_ops, ++ .firmware_path = "meson/gx/vmjpeg_mc", ++ .pixfmts_cap = { V4L2_PIX_FMT_YUV420M, 0 }, ++ }, { + .pixfmt = V4L2_PIX_FMT_MPEG4, + .min_buffers = 8, + .max_buffers = 8, +@@ -80,6 +91,16 @@ static const struct amvdec_format vdec_formats_gxbb[] = { + + static const struct amvdec_format vdec_formats_gxl[] = { + { ++ .pixfmt = V4L2_PIX_FMT_MJPEG, ++ .min_buffers = 4, ++ .max_buffers = 4, ++ .max_width = 1920, ++ .max_height = 1080, ++ .vdec_ops = &vdec_1_ops, ++ .codec_ops = &codec_mjpeg_ops, ++ .firmware_path = "meson/gx/vmjpeg_mc", ++ .pixfmts_cap = { V4L2_PIX_FMT_YUV420M, 0 }, ++ }, { + .pixfmt = V4L2_PIX_FMT_MPEG4, + .min_buffers = 8, + .max_buffers = 8, +@@ -146,6 +167,16 @@ static const struct amvdec_format vdec_formats_gxl[] = { + + static const struct amvdec_format vdec_formats_gxm[] = { + { ++ .pixfmt = V4L2_PIX_FMT_MJPEG, ++ .min_buffers = 4, ++ .max_buffers = 4, ++ .max_width = 1920, ++ .max_height = 1080, ++ .vdec_ops = &vdec_1_ops, ++ .codec_ops = &codec_mjpeg_ops, ++ .firmware_path = "meson/gx/vmjpeg_mc", ++ .pixfmts_cap = { V4L2_PIX_FMT_YUV420M, 0 }, ++ }, { + .pixfmt = V4L2_PIX_FMT_MPEG4, + .min_buffers = 8, + .max_buffers = 8, diff --git a/testing/linux-amlogic/0043-clk-meson-gxbb-set-fclk_div3-as-CLK_IS_CRITICAL.patch b/testing/linux-amlogic/0043-clk-meson-gxbb-set-fclk_div3-as-CLK_IS_CRITICAL.patch new file mode 100644 index 0000000000..33671060cc --- /dev/null +++ b/testing/linux-amlogic/0043-clk-meson-gxbb-set-fclk_div3-as-CLK_IS_CRITICAL.patch @@ -0,0 +1,42 @@ +From 6b98d7c6cd508cfb4a2d4a1e87e192ffcabeda9e Mon Sep 17 00:00:00 2001 +From: Christian Hewitt <christianshewitt@gmail.com> +Date: Sat, 13 Oct 2018 14:04:46 +0400 +Subject: [PATCH] clk: meson-gxbb: set fclk_div3 as CLK_IS_CRITICAL + +On the Khadas VIM2 (GXM) and LePotato (GXL) board there are problems +with reboot; e.g. a ~60 second delay between issuing reboot and the +board power cycling (and in some OS configurations reboot will fail +and require manual power cycling). + +Similar to 'commit c987ac6f1f088663b6dad39281071aeb31d450a8 ("clk: +meson-gxbb: set fclk_div2 as CLK_IS_CRITICAL")' the SCPI Cortex-M4 +Co-Processor seems to depend on FCLK_DIV3 being operational. + +Bisect gives 'commit 05f814402d6174369b3b29832cbb5eb5ed287059 ("clk: +meson: add fdiv clock gates") between 4.16 and 4.16-rc1 as the first +bad commit. This added support for the missing clock gates before the +fixed PLL fixed dividers (FCLK_DIVx) and the clock framework which +disabled all the unused fixed dividers, thus it disabled a critical +clock path for the SCPI Co-Processor. + +This change simply sets the FCLK_DIV3 gate as critical to ensure +nothing can disable it. + +Signed-off-by: Christian Hewitt <christianshewitt@gmail.com> + +--- + drivers/clk/meson/gxbb.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/clk/meson/gxbb.c b/drivers/clk/meson/gxbb.c +index 6628ffa..01f7615 100644 +--- a/drivers/clk/meson/gxbb.c ++++ b/drivers/clk/meson/gxbb.c +@@ -513,6 +513,7 @@ static struct clk_fixed_factor gxbb_fclk_div3_div = { + .ops = &clk_fixed_factor_ops, + .parent_names = (const char *[]){ "fixed_pll" }, + .num_parents = 1, ++ .flags = CLK_IS_CRITICAL, + }, + }; + diff --git a/testing/linux-amlogic/APKBUILD b/testing/linux-amlogic/APKBUILD index 2c7bbc3ff7..ec8ccb65f9 100644 --- a/testing/linux-amlogic/APKBUILD +++ b/testing/linux-amlogic/APKBUILD @@ -1,7 +1,7 @@ # Maintainer: He Yangxuan <yangxuan8282@gmail.com> pkgname=linux-amlogic -pkgver=4.18.14 +pkgver=4.19.8 case $pkgver in *.*.*) _kernver=${pkgver%.*};; *.*) _kernver=${pkgver};; @@ -9,7 +9,7 @@ esac pkgrel=0 pkgdesc="Linux kernel for Amlogic" url=https://github.com/torvalds/linux -_commit=e7d199e92956587695510d147c8de795f944cec9 +_commit=178574b66509c9ff7df4ad26c84a8884567e93b4 depends="mkinitfs" _depends_dev="perl gmp-dev elfutils-dev bash" makedepends="$_depends_dev sed installkernel bc linux-headers linux-firmware openssl-dev bison flex" @@ -17,40 +17,63 @@ 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 + 0001-ARM64-defconfig-enable-CEC-support.patch + 0002-ASoC-meson-add-meson-audio-core-driver.patch + 0003-ASoC-meson-add-register-definitions.patch + 0004-ASoC-meson-add-aiu-i2s-dma-support.patch + 0005-ASoC-meson-add-initial-i2s-dai-support.patch + 0006-ASoC-meson-add-aiu-spdif-dma-support.patch + 0007-ASoC-meson-add-initial-spdif-dai-support.patch + 0008-ARM64-defconfig-enable-audio-support-for-meson-SoCs-.patch + 0009-ARM64-dts-meson-gx-add-audio-controller-nodes.patch + 0010-snd-meson-activate-HDMI-audio-path.patch + 0011-drm-meson-select-dw-hdmi-i2s-audio-for-meson-hdmi.patch + 0012-ARM64-dts-meson-gx-add-sound-dai-cells-to-HDMI-node.patch + 0013-ARM64-dts-meson-activate-hdmi-audio-HDMI-enabled-boa.patch + 0014-drm-bridge-dw-hdmi-Use-AUTO-CTS-setup-mode-when-non-.patch + 0017-soc-amlogic-add-meson-canvas-driver.patch + 0018-ARM64-dts-meson-gx-add-dmcbus-and-canvas-nodes.patch + 0020-drm-meson-Use-optional-canvas-provider.patch + 0021-arm64-dts-meson-gx-Add-canvas-provider-node-to-the-v.patch + 0022-drm-meson-Support-Overlay-plane-for-video-rendering.patch + 0023-drm-meson-move-OSD-scaler-management-into-plane-atom.patch + 0024-drm-meson-Add-primary-plane-scaling.patch + 0026-pinctrl-meson-gxl-remove-invalid-GPIOX-tsin_a-pins.patch + 0027-arm64-dts-meson-gx-Add-hdmi_5v-regulator-as-hdmi-tx-.patch + 0028-arm64-dts-meson-gxl-libretech-cc-fix-GPIO-lines-name.patch + 0029-arm64-dts-meson-gxbb-nanopi-k2-fix-GPIO-lines-names.patch + 0030-arm64-dts-meson-gxbb-odroidc2-fix-GPIO-lines-names.patch + 0031-arm64-dts-meson-gxl-khadas-vim-fix-GPIO-lines-names.patch + 0032-drm-meson-Add-support-for-VIC-alternate-timings.patch + 0033-media-meson-add-v4l2-m2m-video-decoder-driver.patch + 0034-MAINTAINERS-Add-meson-video-decoder.patch + 0035-arm64-dts-meson-gx-add-vdec-entry.patch + 0036-arm64-dts-meson-add-vdec-entries.patch + 0037-meson-vdec-introduce-controls-and-V4L2_CID_MIN_BUFFE.patch + 0038-media-videodev2-add-V4L2_FMT_FLAG_NO_SOURCE_CHANGE.patch + 0039-meson-vdec-allow-subscribing-to-V4L2_EVENT_SOURCE_CH.patch + 0040-media-meson-vdec-add-H.264-decoding-support.patch + 0041-media-meson-vdec-add-MPEG4-decoding-support.patch + 0042-media-meson-vdec-add-MJPEG-decoding-support.patch + 0043-clk-meson-gxbb-set-fclk_div3-as-CLK_IS_CRITICAL.patch + 0008-drm-meson-Add-HDMI-1.4-4k-modes.patch + 0009-drm-meson-Use-drm_fbdev_generic_setup.patch + 0010-fixup-drm-meson-Use-optional-canvas-provider.patch + 0012-drm-meson-add-support-for-1080p25-mode.patch + 0014-drm-bridge-dw-hdmi-Add-SCDC-and-TMDS-Scrambling-supp.patch + 0015-drm-meson-add-HDMI-div40-TMDS-mode.patch + 0016-drm-meson-add-support-for-HDMI2.0-2160p-modes.patch + 0017-drm-bridge-dw-hdmi-add-support-for-YUV420-output.patch + 0018-drm-bridge-dw-hdmi-support-dynamically-get-input-out.patch + 0019-drm-bridge-dw-hdmi-allow-ycbcr420-modes-for-0x200a.patch + 0020-drm-meson-Add-YUV420-output-support.patch + 0021-drm-meson-Output-in-YUV444-if-sink-supports-it.patch + 0023-drm-meson-Fix-an-Alpha-Primary-Plane-bug-on-Meson-GX.patch + 0001-arm64-dts-meson-Fix-IRQ-trigger-type-for-macirq.patch + 0001-drm-meson-fix-max-mode_config-height-width.patch add-phicomm-n1.patch bt-btbcm.patch - brcmfmac-Disable-power-management.patch - offset.patch - off_error_text_offset.patch + text_offset.patch " subpackages="" arch="aarch64" @@ -274,40 +297,63 @@ _dev() { "$subpkgdir"/lib/modules/${_abi_release}/build } -sha512sums="b65dc14801b987b4d8ca786aa6571da12fd929ab147ed2f71f366b32313cd72d3047830e502b3fe51f9f1f3963d46b7f5b45ed937a4bdfee14dc216b32de2d3a e7d199e92956587695510d147c8de795f944cec9.tar.gz +sha512sums="642e3de14702d3034f9108c7b6661294722f4cf455b9167c349111642586d9a48732070e46092e7084f3ffbc26f01bae50f8da52eeceb7cf0f40e1441f9d895e 178574b66509c9ff7df4ad26c84a8884567e93b4.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 +09e8d284608ac0e86db8b36bdefdb17c8ad8e9b3bdf443ee5a687f1591d88d26cffef1a36919b0547acef49394d903ff8067d46f75b55eccadec415971bf0b34 0001-ARM64-defconfig-enable-CEC-support.patch +55d3e84c4e01b94a3bf256a980bd23a8d02c3ea7f4103d350400eb28cc31336bb35f9404ee34ba80f7d3d7427d73ed4ee75119a916ae0e88d911bb83b0303229 0002-ASoC-meson-add-meson-audio-core-driver.patch +b5a65140612b011f22d4bfbb4ab41a4c0fe07bab43f8033195a42b02c5d74c69c7fbc2b186f8d6ff08ed7b66ccbfd9d8d7945f81812ae5021b1d37154acdb12f 0003-ASoC-meson-add-register-definitions.patch +441b0385e88bb7ddb324363536413e6538d4b0367181ae5ceada806a8efe3d1b5daa643bd3ab99db6fa7a2d5ea0944a15a63672fa26bfc7850959ce2d7238aa1 0004-ASoC-meson-add-aiu-i2s-dma-support.patch +4615d01a2548ce5057446ff32c52bca1cea189fc74d902ea22dc90e73c2eb21ef5b4b58c6a4ebbdf3c6cad5bac483dabdee025438ebc0bdf1b4368af0fce1b87 0005-ASoC-meson-add-initial-i2s-dai-support.patch +d1dc1d99393a885776144dea27c0789d9e7968de1164bc85b07cc8d444cdcc898ce2f7ff1f9515ae2424789cbb76b84a8ed56008c1880fef60be79000bf55452 0006-ASoC-meson-add-aiu-spdif-dma-support.patch +b101c64312509738b078732f73b265fa81bb94806bb39524e85d171949505dc0548fc3ece498cca6586b8ca4af08b271b6c02f028040f8cf16ec5eee94ddafa1 0007-ASoC-meson-add-initial-spdif-dai-support.patch +98fd62a02fa76a957d15da9456c85def94a790429c3357b4b06a63d4224b624ffe763410ffdbc09ff560ba0a17a5299b722e2c9cefbb4a59455cfbe7379850b3 0008-ARM64-defconfig-enable-audio-support-for-meson-SoCs-.patch +a286d6ecb284f505ebd29c04c5e95b1e7ba1a4a3c5ffb17580c2387ab78360cba7be6806772a058be8326f7a299f59c84051ed022b2c99372412d935f12f7c6a 0009-ARM64-dts-meson-gx-add-audio-controller-nodes.patch +e84ab2a28d08570590ad06219e1a99a8cc47a589d4170f5ff1bf6eeb1b50c1e4e21857a5633495d2271ae702b9bfdc8aa76bb56d572b236d54a4ec08566f13d6 0010-snd-meson-activate-HDMI-audio-path.patch +35962172f6c2682f0e3ba438ca9e62ddd679a0cd665e6bc6208167a753877a02a1d4db8d78b230de44ab494016cf9d47f0e61d31121cb0b8b676b689f1258f71 0011-drm-meson-select-dw-hdmi-i2s-audio-for-meson-hdmi.patch +db2c10c9e4b58e7f0b72a0b8b81ef39544d0e14ef5814dda4d6d5d98d181c9c0f5993ac0e6bc98ea8c79b5d22a3c4eb38793ddb22e7e2553f4643027767a597e 0012-ARM64-dts-meson-gx-add-sound-dai-cells-to-HDMI-node.patch +4f0073aa0a4ed1297b00817bb6cad704362c32fda284780e7a73505dab8e3cfdec5eb851fccdf7e6bf16c1788bc0f8f6ca58b0898adacca678f46ca940aa493a 0013-ARM64-dts-meson-activate-hdmi-audio-HDMI-enabled-boa.patch +998c39cf37d2258582d6d60a7e6a702601430645b01c971c162bb219684e363fd11d0572cefb15f00551fe6bc95d97d1c101e6f36b1ae015083edeb5742b3df3 0014-drm-bridge-dw-hdmi-Use-AUTO-CTS-setup-mode-when-non-.patch +03c2649558bedc2bf34f0e139db489d6891bda6041384d0023cf700be31d1fb1f983c8bc7d58754acd0a57986432b0330996c82f7dc7fd4a6044589725fc4c9c 0017-soc-amlogic-add-meson-canvas-driver.patch +7c1e84356a62a175326f56e71e6984aa4a00ab4b50ac09cce8a078890f2c2ac700d53c53b1229e8dbfb82f36bac32b3082ccdf91a8f92545148bd705784b38e4 0018-ARM64-dts-meson-gx-add-dmcbus-and-canvas-nodes.patch +f2a94fde352734e1de54f10703d792c62c95ae4b68c2c0ce9eb78fded934084fab4b999c930f7228982044052e5b331cce60cf72dcc1f181bcc88eaf68116bb8 0020-drm-meson-Use-optional-canvas-provider.patch +635410e2f6476ee1f1114d31c06f997d5997e117d857ef67f7acb456b52941a4b4aa0cdd94ce9788a017a07d75143d783e900e03eac009d030c90fbe95798003 0021-arm64-dts-meson-gx-Add-canvas-provider-node-to-the-v.patch +df666c95cc1d0dc4c9dd93f79778c5880afd89c320a0edb22dc3f017388cd2df88806aae4c7cde4c412e6643fcd1a8df4c6f15b41b0b0e6b020eced9cedf8d9a 0022-drm-meson-Support-Overlay-plane-for-video-rendering.patch +8e3f62fc50c8478951f8a2e6c5d8c8f38f56ed4d68f9c0fb55c23c46d3ae2182e8a6e5f23171aac08867da4bb16d7c98741a80a3ae6a3acfc41568350be59a57 0023-drm-meson-move-OSD-scaler-management-into-plane-atom.patch +63c41b6db1386530339bd3b6d87849bb38a8d51ec8fcd0bf3c1bcb77c567c589e069dee02f066030f608288fd510e3af244ff45b346e0addc31614867daab334 0024-drm-meson-Add-primary-plane-scaling.patch +64682336c72ac1e505e19e4a099bb2c6f8ba6e8df2ed3b149e355c50b27a29337ac1de7768dd0fd3274bda4fe2f6cc53d068d8a1e6325d27c42e372a0324cbd2 0026-pinctrl-meson-gxl-remove-invalid-GPIOX-tsin_a-pins.patch +332aa83ae3c581b81dae9b37df047fa5e553b0c31790713395ab562aa03ac32fbf909ab6372aba22d06cb82c509efef68af90aeaf66ca5cf70c0861c69039139 0027-arm64-dts-meson-gx-Add-hdmi_5v-regulator-as-hdmi-tx-.patch +0cb8ed9fb52cfdcfac8dbbab5b08f4b5fe1b8716655fe77f37ff1ee39355fac89314dd318856582c62dce1fd56232b4a2725c60a2d07ef15efba86160eb093f0 0028-arm64-dts-meson-gxl-libretech-cc-fix-GPIO-lines-name.patch +f4d251863ac20a95642a60f7c6c51694d36a12f745864fccbb6513b2e53fb0039fa316f4bcd245abe306d2d0aeb0037498f9e21333ab1d84e58fb5d287632093 0029-arm64-dts-meson-gxbb-nanopi-k2-fix-GPIO-lines-names.patch +c481cde9a8bf2609b99e1a7a94bdfead78afe8665347dfffcde8747b8e2817356123e0e44b71de4c5b85224aa20c3c63d5ce3f44e49e3969f0ea28f4df94dc62 0030-arm64-dts-meson-gxbb-odroidc2-fix-GPIO-lines-names.patch +768d954c5580b68e999bf3ed051219187c96de88f27be54e52242687f2ef505c0616dabbc5bee42fdc6d20d53d973f2e64ef4809649fb2e666d8727ed314f3fd 0031-arm64-dts-meson-gxl-khadas-vim-fix-GPIO-lines-names.patch +e23b9303036d736a3f9abed22712848da514f6388de66b3029bde551a85b7d42d1506ee960a539457c8dd7d8b6c07e80919ca0714a9435bdcaa85ca2e201d69f 0032-drm-meson-Add-support-for-VIC-alternate-timings.patch +671da78856f524f7e491e35dcb2764acb6ff0e99eee4df6c9e9538dc321c6c2e4481fd8c39daa2cb32d23d38c96823e5ab04db0342b2daabd5d1b7efdb0d6288 0033-media-meson-add-v4l2-m2m-video-decoder-driver.patch +8d13b4e5c503a196b29263a85d3bc2d9cd1b95d186e6347532b8ae7efc35c93ed72d3f9c4a7ff12b5c2186b3f7d3b35247858e66a96382ba3a2630db35cdf794 0034-MAINTAINERS-Add-meson-video-decoder.patch +c1817a1b59a4a38f7a0689c1e2603398f538d9270a93b40e2ce3e9633b71943beb385206d802673b18f244fb5d9a6c9b2e0498d7803432cf98750843fb91ce5f 0035-arm64-dts-meson-gx-add-vdec-entry.patch +bdd5bd9e4203e3e7bd6412b2068963e22a974316b8ee1a707933808f1ec03ab43aa174804e7fd21f2f7062dc25226e4f2047a863366f132d8687d098a203c6d1 0036-arm64-dts-meson-add-vdec-entries.patch +9c3b9170bbc5fbf3331052e5a99864b76cee6c2c50629f22d33d6e0b01de164e34db4a57c3864e6cb10ff9486999842c9d5f5d38bcb65ef98ccca34c3a6366be 0037-meson-vdec-introduce-controls-and-V4L2_CID_MIN_BUFFE.patch +6bd52ae9872d79b5b2199d23bbef6f73b7530c54df702edf0ce040ba799c60265d199f75fcd4301587100fe60a6d43988bdffdfcb6f98be0c0d8a79f27d206bf 0038-media-videodev2-add-V4L2_FMT_FLAG_NO_SOURCE_CHANGE.patch +516113c435158775f85a3904e5941bcfe641428cfce1945722a2ab8f7897a8493dca634f3527fa4d8fb0433635efcd48353a2feadee94e3467057ce4ff966964 0039-meson-vdec-allow-subscribing-to-V4L2_EVENT_SOURCE_CH.patch +0332979243ae7c182006050c3c2b34c9035ce9f3b084659ec0c3fd39c6772d42c0f5420628150113d2d149d8b1a2481d42650aae08ac054d25cf19178ba64ebc 0040-media-meson-vdec-add-H.264-decoding-support.patch +d696a4a726801786e65fcb69f70c5271a982b2d494a8036d7d8a3e201f910d0e1dc411283128b3460bf2371cb43817d760c0de866cfc22a985f1ff052b98cbff 0041-media-meson-vdec-add-MPEG4-decoding-support.patch +1381cbabd3e040d6e14c1f270904b2c41c6758f2757c3871d1a09d41ab4ce8f85cde95ed1cc989fe59addfa3eae6246f7cc25b63f213f9b28cc73ec86a1c3deb 0042-media-meson-vdec-add-MJPEG-decoding-support.patch +36281f344133b7f4130d69685b1802524a597e110e792f0cae43991eb3a15282d77779cca8033679b943660cd23c9da54c62178b99999e91dc8581052cb53945 0043-clk-meson-gxbb-set-fclk_div3-as-CLK_IS_CRITICAL.patch +ddcb198b0e052975764dfaf140763247e80632eee5f49e3316dc4f1b6220da80fdca0dc32ec618b27fb0487b3ff0676f2a2ed07d5cecfad350bafba0b79ddfed 0008-drm-meson-Add-HDMI-1.4-4k-modes.patch +a58680d93e56fbc95e9e255b79822fcb7960d1f58dbf819dda729b21b61949f42ac58454a6403e77b0d12708613425f331531de700a74c18b262f1336849d5a1 0009-drm-meson-Use-drm_fbdev_generic_setup.patch +b103daed449e59ce1d73e929c694f042e461241ef9d09592828414ab04a9c7ce07c16f636461d8bc5ba49650b9f584a861d5bd4267e90899b8bd72ec113bee4e 0010-fixup-drm-meson-Use-optional-canvas-provider.patch +5e25559535d0a1529c8b5e31e9dab82c0d41f70e515a2ca4ed614177e127c0933cc1d54566ef6f063b19dbd19f3b1d67cb51ad4d8a87877140cf166c5969a6f0 0012-drm-meson-add-support-for-1080p25-mode.patch +8de5c1f61e8467504f96db3c615658f40bb217d57a1a0c03b505cd8e310961dce38a191cd40ccecd48ed204fac1c3d3facb371e07ff590b8683afc4420e7f303 0014-drm-bridge-dw-hdmi-Add-SCDC-and-TMDS-Scrambling-supp.patch +8fe6432a889ee13081185eeb8602ea48465b7d858f0b573f5a5c4d35e6ed76edc47f697b0c1729a31e9a319e0712bb0eaa111077d86570d2954dcbe6fceb7b60 0015-drm-meson-add-HDMI-div40-TMDS-mode.patch +358f064f42c9e21afeccb867c062fc18484f91ed7805752edaf74492774962c16348099de004ce130134610f8c5d076fd715ca0de3eb30d0abd23f90236c6a7b 0016-drm-meson-add-support-for-HDMI2.0-2160p-modes.patch +9bd13d763a007ffb9d0e9f9f4c9b5018752c010a39780e467d463153cc79a32774120d7af83496ba706b971dd3bd9bb600fad4795b3c0796e22eb42b23374a90 0017-drm-bridge-dw-hdmi-add-support-for-YUV420-output.patch +f15dd5116fe7464009ade33a9c6d305a7720767e1b31acbf7653cd9d2f83b428e734712739e8bb4cfd04407640187f5528f80d7e8b89992a75f2316b6f8513c6 0018-drm-bridge-dw-hdmi-support-dynamically-get-input-out.patch +2399c1c95455f2e4d0d03d2c84cf454251b7c8a151da555e7665bede91b7b62f96391efac3ca9a5b90761e105409bd033f348e25bc163a439a2ff7efd82e2184 0019-drm-bridge-dw-hdmi-allow-ycbcr420-modes-for-0x200a.patch +26eb2b101ab563558649f38a5102773b2d79eeb39cf04a3f8d512361beed70ad585825233716acb58853887bc863ba76ce87f6eaf6d8c5c22e4caa99a5ce294a 0020-drm-meson-Add-YUV420-output-support.patch +fac806a77816f655354499ab597974659e5d6dc74d04ac74cd81bacd6a264ac13ab33bc230a2c233d98d6e3b0342f329c2af0c6359d9ae8b6b456add06809822 0021-drm-meson-Output-in-YUV444-if-sink-supports-it.patch +e365ba0479201f1c4fb08f872447f2496d4c8d75c8e3f632eeb3f7091e56d0659d4b6f580d384183e0e9c80ccf659b1704ecbaca88b66ca34c78c250c0f55a29 0023-drm-meson-Fix-an-Alpha-Primary-Plane-bug-on-Meson-GX.patch +0aca4ad6af15ea8f8e2e8a05aa3c5a16e1a909ee0f80cb1f64e0ee027a7050b0bbd5815520e146f96641b92e5d39688677de39d4294be6a25ea1a2e519df257b 0001-arm64-dts-meson-Fix-IRQ-trigger-type-for-macirq.patch +613d619e86883d83d53d045ed589d91c74a9f2031de953d2f3e4cdf374ae32841a69b28abfd0d8314a5739f51fbd3ab02c7f8baf4c5060db725d21f7a677db7f 0001-drm-meson-fix-max-mode_config-height-width.patch +ea41c0b63d8890cf5db69a37ead0fb9863299a344cee2d3606cc8c049405a563e139dceb6c908abc57b5b545d3daee16f7b9c6f7f1284cb9cf676ff5830d5c96 add-phicomm-n1.patch dcc02a2e35e530490f18dd1f7e304aebd7c268ed8a46971d94e9da7dfd65b287002e6b921e46db5ce118177c9ab79fbe3640565cb2df19809b6eff514122fb43 bt-btbcm.patch -007075ca40915f20c6c8f5e04825c3433a52621deb689a9a690a3fcc618ca9629bc089a1a2ad5141fc47a8aa0aabb302ecf75e81ef8cae466e978074156231ac brcmfmac-Disable-power-management.patch -cb2ec80ae38c4cc46ea2a4ca6d157498632f8154ef6a1dedb898c70593c812743a69a4c0d36a4e146a71c588e30e9f9b2feb415e00166850c2cf79797048b032 offset.patch -e5d214788f9a28d79a9ef135fbed3b3c87982941788ca74d31d919534d1627c1265b268982f67de6c14b331d1a38938b4c2772f17b3db3bddd3983f919fe73ca off_error_text_offset.patch" +298ded76cf87f08c9357505e9dd7582d886a5e59111eee81ecb136b2dd1f3f404899a32029696038cb2299760568b2b0345c92dfd961591b28ec2964460f09b1 text_offset.patch" diff --git a/testing/linux-amlogic/add-phicomm-n1.patch b/testing/linux-amlogic/add-phicomm-n1.patch index 2140d8d828..3c3b16e32c 100644 --- a/testing/linux-amlogic/add-phicomm-n1.patch +++ b/testing/linux-amlogic/add-phicomm-n1.patch @@ -1,5 +1,5 @@ diff --git a/arch/arm64/boot/dts/amlogic/Makefile b/arch/arm64/boot/dts/amlogic/Makefile -index 34dd0e9..773f415 100644 +index a97c0e2..ebd96da 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 @@ -7,9 +7,9 @@ index 34dd0e9..773f415 100644 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 + dtb-$(CONFIG_ARCH_MESON) += meson-gxl-s805x-p241.dtb + dtb-$(CONFIG_ARCH_MESON) += meson-gxl-s905w-p281.dtb + dtb-$(CONFIG_ARCH_MESON) += meson-gxl-s905w-tx3-mini.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 diff --git a/testing/linux-amlogic/brcmfmac-Disable-power-management.patch b/testing/linux-amlogic/brcmfmac-Disable-power-management.patch deleted file mode 100644 index 17629f8e0c..0000000000 --- a/testing/linux-amlogic/brcmfmac-Disable-power-management.patch +++ /dev/null @@ -1,13 +0,0 @@ -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/off_error_text_offset.patch b/testing/linux-amlogic/off_error_text_offset.patch deleted file mode 100644 index a5069502b6..0000000000 --- a/testing/linux-amlogic/off_error_text_offset.patch +++ /dev/null @@ -1,13 +0,0 @@ -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 deleted file mode 100644 index 644b65ee4a..0000000000 --- a/testing/linux-amlogic/offset.patch +++ /dev/null @@ -1,13 +0,0 @@ -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)) diff --git a/testing/linux-amlogic/text_offset.patch b/testing/linux-amlogic/text_offset.patch new file mode 100644 index 0000000000..fff476e656 --- /dev/null +++ b/testing/linux-amlogic/text_offset.patch @@ -0,0 +1,26 @@ +diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile +index 106039d..651d990 100644 +--- a/arch/arm64/Makefile ++++ b/arch/arm64/Makefile +@@ -88,7 +88,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)) +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 + + /* |