mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [RFC PATCH 0/2] ASoC: CS42L83 Apple headset remote support
@ 2026-09-17  7:23 Ryan Murray
  2026-09-17  7:23 ` [RFC PATCH 1/2] ASoC: cs42l42: handle jack interrupt I/O and resume errors Ryan Murray
  2026-09-17  7:23 ` [RFC PATCH 2/2] ASoC: cs42l42: support Apple headset remotes on CS42L83 Ryan Murray
  0 siblings, 2 replies; 4+ messages in thread
From: Ryan Murray @ 2026-09-17  7:23 UTC (permalink / raw)
  To: patches, linux-sound
  Cc: david.rhodes, rf, lgirdwood, broonie, perex, tiwai,
	james.schulman, povik+lin, asahi, linux-kernel

Hi,

This series adds Apple wired-headset remote support to CS42L83, including
volume-button events while audio is idle. Identification is bounded and
falls back to the analogue detector on failure. It uses the existing
regmap, threaded IRQ and ASoC jack paths; no new DT or clock interface is
needed.

Patch 1 checks interrupt I/O and runtime-resume errors. Patch 2 adds remote
identification and button handling. Machine drivers must expose the button
capabilities and key mappings; J700 platform support is outside this series.

Hardware testing on J700/CS42L83 B0 with Apple earbuds covered idle buttons,
unplug/replug, playback, capture and full duplex, including identification
with streams running. The mainline series was build-tested separately:
arm64 modules with PM on/off and built-in PM. Extracted-source ASan/UBSan
tests cover malformed replies, timeouts, I/O faults and recovery.

Other boards/revisions, OMTP, analogue-only headsets and system
suspend/resume remain untested.

I'd appreciate Cirrus/ASoC feedback on:
- Whether CS42L83 identity alone is sufficient across revisions.
- The undocumented receiver controls, particularly HS_BIAS_CTL bits 3:2,
  and the receive-acknowledgment/identifier interpretation.

This is an independent implementation based on register-level reverse
engineering and hardware tests. No proprietary source or firmware is
included. LLM assistance covered implementation, analysis, testing and
text; the patches carry Assisted-by: LLM.

Thanks,
Ryan

Ryan Murray (2):
  ASoC: cs42l42: handle jack interrupt I/O and resume errors
  ASoC: cs42l42: support Apple headset remotes on CS42L83

 sound/soc/codecs/cs42l42.c | 286 +++++++++++++++++++++++++++++++++++--
 sound/soc/codecs/cs42l42.h |  15 ++
 2 files changed, 293 insertions(+), 8 deletions(-)


base-commit: 238650ef6c7c7cca08e032527329424c9fbd70e5

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [RFC PATCH 1/2] ASoC: cs42l42: handle jack interrupt I/O and resume errors
  2026-09-17  7:23 [RFC PATCH 0/2] ASoC: CS42L83 Apple headset remote support Ryan Murray
