18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// soc-dapm.c -- ALSA SoC Dynamic Audio Power Management 48c2ecf20Sopenharmony_ci// 58c2ecf20Sopenharmony_ci// Copyright 2005 Wolfson Microelectronics PLC. 68c2ecf20Sopenharmony_ci// Author: Liam Girdwood <lrg@slimlogic.co.uk> 78c2ecf20Sopenharmony_ci// 88c2ecf20Sopenharmony_ci// Features: 98c2ecf20Sopenharmony_ci// o Changes power status of internal codec blocks depending on the 108c2ecf20Sopenharmony_ci// dynamic configuration of codec internal audio paths and active 118c2ecf20Sopenharmony_ci// DACs/ADCs. 128c2ecf20Sopenharmony_ci// o Platform power domain - can support external components i.e. amps and 138c2ecf20Sopenharmony_ci// mic/headphone insertion events. 148c2ecf20Sopenharmony_ci// o Automatic Mic Bias support 158c2ecf20Sopenharmony_ci// o Jack insertion power event initiation - e.g. hp insertion will enable 168c2ecf20Sopenharmony_ci// sinks, dacs, etc 178c2ecf20Sopenharmony_ci// o Delayed power down of audio subsystem to reduce pops between a quick 188c2ecf20Sopenharmony_ci// device reopen. 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <linux/module.h> 218c2ecf20Sopenharmony_ci#include <linux/init.h> 228c2ecf20Sopenharmony_ci#include <linux/async.h> 238c2ecf20Sopenharmony_ci#include <linux/delay.h> 248c2ecf20Sopenharmony_ci#include <linux/pm.h> 258c2ecf20Sopenharmony_ci#include <linux/bitops.h> 268c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 278c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 288c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 298c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 308c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 318c2ecf20Sopenharmony_ci#include <linux/pinctrl/consumer.h> 328c2ecf20Sopenharmony_ci#include <linux/clk.h> 338c2ecf20Sopenharmony_ci#include <linux/slab.h> 348c2ecf20Sopenharmony_ci#include <sound/core.h> 358c2ecf20Sopenharmony_ci#include <sound/pcm.h> 368c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 378c2ecf20Sopenharmony_ci#include <sound/soc.h> 388c2ecf20Sopenharmony_ci#include <sound/initval.h> 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#include <trace/events/asoc.h> 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define DAPM_UPDATE_STAT(widget, val) widget->dapm->card->dapm_stats.val++; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define SND_SOC_DAPM_DIR_REVERSE(x) ((x == SND_SOC_DAPM_DIR_IN) ? \ 458c2ecf20Sopenharmony_ci SND_SOC_DAPM_DIR_OUT : SND_SOC_DAPM_DIR_IN) 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#define snd_soc_dapm_for_each_direction(dir) \ 488c2ecf20Sopenharmony_ci for ((dir) = SND_SOC_DAPM_DIR_IN; (dir) <= SND_SOC_DAPM_DIR_OUT; \ 498c2ecf20Sopenharmony_ci (dir)++) 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, 528c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink, 538c2ecf20Sopenharmony_ci const char *control, 548c2ecf20Sopenharmony_ci int (*connected)(struct snd_soc_dapm_widget *source, 558c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *sink)); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistruct snd_soc_dapm_widget * 588c2ecf20Sopenharmony_cisnd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, 598c2ecf20Sopenharmony_ci const struct snd_soc_dapm_widget *widget); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistruct snd_soc_dapm_widget * 628c2ecf20Sopenharmony_cisnd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm, 638c2ecf20Sopenharmony_ci const struct snd_soc_dapm_widget *widget); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic unsigned int soc_dapm_read(struct snd_soc_dapm_context *dapm, int reg); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/* dapm power sequences - make this per codec in the future */ 688c2ecf20Sopenharmony_cistatic int dapm_up_seq[] = { 698c2ecf20Sopenharmony_ci [snd_soc_dapm_pre] = 1, 708c2ecf20Sopenharmony_ci [snd_soc_dapm_regulator_supply] = 2, 718c2ecf20Sopenharmony_ci [snd_soc_dapm_pinctrl] = 2, 728c2ecf20Sopenharmony_ci [snd_soc_dapm_clock_supply] = 2, 738c2ecf20Sopenharmony_ci [snd_soc_dapm_supply] = 3, 748c2ecf20Sopenharmony_ci [snd_soc_dapm_micbias] = 4, 758c2ecf20Sopenharmony_ci [snd_soc_dapm_vmid] = 4, 768c2ecf20Sopenharmony_ci [snd_soc_dapm_dai_link] = 3, 778c2ecf20Sopenharmony_ci [snd_soc_dapm_dai_in] = 5, 788c2ecf20Sopenharmony_ci [snd_soc_dapm_dai_out] = 5, 798c2ecf20Sopenharmony_ci [snd_soc_dapm_aif_in] = 5, 808c2ecf20Sopenharmony_ci [snd_soc_dapm_aif_out] = 5, 818c2ecf20Sopenharmony_ci [snd_soc_dapm_mic] = 6, 828c2ecf20Sopenharmony_ci [snd_soc_dapm_siggen] = 6, 838c2ecf20Sopenharmony_ci [snd_soc_dapm_input] = 6, 848c2ecf20Sopenharmony_ci [snd_soc_dapm_output] = 6, 858c2ecf20Sopenharmony_ci [snd_soc_dapm_mux] = 7, 868c2ecf20Sopenharmony_ci [snd_soc_dapm_demux] = 7, 878c2ecf20Sopenharmony_ci [snd_soc_dapm_dac] = 8, 888c2ecf20Sopenharmony_ci [snd_soc_dapm_switch] = 9, 898c2ecf20Sopenharmony_ci [snd_soc_dapm_mixer] = 9, 908c2ecf20Sopenharmony_ci [snd_soc_dapm_mixer_named_ctl] = 9, 918c2ecf20Sopenharmony_ci [snd_soc_dapm_pga] = 10, 928c2ecf20Sopenharmony_ci [snd_soc_dapm_buffer] = 10, 938c2ecf20Sopenharmony_ci [snd_soc_dapm_scheduler] = 10, 948c2ecf20Sopenharmony_ci [snd_soc_dapm_effect] = 10, 958c2ecf20Sopenharmony_ci [snd_soc_dapm_src] = 10, 968c2ecf20Sopenharmony_ci [snd_soc_dapm_asrc] = 10, 978c2ecf20Sopenharmony_ci [snd_soc_dapm_encoder] = 10, 988c2ecf20Sopenharmony_ci [snd_soc_dapm_decoder] = 10, 998c2ecf20Sopenharmony_ci [snd_soc_dapm_adc] = 11, 1008c2ecf20Sopenharmony_ci [snd_soc_dapm_out_drv] = 12, 1018c2ecf20Sopenharmony_ci [snd_soc_dapm_hp] = 12, 1028c2ecf20Sopenharmony_ci [snd_soc_dapm_spk] = 12, 1038c2ecf20Sopenharmony_ci [snd_soc_dapm_line] = 12, 1048c2ecf20Sopenharmony_ci [snd_soc_dapm_sink] = 12, 1058c2ecf20Sopenharmony_ci [snd_soc_dapm_kcontrol] = 13, 1068c2ecf20Sopenharmony_ci [snd_soc_dapm_post] = 14, 1078c2ecf20Sopenharmony_ci}; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic int dapm_down_seq[] = { 1108c2ecf20Sopenharmony_ci [snd_soc_dapm_pre] = 1, 1118c2ecf20Sopenharmony_ci [snd_soc_dapm_kcontrol] = 2, 1128c2ecf20Sopenharmony_ci [snd_soc_dapm_adc] = 3, 1138c2ecf20Sopenharmony_ci [snd_soc_dapm_hp] = 4, 1148c2ecf20Sopenharmony_ci [snd_soc_dapm_spk] = 4, 1158c2ecf20Sopenharmony_ci [snd_soc_dapm_line] = 4, 1168c2ecf20Sopenharmony_ci [snd_soc_dapm_out_drv] = 4, 1178c2ecf20Sopenharmony_ci [snd_soc_dapm_sink] = 4, 1188c2ecf20Sopenharmony_ci [snd_soc_dapm_pga] = 5, 1198c2ecf20Sopenharmony_ci [snd_soc_dapm_buffer] = 5, 1208c2ecf20Sopenharmony_ci [snd_soc_dapm_scheduler] = 5, 1218c2ecf20Sopenharmony_ci [snd_soc_dapm_effect] = 5, 1228c2ecf20Sopenharmony_ci [snd_soc_dapm_src] = 5, 1238c2ecf20Sopenharmony_ci [snd_soc_dapm_asrc] = 5, 1248c2ecf20Sopenharmony_ci [snd_soc_dapm_encoder] = 5, 1258c2ecf20Sopenharmony_ci [snd_soc_dapm_decoder] = 5, 1268c2ecf20Sopenharmony_ci [snd_soc_dapm_switch] = 6, 1278c2ecf20Sopenharmony_ci [snd_soc_dapm_mixer_named_ctl] = 6, 1288c2ecf20Sopenharmony_ci [snd_soc_dapm_mixer] = 6, 1298c2ecf20Sopenharmony_ci [snd_soc_dapm_dac] = 7, 1308c2ecf20Sopenharmony_ci [snd_soc_dapm_mic] = 8, 1318c2ecf20Sopenharmony_ci [snd_soc_dapm_siggen] = 8, 1328c2ecf20Sopenharmony_ci [snd_soc_dapm_input] = 8, 1338c2ecf20Sopenharmony_ci [snd_soc_dapm_output] = 8, 1348c2ecf20Sopenharmony_ci [snd_soc_dapm_micbias] = 9, 1358c2ecf20Sopenharmony_ci [snd_soc_dapm_vmid] = 9, 1368c2ecf20Sopenharmony_ci [snd_soc_dapm_mux] = 10, 1378c2ecf20Sopenharmony_ci [snd_soc_dapm_demux] = 10, 1388c2ecf20Sopenharmony_ci [snd_soc_dapm_aif_in] = 11, 1398c2ecf20Sopenharmony_ci [snd_soc_dapm_aif_out] = 11, 1408c2ecf20Sopenharmony_ci [snd_soc_dapm_dai_in] = 11, 1418c2ecf20Sopenharmony_ci [snd_soc_dapm_dai_out] = 11, 1428c2ecf20Sopenharmony_ci [snd_soc_dapm_dai_link] = 12, 1438c2ecf20Sopenharmony_ci [snd_soc_dapm_supply] = 13, 1448c2ecf20Sopenharmony_ci [snd_soc_dapm_clock_supply] = 14, 1458c2ecf20Sopenharmony_ci [snd_soc_dapm_pinctrl] = 14, 1468c2ecf20Sopenharmony_ci [snd_soc_dapm_regulator_supply] = 14, 1478c2ecf20Sopenharmony_ci [snd_soc_dapm_post] = 15, 1488c2ecf20Sopenharmony_ci}; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic void dapm_assert_locked(struct snd_soc_dapm_context *dapm) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci if (dapm->card && dapm->card->instantiated) 1538c2ecf20Sopenharmony_ci lockdep_assert_held(&dapm->card->dapm_mutex); 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic void pop_wait(u32 pop_time) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci if (pop_time) 1598c2ecf20Sopenharmony_ci schedule_timeout_uninterruptible(msecs_to_jiffies(pop_time)); 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci__printf(3, 4) 1638c2ecf20Sopenharmony_cistatic void pop_dbg(struct device *dev, u32 pop_time, const char *fmt, ...) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci va_list args; 1668c2ecf20Sopenharmony_ci char *buf; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci if (!pop_time) 1698c2ecf20Sopenharmony_ci return; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci buf = kmalloc(PAGE_SIZE, GFP_KERNEL); 1728c2ecf20Sopenharmony_ci if (buf == NULL) 1738c2ecf20Sopenharmony_ci return; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci va_start(args, fmt); 1768c2ecf20Sopenharmony_ci vsnprintf(buf, PAGE_SIZE, fmt, args); 1778c2ecf20Sopenharmony_ci dev_info(dev, "%s", buf); 1788c2ecf20Sopenharmony_ci va_end(args); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci kfree(buf); 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic bool dapm_dirty_widget(struct snd_soc_dapm_widget *w) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci return !list_empty(&w->dirty); 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic void dapm_mark_dirty(struct snd_soc_dapm_widget *w, const char *reason) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci dapm_assert_locked(w->dapm); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci if (!dapm_dirty_widget(w)) { 1938c2ecf20Sopenharmony_ci dev_vdbg(w->dapm->dev, "Marking %s dirty due to %s\n", 1948c2ecf20Sopenharmony_ci w->name, reason); 1958c2ecf20Sopenharmony_ci list_add_tail(&w->dirty, &w->dapm->card->dapm_dirty); 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci/* 2008c2ecf20Sopenharmony_ci * Common implementation for dapm_widget_invalidate_input_paths() and 2018c2ecf20Sopenharmony_ci * dapm_widget_invalidate_output_paths(). The function is inlined since the 2028c2ecf20Sopenharmony_ci * combined size of the two specialized functions is only marginally larger then 2038c2ecf20Sopenharmony_ci * the size of the generic function and at the same time the fast path of the 2048c2ecf20Sopenharmony_ci * specialized functions is significantly smaller than the generic function. 2058c2ecf20Sopenharmony_ci */ 2068c2ecf20Sopenharmony_cistatic __always_inline void dapm_widget_invalidate_paths( 2078c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *w, enum snd_soc_dapm_direction dir) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci enum snd_soc_dapm_direction rdir = SND_SOC_DAPM_DIR_REVERSE(dir); 2108c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *node; 2118c2ecf20Sopenharmony_ci struct snd_soc_dapm_path *p; 2128c2ecf20Sopenharmony_ci LIST_HEAD(list); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci dapm_assert_locked(w->dapm); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci if (w->endpoints[dir] == -1) 2178c2ecf20Sopenharmony_ci return; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci list_add_tail(&w->work_list, &list); 2208c2ecf20Sopenharmony_ci w->endpoints[dir] = -1; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci list_for_each_entry(w, &list, work_list) { 2238c2ecf20Sopenharmony_ci snd_soc_dapm_widget_for_each_path(w, dir, p) { 2248c2ecf20Sopenharmony_ci if (p->is_supply || p->weak || !p->connect) 2258c2ecf20Sopenharmony_ci continue; 2268c2ecf20Sopenharmony_ci node = p->node[rdir]; 2278c2ecf20Sopenharmony_ci if (node->endpoints[dir] != -1) { 2288c2ecf20Sopenharmony_ci node->endpoints[dir] = -1; 2298c2ecf20Sopenharmony_ci list_add_tail(&node->work_list, &list); 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci/* 2368c2ecf20Sopenharmony_ci * dapm_widget_invalidate_input_paths() - Invalidate the cached number of 2378c2ecf20Sopenharmony_ci * input paths 2388c2ecf20Sopenharmony_ci * @w: The widget for which to invalidate the cached number of input paths 2398c2ecf20Sopenharmony_ci * 2408c2ecf20Sopenharmony_ci * Resets the cached number of inputs for the specified widget and all widgets 2418c2ecf20Sopenharmony_ci * that can be reached via outcoming paths from the widget. 2428c2ecf20Sopenharmony_ci * 2438c2ecf20Sopenharmony_ci * This function must be called if the number of output paths for a widget might 2448c2ecf20Sopenharmony_ci * have changed. E.g. if the source state of a widget changes or a path is added 2458c2ecf20Sopenharmony_ci * or activated with the widget as the sink. 2468c2ecf20Sopenharmony_ci */ 2478c2ecf20Sopenharmony_cistatic void dapm_widget_invalidate_input_paths(struct snd_soc_dapm_widget *w) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci dapm_widget_invalidate_paths(w, SND_SOC_DAPM_DIR_IN); 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci/* 2538c2ecf20Sopenharmony_ci * dapm_widget_invalidate_output_paths() - Invalidate the cached number of 2548c2ecf20Sopenharmony_ci * output paths 2558c2ecf20Sopenharmony_ci * @w: The widget for which to invalidate the cached number of output paths 2568c2ecf20Sopenharmony_ci * 2578c2ecf20Sopenharmony_ci * Resets the cached number of outputs for the specified widget and all widgets 2588c2ecf20Sopenharmony_ci * that can be reached via incoming paths from the widget. 2598c2ecf20Sopenharmony_ci * 2608c2ecf20Sopenharmony_ci * This function must be called if the number of output paths for a widget might 2618c2ecf20Sopenharmony_ci * have changed. E.g. if the sink state of a widget changes or a path is added 2628c2ecf20Sopenharmony_ci * or activated with the widget as the source. 2638c2ecf20Sopenharmony_ci */ 2648c2ecf20Sopenharmony_cistatic void dapm_widget_invalidate_output_paths(struct snd_soc_dapm_widget *w) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci dapm_widget_invalidate_paths(w, SND_SOC_DAPM_DIR_OUT); 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci/* 2708c2ecf20Sopenharmony_ci * dapm_path_invalidate() - Invalidates the cached number of inputs and outputs 2718c2ecf20Sopenharmony_ci * for the widgets connected to a path 2728c2ecf20Sopenharmony_ci * @p: The path to invalidate 2738c2ecf20Sopenharmony_ci * 2748c2ecf20Sopenharmony_ci * Resets the cached number of inputs for the sink of the path and the cached 2758c2ecf20Sopenharmony_ci * number of outputs for the source of the path. 2768c2ecf20Sopenharmony_ci * 2778c2ecf20Sopenharmony_ci * This function must be called when a path is added, removed or the connected 2788c2ecf20Sopenharmony_ci * state changes. 2798c2ecf20Sopenharmony_ci */ 2808c2ecf20Sopenharmony_cistatic void dapm_path_invalidate(struct snd_soc_dapm_path *p) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci /* 2838c2ecf20Sopenharmony_ci * Weak paths or supply paths do not influence the number of input or 2848c2ecf20Sopenharmony_ci * output paths of their neighbors. 2858c2ecf20Sopenharmony_ci */ 2868c2ecf20Sopenharmony_ci if (p->weak || p->is_supply) 2878c2ecf20Sopenharmony_ci return; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci /* 2908c2ecf20Sopenharmony_ci * The number of connected endpoints is the sum of the number of 2918c2ecf20Sopenharmony_ci * connected endpoints of all neighbors. If a node with 0 connected 2928c2ecf20Sopenharmony_ci * endpoints is either connected or disconnected that sum won't change, 2938c2ecf20Sopenharmony_ci * so there is no need to re-check the path. 2948c2ecf20Sopenharmony_ci */ 2958c2ecf20Sopenharmony_ci if (p->source->endpoints[SND_SOC_DAPM_DIR_IN] != 0) 2968c2ecf20Sopenharmony_ci dapm_widget_invalidate_input_paths(p->sink); 2978c2ecf20Sopenharmony_ci if (p->sink->endpoints[SND_SOC_DAPM_DIR_OUT] != 0) 2988c2ecf20Sopenharmony_ci dapm_widget_invalidate_output_paths(p->source); 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_civoid dapm_mark_endpoints_dirty(struct snd_soc_card *card) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *w; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci mutex_lock(&card->dapm_mutex); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci for_each_card_widgets(card, w) { 3088c2ecf20Sopenharmony_ci if (w->is_ep) { 3098c2ecf20Sopenharmony_ci dapm_mark_dirty(w, "Rechecking endpoints"); 3108c2ecf20Sopenharmony_ci if (w->is_ep & SND_SOC_DAPM_EP_SINK) 3118c2ecf20Sopenharmony_ci dapm_widget_invalidate_output_paths(w); 3128c2ecf20Sopenharmony_ci if (w->is_ep & SND_SOC_DAPM_EP_SOURCE) 3138c2ecf20Sopenharmony_ci dapm_widget_invalidate_input_paths(w); 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci mutex_unlock(&card->dapm_mutex); 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dapm_mark_endpoints_dirty); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci/* create a new dapm widget */ 3228c2ecf20Sopenharmony_cistatic inline struct snd_soc_dapm_widget *dapm_cnew_widget( 3238c2ecf20Sopenharmony_ci const struct snd_soc_dapm_widget *_widget) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *w; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci w = kmemdup(_widget, sizeof(*_widget), GFP_KERNEL); 3288c2ecf20Sopenharmony_ci if (!w) 3298c2ecf20Sopenharmony_ci return NULL; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci /* 3328c2ecf20Sopenharmony_ci * w->name is duplicated in caller, but w->sname isn't. 3338c2ecf20Sopenharmony_ci * Duplicate it here if defined 3348c2ecf20Sopenharmony_ci */ 3358c2ecf20Sopenharmony_ci if (_widget->sname) { 3368c2ecf20Sopenharmony_ci w->sname = kstrdup_const(_widget->sname, GFP_KERNEL); 3378c2ecf20Sopenharmony_ci if (!w->sname) { 3388c2ecf20Sopenharmony_ci kfree(w); 3398c2ecf20Sopenharmony_ci return NULL; 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci return w; 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_cistruct dapm_kcontrol_data { 3468c2ecf20Sopenharmony_ci unsigned int value; 3478c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *widget; 3488c2ecf20Sopenharmony_ci struct list_head paths; 3498c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget_list *wlist; 3508c2ecf20Sopenharmony_ci}; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_cistatic int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget, 3538c2ecf20Sopenharmony_ci struct snd_kcontrol *kcontrol, const char *ctrl_name) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci struct dapm_kcontrol_data *data; 3568c2ecf20Sopenharmony_ci struct soc_mixer_control *mc; 3578c2ecf20Sopenharmony_ci struct soc_enum *e; 3588c2ecf20Sopenharmony_ci const char *name; 3598c2ecf20Sopenharmony_ci int ret; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci data = kzalloc(sizeof(*data), GFP_KERNEL); 3628c2ecf20Sopenharmony_ci if (!data) 3638c2ecf20Sopenharmony_ci return -ENOMEM; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&data->paths); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci switch (widget->id) { 3688c2ecf20Sopenharmony_ci case snd_soc_dapm_switch: 3698c2ecf20Sopenharmony_ci case snd_soc_dapm_mixer: 3708c2ecf20Sopenharmony_ci case snd_soc_dapm_mixer_named_ctl: 3718c2ecf20Sopenharmony_ci mc = (struct soc_mixer_control *)kcontrol->private_value; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci if (mc->autodisable && snd_soc_volsw_is_stereo(mc)) 3748c2ecf20Sopenharmony_ci dev_warn(widget->dapm->dev, 3758c2ecf20Sopenharmony_ci "ASoC: Unsupported stereo autodisable control '%s'\n", 3768c2ecf20Sopenharmony_ci ctrl_name); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci if (mc->autodisable) { 3798c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget template; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci name = kasprintf(GFP_KERNEL, "%s %s", ctrl_name, 3828c2ecf20Sopenharmony_ci "Autodisable"); 3838c2ecf20Sopenharmony_ci if (!name) { 3848c2ecf20Sopenharmony_ci ret = -ENOMEM; 3858c2ecf20Sopenharmony_ci goto err_data; 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci memset(&template, 0, sizeof(template)); 3898c2ecf20Sopenharmony_ci template.reg = mc->reg; 3908c2ecf20Sopenharmony_ci template.mask = (1 << fls(mc->max)) - 1; 3918c2ecf20Sopenharmony_ci template.shift = mc->shift; 3928c2ecf20Sopenharmony_ci if (mc->invert) 3938c2ecf20Sopenharmony_ci template.off_val = mc->max; 3948c2ecf20Sopenharmony_ci else 3958c2ecf20Sopenharmony_ci template.off_val = 0; 3968c2ecf20Sopenharmony_ci template.on_val = template.off_val; 3978c2ecf20Sopenharmony_ci template.id = snd_soc_dapm_kcontrol; 3988c2ecf20Sopenharmony_ci template.name = name; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci data->value = template.on_val; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci data->widget = 4038c2ecf20Sopenharmony_ci snd_soc_dapm_new_control_unlocked(widget->dapm, 4048c2ecf20Sopenharmony_ci &template); 4058c2ecf20Sopenharmony_ci kfree(name); 4068c2ecf20Sopenharmony_ci if (IS_ERR(data->widget)) { 4078c2ecf20Sopenharmony_ci ret = PTR_ERR(data->widget); 4088c2ecf20Sopenharmony_ci goto err_data; 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci break; 4128c2ecf20Sopenharmony_ci case snd_soc_dapm_demux: 4138c2ecf20Sopenharmony_ci case snd_soc_dapm_mux: 4148c2ecf20Sopenharmony_ci e = (struct soc_enum *)kcontrol->private_value; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci if (e->autodisable) { 4178c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget template; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci name = kasprintf(GFP_KERNEL, "%s %s", ctrl_name, 4208c2ecf20Sopenharmony_ci "Autodisable"); 4218c2ecf20Sopenharmony_ci if (!name) { 4228c2ecf20Sopenharmony_ci ret = -ENOMEM; 4238c2ecf20Sopenharmony_ci goto err_data; 4248c2ecf20Sopenharmony_ci } 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci memset(&template, 0, sizeof(template)); 4278c2ecf20Sopenharmony_ci template.reg = e->reg; 4288c2ecf20Sopenharmony_ci template.mask = e->mask; 4298c2ecf20Sopenharmony_ci template.shift = e->shift_l; 4308c2ecf20Sopenharmony_ci template.off_val = snd_soc_enum_item_to_val(e, 0); 4318c2ecf20Sopenharmony_ci template.on_val = template.off_val; 4328c2ecf20Sopenharmony_ci template.id = snd_soc_dapm_kcontrol; 4338c2ecf20Sopenharmony_ci template.name = name; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci data->value = template.on_val; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci data->widget = snd_soc_dapm_new_control_unlocked( 4388c2ecf20Sopenharmony_ci widget->dapm, &template); 4398c2ecf20Sopenharmony_ci kfree(name); 4408c2ecf20Sopenharmony_ci if (IS_ERR(data->widget)) { 4418c2ecf20Sopenharmony_ci ret = PTR_ERR(data->widget); 4428c2ecf20Sopenharmony_ci goto err_data; 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci snd_soc_dapm_add_path(widget->dapm, data->widget, 4468c2ecf20Sopenharmony_ci widget, NULL, NULL); 4478c2ecf20Sopenharmony_ci } else if (e->reg != SND_SOC_NOPM) { 4488c2ecf20Sopenharmony_ci data->value = soc_dapm_read(widget->dapm, e->reg) & 4498c2ecf20Sopenharmony_ci (e->mask << e->shift_l); 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci break; 4528c2ecf20Sopenharmony_ci default: 4538c2ecf20Sopenharmony_ci break; 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci kcontrol->private_data = data; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci return 0; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_cierr_data: 4618c2ecf20Sopenharmony_ci kfree(data); 4628c2ecf20Sopenharmony_ci return ret; 4638c2ecf20Sopenharmony_ci} 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_cistatic void dapm_kcontrol_free(struct snd_kcontrol *kctl) 4668c2ecf20Sopenharmony_ci{ 4678c2ecf20Sopenharmony_ci struct dapm_kcontrol_data *data = snd_kcontrol_chip(kctl); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci list_del(&data->paths); 4708c2ecf20Sopenharmony_ci kfree(data->wlist); 4718c2ecf20Sopenharmony_ci kfree(data); 4728c2ecf20Sopenharmony_ci} 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cistatic struct snd_soc_dapm_widget_list *dapm_kcontrol_get_wlist( 4758c2ecf20Sopenharmony_ci const struct snd_kcontrol *kcontrol) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci return data->wlist; 4808c2ecf20Sopenharmony_ci} 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_cistatic int dapm_kcontrol_add_widget(struct snd_kcontrol *kcontrol, 4838c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *widget) 4848c2ecf20Sopenharmony_ci{ 4858c2ecf20Sopenharmony_ci struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); 4868c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget_list *new_wlist; 4878c2ecf20Sopenharmony_ci unsigned int n; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci if (data->wlist) 4908c2ecf20Sopenharmony_ci n = data->wlist->num_widgets + 1; 4918c2ecf20Sopenharmony_ci else 4928c2ecf20Sopenharmony_ci n = 1; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci new_wlist = krealloc(data->wlist, 4958c2ecf20Sopenharmony_ci struct_size(new_wlist, widgets, n), 4968c2ecf20Sopenharmony_ci GFP_KERNEL); 4978c2ecf20Sopenharmony_ci if (!new_wlist) 4988c2ecf20Sopenharmony_ci return -ENOMEM; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci new_wlist->widgets[n - 1] = widget; 5018c2ecf20Sopenharmony_ci new_wlist->num_widgets = n; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci data->wlist = new_wlist; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci return 0; 5068c2ecf20Sopenharmony_ci} 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_cistatic void dapm_kcontrol_add_path(const struct snd_kcontrol *kcontrol, 5098c2ecf20Sopenharmony_ci struct snd_soc_dapm_path *path) 5108c2ecf20Sopenharmony_ci{ 5118c2ecf20Sopenharmony_ci struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci list_add_tail(&path->list_kcontrol, &data->paths); 5148c2ecf20Sopenharmony_ci} 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_cistatic bool dapm_kcontrol_is_powered(const struct snd_kcontrol *kcontrol) 5178c2ecf20Sopenharmony_ci{ 5188c2ecf20Sopenharmony_ci struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci if (!data->widget) 5218c2ecf20Sopenharmony_ci return true; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci return data->widget->power; 5248c2ecf20Sopenharmony_ci} 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_cistatic struct list_head *dapm_kcontrol_get_path_list( 5278c2ecf20Sopenharmony_ci const struct snd_kcontrol *kcontrol) 5288c2ecf20Sopenharmony_ci{ 5298c2ecf20Sopenharmony_ci struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci return &data->paths; 5328c2ecf20Sopenharmony_ci} 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci#define dapm_kcontrol_for_each_path(path, kcontrol) \ 5358c2ecf20Sopenharmony_ci list_for_each_entry(path, dapm_kcontrol_get_path_list(kcontrol), \ 5368c2ecf20Sopenharmony_ci list_kcontrol) 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ciunsigned int dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol) 5398c2ecf20Sopenharmony_ci{ 5408c2ecf20Sopenharmony_ci struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci return data->value; 5438c2ecf20Sopenharmony_ci} 5448c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dapm_kcontrol_get_value); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_cistatic bool dapm_kcontrol_set_value(const struct snd_kcontrol *kcontrol, 5478c2ecf20Sopenharmony_ci unsigned int value) 5488c2ecf20Sopenharmony_ci{ 5498c2ecf20Sopenharmony_ci struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci if (data->value == value) 5528c2ecf20Sopenharmony_ci return false; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci if (data->widget) { 5558c2ecf20Sopenharmony_ci switch (dapm_kcontrol_get_wlist(kcontrol)->widgets[0]->id) { 5568c2ecf20Sopenharmony_ci case snd_soc_dapm_switch: 5578c2ecf20Sopenharmony_ci case snd_soc_dapm_mixer: 5588c2ecf20Sopenharmony_ci case snd_soc_dapm_mixer_named_ctl: 5598c2ecf20Sopenharmony_ci data->widget->on_val = value & data->widget->mask; 5608c2ecf20Sopenharmony_ci break; 5618c2ecf20Sopenharmony_ci case snd_soc_dapm_demux: 5628c2ecf20Sopenharmony_ci case snd_soc_dapm_mux: 5638c2ecf20Sopenharmony_ci data->widget->on_val = value >> data->widget->shift; 5648c2ecf20Sopenharmony_ci break; 5658c2ecf20Sopenharmony_ci default: 5668c2ecf20Sopenharmony_ci data->widget->on_val = value; 5678c2ecf20Sopenharmony_ci break; 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci } 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci data->value = value; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci return true; 5748c2ecf20Sopenharmony_ci} 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci/** 5778c2ecf20Sopenharmony_ci * snd_soc_dapm_kcontrol_widget() - Returns the widget associated to a 5788c2ecf20Sopenharmony_ci * kcontrol 5798c2ecf20Sopenharmony_ci * @kcontrol: The kcontrol 5808c2ecf20Sopenharmony_ci */ 5818c2ecf20Sopenharmony_cistruct snd_soc_dapm_widget *snd_soc_dapm_kcontrol_widget( 5828c2ecf20Sopenharmony_ci struct snd_kcontrol *kcontrol) 5838c2ecf20Sopenharmony_ci{ 5848c2ecf20Sopenharmony_ci return dapm_kcontrol_get_wlist(kcontrol)->widgets[0]; 5858c2ecf20Sopenharmony_ci} 5868c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_widget); 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci/** 5898c2ecf20Sopenharmony_ci * snd_soc_dapm_kcontrol_dapm() - Returns the dapm context associated to a 5908c2ecf20Sopenharmony_ci * kcontrol 5918c2ecf20Sopenharmony_ci * @kcontrol: The kcontrol 5928c2ecf20Sopenharmony_ci * 5938c2ecf20Sopenharmony_ci * Note: This function must only be used on kcontrols that are known to have 5948c2ecf20Sopenharmony_ci * been registered for a CODEC. Otherwise the behaviour is undefined. 5958c2ecf20Sopenharmony_ci */ 5968c2ecf20Sopenharmony_cistruct snd_soc_dapm_context *snd_soc_dapm_kcontrol_dapm( 5978c2ecf20Sopenharmony_ci struct snd_kcontrol *kcontrol) 5988c2ecf20Sopenharmony_ci{ 5998c2ecf20Sopenharmony_ci return dapm_kcontrol_get_wlist(kcontrol)->widgets[0]->dapm; 6008c2ecf20Sopenharmony_ci} 6018c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_dapm); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_cistatic void dapm_reset(struct snd_soc_card *card) 6048c2ecf20Sopenharmony_ci{ 6058c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *w; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci lockdep_assert_held(&card->dapm_mutex); 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci memset(&card->dapm_stats, 0, sizeof(card->dapm_stats)); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci for_each_card_widgets(card, w) { 6128c2ecf20Sopenharmony_ci w->new_power = w->power; 6138c2ecf20Sopenharmony_ci w->power_checked = false; 6148c2ecf20Sopenharmony_ci } 6158c2ecf20Sopenharmony_ci} 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_cistatic const char *soc_dapm_prefix(struct snd_soc_dapm_context *dapm) 6188c2ecf20Sopenharmony_ci{ 6198c2ecf20Sopenharmony_ci if (!dapm->component) 6208c2ecf20Sopenharmony_ci return NULL; 6218c2ecf20Sopenharmony_ci return dapm->component->name_prefix; 6228c2ecf20Sopenharmony_ci} 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_cistatic unsigned int soc_dapm_read(struct snd_soc_dapm_context *dapm, int reg) 6258c2ecf20Sopenharmony_ci{ 6268c2ecf20Sopenharmony_ci if (!dapm->component) 6278c2ecf20Sopenharmony_ci return -EIO; 6288c2ecf20Sopenharmony_ci return snd_soc_component_read(dapm->component, reg); 6298c2ecf20Sopenharmony_ci} 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_cistatic int soc_dapm_update_bits(struct snd_soc_dapm_context *dapm, 6328c2ecf20Sopenharmony_ci int reg, unsigned int mask, unsigned int value) 6338c2ecf20Sopenharmony_ci{ 6348c2ecf20Sopenharmony_ci if (!dapm->component) 6358c2ecf20Sopenharmony_ci return -EIO; 6368c2ecf20Sopenharmony_ci return snd_soc_component_update_bits(dapm->component, reg, 6378c2ecf20Sopenharmony_ci mask, value); 6388c2ecf20Sopenharmony_ci} 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_cistatic int soc_dapm_test_bits(struct snd_soc_dapm_context *dapm, 6418c2ecf20Sopenharmony_ci int reg, unsigned int mask, unsigned int value) 6428c2ecf20Sopenharmony_ci{ 6438c2ecf20Sopenharmony_ci if (!dapm->component) 6448c2ecf20Sopenharmony_ci return -EIO; 6458c2ecf20Sopenharmony_ci return snd_soc_component_test_bits(dapm->component, reg, mask, value); 6468c2ecf20Sopenharmony_ci} 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_cistatic void soc_dapm_async_complete(struct snd_soc_dapm_context *dapm) 6498c2ecf20Sopenharmony_ci{ 6508c2ecf20Sopenharmony_ci if (dapm->component) 6518c2ecf20Sopenharmony_ci snd_soc_component_async_complete(dapm->component); 6528c2ecf20Sopenharmony_ci} 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_cistatic struct snd_soc_dapm_widget * 6558c2ecf20Sopenharmony_cidapm_wcache_lookup(struct snd_soc_dapm_wcache *wcache, const char *name) 6568c2ecf20Sopenharmony_ci{ 6578c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *w = wcache->widget; 6588c2ecf20Sopenharmony_ci struct list_head *wlist; 6598c2ecf20Sopenharmony_ci const int depth = 2; 6608c2ecf20Sopenharmony_ci int i = 0; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci if (w) { 6638c2ecf20Sopenharmony_ci wlist = &w->dapm->card->widgets; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci list_for_each_entry_from(w, wlist, list) { 6668c2ecf20Sopenharmony_ci if (!strcmp(name, w->name)) 6678c2ecf20Sopenharmony_ci return w; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci if (++i == depth) 6708c2ecf20Sopenharmony_ci break; 6718c2ecf20Sopenharmony_ci } 6728c2ecf20Sopenharmony_ci } 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci return NULL; 6758c2ecf20Sopenharmony_ci} 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_cistatic inline void dapm_wcache_update(struct snd_soc_dapm_wcache *wcache, 6788c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *w) 6798c2ecf20Sopenharmony_ci{ 6808c2ecf20Sopenharmony_ci wcache->widget = w; 6818c2ecf20Sopenharmony_ci} 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci/** 6848c2ecf20Sopenharmony_ci * snd_soc_dapm_force_bias_level() - Sets the DAPM bias level 6858c2ecf20Sopenharmony_ci * @dapm: The DAPM context for which to set the level 6868c2ecf20Sopenharmony_ci * @level: The level to set 6878c2ecf20Sopenharmony_ci * 6888c2ecf20Sopenharmony_ci * Forces the DAPM bias level to a specific state. It will call the bias level 6898c2ecf20Sopenharmony_ci * callback of DAPM context with the specified level. This will even happen if 6908c2ecf20Sopenharmony_ci * the context is already at the same level. Furthermore it will not go through 6918c2ecf20Sopenharmony_ci * the normal bias level sequencing, meaning any intermediate states between the 6928c2ecf20Sopenharmony_ci * current and the target state will not be entered. 6938c2ecf20Sopenharmony_ci * 6948c2ecf20Sopenharmony_ci * Note that the change in bias level is only temporary and the next time 6958c2ecf20Sopenharmony_ci * snd_soc_dapm_sync() is called the state will be set to the level as 6968c2ecf20Sopenharmony_ci * determined by the DAPM core. The function is mainly intended to be used to 6978c2ecf20Sopenharmony_ci * used during probe or resume from suspend to power up the device so 6988c2ecf20Sopenharmony_ci * initialization can be done, before the DAPM core takes over. 6998c2ecf20Sopenharmony_ci */ 7008c2ecf20Sopenharmony_ciint snd_soc_dapm_force_bias_level(struct snd_soc_dapm_context *dapm, 7018c2ecf20Sopenharmony_ci enum snd_soc_bias_level level) 7028c2ecf20Sopenharmony_ci{ 7038c2ecf20Sopenharmony_ci int ret = 0; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci if (dapm->component) 7068c2ecf20Sopenharmony_ci ret = snd_soc_component_set_bias_level(dapm->component, level); 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci if (ret == 0) 7098c2ecf20Sopenharmony_ci dapm->bias_level = level; 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci return ret; 7128c2ecf20Sopenharmony_ci} 7138c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_force_bias_level); 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci/** 7168c2ecf20Sopenharmony_ci * snd_soc_dapm_set_bias_level - set the bias level for the system 7178c2ecf20Sopenharmony_ci * @dapm: DAPM context 7188c2ecf20Sopenharmony_ci * @level: level to configure 7198c2ecf20Sopenharmony_ci * 7208c2ecf20Sopenharmony_ci * Configure the bias (power) levels for the SoC audio device. 7218c2ecf20Sopenharmony_ci * 7228c2ecf20Sopenharmony_ci * Returns 0 for success else error. 7238c2ecf20Sopenharmony_ci */ 7248c2ecf20Sopenharmony_cistatic int snd_soc_dapm_set_bias_level(struct snd_soc_dapm_context *dapm, 7258c2ecf20Sopenharmony_ci enum snd_soc_bias_level level) 7268c2ecf20Sopenharmony_ci{ 7278c2ecf20Sopenharmony_ci struct snd_soc_card *card = dapm->card; 7288c2ecf20Sopenharmony_ci int ret = 0; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci trace_snd_soc_bias_level_start(card, level); 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci ret = snd_soc_card_set_bias_level(card, dapm, level); 7338c2ecf20Sopenharmony_ci if (ret != 0) 7348c2ecf20Sopenharmony_ci goto out; 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci if (!card || dapm != &card->dapm) 7378c2ecf20Sopenharmony_ci ret = snd_soc_dapm_force_bias_level(dapm, level); 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci if (ret != 0) 7408c2ecf20Sopenharmony_ci goto out; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci ret = snd_soc_card_set_bias_level_post(card, dapm, level); 7438c2ecf20Sopenharmony_ciout: 7448c2ecf20Sopenharmony_ci trace_snd_soc_bias_level_done(card, level); 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci return ret; 7478c2ecf20Sopenharmony_ci} 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci/* connect mux widget to its interconnecting audio paths */ 7508c2ecf20Sopenharmony_cistatic int dapm_connect_mux(struct snd_soc_dapm_context *dapm, 7518c2ecf20Sopenharmony_ci struct snd_soc_dapm_path *path, const char *control_name, 7528c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *w) 7538c2ecf20Sopenharmony_ci{ 7548c2ecf20Sopenharmony_ci const struct snd_kcontrol_new *kcontrol = &w->kcontrol_news[0]; 7558c2ecf20Sopenharmony_ci struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 7568c2ecf20Sopenharmony_ci unsigned int val, item; 7578c2ecf20Sopenharmony_ci int i; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci if (e->reg != SND_SOC_NOPM) { 7608c2ecf20Sopenharmony_ci val = soc_dapm_read(dapm, e->reg); 7618c2ecf20Sopenharmony_ci val = (val >> e->shift_l) & e->mask; 7628c2ecf20Sopenharmony_ci item = snd_soc_enum_val_to_item(e, val); 7638c2ecf20Sopenharmony_ci } else { 7648c2ecf20Sopenharmony_ci /* since a virtual mux has no backing registers to 7658c2ecf20Sopenharmony_ci * decide which path to connect, it will try to match 7668c2ecf20Sopenharmony_ci * with the first enumeration. This is to ensure 7678c2ecf20Sopenharmony_ci * that the default mux choice (the first) will be 7688c2ecf20Sopenharmony_ci * correctly powered up during initialization. 7698c2ecf20Sopenharmony_ci */ 7708c2ecf20Sopenharmony_ci item = 0; 7718c2ecf20Sopenharmony_ci } 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci i = match_string(e->texts, e->items, control_name); 7748c2ecf20Sopenharmony_ci if (i < 0) 7758c2ecf20Sopenharmony_ci return -ENODEV; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci path->name = e->texts[i]; 7788c2ecf20Sopenharmony_ci path->connect = (i == item); 7798c2ecf20Sopenharmony_ci return 0; 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci} 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci/* set up initial codec paths */ 7848c2ecf20Sopenharmony_cistatic void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i, 7858c2ecf20Sopenharmony_ci int nth_path) 7868c2ecf20Sopenharmony_ci{ 7878c2ecf20Sopenharmony_ci struct soc_mixer_control *mc = (struct soc_mixer_control *) 7888c2ecf20Sopenharmony_ci p->sink->kcontrol_news[i].private_value; 7898c2ecf20Sopenharmony_ci unsigned int reg = mc->reg; 7908c2ecf20Sopenharmony_ci unsigned int shift = mc->shift; 7918c2ecf20Sopenharmony_ci unsigned int max = mc->max; 7928c2ecf20Sopenharmony_ci unsigned int mask = (1 << fls(max)) - 1; 7938c2ecf20Sopenharmony_ci unsigned int invert = mc->invert; 7948c2ecf20Sopenharmony_ci unsigned int val; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci if (reg != SND_SOC_NOPM) { 7978c2ecf20Sopenharmony_ci val = soc_dapm_read(p->sink->dapm, reg); 7988c2ecf20Sopenharmony_ci /* 7998c2ecf20Sopenharmony_ci * The nth_path argument allows this function to know 8008c2ecf20Sopenharmony_ci * which path of a kcontrol it is setting the initial 8018c2ecf20Sopenharmony_ci * status for. Ideally this would support any number 8028c2ecf20Sopenharmony_ci * of paths and channels. But since kcontrols only come 8038c2ecf20Sopenharmony_ci * in mono and stereo variants, we are limited to 2 8048c2ecf20Sopenharmony_ci * channels. 8058c2ecf20Sopenharmony_ci * 8068c2ecf20Sopenharmony_ci * The following code assumes for stereo controls the 8078c2ecf20Sopenharmony_ci * first path is the left channel, and all remaining 8088c2ecf20Sopenharmony_ci * paths are the right channel. 8098c2ecf20Sopenharmony_ci */ 8108c2ecf20Sopenharmony_ci if (snd_soc_volsw_is_stereo(mc) && nth_path > 0) { 8118c2ecf20Sopenharmony_ci if (reg != mc->rreg) 8128c2ecf20Sopenharmony_ci val = soc_dapm_read(p->sink->dapm, mc->rreg); 8138c2ecf20Sopenharmony_ci val = (val >> mc->rshift) & mask; 8148c2ecf20Sopenharmony_ci } else { 8158c2ecf20Sopenharmony_ci val = (val >> shift) & mask; 8168c2ecf20Sopenharmony_ci } 8178c2ecf20Sopenharmony_ci if (invert) 8188c2ecf20Sopenharmony_ci val = max - val; 8198c2ecf20Sopenharmony_ci p->connect = !!val; 8208c2ecf20Sopenharmony_ci } else { 8218c2ecf20Sopenharmony_ci /* since a virtual mixer has no backing registers to 8228c2ecf20Sopenharmony_ci * decide which path to connect, it will try to match 8238c2ecf20Sopenharmony_ci * with initial state. This is to ensure 8248c2ecf20Sopenharmony_ci * that the default mixer choice will be 8258c2ecf20Sopenharmony_ci * correctly powered up during initialization. 8268c2ecf20Sopenharmony_ci */ 8278c2ecf20Sopenharmony_ci p->connect = invert; 8288c2ecf20Sopenharmony_ci } 8298c2ecf20Sopenharmony_ci} 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci/* connect mixer widget to its interconnecting audio paths */ 8328c2ecf20Sopenharmony_cistatic int dapm_connect_mixer(struct snd_soc_dapm_context *dapm, 8338c2ecf20Sopenharmony_ci struct snd_soc_dapm_path *path, const char *control_name) 8348c2ecf20Sopenharmony_ci{ 8358c2ecf20Sopenharmony_ci int i, nth_path = 0; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci /* search for mixer kcontrol */ 8388c2ecf20Sopenharmony_ci for (i = 0; i < path->sink->num_kcontrols; i++) { 8398c2ecf20Sopenharmony_ci if (!strcmp(control_name, path->sink->kcontrol_news[i].name)) { 8408c2ecf20Sopenharmony_ci path->name = path->sink->kcontrol_news[i].name; 8418c2ecf20Sopenharmony_ci dapm_set_mixer_path_status(path, i, nth_path++); 8428c2ecf20Sopenharmony_ci return 0; 8438c2ecf20Sopenharmony_ci } 8448c2ecf20Sopenharmony_ci } 8458c2ecf20Sopenharmony_ci return -ENODEV; 8468c2ecf20Sopenharmony_ci} 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_cistatic int dapm_is_shared_kcontrol(struct snd_soc_dapm_context *dapm, 8498c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *kcontrolw, 8508c2ecf20Sopenharmony_ci const struct snd_kcontrol_new *kcontrol_new, 8518c2ecf20Sopenharmony_ci struct snd_kcontrol **kcontrol) 8528c2ecf20Sopenharmony_ci{ 8538c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *w; 8548c2ecf20Sopenharmony_ci int i; 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci *kcontrol = NULL; 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci for_each_card_widgets(dapm->card, w) { 8598c2ecf20Sopenharmony_ci if (w == kcontrolw || w->dapm != kcontrolw->dapm) 8608c2ecf20Sopenharmony_ci continue; 8618c2ecf20Sopenharmony_ci for (i = 0; i < w->num_kcontrols; i++) { 8628c2ecf20Sopenharmony_ci if (&w->kcontrol_news[i] == kcontrol_new) { 8638c2ecf20Sopenharmony_ci if (w->kcontrols) 8648c2ecf20Sopenharmony_ci *kcontrol = w->kcontrols[i]; 8658c2ecf20Sopenharmony_ci return 1; 8668c2ecf20Sopenharmony_ci } 8678c2ecf20Sopenharmony_ci } 8688c2ecf20Sopenharmony_ci } 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci return 0; 8718c2ecf20Sopenharmony_ci} 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci/* 8748c2ecf20Sopenharmony_ci * Determine if a kcontrol is shared. If it is, look it up. If it isn't, 8758c2ecf20Sopenharmony_ci * create it. Either way, add the widget into the control's widget list 8768c2ecf20Sopenharmony_ci */ 8778c2ecf20Sopenharmony_cistatic int dapm_create_or_share_kcontrol(struct snd_soc_dapm_widget *w, 8788c2ecf20Sopenharmony_ci int kci) 8798c2ecf20Sopenharmony_ci{ 8808c2ecf20Sopenharmony_ci struct snd_soc_dapm_context *dapm = w->dapm; 8818c2ecf20Sopenharmony_ci struct snd_card *card = dapm->card->snd_card; 8828c2ecf20Sopenharmony_ci const char *prefix; 8838c2ecf20Sopenharmony_ci size_t prefix_len; 8848c2ecf20Sopenharmony_ci int shared; 8858c2ecf20Sopenharmony_ci struct snd_kcontrol *kcontrol; 8868c2ecf20Sopenharmony_ci bool wname_in_long_name, kcname_in_long_name; 8878c2ecf20Sopenharmony_ci char *long_name = NULL; 8888c2ecf20Sopenharmony_ci const char *name; 8898c2ecf20Sopenharmony_ci int ret = 0; 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci prefix = soc_dapm_prefix(dapm); 8928c2ecf20Sopenharmony_ci if (prefix) 8938c2ecf20Sopenharmony_ci prefix_len = strlen(prefix) + 1; 8948c2ecf20Sopenharmony_ci else 8958c2ecf20Sopenharmony_ci prefix_len = 0; 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci shared = dapm_is_shared_kcontrol(dapm, w, &w->kcontrol_news[kci], 8988c2ecf20Sopenharmony_ci &kcontrol); 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci if (!kcontrol) { 9018c2ecf20Sopenharmony_ci if (shared) { 9028c2ecf20Sopenharmony_ci wname_in_long_name = false; 9038c2ecf20Sopenharmony_ci kcname_in_long_name = true; 9048c2ecf20Sopenharmony_ci } else { 9058c2ecf20Sopenharmony_ci switch (w->id) { 9068c2ecf20Sopenharmony_ci case snd_soc_dapm_switch: 9078c2ecf20Sopenharmony_ci case snd_soc_dapm_mixer: 9088c2ecf20Sopenharmony_ci case snd_soc_dapm_pga: 9098c2ecf20Sopenharmony_ci case snd_soc_dapm_effect: 9108c2ecf20Sopenharmony_ci case snd_soc_dapm_out_drv: 9118c2ecf20Sopenharmony_ci wname_in_long_name = true; 9128c2ecf20Sopenharmony_ci kcname_in_long_name = true; 9138c2ecf20Sopenharmony_ci break; 9148c2ecf20Sopenharmony_ci case snd_soc_dapm_mixer_named_ctl: 9158c2ecf20Sopenharmony_ci wname_in_long_name = false; 9168c2ecf20Sopenharmony_ci kcname_in_long_name = true; 9178c2ecf20Sopenharmony_ci break; 9188c2ecf20Sopenharmony_ci case snd_soc_dapm_demux: 9198c2ecf20Sopenharmony_ci case snd_soc_dapm_mux: 9208c2ecf20Sopenharmony_ci wname_in_long_name = true; 9218c2ecf20Sopenharmony_ci kcname_in_long_name = false; 9228c2ecf20Sopenharmony_ci break; 9238c2ecf20Sopenharmony_ci default: 9248c2ecf20Sopenharmony_ci return -EINVAL; 9258c2ecf20Sopenharmony_ci } 9268c2ecf20Sopenharmony_ci } 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci if (wname_in_long_name && kcname_in_long_name) { 9298c2ecf20Sopenharmony_ci /* 9308c2ecf20Sopenharmony_ci * The control will get a prefix from the control 9318c2ecf20Sopenharmony_ci * creation process but we're also using the same 9328c2ecf20Sopenharmony_ci * prefix for widgets so cut the prefix off the 9338c2ecf20Sopenharmony_ci * front of the widget name. 9348c2ecf20Sopenharmony_ci */ 9358c2ecf20Sopenharmony_ci long_name = kasprintf(GFP_KERNEL, "%s %s", 9368c2ecf20Sopenharmony_ci w->name + prefix_len, 9378c2ecf20Sopenharmony_ci w->kcontrol_news[kci].name); 9388c2ecf20Sopenharmony_ci if (long_name == NULL) 9398c2ecf20Sopenharmony_ci return -ENOMEM; 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci name = long_name; 9428c2ecf20Sopenharmony_ci } else if (wname_in_long_name) { 9438c2ecf20Sopenharmony_ci long_name = NULL; 9448c2ecf20Sopenharmony_ci name = w->name + prefix_len; 9458c2ecf20Sopenharmony_ci } else { 9468c2ecf20Sopenharmony_ci long_name = NULL; 9478c2ecf20Sopenharmony_ci name = w->kcontrol_news[kci].name; 9488c2ecf20Sopenharmony_ci } 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci kcontrol = snd_soc_cnew(&w->kcontrol_news[kci], NULL, name, 9518c2ecf20Sopenharmony_ci prefix); 9528c2ecf20Sopenharmony_ci if (!kcontrol) { 9538c2ecf20Sopenharmony_ci ret = -ENOMEM; 9548c2ecf20Sopenharmony_ci goto exit_free; 9558c2ecf20Sopenharmony_ci } 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci kcontrol->private_free = dapm_kcontrol_free; 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci ret = dapm_kcontrol_data_alloc(w, kcontrol, name); 9608c2ecf20Sopenharmony_ci if (ret) { 9618c2ecf20Sopenharmony_ci snd_ctl_free_one(kcontrol); 9628c2ecf20Sopenharmony_ci goto exit_free; 9638c2ecf20Sopenharmony_ci } 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci ret = snd_ctl_add(card, kcontrol); 9668c2ecf20Sopenharmony_ci if (ret < 0) { 9678c2ecf20Sopenharmony_ci dev_err(dapm->dev, 9688c2ecf20Sopenharmony_ci "ASoC: failed to add widget %s dapm kcontrol %s: %d\n", 9698c2ecf20Sopenharmony_ci w->name, name, ret); 9708c2ecf20Sopenharmony_ci goto exit_free; 9718c2ecf20Sopenharmony_ci } 9728c2ecf20Sopenharmony_ci } 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci ret = dapm_kcontrol_add_widget(kcontrol, w); 9758c2ecf20Sopenharmony_ci if (ret == 0) 9768c2ecf20Sopenharmony_ci w->kcontrols[kci] = kcontrol; 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ciexit_free: 9798c2ecf20Sopenharmony_ci kfree(long_name); 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci return ret; 9828c2ecf20Sopenharmony_ci} 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci/* create new dapm mixer control */ 9858c2ecf20Sopenharmony_cistatic int dapm_new_mixer(struct snd_soc_dapm_widget *w) 9868c2ecf20Sopenharmony_ci{ 9878c2ecf20Sopenharmony_ci int i, ret; 9888c2ecf20Sopenharmony_ci struct snd_soc_dapm_path *path; 9898c2ecf20Sopenharmony_ci struct dapm_kcontrol_data *data; 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci /* add kcontrol */ 9928c2ecf20Sopenharmony_ci for (i = 0; i < w->num_kcontrols; i++) { 9938c2ecf20Sopenharmony_ci /* match name */ 9948c2ecf20Sopenharmony_ci snd_soc_dapm_widget_for_each_source_path(w, path) { 9958c2ecf20Sopenharmony_ci /* mixer/mux paths name must match control name */ 9968c2ecf20Sopenharmony_ci if (path->name != (char *)w->kcontrol_news[i].name) 9978c2ecf20Sopenharmony_ci continue; 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci if (!w->kcontrols[i]) { 10008c2ecf20Sopenharmony_ci ret = dapm_create_or_share_kcontrol(w, i); 10018c2ecf20Sopenharmony_ci if (ret < 0) 10028c2ecf20Sopenharmony_ci return ret; 10038c2ecf20Sopenharmony_ci } 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci dapm_kcontrol_add_path(w->kcontrols[i], path); 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci data = snd_kcontrol_chip(w->kcontrols[i]); 10088c2ecf20Sopenharmony_ci if (data->widget) 10098c2ecf20Sopenharmony_ci snd_soc_dapm_add_path(data->widget->dapm, 10108c2ecf20Sopenharmony_ci data->widget, 10118c2ecf20Sopenharmony_ci path->source, 10128c2ecf20Sopenharmony_ci NULL, NULL); 10138c2ecf20Sopenharmony_ci } 10148c2ecf20Sopenharmony_ci } 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci return 0; 10178c2ecf20Sopenharmony_ci} 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci/* create new dapm mux control */ 10208c2ecf20Sopenharmony_cistatic int dapm_new_mux(struct snd_soc_dapm_widget *w) 10218c2ecf20Sopenharmony_ci{ 10228c2ecf20Sopenharmony_ci struct snd_soc_dapm_context *dapm = w->dapm; 10238c2ecf20Sopenharmony_ci enum snd_soc_dapm_direction dir; 10248c2ecf20Sopenharmony_ci struct snd_soc_dapm_path *path; 10258c2ecf20Sopenharmony_ci const char *type; 10268c2ecf20Sopenharmony_ci int ret; 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci switch (w->id) { 10298c2ecf20Sopenharmony_ci case snd_soc_dapm_mux: 10308c2ecf20Sopenharmony_ci dir = SND_SOC_DAPM_DIR_OUT; 10318c2ecf20Sopenharmony_ci type = "mux"; 10328c2ecf20Sopenharmony_ci break; 10338c2ecf20Sopenharmony_ci case snd_soc_dapm_demux: 10348c2ecf20Sopenharmony_ci dir = SND_SOC_DAPM_DIR_IN; 10358c2ecf20Sopenharmony_ci type = "demux"; 10368c2ecf20Sopenharmony_ci break; 10378c2ecf20Sopenharmony_ci default: 10388c2ecf20Sopenharmony_ci return -EINVAL; 10398c2ecf20Sopenharmony_ci } 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci if (w->num_kcontrols != 1) { 10428c2ecf20Sopenharmony_ci dev_err(dapm->dev, 10438c2ecf20Sopenharmony_ci "ASoC: %s %s has incorrect number of controls\n", type, 10448c2ecf20Sopenharmony_ci w->name); 10458c2ecf20Sopenharmony_ci return -EINVAL; 10468c2ecf20Sopenharmony_ci } 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci if (list_empty(&w->edges[dir])) { 10498c2ecf20Sopenharmony_ci dev_err(dapm->dev, "ASoC: %s %s has no paths\n", type, w->name); 10508c2ecf20Sopenharmony_ci return -EINVAL; 10518c2ecf20Sopenharmony_ci } 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci ret = dapm_create_or_share_kcontrol(w, 0); 10548c2ecf20Sopenharmony_ci if (ret < 0) 10558c2ecf20Sopenharmony_ci return ret; 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci snd_soc_dapm_widget_for_each_path(w, dir, path) { 10588c2ecf20Sopenharmony_ci if (path->name) 10598c2ecf20Sopenharmony_ci dapm_kcontrol_add_path(w->kcontrols[0], path); 10608c2ecf20Sopenharmony_ci } 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci return 0; 10638c2ecf20Sopenharmony_ci} 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci/* create new dapm volume control */ 10668c2ecf20Sopenharmony_cistatic int dapm_new_pga(struct snd_soc_dapm_widget *w) 10678c2ecf20Sopenharmony_ci{ 10688c2ecf20Sopenharmony_ci int i, ret; 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci for (i = 0; i < w->num_kcontrols; i++) { 10718c2ecf20Sopenharmony_ci ret = dapm_create_or_share_kcontrol(w, i); 10728c2ecf20Sopenharmony_ci if (ret < 0) 10738c2ecf20Sopenharmony_ci return ret; 10748c2ecf20Sopenharmony_ci } 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci return 0; 10778c2ecf20Sopenharmony_ci} 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci/* create new dapm dai link control */ 10808c2ecf20Sopenharmony_cistatic int dapm_new_dai_link(struct snd_soc_dapm_widget *w) 10818c2ecf20Sopenharmony_ci{ 10828c2ecf20Sopenharmony_ci int i, ret; 10838c2ecf20Sopenharmony_ci struct snd_kcontrol *kcontrol; 10848c2ecf20Sopenharmony_ci struct snd_soc_dapm_context *dapm = w->dapm; 10858c2ecf20Sopenharmony_ci struct snd_card *card = dapm->card->snd_card; 10868c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = w->priv; 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci /* create control for links with > 1 config */ 10898c2ecf20Sopenharmony_ci if (rtd->dai_link->num_params <= 1) 10908c2ecf20Sopenharmony_ci return 0; 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci /* add kcontrol */ 10938c2ecf20Sopenharmony_ci for (i = 0; i < w->num_kcontrols; i++) { 10948c2ecf20Sopenharmony_ci kcontrol = snd_soc_cnew(&w->kcontrol_news[i], w, 10958c2ecf20Sopenharmony_ci w->name, NULL); 10968c2ecf20Sopenharmony_ci ret = snd_ctl_add(card, kcontrol); 10978c2ecf20Sopenharmony_ci if (ret < 0) { 10988c2ecf20Sopenharmony_ci dev_err(dapm->dev, 10998c2ecf20Sopenharmony_ci "ASoC: failed to add widget %s dapm kcontrol %s: %d\n", 11008c2ecf20Sopenharmony_ci w->name, w->kcontrol_news[i].name, ret); 11018c2ecf20Sopenharmony_ci return ret; 11028c2ecf20Sopenharmony_ci } 11038c2ecf20Sopenharmony_ci kcontrol->private_data = w; 11048c2ecf20Sopenharmony_ci w->kcontrols[i] = kcontrol; 11058c2ecf20Sopenharmony_ci } 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci return 0; 11088c2ecf20Sopenharmony_ci} 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci/* We implement power down on suspend by checking the power state of 11118c2ecf20Sopenharmony_ci * the ALSA card - when we are suspending the ALSA state for the card 11128c2ecf20Sopenharmony_ci * is set to D3. 11138c2ecf20Sopenharmony_ci */ 11148c2ecf20Sopenharmony_cistatic int snd_soc_dapm_suspend_check(struct snd_soc_dapm_widget *widget) 11158c2ecf20Sopenharmony_ci{ 11168c2ecf20Sopenharmony_ci int level = snd_power_get_state(widget->dapm->card->snd_card); 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci switch (level) { 11198c2ecf20Sopenharmony_ci case SNDRV_CTL_POWER_D3hot: 11208c2ecf20Sopenharmony_ci case SNDRV_CTL_POWER_D3cold: 11218c2ecf20Sopenharmony_ci if (widget->ignore_suspend) 11228c2ecf20Sopenharmony_ci dev_dbg(widget->dapm->dev, "ASoC: %s ignoring suspend\n", 11238c2ecf20Sopenharmony_ci widget->name); 11248c2ecf20Sopenharmony_ci return widget->ignore_suspend; 11258c2ecf20Sopenharmony_ci default: 11268c2ecf20Sopenharmony_ci return 1; 11278c2ecf20Sopenharmony_ci } 11288c2ecf20Sopenharmony_ci} 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_cistatic void dapm_widget_list_free(struct snd_soc_dapm_widget_list **list) 11318c2ecf20Sopenharmony_ci{ 11328c2ecf20Sopenharmony_ci kfree(*list); 11338c2ecf20Sopenharmony_ci} 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_cistatic int dapm_widget_list_create(struct snd_soc_dapm_widget_list **list, 11368c2ecf20Sopenharmony_ci struct list_head *widgets) 11378c2ecf20Sopenharmony_ci{ 11388c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *w; 11398c2ecf20Sopenharmony_ci struct list_head *it; 11408c2ecf20Sopenharmony_ci unsigned int size = 0; 11418c2ecf20Sopenharmony_ci unsigned int i = 0; 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci list_for_each(it, widgets) 11448c2ecf20Sopenharmony_ci size++; 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci *list = kzalloc(struct_size(*list, widgets, size), GFP_KERNEL); 11478c2ecf20Sopenharmony_ci if (*list == NULL) 11488c2ecf20Sopenharmony_ci return -ENOMEM; 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci list_for_each_entry(w, widgets, work_list) 11518c2ecf20Sopenharmony_ci (*list)->widgets[i++] = w; 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci (*list)->num_widgets = i; 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci return 0; 11568c2ecf20Sopenharmony_ci} 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci/* 11598c2ecf20Sopenharmony_ci * Recursively reset the cached number of inputs or outputs for the specified 11608c2ecf20Sopenharmony_ci * widget and all widgets that can be reached via incoming or outcoming paths 11618c2ecf20Sopenharmony_ci * from the widget. 11628c2ecf20Sopenharmony_ci */ 11638c2ecf20Sopenharmony_cistatic void invalidate_paths_ep(struct snd_soc_dapm_widget *widget, 11648c2ecf20Sopenharmony_ci enum snd_soc_dapm_direction dir) 11658c2ecf20Sopenharmony_ci{ 11668c2ecf20Sopenharmony_ci enum snd_soc_dapm_direction rdir = SND_SOC_DAPM_DIR_REVERSE(dir); 11678c2ecf20Sopenharmony_ci struct snd_soc_dapm_path *path; 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci widget->endpoints[dir] = -1; 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci snd_soc_dapm_widget_for_each_path(widget, rdir, path) { 11728c2ecf20Sopenharmony_ci if (path->weak || path->is_supply) 11738c2ecf20Sopenharmony_ci continue; 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci if (path->walking) 11768c2ecf20Sopenharmony_ci return; 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci if (path->connect) { 11798c2ecf20Sopenharmony_ci path->walking = 1; 11808c2ecf20Sopenharmony_ci invalidate_paths_ep(path->node[dir], dir); 11818c2ecf20Sopenharmony_ci path->walking = 0; 11828c2ecf20Sopenharmony_ci } 11838c2ecf20Sopenharmony_ci } 11848c2ecf20Sopenharmony_ci} 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci/* 11878c2ecf20Sopenharmony_ci * Common implementation for is_connected_output_ep() and 11888c2ecf20Sopenharmony_ci * is_connected_input_ep(). The function is inlined since the combined size of 11898c2ecf20Sopenharmony_ci * the two specialized functions is only marginally larger then the size of the 11908c2ecf20Sopenharmony_ci * generic function and at the same time the fast path of the specialized 11918c2ecf20Sopenharmony_ci * functions is significantly smaller than the generic function. 11928c2ecf20Sopenharmony_ci */ 11938c2ecf20Sopenharmony_cistatic __always_inline int is_connected_ep(struct snd_soc_dapm_widget *widget, 11948c2ecf20Sopenharmony_ci struct list_head *list, enum snd_soc_dapm_direction dir, 11958c2ecf20Sopenharmony_ci int (*fn)(struct snd_soc_dapm_widget *, struct list_head *, 11968c2ecf20Sopenharmony_ci bool (*custom_stop_condition)(struct snd_soc_dapm_widget *, 11978c2ecf20Sopenharmony_ci enum snd_soc_dapm_direction)), 11988c2ecf20Sopenharmony_ci bool (*custom_stop_condition)(struct snd_soc_dapm_widget *, 11998c2ecf20Sopenharmony_ci enum snd_soc_dapm_direction)) 12008c2ecf20Sopenharmony_ci{ 12018c2ecf20Sopenharmony_ci enum snd_soc_dapm_direction rdir = SND_SOC_DAPM_DIR_REVERSE(dir); 12028c2ecf20Sopenharmony_ci struct snd_soc_dapm_path *path; 12038c2ecf20Sopenharmony_ci int con = 0; 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci if (widget->endpoints[dir] >= 0) 12068c2ecf20Sopenharmony_ci return widget->endpoints[dir]; 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci DAPM_UPDATE_STAT(widget, path_checks); 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci /* do we need to add this widget to the list ? */ 12118c2ecf20Sopenharmony_ci if (list) 12128c2ecf20Sopenharmony_ci list_add_tail(&widget->work_list, list); 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci if (custom_stop_condition && custom_stop_condition(widget, dir)) { 12158c2ecf20Sopenharmony_ci list = NULL; 12168c2ecf20Sopenharmony_ci custom_stop_condition = NULL; 12178c2ecf20Sopenharmony_ci } 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci if ((widget->is_ep & SND_SOC_DAPM_DIR_TO_EP(dir)) && widget->connected) { 12208c2ecf20Sopenharmony_ci widget->endpoints[dir] = snd_soc_dapm_suspend_check(widget); 12218c2ecf20Sopenharmony_ci return widget->endpoints[dir]; 12228c2ecf20Sopenharmony_ci } 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci snd_soc_dapm_widget_for_each_path(widget, rdir, path) { 12258c2ecf20Sopenharmony_ci DAPM_UPDATE_STAT(widget, neighbour_checks); 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci if (path->weak || path->is_supply) 12288c2ecf20Sopenharmony_ci continue; 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci if (path->walking) 12318c2ecf20Sopenharmony_ci return 1; 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci trace_snd_soc_dapm_path(widget, dir, path); 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci if (path->connect) { 12368c2ecf20Sopenharmony_ci path->walking = 1; 12378c2ecf20Sopenharmony_ci con += fn(path->node[dir], list, custom_stop_condition); 12388c2ecf20Sopenharmony_ci path->walking = 0; 12398c2ecf20Sopenharmony_ci } 12408c2ecf20Sopenharmony_ci } 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci widget->endpoints[dir] = con; 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci return con; 12458c2ecf20Sopenharmony_ci} 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci/* 12488c2ecf20Sopenharmony_ci * Recursively check for a completed path to an active or physically connected 12498c2ecf20Sopenharmony_ci * output widget. Returns number of complete paths. 12508c2ecf20Sopenharmony_ci * 12518c2ecf20Sopenharmony_ci * Optionally, can be supplied with a function acting as a stopping condition. 12528c2ecf20Sopenharmony_ci * This function takes the dapm widget currently being examined and the walk 12538c2ecf20Sopenharmony_ci * direction as an arguments, it should return true if widgets from that point 12548c2ecf20Sopenharmony_ci * in the graph onwards should not be added to the widget list. 12558c2ecf20Sopenharmony_ci */ 12568c2ecf20Sopenharmony_cistatic int is_connected_output_ep(struct snd_soc_dapm_widget *widget, 12578c2ecf20Sopenharmony_ci struct list_head *list, 12588c2ecf20Sopenharmony_ci bool (*custom_stop_condition)(struct snd_soc_dapm_widget *i, 12598c2ecf20Sopenharmony_ci enum snd_soc_dapm_direction)) 12608c2ecf20Sopenharmony_ci{ 12618c2ecf20Sopenharmony_ci return is_connected_ep(widget, list, SND_SOC_DAPM_DIR_OUT, 12628c2ecf20Sopenharmony_ci is_connected_output_ep, custom_stop_condition); 12638c2ecf20Sopenharmony_ci} 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci/* 12668c2ecf20Sopenharmony_ci * Recursively check for a completed path to an active or physically connected 12678c2ecf20Sopenharmony_ci * input widget. Returns number of complete paths. 12688c2ecf20Sopenharmony_ci * 12698c2ecf20Sopenharmony_ci * Optionally, can be supplied with a function acting as a stopping condition. 12708c2ecf20Sopenharmony_ci * This function takes the dapm widget currently being examined and the walk 12718c2ecf20Sopenharmony_ci * direction as an arguments, it should return true if the walk should be 12728c2ecf20Sopenharmony_ci * stopped and false otherwise. 12738c2ecf20Sopenharmony_ci */ 12748c2ecf20Sopenharmony_cistatic int is_connected_input_ep(struct snd_soc_dapm_widget *widget, 12758c2ecf20Sopenharmony_ci struct list_head *list, 12768c2ecf20Sopenharmony_ci bool (*custom_stop_condition)(struct snd_soc_dapm_widget *i, 12778c2ecf20Sopenharmony_ci enum snd_soc_dapm_direction)) 12788c2ecf20Sopenharmony_ci{ 12798c2ecf20Sopenharmony_ci return is_connected_ep(widget, list, SND_SOC_DAPM_DIR_IN, 12808c2ecf20Sopenharmony_ci is_connected_input_ep, custom_stop_condition); 12818c2ecf20Sopenharmony_ci} 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci/** 12848c2ecf20Sopenharmony_ci * snd_soc_dapm_dai_get_connected_widgets - query audio path and it's widgets. 12858c2ecf20Sopenharmony_ci * @dai: the soc DAI. 12868c2ecf20Sopenharmony_ci * @stream: stream direction. 12878c2ecf20Sopenharmony_ci * @list: list of active widgets for this stream. 12888c2ecf20Sopenharmony_ci * @custom_stop_condition: (optional) a function meant to stop the widget graph 12898c2ecf20Sopenharmony_ci * walk based on custom logic. 12908c2ecf20Sopenharmony_ci * 12918c2ecf20Sopenharmony_ci * Queries DAPM graph as to whether a valid audio stream path exists for 12928c2ecf20Sopenharmony_ci * the initial stream specified by name. This takes into account 12938c2ecf20Sopenharmony_ci * current mixer and mux kcontrol settings. Creates list of valid widgets. 12948c2ecf20Sopenharmony_ci * 12958c2ecf20Sopenharmony_ci * Optionally, can be supplied with a function acting as a stopping condition. 12968c2ecf20Sopenharmony_ci * This function takes the dapm widget currently being examined and the walk 12978c2ecf20Sopenharmony_ci * direction as an arguments, it should return true if the walk should be 12988c2ecf20Sopenharmony_ci * stopped and false otherwise. 12998c2ecf20Sopenharmony_ci * 13008c2ecf20Sopenharmony_ci * Returns the number of valid paths or negative error. 13018c2ecf20Sopenharmony_ci */ 13028c2ecf20Sopenharmony_ciint snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, 13038c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget_list **list, 13048c2ecf20Sopenharmony_ci bool (*custom_stop_condition)(struct snd_soc_dapm_widget *, 13058c2ecf20Sopenharmony_ci enum snd_soc_dapm_direction)) 13068c2ecf20Sopenharmony_ci{ 13078c2ecf20Sopenharmony_ci struct snd_soc_card *card = dai->component->card; 13088c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *w; 13098c2ecf20Sopenharmony_ci LIST_HEAD(widgets); 13108c2ecf20Sopenharmony_ci int paths; 13118c2ecf20Sopenharmony_ci int ret; 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_ci mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci if (stream == SNDRV_PCM_STREAM_PLAYBACK) { 13168c2ecf20Sopenharmony_ci w = dai->playback_widget; 13178c2ecf20Sopenharmony_ci invalidate_paths_ep(w, SND_SOC_DAPM_DIR_OUT); 13188c2ecf20Sopenharmony_ci paths = is_connected_output_ep(w, &widgets, 13198c2ecf20Sopenharmony_ci custom_stop_condition); 13208c2ecf20Sopenharmony_ci } else { 13218c2ecf20Sopenharmony_ci w = dai->capture_widget; 13228c2ecf20Sopenharmony_ci invalidate_paths_ep(w, SND_SOC_DAPM_DIR_IN); 13238c2ecf20Sopenharmony_ci paths = is_connected_input_ep(w, &widgets, 13248c2ecf20Sopenharmony_ci custom_stop_condition); 13258c2ecf20Sopenharmony_ci } 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci /* Drop starting point */ 13288c2ecf20Sopenharmony_ci list_del(widgets.next); 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ci ret = dapm_widget_list_create(list, &widgets); 13318c2ecf20Sopenharmony_ci if (ret) 13328c2ecf20Sopenharmony_ci paths = ret; 13338c2ecf20Sopenharmony_ci 13348c2ecf20Sopenharmony_ci trace_snd_soc_dapm_connected(paths, stream); 13358c2ecf20Sopenharmony_ci mutex_unlock(&card->dapm_mutex); 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_ci return paths; 13388c2ecf20Sopenharmony_ci} 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_civoid snd_soc_dapm_dai_free_widgets(struct snd_soc_dapm_widget_list **list) 13418c2ecf20Sopenharmony_ci{ 13428c2ecf20Sopenharmony_ci dapm_widget_list_free(list); 13438c2ecf20Sopenharmony_ci} 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci/* 13468c2ecf20Sopenharmony_ci * Handler for regulator supply widget. 13478c2ecf20Sopenharmony_ci */ 13488c2ecf20Sopenharmony_ciint dapm_regulator_event(struct snd_soc_dapm_widget *w, 13498c2ecf20Sopenharmony_ci struct snd_kcontrol *kcontrol, int event) 13508c2ecf20Sopenharmony_ci{ 13518c2ecf20Sopenharmony_ci int ret; 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci soc_dapm_async_complete(w->dapm); 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci if (SND_SOC_DAPM_EVENT_ON(event)) { 13568c2ecf20Sopenharmony_ci if (w->on_val & SND_SOC_DAPM_REGULATOR_BYPASS) { 13578c2ecf20Sopenharmony_ci ret = regulator_allow_bypass(w->regulator, false); 13588c2ecf20Sopenharmony_ci if (ret != 0) 13598c2ecf20Sopenharmony_ci dev_warn(w->dapm->dev, 13608c2ecf20Sopenharmony_ci "ASoC: Failed to unbypass %s: %d\n", 13618c2ecf20Sopenharmony_ci w->name, ret); 13628c2ecf20Sopenharmony_ci } 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_ci return regulator_enable(w->regulator); 13658c2ecf20Sopenharmony_ci } else { 13668c2ecf20Sopenharmony_ci if (w->on_val & SND_SOC_DAPM_REGULATOR_BYPASS) { 13678c2ecf20Sopenharmony_ci ret = regulator_allow_bypass(w->regulator, true); 13688c2ecf20Sopenharmony_ci if (ret != 0) 13698c2ecf20Sopenharmony_ci dev_warn(w->dapm->dev, 13708c2ecf20Sopenharmony_ci "ASoC: Failed to bypass %s: %d\n", 13718c2ecf20Sopenharmony_ci w->name, ret); 13728c2ecf20Sopenharmony_ci } 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci return regulator_disable_deferred(w->regulator, w->shift); 13758c2ecf20Sopenharmony_ci } 13768c2ecf20Sopenharmony_ci} 13778c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dapm_regulator_event); 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci/* 13808c2ecf20Sopenharmony_ci * Handler for pinctrl widget. 13818c2ecf20Sopenharmony_ci */ 13828c2ecf20Sopenharmony_ciint dapm_pinctrl_event(struct snd_soc_dapm_widget *w, 13838c2ecf20Sopenharmony_ci struct snd_kcontrol *kcontrol, int event) 13848c2ecf20Sopenharmony_ci{ 13858c2ecf20Sopenharmony_ci struct snd_soc_dapm_pinctrl_priv *priv = w->priv; 13868c2ecf20Sopenharmony_ci struct pinctrl *p = w->pinctrl; 13878c2ecf20Sopenharmony_ci struct pinctrl_state *s; 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ci if (!p || !priv) 13908c2ecf20Sopenharmony_ci return -EIO; 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_ci if (SND_SOC_DAPM_EVENT_ON(event)) 13938c2ecf20Sopenharmony_ci s = pinctrl_lookup_state(p, priv->active_state); 13948c2ecf20Sopenharmony_ci else 13958c2ecf20Sopenharmony_ci s = pinctrl_lookup_state(p, priv->sleep_state); 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci if (IS_ERR(s)) 13988c2ecf20Sopenharmony_ci return PTR_ERR(s); 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_ci return pinctrl_select_state(p, s); 14018c2ecf20Sopenharmony_ci} 14028c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dapm_pinctrl_event); 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_ci/* 14058c2ecf20Sopenharmony_ci * Handler for clock supply widget. 14068c2ecf20Sopenharmony_ci */ 14078c2ecf20Sopenharmony_ciint dapm_clock_event(struct snd_soc_dapm_widget *w, 14088c2ecf20Sopenharmony_ci struct snd_kcontrol *kcontrol, int event) 14098c2ecf20Sopenharmony_ci{ 14108c2ecf20Sopenharmony_ci if (!w->clk) 14118c2ecf20Sopenharmony_ci return -EIO; 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_ci soc_dapm_async_complete(w->dapm); 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci if (SND_SOC_DAPM_EVENT_ON(event)) { 14168c2ecf20Sopenharmony_ci return clk_prepare_enable(w->clk); 14178c2ecf20Sopenharmony_ci } else { 14188c2ecf20Sopenharmony_ci clk_disable_unprepare(w->clk); 14198c2ecf20Sopenharmony_ci return 0; 14208c2ecf20Sopenharmony_ci } 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci return 0; 14238c2ecf20Sopenharmony_ci} 14248c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dapm_clock_event); 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_cistatic int dapm_widget_power_check(struct snd_soc_dapm_widget *w) 14278c2ecf20Sopenharmony_ci{ 14288c2ecf20Sopenharmony_ci if (w->power_checked) 14298c2ecf20Sopenharmony_ci return w->new_power; 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci if (w->force) 14328c2ecf20Sopenharmony_ci w->new_power = 1; 14338c2ecf20Sopenharmony_ci else 14348c2ecf20Sopenharmony_ci w->new_power = w->power_check(w); 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_ci w->power_checked = true; 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_ci return w->new_power; 14398c2ecf20Sopenharmony_ci} 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ci/* Generic check to see if a widget should be powered. */ 14428c2ecf20Sopenharmony_cistatic int dapm_generic_check_power(struct snd_soc_dapm_widget *w) 14438c2ecf20Sopenharmony_ci{ 14448c2ecf20Sopenharmony_ci int in, out; 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_ci DAPM_UPDATE_STAT(w, power_checks); 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci in = is_connected_input_ep(w, NULL, NULL); 14498c2ecf20Sopenharmony_ci out = is_connected_output_ep(w, NULL, NULL); 14508c2ecf20Sopenharmony_ci return out != 0 && in != 0; 14518c2ecf20Sopenharmony_ci} 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci/* Check to see if a power supply is needed */ 14548c2ecf20Sopenharmony_cistatic int dapm_supply_check_power(struct snd_soc_dapm_widget *w) 14558c2ecf20Sopenharmony_ci{ 14568c2ecf20Sopenharmony_ci struct snd_soc_dapm_path *path; 14578c2ecf20Sopenharmony_ci 14588c2ecf20Sopenharmony_ci DAPM_UPDATE_STAT(w, power_checks); 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_ci /* Check if one of our outputs is connected */ 14618c2ecf20Sopenharmony_ci snd_soc_dapm_widget_for_each_sink_path(w, path) { 14628c2ecf20Sopenharmony_ci DAPM_UPDATE_STAT(w, neighbour_checks); 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_ci if (path->weak) 14658c2ecf20Sopenharmony_ci continue; 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci if (path->connected && 14688c2ecf20Sopenharmony_ci !path->connected(path->source, path->sink)) 14698c2ecf20Sopenharmony_ci continue; 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_ci if (dapm_widget_power_check(path->sink)) 14728c2ecf20Sopenharmony_ci return 1; 14738c2ecf20Sopenharmony_ci } 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci return 0; 14768c2ecf20Sopenharmony_ci} 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_cistatic int dapm_always_on_check_power(struct snd_soc_dapm_widget *w) 14798c2ecf20Sopenharmony_ci{ 14808c2ecf20Sopenharmony_ci return w->connected; 14818c2ecf20Sopenharmony_ci} 14828c2ecf20Sopenharmony_ci 14838c2ecf20Sopenharmony_cistatic int dapm_seq_compare(struct snd_soc_dapm_widget *a, 14848c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *b, 14858c2ecf20Sopenharmony_ci bool power_up) 14868c2ecf20Sopenharmony_ci{ 14878c2ecf20Sopenharmony_ci int *sort; 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci BUILD_BUG_ON(ARRAY_SIZE(dapm_up_seq) != SND_SOC_DAPM_TYPE_COUNT); 14908c2ecf20Sopenharmony_ci BUILD_BUG_ON(ARRAY_SIZE(dapm_down_seq) != SND_SOC_DAPM_TYPE_COUNT); 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_ci if (power_up) 14938c2ecf20Sopenharmony_ci sort = dapm_up_seq; 14948c2ecf20Sopenharmony_ci else 14958c2ecf20Sopenharmony_ci sort = dapm_down_seq; 14968c2ecf20Sopenharmony_ci 14978c2ecf20Sopenharmony_ci WARN_ONCE(sort[a->id] == 0, "offset a->id %d not initialized\n", a->id); 14988c2ecf20Sopenharmony_ci WARN_ONCE(sort[b->id] == 0, "offset b->id %d not initialized\n", b->id); 14998c2ecf20Sopenharmony_ci 15008c2ecf20Sopenharmony_ci if (sort[a->id] != sort[b->id]) 15018c2ecf20Sopenharmony_ci return sort[a->id] - sort[b->id]; 15028c2ecf20Sopenharmony_ci if (a->subseq != b->subseq) { 15038c2ecf20Sopenharmony_ci if (power_up) 15048c2ecf20Sopenharmony_ci return a->subseq - b->subseq; 15058c2ecf20Sopenharmony_ci else 15068c2ecf20Sopenharmony_ci return b->subseq - a->subseq; 15078c2ecf20Sopenharmony_ci } 15088c2ecf20Sopenharmony_ci if (a->reg != b->reg) 15098c2ecf20Sopenharmony_ci return a->reg - b->reg; 15108c2ecf20Sopenharmony_ci if (a->dapm != b->dapm) 15118c2ecf20Sopenharmony_ci return (unsigned long)a->dapm - (unsigned long)b->dapm; 15128c2ecf20Sopenharmony_ci 15138c2ecf20Sopenharmony_ci return 0; 15148c2ecf20Sopenharmony_ci} 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci/* Insert a widget in order into a DAPM power sequence. */ 15178c2ecf20Sopenharmony_cistatic void dapm_seq_insert(struct snd_soc_dapm_widget *new_widget, 15188c2ecf20Sopenharmony_ci struct list_head *list, 15198c2ecf20Sopenharmony_ci bool power_up) 15208c2ecf20Sopenharmony_ci{ 15218c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *w; 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_ci list_for_each_entry(w, list, power_list) 15248c2ecf20Sopenharmony_ci if (dapm_seq_compare(new_widget, w, power_up) < 0) { 15258c2ecf20Sopenharmony_ci list_add_tail(&new_widget->power_list, &w->power_list); 15268c2ecf20Sopenharmony_ci return; 15278c2ecf20Sopenharmony_ci } 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_ci list_add_tail(&new_widget->power_list, list); 15308c2ecf20Sopenharmony_ci} 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_cistatic void dapm_seq_check_event(struct snd_soc_card *card, 15338c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *w, int event) 15348c2ecf20Sopenharmony_ci{ 15358c2ecf20Sopenharmony_ci const char *ev_name; 15368c2ecf20Sopenharmony_ci int power, ret; 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci switch (event) { 15398c2ecf20Sopenharmony_ci case SND_SOC_DAPM_PRE_PMU: 15408c2ecf20Sopenharmony_ci ev_name = "PRE_PMU"; 15418c2ecf20Sopenharmony_ci power = 1; 15428c2ecf20Sopenharmony_ci break; 15438c2ecf20Sopenharmony_ci case SND_SOC_DAPM_POST_PMU: 15448c2ecf20Sopenharmony_ci ev_name = "POST_PMU"; 15458c2ecf20Sopenharmony_ci power = 1; 15468c2ecf20Sopenharmony_ci break; 15478c2ecf20Sopenharmony_ci case SND_SOC_DAPM_PRE_PMD: 15488c2ecf20Sopenharmony_ci ev_name = "PRE_PMD"; 15498c2ecf20Sopenharmony_ci power = 0; 15508c2ecf20Sopenharmony_ci break; 15518c2ecf20Sopenharmony_ci case SND_SOC_DAPM_POST_PMD: 15528c2ecf20Sopenharmony_ci ev_name = "POST_PMD"; 15538c2ecf20Sopenharmony_ci power = 0; 15548c2ecf20Sopenharmony_ci break; 15558c2ecf20Sopenharmony_ci case SND_SOC_DAPM_WILL_PMU: 15568c2ecf20Sopenharmony_ci ev_name = "WILL_PMU"; 15578c2ecf20Sopenharmony_ci power = 1; 15588c2ecf20Sopenharmony_ci break; 15598c2ecf20Sopenharmony_ci case SND_SOC_DAPM_WILL_PMD: 15608c2ecf20Sopenharmony_ci ev_name = "WILL_PMD"; 15618c2ecf20Sopenharmony_ci power = 0; 15628c2ecf20Sopenharmony_ci break; 15638c2ecf20Sopenharmony_ci default: 15648c2ecf20Sopenharmony_ci WARN(1, "Unknown event %d\n", event); 15658c2ecf20Sopenharmony_ci return; 15668c2ecf20Sopenharmony_ci } 15678c2ecf20Sopenharmony_ci 15688c2ecf20Sopenharmony_ci if (w->new_power != power) 15698c2ecf20Sopenharmony_ci return; 15708c2ecf20Sopenharmony_ci 15718c2ecf20Sopenharmony_ci if (w->event && (w->event_flags & event)) { 15728c2ecf20Sopenharmony_ci pop_dbg(w->dapm->dev, card->pop_time, "pop test : %s %s\n", 15738c2ecf20Sopenharmony_ci w->name, ev_name); 15748c2ecf20Sopenharmony_ci soc_dapm_async_complete(w->dapm); 15758c2ecf20Sopenharmony_ci trace_snd_soc_dapm_widget_event_start(w, event); 15768c2ecf20Sopenharmony_ci ret = w->event(w, NULL, event); 15778c2ecf20Sopenharmony_ci trace_snd_soc_dapm_widget_event_done(w, event); 15788c2ecf20Sopenharmony_ci if (ret < 0) 15798c2ecf20Sopenharmony_ci dev_err(w->dapm->dev, "ASoC: %s: %s event failed: %d\n", 15808c2ecf20Sopenharmony_ci ev_name, w->name, ret); 15818c2ecf20Sopenharmony_ci } 15828c2ecf20Sopenharmony_ci} 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci/* Apply the coalesced changes from a DAPM sequence */ 15858c2ecf20Sopenharmony_cistatic void dapm_seq_run_coalesced(struct snd_soc_card *card, 15868c2ecf20Sopenharmony_ci struct list_head *pending) 15878c2ecf20Sopenharmony_ci{ 15888c2ecf20Sopenharmony_ci struct snd_soc_dapm_context *dapm; 15898c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *w; 15908c2ecf20Sopenharmony_ci int reg; 15918c2ecf20Sopenharmony_ci unsigned int value = 0; 15928c2ecf20Sopenharmony_ci unsigned int mask = 0; 15938c2ecf20Sopenharmony_ci 15948c2ecf20Sopenharmony_ci w = list_first_entry(pending, struct snd_soc_dapm_widget, power_list); 15958c2ecf20Sopenharmony_ci reg = w->reg; 15968c2ecf20Sopenharmony_ci dapm = w->dapm; 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_ci list_for_each_entry(w, pending, power_list) { 15998c2ecf20Sopenharmony_ci WARN_ON(reg != w->reg || dapm != w->dapm); 16008c2ecf20Sopenharmony_ci w->power = w->new_power; 16018c2ecf20Sopenharmony_ci 16028c2ecf20Sopenharmony_ci mask |= w->mask << w->shift; 16038c2ecf20Sopenharmony_ci if (w->power) 16048c2ecf20Sopenharmony_ci value |= w->on_val << w->shift; 16058c2ecf20Sopenharmony_ci else 16068c2ecf20Sopenharmony_ci value |= w->off_val << w->shift; 16078c2ecf20Sopenharmony_ci 16088c2ecf20Sopenharmony_ci pop_dbg(dapm->dev, card->pop_time, 16098c2ecf20Sopenharmony_ci "pop test : Queue %s: reg=0x%x, 0x%x/0x%x\n", 16108c2ecf20Sopenharmony_ci w->name, reg, value, mask); 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci /* Check for events */ 16138c2ecf20Sopenharmony_ci dapm_seq_check_event(card, w, SND_SOC_DAPM_PRE_PMU); 16148c2ecf20Sopenharmony_ci dapm_seq_check_event(card, w, SND_SOC_DAPM_PRE_PMD); 16158c2ecf20Sopenharmony_ci } 16168c2ecf20Sopenharmony_ci 16178c2ecf20Sopenharmony_ci if (reg >= 0) { 16188c2ecf20Sopenharmony_ci /* Any widget will do, they should all be updating the 16198c2ecf20Sopenharmony_ci * same register. 16208c2ecf20Sopenharmony_ci */ 16218c2ecf20Sopenharmony_ci 16228c2ecf20Sopenharmony_ci pop_dbg(dapm->dev, card->pop_time, 16238c2ecf20Sopenharmony_ci "pop test : Applying 0x%x/0x%x to %x in %dms\n", 16248c2ecf20Sopenharmony_ci value, mask, reg, card->pop_time); 16258c2ecf20Sopenharmony_ci pop_wait(card->pop_time); 16268c2ecf20Sopenharmony_ci soc_dapm_update_bits(dapm, reg, mask, value); 16278c2ecf20Sopenharmony_ci } 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci list_for_each_entry(w, pending, power_list) { 16308c2ecf20Sopenharmony_ci dapm_seq_check_event(card, w, SND_SOC_DAPM_POST_PMU); 16318c2ecf20Sopenharmony_ci dapm_seq_check_event(card, w, SND_SOC_DAPM_POST_PMD); 16328c2ecf20Sopenharmony_ci } 16338c2ecf20Sopenharmony_ci} 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_ci/* Apply a DAPM power sequence. 16368c2ecf20Sopenharmony_ci * 16378c2ecf20Sopenharmony_ci * We walk over a pre-sorted list of widgets to apply power to. In 16388c2ecf20Sopenharmony_ci * order to minimise the number of writes to the device required 16398c2ecf20Sopenharmony_ci * multiple widgets will be updated in a single write where possible. 16408c2ecf20Sopenharmony_ci * Currently anything that requires more than a single write is not 16418c2ecf20Sopenharmony_ci * handled. 16428c2ecf20Sopenharmony_ci */ 16438c2ecf20Sopenharmony_cistatic void dapm_seq_run(struct snd_soc_card *card, 16448c2ecf20Sopenharmony_ci struct list_head *list, int event, bool power_up) 16458c2ecf20Sopenharmony_ci{ 16468c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *w, *n; 16478c2ecf20Sopenharmony_ci struct snd_soc_dapm_context *d; 16488c2ecf20Sopenharmony_ci LIST_HEAD(pending); 16498c2ecf20Sopenharmony_ci int cur_sort = -1; 16508c2ecf20Sopenharmony_ci int cur_subseq = -1; 16518c2ecf20Sopenharmony_ci int cur_reg = SND_SOC_NOPM; 16528c2ecf20Sopenharmony_ci struct snd_soc_dapm_context *cur_dapm = NULL; 16538c2ecf20Sopenharmony_ci int ret, i; 16548c2ecf20Sopenharmony_ci int *sort; 16558c2ecf20Sopenharmony_ci 16568c2ecf20Sopenharmony_ci if (power_up) 16578c2ecf20Sopenharmony_ci sort = dapm_up_seq; 16588c2ecf20Sopenharmony_ci else 16598c2ecf20Sopenharmony_ci sort = dapm_down_seq; 16608c2ecf20Sopenharmony_ci 16618c2ecf20Sopenharmony_ci list_for_each_entry_safe(w, n, list, power_list) { 16628c2ecf20Sopenharmony_ci ret = 0; 16638c2ecf20Sopenharmony_ci 16648c2ecf20Sopenharmony_ci /* Do we need to apply any queued changes? */ 16658c2ecf20Sopenharmony_ci if (sort[w->id] != cur_sort || w->reg != cur_reg || 16668c2ecf20Sopenharmony_ci w->dapm != cur_dapm || w->subseq != cur_subseq) { 16678c2ecf20Sopenharmony_ci if (!list_empty(&pending)) 16688c2ecf20Sopenharmony_ci dapm_seq_run_coalesced(card, &pending); 16698c2ecf20Sopenharmony_ci 16708c2ecf20Sopenharmony_ci if (cur_dapm && cur_dapm->component) { 16718c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++) 16728c2ecf20Sopenharmony_ci if (sort[i] == cur_sort) 16738c2ecf20Sopenharmony_ci snd_soc_component_seq_notifier( 16748c2ecf20Sopenharmony_ci cur_dapm->component, 16758c2ecf20Sopenharmony_ci i, cur_subseq); 16768c2ecf20Sopenharmony_ci } 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_ci if (cur_dapm && w->dapm != cur_dapm) 16798c2ecf20Sopenharmony_ci soc_dapm_async_complete(cur_dapm); 16808c2ecf20Sopenharmony_ci 16818c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&pending); 16828c2ecf20Sopenharmony_ci cur_sort = -1; 16838c2ecf20Sopenharmony_ci cur_subseq = INT_MIN; 16848c2ecf20Sopenharmony_ci cur_reg = SND_SOC_NOPM; 16858c2ecf20Sopenharmony_ci cur_dapm = NULL; 16868c2ecf20Sopenharmony_ci } 16878c2ecf20Sopenharmony_ci 16888c2ecf20Sopenharmony_ci switch (w->id) { 16898c2ecf20Sopenharmony_ci case snd_soc_dapm_pre: 16908c2ecf20Sopenharmony_ci if (!w->event) 16918c2ecf20Sopenharmony_ci continue; 16928c2ecf20Sopenharmony_ci 16938c2ecf20Sopenharmony_ci if (event == SND_SOC_DAPM_STREAM_START) 16948c2ecf20Sopenharmony_ci ret = w->event(w, 16958c2ecf20Sopenharmony_ci NULL, SND_SOC_DAPM_PRE_PMU); 16968c2ecf20Sopenharmony_ci else if (event == SND_SOC_DAPM_STREAM_STOP) 16978c2ecf20Sopenharmony_ci ret = w->event(w, 16988c2ecf20Sopenharmony_ci NULL, SND_SOC_DAPM_PRE_PMD); 16998c2ecf20Sopenharmony_ci break; 17008c2ecf20Sopenharmony_ci 17018c2ecf20Sopenharmony_ci case snd_soc_dapm_post: 17028c2ecf20Sopenharmony_ci if (!w->event) 17038c2ecf20Sopenharmony_ci continue; 17048c2ecf20Sopenharmony_ci 17058c2ecf20Sopenharmony_ci if (event == SND_SOC_DAPM_STREAM_START) 17068c2ecf20Sopenharmony_ci ret = w->event(w, 17078c2ecf20Sopenharmony_ci NULL, SND_SOC_DAPM_POST_PMU); 17088c2ecf20Sopenharmony_ci else if (event == SND_SOC_DAPM_STREAM_STOP) 17098c2ecf20Sopenharmony_ci ret = w->event(w, 17108c2ecf20Sopenharmony_ci NULL, SND_SOC_DAPM_POST_PMD); 17118c2ecf20Sopenharmony_ci break; 17128c2ecf20Sopenharmony_ci 17138c2ecf20Sopenharmony_ci default: 17148c2ecf20Sopenharmony_ci /* Queue it up for application */ 17158c2ecf20Sopenharmony_ci cur_sort = sort[w->id]; 17168c2ecf20Sopenharmony_ci cur_subseq = w->subseq; 17178c2ecf20Sopenharmony_ci cur_reg = w->reg; 17188c2ecf20Sopenharmony_ci cur_dapm = w->dapm; 17198c2ecf20Sopenharmony_ci list_move(&w->power_list, &pending); 17208c2ecf20Sopenharmony_ci break; 17218c2ecf20Sopenharmony_ci } 17228c2ecf20Sopenharmony_ci 17238c2ecf20Sopenharmony_ci if (ret < 0) 17248c2ecf20Sopenharmony_ci dev_err(w->dapm->dev, 17258c2ecf20Sopenharmony_ci "ASoC: Failed to apply widget power: %d\n", ret); 17268c2ecf20Sopenharmony_ci } 17278c2ecf20Sopenharmony_ci 17288c2ecf20Sopenharmony_ci if (!list_empty(&pending)) 17298c2ecf20Sopenharmony_ci dapm_seq_run_coalesced(card, &pending); 17308c2ecf20Sopenharmony_ci 17318c2ecf20Sopenharmony_ci if (cur_dapm && cur_dapm->component) { 17328c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++) 17338c2ecf20Sopenharmony_ci if (sort[i] == cur_sort) 17348c2ecf20Sopenharmony_ci snd_soc_component_seq_notifier( 17358c2ecf20Sopenharmony_ci cur_dapm->component, 17368c2ecf20Sopenharmony_ci i, cur_subseq); 17378c2ecf20Sopenharmony_ci } 17388c2ecf20Sopenharmony_ci 17398c2ecf20Sopenharmony_ci for_each_card_dapms(card, d) 17408c2ecf20Sopenharmony_ci soc_dapm_async_complete(d); 17418c2ecf20Sopenharmony_ci} 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_cistatic void dapm_widget_update(struct snd_soc_card *card) 17448c2ecf20Sopenharmony_ci{ 17458c2ecf20Sopenharmony_ci struct snd_soc_dapm_update *update = card->update; 17468c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget_list *wlist; 17478c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *w = NULL; 17488c2ecf20Sopenharmony_ci unsigned int wi; 17498c2ecf20Sopenharmony_ci int ret; 17508c2ecf20Sopenharmony_ci 17518c2ecf20Sopenharmony_ci if (!update || !dapm_kcontrol_is_powered(update->kcontrol)) 17528c2ecf20Sopenharmony_ci return; 17538c2ecf20Sopenharmony_ci 17548c2ecf20Sopenharmony_ci wlist = dapm_kcontrol_get_wlist(update->kcontrol); 17558c2ecf20Sopenharmony_ci 17568c2ecf20Sopenharmony_ci for_each_dapm_widgets(wlist, wi, w) { 17578c2ecf20Sopenharmony_ci if (w->event && (w->event_flags & SND_SOC_DAPM_PRE_REG)) { 17588c2ecf20Sopenharmony_ci ret = w->event(w, update->kcontrol, SND_SOC_DAPM_PRE_REG); 17598c2ecf20Sopenharmony_ci if (ret != 0) 17608c2ecf20Sopenharmony_ci dev_err(w->dapm->dev, "ASoC: %s DAPM pre-event failed: %d\n", 17618c2ecf20Sopenharmony_ci w->name, ret); 17628c2ecf20Sopenharmony_ci } 17638c2ecf20Sopenharmony_ci } 17648c2ecf20Sopenharmony_ci 17658c2ecf20Sopenharmony_ci if (!w) 17668c2ecf20Sopenharmony_ci return; 17678c2ecf20Sopenharmony_ci 17688c2ecf20Sopenharmony_ci ret = soc_dapm_update_bits(w->dapm, update->reg, update->mask, 17698c2ecf20Sopenharmony_ci update->val); 17708c2ecf20Sopenharmony_ci if (ret < 0) 17718c2ecf20Sopenharmony_ci dev_err(w->dapm->dev, "ASoC: %s DAPM update failed: %d\n", 17728c2ecf20Sopenharmony_ci w->name, ret); 17738c2ecf20Sopenharmony_ci 17748c2ecf20Sopenharmony_ci if (update->has_second_set) { 17758c2ecf20Sopenharmony_ci ret = soc_dapm_update_bits(w->dapm, update->reg2, 17768c2ecf20Sopenharmony_ci update->mask2, update->val2); 17778c2ecf20Sopenharmony_ci if (ret < 0) 17788c2ecf20Sopenharmony_ci dev_err(w->dapm->dev, 17798c2ecf20Sopenharmony_ci "ASoC: %s DAPM update failed: %d\n", 17808c2ecf20Sopenharmony_ci w->name, ret); 17818c2ecf20Sopenharmony_ci } 17828c2ecf20Sopenharmony_ci 17838c2ecf20Sopenharmony_ci for_each_dapm_widgets(wlist, wi, w) { 17848c2ecf20Sopenharmony_ci if (w->event && (w->event_flags & SND_SOC_DAPM_POST_REG)) { 17858c2ecf20Sopenharmony_ci ret = w->event(w, update->kcontrol, SND_SOC_DAPM_POST_REG); 17868c2ecf20Sopenharmony_ci if (ret != 0) 17878c2ecf20Sopenharmony_ci dev_err(w->dapm->dev, "ASoC: %s DAPM post-event failed: %d\n", 17888c2ecf20Sopenharmony_ci w->name, ret); 17898c2ecf20Sopenharmony_ci } 17908c2ecf20Sopenharmony_ci } 17918c2ecf20Sopenharmony_ci} 17928c2ecf20Sopenharmony_ci 17938c2ecf20Sopenharmony_ci/* Async callback run prior to DAPM sequences - brings to _PREPARE if 17948c2ecf20Sopenharmony_ci * they're changing state. 17958c2ecf20Sopenharmony_ci */ 17968c2ecf20Sopenharmony_cistatic void dapm_pre_sequence_async(void *data, async_cookie_t cookie) 17978c2ecf20Sopenharmony_ci{ 17988c2ecf20Sopenharmony_ci struct snd_soc_dapm_context *d = data; 17998c2ecf20Sopenharmony_ci int ret; 18008c2ecf20Sopenharmony_ci 18018c2ecf20Sopenharmony_ci /* If we're off and we're not supposed to go into STANDBY */ 18028c2ecf20Sopenharmony_ci if (d->bias_level == SND_SOC_BIAS_OFF && 18038c2ecf20Sopenharmony_ci d->target_bias_level != SND_SOC_BIAS_OFF) { 18048c2ecf20Sopenharmony_ci if (d->dev && cookie) 18058c2ecf20Sopenharmony_ci pm_runtime_get_sync(d->dev); 18068c2ecf20Sopenharmony_ci 18078c2ecf20Sopenharmony_ci ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_STANDBY); 18088c2ecf20Sopenharmony_ci if (ret != 0) 18098c2ecf20Sopenharmony_ci dev_err(d->dev, 18108c2ecf20Sopenharmony_ci "ASoC: Failed to turn on bias: %d\n", ret); 18118c2ecf20Sopenharmony_ci } 18128c2ecf20Sopenharmony_ci 18138c2ecf20Sopenharmony_ci /* Prepare for a transition to ON or away from ON */ 18148c2ecf20Sopenharmony_ci if ((d->target_bias_level == SND_SOC_BIAS_ON && 18158c2ecf20Sopenharmony_ci d->bias_level != SND_SOC_BIAS_ON) || 18168c2ecf20Sopenharmony_ci (d->target_bias_level != SND_SOC_BIAS_ON && 18178c2ecf20Sopenharmony_ci d->bias_level == SND_SOC_BIAS_ON)) { 18188c2ecf20Sopenharmony_ci ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_PREPARE); 18198c2ecf20Sopenharmony_ci if (ret != 0) 18208c2ecf20Sopenharmony_ci dev_err(d->dev, 18218c2ecf20Sopenharmony_ci "ASoC: Failed to prepare bias: %d\n", ret); 18228c2ecf20Sopenharmony_ci } 18238c2ecf20Sopenharmony_ci} 18248c2ecf20Sopenharmony_ci 18258c2ecf20Sopenharmony_ci/* Async callback run prior to DAPM sequences - brings to their final 18268c2ecf20Sopenharmony_ci * state. 18278c2ecf20Sopenharmony_ci */ 18288c2ecf20Sopenharmony_cistatic void dapm_post_sequence_async(void *data, async_cookie_t cookie) 18298c2ecf20Sopenharmony_ci{ 18308c2ecf20Sopenharmony_ci struct snd_soc_dapm_context *d = data; 18318c2ecf20Sopenharmony_ci int ret; 18328c2ecf20Sopenharmony_ci 18338c2ecf20Sopenharmony_ci /* If we just powered the last thing off drop to standby bias */ 18348c2ecf20Sopenharmony_ci if (d->bias_level == SND_SOC_BIAS_PREPARE && 18358c2ecf20Sopenharmony_ci (d->target_bias_level == SND_SOC_BIAS_STANDBY || 18368c2ecf20Sopenharmony_ci d->target_bias_level == SND_SOC_BIAS_OFF)) { 18378c2ecf20Sopenharmony_ci ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_STANDBY); 18388c2ecf20Sopenharmony_ci if (ret != 0) 18398c2ecf20Sopenharmony_ci dev_err(d->dev, "ASoC: Failed to apply standby bias: %d\n", 18408c2ecf20Sopenharmony_ci ret); 18418c2ecf20Sopenharmony_ci } 18428c2ecf20Sopenharmony_ci 18438c2ecf20Sopenharmony_ci /* If we're in standby and can support bias off then do that */ 18448c2ecf20Sopenharmony_ci if (d->bias_level == SND_SOC_BIAS_STANDBY && 18458c2ecf20Sopenharmony_ci d->target_bias_level == SND_SOC_BIAS_OFF) { 18468c2ecf20Sopenharmony_ci ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_OFF); 18478c2ecf20Sopenharmony_ci if (ret != 0) 18488c2ecf20Sopenharmony_ci dev_err(d->dev, "ASoC: Failed to turn off bias: %d\n", 18498c2ecf20Sopenharmony_ci ret); 18508c2ecf20Sopenharmony_ci 18518c2ecf20Sopenharmony_ci if (d->dev && cookie) 18528c2ecf20Sopenharmony_ci pm_runtime_put(d->dev); 18538c2ecf20Sopenharmony_ci } 18548c2ecf20Sopenharmony_ci 18558c2ecf20Sopenharmony_ci /* If we just powered up then move to active bias */ 18568c2ecf20Sopenharmony_ci if (d->bias_level == SND_SOC_BIAS_PREPARE && 18578c2ecf20Sopenharmony_ci d->target_bias_level == SND_SOC_BIAS_ON) { 18588c2ecf20Sopenharmony_ci ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_ON); 18598c2ecf20Sopenharmony_ci if (ret != 0) 18608c2ecf20Sopenharmony_ci dev_err(d->dev, "ASoC: Failed to apply active bias: %d\n", 18618c2ecf20Sopenharmony_ci ret); 18628c2ecf20Sopenharmony_ci } 18638c2ecf20Sopenharmony_ci} 18648c2ecf20Sopenharmony_ci 18658c2ecf20Sopenharmony_cistatic void dapm_widget_set_peer_power(struct snd_soc_dapm_widget *peer, 18668c2ecf20Sopenharmony_ci bool power, bool connect) 18678c2ecf20Sopenharmony_ci{ 18688c2ecf20Sopenharmony_ci /* If a connection is being made or broken then that update 18698c2ecf20Sopenharmony_ci * will have marked the peer dirty, otherwise the widgets are 18708c2ecf20Sopenharmony_ci * not connected and this update has no impact. */ 18718c2ecf20Sopenharmony_ci if (!connect) 18728c2ecf20Sopenharmony_ci return; 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_ci /* If the peer is already in the state we're moving to then we 18758c2ecf20Sopenharmony_ci * won't have an impact on it. */ 18768c2ecf20Sopenharmony_ci if (power != peer->power) 18778c2ecf20Sopenharmony_ci dapm_mark_dirty(peer, "peer state change"); 18788c2ecf20Sopenharmony_ci} 18798c2ecf20Sopenharmony_ci 18808c2ecf20Sopenharmony_cistatic void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power, 18818c2ecf20Sopenharmony_ci struct list_head *up_list, 18828c2ecf20Sopenharmony_ci struct list_head *down_list) 18838c2ecf20Sopenharmony_ci{ 18848c2ecf20Sopenharmony_ci struct snd_soc_dapm_path *path; 18858c2ecf20Sopenharmony_ci 18868c2ecf20Sopenharmony_ci if (w->power == power) 18878c2ecf20Sopenharmony_ci return; 18888c2ecf20Sopenharmony_ci 18898c2ecf20Sopenharmony_ci trace_snd_soc_dapm_widget_power(w, power); 18908c2ecf20Sopenharmony_ci 18918c2ecf20Sopenharmony_ci /* If we changed our power state perhaps our neigbours changed 18928c2ecf20Sopenharmony_ci * also. 18938c2ecf20Sopenharmony_ci */ 18948c2ecf20Sopenharmony_ci snd_soc_dapm_widget_for_each_source_path(w, path) 18958c2ecf20Sopenharmony_ci dapm_widget_set_peer_power(path->source, power, path->connect); 18968c2ecf20Sopenharmony_ci 18978c2ecf20Sopenharmony_ci /* Supplies can't affect their outputs, only their inputs */ 18988c2ecf20Sopenharmony_ci if (!w->is_supply) { 18998c2ecf20Sopenharmony_ci snd_soc_dapm_widget_for_each_sink_path(w, path) 19008c2ecf20Sopenharmony_ci dapm_widget_set_peer_power(path->sink, power, 19018c2ecf20Sopenharmony_ci path->connect); 19028c2ecf20Sopenharmony_ci } 19038c2ecf20Sopenharmony_ci 19048c2ecf20Sopenharmony_ci if (power) 19058c2ecf20Sopenharmony_ci dapm_seq_insert(w, up_list, true); 19068c2ecf20Sopenharmony_ci else 19078c2ecf20Sopenharmony_ci dapm_seq_insert(w, down_list, false); 19088c2ecf20Sopenharmony_ci} 19098c2ecf20Sopenharmony_ci 19108c2ecf20Sopenharmony_cistatic void dapm_power_one_widget(struct snd_soc_dapm_widget *w, 19118c2ecf20Sopenharmony_ci struct list_head *up_list, 19128c2ecf20Sopenharmony_ci struct list_head *down_list) 19138c2ecf20Sopenharmony_ci{ 19148c2ecf20Sopenharmony_ci int power; 19158c2ecf20Sopenharmony_ci 19168c2ecf20Sopenharmony_ci switch (w->id) { 19178c2ecf20Sopenharmony_ci case snd_soc_dapm_pre: 19188c2ecf20Sopenharmony_ci dapm_seq_insert(w, down_list, false); 19198c2ecf20Sopenharmony_ci break; 19208c2ecf20Sopenharmony_ci case snd_soc_dapm_post: 19218c2ecf20Sopenharmony_ci dapm_seq_insert(w, up_list, true); 19228c2ecf20Sopenharmony_ci break; 19238c2ecf20Sopenharmony_ci 19248c2ecf20Sopenharmony_ci default: 19258c2ecf20Sopenharmony_ci power = dapm_widget_power_check(w); 19268c2ecf20Sopenharmony_ci 19278c2ecf20Sopenharmony_ci dapm_widget_set_power(w, power, up_list, down_list); 19288c2ecf20Sopenharmony_ci break; 19298c2ecf20Sopenharmony_ci } 19308c2ecf20Sopenharmony_ci} 19318c2ecf20Sopenharmony_ci 19328c2ecf20Sopenharmony_cistatic bool dapm_idle_bias_off(struct snd_soc_dapm_context *dapm) 19338c2ecf20Sopenharmony_ci{ 19348c2ecf20Sopenharmony_ci if (dapm->idle_bias_off) 19358c2ecf20Sopenharmony_ci return true; 19368c2ecf20Sopenharmony_ci 19378c2ecf20Sopenharmony_ci switch (snd_power_get_state(dapm->card->snd_card)) { 19388c2ecf20Sopenharmony_ci case SNDRV_CTL_POWER_D3hot: 19398c2ecf20Sopenharmony_ci case SNDRV_CTL_POWER_D3cold: 19408c2ecf20Sopenharmony_ci return dapm->suspend_bias_off; 19418c2ecf20Sopenharmony_ci default: 19428c2ecf20Sopenharmony_ci break; 19438c2ecf20Sopenharmony_ci } 19448c2ecf20Sopenharmony_ci 19458c2ecf20Sopenharmony_ci return false; 19468c2ecf20Sopenharmony_ci} 19478c2ecf20Sopenharmony_ci 19488c2ecf20Sopenharmony_ci/* 19498c2ecf20Sopenharmony_ci * Scan each dapm widget for complete audio path. 19508c2ecf20Sopenharmony_ci * A complete path is a route that has valid endpoints i.e.:- 19518c2ecf20Sopenharmony_ci * 19528c2ecf20Sopenharmony_ci * o DAC to output pin. 19538c2ecf20Sopenharmony_ci * o Input pin to ADC. 19548c2ecf20Sopenharmony_ci * o Input pin to Output pin (bypass, sidetone) 19558c2ecf20Sopenharmony_ci * o DAC to ADC (loopback). 19568c2ecf20Sopenharmony_ci */ 19578c2ecf20Sopenharmony_cistatic int dapm_power_widgets(struct snd_soc_card *card, int event) 19588c2ecf20Sopenharmony_ci{ 19598c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *w; 19608c2ecf20Sopenharmony_ci struct snd_soc_dapm_context *d; 19618c2ecf20Sopenharmony_ci LIST_HEAD(up_list); 19628c2ecf20Sopenharmony_ci LIST_HEAD(down_list); 19638c2ecf20Sopenharmony_ci ASYNC_DOMAIN_EXCLUSIVE(async_domain); 19648c2ecf20Sopenharmony_ci enum snd_soc_bias_level bias; 19658c2ecf20Sopenharmony_ci int ret; 19668c2ecf20Sopenharmony_ci 19678c2ecf20Sopenharmony_ci lockdep_assert_held(&card->dapm_mutex); 19688c2ecf20Sopenharmony_ci 19698c2ecf20Sopenharmony_ci trace_snd_soc_dapm_start(card); 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_ci for_each_card_dapms(card, d) { 19728c2ecf20Sopenharmony_ci if (dapm_idle_bias_off(d)) 19738c2ecf20Sopenharmony_ci d->target_bias_level = SND_SOC_BIAS_OFF; 19748c2ecf20Sopenharmony_ci else 19758c2ecf20Sopenharmony_ci d->target_bias_level = SND_SOC_BIAS_STANDBY; 19768c2ecf20Sopenharmony_ci } 19778c2ecf20Sopenharmony_ci 19788c2ecf20Sopenharmony_ci dapm_reset(card); 19798c2ecf20Sopenharmony_ci 19808c2ecf20Sopenharmony_ci /* Check which widgets we need to power and store them in 19818c2ecf20Sopenharmony_ci * lists indicating if they should be powered up or down. We 19828c2ecf20Sopenharmony_ci * only check widgets that have been flagged as dirty but note 19838c2ecf20Sopenharmony_ci * that new widgets may be added to the dirty list while we 19848c2ecf20Sopenharmony_ci * iterate. 19858c2ecf20Sopenharmony_ci */ 19868c2ecf20Sopenharmony_ci list_for_each_entry(w, &card->dapm_dirty, dirty) { 19878c2ecf20Sopenharmony_ci dapm_power_one_widget(w, &up_list, &down_list); 19888c2ecf20Sopenharmony_ci } 19898c2ecf20Sopenharmony_ci 19908c2ecf20Sopenharmony_ci for_each_card_widgets(card, w) { 19918c2ecf20Sopenharmony_ci switch (w->id) { 19928c2ecf20Sopenharmony_ci case snd_soc_dapm_pre: 19938c2ecf20Sopenharmony_ci case snd_soc_dapm_post: 19948c2ecf20Sopenharmony_ci /* These widgets always need to be powered */ 19958c2ecf20Sopenharmony_ci break; 19968c2ecf20Sopenharmony_ci default: 19978c2ecf20Sopenharmony_ci list_del_init(&w->dirty); 19988c2ecf20Sopenharmony_ci break; 19998c2ecf20Sopenharmony_ci } 20008c2ecf20Sopenharmony_ci 20018c2ecf20Sopenharmony_ci if (w->new_power) { 20028c2ecf20Sopenharmony_ci d = w->dapm; 20038c2ecf20Sopenharmony_ci 20048c2ecf20Sopenharmony_ci /* Supplies and micbiases only bring the 20058c2ecf20Sopenharmony_ci * context up to STANDBY as unless something 20068c2ecf20Sopenharmony_ci * else is active and passing audio they 20078c2ecf20Sopenharmony_ci * generally don't require full power. Signal 20088c2ecf20Sopenharmony_ci * generators are virtual pins and have no 20098c2ecf20Sopenharmony_ci * power impact themselves. 20108c2ecf20Sopenharmony_ci */ 20118c2ecf20Sopenharmony_ci switch (w->id) { 20128c2ecf20Sopenharmony_ci case snd_soc_dapm_siggen: 20138c2ecf20Sopenharmony_ci case snd_soc_dapm_vmid: 20148c2ecf20Sopenharmony_ci break; 20158c2ecf20Sopenharmony_ci case snd_soc_dapm_supply: 20168c2ecf20Sopenharmony_ci case snd_soc_dapm_regulator_supply: 20178c2ecf20Sopenharmony_ci case snd_soc_dapm_pinctrl: 20188c2ecf20Sopenharmony_ci case snd_soc_dapm_clock_supply: 20198c2ecf20Sopenharmony_ci case snd_soc_dapm_micbias: 20208c2ecf20Sopenharmony_ci if (d->target_bias_level < SND_SOC_BIAS_STANDBY) 20218c2ecf20Sopenharmony_ci d->target_bias_level = SND_SOC_BIAS_STANDBY; 20228c2ecf20Sopenharmony_ci break; 20238c2ecf20Sopenharmony_ci default: 20248c2ecf20Sopenharmony_ci d->target_bias_level = SND_SOC_BIAS_ON; 20258c2ecf20Sopenharmony_ci break; 20268c2ecf20Sopenharmony_ci } 20278c2ecf20Sopenharmony_ci } 20288c2ecf20Sopenharmony_ci 20298c2ecf20Sopenharmony_ci } 20308c2ecf20Sopenharmony_ci 20318c2ecf20Sopenharmony_ci /* Force all contexts in the card to the same bias state if 20328c2ecf20Sopenharmony_ci * they're not ground referenced. 20338c2ecf20Sopenharmony_ci */ 20348c2ecf20Sopenharmony_ci bias = SND_SOC_BIAS_OFF; 20358c2ecf20Sopenharmony_ci for_each_card_dapms(card, d) 20368c2ecf20Sopenharmony_ci if (d->target_bias_level > bias) 20378c2ecf20Sopenharmony_ci bias = d->target_bias_level; 20388c2ecf20Sopenharmony_ci for_each_card_dapms(card, d) 20398c2ecf20Sopenharmony_ci if (!dapm_idle_bias_off(d)) 20408c2ecf20Sopenharmony_ci d->target_bias_level = bias; 20418c2ecf20Sopenharmony_ci 20428c2ecf20Sopenharmony_ci trace_snd_soc_dapm_walk_done(card); 20438c2ecf20Sopenharmony_ci 20448c2ecf20Sopenharmony_ci /* Run card bias changes at first */ 20458c2ecf20Sopenharmony_ci dapm_pre_sequence_async(&card->dapm, 0); 20468c2ecf20Sopenharmony_ci /* Run other bias changes in parallel */ 20478c2ecf20Sopenharmony_ci for_each_card_dapms(card, d) { 20488c2ecf20Sopenharmony_ci if (d != &card->dapm && d->bias_level != d->target_bias_level) 20498c2ecf20Sopenharmony_ci async_schedule_domain(dapm_pre_sequence_async, d, 20508c2ecf20Sopenharmony_ci &async_domain); 20518c2ecf20Sopenharmony_ci } 20528c2ecf20Sopenharmony_ci async_synchronize_full_domain(&async_domain); 20538c2ecf20Sopenharmony_ci 20548c2ecf20Sopenharmony_ci list_for_each_entry(w, &down_list, power_list) { 20558c2ecf20Sopenharmony_ci dapm_seq_check_event(card, w, SND_SOC_DAPM_WILL_PMD); 20568c2ecf20Sopenharmony_ci } 20578c2ecf20Sopenharmony_ci 20588c2ecf20Sopenharmony_ci list_for_each_entry(w, &up_list, power_list) { 20598c2ecf20Sopenharmony_ci dapm_seq_check_event(card, w, SND_SOC_DAPM_WILL_PMU); 20608c2ecf20Sopenharmony_ci } 20618c2ecf20Sopenharmony_ci 20628c2ecf20Sopenharmony_ci /* Power down widgets first; try to avoid amplifying pops. */ 20638c2ecf20Sopenharmony_ci dapm_seq_run(card, &down_list, event, false); 20648c2ecf20Sopenharmony_ci 20658c2ecf20Sopenharmony_ci dapm_widget_update(card); 20668c2ecf20Sopenharmony_ci 20678c2ecf20Sopenharmony_ci /* Now power up. */ 20688c2ecf20Sopenharmony_ci dapm_seq_run(card, &up_list, event, true); 20698c2ecf20Sopenharmony_ci 20708c2ecf20Sopenharmony_ci /* Run all the bias changes in parallel */ 20718c2ecf20Sopenharmony_ci for_each_card_dapms(card, d) { 20728c2ecf20Sopenharmony_ci if (d != &card->dapm && d->bias_level != d->target_bias_level) 20738c2ecf20Sopenharmony_ci async_schedule_domain(dapm_post_sequence_async, d, 20748c2ecf20Sopenharmony_ci &async_domain); 20758c2ecf20Sopenharmony_ci } 20768c2ecf20Sopenharmony_ci async_synchronize_full_domain(&async_domain); 20778c2ecf20Sopenharmony_ci /* Run card bias changes at last */ 20788c2ecf20Sopenharmony_ci dapm_post_sequence_async(&card->dapm, 0); 20798c2ecf20Sopenharmony_ci 20808c2ecf20Sopenharmony_ci /* do we need to notify any clients that DAPM event is complete */ 20818c2ecf20Sopenharmony_ci for_each_card_dapms(card, d) { 20828c2ecf20Sopenharmony_ci if (!d->component) 20838c2ecf20Sopenharmony_ci continue; 20848c2ecf20Sopenharmony_ci 20858c2ecf20Sopenharmony_ci ret = snd_soc_component_stream_event(d->component, event); 20868c2ecf20Sopenharmony_ci if (ret < 0) 20878c2ecf20Sopenharmony_ci return ret; 20888c2ecf20Sopenharmony_ci } 20898c2ecf20Sopenharmony_ci 20908c2ecf20Sopenharmony_ci pop_dbg(card->dev, card->pop_time, 20918c2ecf20Sopenharmony_ci "DAPM sequencing finished, waiting %dms\n", card->pop_time); 20928c2ecf20Sopenharmony_ci pop_wait(card->pop_time); 20938c2ecf20Sopenharmony_ci 20948c2ecf20Sopenharmony_ci trace_snd_soc_dapm_done(card); 20958c2ecf20Sopenharmony_ci 20968c2ecf20Sopenharmony_ci return 0; 20978c2ecf20Sopenharmony_ci} 20988c2ecf20Sopenharmony_ci 20998c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 21008c2ecf20Sopenharmony_cistatic ssize_t dapm_widget_power_read_file(struct file *file, 21018c2ecf20Sopenharmony_ci char __user *user_buf, 21028c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 21038c2ecf20Sopenharmony_ci{ 21048c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *w = file->private_data; 21058c2ecf20Sopenharmony_ci struct snd_soc_card *card = w->dapm->card; 21068c2ecf20Sopenharmony_ci enum snd_soc_dapm_direction dir, rdir; 21078c2ecf20Sopenharmony_ci char *buf; 21088c2ecf20Sopenharmony_ci int in, out; 21098c2ecf20Sopenharmony_ci ssize_t ret; 21108c2ecf20Sopenharmony_ci struct snd_soc_dapm_path *p = NULL; 21118c2ecf20Sopenharmony_ci 21128c2ecf20Sopenharmony_ci buf = kmalloc(PAGE_SIZE, GFP_KERNEL); 21138c2ecf20Sopenharmony_ci if (!buf) 21148c2ecf20Sopenharmony_ci return -ENOMEM; 21158c2ecf20Sopenharmony_ci 21168c2ecf20Sopenharmony_ci mutex_lock(&card->dapm_mutex); 21178c2ecf20Sopenharmony_ci 21188c2ecf20Sopenharmony_ci /* Supply widgets are not handled by is_connected_{input,output}_ep() */ 21198c2ecf20Sopenharmony_ci if (w->is_supply) { 21208c2ecf20Sopenharmony_ci in = 0; 21218c2ecf20Sopenharmony_ci out = 0; 21228c2ecf20Sopenharmony_ci } else { 21238c2ecf20Sopenharmony_ci in = is_connected_input_ep(w, NULL, NULL); 21248c2ecf20Sopenharmony_ci out = is_connected_output_ep(w, NULL, NULL); 21258c2ecf20Sopenharmony_ci } 21268c2ecf20Sopenharmony_ci 21278c2ecf20Sopenharmony_ci ret = scnprintf(buf, PAGE_SIZE, "%s: %s%s in %d out %d", 21288c2ecf20Sopenharmony_ci w->name, w->power ? "On" : "Off", 21298c2ecf20Sopenharmony_ci w->force ? " (forced)" : "", in, out); 21308c2ecf20Sopenharmony_ci 21318c2ecf20Sopenharmony_ci if (w->reg >= 0) 21328c2ecf20Sopenharmony_ci ret += scnprintf(buf + ret, PAGE_SIZE - ret, 21338c2ecf20Sopenharmony_ci " - R%d(0x%x) mask 0x%x", 21348c2ecf20Sopenharmony_ci w->reg, w->reg, w->mask << w->shift); 21358c2ecf20Sopenharmony_ci 21368c2ecf20Sopenharmony_ci ret += scnprintf(buf + ret, PAGE_SIZE - ret, "\n"); 21378c2ecf20Sopenharmony_ci 21388c2ecf20Sopenharmony_ci if (w->sname) 21398c2ecf20Sopenharmony_ci ret += scnprintf(buf + ret, PAGE_SIZE - ret, " stream %s %s\n", 21408c2ecf20Sopenharmony_ci w->sname, 21418c2ecf20Sopenharmony_ci w->active ? "active" : "inactive"); 21428c2ecf20Sopenharmony_ci 21438c2ecf20Sopenharmony_ci snd_soc_dapm_for_each_direction(dir) { 21448c2ecf20Sopenharmony_ci rdir = SND_SOC_DAPM_DIR_REVERSE(dir); 21458c2ecf20Sopenharmony_ci snd_soc_dapm_widget_for_each_path(w, dir, p) { 21468c2ecf20Sopenharmony_ci if (p->connected && !p->connected(p->source, p->sink)) 21478c2ecf20Sopenharmony_ci continue; 21488c2ecf20Sopenharmony_ci 21498c2ecf20Sopenharmony_ci if (!p->connect) 21508c2ecf20Sopenharmony_ci continue; 21518c2ecf20Sopenharmony_ci 21528c2ecf20Sopenharmony_ci ret += scnprintf(buf + ret, PAGE_SIZE - ret, 21538c2ecf20Sopenharmony_ci " %s \"%s\" \"%s\"\n", 21548c2ecf20Sopenharmony_ci (rdir == SND_SOC_DAPM_DIR_IN) ? "in" : "out", 21558c2ecf20Sopenharmony_ci p->name ? p->name : "static", 21568c2ecf20Sopenharmony_ci p->node[rdir]->name); 21578c2ecf20Sopenharmony_ci } 21588c2ecf20Sopenharmony_ci } 21598c2ecf20Sopenharmony_ci 21608c2ecf20Sopenharmony_ci mutex_unlock(&card->dapm_mutex); 21618c2ecf20Sopenharmony_ci 21628c2ecf20Sopenharmony_ci ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); 21638c2ecf20Sopenharmony_ci 21648c2ecf20Sopenharmony_ci kfree(buf); 21658c2ecf20Sopenharmony_ci return ret; 21668c2ecf20Sopenharmony_ci} 21678c2ecf20Sopenharmony_ci 21688c2ecf20Sopenharmony_cistatic const struct file_operations dapm_widget_power_fops = { 21698c2ecf20Sopenharmony_ci .open = simple_open, 21708c2ecf20Sopenharmony_ci .read = dapm_widget_power_read_file, 21718c2ecf20Sopenharmony_ci .llseek = default_llseek, 21728c2ecf20Sopenharmony_ci}; 21738c2ecf20Sopenharmony_ci 21748c2ecf20Sopenharmony_cistatic ssize_t dapm_bias_read_file(struct file *file, char __user *user_buf, 21758c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 21768c2ecf20Sopenharmony_ci{ 21778c2ecf20Sopenharmony_ci struct snd_soc_dapm_context *dapm = file->private_data; 21788c2ecf20Sopenharmony_ci char *level; 21798c2ecf20Sopenharmony_ci 21808c2ecf20Sopenharmony_ci switch (dapm->bias_level) { 21818c2ecf20Sopenharmony_ci case SND_SOC_BIAS_ON: 21828c2ecf20Sopenharmony_ci level = "On\n"; 21838c2ecf20Sopenharmony_ci break; 21848c2ecf20Sopenharmony_ci case SND_SOC_BIAS_PREPARE: 21858c2ecf20Sopenharmony_ci level = "Prepare\n"; 21868c2ecf20Sopenharmony_ci break; 21878c2ecf20Sopenharmony_ci case SND_SOC_BIAS_STANDBY: 21888c2ecf20Sopenharmony_ci level = "Standby\n"; 21898c2ecf20Sopenharmony_ci break; 21908c2ecf20Sopenharmony_ci case SND_SOC_BIAS_OFF: 21918c2ecf20Sopenharmony_ci level = "Off\n"; 21928c2ecf20Sopenharmony_ci break; 21938c2ecf20Sopenharmony_ci default: 21948c2ecf20Sopenharmony_ci WARN(1, "Unknown bias_level %d\n", dapm->bias_level); 21958c2ecf20Sopenharmony_ci level = "Unknown\n"; 21968c2ecf20Sopenharmony_ci break; 21978c2ecf20Sopenharmony_ci } 21988c2ecf20Sopenharmony_ci 21998c2ecf20Sopenharmony_ci return simple_read_from_buffer(user_buf, count, ppos, level, 22008c2ecf20Sopenharmony_ci strlen(level)); 22018c2ecf20Sopenharmony_ci} 22028c2ecf20Sopenharmony_ci 22038c2ecf20Sopenharmony_cistatic const struct file_operations dapm_bias_fops = { 22048c2ecf20Sopenharmony_ci .open = simple_open, 22058c2ecf20Sopenharmony_ci .read = dapm_bias_read_file, 22068c2ecf20Sopenharmony_ci .llseek = default_llseek, 22078c2ecf20Sopenharmony_ci}; 22088c2ecf20Sopenharmony_ci 22098c2ecf20Sopenharmony_civoid snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm, 22108c2ecf20Sopenharmony_ci struct dentry *parent) 22118c2ecf20Sopenharmony_ci{ 22128c2ecf20Sopenharmony_ci if (!parent || IS_ERR(parent)) 22138c2ecf20Sopenharmony_ci return; 22148c2ecf20Sopenharmony_ci 22158c2ecf20Sopenharmony_ci dapm->debugfs_dapm = debugfs_create_dir("dapm", parent); 22168c2ecf20Sopenharmony_ci 22178c2ecf20Sopenharmony_ci debugfs_create_file("bias_level", 0444, dapm->debugfs_dapm, dapm, 22188c2ecf20Sopenharmony_ci &dapm_bias_fops); 22198c2ecf20Sopenharmony_ci} 22208c2ecf20Sopenharmony_ci 22218c2ecf20Sopenharmony_cistatic void dapm_debugfs_add_widget(struct snd_soc_dapm_widget *w) 22228c2ecf20Sopenharmony_ci{ 22238c2ecf20Sopenharmony_ci struct snd_soc_dapm_context *dapm = w->dapm; 22248c2ecf20Sopenharmony_ci 22258c2ecf20Sopenharmony_ci if (!dapm->debugfs_dapm || !w->name) 22268c2ecf20Sopenharmony_ci return; 22278c2ecf20Sopenharmony_ci 22288c2ecf20Sopenharmony_ci debugfs_create_file(w->name, 0444, dapm->debugfs_dapm, w, 22298c2ecf20Sopenharmony_ci &dapm_widget_power_fops); 22308c2ecf20Sopenharmony_ci} 22318c2ecf20Sopenharmony_ci 22328c2ecf20Sopenharmony_cistatic void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm) 22338c2ecf20Sopenharmony_ci{ 22348c2ecf20Sopenharmony_ci debugfs_remove_recursive(dapm->debugfs_dapm); 22358c2ecf20Sopenharmony_ci dapm->debugfs_dapm = NULL; 22368c2ecf20Sopenharmony_ci} 22378c2ecf20Sopenharmony_ci 22388c2ecf20Sopenharmony_ci#else 22398c2ecf20Sopenharmony_civoid snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm, 22408c2ecf20Sopenharmony_ci struct dentry *parent) 22418c2ecf20Sopenharmony_ci{ 22428c2ecf20Sopenharmony_ci} 22438c2ecf20Sopenharmony_ci 22448c2ecf20Sopenharmony_cistatic inline void dapm_debugfs_add_widget(struct snd_soc_dapm_widget *w) 22458c2ecf20Sopenharmony_ci{ 22468c2ecf20Sopenharmony_ci} 22478c2ecf20Sopenharmony_ci 22488c2ecf20Sopenharmony_cistatic inline void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm) 22498c2ecf20Sopenharmony_ci{ 22508c2ecf20Sopenharmony_ci} 22518c2ecf20Sopenharmony_ci 22528c2ecf20Sopenharmony_ci#endif 22538c2ecf20Sopenharmony_ci 22548c2ecf20Sopenharmony_ci/* 22558c2ecf20Sopenharmony_ci * soc_dapm_connect_path() - Connects or disconnects a path 22568c2ecf20Sopenharmony_ci * @path: The path to update 22578c2ecf20Sopenharmony_ci * @connect: The new connect state of the path. True if the path is connected, 22588c2ecf20Sopenharmony_ci * false if it is disconnected. 22598c2ecf20Sopenharmony_ci * @reason: The reason why the path changed (for debugging only) 22608c2ecf20Sopenharmony_ci */ 22618c2ecf20Sopenharmony_cistatic void soc_dapm_connect_path(struct snd_soc_dapm_path *path, 22628c2ecf20Sopenharmony_ci bool connect, const char *reason) 22638c2ecf20Sopenharmony_ci{ 22648c2ecf20Sopenharmony_ci if (path->connect == connect) 22658c2ecf20Sopenharmony_ci return; 22668c2ecf20Sopenharmony_ci 22678c2ecf20Sopenharmony_ci path->connect = connect; 22688c2ecf20Sopenharmony_ci dapm_mark_dirty(path->source, reason); 22698c2ecf20Sopenharmony_ci dapm_mark_dirty(path->sink, reason); 22708c2ecf20Sopenharmony_ci dapm_path_invalidate(path); 22718c2ecf20Sopenharmony_ci} 22728c2ecf20Sopenharmony_ci 22738c2ecf20Sopenharmony_ci/* test and update the power status of a mux widget */ 22748c2ecf20Sopenharmony_cistatic int soc_dapm_mux_update_power(struct snd_soc_card *card, 22758c2ecf20Sopenharmony_ci struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e) 22768c2ecf20Sopenharmony_ci{ 22778c2ecf20Sopenharmony_ci struct snd_soc_dapm_path *path; 22788c2ecf20Sopenharmony_ci int found = 0; 22798c2ecf20Sopenharmony_ci bool connect; 22808c2ecf20Sopenharmony_ci 22818c2ecf20Sopenharmony_ci lockdep_assert_held(&card->dapm_mutex); 22828c2ecf20Sopenharmony_ci 22838c2ecf20Sopenharmony_ci /* find dapm widget path assoc with kcontrol */ 22848c2ecf20Sopenharmony_ci dapm_kcontrol_for_each_path(path, kcontrol) { 22858c2ecf20Sopenharmony_ci found = 1; 22868c2ecf20Sopenharmony_ci /* we now need to match the string in the enum to the path */ 22878c2ecf20Sopenharmony_ci if (e && !(strcmp(path->name, e->texts[mux]))) 22888c2ecf20Sopenharmony_ci connect = true; 22898c2ecf20Sopenharmony_ci else 22908c2ecf20Sopenharmony_ci connect = false; 22918c2ecf20Sopenharmony_ci 22928c2ecf20Sopenharmony_ci soc_dapm_connect_path(path, connect, "mux update"); 22938c2ecf20Sopenharmony_ci } 22948c2ecf20Sopenharmony_ci 22958c2ecf20Sopenharmony_ci if (found) 22968c2ecf20Sopenharmony_ci dapm_power_widgets(card, SND_SOC_DAPM_STREAM_NOP); 22978c2ecf20Sopenharmony_ci 22988c2ecf20Sopenharmony_ci return found; 22998c2ecf20Sopenharmony_ci} 23008c2ecf20Sopenharmony_ci 23018c2ecf20Sopenharmony_ciint snd_soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm, 23028c2ecf20Sopenharmony_ci struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e, 23038c2ecf20Sopenharmony_ci struct snd_soc_dapm_update *update) 23048c2ecf20Sopenharmony_ci{ 23058c2ecf20Sopenharmony_ci struct snd_soc_card *card = dapm->card; 23068c2ecf20Sopenharmony_ci int ret; 23078c2ecf20Sopenharmony_ci 23088c2ecf20Sopenharmony_ci mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); 23098c2ecf20Sopenharmony_ci card->update = update; 23108c2ecf20Sopenharmony_ci ret = soc_dapm_mux_update_power(card, kcontrol, mux, e); 23118c2ecf20Sopenharmony_ci card->update = NULL; 23128c2ecf20Sopenharmony_ci mutex_unlock(&card->dapm_mutex); 23138c2ecf20Sopenharmony_ci if (ret > 0) 23148c2ecf20Sopenharmony_ci snd_soc_dpcm_runtime_update(card); 23158c2ecf20Sopenharmony_ci return ret; 23168c2ecf20Sopenharmony_ci} 23178c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_mux_update_power); 23188c2ecf20Sopenharmony_ci 23198c2ecf20Sopenharmony_ci/* test and update the power status of a mixer or switch widget */ 23208c2ecf20Sopenharmony_cistatic int soc_dapm_mixer_update_power(struct snd_soc_card *card, 23218c2ecf20Sopenharmony_ci struct snd_kcontrol *kcontrol, 23228c2ecf20Sopenharmony_ci int connect, int rconnect) 23238c2ecf20Sopenharmony_ci{ 23248c2ecf20Sopenharmony_ci struct snd_soc_dapm_path *path; 23258c2ecf20Sopenharmony_ci int found = 0; 23268c2ecf20Sopenharmony_ci 23278c2ecf20Sopenharmony_ci lockdep_assert_held(&card->dapm_mutex); 23288c2ecf20Sopenharmony_ci 23298c2ecf20Sopenharmony_ci /* find dapm widget path assoc with kcontrol */ 23308c2ecf20Sopenharmony_ci dapm_kcontrol_for_each_path(path, kcontrol) { 23318c2ecf20Sopenharmony_ci /* 23328c2ecf20Sopenharmony_ci * Ideally this function should support any number of 23338c2ecf20Sopenharmony_ci * paths and channels. But since kcontrols only come 23348c2ecf20Sopenharmony_ci * in mono and stereo variants, we are limited to 2 23358c2ecf20Sopenharmony_ci * channels. 23368c2ecf20Sopenharmony_ci * 23378c2ecf20Sopenharmony_ci * The following code assumes for stereo controls the 23388c2ecf20Sopenharmony_ci * first path (when 'found == 0') is the left channel, 23398c2ecf20Sopenharmony_ci * and all remaining paths (when 'found == 1') are the 23408c2ecf20Sopenharmony_ci * right channel. 23418c2ecf20Sopenharmony_ci * 23428c2ecf20Sopenharmony_ci * A stereo control is signified by a valid 'rconnect' 23438c2ecf20Sopenharmony_ci * value, either 0 for unconnected, or >= 0 for connected. 23448c2ecf20Sopenharmony_ci * This is chosen instead of using snd_soc_volsw_is_stereo, 23458c2ecf20Sopenharmony_ci * so that the behavior of snd_soc_dapm_mixer_update_power 23468c2ecf20Sopenharmony_ci * doesn't change even when the kcontrol passed in is 23478c2ecf20Sopenharmony_ci * stereo. 23488c2ecf20Sopenharmony_ci * 23498c2ecf20Sopenharmony_ci * It passes 'connect' as the path connect status for 23508c2ecf20Sopenharmony_ci * the left channel, and 'rconnect' for the right 23518c2ecf20Sopenharmony_ci * channel. 23528c2ecf20Sopenharmony_ci */ 23538c2ecf20Sopenharmony_ci if (found && rconnect >= 0) 23548c2ecf20Sopenharmony_ci soc_dapm_connect_path(path, rconnect, "mixer update"); 23558c2ecf20Sopenharmony_ci else 23568c2ecf20Sopenharmony_ci soc_dapm_connect_path(path, connect, "mixer update"); 23578c2ecf20Sopenharmony_ci found = 1; 23588c2ecf20Sopenharmony_ci } 23598c2ecf20Sopenharmony_ci 23608c2ecf20Sopenharmony_ci if (found) 23618c2ecf20Sopenharmony_ci dapm_power_widgets(card, SND_SOC_DAPM_STREAM_NOP); 23628c2ecf20Sopenharmony_ci 23638c2ecf20Sopenharmony_ci return found; 23648c2ecf20Sopenharmony_ci} 23658c2ecf20Sopenharmony_ci 23668c2ecf20Sopenharmony_ciint snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm, 23678c2ecf20Sopenharmony_ci struct snd_kcontrol *kcontrol, int connect, 23688c2ecf20Sopenharmony_ci struct snd_soc_dapm_update *update) 23698c2ecf20Sopenharmony_ci{ 23708c2ecf20Sopenharmony_ci struct snd_soc_card *card = dapm->card; 23718c2ecf20Sopenharmony_ci int ret; 23728c2ecf20Sopenharmony_ci 23738c2ecf20Sopenharmony_ci mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); 23748c2ecf20Sopenharmony_ci card->update = update; 23758c2ecf20Sopenharmony_ci ret = soc_dapm_mixer_update_power(card, kcontrol, connect, -1); 23768c2ecf20Sopenharmony_ci card->update = NULL; 23778c2ecf20Sopenharmony_ci mutex_unlock(&card->dapm_mutex); 23788c2ecf20Sopenharmony_ci if (ret > 0) 23798c2ecf20Sopenharmony_ci snd_soc_dpcm_runtime_update(card); 23808c2ecf20Sopenharmony_ci return ret; 23818c2ecf20Sopenharmony_ci} 23828c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_mixer_update_power); 23838c2ecf20Sopenharmony_ci 23848c2ecf20Sopenharmony_cistatic ssize_t dapm_widget_show_component(struct snd_soc_component *cmpnt, 23858c2ecf20Sopenharmony_ci char *buf) 23868c2ecf20Sopenharmony_ci{ 23878c2ecf20Sopenharmony_ci struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt); 23888c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *w; 23898c2ecf20Sopenharmony_ci int count = 0; 23908c2ecf20Sopenharmony_ci char *state = "not set"; 23918c2ecf20Sopenharmony_ci 23928c2ecf20Sopenharmony_ci /* card won't be set for the dummy component, as a spot fix 23938c2ecf20Sopenharmony_ci * we're checking for that case specifically here but in future 23948c2ecf20Sopenharmony_ci * we will ensure that the dummy component looks like others. 23958c2ecf20Sopenharmony_ci */ 23968c2ecf20Sopenharmony_ci if (!cmpnt->card) 23978c2ecf20Sopenharmony_ci return 0; 23988c2ecf20Sopenharmony_ci 23998c2ecf20Sopenharmony_ci for_each_card_widgets(cmpnt->card, w) { 24008c2ecf20Sopenharmony_ci if (w->dapm != dapm) 24018c2ecf20Sopenharmony_ci continue; 24028c2ecf20Sopenharmony_ci 24038c2ecf20Sopenharmony_ci /* only display widgets that burn power */ 24048c2ecf20Sopenharmony_ci switch (w->id) { 24058c2ecf20Sopenharmony_ci case snd_soc_dapm_hp: 24068c2ecf20Sopenharmony_ci case snd_soc_dapm_mic: 24078c2ecf20Sopenharmony_ci case snd_soc_dapm_spk: 24088c2ecf20Sopenharmony_ci case snd_soc_dapm_line: 24098c2ecf20Sopenharmony_ci case snd_soc_dapm_micbias: 24108c2ecf20Sopenharmony_ci case snd_soc_dapm_dac: 24118c2ecf20Sopenharmony_ci case snd_soc_dapm_adc: 24128c2ecf20Sopenharmony_ci case snd_soc_dapm_pga: 24138c2ecf20Sopenharmony_ci case snd_soc_dapm_effect: 24148c2ecf20Sopenharmony_ci case snd_soc_dapm_out_drv: 24158c2ecf20Sopenharmony_ci case snd_soc_dapm_mixer: 24168c2ecf20Sopenharmony_ci case snd_soc_dapm_mixer_named_ctl: 24178c2ecf20Sopenharmony_ci case snd_soc_dapm_supply: 24188c2ecf20Sopenharmony_ci case snd_soc_dapm_regulator_supply: 24198c2ecf20Sopenharmony_ci case snd_soc_dapm_pinctrl: 24208c2ecf20Sopenharmony_ci case snd_soc_dapm_clock_supply: 24218c2ecf20Sopenharmony_ci if (w->name) 24228c2ecf20Sopenharmony_ci count += sprintf(buf + count, "%s: %s\n", 24238c2ecf20Sopenharmony_ci w->name, w->power ? "On":"Off"); 24248c2ecf20Sopenharmony_ci break; 24258c2ecf20Sopenharmony_ci default: 24268c2ecf20Sopenharmony_ci break; 24278c2ecf20Sopenharmony_ci } 24288c2ecf20Sopenharmony_ci } 24298c2ecf20Sopenharmony_ci 24308c2ecf20Sopenharmony_ci switch (snd_soc_dapm_get_bias_level(dapm)) { 24318c2ecf20Sopenharmony_ci case SND_SOC_BIAS_ON: 24328c2ecf20Sopenharmony_ci state = "On"; 24338c2ecf20Sopenharmony_ci break; 24348c2ecf20Sopenharmony_ci case SND_SOC_BIAS_PREPARE: 24358c2ecf20Sopenharmony_ci state = "Prepare"; 24368c2ecf20Sopenharmony_ci break; 24378c2ecf20Sopenharmony_ci case SND_SOC_BIAS_STANDBY: 24388c2ecf20Sopenharmony_ci state = "Standby"; 24398c2ecf20Sopenharmony_ci break; 24408c2ecf20Sopenharmony_ci case SND_SOC_BIAS_OFF: 24418c2ecf20Sopenharmony_ci state = "Off"; 24428c2ecf20Sopenharmony_ci break; 24438c2ecf20Sopenharmony_ci } 24448c2ecf20Sopenharmony_ci count += sprintf(buf + count, "PM State: %s\n", state); 24458c2ecf20Sopenharmony_ci 24468c2ecf20Sopenharmony_ci return count; 24478c2ecf20Sopenharmony_ci} 24488c2ecf20Sopenharmony_ci 24498c2ecf20Sopenharmony_ci/* show dapm widget status in sys fs */ 24508c2ecf20Sopenharmony_cistatic ssize_t dapm_widget_show(struct device *dev, 24518c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 24528c2ecf20Sopenharmony_ci{ 24538c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev); 24548c2ecf20Sopenharmony_ci struct snd_soc_dai *codec_dai; 24558c2ecf20Sopenharmony_ci int i, count = 0; 24568c2ecf20Sopenharmony_ci 24578c2ecf20Sopenharmony_ci mutex_lock(&rtd->card->dapm_mutex); 24588c2ecf20Sopenharmony_ci 24598c2ecf20Sopenharmony_ci for_each_rtd_codec_dais(rtd, i, codec_dai) { 24608c2ecf20Sopenharmony_ci struct snd_soc_component *cmpnt = codec_dai->component; 24618c2ecf20Sopenharmony_ci 24628c2ecf20Sopenharmony_ci count += dapm_widget_show_component(cmpnt, buf + count); 24638c2ecf20Sopenharmony_ci } 24648c2ecf20Sopenharmony_ci 24658c2ecf20Sopenharmony_ci mutex_unlock(&rtd->card->dapm_mutex); 24668c2ecf20Sopenharmony_ci 24678c2ecf20Sopenharmony_ci return count; 24688c2ecf20Sopenharmony_ci} 24698c2ecf20Sopenharmony_ci 24708c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(dapm_widget); 24718c2ecf20Sopenharmony_ci 24728c2ecf20Sopenharmony_cistruct attribute *soc_dapm_dev_attrs[] = { 24738c2ecf20Sopenharmony_ci &dev_attr_dapm_widget.attr, 24748c2ecf20Sopenharmony_ci NULL 24758c2ecf20Sopenharmony_ci}; 24768c2ecf20Sopenharmony_ci 24778c2ecf20Sopenharmony_cistatic void dapm_free_path(struct snd_soc_dapm_path *path) 24788c2ecf20Sopenharmony_ci{ 24798c2ecf20Sopenharmony_ci list_del(&path->list_node[SND_SOC_DAPM_DIR_IN]); 24808c2ecf20Sopenharmony_ci list_del(&path->list_node[SND_SOC_DAPM_DIR_OUT]); 24818c2ecf20Sopenharmony_ci list_del(&path->list_kcontrol); 24828c2ecf20Sopenharmony_ci list_del(&path->list); 24838c2ecf20Sopenharmony_ci kfree(path); 24848c2ecf20Sopenharmony_ci} 24858c2ecf20Sopenharmony_ci 24868c2ecf20Sopenharmony_civoid snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w) 24878c2ecf20Sopenharmony_ci{ 24888c2ecf20Sopenharmony_ci struct snd_soc_dapm_path *p, *next_p; 24898c2ecf20Sopenharmony_ci enum snd_soc_dapm_direction dir; 24908c2ecf20Sopenharmony_ci 24918c2ecf20Sopenharmony_ci list_del(&w->list); 24928c2ecf20Sopenharmony_ci list_del(&w->dirty); 24938c2ecf20Sopenharmony_ci /* 24948c2ecf20Sopenharmony_ci * remove source and sink paths associated to this widget. 24958c2ecf20Sopenharmony_ci * While removing the path, remove reference to it from both 24968c2ecf20Sopenharmony_ci * source and sink widgets so that path is removed only once. 24978c2ecf20Sopenharmony_ci */ 24988c2ecf20Sopenharmony_ci snd_soc_dapm_for_each_direction(dir) { 24998c2ecf20Sopenharmony_ci snd_soc_dapm_widget_for_each_path_safe(w, dir, p, next_p) 25008c2ecf20Sopenharmony_ci dapm_free_path(p); 25018c2ecf20Sopenharmony_ci } 25028c2ecf20Sopenharmony_ci 25038c2ecf20Sopenharmony_ci kfree(w->kcontrols); 25048c2ecf20Sopenharmony_ci kfree_const(w->name); 25058c2ecf20Sopenharmony_ci kfree_const(w->sname); 25068c2ecf20Sopenharmony_ci kfree(w); 25078c2ecf20Sopenharmony_ci} 25088c2ecf20Sopenharmony_ci 25098c2ecf20Sopenharmony_civoid snd_soc_dapm_reset_cache(struct snd_soc_dapm_context *dapm) 25108c2ecf20Sopenharmony_ci{ 25118c2ecf20Sopenharmony_ci dapm->path_sink_cache.widget = NULL; 25128c2ecf20Sopenharmony_ci dapm->path_source_cache.widget = NULL; 25138c2ecf20Sopenharmony_ci} 25148c2ecf20Sopenharmony_ci 25158c2ecf20Sopenharmony_ci/* free all dapm widgets and resources */ 25168c2ecf20Sopenharmony_cistatic void dapm_free_widgets(struct snd_soc_dapm_context *dapm) 25178c2ecf20Sopenharmony_ci{ 25188c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *w, *next_w; 25198c2ecf20Sopenharmony_ci 25208c2ecf20Sopenharmony_ci for_each_card_widgets_safe(dapm->card, w, next_w) { 25218c2ecf20Sopenharmony_ci if (w->dapm != dapm) 25228c2ecf20Sopenharmony_ci continue; 25238c2ecf20Sopenharmony_ci snd_soc_dapm_free_widget(w); 25248c2ecf20Sopenharmony_ci } 25258c2ecf20Sopenharmony_ci snd_soc_dapm_reset_cache(dapm); 25268c2ecf20Sopenharmony_ci} 25278c2ecf20Sopenharmony_ci 25288c2ecf20Sopenharmony_cistatic struct snd_soc_dapm_widget *dapm_find_widget( 25298c2ecf20Sopenharmony_ci struct snd_soc_dapm_context *dapm, const char *pin, 25308c2ecf20Sopenharmony_ci bool search_other_contexts) 25318c2ecf20Sopenharmony_ci{ 25328c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *w; 25338c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *fallback = NULL; 25348c2ecf20Sopenharmony_ci char prefixed_pin[80]; 25358c2ecf20Sopenharmony_ci const char *pin_name; 25368c2ecf20Sopenharmony_ci const char *prefix = soc_dapm_prefix(dapm); 25378c2ecf20Sopenharmony_ci 25388c2ecf20Sopenharmony_ci if (prefix) { 25398c2ecf20Sopenharmony_ci snprintf(prefixed_pin, sizeof(prefixed_pin), "%s %s", 25408c2ecf20Sopenharmony_ci prefix, pin); 25418c2ecf20Sopenharmony_ci pin_name = prefixed_pin; 25428c2ecf20Sopenharmony_ci } else { 25438c2ecf20Sopenharmony_ci pin_name = pin; 25448c2ecf20Sopenharmony_ci } 25458c2ecf20Sopenharmony_ci 25468c2ecf20Sopenharmony_ci for_each_card_widgets(dapm->card, w) { 25478c2ecf20Sopenharmony_ci if (!strcmp(w->name, pin_name)) { 25488c2ecf20Sopenharmony_ci if (w->dapm == dapm) 25498c2ecf20Sopenharmony_ci return w; 25508c2ecf20Sopenharmony_ci else 25518c2ecf20Sopenharmony_ci fallback = w; 25528c2ecf20Sopenharmony_ci } 25538c2ecf20Sopenharmony_ci } 25548c2ecf20Sopenharmony_ci 25558c2ecf20Sopenharmony_ci if (search_other_contexts) 25568c2ecf20Sopenharmony_ci return fallback; 25578c2ecf20Sopenharmony_ci 25588c2ecf20Sopenharmony_ci return NULL; 25598c2ecf20Sopenharmony_ci} 25608c2ecf20Sopenharmony_ci 25618c2ecf20Sopenharmony_ci/* 25628c2ecf20Sopenharmony_ci * set the DAPM pin status: 25638c2ecf20Sopenharmony_ci * returns 1 when the value has been updated, 0 when unchanged, or a negative 25648c2ecf20Sopenharmony_ci * error code; called from kcontrol put callback 25658c2ecf20Sopenharmony_ci */ 25668c2ecf20Sopenharmony_cistatic int __snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm, 25678c2ecf20Sopenharmony_ci const char *pin, int status) 25688c2ecf20Sopenharmony_ci{ 25698c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, true); 25708c2ecf20Sopenharmony_ci int ret = 0; 25718c2ecf20Sopenharmony_ci 25728c2ecf20Sopenharmony_ci dapm_assert_locked(dapm); 25738c2ecf20Sopenharmony_ci 25748c2ecf20Sopenharmony_ci if (!w) { 25758c2ecf20Sopenharmony_ci dev_err(dapm->dev, "ASoC: DAPM unknown pin %s\n", pin); 25768c2ecf20Sopenharmony_ci return -EINVAL; 25778c2ecf20Sopenharmony_ci } 25788c2ecf20Sopenharmony_ci 25798c2ecf20Sopenharmony_ci if (w->connected != status) { 25808c2ecf20Sopenharmony_ci dapm_mark_dirty(w, "pin configuration"); 25818c2ecf20Sopenharmony_ci dapm_widget_invalidate_input_paths(w); 25828c2ecf20Sopenharmony_ci dapm_widget_invalidate_output_paths(w); 25838c2ecf20Sopenharmony_ci ret = 1; 25848c2ecf20Sopenharmony_ci } 25858c2ecf20Sopenharmony_ci 25868c2ecf20Sopenharmony_ci w->connected = status; 25878c2ecf20Sopenharmony_ci if (status == 0) 25888c2ecf20Sopenharmony_ci w->force = 0; 25898c2ecf20Sopenharmony_ci 25908c2ecf20Sopenharmony_ci return ret; 25918c2ecf20Sopenharmony_ci} 25928c2ecf20Sopenharmony_ci 25938c2ecf20Sopenharmony_ci/* 25948c2ecf20Sopenharmony_ci * similar as __snd_soc_dapm_set_pin(), but returns 0 when successful; 25958c2ecf20Sopenharmony_ci * called from several API functions below 25968c2ecf20Sopenharmony_ci */ 25978c2ecf20Sopenharmony_cistatic int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm, 25988c2ecf20Sopenharmony_ci const char *pin, int status) 25998c2ecf20Sopenharmony_ci{ 26008c2ecf20Sopenharmony_ci int ret = __snd_soc_dapm_set_pin(dapm, pin, status); 26018c2ecf20Sopenharmony_ci 26028c2ecf20Sopenharmony_ci return ret < 0 ? ret : 0; 26038c2ecf20Sopenharmony_ci} 26048c2ecf20Sopenharmony_ci 26058c2ecf20Sopenharmony_ci/** 26068c2ecf20Sopenharmony_ci * snd_soc_dapm_sync_unlocked - scan and power dapm paths 26078c2ecf20Sopenharmony_ci * @dapm: DAPM context 26088c2ecf20Sopenharmony_ci * 26098c2ecf20Sopenharmony_ci * Walks all dapm audio paths and powers widgets according to their 26108c2ecf20Sopenharmony_ci * stream or path usage. 26118c2ecf20Sopenharmony_ci * 26128c2ecf20Sopenharmony_ci * Requires external locking. 26138c2ecf20Sopenharmony_ci * 26148c2ecf20Sopenharmony_ci * Returns 0 for success. 26158c2ecf20Sopenharmony_ci */ 26168c2ecf20Sopenharmony_ciint snd_soc_dapm_sync_unlocked(struct snd_soc_dapm_context *dapm) 26178c2ecf20Sopenharmony_ci{ 26188c2ecf20Sopenharmony_ci /* 26198c2ecf20Sopenharmony_ci * Suppress early reports (eg, jacks syncing their state) to avoid 26208c2ecf20Sopenharmony_ci * silly DAPM runs during card startup. 26218c2ecf20Sopenharmony_ci */ 26228c2ecf20Sopenharmony_ci if (!dapm->card || !dapm->card->instantiated) 26238c2ecf20Sopenharmony_ci return 0; 26248c2ecf20Sopenharmony_ci 26258c2ecf20Sopenharmony_ci return dapm_power_widgets(dapm->card, SND_SOC_DAPM_STREAM_NOP); 26268c2ecf20Sopenharmony_ci} 26278c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_sync_unlocked); 26288c2ecf20Sopenharmony_ci 26298c2ecf20Sopenharmony_ci/** 26308c2ecf20Sopenharmony_ci * snd_soc_dapm_sync - scan and power dapm paths 26318c2ecf20Sopenharmony_ci * @dapm: DAPM context 26328c2ecf20Sopenharmony_ci * 26338c2ecf20Sopenharmony_ci * Walks all dapm audio paths and powers widgets according to their 26348c2ecf20Sopenharmony_ci * stream or path usage. 26358c2ecf20Sopenharmony_ci * 26368c2ecf20Sopenharmony_ci * Returns 0 for success. 26378c2ecf20Sopenharmony_ci */ 26388c2ecf20Sopenharmony_ciint snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm) 26398c2ecf20Sopenharmony_ci{ 26408c2ecf20Sopenharmony_ci int ret; 26418c2ecf20Sopenharmony_ci 26428c2ecf20Sopenharmony_ci mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); 26438c2ecf20Sopenharmony_ci ret = snd_soc_dapm_sync_unlocked(dapm); 26448c2ecf20Sopenharmony_ci mutex_unlock(&dapm->card->dapm_mutex); 26458c2ecf20Sopenharmony_ci return ret; 26468c2ecf20Sopenharmony_ci} 26478c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_sync); 26488c2ecf20Sopenharmony_ci 26498c2ecf20Sopenharmony_cistatic int dapm_update_dai_chan(struct snd_soc_dapm_path *p, 26508c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *w, 26518c2ecf20Sopenharmony_ci int channels) 26528c2ecf20Sopenharmony_ci{ 26538c2ecf20Sopenharmony_ci switch (w->id) { 26548c2ecf20Sopenharmony_ci case snd_soc_dapm_aif_out: 26558c2ecf20Sopenharmony_ci case snd_soc_dapm_aif_in: 26568c2ecf20Sopenharmony_ci break; 26578c2ecf20Sopenharmony_ci default: 26588c2ecf20Sopenharmony_ci return 0; 26598c2ecf20Sopenharmony_ci } 26608c2ecf20Sopenharmony_ci 26618c2ecf20Sopenharmony_ci dev_dbg(w->dapm->dev, "%s DAI route %s -> %s\n", 26628c2ecf20Sopenharmony_ci w->channel < channels ? "Connecting" : "Disconnecting", 26638c2ecf20Sopenharmony_ci p->source->name, p->sink->name); 26648c2ecf20Sopenharmony_ci 26658c2ecf20Sopenharmony_ci if (w->channel < channels) 26668c2ecf20Sopenharmony_ci soc_dapm_connect_path(p, true, "dai update"); 26678c2ecf20Sopenharmony_ci else 26688c2ecf20Sopenharmony_ci soc_dapm_connect_path(p, false, "dai update"); 26698c2ecf20Sopenharmony_ci 26708c2ecf20Sopenharmony_ci return 0; 26718c2ecf20Sopenharmony_ci} 26728c2ecf20Sopenharmony_ci 26738c2ecf20Sopenharmony_cistatic int dapm_update_dai_unlocked(struct snd_pcm_substream *substream, 26748c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params, 26758c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 26768c2ecf20Sopenharmony_ci{ 26778c2ecf20Sopenharmony_ci int dir = substream->stream; 26788c2ecf20Sopenharmony_ci int channels = params_channels(params); 26798c2ecf20Sopenharmony_ci struct snd_soc_dapm_path *p; 26808c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *w; 26818c2ecf20Sopenharmony_ci int ret; 26828c2ecf20Sopenharmony_ci 26838c2ecf20Sopenharmony_ci w = snd_soc_dai_get_widget(dai, dir); 26848c2ecf20Sopenharmony_ci 26858c2ecf20Sopenharmony_ci if (!w) 26868c2ecf20Sopenharmony_ci return 0; 26878c2ecf20Sopenharmony_ci 26888c2ecf20Sopenharmony_ci dev_dbg(dai->dev, "Update DAI routes for %s %s\n", dai->name, 26898c2ecf20Sopenharmony_ci dir == SNDRV_PCM_STREAM_PLAYBACK ? "playback" : "capture"); 26908c2ecf20Sopenharmony_ci 26918c2ecf20Sopenharmony_ci snd_soc_dapm_widget_for_each_sink_path(w, p) { 26928c2ecf20Sopenharmony_ci ret = dapm_update_dai_chan(p, p->sink, channels); 26938c2ecf20Sopenharmony_ci if (ret < 0) 26948c2ecf20Sopenharmony_ci return ret; 26958c2ecf20Sopenharmony_ci } 26968c2ecf20Sopenharmony_ci 26978c2ecf20Sopenharmony_ci snd_soc_dapm_widget_for_each_source_path(w, p) { 26988c2ecf20Sopenharmony_ci ret = dapm_update_dai_chan(p, p->source, channels); 26998c2ecf20Sopenharmony_ci if (ret < 0) 27008c2ecf20Sopenharmony_ci return ret; 27018c2ecf20Sopenharmony_ci } 27028c2ecf20Sopenharmony_ci 27038c2ecf20Sopenharmony_ci return 0; 27048c2ecf20Sopenharmony_ci} 27058c2ecf20Sopenharmony_ci 27068c2ecf20Sopenharmony_ciint snd_soc_dapm_update_dai(struct snd_pcm_substream *substream, 27078c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params, 27088c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 27098c2ecf20Sopenharmony_ci{ 27108c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 27118c2ecf20Sopenharmony_ci int ret; 27128c2ecf20Sopenharmony_ci 27138c2ecf20Sopenharmony_ci mutex_lock_nested(&rtd->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); 27148c2ecf20Sopenharmony_ci ret = dapm_update_dai_unlocked(substream, params, dai); 27158c2ecf20Sopenharmony_ci mutex_unlock(&rtd->card->dapm_mutex); 27168c2ecf20Sopenharmony_ci 27178c2ecf20Sopenharmony_ci return ret; 27188c2ecf20Sopenharmony_ci} 27198c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_update_dai); 27208c2ecf20Sopenharmony_ci 27218c2ecf20Sopenharmony_ci/* 27228c2ecf20Sopenharmony_ci * dapm_update_widget_flags() - Re-compute widget sink and source flags 27238c2ecf20Sopenharmony_ci * @w: The widget for which to update the flags 27248c2ecf20Sopenharmony_ci * 27258c2ecf20Sopenharmony_ci * Some widgets have a dynamic category which depends on which neighbors they 27268c2ecf20Sopenharmony_ci * are connected to. This function update the category for these widgets. 27278c2ecf20Sopenharmony_ci * 27288c2ecf20Sopenharmony_ci * This function must be called whenever a path is added or removed to a widget. 27298c2ecf20Sopenharmony_ci */ 27308c2ecf20Sopenharmony_cistatic void dapm_update_widget_flags(struct snd_soc_dapm_widget *w) 27318c2ecf20Sopenharmony_ci{ 27328c2ecf20Sopenharmony_ci enum snd_soc_dapm_direction dir; 27338c2ecf20Sopenharmony_ci struct snd_soc_dapm_path *p; 27348c2ecf20Sopenharmony_ci unsigned int ep; 27358c2ecf20Sopenharmony_ci 27368c2ecf20Sopenharmony_ci switch (w->id) { 27378c2ecf20Sopenharmony_ci case snd_soc_dapm_input: 27388c2ecf20Sopenharmony_ci /* On a fully routed card an input is never a source */ 27398c2ecf20Sopenharmony_ci if (w->dapm->card->fully_routed) 27408c2ecf20Sopenharmony_ci return; 27418c2ecf20Sopenharmony_ci ep = SND_SOC_DAPM_EP_SOURCE; 27428c2ecf20Sopenharmony_ci snd_soc_dapm_widget_for_each_source_path(w, p) { 27438c2ecf20Sopenharmony_ci if (p->source->id == snd_soc_dapm_micbias || 27448c2ecf20Sopenharmony_ci p->source->id == snd_soc_dapm_mic || 27458c2ecf20Sopenharmony_ci p->source->id == snd_soc_dapm_line || 27468c2ecf20Sopenharmony_ci p->source->id == snd_soc_dapm_output) { 27478c2ecf20Sopenharmony_ci ep = 0; 27488c2ecf20Sopenharmony_ci break; 27498c2ecf20Sopenharmony_ci } 27508c2ecf20Sopenharmony_ci } 27518c2ecf20Sopenharmony_ci break; 27528c2ecf20Sopenharmony_ci case snd_soc_dapm_output: 27538c2ecf20Sopenharmony_ci /* On a fully routed card a output is never a sink */ 27548c2ecf20Sopenharmony_ci if (w->dapm->card->fully_routed) 27558c2ecf20Sopenharmony_ci return; 27568c2ecf20Sopenharmony_ci ep = SND_SOC_DAPM_EP_SINK; 27578c2ecf20Sopenharmony_ci snd_soc_dapm_widget_for_each_sink_path(w, p) { 27588c2ecf20Sopenharmony_ci if (p->sink->id == snd_soc_dapm_spk || 27598c2ecf20Sopenharmony_ci p->sink->id == snd_soc_dapm_hp || 27608c2ecf20Sopenharmony_ci p->sink->id == snd_soc_dapm_line || 27618c2ecf20Sopenharmony_ci p->sink->id == snd_soc_dapm_input) { 27628c2ecf20Sopenharmony_ci ep = 0; 27638c2ecf20Sopenharmony_ci break; 27648c2ecf20Sopenharmony_ci } 27658c2ecf20Sopenharmony_ci } 27668c2ecf20Sopenharmony_ci break; 27678c2ecf20Sopenharmony_ci case snd_soc_dapm_line: 27688c2ecf20Sopenharmony_ci ep = 0; 27698c2ecf20Sopenharmony_ci snd_soc_dapm_for_each_direction(dir) { 27708c2ecf20Sopenharmony_ci if (!list_empty(&w->edges[dir])) 27718c2ecf20Sopenharmony_ci ep |= SND_SOC_DAPM_DIR_TO_EP(dir); 27728c2ecf20Sopenharmony_ci } 27738c2ecf20Sopenharmony_ci break; 27748c2ecf20Sopenharmony_ci default: 27758c2ecf20Sopenharmony_ci return; 27768c2ecf20Sopenharmony_ci } 27778c2ecf20Sopenharmony_ci 27788c2ecf20Sopenharmony_ci w->is_ep = ep; 27798c2ecf20Sopenharmony_ci} 27808c2ecf20Sopenharmony_ci 27818c2ecf20Sopenharmony_cistatic int snd_soc_dapm_check_dynamic_path(struct snd_soc_dapm_context *dapm, 27828c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *source, struct snd_soc_dapm_widget *sink, 27838c2ecf20Sopenharmony_ci const char *control) 27848c2ecf20Sopenharmony_ci{ 27858c2ecf20Sopenharmony_ci bool dynamic_source = false; 27868c2ecf20Sopenharmony_ci bool dynamic_sink = false; 27878c2ecf20Sopenharmony_ci 27888c2ecf20Sopenharmony_ci if (!control) 27898c2ecf20Sopenharmony_ci return 0; 27908c2ecf20Sopenharmony_ci 27918c2ecf20Sopenharmony_ci switch (source->id) { 27928c2ecf20Sopenharmony_ci case snd_soc_dapm_demux: 27938c2ecf20Sopenharmony_ci dynamic_source = true; 27948c2ecf20Sopenharmony_ci break; 27958c2ecf20Sopenharmony_ci default: 27968c2ecf20Sopenharmony_ci break; 27978c2ecf20Sopenharmony_ci } 27988c2ecf20Sopenharmony_ci 27998c2ecf20Sopenharmony_ci switch (sink->id) { 28008c2ecf20Sopenharmony_ci case snd_soc_dapm_mux: 28018c2ecf20Sopenharmony_ci case snd_soc_dapm_switch: 28028c2ecf20Sopenharmony_ci case snd_soc_dapm_mixer: 28038c2ecf20Sopenharmony_ci case snd_soc_dapm_mixer_named_ctl: 28048c2ecf20Sopenharmony_ci dynamic_sink = true; 28058c2ecf20Sopenharmony_ci break; 28068c2ecf20Sopenharmony_ci default: 28078c2ecf20Sopenharmony_ci break; 28088c2ecf20Sopenharmony_ci } 28098c2ecf20Sopenharmony_ci 28108c2ecf20Sopenharmony_ci if (dynamic_source && dynamic_sink) { 28118c2ecf20Sopenharmony_ci dev_err(dapm->dev, 28128c2ecf20Sopenharmony_ci "Direct connection between demux and mixer/mux not supported for path %s -> [%s] -> %s\n", 28138c2ecf20Sopenharmony_ci source->name, control, sink->name); 28148c2ecf20Sopenharmony_ci return -EINVAL; 28158c2ecf20Sopenharmony_ci } else if (!dynamic_source && !dynamic_sink) { 28168c2ecf20Sopenharmony_ci dev_err(dapm->dev, 28178c2ecf20Sopenharmony_ci "Control not supported for path %s -> [%s] -> %s\n", 28188c2ecf20Sopenharmony_ci source->name, control, sink->name); 28198c2ecf20Sopenharmony_ci return -EINVAL; 28208c2ecf20Sopenharmony_ci } 28218c2ecf20Sopenharmony_ci 28228c2ecf20Sopenharmony_ci return 0; 28238c2ecf20Sopenharmony_ci} 28248c2ecf20Sopenharmony_ci 28258c2ecf20Sopenharmony_cistatic int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, 28268c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink, 28278c2ecf20Sopenharmony_ci const char *control, 28288c2ecf20Sopenharmony_ci int (*connected)(struct snd_soc_dapm_widget *source, 28298c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *sink)) 28308c2ecf20Sopenharmony_ci{ 28318c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *widgets[2]; 28328c2ecf20Sopenharmony_ci enum snd_soc_dapm_direction dir; 28338c2ecf20Sopenharmony_ci struct snd_soc_dapm_path *path; 28348c2ecf20Sopenharmony_ci int ret; 28358c2ecf20Sopenharmony_ci 28368c2ecf20Sopenharmony_ci if (wsink->is_supply && !wsource->is_supply) { 28378c2ecf20Sopenharmony_ci dev_err(dapm->dev, 28388c2ecf20Sopenharmony_ci "Connecting non-supply widget to supply widget is not supported (%s -> %s)\n", 28398c2ecf20Sopenharmony_ci wsource->name, wsink->name); 28408c2ecf20Sopenharmony_ci return -EINVAL; 28418c2ecf20Sopenharmony_ci } 28428c2ecf20Sopenharmony_ci 28438c2ecf20Sopenharmony_ci if (connected && !wsource->is_supply) { 28448c2ecf20Sopenharmony_ci dev_err(dapm->dev, 28458c2ecf20Sopenharmony_ci "connected() callback only supported for supply widgets (%s -> %s)\n", 28468c2ecf20Sopenharmony_ci wsource->name, wsink->name); 28478c2ecf20Sopenharmony_ci return -EINVAL; 28488c2ecf20Sopenharmony_ci } 28498c2ecf20Sopenharmony_ci 28508c2ecf20Sopenharmony_ci if (wsource->is_supply && control) { 28518c2ecf20Sopenharmony_ci dev_err(dapm->dev, 28528c2ecf20Sopenharmony_ci "Conditional paths are not supported for supply widgets (%s -> [%s] -> %s)\n", 28538c2ecf20Sopenharmony_ci wsource->name, control, wsink->name); 28548c2ecf20Sopenharmony_ci return -EINVAL; 28558c2ecf20Sopenharmony_ci } 28568c2ecf20Sopenharmony_ci 28578c2ecf20Sopenharmony_ci ret = snd_soc_dapm_check_dynamic_path(dapm, wsource, wsink, control); 28588c2ecf20Sopenharmony_ci if (ret) 28598c2ecf20Sopenharmony_ci return ret; 28608c2ecf20Sopenharmony_ci 28618c2ecf20Sopenharmony_ci path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL); 28628c2ecf20Sopenharmony_ci if (!path) 28638c2ecf20Sopenharmony_ci return -ENOMEM; 28648c2ecf20Sopenharmony_ci 28658c2ecf20Sopenharmony_ci path->node[SND_SOC_DAPM_DIR_IN] = wsource; 28668c2ecf20Sopenharmony_ci path->node[SND_SOC_DAPM_DIR_OUT] = wsink; 28678c2ecf20Sopenharmony_ci widgets[SND_SOC_DAPM_DIR_IN] = wsource; 28688c2ecf20Sopenharmony_ci widgets[SND_SOC_DAPM_DIR_OUT] = wsink; 28698c2ecf20Sopenharmony_ci 28708c2ecf20Sopenharmony_ci path->connected = connected; 28718c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&path->list); 28728c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&path->list_kcontrol); 28738c2ecf20Sopenharmony_ci 28748c2ecf20Sopenharmony_ci if (wsource->is_supply || wsink->is_supply) 28758c2ecf20Sopenharmony_ci path->is_supply = 1; 28768c2ecf20Sopenharmony_ci 28778c2ecf20Sopenharmony_ci /* connect static paths */ 28788c2ecf20Sopenharmony_ci if (control == NULL) { 28798c2ecf20Sopenharmony_ci path->connect = 1; 28808c2ecf20Sopenharmony_ci } else { 28818c2ecf20Sopenharmony_ci switch (wsource->id) { 28828c2ecf20Sopenharmony_ci case snd_soc_dapm_demux: 28838c2ecf20Sopenharmony_ci ret = dapm_connect_mux(dapm, path, control, wsource); 28848c2ecf20Sopenharmony_ci if (ret) 28858c2ecf20Sopenharmony_ci goto err; 28868c2ecf20Sopenharmony_ci break; 28878c2ecf20Sopenharmony_ci default: 28888c2ecf20Sopenharmony_ci break; 28898c2ecf20Sopenharmony_ci } 28908c2ecf20Sopenharmony_ci 28918c2ecf20Sopenharmony_ci switch (wsink->id) { 28928c2ecf20Sopenharmony_ci case snd_soc_dapm_mux: 28938c2ecf20Sopenharmony_ci ret = dapm_connect_mux(dapm, path, control, wsink); 28948c2ecf20Sopenharmony_ci if (ret != 0) 28958c2ecf20Sopenharmony_ci goto err; 28968c2ecf20Sopenharmony_ci break; 28978c2ecf20Sopenharmony_ci case snd_soc_dapm_switch: 28988c2ecf20Sopenharmony_ci case snd_soc_dapm_mixer: 28998c2ecf20Sopenharmony_ci case snd_soc_dapm_mixer_named_ctl: 29008c2ecf20Sopenharmony_ci ret = dapm_connect_mixer(dapm, path, control); 29018c2ecf20Sopenharmony_ci if (ret != 0) 29028c2ecf20Sopenharmony_ci goto err; 29038c2ecf20Sopenharmony_ci break; 29048c2ecf20Sopenharmony_ci default: 29058c2ecf20Sopenharmony_ci break; 29068c2ecf20Sopenharmony_ci } 29078c2ecf20Sopenharmony_ci } 29088c2ecf20Sopenharmony_ci 29098c2ecf20Sopenharmony_ci list_add(&path->list, &dapm->card->paths); 29108c2ecf20Sopenharmony_ci snd_soc_dapm_for_each_direction(dir) 29118c2ecf20Sopenharmony_ci list_add(&path->list_node[dir], &widgets[dir]->edges[dir]); 29128c2ecf20Sopenharmony_ci 29138c2ecf20Sopenharmony_ci snd_soc_dapm_for_each_direction(dir) { 29148c2ecf20Sopenharmony_ci dapm_update_widget_flags(widgets[dir]); 29158c2ecf20Sopenharmony_ci dapm_mark_dirty(widgets[dir], "Route added"); 29168c2ecf20Sopenharmony_ci } 29178c2ecf20Sopenharmony_ci 29188c2ecf20Sopenharmony_ci if (dapm->card->instantiated && path->connect) 29198c2ecf20Sopenharmony_ci dapm_path_invalidate(path); 29208c2ecf20Sopenharmony_ci 29218c2ecf20Sopenharmony_ci return 0; 29228c2ecf20Sopenharmony_cierr: 29238c2ecf20Sopenharmony_ci kfree(path); 29248c2ecf20Sopenharmony_ci return ret; 29258c2ecf20Sopenharmony_ci} 29268c2ecf20Sopenharmony_ci 29278c2ecf20Sopenharmony_cistatic int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, 29288c2ecf20Sopenharmony_ci const struct snd_soc_dapm_route *route) 29298c2ecf20Sopenharmony_ci{ 29308c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w; 29318c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *wtsource = NULL, *wtsink = NULL; 29328c2ecf20Sopenharmony_ci const char *sink; 29338c2ecf20Sopenharmony_ci const char *source; 29348c2ecf20Sopenharmony_ci char prefixed_sink[80]; 29358c2ecf20Sopenharmony_ci char prefixed_source[80]; 29368c2ecf20Sopenharmony_ci const char *prefix; 29378c2ecf20Sopenharmony_ci unsigned int sink_ref = 0; 29388c2ecf20Sopenharmony_ci unsigned int source_ref = 0; 29398c2ecf20Sopenharmony_ci int ret; 29408c2ecf20Sopenharmony_ci 29418c2ecf20Sopenharmony_ci prefix = soc_dapm_prefix(dapm); 29428c2ecf20Sopenharmony_ci if (prefix) { 29438c2ecf20Sopenharmony_ci snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s", 29448c2ecf20Sopenharmony_ci prefix, route->sink); 29458c2ecf20Sopenharmony_ci sink = prefixed_sink; 29468c2ecf20Sopenharmony_ci snprintf(prefixed_source, sizeof(prefixed_source), "%s %s", 29478c2ecf20Sopenharmony_ci prefix, route->source); 29488c2ecf20Sopenharmony_ci source = prefixed_source; 29498c2ecf20Sopenharmony_ci } else { 29508c2ecf20Sopenharmony_ci sink = route->sink; 29518c2ecf20Sopenharmony_ci source = route->source; 29528c2ecf20Sopenharmony_ci } 29538c2ecf20Sopenharmony_ci 29548c2ecf20Sopenharmony_ci wsource = dapm_wcache_lookup(&dapm->path_source_cache, source); 29558c2ecf20Sopenharmony_ci wsink = dapm_wcache_lookup(&dapm->path_sink_cache, sink); 29568c2ecf20Sopenharmony_ci 29578c2ecf20Sopenharmony_ci if (wsink && wsource) 29588c2ecf20Sopenharmony_ci goto skip_search; 29598c2ecf20Sopenharmony_ci 29608c2ecf20Sopenharmony_ci /* 29618c2ecf20Sopenharmony_ci * find src and dest widgets over all widgets but favor a widget from 29628c2ecf20Sopenharmony_ci * current DAPM context 29638c2ecf20Sopenharmony_ci */ 29648c2ecf20Sopenharmony_ci for_each_card_widgets(dapm->card, w) { 29658c2ecf20Sopenharmony_ci if (!wsink && !(strcmp(w->name, sink))) { 29668c2ecf20Sopenharmony_ci wtsink = w; 29678c2ecf20Sopenharmony_ci if (w->dapm == dapm) { 29688c2ecf20Sopenharmony_ci wsink = w; 29698c2ecf20Sopenharmony_ci if (wsource) 29708c2ecf20Sopenharmony_ci break; 29718c2ecf20Sopenharmony_ci } 29728c2ecf20Sopenharmony_ci sink_ref++; 29738c2ecf20Sopenharmony_ci if (sink_ref > 1) 29748c2ecf20Sopenharmony_ci dev_warn(dapm->dev, 29758c2ecf20Sopenharmony_ci "ASoC: sink widget %s overwritten\n", 29768c2ecf20Sopenharmony_ci w->name); 29778c2ecf20Sopenharmony_ci continue; 29788c2ecf20Sopenharmony_ci } 29798c2ecf20Sopenharmony_ci if (!wsource && !(strcmp(w->name, source))) { 29808c2ecf20Sopenharmony_ci wtsource = w; 29818c2ecf20Sopenharmony_ci if (w->dapm == dapm) { 29828c2ecf20Sopenharmony_ci wsource = w; 29838c2ecf20Sopenharmony_ci if (wsink) 29848c2ecf20Sopenharmony_ci break; 29858c2ecf20Sopenharmony_ci } 29868c2ecf20Sopenharmony_ci source_ref++; 29878c2ecf20Sopenharmony_ci if (source_ref > 1) 29888c2ecf20Sopenharmony_ci dev_warn(dapm->dev, 29898c2ecf20Sopenharmony_ci "ASoC: source widget %s overwritten\n", 29908c2ecf20Sopenharmony_ci w->name); 29918c2ecf20Sopenharmony_ci } 29928c2ecf20Sopenharmony_ci } 29938c2ecf20Sopenharmony_ci /* use widget from another DAPM context if not found from this */ 29948c2ecf20Sopenharmony_ci if (!wsink) 29958c2ecf20Sopenharmony_ci wsink = wtsink; 29968c2ecf20Sopenharmony_ci if (!wsource) 29978c2ecf20Sopenharmony_ci wsource = wtsource; 29988c2ecf20Sopenharmony_ci 29998c2ecf20Sopenharmony_ci if (wsource == NULL) { 30008c2ecf20Sopenharmony_ci dev_err(dapm->dev, "ASoC: no source widget found for %s\n", 30018c2ecf20Sopenharmony_ci route->source); 30028c2ecf20Sopenharmony_ci return -ENODEV; 30038c2ecf20Sopenharmony_ci } 30048c2ecf20Sopenharmony_ci if (wsink == NULL) { 30058c2ecf20Sopenharmony_ci dev_err(dapm->dev, "ASoC: no sink widget found for %s\n", 30068c2ecf20Sopenharmony_ci route->sink); 30078c2ecf20Sopenharmony_ci return -ENODEV; 30088c2ecf20Sopenharmony_ci } 30098c2ecf20Sopenharmony_ci 30108c2ecf20Sopenharmony_ciskip_search: 30118c2ecf20Sopenharmony_ci dapm_wcache_update(&dapm->path_sink_cache, wsink); 30128c2ecf20Sopenharmony_ci dapm_wcache_update(&dapm->path_source_cache, wsource); 30138c2ecf20Sopenharmony_ci 30148c2ecf20Sopenharmony_ci ret = snd_soc_dapm_add_path(dapm, wsource, wsink, route->control, 30158c2ecf20Sopenharmony_ci route->connected); 30168c2ecf20Sopenharmony_ci if (ret) 30178c2ecf20Sopenharmony_ci goto err; 30188c2ecf20Sopenharmony_ci 30198c2ecf20Sopenharmony_ci return 0; 30208c2ecf20Sopenharmony_cierr: 30218c2ecf20Sopenharmony_ci dev_warn(dapm->dev, "ASoC: no dapm match for %s --> %s --> %s\n", 30228c2ecf20Sopenharmony_ci source, route->control, sink); 30238c2ecf20Sopenharmony_ci return ret; 30248c2ecf20Sopenharmony_ci} 30258c2ecf20Sopenharmony_ci 30268c2ecf20Sopenharmony_cistatic int snd_soc_dapm_del_route(struct snd_soc_dapm_context *dapm, 30278c2ecf20Sopenharmony_ci const struct snd_soc_dapm_route *route) 30288c2ecf20Sopenharmony_ci{ 30298c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *wsource, *wsink; 30308c2ecf20Sopenharmony_ci struct snd_soc_dapm_path *path, *p; 30318c2ecf20Sopenharmony_ci const char *sink; 30328c2ecf20Sopenharmony_ci const char *source; 30338c2ecf20Sopenharmony_ci char prefixed_sink[80]; 30348c2ecf20Sopenharmony_ci char prefixed_source[80]; 30358c2ecf20Sopenharmony_ci const char *prefix; 30368c2ecf20Sopenharmony_ci 30378c2ecf20Sopenharmony_ci if (route->control) { 30388c2ecf20Sopenharmony_ci dev_err(dapm->dev, 30398c2ecf20Sopenharmony_ci "ASoC: Removal of routes with controls not supported\n"); 30408c2ecf20Sopenharmony_ci return -EINVAL; 30418c2ecf20Sopenharmony_ci } 30428c2ecf20Sopenharmony_ci 30438c2ecf20Sopenharmony_ci prefix = soc_dapm_prefix(dapm); 30448c2ecf20Sopenharmony_ci if (prefix) { 30458c2ecf20Sopenharmony_ci snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s", 30468c2ecf20Sopenharmony_ci prefix, route->sink); 30478c2ecf20Sopenharmony_ci sink = prefixed_sink; 30488c2ecf20Sopenharmony_ci snprintf(prefixed_source, sizeof(prefixed_source), "%s %s", 30498c2ecf20Sopenharmony_ci prefix, route->source); 30508c2ecf20Sopenharmony_ci source = prefixed_source; 30518c2ecf20Sopenharmony_ci } else { 30528c2ecf20Sopenharmony_ci sink = route->sink; 30538c2ecf20Sopenharmony_ci source = route->source; 30548c2ecf20Sopenharmony_ci } 30558c2ecf20Sopenharmony_ci 30568c2ecf20Sopenharmony_ci path = NULL; 30578c2ecf20Sopenharmony_ci list_for_each_entry(p, &dapm->card->paths, list) { 30588c2ecf20Sopenharmony_ci if (strcmp(p->source->name, source) != 0) 30598c2ecf20Sopenharmony_ci continue; 30608c2ecf20Sopenharmony_ci if (strcmp(p->sink->name, sink) != 0) 30618c2ecf20Sopenharmony_ci continue; 30628c2ecf20Sopenharmony_ci path = p; 30638c2ecf20Sopenharmony_ci break; 30648c2ecf20Sopenharmony_ci } 30658c2ecf20Sopenharmony_ci 30668c2ecf20Sopenharmony_ci if (path) { 30678c2ecf20Sopenharmony_ci wsource = path->source; 30688c2ecf20Sopenharmony_ci wsink = path->sink; 30698c2ecf20Sopenharmony_ci 30708c2ecf20Sopenharmony_ci dapm_mark_dirty(wsource, "Route removed"); 30718c2ecf20Sopenharmony_ci dapm_mark_dirty(wsink, "Route removed"); 30728c2ecf20Sopenharmony_ci if (path->connect) 30738c2ecf20Sopenharmony_ci dapm_path_invalidate(path); 30748c2ecf20Sopenharmony_ci 30758c2ecf20Sopenharmony_ci dapm_free_path(path); 30768c2ecf20Sopenharmony_ci 30778c2ecf20Sopenharmony_ci /* Update any path related flags */ 30788c2ecf20Sopenharmony_ci dapm_update_widget_flags(wsource); 30798c2ecf20Sopenharmony_ci dapm_update_widget_flags(wsink); 30808c2ecf20Sopenharmony_ci } else { 30818c2ecf20Sopenharmony_ci dev_warn(dapm->dev, "ASoC: Route %s->%s does not exist\n", 30828c2ecf20Sopenharmony_ci source, sink); 30838c2ecf20Sopenharmony_ci } 30848c2ecf20Sopenharmony_ci 30858c2ecf20Sopenharmony_ci return 0; 30868c2ecf20Sopenharmony_ci} 30878c2ecf20Sopenharmony_ci 30888c2ecf20Sopenharmony_ci/** 30898c2ecf20Sopenharmony_ci * snd_soc_dapm_add_routes - Add routes between DAPM widgets 30908c2ecf20Sopenharmony_ci * @dapm: DAPM context 30918c2ecf20Sopenharmony_ci * @route: audio routes 30928c2ecf20Sopenharmony_ci * @num: number of routes 30938c2ecf20Sopenharmony_ci * 30948c2ecf20Sopenharmony_ci * Connects 2 dapm widgets together via a named audio path. The sink is 30958c2ecf20Sopenharmony_ci * the widget receiving the audio signal, whilst the source is the sender 30968c2ecf20Sopenharmony_ci * of the audio signal. 30978c2ecf20Sopenharmony_ci * 30988c2ecf20Sopenharmony_ci * Returns 0 for success else error. On error all resources can be freed 30998c2ecf20Sopenharmony_ci * with a call to snd_soc_card_free(). 31008c2ecf20Sopenharmony_ci */ 31018c2ecf20Sopenharmony_ciint snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm, 31028c2ecf20Sopenharmony_ci const struct snd_soc_dapm_route *route, int num) 31038c2ecf20Sopenharmony_ci{ 31048c2ecf20Sopenharmony_ci int i, r, ret = 0; 31058c2ecf20Sopenharmony_ci 31068c2ecf20Sopenharmony_ci mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); 31078c2ecf20Sopenharmony_ci for (i = 0; i < num; i++) { 31088c2ecf20Sopenharmony_ci r = snd_soc_dapm_add_route(dapm, route); 31098c2ecf20Sopenharmony_ci if (r < 0) { 31108c2ecf20Sopenharmony_ci dev_err(dapm->dev, "ASoC: Failed to add route %s -> %s -> %s\n", 31118c2ecf20Sopenharmony_ci route->source, 31128c2ecf20Sopenharmony_ci route->control ? route->control : "direct", 31138c2ecf20Sopenharmony_ci route->sink); 31148c2ecf20Sopenharmony_ci ret = r; 31158c2ecf20Sopenharmony_ci } 31168c2ecf20Sopenharmony_ci route++; 31178c2ecf20Sopenharmony_ci } 31188c2ecf20Sopenharmony_ci mutex_unlock(&dapm->card->dapm_mutex); 31198c2ecf20Sopenharmony_ci 31208c2ecf20Sopenharmony_ci return ret; 31218c2ecf20Sopenharmony_ci} 31228c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_add_routes); 31238c2ecf20Sopenharmony_ci 31248c2ecf20Sopenharmony_ci/** 31258c2ecf20Sopenharmony_ci * snd_soc_dapm_del_routes - Remove routes between DAPM widgets 31268c2ecf20Sopenharmony_ci * @dapm: DAPM context 31278c2ecf20Sopenharmony_ci * @route: audio routes 31288c2ecf20Sopenharmony_ci * @num: number of routes 31298c2ecf20Sopenharmony_ci * 31308c2ecf20Sopenharmony_ci * Removes routes from the DAPM context. 31318c2ecf20Sopenharmony_ci */ 31328c2ecf20Sopenharmony_ciint snd_soc_dapm_del_routes(struct snd_soc_dapm_context *dapm, 31338c2ecf20Sopenharmony_ci const struct snd_soc_dapm_route *route, int num) 31348c2ecf20Sopenharmony_ci{ 31358c2ecf20Sopenharmony_ci int i; 31368c2ecf20Sopenharmony_ci 31378c2ecf20Sopenharmony_ci mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); 31388c2ecf20Sopenharmony_ci for (i = 0; i < num; i++) { 31398c2ecf20Sopenharmony_ci snd_soc_dapm_del_route(dapm, route); 31408c2ecf20Sopenharmony_ci route++; 31418c2ecf20Sopenharmony_ci } 31428c2ecf20Sopenharmony_ci mutex_unlock(&dapm->card->dapm_mutex); 31438c2ecf20Sopenharmony_ci 31448c2ecf20Sopenharmony_ci return 0; 31458c2ecf20Sopenharmony_ci} 31468c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_del_routes); 31478c2ecf20Sopenharmony_ci 31488c2ecf20Sopenharmony_cistatic int snd_soc_dapm_weak_route(struct snd_soc_dapm_context *dapm, 31498c2ecf20Sopenharmony_ci const struct snd_soc_dapm_route *route) 31508c2ecf20Sopenharmony_ci{ 31518c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *source = dapm_find_widget(dapm, 31528c2ecf20Sopenharmony_ci route->source, 31538c2ecf20Sopenharmony_ci true); 31548c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *sink = dapm_find_widget(dapm, 31558c2ecf20Sopenharmony_ci route->sink, 31568c2ecf20Sopenharmony_ci true); 31578c2ecf20Sopenharmony_ci struct snd_soc_dapm_path *path; 31588c2ecf20Sopenharmony_ci int count = 0; 31598c2ecf20Sopenharmony_ci 31608c2ecf20Sopenharmony_ci if (!source) { 31618c2ecf20Sopenharmony_ci dev_err(dapm->dev, "ASoC: Unable to find source %s for weak route\n", 31628c2ecf20Sopenharmony_ci route->source); 31638c2ecf20Sopenharmony_ci return -ENODEV; 31648c2ecf20Sopenharmony_ci } 31658c2ecf20Sopenharmony_ci 31668c2ecf20Sopenharmony_ci if (!sink) { 31678c2ecf20Sopenharmony_ci dev_err(dapm->dev, "ASoC: Unable to find sink %s for weak route\n", 31688c2ecf20Sopenharmony_ci route->sink); 31698c2ecf20Sopenharmony_ci return -ENODEV; 31708c2ecf20Sopenharmony_ci } 31718c2ecf20Sopenharmony_ci 31728c2ecf20Sopenharmony_ci if (route->control || route->connected) 31738c2ecf20Sopenharmony_ci dev_warn(dapm->dev, "ASoC: Ignoring control for weak route %s->%s\n", 31748c2ecf20Sopenharmony_ci route->source, route->sink); 31758c2ecf20Sopenharmony_ci 31768c2ecf20Sopenharmony_ci snd_soc_dapm_widget_for_each_sink_path(source, path) { 31778c2ecf20Sopenharmony_ci if (path->sink == sink) { 31788c2ecf20Sopenharmony_ci path->weak = 1; 31798c2ecf20Sopenharmony_ci count++; 31808c2ecf20Sopenharmony_ci } 31818c2ecf20Sopenharmony_ci } 31828c2ecf20Sopenharmony_ci 31838c2ecf20Sopenharmony_ci if (count == 0) 31848c2ecf20Sopenharmony_ci dev_err(dapm->dev, "ASoC: No path found for weak route %s->%s\n", 31858c2ecf20Sopenharmony_ci route->source, route->sink); 31868c2ecf20Sopenharmony_ci if (count > 1) 31878c2ecf20Sopenharmony_ci dev_warn(dapm->dev, "ASoC: %d paths found for weak route %s->%s\n", 31888c2ecf20Sopenharmony_ci count, route->source, route->sink); 31898c2ecf20Sopenharmony_ci 31908c2ecf20Sopenharmony_ci return 0; 31918c2ecf20Sopenharmony_ci} 31928c2ecf20Sopenharmony_ci 31938c2ecf20Sopenharmony_ci/** 31948c2ecf20Sopenharmony_ci * snd_soc_dapm_weak_routes - Mark routes between DAPM widgets as weak 31958c2ecf20Sopenharmony_ci * @dapm: DAPM context 31968c2ecf20Sopenharmony_ci * @route: audio routes 31978c2ecf20Sopenharmony_ci * @num: number of routes 31988c2ecf20Sopenharmony_ci * 31998c2ecf20Sopenharmony_ci * Mark existing routes matching those specified in the passed array 32008c2ecf20Sopenharmony_ci * as being weak, meaning that they are ignored for the purpose of 32018c2ecf20Sopenharmony_ci * power decisions. The main intended use case is for sidetone paths 32028c2ecf20Sopenharmony_ci * which couple audio between other independent paths if they are both 32038c2ecf20Sopenharmony_ci * active in order to make the combination work better at the user 32048c2ecf20Sopenharmony_ci * level but which aren't intended to be "used". 32058c2ecf20Sopenharmony_ci * 32068c2ecf20Sopenharmony_ci * Note that CODEC drivers should not use this as sidetone type paths 32078c2ecf20Sopenharmony_ci * can frequently also be used as bypass paths. 32088c2ecf20Sopenharmony_ci */ 32098c2ecf20Sopenharmony_ciint snd_soc_dapm_weak_routes(struct snd_soc_dapm_context *dapm, 32108c2ecf20Sopenharmony_ci const struct snd_soc_dapm_route *route, int num) 32118c2ecf20Sopenharmony_ci{ 32128c2ecf20Sopenharmony_ci int i, err; 32138c2ecf20Sopenharmony_ci int ret = 0; 32148c2ecf20Sopenharmony_ci 32158c2ecf20Sopenharmony_ci mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); 32168c2ecf20Sopenharmony_ci for (i = 0; i < num; i++) { 32178c2ecf20Sopenharmony_ci err = snd_soc_dapm_weak_route(dapm, route); 32188c2ecf20Sopenharmony_ci if (err) 32198c2ecf20Sopenharmony_ci ret = err; 32208c2ecf20Sopenharmony_ci route++; 32218c2ecf20Sopenharmony_ci } 32228c2ecf20Sopenharmony_ci mutex_unlock(&dapm->card->dapm_mutex); 32238c2ecf20Sopenharmony_ci 32248c2ecf20Sopenharmony_ci return ret; 32258c2ecf20Sopenharmony_ci} 32268c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_weak_routes); 32278c2ecf20Sopenharmony_ci 32288c2ecf20Sopenharmony_ci/** 32298c2ecf20Sopenharmony_ci * snd_soc_dapm_new_widgets - add new dapm widgets 32308c2ecf20Sopenharmony_ci * @card: card to be checked for new dapm widgets 32318c2ecf20Sopenharmony_ci * 32328c2ecf20Sopenharmony_ci * Checks the codec for any new dapm widgets and creates them if found. 32338c2ecf20Sopenharmony_ci * 32348c2ecf20Sopenharmony_ci * Returns 0 for success. 32358c2ecf20Sopenharmony_ci */ 32368c2ecf20Sopenharmony_ciint snd_soc_dapm_new_widgets(struct snd_soc_card *card) 32378c2ecf20Sopenharmony_ci{ 32388c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *w; 32398c2ecf20Sopenharmony_ci unsigned int val; 32408c2ecf20Sopenharmony_ci 32418c2ecf20Sopenharmony_ci mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); 32428c2ecf20Sopenharmony_ci 32438c2ecf20Sopenharmony_ci for_each_card_widgets(card, w) 32448c2ecf20Sopenharmony_ci { 32458c2ecf20Sopenharmony_ci if (w->new) 32468c2ecf20Sopenharmony_ci continue; 32478c2ecf20Sopenharmony_ci 32488c2ecf20Sopenharmony_ci if (w->num_kcontrols) { 32498c2ecf20Sopenharmony_ci w->kcontrols = kcalloc(w->num_kcontrols, 32508c2ecf20Sopenharmony_ci sizeof(struct snd_kcontrol *), 32518c2ecf20Sopenharmony_ci GFP_KERNEL); 32528c2ecf20Sopenharmony_ci if (!w->kcontrols) { 32538c2ecf20Sopenharmony_ci mutex_unlock(&card->dapm_mutex); 32548c2ecf20Sopenharmony_ci return -ENOMEM; 32558c2ecf20Sopenharmony_ci } 32568c2ecf20Sopenharmony_ci } 32578c2ecf20Sopenharmony_ci 32588c2ecf20Sopenharmony_ci switch(w->id) { 32598c2ecf20Sopenharmony_ci case snd_soc_dapm_switch: 32608c2ecf20Sopenharmony_ci case snd_soc_dapm_mixer: 32618c2ecf20Sopenharmony_ci case snd_soc_dapm_mixer_named_ctl: 32628c2ecf20Sopenharmony_ci dapm_new_mixer(w); 32638c2ecf20Sopenharmony_ci break; 32648c2ecf20Sopenharmony_ci case snd_soc_dapm_mux: 32658c2ecf20Sopenharmony_ci case snd_soc_dapm_demux: 32668c2ecf20Sopenharmony_ci dapm_new_mux(w); 32678c2ecf20Sopenharmony_ci break; 32688c2ecf20Sopenharmony_ci case snd_soc_dapm_pga: 32698c2ecf20Sopenharmony_ci case snd_soc_dapm_effect: 32708c2ecf20Sopenharmony_ci case snd_soc_dapm_out_drv: 32718c2ecf20Sopenharmony_ci dapm_new_pga(w); 32728c2ecf20Sopenharmony_ci break; 32738c2ecf20Sopenharmony_ci case snd_soc_dapm_dai_link: 32748c2ecf20Sopenharmony_ci dapm_new_dai_link(w); 32758c2ecf20Sopenharmony_ci break; 32768c2ecf20Sopenharmony_ci default: 32778c2ecf20Sopenharmony_ci break; 32788c2ecf20Sopenharmony_ci } 32798c2ecf20Sopenharmony_ci 32808c2ecf20Sopenharmony_ci /* Read the initial power state from the device */ 32818c2ecf20Sopenharmony_ci if (w->reg >= 0) { 32828c2ecf20Sopenharmony_ci val = soc_dapm_read(w->dapm, w->reg); 32838c2ecf20Sopenharmony_ci val = val >> w->shift; 32848c2ecf20Sopenharmony_ci val &= w->mask; 32858c2ecf20Sopenharmony_ci if (val == w->on_val) 32868c2ecf20Sopenharmony_ci w->power = 1; 32878c2ecf20Sopenharmony_ci } 32888c2ecf20Sopenharmony_ci 32898c2ecf20Sopenharmony_ci w->new = 1; 32908c2ecf20Sopenharmony_ci 32918c2ecf20Sopenharmony_ci dapm_mark_dirty(w, "new widget"); 32928c2ecf20Sopenharmony_ci dapm_debugfs_add_widget(w); 32938c2ecf20Sopenharmony_ci } 32948c2ecf20Sopenharmony_ci 32958c2ecf20Sopenharmony_ci dapm_power_widgets(card, SND_SOC_DAPM_STREAM_NOP); 32968c2ecf20Sopenharmony_ci mutex_unlock(&card->dapm_mutex); 32978c2ecf20Sopenharmony_ci return 0; 32988c2ecf20Sopenharmony_ci} 32998c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets); 33008c2ecf20Sopenharmony_ci 33018c2ecf20Sopenharmony_ci/** 33028c2ecf20Sopenharmony_ci * snd_soc_dapm_get_volsw - dapm mixer get callback 33038c2ecf20Sopenharmony_ci * @kcontrol: mixer control 33048c2ecf20Sopenharmony_ci * @ucontrol: control element information 33058c2ecf20Sopenharmony_ci * 33068c2ecf20Sopenharmony_ci * Callback to get the value of a dapm mixer control. 33078c2ecf20Sopenharmony_ci * 33088c2ecf20Sopenharmony_ci * Returns 0 for success. 33098c2ecf20Sopenharmony_ci */ 33108c2ecf20Sopenharmony_ciint snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, 33118c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 33128c2ecf20Sopenharmony_ci{ 33138c2ecf20Sopenharmony_ci struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); 33148c2ecf20Sopenharmony_ci struct snd_soc_card *card = dapm->card; 33158c2ecf20Sopenharmony_ci struct soc_mixer_control *mc = 33168c2ecf20Sopenharmony_ci (struct soc_mixer_control *)kcontrol->private_value; 33178c2ecf20Sopenharmony_ci int reg = mc->reg; 33188c2ecf20Sopenharmony_ci unsigned int shift = mc->shift; 33198c2ecf20Sopenharmony_ci int max = mc->max; 33208c2ecf20Sopenharmony_ci unsigned int width = fls(max); 33218c2ecf20Sopenharmony_ci unsigned int mask = (1 << fls(max)) - 1; 33228c2ecf20Sopenharmony_ci unsigned int invert = mc->invert; 33238c2ecf20Sopenharmony_ci unsigned int reg_val, val, rval = 0; 33248c2ecf20Sopenharmony_ci 33258c2ecf20Sopenharmony_ci mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); 33268c2ecf20Sopenharmony_ci if (dapm_kcontrol_is_powered(kcontrol) && reg != SND_SOC_NOPM) { 33278c2ecf20Sopenharmony_ci reg_val = soc_dapm_read(dapm, reg); 33288c2ecf20Sopenharmony_ci val = (reg_val >> shift) & mask; 33298c2ecf20Sopenharmony_ci 33308c2ecf20Sopenharmony_ci if (reg != mc->rreg) 33318c2ecf20Sopenharmony_ci reg_val = soc_dapm_read(dapm, mc->rreg); 33328c2ecf20Sopenharmony_ci 33338c2ecf20Sopenharmony_ci if (snd_soc_volsw_is_stereo(mc)) 33348c2ecf20Sopenharmony_ci rval = (reg_val >> mc->rshift) & mask; 33358c2ecf20Sopenharmony_ci } else { 33368c2ecf20Sopenharmony_ci reg_val = dapm_kcontrol_get_value(kcontrol); 33378c2ecf20Sopenharmony_ci val = reg_val & mask; 33388c2ecf20Sopenharmony_ci 33398c2ecf20Sopenharmony_ci if (snd_soc_volsw_is_stereo(mc)) 33408c2ecf20Sopenharmony_ci rval = (reg_val >> width) & mask; 33418c2ecf20Sopenharmony_ci } 33428c2ecf20Sopenharmony_ci mutex_unlock(&card->dapm_mutex); 33438c2ecf20Sopenharmony_ci 33448c2ecf20Sopenharmony_ci if (invert) 33458c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = max - val; 33468c2ecf20Sopenharmony_ci else 33478c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = val; 33488c2ecf20Sopenharmony_ci 33498c2ecf20Sopenharmony_ci if (snd_soc_volsw_is_stereo(mc)) { 33508c2ecf20Sopenharmony_ci if (invert) 33518c2ecf20Sopenharmony_ci ucontrol->value.integer.value[1] = max - rval; 33528c2ecf20Sopenharmony_ci else 33538c2ecf20Sopenharmony_ci ucontrol->value.integer.value[1] = rval; 33548c2ecf20Sopenharmony_ci } 33558c2ecf20Sopenharmony_ci 33568c2ecf20Sopenharmony_ci return 0; 33578c2ecf20Sopenharmony_ci} 33588c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_get_volsw); 33598c2ecf20Sopenharmony_ci 33608c2ecf20Sopenharmony_ci/** 33618c2ecf20Sopenharmony_ci * snd_soc_dapm_put_volsw - dapm mixer set callback 33628c2ecf20Sopenharmony_ci * @kcontrol: mixer control 33638c2ecf20Sopenharmony_ci * @ucontrol: control element information 33648c2ecf20Sopenharmony_ci * 33658c2ecf20Sopenharmony_ci * Callback to set the value of a dapm mixer control. 33668c2ecf20Sopenharmony_ci * 33678c2ecf20Sopenharmony_ci * Returns 0 for success. 33688c2ecf20Sopenharmony_ci */ 33698c2ecf20Sopenharmony_ciint snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, 33708c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 33718c2ecf20Sopenharmony_ci{ 33728c2ecf20Sopenharmony_ci struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); 33738c2ecf20Sopenharmony_ci struct snd_soc_card *card = dapm->card; 33748c2ecf20Sopenharmony_ci struct soc_mixer_control *mc = 33758c2ecf20Sopenharmony_ci (struct soc_mixer_control *)kcontrol->private_value; 33768c2ecf20Sopenharmony_ci int reg = mc->reg; 33778c2ecf20Sopenharmony_ci unsigned int shift = mc->shift; 33788c2ecf20Sopenharmony_ci int max = mc->max; 33798c2ecf20Sopenharmony_ci unsigned int width = fls(max); 33808c2ecf20Sopenharmony_ci unsigned int mask = (1 << width) - 1; 33818c2ecf20Sopenharmony_ci unsigned int invert = mc->invert; 33828c2ecf20Sopenharmony_ci unsigned int val, rval = 0; 33838c2ecf20Sopenharmony_ci int connect, rconnect = -1, change, reg_change = 0; 33848c2ecf20Sopenharmony_ci struct snd_soc_dapm_update update = {}; 33858c2ecf20Sopenharmony_ci int ret = 0; 33868c2ecf20Sopenharmony_ci 33878c2ecf20Sopenharmony_ci val = (ucontrol->value.integer.value[0] & mask); 33888c2ecf20Sopenharmony_ci connect = !!val; 33898c2ecf20Sopenharmony_ci 33908c2ecf20Sopenharmony_ci if (invert) 33918c2ecf20Sopenharmony_ci val = max - val; 33928c2ecf20Sopenharmony_ci 33938c2ecf20Sopenharmony_ci if (snd_soc_volsw_is_stereo(mc)) { 33948c2ecf20Sopenharmony_ci rval = (ucontrol->value.integer.value[1] & mask); 33958c2ecf20Sopenharmony_ci rconnect = !!rval; 33968c2ecf20Sopenharmony_ci if (invert) 33978c2ecf20Sopenharmony_ci rval = max - rval; 33988c2ecf20Sopenharmony_ci } 33998c2ecf20Sopenharmony_ci 34008c2ecf20Sopenharmony_ci mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); 34018c2ecf20Sopenharmony_ci 34028c2ecf20Sopenharmony_ci /* This assumes field width < (bits in unsigned int / 2) */ 34038c2ecf20Sopenharmony_ci if (width > sizeof(unsigned int) * 8 / 2) 34048c2ecf20Sopenharmony_ci dev_warn(dapm->dev, 34058c2ecf20Sopenharmony_ci "ASoC: control %s field width limit exceeded\n", 34068c2ecf20Sopenharmony_ci kcontrol->id.name); 34078c2ecf20Sopenharmony_ci change = dapm_kcontrol_set_value(kcontrol, val | (rval << width)); 34088c2ecf20Sopenharmony_ci 34098c2ecf20Sopenharmony_ci if (reg != SND_SOC_NOPM) { 34108c2ecf20Sopenharmony_ci val = val << shift; 34118c2ecf20Sopenharmony_ci rval = rval << mc->rshift; 34128c2ecf20Sopenharmony_ci 34138c2ecf20Sopenharmony_ci reg_change = soc_dapm_test_bits(dapm, reg, mask << shift, val); 34148c2ecf20Sopenharmony_ci 34158c2ecf20Sopenharmony_ci if (snd_soc_volsw_is_stereo(mc)) 34168c2ecf20Sopenharmony_ci reg_change |= soc_dapm_test_bits(dapm, mc->rreg, 34178c2ecf20Sopenharmony_ci mask << mc->rshift, 34188c2ecf20Sopenharmony_ci rval); 34198c2ecf20Sopenharmony_ci } 34208c2ecf20Sopenharmony_ci 34218c2ecf20Sopenharmony_ci if (change || reg_change) { 34228c2ecf20Sopenharmony_ci if (reg_change) { 34238c2ecf20Sopenharmony_ci if (snd_soc_volsw_is_stereo(mc)) { 34248c2ecf20Sopenharmony_ci update.has_second_set = true; 34258c2ecf20Sopenharmony_ci update.reg2 = mc->rreg; 34268c2ecf20Sopenharmony_ci update.mask2 = mask << mc->rshift; 34278c2ecf20Sopenharmony_ci update.val2 = rval; 34288c2ecf20Sopenharmony_ci } 34298c2ecf20Sopenharmony_ci update.kcontrol = kcontrol; 34308c2ecf20Sopenharmony_ci update.reg = reg; 34318c2ecf20Sopenharmony_ci update.mask = mask << shift; 34328c2ecf20Sopenharmony_ci update.val = val; 34338c2ecf20Sopenharmony_ci card->update = &update; 34348c2ecf20Sopenharmony_ci } 34358c2ecf20Sopenharmony_ci 34368c2ecf20Sopenharmony_ci ret = soc_dapm_mixer_update_power(card, kcontrol, connect, 34378c2ecf20Sopenharmony_ci rconnect); 34388c2ecf20Sopenharmony_ci 34398c2ecf20Sopenharmony_ci card->update = NULL; 34408c2ecf20Sopenharmony_ci } 34418c2ecf20Sopenharmony_ci 34428c2ecf20Sopenharmony_ci mutex_unlock(&card->dapm_mutex); 34438c2ecf20Sopenharmony_ci 34448c2ecf20Sopenharmony_ci if (ret > 0) 34458c2ecf20Sopenharmony_ci snd_soc_dpcm_runtime_update(card); 34468c2ecf20Sopenharmony_ci 34478c2ecf20Sopenharmony_ci return change; 34488c2ecf20Sopenharmony_ci} 34498c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw); 34508c2ecf20Sopenharmony_ci 34518c2ecf20Sopenharmony_ci/** 34528c2ecf20Sopenharmony_ci * snd_soc_dapm_get_enum_double - dapm enumerated double mixer get callback 34538c2ecf20Sopenharmony_ci * @kcontrol: mixer control 34548c2ecf20Sopenharmony_ci * @ucontrol: control element information 34558c2ecf20Sopenharmony_ci * 34568c2ecf20Sopenharmony_ci * Callback to get the value of a dapm enumerated double mixer control. 34578c2ecf20Sopenharmony_ci * 34588c2ecf20Sopenharmony_ci * Returns 0 for success. 34598c2ecf20Sopenharmony_ci */ 34608c2ecf20Sopenharmony_ciint snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol, 34618c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 34628c2ecf20Sopenharmony_ci{ 34638c2ecf20Sopenharmony_ci struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); 34648c2ecf20Sopenharmony_ci struct snd_soc_card *card = dapm->card; 34658c2ecf20Sopenharmony_ci struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 34668c2ecf20Sopenharmony_ci unsigned int reg_val, val; 34678c2ecf20Sopenharmony_ci 34688c2ecf20Sopenharmony_ci mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); 34698c2ecf20Sopenharmony_ci if (e->reg != SND_SOC_NOPM && dapm_kcontrol_is_powered(kcontrol)) { 34708c2ecf20Sopenharmony_ci reg_val = soc_dapm_read(dapm, e->reg); 34718c2ecf20Sopenharmony_ci } else { 34728c2ecf20Sopenharmony_ci reg_val = dapm_kcontrol_get_value(kcontrol); 34738c2ecf20Sopenharmony_ci } 34748c2ecf20Sopenharmony_ci mutex_unlock(&card->dapm_mutex); 34758c2ecf20Sopenharmony_ci 34768c2ecf20Sopenharmony_ci val = (reg_val >> e->shift_l) & e->mask; 34778c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(e, val); 34788c2ecf20Sopenharmony_ci if (e->shift_l != e->shift_r) { 34798c2ecf20Sopenharmony_ci val = (reg_val >> e->shift_r) & e->mask; 34808c2ecf20Sopenharmony_ci val = snd_soc_enum_val_to_item(e, val); 34818c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[1] = val; 34828c2ecf20Sopenharmony_ci } 34838c2ecf20Sopenharmony_ci 34848c2ecf20Sopenharmony_ci return 0; 34858c2ecf20Sopenharmony_ci} 34868c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_double); 34878c2ecf20Sopenharmony_ci 34888c2ecf20Sopenharmony_ci/** 34898c2ecf20Sopenharmony_ci * snd_soc_dapm_put_enum_double - dapm enumerated double mixer set callback 34908c2ecf20Sopenharmony_ci * @kcontrol: mixer control 34918c2ecf20Sopenharmony_ci * @ucontrol: control element information 34928c2ecf20Sopenharmony_ci * 34938c2ecf20Sopenharmony_ci * Callback to set the value of a dapm enumerated double mixer control. 34948c2ecf20Sopenharmony_ci * 34958c2ecf20Sopenharmony_ci * Returns 0 for success. 34968c2ecf20Sopenharmony_ci */ 34978c2ecf20Sopenharmony_ciint snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, 34988c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 34998c2ecf20Sopenharmony_ci{ 35008c2ecf20Sopenharmony_ci struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); 35018c2ecf20Sopenharmony_ci struct snd_soc_card *card = dapm->card; 35028c2ecf20Sopenharmony_ci struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 35038c2ecf20Sopenharmony_ci unsigned int *item = ucontrol->value.enumerated.item; 35048c2ecf20Sopenharmony_ci unsigned int val, change, reg_change = 0; 35058c2ecf20Sopenharmony_ci unsigned int mask; 35068c2ecf20Sopenharmony_ci struct snd_soc_dapm_update update = {}; 35078c2ecf20Sopenharmony_ci int ret = 0; 35088c2ecf20Sopenharmony_ci 35098c2ecf20Sopenharmony_ci if (item[0] >= e->items) 35108c2ecf20Sopenharmony_ci return -EINVAL; 35118c2ecf20Sopenharmony_ci 35128c2ecf20Sopenharmony_ci val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; 35138c2ecf20Sopenharmony_ci mask = e->mask << e->shift_l; 35148c2ecf20Sopenharmony_ci if (e->shift_l != e->shift_r) { 35158c2ecf20Sopenharmony_ci if (item[1] > e->items) 35168c2ecf20Sopenharmony_ci return -EINVAL; 35178c2ecf20Sopenharmony_ci val |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_r; 35188c2ecf20Sopenharmony_ci mask |= e->mask << e->shift_r; 35198c2ecf20Sopenharmony_ci } 35208c2ecf20Sopenharmony_ci 35218c2ecf20Sopenharmony_ci mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); 35228c2ecf20Sopenharmony_ci 35238c2ecf20Sopenharmony_ci change = dapm_kcontrol_set_value(kcontrol, val); 35248c2ecf20Sopenharmony_ci 35258c2ecf20Sopenharmony_ci if (e->reg != SND_SOC_NOPM) 35268c2ecf20Sopenharmony_ci reg_change = soc_dapm_test_bits(dapm, e->reg, mask, val); 35278c2ecf20Sopenharmony_ci 35288c2ecf20Sopenharmony_ci if (change || reg_change) { 35298c2ecf20Sopenharmony_ci if (reg_change) { 35308c2ecf20Sopenharmony_ci update.kcontrol = kcontrol; 35318c2ecf20Sopenharmony_ci update.reg = e->reg; 35328c2ecf20Sopenharmony_ci update.mask = mask; 35338c2ecf20Sopenharmony_ci update.val = val; 35348c2ecf20Sopenharmony_ci card->update = &update; 35358c2ecf20Sopenharmony_ci } 35368c2ecf20Sopenharmony_ci 35378c2ecf20Sopenharmony_ci ret = soc_dapm_mux_update_power(card, kcontrol, item[0], e); 35388c2ecf20Sopenharmony_ci 35398c2ecf20Sopenharmony_ci card->update = NULL; 35408c2ecf20Sopenharmony_ci } 35418c2ecf20Sopenharmony_ci 35428c2ecf20Sopenharmony_ci mutex_unlock(&card->dapm_mutex); 35438c2ecf20Sopenharmony_ci 35448c2ecf20Sopenharmony_ci if (ret > 0) 35458c2ecf20Sopenharmony_ci snd_soc_dpcm_runtime_update(card); 35468c2ecf20Sopenharmony_ci 35478c2ecf20Sopenharmony_ci return change; 35488c2ecf20Sopenharmony_ci} 35498c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double); 35508c2ecf20Sopenharmony_ci 35518c2ecf20Sopenharmony_ci/** 35528c2ecf20Sopenharmony_ci * snd_soc_dapm_info_pin_switch - Info for a pin switch 35538c2ecf20Sopenharmony_ci * 35548c2ecf20Sopenharmony_ci * @kcontrol: mixer control 35558c2ecf20Sopenharmony_ci * @uinfo: control element information 35568c2ecf20Sopenharmony_ci * 35578c2ecf20Sopenharmony_ci * Callback to provide information about a pin switch control. 35588c2ecf20Sopenharmony_ci */ 35598c2ecf20Sopenharmony_ciint snd_soc_dapm_info_pin_switch(struct snd_kcontrol *kcontrol, 35608c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 35618c2ecf20Sopenharmony_ci{ 35628c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 35638c2ecf20Sopenharmony_ci uinfo->count = 1; 35648c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 35658c2ecf20Sopenharmony_ci uinfo->value.integer.max = 1; 35668c2ecf20Sopenharmony_ci 35678c2ecf20Sopenharmony_ci return 0; 35688c2ecf20Sopenharmony_ci} 35698c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_info_pin_switch); 35708c2ecf20Sopenharmony_ci 35718c2ecf20Sopenharmony_ci/** 35728c2ecf20Sopenharmony_ci * snd_soc_dapm_get_pin_switch - Get information for a pin switch 35738c2ecf20Sopenharmony_ci * 35748c2ecf20Sopenharmony_ci * @kcontrol: mixer control 35758c2ecf20Sopenharmony_ci * @ucontrol: Value 35768c2ecf20Sopenharmony_ci */ 35778c2ecf20Sopenharmony_ciint snd_soc_dapm_get_pin_switch(struct snd_kcontrol *kcontrol, 35788c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 35798c2ecf20Sopenharmony_ci{ 35808c2ecf20Sopenharmony_ci struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); 35818c2ecf20Sopenharmony_ci const char *pin = (const char *)kcontrol->private_value; 35828c2ecf20Sopenharmony_ci 35838c2ecf20Sopenharmony_ci mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); 35848c2ecf20Sopenharmony_ci 35858c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = 35868c2ecf20Sopenharmony_ci snd_soc_dapm_get_pin_status(&card->dapm, pin); 35878c2ecf20Sopenharmony_ci 35888c2ecf20Sopenharmony_ci mutex_unlock(&card->dapm_mutex); 35898c2ecf20Sopenharmony_ci 35908c2ecf20Sopenharmony_ci return 0; 35918c2ecf20Sopenharmony_ci} 35928c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_get_pin_switch); 35938c2ecf20Sopenharmony_ci 35948c2ecf20Sopenharmony_ci/** 35958c2ecf20Sopenharmony_ci * snd_soc_dapm_put_pin_switch - Set information for a pin switch 35968c2ecf20Sopenharmony_ci * 35978c2ecf20Sopenharmony_ci * @kcontrol: mixer control 35988c2ecf20Sopenharmony_ci * @ucontrol: Value 35998c2ecf20Sopenharmony_ci */ 36008c2ecf20Sopenharmony_ciint snd_soc_dapm_put_pin_switch(struct snd_kcontrol *kcontrol, 36018c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 36028c2ecf20Sopenharmony_ci{ 36038c2ecf20Sopenharmony_ci struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); 36048c2ecf20Sopenharmony_ci const char *pin = (const char *)kcontrol->private_value; 36058c2ecf20Sopenharmony_ci int ret; 36068c2ecf20Sopenharmony_ci 36078c2ecf20Sopenharmony_ci mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); 36088c2ecf20Sopenharmony_ci ret = __snd_soc_dapm_set_pin(&card->dapm, pin, 36098c2ecf20Sopenharmony_ci !!ucontrol->value.integer.value[0]); 36108c2ecf20Sopenharmony_ci mutex_unlock(&card->dapm_mutex); 36118c2ecf20Sopenharmony_ci 36128c2ecf20Sopenharmony_ci snd_soc_dapm_sync(&card->dapm); 36138c2ecf20Sopenharmony_ci return ret; 36148c2ecf20Sopenharmony_ci} 36158c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_put_pin_switch); 36168c2ecf20Sopenharmony_ci 36178c2ecf20Sopenharmony_cistruct snd_soc_dapm_widget * 36188c2ecf20Sopenharmony_cisnd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm, 36198c2ecf20Sopenharmony_ci const struct snd_soc_dapm_widget *widget) 36208c2ecf20Sopenharmony_ci{ 36218c2ecf20Sopenharmony_ci enum snd_soc_dapm_direction dir; 36228c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *w; 36238c2ecf20Sopenharmony_ci const char *prefix; 36248c2ecf20Sopenharmony_ci int ret; 36258c2ecf20Sopenharmony_ci 36268c2ecf20Sopenharmony_ci if ((w = dapm_cnew_widget(widget)) == NULL) 36278c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 36288c2ecf20Sopenharmony_ci 36298c2ecf20Sopenharmony_ci switch (w->id) { 36308c2ecf20Sopenharmony_ci case snd_soc_dapm_regulator_supply: 36318c2ecf20Sopenharmony_ci w->regulator = devm_regulator_get(dapm->dev, w->name); 36328c2ecf20Sopenharmony_ci if (IS_ERR(w->regulator)) { 36338c2ecf20Sopenharmony_ci ret = PTR_ERR(w->regulator); 36348c2ecf20Sopenharmony_ci goto request_failed; 36358c2ecf20Sopenharmony_ci } 36368c2ecf20Sopenharmony_ci 36378c2ecf20Sopenharmony_ci if (w->on_val & SND_SOC_DAPM_REGULATOR_BYPASS) { 36388c2ecf20Sopenharmony_ci ret = regulator_allow_bypass(w->regulator, true); 36398c2ecf20Sopenharmony_ci if (ret != 0) 36408c2ecf20Sopenharmony_ci dev_warn(dapm->dev, 36418c2ecf20Sopenharmony_ci "ASoC: Failed to bypass %s: %d\n", 36428c2ecf20Sopenharmony_ci w->name, ret); 36438c2ecf20Sopenharmony_ci } 36448c2ecf20Sopenharmony_ci break; 36458c2ecf20Sopenharmony_ci case snd_soc_dapm_pinctrl: 36468c2ecf20Sopenharmony_ci w->pinctrl = devm_pinctrl_get(dapm->dev); 36478c2ecf20Sopenharmony_ci if (IS_ERR(w->pinctrl)) { 36488c2ecf20Sopenharmony_ci ret = PTR_ERR(w->pinctrl); 36498c2ecf20Sopenharmony_ci goto request_failed; 36508c2ecf20Sopenharmony_ci } 36518c2ecf20Sopenharmony_ci 36528c2ecf20Sopenharmony_ci /* set to sleep_state when initializing */ 36538c2ecf20Sopenharmony_ci dapm_pinctrl_event(w, NULL, SND_SOC_DAPM_POST_PMD); 36548c2ecf20Sopenharmony_ci break; 36558c2ecf20Sopenharmony_ci case snd_soc_dapm_clock_supply: 36568c2ecf20Sopenharmony_ci w->clk = devm_clk_get(dapm->dev, w->name); 36578c2ecf20Sopenharmony_ci if (IS_ERR(w->clk)) { 36588c2ecf20Sopenharmony_ci ret = PTR_ERR(w->clk); 36598c2ecf20Sopenharmony_ci goto request_failed; 36608c2ecf20Sopenharmony_ci } 36618c2ecf20Sopenharmony_ci break; 36628c2ecf20Sopenharmony_ci default: 36638c2ecf20Sopenharmony_ci break; 36648c2ecf20Sopenharmony_ci } 36658c2ecf20Sopenharmony_ci 36668c2ecf20Sopenharmony_ci prefix = soc_dapm_prefix(dapm); 36678c2ecf20Sopenharmony_ci if (prefix) 36688c2ecf20Sopenharmony_ci w->name = kasprintf(GFP_KERNEL, "%s %s", prefix, widget->name); 36698c2ecf20Sopenharmony_ci else 36708c2ecf20Sopenharmony_ci w->name = kstrdup_const(widget->name, GFP_KERNEL); 36718c2ecf20Sopenharmony_ci if (w->name == NULL) { 36728c2ecf20Sopenharmony_ci kfree_const(w->sname); 36738c2ecf20Sopenharmony_ci kfree(w); 36748c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 36758c2ecf20Sopenharmony_ci } 36768c2ecf20Sopenharmony_ci 36778c2ecf20Sopenharmony_ci switch (w->id) { 36788c2ecf20Sopenharmony_ci case snd_soc_dapm_mic: 36798c2ecf20Sopenharmony_ci w->is_ep = SND_SOC_DAPM_EP_SOURCE; 36808c2ecf20Sopenharmony_ci w->power_check = dapm_generic_check_power; 36818c2ecf20Sopenharmony_ci break; 36828c2ecf20Sopenharmony_ci case snd_soc_dapm_input: 36838c2ecf20Sopenharmony_ci if (!dapm->card->fully_routed) 36848c2ecf20Sopenharmony_ci w->is_ep = SND_SOC_DAPM_EP_SOURCE; 36858c2ecf20Sopenharmony_ci w->power_check = dapm_generic_check_power; 36868c2ecf20Sopenharmony_ci break; 36878c2ecf20Sopenharmony_ci case snd_soc_dapm_spk: 36888c2ecf20Sopenharmony_ci case snd_soc_dapm_hp: 36898c2ecf20Sopenharmony_ci w->is_ep = SND_SOC_DAPM_EP_SINK; 36908c2ecf20Sopenharmony_ci w->power_check = dapm_generic_check_power; 36918c2ecf20Sopenharmony_ci break; 36928c2ecf20Sopenharmony_ci case snd_soc_dapm_output: 36938c2ecf20Sopenharmony_ci if (!dapm->card->fully_routed) 36948c2ecf20Sopenharmony_ci w->is_ep = SND_SOC_DAPM_EP_SINK; 36958c2ecf20Sopenharmony_ci w->power_check = dapm_generic_check_power; 36968c2ecf20Sopenharmony_ci break; 36978c2ecf20Sopenharmony_ci case snd_soc_dapm_vmid: 36988c2ecf20Sopenharmony_ci case snd_soc_dapm_siggen: 36998c2ecf20Sopenharmony_ci w->is_ep = SND_SOC_DAPM_EP_SOURCE; 37008c2ecf20Sopenharmony_ci w->power_check = dapm_always_on_check_power; 37018c2ecf20Sopenharmony_ci break; 37028c2ecf20Sopenharmony_ci case snd_soc_dapm_sink: 37038c2ecf20Sopenharmony_ci w->is_ep = SND_SOC_DAPM_EP_SINK; 37048c2ecf20Sopenharmony_ci w->power_check = dapm_always_on_check_power; 37058c2ecf20Sopenharmony_ci break; 37068c2ecf20Sopenharmony_ci 37078c2ecf20Sopenharmony_ci case snd_soc_dapm_mux: 37088c2ecf20Sopenharmony_ci case snd_soc_dapm_demux: 37098c2ecf20Sopenharmony_ci case snd_soc_dapm_switch: 37108c2ecf20Sopenharmony_ci case snd_soc_dapm_mixer: 37118c2ecf20Sopenharmony_ci case snd_soc_dapm_mixer_named_ctl: 37128c2ecf20Sopenharmony_ci case snd_soc_dapm_adc: 37138c2ecf20Sopenharmony_ci case snd_soc_dapm_aif_out: 37148c2ecf20Sopenharmony_ci case snd_soc_dapm_dac: 37158c2ecf20Sopenharmony_ci case snd_soc_dapm_aif_in: 37168c2ecf20Sopenharmony_ci case snd_soc_dapm_pga: 37178c2ecf20Sopenharmony_ci case snd_soc_dapm_buffer: 37188c2ecf20Sopenharmony_ci case snd_soc_dapm_scheduler: 37198c2ecf20Sopenharmony_ci case snd_soc_dapm_effect: 37208c2ecf20Sopenharmony_ci case snd_soc_dapm_src: 37218c2ecf20Sopenharmony_ci case snd_soc_dapm_asrc: 37228c2ecf20Sopenharmony_ci case snd_soc_dapm_encoder: 37238c2ecf20Sopenharmony_ci case snd_soc_dapm_decoder: 37248c2ecf20Sopenharmony_ci case snd_soc_dapm_out_drv: 37258c2ecf20Sopenharmony_ci case snd_soc_dapm_micbias: 37268c2ecf20Sopenharmony_ci case snd_soc_dapm_line: 37278c2ecf20Sopenharmony_ci case snd_soc_dapm_dai_link: 37288c2ecf20Sopenharmony_ci case snd_soc_dapm_dai_out: 37298c2ecf20Sopenharmony_ci case snd_soc_dapm_dai_in: 37308c2ecf20Sopenharmony_ci w->power_check = dapm_generic_check_power; 37318c2ecf20Sopenharmony_ci break; 37328c2ecf20Sopenharmony_ci case snd_soc_dapm_supply: 37338c2ecf20Sopenharmony_ci case snd_soc_dapm_regulator_supply: 37348c2ecf20Sopenharmony_ci case snd_soc_dapm_pinctrl: 37358c2ecf20Sopenharmony_ci case snd_soc_dapm_clock_supply: 37368c2ecf20Sopenharmony_ci case snd_soc_dapm_kcontrol: 37378c2ecf20Sopenharmony_ci w->is_supply = 1; 37388c2ecf20Sopenharmony_ci w->power_check = dapm_supply_check_power; 37398c2ecf20Sopenharmony_ci break; 37408c2ecf20Sopenharmony_ci default: 37418c2ecf20Sopenharmony_ci w->power_check = dapm_always_on_check_power; 37428c2ecf20Sopenharmony_ci break; 37438c2ecf20Sopenharmony_ci } 37448c2ecf20Sopenharmony_ci 37458c2ecf20Sopenharmony_ci w->dapm = dapm; 37468c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&w->list); 37478c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&w->dirty); 37488c2ecf20Sopenharmony_ci /* see for_each_card_widgets */ 37498c2ecf20Sopenharmony_ci list_add_tail(&w->list, &dapm->card->widgets); 37508c2ecf20Sopenharmony_ci 37518c2ecf20Sopenharmony_ci snd_soc_dapm_for_each_direction(dir) { 37528c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&w->edges[dir]); 37538c2ecf20Sopenharmony_ci w->endpoints[dir] = -1; 37548c2ecf20Sopenharmony_ci } 37558c2ecf20Sopenharmony_ci 37568c2ecf20Sopenharmony_ci /* machine layer sets up unconnected pins and insertions */ 37578c2ecf20Sopenharmony_ci w->connected = 1; 37588c2ecf20Sopenharmony_ci return w; 37598c2ecf20Sopenharmony_ci 37608c2ecf20Sopenharmony_cirequest_failed: 37618c2ecf20Sopenharmony_ci if (ret != -EPROBE_DEFER) 37628c2ecf20Sopenharmony_ci dev_err(dapm->dev, "ASoC: Failed to request %s: %d\n", 37638c2ecf20Sopenharmony_ci w->name, ret); 37648c2ecf20Sopenharmony_ci 37658c2ecf20Sopenharmony_ci kfree_const(w->sname); 37668c2ecf20Sopenharmony_ci kfree(w); 37678c2ecf20Sopenharmony_ci return ERR_PTR(ret); 37688c2ecf20Sopenharmony_ci} 37698c2ecf20Sopenharmony_ci 37708c2ecf20Sopenharmony_ci/** 37718c2ecf20Sopenharmony_ci * snd_soc_dapm_new_control - create new dapm control 37728c2ecf20Sopenharmony_ci * @dapm: DAPM context 37738c2ecf20Sopenharmony_ci * @widget: widget template 37748c2ecf20Sopenharmony_ci * 37758c2ecf20Sopenharmony_ci * Creates new DAPM control based upon a template. 37768c2ecf20Sopenharmony_ci * 37778c2ecf20Sopenharmony_ci * Returns a widget pointer on success or an error pointer on failure 37788c2ecf20Sopenharmony_ci */ 37798c2ecf20Sopenharmony_cistruct snd_soc_dapm_widget * 37808c2ecf20Sopenharmony_cisnd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, 37818c2ecf20Sopenharmony_ci const struct snd_soc_dapm_widget *widget) 37828c2ecf20Sopenharmony_ci{ 37838c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *w; 37848c2ecf20Sopenharmony_ci 37858c2ecf20Sopenharmony_ci mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); 37868c2ecf20Sopenharmony_ci w = snd_soc_dapm_new_control_unlocked(dapm, widget); 37878c2ecf20Sopenharmony_ci mutex_unlock(&dapm->card->dapm_mutex); 37888c2ecf20Sopenharmony_ci 37898c2ecf20Sopenharmony_ci return w; 37908c2ecf20Sopenharmony_ci} 37918c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_new_control); 37928c2ecf20Sopenharmony_ci 37938c2ecf20Sopenharmony_ci/** 37948c2ecf20Sopenharmony_ci * snd_soc_dapm_new_controls - create new dapm controls 37958c2ecf20Sopenharmony_ci * @dapm: DAPM context 37968c2ecf20Sopenharmony_ci * @widget: widget array 37978c2ecf20Sopenharmony_ci * @num: number of widgets 37988c2ecf20Sopenharmony_ci * 37998c2ecf20Sopenharmony_ci * Creates new DAPM controls based upon the templates. 38008c2ecf20Sopenharmony_ci * 38018c2ecf20Sopenharmony_ci * Returns 0 for success else error. 38028c2ecf20Sopenharmony_ci */ 38038c2ecf20Sopenharmony_ciint snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm, 38048c2ecf20Sopenharmony_ci const struct snd_soc_dapm_widget *widget, 38058c2ecf20Sopenharmony_ci int num) 38068c2ecf20Sopenharmony_ci{ 38078c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *w; 38088c2ecf20Sopenharmony_ci int i; 38098c2ecf20Sopenharmony_ci int ret = 0; 38108c2ecf20Sopenharmony_ci 38118c2ecf20Sopenharmony_ci mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); 38128c2ecf20Sopenharmony_ci for (i = 0; i < num; i++) { 38138c2ecf20Sopenharmony_ci w = snd_soc_dapm_new_control_unlocked(dapm, widget); 38148c2ecf20Sopenharmony_ci if (IS_ERR(w)) { 38158c2ecf20Sopenharmony_ci ret = PTR_ERR(w); 38168c2ecf20Sopenharmony_ci break; 38178c2ecf20Sopenharmony_ci } 38188c2ecf20Sopenharmony_ci widget++; 38198c2ecf20Sopenharmony_ci } 38208c2ecf20Sopenharmony_ci mutex_unlock(&dapm->card->dapm_mutex); 38218c2ecf20Sopenharmony_ci return ret; 38228c2ecf20Sopenharmony_ci} 38238c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_new_controls); 38248c2ecf20Sopenharmony_ci 38258c2ecf20Sopenharmony_cistatic int 38268c2ecf20Sopenharmony_cisnd_soc_dai_link_event_pre_pmu(struct snd_soc_dapm_widget *w, 38278c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 38288c2ecf20Sopenharmony_ci{ 38298c2ecf20Sopenharmony_ci struct snd_soc_dapm_path *path; 38308c2ecf20Sopenharmony_ci struct snd_soc_dai *source, *sink; 38318c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 38328c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params = NULL; 38338c2ecf20Sopenharmony_ci const struct snd_soc_pcm_stream *config = NULL; 38348c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = NULL; 38358c2ecf20Sopenharmony_ci unsigned int fmt; 38368c2ecf20Sopenharmony_ci int ret = 0; 38378c2ecf20Sopenharmony_ci 38388c2ecf20Sopenharmony_ci params = kzalloc(sizeof(*params), GFP_KERNEL); 38398c2ecf20Sopenharmony_ci if (!params) 38408c2ecf20Sopenharmony_ci return -ENOMEM; 38418c2ecf20Sopenharmony_ci 38428c2ecf20Sopenharmony_ci runtime = kzalloc(sizeof(*runtime), GFP_KERNEL); 38438c2ecf20Sopenharmony_ci if (!runtime) { 38448c2ecf20Sopenharmony_ci ret = -ENOMEM; 38458c2ecf20Sopenharmony_ci goto out; 38468c2ecf20Sopenharmony_ci } 38478c2ecf20Sopenharmony_ci 38488c2ecf20Sopenharmony_ci substream->runtime = runtime; 38498c2ecf20Sopenharmony_ci 38508c2ecf20Sopenharmony_ci substream->stream = SNDRV_PCM_STREAM_CAPTURE; 38518c2ecf20Sopenharmony_ci snd_soc_dapm_widget_for_each_source_path(w, path) { 38528c2ecf20Sopenharmony_ci source = path->source->priv; 38538c2ecf20Sopenharmony_ci 38548c2ecf20Sopenharmony_ci ret = snd_soc_dai_startup(source, substream); 38558c2ecf20Sopenharmony_ci if (ret < 0) { 38568c2ecf20Sopenharmony_ci dev_err(source->dev, 38578c2ecf20Sopenharmony_ci "ASoC: startup() failed: %d\n", ret); 38588c2ecf20Sopenharmony_ci goto out; 38598c2ecf20Sopenharmony_ci } 38608c2ecf20Sopenharmony_ci snd_soc_dai_activate(source, substream->stream); 38618c2ecf20Sopenharmony_ci } 38628c2ecf20Sopenharmony_ci 38638c2ecf20Sopenharmony_ci substream->stream = SNDRV_PCM_STREAM_PLAYBACK; 38648c2ecf20Sopenharmony_ci snd_soc_dapm_widget_for_each_sink_path(w, path) { 38658c2ecf20Sopenharmony_ci sink = path->sink->priv; 38668c2ecf20Sopenharmony_ci 38678c2ecf20Sopenharmony_ci ret = snd_soc_dai_startup(sink, substream); 38688c2ecf20Sopenharmony_ci if (ret < 0) { 38698c2ecf20Sopenharmony_ci dev_err(sink->dev, 38708c2ecf20Sopenharmony_ci "ASoC: startup() failed: %d\n", ret); 38718c2ecf20Sopenharmony_ci goto out; 38728c2ecf20Sopenharmony_ci } 38738c2ecf20Sopenharmony_ci snd_soc_dai_activate(sink, substream->stream); 38748c2ecf20Sopenharmony_ci } 38758c2ecf20Sopenharmony_ci 38768c2ecf20Sopenharmony_ci substream->hw_opened = 1; 38778c2ecf20Sopenharmony_ci 38788c2ecf20Sopenharmony_ci /* 38798c2ecf20Sopenharmony_ci * Note: getting the config after .startup() gives a chance to 38808c2ecf20Sopenharmony_ci * either party on the link to alter the configuration if 38818c2ecf20Sopenharmony_ci * necessary 38828c2ecf20Sopenharmony_ci */ 38838c2ecf20Sopenharmony_ci config = rtd->dai_link->params + rtd->params_select; 38848c2ecf20Sopenharmony_ci if (WARN_ON(!config)) { 38858c2ecf20Sopenharmony_ci dev_err(w->dapm->dev, "ASoC: link config missing\n"); 38868c2ecf20Sopenharmony_ci ret = -EINVAL; 38878c2ecf20Sopenharmony_ci goto out; 38888c2ecf20Sopenharmony_ci } 38898c2ecf20Sopenharmony_ci 38908c2ecf20Sopenharmony_ci /* Be a little careful as we don't want to overflow the mask array */ 38918c2ecf20Sopenharmony_ci if (config->formats) { 38928c2ecf20Sopenharmony_ci fmt = ffs(config->formats) - 1; 38938c2ecf20Sopenharmony_ci } else { 38948c2ecf20Sopenharmony_ci dev_warn(w->dapm->dev, "ASoC: Invalid format %llx specified\n", 38958c2ecf20Sopenharmony_ci config->formats); 38968c2ecf20Sopenharmony_ci 38978c2ecf20Sopenharmony_ci ret = -EINVAL; 38988c2ecf20Sopenharmony_ci goto out; 38998c2ecf20Sopenharmony_ci } 39008c2ecf20Sopenharmony_ci 39018c2ecf20Sopenharmony_ci snd_mask_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), fmt); 39028c2ecf20Sopenharmony_ci hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE)->min = 39038c2ecf20Sopenharmony_ci config->rate_min; 39048c2ecf20Sopenharmony_ci hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE)->max = 39058c2ecf20Sopenharmony_ci config->rate_max; 39068c2ecf20Sopenharmony_ci hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS)->min 39078c2ecf20Sopenharmony_ci = config->channels_min; 39088c2ecf20Sopenharmony_ci hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS)->max 39098c2ecf20Sopenharmony_ci = config->channels_max; 39108c2ecf20Sopenharmony_ci 39118c2ecf20Sopenharmony_ci substream->stream = SNDRV_PCM_STREAM_CAPTURE; 39128c2ecf20Sopenharmony_ci snd_soc_dapm_widget_for_each_source_path(w, path) { 39138c2ecf20Sopenharmony_ci source = path->source->priv; 39148c2ecf20Sopenharmony_ci 39158c2ecf20Sopenharmony_ci ret = snd_soc_dai_hw_params(source, substream, params); 39168c2ecf20Sopenharmony_ci if (ret < 0) 39178c2ecf20Sopenharmony_ci goto out; 39188c2ecf20Sopenharmony_ci 39198c2ecf20Sopenharmony_ci dapm_update_dai_unlocked(substream, params, source); 39208c2ecf20Sopenharmony_ci } 39218c2ecf20Sopenharmony_ci 39228c2ecf20Sopenharmony_ci substream->stream = SNDRV_PCM_STREAM_PLAYBACK; 39238c2ecf20Sopenharmony_ci snd_soc_dapm_widget_for_each_sink_path(w, path) { 39248c2ecf20Sopenharmony_ci sink = path->sink->priv; 39258c2ecf20Sopenharmony_ci 39268c2ecf20Sopenharmony_ci ret = snd_soc_dai_hw_params(sink, substream, params); 39278c2ecf20Sopenharmony_ci if (ret < 0) 39288c2ecf20Sopenharmony_ci goto out; 39298c2ecf20Sopenharmony_ci 39308c2ecf20Sopenharmony_ci dapm_update_dai_unlocked(substream, params, sink); 39318c2ecf20Sopenharmony_ci } 39328c2ecf20Sopenharmony_ci 39338c2ecf20Sopenharmony_ci runtime->format = params_format(params); 39348c2ecf20Sopenharmony_ci runtime->subformat = params_subformat(params); 39358c2ecf20Sopenharmony_ci runtime->channels = params_channels(params); 39368c2ecf20Sopenharmony_ci runtime->rate = params_rate(params); 39378c2ecf20Sopenharmony_ci 39388c2ecf20Sopenharmony_ciout: 39398c2ecf20Sopenharmony_ci kfree(params); 39408c2ecf20Sopenharmony_ci return ret; 39418c2ecf20Sopenharmony_ci} 39428c2ecf20Sopenharmony_ci 39438c2ecf20Sopenharmony_cistatic int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, 39448c2ecf20Sopenharmony_ci struct snd_kcontrol *kcontrol, int event) 39458c2ecf20Sopenharmony_ci{ 39468c2ecf20Sopenharmony_ci struct snd_soc_dapm_path *path; 39478c2ecf20Sopenharmony_ci struct snd_soc_dai *source, *sink; 39488c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream = w->priv; 39498c2ecf20Sopenharmony_ci int ret = 0, saved_stream = substream->stream; 39508c2ecf20Sopenharmony_ci 39518c2ecf20Sopenharmony_ci if (WARN_ON(list_empty(&w->edges[SND_SOC_DAPM_DIR_OUT]) || 39528c2ecf20Sopenharmony_ci list_empty(&w->edges[SND_SOC_DAPM_DIR_IN]))) 39538c2ecf20Sopenharmony_ci return -EINVAL; 39548c2ecf20Sopenharmony_ci 39558c2ecf20Sopenharmony_ci switch (event) { 39568c2ecf20Sopenharmony_ci case SND_SOC_DAPM_PRE_PMU: 39578c2ecf20Sopenharmony_ci ret = snd_soc_dai_link_event_pre_pmu(w, substream); 39588c2ecf20Sopenharmony_ci if (ret < 0) 39598c2ecf20Sopenharmony_ci goto out; 39608c2ecf20Sopenharmony_ci 39618c2ecf20Sopenharmony_ci break; 39628c2ecf20Sopenharmony_ci 39638c2ecf20Sopenharmony_ci case SND_SOC_DAPM_POST_PMU: 39648c2ecf20Sopenharmony_ci snd_soc_dapm_widget_for_each_sink_path(w, path) { 39658c2ecf20Sopenharmony_ci sink = path->sink->priv; 39668c2ecf20Sopenharmony_ci 39678c2ecf20Sopenharmony_ci ret = snd_soc_dai_digital_mute(sink, 0, 39688c2ecf20Sopenharmony_ci SNDRV_PCM_STREAM_PLAYBACK); 39698c2ecf20Sopenharmony_ci if (ret != 0 && ret != -ENOTSUPP) 39708c2ecf20Sopenharmony_ci dev_warn(sink->dev, 39718c2ecf20Sopenharmony_ci "ASoC: Failed to unmute: %d\n", ret); 39728c2ecf20Sopenharmony_ci ret = 0; 39738c2ecf20Sopenharmony_ci } 39748c2ecf20Sopenharmony_ci break; 39758c2ecf20Sopenharmony_ci 39768c2ecf20Sopenharmony_ci case SND_SOC_DAPM_PRE_PMD: 39778c2ecf20Sopenharmony_ci snd_soc_dapm_widget_for_each_sink_path(w, path) { 39788c2ecf20Sopenharmony_ci sink = path->sink->priv; 39798c2ecf20Sopenharmony_ci 39808c2ecf20Sopenharmony_ci ret = snd_soc_dai_digital_mute(sink, 1, 39818c2ecf20Sopenharmony_ci SNDRV_PCM_STREAM_PLAYBACK); 39828c2ecf20Sopenharmony_ci if (ret != 0 && ret != -ENOTSUPP) 39838c2ecf20Sopenharmony_ci dev_warn(sink->dev, 39848c2ecf20Sopenharmony_ci "ASoC: Failed to mute: %d\n", ret); 39858c2ecf20Sopenharmony_ci ret = 0; 39868c2ecf20Sopenharmony_ci } 39878c2ecf20Sopenharmony_ci 39888c2ecf20Sopenharmony_ci substream->stream = SNDRV_PCM_STREAM_CAPTURE; 39898c2ecf20Sopenharmony_ci snd_soc_dapm_widget_for_each_source_path(w, path) { 39908c2ecf20Sopenharmony_ci source = path->source->priv; 39918c2ecf20Sopenharmony_ci snd_soc_dai_hw_free(source, substream); 39928c2ecf20Sopenharmony_ci } 39938c2ecf20Sopenharmony_ci 39948c2ecf20Sopenharmony_ci substream->stream = SNDRV_PCM_STREAM_PLAYBACK; 39958c2ecf20Sopenharmony_ci snd_soc_dapm_widget_for_each_sink_path(w, path) { 39968c2ecf20Sopenharmony_ci sink = path->sink->priv; 39978c2ecf20Sopenharmony_ci snd_soc_dai_hw_free(sink, substream); 39988c2ecf20Sopenharmony_ci } 39998c2ecf20Sopenharmony_ci 40008c2ecf20Sopenharmony_ci substream->stream = SNDRV_PCM_STREAM_CAPTURE; 40018c2ecf20Sopenharmony_ci snd_soc_dapm_widget_for_each_source_path(w, path) { 40028c2ecf20Sopenharmony_ci source = path->source->priv; 40038c2ecf20Sopenharmony_ci snd_soc_dai_deactivate(source, substream->stream); 40048c2ecf20Sopenharmony_ci snd_soc_dai_shutdown(source, substream, 0); 40058c2ecf20Sopenharmony_ci } 40068c2ecf20Sopenharmony_ci 40078c2ecf20Sopenharmony_ci substream->stream = SNDRV_PCM_STREAM_PLAYBACK; 40088c2ecf20Sopenharmony_ci snd_soc_dapm_widget_for_each_sink_path(w, path) { 40098c2ecf20Sopenharmony_ci sink = path->sink->priv; 40108c2ecf20Sopenharmony_ci snd_soc_dai_deactivate(sink, substream->stream); 40118c2ecf20Sopenharmony_ci snd_soc_dai_shutdown(sink, substream, 0); 40128c2ecf20Sopenharmony_ci } 40138c2ecf20Sopenharmony_ci break; 40148c2ecf20Sopenharmony_ci 40158c2ecf20Sopenharmony_ci case SND_SOC_DAPM_POST_PMD: 40168c2ecf20Sopenharmony_ci kfree(substream->runtime); 40178c2ecf20Sopenharmony_ci substream->runtime = NULL; 40188c2ecf20Sopenharmony_ci break; 40198c2ecf20Sopenharmony_ci 40208c2ecf20Sopenharmony_ci default: 40218c2ecf20Sopenharmony_ci WARN(1, "Unknown event %d\n", event); 40228c2ecf20Sopenharmony_ci ret = -EINVAL; 40238c2ecf20Sopenharmony_ci } 40248c2ecf20Sopenharmony_ci 40258c2ecf20Sopenharmony_ciout: 40268c2ecf20Sopenharmony_ci /* Restore the substream direction */ 40278c2ecf20Sopenharmony_ci substream->stream = saved_stream; 40288c2ecf20Sopenharmony_ci return ret; 40298c2ecf20Sopenharmony_ci} 40308c2ecf20Sopenharmony_ci 40318c2ecf20Sopenharmony_cistatic int snd_soc_dapm_dai_link_get(struct snd_kcontrol *kcontrol, 40328c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 40338c2ecf20Sopenharmony_ci{ 40348c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *w = snd_kcontrol_chip(kcontrol); 40358c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = w->priv; 40368c2ecf20Sopenharmony_ci 40378c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = rtd->params_select; 40388c2ecf20Sopenharmony_ci 40398c2ecf20Sopenharmony_ci return 0; 40408c2ecf20Sopenharmony_ci} 40418c2ecf20Sopenharmony_ci 40428c2ecf20Sopenharmony_cistatic int snd_soc_dapm_dai_link_put(struct snd_kcontrol *kcontrol, 40438c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 40448c2ecf20Sopenharmony_ci{ 40458c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *w = snd_kcontrol_chip(kcontrol); 40468c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = w->priv; 40478c2ecf20Sopenharmony_ci 40488c2ecf20Sopenharmony_ci /* Can't change the config when widget is already powered */ 40498c2ecf20Sopenharmony_ci if (w->power) 40508c2ecf20Sopenharmony_ci return -EBUSY; 40518c2ecf20Sopenharmony_ci 40528c2ecf20Sopenharmony_ci if (ucontrol->value.enumerated.item[0] == rtd->params_select) 40538c2ecf20Sopenharmony_ci return 0; 40548c2ecf20Sopenharmony_ci 40558c2ecf20Sopenharmony_ci if (ucontrol->value.enumerated.item[0] >= rtd->dai_link->num_params) 40568c2ecf20Sopenharmony_ci return -EINVAL; 40578c2ecf20Sopenharmony_ci 40588c2ecf20Sopenharmony_ci rtd->params_select = ucontrol->value.enumerated.item[0]; 40598c2ecf20Sopenharmony_ci 40608c2ecf20Sopenharmony_ci return 1; 40618c2ecf20Sopenharmony_ci} 40628c2ecf20Sopenharmony_ci 40638c2ecf20Sopenharmony_cistatic void 40648c2ecf20Sopenharmony_cisnd_soc_dapm_free_kcontrol(struct snd_soc_card *card, 40658c2ecf20Sopenharmony_ci unsigned long *private_value, 40668c2ecf20Sopenharmony_ci int num_params, 40678c2ecf20Sopenharmony_ci const char **w_param_text) 40688c2ecf20Sopenharmony_ci{ 40698c2ecf20Sopenharmony_ci int count; 40708c2ecf20Sopenharmony_ci 40718c2ecf20Sopenharmony_ci devm_kfree(card->dev, (void *)*private_value); 40728c2ecf20Sopenharmony_ci 40738c2ecf20Sopenharmony_ci if (!w_param_text) 40748c2ecf20Sopenharmony_ci return; 40758c2ecf20Sopenharmony_ci 40768c2ecf20Sopenharmony_ci for (count = 0 ; count < num_params; count++) 40778c2ecf20Sopenharmony_ci devm_kfree(card->dev, (void *)w_param_text[count]); 40788c2ecf20Sopenharmony_ci devm_kfree(card->dev, w_param_text); 40798c2ecf20Sopenharmony_ci} 40808c2ecf20Sopenharmony_ci 40818c2ecf20Sopenharmony_cistatic struct snd_kcontrol_new * 40828c2ecf20Sopenharmony_cisnd_soc_dapm_alloc_kcontrol(struct snd_soc_card *card, 40838c2ecf20Sopenharmony_ci char *link_name, 40848c2ecf20Sopenharmony_ci const struct snd_soc_pcm_stream *params, 40858c2ecf20Sopenharmony_ci int num_params, const char **w_param_text, 40868c2ecf20Sopenharmony_ci unsigned long *private_value) 40878c2ecf20Sopenharmony_ci{ 40888c2ecf20Sopenharmony_ci struct soc_enum w_param_enum[] = { 40898c2ecf20Sopenharmony_ci SOC_ENUM_SINGLE(0, 0, 0, NULL), 40908c2ecf20Sopenharmony_ci }; 40918c2ecf20Sopenharmony_ci struct snd_kcontrol_new kcontrol_dai_link[] = { 40928c2ecf20Sopenharmony_ci SOC_ENUM_EXT(NULL, w_param_enum[0], 40938c2ecf20Sopenharmony_ci snd_soc_dapm_dai_link_get, 40948c2ecf20Sopenharmony_ci snd_soc_dapm_dai_link_put), 40958c2ecf20Sopenharmony_ci }; 40968c2ecf20Sopenharmony_ci struct snd_kcontrol_new *kcontrol_news; 40978c2ecf20Sopenharmony_ci const struct snd_soc_pcm_stream *config = params; 40988c2ecf20Sopenharmony_ci int count; 40998c2ecf20Sopenharmony_ci 41008c2ecf20Sopenharmony_ci for (count = 0 ; count < num_params; count++) { 41018c2ecf20Sopenharmony_ci if (!config->stream_name) { 41028c2ecf20Sopenharmony_ci dev_warn(card->dapm.dev, 41038c2ecf20Sopenharmony_ci "ASoC: anonymous config %d for dai link %s\n", 41048c2ecf20Sopenharmony_ci count, link_name); 41058c2ecf20Sopenharmony_ci w_param_text[count] = 41068c2ecf20Sopenharmony_ci devm_kasprintf(card->dev, GFP_KERNEL, 41078c2ecf20Sopenharmony_ci "Anonymous Configuration %d", 41088c2ecf20Sopenharmony_ci count); 41098c2ecf20Sopenharmony_ci } else { 41108c2ecf20Sopenharmony_ci w_param_text[count] = devm_kmemdup(card->dev, 41118c2ecf20Sopenharmony_ci config->stream_name, 41128c2ecf20Sopenharmony_ci strlen(config->stream_name) + 1, 41138c2ecf20Sopenharmony_ci GFP_KERNEL); 41148c2ecf20Sopenharmony_ci } 41158c2ecf20Sopenharmony_ci if (!w_param_text[count]) 41168c2ecf20Sopenharmony_ci goto outfree_w_param; 41178c2ecf20Sopenharmony_ci config++; 41188c2ecf20Sopenharmony_ci } 41198c2ecf20Sopenharmony_ci 41208c2ecf20Sopenharmony_ci w_param_enum[0].items = num_params; 41218c2ecf20Sopenharmony_ci w_param_enum[0].texts = w_param_text; 41228c2ecf20Sopenharmony_ci 41238c2ecf20Sopenharmony_ci *private_value = 41248c2ecf20Sopenharmony_ci (unsigned long) devm_kmemdup(card->dev, 41258c2ecf20Sopenharmony_ci (void *)(kcontrol_dai_link[0].private_value), 41268c2ecf20Sopenharmony_ci sizeof(struct soc_enum), GFP_KERNEL); 41278c2ecf20Sopenharmony_ci if (!*private_value) { 41288c2ecf20Sopenharmony_ci dev_err(card->dev, "ASoC: Failed to create control for %s widget\n", 41298c2ecf20Sopenharmony_ci link_name); 41308c2ecf20Sopenharmony_ci goto outfree_w_param; 41318c2ecf20Sopenharmony_ci } 41328c2ecf20Sopenharmony_ci kcontrol_dai_link[0].private_value = *private_value; 41338c2ecf20Sopenharmony_ci /* duplicate kcontrol_dai_link on heap so that memory persists */ 41348c2ecf20Sopenharmony_ci kcontrol_news = devm_kmemdup(card->dev, &kcontrol_dai_link[0], 41358c2ecf20Sopenharmony_ci sizeof(struct snd_kcontrol_new), 41368c2ecf20Sopenharmony_ci GFP_KERNEL); 41378c2ecf20Sopenharmony_ci if (!kcontrol_news) { 41388c2ecf20Sopenharmony_ci dev_err(card->dev, "ASoC: Failed to create control for %s widget\n", 41398c2ecf20Sopenharmony_ci link_name); 41408c2ecf20Sopenharmony_ci goto outfree_w_param; 41418c2ecf20Sopenharmony_ci } 41428c2ecf20Sopenharmony_ci return kcontrol_news; 41438c2ecf20Sopenharmony_ci 41448c2ecf20Sopenharmony_cioutfree_w_param: 41458c2ecf20Sopenharmony_ci snd_soc_dapm_free_kcontrol(card, private_value, num_params, w_param_text); 41468c2ecf20Sopenharmony_ci return NULL; 41478c2ecf20Sopenharmony_ci} 41488c2ecf20Sopenharmony_ci 41498c2ecf20Sopenharmony_cistatic struct snd_soc_dapm_widget * 41508c2ecf20Sopenharmony_cisnd_soc_dapm_new_dai(struct snd_soc_card *card, 41518c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream, 41528c2ecf20Sopenharmony_ci char *id) 41538c2ecf20Sopenharmony_ci{ 41548c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 41558c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget template; 41568c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *w; 41578c2ecf20Sopenharmony_ci const char **w_param_text; 41588c2ecf20Sopenharmony_ci unsigned long private_value = 0; 41598c2ecf20Sopenharmony_ci char *link_name; 41608c2ecf20Sopenharmony_ci int ret; 41618c2ecf20Sopenharmony_ci 41628c2ecf20Sopenharmony_ci link_name = devm_kasprintf(card->dev, GFP_KERNEL, "%s-%s", 41638c2ecf20Sopenharmony_ci rtd->dai_link->name, id); 41648c2ecf20Sopenharmony_ci if (!link_name) 41658c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 41668c2ecf20Sopenharmony_ci 41678c2ecf20Sopenharmony_ci memset(&template, 0, sizeof(template)); 41688c2ecf20Sopenharmony_ci template.reg = SND_SOC_NOPM; 41698c2ecf20Sopenharmony_ci template.id = snd_soc_dapm_dai_link; 41708c2ecf20Sopenharmony_ci template.name = link_name; 41718c2ecf20Sopenharmony_ci template.event = snd_soc_dai_link_event; 41728c2ecf20Sopenharmony_ci template.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | 41738c2ecf20Sopenharmony_ci SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD; 41748c2ecf20Sopenharmony_ci template.kcontrol_news = NULL; 41758c2ecf20Sopenharmony_ci 41768c2ecf20Sopenharmony_ci /* allocate memory for control, only in case of multiple configs */ 41778c2ecf20Sopenharmony_ci if (rtd->dai_link->num_params > 1) { 41788c2ecf20Sopenharmony_ci w_param_text = devm_kcalloc(card->dev, 41798c2ecf20Sopenharmony_ci rtd->dai_link->num_params, 41808c2ecf20Sopenharmony_ci sizeof(char *), GFP_KERNEL); 41818c2ecf20Sopenharmony_ci if (!w_param_text) { 41828c2ecf20Sopenharmony_ci ret = -ENOMEM; 41838c2ecf20Sopenharmony_ci goto param_fail; 41848c2ecf20Sopenharmony_ci } 41858c2ecf20Sopenharmony_ci 41868c2ecf20Sopenharmony_ci template.num_kcontrols = 1; 41878c2ecf20Sopenharmony_ci template.kcontrol_news = 41888c2ecf20Sopenharmony_ci snd_soc_dapm_alloc_kcontrol(card, 41898c2ecf20Sopenharmony_ci link_name, 41908c2ecf20Sopenharmony_ci rtd->dai_link->params, 41918c2ecf20Sopenharmony_ci rtd->dai_link->num_params, 41928c2ecf20Sopenharmony_ci w_param_text, &private_value); 41938c2ecf20Sopenharmony_ci if (!template.kcontrol_news) { 41948c2ecf20Sopenharmony_ci ret = -ENOMEM; 41958c2ecf20Sopenharmony_ci goto param_fail; 41968c2ecf20Sopenharmony_ci } 41978c2ecf20Sopenharmony_ci } else { 41988c2ecf20Sopenharmony_ci w_param_text = NULL; 41998c2ecf20Sopenharmony_ci } 42008c2ecf20Sopenharmony_ci dev_dbg(card->dev, "ASoC: adding %s widget\n", link_name); 42018c2ecf20Sopenharmony_ci 42028c2ecf20Sopenharmony_ci w = snd_soc_dapm_new_control_unlocked(&card->dapm, &template); 42038c2ecf20Sopenharmony_ci if (IS_ERR(w)) { 42048c2ecf20Sopenharmony_ci ret = PTR_ERR(w); 42058c2ecf20Sopenharmony_ci dev_err(rtd->dev, "ASoC: Failed to create %s widget: %d\n", 42068c2ecf20Sopenharmony_ci link_name, ret); 42078c2ecf20Sopenharmony_ci goto outfree_kcontrol_news; 42088c2ecf20Sopenharmony_ci } 42098c2ecf20Sopenharmony_ci 42108c2ecf20Sopenharmony_ci w->priv = substream; 42118c2ecf20Sopenharmony_ci 42128c2ecf20Sopenharmony_ci return w; 42138c2ecf20Sopenharmony_ci 42148c2ecf20Sopenharmony_cioutfree_kcontrol_news: 42158c2ecf20Sopenharmony_ci devm_kfree(card->dev, (void *)template.kcontrol_news); 42168c2ecf20Sopenharmony_ci snd_soc_dapm_free_kcontrol(card, &private_value, 42178c2ecf20Sopenharmony_ci rtd->dai_link->num_params, w_param_text); 42188c2ecf20Sopenharmony_ciparam_fail: 42198c2ecf20Sopenharmony_ci devm_kfree(card->dev, link_name); 42208c2ecf20Sopenharmony_ci return ERR_PTR(ret); 42218c2ecf20Sopenharmony_ci} 42228c2ecf20Sopenharmony_ci 42238c2ecf20Sopenharmony_ciint snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm, 42248c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 42258c2ecf20Sopenharmony_ci{ 42268c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget template; 42278c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *w; 42288c2ecf20Sopenharmony_ci 42298c2ecf20Sopenharmony_ci WARN_ON(dapm->dev != dai->dev); 42308c2ecf20Sopenharmony_ci 42318c2ecf20Sopenharmony_ci memset(&template, 0, sizeof(template)); 42328c2ecf20Sopenharmony_ci template.reg = SND_SOC_NOPM; 42338c2ecf20Sopenharmony_ci 42348c2ecf20Sopenharmony_ci if (dai->driver->playback.stream_name) { 42358c2ecf20Sopenharmony_ci template.id = snd_soc_dapm_dai_in; 42368c2ecf20Sopenharmony_ci template.name = dai->driver->playback.stream_name; 42378c2ecf20Sopenharmony_ci template.sname = dai->driver->playback.stream_name; 42388c2ecf20Sopenharmony_ci 42398c2ecf20Sopenharmony_ci dev_dbg(dai->dev, "ASoC: adding %s widget\n", 42408c2ecf20Sopenharmony_ci template.name); 42418c2ecf20Sopenharmony_ci 42428c2ecf20Sopenharmony_ci w = snd_soc_dapm_new_control_unlocked(dapm, &template); 42438c2ecf20Sopenharmony_ci if (IS_ERR(w)) 42448c2ecf20Sopenharmony_ci return PTR_ERR(w); 42458c2ecf20Sopenharmony_ci 42468c2ecf20Sopenharmony_ci w->priv = dai; 42478c2ecf20Sopenharmony_ci dai->playback_widget = w; 42488c2ecf20Sopenharmony_ci } 42498c2ecf20Sopenharmony_ci 42508c2ecf20Sopenharmony_ci if (dai->driver->capture.stream_name) { 42518c2ecf20Sopenharmony_ci template.id = snd_soc_dapm_dai_out; 42528c2ecf20Sopenharmony_ci template.name = dai->driver->capture.stream_name; 42538c2ecf20Sopenharmony_ci template.sname = dai->driver->capture.stream_name; 42548c2ecf20Sopenharmony_ci 42558c2ecf20Sopenharmony_ci dev_dbg(dai->dev, "ASoC: adding %s widget\n", 42568c2ecf20Sopenharmony_ci template.name); 42578c2ecf20Sopenharmony_ci 42588c2ecf20Sopenharmony_ci w = snd_soc_dapm_new_control_unlocked(dapm, &template); 42598c2ecf20Sopenharmony_ci if (IS_ERR(w)) 42608c2ecf20Sopenharmony_ci return PTR_ERR(w); 42618c2ecf20Sopenharmony_ci 42628c2ecf20Sopenharmony_ci w->priv = dai; 42638c2ecf20Sopenharmony_ci dai->capture_widget = w; 42648c2ecf20Sopenharmony_ci } 42658c2ecf20Sopenharmony_ci 42668c2ecf20Sopenharmony_ci return 0; 42678c2ecf20Sopenharmony_ci} 42688c2ecf20Sopenharmony_ci 42698c2ecf20Sopenharmony_ciint snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card) 42708c2ecf20Sopenharmony_ci{ 42718c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *dai_w, *w; 42728c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *src, *sink; 42738c2ecf20Sopenharmony_ci struct snd_soc_dai *dai; 42748c2ecf20Sopenharmony_ci 42758c2ecf20Sopenharmony_ci /* For each DAI widget... */ 42768c2ecf20Sopenharmony_ci for_each_card_widgets(card, dai_w) { 42778c2ecf20Sopenharmony_ci switch (dai_w->id) { 42788c2ecf20Sopenharmony_ci case snd_soc_dapm_dai_in: 42798c2ecf20Sopenharmony_ci case snd_soc_dapm_dai_out: 42808c2ecf20Sopenharmony_ci break; 42818c2ecf20Sopenharmony_ci default: 42828c2ecf20Sopenharmony_ci continue; 42838c2ecf20Sopenharmony_ci } 42848c2ecf20Sopenharmony_ci 42858c2ecf20Sopenharmony_ci /* let users know there is no DAI to link */ 42868c2ecf20Sopenharmony_ci if (!dai_w->priv) { 42878c2ecf20Sopenharmony_ci dev_dbg(card->dev, "dai widget %s has no DAI\n", 42888c2ecf20Sopenharmony_ci dai_w->name); 42898c2ecf20Sopenharmony_ci continue; 42908c2ecf20Sopenharmony_ci } 42918c2ecf20Sopenharmony_ci 42928c2ecf20Sopenharmony_ci dai = dai_w->priv; 42938c2ecf20Sopenharmony_ci 42948c2ecf20Sopenharmony_ci /* ...find all widgets with the same stream and link them */ 42958c2ecf20Sopenharmony_ci for_each_card_widgets(card, w) { 42968c2ecf20Sopenharmony_ci if (w->dapm != dai_w->dapm) 42978c2ecf20Sopenharmony_ci continue; 42988c2ecf20Sopenharmony_ci 42998c2ecf20Sopenharmony_ci switch (w->id) { 43008c2ecf20Sopenharmony_ci case snd_soc_dapm_dai_in: 43018c2ecf20Sopenharmony_ci case snd_soc_dapm_dai_out: 43028c2ecf20Sopenharmony_ci continue; 43038c2ecf20Sopenharmony_ci default: 43048c2ecf20Sopenharmony_ci break; 43058c2ecf20Sopenharmony_ci } 43068c2ecf20Sopenharmony_ci 43078c2ecf20Sopenharmony_ci if (!w->sname || !strstr(w->sname, dai_w->sname)) 43088c2ecf20Sopenharmony_ci continue; 43098c2ecf20Sopenharmony_ci 43108c2ecf20Sopenharmony_ci if (dai_w->id == snd_soc_dapm_dai_in) { 43118c2ecf20Sopenharmony_ci src = dai_w; 43128c2ecf20Sopenharmony_ci sink = w; 43138c2ecf20Sopenharmony_ci } else { 43148c2ecf20Sopenharmony_ci src = w; 43158c2ecf20Sopenharmony_ci sink = dai_w; 43168c2ecf20Sopenharmony_ci } 43178c2ecf20Sopenharmony_ci dev_dbg(dai->dev, "%s -> %s\n", src->name, sink->name); 43188c2ecf20Sopenharmony_ci snd_soc_dapm_add_path(w->dapm, src, sink, NULL, NULL); 43198c2ecf20Sopenharmony_ci } 43208c2ecf20Sopenharmony_ci } 43218c2ecf20Sopenharmony_ci 43228c2ecf20Sopenharmony_ci return 0; 43238c2ecf20Sopenharmony_ci} 43248c2ecf20Sopenharmony_ci 43258c2ecf20Sopenharmony_cistatic void dapm_connect_dai_routes(struct snd_soc_dapm_context *dapm, 43268c2ecf20Sopenharmony_ci struct snd_soc_dai *src_dai, 43278c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *src, 43288c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *dai, 43298c2ecf20Sopenharmony_ci struct snd_soc_dai *sink_dai, 43308c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *sink) 43318c2ecf20Sopenharmony_ci{ 43328c2ecf20Sopenharmony_ci dev_dbg(dapm->dev, "connected DAI link %s:%s -> %s:%s\n", 43338c2ecf20Sopenharmony_ci src_dai->component->name, src->name, 43348c2ecf20Sopenharmony_ci sink_dai->component->name, sink->name); 43358c2ecf20Sopenharmony_ci 43368c2ecf20Sopenharmony_ci if (dai) { 43378c2ecf20Sopenharmony_ci snd_soc_dapm_add_path(dapm, src, dai, NULL, NULL); 43388c2ecf20Sopenharmony_ci src = dai; 43398c2ecf20Sopenharmony_ci } 43408c2ecf20Sopenharmony_ci 43418c2ecf20Sopenharmony_ci snd_soc_dapm_add_path(dapm, src, sink, NULL, NULL); 43428c2ecf20Sopenharmony_ci} 43438c2ecf20Sopenharmony_ci 43448c2ecf20Sopenharmony_cistatic void dapm_connect_dai_pair(struct snd_soc_card *card, 43458c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd, 43468c2ecf20Sopenharmony_ci struct snd_soc_dai *codec_dai, 43478c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai) 43488c2ecf20Sopenharmony_ci{ 43498c2ecf20Sopenharmony_ci struct snd_soc_dai_link *dai_link = rtd->dai_link; 43508c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *dai, *codec, *playback_cpu, *capture_cpu; 43518c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream; 43528c2ecf20Sopenharmony_ci struct snd_pcm_str *streams = rtd->pcm->streams; 43538c2ecf20Sopenharmony_ci 43548c2ecf20Sopenharmony_ci if (dai_link->params) { 43558c2ecf20Sopenharmony_ci playback_cpu = cpu_dai->capture_widget; 43568c2ecf20Sopenharmony_ci capture_cpu = cpu_dai->playback_widget; 43578c2ecf20Sopenharmony_ci } else { 43588c2ecf20Sopenharmony_ci playback_cpu = cpu_dai->playback_widget; 43598c2ecf20Sopenharmony_ci capture_cpu = cpu_dai->capture_widget; 43608c2ecf20Sopenharmony_ci } 43618c2ecf20Sopenharmony_ci 43628c2ecf20Sopenharmony_ci /* connect BE DAI playback if widgets are valid */ 43638c2ecf20Sopenharmony_ci codec = codec_dai->playback_widget; 43648c2ecf20Sopenharmony_ci 43658c2ecf20Sopenharmony_ci if (playback_cpu && codec) { 43668c2ecf20Sopenharmony_ci if (dai_link->params && !rtd->playback_widget) { 43678c2ecf20Sopenharmony_ci substream = streams[SNDRV_PCM_STREAM_PLAYBACK].substream; 43688c2ecf20Sopenharmony_ci dai = snd_soc_dapm_new_dai(card, substream, "playback"); 43698c2ecf20Sopenharmony_ci if (IS_ERR(dai)) 43708c2ecf20Sopenharmony_ci goto capture; 43718c2ecf20Sopenharmony_ci rtd->playback_widget = dai; 43728c2ecf20Sopenharmony_ci } 43738c2ecf20Sopenharmony_ci 43748c2ecf20Sopenharmony_ci dapm_connect_dai_routes(&card->dapm, cpu_dai, playback_cpu, 43758c2ecf20Sopenharmony_ci rtd->playback_widget, 43768c2ecf20Sopenharmony_ci codec_dai, codec); 43778c2ecf20Sopenharmony_ci } 43788c2ecf20Sopenharmony_ci 43798c2ecf20Sopenharmony_cicapture: 43808c2ecf20Sopenharmony_ci /* connect BE DAI capture if widgets are valid */ 43818c2ecf20Sopenharmony_ci codec = codec_dai->capture_widget; 43828c2ecf20Sopenharmony_ci 43838c2ecf20Sopenharmony_ci if (codec && capture_cpu) { 43848c2ecf20Sopenharmony_ci if (dai_link->params && !rtd->capture_widget) { 43858c2ecf20Sopenharmony_ci substream = streams[SNDRV_PCM_STREAM_CAPTURE].substream; 43868c2ecf20Sopenharmony_ci dai = snd_soc_dapm_new_dai(card, substream, "capture"); 43878c2ecf20Sopenharmony_ci if (IS_ERR(dai)) 43888c2ecf20Sopenharmony_ci return; 43898c2ecf20Sopenharmony_ci rtd->capture_widget = dai; 43908c2ecf20Sopenharmony_ci } 43918c2ecf20Sopenharmony_ci 43928c2ecf20Sopenharmony_ci dapm_connect_dai_routes(&card->dapm, codec_dai, codec, 43938c2ecf20Sopenharmony_ci rtd->capture_widget, 43948c2ecf20Sopenharmony_ci cpu_dai, capture_cpu); 43958c2ecf20Sopenharmony_ci } 43968c2ecf20Sopenharmony_ci} 43978c2ecf20Sopenharmony_ci 43988c2ecf20Sopenharmony_cistatic void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream, 43998c2ecf20Sopenharmony_ci int event) 44008c2ecf20Sopenharmony_ci{ 44018c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *w; 44028c2ecf20Sopenharmony_ci unsigned int ep; 44038c2ecf20Sopenharmony_ci 44048c2ecf20Sopenharmony_ci w = snd_soc_dai_get_widget(dai, stream); 44058c2ecf20Sopenharmony_ci 44068c2ecf20Sopenharmony_ci if (w) { 44078c2ecf20Sopenharmony_ci dapm_mark_dirty(w, "stream event"); 44088c2ecf20Sopenharmony_ci 44098c2ecf20Sopenharmony_ci if (w->id == snd_soc_dapm_dai_in) { 44108c2ecf20Sopenharmony_ci ep = SND_SOC_DAPM_EP_SOURCE; 44118c2ecf20Sopenharmony_ci dapm_widget_invalidate_input_paths(w); 44128c2ecf20Sopenharmony_ci } else { 44138c2ecf20Sopenharmony_ci ep = SND_SOC_DAPM_EP_SINK; 44148c2ecf20Sopenharmony_ci dapm_widget_invalidate_output_paths(w); 44158c2ecf20Sopenharmony_ci } 44168c2ecf20Sopenharmony_ci 44178c2ecf20Sopenharmony_ci switch (event) { 44188c2ecf20Sopenharmony_ci case SND_SOC_DAPM_STREAM_START: 44198c2ecf20Sopenharmony_ci w->active = 1; 44208c2ecf20Sopenharmony_ci w->is_ep = ep; 44218c2ecf20Sopenharmony_ci break; 44228c2ecf20Sopenharmony_ci case SND_SOC_DAPM_STREAM_STOP: 44238c2ecf20Sopenharmony_ci w->active = 0; 44248c2ecf20Sopenharmony_ci w->is_ep = 0; 44258c2ecf20Sopenharmony_ci break; 44268c2ecf20Sopenharmony_ci case SND_SOC_DAPM_STREAM_SUSPEND: 44278c2ecf20Sopenharmony_ci case SND_SOC_DAPM_STREAM_RESUME: 44288c2ecf20Sopenharmony_ci case SND_SOC_DAPM_STREAM_PAUSE_PUSH: 44298c2ecf20Sopenharmony_ci case SND_SOC_DAPM_STREAM_PAUSE_RELEASE: 44308c2ecf20Sopenharmony_ci break; 44318c2ecf20Sopenharmony_ci } 44328c2ecf20Sopenharmony_ci } 44338c2ecf20Sopenharmony_ci} 44348c2ecf20Sopenharmony_ci 44358c2ecf20Sopenharmony_civoid snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card) 44368c2ecf20Sopenharmony_ci{ 44378c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd; 44388c2ecf20Sopenharmony_ci struct snd_soc_dai *codec_dai; 44398c2ecf20Sopenharmony_ci int i; 44408c2ecf20Sopenharmony_ci 44418c2ecf20Sopenharmony_ci /* for each BE DAI link... */ 44428c2ecf20Sopenharmony_ci for_each_card_rtds(card, rtd) { 44438c2ecf20Sopenharmony_ci /* 44448c2ecf20Sopenharmony_ci * dynamic FE links have no fixed DAI mapping. 44458c2ecf20Sopenharmony_ci * CODEC<->CODEC links have no direct connection. 44468c2ecf20Sopenharmony_ci */ 44478c2ecf20Sopenharmony_ci if (rtd->dai_link->dynamic) 44488c2ecf20Sopenharmony_ci continue; 44498c2ecf20Sopenharmony_ci 44508c2ecf20Sopenharmony_ci if (rtd->num_cpus == 1) { 44518c2ecf20Sopenharmony_ci for_each_rtd_codec_dais(rtd, i, codec_dai) 44528c2ecf20Sopenharmony_ci dapm_connect_dai_pair(card, rtd, codec_dai, 44538c2ecf20Sopenharmony_ci asoc_rtd_to_cpu(rtd, 0)); 44548c2ecf20Sopenharmony_ci } else if (rtd->num_codecs == rtd->num_cpus) { 44558c2ecf20Sopenharmony_ci for_each_rtd_codec_dais(rtd, i, codec_dai) 44568c2ecf20Sopenharmony_ci dapm_connect_dai_pair(card, rtd, codec_dai, 44578c2ecf20Sopenharmony_ci asoc_rtd_to_cpu(rtd, i)); 44588c2ecf20Sopenharmony_ci } else { 44598c2ecf20Sopenharmony_ci dev_err(card->dev, 44608c2ecf20Sopenharmony_ci "N cpus to M codecs link is not supported yet\n"); 44618c2ecf20Sopenharmony_ci } 44628c2ecf20Sopenharmony_ci } 44638c2ecf20Sopenharmony_ci} 44648c2ecf20Sopenharmony_ci 44658c2ecf20Sopenharmony_cistatic void soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream, 44668c2ecf20Sopenharmony_ci int event) 44678c2ecf20Sopenharmony_ci{ 44688c2ecf20Sopenharmony_ci struct snd_soc_dai *dai; 44698c2ecf20Sopenharmony_ci int i; 44708c2ecf20Sopenharmony_ci 44718c2ecf20Sopenharmony_ci for_each_rtd_dais(rtd, i, dai) 44728c2ecf20Sopenharmony_ci soc_dapm_dai_stream_event(dai, stream, event); 44738c2ecf20Sopenharmony_ci 44748c2ecf20Sopenharmony_ci dapm_power_widgets(rtd->card, event); 44758c2ecf20Sopenharmony_ci} 44768c2ecf20Sopenharmony_ci 44778c2ecf20Sopenharmony_ci/** 44788c2ecf20Sopenharmony_ci * snd_soc_dapm_stream_event - send a stream event to the dapm core 44798c2ecf20Sopenharmony_ci * @rtd: PCM runtime data 44808c2ecf20Sopenharmony_ci * @stream: stream name 44818c2ecf20Sopenharmony_ci * @event: stream event 44828c2ecf20Sopenharmony_ci * 44838c2ecf20Sopenharmony_ci * Sends a stream event to the dapm core. The core then makes any 44848c2ecf20Sopenharmony_ci * necessary widget power changes. 44858c2ecf20Sopenharmony_ci * 44868c2ecf20Sopenharmony_ci * Returns 0 for success else error. 44878c2ecf20Sopenharmony_ci */ 44888c2ecf20Sopenharmony_civoid snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream, 44898c2ecf20Sopenharmony_ci int event) 44908c2ecf20Sopenharmony_ci{ 44918c2ecf20Sopenharmony_ci struct snd_soc_card *card = rtd->card; 44928c2ecf20Sopenharmony_ci 44938c2ecf20Sopenharmony_ci mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); 44948c2ecf20Sopenharmony_ci soc_dapm_stream_event(rtd, stream, event); 44958c2ecf20Sopenharmony_ci mutex_unlock(&card->dapm_mutex); 44968c2ecf20Sopenharmony_ci} 44978c2ecf20Sopenharmony_ci 44988c2ecf20Sopenharmony_civoid snd_soc_dapm_stream_stop(struct snd_soc_pcm_runtime *rtd, int stream) 44998c2ecf20Sopenharmony_ci{ 45008c2ecf20Sopenharmony_ci if (stream == SNDRV_PCM_STREAM_PLAYBACK) { 45018c2ecf20Sopenharmony_ci if (snd_soc_runtime_ignore_pmdown_time(rtd)) { 45028c2ecf20Sopenharmony_ci /* powered down playback stream now */ 45038c2ecf20Sopenharmony_ci snd_soc_dapm_stream_event(rtd, 45048c2ecf20Sopenharmony_ci SNDRV_PCM_STREAM_PLAYBACK, 45058c2ecf20Sopenharmony_ci SND_SOC_DAPM_STREAM_STOP); 45068c2ecf20Sopenharmony_ci } else { 45078c2ecf20Sopenharmony_ci /* start delayed pop wq here for playback streams */ 45088c2ecf20Sopenharmony_ci rtd->pop_wait = 1; 45098c2ecf20Sopenharmony_ci queue_delayed_work(system_power_efficient_wq, 45108c2ecf20Sopenharmony_ci &rtd->delayed_work, 45118c2ecf20Sopenharmony_ci msecs_to_jiffies(rtd->pmdown_time)); 45128c2ecf20Sopenharmony_ci } 45138c2ecf20Sopenharmony_ci } else { 45148c2ecf20Sopenharmony_ci /* capture streams can be powered down now */ 45158c2ecf20Sopenharmony_ci snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_CAPTURE, 45168c2ecf20Sopenharmony_ci SND_SOC_DAPM_STREAM_STOP); 45178c2ecf20Sopenharmony_ci } 45188c2ecf20Sopenharmony_ci} 45198c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_stream_stop); 45208c2ecf20Sopenharmony_ci 45218c2ecf20Sopenharmony_ci/** 45228c2ecf20Sopenharmony_ci * snd_soc_dapm_enable_pin_unlocked - enable pin. 45238c2ecf20Sopenharmony_ci * @dapm: DAPM context 45248c2ecf20Sopenharmony_ci * @pin: pin name 45258c2ecf20Sopenharmony_ci * 45268c2ecf20Sopenharmony_ci * Enables input/output pin and its parents or children widgets iff there is 45278c2ecf20Sopenharmony_ci * a valid audio route and active audio stream. 45288c2ecf20Sopenharmony_ci * 45298c2ecf20Sopenharmony_ci * Requires external locking. 45308c2ecf20Sopenharmony_ci * 45318c2ecf20Sopenharmony_ci * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to 45328c2ecf20Sopenharmony_ci * do any widget power switching. 45338c2ecf20Sopenharmony_ci */ 45348c2ecf20Sopenharmony_ciint snd_soc_dapm_enable_pin_unlocked(struct snd_soc_dapm_context *dapm, 45358c2ecf20Sopenharmony_ci const char *pin) 45368c2ecf20Sopenharmony_ci{ 45378c2ecf20Sopenharmony_ci return snd_soc_dapm_set_pin(dapm, pin, 1); 45388c2ecf20Sopenharmony_ci} 45398c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_enable_pin_unlocked); 45408c2ecf20Sopenharmony_ci 45418c2ecf20Sopenharmony_ci/** 45428c2ecf20Sopenharmony_ci * snd_soc_dapm_enable_pin - enable pin. 45438c2ecf20Sopenharmony_ci * @dapm: DAPM context 45448c2ecf20Sopenharmony_ci * @pin: pin name 45458c2ecf20Sopenharmony_ci * 45468c2ecf20Sopenharmony_ci * Enables input/output pin and its parents or children widgets iff there is 45478c2ecf20Sopenharmony_ci * a valid audio route and active audio stream. 45488c2ecf20Sopenharmony_ci * 45498c2ecf20Sopenharmony_ci * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to 45508c2ecf20Sopenharmony_ci * do any widget power switching. 45518c2ecf20Sopenharmony_ci */ 45528c2ecf20Sopenharmony_ciint snd_soc_dapm_enable_pin(struct snd_soc_dapm_context *dapm, const char *pin) 45538c2ecf20Sopenharmony_ci{ 45548c2ecf20Sopenharmony_ci int ret; 45558c2ecf20Sopenharmony_ci 45568c2ecf20Sopenharmony_ci mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); 45578c2ecf20Sopenharmony_ci 45588c2ecf20Sopenharmony_ci ret = snd_soc_dapm_set_pin(dapm, pin, 1); 45598c2ecf20Sopenharmony_ci 45608c2ecf20Sopenharmony_ci mutex_unlock(&dapm->card->dapm_mutex); 45618c2ecf20Sopenharmony_ci 45628c2ecf20Sopenharmony_ci return ret; 45638c2ecf20Sopenharmony_ci} 45648c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_enable_pin); 45658c2ecf20Sopenharmony_ci 45668c2ecf20Sopenharmony_ci/** 45678c2ecf20Sopenharmony_ci * snd_soc_dapm_force_enable_pin_unlocked - force a pin to be enabled 45688c2ecf20Sopenharmony_ci * @dapm: DAPM context 45698c2ecf20Sopenharmony_ci * @pin: pin name 45708c2ecf20Sopenharmony_ci * 45718c2ecf20Sopenharmony_ci * Enables input/output pin regardless of any other state. This is 45728c2ecf20Sopenharmony_ci * intended for use with microphone bias supplies used in microphone 45738c2ecf20Sopenharmony_ci * jack detection. 45748c2ecf20Sopenharmony_ci * 45758c2ecf20Sopenharmony_ci * Requires external locking. 45768c2ecf20Sopenharmony_ci * 45778c2ecf20Sopenharmony_ci * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to 45788c2ecf20Sopenharmony_ci * do any widget power switching. 45798c2ecf20Sopenharmony_ci */ 45808c2ecf20Sopenharmony_ciint snd_soc_dapm_force_enable_pin_unlocked(struct snd_soc_dapm_context *dapm, 45818c2ecf20Sopenharmony_ci const char *pin) 45828c2ecf20Sopenharmony_ci{ 45838c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, true); 45848c2ecf20Sopenharmony_ci 45858c2ecf20Sopenharmony_ci if (!w) { 45868c2ecf20Sopenharmony_ci dev_err(dapm->dev, "ASoC: unknown pin %s\n", pin); 45878c2ecf20Sopenharmony_ci return -EINVAL; 45888c2ecf20Sopenharmony_ci } 45898c2ecf20Sopenharmony_ci 45908c2ecf20Sopenharmony_ci dev_dbg(w->dapm->dev, "ASoC: force enable pin %s\n", pin); 45918c2ecf20Sopenharmony_ci if (!w->connected) { 45928c2ecf20Sopenharmony_ci /* 45938c2ecf20Sopenharmony_ci * w->force does not affect the number of input or output paths, 45948c2ecf20Sopenharmony_ci * so we only have to recheck if w->connected is changed 45958c2ecf20Sopenharmony_ci */ 45968c2ecf20Sopenharmony_ci dapm_widget_invalidate_input_paths(w); 45978c2ecf20Sopenharmony_ci dapm_widget_invalidate_output_paths(w); 45988c2ecf20Sopenharmony_ci w->connected = 1; 45998c2ecf20Sopenharmony_ci } 46008c2ecf20Sopenharmony_ci w->force = 1; 46018c2ecf20Sopenharmony_ci dapm_mark_dirty(w, "force enable"); 46028c2ecf20Sopenharmony_ci 46038c2ecf20Sopenharmony_ci return 0; 46048c2ecf20Sopenharmony_ci} 46058c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_force_enable_pin_unlocked); 46068c2ecf20Sopenharmony_ci 46078c2ecf20Sopenharmony_ci/** 46088c2ecf20Sopenharmony_ci * snd_soc_dapm_force_enable_pin - force a pin to be enabled 46098c2ecf20Sopenharmony_ci * @dapm: DAPM context 46108c2ecf20Sopenharmony_ci * @pin: pin name 46118c2ecf20Sopenharmony_ci * 46128c2ecf20Sopenharmony_ci * Enables input/output pin regardless of any other state. This is 46138c2ecf20Sopenharmony_ci * intended for use with microphone bias supplies used in microphone 46148c2ecf20Sopenharmony_ci * jack detection. 46158c2ecf20Sopenharmony_ci * 46168c2ecf20Sopenharmony_ci * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to 46178c2ecf20Sopenharmony_ci * do any widget power switching. 46188c2ecf20Sopenharmony_ci */ 46198c2ecf20Sopenharmony_ciint snd_soc_dapm_force_enable_pin(struct snd_soc_dapm_context *dapm, 46208c2ecf20Sopenharmony_ci const char *pin) 46218c2ecf20Sopenharmony_ci{ 46228c2ecf20Sopenharmony_ci int ret; 46238c2ecf20Sopenharmony_ci 46248c2ecf20Sopenharmony_ci mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); 46258c2ecf20Sopenharmony_ci 46268c2ecf20Sopenharmony_ci ret = snd_soc_dapm_force_enable_pin_unlocked(dapm, pin); 46278c2ecf20Sopenharmony_ci 46288c2ecf20Sopenharmony_ci mutex_unlock(&dapm->card->dapm_mutex); 46298c2ecf20Sopenharmony_ci 46308c2ecf20Sopenharmony_ci return ret; 46318c2ecf20Sopenharmony_ci} 46328c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_force_enable_pin); 46338c2ecf20Sopenharmony_ci 46348c2ecf20Sopenharmony_ci/** 46358c2ecf20Sopenharmony_ci * snd_soc_dapm_disable_pin_unlocked - disable pin. 46368c2ecf20Sopenharmony_ci * @dapm: DAPM context 46378c2ecf20Sopenharmony_ci * @pin: pin name 46388c2ecf20Sopenharmony_ci * 46398c2ecf20Sopenharmony_ci * Disables input/output pin and its parents or children widgets. 46408c2ecf20Sopenharmony_ci * 46418c2ecf20Sopenharmony_ci * Requires external locking. 46428c2ecf20Sopenharmony_ci * 46438c2ecf20Sopenharmony_ci * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to 46448c2ecf20Sopenharmony_ci * do any widget power switching. 46458c2ecf20Sopenharmony_ci */ 46468c2ecf20Sopenharmony_ciint snd_soc_dapm_disable_pin_unlocked(struct snd_soc_dapm_context *dapm, 46478c2ecf20Sopenharmony_ci const char *pin) 46488c2ecf20Sopenharmony_ci{ 46498c2ecf20Sopenharmony_ci return snd_soc_dapm_set_pin(dapm, pin, 0); 46508c2ecf20Sopenharmony_ci} 46518c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_disable_pin_unlocked); 46528c2ecf20Sopenharmony_ci 46538c2ecf20Sopenharmony_ci/** 46548c2ecf20Sopenharmony_ci * snd_soc_dapm_disable_pin - disable pin. 46558c2ecf20Sopenharmony_ci * @dapm: DAPM context 46568c2ecf20Sopenharmony_ci * @pin: pin name 46578c2ecf20Sopenharmony_ci * 46588c2ecf20Sopenharmony_ci * Disables input/output pin and its parents or children widgets. 46598c2ecf20Sopenharmony_ci * 46608c2ecf20Sopenharmony_ci * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to 46618c2ecf20Sopenharmony_ci * do any widget power switching. 46628c2ecf20Sopenharmony_ci */ 46638c2ecf20Sopenharmony_ciint snd_soc_dapm_disable_pin(struct snd_soc_dapm_context *dapm, 46648c2ecf20Sopenharmony_ci const char *pin) 46658c2ecf20Sopenharmony_ci{ 46668c2ecf20Sopenharmony_ci int ret; 46678c2ecf20Sopenharmony_ci 46688c2ecf20Sopenharmony_ci mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); 46698c2ecf20Sopenharmony_ci 46708c2ecf20Sopenharmony_ci ret = snd_soc_dapm_set_pin(dapm, pin, 0); 46718c2ecf20Sopenharmony_ci 46728c2ecf20Sopenharmony_ci mutex_unlock(&dapm->card->dapm_mutex); 46738c2ecf20Sopenharmony_ci 46748c2ecf20Sopenharmony_ci return ret; 46758c2ecf20Sopenharmony_ci} 46768c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_disable_pin); 46778c2ecf20Sopenharmony_ci 46788c2ecf20Sopenharmony_ci/** 46798c2ecf20Sopenharmony_ci * snd_soc_dapm_nc_pin_unlocked - permanently disable pin. 46808c2ecf20Sopenharmony_ci * @dapm: DAPM context 46818c2ecf20Sopenharmony_ci * @pin: pin name 46828c2ecf20Sopenharmony_ci * 46838c2ecf20Sopenharmony_ci * Marks the specified pin as being not connected, disabling it along 46848c2ecf20Sopenharmony_ci * any parent or child widgets. At present this is identical to 46858c2ecf20Sopenharmony_ci * snd_soc_dapm_disable_pin() but in future it will be extended to do 46868c2ecf20Sopenharmony_ci * additional things such as disabling controls which only affect 46878c2ecf20Sopenharmony_ci * paths through the pin. 46888c2ecf20Sopenharmony_ci * 46898c2ecf20Sopenharmony_ci * Requires external locking. 46908c2ecf20Sopenharmony_ci * 46918c2ecf20Sopenharmony_ci * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to 46928c2ecf20Sopenharmony_ci * do any widget power switching. 46938c2ecf20Sopenharmony_ci */ 46948c2ecf20Sopenharmony_ciint snd_soc_dapm_nc_pin_unlocked(struct snd_soc_dapm_context *dapm, 46958c2ecf20Sopenharmony_ci const char *pin) 46968c2ecf20Sopenharmony_ci{ 46978c2ecf20Sopenharmony_ci return snd_soc_dapm_set_pin(dapm, pin, 0); 46988c2ecf20Sopenharmony_ci} 46998c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_nc_pin_unlocked); 47008c2ecf20Sopenharmony_ci 47018c2ecf20Sopenharmony_ci/** 47028c2ecf20Sopenharmony_ci * snd_soc_dapm_nc_pin - permanently disable pin. 47038c2ecf20Sopenharmony_ci * @dapm: DAPM context 47048c2ecf20Sopenharmony_ci * @pin: pin name 47058c2ecf20Sopenharmony_ci * 47068c2ecf20Sopenharmony_ci * Marks the specified pin as being not connected, disabling it along 47078c2ecf20Sopenharmony_ci * any parent or child widgets. At present this is identical to 47088c2ecf20Sopenharmony_ci * snd_soc_dapm_disable_pin() but in future it will be extended to do 47098c2ecf20Sopenharmony_ci * additional things such as disabling controls which only affect 47108c2ecf20Sopenharmony_ci * paths through the pin. 47118c2ecf20Sopenharmony_ci * 47128c2ecf20Sopenharmony_ci * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to 47138c2ecf20Sopenharmony_ci * do any widget power switching. 47148c2ecf20Sopenharmony_ci */ 47158c2ecf20Sopenharmony_ciint snd_soc_dapm_nc_pin(struct snd_soc_dapm_context *dapm, const char *pin) 47168c2ecf20Sopenharmony_ci{ 47178c2ecf20Sopenharmony_ci int ret; 47188c2ecf20Sopenharmony_ci 47198c2ecf20Sopenharmony_ci mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); 47208c2ecf20Sopenharmony_ci 47218c2ecf20Sopenharmony_ci ret = snd_soc_dapm_set_pin(dapm, pin, 0); 47228c2ecf20Sopenharmony_ci 47238c2ecf20Sopenharmony_ci mutex_unlock(&dapm->card->dapm_mutex); 47248c2ecf20Sopenharmony_ci 47258c2ecf20Sopenharmony_ci return ret; 47268c2ecf20Sopenharmony_ci} 47278c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_nc_pin); 47288c2ecf20Sopenharmony_ci 47298c2ecf20Sopenharmony_ci/** 47308c2ecf20Sopenharmony_ci * snd_soc_dapm_get_pin_status - get audio pin status 47318c2ecf20Sopenharmony_ci * @dapm: DAPM context 47328c2ecf20Sopenharmony_ci * @pin: audio signal pin endpoint (or start point) 47338c2ecf20Sopenharmony_ci * 47348c2ecf20Sopenharmony_ci * Get audio pin status - connected or disconnected. 47358c2ecf20Sopenharmony_ci * 47368c2ecf20Sopenharmony_ci * Returns 1 for connected otherwise 0. 47378c2ecf20Sopenharmony_ci */ 47388c2ecf20Sopenharmony_ciint snd_soc_dapm_get_pin_status(struct snd_soc_dapm_context *dapm, 47398c2ecf20Sopenharmony_ci const char *pin) 47408c2ecf20Sopenharmony_ci{ 47418c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, true); 47428c2ecf20Sopenharmony_ci 47438c2ecf20Sopenharmony_ci if (w) 47448c2ecf20Sopenharmony_ci return w->connected; 47458c2ecf20Sopenharmony_ci 47468c2ecf20Sopenharmony_ci return 0; 47478c2ecf20Sopenharmony_ci} 47488c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_get_pin_status); 47498c2ecf20Sopenharmony_ci 47508c2ecf20Sopenharmony_ci/** 47518c2ecf20Sopenharmony_ci * snd_soc_dapm_ignore_suspend - ignore suspend status for DAPM endpoint 47528c2ecf20Sopenharmony_ci * @dapm: DAPM context 47538c2ecf20Sopenharmony_ci * @pin: audio signal pin endpoint (or start point) 47548c2ecf20Sopenharmony_ci * 47558c2ecf20Sopenharmony_ci * Mark the given endpoint or pin as ignoring suspend. When the 47568c2ecf20Sopenharmony_ci * system is disabled a path between two endpoints flagged as ignoring 47578c2ecf20Sopenharmony_ci * suspend will not be disabled. The path must already be enabled via 47588c2ecf20Sopenharmony_ci * normal means at suspend time, it will not be turned on if it was not 47598c2ecf20Sopenharmony_ci * already enabled. 47608c2ecf20Sopenharmony_ci */ 47618c2ecf20Sopenharmony_ciint snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm, 47628c2ecf20Sopenharmony_ci const char *pin) 47638c2ecf20Sopenharmony_ci{ 47648c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, false); 47658c2ecf20Sopenharmony_ci 47668c2ecf20Sopenharmony_ci if (!w) { 47678c2ecf20Sopenharmony_ci dev_err(dapm->dev, "ASoC: unknown pin %s\n", pin); 47688c2ecf20Sopenharmony_ci return -EINVAL; 47698c2ecf20Sopenharmony_ci } 47708c2ecf20Sopenharmony_ci 47718c2ecf20Sopenharmony_ci w->ignore_suspend = 1; 47728c2ecf20Sopenharmony_ci 47738c2ecf20Sopenharmony_ci return 0; 47748c2ecf20Sopenharmony_ci} 47758c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_ignore_suspend); 47768c2ecf20Sopenharmony_ci 47778c2ecf20Sopenharmony_ci/** 47788c2ecf20Sopenharmony_ci * snd_soc_dapm_free - free dapm resources 47798c2ecf20Sopenharmony_ci * @dapm: DAPM context 47808c2ecf20Sopenharmony_ci * 47818c2ecf20Sopenharmony_ci * Free all dapm widgets and resources. 47828c2ecf20Sopenharmony_ci */ 47838c2ecf20Sopenharmony_civoid snd_soc_dapm_free(struct snd_soc_dapm_context *dapm) 47848c2ecf20Sopenharmony_ci{ 47858c2ecf20Sopenharmony_ci dapm_debugfs_cleanup(dapm); 47868c2ecf20Sopenharmony_ci dapm_free_widgets(dapm); 47878c2ecf20Sopenharmony_ci list_del(&dapm->list); 47888c2ecf20Sopenharmony_ci} 47898c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_free); 47908c2ecf20Sopenharmony_ci 47918c2ecf20Sopenharmony_civoid snd_soc_dapm_init(struct snd_soc_dapm_context *dapm, 47928c2ecf20Sopenharmony_ci struct snd_soc_card *card, 47938c2ecf20Sopenharmony_ci struct snd_soc_component *component) 47948c2ecf20Sopenharmony_ci{ 47958c2ecf20Sopenharmony_ci dapm->card = card; 47968c2ecf20Sopenharmony_ci dapm->component = component; 47978c2ecf20Sopenharmony_ci dapm->bias_level = SND_SOC_BIAS_OFF; 47988c2ecf20Sopenharmony_ci 47998c2ecf20Sopenharmony_ci if (component) { 48008c2ecf20Sopenharmony_ci dapm->dev = component->dev; 48018c2ecf20Sopenharmony_ci dapm->idle_bias_off = !component->driver->idle_bias_on, 48028c2ecf20Sopenharmony_ci dapm->suspend_bias_off = component->driver->suspend_bias_off; 48038c2ecf20Sopenharmony_ci } else { 48048c2ecf20Sopenharmony_ci dapm->dev = card->dev; 48058c2ecf20Sopenharmony_ci } 48068c2ecf20Sopenharmony_ci 48078c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dapm->list); 48088c2ecf20Sopenharmony_ci /* see for_each_card_dapms */ 48098c2ecf20Sopenharmony_ci list_add(&dapm->list, &card->dapm_list); 48108c2ecf20Sopenharmony_ci} 48118c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dapm_init); 48128c2ecf20Sopenharmony_ci 48138c2ecf20Sopenharmony_cistatic void soc_dapm_shutdown_dapm(struct snd_soc_dapm_context *dapm) 48148c2ecf20Sopenharmony_ci{ 48158c2ecf20Sopenharmony_ci struct snd_soc_card *card = dapm->card; 48168c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *w; 48178c2ecf20Sopenharmony_ci LIST_HEAD(down_list); 48188c2ecf20Sopenharmony_ci int powerdown = 0; 48198c2ecf20Sopenharmony_ci 48208c2ecf20Sopenharmony_ci mutex_lock(&card->dapm_mutex); 48218c2ecf20Sopenharmony_ci 48228c2ecf20Sopenharmony_ci for_each_card_widgets(dapm->card, w) { 48238c2ecf20Sopenharmony_ci if (w->dapm != dapm) 48248c2ecf20Sopenharmony_ci continue; 48258c2ecf20Sopenharmony_ci if (w->power) { 48268c2ecf20Sopenharmony_ci dapm_seq_insert(w, &down_list, false); 48278c2ecf20Sopenharmony_ci w->new_power = 0; 48288c2ecf20Sopenharmony_ci powerdown = 1; 48298c2ecf20Sopenharmony_ci } 48308c2ecf20Sopenharmony_ci } 48318c2ecf20Sopenharmony_ci 48328c2ecf20Sopenharmony_ci /* If there were no widgets to power down we're already in 48338c2ecf20Sopenharmony_ci * standby. 48348c2ecf20Sopenharmony_ci */ 48358c2ecf20Sopenharmony_ci if (powerdown) { 48368c2ecf20Sopenharmony_ci if (dapm->bias_level == SND_SOC_BIAS_ON) 48378c2ecf20Sopenharmony_ci snd_soc_dapm_set_bias_level(dapm, 48388c2ecf20Sopenharmony_ci SND_SOC_BIAS_PREPARE); 48398c2ecf20Sopenharmony_ci dapm_seq_run(card, &down_list, 0, false); 48408c2ecf20Sopenharmony_ci if (dapm->bias_level == SND_SOC_BIAS_PREPARE) 48418c2ecf20Sopenharmony_ci snd_soc_dapm_set_bias_level(dapm, 48428c2ecf20Sopenharmony_ci SND_SOC_BIAS_STANDBY); 48438c2ecf20Sopenharmony_ci } 48448c2ecf20Sopenharmony_ci 48458c2ecf20Sopenharmony_ci mutex_unlock(&card->dapm_mutex); 48468c2ecf20Sopenharmony_ci} 48478c2ecf20Sopenharmony_ci 48488c2ecf20Sopenharmony_ci/* 48498c2ecf20Sopenharmony_ci * snd_soc_dapm_shutdown - callback for system shutdown 48508c2ecf20Sopenharmony_ci */ 48518c2ecf20Sopenharmony_civoid snd_soc_dapm_shutdown(struct snd_soc_card *card) 48528c2ecf20Sopenharmony_ci{ 48538c2ecf20Sopenharmony_ci struct snd_soc_dapm_context *dapm; 48548c2ecf20Sopenharmony_ci 48558c2ecf20Sopenharmony_ci for_each_card_dapms(card, dapm) { 48568c2ecf20Sopenharmony_ci if (dapm != &card->dapm) { 48578c2ecf20Sopenharmony_ci soc_dapm_shutdown_dapm(dapm); 48588c2ecf20Sopenharmony_ci if (dapm->bias_level == SND_SOC_BIAS_STANDBY) 48598c2ecf20Sopenharmony_ci snd_soc_dapm_set_bias_level(dapm, 48608c2ecf20Sopenharmony_ci SND_SOC_BIAS_OFF); 48618c2ecf20Sopenharmony_ci } 48628c2ecf20Sopenharmony_ci } 48638c2ecf20Sopenharmony_ci 48648c2ecf20Sopenharmony_ci soc_dapm_shutdown_dapm(&card->dapm); 48658c2ecf20Sopenharmony_ci if (card->dapm.bias_level == SND_SOC_BIAS_STANDBY) 48668c2ecf20Sopenharmony_ci snd_soc_dapm_set_bias_level(&card->dapm, 48678c2ecf20Sopenharmony_ci SND_SOC_BIAS_OFF); 48688c2ecf20Sopenharmony_ci} 48698c2ecf20Sopenharmony_ci 48708c2ecf20Sopenharmony_ci/* Module information */ 48718c2ecf20Sopenharmony_ciMODULE_AUTHOR("Liam Girdwood, lrg@slimlogic.co.uk"); 48728c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Dynamic Audio Power Management core for ALSA SoC"); 48738c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 4874