Skip to content

Commit 980d97e

Browse files
committed
ASoC/soundwire/qdsp6/wcd: fix leaks and probe deferral
Merge series from Johan Hovold <johan+linaro@kernel.org>: I've been hitting a race during boot which breaks probe of the sound card on the Lenovo ThinkPad X13s as I've previously reported here: https://lore.kernel.org/all/ZIHMMFtuDtvdpFAZ@hovoldconsulting.com/ The immediate issue appeared to be a probe deferral that was turned into a hard failure, but addressing that in itself only made things worse as it exposed further bugs. I was hoping someone more familiar with the code in question would look into this, but as this affects users of the X13s and breaks audio on my machine every fifth boot or so, I decided to investigate it myself. As expected, the Qualcomm codec drivers are broken and specifically leak resources on component remove, which in turn breaks sound card probe deferrals. The source of the deferral itself appears to be legitimate and was simply due to some audio component not yet having been registered due to random changes in timing during boot. These issues can most easily be reproduced by simply blacklisting the q6apm_dai module and loading it manually after boot. Included are also two patches that suppresses error messages on component probe deferral to avoid spamming the logs during boot.
2 parents e231cd8 + f09b6e9 commit 980d97e

File tree

6 files changed

+118
-30
lines changed

6 files changed

+118
-30
lines changed

sound/soc/codecs/wcd-mbhc-v2.c

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1454,7 +1454,7 @@ struct wcd_mbhc *wcd_mbhc_init(struct snd_soc_component *component,
14541454
return ERR_PTR(-EINVAL);
14551455
}
14561456

1457-
mbhc = devm_kzalloc(dev, sizeof(*mbhc), GFP_KERNEL);
1457+
mbhc = kzalloc(sizeof(*mbhc), GFP_KERNEL);
14581458
if (!mbhc)
14591459
return ERR_PTR(-ENOMEM);
14601460

