[PATCH 0/3] ASoC: fix audio on the Microsoft Surface Pro 11 (Intel)

Sergey Lebedev posted 3 patches 1 month, 4 weeks ago
There is a newer version of this series
sound/soc/codecs/rt1320-sdw.c       |  2 +-
sound/soc/sdw_utils/soc_sdw_utils.c | 46 +++++++++++++++++++++++++++++
sound/soc/sof/intel/hda.c           | 16 ++++++++++
3 files changed, 63 insertions(+), 1 deletion(-)
[PATCH 0/3] ASoC: fix audio on the Microsoft Surface Pro 11 (Intel)
Posted by Sergey Lebedev 1 month, 4 weeks ago
The Microsoft Surface Pro 11 for Business (Intel, Lunar Lake) has no working
audio under Linux: the speakers are silent, while everything reports success.
These three patches fix it, and the machine then works with the stock
sof-soundwire UCM profile and the shipped topologies - no board quirk, no new
match entry, no local configuration of any kind.

All three failures come from one firmware defect. The board carries a single
physical RT1320 amplifier on SoundWire link 0, and describes it twice:

  SWRA  _ADR 0x000030025D132000   SDCA class 0
  SWRB  _ADR 0x000030025D132001   SDCA class 1

Identical apart from the class id: same link, same manufacturer, part and
version, same unique id 0. The part reports class 1, so only SWRB ever
enumerates. SWRA stays UNATTACHED on every boot, on every firmware version we
have tested, including the November 2025 bundle. Both entries nonetheless reach
the machine-select and card-probe paths, and the phantom breaks each in a
different way.

  1/3 rt1320: the amplifier's preset never runs, because the driver waits for
      FUNCTION_NEEDS_INITIALIZATION and this part never sets it. rt712-sdca and
      rt722-sdca already handle this by also running the preset on the first
      hardware init; rt1320 is the odd one out. One line.

  2/3 sdw_utils: DAI links get built for the phantom, and later fail to prepare
      with -61, taking the whole link down rather than degrading it.

  3/3 SOF/Intel hda: the phantom consumes an amplifier index, so the real part
      is named "rt1320-2". The stock UCM profile addresses the first amplifier
      and therefore enables switches on a device that is not there.

Only 1/3 is codec-specific. The other two are general: any firmware that
describes a peripheral twice, or describes one that never enumerates, hits them.

One thing worth raising, since a reviewer will reasonably ask. Before 2/3
existed we also hit a DAI link name collision: create_sdw_dailink() builds names
from link id and function type alone, so the phantom's SmartMic endpoint
produced a second "SDW0-Capture-SmartMic" and the card failed to register at all
with -EEXIST. With 2/3 applied the phantom's endpoints never reach the naming
code, so that collision is no longer reachable on this machine and we cannot
demonstrate it. The naming scheme is still not unique in general - a board with
two genuinely distinct codecs of the same function type on one link would hit
it - but we have no such board, so it is deliberately not part of this series.
Happy to send it separately if you would like it.

Testing. Developed and tested on a Surface Pro 11 for Business (Intel Core Ultra
7 268V), booting 7.1.0-rc7 built from thesofproject/linux topic/sof-dev at
7e9e0409c with these three patches and nothing else. Verified on that kernel:

  - card registers as sof-soundwire, 4 playback + 1 capture devices
  - no -EEXIST, no -61 link startup errors
  - amplifier named rt1320-1, controls "rt1320-1 OT23 L/R Switch"
  - rt1320_vc_preset runs, reporting RT1320_KR0_INT_READY=0x1f
  - amp function status reads 0x41 (NEWLY_ATTACHED | FUNCTION_HAS_BEEN_RESET)
    on every boot, confirming bit 5 is never set on this part
  - speakers audible, internal microphone captures signal
  - stock alsa-ucm-conf 1.2.15.3 and firmware-sof-signed 2025.12.2, with no
    local UCM, PipeWire or WirePlumber configuration

checkpatch --strict is clean on all three.

