[alsa-devel] [PATCH 1/2 v2] ASoC: wm_adsp: Factor out ADSP2 boot proceedure
Move the ADSP2 boot proceedure into a work structure in preparation for running it asynchronously with the reset of the audio path bring up.
Signed-off-by: Charles Keepax ckeepax@opensource.wolfsonmicro.com --- sound/soc/codecs/wm_adsp.c | 181 +++++++++++++++++++++++++------------------- sound/soc/codecs/wm_adsp.h | 2 + 2 files changed, 105 insertions(+), 78 deletions(-)
Changes since v1: - Schedule to the system unbound work queue since it is likely to be a fairly long task. - Move final start signal to the DSP to be an out_drv widget such that the download is overlapped with the output enables.
Thanks, Charles
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index fb0c678..41bbac9 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -1497,107 +1497,131 @@ static int wm_adsp2_ena(struct wm_adsp *dsp) return 0; }
-int wm_adsp2_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) +void wm_adsp2_boot_work(struct work_struct *work) { - struct snd_soc_codec *codec = w->codec; - struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); - struct wm_adsp *dsp = &dsps[w->shift]; - struct wm_adsp_alg_region *alg_region; - struct wm_coeff_ctl *ctl; - unsigned int val; + struct wm_adsp *dsp = container_of(work, + struct wm_adsp, + boot_work); int ret; + unsigned int val;
- dsp->card = codec->card; + /* + * For simplicity set the DSP clock rate to be the + * SYSCLK rate rather than making it configurable. + */ + ret = regmap_read(dsp->regmap, ARIZONA_SYSTEM_CLOCK_1, &val); + if (ret != 0) { + adsp_err(dsp, "Failed to read SYSCLK state: %d\n", ret); + return; + } + val = (val & ARIZONA_SYSCLK_FREQ_MASK) + >> ARIZONA_SYSCLK_FREQ_SHIFT;
- switch (event) { - case SND_SOC_DAPM_POST_PMU: - /* - * For simplicity set the DSP clock rate to be the - * SYSCLK rate rather than making it configurable. - */ - ret = regmap_read(dsp->regmap, ARIZONA_SYSTEM_CLOCK_1, &val); - if (ret != 0) { - adsp_err(dsp, "Failed to read SYSCLK state: %d\n", - ret); - return ret; - } - val = (val & ARIZONA_SYSCLK_FREQ_MASK) - >> ARIZONA_SYSCLK_FREQ_SHIFT; + ret = regmap_update_bits_async(dsp->regmap, + dsp->base + ADSP2_CLOCKING, + ADSP2_CLK_SEL_MASK, val); + if (ret != 0) { + adsp_err(dsp, "Failed to set clock rate: %d\n", ret); + return; + }
- ret = regmap_update_bits_async(dsp->regmap, - dsp->base + ADSP2_CLOCKING, - ADSP2_CLK_SEL_MASK, val); + if (dsp->dvfs) { + ret = regmap_read(dsp->regmap, + dsp->base + ADSP2_CLOCKING, &val); if (ret != 0) { - adsp_err(dsp, "Failed to set clock rate: %d\n", - ret); - return ret; + dev_err(dsp->dev, "Failed to read clocking: %d\n", ret); + return; }
- if (dsp->dvfs) { - ret = regmap_read(dsp->regmap, - dsp->base + ADSP2_CLOCKING, &val); + if ((val & ADSP2_CLK_SEL_MASK) >= 3) { + ret = regulator_enable(dsp->dvfs); if (ret != 0) { dev_err(dsp->dev, - "Failed to read clocking: %d\n", ret); - return ret; + "Failed to enable supply: %d\n", + ret); + return; }
- if ((val & ADSP2_CLK_SEL_MASK) >= 3) { - ret = regulator_enable(dsp->dvfs); - if (ret != 0) { - dev_err(dsp->dev, - "Failed to enable supply: %d\n", - ret); - return ret; - } - - ret = regulator_set_voltage(dsp->dvfs, - 1800000, - 1800000); - if (ret != 0) { - dev_err(dsp->dev, - "Failed to raise supply: %d\n", - ret); - return ret; - } + ret = regulator_set_voltage(dsp->dvfs, + 1800000, + 1800000); + if (ret != 0) { + dev_err(dsp->dev, + "Failed to raise supply: %d\n", + ret); + return; } } + }
- ret = wm_adsp2_ena(dsp); - if (ret != 0) - return ret; + ret = wm_adsp2_ena(dsp); + if (ret != 0) + return;
- ret = wm_adsp_load(dsp); - if (ret != 0) - goto err; + ret = wm_adsp_load(dsp); + if (ret != 0) + goto err;
- ret = wm_adsp_setup_algs(dsp); - if (ret != 0) - goto err; + ret = wm_adsp_setup_algs(dsp); + if (ret != 0) + goto err;
- ret = wm_adsp_load_coeff(dsp); - if (ret != 0) - goto err; + ret = wm_adsp_load_coeff(dsp); + if (ret != 0) + goto err;
- /* Initialize caches for enabled and unset controls */ - ret = wm_coeff_init_control_caches(dsp); - if (ret != 0) - goto err; + /* Initialize caches for enabled and unset controls */ + ret = wm_coeff_init_control_caches(dsp); + if (ret != 0) + goto err;
- /* Sync set controls */ - ret = wm_coeff_sync_controls(dsp); - if (ret != 0) - goto err; + /* Sync set controls */ + ret = wm_coeff_sync_controls(dsp); + if (ret != 0) + goto err; + + ret = regmap_update_bits_async(dsp->regmap, + dsp->base + ADSP2_CONTROL, + ADSP2_CORE_ENA, + ADSP2_CORE_ENA); + if (ret != 0) + goto err; + + dsp->running = true; + + return;
- ret = regmap_update_bits_async(dsp->regmap, - dsp->base + ADSP2_CONTROL, - ADSP2_CORE_ENA | ADSP2_START, - ADSP2_CORE_ENA | ADSP2_START); +err: + regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, + ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0); +} + +int wm_adsp2_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); + struct wm_adsp *dsp = &dsps[w->shift]; + struct wm_adsp_alg_region *alg_region; + struct wm_coeff_ctl *ctl; + int ret; + + dsp->card = codec->card; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + queue_work(system_unbound_wq, &dsp->boot_work); + flush_work(&dsp->boot_work); + + if (!dsp->running) + return -EIO; + + ret = regmap_update_bits(dsp->regmap, + dsp->base + ADSP2_CONTROL, + ADSP2_START, + ADSP2_START); if (ret != 0) goto err; - - dsp->running = true; break;
case SND_SOC_DAPM_PRE_PMD: @@ -1668,6 +1692,7 @@ int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs)
INIT_LIST_HEAD(&adsp->alg_regions); INIT_LIST_HEAD(&adsp->ctl_list); + INIT_WORK(&adsp->boot_work, wm_adsp2_boot_work);
if (dvfs) { adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD"); diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index d018dea..b172c1d 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -59,6 +59,8 @@ struct wm_adsp { struct regulator *dvfs;
struct list_head ctl_list; + + struct work_struct boot_work; };
#define WM_ADSP1(wname, num) \
Move the start of booting the DSP to earlier in the DAPM process, and move the final starting of the DSP to later in the DAPM process. This allows us to overlap some of the processing with other components of the system being brought up.
Signed-off-by: Charles Keepax ckeepax@opensource.wolfsonmicro.com --- sound/soc/codecs/arizona.h | 17 +++++++++-------- sound/soc/codecs/wm_adsp.c | 24 +++++++++++++++++++++--- sound/soc/codecs/wm_adsp.h | 10 ++++++++-- 3 files changed, 38 insertions(+), 13 deletions(-)
diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h index 10b3984..16df0f9 100644 --- a/sound/soc/codecs/arizona.h +++ b/sound/soc/codecs/arizona.h @@ -166,20 +166,21 @@ extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS]; ARIZONA_MIXER_INPUT_ROUTES(name " Input 4")
#define ARIZONA_DSP_ROUTES(name) \ - { name, NULL, name " Aux 1" }, \ - { name, NULL, name " Aux 2" }, \ - { name, NULL, name " Aux 3" }, \ - { name, NULL, name " Aux 4" }, \ - { name, NULL, name " Aux 5" }, \ - { name, NULL, name " Aux 6" }, \ + { name, NULL, name " Preloader"}, \ + { name " Preloader", NULL, name " Aux 1" }, \ + { name " Preloader", NULL, name " Aux 2" }, \ + { name " Preloader", NULL, name " Aux 3" }, \ + { name " Preloader", NULL, name " Aux 4" }, \ + { name " Preloader", NULL, name " Aux 5" }, \ + { name " Preloader", NULL, name " Aux 6" }, \ ARIZONA_MIXER_INPUT_ROUTES(name " Aux 1"), \ ARIZONA_MIXER_INPUT_ROUTES(name " Aux 2"), \ ARIZONA_MIXER_INPUT_ROUTES(name " Aux 3"), \ ARIZONA_MIXER_INPUT_ROUTES(name " Aux 4"), \ ARIZONA_MIXER_INPUT_ROUTES(name " Aux 5"), \ ARIZONA_MIXER_INPUT_ROUTES(name " Aux 6"), \ - ARIZONA_MIXER_ROUTES(name, name "L"), \ - ARIZONA_MIXER_ROUTES(name, name "R") + ARIZONA_MIXER_ROUTES(name " Preloader", name "L"), \ + ARIZONA_MIXER_ROUTES(name " Preloader", name "R")
#define ARIZONA_RATE_ENUM_SIZE 4 extern const char *arizona_rate_text[ARIZONA_RATE_ENUM_SIZE]; diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 41bbac9..41669ad 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -1596,6 +1596,27 @@ err: ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0); }
+int wm_adsp2_early_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); + struct wm_adsp *dsp = &dsps[w->shift]; + + dsp->card = codec->card; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + queue_work(system_unbound_wq, &dsp->boot_work); + break; + default: + break; + }; + + return 0; +} +EXPORT_SYMBOL_GPL(wm_adsp2_early_event); + int wm_adsp2_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -1606,11 +1627,8 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, struct wm_coeff_ctl *ctl; int ret;
- dsp->card = codec->card; - switch (event) { case SND_SOC_DAPM_POST_PMU: - queue_work(system_unbound_wq, &dsp->boot_work); flush_work(&dsp->boot_work);
if (!dsp->running) diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index b172c1d..a4f6b64 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -68,8 +68,12 @@ struct wm_adsp { wm_adsp1_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)
#define WM_ADSP2(wname, num) \ - SND_SOC_DAPM_PGA_E(wname, SND_SOC_NOPM, num, 0, NULL, 0, \ - wm_adsp2_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD) +{ .id = snd_soc_dapm_dai_link, .name = wname " Preloader", \ + .reg = SND_SOC_NOPM, .shift = num, .event = wm_adsp2_early_event, \ + .event_flags = SND_SOC_DAPM_PRE_PMU }, \ +{ .id = snd_soc_dapm_out_drv, .name = wname, \ + .reg = SND_SOC_NOPM, .shift = num, .event = wm_adsp2_event, \ + .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD }
extern const struct snd_kcontrol_new wm_adsp1_fw_controls[]; extern const struct snd_kcontrol_new wm_adsp2_fw_controls[]; @@ -78,6 +82,8 @@ int wm_adsp1_init(struct wm_adsp *adsp); int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs); int wm_adsp1_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); +int wm_adsp2_early_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); int wm_adsp2_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event);
On Wed, Jan 08, 2014 at 05:42:18PM +0000, Charles Keepax wrote:
Move the ADSP2 boot proceedure into a work structure in preparation for running it asynchronously with the reset of the audio path bring up.
Applied both, thanks. Might be worth considering pushing the RAM enable into the context of the initiating thread so that the time taken to poll is eaten up in the scheduling delay, or at least you end up with bigger chains of I/O.
participants (2)
-
Charles Keepax
-
Mark Brown