@@ -1474,61 +1474,76 @@ struct wcd_mbhc *wcd_mbhc_init(struct snd_soc_component *component,
14741474

14751475
INIT_WORK(&mbhc->correct_plug_swch, wcd_correct_swch_plug);
14761476

1477-
ret = devm_request_threaded_irq(dev, mbhc->intr_ids->mbhc_sw_intr, NULL,
1477+
ret = request_threaded_irq(mbhc->intr_ids->mbhc_sw_intr, NULL,
14781478
wcd_mbhc_mech_plug_detect_irq,
14791479
IRQF_ONESHOT | IRQF_TRIGGER_RISING,
14801480
"mbhc sw intr", mbhc);
14811481
if (ret)
1482-
goto err;
1482+
goto err_free_mbhc;
14831483

1484-
ret = devm_request_threaded_irq(dev, mbhc->intr_ids->mbhc_btn_press_intr, NULL,
1484+
ret = request_threaded_irq(mbhc->intr_ids->mbhc_btn_press_intr, NULL,
14851485
wcd_mbhc_btn_press_handler,
14861486
IRQF_ONESHOT | IRQF_TRIGGER_RISING,
14871487
"Button Press detect", mbhc);
14881488
if (ret)
1489-
goto err;
1489+
goto err_free_sw_intr;
14901490

1491-
ret = devm_request_threaded_irq(dev, mbhc->intr_ids->mbhc_btn_release_intr, NULL,
1491+
ret = request_threaded_irq(mbhc->intr_ids->mbhc_btn_release_intr, NULL,
14921492
wcd_mbhc_btn_release_handler,
14931493
IRQF_ONESHOT | IRQF_TRIGGER_RISING,
14941494
"Button Release detect", mbhc);
14951495
if (ret)
1496-
goto err;
1496+
goto err_free_btn_press_intr;
14971497

1498-
ret = devm_request_threaded_irq(dev, mbhc->intr_ids->mbhc_hs_ins_intr, NULL,
1498+
ret = request_threaded_irq(mbhc->intr_ids->mbhc_hs_ins_intr, NULL,
14991499
wcd_mbhc_adc_hs_ins_irq,
15001500
IRQF_ONESHOT | IRQF_TRIGGER_RISING,
15011501
"Elect Insert", mbhc);
15021502
if (ret)
1503-
goto err;
1503+
goto err_free_btn_release_intr;
15041504

15051505
disable_irq_nosync(mbhc->intr_ids->mbhc_hs_ins_intr);
15061506

1507-
ret = devm_request_threaded_irq(dev, mbhc->intr_ids->mbhc_hs_rem_intr, NULL,
1507+
ret = request_threaded_irq(mbhc->intr_ids->mbhc_hs_rem_intr, NULL,
15081508
wcd_mbhc_adc_hs_rem_irq,
15091509
IRQF_ONESHOT | IRQF_TRIGGER_RISING,
15101510
"Elect Remove", mbhc);
15111511
if (ret)
1512-
goto err;
1512+
goto err_free_hs_ins_intr;
15131513

15141514
disable_irq_nosync(mbhc->intr_ids->mbhc_hs_rem_intr);
15151515

1516-
ret = devm_request_threaded_irq(dev, mbhc->intr_ids->hph_left_ocp, NULL,
1516+
ret = request_threaded_irq(mbhc->intr_ids->hph_left_ocp, NULL,
15171517
wcd_mbhc_hphl_ocp_irq,
15181518
IRQF_ONESHOT | IRQF_TRIGGER_RISING,
15191519
"HPH_L OCP detect", mbhc);
15201520
if (ret)
1521-
goto err;
1521+
goto err_free_hs_rem_intr;
15221522

1523-
ret = devm_request_threaded_irq(dev, mbhc->intr_ids->hph_right_ocp, NULL,
1523+
ret = request_threaded_irq(mbhc->intr_ids->hph_right_ocp, NULL,
15241524
wcd_mbhc_hphr_ocp_irq,
15251525
IRQF_ONESHOT | IRQF_TRIGGER_RISING,
15261526
"HPH_R OCP detect", mbhc);
15271527
if (ret)
1528-
goto err;
1528+
goto err_free_hph_left_ocp;
15291529

15301530
return mbhc;
1531-
err:
1531+
1532+
err_free_hph_left_ocp:
1533+
free_irq(mbhc->intr_ids->hph_left_ocp, mbhc);
1534+
err_free_hs_rem_intr:
1535+
free_irq(mbhc->intr_ids->mbhc_hs_rem_intr, mbhc);
1536+
err_free_hs_ins_intr:
1537+
free_irq(mbhc->intr_ids->mbhc_hs_ins_intr, mbhc);
1538+
err_free_btn_release_intr:
1539+
free_irq(mbhc->intr_ids->mbhc_btn_release_intr, mbhc);
1540+
err_free_btn_press_intr:
1541+
free_irq(mbhc->intr_ids->mbhc_btn_press_intr, mbhc);
1542+
err_free_sw_intr:
1543+
free_irq(mbhc->intr_ids->mbhc_sw_intr, mbhc);
1544+
err_free_mbhc:
1545+
kfree(mbhc);
1546+
15321547
dev_err(dev, "Failed to request mbhc interrupts %d\n", ret);
15331548

15341549
return ERR_PTR(ret);
@@ -1537,9 +1552,19 @@ EXPORT_SYMBOL(wcd_mbhc_init);
15371552

15381553
void wcd_mbhc_deinit(struct wcd_mbhc *mbhc)
15391554
{
1555+
free_irq(mbhc->intr_ids->hph_right_ocp, mbhc);
1556+
free_irq(mbhc->intr_ids->hph_left_ocp, mbhc);
1557+
free_irq(mbhc->intr_ids->mbhc_hs_rem_intr, mbhc);
1558+
free_irq(mbhc->intr_ids->mbhc_hs_ins_intr, mbhc);
1559+
free_irq(mbhc->intr_ids->mbhc_btn_release_intr, mbhc);
1560+
free_irq(mbhc->intr_ids->mbhc_btn_press_intr, mbhc);
1561+
free_irq(mbhc->intr_ids->mbhc_sw_intr, mbhc);
1562+
15401563
mutex_lock(&mbhc->lock);
15411564
wcd_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
15421565
mutex_unlock(&mbhc->lock);
1566+
1567+
kfree(mbhc);
15431568
}
15441569
EXPORT_SYMBOL(wcd_mbhc_deinit);
15451570

sound/soc/codecs/wcd934x.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3044,6 +3044,17 @@ static int wcd934x_mbhc_init(struct snd_soc_component *component)
30443044

30453045
return 0;
30463046
}
3047+
3048+
static void wcd934x_mbhc_deinit(struct snd_soc_component *component)
3049+
{
3050+
struct wcd934x_codec *wcd = snd_soc_component_get_drvdata(component);
3051+
3052+
if (!wcd->mbhc)
3053+
return;
3054+
3055+
wcd_mbhc_deinit(wcd->mbhc);
3056+
}
3057+
30473058
static int wcd934x_comp_probe(struct snd_soc_component *component)
30483059
{
30493060
struct wcd934x_codec *wcd = dev_get_drvdata(component->dev);
@@ -3077,6 +3088,7 @@ static void wcd934x_comp_remove(struct snd_soc_component *comp)
30773088
{
30783089
struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev);
30793090

3091+
wcd934x_mbhc_deinit(comp);
30803092
wcd_clsh_ctrl_free(wcd->clsh_ctrl);
30813093
}
30823094

sound/soc/codecs/wcd938x.c

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2636,6 +2636,14 @@ static int wcd938x_mbhc_init(struct snd_soc_component *component)
26362636

26372637
return 0;
26382638
}
2639+
2640+
static void wcd938x_mbhc_deinit(struct snd_soc_component *component)
2641+
{
2642+
struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
2643+
2644+
wcd_mbhc_deinit(wcd938x->wcd_mbhc);
2645+
}
2646+
26392647
/* END MBHC */
26402648

26412649
static const struct snd_kcontrol_new wcd938x_snd_controls[] = {
@@ -3106,6 +3114,10 @@ static int wcd938x_soc_codec_probe(struct snd_soc_component *component)
31063114
WCD938X_ID_MASK);
31073115

31083116
wcd938x->clsh_info = wcd_clsh_ctrl_alloc(component, WCD938X);
3117+
if (IS_ERR(wcd938x->clsh_info)) {
3118+
pm_runtime_put(dev);
3119+
return PTR_ERR(wcd938x->clsh_info);
3120+
}
31093121

31103122
wcd938x_io_init(wcd938x);
31113123
/* Set all interrupts as edge triggered */
@@ -3127,20 +3139,26 @@ static int wcd938x_soc_codec_probe(struct snd_soc_component *component)
31273139
ret = request_threaded_irq(wcd938x->hphr_pdm_wd_int, NULL, wcd938x_wd_handle_irq,
31283140
IRQF_ONESHOT | IRQF_TRIGGER_RISING,
31293141
"HPHR PDM WD INT", wcd938x);
3130-
if (ret)
3142+
if (ret) {
31313143
dev_err(dev, "Failed to request HPHR WD interrupt (%d)\n", ret);
3144+
goto err_free_clsh_ctrl;
3145+
}
31323146

31333147
ret = request_threaded_irq(wcd938x->hphl_pdm_wd_int, NULL, wcd938x_wd_handle_irq,
31343148
IRQF_ONESHOT | IRQF_TRIGGER_RISING,
31353149
"HPHL PDM WD INT", wcd938x);
3136-
if (ret)
3150+
if (ret) {
31373151
dev_err(dev, "Failed to request HPHL WD interrupt (%d)\n", ret);
3152+
goto err_free_hphr_pdm_wd_int;
3153+
}
31383154

31393155
ret = request_threaded_irq(wcd938x->aux_pdm_wd_int, NULL, wcd938x_wd_handle_irq,
31403156
IRQF_ONESHOT | IRQF_TRIGGER_RISING,
31413157
"AUX PDM WD INT", wcd938x);
3142-
if (ret)
3158+
if (ret) {
31433159
dev_err(dev, "Failed to request Aux WD interrupt (%d)\n", ret);
3160+
goto err_free_hphl_pdm_wd_int;
3161+
}
31443162

31453163
/* Disable watchdog interrupt for HPH and AUX */
31463164
disable_irq_nosync(wcd938x->hphr_pdm_wd_int);
@@ -3155,7 +3173,7 @@ static int wcd938x_soc_codec_probe(struct snd_soc_component *component)
31553173
dev_err(component->dev,
31563174
"%s: Failed to add snd ctrls for variant: %d\n",
31573175
__func__, wcd938x->variant);
3158-
goto err;
3176+
goto err_free_aux_pdm_wd_int;
31593177
}
31603178
break;
31613179
case WCD9385:
@@ -3165,20 +3183,46 @@ static int wcd938x_soc_codec_probe(struct snd_soc_component *component)
31653183
dev_err(component->dev,
31663184
"%s: Failed to add snd ctrls for variant: %d\n",
31673185
__func__, wcd938x->variant);
3168-
goto err;
3186+
goto err_free_aux_pdm_wd_int;
31693187
}
31703188
break;
31713189
default:
31723190
break;
31733191
}
31743192

31753193
ret = wcd938x_mbhc_init(component);
3176-
if (ret)
3194+
if (ret) {
31773195
dev_err(component->dev, "mbhc initialization failed\n");
3178-
err:
3196+
goto err_free_aux_pdm_wd_int;
3197+
}
3198+
3199+
return 0;
3200+
3201+
err_free_aux_pdm_wd_int:
3202+
free_irq(wcd938x->aux_pdm_wd_int, wcd938x);
3203+
err_free_hphl_pdm_wd_int:
3204+
free_irq(wcd938x->hphl_pdm_wd_int, wcd938x);
3205+
err_free_hphr_pdm_wd_int:
3206+
free_irq(wcd938x->hphr_pdm_wd_int, wcd938x);
3207+
err_free_clsh_ctrl:
3208+
wcd_clsh_ctrl_free(wcd938x->clsh_info);
3209+
31793210
return ret;
31803211
}
31813212

3213+
static void wcd938x_soc_codec_remove(struct snd_soc_component *component)
3214+
{
3215+
struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
3216+
3217+
wcd938x_mbhc_deinit(component);
3218+
3219+
free_irq(wcd938x->aux_pdm_wd_int, wcd938x);
3220+
free_irq(wcd938x->hphl_pdm_wd_int, wcd938x);
3221+
free_irq(wcd938x->hphr_pdm_wd_int, wcd938x);
3222+
3223+
wcd_clsh_ctrl_free(wcd938x->clsh_info);
3224+
}
3225+
31823226
static int wcd938x_codec_set_jack(struct snd_soc_component *comp,
31833227
struct snd_soc_jack *jack, void *data)
31843228
{
@@ -3195,6 +3239,7 @@ static int wcd938x_codec_set_jack(struct snd_soc_component *comp,
31953239
static const struct snd_soc_component_driver soc_codec_dev_wcd938x = {
31963240
.name = "wcd938x_codec",
31973241
.probe = wcd938x_soc_codec_probe,
3242+
.remove = wcd938x_soc_codec_remove,
31983243
.controls = wcd938x_snd_controls,
31993244
.num_controls = ARRAY_SIZE(wcd938x_snd_controls),
32003245
.dapm_widgets = wcd938x_dapm_widgets,

sound/soc/qcom/qdsp6/topology.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1277,8 +1277,8 @@ int audioreach_tplg_init(struct snd_soc_component *component)
12771277

12781278
ret = snd_soc_tplg_component_load(component, &audioreach_tplg_ops, fw);
12791279
if (ret < 0) {
1280-
dev_err(dev, "tplg component load failed%d\n", ret);
1281-
ret = -EINVAL;
1280+
if (ret != -EPROBE_DEFER)
1281+
dev_err(dev, "tplg component load failed: %d\n", ret);
12821282
}
12831283

12841284
release_firmware(fw);

sound/soc/soc-core.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1988,8 +1988,10 @@ static int snd_soc_bind_card(struct snd_soc_card *card)
19881988
/* probe all components used by DAI links on this card */
19891989
ret = soc_probe_link_components(card);
19901990
if (ret < 0) {
1991-
dev_err(card->dev,
1992-
"ASoC: failed to instantiate card %d\n", ret);
1991+
if (ret != -EPROBE_DEFER) {
1992+
dev_err(card->dev,
1993+
"ASoC: failed to instantiate card %d\n", ret);
1994+
}
19931995
goto probe_end;
19941996
}
19951997

sound/soc/soc-topology.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1732,7 +1732,8 @@ static int soc_tplg_fe_link_create(struct soc_tplg *tplg,
17321732

17331733
ret = snd_soc_add_pcm_runtimes(tplg->comp->card, link, 1);
17341734
if (ret < 0) {
1735-
dev_err(tplg->dev, "ASoC: adding FE link failed\n");
1735+
if (ret != -EPROBE_DEFER)
1736+
dev_err(tplg->dev, "ASoC: adding FE link failed\n");
17361737
goto err;
17371738
}
17381739

@@ -2492,8 +2493,11 @@ static int soc_tplg_process_headers(struct soc_tplg *tplg)
24922493
/* load the header object */
24932494
ret = soc_tplg_load_header(tplg, hdr);
24942495
if (ret < 0) {
2495-
dev_err(tplg->dev,
2496-
"ASoC: topology: could not load header: %d\n", ret);
2496+
if (ret != -EPROBE_DEFER) {
2497+
dev_err(tplg->dev,
2498+
"ASoC: topology: could not load header: %d\n",
2499+
ret);
2500+
}
24972501
return ret;
24982502
}
24992503

0 commit comments

Comments
 (0)