Sergey Lebedev (3):
  ASoC: rt1320: run the initialisation preset on the first hardware init
  ASoC: sdw_utils: skip endpoints of a peripheral that is not on the bus
  ASoC: SOF: Intel: hda: duplicate _ADR entries share one amp index

 sound/soc/codecs/rt1320-sdw.c       |  2 +-
 sound/soc/sdw_utils/soc_sdw_utils.c | 46 +++++++++++++++++++++++++++++
 sound/soc/sof/intel/hda.c           | 16 ++++++++++
 3 files changed, 63 insertions(+), 1 deletion(-)


base-commit: 7e9e0409cd57924c4099090879154300c07b8643
-- 
2.50.1 (Apple Git-155)
[PATCH v2 0/2] ASoC: fix audio on the Microsoft Surface Pro 11 (Intel)
Posted by Sergey Lebedev 1 month ago
The Microsoft Surface Pro 11 for Business (Intel, Lunar Lake) has no working
audio under Linux: the speakers are silent while every layer reports success.
These two patches fix it, and the machine then works with the stock
sof-soundwire UCM profile and the shipped topologies - no board quirk, no new
match entry, no local configuration of any kind.

v1 was three patches: https://lore.kernel.org/linux-sound/20260804225853.31585-1-lsa.uz@pm.me/
Two of them are replaced here by a single DMI quirk, which is what Bard Liao
and Pierre-Louis Bossart asked for in review, and which is both smaller and
safer than what it replaces.

The board carries one physical RT1320 amplifier on SoundWire link 0 and
describes it twice:

  SWRA  _ADR 0x000030025D132000   SDCA class 0
  SWRB  _ADR 0x000030025D132001   SDCA class 1

Identical apart from the class id: same link, same manufacturer, part and
version, same unique id 0. The part reports class 1, so only SWRB ever
enumerates. SWRA stays UNATTACHED on every boot and on every firmware version
tested, including the November 2025 bundle, and the firmware is signed vendor
firmware we cannot have corrected at the source.

That ghost broke two things at once. It consumed an amplifier index, so the
real part was named "rt1320-2" and the stock UCM enabled switches on a device
that is not there; and its endpoints reached create_sdw_dailink(), which builds
DAI link names from link id and function type alone, so its SmartMic collided
with the real one and the card failed to register at all with -EEXIST.

v1 fixed those two symptoms in two places, each with its own way of noticing
the ghost. Removing the ghost at enumeration instead fixes both at once and
needs no runtime presence test:

  1/2 rt1320: the amplifier's preset never runs, because the driver waits for
      FUNCTION_NEEDS_INITIALIZATION and this part never sets it. rt712-sdca and
      rt722-sdca already handle this by also running the preset on the first
      hardware init; rt1320 is the odd one out. One line, unchanged from v1.

  2/2 dmi-quirks: remap the ghost _ADR to zero so sdw_acpi_find_slaves() never
      creates the peripheral, as ghost_realtek and global_ghost_adr already do.
      Matched on DMI_PRODUCT_SKU, not the product name, so a later batch with a
      different RT1320 version - and therefore a different _ADR - cannot be
      caught by a remap it was never verified against.

Testing. Surface Pro 11 for Business (Intel Core Ultra 7 268V). Verified on the
machine's own kernel, 7.0.0-30 (Ubuntu 26.04), with 2/2 backported to that
tree: its dmi-quirks.c predates ghost_realtek, but the table entry is identical
and the mechanism is unchanged - slave.c drops a peripheral whose overridden
_ADR is zero in both trees. 1/2 is byte-identical to v1's 1/3, which was built
and booted on 7.1.0-rc7.

  - /sys/bus/soundwire/devices/ shows only sdw:0:0:025d:1320:01; the class-0
    ghost is gone
  - amplifier named rt1320-1, controls "rt1320-1 OT23 L/R Switch"
  - card registers as sof-soundwire, 4 playback + 1 capture
  - no -EEXIST, no -61 link startup errors
  - speakers audible, internal microphone captures signal
  - stock alsa-ucm-conf and firmware-sof-signed, no local configuration
  - Secure Boot enabled with module signature enforcement, no rejections

