[PATCH 5/5] arm64: dts: imx8mq-phanbell: Enable the rt5645 analog audio

Rudi Heitbaum rudi at heitbaum.com
Fri Aug 7 06:32:44 PDT 2026


The board has an rt5645 on i2c3 driving the 3.5 mm headphone jack, its
microphone and two on-board DMICs, fed by sai2.

The vendor kernel drives this codec from a machine driver of its own,
"google,edgetpu-audio-card", so its devicetree carries no widgets or
routing and the graph here is rebuilt for simple-audio-card. micbias1 is a
supply widget, and a supply is the source of a route rather than its sink -
as rt5645's own { "IN1P", NULL, "LDO2" } has it - so the sink/source pair
is "Headphone Mic", "micbias1".

GPIO5_IO4 carries headphone detect. The pad has to be muxed away from its
reset function, or both hp-detect-gpios and the codec interrupt point at a
pad that is still SPDIF_RX. Leaving realtek,jd-mode unset selects the mode
in which the driver takes jack state from hp-detect-gpios.

The slot width is pinned at 32 bits. With none set fsl_sai_hw_params()
takes it from the sample width, so a 24 bit open asks for a bit clock of
48000 * 2 * 24 = 2.304 MHz; fsl_sai_set_bclk() only accepts even dividers
and 24.576 MHz / 2.304 MHz is 10.67, so hw_params returns -EINVAL. 32 bit
slots ask for 3.072 MHz, a ratio of 8, and still carry 16 bit at the same
clock: rt5645_hw_params() takes the data length from params_width() and
only writes a BCLK-to-frame ratio for AIF2, so on AIF1 the codec ignores
the padding bits.

Declare both audio PLL families. fsl_sai only moves its mclk between the
8 kHz and 11.025 kHz families if the node names them as pll8k and pll11k,
which fsl_asoc_reparent_pll_clocks() then selects between by requested
rate. Without them the mclk is stuck at whatever assigned-clock-rates set
while 44100 is still on the advertised rate list, so ALSA is offered a rate
the clock cannot produce, does not resample, and hw_params fails:
44100 * 2 * 32 = 2.8224 MHz, and 24.576 MHz divided by that is 8.707.

The cpu dai also needs system-clock-direction-out. Without it
simple_util_parse_clk() leaves clk_direction at 0, which is
SND_SOC_CLOCK_IN, and fsl_sai_set_dai_sysclk() opens with
"if (dir == SND_SOC_CLOCK_IN) return 0". So set_sysclk returns without
setting the mclk rate and without ever reaching the reparent, silently and
at every rate.

Signed-off-by: Rudi Heitbaum <rudi at heitbaum.com>
---
 .../boot/dts/freescale/imx8mq-phanbell.dts    | 84 +++++++++++++++++++
 1 file changed, 84 insertions(+)

diff --git a/arch/arm64/boot/dts/freescale/imx8mq-phanbell.dts b/arch/arm64/boot/dts/freescale/imx8mq-phanbell.dts
index 2d134eefb765..b819e644fa54 100644
--- a/arch/arm64/boot/dts/freescale/imx8mq-phanbell.dts
+++ b/arch/arm64/boot/dts/freescale/imx8mq-phanbell.dts
@@ -46,6 +46,41 @@ fan: gpio-fan {
 		pinctrl-0 = <&pinctrl_gpio_fan>;
 		status = "okay";
 	};