@ 2026-09-17  7:23 ` Ryan Murray
  2026-09-17 20:20   ` Mark Brown
  2026-09-17  7:23 ` [RFC PATCH 2/2] ASoC: cs42l42: support Apple headset remotes on CS42L83 Ryan Murray
  1 sibling, 1 reply; 4+ messages in thread
From: Ryan Murray @ 2026-09-17  7:23 UTC (permalink / raw)
  To: patches, linux-sound
  Cc: david.rhodes, rf, lgirdwood, broonie, perex, tiwai,
	james.schulman, povik+lin, asahi, linux-kernel

A failed status or mask read leaves part of the interrupt snapshot
uninitialized. The handler can then interpret that data as headset or
button events. It also attempts register access after a failed runtime
resume.

Check the runtime-PM acquisition and each snapshot read before processing
any events. Use the transparent autosuspend guard so the always-powered
I2C transport continues to work when runtime PM is disabled. Keep the
existing scoped mutex and PM cleanup on every return path.

An extracted-handler fault-injection test reproduces event processing on
failed reads before this change and checks all 24 snapshot read failures
after it, together with resume failure and disabled-runtime-PM cases.

Fixes: 2c394ca79604 ("ASoC: Add support for CS42L42 codec")
Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
---
 sound/soc/codecs/cs42l42.c | 23 +++++++++++++++++------
 1 file changed, 17 insertions(+), 6 deletions(-)

diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c
index fadf68860..2d1b45ea1 100644
--- a/sound/soc/codecs/cs42l42.c
+++ b/sound/soc/codecs/cs42l42.c
@@ -1667,18 +1667,25 @@ irqreturn_t cs42l42_irq_thread(int irq, void *data)
 	unsigned int current_plug_status;
 	unsigned int current_button_status;
 	unsigned int i;
+	int ret;
 
-	guard(pm_runtime_active_auto)(cs42l42->dev);
+	PM_RUNTIME_ACQUIRE_AUTOSUSPEND(cs42l42->dev, pm);
+	if (PM_RUNTIME_ACQUIRE_ERR(&pm))
+		return IRQ_NONE;
 	guard(mutex)(&cs42l42->irq_lock);
 	if (cs42l42->suspended || !cs42l42->init_done)
 		return IRQ_NONE;
 
-	/* Read sticky registers to clear interurpt */
+	/* Read sticky registers to clear interrupt */
 	for (i = 0; i < ARRAY_SIZE(stickies); i++) {
-		regmap_read(cs42l42->regmap, irq_params_table[i].status_addr,
-				&(stickies[i]));
-		regmap_read(cs42l42->regmap, irq_params_table[i].mask_addr,
-				&(masks[i]));
+		ret = regmap_read(cs42l42->regmap, irq_params_table[i].status_addr,
+				  &stickies[i]);
+		if (ret)
+			goto out_error;
+		ret = regmap_read(cs42l42->regmap, irq_params_table[i].mask_addr,
+				  &masks[i]);
+		if (ret)
+			goto out_error;
 		stickies[i] = stickies[i] & (~masks[i]) &
 				irq_params_table[i].mask;
 	}
@@ -1772,6 +1779,10 @@ irqreturn_t cs42l42_irq_thread(int irq, void *data)
 	}
 
 	return IRQ_HANDLED;
+
+out_error:
+	dev_err_ratelimited(cs42l42->dev, "Failed to read jack interrupt state: %d\n", ret);
+	return IRQ_NONE;
 }
 EXPORT_SYMBOL_NS_GPL(cs42l42_irq_thread, "SND_SOC_CS42L42_CORE");
 

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [RFC PATCH 2/2] ASoC: cs42l42: support Apple headset remotes on CS42L83
  2026-09-17  7:23 [RFC PATCH 0/2] ASoC: CS42L83 Apple headset remote support Ryan Murray
  2026-09-17  7:23 ` [RFC PATCH 1/2] ASoC: cs42l42: handle jack interrupt I/O and resume errors Ryan Murray