One thing the review process turned up that is worth recording. The v1 cover
letter said the DAI link name collision was "no longer reachable on this
machine and we cannot demonstrate it". That was wrong: during v2 testing a boot
where the quirk did not take effect reproduced it exactly, and it is fatal.
create_sdw_dailink()'s naming scheme is still not unique in general. This series
does not address that - it removes the ghost before the naming code sees it -
and I am happy to send a separate patch if you would like it fixed.

checkpatch --strict is clean on both.

Sergey Lebedev (2):
  ASoC: rt1320: run the initialisation preset on the first hardware init
  soundwire: dmi-quirks: drop the ghost RT1320 on the Surface Pro 11
    (Intel)

 drivers/soundwire/dmi-quirks.c | 28 ++++++++++++++++++++++++++++
 sound/soc/codecs/rt1320-sdw.c  |  2 +-
 2 files changed, 29 insertions(+), 1 deletion(-)


base-commit: 7e9e0409cd57924c4099090879154300c07b8643
-- 
2.50.1 (Apple Git-155)
Re: [PATCH v2 0/2] ASoC: fix audio on the Microsoft Surface Pro 11 (Intel)
Posted by Mark Brown 1 month ago
On Sun, Aug 30, 2026 at 08:45:05AM +0000, Sergey Lebedev wrote:
> The Microsoft Surface Pro 11 for Business (Intel, Lunar Lake) has no working
> audio under Linux: the speakers are silent while every layer reports success.
> These two patches fix it, and the machine then works with the stock
> sof-soundwire UCM profile and the shipped topologies - no board quirk, no new
> match entry, no local configuration of any kind.

Please don't send new patches in reply to old patches or serieses, this
makes it harder for both people and tools to understand what is going
on - it can bury things in mailboxes and make it difficult to keep track
of what current patches are, both for the new patches and the old ones.
Re: [PATCH v2 0/2] ASoC: fix audio on the Microsoft Surface Pro 11 (Intel)
Posted by Sergey Lebedev 1 month ago
Some context I should have gathered before v1, not after v2. None of it
needs action from you; it is here so nobody spends time twice.

1. An overlapping series is already on the list
-----------------------------------------------

Amaan Lalani sent this on 2026-08-28, two days before my v2:

  [PATCH 0/2] ASoC: fix SoundWire enumeration on Surface Pro 11
  https://lore.kernel.org/linux-sound/BY5PR02MB63701A05A58EF6B4D6CAAC31A0AC2@BY5PR02MB6370.namprd02.prod.outlook.com/

Same machine, and in substance the same two changes as my v1 2/3 and 3/3 -
the name-prefix reuse for _ADR entries differing only in class id, and an
is_peripheral_attached() test on SDW_SLAVE_UNATTACHED. So the review you
already gave me applies to it unchanged: it detects the ghost at runtime
rather than removing it at enumeration.

Their version is better than mine was in two respects worth keeping if that
approach is ever preferred: it hooks both asoc_sdw_count_sdw_endpoints() and
asoc_sdw_parse_sdw_endpoints() rather than only the parse path, and it
carries Fixes: tags.

2. There is a body of downstream work for this machine
------------------------------------------------------

André Gilerson has carried Surface Pro 11 Intel patches in a linux-surface
fork since February - audio, cameras, an IR sensor driver, touchpad, lid
wake, a suspend hang fix. People have been running them daily for months.
None of it has been submitted here.

  https://github.com/linux-surface/linux-surface/pull/1990

His audio fix takes the traditional shape: a DMI-gated machine entry in
soc-acpi-intel-lnl-match.c naming the real peripheral outright, with
name_prefix "rt1320-1" and two endpoints. With such an entry matched, the
generic find_acpi_adr_device() path is never reached, so the ghost never
gets the chance to consume an amp index.

3. The part that is actually interesting: the generic path is better here
--------------------------------------------------------------------------

Measured on this machine today, with v2 applied, stock alsa-ucm-conf
1.2.15.3 and firmware-sof 2025.12.2, and no local configuration:

  Components: 'HDA:80862820,80860101,00100000  cfg-amp:1
               iec61937-pcm:7,6,5 spk:rt1320 mic:rt1320-1 cfg-mics:1'

  Using function topologies instead intel/sof-ipc4-tplg/sof-lnl-dummy.tplg
  loading topology 0: intel/sof-ipc4-tplg/sof-sdca-1amp-id2.tplg
  loading topology 1: intel/sof-ipc4-tplg/sof-sdca-mic-id4.tplg
  loading topology 2: intel/sof-ipc4-tplg/sof-hdmi-pcm5-id5.tplg

  arecord -l: card 0: sofsoundwire, device 4: Microphone

