4919 lines · c
1// SPDX-License-Identifier: GPL-2.0+2//3// soc-dapm.c -- ALSA SoC Dynamic Audio Power Management4//5// Copyright 2005 Wolfson Microelectronics PLC.6// Author: Liam Girdwood <lrg@slimlogic.co.uk>7//8// Features:9// o Changes power status of internal codec blocks depending on the10// dynamic configuration of codec internal audio paths and active11// DACs/ADCs.12// o Platform power domain - can support external components i.e. amps and13// mic/headphone insertion events.14// o Automatic Mic Bias support15// o Jack insertion power event initiation - e.g. hp insertion will enable16// sinks, dacs, etc17// o Delayed power down of audio subsystem to reduce pops between a quick18// device reopen.19 20#include <linux/module.h>21#include <linux/init.h>22#include <linux/async.h>23#include <linux/cleanup.h>24#include <linux/delay.h>25#include <linux/pm.h>26#include <linux/bitops.h>27#include <linux/platform_device.h>28#include <linux/jiffies.h>29#include <linux/debugfs.h>30#include <linux/pm_runtime.h>31#include <linux/regulator/consumer.h>32#include <linux/pinctrl/consumer.h>33#include <linux/clk.h>34#include <linux/slab.h>35#include <sound/core.h>36#include <sound/pcm.h>37#include <sound/pcm_params.h>38#include <sound/soc.h>39#include <sound/initval.h>40 41#include <trace/events/asoc.h>42 43#define DAPM_UPDATE_STAT(widget, val) widget->dapm->card->dapm_stats.val++;44 45#define SND_SOC_DAPM_DIR_REVERSE(x) ((x == SND_SOC_DAPM_DIR_IN) ? \46 SND_SOC_DAPM_DIR_OUT : SND_SOC_DAPM_DIR_IN)47 48#define snd_soc_dapm_for_each_direction(dir) \49 for ((dir) = SND_SOC_DAPM_DIR_IN; (dir) <= SND_SOC_DAPM_DIR_OUT; \50 (dir)++)51 52static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,53 struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink,54 const char *control,55 int (*connected)(struct snd_soc_dapm_widget *source,56 struct snd_soc_dapm_widget *sink));57 58struct snd_soc_dapm_widget *59snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,60 const struct snd_soc_dapm_widget *widget);61 62struct snd_soc_dapm_widget *63snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm,64 const struct snd_soc_dapm_widget *widget);65 66static unsigned int soc_dapm_read(struct snd_soc_dapm_context *dapm, int reg);67 68/* dapm power sequences - make this per codec in the future */69static int dapm_up_seq[] = {70 [snd_soc_dapm_pre] = 1,71 [snd_soc_dapm_regulator_supply] = 2,72 [snd_soc_dapm_pinctrl] = 2,73 [snd_soc_dapm_clock_supply] = 2,74 [snd_soc_dapm_supply] = 3,75 [snd_soc_dapm_dai_link] = 3,76 [snd_soc_dapm_micbias] = 4,77 [snd_soc_dapm_vmid] = 4,78 [snd_soc_dapm_dai_in] = 5,79 [snd_soc_dapm_dai_out] = 5,80 [snd_soc_dapm_aif_in] = 5,81 [snd_soc_dapm_aif_out] = 5,82 [snd_soc_dapm_mic] = 6,83 [snd_soc_dapm_siggen] = 6,84 [snd_soc_dapm_input] = 6,85 [snd_soc_dapm_output] = 6,86 [snd_soc_dapm_mux] = 7,87 [snd_soc_dapm_demux] = 7,88 [snd_soc_dapm_dac] = 8,89 [snd_soc_dapm_switch] = 9,90 [snd_soc_dapm_mixer] = 9,91 [snd_soc_dapm_mixer_named_ctl] = 9,92 [snd_soc_dapm_pga] = 10,93 [snd_soc_dapm_buffer] = 10,94 [snd_soc_dapm_scheduler] = 10,95 [snd_soc_dapm_effect] = 10,96 [snd_soc_dapm_src] = 10,97 [snd_soc_dapm_asrc] = 10,98 [snd_soc_dapm_encoder] = 10,99 [snd_soc_dapm_decoder] = 10,100 [snd_soc_dapm_adc] = 11,101 [snd_soc_dapm_out_drv] = 12,102 [snd_soc_dapm_hp] = 12,103 [snd_soc_dapm_line] = 12,104 [snd_soc_dapm_sink] = 12,105 [snd_soc_dapm_spk] = 13,106 [snd_soc_dapm_kcontrol] = 14,107 [snd_soc_dapm_post] = 15,108};109 110static int dapm_down_seq[] = {111 [snd_soc_dapm_pre] = 1,112 [snd_soc_dapm_kcontrol] = 2,113 [snd_soc_dapm_adc] = 3,114 [snd_soc_dapm_spk] = 4,115 [snd_soc_dapm_hp] = 5,116 [snd_soc_dapm_line] = 5,117 [snd_soc_dapm_out_drv] = 5,118 [snd_soc_dapm_sink] = 6,119 [snd_soc_dapm_pga] = 6,120 [snd_soc_dapm_buffer] = 6,121 [snd_soc_dapm_scheduler] = 6,122 [snd_soc_dapm_effect] = 6,123 [snd_soc_dapm_src] = 6,124 [snd_soc_dapm_asrc] = 6,125 [snd_soc_dapm_encoder] = 6,126 [snd_soc_dapm_decoder] = 6,127 [snd_soc_dapm_switch] = 7,128 [snd_soc_dapm_mixer_named_ctl] = 7,129 [snd_soc_dapm_mixer] = 7,130 [snd_soc_dapm_dac] = 8,131 [snd_soc_dapm_mic] = 9,132 [snd_soc_dapm_siggen] = 9,133 [snd_soc_dapm_input] = 9,134 [snd_soc_dapm_output] = 9,135 [snd_soc_dapm_micbias] = 10,136 [snd_soc_dapm_vmid] = 10,137 [snd_soc_dapm_mux] = 11,138 [snd_soc_dapm_demux] = 11,139 [snd_soc_dapm_aif_in] = 12,140 [snd_soc_dapm_aif_out] = 12,141 [snd_soc_dapm_dai_in] = 12,142 [snd_soc_dapm_dai_out] = 12,143 [snd_soc_dapm_dai_link] = 13,144 [snd_soc_dapm_supply] = 14,145 [snd_soc_dapm_clock_supply] = 15,146 [snd_soc_dapm_pinctrl] = 15,147 [snd_soc_dapm_regulator_supply] = 15,148 [snd_soc_dapm_post] = 16,149};150 151static void dapm_assert_locked(struct snd_soc_dapm_context *dapm)152{153 if (snd_soc_card_is_instantiated(dapm->card))154 snd_soc_dapm_mutex_assert_held(dapm);155}156 157static void pop_wait(u32 pop_time)158{159 if (pop_time)160 schedule_timeout_uninterruptible(msecs_to_jiffies(pop_time));161}162 163__printf(3, 4)164static void pop_dbg(struct device *dev, u32 pop_time, const char *fmt, ...)165{166 va_list args;167 char *buf;168 169 if (!pop_time)170 return;171 172 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);173 if (buf == NULL)174 return;175 176 va_start(args, fmt);177 vsnprintf(buf, PAGE_SIZE, fmt, args);178 dev_info(dev, "%s", buf);179 va_end(args);180 181 kfree(buf);182}183 184static bool dapm_dirty_widget(struct snd_soc_dapm_widget *w)185{186 return !list_empty(&w->dirty);187}188 189static void dapm_mark_dirty(struct snd_soc_dapm_widget *w, const char *reason)190{191 dapm_assert_locked(w->dapm);192 193 if (!dapm_dirty_widget(w)) {194 dev_vdbg(w->dapm->dev, "Marking %s dirty due to %s\n",195 w->name, reason);196 list_add_tail(&w->dirty, &w->dapm->card->dapm_dirty);197 }198}199 200/*201 * Common implementation for dapm_widget_invalidate_input_paths() and202 * dapm_widget_invalidate_output_paths(). The function is inlined since the203 * combined size of the two specialized functions is only marginally larger then204 * the size of the generic function and at the same time the fast path of the205 * specialized functions is significantly smaller than the generic function.206 */207static __always_inline void dapm_widget_invalidate_paths(208 struct snd_soc_dapm_widget *w, enum snd_soc_dapm_direction dir)209{210 enum snd_soc_dapm_direction rdir = SND_SOC_DAPM_DIR_REVERSE(dir);211 struct snd_soc_dapm_widget *node;212 struct snd_soc_dapm_path *p;213 LIST_HEAD(list);214 215 dapm_assert_locked(w->dapm);216 217 if (w->endpoints[dir] == -1)218 return;219 220 list_add_tail(&w->work_list, &list);221 w->endpoints[dir] = -1;222 223 list_for_each_entry(w, &list, work_list) {224 snd_soc_dapm_widget_for_each_path(w, dir, p) {225 if (p->is_supply || p->weak || !p->connect)226 continue;227 node = p->node[rdir];228 if (node->endpoints[dir] != -1) {229 node->endpoints[dir] = -1;230 list_add_tail(&node->work_list, &list);231 }232 }233 }234}235 236/*237 * dapm_widget_invalidate_input_paths() - Invalidate the cached number of238 * input paths239 * @w: The widget for which to invalidate the cached number of input paths240 *241 * Resets the cached number of inputs for the specified widget and all widgets242 * that can be reached via outcoming paths from the widget.243 *244 * This function must be called if the number of output paths for a widget might245 * have changed. E.g. if the source state of a widget changes or a path is added246 * or activated with the widget as the sink.247 */248static void dapm_widget_invalidate_input_paths(struct snd_soc_dapm_widget *w)249{250 dapm_widget_invalidate_paths(w, SND_SOC_DAPM_DIR_IN);251}252 253/*254 * dapm_widget_invalidate_output_paths() - Invalidate the cached number of255 * output paths256 * @w: The widget for which to invalidate the cached number of output paths257 *258 * Resets the cached number of outputs for the specified widget and all widgets259 * that can be reached via incoming paths from the widget.260 *261 * This function must be called if the number of output paths for a widget might262 * have changed. E.g. if the sink state of a widget changes or a path is added263 * or activated with the widget as the source.264 */265static void dapm_widget_invalidate_output_paths(struct snd_soc_dapm_widget *w)266{267 dapm_widget_invalidate_paths(w, SND_SOC_DAPM_DIR_OUT);268}269 270/*271 * dapm_path_invalidate() - Invalidates the cached number of inputs and outputs272 * for the widgets connected to a path273 * @p: The path to invalidate274 *275 * Resets the cached number of inputs for the sink of the path and the cached276 * number of outputs for the source of the path.277 *278 * This function must be called when a path is added, removed or the connected279 * state changes.280 */281static void dapm_path_invalidate(struct snd_soc_dapm_path *p)282{283 /*284 * Weak paths or supply paths do not influence the number of input or285 * output paths of their neighbors.286 */287 if (p->weak || p->is_supply)288 return;289 290 /*291 * The number of connected endpoints is the sum of the number of292 * connected endpoints of all neighbors. If a node with 0 connected293 * endpoints is either connected or disconnected that sum won't change,294 * so there is no need to re-check the path.295 */296 if (p->source->endpoints[SND_SOC_DAPM_DIR_IN] != 0)297 dapm_widget_invalidate_input_paths(p->sink);298 if (p->sink->endpoints[SND_SOC_DAPM_DIR_OUT] != 0)299 dapm_widget_invalidate_output_paths(p->source);300}301 302void dapm_mark_endpoints_dirty(struct snd_soc_card *card)303{304 struct snd_soc_dapm_widget *w;305 306 snd_soc_dapm_mutex_lock_root(card);307 308 for_each_card_widgets(card, w) {309 if (w->is_ep) {310 dapm_mark_dirty(w, "Rechecking endpoints");311 if (w->is_ep & SND_SOC_DAPM_EP_SINK)312 dapm_widget_invalidate_output_paths(w);313 if (w->is_ep & SND_SOC_DAPM_EP_SOURCE)314 dapm_widget_invalidate_input_paths(w);315 }316 }317 318 snd_soc_dapm_mutex_unlock(card);319}320EXPORT_SYMBOL_GPL(dapm_mark_endpoints_dirty);321 322/* create a new dapm widget */323static inline struct snd_soc_dapm_widget *dapm_cnew_widget(324 const struct snd_soc_dapm_widget *_widget,325 const char *prefix)326{327 struct snd_soc_dapm_widget *w __free(kfree) = kmemdup(_widget,328 sizeof(*_widget),329 GFP_KERNEL);330 if (!w)331 return NULL;332 333 if (prefix)334 w->name = kasprintf(GFP_KERNEL, "%s %s", prefix, _widget->name);335 else336 w->name = kstrdup_const(_widget->name, GFP_KERNEL);337 if (!w->name)338 return NULL;339 340 if (_widget->sname) {341 w->sname = kstrdup_const(_widget->sname, GFP_KERNEL);342 if (!w->sname) {343 kfree_const(w->name);344 return NULL;345 }346 }347 348 return_ptr(w);349}350 351struct dapm_kcontrol_data {352 unsigned int value;353 struct snd_soc_dapm_widget *widget;354 struct list_head paths;355 struct snd_soc_dapm_widget_list *wlist;356};357 358static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget,359 struct snd_kcontrol *kcontrol, const char *ctrl_name)360{361 struct dapm_kcontrol_data *data;362 struct soc_mixer_control *mc;363 struct soc_enum *e;364 const char *name;365 int ret;366 367 data = kzalloc(sizeof(*data), GFP_KERNEL);368 if (!data)369 return -ENOMEM;370 371 INIT_LIST_HEAD(&data->paths);372 373 switch (widget->id) {374 case snd_soc_dapm_switch:375 case snd_soc_dapm_mixer:376 case snd_soc_dapm_mixer_named_ctl:377 mc = (struct soc_mixer_control *)kcontrol->private_value;378 379 if (mc->autodisable) {380 struct snd_soc_dapm_widget template;381 382 if (snd_soc_volsw_is_stereo(mc))383 dev_warn(widget->dapm->dev,384 "ASoC: Unsupported stereo autodisable control '%s'\n",385 ctrl_name);386 387 name = kasprintf(GFP_KERNEL, "%s %s", ctrl_name,388 "Autodisable");389 if (!name) {390 ret = -ENOMEM;391 goto err_data;392 }393 394 memset(&template, 0, sizeof(template));395 template.reg = mc->reg;396 template.mask = (1 << fls(mc->max)) - 1;397 template.shift = mc->shift;398 if (mc->invert)399 template.off_val = mc->max;400 else401 template.off_val = 0;402 template.on_val = template.off_val;403 template.id = snd_soc_dapm_kcontrol;404 template.name = name;405 406 data->value = template.on_val;407 408 data->widget =409 snd_soc_dapm_new_control_unlocked(widget->dapm,410 &template);411 kfree(name);412 if (IS_ERR(data->widget)) {413 ret = PTR_ERR(data->widget);414 goto err_data;415 }416 }417 break;418 case snd_soc_dapm_demux:419 case snd_soc_dapm_mux:420 e = (struct soc_enum *)kcontrol->private_value;421 422 if (e->autodisable) {423 struct snd_soc_dapm_widget template;424 425 name = kasprintf(GFP_KERNEL, "%s %s", ctrl_name,426 "Autodisable");427 if (!name) {428 ret = -ENOMEM;429 goto err_data;430 }431 432 memset(&template, 0, sizeof(template));433 template.reg = e->reg;434 template.mask = e->mask;435 template.shift = e->shift_l;436 template.off_val = snd_soc_enum_item_to_val(e, 0);437 template.on_val = template.off_val;438 template.id = snd_soc_dapm_kcontrol;439 template.name = name;440 441 data->value = template.on_val;442 443 data->widget = snd_soc_dapm_new_control_unlocked(444 widget->dapm, &template);445 kfree(name);446 if (IS_ERR(data->widget)) {447 ret = PTR_ERR(data->widget);448 goto err_data;449 }450 451 snd_soc_dapm_add_path(widget->dapm, data->widget,452 widget, NULL, NULL);453 } else if (e->reg != SND_SOC_NOPM) {454 data->value = soc_dapm_read(widget->dapm, e->reg) &455 (e->mask << e->shift_l);456 }457 break;458 default:459 break;460 }461 462 kcontrol->private_data = data;463 464 return 0;465 466err_data:467 kfree(data);468 return ret;469}470 471static void dapm_kcontrol_free(struct snd_kcontrol *kctl)472{473 struct dapm_kcontrol_data *data = snd_kcontrol_chip(kctl);474 475 list_del(&data->paths);476 kfree(data->wlist);477 kfree(data);478}479 480static struct snd_soc_dapm_widget_list *dapm_kcontrol_get_wlist(481 const struct snd_kcontrol *kcontrol)482{483 struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol);484 485 return data->wlist;486}487 488static int dapm_kcontrol_add_widget(struct snd_kcontrol *kcontrol,489 struct snd_soc_dapm_widget *widget)490{491 struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol);492 struct snd_soc_dapm_widget_list *new_wlist;493 unsigned int n;494 495 if (data->wlist)496 n = data->wlist->num_widgets + 1;497 else498 n = 1;499 500 new_wlist = krealloc(data->wlist,501 struct_size(new_wlist, widgets, n),502 GFP_KERNEL);503 if (!new_wlist)504 return -ENOMEM;505 506 new_wlist->num_widgets = n;507 new_wlist->widgets[n - 1] = widget;508 509 data->wlist = new_wlist;510 511 return 0;512}513 514static void dapm_kcontrol_add_path(const struct snd_kcontrol *kcontrol,515 struct snd_soc_dapm_path *path)516{517 struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol);518 519 list_add_tail(&path->list_kcontrol, &data->paths);520}521 522static bool dapm_kcontrol_is_powered(const struct snd_kcontrol *kcontrol)523{524 struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol);525 526 if (!data->widget)527 return true;528 529 return data->widget->power;530}531 532static struct list_head *dapm_kcontrol_get_path_list(533 const struct snd_kcontrol *kcontrol)534{535 struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol);536 537 return &data->paths;538}539 540#define dapm_kcontrol_for_each_path(path, kcontrol) \541 list_for_each_entry(path, dapm_kcontrol_get_path_list(kcontrol), \542 list_kcontrol)543 544unsigned int dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol)545{546 struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol);547 548 return data->value;549}550EXPORT_SYMBOL_GPL(dapm_kcontrol_get_value);551 552static bool dapm_kcontrol_set_value(const struct snd_kcontrol *kcontrol,553 unsigned int value)554{555 struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol);556 557 if (data->value == value)558 return false;559 560 if (data->widget) {561 switch (dapm_kcontrol_get_wlist(kcontrol)->widgets[0]->id) {562 case snd_soc_dapm_switch:563 case snd_soc_dapm_mixer:564 case snd_soc_dapm_mixer_named_ctl:565 data->widget->on_val = value & data->widget->mask;566 break;567 case snd_soc_dapm_demux:568 case snd_soc_dapm_mux:569 data->widget->on_val = value >> data->widget->shift;570 break;571 default:572 data->widget->on_val = value;573 break;574 }575 }576 577 data->value = value;578 579 return true;580}581 582/**583 * snd_soc_dapm_kcontrol_widget() - Returns the widget associated to a584 * kcontrol585 * @kcontrol: The kcontrol586 */587struct snd_soc_dapm_widget *snd_soc_dapm_kcontrol_widget(588 struct snd_kcontrol *kcontrol)589{590 return dapm_kcontrol_get_wlist(kcontrol)->widgets[0];591}592EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_widget);593 594/**595 * snd_soc_dapm_kcontrol_dapm() - Returns the dapm context associated to a596 * kcontrol597 * @kcontrol: The kcontrol598 *599 * Note: This function must only be used on kcontrols that are known to have600 * been registered for a CODEC. Otherwise the behaviour is undefined.601 */602struct snd_soc_dapm_context *snd_soc_dapm_kcontrol_dapm(603 struct snd_kcontrol *kcontrol)604{605 return dapm_kcontrol_get_wlist(kcontrol)->widgets[0]->dapm;606}607EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_dapm);608 609static void dapm_reset(struct snd_soc_card *card)610{611 struct snd_soc_dapm_widget *w;612 613 snd_soc_dapm_mutex_assert_held(card);614 615 memset(&card->dapm_stats, 0, sizeof(card->dapm_stats));616 617 for_each_card_widgets(card, w) {618 w->new_power = w->power;619 w->power_checked = false;620 }621}622 623static const char *soc_dapm_prefix(struct snd_soc_dapm_context *dapm)624{625 if (!dapm->component)626 return NULL;627 return dapm->component->name_prefix;628}629 630static unsigned int soc_dapm_read(struct snd_soc_dapm_context *dapm, int reg)631{632 if (!dapm->component)633 return -EIO;634 return snd_soc_component_read(dapm->component, reg);635}636 637static int soc_dapm_update_bits(struct snd_soc_dapm_context *dapm,638 int reg, unsigned int mask, unsigned int value)639{640 if (!dapm->component)641 return -EIO;642 return snd_soc_component_update_bits(dapm->component, reg,643 mask, value);644}645 646static int soc_dapm_test_bits(struct snd_soc_dapm_context *dapm,647 int reg, unsigned int mask, unsigned int value)648{649 if (!dapm->component)650 return -EIO;651 return snd_soc_component_test_bits(dapm->component, reg, mask, value);652}653 654static void soc_dapm_async_complete(struct snd_soc_dapm_context *dapm)655{656 if (dapm->component)657 snd_soc_component_async_complete(dapm->component);658}659 660static struct snd_soc_dapm_widget *661dapm_wcache_lookup(struct snd_soc_dapm_widget *w, const char *name)662{663 if (w) {664 struct list_head *wlist = &w->dapm->card->widgets;665 const int depth = 2;666 int i = 0;667 668 list_for_each_entry_from(w, wlist, list) {669 if (!strcmp(name, w->name))670 return w;671 672 if (++i == depth)673 break;674 }675 }676 677 return NULL;678}679 680/**681 * snd_soc_dapm_force_bias_level() - Sets the DAPM bias level682 * @dapm: The DAPM context for which to set the level683 * @level: The level to set684 *685 * Forces the DAPM bias level to a specific state. It will call the bias level686 * callback of DAPM context with the specified level. This will even happen if687 * the context is already at the same level. Furthermore it will not go through688 * the normal bias level sequencing, meaning any intermediate states between the689 * current and the target state will not be entered.690 *691 * Note that the change in bias level is only temporary and the next time692 * snd_soc_dapm_sync() is called the state will be set to the level as693 * determined by the DAPM core. The function is mainly intended to be used to694 * used during probe or resume from suspend to power up the device so695 * initialization can be done, before the DAPM core takes over.696 */697int snd_soc_dapm_force_bias_level(struct snd_soc_dapm_context *dapm,698 enum snd_soc_bias_level level)699{700 int ret = 0;701 702 if (dapm->component)703 ret = snd_soc_component_set_bias_level(dapm->component, level);704 705 if (ret == 0)706 dapm->bias_level = level;707 708 return ret;709}710EXPORT_SYMBOL_GPL(snd_soc_dapm_force_bias_level);711 712/**713 * snd_soc_dapm_set_bias_level - set the bias level for the system714 * @dapm: DAPM context715 * @level: level to configure716 *717 * Configure the bias (power) levels for the SoC audio device.718 *719 * Returns 0 for success else error.720 */721static int snd_soc_dapm_set_bias_level(struct snd_soc_dapm_context *dapm,722 enum snd_soc_bias_level level)723{724 struct snd_soc_card *card = dapm->card;725 int ret = 0;726 727 trace_snd_soc_bias_level_start(dapm, level);728 729 ret = snd_soc_card_set_bias_level(card, dapm, level);730 if (ret != 0)731 goto out;732 733 if (!card || dapm != &card->dapm)734 ret = snd_soc_dapm_force_bias_level(dapm, level);735 736 if (ret != 0)737 goto out;738 739 ret = snd_soc_card_set_bias_level_post(card, dapm, level);740out:741 trace_snd_soc_bias_level_done(dapm, level);742 743 return ret;744}745 746/* connect mux widget to its interconnecting audio paths */747static int dapm_connect_mux(struct snd_soc_dapm_context *dapm,748 struct snd_soc_dapm_path *path, const char *control_name,749 struct snd_soc_dapm_widget *w)750{751 const struct snd_kcontrol_new *kcontrol = &w->kcontrol_news[0];752 struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;753 unsigned int item;754 int i;755 756 if (e->reg != SND_SOC_NOPM) {757 unsigned int val;758 val = soc_dapm_read(dapm, e->reg);759 val = (val >> e->shift_l) & e->mask;760 item = snd_soc_enum_val_to_item(e, val);761 } else {762 /* since a virtual mux has no backing registers to763 * decide which path to connect, it will try to match764 * with the first enumeration. This is to ensure765 * that the default mux choice (the first) will be766 * correctly powered up during initialization.767 */768 item = 0;769 }770 771 i = match_string(e->texts, e->items, control_name);772 if (i < 0)773 return -ENODEV;774 775 path->name = e->texts[i];776 path->connect = (i == item);777 return 0;778 779}780 781/* set up initial codec paths */782static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i,783 int nth_path)784{785 struct soc_mixer_control *mc = (struct soc_mixer_control *)786 p->sink->kcontrol_news[i].private_value;787 unsigned int reg = mc->reg;788 unsigned int invert = mc->invert;789 790 if (reg != SND_SOC_NOPM) {791 unsigned int shift = mc->shift;792 unsigned int max = mc->max;793 unsigned int mask = (1 << fls(max)) - 1;794 unsigned int val = soc_dapm_read(p->sink->dapm, reg);795 796 /*797 * The nth_path argument allows this function to know798 * which path of a kcontrol it is setting the initial799 * status for. Ideally this would support any number800 * of paths and channels. But since kcontrols only come801 * in mono and stereo variants, we are limited to 2802 * channels.803 *804 * The following code assumes for stereo controls the805 * first path is the left channel, and all remaining806 * paths are the right channel.807 */808 if (snd_soc_volsw_is_stereo(mc) && nth_path > 0) {809 if (reg != mc->rreg)810 val = soc_dapm_read(p->sink->dapm, mc->rreg);811 val = (val >> mc->rshift) & mask;812 } else {813 val = (val >> shift) & mask;814 }815 if (invert)816 val = max - val;817 p->connect = !!val;818 } else {819 /* since a virtual mixer has no backing registers to820 * decide which path to connect, it will try to match821 * with initial state. This is to ensure822 * that the default mixer choice will be823 * correctly powered up during initialization.824 */825 p->connect = invert;826 }827}828 829/* connect mixer widget to its interconnecting audio paths */830static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm,831 struct snd_soc_dapm_path *path, const char *control_name)832{833 int i, nth_path = 0;834 835 /* search for mixer kcontrol */836 for (i = 0; i < path->sink->num_kcontrols; i++) {837 if (!strcmp(control_name, path->sink->kcontrol_news[i].name)) {838 path->name = path->sink->kcontrol_news[i].name;839 dapm_set_mixer_path_status(path, i, nth_path++);840 return 0;841 }842 }843 return -ENODEV;844}845 846static int dapm_is_shared_kcontrol(struct snd_soc_dapm_context *dapm,847 struct snd_soc_dapm_widget *kcontrolw,848 const struct snd_kcontrol_new *kcontrol_new,849 struct snd_kcontrol **kcontrol)850{851 struct snd_soc_dapm_widget *w;852 int i;853 854 *kcontrol = NULL;855 856 for_each_card_widgets(dapm->card, w) {857 if (w == kcontrolw || w->dapm != kcontrolw->dapm)858 continue;859 for (i = 0; i < w->num_kcontrols; i++) {860 if (&w->kcontrol_news[i] == kcontrol_new) {861 if (w->kcontrols)862 *kcontrol = w->kcontrols[i];863 return 1;864 }865 }866 }867 868 return 0;869}870 871/*872 * Determine if a kcontrol is shared. If it is, look it up. If it isn't,873 * create it. Either way, add the widget into the control's widget list874 */875static int dapm_create_or_share_kcontrol(struct snd_soc_dapm_widget *w,876 int kci)877{878 struct snd_soc_dapm_context *dapm = w->dapm;879 struct snd_card *card = dapm->card->snd_card;880 const char *prefix;881 size_t prefix_len;882 int shared;883 struct snd_kcontrol *kcontrol;884 bool wname_in_long_name, kcname_in_long_name;885 char *long_name = NULL;886 const char *name;887 int ret = 0;888 889 prefix = soc_dapm_prefix(dapm);890 if (prefix)891 prefix_len = strlen(prefix) + 1;892 else893 prefix_len = 0;894 895 shared = dapm_is_shared_kcontrol(dapm, w, &w->kcontrol_news[kci],896 &kcontrol);897 898 if (!kcontrol) {899 if (shared) {900 wname_in_long_name = false;901 kcname_in_long_name = true;902 } else {903 switch (w->id) {904 case snd_soc_dapm_switch:905 case snd_soc_dapm_mixer:906 case snd_soc_dapm_pga:907 case snd_soc_dapm_effect:908 case snd_soc_dapm_out_drv:909 wname_in_long_name = true;910 kcname_in_long_name = true;911 break;912 case snd_soc_dapm_mixer_named_ctl:913 wname_in_long_name = false;914 kcname_in_long_name = true;915 break;916 case snd_soc_dapm_demux:917 case snd_soc_dapm_mux:918 wname_in_long_name = true;919 kcname_in_long_name = false;920 break;921 default:922 return -EINVAL;923 }924 }925 if (w->no_wname_in_kcontrol_name)926 wname_in_long_name = false;927 928 if (wname_in_long_name && kcname_in_long_name) {929 /*930 * The control will get a prefix from the control931 * creation process but we're also using the same932 * prefix for widgets so cut the prefix off the933 * front of the widget name.934 */935 long_name = kasprintf(GFP_KERNEL, "%s %s",936 w->name + prefix_len,937 w->kcontrol_news[kci].name);938 if (long_name == NULL)939 return -ENOMEM;940 941 name = long_name;942 } else if (wname_in_long_name) {943 long_name = NULL;944 name = w->name + prefix_len;945 } else {946 long_name = NULL;947 name = w->kcontrol_news[kci].name;948 }949 950 kcontrol = snd_soc_cnew(&w->kcontrol_news[kci], NULL, name,951 prefix);952 if (!kcontrol) {953 ret = -ENOMEM;954 goto exit_free;955 }956 957 kcontrol->private_free = dapm_kcontrol_free;958 959 ret = dapm_kcontrol_data_alloc(w, kcontrol, name);960 if (ret) {961 snd_ctl_free_one(kcontrol);962 goto exit_free;963 }964 965 ret = snd_ctl_add(card, kcontrol);966 if (ret < 0) {967 dev_err(dapm->dev,968 "ASoC: failed to add widget %s dapm kcontrol %s: %d\n",969 w->name, name, ret);970 goto exit_free;971 }972 }973 974 ret = dapm_kcontrol_add_widget(kcontrol, w);975 if (ret == 0)976 w->kcontrols[kci] = kcontrol;977 978exit_free:979 kfree(long_name);980 981 return ret;982}983 984/* create new dapm mixer control */985static int dapm_new_mixer(struct snd_soc_dapm_widget *w)986{987 int i, ret;988 struct snd_soc_dapm_path *path;989 struct dapm_kcontrol_data *data;990 991 /* add kcontrol */992 for (i = 0; i < w->num_kcontrols; i++) {993 /* match name */994 snd_soc_dapm_widget_for_each_source_path(w, path) {995 /* mixer/mux paths name must match control name */996 if (path->name != (char *)w->kcontrol_news[i].name)997 continue;998 999 if (!w->kcontrols[i]) {1000 ret = dapm_create_or_share_kcontrol(w, i);1001 if (ret < 0)1002 return ret;1003 }1004 1005 dapm_kcontrol_add_path(w->kcontrols[i], path);1006 1007 data = snd_kcontrol_chip(w->kcontrols[i]);1008 if (data->widget)1009 snd_soc_dapm_add_path(data->widget->dapm,1010 data->widget,1011 path->source,1012 NULL, NULL);1013 }1014 }1015 1016 return 0;1017}1018 1019/* create new dapm mux control */1020static int dapm_new_mux(struct snd_soc_dapm_widget *w)1021{1022 struct snd_soc_dapm_context *dapm = w->dapm;1023 enum snd_soc_dapm_direction dir;1024 struct snd_soc_dapm_path *path;1025 const char *type;1026 int ret;1027 1028 switch (w->id) {1029 case snd_soc_dapm_mux:1030 dir = SND_SOC_DAPM_DIR_OUT;1031 type = "mux";1032 break;1033 case snd_soc_dapm_demux:1034 dir = SND_SOC_DAPM_DIR_IN;1035 type = "demux";1036 break;1037 default:1038 return -EINVAL;1039 }1040 1041 if (w->num_kcontrols != 1) {1042 dev_err(dapm->dev,1043 "ASoC: %s %s has incorrect number of controls\n", type,1044 w->name);1045 return -EINVAL;1046 }1047 1048 if (list_empty(&w->edges[dir])) {1049 dev_err(dapm->dev, "ASoC: %s %s has no paths\n", type, w->name);1050 return -EINVAL;1051 }1052 1053 ret = dapm_create_or_share_kcontrol(w, 0);1054 if (ret < 0)1055 return ret;1056 1057 snd_soc_dapm_widget_for_each_path(w, dir, path) {1058 if (path->name)1059 dapm_kcontrol_add_path(w->kcontrols[0], path);1060 }1061 1062 return 0;1063}1064 1065/* create new dapm volume control */1066static int dapm_new_pga(struct snd_soc_dapm_widget *w)1067{1068 int i;1069 1070 for (i = 0; i < w->num_kcontrols; i++) {1071 int ret = dapm_create_or_share_kcontrol(w, i);1072 if (ret < 0)1073 return ret;1074 }1075 1076 return 0;1077}1078 1079/* create new dapm dai link control */1080static int dapm_new_dai_link(struct snd_soc_dapm_widget *w)1081{1082 int i;1083 struct snd_soc_pcm_runtime *rtd = w->priv;1084 1085 /* create control for links with > 1 config */1086 if (rtd->dai_link->num_c2c_params <= 1)1087 return 0;1088 1089 /* add kcontrol */1090 for (i = 0; i < w->num_kcontrols; i++) {1091 struct snd_soc_dapm_context *dapm = w->dapm;1092 struct snd_card *card = dapm->card->snd_card;1093 struct snd_kcontrol *kcontrol = snd_soc_cnew(&w->kcontrol_news[i],1094 w, w->name, NULL);1095 int ret = snd_ctl_add(card, kcontrol);1096 1097 if (ret < 0) {1098 dev_err(dapm->dev,1099 "ASoC: failed to add widget %s dapm kcontrol %s: %d\n",1100 w->name, w->kcontrol_news[i].name, ret);1101 return ret;1102 }1103 kcontrol->private_data = w;1104 w->kcontrols[i] = kcontrol;1105 }1106 1107 return 0;1108}1109 1110/* We implement power down on suspend by checking the power state of1111 * the ALSA card - when we are suspending the ALSA state for the card1112 * is set to D3.1113 */1114static int snd_soc_dapm_suspend_check(struct snd_soc_dapm_widget *widget)1115{1116 int level = snd_power_get_state(widget->dapm->card->snd_card);1117 1118 switch (level) {1119 case SNDRV_CTL_POWER_D3hot:1120 case SNDRV_CTL_POWER_D3cold:1121 if (widget->ignore_suspend)1122 dev_dbg(widget->dapm->dev, "ASoC: %s ignoring suspend\n",1123 widget->name);1124 return widget->ignore_suspend;1125 default:1126 return 1;1127 }1128}1129 1130static void dapm_widget_list_free(struct snd_soc_dapm_widget_list **list)1131{1132 kfree(*list);1133}1134 1135static int dapm_widget_list_create(struct snd_soc_dapm_widget_list **list,1136 struct list_head *widgets)1137{1138 struct snd_soc_dapm_widget *w;1139 struct list_head *it;1140 unsigned int size = 0;1141 unsigned int i = 0;1142 1143 list_for_each(it, widgets)1144 size++;1145 1146 *list = kzalloc(struct_size(*list, widgets, size), GFP_KERNEL);1147 if (*list == NULL)1148 return -ENOMEM;1149 1150 (*list)->num_widgets = size;1151 1152 list_for_each_entry(w, widgets, work_list)1153 (*list)->widgets[i++] = w;1154 1155 (*list)->num_widgets = i;1156 1157 return 0;1158}1159 1160/*1161 * Recursively reset the cached number of inputs or outputs for the specified1162 * widget and all widgets that can be reached via incoming or outcoming paths1163 * from the widget.1164 */1165static void invalidate_paths_ep(struct snd_soc_dapm_widget *widget,1166 enum snd_soc_dapm_direction dir)1167{1168 enum snd_soc_dapm_direction rdir = SND_SOC_DAPM_DIR_REVERSE(dir);1169 struct snd_soc_dapm_path *path;1170 1171 widget->endpoints[dir] = -1;1172 1173 snd_soc_dapm_widget_for_each_path(widget, rdir, path) {1174 if (path->weak || path->is_supply)1175 continue;1176 1177 if (path->walking)1178 return;1179 1180 if (path->connect) {1181 path->walking = 1;1182 invalidate_paths_ep(path->node[dir], dir);1183 path->walking = 0;1184 }1185 }1186}1187 1188/*1189 * Common implementation for is_connected_output_ep() and1190 * is_connected_input_ep(). The function is inlined since the combined size of1191 * the two specialized functions is only marginally larger then the size of the1192 * generic function and at the same time the fast path of the specialized1193 * functions is significantly smaller than the generic function.1194 */1195static __always_inline int is_connected_ep(struct snd_soc_dapm_widget *widget,1196 struct list_head *list, enum snd_soc_dapm_direction dir,1197 int (*fn)(struct snd_soc_dapm_widget *, struct list_head *,1198 bool (*custom_stop_condition)(struct snd_soc_dapm_widget *,1199 enum snd_soc_dapm_direction)),1200 bool (*custom_stop_condition)(struct snd_soc_dapm_widget *,1201 enum snd_soc_dapm_direction))1202{1203 enum snd_soc_dapm_direction rdir = SND_SOC_DAPM_DIR_REVERSE(dir);1204 struct snd_soc_dapm_path *path;1205 int con = 0;1206 1207 if (widget->endpoints[dir] >= 0)1208 return widget->endpoints[dir];1209 1210 DAPM_UPDATE_STAT(widget, path_checks);1211 1212 /* do we need to add this widget to the list ? */1213 if (list)1214 list_add_tail(&widget->work_list, list);1215 1216 if (custom_stop_condition && custom_stop_condition(widget, dir)) {1217 list = NULL;1218 custom_stop_condition = NULL;1219 }1220 1221 if ((widget->is_ep & SND_SOC_DAPM_DIR_TO_EP(dir)) && widget->connected) {1222 widget->endpoints[dir] = snd_soc_dapm_suspend_check(widget);1223 return widget->endpoints[dir];1224 }1225 1226 snd_soc_dapm_widget_for_each_path(widget, rdir, path) {1227 DAPM_UPDATE_STAT(widget, neighbour_checks);1228 1229 if (path->weak || path->is_supply)1230 continue;1231 1232 if (path->walking)1233 return 1;1234 1235 trace_snd_soc_dapm_path(widget, dir, path);1236 1237 if (path->connect) {1238 path->walking = 1;1239 con += fn(path->node[dir], list, custom_stop_condition);1240 path->walking = 0;1241 }1242 }1243 1244 widget->endpoints[dir] = con;1245 1246 return con;1247}1248 1249/*1250 * Recursively check for a completed path to an active or physically connected1251 * output widget. Returns number of complete paths.1252 *1253 * Optionally, can be supplied with a function acting as a stopping condition.1254 * This function takes the dapm widget currently being examined and the walk1255 * direction as an arguments, it should return true if widgets from that point1256 * in the graph onwards should not be added to the widget list.1257 */1258static int is_connected_output_ep(struct snd_soc_dapm_widget *widget,1259 struct list_head *list,1260 bool (*custom_stop_condition)(struct snd_soc_dapm_widget *i,1261 enum snd_soc_dapm_direction))1262{1263 return is_connected_ep(widget, list, SND_SOC_DAPM_DIR_OUT,1264 is_connected_output_ep, custom_stop_condition);1265}1266 1267/*1268 * Recursively check for a completed path to an active or physically connected1269 * input widget. Returns number of complete paths.1270 *1271 * Optionally, can be supplied with a function acting as a stopping condition.1272 * This function takes the dapm widget currently being examined and the walk1273 * direction as an arguments, it should return true if the walk should be1274 * stopped and false otherwise.1275 */1276static int is_connected_input_ep(struct snd_soc_dapm_widget *widget,1277 struct list_head *list,1278 bool (*custom_stop_condition)(struct snd_soc_dapm_widget *i,1279 enum snd_soc_dapm_direction))1280{1281 return is_connected_ep(widget, list, SND_SOC_DAPM_DIR_IN,1282 is_connected_input_ep, custom_stop_condition);1283}1284 1285/**1286 * snd_soc_dapm_dai_get_connected_widgets - query audio path and it's widgets.1287 * @dai: the soc DAI.1288 * @stream: stream direction.1289 * @list: list of active widgets for this stream.1290 * @custom_stop_condition: (optional) a function meant to stop the widget graph1291 * walk based on custom logic.1292 *1293 * Queries DAPM graph as to whether a valid audio stream path exists for1294 * the initial stream specified by name. This takes into account1295 * current mixer and mux kcontrol settings. Creates list of valid widgets.1296 *1297 * Optionally, can be supplied with a function acting as a stopping condition.1298 * This function takes the dapm widget currently being examined and the walk1299 * direction as an arguments, it should return true if the walk should be1300 * stopped and false otherwise.1301 *1302 * Returns the number of valid paths or negative error.1303 */1304int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,1305 struct snd_soc_dapm_widget_list **list,1306 bool (*custom_stop_condition)(struct snd_soc_dapm_widget *,1307 enum snd_soc_dapm_direction))1308{1309 struct snd_soc_card *card = dai->component->card;1310 struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, stream);1311 LIST_HEAD(widgets);1312 int paths;1313 int ret;1314 1315 snd_soc_dapm_mutex_lock(card);1316 1317 if (stream == SNDRV_PCM_STREAM_PLAYBACK) {1318 invalidate_paths_ep(w, SND_SOC_DAPM_DIR_OUT);1319 paths = is_connected_output_ep(w, &widgets,1320 custom_stop_condition);1321 } else {1322 invalidate_paths_ep(w, SND_SOC_DAPM_DIR_IN);1323 paths = is_connected_input_ep(w, &widgets,1324 custom_stop_condition);1325 }1326 1327 /* Drop starting point */1328 list_del(widgets.next);1329 1330 ret = dapm_widget_list_create(list, &widgets);1331 if (ret)1332 paths = ret;1333 1334 trace_snd_soc_dapm_connected(paths, stream);1335 snd_soc_dapm_mutex_unlock(card);1336 1337 return paths;1338}1339EXPORT_SYMBOL_GPL(snd_soc_dapm_dai_get_connected_widgets);1340 1341void snd_soc_dapm_dai_free_widgets(struct snd_soc_dapm_widget_list **list)1342{1343 dapm_widget_list_free(list);1344}1345EXPORT_SYMBOL_GPL(snd_soc_dapm_dai_free_widgets);1346 1347/*1348 * Handler for regulator supply widget.1349 */1350int dapm_regulator_event(struct snd_soc_dapm_widget *w,1351 struct snd_kcontrol *kcontrol, int event)1352{1353 int ret;1354 1355 soc_dapm_async_complete(w->dapm);1356 1357 if (SND_SOC_DAPM_EVENT_ON(event)) {1358 if (w->on_val & SND_SOC_DAPM_REGULATOR_BYPASS) {1359 ret = regulator_allow_bypass(w->regulator, false);1360 if (ret != 0)1361 dev_warn(w->dapm->dev,1362 "ASoC: Failed to unbypass %s: %d\n",1363 w->name, ret);1364 }1365 1366 return regulator_enable(w->regulator);1367 } else {1368 if (w->on_val & SND_SOC_DAPM_REGULATOR_BYPASS) {1369 ret = regulator_allow_bypass(w->regulator, true);1370 if (ret != 0)1371 dev_warn(w->dapm->dev,1372 "ASoC: Failed to bypass %s: %d\n",1373 w->name, ret);1374 }1375 1376 return regulator_disable_deferred(w->regulator, w->shift);1377 }1378}1379EXPORT_SYMBOL_GPL(dapm_regulator_event);1380 1381/*1382 * Handler for pinctrl widget.1383 */1384int dapm_pinctrl_event(struct snd_soc_dapm_widget *w,1385 struct snd_kcontrol *kcontrol, int event)1386{1387 struct snd_soc_dapm_pinctrl_priv *priv = w->priv;1388 struct pinctrl *p = w->pinctrl;1389 struct pinctrl_state *s;1390 1391 if (!p || !priv)1392 return -EIO;1393 1394 if (SND_SOC_DAPM_EVENT_ON(event))1395 s = pinctrl_lookup_state(p, priv->active_state);1396 else1397 s = pinctrl_lookup_state(p, priv->sleep_state);1398 1399 if (IS_ERR(s))1400 return PTR_ERR(s);1401 1402 return pinctrl_select_state(p, s);1403}1404EXPORT_SYMBOL_GPL(dapm_pinctrl_event);1405 1406/*1407 * Handler for clock supply widget.1408 */1409int dapm_clock_event(struct snd_soc_dapm_widget *w,1410 struct snd_kcontrol *kcontrol, int event)1411{1412 if (!w->clk)1413 return -EIO;1414 1415 soc_dapm_async_complete(w->dapm);1416 1417 if (SND_SOC_DAPM_EVENT_ON(event)) {1418 return clk_prepare_enable(w->clk);1419 } else {1420 clk_disable_unprepare(w->clk);1421 return 0;1422 }1423 1424 return 0;1425}1426EXPORT_SYMBOL_GPL(dapm_clock_event);1427 1428static int dapm_widget_power_check(struct snd_soc_dapm_widget *w)1429{1430 if (w->power_checked)1431 return w->new_power;1432 1433 if (w->force)1434 w->new_power = 1;1435 else1436 w->new_power = w->power_check(w);1437 1438 w->power_checked = true;1439 1440 return w->new_power;1441}1442 1443/* Generic check to see if a widget should be powered. */1444static int dapm_generic_check_power(struct snd_soc_dapm_widget *w)1445{1446 int in, out;1447 1448 DAPM_UPDATE_STAT(w, power_checks);1449 1450 in = is_connected_input_ep(w, NULL, NULL);1451 out = is_connected_output_ep(w, NULL, NULL);1452 return out != 0 && in != 0;1453}1454 1455/* Check to see if a power supply is needed */1456static int dapm_supply_check_power(struct snd_soc_dapm_widget *w)1457{1458 struct snd_soc_dapm_path *path;1459 1460 DAPM_UPDATE_STAT(w, power_checks);1461 1462 /* Check if one of our outputs is connected */1463 snd_soc_dapm_widget_for_each_sink_path(w, path) {1464 DAPM_UPDATE_STAT(w, neighbour_checks);1465 1466 if (path->weak)1467 continue;1468 1469 if (path->connected &&1470 !path->connected(path->source, path->sink))1471 continue;1472 1473 if (dapm_widget_power_check(path->sink))1474 return 1;1475 }1476 1477 return 0;1478}1479 1480static int dapm_always_on_check_power(struct snd_soc_dapm_widget *w)1481{1482 return w->connected;1483}1484 1485static int dapm_seq_compare(struct snd_soc_dapm_widget *a,1486 struct snd_soc_dapm_widget *b,1487 bool power_up)1488{1489 int *sort;1490 1491 BUILD_BUG_ON(ARRAY_SIZE(dapm_up_seq) != SND_SOC_DAPM_TYPE_COUNT);1492 BUILD_BUG_ON(ARRAY_SIZE(dapm_down_seq) != SND_SOC_DAPM_TYPE_COUNT);1493 1494 if (power_up)1495 sort = dapm_up_seq;1496 else1497 sort = dapm_down_seq;1498 1499 WARN_ONCE(sort[a->id] == 0, "offset a->id %d not initialized\n", a->id);1500 WARN_ONCE(sort[b->id] == 0, "offset b->id %d not initialized\n", b->id);1501 1502 if (sort[a->id] != sort[b->id])1503 return sort[a->id] - sort[b->id];1504 if (a->subseq != b->subseq) {1505 if (power_up)1506 return a->subseq - b->subseq;1507 else1508 return b->subseq - a->subseq;1509 }1510 if (a->reg != b->reg)1511 return a->reg - b->reg;1512 if (a->dapm != b->dapm)1513 return (unsigned long)a->dapm - (unsigned long)b->dapm;1514 1515 return 0;1516}1517 1518/* Insert a widget in order into a DAPM power sequence. */1519static void dapm_seq_insert(struct snd_soc_dapm_widget *new_widget,1520 struct list_head *list,1521 bool power_up)1522{1523 struct snd_soc_dapm_widget *w;1524 1525 list_for_each_entry(w, list, power_list)1526 if (dapm_seq_compare(new_widget, w, power_up) < 0) {1527 list_add_tail(&new_widget->power_list, &w->power_list);1528 return;1529 }1530 1531 list_add_tail(&new_widget->power_list, list);1532}1533 1534static void dapm_seq_check_event(struct snd_soc_card *card,1535 struct snd_soc_dapm_widget *w, int event)1536{1537 const char *ev_name;1538 int power;1539 1540 switch (event) {1541 case SND_SOC_DAPM_PRE_PMU:1542 ev_name = "PRE_PMU";1543 power = 1;1544 break;1545 case SND_SOC_DAPM_POST_PMU:1546 ev_name = "POST_PMU";1547 power = 1;1548 break;1549 case SND_SOC_DAPM_PRE_PMD:1550 ev_name = "PRE_PMD";1551 power = 0;1552 break;1553 case SND_SOC_DAPM_POST_PMD:1554 ev_name = "POST_PMD";1555 power = 0;1556 break;1557 case SND_SOC_DAPM_WILL_PMU:1558 ev_name = "WILL_PMU";1559 power = 1;1560 break;1561 case SND_SOC_DAPM_WILL_PMD:1562 ev_name = "WILL_PMD";1563 power = 0;1564 break;1565 default:1566 WARN(1, "Unknown event %d\n", event);1567 return;1568 }1569 1570 if (w->new_power != power)1571 return;1572 1573 if (w->event && (w->event_flags & event)) {1574 int ret;1575 1576 pop_dbg(w->dapm->dev, card->pop_time, "pop test : %s %s\n",1577 w->name, ev_name);1578 soc_dapm_async_complete(w->dapm);1579 trace_snd_soc_dapm_widget_event_start(w, event);1580 ret = w->event(w, NULL, event);1581 trace_snd_soc_dapm_widget_event_done(w, event);1582 if (ret < 0)1583 dev_err(w->dapm->dev, "ASoC: %s: %s event failed: %d\n",1584 ev_name, w->name, ret);1585 }1586}1587 1588/* Apply the coalesced changes from a DAPM sequence */1589static void dapm_seq_run_coalesced(struct snd_soc_card *card,1590 struct list_head *pending)1591{1592 struct snd_soc_dapm_context *dapm;1593 struct snd_soc_dapm_widget *w;1594 int reg;1595 unsigned int value = 0;1596 unsigned int mask = 0;1597 1598 w = list_first_entry(pending, struct snd_soc_dapm_widget, power_list);1599 reg = w->reg;1600 dapm = w->dapm;1601 1602 list_for_each_entry(w, pending, power_list) {1603 WARN_ON(reg != w->reg || dapm != w->dapm);1604 w->power = w->new_power;1605 1606 mask |= w->mask << w->shift;1607 if (w->power)1608 value |= w->on_val << w->shift;1609 else1610 value |= w->off_val << w->shift;1611 1612 pop_dbg(dapm->dev, card->pop_time,1613 "pop test : Queue %s: reg=0x%x, 0x%x/0x%x\n",1614 w->name, reg, value, mask);1615 1616 /* Check for events */1617 dapm_seq_check_event(card, w, SND_SOC_DAPM_PRE_PMU);1618 dapm_seq_check_event(card, w, SND_SOC_DAPM_PRE_PMD);1619 }1620 1621 if (reg >= 0) {1622 /* Any widget will do, they should all be updating the1623 * same register.1624 */1625 1626 pop_dbg(dapm->dev, card->pop_time,1627 "pop test : Applying 0x%x/0x%x to %x in %dms\n",1628 value, mask, reg, card->pop_time);1629 pop_wait(card->pop_time);1630 soc_dapm_update_bits(dapm, reg, mask, value);1631 }1632 1633 list_for_each_entry(w, pending, power_list) {1634 dapm_seq_check_event(card, w, SND_SOC_DAPM_POST_PMU);1635 dapm_seq_check_event(card, w, SND_SOC_DAPM_POST_PMD);1636 }1637}1638 1639/* Apply a DAPM power sequence.1640 *1641 * We walk over a pre-sorted list of widgets to apply power to. In1642 * order to minimise the number of writes to the device required1643 * multiple widgets will be updated in a single write where possible.1644 * Currently anything that requires more than a single write is not1645 * handled.1646 */1647static void dapm_seq_run(struct snd_soc_card *card,1648 struct list_head *list, int event, bool power_up)1649{1650 struct snd_soc_dapm_widget *w, *n;1651 struct snd_soc_dapm_context *d;1652 LIST_HEAD(pending);1653 int cur_sort = -1;1654 int cur_subseq = -1;1655 int cur_reg = SND_SOC_NOPM;1656 struct snd_soc_dapm_context *cur_dapm = NULL;1657 int i;1658 int *sort;1659 1660 if (power_up)1661 sort = dapm_up_seq;1662 else1663 sort = dapm_down_seq;1664 1665 list_for_each_entry_safe(w, n, list, power_list) {1666 int ret = 0;1667 1668 /* Do we need to apply any queued changes? */1669 if (sort[w->id] != cur_sort || w->reg != cur_reg ||1670 w->dapm != cur_dapm || w->subseq != cur_subseq) {1671 if (!list_empty(&pending))1672 dapm_seq_run_coalesced(card, &pending);1673 1674 if (cur_dapm && cur_dapm->component) {1675 for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++)1676 if (sort[i] == cur_sort)1677 snd_soc_component_seq_notifier(1678 cur_dapm->component,1679 i, cur_subseq);1680 }1681 1682 if (cur_dapm && w->dapm != cur_dapm)1683 soc_dapm_async_complete(cur_dapm);1684 1685 INIT_LIST_HEAD(&pending);1686 cur_sort = -1;1687 cur_subseq = INT_MIN;1688 cur_reg = SND_SOC_NOPM;1689 cur_dapm = NULL;1690 }1691 1692 switch (w->id) {1693 case snd_soc_dapm_pre:1694 if (!w->event)1695 continue;1696 1697 if (event == SND_SOC_DAPM_STREAM_START)1698 ret = w->event(w,1699 NULL, SND_SOC_DAPM_PRE_PMU);1700 else if (event == SND_SOC_DAPM_STREAM_STOP)1701 ret = w->event(w,1702 NULL, SND_SOC_DAPM_PRE_PMD);1703 break;1704 1705 case snd_soc_dapm_post:1706 if (!w->event)1707 continue;1708 1709 if (event == SND_SOC_DAPM_STREAM_START)1710 ret = w->event(w,1711 NULL, SND_SOC_DAPM_POST_PMU);1712 else if (event == SND_SOC_DAPM_STREAM_STOP)1713 ret = w->event(w,1714 NULL, SND_SOC_DAPM_POST_PMD);1715 break;1716 1717 default:1718 /* Queue it up for application */1719 cur_sort = sort[w->id];1720 cur_subseq = w->subseq;1721 cur_reg = w->reg;1722 cur_dapm = w->dapm;1723 list_move(&w->power_list, &pending);1724 break;1725 }1726 1727 if (ret < 0)1728 dev_err(w->dapm->dev,1729 "ASoC: Failed to apply widget power: %d\n", ret);1730 }1731 1732 if (!list_empty(&pending))1733 dapm_seq_run_coalesced(card, &pending);1734 1735 if (cur_dapm && cur_dapm->component) {1736 for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++)1737 if (sort[i] == cur_sort)1738 snd_soc_component_seq_notifier(1739 cur_dapm->component,1740 i, cur_subseq);1741 }1742 1743 for_each_card_dapms(card, d)1744 soc_dapm_async_complete(d);1745}1746 1747static void dapm_widget_update(struct snd_soc_card *card)1748{1749 struct snd_soc_dapm_update *update = card->update;1750 struct snd_soc_dapm_widget_list *wlist;1751 struct snd_soc_dapm_widget *w = NULL;1752 unsigned int wi;1753 int ret;1754 1755 if (!update || !dapm_kcontrol_is_powered(update->kcontrol))1756 return;1757 1758 wlist = dapm_kcontrol_get_wlist(update->kcontrol);1759 1760 for_each_dapm_widgets(wlist, wi, w) {1761 if (w->event && (w->event_flags & SND_SOC_DAPM_PRE_REG)) {1762 ret = w->event(w, update->kcontrol, SND_SOC_DAPM_PRE_REG);1763 if (ret != 0)1764 dev_err(w->dapm->dev, "ASoC: %s DAPM pre-event failed: %d\n",1765 w->name, ret);1766 }1767 }1768 1769 if (!w)1770 return;1771 1772 ret = soc_dapm_update_bits(w->dapm, update->reg, update->mask,1773 update->val);1774 if (ret < 0)1775 dev_err(w->dapm->dev, "ASoC: %s DAPM update failed: %d\n",1776 w->name, ret);1777 1778 if (update->has_second_set) {1779 ret = soc_dapm_update_bits(w->dapm, update->reg2,1780 update->mask2, update->val2);1781 if (ret < 0)1782 dev_err(w->dapm->dev,1783 "ASoC: %s DAPM update failed: %d\n",1784 w->name, ret);1785 }1786 1787 for_each_dapm_widgets(wlist, wi, w) {1788 if (w->event && (w->event_flags & SND_SOC_DAPM_POST_REG)) {1789 ret = w->event(w, update->kcontrol, SND_SOC_DAPM_POST_REG);1790 if (ret != 0)1791 dev_err(w->dapm->dev, "ASoC: %s DAPM post-event failed: %d\n",1792 w->name, ret);1793 }1794 }1795}1796 1797/* Async callback run prior to DAPM sequences - brings to _PREPARE if1798 * they're changing state.1799 */1800static void dapm_pre_sequence_async(void *data, async_cookie_t cookie)1801{1802 struct snd_soc_dapm_context *d = data;1803 int ret;1804 1805 /* If we're off and we're not supposed to go into STANDBY */1806 if (d->bias_level == SND_SOC_BIAS_OFF &&1807 d->target_bias_level != SND_SOC_BIAS_OFF) {1808 if (d->dev && cookie)1809 pm_runtime_get_sync(d->dev);1810 1811 ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_STANDBY);1812 if (ret != 0)1813 dev_err(d->dev,1814 "ASoC: Failed to turn on bias: %d\n", ret);1815 }1816 1817 /* Prepare for a transition to ON or away from ON */1818 if ((d->target_bias_level == SND_SOC_BIAS_ON &&1819 d->bias_level != SND_SOC_BIAS_ON) ||1820 (d->target_bias_level != SND_SOC_BIAS_ON &&1821 d->bias_level == SND_SOC_BIAS_ON)) {1822 ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_PREPARE);1823 if (ret != 0)1824 dev_err(d->dev,1825 "ASoC: Failed to prepare bias: %d\n", ret);1826 }1827}1828 1829/* Async callback run prior to DAPM sequences - brings to their final1830 * state.1831 */1832static void dapm_post_sequence_async(void *data, async_cookie_t cookie)1833{1834 struct snd_soc_dapm_context *d = data;1835 int ret;1836 1837 /* If we just powered the last thing off drop to standby bias */1838 if (d->bias_level == SND_SOC_BIAS_PREPARE &&1839 (d->target_bias_level == SND_SOC_BIAS_STANDBY ||1840 d->target_bias_level == SND_SOC_BIAS_OFF)) {1841 ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_STANDBY);1842 if (ret != 0)1843 dev_err(d->dev, "ASoC: Failed to apply standby bias: %d\n",1844 ret);1845 }1846 1847 /* If we're in standby and can support bias off then do that */1848 if (d->bias_level == SND_SOC_BIAS_STANDBY &&1849 d->target_bias_level == SND_SOC_BIAS_OFF) {1850 ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_OFF);1851 if (ret != 0)1852 dev_err(d->dev, "ASoC: Failed to turn off bias: %d\n",1853 ret);1854 1855 if (d->dev && cookie)1856 pm_runtime_put(d->dev);1857 }1858 1859 /* If we just powered up then move to active bias */1860 if (d->bias_level == SND_SOC_BIAS_PREPARE &&1861 d->target_bias_level == SND_SOC_BIAS_ON) {1862 ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_ON);1863 if (ret != 0)1864 dev_err(d->dev, "ASoC: Failed to apply active bias: %d\n",1865 ret);1866 }1867}1868 1869static void dapm_widget_set_peer_power(struct snd_soc_dapm_widget *peer,1870 bool power, bool connect)1871{1872 /* If a connection is being made or broken then that update1873 * will have marked the peer dirty, otherwise the widgets are1874 * not connected and this update has no impact. */1875 if (!connect)1876 return;1877 1878 /* If the peer is already in the state we're moving to then we1879 * won't have an impact on it. */1880 if (power != peer->power)1881 dapm_mark_dirty(peer, "peer state change");1882}1883 1884static void dapm_power_one_widget(struct snd_soc_dapm_widget *w,1885 struct list_head *up_list,1886 struct list_head *down_list)1887{1888 struct snd_soc_dapm_path *path;1889 int power;1890 1891 switch (w->id) {1892 case snd_soc_dapm_pre:1893 power = 0;1894 goto end;1895 case snd_soc_dapm_post:1896 power = 1;1897 goto end;1898 default:1899 break;1900 }1901 1902 power = dapm_widget_power_check(w);1903 1904 if (w->power == power)1905 return;1906 1907 trace_snd_soc_dapm_widget_power(w, power);1908 1909 /*1910 * If we changed our power state perhaps our neigbours1911 * changed also.1912 */1913 snd_soc_dapm_widget_for_each_source_path(w, path)1914 dapm_widget_set_peer_power(path->source, power, path->connect);1915 1916 /*1917 * Supplies can't affect their outputs, only their inputs1918 */1919 if (!w->is_supply)1920 snd_soc_dapm_widget_for_each_sink_path(w, path)1921 dapm_widget_set_peer_power(path->sink, power, path->connect);1922 1923end:1924 if (power)1925 dapm_seq_insert(w, up_list, true);1926 else1927 dapm_seq_insert(w, down_list, false);1928}1929 1930static bool dapm_idle_bias_off(struct snd_soc_dapm_context *dapm)1931{1932 if (dapm->idle_bias_off)1933 return true;1934 1935 switch (snd_power_get_state(dapm->card->snd_card)) {1936 case SNDRV_CTL_POWER_D3hot:1937 case SNDRV_CTL_POWER_D3cold:1938 return dapm->suspend_bias_off;1939 default:1940 break;1941 }1942 1943 return false;1944}1945 1946/*1947 * Scan each dapm widget for complete audio path.1948 * A complete path is a route that has valid endpoints i.e.:-1949 *1950 * o DAC to output pin.1951 * o Input pin to ADC.1952 * o Input pin to Output pin (bypass, sidetone)1953 * o DAC to ADC (loopback).1954 */1955static int dapm_power_widgets(struct snd_soc_card *card, int event)1956{1957 struct snd_soc_dapm_widget *w;1958 struct snd_soc_dapm_context *d;1959 LIST_HEAD(up_list);1960 LIST_HEAD(down_list);1961 ASYNC_DOMAIN_EXCLUSIVE(async_domain);1962 enum snd_soc_bias_level bias;1963 int ret;1964 1965 snd_soc_dapm_mutex_assert_held(card);1966 1967 trace_snd_soc_dapm_start(card, event);1968 1969 for_each_card_dapms(card, d) {1970 if (dapm_idle_bias_off(d))1971 d->target_bias_level = SND_SOC_BIAS_OFF;1972 else1973 d->target_bias_level = SND_SOC_BIAS_STANDBY;1974 }1975 1976 dapm_reset(card);1977 1978 /* Check which widgets we need to power and store them in1979 * lists indicating if they should be powered up or down. We1980 * only check widgets that have been flagged as dirty but note1981 * that new widgets may be added to the dirty list while we1982 * iterate.1983 */1984 list_for_each_entry(w, &card->dapm_dirty, dirty) {1985 dapm_power_one_widget(w, &up_list, &down_list);1986 }1987 1988 for_each_card_widgets(card, w) {1989 switch (w->id) {1990 case snd_soc_dapm_pre:1991 case snd_soc_dapm_post:1992 /* These widgets always need to be powered */1993 break;1994 default:1995 list_del_init(&w->dirty);1996 break;1997 }1998 1999 if (w->new_power) {2000 d = w->dapm;2001 2002 /* Supplies and micbiases only bring the2003 * context up to STANDBY as unless something2004 * else is active and passing audio they2005 * generally don't require full power. Signal2006 * generators are virtual pins and have no2007 * power impact themselves.2008 */2009 switch (w->id) {2010 case snd_soc_dapm_siggen:2011 case snd_soc_dapm_vmid:2012 break;2013 case snd_soc_dapm_supply:2014 case snd_soc_dapm_regulator_supply:2015 case snd_soc_dapm_pinctrl:2016 case snd_soc_dapm_clock_supply:2017 case snd_soc_dapm_micbias:2018 if (d->target_bias_level < SND_SOC_BIAS_STANDBY)2019 d->target_bias_level = SND_SOC_BIAS_STANDBY;2020 break;2021 default:2022 d->target_bias_level = SND_SOC_BIAS_ON;2023 break;2024 }2025 }2026 2027 }2028 2029 /* Force all contexts in the card to the same bias state if2030 * they're not ground referenced.2031 */2032 bias = SND_SOC_BIAS_OFF;2033 for_each_card_dapms(card, d)2034 if (d->target_bias_level > bias)2035 bias = d->target_bias_level;2036 for_each_card_dapms(card, d)2037 if (!dapm_idle_bias_off(d))2038 d->target_bias_level = bias;2039 2040 trace_snd_soc_dapm_walk_done(card);2041 2042 /* Run card bias changes at first */2043 dapm_pre_sequence_async(&card->dapm, 0);2044 /* Run other bias changes in parallel */2045 for_each_card_dapms(card, d) {2046 if (d != &card->dapm && d->bias_level != d->target_bias_level)2047 async_schedule_domain(dapm_pre_sequence_async, d,2048 &async_domain);2049 }2050 async_synchronize_full_domain(&async_domain);2051 2052 list_for_each_entry(w, &down_list, power_list) {2053 dapm_seq_check_event(card, w, SND_SOC_DAPM_WILL_PMD);2054 }2055 2056 list_for_each_entry(w, &up_list, power_list) {2057 dapm_seq_check_event(card, w, SND_SOC_DAPM_WILL_PMU);2058 }2059 2060 /* Power down widgets first; try to avoid amplifying pops. */2061 dapm_seq_run(card, &down_list, event, false);2062 2063 dapm_widget_update(card);2064 2065 /* Now power up. */2066 dapm_seq_run(card, &up_list, event, true);2067 2068 /* Run all the bias changes in parallel */2069 for_each_card_dapms(card, d) {2070 if (d != &card->dapm && d->bias_level != d->target_bias_level)2071 async_schedule_domain(dapm_post_sequence_async, d,2072 &async_domain);2073 }2074 async_synchronize_full_domain(&async_domain);2075 /* Run card bias changes at last */2076 dapm_post_sequence_async(&card->dapm, 0);2077 2078 /* do we need to notify any clients that DAPM event is complete */2079 for_each_card_dapms(card, d) {2080 if (!d->component)2081 continue;2082 2083 ret = snd_soc_component_stream_event(d->component, event);2084 if (ret < 0)2085 return ret;2086 }2087 2088 pop_dbg(card->dev, card->pop_time,2089 "DAPM sequencing finished, waiting %dms\n", card->pop_time);2090 pop_wait(card->pop_time);2091 2092 trace_snd_soc_dapm_done(card, event);2093 2094 return 0;2095}2096 2097#ifdef CONFIG_DEBUG_FS2098 2099static const char * const snd_soc_dapm_type_name[] = {2100 [snd_soc_dapm_input] = "input",2101 [snd_soc_dapm_output] = "output",2102 [snd_soc_dapm_mux] = "mux",2103 [snd_soc_dapm_demux] = "demux",2104 [snd_soc_dapm_mixer] = "mixer",2105 [snd_soc_dapm_mixer_named_ctl] = "mixer_named_ctl",2106 [snd_soc_dapm_pga] = "pga",2107 [snd_soc_dapm_out_drv] = "out_drv",2108 [snd_soc_dapm_adc] = "adc",2109 [snd_soc_dapm_dac] = "dac",2110 [snd_soc_dapm_micbias] = "micbias",2111 [snd_soc_dapm_mic] = "mic",2112 [snd_soc_dapm_hp] = "hp",2113 [snd_soc_dapm_spk] = "spk",2114 [snd_soc_dapm_line] = "line",2115 [snd_soc_dapm_switch] = "switch",2116 [snd_soc_dapm_vmid] = "vmid",2117 [snd_soc_dapm_pre] = "pre",2118 [snd_soc_dapm_post] = "post",2119 [snd_soc_dapm_supply] = "supply",2120 [snd_soc_dapm_pinctrl] = "pinctrl",2121 [snd_soc_dapm_regulator_supply] = "regulator_supply",2122 [snd_soc_dapm_clock_supply] = "clock_supply",2123 [snd_soc_dapm_aif_in] = "aif_in",2124 [snd_soc_dapm_aif_out] = "aif_out",2125 [snd_soc_dapm_siggen] = "siggen",2126 [snd_soc_dapm_sink] = "sink",2127 [snd_soc_dapm_dai_in] = "dai_in",2128 [snd_soc_dapm_dai_out] = "dai_out",2129 [snd_soc_dapm_dai_link] = "dai_link",2130 [snd_soc_dapm_kcontrol] = "kcontrol",2131 [snd_soc_dapm_buffer] = "buffer",2132 [snd_soc_dapm_scheduler] = "scheduler",2133 [snd_soc_dapm_effect] = "effect",2134 [snd_soc_dapm_src] = "src",2135 [snd_soc_dapm_asrc] = "asrc",2136 [snd_soc_dapm_encoder] = "encoder",2137 [snd_soc_dapm_decoder] = "decoder",2138};2139 2140static ssize_t dapm_widget_power_read_file(struct file *file,2141 char __user *user_buf,2142 size_t count, loff_t *ppos)2143{2144 struct snd_soc_dapm_widget *w = file->private_data;2145 enum snd_soc_dapm_direction dir, rdir;2146 char *buf;2147 int in, out;2148 ssize_t ret;2149 struct snd_soc_dapm_path *p = NULL;2150 const char *c_name;2151 2152 BUILD_BUG_ON(ARRAY_SIZE(snd_soc_dapm_type_name) != SND_SOC_DAPM_TYPE_COUNT);2153 2154 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);2155 if (!buf)2156 return -ENOMEM;2157 2158 snd_soc_dapm_mutex_lock_root(w->dapm);2159 2160 /* Supply widgets are not handled by is_connected_{input,output}_ep() */2161 if (w->is_supply) {2162 in = 0;2163 out = 0;2164 } else {2165 in = is_connected_input_ep(w, NULL, NULL);2166 out = is_connected_output_ep(w, NULL, NULL);2167 }2168 2169 ret = scnprintf(buf, PAGE_SIZE, "%s: %s%s in %d out %d",2170 w->name, w->power ? "On" : "Off",2171 w->force ? " (forced)" : "", in, out);2172 2173 if (w->reg >= 0)2174 ret += scnprintf(buf + ret, PAGE_SIZE - ret,2175 " - R%d(0x%x) mask 0x%x",2176 w->reg, w->reg, w->mask << w->shift);2177 2178 ret += scnprintf(buf + ret, PAGE_SIZE - ret, "\n");2179 2180 if (w->sname)2181 ret += scnprintf(buf + ret, PAGE_SIZE - ret, " stream %s %s\n",2182 w->sname,2183 w->active ? "active" : "inactive");2184 2185 ret += scnprintf(buf + ret, PAGE_SIZE - ret, " widget-type %s\n",2186 snd_soc_dapm_type_name[w->id]);2187 2188 snd_soc_dapm_for_each_direction(dir) {2189 rdir = SND_SOC_DAPM_DIR_REVERSE(dir);2190 snd_soc_dapm_widget_for_each_path(w, dir, p) {2191 if (p->connected && !p->connected(p->source, p->sink))2192 continue;2193 2194 if (!p->connect)2195 continue;2196 2197 c_name = p->node[rdir]->dapm->component ?2198 p->node[rdir]->dapm->component->name : NULL;2199 ret += scnprintf(buf + ret, PAGE_SIZE - ret,2200 " %s \"%s\" \"%s\" \"%s\"\n",2201 (rdir == SND_SOC_DAPM_DIR_IN) ? "in" : "out",2202 p->name ? p->name : "static",2203 p->node[rdir]->name, c_name);2204 }2205 }2206 2207 snd_soc_dapm_mutex_unlock(w->dapm);2208 2209 ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);2210 2211 kfree(buf);2212 return ret;2213}2214 2215static const struct file_operations dapm_widget_power_fops = {2216 .open = simple_open,2217 .read = dapm_widget_power_read_file,2218 .llseek = default_llseek,2219};2220 2221static ssize_t dapm_bias_read_file(struct file *file, char __user *user_buf,2222 size_t count, loff_t *ppos)2223{2224 struct snd_soc_dapm_context *dapm = file->private_data;2225 char *level;2226 2227 switch (dapm->bias_level) {2228 case SND_SOC_BIAS_ON:2229 level = "On\n";2230 break;2231 case SND_SOC_BIAS_PREPARE:2232 level = "Prepare\n";2233 break;2234 case SND_SOC_BIAS_STANDBY:2235 level = "Standby\n";2236 break;2237 case SND_SOC_BIAS_OFF:2238 level = "Off\n";2239 break;2240 default:2241 WARN(1, "Unknown bias_level %d\n", dapm->bias_level);2242 level = "Unknown\n";2243 break;2244 }2245 2246 return simple_read_from_buffer(user_buf, count, ppos, level,2247 strlen(level));2248}2249 2250static const struct file_operations dapm_bias_fops = {2251 .open = simple_open,2252 .read = dapm_bias_read_file,2253 .llseek = default_llseek,2254};2255 2256void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm,2257 struct dentry *parent)2258{2259 if (IS_ERR_OR_NULL(parent))2260 return;2261 2262 dapm->debugfs_dapm = debugfs_create_dir("dapm", parent);2263 2264 debugfs_create_file("bias_level", 0444, dapm->debugfs_dapm, dapm,2265 &dapm_bias_fops);2266}2267 2268static void dapm_debugfs_add_widget(struct snd_soc_dapm_widget *w)2269{2270 struct snd_soc_dapm_context *dapm = w->dapm;2271 2272 if (!dapm->debugfs_dapm || !w->name)2273 return;2274 2275 debugfs_create_file(w->name, 0444, dapm->debugfs_dapm, w,2276 &dapm_widget_power_fops);2277}2278 2279static void dapm_debugfs_free_widget(struct snd_soc_dapm_widget *w)2280{2281 struct snd_soc_dapm_context *dapm = w->dapm;2282 2283 if (!dapm->debugfs_dapm || !w->name)2284 return;2285 2286 debugfs_lookup_and_remove(w->name, dapm->debugfs_dapm);2287}2288 2289static void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm)2290{2291 debugfs_remove_recursive(dapm->debugfs_dapm);2292 dapm->debugfs_dapm = NULL;2293}2294 2295#else2296void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm,2297 struct dentry *parent)2298{2299}2300 2301static inline void dapm_debugfs_add_widget(struct snd_soc_dapm_widget *w)2302{2303}2304 2305static inline void dapm_debugfs_free_widget(struct snd_soc_dapm_widget *w)2306{2307}2308 2309static inline void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm)2310{2311}2312 2313#endif2314 2315/*2316 * soc_dapm_connect_path() - Connects or disconnects a path2317 * @path: The path to update2318 * @connect: The new connect state of the path. True if the path is connected,2319 * false if it is disconnected.2320 * @reason: The reason why the path changed (for debugging only)2321 */2322static void soc_dapm_connect_path(struct snd_soc_dapm_path *path,2323 bool connect, const char *reason)2324{2325 if (path->connect == connect)2326 return;2327 2328 path->connect = connect;2329 dapm_mark_dirty(path->source, reason);2330 dapm_mark_dirty(path->sink, reason);2331 dapm_path_invalidate(path);2332}2333 2334/* test and update the power status of a mux widget */2335static int soc_dapm_mux_update_power(struct snd_soc_card *card,2336 struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e)2337{2338 struct snd_soc_dapm_path *path;2339 int found = 0;2340 bool connect;2341 2342 snd_soc_dapm_mutex_assert_held(card);2343 2344 /* find dapm widget path assoc with kcontrol */2345 dapm_kcontrol_for_each_path(path, kcontrol) {2346 found = 1;2347 /* we now need to match the string in the enum to the path */2348 if (e && !(strcmp(path->name, e->texts[mux])))2349 connect = true;2350 else2351 connect = false;2352 2353 soc_dapm_connect_path(path, connect, "mux update");2354 }2355 2356 if (found)2357 dapm_power_widgets(card, SND_SOC_DAPM_STREAM_NOP);2358 2359 return found;2360}2361 2362int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm,2363 struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e,2364 struct snd_soc_dapm_update *update)2365{2366 struct snd_soc_card *card = dapm->card;2367 int ret;2368 2369 snd_soc_dapm_mutex_lock(card);2370 card->update = update;2371 ret = soc_dapm_mux_update_power(card, kcontrol, mux, e);2372 card->update = NULL;2373 snd_soc_dapm_mutex_unlock(card);2374 if (ret > 0)2375 snd_soc_dpcm_runtime_update(card);2376 return ret;2377}2378EXPORT_SYMBOL_GPL(snd_soc_dapm_mux_update_power);2379 2380/* test and update the power status of a mixer or switch widget */2381static int soc_dapm_mixer_update_power(struct snd_soc_card *card,2382 struct snd_kcontrol *kcontrol,2383 int connect, int rconnect)2384{2385 struct snd_soc_dapm_path *path;2386 int found = 0;2387 2388 snd_soc_dapm_mutex_assert_held(card);2389 2390 /* find dapm widget path assoc with kcontrol */2391 dapm_kcontrol_for_each_path(path, kcontrol) {2392 /*2393 * Ideally this function should support any number of2394 * paths and channels. But since kcontrols only come2395 * in mono and stereo variants, we are limited to 22396 * channels.2397 *2398 * The following code assumes for stereo controls the2399 * first path (when 'found == 0') is the left channel,2400 * and all remaining paths (when 'found == 1') are the2401 * right channel.2402 *2403 * A stereo control is signified by a valid 'rconnect'2404 * value, either 0 for unconnected, or >= 0 for connected.2405 * This is chosen instead of using snd_soc_volsw_is_stereo,2406 * so that the behavior of snd_soc_dapm_mixer_update_power2407 * doesn't change even when the kcontrol passed in is2408 * stereo.2409 *2410 * It passes 'connect' as the path connect status for2411 * the left channel, and 'rconnect' for the right2412 * channel.2413 */2414 if (found && rconnect >= 0)2415 soc_dapm_connect_path(path, rconnect, "mixer update");2416 else2417 soc_dapm_connect_path(path, connect, "mixer update");2418 found = 1;2419 }2420 2421 if (found)2422 dapm_power_widgets(card, SND_SOC_DAPM_STREAM_NOP);2423 2424 return found;2425}2426 2427int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm,2428 struct snd_kcontrol *kcontrol, int connect,2429 struct snd_soc_dapm_update *update)2430{2431 struct snd_soc_card *card = dapm->card;2432 int ret;2433 2434 snd_soc_dapm_mutex_lock(card);2435 card->update = update;2436 ret = soc_dapm_mixer_update_power(card, kcontrol, connect, -1);2437 card->update = NULL;2438 snd_soc_dapm_mutex_unlock(card);2439 if (ret > 0)2440 snd_soc_dpcm_runtime_update(card);2441 return ret;2442}2443EXPORT_SYMBOL_GPL(snd_soc_dapm_mixer_update_power);2444 2445static ssize_t dapm_widget_show_component(struct snd_soc_component *cmpnt,2446 char *buf, int count)2447{2448 struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);2449 struct snd_soc_dapm_widget *w;2450 char *state = "not set";2451 2452 /* card won't be set for the dummy component, as a spot fix2453 * we're checking for that case specifically here but in future2454 * we will ensure that the dummy component looks like others.2455 */2456 if (!cmpnt->card)2457 return 0;2458 2459 for_each_card_widgets(cmpnt->card, w) {2460 if (w->dapm != dapm)2461 continue;2462 2463 /* only display widgets that burn power */2464 switch (w->id) {2465 case snd_soc_dapm_hp:2466 case snd_soc_dapm_mic:2467 case snd_soc_dapm_spk:2468 case snd_soc_dapm_line:2469 case snd_soc_dapm_micbias:2470 case snd_soc_dapm_dac:2471 case snd_soc_dapm_adc:2472 case snd_soc_dapm_pga:2473 case snd_soc_dapm_effect:2474 case snd_soc_dapm_out_drv:2475 case snd_soc_dapm_mixer:2476 case snd_soc_dapm_mixer_named_ctl:2477 case snd_soc_dapm_supply:2478 case snd_soc_dapm_regulator_supply:2479 case snd_soc_dapm_pinctrl:2480 case snd_soc_dapm_clock_supply:2481 if (w->name)2482 count += sysfs_emit_at(buf, count, "%s: %s\n",2483 w->name, w->power ? "On":"Off");2484 break;2485 default:2486 break;2487 }2488 }2489 2490 switch (snd_soc_dapm_get_bias_level(dapm)) {2491 case SND_SOC_BIAS_ON:2492 state = "On";2493 break;2494 case SND_SOC_BIAS_PREPARE:2495 state = "Prepare";2496 break;2497 case SND_SOC_BIAS_STANDBY:2498 state = "Standby";2499 break;2500 case SND_SOC_BIAS_OFF:2501 state = "Off";2502 break;2503 }2504 count += sysfs_emit_at(buf, count, "PM State: %s\n", state);2505 2506 return count;2507}2508 2509/* show dapm widget status in sys fs */2510static ssize_t dapm_widget_show(struct device *dev,2511 struct device_attribute *attr, char *buf)2512{2513 struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev);2514 struct snd_soc_dai *codec_dai;2515 int i, count = 0;2516 2517 snd_soc_dapm_mutex_lock_root(rtd->card);2518 2519 for_each_rtd_codec_dais(rtd, i, codec_dai) {2520 struct snd_soc_component *cmpnt = codec_dai->component;2521 2522 count = dapm_widget_show_component(cmpnt, buf, count);2523 }2524 2525 snd_soc_dapm_mutex_unlock(rtd->card);2526 2527 return count;2528}2529 2530static DEVICE_ATTR_RO(dapm_widget);2531 2532struct attribute *soc_dapm_dev_attrs[] = {2533 &dev_attr_dapm_widget.attr,2534 NULL2535};2536 2537static void dapm_free_path(struct snd_soc_dapm_path *path)2538{2539 list_del(&path->list_node[SND_SOC_DAPM_DIR_IN]);2540 list_del(&path->list_node[SND_SOC_DAPM_DIR_OUT]);2541 list_del(&path->list_kcontrol);2542 list_del(&path->list);2543 kfree(path);2544}2545 2546/**2547 * snd_soc_dapm_free_widget - Free specified widget2548 * @w: widget to free2549 *2550 * Removes widget from all paths and frees memory occupied by it.2551 */2552void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w)2553{2554 struct snd_soc_dapm_path *p, *next_p;2555 enum snd_soc_dapm_direction dir;2556 2557 if (!w)2558 return;2559 2560 list_del(&w->list);2561 list_del(&w->dirty);2562 /*2563 * remove source and sink paths associated to this widget.2564 * While removing the path, remove reference to it from both2565 * source and sink widgets so that path is removed only once.2566 */2567 snd_soc_dapm_for_each_direction(dir) {2568 snd_soc_dapm_widget_for_each_path_safe(w, dir, p, next_p)2569 dapm_free_path(p);2570 }2571 2572 dapm_debugfs_free_widget(w);2573 2574 kfree(w->kcontrols);2575 kfree_const(w->name);2576 kfree_const(w->sname);2577 kfree(w);2578}2579EXPORT_SYMBOL_GPL(snd_soc_dapm_free_widget);2580 2581/* free all dapm widgets and resources */2582static void dapm_free_widgets(struct snd_soc_dapm_context *dapm)2583{2584 struct snd_soc_dapm_widget *w, *next_w;2585 2586 for_each_card_widgets_safe(dapm->card, w, next_w) {2587 if (w->dapm != dapm)2588 continue;2589 snd_soc_dapm_free_widget(w);2590 }2591 2592 dapm->wcache_sink = NULL;2593 dapm->wcache_source = NULL;2594}2595 2596static struct snd_soc_dapm_widget *dapm_find_widget(2597 struct snd_soc_dapm_context *dapm, const char *pin,2598 bool search_other_contexts)2599{2600 struct snd_soc_dapm_widget *w;2601 struct snd_soc_dapm_widget *fallback = NULL;2602 char prefixed_pin[80];2603 const char *pin_name;2604 const char *prefix = soc_dapm_prefix(dapm);2605 2606 if (prefix) {2607 snprintf(prefixed_pin, sizeof(prefixed_pin), "%s %s",2608 prefix, pin);2609 pin_name = prefixed_pin;2610 } else {2611 pin_name = pin;2612 }2613 2614 for_each_card_widgets(dapm->card, w) {2615 if (!strcmp(w->name, pin_name)) {2616 if (w->dapm == dapm)2617 return w;2618 else2619 fallback = w;2620 }2621 }2622 2623 if (search_other_contexts)2624 return fallback;2625 2626 return NULL;2627}2628 2629/*2630 * set the DAPM pin status:2631 * returns 1 when the value has been updated, 0 when unchanged, or a negative2632 * error code; called from kcontrol put callback2633 */2634static int __snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm,2635 const char *pin, int status)2636{2637 struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, true);2638 int ret = 0;2639 2640 dapm_assert_locked(dapm);2641 2642 if (!w) {2643 dev_err(dapm->dev, "ASoC: DAPM unknown pin %s\n", pin);2644 return -EINVAL;2645 }2646 2647 if (w->connected != status) {2648 dapm_mark_dirty(w, "pin configuration");2649 dapm_widget_invalidate_input_paths(w);2650 dapm_widget_invalidate_output_paths(w);2651 ret = 1;2652 }2653 2654 w->connected = status;2655 if (status == 0)2656 w->force = 0;2657 2658 return ret;2659}2660 2661/*2662 * similar as __snd_soc_dapm_set_pin(), but returns 0 when successful;2663 * called from several API functions below2664 */2665static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm,2666 const char *pin, int status)2667{2668 int ret = __snd_soc_dapm_set_pin(dapm, pin, status);2669 2670 return ret < 0 ? ret : 0;2671}2672 2673/**2674 * snd_soc_dapm_sync_unlocked - scan and power dapm paths2675 * @dapm: DAPM context2676 *2677 * Walks all dapm audio paths and powers widgets according to their2678 * stream or path usage.2679 *2680 * Requires external locking.2681 *2682 * Returns 0 for success.2683 */2684int snd_soc_dapm_sync_unlocked(struct snd_soc_dapm_context *dapm)2685{2686 /*2687 * Suppress early reports (eg, jacks syncing their state) to avoid2688 * silly DAPM runs during card startup.2689 */2690 if (!snd_soc_card_is_instantiated(dapm->card))2691 return 0;2692 2693 return dapm_power_widgets(dapm->card, SND_SOC_DAPM_STREAM_NOP);2694}2695EXPORT_SYMBOL_GPL(snd_soc_dapm_sync_unlocked);2696 2697/**2698 * snd_soc_dapm_sync - scan and power dapm paths2699 * @dapm: DAPM context2700 *2701 * Walks all dapm audio paths and powers widgets according to their2702 * stream or path usage.2703 *2704 * Returns 0 for success.2705 */2706int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm)2707{2708 int ret;2709 2710 snd_soc_dapm_mutex_lock(dapm);2711 ret = snd_soc_dapm_sync_unlocked(dapm);2712 snd_soc_dapm_mutex_unlock(dapm);2713 return ret;2714}2715EXPORT_SYMBOL_GPL(snd_soc_dapm_sync);2716 2717static int dapm_update_dai_chan(struct snd_soc_dapm_path *p,2718 struct snd_soc_dapm_widget *w,2719 int channels)2720{2721 switch (w->id) {2722 case snd_soc_dapm_aif_out:2723 case snd_soc_dapm_aif_in:2724 break;2725 default:2726 return 0;2727 }2728 2729 dev_dbg(w->dapm->dev, "%s DAI route %s -> %s\n",2730 w->channel < channels ? "Connecting" : "Disconnecting",2731 p->source->name, p->sink->name);2732 2733 if (w->channel < channels)2734 soc_dapm_connect_path(p, true, "dai update");2735 else2736 soc_dapm_connect_path(p, false, "dai update");2737 2738 return 0;2739}2740 2741static int dapm_update_dai_unlocked(struct snd_pcm_substream *substream,2742 struct snd_pcm_hw_params *params,2743 struct snd_soc_dai *dai)2744{2745 int dir = substream->stream;2746 int channels = params_channels(params);2747 struct snd_soc_dapm_path *p;2748 struct snd_soc_dapm_widget *w;2749 int ret;2750 2751 w = snd_soc_dai_get_widget(dai, dir);2752 2753 if (!w)2754 return 0;2755 2756 dev_dbg(dai->dev, "Update DAI routes for %s %s\n", dai->name, snd_pcm_direction_name(dir));2757 2758 snd_soc_dapm_widget_for_each_sink_path(w, p) {2759 ret = dapm_update_dai_chan(p, p->sink, channels);2760 if (ret < 0)2761 return ret;2762 }2763 2764 snd_soc_dapm_widget_for_each_source_path(w, p) {2765 ret = dapm_update_dai_chan(p, p->source, channels);2766 if (ret < 0)2767 return ret;2768 }2769 2770 return 0;2771}2772 2773int snd_soc_dapm_update_dai(struct snd_pcm_substream *substream,2774 struct snd_pcm_hw_params *params,2775 struct snd_soc_dai *dai)2776{2777 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);2778 int ret;2779 2780 snd_soc_dapm_mutex_lock(rtd->card);2781 ret = dapm_update_dai_unlocked(substream, params, dai);2782 snd_soc_dapm_mutex_unlock(rtd->card);2783 2784 return ret;2785}2786EXPORT_SYMBOL_GPL(snd_soc_dapm_update_dai);2787 2788int snd_soc_dapm_widget_name_cmp(struct snd_soc_dapm_widget *widget, const char *s)2789{2790 struct snd_soc_component *component = widget->dapm->component;2791 const char *wname = widget->name;2792 2793 if (component && component->name_prefix)2794 wname += strlen(component->name_prefix) + 1; /* plus space */2795 2796 return strcmp(wname, s);2797}2798EXPORT_SYMBOL_GPL(snd_soc_dapm_widget_name_cmp);2799 2800/*2801 * dapm_update_widget_flags() - Re-compute widget sink and source flags2802 * @w: The widget for which to update the flags2803 *2804 * Some widgets have a dynamic category which depends on which neighbors they2805 * are connected to. This function update the category for these widgets.2806 *2807 * This function must be called whenever a path is added or removed to a widget.2808 */2809static void dapm_update_widget_flags(struct snd_soc_dapm_widget *w)2810{2811 enum snd_soc_dapm_direction dir;2812 struct snd_soc_dapm_path *p;2813 unsigned int ep;2814 2815 switch (w->id) {2816 case snd_soc_dapm_input:2817 /* On a fully routed card an input is never a source */2818 if (w->dapm->card->fully_routed)2819 return;2820 ep = SND_SOC_DAPM_EP_SOURCE;2821 snd_soc_dapm_widget_for_each_source_path(w, p) {2822 if (p->source->id == snd_soc_dapm_micbias ||2823 p->source->id == snd_soc_dapm_mic ||2824 p->source->id == snd_soc_dapm_line ||2825 p->source->id == snd_soc_dapm_output) {2826 ep = 0;2827 break;2828 }2829 }2830 break;2831 case snd_soc_dapm_output:2832 /* On a fully routed card a output is never a sink */2833 if (w->dapm->card->fully_routed)2834 return;2835 ep = SND_SOC_DAPM_EP_SINK;2836 snd_soc_dapm_widget_for_each_sink_path(w, p) {2837 if (p->sink->id == snd_soc_dapm_spk ||2838 p->sink->id == snd_soc_dapm_hp ||2839 p->sink->id == snd_soc_dapm_line ||2840 p->sink->id == snd_soc_dapm_input) {2841 ep = 0;2842 break;2843 }2844 }2845 break;2846 case snd_soc_dapm_line:2847 ep = 0;2848 snd_soc_dapm_for_each_direction(dir) {2849 if (!list_empty(&w->edges[dir]))2850 ep |= SND_SOC_DAPM_DIR_TO_EP(dir);2851 }2852 break;2853 default:2854 return;2855 }2856 2857 w->is_ep = ep;2858}2859 2860static int snd_soc_dapm_check_dynamic_path(struct snd_soc_dapm_context *dapm,2861 struct snd_soc_dapm_widget *source, struct snd_soc_dapm_widget *sink,2862 const char *control)2863{2864 bool dynamic_source = false;2865 bool dynamic_sink = false;2866 2867 if (!control)2868 return 0;2869 2870 switch (source->id) {2871 case snd_soc_dapm_demux:2872 dynamic_source = true;2873 break;2874 default:2875 break;2876 }2877 2878 switch (sink->id) {2879 case snd_soc_dapm_mux:2880 case snd_soc_dapm_switch:2881 case snd_soc_dapm_mixer:2882 case snd_soc_dapm_mixer_named_ctl:2883 dynamic_sink = true;2884 break;2885 default:2886 break;2887 }2888 2889 if (dynamic_source && dynamic_sink) {2890 dev_err(dapm->dev,2891 "Direct connection between demux and mixer/mux not supported for path %s -> [%s] -> %s\n",2892 source->name, control, sink->name);2893 return -EINVAL;2894 } else if (!dynamic_source && !dynamic_sink) {2895 dev_err(dapm->dev,2896 "Control not supported for path %s -> [%s] -> %s\n",2897 source->name, control, sink->name);2898 return -EINVAL;2899 }2900 2901 return 0;2902}2903 2904static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,2905 struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink,2906 const char *control,2907 int (*connected)(struct snd_soc_dapm_widget *source,2908 struct snd_soc_dapm_widget *sink))2909{2910 enum snd_soc_dapm_direction dir;2911 struct snd_soc_dapm_path *path;2912 int ret;2913 2914 if (wsink->is_supply && !wsource->is_supply) {2915 dev_err(dapm->dev,2916 "Connecting non-supply widget to supply widget is not supported (%s -> %s)\n",2917 wsource->name, wsink->name);2918 return -EINVAL;2919 }2920 2921 if (connected && !wsource->is_supply) {2922 dev_err(dapm->dev,2923 "connected() callback only supported for supply widgets (%s -> %s)\n",2924 wsource->name, wsink->name);2925 return -EINVAL;2926 }2927 2928 if (wsource->is_supply && control) {2929 dev_err(dapm->dev,2930 "Conditional paths are not supported for supply widgets (%s -> [%s] -> %s)\n",2931 wsource->name, control, wsink->name);2932 return -EINVAL;2933 }2934 2935 ret = snd_soc_dapm_check_dynamic_path(dapm, wsource, wsink, control);2936 if (ret)2937 return ret;2938 2939 path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL);2940 if (!path)2941 return -ENOMEM;2942 2943 path->node[SND_SOC_DAPM_DIR_IN] = wsource;2944 path->node[SND_SOC_DAPM_DIR_OUT] = wsink;2945 2946 path->connected = connected;2947 INIT_LIST_HEAD(&path->list);2948 INIT_LIST_HEAD(&path->list_kcontrol);2949 2950 if (wsource->is_supply || wsink->is_supply)2951 path->is_supply = 1;2952 2953 /* connect static paths */2954 if (control == NULL) {2955 path->connect = 1;2956 } else {2957 switch (wsource->id) {2958 case snd_soc_dapm_demux:2959 ret = dapm_connect_mux(dapm, path, control, wsource);2960 if (ret)2961 goto err;2962 break;2963 default:2964 break;2965 }2966 2967 switch (wsink->id) {2968 case snd_soc_dapm_mux:2969 ret = dapm_connect_mux(dapm, path, control, wsink);2970 if (ret != 0)2971 goto err;2972 break;2973 case snd_soc_dapm_switch:2974 case snd_soc_dapm_mixer:2975 case snd_soc_dapm_mixer_named_ctl:2976 ret = dapm_connect_mixer(dapm, path, control);2977 if (ret != 0)2978 goto err;2979 break;2980 default:2981 break;2982 }2983 }2984 2985 list_add(&path->list, &dapm->card->paths);2986 2987 snd_soc_dapm_for_each_direction(dir)2988 list_add(&path->list_node[dir], &path->node[dir]->edges[dir]);2989 2990 snd_soc_dapm_for_each_direction(dir) {2991 dapm_update_widget_flags(path->node[dir]);2992 dapm_mark_dirty(path->node[dir], "Route added");2993 }2994 2995 if (snd_soc_card_is_instantiated(dapm->card) && path->connect)2996 dapm_path_invalidate(path);2997 2998 return 0;2999err:3000 kfree(path);3001 return ret;3002}3003 3004static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,3005 const struct snd_soc_dapm_route *route)3006{3007 struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w;3008 struct snd_soc_dapm_widget *wtsource = NULL, *wtsink = NULL;3009 const char *sink;3010 const char *source;3011 char prefixed_sink[80];3012 char prefixed_source[80];3013 const char *prefix;3014 unsigned int sink_ref = 0;3015 unsigned int source_ref = 0;3016 int ret;3017 3018 prefix = soc_dapm_prefix(dapm);3019 if (prefix) {3020 snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s",3021 prefix, route->sink);3022 sink = prefixed_sink;3023 snprintf(prefixed_source, sizeof(prefixed_source), "%s %s",3024 prefix, route->source);3025 source = prefixed_source;3026 } else {3027 sink = route->sink;3028 source = route->source;3029 }3030 3031 wsource = dapm_wcache_lookup(dapm->wcache_source, source);3032 wsink = dapm_wcache_lookup(dapm->wcache_sink, sink);3033 3034 if (wsink && wsource)3035 goto skip_search;3036 3037 /*3038 * find src and dest widgets over all widgets but favor a widget from3039 * current DAPM context3040 */3041 for_each_card_widgets(dapm->card, w) {3042 if (!wsink && !(strcmp(w->name, sink))) {3043 wtsink = w;3044 if (w->dapm == dapm) {3045 wsink = w;3046 if (wsource)3047 break;3048 }3049 sink_ref++;3050 if (sink_ref > 1)3051 dev_warn(dapm->dev,3052 "ASoC: sink widget %s overwritten\n",3053 w->name);3054 continue;3055 }3056 if (!wsource && !(strcmp(w->name, source))) {3057 wtsource = w;3058 if (w->dapm == dapm) {3059 wsource = w;3060 if (wsink)3061 break;3062 }3063 source_ref++;3064 if (source_ref > 1)3065 dev_warn(dapm->dev,3066 "ASoC: source widget %s overwritten\n",3067 w->name);3068 }3069 }3070 /* use widget from another DAPM context if not found from this */3071 if (!wsink)3072 wsink = wtsink;3073 if (!wsource)3074 wsource = wtsource;3075 3076 ret = -ENODEV;3077 if (!wsource)3078 goto err;3079 if (!wsink)3080 goto err;3081 3082skip_search:3083 /* update cache */3084 dapm->wcache_sink = wsink;3085 dapm->wcache_source = wsource;3086 3087 ret = snd_soc_dapm_add_path(dapm, wsource, wsink, route->control,3088 route->connected);3089err:3090 if (ret)3091 dev_err(dapm->dev, "ASoC: Failed to add route %s%s -%s%s%s> %s%s\n",3092 source, !wsource ? "(*)" : "",3093 !route->control ? "" : "> [",3094 !route->control ? "" : route->control,3095 !route->control ? "" : "] -",3096 sink, !wsink ? "(*)" : "");3097 return ret;3098}3099 3100static int snd_soc_dapm_del_route(struct snd_soc_dapm_context *dapm,3101 const struct snd_soc_dapm_route *route)3102{3103 struct snd_soc_dapm_path *path, *p;3104 const char *sink;3105 const char *source;3106 char prefixed_sink[80];3107 char prefixed_source[80];3108 const char *prefix;3109 3110 if (route->control) {3111 dev_err(dapm->dev,3112 "ASoC: Removal of routes with controls not supported\n");3113 return -EINVAL;3114 }3115 3116 prefix = soc_dapm_prefix(dapm);3117 if (prefix) {3118 snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s",3119 prefix, route->sink);3120 sink = prefixed_sink;3121 snprintf(prefixed_source, sizeof(prefixed_source), "%s %s",3122 prefix, route->source);3123 source = prefixed_source;3124 } else {3125 sink = route->sink;3126 source = route->source;3127 }3128 3129 path = NULL;3130 list_for_each_entry(p, &dapm->card->paths, list) {3131 if (strcmp(p->source->name, source) != 0)3132 continue;3133 if (strcmp(p->sink->name, sink) != 0)3134 continue;3135 path = p;3136 break;3137 }3138 3139 if (path) {3140 struct snd_soc_dapm_widget *wsource = path->source;3141 struct snd_soc_dapm_widget *wsink = path->sink;3142 3143 dapm_mark_dirty(wsource, "Route removed");3144 dapm_mark_dirty(wsink, "Route removed");3145 if (path->connect)3146 dapm_path_invalidate(path);3147 3148 dapm_free_path(path);3149 3150 /* Update any path related flags */3151 dapm_update_widget_flags(wsource);3152 dapm_update_widget_flags(wsink);3153 } else {3154 dev_warn(dapm->dev, "ASoC: Route %s->%s does not exist\n",3155 source, sink);3156 }3157 3158 return 0;3159}3160 3161/**3162 * snd_soc_dapm_add_routes - Add routes between DAPM widgets3163 * @dapm: DAPM context3164 * @route: audio routes3165 * @num: number of routes3166 *3167 * Connects 2 dapm widgets together via a named audio path. The sink is3168 * the widget receiving the audio signal, whilst the source is the sender3169 * of the audio signal.3170 *3171 * Returns 0 for success else error. On error all resources can be freed3172 * with a call to snd_soc_card_free().3173 */3174int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm,3175 const struct snd_soc_dapm_route *route, int num)3176{3177 int i, ret = 0;3178 3179 snd_soc_dapm_mutex_lock(dapm);3180 for (i = 0; i < num; i++) {3181 int r = snd_soc_dapm_add_route(dapm, route);3182 if (r < 0)3183 ret = r;3184 route++;3185 }3186 snd_soc_dapm_mutex_unlock(dapm);3187 3188 return ret;3189}3190EXPORT_SYMBOL_GPL(snd_soc_dapm_add_routes);3191 3192/**3193 * snd_soc_dapm_del_routes - Remove routes between DAPM widgets3194 * @dapm: DAPM context3195 * @route: audio routes3196 * @num: number of routes3197 *3198 * Removes routes from the DAPM context.3199 */3200int snd_soc_dapm_del_routes(struct snd_soc_dapm_context *dapm,3201 const struct snd_soc_dapm_route *route, int num)3202{3203 int i;3204 3205 snd_soc_dapm_mutex_lock(dapm);3206 for (i = 0; i < num; i++) {3207 snd_soc_dapm_del_route(dapm, route);3208 route++;3209 }3210 snd_soc_dapm_mutex_unlock(dapm);3211 3212 return 0;3213}3214EXPORT_SYMBOL_GPL(snd_soc_dapm_del_routes);3215 3216static int snd_soc_dapm_weak_route(struct snd_soc_dapm_context *dapm,3217 const struct snd_soc_dapm_route *route)3218{3219 struct snd_soc_dapm_widget *source = dapm_find_widget(dapm,3220 route->source,3221 true);3222 struct snd_soc_dapm_widget *sink = dapm_find_widget(dapm,3223 route->sink,3224 true);3225 struct snd_soc_dapm_path *path;3226 int count = 0;3227 3228 if (!source) {3229 dev_err(dapm->dev, "ASoC: Unable to find source %s for weak route\n",3230 route->source);3231 return -ENODEV;3232 }3233 3234 if (!sink) {3235 dev_err(dapm->dev, "ASoC: Unable to find sink %s for weak route\n",3236 route->sink);3237 return -ENODEV;3238 }3239 3240 if (route->control || route->connected)3241 dev_warn(dapm->dev, "ASoC: Ignoring control for weak route %s->%s\n",3242 route->source, route->sink);3243 3244 snd_soc_dapm_widget_for_each_sink_path(source, path) {3245 if (path->sink == sink) {3246 path->weak = 1;3247 count++;3248 }3249 }3250 3251 if (count == 0)3252 dev_err(dapm->dev, "ASoC: No path found for weak route %s->%s\n",3253 route->source, route->sink);3254 if (count > 1)3255 dev_warn(dapm->dev, "ASoC: %d paths found for weak route %s->%s\n",3256 count, route->source, route->sink);3257 3258 return 0;3259}3260 3261/**3262 * snd_soc_dapm_weak_routes - Mark routes between DAPM widgets as weak3263 * @dapm: DAPM context3264 * @route: audio routes3265 * @num: number of routes3266 *3267 * Mark existing routes matching those specified in the passed array3268 * as being weak, meaning that they are ignored for the purpose of3269 * power decisions. The main intended use case is for sidetone paths3270 * which couple audio between other independent paths if they are both3271 * active in order to make the combination work better at the user3272 * level but which aren't intended to be "used".3273 *3274 * Note that CODEC drivers should not use this as sidetone type paths3275 * can frequently also be used as bypass paths.3276 */3277int snd_soc_dapm_weak_routes(struct snd_soc_dapm_context *dapm,3278 const struct snd_soc_dapm_route *route, int num)3279{3280 int i;3281 int ret = 0;3282 3283 snd_soc_dapm_mutex_lock_root(dapm);3284 for (i = 0; i < num; i++) {3285 int err = snd_soc_dapm_weak_route(dapm, route);3286 if (err)3287 ret = err;3288 route++;3289 }3290 snd_soc_dapm_mutex_unlock(dapm);3291 3292 return ret;3293}3294EXPORT_SYMBOL_GPL(snd_soc_dapm_weak_routes);3295 3296/**3297 * snd_soc_dapm_new_widgets - add new dapm widgets3298 * @card: card to be checked for new dapm widgets3299 *3300 * Checks the codec for any new dapm widgets and creates them if found.3301 *3302 * Returns 0 for success.3303 */3304int snd_soc_dapm_new_widgets(struct snd_soc_card *card)3305{3306 struct snd_soc_dapm_widget *w;3307 unsigned int val;3308 3309 snd_soc_dapm_mutex_lock_root(card);3310 3311 for_each_card_widgets(card, w)3312 {3313 if (w->new)3314 continue;3315 3316 if (w->num_kcontrols) {3317 w->kcontrols = kcalloc(w->num_kcontrols,3318 sizeof(struct snd_kcontrol *),3319 GFP_KERNEL);3320 if (!w->kcontrols) {3321 snd_soc_dapm_mutex_unlock(card);3322 return -ENOMEM;3323 }3324 }3325 3326 switch(w->id) {3327 case snd_soc_dapm_switch:3328 case snd_soc_dapm_mixer:3329 case snd_soc_dapm_mixer_named_ctl:3330 dapm_new_mixer(w);3331 break;3332 case snd_soc_dapm_mux:3333 case snd_soc_dapm_demux:3334 dapm_new_mux(w);3335 break;3336 case snd_soc_dapm_pga:3337 case snd_soc_dapm_effect:3338 case snd_soc_dapm_out_drv:3339 dapm_new_pga(w);3340 break;3341 case snd_soc_dapm_dai_link:3342 dapm_new_dai_link(w);3343 break;3344 default:3345 break;3346 }3347 3348 /* Read the initial power state from the device */3349 if (w->reg >= 0) {3350 val = soc_dapm_read(w->dapm, w->reg);3351 val = val >> w->shift;3352 val &= w->mask;3353 if (val == w->on_val)3354 w->power = 1;3355 }3356 3357 w->new = 1;3358 3359 dapm_mark_dirty(w, "new widget");3360 dapm_debugfs_add_widget(w);3361 }3362 3363 dapm_power_widgets(card, SND_SOC_DAPM_STREAM_NOP);3364 snd_soc_dapm_mutex_unlock(card);3365 return 0;3366}3367EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets);3368 3369/**3370 * snd_soc_dapm_get_volsw - dapm mixer get callback3371 * @kcontrol: mixer control3372 * @ucontrol: control element information3373 *3374 * Callback to get the value of a dapm mixer control.3375 *3376 * Returns 0 for success.3377 */3378int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,3379 struct snd_ctl_elem_value *ucontrol)3380{3381 struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);3382 struct soc_mixer_control *mc =3383 (struct soc_mixer_control *)kcontrol->private_value;3384 int reg = mc->reg;3385 unsigned int shift = mc->shift;3386 int max = mc->max;3387 unsigned int width = fls(max);3388 unsigned int mask = (1 << fls(max)) - 1;3389 unsigned int invert = mc->invert;3390 unsigned int reg_val, val, rval = 0;3391 3392 snd_soc_dapm_mutex_lock(dapm);3393 if (dapm_kcontrol_is_powered(kcontrol) && reg != SND_SOC_NOPM) {3394 reg_val = soc_dapm_read(dapm, reg);3395 val = (reg_val >> shift) & mask;3396 3397 if (reg != mc->rreg)3398 reg_val = soc_dapm_read(dapm, mc->rreg);3399 3400 if (snd_soc_volsw_is_stereo(mc))3401 rval = (reg_val >> mc->rshift) & mask;3402 } else {3403 reg_val = dapm_kcontrol_get_value(kcontrol);3404 val = reg_val & mask;3405 3406 if (snd_soc_volsw_is_stereo(mc))3407 rval = (reg_val >> width) & mask;3408 }3409 snd_soc_dapm_mutex_unlock(dapm);3410 3411 if (invert)3412 ucontrol->value.integer.value[0] = max - val;3413 else3414 ucontrol->value.integer.value[0] = val;3415 3416 if (snd_soc_volsw_is_stereo(mc)) {3417 if (invert)3418 ucontrol->value.integer.value[1] = max - rval;3419 else3420 ucontrol->value.integer.value[1] = rval;3421 }3422 3423 return 0;3424}3425EXPORT_SYMBOL_GPL(snd_soc_dapm_get_volsw);3426 3427/**3428 * snd_soc_dapm_put_volsw - dapm mixer set callback3429 * @kcontrol: mixer control3430 * @ucontrol: control element information3431 *3432 * Callback to set the value of a dapm mixer control.3433 *3434 * Returns 0 for success.3435 */3436int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,3437 struct snd_ctl_elem_value *ucontrol)3438{3439 struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);3440 struct snd_soc_card *card = dapm->card;3441 struct soc_mixer_control *mc =3442 (struct soc_mixer_control *)kcontrol->private_value;3443 int reg = mc->reg;3444 unsigned int shift = mc->shift;3445 int max = mc->max;3446 unsigned int width = fls(max);3447 unsigned int mask = (1 << width) - 1;3448 unsigned int invert = mc->invert;3449 unsigned int val, rval = 0;3450 int connect, rconnect = -1, change, reg_change = 0;3451 struct snd_soc_dapm_update update = {};3452 int ret = 0;3453 3454 val = (ucontrol->value.integer.value[0] & mask);3455 connect = !!val;3456 3457 if (invert)3458 val = max - val;3459 3460 if (snd_soc_volsw_is_stereo(mc)) {3461 rval = (ucontrol->value.integer.value[1] & mask);3462 rconnect = !!rval;3463 if (invert)3464 rval = max - rval;3465 }3466 3467 snd_soc_dapm_mutex_lock(card);3468 3469 /* This assumes field width < (bits in unsigned int / 2) */3470 if (width > sizeof(unsigned int) * 8 / 2)3471 dev_warn(dapm->dev,3472 "ASoC: control %s field width limit exceeded\n",3473 kcontrol->id.name);3474 change = dapm_kcontrol_set_value(kcontrol, val | (rval << width));3475 3476 if (reg != SND_SOC_NOPM) {3477 val = val << shift;3478 rval = rval << mc->rshift;3479 3480 reg_change = soc_dapm_test_bits(dapm, reg, mask << shift, val);3481 3482 if (snd_soc_volsw_is_stereo(mc))3483 reg_change |= soc_dapm_test_bits(dapm, mc->rreg,3484 mask << mc->rshift,3485 rval);3486 }3487 3488 if (change || reg_change) {3489 if (reg_change) {3490 if (snd_soc_volsw_is_stereo(mc)) {3491 update.has_second_set = true;3492 update.reg2 = mc->rreg;3493 update.mask2 = mask << mc->rshift;3494 update.val2 = rval;3495 }3496 update.kcontrol = kcontrol;3497 update.reg = reg;3498 update.mask = mask << shift;3499 update.val = val;3500 card->update = &update;3501 }3502 3503 ret = soc_dapm_mixer_update_power(card, kcontrol, connect,3504 rconnect);3505 3506 card->update = NULL;3507 }3508 3509 snd_soc_dapm_mutex_unlock(card);3510 3511 if (ret > 0)3512 snd_soc_dpcm_runtime_update(card);3513 3514 return change;3515}3516EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw);3517 3518/**3519 * snd_soc_dapm_get_enum_double - dapm enumerated double mixer get callback3520 * @kcontrol: mixer control3521 * @ucontrol: control element information3522 *3523 * Callback to get the value of a dapm enumerated double mixer control.3524 *3525 * Returns 0 for success.3526 */3527int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol,3528 struct snd_ctl_elem_value *ucontrol)3529{3530 struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);3531 struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;3532 unsigned int reg_val, val;3533 3534 snd_soc_dapm_mutex_lock(dapm);3535 if (e->reg != SND_SOC_NOPM && dapm_kcontrol_is_powered(kcontrol)) {3536 reg_val = soc_dapm_read(dapm, e->reg);3537 } else {3538 reg_val = dapm_kcontrol_get_value(kcontrol);3539 }3540 snd_soc_dapm_mutex_unlock(dapm);3541 3542 val = (reg_val >> e->shift_l) & e->mask;3543 ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(e, val);3544 if (e->shift_l != e->shift_r) {3545 val = (reg_val >> e->shift_r) & e->mask;3546 val = snd_soc_enum_val_to_item(e, val);3547 ucontrol->value.enumerated.item[1] = val;3548 }3549 3550 return 0;3551}3552EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_double);3553 3554/**3555 * snd_soc_dapm_put_enum_double - dapm enumerated double mixer set callback3556 * @kcontrol: mixer control3557 * @ucontrol: control element information3558 *3559 * Callback to set the value of a dapm enumerated double mixer control.3560 *3561 * Returns 0 for success.3562 */3563int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,3564 struct snd_ctl_elem_value *ucontrol)3565{3566 struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);3567 struct snd_soc_card *card = dapm->card;3568 struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;3569 unsigned int *item = ucontrol->value.enumerated.item;3570 unsigned int val, change, reg_change = 0;3571 unsigned int mask;3572 struct snd_soc_dapm_update update = {};3573 int ret = 0;3574 3575 if (item[0] >= e->items)3576 return -EINVAL;3577 3578 val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;3579 mask = e->mask << e->shift_l;3580 if (e->shift_l != e->shift_r) {3581 if (item[1] > e->items)3582 return -EINVAL;3583 val |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_r;3584 mask |= e->mask << e->shift_r;3585 }3586 3587 snd_soc_dapm_mutex_lock(card);3588 3589 change = dapm_kcontrol_set_value(kcontrol, val);3590 3591 if (e->reg != SND_SOC_NOPM)3592 reg_change = soc_dapm_test_bits(dapm, e->reg, mask, val);3593 3594 if (change || reg_change) {3595 if (reg_change) {3596 update.kcontrol = kcontrol;3597 update.reg = e->reg;3598 update.mask = mask;3599 update.val = val;3600 card->update = &update;3601 }3602 3603 ret = soc_dapm_mux_update_power(card, kcontrol, item[0], e);3604 3605 card->update = NULL;3606 }3607 3608 snd_soc_dapm_mutex_unlock(card);3609 3610 if (ret > 0)3611 snd_soc_dpcm_runtime_update(card);3612 3613 return change;3614}3615EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double);3616 3617/**3618 * snd_soc_dapm_info_pin_switch - Info for a pin switch3619 *3620 * @kcontrol: mixer control3621 * @uinfo: control element information3622 *3623 * Callback to provide information about a pin switch control.3624 */3625int snd_soc_dapm_info_pin_switch(struct snd_kcontrol *kcontrol,3626 struct snd_ctl_elem_info *uinfo)3627{3628 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;3629 uinfo->count = 1;3630 uinfo->value.integer.min = 0;3631 uinfo->value.integer.max = 1;3632 3633 return 0;3634}3635EXPORT_SYMBOL_GPL(snd_soc_dapm_info_pin_switch);3636 3637/**3638 * snd_soc_dapm_get_pin_switch - Get information for a pin switch3639 *3640 * @kcontrol: mixer control3641 * @ucontrol: Value3642 */3643int snd_soc_dapm_get_pin_switch(struct snd_kcontrol *kcontrol,3644 struct snd_ctl_elem_value *ucontrol)3645{3646 struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);3647 const char *pin = (const char *)kcontrol->private_value;3648 3649 snd_soc_dapm_mutex_lock(card);3650 3651 ucontrol->value.integer.value[0] =3652 snd_soc_dapm_get_pin_status(&card->dapm, pin);3653 3654 snd_soc_dapm_mutex_unlock(card);3655 3656 return 0;3657}3658EXPORT_SYMBOL_GPL(snd_soc_dapm_get_pin_switch);3659 3660/**3661 * snd_soc_dapm_put_pin_switch - Set information for a pin switch3662 *3663 * @kcontrol: mixer control3664 * @ucontrol: Value3665 */3666int snd_soc_dapm_put_pin_switch(struct snd_kcontrol *kcontrol,3667 struct snd_ctl_elem_value *ucontrol)3668{3669 struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);3670 const char *pin = (const char *)kcontrol->private_value;3671 int ret;3672 3673 snd_soc_dapm_mutex_lock(card);3674 ret = __snd_soc_dapm_set_pin(&card->dapm, pin,3675 !!ucontrol->value.integer.value[0]);3676 snd_soc_dapm_mutex_unlock(card);3677 3678 snd_soc_dapm_sync(&card->dapm);3679 return ret;3680}3681EXPORT_SYMBOL_GPL(snd_soc_dapm_put_pin_switch);3682 3683struct snd_soc_dapm_widget *3684snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm,3685 const struct snd_soc_dapm_widget *widget)3686{3687 enum snd_soc_dapm_direction dir;3688 struct snd_soc_dapm_widget *w;3689 int ret = -ENOMEM;3690 3691 w = dapm_cnew_widget(widget, soc_dapm_prefix(dapm));3692 if (!w)3693 goto cnew_failed;3694 3695 switch (w->id) {3696 case snd_soc_dapm_regulator_supply:3697 w->regulator = devm_regulator_get(dapm->dev, widget->name);3698 if (IS_ERR(w->regulator)) {3699 ret = PTR_ERR(w->regulator);3700 goto request_failed;3701 }3702 3703 if (w->on_val & SND_SOC_DAPM_REGULATOR_BYPASS) {3704 ret = regulator_allow_bypass(w->regulator, true);3705 if (ret != 0)3706 dev_warn(dapm->dev,3707 "ASoC: Failed to bypass %s: %d\n",3708 w->name, ret);3709 }3710 break;3711 case snd_soc_dapm_pinctrl:3712 w->pinctrl = devm_pinctrl_get(dapm->dev);3713 if (IS_ERR(w->pinctrl)) {3714 ret = PTR_ERR(w->pinctrl);3715 goto request_failed;3716 }3717 3718 /* set to sleep_state when initializing */3719 dapm_pinctrl_event(w, NULL, SND_SOC_DAPM_POST_PMD);3720 break;3721 case snd_soc_dapm_clock_supply:3722 w->clk = devm_clk_get(dapm->dev, widget->name);3723 if (IS_ERR(w->clk)) {3724 ret = PTR_ERR(w->clk);3725 goto request_failed;3726 }3727 break;3728 default:3729 break;3730 }3731 3732 switch (w->id) {3733 case snd_soc_dapm_mic:3734 w->is_ep = SND_SOC_DAPM_EP_SOURCE;3735 w->power_check = dapm_generic_check_power;3736 break;3737 case snd_soc_dapm_input:3738 if (!dapm->card->fully_routed)3739 w->is_ep = SND_SOC_DAPM_EP_SOURCE;3740 w->power_check = dapm_generic_check_power;3741 break;3742 case snd_soc_dapm_spk:3743 case snd_soc_dapm_hp:3744 w->is_ep = SND_SOC_DAPM_EP_SINK;3745 w->power_check = dapm_generic_check_power;3746 break;3747 case snd_soc_dapm_output:3748 if (!dapm->card->fully_routed)3749 w->is_ep = SND_SOC_DAPM_EP_SINK;3750 w->power_check = dapm_generic_check_power;3751 break;3752 case snd_soc_dapm_vmid:3753 case snd_soc_dapm_siggen:3754 w->is_ep = SND_SOC_DAPM_EP_SOURCE;3755 w->power_check = dapm_always_on_check_power;3756 break;3757 case snd_soc_dapm_sink:3758 w->is_ep = SND_SOC_DAPM_EP_SINK;3759 w->power_check = dapm_always_on_check_power;3760 break;3761 3762 case snd_soc_dapm_mux:3763 case snd_soc_dapm_demux:3764 case snd_soc_dapm_switch:3765 case snd_soc_dapm_mixer:3766 case snd_soc_dapm_mixer_named_ctl:3767 case snd_soc_dapm_adc:3768 case snd_soc_dapm_aif_out:3769 case snd_soc_dapm_dac:3770 case snd_soc_dapm_aif_in:3771 case snd_soc_dapm_pga:3772 case snd_soc_dapm_buffer:3773 case snd_soc_dapm_scheduler:3774 case snd_soc_dapm_effect:3775 case snd_soc_dapm_src:3776 case snd_soc_dapm_asrc:3777 case snd_soc_dapm_encoder:3778 case snd_soc_dapm_decoder:3779 case snd_soc_dapm_out_drv:3780 case snd_soc_dapm_micbias:3781 case snd_soc_dapm_line:3782 case snd_soc_dapm_dai_link:3783 case snd_soc_dapm_dai_out:3784 case snd_soc_dapm_dai_in:3785 w->power_check = dapm_generic_check_power;3786 break;3787 case snd_soc_dapm_supply:3788 case snd_soc_dapm_regulator_supply:3789 case snd_soc_dapm_pinctrl:3790 case snd_soc_dapm_clock_supply:3791 case snd_soc_dapm_kcontrol:3792 w->is_supply = 1;3793 w->power_check = dapm_supply_check_power;3794 break;3795 default:3796 w->power_check = dapm_always_on_check_power;3797 break;3798 }3799 3800 w->dapm = dapm;3801 INIT_LIST_HEAD(&w->list);3802 INIT_LIST_HEAD(&w->dirty);3803 /* see for_each_card_widgets */3804 list_add_tail(&w->list, &dapm->card->widgets);3805 3806 snd_soc_dapm_for_each_direction(dir) {3807 INIT_LIST_HEAD(&w->edges[dir]);3808 w->endpoints[dir] = -1;3809 }3810 3811 /* machine layer sets up unconnected pins and insertions */3812 w->connected = 1;3813 return w;3814 3815request_failed:3816 dev_err_probe(dapm->dev, ret, "ASoC: Failed to request %s\n",3817 w->name);3818 kfree_const(w->name);3819 kfree_const(w->sname);3820 kfree(w);3821cnew_failed:3822 return ERR_PTR(ret);3823}3824 3825/**3826 * snd_soc_dapm_new_control - create new dapm control3827 * @dapm: DAPM context3828 * @widget: widget template3829 *3830 * Creates new DAPM control based upon a template.3831 *3832 * Returns a widget pointer on success or an error pointer on failure3833 */3834struct snd_soc_dapm_widget *3835snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,3836 const struct snd_soc_dapm_widget *widget)3837{3838 struct snd_soc_dapm_widget *w;3839 3840 snd_soc_dapm_mutex_lock(dapm);3841 w = snd_soc_dapm_new_control_unlocked(dapm, widget);3842 snd_soc_dapm_mutex_unlock(dapm);3843 3844 return w;3845}3846EXPORT_SYMBOL_GPL(snd_soc_dapm_new_control);3847 3848/**3849 * snd_soc_dapm_new_controls - create new dapm controls3850 * @dapm: DAPM context3851 * @widget: widget array3852 * @num: number of widgets3853 *3854 * Creates new DAPM controls based upon the templates.3855 *3856 * Returns 0 for success else error.3857 */3858int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm,3859 const struct snd_soc_dapm_widget *widget,3860 unsigned int num)3861{3862 int i;3863 int ret = 0;3864 3865 snd_soc_dapm_mutex_lock_root(dapm);3866 for (i = 0; i < num; i++) {3867 struct snd_soc_dapm_widget *w = snd_soc_dapm_new_control_unlocked(dapm, widget);3868 if (IS_ERR(w)) {3869 ret = PTR_ERR(w);3870 break;3871 }3872 widget++;3873 }3874 snd_soc_dapm_mutex_unlock(dapm);3875 return ret;3876}3877EXPORT_SYMBOL_GPL(snd_soc_dapm_new_controls);3878 3879static int3880snd_soc_dai_link_event_pre_pmu(struct snd_soc_dapm_widget *w,3881 struct snd_pcm_substream *substream)3882{3883 struct snd_soc_dapm_path *path;3884 struct snd_soc_dai *source, *sink;3885 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);3886 const struct snd_soc_pcm_stream *config = NULL;3887 struct snd_pcm_runtime *runtime = NULL;3888 unsigned int fmt;3889 int ret;3890 3891 /*3892 * NOTE3893 *3894 * snd_pcm_hw_params is quite large (608 bytes on arm64) and is3895 * starting to get a bit excessive for allocation on the stack,3896 * especially when you're building with some of the KASAN type3897 * stuff that increases stack usage.3898 * So, we use kzalloc()/kfree() for params in this function.3899 */3900 struct snd_pcm_hw_params *params __free(kfree) = kzalloc(sizeof(*params),3901 GFP_KERNEL);3902 if (!params)3903 return -ENOMEM;3904 3905 runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);3906 if (!runtime)3907 return -ENOMEM;3908 3909 substream->runtime = runtime;3910 3911 substream->stream = SNDRV_PCM_STREAM_CAPTURE;3912 snd_soc_dapm_widget_for_each_source_path(w, path) {3913 source = path->source->priv;3914 3915 ret = snd_soc_dai_startup(source, substream);3916 if (ret < 0)3917 return ret;3918 3919 snd_soc_dai_activate(source, substream->stream);3920 }3921 3922 substream->stream = SNDRV_PCM_STREAM_PLAYBACK;3923 snd_soc_dapm_widget_for_each_sink_path(w, path) {3924 sink = path->sink->priv;3925 3926 ret = snd_soc_dai_startup(sink, substream);3927 if (ret < 0)3928 return ret;3929 3930 snd_soc_dai_activate(sink, substream->stream);3931 }3932 3933 substream->hw_opened = 1;3934 3935 /*3936 * Note: getting the config after .startup() gives a chance to3937 * either party on the link to alter the configuration if3938 * necessary3939 */3940 config = rtd->dai_link->c2c_params + rtd->c2c_params_select;3941 if (!config) {3942 dev_err(w->dapm->dev, "ASoC: link config missing\n");3943 return -EINVAL;3944 }3945 3946 /* Be a little careful as we don't want to overflow the mask array */3947 if (!config->formats) {3948 dev_warn(w->dapm->dev, "ASoC: Invalid format was specified\n");3949 3950 return -EINVAL;3951 }3952 3953 fmt = ffs(config->formats) - 1;3954 3955 snd_mask_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), fmt);3956 hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE)->min =3957 config->rate_min;3958 hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE)->max =3959 config->rate_max;3960 hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS)->min3961 = config->channels_min;3962 hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS)->max3963 = config->channels_max;3964 3965 substream->stream = SNDRV_PCM_STREAM_CAPTURE;3966 snd_soc_dapm_widget_for_each_source_path(w, path) {3967 source = path->source->priv;3968 3969 ret = snd_soc_dai_hw_params(source, substream, params);3970 if (ret < 0)3971 return ret;3972 3973 dapm_update_dai_unlocked(substream, params, source);3974 }3975 3976 substream->stream = SNDRV_PCM_STREAM_PLAYBACK;3977 snd_soc_dapm_widget_for_each_sink_path(w, path) {3978 sink = path->sink->priv;3979 3980 ret = snd_soc_dai_hw_params(sink, substream, params);3981 if (ret < 0)3982 return ret;3983 3984 dapm_update_dai_unlocked(substream, params, sink);3985 }3986 3987 runtime->format = params_format(params);3988 runtime->subformat = params_subformat(params);3989 runtime->channels = params_channels(params);3990 runtime->rate = params_rate(params);3991 3992 return 0;3993}3994 3995static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,3996 struct snd_kcontrol *kcontrol, int event)3997{3998 struct snd_soc_dapm_path *path;3999 struct snd_soc_dai *source, *sink;4000 struct snd_pcm_substream *substream = w->priv;4001 int ret = 0, saved_stream = substream->stream;4002 4003 if (WARN_ON(list_empty(&w->edges[SND_SOC_DAPM_DIR_OUT]) ||4004 list_empty(&w->edges[SND_SOC_DAPM_DIR_IN])))4005 return -EINVAL;4006 4007 switch (event) {4008 case SND_SOC_DAPM_PRE_PMU:4009 ret = snd_soc_dai_link_event_pre_pmu(w, substream);4010 if (ret < 0)4011 goto out;4012 4013 break;4014 4015 case SND_SOC_DAPM_POST_PMU:4016 snd_soc_dapm_widget_for_each_sink_path(w, path) {4017 sink = path->sink->priv;4018 4019 snd_soc_dai_digital_mute(sink, 0, SNDRV_PCM_STREAM_PLAYBACK);4020 ret = 0;4021 }4022 break;4023 4024 case SND_SOC_DAPM_PRE_PMD:4025 snd_soc_dapm_widget_for_each_sink_path(w, path) {4026 sink = path->sink->priv;4027 4028 snd_soc_dai_digital_mute(sink, 1, SNDRV_PCM_STREAM_PLAYBACK);4029 ret = 0;4030 }4031 4032 substream->stream = SNDRV_PCM_STREAM_CAPTURE;4033 snd_soc_dapm_widget_for_each_source_path(w, path) {4034 source = path->source->priv;4035 snd_soc_dai_hw_free(source, substream, 0);4036 }4037 4038 substream->stream = SNDRV_PCM_STREAM_PLAYBACK;4039 snd_soc_dapm_widget_for_each_sink_path(w, path) {4040 sink = path->sink->priv;4041 snd_soc_dai_hw_free(sink, substream, 0);4042 }4043 4044 substream->stream = SNDRV_PCM_STREAM_CAPTURE;4045 snd_soc_dapm_widget_for_each_source_path(w, path) {4046 source = path->source->priv;4047 snd_soc_dai_deactivate(source, substream->stream);4048 snd_soc_dai_shutdown(source, substream, 0);4049 }4050 4051 substream->stream = SNDRV_PCM_STREAM_PLAYBACK;4052 snd_soc_dapm_widget_for_each_sink_path(w, path) {4053 sink = path->sink->priv;4054 snd_soc_dai_deactivate(sink, substream->stream);4055 snd_soc_dai_shutdown(sink, substream, 0);4056 }4057 break;4058 4059 case SND_SOC_DAPM_POST_PMD:4060 kfree(substream->runtime);4061 substream->runtime = NULL;4062 break;4063 4064 default:4065 WARN(1, "Unknown event %d\n", event);4066 ret = -EINVAL;4067 }4068 4069out:4070 /* Restore the substream direction */4071 substream->stream = saved_stream;4072 return ret;4073}4074 4075static int snd_soc_dapm_dai_link_get(struct snd_kcontrol *kcontrol,4076 struct snd_ctl_elem_value *ucontrol)4077{4078 struct snd_soc_dapm_widget *w = snd_kcontrol_chip(kcontrol);4079 struct snd_soc_pcm_runtime *rtd = w->priv;4080 4081 ucontrol->value.enumerated.item[0] = rtd->c2c_params_select;4082 4083 return 0;4084}4085 4086static int snd_soc_dapm_dai_link_put(struct snd_kcontrol *kcontrol,4087 struct snd_ctl_elem_value *ucontrol)4088{4089 struct snd_soc_dapm_widget *w = snd_kcontrol_chip(kcontrol);4090 struct snd_soc_pcm_runtime *rtd = w->priv;4091 4092 /* Can't change the config when widget is already powered */4093 if (w->power)4094 return -EBUSY;4095 4096 if (ucontrol->value.enumerated.item[0] == rtd->c2c_params_select)4097 return 0;4098 4099 if (ucontrol->value.enumerated.item[0] >= rtd->dai_link->num_c2c_params)4100 return -EINVAL;4101 4102 rtd->c2c_params_select = ucontrol->value.enumerated.item[0];4103 4104 return 1;4105}4106 4107static void4108snd_soc_dapm_free_kcontrol(struct snd_soc_card *card,4109 unsigned long *private_value,4110 int num_c2c_params,4111 const char **w_param_text)4112{4113 int count;4114 4115 devm_kfree(card->dev, (void *)*private_value);4116 4117 if (!w_param_text)4118 return;4119 4120 for (count = 0 ; count < num_c2c_params; count++)4121 devm_kfree(card->dev, (void *)w_param_text[count]);4122 devm_kfree(card->dev, w_param_text);4123}4124 4125static struct snd_kcontrol_new *4126snd_soc_dapm_alloc_kcontrol(struct snd_soc_card *card,4127 char *link_name,4128 const struct snd_soc_pcm_stream *c2c_params,4129 int num_c2c_params, const char **w_param_text,4130 unsigned long *private_value)4131{4132 struct soc_enum w_param_enum[] = {4133 SOC_ENUM_SINGLE(0, 0, 0, NULL),4134 };4135 struct snd_kcontrol_new kcontrol_dai_link[] = {4136 SOC_ENUM_EXT(NULL, w_param_enum[0],4137 snd_soc_dapm_dai_link_get,4138 snd_soc_dapm_dai_link_put),4139 };4140 struct snd_kcontrol_new *kcontrol_news;4141 const struct snd_soc_pcm_stream *config = c2c_params;4142 int count;4143 4144 for (count = 0 ; count < num_c2c_params; count++) {4145 if (!config->stream_name) {4146 dev_warn(card->dapm.dev,4147 "ASoC: anonymous config %d for dai link %s\n",4148 count, link_name);4149 w_param_text[count] =4150 devm_kasprintf(card->dev, GFP_KERNEL,4151 "Anonymous Configuration %d",4152 count);4153 } else {4154 w_param_text[count] = devm_kmemdup(card->dev,4155 config->stream_name,4156 strlen(config->stream_name) + 1,4157 GFP_KERNEL);4158 }4159 if (!w_param_text[count])4160 goto outfree_w_param;4161 config++;4162 }4163 4164 w_param_enum[0].items = num_c2c_params;4165 w_param_enum[0].texts = w_param_text;4166 4167 *private_value =4168 (unsigned long) devm_kmemdup(card->dev,4169 (void *)(kcontrol_dai_link[0].private_value),4170 sizeof(struct soc_enum), GFP_KERNEL);4171 if (!*private_value) {4172 dev_err(card->dev, "ASoC: Failed to create control for %s widget\n",4173 link_name);4174 goto outfree_w_param;4175 }4176 kcontrol_dai_link[0].private_value = *private_value;4177 /* duplicate kcontrol_dai_link on heap so that memory persists */4178 kcontrol_news = devm_kmemdup(card->dev, &kcontrol_dai_link[0],4179 sizeof(struct snd_kcontrol_new),4180 GFP_KERNEL);4181 if (!kcontrol_news) {4182 dev_err(card->dev, "ASoC: Failed to create control for %s widget\n",4183 link_name);4184 goto outfree_w_param;4185 }4186 return kcontrol_news;4187 4188outfree_w_param:4189 snd_soc_dapm_free_kcontrol(card, private_value, num_c2c_params, w_param_text);4190 return NULL;4191}4192 4193static struct snd_soc_dapm_widget *4194snd_soc_dapm_new_dai(struct snd_soc_card *card,4195 struct snd_pcm_substream *substream,4196 char *id)4197{4198 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);4199 struct snd_soc_dapm_widget template;4200 struct snd_soc_dapm_widget *w;4201 const struct snd_kcontrol_new *kcontrol_news;4202 int num_kcontrols;4203 const char **w_param_text;4204 unsigned long private_value = 0;4205 char *link_name;4206 int ret = -ENOMEM;4207 4208 link_name = devm_kasprintf(card->dev, GFP_KERNEL, "%s-%s",4209 rtd->dai_link->name, id);4210 if (!link_name)4211 goto name_fail;4212 4213 /* allocate memory for control, only in case of multiple configs */4214 w_param_text = NULL;4215 kcontrol_news = NULL;4216 num_kcontrols = 0;4217 if (rtd->dai_link->num_c2c_params > 1) {4218 w_param_text = devm_kcalloc(card->dev,4219 rtd->dai_link->num_c2c_params,4220 sizeof(char *), GFP_KERNEL);4221 if (!w_param_text)4222 goto param_fail;4223 4224 num_kcontrols = 1;4225 kcontrol_news = snd_soc_dapm_alloc_kcontrol(card, link_name,4226 rtd->dai_link->c2c_params,4227 rtd->dai_link->num_c2c_params,4228 w_param_text, &private_value);4229 if (!kcontrol_news)4230 goto param_fail;4231 }4232 4233 memset(&template, 0, sizeof(template));4234 template.reg = SND_SOC_NOPM;4235 template.id = snd_soc_dapm_dai_link;4236 template.name = link_name;4237 template.event = snd_soc_dai_link_event;4238 template.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |4239 SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD;4240 template.kcontrol_news = kcontrol_news;4241 template.num_kcontrols = num_kcontrols;4242 4243 dev_dbg(card->dev, "ASoC: adding %s widget\n", link_name);4244 4245 w = snd_soc_dapm_new_control_unlocked(&card->dapm, &template);4246 if (IS_ERR(w)) {4247 ret = PTR_ERR(w);4248 goto outfree_kcontrol_news;4249 }4250 4251 w->priv = substream;4252 4253 return w;4254 4255outfree_kcontrol_news:4256 devm_kfree(card->dev, (void *)template.kcontrol_news);4257 snd_soc_dapm_free_kcontrol(card, &private_value,4258 rtd->dai_link->num_c2c_params, w_param_text);4259param_fail:4260 devm_kfree(card->dev, link_name);4261name_fail:4262 dev_err(rtd->dev, "ASoC: Failed to create %s-%s widget: %d\n",4263 rtd->dai_link->name, id, ret);4264 return ERR_PTR(ret);4265}4266 4267/**4268 * snd_soc_dapm_new_dai_widgets - Create new DAPM widgets4269 * @dapm: DAPM context4270 * @dai: parent DAI4271 *4272 * Returns 0 on success, error code otherwise.4273 */4274int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,4275 struct snd_soc_dai *dai)4276{4277 struct snd_soc_dapm_widget template;4278 struct snd_soc_dapm_widget *w;4279 4280 WARN_ON(dapm->dev != dai->dev);4281 4282 memset(&template, 0, sizeof(template));4283 template.reg = SND_SOC_NOPM;4284 4285 if (dai->driver->playback.stream_name) {4286 template.id = snd_soc_dapm_dai_in;4287 template.name = dai->driver->playback.stream_name;4288 template.sname = dai->driver->playback.stream_name;4289 4290 dev_dbg(dai->dev, "ASoC: adding %s widget\n",4291 template.name);4292 4293 w = snd_soc_dapm_new_control_unlocked(dapm, &template);4294 if (IS_ERR(w))4295 return PTR_ERR(w);4296 4297 w->priv = dai;4298 snd_soc_dai_set_widget_playback(dai, w);4299 }4300 4301 if (dai->driver->capture.stream_name) {4302 template.id = snd_soc_dapm_dai_out;4303 template.name = dai->driver->capture.stream_name;4304 template.sname = dai->driver->capture.stream_name;4305 4306 dev_dbg(dai->dev, "ASoC: adding %s widget\n",4307 template.name);4308 4309 w = snd_soc_dapm_new_control_unlocked(dapm, &template);4310 if (IS_ERR(w))4311 return PTR_ERR(w);4312 4313 w->priv = dai;4314 snd_soc_dai_set_widget_capture(dai, w);4315 }4316 4317 return 0;4318}4319EXPORT_SYMBOL_GPL(snd_soc_dapm_new_dai_widgets);4320 4321int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card)4322{4323 struct snd_soc_dapm_widget *dai_w, *w;4324 struct snd_soc_dapm_widget *src, *sink;4325 struct snd_soc_dai *dai;4326 4327 /* For each DAI widget... */4328 for_each_card_widgets(card, dai_w) {4329 switch (dai_w->id) {4330 case snd_soc_dapm_dai_in:4331 case snd_soc_dapm_dai_out:4332 break;4333 default:4334 continue;4335 }4336 4337 /* let users know there is no DAI to link */4338 if (!dai_w->priv) {4339 dev_dbg(card->dev, "dai widget %s has no DAI\n",4340 dai_w->name);4341 continue;4342 }4343 4344 dai = dai_w->priv;4345 4346 /* ...find all widgets with the same stream and link them */4347 for_each_card_widgets(card, w) {4348 if (w->dapm != dai_w->dapm)4349 continue;4350 4351 switch (w->id) {4352 case snd_soc_dapm_dai_in:4353 case snd_soc_dapm_dai_out:4354 continue;4355 default:4356 break;4357 }4358 4359 if (!w->sname || !strstr(w->sname, dai_w->sname))4360 continue;4361 4362 if (dai_w->id == snd_soc_dapm_dai_in) {4363 src = dai_w;4364 sink = w;4365 } else {4366 src = w;4367 sink = dai_w;4368 }4369 dev_dbg(dai->dev, "%s -> %s\n", src->name, sink->name);4370 snd_soc_dapm_add_path(w->dapm, src, sink, NULL, NULL);4371 }4372 }4373 4374 return 0;4375}4376 4377static void dapm_connect_dai_routes(struct snd_soc_dapm_context *dapm,4378 struct snd_soc_dai *src_dai,4379 struct snd_soc_dapm_widget *src,4380 struct snd_soc_dapm_widget *dai,4381 struct snd_soc_dai *sink_dai,4382 struct snd_soc_dapm_widget *sink)4383{4384 dev_dbg(dapm->dev, "connected DAI link %s:%s -> %s:%s\n",4385 src_dai->component->name, src->name,4386 sink_dai->component->name, sink->name);4387 4388 if (dai) {4389 snd_soc_dapm_add_path(dapm, src, dai, NULL, NULL);4390 src = dai;4391 }4392 4393 snd_soc_dapm_add_path(dapm, src, sink, NULL, NULL);4394}4395 4396static void dapm_connect_dai_pair(struct snd_soc_card *card,4397 struct snd_soc_pcm_runtime *rtd,4398 struct snd_soc_dai *codec_dai,4399 struct snd_soc_dai *cpu_dai)4400{4401 struct snd_soc_dai_link *dai_link = rtd->dai_link;4402 struct snd_soc_dapm_widget *codec, *cpu;4403 struct snd_soc_dai *src_dai[] = { cpu_dai, codec_dai };4404 struct snd_soc_dai *sink_dai[] = { codec_dai, cpu_dai };4405 struct snd_soc_dapm_widget **src[] = { &cpu, &codec };4406 struct snd_soc_dapm_widget **sink[] = { &codec, &cpu };4407 char *widget_name[] = { "playback", "capture" };4408 int stream;4409 4410 for_each_pcm_streams(stream) {4411 int stream_cpu, stream_codec;4412 4413 stream_cpu = snd_soc_get_stream_cpu(dai_link, stream);4414 stream_codec = stream;4415 4416 /* connect BE DAI playback if widgets are valid */4417 cpu = snd_soc_dai_get_widget(cpu_dai, stream_cpu);4418 codec = snd_soc_dai_get_widget(codec_dai, stream_codec);4419 4420 if (!cpu || !codec)4421 continue;4422 4423 /* special handling for [Codec2Codec] */4424 if (dai_link->c2c_params && !rtd->c2c_widget[stream]) {4425 struct snd_pcm_substream *substream = rtd->pcm->streams[stream].substream;4426 struct snd_soc_dapm_widget *dai = snd_soc_dapm_new_dai(card, substream,4427 widget_name[stream]);4428 4429 if (IS_ERR(dai))4430 continue;4431 4432 rtd->c2c_widget[stream] = dai;4433 }4434 4435 dapm_connect_dai_routes(&card->dapm, src_dai[stream], *src[stream],4436 rtd->c2c_widget[stream],4437 sink_dai[stream], *sink[stream]);4438 }4439}4440 4441static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream,4442 int event)4443{4444 struct snd_soc_dapm_widget *w;4445 4446 w = snd_soc_dai_get_widget(dai, stream);4447 4448 if (w) {4449 unsigned int ep;4450 4451 dapm_mark_dirty(w, "stream event");4452 4453 if (w->id == snd_soc_dapm_dai_in) {4454 ep = SND_SOC_DAPM_EP_SOURCE;4455 dapm_widget_invalidate_input_paths(w);4456 } else {4457 ep = SND_SOC_DAPM_EP_SINK;4458 dapm_widget_invalidate_output_paths(w);4459 }4460 4461 switch (event) {4462 case SND_SOC_DAPM_STREAM_START:4463 w->active = 1;4464 w->is_ep = ep;4465 break;4466 case SND_SOC_DAPM_STREAM_STOP:4467 w->active = 0;4468 w->is_ep = 0;4469 break;4470 case SND_SOC_DAPM_STREAM_SUSPEND:4471 case SND_SOC_DAPM_STREAM_RESUME:4472 case SND_SOC_DAPM_STREAM_PAUSE_PUSH:4473 case SND_SOC_DAPM_STREAM_PAUSE_RELEASE:4474 break;4475 }4476 }4477}4478 4479void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card)4480{4481 struct snd_soc_pcm_runtime *rtd;4482 struct snd_soc_dai *cpu_dai;4483 struct snd_soc_dai *codec_dai;4484 4485 /* for each BE DAI link... */4486 for_each_card_rtds(card, rtd) {4487 struct snd_soc_dai_link_ch_map *ch_maps;4488 int i;4489 4490 /*4491 * dynamic FE links have no fixed DAI mapping.4492 * CODEC<->CODEC links have no direct connection.4493 */4494 if (rtd->dai_link->dynamic)4495 continue;4496 4497 /*4498 * see4499 * soc.h :: [dai_link->ch_maps Image sample]4500 */4501 for_each_rtd_ch_maps(rtd, i, ch_maps) {4502 cpu_dai = snd_soc_rtd_to_cpu(rtd, ch_maps->cpu);4503 codec_dai = snd_soc_rtd_to_codec(rtd, ch_maps->codec);4504 4505 dapm_connect_dai_pair(card, rtd, codec_dai, cpu_dai);4506 }4507 }4508}4509 4510static void soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream,4511 int event)4512{4513 struct snd_soc_dai *dai;4514 int i;4515 4516 for_each_rtd_dais(rtd, i, dai)4517 soc_dapm_dai_stream_event(dai, stream, event);4518 4519 dapm_power_widgets(rtd->card, event);4520}4521 4522/**4523 * snd_soc_dapm_stream_event - send a stream event to the dapm core4524 * @rtd: PCM runtime data4525 * @stream: stream name4526 * @event: stream event4527 *4528 * Sends a stream event to the dapm core. The core then makes any4529 * necessary widget power changes.4530 *4531 * Returns 0 for success else error.4532 */4533void snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream,4534 int event)4535{4536 struct snd_soc_card *card = rtd->card;4537 4538 snd_soc_dapm_mutex_lock(card);4539 soc_dapm_stream_event(rtd, stream, event);4540 snd_soc_dapm_mutex_unlock(card);4541}4542 4543void snd_soc_dapm_stream_stop(struct snd_soc_pcm_runtime *rtd, int stream)4544{4545 if (stream == SNDRV_PCM_STREAM_PLAYBACK) {4546 if (snd_soc_runtime_ignore_pmdown_time(rtd)) {4547 /* powered down playback stream now */4548 snd_soc_dapm_stream_event(rtd,4549 SNDRV_PCM_STREAM_PLAYBACK,4550 SND_SOC_DAPM_STREAM_STOP);4551 } else {4552 /* start delayed pop wq here for playback streams */4553 rtd->pop_wait = 1;4554 queue_delayed_work(system_power_efficient_wq,4555 &rtd->delayed_work,4556 msecs_to_jiffies(rtd->pmdown_time));4557 }4558 } else {4559 /* capture streams can be powered down now */4560 snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_CAPTURE,4561 SND_SOC_DAPM_STREAM_STOP);4562 }4563}4564EXPORT_SYMBOL_GPL(snd_soc_dapm_stream_stop);4565 4566/**4567 * snd_soc_dapm_enable_pin_unlocked - enable pin.4568 * @dapm: DAPM context4569 * @pin: pin name4570 *4571 * Enables input/output pin and its parents or children widgets iff there is4572 * a valid audio route and active audio stream.4573 *4574 * Requires external locking.4575 *4576 * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to4577 * do any widget power switching.4578 */4579int snd_soc_dapm_enable_pin_unlocked(struct snd_soc_dapm_context *dapm,4580 const char *pin)4581{4582 return snd_soc_dapm_set_pin(dapm, pin, 1);4583}4584EXPORT_SYMBOL_GPL(snd_soc_dapm_enable_pin_unlocked);4585 4586/**4587 * snd_soc_dapm_enable_pin - enable pin.4588 * @dapm: DAPM context4589 * @pin: pin name4590 *4591 * Enables input/output pin and its parents or children widgets iff there is4592 * a valid audio route and active audio stream.4593 *4594 * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to4595 * do any widget power switching.4596 */4597int snd_soc_dapm_enable_pin(struct snd_soc_dapm_context *dapm, const char *pin)4598{4599 int ret;4600 4601 snd_soc_dapm_mutex_lock(dapm);4602 4603 ret = snd_soc_dapm_set_pin(dapm, pin, 1);4604 4605 snd_soc_dapm_mutex_unlock(dapm);4606 4607 return ret;4608}4609EXPORT_SYMBOL_GPL(snd_soc_dapm_enable_pin);4610 4611/**4612 * snd_soc_dapm_force_enable_pin_unlocked - force a pin to be enabled4613 * @dapm: DAPM context4614 * @pin: pin name4615 *4616 * Enables input/output pin regardless of any other state. This is4617 * intended for use with microphone bias supplies used in microphone4618 * jack detection.4619 *4620 * Requires external locking.4621 *4622 * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to4623 * do any widget power switching.4624 */4625int snd_soc_dapm_force_enable_pin_unlocked(struct snd_soc_dapm_context *dapm,4626 const char *pin)4627{4628 struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, true);4629 4630 if (!w) {4631 dev_err(dapm->dev, "ASoC: unknown pin %s\n", pin);4632 return -EINVAL;4633 }4634 4635 dev_dbg(w->dapm->dev, "ASoC: force enable pin %s\n", pin);4636 if (!w->connected) {4637 /*4638 * w->force does not affect the number of input or output paths,4639 * so we only have to recheck if w->connected is changed4640 */4641 dapm_widget_invalidate_input_paths(w);4642 dapm_widget_invalidate_output_paths(w);4643 w->connected = 1;4644 }4645 w->force = 1;4646 dapm_mark_dirty(w, "force enable");4647 4648 return 0;4649}4650EXPORT_SYMBOL_GPL(snd_soc_dapm_force_enable_pin_unlocked);4651 4652/**4653 * snd_soc_dapm_force_enable_pin - force a pin to be enabled4654 * @dapm: DAPM context4655 * @pin: pin name4656 *4657 * Enables input/output pin regardless of any other state. This is4658 * intended for use with microphone bias supplies used in microphone4659 * jack detection.4660 *4661 * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to4662 * do any widget power switching.4663 */4664int snd_soc_dapm_force_enable_pin(struct snd_soc_dapm_context *dapm,4665 const char *pin)4666{4667 int ret;4668 4669 snd_soc_dapm_mutex_lock(dapm);4670 4671 ret = snd_soc_dapm_force_enable_pin_unlocked(dapm, pin);4672 4673 snd_soc_dapm_mutex_unlock(dapm);4674 4675 return ret;4676}4677EXPORT_SYMBOL_GPL(snd_soc_dapm_force_enable_pin);4678 4679/**4680 * snd_soc_dapm_disable_pin_unlocked - disable pin.4681 * @dapm: DAPM context4682 * @pin: pin name4683 *4684 * Disables input/output pin and its parents or children widgets.4685 *4686 * Requires external locking.4687 *4688 * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to4689 * do any widget power switching.4690 */4691int snd_soc_dapm_disable_pin_unlocked(struct snd_soc_dapm_context *dapm,4692 const char *pin)4693{4694 return snd_soc_dapm_set_pin(dapm, pin, 0);4695}4696EXPORT_SYMBOL_GPL(snd_soc_dapm_disable_pin_unlocked);4697 4698/**4699 * snd_soc_dapm_disable_pin - disable pin.4700 * @dapm: DAPM context4701 * @pin: pin name4702 *4703 * Disables input/output pin and its parents or children widgets.4704 *4705 * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to4706 * do any widget power switching.4707 */4708int snd_soc_dapm_disable_pin(struct snd_soc_dapm_context *dapm,4709 const char *pin)4710{4711 int ret;4712 4713 snd_soc_dapm_mutex_lock(dapm);4714 4715 ret = snd_soc_dapm_set_pin(dapm, pin, 0);4716 4717 snd_soc_dapm_mutex_unlock(dapm);4718 4719 return ret;4720}4721EXPORT_SYMBOL_GPL(snd_soc_dapm_disable_pin);4722 4723/**4724 * snd_soc_dapm_nc_pin_unlocked - permanently disable pin.4725 * @dapm: DAPM context4726 * @pin: pin name4727 *4728 * Marks the specified pin as being not connected, disabling it along4729 * any parent or child widgets. At present this is identical to4730 * snd_soc_dapm_disable_pin() but in future it will be extended to do4731 * additional things such as disabling controls which only affect4732 * paths through the pin.4733 *4734 * Requires external locking.4735 *4736 * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to4737 * do any widget power switching.4738 */4739int snd_soc_dapm_nc_pin_unlocked(struct snd_soc_dapm_context *dapm,4740 const char *pin)4741{4742 return snd_soc_dapm_set_pin(dapm, pin, 0);4743}4744EXPORT_SYMBOL_GPL(snd_soc_dapm_nc_pin_unlocked);4745 4746/**4747 * snd_soc_dapm_nc_pin - permanently disable pin.4748 * @dapm: DAPM context4749 * @pin: pin name4750 *4751 * Marks the specified pin as being not connected, disabling it along4752 * any parent or child widgets. At present this is identical to4753 * snd_soc_dapm_disable_pin() but in future it will be extended to do4754 * additional things such as disabling controls which only affect4755 * paths through the pin.4756 *4757 * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to4758 * do any widget power switching.4759 */4760int snd_soc_dapm_nc_pin(struct snd_soc_dapm_context *dapm, const char *pin)4761{4762 int ret;4763 4764 snd_soc_dapm_mutex_lock(dapm);4765 4766 ret = snd_soc_dapm_set_pin(dapm, pin, 0);4767 4768 snd_soc_dapm_mutex_unlock(dapm);4769 4770 return ret;4771}4772EXPORT_SYMBOL_GPL(snd_soc_dapm_nc_pin);4773 4774/**4775 * snd_soc_dapm_get_pin_status - get audio pin status4776 * @dapm: DAPM context4777 * @pin: audio signal pin endpoint (or start point)4778 *4779 * Get audio pin status - connected or disconnected.4780 *4781 * Returns 1 for connected otherwise 0.4782 */4783int snd_soc_dapm_get_pin_status(struct snd_soc_dapm_context *dapm,4784 const char *pin)4785{4786 struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, true);4787 4788 if (w)4789 return w->connected;4790 4791 return 0;4792}4793EXPORT_SYMBOL_GPL(snd_soc_dapm_get_pin_status);4794 4795/**4796 * snd_soc_dapm_ignore_suspend - ignore suspend status for DAPM endpoint4797 * @dapm: DAPM context4798 * @pin: audio signal pin endpoint (or start point)4799 *4800 * Mark the given endpoint or pin as ignoring suspend. When the4801 * system is disabled a path between two endpoints flagged as ignoring4802 * suspend will not be disabled. The path must already be enabled via4803 * normal means at suspend time, it will not be turned on if it was not4804 * already enabled.4805 */4806int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm,4807 const char *pin)4808{4809 struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, false);4810 4811 if (!w) {4812 dev_err(dapm->dev, "ASoC: unknown pin %s\n", pin);4813 return -EINVAL;4814 }4815 4816 w->ignore_suspend = 1;4817 4818 return 0;4819}4820EXPORT_SYMBOL_GPL(snd_soc_dapm_ignore_suspend);4821 4822/**4823 * snd_soc_dapm_free - free dapm resources4824 * @dapm: DAPM context4825 *4826 * Free all dapm widgets and resources.4827 */4828void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm)4829{4830 dapm_debugfs_cleanup(dapm);4831 dapm_free_widgets(dapm);4832 list_del(&dapm->list);4833}4834EXPORT_SYMBOL_GPL(snd_soc_dapm_free);4835 4836void snd_soc_dapm_init(struct snd_soc_dapm_context *dapm,4837 struct snd_soc_card *card,4838 struct snd_soc_component *component)4839{4840 dapm->card = card;4841 dapm->component = component;4842 dapm->bias_level = SND_SOC_BIAS_OFF;4843 4844 if (component) {4845 dapm->dev = component->dev;4846 dapm->idle_bias_off = !component->driver->idle_bias_on;4847 dapm->suspend_bias_off = component->driver->suspend_bias_off;4848 } else {4849 dapm->dev = card->dev;4850 }4851 4852 INIT_LIST_HEAD(&dapm->list);4853 /* see for_each_card_dapms */4854 list_add(&dapm->list, &card->dapm_list);4855}4856EXPORT_SYMBOL_GPL(snd_soc_dapm_init);4857 4858static void soc_dapm_shutdown_dapm(struct snd_soc_dapm_context *dapm)4859{4860 struct snd_soc_card *card = dapm->card;4861 struct snd_soc_dapm_widget *w;4862 LIST_HEAD(down_list);4863 int powerdown = 0;4864 4865 snd_soc_dapm_mutex_lock_root(card);4866 4867 for_each_card_widgets(dapm->card, w) {4868 if (w->dapm != dapm)4869 continue;4870 if (w->power) {4871 dapm_seq_insert(w, &down_list, false);4872 w->new_power = 0;4873 powerdown = 1;4874 }4875 }4876 4877 /* If there were no widgets to power down we're already in4878 * standby.4879 */4880 if (powerdown) {4881 if (dapm->bias_level == SND_SOC_BIAS_ON)4882 snd_soc_dapm_set_bias_level(dapm,4883 SND_SOC_BIAS_PREPARE);4884 dapm_seq_run(card, &down_list, 0, false);4885 if (dapm->bias_level == SND_SOC_BIAS_PREPARE)4886 snd_soc_dapm_set_bias_level(dapm,4887 SND_SOC_BIAS_STANDBY);4888 }4889 4890 snd_soc_dapm_mutex_unlock(card);4891}4892 4893/*4894 * snd_soc_dapm_shutdown - callback for system shutdown4895 */4896void snd_soc_dapm_shutdown(struct snd_soc_card *card)4897{4898 struct snd_soc_dapm_context *dapm;4899 4900 for_each_card_dapms(card, dapm) {4901 if (dapm != &card->dapm) {4902 soc_dapm_shutdown_dapm(dapm);4903 if (dapm->bias_level == SND_SOC_BIAS_STANDBY)4904 snd_soc_dapm_set_bias_level(dapm,4905 SND_SOC_BIAS_OFF);4906 }4907 }4908 4909 soc_dapm_shutdown_dapm(&card->dapm);4910 if (card->dapm.bias_level == SND_SOC_BIAS_STANDBY)4911 snd_soc_dapm_set_bias_level(&card->dapm,4912 SND_SOC_BIAS_OFF);4913}4914 4915/* Module information */4916MODULE_AUTHOR("Liam Girdwood, lrg@slimlogic.co.uk");4917MODULE_DESCRIPTION("Dynamic Audio Power Management core for ALSA SoC");4918MODULE_LICENSE("GPL");4919