176612Stshiozak// SPDX-License-Identifier: GPL-2.0
276612Stshiozak// Copyright (c) 2018-2023, Linaro Limited.
376612Stshiozak// Copyright (c) 2018, The Linux Foundation. All rights reserved.
476612Stshiozak
576612Stshiozak#include <dt-bindings/sound/qcom,q6afe.h>
676612Stshiozak#include <linux/module.h>
776612Stshiozak#include <sound/soc.h>
876612Stshiozak#include "sdw.h"
976612Stshiozak
1076612Stshiozak/**
1176612Stshiozak * qcom_snd_sdw_startup() - Helper to start Soundwire stream for SoC audio card
1276612Stshiozak * @substream: The PCM substream from audio, as passed to snd_soc_ops->startup()
1376612Stshiozak *
1476612Stshiozak * Helper for the SoC audio card (snd_soc_ops->startup()) to allocate and set
1576612Stshiozak * Soundwire stream runtime to each codec DAI.
1676612Stshiozak *
1776612Stshiozak * The shutdown() callback should call sdw_release_stream() on the same
1876612Stshiozak * sdw_stream_runtime.
1976612Stshiozak *
2076612Stshiozak * Return: 0 or errno
2176612Stshiozak */
2276612Stshiozakint qcom_snd_sdw_startup(struct snd_pcm_substream *substream)
2376612Stshiozak{
2476612Stshiozak	struct snd_soc_pcm_runtime *rtd = substream->private_data;
2576612Stshiozak	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
2676612Stshiozak	struct sdw_stream_runtime *sruntime;
2776612Stshiozak	struct snd_soc_dai *codec_dai;
2876612Stshiozak	int ret, i;
2976612Stshiozak
3092986Sobrien	sruntime = sdw_alloc_stream(cpu_dai->name);
3176612Stshiozak	if (!sruntime)
3276612Stshiozak		return -ENOMEM;
3376612Stshiozak
3492986Sobrien	for_each_rtd_codec_dais(rtd, i, codec_dai) {
3586170Sobrien		ret = snd_soc_dai_set_stream(codec_dai, sruntime,
3676612Stshiozak					     substream->stream);
3776612Stshiozak		if (ret < 0 && ret != -ENOTSUPP) {
3876612Stshiozak			dev_err(rtd->dev, "Failed to set sdw stream on %s\n",
3976612Stshiozak				codec_dai->name);
4076612Stshiozak			goto err_set_stream;
41103059Stjr		}
42103059Stjr	}
4376612Stshiozak
44103998Stjr	return 0;
4576612Stshiozak
46103998Stjrerr_set_stream:
47103998Stjr	sdw_release_stream(sruntime);
48103998Stjr
4976612Stshiozak	return ret;
50103998Stjr}
5176612StshiozakEXPORT_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