With no machine entry, the generic path uses a *-dummy.tplg base, dispatches
the function topologies itself, and produces cfg-mics:1.

On the machine-entry branches the microphone is a recurring problem. Users
report the mic missing entirely on the 6.18 branch - sof-sdca-mic-id4.tplg
present in firmware but never requested - and the v7.0 commit fixes it by
switching to a dummy base and forcing "Playback-SmartAmp" /
"Capture-SmartMic" dailink names, which is the dispatcher's own behaviour
reimplemented by hand. Its commit message attributes the loss to the
monolithic base topology discarding the SmartMic function. Separately, the
missing cfg-mics breaks the UCM import outright on alsa-ucm-conf 1.2.16.

I have not run that branch here, so that comparison is their reports against
my measurements rather than one machine tested both ways. I have offered to
do exactly that on this device and will report the result.

If it holds, the general shape is worth more than this laptop: remove ghost
_ADRs at enumeration and let the generic path do its job, instead of adding
a match entry per model - which for this hardware appears to cost the
microphone. Surface Pro 12 (Panther Lake) is reported to have the same
firmware shape, so it would be the next test of that idea rather than the
next entry.

Thanks,
Sergey
[PATCH v2 1/2] ASoC: rt1320: run the initialisation preset on the first hardware init
Posted by Sergey Lebedev 1 month ago
rt1320_io_init() applies the vendor initialisation preset only when the
amplifier's SDCA function status has FUNCTION_NEEDS_INITIALIZATION set:

	if ((amp_func_status & FUNCTION_NEEDS_INITIALIZATION)) {

Its two sibling drivers guard the same write differently, also running
the preset on the first hardware init:

  rt712-sdca.c:  if ((amp_func_status & FUNCTION_NEEDS_INITIALIZATION) ||
                     (!rt712->first_hw_init)) {
  rt722-sdca.c:  if ((amp_func_status & FUNCTION_NEEDS_INITIALIZATION) ||
                     (!rt722->first_hw_init)) {

On the Microsoft Surface Pro 11 (Intel) the RT1320 never sets that bit.
Its function status reads back 0x41 on every boot, cold or warm:

  rt1320-sdca sdw:0:0:025d:1320:01: rt1320_io_init amp func_status=0x41

which is NEWLY_ATTACHED | FUNCTION_HAS_BEEN_RESET: the function reports
that it has been reset and does not consider itself in need of
initialisation. Bit 5 is never set, so the preset never runs,
rt1320_vc_preset() and the MCU patch load are skipped, and the amplifier
is left unprogrammed. rt712 and rt722 would have run it via their
first_hw_init fallback.

Add the same fallback. With it rt1320_vc_preset() executes and the
amplifier reports RT1320_KR0_INT_READY=0x1f where previously it did not.

Signed-off-by: Sergey Lebedev <lsa.uz@pm.me>
---
v2: unchanged from v1's 1/3.
 sound/soc/codecs/rt1320-sdw.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c
index 13493b85f..d1f3b160a 100644
--- a/sound/soc/codecs/rt1320-sdw.c
+++ b/sound/soc/codecs/rt1320-sdw.c
@@ -1900,7 +1900,7 @@ static int rt1320_io_init(struct device *dev, struct sdw_slave *slave)
 	dev_dbg(dev, "%s amp func_status=0x%x\n", __func__, amp_func_status);
 
 	/* initialization write */
-	if ((amp_func_status & FUNCTION_NEEDS_INITIALIZATION)) {
+	if ((amp_func_status & FUNCTION_NEEDS_INITIALIZATION) || !rt1320->first_hw_init) {
 		switch (rt1320->dev_id) {
 		case RT1320_DEV_ID:
 			if (rt1320->version_id < RT1320_VC)
-- 
2.50.1 (Apple Git-155)
[PATCH v2 2/2] soundwire: dmi-quirks: drop the ghost RT1320 on the Surface Pro 11 (Intel)
Posted by Sergey Lebedev 1 month ago
The Microsoft Surface Pro 11 for Business (Intel, Lunar Lake) carries a
single physical RT1320 amplifier on link 0 and describes it twice:

  SWRA  _ADR 0x000030025D132000   SDCA class 0
  SWRB  _ADR 0x000030025D132001   SDCA class 1

Identical apart from the class id: same link, same manufacturer, part and
version, same unique id 0. The part reports class 1, so only SWRB
enumerates. SWRA never attaches on any boot or firmware version tested,
including the November 2025 bundle.

The ghost is not harmless. It consumes an amplifier index in
find_acpi_adr_device(), so the real part is named "rt1320-2" and the stock
sof-soundwire UCM profile enables switches on a device that is not there.
Its endpoints also reach create_sdw_dailink(), which builds DAI link names
from link id and function type alone; the ghost declares the same SmartMic
function as the real part, so the card fails to register at all:

  sysfs: cannot create duplicate filename
    '.../sof_sdw/SDW0-Capture-SmartMic'
  kobject_add_internal failed for SDW0-Capture-SmartMic with -EEXIST
  sof_sdw sof_sdw: probe with driver sof_sdw failed with error -12

Remap its _ADR to zero so sdw_acpi_find_slaves() never creates the
peripheral, which is how ghost devices are handled already - see
ghost_realtek and global_ghost_adr in this file. Both failures then
disappear, because neither path is reached.

Matched on DMI_PRODUCT_SKU rather than the product name. A later batch of
the same model could carry a different RT1320 version, which would change
the _ADR; keying on the SKU keeps this remap to the hardware it was
verified on, and follows dell_sku_0A3E above.

Suggested-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.dev>
Suggested-by: Bard Liao <bard.liao@intel.com>
Signed-off-by: Sergey Lebedev <lsa.uz@pm.me>
---
v2:
 - new in v2. Replaces v1's 2/3 (sdw_utils runtime presence test) and 3/3
   (SOF/Intel amp-index dedup) with a single remap at enumeration, per
   Pierre-Louis Bossart's suggestion.
 - matched on DMI_PRODUCT_SKU rather than the product name, per Bard Liao:
   a later batch could carry a different RT1320 version and therefore a
   different _ADR.
 drivers/soundwire/dmi-quirks.c | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/drivers/soundwire/dmi-quirks.c b/drivers/soundwire/dmi-quirks.c
index d75037558..8b3c5c8d1 100644
--- a/drivers/soundwire/dmi-quirks.c
+++ b/drivers/soundwire/dmi-quirks.c
@@ -111,6 +111,25 @@ static const struct adr_remap ghost_realtek[] = {
 	{}
 };
 
+/*
+ * The Microsoft Surface Pro 11 (Intel) describes its single physical RT1320
+ * amplifier twice on link 0, as two _ADR entries differing only in SDCA class
+ * id and sharing the same unique id. Only the class 1 entry enumerates; the
+ * class 0 entry is a ghost.
+ *
+ * Matched on the product SKU rather than the product name: a later batch of the
+ * same model could carry a different RT1320 version, which would change the
+ * _ADR. Keying on the SKU keeps this remap to the hardware it was verified on.
+ */
+static const struct adr_remap microsoft_sp11_intel[] = {
+	/* ghost rt1320 on link0 */
+	{
+		0x000030025d132000ull,
+		0x0000000000000000ull
+	},
+	{}
+};
+
 static const struct dmi_system_id adr_remap_quirk_table[] = {
 	/* TGL devices */
 	{
@@ -207,6 +226,15 @@ static const struct dmi_system_id adr_remap_quirk_table[] = {
 		},
 		.driver_data = (void *)ghost_realtek,
 	},
+	/* LNL devices */
+	{
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
+			DMI_EXACT_MATCH(DMI_PRODUCT_SKU,
+					"Surface_Pro_11th_Edition_With_Intel_For_Business_2103")
+		},
+		.driver_data = (void *)microsoft_sp11_intel,
+	},
 	{}
 };
 
-- 
2.50.1 (Apple Git-155)