1// SPDX-License-Identifier: GPL-2.0
2//
3// MediaTek ALSA SoC Audio DAI I2S Control
4//
5// Copyright (c) 2022 MediaTek Inc.
6// Author: Jiaxin Yu <jiaxin.yu@mediatek.com>
7
8#include <linux/regmap.h>
9#include <sound/pcm_params.h>
10#include "mt8186-afe-common.h"
11#include "mt8186-afe-gpio.h"
12#include "mt8186-interconnection.h"
13
14struct mtk_afe_pcm_priv {
15	unsigned int id;
16	unsigned int fmt;
17	unsigned int bck_invert;
18	unsigned int lck_invert;
19};
20
21enum aud_tx_lch_rpt {
22	AUD_TX_LCH_RPT_NO_REPEAT = 0,
23	AUD_TX_LCH_RPT_REPEAT = 1
24};
25
26enum aud_vbt_16k_mode {
27	AUD_VBT_16K_MODE_DISABLE = 0,
28	AUD_VBT_16K_MODE_ENABLE = 1
29};
30
31enum aud_ext_modem {
32	AUD_EXT_MODEM_SELECT_INTERNAL = 0,
33	AUD_EXT_MODEM_SELECT_EXTERNAL = 1
34};
35
36enum aud_pcm_sync_type {
37	/* bck sync length = 1 */
38	AUD_PCM_ONE_BCK_CYCLE_SYNC = 0,
39	/* bck sync length = PCM_INTF_CON1[9:13] */
40	AUD_PCM_EXTENDED_BCK_CYCLE_SYNC = 1
41};
42
43enum aud_bt_mode {
44	AUD_BT_MODE_DUAL_MIC_ON_TX = 0,
45	AUD_BT_MODE_SINGLE_MIC_ON_TX = 1
46};
47
48enum aud_pcm_afifo_src {
49	/* slave mode & external modem uses different crystal */
50	AUD_PCM_AFIFO_ASRC = 0,
51	/* slave mode & external modem uses the same crystal */
52	AUD_PCM_AFIFO_AFIFO = 1
53};
54
55enum aud_pcm_clock_source {
56	AUD_PCM_CLOCK_MASTER_MODE = 0,
57	AUD_PCM_CLOCK_SLAVE_MODE = 1
58};
59
60enum aud_pcm_wlen {
61	AUD_PCM_WLEN_PCM_32_BCK_CYCLES = 0,
62	AUD_PCM_WLEN_PCM_64_BCK_CYCLES = 1
63};
64
65enum aud_pcm_24bit {
66	AUD_PCM_24BIT_PCM_16_BITS = 0,
67	AUD_PCM_24BIT_PCM_24_BITS = 1
68};
69
70enum aud_pcm_mode {
71	AUD_PCM_MODE_PCM_MODE_8K = 0,
72	AUD_PCM_MODE_PCM_MODE_16K = 1,
73	AUD_PCM_MODE_PCM_MODE_32K = 2,
74	AUD_PCM_MODE_PCM_MODE_48K = 3,
75};
76
77enum aud_pcm_fmt {
78	AUD_PCM_FMT_I2S = 0,
79	AUD_PCM_FMT_EIAJ = 1,
80	AUD_PCM_FMT_PCM_MODE_A = 2,
81	AUD_PCM_FMT_PCM_MODE_B = 3
82};
83
84enum aud_bclk_out_inv {
85	AUD_BCLK_OUT_INV_NO_INVERSE = 0,
86	AUD_BCLK_OUT_INV_INVERSE = 1
87};
88
89enum aud_lrclk_out_inv {
90	AUD_LRCLK_OUT_INV_NO_INVERSE = 0,
91	AUD_LRCLK_OUT_INV_INVERSE = 1
92};
93
94enum aud_pcm_en {
95	AUD_PCM_EN_DISABLE = 0,
96	AUD_PCM_EN_ENABLE = 1
97};
98
99/* dai component */
100static const struct snd_kcontrol_new mtk_pcm_1_playback_ch1_mix[] = {
101	SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1 Switch", AFE_CONN7,
102				    I_ADDA_UL_CH1, 1, 0),
103	SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1 Switch", AFE_CONN7,
104				    I_DL2_CH1, 1, 0),
105	SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH1 Switch", AFE_CONN7_1,
106				    I_DL4_CH1, 1, 0),
107};
108
109static const struct snd_kcontrol_new mtk_pcm_1_playback_ch2_mix[] = {
110	SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2 Switch", AFE_CONN8,
111				    I_ADDA_UL_CH2, 1, 0),
112	SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2 Switch", AFE_CONN8,
113				    I_DL2_CH2, 1, 0),
114	SOC_DAPM_SINGLE_AUTODISABLE("DL4_CH2 Switch", AFE_CONN8_1,
115				    I_DL4_CH2, 1, 0),
116};
117
118static int mtk_pcm_en_event(struct snd_soc_dapm_widget *w,
119			    struct snd_kcontrol *kcontrol,
120			    int event)
121{
122	struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
123	struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
124
125	dev_dbg(afe->dev, "%s(), name %s, event 0x%x\n",
126		__func__, w->name, event);
127
128	switch (event) {
129	case SND_SOC_DAPM_PRE_PMU:
130		mt8186_afe_gpio_request(afe->dev, true, MT8186_DAI_PCM, 0);
131		break;
132	case SND_SOC_DAPM_POST_PMD:
133		mt8186_afe_gpio_request(afe->dev, false, MT8186_DAI_PCM, 0);
134		break;
135	}
136
137	return 0;
138}
139
140/* pcm in/out lpbk */
141static const char * const pcm_lpbk_mux_map[] = {
142	"Normal", "Lpbk",
143};
144
145static int pcm_lpbk_mux_map_value[] = {
146	0, 1,
147};
148
149static SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL(pcm_in_lpbk_mux_map_enum,
150					      PCM_INTF_CON1,
151					      PCM_I2S_PCM_LOOPBACK_SFT,
152					      1,
153					      pcm_lpbk_mux_map,
154					      pcm_lpbk_mux_map_value);
155
156static const struct snd_kcontrol_new pcm_in_lpbk_mux_control =
157	SOC_DAPM_ENUM("PCM In Lpbk Select", pcm_in_lpbk_mux_map_enum);
158
159static SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL(pcm_out_lpbk_mux_map_enum,
160					      PCM_INTF_CON1,
161					      PCM_I2S_PCM_LOOPBACK_SFT,
162					      1,
163					      pcm_lpbk_mux_map,
164					      pcm_lpbk_mux_map_value);
165
166static const struct snd_kcontrol_new pcm_out_lpbk_mux_control =
167	SOC_DAPM_ENUM("PCM Out Lpbk Select", pcm_out_lpbk_mux_map_enum);
168
169static const struct snd_soc_dapm_widget mtk_dai_pcm_widgets[] = {
170	/* inter-connections */
171	SND_SOC_DAPM_MIXER("PCM_1_PB_CH1", SND_SOC_NOPM, 0, 0,
172			   mtk_pcm_1_playback_ch1_mix,
173			   ARRAY_SIZE(mtk_pcm_1_playback_ch1_mix)),
174	SND_SOC_DAPM_MIXER("PCM_1_PB_CH2", SND_SOC_NOPM, 0, 0,
175			   mtk_pcm_1_playback_ch2_mix,
176			   ARRAY_SIZE(mtk_pcm_1_playback_ch2_mix)),
177
178	SND_SOC_DAPM_SUPPLY("PCM_1_EN",
179			    PCM_INTF_CON1, PCM_EN_SFT, 0,
180			    mtk_pcm_en_event,
181			    SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
182
183	/* pcm in lpbk */
184	SND_SOC_DAPM_MUX("PCM_In_Lpbk_Mux",
185			 SND_SOC_NOPM, 0, 0, &pcm_in_lpbk_mux_control),
186
187	/* pcm out lpbk */
188	SND_SOC_DAPM_MUX("PCM_Out_Lpbk_Mux",
189			 SND_SOC_NOPM, 0, 0, &pcm_out_lpbk_mux_control),
190};
191
192static const struct snd_soc_dapm_route mtk_dai_pcm_routes[] = {
193	{"PCM 1 Playback", NULL, "PCM_1_PB_CH1"},
194	{"PCM 1 Playback", NULL, "PCM_1_PB_CH2"},
195
196	{"PCM 1 Playback", NULL, "PCM_1_EN"},
197	{"PCM 1 Capture", NULL, "PCM_1_EN"},
198
199	{"PCM_1_PB_CH1", "DL2_CH1 Switch", "DL2"},
200	{"PCM_1_PB_CH2", "DL2_CH2 Switch", "DL2"},
201
202	{"PCM_1_PB_CH1", "DL4_CH1 Switch", "DL4"},
203	{"PCM_1_PB_CH2", "DL4_CH2 Switch", "DL4"},
204
205	/* pcm out lpbk */
206	{"PCM_Out_Lpbk_Mux", "Lpbk", "PCM 1 Playback"},
207	{"I2S0", NULL, "PCM_Out_Lpbk_Mux"},
208
209	/* pcm in lpbk */
210	{"PCM_In_Lpbk_Mux", "Lpbk", "PCM 1 Capture"},
211	{"I2S3", NULL, "PCM_In_Lpbk_Mux"},
212};
213
214/* dai ops */
215static int mtk_dai_pcm_hw_params(struct snd_pcm_substream *substream,
216				 struct snd_pcm_hw_params *params,
217				 struct snd_soc_dai *dai)
218{
219	struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
220	struct mt8186_afe_private *afe_priv = afe->platform_priv;
221	struct snd_soc_dapm_widget *p = snd_soc_dai_get_widget_playback(dai);
222	struct snd_soc_dapm_widget *c = snd_soc_dai_get_widget_capture(dai);
223	int pcm_id = dai->id;
224	struct mtk_afe_pcm_priv *pcm_priv = afe_priv->dai_priv[pcm_id];
225	unsigned int rate = params_rate(params);
226	unsigned int rate_reg = mt8186_rate_transform(afe->dev, rate, dai->id);
227	snd_pcm_format_t format = params_format(params);
228	unsigned int data_width =
229		snd_pcm_format_width(format);
230	unsigned int wlen_width =
231		snd_pcm_format_physical_width(format);
232	unsigned int pcm_con = 0;
233
234	dev_dbg(afe->dev, "%s(), id %d, stream %d, widget active p %d, c %d\n",
235		__func__, dai->id, substream->stream, p->active, c->active);
236	dev_dbg(afe->dev, "%s(), rate %d, rate_reg %d, data_width %d, wlen_width %d\n",
237		__func__, rate, rate_reg, data_width, wlen_width);
238
239	if (p->active || c->active)
240		return 0;
241
242	switch (dai->id) {
243	case MT8186_DAI_PCM:
244		pcm_con |= AUD_TX_LCH_RPT_NO_REPEAT << PCM_TX_LCH_RPT_SFT;
245		pcm_con |= AUD_VBT_16K_MODE_DISABLE << PCM_VBT_16K_MODE_SFT;
246		pcm_con |= AUD_EXT_MODEM_SELECT_EXTERNAL << PCM_EXT_MODEM_SFT;
247		pcm_con |= AUD_PCM_ONE_BCK_CYCLE_SYNC << PCM_SYNC_TYPE_SFT;
248		pcm_con |= AUD_BT_MODE_DUAL_MIC_ON_TX << PCM_BT_MODE_SFT;
249		pcm_con |= AUD_PCM_AFIFO_AFIFO << PCM_BYP_ASRC_SFT;
250		pcm_con |= AUD_PCM_CLOCK_MASTER_MODE << PCM_SLAVE_SFT;
251		pcm_con |= 0 << PCM_SYNC_LENGTH_SFT;
252
253		/* sampling rate */
254		pcm_con |= rate_reg << PCM_MODE_SFT;
255
256		/* format */
257		pcm_con |= pcm_priv->fmt << PCM_FMT_SFT;
258
259		/* 24bit data width */
260		if (data_width > 16)
261			pcm_con |= AUD_PCM_24BIT_PCM_24_BITS << PCM_24BIT_SFT;
262		else
263			pcm_con |= AUD_PCM_24BIT_PCM_16_BITS << PCM_24BIT_SFT;
264
265		/* wlen width*/
266		if (wlen_width > 16)
267			pcm_con |= AUD_PCM_WLEN_PCM_64_BCK_CYCLES << PCM_WLEN_SFT;
268		else
269			pcm_con |= AUD_PCM_WLEN_PCM_32_BCK_CYCLES << PCM_WLEN_SFT;
270
271		/* clock invert */
272		pcm_con |= pcm_priv->lck_invert << PCM_SYNC_OUT_INV_SFT;
273		pcm_con |= pcm_priv->bck_invert << PCM_BCLK_OUT_INV_SFT;
274
275		regmap_update_bits(afe->regmap, PCM_INTF_CON1, 0xfffffffe, pcm_con);
276		break;
277	default:
278		dev_err(afe->dev, "%s(), id %d not support\n", __func__, dai->id);
279		return -EINVAL;
280	}
281
282	return 0;
283}
284
285static int mtk_dai_pcm_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
286{
287	struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
288	struct mt8186_afe_private *afe_priv = afe->platform_priv;
289	struct mtk_afe_pcm_priv *pcm_priv = afe_priv->dai_priv[dai->id];
290
291	/* DAI mode*/
292	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
293	case SND_SOC_DAIFMT_I2S:
294		pcm_priv->fmt = AUD_PCM_FMT_I2S;
295		break;
296	case SND_SOC_DAIFMT_LEFT_J:
297		pcm_priv->fmt = AUD_PCM_FMT_EIAJ;
298		break;
299	case SND_SOC_DAIFMT_DSP_A:
300		pcm_priv->fmt = AUD_PCM_FMT_PCM_MODE_A;
301		break;
302	case SND_SOC_DAIFMT_DSP_B:
303		pcm_priv->fmt = AUD_PCM_FMT_PCM_MODE_B;
304		break;
305	default:
306		pcm_priv->fmt = AUD_PCM_FMT_I2S;
307	}
308
309	/* DAI clock inversion*/
310	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
311	case SND_SOC_DAIFMT_NB_NF:
312		pcm_priv->bck_invert = AUD_BCLK_OUT_INV_NO_INVERSE;
313		pcm_priv->lck_invert = AUD_LRCLK_OUT_INV_NO_INVERSE;
314		break;
315	case SND_SOC_DAIFMT_NB_IF:
316		pcm_priv->bck_invert = AUD_BCLK_OUT_INV_NO_INVERSE;
317		pcm_priv->lck_invert = AUD_BCLK_OUT_INV_INVERSE;
318		break;
319	case SND_SOC_DAIFMT_IB_NF:
320		pcm_priv->bck_invert = AUD_BCLK_OUT_INV_INVERSE;
321		pcm_priv->lck_invert = AUD_LRCLK_OUT_INV_NO_INVERSE;
322		break;
323	case SND_SOC_DAIFMT_IB_IF:
324		pcm_priv->bck_invert = AUD_BCLK_OUT_INV_INVERSE;
325		pcm_priv->lck_invert = AUD_BCLK_OUT_INV_INVERSE;
326		break;
327	default:
328		pcm_priv->bck_invert = AUD_BCLK_OUT_INV_NO_INVERSE;
329		pcm_priv->lck_invert = AUD_LRCLK_OUT_INV_NO_INVERSE;
330		break;
331	}
332
333	return 0;
334}
335
336static const struct snd_soc_dai_ops mtk_dai_pcm_ops = {
337	.hw_params = mtk_dai_pcm_hw_params,
338	.set_fmt = mtk_dai_pcm_set_fmt,
339};
340
341/* dai driver */
342#define MTK_PCM_RATES (SNDRV_PCM_RATE_8000 |\
343		       SNDRV_PCM_RATE_16000 |\
344		       SNDRV_PCM_RATE_32000 |\
345		       SNDRV_PCM_RATE_48000)
346
347#define MTK_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
348			 SNDRV_PCM_FMTBIT_S24_LE |\
349			 SNDRV_PCM_FMTBIT_S32_LE)
350
351static struct snd_soc_dai_driver mtk_dai_pcm_driver[] = {
352	{
353		.name = "PCM 1",
354		.id = MT8186_DAI_PCM,
355		.playback = {
356			.stream_name = "PCM 1 Playback",
357			.channels_min = 1,
358			.channels_max = 2,
359			.rates = MTK_PCM_RATES,
360			.formats = MTK_PCM_FORMATS,
361		},
362		.capture = {
363			.stream_name = "PCM 1 Capture",
364			.channels_min = 1,
365			.channels_max = 2,
366			.rates = MTK_PCM_RATES,
367			.formats = MTK_PCM_FORMATS,
368		},
369		.ops = &mtk_dai_pcm_ops,
370		.symmetric_rate = 1,
371		.symmetric_sample_bits = 1,
372	},
373};
374
375static struct mtk_afe_pcm_priv *init_pcm_priv_data(struct mtk_base_afe *afe)
376{
377	struct mtk_afe_pcm_priv *pcm_priv;
378
379	pcm_priv = devm_kzalloc(afe->dev, sizeof(struct mtk_afe_pcm_priv),
380				GFP_KERNEL);
381	if (!pcm_priv)
382		return NULL;
383
384	pcm_priv->id = MT8186_DAI_PCM;
385	pcm_priv->fmt = AUD_PCM_FMT_I2S;
386	pcm_priv->bck_invert = AUD_BCLK_OUT_INV_NO_INVERSE;
387	pcm_priv->lck_invert = AUD_LRCLK_OUT_INV_NO_INVERSE;
388
389	return pcm_priv;
390}
391
392int mt8186_dai_pcm_register(struct mtk_base_afe *afe)
393{
394	struct mt8186_afe_private *afe_priv = afe->platform_priv;
395	struct mtk_afe_pcm_priv *pcm_priv;
396	struct mtk_base_afe_dai *dai;
397
398	dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
399	if (!dai)
400		return -ENOMEM;
401
402	list_add(&dai->list, &afe->sub_dais);
403
404	dai->dai_drivers = mtk_dai_pcm_driver;
405	dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_pcm_driver);
406
407	dai->dapm_widgets = mtk_dai_pcm_widgets;
408	dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_pcm_widgets);
409	dai->dapm_routes = mtk_dai_pcm_routes;
410	dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_pcm_routes);
411
412	pcm_priv = init_pcm_priv_data(afe);
413	if (!pcm_priv)
414		return -ENOMEM;
415
416	afe_priv->dai_priv[MT8186_DAI_PCM] = pcm_priv;
417
418	return 0;
419}
420