xref: /openbmc/linux/sound/soc/amd/raven/acp3x-i2s.c (revision dc6a81c3)
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"
19 
20 static 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 
41 static 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 val, reg_val, frmt_reg, 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 
68 	/* Enable I2S/BT channels TDM, respective TX/RX frame lengths.*/
69 
70 	frm_len = FRM_LEN | (slots << 15) | (slot_len << 18);
71 	if (adata->substream_type == SNDRV_PCM_STREAM_PLAYBACK) {
72 		switch (adata->i2s_instance) {
73 		case I2S_BT_INSTANCE:
74 			reg_val = mmACP_BTTDM_ITER;
75 			frmt_reg = mmACP_BTTDM_TXFRMT;
76 			break;
77 		case I2S_SP_INSTANCE:
78 		default:
79 			reg_val = mmACP_I2STDM_ITER;
80 			frmt_reg = mmACP_I2STDM_TXFRMT;
81 		}
82 	} else {
83 		switch (adata->i2s_instance) {
84 		case I2S_BT_INSTANCE:
85 			reg_val = mmACP_BTTDM_IRER;
86 			frmt_reg = mmACP_BTTDM_RXFRMT;
87 			break;
88 		case I2S_SP_INSTANCE:
89 		default:
90 			reg_val = mmACP_I2STDM_IRER;
91 			frmt_reg = mmACP_I2STDM_RXFRMT;
92 		}
93 	}
94 	val = rv_readl(adata->acp3x_base + reg_val);
95 	rv_writel(val | 0x2, adata->acp3x_base + reg_val);
96 	rv_writel(frm_len, adata->acp3x_base + frmt_reg);
97 	adata->tdm_fmt = frm_len;
98 	return 0;
99 }
100 
101 static int acp3x_i2s_hwparams(struct snd_pcm_substream *substream,
102 	struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
103 {
104 	struct i2s_stream_instance *rtd;
105 	struct snd_soc_pcm_runtime *prtd;
106 	struct snd_soc_card *card;
107 	struct acp3x_platform_info *pinfo;
108 	u32 val;
109 	u32 reg_val;
110 
111 	prtd = substream->private_data;
112 	rtd = substream->runtime->private_data;
113 	card = prtd->card;
114 	pinfo = snd_soc_card_get_drvdata(card);
115 	if (pinfo) {
116 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
117 			rtd->i2s_instance = pinfo->play_i2s_instance;
118 		else
119 			rtd->i2s_instance = pinfo->cap_i2s_instance;
120 	}
121 
122 	/* These values are as per Hardware Spec */
123 	switch (params_format(params)) {
124 	case SNDRV_PCM_FORMAT_U8:
125 	case SNDRV_PCM_FORMAT_S8:
126 		rtd->xfer_resolution = 0x0;
127 		break;
128 	case SNDRV_PCM_FORMAT_S16_LE:
129 		rtd->xfer_resolution = 0x02;
130 		break;
131 	case SNDRV_PCM_FORMAT_S24_LE:
132 		rtd->xfer_resolution = 0x04;
133 		break;
134 	case SNDRV_PCM_FORMAT_S32_LE:
135 		rtd->xfer_resolution = 0x05;
136 		break;
137 	default:
138 		return -EINVAL;
139 	}
140 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
141 		switch (rtd->i2s_instance) {
142 		case I2S_BT_INSTANCE:
143 			reg_val = mmACP_BTTDM_ITER;
144 			break;
145 		case I2S_SP_INSTANCE:
146 		default:
147 			reg_val = mmACP_I2STDM_ITER;
148 		}
149 	} else {
150 		switch (rtd->i2s_instance) {
151 		case I2S_BT_INSTANCE:
152 			reg_val = mmACP_BTTDM_IRER;
153 			break;
154 		case I2S_SP_INSTANCE:
155 		default:
156 			reg_val = mmACP_I2STDM_IRER;
157 		}
158 	}
159 	val = rv_readl(rtd->acp3x_base + reg_val);
160 	val = val | (rtd->xfer_resolution  << 3);
161 	rv_writel(val, rtd->acp3x_base + reg_val);
162 	return 0;
163 }
164 
165 static int acp3x_i2s_trigger(struct snd_pcm_substream *substream,
166 				int cmd, struct snd_soc_dai *dai)
167 {
168 	struct i2s_stream_instance *rtd;
169 	struct snd_soc_pcm_runtime *prtd;
170 	struct snd_soc_card *card;
171 	struct acp3x_platform_info *pinfo;
172 	u32 ret, val, period_bytes, reg_val, ier_val, water_val;
173 
174 	prtd = substream->private_data;
175 	rtd = substream->runtime->private_data;
176 	card = prtd->card;
177 	pinfo = snd_soc_card_get_drvdata(card);
178 	if (pinfo) {
179 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
180 			rtd->i2s_instance = pinfo->play_i2s_instance;
181 		else
182 			rtd->i2s_instance = pinfo->cap_i2s_instance;
183 	}
184 	period_bytes = frames_to_bytes(substream->runtime,
185 			substream->runtime->period_size);
186 	switch (cmd) {
187 	case SNDRV_PCM_TRIGGER_START:
188 	case SNDRV_PCM_TRIGGER_RESUME:
189 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
190 		rtd->bytescount = acp_get_byte_count(rtd,
191 						substream->stream);
192 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
193 			switch (rtd->i2s_instance) {
194 			case I2S_BT_INSTANCE:
195 				water_val =
196 					mmACP_BT_TX_INTR_WATERMARK_SIZE;
197 				reg_val = mmACP_BTTDM_ITER;
198 				ier_val = mmACP_BTTDM_IER;
199 				break;
200 			case I2S_SP_INSTANCE:
201 			default:
202 				water_val =
203 					mmACP_I2S_TX_INTR_WATERMARK_SIZE;
204 				reg_val = mmACP_I2STDM_ITER;
205 				ier_val = mmACP_I2STDM_IER;
206 			}
207 		} else {
208 			switch (rtd->i2s_instance) {
209 			case I2S_BT_INSTANCE:
210 				water_val =
211 					mmACP_BT_RX_INTR_WATERMARK_SIZE;
212 				reg_val = mmACP_BTTDM_IRER;
213 				ier_val = mmACP_BTTDM_IER;
214 				break;
215 			case I2S_SP_INSTANCE:
216 			default:
217 				water_val =
218 					mmACP_I2S_RX_INTR_WATERMARK_SIZE;
219 				reg_val = mmACP_I2STDM_IRER;
220 				ier_val = mmACP_I2STDM_IER;
221 			}
222 		}
223 		rv_writel(period_bytes, rtd->acp3x_base + water_val);
224 		val = rv_readl(rtd->acp3x_base + reg_val);
225 		val = val | BIT(0);
226 		rv_writel(val, rtd->acp3x_base + reg_val);
227 		rv_writel(1, rtd->acp3x_base + ier_val);
228 		ret = 0;
229 		break;
230 	case SNDRV_PCM_TRIGGER_STOP:
231 	case SNDRV_PCM_TRIGGER_SUSPEND:
232 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
233 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
234 			switch (rtd->i2s_instance) {
235 			case I2S_BT_INSTANCE:
236 				reg_val = mmACP_BTTDM_ITER;
237 				break;
238 			case I2S_SP_INSTANCE:
239 			default:
240 				reg_val = mmACP_I2STDM_ITER;
241 			}
242 
243 		} else {
244 			switch (rtd->i2s_instance) {
245 			case I2S_BT_INSTANCE:
246 				reg_val = mmACP_BTTDM_IRER;
247 				break;
248 			case I2S_SP_INSTANCE:
249 			default:
250 				reg_val = mmACP_I2STDM_IRER;
251 			}
252 		}
253 		val = rv_readl(rtd->acp3x_base + reg_val);
254 		val = val & ~BIT(0);
255 		rv_writel(val, rtd->acp3x_base + reg_val);
256 
257 		if (!(rv_readl(rtd->acp3x_base + mmACP_BTTDM_ITER) & BIT(0)) &&
258 		     !(rv_readl(rtd->acp3x_base + mmACP_BTTDM_IRER) & BIT(0)))
259 			rv_writel(0, rtd->acp3x_base + mmACP_BTTDM_IER);
260 		if (!(rv_readl(rtd->acp3x_base + mmACP_I2STDM_ITER) & BIT(0)) &&
261 		     !(rv_readl(rtd->acp3x_base + mmACP_I2STDM_IRER) & BIT(0)))
262 			rv_writel(0, rtd->acp3x_base + mmACP_I2STDM_IER);
263 		ret = 0;
264 		break;
265 	default:
266 		ret = -EINVAL;
267 		break;
268 	}
269 
270 	return ret;
271 }
272 
273 static struct snd_soc_dai_ops acp3x_i2s_dai_ops = {
274 	.hw_params = acp3x_i2s_hwparams,
275 	.trigger = acp3x_i2s_trigger,
276 	.set_fmt = acp3x_i2s_set_fmt,
277 	.set_tdm_slot = acp3x_i2s_set_tdm_slot,
278 };
279 
280 static const struct snd_soc_component_driver acp3x_dai_component = {
281 	.name           = "acp3x-i2s",
282 };
283 
284 static struct snd_soc_dai_driver acp3x_i2s_dai = {
285 	.playback = {
286 		.rates = SNDRV_PCM_RATE_8000_96000,
287 		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
288 			SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE |
289 			SNDRV_PCM_FMTBIT_S32_LE,
290 		.channels_min = 2,
291 		.channels_max = 8,
292 		.rate_min = 8000,
293 		.rate_max = 96000,
294 	},
295 	.capture = {
296 		.rates = SNDRV_PCM_RATE_8000_48000,
297 		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
298 			SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE |
299 			SNDRV_PCM_FMTBIT_S32_LE,
300 		.channels_min = 2,
301 		.channels_max = 2,
302 		.rate_min = 8000,
303 		.rate_max = 48000,
304 	},
305 	.ops = &acp3x_i2s_dai_ops,
306 };
307 
308 static int acp3x_dai_probe(struct platform_device *pdev)
309 {
310 	struct resource *res;
311 	struct i2s_dev_data *adata;
312 	int ret;
313 
314 	adata = devm_kzalloc(&pdev->dev, sizeof(struct i2s_dev_data),
315 			GFP_KERNEL);
316 	if (!adata)
317 		return -ENOMEM;
318 
319 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
320 	if (!res) {
321 		dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
322 		return -ENOMEM;
323 	}
324 	adata->acp3x_base = devm_ioremap(&pdev->dev, res->start,
325 						resource_size(res));
326 	if (!adata->acp3x_base)
327 		return -ENOMEM;
328 
329 	adata->i2s_irq = res->start;
330 	dev_set_drvdata(&pdev->dev, adata);
331 	ret = devm_snd_soc_register_component(&pdev->dev,
332 			&acp3x_dai_component, &acp3x_i2s_dai, 1);
333 	if (ret) {
334 		dev_err(&pdev->dev, "Fail to register acp i2s dai\n");
335 		return -ENODEV;
336 	}
337 	return 0;
338 }
339 
340 static int acp3x_dai_remove(struct platform_device *pdev)
341 {
342 	/* As we use devm_ memory alloc there is nothing TBD here */
343 
344 	return 0;
345 }
346 
347 static struct platform_driver acp3x_dai_driver = {
348 	.probe = acp3x_dai_probe,
349 	.remove = acp3x_dai_remove,
350 	.driver = {
351 		.name = "acp3x_i2s_playcap",
352 	},
353 };
354 
355 module_platform_driver(acp3x_dai_driver);
356 
357 MODULE_AUTHOR("Vishnuvardhanrao.Ravulapati@amd.com");
358 MODULE_DESCRIPTION("AMD ACP 3.x PCM Driver");
359 MODULE_LICENSE("GPL v2");
360 MODULE_ALIAS("platform:" DRV_NAME);
361