From: Hector Martin marcan@marcan.st
[ Upstream commit f529c91be8a34ac12e7599bf87c65b6f4a2c9f5c ]
The ISENSE/VSENSE blocks are only powered up when the amplifier transitions from shutdown to active. This means that if those controls are flipped on while the amplifier is already playing back audio, they will have no effect.
Fix this by forcing a power cycle around transitions in those controls.
Reviewed-by: Neal Gompa neal@gompa.dev Signed-off-by: Hector Martin marcan@marcan.st Signed-off-by: James Calligeros jcalligeros99@gmail.com Link: https://patch.msgid.link/20250406-apple-codec-changes-v5-1-50a00ec850a3@gmai... Signed-off-by: Mark Brown broonie@kernel.org Signed-off-by: Sasha Levin sashal@kernel.org ---
Based on my comprehensive analysis of both the commit and the TAS2770 codebase, here is my assessment: **YES** This commit should be backported to stable kernel trees for the following reasons: ## Critical Functionality Fix The commit addresses a **fundamental hardware functionality issue** where ISENSE/VSENSE blocks (current/voltage monitoring) only power up during amplifier state transitions from shutdown to active. This is a hardware-level limitation that affects the core operation of the audio codec. ## Speaker Protection System Impact The code changes reveal this is about **speaker protection**, which is safety-critical functionality: ```c /bin /bin.usr-is-merged /boot /dev /etc /home /init /lib /lib.usr-is-merged /lib64 /lost+found /media /mnt /opt /proc /root /run /sbin /sbin.usr-is-merged /snap /srv /sys /tmp /usr /var * Powering up ISENSE/VSENSE requires a trip through the shutdown state. * Do that here to ensure that our changes are applied properly, otherwise * we might end up with non-functional IVSENSE if playback started earlier, * which would break software speaker protection. */ ``` Non-functional IVSENSE/VSENSE breaks software speaker protection algorithms that prevent hardware damage from overcurrent/overvoltage conditions. ## Clean, Contained Fix The implementation is minimal and surgical: - Adds a new `sense_event()` function with only 12 lines of logic - Modifies DAPM widget definitions to use `SND_SOC_DAPM_SWITCH_E` instead of `SND_SOC_DAPM_SWITCH` - Forces a controlled power cycle (shutdown → normal operation) when sense controls change - No architectural changes or new features ## Historical Pattern Alignment This follows the **positive backport pattern** seen in similar commit #2 (tas2562 amp_level fix) and #5 (tas2781 power state restoration), both marked "Backport Status: YES" for fixing hardware control issues in TAS codec family. ## Low Regression Risk The fix operates within existing DAPM event handling framework: - `SND_SOC_DAPM_PRE_REG`: Forces shutdown before register changes - `SND_SOC_DAPM_POST_REG`: Restores proper power state after changes - Uses existing `tas2770_update_pwr_ctrl()` function - No changes to normal playback paths when sense controls aren't modified ## User- Affecting Bug Users enabling ISENSE/VSENSE monitoring during active playback would experience: - Silent failure of speaker protection - Potential hardware damage risk - Inconsistent behavior depending on timing of control changes The fix ensures these controls work reliably regardless of when they're activated, which is essential for proper codec operation and hardware protection.
sound/soc/codecs/tas2770.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-)
diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c index e284a3a854591..8bd98e9817c97 100644 --- a/sound/soc/codecs/tas2770.c +++ b/sound/soc/codecs/tas2770.c @@ -158,11 +158,37 @@ static const struct snd_kcontrol_new isense_switch = static const struct snd_kcontrol_new vsense_switch = SOC_DAPM_SINGLE("Switch", TAS2770_PWR_CTRL, 2, 1, 1);
+static int sense_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct tas2770_priv *tas2770 = snd_soc_component_get_drvdata(component); + + /* + * Powering up ISENSE/VSENSE requires a trip through the shutdown state. + * Do that here to ensure that our changes are applied properly, otherwise + * we might end up with non-functional IVSENSE if playback started earlier, + * which would break software speaker protection. + */ + switch (event) { + case SND_SOC_DAPM_PRE_REG: + return snd_soc_component_update_bits(component, TAS2770_PWR_CTRL, + TAS2770_PWR_CTRL_MASK, + TAS2770_PWR_CTRL_SHUTDOWN); + case SND_SOC_DAPM_POST_REG: + return tas2770_update_pwr_ctrl(tas2770); + default: + return 0; + } +} + static const struct snd_soc_dapm_widget tas2770_dapm_widgets[] = { SND_SOC_DAPM_AIF_IN("ASI1", "ASI1 Playback", 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_MUX("ASI1 Sel", SND_SOC_NOPM, 0, 0, &tas2770_asi1_mux), - SND_SOC_DAPM_SWITCH("ISENSE", TAS2770_PWR_CTRL, 3, 1, &isense_switch), - SND_SOC_DAPM_SWITCH("VSENSE", TAS2770_PWR_CTRL, 2, 1, &vsense_switch), + SND_SOC_DAPM_SWITCH_E("ISENSE", TAS2770_PWR_CTRL, 3, 1, &isense_switch, + sense_event, SND_SOC_DAPM_PRE_REG | SND_SOC_DAPM_POST_REG), + SND_SOC_DAPM_SWITCH_E("VSENSE", TAS2770_PWR_CTRL, 2, 1, &vsense_switch, + sense_event, SND_SOC_DAPM_PRE_REG | SND_SOC_DAPM_POST_REG), SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas2770_dac_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), SND_SOC_DAPM_OUTPUT("OUT"),