mirror of
https://github.com/Fishwaldo/build.git
synced 2025-04-15 02:21:28 +00:00
6213 lines
173 KiB
Diff
6213 lines
173 KiB
Diff
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 <pantelis.antoniou@konsulko.com>
|
|
+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 <pantelis.antoniou@konsulko.com>
|
|
+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/<id>
|
|
+Date: October 2015
|
|
+Contact: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
|
|
+Description:
|
|
+ Each directory represents an applied overlay, containing
|
|
+ the following attribute files.
|
|
+
|
|
+What: /sys/firmware/devicetree/overlays/<id>/can_remove
|
|
+Date: October 2015
|
|
+Contact: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
|
|
+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/<id>/<fragment-name>/
|
|
+Date: October 2015
|
|
+Contact: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
|
|
+Description:
|
|
+ Each of these directories contain information about of the
|
|
+ particular overlay fragment.
|
|
+
|
|
+What: /sys/firmware/devicetree/overlays/<id>/<fragment-name>/target
|
|
+Date: October 2015
|
|
+Contact: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
|
|
+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 <carlo@caione.org>
|
|
*/
|
|
|
|
-#include <dt-bindings/clock/meson8-ddr-clkc.h>
|
|
#include <dt-bindings/clock/meson8b-clkc.h>
|
|
#include <dt-bindings/gpio/meson8-gpio.h>
|
|
#include <dt-bindings/reset/amlogic,meson8b-clkc-reset.h>
|
|
@@ -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 <carlo@endlessm.com>
|
|
*/
|
|
|
|
-#include <dt-bindings/clock/meson8-ddr-clkc.h>
|
|
#include <dt-bindings/clock/meson8b-clkc.h>
|
|
#include <dt-bindings/gpio/meson8b-gpio.h>
|
|
#include <dt-bindings/reset/amlogic,meson8b-reset.h>
|
|
@@ -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 <jbrunet@baylibre.com>
|
|
+ */
|
|
+
|
|
+/* Libretech Amlogic GX PC form factor - AKA: Tartiflette */
|
|
+
|
|
+#include <dt-bindings/input/input.h>
|
|
+#include <dt-bindings/leds/common.h>
|
|
+
|
|
+/ {
|
|
+ adc-keys {
|
|
+ compatible = "adc-keys";
|
|
+ io-channels = <&saradc 0>;
|
|
+ io-channel-names = "buttons";
|
|
+ keyup-threshold-microvolt = <1800000>;
|
|
+
|
|
+ update-button {
|
|
+ label = "update";
|
|
+ linux,code = <KEY_VENDOR>;
|
|
+ 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 = <KEY_POWER>;
|
|
+ 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 = <LED_COLOR_ID_GREEN>;
|
|
+ function = LED_FUNCTION_DISK_ACTIVITY;
|
|
+ gpios = <&gpio_ao GPIOAO_9 GPIO_ACTIVE_HIGH>;
|
|
+ linux,default-trigger = "disk-activity";
|
|
+ };
|
|
+
|
|
+ blue {
|
|
+ color = <LED_COLOR_ID_BLUE>;
|
|
+ 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.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
|
|
@@ -161,7 +161,6 @@
|
|
#address-cells = <1>;
|
|
#size-cells = <1>;
|
|
read-only;
|
|
- secure-monitor = <&sm>;
|
|
|
|
sn: sn@14 {
|
|
reg = <0x14 0x10>;
|
|
@@ -596,6 +606,7 @@
|
|
interrupts = <GIC_SPI 57 IRQ_TYPE_EDGE_RISING>;
|
|
#address-cells = <1>;
|
|
#size-cells = <0>;
|
|
+ #sound-dai-cells = <0>;
|
|
status = "disabled";
|
|
|
|
/* VPU VENC Input */
|
|
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 <jbrunet@baylibre.com>
|
|
+ */
|
|
+
|
|
+/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-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.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 = <GIC_SPI 188 IRQ_TYPE_EDGE_RISING>,
|
|
- <GIC_SPI 189 IRQ_TYPE_EDGE_RISING>;
|
|
- 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";
|
|
@@ -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",
|
|
@@ -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";
|
|
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 <jbrunet@baylibre.com>
|
|
+ */
|
|
+
|
|
+/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/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)
|
|
{
|
|
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 <drm/drm_device.h>
|
|
-#include <linux/atomic.h>
|
|
#include <linux/delay.h>
|
|
|
|
#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 <drm/drm_prime.h>
|
|
#include <drm/lima_drm.h>
|
|
|
|
-#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 <linux/mm.h>
|
|
#include <linux/sync_file.h>
|
|
-#include <linux/pagemap.h>
|
|
+#include <linux/pfn_t.h>
|
|
|
|
#include <drm/drm_file.h>
|
|
#include <drm/drm_syncobj.h>
|
|
@@ -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 <drm/drm_gem_shmem_helper.h>
|
|
-
|
|
+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 <yuq825@gmail.com> */
|
|
+
|
|
+#include <linux/dma-buf.h>
|
|
+#include <drm/drm_prime.h>
|
|
+#include <drm/drm_drv.h>
|
|
+#include <drm/drm_file.h>
|
|
+
|
|
+#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 <yuq825@gmail.com> */
|
|
+
|
|
+#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 <yuq825@gmail.com> */
|
|
+
|
|
+#include <drm/drm_prime.h>
|
|
+#include <linux/pagemap.h>
|
|
+#include <linux/dma-mapping.h>
|
|
+
|
|
+#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 <yuq825@gmail.com> */
|
|
+
|
|
+#ifndef __LIMA_OBJECT_H__
|
|
+#define __LIMA_OBJECT_H__
|
|
+
|
|
+#include <drm/drm_gem.h>
|
|
+
|
|
+#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 <linux/slab.h>
|
|
#include <linux/xarray.h>
|
|
|
|
-#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 <drm/gpu_scheduler.h>
|
|
|
|
-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 <linux/delay.h>
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/interrupt.h>
|
|
-#include <linux/reset.h>
|
|
#include <linux/io.h>
|
|
-#include <linux/of.h>
|
|
#include <linux/iopoll.h>
|
|
#include <linux/platform_device.h>
|
|
|
|
@@ -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 <media/cec.h>
|
|
#include <media/cec-notifier.h>
|
|
#include <linux/clk-provider.h>
|
|
-#include <linux/mfd/syscon.h>
|
|
|
|
/* 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 <jbrunet@baylibre.com>
|
|
+
|
|
+#include <linux/clk.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/of_platform.h>
|
|
+#include <linux/reset.h>
|
|
+
|
|
+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 <jbrunet@baylibre.com>");
|
|
+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 <jbrunet@baylibre.com>
|
|
+
|
|
+#include <linux/bitfield.h>
|
|
+#include <linux/clk.h>
|
|
+#include <linux/mfd/syscon.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/of_platform.h>
|
|
+#include <sound/pcm_params.h>
|
|
+#include <sound/soc.h>
|
|
+#include <sound/soc-dai.h>
|
|
+
|
|
+#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 <jbrunet@baylibre.com>");
|
|
+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 <jbrunet@baylibre.com>
|
|
+ */
|
|
+
|
|
+#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 <jbrunet@baylibre.com>
|
|
+
|
|
+#include <linux/bitfield.h>
|
|
+#include <linux/clk.h>
|
|
+#include <linux/mfd/syscon.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/of_platform.h>
|
|
+#include <sound/pcm_params.h>
|
|
+#include <sound/soc.h>
|
|
+#include <sound/soc-dai.h>
|
|
+
|
|
+#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 <jbrunet@baylibre.com>");
|
|
+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 <jbrunet@baylibre.com>
|
|
+
|
|
+#include <linux/bitfield.h>
|
|
+#include <linux/clk.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/of_platform.h>
|
|
+#include <sound/pcm_params.h>
|
|
+#include <sound/soc.h>
|
|
+#include <sound/soc-dai.h>
|
|
+
|
|
+#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 <jbrunet@baylibre.com>");
|
|
+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 <jbrunet@baylibre.com>
|
|
+
|
|
+#include <linux/bitfield.h>
|
|
+#include <linux/clk.h>
|
|
+#include <linux/mfd/syscon.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/of_platform.h>
|
|
+#include <sound/pcm_params.h>
|
|
+#include <sound/pcm_iec958.h>
|
|
+#include <sound/soc.h>
|
|
+#include <sound/soc-dai.h>
|
|
+
|
|
+#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 <jbrunet@baylibre.com>");
|
|
+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 <jbrunet@baylibre.com>
|
|
+
|
|
+#include <linux/clk.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/of_platform.h>
|
|
+#include <sound/pcm_params.h>
|
|
+#include <sound/soc.h>
|
|
+#include <sound/soc-dai.h>
|
|
+
|
|
+#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 <jbrunet@baylibre.com>");
|
|
+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 <sound/soc-dai.h>
|
|
|
|
#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 <jbrunet@baylibre.com>
|
|
+
|
|
+#include <linux/module.h>
|
|
+#include <linux/of_platform.h>
|
|
+#include <sound/soc.h>
|
|
+#include <sound/soc-dai.h>
|
|
+
|
|
+#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 <jbrunet@baylibre.com>");
|
|
+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 <jbrunet@baylibre.com>
|
|
+
|
|
+#include <linux/module.h>
|
|
+#include <linux/of_platform.h>
|
|
+#include <sound/soc.h>
|
|
+
|
|
+#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 <jbrunet@baylibre.com>");
|
|
+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 <jbrunet@baylibre.com>
|
|
+ */
|
|
+
|
|
+#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;
|
|
}
|
|
|