diff options
Diffstat (limited to 'sound/soc/soc-dapm.c')
-rw-r--r-- | sound/soc/soc-dapm.c | 252 |
1 files changed, 180 insertions, 72 deletions
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index c17c14c..dc8ff13 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -59,31 +59,31 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, /* dapm power sequences - make this per codec in the future */ static int dapm_up_seq[] = { [snd_soc_dapm_pre] = 0, - [snd_soc_dapm_supply] = 1, [snd_soc_dapm_regulator_supply] = 1, [snd_soc_dapm_clock_supply] = 1, - [snd_soc_dapm_micbias] = 2, + [snd_soc_dapm_supply] = 2, + [snd_soc_dapm_micbias] = 3, [snd_soc_dapm_dai_link] = 2, - [snd_soc_dapm_dai_in] = 3, - [snd_soc_dapm_dai_out] = 3, - [snd_soc_dapm_aif_in] = 3, - [snd_soc_dapm_aif_out] = 3, - [snd_soc_dapm_mic] = 4, - [snd_soc_dapm_mux] = 5, - [snd_soc_dapm_virt_mux] = 5, - [snd_soc_dapm_value_mux] = 5, - [snd_soc_dapm_dac] = 6, - [snd_soc_dapm_switch] = 7, - [snd_soc_dapm_mixer] = 7, - [snd_soc_dapm_mixer_named_ctl] = 7, - [snd_soc_dapm_pga] = 8, - [snd_soc_dapm_adc] = 9, - [snd_soc_dapm_out_drv] = 10, - [snd_soc_dapm_hp] = 10, - [snd_soc_dapm_spk] = 10, - [snd_soc_dapm_line] = 10, - [snd_soc_dapm_kcontrol] = 11, - [snd_soc_dapm_post] = 12, + [snd_soc_dapm_dai_in] = 4, + [snd_soc_dapm_dai_out] = 4, + [snd_soc_dapm_aif_in] = 4, + [snd_soc_dapm_aif_out] = 4, + [snd_soc_dapm_mic] = 5, + [snd_soc_dapm_mux] = 6, + [snd_soc_dapm_virt_mux] = 6, + [snd_soc_dapm_value_mux] = 6, + [snd_soc_dapm_dac] = 7, + [snd_soc_dapm_switch] = 8, + [snd_soc_dapm_mixer] = 8, + [snd_soc_dapm_mixer_named_ctl] = 8, + [snd_soc_dapm_pga] = 9, + [snd_soc_dapm_adc] = 10, + [snd_soc_dapm_out_drv] = 11, + [snd_soc_dapm_hp] = 11, + [snd_soc_dapm_spk] = 11, + [snd_soc_dapm_line] = 11, + [snd_soc_dapm_kcontrol] = 12, + [snd_soc_dapm_post] = 13, }; static int dapm_down_seq[] = { @@ -109,10 +109,10 @@ static int dapm_down_seq[] = { [snd_soc_dapm_dai_in] = 10, [snd_soc_dapm_dai_out] = 10, [snd_soc_dapm_dai_link] = 11, - [snd_soc_dapm_clock_supply] = 12, - [snd_soc_dapm_regulator_supply] = 12, [snd_soc_dapm_supply] = 12, - [snd_soc_dapm_post] = 13, + [snd_soc_dapm_clock_supply] = 13, + [snd_soc_dapm_regulator_supply] = 13, + [snd_soc_dapm_post] = 14, }; static void pop_wait(u32 pop_time) @@ -371,12 +371,16 @@ static void dapm_reset(struct snd_soc_card *card) } } -static int soc_widget_read(struct snd_soc_dapm_widget *w, int reg) +static int soc_widget_read(struct snd_soc_dapm_widget *w, int reg, + unsigned int *value) { - if (w->codec) - return snd_soc_read(w->codec, reg); - else if (w->platform) - return snd_soc_platform_read(w->platform, reg); + if (w->codec) { + *value = snd_soc_read(w->codec, reg); + return 0; + } else if (w->platform) { + *value = snd_soc_platform_read(w->platform, reg); + return 0; + } dev_err(w->dapm->dev, "ASoC: no valid widget read method\n"); return -1; @@ -409,6 +413,12 @@ static inline void soc_widget_unlock(struct snd_soc_dapm_widget *w) mutex_unlock(&w->platform->mutex); } +static void soc_dapm_async_complete(struct snd_soc_dapm_context *dapm) +{ + if (dapm->codec && dapm->codec->using_regmap) + regmap_async_complete(dapm->codec->control_data); +} + static int soc_widget_update_bits_locked(struct snd_soc_dapm_widget *w, unsigned short reg, unsigned int mask, unsigned int value) { @@ -417,19 +427,19 @@ static int soc_widget_update_bits_locked(struct snd_soc_dapm_widget *w, int ret; if (w->codec && w->codec->using_regmap) { - ret = regmap_update_bits_check(w->codec->control_data, - reg, mask, value, &change); + ret = regmap_update_bits_check_async(w->codec->control_data, + reg, mask, value, + &change); if (ret != 0) return ret; } else { soc_widget_lock(w); - ret = soc_widget_read(w, reg); + ret = soc_widget_read(w, reg, &old); if (ret < 0) { soc_widget_unlock(w); return ret; } - old = ret; new = (old & ~mask) | (value & mask); change = old != new; if (change) { @@ -499,18 +509,22 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, int val; struct soc_mixer_control *mc = (struct soc_mixer_control *) w->kcontrol_news[i].private_value; - unsigned int reg = mc->reg; + int reg = mc->reg; unsigned int shift = mc->shift; int max = mc->max; unsigned int mask = (1 << fls(max)) - 1; unsigned int invert = mc->invert; - val = soc_widget_read(w, reg); - val = (val >> shift) & mask; - if (invert) - val = max - val; + if (reg != SND_SOC_NOPM) { + soc_widget_read(w, reg, &val); + val = (val >> shift) & mask; + if (invert) + val = max - val; + p->connect = !!val; + } else { + p->connect = 0; + } - p->connect = !!val; } break; case snd_soc_dapm_mux: { @@ -518,7 +532,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, w->kcontrol_news[i].private_value; int val, item; - val = soc_widget_read(w, e->reg); + soc_widget_read(w, e->reg, &val); item = (val >> e->shift_l) & e->mask; if (item < e->max && !strcmp(p->name, e->texts[item])) @@ -547,7 +561,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, w->kcontrol_news[i].private_value; int val, item; - val = soc_widget_read(w, e->reg); + soc_widget_read(w, e->reg, &val); val = (val >> e->shift_l) & e->mask; for (item = 0; item < e->max; item++) { if (val == e->values[item]) @@ -1197,6 +1211,8 @@ int dapm_regulator_event(struct snd_soc_dapm_widget *w, { int ret; + soc_dapm_async_complete(w->dapm); + if (SND_SOC_DAPM_EVENT_ON(event)) { if (w->on_val & SND_SOC_DAPM_REGULATOR_BYPASS) { ret = regulator_allow_bypass(w->regulator, false); @@ -1230,6 +1246,8 @@ int dapm_clock_event(struct snd_soc_dapm_widget *w, if (!w->clk) return -EIO; + soc_dapm_async_complete(w->dapm); + #ifdef CONFIG_HAVE_CLK if (SND_SOC_DAPM_EVENT_ON(event)) { return clk_prepare_enable(w->clk); @@ -1412,7 +1430,7 @@ static void dapm_seq_check_event(struct snd_soc_card *card, power = 0; break; default: - BUG(); + WARN(1, "Unknown event %d\n", event); return; } @@ -1422,6 +1440,7 @@ static void dapm_seq_check_event(struct snd_soc_card *card, if (w->event && (w->event_flags & event)) { pop_dbg(w->dapm->dev, card->pop_time, "pop test : %s %s\n", w->name, ev_name); + soc_dapm_async_complete(w->dapm); trace_snd_soc_dapm_widget_event_start(w, event); ret = w->event(w, NULL, event); trace_snd_soc_dapm_widget_event_done(w, event); @@ -1444,7 +1463,7 @@ static void dapm_seq_run_coalesced(struct snd_soc_card *card, power_list)->reg; list_for_each_entry(w, pending, power_list) { - BUG_ON(reg != w->reg); + WARN_ON(reg != w->reg); w->power = w->new_power; mask |= w->mask << w->shift; @@ -1494,6 +1513,7 @@ static void dapm_seq_run(struct snd_soc_card *card, struct list_head *list, int event, bool power_up) { struct snd_soc_dapm_widget *w, *n; + struct snd_soc_dapm_context *d; LIST_HEAD(pending); int cur_sort = -1; int cur_subseq = -1; @@ -1524,6 +1544,9 @@ static void dapm_seq_run(struct snd_soc_card *card, cur_subseq); } + if (cur_dapm && w->dapm != cur_dapm) + soc_dapm_async_complete(cur_dapm); + INIT_LIST_HEAD(&pending); cur_sort = -1; cur_subseq = INT_MIN; @@ -1582,6 +1605,10 @@ static void dapm_seq_run(struct snd_soc_card *card, cur_dapm->seq_notifier(cur_dapm, i, cur_subseq); } + + list_for_each_entry(d, &card->dapm_list, list) { + soc_dapm_async_complete(d); + } } static void dapm_widget_update(struct snd_soc_card *card) @@ -1840,6 +1867,7 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event) */ switch (w->id) { case snd_soc_dapm_siggen: + case snd_soc_dapm_vmid: break; case snd_soc_dapm_supply: case snd_soc_dapm_regulator_supply: @@ -1949,7 +1977,7 @@ static ssize_t dapm_widget_power_read_file(struct file *file, w->active ? "active" : "inactive"); list_for_each_entry(p, &w->sources, list_sink) { - if (p->connected && !p->connected(w, p->sink)) + if (p->connected && !p->connected(w, p->source)) continue; if (p->connect) @@ -2001,7 +2029,7 @@ static ssize_t dapm_bias_read_file(struct file *file, char __user *user_buf, level = "Off\n"; break; default: - BUG(); + WARN(1, "Unknown bias_level %d\n", dapm->bias_level); level = "Unknown\n"; break; } @@ -2448,7 +2476,8 @@ err: } static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, - const struct snd_soc_dapm_route *route) + const struct snd_soc_dapm_route *route, + unsigned int is_prefixed) { struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w; struct snd_soc_dapm_widget *wtsource = NULL, *wtsink = NULL; @@ -2458,7 +2487,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, char prefixed_source[80]; int ret; - if (dapm->codec && dapm->codec->name_prefix) { + if (dapm->codec && dapm->codec->name_prefix && !is_prefixed) { snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s", dapm->codec->name_prefix, route->sink); sink = prefixed_sink; @@ -2586,7 +2615,7 @@ int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm, mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); for (i = 0; i < num; i++) { - r = snd_soc_dapm_add_route(dapm, route); + r = snd_soc_dapm_add_route(dapm, route, false); if (r < 0) { dev_err(dapm->dev, "ASoC: Failed to add route %s -> %s -> %s\n", route->source, @@ -2757,7 +2786,8 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card) /* Read the initial power state from the device */ if (w->reg >= 0) { - val = soc_widget_read(w, w->reg) >> w->shift; + soc_widget_read(w, w->reg, &val); + val = val >> w->shift; val &= w->mask; if (val == w->on_val) w->power = 1; @@ -2791,7 +2821,7 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, struct snd_soc_card *card = codec->card; struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - unsigned int reg = mc->reg; + int reg = mc->reg; unsigned int shift = mc->shift; int max = mc->max; unsigned int mask = (1 << fls(max)) - 1; @@ -2804,7 +2834,7 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, kcontrol->id.name); mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - if (dapm_kcontrol_is_powered(kcontrol)) + if (dapm_kcontrol_is_powered(kcontrol) && reg != SND_SOC_NOPM) val = (snd_soc_read(codec, reg) >> shift) & mask; else val = dapm_kcontrol_get_value(kcontrol); @@ -2835,7 +2865,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, struct snd_soc_card *card = codec->card; struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - unsigned int reg = mc->reg; + int reg = mc->reg; unsigned int shift = mc->shift; int max = mc->max; unsigned int mask = (1 << fls(max)) - 1; @@ -2843,6 +2873,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, unsigned int val; int connect, change; struct snd_soc_dapm_update update; + int ret = 0; if (snd_soc_volsw_is_stereo(mc)) dev_warn(codec->dapm.dev, @@ -2857,26 +2888,35 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - dapm_kcontrol_set_value(kcontrol, val); + change = dapm_kcontrol_set_value(kcontrol, val); + + if (reg != SND_SOC_NOPM) { + mask = mask << shift; + val = val << shift; - mask = mask << shift; - val = val << shift; + change = snd_soc_test_bits(codec, reg, mask, val); + } - change = snd_soc_test_bits(codec, reg, mask, val); if (change) { - update.kcontrol = kcontrol; - update.reg = reg; - update.mask = mask; - update.val = val; + if (reg != SND_SOC_NOPM) { + update.kcontrol = kcontrol; + update.reg = reg; + update.mask = mask; + update.val = val; - card->update = &update; + card->update = &update; + } - soc_dapm_mixer_update_power(card, kcontrol, connect); + ret = soc_dapm_mixer_update_power(card, kcontrol, connect); card->update = NULL; } mutex_unlock(&card->dapm_mutex); + + if (ret > 0) + soc_dpcm_runtime_update(card); + return change; } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw); @@ -2925,6 +2965,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, unsigned int val, mux, change; unsigned int mask; struct snd_soc_dapm_update update; + int ret = 0; if (ucontrol->value.enumerated.item[0] > e->max - 1) return -EINVAL; @@ -2948,12 +2989,16 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, update.val = val; card->update = &update; - soc_dapm_mux_update_power(card, kcontrol, mux, e); + ret = soc_dapm_mux_update_power(card, kcontrol, mux, e); card->update = NULL; } mutex_unlock(&card->dapm_mutex); + + if (ret > 0) + soc_dpcm_runtime_update(card); + return change; } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double); @@ -2989,6 +3034,7 @@ int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; int change; + int ret = 0; if (ucontrol->value.enumerated.item[0] >= e->max) return -EINVAL; @@ -2998,9 +3044,13 @@ int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, value = ucontrol->value.enumerated.item[0]; change = dapm_kcontrol_set_value(kcontrol, value); if (change) - soc_dapm_mux_update_power(card, kcontrol, value, e); + ret = soc_dapm_mux_update_power(card, kcontrol, value, e); mutex_unlock(&card->dapm_mutex); + + if (ret > 0) + soc_dpcm_runtime_update(card); + return change; } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_virt); @@ -3067,6 +3117,7 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, unsigned int val, mux, change; unsigned int mask; struct snd_soc_dapm_update update; + int ret = 0; if (ucontrol->value.enumerated.item[0] > e->max - 1) return -EINVAL; @@ -3090,12 +3141,16 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, update.val = val; card->update = &update; - soc_dapm_mux_update_power(card, kcontrol, mux, e); + ret = soc_dapm_mux_update_power(card, kcontrol, mux, e); card->update = NULL; } mutex_unlock(&card->dapm_mutex); + + if (ret > 0) + soc_dpcm_runtime_update(card); + return change; } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_value_enum_double); @@ -3329,8 +3384,9 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, u64 fmt; int ret; - BUG_ON(!config); - BUG_ON(list_empty(&w->sources) || list_empty(&w->sinks)); + if (WARN_ON(!config) || + WARN_ON(list_empty(&w->sources) || list_empty(&w->sinks))) + return -EINVAL; /* We only support a single source and sink, pick the first */ source_p = list_first_entry(&w->sources, struct snd_soc_dapm_path, @@ -3338,9 +3394,10 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, sink_p = list_first_entry(&w->sinks, struct snd_soc_dapm_path, list_source); - BUG_ON(!source_p || !sink_p); - BUG_ON(!sink_p->source || !source_p->sink); - BUG_ON(!source_p->source || !sink_p->sink); + if (WARN_ON(!source_p || !sink_p) || + WARN_ON(!sink_p->source || !source_p->sink) || + WARN_ON(!source_p->source || !sink_p->sink)) + return -EINVAL; source = source_p->source->priv; sink = sink_p->sink->priv; @@ -3416,7 +3473,7 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, break; default: - BUG(); + WARN(1, "Unknown event %d\n", event); return -EINVAL; } @@ -3495,6 +3552,7 @@ int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm, if (!w) { dev_err(dapm->dev, "ASoC: Failed to create %s widget\n", dai->driver->playback.stream_name); + return -ENOMEM; } w->priv = dai; @@ -3513,6 +3571,7 @@ int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm, if (!w) { dev_err(dapm->dev, "ASoC: Failed to create %s widget\n", dai->driver->capture.stream_name); + return -ENOMEM; } w->priv = dai; @@ -3580,6 +3639,55 @@ int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card) return 0; } +void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card) +{ + struct snd_soc_pcm_runtime *rtd = card->rtd; + struct snd_soc_dai *cpu_dai, *codec_dai; + struct snd_soc_dapm_route r; + int i; + + memset(&r, 0, sizeof(r)); + + /* for each BE DAI link... */ + for (i = 0; i < card->num_rtd; i++) { + rtd = &card->rtd[i]; + cpu_dai = rtd->cpu_dai; + codec_dai = rtd->codec_dai; + + /* dynamic FE links have no fixed DAI mapping */ + if (rtd->dai_link->dynamic) + continue; + + /* there is no point in connecting BE DAI links with dummies */ + if (snd_soc_dai_is_dummy(codec_dai) || + snd_soc_dai_is_dummy(cpu_dai)) + continue; + + /* connect BE DAI playback if widgets are valid */ + if (codec_dai->playback_widget && cpu_dai->playback_widget) { + r.source = cpu_dai->playback_widget->name; + r.sink = codec_dai->playback_widget->name; + dev_dbg(rtd->dev, "connected DAI link %s:%s -> %s:%s\n", + cpu_dai->codec->name, r.source, + codec_dai->platform->name, r.sink); + + snd_soc_dapm_add_route(&card->dapm, &r, true); + } + + /* connect BE DAI capture if widgets are valid */ + if (codec_dai->capture_widget && cpu_dai->capture_widget) { + r.source = codec_dai->capture_widget->name; + r.sink = cpu_dai->capture_widget->name; + dev_dbg(rtd->dev, "connected DAI link %s:%s -> %s:%s\n", + codec_dai->codec->name, r.source, + cpu_dai->platform->name, r.sink); + + snd_soc_dapm_add_route(&card->dapm, &r, true); + } + + } +} + static void soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream, int event) { |