Skip to content

Surface 2 / Surface RT: WM8962 silent — the I2S and MCLK pins are never muxed (MUX UNCLAIMED) #141

Closed

Description

@zefr0g

Follow-up to #140 and #108, from the Surface 2 (Tegra 4 / T114) side. Audio is silent there too, and I found something that I do not think has been raised in either thread: the I2S and MCLK pins are never muxed at all.

Kernel: linux-postmarketos-grate 7.0.1 (libre-tegra stable), Raspbian 12 userland, booted via UEFI + EFI stub.

The finding

tegra114-microsoft-surface-2-common.dtsi contains no audio pinmux entries whatsoever, and the pins confirm it at runtime:

# /sys/kernel/debug/pinctrl/70000868.pinmux/pinmux-pins
pin 2   (DAP2_FS PA2):    (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 3   (DAP2_SCLK PA3):  (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 4   (DAP2_DIN PA4):   (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 5   (DAP2_DOUT PA5):  (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 180 (CLK1_OUT PW4):   (MUX UNCLAIMED) (GPIO UNCLAIMED)

The card is on 70080400.i2s = tegra_i2s1, i.e. DAP2, and MCLK is pmc clk_out_1 = CLK1_OUT PW4 (extperiph1). Counting audio pin entries per board:

board audio pin entries
tegra114-microsoft-surface-2-common.dtsi 0
tegra30-microsoft-surface-rt.dts 0
tegra114-dalmore.dts 26
tegra114-asus-tf701t.dts 17

So the SoC generates MCLK (pmc_clk_out_1 at 12.288 MHz, verified in clk_summary) and the I2S controller DMAs happily (hw_ptr advancing), but neither the clock nor the data can physically reach the codec. That is consistent with a codec whose registers all look perfect over I2C while nothing comes out, and with the headphone DC servo timing out, since the DC servo needs SYSCLK.

Adding the pins (values copied from dalmore) makes them claim correctly:

pin 2 (DAP2_FS PA2): 70000868.pinmux (GPIO UNCLAIMED) (HOG) function i2s1 group dap2_fs_pa2
pin 180 (CLK1_OUT PW4): 70000868.pinmux (GPIO UNCLAIMED) (HOG) function extperiph1 group clk1_out_pw4

Honesty first: this did NOT restore audio on my Surface 2. Still silent on speakers and headphones. So it is either not the only fault, or the pins were already correct in hardware and pinctrl simply was not describing them. But given #140 reports the same silence on Surface RT, and Surface RT's DTS has the same omission, it seemed worth putting in front of people who know the hardware.

Confirming @digetx's point in #140

both 5v and 1.8v regulators are defined as always-on in device-tree and driver doesn't care about actual voltages, hence the change shall have no practical effect

Confirmed experimentally. Surface 2's DTS points all eight wm8962 supplies at a stub_audio_reg placeholder (regulator-fixed, no voltage, no parent, with a // TODO check regulators comment). I repointed them at the real vdd-1v8 rail (tps65913_smps8) — no change, exactly as predicted, because that rail is already regulator-always-on.

I also tested the related theory that Linux switches off what UEFI enabled: rebuilt with regulator_ignore_unused, which does change behaviour (SYSEN1 stays enabled where it was previously disabled on every boot) — but audio remained silent.

Ruled out on Surface 2, so nobody repeats it

  • MCLK chain verified live while streaming: pll_a 368.64 MHz → pll_a_out0/extern1/pmc_clk_out_1 12.288 MHz, i2s1 3.072 MHz
  • Codec registers correct: R26=0x01f8 (DACs + HP/SPK PGAs powered), R49=0x00c3 (Class-D enabled), R5=0x0010 (DAC not muted)
  • DAPM path complete on both outputs (Int Spk: On in 2, Headphone Jack: On in 4); SPKOUTL PGA is a mux taking DACL directly, so R99=0 is expected rather than a fault
  • Jack detection correct, volume keys work (both GPIO paths — they work precisely because they do not use the I2S pins)
  • TPS65090 FET3 and FET7 enabled at runtime via i2c: POWER_GOOD never asserts, so nothing is behind them
  • wm8962 GPIOs 2/3/5/6 driven to logic-output-high at runtime (0x0041, per wm8962_gpio_direction_out): no effect, so nothing obvious gates an external amp
  • The codec's own beep generator, routed internally to the DACs with the whole chain powered, is also inaudible — which is what makes me think the remaining fault is analogue rather than anything in the digital/routing path

I deliberately did not touch palmas LDO4: it is vpp-fuse on the Tegra 4 reference design, and a mistake there is irreversible.

Question

Has anyone ever had wm8962 audio working on either Surface RT or Surface 2? #140 suggests not. If it has never worked on either, that reframes this as an unfinished port rather than a per-unit fault, and the pinmux omission above might be worth checking on Surface RT hardware by someone who has one.

Also happy to send patches for three unrelated Surface 2 device-tree fixes found along the way, all confirmed working on hardware, if they would be useful:

  1. ti,palmas-override-powerhold on the palmas node — without it the machine halts but never powers down (GPIO7 POWERHOLD outranks the DEV_ON bit), requiring a 10-second hardware force-off
  2. microSD slot: sdmmc3 enabled with its supplies, and its pins taken back from uarta (upstream currently trades the SD slot for a serial console)
  3. ti,tps65090-charger subnode — the driver was probing and failing with "no platform data available" (already present in 7.0.1; the fix applies to older DTBs)

Happy to test anything on Surface 2 hardware.

Activity

  1. zefr0g commented on Sep 4, 2026

    @zefr0g
    ContributorAuthor

    Patches for the two confirmed fixes mentioned above, against libre-tegra stable (7.0.1). Both tested on Surface 2 hardware. The third (audio pins/supplies) is described in the issue body — happy to send it too, but it does not fix audio, so it is descriptive-correctness only.

    1. Power-off

    diff --git a/arch/arm/boot/dts/nvidia/tegra114-microsoft-surface-2-common.dtsi b/arch/arm/boot/dts/nvidia/tegra114-microsoft-surface-2-common.dtsi
    index 2d3358131..0a635f932 100644
    --- a/arch/arm/boot/dts/nvidia/tegra114-microsoft-surface-2-common.dtsi
    +++ b/arch/arm/boot/dts/nvidia/tegra114-microsoft-surface-2-common.dtsi
    @@ -316,6 +316,7 @@ palmas: pmic@58 {
     
     			ti,system-power-controller;
     			ti,power-ctrl;
    +			ti,palmas-override-powerhold;
     
     			palmas_gpio: gpio {
     				compatible = "ti,palmas-gpio";

    2. microSD slot

    diff --git a/arch/arm/boot/dts/nvidia/tegra114-microsoft-surface-2-common.dtsi b/arch/arm/boot/dts/nvidia/tegra114-microsoft-surface-2-common.dtsi
    index 0a635f932..e6efa1e1f 100644
    --- a/arch/arm/boot/dts/nvidia/tegra114-microsoft-surface-2-common.dtsi
    +++ b/arch/arm/boot/dts/nvidia/tegra114-microsoft-surface-2-common.dtsi
    @@ -54,22 +54,26 @@ pinmux@70000868 {
     		pinctrl-0 = <&state_default>;
     
     		state_default: pinmux {
    -			/* UART A RX ??? */
    -			sdmmc3_cmd_pa7 {
    -				nvidia,pins = "sdmmc3_cmd_pa7";
    -				nvidia,function = "uarta";
    -				nvidia,pull = <TEGRA_PIN_PULL_UP>;
    -				nvidia,tristate = <TEGRA_PIN_ENABLE>;
    +			sdmmc3_clk_pa6 {
    +				nvidia,pins = "sdmmc3_clk_pa6";
    +				nvidia,function = "sdmmc3";
    +				nvidia,pull = <TEGRA_PIN_PULL_NONE>;
    +				nvidia,tristate = <TEGRA_PIN_DISABLE>;
     				nvidia,enable-input = <TEGRA_PIN_ENABLE>;
     			};
     
    -			/* UART A TX */
    -			sdmmc3_dat1_pb6 {
    -				nvidia,pins = "sdmmc3_dat1_pb6";
    -				nvidia,function = "uarta";
    -				nvidia,pull = <TEGRA_PIN_PULL_NONE>;
    +			sdmmc3_cmd_pa7 {
    +				nvidia,pins = "sdmmc3_cmd_pa7",
    +						"sdmmc3_dat0_pb7",
    +						"sdmmc3_dat1_pb6",
    +						"sdmmc3_dat2_pb5",
    +						"sdmmc3_dat3_pb4",
    +						"sdmmc3_clk_lb_out_pee4",
    +						"sdmmc3_clk_lb_in_pee5";
    +				nvidia,function = "sdmmc3";
    +				nvidia,pull = <TEGRA_PIN_PULL_UP>;
     				nvidia,tristate = <TEGRA_PIN_DISABLE>;
    -				nvidia,enable-input = <TEGRA_PIN_DISABLE>;
    +				nvidia,enable-input = <TEGRA_PIN_ENABLE>;
     			};
     
     			panel-backlight {
    @@ -564,12 +568,11 @@ wifi@1 {
     
     	/* SD - SDMMC-3 */
     	sdmmc3: mmc@78000400 {
    -		status = "disabled"; // disable uart in UEFI stub
    +		status = "okay";
     		bus-width = <4>;
     		cd-gpios = <&gpio TEGRA_GPIO(V, 2) GPIO_ACTIVE_LOW>;
    -		// Need to recheck if SD works but UART is more important for now
    -		// vqmmc-supply = <&tps65913_ldo9>; // io voltage
    -		// vmmc-supply = <&tps65090_fet6>; // card power
    +		vqmmc-supply = <&tps65913_ldo9>; /* io voltage */
    +		vmmc-supply = <&tps65090_fet6>;  /* card power, "vdd-sd-slot" */
     	};
     
     	/* eMMC - SDMMC-4 */
  2. zefr0g commented on Sep 4, 2026

    @zefr0g
    ContributorAuthor

    Solved — it is a wm8962 codec-driver bug, not the device tree, not the pins, not the rails. Sound confirmed on Surface 2, and confirmed again after a clean boot with the patched kernel: no DC servo timed out at all, SYSCLK_ENA/CP_ENA read 1 during playback, DC servo completes.

    What is wrong

    wm8962_configure_bclk() (mainline sound/soc/codecs/wm8962.c, unchanged) provisionally sets SYSCLK_ENA so that the read-only DSPCLK_DIV field becomes valid, then clears it unconditionally whenever the bias level is below SND_SOC_BIAS_ON:

    	if (snd_soc_dapm_get_bias_level(dapm) != SND_SOC_BIAS_ON)
    		snd_soc_component_update_bits(component, WM8962_CLOCKING2,
    				WM8962_SYSCLK_ENA_MASK, WM8962_SYSCLK_ENA);
    	...
    	if (snd_soc_dapm_get_bias_level(dapm) != SND_SOC_BIAS_ON)
    		snd_soc_component_update_bits(component, WM8962_CLOCKING2,
    				WM8962_SYSCLK_ENA_MASK, 0);

    That bit is owned by the SYSCLK DAPM supply widget — and wm8962_mic_detect() force-enables that pin. The Tegra machine driver calls wm8962_mic_detect(), so on Surface RT / Surface 2 DAPM writes SYSCLK_ENA = 1 once at card init and, because the widget never changes power state again, never writes it again. The first stream's transition to BIAS_PREPARE calls configure_bclk(), which clears the bit behind DAPM's back. From then on every stream plays with SYSCLK off: DACs do not run, CP_ENA refuses to latch, hp_event() reports DC servo timed out, and the codec is silent although every register DAPM believes in looks perfect.

    Only boards that call wm8962_mic_detect() are affected — i.e. the Tegra machine driver — which is exactly why it is "both Surfaces, nobody else".

    How it was found

    Diffed the driver's regmap cache (/sys/kernel/debug/regmap/2-001a/registers) against raw I2C reads of the chip during playback: R8 = 0x09C7 in both, i.e. SYSCLK_ENA = 0, while debugfs/asoc/.../dapm/SYSCLK says On (forced) … R8 mask 0x20. Forcing bit 5 of R8 over I2C during a stream: CP_ENA latches immediately, the DC servo completes in 56 ms, and there is sound — first time ever under Linux on this unit. So MCLK does reach the codec, the analogue supplies are fine, and DAP2/I2S1 is correct.

    Side note for anyone reading the earlier posts: the "registers look correct" checks were reading the cache, not the chip.

    Fix

    --- a/sound/soc/codecs/wm8962.c
    +++ b/sound/soc/codecs/wm8962.c
    @@ static void wm8962_configure_bclk(struct snd_soc_component *component)
     	int clocking2 = 0;
     	int clocking4 = 0;
     	int aif2 = 0;
    +	bool sysclk_was_ena;
    @@
    -	if (snd_soc_dapm_get_bias_level(dapm) != SND_SOC_BIAS_ON)
    +	sysclk_was_ena = snd_soc_component_read(component, WM8962_CLOCKING2) &
    +			 WM8962_SYSCLK_ENA;
    +	if (!sysclk_was_ena &&
    +	    snd_soc_dapm_get_bias_level(dapm) != SND_SOC_BIAS_ON)
     		snd_soc_component_update_bits(component, WM8962_CLOCKING2,
     				WM8962_SYSCLK_ENA_MASK, WM8962_SYSCLK_ENA);
    @@
    -	if (snd_soc_dapm_get_bias_level(dapm) != SND_SOC_BIAS_ON)
    +	if (!sysclk_was_ena &&
    +	    snd_soc_dapm_get_bias_level(dapm) != SND_SOC_BIAS_ON)
     		snd_soc_component_update_bits(component, WM8962_CLOCKING2,
     				WM8962_SYSCLK_ENA_MASK, 0);

    Full patch with commit message: https://gist.github.com/zefr0g/97ff52172502ccde6a23dfd6c71608fc (against libre-tegra stable 7.0.1; applies to mainline unchanged). I'll send it to alsa-devel as well. Surface RT owners from #140: this should fix you too — the Tegra machine driver is the same.

    Bonus: the Windows ACPI tables

    The firmware's ACPI tables survive in memory under Linux (efi: ACPI 2.0=0xfdf1c014, readable via /dev/mem). The AUDI device confirms the DTS wiring (codec on I2C3/0x1a, hp-det PW2, IRQ PX3) and adds one output-only GPIO for the codec, PR6 / GPIO 142, that no DTS has; UEFI leaves it high and toggling it made no audible difference, but it may be an amp mute worth describing. No ACPI power resources are attached to the audio device, confirming the rails are always-on. Happy to share the decompiled tables.

  3. zefr0g commented on Sep 4, 2026

    @zefr0g
    ContributorAuthor

    Pull request with the codec fix: #142. The three Surface 2 device-tree fixes (power-off, microSD, audio pinmux/supplies) are on https://github.com/zefr0g/linux/tree/surface2-fixes rebased on libre-tegra stable 7.0.1, since this repo's master does not carry the Surface 2 DTS.

  4. wmjb commented on Sep 5, 2026

    @wmjb

    surface 1 / 2 audio always worked fine from 5.17 up until 6.4 kernel. after 6.4 kernel wm8962 was given it's own code file and some incorrect assumptions at pmos were made about it regarding the new file, they were taken from our 3.4 android kernel, which also had the same incorrect assumptions regarding mic switch, but it didn't hang the codec leading to no sound on that kernel. a couple of points regarding both devices surface 1 and 2. there is no mic enable switch in the hardware, the mic are always physically routed to the sound soc. the regulators are hard wired, always on, there is no enabling or disabling them. the fix you have above is correct.. do not set bias on mic, this is legacy hack nonsense from nvidia dev boards, never should it be done in a production board, nvidia have some really bad practices in the early days, probably still do. if the codec is already powered it waits for MICBIAS to stabilise, which it never does and hangs there forever leaving the codec init incomplete. regarding the sd card, that's just left over from using the sdcard as a serial uart for the initial bringup we did of the kernel on this device, none of this was changed on this repo, because we don't use this repo or the pmos kernel to produce the kernels for the surface 1 / 2 at Open-Surface-RT

  5. zefr0g commented on Sep 8, 2026

    @zefr0g
    ContributorAuthor

    Thanks for the reply.
    should i post this patch to https://github.com/Open-Surface-RT/grate-linux ?

  6. digetx commented on Sep 9, 2026

    @digetx
    Member

    Merged the PR. The grate-linux hasn't been updated in a while and the expectation is that upstream kernel should be used instead and grate-linux only contains WIP patches staged for upstreaming. The grate driver also was forked to https://codeberg.org/libre-tegra, I don't know about its status but it looks active and you may get in touch with people there.

  7. digetx commented on Sep 9, 2026

    @digetx
    Member

    See now you already mentioned the libre-tegra. Any reason you haven't contributed fix there?

  8. digetx commented on Sep 9, 2026

    @digetx
    Member

    Again, if the fix is relevant to upstream kernel, i.e. upstream kernel works on your surface2 and has same audio bug, please send fix to upstream.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions