Skip to content

Commit f1a450f

Browse files
committed
ASoC: nau8821: Fix IRQ handling and improve jack
Merge series from Cristian Ciocaltea <[email protected]>: This patch series addresses a set of issues in the Nuvoton NAU88L21 audio codec driver related to interrupt handling and jack hotplug detection reliability. The changes focus on: * Eliminating race conditions between jack insertion and ejection events * Ensuring interrupts are consistently and correctly cleared before unmasking * Introducing a DMI-based quirk to bypass the jack debounce circuit on Valve Steam Deck, improving detection accuracy under stress * Improving robustness of the IRQ handler by avoiding unnecessary blocking operations The series has been tested on affected hardware to verify correct behavior during repeated and rapid jack hotplug cycles.
2 parents 62ef9b2 + ee70bac commit f1a450f

File tree

2 files changed

+81
-50
lines changed

2 files changed

+81
-50
lines changed

sound/soc/codecs/nau8821.c

Lines changed: 80 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626
#include <sound/tlv.h>
2727
#include "nau8821.h"
2828

29-
#define NAU8821_JD_ACTIVE_HIGH BIT(0)
29+
#define NAU8821_QUIRK_JD_ACTIVE_HIGH BIT(0)
30+
#define NAU8821_QUIRK_JD_DB_BYPASS BIT(1)
3031

3132
static int nau8821_quirk;
3233
static int quirk_override = -1;
@@ -1021,12 +1022,17 @@ static bool nau8821_is_jack_inserted(struct regmap *regmap)
10211022
return active_high == is_high;
10221023
}
10231024

1024-
static void nau8821_int_status_clear_all(struct regmap *regmap)
1025+
static void nau8821_irq_status_clear(struct regmap *regmap, int active_irq)
10251026
{
1026-
int active_irq, clear_irq, i;
1027+
int clear_irq, i;
10271028

1028-
/* Reset the intrruption status from rightmost bit if the corres-
1029-
* ponding irq event occurs.
1029+
if (active_irq) {
1030+
regmap_write(regmap, NAU8821_R11_INT_CLR_KEY_STATUS, active_irq);
1031+
return;
1032+
}
1033+
1034+
/* Reset the interruption status from rightmost bit if the
1035+
* corresponding irq event occurs.
10301036
*/
10311037
regmap_read(regmap, NAU8821_R10_IRQ_STATUS, &active_irq);
10321038
for (i = 0; i < NAU8821_REG_DATA_LEN; i++) {
@@ -1052,20 +1058,24 @@ static void nau8821_eject_jack(struct nau8821 *nau8821)
10521058
snd_soc_component_disable_pin(component, "MICBIAS");
10531059
snd_soc_dapm_sync(dapm);
10541060

1061+
/* Disable & mask both insertion & ejection IRQs */
1062+
regmap_update_bits(regmap, NAU8821_R12_INTERRUPT_DIS_CTRL,
1063+
NAU8821_IRQ_INSERT_DIS | NAU8821_IRQ_EJECT_DIS,
1064+
NAU8821_IRQ_INSERT_DIS | NAU8821_IRQ_EJECT_DIS);
1065+
regmap_update_bits(regmap, NAU8821_R0F_INTERRUPT_MASK,
1066+
NAU8821_IRQ_INSERT_EN | NAU8821_IRQ_EJECT_EN,
1067+
NAU8821_IRQ_INSERT_EN | NAU8821_IRQ_EJECT_EN);
1068+
10551069
/* Clear all interruption status */
1056-
nau8821_int_status_clear_all(regmap);
1070+
nau8821_irq_status_clear(regmap, 0);
10571071

1058-
/* Enable the insertion interruption, disable the ejection inter-
1059-
* ruption, and then bypass de-bounce circuit.
1060-
*/
1072+
/* Enable & unmask the insertion IRQ */
10611073
regmap_update_bits(regmap, NAU8821_R12_INTERRUPT_DIS_CTRL,
1062-
NAU8821_IRQ_EJECT_DIS | NAU8821_IRQ_INSERT_DIS,
1063-
NAU8821_IRQ_EJECT_DIS);
1064-
/* Mask unneeded IRQs: 1 - disable, 0 - enable */
1074+
NAU8821_IRQ_INSERT_DIS, 0);
10651075
regmap_update_bits(regmap, NAU8821_R0F_INTERRUPT_MASK,
1066-
NAU8821_IRQ_EJECT_EN | NAU8821_IRQ_INSERT_EN,
1067-
NAU8821_IRQ_EJECT_EN);
1076+
NAU8821_IRQ_INSERT_EN, 0);
10681077

1078+
/* Bypass de-bounce circuit */
10691079
regmap_update_bits(regmap, NAU8821_R0D_JACK_DET_CTRL,
10701080
NAU8821_JACK_DET_DB_BYPASS, NAU8821_JACK_DET_DB_BYPASS);
10711081

@@ -1089,22 +1099,17 @@ static void nau8821_eject_jack(struct nau8821 *nau8821)
10891099
NAU8821_IRQ_KEY_RELEASE_DIS |
10901100
NAU8821_IRQ_KEY_PRESS_DIS);
10911101
}
1092-
10931102
}
10941103

10951104
static void nau8821_jdet_work(struct work_struct *work)
10961105
{
10971106
struct nau8821 *nau8821 =
1098-
container_of(work, struct nau8821, jdet_work);
1107+
container_of(work, struct nau8821, jdet_work.work);
10991108
struct snd_soc_dapm_context *dapm = nau8821->dapm;
11001109
struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
11011110
struct regmap *regmap = nau8821->regmap;
11021111
int jack_status_reg, mic_detected, event = 0, event_mask = 0;
11031112

1104-
snd_soc_component_force_enable_pin(component, "MICBIAS");
1105-
snd_soc_dapm_sync(dapm);
1106-
msleep(20);
1107-
11081113
regmap_read(regmap, NAU8821_R58_I2C_DEVICE_ID, &jack_status_reg);
11091114
mic_detected = !(jack_status_reg & NAU8821_KEYDET);
11101115
if (mic_detected) {
@@ -1137,6 +1142,7 @@ static void nau8821_jdet_work(struct work_struct *work)
11371142
snd_soc_component_disable_pin(component, "MICBIAS");
11381143
snd_soc_dapm_sync(dapm);
11391144
}
1145+
11401146
event_mask |= SND_JACK_HEADSET;
11411147
snd_soc_jack_report(nau8821->jack, event, event_mask);
11421148
}
@@ -1146,6 +1152,15 @@ static void nau8821_setup_inserted_irq(struct nau8821 *nau8821)
11461152
{
11471153
struct regmap *regmap = nau8821->regmap;
11481154

1155+
/* Disable & mask insertion IRQ */
1156+
regmap_update_bits(regmap, NAU8821_R12_INTERRUPT_DIS_CTRL,
1157+
NAU8821_IRQ_INSERT_DIS, NAU8821_IRQ_INSERT_DIS);
1158+
regmap_update_bits(regmap, NAU8821_R0F_INTERRUPT_MASK,
1159+
NAU8821_IRQ_INSERT_EN, NAU8821_IRQ_INSERT_EN);
1160+
1161+
/* Clear insert IRQ status */
1162+
nau8821_irq_status_clear(regmap, NAU8821_JACK_INSERT_DETECTED);
1163+
11491164
/* Enable internal VCO needed for interruptions */
11501165
if (nau8821->dapm->bias_level < SND_SOC_BIAS_PREPARE)
11511166
nau8821_configure_sysclk(nau8821, NAU8821_CLK_INTERNAL, 0);
@@ -1160,21 +1175,24 @@ static void nau8821_setup_inserted_irq(struct nau8821 *nau8821)
11601175
regmap_update_bits(regmap, NAU8821_R1D_I2S_PCM_CTRL2,
11611176
NAU8821_I2S_MS_MASK, NAU8821_I2S_MS_SLAVE);
11621177

1163-
/* Not bypass de-bounce circuit */
1164-
regmap_update_bits(regmap, NAU8821_R0D_JACK_DET_CTRL,
1165-
NAU8821_JACK_DET_DB_BYPASS, 0);
1178+
/* Do not bypass de-bounce circuit */
1179+
if (!(nau8821_quirk & NAU8821_QUIRK_JD_DB_BYPASS))
1180+
regmap_update_bits(regmap, NAU8821_R0D_JACK_DET_CTRL,
1181+
NAU8821_JACK_DET_DB_BYPASS, 0);
11661182

1183+
/* Unmask & enable the ejection IRQs */
11671184
regmap_update_bits(regmap, NAU8821_R0F_INTERRUPT_MASK,
1168-
NAU8821_IRQ_EJECT_EN, 0);
1185+
NAU8821_IRQ_EJECT_EN, 0);
11691186
regmap_update_bits(regmap, NAU8821_R12_INTERRUPT_DIS_CTRL,
1170-
NAU8821_IRQ_EJECT_DIS, 0);
1187+
NAU8821_IRQ_EJECT_DIS, 0);
11711188
}
11721189

11731190
static irqreturn_t nau8821_interrupt(int irq, void *data)
11741191
{
11751192
struct nau8821 *nau8821 = (struct nau8821 *)data;
11761193
struct regmap *regmap = nau8821->regmap;
1177-
int active_irq, clear_irq = 0, event = 0, event_mask = 0;
1194+
struct snd_soc_component *component;
1195+
int active_irq, event = 0, event_mask = 0;
11781196

11791197
if (regmap_read(regmap, NAU8821_R10_IRQ_STATUS, &active_irq)) {
11801198
dev_err(nau8821->dev, "failed to read irq status\n");
@@ -1185,48 +1203,41 @@ static irqreturn_t nau8821_interrupt(int irq, void *data)
11851203

11861204
if ((active_irq & NAU8821_JACK_EJECT_IRQ_MASK) ==
11871205
NAU8821_JACK_EJECT_DETECTED) {
1206+
cancel_delayed_work_sync(&nau8821->jdet_work);
11881207
regmap_update_bits(regmap, NAU8821_R71_ANALOG_ADC_1,
11891208
NAU8821_MICDET_MASK, NAU8821_MICDET_DIS);
11901209
nau8821_eject_jack(nau8821);
11911210
event_mask |= SND_JACK_HEADSET;
1192-
clear_irq = NAU8821_JACK_EJECT_IRQ_MASK;
11931211
} else if (active_irq & NAU8821_KEY_SHORT_PRESS_IRQ) {
11941212
event |= NAU8821_BUTTON;
11951213
event_mask |= NAU8821_BUTTON;
1196-
clear_irq = NAU8821_KEY_SHORT_PRESS_IRQ;
1214+
nau8821_irq_status_clear(regmap, NAU8821_KEY_SHORT_PRESS_IRQ);
11971215
} else if (active_irq & NAU8821_KEY_RELEASE_IRQ) {
11981216
event_mask = NAU8821_BUTTON;
1199-
clear_irq = NAU8821_KEY_RELEASE_IRQ;
1217+
nau8821_irq_status_clear(regmap, NAU8821_KEY_RELEASE_IRQ);
12001218
} else if ((active_irq & NAU8821_JACK_INSERT_IRQ_MASK) ==
12011219
NAU8821_JACK_INSERT_DETECTED) {
1220+
cancel_delayed_work_sync(&nau8821->jdet_work);
12021221
regmap_update_bits(regmap, NAU8821_R71_ANALOG_ADC_1,
12031222
NAU8821_MICDET_MASK, NAU8821_MICDET_EN);
12041223
if (nau8821_is_jack_inserted(regmap)) {
1205-
/* detect microphone and jack type */
1206-
cancel_work_sync(&nau8821->jdet_work);
1207-
schedule_work(&nau8821->jdet_work);
1224+
/* Detect microphone and jack type */
1225+
component = snd_soc_dapm_to_component(nau8821->dapm);
1226+
snd_soc_component_force_enable_pin(component, "MICBIAS");
1227+
snd_soc_dapm_sync(nau8821->dapm);
1228+
schedule_delayed_work(&nau8821->jdet_work, msecs_to_jiffies(20));
12081229
/* Turn off insertion interruption at manual mode */
1209-
regmap_update_bits(regmap,
1210-
NAU8821_R12_INTERRUPT_DIS_CTRL,
1211-
NAU8821_IRQ_INSERT_DIS,
1212-
NAU8821_IRQ_INSERT_DIS);
1213-
regmap_update_bits(regmap,
1214-
NAU8821_R0F_INTERRUPT_MASK,
1215-
NAU8821_IRQ_INSERT_EN,
1216-
NAU8821_IRQ_INSERT_EN);
12171230
nau8821_setup_inserted_irq(nau8821);
12181231
} else {
12191232
dev_warn(nau8821->dev,
12201233
"Inserted IRQ fired but not connected\n");
12211234
nau8821_eject_jack(nau8821);
12221235
}
1236+
} else {
1237+
/* Clear the rightmost interrupt */
1238+
nau8821_irq_status_clear(regmap, active_irq);
12231239
}
12241240

1225-
if (!clear_irq)
1226-
clear_irq = active_irq;
1227-
/* clears the rightmost interruption */
1228-
regmap_write(regmap, NAU8821_R11_INT_CLR_KEY_STATUS, clear_irq);
1229-
12301241
if (event_mask)
12311242
snd_soc_jack_report(nau8821->jack, event, event_mask);
12321243

@@ -1521,7 +1532,7 @@ static int nau8821_resume_setup(struct nau8821 *nau8821)
15211532
nau8821_configure_sysclk(nau8821, NAU8821_CLK_DIS, 0);
15221533
if (nau8821->irq) {
15231534
/* Clear all interruption status */
1524-
nau8821_int_status_clear_all(regmap);
1535+
nau8821_irq_status_clear(regmap, 0);
15251536

15261537
/* Enable both insertion and ejection interruptions, and then
15271538
* bypass de-bounce circuit.
@@ -1651,7 +1662,8 @@ int nau8821_enable_jack_detect(struct snd_soc_component *component,
16511662

16521663
nau8821->jack = jack;
16531664
/* Initiate jack detection work queue */
1654-
INIT_WORK(&nau8821->jdet_work, nau8821_jdet_work);
1665+
INIT_DELAYED_WORK(&nau8821->jdet_work, nau8821_jdet_work);
1666+
16551667
ret = devm_request_threaded_irq(nau8821->dev, nau8821->irq, NULL,
16561668
nau8821_interrupt, IRQF_TRIGGER_LOW | IRQF_ONESHOT,
16571669
"nau8821", nau8821);
@@ -1856,7 +1868,23 @@ static const struct dmi_system_id nau8821_quirk_table[] = {
18561868
DMI_MATCH(DMI_SYS_VENDOR, "Positivo Tecnologia SA"),
18571869
DMI_MATCH(DMI_BOARD_NAME, "CW14Q01P-V2"),
18581870
},
1859-
.driver_data = (void *)(NAU8821_JD_ACTIVE_HIGH),
1871+
.driver_data = (void *)(NAU8821_QUIRK_JD_ACTIVE_HIGH),
1872+
},
1873+
{
1874+
/* Valve Steam Deck LCD */
1875+
.matches = {
1876+
DMI_MATCH(DMI_SYS_VENDOR, "Valve"),
1877+
DMI_MATCH(DMI_PRODUCT_NAME, "Jupiter"),
1878+
},
1879+
.driver_data = (void *)(NAU8821_QUIRK_JD_DB_BYPASS),
1880+
},
1881+
{
1882+
/* Valve Steam Deck OLED */
1883+
.matches = {
1884+
DMI_MATCH(DMI_SYS_VENDOR, "Valve"),
1885+
DMI_MATCH(DMI_PRODUCT_NAME, "Galileo"),
1886+
},
1887+
.driver_data = (void *)(NAU8821_QUIRK_JD_DB_BYPASS),
18601888
},
18611889
{}
18621890
};
@@ -1898,9 +1926,12 @@ static int nau8821_i2c_probe(struct i2c_client *i2c)
18981926

18991927
nau8821_check_quirks();
19001928

1901-
if (nau8821_quirk & NAU8821_JD_ACTIVE_HIGH)
1929+
if (nau8821_quirk & NAU8821_QUIRK_JD_ACTIVE_HIGH)
19021930
nau8821->jkdet_polarity = 0;
19031931

1932+
if (nau8821_quirk & NAU8821_QUIRK_JD_DB_BYPASS)
1933+
dev_dbg(dev, "Force bypassing jack detection debounce circuit\n");
1934+
19041935
nau8821_print_device_properties(nau8821);
19051936

19061937
nau8821_reset_chip(nau8821->regmap);

sound/soc/codecs/nau8821.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -561,7 +561,7 @@ struct nau8821 {
561561
struct regmap *regmap;
562562
struct snd_soc_dapm_context *dapm;
563563
struct snd_soc_jack *jack;
564-
struct work_struct jdet_work;
564+
struct delayed_work jdet_work;
565565
int irq;
566566
int clk_id;
567567
int micbias_voltage;

0 commit comments

Comments
 (0)