diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index ebed5ab1245b..98d3957c66e7 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -68,63 +68,7 @@ static const char * const cs35l56_asp1_mux_control_names[] = { "ASP1 TX1 Source", "ASP1 TX2 Source", "ASP1 TX3 Source", "ASP1 TX4 Source" }; -static int cs35l56_dspwait_asp1tx_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol); - struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - int index = e->shift_l; - unsigned int addr, val; - int ret; - - /* Wait for mux to be initialized */ - cs35l56_wait_dsp_ready(cs35l56); - flush_work(&cs35l56->mux_init_work); - - addr = cs35l56_asp1_mixer_regs[index]; - ret = regmap_read(cs35l56->base.regmap, addr, &val); - if (ret) - return ret; - - val &= CS35L56_ASP_TXn_SRC_MASK; - ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(e, val); - - return 0; -} - -static int cs35l56_dspwait_asp1tx_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol); - struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); - struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - int item = ucontrol->value.enumerated.item[0]; - int index = e->shift_l; - unsigned int addr, val; - bool changed; - int ret; - - /* Wait for mux to be initialized */ - cs35l56_wait_dsp_ready(cs35l56); - flush_work(&cs35l56->mux_init_work); - - addr = cs35l56_asp1_mixer_regs[index]; - val = snd_soc_enum_item_to_val(e, item); - - ret = regmap_update_bits_check(cs35l56->base.regmap, addr, - CS35L56_ASP_TXn_SRC_MASK, val, &changed); - if (ret) - return ret; - - if (changed) - snd_soc_dapm_mux_update_power(dapm, kcontrol, item, e, NULL); - - return changed; -} - -static void cs35l56_mark_asp1_mixer_widgets_dirty(struct cs35l56_private *cs35l56) +static int cs35l56_sync_asp1_mixer_widgets_with_firmware(struct cs35l56_private *cs35l56) { struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cs35l56->component); const char *prefix = cs35l56->component->name_prefix; @@ -135,13 +79,19 @@ static void cs35l56_mark_asp1_mixer_widgets_dirty(struct cs35l56_private *cs35l5 unsigned int val[4]; int i, item, ret; + if (cs35l56->asp1_mixer_widgets_initialized) + return 0; + /* * Resume so we can read the registers from silicon if the regmap * cache has not yet been populated. */ ret = pm_runtime_resume_and_get(cs35l56->base.dev); if (ret < 0) - return; + return ret; + + /* Wait for firmware download and reboot */ + cs35l56_wait_dsp_ready(cs35l56); ret = regmap_bulk_read(cs35l56->base.regmap, CS35L56_ASP1TX1_INPUT, val, ARRAY_SIZE(val)); @@ -151,12 +101,9 @@ static void cs35l56_mark_asp1_mixer_widgets_dirty(struct cs35l56_private *cs35l5 if (ret) { dev_err(cs35l56->base.dev, "Failed to read ASP1 mixer regs: %d\n", ret); - return; + return ret; } - snd_soc_card_mutex_lock(dapm->card); - WARN_ON(!dapm->card->instantiated); - for (i = 0; i < ARRAY_SIZE(cs35l56_asp1_mux_control_names); ++i) { name = cs35l56_asp1_mux_control_names[i]; @@ -176,16 +123,65 @@ static void cs35l56_mark_asp1_mixer_widgets_dirty(struct cs35l56_private *cs35l5 snd_soc_dapm_mux_update_power(dapm, kcontrol, item, e, NULL); } - snd_soc_card_mutex_unlock(dapm->card); + cs35l56->asp1_mixer_widgets_initialized = true; + + return 0; } -static void cs35l56_mux_init_work(struct work_struct *work) +static int cs35l56_dspwait_asp1tx_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - struct cs35l56_private *cs35l56 = container_of(work, - struct cs35l56_private, - mux_init_work); + struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol); + struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + int index = e->shift_l; + unsigned int addr, val; + int ret; - cs35l56_mark_asp1_mixer_widgets_dirty(cs35l56); + ret = cs35l56_sync_asp1_mixer_widgets_with_firmware(cs35l56); + if (ret) + return ret; + + addr = cs35l56_asp1_mixer_regs[index]; + ret = regmap_read(cs35l56->base.regmap, addr, &val); + if (ret) + return ret; + + val &= CS35L56_ASP_TXn_SRC_MASK; + ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(e, val); + + return 0; +} + +static int cs35l56_dspwait_asp1tx_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); + struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + int item = ucontrol->value.enumerated.item[0]; + int index = e->shift_l; + unsigned int addr, val; + bool changed; + int ret; + + ret = cs35l56_sync_asp1_mixer_widgets_with_firmware(cs35l56); + if (ret) + return ret; + + addr = cs35l56_asp1_mixer_regs[index]; + val = snd_soc_enum_item_to_val(e, item); + + ret = regmap_update_bits_check(cs35l56->base.regmap, addr, + CS35L56_ASP_TXn_SRC_MASK, val, &changed); + if (ret) + return ret; + + if (changed) + snd_soc_dapm_mux_update_power(dapm, kcontrol, item, e, NULL); + + return changed; } static DECLARE_TLV_DB_SCALE(vol_tlv, -10000, 25, 0); @@ -936,14 +932,6 @@ static void cs35l56_dsp_work(struct work_struct *work) else cs35l56_patch(cs35l56, firmware_missing); - - /* - * Set starting value of ASP1 mux widgets. Updating a mux takes - * the DAPM mutex. Post this to a separate job so that DAPM - * power-up can wait for dsp_work to complete without deadlocking - * on the DAPM mutex. - */ - queue_work(cs35l56->dsp_wq, &cs35l56->mux_init_work); err: pm_runtime_mark_last_busy(cs35l56->base.dev); pm_runtime_put_autosuspend(cs35l56->base.dev); @@ -989,6 +977,13 @@ static int cs35l56_component_probe(struct snd_soc_component *component) debugfs_create_bool("can_hibernate", 0444, debugfs_root, &cs35l56->base.can_hibernate); debugfs_create_bool("fw_patched", 0444, debugfs_root, &cs35l56->base.fw_patched); + /* + * The widgets for the ASP1TX mixer can't be initialized + * until the firmware has been downloaded and rebooted. + */ + regcache_drop_region(cs35l56->base.regmap, CS35L56_ASP1TX1_INPUT, CS35L56_ASP1TX4_INPUT); + cs35l56->asp1_mixer_widgets_initialized = false; + queue_work(cs35l56->dsp_wq, &cs35l56->dsp_work); return 0; @@ -999,7 +994,6 @@ static void cs35l56_component_remove(struct snd_soc_component *component) struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); cancel_work_sync(&cs35l56->dsp_work); - cancel_work_sync(&cs35l56->mux_init_work); if (cs35l56->dsp.cs_dsp.booted) wm_adsp_power_down(&cs35l56->dsp); @@ -1070,10 +1064,8 @@ int cs35l56_system_suspend(struct device *dev) dev_dbg(dev, "system_suspend\n"); - if (cs35l56->component) { + if (cs35l56->component) flush_work(&cs35l56->dsp_work); - cancel_work_sync(&cs35l56->mux_init_work); - } /* * The interrupt line is normally shared, but after we start suspending @@ -1224,7 +1216,6 @@ static int cs35l56_dsp_init(struct cs35l56_private *cs35l56) return -ENOMEM; INIT_WORK(&cs35l56->dsp_work, cs35l56_dsp_work); - INIT_WORK(&cs35l56->mux_init_work, cs35l56_mux_init_work); dsp = &cs35l56->dsp; cs35l56_init_cs_dsp(&cs35l56->base, &dsp->cs_dsp); diff --git a/sound/soc/codecs/cs35l56.h b/sound/soc/codecs/cs35l56.h index 596b141e3f96..b000e7365e40 100644 --- a/sound/soc/codecs/cs35l56.h +++ b/sound/soc/codecs/cs35l56.h @@ -34,7 +34,6 @@ struct cs35l56_private { struct wm_adsp dsp; /* must be first member */ struct cs35l56_base base; struct work_struct dsp_work; - struct work_struct mux_init_work; struct workqueue_struct *dsp_wq; struct snd_soc_component *component; struct regulator_bulk_data supplies[CS35L56_NUM_BULK_SUPPLIES]; @@ -52,6 +51,7 @@ struct cs35l56_private { u8 asp_slot_count; bool tdm_mode; bool sysclk_set; + bool asp1_mixer_widgets_initialized; u8 old_sdw_clock_scale; };