* [PATCH v2 1/3] ASoC: amd: acp-config: override ACP config flag on HP OmniBook X Flip 14
2026-08-24 3:41 [PATCH v2 0/3] ASoC: fix SoundWire audio on HP OmniBook X Flip 14 (ACP7.1 + TAS2783) jml
@ 2026-08-24 3:41 ` jml
2026-08-24 5:33 ` Mukunda,Vijendar
2026-08-24 3:41 ` [PATCH v2 2/3] ASoC: tas2783: also look for calibration data under the OEM GUID jml
2026-08-24 3:41 ` [PATCH v2 3/3] ASoC: tas2783: re-initialise amplifiers from .startup after system sleep jml
2 siblings, 1 reply; 5+ messages in thread
From: jml @ 2026-08-24 3:41 UTC (permalink / raw)
To: Vijendar.Mukunda, shenghao-ding, broonie
Cc: venkataprasad.potturu, kevin-lu, baojun.xu, sen, lgirdwood, perex,
tiwai, linux-sound, jml
The HP OmniBook X Flip 14-kc0xxx (board 8EA1, ACP 7.1) does not expose
the "acp-audio-config-flag" ACPI property on its ACP device, so
snd_amd_acp_acpi_find_config() falls back to its
FLAG_AMD_LEGACY_ONLY_DMIC default. snd_pci_ps then declines to probe and
snd_acp_pci binds instead, which has no SoundWire support at all.
The result is a machine with only the PDM microphone and HDMI: the
SoundWire codecs driving the speakers and the headset jack never come
up, even though a matching entry for this board already exists in
snd_soc_acpi_amd_acp70_sdw_machines[].
Add the board to acp70_acpi_flag_override_table so the config lookup
returns 0 and snd_pci_ps can claim the device.
Signed-off-by: jml <jml@carbonforge.ai>
---
sound/soc/amd/acp-config.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/sound/soc/amd/acp-config.c b/sound/soc/amd/acp-config.c
index 3021dc498..a545dd679 100644
--- a/sound/soc/amd/acp-config.c
+++ b/sound/soc/amd/acp-config.c
@@ -30,6 +30,13 @@ static const struct dmi_system_id acp70_acpi_flag_override_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "HN7306EA"),
},
},
+ {
+ /* HP OmniBook X Flip 14-kc0xxx (Krackan Point, ACP 7.1) */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
+ DMI_MATCH(DMI_BOARD_NAME, "8EA1"),
+ },
+ },
{
/* ASUS Zenbook S16 UM5606GA (Strix Point, ACP 7.0) */
.matches = {
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* Re: [PATCH v2 1/3] ASoC: amd: acp-config: override ACP config flag on HP OmniBook X Flip 14
2026-08-24 3:41 ` [PATCH v2 1/3] ASoC: amd: acp-config: override ACP config flag on HP OmniBook X Flip 14 jml
@ 2026-08-24 5:33 ` Mukunda,Vijendar
0 siblings, 0 replies; 5+ messages in thread
From: Mukunda,Vijendar @ 2026-08-24 5:33 UTC (permalink / raw)
To: jml, shenghao-ding, broonie
Cc: venkataprasad.potturu, kevin-lu, baojun.xu, sen, lgirdwood, perex,
tiwai, linux-sound
On 8/24/26 09:11, jml wrote:
> [You don't often get email from jml@carbonforge.ai. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ]
>
> The HP OmniBook X Flip 14-kc0xxx (board 8EA1, ACP 7.1) does not expose
> the "acp-audio-config-flag" ACPI property on its ACP device, so
> snd_amd_acp_acpi_find_config() falls back to its
> FLAG_AMD_LEGACY_ONLY_DMIC default. snd_pci_ps then declines to probe and
> snd_acp_pci binds instead, which has no SoundWire support at all.
>
> The result is a machine with only the PDM microphone and HDMI: the
> SoundWire codecs driving the speakers and the headset jack never come
> up, even though a matching entry for this board already exists in
> snd_soc_acpi_amd_acp70_sdw_machines[].
>
> Add the board to acp70_acpi_flag_override_table so the config lookup
> returns 0 and snd_pci_ps can claim the device.
>
> Signed-off-by: jml <jml@carbonforge.ai>
Reviewed-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
> ---
> sound/soc/amd/acp-config.c | 7 +++++++
> 1 file changed, 7 insertions(+)
>
> diff --git a/sound/soc/amd/acp-config.c b/sound/soc/amd/acp-config.c
> index 3021dc498..a545dd679 100644
> --- a/sound/soc/amd/acp-config.c
> +++ b/sound/soc/amd/acp-config.c
> @@ -30,6 +30,13 @@ static const struct dmi_system_id acp70_acpi_flag_override_table[] = {
> DMI_MATCH(DMI_PRODUCT_NAME, "HN7306EA"),
> },
> },
> + {
> + /* HP OmniBook X Flip 14-kc0xxx (Krackan Point, ACP 7.1) */
> + .matches = {
> + DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
> + DMI_MATCH(DMI_BOARD_NAME, "8EA1"),
> + },
> + },
> {
> /* ASUS Zenbook S16 UM5606GA (Strix Point, ACP 7.0) */
> .matches = {
> --
> 2.53.0
>
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v2 2/3] ASoC: tas2783: also look for calibration data under the OEM GUID
2026-08-24 3:41 [PATCH v2 0/3] ASoC: fix SoundWire audio on HP OmniBook X Flip 14 (ACP7.1 + TAS2783) jml
2026-08-24 3:41 ` [PATCH v2 1/3] ASoC: amd: acp-config: override ACP config flag on HP OmniBook X Flip 14 jml
@ 2026-08-24 3:41 ` jml
2026-08-24 3:41 ` [PATCH v2 3/3] ASoC: tas2783: re-initialise amplifiers from .startup after system sleep jml
2 siblings, 0 replies; 5+ messages in thread
From: jml @ 2026-08-24 3:41 UTC (permalink / raw)
To: Vijendar.Mukunda, shenghao-ding, broonie
Cc: venkataprasad.potturu, kevin-lu, baojun.xu, sen, lgirdwood, perex,
tiwai, linux-sound, jml
tas2783_update_calibdata() looks the SmartAmpCalibrationData UEFI
variable up under a single vendor GUID. At least one OEM stores the very
same payload under a different GUID: on the HP OmniBook X Flip
14-kc0xxx the variable is named SmartAmpCalibrationData but sits under
53559579-8753-4f5c-9130-e82acfb8d893.
efi.get_variable() matches on name *and* GUID, so the lookup fails, the
driver takes the silent dev_dbg("No calibration data in UEFI") path, and
the amplifiers run on generic defaults. The factory data is present and
valid - magic 2783, spk_count 2, CRC32 verified, per-speaker records
carrying the unique_ids of the two amplifiers fitted - it is simply
never read. Audibly this shows up as distortion at higher volumes, since
the protection algorithm has no real R0/TLim values for the drivers.
Try both GUIDs before giving up.
Signed-off-by: jml <jml@carbonforge.ai>
---
sound/soc/codecs/tas2783-sdw.c | 52 +++++++++++++++++++++++-----------
1 file changed, 35 insertions(+), 17 deletions(-)
diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c
index eaebb0ebb..2ca634a6e 100644
--- a/sound/soc/codecs/tas2783-sdw.c
+++ b/sound/soc/codecs/tas2783-sdw.c
@@ -46,6 +46,15 @@
#define TAS2783_PROBE_TIMEOUT 5000
#define TAS2783_CALI_GUID EFI_GUID(0x1f52d2a1, 0xbb3a, 0x457d, 0xbc, \
0x09, 0x43, 0xa3, 0xf4, 0x31, 0x0a, 0x92)
+/*
+ * Some OEMs store the same SmartAmpCalibrationData payload under a different
+ * vendor GUID. Seen on the HP OmniBook X Flip 14-kc0xxx (board 8EA1): the
+ * variable name matches but the GUID does not, so the factory per-speaker
+ * calibration was silently ignored and the protection algorithm ran on
+ * defaults.
+ */
+#define TAS2783_CALI_GUID_ALT EFI_GUID(0x53559579, 0x8753, 0x4f5c, 0x91, \
+ 0x30, 0xe8, 0x2a, 0xcf, 0xb8, 0xd8, 0x93)
static const u32 tas2783_cali_reg[] = {
TAS2783_CAL_R0,
@@ -702,8 +711,10 @@ static void tas2783_set_calib_params_to_device(struct tas2783_prv *tas_dev, u32
static s32 tas2783_update_calibdata(struct tas2783_prv *tas_dev)
{
- efi_guid_t efi_guid = TAS2783_CALI_GUID;
- u32 attr, i, *tmp_val;
+ static const efi_guid_t efi_guids[] = {
+ TAS2783_CALI_GUID, TAS2783_CALI_GUID_ALT };
+ efi_guid_t efi_guid;
+ u32 attr, i, g, *tmp_val;
unsigned long size;
s32 ret;
efi_status_t status;
@@ -717,22 +728,29 @@ static s32 tas2783_update_calibdata(struct tas2783_prv *tas_dev)
* In some cases, the calibration is performed in Windows,
* and data was saved in UEFI. Linux can access it.
*/
- for (i = 0; i < ARRAY_SIZE(efi_names); i++) {
- size = 0;
- status = efi.get_variable(efi_names[i], &efi_guid, &attr,
- &size, NULL);
- if (size > TAS2783_CALIB_DATA_SZ) {
- dev_err(tas_dev->dev, "cali data too large\n");
- break;
- }
-
- tas_dev->cali_data.read_sz = size;
- if (status == EFI_BUFFER_TOO_SMALL) {
+ status = EFI_NOT_FOUND;
+ for (g = 0; g < ARRAY_SIZE(efi_guids); g++) {
+ efi_guid = efi_guids[g];
+ for (i = 0; i < ARRAY_SIZE(efi_names); i++) {
+ size = 0;
status = efi.get_variable(efi_names[i], &efi_guid, &attr,
- &tas_dev->cali_data.read_sz,
- tas_dev->cali_data.data);
- dev_dbg(tas_dev->dev, "cali get %lu bytes result:%ld\n",
- tas_dev->cali_data.read_sz, status);
+ &size, NULL);
+ if (size > TAS2783_CALIB_DATA_SZ) {
+ dev_err(tas_dev->dev, "cali data too large\n");
+ status = EFI_NOT_FOUND;
+ break;
+ }
+
+ tas_dev->cali_data.read_sz = size;
+ if (status == EFI_BUFFER_TOO_SMALL) {
+ status = efi.get_variable(efi_names[i], &efi_guid, &attr,
+ &tas_dev->cali_data.read_sz,
+ tas_dev->cali_data.data);
+ dev_dbg(tas_dev->dev, "cali get %lu bytes result:%ld\n",
+ tas_dev->cali_data.read_sz, status);
+ }
+ if (status == EFI_SUCCESS)
+ break;
}
if (status == EFI_SUCCESS)
break;
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH v2 3/3] ASoC: tas2783: re-initialise amplifiers from .startup after system sleep
2026-08-24 3:41 [PATCH v2 0/3] ASoC: fix SoundWire audio on HP OmniBook X Flip 14 (ACP7.1 + TAS2783) jml
2026-08-24 3:41 ` [PATCH v2 1/3] ASoC: amd: acp-config: override ACP config flag on HP OmniBook X Flip 14 jml
2026-08-24 3:41 ` [PATCH v2 2/3] ASoC: tas2783: also look for calibration data under the OEM GUID jml
@ 2026-08-24 3:41 ` jml
2 siblings, 0 replies; 5+ messages in thread
From: jml @ 2026-08-24 3:41 UTC (permalink / raw)
To: Vijendar.Mukunda, shenghao-ding, broonie
Cc: venkataprasad.potturu, kevin-lu, baojun.xu, sen, lgirdwood, perex,
tiwai, linux-sound, jml
After system sleep the amplifiers come back silent. The firmware is in
fact re-downloaded correctly when the peripheral re-enumerates - the
download reports success and both fw_dl_success and hw_init are set -
but the part still produces no output. Only a firmware download
performed shortly before playback restores sound.
Doing that from .hw_params is not safe when the parts are used as an
aggregated pair on a single SoundWire stream, as they are on the HP
OmniBook X Flip 14-kc0xxx: by then the first amplifier's port
configuration is already in place, and resetting the second one knocks
the first out. Exactly one of the two speakers then works, and which one
varies from resume to resume.
Do the re-initialisation from .startup instead. ASoC calls .startup for
every codec DAI of a link before any of them reaches .hw_params, so both
amplifiers can be re-initialised while the stream is still unconfigured.
Gate it on a flag set from a PM notifier rather than on fw_dl_success:
whether a given peripheral re-enumerates before or after the notifier
runs is a race, so fw_dl_success is not a reliable indication that the
DSP still holds its program.
Signed-off-by: jml <jml@carbonforge.ai>
---
sound/soc/codecs/tas2783-sdw.c | 78 ++++++++++++++++++++++++++++++++++
1 file changed, 78 insertions(+)
diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c
index 2ca634a6e..4c850a857 100644
--- a/sound/soc/codecs/tas2783-sdw.c
+++ b/sound/soc/codecs/tas2783-sdw.c
@@ -24,6 +24,7 @@
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
+#include <linux/suspend.h>
#include <linux/wait.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_registers.h>
@@ -112,8 +113,13 @@ struct tas2783_prv {
bool fw_dl_success;
/* use fallback fw name */
bool fw_use_fallback;
+ /* set across system sleep, cleared once the part has been re-inited */
+ bool force_reinit;
+ struct notifier_block pm_nb;
};
+static s32 tas_io_init(struct device *dev, struct sdw_slave *slave);
+
static const struct reg_default tas2783_reg_default[] = {
{TAS2783_AMP_LEVEL, 0x28},
{TASDEV_REG_SDW(0, 0, 0x04), 0x21},
@@ -1051,7 +1057,47 @@ static s32 tas_sdw_pcm_hw_free(struct snd_pcm_substream *substream,
TAS2783_SDCA_POW_STATE_OFF);
}
+/*
+ * The amplifier loses its DSP program across system sleep and only a
+ * firmware download performed shortly before playback makes it produce
+ * sound again. Doing that from .hw_params is not safe when the parts are
+ * used as an aggregated pair on one SoundWire stream: by then the first
+ * amplifier's port configuration is already in place, and resetting the
+ * second one knocks the first out, so exactly one of the two speakers
+ * works and which one varies from resume to resume.
+ *
+ * .startup runs for every codec DAI of the link before any of them
+ * reaches .hw_params, so both amplifiers can be re-initialised while the
+ * stream is still unconfigured.
+ */
+static int tas_sdw_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct tas2783_prv *tas_dev =
+ snd_soc_component_get_drvdata(component);
+ s32 ret;
+
+ if (!tas_dev->force_reinit && tas_dev->fw_dl_success)
+ return 0;
+
+ tas_dev->hw_init = false;
+ regcache_cache_only(tas_dev->regmap, false);
+
+ ret = tas_io_init(tas_dev->dev, tas_dev->sdw_peripheral);
+ if (ret || !tas_dev->fw_dl_success) {
+ dev_err(tas_dev->dev, "re-init before playback failed, err=%d\n",
+ ret);
+ return -EIO;
+ }
+
+ tas_dev->force_reinit = false;
+
+ return 0;
+}
+
static const struct snd_soc_dai_ops tas_dai_ops = {
+ .startup = tas_sdw_startup,
.hw_params = tas_sdw_hw_params,
.hw_free = tas_sdw_pcm_hw_free,
.set_stream = tas_set_sdw_stream,
@@ -1391,6 +1437,32 @@ static void tas_remove(struct tas2783_prv *tas_dev)
snd_soc_unregister_component(tas_dev->dev);
}
+/*
+ * fw_dl_success on its own is not a reliable indication that the DSP still
+ * holds its program: whether a given peripheral re-enumerates before or
+ * after this notifier runs is a race, and a download performed while the
+ * bus is still settling does not stick.
+ */
+static int tas2783_pm_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct tas2783_prv *tas_dev =
+ container_of(nb, struct tas2783_prv, pm_nb);
+
+ switch (action) {
+ case PM_SUSPEND_PREPARE:
+ case PM_HIBERNATION_PREPARE:
+ case PM_POST_SUSPEND:
+ case PM_POST_HIBERNATION:
+ tas_dev->force_reinit = true;
+ break;
+ default:
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
static s32 tas_sdw_probe(struct sdw_slave *peripheral,
const struct sdw_device_id *id)
{
@@ -1462,6 +1534,11 @@ static s32 tas_sdw_probe(struct sdw_slave *peripheral,
/* keep in cache until the device is fully initialized */
regcache_cache_only(regmap, true);
tas_dev->regmap = regmap;
+
+ tas_dev->pm_nb.notifier_call = tas2783_pm_notify;
+ if (register_pm_notifier(&tas_dev->pm_nb))
+ dev_warn(dev, "pm notifier registration failed");
+
return tas_init(tas_dev);
}
@@ -1469,6 +1546,7 @@ static void tas_sdw_remove(struct sdw_slave *peripheral)
{
struct tas2783_prv *tas_dev = dev_get_drvdata(&peripheral->dev);
+ unregister_pm_notifier(&tas_dev->pm_nb);
pm_runtime_disable(tas_dev->dev);
tas_remove(tas_dev);
mutex_destroy(&tas_dev->calib_lock);
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread