File Index
Files and directories
Sign in
FILE

mt8365-dai-pcm.c

File preview with syntax highlighting.

linux-6.19.14/sound/soc/mediatek/mt8365/mt8365-dai-pcm.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * MediaTek 8365 ALSA SoC Audio DAI PCM Control
  4 *
  5 * Copyright (c) 2024 MediaTek Inc.
  6 * Authors: Jia Zeng <jia.zeng@mediatek.com>
  7 *          Alexandre Mergnat <amergnat@baylibre.com>
  8 */
  9
 10#include <linux/bitops.h>
 11#include <linux/regmap.h>
 12#include <sound/pcm_params.h>
 13#include "mt8365-afe-clk.h"
 14#include "mt8365-afe-common.h"
 15
 16struct mt8365_pcm_intf_data {
 17	bool slave_mode;
 18	bool lrck_inv;
 19	bool bck_inv;
 20	unsigned int format;
 21};
 22
 23/* DAI Drivers */
 24
 25static void mt8365_dai_enable_pcm1(struct mtk_base_afe *afe)
 26{
 27	regmap_update_bits(afe->regmap, PCM_INTF_CON1,
 28			   PCM_INTF_CON1_EN, PCM_INTF_CON1_EN);
 29}
 30
 31static void mt8365_dai_disable_pcm1(struct mtk_base_afe *afe)
 32{
 33	regmap_update_bits(afe->regmap, PCM_INTF_CON1,
 34			   PCM_INTF_CON1_EN, 0x0);
 35}
 36
 37static int mt8365_dai_configure_pcm1(struct snd_pcm_substream *substream,
 38				     struct snd_soc_dai *dai)
 39{
 40	struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
 41	struct mt8365_afe_private *afe_priv = afe->platform_priv;
 42	struct mt8365_pcm_intf_data *pcm_priv = afe_priv->dai_priv[MT8365_AFE_IO_PCM1];
 43	bool slave_mode = pcm_priv->slave_mode;
 44	bool lrck_inv = pcm_priv->lrck_inv;
 45	bool bck_inv = pcm_priv->bck_inv;
 46	unsigned int fmt = pcm_priv->format;
 47	unsigned int bit_width = dai->symmetric_sample_bits;
 48	unsigned int val = 0;
 49
 50	if (!slave_mode) {
 51		val |= PCM_INTF_CON1_MASTER_MODE |
 52		       PCM_INTF_CON1_BYPASS_ASRC;
 53
 54		if (lrck_inv)
 55			val |= PCM_INTF_CON1_SYNC_OUT_INV;
 56		if (bck_inv)
 57			val |= PCM_INTF_CON1_BCLK_OUT_INV;
 58	} else {
 59		val |= PCM_INTF_CON1_SLAVE_MODE;
 60
 61		if (lrck_inv)
 62			val |= PCM_INTF_CON1_SYNC_IN_INV;
 63		if (bck_inv)
 64			val |= PCM_INTF_CON1_BCLK_IN_INV;
 65
 66		/* TODO: add asrc setting */
 67	}
 68
 69	val |= FIELD_PREP(PCM_INTF_CON1_FORMAT_MASK, fmt);
 70
 71	if (fmt == MT8365_PCM_FORMAT_PCMA ||
 72	    fmt == MT8365_PCM_FORMAT_PCMB)
 73		val |= PCM_INTF_CON1_SYNC_LEN(1);
 74	else
 75		val |= PCM_INTF_CON1_SYNC_LEN(bit_width);
 76
 77	switch (substream->runtime->rate) {
 78	case 48000:
 79		val |= PCM_INTF_CON1_FS_48K;
 80		break;
 81	case 32000:
 82		val |= PCM_INTF_CON1_FS_32K;
 83		break;
 84	case 16000:
 85		val |= PCM_INTF_CON1_FS_16K;
 86		break;
 87	case 8000:
 88		val |= PCM_INTF_CON1_FS_8K;
 89		break;
 90	default:
 91		return -EINVAL;
 92	}
 93
 94	if (bit_width > 16)
 95		val |= PCM_INTF_CON1_24BIT | PCM_INTF_CON1_64BCK;
 96	else
 97		val |= PCM_INTF_CON1_16BIT | PCM_INTF_CON1_32BCK;
 98
 99	val |= PCM_INTF_CON1_EXT_MODEM;
100
101	regmap_update_bits(afe->regmap, PCM_INTF_CON1,
102			   PCM_INTF_CON1_CONFIG_MASK, val);
103
104	return 0;
105}
106
107static int mt8365_dai_pcm1_startup(struct snd_pcm_substream *substream,
108				   struct snd_soc_dai *dai)
109{
110	struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
111
112	if (snd_soc_dai_active(dai))
113		return 0;
114
115	mt8365_afe_enable_main_clk(afe);
116
117	return 0;
118}
119
120static void mt8365_dai_pcm1_shutdown(struct snd_pcm_substream *substream,
121				     struct snd_soc_dai *dai)
122{
123	struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
124
125	if (snd_soc_dai_active(dai))
126		return;
127
128	mt8365_dai_disable_pcm1(afe);
129	mt8365_afe_disable_main_clk(afe);
130}
131
132static int mt8365_dai_pcm1_prepare(struct snd_pcm_substream *substream,
133				   struct snd_soc_dai *dai)
134{
135	struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
136	int ret;
137
138	if ((snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_PLAYBACK) +
139	    snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_CAPTURE)) > 1) {
140		dev_info(afe->dev, "%s '%s' active(%u-%u) already\n",
141			 __func__, snd_pcm_stream_str(substream),
142			 snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_PLAYBACK),
143			 snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_CAPTURE));
144		return 0;
145	}
146
147	ret = mt8365_dai_configure_pcm1(substream, dai);
148	if (ret)
149		return ret;
150
151	mt8365_dai_enable_pcm1(afe);
152
153	return 0;
154}
155
156static int mt8365_dai_pcm1_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
157{
158	struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
159	struct mt8365_afe_private *afe_priv = afe->platform_priv;
160	struct mt8365_pcm_intf_data *pcm_priv = afe_priv->dai_priv[MT8365_AFE_IO_PCM1];
161
162	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
163	case SND_SOC_DAIFMT_I2S:
164		pcm_priv->format = MT8365_PCM_FORMAT_I2S;
165		break;
166	default:
167		return -EINVAL;
168	}
169
170	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
171	case SND_SOC_DAIFMT_NB_NF:
172		pcm_priv->bck_inv = false;
173		pcm_priv->lrck_inv = false;
174		break;
175	case SND_SOC_DAIFMT_NB_IF:
176		pcm_priv->bck_inv = false;
177		pcm_priv->lrck_inv = true;
178		break;
179	case SND_SOC_DAIFMT_IB_NF:
180		pcm_priv->bck_inv = true;
181		pcm_priv->lrck_inv = false;
182		break;
183	case SND_SOC_DAIFMT_IB_IF:
184		pcm_priv->bck_inv = true;
185		pcm_priv->lrck_inv = true;
186		break;
187	default:
188		return -EINVAL;
189	}
190
191	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
192	case SND_SOC_DAIFMT_CBP_CFP:
193		pcm_priv->slave_mode = true;
194		break;
195	case SND_SOC_DAIFMT_CBC_CFC:
196		pcm_priv->slave_mode = false;
197		break;
198	default:
199		return -EINVAL;
200	}
201
202	return 0;
203}
204
205static const struct snd_soc_dai_ops mt8365_dai_pcm1_ops = {
206	.startup	= mt8365_dai_pcm1_startup,
207	.shutdown	= mt8365_dai_pcm1_shutdown,
208	.prepare	= mt8365_dai_pcm1_prepare,
209	.set_fmt	= mt8365_dai_pcm1_set_fmt,
210};
211
212static struct snd_soc_dai_driver mtk_dai_pcm_driver[] = {
213	{
214		.name = "PCM1",
215		.id = MT8365_AFE_IO_PCM1,
216		.playback = {
217			.stream_name = "PCM1 Playback",
218			.channels_min = 1,
219			.channels_max = 2,
220			.rates = SNDRV_PCM_RATE_8000 |
221				 SNDRV_PCM_RATE_16000 |
222				 SNDRV_PCM_RATE_32000 |
223				 SNDRV_PCM_RATE_48000,
224			.formats = SNDRV_PCM_FMTBIT_S16_LE |
225				   SNDRV_PCM_FMTBIT_S32_LE,
226		},
227		.capture = {
228			.stream_name = "PCM1 Capture",
229			.channels_min = 1,
230			.channels_max = 2,
231			.rates = SNDRV_PCM_RATE_8000 |
232				 SNDRV_PCM_RATE_16000 |
233				 SNDRV_PCM_RATE_32000 |
234				 SNDRV_PCM_RATE_48000,
235			.formats = SNDRV_PCM_FMTBIT_S16_LE |
236				   SNDRV_PCM_FMTBIT_S32_LE,
237		},
238		.ops = &mt8365_dai_pcm1_ops,
239		.symmetric_rate = 1,
240		.symmetric_sample_bits = 1,
241	}
242};
243
244/* DAI widget */
245
246static const struct snd_soc_dapm_widget mtk_dai_pcm_widgets[] = {
247	SND_SOC_DAPM_OUTPUT("PCM1 Out"),
248	SND_SOC_DAPM_INPUT("PCM1 In"),
249};
250
251/* DAI route */
252
253static const struct snd_soc_dapm_route mtk_dai_pcm_routes[] = {
254	{"PCM1 Playback", NULL, "O07"},
255	{"PCM1 Playback", NULL, "O08"},
256	{"PCM1 Out", NULL, "PCM1 Playback"},
257
258	{"I09", NULL, "PCM1 Capture"},
259	{"I22", NULL, "PCM1 Capture"},
260	{"PCM1 Capture", NULL, "PCM1 In"},
261};
262
263static int init_pcmif_priv_data(struct mtk_base_afe *afe)
264{
265	struct mt8365_afe_private *afe_priv = afe->platform_priv;
266	struct mt8365_pcm_intf_data *pcmif_priv;
267
268	pcmif_priv = devm_kzalloc(afe->dev, sizeof(struct mt8365_pcm_intf_data),
269				  GFP_KERNEL);
270	if (!pcmif_priv)
271		return -ENOMEM;
272
273	afe_priv->dai_priv[MT8365_AFE_IO_PCM1] = pcmif_priv;
274	return 0;
275}
276
277int mt8365_dai_pcm_register(struct mtk_base_afe *afe)
278{
279	struct mtk_base_afe_dai *dai;
280
281	dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
282	if (!dai)
283		return -ENOMEM;
284
285	list_add(&dai->list, &afe->sub_dais);
286	dai->dai_drivers = mtk_dai_pcm_driver;
287	dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_pcm_driver);
288	dai->dapm_widgets = mtk_dai_pcm_widgets;
289	dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_pcm_widgets);
290	dai->dapm_routes = mtk_dai_pcm_routes;
291	dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_pcm_routes);
292	return init_pcmif_priv_data(afe);
293}
~/ Read-only file preview.