162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// soc-dapm.c -- ALSA SoC Dynamic Audio Power Management 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// Copyright 2005 Wolfson Microelectronics PLC. 662306a36Sopenharmony_ci// Author: Liam Girdwood <lrg@slimlogic.co.uk> 762306a36Sopenharmony_ci// 862306a36Sopenharmony_ci// Features: 962306a36Sopenharmony_ci// o Changes power status of internal codec blocks depending on the 1062306a36Sopenharmony_ci// dynamic configuration of codec internal audio paths and active 1162306a36Sopenharmony_ci// DACs/ADCs. 1262306a36Sopenharmony_ci// o Platform power domain - can support external components i.e. amps and 1362306a36Sopenharmony_ci// mic/headphone insertion events. 1462306a36Sopenharmony_ci// o Automatic Mic Bias support 1562306a36Sopenharmony_ci// o Jack insertion power event initiation - e.g. hp insertion will enable 1662306a36Sopenharmony_ci// sinks, dacs, etc 1762306a36Sopenharmony_ci// o Delayed power down of audio subsystem to reduce pops between a quick 1862306a36Sopenharmony_ci// device reopen. 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <linux/module.h> 2162306a36Sopenharmony_ci#include <linux/init.h> 2262306a36Sopenharmony_ci#include <linux/async.h> 2362306a36Sopenharmony_ci#include <linux/delay.h> 2462306a36Sopenharmony_ci#include <linux/pm.h> 2562306a36Sopenharmony_ci#include <linux/bitops.h> 2662306a36Sopenharmony_ci#include <linux/platform_device.h> 2762306a36Sopenharmony_ci#include <linux/jiffies.h> 2862306a36Sopenharmony_ci#include <linux/debugfs.h> 2962306a36Sopenharmony_ci#include <linux/pm_runtime.h> 3062306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 3162306a36Sopenharmony_ci#include <linux/pinctrl/consumer.h> 3262306a36Sopenharmony_ci#include <linux/clk.h> 3362306a36Sopenharmony_ci#include <linux/slab.h> 3462306a36Sopenharmony_ci#include <sound/core.h> 3562306a36Sopenharmony_ci#include <sound/pcm.h> 3662306a36Sopenharmony_ci#include <sound/pcm_params.h> 3762306a36Sopenharmony_ci#include <sound/soc.h> 3862306a36Sopenharmony_ci#include <sound/initval.h> 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#include <trace/events/asoc.h> 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define DAPM_UPDATE_STAT(widget, val) widget->dapm->card->dapm_stats.val++; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define SND_SOC_DAPM_DIR_REVERSE(x) ((x == SND_SOC_DAPM_DIR_IN) ? \ 4562306a36Sopenharmony_ci SND_SOC_DAPM_DIR_OUT : SND_SOC_DAPM_DIR_IN) 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define snd_soc_dapm_for_each_direction(dir) \ 4862306a36Sopenharmony_ci for ((dir) = SND_SOC_DAPM_DIR_IN; (dir) <= SND_SOC_DAPM_DIR_OUT; \ 4962306a36Sopenharmony_ci (dir)++) 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, 5262306a36Sopenharmony_ci struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink, 5362306a36Sopenharmony_ci const char *control, 5462306a36Sopenharmony_ci int (*connected)(struct snd_soc_dapm_widget *source, 5562306a36Sopenharmony_ci struct snd_soc_dapm_widget *sink)); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistruct snd_soc_dapm_widget * 5862306a36Sopenharmony_cisnd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, 5962306a36Sopenharmony_ci const struct snd_soc_dapm_widget *widget); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistruct snd_soc_dapm_widget * 6262306a36Sopenharmony_cisnd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm, 6362306a36Sopenharmony_ci const struct snd_soc_dapm_widget *widget); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic unsigned int soc_dapm_read(struct snd_soc_dapm_context *dapm, int reg); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci/* dapm power sequences - make this per codec in the future */ 6862306a36Sopenharmony_cistatic int dapm_up_seq[] = { 6962306a36Sopenharmony_ci [snd_soc_dapm_pre] = 1, 7062306a36Sopenharmony_ci [snd_soc_dapm_regulator_supply] = 2, 7162306a36Sopenharmony_ci [snd_soc_dapm_pinctrl] = 2, 7262306a36Sopenharmony_ci [snd_soc_dapm_clock_supply] = 2, 7362306a36Sopenharmony_ci [snd_soc_dapm_supply] = 3, 7462306a36Sopenharmony_ci [snd_soc_dapm_dai_link] = 3, 7562306a36Sopenharmony_ci [snd_soc_dapm_micbias] = 4, 7662306a36Sopenharmony_ci [snd_soc_dapm_vmid] = 4, 7762306a36Sopenharmony_ci [snd_soc_dapm_dai_in] = 5, 7862306a36Sopenharmony_ci [snd_soc_dapm_dai_out] = 5, 7962306a36Sopenharmony_ci [snd_soc_dapm_aif_in] = 5, 8062306a36Sopenharmony_ci [snd_soc_dapm_aif_out] = 5, 8162306a36Sopenharmony_ci [snd_soc_dapm_mic] = 6, 8262306a36Sopenharmony_ci [snd_soc_dapm_siggen] = 6, 8362306a36Sopenharmony_ci [snd_soc_dapm_input] = 6, 8462306a36Sopenharmony_ci [snd_soc_dapm_output] = 6, 8562306a36Sopenharmony_ci [snd_soc_dapm_mux] = 7, 8662306a36Sopenharmony_ci [snd_soc_dapm_demux] = 7, 8762306a36Sopenharmony_ci [snd_soc_dapm_dac] = 8, 8862306a36Sopenharmony_ci [snd_soc_dapm_switch] = 9, 8962306a36Sopenharmony_ci [snd_soc_dapm_mixer] = 9, 9062306a36Sopenharmony_ci [snd_soc_dapm_mixer_named_ctl] = 9, 9162306a36Sopenharmony_ci [snd_soc_dapm_pga] = 10, 9262306a36Sopenharmony_ci [snd_soc_dapm_buffer] = 10, 9362306a36Sopenharmony_ci [snd_soc_dapm_scheduler] = 10, 9462306a36Sopenharmony_ci [snd_soc_dapm_effect] = 10, 9562306a36Sopenharmony_ci [snd_soc_dapm_src] = 10, 9662306a36Sopenharmony_ci [snd_soc_dapm_asrc] = 10, 9762306a36Sopenharmony_ci [snd_soc_dapm_encoder] = 10, 9862306a36Sopenharmony_ci [snd_soc_dapm_decoder] = 10, 9962306a36Sopenharmony_ci [snd_soc_dapm_adc] = 11, 10062306a36Sopenharmony_ci [snd_soc_dapm_out_drv] = 12, 10162306a36Sopenharmony_ci [snd_soc_dapm_hp] = 12, 10262306a36Sopenharmony_ci [snd_soc_dapm_line] = 12, 10362306a36Sopenharmony_ci [snd_soc_dapm_sink] = 12, 10462306a36Sopenharmony_ci [snd_soc_dapm_spk] = 13, 10562306a36Sopenharmony_ci [snd_soc_dapm_kcontrol] = 14, 10662306a36Sopenharmony_ci [snd_soc_dapm_post] = 15, 10762306a36Sopenharmony_ci}; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic int dapm_down_seq[] = { 11062306a36Sopenharmony_ci [snd_soc_dapm_pre] = 1, 11162306a36Sopenharmony_ci [snd_soc_dapm_kcontrol] = 2, 11262306a36Sopenharmony_ci [snd_soc_dapm_adc] = 3, 11362306a36Sopenharmony_ci [snd_soc_dapm_spk] = 4, 11462306a36Sopenharmony_ci [snd_soc_dapm_hp] = 5, 11562306a36Sopenharmony_ci [snd_soc_dapm_line] = 5, 11662306a36Sopenharmony_ci [snd_soc_dapm_out_drv] = 5, 11762306a36Sopenharmony_ci [snd_soc_dapm_sink] = 6, 11862306a36Sopenharmony_ci [snd_soc_dapm_pga] = 6, 11962306a36Sopenharmony_ci [snd_soc_dapm_buffer] = 6, 12062306a36Sopenharmony_ci [snd_soc_dapm_scheduler] = 6, 12162306a36Sopenharmony_ci [snd_soc_dapm_effect] = 6, 12262306a36Sopenharmony_ci [snd_soc_dapm_src] = 6, 12362306a36Sopenharmony_ci [snd_soc_dapm_asrc] = 6, 12462306a36Sopenharmony_ci [snd_soc_dapm_encoder] = 6, 12562306a36Sopenharmony_ci [snd_soc_dapm_decoder] = 6, 12662306a36Sopenharmony_ci [snd_soc_dapm_switch] = 7, 12762306a36Sopenharmony_ci [snd_soc_dapm_mixer_named_ctl] = 7, 12862306a36Sopenharmony_ci [snd_soc_dapm_mixer] = 7, 12962306a36Sopenharmony_ci [snd_soc_dapm_dac] = 8, 13062306a36Sopenharmony_ci [snd_soc_dapm_mic] = 9, 13162306a36Sopenharmony_ci [snd_soc_dapm_siggen] = 9, 13262306a36Sopenharmony_ci [snd_soc_dapm_input] = 9, 13362306a36Sopenharmony_ci [snd_soc_dapm_output] = 9, 13462306a36Sopenharmony_ci [snd_soc_dapm_micbias] = 10, 13562306a36Sopenharmony_ci [snd_soc_dapm_vmid] = 10, 13662306a36Sopenharmony_ci [snd_soc_dapm_mux] = 11, 13762306a36Sopenharmony_ci [snd_soc_dapm_demux] = 11, 13862306a36Sopenharmony_ci [snd_soc_dapm_aif_in] = 12, 13962306a36Sopenharmony_ci [snd_soc_dapm_aif_out] = 12, 14062306a36Sopenharmony_ci [snd_soc_dapm_dai_in] = 12, 14162306a36Sopenharmony_ci [snd_soc_dapm_dai_out] = 12, 14262306a36Sopenharmony_ci [snd_soc_dapm_dai_link] = 13, 14362306a36Sopenharmony_ci [snd_soc_dapm_supply] = 14, 14462306a36Sopenharmony_ci [snd_soc_dapm_clock_supply] = 15, 14562306a36Sopenharmony_ci [snd_soc_dapm_pinctrl] = 15, 14662306a36Sopenharmony_ci [snd_soc_dapm_regulator_supply] = 15, 14762306a36Sopenharmony_ci [snd_soc_dapm_post] = 16, 14862306a36Sopenharmony_ci}; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic void dapm_assert_locked(struct snd_soc_dapm_context *dapm) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci if (snd_soc_card_is_instantiated(dapm->card)) 15362306a36Sopenharmony_ci snd_soc_dapm_mutex_assert_held(dapm); 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic void pop_wait(u32 pop_time) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci if (pop_time) 15962306a36Sopenharmony_ci schedule_timeout_uninterruptible(msecs_to_jiffies(pop_time)); 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci__printf(3, 4) 16362306a36Sopenharmony_cistatic void pop_dbg(struct device *dev, u32 pop_time, const char *fmt, ...) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci va_list args; 16662306a36Sopenharmony_ci char *buf; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (!pop_time) 16962306a36Sopenharmony_ci return; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci buf = kmalloc(PAGE_SIZE, GFP_KERNEL); 17262306a36Sopenharmony_ci if (buf == NULL) 17362306a36Sopenharmony_ci return; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci va_start(args, fmt); 17662306a36Sopenharmony_ci vsnprintf(buf, PAGE_SIZE, fmt, args); 17762306a36Sopenharmony_ci dev_info(dev, "%s", buf); 17862306a36Sopenharmony_ci va_end(args); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci kfree(buf); 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic bool dapm_dirty_widget(struct snd_soc_dapm_widget *w) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci return !list_empty(&w->dirty); 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic void dapm_mark_dirty(struct snd_soc_dapm_widget *w, const char *reason) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci dapm_assert_locked(w->dapm); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if (!dapm_dirty_widget(w)) { 19362306a36Sopenharmony_ci dev_vdbg(w->dapm->dev, "Marking %s dirty due to %s\n", 19462306a36Sopenharmony_ci w->name, reason); 19562306a36Sopenharmony_ci list_add_tail(&w->dirty, &w->dapm->card->dapm_dirty); 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci/* 20062306a36Sopenharmony_ci * Common implementation for dapm_widget_invalidate_input_paths() and 20162306a36Sopenharmony_ci * dapm_widget_invalidate_output_paths(). The function is inlined since the 20262306a36Sopenharmony_ci * combined size of the two specialized functions is only marginally larger then 20362306a36Sopenharmony_ci * the size of the generic function and at the same time the fast path of the 20462306a36Sopenharmony_ci * specialized functions is significantly smaller than the generic function. 20562306a36Sopenharmony_ci */ 20662306a36Sopenharmony_cistatic __always_inline void dapm_widget_invalidate_paths( 20762306a36Sopenharmony_ci struct snd_soc_dapm_widget *w, enum snd_soc_dapm_direction dir) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci enum snd_soc_dapm_direction rdir = SND_SOC_DAPM_DIR_REVERSE(dir); 21062306a36Sopenharmony_ci struct snd_soc_dapm_widget *node; 21162306a36Sopenharmony_ci struct snd_soc_dapm_path *p; 21262306a36Sopenharmony_ci LIST_HEAD(list); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci dapm_assert_locked(w->dapm); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if (w->endpoints[dir] == -1) 21762306a36Sopenharmony_ci return; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci list_add_tail(&w->work_list, &list); 22062306a36Sopenharmony_ci w->endpoints[dir] = -1; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci list_for_each_entry(w, &list, work_list) { 22362306a36Sopenharmony_ci snd_soc_dapm_widget_for_each_path(w, dir, p) { 22462306a36Sopenharmony_ci if (p->is_supply || p->weak || !p->connect) 22562306a36Sopenharmony_ci continue; 22662306a36Sopenharmony_ci node = p->node[rdir]; 22762306a36Sopenharmony_ci if (node->endpoints[dir] != -1) { 22862306a36Sopenharmony_ci node->endpoints[dir] = -1; 22962306a36Sopenharmony_ci list_add_tail(&node->work_list, &list); 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci/* 23662306a36Sopenharmony_ci * dapm_widget_invalidate_input_paths() - Invalidate the cached number of 23762306a36Sopenharmony_ci * input paths 23862306a36Sopenharmony_ci * @w: The widget for which to invalidate the cached number of input paths 23962306a36Sopenharmony_ci * 24062306a36Sopenharmony_ci * Resets the cached number of inputs for the specified widget and all widgets 24162306a36Sopenharmony_ci * that can be reached via outcoming paths from the widget. 24262306a36Sopenharmony_ci * 24362306a36Sopenharmony_ci * This function must be called if the number of output paths for a widget might 24462306a36Sopenharmony_ci * have changed. E.g. if the source state of a widget changes or a path is added 24562306a36Sopenharmony_ci * or activated with the widget as the sink. 24662306a36Sopenharmony_ci */ 24762306a36Sopenharmony_cistatic void dapm_widget_invalidate_input_paths(struct snd_soc_dapm_widget *w) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci dapm_widget_invalidate_paths(w, SND_SOC_DAPM_DIR_IN); 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci/* 25362306a36Sopenharmony_ci * dapm_widget_invalidate_output_paths() - Invalidate the cached number of 25462306a36Sopenharmony_ci * output paths 25562306a36Sopenharmony_ci * @w: The widget for which to invalidate the cached number of output paths 25662306a36Sopenharmony_ci * 25762306a36Sopenharmony_ci * Resets the cached number of outputs for the specified widget and all widgets 25862306a36Sopenharmony_ci * that can be reached via incoming paths from the widget. 25962306a36Sopenharmony_ci * 26062306a36Sopenharmony_ci * This function must be called if the number of output paths for a widget might 26162306a36Sopenharmony_ci * have changed. E.g. if the sink state of a widget changes or a path is added 26262306a36Sopenharmony_ci * or activated with the widget as the source. 26362306a36Sopenharmony_ci */ 26462306a36Sopenharmony_cistatic void dapm_widget_invalidate_output_paths(struct snd_soc_dapm_widget *w) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci dapm_widget_invalidate_paths(w, SND_SOC_DAPM_DIR_OUT); 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci/* 27062306a36Sopenharmony_ci * dapm_path_invalidate() - Invalidates the cached number of inputs and outputs 27162306a36Sopenharmony_ci * for the widgets connected to a path 27262306a36Sopenharmony_ci * @p: The path to invalidate 27362306a36Sopenharmony_ci * 27462306a36Sopenharmony_ci * Resets the cached number of inputs for the sink of the path and the cached 27562306a36Sopenharmony_ci * number of outputs for the source of the path. 27662306a36Sopenharmony_ci * 27762306a36Sopenharmony_ci * This function must be called when a path is added, removed or the connected 27862306a36Sopenharmony_ci * state changes. 27962306a36Sopenharmony_ci */ 28062306a36Sopenharmony_cistatic void dapm_path_invalidate(struct snd_soc_dapm_path *p) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci /* 28362306a36Sopenharmony_ci * Weak paths or supply paths do not influence the number of input or 28462306a36Sopenharmony_ci * output paths of their neighbors. 28562306a36Sopenharmony_ci */ 28662306a36Sopenharmony_ci if (p->weak || p->is_supply) 28762306a36Sopenharmony_ci return; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci /* 29062306a36Sopenharmony_ci * The number of connected endpoints is the sum of the number of 29162306a36Sopenharmony_ci * connected endpoints of all neighbors. If a node with 0 connected 29262306a36Sopenharmony_ci * endpoints is either connected or disconnected that sum won't change, 29362306a36Sopenharmony_ci * so there is no need to re-check the path. 29462306a36Sopenharmony_ci */ 29562306a36Sopenharmony_ci if (p->source->endpoints[SND_SOC_DAPM_DIR_IN] != 0) 29662306a36Sopenharmony_ci dapm_widget_invalidate_input_paths(p->sink); 29762306a36Sopenharmony_ci if (p->sink->endpoints[SND_SOC_DAPM_DIR_OUT] != 0) 29862306a36Sopenharmony_ci dapm_widget_invalidate_output_paths(p->source); 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_civoid dapm_mark_endpoints_dirty(struct snd_soc_card *card) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci struct snd_soc_dapm_widget *w; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci snd_soc_dapm_mutex_lock_root(card); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci for_each_card_widgets(card, w) { 30862306a36Sopenharmony_ci if (w->is_ep) { 30962306a36Sopenharmony_ci dapm_mark_dirty(w, "Rechecking endpoints"); 31062306a36Sopenharmony_ci if (w->is_ep & SND_SOC_DAPM_EP_SINK) 31162306a36Sopenharmony_ci dapm_widget_invalidate_output_paths(w); 31262306a36Sopenharmony_ci if (w->is_ep & SND_SOC_DAPM_EP_SOURCE) 31362306a36Sopenharmony_ci dapm_widget_invalidate_input_paths(w); 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci snd_soc_dapm_mutex_unlock(card); 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dapm_mark_endpoints_dirty); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci/* create a new dapm widget */ 32262306a36Sopenharmony_cistatic inline struct snd_soc_dapm_widget *dapm_cnew_widget( 32362306a36Sopenharmony_ci const struct snd_soc_dapm_widget *_widget) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci struct snd_soc_dapm_widget *w; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci w = kmemdup(_widget, sizeof(*_widget), GFP_KERNEL); 32862306a36Sopenharmony_ci if (!w) 32962306a36Sopenharmony_ci return NULL; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci /* 33262306a36Sopenharmony_ci * w->name is duplicated in caller, but w->sname isn't. 33362306a36Sopenharmony_ci * Duplicate it here if defined 33462306a36Sopenharmony_ci */ 33562306a36Sopenharmony_ci if (_widget->sname) { 33662306a36Sopenharmony_ci w->sname = kstrdup_const(_widget->sname, GFP_KERNEL); 33762306a36Sopenharmony_ci if (!w->sname) { 33862306a36Sopenharmony_ci kfree(w); 33962306a36Sopenharmony_ci return NULL; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci return w; 34362306a36Sopenharmony_ci} 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_cistruct dapm_kcontrol_data { 34662306a36Sopenharmony_ci unsigned int value; 34762306a36Sopenharmony_ci struct snd_soc_dapm_widget *widget; 34862306a36Sopenharmony_ci struct list_head paths; 34962306a36Sopenharmony_ci struct snd_soc_dapm_widget_list *wlist; 35062306a36Sopenharmony_ci}; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget, 35362306a36Sopenharmony_ci struct snd_kcontrol *kcontrol, const char *ctrl_name) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci struct dapm_kcontrol_data *data; 35662306a36Sopenharmony_ci struct soc_mixer_control *mc; 35762306a36Sopenharmony_ci struct soc_enum *e; 35862306a36Sopenharmony_ci const char *name; 35962306a36Sopenharmony_ci int ret; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci data = kzalloc(sizeof(*data), GFP_KERNEL); 36262306a36Sopenharmony_ci if (!data) 36362306a36Sopenharmony_ci return -ENOMEM; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci INIT_LIST_HEAD(&data->paths); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci switch (widget->id) { 36862306a36Sopenharmony_ci case snd_soc_dapm_switch: 36962306a36Sopenharmony_ci case snd_soc_dapm_mixer: 37062306a36Sopenharmony_ci case snd_soc_dapm_mixer_named_ctl: 37162306a36Sopenharmony_ci mc = (struct soc_mixer_control *)kcontrol->private_value; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci if (mc->autodisable) { 37462306a36Sopenharmony_ci struct snd_soc_dapm_widget template; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci if (snd_soc_volsw_is_stereo(mc)) 37762306a36Sopenharmony_ci dev_warn(widget->dapm->dev, 37862306a36Sopenharmony_ci "ASoC: Unsupported stereo autodisable control '%s'\n", 37962306a36Sopenharmony_ci ctrl_name); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci name = kasprintf(GFP_KERNEL, "%s %s", ctrl_name, 38262306a36Sopenharmony_ci "Autodisable"); 38362306a36Sopenharmony_ci if (!name) { 38462306a36Sopenharmony_ci ret = -ENOMEM; 38562306a36Sopenharmony_ci goto err_data; 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci memset(&template, 0, sizeof(template)); 38962306a36Sopenharmony_ci template.reg = mc->reg; 39062306a36Sopenharmony_ci template.mask = (1 << fls(mc->max)) - 1; 39162306a36Sopenharmony_ci template.shift = mc->shift; 39262306a36Sopenharmony_ci if (mc->invert) 39362306a36Sopenharmony_ci template.off_val = mc->max; 39462306a36Sopenharmony_ci else 39562306a36Sopenharmony_ci template.off_val = 0; 39662306a36Sopenharmony_ci template.on_val = template.off_val; 39762306a36Sopenharmony_ci template.id = snd_soc_dapm_kcontrol; 39862306a36Sopenharmony_ci template.name = name; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci data->value = template.on_val; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci data->widget = 40362306a36Sopenharmony_ci snd_soc_dapm_new_control_unlocked(widget->dapm, 40462306a36Sopenharmony_ci &template); 40562306a36Sopenharmony_ci kfree(name); 40662306a36Sopenharmony_ci if (IS_ERR(data->widget)) { 40762306a36Sopenharmony_ci ret = PTR_ERR(data->widget); 40862306a36Sopenharmony_ci goto err_data; 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci break; 41262306a36Sopenharmony_ci case snd_soc_dapm_demux: 41362306a36Sopenharmony_ci case snd_soc_dapm_mux: 41462306a36Sopenharmony_ci e = (struct soc_enum *)kcontrol->private_value; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci if (e->autodisable) { 41762306a36Sopenharmony_ci struct snd_soc_dapm_widget template; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci name = kasprintf(GFP_KERNEL, "%s %s", ctrl_name, 42062306a36Sopenharmony_ci "Autodisable"); 42162306a36Sopenharmony_ci if (!name) { 42262306a36Sopenharmony_ci ret = -ENOMEM; 42362306a36Sopenharmony_ci goto err_data; 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci memset(&template, 0, sizeof(template)); 42762306a36Sopenharmony_ci template.reg = e->reg; 42862306a36Sopenharmony_ci template.mask = e->mask; 42962306a36Sopenharmony_ci template.shift = e->shift_l; 43062306a36Sopenharmony_ci template.off_val = snd_soc_enum_item_to_val(e, 0); 43162306a36Sopenharmony_ci template.on_val = template.off_val; 43262306a36Sopenharmony_ci template.id = snd_soc_dapm_kcontrol; 43362306a36Sopenharmony_ci template.name = name; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci data->value = template.on_val; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci data->widget = snd_soc_dapm_new_control_unlocked( 43862306a36Sopenharmony_ci widget->dapm, &template); 43962306a36Sopenharmony_ci kfree(name); 44062306a36Sopenharmony_ci if (IS_ERR(data->widget)) { 44162306a36Sopenharmony_ci ret = PTR_ERR(data->widget); 44262306a36Sopenharmony_ci goto err_data; 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci snd_soc_dapm_add_path(widget->dapm, data->widget, 44662306a36Sopenharmony_ci widget, NULL, NULL); 44762306a36Sopenharmony_ci } else if (e->reg != SND_SOC_NOPM) { 44862306a36Sopenharmony_ci data->value = soc_dapm_read(widget->dapm, e->reg) & 44962306a36Sopenharmony_ci (e->mask << e->shift_l); 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci break; 45262306a36Sopenharmony_ci default: 45362306a36Sopenharmony_ci break; 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci kcontrol->private_data = data; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci return 0; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_cierr_data: 46162306a36Sopenharmony_ci kfree(data); 46262306a36Sopenharmony_ci return ret; 46362306a36Sopenharmony_ci} 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_cistatic void dapm_kcontrol_free(struct snd_kcontrol *kctl) 46662306a36Sopenharmony_ci{ 46762306a36Sopenharmony_ci struct dapm_kcontrol_data *data = snd_kcontrol_chip(kctl); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci list_del(&data->paths); 47062306a36Sopenharmony_ci kfree(data->wlist); 47162306a36Sopenharmony_ci kfree(data); 47262306a36Sopenharmony_ci} 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_cistatic struct snd_soc_dapm_widget_list *dapm_kcontrol_get_wlist( 47562306a36Sopenharmony_ci const struct snd_kcontrol *kcontrol) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci return data->wlist; 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_cistatic int dapm_kcontrol_add_widget(struct snd_kcontrol *kcontrol, 48362306a36Sopenharmony_ci struct snd_soc_dapm_widget *widget) 48462306a36Sopenharmony_ci{ 48562306a36Sopenharmony_ci struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); 48662306a36Sopenharmony_ci struct snd_soc_dapm_widget_list *new_wlist; 48762306a36Sopenharmony_ci unsigned int n; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci if (data->wlist) 49062306a36Sopenharmony_ci n = data->wlist->num_widgets + 1; 49162306a36Sopenharmony_ci else 49262306a36Sopenharmony_ci n = 1; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci new_wlist = krealloc(data->wlist, 49562306a36Sopenharmony_ci struct_size(new_wlist, widgets, n), 49662306a36Sopenharmony_ci GFP_KERNEL); 49762306a36Sopenharmony_ci if (!new_wlist) 49862306a36Sopenharmony_ci return -ENOMEM; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci new_wlist->widgets[n - 1] = widget; 50162306a36Sopenharmony_ci new_wlist->num_widgets = n; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci data->wlist = new_wlist; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci return 0; 50662306a36Sopenharmony_ci} 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_cistatic void dapm_kcontrol_add_path(const struct snd_kcontrol *kcontrol, 50962306a36Sopenharmony_ci struct snd_soc_dapm_path *path) 51062306a36Sopenharmony_ci{ 51162306a36Sopenharmony_ci struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci list_add_tail(&path->list_kcontrol, &data->paths); 51462306a36Sopenharmony_ci} 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_cistatic bool dapm_kcontrol_is_powered(const struct snd_kcontrol *kcontrol) 51762306a36Sopenharmony_ci{ 51862306a36Sopenharmony_ci struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci if (!data->widget) 52162306a36Sopenharmony_ci return true; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci return data->widget->power; 52462306a36Sopenharmony_ci} 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_cistatic struct list_head *dapm_kcontrol_get_path_list( 52762306a36Sopenharmony_ci const struct snd_kcontrol *kcontrol) 52862306a36Sopenharmony_ci{ 52962306a36Sopenharmony_ci struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci return &data->paths; 53262306a36Sopenharmony_ci} 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci#define dapm_kcontrol_for_each_path(path, kcontrol) \ 53562306a36Sopenharmony_ci list_for_each_entry(path, dapm_kcontrol_get_path_list(kcontrol), \ 53662306a36Sopenharmony_ci list_kcontrol) 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ciunsigned int dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol) 53962306a36Sopenharmony_ci{ 54062306a36Sopenharmony_ci struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci return data->value; 54362306a36Sopenharmony_ci} 54462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dapm_kcontrol_get_value); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_cistatic bool dapm_kcontrol_set_value(const struct snd_kcontrol *kcontrol, 54762306a36Sopenharmony_ci unsigned int value) 54862306a36Sopenharmony_ci{ 54962306a36Sopenharmony_ci struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci if (data->value == value) 55262306a36Sopenharmony_ci return false; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci if (data->widget) { 55562306a36Sopenharmony_ci switch (dapm_kcontrol_get_wlist(kcontrol)->widgets[0]->id) { 55662306a36Sopenharmony_ci case snd_soc_dapm_switch: 55762306a36Sopenharmony_ci case snd_soc_dapm_mixer: 55862306a36Sopenharmony_ci case snd_soc_dapm_mixer_named_ctl: 55962306a36Sopenharmony_ci data->widget->on_val = value & data->widget->mask; 56062306a36Sopenharmony_ci break; 56162306a36Sopenharmony_ci case snd_soc_dapm_demux: 56262306a36Sopenharmony_ci case snd_soc_dapm_mux: 56362306a36Sopenharmony_ci data->widget->on_val = value >> data->widget->shift; 56462306a36Sopenharmony_ci break; 56562306a36Sopenharmony_ci default: 56662306a36Sopenharmony_ci data->widget->on_val = value; 56762306a36Sopenharmony_ci break; 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci data->value = value; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci return true; 57462306a36Sopenharmony_ci} 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci/** 57762306a36Sopenharmony_ci * snd_soc_dapm_kcontrol_widget() - Returns the widget associated to a 57862306a36Sopenharmony_ci * kcontrol 57962306a36Sopenharmony_ci * @kcontrol: The kcontrol 58062306a36Sopenharmony_ci */ 58162306a36Sopenharmony_cistruct snd_soc_dapm_widget *snd_soc_dapm_kcontrol_widget( 58262306a36Sopenharmony_ci struct snd_kcontrol *kcontrol) 58362306a36Sopenharmony_ci{ 58462306a36Sopenharmony_ci return dapm_kcontrol_get_wlist(kcontrol)->widgets[0]; 58562306a36Sopenharmony_ci} 58662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_widget); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci/** 58962306a36Sopenharmony_ci * snd_soc_dapm_kcontrol_dapm() - Returns the dapm context associated to a 59062306a36Sopenharmony_ci * kcontrol 59162306a36Sopenharmony_ci * @kcontrol: The kcontrol 59262306a36Sopenharmony_ci * 59362306a36Sopenharmony_ci * Note: This function must only be used on kcontrols that are known to have 59462306a36Sopenharmony_ci * been registered for a CODEC. Otherwise the behaviour is undefined. 59562306a36Sopenharmony_ci */ 59662306a36Sopenharmony_cistruct snd_soc_dapm_context *snd_soc_dapm_kcontrol_dapm( 59762306a36Sopenharmony_ci struct snd_kcontrol *kcontrol) 59862306a36Sopenharmony_ci{ 59962306a36Sopenharmony_ci return dapm_kcontrol_get_wlist(kcontrol)->widgets[0]->dapm; 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_dapm); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_cistatic void dapm_reset(struct snd_soc_card *card) 60462306a36Sopenharmony_ci{ 60562306a36Sopenharmony_ci struct snd_soc_dapm_widget *w; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci snd_soc_dapm_mutex_assert_held(card); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci memset(&card->dapm_stats, 0, sizeof(card->dapm_stats)); 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci for_each_card_widgets(card, w) { 61262306a36Sopenharmony_ci w->new_power = w->power; 61362306a36Sopenharmony_ci w->power_checked = false; 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci} 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_cistatic const char *soc_dapm_prefix(struct snd_soc_dapm_context *dapm) 61862306a36Sopenharmony_ci{ 61962306a36Sopenharmony_ci if (!dapm->component) 62062306a36Sopenharmony_ci return NULL; 62162306a36Sopenharmony_ci return dapm->component->name_prefix; 62262306a36Sopenharmony_ci} 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_cistatic unsigned int soc_dapm_read(struct snd_soc_dapm_context *dapm, int reg) 62562306a36Sopenharmony_ci{ 62662306a36Sopenharmony_ci if (!dapm->component) 62762306a36Sopenharmony_ci return -EIO; 62862306a36Sopenharmony_ci return snd_soc_component_read(dapm->component, reg); 62962306a36Sopenharmony_ci} 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_cistatic int soc_dapm_update_bits(struct snd_soc_dapm_context *dapm, 63262306a36Sopenharmony_ci int reg, unsigned int mask, unsigned int value) 63362306a36Sopenharmony_ci{ 63462306a36Sopenharmony_ci if (!dapm->component) 63562306a36Sopenharmony_ci return -EIO; 63662306a36Sopenharmony_ci return snd_soc_component_update_bits(dapm->component, reg, 63762306a36Sopenharmony_ci mask, value); 63862306a36Sopenharmony_ci} 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_cistatic int soc_dapm_test_bits(struct snd_soc_dapm_context *dapm, 64162306a36Sopenharmony_ci int reg, unsigned int mask, unsigned int value) 64262306a36Sopenharmony_ci{ 64362306a36Sopenharmony_ci if (!dapm->component) 64462306a36Sopenharmony_ci return -EIO; 64562306a36Sopenharmony_ci return snd_soc_component_test_bits(dapm->component, reg, mask, value); 64662306a36Sopenharmony_ci} 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_cistatic void soc_dapm_async_complete(struct snd_soc_dapm_context *dapm) 64962306a36Sopenharmony_ci{ 65062306a36Sopenharmony_ci if (dapm->component) 65162306a36Sopenharmony_ci snd_soc_component_async_complete(dapm->component); 65262306a36Sopenharmony_ci} 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_cistatic struct snd_soc_dapm_widget * 65562306a36Sopenharmony_cidapm_wcache_lookup(struct snd_soc_dapm_widget *w, const char *name) 65662306a36Sopenharmony_ci{ 65762306a36Sopenharmony_ci if (w) { 65862306a36Sopenharmony_ci struct list_head *wlist = &w->dapm->card->widgets; 65962306a36Sopenharmony_ci const int depth = 2; 66062306a36Sopenharmony_ci int i = 0; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci list_for_each_entry_from(w, wlist, list) { 66362306a36Sopenharmony_ci if (!strcmp(name, w->name)) 66462306a36Sopenharmony_ci return w; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci if (++i == depth) 66762306a36Sopenharmony_ci break; 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci } 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci return NULL; 67262306a36Sopenharmony_ci} 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci/** 67562306a36Sopenharmony_ci * snd_soc_dapm_force_bias_level() - Sets the DAPM bias level 67662306a36Sopenharmony_ci * @dapm: The DAPM context for which to set the level 67762306a36Sopenharmony_ci * @level: The level to set 67862306a36Sopenharmony_ci * 67962306a36Sopenharmony_ci * Forces the DAPM bias level to a specific state. It will call the bias level 68062306a36Sopenharmony_ci * callback of DAPM context with the specified level. This will even happen if 68162306a36Sopenharmony_ci * the context is already at the same level. Furthermore it will not go through 68262306a36Sopenharmony_ci * the normal bias level sequencing, meaning any intermediate states between the 68362306a36Sopenharmony_ci * current and the target state will not be entered. 68462306a36Sopenharmony_ci * 68562306a36Sopenharmony_ci * Note that the change in bias level is only temporary and the next time 68662306a36Sopenharmony_ci * snd_soc_dapm_sync() is called the state will be set to the level as 68762306a36Sopenharmony_ci * determined by the DAPM core. The function is mainly intended to be used to 68862306a36Sopenharmony_ci * used during probe or resume from suspend to power up the device so 68962306a36Sopenharmony_ci * initialization can be done, before the DAPM core takes over. 69062306a36Sopenharmony_ci */ 69162306a36Sopenharmony_ciint snd_soc_dapm_force_bias_level(struct snd_soc_dapm_context *dapm, 69262306a36Sopenharmony_ci enum snd_soc_bias_level level) 69362306a36Sopenharmony_ci{ 69462306a36Sopenharmony_ci int ret = 0; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci if (dapm->component) 69762306a36Sopenharmony_ci ret = snd_soc_component_set_bias_level(dapm->component, level); 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci if (ret == 0) 70062306a36Sopenharmony_ci dapm->bias_level = level; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci return ret; 70362306a36Sopenharmony_ci} 70462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_force_bias_level); 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci/** 70762306a36Sopenharmony_ci * snd_soc_dapm_set_bias_level - set the bias level for the system 70862306a36Sopenharmony_ci * @dapm: DAPM context 70962306a36Sopenharmony_ci * @level: level to configure 71062306a36Sopenharmony_ci * 71162306a36Sopenharmony_ci * Configure the bias (power) levels for the SoC audio device. 71262306a36Sopenharmony_ci * 71362306a36Sopenharmony_ci * Returns 0 for success else error. 71462306a36Sopenharmony_ci */ 71562306a36Sopenharmony_cistatic int snd_soc_dapm_set_bias_level(struct snd_soc_dapm_context *dapm, 71662306a36Sopenharmony_ci enum snd_soc_bias_level level) 71762306a36Sopenharmony_ci{ 71862306a36Sopenharmony_ci struct snd_soc_card *card = dapm->card; 71962306a36Sopenharmony_ci int ret = 0; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci trace_snd_soc_bias_level_start(card, level); 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci ret = snd_soc_card_set_bias_level(card, dapm, level); 72462306a36Sopenharmony_ci if (ret != 0) 72562306a36Sopenharmony_ci goto out; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci if (!card || dapm != &card->dapm) 72862306a36Sopenharmony_ci ret = snd_soc_dapm_force_bias_level(dapm, level); 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci if (ret != 0) 73162306a36Sopenharmony_ci goto out; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci ret = snd_soc_card_set_bias_level_post(card, dapm, level); 73462306a36Sopenharmony_ciout: 73562306a36Sopenharmony_ci trace_snd_soc_bias_level_done(card, level); 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci return ret; 73862306a36Sopenharmony_ci} 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci/* connect mux widget to its interconnecting audio paths */ 74162306a36Sopenharmony_cistatic int dapm_connect_mux(struct snd_soc_dapm_context *dapm, 74262306a36Sopenharmony_ci struct snd_soc_dapm_path *path, const char *control_name, 74362306a36Sopenharmony_ci struct snd_soc_dapm_widget *w) 74462306a36Sopenharmony_ci{ 74562306a36Sopenharmony_ci const struct snd_kcontrol_new *kcontrol = &w->kcontrol_news[0]; 74662306a36Sopenharmony_ci struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 74762306a36Sopenharmony_ci unsigned int item; 74862306a36Sopenharmony_ci int i; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci if (e->reg != SND_SOC_NOPM) { 75162306a36Sopenharmony_ci unsigned int val; 75262306a36Sopenharmony_ci val = soc_dapm_read(dapm, e->reg); 75362306a36Sopenharmony_ci val = (val >> e->shift_l) & e->mask; 75462306a36Sopenharmony_ci item = snd_soc_enum_val_to_item(e, val); 75562306a36Sopenharmony_ci } else { 75662306a36Sopenharmony_ci /* since a virtual mux has no backing registers to 75762306a36Sopenharmony_ci * decide which path to connect, it will try to match 75862306a36Sopenharmony_ci * with the first enumeration. This is to ensure 75962306a36Sopenharmony_ci * that the default mux choice (the first) will be 76062306a36Sopenharmony_ci * correctly powered up during initialization. 76162306a36Sopenharmony_ci */ 76262306a36Sopenharmony_ci item = 0; 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci i = match_string(e->texts, e->items, control_name); 76662306a36Sopenharmony_ci if (i < 0) 76762306a36Sopenharmony_ci return -ENODEV; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci path->name = e->texts[i]; 77062306a36Sopenharmony_ci path->connect = (i == item); 77162306a36Sopenharmony_ci return 0; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci} 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci/* set up initial codec paths */ 77662306a36Sopenharmony_cistatic void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i, 77762306a36Sopenharmony_ci int nth_path) 77862306a36Sopenharmony_ci{ 77962306a36Sopenharmony_ci struct soc_mixer_control *mc = (struct soc_mixer_control *) 78062306a36Sopenharmony_ci p->sink->kcontrol_news[i].private_value; 78162306a36Sopenharmony_ci unsigned int reg = mc->reg; 78262306a36Sopenharmony_ci unsigned int invert = mc->invert; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci if (reg != SND_SOC_NOPM) { 78562306a36Sopenharmony_ci unsigned int shift = mc->shift; 78662306a36Sopenharmony_ci unsigned int max = mc->max; 78762306a36Sopenharmony_ci unsigned int mask = (1 << fls(max)) - 1; 78862306a36Sopenharmony_ci unsigned int val = soc_dapm_read(p->sink->dapm, reg); 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci /* 79162306a36Sopenharmony_ci * The nth_path argument allows this function to know 79262306a36Sopenharmony_ci * which path of a kcontrol it is setting the initial 79362306a36Sopenharmony_ci * status for. Ideally this would support any number 79462306a36Sopenharmony_ci * of paths and channels. But since kcontrols only come 79562306a36Sopenharmony_ci * in mono and stereo variants, we are limited to 2 79662306a36Sopenharmony_ci * channels. 79762306a36Sopenharmony_ci * 79862306a36Sopenharmony_ci * The following code assumes for stereo controls the 79962306a36Sopenharmony_ci * first path is the left channel, and all remaining 80062306a36Sopenharmony_ci * paths are the right channel. 80162306a36Sopenharmony_ci */ 80262306a36Sopenharmony_ci if (snd_soc_volsw_is_stereo(mc) && nth_path > 0) { 80362306a36Sopenharmony_ci if (reg != mc->rreg) 80462306a36Sopenharmony_ci val = soc_dapm_read(p->sink->dapm, mc->rreg); 80562306a36Sopenharmony_ci val = (val >> mc->rshift) & mask; 80662306a36Sopenharmony_ci } else { 80762306a36Sopenharmony_ci val = (val >> shift) & mask; 80862306a36Sopenharmony_ci } 80962306a36Sopenharmony_ci if (invert) 81062306a36Sopenharmony_ci val = max - val; 81162306a36Sopenharmony_ci p->connect = !!val; 81262306a36Sopenharmony_ci } else { 81362306a36Sopenharmony_ci /* since a virtual mixer has no backing registers to 81462306a36Sopenharmony_ci * decide which path to connect, it will try to match 81562306a36Sopenharmony_ci * with initial state. This is to ensure 81662306a36Sopenharmony_ci * that the default mixer choice will be 81762306a36Sopenharmony_ci * correctly powered up during initialization. 81862306a36Sopenharmony_ci */ 81962306a36Sopenharmony_ci p->connect = invert; 82062306a36Sopenharmony_ci } 82162306a36Sopenharmony_ci} 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci/* connect mixer widget to its interconnecting audio paths */ 82462306a36Sopenharmony_cistatic int dapm_connect_mixer(struct snd_soc_dapm_context *dapm, 82562306a36Sopenharmony_ci struct snd_soc_dapm_path *path, const char *control_name) 82662306a36Sopenharmony_ci{ 82762306a36Sopenharmony_ci int i, nth_path = 0; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci /* search for mixer kcontrol */ 83062306a36Sopenharmony_ci for (i = 0; i < path->sink->num_kcontrols; i++) { 83162306a36Sopenharmony_ci if (!strcmp(control_name, path->sink->kcontrol_news[i].name)) { 83262306a36Sopenharmony_ci path->name = path->sink->kcontrol_news[i].name; 83362306a36Sopenharmony_ci dapm_set_mixer_path_status(path, i, nth_path++); 83462306a36Sopenharmony_ci return 0; 83562306a36Sopenharmony_ci } 83662306a36Sopenharmony_ci } 83762306a36Sopenharmony_ci return -ENODEV; 83862306a36Sopenharmony_ci} 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_cistatic int dapm_is_shared_kcontrol(struct snd_soc_dapm_context *dapm, 84162306a36Sopenharmony_ci struct snd_soc_dapm_widget *kcontrolw, 84262306a36Sopenharmony_ci const struct snd_kcontrol_new *kcontrol_new, 84362306a36Sopenharmony_ci struct snd_kcontrol **kcontrol) 84462306a36Sopenharmony_ci{ 84562306a36Sopenharmony_ci struct snd_soc_dapm_widget *w; 84662306a36Sopenharmony_ci int i; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci *kcontrol = NULL; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci for_each_card_widgets(dapm->card, w) { 85162306a36Sopenharmony_ci if (w == kcontrolw || w->dapm != kcontrolw->dapm) 85262306a36Sopenharmony_ci continue; 85362306a36Sopenharmony_ci for (i = 0; i < w->num_kcontrols; i++) { 85462306a36Sopenharmony_ci if (&w->kcontrol_news[i] == kcontrol_new) { 85562306a36Sopenharmony_ci if (w->kcontrols) 85662306a36Sopenharmony_ci *kcontrol = w->kcontrols[i]; 85762306a36Sopenharmony_ci return 1; 85862306a36Sopenharmony_ci } 85962306a36Sopenharmony_ci } 86062306a36Sopenharmony_ci } 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci return 0; 86362306a36Sopenharmony_ci} 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci/* 86662306a36Sopenharmony_ci * Determine if a kcontrol is shared. If it is, look it up. If it isn't, 86762306a36Sopenharmony_ci * create it. Either way, add the widget into the control's widget list 86862306a36Sopenharmony_ci */ 86962306a36Sopenharmony_cistatic int dapm_create_or_share_kcontrol(struct snd_soc_dapm_widget *w, 87062306a36Sopenharmony_ci int kci) 87162306a36Sopenharmony_ci{ 87262306a36Sopenharmony_ci struct snd_soc_dapm_context *dapm = w->dapm; 87362306a36Sopenharmony_ci struct snd_card *card = dapm->card->snd_card; 87462306a36Sopenharmony_ci const char *prefix; 87562306a36Sopenharmony_ci size_t prefix_len; 87662306a36Sopenharmony_ci int shared; 87762306a36Sopenharmony_ci struct snd_kcontrol *kcontrol; 87862306a36Sopenharmony_ci bool wname_in_long_name, kcname_in_long_name; 87962306a36Sopenharmony_ci char *long_name = NULL; 88062306a36Sopenharmony_ci const char *name; 88162306a36Sopenharmony_ci int ret = 0; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci prefix = soc_dapm_prefix(dapm); 88462306a36Sopenharmony_ci if (prefix) 88562306a36Sopenharmony_ci prefix_len = strlen(prefix) + 1; 88662306a36Sopenharmony_ci else 88762306a36Sopenharmony_ci prefix_len = 0; 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci shared = dapm_is_shared_kcontrol(dapm, w, &w->kcontrol_news[kci], 89062306a36Sopenharmony_ci &kcontrol); 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci if (!kcontrol) { 89362306a36Sopenharmony_ci if (shared) { 89462306a36Sopenharmony_ci wname_in_long_name = false; 89562306a36Sopenharmony_ci kcname_in_long_name = true; 89662306a36Sopenharmony_ci } else { 89762306a36Sopenharmony_ci switch (w->id) { 89862306a36Sopenharmony_ci case snd_soc_dapm_switch: 89962306a36Sopenharmony_ci case snd_soc_dapm_mixer: 90062306a36Sopenharmony_ci case snd_soc_dapm_pga: 90162306a36Sopenharmony_ci case snd_soc_dapm_effect: 90262306a36Sopenharmony_ci case snd_soc_dapm_out_drv: 90362306a36Sopenharmony_ci wname_in_long_name = true; 90462306a36Sopenharmony_ci kcname_in_long_name = true; 90562306a36Sopenharmony_ci break; 90662306a36Sopenharmony_ci case snd_soc_dapm_mixer_named_ctl: 90762306a36Sopenharmony_ci wname_in_long_name = false; 90862306a36Sopenharmony_ci kcname_in_long_name = true; 90962306a36Sopenharmony_ci break; 91062306a36Sopenharmony_ci case snd_soc_dapm_demux: 91162306a36Sopenharmony_ci case snd_soc_dapm_mux: 91262306a36Sopenharmony_ci wname_in_long_name = true; 91362306a36Sopenharmony_ci kcname_in_long_name = false; 91462306a36Sopenharmony_ci break; 91562306a36Sopenharmony_ci default: 91662306a36Sopenharmony_ci return -EINVAL; 91762306a36Sopenharmony_ci } 91862306a36Sopenharmony_ci } 91962306a36Sopenharmony_ci if (w->no_wname_in_kcontrol_name) 92062306a36Sopenharmony_ci wname_in_long_name = false; 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci if (wname_in_long_name && kcname_in_long_name) { 92362306a36Sopenharmony_ci /* 92462306a36Sopenharmony_ci * The control will get a prefix from the control 92562306a36Sopenharmony_ci * creation process but we're also using the same 92662306a36Sopenharmony_ci * prefix for widgets so cut the prefix off the 92762306a36Sopenharmony_ci * front of the widget name. 92862306a36Sopenharmony_ci */ 92962306a36Sopenharmony_ci long_name = kasprintf(GFP_KERNEL, "%s %s", 93062306a36Sopenharmony_ci w->name + prefix_len, 93162306a36Sopenharmony_ci w->kcontrol_news[kci].name); 93262306a36Sopenharmony_ci if (long_name == NULL) 93362306a36Sopenharmony_ci return -ENOMEM; 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci name = long_name; 93662306a36Sopenharmony_ci } else if (wname_in_long_name) { 93762306a36Sopenharmony_ci long_name = NULL; 93862306a36Sopenharmony_ci name = w->name + prefix_len; 93962306a36Sopenharmony_ci } else { 94062306a36Sopenharmony_ci long_name = NULL; 94162306a36Sopenharmony_ci name = w->kcontrol_news[kci].name; 94262306a36Sopenharmony_ci } 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci kcontrol = snd_soc_cnew(&w->kcontrol_news[kci], NULL, name, 94562306a36Sopenharmony_ci prefix); 94662306a36Sopenharmony_ci if (!kcontrol) { 94762306a36Sopenharmony_ci ret = -ENOMEM; 94862306a36Sopenharmony_ci goto exit_free; 94962306a36Sopenharmony_ci } 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci kcontrol->private_free = dapm_kcontrol_free; 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci ret = dapm_kcontrol_data_alloc(w, kcontrol, name); 95462306a36Sopenharmony_ci if (ret) { 95562306a36Sopenharmony_ci snd_ctl_free_one(kcontrol); 95662306a36Sopenharmony_ci goto exit_free; 95762306a36Sopenharmony_ci } 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci ret = snd_ctl_add(card, kcontrol); 96062306a36Sopenharmony_ci if (ret < 0) { 96162306a36Sopenharmony_ci dev_err(dapm->dev, 96262306a36Sopenharmony_ci "ASoC: failed to add widget %s dapm kcontrol %s: %d\n", 96362306a36Sopenharmony_ci w->name, name, ret); 96462306a36Sopenharmony_ci goto exit_free; 96562306a36Sopenharmony_ci } 96662306a36Sopenharmony_ci } 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci ret = dapm_kcontrol_add_widget(kcontrol, w); 96962306a36Sopenharmony_ci if (ret == 0) 97062306a36Sopenharmony_ci w->kcontrols[kci] = kcontrol; 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ciexit_free: 97362306a36Sopenharmony_ci kfree(long_name); 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci return ret; 97662306a36Sopenharmony_ci} 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci/* create new dapm mixer control */ 97962306a36Sopenharmony_cistatic int dapm_new_mixer(struct snd_soc_dapm_widget *w) 98062306a36Sopenharmony_ci{ 98162306a36Sopenharmony_ci int i, ret; 98262306a36Sopenharmony_ci struct snd_soc_dapm_path *path; 98362306a36Sopenharmony_ci struct dapm_kcontrol_data *data; 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci /* add kcontrol */ 98662306a36Sopenharmony_ci for (i = 0; i < w->num_kcontrols; i++) { 98762306a36Sopenharmony_ci /* match name */ 98862306a36Sopenharmony_ci snd_soc_dapm_widget_for_each_source_path(w, path) { 98962306a36Sopenharmony_ci /* mixer/mux paths name must match control name */ 99062306a36Sopenharmony_ci if (path->name != (char *)w->kcontrol_news[i].name) 99162306a36Sopenharmony_ci continue; 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci if (!w->kcontrols[i]) { 99462306a36Sopenharmony_ci ret = dapm_create_or_share_kcontrol(w, i); 99562306a36Sopenharmony_ci if (ret < 0) 99662306a36Sopenharmony_ci return ret; 99762306a36Sopenharmony_ci } 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci dapm_kcontrol_add_path(w->kcontrols[i], path); 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci data = snd_kcontrol_chip(w->kcontrols[i]); 100262306a36Sopenharmony_ci if (data->widget) 100362306a36Sopenharmony_ci snd_soc_dapm_add_path(data->widget->dapm, 100462306a36Sopenharmony_ci data->widget, 100562306a36Sopenharmony_ci path->source, 100662306a36Sopenharmony_ci NULL, NULL); 100762306a36Sopenharmony_ci } 100862306a36Sopenharmony_ci } 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci return 0; 101162306a36Sopenharmony_ci} 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci/* create new dapm mux control */ 101462306a36Sopenharmony_cistatic int dapm_new_mux(struct snd_soc_dapm_widget *w) 101562306a36Sopenharmony_ci{ 101662306a36Sopenharmony_ci struct snd_soc_dapm_context *dapm = w->dapm; 101762306a36Sopenharmony_ci enum snd_soc_dapm_direction dir; 101862306a36Sopenharmony_ci struct snd_soc_dapm_path *path; 101962306a36Sopenharmony_ci const char *type; 102062306a36Sopenharmony_ci int ret; 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci switch (w->id) { 102362306a36Sopenharmony_ci case snd_soc_dapm_mux: 102462306a36Sopenharmony_ci dir = SND_SOC_DAPM_DIR_OUT; 102562306a36Sopenharmony_ci type = "mux"; 102662306a36Sopenharmony_ci break; 102762306a36Sopenharmony_ci case snd_soc_dapm_demux: 102862306a36Sopenharmony_ci dir = SND_SOC_DAPM_DIR_IN; 102962306a36Sopenharmony_ci type = "demux"; 103062306a36Sopenharmony_ci break; 103162306a36Sopenharmony_ci default: 103262306a36Sopenharmony_ci return -EINVAL; 103362306a36Sopenharmony_ci } 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci if (w->num_kcontrols != 1) { 103662306a36Sopenharmony_ci dev_err(dapm->dev, 103762306a36Sopenharmony_ci "ASoC: %s %s has incorrect number of controls\n", type, 103862306a36Sopenharmony_ci w->name); 103962306a36Sopenharmony_ci return -EINVAL; 104062306a36Sopenharmony_ci } 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci if (list_empty(&w->edges[dir])) { 104362306a36Sopenharmony_ci dev_err(dapm->dev, "ASoC: %s %s has no paths\n", type, w->name); 104462306a36Sopenharmony_ci return -EINVAL; 104562306a36Sopenharmony_ci } 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci ret = dapm_create_or_share_kcontrol(w, 0); 104862306a36Sopenharmony_ci if (ret < 0) 104962306a36Sopenharmony_ci return ret; 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci snd_soc_dapm_widget_for_each_path(w, dir, path) { 105262306a36Sopenharmony_ci if (path->name) 105362306a36Sopenharmony_ci dapm_kcontrol_add_path(w->kcontrols[0], path); 105462306a36Sopenharmony_ci } 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci return 0; 105762306a36Sopenharmony_ci} 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci/* create new dapm volume control */ 106062306a36Sopenharmony_cistatic int dapm_new_pga(struct snd_soc_dapm_widget *w) 106162306a36Sopenharmony_ci{ 106262306a36Sopenharmony_ci int i; 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci for (i = 0; i < w->num_kcontrols; i++) { 106562306a36Sopenharmony_ci int ret = dapm_create_or_share_kcontrol(w, i); 106662306a36Sopenharmony_ci if (ret < 0) 106762306a36Sopenharmony_ci return ret; 106862306a36Sopenharmony_ci } 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci return 0; 107162306a36Sopenharmony_ci} 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci/* create new dapm dai link control */ 107462306a36Sopenharmony_cistatic int dapm_new_dai_link(struct snd_soc_dapm_widget *w) 107562306a36Sopenharmony_ci{ 107662306a36Sopenharmony_ci int i; 107762306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = w->priv; 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci /* create control for links with > 1 config */ 108062306a36Sopenharmony_ci if (rtd->dai_link->num_c2c_params <= 1) 108162306a36Sopenharmony_ci return 0; 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci /* add kcontrol */ 108462306a36Sopenharmony_ci for (i = 0; i < w->num_kcontrols; i++) { 108562306a36Sopenharmony_ci struct snd_soc_dapm_context *dapm = w->dapm; 108662306a36Sopenharmony_ci struct snd_card *card = dapm->card->snd_card; 108762306a36Sopenharmony_ci struct snd_kcontrol *kcontrol = snd_soc_cnew(&w->kcontrol_news[i], 108862306a36Sopenharmony_ci w, w->name, NULL); 108962306a36Sopenharmony_ci int ret = snd_ctl_add(card, kcontrol); 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci if (ret < 0) { 109262306a36Sopenharmony_ci dev_err(dapm->dev, 109362306a36Sopenharmony_ci "ASoC: failed to add widget %s dapm kcontrol %s: %d\n", 109462306a36Sopenharmony_ci w->name, w->kcontrol_news[i].name, ret); 109562306a36Sopenharmony_ci return ret; 109662306a36Sopenharmony_ci } 109762306a36Sopenharmony_ci kcontrol->private_data = w; 109862306a36Sopenharmony_ci w->kcontrols[i] = kcontrol; 109962306a36Sopenharmony_ci } 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci return 0; 110262306a36Sopenharmony_ci} 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci/* We implement power down on suspend by checking the power state of 110562306a36Sopenharmony_ci * the ALSA card - when we are suspending the ALSA state for the card 110662306a36Sopenharmony_ci * is set to D3. 110762306a36Sopenharmony_ci */ 110862306a36Sopenharmony_cistatic int snd_soc_dapm_suspend_check(struct snd_soc_dapm_widget *widget) 110962306a36Sopenharmony_ci{ 111062306a36Sopenharmony_ci int level = snd_power_get_state(widget->dapm->card->snd_card); 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci switch (level) { 111362306a36Sopenharmony_ci case SNDRV_CTL_POWER_D3hot: 111462306a36Sopenharmony_ci case SNDRV_CTL_POWER_D3cold: 111562306a36Sopenharmony_ci if (widget->ignore_suspend) 111662306a36Sopenharmony_ci dev_dbg(widget->dapm->dev, "ASoC: %s ignoring suspend\n", 111762306a36Sopenharmony_ci widget->name); 111862306a36Sopenharmony_ci return widget->ignore_suspend; 111962306a36Sopenharmony_ci default: 112062306a36Sopenharmony_ci return 1; 112162306a36Sopenharmony_ci } 112262306a36Sopenharmony_ci} 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_cistatic void dapm_widget_list_free(struct snd_soc_dapm_widget_list **list) 112562306a36Sopenharmony_ci{ 112662306a36Sopenharmony_ci kfree(*list); 112762306a36Sopenharmony_ci} 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_cistatic int dapm_widget_list_create(struct snd_soc_dapm_widget_list **list, 113062306a36Sopenharmony_ci struct list_head *widgets) 113162306a36Sopenharmony_ci{ 113262306a36Sopenharmony_ci struct snd_soc_dapm_widget *w; 113362306a36Sopenharmony_ci struct list_head *it; 113462306a36Sopenharmony_ci unsigned int size = 0; 113562306a36Sopenharmony_ci unsigned int i = 0; 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci list_for_each(it, widgets) 113862306a36Sopenharmony_ci size++; 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci *list = kzalloc(struct_size(*list, widgets, size), GFP_KERNEL); 114162306a36Sopenharmony_ci if (*list == NULL) 114262306a36Sopenharmony_ci return -ENOMEM; 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci list_for_each_entry(w, widgets, work_list) 114562306a36Sopenharmony_ci (*list)->widgets[i++] = w; 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci (*list)->num_widgets = i; 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci return 0; 115062306a36Sopenharmony_ci} 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci/* 115362306a36Sopenharmony_ci * Recursively reset the cached number of inputs or outputs for the specified 115462306a36Sopenharmony_ci * widget and all widgets that can be reached via incoming or outcoming paths 115562306a36Sopenharmony_ci * from the widget. 115662306a36Sopenharmony_ci */ 115762306a36Sopenharmony_cistatic void invalidate_paths_ep(struct snd_soc_dapm_widget *widget, 115862306a36Sopenharmony_ci enum snd_soc_dapm_direction dir) 115962306a36Sopenharmony_ci{ 116062306a36Sopenharmony_ci enum snd_soc_dapm_direction rdir = SND_SOC_DAPM_DIR_REVERSE(dir); 116162306a36Sopenharmony_ci struct snd_soc_dapm_path *path; 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci widget->endpoints[dir] = -1; 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci snd_soc_dapm_widget_for_each_path(widget, rdir, path) { 116662306a36Sopenharmony_ci if (path->weak || path->is_supply) 116762306a36Sopenharmony_ci continue; 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci if (path->walking) 117062306a36Sopenharmony_ci return; 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci if (path->connect) { 117362306a36Sopenharmony_ci path->walking = 1; 117462306a36Sopenharmony_ci invalidate_paths_ep(path->node[dir], dir); 117562306a36Sopenharmony_ci path->walking = 0; 117662306a36Sopenharmony_ci } 117762306a36Sopenharmony_ci } 117862306a36Sopenharmony_ci} 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci/* 118162306a36Sopenharmony_ci * Common implementation for is_connected_output_ep() and 118262306a36Sopenharmony_ci * is_connected_input_ep(). The function is inlined since the combined size of 118362306a36Sopenharmony_ci * the two specialized functions is only marginally larger then the size of the 118462306a36Sopenharmony_ci * generic function and at the same time the fast path of the specialized 118562306a36Sopenharmony_ci * functions is significantly smaller than the generic function. 118662306a36Sopenharmony_ci */ 118762306a36Sopenharmony_cistatic __always_inline int is_connected_ep(struct snd_soc_dapm_widget *widget, 118862306a36Sopenharmony_ci struct list_head *list, enum snd_soc_dapm_direction dir, 118962306a36Sopenharmony_ci int (*fn)(struct snd_soc_dapm_widget *, struct list_head *, 119062306a36Sopenharmony_ci bool (*custom_stop_condition)(struct snd_soc_dapm_widget *, 119162306a36Sopenharmony_ci enum snd_soc_dapm_direction)), 119262306a36Sopenharmony_ci bool (*custom_stop_condition)(struct snd_soc_dapm_widget *, 119362306a36Sopenharmony_ci enum snd_soc_dapm_direction)) 119462306a36Sopenharmony_ci{ 119562306a36Sopenharmony_ci enum snd_soc_dapm_direction rdir = SND_SOC_DAPM_DIR_REVERSE(dir); 119662306a36Sopenharmony_ci struct snd_soc_dapm_path *path; 119762306a36Sopenharmony_ci int con = 0; 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci if (widget->endpoints[dir] >= 0) 120062306a36Sopenharmony_ci return widget->endpoints[dir]; 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci DAPM_UPDATE_STAT(widget, path_checks); 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci /* do we need to add this widget to the list ? */ 120562306a36Sopenharmony_ci if (list) 120662306a36Sopenharmony_ci list_add_tail(&widget->work_list, list); 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci if (custom_stop_condition && custom_stop_condition(widget, dir)) { 120962306a36Sopenharmony_ci list = NULL; 121062306a36Sopenharmony_ci custom_stop_condition = NULL; 121162306a36Sopenharmony_ci } 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci if ((widget->is_ep & SND_SOC_DAPM_DIR_TO_EP(dir)) && widget->connected) { 121462306a36Sopenharmony_ci widget->endpoints[dir] = snd_soc_dapm_suspend_check(widget); 121562306a36Sopenharmony_ci return widget->endpoints[dir]; 121662306a36Sopenharmony_ci } 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci snd_soc_dapm_widget_for_each_path(widget, rdir, path) { 121962306a36Sopenharmony_ci DAPM_UPDATE_STAT(widget, neighbour_checks); 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci if (path->weak || path->is_supply) 122262306a36Sopenharmony_ci continue; 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci if (path->walking) 122562306a36Sopenharmony_ci return 1; 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci trace_snd_soc_dapm_path(widget, dir, path); 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci if (path->connect) { 123062306a36Sopenharmony_ci path->walking = 1; 123162306a36Sopenharmony_ci con += fn(path->node[dir], list, custom_stop_condition); 123262306a36Sopenharmony_ci path->walking = 0; 123362306a36Sopenharmony_ci } 123462306a36Sopenharmony_ci } 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci widget->endpoints[dir] = con; 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci return con; 123962306a36Sopenharmony_ci} 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci/* 124262306a36Sopenharmony_ci * Recursively check for a completed path to an active or physically connected 124362306a36Sopenharmony_ci * output widget. Returns number of complete paths. 124462306a36Sopenharmony_ci * 124562306a36Sopenharmony_ci * Optionally, can be supplied with a function acting as a stopping condition. 124662306a36Sopenharmony_ci * This function takes the dapm widget currently being examined and the walk 124762306a36Sopenharmony_ci * direction as an arguments, it should return true if widgets from that point 124862306a36Sopenharmony_ci * in the graph onwards should not be added to the widget list. 124962306a36Sopenharmony_ci */ 125062306a36Sopenharmony_cistatic int is_connected_output_ep(struct snd_soc_dapm_widget *widget, 125162306a36Sopenharmony_ci struct list_head *list, 125262306a36Sopenharmony_ci bool (*custom_stop_condition)(struct snd_soc_dapm_widget *i, 125362306a36Sopenharmony_ci enum snd_soc_dapm_direction)) 125462306a36Sopenharmony_ci{ 125562306a36Sopenharmony_ci return is_connected_ep(widget, list, SND_SOC_DAPM_DIR_OUT, 125662306a36Sopenharmony_ci is_connected_output_ep, custom_stop_condition); 125762306a36Sopenharmony_ci} 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci/* 126062306a36Sopenharmony_ci * Recursively check for a completed path to an active or physically connected 126162306a36Sopenharmony_ci * input widget. Returns number of complete paths. 126262306a36Sopenharmony_ci * 126362306a36Sopenharmony_ci * Optionally, can be supplied with a function acting as a stopping condition. 126462306a36Sopenharmony_ci * This function takes the dapm widget currently being examined and the walk 126562306a36Sopenharmony_ci * direction as an arguments, it should return true if the walk should be 126662306a36Sopenharmony_ci * stopped and false otherwise. 126762306a36Sopenharmony_ci */ 126862306a36Sopenharmony_cistatic int is_connected_input_ep(struct snd_soc_dapm_widget *widget, 126962306a36Sopenharmony_ci struct list_head *list, 127062306a36Sopenharmony_ci bool (*custom_stop_condition)(struct snd_soc_dapm_widget *i, 127162306a36Sopenharmony_ci enum snd_soc_dapm_direction)) 127262306a36Sopenharmony_ci{ 127362306a36Sopenharmony_ci return is_connected_ep(widget, list, SND_SOC_DAPM_DIR_IN, 127462306a36Sopenharmony_ci is_connected_input_ep, custom_stop_condition); 127562306a36Sopenharmony_ci} 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci/** 127862306a36Sopenharmony_ci * snd_soc_dapm_dai_get_connected_widgets - query audio path and it's widgets. 127962306a36Sopenharmony_ci * @dai: the soc DAI. 128062306a36Sopenharmony_ci * @stream: stream direction. 128162306a36Sopenharmony_ci * @list: list of active widgets for this stream. 128262306a36Sopenharmony_ci * @custom_stop_condition: (optional) a function meant to stop the widget graph 128362306a36Sopenharmony_ci * walk based on custom logic. 128462306a36Sopenharmony_ci * 128562306a36Sopenharmony_ci * Queries DAPM graph as to whether a valid audio stream path exists for 128662306a36Sopenharmony_ci * the initial stream specified by name. This takes into account 128762306a36Sopenharmony_ci * current mixer and mux kcontrol settings. Creates list of valid widgets. 128862306a36Sopenharmony_ci * 128962306a36Sopenharmony_ci * Optionally, can be supplied with a function acting as a stopping condition. 129062306a36Sopenharmony_ci * This function takes the dapm widget currently being examined and the walk 129162306a36Sopenharmony_ci * direction as an arguments, it should return true if the walk should be 129262306a36Sopenharmony_ci * stopped and false otherwise. 129362306a36Sopenharmony_ci * 129462306a36Sopenharmony_ci * Returns the number of valid paths or negative error. 129562306a36Sopenharmony_ci */ 129662306a36Sopenharmony_ciint snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, 129762306a36Sopenharmony_ci struct snd_soc_dapm_widget_list **list, 129862306a36Sopenharmony_ci bool (*custom_stop_condition)(struct snd_soc_dapm_widget *, 129962306a36Sopenharmony_ci enum snd_soc_dapm_direction)) 130062306a36Sopenharmony_ci{ 130162306a36Sopenharmony_ci struct snd_soc_card *card = dai->component->card; 130262306a36Sopenharmony_ci struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, stream); 130362306a36Sopenharmony_ci LIST_HEAD(widgets); 130462306a36Sopenharmony_ci int paths; 130562306a36Sopenharmony_ci int ret; 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci snd_soc_dapm_mutex_lock(card); 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci if (stream == SNDRV_PCM_STREAM_PLAYBACK) { 131062306a36Sopenharmony_ci invalidate_paths_ep(w, SND_SOC_DAPM_DIR_OUT); 131162306a36Sopenharmony_ci paths = is_connected_output_ep(w, &widgets, 131262306a36Sopenharmony_ci custom_stop_condition); 131362306a36Sopenharmony_ci } else { 131462306a36Sopenharmony_ci invalidate_paths_ep(w, SND_SOC_DAPM_DIR_IN); 131562306a36Sopenharmony_ci paths = is_connected_input_ep(w, &widgets, 131662306a36Sopenharmony_ci custom_stop_condition); 131762306a36Sopenharmony_ci } 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci /* Drop starting point */ 132062306a36Sopenharmony_ci list_del(widgets.next); 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci ret = dapm_widget_list_create(list, &widgets); 132362306a36Sopenharmony_ci if (ret) 132462306a36Sopenharmony_ci paths = ret; 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_ci trace_snd_soc_dapm_connected(paths, stream); 132762306a36Sopenharmony_ci snd_soc_dapm_mutex_unlock(card); 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci return paths; 133062306a36Sopenharmony_ci} 133162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_dai_get_connected_widgets); 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_civoid snd_soc_dapm_dai_free_widgets(struct snd_soc_dapm_widget_list **list) 133462306a36Sopenharmony_ci{ 133562306a36Sopenharmony_ci dapm_widget_list_free(list); 133662306a36Sopenharmony_ci} 133762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_dai_free_widgets); 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci/* 134062306a36Sopenharmony_ci * Handler for regulator supply widget. 134162306a36Sopenharmony_ci */ 134262306a36Sopenharmony_ciint dapm_regulator_event(struct snd_soc_dapm_widget *w, 134362306a36Sopenharmony_ci struct snd_kcontrol *kcontrol, int event) 134462306a36Sopenharmony_ci{ 134562306a36Sopenharmony_ci int ret; 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_ci soc_dapm_async_complete(w->dapm); 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci if (SND_SOC_DAPM_EVENT_ON(event)) { 135062306a36Sopenharmony_ci if (w->on_val & SND_SOC_DAPM_REGULATOR_BYPASS) { 135162306a36Sopenharmony_ci ret = regulator_allow_bypass(w->regulator, false); 135262306a36Sopenharmony_ci if (ret != 0) 135362306a36Sopenharmony_ci dev_warn(w->dapm->dev, 135462306a36Sopenharmony_ci "ASoC: Failed to unbypass %s: %d\n", 135562306a36Sopenharmony_ci w->name, ret); 135662306a36Sopenharmony_ci } 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci return regulator_enable(w->regulator); 135962306a36Sopenharmony_ci } else { 136062306a36Sopenharmony_ci if (w->on_val & SND_SOC_DAPM_REGULATOR_BYPASS) { 136162306a36Sopenharmony_ci ret = regulator_allow_bypass(w->regulator, true); 136262306a36Sopenharmony_ci if (ret != 0) 136362306a36Sopenharmony_ci dev_warn(w->dapm->dev, 136462306a36Sopenharmony_ci "ASoC: Failed to bypass %s: %d\n", 136562306a36Sopenharmony_ci w->name, ret); 136662306a36Sopenharmony_ci } 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci return regulator_disable_deferred(w->regulator, w->shift); 136962306a36Sopenharmony_ci } 137062306a36Sopenharmony_ci} 137162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dapm_regulator_event); 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_ci/* 137462306a36Sopenharmony_ci * Handler for pinctrl widget. 137562306a36Sopenharmony_ci */ 137662306a36Sopenharmony_ciint dapm_pinctrl_event(struct snd_soc_dapm_widget *w, 137762306a36Sopenharmony_ci struct snd_kcontrol *kcontrol, int event) 137862306a36Sopenharmony_ci{ 137962306a36Sopenharmony_ci struct snd_soc_dapm_pinctrl_priv *priv = w->priv; 138062306a36Sopenharmony_ci struct pinctrl *p = w->pinctrl; 138162306a36Sopenharmony_ci struct pinctrl_state *s; 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci if (!p || !priv) 138462306a36Sopenharmony_ci return -EIO; 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci if (SND_SOC_DAPM_EVENT_ON(event)) 138762306a36Sopenharmony_ci s = pinctrl_lookup_state(p, priv->active_state); 138862306a36Sopenharmony_ci else 138962306a36Sopenharmony_ci s = pinctrl_lookup_state(p, priv->sleep_state); 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_ci if (IS_ERR(s)) 139262306a36Sopenharmony_ci return PTR_ERR(s); 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_ci return pinctrl_select_state(p, s); 139562306a36Sopenharmony_ci} 139662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dapm_pinctrl_event); 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ci/* 139962306a36Sopenharmony_ci * Handler for clock supply widget. 140062306a36Sopenharmony_ci */ 140162306a36Sopenharmony_ciint dapm_clock_event(struct snd_soc_dapm_widget *w, 140262306a36Sopenharmony_ci struct snd_kcontrol *kcontrol, int event) 140362306a36Sopenharmony_ci{ 140462306a36Sopenharmony_ci if (!w->clk) 140562306a36Sopenharmony_ci return -EIO; 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_ci soc_dapm_async_complete(w->dapm); 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ci if (SND_SOC_DAPM_EVENT_ON(event)) { 141062306a36Sopenharmony_ci return clk_prepare_enable(w->clk); 141162306a36Sopenharmony_ci } else { 141262306a36Sopenharmony_ci clk_disable_unprepare(w->clk); 141362306a36Sopenharmony_ci return 0; 141462306a36Sopenharmony_ci } 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci return 0; 141762306a36Sopenharmony_ci} 141862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dapm_clock_event); 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_cistatic int dapm_widget_power_check(struct snd_soc_dapm_widget *w) 142162306a36Sopenharmony_ci{ 142262306a36Sopenharmony_ci if (w->power_checked) 142362306a36Sopenharmony_ci return w->new_power; 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ci if (w->force) 142662306a36Sopenharmony_ci w->new_power = 1; 142762306a36Sopenharmony_ci else 142862306a36Sopenharmony_ci w->new_power = w->power_check(w); 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci w->power_checked = true; 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_ci return w->new_power; 143362306a36Sopenharmony_ci} 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci/* Generic check to see if a widget should be powered. */ 143662306a36Sopenharmony_cistatic int dapm_generic_check_power(struct snd_soc_dapm_widget *w) 143762306a36Sopenharmony_ci{ 143862306a36Sopenharmony_ci int in, out; 143962306a36Sopenharmony_ci 144062306a36Sopenharmony_ci DAPM_UPDATE_STAT(w, power_checks); 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ci in = is_connected_input_ep(w, NULL, NULL); 144362306a36Sopenharmony_ci out = is_connected_output_ep(w, NULL, NULL); 144462306a36Sopenharmony_ci return out != 0 && in != 0; 144562306a36Sopenharmony_ci} 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_ci/* Check to see if a power supply is needed */ 144862306a36Sopenharmony_cistatic int dapm_supply_check_power(struct snd_soc_dapm_widget *w) 144962306a36Sopenharmony_ci{ 145062306a36Sopenharmony_ci struct snd_soc_dapm_path *path; 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci DAPM_UPDATE_STAT(w, power_checks); 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_ci /* Check if one of our outputs is connected */ 145562306a36Sopenharmony_ci snd_soc_dapm_widget_for_each_sink_path(w, path) { 145662306a36Sopenharmony_ci DAPM_UPDATE_STAT(w, neighbour_checks); 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_ci if (path->weak) 145962306a36Sopenharmony_ci continue; 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ci if (path->connected && 146262306a36Sopenharmony_ci !path->connected(path->source, path->sink)) 146362306a36Sopenharmony_ci continue; 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci if (dapm_widget_power_check(path->sink)) 146662306a36Sopenharmony_ci return 1; 146762306a36Sopenharmony_ci } 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ci return 0; 147062306a36Sopenharmony_ci} 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_cistatic int dapm_always_on_check_power(struct snd_soc_dapm_widget *w) 147362306a36Sopenharmony_ci{ 147462306a36Sopenharmony_ci return w->connected; 147562306a36Sopenharmony_ci} 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_cistatic int dapm_seq_compare(struct snd_soc_dapm_widget *a, 147862306a36Sopenharmony_ci struct snd_soc_dapm_widget *b, 147962306a36Sopenharmony_ci bool power_up) 148062306a36Sopenharmony_ci{ 148162306a36Sopenharmony_ci int *sort; 148262306a36Sopenharmony_ci 148362306a36Sopenharmony_ci BUILD_BUG_ON(ARRAY_SIZE(dapm_up_seq) != SND_SOC_DAPM_TYPE_COUNT); 148462306a36Sopenharmony_ci BUILD_BUG_ON(ARRAY_SIZE(dapm_down_seq) != SND_SOC_DAPM_TYPE_COUNT); 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_ci if (power_up) 148762306a36Sopenharmony_ci sort = dapm_up_seq; 148862306a36Sopenharmony_ci else 148962306a36Sopenharmony_ci sort = dapm_down_seq; 149062306a36Sopenharmony_ci 149162306a36Sopenharmony_ci WARN_ONCE(sort[a->id] == 0, "offset a->id %d not initialized\n", a->id); 149262306a36Sopenharmony_ci WARN_ONCE(sort[b->id] == 0, "offset b->id %d not initialized\n", b->id); 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci if (sort[a->id] != sort[b->id]) 149562306a36Sopenharmony_ci return sort[a->id] - sort[b->id]; 149662306a36Sopenharmony_ci if (a->subseq != b->subseq) { 149762306a36Sopenharmony_ci if (power_up) 149862306a36Sopenharmony_ci return a->subseq - b->subseq; 149962306a36Sopenharmony_ci else 150062306a36Sopenharmony_ci return b->subseq - a->subseq; 150162306a36Sopenharmony_ci } 150262306a36Sopenharmony_ci if (a->reg != b->reg) 150362306a36Sopenharmony_ci return a->reg - b->reg; 150462306a36Sopenharmony_ci if (a->dapm != b->dapm) 150562306a36Sopenharmony_ci return (unsigned long)a->dapm - (unsigned long)b->dapm; 150662306a36Sopenharmony_ci 150762306a36Sopenharmony_ci return 0; 150862306a36Sopenharmony_ci} 150962306a36Sopenharmony_ci 151062306a36Sopenharmony_ci/* Insert a widget in order into a DAPM power sequence. */ 151162306a36Sopenharmony_cistatic void dapm_seq_insert(struct snd_soc_dapm_widget *new_widget, 151262306a36Sopenharmony_ci struct list_head *list, 151362306a36Sopenharmony_ci bool power_up) 151462306a36Sopenharmony_ci{ 151562306a36Sopenharmony_ci struct snd_soc_dapm_widget *w; 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_ci list_for_each_entry(w, list, power_list) 151862306a36Sopenharmony_ci if (dapm_seq_compare(new_widget, w, power_up) < 0) { 151962306a36Sopenharmony_ci list_add_tail(&new_widget->power_list, &w->power_list); 152062306a36Sopenharmony_ci return; 152162306a36Sopenharmony_ci } 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_ci list_add_tail(&new_widget->power_list, list); 152462306a36Sopenharmony_ci} 152562306a36Sopenharmony_ci 152662306a36Sopenharmony_cistatic void dapm_seq_check_event(struct snd_soc_card *card, 152762306a36Sopenharmony_ci struct snd_soc_dapm_widget *w, int event) 152862306a36Sopenharmony_ci{ 152962306a36Sopenharmony_ci const char *ev_name; 153062306a36Sopenharmony_ci int power; 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_ci switch (event) { 153362306a36Sopenharmony_ci case SND_SOC_DAPM_PRE_PMU: 153462306a36Sopenharmony_ci ev_name = "PRE_PMU"; 153562306a36Sopenharmony_ci power = 1; 153662306a36Sopenharmony_ci break; 153762306a36Sopenharmony_ci case SND_SOC_DAPM_POST_PMU: 153862306a36Sopenharmony_ci ev_name = "POST_PMU"; 153962306a36Sopenharmony_ci power = 1; 154062306a36Sopenharmony_ci break; 154162306a36Sopenharmony_ci case SND_SOC_DAPM_PRE_PMD: 154262306a36Sopenharmony_ci ev_name = "PRE_PMD"; 154362306a36Sopenharmony_ci power = 0; 154462306a36Sopenharmony_ci break; 154562306a36Sopenharmony_ci case SND_SOC_DAPM_POST_PMD: 154662306a36Sopenharmony_ci ev_name = "POST_PMD"; 154762306a36Sopenharmony_ci power = 0; 154862306a36Sopenharmony_ci break; 154962306a36Sopenharmony_ci case SND_SOC_DAPM_WILL_PMU: 155062306a36Sopenharmony_ci ev_name = "WILL_PMU"; 155162306a36Sopenharmony_ci power = 1; 155262306a36Sopenharmony_ci break; 155362306a36Sopenharmony_ci case SND_SOC_DAPM_WILL_PMD: 155462306a36Sopenharmony_ci ev_name = "WILL_PMD"; 155562306a36Sopenharmony_ci power = 0; 155662306a36Sopenharmony_ci break; 155762306a36Sopenharmony_ci default: 155862306a36Sopenharmony_ci WARN(1, "Unknown event %d\n", event); 155962306a36Sopenharmony_ci return; 156062306a36Sopenharmony_ci } 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ci if (w->new_power != power) 156362306a36Sopenharmony_ci return; 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci if (w->event && (w->event_flags & event)) { 156662306a36Sopenharmony_ci int ret; 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_ci pop_dbg(w->dapm->dev, card->pop_time, "pop test : %s %s\n", 156962306a36Sopenharmony_ci w->name, ev_name); 157062306a36Sopenharmony_ci soc_dapm_async_complete(w->dapm); 157162306a36Sopenharmony_ci trace_snd_soc_dapm_widget_event_start(w, event); 157262306a36Sopenharmony_ci ret = w->event(w, NULL, event); 157362306a36Sopenharmony_ci trace_snd_soc_dapm_widget_event_done(w, event); 157462306a36Sopenharmony_ci if (ret < 0) 157562306a36Sopenharmony_ci dev_err(w->dapm->dev, "ASoC: %s: %s event failed: %d\n", 157662306a36Sopenharmony_ci ev_name, w->name, ret); 157762306a36Sopenharmony_ci } 157862306a36Sopenharmony_ci} 157962306a36Sopenharmony_ci 158062306a36Sopenharmony_ci/* Apply the coalesced changes from a DAPM sequence */ 158162306a36Sopenharmony_cistatic void dapm_seq_run_coalesced(struct snd_soc_card *card, 158262306a36Sopenharmony_ci struct list_head *pending) 158362306a36Sopenharmony_ci{ 158462306a36Sopenharmony_ci struct snd_soc_dapm_context *dapm; 158562306a36Sopenharmony_ci struct snd_soc_dapm_widget *w; 158662306a36Sopenharmony_ci int reg; 158762306a36Sopenharmony_ci unsigned int value = 0; 158862306a36Sopenharmony_ci unsigned int mask = 0; 158962306a36Sopenharmony_ci 159062306a36Sopenharmony_ci w = list_first_entry(pending, struct snd_soc_dapm_widget, power_list); 159162306a36Sopenharmony_ci reg = w->reg; 159262306a36Sopenharmony_ci dapm = w->dapm; 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci list_for_each_entry(w, pending, power_list) { 159562306a36Sopenharmony_ci WARN_ON(reg != w->reg || dapm != w->dapm); 159662306a36Sopenharmony_ci w->power = w->new_power; 159762306a36Sopenharmony_ci 159862306a36Sopenharmony_ci mask |= w->mask << w->shift; 159962306a36Sopenharmony_ci if (w->power) 160062306a36Sopenharmony_ci value |= w->on_val << w->shift; 160162306a36Sopenharmony_ci else 160262306a36Sopenharmony_ci value |= w->off_val << w->shift; 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_ci pop_dbg(dapm->dev, card->pop_time, 160562306a36Sopenharmony_ci "pop test : Queue %s: reg=0x%x, 0x%x/0x%x\n", 160662306a36Sopenharmony_ci w->name, reg, value, mask); 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci /* Check for events */ 160962306a36Sopenharmony_ci dapm_seq_check_event(card, w, SND_SOC_DAPM_PRE_PMU); 161062306a36Sopenharmony_ci dapm_seq_check_event(card, w, SND_SOC_DAPM_PRE_PMD); 161162306a36Sopenharmony_ci } 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_ci if (reg >= 0) { 161462306a36Sopenharmony_ci /* Any widget will do, they should all be updating the 161562306a36Sopenharmony_ci * same register. 161662306a36Sopenharmony_ci */ 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_ci pop_dbg(dapm->dev, card->pop_time, 161962306a36Sopenharmony_ci "pop test : Applying 0x%x/0x%x to %x in %dms\n", 162062306a36Sopenharmony_ci value, mask, reg, card->pop_time); 162162306a36Sopenharmony_ci pop_wait(card->pop_time); 162262306a36Sopenharmony_ci soc_dapm_update_bits(dapm, reg, mask, value); 162362306a36Sopenharmony_ci } 162462306a36Sopenharmony_ci 162562306a36Sopenharmony_ci list_for_each_entry(w, pending, power_list) { 162662306a36Sopenharmony_ci dapm_seq_check_event(card, w, SND_SOC_DAPM_POST_PMU); 162762306a36Sopenharmony_ci dapm_seq_check_event(card, w, SND_SOC_DAPM_POST_PMD); 162862306a36Sopenharmony_ci } 162962306a36Sopenharmony_ci} 163062306a36Sopenharmony_ci 163162306a36Sopenharmony_ci/* Apply a DAPM power sequence. 163262306a36Sopenharmony_ci * 163362306a36Sopenharmony_ci * We walk over a pre-sorted list of widgets to apply power to. In 163462306a36Sopenharmony_ci * order to minimise the number of writes to the device required 163562306a36Sopenharmony_ci * multiple widgets will be updated in a single write where possible. 163662306a36Sopenharmony_ci * Currently anything that requires more than a single write is not 163762306a36Sopenharmony_ci * handled. 163862306a36Sopenharmony_ci */ 163962306a36Sopenharmony_cistatic void dapm_seq_run(struct snd_soc_card *card, 164062306a36Sopenharmony_ci struct list_head *list, int event, bool power_up) 164162306a36Sopenharmony_ci{ 164262306a36Sopenharmony_ci struct snd_soc_dapm_widget *w, *n; 164362306a36Sopenharmony_ci struct snd_soc_dapm_context *d; 164462306a36Sopenharmony_ci LIST_HEAD(pending); 164562306a36Sopenharmony_ci int cur_sort = -1; 164662306a36Sopenharmony_ci int cur_subseq = -1; 164762306a36Sopenharmony_ci int cur_reg = SND_SOC_NOPM; 164862306a36Sopenharmony_ci struct snd_soc_dapm_context *cur_dapm = NULL; 164962306a36Sopenharmony_ci int i; 165062306a36Sopenharmony_ci int *sort; 165162306a36Sopenharmony_ci 165262306a36Sopenharmony_ci if (power_up) 165362306a36Sopenharmony_ci sort = dapm_up_seq; 165462306a36Sopenharmony_ci else 165562306a36Sopenharmony_ci sort = dapm_down_seq; 165662306a36Sopenharmony_ci 165762306a36Sopenharmony_ci list_for_each_entry_safe(w, n, list, power_list) { 165862306a36Sopenharmony_ci int ret = 0; 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci /* Do we need to apply any queued changes? */ 166162306a36Sopenharmony_ci if (sort[w->id] != cur_sort || w->reg != cur_reg || 166262306a36Sopenharmony_ci w->dapm != cur_dapm || w->subseq != cur_subseq) { 166362306a36Sopenharmony_ci if (!list_empty(&pending)) 166462306a36Sopenharmony_ci dapm_seq_run_coalesced(card, &pending); 166562306a36Sopenharmony_ci 166662306a36Sopenharmony_ci if (cur_dapm && cur_dapm->component) { 166762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++) 166862306a36Sopenharmony_ci if (sort[i] == cur_sort) 166962306a36Sopenharmony_ci snd_soc_component_seq_notifier( 167062306a36Sopenharmony_ci cur_dapm->component, 167162306a36Sopenharmony_ci i, cur_subseq); 167262306a36Sopenharmony_ci } 167362306a36Sopenharmony_ci 167462306a36Sopenharmony_ci if (cur_dapm && w->dapm != cur_dapm) 167562306a36Sopenharmony_ci soc_dapm_async_complete(cur_dapm); 167662306a36Sopenharmony_ci 167762306a36Sopenharmony_ci INIT_LIST_HEAD(&pending); 167862306a36Sopenharmony_ci cur_sort = -1; 167962306a36Sopenharmony_ci cur_subseq = INT_MIN; 168062306a36Sopenharmony_ci cur_reg = SND_SOC_NOPM; 168162306a36Sopenharmony_ci cur_dapm = NULL; 168262306a36Sopenharmony_ci } 168362306a36Sopenharmony_ci 168462306a36Sopenharmony_ci switch (w->id) { 168562306a36Sopenharmony_ci case snd_soc_dapm_pre: 168662306a36Sopenharmony_ci if (!w->event) 168762306a36Sopenharmony_ci continue; 168862306a36Sopenharmony_ci 168962306a36Sopenharmony_ci if (event == SND_SOC_DAPM_STREAM_START) 169062306a36Sopenharmony_ci ret = w->event(w, 169162306a36Sopenharmony_ci NULL, SND_SOC_DAPM_PRE_PMU); 169262306a36Sopenharmony_ci else if (event == SND_SOC_DAPM_STREAM_STOP) 169362306a36Sopenharmony_ci ret = w->event(w, 169462306a36Sopenharmony_ci NULL, SND_SOC_DAPM_PRE_PMD); 169562306a36Sopenharmony_ci break; 169662306a36Sopenharmony_ci 169762306a36Sopenharmony_ci case snd_soc_dapm_post: 169862306a36Sopenharmony_ci if (!w->event) 169962306a36Sopenharmony_ci continue; 170062306a36Sopenharmony_ci 170162306a36Sopenharmony_ci if (event == SND_SOC_DAPM_STREAM_START) 170262306a36Sopenharmony_ci ret = w->event(w, 170362306a36Sopenharmony_ci NULL, SND_SOC_DAPM_POST_PMU); 170462306a36Sopenharmony_ci else if (event == SND_SOC_DAPM_STREAM_STOP) 170562306a36Sopenharmony_ci ret = w->event(w, 170662306a36Sopenharmony_ci NULL, SND_SOC_DAPM_POST_PMD); 170762306a36Sopenharmony_ci break; 170862306a36Sopenharmony_ci 170962306a36Sopenharmony_ci default: 171062306a36Sopenharmony_ci /* Queue it up for application */ 171162306a36Sopenharmony_ci cur_sort = sort[w->id]; 171262306a36Sopenharmony_ci cur_subseq = w->subseq; 171362306a36Sopenharmony_ci cur_reg = w->reg; 171462306a36Sopenharmony_ci cur_dapm = w->dapm; 171562306a36Sopenharmony_ci list_move(&w->power_list, &pending); 171662306a36Sopenharmony_ci break; 171762306a36Sopenharmony_ci } 171862306a36Sopenharmony_ci 171962306a36Sopenharmony_ci if (ret < 0) 172062306a36Sopenharmony_ci dev_err(w->dapm->dev, 172162306a36Sopenharmony_ci "ASoC: Failed to apply widget power: %d\n", ret); 172262306a36Sopenharmony_ci } 172362306a36Sopenharmony_ci 172462306a36Sopenharmony_ci if (!list_empty(&pending)) 172562306a36Sopenharmony_ci dapm_seq_run_coalesced(card, &pending); 172662306a36Sopenharmony_ci 172762306a36Sopenharmony_ci if (cur_dapm && cur_dapm->component) { 172862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++) 172962306a36Sopenharmony_ci if (sort[i] == cur_sort) 173062306a36Sopenharmony_ci snd_soc_component_seq_notifier( 173162306a36Sopenharmony_ci cur_dapm->component, 173262306a36Sopenharmony_ci i, cur_subseq); 173362306a36Sopenharmony_ci } 173462306a36Sopenharmony_ci 173562306a36Sopenharmony_ci for_each_card_dapms(card, d) 173662306a36Sopenharmony_ci soc_dapm_async_complete(d); 173762306a36Sopenharmony_ci} 173862306a36Sopenharmony_ci 173962306a36Sopenharmony_cistatic void dapm_widget_update(struct snd_soc_card *card) 174062306a36Sopenharmony_ci{ 174162306a36Sopenharmony_ci struct snd_soc_dapm_update *update = card->update; 174262306a36Sopenharmony_ci struct snd_soc_dapm_widget_list *wlist; 174362306a36Sopenharmony_ci struct snd_soc_dapm_widget *w = NULL; 174462306a36Sopenharmony_ci unsigned int wi; 174562306a36Sopenharmony_ci int ret; 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_ci if (!update || !dapm_kcontrol_is_powered(update->kcontrol)) 174862306a36Sopenharmony_ci return; 174962306a36Sopenharmony_ci 175062306a36Sopenharmony_ci wlist = dapm_kcontrol_get_wlist(update->kcontrol); 175162306a36Sopenharmony_ci 175262306a36Sopenharmony_ci for_each_dapm_widgets(wlist, wi, w) { 175362306a36Sopenharmony_ci if (w->event && (w->event_flags & SND_SOC_DAPM_PRE_REG)) { 175462306a36Sopenharmony_ci ret = w->event(w, update->kcontrol, SND_SOC_DAPM_PRE_REG); 175562306a36Sopenharmony_ci if (ret != 0) 175662306a36Sopenharmony_ci dev_err(w->dapm->dev, "ASoC: %s DAPM pre-event failed: %d\n", 175762306a36Sopenharmony_ci w->name, ret); 175862306a36Sopenharmony_ci } 175962306a36Sopenharmony_ci } 176062306a36Sopenharmony_ci 176162306a36Sopenharmony_ci if (!w) 176262306a36Sopenharmony_ci return; 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_ci ret = soc_dapm_update_bits(w->dapm, update->reg, update->mask, 176562306a36Sopenharmony_ci update->val); 176662306a36Sopenharmony_ci if (ret < 0) 176762306a36Sopenharmony_ci dev_err(w->dapm->dev, "ASoC: %s DAPM update failed: %d\n", 176862306a36Sopenharmony_ci w->name, ret); 176962306a36Sopenharmony_ci 177062306a36Sopenharmony_ci if (update->has_second_set) { 177162306a36Sopenharmony_ci ret = soc_dapm_update_bits(w->dapm, update->reg2, 177262306a36Sopenharmony_ci update->mask2, update->val2); 177362306a36Sopenharmony_ci if (ret < 0) 177462306a36Sopenharmony_ci dev_err(w->dapm->dev, 177562306a36Sopenharmony_ci "ASoC: %s DAPM update failed: %d\n", 177662306a36Sopenharmony_ci w->name, ret); 177762306a36Sopenharmony_ci } 177862306a36Sopenharmony_ci 177962306a36Sopenharmony_ci for_each_dapm_widgets(wlist, wi, w) { 178062306a36Sopenharmony_ci if (w->event && (w->event_flags & SND_SOC_DAPM_POST_REG)) { 178162306a36Sopenharmony_ci ret = w->event(w, update->kcontrol, SND_SOC_DAPM_POST_REG); 178262306a36Sopenharmony_ci if (ret != 0) 178362306a36Sopenharmony_ci dev_err(w->dapm->dev, "ASoC: %s DAPM post-event failed: %d\n", 178462306a36Sopenharmony_ci w->name, ret); 178562306a36Sopenharmony_ci } 178662306a36Sopenharmony_ci } 178762306a36Sopenharmony_ci} 178862306a36Sopenharmony_ci 178962306a36Sopenharmony_ci/* Async callback run prior to DAPM sequences - brings to _PREPARE if 179062306a36Sopenharmony_ci * they're changing state. 179162306a36Sopenharmony_ci */ 179262306a36Sopenharmony_cistatic void dapm_pre_sequence_async(void *data, async_cookie_t cookie) 179362306a36Sopenharmony_ci{ 179462306a36Sopenharmony_ci struct snd_soc_dapm_context *d = data; 179562306a36Sopenharmony_ci int ret; 179662306a36Sopenharmony_ci 179762306a36Sopenharmony_ci /* If we're off and we're not supposed to go into STANDBY */ 179862306a36Sopenharmony_ci if (d->bias_level == SND_SOC_BIAS_OFF && 179962306a36Sopenharmony_ci d->target_bias_level != SND_SOC_BIAS_OFF) { 180062306a36Sopenharmony_ci if (d->dev && cookie) 180162306a36Sopenharmony_ci pm_runtime_get_sync(d->dev); 180262306a36Sopenharmony_ci 180362306a36Sopenharmony_ci ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_STANDBY); 180462306a36Sopenharmony_ci if (ret != 0) 180562306a36Sopenharmony_ci dev_err(d->dev, 180662306a36Sopenharmony_ci "ASoC: Failed to turn on bias: %d\n", ret); 180762306a36Sopenharmony_ci } 180862306a36Sopenharmony_ci 180962306a36Sopenharmony_ci /* Prepare for a transition to ON or away from ON */ 181062306a36Sopenharmony_ci if ((d->target_bias_level == SND_SOC_BIAS_ON && 181162306a36Sopenharmony_ci d->bias_level != SND_SOC_BIAS_ON) || 181262306a36Sopenharmony_ci (d->target_bias_level != SND_SOC_BIAS_ON && 181362306a36Sopenharmony_ci d->bias_level == SND_SOC_BIAS_ON)) { 181462306a36Sopenharmony_ci ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_PREPARE); 181562306a36Sopenharmony_ci if (ret != 0) 181662306a36Sopenharmony_ci dev_err(d->dev, 181762306a36Sopenharmony_ci "ASoC: Failed to prepare bias: %d\n", ret); 181862306a36Sopenharmony_ci } 181962306a36Sopenharmony_ci} 182062306a36Sopenharmony_ci 182162306a36Sopenharmony_ci/* Async callback run prior to DAPM sequences - brings to their final 182262306a36Sopenharmony_ci * state. 182362306a36Sopenharmony_ci */ 182462306a36Sopenharmony_cistatic void dapm_post_sequence_async(void *data, async_cookie_t cookie) 182562306a36Sopenharmony_ci{ 182662306a36Sopenharmony_ci struct snd_soc_dapm_context *d = data; 182762306a36Sopenharmony_ci int ret; 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_ci /* If we just powered the last thing off drop to standby bias */ 183062306a36Sopenharmony_ci if (d->bias_level == SND_SOC_BIAS_PREPARE && 183162306a36Sopenharmony_ci (d->target_bias_level == SND_SOC_BIAS_STANDBY || 183262306a36Sopenharmony_ci d->target_bias_level == SND_SOC_BIAS_OFF)) { 183362306a36Sopenharmony_ci ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_STANDBY); 183462306a36Sopenharmony_ci if (ret != 0) 183562306a36Sopenharmony_ci dev_err(d->dev, "ASoC: Failed to apply standby bias: %d\n", 183662306a36Sopenharmony_ci ret); 183762306a36Sopenharmony_ci } 183862306a36Sopenharmony_ci 183962306a36Sopenharmony_ci /* If we're in standby and can support bias off then do that */ 184062306a36Sopenharmony_ci if (d->bias_level == SND_SOC_BIAS_STANDBY && 184162306a36Sopenharmony_ci d->target_bias_level == SND_SOC_BIAS_OFF) { 184262306a36Sopenharmony_ci ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_OFF); 184362306a36Sopenharmony_ci if (ret != 0) 184462306a36Sopenharmony_ci dev_err(d->dev, "ASoC: Failed to turn off bias: %d\n", 184562306a36Sopenharmony_ci ret); 184662306a36Sopenharmony_ci 184762306a36Sopenharmony_ci if (d->dev && cookie) 184862306a36Sopenharmony_ci pm_runtime_put(d->dev); 184962306a36Sopenharmony_ci } 185062306a36Sopenharmony_ci 185162306a36Sopenharmony_ci /* If we just powered up then move to active bias */ 185262306a36Sopenharmony_ci if (d->bias_level == SND_SOC_BIAS_PREPARE && 185362306a36Sopenharmony_ci d->target_bias_level == SND_SOC_BIAS_ON) { 185462306a36Sopenharmony_ci ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_ON); 185562306a36Sopenharmony_ci if (ret != 0) 185662306a36Sopenharmony_ci dev_err(d->dev, "ASoC: Failed to apply active bias: %d\n", 185762306a36Sopenharmony_ci ret); 185862306a36Sopenharmony_ci } 185962306a36Sopenharmony_ci} 186062306a36Sopenharmony_ci 186162306a36Sopenharmony_cistatic void dapm_widget_set_peer_power(struct snd_soc_dapm_widget *peer, 186262306a36Sopenharmony_ci bool power, bool connect) 186362306a36Sopenharmony_ci{ 186462306a36Sopenharmony_ci /* If a connection is being made or broken then that update 186562306a36Sopenharmony_ci * will have marked the peer dirty, otherwise the widgets are 186662306a36Sopenharmony_ci * not connected and this update has no impact. */ 186762306a36Sopenharmony_ci if (!connect) 186862306a36Sopenharmony_ci return; 186962306a36Sopenharmony_ci 187062306a36Sopenharmony_ci /* If the peer is already in the state we're moving to then we 187162306a36Sopenharmony_ci * won't have an impact on it. */ 187262306a36Sopenharmony_ci if (power != peer->power) 187362306a36Sopenharmony_ci dapm_mark_dirty(peer, "peer state change"); 187462306a36Sopenharmony_ci} 187562306a36Sopenharmony_ci 187662306a36Sopenharmony_cistatic void dapm_power_one_widget(struct snd_soc_dapm_widget *w, 187762306a36Sopenharmony_ci struct list_head *up_list, 187862306a36Sopenharmony_ci struct list_head *down_list) 187962306a36Sopenharmony_ci{ 188062306a36Sopenharmony_ci struct snd_soc_dapm_path *path; 188162306a36Sopenharmony_ci int power; 188262306a36Sopenharmony_ci 188362306a36Sopenharmony_ci switch (w->id) { 188462306a36Sopenharmony_ci case snd_soc_dapm_pre: 188562306a36Sopenharmony_ci power = 0; 188662306a36Sopenharmony_ci goto end; 188762306a36Sopenharmony_ci case snd_soc_dapm_post: 188862306a36Sopenharmony_ci power = 1; 188962306a36Sopenharmony_ci goto end; 189062306a36Sopenharmony_ci default: 189162306a36Sopenharmony_ci break; 189262306a36Sopenharmony_ci } 189362306a36Sopenharmony_ci 189462306a36Sopenharmony_ci power = dapm_widget_power_check(w); 189562306a36Sopenharmony_ci 189662306a36Sopenharmony_ci if (w->power == power) 189762306a36Sopenharmony_ci return; 189862306a36Sopenharmony_ci 189962306a36Sopenharmony_ci trace_snd_soc_dapm_widget_power(w, power); 190062306a36Sopenharmony_ci 190162306a36Sopenharmony_ci /* 190262306a36Sopenharmony_ci * If we changed our power state perhaps our neigbours 190362306a36Sopenharmony_ci * changed also. 190462306a36Sopenharmony_ci */ 190562306a36Sopenharmony_ci snd_soc_dapm_widget_for_each_source_path(w, path) 190662306a36Sopenharmony_ci dapm_widget_set_peer_power(path->source, power, path->connect); 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_ci /* 190962306a36Sopenharmony_ci * Supplies can't affect their outputs, only their inputs 191062306a36Sopenharmony_ci */ 191162306a36Sopenharmony_ci if (!w->is_supply) 191262306a36Sopenharmony_ci snd_soc_dapm_widget_for_each_sink_path(w, path) 191362306a36Sopenharmony_ci dapm_widget_set_peer_power(path->sink, power, path->connect); 191462306a36Sopenharmony_ci 191562306a36Sopenharmony_ciend: 191662306a36Sopenharmony_ci if (power) 191762306a36Sopenharmony_ci dapm_seq_insert(w, up_list, true); 191862306a36Sopenharmony_ci else 191962306a36Sopenharmony_ci dapm_seq_insert(w, down_list, false); 192062306a36Sopenharmony_ci} 192162306a36Sopenharmony_ci 192262306a36Sopenharmony_cistatic bool dapm_idle_bias_off(struct snd_soc_dapm_context *dapm) 192362306a36Sopenharmony_ci{ 192462306a36Sopenharmony_ci if (dapm->idle_bias_off) 192562306a36Sopenharmony_ci return true; 192662306a36Sopenharmony_ci 192762306a36Sopenharmony_ci switch (snd_power_get_state(dapm->card->snd_card)) { 192862306a36Sopenharmony_ci case SNDRV_CTL_POWER_D3hot: 192962306a36Sopenharmony_ci case SNDRV_CTL_POWER_D3cold: 193062306a36Sopenharmony_ci return dapm->suspend_bias_off; 193162306a36Sopenharmony_ci default: 193262306a36Sopenharmony_ci break; 193362306a36Sopenharmony_ci } 193462306a36Sopenharmony_ci 193562306a36Sopenharmony_ci return false; 193662306a36Sopenharmony_ci} 193762306a36Sopenharmony_ci 193862306a36Sopenharmony_ci/* 193962306a36Sopenharmony_ci * Scan each dapm widget for complete audio path. 194062306a36Sopenharmony_ci * A complete path is a route that has valid endpoints i.e.:- 194162306a36Sopenharmony_ci * 194262306a36Sopenharmony_ci * o DAC to output pin. 194362306a36Sopenharmony_ci * o Input pin to ADC. 194462306a36Sopenharmony_ci * o Input pin to Output pin (bypass, sidetone) 194562306a36Sopenharmony_ci * o DAC to ADC (loopback). 194662306a36Sopenharmony_ci */ 194762306a36Sopenharmony_cistatic int dapm_power_widgets(struct snd_soc_card *card, int event) 194862306a36Sopenharmony_ci{ 194962306a36Sopenharmony_ci struct snd_soc_dapm_widget *w; 195062306a36Sopenharmony_ci struct snd_soc_dapm_context *d; 195162306a36Sopenharmony_ci LIST_HEAD(up_list); 195262306a36Sopenharmony_ci LIST_HEAD(down_list); 195362306a36Sopenharmony_ci ASYNC_DOMAIN_EXCLUSIVE(async_domain); 195462306a36Sopenharmony_ci enum snd_soc_bias_level bias; 195562306a36Sopenharmony_ci int ret; 195662306a36Sopenharmony_ci 195762306a36Sopenharmony_ci snd_soc_dapm_mutex_assert_held(card); 195862306a36Sopenharmony_ci 195962306a36Sopenharmony_ci trace_snd_soc_dapm_start(card); 196062306a36Sopenharmony_ci 196162306a36Sopenharmony_ci for_each_card_dapms(card, d) { 196262306a36Sopenharmony_ci if (dapm_idle_bias_off(d)) 196362306a36Sopenharmony_ci d->target_bias_level = SND_SOC_BIAS_OFF; 196462306a36Sopenharmony_ci else 196562306a36Sopenharmony_ci d->target_bias_level = SND_SOC_BIAS_STANDBY; 196662306a36Sopenharmony_ci } 196762306a36Sopenharmony_ci 196862306a36Sopenharmony_ci dapm_reset(card); 196962306a36Sopenharmony_ci 197062306a36Sopenharmony_ci /* Check which widgets we need to power and store them in 197162306a36Sopenharmony_ci * lists indicating if they should be powered up or down. We 197262306a36Sopenharmony_ci * only check widgets that have been flagged as dirty but note 197362306a36Sopenharmony_ci * that new widgets may be added to the dirty list while we 197462306a36Sopenharmony_ci * iterate. 197562306a36Sopenharmony_ci */ 197662306a36Sopenharmony_ci list_for_each_entry(w, &card->dapm_dirty, dirty) { 197762306a36Sopenharmony_ci dapm_power_one_widget(w, &up_list, &down_list); 197862306a36Sopenharmony_ci } 197962306a36Sopenharmony_ci 198062306a36Sopenharmony_ci for_each_card_widgets(card, w) { 198162306a36Sopenharmony_ci switch (w->id) { 198262306a36Sopenharmony_ci case snd_soc_dapm_pre: 198362306a36Sopenharmony_ci case snd_soc_dapm_post: 198462306a36Sopenharmony_ci /* These widgets always need to be powered */ 198562306a36Sopenharmony_ci break; 198662306a36Sopenharmony_ci default: 198762306a36Sopenharmony_ci list_del_init(&w->dirty); 198862306a36Sopenharmony_ci break; 198962306a36Sopenharmony_ci } 199062306a36Sopenharmony_ci 199162306a36Sopenharmony_ci if (w->new_power) { 199262306a36Sopenharmony_ci d = w->dapm; 199362306a36Sopenharmony_ci 199462306a36Sopenharmony_ci /* Supplies and micbiases only bring the 199562306a36Sopenharmony_ci * context up to STANDBY as unless something 199662306a36Sopenharmony_ci * else is active and passing audio they 199762306a36Sopenharmony_ci * generally don't require full power. Signal 199862306a36Sopenharmony_ci * generators are virtual pins and have no 199962306a36Sopenharmony_ci * power impact themselves. 200062306a36Sopenharmony_ci */ 200162306a36Sopenharmony_ci switch (w->id) { 200262306a36Sopenharmony_ci case snd_soc_dapm_siggen: 200362306a36Sopenharmony_ci case snd_soc_dapm_vmid: 200462306a36Sopenharmony_ci break; 200562306a36Sopenharmony_ci case snd_soc_dapm_supply: 200662306a36Sopenharmony_ci case snd_soc_dapm_regulator_supply: 200762306a36Sopenharmony_ci case snd_soc_dapm_pinctrl: 200862306a36Sopenharmony_ci case snd_soc_dapm_clock_supply: 200962306a36Sopenharmony_ci case snd_soc_dapm_micbias: 201062306a36Sopenharmony_ci if (d->target_bias_level < SND_SOC_BIAS_STANDBY) 201162306a36Sopenharmony_ci d->target_bias_level = SND_SOC_BIAS_STANDBY; 201262306a36Sopenharmony_ci break; 201362306a36Sopenharmony_ci default: 201462306a36Sopenharmony_ci d->target_bias_level = SND_SOC_BIAS_ON; 201562306a36Sopenharmony_ci break; 201662306a36Sopenharmony_ci } 201762306a36Sopenharmony_ci } 201862306a36Sopenharmony_ci 201962306a36Sopenharmony_ci } 202062306a36Sopenharmony_ci 202162306a36Sopenharmony_ci /* Force all contexts in the card to the same bias state if 202262306a36Sopenharmony_ci * they're not ground referenced. 202362306a36Sopenharmony_ci */ 202462306a36Sopenharmony_ci bias = SND_SOC_BIAS_OFF; 202562306a36Sopenharmony_ci for_each_card_dapms(card, d) 202662306a36Sopenharmony_ci if (d->target_bias_level > bias) 202762306a36Sopenharmony_ci bias = d->target_bias_level; 202862306a36Sopenharmony_ci for_each_card_dapms(card, d) 202962306a36Sopenharmony_ci if (!dapm_idle_bias_off(d)) 203062306a36Sopenharmony_ci d->target_bias_level = bias; 203162306a36Sopenharmony_ci 203262306a36Sopenharmony_ci trace_snd_soc_dapm_walk_done(card); 203362306a36Sopenharmony_ci 203462306a36Sopenharmony_ci /* Run card bias changes at first */ 203562306a36Sopenharmony_ci dapm_pre_sequence_async(&card->dapm, 0); 203662306a36Sopenharmony_ci /* Run other bias changes in parallel */ 203762306a36Sopenharmony_ci for_each_card_dapms(card, d) { 203862306a36Sopenharmony_ci if (d != &card->dapm && d->bias_level != d->target_bias_level) 203962306a36Sopenharmony_ci async_schedule_domain(dapm_pre_sequence_async, d, 204062306a36Sopenharmony_ci &async_domain); 204162306a36Sopenharmony_ci } 204262306a36Sopenharmony_ci async_synchronize_full_domain(&async_domain); 204362306a36Sopenharmony_ci 204462306a36Sopenharmony_ci list_for_each_entry(w, &down_list, power_list) { 204562306a36Sopenharmony_ci dapm_seq_check_event(card, w, SND_SOC_DAPM_WILL_PMD); 204662306a36Sopenharmony_ci } 204762306a36Sopenharmony_ci 204862306a36Sopenharmony_ci list_for_each_entry(w, &up_list, power_list) { 204962306a36Sopenharmony_ci dapm_seq_check_event(card, w, SND_SOC_DAPM_WILL_PMU); 205062306a36Sopenharmony_ci } 205162306a36Sopenharmony_ci 205262306a36Sopenharmony_ci /* Power down widgets first; try to avoid amplifying pops. */ 205362306a36Sopenharmony_ci dapm_seq_run(card, &down_list, event, false); 205462306a36Sopenharmony_ci 205562306a36Sopenharmony_ci dapm_widget_update(card); 205662306a36Sopenharmony_ci 205762306a36Sopenharmony_ci /* Now power up. */ 205862306a36Sopenharmony_ci dapm_seq_run(card, &up_list, event, true); 205962306a36Sopenharmony_ci 206062306a36Sopenharmony_ci /* Run all the bias changes in parallel */ 206162306a36Sopenharmony_ci for_each_card_dapms(card, d) { 206262306a36Sopenharmony_ci if (d != &card->dapm && d->bias_level != d->target_bias_level) 206362306a36Sopenharmony_ci async_schedule_domain(dapm_post_sequence_async, d, 206462306a36Sopenharmony_ci &async_domain); 206562306a36Sopenharmony_ci } 206662306a36Sopenharmony_ci async_synchronize_full_domain(&async_domain); 206762306a36Sopenharmony_ci /* Run card bias changes at last */ 206862306a36Sopenharmony_ci dapm_post_sequence_async(&card->dapm, 0); 206962306a36Sopenharmony_ci 207062306a36Sopenharmony_ci /* do we need to notify any clients that DAPM event is complete */ 207162306a36Sopenharmony_ci for_each_card_dapms(card, d) { 207262306a36Sopenharmony_ci if (!d->component) 207362306a36Sopenharmony_ci continue; 207462306a36Sopenharmony_ci 207562306a36Sopenharmony_ci ret = snd_soc_component_stream_event(d->component, event); 207662306a36Sopenharmony_ci if (ret < 0) 207762306a36Sopenharmony_ci return ret; 207862306a36Sopenharmony_ci } 207962306a36Sopenharmony_ci 208062306a36Sopenharmony_ci pop_dbg(card->dev, card->pop_time, 208162306a36Sopenharmony_ci "DAPM sequencing finished, waiting %dms\n", card->pop_time); 208262306a36Sopenharmony_ci pop_wait(card->pop_time); 208362306a36Sopenharmony_ci 208462306a36Sopenharmony_ci trace_snd_soc_dapm_done(card); 208562306a36Sopenharmony_ci 208662306a36Sopenharmony_ci return 0; 208762306a36Sopenharmony_ci} 208862306a36Sopenharmony_ci 208962306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 209062306a36Sopenharmony_cistatic ssize_t dapm_widget_power_read_file(struct file *file, 209162306a36Sopenharmony_ci char __user *user_buf, 209262306a36Sopenharmony_ci size_t count, loff_t *ppos) 209362306a36Sopenharmony_ci{ 209462306a36Sopenharmony_ci struct snd_soc_dapm_widget *w = file->private_data; 209562306a36Sopenharmony_ci enum snd_soc_dapm_direction dir, rdir; 209662306a36Sopenharmony_ci char *buf; 209762306a36Sopenharmony_ci int in, out; 209862306a36Sopenharmony_ci ssize_t ret; 209962306a36Sopenharmony_ci struct snd_soc_dapm_path *p = NULL; 210062306a36Sopenharmony_ci 210162306a36Sopenharmony_ci buf = kmalloc(PAGE_SIZE, GFP_KERNEL); 210262306a36Sopenharmony_ci if (!buf) 210362306a36Sopenharmony_ci return -ENOMEM; 210462306a36Sopenharmony_ci 210562306a36Sopenharmony_ci snd_soc_dapm_mutex_lock_root(w->dapm); 210662306a36Sopenharmony_ci 210762306a36Sopenharmony_ci /* Supply widgets are not handled by is_connected_{input,output}_ep() */ 210862306a36Sopenharmony_ci if (w->is_supply) { 210962306a36Sopenharmony_ci in = 0; 211062306a36Sopenharmony_ci out = 0; 211162306a36Sopenharmony_ci } else { 211262306a36Sopenharmony_ci in = is_connected_input_ep(w, NULL, NULL); 211362306a36Sopenharmony_ci out = is_connected_output_ep(w, NULL, NULL); 211462306a36Sopenharmony_ci } 211562306a36Sopenharmony_ci 211662306a36Sopenharmony_ci ret = scnprintf(buf, PAGE_SIZE, "%s: %s%s in %d out %d", 211762306a36Sopenharmony_ci w->name, w->power ? "On" : "Off", 211862306a36Sopenharmony_ci w->force ? " (forced)" : "", in, out); 211962306a36Sopenharmony_ci 212062306a36Sopenharmony_ci if (w->reg >= 0) 212162306a36Sopenharmony_ci ret += scnprintf(buf + ret, PAGE_SIZE - ret, 212262306a36Sopenharmony_ci " - R%d(0x%x) mask 0x%x", 212362306a36Sopenharmony_ci w->reg, w->reg, w->mask << w->shift); 212462306a36Sopenharmony_ci 212562306a36Sopenharmony_ci ret += scnprintf(buf + ret, PAGE_SIZE - ret, "\n"); 212662306a36Sopenharmony_ci 212762306a36Sopenharmony_ci if (w->sname) 212862306a36Sopenharmony_ci ret += scnprintf(buf + ret, PAGE_SIZE - ret, " stream %s %s\n", 212962306a36Sopenharmony_ci w->sname, 213062306a36Sopenharmony_ci w->active ? "active" : "inactive"); 213162306a36Sopenharmony_ci 213262306a36Sopenharmony_ci snd_soc_dapm_for_each_direction(dir) { 213362306a36Sopenharmony_ci rdir = SND_SOC_DAPM_DIR_REVERSE(dir); 213462306a36Sopenharmony_ci snd_soc_dapm_widget_for_each_path(w, dir, p) { 213562306a36Sopenharmony_ci if (p->connected && !p->connected(p->source, p->sink)) 213662306a36Sopenharmony_ci continue; 213762306a36Sopenharmony_ci 213862306a36Sopenharmony_ci if (!p->connect) 213962306a36Sopenharmony_ci continue; 214062306a36Sopenharmony_ci 214162306a36Sopenharmony_ci ret += scnprintf(buf + ret, PAGE_SIZE - ret, 214262306a36Sopenharmony_ci " %s \"%s\" \"%s\"\n", 214362306a36Sopenharmony_ci (rdir == SND_SOC_DAPM_DIR_IN) ? "in" : "out", 214462306a36Sopenharmony_ci p->name ? p->name : "static", 214562306a36Sopenharmony_ci p->node[rdir]->name); 214662306a36Sopenharmony_ci } 214762306a36Sopenharmony_ci } 214862306a36Sopenharmony_ci 214962306a36Sopenharmony_ci snd_soc_dapm_mutex_unlock(w->dapm); 215062306a36Sopenharmony_ci 215162306a36Sopenharmony_ci ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); 215262306a36Sopenharmony_ci 215362306a36Sopenharmony_ci kfree(buf); 215462306a36Sopenharmony_ci return ret; 215562306a36Sopenharmony_ci} 215662306a36Sopenharmony_ci 215762306a36Sopenharmony_cistatic const struct file_operations dapm_widget_power_fops = { 215862306a36Sopenharmony_ci .open = simple_open, 215962306a36Sopenharmony_ci .read = dapm_widget_power_read_file, 216062306a36Sopenharmony_ci .llseek = default_llseek, 216162306a36Sopenharmony_ci}; 216262306a36Sopenharmony_ci 216362306a36Sopenharmony_cistatic ssize_t dapm_bias_read_file(struct file *file, char __user *user_buf, 216462306a36Sopenharmony_ci size_t count, loff_t *ppos) 216562306a36Sopenharmony_ci{ 216662306a36Sopenharmony_ci struct snd_soc_dapm_context *dapm = file->private_data; 216762306a36Sopenharmony_ci char *level; 216862306a36Sopenharmony_ci 216962306a36Sopenharmony_ci switch (dapm->bias_level) { 217062306a36Sopenharmony_ci case SND_SOC_BIAS_ON: 217162306a36Sopenharmony_ci level = "On\n"; 217262306a36Sopenharmony_ci break; 217362306a36Sopenharmony_ci case SND_SOC_BIAS_PREPARE: 217462306a36Sopenharmony_ci level = "Prepare\n"; 217562306a36Sopenharmony_ci break; 217662306a36Sopenharmony_ci case SND_SOC_BIAS_STANDBY: 217762306a36Sopenharmony_ci level = "Standby\n"; 217862306a36Sopenharmony_ci break; 217962306a36Sopenharmony_ci case SND_SOC_BIAS_OFF: 218062306a36Sopenharmony_ci level = "Off\n"; 218162306a36Sopenharmony_ci break; 218262306a36Sopenharmony_ci default: 218362306a36Sopenharmony_ci WARN(1, "Unknown bias_level %d\n", dapm->bias_level); 218462306a36Sopenharmony_ci level = "Unknown\n"; 218562306a36Sopenharmony_ci break; 218662306a36Sopenharmony_ci } 218762306a36Sopenharmony_ci 218862306a36Sopenharmony_ci return simple_read_from_buffer(user_buf, count, ppos, level, 218962306a36Sopenharmony_ci strlen(level)); 219062306a36Sopenharmony_ci} 219162306a36Sopenharmony_ci 219262306a36Sopenharmony_cistatic const struct file_operations dapm_bias_fops = { 219362306a36Sopenharmony_ci .open = simple_open, 219462306a36Sopenharmony_ci .read = dapm_bias_read_file, 219562306a36Sopenharmony_ci .llseek = default_llseek, 219662306a36Sopenharmony_ci}; 219762306a36Sopenharmony_ci 219862306a36Sopenharmony_civoid snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm, 219962306a36Sopenharmony_ci struct dentry *parent) 220062306a36Sopenharmony_ci{ 220162306a36Sopenharmony_ci if (!parent || IS_ERR(parent)) 220262306a36Sopenharmony_ci return; 220362306a36Sopenharmony_ci 220462306a36Sopenharmony_ci dapm->debugfs_dapm = debugfs_create_dir("dapm", parent); 220562306a36Sopenharmony_ci 220662306a36Sopenharmony_ci debugfs_create_file("bias_level", 0444, dapm->debugfs_dapm, dapm, 220762306a36Sopenharmony_ci &dapm_bias_fops); 220862306a36Sopenharmony_ci} 220962306a36Sopenharmony_ci 221062306a36Sopenharmony_cistatic void dapm_debugfs_add_widget(struct snd_soc_dapm_widget *w) 221162306a36Sopenharmony_ci{ 221262306a36Sopenharmony_ci struct snd_soc_dapm_context *dapm = w->dapm; 221362306a36Sopenharmony_ci 221462306a36Sopenharmony_ci if (!dapm->debugfs_dapm || !w->name) 221562306a36Sopenharmony_ci return; 221662306a36Sopenharmony_ci 221762306a36Sopenharmony_ci debugfs_create_file(w->name, 0444, dapm->debugfs_dapm, w, 221862306a36Sopenharmony_ci &dapm_widget_power_fops); 221962306a36Sopenharmony_ci} 222062306a36Sopenharmony_ci 222162306a36Sopenharmony_cistatic void dapm_debugfs_free_widget(struct snd_soc_dapm_widget *w) 222262306a36Sopenharmony_ci{ 222362306a36Sopenharmony_ci struct snd_soc_dapm_context *dapm = w->dapm; 222462306a36Sopenharmony_ci 222562306a36Sopenharmony_ci if (!dapm->debugfs_dapm || !w->name) 222662306a36Sopenharmony_ci return; 222762306a36Sopenharmony_ci 222862306a36Sopenharmony_ci debugfs_lookup_and_remove(w->name, dapm->debugfs_dapm); 222962306a36Sopenharmony_ci} 223062306a36Sopenharmony_ci 223162306a36Sopenharmony_cistatic void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm) 223262306a36Sopenharmony_ci{ 223362306a36Sopenharmony_ci debugfs_remove_recursive(dapm->debugfs_dapm); 223462306a36Sopenharmony_ci dapm->debugfs_dapm = NULL; 223562306a36Sopenharmony_ci} 223662306a36Sopenharmony_ci 223762306a36Sopenharmony_ci#else 223862306a36Sopenharmony_civoid snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm, 223962306a36Sopenharmony_ci struct dentry *parent) 224062306a36Sopenharmony_ci{ 224162306a36Sopenharmony_ci} 224262306a36Sopenharmony_ci 224362306a36Sopenharmony_cistatic inline void dapm_debugfs_add_widget(struct snd_soc_dapm_widget *w) 224462306a36Sopenharmony_ci{ 224562306a36Sopenharmony_ci} 224662306a36Sopenharmony_ci 224762306a36Sopenharmony_cistatic inline void dapm_debugfs_free_widget(struct snd_soc_dapm_widget *w) 224862306a36Sopenharmony_ci{ 224962306a36Sopenharmony_ci} 225062306a36Sopenharmony_ci 225162306a36Sopenharmony_cistatic inline void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm) 225262306a36Sopenharmony_ci{ 225362306a36Sopenharmony_ci} 225462306a36Sopenharmony_ci 225562306a36Sopenharmony_ci#endif 225662306a36Sopenharmony_ci 225762306a36Sopenharmony_ci/* 225862306a36Sopenharmony_ci * soc_dapm_connect_path() - Connects or disconnects a path 225962306a36Sopenharmony_ci * @path: The path to update 226062306a36Sopenharmony_ci * @connect: The new connect state of the path. True if the path is connected, 226162306a36Sopenharmony_ci * false if it is disconnected. 226262306a36Sopenharmony_ci * @reason: The reason why the path changed (for debugging only) 226362306a36Sopenharmony_ci */ 226462306a36Sopenharmony_cistatic void soc_dapm_connect_path(struct snd_soc_dapm_path *path, 226562306a36Sopenharmony_ci bool connect, const char *reason) 226662306a36Sopenharmony_ci{ 226762306a36Sopenharmony_ci if (path->connect == connect) 226862306a36Sopenharmony_ci return; 226962306a36Sopenharmony_ci 227062306a36Sopenharmony_ci path->connect = connect; 227162306a36Sopenharmony_ci dapm_mark_dirty(path->source, reason); 227262306a36Sopenharmony_ci dapm_mark_dirty(path->sink, reason); 227362306a36Sopenharmony_ci dapm_path_invalidate(path); 227462306a36Sopenharmony_ci} 227562306a36Sopenharmony_ci 227662306a36Sopenharmony_ci/* test and update the power status of a mux widget */ 227762306a36Sopenharmony_cistatic int soc_dapm_mux_update_power(struct snd_soc_card *card, 227862306a36Sopenharmony_ci struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e) 227962306a36Sopenharmony_ci{ 228062306a36Sopenharmony_ci struct snd_soc_dapm_path *path; 228162306a36Sopenharmony_ci int found = 0; 228262306a36Sopenharmony_ci bool connect; 228362306a36Sopenharmony_ci 228462306a36Sopenharmony_ci snd_soc_dapm_mutex_assert_held(card); 228562306a36Sopenharmony_ci 228662306a36Sopenharmony_ci /* find dapm widget path assoc with kcontrol */ 228762306a36Sopenharmony_ci dapm_kcontrol_for_each_path(path, kcontrol) { 228862306a36Sopenharmony_ci found = 1; 228962306a36Sopenharmony_ci /* we now need to match the string in the enum to the path */ 229062306a36Sopenharmony_ci if (e && !(strcmp(path->name, e->texts[mux]))) 229162306a36Sopenharmony_ci connect = true; 229262306a36Sopenharmony_ci else 229362306a36Sopenharmony_ci connect = false; 229462306a36Sopenharmony_ci 229562306a36Sopenharmony_ci soc_dapm_connect_path(path, connect, "mux update"); 229662306a36Sopenharmony_ci } 229762306a36Sopenharmony_ci 229862306a36Sopenharmony_ci if (found) 229962306a36Sopenharmony_ci dapm_power_widgets(card, SND_SOC_DAPM_STREAM_NOP); 230062306a36Sopenharmony_ci 230162306a36Sopenharmony_ci return found; 230262306a36Sopenharmony_ci} 230362306a36Sopenharmony_ci 230462306a36Sopenharmony_ciint snd_soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm, 230562306a36Sopenharmony_ci struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e, 230662306a36Sopenharmony_ci struct snd_soc_dapm_update *update) 230762306a36Sopenharmony_ci{ 230862306a36Sopenharmony_ci struct snd_soc_card *card = dapm->card; 230962306a36Sopenharmony_ci int ret; 231062306a36Sopenharmony_ci 231162306a36Sopenharmony_ci snd_soc_dapm_mutex_lock(card); 231262306a36Sopenharmony_ci card->update = update; 231362306a36Sopenharmony_ci ret = soc_dapm_mux_update_power(card, kcontrol, mux, e); 231462306a36Sopenharmony_ci card->update = NULL; 231562306a36Sopenharmony_ci snd_soc_dapm_mutex_unlock(card); 231662306a36Sopenharmony_ci if (ret > 0) 231762306a36Sopenharmony_ci snd_soc_dpcm_runtime_update(card); 231862306a36Sopenharmony_ci return ret; 231962306a36Sopenharmony_ci} 232062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_mux_update_power); 232162306a36Sopenharmony_ci 232262306a36Sopenharmony_ci/* test and update the power status of a mixer or switch widget */ 232362306a36Sopenharmony_cistatic int soc_dapm_mixer_update_power(struct snd_soc_card *card, 232462306a36Sopenharmony_ci struct snd_kcontrol *kcontrol, 232562306a36Sopenharmony_ci int connect, int rconnect) 232662306a36Sopenharmony_ci{ 232762306a36Sopenharmony_ci struct snd_soc_dapm_path *path; 232862306a36Sopenharmony_ci int found = 0; 232962306a36Sopenharmony_ci 233062306a36Sopenharmony_ci snd_soc_dapm_mutex_assert_held(card); 233162306a36Sopenharmony_ci 233262306a36Sopenharmony_ci /* find dapm widget path assoc with kcontrol */ 233362306a36Sopenharmony_ci dapm_kcontrol_for_each_path(path, kcontrol) { 233462306a36Sopenharmony_ci /* 233562306a36Sopenharmony_ci * Ideally this function should support any number of 233662306a36Sopenharmony_ci * paths and channels. But since kcontrols only come 233762306a36Sopenharmony_ci * in mono and stereo variants, we are limited to 2 233862306a36Sopenharmony_ci * channels. 233962306a36Sopenharmony_ci * 234062306a36Sopenharmony_ci * The following code assumes for stereo controls the 234162306a36Sopenharmony_ci * first path (when 'found == 0') is the left channel, 234262306a36Sopenharmony_ci * and all remaining paths (when 'found == 1') are the 234362306a36Sopenharmony_ci * right channel. 234462306a36Sopenharmony_ci * 234562306a36Sopenharmony_ci * A stereo control is signified by a valid 'rconnect' 234662306a36Sopenharmony_ci * value, either 0 for unconnected, or >= 0 for connected. 234762306a36Sopenharmony_ci * This is chosen instead of using snd_soc_volsw_is_stereo, 234862306a36Sopenharmony_ci * so that the behavior of snd_soc_dapm_mixer_update_power 234962306a36Sopenharmony_ci * doesn't change even when the kcontrol passed in is 235062306a36Sopenharmony_ci * stereo. 235162306a36Sopenharmony_ci * 235262306a36Sopenharmony_ci * It passes 'connect' as the path connect status for 235362306a36Sopenharmony_ci * the left channel, and 'rconnect' for the right 235462306a36Sopenharmony_ci * channel. 235562306a36Sopenharmony_ci */ 235662306a36Sopenharmony_ci if (found && rconnect >= 0) 235762306a36Sopenharmony_ci soc_dapm_connect_path(path, rconnect, "mixer update"); 235862306a36Sopenharmony_ci else 235962306a36Sopenharmony_ci soc_dapm_connect_path(path, connect, "mixer update"); 236062306a36Sopenharmony_ci found = 1; 236162306a36Sopenharmony_ci } 236262306a36Sopenharmony_ci 236362306a36Sopenharmony_ci if (found) 236462306a36Sopenharmony_ci dapm_power_widgets(card, SND_SOC_DAPM_STREAM_NOP); 236562306a36Sopenharmony_ci 236662306a36Sopenharmony_ci return found; 236762306a36Sopenharmony_ci} 236862306a36Sopenharmony_ci 236962306a36Sopenharmony_ciint snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm, 237062306a36Sopenharmony_ci struct snd_kcontrol *kcontrol, int connect, 237162306a36Sopenharmony_ci struct snd_soc_dapm_update *update) 237262306a36Sopenharmony_ci{ 237362306a36Sopenharmony_ci struct snd_soc_card *card = dapm->card; 237462306a36Sopenharmony_ci int ret; 237562306a36Sopenharmony_ci 237662306a36Sopenharmony_ci snd_soc_dapm_mutex_lock(card); 237762306a36Sopenharmony_ci card->update = update; 237862306a36Sopenharmony_ci ret = soc_dapm_mixer_update_power(card, kcontrol, connect, -1); 237962306a36Sopenharmony_ci card->update = NULL; 238062306a36Sopenharmony_ci snd_soc_dapm_mutex_unlock(card); 238162306a36Sopenharmony_ci if (ret > 0) 238262306a36Sopenharmony_ci snd_soc_dpcm_runtime_update(card); 238362306a36Sopenharmony_ci return ret; 238462306a36Sopenharmony_ci} 238562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_mixer_update_power); 238662306a36Sopenharmony_ci 238762306a36Sopenharmony_cistatic ssize_t dapm_widget_show_component(struct snd_soc_component *cmpnt, 238862306a36Sopenharmony_ci char *buf, int count) 238962306a36Sopenharmony_ci{ 239062306a36Sopenharmony_ci struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt); 239162306a36Sopenharmony_ci struct snd_soc_dapm_widget *w; 239262306a36Sopenharmony_ci char *state = "not set"; 239362306a36Sopenharmony_ci 239462306a36Sopenharmony_ci /* card won't be set for the dummy component, as a spot fix 239562306a36Sopenharmony_ci * we're checking for that case specifically here but in future 239662306a36Sopenharmony_ci * we will ensure that the dummy component looks like others. 239762306a36Sopenharmony_ci */ 239862306a36Sopenharmony_ci if (!cmpnt->card) 239962306a36Sopenharmony_ci return 0; 240062306a36Sopenharmony_ci 240162306a36Sopenharmony_ci for_each_card_widgets(cmpnt->card, w) { 240262306a36Sopenharmony_ci if (w->dapm != dapm) 240362306a36Sopenharmony_ci continue; 240462306a36Sopenharmony_ci 240562306a36Sopenharmony_ci /* only display widgets that burn power */ 240662306a36Sopenharmony_ci switch (w->id) { 240762306a36Sopenharmony_ci case snd_soc_dapm_hp: 240862306a36Sopenharmony_ci case snd_soc_dapm_mic: 240962306a36Sopenharmony_ci case snd_soc_dapm_spk: 241062306a36Sopenharmony_ci case snd_soc_dapm_line: 241162306a36Sopenharmony_ci case snd_soc_dapm_micbias: 241262306a36Sopenharmony_ci case snd_soc_dapm_dac: 241362306a36Sopenharmony_ci case snd_soc_dapm_adc: 241462306a36Sopenharmony_ci case snd_soc_dapm_pga: 241562306a36Sopenharmony_ci case snd_soc_dapm_effect: 241662306a36Sopenharmony_ci case snd_soc_dapm_out_drv: 241762306a36Sopenharmony_ci case snd_soc_dapm_mixer: 241862306a36Sopenharmony_ci case snd_soc_dapm_mixer_named_ctl: 241962306a36Sopenharmony_ci case snd_soc_dapm_supply: 242062306a36Sopenharmony_ci case snd_soc_dapm_regulator_supply: 242162306a36Sopenharmony_ci case snd_soc_dapm_pinctrl: 242262306a36Sopenharmony_ci case snd_soc_dapm_clock_supply: 242362306a36Sopenharmony_ci if (w->name) 242462306a36Sopenharmony_ci count += sysfs_emit_at(buf, count, "%s: %s\n", 242562306a36Sopenharmony_ci w->name, w->power ? "On":"Off"); 242662306a36Sopenharmony_ci break; 242762306a36Sopenharmony_ci default: 242862306a36Sopenharmony_ci break; 242962306a36Sopenharmony_ci } 243062306a36Sopenharmony_ci } 243162306a36Sopenharmony_ci 243262306a36Sopenharmony_ci switch (snd_soc_dapm_get_bias_level(dapm)) { 243362306a36Sopenharmony_ci case SND_SOC_BIAS_ON: 243462306a36Sopenharmony_ci state = "On"; 243562306a36Sopenharmony_ci break; 243662306a36Sopenharmony_ci case SND_SOC_BIAS_PREPARE: 243762306a36Sopenharmony_ci state = "Prepare"; 243862306a36Sopenharmony_ci break; 243962306a36Sopenharmony_ci case SND_SOC_BIAS_STANDBY: 244062306a36Sopenharmony_ci state = "Standby"; 244162306a36Sopenharmony_ci break; 244262306a36Sopenharmony_ci case SND_SOC_BIAS_OFF: 244362306a36Sopenharmony_ci state = "Off"; 244462306a36Sopenharmony_ci break; 244562306a36Sopenharmony_ci } 244662306a36Sopenharmony_ci count += sysfs_emit_at(buf, count, "PM State: %s\n", state); 244762306a36Sopenharmony_ci 244862306a36Sopenharmony_ci return count; 244962306a36Sopenharmony_ci} 245062306a36Sopenharmony_ci 245162306a36Sopenharmony_ci/* show dapm widget status in sys fs */ 245262306a36Sopenharmony_cistatic ssize_t dapm_widget_show(struct device *dev, 245362306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 245462306a36Sopenharmony_ci{ 245562306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev); 245662306a36Sopenharmony_ci struct snd_soc_dai *codec_dai; 245762306a36Sopenharmony_ci int i, count = 0; 245862306a36Sopenharmony_ci 245962306a36Sopenharmony_ci snd_soc_dapm_mutex_lock_root(rtd->card); 246062306a36Sopenharmony_ci 246162306a36Sopenharmony_ci for_each_rtd_codec_dais(rtd, i, codec_dai) { 246262306a36Sopenharmony_ci struct snd_soc_component *cmpnt = codec_dai->component; 246362306a36Sopenharmony_ci 246462306a36Sopenharmony_ci count = dapm_widget_show_component(cmpnt, buf, count); 246562306a36Sopenharmony_ci } 246662306a36Sopenharmony_ci 246762306a36Sopenharmony_ci snd_soc_dapm_mutex_unlock(rtd->card); 246862306a36Sopenharmony_ci 246962306a36Sopenharmony_ci return count; 247062306a36Sopenharmony_ci} 247162306a36Sopenharmony_ci 247262306a36Sopenharmony_cistatic DEVICE_ATTR_RO(dapm_widget); 247362306a36Sopenharmony_ci 247462306a36Sopenharmony_cistruct attribute *soc_dapm_dev_attrs[] = { 247562306a36Sopenharmony_ci &dev_attr_dapm_widget.attr, 247662306a36Sopenharmony_ci NULL 247762306a36Sopenharmony_ci}; 247862306a36Sopenharmony_ci 247962306a36Sopenharmony_cistatic void dapm_free_path(struct snd_soc_dapm_path *path) 248062306a36Sopenharmony_ci{ 248162306a36Sopenharmony_ci list_del(&path->list_node[SND_SOC_DAPM_DIR_IN]); 248262306a36Sopenharmony_ci list_del(&path->list_node[SND_SOC_DAPM_DIR_OUT]); 248362306a36Sopenharmony_ci list_del(&path->list_kcontrol); 248462306a36Sopenharmony_ci list_del(&path->list); 248562306a36Sopenharmony_ci kfree(path); 248662306a36Sopenharmony_ci} 248762306a36Sopenharmony_ci 248862306a36Sopenharmony_ci/** 248962306a36Sopenharmony_ci * snd_soc_dapm_free_widget - Free specified widget 249062306a36Sopenharmony_ci * @w: widget to free 249162306a36Sopenharmony_ci * 249262306a36Sopenharmony_ci * Removes widget from all paths and frees memory occupied by it. 249362306a36Sopenharmony_ci */ 249462306a36Sopenharmony_civoid snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w) 249562306a36Sopenharmony_ci{ 249662306a36Sopenharmony_ci struct snd_soc_dapm_path *p, *next_p; 249762306a36Sopenharmony_ci enum snd_soc_dapm_direction dir; 249862306a36Sopenharmony_ci 249962306a36Sopenharmony_ci if (!w) 250062306a36Sopenharmony_ci return; 250162306a36Sopenharmony_ci 250262306a36Sopenharmony_ci list_del(&w->list); 250362306a36Sopenharmony_ci list_del(&w->dirty); 250462306a36Sopenharmony_ci /* 250562306a36Sopenharmony_ci * remove source and sink paths associated to this widget. 250662306a36Sopenharmony_ci * While removing the path, remove reference to it from both 250762306a36Sopenharmony_ci * source and sink widgets so that path is removed only once. 250862306a36Sopenharmony_ci */ 250962306a36Sopenharmony_ci snd_soc_dapm_for_each_direction(dir) { 251062306a36Sopenharmony_ci snd_soc_dapm_widget_for_each_path_safe(w, dir, p, next_p) 251162306a36Sopenharmony_ci dapm_free_path(p); 251262306a36Sopenharmony_ci } 251362306a36Sopenharmony_ci 251462306a36Sopenharmony_ci dapm_debugfs_free_widget(w); 251562306a36Sopenharmony_ci 251662306a36Sopenharmony_ci kfree(w->kcontrols); 251762306a36Sopenharmony_ci kfree_const(w->name); 251862306a36Sopenharmony_ci kfree_const(w->sname); 251962306a36Sopenharmony_ci kfree(w); 252062306a36Sopenharmony_ci} 252162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_free_widget); 252262306a36Sopenharmony_ci 252362306a36Sopenharmony_ci/* free all dapm widgets and resources */ 252462306a36Sopenharmony_cistatic void dapm_free_widgets(struct snd_soc_dapm_context *dapm) 252562306a36Sopenharmony_ci{ 252662306a36Sopenharmony_ci struct snd_soc_dapm_widget *w, *next_w; 252762306a36Sopenharmony_ci 252862306a36Sopenharmony_ci for_each_card_widgets_safe(dapm->card, w, next_w) { 252962306a36Sopenharmony_ci if (w->dapm != dapm) 253062306a36Sopenharmony_ci continue; 253162306a36Sopenharmony_ci snd_soc_dapm_free_widget(w); 253262306a36Sopenharmony_ci } 253362306a36Sopenharmony_ci 253462306a36Sopenharmony_ci dapm->wcache_sink = NULL; 253562306a36Sopenharmony_ci dapm->wcache_source = NULL; 253662306a36Sopenharmony_ci} 253762306a36Sopenharmony_ci 253862306a36Sopenharmony_cistatic struct snd_soc_dapm_widget *dapm_find_widget( 253962306a36Sopenharmony_ci struct snd_soc_dapm_context *dapm, const char *pin, 254062306a36Sopenharmony_ci bool search_other_contexts) 254162306a36Sopenharmony_ci{ 254262306a36Sopenharmony_ci struct snd_soc_dapm_widget *w; 254362306a36Sopenharmony_ci struct snd_soc_dapm_widget *fallback = NULL; 254462306a36Sopenharmony_ci char prefixed_pin[80]; 254562306a36Sopenharmony_ci const char *pin_name; 254662306a36Sopenharmony_ci const char *prefix = soc_dapm_prefix(dapm); 254762306a36Sopenharmony_ci 254862306a36Sopenharmony_ci if (prefix) { 254962306a36Sopenharmony_ci snprintf(prefixed_pin, sizeof(prefixed_pin), "%s %s", 255062306a36Sopenharmony_ci prefix, pin); 255162306a36Sopenharmony_ci pin_name = prefixed_pin; 255262306a36Sopenharmony_ci } else { 255362306a36Sopenharmony_ci pin_name = pin; 255462306a36Sopenharmony_ci } 255562306a36Sopenharmony_ci 255662306a36Sopenharmony_ci for_each_card_widgets(dapm->card, w) { 255762306a36Sopenharmony_ci if (!strcmp(w->name, pin_name)) { 255862306a36Sopenharmony_ci if (w->dapm == dapm) 255962306a36Sopenharmony_ci return w; 256062306a36Sopenharmony_ci else 256162306a36Sopenharmony_ci fallback = w; 256262306a36Sopenharmony_ci } 256362306a36Sopenharmony_ci } 256462306a36Sopenharmony_ci 256562306a36Sopenharmony_ci if (search_other_contexts) 256662306a36Sopenharmony_ci return fallback; 256762306a36Sopenharmony_ci 256862306a36Sopenharmony_ci return NULL; 256962306a36Sopenharmony_ci} 257062306a36Sopenharmony_ci 257162306a36Sopenharmony_ci/* 257262306a36Sopenharmony_ci * set the DAPM pin status: 257362306a36Sopenharmony_ci * returns 1 when the value has been updated, 0 when unchanged, or a negative 257462306a36Sopenharmony_ci * error code; called from kcontrol put callback 257562306a36Sopenharmony_ci */ 257662306a36Sopenharmony_cistatic int __snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm, 257762306a36Sopenharmony_ci const char *pin, int status) 257862306a36Sopenharmony_ci{ 257962306a36Sopenharmony_ci struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, true); 258062306a36Sopenharmony_ci int ret = 0; 258162306a36Sopenharmony_ci 258262306a36Sopenharmony_ci dapm_assert_locked(dapm); 258362306a36Sopenharmony_ci 258462306a36Sopenharmony_ci if (!w) { 258562306a36Sopenharmony_ci dev_err(dapm->dev, "ASoC: DAPM unknown pin %s\n", pin); 258662306a36Sopenharmony_ci return -EINVAL; 258762306a36Sopenharmony_ci } 258862306a36Sopenharmony_ci 258962306a36Sopenharmony_ci if (w->connected != status) { 259062306a36Sopenharmony_ci dapm_mark_dirty(w, "pin configuration"); 259162306a36Sopenharmony_ci dapm_widget_invalidate_input_paths(w); 259262306a36Sopenharmony_ci dapm_widget_invalidate_output_paths(w); 259362306a36Sopenharmony_ci ret = 1; 259462306a36Sopenharmony_ci } 259562306a36Sopenharmony_ci 259662306a36Sopenharmony_ci w->connected = status; 259762306a36Sopenharmony_ci if (status == 0) 259862306a36Sopenharmony_ci w->force = 0; 259962306a36Sopenharmony_ci 260062306a36Sopenharmony_ci return ret; 260162306a36Sopenharmony_ci} 260262306a36Sopenharmony_ci 260362306a36Sopenharmony_ci/* 260462306a36Sopenharmony_ci * similar as __snd_soc_dapm_set_pin(), but returns 0 when successful; 260562306a36Sopenharmony_ci * called from several API functions below 260662306a36Sopenharmony_ci */ 260762306a36Sopenharmony_cistatic int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm, 260862306a36Sopenharmony_ci const char *pin, int status) 260962306a36Sopenharmony_ci{ 261062306a36Sopenharmony_ci int ret = __snd_soc_dapm_set_pin(dapm, pin, status); 261162306a36Sopenharmony_ci 261262306a36Sopenharmony_ci return ret < 0 ? ret : 0; 261362306a36Sopenharmony_ci} 261462306a36Sopenharmony_ci 261562306a36Sopenharmony_ci/** 261662306a36Sopenharmony_ci * snd_soc_dapm_sync_unlocked - scan and power dapm paths 261762306a36Sopenharmony_ci * @dapm: DAPM context 261862306a36Sopenharmony_ci * 261962306a36Sopenharmony_ci * Walks all dapm audio paths and powers widgets according to their 262062306a36Sopenharmony_ci * stream or path usage. 262162306a36Sopenharmony_ci * 262262306a36Sopenharmony_ci * Requires external locking. 262362306a36Sopenharmony_ci * 262462306a36Sopenharmony_ci * Returns 0 for success. 262562306a36Sopenharmony_ci */ 262662306a36Sopenharmony_ciint snd_soc_dapm_sync_unlocked(struct snd_soc_dapm_context *dapm) 262762306a36Sopenharmony_ci{ 262862306a36Sopenharmony_ci /* 262962306a36Sopenharmony_ci * Suppress early reports (eg, jacks syncing their state) to avoid 263062306a36Sopenharmony_ci * silly DAPM runs during card startup. 263162306a36Sopenharmony_ci */ 263262306a36Sopenharmony_ci if (!snd_soc_card_is_instantiated(dapm->card)) 263362306a36Sopenharmony_ci return 0; 263462306a36Sopenharmony_ci 263562306a36Sopenharmony_ci return dapm_power_widgets(dapm->card, SND_SOC_DAPM_STREAM_NOP); 263662306a36Sopenharmony_ci} 263762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_sync_unlocked); 263862306a36Sopenharmony_ci 263962306a36Sopenharmony_ci/** 264062306a36Sopenharmony_ci * snd_soc_dapm_sync - scan and power dapm paths 264162306a36Sopenharmony_ci * @dapm: DAPM context 264262306a36Sopenharmony_ci * 264362306a36Sopenharmony_ci * Walks all dapm audio paths and powers widgets according to their 264462306a36Sopenharmony_ci * stream or path usage. 264562306a36Sopenharmony_ci * 264662306a36Sopenharmony_ci * Returns 0 for success. 264762306a36Sopenharmony_ci */ 264862306a36Sopenharmony_ciint snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm) 264962306a36Sopenharmony_ci{ 265062306a36Sopenharmony_ci int ret; 265162306a36Sopenharmony_ci 265262306a36Sopenharmony_ci snd_soc_dapm_mutex_lock(dapm); 265362306a36Sopenharmony_ci ret = snd_soc_dapm_sync_unlocked(dapm); 265462306a36Sopenharmony_ci snd_soc_dapm_mutex_unlock(dapm); 265562306a36Sopenharmony_ci return ret; 265662306a36Sopenharmony_ci} 265762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_sync); 265862306a36Sopenharmony_ci 265962306a36Sopenharmony_cistatic int dapm_update_dai_chan(struct snd_soc_dapm_path *p, 266062306a36Sopenharmony_ci struct snd_soc_dapm_widget *w, 266162306a36Sopenharmony_ci int channels) 266262306a36Sopenharmony_ci{ 266362306a36Sopenharmony_ci switch (w->id) { 266462306a36Sopenharmony_ci case snd_soc_dapm_aif_out: 266562306a36Sopenharmony_ci case snd_soc_dapm_aif_in: 266662306a36Sopenharmony_ci break; 266762306a36Sopenharmony_ci default: 266862306a36Sopenharmony_ci return 0; 266962306a36Sopenharmony_ci } 267062306a36Sopenharmony_ci 267162306a36Sopenharmony_ci dev_dbg(w->dapm->dev, "%s DAI route %s -> %s\n", 267262306a36Sopenharmony_ci w->channel < channels ? "Connecting" : "Disconnecting", 267362306a36Sopenharmony_ci p->source->name, p->sink->name); 267462306a36Sopenharmony_ci 267562306a36Sopenharmony_ci if (w->channel < channels) 267662306a36Sopenharmony_ci soc_dapm_connect_path(p, true, "dai update"); 267762306a36Sopenharmony_ci else 267862306a36Sopenharmony_ci soc_dapm_connect_path(p, false, "dai update"); 267962306a36Sopenharmony_ci 268062306a36Sopenharmony_ci return 0; 268162306a36Sopenharmony_ci} 268262306a36Sopenharmony_ci 268362306a36Sopenharmony_cistatic int dapm_update_dai_unlocked(struct snd_pcm_substream *substream, 268462306a36Sopenharmony_ci struct snd_pcm_hw_params *params, 268562306a36Sopenharmony_ci struct snd_soc_dai *dai) 268662306a36Sopenharmony_ci{ 268762306a36Sopenharmony_ci int dir = substream->stream; 268862306a36Sopenharmony_ci int channels = params_channels(params); 268962306a36Sopenharmony_ci struct snd_soc_dapm_path *p; 269062306a36Sopenharmony_ci struct snd_soc_dapm_widget *w; 269162306a36Sopenharmony_ci int ret; 269262306a36Sopenharmony_ci 269362306a36Sopenharmony_ci w = snd_soc_dai_get_widget(dai, dir); 269462306a36Sopenharmony_ci 269562306a36Sopenharmony_ci if (!w) 269662306a36Sopenharmony_ci return 0; 269762306a36Sopenharmony_ci 269862306a36Sopenharmony_ci dev_dbg(dai->dev, "Update DAI routes for %s %s\n", dai->name, 269962306a36Sopenharmony_ci dir == SNDRV_PCM_STREAM_PLAYBACK ? "playback" : "capture"); 270062306a36Sopenharmony_ci 270162306a36Sopenharmony_ci snd_soc_dapm_widget_for_each_sink_path(w, p) { 270262306a36Sopenharmony_ci ret = dapm_update_dai_chan(p, p->sink, channels); 270362306a36Sopenharmony_ci if (ret < 0) 270462306a36Sopenharmony_ci return ret; 270562306a36Sopenharmony_ci } 270662306a36Sopenharmony_ci 270762306a36Sopenharmony_ci snd_soc_dapm_widget_for_each_source_path(w, p) { 270862306a36Sopenharmony_ci ret = dapm_update_dai_chan(p, p->source, channels); 270962306a36Sopenharmony_ci if (ret < 0) 271062306a36Sopenharmony_ci return ret; 271162306a36Sopenharmony_ci } 271262306a36Sopenharmony_ci 271362306a36Sopenharmony_ci return 0; 271462306a36Sopenharmony_ci} 271562306a36Sopenharmony_ci 271662306a36Sopenharmony_ciint snd_soc_dapm_update_dai(struct snd_pcm_substream *substream, 271762306a36Sopenharmony_ci struct snd_pcm_hw_params *params, 271862306a36Sopenharmony_ci struct snd_soc_dai *dai) 271962306a36Sopenharmony_ci{ 272062306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 272162306a36Sopenharmony_ci int ret; 272262306a36Sopenharmony_ci 272362306a36Sopenharmony_ci snd_soc_dapm_mutex_lock(rtd->card); 272462306a36Sopenharmony_ci ret = dapm_update_dai_unlocked(substream, params, dai); 272562306a36Sopenharmony_ci snd_soc_dapm_mutex_unlock(rtd->card); 272662306a36Sopenharmony_ci 272762306a36Sopenharmony_ci return ret; 272862306a36Sopenharmony_ci} 272962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_update_dai); 273062306a36Sopenharmony_ci 273162306a36Sopenharmony_ciint snd_soc_dapm_widget_name_cmp(struct snd_soc_dapm_widget *widget, const char *s) 273262306a36Sopenharmony_ci{ 273362306a36Sopenharmony_ci struct snd_soc_component *component = snd_soc_dapm_to_component(widget->dapm); 273462306a36Sopenharmony_ci const char *wname = widget->name; 273562306a36Sopenharmony_ci 273662306a36Sopenharmony_ci if (component->name_prefix) 273762306a36Sopenharmony_ci wname += strlen(component->name_prefix) + 1; /* plus space */ 273862306a36Sopenharmony_ci 273962306a36Sopenharmony_ci return strcmp(wname, s); 274062306a36Sopenharmony_ci} 274162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_widget_name_cmp); 274262306a36Sopenharmony_ci 274362306a36Sopenharmony_ci/* 274462306a36Sopenharmony_ci * dapm_update_widget_flags() - Re-compute widget sink and source flags 274562306a36Sopenharmony_ci * @w: The widget for which to update the flags 274662306a36Sopenharmony_ci * 274762306a36Sopenharmony_ci * Some widgets have a dynamic category which depends on which neighbors they 274862306a36Sopenharmony_ci * are connected to. This function update the category for these widgets. 274962306a36Sopenharmony_ci * 275062306a36Sopenharmony_ci * This function must be called whenever a path is added or removed to a widget. 275162306a36Sopenharmony_ci */ 275262306a36Sopenharmony_cistatic void dapm_update_widget_flags(struct snd_soc_dapm_widget *w) 275362306a36Sopenharmony_ci{ 275462306a36Sopenharmony_ci enum snd_soc_dapm_direction dir; 275562306a36Sopenharmony_ci struct snd_soc_dapm_path *p; 275662306a36Sopenharmony_ci unsigned int ep; 275762306a36Sopenharmony_ci 275862306a36Sopenharmony_ci switch (w->id) { 275962306a36Sopenharmony_ci case snd_soc_dapm_input: 276062306a36Sopenharmony_ci /* On a fully routed card an input is never a source */ 276162306a36Sopenharmony_ci if (w->dapm->card->fully_routed) 276262306a36Sopenharmony_ci return; 276362306a36Sopenharmony_ci ep = SND_SOC_DAPM_EP_SOURCE; 276462306a36Sopenharmony_ci snd_soc_dapm_widget_for_each_source_path(w, p) { 276562306a36Sopenharmony_ci if (p->source->id == snd_soc_dapm_micbias || 276662306a36Sopenharmony_ci p->source->id == snd_soc_dapm_mic || 276762306a36Sopenharmony_ci p->source->id == snd_soc_dapm_line || 276862306a36Sopenharmony_ci p->source->id == snd_soc_dapm_output) { 276962306a36Sopenharmony_ci ep = 0; 277062306a36Sopenharmony_ci break; 277162306a36Sopenharmony_ci } 277262306a36Sopenharmony_ci } 277362306a36Sopenharmony_ci break; 277462306a36Sopenharmony_ci case snd_soc_dapm_output: 277562306a36Sopenharmony_ci /* On a fully routed card a output is never a sink */ 277662306a36Sopenharmony_ci if (w->dapm->card->fully_routed) 277762306a36Sopenharmony_ci return; 277862306a36Sopenharmony_ci ep = SND_SOC_DAPM_EP_SINK; 277962306a36Sopenharmony_ci snd_soc_dapm_widget_for_each_sink_path(w, p) { 278062306a36Sopenharmony_ci if (p->sink->id == snd_soc_dapm_spk || 278162306a36Sopenharmony_ci p->sink->id == snd_soc_dapm_hp || 278262306a36Sopenharmony_ci p->sink->id == snd_soc_dapm_line || 278362306a36Sopenharmony_ci p->sink->id == snd_soc_dapm_input) { 278462306a36Sopenharmony_ci ep = 0; 278562306a36Sopenharmony_ci break; 278662306a36Sopenharmony_ci } 278762306a36Sopenharmony_ci } 278862306a36Sopenharmony_ci break; 278962306a36Sopenharmony_ci case snd_soc_dapm_line: 279062306a36Sopenharmony_ci ep = 0; 279162306a36Sopenharmony_ci snd_soc_dapm_for_each_direction(dir) { 279262306a36Sopenharmony_ci if (!list_empty(&w->edges[dir])) 279362306a36Sopenharmony_ci ep |= SND_SOC_DAPM_DIR_TO_EP(dir); 279462306a36Sopenharmony_ci } 279562306a36Sopenharmony_ci break; 279662306a36Sopenharmony_ci default: 279762306a36Sopenharmony_ci return; 279862306a36Sopenharmony_ci } 279962306a36Sopenharmony_ci 280062306a36Sopenharmony_ci w->is_ep = ep; 280162306a36Sopenharmony_ci} 280262306a36Sopenharmony_ci 280362306a36Sopenharmony_cistatic int snd_soc_dapm_check_dynamic_path(struct snd_soc_dapm_context *dapm, 280462306a36Sopenharmony_ci struct snd_soc_dapm_widget *source, struct snd_soc_dapm_widget *sink, 280562306a36Sopenharmony_ci const char *control) 280662306a36Sopenharmony_ci{ 280762306a36Sopenharmony_ci bool dynamic_source = false; 280862306a36Sopenharmony_ci bool dynamic_sink = false; 280962306a36Sopenharmony_ci 281062306a36Sopenharmony_ci if (!control) 281162306a36Sopenharmony_ci return 0; 281262306a36Sopenharmony_ci 281362306a36Sopenharmony_ci switch (source->id) { 281462306a36Sopenharmony_ci case snd_soc_dapm_demux: 281562306a36Sopenharmony_ci dynamic_source = true; 281662306a36Sopenharmony_ci break; 281762306a36Sopenharmony_ci default: 281862306a36Sopenharmony_ci break; 281962306a36Sopenharmony_ci } 282062306a36Sopenharmony_ci 282162306a36Sopenharmony_ci switch (sink->id) { 282262306a36Sopenharmony_ci case snd_soc_dapm_mux: 282362306a36Sopenharmony_ci case snd_soc_dapm_switch: 282462306a36Sopenharmony_ci case snd_soc_dapm_mixer: 282562306a36Sopenharmony_ci case snd_soc_dapm_mixer_named_ctl: 282662306a36Sopenharmony_ci dynamic_sink = true; 282762306a36Sopenharmony_ci break; 282862306a36Sopenharmony_ci default: 282962306a36Sopenharmony_ci break; 283062306a36Sopenharmony_ci } 283162306a36Sopenharmony_ci 283262306a36Sopenharmony_ci if (dynamic_source && dynamic_sink) { 283362306a36Sopenharmony_ci dev_err(dapm->dev, 283462306a36Sopenharmony_ci "Direct connection between demux and mixer/mux not supported for path %s -> [%s] -> %s\n", 283562306a36Sopenharmony_ci source->name, control, sink->name); 283662306a36Sopenharmony_ci return -EINVAL; 283762306a36Sopenharmony_ci } else if (!dynamic_source && !dynamic_sink) { 283862306a36Sopenharmony_ci dev_err(dapm->dev, 283962306a36Sopenharmony_ci "Control not supported for path %s -> [%s] -> %s\n", 284062306a36Sopenharmony_ci source->name, control, sink->name); 284162306a36Sopenharmony_ci return -EINVAL; 284262306a36Sopenharmony_ci } 284362306a36Sopenharmony_ci 284462306a36Sopenharmony_ci return 0; 284562306a36Sopenharmony_ci} 284662306a36Sopenharmony_ci 284762306a36Sopenharmony_cistatic int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, 284862306a36Sopenharmony_ci struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink, 284962306a36Sopenharmony_ci const char *control, 285062306a36Sopenharmony_ci int (*connected)(struct snd_soc_dapm_widget *source, 285162306a36Sopenharmony_ci struct snd_soc_dapm_widget *sink)) 285262306a36Sopenharmony_ci{ 285362306a36Sopenharmony_ci enum snd_soc_dapm_direction dir; 285462306a36Sopenharmony_ci struct snd_soc_dapm_path *path; 285562306a36Sopenharmony_ci int ret; 285662306a36Sopenharmony_ci 285762306a36Sopenharmony_ci if (wsink->is_supply && !wsource->is_supply) { 285862306a36Sopenharmony_ci dev_err(dapm->dev, 285962306a36Sopenharmony_ci "Connecting non-supply widget to supply widget is not supported (%s -> %s)\n", 286062306a36Sopenharmony_ci wsource->name, wsink->name); 286162306a36Sopenharmony_ci return -EINVAL; 286262306a36Sopenharmony_ci } 286362306a36Sopenharmony_ci 286462306a36Sopenharmony_ci if (connected && !wsource->is_supply) { 286562306a36Sopenharmony_ci dev_err(dapm->dev, 286662306a36Sopenharmony_ci "connected() callback only supported for supply widgets (%s -> %s)\n", 286762306a36Sopenharmony_ci wsource->name, wsink->name); 286862306a36Sopenharmony_ci return -EINVAL; 286962306a36Sopenharmony_ci } 287062306a36Sopenharmony_ci 287162306a36Sopenharmony_ci if (wsource->is_supply && control) { 287262306a36Sopenharmony_ci dev_err(dapm->dev, 287362306a36Sopenharmony_ci "Conditional paths are not supported for supply widgets (%s -> [%s] -> %s)\n", 287462306a36Sopenharmony_ci wsource->name, control, wsink->name); 287562306a36Sopenharmony_ci return -EINVAL; 287662306a36Sopenharmony_ci } 287762306a36Sopenharmony_ci 287862306a36Sopenharmony_ci ret = snd_soc_dapm_check_dynamic_path(dapm, wsource, wsink, control); 287962306a36Sopenharmony_ci if (ret) 288062306a36Sopenharmony_ci return ret; 288162306a36Sopenharmony_ci 288262306a36Sopenharmony_ci path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL); 288362306a36Sopenharmony_ci if (!path) 288462306a36Sopenharmony_ci return -ENOMEM; 288562306a36Sopenharmony_ci 288662306a36Sopenharmony_ci path->node[SND_SOC_DAPM_DIR_IN] = wsource; 288762306a36Sopenharmony_ci path->node[SND_SOC_DAPM_DIR_OUT] = wsink; 288862306a36Sopenharmony_ci 288962306a36Sopenharmony_ci path->connected = connected; 289062306a36Sopenharmony_ci INIT_LIST_HEAD(&path->list); 289162306a36Sopenharmony_ci INIT_LIST_HEAD(&path->list_kcontrol); 289262306a36Sopenharmony_ci 289362306a36Sopenharmony_ci if (wsource->is_supply || wsink->is_supply) 289462306a36Sopenharmony_ci path->is_supply = 1; 289562306a36Sopenharmony_ci 289662306a36Sopenharmony_ci /* connect static paths */ 289762306a36Sopenharmony_ci if (control == NULL) { 289862306a36Sopenharmony_ci path->connect = 1; 289962306a36Sopenharmony_ci } else { 290062306a36Sopenharmony_ci switch (wsource->id) { 290162306a36Sopenharmony_ci case snd_soc_dapm_demux: 290262306a36Sopenharmony_ci ret = dapm_connect_mux(dapm, path, control, wsource); 290362306a36Sopenharmony_ci if (ret) 290462306a36Sopenharmony_ci goto err; 290562306a36Sopenharmony_ci break; 290662306a36Sopenharmony_ci default: 290762306a36Sopenharmony_ci break; 290862306a36Sopenharmony_ci } 290962306a36Sopenharmony_ci 291062306a36Sopenharmony_ci switch (wsink->id) { 291162306a36Sopenharmony_ci case snd_soc_dapm_mux: 291262306a36Sopenharmony_ci ret = dapm_connect_mux(dapm, path, control, wsink); 291362306a36Sopenharmony_ci if (ret != 0) 291462306a36Sopenharmony_ci goto err; 291562306a36Sopenharmony_ci break; 291662306a36Sopenharmony_ci case snd_soc_dapm_switch: 291762306a36Sopenharmony_ci case snd_soc_dapm_mixer: 291862306a36Sopenharmony_ci case snd_soc_dapm_mixer_named_ctl: 291962306a36Sopenharmony_ci ret = dapm_connect_mixer(dapm, path, control); 292062306a36Sopenharmony_ci if (ret != 0) 292162306a36Sopenharmony_ci goto err; 292262306a36Sopenharmony_ci break; 292362306a36Sopenharmony_ci default: 292462306a36Sopenharmony_ci break; 292562306a36Sopenharmony_ci } 292662306a36Sopenharmony_ci } 292762306a36Sopenharmony_ci 292862306a36Sopenharmony_ci list_add(&path->list, &dapm->card->paths); 292962306a36Sopenharmony_ci 293062306a36Sopenharmony_ci snd_soc_dapm_for_each_direction(dir) 293162306a36Sopenharmony_ci list_add(&path->list_node[dir], &path->node[dir]->edges[dir]); 293262306a36Sopenharmony_ci 293362306a36Sopenharmony_ci snd_soc_dapm_for_each_direction(dir) { 293462306a36Sopenharmony_ci dapm_update_widget_flags(path->node[dir]); 293562306a36Sopenharmony_ci dapm_mark_dirty(path->node[dir], "Route added"); 293662306a36Sopenharmony_ci } 293762306a36Sopenharmony_ci 293862306a36Sopenharmony_ci if (snd_soc_card_is_instantiated(dapm->card) && path->connect) 293962306a36Sopenharmony_ci dapm_path_invalidate(path); 294062306a36Sopenharmony_ci 294162306a36Sopenharmony_ci return 0; 294262306a36Sopenharmony_cierr: 294362306a36Sopenharmony_ci kfree(path); 294462306a36Sopenharmony_ci return ret; 294562306a36Sopenharmony_ci} 294662306a36Sopenharmony_ci 294762306a36Sopenharmony_cistatic int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, 294862306a36Sopenharmony_ci const struct snd_soc_dapm_route *route) 294962306a36Sopenharmony_ci{ 295062306a36Sopenharmony_ci struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w; 295162306a36Sopenharmony_ci struct snd_soc_dapm_widget *wtsource = NULL, *wtsink = NULL; 295262306a36Sopenharmony_ci const char *sink; 295362306a36Sopenharmony_ci const char *source; 295462306a36Sopenharmony_ci char prefixed_sink[80]; 295562306a36Sopenharmony_ci char prefixed_source[80]; 295662306a36Sopenharmony_ci const char *prefix; 295762306a36Sopenharmony_ci unsigned int sink_ref = 0; 295862306a36Sopenharmony_ci unsigned int source_ref = 0; 295962306a36Sopenharmony_ci int ret; 296062306a36Sopenharmony_ci 296162306a36Sopenharmony_ci prefix = soc_dapm_prefix(dapm); 296262306a36Sopenharmony_ci if (prefix) { 296362306a36Sopenharmony_ci snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s", 296462306a36Sopenharmony_ci prefix, route->sink); 296562306a36Sopenharmony_ci sink = prefixed_sink; 296662306a36Sopenharmony_ci snprintf(prefixed_source, sizeof(prefixed_source), "%s %s", 296762306a36Sopenharmony_ci prefix, route->source); 296862306a36Sopenharmony_ci source = prefixed_source; 296962306a36Sopenharmony_ci } else { 297062306a36Sopenharmony_ci sink = route->sink; 297162306a36Sopenharmony_ci source = route->source; 297262306a36Sopenharmony_ci } 297362306a36Sopenharmony_ci 297462306a36Sopenharmony_ci wsource = dapm_wcache_lookup(dapm->wcache_source, source); 297562306a36Sopenharmony_ci wsink = dapm_wcache_lookup(dapm->wcache_sink, sink); 297662306a36Sopenharmony_ci 297762306a36Sopenharmony_ci if (wsink && wsource) 297862306a36Sopenharmony_ci goto skip_search; 297962306a36Sopenharmony_ci 298062306a36Sopenharmony_ci /* 298162306a36Sopenharmony_ci * find src and dest widgets over all widgets but favor a widget from 298262306a36Sopenharmony_ci * current DAPM context 298362306a36Sopenharmony_ci */ 298462306a36Sopenharmony_ci for_each_card_widgets(dapm->card, w) { 298562306a36Sopenharmony_ci if (!wsink && !(strcmp(w->name, sink))) { 298662306a36Sopenharmony_ci wtsink = w; 298762306a36Sopenharmony_ci if (w->dapm == dapm) { 298862306a36Sopenharmony_ci wsink = w; 298962306a36Sopenharmony_ci if (wsource) 299062306a36Sopenharmony_ci break; 299162306a36Sopenharmony_ci } 299262306a36Sopenharmony_ci sink_ref++; 299362306a36Sopenharmony_ci if (sink_ref > 1) 299462306a36Sopenharmony_ci dev_warn(dapm->dev, 299562306a36Sopenharmony_ci "ASoC: sink widget %s overwritten\n", 299662306a36Sopenharmony_ci w->name); 299762306a36Sopenharmony_ci continue; 299862306a36Sopenharmony_ci } 299962306a36Sopenharmony_ci if (!wsource && !(strcmp(w->name, source))) { 300062306a36Sopenharmony_ci wtsource = w; 300162306a36Sopenharmony_ci if (w->dapm == dapm) { 300262306a36Sopenharmony_ci wsource = w; 300362306a36Sopenharmony_ci if (wsink) 300462306a36Sopenharmony_ci break; 300562306a36Sopenharmony_ci } 300662306a36Sopenharmony_ci source_ref++; 300762306a36Sopenharmony_ci if (source_ref > 1) 300862306a36Sopenharmony_ci dev_warn(dapm->dev, 300962306a36Sopenharmony_ci "ASoC: source widget %s overwritten\n", 301062306a36Sopenharmony_ci w->name); 301162306a36Sopenharmony_ci } 301262306a36Sopenharmony_ci } 301362306a36Sopenharmony_ci /* use widget from another DAPM context if not found from this */ 301462306a36Sopenharmony_ci if (!wsink) 301562306a36Sopenharmony_ci wsink = wtsink; 301662306a36Sopenharmony_ci if (!wsource) 301762306a36Sopenharmony_ci wsource = wtsource; 301862306a36Sopenharmony_ci 301962306a36Sopenharmony_ci ret = -ENODEV; 302062306a36Sopenharmony_ci if (!wsource) 302162306a36Sopenharmony_ci goto err; 302262306a36Sopenharmony_ci if (!wsink) 302362306a36Sopenharmony_ci goto err; 302462306a36Sopenharmony_ci 302562306a36Sopenharmony_ciskip_search: 302662306a36Sopenharmony_ci /* update cache */ 302762306a36Sopenharmony_ci dapm->wcache_sink = wsink; 302862306a36Sopenharmony_ci dapm->wcache_source = wsource; 302962306a36Sopenharmony_ci 303062306a36Sopenharmony_ci ret = snd_soc_dapm_add_path(dapm, wsource, wsink, route->control, 303162306a36Sopenharmony_ci route->connected); 303262306a36Sopenharmony_cierr: 303362306a36Sopenharmony_ci if (ret) 303462306a36Sopenharmony_ci dev_err(dapm->dev, "ASoC: Failed to add route %s%s -%s%s%s> %s%s\n", 303562306a36Sopenharmony_ci source, !wsource ? "(*)" : "", 303662306a36Sopenharmony_ci !route->control ? "" : "> [", 303762306a36Sopenharmony_ci !route->control ? "" : route->control, 303862306a36Sopenharmony_ci !route->control ? "" : "] -", 303962306a36Sopenharmony_ci sink, !wsink ? "(*)" : ""); 304062306a36Sopenharmony_ci return ret; 304162306a36Sopenharmony_ci} 304262306a36Sopenharmony_ci 304362306a36Sopenharmony_cistatic int snd_soc_dapm_del_route(struct snd_soc_dapm_context *dapm, 304462306a36Sopenharmony_ci const struct snd_soc_dapm_route *route) 304562306a36Sopenharmony_ci{ 304662306a36Sopenharmony_ci struct snd_soc_dapm_path *path, *p; 304762306a36Sopenharmony_ci const char *sink; 304862306a36Sopenharmony_ci const char *source; 304962306a36Sopenharmony_ci char prefixed_sink[80]; 305062306a36Sopenharmony_ci char prefixed_source[80]; 305162306a36Sopenharmony_ci const char *prefix; 305262306a36Sopenharmony_ci 305362306a36Sopenharmony_ci if (route->control) { 305462306a36Sopenharmony_ci dev_err(dapm->dev, 305562306a36Sopenharmony_ci "ASoC: Removal of routes with controls not supported\n"); 305662306a36Sopenharmony_ci return -EINVAL; 305762306a36Sopenharmony_ci } 305862306a36Sopenharmony_ci 305962306a36Sopenharmony_ci prefix = soc_dapm_prefix(dapm); 306062306a36Sopenharmony_ci if (prefix) { 306162306a36Sopenharmony_ci snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s", 306262306a36Sopenharmony_ci prefix, route->sink); 306362306a36Sopenharmony_ci sink = prefixed_sink; 306462306a36Sopenharmony_ci snprintf(prefixed_source, sizeof(prefixed_source), "%s %s", 306562306a36Sopenharmony_ci prefix, route->source); 306662306a36Sopenharmony_ci source = prefixed_source; 306762306a36Sopenharmony_ci } else { 306862306a36Sopenharmony_ci sink = route->sink; 306962306a36Sopenharmony_ci source = route->source; 307062306a36Sopenharmony_ci } 307162306a36Sopenharmony_ci 307262306a36Sopenharmony_ci path = NULL; 307362306a36Sopenharmony_ci list_for_each_entry(p, &dapm->card->paths, list) { 307462306a36Sopenharmony_ci if (strcmp(p->source->name, source) != 0) 307562306a36Sopenharmony_ci continue; 307662306a36Sopenharmony_ci if (strcmp(p->sink->name, sink) != 0) 307762306a36Sopenharmony_ci continue; 307862306a36Sopenharmony_ci path = p; 307962306a36Sopenharmony_ci break; 308062306a36Sopenharmony_ci } 308162306a36Sopenharmony_ci 308262306a36Sopenharmony_ci if (path) { 308362306a36Sopenharmony_ci struct snd_soc_dapm_widget *wsource = path->source; 308462306a36Sopenharmony_ci struct snd_soc_dapm_widget *wsink = path->sink; 308562306a36Sopenharmony_ci 308662306a36Sopenharmony_ci dapm_mark_dirty(wsource, "Route removed"); 308762306a36Sopenharmony_ci dapm_mark_dirty(wsink, "Route removed"); 308862306a36Sopenharmony_ci if (path->connect) 308962306a36Sopenharmony_ci dapm_path_invalidate(path); 309062306a36Sopenharmony_ci 309162306a36Sopenharmony_ci dapm_free_path(path); 309262306a36Sopenharmony_ci 309362306a36Sopenharmony_ci /* Update any path related flags */ 309462306a36Sopenharmony_ci dapm_update_widget_flags(wsource); 309562306a36Sopenharmony_ci dapm_update_widget_flags(wsink); 309662306a36Sopenharmony_ci } else { 309762306a36Sopenharmony_ci dev_warn(dapm->dev, "ASoC: Route %s->%s does not exist\n", 309862306a36Sopenharmony_ci source, sink); 309962306a36Sopenharmony_ci } 310062306a36Sopenharmony_ci 310162306a36Sopenharmony_ci return 0; 310262306a36Sopenharmony_ci} 310362306a36Sopenharmony_ci 310462306a36Sopenharmony_ci/** 310562306a36Sopenharmony_ci * snd_soc_dapm_add_routes - Add routes between DAPM widgets 310662306a36Sopenharmony_ci * @dapm: DAPM context 310762306a36Sopenharmony_ci * @route: audio routes 310862306a36Sopenharmony_ci * @num: number of routes 310962306a36Sopenharmony_ci * 311062306a36Sopenharmony_ci * Connects 2 dapm widgets together via a named audio path. The sink is 311162306a36Sopenharmony_ci * the widget receiving the audio signal, whilst the source is the sender 311262306a36Sopenharmony_ci * of the audio signal. 311362306a36Sopenharmony_ci * 311462306a36Sopenharmony_ci * Returns 0 for success else error. On error all resources can be freed 311562306a36Sopenharmony_ci * with a call to snd_soc_card_free(). 311662306a36Sopenharmony_ci */ 311762306a36Sopenharmony_ciint snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm, 311862306a36Sopenharmony_ci const struct snd_soc_dapm_route *route, int num) 311962306a36Sopenharmony_ci{ 312062306a36Sopenharmony_ci int i, ret = 0; 312162306a36Sopenharmony_ci 312262306a36Sopenharmony_ci snd_soc_dapm_mutex_lock(dapm); 312362306a36Sopenharmony_ci for (i = 0; i < num; i++) { 312462306a36Sopenharmony_ci int r = snd_soc_dapm_add_route(dapm, route); 312562306a36Sopenharmony_ci if (r < 0) 312662306a36Sopenharmony_ci ret = r; 312762306a36Sopenharmony_ci route++; 312862306a36Sopenharmony_ci } 312962306a36Sopenharmony_ci snd_soc_dapm_mutex_unlock(dapm); 313062306a36Sopenharmony_ci 313162306a36Sopenharmony_ci return ret; 313262306a36Sopenharmony_ci} 313362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_add_routes); 313462306a36Sopenharmony_ci 313562306a36Sopenharmony_ci/** 313662306a36Sopenharmony_ci * snd_soc_dapm_del_routes - Remove routes between DAPM widgets 313762306a36Sopenharmony_ci * @dapm: DAPM context 313862306a36Sopenharmony_ci * @route: audio routes 313962306a36Sopenharmony_ci * @num: number of routes 314062306a36Sopenharmony_ci * 314162306a36Sopenharmony_ci * Removes routes from the DAPM context. 314262306a36Sopenharmony_ci */ 314362306a36Sopenharmony_ciint snd_soc_dapm_del_routes(struct snd_soc_dapm_context *dapm, 314462306a36Sopenharmony_ci const struct snd_soc_dapm_route *route, int num) 314562306a36Sopenharmony_ci{ 314662306a36Sopenharmony_ci int i; 314762306a36Sopenharmony_ci 314862306a36Sopenharmony_ci snd_soc_dapm_mutex_lock(dapm); 314962306a36Sopenharmony_ci for (i = 0; i < num; i++) { 315062306a36Sopenharmony_ci snd_soc_dapm_del_route(dapm, route); 315162306a36Sopenharmony_ci route++; 315262306a36Sopenharmony_ci } 315362306a36Sopenharmony_ci snd_soc_dapm_mutex_unlock(dapm); 315462306a36Sopenharmony_ci 315562306a36Sopenharmony_ci return 0; 315662306a36Sopenharmony_ci} 315762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_del_routes); 315862306a36Sopenharmony_ci 315962306a36Sopenharmony_cistatic int snd_soc_dapm_weak_route(struct snd_soc_dapm_context *dapm, 316062306a36Sopenharmony_ci const struct snd_soc_dapm_route *route) 316162306a36Sopenharmony_ci{ 316262306a36Sopenharmony_ci struct snd_soc_dapm_widget *source = dapm_find_widget(dapm, 316362306a36Sopenharmony_ci route->source, 316462306a36Sopenharmony_ci true); 316562306a36Sopenharmony_ci struct snd_soc_dapm_widget *sink = dapm_find_widget(dapm, 316662306a36Sopenharmony_ci route->sink, 316762306a36Sopenharmony_ci true); 316862306a36Sopenharmony_ci struct snd_soc_dapm_path *path; 316962306a36Sopenharmony_ci int count = 0; 317062306a36Sopenharmony_ci 317162306a36Sopenharmony_ci if (!source) { 317262306a36Sopenharmony_ci dev_err(dapm->dev, "ASoC: Unable to find source %s for weak route\n", 317362306a36Sopenharmony_ci route->source); 317462306a36Sopenharmony_ci return -ENODEV; 317562306a36Sopenharmony_ci } 317662306a36Sopenharmony_ci 317762306a36Sopenharmony_ci if (!sink) { 317862306a36Sopenharmony_ci dev_err(dapm->dev, "ASoC: Unable to find sink %s for weak route\n", 317962306a36Sopenharmony_ci route->sink); 318062306a36Sopenharmony_ci return -ENODEV; 318162306a36Sopenharmony_ci } 318262306a36Sopenharmony_ci 318362306a36Sopenharmony_ci if (route->control || route->connected) 318462306a36Sopenharmony_ci dev_warn(dapm->dev, "ASoC: Ignoring control for weak route %s->%s\n", 318562306a36Sopenharmony_ci route->source, route->sink); 318662306a36Sopenharmony_ci 318762306a36Sopenharmony_ci snd_soc_dapm_widget_for_each_sink_path(source, path) { 318862306a36Sopenharmony_ci if (path->sink == sink) { 318962306a36Sopenharmony_ci path->weak = 1; 319062306a36Sopenharmony_ci count++; 319162306a36Sopenharmony_ci } 319262306a36Sopenharmony_ci } 319362306a36Sopenharmony_ci 319462306a36Sopenharmony_ci if (count == 0) 319562306a36Sopenharmony_ci dev_err(dapm->dev, "ASoC: No path found for weak route %s->%s\n", 319662306a36Sopenharmony_ci route->source, route->sink); 319762306a36Sopenharmony_ci if (count > 1) 319862306a36Sopenharmony_ci dev_warn(dapm->dev, "ASoC: %d paths found for weak route %s->%s\n", 319962306a36Sopenharmony_ci count, route->source, route->sink); 320062306a36Sopenharmony_ci 320162306a36Sopenharmony_ci return 0; 320262306a36Sopenharmony_ci} 320362306a36Sopenharmony_ci 320462306a36Sopenharmony_ci/** 320562306a36Sopenharmony_ci * snd_soc_dapm_weak_routes - Mark routes between DAPM widgets as weak 320662306a36Sopenharmony_ci * @dapm: DAPM context 320762306a36Sopenharmony_ci * @route: audio routes 320862306a36Sopenharmony_ci * @num: number of routes 320962306a36Sopenharmony_ci * 321062306a36Sopenharmony_ci * Mark existing routes matching those specified in the passed array 321162306a36Sopenharmony_ci * as being weak, meaning that they are ignored for the purpose of 321262306a36Sopenharmony_ci * power decisions. The main intended use case is for sidetone paths 321362306a36Sopenharmony_ci * which couple audio between other independent paths if they are both 321462306a36Sopenharmony_ci * active in order to make the combination work better at the user 321562306a36Sopenharmony_ci * level but which aren't intended to be "used". 321662306a36Sopenharmony_ci * 321762306a36Sopenharmony_ci * Note that CODEC drivers should not use this as sidetone type paths 321862306a36Sopenharmony_ci * can frequently also be used as bypass paths. 321962306a36Sopenharmony_ci */ 322062306a36Sopenharmony_ciint snd_soc_dapm_weak_routes(struct snd_soc_dapm_context *dapm, 322162306a36Sopenharmony_ci const struct snd_soc_dapm_route *route, int num) 322262306a36Sopenharmony_ci{ 322362306a36Sopenharmony_ci int i; 322462306a36Sopenharmony_ci int ret = 0; 322562306a36Sopenharmony_ci 322662306a36Sopenharmony_ci snd_soc_dapm_mutex_lock_root(dapm); 322762306a36Sopenharmony_ci for (i = 0; i < num; i++) { 322862306a36Sopenharmony_ci int err = snd_soc_dapm_weak_route(dapm, route); 322962306a36Sopenharmony_ci if (err) 323062306a36Sopenharmony_ci ret = err; 323162306a36Sopenharmony_ci route++; 323262306a36Sopenharmony_ci } 323362306a36Sopenharmony_ci snd_soc_dapm_mutex_unlock(dapm); 323462306a36Sopenharmony_ci 323562306a36Sopenharmony_ci return ret; 323662306a36Sopenharmony_ci} 323762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_weak_routes); 323862306a36Sopenharmony_ci 323962306a36Sopenharmony_ci/** 324062306a36Sopenharmony_ci * snd_soc_dapm_new_widgets - add new dapm widgets 324162306a36Sopenharmony_ci * @card: card to be checked for new dapm widgets 324262306a36Sopenharmony_ci * 324362306a36Sopenharmony_ci * Checks the codec for any new dapm widgets and creates them if found. 324462306a36Sopenharmony_ci * 324562306a36Sopenharmony_ci * Returns 0 for success. 324662306a36Sopenharmony_ci */ 324762306a36Sopenharmony_ciint snd_soc_dapm_new_widgets(struct snd_soc_card *card) 324862306a36Sopenharmony_ci{ 324962306a36Sopenharmony_ci struct snd_soc_dapm_widget *w; 325062306a36Sopenharmony_ci unsigned int val; 325162306a36Sopenharmony_ci 325262306a36Sopenharmony_ci snd_soc_dapm_mutex_lock_root(card); 325362306a36Sopenharmony_ci 325462306a36Sopenharmony_ci for_each_card_widgets(card, w) 325562306a36Sopenharmony_ci { 325662306a36Sopenharmony_ci if (w->new) 325762306a36Sopenharmony_ci continue; 325862306a36Sopenharmony_ci 325962306a36Sopenharmony_ci if (w->num_kcontrols) { 326062306a36Sopenharmony_ci w->kcontrols = kcalloc(w->num_kcontrols, 326162306a36Sopenharmony_ci sizeof(struct snd_kcontrol *), 326262306a36Sopenharmony_ci GFP_KERNEL); 326362306a36Sopenharmony_ci if (!w->kcontrols) { 326462306a36Sopenharmony_ci snd_soc_dapm_mutex_unlock(card); 326562306a36Sopenharmony_ci return -ENOMEM; 326662306a36Sopenharmony_ci } 326762306a36Sopenharmony_ci } 326862306a36Sopenharmony_ci 326962306a36Sopenharmony_ci switch(w->id) { 327062306a36Sopenharmony_ci case snd_soc_dapm_switch: 327162306a36Sopenharmony_ci case snd_soc_dapm_mixer: 327262306a36Sopenharmony_ci case snd_soc_dapm_mixer_named_ctl: 327362306a36Sopenharmony_ci dapm_new_mixer(w); 327462306a36Sopenharmony_ci break; 327562306a36Sopenharmony_ci case snd_soc_dapm_mux: 327662306a36Sopenharmony_ci case snd_soc_dapm_demux: 327762306a36Sopenharmony_ci dapm_new_mux(w); 327862306a36Sopenharmony_ci break; 327962306a36Sopenharmony_ci case snd_soc_dapm_pga: 328062306a36Sopenharmony_ci case snd_soc_dapm_effect: 328162306a36Sopenharmony_ci case snd_soc_dapm_out_drv: 328262306a36Sopenharmony_ci dapm_new_pga(w); 328362306a36Sopenharmony_ci break; 328462306a36Sopenharmony_ci case snd_soc_dapm_dai_link: 328562306a36Sopenharmony_ci dapm_new_dai_link(w); 328662306a36Sopenharmony_ci break; 328762306a36Sopenharmony_ci default: 328862306a36Sopenharmony_ci break; 328962306a36Sopenharmony_ci } 329062306a36Sopenharmony_ci 329162306a36Sopenharmony_ci /* Read the initial power state from the device */ 329262306a36Sopenharmony_ci if (w->reg >= 0) { 329362306a36Sopenharmony_ci val = soc_dapm_read(w->dapm, w->reg); 329462306a36Sopenharmony_ci val = val >> w->shift; 329562306a36Sopenharmony_ci val &= w->mask; 329662306a36Sopenharmony_ci if (val == w->on_val) 329762306a36Sopenharmony_ci w->power = 1; 329862306a36Sopenharmony_ci } 329962306a36Sopenharmony_ci 330062306a36Sopenharmony_ci w->new = 1; 330162306a36Sopenharmony_ci 330262306a36Sopenharmony_ci dapm_mark_dirty(w, "new widget"); 330362306a36Sopenharmony_ci dapm_debugfs_add_widget(w); 330462306a36Sopenharmony_ci } 330562306a36Sopenharmony_ci 330662306a36Sopenharmony_ci dapm_power_widgets(card, SND_SOC_DAPM_STREAM_NOP); 330762306a36Sopenharmony_ci snd_soc_dapm_mutex_unlock(card); 330862306a36Sopenharmony_ci return 0; 330962306a36Sopenharmony_ci} 331062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets); 331162306a36Sopenharmony_ci 331262306a36Sopenharmony_ci/** 331362306a36Sopenharmony_ci * snd_soc_dapm_get_volsw - dapm mixer get callback 331462306a36Sopenharmony_ci * @kcontrol: mixer control 331562306a36Sopenharmony_ci * @ucontrol: control element information 331662306a36Sopenharmony_ci * 331762306a36Sopenharmony_ci * Callback to get the value of a dapm mixer control. 331862306a36Sopenharmony_ci * 331962306a36Sopenharmony_ci * Returns 0 for success. 332062306a36Sopenharmony_ci */ 332162306a36Sopenharmony_ciint snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, 332262306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 332362306a36Sopenharmony_ci{ 332462306a36Sopenharmony_ci struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); 332562306a36Sopenharmony_ci struct soc_mixer_control *mc = 332662306a36Sopenharmony_ci (struct soc_mixer_control *)kcontrol->private_value; 332762306a36Sopenharmony_ci int reg = mc->reg; 332862306a36Sopenharmony_ci unsigned int shift = mc->shift; 332962306a36Sopenharmony_ci int max = mc->max; 333062306a36Sopenharmony_ci unsigned int width = fls(max); 333162306a36Sopenharmony_ci unsigned int mask = (1 << fls(max)) - 1; 333262306a36Sopenharmony_ci unsigned int invert = mc->invert; 333362306a36Sopenharmony_ci unsigned int reg_val, val, rval = 0; 333462306a36Sopenharmony_ci 333562306a36Sopenharmony_ci snd_soc_dapm_mutex_lock(dapm); 333662306a36Sopenharmony_ci if (dapm_kcontrol_is_powered(kcontrol) && reg != SND_SOC_NOPM) { 333762306a36Sopenharmony_ci reg_val = soc_dapm_read(dapm, reg); 333862306a36Sopenharmony_ci val = (reg_val >> shift) & mask; 333962306a36Sopenharmony_ci 334062306a36Sopenharmony_ci if (reg != mc->rreg) 334162306a36Sopenharmony_ci reg_val = soc_dapm_read(dapm, mc->rreg); 334262306a36Sopenharmony_ci 334362306a36Sopenharmony_ci if (snd_soc_volsw_is_stereo(mc)) 334462306a36Sopenharmony_ci rval = (reg_val >> mc->rshift) & mask; 334562306a36Sopenharmony_ci } else { 334662306a36Sopenharmony_ci reg_val = dapm_kcontrol_get_value(kcontrol); 334762306a36Sopenharmony_ci val = reg_val & mask; 334862306a36Sopenharmony_ci 334962306a36Sopenharmony_ci if (snd_soc_volsw_is_stereo(mc)) 335062306a36Sopenharmony_ci rval = (reg_val >> width) & mask; 335162306a36Sopenharmony_ci } 335262306a36Sopenharmony_ci snd_soc_dapm_mutex_unlock(dapm); 335362306a36Sopenharmony_ci 335462306a36Sopenharmony_ci if (invert) 335562306a36Sopenharmony_ci ucontrol->value.integer.value[0] = max - val; 335662306a36Sopenharmony_ci else 335762306a36Sopenharmony_ci ucontrol->value.integer.value[0] = val; 335862306a36Sopenharmony_ci 335962306a36Sopenharmony_ci if (snd_soc_volsw_is_stereo(mc)) { 336062306a36Sopenharmony_ci if (invert) 336162306a36Sopenharmony_ci ucontrol->value.integer.value[1] = max - rval; 336262306a36Sopenharmony_ci else 336362306a36Sopenharmony_ci ucontrol->value.integer.value[1] = rval; 336462306a36Sopenharmony_ci } 336562306a36Sopenharmony_ci 336662306a36Sopenharmony_ci return 0; 336762306a36Sopenharmony_ci} 336862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_get_volsw); 336962306a36Sopenharmony_ci 337062306a36Sopenharmony_ci/** 337162306a36Sopenharmony_ci * snd_soc_dapm_put_volsw - dapm mixer set callback 337262306a36Sopenharmony_ci * @kcontrol: mixer control 337362306a36Sopenharmony_ci * @ucontrol: control element information 337462306a36Sopenharmony_ci * 337562306a36Sopenharmony_ci * Callback to set the value of a dapm mixer control. 337662306a36Sopenharmony_ci * 337762306a36Sopenharmony_ci * Returns 0 for success. 337862306a36Sopenharmony_ci */ 337962306a36Sopenharmony_ciint snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, 338062306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 338162306a36Sopenharmony_ci{ 338262306a36Sopenharmony_ci struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); 338362306a36Sopenharmony_ci struct snd_soc_card *card = dapm->card; 338462306a36Sopenharmony_ci struct soc_mixer_control *mc = 338562306a36Sopenharmony_ci (struct soc_mixer_control *)kcontrol->private_value; 338662306a36Sopenharmony_ci int reg = mc->reg; 338762306a36Sopenharmony_ci unsigned int shift = mc->shift; 338862306a36Sopenharmony_ci int max = mc->max; 338962306a36Sopenharmony_ci unsigned int width = fls(max); 339062306a36Sopenharmony_ci unsigned int mask = (1 << width) - 1; 339162306a36Sopenharmony_ci unsigned int invert = mc->invert; 339262306a36Sopenharmony_ci unsigned int val, rval = 0; 339362306a36Sopenharmony_ci int connect, rconnect = -1, change, reg_change = 0; 339462306a36Sopenharmony_ci struct snd_soc_dapm_update update = {}; 339562306a36Sopenharmony_ci int ret = 0; 339662306a36Sopenharmony_ci 339762306a36Sopenharmony_ci val = (ucontrol->value.integer.value[0] & mask); 339862306a36Sopenharmony_ci connect = !!val; 339962306a36Sopenharmony_ci 340062306a36Sopenharmony_ci if (invert) 340162306a36Sopenharmony_ci val = max - val; 340262306a36Sopenharmony_ci 340362306a36Sopenharmony_ci if (snd_soc_volsw_is_stereo(mc)) { 340462306a36Sopenharmony_ci rval = (ucontrol->value.integer.value[1] & mask); 340562306a36Sopenharmony_ci rconnect = !!rval; 340662306a36Sopenharmony_ci if (invert) 340762306a36Sopenharmony_ci rval = max - rval; 340862306a36Sopenharmony_ci } 340962306a36Sopenharmony_ci 341062306a36Sopenharmony_ci snd_soc_dapm_mutex_lock(card); 341162306a36Sopenharmony_ci 341262306a36Sopenharmony_ci /* This assumes field width < (bits in unsigned int / 2) */ 341362306a36Sopenharmony_ci if (width > sizeof(unsigned int) * 8 / 2) 341462306a36Sopenharmony_ci dev_warn(dapm->dev, 341562306a36Sopenharmony_ci "ASoC: control %s field width limit exceeded\n", 341662306a36Sopenharmony_ci kcontrol->id.name); 341762306a36Sopenharmony_ci change = dapm_kcontrol_set_value(kcontrol, val | (rval << width)); 341862306a36Sopenharmony_ci 341962306a36Sopenharmony_ci if (reg != SND_SOC_NOPM) { 342062306a36Sopenharmony_ci val = val << shift; 342162306a36Sopenharmony_ci rval = rval << mc->rshift; 342262306a36Sopenharmony_ci 342362306a36Sopenharmony_ci reg_change = soc_dapm_test_bits(dapm, reg, mask << shift, val); 342462306a36Sopenharmony_ci 342562306a36Sopenharmony_ci if (snd_soc_volsw_is_stereo(mc)) 342662306a36Sopenharmony_ci reg_change |= soc_dapm_test_bits(dapm, mc->rreg, 342762306a36Sopenharmony_ci mask << mc->rshift, 342862306a36Sopenharmony_ci rval); 342962306a36Sopenharmony_ci } 343062306a36Sopenharmony_ci 343162306a36Sopenharmony_ci if (change || reg_change) { 343262306a36Sopenharmony_ci if (reg_change) { 343362306a36Sopenharmony_ci if (snd_soc_volsw_is_stereo(mc)) { 343462306a36Sopenharmony_ci update.has_second_set = true; 343562306a36Sopenharmony_ci update.reg2 = mc->rreg; 343662306a36Sopenharmony_ci update.mask2 = mask << mc->rshift; 343762306a36Sopenharmony_ci update.val2 = rval; 343862306a36Sopenharmony_ci } 343962306a36Sopenharmony_ci update.kcontrol = kcontrol; 344062306a36Sopenharmony_ci update.reg = reg; 344162306a36Sopenharmony_ci update.mask = mask << shift; 344262306a36Sopenharmony_ci update.val = val; 344362306a36Sopenharmony_ci card->update = &update; 344462306a36Sopenharmony_ci } 344562306a36Sopenharmony_ci 344662306a36Sopenharmony_ci ret = soc_dapm_mixer_update_power(card, kcontrol, connect, 344762306a36Sopenharmony_ci rconnect); 344862306a36Sopenharmony_ci 344962306a36Sopenharmony_ci card->update = NULL; 345062306a36Sopenharmony_ci } 345162306a36Sopenharmony_ci 345262306a36Sopenharmony_ci snd_soc_dapm_mutex_unlock(card); 345362306a36Sopenharmony_ci 345462306a36Sopenharmony_ci if (ret > 0) 345562306a36Sopenharmony_ci snd_soc_dpcm_runtime_update(card); 345662306a36Sopenharmony_ci 345762306a36Sopenharmony_ci return change; 345862306a36Sopenharmony_ci} 345962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw); 346062306a36Sopenharmony_ci 346162306a36Sopenharmony_ci/** 346262306a36Sopenharmony_ci * snd_soc_dapm_get_enum_double - dapm enumerated double mixer get callback 346362306a36Sopenharmony_ci * @kcontrol: mixer control 346462306a36Sopenharmony_ci * @ucontrol: control element information 346562306a36Sopenharmony_ci * 346662306a36Sopenharmony_ci * Callback to get the value of a dapm enumerated double mixer control. 346762306a36Sopenharmony_ci * 346862306a36Sopenharmony_ci * Returns 0 for success. 346962306a36Sopenharmony_ci */ 347062306a36Sopenharmony_ciint snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol, 347162306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 347262306a36Sopenharmony_ci{ 347362306a36Sopenharmony_ci struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); 347462306a36Sopenharmony_ci struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 347562306a36Sopenharmony_ci unsigned int reg_val, val; 347662306a36Sopenharmony_ci 347762306a36Sopenharmony_ci snd_soc_dapm_mutex_lock(dapm); 347862306a36Sopenharmony_ci if (e->reg != SND_SOC_NOPM && dapm_kcontrol_is_powered(kcontrol)) { 347962306a36Sopenharmony_ci reg_val = soc_dapm_read(dapm, e->reg); 348062306a36Sopenharmony_ci } else { 348162306a36Sopenharmony_ci reg_val = dapm_kcontrol_get_value(kcontrol); 348262306a36Sopenharmony_ci } 348362306a36Sopenharmony_ci snd_soc_dapm_mutex_unlock(dapm); 348462306a36Sopenharmony_ci 348562306a36Sopenharmony_ci val = (reg_val >> e->shift_l) & e->mask; 348662306a36Sopenharmony_ci ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(e, val); 348762306a36Sopenharmony_ci if (e->shift_l != e->shift_r) { 348862306a36Sopenharmony_ci val = (reg_val >> e->shift_r) & e->mask; 348962306a36Sopenharmony_ci val = snd_soc_enum_val_to_item(e, val); 349062306a36Sopenharmony_ci ucontrol->value.enumerated.item[1] = val; 349162306a36Sopenharmony_ci } 349262306a36Sopenharmony_ci 349362306a36Sopenharmony_ci return 0; 349462306a36Sopenharmony_ci} 349562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_double); 349662306a36Sopenharmony_ci 349762306a36Sopenharmony_ci/** 349862306a36Sopenharmony_ci * snd_soc_dapm_put_enum_double - dapm enumerated double mixer set callback 349962306a36Sopenharmony_ci * @kcontrol: mixer control 350062306a36Sopenharmony_ci * @ucontrol: control element information 350162306a36Sopenharmony_ci * 350262306a36Sopenharmony_ci * Callback to set the value of a dapm enumerated double mixer control. 350362306a36Sopenharmony_ci * 350462306a36Sopenharmony_ci * Returns 0 for success. 350562306a36Sopenharmony_ci */ 350662306a36Sopenharmony_ciint snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, 350762306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 350862306a36Sopenharmony_ci{ 350962306a36Sopenharmony_ci struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); 351062306a36Sopenharmony_ci struct snd_soc_card *card = dapm->card; 351162306a36Sopenharmony_ci struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 351262306a36Sopenharmony_ci unsigned int *item = ucontrol->value.enumerated.item; 351362306a36Sopenharmony_ci unsigned int val, change, reg_change = 0; 351462306a36Sopenharmony_ci unsigned int mask; 351562306a36Sopenharmony_ci struct snd_soc_dapm_update update = {}; 351662306a36Sopenharmony_ci int ret = 0; 351762306a36Sopenharmony_ci 351862306a36Sopenharmony_ci if (item[0] >= e->items) 351962306a36Sopenharmony_ci return -EINVAL; 352062306a36Sopenharmony_ci 352162306a36Sopenharmony_ci val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; 352262306a36Sopenharmony_ci mask = e->mask << e->shift_l; 352362306a36Sopenharmony_ci if (e->shift_l != e->shift_r) { 352462306a36Sopenharmony_ci if (item[1] > e->items) 352562306a36Sopenharmony_ci return -EINVAL; 352662306a36Sopenharmony_ci val |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_r; 352762306a36Sopenharmony_ci mask |= e->mask << e->shift_r; 352862306a36Sopenharmony_ci } 352962306a36Sopenharmony_ci 353062306a36Sopenharmony_ci snd_soc_dapm_mutex_lock(card); 353162306a36Sopenharmony_ci 353262306a36Sopenharmony_ci change = dapm_kcontrol_set_value(kcontrol, val); 353362306a36Sopenharmony_ci 353462306a36Sopenharmony_ci if (e->reg != SND_SOC_NOPM) 353562306a36Sopenharmony_ci reg_change = soc_dapm_test_bits(dapm, e->reg, mask, val); 353662306a36Sopenharmony_ci 353762306a36Sopenharmony_ci if (change || reg_change) { 353862306a36Sopenharmony_ci if (reg_change) { 353962306a36Sopenharmony_ci update.kcontrol = kcontrol; 354062306a36Sopenharmony_ci update.reg = e->reg; 354162306a36Sopenharmony_ci update.mask = mask; 354262306a36Sopenharmony_ci update.val = val; 354362306a36Sopenharmony_ci card->update = &update; 354462306a36Sopenharmony_ci } 354562306a36Sopenharmony_ci 354662306a36Sopenharmony_ci ret = soc_dapm_mux_update_power(card, kcontrol, item[0], e); 354762306a36Sopenharmony_ci 354862306a36Sopenharmony_ci card->update = NULL; 354962306a36Sopenharmony_ci } 355062306a36Sopenharmony_ci 355162306a36Sopenharmony_ci snd_soc_dapm_mutex_unlock(card); 355262306a36Sopenharmony_ci 355362306a36Sopenharmony_ci if (ret > 0) 355462306a36Sopenharmony_ci snd_soc_dpcm_runtime_update(card); 355562306a36Sopenharmony_ci 355662306a36Sopenharmony_ci return change; 355762306a36Sopenharmony_ci} 355862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double); 355962306a36Sopenharmony_ci 356062306a36Sopenharmony_ci/** 356162306a36Sopenharmony_ci * snd_soc_dapm_info_pin_switch - Info for a pin switch 356262306a36Sopenharmony_ci * 356362306a36Sopenharmony_ci * @kcontrol: mixer control 356462306a36Sopenharmony_ci * @uinfo: control element information 356562306a36Sopenharmony_ci * 356662306a36Sopenharmony_ci * Callback to provide information about a pin switch control. 356762306a36Sopenharmony_ci */ 356862306a36Sopenharmony_ciint snd_soc_dapm_info_pin_switch(struct snd_kcontrol *kcontrol, 356962306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 357062306a36Sopenharmony_ci{ 357162306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 357262306a36Sopenharmony_ci uinfo->count = 1; 357362306a36Sopenharmony_ci uinfo->value.integer.min = 0; 357462306a36Sopenharmony_ci uinfo->value.integer.max = 1; 357562306a36Sopenharmony_ci 357662306a36Sopenharmony_ci return 0; 357762306a36Sopenharmony_ci} 357862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_info_pin_switch); 357962306a36Sopenharmony_ci 358062306a36Sopenharmony_ci/** 358162306a36Sopenharmony_ci * snd_soc_dapm_get_pin_switch - Get information for a pin switch 358262306a36Sopenharmony_ci * 358362306a36Sopenharmony_ci * @kcontrol: mixer control 358462306a36Sopenharmony_ci * @ucontrol: Value 358562306a36Sopenharmony_ci */ 358662306a36Sopenharmony_ciint snd_soc_dapm_get_pin_switch(struct snd_kcontrol *kcontrol, 358762306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 358862306a36Sopenharmony_ci{ 358962306a36Sopenharmony_ci struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); 359062306a36Sopenharmony_ci const char *pin = (const char *)kcontrol->private_value; 359162306a36Sopenharmony_ci 359262306a36Sopenharmony_ci snd_soc_dapm_mutex_lock(card); 359362306a36Sopenharmony_ci 359462306a36Sopenharmony_ci ucontrol->value.integer.value[0] = 359562306a36Sopenharmony_ci snd_soc_dapm_get_pin_status(&card->dapm, pin); 359662306a36Sopenharmony_ci 359762306a36Sopenharmony_ci snd_soc_dapm_mutex_unlock(card); 359862306a36Sopenharmony_ci 359962306a36Sopenharmony_ci return 0; 360062306a36Sopenharmony_ci} 360162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_get_pin_switch); 360262306a36Sopenharmony_ci 360362306a36Sopenharmony_ci/** 360462306a36Sopenharmony_ci * snd_soc_dapm_put_pin_switch - Set information for a pin switch 360562306a36Sopenharmony_ci * 360662306a36Sopenharmony_ci * @kcontrol: mixer control 360762306a36Sopenharmony_ci * @ucontrol: Value 360862306a36Sopenharmony_ci */ 360962306a36Sopenharmony_ciint snd_soc_dapm_put_pin_switch(struct snd_kcontrol *kcontrol, 361062306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 361162306a36Sopenharmony_ci{ 361262306a36Sopenharmony_ci struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); 361362306a36Sopenharmony_ci const char *pin = (const char *)kcontrol->private_value; 361462306a36Sopenharmony_ci int ret; 361562306a36Sopenharmony_ci 361662306a36Sopenharmony_ci snd_soc_dapm_mutex_lock(card); 361762306a36Sopenharmony_ci ret = __snd_soc_dapm_set_pin(&card->dapm, pin, 361862306a36Sopenharmony_ci !!ucontrol->value.integer.value[0]); 361962306a36Sopenharmony_ci snd_soc_dapm_mutex_unlock(card); 362062306a36Sopenharmony_ci 362162306a36Sopenharmony_ci snd_soc_dapm_sync(&card->dapm); 362262306a36Sopenharmony_ci return ret; 362362306a36Sopenharmony_ci} 362462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_put_pin_switch); 362562306a36Sopenharmony_ci 362662306a36Sopenharmony_cistruct snd_soc_dapm_widget * 362762306a36Sopenharmony_cisnd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm, 362862306a36Sopenharmony_ci const struct snd_soc_dapm_widget *widget) 362962306a36Sopenharmony_ci{ 363062306a36Sopenharmony_ci enum snd_soc_dapm_direction dir; 363162306a36Sopenharmony_ci struct snd_soc_dapm_widget *w; 363262306a36Sopenharmony_ci const char *prefix; 363362306a36Sopenharmony_ci int ret = -ENOMEM; 363462306a36Sopenharmony_ci 363562306a36Sopenharmony_ci if ((w = dapm_cnew_widget(widget)) == NULL) 363662306a36Sopenharmony_ci goto cnew_failed; 363762306a36Sopenharmony_ci 363862306a36Sopenharmony_ci prefix = soc_dapm_prefix(dapm); 363962306a36Sopenharmony_ci if (prefix) 364062306a36Sopenharmony_ci w->name = kasprintf(GFP_KERNEL, "%s %s", prefix, widget->name); 364162306a36Sopenharmony_ci else 364262306a36Sopenharmony_ci w->name = kstrdup_const(widget->name, GFP_KERNEL); 364362306a36Sopenharmony_ci if (!w->name) 364462306a36Sopenharmony_ci goto name_failed; 364562306a36Sopenharmony_ci 364662306a36Sopenharmony_ci switch (w->id) { 364762306a36Sopenharmony_ci case snd_soc_dapm_regulator_supply: 364862306a36Sopenharmony_ci w->regulator = devm_regulator_get(dapm->dev, widget->name); 364962306a36Sopenharmony_ci if (IS_ERR(w->regulator)) { 365062306a36Sopenharmony_ci ret = PTR_ERR(w->regulator); 365162306a36Sopenharmony_ci goto request_failed; 365262306a36Sopenharmony_ci } 365362306a36Sopenharmony_ci 365462306a36Sopenharmony_ci if (w->on_val & SND_SOC_DAPM_REGULATOR_BYPASS) { 365562306a36Sopenharmony_ci ret = regulator_allow_bypass(w->regulator, true); 365662306a36Sopenharmony_ci if (ret != 0) 365762306a36Sopenharmony_ci dev_warn(dapm->dev, 365862306a36Sopenharmony_ci "ASoC: Failed to bypass %s: %d\n", 365962306a36Sopenharmony_ci w->name, ret); 366062306a36Sopenharmony_ci } 366162306a36Sopenharmony_ci break; 366262306a36Sopenharmony_ci case snd_soc_dapm_pinctrl: 366362306a36Sopenharmony_ci w->pinctrl = devm_pinctrl_get(dapm->dev); 366462306a36Sopenharmony_ci if (IS_ERR(w->pinctrl)) { 366562306a36Sopenharmony_ci ret = PTR_ERR(w->pinctrl); 366662306a36Sopenharmony_ci goto request_failed; 366762306a36Sopenharmony_ci } 366862306a36Sopenharmony_ci 366962306a36Sopenharmony_ci /* set to sleep_state when initializing */ 367062306a36Sopenharmony_ci dapm_pinctrl_event(w, NULL, SND_SOC_DAPM_POST_PMD); 367162306a36Sopenharmony_ci break; 367262306a36Sopenharmony_ci case snd_soc_dapm_clock_supply: 367362306a36Sopenharmony_ci w->clk = devm_clk_get(dapm->dev, widget->name); 367462306a36Sopenharmony_ci if (IS_ERR(w->clk)) { 367562306a36Sopenharmony_ci ret = PTR_ERR(w->clk); 367662306a36Sopenharmony_ci goto request_failed; 367762306a36Sopenharmony_ci } 367862306a36Sopenharmony_ci break; 367962306a36Sopenharmony_ci default: 368062306a36Sopenharmony_ci break; 368162306a36Sopenharmony_ci } 368262306a36Sopenharmony_ci 368362306a36Sopenharmony_ci switch (w->id) { 368462306a36Sopenharmony_ci case snd_soc_dapm_mic: 368562306a36Sopenharmony_ci w->is_ep = SND_SOC_DAPM_EP_SOURCE; 368662306a36Sopenharmony_ci w->power_check = dapm_generic_check_power; 368762306a36Sopenharmony_ci break; 368862306a36Sopenharmony_ci case snd_soc_dapm_input: 368962306a36Sopenharmony_ci if (!dapm->card->fully_routed) 369062306a36Sopenharmony_ci w->is_ep = SND_SOC_DAPM_EP_SOURCE; 369162306a36Sopenharmony_ci w->power_check = dapm_generic_check_power; 369262306a36Sopenharmony_ci break; 369362306a36Sopenharmony_ci case snd_soc_dapm_spk: 369462306a36Sopenharmony_ci case snd_soc_dapm_hp: 369562306a36Sopenharmony_ci w->is_ep = SND_SOC_DAPM_EP_SINK; 369662306a36Sopenharmony_ci w->power_check = dapm_generic_check_power; 369762306a36Sopenharmony_ci break; 369862306a36Sopenharmony_ci case snd_soc_dapm_output: 369962306a36Sopenharmony_ci if (!dapm->card->fully_routed) 370062306a36Sopenharmony_ci w->is_ep = SND_SOC_DAPM_EP_SINK; 370162306a36Sopenharmony_ci w->power_check = dapm_generic_check_power; 370262306a36Sopenharmony_ci break; 370362306a36Sopenharmony_ci case snd_soc_dapm_vmid: 370462306a36Sopenharmony_ci case snd_soc_dapm_siggen: 370562306a36Sopenharmony_ci w->is_ep = SND_SOC_DAPM_EP_SOURCE; 370662306a36Sopenharmony_ci w->power_check = dapm_always_on_check_power; 370762306a36Sopenharmony_ci break; 370862306a36Sopenharmony_ci case snd_soc_dapm_sink: 370962306a36Sopenharmony_ci w->is_ep = SND_SOC_DAPM_EP_SINK; 371062306a36Sopenharmony_ci w->power_check = dapm_always_on_check_power; 371162306a36Sopenharmony_ci break; 371262306a36Sopenharmony_ci 371362306a36Sopenharmony_ci case snd_soc_dapm_mux: 371462306a36Sopenharmony_ci case snd_soc_dapm_demux: 371562306a36Sopenharmony_ci case snd_soc_dapm_switch: 371662306a36Sopenharmony_ci case snd_soc_dapm_mixer: 371762306a36Sopenharmony_ci case snd_soc_dapm_mixer_named_ctl: 371862306a36Sopenharmony_ci case snd_soc_dapm_adc: 371962306a36Sopenharmony_ci case snd_soc_dapm_aif_out: 372062306a36Sopenharmony_ci case snd_soc_dapm_dac: 372162306a36Sopenharmony_ci case snd_soc_dapm_aif_in: 372262306a36Sopenharmony_ci case snd_soc_dapm_pga: 372362306a36Sopenharmony_ci case snd_soc_dapm_buffer: 372462306a36Sopenharmony_ci case snd_soc_dapm_scheduler: 372562306a36Sopenharmony_ci case snd_soc_dapm_effect: 372662306a36Sopenharmony_ci case snd_soc_dapm_src: 372762306a36Sopenharmony_ci case snd_soc_dapm_asrc: 372862306a36Sopenharmony_ci case snd_soc_dapm_encoder: 372962306a36Sopenharmony_ci case snd_soc_dapm_decoder: 373062306a36Sopenharmony_ci case snd_soc_dapm_out_drv: 373162306a36Sopenharmony_ci case snd_soc_dapm_micbias: 373262306a36Sopenharmony_ci case snd_soc_dapm_line: 373362306a36Sopenharmony_ci case snd_soc_dapm_dai_link: 373462306a36Sopenharmony_ci case snd_soc_dapm_dai_out: 373562306a36Sopenharmony_ci case snd_soc_dapm_dai_in: 373662306a36Sopenharmony_ci w->power_check = dapm_generic_check_power; 373762306a36Sopenharmony_ci break; 373862306a36Sopenharmony_ci case snd_soc_dapm_supply: 373962306a36Sopenharmony_ci case snd_soc_dapm_regulator_supply: 374062306a36Sopenharmony_ci case snd_soc_dapm_pinctrl: 374162306a36Sopenharmony_ci case snd_soc_dapm_clock_supply: 374262306a36Sopenharmony_ci case snd_soc_dapm_kcontrol: 374362306a36Sopenharmony_ci w->is_supply = 1; 374462306a36Sopenharmony_ci w->power_check = dapm_supply_check_power; 374562306a36Sopenharmony_ci break; 374662306a36Sopenharmony_ci default: 374762306a36Sopenharmony_ci w->power_check = dapm_always_on_check_power; 374862306a36Sopenharmony_ci break; 374962306a36Sopenharmony_ci } 375062306a36Sopenharmony_ci 375162306a36Sopenharmony_ci w->dapm = dapm; 375262306a36Sopenharmony_ci INIT_LIST_HEAD(&w->list); 375362306a36Sopenharmony_ci INIT_LIST_HEAD(&w->dirty); 375462306a36Sopenharmony_ci /* see for_each_card_widgets */ 375562306a36Sopenharmony_ci list_add_tail(&w->list, &dapm->card->widgets); 375662306a36Sopenharmony_ci 375762306a36Sopenharmony_ci snd_soc_dapm_for_each_direction(dir) { 375862306a36Sopenharmony_ci INIT_LIST_HEAD(&w->edges[dir]); 375962306a36Sopenharmony_ci w->endpoints[dir] = -1; 376062306a36Sopenharmony_ci } 376162306a36Sopenharmony_ci 376262306a36Sopenharmony_ci /* machine layer sets up unconnected pins and insertions */ 376362306a36Sopenharmony_ci w->connected = 1; 376462306a36Sopenharmony_ci return w; 376562306a36Sopenharmony_ci 376662306a36Sopenharmony_cirequest_failed: 376762306a36Sopenharmony_ci dev_err_probe(dapm->dev, ret, "ASoC: Failed to request %s\n", 376862306a36Sopenharmony_ci w->name); 376962306a36Sopenharmony_ci kfree_const(w->name); 377062306a36Sopenharmony_ciname_failed: 377162306a36Sopenharmony_ci kfree_const(w->sname); 377262306a36Sopenharmony_ci kfree(w); 377362306a36Sopenharmony_cicnew_failed: 377462306a36Sopenharmony_ci return ERR_PTR(ret); 377562306a36Sopenharmony_ci} 377662306a36Sopenharmony_ci 377762306a36Sopenharmony_ci/** 377862306a36Sopenharmony_ci * snd_soc_dapm_new_control - create new dapm control 377962306a36Sopenharmony_ci * @dapm: DAPM context 378062306a36Sopenharmony_ci * @widget: widget template 378162306a36Sopenharmony_ci * 378262306a36Sopenharmony_ci * Creates new DAPM control based upon a template. 378362306a36Sopenharmony_ci * 378462306a36Sopenharmony_ci * Returns a widget pointer on success or an error pointer on failure 378562306a36Sopenharmony_ci */ 378662306a36Sopenharmony_cistruct snd_soc_dapm_widget * 378762306a36Sopenharmony_cisnd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, 378862306a36Sopenharmony_ci const struct snd_soc_dapm_widget *widget) 378962306a36Sopenharmony_ci{ 379062306a36Sopenharmony_ci struct snd_soc_dapm_widget *w; 379162306a36Sopenharmony_ci 379262306a36Sopenharmony_ci snd_soc_dapm_mutex_lock(dapm); 379362306a36Sopenharmony_ci w = snd_soc_dapm_new_control_unlocked(dapm, widget); 379462306a36Sopenharmony_ci snd_soc_dapm_mutex_unlock(dapm); 379562306a36Sopenharmony_ci 379662306a36Sopenharmony_ci return w; 379762306a36Sopenharmony_ci} 379862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_new_control); 379962306a36Sopenharmony_ci 380062306a36Sopenharmony_ci/** 380162306a36Sopenharmony_ci * snd_soc_dapm_new_controls - create new dapm controls 380262306a36Sopenharmony_ci * @dapm: DAPM context 380362306a36Sopenharmony_ci * @widget: widget array 380462306a36Sopenharmony_ci * @num: number of widgets 380562306a36Sopenharmony_ci * 380662306a36Sopenharmony_ci * Creates new DAPM controls based upon the templates. 380762306a36Sopenharmony_ci * 380862306a36Sopenharmony_ci * Returns 0 for success else error. 380962306a36Sopenharmony_ci */ 381062306a36Sopenharmony_ciint snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm, 381162306a36Sopenharmony_ci const struct snd_soc_dapm_widget *widget, 381262306a36Sopenharmony_ci int num) 381362306a36Sopenharmony_ci{ 381462306a36Sopenharmony_ci int i; 381562306a36Sopenharmony_ci int ret = 0; 381662306a36Sopenharmony_ci 381762306a36Sopenharmony_ci snd_soc_dapm_mutex_lock_root(dapm); 381862306a36Sopenharmony_ci for (i = 0; i < num; i++) { 381962306a36Sopenharmony_ci struct snd_soc_dapm_widget *w = snd_soc_dapm_new_control_unlocked(dapm, widget); 382062306a36Sopenharmony_ci if (IS_ERR(w)) { 382162306a36Sopenharmony_ci ret = PTR_ERR(w); 382262306a36Sopenharmony_ci break; 382362306a36Sopenharmony_ci } 382462306a36Sopenharmony_ci widget++; 382562306a36Sopenharmony_ci } 382662306a36Sopenharmony_ci snd_soc_dapm_mutex_unlock(dapm); 382762306a36Sopenharmony_ci return ret; 382862306a36Sopenharmony_ci} 382962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_new_controls); 383062306a36Sopenharmony_ci 383162306a36Sopenharmony_cistatic int 383262306a36Sopenharmony_cisnd_soc_dai_link_event_pre_pmu(struct snd_soc_dapm_widget *w, 383362306a36Sopenharmony_ci struct snd_pcm_substream *substream) 383462306a36Sopenharmony_ci{ 383562306a36Sopenharmony_ci struct snd_soc_dapm_path *path; 383662306a36Sopenharmony_ci struct snd_soc_dai *source, *sink; 383762306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 383862306a36Sopenharmony_ci struct snd_pcm_hw_params *params = NULL; 383962306a36Sopenharmony_ci const struct snd_soc_pcm_stream *config = NULL; 384062306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = NULL; 384162306a36Sopenharmony_ci unsigned int fmt; 384262306a36Sopenharmony_ci int ret = 0; 384362306a36Sopenharmony_ci 384462306a36Sopenharmony_ci /* 384562306a36Sopenharmony_ci * NOTE 384662306a36Sopenharmony_ci * 384762306a36Sopenharmony_ci * snd_pcm_hw_params is quite large (608 bytes on arm64) and is 384862306a36Sopenharmony_ci * starting to get a bit excessive for allocation on the stack, 384962306a36Sopenharmony_ci * especially when you're building with some of the KASAN type 385062306a36Sopenharmony_ci * stuff that increases stack usage. 385162306a36Sopenharmony_ci * So, we use kzalloc()/kfree() for params in this function. 385262306a36Sopenharmony_ci */ 385362306a36Sopenharmony_ci params = kzalloc(sizeof(*params), GFP_KERNEL); 385462306a36Sopenharmony_ci if (!params) 385562306a36Sopenharmony_ci return -ENOMEM; 385662306a36Sopenharmony_ci 385762306a36Sopenharmony_ci runtime = kzalloc(sizeof(*runtime), GFP_KERNEL); 385862306a36Sopenharmony_ci if (!runtime) { 385962306a36Sopenharmony_ci ret = -ENOMEM; 386062306a36Sopenharmony_ci goto out; 386162306a36Sopenharmony_ci } 386262306a36Sopenharmony_ci 386362306a36Sopenharmony_ci substream->runtime = runtime; 386462306a36Sopenharmony_ci 386562306a36Sopenharmony_ci substream->stream = SNDRV_PCM_STREAM_CAPTURE; 386662306a36Sopenharmony_ci snd_soc_dapm_widget_for_each_source_path(w, path) { 386762306a36Sopenharmony_ci source = path->source->priv; 386862306a36Sopenharmony_ci 386962306a36Sopenharmony_ci ret = snd_soc_dai_startup(source, substream); 387062306a36Sopenharmony_ci if (ret < 0) 387162306a36Sopenharmony_ci goto out; 387262306a36Sopenharmony_ci 387362306a36Sopenharmony_ci snd_soc_dai_activate(source, substream->stream); 387462306a36Sopenharmony_ci } 387562306a36Sopenharmony_ci 387662306a36Sopenharmony_ci substream->stream = SNDRV_PCM_STREAM_PLAYBACK; 387762306a36Sopenharmony_ci snd_soc_dapm_widget_for_each_sink_path(w, path) { 387862306a36Sopenharmony_ci sink = path->sink->priv; 387962306a36Sopenharmony_ci 388062306a36Sopenharmony_ci ret = snd_soc_dai_startup(sink, substream); 388162306a36Sopenharmony_ci if (ret < 0) 388262306a36Sopenharmony_ci goto out; 388362306a36Sopenharmony_ci 388462306a36Sopenharmony_ci snd_soc_dai_activate(sink, substream->stream); 388562306a36Sopenharmony_ci } 388662306a36Sopenharmony_ci 388762306a36Sopenharmony_ci substream->hw_opened = 1; 388862306a36Sopenharmony_ci 388962306a36Sopenharmony_ci /* 389062306a36Sopenharmony_ci * Note: getting the config after .startup() gives a chance to 389162306a36Sopenharmony_ci * either party on the link to alter the configuration if 389262306a36Sopenharmony_ci * necessary 389362306a36Sopenharmony_ci */ 389462306a36Sopenharmony_ci config = rtd->dai_link->c2c_params + rtd->c2c_params_select; 389562306a36Sopenharmony_ci if (!config) { 389662306a36Sopenharmony_ci dev_err(w->dapm->dev, "ASoC: link config missing\n"); 389762306a36Sopenharmony_ci ret = -EINVAL; 389862306a36Sopenharmony_ci goto out; 389962306a36Sopenharmony_ci } 390062306a36Sopenharmony_ci 390162306a36Sopenharmony_ci /* Be a little careful as we don't want to overflow the mask array */ 390262306a36Sopenharmony_ci if (!config->formats) { 390362306a36Sopenharmony_ci dev_warn(w->dapm->dev, "ASoC: Invalid format was specified\n"); 390462306a36Sopenharmony_ci 390562306a36Sopenharmony_ci ret = -EINVAL; 390662306a36Sopenharmony_ci goto out; 390762306a36Sopenharmony_ci } 390862306a36Sopenharmony_ci 390962306a36Sopenharmony_ci fmt = ffs(config->formats) - 1; 391062306a36Sopenharmony_ci 391162306a36Sopenharmony_ci snd_mask_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), fmt); 391262306a36Sopenharmony_ci hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE)->min = 391362306a36Sopenharmony_ci config->rate_min; 391462306a36Sopenharmony_ci hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE)->max = 391562306a36Sopenharmony_ci config->rate_max; 391662306a36Sopenharmony_ci hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS)->min 391762306a36Sopenharmony_ci = config->channels_min; 391862306a36Sopenharmony_ci hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS)->max 391962306a36Sopenharmony_ci = config->channels_max; 392062306a36Sopenharmony_ci 392162306a36Sopenharmony_ci substream->stream = SNDRV_PCM_STREAM_CAPTURE; 392262306a36Sopenharmony_ci snd_soc_dapm_widget_for_each_source_path(w, path) { 392362306a36Sopenharmony_ci source = path->source->priv; 392462306a36Sopenharmony_ci 392562306a36Sopenharmony_ci ret = snd_soc_dai_hw_params(source, substream, params); 392662306a36Sopenharmony_ci if (ret < 0) 392762306a36Sopenharmony_ci goto out; 392862306a36Sopenharmony_ci 392962306a36Sopenharmony_ci dapm_update_dai_unlocked(substream, params, source); 393062306a36Sopenharmony_ci } 393162306a36Sopenharmony_ci 393262306a36Sopenharmony_ci substream->stream = SNDRV_PCM_STREAM_PLAYBACK; 393362306a36Sopenharmony_ci snd_soc_dapm_widget_for_each_sink_path(w, path) { 393462306a36Sopenharmony_ci sink = path->sink->priv; 393562306a36Sopenharmony_ci 393662306a36Sopenharmony_ci ret = snd_soc_dai_hw_params(sink, substream, params); 393762306a36Sopenharmony_ci if (ret < 0) 393862306a36Sopenharmony_ci goto out; 393962306a36Sopenharmony_ci 394062306a36Sopenharmony_ci dapm_update_dai_unlocked(substream, params, sink); 394162306a36Sopenharmony_ci } 394262306a36Sopenharmony_ci 394362306a36Sopenharmony_ci runtime->format = params_format(params); 394462306a36Sopenharmony_ci runtime->subformat = params_subformat(params); 394562306a36Sopenharmony_ci runtime->channels = params_channels(params); 394662306a36Sopenharmony_ci runtime->rate = params_rate(params); 394762306a36Sopenharmony_ci 394862306a36Sopenharmony_ciout: 394962306a36Sopenharmony_ci /* see above NOTE */ 395062306a36Sopenharmony_ci kfree(params); 395162306a36Sopenharmony_ci 395262306a36Sopenharmony_ci return ret; 395362306a36Sopenharmony_ci} 395462306a36Sopenharmony_ci 395562306a36Sopenharmony_cistatic int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, 395662306a36Sopenharmony_ci struct snd_kcontrol *kcontrol, int event) 395762306a36Sopenharmony_ci{ 395862306a36Sopenharmony_ci struct snd_soc_dapm_path *path; 395962306a36Sopenharmony_ci struct snd_soc_dai *source, *sink; 396062306a36Sopenharmony_ci struct snd_pcm_substream *substream = w->priv; 396162306a36Sopenharmony_ci int ret = 0, saved_stream = substream->stream; 396262306a36Sopenharmony_ci 396362306a36Sopenharmony_ci if (WARN_ON(list_empty(&w->edges[SND_SOC_DAPM_DIR_OUT]) || 396462306a36Sopenharmony_ci list_empty(&w->edges[SND_SOC_DAPM_DIR_IN]))) 396562306a36Sopenharmony_ci return -EINVAL; 396662306a36Sopenharmony_ci 396762306a36Sopenharmony_ci switch (event) { 396862306a36Sopenharmony_ci case SND_SOC_DAPM_PRE_PMU: 396962306a36Sopenharmony_ci ret = snd_soc_dai_link_event_pre_pmu(w, substream); 397062306a36Sopenharmony_ci if (ret < 0) 397162306a36Sopenharmony_ci goto out; 397262306a36Sopenharmony_ci 397362306a36Sopenharmony_ci break; 397462306a36Sopenharmony_ci 397562306a36Sopenharmony_ci case SND_SOC_DAPM_POST_PMU: 397662306a36Sopenharmony_ci snd_soc_dapm_widget_for_each_sink_path(w, path) { 397762306a36Sopenharmony_ci sink = path->sink->priv; 397862306a36Sopenharmony_ci 397962306a36Sopenharmony_ci snd_soc_dai_digital_mute(sink, 0, SNDRV_PCM_STREAM_PLAYBACK); 398062306a36Sopenharmony_ci ret = 0; 398162306a36Sopenharmony_ci } 398262306a36Sopenharmony_ci break; 398362306a36Sopenharmony_ci 398462306a36Sopenharmony_ci case SND_SOC_DAPM_PRE_PMD: 398562306a36Sopenharmony_ci snd_soc_dapm_widget_for_each_sink_path(w, path) { 398662306a36Sopenharmony_ci sink = path->sink->priv; 398762306a36Sopenharmony_ci 398862306a36Sopenharmony_ci snd_soc_dai_digital_mute(sink, 1, SNDRV_PCM_STREAM_PLAYBACK); 398962306a36Sopenharmony_ci ret = 0; 399062306a36Sopenharmony_ci } 399162306a36Sopenharmony_ci 399262306a36Sopenharmony_ci substream->stream = SNDRV_PCM_STREAM_CAPTURE; 399362306a36Sopenharmony_ci snd_soc_dapm_widget_for_each_source_path(w, path) { 399462306a36Sopenharmony_ci source = path->source->priv; 399562306a36Sopenharmony_ci snd_soc_dai_hw_free(source, substream, 0); 399662306a36Sopenharmony_ci } 399762306a36Sopenharmony_ci 399862306a36Sopenharmony_ci substream->stream = SNDRV_PCM_STREAM_PLAYBACK; 399962306a36Sopenharmony_ci snd_soc_dapm_widget_for_each_sink_path(w, path) { 400062306a36Sopenharmony_ci sink = path->sink->priv; 400162306a36Sopenharmony_ci snd_soc_dai_hw_free(sink, substream, 0); 400262306a36Sopenharmony_ci } 400362306a36Sopenharmony_ci 400462306a36Sopenharmony_ci substream->stream = SNDRV_PCM_STREAM_CAPTURE; 400562306a36Sopenharmony_ci snd_soc_dapm_widget_for_each_source_path(w, path) { 400662306a36Sopenharmony_ci source = path->source->priv; 400762306a36Sopenharmony_ci snd_soc_dai_deactivate(source, substream->stream); 400862306a36Sopenharmony_ci snd_soc_dai_shutdown(source, substream, 0); 400962306a36Sopenharmony_ci } 401062306a36Sopenharmony_ci 401162306a36Sopenharmony_ci substream->stream = SNDRV_PCM_STREAM_PLAYBACK; 401262306a36Sopenharmony_ci snd_soc_dapm_widget_for_each_sink_path(w, path) { 401362306a36Sopenharmony_ci sink = path->sink->priv; 401462306a36Sopenharmony_ci snd_soc_dai_deactivate(sink, substream->stream); 401562306a36Sopenharmony_ci snd_soc_dai_shutdown(sink, substream, 0); 401662306a36Sopenharmony_ci } 401762306a36Sopenharmony_ci break; 401862306a36Sopenharmony_ci 401962306a36Sopenharmony_ci case SND_SOC_DAPM_POST_PMD: 402062306a36Sopenharmony_ci kfree(substream->runtime); 402162306a36Sopenharmony_ci break; 402262306a36Sopenharmony_ci 402362306a36Sopenharmony_ci default: 402462306a36Sopenharmony_ci WARN(1, "Unknown event %d\n", event); 402562306a36Sopenharmony_ci ret = -EINVAL; 402662306a36Sopenharmony_ci } 402762306a36Sopenharmony_ci 402862306a36Sopenharmony_ciout: 402962306a36Sopenharmony_ci /* Restore the substream direction */ 403062306a36Sopenharmony_ci substream->stream = saved_stream; 403162306a36Sopenharmony_ci return ret; 403262306a36Sopenharmony_ci} 403362306a36Sopenharmony_ci 403462306a36Sopenharmony_cistatic int snd_soc_dapm_dai_link_get(struct snd_kcontrol *kcontrol, 403562306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 403662306a36Sopenharmony_ci{ 403762306a36Sopenharmony_ci struct snd_soc_dapm_widget *w = snd_kcontrol_chip(kcontrol); 403862306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = w->priv; 403962306a36Sopenharmony_ci 404062306a36Sopenharmony_ci ucontrol->value.enumerated.item[0] = rtd->c2c_params_select; 404162306a36Sopenharmony_ci 404262306a36Sopenharmony_ci return 0; 404362306a36Sopenharmony_ci} 404462306a36Sopenharmony_ci 404562306a36Sopenharmony_cistatic int snd_soc_dapm_dai_link_put(struct snd_kcontrol *kcontrol, 404662306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 404762306a36Sopenharmony_ci{ 404862306a36Sopenharmony_ci struct snd_soc_dapm_widget *w = snd_kcontrol_chip(kcontrol); 404962306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = w->priv; 405062306a36Sopenharmony_ci 405162306a36Sopenharmony_ci /* Can't change the config when widget is already powered */ 405262306a36Sopenharmony_ci if (w->power) 405362306a36Sopenharmony_ci return -EBUSY; 405462306a36Sopenharmony_ci 405562306a36Sopenharmony_ci if (ucontrol->value.enumerated.item[0] == rtd->c2c_params_select) 405662306a36Sopenharmony_ci return 0; 405762306a36Sopenharmony_ci 405862306a36Sopenharmony_ci if (ucontrol->value.enumerated.item[0] >= rtd->dai_link->num_c2c_params) 405962306a36Sopenharmony_ci return -EINVAL; 406062306a36Sopenharmony_ci 406162306a36Sopenharmony_ci rtd->c2c_params_select = ucontrol->value.enumerated.item[0]; 406262306a36Sopenharmony_ci 406362306a36Sopenharmony_ci return 1; 406462306a36Sopenharmony_ci} 406562306a36Sopenharmony_ci 406662306a36Sopenharmony_cistatic void 406762306a36Sopenharmony_cisnd_soc_dapm_free_kcontrol(struct snd_soc_card *card, 406862306a36Sopenharmony_ci unsigned long *private_value, 406962306a36Sopenharmony_ci int num_c2c_params, 407062306a36Sopenharmony_ci const char **w_param_text) 407162306a36Sopenharmony_ci{ 407262306a36Sopenharmony_ci int count; 407362306a36Sopenharmony_ci 407462306a36Sopenharmony_ci devm_kfree(card->dev, (void *)*private_value); 407562306a36Sopenharmony_ci 407662306a36Sopenharmony_ci if (!w_param_text) 407762306a36Sopenharmony_ci return; 407862306a36Sopenharmony_ci 407962306a36Sopenharmony_ci for (count = 0 ; count < num_c2c_params; count++) 408062306a36Sopenharmony_ci devm_kfree(card->dev, (void *)w_param_text[count]); 408162306a36Sopenharmony_ci devm_kfree(card->dev, w_param_text); 408262306a36Sopenharmony_ci} 408362306a36Sopenharmony_ci 408462306a36Sopenharmony_cistatic struct snd_kcontrol_new * 408562306a36Sopenharmony_cisnd_soc_dapm_alloc_kcontrol(struct snd_soc_card *card, 408662306a36Sopenharmony_ci char *link_name, 408762306a36Sopenharmony_ci const struct snd_soc_pcm_stream *c2c_params, 408862306a36Sopenharmony_ci int num_c2c_params, const char **w_param_text, 408962306a36Sopenharmony_ci unsigned long *private_value) 409062306a36Sopenharmony_ci{ 409162306a36Sopenharmony_ci struct soc_enum w_param_enum[] = { 409262306a36Sopenharmony_ci SOC_ENUM_SINGLE(0, 0, 0, NULL), 409362306a36Sopenharmony_ci }; 409462306a36Sopenharmony_ci struct snd_kcontrol_new kcontrol_dai_link[] = { 409562306a36Sopenharmony_ci SOC_ENUM_EXT(NULL, w_param_enum[0], 409662306a36Sopenharmony_ci snd_soc_dapm_dai_link_get, 409762306a36Sopenharmony_ci snd_soc_dapm_dai_link_put), 409862306a36Sopenharmony_ci }; 409962306a36Sopenharmony_ci struct snd_kcontrol_new *kcontrol_news; 410062306a36Sopenharmony_ci const struct snd_soc_pcm_stream *config = c2c_params; 410162306a36Sopenharmony_ci int count; 410262306a36Sopenharmony_ci 410362306a36Sopenharmony_ci for (count = 0 ; count < num_c2c_params; count++) { 410462306a36Sopenharmony_ci if (!config->stream_name) { 410562306a36Sopenharmony_ci dev_warn(card->dapm.dev, 410662306a36Sopenharmony_ci "ASoC: anonymous config %d for dai link %s\n", 410762306a36Sopenharmony_ci count, link_name); 410862306a36Sopenharmony_ci w_param_text[count] = 410962306a36Sopenharmony_ci devm_kasprintf(card->dev, GFP_KERNEL, 411062306a36Sopenharmony_ci "Anonymous Configuration %d", 411162306a36Sopenharmony_ci count); 411262306a36Sopenharmony_ci } else { 411362306a36Sopenharmony_ci w_param_text[count] = devm_kmemdup(card->dev, 411462306a36Sopenharmony_ci config->stream_name, 411562306a36Sopenharmony_ci strlen(config->stream_name) + 1, 411662306a36Sopenharmony_ci GFP_KERNEL); 411762306a36Sopenharmony_ci } 411862306a36Sopenharmony_ci if (!w_param_text[count]) 411962306a36Sopenharmony_ci goto outfree_w_param; 412062306a36Sopenharmony_ci config++; 412162306a36Sopenharmony_ci } 412262306a36Sopenharmony_ci 412362306a36Sopenharmony_ci w_param_enum[0].items = num_c2c_params; 412462306a36Sopenharmony_ci w_param_enum[0].texts = w_param_text; 412562306a36Sopenharmony_ci 412662306a36Sopenharmony_ci *private_value = 412762306a36Sopenharmony_ci (unsigned long) devm_kmemdup(card->dev, 412862306a36Sopenharmony_ci (void *)(kcontrol_dai_link[0].private_value), 412962306a36Sopenharmony_ci sizeof(struct soc_enum), GFP_KERNEL); 413062306a36Sopenharmony_ci if (!*private_value) { 413162306a36Sopenharmony_ci dev_err(card->dev, "ASoC: Failed to create control for %s widget\n", 413262306a36Sopenharmony_ci link_name); 413362306a36Sopenharmony_ci goto outfree_w_param; 413462306a36Sopenharmony_ci } 413562306a36Sopenharmony_ci kcontrol_dai_link[0].private_value = *private_value; 413662306a36Sopenharmony_ci /* duplicate kcontrol_dai_link on heap so that memory persists */ 413762306a36Sopenharmony_ci kcontrol_news = devm_kmemdup(card->dev, &kcontrol_dai_link[0], 413862306a36Sopenharmony_ci sizeof(struct snd_kcontrol_new), 413962306a36Sopenharmony_ci GFP_KERNEL); 414062306a36Sopenharmony_ci if (!kcontrol_news) { 414162306a36Sopenharmony_ci dev_err(card->dev, "ASoC: Failed to create control for %s widget\n", 414262306a36Sopenharmony_ci link_name); 414362306a36Sopenharmony_ci goto outfree_w_param; 414462306a36Sopenharmony_ci } 414562306a36Sopenharmony_ci return kcontrol_news; 414662306a36Sopenharmony_ci 414762306a36Sopenharmony_cioutfree_w_param: 414862306a36Sopenharmony_ci snd_soc_dapm_free_kcontrol(card, private_value, num_c2c_params, w_param_text); 414962306a36Sopenharmony_ci return NULL; 415062306a36Sopenharmony_ci} 415162306a36Sopenharmony_ci 415262306a36Sopenharmony_cistatic struct snd_soc_dapm_widget * 415362306a36Sopenharmony_cisnd_soc_dapm_new_dai(struct snd_soc_card *card, 415462306a36Sopenharmony_ci struct snd_pcm_substream *substream, 415562306a36Sopenharmony_ci char *id) 415662306a36Sopenharmony_ci{ 415762306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 415862306a36Sopenharmony_ci struct snd_soc_dapm_widget template; 415962306a36Sopenharmony_ci struct snd_soc_dapm_widget *w; 416062306a36Sopenharmony_ci const struct snd_kcontrol_new *kcontrol_news; 416162306a36Sopenharmony_ci int num_kcontrols; 416262306a36Sopenharmony_ci const char **w_param_text; 416362306a36Sopenharmony_ci unsigned long private_value = 0; 416462306a36Sopenharmony_ci char *link_name; 416562306a36Sopenharmony_ci int ret = -ENOMEM; 416662306a36Sopenharmony_ci 416762306a36Sopenharmony_ci link_name = devm_kasprintf(card->dev, GFP_KERNEL, "%s-%s", 416862306a36Sopenharmony_ci rtd->dai_link->name, id); 416962306a36Sopenharmony_ci if (!link_name) 417062306a36Sopenharmony_ci goto name_fail; 417162306a36Sopenharmony_ci 417262306a36Sopenharmony_ci /* allocate memory for control, only in case of multiple configs */ 417362306a36Sopenharmony_ci w_param_text = NULL; 417462306a36Sopenharmony_ci kcontrol_news = NULL; 417562306a36Sopenharmony_ci num_kcontrols = 0; 417662306a36Sopenharmony_ci if (rtd->dai_link->num_c2c_params > 1) { 417762306a36Sopenharmony_ci w_param_text = devm_kcalloc(card->dev, 417862306a36Sopenharmony_ci rtd->dai_link->num_c2c_params, 417962306a36Sopenharmony_ci sizeof(char *), GFP_KERNEL); 418062306a36Sopenharmony_ci if (!w_param_text) 418162306a36Sopenharmony_ci goto param_fail; 418262306a36Sopenharmony_ci 418362306a36Sopenharmony_ci num_kcontrols = 1; 418462306a36Sopenharmony_ci kcontrol_news = snd_soc_dapm_alloc_kcontrol(card, link_name, 418562306a36Sopenharmony_ci rtd->dai_link->c2c_params, 418662306a36Sopenharmony_ci rtd->dai_link->num_c2c_params, 418762306a36Sopenharmony_ci w_param_text, &private_value); 418862306a36Sopenharmony_ci if (!kcontrol_news) 418962306a36Sopenharmony_ci goto param_fail; 419062306a36Sopenharmony_ci } 419162306a36Sopenharmony_ci 419262306a36Sopenharmony_ci memset(&template, 0, sizeof(template)); 419362306a36Sopenharmony_ci template.reg = SND_SOC_NOPM; 419462306a36Sopenharmony_ci template.id = snd_soc_dapm_dai_link; 419562306a36Sopenharmony_ci template.name = link_name; 419662306a36Sopenharmony_ci template.event = snd_soc_dai_link_event; 419762306a36Sopenharmony_ci template.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | 419862306a36Sopenharmony_ci SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD; 419962306a36Sopenharmony_ci template.kcontrol_news = kcontrol_news; 420062306a36Sopenharmony_ci template.num_kcontrols = num_kcontrols; 420162306a36Sopenharmony_ci 420262306a36Sopenharmony_ci dev_dbg(card->dev, "ASoC: adding %s widget\n", link_name); 420362306a36Sopenharmony_ci 420462306a36Sopenharmony_ci w = snd_soc_dapm_new_control_unlocked(&card->dapm, &template); 420562306a36Sopenharmony_ci if (IS_ERR(w)) { 420662306a36Sopenharmony_ci ret = PTR_ERR(w); 420762306a36Sopenharmony_ci goto outfree_kcontrol_news; 420862306a36Sopenharmony_ci } 420962306a36Sopenharmony_ci 421062306a36Sopenharmony_ci w->priv = substream; 421162306a36Sopenharmony_ci 421262306a36Sopenharmony_ci return w; 421362306a36Sopenharmony_ci 421462306a36Sopenharmony_cioutfree_kcontrol_news: 421562306a36Sopenharmony_ci devm_kfree(card->dev, (void *)template.kcontrol_news); 421662306a36Sopenharmony_ci snd_soc_dapm_free_kcontrol(card, &private_value, 421762306a36Sopenharmony_ci rtd->dai_link->num_c2c_params, w_param_text); 421862306a36Sopenharmony_ciparam_fail: 421962306a36Sopenharmony_ci devm_kfree(card->dev, link_name); 422062306a36Sopenharmony_ciname_fail: 422162306a36Sopenharmony_ci dev_err(rtd->dev, "ASoC: Failed to create %s-%s widget: %d\n", 422262306a36Sopenharmony_ci rtd->dai_link->name, id, ret); 422362306a36Sopenharmony_ci return ERR_PTR(ret); 422462306a36Sopenharmony_ci} 422562306a36Sopenharmony_ci 422662306a36Sopenharmony_ci/** 422762306a36Sopenharmony_ci * snd_soc_dapm_new_dai_widgets - Create new DAPM widgets 422862306a36Sopenharmony_ci * @dapm: DAPM context 422962306a36Sopenharmony_ci * @dai: parent DAI 423062306a36Sopenharmony_ci * 423162306a36Sopenharmony_ci * Returns 0 on success, error code otherwise. 423262306a36Sopenharmony_ci */ 423362306a36Sopenharmony_ciint snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm, 423462306a36Sopenharmony_ci struct snd_soc_dai *dai) 423562306a36Sopenharmony_ci{ 423662306a36Sopenharmony_ci struct snd_soc_dapm_widget template; 423762306a36Sopenharmony_ci struct snd_soc_dapm_widget *w; 423862306a36Sopenharmony_ci 423962306a36Sopenharmony_ci WARN_ON(dapm->dev != dai->dev); 424062306a36Sopenharmony_ci 424162306a36Sopenharmony_ci memset(&template, 0, sizeof(template)); 424262306a36Sopenharmony_ci template.reg = SND_SOC_NOPM; 424362306a36Sopenharmony_ci 424462306a36Sopenharmony_ci if (dai->driver->playback.stream_name) { 424562306a36Sopenharmony_ci template.id = snd_soc_dapm_dai_in; 424662306a36Sopenharmony_ci template.name = dai->driver->playback.stream_name; 424762306a36Sopenharmony_ci template.sname = dai->driver->playback.stream_name; 424862306a36Sopenharmony_ci 424962306a36Sopenharmony_ci dev_dbg(dai->dev, "ASoC: adding %s widget\n", 425062306a36Sopenharmony_ci template.name); 425162306a36Sopenharmony_ci 425262306a36Sopenharmony_ci w = snd_soc_dapm_new_control_unlocked(dapm, &template); 425362306a36Sopenharmony_ci if (IS_ERR(w)) 425462306a36Sopenharmony_ci return PTR_ERR(w); 425562306a36Sopenharmony_ci 425662306a36Sopenharmony_ci w->priv = dai; 425762306a36Sopenharmony_ci snd_soc_dai_set_widget_playback(dai, w); 425862306a36Sopenharmony_ci } 425962306a36Sopenharmony_ci 426062306a36Sopenharmony_ci if (dai->driver->capture.stream_name) { 426162306a36Sopenharmony_ci template.id = snd_soc_dapm_dai_out; 426262306a36Sopenharmony_ci template.name = dai->driver->capture.stream_name; 426362306a36Sopenharmony_ci template.sname = dai->driver->capture.stream_name; 426462306a36Sopenharmony_ci 426562306a36Sopenharmony_ci dev_dbg(dai->dev, "ASoC: adding %s widget\n", 426662306a36Sopenharmony_ci template.name); 426762306a36Sopenharmony_ci 426862306a36Sopenharmony_ci w = snd_soc_dapm_new_control_unlocked(dapm, &template); 426962306a36Sopenharmony_ci if (IS_ERR(w)) 427062306a36Sopenharmony_ci return PTR_ERR(w); 427162306a36Sopenharmony_ci 427262306a36Sopenharmony_ci w->priv = dai; 427362306a36Sopenharmony_ci snd_soc_dai_set_widget_capture(dai, w); 427462306a36Sopenharmony_ci } 427562306a36Sopenharmony_ci 427662306a36Sopenharmony_ci return 0; 427762306a36Sopenharmony_ci} 427862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_new_dai_widgets); 427962306a36Sopenharmony_ci 428062306a36Sopenharmony_ciint snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card) 428162306a36Sopenharmony_ci{ 428262306a36Sopenharmony_ci struct snd_soc_dapm_widget *dai_w, *w; 428362306a36Sopenharmony_ci struct snd_soc_dapm_widget *src, *sink; 428462306a36Sopenharmony_ci struct snd_soc_dai *dai; 428562306a36Sopenharmony_ci 428662306a36Sopenharmony_ci /* For each DAI widget... */ 428762306a36Sopenharmony_ci for_each_card_widgets(card, dai_w) { 428862306a36Sopenharmony_ci switch (dai_w->id) { 428962306a36Sopenharmony_ci case snd_soc_dapm_dai_in: 429062306a36Sopenharmony_ci case snd_soc_dapm_dai_out: 429162306a36Sopenharmony_ci break; 429262306a36Sopenharmony_ci default: 429362306a36Sopenharmony_ci continue; 429462306a36Sopenharmony_ci } 429562306a36Sopenharmony_ci 429662306a36Sopenharmony_ci /* let users know there is no DAI to link */ 429762306a36Sopenharmony_ci if (!dai_w->priv) { 429862306a36Sopenharmony_ci dev_dbg(card->dev, "dai widget %s has no DAI\n", 429962306a36Sopenharmony_ci dai_w->name); 430062306a36Sopenharmony_ci continue; 430162306a36Sopenharmony_ci } 430262306a36Sopenharmony_ci 430362306a36Sopenharmony_ci dai = dai_w->priv; 430462306a36Sopenharmony_ci 430562306a36Sopenharmony_ci /* ...find all widgets with the same stream and link them */ 430662306a36Sopenharmony_ci for_each_card_widgets(card, w) { 430762306a36Sopenharmony_ci if (w->dapm != dai_w->dapm) 430862306a36Sopenharmony_ci continue; 430962306a36Sopenharmony_ci 431062306a36Sopenharmony_ci switch (w->id) { 431162306a36Sopenharmony_ci case snd_soc_dapm_dai_in: 431262306a36Sopenharmony_ci case snd_soc_dapm_dai_out: 431362306a36Sopenharmony_ci continue; 431462306a36Sopenharmony_ci default: 431562306a36Sopenharmony_ci break; 431662306a36Sopenharmony_ci } 431762306a36Sopenharmony_ci 431862306a36Sopenharmony_ci if (!w->sname || !strstr(w->sname, dai_w->sname)) 431962306a36Sopenharmony_ci continue; 432062306a36Sopenharmony_ci 432162306a36Sopenharmony_ci if (dai_w->id == snd_soc_dapm_dai_in) { 432262306a36Sopenharmony_ci src = dai_w; 432362306a36Sopenharmony_ci sink = w; 432462306a36Sopenharmony_ci } else { 432562306a36Sopenharmony_ci src = w; 432662306a36Sopenharmony_ci sink = dai_w; 432762306a36Sopenharmony_ci } 432862306a36Sopenharmony_ci dev_dbg(dai->dev, "%s -> %s\n", src->name, sink->name); 432962306a36Sopenharmony_ci snd_soc_dapm_add_path(w->dapm, src, sink, NULL, NULL); 433062306a36Sopenharmony_ci } 433162306a36Sopenharmony_ci } 433262306a36Sopenharmony_ci 433362306a36Sopenharmony_ci return 0; 433462306a36Sopenharmony_ci} 433562306a36Sopenharmony_ci 433662306a36Sopenharmony_cistatic void dapm_connect_dai_routes(struct snd_soc_dapm_context *dapm, 433762306a36Sopenharmony_ci struct snd_soc_dai *src_dai, 433862306a36Sopenharmony_ci struct snd_soc_dapm_widget *src, 433962306a36Sopenharmony_ci struct snd_soc_dapm_widget *dai, 434062306a36Sopenharmony_ci struct snd_soc_dai *sink_dai, 434162306a36Sopenharmony_ci struct snd_soc_dapm_widget *sink) 434262306a36Sopenharmony_ci{ 434362306a36Sopenharmony_ci dev_dbg(dapm->dev, "connected DAI link %s:%s -> %s:%s\n", 434462306a36Sopenharmony_ci src_dai->component->name, src->name, 434562306a36Sopenharmony_ci sink_dai->component->name, sink->name); 434662306a36Sopenharmony_ci 434762306a36Sopenharmony_ci if (dai) { 434862306a36Sopenharmony_ci snd_soc_dapm_add_path(dapm, src, dai, NULL, NULL); 434962306a36Sopenharmony_ci src = dai; 435062306a36Sopenharmony_ci } 435162306a36Sopenharmony_ci 435262306a36Sopenharmony_ci snd_soc_dapm_add_path(dapm, src, sink, NULL, NULL); 435362306a36Sopenharmony_ci} 435462306a36Sopenharmony_ci 435562306a36Sopenharmony_cistatic void dapm_connect_dai_pair(struct snd_soc_card *card, 435662306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd, 435762306a36Sopenharmony_ci struct snd_soc_dai *codec_dai, 435862306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai) 435962306a36Sopenharmony_ci{ 436062306a36Sopenharmony_ci struct snd_soc_dai_link *dai_link = rtd->dai_link; 436162306a36Sopenharmony_ci struct snd_soc_dapm_widget *codec, *cpu; 436262306a36Sopenharmony_ci struct snd_soc_dai *src_dai[] = { cpu_dai, codec_dai }; 436362306a36Sopenharmony_ci struct snd_soc_dai *sink_dai[] = { codec_dai, cpu_dai }; 436462306a36Sopenharmony_ci struct snd_soc_dapm_widget **src[] = { &cpu, &codec }; 436562306a36Sopenharmony_ci struct snd_soc_dapm_widget **sink[] = { &codec, &cpu }; 436662306a36Sopenharmony_ci char *widget_name[] = { "playback", "capture" }; 436762306a36Sopenharmony_ci int stream; 436862306a36Sopenharmony_ci 436962306a36Sopenharmony_ci for_each_pcm_streams(stream) { 437062306a36Sopenharmony_ci int stream_cpu, stream_codec; 437162306a36Sopenharmony_ci 437262306a36Sopenharmony_ci stream_cpu = snd_soc_get_stream_cpu(dai_link, stream); 437362306a36Sopenharmony_ci stream_codec = stream; 437462306a36Sopenharmony_ci 437562306a36Sopenharmony_ci /* connect BE DAI playback if widgets are valid */ 437662306a36Sopenharmony_ci cpu = snd_soc_dai_get_widget(cpu_dai, stream_cpu); 437762306a36Sopenharmony_ci codec = snd_soc_dai_get_widget(codec_dai, stream_codec); 437862306a36Sopenharmony_ci 437962306a36Sopenharmony_ci if (!cpu || !codec) 438062306a36Sopenharmony_ci continue; 438162306a36Sopenharmony_ci 438262306a36Sopenharmony_ci /* special handling for [Codec2Codec] */ 438362306a36Sopenharmony_ci if (dai_link->c2c_params && !rtd->c2c_widget[stream]) { 438462306a36Sopenharmony_ci struct snd_pcm_substream *substream = rtd->pcm->streams[stream].substream; 438562306a36Sopenharmony_ci struct snd_soc_dapm_widget *dai = snd_soc_dapm_new_dai(card, substream, 438662306a36Sopenharmony_ci widget_name[stream]); 438762306a36Sopenharmony_ci 438862306a36Sopenharmony_ci if (IS_ERR(dai)) 438962306a36Sopenharmony_ci continue; 439062306a36Sopenharmony_ci 439162306a36Sopenharmony_ci rtd->c2c_widget[stream] = dai; 439262306a36Sopenharmony_ci } 439362306a36Sopenharmony_ci 439462306a36Sopenharmony_ci dapm_connect_dai_routes(&card->dapm, src_dai[stream], *src[stream], 439562306a36Sopenharmony_ci rtd->c2c_widget[stream], 439662306a36Sopenharmony_ci sink_dai[stream], *sink[stream]); 439762306a36Sopenharmony_ci } 439862306a36Sopenharmony_ci} 439962306a36Sopenharmony_ci 440062306a36Sopenharmony_cistatic void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream, 440162306a36Sopenharmony_ci int event) 440262306a36Sopenharmony_ci{ 440362306a36Sopenharmony_ci struct snd_soc_dapm_widget *w; 440462306a36Sopenharmony_ci 440562306a36Sopenharmony_ci w = snd_soc_dai_get_widget(dai, stream); 440662306a36Sopenharmony_ci 440762306a36Sopenharmony_ci if (w) { 440862306a36Sopenharmony_ci unsigned int ep; 440962306a36Sopenharmony_ci 441062306a36Sopenharmony_ci dapm_mark_dirty(w, "stream event"); 441162306a36Sopenharmony_ci 441262306a36Sopenharmony_ci if (w->id == snd_soc_dapm_dai_in) { 441362306a36Sopenharmony_ci ep = SND_SOC_DAPM_EP_SOURCE; 441462306a36Sopenharmony_ci dapm_widget_invalidate_input_paths(w); 441562306a36Sopenharmony_ci } else { 441662306a36Sopenharmony_ci ep = SND_SOC_DAPM_EP_SINK; 441762306a36Sopenharmony_ci dapm_widget_invalidate_output_paths(w); 441862306a36Sopenharmony_ci } 441962306a36Sopenharmony_ci 442062306a36Sopenharmony_ci switch (event) { 442162306a36Sopenharmony_ci case SND_SOC_DAPM_STREAM_START: 442262306a36Sopenharmony_ci w->active = 1; 442362306a36Sopenharmony_ci w->is_ep = ep; 442462306a36Sopenharmony_ci break; 442562306a36Sopenharmony_ci case SND_SOC_DAPM_STREAM_STOP: 442662306a36Sopenharmony_ci w->active = 0; 442762306a36Sopenharmony_ci w->is_ep = 0; 442862306a36Sopenharmony_ci break; 442962306a36Sopenharmony_ci case SND_SOC_DAPM_STREAM_SUSPEND: 443062306a36Sopenharmony_ci case SND_SOC_DAPM_STREAM_RESUME: 443162306a36Sopenharmony_ci case SND_SOC_DAPM_STREAM_PAUSE_PUSH: 443262306a36Sopenharmony_ci case SND_SOC_DAPM_STREAM_PAUSE_RELEASE: 443362306a36Sopenharmony_ci break; 443462306a36Sopenharmony_ci } 443562306a36Sopenharmony_ci } 443662306a36Sopenharmony_ci} 443762306a36Sopenharmony_ci 443862306a36Sopenharmony_civoid snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card) 443962306a36Sopenharmony_ci{ 444062306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd; 444162306a36Sopenharmony_ci struct snd_soc_dai *codec_dai; 444262306a36Sopenharmony_ci int i; 444362306a36Sopenharmony_ci 444462306a36Sopenharmony_ci /* for each BE DAI link... */ 444562306a36Sopenharmony_ci for_each_card_rtds(card, rtd) { 444662306a36Sopenharmony_ci /* 444762306a36Sopenharmony_ci * dynamic FE links have no fixed DAI mapping. 444862306a36Sopenharmony_ci * CODEC<->CODEC links have no direct connection. 444962306a36Sopenharmony_ci */ 445062306a36Sopenharmony_ci if (rtd->dai_link->dynamic) 445162306a36Sopenharmony_ci continue; 445262306a36Sopenharmony_ci 445362306a36Sopenharmony_ci if (rtd->dai_link->num_cpus == 1) { 445462306a36Sopenharmony_ci for_each_rtd_codec_dais(rtd, i, codec_dai) 445562306a36Sopenharmony_ci dapm_connect_dai_pair(card, rtd, codec_dai, 445662306a36Sopenharmony_ci asoc_rtd_to_cpu(rtd, 0)); 445762306a36Sopenharmony_ci } else if (rtd->dai_link->num_codecs == rtd->dai_link->num_cpus) { 445862306a36Sopenharmony_ci for_each_rtd_codec_dais(rtd, i, codec_dai) 445962306a36Sopenharmony_ci dapm_connect_dai_pair(card, rtd, codec_dai, 446062306a36Sopenharmony_ci asoc_rtd_to_cpu(rtd, i)); 446162306a36Sopenharmony_ci } else if (rtd->dai_link->num_codecs > rtd->dai_link->num_cpus) { 446262306a36Sopenharmony_ci int cpu_id; 446362306a36Sopenharmony_ci 446462306a36Sopenharmony_ci if (!rtd->dai_link->codec_ch_maps) { 446562306a36Sopenharmony_ci dev_err(card->dev, "%s: no codec channel mapping table provided\n", 446662306a36Sopenharmony_ci __func__); 446762306a36Sopenharmony_ci continue; 446862306a36Sopenharmony_ci } 446962306a36Sopenharmony_ci 447062306a36Sopenharmony_ci for_each_rtd_codec_dais(rtd, i, codec_dai) { 447162306a36Sopenharmony_ci cpu_id = rtd->dai_link->codec_ch_maps[i].connected_cpu_id; 447262306a36Sopenharmony_ci if (cpu_id >= rtd->dai_link->num_cpus) { 447362306a36Sopenharmony_ci dev_err(card->dev, 447462306a36Sopenharmony_ci "%s: dai_link %s cpu_id %d too large, num_cpus is %d\n", 447562306a36Sopenharmony_ci __func__, rtd->dai_link->name, cpu_id, 447662306a36Sopenharmony_ci rtd->dai_link->num_cpus); 447762306a36Sopenharmony_ci continue; 447862306a36Sopenharmony_ci } 447962306a36Sopenharmony_ci dapm_connect_dai_pair(card, rtd, codec_dai, 448062306a36Sopenharmony_ci asoc_rtd_to_cpu(rtd, cpu_id)); 448162306a36Sopenharmony_ci } 448262306a36Sopenharmony_ci } else { 448362306a36Sopenharmony_ci dev_err(card->dev, 448462306a36Sopenharmony_ci "%s: codec number %d < cpu number %d is not supported\n", 448562306a36Sopenharmony_ci __func__, rtd->dai_link->num_codecs, rtd->dai_link->num_cpus); 448662306a36Sopenharmony_ci } 448762306a36Sopenharmony_ci } 448862306a36Sopenharmony_ci} 448962306a36Sopenharmony_ci 449062306a36Sopenharmony_cistatic void soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream, 449162306a36Sopenharmony_ci int event) 449262306a36Sopenharmony_ci{ 449362306a36Sopenharmony_ci struct snd_soc_dai *dai; 449462306a36Sopenharmony_ci int i; 449562306a36Sopenharmony_ci 449662306a36Sopenharmony_ci for_each_rtd_dais(rtd, i, dai) 449762306a36Sopenharmony_ci soc_dapm_dai_stream_event(dai, stream, event); 449862306a36Sopenharmony_ci 449962306a36Sopenharmony_ci dapm_power_widgets(rtd->card, event); 450062306a36Sopenharmony_ci} 450162306a36Sopenharmony_ci 450262306a36Sopenharmony_ci/** 450362306a36Sopenharmony_ci * snd_soc_dapm_stream_event - send a stream event to the dapm core 450462306a36Sopenharmony_ci * @rtd: PCM runtime data 450562306a36Sopenharmony_ci * @stream: stream name 450662306a36Sopenharmony_ci * @event: stream event 450762306a36Sopenharmony_ci * 450862306a36Sopenharmony_ci * Sends a stream event to the dapm core. The core then makes any 450962306a36Sopenharmony_ci * necessary widget power changes. 451062306a36Sopenharmony_ci * 451162306a36Sopenharmony_ci * Returns 0 for success else error. 451262306a36Sopenharmony_ci */ 451362306a36Sopenharmony_civoid snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream, 451462306a36Sopenharmony_ci int event) 451562306a36Sopenharmony_ci{ 451662306a36Sopenharmony_ci struct snd_soc_card *card = rtd->card; 451762306a36Sopenharmony_ci 451862306a36Sopenharmony_ci snd_soc_dapm_mutex_lock(card); 451962306a36Sopenharmony_ci soc_dapm_stream_event(rtd, stream, event); 452062306a36Sopenharmony_ci snd_soc_dapm_mutex_unlock(card); 452162306a36Sopenharmony_ci} 452262306a36Sopenharmony_ci 452362306a36Sopenharmony_civoid snd_soc_dapm_stream_stop(struct snd_soc_pcm_runtime *rtd, int stream) 452462306a36Sopenharmony_ci{ 452562306a36Sopenharmony_ci if (stream == SNDRV_PCM_STREAM_PLAYBACK) { 452662306a36Sopenharmony_ci if (snd_soc_runtime_ignore_pmdown_time(rtd)) { 452762306a36Sopenharmony_ci /* powered down playback stream now */ 452862306a36Sopenharmony_ci snd_soc_dapm_stream_event(rtd, 452962306a36Sopenharmony_ci SNDRV_PCM_STREAM_PLAYBACK, 453062306a36Sopenharmony_ci SND_SOC_DAPM_STREAM_STOP); 453162306a36Sopenharmony_ci } else { 453262306a36Sopenharmony_ci /* start delayed pop wq here for playback streams */ 453362306a36Sopenharmony_ci rtd->pop_wait = 1; 453462306a36Sopenharmony_ci queue_delayed_work(system_power_efficient_wq, 453562306a36Sopenharmony_ci &rtd->delayed_work, 453662306a36Sopenharmony_ci msecs_to_jiffies(rtd->pmdown_time)); 453762306a36Sopenharmony_ci } 453862306a36Sopenharmony_ci } else { 453962306a36Sopenharmony_ci /* capture streams can be powered down now */ 454062306a36Sopenharmony_ci snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_CAPTURE, 454162306a36Sopenharmony_ci SND_SOC_DAPM_STREAM_STOP); 454262306a36Sopenharmony_ci } 454362306a36Sopenharmony_ci} 454462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_stream_stop); 454562306a36Sopenharmony_ci 454662306a36Sopenharmony_ci/** 454762306a36Sopenharmony_ci * snd_soc_dapm_enable_pin_unlocked - enable pin. 454862306a36Sopenharmony_ci * @dapm: DAPM context 454962306a36Sopenharmony_ci * @pin: pin name 455062306a36Sopenharmony_ci * 455162306a36Sopenharmony_ci * Enables input/output pin and its parents or children widgets iff there is 455262306a36Sopenharmony_ci * a valid audio route and active audio stream. 455362306a36Sopenharmony_ci * 455462306a36Sopenharmony_ci * Requires external locking. 455562306a36Sopenharmony_ci * 455662306a36Sopenharmony_ci * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to 455762306a36Sopenharmony_ci * do any widget power switching. 455862306a36Sopenharmony_ci */ 455962306a36Sopenharmony_ciint snd_soc_dapm_enable_pin_unlocked(struct snd_soc_dapm_context *dapm, 456062306a36Sopenharmony_ci const char *pin) 456162306a36Sopenharmony_ci{ 456262306a36Sopenharmony_ci return snd_soc_dapm_set_pin(dapm, pin, 1); 456362306a36Sopenharmony_ci} 456462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_enable_pin_unlocked); 456562306a36Sopenharmony_ci 456662306a36Sopenharmony_ci/** 456762306a36Sopenharmony_ci * snd_soc_dapm_enable_pin - enable pin. 456862306a36Sopenharmony_ci * @dapm: DAPM context 456962306a36Sopenharmony_ci * @pin: pin name 457062306a36Sopenharmony_ci * 457162306a36Sopenharmony_ci * Enables input/output pin and its parents or children widgets iff there is 457262306a36Sopenharmony_ci * a valid audio route and active audio stream. 457362306a36Sopenharmony_ci * 457462306a36Sopenharmony_ci * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to 457562306a36Sopenharmony_ci * do any widget power switching. 457662306a36Sopenharmony_ci */ 457762306a36Sopenharmony_ciint snd_soc_dapm_enable_pin(struct snd_soc_dapm_context *dapm, const char *pin) 457862306a36Sopenharmony_ci{ 457962306a36Sopenharmony_ci int ret; 458062306a36Sopenharmony_ci 458162306a36Sopenharmony_ci snd_soc_dapm_mutex_lock(dapm); 458262306a36Sopenharmony_ci 458362306a36Sopenharmony_ci ret = snd_soc_dapm_set_pin(dapm, pin, 1); 458462306a36Sopenharmony_ci 458562306a36Sopenharmony_ci snd_soc_dapm_mutex_unlock(dapm); 458662306a36Sopenharmony_ci 458762306a36Sopenharmony_ci return ret; 458862306a36Sopenharmony_ci} 458962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_enable_pin); 459062306a36Sopenharmony_ci 459162306a36Sopenharmony_ci/** 459262306a36Sopenharmony_ci * snd_soc_dapm_force_enable_pin_unlocked - force a pin to be enabled 459362306a36Sopenharmony_ci * @dapm: DAPM context 459462306a36Sopenharmony_ci * @pin: pin name 459562306a36Sopenharmony_ci * 459662306a36Sopenharmony_ci * Enables input/output pin regardless of any other state. This is 459762306a36Sopenharmony_ci * intended for use with microphone bias supplies used in microphone 459862306a36Sopenharmony_ci * jack detection. 459962306a36Sopenharmony_ci * 460062306a36Sopenharmony_ci * Requires external locking. 460162306a36Sopenharmony_ci * 460262306a36Sopenharmony_ci * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to 460362306a36Sopenharmony_ci * do any widget power switching. 460462306a36Sopenharmony_ci */ 460562306a36Sopenharmony_ciint snd_soc_dapm_force_enable_pin_unlocked(struct snd_soc_dapm_context *dapm, 460662306a36Sopenharmony_ci const char *pin) 460762306a36Sopenharmony_ci{ 460862306a36Sopenharmony_ci struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, true); 460962306a36Sopenharmony_ci 461062306a36Sopenharmony_ci if (!w) { 461162306a36Sopenharmony_ci dev_err(dapm->dev, "ASoC: unknown pin %s\n", pin); 461262306a36Sopenharmony_ci return -EINVAL; 461362306a36Sopenharmony_ci } 461462306a36Sopenharmony_ci 461562306a36Sopenharmony_ci dev_dbg(w->dapm->dev, "ASoC: force enable pin %s\n", pin); 461662306a36Sopenharmony_ci if (!w->connected) { 461762306a36Sopenharmony_ci /* 461862306a36Sopenharmony_ci * w->force does not affect the number of input or output paths, 461962306a36Sopenharmony_ci * so we only have to recheck if w->connected is changed 462062306a36Sopenharmony_ci */ 462162306a36Sopenharmony_ci dapm_widget_invalidate_input_paths(w); 462262306a36Sopenharmony_ci dapm_widget_invalidate_output_paths(w); 462362306a36Sopenharmony_ci w->connected = 1; 462462306a36Sopenharmony_ci } 462562306a36Sopenharmony_ci w->force = 1; 462662306a36Sopenharmony_ci dapm_mark_dirty(w, "force enable"); 462762306a36Sopenharmony_ci 462862306a36Sopenharmony_ci return 0; 462962306a36Sopenharmony_ci} 463062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_force_enable_pin_unlocked); 463162306a36Sopenharmony_ci 463262306a36Sopenharmony_ci/** 463362306a36Sopenharmony_ci * snd_soc_dapm_force_enable_pin - force a pin to be enabled 463462306a36Sopenharmony_ci * @dapm: DAPM context 463562306a36Sopenharmony_ci * @pin: pin name 463662306a36Sopenharmony_ci * 463762306a36Sopenharmony_ci * Enables input/output pin regardless of any other state. This is 463862306a36Sopenharmony_ci * intended for use with microphone bias supplies used in microphone 463962306a36Sopenharmony_ci * jack detection. 464062306a36Sopenharmony_ci * 464162306a36Sopenharmony_ci * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to 464262306a36Sopenharmony_ci * do any widget power switching. 464362306a36Sopenharmony_ci */ 464462306a36Sopenharmony_ciint snd_soc_dapm_force_enable_pin(struct snd_soc_dapm_context *dapm, 464562306a36Sopenharmony_ci const char *pin) 464662306a36Sopenharmony_ci{ 464762306a36Sopenharmony_ci int ret; 464862306a36Sopenharmony_ci 464962306a36Sopenharmony_ci snd_soc_dapm_mutex_lock(dapm); 465062306a36Sopenharmony_ci 465162306a36Sopenharmony_ci ret = snd_soc_dapm_force_enable_pin_unlocked(dapm, pin); 465262306a36Sopenharmony_ci 465362306a36Sopenharmony_ci snd_soc_dapm_mutex_unlock(dapm); 465462306a36Sopenharmony_ci 465562306a36Sopenharmony_ci return ret; 465662306a36Sopenharmony_ci} 465762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_force_enable_pin); 465862306a36Sopenharmony_ci 465962306a36Sopenharmony_ci/** 466062306a36Sopenharmony_ci * snd_soc_dapm_disable_pin_unlocked - disable pin. 466162306a36Sopenharmony_ci * @dapm: DAPM context 466262306a36Sopenharmony_ci * @pin: pin name 466362306a36Sopenharmony_ci * 466462306a36Sopenharmony_ci * Disables input/output pin and its parents or children widgets. 466562306a36Sopenharmony_ci * 466662306a36Sopenharmony_ci * Requires external locking. 466762306a36Sopenharmony_ci * 466862306a36Sopenharmony_ci * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to 466962306a36Sopenharmony_ci * do any widget power switching. 467062306a36Sopenharmony_ci */ 467162306a36Sopenharmony_ciint snd_soc_dapm_disable_pin_unlocked(struct snd_soc_dapm_context *dapm, 467262306a36Sopenharmony_ci const char *pin) 467362306a36Sopenharmony_ci{ 467462306a36Sopenharmony_ci return snd_soc_dapm_set_pin(dapm, pin, 0); 467562306a36Sopenharmony_ci} 467662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_disable_pin_unlocked); 467762306a36Sopenharmony_ci 467862306a36Sopenharmony_ci/** 467962306a36Sopenharmony_ci * snd_soc_dapm_disable_pin - disable pin. 468062306a36Sopenharmony_ci * @dapm: DAPM context 468162306a36Sopenharmony_ci * @pin: pin name 468262306a36Sopenharmony_ci * 468362306a36Sopenharmony_ci * Disables input/output pin and its parents or children widgets. 468462306a36Sopenharmony_ci * 468562306a36Sopenharmony_ci * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to 468662306a36Sopenharmony_ci * do any widget power switching. 468762306a36Sopenharmony_ci */ 468862306a36Sopenharmony_ciint snd_soc_dapm_disable_pin(struct snd_soc_dapm_context *dapm, 468962306a36Sopenharmony_ci const char *pin) 469062306a36Sopenharmony_ci{ 469162306a36Sopenharmony_ci int ret; 469262306a36Sopenharmony_ci 469362306a36Sopenharmony_ci snd_soc_dapm_mutex_lock(dapm); 469462306a36Sopenharmony_ci 469562306a36Sopenharmony_ci ret = snd_soc_dapm_set_pin(dapm, pin, 0); 469662306a36Sopenharmony_ci 469762306a36Sopenharmony_ci snd_soc_dapm_mutex_unlock(dapm); 469862306a36Sopenharmony_ci 469962306a36Sopenharmony_ci return ret; 470062306a36Sopenharmony_ci} 470162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_disable_pin); 470262306a36Sopenharmony_ci 470362306a36Sopenharmony_ci/** 470462306a36Sopenharmony_ci * snd_soc_dapm_nc_pin_unlocked - permanently disable pin. 470562306a36Sopenharmony_ci * @dapm: DAPM context 470662306a36Sopenharmony_ci * @pin: pin name 470762306a36Sopenharmony_ci * 470862306a36Sopenharmony_ci * Marks the specified pin as being not connected, disabling it along 470962306a36Sopenharmony_ci * any parent or child widgets. At present this is identical to 471062306a36Sopenharmony_ci * snd_soc_dapm_disable_pin() but in future it will be extended to do 471162306a36Sopenharmony_ci * additional things such as disabling controls which only affect 471262306a36Sopenharmony_ci * paths through the pin. 471362306a36Sopenharmony_ci * 471462306a36Sopenharmony_ci * Requires external locking. 471562306a36Sopenharmony_ci * 471662306a36Sopenharmony_ci * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to 471762306a36Sopenharmony_ci * do any widget power switching. 471862306a36Sopenharmony_ci */ 471962306a36Sopenharmony_ciint snd_soc_dapm_nc_pin_unlocked(struct snd_soc_dapm_context *dapm, 472062306a36Sopenharmony_ci const char *pin) 472162306a36Sopenharmony_ci{ 472262306a36Sopenharmony_ci return snd_soc_dapm_set_pin(dapm, pin, 0); 472362306a36Sopenharmony_ci} 472462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_nc_pin_unlocked); 472562306a36Sopenharmony_ci 472662306a36Sopenharmony_ci/** 472762306a36Sopenharmony_ci * snd_soc_dapm_nc_pin - permanently disable pin. 472862306a36Sopenharmony_ci * @dapm: DAPM context 472962306a36Sopenharmony_ci * @pin: pin name 473062306a36Sopenharmony_ci * 473162306a36Sopenharmony_ci * Marks the specified pin as being not connected, disabling it along 473262306a36Sopenharmony_ci * any parent or child widgets. At present this is identical to 473362306a36Sopenharmony_ci * snd_soc_dapm_disable_pin() but in future it will be extended to do 473462306a36Sopenharmony_ci * additional things such as disabling controls which only affect 473562306a36Sopenharmony_ci * paths through the pin. 473662306a36Sopenharmony_ci * 473762306a36Sopenharmony_ci * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to 473862306a36Sopenharmony_ci * do any widget power switching. 473962306a36Sopenharmony_ci */ 474062306a36Sopenharmony_ciint snd_soc_dapm_nc_pin(struct snd_soc_dapm_context *dapm, const char *pin) 474162306a36Sopenharmony_ci{ 474262306a36Sopenharmony_ci int ret; 474362306a36Sopenharmony_ci 474462306a36Sopenharmony_ci snd_soc_dapm_mutex_lock(dapm); 474562306a36Sopenharmony_ci 474662306a36Sopenharmony_ci ret = snd_soc_dapm_set_pin(dapm, pin, 0); 474762306a36Sopenharmony_ci 474862306a36Sopenharmony_ci snd_soc_dapm_mutex_unlock(dapm); 474962306a36Sopenharmony_ci 475062306a36Sopenharmony_ci return ret; 475162306a36Sopenharmony_ci} 475262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_nc_pin); 475362306a36Sopenharmony_ci 475462306a36Sopenharmony_ci/** 475562306a36Sopenharmony_ci * snd_soc_dapm_get_pin_status - get audio pin status 475662306a36Sopenharmony_ci * @dapm: DAPM context 475762306a36Sopenharmony_ci * @pin: audio signal pin endpoint (or start point) 475862306a36Sopenharmony_ci * 475962306a36Sopenharmony_ci * Get audio pin status - connected or disconnected. 476062306a36Sopenharmony_ci * 476162306a36Sopenharmony_ci * Returns 1 for connected otherwise 0. 476262306a36Sopenharmony_ci */ 476362306a36Sopenharmony_ciint snd_soc_dapm_get_pin_status(struct snd_soc_dapm_context *dapm, 476462306a36Sopenharmony_ci const char *pin) 476562306a36Sopenharmony_ci{ 476662306a36Sopenharmony_ci struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, true); 476762306a36Sopenharmony_ci 476862306a36Sopenharmony_ci if (w) 476962306a36Sopenharmony_ci return w->connected; 477062306a36Sopenharmony_ci 477162306a36Sopenharmony_ci return 0; 477262306a36Sopenharmony_ci} 477362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_get_pin_status); 477462306a36Sopenharmony_ci 477562306a36Sopenharmony_ci/** 477662306a36Sopenharmony_ci * snd_soc_dapm_ignore_suspend - ignore suspend status for DAPM endpoint 477762306a36Sopenharmony_ci * @dapm: DAPM context 477862306a36Sopenharmony_ci * @pin: audio signal pin endpoint (or start point) 477962306a36Sopenharmony_ci * 478062306a36Sopenharmony_ci * Mark the given endpoint or pin as ignoring suspend. When the 478162306a36Sopenharmony_ci * system is disabled a path between two endpoints flagged as ignoring 478262306a36Sopenharmony_ci * suspend will not be disabled. The path must already be enabled via 478362306a36Sopenharmony_ci * normal means at suspend time, it will not be turned on if it was not 478462306a36Sopenharmony_ci * already enabled. 478562306a36Sopenharmony_ci */ 478662306a36Sopenharmony_ciint snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm, 478762306a36Sopenharmony_ci const char *pin) 478862306a36Sopenharmony_ci{ 478962306a36Sopenharmony_ci struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, false); 479062306a36Sopenharmony_ci 479162306a36Sopenharmony_ci if (!w) { 479262306a36Sopenharmony_ci dev_err(dapm->dev, "ASoC: unknown pin %s\n", pin); 479362306a36Sopenharmony_ci return -EINVAL; 479462306a36Sopenharmony_ci } 479562306a36Sopenharmony_ci 479662306a36Sopenharmony_ci w->ignore_suspend = 1; 479762306a36Sopenharmony_ci 479862306a36Sopenharmony_ci return 0; 479962306a36Sopenharmony_ci} 480062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_ignore_suspend); 480162306a36Sopenharmony_ci 480262306a36Sopenharmony_ci/** 480362306a36Sopenharmony_ci * snd_soc_dapm_free - free dapm resources 480462306a36Sopenharmony_ci * @dapm: DAPM context 480562306a36Sopenharmony_ci * 480662306a36Sopenharmony_ci * Free all dapm widgets and resources. 480762306a36Sopenharmony_ci */ 480862306a36Sopenharmony_civoid snd_soc_dapm_free(struct snd_soc_dapm_context *dapm) 480962306a36Sopenharmony_ci{ 481062306a36Sopenharmony_ci dapm_debugfs_cleanup(dapm); 481162306a36Sopenharmony_ci dapm_free_widgets(dapm); 481262306a36Sopenharmony_ci list_del(&dapm->list); 481362306a36Sopenharmony_ci} 481462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_free); 481562306a36Sopenharmony_ci 481662306a36Sopenharmony_civoid snd_soc_dapm_init(struct snd_soc_dapm_context *dapm, 481762306a36Sopenharmony_ci struct snd_soc_card *card, 481862306a36Sopenharmony_ci struct snd_soc_component *component) 481962306a36Sopenharmony_ci{ 482062306a36Sopenharmony_ci dapm->card = card; 482162306a36Sopenharmony_ci dapm->component = component; 482262306a36Sopenharmony_ci dapm->bias_level = SND_SOC_BIAS_OFF; 482362306a36Sopenharmony_ci 482462306a36Sopenharmony_ci if (component) { 482562306a36Sopenharmony_ci dapm->dev = component->dev; 482662306a36Sopenharmony_ci dapm->idle_bias_off = !component->driver->idle_bias_on; 482762306a36Sopenharmony_ci dapm->suspend_bias_off = component->driver->suspend_bias_off; 482862306a36Sopenharmony_ci } else { 482962306a36Sopenharmony_ci dapm->dev = card->dev; 483062306a36Sopenharmony_ci } 483162306a36Sopenharmony_ci 483262306a36Sopenharmony_ci INIT_LIST_HEAD(&dapm->list); 483362306a36Sopenharmony_ci /* see for_each_card_dapms */ 483462306a36Sopenharmony_ci list_add(&dapm->list, &card->dapm_list); 483562306a36Sopenharmony_ci} 483662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_init); 483762306a36Sopenharmony_ci 483862306a36Sopenharmony_cistatic void soc_dapm_shutdown_dapm(struct snd_soc_dapm_context *dapm) 483962306a36Sopenharmony_ci{ 484062306a36Sopenharmony_ci struct snd_soc_card *card = dapm->card; 484162306a36Sopenharmony_ci struct snd_soc_dapm_widget *w; 484262306a36Sopenharmony_ci LIST_HEAD(down_list); 484362306a36Sopenharmony_ci int powerdown = 0; 484462306a36Sopenharmony_ci 484562306a36Sopenharmony_ci snd_soc_dapm_mutex_lock_root(card); 484662306a36Sopenharmony_ci 484762306a36Sopenharmony_ci for_each_card_widgets(dapm->card, w) { 484862306a36Sopenharmony_ci if (w->dapm != dapm) 484962306a36Sopenharmony_ci continue; 485062306a36Sopenharmony_ci if (w->power) { 485162306a36Sopenharmony_ci dapm_seq_insert(w, &down_list, false); 485262306a36Sopenharmony_ci w->new_power = 0; 485362306a36Sopenharmony_ci powerdown = 1; 485462306a36Sopenharmony_ci } 485562306a36Sopenharmony_ci } 485662306a36Sopenharmony_ci 485762306a36Sopenharmony_ci /* If there were no widgets to power down we're already in 485862306a36Sopenharmony_ci * standby. 485962306a36Sopenharmony_ci */ 486062306a36Sopenharmony_ci if (powerdown) { 486162306a36Sopenharmony_ci if (dapm->bias_level == SND_SOC_BIAS_ON) 486262306a36Sopenharmony_ci snd_soc_dapm_set_bias_level(dapm, 486362306a36Sopenharmony_ci SND_SOC_BIAS_PREPARE); 486462306a36Sopenharmony_ci dapm_seq_run(card, &down_list, 0, false); 486562306a36Sopenharmony_ci if (dapm->bias_level == SND_SOC_BIAS_PREPARE) 486662306a36Sopenharmony_ci snd_soc_dapm_set_bias_level(dapm, 486762306a36Sopenharmony_ci SND_SOC_BIAS_STANDBY); 486862306a36Sopenharmony_ci } 486962306a36Sopenharmony_ci 487062306a36Sopenharmony_ci snd_soc_dapm_mutex_unlock(card); 487162306a36Sopenharmony_ci} 487262306a36Sopenharmony_ci 487362306a36Sopenharmony_ci/* 487462306a36Sopenharmony_ci * snd_soc_dapm_shutdown - callback for system shutdown 487562306a36Sopenharmony_ci */ 487662306a36Sopenharmony_civoid snd_soc_dapm_shutdown(struct snd_soc_card *card) 487762306a36Sopenharmony_ci{ 487862306a36Sopenharmony_ci struct snd_soc_dapm_context *dapm; 487962306a36Sopenharmony_ci 488062306a36Sopenharmony_ci for_each_card_dapms(card, dapm) { 488162306a36Sopenharmony_ci if (dapm != &card->dapm) { 488262306a36Sopenharmony_ci soc_dapm_shutdown_dapm(dapm); 488362306a36Sopenharmony_ci if (dapm->bias_level == SND_SOC_BIAS_STANDBY) 488462306a36Sopenharmony_ci snd_soc_dapm_set_bias_level(dapm, 488562306a36Sopenharmony_ci SND_SOC_BIAS_OFF); 488662306a36Sopenharmony_ci } 488762306a36Sopenharmony_ci } 488862306a36Sopenharmony_ci 488962306a36Sopenharmony_ci soc_dapm_shutdown_dapm(&card->dapm); 489062306a36Sopenharmony_ci if (card->dapm.bias_level == SND_SOC_BIAS_STANDBY) 489162306a36Sopenharmony_ci snd_soc_dapm_set_bias_level(&card->dapm, 489262306a36Sopenharmony_ci SND_SOC_BIAS_OFF); 489362306a36Sopenharmony_ci} 489462306a36Sopenharmony_ci 489562306a36Sopenharmony_ci/* Module information */ 489662306a36Sopenharmony_ciMODULE_AUTHOR("Liam Girdwood, lrg@slimlogic.co.uk"); 489762306a36Sopenharmony_ciMODULE_DESCRIPTION("Dynamic Audio Power Management core for ALSA SoC"); 489862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 4899