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,q6afe.h>
6#include <linux/module.h>
7#include <sound/soc.h>
8#include "sdw.h"
9
10/**
11 * qcom_snd_sdw_startup() - Helper to start Soundwire stream for SoC audio card
12 * @substream: The PCM substream from audio, as passed to snd_soc_ops->startup()
13 *
14 * Helper for the SoC audio card (snd_soc_ops->startup()) to allocate and set
15 * Soundwire stream runtime to each codec DAI.
16 *
17 * The shutdown() callback should call sdw_release_stream() on the same
18 * sdw_stream_runtime.
19 *
20 * Return: 0 or errno
21 */
22int qcom_snd_sdw_startup(struct snd_pcm_substream *substream)
23{
24	struct snd_soc_pcm_runtime *rtd = substream->private_data;
25	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
26	struct sdw_stream_runtime *sruntime;
27	struct snd_soc_dai *codec_dai;
28	int ret, i;
29
30	sruntime = sdw_alloc_stream(cpu_dai->name);
31	if (!sruntime)
32		return -ENOMEM;
33
34	for_each_rtd_codec_dais(rtd, i, codec_dai) {
35		ret = snd_soc_dai_set_stream(codec_dai, sruntime,
36					     substream->stream);
37		if (ret < 0 && ret != -ENOTSUPP) {
38			dev_err(rtd->dev, "Failed to set sdw stream on %s\n",
39				codec_dai->name);
40			goto err_set_stream;
41		}
42	}
43
44	return 0;
45
46err_set_stream:
47	sdw_release_stream(sruntime);
48
49	return ret;
50}
51EXPORT_SYMBOL_GPL(qcom_snd_sdw_startup);
52
53int qcom_snd_sdw_prepare(struct snd_pcm_substream *substream,
54			 struct sdw_stream_runtime *sruntime,
55			 bool *stream_prepared)
56{
57	struct snd_soc_pcm_runtime *rtd = substream->private_data;
58	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
59	int ret;
60
61	if (!sruntime)
62		return 0;
63
64	switch (cpu_dai->id) {
65	case WSA_CODEC_DMA_RX_0:
66	case WSA_CODEC_DMA_RX_1:
67	case RX_CODEC_DMA_RX_0:
68	case RX_CODEC_DMA_RX_1:
69	case TX_CODEC_DMA_TX_0:
70	case TX_CODEC_DMA_TX_1:
71	case TX_CODEC_DMA_TX_2:
72	case TX_CODEC_DMA_TX_3:
73		break;
74	default:
75		return 0;
76	}
77
78	if (*stream_prepared)
79		return 0;
80
81	ret = sdw_prepare_stream(sruntime);
82	if (ret)
83		return ret;
84
85	/**
86	 * NOTE: there is a strict hw requirement about the ordering of port
87	 * enables and actual WSA881x PA enable. PA enable should only happen
88	 * after soundwire ports are enabled if not DC on the line is
89	 * accumulated resulting in Click/Pop Noise
90	 * PA enable/mute are handled as part of codec DAPM and digital mute.
91	 */
92
93	ret = sdw_enable_stream(sruntime);
94	if (ret) {
95		sdw_deprepare_stream(sruntime);
96		return ret;
97	}
98	*stream_prepared  = true;
99
100	return ret;
101}
102EXPORT_SYMBOL_GPL(qcom_snd_sdw_prepare);
103
104int qcom_snd_sdw_hw_params(struct snd_pcm_substream *substream,
105			   struct snd_pcm_hw_params *params,
106			   struct sdw_stream_runtime **psruntime)
107{
108	struct snd_soc_pcm_runtime *rtd = substream->private_data;
109	struct snd_soc_dai *codec_dai;
110	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
111	struct sdw_stream_runtime *sruntime;
112	int i;
113
114	switch (cpu_dai->id) {
115	case WSA_CODEC_DMA_RX_0:
116	case RX_CODEC_DMA_RX_0:
117	case RX_CODEC_DMA_RX_1:
118	case TX_CODEC_DMA_TX_0:
119	case TX_CODEC_DMA_TX_1:
120	case TX_CODEC_DMA_TX_2:
121	case TX_CODEC_DMA_TX_3:
122		for_each_rtd_codec_dais(rtd, i, codec_dai) {
123			sruntime = snd_soc_dai_get_stream(codec_dai, substream->stream);
124			if (sruntime != ERR_PTR(-ENOTSUPP))
125				*psruntime = sruntime;
126		}
127		break;
128	}
129
130	return 0;
131
132}
133EXPORT_SYMBOL_GPL(qcom_snd_sdw_hw_params);
134
135int qcom_snd_sdw_hw_free(struct snd_pcm_substream *substream,
136			 struct sdw_stream_runtime *sruntime, bool *stream_prepared)
137{
138	struct snd_soc_pcm_runtime *rtd = substream->private_data;
139	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
140
141	switch (cpu_dai->id) {
142	case WSA_CODEC_DMA_RX_0:
143	case WSA_CODEC_DMA_RX_1:
144	case RX_CODEC_DMA_RX_0:
145	case RX_CODEC_DMA_RX_1:
146	case TX_CODEC_DMA_TX_0:
147	case TX_CODEC_DMA_TX_1:
148	case TX_CODEC_DMA_TX_2:
149	case TX_CODEC_DMA_TX_3:
150		if (sruntime && *stream_prepared) {
151			sdw_disable_stream(sruntime);
152			sdw_deprepare_stream(sruntime);
153			*stream_prepared = false;
154		}
155		break;
156	default:
157		break;
158	}
159
160	return 0;
161}
162EXPORT_SYMBOL_GPL(qcom_snd_sdw_hw_free);
163MODULE_LICENSE("GPL");
164