1// SPDX-License-Identifier: GPL-2.0+
2//
3// AMD ALSA SoC PCM Driver
4//
5//Copyright 2016 Advanced Micro Devices, Inc.
6
7#include <linux/platform_device.h>
8#include <linux/module.h>
9#include <linux/err.h>
10#include <linux/io.h>
11#include <sound/pcm_params.h>
12#include <sound/soc.h>
13#include <sound/soc-dai.h>
14#include <linux/dma-mapping.h>
15
16#include "acp3x.h"
17
18#define DRV_NAME "acp3x_i2s_playcap"
19
20static int acp3x_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
21					unsigned int fmt)
22{
23	struct i2s_dev_data *adata;
24	int mode;
25
26	adata = snd_soc_dai_get_drvdata(cpu_dai);
27	mode = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
28	switch (mode) {
29	case SND_SOC_DAIFMT_I2S:
30		adata->tdm_mode = TDM_DISABLE;
31		break;
32	case SND_SOC_DAIFMT_DSP_A:
33		adata->tdm_mode = TDM_ENABLE;
34		break;
35	default:
36		return -EINVAL;
37	}
38	return 0;
39}
40
41static int acp3x_i2s_set_tdm_slot(struct snd_soc_dai *cpu_dai,
42		u32 tx_mask, u32 rx_mask, int slots, int slot_width)
43{
44	struct i2s_dev_data *adata;
45	u32 frm_len;
46	u16 slot_len;
47
48	adata = snd_soc_dai_get_drvdata(cpu_dai);
49
50	/* These values are as per Hardware Spec */
51	switch (slot_width) {
52	case SLOT_WIDTH_8:
53		slot_len = 8;
54		break;
55	case SLOT_WIDTH_16:
56		slot_len = 16;
57		break;
58	case SLOT_WIDTH_24:
59		slot_len = 24;
60		break;
61	case SLOT_WIDTH_32:
62		slot_len = 0;
63		break;
64	default:
65		return -EINVAL;
66	}
67	frm_len = FRM_LEN | (slots << 15) | (slot_len << 18);
68	adata->tdm_fmt = frm_len;
69	return 0;
70}
71
72static int acp3x_i2s_hwparams(struct snd_pcm_substream *substream,
73	struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
74{
75	struct i2s_stream_instance *rtd;
76	struct snd_soc_pcm_runtime *prtd;
77	struct snd_soc_card *card;
78	struct acp3x_platform_info *pinfo;
79	struct i2s_dev_data *adata;
80	u32 val;
81	u32 reg_val, frmt_reg;
82
83	prtd = snd_soc_substream_to_rtd(substream);
84	rtd = substream->runtime->private_data;
85	card = prtd->card;
86	adata = snd_soc_dai_get_drvdata(dai);
87	pinfo = snd_soc_card_get_drvdata(card);
88	if (pinfo) {
89		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
90			rtd->i2s_instance = pinfo->play_i2s_instance;
91		else
92			rtd->i2s_instance = pinfo->cap_i2s_instance;
93	}
94
95	/* These values are as per Hardware Spec */
96	switch (params_format(params)) {
97	case SNDRV_PCM_FORMAT_U8:
98	case SNDRV_PCM_FORMAT_S8:
99		rtd->xfer_resolution = 0x0;
100		break;
101	case SNDRV_PCM_FORMAT_S16_LE:
102		rtd->xfer_resolution = 0x02;
103		break;
104	case SNDRV_PCM_FORMAT_S24_LE:
105		rtd->xfer_resolution = 0x04;
106		break;
107	case SNDRV_PCM_FORMAT_S32_LE:
108		rtd->xfer_resolution = 0x05;
109		break;
110	default:
111		return -EINVAL;
112	}
113	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
114		switch (rtd->i2s_instance) {
115		case I2S_BT_INSTANCE:
116			reg_val = mmACP_BTTDM_ITER;
117			frmt_reg = mmACP_BTTDM_TXFRMT;
118			break;
119		case I2S_SP_INSTANCE:
120		default:
121			reg_val = mmACP_I2STDM_ITER;
122			frmt_reg = mmACP_I2STDM_TXFRMT;
123		}
124	} else {
125		switch (rtd->i2s_instance) {
126		case I2S_BT_INSTANCE:
127			reg_val = mmACP_BTTDM_IRER;
128			frmt_reg = mmACP_BTTDM_RXFRMT;
129			break;
130		case I2S_SP_INSTANCE:
131		default:
132			reg_val = mmACP_I2STDM_IRER;
133			frmt_reg = mmACP_I2STDM_RXFRMT;
134		}
135	}
136	if (adata->tdm_mode) {
137		val = rv_readl(rtd->acp3x_base + reg_val);
138		rv_writel(val | 0x2, rtd->acp3x_base + reg_val);
139		rv_writel(adata->tdm_fmt, rtd->acp3x_base + frmt_reg);
140	}
141	val = rv_readl(rtd->acp3x_base + reg_val);
142	val &= ~ACP3x_ITER_IRER_SAMP_LEN_MASK;
143	val = val | (rtd->xfer_resolution  << 3);
144	rv_writel(val, rtd->acp3x_base + reg_val);
145	return 0;
146}
147
148static int acp3x_i2s_trigger(struct snd_pcm_substream *substream,
149				int cmd, struct snd_soc_dai *dai)
150{
151	struct i2s_stream_instance *rtd;
152	u32 ret, val, period_bytes, reg_val, ier_val, water_val;
153	u32 buf_size, buf_reg;
154
155	rtd = substream->runtime->private_data;
156	period_bytes = frames_to_bytes(substream->runtime,
157			substream->runtime->period_size);
158	buf_size = frames_to_bytes(substream->runtime,
159			substream->runtime->buffer_size);
160	switch (cmd) {
161	case SNDRV_PCM_TRIGGER_START:
162	case SNDRV_PCM_TRIGGER_RESUME:
163	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
164		rtd->bytescount = acp_get_byte_count(rtd,
165						substream->stream);
166		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
167			switch (rtd->i2s_instance) {
168			case I2S_BT_INSTANCE:
169				water_val =
170					mmACP_BT_TX_INTR_WATERMARK_SIZE;
171				reg_val = mmACP_BTTDM_ITER;
172				ier_val = mmACP_BTTDM_IER;
173				buf_reg = mmACP_BT_TX_RINGBUFSIZE;
174				break;
175			case I2S_SP_INSTANCE:
176			default:
177				water_val =
178					mmACP_I2S_TX_INTR_WATERMARK_SIZE;
179				reg_val = mmACP_I2STDM_ITER;
180				ier_val = mmACP_I2STDM_IER;
181				buf_reg = mmACP_I2S_TX_RINGBUFSIZE;
182			}
183		} else {
184			switch (rtd->i2s_instance) {
185			case I2S_BT_INSTANCE:
186				water_val =
187					mmACP_BT_RX_INTR_WATERMARK_SIZE;
188				reg_val = mmACP_BTTDM_IRER;
189				ier_val = mmACP_BTTDM_IER;
190				buf_reg = mmACP_BT_RX_RINGBUFSIZE;
191				break;
192			case I2S_SP_INSTANCE:
193			default:
194				water_val =
195					mmACP_I2S_RX_INTR_WATERMARK_SIZE;
196				reg_val = mmACP_I2STDM_IRER;
197				ier_val = mmACP_I2STDM_IER;
198				buf_reg = mmACP_I2S_RX_RINGBUFSIZE;
199			}
200		}
201		rv_writel(period_bytes, rtd->acp3x_base + water_val);
202		rv_writel(buf_size, rtd->acp3x_base + buf_reg);
203		val = rv_readl(rtd->acp3x_base + reg_val);
204		val = val | BIT(0);
205		rv_writel(val, rtd->acp3x_base + reg_val);
206		rv_writel(1, rtd->acp3x_base + ier_val);
207		ret = 0;
208		break;
209	case SNDRV_PCM_TRIGGER_STOP:
210	case SNDRV_PCM_TRIGGER_SUSPEND:
211	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
212		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
213			switch (rtd->i2s_instance) {
214			case I2S_BT_INSTANCE:
215				reg_val = mmACP_BTTDM_ITER;
216				break;
217			case I2S_SP_INSTANCE:
218			default:
219				reg_val = mmACP_I2STDM_ITER;
220			}
221
222		} else {
223			switch (rtd->i2s_instance) {
224			case I2S_BT_INSTANCE:
225				reg_val = mmACP_BTTDM_IRER;
226				break;
227			case I2S_SP_INSTANCE:
228			default:
229				reg_val = mmACP_I2STDM_IRER;
230			}
231		}
232		val = rv_readl(rtd->acp3x_base + reg_val);
233		val = val & ~BIT(0);
234		rv_writel(val, rtd->acp3x_base + reg_val);
235
236		if (!(rv_readl(rtd->acp3x_base + mmACP_BTTDM_ITER) & BIT(0)) &&
237		     !(rv_readl(rtd->acp3x_base + mmACP_BTTDM_IRER) & BIT(0)))
238			rv_writel(0, rtd->acp3x_base + mmACP_BTTDM_IER);
239		if (!(rv_readl(rtd->acp3x_base + mmACP_I2STDM_ITER) & BIT(0)) &&
240		     !(rv_readl(rtd->acp3x_base + mmACP_I2STDM_IRER) & BIT(0)))
241			rv_writel(0, rtd->acp3x_base + mmACP_I2STDM_IER);
242		ret = 0;
243		break;
244	default:
245		ret = -EINVAL;
246		break;
247	}
248
249	return ret;
250}
251
252static const struct snd_soc_dai_ops acp3x_i2s_dai_ops = {
253	.hw_params = acp3x_i2s_hwparams,
254	.trigger = acp3x_i2s_trigger,
255	.set_fmt = acp3x_i2s_set_fmt,
256	.set_tdm_slot = acp3x_i2s_set_tdm_slot,
257};
258
259static const struct snd_soc_component_driver acp3x_dai_component = {
260	.name			= DRV_NAME,
261	.legacy_dai_naming	= 1,
262};
263
264static struct snd_soc_dai_driver acp3x_i2s_dai = {
265	.playback = {
266		.rates = SNDRV_PCM_RATE_8000_96000,
267		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
268			SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
269		.channels_min = 2,
270		.channels_max = 8,
271		.rate_min = 8000,
272		.rate_max = 96000,
273	},
274	.capture = {
275		.rates = SNDRV_PCM_RATE_8000_48000,
276		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
277			SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
278		.channels_min = 2,
279		.channels_max = 2,
280		.rate_min = 8000,
281		.rate_max = 48000,
282	},
283	.ops = &acp3x_i2s_dai_ops,
284};
285
286static int acp3x_dai_probe(struct platform_device *pdev)
287{
288	struct resource *res;
289	struct i2s_dev_data *adata;
290	int ret;
291
292	adata = devm_kzalloc(&pdev->dev, sizeof(struct i2s_dev_data),
293			GFP_KERNEL);
294	if (!adata)
295		return -ENOMEM;
296
297	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
298	if (!res) {
299		dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
300		return -ENOMEM;
301	}
302	adata->acp3x_base = devm_ioremap(&pdev->dev, res->start,
303						resource_size(res));
304	if (!adata->acp3x_base)
305		return -ENOMEM;
306
307	adata->i2s_irq = res->start;
308	dev_set_drvdata(&pdev->dev, adata);
309	ret = devm_snd_soc_register_component(&pdev->dev,
310			&acp3x_dai_component, &acp3x_i2s_dai, 1);
311	if (ret) {
312		dev_err(&pdev->dev, "Fail to register acp i2s dai\n");
313		return -ENODEV;
314	}
315	return 0;
316}
317
318static struct platform_driver acp3x_dai_driver = {
319	.probe = acp3x_dai_probe,
320	.driver = {
321		.name = "acp3x_i2s_playcap",
322	},
323};
324
325module_platform_driver(acp3x_dai_driver);
326
327MODULE_AUTHOR("Vishnuvardhanrao.Ravulapati@amd.com");
328MODULE_DESCRIPTION("AMD ACP 3.x PCM Driver");
329MODULE_LICENSE("GPL v2");
330MODULE_ALIAS("platform:"DRV_NAME);
331