xref: /kernel/linux/linux-5.10/sound/soc/soc-pcm.c (revision 8c2ecf20)
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