File Index
Files and directories
Sign in
FILE

sdw.c

File preview with syntax highlighting.

linux-6.19.14/sound/soc/qcom/sdw.c C
  1// SPDX-License-Identifier: GPL-2.0
  2// Copyright (c) 2018-2023, Linaro Limited.
  3// Copyright (c) 2018, The Linux Foundation. All rights reserved.
  4
  5#include <dt-bindings/sound/qcom,lpass.h>
  6#include <dt-bindings/sound/qcom,q6afe.h>
  7#include <linux/module.h>
  8#include <sound/soc.h>
  9#include "sdw.h"
 10
 11static bool qcom_snd_is_sdw_dai(int id)
 12{
 13	switch (id) {
 14	case WSA_CODEC_DMA_RX_0:
 15	case WSA_CODEC_DMA_TX_0:
 16	case WSA_CODEC_DMA_RX_1:
 17	case WSA_CODEC_DMA_TX_1:
 18	case WSA_CODEC_DMA_TX_2:
 19	case RX_CODEC_DMA_RX_0:
 20	case TX_CODEC_DMA_TX_0:
 21	case RX_CODEC_DMA_RX_1:
 22	case TX_CODEC_DMA_TX_1:
 23	case RX_CODEC_DMA_RX_2:
 24	case TX_CODEC_DMA_TX_2:
 25	case RX_CODEC_DMA_RX_3:
 26	case TX_CODEC_DMA_TX_3:
 27	case RX_CODEC_DMA_RX_4:
 28	case TX_CODEC_DMA_TX_4:
 29	case RX_CODEC_DMA_RX_5:
 30	case TX_CODEC_DMA_TX_5:
 31	case RX_CODEC_DMA_RX_6:
 32	case RX_CODEC_DMA_RX_7:
 33	case SLIMBUS_0_RX...SLIMBUS_6_TX:
 34		return true;
 35	default:
 36		break;
 37	}
 38
 39	/* DSP Bypass usecase, cpu dai index overlaps with DSP dai ids,
 40	 * DO NOT MERGE into top switch case */
 41	switch (id) {
 42	case LPASS_CDC_DMA_TX3:
 43	case LPASS_CDC_DMA_RX0:
 44		return true;
 45	default:
 46		break;
 47	}
 48
 49	return false;
 50}
 51
 52/**
 53 * qcom_snd_sdw_startup() - Helper to start Soundwire stream for SoC audio card
 54 * @substream: The PCM substream from audio, as passed to snd_soc_ops->startup()
 55 *
 56 * Helper for the SoC audio card (snd_soc_ops->startup()) to allocate and set
 57 * Soundwire stream runtime to each codec DAI.
 58 *
 59 * The shutdown() callback should call sdw_release_stream() on the same
 60 * sdw_stream_runtime.
 61 *
 62 * Return: 0 or errno
 63 */
 64int qcom_snd_sdw_startup(struct snd_pcm_substream *substream)
 65{
 66	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
 67	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
 68	u32 rx_ch[SDW_MAX_PORTS], tx_ch[SDW_MAX_PORTS];
 69	struct sdw_stream_runtime *sruntime;
 70	struct snd_soc_dai *codec_dai;
 71	u32 rx_ch_cnt = 0, tx_ch_cnt = 0;
 72	int ret, i, j;
 73
 74	if (!qcom_snd_is_sdw_dai(cpu_dai->id))
 75		return 0;
 76
 77	sruntime = sdw_alloc_stream(cpu_dai->name, SDW_STREAM_PCM);
 78	if (!sruntime)
 79		return -ENOMEM;
 80
 81	for_each_rtd_codec_dais(rtd, i, codec_dai) {
 82		ret = snd_soc_dai_set_stream(codec_dai, sruntime,
 83					     substream->stream);
 84		if (ret < 0 && ret != -ENOTSUPP) {
 85			dev_err(rtd->dev, "Failed to set sdw stream on %s\n", codec_dai->name);
 86			goto err_set_stream;
 87		} else if (ret == -ENOTSUPP) {
 88			/* Ignore unsupported */
 89			continue;
 90		}
 91
 92		ret = snd_soc_dai_get_channel_map(codec_dai, &tx_ch_cnt, tx_ch,
 93						  &rx_ch_cnt, rx_ch);
 94		if (ret != 0 && ret != -ENOTSUPP) {
 95			dev_err(rtd->dev, "Failed to get codec chan map %s\n", codec_dai->name);
 96			goto err_set_stream;
 97		} else if (ret == -ENOTSUPP) {
 98			/* Ignore unsupported */
 99			continue;
100		}
101	}
102
103	switch (cpu_dai->id) {
104	case RX_CODEC_DMA_RX_0:
105	case TX_CODEC_DMA_TX_3:
106		if (tx_ch_cnt || rx_ch_cnt) {
107			for_each_rtd_codec_dais(rtd, j, codec_dai) {
108				ret = snd_soc_dai_set_channel_map(codec_dai,
109								  tx_ch_cnt, tx_ch,
110								  rx_ch_cnt, rx_ch);
111				if (ret != 0 && ret != -ENOTSUPP)
112					goto err_set_stream;
113			}
114		}
115	}
116
117	return 0;
118
119err_set_stream:
120	sdw_release_stream(sruntime);
121
122	return ret;
123}
124EXPORT_SYMBOL_GPL(qcom_snd_sdw_startup);
125
126int qcom_snd_sdw_prepare(struct snd_pcm_substream *substream,
127			 bool *stream_prepared)
128{
129	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
130	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
131	struct sdw_stream_runtime *sruntime;
132	int ret;
133
134
135	if (!qcom_snd_is_sdw_dai(cpu_dai->id))
136		return 0;
137
138	sruntime = qcom_snd_sdw_get_stream(substream);
139	if (!sruntime)
140		return 0;
141
142	if (*stream_prepared)
143		return 0;
144
145	ret = sdw_prepare_stream(sruntime);
146	if (ret)
147		return ret;
148
149	/**
150	 * NOTE: there is a strict hw requirement about the ordering of port
151	 * enables and actual WSA881x PA enable. PA enable should only happen
152	 * after soundwire ports are enabled if not DC on the line is
153	 * accumulated resulting in Click/Pop Noise
154	 * PA enable/mute are handled as part of codec DAPM and digital mute.
155	 */
156
157	ret = sdw_enable_stream(sruntime);
158	if (ret) {
159		sdw_deprepare_stream(sruntime);
160		return ret;
161	}
162	*stream_prepared  = true;
163
164	return ret;
165}
166EXPORT_SYMBOL_GPL(qcom_snd_sdw_prepare);
167
168struct sdw_stream_runtime *qcom_snd_sdw_get_stream(struct snd_pcm_substream *substream)
169{
170	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
171	struct snd_soc_dai *codec_dai;
172	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
173	struct sdw_stream_runtime *sruntime;
174	int i;
175
176	if (!qcom_snd_is_sdw_dai(cpu_dai->id))
177		return NULL;
178
179	for_each_rtd_codec_dais(rtd, i, codec_dai) {
180		sruntime = snd_soc_dai_get_stream(codec_dai, substream->stream);
181		if (sruntime != ERR_PTR(-ENOTSUPP))
182			return sruntime;
183	}
184	return NULL;
185}
186EXPORT_SYMBOL_GPL(qcom_snd_sdw_get_stream);
187
188void qcom_snd_sdw_shutdown(struct snd_pcm_substream *substream)
189{
190	struct sdw_stream_runtime *sruntime = qcom_snd_sdw_get_stream(substream);
191
192	sdw_release_stream(sruntime);
193}
194EXPORT_SYMBOL_GPL(qcom_snd_sdw_shutdown);
195
196int qcom_snd_sdw_hw_free(struct snd_pcm_substream *substream, bool *stream_prepared)
197{
198	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
199	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
200	struct sdw_stream_runtime *sruntime;
201
202	if (!qcom_snd_is_sdw_dai(cpu_dai->id))
203		return 0;
204
205	sruntime = qcom_snd_sdw_get_stream(substream);
206	if (sruntime && *stream_prepared) {
207		sdw_disable_stream(sruntime);
208		sdw_deprepare_stream(sruntime);
209		*stream_prepared = false;
210	}
211
212	return 0;
213}
214EXPORT_SYMBOL_GPL(qcom_snd_sdw_hw_free);
215MODULE_DESCRIPTION("Qualcomm ASoC SoundWire helper functions");
216MODULE_LICENSE("GPL");
~/ Read-only file preview.