diff --git a/config/kernel/linux-meson64-current.config b/config/kernel/linux-meson64-current.config index 7fb545173..8854751e9 100644 --- a/config/kernel/linux-meson64-current.config +++ b/config/kernel/linux-meson64-current.config @@ -1,6 +1,6 @@ # # Automatically generated file; DO NOT EDIT. -# Linux/arm64 5.4.13 Kernel Configuration +# Linux/arm64 5.4.20 Kernel Configuration # # @@ -5351,6 +5351,12 @@ CONFIG_SND_DESIGNWARE_I2S=m # # ASoC support for Amlogic platforms # +CONFIG_SND_MESON_AIU_BUS=m +CONFIG_SND_MESON_AIU_FIFO=m +CONFIG_SND_MESON_AIU_I2S_FIFO=m +CONFIG_SND_MESON_AIU_SPDIF_FIFO=m +CONFIG_SND_MESON_AIU_I2S_ENCODER=m +CONFIG_SND_MESON_AIU_SPDIF_ENCODER=m CONFIG_SND_MESON_AXG_FIFO=m CONFIG_SND_MESON_AXG_FRDDR=m CONFIG_SND_MESON_AXG_TODDR=m @@ -5362,12 +5368,11 @@ CONFIG_SND_MESON_AXG_SOUND_CARD=m CONFIG_SND_MESON_AXG_SPDIFOUT=m CONFIG_SND_MESON_AXG_SPDIFIN=m CONFIG_SND_MESON_AXG_PDM=m +CONFIG_SND_MESON_CARD_UTILS=m +CONFIG_SND_MESON_GX_SOUND_CARD=m CONFIG_SND_MESON_G12A_TOHDMITX=m # end of ASoC support for Amlogic platforms -CONFIG_SND_SOC_MESON_GX=m -CONFIG_SND_SOC_MESON_GX_I2S=m -CONFIG_SND_SOC_MESON_GX_SPDIF=m # CONFIG_SND_SOC_SOF_TOPLEVEL is not set # @@ -7883,8 +7888,6 @@ CONFIG_CRYPTO_DEV_CCREE=m CONFIG_CRYPTO_DEV_HISI_QM=m CONFIG_CRYPTO_HISI_SGL=m CONFIG_CRYPTO_DEV_HISI_ZIP=m -CONFIG_CRYPTO_DEV_AMLOGIC_GXL=m -# CONFIG_CRYPTO_DEV_AMLOGIC_GXL_DEBUG is not set CONFIG_ASYMMETRIC_KEY_TYPE=y CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y CONFIG_ASYMMETRIC_TPM_KEY_SUBTYPE=m @@ -8025,7 +8028,6 @@ CONFIG_FONT_SUPPORT=y # CONFIG_FONTS is not set CONFIG_FONT_8x8=y CONFIG_FONT_8x16=y -CONFIG_SG_SPLIT=y CONFIG_SG_POOL=y CONFIG_SBITMAP=y # CONFIG_STRING_SELFTEST is not set diff --git a/patch/kernel/meson64-current/linux-5.4-net-smsc95xx-allow-mac-address-as-param.patch b/patch/kernel/meson64-current/linux-5.4-net-smsc95xx-allow-mac-address-as-param.disabled similarity index 100% rename from patch/kernel/meson64-current/linux-5.4-net-smsc95xx-allow-mac-address-as-param.patch rename to patch/kernel/meson64-current/linux-5.4-net-smsc95xx-allow-mac-address-as-param.disabled diff --git a/patch/kernel/meson64-current/meson64_fclk_div3.patch b/patch/kernel/meson64-current/meson64_fclk_div3.disabled similarity index 100% rename from patch/kernel/meson64-current/meson64_fclk_div3.patch rename to patch/kernel/meson64-current/meson64_fclk_div3.disabled diff --git a/patch/kernel/meson64-current/z-libretech-stable-build.patch b/patch/kernel/meson64-current/z-libretech-stable-build.patch new file mode 100644 index 000000000..0b47ff8d5 --- /dev/null +++ b/patch/kernel/meson64-current/z-libretech-stable-build.patch @@ -0,0 +1,7226 @@ +diff --git b/Documentation/ABI/testing/sysfs-firmware-devicetree-overlays a/Documentation/ABI/testing/sysfs-firmware-devicetree-overlays +new file mode 100644 +index 000000000000..88d15498a21b +--- /dev/null ++++ a/Documentation/ABI/testing/sysfs-firmware-devicetree-overlays +@@ -0,0 +1,52 @@ ++What: /sys/firmware/devicetree/overlays/ ++Date: October 2015 ++Contact: Pantelis Antoniou ++Description: ++ This directory contains the applied device tree overlays of ++ the running system, as directories of the overlay id. ++ ++What: /sys/firmware/devicetree/overlays/enable ++Date: October 2015 ++Contact: Pantelis Antoniou ++Description: ++ The master enable switch, by default is 1, and when ++ set to 0 it cannot be re-enabled for security reasons. ++ ++ The discussion about this switch takes place in: ++ http://comments.gmane.org/gmane.linux.drivers.devicetree/101871 ++ ++ Kees Cook: ++ "Coming from the perspective of drawing a bright line between ++ kernel and the root user (which tends to start with disabling ++ kernel module loading), I would say that there at least needs ++ to be a high-level one-way "off" switch for the interface so ++ that systems that have this interface can choose to turn it off ++ during initial boot, etc." ++ ++What: /sys/firmware/devicetree/overlays/ ++Date: October 2015 ++Contact: Pantelis Antoniou ++Description: ++ Each directory represents an applied overlay, containing ++ the following attribute files. ++ ++What: /sys/firmware/devicetree/overlays//can_remove ++Date: October 2015 ++Contact: Pantelis Antoniou ++Description: ++ The attribute set to 1 means that the overlay can be removed, ++ while 0 means that the overlay is being overlapped therefore ++ removal is prohibited. ++ ++What: /sys/firmware/devicetree/overlays/// ++Date: October 2015 ++Contact: Pantelis Antoniou ++Description: ++ Each of these directories contain information about of the ++ particular overlay fragment. ++ ++What: /sys/firmware/devicetree/overlays///target ++Date: October 2015 ++Contact: Pantelis Antoniou ++Description: ++ The full-path of the target of the fragment +diff --git b/Documentation/admin-guide/kernel-parameters.rst a/Documentation/admin-guide/kernel-parameters.rst +index d05d531b4ec9..63841437ae65 100644 +--- b/Documentation/admin-guide/kernel-parameters.rst ++++ a/Documentation/admin-guide/kernel-parameters.rst +@@ -127,6 +127,7 @@ parameter is applicable:: + NET Appropriate network support is enabled. + NUMA NUMA support is enabled. + NFS Appropriate NFS support is enabled. ++ OF Open Firmware support (device tree) is enabled. + OSS OSS sound support is enabled. + PV_OPS A paravirtualized kernel is enabled. + PARIDE The ParIDE (parallel port IDE) subsystem is enabled. +diff --git b/Documentation/admin-guide/kernel-parameters.txt a/Documentation/admin-guide/kernel-parameters.txt +index 5594c8bf1dcd..167a8bbe087e 100644 +--- b/Documentation/admin-guide/kernel-parameters.txt ++++ a/Documentation/admin-guide/kernel-parameters.txt +@@ -3227,6 +3227,8 @@ + This can be set from sysctl after boot. + See Documentation/admin-guide/sysctl/vm.rst for details. + ++ of_overlay_disable [OF] Disable device tree overlays at boot time. ++ + ohci1394_dma=early [HW] enable debugging via the ohci1394 driver. + See Documentation/debugging-via-ohci1394.txt for more + info. +diff --git b/Documentation/devicetree/bindings/arm/amlogic.yaml a/Documentation/devicetree/bindings/arm/amlogic.yaml +index 99015cef8bb1..19b7851ede34 100644 +--- b/Documentation/devicetree/bindings/arm/amlogic.yaml ++++ a/Documentation/devicetree/bindings/arm/amlogic.yaml +@@ -104,6 +104,7 @@ properties: + - enum: + - amlogic,p230 + - amlogic,p231 ++ - libretech,aml-s905d-pc + - phicomm,n1 + - const: amlogic,s905d + - const: amlogic,meson-gxl +@@ -115,6 +116,7 @@ properties: + - amlogic,q201 + - khadas,vim2 + - kingnovel,r-box-pro ++ - libretech,aml-s912-pc + - nexbox,a1 + - tronsmart,vega-s96 + - const: amlogic,s912 +diff --git b/arch/arm/boot/dts/meson.dtsi a/arch/arm/boot/dts/meson.dtsi +index 5d198309058a..c4447f6c8b2c 100644 +--- b/arch/arm/boot/dts/meson.dtsi ++++ a/arch/arm/boot/dts/meson.dtsi +@@ -282,11 +282,4 @@ + }; + }; + }; +- +- xtal: xtal-clk { +- compatible = "fixed-clock"; +- clock-frequency = <24000000>; +- clock-output-names = "xtal"; +- #clock-cells = <0>; +- }; + }; /* end of / */ +diff --git b/arch/arm/boot/dts/meson6.dtsi a/arch/arm/boot/dts/meson6.dtsi +index 4716030a48d0..2d31b7ce3f8c 100644 +--- b/arch/arm/boot/dts/meson6.dtsi ++++ a/arch/arm/boot/dts/meson6.dtsi +@@ -36,6 +36,13 @@ + ranges = <0x0 0xd0000000 0x40000>; + }; + ++ xtal: xtal-clk { ++ compatible = "fixed-clock"; ++ clock-frequency = <24000000>; ++ clock-output-names = "xtal"; ++ #clock-cells = <0>; ++ }; ++ + clk81: clk@0 { + #clock-cells = <0>; + compatible = "fixed-clock"; +diff --git b/arch/arm/boot/dts/meson8.dtsi a/arch/arm/boot/dts/meson8.dtsi +index eedb92526968..db2033f674c6 100644 +--- b/arch/arm/boot/dts/meson8.dtsi ++++ a/arch/arm/boot/dts/meson8.dtsi +@@ -3,7 +3,6 @@ + * Copyright 2014 Carlo Caione + */ + +-#include + #include + #include + #include +@@ -196,14 +195,6 @@ + #size-cells = <1>; + ranges = <0x0 0xc8000000 0x8000>; + +- ddr_clkc: clock-controller@400 { +- compatible = "amlogic,meson8-ddr-clkc"; +- reg = <0x400 0x20>; +- clocks = <&xtal>; +- clock-names = "xtal"; +- #clock-cells = <1>; +- }; +- + dmcbus: bus@6000 { + compatible = "simple-bus"; + reg = <0x6000 0x400>; +@@ -464,8 +455,6 @@ + &hhi { + clkc: clock-controller { + compatible = "amlogic,meson8-clkc"; +- clocks = <&xtal>, <&ddr_clkc DDR_CLKID_DDR_PLL>; +- clock-names = "xtal", "ddr_pll"; + #clock-cells = <1>; + #reset-cells = <1>; + }; +@@ -540,7 +529,8 @@ + + &saradc { + compatible = "amlogic,meson8-saradc", "amlogic,meson-saradc"; +- clocks = <&xtal>, <&clkc CLKID_SAR_ADC>; ++ clocks = <&clkc CLKID_XTAL>, ++ <&clkc CLKID_SAR_ADC>; + clock-names = "clkin", "core"; + amlogic,hhi-sysctrl = <&hhi>; + nvmem-cells = <&temperature_calib>; +@@ -558,31 +548,31 @@ + }; + + &timer_abcde { +- clocks = <&xtal>, <&clkc CLKID_CLK81>; ++ clocks = <&clkc CLKID_XTAL>, <&clkc CLKID_CLK81>; + clock-names = "xtal", "pclk"; + }; + + &uart_AO { + compatible = "amlogic,meson8-uart", "amlogic,meson-uart"; +- clocks = <&clkc CLKID_CLK81>, <&xtal>, <&clkc CLKID_CLK81>; ++ clocks = <&clkc CLKID_CLK81>, <&clkc CLKID_XTAL>, <&clkc CLKID_CLK81>; + clock-names = "baud", "xtal", "pclk"; + }; + + &uart_A { + compatible = "amlogic,meson8-uart", "amlogic,meson-uart"; +- clocks = <&clkc CLKID_CLK81>, <&xtal>, <&clkc CLKID_UART0>; ++ clocks = <&clkc CLKID_CLK81>, <&clkc CLKID_XTAL>, <&clkc CLKID_UART0>; + clock-names = "baud", "xtal", "pclk"; + }; + + &uart_B { + compatible = "amlogic,meson8-uart", "amlogic,meson-uart"; +- clocks = <&clkc CLKID_CLK81>, <&xtal>, <&clkc CLKID_UART1>; ++ clocks = <&clkc CLKID_CLK81>, <&clkc CLKID_XTAL>, <&clkc CLKID_UART1>; + clock-names = "baud", "xtal", "pclk"; + }; + + &uart_C { + compatible = "amlogic,meson8-uart", "amlogic,meson-uart"; +- clocks = <&clkc CLKID_CLK81>, <&xtal>, <&clkc CLKID_UART2>; ++ clocks = <&clkc CLKID_CLK81>, <&clkc CLKID_XTAL>, <&clkc CLKID_UART2>; + clock-names = "baud", "xtal", "pclk"; + }; + +diff --git b/arch/arm/boot/dts/meson8b-ec100.dts a/arch/arm/boot/dts/meson8b-ec100.dts +index 163a200d5a7b..bed1dfef1985 100644 +--- b/arch/arm/boot/dts/meson8b-ec100.dts ++++ a/arch/arm/boot/dts/meson8b-ec100.dts +@@ -377,7 +377,7 @@ + status = "okay"; + pinctrl-0 = <&pwm_c1_pins>, <&pwm_d_pins>; + pinctrl-names = "default"; +- clocks = <&xtal>, <&xtal>; ++ clocks = <&clkc CLKID_XTAL>, <&clkc CLKID_XTAL>; + clock-names = "clkin0", "clkin1"; + }; + +diff --git b/arch/arm/boot/dts/meson8b-mxq.dts a/arch/arm/boot/dts/meson8b-mxq.dts +index 33037ef62d0a..6e39ad52e42d 100644 +--- b/arch/arm/boot/dts/meson8b-mxq.dts ++++ a/arch/arm/boot/dts/meson8b-mxq.dts +@@ -165,7 +165,7 @@ + status = "okay"; + pinctrl-0 = <&pwm_c1_pins>, <&pwm_d_pins>; + pinctrl-names = "default"; +- clocks = <&xtal>, <&xtal>; ++ clocks = <&clkc CLKID_XTAL>, <&clkc CLKID_XTAL>; + clock-names = "clkin0", "clkin1"; + }; + +diff --git b/arch/arm/boot/dts/meson8b-odroidc1.dts a/arch/arm/boot/dts/meson8b-odroidc1.dts +index a2a47804fc4a..a24eccc354b9 100644 +--- b/arch/arm/boot/dts/meson8b-odroidc1.dts ++++ a/arch/arm/boot/dts/meson8b-odroidc1.dts +@@ -340,7 +340,7 @@ + status = "okay"; + pinctrl-0 = <&pwm_c1_pins>, <&pwm_d_pins>; + pinctrl-names = "default"; +- clocks = <&xtal>, <&xtal>; ++ clocks = <&clkc CLKID_XTAL>, <&clkc CLKID_XTAL>; + clock-names = "clkin0", "clkin1"; + }; + +diff --git b/arch/arm/boot/dts/meson8b.dtsi a/arch/arm/boot/dts/meson8b.dtsi +index 2737b99da5f6..1e8c5d7bc824 100644 +--- b/arch/arm/boot/dts/meson8b.dtsi ++++ a/arch/arm/boot/dts/meson8b.dtsi +@@ -4,7 +4,6 @@ + * Author: Carlo Caione + */ + +-#include + #include + #include + #include +@@ -173,14 +172,6 @@ + #size-cells = <1>; + ranges = <0x0 0xc8000000 0x8000>; + +- ddr_clkc: clock-controller@400 { +- compatible = "amlogic,meson8b-ddr-clkc"; +- reg = <0x400 0x20>; +- clocks = <&xtal>; +- clock-names = "xtal"; +- #clock-cells = <1>; +- }; +- + dmcbus: bus@6000 { + compatible = "simple-bus"; + reg = <0x6000 0x400>; +@@ -443,8 +434,6 @@ + &hhi { + clkc: clock-controller { + compatible = "amlogic,meson8-clkc"; +- clocks = <&xtal>, <&ddr_clkc DDR_CLKID_DDR_PLL>; +- clock-names = "xtal", "ddr_pll"; + #clock-cells = <1>; + #reset-cells = <1>; + }; +@@ -519,7 +508,8 @@ + + &saradc { + compatible = "amlogic,meson8b-saradc", "amlogic,meson-saradc"; +- clocks = <&xtal>, <&clkc CLKID_SAR_ADC>; ++ clocks = <&clkc CLKID_XTAL>, ++ <&clkc CLKID_SAR_ADC>; + clock-names = "clkin", "core"; + amlogic,hhi-sysctrl = <&hhi>; + nvmem-cells = <&temperature_calib>; +@@ -533,31 +523,31 @@ + }; + + &timer_abcde { +- clocks = <&xtal>, <&clkc CLKID_CLK81>; ++ clocks = <&clkc CLKID_XTAL>, <&clkc CLKID_CLK81>; + clock-names = "xtal", "pclk"; + }; + + &uart_AO { + compatible = "amlogic,meson8b-uart", "amlogic,meson-uart"; +- clocks = <&clkc CLKID_CLK81>, <&xtal>, <&clkc CLKID_CLK81>; ++ clocks = <&clkc CLKID_CLK81>, <&clkc CLKID_XTAL>, <&clkc CLKID_CLK81>; + clock-names = "baud", "xtal", "pclk"; + }; + + &uart_A { + compatible = "amlogic,meson8b-uart", "amlogic,meson-uart"; +- clocks = <&clkc CLKID_CLK81>, <&xtal>, <&clkc CLKID_UART0>; ++ clocks = <&clkc CLKID_CLK81>, <&clkc CLKID_XTAL>, <&clkc CLKID_UART0>; + clock-names = "baud", "xtal", "pclk"; + }; + + &uart_B { + compatible = "amlogic,meson8b-uart", "amlogic,meson-uart"; +- clocks = <&clkc CLKID_CLK81>, <&xtal>, <&clkc CLKID_UART1>; ++ clocks = <&clkc CLKID_CLK81>, <&clkc CLKID_XTAL>, <&clkc CLKID_UART1>; + clock-names = "baud", "xtal", "pclk"; + }; + + &uart_C { + compatible = "amlogic,meson8b-uart", "amlogic,meson-uart"; +- clocks = <&clkc CLKID_CLK81>, <&xtal>, <&clkc CLKID_UART2>; ++ clocks = <&clkc CLKID_CLK81>, <&clkc CLKID_XTAL>, <&clkc CLKID_UART2>; + clock-names = "baud", "xtal", "pclk"; + }; + +diff --git b/arch/arm64/boot/dts/amlogic/Makefile a/arch/arm64/boot/dts/amlogic/Makefile +index f726804d32ed..f25cd8675db8 100644 +--- b/arch/arm64/boot/dts/amlogic/Makefile ++++ a/arch/arm64/boot/dts/amlogic/Makefile +@@ -28,13 +28,13 @@ dtb-$(CONFIG_ARCH_MESON) += meson-gxl-s905d-phicomm-n1.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 ++dtb-$(CONFIG_ARCH_MESON) += meson-gxl-s905d-libretech-pc.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-gxm-q201.dtb + dtb-$(CONFIG_ARCH_MESON) += meson-gxm-rbox-pro.dtb ++dtb-$(CONFIG_ARCH_MESON) += meson-gxm-s912-libretech-pc.dtb + dtb-$(CONFIG_ARCH_MESON) += meson-gxm-vega-s96.dtb + dtb-$(CONFIG_ARCH_MESON) += meson-sm1-sei610.dtb + dtb-$(CONFIG_ARCH_MESON) += meson-sm1-khadas-vim3l.dtb +- +-subdir-y := $(dts-dirs) overlay +diff --git b/arch/arm64/boot/dts/amlogic/meson-axg.dtsi a/arch/arm64/boot/dts/amlogic/meson-axg.dtsi +index 04803c3bccfa..bb4a2acb9970 100644 +--- b/arch/arm64/boot/dts/amlogic/meson-axg.dtsi ++++ a/arch/arm64/boot/dts/amlogic/meson-axg.dtsi +@@ -117,7 +117,6 @@ + #address-cells = <1>; + #size-cells = <1>; + read-only; +- secure-monitor = <&sm>; + }; + + psci { +diff --git b/arch/arm64/boot/dts/amlogic/meson-gx-libretech-pc.dtsi a/arch/arm64/boot/dts/amlogic/meson-gx-libretech-pc.dtsi +new file mode 100644 +index 000000000000..a3fce8f2cf48 +--- /dev/null ++++ a/arch/arm64/boot/dts/amlogic/meson-gx-libretech-pc.dtsi +@@ -0,0 +1,416 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2019 BayLibre SAS. ++ * Author: Jerome Brunet ++ */ ++ ++/* Libretech Amlogic GX PC form factor - AKA: Tartiflette */ ++ ++#include ++#include ++ ++/ { ++ adc-keys { ++ compatible = "adc-keys"; ++ io-channels = <&saradc 0>; ++ io-channel-names = "buttons"; ++ keyup-threshold-microvolt = <1800000>; ++ ++ update-button { ++ label = "update"; ++ linux,code = ; ++ press-threshold-microvolt = <1300000>; ++ }; ++ }; ++ ++ aliases { ++ serial0 = &uart_AO; ++ ethernet0 = ðmac; ++ spi0 = &spifc; ++ }; ++ ++ chosen { ++ stdout-path = "serial0:115200n8"; ++ }; ++ ++ cvbs-connector { ++ compatible = "composite-video-connector"; ++ status = "disabled"; ++ ++ port { ++ cvbs_connector_in: endpoint { ++ remote-endpoint = <&cvbs_vdac_out>; ++ }; ++ }; ++ }; ++ ++ emmc_pwrseq: emmc-pwrseq { ++ compatible = "mmc-pwrseq-emmc"; ++ reset-gpios = <&gpio BOOT_9 GPIO_ACTIVE_LOW>; ++ }; ++ ++ hdmi-connector { ++ compatible = "hdmi-connector"; ++ type = "a"; ++ ++ port { ++ hdmi_connector_in: endpoint { ++ remote-endpoint = <&hdmi_tx_tmds_out>; ++ }; ++ }; ++ }; ++ ++ gpio-keys-polled { ++ compatible = "gpio-keys-polled"; ++ poll-interval = <100>; ++ ++ power-button { ++ label = "power"; ++ linux,code = ; ++ gpios = <&gpio_ao GPIOAO_2 GPIO_ACTIVE_LOW>; ++ }; ++ }; ++ ++ memory@0 { ++ device_type = "memory"; ++ reg = <0x0 0x0 0x0 0x80000000>; ++ }; ++ ++ ao_5v: regulator-ao_5v { ++ compatible = "regulator-fixed"; ++ regulator-name = "AO_5V"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ vin-supply = <&dc_in>; ++ regulator-always-on; ++ }; ++ ++ dc_in: regulator-dc_in { ++ compatible = "regulator-fixed"; ++ regulator-name = "DC_IN"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ regulator-always-on; ++ }; ++ ++ leds { ++ compatible = "gpio-leds"; ++ ++ green { ++ color = ; ++ function = LED_FUNCTION_DISK_ACTIVITY; ++ gpios = <&gpio_ao GPIOAO_9 GPIO_ACTIVE_HIGH>; ++ linux,default-trigger = "disk-activity"; ++ }; ++ ++ blue { ++ color = ; ++ function = LED_FUNCTION_STATUS; ++ gpios = <&gpio GPIODV_28 GPIO_ACTIVE_HIGH>; ++ linux,default-trigger = "heartbeat"; ++ panic-indicator; ++ }; ++ }; ++ ++ vcc_card: regulator-vcc_card { ++ compatible = "regulator-fixed"; ++ regulator-name = "VCC_CARD"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ vin-supply = <&vddio_ao3v3>; ++ ++ gpio = <&gpio GPIODV_4 GPIO_ACTIVE_HIGH>; ++ enable-active-high; ++ }; ++ ++ vcc5v: regulator-vcc5v { ++ compatible = "regulator-fixed"; ++ regulator-name = "VCC5V"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ vin-supply = <&ao_5v>; ++ ++ gpio = <&gpio GPIOH_3 GPIO_OPEN_DRAIN>; ++ }; ++ ++ vddio_ao18: regulator-vddio_ao18 { ++ compatible = "regulator-fixed"; ++ regulator-name = "VDDIO_AO18"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ vin-supply = <&ao_5v>; ++ regulator-always-on; ++ }; ++ ++ vddio_ao3v3: regulator-vddio_ao3v3 { ++ compatible = "regulator-fixed"; ++ regulator-name = "VDDIO_AO3V3"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ vin-supply = <&ao_5v>; ++ regulator-always-on; ++ }; ++ ++ vddio_boot: regulator-vddio_boot { ++ compatible = "regulator-fixed"; ++ regulator-name = "VDDIO_BOOT"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ vin-supply = <&vddio_ao3v3>; ++ regulator-always-on; ++ }; ++ ++ vddio_card: regulator-vddio-card { ++ compatible = "regulator-gpio"; ++ regulator-name = "VDDIO_CARD"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <3300000>; ++ ++ gpios = <&gpio GPIODV_5 GPIO_ACTIVE_HIGH>; ++ gpios-states = <0>; ++ ++ states = <3300000 0>, ++ <1800000 1>; ++ ++ regulator-settling-time-up-us = <200>; ++ regulator-settling-time-down-us = <50000>; ++ }; ++ ++ sound { ++ compatible = "amlogic,gx-sound-card"; ++ model = "GXL-LIBRETECH-S9XX-PC"; ++ audio-routing = "I2S ENCODER Playback", "I2S FIFO Playback"; ++ ++ assigned-clocks = <&clkc CLKID_MPLL2>, ++ <&clkc CLKID_MPLL0>, ++ <&clkc CLKID_MPLL1>; ++ assigned-clock-parents = <0>, <0>, <0>; ++ assigned-clock-rates = <294912000>, ++ <270950400>, ++ <393216000>; ++ status = "okay"; ++ ++ dai-link-0 { ++ sound-dai = <&i2s_fifo>; ++ }; ++ ++ dai-link-1 { ++ sound-dai = <&i2s_encoder>; ++ dai-format = "i2s"; ++ mclk-fs = <256>; ++ ++ codec-0 { ++ sound-dai = <&hdmi_tx>; ++ }; ++ }; ++ }; ++}; ++ ++&aiu { ++ status = "okay"; ++}; ++ ++&cec_AO { ++ pinctrl-0 = <&ao_cec_pins>; ++ pinctrl-names = "default"; ++ hdmi-phandle = <&hdmi_tx>; ++ status = "okay"; ++}; ++ ++&cvbs_vdac_port { ++ cvbs_vdac_out: endpoint { ++ remote-endpoint = <&cvbs_connector_in>; ++ }; ++}; ++ ++ðmac { ++ pinctrl-0 = <ð_pins>, <ð_phy_irq_pins>; ++ pinctrl-names = "default"; ++ phy-handle = <&external_phy>; ++ amlogic,tx-delay-ns = <2>; ++ phy-mode = "rgmii"; ++ status = "okay"; ++}; ++ ++&external_mdio { ++ external_phy: ethernet-phy@0 { ++ reg = <0>; ++ max-speed = <1000>; ++ reset-assert-us = <10000>; ++ reset-deassert-us = <30000>; ++ reset-gpios = <&gpio GPIOZ_14 GPIO_ACTIVE_LOW>; ++ interrupt-parent = <&gpio_intc>; ++ interrupts = <25 IRQ_TYPE_LEVEL_LOW>; ++ }; ++}; ++ ++&i2s_fifo { ++ status = "okay"; ++}; ++ ++&i2s_encoder { ++ status = "okay"; ++}; ++ ++&pinctrl_periphs { ++ /* ++ * Make sure the reset pin of the usb HUB is driven high to take ++ * it out of reset. ++ */ ++ usb1_rst_pins: usb1_rst_irq { ++ mux { ++ groups = "GPIODV_3"; ++ function = "gpio_periphs"; ++ bias-disable; ++ output-high; ++ }; ++ }; ++ ++ /* Make sure the phy irq pin is properly configured as input */ ++ eth_phy_irq_pins: eth_phy_irq { ++ mux { ++ groups = "GPIOZ_15"; ++ function = "gpio_periphs"; ++ bias-disable; ++ output-disable; ++ }; ++ }; ++}; ++ ++&hdmi_tx { ++ pinctrl-0 = <&hdmi_hpd_pins>, <&hdmi_i2c_pins>; ++ pinctrl-names = "default"; ++ hdmi-supply = <&vcc5v>; ++ status = "okay"; ++}; ++ ++&hdmi_tx_tmds_port { ++ hdmi_tx_tmds_out: endpoint { ++ remote-endpoint = <&hdmi_connector_in>; ++ }; ++}; ++ ++&ir { ++ pinctrl-0 = <&remote_input_ao_pins>; ++ pinctrl-names = "default"; ++ status = "okay"; ++}; ++ ++&i2c_C { ++ pinctrl-0 = <&i2c_c_dv18_pins>; ++ pinctrl-names = "default"; ++ status = "okay"; ++ ++ rtc: rtc@51 { ++ reg = <0x51>; ++ compatible = "nxp,pcf8563"; ++ #clock-cells = <0>; ++ clock-output-names = "rtc_clkout"; ++ }; ++}; ++ ++&pwm_AO_ab { ++ pinctrl-0 = <&pwm_ao_a_3_pins>; ++ pinctrl-names = "default"; ++ clocks = <&clkc CLKID_FCLK_DIV4>; ++ clock-names = "clkin0"; ++ status = "okay"; ++}; ++ ++&pwm_ab { ++ pinctrl-0 = <&pwm_b_pins>; ++ pinctrl-names = "default"; ++ clocks = <&clkc CLKID_FCLK_DIV4>; ++ clock-names = "clkin0"; ++ status = "okay"; ++}; ++ ++&pwm_ef { ++ pinctrl-0 = <&pwm_e_pins>, <&pwm_f_clk_pins>; ++ pinctrl-names = "default"; ++ clocks = <&clkc CLKID_FCLK_DIV4>; ++ clock-names = "clkin0"; ++ status = "okay"; ++}; ++ ++&saradc { ++ vref-supply = <&vddio_ao18>; ++ status = "okay"; ++}; ++ ++/* SD card */ ++&sd_emmc_b { ++ pinctrl-0 = <&sdcard_pins>; ++ pinctrl-1 = <&sdcard_clk_gate_pins>; ++ pinctrl-names = "default", "clk-gate"; ++ ++ bus-width = <4>; ++ cap-sd-highspeed; ++ sd-uhs-sdr12; ++ sd-uhs-sdr25; ++ sd-uhs-sdr50; ++ sd-uhs-ddr50; ++ max-frequency = <200000000>; ++ disable-wp; ++ ++ cd-gpios = <&gpio CARD_6 GPIO_ACTIVE_LOW>; ++ ++ vmmc-supply = <&vcc_card>; ++ vqmmc-supply = <&vddio_card>; ++ ++ status = "okay"; ++}; ++ ++/* eMMC */ ++&sd_emmc_c { ++ pinctrl-0 = <&emmc_pins>; ++ pinctrl-1 = <&emmc_clk_gate_pins>; ++ pinctrl-names = "default", "clk-gate"; ++ ++ bus-width = <8>; ++ cap-mmc-highspeed; ++ mmc-ddr-1_8v; ++ mmc-hs200-1_8v; ++ max-frequency = <200000000>; ++ disable-wp; ++ ++ mmc-pwrseq = <&emmc_pwrseq>; ++ vmmc-supply = <&vddio_ao3v3>; ++ vqmmc-supply = <&vddio_boot>; ++ ++ status = "okay"; ++}; ++ ++&spifc { ++ pinctrl-0 = <&nor_pins>; ++ pinctrl-names = "default"; ++ status = "okay"; ++ ++ gd25lq128: spi-flash@0 { ++ compatible = "jedec,spi-nor"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ reg = <0>; ++ spi-max-frequency = <12000000>; ++ }; ++}; ++ ++&uart_AO { ++ pinctrl-0 = <&uart_ao_a_pins>; ++ pinctrl-names = "default"; ++ status = "okay"; ++}; ++ ++&usb0 { ++ status = "okay"; ++}; ++ ++&usb2_phy0 { ++ pinctrl-0 = <&usb1_rst_pins>; ++ pinctrl-names = "default"; ++ phy-supply = <&vcc5v>; ++}; ++ ++&usb2_phy1 { ++ phy-supply = <&vcc5v>; ++}; +diff --git b/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi a/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi +index 05673221dcc3..a9b778571cf5 100644 +--- b/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi ++++ a/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi +@@ -102,35 +102,6 @@ + }; + }; + }; +- +- sound { +- compatible = "simple-audio-card"; +- simple-audio-card,name = "meson-gx-audio"; +- +- assigned-clocks = <&clkc CLKID_MPLL2>, +- <&clkc CLKID_MPLL0>, +- <&clkc CLKID_MPLL1>; +- assigned-clock-parents = <0>, <0>, <0>; +- assigned-clock-rates = <294912000>, +- <270950400>, +- <393216000>; +- +- simple-audio-card,dai-link@0 { +- /* HDMI Output */ +- format = "i2s"; +- mclk-fs = <256>; +- bitclock-master = <&aiu_i2s>; +- frame-master = <&aiu_i2s>; +- +- cpu { +- sound-dai = <&aiu_i2s>; +- }; +- +- codec { +- sound-dai = <&hdmi_tx>; +- }; +- }; +- }; + }; + + &cec_AO { +@@ -140,14 +111,6 @@ + hdmi-phandle = <&hdmi_tx>; + }; + +-&audio { +- status = "okay"; +-}; +- +-&aiu_i2s { +- status = "okay"; +-}; +- + &cvbs_vdac_port { + cvbs_vdac_out: endpoint { + remote-endpoint = <&cvbs_connector_in>; +diff --git b/arch/arm64/boot/dts/amlogic/meson-gx.dtsi a/arch/arm64/boot/dts/amlogic/meson-gx.dtsi +index cbbd6b40ea9e..ec60b3cc6d14 100644 +--- b/arch/arm64/boot/dts/amlogic/meson-gx.dtsi ++++ a/arch/arm64/boot/dts/amlogic/meson-gx.dtsi +@@ -44,7 +44,7 @@ + linux,cma { + compatible = "shared-dma-pool"; + reusable; +- size = <0x0 0x38000000>; ++ size = <0x0 0x10000000>; + alignment = <0x0 0x400000>; + linux,cma-default; + }; +@@ -161,7 +161,6 @@ + #address-cells = <1>; + #size-cells = <1>; + read-only; +- secure-monitor = <&sm>; + + sn: sn@14 { + reg = <0x14 0x10>; +@@ -226,27 +225,38 @@ + #reset-cells = <1>; + }; + +- audio: audio@5400 { +- compatible = "amlogic,meson-gx-audio-core"; +- reg = <0x0 0x5400 0x0 0x2ac>, +- <0x0 0xa000 0x0 0x304>; +- reg-names = "aiu", "audin"; ++ aiu: bus@5400 { ++ compatible = "amlogic,aiu-bus", "syscon"; ++ reg = <0x0 0x5400 0x0 0x2ac>; + status = "disabled"; + +- aiu_i2s: audio-controller-0 { +- #sound-dai-cells = <0>; +- compatible = "amlogic,meson-aiu-i2s"; ++ i2s_fifo: audio-controller-0 { ++ compatible = "amlogic,aiu-i2s-fifo", ++ "amlogic,aiu-fifo"; ++ sound-name-prefix = "I2S FIFO"; + interrupts = ; +- status = "disabled"; ++ #sound-dai-cells = <0>; + }; + +- aiu_spdif: audio-controller-1 { +- #sound-dai-cells = <0>; +- compatible = "amlogic,meson-aiu-spdif"; ++ spdif_fifo: audio-controller-1 { ++ compatible = "amlogic,aiu-spdif-fifo", ++ "amlogic,aiu-fifo"; ++ sound-name-prefix = "SPDIF FIFO"; + interrupts = ; +- status = "disabled"; ++ #sound-dai-cells = <0>; + }; + ++ i2s_encoder: audio-controller-2 { ++ compatible = "amlogic,aiu-i2s-encode"; ++ sound-name-prefix = "I2S ENCODER"; ++ #sound-dai-cells = <0>; ++ }; ++ ++ spdif_encoder: audio-controller-3 { ++ compatible = "amlogic,aiu-spdif-encode"; ++ sound-name-prefix = "SPDIF ENCODER"; ++ #sound-dai-cells = <0>; ++ }; + }; + + uart_A: serial@84c0 { +@@ -596,6 +606,7 @@ + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; ++ #sound-dai-cells = <0>; + status = "disabled"; + + /* VPU VENC Input */ +diff --git b/arch/arm64/boot/dts/amlogic/meson-gxbb-nanopi-k2.dts a/arch/arm64/boot/dts/amlogic/meson-gxbb-nanopi-k2.dts +index 106a6ffd68ca..233eb1cd7967 100644 +--- b/arch/arm64/boot/dts/amlogic/meson-gxbb-nanopi-k2.dts ++++ a/arch/arm64/boot/dts/amlogic/meson-gxbb-nanopi-k2.dts +@@ -14,7 +14,6 @@ + + aliases { + serial0 = &uart_AO; +- serial2 = &uart_C; + ethernet0 = ðmac; + }; + +@@ -90,35 +89,6 @@ + clock-names = "ext_clock"; + }; + +- sound { +- compatible = "simple-audio-card"; +- simple-audio-card,name = "meson-gx-audio"; +- +- assigned-clocks = <&clkc CLKID_MPLL2>, +- <&clkc CLKID_MPLL0>, +- <&clkc CLKID_MPLL1>; +- assigned-clock-parents = <0>, <0>, <0>; +- assigned-clock-rates = <294912000>, +- <270950400>, +- <393216000>; +- +- simple-audio-card,dai-link@0 { +- /* HDMI Output */ +- format = "i2s"; +- mclk-fs = <256>; +- bitclock-master = <&aiu_i2s>; +- frame-master = <&aiu_i2s>; +- +- cpu { +- sound-dai = <&aiu_i2s>; +- }; +- +- codec { +- sound-dai = <&hdmi_tx>; +- }; +- }; +- }; +- + vcc1v8: regulator-vcc1v8 { + compatible = "regulator-fixed"; + regulator-name = "VCC1.8V"; +@@ -160,40 +130,6 @@ + }; + }; + }; +- +- spi-gpio { +- compatible = "spi-gpio"; +- #address-cells = <0x1>; +- #size-cells = <0x0>; +- ranges; +- status = "ok"; +- sck-gpios = <&gpio GPIOY_9 0>; +- miso-gpios = <&gpio GPIOY_7 0>; +- mosi-gpios = <&gpio GPIOY_5 0>; +- cs-gpios = <&gpio GPIOY_8 0 +- &gpio GPIOY_10 0>; +- num-chipselects = <2>; +- +- /* clients */ +- spidev0@0 { +- compatible = "spidev"; +- reg = <0>; +- spi-max-frequency = <500000>; +- }; +- spidev0@1 { +- compatible = "spidev"; +- reg = <1>; +- spi-max-frequency = <500000>; +- }; +- }; +-}; +- +-&audio { +- status = "okay"; +-}; +- +-&aiu_i2s { +- status = "okay"; + }; + + &cec_AO { +@@ -379,7 +315,7 @@ + + /* eMMC */ + &sd_emmc_c { +- status = "okay"; ++ status = "disabled"; + pinctrl-0 = <&emmc_pins>, <&emmc_ds_pins>; + pinctrl-1 = <&emmc_clk_gate_pins>; + pinctrl-names = "default", "clk-gate"; +@@ -389,6 +325,8 @@ + non-removable; + disable-wp; + cap-mmc-highspeed; ++ mmc-ddr-1_8v; ++ mmc-hs200-1_8v; + + mmc-pwrseq = <&emmc_pwrseq>; + vmmc-supply = <&vcc3v3>; +diff --git b/arch/arm64/boot/dts/amlogic/meson-gxbb-nexbox-a95x.dts a/arch/arm64/boot/dts/amlogic/meson-gxbb-nexbox-a95x.dts +index 17c419c8162b..afcf8a9f667b 100644 +--- b/arch/arm64/boot/dts/amlogic/meson-gxbb-nexbox-a95x.dts ++++ a/arch/arm64/boot/dts/amlogic/meson-gxbb-nexbox-a95x.dts +@@ -119,35 +119,6 @@ + clock-names = "ext_clock"; + }; + +- sound { +- compatible = "simple-audio-card"; +- simple-audio-card,name = "meson-gx-audio"; +- +- assigned-clocks = <&clkc CLKID_MPLL2>, +- <&clkc CLKID_MPLL0>, +- <&clkc CLKID_MPLL1>; +- assigned-clock-parents = <0>, <0>, <0>; +- assigned-clock-rates = <294912000>, +- <270950400>, +- <393216000>; +- +- simple-audio-card,dai-link@0 { +- /* HDMI Output */ +- format = "i2s"; +- mclk-fs = <256>; +- bitclock-master = <&aiu_i2s>; +- frame-master = <&aiu_i2s>; +- +- cpu { +- sound-dai = <&aiu_i2s>; +- }; +- +- codec { +- sound-dai = <&hdmi_tx>; +- }; +- }; +- }; +- + cvbs-connector { + compatible = "composite-video-connector"; + +@@ -183,14 +154,6 @@ + hdmi-phandle = <&hdmi_tx>; + }; + +-&audio { +- status = "okay"; +-}; +- +-&aiu_i2s { +- status = "okay"; +-}; +- + ðmac { + status = "okay"; + pinctrl-0 = <ð_rmii_pins>; +diff --git b/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts a/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts +index a39837dee681..b0b12e389835 100644 +--- b/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts ++++ a/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts +@@ -16,8 +16,6 @@ + + aliases { + serial0 = &uart_AO; +- serial1 = &uart_A; +- serial2 = &uart_C; + ethernet0 = ðmac; + }; + +@@ -112,61 +110,6 @@ + }; + }; + }; +- +- spi-gpio { +- compatible = "spi-gpio"; +- #address-cells = <0x1>; +- #size-cells = <0x0>; +- ranges; +- status = "disabled"; +- sck-gpios = <&gpio GPIOX_2 0>; +- miso-gpios = <&gpio GPIOX_4 0>; +- mosi-gpios = <&gpio GPIOX_7 0>; +- cs-gpios = <&gpio GPIOX_3 0 +- &gpio GPIOX_1 0>; +- num-chipselects = <2>; +- +- /* clients */ +- spidev0@0 { +- compatible = "spidev"; +- reg = <0>; +- spi-max-frequency = <500000>; +- }; +- spidev0@1 { +- compatible = "spidev"; +- reg = <1>; +- spi-max-frequency = <500000>; +- }; +- }; +- +- sound { +- compatible = "simple-audio-card"; +- simple-audio-card,name = "meson-gx-audio"; +- +- assigned-clocks = <&clkc CLKID_MPLL2>, +- <&clkc CLKID_MPLL0>, +- <&clkc CLKID_MPLL1>; +- assigned-clock-parents = <0>, <0>, <0>; +- assigned-clock-rates = <294912000>, +- <270950400>, +- <393216000>; +- +- simple-audio-card,dai-link@0 { +- /* HDMI Output */ +- format = "i2s"; +- mclk-fs = <256>; +- bitclock-master = <&aiu_i2s>; +- frame-master = <&aiu_i2s>; +- +- cpu { +- sound-dai = <&aiu_i2s>; +- }; +- +- codec { +- sound-dai = <&hdmi_tx>; +- }; +- }; +- }; + }; + + &cec_AO { +@@ -176,14 +119,6 @@ + hdmi-phandle = <&hdmi_tx>; + }; + +-&audio { +- status = "okay"; +-}; +- +-&aiu_i2s { +- status = "okay"; +-}; +- + ðmac { + status = "okay"; + pinctrl-0 = <ð_rgmii_pins>; +@@ -309,8 +244,7 @@ + }; + + &scpi_clocks { +- /* Works only with new blobs that have limited DVFS table */ +- status = "okay"; ++ status = "disabled"; + }; + + /* SD */ +@@ -361,18 +295,6 @@ + pinctrl-names = "default"; + }; + +-&uart_A { +- status = "disabled"; +- pinctrl-0 = <&uart_a_pins>; +- pinctrl-names = "default"; +-}; +- +-&uart_C { +- status = "disabled"; +- pinctrl-0 = <&uart_c_pins>; +- pinctrl-names = "default"; +-}; +- + &usb0_phy { + status = "disabled"; + phy-supply = <&usb_otg_pwr>; +diff --git b/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi a/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi +index 95b6e2350369..89f7b41b0e9e 100644 +--- b/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi ++++ a/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi +@@ -113,35 +113,6 @@ + }; + }; + }; +- +- sound { +- compatible = "simple-audio-card"; +- simple-audio-card,name = "meson-gx-audio"; +- +- assigned-clocks = <&clkc CLKID_MPLL2>, +- <&clkc CLKID_MPLL0>, +- <&clkc CLKID_MPLL1>; +- assigned-clock-parents = <0>, <0>, <0>; +- assigned-clock-rates = <294912000>, +- <270950400>, +- <393216000>; +- +- simple-audio-card,dai-link@0 { +- /* HDMI Output */ +- format = "i2s"; +- mclk-fs = <256>; +- bitclock-master = <&aiu_i2s>; +- frame-master = <&aiu_i2s>; +- +- cpu { +- sound-dai = <&aiu_i2s>; +- }; +- +- codec { +- sound-dai = <&hdmi_tx>; +- }; +- }; +- }; + }; + + &cec_AO { +@@ -151,14 +122,6 @@ + hdmi-phandle = <&hdmi_tx>; + }; + +-&audio { +- status = "okay"; +-}; +- +-&aiu_i2s { +- status = "okay"; +-}; +- + &cvbs_vdac_port { + cvbs_vdac_out: endpoint { + remote-endpoint = <&cvbs_connector_in>; +@@ -175,18 +138,6 @@ + hdmi_tx_tmds_out: endpoint { + remote-endpoint = <&hdmi_connector_in>; + }; +- +- amp: analog-amplifier { +- compatible = "dioo,dio2125"; +- enable-gpios = <&gpio GPIOH_3 0>; +- status = "okay"; +- }; +- +- spdif_out: spdif-out { +- #sound-dai-cells = <0>; +- compatible = "linux,spdif-dit"; +- status = "okay"; +- }; + }; + + &ir { +@@ -294,20 +245,3 @@ + &usb1 { + status = "okay"; + }; +- +-&audio { +- status = "okay"; +-}; +- +-&aiu_i2s { +- pinctrl-0 = <&i2s_am_clk_pins>, <&i2s_out_ao_clk_pins>, +- <&i2s_out_lr_clk_pins>, <&i2s_out_ch01_ao_pins>; +- pinctrl-names = "default"; +- status = "okay"; +-}; +- +-&aiu_spdif { +- pinctrl-0 = <&spdif_out_ao_6_pins>; +- pinctrl-names = "default"; +- status = "okay"; +-}; +diff --git b/arch/arm64/boot/dts/amlogic/meson-gxbb-wetek.dtsi a/arch/arm64/boot/dts/amlogic/meson-gxbb-wetek.dtsi +index 6b7ac033ce22..4c539881fbb7 100644 +--- b/arch/arm64/boot/dts/amlogic/meson-gxbb-wetek.dtsi ++++ a/arch/arm64/boot/dts/amlogic/meson-gxbb-wetek.dtsi +@@ -112,43 +112,6 @@ + }; + }; + }; +- +- sound { +- compatible = "simple-audio-card"; +- simple-audio-card,name = "meson-gx-audio"; +- +- assigned-clocks = <&clkc CLKID_MPLL2>, +- <&clkc CLKID_MPLL0>, +- <&clkc CLKID_MPLL1>; +- assigned-clock-parents = <0>, <0>, <0>; +- assigned-clock-rates = <294912000>, +- <270950400>, +- <393216000>; +- +- simple-audio-card,dai-link@0 { +- /* HDMI Output */ +- format = "i2s"; +- mclk-fs = <256>; +- bitclock-master = <&aiu_i2s>; +- frame-master = <&aiu_i2s>; +- +- cpu { +- sound-dai = <&aiu_i2s>; +- }; +- +- codec { +- sound-dai = <&hdmi_tx>; +- }; +- }; +- }; +-}; +- +-&audio { +- status = "okay"; +-}; +- +-&aiu_i2s { +- status = "okay"; + }; + + &cec_AO { +diff --git b/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi a/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi +index ca0707d096b9..0cb40326b0d3 100644 +--- b/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi ++++ a/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi +@@ -330,7 +330,6 @@ + <&clkc CLKID_CLK81>, + <&clkc CLKID_GCLK_VENCI_INT0>; + clock-names = "isfr", "iahb", "venci"; +- #sound-dai-cells = <0>; + }; + + &sysctrl { +@@ -349,8 +348,6 @@ + + &i2c_A { + clocks = <&clkc CLKID_I2C>; +- pinctrl-names = "default"; +- pinctrl-0 = <&i2c_a_pins>; + }; + + &i2c_AO { +@@ -359,8 +356,6 @@ + + &i2c_B { + clocks = <&clkc CLKID_I2C>; +- pinctrl-names = "default"; +- pinctrl-0 = <&i2c_b_pins>; + }; + + &i2c_C { +@@ -729,24 +724,6 @@ + }; + }; + +-&audio { +- clocks = <&clkc CLKID_AIU>, +- <&clkc CLKID_AIU_GLUE>, +- <&clkc CLKID_I2S_SPDIF>; +- clock-names = "aiu_top", "aiu_glue", "audin"; +- resets = <&reset RESET_AIU>, +- <&reset RESET_AUDIN>; +- reset-names = "aiu", "audin"; +-}; +- +-&aiu_i2s { +- clocks = <&clkc CLKID_I2S_OUT>, +- <&clkc CLKID_MIXER_IFACE>, +- <&clkc CLKID_AOCLK_GATE>, +- <&clkc CLKID_CTS_AMCLK>; +- clock-names = "fast", "iface", "bclks", "mclk"; +-}; +- + &pwrc_vpu { + resets = <&reset RESET_VIU>, + <&reset RESET_VENC>, +@@ -835,15 +812,6 @@ + num-cs = <1>; + }; + +-&aiu_spdif { +- clocks = <&clkc CLKID_IEC958>, +- <&clkc CLKID_IEC958_GATE>, +- <&clkc CLKID_CTS_MCLK_I958>, +- <&clkc CLKID_CTS_AMCLK>, +- <&clkc CLKID_CTS_I958>; +- clock-names = "fast", "iface", "mclk_i958", "mclk_i2s", "mclk"; +-}; +- + &spifc { + clocks = <&clkc CLKID_SPI>; + }; +@@ -888,4 +856,3 @@ + resets = <&reset RESET_PARSER>; + reset-names = "esparser"; + }; +- +diff --git b/arch/arm64/boot/dts/amlogic/meson-gxl-s805x-libretech-ac.dts a/arch/arm64/boot/dts/amlogic/meson-gxl-s805x-libretech-ac.dts +index 82b1c4851147..97bd6e2da92c 100644 +--- b/arch/arm64/boot/dts/amlogic/meson-gxl-s805x-libretech-ac.dts ++++ a/arch/arm64/boot/dts/amlogic/meson-gxl-s805x-libretech-ac.dts +@@ -105,6 +105,35 @@ + vin-supply = <&vcc_3v3>; + regulator-always-on; + }; ++ ++ sound { ++ compatible = "amlogic,gx-sound-card"; ++ model = "GXL-LIBRETECH-S805X-AC"; ++ audio-routing = "I2S ENCODER Playback", "I2S FIFO Playback"; ++ ++ assigned-clocks = <&clkc CLKID_MPLL2>, ++ <&clkc CLKID_MPLL0>, ++ <&clkc CLKID_MPLL1>; ++ assigned-clock-parents = <0>, <0>, <0>; ++ assigned-clock-rates = <294912000>, ++ <270950400>, ++ <393216000>; ++ status = "okay"; ++ ++ dai-link-0 { ++ sound-dai = <&i2s_fifo>; ++ }; ++ ++ dai-link-1 { ++ sound-dai = <&i2s_encoder>; ++ dai-format = "i2s"; ++ mclk-fs = <256>; ++ ++ codec-0 { ++ sound-dai = <&hdmi_tx>; ++ }; ++ }; ++ }; + }; + + &cec_AO { +@@ -135,6 +164,14 @@ + pinctrl-names = "default"; + }; + ++&i2s_fifo { ++ status = "okay"; ++}; ++ ++&i2s_encoder { ++ status = "okay"; ++}; ++ + &hdmi_tx { + status = "okay"; + pinctrl-0 = <&hdmi_hpd_pins>, <&hdmi_i2c_pins>; +diff --git b/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-libretech-pc.dts a/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-libretech-pc.dts +new file mode 100644 +index 000000000000..100a1cfeea15 +--- /dev/null ++++ a/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-libretech-pc.dts +@@ -0,0 +1,16 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2019 BayLibre SAS. All rights reserved. ++ * Author: Jerome Brunet ++ */ ++ ++/dts-v1/; ++ ++#include "meson-gxl-s905d.dtsi" ++#include "meson-gx-libretech-pc.dtsi" ++ ++/ { ++ compatible = "libretech,aml-s905d-pc", "amlogic,s905d", ++ "amlogic,meson-gxl"; ++ model = "Libre Computer AML-S905D-PC"; ++}; +diff --git b/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p230.dts a/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p230.dts +index 5d77ca683d68..b08c4537f260 100644 +--- b/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p230.dts ++++ a/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p230.dts +@@ -51,12 +51,6 @@ + }; + }; + }; +- +- spdif_out: spdif-out { +- #sound-dai-cells = <0>; +- compatible = "linux,spdif-dit"; +- status = "okay"; +- }; + }; + + &cec_AO { +@@ -115,13 +109,3 @@ + compatible = "brcm,bcm4329-fmac"; + }; + }; +- +-&audio { +- status = "okay"; +-}; +- +-&aiu_spdif { +- pinctrl-0 = <&spdif_out_h_pins>; +- pinctrl-names = "default"; +- status = "okay"; +-}; +diff --git b/arch/arm64/boot/dts/amlogic/meson-gxl-s905w-tx3-mini.dts a/arch/arm64/boot/dts/amlogic/meson-gxl-s905w-tx3-mini.dts +index e554b7af32d8..dd729ac2300d 100644 +--- b/arch/arm64/boot/dts/amlogic/meson-gxl-s905w-tx3-mini.dts ++++ a/arch/arm64/boot/dts/amlogic/meson-gxl-s905w-tx3-mini.dts +@@ -10,7 +10,6 @@ + + #include "meson-gxl-s905x.dtsi" + #include "meson-gx-p23x-q20x.dtsi" +-#include + + / { + compatible = "oranth,tx3-mini", "amlogic,s905w", "amlogic,meson-gxl"; +@@ -20,49 +19,8 @@ + device_type = "memory"; + reg = <0x0 0x0 0x0 0x40000000>; /* 1 GiB or 2 GiB */ + }; ++}; + +- thermal-zones { +- cpu-thermal { +- polling-delay-passive = <250>; /* milliseconds */ +- polling-delay = <1000>; /* milliseconds */ +- +- thermal-sensors = <&scpi_sensors 0>; +- +- trips { +- cpu_alert0: cpu-alert0 { +- temperature = <70000>; /* millicelsius */ +- hysteresis = <2000>; /* millicelsius */ +- type = "active"; +- }; +- +- cpu_alert1: cpu-alert1 { +- temperature = <80000>; /* millicelsius */ +- hysteresis = <2000>; /* millicelsius */ +- type = "passive"; +- }; +- }; +- +- cooling-maps { +- map0 { +- trip = <&cpu_alert0>; +- cooling-device = <&gpio_fan THERMAL_NO_LIMIT 1>; +- }; +- +- map1 { +- trip = <&cpu_alert1>; +- cooling-device = <&gpio_fan THERMAL_NO_LIMIT 1>; +- }; +- }; +- }; +- }; +- +- gpio_fan: gpio-fan { +- compatible = "gpio-fan"; +- /* Dummy RPM values since fan is optional */ +- gpio-fan,speed-map = <0 0 +- 1 1 +- 2 2 +- 3 3>; +- #cooling-cells = <2>; +- }; ++&ir { ++ linux,rc-map-name = "rc-tanix-tx3mini"; + }; +diff --git b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-khadas-vim.dts a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-khadas-vim.dts +index a3c67d7a70e4..440bc23c7342 100644 +--- b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-khadas-vim.dts ++++ a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-khadas-vim.dts +@@ -63,35 +63,6 @@ + }; + }; + }; +- +- sound { +- compatible = "simple-audio-card"; +- simple-audio-card,name = "meson-gx-audio"; +- +- assigned-clocks = <&clkc CLKID_MPLL2>, +- <&clkc CLKID_MPLL0>, +- <&clkc CLKID_MPLL1>; +- assigned-clock-parents = <0>, <0>, <0>; +- assigned-clock-rates = <294912000>, +- <270950400>, +- <393216000>; +- +- simple-audio-card,dai-link@0 { +- /* HDMI Output */ +- format = "i2s"; +- mclk-fs = <256>; +- bitclock-master = <&aiu_i2s>; +- frame-master = <&aiu_i2s>; +- +- cpu { +- sound-dai = <&aiu_i2s>; +- }; +- +- codec { +- sound-dai = <&hdmi_tx>; +- }; +- }; +- }; + }; + + &cec_AO { +@@ -101,14 +72,6 @@ + hdmi-phandle = <&hdmi_tx>; + }; + +-&audio { +- status = "okay"; +-}; +- +-&aiu_i2s { +- status = "okay"; +-}; +- + &hdmi_tx { + status = "okay"; + pinctrl-0 = <&hdmi_hpd_pins>, <&hdmi_i2c_pins>; +diff --git b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-libretech-cc.dts a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-libretech-cc.dts +index 969ee02e7429..af1cf92c1d0a 100644 +--- b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-libretech-cc.dts ++++ a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-libretech-cc.dts +@@ -14,6 +14,8 @@ + / { + compatible = "libretech,cc", "amlogic,s905x", "amlogic,meson-gxl"; + model = "Libre Computer Board AML-S905X-CC"; ++ vendor = "libre-computer"; ++ boardname = "aml-s905x-cc"; + + aliases { + serial0 = &uart_AO; +@@ -54,16 +56,16 @@ + compatible = "gpio-leds"; + + system { +- label = "librecomputer:system-status"; ++ label = "librecomputer:green:disk"; + gpios = <&gpio GPIODV_24 GPIO_ACTIVE_HIGH>; +- default-state = "on"; ++ linux,default-trigger = "heartbeat"; + panic-indicator; + }; + + blue { +- label = "librecomputer:blue"; ++ label = "librecomputer:blue:cpu"; + gpios = <&gpio_ao GPIOAO_2 GPIO_ACTIVE_HIGH>; +- linux,default-trigger = "heartbeat"; ++ linux,default-trigger = "activity"; + }; + }; + +@@ -84,35 +86,6 @@ + regulator-always-on; + }; + +- sound { +- compatible = "simple-audio-card"; +- simple-audio-card,name = "meson-gx-audio"; +- +- assigned-clocks = <&clkc CLKID_MPLL2>, +- <&clkc CLKID_MPLL0>, +- <&clkc CLKID_MPLL1>; +- assigned-clock-parents = <0>, <0>, <0>; +- assigned-clock-rates = <294912000>, +- <270950400>, +- <393216000>; +- +- simple-audio-card,dai-link@0 { +- /* HDMI Output */ +- format = "i2s"; +- mclk-fs = <256>; +- bitclock-master = <&aiu_i2s>; +- frame-master = <&aiu_i2s>; +- +- cpu { +- sound-dai = <&aiu_i2s>; +- }; +- +- codec { +- sound-dai = <&hdmi_tx>; +- }; +- }; +- }; +- + vcc_3v3: regulator-vcc_3v3 { + compatible = "regulator-fixed"; + regulator-name = "VCC_3V3"; +@@ -152,6 +125,39 @@ + regulator-max-microvolt = <1800000>; + vin-supply = <&vcc_3v3>; + }; ++ ++ sound { ++ compatible = "amlogic,gx-sound-card"; ++ model = "GXL-LIBRETECH-S905X-CC"; ++ audio-routing = "I2S ENCODER Playback", "I2S FIFO Playback"; ++ ++ assigned-clocks = <&clkc CLKID_MPLL2>, ++ <&clkc CLKID_MPLL0>, ++ <&clkc CLKID_MPLL1>; ++ assigned-clock-parents = <0>, <0>, <0>; ++ assigned-clock-rates = <294912000>, ++ <270950400>, ++ <393216000>; ++ status = "okay"; ++ ++ dai-link-0 { ++ sound-dai = <&i2s_fifo>; ++ }; ++ ++ dai-link-1 { ++ sound-dai = <&i2s_encoder>; ++ dai-format = "i2s"; ++ mclk-fs = <256>; ++ ++ codec-0 { ++ sound-dai = <&hdmi_tx>; ++ }; ++ }; ++ }; ++}; ++ ++&aiu { ++ status = "okay"; + }; + + &cec_AO { +@@ -161,14 +167,6 @@ + hdmi-phandle = <&hdmi_tx>; + }; + +-&audio { +- status = "okay"; +-}; +- +-&aiu_i2s { +- status = "okay"; +-}; +- + &cvbs_vdac_port { + cvbs_vdac_out: endpoint { + remote-endpoint = <&cvbs_connector_in>; +@@ -190,6 +188,14 @@ + pinctrl-names = "default"; + }; + ++&i2s_fifo { ++ status = "okay"; ++}; ++ ++&i2s_encoder { ++ status = "okay"; ++}; ++ + &hdmi_tx { + status = "okay"; + pinctrl-0 = <&hdmi_hpd_pins>, <&hdmi_i2c_pins>; +@@ -274,7 +280,7 @@ + + bus-width = <4>; + cap-sd-highspeed; +- max-frequency = <50000000>; ++ max-frequency = <100000000>; + disable-wp; + + cd-gpios = <&gpio CARD_6 GPIO_ACTIVE_LOW>; +diff --git b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-nexbox-a95x.dts a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-nexbox-a95x.dts +index 25db0354ef1b..c433a031841f 100644 +--- b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-nexbox-a95x.dts ++++ a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-nexbox-a95x.dts +@@ -102,35 +102,6 @@ + }; + }; + }; +- +- sound { +- compatible = "simple-audio-card"; +- simple-audio-card,name = "meson-gx-audio"; +- +- assigned-clocks = <&clkc CLKID_MPLL2>, +- <&clkc CLKID_MPLL0>, +- <&clkc CLKID_MPLL1>; +- assigned-clock-parents = <0>, <0>, <0>; +- assigned-clock-rates = <294912000>, +- <270950400>, +- <393216000>; +- +- simple-audio-card,dai-link@0 { +- /* HDMI Output */ +- format = "i2s"; +- mclk-fs = <256>; +- bitclock-master = <&aiu_i2s>; +- frame-master = <&aiu_i2s>; +- +- cpu { +- sound-dai = <&aiu_i2s>; +- }; +- +- codec { +- sound-dai = <&hdmi_tx>; +- }; +- }; +- }; + }; + + &cec_AO { +@@ -140,14 +111,6 @@ + hdmi-phandle = <&hdmi_tx>; + }; + +-&audio { +- status = "okay"; +-}; +- +-&aiu_i2s { +- status = "okay"; +-}; +- + &cvbs_vdac_port { + cvbs_vdac_out: endpoint { + remote-endpoint = <&cvbs_connector_in>; +diff --git b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dts a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dts +index 78c3060c5d56..2602940c2077 100644 +--- b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dts ++++ a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dts +@@ -32,35 +32,6 @@ + }; + }; + }; +- +- sound { +- compatible = "simple-audio-card"; +- simple-audio-card,name = "meson-gx-audio"; +- +- assigned-clocks = <&clkc CLKID_MPLL2>, +- <&clkc CLKID_MPLL0>, +- <&clkc CLKID_MPLL1>; +- assigned-clock-parents = <0>, <0>, <0>; +- assigned-clock-rates = <294912000>, +- <270950400>, +- <393216000>; +- +- simple-audio-card,dai-link@0 { +- /* HDMI Output */ +- format = "i2s"; +- mclk-fs = <256>; +- bitclock-master = <&aiu_i2s>; +- frame-master = <&aiu_i2s>; +- +- cpu { +- sound-dai = <&aiu_i2s>; +- }; +- +- codec { +- sound-dai = <&hdmi_tx>; +- }; +- }; +- }; + }; + + &cec_AO { +@@ -70,14 +41,6 @@ + hdmi-phandle = <&hdmi_tx>; + }; + +-&audio { +- status = "okay"; +-}; +- +-&aiu_i2s { +- status = "okay"; +-}; +- + &cvbs_vdac_port { + cvbs_vdac_out: endpoint { + remote-endpoint = <&cvbs_connector_in>; +diff --git b/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi a/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi +index da899d07f2d3..321ed8b3bf53 100644 +--- b/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi ++++ a/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi +@@ -36,19 +36,16 @@ + phys = <&usb3_phy>, <&usb2_phy0>, <&usb2_phy1>; + }; + }; +- +- crypto: crypto@c883e000 { +- compatible = "amlogic,gxl-crypto"; +- reg = <0x0 0xc883e000 0x0 0x36>; +- interrupts = , +- ; +- clocks = <&clkc CLKID_BLKMV>; +- clock-names = "blkmv"; +- status = "okay"; +- }; + }; + }; + ++&aiu { ++ clocks = <&clkc CLKID_AIU>, ++ <&clkc CLKID_AIU_GLUE>; ++ clock-names = "top", "glue"; ++ resets = <&reset RESET_AIU>; ++}; ++ + &apb { + usb2_phy0: phy@78000 { + compatible = "amlogic,meson-gxl-usb2-phy"; +@@ -287,7 +284,6 @@ + <&clkc CLKID_CLK81>, + <&clkc CLKID_GCLK_VENCI_INT0>; + clock-names = "isfr", "iahb", "venci"; +- #sound-dai-cells = <0>; + }; + + &sysctrl { +@@ -315,6 +311,17 @@ + clocks = <&clkc CLKID_I2C>; + }; + ++&i2s_fifo { ++ clocks = <&clkc CLKID_I2S_OUT>; ++}; ++ ++&i2s_encoder { ++ clocks = <&clkc CLKID_MIXER_IFACE>, ++ <&clkc CLKID_AOCLK_GATE>, ++ <&clkc CLKID_CTS_AMCLK>; ++ clock-names = "pclk", "aoclk", "mclk"; ++}; ++ + &periphs { + pinctrl_periphs: pinctrl@4b0 { + compatible = "amlogic,meson-gxl-periphs-pinctrl"; +@@ -534,6 +541,15 @@ + }; + }; + ++ i2c_c_dv18_pins: i2c_c_dv18 { ++ mux { ++ groups = "i2c_sck_c_dv19", ++ "i2c_sda_c_dv18"; ++ function = "i2c_c"; ++ bias-disable; ++ }; ++ }; ++ + eth_pins: eth_c { + mux { + groups = "eth_mdio", +@@ -735,24 +751,6 @@ + }; + }; + +-&audio { +- clocks = <&clkc CLKID_AIU>, +- <&clkc CLKID_AIU_GLUE>, +- <&clkc CLKID_I2S_SPDIF>; +- clock-names = "aiu_top", "aiu_glue", "audin"; +- resets = <&reset RESET_AIU>, +- <&reset RESET_AUDIN>; +- reset-names = "aiu", "audin"; +-}; +- +-&aiu_i2s { +- clocks = <&clkc CLKID_I2S_OUT>, +- <&clkc CLKID_MIXER_IFACE>, +- <&clkc CLKID_AOCLK_GATE>, +- <&clkc CLKID_CTS_AMCLK>; +- clock-names = "fast", "iface", "bclks", "mclk"; +-}; +- + &pwrc_vpu { + resets = <&reset RESET_VIU>, + <&reset RESET_VENC>, +@@ -834,6 +832,18 @@ + <&clkc CLKID_GCLK_VENCI_INT0>; + }; + ++&spdif_fifo { ++ clocks = <&clkc CLKID_IEC958>; ++}; ++ ++&spdif_encoder { ++ clocks = <&clkc CLKID_IEC958_GATE>, ++ <&clkc CLKID_CTS_MCLK_I958>, ++ <&clkc CLKID_CTS_AMCLK>, ++ <&clkc CLKID_CTS_I958>; ++ clock-names = "pclk", "mclk_i958", "mclk_i2s", "mclk"; ++}; ++ + &spicc { + clocks = <&clkc CLKID_SPICC>; + clock-names = "core"; +@@ -841,15 +851,6 @@ + num-cs = <1>; + }; + +-&aiu_spdif { +- clocks = <&clkc CLKID_IEC958>, +- <&clkc CLKID_IEC958_GATE>, +- <&clkc CLKID_CTS_MCLK_I958>, +- <&clkc CLKID_CTS_AMCLK>, +- <&clkc CLKID_CTS_I958>; +- clock-names = "fast", "iface", "mclk_i958", "mclk_i2s", "mclk"; +-}; +- + &spifc { + clocks = <&clkc CLKID_SPI>; + }; +@@ -894,4 +895,3 @@ + resets = <&reset RESET_PARSER>; + reset-names = "esparser"; + }; +- +diff --git b/arch/arm64/boot/dts/amlogic/meson-gxm-khadas-vim2.dts a/arch/arm64/boot/dts/amlogic/meson-gxm-khadas-vim2.dts +index b1fe2ce39034..4d67eb715b91 100644 +--- b/arch/arm64/boot/dts/amlogic/meson-gxm-khadas-vim2.dts ++++ a/arch/arm64/boot/dts/amlogic/meson-gxm-khadas-vim2.dts +@@ -82,35 +82,6 @@ + }; + }; + +- sound { +- compatible = "simple-audio-card"; +- simple-audio-card,name = "meson-gx-audio"; +- +- assigned-clocks = <&clkc CLKID_MPLL2>, +- <&clkc CLKID_MPLL0>, +- <&clkc CLKID_MPLL1>; +- assigned-clock-parents = <0>, <0>, <0>; +- assigned-clock-rates = <294912000>, +- <270950400>, +- <393216000>; +- +- simple-audio-card,dai-link@0 { +- /* HDMI Output */ +- format = "i2s"; +- mclk-fs = <256>; +- bitclock-master = <&aiu_i2s>; +- frame-master = <&aiu_i2s>; +- +- cpu { +- sound-dai = <&aiu_i2s>; +- }; +- +- codec { +- sound-dai = <&hdmi_tx>; +- }; +- }; +- }; +- + pwmleds { + compatible = "pwm-leds"; + +@@ -227,14 +198,6 @@ + hdmi-phandle = <&hdmi_tx>; + }; + +-&audio { +- status = "okay"; +-}; +- +-&aiu_i2s { +- status = "okay"; +-}; +- + &cpu0 { + #cooling-cells = <2>; + }; +diff --git b/arch/arm64/boot/dts/amlogic/meson-gxm-nexbox-a1.dts a/arch/arm64/boot/dts/amlogic/meson-gxm-nexbox-a1.dts +index b6519bbdaadb..c2bd4dbbf38c 100644 +--- b/arch/arm64/boot/dts/amlogic/meson-gxm-nexbox-a1.dts ++++ a/arch/arm64/boot/dts/amlogic/meson-gxm-nexbox-a1.dts +@@ -75,35 +75,6 @@ + }; + }; + }; +- +- sound { +- compatible = "simple-audio-card"; +- simple-audio-card,name = "meson-gx-audio"; +- +- assigned-clocks = <&clkc CLKID_MPLL2>, +- <&clkc CLKID_MPLL0>, +- <&clkc CLKID_MPLL1>; +- assigned-clock-parents = <0>, <0>, <0>; +- assigned-clock-rates = <294912000>, +- <270950400>, +- <393216000>; +- +- simple-audio-card,dai-link@0 { +- /* HDMI Output */ +- format = "i2s"; +- mclk-fs = <256>; +- bitclock-master = <&aiu_i2s>; +- frame-master = <&aiu_i2s>; +- +- cpu { +- sound-dai = <&aiu_i2s>; +- }; +- +- codec { +- sound-dai = <&hdmi_tx>; +- }; +- }; +- }; + }; + + &cec_AO { +@@ -113,14 +84,6 @@ + hdmi-phandle = <&hdmi_tx>; + }; + +-&audio { +- status = "okay"; +-}; +- +-&aiu_i2s { +- status = "okay"; +-}; +- + &cvbs_vdac_port { + cvbs_vdac_out: endpoint { + remote-endpoint = <&cvbs_connector_in>; +diff --git b/arch/arm64/boot/dts/amlogic/meson-gxm-s912-libretech-pc.dts a/arch/arm64/boot/dts/amlogic/meson-gxm-s912-libretech-pc.dts +new file mode 100644 +index 000000000000..444c249863cb +--- /dev/null ++++ a/arch/arm64/boot/dts/amlogic/meson-gxm-s912-libretech-pc.dts +@@ -0,0 +1,62 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2019 BayLibre SAS. All rights reserved. ++ * Author: Jerome Brunet ++ */ ++ ++/dts-v1/; ++ ++#include "meson-gxm.dtsi" ++#include "meson-gx-libretech-pc.dtsi" ++ ++/ { ++ compatible = "libretech,aml-s912-pc", "amlogic,s912", ++ "amlogic,meson-gxm"; ++ model = "Libre Computer AML-S912-PC"; ++ ++ typec2_vbus: regulator-typec2_vbus { ++ compatible = "regulator-fixed"; ++ regulator-name = "TYPEC2_VBUS"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ vin-supply = <&vcc5v>; ++ ++ gpio = <&gpio GPIODV_1 GPIO_ACTIVE_HIGH>; ++ enable-active-high; ++ }; ++}; ++ ++&pinctrl_periphs { ++ /* ++ * Make sure the irq pin of the TYPE C controller is not driven ++ * by the SoC. ++ */ ++ fusb302_irq_pins: fusb302_irq { ++ mux { ++ groups = "GPIODV_0"; ++ function = "gpio_periphs"; ++ bias-pull-up; ++ output-disable; ++ }; ++ }; ++}; ++ ++&i2c_C { ++ fusb302@22 { ++ compatible = "fcs,fusb302"; ++ reg = <0x22>; ++ ++ pinctrl-0 = <&fusb302_irq_pins>; ++ pinctrl-names = "default"; ++ interrupt-parent = <&gpio_intc>; ++ interrupts = <59 IRQ_TYPE_LEVEL_LOW>; ++ ++ vbus-supply = <&typec2_vbus>; ++ ++ status = "okay"; ++ }; ++}; ++ ++&usb2_phy2 { ++ phy-supply = <&typec2_vbus>; ++}; +diff --git b/arch/arm64/configs/defconfig a/arch/arm64/configs/defconfig +index c9a867ac32d4..1923266edddc 100644 +--- b/arch/arm64/configs/defconfig ++++ a/arch/arm64/configs/defconfig +@@ -211,6 +211,8 @@ CONFIG_MTD_NAND_DENALI_DT=y + CONFIG_MTD_NAND_MARVELL=y + CONFIG_MTD_NAND_QCOM=y + CONFIG_MTD_SPI_NOR=y ++CONFIG_OF_OVERLAY=y ++CONFIG_OF_CONFIGFS=y + CONFIG_BLK_DEV_LOOP=y + CONFIG_BLK_DEV_NBD=m + CONFIG_VIRTIO_BLK=y +@@ -564,7 +566,10 @@ CONFIG_SND_HDA_TEGRA=m + CONFIG_SND_HDA_CODEC_HDMI=m + CONFIG_SND_SOC=y + CONFIG_SND_BCM2835_SOC_I2S=m ++CONFIG_SND_MESON_AIU_I2S_ENCODER=m ++CONFIG_SND_MESON_AIU_SPDIF_ENCODER=m + CONFIG_SND_MESON_AXG_SOUND_CARD=m ++CONFIG_SND_MESON_GX_SOUND_CARD=m + CONFIG_SND_SOC_ROCKCHIP=m + CONFIG_SND_SOC_ROCKCHIP_SPDIF=m + CONFIG_SND_SOC_ROCKCHIP_RT5645=m +@@ -607,6 +612,8 @@ CONFIG_USB_GADGET=y + CONFIG_USB_RENESAS_USBHS_UDC=m + CONFIG_USB_RENESAS_USB3=m + CONFIG_TYPEC=m ++CONFIG_TYPEC_TCPM=m ++CONFIG_TYPEC_FUSB302=m + CONFIG_TYPEC_HD3SS3220=m + CONFIG_MMC=y + CONFIG_MMC_BLOCK_MINORS=32 +@@ -683,6 +690,9 @@ CONFIG_VIRTIO_BALLOON=y + CONFIG_VIRTIO_MMIO=y + CONFIG_XEN_GNTDEV=y + CONFIG_XEN_GRANT_DEV_ALLOC=y ++CONFIG_STAGING=y ++CONFIG_STAGING_MEDIA=y ++CONFIG_VIDEO_MESON_VDEC=m + CONFIG_CROS_EC_I2C=y + CONFIG_CROS_EC_SPI=y + CONFIG_COMMON_CLK_RK808=y +diff --git b/drivers/clk/meson/clk-pll.c a/drivers/clk/meson/clk-pll.c +index 4d3a8003ca20..ddb1e5634739 100644 +--- b/drivers/clk/meson/clk-pll.c ++++ a/drivers/clk/meson/clk-pll.c +@@ -77,10 +77,6 @@ static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw, + unsigned int m, n, frac; + + n = meson_parm_read(clk->map, &pll->n); +- /* Some hw may have n set to 0 at init, avoid div by 0 in that case */ +- if (n == 0) +- return 0; +- + m = meson_parm_read(clk->map, &pll->m); + + frac = MESON_PARM_APPLICABLE(&pll->frac) ? +diff --git b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c +index 995d1960a88a..1d15cf9b6821 100644 +--- b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c ++++ a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c +@@ -57,8 +57,6 @@ static int dw_hdmi_i2s_hw_params(struct device *dev, void *data, + inputclkfs = HDMI_AUD_INPUTCLKFS_64FS; + conf0 = (HDMI_AUD_CONF0_I2S_SELECT | HDMI_AUD_CONF0_I2S_EN0); + +- dev_info(dev, "channels=%d sample_width=%d sample_rate=%d\n", hparms->channels, hparms->sample_width, hparms->sample_rate); +- + /* Enable the required i2s lanes */ + switch (hparms->channels) { + case 7 ... 8: +@@ -104,7 +102,6 @@ static int dw_hdmi_i2s_hw_params(struct device *dev, void *data, + } + + dw_hdmi_set_sample_rate(hdmi, hparms->sample_rate); +- dw_hdmi_set_channel_status(hdmi, hparms->iec.status); + dw_hdmi_set_channel_count(hdmi, hparms->channels); + dw_hdmi_set_channel_allocation(hdmi, hparms->cea.channel_allocation); + +diff --git b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +index afc22e6f8c9c..1326f2c734bf 100644 +--- b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c ++++ a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +@@ -590,26 +590,6 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk) + return n; + } + +-/* +- * When transmitting IEC60958 linear PCM audio, these registers allow to +- * configure the channel status information of all the channel status +- * bits in the IEC60958 frame. For the moment this configuration is only +- * used when the I2S audio interface, General Purpose Audio (GPA), +- * or AHB audio DMA (AHBAUDDMA) interface is active +- * (for S/PDIF interface this information comes from the stream). +- */ +-void dw_hdmi_set_channel_status(struct dw_hdmi *hdmi, +- u8 *channel_status) +-{ +- /* +- * Set channel status register for frequency and word length. +- * Use default values for other registers. +- */ +- hdmi_writeb(hdmi, channel_status[3], HDMI_FC_AUDSCHNLS7); +- hdmi_writeb(hdmi, channel_status[4], HDMI_FC_AUDSCHNLS8); +-} +-EXPORT_SYMBOL_GPL(dw_hdmi_set_channel_status); +- + static void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi, + unsigned long pixel_clk, unsigned int sample_rate) + { +@@ -718,11 +698,6 @@ static void hdmi_enable_audio_clk(struct dw_hdmi *hdmi, bool enable) + else + hdmi->mc_clkdis |= HDMI_MC_CLKDIS_AUDCLK_DISABLE; + hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS); +- +- if (enable) { +- hdmi_set_cts_n(hdmi, hdmi->audio_cts, 0); +- hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n); +- } + } + + static void dw_hdmi_ahb_audio_enable(struct dw_hdmi *hdmi) +diff --git b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h +index fcff5059db24..6988f12d89d9 100644 +--- b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h ++++ a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h +@@ -158,8 +158,6 @@ + #define HDMI_FC_SPDDEVICEINF 0x1062 + #define HDMI_FC_AUDSCONF 0x1063 + #define HDMI_FC_AUDSSTAT 0x1064 +-#define HDMI_FC_AUDSCHNLS7 0x106e +-#define HDMI_FC_AUDSCHNLS8 0x106f + #define HDMI_FC_DATACH0FILL 0x1070 + #define HDMI_FC_DATACH1FILL 0x1071 + #define HDMI_FC_DATACH2FILL 0x1072 +diff --git b/drivers/gpu/drm/drm_gem.c a/drivers/gpu/drm/drm_gem.c +index a5e88c3e6d25..6854f5867d51 100644 +--- b/drivers/gpu/drm/drm_gem.c ++++ a/drivers/gpu/drm/drm_gem.c +@@ -679,11 +679,11 @@ static int objects_lookup(struct drm_file *filp, u32 *handle, int count, + /** + * drm_gem_objects_lookup - look up GEM objects from an array of handles + * @filp: DRM file private date +- * @bo_handles: array of GEM object handles ++ * @bo_handles: user pointer to array of userspace handle + * @count: size of handle array + * @objs_out: returned pointer to array of drm_gem_object pointers + * +- * Takes an array of GEM object handles and returns a newly allocated array of ++ * Takes an array of userspace handles and returns a newly allocated array of + * GEM objects. + * + * For a single handle lookup, use drm_gem_object_lookup(). +@@ -695,56 +695,26 @@ static int objects_lookup(struct drm_file *filp, u32 *handle, int count, + * failure. 0 is returned on success. + * + */ +-int drm_gem_objects_lookup(struct drm_file *filp, u32 *bo_handles, ++int drm_gem_objects_lookup(struct drm_file *filp, void __user *bo_handles, + int count, struct drm_gem_object ***objs_out) + { + int ret; ++ u32 *handles; + struct drm_gem_object **objs; + ++ if (!count) ++ return 0; ++ + objs = kvmalloc_array(count, sizeof(struct drm_gem_object *), + GFP_KERNEL | __GFP_ZERO); + if (!objs) + return -ENOMEM; + +- ret = objects_lookup(filp, bo_handles, count, objs); +- if (ret) +- kvfree(objs); +- else +- *objs_out = objs; +- +- return ret; +- +-} +-EXPORT_SYMBOL(drm_gem_objects_lookup); +- +-/** +- * drm_gem_objects_lookup_user - look up GEM objects from an array of handles +- * @filp: DRM file private date +- * @bo_handles: user pointer to array of userspace handle +- * @count: size of handle array +- * @objs_out: returned pointer to array of drm_gem_object pointers +- * +- * Takes an array of userspace handles and returns a newly allocated array of +- * GEM objects. +- * +- * For a single handle lookup, use drm_gem_object_lookup(). +- * +- * Returns: +- * +- * @objs filled in with GEM object pointers. Returned GEM objects need to be +- * released with drm_gem_object_put(). -ENOENT is returned on a lookup +- * failure. 0 is returned on success. +- * +- */ +-int drm_gem_objects_lookup_user(struct drm_file *filp, void __user *bo_handles, +- int count, struct drm_gem_object ***objs_out) +-{ +- int ret; +- u32 *handles; +- + handles = kvmalloc_array(count, sizeof(u32), GFP_KERNEL); +- if (!handles) +- return -ENOMEM; ++ if (!handles) { ++ ret = -ENOMEM; ++ goto out; ++ } + + if (copy_from_user(handles, bo_handles, count * sizeof(u32))) { + ret = -EFAULT; +@@ -752,14 +722,15 @@ int drm_gem_objects_lookup_user(struct drm_file *filp, void __user *bo_handles, + goto out; + } + +- ret = drm_gem_objects_lookup(filp, handles, count, objs_out); ++ ret = objects_lookup(filp, handles, count, objs); ++ *objs_out = objs; + + out: + kvfree(handles); + return ret; + + } +-EXPORT_SYMBOL(drm_gem_objects_lookup_user); ++EXPORT_SYMBOL(drm_gem_objects_lookup); + + /** + * drm_gem_object_lookup - look up a GEM object from its handle +diff --git b/drivers/gpu/drm/lima/Kconfig a/drivers/gpu/drm/lima/Kconfig +index cdd24b68b5d4..bb4ddc6bb0a6 100644 +--- b/drivers/gpu/drm/lima/Kconfig ++++ a/drivers/gpu/drm/lima/Kconfig +@@ -9,7 +9,5 @@ config DRM_LIMA + depends on COMMON_CLK + depends on OF + select DRM_SCHED +- select DRM_GEM_SHMEM_HELPER +- select PM_DEVFREQ + help + DRM driver for ARM Mali 400/450 GPUs. +diff --git b/drivers/gpu/drm/lima/Makefile a/drivers/gpu/drm/lima/Makefile +index 5e5c29875e9c..38cc70281ba5 100644 +--- b/drivers/gpu/drm/lima/Makefile ++++ a/drivers/gpu/drm/lima/Makefile +@@ -13,8 +13,9 @@ lima-y := \ + lima_vm.o \ + lima_sched.o \ + lima_ctx.o \ ++ lima_gem_prime.o \ + lima_dlbu.o \ + lima_bcast.o \ +- lima_devfreq.o ++ lima_object.o + + obj-$(CONFIG_DRM_LIMA) += lima.o +diff --git b/drivers/gpu/drm/lima/lima_device.c a/drivers/gpu/drm/lima/lima_device.c +index 2a1a683585ad..d86b8d81a483 100644 +--- b/drivers/gpu/drm/lima/lima_device.c ++++ a/drivers/gpu/drm/lima/lima_device.c +@@ -213,8 +213,6 @@ static int lima_init_gp_pipe(struct lima_device *dev) + struct lima_sched_pipe *pipe = dev->pipe + lima_pipe_gp; + int err; + +- pipe->ldev = dev; +- + err = lima_sched_pipe_init(pipe, "gp"); + if (err) + return err; +@@ -245,8 +243,6 @@ static int lima_init_pp_pipe(struct lima_device *dev) + struct lima_sched_pipe *pipe = dev->pipe + lima_pipe_pp; + int err, i; + +- pipe->ldev = dev; +- + err = lima_sched_pipe_init(pipe, "pp"); + if (err) + return err; +@@ -317,7 +313,7 @@ int lima_device_init(struct lima_device *ldev) + ldev->va_end = LIMA_VA_RESERVE_START; + ldev->dlbu_cpu = dma_alloc_wc( + ldev->dev, LIMA_PAGE_SIZE, +- &ldev->dlbu_dma, GFP_KERNEL | __GFP_NOWARN); ++ &ldev->dlbu_dma, GFP_KERNEL); + if (!ldev->dlbu_cpu) { + err = -ENOMEM; + goto err_out2; +diff --git b/drivers/gpu/drm/lima/lima_device.h a/drivers/gpu/drm/lima/lima_device.h +index 26f0efdd17f1..31158d86271c 100644 +--- b/drivers/gpu/drm/lima/lima_device.h ++++ a/drivers/gpu/drm/lima/lima_device.h +@@ -5,7 +5,6 @@ + #define __LIMA_DEVICE_H__ + + #include +-#include + #include + + #include "lima_sched.h" +@@ -95,22 +94,6 @@ struct lima_device { + + u32 *dlbu_cpu; + dma_addr_t dlbu_dma; +- +- struct { +- struct devfreq *devfreq; +- struct opp_table *opp_table; +- struct thermal_cooling_device *cooling; +- ktime_t busy_time; +- ktime_t idle_time; +- ktime_t time_last_update; +- atomic_t busy_count; +- /* +- * Protect busy_time, idle_time and time_last_update because +- * these can be updated concurrently - for example by the GP +- * and PP interrupts. +- */ +- spinlock_t lock; +- } devfreq; + }; + + static inline struct lima_device * +diff --git b/drivers/gpu/drm/lima/lima_drv.c a/drivers/gpu/drm/lima/lima_drv.c +index b618b33f066a..75ec703d22e0 100644 +--- b/drivers/gpu/drm/lima/lima_drv.c ++++ a/drivers/gpu/drm/lima/lima_drv.c +@@ -10,9 +10,9 @@ + #include + #include + +-#include "lima_devfreq.h" + #include "lima_drv.h" + #include "lima_gem.h" ++#include "lima_gem_prime.h" + #include "lima_vm.h" + + int lima_sched_timeout_ms; +@@ -108,7 +108,7 @@ static int lima_ioctl_gem_submit(struct drm_device *dev, void *data, struct drm_ + if (args->frame_size != pipe->frame_size) + return -EINVAL; + +- bos = kvcalloc(args->nr_bos, sizeof(*submit.bos), GFP_KERNEL); ++ bos = kvcalloc(args->nr_bos, sizeof(*submit.bos) + sizeof(*submit.lbos), GFP_KERNEL); + if (!bos) + return -ENOMEM; + +@@ -142,6 +142,7 @@ static int lima_ioctl_gem_submit(struct drm_device *dev, void *data, struct drm_ + + submit.pipe = args->pipe; + submit.bos = bos; ++ submit.lbos = (void *)bos + size; + submit.nr_bos = args->nr_bos; + submit.task = task; + submit.ctx = ctx; +@@ -158,8 +159,6 @@ static int lima_ioctl_gem_submit(struct drm_device *dev, void *data, struct drm_ + kmem_cache_free(pipe->task_slab, task); + out0: + kvfree(bos); +- if (submit.lbos) +- kvfree(submit.lbos); + return err; + } + +@@ -241,7 +240,16 @@ static const struct drm_ioctl_desc lima_drm_driver_ioctls[] = { + DRM_IOCTL_DEF_DRV(LIMA_CTX_FREE, lima_ioctl_ctx_free, DRM_RENDER_ALLOW), + }; + +-DEFINE_DRM_GEM_SHMEM_FOPS(lima_drm_driver_fops); ++static const struct file_operations lima_drm_driver_fops = { ++ .owner = THIS_MODULE, ++ .open = drm_open, ++ .release = drm_release, ++ .unlocked_ioctl = drm_ioctl, ++#ifdef CONFIG_COMPAT ++ .compat_ioctl = drm_compat_ioctl, ++#endif ++ .mmap = lima_gem_mmap, ++}; + + static struct drm_driver lima_drm_driver = { + .driver_features = DRIVER_RENDER | DRIVER_GEM | DRIVER_SYNCOBJ, +@@ -250,6 +258,10 @@ static struct drm_driver lima_drm_driver = { + .ioctls = lima_drm_driver_ioctls, + .num_ioctls = ARRAY_SIZE(lima_drm_driver_ioctls), + .fops = &lima_drm_driver_fops, ++ .gem_free_object_unlocked = lima_gem_free_object, ++ .gem_open_object = lima_gem_object_open, ++ .gem_close_object = lima_gem_object_close, ++ .gem_vm_ops = &lima_gem_vm_ops, + .name = "lima", + .desc = "lima DRM", + .date = "20190217", +@@ -257,11 +269,11 @@ static struct drm_driver lima_drm_driver = { + .minor = 0, + .patchlevel = 0, + +- .gem_create_object = lima_gem_create_object, + .prime_fd_to_handle = drm_gem_prime_fd_to_handle, +- .gem_prime_import_sg_table = drm_gem_shmem_prime_import_sg_table, ++ .gem_prime_import_sg_table = lima_gem_prime_import_sg_table, + .prime_handle_to_fd = drm_gem_prime_handle_to_fd, +- .gem_prime_mmap = drm_gem_prime_mmap, ++ .gem_prime_get_sg_table = lima_gem_prime_get_sg_table, ++ .gem_prime_mmap = lima_gem_prime_mmap, + }; + + static int lima_pdev_probe(struct platform_device *pdev) +@@ -298,26 +310,18 @@ static int lima_pdev_probe(struct platform_device *pdev) + if (err) + goto err_out1; + +- err = lima_devfreq_init(ldev); +- if (err) { +- dev_err(&pdev->dev, "Fatal error during devfreq init\n"); +- goto err_out2; +- } +- + /* + * Register the DRM device with the core and the connectors with + * sysfs. + */ + err = drm_dev_register(ddev, 0); + if (err < 0) +- goto err_out3; ++ goto err_out2; + + return 0; + +-err_out3: +- lima_device_fini(ldev); + err_out2: +- lima_devfreq_fini(ldev); ++ lima_device_fini(ldev); + err_out1: + drm_dev_put(ddev); + err_out0: +@@ -331,7 +335,6 @@ static int lima_pdev_remove(struct platform_device *pdev) + struct drm_device *ddev = ldev->ddev; + + drm_dev_unregister(ddev); +- lima_devfreq_fini(ldev); + lima_device_fini(ldev); + drm_dev_put(ddev); + lima_sched_slab_fini(); +diff --git b/drivers/gpu/drm/lima/lima_gem.c a/drivers/gpu/drm/lima/lima_gem.c +index 894d505f4f78..4da21353c3a2 100644 +--- b/drivers/gpu/drm/lima/lima_gem.c ++++ a/drivers/gpu/drm/lima/lima_gem.c +@@ -3,7 +3,7 @@ + + #include + #include +-#include ++#include + + #include + #include +@@ -13,55 +13,40 @@ + + #include "lima_drv.h" + #include "lima_gem.h" ++#include "lima_gem_prime.h" + #include "lima_vm.h" ++#include "lima_object.h" + + int lima_gem_create_handle(struct drm_device *dev, struct drm_file *file, + u32 size, u32 flags, u32 *handle) + { + int err; +- gfp_t mask; +- struct drm_gem_shmem_object *shmem; +- struct drm_gem_object *obj; +- struct sg_table *sgt; ++ struct lima_bo *bo; ++ struct lima_device *ldev = to_lima_dev(dev); + +- shmem = drm_gem_shmem_create(dev, size); +- if (IS_ERR(shmem)) +- return PTR_ERR(shmem); ++ bo = lima_bo_create(ldev, size, flags, NULL); ++ if (IS_ERR(bo)) ++ return PTR_ERR(bo); + +- obj = &shmem->base; ++ err = drm_gem_handle_create(file, &bo->gem, handle); + +- /* Mali Utgard GPU can only support 32bit address space */ +- mask = mapping_gfp_mask(obj->filp->f_mapping); +- mask &= ~__GFP_HIGHMEM; +- mask |= __GFP_DMA32; +- mapping_set_gfp_mask(obj->filp->f_mapping, mask); +- +- sgt = drm_gem_shmem_get_pages_sgt(obj); +- if (IS_ERR(sgt)) { +- err = PTR_ERR(sgt); +- goto out; +- } +- +- err = drm_gem_handle_create(file, obj, handle); +- +-out: + /* drop reference from allocate - handle holds it now */ +- drm_gem_object_put_unlocked(obj); ++ drm_gem_object_put_unlocked(&bo->gem); + + return err; + } + +-static void lima_gem_free_object(struct drm_gem_object *obj) ++void lima_gem_free_object(struct drm_gem_object *obj) + { + struct lima_bo *bo = to_lima_bo(obj); + + if (!list_empty(&bo->va)) + dev_err(obj->dev->dev, "lima gem free bo still has va\n"); + +- drm_gem_shmem_free_object(obj); ++ lima_bo_destroy(bo); + } + +-static int lima_gem_object_open(struct drm_gem_object *obj, struct drm_file *file) ++int lima_gem_object_open(struct drm_gem_object *obj, struct drm_file *file) + { + struct lima_bo *bo = to_lima_bo(obj); + struct lima_drm_priv *priv = to_lima_drm_priv(file); +@@ -70,7 +55,7 @@ static int lima_gem_object_open(struct drm_gem_object *obj, struct drm_file *fil + return lima_vm_bo_add(vm, bo, true); + } + +-static void lima_gem_object_close(struct drm_gem_object *obj, struct drm_file *file) ++void lima_gem_object_close(struct drm_gem_object *obj, struct drm_file *file) + { + struct lima_bo *bo = to_lima_bo(obj); + struct lima_drm_priv *priv = to_lima_drm_priv(file); +@@ -79,41 +64,13 @@ static void lima_gem_object_close(struct drm_gem_object *obj, struct drm_file *f + lima_vm_bo_del(vm, bo); + } + +-static const struct drm_gem_object_funcs lima_gem_funcs = { +- .free = lima_gem_free_object, +- .open = lima_gem_object_open, +- .close = lima_gem_object_close, +- .print_info = drm_gem_shmem_print_info, +- .pin = drm_gem_shmem_pin, +- .unpin = drm_gem_shmem_unpin, +- .get_sg_table = drm_gem_shmem_get_sg_table, +- .vmap = drm_gem_shmem_vmap, +- .vunmap = drm_gem_shmem_vunmap, +- .vm_ops = &drm_gem_shmem_vm_ops, +-}; +- +-struct drm_gem_object *lima_gem_create_object(struct drm_device *dev, size_t size) +-{ +- struct lima_bo *bo; +- +- bo = kzalloc(sizeof(*bo), GFP_KERNEL); +- if (!bo) +- return NULL; +- +- mutex_init(&bo->lock); +- INIT_LIST_HEAD(&bo->va); +- +- bo->base.base.funcs = &lima_gem_funcs; +- +- return &bo->base.base; +-} +- + int lima_gem_get_info(struct drm_file *file, u32 handle, u32 *va, u64 *offset) + { + struct drm_gem_object *obj; + struct lima_bo *bo; + struct lima_drm_priv *priv = to_lima_drm_priv(file); + struct lima_vm *vm = priv->vm; ++ int err; + + obj = drm_gem_object_lookup(file, handle); + if (!obj) +@@ -123,38 +80,63 @@ int lima_gem_get_info(struct drm_file *file, u32 handle, u32 *va, u64 *offset) + + *va = lima_vm_get_va(vm, bo); + +- *offset = drm_vma_node_offset_addr(&obj->vma_node); ++ err = drm_gem_create_mmap_offset(obj); ++ if (!err) ++ *offset = drm_vma_node_offset_addr(&obj->vma_node); + + drm_gem_object_put_unlocked(obj); ++ return err; ++} ++ ++static vm_fault_t lima_gem_fault(struct vm_fault *vmf) ++{ ++ struct vm_area_struct *vma = vmf->vma; ++ struct drm_gem_object *obj = vma->vm_private_data; ++ struct lima_bo *bo = to_lima_bo(obj); ++ pfn_t pfn; ++ pgoff_t pgoff; ++ ++ /* We don't use vmf->pgoff since that has the fake offset: */ ++ pgoff = (vmf->address - vma->vm_start) >> PAGE_SHIFT; ++ pfn = __pfn_to_pfn_t(page_to_pfn(bo->pages[pgoff]), PFN_DEV); ++ ++ return vmf_insert_mixed(vma, vmf->address, pfn); ++} ++ ++const struct vm_operations_struct lima_gem_vm_ops = { ++ .fault = lima_gem_fault, ++ .open = drm_gem_vm_open, ++ .close = drm_gem_vm_close, ++}; ++ ++void lima_set_vma_flags(struct vm_area_struct *vma) ++{ ++ pgprot_t prot = vm_get_page_prot(vma->vm_flags); ++ ++ vma->vm_flags |= VM_MIXEDMAP; ++ vma->vm_flags &= ~VM_PFNMAP; ++ vma->vm_page_prot = pgprot_writecombine(prot); ++} ++ ++int lima_gem_mmap(struct file *filp, struct vm_area_struct *vma) ++{ ++ int ret; ++ ++ ret = drm_gem_mmap(filp, vma); ++ if (ret) ++ return ret; ++ ++ lima_set_vma_flags(vma); + return 0; + } + +-static int lima_gem_lookup_bos(struct drm_file *file, struct lima_submit *submit) +-{ +- int i, ret; +- u32 *handles; +- +- handles = kvmalloc_array(submit->nr_bos, sizeof(u32), GFP_KERNEL); +- if (!handles) +- return -ENOMEM; +- +- for (i = 0; i < submit->nr_bos; i++) +- handles[i] = submit->bos[i].handle; +- +- ret = drm_gem_objects_lookup(file, handles, submit->nr_bos, +- (struct drm_gem_object ***)&submit->lbos); +- +- kvfree(handles); +- return ret; +-} +- + static int lima_gem_sync_bo(struct lima_sched_task *task, struct lima_bo *bo, + bool write, bool explicit) + { + int err = 0; + + if (!write) { +- err = dma_resv_reserve_shared(lima_bo_resv(bo), 1); ++ err = dma_resv_reserve_shared(bo->gem.resv, 1); + if (err) + return err; + } +@@ -163,7 +145,62 @@ static int lima_gem_sync_bo(struct lima_sched_task *task, struct lima_bo *bo, + if (explicit) + return 0; + +- return drm_gem_fence_array_add_implicit(&task->deps, &bo->base.base, write); ++ return drm_gem_fence_array_add_implicit(&task->deps, &bo->gem, write); ++} ++ ++static int lima_gem_lock_bos(struct lima_bo **bos, u32 nr_bos, ++ struct ww_acquire_ctx *ctx) ++{ ++ int i, ret = 0, contended, slow_locked = -1; ++ ++ ww_acquire_init(ctx, &reservation_ww_class); ++ ++retry: ++ for (i = 0; i < nr_bos; i++) { ++ if (i == slow_locked) { ++ slow_locked = -1; ++ continue; ++ } ++ ++ ret = ww_mutex_lock_interruptible(&bos[i]->gem.resv->lock, ctx); ++ if (ret < 0) { ++ contended = i; ++ goto err; ++ } ++ } ++ ++ ww_acquire_done(ctx); ++ return 0; ++ ++err: ++ for (i--; i >= 0; i--) ++ ww_mutex_unlock(&bos[i]->gem.resv->lock); ++ ++ if (slow_locked >= 0) ++ ww_mutex_unlock(&bos[slow_locked]->gem.resv->lock); ++ ++ if (ret == -EDEADLK) { ++ /* we lost out in a seqno race, lock and retry.. */ ++ ret = ww_mutex_lock_slow_interruptible( ++ &bos[contended]->gem.resv->lock, ctx); ++ if (!ret) { ++ slow_locked = contended; ++ goto retry; ++ } ++ } ++ ww_acquire_fini(ctx); ++ ++ return ret; ++} ++ ++static void lima_gem_unlock_bos(struct lima_bo **bos, u32 nr_bos, ++ struct ww_acquire_ctx *ctx) ++{ ++ int i; ++ ++ for (i = 0; i < nr_bos; i++) ++ ww_mutex_unlock(&bos[i]->gem.resv->lock); ++ ww_acquire_fini(ctx); + } + + static int lima_gem_add_deps(struct drm_file *file, struct lima_submit *submit) +@@ -199,7 +236,7 @@ int lima_gem_submit(struct drm_file *file, struct lima_submit *submit) + struct lima_vm *vm = priv->vm; + struct drm_syncobj *out_sync = NULL; + struct dma_fence *fence; +- struct lima_bo **bos; ++ struct lima_bo **bos = submit->lbos; + + if (submit->out_sync) { + out_sync = drm_syncobj_find(file, submit->out_sync); +@@ -207,38 +244,43 @@ int lima_gem_submit(struct drm_file *file, struct lima_submit *submit) + return -ENOENT; + } + +- err = lima_gem_lookup_bos(file, submit); ++ for (i = 0; i < submit->nr_bos; i++) { ++ struct drm_gem_object *obj; ++ struct lima_bo *bo; ++ ++ obj = drm_gem_object_lookup(file, submit->bos[i].handle); ++ if (!obj) { ++ err = -ENOENT; ++ goto err_out0; ++ } ++ ++ bo = to_lima_bo(obj); ++ ++ /* increase refcnt of gpu va map to prevent unmapped when executing, ++ * will be decreased when task done ++ */ ++ err = lima_vm_bo_add(vm, bo, false); ++ if (err) { ++ drm_gem_object_put_unlocked(obj); ++ goto err_out0; ++ } ++ ++ bos[i] = bo; ++ } ++ ++ err = lima_gem_lock_bos(bos, submit->nr_bos, &ctx); + if (err) + goto err_out0; + +- bos = submit->lbos; +- +- /* increase refcnt of gpu va map to prevent unmapped when executing, +- * will be decreased when task done +- */ +- for (i = 0; i < submit->nr_bos; i++) { +- err = lima_vm_bo_add(vm, bos[i], false); +- if (err) { +- for (i--; i >= 0; i--) +- lima_vm_bo_del(vm, bos[i]); +- goto err_out1; +- } +- } +- +- err = drm_gem_lock_reservations((struct drm_gem_object **)bos, +- submit->nr_bos, &ctx); +- if (err) +- goto err_out2; +- + err = lima_sched_task_init( + submit->task, submit->ctx->context + submit->pipe, + bos, submit->nr_bos, vm); + if (err) +- goto err_out3; ++ goto err_out1; + + err = lima_gem_add_deps(file, submit); + if (err) +- goto err_out4; ++ goto err_out2; + + for (i = 0; i < submit->nr_bos; i++) { + err = lima_gem_sync_bo( +@@ -246,7 +288,7 @@ int lima_gem_submit(struct drm_file *file, struct lima_submit *submit) + submit->bos[i].flags & LIMA_SUBMIT_BO_WRITE, + submit->flags & LIMA_SUBMIT_FLAG_EXPLICIT_FENCE); + if (err) +- goto err_out4; ++ goto err_out2; + } + + fence = lima_sched_context_queue_task( +@@ -254,16 +296,15 @@ int lima_gem_submit(struct drm_file *file, struct lima_submit *submit) + + for (i = 0; i < submit->nr_bos; i++) { + if (submit->bos[i].flags & LIMA_SUBMIT_BO_WRITE) +- dma_resv_add_excl_fence(lima_bo_resv(bos[i]), fence); ++ dma_resv_add_excl_fence(bos[i]->gem.resv, fence); + else +- dma_resv_add_shared_fence(lima_bo_resv(bos[i]), fence); ++ dma_resv_add_shared_fence(bos[i]->gem.resv, fence); + } + +- drm_gem_unlock_reservations((struct drm_gem_object **)bos, +- submit->nr_bos, &ctx); ++ lima_gem_unlock_bos(bos, submit->nr_bos, &ctx); + + for (i = 0; i < submit->nr_bos; i++) +- drm_gem_object_put_unlocked(&bos[i]->base.base); ++ drm_gem_object_put_unlocked(&bos[i]->gem); + + if (out_sync) { + drm_syncobj_replace_fence(out_sync, fence); +@@ -274,18 +315,17 @@ int lima_gem_submit(struct drm_file *file, struct lima_submit *submit) + + return 0; + +-err_out4: +- lima_sched_task_fini(submit->task); +-err_out3: +- drm_gem_unlock_reservations((struct drm_gem_object **)bos, +- submit->nr_bos, &ctx); + err_out2: +- for (i = 0; i < submit->nr_bos; i++) +- lima_vm_bo_del(vm, bos[i]); ++ lima_sched_task_fini(submit->task); + err_out1: +- for (i = 0; i < submit->nr_bos; i++) +- drm_gem_object_put_unlocked(&bos[i]->base.base); ++ lima_gem_unlock_bos(bos, submit->nr_bos, &ctx); + err_out0: ++ for (i = 0; i < submit->nr_bos; i++) { ++ if (!bos[i]) ++ break; ++ lima_vm_bo_del(vm, bos[i]); ++ drm_gem_object_put_unlocked(&bos[i]->gem); ++ } + if (out_sync) + drm_syncobj_put(out_sync); + return err; +diff --git b/drivers/gpu/drm/lima/lima_gem.h a/drivers/gpu/drm/lima/lima_gem.h +index 1800feb3e47f..556111a01135 100644 +--- b/drivers/gpu/drm/lima/lima_gem.h ++++ a/drivers/gpu/drm/lima/lima_gem.h +@@ -4,37 +4,19 @@ + #ifndef __LIMA_GEM_H__ + #define __LIMA_GEM_H__ + +-#include +- ++struct lima_bo; + struct lima_submit; + +-struct lima_bo { +- struct drm_gem_shmem_object base; ++extern const struct vm_operations_struct lima_gem_vm_ops; + +- struct mutex lock; +- struct list_head va; +-}; +- +-static inline struct lima_bo * +-to_lima_bo(struct drm_gem_object *obj) +-{ +- return container_of(to_drm_gem_shmem_obj(obj), struct lima_bo, base); +-} +- +-static inline size_t lima_bo_size(struct lima_bo *bo) +-{ +- return bo->base.base.size; +-} +- +-static inline struct dma_resv *lima_bo_resv(struct lima_bo *bo) +-{ +- return bo->base.base.resv; +-} +- +-struct drm_gem_object *lima_gem_create_object(struct drm_device *dev, size_t size); ++struct lima_bo *lima_gem_create_bo(struct drm_device *dev, u32 size, u32 flags); + int lima_gem_create_handle(struct drm_device *dev, struct drm_file *file, + u32 size, u32 flags, u32 *handle); ++void lima_gem_free_object(struct drm_gem_object *obj); ++int lima_gem_object_open(struct drm_gem_object *obj, struct drm_file *file); ++void lima_gem_object_close(struct drm_gem_object *obj, struct drm_file *file); + int lima_gem_get_info(struct drm_file *file, u32 handle, u32 *va, u64 *offset); ++int lima_gem_mmap(struct file *filp, struct vm_area_struct *vma); + int lima_gem_submit(struct drm_file *file, struct lima_submit *submit); + int lima_gem_wait(struct drm_file *file, u32 handle, u32 op, s64 timeout_ns); + +diff --git b/drivers/gpu/drm/lima/lima_gem_prime.c a/drivers/gpu/drm/lima/lima_gem_prime.c +new file mode 100644 +index 000000000000..e3eb251e0a12 +--- /dev/null ++++ a/drivers/gpu/drm/lima/lima_gem_prime.c +@@ -0,0 +1,46 @@ ++// SPDX-License-Identifier: GPL-2.0 OR MIT ++/* Copyright 2018-2019 Qiang Yu */ ++ ++#include ++#include ++#include ++#include ++ ++#include "lima_device.h" ++#include "lima_object.h" ++#include "lima_gem.h" ++#include "lima_gem_prime.h" ++ ++struct drm_gem_object *lima_gem_prime_import_sg_table( ++ struct drm_device *dev, struct dma_buf_attachment *attach, ++ struct sg_table *sgt) ++{ ++ struct lima_device *ldev = to_lima_dev(dev); ++ struct lima_bo *bo; ++ ++ bo = lima_bo_create(ldev, attach->dmabuf->size, 0, sgt); ++ if (IS_ERR(bo)) ++ return ERR_CAST(bo); ++ ++ return &bo->gem; ++} ++ ++struct sg_table *lima_gem_prime_get_sg_table(struct drm_gem_object *obj) ++{ ++ struct lima_bo *bo = to_lima_bo(obj); ++ int npages = obj->size >> PAGE_SHIFT; ++ ++ return drm_prime_pages_to_sg(bo->pages, npages); ++} ++ ++int lima_gem_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma) ++{ ++ int ret; ++ ++ ret = drm_gem_mmap_obj(obj, obj->size, vma); ++ if (ret) ++ return ret; ++ ++ lima_set_vma_flags(vma); ++ return 0; ++} +diff --git b/drivers/gpu/drm/lima/lima_gem_prime.h a/drivers/gpu/drm/lima/lima_gem_prime.h +new file mode 100644 +index 000000000000..34b4d35c21e3 +--- /dev/null ++++ a/drivers/gpu/drm/lima/lima_gem_prime.h +@@ -0,0 +1,13 @@ ++/* SPDX-License-Identifier: GPL-2.0 OR MIT */ ++/* Copyright 2018-2019 Qiang Yu */ ++ ++#ifndef __LIMA_GEM_PRIME_H__ ++#define __LIMA_GEM_PRIME_H__ ++ ++struct drm_gem_object *lima_gem_prime_import_sg_table( ++ struct drm_device *dev, struct dma_buf_attachment *attach, ++ struct sg_table *sgt); ++struct sg_table *lima_gem_prime_get_sg_table(struct drm_gem_object *obj); ++int lima_gem_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma); ++ ++#endif +diff --git b/drivers/gpu/drm/lima/lima_mmu.c a/drivers/gpu/drm/lima/lima_mmu.c +index 97ec09dee572..8e1651d6a61f 100644 +--- b/drivers/gpu/drm/lima/lima_mmu.c ++++ a/drivers/gpu/drm/lima/lima_mmu.c +@@ -8,6 +8,7 @@ + #include "lima_device.h" + #include "lima_mmu.h" + #include "lima_vm.h" ++#include "lima_object.h" + #include "lima_regs.h" + + #define mmu_write(reg, data) writel(data, ip->iomem + reg) +diff --git b/drivers/gpu/drm/lima/lima_object.c a/drivers/gpu/drm/lima/lima_object.c +new file mode 100644 +index 000000000000..87123b1d083c +--- /dev/null ++++ a/drivers/gpu/drm/lima/lima_object.c +@@ -0,0 +1,119 @@ ++// SPDX-License-Identifier: GPL-2.0 OR MIT ++/* Copyright 2018-2019 Qiang Yu */ ++ ++#include ++#include ++#include ++ ++#include "lima_object.h" ++ ++void lima_bo_destroy(struct lima_bo *bo) ++{ ++ if (bo->sgt) { ++ kfree(bo->pages); ++ drm_prime_gem_destroy(&bo->gem, bo->sgt); ++ } else { ++ if (bo->pages_dma_addr) { ++ int i, npages = bo->gem.size >> PAGE_SHIFT; ++ ++ for (i = 0; i < npages; i++) { ++ if (bo->pages_dma_addr[i]) ++ dma_unmap_page(bo->gem.dev->dev, ++ bo->pages_dma_addr[i], ++ PAGE_SIZE, DMA_BIDIRECTIONAL); ++ } ++ } ++ ++ if (bo->pages) ++ drm_gem_put_pages(&bo->gem, bo->pages, true, true); ++ } ++ ++ kfree(bo->pages_dma_addr); ++ drm_gem_object_release(&bo->gem); ++ kfree(bo); ++} ++ ++static struct lima_bo *lima_bo_create_struct(struct lima_device *dev, u32 size, u32 flags) ++{ ++ struct lima_bo *bo; ++ int err; ++ ++ size = PAGE_ALIGN(size); ++ ++ bo = kzalloc(sizeof(*bo), GFP_KERNEL); ++ if (!bo) ++ return ERR_PTR(-ENOMEM); ++ ++ mutex_init(&bo->lock); ++ INIT_LIST_HEAD(&bo->va); ++ ++ err = drm_gem_object_init(dev->ddev, &bo->gem, size); ++ if (err) { ++ kfree(bo); ++ return ERR_PTR(err); ++ } ++ ++ return bo; ++} ++ ++struct lima_bo *lima_bo_create(struct lima_device *dev, u32 size, ++ u32 flags, struct sg_table *sgt) ++{ ++ int i, err; ++ size_t npages; ++ struct lima_bo *bo, *ret; ++ ++ bo = lima_bo_create_struct(dev, size, flags); ++ if (IS_ERR(bo)) ++ return bo; ++ ++ npages = bo->gem.size >> PAGE_SHIFT; ++ ++ bo->pages_dma_addr = kcalloc(npages, sizeof(dma_addr_t), GFP_KERNEL); ++ if (!bo->pages_dma_addr) { ++ ret = ERR_PTR(-ENOMEM); ++ goto err_out; ++ } ++ ++ if (sgt) { ++ bo->sgt = sgt; ++ ++ bo->pages = kcalloc(npages, sizeof(*bo->pages), GFP_KERNEL); ++ if (!bo->pages) { ++ ret = ERR_PTR(-ENOMEM); ++ goto err_out; ++ } ++ ++ err = drm_prime_sg_to_page_addr_arrays( ++ sgt, bo->pages, bo->pages_dma_addr, npages); ++ if (err) { ++ ret = ERR_PTR(err); ++ goto err_out; ++ } ++ } else { ++ mapping_set_gfp_mask(bo->gem.filp->f_mapping, GFP_DMA32); ++ bo->pages = drm_gem_get_pages(&bo->gem); ++ if (IS_ERR(bo->pages)) { ++ ret = ERR_CAST(bo->pages); ++ bo->pages = NULL; ++ goto err_out; ++ } ++ ++ for (i = 0; i < npages; i++) { ++ dma_addr_t addr = dma_map_page(dev->dev, bo->pages[i], 0, ++ PAGE_SIZE, DMA_BIDIRECTIONAL); ++ if (dma_mapping_error(dev->dev, addr)) { ++ ret = ERR_PTR(-EFAULT); ++ goto err_out; ++ } ++ bo->pages_dma_addr[i] = addr; ++ } ++ ++ } ++ ++ return bo; ++ ++err_out: ++ lima_bo_destroy(bo); ++ return ret; ++} +diff --git b/drivers/gpu/drm/lima/lima_object.h a/drivers/gpu/drm/lima/lima_object.h +new file mode 100644 +index 000000000000..31ca2d8dc0a1 +--- /dev/null ++++ a/drivers/gpu/drm/lima/lima_object.h +@@ -0,0 +1,35 @@ ++/* SPDX-License-Identifier: GPL-2.0 OR MIT */ ++/* Copyright 2018-2019 Qiang Yu */ ++ ++#ifndef __LIMA_OBJECT_H__ ++#define __LIMA_OBJECT_H__ ++ ++#include ++ ++#include "lima_device.h" ++ ++struct lima_bo { ++ struct drm_gem_object gem; ++ ++ struct page **pages; ++ dma_addr_t *pages_dma_addr; ++ struct sg_table *sgt; ++ void *vaddr; ++ ++ struct mutex lock; ++ struct list_head va; ++}; ++ ++static inline struct lima_bo * ++to_lima_bo(struct drm_gem_object *obj) ++{ ++ return container_of(obj, struct lima_bo, gem); ++} ++ ++struct lima_bo *lima_bo_create(struct lima_device *dev, u32 size, ++ u32 flags, struct sg_table *sgt); ++void lima_bo_destroy(struct lima_bo *bo); ++void *lima_bo_vmap(struct lima_bo *bo); ++void lima_bo_vunmap(struct lima_bo *bo); ++ ++#endif +diff --git b/drivers/gpu/drm/lima/lima_sched.c a/drivers/gpu/drm/lima/lima_sched.c +index 851c496a168b..4127cacac454 100644 +--- b/drivers/gpu/drm/lima/lima_sched.c ++++ a/drivers/gpu/drm/lima/lima_sched.c +@@ -5,13 +5,12 @@ + #include + #include + +-#include "lima_devfreq.h" + #include "lima_drv.h" + #include "lima_sched.h" + #include "lima_vm.h" + #include "lima_mmu.h" + #include "lima_l2_cache.h" +-#include "lima_gem.h" ++#include "lima_object.h" + + struct lima_fence { + struct dma_fence base; +@@ -118,7 +117,7 @@ int lima_sched_task_init(struct lima_sched_task *task, + return -ENOMEM; + + for (i = 0; i < num_bos; i++) +- drm_gem_object_get(&bos[i]->base.base); ++ drm_gem_object_get(&bos[i]->gem); + + err = drm_sched_job_init(&task->base, &context->base, vm); + if (err) { +@@ -149,7 +148,7 @@ void lima_sched_task_fini(struct lima_sched_task *task) + + if (task->bos) { + for (i = 0; i < task->num_bos; i++) +- drm_gem_object_put_unlocked(&task->bos[i]->base.base); ++ drm_gem_object_put_unlocked(&task->bos[i]->gem); + kfree(task->bos); + } + +@@ -214,8 +213,6 @@ static struct dma_fence *lima_sched_run_job(struct drm_sched_job *job) + */ + ret = dma_fence_get(task->fence); + +- lima_devfreq_record_busy(pipe->ldev); +- + pipe->current_task = task; + + /* this is needed for MMU to work correctly, otherwise GP/PP +@@ -283,8 +280,6 @@ static void lima_sched_handle_error_task(struct lima_sched_pipe *pipe, + pipe->current_vm = NULL; + pipe->current_task = NULL; + +- lima_devfreq_record_idle(pipe->ldev); +- + drm_sched_resubmit_jobs(&pipe->base); + drm_sched_start(&pipe->base, true); + } +@@ -353,8 +348,6 @@ void lima_sched_pipe_fini(struct lima_sched_pipe *pipe) + + void lima_sched_pipe_task_done(struct lima_sched_pipe *pipe) + { +- lima_devfreq_record_idle(pipe->ldev); +- + if (pipe->error) + schedule_work(&pipe->error_work); + else { +diff --git b/drivers/gpu/drm/lima/lima_sched.h a/drivers/gpu/drm/lima/lima_sched.h +index 9ae7df7d7fbb..928af91c1118 100644 +--- b/drivers/gpu/drm/lima/lima_sched.h ++++ a/drivers/gpu/drm/lima/lima_sched.h +@@ -6,7 +6,6 @@ + + #include + +-struct lima_device; + struct lima_vm; + + struct lima_sched_task { +@@ -42,8 +41,6 @@ struct lima_sched_pipe { + u32 fence_seqno; + spinlock_t fence_lock; + +- struct lima_device *ldev; +- + struct lima_sched_task *current_task; + struct lima_vm *current_vm; + +diff --git b/drivers/gpu/drm/lima/lima_vm.c a/drivers/gpu/drm/lima/lima_vm.c +index 840e2350d872..19e88ca16527 100644 +--- b/drivers/gpu/drm/lima/lima_vm.c ++++ a/drivers/gpu/drm/lima/lima_vm.c +@@ -6,7 +6,7 @@ + + #include "lima_device.h" + #include "lima_vm.h" +-#include "lima_gem.h" ++#include "lima_object.h" + #include "lima_regs.h" + + struct lima_bo_va { +@@ -32,7 +32,7 @@ struct lima_bo_va { + #define LIMA_BTE(va) ((va & LIMA_VM_BT_MASK) >> LIMA_VM_BT_SHIFT) + + +-static void lima_vm_unmap_range(struct lima_vm *vm, u32 start, u32 end) ++static void lima_vm_unmap_page_table(struct lima_vm *vm, u32 start, u32 end) + { + u32 addr; + +@@ -44,32 +44,41 @@ static void lima_vm_unmap_range(struct lima_vm *vm, u32 start, u32 end) + } + } + +-static int lima_vm_map_page(struct lima_vm *vm, dma_addr_t pa, u32 va) ++static int lima_vm_map_page_table(struct lima_vm *vm, dma_addr_t *dma, ++ u32 start, u32 end) + { +- u32 pbe = LIMA_PBE(va); +- u32 bte = LIMA_BTE(va); ++ u64 addr; ++ int i = 0; + +- if (!vm->bts[pbe].cpu) { +- dma_addr_t pts; +- u32 *pd; +- int j; ++ for (addr = start; addr <= end; addr += LIMA_PAGE_SIZE) { ++ u32 pbe = LIMA_PBE(addr); ++ u32 bte = LIMA_BTE(addr); + +- vm->bts[pbe].cpu = dma_alloc_wc( +- vm->dev->dev, LIMA_PAGE_SIZE << LIMA_VM_NUM_PT_PER_BT_SHIFT, +- &vm->bts[pbe].dma, GFP_KERNEL | __GFP_NOWARN | __GFP_ZERO); +- if (!vm->bts[pbe].cpu) +- return -ENOMEM; ++ if (!vm->bts[pbe].cpu) { ++ dma_addr_t pts; ++ u32 *pd; ++ int j; + +- pts = vm->bts[pbe].dma; +- pd = vm->pd.cpu + (pbe << LIMA_VM_NUM_PT_PER_BT_SHIFT); +- for (j = 0; j < LIMA_VM_NUM_PT_PER_BT; j++) { +- pd[j] = pts | LIMA_VM_FLAG_PRESENT; +- pts += LIMA_PAGE_SIZE; ++ vm->bts[pbe].cpu = dma_alloc_wc( ++ vm->dev->dev, LIMA_PAGE_SIZE << LIMA_VM_NUM_PT_PER_BT_SHIFT, ++ &vm->bts[pbe].dma, GFP_KERNEL | __GFP_ZERO); ++ if (!vm->bts[pbe].cpu) { ++ if (addr != start) ++ lima_vm_unmap_page_table(vm, start, addr - 1); ++ return -ENOMEM; ++ } ++ ++ pts = vm->bts[pbe].dma; ++ pd = vm->pd.cpu + (pbe << LIMA_VM_NUM_PT_PER_BT_SHIFT); ++ for (j = 0; j < LIMA_VM_NUM_PT_PER_BT; j++) { ++ pd[j] = pts | LIMA_VM_FLAG_PRESENT; ++ pts += LIMA_PAGE_SIZE; ++ } + } ++ ++ vm->bts[pbe].cpu[bte] = dma[i++] | LIMA_VM_FLAGS_CACHE; + } + +- vm->bts[pbe].cpu[bte] = pa | LIMA_VM_FLAGS_CACHE; +- + return 0; + } + +@@ -91,8 +100,7 @@ lima_vm_bo_find(struct lima_vm *vm, struct lima_bo *bo) + int lima_vm_bo_add(struct lima_vm *vm, struct lima_bo *bo, bool create) + { + struct lima_bo_va *bo_va; +- struct sg_dma_page_iter sg_iter; +- int offset = 0, err; ++ int err; + + mutex_lock(&bo->lock); + +@@ -120,18 +128,14 @@ int lima_vm_bo_add(struct lima_vm *vm, struct lima_bo *bo, bool create) + + mutex_lock(&vm->lock); + +- err = drm_mm_insert_node(&vm->mm, &bo_va->node, lima_bo_size(bo)); ++ err = drm_mm_insert_node(&vm->mm, &bo_va->node, bo->gem.size); + if (err) + goto err_out1; + +- for_each_sg_dma_page(bo->base.sgt->sgl, &sg_iter, bo->base.sgt->nents, 0) { +- err = lima_vm_map_page(vm, sg_page_iter_dma_address(&sg_iter), +- bo_va->node.start + offset); +- if (err) +- goto err_out2; +- +- offset += PAGE_SIZE; +- } ++ err = lima_vm_map_page_table(vm, bo->pages_dma_addr, bo_va->node.start, ++ bo_va->node.start + bo_va->node.size - 1); ++ if (err) ++ goto err_out2; + + mutex_unlock(&vm->lock); + +@@ -141,8 +145,6 @@ int lima_vm_bo_add(struct lima_vm *vm, struct lima_bo *bo, bool create) + return 0; + + err_out2: +- if (offset) +- lima_vm_unmap_range(vm, bo_va->node.start, bo_va->node.start + offset - 1); + drm_mm_remove_node(&bo_va->node); + err_out1: + mutex_unlock(&vm->lock); +@@ -166,8 +168,8 @@ void lima_vm_bo_del(struct lima_vm *vm, struct lima_bo *bo) + + mutex_lock(&vm->lock); + +- lima_vm_unmap_range(vm, bo_va->node.start, +- bo_va->node.start + bo_va->node.size - 1); ++ lima_vm_unmap_page_table(vm, bo_va->node.start, ++ bo_va->node.start + bo_va->node.size - 1); + + drm_mm_remove_node(&bo_va->node); + +@@ -208,13 +210,14 @@ struct lima_vm *lima_vm_create(struct lima_device *dev) + kref_init(&vm->refcount); + + vm->pd.cpu = dma_alloc_wc(dev->dev, LIMA_PAGE_SIZE, &vm->pd.dma, +- GFP_KERNEL | __GFP_NOWARN | __GFP_ZERO); ++ GFP_KERNEL | __GFP_ZERO); + if (!vm->pd.cpu) + goto err_out0; + + if (dev->dlbu_cpu) { +- int err = lima_vm_map_page( +- vm, dev->dlbu_dma, LIMA_VA_RESERVE_DLBU); ++ int err = lima_vm_map_page_table( ++ vm, &dev->dlbu_dma, LIMA_VA_RESERVE_DLBU, ++ LIMA_VA_RESERVE_DLBU + LIMA_PAGE_SIZE - 1); + if (err) + goto err_out1; + } +diff --git b/drivers/gpu/drm/meson/meson_crtc.c a/drivers/gpu/drm/meson/meson_crtc.c +index eefefc488cd6..57ae1c13d1e6 100644 +--- b/drivers/gpu/drm/meson/meson_crtc.c ++++ a/drivers/gpu/drm/meson/meson_crtc.c +@@ -357,7 +357,7 @@ void meson_crtc_irq(struct meson_drm *priv) + MESON_CANVAS_WRAP_NONE, + MESON_CANVAS_BLKMODE_LINEAR, + MESON_CANVAS_ENDIAN_SWAP64); +- } ++ }; + + writel_relaxed(priv->viu.vd1_if0_gen_reg, + priv->io_base + meson_crtc->viu_offset + +diff --git b/drivers/gpu/drm/meson/meson_plane.c a/drivers/gpu/drm/meson/meson_plane.c +index b96fa431c1ce..ed543227b00d 100644 +--- b/drivers/gpu/drm/meson/meson_plane.c ++++ a/drivers/gpu/drm/meson/meson_plane.c +@@ -178,7 +178,7 @@ static void meson_plane_atomic_update(struct drm_plane *plane, + priv->viu.osd1_blk0_cfg[0] |= OSD_BLK_MODE_16 | + OSD_COLOR_MATRIX_16_RGB565; + break; +- } ++ }; + + /* Default scaler parameters */ + vsc_bot_rcv_num = 0; +diff --git b/drivers/gpu/drm/panfrost/panfrost_drv.c a/drivers/gpu/drm/panfrost/panfrost_drv.c +index f57dd195dfb8..5906c80c4b2c 100644 +--- b/drivers/gpu/drm/panfrost/panfrost_drv.c ++++ a/drivers/gpu/drm/panfrost/panfrost_drv.c +@@ -166,7 +166,6 @@ panfrost_lookup_bos(struct drm_device *dev, + break; + } + +- atomic_inc(&bo->gpu_usecount); + job->mappings[i] = mapping; + } + +diff --git b/drivers/gpu/drm/panfrost/panfrost_gem.h a/drivers/gpu/drm/panfrost/panfrost_gem.h +index b3517ff9630c..ca1bc9019600 100644 +--- b/drivers/gpu/drm/panfrost/panfrost_gem.h ++++ a/drivers/gpu/drm/panfrost/panfrost_gem.h +@@ -30,12 +30,6 @@ struct panfrost_gem_object { + struct mutex lock; + } mappings; + +- /* +- * Count the number of jobs referencing this BO so we don't let the +- * shrinker reclaim this object prematurely. +- */ +- atomic_t gpu_usecount; +- + bool noexec :1; + bool is_heap :1; + }; +diff --git b/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c a/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c +index 288e46c40673..f5dd7b29bc95 100644 +--- b/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c ++++ a/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c +@@ -41,9 +41,6 @@ static bool panfrost_gem_purge(struct drm_gem_object *obj) + struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj); + struct panfrost_gem_object *bo = to_panfrost_bo(obj); + +- if (atomic_read(&bo->gpu_usecount)) +- return false; +- + if (!mutex_trylock(&shmem->pages_lock)) + return false; + +diff --git b/drivers/gpu/drm/panfrost/panfrost_gpu.c a/drivers/gpu/drm/panfrost/panfrost_gpu.c +index 90728a177208..8822ec13a0d6 100644 +--- b/drivers/gpu/drm/panfrost/panfrost_gpu.c ++++ a/drivers/gpu/drm/panfrost/panfrost_gpu.c +@@ -7,9 +7,7 @@ + #include + #include + #include +-#include + #include +-#include + #include + #include + +@@ -61,16 +59,7 @@ int panfrost_gpu_soft_reset(struct panfrost_device *pfdev) + + gpu_write(pfdev, GPU_INT_MASK, 0); + gpu_write(pfdev, GPU_INT_CLEAR, GPU_IRQ_RESET_COMPLETED); +- +- if (of_device_is_compatible(pfdev->dev->of_node, "amlogic,meson-gxm-mali")) { +- reset_control_assert(pfdev->rstc); +- udelay(10); +- reset_control_deassert(pfdev->rstc); +- +- gpu_write(pfdev, GPU_PWR_KEY, 0x2968A819); +- gpu_write(pfdev, GPU_PWR_OVERRIDE1, 0xfff | (0x20 << 16)); +- } else +- gpu_write(pfdev, GPU_CMD, GPU_CMD_SOFT_RESET); ++ gpu_write(pfdev, GPU_CMD, GPU_CMD_SOFT_RESET); + + ret = readl_relaxed_poll_timeout(pfdev->iomem + GPU_INT_RAWSTAT, + val, val & GPU_IRQ_RESET_COMPLETED, 100, 10000); +diff --git b/drivers/gpu/drm/panfrost/panfrost_job.c a/drivers/gpu/drm/panfrost/panfrost_job.c +index 9f770d454684..bbb0c5e3ca6f 100644 +--- b/drivers/gpu/drm/panfrost/panfrost_job.c ++++ a/drivers/gpu/drm/panfrost/panfrost_job.c +@@ -270,13 +270,8 @@ static void panfrost_job_cleanup(struct kref *ref) + dma_fence_put(job->render_done_fence); + + if (job->mappings) { +- for (i = 0; i < job->bo_count; i++) { +- if (!job->mappings[i]) +- break; +- +- atomic_dec(&job->mappings[i]->obj->gpu_usecount); ++ for (i = 0; i < job->bo_count; i++) + panfrost_gem_mapping_put(job->mappings[i]); +- } + kvfree(job->mappings); + } + +diff --git b/drivers/gpu/drm/panfrost/panfrost_regs.h a/drivers/gpu/drm/panfrost/panfrost_regs.h +index 7a0cfa629760..ea38ac60581c 100644 +--- b/drivers/gpu/drm/panfrost/panfrost_regs.h ++++ a/drivers/gpu/drm/panfrost/panfrost_regs.h +@@ -69,10 +69,6 @@ + #define GPU_PRFCNT_TILER_EN 0x74 + #define GPU_PRFCNT_MMU_L2_EN 0x7c + +-#define GPU_PWR_KEY 0x050 /* (WO) Power manager key register */ +-#define GPU_PWR_OVERRIDE0 0x054 /* (RW) Power manager override settings */ +-#define GPU_PWR_OVERRIDE1 0x058 /* (RW) Power manager override settings */ +- + #define GPU_THREAD_MAX_THREADS 0x0A0 /* (RO) Maximum number of threads per core */ + #define GPU_THREAD_MAX_WORKGROUP_SIZE 0x0A4 /* (RO) Maximum workgroup size */ + #define GPU_THREAD_MAX_BARRIER_SIZE 0x0A8 /* (RO) Maximum threads waiting at a barrier */ +diff --git b/drivers/gpu/drm/sun4i/sun4i_drv.c a/drivers/gpu/drm/sun4i/sun4i_drv.c +index 5b54eff12cc0..a5757b11b730 100644 +--- b/drivers/gpu/drm/sun4i/sun4i_drv.c ++++ a/drivers/gpu/drm/sun4i/sun4i_drv.c +@@ -85,6 +85,7 @@ static int sun4i_drv_bind(struct device *dev) + } + + drm_mode_config_init(drm); ++ drm->mode_config.allow_fb_modifiers = true; + + ret = component_bind_all(drm->dev, drm); + if (ret) { +diff --git b/drivers/gpu/drm/v3d/v3d_gem.c a/drivers/gpu/drm/v3d/v3d_gem.c +index 810d2138e594..19c092d75266 100644 +--- b/drivers/gpu/drm/v3d/v3d_gem.c ++++ a/drivers/gpu/drm/v3d/v3d_gem.c +@@ -290,6 +290,10 @@ v3d_lookup_bos(struct drm_device *dev, + u64 bo_handles, + u32 bo_count) + { ++ u32 *handles; ++ int ret = 0; ++ int i; ++ + job->bo_count = bo_count; + + if (!job->bo_count) { +@@ -300,9 +304,48 @@ v3d_lookup_bos(struct drm_device *dev, + return -EINVAL; + } + +- return drm_gem_objects_lookup_user(file_priv, +- (void __user *)(uintptr_t)bo_handles, +- job->bo_count, &job->bo); ++ job->bo = kvmalloc_array(job->bo_count, ++ sizeof(struct drm_gem_cma_object *), ++ GFP_KERNEL | __GFP_ZERO); ++ if (!job->bo) { ++ DRM_DEBUG("Failed to allocate validated BO pointers\n"); ++ return -ENOMEM; ++ } ++ ++ handles = kvmalloc_array(job->bo_count, sizeof(u32), GFP_KERNEL); ++ if (!handles) { ++ ret = -ENOMEM; ++ DRM_DEBUG("Failed to allocate incoming GEM handles\n"); ++ goto fail; ++ } ++ ++ if (copy_from_user(handles, ++ (void __user *)(uintptr_t)bo_handles, ++ job->bo_count * sizeof(u32))) { ++ ret = -EFAULT; ++ DRM_DEBUG("Failed to copy in GEM handles\n"); ++ goto fail; ++ } ++ ++ spin_lock(&file_priv->table_lock); ++ for (i = 0; i < job->bo_count; i++) { ++ struct drm_gem_object *bo = idr_find(&file_priv->object_idr, ++ handles[i]); ++ if (!bo) { ++ DRM_DEBUG("Failed to look up GEM BO %d: %d\n", ++ i, handles[i]); ++ ret = -ENOENT; ++ spin_unlock(&file_priv->table_lock); ++ goto fail; ++ } ++ drm_gem_object_get(bo); ++ job->bo[i] = bo; ++ } ++ spin_unlock(&file_priv->table_lock); ++ ++fail: ++ kvfree(handles); ++ return ret; + } + + static void +diff --git b/drivers/gpu/drm/vgem/vgem_drv.c a/drivers/gpu/drm/vgem/vgem_drv.c +index 909eba43664a..5bd60ded3d81 100644 +--- b/drivers/gpu/drm/vgem/vgem_drv.c ++++ a/drivers/gpu/drm/vgem/vgem_drv.c +@@ -196,10 +196,9 @@ static struct drm_gem_object *vgem_gem_create(struct drm_device *dev, + return ERR_CAST(obj); + + ret = drm_gem_handle_create(file, &obj->base, handle); +- if (ret) { +- drm_gem_object_put_unlocked(&obj->base); ++ drm_gem_object_put_unlocked(&obj->base); ++ if (ret) + return ERR_PTR(ret); +- } + + return &obj->base; + } +@@ -222,9 +221,7 @@ static int vgem_gem_dumb_create(struct drm_file *file, struct drm_device *dev, + args->size = gem_object->size; + args->pitch = pitch; + +- drm_gem_object_put_unlocked(gem_object); +- +- DRM_DEBUG("Created object of size %llu\n", args->size); ++ DRM_DEBUG("Created object of size %lld\n", size); + + return 0; + } +diff --git b/drivers/media/platform/meson/ao-cec-g12a.c a/drivers/media/platform/meson/ao-cec-g12a.c +index 9d7f10ea0692..3d8fe854feb0 100644 +--- b/drivers/media/platform/meson/ao-cec-g12a.c ++++ a/drivers/media/platform/meson/ao-cec-g12a.c +@@ -25,7 +25,6 @@ + #include + #include + #include +-#include + + /* CEC Registers */ + +@@ -169,18 +168,6 @@ + + #define CECB_WAKEUPCTRL 0x31 + +-#define CECB_FUNC_CFG_REG 0xA0 +-#define CECB_FUNC_CFG_MASK GENMASK(6, 0) +-#define CECB_FUNC_CFG_CEC_ON 0x01 +-#define CECB_FUNC_CFG_OTP_ON 0x02 +-#define CECB_FUNC_CFG_AUTO_STANDBY 0x04 +-#define CECB_FUNC_CFG_AUTO_POWER_ON 0x08 +-#define CECB_FUNC_CFG_ALL 0x2f +-#define CECB_FUNC_CFG_NONE 0x0 +- +-#define CECB_LOG_ADDR_REG 0xA4 +-#define CECB_LOG_ADDR_MASK GENMASK(22, 16) +- + struct meson_ao_cec_g12a_data { + /* Setup the internal CECB_CTRL2 register */ + bool ctrl2_setup; +@@ -190,7 +177,6 @@ struct meson_ao_cec_g12a_device { + struct platform_device *pdev; + struct regmap *regmap; + struct regmap *regmap_cec; +- struct regmap *regmap_ao_sysctrl; + spinlock_t cec_reg_lock; + struct cec_notifier *notify; + struct cec_adapter *adap; +@@ -532,13 +518,6 @@ meson_ao_cec_g12a_set_log_addr(struct cec_adapter *adap, u8 logical_addr) + BIT(logical_addr - 8)); + } + +- if (ao_cec->regmap_ao_sysctrl) +- ret |= regmap_update_bits(ao_cec->regmap_ao_sysctrl, +- CECB_LOG_ADDR_REG, +- CECB_LOG_ADDR_MASK, +- FIELD_PREP(CECB_LOG_ADDR_MASK, +- logical_addr)); +- + /* Always set Broadcast/Unregistered 15 address */ + ret |= regmap_update_bits(ao_cec->regmap_cec, CECB_LADD_HIGH, + BIT(CEC_LOG_ADDR_UNREGISTERED - 8), +@@ -639,13 +618,6 @@ static int meson_ao_cec_g12a_adap_enable(struct cec_adapter *adap, bool enable) + regmap_write(ao_cec->regmap_cec, CECB_CTRL2, + FIELD_PREP(CECB_CTRL2_RISE_DEL_MAX, 2)); + +- if (ao_cec->regmap_ao_sysctrl) { +- regmap_update_bits(ao_cec->regmap_ao_sysctrl, +- CECB_FUNC_CFG_REG, +- CECB_FUNC_CFG_MASK, +- CECB_FUNC_CFG_ALL); +- } +- + meson_ao_cec_g12a_irq_setup(ao_cec, true); + + return 0; +@@ -713,11 +685,6 @@ static int meson_ao_cec_g12a_probe(struct platform_device *pdev) + goto out_probe_adapter; + } + +- ao_cec->regmap_ao_sysctrl = syscon_regmap_lookup_by_phandle +- (pdev->dev.of_node, "amlogic,ao-sysctrl"); +- if (IS_ERR(ao_cec->regmap_ao_sysctrl)) +- dev_warn(&pdev->dev, "ao-sysctrl syscon regmap lookup failed.\n"); +- + irq = platform_get_irq(pdev, 0); + ret = devm_request_threaded_irq(&pdev->dev, irq, + meson_ao_cec_g12a_irq, +diff --git b/drivers/media/rc/meson-ir.c a/drivers/media/rc/meson-ir.c +index 4e257cc74b7b..51c6dd3406a0 100644 +--- b/drivers/media/rc/meson-ir.c ++++ a/drivers/media/rc/meson-ir.c +@@ -91,8 +91,7 @@ static irqreturn_t meson_ir_irq(int irqno, void *dev_id) + status = readl_relaxed(ir->reg + IR_DEC_STATUS); + rawir.pulse = !!(status & STATUS_IR_DEC_IN); + +- if (ir_raw_event_store_with_filter(ir->rc, &rawir)) +- ir_raw_event_handle(ir->rc); ++ ir_raw_event_store_with_timeout(ir->rc, &rawir); + + spin_unlock(&ir->lock); + +diff --git b/drivers/soc/amlogic/meson-canvas.c a/drivers/soc/amlogic/meson-canvas.c +index 561044063319..c655f5f92b12 100644 +--- b/drivers/soc/amlogic/meson-canvas.c ++++ a/drivers/soc/amlogic/meson-canvas.c +@@ -166,6 +166,7 @@ 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; + +@@ -173,7 +174,8 @@ static int meson_canvas_probe(struct platform_device *pdev) + if (!canvas) + return -ENOMEM; + +- canvas->reg_base = devm_platform_ioremap_resource(pdev, 0); ++ 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); + +diff --git b/drivers/soc/amlogic/meson-clk-measure.c a/drivers/soc/amlogic/meson-clk-measure.c +index 173baa53fce3..0fa47d77577d 100644 +--- b/drivers/soc/amlogic/meson-clk-measure.c ++++ a/drivers/soc/amlogic/meson-clk-measure.c +@@ -605,6 +605,7 @@ static int meson_msr_probe(struct platform_device *pdev) + { + const struct meson_msr_id *match_data; + struct meson_msr *priv; ++ struct resource *res; + struct dentry *root, *clks; + void __iomem *base; + int i; +@@ -622,7 +623,8 @@ static int meson_msr_probe(struct platform_device *pdev) + + memcpy(priv->msr_table, match_data, sizeof(priv->msr_table)); + +- base = devm_platform_ioremap_resource(pdev, 0); ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) { + dev_err(&pdev->dev, "io resource mapping failed\n"); + return PTR_ERR(base); +diff --git b/include/drm/bridge/dw_hdmi.h a/include/drm/bridge/dw_hdmi.h +index 4b3e863c4f8a..cf528c289857 100644 +--- b/include/drm/bridge/dw_hdmi.h ++++ a/include/drm/bridge/dw_hdmi.h +@@ -156,7 +156,6 @@ 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_set_channel_count(struct dw_hdmi *hdmi, unsigned int cnt); +-void dw_hdmi_set_channel_status(struct dw_hdmi *hdmi, u8 *channel_status); + void dw_hdmi_set_channel_allocation(struct dw_hdmi *hdmi, unsigned int ca); + void dw_hdmi_audio_enable(struct dw_hdmi *hdmi); + void dw_hdmi_audio_disable(struct dw_hdmi *hdmi); +diff --git b/include/drm/drm_gem.h a/include/drm/drm_gem.h +index 354fc8d240e4..6aaba14f5972 100644 +--- b/include/drm/drm_gem.h ++++ a/include/drm/drm_gem.h +@@ -387,10 +387,8 @@ struct page **drm_gem_get_pages(struct drm_gem_object *obj); + void drm_gem_put_pages(struct drm_gem_object *obj, struct page **pages, + bool dirty, bool accessed); + +-int drm_gem_objects_lookup(struct drm_file *filp, u32 *bo_handles, ++int drm_gem_objects_lookup(struct drm_file *filp, void __user *bo_handles, + int count, struct drm_gem_object ***objs_out); +-int drm_gem_objects_lookup_user(struct drm_file *filp, void __user *bo_handles, +- int count, struct drm_gem_object ***objs_out); + struct drm_gem_object *drm_gem_object_lookup(struct drm_file *filp, u32 handle); + long drm_gem_dma_resv_wait(struct drm_file *filep, u32 handle, + bool wait_all, unsigned long timeout); +diff --git b/sound/soc/meson/Kconfig a/sound/soc/meson/Kconfig +index 2e3676147cea..77611ddd64b4 100644 +--- b/sound/soc/meson/Kconfig ++++ a/sound/soc/meson/Kconfig +@@ -2,6 +2,47 @@ + menu "ASoC support for Amlogic platforms" + depends on ARCH_MESON || COMPILE_TEST + ++config SND_MESON_AIU_BUS ++ tristate "Amlogic AIU bus support" ++ select REGMAP_MMIO ++ help ++ Select Y or M to add support for audio output interfaces ++ embedded in the Amlogic GX SoC families ++ ++config SND_MESON_AIU_FIFO ++ tristate ++ imply SND_MESON_AIU_BUS ++ select MFD_SYSCON ++ ++config SND_MESON_AIU_I2S_FIFO ++ tristate "Amlogic AIU I2S FIFO" ++ select SND_MESON_AIU_FIFO ++ help ++ Select Y or M to add support for i2s FIFO of the GXL family ++ ++config SND_MESON_AIU_SPDIF_FIFO ++ tristate "Amlogic AIU SPDIF FIFO" ++ select SND_MESON_AIU_FIFO ++ help ++ Select Y or M to add support for spdif FIFO of the GXL family ++ ++config SND_MESON_AIU_I2S_ENCODER ++ tristate "Amlogic AIU I2S Encoder" ++ imply SND_MESON_AIU_I2S_FIFO ++ imply SND_MESON_AIU_BUS ++ select MFD_SYSCON ++ help ++ Select Y or M to add support for i2s Encoder of the GXL family ++ ++config SND_MESON_AIU_SPDIF_ENCODER ++ tristate "Amlogic AIU SPDIF Encoder" ++ imply SND_MESON_AIU_SPDIF_FIFO ++ imply SND_MESON_AIU_BUS ++ select SND_PCM_IEC958 ++ select MFD_SYSCON ++ help ++ Select Y or M to add support for spdif Encoder of the GXL family ++ + config SND_MESON_AXG_FIFO + tristate + select REGMAP_MMIO +@@ -50,6 +91,7 @@ config SND_MESON_AXG_TDMOUT + config SND_MESON_AXG_SOUND_CARD + tristate "Amlogic AXG Sound Card Support" + select SND_MESON_AXG_TDM_INTERFACE ++ select SND_MESON_CARD_UTILS + imply SND_MESON_AXG_FRDDR + imply SND_MESON_AXG_TODDR + imply SND_MESON_AXG_TDMIN +@@ -85,6 +127,19 @@ config SND_MESON_AXG_PDM + Select Y or M to add support for PDM input embedded + in the Amlogic AXG SoC family + ++config SND_MESON_CARD_UTILS ++ tristate ++ ++config SND_MESON_GX_SOUND_CARD ++ tristate "Amlogic GX Sound Card Support" ++ select SND_MESON_CARD_UTILS ++ imply SND_MESON_AIU_I2S_FIFO ++ imply SND_MESON_AIU_SPDIF_FIFO ++ imply SND_MESON_AIU_I2S_ENCODE ++ imply SND_MESON_AIU_SPDIF_ENCODE ++ help ++ Select Y or M to add support for the GXBB/GXL SoC sound card ++ + config SND_MESON_G12A_TOHDMITX + tristate "Amlogic G12A To HDMI TX Control Support" + select REGMAP_MMIO +diff --git b/sound/soc/meson/Makefile a/sound/soc/meson/Makefile +index 1a8b1470ed84..b09de82a4714 100644 +--- b/sound/soc/meson/Makefile ++++ a/sound/soc/meson/Makefile +@@ -1,5 +1,11 @@ + # SPDX-License-Identifier: (GPL-2.0 OR MIT) + ++snd-soc-meson-aiu-bus-objs := aiu-bus.o ++snd-soc-meson-aiu-fifo-objs := aiu-fifo.o ++snd-soc-meson-aiu-i2s-fifo-objs := aiu-i2s-fifo.o ++snd-soc-meson-aiu-spdif-fifo-objs := aiu-spdif-fifo.o ++snd-soc-meson-aiu-i2s-encode-objs := aiu-i2s-encode.o ++snd-soc-meson-aiu-spdif-encode-objs := aiu-spdif-encode.o + snd-soc-meson-axg-fifo-objs := axg-fifo.o + snd-soc-meson-axg-frddr-objs := axg-frddr.o + snd-soc-meson-axg-toddr-objs := axg-toddr.o +@@ -11,8 +17,17 @@ snd-soc-meson-axg-sound-card-objs := axg-card.o + snd-soc-meson-axg-spdifin-objs := axg-spdifin.o + snd-soc-meson-axg-spdifout-objs := axg-spdifout.o + snd-soc-meson-axg-pdm-objs := axg-pdm.o ++snd-soc-meson-card-utils-objs := meson-card-utils.o ++snd-soc-meson-gx-sound-card-objs := gx-card.o ++snd-soc-meson-g12a-toacodec-objs := g12a-toacodec.o + snd-soc-meson-g12a-tohdmitx-objs := g12a-tohdmitx.o + ++obj-$(CONFIG_SND_MESON_AIU_BUS) += snd-soc-meson-aiu-bus.o ++obj-$(CONFIG_SND_MESON_AIU_FIFO) += snd-soc-meson-aiu-fifo.o ++obj-$(CONFIG_SND_MESON_AIU_I2S_FIFO) += snd-soc-meson-aiu-i2s-fifo.o ++obj-$(CONFIG_SND_MESON_AIU_SPDIF_FIFO) += snd-soc-meson-aiu-spdif-fifo.o ++obj-$(CONFIG_SND_MESON_AIU_I2S_ENCODER) += snd-soc-meson-aiu-i2s-encode.o ++obj-$(CONFIG_SND_MESON_AIU_SPDIF_ENCODER) += snd-soc-meson-aiu-spdif-encode.o + obj-$(CONFIG_SND_MESON_AXG_FIFO) += snd-soc-meson-axg-fifo.o + obj-$(CONFIG_SND_MESON_AXG_FRDDR) += snd-soc-meson-axg-frddr.o + obj-$(CONFIG_SND_MESON_AXG_TODDR) += snd-soc-meson-axg-toddr.o +@@ -24,4 +39,6 @@ obj-$(CONFIG_SND_MESON_AXG_SOUND_CARD) += snd-soc-meson-axg-sound-card.o + obj-$(CONFIG_SND_MESON_AXG_SPDIFIN) += snd-soc-meson-axg-spdifin.o + obj-$(CONFIG_SND_MESON_AXG_SPDIFOUT) += snd-soc-meson-axg-spdifout.o + obj-$(CONFIG_SND_MESON_AXG_PDM) += snd-soc-meson-axg-pdm.o ++obj-$(CONFIG_SND_MESON_CARD_UTILS) += snd-soc-meson-card-utils.o ++obj-$(CONFIG_SND_MESON_GX_SOUND_CARD) += snd-soc-meson-gx-sound-card.o + obj-$(CONFIG_SND_MESON_G12A_TOHDMITX) += snd-soc-meson-g12a-tohdmitx.o +diff --git b/sound/soc/meson/aiu-bus.c a/sound/soc/meson/aiu-bus.c +new file mode 100644 +index 000000000000..479c5e6b0f93 +--- /dev/null ++++ a/sound/soc/meson/aiu-bus.c +@@ -0,0 +1,82 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// ++// Copyright (c) 2019 BayLibre, SAS. ++// Author: Jerome Brunet ++ ++#include ++#include ++#include ++#include ++ ++static const char * const aiu_bus_clk_names[] = { "top", "glue", }; ++ ++static int aiu_bus_enable_pclks(struct device *dev) ++{ ++ struct clk *clock; ++ int i, ret; ++ ++ for (i = 0; i < ARRAY_SIZE(aiu_bus_clk_names); i++) { ++ clock = devm_clk_get(dev, aiu_bus_clk_names[i]); ++ if (IS_ERR(clock)) { ++ if (PTR_ERR(clock) != -EPROBE_DEFER) ++ dev_err(dev, "Failed to get %s clock\n", ++ aiu_bus_clk_names[i]); ++ return PTR_ERR(clock); ++ } ++ ++ ret = clk_prepare_enable(clock); ++ if (ret) { ++ dev_err(dev, "Failed to enable %s clock\n", ++ aiu_bus_clk_names[i]); ++ return ret; ++ } ++ ++ ret = devm_add_action_or_reset(dev, ++ (void(*)(void *))clk_disable_unprepare, ++ clock); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static const struct of_device_id aiu_bus_of_match[] = { ++ { ++ .compatible = "amlogic,aiu-bus", ++ .data = NULL, ++ }, {} ++}; ++MODULE_DEVICE_TABLE(of, aiu_bus_of_match); ++ ++static int aiu_bus_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ int ret; ++ ++ /* Fire and forget bus pclks */ ++ ret = aiu_bus_enable_pclks(dev); ++ if (ret) ++ return ret; ++ ++ ret = device_reset(dev); ++ if (ret) { ++ dev_err(dev, "reset failed\n"); ++ return ret; ++ } ++ ++ return devm_of_platform_populate(dev); ++} ++ ++static struct platform_driver aiu_bus_pdrv = { ++ .probe = aiu_bus_probe, ++ .driver = { ++ .name = "meson-aiu-bus", ++ .of_match_table = aiu_bus_of_match, ++ }, ++}; ++module_platform_driver(aiu_bus_pdrv); ++ ++MODULE_DESCRIPTION("Amlogic AIU bus driver"); ++MODULE_AUTHOR("Jerome Brunet "); ++MODULE_LICENSE("GPL v2"); +diff --git b/sound/soc/meson/aiu-fifo.c a/sound/soc/meson/aiu-fifo.c +new file mode 100644 +index 000000000000..2c50145a6cac +--- /dev/null ++++ a/sound/soc/meson/aiu-fifo.c +@@ -0,0 +1,276 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// ++// Copyright (c) 2019 BayLibre, SAS. ++// Author: Jerome Brunet ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "aiu-fifo.h" ++ ++#define AIU_MEM_START 0x00 ++#define AIU_MEM_RD 0x04 ++#define AIU_MEM_END 0x08 ++#define AIU_MEM_MASKS 0x0c ++#define AIU_MEM_MASK_CH_RD GENMASK(7, 0) ++#define AIU_MEM_MASK_CH_MEM GENMASK(15, 8) ++#define AIU_MEM_CONTROL 0x10 ++#define AIU_MEM_CONTROL_INIT BIT(0) ++#define AIU_MEM_CONTROL_FILL_EN BIT(1) ++#define AIU_MEM_CONTROL_EMPTY_EN BIT(2) ++ ++snd_pcm_uframes_t aiu_fifo_pointer(struct snd_pcm_substream *substream) ++{ ++ struct snd_soc_pcm_runtime *rtd = substream->private_data; ++ struct snd_soc_dai *dai = rtd->cpu_dai; ++ struct snd_soc_component *component = dai->component; ++ struct aiu_fifo *fifo = snd_soc_component_get_drvdata(component); ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ unsigned int addr; ++ ++ snd_soc_component_read(component, fifo->hw->mem_offset + AIU_MEM_RD, ++ &addr); ++ ++ return bytes_to_frames(runtime, addr - (unsigned int)runtime->dma_addr); ++} ++EXPORT_SYMBOL_GPL(aiu_fifo_pointer); ++ ++static void aiu_fifo_enable(struct snd_soc_component *component, ++ bool enable) ++{ ++ struct aiu_fifo *fifo = snd_soc_component_get_drvdata(component); ++ unsigned int en_mask = (AIU_MEM_CONTROL_FILL_EN | ++ AIU_MEM_CONTROL_EMPTY_EN); ++ ++ snd_soc_component_update_bits(component, ++ fifo->hw->mem_offset + AIU_MEM_CONTROL, ++ en_mask, enable ? en_mask : 0); ++} ++ ++int aiu_fifo_trigger(struct snd_pcm_substream *substream, int cmd, ++ struct snd_soc_dai *dai) ++{ ++ struct snd_soc_component *component = dai->component; ++ ++ switch (cmd) { ++ case SNDRV_PCM_TRIGGER_START: ++ case SNDRV_PCM_TRIGGER_RESUME: ++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: ++ aiu_fifo_enable(component, true); ++ break; ++ case SNDRV_PCM_TRIGGER_SUSPEND: ++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: ++ case SNDRV_PCM_TRIGGER_STOP: ++ aiu_fifo_enable(component, false); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(aiu_fifo_trigger); ++ ++ ++int aiu_fifo_prepare(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct snd_soc_component *component = dai->component; ++ struct aiu_fifo *fifo = snd_soc_component_get_drvdata(component); ++ ++ snd_soc_component_update_bits(component, ++ fifo->hw->mem_offset + AIU_MEM_CONTROL, ++ AIU_MEM_CONTROL_INIT, ++ AIU_MEM_CONTROL_INIT); ++ snd_soc_component_update_bits(component, ++ fifo->hw->mem_offset + AIU_MEM_CONTROL, ++ AIU_MEM_CONTROL_INIT, 0); ++ return 0; ++} ++EXPORT_SYMBOL_GPL(aiu_fifo_prepare); ++ ++int aiu_fifo_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params, ++ struct snd_soc_dai *dai) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ struct snd_soc_component *component = dai->component; ++ struct aiu_fifo *fifo = snd_soc_component_get_drvdata(component); ++ dma_addr_t end; ++ int ret; ++ ++ ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); ++ if (ret < 0) ++ return ret; ++ ++ /* Setup the fifo boundaries */ ++ end = runtime->dma_addr + runtime->dma_bytes - fifo->hw->fifo_block; ++ snd_soc_component_write(component, fifo->hw->mem_offset + AIU_MEM_START, ++ runtime->dma_addr); ++ snd_soc_component_write(component, fifo->hw->mem_offset + AIU_MEM_RD, ++ runtime->dma_addr); ++ snd_soc_component_write(component, fifo->hw->mem_offset + AIU_MEM_END, ++ end); ++ ++ /* Setup the fifo to read all the memory - no skip */ ++ snd_soc_component_update_bits(component, ++ fifo->hw->mem_offset + AIU_MEM_MASKS, ++ AIU_MEM_MASK_CH_RD | AIU_MEM_MASK_CH_MEM, ++ FIELD_PREP(AIU_MEM_MASK_CH_RD, 0xff) | ++ FIELD_PREP(AIU_MEM_MASK_CH_MEM, 0xff)); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(aiu_fifo_hw_params); ++ ++int aiu_fifo_hw_free(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ return snd_pcm_lib_free_pages(substream); ++} ++EXPORT_SYMBOL_GPL(aiu_fifo_hw_free); ++ ++static irqreturn_t aiu_fifo_isr(int irq, void *dev_id) ++{ ++ struct snd_pcm_substream *playback = dev_id; ++ ++ snd_pcm_period_elapsed(playback); ++ ++ return IRQ_HANDLED; ++} ++ ++int aiu_fifo_startup(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct snd_soc_component *component = dai->component; ++ struct aiu_fifo *fifo = snd_soc_component_get_drvdata(component); ++ int ret; ++ ++ snd_soc_set_runtime_hwparams(substream, fifo->hw->pcm); ++ ++ /* ++ * Make sure the buffer and period size are multiple of the fifo burst ++ * size ++ */ ++ ret = snd_pcm_hw_constraint_step(substream->runtime, 0, ++ SNDRV_PCM_HW_PARAM_BUFFER_BYTES, ++ fifo->hw->fifo_block); ++ if (ret) ++ return ret; ++ ++ ret = snd_pcm_hw_constraint_step(substream->runtime, 0, ++ SNDRV_PCM_HW_PARAM_PERIOD_BYTES, ++ fifo->hw->fifo_block); ++ if (ret) ++ return ret; ++ ++ ret = clk_prepare_enable(fifo->pclk); ++ if (ret) ++ return ret; ++ ++ ret = request_irq(fifo->irq, aiu_fifo_isr, 0, dev_name(dai->dev), ++ substream); ++ if (ret) ++ clk_disable_unprepare(fifo->pclk); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(aiu_fifo_startup); ++ ++void aiu_fifo_shutdown(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct snd_soc_component *component = dai->component; ++ struct aiu_fifo *fifo = snd_soc_component_get_drvdata(component); ++ ++ free_irq(fifo->irq, substream); ++ clk_disable_unprepare(fifo->pclk); ++} ++EXPORT_SYMBOL_GPL(aiu_fifo_shutdown); ++ ++const struct snd_pcm_ops aiu_fifo_pcm_ops = { ++ .ioctl = snd_pcm_lib_ioctl, ++ .pointer = aiu_fifo_pointer, ++}; ++EXPORT_SYMBOL_GPL(aiu_fifo_pcm_ops); ++ ++int aiu_fifo_pcm_new(struct snd_soc_pcm_runtime *rtd, ++ struct snd_soc_dai *dai) ++{ ++ struct snd_card *card = rtd->card->snd_card; ++ struct snd_soc_component *component = dai->component; ++ struct aiu_fifo *fifo = snd_soc_component_get_drvdata(component); ++ size_t size = fifo->hw->pcm->buffer_bytes_max; ++ ++ ++ snd_pcm_lib_preallocate_pages( ++ rtd->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream, ++ SNDRV_DMA_TYPE_DEV, card->dev, size, size); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(aiu_fifo_pcm_new); ++ ++int aiu_fifo_component_probe(struct snd_soc_component *component) ++{ ++ struct device *dev = component->dev; ++ struct regmap *map; ++ ++ map = syscon_node_to_regmap(dev->parent->of_node); ++ if (IS_ERR(map)) { ++ dev_err(dev, "Could not get regmap\n"); ++ return PTR_ERR(map); ++ } ++ ++ snd_soc_component_init_regmap(component, map); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(aiu_fifo_component_probe); ++ ++int aiu_fifo_probe(struct platform_device *pdev) ++{ ++ const struct aiu_fifo_match_data *data; ++ struct device *dev = &pdev->dev; ++ struct aiu_fifo *fifo; ++ ++ data = of_device_get_match_data(dev); ++ if (!data) { ++ dev_err(dev, "failed to match device\n"); ++ return -ENODEV; ++ } ++ ++ fifo = devm_kzalloc(dev, sizeof(*fifo), GFP_KERNEL); ++ if (!fifo) ++ return -ENOMEM; ++ platform_set_drvdata(pdev, fifo); ++ fifo->hw = data->hw; ++ ++ fifo->pclk = devm_clk_get(dev, NULL); ++ if (IS_ERR(fifo->pclk)) { ++ if (PTR_ERR(fifo->pclk) != -EPROBE_DEFER) ++ dev_err(dev, "failed to get pclk: %ld\n", ++ PTR_ERR(fifo->pclk)); ++ return PTR_ERR(fifo->pclk); ++ } ++ ++ fifo->irq = platform_get_irq(pdev, 0); ++ if (fifo->irq <= 0) { ++ dev_err(dev, "Can't get irq\n"); ++ return fifo->irq; ++ } ++ ++ return devm_snd_soc_register_component(dev, data->component_drv, ++ data->dai_drv, 1); ++} ++EXPORT_SYMBOL_GPL(aiu_fifo_probe); ++ ++MODULE_DESCRIPTION("Amlogic AIU FIFO driver"); ++MODULE_AUTHOR("Jerome Brunet "); ++MODULE_LICENSE("GPL v2"); +diff --git b/sound/soc/meson/aiu-fifo.h a/sound/soc/meson/aiu-fifo.h +new file mode 100644 +index 000000000000..eb44f013f1b8 +--- /dev/null ++++ a/sound/soc/meson/aiu-fifo.h +@@ -0,0 +1,59 @@ ++/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ ++/* ++ * Copyright (c) 2019 BayLibre, SAS. ++ * Author: Jerome Brunet ++ */ ++ ++#ifndef _MESON_AIU_FIFO_H ++#define _MESON_AIU_FIFO_H ++ ++struct snd_pcm_hardware; ++struct snd_soc_component_driver; ++struct snd_soc_dai_driver; ++struct clk; ++struct snd_pcm_ops; ++struct snd_pcm_substream; ++struct snd_soc_dai; ++struct snd_pcm_hw_params; ++struct platform_device; ++ ++struct aiu_fifo_hw { ++ struct snd_pcm_hardware *pcm; ++ unsigned int mem_offset; ++ unsigned int fifo_block; ++}; ++ ++struct aiu_fifo_match_data { ++ const struct snd_soc_component_driver *component_drv; ++ const struct aiu_fifo_hw *hw; ++ struct snd_soc_dai_driver *dai_drv; ++}; ++ ++struct aiu_fifo { ++ const struct aiu_fifo_hw *hw; ++ struct clk *pclk; ++ int irq; ++}; ++ ++extern const struct snd_pcm_ops aiu_fifo_pcm_ops; ++ ++int aiu_fifo_trigger(struct snd_pcm_substream *substream, int cmd, ++ struct snd_soc_dai *dai); ++int aiu_fifo_prepare(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai); ++int aiu_fifo_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params, ++ struct snd_soc_dai *dai); ++int aiu_fifo_hw_free(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai); ++int aiu_fifo_startup(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai); ++void aiu_fifo_shutdown(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai); ++int aiu_fifo_pcm_new(struct snd_soc_pcm_runtime *rtd, ++ struct snd_soc_dai *dai); ++ ++int aiu_fifo_component_probe(struct snd_soc_component *component); ++int aiu_fifo_probe(struct platform_device *pdev); ++ ++#endif /* _MESON_AIU_FIFO_H */ +diff --git b/sound/soc/meson/aiu-i2s-encode.c a/sound/soc/meson/aiu-i2s-encode.c +new file mode 100644 +index 000000000000..4a77fba5f799 +--- /dev/null ++++ a/sound/soc/meson/aiu-i2s-encode.c +@@ -0,0 +1,426 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// ++// Copyright (c) 2018 BayLibre, SAS. ++// Author: Jerome Brunet ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define AIU_CLK_CTRL 0x058 ++#define AIU_CLK_CTRL_I2S_DIV_EN BIT(0) ++#define AIU_CLK_CTRL_I2S_DIV GENMASK(3, 2) ++#define AIU_CLK_CTRL_AOCLK_INVERT BIT(6) ++#define AIU_CLK_CTRL_LRCLK_INVERT BIT(7) ++#define AIU_CLK_CTRL_LRCLK_SKEW GENMASK(9, 8) ++#define AIU_CLK_CTRL_MORE 0x064 ++#define AIU_CLK_CTRL_MORE_HDMI_AMCLK BIT(6) ++#define AIU_CLK_CTRL_MORE_I2S_DIV GENMASK(5, 0) ++#define AIU_I2S_SOURCE_DESC 0x034 ++#define AIU_I2S_SOURCE_DESC_MODE_8CH BIT(0) ++#define AIU_I2S_SOURCE_DESC_MODE_24BIT BIT(5) ++#define AIU_I2S_SOURCE_DESC_MODE_32BIT BIT(9) ++#define AIU_I2S_SOURCE_DESC_MODE_SPLIT BIT(11) ++#define AIU_I2S_DAC_CFG 0x040 ++#define AIU_I2S_DAC_CFG_PAYLOAD_SIZE GENMASK(1, 0) ++#define CFG_AOCLK_64 3 ++#define AIU_CODEC_DAC_LRCLK_CTRL 0x0a0 ++#define AIU_CODEC_DAC_LRCLK_CTRL_DIV GENMASK(11, 0) ++#define AIU_I2S_MISC 0x048 ++#define AIU_I2S_MISC_HOLD_EN BIT(2) ++ ++struct aiu_i2s_encode { ++ struct clk *aoclk; ++ struct clk *mclk; ++ struct clk *pclk; ++}; ++ ++static void aiu_i2s_encode_divider_enable(struct snd_soc_component *component, ++ bool enable) ++{ ++ snd_soc_component_update_bits(component, AIU_CLK_CTRL, ++ AIU_CLK_CTRL_I2S_DIV_EN, ++ enable ? AIU_CLK_CTRL_I2S_DIV_EN : 0); ++} ++ ++static void aiu_i2s_encode_hold(struct snd_soc_component *component, ++ bool enable) ++{ ++ snd_soc_component_update_bits(component, AIU_I2S_MISC, ++ AIU_I2S_MISC_HOLD_EN, ++ enable ? AIU_I2S_MISC_HOLD_EN : 0); ++} ++ ++static int aiu_i2s_encode_trigger(struct snd_pcm_substream *substream, int cmd, ++ struct snd_soc_dai *dai) ++{ ++ struct snd_soc_component *component = dai->component; ++ ++ switch (cmd) { ++ case SNDRV_PCM_TRIGGER_START: ++ case SNDRV_PCM_TRIGGER_RESUME: ++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: ++ aiu_i2s_encode_hold(component, false); ++ return 0; ++ ++ case SNDRV_PCM_TRIGGER_STOP: ++ case SNDRV_PCM_TRIGGER_SUSPEND: ++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: ++ aiu_i2s_encode_hold(component, true); ++ return 0; ++ ++ default: ++ return -EINVAL; ++ } ++} ++ ++/* Does this register/setting belong in the FIFO driver */ ++static int aiu_i2s_encode_setup_desc(struct snd_soc_component *component, ++ struct snd_pcm_hw_params *params) ++{ ++ /* Always operate in split (classic interleaved) mode */ ++ unsigned int desc = AIU_I2S_SOURCE_DESC_MODE_SPLIT; ++ ++ /* ++ * TODO: The encoder probably supports S24_3LE (24 bits ++ * physical width) formats but it is not clear how to support ++ * this in the FIFO driver so we can't test it yet ++ */ ++ switch (params_physical_width(params)) { ++ case 16: /* Nothing to do */ ++ break; ++ ++ case 32: ++ desc |= (AIU_I2S_SOURCE_DESC_MODE_24BIT | ++ AIU_I2S_SOURCE_DESC_MODE_32BIT); ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ switch (params_channels(params)) { ++ case 2: /* Nothing to do */ ++ break; ++ case 8: ++ desc |= AIU_I2S_SOURCE_DESC_MODE_8CH; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ snd_soc_component_update_bits(component, AIU_I2S_SOURCE_DESC, ++ AIU_I2S_SOURCE_DESC_MODE_8CH | ++ AIU_I2S_SOURCE_DESC_MODE_24BIT | ++ AIU_I2S_SOURCE_DESC_MODE_32BIT | ++ AIU_I2S_SOURCE_DESC_MODE_SPLIT, ++ desc); ++ ++ return 0; ++} ++ ++static int aiu_i2s_encode_set_clocks(struct snd_soc_component *component, ++ struct snd_pcm_hw_params *params) ++{ ++ struct aiu_i2s_encode *encoder = snd_soc_component_get_drvdata(component); ++ unsigned int srate = params_rate(params); ++ unsigned int fs; ++ ++ /* Get the oversampling factor */ ++ fs = DIV_ROUND_CLOSEST(clk_get_rate(encoder->mclk), srate); ++ ++ /* TODO: add support for 24 and 16 bits slot width */ ++ if (fs % 64) ++ return -EINVAL; ++ ++ /* Set the divider between bclk and lrclk to 64 */ ++ snd_soc_component_update_bits(component, AIU_I2S_DAC_CFG, ++ AIU_I2S_DAC_CFG_PAYLOAD_SIZE, ++ FIELD_PREP(AIU_I2S_DAC_CFG_PAYLOAD_SIZE, ++ CFG_AOCLK_64)); ++ ++ snd_soc_component_update_bits(component, AIU_CODEC_DAC_LRCLK_CTRL, ++ AIU_CODEC_DAC_LRCLK_CTRL_DIV, ++ FIELD_PREP(AIU_CODEC_DAC_LRCLK_CTRL_DIV, ++ 64 - 1)); ++ ++ /* Use CLK_MORE for mclk to bclk divider */ ++ snd_soc_component_update_bits(component, AIU_CLK_CTRL, ++ AIU_CLK_CTRL_I2S_DIV, 0); ++ ++ snd_soc_component_update_bits(component, AIU_CLK_CTRL_MORE, ++ AIU_CLK_CTRL_MORE_I2S_DIV, ++ FIELD_PREP(AIU_CLK_CTRL_MORE_I2S_DIV, ++ (fs / 64) - 1)); ++ ++ /* Make sure amclk is used for HDMI i2s as well */ ++ snd_soc_component_update_bits(component, AIU_CLK_CTRL_MORE, ++ AIU_CLK_CTRL_MORE_HDMI_AMCLK, ++ AIU_CLK_CTRL_MORE_HDMI_AMCLK); ++ ++ /* REMOVE ME: HARD CODED TEST */ ++ /* Set HDMI path */ ++ snd_soc_component_write(component, 0xa8, 0x22); ++ ++ /* Internal DAC Path */ ++ snd_soc_component_write(component, 0xb0, 0x8058); ++ ++ return 0; ++} ++ ++static int aiu_i2s_encode_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params, ++ struct snd_soc_dai *dai) ++{ ++ struct snd_soc_component *component = dai->component; ++ int ret; ++ ++ /* Disable the clock while changing the settings */ ++ aiu_i2s_encode_divider_enable(component, false); ++ ++ ret = aiu_i2s_encode_setup_desc(component, params); ++ if (ret) { ++ dev_err(dai->dev, "setting i2s desc failed\n"); ++ return ret; ++ } ++ ++ ret = aiu_i2s_encode_set_clocks(component, params); ++ if (ret) { ++ dev_err(dai->dev, "setting i2s clocks failed\n"); ++ return ret; ++ } ++ ++ aiu_i2s_encode_divider_enable(component, true); ++ ++ return 0; ++} ++ ++static int aiu_i2s_encode_hw_free(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct snd_soc_component *component = dai->component; ++ ++ aiu_i2s_encode_divider_enable(component, false); ++ ++ return 0; ++} ++ ++static int aiu_i2s_encode_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) ++{ ++ struct snd_soc_component *component = dai->component; ++ unsigned int inv = fmt & SND_SOC_DAIFMT_INV_MASK; ++ unsigned int val = 0; ++ unsigned int skew; ++ ++ /* Only CPU Master / Codec Slave supported ATM */ ++ if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) ++ return -EINVAL; ++ ++ if ((inv == SND_SOC_DAIFMT_NB_IF) || (inv == SND_SOC_DAIFMT_IB_IF)) ++ val |= AIU_CLK_CTRL_LRCLK_INVERT; ++ ++ if ((inv == SND_SOC_DAIFMT_IB_NF) || (inv == SND_SOC_DAIFMT_IB_IF)) ++ val |= AIU_CLK_CTRL_AOCLK_INVERT; ++ ++ /* Signal skew */ ++ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { ++ case SND_SOC_DAIFMT_I2S: ++ /* Invert sample clock for i2s */ ++ val ^= AIU_CLK_CTRL_LRCLK_INVERT; ++ skew = 1; ++ break; ++ case SND_SOC_DAIFMT_LEFT_J: ++ skew = 0; ++ break; ++ case SND_SOC_DAIFMT_RIGHT_J: ++ skew = 2; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ val |= FIELD_PREP(AIU_CLK_CTRL_LRCLK_SKEW, skew); ++ snd_soc_component_update_bits(component, AIU_CLK_CTRL, ++ AIU_CLK_CTRL_LRCLK_INVERT | ++ AIU_CLK_CTRL_AOCLK_INVERT | ++ AIU_CLK_CTRL_LRCLK_SKEW, ++ val); ++ ++ return 0; ++} ++ ++static int aiu_i2s_encode_set_sysclk(struct snd_soc_dai *dai, int clk_id, ++ unsigned int freq, int dir) ++{ ++ struct aiu_i2s_encode *encoder = snd_soc_dai_get_drvdata(dai); ++ int ret; ++ ++ if (WARN_ON(clk_id != 0)) ++ return -EINVAL; ++ ++ if (dir == SND_SOC_CLOCK_IN) ++ return 0; ++ ++ ret = clk_set_rate(encoder->mclk, freq); ++ if (ret) ++ dev_err(dai->dev, "Failed to set sysclk to %uHz", freq); ++ ++ return ret; ++} ++ ++static const unsigned int hw_channels[] = {2, 8}; ++static const struct snd_pcm_hw_constraint_list hw_channel_constraints = { ++ .list = hw_channels, ++ .count = ARRAY_SIZE(hw_channels), ++ .mask = 0, ++}; ++ ++static int aiu_i2s_encode_startup(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct aiu_i2s_encode *encoder = snd_soc_dai_get_drvdata(dai); ++ int ret; ++ ++ /* Make sure the encoder gets either 2 or 8 channels */ ++ ret = snd_pcm_hw_constraint_list(substream->runtime, 0, ++ SNDRV_PCM_HW_PARAM_CHANNELS, ++ &hw_channel_constraints); ++ if (ret) { ++ dev_err(dai->dev, "adding channels constraints failed\n"); ++ return ret; ++ } ++ ++ ret = clk_prepare_enable(encoder->pclk); ++ if (ret) ++ return ret; ++ ++ ret = clk_prepare_enable(encoder->mclk); ++ if (ret) ++ goto err_mclk; ++ ++ ret = clk_prepare_enable(encoder->aoclk); ++ if (ret) ++ goto err_aoclk; ++ ++ return 0; ++ ++err_aoclk: ++ clk_disable_unprepare(encoder->mclk); ++err_mclk: ++ clk_disable_unprepare(encoder->pclk); ++ return ret; ++} ++ ++static void aiu_i2s_encode_shutdown(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct aiu_i2s_encode *encoder = snd_soc_dai_get_drvdata(dai); ++ ++ clk_disable_unprepare(encoder->aoclk); ++ clk_disable_unprepare(encoder->mclk); ++ clk_disable_unprepare(encoder->pclk); ++} ++ ++static const struct snd_soc_dai_ops aiu_i2s_encode_dai_ops = { ++ .trigger = aiu_i2s_encode_trigger, ++ .hw_params = aiu_i2s_encode_hw_params, ++ .hw_free = aiu_i2s_encode_hw_free, ++ .set_fmt = aiu_i2s_encode_set_fmt, ++ .set_sysclk = aiu_i2s_encode_set_sysclk, ++ .startup = aiu_i2s_encode_startup, ++ .shutdown = aiu_i2s_encode_shutdown, ++}; ++ ++static struct snd_soc_dai_driver aiu_i2s_encode_dai_drv = { ++ .playback = { ++ .stream_name = "Playback", ++ .channels_min = 2, ++ .channels_max = 8, ++ .rates = SNDRV_PCM_RATE_8000_192000, ++ .formats = (SNDRV_PCM_FMTBIT_S16_LE | ++ SNDRV_PCM_FMTBIT_S20_LE | ++ SNDRV_PCM_FMTBIT_S24_LE | ++ SNDRV_PCM_FMTBIT_S32_LE) ++ }, ++ .ops = &aiu_i2s_encode_dai_ops, ++}; ++ ++int aiu_i2s_encode_component_probe(struct snd_soc_component *component) ++{ ++ struct device *dev = component->dev; ++ struct regmap *map; ++ ++ map = syscon_node_to_regmap(dev->parent->of_node); ++ if (IS_ERR(map)) { ++ dev_err(dev, "Could not get regmap\n"); ++ return PTR_ERR(map); ++ } ++ ++ snd_soc_component_init_regmap(component, map); ++ ++ return 0; ++} ++ ++static const struct snd_soc_component_driver aiu_i2s_encode_component = { ++ .probe = aiu_i2s_encode_component_probe, ++}; ++ ++static int aiu_i2s_encode_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct aiu_i2s_encode *encoder; ++ ++ encoder = devm_kzalloc(dev, sizeof(*encoder), GFP_KERNEL); ++ if (!encoder) ++ return -ENOMEM; ++ platform_set_drvdata(pdev, encoder); ++ ++ encoder->pclk = devm_clk_get(dev, "pclk"); ++ if (IS_ERR(encoder->pclk)) { ++ if (PTR_ERR(encoder->pclk) != -EPROBE_DEFER) ++ dev_err(dev, ++ "Can't get the peripheral clock\n"); ++ return PTR_ERR(encoder->pclk); ++ } ++ ++ encoder->aoclk = devm_clk_get(dev, "aoclk"); ++ if (IS_ERR(encoder->aoclk)) { ++ if (PTR_ERR(encoder->aoclk) != -EPROBE_DEFER) ++ dev_err(dev, "Can't get the ao clock\n"); ++ return PTR_ERR(encoder->aoclk); ++ } ++ ++ encoder->mclk = devm_clk_get(dev, "mclk"); ++ if (IS_ERR(encoder->mclk)) { ++ if (PTR_ERR(encoder->mclk) != -EPROBE_DEFER) ++ dev_err(dev, "Can't get the i2s m\n"); ++ return PTR_ERR(encoder->mclk); ++ } ++ ++ return devm_snd_soc_register_component(dev, &aiu_i2s_encode_component, ++ &aiu_i2s_encode_dai_drv, 1); ++} ++ ++static const struct of_device_id aiu_i2s_encode_of_match[] = { ++ { .compatible = "amlogic,aiu-i2s-encode", }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, aiu_i2s_encode_of_match); ++ ++static struct platform_driver aiu_i2s_encode_pdrv = { ++ .probe = aiu_i2s_encode_probe, ++ .driver = { ++ .name = "meson-aiu-i2s-encode", ++ .of_match_table = aiu_i2s_encode_of_match, ++ }, ++}; ++module_platform_driver(aiu_i2s_encode_pdrv); ++ ++MODULE_DESCRIPTION("Meson AIU I2S Encode Driver"); ++MODULE_AUTHOR("Jerome Brunet "); ++MODULE_LICENSE("GPL v2"); +diff --git b/sound/soc/meson/aiu-i2s-fifo.c a/sound/soc/meson/aiu-i2s-fifo.c +new file mode 100644 +index 000000000000..7a9aa1439a60 +--- /dev/null ++++ a/sound/soc/meson/aiu-i2s-fifo.c +@@ -0,0 +1,169 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// ++// Copyright (c) 2019 BayLibre, SAS. ++// Author: Jerome Brunet ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "aiu-fifo.h" ++ ++#define AIU_MEM_I2S_START 0x180 ++#define AIU_MEM_I2S_MASKS 0x18c ++#define AIU_MEM_I2S_MASKS_IRQ_BLOCK GENMASK(31, 16) ++#define AIU_MEM_I2S_CONTROL 0x190 ++#define AIU_MEM_I2S_CONTROL_MODE_16BIT BIT(6) ++#define AIU_MEM_I2S_BUF_CNTL 0x1d8 ++#define AIU_MEM_I2S_BUF_CNTL_INIT BIT(0) ++ ++#define AIU_I2S_FIFO_BLOCK 256 ++#define AIU_I2S_FIFO_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ ++ SNDRV_PCM_FMTBIT_S20_LE | \ ++ SNDRV_PCM_FMTBIT_S24_LE | \ ++ SNDRV_PCM_FMTBIT_S32_LE) ++ ++static int i2s_fifo_prepare(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct snd_soc_component *component = dai->component; ++ int ret; ++ ++ ret = aiu_fifo_prepare(substream, dai); ++ if (ret) ++ return ret; ++ ++ snd_soc_component_update_bits(component, ++ AIU_MEM_I2S_BUF_CNTL, ++ AIU_MEM_I2S_BUF_CNTL_INIT, ++ AIU_MEM_I2S_BUF_CNTL_INIT); ++ snd_soc_component_update_bits(component, ++ AIU_MEM_I2S_BUF_CNTL, ++ AIU_MEM_I2S_BUF_CNTL_INIT, 0); ++ ++ return 0; ++} ++ ++static int i2s_fifo_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params, ++ struct snd_soc_dai *dai) ++{ ++ struct snd_soc_component *component = dai->component; ++ struct aiu_fifo *fifo = snd_soc_component_get_drvdata(component); ++ unsigned int val; ++ int ret; ++ ++ ret = aiu_fifo_hw_params(substream, params, dai); ++ if (ret) ++ return ret; ++ ++ switch (params_physical_width(params)) { ++ case 16: ++ val = AIU_MEM_I2S_CONTROL_MODE_16BIT; ++ break; ++ case 32: ++ val = 0; ++ break; ++ default: ++ dev_err(dai->dev, "Unsupported physical width %u\n", ++ params_physical_width(params)); ++ return -EINVAL; ++ } ++ ++ snd_soc_component_update_bits(component, AIU_MEM_I2S_CONTROL, ++ AIU_MEM_I2S_CONTROL_MODE_16BIT, ++ val); ++ ++ /* Setup the irq periodicity */ ++ val = params_period_bytes(params) / fifo->hw->fifo_block; ++ val = FIELD_PREP(AIU_MEM_I2S_MASKS_IRQ_BLOCK, val); ++ snd_soc_component_update_bits(component, AIU_MEM_I2S_MASKS, ++ AIU_MEM_I2S_MASKS_IRQ_BLOCK, val); ++ ++ return 0; ++} ++ ++static const struct snd_soc_dai_ops i2s_fifo_dai_ops = { ++ .trigger = aiu_fifo_trigger, ++ .prepare = i2s_fifo_prepare, ++ .hw_params = i2s_fifo_hw_params, ++ .hw_free = aiu_fifo_hw_free, ++ .startup = aiu_fifo_startup, ++ .shutdown = aiu_fifo_shutdown, ++}; ++ ++static struct snd_soc_dai_driver i2s_fifo_dai_drv = { ++ .name = "AIU I2S FIFO", ++ .playback = { ++ .stream_name = "Playback", ++ .channels_min = 2, ++ .channels_max = 8, ++ .rates = SNDRV_PCM_RATE_CONTINUOUS, ++ .rate_min = 5512, ++ .rate_max = 192000, ++ .formats = AIU_I2S_FIFO_FORMATS, ++ }, ++ .ops = &i2s_fifo_dai_ops, ++ .pcm_new = aiu_fifo_pcm_new, ++}; ++ ++static const struct snd_soc_component_driver i2s_fifo_component_drv = { ++ .probe = aiu_fifo_component_probe, ++ .ops = &aiu_fifo_pcm_ops, ++}; ++ ++static struct snd_pcm_hardware i2s_fifo_pcm = { ++ .info = (SNDRV_PCM_INFO_INTERLEAVED | ++ SNDRV_PCM_INFO_MMAP | ++ SNDRV_PCM_INFO_MMAP_VALID | ++ SNDRV_PCM_INFO_PAUSE), ++ .formats = AIU_I2S_FIFO_FORMATS, ++ .rate_min = 5512, ++ .rate_max = 192000, ++ .channels_min = 2, ++ .channels_max = 8, ++ .period_bytes_min = AIU_I2S_FIFO_BLOCK, ++ .period_bytes_max = AIU_I2S_FIFO_BLOCK * USHRT_MAX, ++ .periods_min = 2, ++ .periods_max = UINT_MAX, ++ ++ /* No real justification for this */ ++ .buffer_bytes_max = 1 * 1024 * 1024, ++}; ++ ++static const struct aiu_fifo_hw i2s_fifo_hw = { ++ .fifo_block = AIU_I2S_FIFO_BLOCK, ++ .mem_offset = AIU_MEM_I2S_START, ++ .pcm = &i2s_fifo_pcm, ++}; ++ ++static const struct aiu_fifo_match_data i2s_fifo_data = { ++ .component_drv = &i2s_fifo_component_drv, ++ .dai_drv = &i2s_fifo_dai_drv, ++ .hw = &i2s_fifo_hw, ++}; ++ ++static const struct of_device_id aiu_i2s_fifo_of_match[] = { ++ { ++ .compatible = "amlogic,aiu-i2s-fifo", ++ .data = &i2s_fifo_data, ++ }, {} ++}; ++MODULE_DEVICE_TABLE(of, aiu_i2s_fifo_of_match); ++ ++static struct platform_driver aiu_i2s_fifo_pdrv = { ++ .probe = aiu_fifo_probe, ++ .driver = { ++ .name = "meson-aiu-i2s-fifo", ++ .of_match_table = aiu_i2s_fifo_of_match, ++ }, ++}; ++module_platform_driver(aiu_i2s_fifo_pdrv); ++ ++MODULE_DESCRIPTION("Amlogic AIU I2S FIFO driver"); ++MODULE_AUTHOR("Jerome Brunet "); ++MODULE_LICENSE("GPL v2"); +diff --git b/sound/soc/meson/aiu-spdif-encode.c a/sound/soc/meson/aiu-spdif-encode.c +new file mode 100644 +index 000000000000..92b101117fd7 +--- /dev/null ++++ a/sound/soc/meson/aiu-spdif-encode.c +@@ -0,0 +1,348 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// ++// Copyright (c) 2018 BayLibre, SAS. ++// Author: Jerome Brunet ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define AIU_958_MISC 0x010 ++#define AIU_958_MISC_NON_PCM BIT(0) ++#define AIU_958_MISC_MODE_16BITS BIT(1) ++#define AIU_958_MISC_16BITS_ALIGN GENMASK(6, 5) ++#define AIU_958_MISC_MODE_32BITS BIT(7) ++#define AIU_958_MISC_U_FROM_STREAM BIT(12) ++#define AIU_958_MISC_FORCE_LR BIT(13) ++#define AIU_958_CHSTAT_L0 0x020 ++#define AIU_958_CHSTAT_L1 0x024 ++#define AIU_958_CTRL 0x028 ++#define AIU_958_CTRL_HOLD_EN BIT(0) ++#define AIU_I2S_MISC 0x048 ++#define AIU_I2S_MISC_958_SRC_SHIFT 3 ++#define AIU_CLK_CTRL 0x058 ++#define AIU_CLK_CTRL_958_DIV_EN BIT(1) ++#define AIU_CLK_CTRL_958_DIV GENMASK(5, 4) ++#define AIU_CLK_CTRL_958_DIV_MORE BIT(12) ++#define AIU_958_CHSTAT_R0 0x0c0 ++#define AIU_958_CHSTAT_R1 0x0c4 ++ ++#define AIU_CS_WORD_LEN 4 ++#define AIU_958_INTERNAL_DIV 2 ++ ++struct aiu_spdif_encode { ++ struct clk *mclk_i958; ++ struct clk *mclk_i2s; ++ struct clk *mclk; ++ struct clk *pclk; ++}; ++ ++static void aiu_spdif_encode_divider_enable(struct snd_soc_component *component, ++ bool enable) ++{ ++ snd_soc_component_update_bits(component, AIU_CLK_CTRL, ++ AIU_CLK_CTRL_958_DIV_EN, ++ enable ? AIU_CLK_CTRL_958_DIV_EN : 0); ++} ++ ++static void aiu_spdif_encode_hold(struct snd_soc_component *component, ++ bool enable) ++{ ++ snd_soc_component_update_bits(component, AIU_958_CTRL, ++ AIU_958_CTRL_HOLD_EN, ++ enable ? AIU_958_CTRL_HOLD_EN : 0); ++} ++ ++static int aiu_spdif_encode_trigger(struct snd_pcm_substream *substream, int cmd, ++ struct snd_soc_dai *dai) ++{ ++ struct snd_soc_component *component = dai->component; ++ ++ switch (cmd) { ++ case SNDRV_PCM_TRIGGER_START: ++ case SNDRV_PCM_TRIGGER_RESUME: ++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: ++ aiu_spdif_encode_hold(component, false); ++ return 0; ++ ++ case SNDRV_PCM_TRIGGER_STOP: ++ case SNDRV_PCM_TRIGGER_SUSPEND: ++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: ++ aiu_spdif_encode_hold(component, true); ++ return 0; ++ ++ default: ++ return -EINVAL; ++ } ++} ++ ++static int aiu_spdif_encode_setup_cs_word(struct snd_soc_component *component, ++ struct snd_pcm_hw_params *params) ++{ ++ u8 cs[AIU_CS_WORD_LEN]; ++ unsigned int val; ++ int ret; ++ ++ ret = snd_pcm_create_iec958_consumer_hw_params(params, cs, ++ AIU_CS_WORD_LEN); ++ if (ret < 0) ++ return ret; ++ ++ /* Write the 1st half word */ ++ val = cs[1] | cs[0] << 8; ++ snd_soc_component_write(component, AIU_958_CHSTAT_L0, val); ++ snd_soc_component_write(component, AIU_958_CHSTAT_R0, val); ++ ++ /* Write the 2nd half word */ ++ val = cs[3] | cs[2] << 8; ++ snd_soc_component_write(component, AIU_958_CHSTAT_L1, val); ++ snd_soc_component_write(component, AIU_958_CHSTAT_R1, val); ++ ++ return 0; ++} ++ ++static int aiu_spdif_encode_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params, ++ struct snd_soc_dai *dai) ++{ ++ struct snd_soc_component *component = dai->component; ++ struct aiu_spdif_encode *encoder = snd_soc_dai_get_drvdata(dai); ++ unsigned int val = 0, mrate; ++ int ret; ++ ++ /* Disable the clock while changing the settings */ ++ aiu_spdif_encode_divider_enable(component, false); ++ ++ switch (params_physical_width(params)) { ++ case 16: ++ val |= AIU_958_MISC_MODE_16BITS; ++ val |= FIELD_PREP(AIU_958_MISC_16BITS_ALIGN, 2); ++ break; ++ case 32: ++ val |= AIU_958_MISC_MODE_32BITS; ++ break; ++ default: ++ dev_err(dai->dev, "Unsupport physical width\n"); ++ return -EINVAL; ++ } ++ ++ snd_soc_component_update_bits(component, AIU_958_MISC, ++ AIU_958_MISC_NON_PCM | ++ AIU_958_MISC_MODE_16BITS | ++ AIU_958_MISC_16BITS_ALIGN | ++ AIU_958_MISC_MODE_32BITS | ++ AIU_958_MISC_FORCE_LR | ++ AIU_958_MISC_U_FROM_STREAM, ++ val); ++ ++ /* Set the stream channel status word */ ++ ret = aiu_spdif_encode_setup_cs_word(component, params); ++ if (ret) { ++ dev_err(dai->dev, "failed to set channel status word\n"); ++ return ret; ++ } ++ ++ snd_soc_component_update_bits(component, AIU_CLK_CTRL, ++ AIU_CLK_CTRL_958_DIV | ++ AIU_CLK_CTRL_958_DIV_MORE, ++ FIELD_PREP(AIU_CLK_CTRL_958_DIV, ++ __ffs(AIU_958_INTERNAL_DIV))); ++ ++ /* 2 * 32bits per subframe * 2 channels = 128 */ ++ mrate = params_rate(params) * 128 * AIU_958_INTERNAL_DIV; ++ ret = clk_set_rate(encoder->mclk, mrate); ++ if (ret) { ++ dev_err(dai->dev, "failed to set mclk rate\n"); ++ return ret; ++ } ++ ++ aiu_spdif_encode_divider_enable(component, true); ++ ++ return 0; ++} ++ ++static int aiu_spdif_encode_hw_free(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct snd_soc_component *component = dai->component; ++ ++ aiu_spdif_encode_divider_enable(component, false); ++ ++ return 0; ++} ++ ++static int aiu_spdif_encode_startup(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct aiu_spdif_encode *encoder = snd_soc_dai_get_drvdata(dai); ++ int ret; ++ ++ /* make sure the spdif block is on its own divider */ ++ ret = clk_set_parent(encoder->mclk, encoder->mclk_i958); ++ if (ret) ++ return ret; ++ ++ ret = clk_prepare_enable(encoder->pclk); ++ if (ret) ++ return ret; ++ ++ ret = clk_prepare_enable(encoder->mclk); ++ if (ret) ++ clk_disable_unprepare(encoder->pclk); ++ ++ return ret; ++} ++ ++static void aiu_spdif_encode_shutdown(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct aiu_spdif_encode *encoder = snd_soc_dai_get_drvdata(dai); ++ ++ clk_disable_unprepare(encoder->mclk); ++ clk_disable_unprepare(encoder->pclk); ++} ++ ++static const struct snd_soc_dai_ops aiu_spdif_encode_dai_ops = { ++ .trigger = aiu_spdif_encode_trigger, ++ .hw_params = aiu_spdif_encode_hw_params, ++ .hw_free = aiu_spdif_encode_hw_free, ++ .startup = aiu_spdif_encode_startup, ++ .shutdown = aiu_spdif_encode_shutdown, ++}; ++ ++static struct snd_soc_dai_driver aiu_spdif_encode_dai_drv = { ++ .playback = { ++ .stream_name = "Playback", ++ .channels_min = 2, ++ .channels_max = 2, ++ .rates = (SNDRV_PCM_RATE_32000 | ++ SNDRV_PCM_RATE_44100 | ++ SNDRV_PCM_RATE_48000 | ++ SNDRV_PCM_RATE_88200 | ++ SNDRV_PCM_RATE_96000 | ++ SNDRV_PCM_RATE_176400 | ++ SNDRV_PCM_RATE_192000), ++ .formats = (SNDRV_PCM_FMTBIT_S16_LE | ++ SNDRV_PCM_FMTBIT_S20_LE | ++ SNDRV_PCM_FMTBIT_S24_LE), ++ }, ++ .ops = &aiu_spdif_encode_dai_ops, ++}; ++ ++int aiu_spdif_encode_component_probe(struct snd_soc_component *component) ++{ ++ struct device *dev = component->dev; ++ struct regmap *map; ++ ++ map = syscon_node_to_regmap(dev->parent->of_node); ++ if (IS_ERR(map)) { ++ dev_err(dev, "Could not get regmap\n"); ++ return PTR_ERR(map); ++ } ++ ++ snd_soc_component_init_regmap(component, map); ++ ++ return 0; ++} ++ ++static const char * const aiu_spdif_encode_sel_texts[] = { ++ "SPDIF", "I2S", ++}; ++ ++static SOC_ENUM_SINGLE_DECL(aiu_spdif_encode_sel_enum, AIU_I2S_MISC, ++ AIU_I2S_MISC_958_SRC_SHIFT, ++ aiu_spdif_encode_sel_texts); ++ ++static const struct snd_kcontrol_new aiu_spdif_encode_mux = ++ SOC_DAPM_ENUM("Buffer Src", aiu_spdif_encode_sel_enum); ++ ++static const struct snd_soc_dapm_widget aiu_spdif_encode_dapm_widgets[] = { ++ SND_SOC_DAPM_AIF_IN("SPDIF IN", NULL, 0, SND_SOC_NOPM, 0, 0), ++ SND_SOC_DAPM_AIF_IN("I2S IN", NULL, 0, SND_SOC_NOPM, 0, 0), ++ SND_SOC_DAPM_MUX("SRC SEL", SND_SOC_NOPM, 0, 0, &aiu_spdif_encode_mux), ++}; ++ ++static const struct snd_soc_dapm_route aiu_spdif_encode_dapm_routes[] = { ++ { "SRC SEL", "SPDIF", "SPDIF IN" }, ++ { "SRC SEL", "I2S", "I2S IN" }, ++ { "Playback", NULL, "SRC SEL" }, ++}; ++ ++static const struct snd_soc_component_driver aiu_spdif_encode_component = { ++ .dapm_widgets = aiu_spdif_encode_dapm_widgets, ++ .num_dapm_widgets = ARRAY_SIZE(aiu_spdif_encode_dapm_widgets), ++ .dapm_routes = aiu_spdif_encode_dapm_routes, ++ .num_dapm_routes = ARRAY_SIZE(aiu_spdif_encode_dapm_routes), ++ .probe = aiu_spdif_encode_component_probe, ++}; ++ ++static int aiu_spdif_encode_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct aiu_spdif_encode *encoder; ++ ++ encoder = devm_kzalloc(dev, sizeof(*encoder), GFP_KERNEL); ++ if (!encoder) ++ return -ENOMEM; ++ platform_set_drvdata(pdev, encoder); ++ ++ encoder->pclk = devm_clk_get(dev, "pclk"); ++ if (IS_ERR(encoder->pclk)) { ++ if (PTR_ERR(encoder->pclk) != -EPROBE_DEFER) ++ dev_err(dev, ++ "Can't get the dai clock gate\n"); ++ return PTR_ERR(encoder->pclk); ++ } ++ ++ encoder->mclk_i958 = devm_clk_get(dev, "mclk_i958"); ++ if (IS_ERR(encoder->mclk_i958)) { ++ if (PTR_ERR(encoder->mclk_i958) != -EPROBE_DEFER) ++ dev_err(dev, "Can't get the spdif master clock\n"); ++ return PTR_ERR(encoder->mclk_i958); ++ } ++ ++ /* ++ * NOTE: the spdif can be clock by i2s master clock or its own clock, ++ * We should (maybe) change the source depending on the origin of the ++ * data. ++ * However, considering the clocking scheme used on these platforms, ++ * the master clocks should pick the same PLL source when they are ++ * playing from the same FIFO. The clock should be in sync so, it ++ * should not be necessary to reparent the spdif master clock. ++ */ ++ ++ encoder->mclk = devm_clk_get(dev, "mclk"); ++ if (IS_ERR(encoder->mclk)) { ++ if (PTR_ERR(encoder->mclk) != -EPROBE_DEFER) ++ dev_err(dev, "Can't get the spdif input mux clock\n"); ++ return PTR_ERR(encoder->mclk); ++ } ++ ++ return devm_snd_soc_register_component(dev, &aiu_spdif_encode_component, ++ &aiu_spdif_encode_dai_drv, 1); ++} ++ ++static const struct of_device_id aiu_spdif_encode_of_match[] = { ++ { .compatible = "amlogic,aiu-spdif-encode", }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, aiu_spdif_encode_of_match); ++ ++static struct platform_driver aiu_spdif_encode_pdrv = { ++ .probe = aiu_spdif_encode_probe, ++ .driver = { ++ .name = "meson-aiu-spdif-encode", ++ .of_match_table = aiu_spdif_encode_of_match, ++ }, ++}; ++module_platform_driver(aiu_spdif_encode_pdrv); ++ ++MODULE_DESCRIPTION("Meson AIU SPDIF Encode Driver"); ++MODULE_AUTHOR("Jerome Brunet "); ++MODULE_LICENSE("GPL v2"); +diff --git b/sound/soc/meson/aiu-spdif-fifo.c a/sound/soc/meson/aiu-spdif-fifo.c +new file mode 100644 +index 000000000000..8d34ca51c6d0 +--- /dev/null ++++ a/sound/soc/meson/aiu-spdif-fifo.c +@@ -0,0 +1,226 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// ++// Copyright (c) 2019 BayLibre, SAS. ++// Author: Jerome Brunet ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "aiu-fifo.h" ++ ++#define AIU_IEC958_BPF 0x000 ++#define AIU_IEC958_DCU_FF_CTRL 0x01c ++#define AIU_IEC958_DCU_FF_CTRL_EN BIT(0) ++#define AIU_IEC958_DCU_FF_CTRL_AUTO_DISABLE BIT(1) ++#define AIU_IEC958_DCU_FF_CTRL_IRQ_MODE GENMASK(3, 2) ++#define AIU_IEC958_DCU_FF_CTRL_IRQ_OUT_THD BIT(2) ++#define AIU_IEC958_DCU_FF_CTRL_IRQ_FRAME_READ BIT(3) ++#define AIU_IEC958_DCU_FF_CTRL_SYNC_HEAD_EN BIT(4) ++#define AIU_IEC958_DCU_FF_CTRL_BYTE_SEEK BIT(5) ++#define AIU_IEC958_DCU_FF_CTRL_CONTINUE BIT(6) ++#define AIU_MEM_IEC958_START 0x194 ++#define AIU_MEM_IEC958_CONTROL 0x1a4 ++#define AIU_MEM_IEC958_CONTROL_ENDIAN GENMASK(5, 3) ++#define AIU_MEM_IEC958_CONTROL_RD_DDR BIT(6) ++#define AIU_MEM_IEC958_CONTROL_MODE_16BIT BIT(7) ++#define AIU_MEM_IEC958_CONTROL_MODE_LINEAR BIT(8) ++#define AIU_MEM_IEC958_BUF_CNTL 0x1fc ++#define AIU_MEM_IEC958_BUF_CNTL_INIT BIT(0) ++ ++#define AIU_SPDIF_FIFO_BLOCK 8 ++#define AIU_SPDIF_FIFO_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ ++ SNDRV_PCM_FMTBIT_S20_LE | \ ++ SNDRV_PCM_FMTBIT_S24_LE) ++ ++static void spdif_fifo_dcu_enable(struct snd_soc_component *component, ++ bool enable) ++{ ++ snd_soc_component_update_bits(component, AIU_IEC958_DCU_FF_CTRL, ++ AIU_IEC958_DCU_FF_CTRL_EN, ++ enable ? AIU_IEC958_DCU_FF_CTRL_EN : 0); ++} ++ ++static int spdif_fifo_trigger(struct snd_pcm_substream *substream, int cmd, ++ struct snd_soc_dai *dai) ++{ ++ struct snd_soc_component *component = dai->component; ++ int ret; ++ ++ ret = aiu_fifo_trigger(substream, cmd, dai); ++ if (ret) ++ return ret; ++ ++ switch (cmd) { ++ case SNDRV_PCM_TRIGGER_START: ++ case SNDRV_PCM_TRIGGER_RESUME: ++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: ++ spdif_fifo_dcu_enable(component, true); ++ break; ++ case SNDRV_PCM_TRIGGER_SUSPEND: ++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: ++ case SNDRV_PCM_TRIGGER_STOP: ++ spdif_fifo_dcu_enable(component, false); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int spdif_fifo_prepare(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct snd_soc_component *component = dai->component; ++ int ret; ++ ++ ret = aiu_fifo_prepare(substream, dai); ++ if (ret) ++ return ret; ++ ++ snd_soc_component_update_bits(component, ++ AIU_MEM_IEC958_BUF_CNTL, ++ AIU_MEM_IEC958_BUF_CNTL_INIT, ++ AIU_MEM_IEC958_BUF_CNTL_INIT); ++ snd_soc_component_update_bits(component, ++ AIU_MEM_IEC958_BUF_CNTL, ++ AIU_MEM_IEC958_BUF_CNTL_INIT, 0); ++ ++ return 0; ++} ++ ++static int spdif_fifo_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params, ++ struct snd_soc_dai *dai) ++{ ++ struct snd_soc_component *component = dai->component; ++ unsigned int val; ++ int ret; ++ ++ ret = aiu_fifo_hw_params(substream, params, dai); ++ if (ret) ++ return ret; ++ ++ val = AIU_MEM_IEC958_CONTROL_RD_DDR | ++ AIU_MEM_IEC958_CONTROL_MODE_LINEAR; ++ ++ switch (params_physical_width(params)) { ++ case 16: ++ val |= AIU_MEM_IEC958_CONTROL_MODE_16BIT; ++ break; ++ case 32: ++ break; ++ default: ++ dev_err(dai->dev, "Unsupported physical width %u\n", ++ params_physical_width(params)); ++ return -EINVAL; ++ } ++ ++ snd_soc_component_update_bits(component, AIU_MEM_IEC958_CONTROL, ++ AIU_MEM_IEC958_CONTROL_ENDIAN | ++ AIU_MEM_IEC958_CONTROL_RD_DDR | ++ AIU_MEM_IEC958_CONTROL_MODE_LINEAR | ++ AIU_MEM_IEC958_CONTROL_MODE_16BIT, ++ val); ++ ++ /* Number bytes read by the FIFO between each IRQ */ ++ snd_soc_component_write(component, AIU_IEC958_BPF, ++ params_period_bytes(params)); ++ ++ /* ++ * AUTO_DISABLE and SYNC_HEAD are enabled by default but ++ * this should be disabled in PCM (uncompressed) mode ++ */ ++ snd_soc_component_update_bits(component, AIU_IEC958_DCU_FF_CTRL, ++ AIU_IEC958_DCU_FF_CTRL_AUTO_DISABLE | ++ AIU_IEC958_DCU_FF_CTRL_IRQ_MODE | ++ AIU_IEC958_DCU_FF_CTRL_SYNC_HEAD_EN, ++ AIU_IEC958_DCU_FF_CTRL_IRQ_FRAME_READ); ++ ++ return 0; ++} ++ ++static const struct snd_soc_dai_ops spdif_fifo_dai_ops = { ++ .trigger = spdif_fifo_trigger, ++ .prepare = spdif_fifo_prepare, ++ .hw_params = spdif_fifo_hw_params, ++ .hw_free = aiu_fifo_hw_free, ++ .startup = aiu_fifo_startup, ++ .shutdown = aiu_fifo_shutdown, ++}; ++ ++static struct snd_soc_dai_driver spdif_fifo_dai_drv = { ++ .name = "AIU SPDIF FIFO", ++ .playback = { ++ .stream_name = "Playback", ++ .channels_min = 2, ++ .channels_max = 2, ++ .rates = SNDRV_PCM_RATE_CONTINUOUS, ++ .rate_min = 5512, ++ .rate_max = 192000, ++ .formats = AIU_SPDIF_FIFO_FORMATS, ++ }, ++ .ops = &spdif_fifo_dai_ops, ++ .pcm_new = aiu_fifo_pcm_new, ++}; ++ ++static const struct snd_soc_component_driver spdif_fifo_component_drv = { ++ .probe = aiu_fifo_component_probe, ++ .ops = &aiu_fifo_pcm_ops, ++}; ++ ++static struct snd_pcm_hardware spdif_fifo_pcm = { ++ .info = (SNDRV_PCM_INFO_INTERLEAVED | ++ SNDRV_PCM_INFO_MMAP | ++ SNDRV_PCM_INFO_MMAP_VALID | ++ SNDRV_PCM_INFO_PAUSE), ++ .formats = AIU_SPDIF_FIFO_FORMATS, ++ .rate_min = 5512, ++ .rate_max = 192000, ++ .channels_min = 2, ++ .channels_max = 2, ++ .period_bytes_min = AIU_SPDIF_FIFO_BLOCK, ++ .period_bytes_max = AIU_SPDIF_FIFO_BLOCK * USHRT_MAX, ++ .periods_min = 2, ++ .periods_max = UINT_MAX, ++ ++ /* No real justification for this */ ++ .buffer_bytes_max = 1 * 1024 * 1024, ++}; ++ ++static const struct aiu_fifo_hw spdif_fifo_hw = { ++ .fifo_block = /*AIU_SPDIF_FIFO_BLOCK*/ 1, ++ .mem_offset = AIU_MEM_IEC958_START, ++ .pcm = &spdif_fifo_pcm, ++}; ++ ++static const struct aiu_fifo_match_data spdif_fifo_data = { ++ .component_drv = &spdif_fifo_component_drv, ++ .dai_drv = &spdif_fifo_dai_drv, ++ .hw = &spdif_fifo_hw, ++}; ++ ++static const struct of_device_id aiu_spdif_fifo_of_match[] = { ++ { ++ .compatible = "amlogic,aiu-spdif-fifo", ++ .data = &spdif_fifo_data, ++ }, {} ++}; ++MODULE_DEVICE_TABLE(of, aiu_spdif_fifo_of_match); ++ ++static struct platform_driver aiu_spdif_fifo_pdrv = { ++ .probe = aiu_fifo_probe, ++ .driver = { ++ .name = "meson-aiu-spdif-fifo", ++ .of_match_table = aiu_spdif_fifo_of_match, ++ }, ++}; ++module_platform_driver(aiu_spdif_fifo_pdrv); ++ ++MODULE_DESCRIPTION("Amlogic AIU SPDIF FIFO driver"); ++MODULE_AUTHOR("Jerome Brunet "); ++MODULE_LICENSE("GPL v2"); +diff --git b/sound/soc/meson/axg-card.c a/sound/soc/meson/axg-card.c +index 1f698adde506..ebf058d0651e 100644 +--- b/sound/soc/meson/axg-card.c ++++ a/sound/soc/meson/axg-card.c +@@ -9,11 +9,7 @@ + #include + + #include "axg-tdm.h" +- +-struct axg_card { +- struct snd_soc_card card; +- void **link_data; +-}; ++#include "meson-card.h" + + struct axg_dai_link_tdm_mask { + u32 tx; +@@ -41,161 +37,15 @@ static const struct snd_soc_pcm_stream codec_params = { + .channels_max = 8, + }; + +-#define PREFIX "amlogic," +- +-static int axg_card_reallocate_links(struct axg_card *priv, +- unsigned int num_links) +-{ +- struct snd_soc_dai_link *links; +- void **ldata; +- +- links = krealloc(priv->card.dai_link, +- num_links * sizeof(*priv->card.dai_link), +- GFP_KERNEL | __GFP_ZERO); +- ldata = krealloc(priv->link_data, +- num_links * sizeof(*priv->link_data), +- GFP_KERNEL | __GFP_ZERO); +- +- if (!links || !ldata) { +- dev_err(priv->card.dev, "failed to allocate links\n"); +- return -ENOMEM; +- } +- +- priv->card.dai_link = links; +- priv->link_data = ldata; +- priv->card.num_links = num_links; +- return 0; +-} +- +-static int axg_card_parse_dai(struct snd_soc_card *card, +- struct device_node *node, +- struct device_node **dai_of_node, +- const char **dai_name) +-{ +- struct of_phandle_args args; +- int ret; +- +- if (!dai_name || !dai_of_node || !node) +- return -EINVAL; +- +- ret = of_parse_phandle_with_args(node, "sound-dai", +- "#sound-dai-cells", 0, &args); +- if (ret) { +- if (ret != -EPROBE_DEFER) +- dev_err(card->dev, "can't parse dai %d\n", ret); +- return ret; +- } +- *dai_of_node = args.np; +- +- return snd_soc_get_dai_name(&args, dai_name); +-} +- +-static int axg_card_set_link_name(struct snd_soc_card *card, +- struct snd_soc_dai_link *link, +- struct device_node *node, +- const char *prefix) +-{ +- char *name = devm_kasprintf(card->dev, GFP_KERNEL, "%s.%s", +- prefix, node->full_name); +- if (!name) +- return -ENOMEM; +- +- link->name = name; +- link->stream_name = name; +- +- return 0; +-} +- +-static void axg_card_clean_references(struct axg_card *priv) +-{ +- struct snd_soc_card *card = &priv->card; +- struct snd_soc_dai_link *link; +- struct snd_soc_dai_link_component *codec; +- struct snd_soc_aux_dev *aux; +- int i, j; +- +- if (card->dai_link) { +- for_each_card_prelinks(card, i, link) { +- if (link->cpus) +- of_node_put(link->cpus->of_node); +- for_each_link_codecs(link, j, codec) +- of_node_put(codec->of_node); +- } +- } +- +- if (card->aux_dev) { +- for_each_card_pre_auxs(card, i, aux) +- of_node_put(aux->dlc.of_node); +- } +- +- kfree(card->dai_link); +- kfree(priv->link_data); +-} +- +-static int axg_card_add_aux_devices(struct snd_soc_card *card) +-{ +- struct device_node *node = card->dev->of_node; +- struct snd_soc_aux_dev *aux; +- int num, i; +- +- num = of_count_phandle_with_args(node, "audio-aux-devs", NULL); +- if (num == -ENOENT) { +- /* +- * It is ok to have no auxiliary devices but for this card it +- * is a strange situtation. Let's warn the about it. +- */ +- dev_warn(card->dev, "card has no auxiliary devices\n"); +- return 0; +- } else if (num < 0) { +- dev_err(card->dev, "error getting auxiliary devices: %d\n", +- num); +- return num; +- } +- +- aux = devm_kcalloc(card->dev, num, sizeof(*aux), GFP_KERNEL); +- if (!aux) +- return -ENOMEM; +- card->aux_dev = aux; +- card->num_aux_devs = num; +- +- for_each_card_pre_auxs(card, i, aux) { +- aux->dlc.of_node = +- of_parse_phandle(node, "audio-aux-devs", i); +- if (!aux->dlc.of_node) +- return -EINVAL; +- } +- +- return 0; +-} +- + static int axg_card_tdm_be_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) + { + struct snd_soc_pcm_runtime *rtd = substream->private_data; +- struct axg_card *priv = snd_soc_card_get_drvdata(rtd->card); ++ struct meson_card *priv = snd_soc_card_get_drvdata(rtd->card); + struct axg_dai_link_tdm_data *be = + (struct axg_dai_link_tdm_data *)priv->link_data[rtd->num]; +- struct snd_soc_dai *codec_dai; +- unsigned int mclk; +- int ret, i; + +- if (be->mclk_fs) { +- mclk = params_rate(params) * be->mclk_fs; +- +- for_each_rtd_codec_dai(rtd, i, codec_dai) { +- ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, +- SND_SOC_CLOCK_IN); +- if (ret && ret != -ENOTSUPP) +- return ret; +- } +- +- ret = snd_soc_dai_set_sysclk(rtd->cpu_dai, 0, mclk, +- SND_SOC_CLOCK_OUT); +- if (ret && ret != -ENOTSUPP) +- return ret; +- } +- +- return 0; ++ return meson_card_i2s_set_sysclk(substream, params, be->mclk_fs); + } + + static const struct snd_soc_ops axg_card_tdm_be_ops = { +@@ -204,7 +54,7 @@ static const struct snd_soc_ops axg_card_tdm_be_ops = { + + static int axg_card_tdm_dai_init(struct snd_soc_pcm_runtime *rtd) + { +- struct axg_card *priv = snd_soc_card_get_drvdata(rtd->card); ++ struct meson_card *priv = snd_soc_card_get_drvdata(rtd->card); + struct axg_dai_link_tdm_data *be = + (struct axg_dai_link_tdm_data *)priv->link_data[rtd->num]; + struct snd_soc_dai *codec_dai; +@@ -234,7 +84,7 @@ static int axg_card_tdm_dai_init(struct snd_soc_pcm_runtime *rtd) + + static int axg_card_tdm_dai_lb_init(struct snd_soc_pcm_runtime *rtd) + { +- struct axg_card *priv = snd_soc_card_get_drvdata(rtd->card); ++ struct meson_card *priv = snd_soc_card_get_drvdata(rtd->card); + struct axg_dai_link_tdm_data *be = + (struct axg_dai_link_tdm_data *)priv->link_data[rtd->num]; + int ret; +@@ -253,14 +103,14 @@ static int axg_card_tdm_dai_lb_init(struct snd_soc_pcm_runtime *rtd) + static int axg_card_add_tdm_loopback(struct snd_soc_card *card, + int *index) + { +- struct axg_card *priv = snd_soc_card_get_drvdata(card); ++ struct meson_card *priv = snd_soc_card_get_drvdata(card); + struct snd_soc_dai_link *pad = &card->dai_link[*index]; + struct snd_soc_dai_link *lb; + struct snd_soc_dai_link_component *dlc; + int ret; + + /* extend links */ +- ret = axg_card_reallocate_links(priv, card->num_links + 1); ++ ret = meson_card_reallocate_links(card, card->num_links + 1); + if (ret) + return ret; + +@@ -304,32 +154,6 @@ static int axg_card_add_tdm_loopback(struct snd_soc_card *card, + return 0; + } + +-static unsigned int axg_card_parse_daifmt(struct device_node *node, +- struct device_node *cpu_node) +-{ +- struct device_node *bitclkmaster = NULL; +- struct device_node *framemaster = NULL; +- unsigned int daifmt; +- +- daifmt = snd_soc_of_parse_daifmt(node, PREFIX, +- &bitclkmaster, &framemaster); +- daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK; +- +- /* If no master is provided, default to cpu master */ +- if (!bitclkmaster || bitclkmaster == cpu_node) { +- daifmt |= (!framemaster || framemaster == cpu_node) ? +- SND_SOC_DAIFMT_CBS_CFS : SND_SOC_DAIFMT_CBS_CFM; +- } else { +- daifmt |= (!framemaster || framemaster == cpu_node) ? +- SND_SOC_DAIFMT_CBM_CFS : SND_SOC_DAIFMT_CBM_CFM; +- } +- +- of_node_put(bitclkmaster); +- of_node_put(framemaster); +- +- return daifmt; +-} +- + static int axg_card_parse_cpu_tdm_slots(struct snd_soc_card *card, + struct snd_soc_dai_link *link, + struct device_node *node, +@@ -424,7 +248,7 @@ static int axg_card_parse_tdm(struct snd_soc_card *card, + struct device_node *node, + int *index) + { +- struct axg_card *priv = snd_soc_card_get_drvdata(card); ++ struct meson_card *priv = snd_soc_card_get_drvdata(card); + struct snd_soc_dai_link *link = &card->dai_link[*index]; + struct axg_dai_link_tdm_data *be; + int ret; +@@ -438,7 +262,7 @@ static int axg_card_parse_tdm(struct snd_soc_card *card, + /* Setup tdm link */ + link->ops = &axg_card_tdm_be_ops; + link->init = axg_card_tdm_dai_init; +- link->dai_fmt = axg_card_parse_daifmt(node, link->cpus->of_node); ++ link->dai_fmt = meson_card_parse_daifmt(node, link->cpus->of_node); + + of_property_read_u32(node, "mclk-fs", &be->mclk_fs); + +@@ -462,97 +286,24 @@ static int axg_card_parse_tdm(struct snd_soc_card *card, + return 0; + } + +-static int axg_card_set_be_link(struct snd_soc_card *card, +- struct snd_soc_dai_link *link, +- struct device_node *node) +-{ +- struct snd_soc_dai_link_component *codec; +- struct device_node *np; +- int ret, num_codecs; +- +- link->no_pcm = 1; +- link->dpcm_playback = 1; +- link->dpcm_capture = 1; +- +- num_codecs = of_get_child_count(node); +- if (!num_codecs) { +- dev_err(card->dev, "be link %s has no codec\n", +- node->full_name); +- return -EINVAL; +- } +- +- codec = devm_kcalloc(card->dev, num_codecs, sizeof(*codec), GFP_KERNEL); +- if (!codec) +- return -ENOMEM; +- +- link->codecs = codec; +- link->num_codecs = num_codecs; +- +- for_each_child_of_node(node, np) { +- ret = axg_card_parse_dai(card, np, &codec->of_node, +- &codec->dai_name); +- if (ret) { +- of_node_put(np); +- return ret; +- } +- +- codec++; +- } +- +- ret = axg_card_set_link_name(card, link, node, "be"); +- if (ret) +- dev_err(card->dev, "error setting %pOFn link name\n", np); +- +- return ret; +-} +- +-static int axg_card_set_fe_link(struct snd_soc_card *card, +- struct snd_soc_dai_link *link, +- struct device_node *node, +- bool is_playback) +-{ +- struct snd_soc_dai_link_component *codec; +- +- codec = devm_kzalloc(card->dev, sizeof(*codec), GFP_KERNEL); +- if (!codec) +- return -ENOMEM; +- +- link->codecs = codec; +- link->num_codecs = 1; +- +- link->dynamic = 1; +- link->dpcm_merged_format = 1; +- link->dpcm_merged_chan = 1; +- link->dpcm_merged_rate = 1; +- link->codecs->dai_name = "snd-soc-dummy-dai"; +- link->codecs->name = "snd-soc-dummy"; +- +- if (is_playback) +- link->dpcm_playback = 1; +- else +- link->dpcm_capture = 1; +- +- return axg_card_set_link_name(card, link, node, "fe"); +-} +- + static int axg_card_cpu_is_capture_fe(struct device_node *np) + { +- return of_device_is_compatible(np, PREFIX "axg-toddr"); ++ return of_device_is_compatible(np, DT_PREFIX "axg-toddr"); + } + + static int axg_card_cpu_is_playback_fe(struct device_node *np) + { +- return of_device_is_compatible(np, PREFIX "axg-frddr"); ++ return of_device_is_compatible(np, DT_PREFIX "axg-frddr"); + } + + static int axg_card_cpu_is_tdm_iface(struct device_node *np) + { +- return of_device_is_compatible(np, PREFIX "axg-tdm-iface"); ++ return of_device_is_compatible(np, DT_PREFIX "axg-tdm-iface"); + } + + static int axg_card_cpu_is_codec(struct device_node *np) + { +- return of_device_is_compatible(np, PREFIX "g12a-tohdmitx"); ++ return of_device_is_compatible(np, DT_PREFIX "g12a-tohdmitx"); + } + + static int axg_card_add_link(struct snd_soc_card *card, struct device_node *np, +@@ -569,17 +320,17 @@ static int axg_card_add_link(struct snd_soc_card *card, struct device_node *np, + dai_link->cpus = cpu; + dai_link->num_cpus = 1; + +- ret = axg_card_parse_dai(card, np, &dai_link->cpus->of_node, ++ ret = meson_card_parse_dai(card, np, &dai_link->cpus->of_node, + &dai_link->cpus->dai_name); + if (ret) + return ret; + + if (axg_card_cpu_is_playback_fe(dai_link->cpus->of_node)) +- ret = axg_card_set_fe_link(card, dai_link, np, true); ++ ret = meson_card_set_fe_link(card, dai_link, np, true); + else if (axg_card_cpu_is_capture_fe(dai_link->cpus->of_node)) +- ret = axg_card_set_fe_link(card, dai_link, np, false); ++ ret = meson_card_set_fe_link(card, dai_link, np, false); + else +- ret = axg_card_set_be_link(card, dai_link, np); ++ ret = meson_card_set_be_link(card, dai_link, np); + + if (ret) + return ret; +@@ -592,121 +343,21 @@ static int axg_card_add_link(struct snd_soc_card *card, struct device_node *np, + return ret; + } + +-static int axg_card_add_links(struct snd_soc_card *card) +-{ +- struct axg_card *priv = snd_soc_card_get_drvdata(card); +- struct device_node *node = card->dev->of_node; +- struct device_node *np; +- int num, i, ret; +- +- num = of_get_child_count(node); +- if (!num) { +- dev_err(card->dev, "card has no links\n"); +- return -EINVAL; +- } +- +- ret = axg_card_reallocate_links(priv, num); +- if (ret) +- return ret; +- +- i = 0; +- for_each_child_of_node(node, np) { +- ret = axg_card_add_link(card, np, &i); +- if (ret) { +- of_node_put(np); +- return ret; +- } +- +- i++; +- } +- +- return 0; +-} +- +-static int axg_card_parse_of_optional(struct snd_soc_card *card, +- const char *propname, +- int (*func)(struct snd_soc_card *c, +- const char *p)) +-{ +- /* If property is not provided, don't fail ... */ +- if (!of_property_read_bool(card->dev->of_node, propname)) +- return 0; +- +- /* ... but do fail if it is provided and the parsing fails */ +- return func(card, propname); +-} ++static const struct meson_card_match_data axg_card_match_data = { ++ .add_link = axg_card_add_link, ++}; + + static const struct of_device_id axg_card_of_match[] = { +- { .compatible = "amlogic,axg-sound-card", }, +- {} ++ { ++ .compatible = "amlogic,axg-sound-card", ++ .data = &axg_card_match_data, ++ }, {} + }; + MODULE_DEVICE_TABLE(of, axg_card_of_match); + +-static int axg_card_probe(struct platform_device *pdev) +-{ +- struct device *dev = &pdev->dev; +- struct axg_card *priv; +- int ret; +- +- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); +- if (!priv) +- return -ENOMEM; +- +- platform_set_drvdata(pdev, priv); +- snd_soc_card_set_drvdata(&priv->card, priv); +- +- priv->card.owner = THIS_MODULE; +- priv->card.dev = dev; +- +- ret = snd_soc_of_parse_card_name(&priv->card, "model"); +- if (ret < 0) +- return ret; +- +- ret = axg_card_parse_of_optional(&priv->card, "audio-routing", +- snd_soc_of_parse_audio_routing); +- if (ret) { +- dev_err(dev, "error while parsing routing\n"); +- return ret; +- } +- +- ret = axg_card_parse_of_optional(&priv->card, "audio-widgets", +- snd_soc_of_parse_audio_simple_widgets); +- if (ret) { +- dev_err(dev, "error while parsing widgets\n"); +- return ret; +- } +- +- ret = axg_card_add_links(&priv->card); +- if (ret) +- goto out_err; +- +- ret = axg_card_add_aux_devices(&priv->card); +- if (ret) +- goto out_err; +- +- ret = devm_snd_soc_register_card(dev, &priv->card); +- if (ret) +- goto out_err; +- +- return 0; +- +-out_err: +- axg_card_clean_references(priv); +- return ret; +-} +- +-static int axg_card_remove(struct platform_device *pdev) +-{ +- struct axg_card *priv = platform_get_drvdata(pdev); +- +- axg_card_clean_references(priv); +- +- return 0; +-} +- + static struct platform_driver axg_card_pdrv = { +- .probe = axg_card_probe, +- .remove = axg_card_remove, ++ .probe = meson_card_probe, ++ .remove = meson_card_remove, + .driver = { + .name = "axg-sound-card", + .of_match_table = axg_card_of_match, +diff --git b/sound/soc/meson/gx-card.c a/sound/soc/meson/gx-card.c +new file mode 100644 +index 000000000000..b7a0e89aaf8c +--- /dev/null ++++ a/sound/soc/meson/gx-card.c +@@ -0,0 +1,142 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++// ++// Copyright (c) 2019 BayLibre, SAS. ++// Author: Jerome Brunet ++ ++#include ++#include ++#include ++#include ++ ++#include "meson-card.h" ++ ++struct gx_dai_link_i2s_data { ++ unsigned int mclk_fs; ++}; ++ ++/* ++ * Base params for the codec to codec links ++ * Those will be over-written by the CPU side of the link ++ */ ++static const struct snd_soc_pcm_stream codec_params = { ++ .formats = SNDRV_PCM_FMTBIT_S24_LE, ++ .rate_min = 5525, ++ .rate_max = 192000, ++ .channels_min = 1, ++ .channels_max = 8, ++}; ++ ++static int gx_card_i2s_be_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params) ++{ ++ struct snd_soc_pcm_runtime *rtd = substream->private_data; ++ struct meson_card *priv = snd_soc_card_get_drvdata(rtd->card); ++ struct gx_dai_link_i2s_data *be = ++ (struct gx_dai_link_i2s_data *)priv->link_data[rtd->num]; ++ ++ return meson_card_i2s_set_sysclk(substream, params, be->mclk_fs); ++} ++ ++static const struct snd_soc_ops gx_card_i2s_be_ops = { ++ .hw_params = gx_card_i2s_be_hw_params, ++}; ++ ++static int gx_card_parse_i2s(struct snd_soc_card *card, ++ struct device_node *node, ++ int *index) ++{ ++ struct meson_card *priv = snd_soc_card_get_drvdata(card); ++ struct snd_soc_dai_link *link = &card->dai_link[*index]; ++ struct gx_dai_link_i2s_data *be; ++ ++ /* Allocate i2s link parameters */ ++ be = devm_kzalloc(card->dev, sizeof(*be), GFP_KERNEL); ++ if (!be) ++ return -ENOMEM; ++ priv->link_data[*index] = be; ++ ++ /* Setup i2s link */ ++ link->ops = &gx_card_i2s_be_ops; ++ link->dai_fmt = meson_card_parse_daifmt(node, link->cpus->of_node); ++ ++ of_property_read_u32(node, "mclk-fs", &be->mclk_fs); ++ ++ return 0; ++} ++ ++static int gx_card_cpu_is_playback_fe(struct device_node *np) ++{ ++ return of_device_is_compatible(np, DT_PREFIX "aiu-fifo"); ++} ++ ++static int gx_card_cpu_is_i2s_encoder(struct device_node *np) ++{ ++ return of_device_is_compatible(np, DT_PREFIX "aiu-i2s-encode"); ++} ++ ++static int gx_card_cpu_is_codec(struct device_node *np) ++{ ++ return of_device_is_compatible(np, DT_PREFIX "gx-tohdmitx") || ++ of_device_is_compatible(np, DT_PREFIX "gxl-toacodec"); ++} ++ ++static int gx_card_add_link(struct snd_soc_card *card, struct device_node *np, ++ int *index) ++{ ++ struct snd_soc_dai_link *dai_link = &card->dai_link[*index]; ++ struct snd_soc_dai_link_component *cpu; ++ int ret; ++ ++ cpu = devm_kzalloc(card->dev, sizeof(*cpu), GFP_KERNEL); ++ if (!cpu) ++ return -ENOMEM; ++ ++ dai_link->cpus = cpu; ++ dai_link->num_cpus = 1; ++ ++ ret = meson_card_parse_dai(card, np, &dai_link->cpus->of_node, ++ &dai_link->cpus->dai_name); ++ if (ret) ++ return ret; ++ ++ if (gx_card_cpu_is_playback_fe(dai_link->cpus->of_node)) ++ ret = meson_card_set_fe_link(card, dai_link, np, true); ++ else ++ ret = meson_card_set_be_link(card, dai_link, np); ++ ++ if (ret) ++ return ret; ++ ++ if (gx_card_cpu_is_i2s_encoder(dai_link->cpus->of_node)) ++ ret = gx_card_parse_i2s(card, np, index); ++ else if (gx_card_cpu_is_codec(dai_link->cpus->of_node)) ++ dai_link->params = &codec_params; ++ ++ return ret; ++} ++ ++static const struct meson_card_match_data gx_card_match_data = { ++ .add_link = gx_card_add_link, ++}; ++ ++static const struct of_device_id gx_card_of_match[] = { ++ { ++ .compatible = "amlogic,gx-sound-card", ++ .data = &gx_card_match_data, ++ }, {} ++}; ++MODULE_DEVICE_TABLE(of, gx_card_of_match); ++ ++static struct platform_driver gx_card_pdrv = { ++ .probe = meson_card_probe, ++ .remove = meson_card_remove, ++ .driver = { ++ .name = "gx-sound-card", ++ .of_match_table = gx_card_of_match, ++ }, ++}; ++module_platform_driver(gx_card_pdrv); ++ ++MODULE_DESCRIPTION("Amlogic GX ALSA machine driver"); ++MODULE_AUTHOR("Jerome Brunet "); ++MODULE_LICENSE("GPL v2"); +diff --git b/sound/soc/meson/meson-card-utils.c a/sound/soc/meson/meson-card-utils.c +new file mode 100644 +index 000000000000..88250b6699f8 +--- /dev/null ++++ a/sound/soc/meson/meson-card-utils.c +@@ -0,0 +1,386 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// ++// Copyright (c) 2019 BayLibre, SAS. ++// Author: Jerome Brunet ++ ++#include ++#include ++#include ++ ++#include "meson-card.h" ++ ++int meson_card_i2s_set_sysclk(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params, ++ unsigned int mclk_fs) ++{ ++ struct snd_soc_pcm_runtime *rtd = substream->private_data; ++ struct snd_soc_dai *codec_dai; ++ unsigned int mclk; ++ int ret, i; ++ ++ if (!mclk_fs) ++ return 0; ++ ++ mclk = params_rate(params) * mclk_fs; ++ ++ for_each_rtd_codec_dai(rtd, i, codec_dai) { ++ ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, ++ SND_SOC_CLOCK_IN); ++ if (ret && ret != -ENOTSUPP) ++ return ret; ++ } ++ ++ ret = snd_soc_dai_set_sysclk(rtd->cpu_dai, 0, mclk, ++ SND_SOC_CLOCK_OUT); ++ if (ret && ret != -ENOTSUPP) ++ return ret; ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(meson_card_i2s_set_sysclk); ++ ++int meson_card_reallocate_links(struct snd_soc_card *card, ++ unsigned int num_links) ++{ ++ struct meson_card *priv = snd_soc_card_get_drvdata(card); ++ struct snd_soc_dai_link *links; ++ void **ldata; ++ ++ links = krealloc(priv->card.dai_link, ++ num_links * sizeof(*priv->card.dai_link), ++ GFP_KERNEL | __GFP_ZERO); ++ ldata = krealloc(priv->link_data, ++ num_links * sizeof(*priv->link_data), ++ GFP_KERNEL | __GFP_ZERO); ++ ++ if (!links || !ldata) { ++ dev_err(priv->card.dev, "failed to allocate links\n"); ++ return -ENOMEM; ++ } ++ ++ priv->card.dai_link = links; ++ priv->link_data = ldata; ++ priv->card.num_links = num_links; ++ return 0; ++} ++EXPORT_SYMBOL_GPL(meson_card_reallocate_links); ++ ++int meson_card_parse_dai(struct snd_soc_card *card, ++ struct device_node *node, ++ struct device_node **dai_of_node, ++ const char **dai_name) ++{ ++ struct of_phandle_args args; ++ int ret; ++ ++ if (!dai_name || !dai_of_node || !node) ++ return -EINVAL; ++ ++ ret = of_parse_phandle_with_args(node, "sound-dai", ++ "#sound-dai-cells", 0, &args); ++ if (ret) { ++ if (ret != -EPROBE_DEFER) ++ dev_err(card->dev, "can't parse dai %d\n", ret); ++ return ret; ++ } ++ *dai_of_node = args.np; ++ ++ return snd_soc_get_dai_name(&args, dai_name); ++} ++EXPORT_SYMBOL_GPL(meson_card_parse_dai); ++ ++static int meson_card_set_link_name(struct snd_soc_card *card, ++ struct snd_soc_dai_link *link, ++ struct device_node *node, ++ const char *prefix) ++{ ++ char *name = devm_kasprintf(card->dev, GFP_KERNEL, "%s.%s", ++ prefix, node->full_name); ++ if (!name) ++ return -ENOMEM; ++ ++ link->name = name; ++ link->stream_name = name; ++ ++ return 0; ++} ++ ++ ++unsigned int meson_card_parse_daifmt(struct device_node *node, ++ struct device_node *cpu_node) ++{ ++ struct device_node *bitclkmaster = NULL; ++ struct device_node *framemaster = NULL; ++ unsigned int daifmt; ++ ++ daifmt = snd_soc_of_parse_daifmt(node, DT_PREFIX, ++ &bitclkmaster, &framemaster); ++ daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK; ++ ++ /* If no master is provided, default to cpu master */ ++ if (!bitclkmaster || bitclkmaster == cpu_node) { ++ daifmt |= (!framemaster || framemaster == cpu_node) ? ++ SND_SOC_DAIFMT_CBS_CFS : SND_SOC_DAIFMT_CBS_CFM; ++ } else { ++ daifmt |= (!framemaster || framemaster == cpu_node) ? ++ SND_SOC_DAIFMT_CBM_CFS : SND_SOC_DAIFMT_CBM_CFM; ++ } ++ ++ of_node_put(bitclkmaster); ++ of_node_put(framemaster); ++ ++ return daifmt; ++} ++EXPORT_SYMBOL_GPL(meson_card_parse_daifmt); ++ ++int meson_card_set_be_link(struct snd_soc_card *card, ++ struct snd_soc_dai_link *link, ++ struct device_node *node) ++{ ++ struct snd_soc_dai_link_component *codec; ++ struct device_node *np; ++ int ret, num_codecs; ++ ++ link->no_pcm = 1; ++ link->dpcm_playback = 1; ++ link->dpcm_capture = 1; ++ ++ num_codecs = of_get_child_count(node); ++ if (!num_codecs) { ++ dev_err(card->dev, "be link %s has no codec\n", ++ node->full_name); ++ return -EINVAL; ++ } ++ ++ codec = devm_kcalloc(card->dev, num_codecs, sizeof(*codec), GFP_KERNEL); ++ if (!codec) ++ return -ENOMEM; ++ ++ link->codecs = codec; ++ link->num_codecs = num_codecs; ++ ++ for_each_child_of_node(node, np) { ++ ret = meson_card_parse_dai(card, np, &codec->of_node, ++ &codec->dai_name); ++ if (ret) { ++ of_node_put(np); ++ return ret; ++ } ++ ++ codec++; ++ } ++ ++ ret = meson_card_set_link_name(card, link, node, "be"); ++ if (ret) ++ dev_err(card->dev, "error setting %pOFn link name\n", np); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(meson_card_set_be_link); ++ ++int meson_card_set_fe_link(struct snd_soc_card *card, ++ struct snd_soc_dai_link *link, ++ struct device_node *node, ++ bool is_playback) ++{ ++ struct snd_soc_dai_link_component *codec; ++ ++ codec = devm_kzalloc(card->dev, sizeof(*codec), GFP_KERNEL); ++ if (!codec) ++ return -ENOMEM; ++ ++ link->codecs = codec; ++ link->num_codecs = 1; ++ ++ link->dynamic = 1; ++ link->dpcm_merged_format = 1; ++ link->dpcm_merged_chan = 1; ++ link->dpcm_merged_rate = 1; ++ link->codecs->dai_name = "snd-soc-dummy-dai"; ++ link->codecs->name = "snd-soc-dummy"; ++ ++ if (is_playback) ++ link->dpcm_playback = 1; ++ else ++ link->dpcm_capture = 1; ++ ++ return meson_card_set_link_name(card, link, node, "fe"); ++} ++EXPORT_SYMBOL_GPL(meson_card_set_fe_link); ++ ++static int meson_card_add_links(struct snd_soc_card *card) ++{ ++ struct meson_card *priv = snd_soc_card_get_drvdata(card); ++ struct device_node *node = card->dev->of_node; ++ struct device_node *np; ++ int num, i, ret; ++ ++ num = of_get_child_count(node); ++ if (!num) { ++ dev_err(card->dev, "card has no links\n"); ++ return -EINVAL; ++ } ++ ++ ret = meson_card_reallocate_links(card, num); ++ if (ret) ++ return ret; ++ ++ i = 0; ++ for_each_child_of_node(node, np) { ++ ret = priv->match_data->add_link(card, np, &i); ++ if (ret) { ++ of_node_put(np); ++ return ret; ++ } ++ ++ i++; ++ } ++ ++ return 0; ++} ++ ++static int meson_card_parse_of_optional(struct snd_soc_card *card, ++ const char *propname, ++ int (*func)(struct snd_soc_card *c, ++ const char *p)) ++{ ++ /* If property is not provided, don't fail ... */ ++ if (!of_property_read_bool(card->dev->of_node, propname)) ++ return 0; ++ ++ /* ... but do fail if it is provided and the parsing fails */ ++ return func(card, propname); ++} ++ ++static int meson_card_add_aux_devices(struct snd_soc_card *card) ++{ ++ struct device_node *node = card->dev->of_node; ++ struct snd_soc_aux_dev *aux; ++ int num, i; ++ ++ num = of_count_phandle_with_args(node, "audio-aux-devs", NULL); ++ if (num == -ENOENT) { ++ return 0; ++ } else if (num < 0) { ++ dev_err(card->dev, "error getting auxiliary devices: %d\n", ++ num); ++ return num; ++ } ++ ++ aux = devm_kcalloc(card->dev, num, sizeof(*aux), GFP_KERNEL); ++ if (!aux) ++ return -ENOMEM; ++ card->aux_dev = aux; ++ card->num_aux_devs = num; ++ ++ for_each_card_pre_auxs(card, i, aux) { ++ aux->dlc.of_node = ++ of_parse_phandle(node, "audio-aux-devs", i); ++ if (!aux->dlc.of_node) ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static void meson_card_clean_references(struct meson_card *priv) ++{ ++ struct snd_soc_card *card = &priv->card; ++ struct snd_soc_dai_link *link; ++ struct snd_soc_dai_link_component *codec; ++ struct snd_soc_aux_dev *aux; ++ int i, j; ++ ++ if (card->dai_link) { ++ for_each_card_prelinks(card, i, link) { ++ if (link->cpus) ++ of_node_put(link->cpus->of_node); ++ for_each_link_codecs(link, j, codec) ++ of_node_put(codec->of_node); ++ } ++ } ++ ++ if (card->aux_dev) { ++ for_each_card_pre_auxs(card, i, aux) ++ of_node_put(aux->dlc.of_node); ++ } ++ ++ kfree(card->dai_link); ++ kfree(priv->link_data); ++} ++ ++int meson_card_probe(struct platform_device *pdev) ++{ ++ const struct meson_card_match_data *data; ++ struct device *dev = &pdev->dev; ++ struct meson_card *priv; ++ int ret; ++ ++ data = of_device_get_match_data(dev); ++ if (!data) { ++ dev_err(dev, "failed to match device\n"); ++ return -ENODEV; ++ } ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, priv); ++ snd_soc_card_set_drvdata(&priv->card, priv); ++ ++ priv->card.owner = THIS_MODULE; ++ priv->card.dev = dev; ++ priv->match_data = data; ++ ++ ret = snd_soc_of_parse_card_name(&priv->card, "model"); ++ if (ret < 0) ++ return ret; ++ ++ ret = meson_card_parse_of_optional(&priv->card, "audio-routing", ++ snd_soc_of_parse_audio_routing); ++ if (ret) { ++ dev_err(dev, "error while parsing routing\n"); ++ return ret; ++ } ++ ++ ret = meson_card_parse_of_optional(&priv->card, "audio-widgets", ++ snd_soc_of_parse_audio_simple_widgets); ++ if (ret) { ++ dev_err(dev, "error while parsing widgets\n"); ++ return ret; ++ } ++ ++ ret = meson_card_add_links(&priv->card); ++ if (ret) ++ goto out_err; ++ ++ ret = meson_card_add_aux_devices(&priv->card); ++ if (ret) ++ goto out_err; ++ ++ ret = devm_snd_soc_register_card(dev, &priv->card); ++ if (ret) ++ goto out_err; ++ ++ return 0; ++ ++out_err: ++ meson_card_clean_references(priv); ++ return ret; ++} ++EXPORT_SYMBOL_GPL(meson_card_probe); ++ ++int meson_card_remove(struct platform_device *pdev) ++{ ++ struct meson_card *priv = platform_get_drvdata(pdev); ++ ++ meson_card_clean_references(priv); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(meson_card_remove); ++ ++MODULE_DESCRIPTION("Amlogic Sound Card Utils"); ++MODULE_AUTHOR("Jerome Brunet "); ++MODULE_LICENSE("GPL v2"); +diff --git b/sound/soc/meson/meson-card.h a/sound/soc/meson/meson-card.h +new file mode 100644 +index 000000000000..15920c71cde5 +--- /dev/null ++++ a/sound/soc/meson/meson-card.h +@@ -0,0 +1,53 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Copyright (c) 2019 BayLibre, SAS. ++ * Author: Jerome Brunet ++ */ ++ ++#ifndef _MESON_SND_CARD_H ++#define _MESON_SND_CARD_H ++ ++struct device_node; ++struct platform_device; ++ ++struct snd_soc_card; ++struct snd_pcm_substream; ++struct snd_pcm_hw_params; ++ ++#define DT_PREFIX "amlogic," ++ ++struct meson_card_match_data { ++ int (*add_link)(struct snd_soc_card *, struct device_node *, int *); ++}; ++ ++struct meson_card { ++ const struct meson_card_match_data *match_data; ++ struct snd_soc_card card; ++ void **link_data; ++}; ++ ++unsigned int meson_card_parse_daifmt(struct device_node *node, ++ struct device_node *cpu_node); ++ ++int meson_card_i2s_set_sysclk(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params, ++ unsigned int mclk_fs); ++ ++int meson_card_reallocate_links(struct snd_soc_card *card, ++ unsigned int num_links); ++int meson_card_parse_dai(struct snd_soc_card *card, ++ struct device_node *node, ++ struct device_node **dai_of_node, ++ const char **dai_name); ++int meson_card_set_be_link(struct snd_soc_card *card, ++ struct snd_soc_dai_link *link, ++ struct device_node *node); ++int meson_card_set_fe_link(struct snd_soc_card *card, ++ struct snd_soc_dai_link *link, ++ struct device_node *node, ++ bool is_playback); ++ ++int meson_card_probe(struct platform_device *pdev); ++int meson_card_remove(struct platform_device *pdev); ++ ++#endif /* _MESON_SND_CARD_H */ +diff --git b/sound/usb/clock.c a/sound/usb/clock.c +index a48313dfa967..6b8c14f9b5d4 100644 +--- b/sound/usb/clock.c ++++ a/sound/usb/clock.c +@@ -151,34 +151,8 @@ static int uac_clock_selector_set_val(struct snd_usb_audio *chip, int selector_i + return ret; + } + +-/* +- * Assume the clock is valid if clock source supports only one single sample +- * rate, the terminal is connected directly to it (there is no clock selector) +- * and clock type is internal. This is to deal with some Denon DJ controllers +- * that always reports that clock is invalid. +- */ +-static bool uac_clock_source_is_valid_quirk(struct snd_usb_audio *chip, +- struct audioformat *fmt, +- int source_id) +-{ +- if (fmt->protocol == UAC_VERSION_2) { +- struct uac_clock_source_descriptor *cs_desc = +- snd_usb_find_clock_source(chip->ctrl_intf, source_id); +- +- if (!cs_desc) +- return false; +- +- return (fmt->nr_rates == 1 && +- (fmt->clock & 0xff) == cs_desc->bClockID && +- (cs_desc->bmAttributes & 0x3) != +- UAC_CLOCK_SOURCE_TYPE_EXT); +- } +- +- return false; +-} +- + static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, +- struct audioformat *fmt, ++ int protocol, + int source_id) + { + int err; +@@ -186,26 +160,26 @@ static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, + struct usb_device *dev = chip->dev; + u32 bmControls; + +- if (fmt->protocol == UAC_VERSION_3) { ++ if (protocol == UAC_VERSION_3) { + struct uac3_clock_source_descriptor *cs_desc = + snd_usb_find_clock_source_v3(chip->ctrl_intf, source_id); + + if (!cs_desc) +- return false; ++ return 0; + bmControls = le32_to_cpu(cs_desc->bmControls); + } else { /* UAC_VERSION_1/2 */ + struct uac_clock_source_descriptor *cs_desc = + snd_usb_find_clock_source(chip->ctrl_intf, source_id); + + if (!cs_desc) +- return false; ++ return 0; + bmControls = cs_desc->bmControls; + } + + /* If a clock source can't tell us whether it's valid, we assume it is */ + if (!uac_v2v3_control_is_readable(bmControls, + UAC2_CS_CONTROL_CLOCK_VALID)) +- return true; ++ return 1; + + err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, +@@ -217,17 +191,13 @@ static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, + dev_warn(&dev->dev, + "%s(): cannot get clock validity for id %d\n", + __func__, source_id); +- return false; ++ return 0; + } + +- if (data) +- return true; +- else +- return uac_clock_source_is_valid_quirk(chip, fmt, source_id); ++ return !!data; + } + +-static int __uac_clock_find_source(struct snd_usb_audio *chip, +- struct audioformat *fmt, int entity_id, ++static int __uac_clock_find_source(struct snd_usb_audio *chip, int entity_id, + unsigned long *visited, bool validate) + { + struct uac_clock_source_descriptor *source; +@@ -247,7 +217,7 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip, + source = snd_usb_find_clock_source(chip->ctrl_intf, entity_id); + if (source) { + entity_id = source->bClockID; +- if (validate && !uac_clock_source_is_valid(chip, fmt, ++ if (validate && !uac_clock_source_is_valid(chip, UAC_VERSION_2, + entity_id)) { + usb_audio_err(chip, + "clock source %d is not valid, cannot use\n", +@@ -278,9 +248,8 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip, + } + + cur = ret; +- ret = __uac_clock_find_source(chip, fmt, +- selector->baCSourceID[ret - 1], +- visited, validate); ++ ret = __uac_clock_find_source(chip, selector->baCSourceID[ret - 1], ++ visited, validate); + if (!validate || ret > 0 || !chip->autoclock) + return ret; + +@@ -291,9 +260,8 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip, + if (i == cur) + continue; + +- ret = __uac_clock_find_source(chip, fmt, +- selector->baCSourceID[i - 1], +- visited, true); ++ ret = __uac_clock_find_source(chip, selector->baCSourceID[i - 1], ++ visited, true); + if (ret < 0) + continue; + +@@ -313,16 +281,14 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip, + /* FIXME: multipliers only act as pass-thru element for now */ + multiplier = snd_usb_find_clock_multiplier(chip->ctrl_intf, entity_id); + if (multiplier) +- return __uac_clock_find_source(chip, fmt, +- multiplier->bCSourceID, +- visited, validate); ++ return __uac_clock_find_source(chip, multiplier->bCSourceID, ++ visited, validate); + + return -EINVAL; + } + +-static int __uac3_clock_find_source(struct snd_usb_audio *chip, +- struct audioformat *fmt, int entity_id, +- unsigned long *visited, bool validate) ++static int __uac3_clock_find_source(struct snd_usb_audio *chip, int entity_id, ++ unsigned long *visited, bool validate) + { + struct uac3_clock_source_descriptor *source; + struct uac3_clock_selector_descriptor *selector; +@@ -341,7 +307,7 @@ static int __uac3_clock_find_source(struct snd_usb_audio *chip, + source = snd_usb_find_clock_source_v3(chip->ctrl_intf, entity_id); + if (source) { + entity_id = source->bClockID; +- if (validate && !uac_clock_source_is_valid(chip, fmt, ++ if (validate && !uac_clock_source_is_valid(chip, UAC_VERSION_3, + entity_id)) { + usb_audio_err(chip, + "clock source %d is not valid, cannot use\n", +@@ -372,8 +338,7 @@ static int __uac3_clock_find_source(struct snd_usb_audio *chip, + } + + cur = ret; +- ret = __uac3_clock_find_source(chip, fmt, +- selector->baCSourceID[ret - 1], ++ ret = __uac3_clock_find_source(chip, selector->baCSourceID[ret - 1], + visited, validate); + if (!validate || ret > 0 || !chip->autoclock) + return ret; +@@ -385,9 +350,8 @@ static int __uac3_clock_find_source(struct snd_usb_audio *chip, + if (i == cur) + continue; + +- ret = __uac3_clock_find_source(chip, fmt, +- selector->baCSourceID[i - 1], +- visited, true); ++ ret = __uac3_clock_find_source(chip, selector->baCSourceID[i - 1], ++ visited, true); + if (ret < 0) + continue; + +@@ -408,8 +372,7 @@ static int __uac3_clock_find_source(struct snd_usb_audio *chip, + multiplier = snd_usb_find_clock_multiplier_v3(chip->ctrl_intf, + entity_id); + if (multiplier) +- return __uac3_clock_find_source(chip, fmt, +- multiplier->bCSourceID, ++ return __uac3_clock_find_source(chip, multiplier->bCSourceID, + visited, validate); + + return -EINVAL; +@@ -426,18 +389,18 @@ static int __uac3_clock_find_source(struct snd_usb_audio *chip, + * + * Returns the clock source UnitID (>=0) on success, or an error. + */ +-int snd_usb_clock_find_source(struct snd_usb_audio *chip, +- struct audioformat *fmt, bool validate) ++int snd_usb_clock_find_source(struct snd_usb_audio *chip, int protocol, ++ int entity_id, bool validate) + { + DECLARE_BITMAP(visited, 256); + memset(visited, 0, sizeof(visited)); + +- switch (fmt->protocol) { ++ switch (protocol) { + case UAC_VERSION_2: +- return __uac_clock_find_source(chip, fmt, fmt->clock, visited, ++ return __uac_clock_find_source(chip, entity_id, visited, + validate); + case UAC_VERSION_3: +- return __uac3_clock_find_source(chip, fmt, fmt->clock, visited, ++ return __uac3_clock_find_source(chip, entity_id, visited, + validate); + default: + return -EINVAL; +@@ -538,7 +501,8 @@ static int set_sample_rate_v2v3(struct snd_usb_audio *chip, int iface, + * automatic clock selection if the current clock is not + * valid. + */ +- clock = snd_usb_clock_find_source(chip, fmt, true); ++ clock = snd_usb_clock_find_source(chip, fmt->protocol, ++ fmt->clock, true); + if (clock < 0) { + /* We did not find a valid clock, but that might be + * because the current sample rate does not match an +@@ -546,7 +510,8 @@ static int set_sample_rate_v2v3(struct snd_usb_audio *chip, int iface, + * and we will do another validation after setting the + * rate. + */ +- clock = snd_usb_clock_find_source(chip, fmt, false); ++ clock = snd_usb_clock_find_source(chip, fmt->protocol, ++ fmt->clock, false); + if (clock < 0) + return clock; + } +@@ -612,7 +577,7 @@ static int set_sample_rate_v2v3(struct snd_usb_audio *chip, int iface, + + validation: + /* validate clock after rate change */ +- if (!uac_clock_source_is_valid(chip, fmt, clock)) ++ if (!uac_clock_source_is_valid(chip, fmt->protocol, clock)) + return -ENXIO; + return 0; + } +diff --git b/sound/usb/clock.h a/sound/usb/clock.h +index 68df0fbe09d0..076e31b79ee0 100644 +--- b/sound/usb/clock.h ++++ a/sound/usb/clock.h +@@ -6,7 +6,7 @@ int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface, + struct usb_host_interface *alts, + struct audioformat *fmt, int rate); + +-int snd_usb_clock_find_source(struct snd_usb_audio *chip, +- struct audioformat *fmt, bool validate); ++int snd_usb_clock_find_source(struct snd_usb_audio *chip, int protocol, ++ int entity_id, bool validate); + + #endif /* __USBAUDIO_CLOCK_H */ +diff --git b/sound/usb/format.c a/sound/usb/format.c +index 25668ba5e68e..d79db71305f6 100644 +--- b/sound/usb/format.c ++++ a/sound/usb/format.c +@@ -322,7 +322,8 @@ static int parse_audio_format_rates_v2v3(struct snd_usb_audio *chip, + struct usb_device *dev = chip->dev; + unsigned char tmp[2], *data; + int nr_triplets, data_size, ret = 0, ret_l6; +- int clock = snd_usb_clock_find_source(chip, fp, false); ++ int clock = snd_usb_clock_find_source(chip, fp->protocol, ++ fp->clock, false); + + if (clock < 0) { + dev_err(&dev->dev, +diff --git b/sound/usb/mixer.c a/sound/usb/mixer.c +index d2a050bb8341..6cd4ff09c5ee 100644 +--- b/sound/usb/mixer.c ++++ a/sound/usb/mixer.c +@@ -897,15 +897,6 @@ static int parse_term_proc_unit(struct mixer_build *state, + return 0; + } + +-static int parse_term_effect_unit(struct mixer_build *state, +- struct usb_audio_term *term, +- void *p1, int id) +-{ +- term->type = UAC3_EFFECT_UNIT << 16; /* virtual type */ +- term->id = id; +- return 0; +-} +- + static int parse_term_uac2_clock_source(struct mixer_build *state, + struct usb_audio_term *term, + void *p1, int id) +@@ -990,7 +981,8 @@ static int __check_input_term(struct mixer_build *state, int id, + UAC3_PROCESSING_UNIT); + case PTYPE(UAC_VERSION_2, UAC2_EFFECT_UNIT): + case PTYPE(UAC_VERSION_3, UAC3_EFFECT_UNIT): +- return parse_term_effect_unit(state, term, p1, id); ++ return parse_term_proc_unit(state, term, p1, id, ++ UAC3_EFFECT_UNIT); + case PTYPE(UAC_VERSION_1, UAC1_EXTENSION_UNIT): + case PTYPE(UAC_VERSION_2, UAC2_EXTENSION_UNIT_V2): + case PTYPE(UAC_VERSION_3, UAC3_EXTENSION_UNIT): +diff --git b/sound/usb/quirks.c a/sound/usb/quirks.c +index 1ed25b1d2a6a..82184036437b 100644 +--- b/sound/usb/quirks.c ++++ a/sound/usb/quirks.c +@@ -1402,7 +1402,6 @@ bool snd_usb_get_sample_rate_quirk(struct snd_usb_audio *chip) + case USB_ID(0x1395, 0x740a): /* Sennheiser DECT */ + case USB_ID(0x1901, 0x0191): /* GE B850V3 CP2114 audio interface */ + case USB_ID(0x21B4, 0x0081): /* AudioQuest DragonFly */ +- case USB_ID(0x2912, 0x30c8): /* Audioengine D1 */ + return true; + } +