18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// soc-pcm.c -- ALSA SoC PCM 48c2ecf20Sopenharmony_ci// 58c2ecf20Sopenharmony_ci// Copyright 2005 Wolfson Microelectronics PLC. 68c2ecf20Sopenharmony_ci// Copyright 2005 Openedhand Ltd. 78c2ecf20Sopenharmony_ci// Copyright (C) 2010 Slimlogic Ltd. 88c2ecf20Sopenharmony_ci// Copyright (C) 2010 Texas Instruments Inc. 98c2ecf20Sopenharmony_ci// 108c2ecf20Sopenharmony_ci// Authors: Liam Girdwood <lrg@ti.com> 118c2ecf20Sopenharmony_ci// Mark Brown <broonie@opensource.wolfsonmicro.com> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/init.h> 158c2ecf20Sopenharmony_ci#include <linux/delay.h> 168c2ecf20Sopenharmony_ci#include <linux/pinctrl/consumer.h> 178c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 208c2ecf20Sopenharmony_ci#include <linux/export.h> 218c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 228c2ecf20Sopenharmony_ci#include <sound/core.h> 238c2ecf20Sopenharmony_ci#include <sound/pcm.h> 248c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 258c2ecf20Sopenharmony_ci#include <sound/soc.h> 268c2ecf20Sopenharmony_ci#include <sound/soc-dpcm.h> 278c2ecf20Sopenharmony_ci#include <sound/soc-link.h> 288c2ecf20Sopenharmony_ci#include <sound/initval.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define DPCM_MAX_BE_USERS 8 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 338c2ecf20Sopenharmony_cistatic const char *dpcm_state_string(enum snd_soc_dpcm_state state) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci switch (state) { 368c2ecf20Sopenharmony_ci case SND_SOC_DPCM_STATE_NEW: 378c2ecf20Sopenharmony_ci return "new"; 388c2ecf20Sopenharmony_ci case SND_SOC_DPCM_STATE_OPEN: 398c2ecf20Sopenharmony_ci return "open"; 408c2ecf20Sopenharmony_ci case SND_SOC_DPCM_STATE_HW_PARAMS: 418c2ecf20Sopenharmony_ci return "hw_params"; 428c2ecf20Sopenharmony_ci case SND_SOC_DPCM_STATE_PREPARE: 438c2ecf20Sopenharmony_ci return "prepare"; 448c2ecf20Sopenharmony_ci case SND_SOC_DPCM_STATE_START: 458c2ecf20Sopenharmony_ci return "start"; 468c2ecf20Sopenharmony_ci case SND_SOC_DPCM_STATE_STOP: 478c2ecf20Sopenharmony_ci return "stop"; 488c2ecf20Sopenharmony_ci case SND_SOC_DPCM_STATE_SUSPEND: 498c2ecf20Sopenharmony_ci return "suspend"; 508c2ecf20Sopenharmony_ci case SND_SOC_DPCM_STATE_PAUSED: 518c2ecf20Sopenharmony_ci return "paused"; 528c2ecf20Sopenharmony_ci case SND_SOC_DPCM_STATE_HW_FREE: 538c2ecf20Sopenharmony_ci return "hw_free"; 548c2ecf20Sopenharmony_ci case SND_SOC_DPCM_STATE_CLOSE: 558c2ecf20Sopenharmony_ci return "close"; 568c2ecf20Sopenharmony_ci } 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci return "unknown"; 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe, 628c2ecf20Sopenharmony_ci int stream, char *buf, size_t size) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params = &fe->dpcm[stream].hw_params; 658c2ecf20Sopenharmony_ci struct snd_soc_dpcm *dpcm; 668c2ecf20Sopenharmony_ci ssize_t offset = 0; 678c2ecf20Sopenharmony_ci unsigned long flags; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci /* FE state */ 708c2ecf20Sopenharmony_ci offset += scnprintf(buf + offset, size - offset, 718c2ecf20Sopenharmony_ci "[%s - %s]\n", fe->dai_link->name, 728c2ecf20Sopenharmony_ci stream ? "Capture" : "Playback"); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci offset += scnprintf(buf + offset, size - offset, "State: %s\n", 758c2ecf20Sopenharmony_ci dpcm_state_string(fe->dpcm[stream].state)); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci if ((fe->dpcm[stream].state >= SND_SOC_DPCM_STATE_HW_PARAMS) && 788c2ecf20Sopenharmony_ci (fe->dpcm[stream].state <= SND_SOC_DPCM_STATE_STOP)) 798c2ecf20Sopenharmony_ci offset += scnprintf(buf + offset, size - offset, 808c2ecf20Sopenharmony_ci "Hardware Params: " 818c2ecf20Sopenharmony_ci "Format = %s, Channels = %d, Rate = %d\n", 828c2ecf20Sopenharmony_ci snd_pcm_format_name(params_format(params)), 838c2ecf20Sopenharmony_ci params_channels(params), 848c2ecf20Sopenharmony_ci params_rate(params)); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci /* BEs state */ 878c2ecf20Sopenharmony_ci offset += scnprintf(buf + offset, size - offset, "Backends:\n"); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (list_empty(&fe->dpcm[stream].be_clients)) { 908c2ecf20Sopenharmony_ci offset += scnprintf(buf + offset, size - offset, 918c2ecf20Sopenharmony_ci " No active DSP links\n"); 928c2ecf20Sopenharmony_ci goto out; 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci spin_lock_irqsave(&fe->card->dpcm_lock, flags); 968c2ecf20Sopenharmony_ci for_each_dpcm_be(fe, stream, dpcm) { 978c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *be = dpcm->be; 988c2ecf20Sopenharmony_ci params = &dpcm->hw_params; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci offset += scnprintf(buf + offset, size - offset, 1018c2ecf20Sopenharmony_ci "- %s\n", be->dai_link->name); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci offset += scnprintf(buf + offset, size - offset, 1048c2ecf20Sopenharmony_ci " State: %s\n", 1058c2ecf20Sopenharmony_ci dpcm_state_string(be->dpcm[stream].state)); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci if ((be->dpcm[stream].state >= SND_SOC_DPCM_STATE_HW_PARAMS) && 1088c2ecf20Sopenharmony_ci (be->dpcm[stream].state <= SND_SOC_DPCM_STATE_STOP)) 1098c2ecf20Sopenharmony_ci offset += scnprintf(buf + offset, size - offset, 1108c2ecf20Sopenharmony_ci " Hardware Params: " 1118c2ecf20Sopenharmony_ci "Format = %s, Channels = %d, Rate = %d\n", 1128c2ecf20Sopenharmony_ci snd_pcm_format_name(params_format(params)), 1138c2ecf20Sopenharmony_ci params_channels(params), 1148c2ecf20Sopenharmony_ci params_rate(params)); 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fe->card->dpcm_lock, flags); 1178c2ecf20Sopenharmony_ciout: 1188c2ecf20Sopenharmony_ci return offset; 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic ssize_t dpcm_state_read_file(struct file *file, char __user *user_buf, 1228c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *fe = file->private_data; 1258c2ecf20Sopenharmony_ci ssize_t out_count = PAGE_SIZE, offset = 0, ret = 0; 1268c2ecf20Sopenharmony_ci int stream; 1278c2ecf20Sopenharmony_ci char *buf; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (fe->num_cpus > 1) { 1308c2ecf20Sopenharmony_ci dev_err(fe->dev, 1318c2ecf20Sopenharmony_ci "%s doesn't support Multi CPU yet\n", __func__); 1328c2ecf20Sopenharmony_ci return -EINVAL; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci buf = kmalloc(out_count, GFP_KERNEL); 1368c2ecf20Sopenharmony_ci if (!buf) 1378c2ecf20Sopenharmony_ci return -ENOMEM; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci for_each_pcm_streams(stream) 1408c2ecf20Sopenharmony_ci if (snd_soc_dai_stream_valid(asoc_rtd_to_cpu(fe, 0), stream)) 1418c2ecf20Sopenharmony_ci offset += dpcm_show_state(fe, stream, 1428c2ecf20Sopenharmony_ci buf + offset, 1438c2ecf20Sopenharmony_ci out_count - offset); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci ret = simple_read_from_buffer(user_buf, count, ppos, buf, offset); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci kfree(buf); 1488c2ecf20Sopenharmony_ci return ret; 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic const struct file_operations dpcm_state_fops = { 1528c2ecf20Sopenharmony_ci .open = simple_open, 1538c2ecf20Sopenharmony_ci .read = dpcm_state_read_file, 1548c2ecf20Sopenharmony_ci .llseek = default_llseek, 1558c2ecf20Sopenharmony_ci}; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_civoid soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci if (!rtd->dai_link) 1608c2ecf20Sopenharmony_ci return; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci if (!rtd->dai_link->dynamic) 1638c2ecf20Sopenharmony_ci return; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci if (!rtd->card->debugfs_card_root) 1668c2ecf20Sopenharmony_ci return; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci rtd->debugfs_dpcm_root = debugfs_create_dir(rtd->dai_link->name, 1698c2ecf20Sopenharmony_ci rtd->card->debugfs_card_root); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci debugfs_create_file("state", 0444, rtd->debugfs_dpcm_root, 1728c2ecf20Sopenharmony_ci rtd, &dpcm_state_fops); 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic void dpcm_create_debugfs_state(struct snd_soc_dpcm *dpcm, int stream) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci char *name; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci name = kasprintf(GFP_KERNEL, "%s:%s", dpcm->be->dai_link->name, 1808c2ecf20Sopenharmony_ci stream ? "capture" : "playback"); 1818c2ecf20Sopenharmony_ci if (name) { 1828c2ecf20Sopenharmony_ci dpcm->debugfs_state = debugfs_create_dir( 1838c2ecf20Sopenharmony_ci name, dpcm->fe->debugfs_dpcm_root); 1848c2ecf20Sopenharmony_ci debugfs_create_u32("state", 0644, dpcm->debugfs_state, 1858c2ecf20Sopenharmony_ci &dpcm->state); 1868c2ecf20Sopenharmony_ci kfree(name); 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic void dpcm_remove_debugfs_state(struct snd_soc_dpcm *dpcm) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci debugfs_remove_recursive(dpcm->debugfs_state); 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci#else 1968c2ecf20Sopenharmony_cistatic inline void dpcm_create_debugfs_state(struct snd_soc_dpcm *dpcm, 1978c2ecf20Sopenharmony_ci int stream) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic inline void dpcm_remove_debugfs_state(struct snd_soc_dpcm *dpcm) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci#endif 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci/** 2078c2ecf20Sopenharmony_ci * snd_soc_runtime_action() - Increment/Decrement active count for 2088c2ecf20Sopenharmony_ci * PCM runtime components 2098c2ecf20Sopenharmony_ci * @rtd: ASoC PCM runtime that is activated 2108c2ecf20Sopenharmony_ci * @stream: Direction of the PCM stream 2118c2ecf20Sopenharmony_ci * @action: Activate stream if 1. Deactivate if -1. 2128c2ecf20Sopenharmony_ci * 2138c2ecf20Sopenharmony_ci * Increments/Decrements the active count for all the DAIs and components 2148c2ecf20Sopenharmony_ci * attached to a PCM runtime. 2158c2ecf20Sopenharmony_ci * Should typically be called when a stream is opened. 2168c2ecf20Sopenharmony_ci * 2178c2ecf20Sopenharmony_ci * Must be called with the rtd->card->pcm_mutex being held 2188c2ecf20Sopenharmony_ci */ 2198c2ecf20Sopenharmony_civoid snd_soc_runtime_action(struct snd_soc_pcm_runtime *rtd, 2208c2ecf20Sopenharmony_ci int stream, int action) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci struct snd_soc_dai *dai; 2238c2ecf20Sopenharmony_ci int i; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci lockdep_assert_held(&rtd->card->pcm_mutex); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci for_each_rtd_dais(rtd, i, dai) 2288c2ecf20Sopenharmony_ci snd_soc_dai_action(dai, stream, action); 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_runtime_action); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci/** 2338c2ecf20Sopenharmony_ci * snd_soc_runtime_ignore_pmdown_time() - Check whether to ignore the power down delay 2348c2ecf20Sopenharmony_ci * @rtd: The ASoC PCM runtime that should be checked. 2358c2ecf20Sopenharmony_ci * 2368c2ecf20Sopenharmony_ci * This function checks whether the power down delay should be ignored for a 2378c2ecf20Sopenharmony_ci * specific PCM runtime. Returns true if the delay is 0, if it the DAI link has 2388c2ecf20Sopenharmony_ci * been configured to ignore the delay, or if none of the components benefits 2398c2ecf20Sopenharmony_ci * from having the delay. 2408c2ecf20Sopenharmony_ci */ 2418c2ecf20Sopenharmony_cibool snd_soc_runtime_ignore_pmdown_time(struct snd_soc_pcm_runtime *rtd) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci struct snd_soc_component *component; 2448c2ecf20Sopenharmony_ci bool ignore = true; 2458c2ecf20Sopenharmony_ci int i; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci if (!rtd->pmdown_time || rtd->dai_link->ignore_pmdown_time) 2488c2ecf20Sopenharmony_ci return true; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci for_each_rtd_components(rtd, i, component) 2518c2ecf20Sopenharmony_ci ignore &= !component->driver->use_pmdown_time; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci return ignore; 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci/** 2578c2ecf20Sopenharmony_ci * snd_soc_set_runtime_hwparams - set the runtime hardware parameters 2588c2ecf20Sopenharmony_ci * @substream: the pcm substream 2598c2ecf20Sopenharmony_ci * @hw: the hardware parameters 2608c2ecf20Sopenharmony_ci * 2618c2ecf20Sopenharmony_ci * Sets the substream runtime hardware parameters. 2628c2ecf20Sopenharmony_ci */ 2638c2ecf20Sopenharmony_ciint snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream, 2648c2ecf20Sopenharmony_ci const struct snd_pcm_hardware *hw) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 2678c2ecf20Sopenharmony_ci runtime->hw.info = hw->info; 2688c2ecf20Sopenharmony_ci runtime->hw.formats = hw->formats; 2698c2ecf20Sopenharmony_ci runtime->hw.period_bytes_min = hw->period_bytes_min; 2708c2ecf20Sopenharmony_ci runtime->hw.period_bytes_max = hw->period_bytes_max; 2718c2ecf20Sopenharmony_ci runtime->hw.periods_min = hw->periods_min; 2728c2ecf20Sopenharmony_ci runtime->hw.periods_max = hw->periods_max; 2738c2ecf20Sopenharmony_ci runtime->hw.buffer_bytes_max = hw->buffer_bytes_max; 2748c2ecf20Sopenharmony_ci runtime->hw.fifo_size = hw->fifo_size; 2758c2ecf20Sopenharmony_ci return 0; 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_set_runtime_hwparams); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci/* DPCM stream event, send event to FE and all active BEs. */ 2808c2ecf20Sopenharmony_ciint dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir, 2818c2ecf20Sopenharmony_ci int event) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci struct snd_soc_dpcm *dpcm; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci for_each_dpcm_be(fe, dir, dpcm) { 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *be = dpcm->be; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci dev_dbg(be->dev, "ASoC: BE %s event %d dir %d\n", 2908c2ecf20Sopenharmony_ci be->dai_link->name, event, dir); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci if ((event == SND_SOC_DAPM_STREAM_STOP) && 2938c2ecf20Sopenharmony_ci (be->dpcm[dir].users >= 1)) 2948c2ecf20Sopenharmony_ci continue; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci snd_soc_dapm_stream_event(be, dir, event); 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci snd_soc_dapm_stream_event(fe, dir, event); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci return 0; 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cistatic int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream, 3058c2ecf20Sopenharmony_ci struct snd_soc_dai *soc_dai) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 3088c2ecf20Sopenharmony_ci int ret; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci if (soc_dai->rate && (soc_dai->driver->symmetric_rates || 3118c2ecf20Sopenharmony_ci rtd->dai_link->symmetric_rates)) { 3128c2ecf20Sopenharmony_ci dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %dHz rate\n", 3138c2ecf20Sopenharmony_ci soc_dai->rate); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci ret = snd_pcm_hw_constraint_single(substream->runtime, 3168c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_RATE, 3178c2ecf20Sopenharmony_ci soc_dai->rate); 3188c2ecf20Sopenharmony_ci if (ret < 0) { 3198c2ecf20Sopenharmony_ci dev_err(soc_dai->dev, 3208c2ecf20Sopenharmony_ci "ASoC: Unable to apply rate constraint: %d\n", 3218c2ecf20Sopenharmony_ci ret); 3228c2ecf20Sopenharmony_ci return ret; 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci if (soc_dai->channels && (soc_dai->driver->symmetric_channels || 3278c2ecf20Sopenharmony_ci rtd->dai_link->symmetric_channels)) { 3288c2ecf20Sopenharmony_ci dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %d channel(s)\n", 3298c2ecf20Sopenharmony_ci soc_dai->channels); 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci ret = snd_pcm_hw_constraint_single(substream->runtime, 3328c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_CHANNELS, 3338c2ecf20Sopenharmony_ci soc_dai->channels); 3348c2ecf20Sopenharmony_ci if (ret < 0) { 3358c2ecf20Sopenharmony_ci dev_err(soc_dai->dev, 3368c2ecf20Sopenharmony_ci "ASoC: Unable to apply channel symmetry constraint: %d\n", 3378c2ecf20Sopenharmony_ci ret); 3388c2ecf20Sopenharmony_ci return ret; 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci if (soc_dai->sample_bits && (soc_dai->driver->symmetric_samplebits || 3438c2ecf20Sopenharmony_ci rtd->dai_link->symmetric_samplebits)) { 3448c2ecf20Sopenharmony_ci dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %d sample bits\n", 3458c2ecf20Sopenharmony_ci soc_dai->sample_bits); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci ret = snd_pcm_hw_constraint_single(substream->runtime, 3488c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_SAMPLE_BITS, 3498c2ecf20Sopenharmony_ci soc_dai->sample_bits); 3508c2ecf20Sopenharmony_ci if (ret < 0) { 3518c2ecf20Sopenharmony_ci dev_err(soc_dai->dev, 3528c2ecf20Sopenharmony_ci "ASoC: Unable to apply sample bits symmetry constraint: %d\n", 3538c2ecf20Sopenharmony_ci ret); 3548c2ecf20Sopenharmony_ci return ret; 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci return 0; 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cistatic int soc_pcm_params_symmetry(struct snd_pcm_substream *substream, 3628c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params) 3638c2ecf20Sopenharmony_ci{ 3648c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 3658c2ecf20Sopenharmony_ci struct snd_soc_dai *dai; 3668c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai; 3678c2ecf20Sopenharmony_ci unsigned int rate, channels, sample_bits, symmetry, i; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci rate = params_rate(params); 3708c2ecf20Sopenharmony_ci channels = params_channels(params); 3718c2ecf20Sopenharmony_ci sample_bits = snd_pcm_format_physical_width(params_format(params)); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci /* reject unmatched parameters when applying symmetry */ 3748c2ecf20Sopenharmony_ci symmetry = rtd->dai_link->symmetric_rates; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci for_each_rtd_cpu_dais(rtd, i, dai) 3778c2ecf20Sopenharmony_ci symmetry |= dai->driver->symmetric_rates; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci if (symmetry) { 3808c2ecf20Sopenharmony_ci for_each_rtd_cpu_dais(rtd, i, cpu_dai) { 3818c2ecf20Sopenharmony_ci if (cpu_dai->rate && cpu_dai->rate != rate) { 3828c2ecf20Sopenharmony_ci dev_err(rtd->dev, "ASoC: unmatched rate symmetry: %d - %d\n", 3838c2ecf20Sopenharmony_ci cpu_dai->rate, rate); 3848c2ecf20Sopenharmony_ci return -EINVAL; 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci symmetry = rtd->dai_link->symmetric_channels; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci for_each_rtd_dais(rtd, i, dai) 3928c2ecf20Sopenharmony_ci symmetry |= dai->driver->symmetric_channels; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci if (symmetry) { 3958c2ecf20Sopenharmony_ci for_each_rtd_cpu_dais(rtd, i, cpu_dai) { 3968c2ecf20Sopenharmony_ci if (cpu_dai->channels && 3978c2ecf20Sopenharmony_ci cpu_dai->channels != channels) { 3988c2ecf20Sopenharmony_ci dev_err(rtd->dev, "ASoC: unmatched channel symmetry: %d - %d\n", 3998c2ecf20Sopenharmony_ci cpu_dai->channels, channels); 4008c2ecf20Sopenharmony_ci return -EINVAL; 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci symmetry = rtd->dai_link->symmetric_samplebits; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci for_each_rtd_dais(rtd, i, dai) 4088c2ecf20Sopenharmony_ci symmetry |= dai->driver->symmetric_samplebits; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci if (symmetry) { 4118c2ecf20Sopenharmony_ci for_each_rtd_cpu_dais(rtd, i, cpu_dai) { 4128c2ecf20Sopenharmony_ci if (cpu_dai->sample_bits && 4138c2ecf20Sopenharmony_ci cpu_dai->sample_bits != sample_bits) { 4148c2ecf20Sopenharmony_ci dev_err(rtd->dev, "ASoC: unmatched sample bits symmetry: %d - %d\n", 4158c2ecf20Sopenharmony_ci cpu_dai->sample_bits, sample_bits); 4168c2ecf20Sopenharmony_ci return -EINVAL; 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci } 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci return 0; 4228c2ecf20Sopenharmony_ci} 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_cistatic bool soc_pcm_has_symmetry(struct snd_pcm_substream *substream) 4258c2ecf20Sopenharmony_ci{ 4268c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 4278c2ecf20Sopenharmony_ci struct snd_soc_dai_link *link = rtd->dai_link; 4288c2ecf20Sopenharmony_ci struct snd_soc_dai *dai; 4298c2ecf20Sopenharmony_ci unsigned int symmetry, i; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci symmetry = link->symmetric_rates || 4328c2ecf20Sopenharmony_ci link->symmetric_channels || 4338c2ecf20Sopenharmony_ci link->symmetric_samplebits; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci for_each_rtd_dais(rtd, i, dai) 4368c2ecf20Sopenharmony_ci symmetry = symmetry || 4378c2ecf20Sopenharmony_ci dai->driver->symmetric_rates || 4388c2ecf20Sopenharmony_ci dai->driver->symmetric_channels || 4398c2ecf20Sopenharmony_ci dai->driver->symmetric_samplebits; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci return symmetry; 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_cistatic void soc_pcm_set_msb(struct snd_pcm_substream *substream, int bits) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 4478c2ecf20Sopenharmony_ci int ret; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci if (!bits) 4508c2ecf20Sopenharmony_ci return; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci ret = snd_pcm_hw_constraint_msbits(substream->runtime, 0, 0, bits); 4538c2ecf20Sopenharmony_ci if (ret != 0) 4548c2ecf20Sopenharmony_ci dev_warn(rtd->dev, "ASoC: Failed to set MSB %d: %d\n", 4558c2ecf20Sopenharmony_ci bits, ret); 4568c2ecf20Sopenharmony_ci} 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_cistatic void soc_pcm_apply_msb(struct snd_pcm_substream *substream) 4598c2ecf20Sopenharmony_ci{ 4608c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 4618c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai; 4628c2ecf20Sopenharmony_ci struct snd_soc_dai *codec_dai; 4638c2ecf20Sopenharmony_ci struct snd_soc_pcm_stream *pcm_codec, *pcm_cpu; 4648c2ecf20Sopenharmony_ci int stream = substream->stream; 4658c2ecf20Sopenharmony_ci int i; 4668c2ecf20Sopenharmony_ci unsigned int bits = 0, cpu_bits = 0; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci for_each_rtd_codec_dais(rtd, i, codec_dai) { 4698c2ecf20Sopenharmony_ci pcm_codec = snd_soc_dai_get_pcm_stream(codec_dai, stream); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci if (pcm_codec->sig_bits == 0) { 4728c2ecf20Sopenharmony_ci bits = 0; 4738c2ecf20Sopenharmony_ci break; 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci bits = max(pcm_codec->sig_bits, bits); 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci for_each_rtd_cpu_dais(rtd, i, cpu_dai) { 4798c2ecf20Sopenharmony_ci pcm_cpu = snd_soc_dai_get_pcm_stream(cpu_dai, stream); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci if (pcm_cpu->sig_bits == 0) { 4828c2ecf20Sopenharmony_ci cpu_bits = 0; 4838c2ecf20Sopenharmony_ci break; 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci cpu_bits = max(pcm_cpu->sig_bits, cpu_bits); 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci soc_pcm_set_msb(substream, bits); 4898c2ecf20Sopenharmony_ci soc_pcm_set_msb(substream, cpu_bits); 4908c2ecf20Sopenharmony_ci} 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci/** 4938c2ecf20Sopenharmony_ci * snd_soc_runtime_calc_hw() - Calculate hw limits for a PCM stream 4948c2ecf20Sopenharmony_ci * @rtd: ASoC PCM runtime 4958c2ecf20Sopenharmony_ci * @hw: PCM hardware parameters (output) 4968c2ecf20Sopenharmony_ci * @stream: Direction of the PCM stream 4978c2ecf20Sopenharmony_ci * 4988c2ecf20Sopenharmony_ci * Calculates the subset of stream parameters supported by all DAIs 4998c2ecf20Sopenharmony_ci * associated with the PCM stream. 5008c2ecf20Sopenharmony_ci */ 5018c2ecf20Sopenharmony_ciint snd_soc_runtime_calc_hw(struct snd_soc_pcm_runtime *rtd, 5028c2ecf20Sopenharmony_ci struct snd_pcm_hardware *hw, int stream) 5038c2ecf20Sopenharmony_ci{ 5048c2ecf20Sopenharmony_ci struct snd_soc_dai *codec_dai; 5058c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai; 5068c2ecf20Sopenharmony_ci struct snd_soc_pcm_stream *codec_stream; 5078c2ecf20Sopenharmony_ci struct snd_soc_pcm_stream *cpu_stream; 5088c2ecf20Sopenharmony_ci unsigned int chan_min = 0, chan_max = UINT_MAX; 5098c2ecf20Sopenharmony_ci unsigned int cpu_chan_min = 0, cpu_chan_max = UINT_MAX; 5108c2ecf20Sopenharmony_ci unsigned int rate_min = 0, rate_max = UINT_MAX; 5118c2ecf20Sopenharmony_ci unsigned int cpu_rate_min = 0, cpu_rate_max = UINT_MAX; 5128c2ecf20Sopenharmony_ci unsigned int rates = UINT_MAX, cpu_rates = UINT_MAX; 5138c2ecf20Sopenharmony_ci u64 formats = ULLONG_MAX; 5148c2ecf20Sopenharmony_ci int i; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci /* first calculate min/max only for CPUs in the DAI link */ 5178c2ecf20Sopenharmony_ci for_each_rtd_cpu_dais(rtd, i, cpu_dai) { 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci /* 5208c2ecf20Sopenharmony_ci * Skip CPUs which don't support the current stream type. 5218c2ecf20Sopenharmony_ci * Otherwise, since the rate, channel, and format values will 5228c2ecf20Sopenharmony_ci * zero in that case, we would have no usable settings left, 5238c2ecf20Sopenharmony_ci * causing the resulting setup to fail. 5248c2ecf20Sopenharmony_ci */ 5258c2ecf20Sopenharmony_ci if (!snd_soc_dai_stream_valid(cpu_dai, stream)) 5268c2ecf20Sopenharmony_ci continue; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci cpu_stream = snd_soc_dai_get_pcm_stream(cpu_dai, stream); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci cpu_chan_min = max(cpu_chan_min, cpu_stream->channels_min); 5318c2ecf20Sopenharmony_ci cpu_chan_max = min(cpu_chan_max, cpu_stream->channels_max); 5328c2ecf20Sopenharmony_ci cpu_rate_min = max(cpu_rate_min, cpu_stream->rate_min); 5338c2ecf20Sopenharmony_ci cpu_rate_max = min_not_zero(cpu_rate_max, cpu_stream->rate_max); 5348c2ecf20Sopenharmony_ci formats &= cpu_stream->formats; 5358c2ecf20Sopenharmony_ci cpu_rates = snd_pcm_rate_mask_intersect(cpu_stream->rates, 5368c2ecf20Sopenharmony_ci cpu_rates); 5378c2ecf20Sopenharmony_ci } 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci /* second calculate min/max only for CODECs in the DAI link */ 5408c2ecf20Sopenharmony_ci for_each_rtd_codec_dais(rtd, i, codec_dai) { 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci /* 5438c2ecf20Sopenharmony_ci * Skip CODECs which don't support the current stream type. 5448c2ecf20Sopenharmony_ci * Otherwise, since the rate, channel, and format values will 5458c2ecf20Sopenharmony_ci * zero in that case, we would have no usable settings left, 5468c2ecf20Sopenharmony_ci * causing the resulting setup to fail. 5478c2ecf20Sopenharmony_ci */ 5488c2ecf20Sopenharmony_ci if (!snd_soc_dai_stream_valid(codec_dai, stream)) 5498c2ecf20Sopenharmony_ci continue; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci codec_stream = snd_soc_dai_get_pcm_stream(codec_dai, stream); 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci chan_min = max(chan_min, codec_stream->channels_min); 5548c2ecf20Sopenharmony_ci chan_max = min(chan_max, codec_stream->channels_max); 5558c2ecf20Sopenharmony_ci rate_min = max(rate_min, codec_stream->rate_min); 5568c2ecf20Sopenharmony_ci rate_max = min_not_zero(rate_max, codec_stream->rate_max); 5578c2ecf20Sopenharmony_ci formats &= codec_stream->formats; 5588c2ecf20Sopenharmony_ci rates = snd_pcm_rate_mask_intersect(codec_stream->rates, rates); 5598c2ecf20Sopenharmony_ci } 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci /* Verify both a valid CPU DAI and a valid CODEC DAI were found */ 5628c2ecf20Sopenharmony_ci if (!chan_min || !cpu_chan_min) 5638c2ecf20Sopenharmony_ci return -EINVAL; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci /* 5668c2ecf20Sopenharmony_ci * chan min/max cannot be enforced if there are multiple CODEC DAIs 5678c2ecf20Sopenharmony_ci * connected to CPU DAI(s), use CPU DAI's directly and let 5688c2ecf20Sopenharmony_ci * channel allocation be fixed up later 5698c2ecf20Sopenharmony_ci */ 5708c2ecf20Sopenharmony_ci if (rtd->num_codecs > 1) { 5718c2ecf20Sopenharmony_ci chan_min = cpu_chan_min; 5728c2ecf20Sopenharmony_ci chan_max = cpu_chan_max; 5738c2ecf20Sopenharmony_ci } 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci /* finally find a intersection between CODECs and CPUs */ 5768c2ecf20Sopenharmony_ci hw->channels_min = max(chan_min, cpu_chan_min); 5778c2ecf20Sopenharmony_ci hw->channels_max = min(chan_max, cpu_chan_max); 5788c2ecf20Sopenharmony_ci hw->formats = formats; 5798c2ecf20Sopenharmony_ci hw->rates = snd_pcm_rate_mask_intersect(rates, cpu_rates); 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci snd_pcm_hw_limit_rates(hw); 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci hw->rate_min = max(hw->rate_min, cpu_rate_min); 5848c2ecf20Sopenharmony_ci hw->rate_min = max(hw->rate_min, rate_min); 5858c2ecf20Sopenharmony_ci hw->rate_max = min_not_zero(hw->rate_max, cpu_rate_max); 5868c2ecf20Sopenharmony_ci hw->rate_max = min_not_zero(hw->rate_max, rate_max); 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci return 0; 5898c2ecf20Sopenharmony_ci} 5908c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_runtime_calc_hw); 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_cistatic void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream) 5938c2ecf20Sopenharmony_ci{ 5948c2ecf20Sopenharmony_ci struct snd_pcm_hardware *hw = &substream->runtime->hw; 5958c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 5968c2ecf20Sopenharmony_ci u64 formats = hw->formats; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci /* 5998c2ecf20Sopenharmony_ci * At least one CPU and one CODEC should match. Otherwise, we should 6008c2ecf20Sopenharmony_ci * have bailed out on a higher level, since there would be no CPU or 6018c2ecf20Sopenharmony_ci * CODEC to support the transfer direction in that case. 6028c2ecf20Sopenharmony_ci */ 6038c2ecf20Sopenharmony_ci snd_soc_runtime_calc_hw(rtd, hw, substream->stream); 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci if (formats) 6068c2ecf20Sopenharmony_ci hw->formats &= formats; 6078c2ecf20Sopenharmony_ci} 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_cistatic int soc_pcm_components_open(struct snd_pcm_substream *substream) 6108c2ecf20Sopenharmony_ci{ 6118c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 6128c2ecf20Sopenharmony_ci struct snd_soc_component *component; 6138c2ecf20Sopenharmony_ci int i, ret = 0; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci for_each_rtd_components(rtd, i, component) { 6168c2ecf20Sopenharmony_ci ret = snd_soc_component_module_get_when_open(component, substream); 6178c2ecf20Sopenharmony_ci if (ret < 0) 6188c2ecf20Sopenharmony_ci break; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci ret = snd_soc_component_open(component, substream); 6218c2ecf20Sopenharmony_ci if (ret < 0) 6228c2ecf20Sopenharmony_ci break; 6238c2ecf20Sopenharmony_ci } 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci return ret; 6268c2ecf20Sopenharmony_ci} 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_cistatic int soc_pcm_components_close(struct snd_pcm_substream *substream, 6298c2ecf20Sopenharmony_ci int rollback) 6308c2ecf20Sopenharmony_ci{ 6318c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 6328c2ecf20Sopenharmony_ci struct snd_soc_component *component; 6338c2ecf20Sopenharmony_ci int i, r, ret = 0; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci for_each_rtd_components(rtd, i, component) { 6368c2ecf20Sopenharmony_ci r = snd_soc_component_close(component, substream, rollback); 6378c2ecf20Sopenharmony_ci if (r < 0) 6388c2ecf20Sopenharmony_ci ret = r; /* use last ret */ 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci snd_soc_component_module_put_when_close(component, substream, rollback); 6418c2ecf20Sopenharmony_ci } 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci return ret; 6448c2ecf20Sopenharmony_ci} 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_cistatic int soc_pcm_clean(struct snd_pcm_substream *substream, int rollback) 6478c2ecf20Sopenharmony_ci{ 6488c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 6498c2ecf20Sopenharmony_ci struct snd_soc_component *component; 6508c2ecf20Sopenharmony_ci struct snd_soc_dai *dai; 6518c2ecf20Sopenharmony_ci int i; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci if (!rollback) 6568c2ecf20Sopenharmony_ci snd_soc_runtime_deactivate(rtd, substream->stream); 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci for_each_rtd_dais(rtd, i, dai) 6598c2ecf20Sopenharmony_ci snd_soc_dai_shutdown(dai, substream, rollback); 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci snd_soc_link_shutdown(substream, rollback); 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci soc_pcm_components_close(substream, rollback); 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci if (!rollback) 6668c2ecf20Sopenharmony_ci snd_soc_dapm_stream_stop(rtd, substream->stream); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci mutex_unlock(&rtd->card->pcm_mutex); 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci snd_soc_pcm_component_pm_runtime_put(rtd, substream, rollback); 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci for_each_rtd_components(rtd, i, component) 6738c2ecf20Sopenharmony_ci if (!snd_soc_component_active(component)) 6748c2ecf20Sopenharmony_ci pinctrl_pm_select_sleep_state(component->dev); 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci return 0; 6778c2ecf20Sopenharmony_ci} 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci/* 6808c2ecf20Sopenharmony_ci * Called by ALSA when a PCM substream is closed. Private data can be 6818c2ecf20Sopenharmony_ci * freed here. The cpu DAI, codec DAI, machine and components are also 6828c2ecf20Sopenharmony_ci * shutdown. 6838c2ecf20Sopenharmony_ci */ 6848c2ecf20Sopenharmony_cistatic int soc_pcm_close(struct snd_pcm_substream *substream) 6858c2ecf20Sopenharmony_ci{ 6868c2ecf20Sopenharmony_ci return soc_pcm_clean(substream, 0); 6878c2ecf20Sopenharmony_ci} 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci/* 6908c2ecf20Sopenharmony_ci * Called by ALSA when a PCM substream is opened, the runtime->hw record is 6918c2ecf20Sopenharmony_ci * then initialized and any private data can be allocated. This also calls 6928c2ecf20Sopenharmony_ci * startup for the cpu DAI, component, machine and codec DAI. 6938c2ecf20Sopenharmony_ci */ 6948c2ecf20Sopenharmony_cistatic int soc_pcm_open(struct snd_pcm_substream *substream) 6958c2ecf20Sopenharmony_ci{ 6968c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 6978c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 6988c2ecf20Sopenharmony_ci struct snd_soc_component *component; 6998c2ecf20Sopenharmony_ci struct snd_soc_dai *dai; 7008c2ecf20Sopenharmony_ci const char *codec_dai_name = "multicodec"; 7018c2ecf20Sopenharmony_ci const char *cpu_dai_name = "multicpu"; 7028c2ecf20Sopenharmony_ci int i, ret = 0; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci for_each_rtd_components(rtd, i, component) 7058c2ecf20Sopenharmony_ci pinctrl_pm_select_default_state(component->dev); 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci ret = snd_soc_pcm_component_pm_runtime_get(rtd, substream); 7088c2ecf20Sopenharmony_ci if (ret < 0) 7098c2ecf20Sopenharmony_ci goto pm_err; 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci ret = soc_pcm_components_open(substream); 7148c2ecf20Sopenharmony_ci if (ret < 0) 7158c2ecf20Sopenharmony_ci goto err; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci ret = snd_soc_link_startup(substream); 7188c2ecf20Sopenharmony_ci if (ret < 0) 7198c2ecf20Sopenharmony_ci goto err; 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci /* startup the audio subsystem */ 7228c2ecf20Sopenharmony_ci for_each_rtd_dais(rtd, i, dai) { 7238c2ecf20Sopenharmony_ci ret = snd_soc_dai_startup(dai, substream); 7248c2ecf20Sopenharmony_ci if (ret < 0) 7258c2ecf20Sopenharmony_ci goto err; 7268c2ecf20Sopenharmony_ci } 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci /* Dynamic PCM DAI links compat checks use dynamic capabilities */ 7298c2ecf20Sopenharmony_ci if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm) 7308c2ecf20Sopenharmony_ci goto dynamic; 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci /* Check that the codec and cpu DAIs are compatible */ 7338c2ecf20Sopenharmony_ci soc_pcm_init_runtime_hw(substream); 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci if (rtd->num_codecs == 1) 7368c2ecf20Sopenharmony_ci codec_dai_name = asoc_rtd_to_codec(rtd, 0)->name; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci if (rtd->num_cpus == 1) 7398c2ecf20Sopenharmony_ci cpu_dai_name = asoc_rtd_to_cpu(rtd, 0)->name; 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci if (soc_pcm_has_symmetry(substream)) 7428c2ecf20Sopenharmony_ci runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci ret = -EINVAL; 7458c2ecf20Sopenharmony_ci if (!runtime->hw.rates) { 7468c2ecf20Sopenharmony_ci printk(KERN_ERR "ASoC: %s <-> %s No matching rates\n", 7478c2ecf20Sopenharmony_ci codec_dai_name, cpu_dai_name); 7488c2ecf20Sopenharmony_ci goto err; 7498c2ecf20Sopenharmony_ci } 7508c2ecf20Sopenharmony_ci if (!runtime->hw.formats) { 7518c2ecf20Sopenharmony_ci printk(KERN_ERR "ASoC: %s <-> %s No matching formats\n", 7528c2ecf20Sopenharmony_ci codec_dai_name, cpu_dai_name); 7538c2ecf20Sopenharmony_ci goto err; 7548c2ecf20Sopenharmony_ci } 7558c2ecf20Sopenharmony_ci if (!runtime->hw.channels_min || !runtime->hw.channels_max || 7568c2ecf20Sopenharmony_ci runtime->hw.channels_min > runtime->hw.channels_max) { 7578c2ecf20Sopenharmony_ci printk(KERN_ERR "ASoC: %s <-> %s No matching channels\n", 7588c2ecf20Sopenharmony_ci codec_dai_name, cpu_dai_name); 7598c2ecf20Sopenharmony_ci goto err; 7608c2ecf20Sopenharmony_ci } 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci soc_pcm_apply_msb(substream); 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci /* Symmetry only applies if we've already got an active stream. */ 7658c2ecf20Sopenharmony_ci for_each_rtd_dais(rtd, i, dai) { 7668c2ecf20Sopenharmony_ci if (snd_soc_dai_active(dai)) { 7678c2ecf20Sopenharmony_ci ret = soc_pcm_apply_symmetry(substream, dai); 7688c2ecf20Sopenharmony_ci if (ret != 0) 7698c2ecf20Sopenharmony_ci goto err; 7708c2ecf20Sopenharmony_ci } 7718c2ecf20Sopenharmony_ci } 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci pr_debug("ASoC: %s <-> %s info:\n", 7748c2ecf20Sopenharmony_ci codec_dai_name, cpu_dai_name); 7758c2ecf20Sopenharmony_ci pr_debug("ASoC: rate mask 0x%x\n", runtime->hw.rates); 7768c2ecf20Sopenharmony_ci pr_debug("ASoC: min ch %d max ch %d\n", runtime->hw.channels_min, 7778c2ecf20Sopenharmony_ci runtime->hw.channels_max); 7788c2ecf20Sopenharmony_ci pr_debug("ASoC: min rate %d max rate %d\n", runtime->hw.rate_min, 7798c2ecf20Sopenharmony_ci runtime->hw.rate_max); 7808c2ecf20Sopenharmony_cidynamic: 7818c2ecf20Sopenharmony_ci snd_soc_runtime_activate(rtd, substream->stream); 7828c2ecf20Sopenharmony_ci ret = 0; 7838c2ecf20Sopenharmony_cierr: 7848c2ecf20Sopenharmony_ci mutex_unlock(&rtd->card->pcm_mutex); 7858c2ecf20Sopenharmony_cipm_err: 7868c2ecf20Sopenharmony_ci if (ret < 0) 7878c2ecf20Sopenharmony_ci soc_pcm_clean(substream, 1); 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci return ret; 7908c2ecf20Sopenharmony_ci} 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_cistatic void codec2codec_close_delayed_work(struct snd_soc_pcm_runtime *rtd) 7938c2ecf20Sopenharmony_ci{ 7948c2ecf20Sopenharmony_ci /* 7958c2ecf20Sopenharmony_ci * Currently nothing to do for c2c links 7968c2ecf20Sopenharmony_ci * Since c2c links are internal nodes in the DAPM graph and 7978c2ecf20Sopenharmony_ci * don't interface with the outside world or application layer 7988c2ecf20Sopenharmony_ci * we don't have to do any special handling on close. 7998c2ecf20Sopenharmony_ci */ 8008c2ecf20Sopenharmony_ci} 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci/* 8038c2ecf20Sopenharmony_ci * Called by ALSA when the PCM substream is prepared, can set format, sample 8048c2ecf20Sopenharmony_ci * rate, etc. This function is non atomic and can be called multiple times, 8058c2ecf20Sopenharmony_ci * it can refer to the runtime info. 8068c2ecf20Sopenharmony_ci */ 8078c2ecf20Sopenharmony_cistatic int soc_pcm_prepare(struct snd_pcm_substream *substream) 8088c2ecf20Sopenharmony_ci{ 8098c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 8108c2ecf20Sopenharmony_ci struct snd_soc_dai *dai; 8118c2ecf20Sopenharmony_ci int i, ret = 0; 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci ret = snd_soc_link_prepare(substream); 8168c2ecf20Sopenharmony_ci if (ret < 0) 8178c2ecf20Sopenharmony_ci goto out; 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci ret = snd_soc_pcm_component_prepare(substream); 8208c2ecf20Sopenharmony_ci if (ret < 0) 8218c2ecf20Sopenharmony_ci goto out; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci ret = snd_soc_pcm_dai_prepare(substream); 8248c2ecf20Sopenharmony_ci if (ret < 0) { 8258c2ecf20Sopenharmony_ci dev_err(rtd->dev, "ASoC: DAI prepare error: %d\n", ret); 8268c2ecf20Sopenharmony_ci goto out; 8278c2ecf20Sopenharmony_ci } 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci /* cancel any delayed stream shutdown that is pending */ 8308c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && 8318c2ecf20Sopenharmony_ci rtd->pop_wait) { 8328c2ecf20Sopenharmony_ci rtd->pop_wait = 0; 8338c2ecf20Sopenharmony_ci cancel_delayed_work(&rtd->delayed_work); 8348c2ecf20Sopenharmony_ci } 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci snd_soc_dapm_stream_event(rtd, substream->stream, 8378c2ecf20Sopenharmony_ci SND_SOC_DAPM_STREAM_START); 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci for_each_rtd_dais(rtd, i, dai) 8408c2ecf20Sopenharmony_ci snd_soc_dai_digital_mute(dai, 0, substream->stream); 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ciout: 8438c2ecf20Sopenharmony_ci mutex_unlock(&rtd->card->pcm_mutex); 8448c2ecf20Sopenharmony_ci return ret; 8458c2ecf20Sopenharmony_ci} 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_cistatic void soc_pcm_codec_params_fixup(struct snd_pcm_hw_params *params, 8488c2ecf20Sopenharmony_ci unsigned int mask) 8498c2ecf20Sopenharmony_ci{ 8508c2ecf20Sopenharmony_ci struct snd_interval *interval; 8518c2ecf20Sopenharmony_ci int channels = hweight_long(mask); 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci interval = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); 8548c2ecf20Sopenharmony_ci interval->min = channels; 8558c2ecf20Sopenharmony_ci interval->max = channels; 8568c2ecf20Sopenharmony_ci} 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci/* 8598c2ecf20Sopenharmony_ci * Called by ALSA when the hardware params are set by application. This 8608c2ecf20Sopenharmony_ci * function can also be called multiple times and can allocate buffers 8618c2ecf20Sopenharmony_ci * (using snd_pcm_lib_* ). It's non-atomic. 8628c2ecf20Sopenharmony_ci */ 8638c2ecf20Sopenharmony_cistatic int soc_pcm_hw_params(struct snd_pcm_substream *substream, 8648c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params) 8658c2ecf20Sopenharmony_ci{ 8668c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 8678c2ecf20Sopenharmony_ci struct snd_soc_component *component; 8688c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai; 8698c2ecf20Sopenharmony_ci struct snd_soc_dai *codec_dai; 8708c2ecf20Sopenharmony_ci int i, ret = 0; 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci ret = soc_pcm_params_symmetry(substream, params); 8758c2ecf20Sopenharmony_ci if (ret) 8768c2ecf20Sopenharmony_ci goto out; 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci ret = snd_soc_link_hw_params(substream, params); 8798c2ecf20Sopenharmony_ci if (ret < 0) 8808c2ecf20Sopenharmony_ci goto out; 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci for_each_rtd_codec_dais(rtd, i, codec_dai) { 8838c2ecf20Sopenharmony_ci struct snd_pcm_hw_params codec_params; 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci /* 8868c2ecf20Sopenharmony_ci * Skip CODECs which don't support the current stream type, 8878c2ecf20Sopenharmony_ci * the idea being that if a CODEC is not used for the currently 8888c2ecf20Sopenharmony_ci * set up transfer direction, it should not need to be 8898c2ecf20Sopenharmony_ci * configured, especially since the configuration used might 8908c2ecf20Sopenharmony_ci * not even be supported by that CODEC. There may be cases 8918c2ecf20Sopenharmony_ci * however where a CODEC needs to be set up although it is 8928c2ecf20Sopenharmony_ci * actually not being used for the transfer, e.g. if a 8938c2ecf20Sopenharmony_ci * capture-only CODEC is acting as an LRCLK and/or BCLK master 8948c2ecf20Sopenharmony_ci * for the DAI link including a playback-only CODEC. 8958c2ecf20Sopenharmony_ci * If this becomes necessary, we will have to augment the 8968c2ecf20Sopenharmony_ci * machine driver setup with information on how to act, so 8978c2ecf20Sopenharmony_ci * we can do the right thing here. 8988c2ecf20Sopenharmony_ci */ 8998c2ecf20Sopenharmony_ci if (!snd_soc_dai_stream_valid(codec_dai, substream->stream)) 9008c2ecf20Sopenharmony_ci continue; 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci /* copy params for each codec */ 9038c2ecf20Sopenharmony_ci codec_params = *params; 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci /* fixup params based on TDM slot masks */ 9068c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && 9078c2ecf20Sopenharmony_ci codec_dai->tx_mask) 9088c2ecf20Sopenharmony_ci soc_pcm_codec_params_fixup(&codec_params, 9098c2ecf20Sopenharmony_ci codec_dai->tx_mask); 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_CAPTURE && 9128c2ecf20Sopenharmony_ci codec_dai->rx_mask) 9138c2ecf20Sopenharmony_ci soc_pcm_codec_params_fixup(&codec_params, 9148c2ecf20Sopenharmony_ci codec_dai->rx_mask); 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci ret = snd_soc_dai_hw_params(codec_dai, substream, 9178c2ecf20Sopenharmony_ci &codec_params); 9188c2ecf20Sopenharmony_ci if(ret < 0) 9198c2ecf20Sopenharmony_ci goto codec_err; 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci codec_dai->rate = params_rate(&codec_params); 9228c2ecf20Sopenharmony_ci codec_dai->channels = params_channels(&codec_params); 9238c2ecf20Sopenharmony_ci codec_dai->sample_bits = snd_pcm_format_physical_width( 9248c2ecf20Sopenharmony_ci params_format(&codec_params)); 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci snd_soc_dapm_update_dai(substream, &codec_params, codec_dai); 9278c2ecf20Sopenharmony_ci } 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci for_each_rtd_cpu_dais(rtd, i, cpu_dai) { 9308c2ecf20Sopenharmony_ci /* 9318c2ecf20Sopenharmony_ci * Skip CPUs which don't support the current stream 9328c2ecf20Sopenharmony_ci * type. See soc_pcm_init_runtime_hw() for more details 9338c2ecf20Sopenharmony_ci */ 9348c2ecf20Sopenharmony_ci if (!snd_soc_dai_stream_valid(cpu_dai, substream->stream)) 9358c2ecf20Sopenharmony_ci continue; 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci ret = snd_soc_dai_hw_params(cpu_dai, substream, params); 9388c2ecf20Sopenharmony_ci if (ret < 0) 9398c2ecf20Sopenharmony_ci goto interface_err; 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci /* store the parameters for each DAI */ 9428c2ecf20Sopenharmony_ci cpu_dai->rate = params_rate(params); 9438c2ecf20Sopenharmony_ci cpu_dai->channels = params_channels(params); 9448c2ecf20Sopenharmony_ci cpu_dai->sample_bits = 9458c2ecf20Sopenharmony_ci snd_pcm_format_physical_width(params_format(params)); 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci snd_soc_dapm_update_dai(substream, params, cpu_dai); 9488c2ecf20Sopenharmony_ci } 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci ret = snd_soc_pcm_component_hw_params(substream, params, &component); 9518c2ecf20Sopenharmony_ci if (ret < 0) 9528c2ecf20Sopenharmony_ci goto component_err; 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ciout: 9558c2ecf20Sopenharmony_ci mutex_unlock(&rtd->card->pcm_mutex); 9568c2ecf20Sopenharmony_ci return ret; 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_cicomponent_err: 9598c2ecf20Sopenharmony_ci snd_soc_pcm_component_hw_free(substream, component); 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci i = rtd->num_cpus; 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ciinterface_err: 9648c2ecf20Sopenharmony_ci for_each_rtd_cpu_dais_rollback(rtd, i, cpu_dai) { 9658c2ecf20Sopenharmony_ci if (!snd_soc_dai_stream_valid(cpu_dai, substream->stream)) 9668c2ecf20Sopenharmony_ci continue; 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci snd_soc_dai_hw_free(cpu_dai, substream); 9698c2ecf20Sopenharmony_ci cpu_dai->rate = 0; 9708c2ecf20Sopenharmony_ci } 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci i = rtd->num_codecs; 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_cicodec_err: 9758c2ecf20Sopenharmony_ci for_each_rtd_codec_dais_rollback(rtd, i, codec_dai) { 9768c2ecf20Sopenharmony_ci if (!snd_soc_dai_stream_valid(codec_dai, substream->stream)) 9778c2ecf20Sopenharmony_ci continue; 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci snd_soc_dai_hw_free(codec_dai, substream); 9808c2ecf20Sopenharmony_ci codec_dai->rate = 0; 9818c2ecf20Sopenharmony_ci } 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci snd_soc_link_hw_free(substream); 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci mutex_unlock(&rtd->card->pcm_mutex); 9868c2ecf20Sopenharmony_ci return ret; 9878c2ecf20Sopenharmony_ci} 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci/* 9908c2ecf20Sopenharmony_ci * Frees resources allocated by hw_params, can be called multiple times 9918c2ecf20Sopenharmony_ci */ 9928c2ecf20Sopenharmony_cistatic int soc_pcm_hw_free(struct snd_pcm_substream *substream) 9938c2ecf20Sopenharmony_ci{ 9948c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 9958c2ecf20Sopenharmony_ci struct snd_soc_dai *dai; 9968c2ecf20Sopenharmony_ci int i; 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci /* clear the corresponding DAIs parameters when going to be inactive */ 10018c2ecf20Sopenharmony_ci for_each_rtd_dais(rtd, i, dai) { 10028c2ecf20Sopenharmony_ci int active = snd_soc_dai_stream_active(dai, substream->stream); 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci if (snd_soc_dai_active(dai) == 1) { 10058c2ecf20Sopenharmony_ci dai->rate = 0; 10068c2ecf20Sopenharmony_ci dai->channels = 0; 10078c2ecf20Sopenharmony_ci dai->sample_bits = 0; 10088c2ecf20Sopenharmony_ci } 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci if (active == 1) 10118c2ecf20Sopenharmony_ci snd_soc_dai_digital_mute(dai, 1, substream->stream); 10128c2ecf20Sopenharmony_ci } 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci /* free any machine hw params */ 10158c2ecf20Sopenharmony_ci snd_soc_link_hw_free(substream); 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci /* free any component resources */ 10188c2ecf20Sopenharmony_ci snd_soc_pcm_component_hw_free(substream, NULL); 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci /* now free hw params for the DAIs */ 10218c2ecf20Sopenharmony_ci for_each_rtd_dais(rtd, i, dai) { 10228c2ecf20Sopenharmony_ci if (!snd_soc_dai_stream_valid(dai, substream->stream)) 10238c2ecf20Sopenharmony_ci continue; 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci snd_soc_dai_hw_free(dai, substream); 10268c2ecf20Sopenharmony_ci } 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci mutex_unlock(&rtd->card->pcm_mutex); 10298c2ecf20Sopenharmony_ci return 0; 10308c2ecf20Sopenharmony_ci} 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_cistatic int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) 10338c2ecf20Sopenharmony_ci{ 10348c2ecf20Sopenharmony_ci int ret = -EINVAL; 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci switch (cmd) { 10378c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 10388c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 10398c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 10408c2ecf20Sopenharmony_ci ret = snd_soc_link_trigger(substream, cmd); 10418c2ecf20Sopenharmony_ci if (ret < 0) 10428c2ecf20Sopenharmony_ci break; 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci ret = snd_soc_pcm_component_trigger(substream, cmd); 10458c2ecf20Sopenharmony_ci if (ret < 0) 10468c2ecf20Sopenharmony_ci break; 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci ret = snd_soc_pcm_dai_trigger(substream, cmd); 10498c2ecf20Sopenharmony_ci break; 10508c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 10518c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 10528c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 10538c2ecf20Sopenharmony_ci ret = snd_soc_pcm_dai_trigger(substream, cmd); 10548c2ecf20Sopenharmony_ci if (ret < 0) 10558c2ecf20Sopenharmony_ci break; 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci ret = snd_soc_pcm_component_trigger(substream, cmd); 10588c2ecf20Sopenharmony_ci if (ret < 0) 10598c2ecf20Sopenharmony_ci break; 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci ret = snd_soc_link_trigger(substream, cmd); 10628c2ecf20Sopenharmony_ci break; 10638c2ecf20Sopenharmony_ci } 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci return ret; 10668c2ecf20Sopenharmony_ci} 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci/* 10698c2ecf20Sopenharmony_ci * soc level wrapper for pointer callback 10708c2ecf20Sopenharmony_ci * If cpu_dai, codec_dai, component driver has the delay callback, then 10718c2ecf20Sopenharmony_ci * the runtime->delay will be updated accordingly. 10728c2ecf20Sopenharmony_ci */ 10738c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) 10748c2ecf20Sopenharmony_ci{ 10758c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 10768c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai; 10778c2ecf20Sopenharmony_ci struct snd_soc_dai *codec_dai; 10788c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 10798c2ecf20Sopenharmony_ci snd_pcm_uframes_t offset = 0; 10808c2ecf20Sopenharmony_ci snd_pcm_sframes_t delay = 0; 10818c2ecf20Sopenharmony_ci snd_pcm_sframes_t codec_delay = 0; 10828c2ecf20Sopenharmony_ci snd_pcm_sframes_t cpu_delay = 0; 10838c2ecf20Sopenharmony_ci int i; 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci /* clearing the previous total delay */ 10868c2ecf20Sopenharmony_ci runtime->delay = 0; 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci offset = snd_soc_pcm_component_pointer(substream); 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci /* base delay if assigned in pointer callback */ 10918c2ecf20Sopenharmony_ci delay = runtime->delay; 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci for_each_rtd_cpu_dais(rtd, i, cpu_dai) { 10948c2ecf20Sopenharmony_ci cpu_delay = max(cpu_delay, 10958c2ecf20Sopenharmony_ci snd_soc_dai_delay(cpu_dai, substream)); 10968c2ecf20Sopenharmony_ci } 10978c2ecf20Sopenharmony_ci delay += cpu_delay; 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci for_each_rtd_codec_dais(rtd, i, codec_dai) { 11008c2ecf20Sopenharmony_ci codec_delay = max(codec_delay, 11018c2ecf20Sopenharmony_ci snd_soc_dai_delay(codec_dai, substream)); 11028c2ecf20Sopenharmony_ci } 11038c2ecf20Sopenharmony_ci delay += codec_delay; 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci runtime->delay = delay; 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci return offset; 11088c2ecf20Sopenharmony_ci} 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci/* connect a FE and BE */ 11118c2ecf20Sopenharmony_cistatic int dpcm_be_connect(struct snd_soc_pcm_runtime *fe, 11128c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *be, int stream) 11138c2ecf20Sopenharmony_ci{ 11148c2ecf20Sopenharmony_ci struct snd_soc_dpcm *dpcm; 11158c2ecf20Sopenharmony_ci unsigned long flags; 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci /* only add new dpcms */ 11188c2ecf20Sopenharmony_ci for_each_dpcm_be(fe, stream, dpcm) { 11198c2ecf20Sopenharmony_ci if (dpcm->be == be && dpcm->fe == fe) 11208c2ecf20Sopenharmony_ci return 0; 11218c2ecf20Sopenharmony_ci } 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci dpcm = kzalloc(sizeof(struct snd_soc_dpcm), GFP_KERNEL); 11248c2ecf20Sopenharmony_ci if (!dpcm) 11258c2ecf20Sopenharmony_ci return -ENOMEM; 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci dpcm->be = be; 11288c2ecf20Sopenharmony_ci dpcm->fe = fe; 11298c2ecf20Sopenharmony_ci be->dpcm[stream].runtime = fe->dpcm[stream].runtime; 11308c2ecf20Sopenharmony_ci dpcm->state = SND_SOC_DPCM_LINK_STATE_NEW; 11318c2ecf20Sopenharmony_ci spin_lock_irqsave(&fe->card->dpcm_lock, flags); 11328c2ecf20Sopenharmony_ci list_add(&dpcm->list_be, &fe->dpcm[stream].be_clients); 11338c2ecf20Sopenharmony_ci list_add(&dpcm->list_fe, &be->dpcm[stream].fe_clients); 11348c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fe->card->dpcm_lock, flags); 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci dev_dbg(fe->dev, "connected new DPCM %s path %s %s %s\n", 11378c2ecf20Sopenharmony_ci stream ? "capture" : "playback", fe->dai_link->name, 11388c2ecf20Sopenharmony_ci stream ? "<-" : "->", be->dai_link->name); 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci dpcm_create_debugfs_state(dpcm, stream); 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci return 1; 11438c2ecf20Sopenharmony_ci} 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci/* reparent a BE onto another FE */ 11468c2ecf20Sopenharmony_cistatic void dpcm_be_reparent(struct snd_soc_pcm_runtime *fe, 11478c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *be, int stream) 11488c2ecf20Sopenharmony_ci{ 11498c2ecf20Sopenharmony_ci struct snd_soc_dpcm *dpcm; 11508c2ecf20Sopenharmony_ci struct snd_pcm_substream *fe_substream, *be_substream; 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci /* reparent if BE is connected to other FEs */ 11538c2ecf20Sopenharmony_ci if (!be->dpcm[stream].users) 11548c2ecf20Sopenharmony_ci return; 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci be_substream = snd_soc_dpcm_get_substream(be, stream); 11578c2ecf20Sopenharmony_ci if (!be_substream) 11588c2ecf20Sopenharmony_ci return; 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci for_each_dpcm_fe(be, stream, dpcm) { 11618c2ecf20Sopenharmony_ci if (dpcm->fe == fe) 11628c2ecf20Sopenharmony_ci continue; 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci dev_dbg(fe->dev, "reparent %s path %s %s %s\n", 11658c2ecf20Sopenharmony_ci stream ? "capture" : "playback", 11668c2ecf20Sopenharmony_ci dpcm->fe->dai_link->name, 11678c2ecf20Sopenharmony_ci stream ? "<-" : "->", dpcm->be->dai_link->name); 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci fe_substream = snd_soc_dpcm_get_substream(dpcm->fe, stream); 11708c2ecf20Sopenharmony_ci be_substream->runtime = fe_substream->runtime; 11718c2ecf20Sopenharmony_ci break; 11728c2ecf20Sopenharmony_ci } 11738c2ecf20Sopenharmony_ci} 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci/* disconnect a BE and FE */ 11768c2ecf20Sopenharmony_civoid dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream) 11778c2ecf20Sopenharmony_ci{ 11788c2ecf20Sopenharmony_ci struct snd_soc_dpcm *dpcm, *d; 11798c2ecf20Sopenharmony_ci unsigned long flags; 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci for_each_dpcm_be_safe(fe, stream, dpcm, d) { 11828c2ecf20Sopenharmony_ci dev_dbg(fe->dev, "ASoC: BE %s disconnect check for %s\n", 11838c2ecf20Sopenharmony_ci stream ? "capture" : "playback", 11848c2ecf20Sopenharmony_ci dpcm->be->dai_link->name); 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci if (dpcm->state != SND_SOC_DPCM_LINK_STATE_FREE) 11878c2ecf20Sopenharmony_ci continue; 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci dev_dbg(fe->dev, "freed DSP %s path %s %s %s\n", 11908c2ecf20Sopenharmony_ci stream ? "capture" : "playback", fe->dai_link->name, 11918c2ecf20Sopenharmony_ci stream ? "<-" : "->", dpcm->be->dai_link->name); 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci /* BEs still alive need new FE */ 11948c2ecf20Sopenharmony_ci dpcm_be_reparent(fe, dpcm->be, stream); 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci dpcm_remove_debugfs_state(dpcm); 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci spin_lock_irqsave(&fe->card->dpcm_lock, flags); 11998c2ecf20Sopenharmony_ci list_del(&dpcm->list_be); 12008c2ecf20Sopenharmony_ci list_del(&dpcm->list_fe); 12018c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fe->card->dpcm_lock, flags); 12028c2ecf20Sopenharmony_ci kfree(dpcm); 12038c2ecf20Sopenharmony_ci } 12048c2ecf20Sopenharmony_ci} 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci/* get BE for DAI widget and stream */ 12078c2ecf20Sopenharmony_cistatic struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card, 12088c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *widget, int stream) 12098c2ecf20Sopenharmony_ci{ 12108c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *be; 12118c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *w; 12128c2ecf20Sopenharmony_ci struct snd_soc_dai *dai; 12138c2ecf20Sopenharmony_ci int i; 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci dev_dbg(card->dev, "ASoC: find BE for widget %s\n", widget->name); 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci for_each_card_rtds(card, be) { 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci if (!be->dai_link->no_pcm) 12208c2ecf20Sopenharmony_ci continue; 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci for_each_rtd_dais(be, i, dai) { 12238c2ecf20Sopenharmony_ci w = snd_soc_dai_get_widget(dai, stream); 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci dev_dbg(card->dev, "ASoC: try BE : %s\n", 12268c2ecf20Sopenharmony_ci w ? w->name : "(not set)"); 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci if (w == widget) 12298c2ecf20Sopenharmony_ci return be; 12308c2ecf20Sopenharmony_ci } 12318c2ecf20Sopenharmony_ci } 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci /* Widget provided is not a BE */ 12348c2ecf20Sopenharmony_ci return NULL; 12358c2ecf20Sopenharmony_ci} 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_cistatic int widget_in_list(struct snd_soc_dapm_widget_list *list, 12388c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *widget) 12398c2ecf20Sopenharmony_ci{ 12408c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *w; 12418c2ecf20Sopenharmony_ci int i; 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci for_each_dapm_widgets(list, i, w) 12448c2ecf20Sopenharmony_ci if (widget == w) 12458c2ecf20Sopenharmony_ci return 1; 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci return 0; 12488c2ecf20Sopenharmony_ci} 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_cistatic bool dpcm_end_walk_at_be(struct snd_soc_dapm_widget *widget, 12518c2ecf20Sopenharmony_ci enum snd_soc_dapm_direction dir) 12528c2ecf20Sopenharmony_ci{ 12538c2ecf20Sopenharmony_ci struct snd_soc_card *card = widget->dapm->card; 12548c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd; 12558c2ecf20Sopenharmony_ci int stream; 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci /* adjust dir to stream */ 12588c2ecf20Sopenharmony_ci if (dir == SND_SOC_DAPM_DIR_OUT) 12598c2ecf20Sopenharmony_ci stream = SNDRV_PCM_STREAM_PLAYBACK; 12608c2ecf20Sopenharmony_ci else 12618c2ecf20Sopenharmony_ci stream = SNDRV_PCM_STREAM_CAPTURE; 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_ci rtd = dpcm_get_be(card, widget, stream); 12648c2ecf20Sopenharmony_ci if (rtd) 12658c2ecf20Sopenharmony_ci return true; 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_ci return false; 12688c2ecf20Sopenharmony_ci} 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ciint dpcm_path_get(struct snd_soc_pcm_runtime *fe, 12718c2ecf20Sopenharmony_ci int stream, struct snd_soc_dapm_widget_list **list) 12728c2ecf20Sopenharmony_ci{ 12738c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(fe, 0); 12748c2ecf20Sopenharmony_ci int paths; 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci if (fe->num_cpus > 1) { 12778c2ecf20Sopenharmony_ci dev_err(fe->dev, 12788c2ecf20Sopenharmony_ci "%s doesn't support Multi CPU yet\n", __func__); 12798c2ecf20Sopenharmony_ci return -EINVAL; 12808c2ecf20Sopenharmony_ci } 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci /* get number of valid DAI paths and their widgets */ 12838c2ecf20Sopenharmony_ci paths = snd_soc_dapm_dai_get_connected_widgets(cpu_dai, stream, list, 12848c2ecf20Sopenharmony_ci dpcm_end_walk_at_be); 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci dev_dbg(fe->dev, "ASoC: found %d audio %s paths\n", paths, 12878c2ecf20Sopenharmony_ci stream ? "capture" : "playback"); 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci return paths; 12908c2ecf20Sopenharmony_ci} 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_civoid dpcm_path_put(struct snd_soc_dapm_widget_list **list) 12938c2ecf20Sopenharmony_ci{ 12948c2ecf20Sopenharmony_ci snd_soc_dapm_dai_free_widgets(list); 12958c2ecf20Sopenharmony_ci} 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_cistatic bool dpcm_be_is_active(struct snd_soc_dpcm *dpcm, int stream, 12988c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget_list *list) 12998c2ecf20Sopenharmony_ci{ 13008c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *widget; 13018c2ecf20Sopenharmony_ci struct snd_soc_dai *dai; 13028c2ecf20Sopenharmony_ci unsigned int i; 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci /* is there a valid DAI widget for this BE */ 13058c2ecf20Sopenharmony_ci for_each_rtd_dais(dpcm->be, i, dai) { 13068c2ecf20Sopenharmony_ci widget = snd_soc_dai_get_widget(dai, stream); 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci /* 13098c2ecf20Sopenharmony_ci * The BE is pruned only if none of the dai 13108c2ecf20Sopenharmony_ci * widgets are in the active list. 13118c2ecf20Sopenharmony_ci */ 13128c2ecf20Sopenharmony_ci if (widget && widget_in_list(list, widget)) 13138c2ecf20Sopenharmony_ci return true; 13148c2ecf20Sopenharmony_ci } 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci return false; 13178c2ecf20Sopenharmony_ci} 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_cistatic int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream, 13208c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget_list **list_) 13218c2ecf20Sopenharmony_ci{ 13228c2ecf20Sopenharmony_ci struct snd_soc_dpcm *dpcm; 13238c2ecf20Sopenharmony_ci int prune = 0; 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci /* Destroy any old FE <--> BE connections */ 13268c2ecf20Sopenharmony_ci for_each_dpcm_be(fe, stream, dpcm) { 13278c2ecf20Sopenharmony_ci if (dpcm_be_is_active(dpcm, stream, *list_)) 13288c2ecf20Sopenharmony_ci continue; 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ci dev_dbg(fe->dev, "ASoC: pruning %s BE %s for %s\n", 13318c2ecf20Sopenharmony_ci stream ? "capture" : "playback", 13328c2ecf20Sopenharmony_ci dpcm->be->dai_link->name, fe->dai_link->name); 13338c2ecf20Sopenharmony_ci dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; 13348c2ecf20Sopenharmony_ci dpcm->be->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE; 13358c2ecf20Sopenharmony_ci prune++; 13368c2ecf20Sopenharmony_ci } 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci dev_dbg(fe->dev, "ASoC: found %d old BE paths for pruning\n", prune); 13398c2ecf20Sopenharmony_ci return prune; 13408c2ecf20Sopenharmony_ci} 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_cistatic int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream, 13438c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget_list **list_) 13448c2ecf20Sopenharmony_ci{ 13458c2ecf20Sopenharmony_ci struct snd_soc_card *card = fe->card; 13468c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget_list *list = *list_; 13478c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *be; 13488c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *widget; 13498c2ecf20Sopenharmony_ci int i, new = 0, err; 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_ci /* Create any new FE <--> BE connections */ 13528c2ecf20Sopenharmony_ci for_each_dapm_widgets(list, i, widget) { 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci switch (widget->id) { 13558c2ecf20Sopenharmony_ci case snd_soc_dapm_dai_in: 13568c2ecf20Sopenharmony_ci if (stream != SNDRV_PCM_STREAM_PLAYBACK) 13578c2ecf20Sopenharmony_ci continue; 13588c2ecf20Sopenharmony_ci break; 13598c2ecf20Sopenharmony_ci case snd_soc_dapm_dai_out: 13608c2ecf20Sopenharmony_ci if (stream != SNDRV_PCM_STREAM_CAPTURE) 13618c2ecf20Sopenharmony_ci continue; 13628c2ecf20Sopenharmony_ci break; 13638c2ecf20Sopenharmony_ci default: 13648c2ecf20Sopenharmony_ci continue; 13658c2ecf20Sopenharmony_ci } 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ci /* is there a valid BE rtd for this widget */ 13688c2ecf20Sopenharmony_ci be = dpcm_get_be(card, widget, stream); 13698c2ecf20Sopenharmony_ci if (!be) { 13708c2ecf20Sopenharmony_ci dev_err(fe->dev, "ASoC: no BE found for %s\n", 13718c2ecf20Sopenharmony_ci widget->name); 13728c2ecf20Sopenharmony_ci continue; 13738c2ecf20Sopenharmony_ci } 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci /* don't connect if FE is not running */ 13768c2ecf20Sopenharmony_ci if (!fe->dpcm[stream].runtime && !fe->fe_compr) 13778c2ecf20Sopenharmony_ci continue; 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci /* newly connected FE and BE */ 13808c2ecf20Sopenharmony_ci err = dpcm_be_connect(fe, be, stream); 13818c2ecf20Sopenharmony_ci if (err < 0) { 13828c2ecf20Sopenharmony_ci dev_err(fe->dev, "ASoC: can't connect %s\n", 13838c2ecf20Sopenharmony_ci widget->name); 13848c2ecf20Sopenharmony_ci break; 13858c2ecf20Sopenharmony_ci } else if (err == 0) /* already connected */ 13868c2ecf20Sopenharmony_ci continue; 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci /* new */ 13898c2ecf20Sopenharmony_ci be->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE; 13908c2ecf20Sopenharmony_ci new++; 13918c2ecf20Sopenharmony_ci } 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci dev_dbg(fe->dev, "ASoC: found %d new BE paths\n", new); 13948c2ecf20Sopenharmony_ci return new; 13958c2ecf20Sopenharmony_ci} 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci/* 13988c2ecf20Sopenharmony_ci * Find the corresponding BE DAIs that source or sink audio to this 13998c2ecf20Sopenharmony_ci * FE substream. 14008c2ecf20Sopenharmony_ci */ 14018c2ecf20Sopenharmony_ciint dpcm_process_paths(struct snd_soc_pcm_runtime *fe, 14028c2ecf20Sopenharmony_ci int stream, struct snd_soc_dapm_widget_list **list, int new) 14038c2ecf20Sopenharmony_ci{ 14048c2ecf20Sopenharmony_ci if (new) 14058c2ecf20Sopenharmony_ci return dpcm_add_paths(fe, stream, list); 14068c2ecf20Sopenharmony_ci else 14078c2ecf20Sopenharmony_ci return dpcm_prune_paths(fe, stream, list); 14088c2ecf20Sopenharmony_ci} 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_civoid dpcm_clear_pending_state(struct snd_soc_pcm_runtime *fe, int stream) 14118c2ecf20Sopenharmony_ci{ 14128c2ecf20Sopenharmony_ci struct snd_soc_dpcm *dpcm; 14138c2ecf20Sopenharmony_ci unsigned long flags; 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci spin_lock_irqsave(&fe->card->dpcm_lock, flags); 14168c2ecf20Sopenharmony_ci for_each_dpcm_be(fe, stream, dpcm) 14178c2ecf20Sopenharmony_ci dpcm->be->dpcm[stream].runtime_update = 14188c2ecf20Sopenharmony_ci SND_SOC_DPCM_UPDATE_NO; 14198c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fe->card->dpcm_lock, flags); 14208c2ecf20Sopenharmony_ci} 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_cistatic void dpcm_be_dai_startup_unwind(struct snd_soc_pcm_runtime *fe, 14238c2ecf20Sopenharmony_ci int stream) 14248c2ecf20Sopenharmony_ci{ 14258c2ecf20Sopenharmony_ci struct snd_soc_dpcm *dpcm; 14268c2ecf20Sopenharmony_ci 14278c2ecf20Sopenharmony_ci /* disable any enabled and non active backends */ 14288c2ecf20Sopenharmony_ci for_each_dpcm_be(fe, stream, dpcm) { 14298c2ecf20Sopenharmony_ci 14308c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *be = dpcm->be; 14318c2ecf20Sopenharmony_ci struct snd_pcm_substream *be_substream = 14328c2ecf20Sopenharmony_ci snd_soc_dpcm_get_substream(be, stream); 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci if (be->dpcm[stream].users == 0) 14358c2ecf20Sopenharmony_ci dev_err(be->dev, "ASoC: no users %s at close - state %d\n", 14368c2ecf20Sopenharmony_ci stream ? "capture" : "playback", 14378c2ecf20Sopenharmony_ci be->dpcm[stream].state); 14388c2ecf20Sopenharmony_ci 14398c2ecf20Sopenharmony_ci if (--be->dpcm[stream].users != 0) 14408c2ecf20Sopenharmony_ci continue; 14418c2ecf20Sopenharmony_ci 14428c2ecf20Sopenharmony_ci if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) 14438c2ecf20Sopenharmony_ci continue; 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci soc_pcm_close(be_substream); 14468c2ecf20Sopenharmony_ci be_substream->runtime = NULL; 14478c2ecf20Sopenharmony_ci be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; 14488c2ecf20Sopenharmony_ci } 14498c2ecf20Sopenharmony_ci} 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_ciint dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream) 14528c2ecf20Sopenharmony_ci{ 14538c2ecf20Sopenharmony_ci struct snd_soc_dpcm *dpcm; 14548c2ecf20Sopenharmony_ci int err, count = 0; 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_ci /* only startup BE DAIs that are either sinks or sources to this FE DAI */ 14578c2ecf20Sopenharmony_ci for_each_dpcm_be(fe, stream, dpcm) { 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *be = dpcm->be; 14608c2ecf20Sopenharmony_ci struct snd_pcm_substream *be_substream = 14618c2ecf20Sopenharmony_ci snd_soc_dpcm_get_substream(be, stream); 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci if (!be_substream) { 14648c2ecf20Sopenharmony_ci dev_err(be->dev, "ASoC: no backend %s stream\n", 14658c2ecf20Sopenharmony_ci stream ? "capture" : "playback"); 14668c2ecf20Sopenharmony_ci continue; 14678c2ecf20Sopenharmony_ci } 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci /* is this op for this BE ? */ 14708c2ecf20Sopenharmony_ci if (!snd_soc_dpcm_be_can_update(fe, be, stream)) 14718c2ecf20Sopenharmony_ci continue; 14728c2ecf20Sopenharmony_ci 14738c2ecf20Sopenharmony_ci /* first time the dpcm is open ? */ 14748c2ecf20Sopenharmony_ci if (be->dpcm[stream].users == DPCM_MAX_BE_USERS) 14758c2ecf20Sopenharmony_ci dev_err(be->dev, "ASoC: too many users %s at open %d\n", 14768c2ecf20Sopenharmony_ci stream ? "capture" : "playback", 14778c2ecf20Sopenharmony_ci be->dpcm[stream].state); 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_ci if (be->dpcm[stream].users++ != 0) 14808c2ecf20Sopenharmony_ci continue; 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_ci if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_NEW) && 14838c2ecf20Sopenharmony_ci (be->dpcm[stream].state != SND_SOC_DPCM_STATE_CLOSE)) 14848c2ecf20Sopenharmony_ci continue; 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_ci dev_dbg(be->dev, "ASoC: open %s BE %s\n", 14878c2ecf20Sopenharmony_ci stream ? "capture" : "playback", be->dai_link->name); 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci be_substream->runtime = be->dpcm[stream].runtime; 14908c2ecf20Sopenharmony_ci err = soc_pcm_open(be_substream); 14918c2ecf20Sopenharmony_ci if (err < 0) { 14928c2ecf20Sopenharmony_ci dev_err(be->dev, "ASoC: BE open failed %d\n", err); 14938c2ecf20Sopenharmony_ci be->dpcm[stream].users--; 14948c2ecf20Sopenharmony_ci if (be->dpcm[stream].users < 0) 14958c2ecf20Sopenharmony_ci dev_err(be->dev, "ASoC: no users %s at unwind %d\n", 14968c2ecf20Sopenharmony_ci stream ? "capture" : "playback", 14978c2ecf20Sopenharmony_ci be->dpcm[stream].state); 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_ci be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; 15008c2ecf20Sopenharmony_ci goto unwind; 15018c2ecf20Sopenharmony_ci } 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_ci be->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN; 15048c2ecf20Sopenharmony_ci count++; 15058c2ecf20Sopenharmony_ci } 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_ci return count; 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ciunwind: 15108c2ecf20Sopenharmony_ci /* disable any enabled and non active backends */ 15118c2ecf20Sopenharmony_ci for_each_dpcm_be_rollback(fe, stream, dpcm) { 15128c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *be = dpcm->be; 15138c2ecf20Sopenharmony_ci struct snd_pcm_substream *be_substream = 15148c2ecf20Sopenharmony_ci snd_soc_dpcm_get_substream(be, stream); 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci if (!snd_soc_dpcm_be_can_update(fe, be, stream)) 15178c2ecf20Sopenharmony_ci continue; 15188c2ecf20Sopenharmony_ci 15198c2ecf20Sopenharmony_ci if (be->dpcm[stream].users == 0) 15208c2ecf20Sopenharmony_ci dev_err(be->dev, "ASoC: no users %s at close %d\n", 15218c2ecf20Sopenharmony_ci stream ? "capture" : "playback", 15228c2ecf20Sopenharmony_ci be->dpcm[stream].state); 15238c2ecf20Sopenharmony_ci 15248c2ecf20Sopenharmony_ci if (--be->dpcm[stream].users != 0) 15258c2ecf20Sopenharmony_ci continue; 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_ci if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) 15288c2ecf20Sopenharmony_ci continue; 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_ci soc_pcm_close(be_substream); 15318c2ecf20Sopenharmony_ci be_substream->runtime = NULL; 15328c2ecf20Sopenharmony_ci be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; 15338c2ecf20Sopenharmony_ci } 15348c2ecf20Sopenharmony_ci 15358c2ecf20Sopenharmony_ci return err; 15368c2ecf20Sopenharmony_ci} 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_cistatic void dpcm_init_runtime_hw(struct snd_pcm_runtime *runtime, 15398c2ecf20Sopenharmony_ci struct snd_soc_pcm_stream *stream) 15408c2ecf20Sopenharmony_ci{ 15418c2ecf20Sopenharmony_ci runtime->hw.rate_min = stream->rate_min; 15428c2ecf20Sopenharmony_ci runtime->hw.rate_max = min_not_zero(stream->rate_max, UINT_MAX); 15438c2ecf20Sopenharmony_ci runtime->hw.channels_min = stream->channels_min; 15448c2ecf20Sopenharmony_ci runtime->hw.channels_max = stream->channels_max; 15458c2ecf20Sopenharmony_ci if (runtime->hw.formats) 15468c2ecf20Sopenharmony_ci runtime->hw.formats &= stream->formats; 15478c2ecf20Sopenharmony_ci else 15488c2ecf20Sopenharmony_ci runtime->hw.formats = stream->formats; 15498c2ecf20Sopenharmony_ci runtime->hw.rates = stream->rates; 15508c2ecf20Sopenharmony_ci} 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_cistatic void dpcm_runtime_merge_format(struct snd_pcm_substream *substream, 15538c2ecf20Sopenharmony_ci u64 *formats) 15548c2ecf20Sopenharmony_ci{ 15558c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream); 15568c2ecf20Sopenharmony_ci struct snd_soc_dpcm *dpcm; 15578c2ecf20Sopenharmony_ci struct snd_soc_dai *dai; 15588c2ecf20Sopenharmony_ci int stream = substream->stream; 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_ci if (!fe->dai_link->dpcm_merged_format) 15618c2ecf20Sopenharmony_ci return; 15628c2ecf20Sopenharmony_ci 15638c2ecf20Sopenharmony_ci /* 15648c2ecf20Sopenharmony_ci * It returns merged BE codec format 15658c2ecf20Sopenharmony_ci * if FE want to use it (= dpcm_merged_format) 15668c2ecf20Sopenharmony_ci */ 15678c2ecf20Sopenharmony_ci 15688c2ecf20Sopenharmony_ci for_each_dpcm_be(fe, stream, dpcm) { 15698c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *be = dpcm->be; 15708c2ecf20Sopenharmony_ci struct snd_soc_pcm_stream *codec_stream; 15718c2ecf20Sopenharmony_ci int i; 15728c2ecf20Sopenharmony_ci 15738c2ecf20Sopenharmony_ci for_each_rtd_codec_dais(be, i, dai) { 15748c2ecf20Sopenharmony_ci /* 15758c2ecf20Sopenharmony_ci * Skip CODECs which don't support the current stream 15768c2ecf20Sopenharmony_ci * type. See soc_pcm_init_runtime_hw() for more details 15778c2ecf20Sopenharmony_ci */ 15788c2ecf20Sopenharmony_ci if (!snd_soc_dai_stream_valid(dai, stream)) 15798c2ecf20Sopenharmony_ci continue; 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ci codec_stream = snd_soc_dai_get_pcm_stream(dai, stream); 15828c2ecf20Sopenharmony_ci 15838c2ecf20Sopenharmony_ci *formats &= codec_stream->formats; 15848c2ecf20Sopenharmony_ci } 15858c2ecf20Sopenharmony_ci } 15868c2ecf20Sopenharmony_ci} 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_cistatic void dpcm_runtime_merge_chan(struct snd_pcm_substream *substream, 15898c2ecf20Sopenharmony_ci unsigned int *channels_min, 15908c2ecf20Sopenharmony_ci unsigned int *channels_max) 15918c2ecf20Sopenharmony_ci{ 15928c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream); 15938c2ecf20Sopenharmony_ci struct snd_soc_dpcm *dpcm; 15948c2ecf20Sopenharmony_ci int stream = substream->stream; 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_ci if (!fe->dai_link->dpcm_merged_chan) 15978c2ecf20Sopenharmony_ci return; 15988c2ecf20Sopenharmony_ci 15998c2ecf20Sopenharmony_ci /* 16008c2ecf20Sopenharmony_ci * It returns merged BE codec channel; 16018c2ecf20Sopenharmony_ci * if FE want to use it (= dpcm_merged_chan) 16028c2ecf20Sopenharmony_ci */ 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_ci for_each_dpcm_be(fe, stream, dpcm) { 16058c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *be = dpcm->be; 16068c2ecf20Sopenharmony_ci struct snd_soc_pcm_stream *codec_stream; 16078c2ecf20Sopenharmony_ci struct snd_soc_pcm_stream *cpu_stream; 16088c2ecf20Sopenharmony_ci struct snd_soc_dai *dai; 16098c2ecf20Sopenharmony_ci int i; 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_ci for_each_rtd_cpu_dais(be, i, dai) { 16128c2ecf20Sopenharmony_ci /* 16138c2ecf20Sopenharmony_ci * Skip CPUs which don't support the current stream 16148c2ecf20Sopenharmony_ci * type. See soc_pcm_init_runtime_hw() for more details 16158c2ecf20Sopenharmony_ci */ 16168c2ecf20Sopenharmony_ci if (!snd_soc_dai_stream_valid(dai, stream)) 16178c2ecf20Sopenharmony_ci continue; 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_ci cpu_stream = snd_soc_dai_get_pcm_stream(dai, stream); 16208c2ecf20Sopenharmony_ci 16218c2ecf20Sopenharmony_ci *channels_min = max(*channels_min, 16228c2ecf20Sopenharmony_ci cpu_stream->channels_min); 16238c2ecf20Sopenharmony_ci *channels_max = min(*channels_max, 16248c2ecf20Sopenharmony_ci cpu_stream->channels_max); 16258c2ecf20Sopenharmony_ci } 16268c2ecf20Sopenharmony_ci 16278c2ecf20Sopenharmony_ci /* 16288c2ecf20Sopenharmony_ci * chan min/max cannot be enforced if there are multiple CODEC 16298c2ecf20Sopenharmony_ci * DAIs connected to a single CPU DAI, use CPU DAI's directly 16308c2ecf20Sopenharmony_ci */ 16318c2ecf20Sopenharmony_ci if (be->num_codecs == 1) { 16328c2ecf20Sopenharmony_ci codec_stream = snd_soc_dai_get_pcm_stream(asoc_rtd_to_codec(be, 0), stream); 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci *channels_min = max(*channels_min, 16358c2ecf20Sopenharmony_ci codec_stream->channels_min); 16368c2ecf20Sopenharmony_ci *channels_max = min(*channels_max, 16378c2ecf20Sopenharmony_ci codec_stream->channels_max); 16388c2ecf20Sopenharmony_ci } 16398c2ecf20Sopenharmony_ci } 16408c2ecf20Sopenharmony_ci} 16418c2ecf20Sopenharmony_ci 16428c2ecf20Sopenharmony_cistatic void dpcm_runtime_merge_rate(struct snd_pcm_substream *substream, 16438c2ecf20Sopenharmony_ci unsigned int *rates, 16448c2ecf20Sopenharmony_ci unsigned int *rate_min, 16458c2ecf20Sopenharmony_ci unsigned int *rate_max) 16468c2ecf20Sopenharmony_ci{ 16478c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream); 16488c2ecf20Sopenharmony_ci struct snd_soc_dpcm *dpcm; 16498c2ecf20Sopenharmony_ci int stream = substream->stream; 16508c2ecf20Sopenharmony_ci 16518c2ecf20Sopenharmony_ci if (!fe->dai_link->dpcm_merged_rate) 16528c2ecf20Sopenharmony_ci return; 16538c2ecf20Sopenharmony_ci 16548c2ecf20Sopenharmony_ci /* 16558c2ecf20Sopenharmony_ci * It returns merged BE codec channel; 16568c2ecf20Sopenharmony_ci * if FE want to use it (= dpcm_merged_chan) 16578c2ecf20Sopenharmony_ci */ 16588c2ecf20Sopenharmony_ci 16598c2ecf20Sopenharmony_ci for_each_dpcm_be(fe, stream, dpcm) { 16608c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *be = dpcm->be; 16618c2ecf20Sopenharmony_ci struct snd_soc_pcm_stream *pcm; 16628c2ecf20Sopenharmony_ci struct snd_soc_dai *dai; 16638c2ecf20Sopenharmony_ci int i; 16648c2ecf20Sopenharmony_ci 16658c2ecf20Sopenharmony_ci for_each_rtd_dais(be, i, dai) { 16668c2ecf20Sopenharmony_ci /* 16678c2ecf20Sopenharmony_ci * Skip DAIs which don't support the current stream 16688c2ecf20Sopenharmony_ci * type. See soc_pcm_init_runtime_hw() for more details 16698c2ecf20Sopenharmony_ci */ 16708c2ecf20Sopenharmony_ci if (!snd_soc_dai_stream_valid(dai, stream)) 16718c2ecf20Sopenharmony_ci continue; 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_ci pcm = snd_soc_dai_get_pcm_stream(dai, stream); 16748c2ecf20Sopenharmony_ci 16758c2ecf20Sopenharmony_ci *rate_min = max(*rate_min, pcm->rate_min); 16768c2ecf20Sopenharmony_ci *rate_max = min_not_zero(*rate_max, pcm->rate_max); 16778c2ecf20Sopenharmony_ci *rates = snd_pcm_rate_mask_intersect(*rates, pcm->rates); 16788c2ecf20Sopenharmony_ci } 16798c2ecf20Sopenharmony_ci } 16808c2ecf20Sopenharmony_ci} 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_cistatic void dpcm_set_fe_runtime(struct snd_pcm_substream *substream) 16838c2ecf20Sopenharmony_ci{ 16848c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 16858c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 16868c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai; 16878c2ecf20Sopenharmony_ci int i; 16888c2ecf20Sopenharmony_ci 16898c2ecf20Sopenharmony_ci for_each_rtd_cpu_dais(rtd, i, cpu_dai) { 16908c2ecf20Sopenharmony_ci /* 16918c2ecf20Sopenharmony_ci * Skip CPUs which don't support the current stream 16928c2ecf20Sopenharmony_ci * type. See soc_pcm_init_runtime_hw() for more details 16938c2ecf20Sopenharmony_ci */ 16948c2ecf20Sopenharmony_ci if (!snd_soc_dai_stream_valid(cpu_dai, substream->stream)) 16958c2ecf20Sopenharmony_ci continue; 16968c2ecf20Sopenharmony_ci 16978c2ecf20Sopenharmony_ci dpcm_init_runtime_hw(runtime, 16988c2ecf20Sopenharmony_ci snd_soc_dai_get_pcm_stream(cpu_dai, 16998c2ecf20Sopenharmony_ci substream->stream)); 17008c2ecf20Sopenharmony_ci } 17018c2ecf20Sopenharmony_ci 17028c2ecf20Sopenharmony_ci dpcm_runtime_merge_format(substream, &runtime->hw.formats); 17038c2ecf20Sopenharmony_ci dpcm_runtime_merge_chan(substream, &runtime->hw.channels_min, 17048c2ecf20Sopenharmony_ci &runtime->hw.channels_max); 17058c2ecf20Sopenharmony_ci dpcm_runtime_merge_rate(substream, &runtime->hw.rates, 17068c2ecf20Sopenharmony_ci &runtime->hw.rate_min, &runtime->hw.rate_max); 17078c2ecf20Sopenharmony_ci} 17088c2ecf20Sopenharmony_ci 17098c2ecf20Sopenharmony_cistatic int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd); 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_ci/* Set FE's runtime_update state; the state is protected via PCM stream lock 17128c2ecf20Sopenharmony_ci * for avoiding the race with trigger callback. 17138c2ecf20Sopenharmony_ci * If the state is unset and a trigger is pending while the previous operation, 17148c2ecf20Sopenharmony_ci * process the pending trigger action here. 17158c2ecf20Sopenharmony_ci */ 17168c2ecf20Sopenharmony_cistatic void dpcm_set_fe_update_state(struct snd_soc_pcm_runtime *fe, 17178c2ecf20Sopenharmony_ci int stream, enum snd_soc_dpcm_update state) 17188c2ecf20Sopenharmony_ci{ 17198c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream = 17208c2ecf20Sopenharmony_ci snd_soc_dpcm_get_substream(fe, stream); 17218c2ecf20Sopenharmony_ci 17228c2ecf20Sopenharmony_ci snd_pcm_stream_lock_irq(substream); 17238c2ecf20Sopenharmony_ci if (state == SND_SOC_DPCM_UPDATE_NO && fe->dpcm[stream].trigger_pending) { 17248c2ecf20Sopenharmony_ci dpcm_fe_dai_do_trigger(substream, 17258c2ecf20Sopenharmony_ci fe->dpcm[stream].trigger_pending - 1); 17268c2ecf20Sopenharmony_ci fe->dpcm[stream].trigger_pending = 0; 17278c2ecf20Sopenharmony_ci } 17288c2ecf20Sopenharmony_ci fe->dpcm[stream].runtime_update = state; 17298c2ecf20Sopenharmony_ci snd_pcm_stream_unlock_irq(substream); 17308c2ecf20Sopenharmony_ci} 17318c2ecf20Sopenharmony_ci 17328c2ecf20Sopenharmony_cistatic int dpcm_apply_symmetry(struct snd_pcm_substream *fe_substream, 17338c2ecf20Sopenharmony_ci int stream) 17348c2ecf20Sopenharmony_ci{ 17358c2ecf20Sopenharmony_ci struct snd_soc_dpcm *dpcm; 17368c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(fe_substream); 17378c2ecf20Sopenharmony_ci struct snd_soc_dai *fe_cpu_dai; 17388c2ecf20Sopenharmony_ci int err = 0; 17398c2ecf20Sopenharmony_ci int i; 17408c2ecf20Sopenharmony_ci 17418c2ecf20Sopenharmony_ci /* apply symmetry for FE */ 17428c2ecf20Sopenharmony_ci if (soc_pcm_has_symmetry(fe_substream)) 17438c2ecf20Sopenharmony_ci fe_substream->runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX; 17448c2ecf20Sopenharmony_ci 17458c2ecf20Sopenharmony_ci for_each_rtd_cpu_dais (fe, i, fe_cpu_dai) { 17468c2ecf20Sopenharmony_ci /* Symmetry only applies if we've got an active stream. */ 17478c2ecf20Sopenharmony_ci if (snd_soc_dai_active(fe_cpu_dai)) { 17488c2ecf20Sopenharmony_ci err = soc_pcm_apply_symmetry(fe_substream, fe_cpu_dai); 17498c2ecf20Sopenharmony_ci if (err < 0) 17508c2ecf20Sopenharmony_ci return err; 17518c2ecf20Sopenharmony_ci } 17528c2ecf20Sopenharmony_ci } 17538c2ecf20Sopenharmony_ci 17548c2ecf20Sopenharmony_ci /* apply symmetry for BE */ 17558c2ecf20Sopenharmony_ci for_each_dpcm_be(fe, stream, dpcm) { 17568c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *be = dpcm->be; 17578c2ecf20Sopenharmony_ci struct snd_pcm_substream *be_substream = 17588c2ecf20Sopenharmony_ci snd_soc_dpcm_get_substream(be, stream); 17598c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd; 17608c2ecf20Sopenharmony_ci struct snd_soc_dai *dai; 17618c2ecf20Sopenharmony_ci int i; 17628c2ecf20Sopenharmony_ci 17638c2ecf20Sopenharmony_ci /* A backend may not have the requested substream */ 17648c2ecf20Sopenharmony_ci if (!be_substream) 17658c2ecf20Sopenharmony_ci continue; 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_ci rtd = asoc_substream_to_rtd(be_substream); 17688c2ecf20Sopenharmony_ci if (rtd->dai_link->be_hw_params_fixup) 17698c2ecf20Sopenharmony_ci continue; 17708c2ecf20Sopenharmony_ci 17718c2ecf20Sopenharmony_ci if (soc_pcm_has_symmetry(be_substream)) 17728c2ecf20Sopenharmony_ci be_substream->runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX; 17738c2ecf20Sopenharmony_ci 17748c2ecf20Sopenharmony_ci /* Symmetry only applies if we've got an active stream. */ 17758c2ecf20Sopenharmony_ci for_each_rtd_dais(rtd, i, dai) { 17768c2ecf20Sopenharmony_ci if (snd_soc_dai_active(dai)) { 17778c2ecf20Sopenharmony_ci err = soc_pcm_apply_symmetry(fe_substream, dai); 17788c2ecf20Sopenharmony_ci if (err < 0) 17798c2ecf20Sopenharmony_ci return err; 17808c2ecf20Sopenharmony_ci } 17818c2ecf20Sopenharmony_ci } 17828c2ecf20Sopenharmony_ci } 17838c2ecf20Sopenharmony_ci 17848c2ecf20Sopenharmony_ci return 0; 17858c2ecf20Sopenharmony_ci} 17868c2ecf20Sopenharmony_ci 17878c2ecf20Sopenharmony_cistatic int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream) 17888c2ecf20Sopenharmony_ci{ 17898c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(fe_substream); 17908c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = fe_substream->runtime; 17918c2ecf20Sopenharmony_ci int stream = fe_substream->stream, ret = 0; 17928c2ecf20Sopenharmony_ci 17938c2ecf20Sopenharmony_ci dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE); 17948c2ecf20Sopenharmony_ci 17958c2ecf20Sopenharmony_ci ret = dpcm_be_dai_startup(fe, stream); 17968c2ecf20Sopenharmony_ci if (ret < 0) { 17978c2ecf20Sopenharmony_ci dev_err(fe->dev,"ASoC: failed to start some BEs %d\n", ret); 17988c2ecf20Sopenharmony_ci goto be_err; 17998c2ecf20Sopenharmony_ci } 18008c2ecf20Sopenharmony_ci 18018c2ecf20Sopenharmony_ci dev_dbg(fe->dev, "ASoC: open FE %s\n", fe->dai_link->name); 18028c2ecf20Sopenharmony_ci 18038c2ecf20Sopenharmony_ci /* start the DAI frontend */ 18048c2ecf20Sopenharmony_ci ret = soc_pcm_open(fe_substream); 18058c2ecf20Sopenharmony_ci if (ret < 0) { 18068c2ecf20Sopenharmony_ci dev_err(fe->dev,"ASoC: failed to start FE %d\n", ret); 18078c2ecf20Sopenharmony_ci goto unwind; 18088c2ecf20Sopenharmony_ci } 18098c2ecf20Sopenharmony_ci 18108c2ecf20Sopenharmony_ci fe->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN; 18118c2ecf20Sopenharmony_ci 18128c2ecf20Sopenharmony_ci dpcm_set_fe_runtime(fe_substream); 18138c2ecf20Sopenharmony_ci snd_pcm_limit_hw_rates(runtime); 18148c2ecf20Sopenharmony_ci 18158c2ecf20Sopenharmony_ci ret = dpcm_apply_symmetry(fe_substream, stream); 18168c2ecf20Sopenharmony_ci if (ret < 0) 18178c2ecf20Sopenharmony_ci dev_err(fe->dev, "ASoC: failed to apply dpcm symmetry %d\n", 18188c2ecf20Sopenharmony_ci ret); 18198c2ecf20Sopenharmony_ci 18208c2ecf20Sopenharmony_ciunwind: 18218c2ecf20Sopenharmony_ci if (ret < 0) 18228c2ecf20Sopenharmony_ci dpcm_be_dai_startup_unwind(fe, stream); 18238c2ecf20Sopenharmony_cibe_err: 18248c2ecf20Sopenharmony_ci dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO); 18258c2ecf20Sopenharmony_ci return ret; 18268c2ecf20Sopenharmony_ci} 18278c2ecf20Sopenharmony_ci 18288c2ecf20Sopenharmony_ciint dpcm_be_dai_shutdown(struct snd_soc_pcm_runtime *fe, int stream) 18298c2ecf20Sopenharmony_ci{ 18308c2ecf20Sopenharmony_ci struct snd_soc_dpcm *dpcm; 18318c2ecf20Sopenharmony_ci 18328c2ecf20Sopenharmony_ci /* only shutdown BEs that are either sinks or sources to this FE DAI */ 18338c2ecf20Sopenharmony_ci for_each_dpcm_be(fe, stream, dpcm) { 18348c2ecf20Sopenharmony_ci 18358c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *be = dpcm->be; 18368c2ecf20Sopenharmony_ci struct snd_pcm_substream *be_substream = 18378c2ecf20Sopenharmony_ci snd_soc_dpcm_get_substream(be, stream); 18388c2ecf20Sopenharmony_ci 18398c2ecf20Sopenharmony_ci /* is this op for this BE ? */ 18408c2ecf20Sopenharmony_ci if (!snd_soc_dpcm_be_can_update(fe, be, stream)) 18418c2ecf20Sopenharmony_ci continue; 18428c2ecf20Sopenharmony_ci 18438c2ecf20Sopenharmony_ci if (be->dpcm[stream].users == 0) 18448c2ecf20Sopenharmony_ci dev_err(be->dev, "ASoC: no users %s at close - state %d\n", 18458c2ecf20Sopenharmony_ci stream ? "capture" : "playback", 18468c2ecf20Sopenharmony_ci be->dpcm[stream].state); 18478c2ecf20Sopenharmony_ci 18488c2ecf20Sopenharmony_ci if (--be->dpcm[stream].users != 0) 18498c2ecf20Sopenharmony_ci continue; 18508c2ecf20Sopenharmony_ci 18518c2ecf20Sopenharmony_ci if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) && 18528c2ecf20Sopenharmony_ci (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN)) { 18538c2ecf20Sopenharmony_ci soc_pcm_hw_free(be_substream); 18548c2ecf20Sopenharmony_ci be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE; 18558c2ecf20Sopenharmony_ci } 18568c2ecf20Sopenharmony_ci 18578c2ecf20Sopenharmony_ci dev_dbg(be->dev, "ASoC: close BE %s\n", 18588c2ecf20Sopenharmony_ci be->dai_link->name); 18598c2ecf20Sopenharmony_ci 18608c2ecf20Sopenharmony_ci soc_pcm_close(be_substream); 18618c2ecf20Sopenharmony_ci be_substream->runtime = NULL; 18628c2ecf20Sopenharmony_ci 18638c2ecf20Sopenharmony_ci be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; 18648c2ecf20Sopenharmony_ci } 18658c2ecf20Sopenharmony_ci return 0; 18668c2ecf20Sopenharmony_ci} 18678c2ecf20Sopenharmony_ci 18688c2ecf20Sopenharmony_cistatic int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream) 18698c2ecf20Sopenharmony_ci{ 18708c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream); 18718c2ecf20Sopenharmony_ci int stream = substream->stream; 18728c2ecf20Sopenharmony_ci 18738c2ecf20Sopenharmony_ci dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE); 18748c2ecf20Sopenharmony_ci 18758c2ecf20Sopenharmony_ci /* shutdown the BEs */ 18768c2ecf20Sopenharmony_ci dpcm_be_dai_shutdown(fe, stream); 18778c2ecf20Sopenharmony_ci 18788c2ecf20Sopenharmony_ci dev_dbg(fe->dev, "ASoC: close FE %s\n", fe->dai_link->name); 18798c2ecf20Sopenharmony_ci 18808c2ecf20Sopenharmony_ci /* now shutdown the frontend */ 18818c2ecf20Sopenharmony_ci soc_pcm_close(substream); 18828c2ecf20Sopenharmony_ci 18838c2ecf20Sopenharmony_ci /* run the stream event for each BE */ 18848c2ecf20Sopenharmony_ci dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP); 18858c2ecf20Sopenharmony_ci 18868c2ecf20Sopenharmony_ci fe->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; 18878c2ecf20Sopenharmony_ci dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO); 18888c2ecf20Sopenharmony_ci return 0; 18898c2ecf20Sopenharmony_ci} 18908c2ecf20Sopenharmony_ci 18918c2ecf20Sopenharmony_ciint dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream) 18928c2ecf20Sopenharmony_ci{ 18938c2ecf20Sopenharmony_ci struct snd_soc_dpcm *dpcm; 18948c2ecf20Sopenharmony_ci 18958c2ecf20Sopenharmony_ci /* only hw_params backends that are either sinks or sources 18968c2ecf20Sopenharmony_ci * to this frontend DAI */ 18978c2ecf20Sopenharmony_ci for_each_dpcm_be(fe, stream, dpcm) { 18988c2ecf20Sopenharmony_ci 18998c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *be = dpcm->be; 19008c2ecf20Sopenharmony_ci struct snd_pcm_substream *be_substream = 19018c2ecf20Sopenharmony_ci snd_soc_dpcm_get_substream(be, stream); 19028c2ecf20Sopenharmony_ci 19038c2ecf20Sopenharmony_ci /* is this op for this BE ? */ 19048c2ecf20Sopenharmony_ci if (!snd_soc_dpcm_be_can_update(fe, be, stream)) 19058c2ecf20Sopenharmony_ci continue; 19068c2ecf20Sopenharmony_ci 19078c2ecf20Sopenharmony_ci /* only free hw when no longer used - check all FEs */ 19088c2ecf20Sopenharmony_ci if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) 19098c2ecf20Sopenharmony_ci continue; 19108c2ecf20Sopenharmony_ci 19118c2ecf20Sopenharmony_ci /* do not free hw if this BE is used by other FE */ 19128c2ecf20Sopenharmony_ci if (be->dpcm[stream].users > 1) 19138c2ecf20Sopenharmony_ci continue; 19148c2ecf20Sopenharmony_ci 19158c2ecf20Sopenharmony_ci if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) && 19168c2ecf20Sopenharmony_ci (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) && 19178c2ecf20Sopenharmony_ci (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) && 19188c2ecf20Sopenharmony_ci (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED) && 19198c2ecf20Sopenharmony_ci (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP) && 19208c2ecf20Sopenharmony_ci (be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND)) 19218c2ecf20Sopenharmony_ci continue; 19228c2ecf20Sopenharmony_ci 19238c2ecf20Sopenharmony_ci dev_dbg(be->dev, "ASoC: hw_free BE %s\n", 19248c2ecf20Sopenharmony_ci be->dai_link->name); 19258c2ecf20Sopenharmony_ci 19268c2ecf20Sopenharmony_ci soc_pcm_hw_free(be_substream); 19278c2ecf20Sopenharmony_ci 19288c2ecf20Sopenharmony_ci be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE; 19298c2ecf20Sopenharmony_ci } 19308c2ecf20Sopenharmony_ci 19318c2ecf20Sopenharmony_ci return 0; 19328c2ecf20Sopenharmony_ci} 19338c2ecf20Sopenharmony_ci 19348c2ecf20Sopenharmony_cistatic int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream) 19358c2ecf20Sopenharmony_ci{ 19368c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream); 19378c2ecf20Sopenharmony_ci int err, stream = substream->stream; 19388c2ecf20Sopenharmony_ci 19398c2ecf20Sopenharmony_ci mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); 19408c2ecf20Sopenharmony_ci dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE); 19418c2ecf20Sopenharmony_ci 19428c2ecf20Sopenharmony_ci dev_dbg(fe->dev, "ASoC: hw_free FE %s\n", fe->dai_link->name); 19438c2ecf20Sopenharmony_ci 19448c2ecf20Sopenharmony_ci /* call hw_free on the frontend */ 19458c2ecf20Sopenharmony_ci err = soc_pcm_hw_free(substream); 19468c2ecf20Sopenharmony_ci if (err < 0) 19478c2ecf20Sopenharmony_ci dev_err(fe->dev,"ASoC: hw_free FE %s failed\n", 19488c2ecf20Sopenharmony_ci fe->dai_link->name); 19498c2ecf20Sopenharmony_ci 19508c2ecf20Sopenharmony_ci /* only hw_params backends that are either sinks or sources 19518c2ecf20Sopenharmony_ci * to this frontend DAI */ 19528c2ecf20Sopenharmony_ci err = dpcm_be_dai_hw_free(fe, stream); 19538c2ecf20Sopenharmony_ci 19548c2ecf20Sopenharmony_ci fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE; 19558c2ecf20Sopenharmony_ci dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO); 19568c2ecf20Sopenharmony_ci 19578c2ecf20Sopenharmony_ci mutex_unlock(&fe->card->mutex); 19588c2ecf20Sopenharmony_ci return 0; 19598c2ecf20Sopenharmony_ci} 19608c2ecf20Sopenharmony_ci 19618c2ecf20Sopenharmony_ciint dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream) 19628c2ecf20Sopenharmony_ci{ 19638c2ecf20Sopenharmony_ci struct snd_soc_dpcm *dpcm; 19648c2ecf20Sopenharmony_ci int ret; 19658c2ecf20Sopenharmony_ci 19668c2ecf20Sopenharmony_ci for_each_dpcm_be(fe, stream, dpcm) { 19678c2ecf20Sopenharmony_ci 19688c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *be = dpcm->be; 19698c2ecf20Sopenharmony_ci struct snd_pcm_substream *be_substream = 19708c2ecf20Sopenharmony_ci snd_soc_dpcm_get_substream(be, stream); 19718c2ecf20Sopenharmony_ci 19728c2ecf20Sopenharmony_ci /* is this op for this BE ? */ 19738c2ecf20Sopenharmony_ci if (!snd_soc_dpcm_be_can_update(fe, be, stream)) 19748c2ecf20Sopenharmony_ci continue; 19758c2ecf20Sopenharmony_ci 19768c2ecf20Sopenharmony_ci /* copy params for each dpcm */ 19778c2ecf20Sopenharmony_ci memcpy(&dpcm->hw_params, &fe->dpcm[stream].hw_params, 19788c2ecf20Sopenharmony_ci sizeof(struct snd_pcm_hw_params)); 19798c2ecf20Sopenharmony_ci 19808c2ecf20Sopenharmony_ci /* perform any hw_params fixups */ 19818c2ecf20Sopenharmony_ci ret = snd_soc_link_be_hw_params_fixup(be, &dpcm->hw_params); 19828c2ecf20Sopenharmony_ci if (ret < 0) 19838c2ecf20Sopenharmony_ci goto unwind; 19848c2ecf20Sopenharmony_ci 19858c2ecf20Sopenharmony_ci /* copy the fixed-up hw params for BE dai */ 19868c2ecf20Sopenharmony_ci memcpy(&be->dpcm[stream].hw_params, &dpcm->hw_params, 19878c2ecf20Sopenharmony_ci sizeof(struct snd_pcm_hw_params)); 19888c2ecf20Sopenharmony_ci 19898c2ecf20Sopenharmony_ci /* only allow hw_params() if no connected FEs are running */ 19908c2ecf20Sopenharmony_ci if (!snd_soc_dpcm_can_be_params(fe, be, stream)) 19918c2ecf20Sopenharmony_ci continue; 19928c2ecf20Sopenharmony_ci 19938c2ecf20Sopenharmony_ci if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) && 19948c2ecf20Sopenharmony_ci (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) && 19958c2ecf20Sopenharmony_ci (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE)) 19968c2ecf20Sopenharmony_ci continue; 19978c2ecf20Sopenharmony_ci 19988c2ecf20Sopenharmony_ci dev_dbg(be->dev, "ASoC: hw_params BE %s\n", 19998c2ecf20Sopenharmony_ci be->dai_link->name); 20008c2ecf20Sopenharmony_ci 20018c2ecf20Sopenharmony_ci ret = soc_pcm_hw_params(be_substream, &dpcm->hw_params); 20028c2ecf20Sopenharmony_ci if (ret < 0) { 20038c2ecf20Sopenharmony_ci dev_err(dpcm->be->dev, 20048c2ecf20Sopenharmony_ci "ASoC: hw_params BE failed %d\n", ret); 20058c2ecf20Sopenharmony_ci goto unwind; 20068c2ecf20Sopenharmony_ci } 20078c2ecf20Sopenharmony_ci 20088c2ecf20Sopenharmony_ci be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_PARAMS; 20098c2ecf20Sopenharmony_ci } 20108c2ecf20Sopenharmony_ci return 0; 20118c2ecf20Sopenharmony_ci 20128c2ecf20Sopenharmony_ciunwind: 20138c2ecf20Sopenharmony_ci /* disable any enabled and non active backends */ 20148c2ecf20Sopenharmony_ci for_each_dpcm_be_rollback(fe, stream, dpcm) { 20158c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *be = dpcm->be; 20168c2ecf20Sopenharmony_ci struct snd_pcm_substream *be_substream = 20178c2ecf20Sopenharmony_ci snd_soc_dpcm_get_substream(be, stream); 20188c2ecf20Sopenharmony_ci 20198c2ecf20Sopenharmony_ci if (!snd_soc_dpcm_be_can_update(fe, be, stream)) 20208c2ecf20Sopenharmony_ci continue; 20218c2ecf20Sopenharmony_ci 20228c2ecf20Sopenharmony_ci /* only allow hw_free() if no connected FEs are running */ 20238c2ecf20Sopenharmony_ci if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) 20248c2ecf20Sopenharmony_ci continue; 20258c2ecf20Sopenharmony_ci 20268c2ecf20Sopenharmony_ci if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) && 20278c2ecf20Sopenharmony_ci (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) && 20288c2ecf20Sopenharmony_ci (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) && 20298c2ecf20Sopenharmony_ci (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP)) 20308c2ecf20Sopenharmony_ci continue; 20318c2ecf20Sopenharmony_ci 20328c2ecf20Sopenharmony_ci soc_pcm_hw_free(be_substream); 20338c2ecf20Sopenharmony_ci } 20348c2ecf20Sopenharmony_ci 20358c2ecf20Sopenharmony_ci return ret; 20368c2ecf20Sopenharmony_ci} 20378c2ecf20Sopenharmony_ci 20388c2ecf20Sopenharmony_cistatic int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream, 20398c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params) 20408c2ecf20Sopenharmony_ci{ 20418c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream); 20428c2ecf20Sopenharmony_ci int ret, stream = substream->stream; 20438c2ecf20Sopenharmony_ci 20448c2ecf20Sopenharmony_ci mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); 20458c2ecf20Sopenharmony_ci dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE); 20468c2ecf20Sopenharmony_ci 20478c2ecf20Sopenharmony_ci memcpy(&fe->dpcm[stream].hw_params, params, 20488c2ecf20Sopenharmony_ci sizeof(struct snd_pcm_hw_params)); 20498c2ecf20Sopenharmony_ci ret = dpcm_be_dai_hw_params(fe, stream); 20508c2ecf20Sopenharmony_ci if (ret < 0) { 20518c2ecf20Sopenharmony_ci dev_err(fe->dev,"ASoC: hw_params BE failed %d\n", ret); 20528c2ecf20Sopenharmony_ci goto out; 20538c2ecf20Sopenharmony_ci } 20548c2ecf20Sopenharmony_ci 20558c2ecf20Sopenharmony_ci dev_dbg(fe->dev, "ASoC: hw_params FE %s rate %d chan %x fmt %d\n", 20568c2ecf20Sopenharmony_ci fe->dai_link->name, params_rate(params), 20578c2ecf20Sopenharmony_ci params_channels(params), params_format(params)); 20588c2ecf20Sopenharmony_ci 20598c2ecf20Sopenharmony_ci /* call hw_params on the frontend */ 20608c2ecf20Sopenharmony_ci ret = soc_pcm_hw_params(substream, params); 20618c2ecf20Sopenharmony_ci if (ret < 0) { 20628c2ecf20Sopenharmony_ci dev_err(fe->dev,"ASoC: hw_params FE failed %d\n", ret); 20638c2ecf20Sopenharmony_ci dpcm_be_dai_hw_free(fe, stream); 20648c2ecf20Sopenharmony_ci } else 20658c2ecf20Sopenharmony_ci fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_PARAMS; 20668c2ecf20Sopenharmony_ci 20678c2ecf20Sopenharmony_ciout: 20688c2ecf20Sopenharmony_ci dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO); 20698c2ecf20Sopenharmony_ci mutex_unlock(&fe->card->mutex); 20708c2ecf20Sopenharmony_ci return ret; 20718c2ecf20Sopenharmony_ci} 20728c2ecf20Sopenharmony_ci 20738c2ecf20Sopenharmony_cistatic int dpcm_do_trigger(struct snd_soc_dpcm *dpcm, 20748c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream, int cmd) 20758c2ecf20Sopenharmony_ci{ 20768c2ecf20Sopenharmony_ci int ret; 20778c2ecf20Sopenharmony_ci 20788c2ecf20Sopenharmony_ci dev_dbg(dpcm->be->dev, "ASoC: trigger BE %s cmd %d\n", 20798c2ecf20Sopenharmony_ci dpcm->be->dai_link->name, cmd); 20808c2ecf20Sopenharmony_ci 20818c2ecf20Sopenharmony_ci ret = soc_pcm_trigger(substream, cmd); 20828c2ecf20Sopenharmony_ci if (ret < 0) 20838c2ecf20Sopenharmony_ci dev_err(dpcm->be->dev,"ASoC: trigger BE failed %d\n", ret); 20848c2ecf20Sopenharmony_ci 20858c2ecf20Sopenharmony_ci return ret; 20868c2ecf20Sopenharmony_ci} 20878c2ecf20Sopenharmony_ci 20888c2ecf20Sopenharmony_ciint dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, 20898c2ecf20Sopenharmony_ci int cmd) 20908c2ecf20Sopenharmony_ci{ 20918c2ecf20Sopenharmony_ci struct snd_soc_dpcm *dpcm; 20928c2ecf20Sopenharmony_ci int ret = 0; 20938c2ecf20Sopenharmony_ci 20948c2ecf20Sopenharmony_ci for_each_dpcm_be(fe, stream, dpcm) { 20958c2ecf20Sopenharmony_ci 20968c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *be = dpcm->be; 20978c2ecf20Sopenharmony_ci struct snd_pcm_substream *be_substream = 20988c2ecf20Sopenharmony_ci snd_soc_dpcm_get_substream(be, stream); 20998c2ecf20Sopenharmony_ci 21008c2ecf20Sopenharmony_ci /* is this op for this BE ? */ 21018c2ecf20Sopenharmony_ci if (!snd_soc_dpcm_be_can_update(fe, be, stream)) 21028c2ecf20Sopenharmony_ci continue; 21038c2ecf20Sopenharmony_ci 21048c2ecf20Sopenharmony_ci switch (cmd) { 21058c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 21068c2ecf20Sopenharmony_ci if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) && 21078c2ecf20Sopenharmony_ci (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP) && 21088c2ecf20Sopenharmony_ci (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED)) 21098c2ecf20Sopenharmony_ci continue; 21108c2ecf20Sopenharmony_ci 21118c2ecf20Sopenharmony_ci ret = dpcm_do_trigger(dpcm, be_substream, cmd); 21128c2ecf20Sopenharmony_ci if (ret) 21138c2ecf20Sopenharmony_ci return ret; 21148c2ecf20Sopenharmony_ci 21158c2ecf20Sopenharmony_ci be->dpcm[stream].state = SND_SOC_DPCM_STATE_START; 21168c2ecf20Sopenharmony_ci break; 21178c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 21188c2ecf20Sopenharmony_ci if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND)) 21198c2ecf20Sopenharmony_ci continue; 21208c2ecf20Sopenharmony_ci 21218c2ecf20Sopenharmony_ci ret = dpcm_do_trigger(dpcm, be_substream, cmd); 21228c2ecf20Sopenharmony_ci if (ret) 21238c2ecf20Sopenharmony_ci return ret; 21248c2ecf20Sopenharmony_ci 21258c2ecf20Sopenharmony_ci be->dpcm[stream].state = SND_SOC_DPCM_STATE_START; 21268c2ecf20Sopenharmony_ci break; 21278c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 21288c2ecf20Sopenharmony_ci if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED)) 21298c2ecf20Sopenharmony_ci continue; 21308c2ecf20Sopenharmony_ci 21318c2ecf20Sopenharmony_ci ret = dpcm_do_trigger(dpcm, be_substream, cmd); 21328c2ecf20Sopenharmony_ci if (ret) 21338c2ecf20Sopenharmony_ci return ret; 21348c2ecf20Sopenharmony_ci 21358c2ecf20Sopenharmony_ci be->dpcm[stream].state = SND_SOC_DPCM_STATE_START; 21368c2ecf20Sopenharmony_ci break; 21378c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 21388c2ecf20Sopenharmony_ci if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) && 21398c2ecf20Sopenharmony_ci (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED)) 21408c2ecf20Sopenharmony_ci continue; 21418c2ecf20Sopenharmony_ci 21428c2ecf20Sopenharmony_ci if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) 21438c2ecf20Sopenharmony_ci continue; 21448c2ecf20Sopenharmony_ci 21458c2ecf20Sopenharmony_ci ret = dpcm_do_trigger(dpcm, be_substream, cmd); 21468c2ecf20Sopenharmony_ci if (ret) 21478c2ecf20Sopenharmony_ci return ret; 21488c2ecf20Sopenharmony_ci 21498c2ecf20Sopenharmony_ci be->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP; 21508c2ecf20Sopenharmony_ci break; 21518c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 21528c2ecf20Sopenharmony_ci if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) 21538c2ecf20Sopenharmony_ci continue; 21548c2ecf20Sopenharmony_ci 21558c2ecf20Sopenharmony_ci if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) 21568c2ecf20Sopenharmony_ci continue; 21578c2ecf20Sopenharmony_ci 21588c2ecf20Sopenharmony_ci ret = dpcm_do_trigger(dpcm, be_substream, cmd); 21598c2ecf20Sopenharmony_ci if (ret) 21608c2ecf20Sopenharmony_ci return ret; 21618c2ecf20Sopenharmony_ci 21628c2ecf20Sopenharmony_ci be->dpcm[stream].state = SND_SOC_DPCM_STATE_SUSPEND; 21638c2ecf20Sopenharmony_ci break; 21648c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 21658c2ecf20Sopenharmony_ci if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) 21668c2ecf20Sopenharmony_ci continue; 21678c2ecf20Sopenharmony_ci 21688c2ecf20Sopenharmony_ci if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) 21698c2ecf20Sopenharmony_ci continue; 21708c2ecf20Sopenharmony_ci 21718c2ecf20Sopenharmony_ci ret = dpcm_do_trigger(dpcm, be_substream, cmd); 21728c2ecf20Sopenharmony_ci if (ret) 21738c2ecf20Sopenharmony_ci return ret; 21748c2ecf20Sopenharmony_ci 21758c2ecf20Sopenharmony_ci be->dpcm[stream].state = SND_SOC_DPCM_STATE_PAUSED; 21768c2ecf20Sopenharmony_ci break; 21778c2ecf20Sopenharmony_ci } 21788c2ecf20Sopenharmony_ci } 21798c2ecf20Sopenharmony_ci 21808c2ecf20Sopenharmony_ci return ret; 21818c2ecf20Sopenharmony_ci} 21828c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dpcm_be_dai_trigger); 21838c2ecf20Sopenharmony_ci 21848c2ecf20Sopenharmony_cistatic int dpcm_dai_trigger_fe_be(struct snd_pcm_substream *substream, 21858c2ecf20Sopenharmony_ci int cmd, bool fe_first) 21868c2ecf20Sopenharmony_ci{ 21878c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream); 21888c2ecf20Sopenharmony_ci int ret; 21898c2ecf20Sopenharmony_ci 21908c2ecf20Sopenharmony_ci /* call trigger on the frontend before the backend. */ 21918c2ecf20Sopenharmony_ci if (fe_first) { 21928c2ecf20Sopenharmony_ci dev_dbg(fe->dev, "ASoC: pre trigger FE %s cmd %d\n", 21938c2ecf20Sopenharmony_ci fe->dai_link->name, cmd); 21948c2ecf20Sopenharmony_ci 21958c2ecf20Sopenharmony_ci ret = soc_pcm_trigger(substream, cmd); 21968c2ecf20Sopenharmony_ci if (ret < 0) 21978c2ecf20Sopenharmony_ci return ret; 21988c2ecf20Sopenharmony_ci 21998c2ecf20Sopenharmony_ci ret = dpcm_be_dai_trigger(fe, substream->stream, cmd); 22008c2ecf20Sopenharmony_ci return ret; 22018c2ecf20Sopenharmony_ci } 22028c2ecf20Sopenharmony_ci 22038c2ecf20Sopenharmony_ci /* call trigger on the frontend after the backend. */ 22048c2ecf20Sopenharmony_ci ret = dpcm_be_dai_trigger(fe, substream->stream, cmd); 22058c2ecf20Sopenharmony_ci if (ret < 0) 22068c2ecf20Sopenharmony_ci return ret; 22078c2ecf20Sopenharmony_ci 22088c2ecf20Sopenharmony_ci dev_dbg(fe->dev, "ASoC: post trigger FE %s cmd %d\n", 22098c2ecf20Sopenharmony_ci fe->dai_link->name, cmd); 22108c2ecf20Sopenharmony_ci 22118c2ecf20Sopenharmony_ci ret = soc_pcm_trigger(substream, cmd); 22128c2ecf20Sopenharmony_ci 22138c2ecf20Sopenharmony_ci return ret; 22148c2ecf20Sopenharmony_ci} 22158c2ecf20Sopenharmony_ci 22168c2ecf20Sopenharmony_cistatic int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd) 22178c2ecf20Sopenharmony_ci{ 22188c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream); 22198c2ecf20Sopenharmony_ci int stream = substream->stream; 22208c2ecf20Sopenharmony_ci int ret = 0; 22218c2ecf20Sopenharmony_ci enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream]; 22228c2ecf20Sopenharmony_ci 22238c2ecf20Sopenharmony_ci fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; 22248c2ecf20Sopenharmony_ci 22258c2ecf20Sopenharmony_ci switch (trigger) { 22268c2ecf20Sopenharmony_ci case SND_SOC_DPCM_TRIGGER_PRE: 22278c2ecf20Sopenharmony_ci switch (cmd) { 22288c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 22298c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 22308c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 22318c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_DRAIN: 22328c2ecf20Sopenharmony_ci ret = dpcm_dai_trigger_fe_be(substream, cmd, true); 22338c2ecf20Sopenharmony_ci break; 22348c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 22358c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 22368c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 22378c2ecf20Sopenharmony_ci ret = dpcm_dai_trigger_fe_be(substream, cmd, false); 22388c2ecf20Sopenharmony_ci break; 22398c2ecf20Sopenharmony_ci default: 22408c2ecf20Sopenharmony_ci ret = -EINVAL; 22418c2ecf20Sopenharmony_ci break; 22428c2ecf20Sopenharmony_ci } 22438c2ecf20Sopenharmony_ci break; 22448c2ecf20Sopenharmony_ci case SND_SOC_DPCM_TRIGGER_POST: 22458c2ecf20Sopenharmony_ci switch (cmd) { 22468c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 22478c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 22488c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 22498c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_DRAIN: 22508c2ecf20Sopenharmony_ci ret = dpcm_dai_trigger_fe_be(substream, cmd, false); 22518c2ecf20Sopenharmony_ci break; 22528c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 22538c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 22548c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 22558c2ecf20Sopenharmony_ci ret = dpcm_dai_trigger_fe_be(substream, cmd, true); 22568c2ecf20Sopenharmony_ci break; 22578c2ecf20Sopenharmony_ci default: 22588c2ecf20Sopenharmony_ci ret = -EINVAL; 22598c2ecf20Sopenharmony_ci break; 22608c2ecf20Sopenharmony_ci } 22618c2ecf20Sopenharmony_ci break; 22628c2ecf20Sopenharmony_ci case SND_SOC_DPCM_TRIGGER_BESPOKE: 22638c2ecf20Sopenharmony_ci /* bespoke trigger() - handles both FE and BEs */ 22648c2ecf20Sopenharmony_ci 22658c2ecf20Sopenharmony_ci dev_dbg(fe->dev, "ASoC: bespoke trigger FE %s cmd %d\n", 22668c2ecf20Sopenharmony_ci fe->dai_link->name, cmd); 22678c2ecf20Sopenharmony_ci 22688c2ecf20Sopenharmony_ci ret = snd_soc_pcm_dai_bespoke_trigger(substream, cmd); 22698c2ecf20Sopenharmony_ci break; 22708c2ecf20Sopenharmony_ci default: 22718c2ecf20Sopenharmony_ci dev_err(fe->dev, "ASoC: invalid trigger cmd %d for %s\n", cmd, 22728c2ecf20Sopenharmony_ci fe->dai_link->name); 22738c2ecf20Sopenharmony_ci ret = -EINVAL; 22748c2ecf20Sopenharmony_ci goto out; 22758c2ecf20Sopenharmony_ci } 22768c2ecf20Sopenharmony_ci 22778c2ecf20Sopenharmony_ci if (ret < 0) { 22788c2ecf20Sopenharmony_ci dev_err(fe->dev, "ASoC: trigger FE cmd: %d failed: %d\n", 22798c2ecf20Sopenharmony_ci cmd, ret); 22808c2ecf20Sopenharmony_ci goto out; 22818c2ecf20Sopenharmony_ci } 22828c2ecf20Sopenharmony_ci 22838c2ecf20Sopenharmony_ci switch (cmd) { 22848c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 22858c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 22868c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 22878c2ecf20Sopenharmony_ci fe->dpcm[stream].state = SND_SOC_DPCM_STATE_START; 22888c2ecf20Sopenharmony_ci break; 22898c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 22908c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 22918c2ecf20Sopenharmony_ci fe->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP; 22928c2ecf20Sopenharmony_ci break; 22938c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 22948c2ecf20Sopenharmony_ci fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PAUSED; 22958c2ecf20Sopenharmony_ci break; 22968c2ecf20Sopenharmony_ci } 22978c2ecf20Sopenharmony_ci 22988c2ecf20Sopenharmony_ciout: 22998c2ecf20Sopenharmony_ci fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; 23008c2ecf20Sopenharmony_ci return ret; 23018c2ecf20Sopenharmony_ci} 23028c2ecf20Sopenharmony_ci 23038c2ecf20Sopenharmony_cistatic int dpcm_fe_dai_trigger(struct snd_pcm_substream *substream, int cmd) 23048c2ecf20Sopenharmony_ci{ 23058c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream); 23068c2ecf20Sopenharmony_ci int stream = substream->stream; 23078c2ecf20Sopenharmony_ci 23088c2ecf20Sopenharmony_ci /* if FE's runtime_update is already set, we're in race; 23098c2ecf20Sopenharmony_ci * process this trigger later at exit 23108c2ecf20Sopenharmony_ci */ 23118c2ecf20Sopenharmony_ci if (fe->dpcm[stream].runtime_update != SND_SOC_DPCM_UPDATE_NO) { 23128c2ecf20Sopenharmony_ci fe->dpcm[stream].trigger_pending = cmd + 1; 23138c2ecf20Sopenharmony_ci return 0; /* delayed, assuming it's successful */ 23148c2ecf20Sopenharmony_ci } 23158c2ecf20Sopenharmony_ci 23168c2ecf20Sopenharmony_ci /* we're alone, let's trigger */ 23178c2ecf20Sopenharmony_ci return dpcm_fe_dai_do_trigger(substream, cmd); 23188c2ecf20Sopenharmony_ci} 23198c2ecf20Sopenharmony_ci 23208c2ecf20Sopenharmony_ciint dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream) 23218c2ecf20Sopenharmony_ci{ 23228c2ecf20Sopenharmony_ci struct snd_soc_dpcm *dpcm; 23238c2ecf20Sopenharmony_ci int ret = 0; 23248c2ecf20Sopenharmony_ci 23258c2ecf20Sopenharmony_ci for_each_dpcm_be(fe, stream, dpcm) { 23268c2ecf20Sopenharmony_ci 23278c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *be = dpcm->be; 23288c2ecf20Sopenharmony_ci struct snd_pcm_substream *be_substream = 23298c2ecf20Sopenharmony_ci snd_soc_dpcm_get_substream(be, stream); 23308c2ecf20Sopenharmony_ci 23318c2ecf20Sopenharmony_ci /* is this op for this BE ? */ 23328c2ecf20Sopenharmony_ci if (!snd_soc_dpcm_be_can_update(fe, be, stream)) 23338c2ecf20Sopenharmony_ci continue; 23348c2ecf20Sopenharmony_ci 23358c2ecf20Sopenharmony_ci if (!snd_soc_dpcm_can_be_prepared(fe, be, stream)) 23368c2ecf20Sopenharmony_ci continue; 23378c2ecf20Sopenharmony_ci 23388c2ecf20Sopenharmony_ci if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) && 23398c2ecf20Sopenharmony_ci (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP) && 23408c2ecf20Sopenharmony_ci (be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND) && 23418c2ecf20Sopenharmony_ci (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED)) 23428c2ecf20Sopenharmony_ci continue; 23438c2ecf20Sopenharmony_ci 23448c2ecf20Sopenharmony_ci dev_dbg(be->dev, "ASoC: prepare BE %s\n", 23458c2ecf20Sopenharmony_ci be->dai_link->name); 23468c2ecf20Sopenharmony_ci 23478c2ecf20Sopenharmony_ci ret = soc_pcm_prepare(be_substream); 23488c2ecf20Sopenharmony_ci if (ret < 0) { 23498c2ecf20Sopenharmony_ci dev_err(be->dev, "ASoC: backend prepare failed %d\n", 23508c2ecf20Sopenharmony_ci ret); 23518c2ecf20Sopenharmony_ci break; 23528c2ecf20Sopenharmony_ci } 23538c2ecf20Sopenharmony_ci 23548c2ecf20Sopenharmony_ci be->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE; 23558c2ecf20Sopenharmony_ci } 23568c2ecf20Sopenharmony_ci return ret; 23578c2ecf20Sopenharmony_ci} 23588c2ecf20Sopenharmony_ci 23598c2ecf20Sopenharmony_cistatic int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream) 23608c2ecf20Sopenharmony_ci{ 23618c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream); 23628c2ecf20Sopenharmony_ci int stream = substream->stream, ret = 0; 23638c2ecf20Sopenharmony_ci 23648c2ecf20Sopenharmony_ci mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); 23658c2ecf20Sopenharmony_ci 23668c2ecf20Sopenharmony_ci dev_dbg(fe->dev, "ASoC: prepare FE %s\n", fe->dai_link->name); 23678c2ecf20Sopenharmony_ci 23688c2ecf20Sopenharmony_ci dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE); 23698c2ecf20Sopenharmony_ci 23708c2ecf20Sopenharmony_ci /* there is no point preparing this FE if there are no BEs */ 23718c2ecf20Sopenharmony_ci if (list_empty(&fe->dpcm[stream].be_clients)) { 23728c2ecf20Sopenharmony_ci dev_err(fe->dev, "ASoC: no backend DAIs enabled for %s\n", 23738c2ecf20Sopenharmony_ci fe->dai_link->name); 23748c2ecf20Sopenharmony_ci ret = -EINVAL; 23758c2ecf20Sopenharmony_ci goto out; 23768c2ecf20Sopenharmony_ci } 23778c2ecf20Sopenharmony_ci 23788c2ecf20Sopenharmony_ci ret = dpcm_be_dai_prepare(fe, stream); 23798c2ecf20Sopenharmony_ci if (ret < 0) 23808c2ecf20Sopenharmony_ci goto out; 23818c2ecf20Sopenharmony_ci 23828c2ecf20Sopenharmony_ci /* call prepare on the frontend */ 23838c2ecf20Sopenharmony_ci ret = soc_pcm_prepare(substream); 23848c2ecf20Sopenharmony_ci if (ret < 0) { 23858c2ecf20Sopenharmony_ci dev_err(fe->dev,"ASoC: prepare FE %s failed\n", 23868c2ecf20Sopenharmony_ci fe->dai_link->name); 23878c2ecf20Sopenharmony_ci goto out; 23888c2ecf20Sopenharmony_ci } 23898c2ecf20Sopenharmony_ci 23908c2ecf20Sopenharmony_ci /* run the stream event for each BE */ 23918c2ecf20Sopenharmony_ci dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START); 23928c2ecf20Sopenharmony_ci fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE; 23938c2ecf20Sopenharmony_ci 23948c2ecf20Sopenharmony_ciout: 23958c2ecf20Sopenharmony_ci dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO); 23968c2ecf20Sopenharmony_ci mutex_unlock(&fe->card->mutex); 23978c2ecf20Sopenharmony_ci 23988c2ecf20Sopenharmony_ci return ret; 23998c2ecf20Sopenharmony_ci} 24008c2ecf20Sopenharmony_ci 24018c2ecf20Sopenharmony_cistatic int dpcm_run_update_shutdown(struct snd_soc_pcm_runtime *fe, int stream) 24028c2ecf20Sopenharmony_ci{ 24038c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream = 24048c2ecf20Sopenharmony_ci snd_soc_dpcm_get_substream(fe, stream); 24058c2ecf20Sopenharmony_ci enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream]; 24068c2ecf20Sopenharmony_ci int err; 24078c2ecf20Sopenharmony_ci 24088c2ecf20Sopenharmony_ci dev_dbg(fe->dev, "ASoC: runtime %s close on FE %s\n", 24098c2ecf20Sopenharmony_ci stream ? "capture" : "playback", fe->dai_link->name); 24108c2ecf20Sopenharmony_ci 24118c2ecf20Sopenharmony_ci if (trigger == SND_SOC_DPCM_TRIGGER_BESPOKE) { 24128c2ecf20Sopenharmony_ci /* call bespoke trigger - FE takes care of all BE triggers */ 24138c2ecf20Sopenharmony_ci dev_dbg(fe->dev, "ASoC: bespoke trigger FE %s cmd stop\n", 24148c2ecf20Sopenharmony_ci fe->dai_link->name); 24158c2ecf20Sopenharmony_ci 24168c2ecf20Sopenharmony_ci err = snd_soc_pcm_dai_bespoke_trigger(substream, SNDRV_PCM_TRIGGER_STOP); 24178c2ecf20Sopenharmony_ci if (err < 0) 24188c2ecf20Sopenharmony_ci dev_err(fe->dev,"ASoC: trigger FE failed %d\n", err); 24198c2ecf20Sopenharmony_ci } else { 24208c2ecf20Sopenharmony_ci dev_dbg(fe->dev, "ASoC: trigger FE %s cmd stop\n", 24218c2ecf20Sopenharmony_ci fe->dai_link->name); 24228c2ecf20Sopenharmony_ci 24238c2ecf20Sopenharmony_ci err = dpcm_be_dai_trigger(fe, stream, SNDRV_PCM_TRIGGER_STOP); 24248c2ecf20Sopenharmony_ci if (err < 0) 24258c2ecf20Sopenharmony_ci dev_err(fe->dev,"ASoC: trigger FE failed %d\n", err); 24268c2ecf20Sopenharmony_ci } 24278c2ecf20Sopenharmony_ci 24288c2ecf20Sopenharmony_ci err = dpcm_be_dai_hw_free(fe, stream); 24298c2ecf20Sopenharmony_ci if (err < 0) 24308c2ecf20Sopenharmony_ci dev_err(fe->dev,"ASoC: hw_free FE failed %d\n", err); 24318c2ecf20Sopenharmony_ci 24328c2ecf20Sopenharmony_ci err = dpcm_be_dai_shutdown(fe, stream); 24338c2ecf20Sopenharmony_ci if (err < 0) 24348c2ecf20Sopenharmony_ci dev_err(fe->dev,"ASoC: shutdown FE failed %d\n", err); 24358c2ecf20Sopenharmony_ci 24368c2ecf20Sopenharmony_ci /* run the stream event for each BE */ 24378c2ecf20Sopenharmony_ci dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_NOP); 24388c2ecf20Sopenharmony_ci 24398c2ecf20Sopenharmony_ci return 0; 24408c2ecf20Sopenharmony_ci} 24418c2ecf20Sopenharmony_ci 24428c2ecf20Sopenharmony_cistatic int dpcm_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream) 24438c2ecf20Sopenharmony_ci{ 24448c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream = 24458c2ecf20Sopenharmony_ci snd_soc_dpcm_get_substream(fe, stream); 24468c2ecf20Sopenharmony_ci struct snd_soc_dpcm *dpcm; 24478c2ecf20Sopenharmony_ci enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream]; 24488c2ecf20Sopenharmony_ci int ret; 24498c2ecf20Sopenharmony_ci unsigned long flags; 24508c2ecf20Sopenharmony_ci 24518c2ecf20Sopenharmony_ci dev_dbg(fe->dev, "ASoC: runtime %s open on FE %s\n", 24528c2ecf20Sopenharmony_ci stream ? "capture" : "playback", fe->dai_link->name); 24538c2ecf20Sopenharmony_ci 24548c2ecf20Sopenharmony_ci /* Only start the BE if the FE is ready */ 24558c2ecf20Sopenharmony_ci if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_HW_FREE || 24568c2ecf20Sopenharmony_ci fe->dpcm[stream].state == SND_SOC_DPCM_STATE_CLOSE) 24578c2ecf20Sopenharmony_ci return -EINVAL; 24588c2ecf20Sopenharmony_ci 24598c2ecf20Sopenharmony_ci /* startup must always be called for new BEs */ 24608c2ecf20Sopenharmony_ci ret = dpcm_be_dai_startup(fe, stream); 24618c2ecf20Sopenharmony_ci if (ret < 0) 24628c2ecf20Sopenharmony_ci goto disconnect; 24638c2ecf20Sopenharmony_ci 24648c2ecf20Sopenharmony_ci /* keep going if FE state is > open */ 24658c2ecf20Sopenharmony_ci if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_OPEN) 24668c2ecf20Sopenharmony_ci return 0; 24678c2ecf20Sopenharmony_ci 24688c2ecf20Sopenharmony_ci ret = dpcm_be_dai_hw_params(fe, stream); 24698c2ecf20Sopenharmony_ci if (ret < 0) 24708c2ecf20Sopenharmony_ci goto close; 24718c2ecf20Sopenharmony_ci 24728c2ecf20Sopenharmony_ci /* keep going if FE state is > hw_params */ 24738c2ecf20Sopenharmony_ci if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_HW_PARAMS) 24748c2ecf20Sopenharmony_ci return 0; 24758c2ecf20Sopenharmony_ci 24768c2ecf20Sopenharmony_ci 24778c2ecf20Sopenharmony_ci ret = dpcm_be_dai_prepare(fe, stream); 24788c2ecf20Sopenharmony_ci if (ret < 0) 24798c2ecf20Sopenharmony_ci goto hw_free; 24808c2ecf20Sopenharmony_ci 24818c2ecf20Sopenharmony_ci /* run the stream event for each BE */ 24828c2ecf20Sopenharmony_ci dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_NOP); 24838c2ecf20Sopenharmony_ci 24848c2ecf20Sopenharmony_ci /* keep going if FE state is > prepare */ 24858c2ecf20Sopenharmony_ci if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_PREPARE || 24868c2ecf20Sopenharmony_ci fe->dpcm[stream].state == SND_SOC_DPCM_STATE_STOP) 24878c2ecf20Sopenharmony_ci return 0; 24888c2ecf20Sopenharmony_ci 24898c2ecf20Sopenharmony_ci if (trigger == SND_SOC_DPCM_TRIGGER_BESPOKE) { 24908c2ecf20Sopenharmony_ci /* call trigger on the frontend - FE takes care of all BE triggers */ 24918c2ecf20Sopenharmony_ci dev_dbg(fe->dev, "ASoC: bespoke trigger FE %s cmd start\n", 24928c2ecf20Sopenharmony_ci fe->dai_link->name); 24938c2ecf20Sopenharmony_ci 24948c2ecf20Sopenharmony_ci ret = snd_soc_pcm_dai_bespoke_trigger(substream, SNDRV_PCM_TRIGGER_START); 24958c2ecf20Sopenharmony_ci if (ret < 0) { 24968c2ecf20Sopenharmony_ci dev_err(fe->dev,"ASoC: bespoke trigger FE failed %d\n", ret); 24978c2ecf20Sopenharmony_ci goto hw_free; 24988c2ecf20Sopenharmony_ci } 24998c2ecf20Sopenharmony_ci } else { 25008c2ecf20Sopenharmony_ci dev_dbg(fe->dev, "ASoC: trigger FE %s cmd start\n", 25018c2ecf20Sopenharmony_ci fe->dai_link->name); 25028c2ecf20Sopenharmony_ci 25038c2ecf20Sopenharmony_ci ret = dpcm_be_dai_trigger(fe, stream, 25048c2ecf20Sopenharmony_ci SNDRV_PCM_TRIGGER_START); 25058c2ecf20Sopenharmony_ci if (ret < 0) { 25068c2ecf20Sopenharmony_ci dev_err(fe->dev,"ASoC: trigger FE failed %d\n", ret); 25078c2ecf20Sopenharmony_ci goto hw_free; 25088c2ecf20Sopenharmony_ci } 25098c2ecf20Sopenharmony_ci } 25108c2ecf20Sopenharmony_ci 25118c2ecf20Sopenharmony_ci return 0; 25128c2ecf20Sopenharmony_ci 25138c2ecf20Sopenharmony_cihw_free: 25148c2ecf20Sopenharmony_ci dpcm_be_dai_hw_free(fe, stream); 25158c2ecf20Sopenharmony_ciclose: 25168c2ecf20Sopenharmony_ci dpcm_be_dai_shutdown(fe, stream); 25178c2ecf20Sopenharmony_cidisconnect: 25188c2ecf20Sopenharmony_ci /* disconnect any closed BEs */ 25198c2ecf20Sopenharmony_ci spin_lock_irqsave(&fe->card->dpcm_lock, flags); 25208c2ecf20Sopenharmony_ci for_each_dpcm_be(fe, stream, dpcm) { 25218c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *be = dpcm->be; 25228c2ecf20Sopenharmony_ci if (be->dpcm[stream].state == SND_SOC_DPCM_STATE_CLOSE) 25238c2ecf20Sopenharmony_ci dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; 25248c2ecf20Sopenharmony_ci } 25258c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fe->card->dpcm_lock, flags); 25268c2ecf20Sopenharmony_ci 25278c2ecf20Sopenharmony_ci return ret; 25288c2ecf20Sopenharmony_ci} 25298c2ecf20Sopenharmony_ci 25308c2ecf20Sopenharmony_cistatic int soc_dpcm_fe_runtime_update(struct snd_soc_pcm_runtime *fe, int new) 25318c2ecf20Sopenharmony_ci{ 25328c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget_list *list; 25338c2ecf20Sopenharmony_ci int stream; 25348c2ecf20Sopenharmony_ci int count, paths; 25358c2ecf20Sopenharmony_ci int ret; 25368c2ecf20Sopenharmony_ci 25378c2ecf20Sopenharmony_ci if (!fe->dai_link->dynamic) 25388c2ecf20Sopenharmony_ci return 0; 25398c2ecf20Sopenharmony_ci 25408c2ecf20Sopenharmony_ci if (fe->num_cpus > 1) { 25418c2ecf20Sopenharmony_ci dev_err(fe->dev, 25428c2ecf20Sopenharmony_ci "%s doesn't support Multi CPU yet\n", __func__); 25438c2ecf20Sopenharmony_ci return -EINVAL; 25448c2ecf20Sopenharmony_ci } 25458c2ecf20Sopenharmony_ci 25468c2ecf20Sopenharmony_ci /* only check active links */ 25478c2ecf20Sopenharmony_ci if (!snd_soc_dai_active(asoc_rtd_to_cpu(fe, 0))) 25488c2ecf20Sopenharmony_ci return 0; 25498c2ecf20Sopenharmony_ci 25508c2ecf20Sopenharmony_ci /* DAPM sync will call this to update DSP paths */ 25518c2ecf20Sopenharmony_ci dev_dbg(fe->dev, "ASoC: DPCM %s runtime update for FE %s\n", 25528c2ecf20Sopenharmony_ci new ? "new" : "old", fe->dai_link->name); 25538c2ecf20Sopenharmony_ci 25548c2ecf20Sopenharmony_ci for_each_pcm_streams(stream) { 25558c2ecf20Sopenharmony_ci 25568c2ecf20Sopenharmony_ci /* skip if FE doesn't have playback/capture capability */ 25578c2ecf20Sopenharmony_ci if (!snd_soc_dai_stream_valid(asoc_rtd_to_cpu(fe, 0), stream) || 25588c2ecf20Sopenharmony_ci !snd_soc_dai_stream_valid(asoc_rtd_to_codec(fe, 0), stream)) 25598c2ecf20Sopenharmony_ci continue; 25608c2ecf20Sopenharmony_ci 25618c2ecf20Sopenharmony_ci /* skip if FE isn't currently playing/capturing */ 25628c2ecf20Sopenharmony_ci if (!snd_soc_dai_stream_active(asoc_rtd_to_cpu(fe, 0), stream) || 25638c2ecf20Sopenharmony_ci !snd_soc_dai_stream_active(asoc_rtd_to_codec(fe, 0), stream)) 25648c2ecf20Sopenharmony_ci continue; 25658c2ecf20Sopenharmony_ci 25668c2ecf20Sopenharmony_ci paths = dpcm_path_get(fe, stream, &list); 25678c2ecf20Sopenharmony_ci if (paths < 0) { 25688c2ecf20Sopenharmony_ci dev_warn(fe->dev, "ASoC: %s no valid %s path\n", 25698c2ecf20Sopenharmony_ci fe->dai_link->name, 25708c2ecf20Sopenharmony_ci stream == SNDRV_PCM_STREAM_PLAYBACK ? 25718c2ecf20Sopenharmony_ci "playback" : "capture"); 25728c2ecf20Sopenharmony_ci return paths; 25738c2ecf20Sopenharmony_ci } 25748c2ecf20Sopenharmony_ci 25758c2ecf20Sopenharmony_ci /* update any playback/capture paths */ 25768c2ecf20Sopenharmony_ci count = dpcm_process_paths(fe, stream, &list, new); 25778c2ecf20Sopenharmony_ci if (count) { 25788c2ecf20Sopenharmony_ci dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_BE); 25798c2ecf20Sopenharmony_ci if (new) 25808c2ecf20Sopenharmony_ci ret = dpcm_run_update_startup(fe, stream); 25818c2ecf20Sopenharmony_ci else 25828c2ecf20Sopenharmony_ci ret = dpcm_run_update_shutdown(fe, stream); 25838c2ecf20Sopenharmony_ci if (ret < 0) 25848c2ecf20Sopenharmony_ci dev_err(fe->dev, "ASoC: failed to shutdown some BEs\n"); 25858c2ecf20Sopenharmony_ci dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO); 25868c2ecf20Sopenharmony_ci 25878c2ecf20Sopenharmony_ci dpcm_clear_pending_state(fe, stream); 25888c2ecf20Sopenharmony_ci dpcm_be_disconnect(fe, stream); 25898c2ecf20Sopenharmony_ci } 25908c2ecf20Sopenharmony_ci 25918c2ecf20Sopenharmony_ci dpcm_path_put(&list); 25928c2ecf20Sopenharmony_ci } 25938c2ecf20Sopenharmony_ci 25948c2ecf20Sopenharmony_ci return 0; 25958c2ecf20Sopenharmony_ci} 25968c2ecf20Sopenharmony_ci 25978c2ecf20Sopenharmony_ci/* Called by DAPM mixer/mux changes to update audio routing between PCMs and 25988c2ecf20Sopenharmony_ci * any DAI links. 25998c2ecf20Sopenharmony_ci */ 26008c2ecf20Sopenharmony_ciint snd_soc_dpcm_runtime_update(struct snd_soc_card *card) 26018c2ecf20Sopenharmony_ci{ 26028c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *fe; 26038c2ecf20Sopenharmony_ci int ret = 0; 26048c2ecf20Sopenharmony_ci 26058c2ecf20Sopenharmony_ci mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_RUNTIME); 26068c2ecf20Sopenharmony_ci /* shutdown all old paths first */ 26078c2ecf20Sopenharmony_ci for_each_card_rtds(card, fe) { 26088c2ecf20Sopenharmony_ci ret = soc_dpcm_fe_runtime_update(fe, 0); 26098c2ecf20Sopenharmony_ci if (ret) 26108c2ecf20Sopenharmony_ci goto out; 26118c2ecf20Sopenharmony_ci } 26128c2ecf20Sopenharmony_ci 26138c2ecf20Sopenharmony_ci /* bring new paths up */ 26148c2ecf20Sopenharmony_ci for_each_card_rtds(card, fe) { 26158c2ecf20Sopenharmony_ci ret = soc_dpcm_fe_runtime_update(fe, 1); 26168c2ecf20Sopenharmony_ci if (ret) 26178c2ecf20Sopenharmony_ci goto out; 26188c2ecf20Sopenharmony_ci } 26198c2ecf20Sopenharmony_ci 26208c2ecf20Sopenharmony_ciout: 26218c2ecf20Sopenharmony_ci mutex_unlock(&card->mutex); 26228c2ecf20Sopenharmony_ci return ret; 26238c2ecf20Sopenharmony_ci} 26248c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dpcm_runtime_update); 26258c2ecf20Sopenharmony_ci 26268c2ecf20Sopenharmony_cistatic void dpcm_fe_dai_cleanup(struct snd_pcm_substream *fe_substream) 26278c2ecf20Sopenharmony_ci{ 26288c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(fe_substream); 26298c2ecf20Sopenharmony_ci struct snd_soc_dpcm *dpcm; 26308c2ecf20Sopenharmony_ci int stream = fe_substream->stream; 26318c2ecf20Sopenharmony_ci 26328c2ecf20Sopenharmony_ci /* mark FE's links ready to prune */ 26338c2ecf20Sopenharmony_ci for_each_dpcm_be(fe, stream, dpcm) 26348c2ecf20Sopenharmony_ci dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; 26358c2ecf20Sopenharmony_ci 26368c2ecf20Sopenharmony_ci dpcm_be_disconnect(fe, stream); 26378c2ecf20Sopenharmony_ci 26388c2ecf20Sopenharmony_ci fe->dpcm[stream].runtime = NULL; 26398c2ecf20Sopenharmony_ci} 26408c2ecf20Sopenharmony_ci 26418c2ecf20Sopenharmony_cistatic int dpcm_fe_dai_close(struct snd_pcm_substream *fe_substream) 26428c2ecf20Sopenharmony_ci{ 26438c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(fe_substream); 26448c2ecf20Sopenharmony_ci int ret; 26458c2ecf20Sopenharmony_ci 26468c2ecf20Sopenharmony_ci mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); 26478c2ecf20Sopenharmony_ci ret = dpcm_fe_dai_shutdown(fe_substream); 26488c2ecf20Sopenharmony_ci 26498c2ecf20Sopenharmony_ci dpcm_fe_dai_cleanup(fe_substream); 26508c2ecf20Sopenharmony_ci 26518c2ecf20Sopenharmony_ci mutex_unlock(&fe->card->mutex); 26528c2ecf20Sopenharmony_ci return ret; 26538c2ecf20Sopenharmony_ci} 26548c2ecf20Sopenharmony_ci 26558c2ecf20Sopenharmony_cistatic int dpcm_fe_dai_open(struct snd_pcm_substream *fe_substream) 26568c2ecf20Sopenharmony_ci{ 26578c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(fe_substream); 26588c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget_list *list; 26598c2ecf20Sopenharmony_ci int ret; 26608c2ecf20Sopenharmony_ci int stream = fe_substream->stream; 26618c2ecf20Sopenharmony_ci 26628c2ecf20Sopenharmony_ci mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); 26638c2ecf20Sopenharmony_ci fe->dpcm[stream].runtime = fe_substream->runtime; 26648c2ecf20Sopenharmony_ci 26658c2ecf20Sopenharmony_ci ret = dpcm_path_get(fe, stream, &list); 26668c2ecf20Sopenharmony_ci if (ret < 0) { 26678c2ecf20Sopenharmony_ci goto open_end; 26688c2ecf20Sopenharmony_ci } else if (ret == 0) { 26698c2ecf20Sopenharmony_ci dev_dbg(fe->dev, "ASoC: %s no valid %s route\n", 26708c2ecf20Sopenharmony_ci fe->dai_link->name, stream ? "capture" : "playback"); 26718c2ecf20Sopenharmony_ci } 26728c2ecf20Sopenharmony_ci 26738c2ecf20Sopenharmony_ci /* calculate valid and active FE <-> BE dpcms */ 26748c2ecf20Sopenharmony_ci dpcm_process_paths(fe, stream, &list, 1); 26758c2ecf20Sopenharmony_ci 26768c2ecf20Sopenharmony_ci ret = dpcm_fe_dai_startup(fe_substream); 26778c2ecf20Sopenharmony_ci if (ret < 0) 26788c2ecf20Sopenharmony_ci dpcm_fe_dai_cleanup(fe_substream); 26798c2ecf20Sopenharmony_ci 26808c2ecf20Sopenharmony_ci dpcm_clear_pending_state(fe, stream); 26818c2ecf20Sopenharmony_ci dpcm_path_put(&list); 26828c2ecf20Sopenharmony_ciopen_end: 26838c2ecf20Sopenharmony_ci mutex_unlock(&fe->card->mutex); 26848c2ecf20Sopenharmony_ci return ret; 26858c2ecf20Sopenharmony_ci} 26868c2ecf20Sopenharmony_ci 26878c2ecf20Sopenharmony_ci/* create a new pcm */ 26888c2ecf20Sopenharmony_ciint soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) 26898c2ecf20Sopenharmony_ci{ 26908c2ecf20Sopenharmony_ci struct snd_soc_dai *codec_dai; 26918c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai; 26928c2ecf20Sopenharmony_ci struct snd_soc_component *component; 26938c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 26948c2ecf20Sopenharmony_ci char new_name[64]; 26958c2ecf20Sopenharmony_ci int ret = 0, playback = 0, capture = 0; 26968c2ecf20Sopenharmony_ci int stream; 26978c2ecf20Sopenharmony_ci int i; 26988c2ecf20Sopenharmony_ci 26998c2ecf20Sopenharmony_ci if (rtd->dai_link->dynamic && rtd->num_cpus > 1) { 27008c2ecf20Sopenharmony_ci dev_err(rtd->dev, 27018c2ecf20Sopenharmony_ci "DPCM doesn't support Multi CPU for Front-Ends yet\n"); 27028c2ecf20Sopenharmony_ci return -EINVAL; 27038c2ecf20Sopenharmony_ci } 27048c2ecf20Sopenharmony_ci 27058c2ecf20Sopenharmony_ci if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm) { 27068c2ecf20Sopenharmony_ci if (rtd->dai_link->dpcm_playback) { 27078c2ecf20Sopenharmony_ci stream = SNDRV_PCM_STREAM_PLAYBACK; 27088c2ecf20Sopenharmony_ci 27098c2ecf20Sopenharmony_ci for_each_rtd_cpu_dais(rtd, i, cpu_dai) { 27108c2ecf20Sopenharmony_ci if (snd_soc_dai_stream_valid(cpu_dai, stream)) { 27118c2ecf20Sopenharmony_ci playback = 1; 27128c2ecf20Sopenharmony_ci break; 27138c2ecf20Sopenharmony_ci } 27148c2ecf20Sopenharmony_ci } 27158c2ecf20Sopenharmony_ci 27168c2ecf20Sopenharmony_ci if (!playback) { 27178c2ecf20Sopenharmony_ci dev_err(rtd->card->dev, 27188c2ecf20Sopenharmony_ci "No CPU DAIs support playback for stream %s\n", 27198c2ecf20Sopenharmony_ci rtd->dai_link->stream_name); 27208c2ecf20Sopenharmony_ci return -EINVAL; 27218c2ecf20Sopenharmony_ci } 27228c2ecf20Sopenharmony_ci } 27238c2ecf20Sopenharmony_ci if (rtd->dai_link->dpcm_capture) { 27248c2ecf20Sopenharmony_ci stream = SNDRV_PCM_STREAM_CAPTURE; 27258c2ecf20Sopenharmony_ci 27268c2ecf20Sopenharmony_ci for_each_rtd_cpu_dais(rtd, i, cpu_dai) { 27278c2ecf20Sopenharmony_ci if (snd_soc_dai_stream_valid(cpu_dai, stream)) { 27288c2ecf20Sopenharmony_ci capture = 1; 27298c2ecf20Sopenharmony_ci break; 27308c2ecf20Sopenharmony_ci } 27318c2ecf20Sopenharmony_ci } 27328c2ecf20Sopenharmony_ci 27338c2ecf20Sopenharmony_ci if (!capture) { 27348c2ecf20Sopenharmony_ci dev_err(rtd->card->dev, 27358c2ecf20Sopenharmony_ci "No CPU DAIs support capture for stream %s\n", 27368c2ecf20Sopenharmony_ci rtd->dai_link->stream_name); 27378c2ecf20Sopenharmony_ci return -EINVAL; 27388c2ecf20Sopenharmony_ci } 27398c2ecf20Sopenharmony_ci } 27408c2ecf20Sopenharmony_ci } else { 27418c2ecf20Sopenharmony_ci /* Adapt stream for codec2codec links */ 27428c2ecf20Sopenharmony_ci int cpu_capture = rtd->dai_link->params ? 27438c2ecf20Sopenharmony_ci SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE; 27448c2ecf20Sopenharmony_ci int cpu_playback = rtd->dai_link->params ? 27458c2ecf20Sopenharmony_ci SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; 27468c2ecf20Sopenharmony_ci 27478c2ecf20Sopenharmony_ci for_each_rtd_codec_dais(rtd, i, codec_dai) { 27488c2ecf20Sopenharmony_ci if (rtd->num_cpus == 1) { 27498c2ecf20Sopenharmony_ci cpu_dai = asoc_rtd_to_cpu(rtd, 0); 27508c2ecf20Sopenharmony_ci } else if (rtd->num_cpus == rtd->num_codecs) { 27518c2ecf20Sopenharmony_ci cpu_dai = asoc_rtd_to_cpu(rtd, i); 27528c2ecf20Sopenharmony_ci } else { 27538c2ecf20Sopenharmony_ci dev_err(rtd->card->dev, 27548c2ecf20Sopenharmony_ci "N cpus to M codecs link is not supported yet\n"); 27558c2ecf20Sopenharmony_ci return -EINVAL; 27568c2ecf20Sopenharmony_ci } 27578c2ecf20Sopenharmony_ci 27588c2ecf20Sopenharmony_ci if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_PLAYBACK) && 27598c2ecf20Sopenharmony_ci snd_soc_dai_stream_valid(cpu_dai, cpu_playback)) 27608c2ecf20Sopenharmony_ci playback = 1; 27618c2ecf20Sopenharmony_ci if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_CAPTURE) && 27628c2ecf20Sopenharmony_ci snd_soc_dai_stream_valid(cpu_dai, cpu_capture)) 27638c2ecf20Sopenharmony_ci capture = 1; 27648c2ecf20Sopenharmony_ci } 27658c2ecf20Sopenharmony_ci } 27668c2ecf20Sopenharmony_ci 27678c2ecf20Sopenharmony_ci if (rtd->dai_link->playback_only) { 27688c2ecf20Sopenharmony_ci playback = 1; 27698c2ecf20Sopenharmony_ci capture = 0; 27708c2ecf20Sopenharmony_ci } 27718c2ecf20Sopenharmony_ci 27728c2ecf20Sopenharmony_ci if (rtd->dai_link->capture_only) { 27738c2ecf20Sopenharmony_ci playback = 0; 27748c2ecf20Sopenharmony_ci capture = 1; 27758c2ecf20Sopenharmony_ci } 27768c2ecf20Sopenharmony_ci 27778c2ecf20Sopenharmony_ci /* create the PCM */ 27788c2ecf20Sopenharmony_ci if (rtd->dai_link->params) { 27798c2ecf20Sopenharmony_ci snprintf(new_name, sizeof(new_name), "codec2codec(%s)", 27808c2ecf20Sopenharmony_ci rtd->dai_link->stream_name); 27818c2ecf20Sopenharmony_ci 27828c2ecf20Sopenharmony_ci ret = snd_pcm_new_internal(rtd->card->snd_card, new_name, num, 27838c2ecf20Sopenharmony_ci playback, capture, &pcm); 27848c2ecf20Sopenharmony_ci } else if (rtd->dai_link->no_pcm) { 27858c2ecf20Sopenharmony_ci snprintf(new_name, sizeof(new_name), "(%s)", 27868c2ecf20Sopenharmony_ci rtd->dai_link->stream_name); 27878c2ecf20Sopenharmony_ci 27888c2ecf20Sopenharmony_ci ret = snd_pcm_new_internal(rtd->card->snd_card, new_name, num, 27898c2ecf20Sopenharmony_ci playback, capture, &pcm); 27908c2ecf20Sopenharmony_ci } else { 27918c2ecf20Sopenharmony_ci if (rtd->dai_link->dynamic) 27928c2ecf20Sopenharmony_ci snprintf(new_name, sizeof(new_name), "%s (*)", 27938c2ecf20Sopenharmony_ci rtd->dai_link->stream_name); 27948c2ecf20Sopenharmony_ci else 27958c2ecf20Sopenharmony_ci snprintf(new_name, sizeof(new_name), "%s %s-%d", 27968c2ecf20Sopenharmony_ci rtd->dai_link->stream_name, 27978c2ecf20Sopenharmony_ci (rtd->num_codecs > 1) ? 27988c2ecf20Sopenharmony_ci "multicodec" : asoc_rtd_to_codec(rtd, 0)->name, num); 27998c2ecf20Sopenharmony_ci 28008c2ecf20Sopenharmony_ci ret = snd_pcm_new(rtd->card->snd_card, new_name, num, playback, 28018c2ecf20Sopenharmony_ci capture, &pcm); 28028c2ecf20Sopenharmony_ci } 28038c2ecf20Sopenharmony_ci if (ret < 0) { 28048c2ecf20Sopenharmony_ci dev_err(rtd->card->dev, "ASoC: can't create pcm %s for dailink %s: %d\n", 28058c2ecf20Sopenharmony_ci new_name, rtd->dai_link->name, ret); 28068c2ecf20Sopenharmony_ci return ret; 28078c2ecf20Sopenharmony_ci } 28088c2ecf20Sopenharmony_ci dev_dbg(rtd->card->dev, "ASoC: registered pcm #%d %s\n",num, new_name); 28098c2ecf20Sopenharmony_ci 28108c2ecf20Sopenharmony_ci /* DAPM dai link stream work */ 28118c2ecf20Sopenharmony_ci if (rtd->dai_link->params) 28128c2ecf20Sopenharmony_ci rtd->close_delayed_work_func = codec2codec_close_delayed_work; 28138c2ecf20Sopenharmony_ci else 28148c2ecf20Sopenharmony_ci rtd->close_delayed_work_func = snd_soc_close_delayed_work; 28158c2ecf20Sopenharmony_ci 28168c2ecf20Sopenharmony_ci pcm->nonatomic = rtd->dai_link->nonatomic; 28178c2ecf20Sopenharmony_ci rtd->pcm = pcm; 28188c2ecf20Sopenharmony_ci pcm->private_data = rtd; 28198c2ecf20Sopenharmony_ci 28208c2ecf20Sopenharmony_ci if (rtd->dai_link->no_pcm || rtd->dai_link->params) { 28218c2ecf20Sopenharmony_ci if (playback) 28228c2ecf20Sopenharmony_ci pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->private_data = rtd; 28238c2ecf20Sopenharmony_ci if (capture) 28248c2ecf20Sopenharmony_ci pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->private_data = rtd; 28258c2ecf20Sopenharmony_ci goto out; 28268c2ecf20Sopenharmony_ci } 28278c2ecf20Sopenharmony_ci 28288c2ecf20Sopenharmony_ci /* ASoC PCM operations */ 28298c2ecf20Sopenharmony_ci if (rtd->dai_link->dynamic) { 28308c2ecf20Sopenharmony_ci rtd->ops.open = dpcm_fe_dai_open; 28318c2ecf20Sopenharmony_ci rtd->ops.hw_params = dpcm_fe_dai_hw_params; 28328c2ecf20Sopenharmony_ci rtd->ops.prepare = dpcm_fe_dai_prepare; 28338c2ecf20Sopenharmony_ci rtd->ops.trigger = dpcm_fe_dai_trigger; 28348c2ecf20Sopenharmony_ci rtd->ops.hw_free = dpcm_fe_dai_hw_free; 28358c2ecf20Sopenharmony_ci rtd->ops.close = dpcm_fe_dai_close; 28368c2ecf20Sopenharmony_ci rtd->ops.pointer = soc_pcm_pointer; 28378c2ecf20Sopenharmony_ci } else { 28388c2ecf20Sopenharmony_ci rtd->ops.open = soc_pcm_open; 28398c2ecf20Sopenharmony_ci rtd->ops.hw_params = soc_pcm_hw_params; 28408c2ecf20Sopenharmony_ci rtd->ops.prepare = soc_pcm_prepare; 28418c2ecf20Sopenharmony_ci rtd->ops.trigger = soc_pcm_trigger; 28428c2ecf20Sopenharmony_ci rtd->ops.hw_free = soc_pcm_hw_free; 28438c2ecf20Sopenharmony_ci rtd->ops.close = soc_pcm_close; 28448c2ecf20Sopenharmony_ci rtd->ops.pointer = soc_pcm_pointer; 28458c2ecf20Sopenharmony_ci } 28468c2ecf20Sopenharmony_ci 28478c2ecf20Sopenharmony_ci for_each_rtd_components(rtd, i, component) { 28488c2ecf20Sopenharmony_ci const struct snd_soc_component_driver *drv = component->driver; 28498c2ecf20Sopenharmony_ci 28508c2ecf20Sopenharmony_ci if (drv->ioctl) 28518c2ecf20Sopenharmony_ci rtd->ops.ioctl = snd_soc_pcm_component_ioctl; 28528c2ecf20Sopenharmony_ci if (drv->sync_stop) 28538c2ecf20Sopenharmony_ci rtd->ops.sync_stop = snd_soc_pcm_component_sync_stop; 28548c2ecf20Sopenharmony_ci if (drv->copy_user) 28558c2ecf20Sopenharmony_ci rtd->ops.copy_user = snd_soc_pcm_component_copy_user; 28568c2ecf20Sopenharmony_ci if (drv->page) 28578c2ecf20Sopenharmony_ci rtd->ops.page = snd_soc_pcm_component_page; 28588c2ecf20Sopenharmony_ci if (drv->mmap) 28598c2ecf20Sopenharmony_ci rtd->ops.mmap = snd_soc_pcm_component_mmap; 28608c2ecf20Sopenharmony_ci } 28618c2ecf20Sopenharmony_ci 28628c2ecf20Sopenharmony_ci if (playback) 28638c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &rtd->ops); 28648c2ecf20Sopenharmony_ci 28658c2ecf20Sopenharmony_ci if (capture) 28668c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &rtd->ops); 28678c2ecf20Sopenharmony_ci 28688c2ecf20Sopenharmony_ci ret = snd_soc_pcm_component_new(rtd); 28698c2ecf20Sopenharmony_ci if (ret < 0) { 28708c2ecf20Sopenharmony_ci dev_err(rtd->dev, "ASoC: pcm %s constructor failed for dailink %s: %d\n", 28718c2ecf20Sopenharmony_ci new_name, rtd->dai_link->name, ret); 28728c2ecf20Sopenharmony_ci return ret; 28738c2ecf20Sopenharmony_ci } 28748c2ecf20Sopenharmony_ci 28758c2ecf20Sopenharmony_ci pcm->no_device_suspend = true; 28768c2ecf20Sopenharmony_ciout: 28778c2ecf20Sopenharmony_ci dev_dbg(rtd->card->dev, "%s <-> %s mapping ok\n", 28788c2ecf20Sopenharmony_ci (rtd->num_codecs > 1) ? "multicodec" : asoc_rtd_to_codec(rtd, 0)->name, 28798c2ecf20Sopenharmony_ci (rtd->num_cpus > 1) ? "multicpu" : asoc_rtd_to_cpu(rtd, 0)->name); 28808c2ecf20Sopenharmony_ci return ret; 28818c2ecf20Sopenharmony_ci} 28828c2ecf20Sopenharmony_ci 28838c2ecf20Sopenharmony_ci/* is the current PCM operation for this FE ? */ 28848c2ecf20Sopenharmony_ciint snd_soc_dpcm_fe_can_update(struct snd_soc_pcm_runtime *fe, int stream) 28858c2ecf20Sopenharmony_ci{ 28868c2ecf20Sopenharmony_ci if (fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_FE) 28878c2ecf20Sopenharmony_ci return 1; 28888c2ecf20Sopenharmony_ci return 0; 28898c2ecf20Sopenharmony_ci} 28908c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dpcm_fe_can_update); 28918c2ecf20Sopenharmony_ci 28928c2ecf20Sopenharmony_ci/* is the current PCM operation for this BE ? */ 28938c2ecf20Sopenharmony_ciint snd_soc_dpcm_be_can_update(struct snd_soc_pcm_runtime *fe, 28948c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *be, int stream) 28958c2ecf20Sopenharmony_ci{ 28968c2ecf20Sopenharmony_ci if ((fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_FE) || 28978c2ecf20Sopenharmony_ci ((fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_BE) && 28988c2ecf20Sopenharmony_ci be->dpcm[stream].runtime_update)) 28998c2ecf20Sopenharmony_ci return 1; 29008c2ecf20Sopenharmony_ci return 0; 29018c2ecf20Sopenharmony_ci} 29028c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dpcm_be_can_update); 29038c2ecf20Sopenharmony_ci 29048c2ecf20Sopenharmony_ci/* get the substream for this BE */ 29058c2ecf20Sopenharmony_cistruct snd_pcm_substream * 29068c2ecf20Sopenharmony_ci snd_soc_dpcm_get_substream(struct snd_soc_pcm_runtime *be, int stream) 29078c2ecf20Sopenharmony_ci{ 29088c2ecf20Sopenharmony_ci return be->pcm->streams[stream].substream; 29098c2ecf20Sopenharmony_ci} 29108c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dpcm_get_substream); 29118c2ecf20Sopenharmony_ci 29128c2ecf20Sopenharmony_cistatic int snd_soc_dpcm_check_state(struct snd_soc_pcm_runtime *fe, 29138c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *be, 29148c2ecf20Sopenharmony_ci int stream, 29158c2ecf20Sopenharmony_ci const enum snd_soc_dpcm_state *states, 29168c2ecf20Sopenharmony_ci int num_states) 29178c2ecf20Sopenharmony_ci{ 29188c2ecf20Sopenharmony_ci struct snd_soc_dpcm *dpcm; 29198c2ecf20Sopenharmony_ci int state; 29208c2ecf20Sopenharmony_ci int ret = 1; 29218c2ecf20Sopenharmony_ci unsigned long flags; 29228c2ecf20Sopenharmony_ci int i; 29238c2ecf20Sopenharmony_ci 29248c2ecf20Sopenharmony_ci spin_lock_irqsave(&fe->card->dpcm_lock, flags); 29258c2ecf20Sopenharmony_ci for_each_dpcm_fe(be, stream, dpcm) { 29268c2ecf20Sopenharmony_ci 29278c2ecf20Sopenharmony_ci if (dpcm->fe == fe) 29288c2ecf20Sopenharmony_ci continue; 29298c2ecf20Sopenharmony_ci 29308c2ecf20Sopenharmony_ci state = dpcm->fe->dpcm[stream].state; 29318c2ecf20Sopenharmony_ci for (i = 0; i < num_states; i++) { 29328c2ecf20Sopenharmony_ci if (state == states[i]) { 29338c2ecf20Sopenharmony_ci ret = 0; 29348c2ecf20Sopenharmony_ci break; 29358c2ecf20Sopenharmony_ci } 29368c2ecf20Sopenharmony_ci } 29378c2ecf20Sopenharmony_ci } 29388c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fe->card->dpcm_lock, flags); 29398c2ecf20Sopenharmony_ci 29408c2ecf20Sopenharmony_ci /* it's safe to do this BE DAI */ 29418c2ecf20Sopenharmony_ci return ret; 29428c2ecf20Sopenharmony_ci} 29438c2ecf20Sopenharmony_ci 29448c2ecf20Sopenharmony_ci/* 29458c2ecf20Sopenharmony_ci * We can only hw_free, stop, pause or suspend a BE DAI if any of it's FE 29468c2ecf20Sopenharmony_ci * are not running, paused or suspended for the specified stream direction. 29478c2ecf20Sopenharmony_ci */ 29488c2ecf20Sopenharmony_ciint snd_soc_dpcm_can_be_free_stop(struct snd_soc_pcm_runtime *fe, 29498c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *be, int stream) 29508c2ecf20Sopenharmony_ci{ 29518c2ecf20Sopenharmony_ci const enum snd_soc_dpcm_state state[] = { 29528c2ecf20Sopenharmony_ci SND_SOC_DPCM_STATE_START, 29538c2ecf20Sopenharmony_ci SND_SOC_DPCM_STATE_PAUSED, 29548c2ecf20Sopenharmony_ci SND_SOC_DPCM_STATE_SUSPEND, 29558c2ecf20Sopenharmony_ci }; 29568c2ecf20Sopenharmony_ci 29578c2ecf20Sopenharmony_ci return snd_soc_dpcm_check_state(fe, be, stream, state, ARRAY_SIZE(state)); 29588c2ecf20Sopenharmony_ci} 29598c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dpcm_can_be_free_stop); 29608c2ecf20Sopenharmony_ci 29618c2ecf20Sopenharmony_ci/* 29628c2ecf20Sopenharmony_ci * We can only change hw params a BE DAI if any of it's FE are not prepared, 29638c2ecf20Sopenharmony_ci * running, paused or suspended for the specified stream direction. 29648c2ecf20Sopenharmony_ci */ 29658c2ecf20Sopenharmony_ciint snd_soc_dpcm_can_be_params(struct snd_soc_pcm_runtime *fe, 29668c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *be, int stream) 29678c2ecf20Sopenharmony_ci{ 29688c2ecf20Sopenharmony_ci const enum snd_soc_dpcm_state state[] = { 29698c2ecf20Sopenharmony_ci SND_SOC_DPCM_STATE_START, 29708c2ecf20Sopenharmony_ci SND_SOC_DPCM_STATE_PAUSED, 29718c2ecf20Sopenharmony_ci SND_SOC_DPCM_STATE_SUSPEND, 29728c2ecf20Sopenharmony_ci SND_SOC_DPCM_STATE_PREPARE, 29738c2ecf20Sopenharmony_ci }; 29748c2ecf20Sopenharmony_ci 29758c2ecf20Sopenharmony_ci return snd_soc_dpcm_check_state(fe, be, stream, state, ARRAY_SIZE(state)); 29768c2ecf20Sopenharmony_ci} 29778c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dpcm_can_be_params); 29788c2ecf20Sopenharmony_ci 29798c2ecf20Sopenharmony_ci/* 29808c2ecf20Sopenharmony_ci * We can only prepare a BE DAI if any of it's FE are not prepared, 29818c2ecf20Sopenharmony_ci * running or paused for the specified stream direction. 29828c2ecf20Sopenharmony_ci */ 29838c2ecf20Sopenharmony_ciint snd_soc_dpcm_can_be_prepared(struct snd_soc_pcm_runtime *fe, 29848c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *be, int stream) 29858c2ecf20Sopenharmony_ci{ 29868c2ecf20Sopenharmony_ci const enum snd_soc_dpcm_state state[] = { 29878c2ecf20Sopenharmony_ci SND_SOC_DPCM_STATE_START, 29888c2ecf20Sopenharmony_ci SND_SOC_DPCM_STATE_PAUSED, 29898c2ecf20Sopenharmony_ci SND_SOC_DPCM_STATE_PREPARE, 29908c2ecf20Sopenharmony_ci }; 29918c2ecf20Sopenharmony_ci 29928c2ecf20Sopenharmony_ci return snd_soc_dpcm_check_state(fe, be, stream, state, ARRAY_SIZE(state)); 29938c2ecf20Sopenharmony_ci} 29948c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dpcm_can_be_prepared); 2995