I recently picked up a Microsoft Surface Pro X ARM64 tablet, specifically the 16GB SQ2 (the second iteration of Microsoft's version of the Qualcomm 8cx Gen 2 ARM SoC. In my initial evaluation, I was impressed by the usual Surface qualities: the screen, the decent typing experience of the type cover without the weight of Apple's Magic Keyboard for the iPad. However, as many have noted, the ARM+Windows experience isn't the greatest. This is especially evident when trying to run power-hungry x86-64 applications.
I had checked on the state of support for the SQ2 version of the Pro X under Linux, and things mostly are functional, with a few sharp edges. Folks (especially the amazing @qzed) have put a ton of effort into the development of the support drivers for this machine. The main blocker to this being usable as a daily driver machine is the lack of audio. Neither the speakers nor the built-in microphones work. At the level of annoying, rather than being a true blocker, is the sometimes problem of the eDP interface not syncing properly on boot, causing the display to remain black after the transition from the GRUB-initiated dumb framebuffer mode to the activation of the Qualcomm Adreno graphics. For some reason, I tend to hit this problem more often than some others seem to, but I just reboot until things sync properly.
I have been using LLMs as code assistance when writing Ansible playbooks for almost two years now, and I have found them to be genuinely helpful. So, given the success that others have had with having LLMs assist with emulator development, I thought that it would be worth the effort to try having Claude Sonnet (the mid-level option in Anthropic's LLM portfolio) work with me to try to create the devicetree support for the audio.
As it turned out, this was a good path to take. I did encounter a bug in the existing wcd934x sound CODEC, which has already been fixed upstream, but haven't made it to the linux-surface kernel yet. The start of the work was significantly sped up by another SPX user (@jiringgot) who commented that the underlying hardware in the Surface Pro X is somewhat similar to that in the Lenovo Flex 5g, in the open issue on sound support in the linux-surface/surface-pro-x repository. It turns out that the SPX is very similar to the Flex 5g. They use the same WCD9340 CODEC, hooked up to the same SLIMbus, with a very similar speaker and amp configuration. Given this, I started feeding data to Claude, and doing the requisite (and long, and repetitive) testing on my SPX. I also helped the LLM get past some internal inconsistencies with how it was seeing my data, but overall, Claude did the heavy lifting, and I did the testing and evaluation.
What we ended up with is this:
- the speakers work
- they are currently limited to 16bit audio @ 48kHz. 24bit samples come out silent, so WirePlumber forces 16 bit
- there is no speaker protection, and so could be damaged by turning the levels up too high
- the soundwire master and the amps can't currently suspend properly. there is a workaround udev rule that pins them on.
- the mics work
- there are 2 digital mics built into the SPX, and they are both powered from MIC BIAS1
- there has been no effort to make USB-C headphones or displayport audio work. bluetooth audio has already been reported to work, but I haven't verified this.
The details of what has been created are below, after a more detailed description of the hardware.
The audio path is a Qualcomm WCD9340 codec on SLIMbus, two WSA881x-family speaker amplifiers on the codec's SoundWire master, and two digital microphones on the codec's DMIC0 and DMIC1 inputs. It closely follows the Lenovo Flex 5G devicetree in the Snapdragon 8cx Mainline repository, which the nodes below are modeled on.
| Part | What it is | How it was established |
|---|---|---|
| Codec | WCD9340 ("Aqstic") on SLIMbus, address 217:250:1:0, SLIM controller at 0x171c0000 | Windows ACPI (the SLM1 device), the codec name in Windows' calibration database, and Linux enumerating it ("WCD934x chip id major 0x108, minor 0x1") |
| Audio DSP | ADSP booted from qcom/msft/surface/pro-x-sq2/qcadsp8180.mbn | Already booted by remoteproc before this work. APR/q6 audio services hang off its glink edge |
| Speaker amps | Two WSA881x-family devices, SoundWire ID 0217:2110, unique IDs 3 and 4 | Windows driver config (two slaves, speaker-protection parameters) and Linux SoundWire enumeration |
| Microphones | Two digital mics on DMIC0 and DMIC1, both powered from MIC BIAS1 | Measured: only MIC BIAS1 makes either mic respond |
| Codec reset | TLMM GPIO 143 | Windows ACPI |
| Codec interrupt | TLMM GPIO 123 | Copied from the Flex 5G; Windows lists the line as a pin that does not map to a TLMM number, but the interrupt count rises during playback |
| Codec clock | RPMh LN_BB_CLK2 | Windows platform power table (BBCLK2) and the Flex 5G devicetree |
| Amp rail | LDO14 at 1.8 V | Windows platform power table and the Flex 5G devicetree |
| Amp power-down and battery-rail pins | Codec GPIOs 1 and 2 (power-down), 4 (battery rail) | Copied from the Flex 5G; they work, but were not independently confirmed |
There are seven total changes needed to get things working. Five of them are devicetree patches, one is a new file (sc8180x-wcd9340.dtsi, taken from the 8cx-mainline repo), and one is a minor fix to an already upstream-fixed issue that hasn't made it to the linux-surface kernel tree yet.
- 0000-arm64-dts-qcom-sc8180x-add-adsp-audio-and-slimbus.patch
- adds the SLIMbus BAM and controller (slim-ngd@171c0000), the APR/q6 audio service nodes under the ADSP glink edge, and an empty sound node. Values come from the sc8180x-mainline fork and match the stream IDs in the Windows IORT table (0x1b46 and 0x1b21). The linux-surface tree's sc8180x.dtsi had none of these.
- sc8180x-wcd9340.dtsi
- copy this file from the sc8180x-mainline fork into arch/arm64/boot/dts/qcom/. It describes the codec under the SLIMbus controller, its SoundWire master with per-port parameters, its GPIO block, the interrupt (TLMM 123) and 1.8 V mic bias values.
- 0001-arm64-dts-qcom-sc8180x-surface-pro-x-add-audio-codec.patch
- modifies the file sc8180x-surface-pro-x.dts which adds the q6 front-end DAIs, the codec properties (reset on TLMM 143, 1.8 V supply) and the sound card with its MultiMedia and SLIMbus links.
- 0002-arm64-dts-qcom-sc8180x-surface-pro-x-add-speaker-amps.patch
- defines the speakers, an LDO14 regulator at 1.8 V, a fixed regulator for the amplifier battery rail (codec GPIO 4), two SoundWire speaker nodes at addresses 0,3 and 0,4, and the routes from SPK1 OUT and SPK2 OUT.
- 0004-arm64-dts-qcom-sc8180x-surface-pro-x-use-sdm845-sndcard.patch
- sets the sound system to bind to the sdm845 (the Snapdragon 845's sound card) since the sm8250 doesn't handle SLIMbus
- 0005-arm64-dts-qcom-sc8180x-surface-pro-x-add-dmic-bias-route.patch
- configures the two microphone routes to their single bias MIC BIAS1
- 0008-ASoC-codecs-wcd934x-fix-typo-in-dt-parsing.patch
- the aforementioned simple fix that can go away when linux-surface merges the fix described here that has already gone into the mainline kernel
Once these patches are applied and the kernel rebuilt and installed, providing the updated module and the dtb, you will need to run the installer script install-surface-pro-x-audio-v6.sh, which will install the UCM profile, create the udev rules needed to avoid the suspend problem, and create the WirePlumber rules.
I am testing here on Arch, as that is where most of the work to create a running Surface Pro X has been directed, and a nice installation image creator has been published here. My patch to the existing linux-surface kernel PKGBUILD is this:
[ajacocks@surfaceprox aarch64-packages]$ diff -u linux-surface/PKGBUILD linux-surface-patched/PKGBUILD
--- linux-surface/PKGBUILD 2026-10-04 01:59:33.926999697 -0400
+++ linux-surface-patched/PKGBUILD 2026-10-06 02:27:56.861225699 -0400
@@ -4,7 +4,7 @@
pkgbase="linux-surface"
pkgver="6.18.3"
-pkgrel="1"
+pkgrel="1.0002"
_pkgdesc="Linux Surface"
@@ -22,12 +22,26 @@
"surface.config::https://raw.githubusercontent.com/qzed/aarch64-kernel-configs/${_ref_config_surface}/fragments/spx.config"
"overrides.config"
"linux-surface.preset"
+ sc8180x-wcd9340.dtsi
+ 0000-arm64-dts-qcom-sc8180x-add-adsp-audio-and-slimbus.patch
+ 0001-arm64-dts-qcom-sc8180x-surface-pro-x-add-audio-codec.patch
+ 0002-arm64-dts-qcom-sc8180x-surface-pro-x-add-speaker-amps.patch
+ 0004-arm64-dts-qcom-sc8180x-surface-pro-x-use-sdm845-sndcard.patch
+ 0005-arm64-dts-qcom-sc8180x-surface-pro-x-add-dmic-bias-route.patch
+ 0008-ASoC-codecs-wcd934x-fix-typo-in-dt-parsing.patch
)
sha256sums=('548fb5c7b626e955af82c77ee6fdd9437f6a5383ac962f825d40b71186ebb3cb'
'7224624cb815e7c4d989cea2d8c6abf25f02c280db66ce28e023e12cb177965f'
'6769de357507321f4332b1bbd20f4e3fd4460790f2ef4728d49ab9e2634f085a'
'f8041d666e2775b2e36d243708b0fae574914a0ce2a4e260c19127932f3d072e'
- '93a04085e8cebbd5feab98482d764b515b30dd95945d33117d1cd27ffe03e3e7')
+ '93a04085e8cebbd5feab98482d764b515b30dd95945d33117d1cd27ffe03e3e7'
+ '1a468e0be9874dabd85e561809a006a79fa2eea3c7a74b8865d84d62c3c2ab14'
+ '7b1b92784ead83498a93653b76a9c7ae28d967c2f7b128a85b2af329e5aa6fd6'
+ '66ba391aee3ef39d2c61adc8d2fa403dd10e06a5f5926d543cc6d0aaf1d15ad4'
+ '39267677172f730aba56f842721027184fc2ac190d06e049a610a6d4bb314cd6'
+ '36c2e431f7fbd3a624defe3d83e2f83fcf2031b158622d9cf75f9107f1befc57'
+ '8938804b6d6a4f03a61b1accccd69e1d6f97e5413deac0704884a5bbf4defc39'
+ '62458c6ed87b19673b091516ba801ec98ab904fbfbb800b7970be831ece364f4')
prepare() {
cd "${srcdir}/kernel"
@@ -41,6 +55,24 @@
msg2 "Configuring..."
./scripts/kconfig/merge_config.sh -m "${srcdir}/base.config" "${srcdir}/surface.config" "${srcdir}/overrides.config"
+
+ msg2 "Applying patches..."
+ for patchfile in \
+0000-arm64-dts-qcom-sc8180x-add-adsp-audio-and-slimbus.patch \
+0001-arm64-dts-qcom-sc8180x-surface-pro-x-add-audio-codec.patch \
+0004-arm64-dts-qcom-sc8180x-surface-pro-x-use-sdm845-sndcard.patch \
+0002-arm64-dts-qcom-sc8180x-surface-pro-x-add-speaker-amps.patch \
+0005-arm64-dts-qcom-sc8180x-surface-pro-x-add-dmic-bias-route.patch \
+0008-ASoC-codecs-wcd934x-fix-typo-in-dt-parsing.patch \
+; do
+ msg2 " ${patchfile}"
+ patch -p1 < "${srcdir}/${patchfile}"
+ done
+
+ msg2 "Adding missing dtsi..."
+ cp "${srcdir}/sc8180x-wcd9340.dtsi" arch/arm64/boot/dts/qcom/
+ msg2 " cp ${srcdir}/sc8180x-wcd9340.dtsi arch/arm64/boot/dts/qcom/"
+
make olddefconfig
}
@@ -59,7 +91,7 @@
unset LDFLAGS
unset CFLAGS
- make all modules dtbs
+ make -j5 all modules dtbs
}
_package() {
The patches themselves are linked in the "Support for Audio Subsystem" issue #21 in the linux-surface/surface-pro-x repo. I'll create a fork of the https://github.com/linux-surface/aarch64-packages repo and submit the patches, though I don't know if they'll be acceptable or not.
I'm curious to hear what anyone who might read this thinks of this kind of AI assisted development. Definitely, the work wouldn't have been done by me without the work of Claude Sonnet, but Claude wouldn't have gotten anything done without my input, testing and evaluation, either. In the end, the patches to the devicetree are based on the work of Srinivas Kandagatla and the report by Joel Selvaraj and the existing archived 8cx mainline tree, but integrated by Claude. The PKGBUILD changes are mine. I have tested them pretty significantly on my own hardware, but of course, there's no guarantee that they will work on yours.