@ 2026-09-17  7:23 ` Ryan Murray
  1 sibling, 0 replies; 4+ messages in thread
From: Ryan Murray @ 2026-09-17  7:23 UTC (permalink / raw)
  To: patches, linux-sound
  Cc: david.rhodes, rf, lgirdwood, broonie, perex, tiwai,
	james.schulman, povik+lin, asahi, linux-kernel

Apple wired headset volume buttons use a microphone-bias signalling
protocol instead of the resistor levels handled by the analogue detector.
The CS42L83 can identify these remotes and decode their button indications
without an audio stream or an external serial clock.

After CTIA or OMTP detection, perform a bounded identification exchange on
CS42L83. Wait for the receive acknowledgment rather than the earlier
transmit-complete indication, which can expose an incomplete identifier.
Validate all three identifier symbols before enabling remote reception.
Restore the analogue detector on timeout, a short, an invalid identifier,
or a register-access error.

Preserve the receiver's raw volume indications before applying the
analogue interrupt masks. Report volume indications as press/release
pairs and use short-detect transitions for the centre button. Discard
pre-identification button state and restore the saved detector settings
on unplug, redetection and suspend under the existing IRQ mutex.

Select this capability from the CS42L83 device identity. No board-specific
property, clock provider, work item or extra power reference is needed.
The CS42L42 continues to use its analogue detector.

The undocumented CS42L83 sequence was independently implemented from
register-level reverse engineering and validated on a J700 with a B0
codec and Apple wired earbuds. Other boards and codec revisions have not
been hardware-tested. The machine driver must expose the appropriate
SND_JACK_BTN capabilities and key mappings.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
---
 sound/soc/codecs/cs42l42.c | 263 ++++++++++++++++++++++++++++++++++++-
 sound/soc/codecs/cs42l42.h |  15 +++
 2 files changed, 276 insertions(+), 2 deletions(-)

diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c
index 2d1b45ea1..c510ceae8 100644
--- a/sound/soc/codecs/cs42l42.c
+++ b/sound/soc/codecs/cs42l42.c
@@ -15,6 +15,7 @@
 #include <linux/types.h>
 #include <linux/init.h>
 #include <linux/delay.h>
+#include <linux/jiffies.h>
 #include <linux/regmap.h>
 #include <linux/slab.h>
 #include <linux/acpi.h>
@@ -1257,6 +1258,240 @@ static void cs42l42_manual_hs_type_detect(struct cs42l42_private *cs42l42)
 				(CS42L42_HSDET_COMP2_LVL_DEFAULT << CS42L42_HSDET_COMP2_LVL_SHIFT));
 }
 
+/*
+ * CS42L83 receives Apple headset remote data over the microphone-bias line.
+ * The receiver operates on the internal oscillator, including while audio
+ * is idle. These control values and status bits are specific to CS42L83.
+ */
+#define CS42L83_REMOTE_TX_START		BIT(7)
+#define CS42L83_REMOTE_RX_ACK		BIT(3)
+#define CS42L83_REMOTE_BUTTON_IRQ		BIT(5)
+#define CS42L83_REMOTE_BUTTONS		GENMASK(4, 0)
+#define CS42L83_REMOTE_VOLUME_DOWN	BIT(0)
+#define CS42L83_REMOTE_VOLUME_UP		BIT(1)
+
+/* Complete receiver control values; these are not CS42L42 detect modes. */
+#define CS42L83_REMOTE_BIAS_RESET		0x03
+#define CS42L83_REMOTE_BIAS_IDENTIFY	0x9e
+#define CS42L83_REMOTE_BIAS_BUTTONS	0x5f
+#define CS42L83_REMOTE_RX_CONFIG		0x8f
+#define CS42L83_REMOTE_LEVEL_IDENTIFY	0x36
+#define CS42L83_REMOTE_LEVEL_BUTTONS	0x3c
+
+static const unsigned int cs42l83_remote_regs[] = {
+	[CS42L83_REMOTE_SAVED_HSBIAS_SC_AUTOCTL] = CS42L42_HSBIAS_SC_AUTOCTL,
+	[CS42L83_REMOTE_SAVED_WAKE_CTL] = CS42L42_WAKE_CTL,
+	[CS42L83_REMOTE_SAVED_MISC_DET_CTL] = CS42L42_MISC_DET_CTL,
+	[CS42L83_REMOTE_SAVED_MIC_DET_CTL1] = CS42L42_MIC_DET_CTL1,
+	[CS42L83_REMOTE_SAVED_MIC_DET_CTL2] = CS42L42_MIC_DET_CTL2,
+	[CS42L83_REMOTE_SAVED_DET_INT1_MASK] = CS42L42_DET_INT1_MASK,
+	[CS42L83_REMOTE_SAVED_DET_INT2_MASK] = CS42L42_DET_INT2_MASK,
+	[CS42L83_REMOTE_SAVED_HS_BIAS_CTL] = CS42L42_HS_BIAS_CTL,
+	[CS42L83_REMOTE_SAVED_HSDET_CTL2] = CS42L42_HSDET_CTL2,
+};
+
+static void cs42l83_remote_restore(struct cs42l42_private *cs42l42)
+{
+	int i, ret;
+
+	for (i = 0; i < ARRAY_SIZE(cs42l83_remote_regs); i++) {
+		ret = regmap_write(cs42l42->regmap, cs42l83_remote_regs[i],
+				   cs42l42->remote_saved[i]);
+		if (ret)
+			dev_warn(cs42l42->dev, "Failed to restore headset detection register %#x: %d\n",
+				 cs42l83_remote_regs[i], ret);
+	}
+	cs42l42->remote_active = false;
+}
+
+/*
+ * Hold the bias reference during a bias-mode change and allow it to settle
+ * before starting transmission. Apply the TX bit only after the bias bits.
+ */
+static int cs42l83_remote_set_detect(struct cs42l42_private *cs42l42, unsigned int value)
+{
+	unsigned int old;
+	bool changed;
+	int ret;
+
+	ret = regmap_read(cs42l42->regmap, CS42L42_MISC_DET_CTL, &old);
+	if (ret)
+		return ret;
+	changed = (old & CS42L42_HSBIAS_CTL_MASK) != (value & CS42L42_HSBIAS_CTL_MASK);
+	if (changed) {
+		/* CS42L83 bias transitions require clearing this undocumented field. */
+		ret = regmap_update_bits(cs42l42->regmap, CS42L42_HS_BIAS_CTL, GENMASK(3, 2), 0);
+		if (ret)
+			return ret;
+		ret = regmap_update_bits(cs42l42->regmap, CS42L42_HSDET_CTL2,
+					 CS42L42_HSBIAS_REF_MASK, CS42L42_HSBIAS_REF_MASK);
+		if (ret)
+			return ret;
+	}
+	ret = regmap_update_bits(cs42l42->regmap, CS42L42_MISC_DET_CTL,
+				 value & CS42L83_REMOTE_TX_START ? 0x7f : 0xff, value);
+	if (ret)
+		return ret;
+	if (!!(old & BIT(2)) != !!(value & BIT(2)))
+		usleep_range(10000, 11000);
+	if (value & CS42L83_REMOTE_TX_START) {
+		ret = regmap_write(cs42l42->regmap, CS42L42_MISC_DET_CTL, value);
+		if (ret)
+			return ret;
+	}
+	if (changed)
+		return regmap_update_bits(cs42l42->regmap, CS42L42_HSDET_CTL2,
+					  CS42L42_HSBIAS_REF_MASK, 0);
+	return 0;
+}
+
+/* Called with irq_lock held; every attempt is bounded and has analogue fallback. */
+static void cs42l83_remote_start(struct cs42l42_private *cs42l42)
+{
+	static const struct reg_sequence identify[] = {
+		{ CS42L42_DET_INT2_MASK, 0xff },
+		{ CS42L42_HSBIAS_SC_AUTOCTL, 0x03 },
+		{ CS42L42_WAKE_CTL, CS42L42_M_HP_WAKE_MASK | CS42L42_M_MIC_WAKE_MASK },
+		{ CS42L42_MIC_DET_CTL2, CS42L83_REMOTE_RX_CONFIG },
+	};
+	unsigned int int1, int2, status1, status2, symbols, symbol[3];
+	unsigned long deadline;
+	int i, ret;
+
+	if (cs42l42->devid != CS42L83_CHIP_ID ||
+	    (cs42l42->hs_type != CS42L42_PLUG_CTIA &&
+	     cs42l42->hs_type != CS42L42_PLUG_OMTP))
+		return;
+
+	for (i = 0; i < ARRAY_SIZE(cs42l83_remote_regs); i++) {
+		ret = regmap_read(cs42l42->regmap, cs42l83_remote_regs[i],
+				  &cs42l42->remote_saved[i]);
+		if (ret)
+			return;
+	}
+
+	ret = regmap_multi_reg_write(cs42l42->regmap, identify, ARRAY_SIZE(identify));
+	if (ret)
+		goto fallback;
+	/* Reset the bias before applying the identification threshold. */
+	ret = cs42l83_remote_set_detect(cs42l42, CS42L83_REMOTE_BIAS_RESET);
+	if (ret)
+		goto fallback;
+	usleep_range(10000, 11000);
+	ret = regmap_update_bits(cs42l42->regmap, CS42L42_MIC_DET_CTL1,
+				 CS42L42_HS_DET_LEVEL_MASK, CS42L83_REMOTE_LEVEL_IDENTIFY);
+	if (ret)
+		goto fallback;
+	ret = regmap_read(cs42l42->regmap, CS42L42_DET_INT_STATUS1, &int1);
+	if (ret)
+		goto fallback;
+	ret = regmap_read(cs42l42->regmap, CS42L42_DET_INT_STATUS2, &int2);
+	if (ret)
+		goto fallback;
+	/* Start identification after the controlled bias rise. */
+	ret = cs42l83_remote_set_detect(cs42l42, CS42L83_REMOTE_BIAS_IDENTIFY);
+	if (ret)
+		goto fallback;
+
+	/*
+	 * Bit 4 signals TX completion, before the remote reply is ready. Wait
+	 * for RX acknowledgment (bit 3), with a bounded analogue fallback.
+	 */
+	msleep(35);
+	deadline = jiffies + msecs_to_jiffies(500);
+	do {
+		ret = regmap_read(cs42l42->regmap, CS42L42_DET_INT_STATUS2, &int2);
+		if (ret)
+			goto fallback;
+		if (int2 & CS42L83_REMOTE_RX_ACK)
+			break;
+		usleep_range(5000, 6000);
+	} while (time_before(jiffies, deadline));
+	if (!(int2 & CS42L83_REMOTE_RX_ACK)) {
+		ret = -ETIMEDOUT;
+		goto fallback;
+	}
+	ret = regmap_read(cs42l42->regmap, CS42L42_DET_INT_STATUS1, &int1);
+	if (ret)
+		goto fallback;
+	ret = regmap_read(cs42l42->regmap, CS42L42_DET_STATUS1, &status1);
+	if (ret)
+		goto fallback;
+	ret = regmap_read(cs42l42->regmap, CS42L42_DET_STATUS2, &status2);
+	if (ret)
+		goto fallback;
+	symbols = (status2 >> 4) | ((status1 & 0x1f) << 4);
+	for (i = 0; i < ARRAY_SIZE(symbol); i++)
+		symbol[i] = (symbols >> (i * 3)) & 7;
+	/* A short or an invalid identifier must retain the analogue detector. */
+	if (status2 & CS42L42_SHORT_TRUE_MASK) {
+		ret = -ENODEV;
+		goto fallback;
+	}
+	for (i = 0; i < ARRAY_SIZE(symbol); i++) {
+		if (symbol[i] < 1 || symbol[i] > 4) {
+			ret = -ENODEV;
+			goto fallback;
+		}
+	}
+
+	/* Use the remote-button threshold only after a valid identification. */
+	ret = regmap_update_bits(cs42l42->regmap, CS42L42_MIC_DET_CTL1,
+				 CS42L42_HS_DET_LEVEL_MASK, CS42L83_REMOTE_LEVEL_BUTTONS);
+	if (ret)
+		goto fallback;
+
+	/* The receiver now decodes remote button pulses autonomously. */
+	ret = cs42l83_remote_set_detect(cs42l42, CS42L83_REMOTE_BIAS_BUTTONS);
+	if (ret)
+		goto fallback;
+	msleep(35);
+	ret = regmap_write(cs42l42->regmap, CS42L42_HSBIAS_SC_AUTOCTL,
+			   (cs42l42->hs_bias_sense_en << CS42L42_HSBIAS_SENSE_EN_SHIFT) | 3);
+	if (ret)
+		goto fallback;
+	ret = regmap_write(cs42l42->regmap, CS42L42_DET_INT1_MASK,
+			   cs42l42->remote_saved[CS42L83_REMOTE_SAVED_DET_INT1_MASK] &
+			   ~(cs42l42->hs_bias_sense_en << CS42L42_HSBIAS_SENSE_SHIFT));
+	if (ret)
+		goto fallback;
+	ret = regmap_write(cs42l42->regmap, CS42L42_DET_INT2_MASK,
+			   0xff & ~(CS42L83_REMOTE_BUTTON_IRQ |
+				    CS42L42_M_SHORT_DET_MASK | CS42L42_M_SHORT_RLS_MASK));
+	if (ret)
+		goto fallback;
+	cs42l42->remote_active = true;
+	dev_dbg(cs42l42->dev, "Apple headset remote enabled\n");
+	return;
+
+fallback:
+	dev_dbg(cs42l42->dev, "Headset remote identification failed: %d\n", ret);
+	cs42l83_remote_restore(cs42l42);
+}
+
+static void cs42l83_remote_buttons(struct cs42l42_private *cs42l42,
+				 unsigned int int1, unsigned int int2)
+{
+	unsigned int buttons = 0;
+
+	/* Volume indications are pulses, without a separate release event. */
+	if (int1 & CS42L83_REMOTE_BUTTONS) {
+		if (int1 & CS42L83_REMOTE_VOLUME_DOWN)
+			buttons |= SND_JACK_BTN_2;
+		if (int1 & CS42L83_REMOTE_VOLUME_UP)
+			buttons |= SND_JACK_BTN_1;
+		if (buttons)
+			snd_soc_jack_report(cs42l42->jack, buttons,
+					    SND_JACK_BTN_1 | SND_JACK_BTN_2);
+		snd_soc_jack_report(cs42l42->jack, 0,
+					    SND_JACK_BTN_1 | SND_JACK_BTN_2);
+	}
+	if (int2 & CS42L42_M_SHORT_DET_MASK)
+		snd_soc_jack_report(cs42l42->jack, SND_JACK_BTN_0, SND_JACK_BTN_0);
+	if (int2 & CS42L42_M_SHORT_RLS_MASK)
+		snd_soc_jack_report(cs42l42->jack, 0, SND_JACK_BTN_0);
+}
+
 static void cs42l42_process_hs_type_detect(struct cs42l42_private *cs42l42)
 {
 	unsigned int hs_det_status;
@@ -1398,6 +1633,9 @@ static void cs42l42_process_hs_type_detect(struct cs42l42_private *cs42l42)
 
 static void cs42l42_init_hs_type_detect(struct cs42l42_private *cs42l42)
 {
+	if (cs42l42->remote_active)
+		cs42l83_remote_restore(cs42l42);
+
 	/* Mask tip sense interrupts */
 	regmap_update_bits(cs42l42->regmap,
 				CS42L42_TSRS_PLUG_INT_MASK,
@@ -1485,6 +1723,9 @@ static void cs42l42_init_hs_type_detect(struct cs42l42_private *cs42l42)
 
 static void cs42l42_cancel_hs_type_detect(struct cs42l42_private *cs42l42)
 {
+	if (cs42l42->remote_active)
+		cs42l83_remote_restore(cs42l42);
+
 	/* Mask button detect interrupts */
 	regmap_update_bits(cs42l42->regmap,
 		CS42L42_DET_INT2_MASK,
@@ -1664,6 +1905,7 @@ irqreturn_t cs42l42_irq_thread(int irq, void *data)
 	struct cs42l42_private *cs42l42 = (struct cs42l42_private *)data;
 	unsigned int stickies[12];
 	unsigned int masks[12];
+	unsigned int raw_remote_buttons = 0;
 	unsigned int current_plug_status;
 	unsigned int current_button_status;
 	unsigned int i;
@@ -1686,8 +1928,11 @@ irqreturn_t cs42l42_irq_thread(int irq, void *data)
 				  &masks[i]);
 		if (ret)
 			goto out_error;
-		stickies[i] = stickies[i] & (~masks[i]) &
-				irq_params_table[i].mask;
+		if (cs42l42->remote_active && i == 6)
+			raw_remote_buttons = stickies[i] & CS42L83_REMOTE_BUTTONS;
+		stickies[i] &= ~masks[i];
+		if (!(cs42l42->remote_active && i == 7))
+			stickies[i] &= irq_params_table[i].mask;
 	}
 
 	/* Read tip sense status before handling type detect */
@@ -1709,6 +1954,13 @@ irqreturn_t cs42l42_irq_thread(int irq, void *data)
 	if ((~masks[5]) & irq_params_table[5].mask) {
 		if (stickies[5] & CS42L42_HSDET_AUTO_DONE_MASK) {
 			cs42l42_process_hs_type_detect(cs42l42);
+			if (cs42l42->devid == CS42L83_CHIP_ID) {
+				cs42l83_remote_start(cs42l42);
+				/* Do not interpret pre-identification events as keys. */
+				current_button_status = 0;
+				stickies[7] = 0;
+				raw_remote_buttons = 0;
+			}
 			switch (cs42l42->hs_type) {
 			case CS42L42_PLUG_CTIA:
 			case CS42L42_PLUG_OMTP:
@@ -1759,6 +2011,11 @@ irqreturn_t cs42l42_irq_thread(int irq, void *data)
 		}
 	}
 
+	if (cs42l42->remote_active && cs42l42->plug_state == CS42L42_TS_PLUG) {
+		cs42l83_remote_buttons(cs42l42, raw_remote_buttons, stickies[7]);
+		return IRQ_HANDLED;
+	}
+
 	/* Check button detect status */
 	if (cs42l42->plug_state == CS42L42_TS_PLUG && ((~masks[7]) & irq_params_table[7].mask)) {
 		if (!(current_button_status &
@@ -2169,6 +2426,8 @@ int cs42l42_suspend(struct device *dev)
 	 * is shared.
 	 */
 	scoped_guard(mutex, &cs42l42->irq_lock) {
+		if (cs42l42->remote_active)
+			cs42l83_remote_restore(cs42l42);
 		cs42l42->suspended = true;
 
 		/* Save register values that will be overwritten by shutdown sequence */
diff --git a/sound/soc/codecs/cs42l42.h b/sound/soc/codecs/cs42l42.h
index 3d85ebc59..1065d8cd6 100644
--- a/sound/soc/codecs/cs42l42.h
+++ b/sound/soc/codecs/cs42l42.h
@@ -24,6 +24,19 @@
 #include <sound/soc-component.h>
 #include <sound/soc-dai.h>
 
+enum cs42l83_remote_saved_reg {
+	CS42L83_REMOTE_SAVED_HSBIAS_SC_AUTOCTL,
+	CS42L83_REMOTE_SAVED_WAKE_CTL,
+	CS42L83_REMOTE_SAVED_MISC_DET_CTL,
+	CS42L83_REMOTE_SAVED_MIC_DET_CTL1,
+	CS42L83_REMOTE_SAVED_MIC_DET_CTL2,
+	CS42L83_REMOTE_SAVED_DET_INT1_MASK,
+	CS42L83_REMOTE_SAVED_DET_INT2_MASK,
+	CS42L83_REMOTE_SAVED_HS_BIAS_CTL,
+	CS42L83_REMOTE_SAVED_HSDET_CTL2,
+	CS42L83_REMOTE_NUM_SAVED_REGS,
+};
+
 struct  cs42l42_private {
 	struct regmap *regmap;
 	struct device *dev;
@@ -51,6 +64,8 @@ struct  cs42l42_private {
 	u8 hs_bias_ramp_time;
 	u8 hs_bias_sense_en;
 	u8 stream_use;
+	bool remote_active;
+	unsigned int remote_saved[CS42L83_REMOTE_NUM_SAVED_REGS];
 	bool hp_adc_up_pending;
 	bool suspended;
 	bool sdw_waiting_first_unattach;

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [RFC PATCH 1/2] ASoC: cs42l42: handle jack interrupt I/O and resume errors
  2026-09-17  7:23 ` [RFC PATCH 1/2] ASoC: cs42l42: handle jack interrupt I/O and resume errors Ryan Murray
