File Index
Files and directories
Sign in
FILE

acp3x-i2s.c

File preview with syntax highlighting.

linux-6.19.14/sound/soc/amd/raven/acp3x-i2s.c C
  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 val, period_bytes, reg_val, ier_val, water_val;
153	u32 buf_size, buf_reg;
154	int ret;
155
156	rtd = substream->runtime->private_data;
157	period_bytes = frames_to_bytes(substream->runtime,
158			substream->runtime->period_size);
159	buf_size = frames_to_bytes(substream->runtime,
160			substream->runtime->buffer_size);
161	switch (cmd) {
162	case SNDRV_PCM_TRIGGER_START:
163	case SNDRV_PCM_TRIGGER_RESUME:
164	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
165		rtd->bytescount = acp_get_byte_count(rtd,
166						substream->stream);
167		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
168			switch (rtd->i2s_instance) {
169			case I2S_BT_INSTANCE:
170				water_val =
171					mmACP_BT_TX_INTR_WATERMARK_SIZE;
172				reg_val = mmACP_BTTDM_ITER;
173				ier_val = mmACP_BTTDM_IER;
174				buf_reg = mmACP_BT_TX_RINGBUFSIZE;
175				break;
176			case I2S_SP_INSTANCE:
177			default:
178				water_val =
179					mmACP_I2S_TX_INTR_WATERMARK_SIZE;
180				reg_val = mmACP_I2STDM_ITER;
181				ier_val = mmACP_I2STDM_IER;
182				buf_reg = mmACP_I2S_TX_RINGBUFSIZE;
183			}
184		} else {
185			switch (rtd->i2s_instance) {
186			case I2S_BT_INSTANCE:
187				water_val =
188					mmACP_BT_RX_INTR_WATERMARK_SIZE;
189				reg_val = mmACP_BTTDM_IRER;
190				ier_val = mmACP_BTTDM_IER;
191				buf_reg = mmACP_BT_RX_RINGBUFSIZE;
192				break;
193			case I2S_SP_INSTANCE:
194			default:
195				water_val =
196					mmACP_I2S_RX_INTR_WATERMARK_SIZE;
197				reg_val = mmACP_I2STDM_IRER;
198				ier_val = mmACP_I2STDM_IER;
199				buf_reg = mmACP_I2S_RX_RINGBUFSIZE;
200			}
201		}
202		rv_writel(period_bytes, rtd->acp3x_base + water_val);
203		rv_writel(buf_size, rtd->acp3x_base + buf_reg);
204		val = rv_readl(rtd->acp3x_base + reg_val);
205		val = val | BIT(0);
206		rv_writel(val, rtd->acp3x_base + reg_val);
207		rv_writel(1, rtd->acp3x_base + ier_val);
208		ret = 0;
209		break;
210	case SNDRV_PCM_TRIGGER_STOP:
211	case SNDRV_PCM_TRIGGER_SUSPEND:
212	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
213		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
214			switch (rtd->i2s_instance) {
215			case I2S_BT_INSTANCE:
216				reg_val = mmACP_BTTDM_ITER;
217				break;
218			case I2S_SP_INSTANCE:
219			default:
220				reg_val = mmACP_I2STDM_ITER;
221			}
222
223		} else {
224			switch (rtd->i2s_instance) {
225			case I2S_BT_INSTANCE:
226				reg_val = mmACP_BTTDM_IRER;
227				break;
228			case I2S_SP_INSTANCE:
229			default:
230				reg_val = mmACP_I2STDM_IRER;
231			}
232		}
233		val = rv_readl(rtd->acp3x_base + reg_val);
234		val = val & ~BIT(0);
235		rv_writel(val, rtd->acp3x_base + reg_val);
236
237		if (!(rv_readl(rtd->acp3x_base + mmACP_BTTDM_ITER) & BIT(0)) &&
238		     !(rv_readl(rtd->acp3x_base + mmACP_BTTDM_IRER) & BIT(0)))
239			rv_writel(0, rtd->acp3x_base + mmACP_BTTDM_IER);
240		if (!(rv_readl(rtd->acp3x_base + mmACP_I2STDM_ITER) & BIT(0)) &&
241		     !(rv_readl(rtd->acp3x_base + mmACP_I2STDM_IRER) & BIT(0)))
242			rv_writel(0, rtd->acp3x_base + mmACP_I2STDM_IER);
243		ret = 0;
244		break;
245	default:
246		ret = -EINVAL;
247		break;
248	}
249
250	return ret;
251}
252
253static const struct snd_soc_dai_ops acp3x_i2s_dai_ops = {
254	.hw_params = acp3x_i2s_hwparams,
255	.trigger = acp3x_i2s_trigger,
256	.set_fmt = acp3x_i2s_set_fmt,
257	.set_tdm_slot = acp3x_i2s_set_tdm_slot,
258};
259
260static const struct snd_soc_component_driver acp3x_dai_component = {
261	.name			= DRV_NAME,
262	.legacy_dai_naming	= 1,
263};
264
265static struct snd_soc_dai_driver acp3x_i2s_dai = {
266	.playback = {
267		.rates = SNDRV_PCM_RATE_8000_96000,
268		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
269			SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
270		.channels_min = 2,
271		.channels_max = 8,
272		.rate_min = 8000,
273		.rate_max = 96000,
274	},
275	.capture = {
276		.rates = SNDRV_PCM_RATE_8000_48000,
277		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
278			SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
279		.channels_min = 2,
280		.channels_max = 2,
281		.rate_min = 8000,
282		.rate_max = 48000,
283	},
284	.ops = &acp3x_i2s_dai_ops,
285};
286
287static int acp3x_dai_probe(struct platform_device *pdev)
288{
289	struct resource *res;
290	struct i2s_dev_data *adata;
291	int ret;
292
293	adata = devm_kzalloc(&pdev->dev, sizeof(struct i2s_dev_data),
294			GFP_KERNEL);
295	if (!adata)
296		return -ENOMEM;
297
298	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
299	if (!res) {
300		dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
301		return -ENOMEM;
302	}
303	adata->acp3x_base = devm_ioremap(&pdev->dev, res->start,
304						resource_size(res));
305	if (!adata->acp3x_base)
306		return -ENOMEM;
307
308	adata->i2s_irq = res->start;
309	dev_set_drvdata(&pdev->dev, adata);
310	ret = devm_snd_soc_register_component(&pdev->dev,
311			&acp3x_dai_component, &acp3x_i2s_dai, 1);
312	if (ret) {
313		dev_err(&pdev->dev, "Fail to register acp i2s dai\n");
314		return -ENODEV;
315	}
316	return 0;
317}
318
319static struct platform_driver acp3x_dai_driver = {
320	.probe = acp3x_dai_probe,
321	.driver = {
322		.name = "acp3x_i2s_playcap",
323	},
324};
325
326module_platform_driver(acp3x_dai_driver);
327
328MODULE_AUTHOR("Vishnuvardhanrao.Ravulapati@amd.com");
329MODULE_DESCRIPTION("AMD ACP 3.x PCM Driver");
330MODULE_LICENSE("GPL v2");
331MODULE_ALIAS("platform:"DRV_NAME);
~/ Read-only file preview.