Skip to content

Commit 634f3b4

Browse files
simontrimmertiwai
authored andcommitted
ALSA: hda: cs35l56: Perform firmware download in the background
It is possible that during system boot when there multiple devices attempting simultaneous initialization on a slow control bus the download of firmware and tuning data may take a user perceivable amount of time (a slow I2C bus with 4 amps this work could take over 2 seconds). Adopt a pattern used in the ASoC driver and perform this activity in a background thread so that interactive performance is not impaired. The system_long_wq is a parallel workqueue and driver instances will perform their firmware downloads in parallel to make best use of available bus bandwidth. Signed-off-by: Simon Trimmer <[email protected]> Link: https://lore.kernel.org/[email protected] Signed-off-by: Takashi Iwai <[email protected]>
1 parent f900a05 commit 634f3b4

File tree

2 files changed

+76
-17
lines changed

2 files changed

+76
-17
lines changed

sound/pci/hda/cs35l56_hda.c

Lines changed: 73 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,19 @@ static const struct reg_sequence cs35l56_hda_dai_config[] = {
5050

5151
};
5252

53+
static void cs35l56_hda_wait_dsp_ready(struct cs35l56_hda *cs35l56)
54+
{
55+
/* Wait for patching to complete */
56+
flush_work(&cs35l56->dsp_work);
57+
}
58+
5359
static void cs35l56_hda_play(struct cs35l56_hda *cs35l56)
5460
{
5561
unsigned int val;
5662
int ret;
5763

64+
cs35l56_hda_wait_dsp_ready(cs35l56);
65+
5866
pm_runtime_get_sync(cs35l56->base.dev);
5967
ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_PLAY);
6068
if (ret == 0) {
@@ -180,6 +188,8 @@ static int cs35l56_hda_mixer_get(struct snd_kcontrol *kcontrol,
180188
unsigned int reg_val;
181189
int i;
182190

191+
cs35l56_hda_wait_dsp_ready(cs35l56);
192+
183193
regmap_read(cs35l56->base.regmap, kcontrol->private_value, &reg_val);
184194
reg_val &= CS35L56_ASP_TXn_SRC_MASK;
185195

@@ -203,6 +213,8 @@ static int cs35l56_hda_mixer_put(struct snd_kcontrol *kcontrol,
203213
if (item >= CS35L56_NUM_INPUT_SRC)
204214
return -EINVAL;
205215

216+
cs35l56_hda_wait_dsp_ready(cs35l56);
217+
206218
regmap_update_bits_check(cs35l56->base.regmap, kcontrol->private_value,
207219
CS35L56_INPUT_MASK, cs35l56_tx_input_values[item],
208220
&changed);
@@ -227,6 +239,8 @@ static int cs35l56_hda_posture_get(struct snd_kcontrol *kcontrol,
227239
unsigned int pos;
228240
int ret;
229241

242+
cs35l56_hda_wait_dsp_ready(cs35l56);
243+
230244
ret = regmap_read(cs35l56->base.regmap, CS35L56_MAIN_POSTURE_NUMBER, &pos);
231245
if (ret)
232246
return ret;
@@ -248,6 +262,8 @@ static int cs35l56_hda_posture_put(struct snd_kcontrol *kcontrol,
248262
(pos > CS35L56_MAIN_POSTURE_MAX))
249263
return -EINVAL;
250264

265+
cs35l56_hda_wait_dsp_ready(cs35l56);
266+
251267
ret = regmap_update_bits_check(cs35l56->base.regmap,
252268
CS35L56_MAIN_POSTURE_NUMBER,
253269
CS35L56_MAIN_POSTURE_MASK,
@@ -291,6 +307,8 @@ static int cs35l56_hda_vol_get(struct snd_kcontrol *kcontrol,
291307
int vol;
292308
int ret;
293309

310+
cs35l56_hda_wait_dsp_ready(cs35l56);
311+
294312
ret = regmap_read(cs35l56->base.regmap, CS35L56_MAIN_RENDER_USER_VOLUME, &raw_vol);
295313

296314
if (ret)
@@ -323,6 +341,8 @@ static int cs35l56_hda_vol_put(struct snd_kcontrol *kcontrol,
323341
raw_vol = (vol + CS35L56_MAIN_RENDER_USER_VOLUME_MIN) <<
324342
CS35L56_MAIN_RENDER_USER_VOLUME_SHIFT;
325343

344+
cs35l56_hda_wait_dsp_ready(cs35l56);
345+
326346
ret = regmap_update_bits_check(cs35l56->base.regmap,
327347
CS35L56_MAIN_RENDER_USER_VOLUME,
328348
CS35L56_MAIN_RENDER_USER_VOLUME_MASK,
@@ -539,8 +559,9 @@ static void cs35l56_hda_release_firmware_files(const struct firmware *wmfw_firmw
539559
kfree(coeff_filename);
540560
}
541561

542-
static void cs35l56_hda_add_dsp_controls(struct cs35l56_hda *cs35l56)
562+
static void cs35l56_hda_create_dsp_controls_work(struct work_struct *work)
543563
{
564+
struct cs35l56_hda *cs35l56 = container_of(work, struct cs35l56_hda, control_work);
544565
struct hda_cs_dsp_ctl_info info;
545566

546567
info.device_name = cs35l56->amp_name;
@@ -566,23 +587,42 @@ static void cs35l56_hda_apply_calibration(struct cs35l56_hda *cs35l56)
566587
dev_info(cs35l56->base.dev, "Calibration applied\n");
567588
}
568589

569-
static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
590+
static void cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
570591
{
571592
const struct firmware *coeff_firmware = NULL;
572593
const struct firmware *wmfw_firmware = NULL;
573594
char *coeff_filename = NULL;
574595
char *wmfw_filename = NULL;
575596
unsigned int preloaded_fw_ver;
576597
bool firmware_missing;
577-
int ret = 0;
598+
bool add_dsp_controls_required = false;
599+
int ret;
600+
601+
/*
602+
* control_work must be flushed before proceeding, but we can't do that
603+
* here as it would create a deadlock on controls_rwsem so it must be
604+
* performed before queuing dsp_work.
605+
*/
606+
WARN_ON_ONCE(work_busy(&cs35l56->control_work));
578607

579-
/* Prepare for a new DSP power-up */
608+
/*
609+
* Prepare for a new DSP power-up. If the DSP has had firmware
610+
* downloaded previously then it needs to be powered down so that it
611+
* can be updated and if hadn't been patched before then the controls
612+
* will need to be added once firmware download succeeds.
613+
*/
580614
if (cs35l56->base.fw_patched)
581615
cs_dsp_power_down(&cs35l56->cs_dsp);
616+
else
617+
add_dsp_controls_required = true;
582618

583619
cs35l56->base.fw_patched = false;
584620

585-
pm_runtime_get_sync(cs35l56->base.dev);
621+
ret = pm_runtime_resume_and_get(cs35l56->base.dev);
622+
if (ret < 0) {
623+
dev_err(cs35l56->base.dev, "Failed to resume and get %d\n", ret);
624+
return;
625+
}
586626

587627
/*
588628
* The firmware can only be upgraded if it is currently running
@@ -606,7 +646,6 @@ static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
606646
*/
607647
if (!coeff_firmware && firmware_missing) {
608648
dev_err(cs35l56->base.dev, ".bin file required but not found\n");
609-
ret = -ENOENT;
610649
goto err_fw_release;
611650
}
612651

@@ -659,6 +698,15 @@ static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
659698
CS35L56_FIRMWARE_MISSING);
660699
cs35l56->base.fw_patched = true;
661700

701+
/*
702+
* Adding controls is deferred to prevent a lock inversion - ALSA takes
703+
* the controls_rwsem when adding a control, the get() / put()
704+
* functions of a control are called holding controls_rwsem and those
705+
* that depend on running firmware wait for dsp_work() to complete.
706+
*/
707+
if (add_dsp_controls_required)
708+
queue_work(system_long_wq, &cs35l56->control_work);
709+
662710
ret = cs_dsp_run(&cs35l56->cs_dsp);
663711
if (ret)
664712
dev_dbg(cs35l56->base.dev, "%s: cs_dsp_run ret %d\n", __func__, ret);
@@ -678,16 +726,20 @@ static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
678726
coeff_firmware, coeff_filename);
679727
err_pm_put:
680728
pm_runtime_put(cs35l56->base.dev);
729+
}
681730

682-
return ret;
731+
static void cs35l56_hda_dsp_work(struct work_struct *work)
732+
{
733+
struct cs35l56_hda *cs35l56 = container_of(work, struct cs35l56_hda, dsp_work);
734+
735+
cs35l56_hda_fw_load(cs35l56);
683736
}
684737

685738
static int cs35l56_hda_bind(struct device *dev, struct device *master, void *master_data)
686739
{
687740
struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
688741
struct hda_component_parent *parent = master_data;
689742
struct hda_component *comp;
690-
int ret;
691743

692744
comp = hda_component_from_index(parent, cs35l56->index);
693745
if (!comp)
@@ -701,12 +753,10 @@ static int cs35l56_hda_bind(struct device *dev, struct device *master, void *mas
701753
strscpy(comp->name, dev_name(dev), sizeof(comp->name));
702754
comp->playback_hook = cs35l56_hda_playback_hook;
703755

704-
ret = cs35l56_hda_fw_load(cs35l56);
705-
if (ret)
706-
return ret;
756+
flush_work(&cs35l56->control_work);
757+
queue_work(system_long_wq, &cs35l56->dsp_work);
707758

708759
cs35l56_hda_create_controls(cs35l56);
709-
cs35l56_hda_add_dsp_controls(cs35l56);
710760

711761
#if IS_ENABLED(CONFIG_SND_DEBUG)
712762
cs35l56->debugfs_root = debugfs_create_dir(dev_name(cs35l56->base.dev), sound_debugfs_root);
@@ -724,6 +774,9 @@ static void cs35l56_hda_unbind(struct device *dev, struct device *master, void *
724774
struct hda_component_parent *parent = master_data;
725775
struct hda_component *comp;
726776

777+
cancel_work_sync(&cs35l56->dsp_work);
778+
cancel_work_sync(&cs35l56->control_work);
779+
727780
cs35l56_hda_remove_controls(cs35l56);
728781

729782
#if IS_ENABLED(CONFIG_SND_DEBUG)
@@ -752,6 +805,9 @@ static int cs35l56_hda_system_suspend(struct device *dev)
752805
{
753806
struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
754807

808+
cs35l56_hda_wait_dsp_ready(cs35l56);
809+
flush_work(&cs35l56->control_work);
810+
755811
if (cs35l56->playing)
756812
cs35l56_hda_pause(cs35l56);
757813

@@ -850,11 +906,8 @@ static int cs35l56_hda_system_resume(struct device *dev)
850906

851907
ret = cs35l56_is_fw_reload_needed(&cs35l56->base);
852908
dev_dbg(cs35l56->base.dev, "fw_reload_needed: %d\n", ret);
853-
if (ret > 0) {
854-
ret = cs35l56_hda_fw_load(cs35l56);
855-
if (ret)
856-
return ret;
857-
}
909+
if (ret > 0)
910+
queue_work(system_long_wq, &cs35l56->dsp_work);
858911

859912
if (cs35l56->playing)
860913
cs35l56_hda_play(cs35l56);
@@ -972,6 +1025,9 @@ int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int hid, int id)
9721025
mutex_init(&cs35l56->base.irq_lock);
9731026
dev_set_drvdata(cs35l56->base.dev, cs35l56);
9741027

1028+
INIT_WORK(&cs35l56->dsp_work, cs35l56_hda_dsp_work);
1029+
INIT_WORK(&cs35l56->control_work, cs35l56_hda_create_dsp_controls_work);
1030+
9751031
ret = cs35l56_hda_read_acpi(cs35l56, hid, id);
9761032
if (ret)
9771033
goto err;

sound/pci/hda/cs35l56_hda.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,16 @@
1414
#include <linux/firmware/cirrus/cs_dsp.h>
1515
#include <linux/firmware/cirrus/wmfw.h>
1616
#include <linux/regulator/consumer.h>
17+
#include <linux/workqueue.h>
1718
#include <sound/cs35l56.h>
1819

1920
struct dentry;
2021

2122
struct cs35l56_hda {
2223
struct cs35l56_base base;
2324
struct hda_codec *codec;
25+
struct work_struct dsp_work;
26+
struct work_struct control_work;
2427

2528
int index;
2629
const char *system_name;

0 commit comments

Comments
 (0)