+
+	sound-analog {
+		compatible = "simple-audio-card";
+		simple-audio-card,name = "Coral Analog";
+		simple-audio-card,format = "i2s";
+		simple-audio-card,bitclock-master = <&sound_analog_cpu>;
+		simple-audio-card,frame-master = <&sound_analog_cpu>;
+		simple-audio-card,mclk-fs = <256>;
+
+		simple-audio-card,widgets = "Headphone", "Headphone Jack",
+					    "Microphone", "Headphone Mic",
+					    "Microphone", "Internal Mic";
+
+		simple-audio-card,routing = "Headphone Jack", "HPOL",
+					    "Headphone Jack", "HPOR",
+					    "Headphone Mic", "micbias1",
+					    "DMIC L1", "Internal Mic",
+					    "DMIC R1", "Internal Mic";
+
+		/*
+		 * 32 bit slots regardless of the sample width. sai2 divides its
+		 * 24.576 MHz root by an even integer only, so a 24 bit frame at
+		 * 48 kHz would ask for 2.304 MHz, a ratio of 10.67.
+		 */
+		sound_analog_cpu: simple-audio-card,cpu {
+			sound-dai = <&sai2>;
+			system-clock-direction-out;
+			dai-tdm-slot-num = <2>;
+			dai-tdm-slot-width = <32>;
+		};
+
+		simple-audio-card,codec {
+			sound-dai = <&rt5645>;
+		};
+	};
 };
 
 &A53_0 {
@@ -272,6 +307,18 @@ &i2c3 {
 	pinctrl-names = "default";
 	pinctrl-0 = <&pinctrl_i2c3>;
 	status = "okay";
+
+	rt5645: audio-codec at 1a {
+		compatible = "realtek,rt5645";
+		reg = <0x1a>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&pinctrl_hp_det>;
+		#sound-dai-cells = <0>;
+		interrupt-parent = <&gpio5>;
+		interrupts = <4 IRQ_TYPE_EDGE_BOTH>;
+		hp-detect-gpios = <&gpio5 4 GPIO_ACTIVE_HIGH>;
+		realtek,dmic1-data-pin = <2>;
+	};
 };
 
 &fec1 {
@@ -295,6 +342,27 @@ ethphy0: ethernet-phy at 0 {
 	};
 };
 
+&sai2 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_sai2>;
+	/*
+	 * pll8k and pll11k let fsl_sai move the mclk between the two
+	 * audio PLL families, so 44.1 kHz rates work as well as 48 kHz.
+	 */
+	clocks = <&clk IMX8MQ_CLK_SAI2_IPG>,
+		 <&clk IMX8MQ_CLK_SAI2_ROOT>,
+		 <&clk IMX8MQ_CLK_DUMMY>, <&clk IMX8MQ_CLK_DUMMY>,
+		 <&clk IMX8MQ_AUDIO_PLL1_OUT>,
+		 <&clk IMX8MQ_AUDIO_PLL2_OUT>;
+	clock-names = "bus", "mclk1", "mclk2", "mclk3",
+		      "pll8k", "pll11k";
+	assigned-clocks = <&clk IMX8MQ_CLK_SAI2>;
+	assigned-clock-parents = <&clk IMX8MQ_AUDIO_PLL1_OUT>;
+	assigned-clock-rates = <24576000>;
+	fsl,sai-mclk-direction-output;
+	status = "okay";
+};
+
 &uart1 {
 	pinctrl-names = "default";
 	pinctrl-0 = <&pinctrl_uart1>;
@@ -383,6 +451,12 @@ MX8MQ_IOMUXC_GPIO1_IO00_ANAMIX_REF_CLK_32K	0x05
 		>;
 	};
 
+	pinctrl_hp_det: hpdetgrp {
+		fsl,pins = <
+			MX8MQ_IOMUXC_SPDIF_RX_GPIO5_IO4			0xd6
+		>;
+	};
+
 	pinctrl_i2c1: i2c1grp {
 		fsl,pins = <
 			MX8MQ_IOMUXC_I2C1_SCL_I2C1_SCL			0x4000007f
@@ -410,6 +484,16 @@ MX8MQ_IOMUXC_GPIO1_IO03_GPIO1_IO3	0x41
 		>;
 	};
 
+	pinctrl_sai2: sai2grp {
+		fsl,pins = <
+			MX8MQ_IOMUXC_SAI2_MCLK_SAI2_MCLK		0xd6
+			MX8MQ_IOMUXC_SAI2_TXFS_SAI2_TX_SYNC		0xd6
+			MX8MQ_IOMUXC_SAI2_TXC_SAI2_TX_BCLK		0xd6
+			MX8MQ_IOMUXC_SAI2_TXD0_SAI2_TX_DATA0		0xd6
+			MX8MQ_IOMUXC_SAI2_RXD0_SAI2_RX_DATA0		0xd6
+		>;
+	};
+
 	pinctrl_uart1: uart1grp {
 		fsl,pins = <
 			MX8MQ_IOMUXC_UART1_RXD_UART1_DCE_RX		0x49
-- 
2.53.0




More information about the linux-arm-kernel mailing list