
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 ---
**YES** This commit should be backported to stable kernel trees based on my analysis of both the commit message and code changes. Here's my extensive explanation: ## Bug Fix Analysis This commit addresses a specific functional bug in the tas2770 audio codec driver where ISENSE/VSENSE controls don't work properly when changed during active playback. The commit message clearly states: "if those controls are flipped on while the amplifier is already playing back audio, they will have no effect." ## Code Changes Analysis The fix is contained and minimal, involving three key changes to `sound/soc/codecs/tas2770.c`: 1. **Addition of `sense_event` function (lines +21 to +43)**: This function implements a power cycling mechanism that forces the amplifier through a shutdown state when ISENSE/VSENSE controls are changed. This ensures the changes take effect regardless of playback state. 2. **Modified DAPM widget definitions (lines +45 to +48)**: The ISENSE and VSENSE switches are changed from simple `SND_SOC_DAPM_SWITCH` to `SND_SOC_DAPM_SWITCH_E` with event handling, connecting them to the new `sense_event` function. 3. **Event triggers**: The widgets respond to `SND_SOC_DAPM_PRE_REG` and `SND_SOC_DAPM_POST_REG` events to perform the power cycling around register changes. ## Why This Should Be Backported 1. **User-Affecting Bug**: This fixes a real functional issue where audio controls don't work as expected during playback, which directly impacts user experience. 2. **Small and Contained**: The fix is confined to a single driver file (`tas2770.c`) and doesn't affect other subsystems. The changes are surgical and targeted. 3. **Low Risk**: The fix follows established ASoC patterns using standard DAPM event handling. Similar power cycling approaches are used throughout the ASoC subsystem. 4. **No New Features**: This purely fixes existing functionality rather than adding new features. 5. **Comparison with Similar Commits**: Looking at the provided examples, this commit is very similar to "Similar Commit #2" and "Similar Commit #5" which were both marked as backportable (YES). Like commit #2, it fixes incorrect hardware behavior with a small register/control change. Like commit #5, it addresses power state management issues in audio hardware. 6. **Hardware-Specific Fix**: The commit addresses a hardware limitation specific to the tas2770 chip where ISENSE/VSENSE blocks are only powered up during shutdown-to-active transitions. This is documented in the commit message and is a legitimate hardware workaround. The fix ensures that software speaker protection functionality works correctly by guaranteeing that IVSENSE controls are functional, which is critical for protecting audio hardware from damage.
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 7f219df8be704..8de7e94d4ba47 100644 --- a/sound/soc/codecs/tas2770.c +++ b/sound/soc/codecs/tas2770.c @@ -156,11 +156,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"),