@ 2026-09-17 20:20   ` Mark Brown
  0 siblings, 0 replies; 4+ messages in thread
From: Mark Brown @ 2026-09-17 20:20 UTC (permalink / raw)
  To: Ryan Murray
  Cc: patches, linux-sound, david.rhodes, rf, lgirdwood, perex, tiwai,
	james.schulman, povik+lin, asahi, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 753 bytes --]

On Thu, Sep 17, 2026 at 05:23:37PM +1000, Ryan Murray wrote:
> A failed status or mask read leaves part of the interrupt snapshot
> uninitialized. The handler can then interpret that data as headset or
> button events. It also attempts register access after a failed runtime
> resume.

> +	if (PM_RUNTIME_ACQUIRE_ERR(&pm))
> +		return IRQ_NONE;
>  	guard(mutex)(&cs42l42->irq_lock);
>  	if (cs42l42->suspended || !cs42l42->init_done)
>  		return IRQ_NONE;

> +		ret = regmap_read(cs42l42->regmap, irq_params_table[i].status_addr,
> +				  &stickies[i]);
> +		if (ret)
> +			goto out_error;

Don't combine goto and scope based handling, there's cases where it's a
compile error and it's just generally asking for trouble due to the
differing approaches.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-09-17 20:20 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-17  7:23 [RFC PATCH 0/2] ASoC: CS42L83 Apple headset remote support Ryan Murray
2026-09-17  7:23 ` [RFC PATCH 1/2] ASoC: cs42l42: handle jack interrupt I/O and resume errors Ryan Murray
2026-09-17 20:20   ` Mark Brown
2026-09-17  7:23 ` [RFC PATCH 2/2] ASoC: cs42l42: support Apple headset remotes on CS42L83 Ryan Murray

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®