1 // SPDX-License-Identifier: GPL-2.0+
3 // AMD ALSA SoC PCM Driver
5 // Copyright (C) 2021 Advanced Micro Devices, Inc. All rights reserved.
7 #include <linux/platform_device.h>
8 #include <linux/module.h>
11 #include <sound/pcm_params.h>
12 #include <sound/soc.h>
13 #include <sound/soc-dai.h>
14 #include <linux/dma-mapping.h>
18 #define DRV_NAME "acp5x_i2s_playcap"
20 static int acp5x_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
23 struct i2s_dev_data *adata;
26 adata = snd_soc_dai_get_drvdata(cpu_dai);
27 mode = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
29 case SND_SOC_DAIFMT_I2S:
30 adata->tdm_mode = TDM_DISABLE;
32 case SND_SOC_DAIFMT_DSP_A:
33 adata->tdm_mode = TDM_ENABLE;
38 mode = fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK;
40 case SND_SOC_DAIFMT_BP_FP:
41 adata->master_mode = I2S_MASTER_MODE_ENABLE;
43 case SND_SOC_DAIFMT_BC_FC:
44 adata->master_mode = I2S_MASTER_MODE_DISABLE;
50 static int acp5x_i2s_set_tdm_slot(struct snd_soc_dai *cpu_dai,
51 u32 tx_mask, u32 rx_mask,
52 int slots, int slot_width)
54 struct i2s_dev_data *adata;
58 adata = snd_soc_dai_get_drvdata(cpu_dai);
60 /* These values are as per Hardware Spec */
77 frm_len = FRM_LEN | (slots << 15) | (slot_len << 18);
78 adata->tdm_fmt = frm_len;
82 static int acp5x_i2s_hwparams(struct snd_pcm_substream *substream,
83 struct snd_pcm_hw_params *params,
84 struct snd_soc_dai *dai)
86 struct i2s_stream_instance *rtd;
87 struct snd_soc_pcm_runtime *prtd;
88 struct snd_soc_card *card;
89 struct acp5x_platform_info *pinfo;
90 struct i2s_dev_data *adata;
93 u32 reg_val, frmt_reg;
94 u32 lrclk_div_val, bclk_div_val;
98 prtd = asoc_substream_to_rtd(substream);
99 rtd = substream->runtime->private_data;
101 adata = snd_soc_dai_get_drvdata(dai);
102 pinfo = snd_soc_card_get_drvdata(card);
104 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
105 rtd->i2s_instance = pinfo->play_i2s_instance;
107 rtd->i2s_instance = pinfo->cap_i2s_instance;
110 /* These values are as per Hardware Spec */
111 switch (params_format(params)) {
112 case SNDRV_PCM_FORMAT_U8:
113 case SNDRV_PCM_FORMAT_S8:
114 rtd->xfer_resolution = 0x0;
116 case SNDRV_PCM_FORMAT_S16_LE:
117 rtd->xfer_resolution = 0x02;
119 case SNDRV_PCM_FORMAT_S24_LE:
120 rtd->xfer_resolution = 0x04;
122 case SNDRV_PCM_FORMAT_S32_LE:
123 rtd->xfer_resolution = 0x05;
128 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
129 switch (rtd->i2s_instance) {
130 case I2S_HS_INSTANCE:
131 reg_val = ACP_HSTDM_ITER;
132 frmt_reg = ACP_HSTDM_TXFRMT;
134 case I2S_SP_INSTANCE:
136 reg_val = ACP_I2STDM_ITER;
137 frmt_reg = ACP_I2STDM_TXFRMT;
140 switch (rtd->i2s_instance) {
141 case I2S_HS_INSTANCE:
142 reg_val = ACP_HSTDM_IRER;
143 frmt_reg = ACP_HSTDM_RXFRMT;
145 case I2S_SP_INSTANCE:
147 reg_val = ACP_I2STDM_IRER;
148 frmt_reg = ACP_I2STDM_RXFRMT;
151 if (adata->tdm_mode) {
152 val = acp_readl(rtd->acp5x_base + reg_val);
153 acp_writel(val | 0x2, rtd->acp5x_base + reg_val);
154 acp_writel(adata->tdm_fmt, rtd->acp5x_base + frmt_reg);
156 val = acp_readl(rtd->acp5x_base + reg_val);
157 val &= ~ACP5x_ITER_IRER_SAMP_LEN_MASK;
158 val = val | (rtd->xfer_resolution << 3);
159 acp_writel(val, rtd->acp5x_base + reg_val);
161 if (adata->master_mode) {
162 switch (params_format(params)) {
163 case SNDRV_PCM_FORMAT_S16_LE:
164 switch (params_rate(params)) {
193 case SNDRV_PCM_FORMAT_S32_LE:
194 switch (params_rate(params)) {
226 rtd->lrclk_div = lrclk_div_val;
227 rtd->bclk_div = bclk_div_val;
232 static int acp5x_i2s_trigger(struct snd_pcm_substream *substream,
233 int cmd, struct snd_soc_dai *dai)
235 struct i2s_stream_instance *rtd;
236 struct i2s_dev_data *adata;
237 u32 ret, val, period_bytes, reg_val, ier_val, water_val;
238 u32 buf_size, buf_reg;
240 adata = snd_soc_dai_get_drvdata(dai);
241 rtd = substream->runtime->private_data;
242 period_bytes = frames_to_bytes(substream->runtime,
243 substream->runtime->period_size);
244 buf_size = frames_to_bytes(substream->runtime,
245 substream->runtime->buffer_size);
247 case SNDRV_PCM_TRIGGER_START:
248 case SNDRV_PCM_TRIGGER_RESUME:
249 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
250 rtd->bytescount = acp_get_byte_count(rtd,
252 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
253 switch (rtd->i2s_instance) {
254 case I2S_HS_INSTANCE:
256 ACP_HS_TX_INTR_WATERMARK_SIZE;
257 reg_val = ACP_HSTDM_ITER;
258 ier_val = ACP_HSTDM_IER;
259 buf_reg = ACP_HS_TX_RINGBUFSIZE;
261 case I2S_SP_INSTANCE:
264 ACP_I2S_TX_INTR_WATERMARK_SIZE;
265 reg_val = ACP_I2STDM_ITER;
266 ier_val = ACP_I2STDM_IER;
267 buf_reg = ACP_I2S_TX_RINGBUFSIZE;
270 switch (rtd->i2s_instance) {
271 case I2S_HS_INSTANCE:
273 ACP_HS_RX_INTR_WATERMARK_SIZE;
274 reg_val = ACP_HSTDM_IRER;
275 ier_val = ACP_HSTDM_IER;
276 buf_reg = ACP_HS_RX_RINGBUFSIZE;
278 case I2S_SP_INSTANCE:
281 ACP_I2S_RX_INTR_WATERMARK_SIZE;
282 reg_val = ACP_I2STDM_IRER;
283 ier_val = ACP_I2STDM_IER;
284 buf_reg = ACP_I2S_RX_RINGBUFSIZE;
287 acp_writel(period_bytes, rtd->acp5x_base + water_val);
288 acp_writel(buf_size, rtd->acp5x_base + buf_reg);
289 if (adata->master_mode)
290 acp5x_set_i2s_clk(adata, rtd);
291 val = acp_readl(rtd->acp5x_base + reg_val);
293 acp_writel(val, rtd->acp5x_base + reg_val);
294 acp_writel(1, rtd->acp5x_base + ier_val);
297 case SNDRV_PCM_TRIGGER_STOP:
298 case SNDRV_PCM_TRIGGER_SUSPEND:
299 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
300 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
301 switch (rtd->i2s_instance) {
302 case I2S_HS_INSTANCE:
303 reg_val = ACP_HSTDM_ITER;
305 case I2S_SP_INSTANCE:
307 reg_val = ACP_I2STDM_ITER;
311 switch (rtd->i2s_instance) {
312 case I2S_HS_INSTANCE:
313 reg_val = ACP_HSTDM_IRER;
315 case I2S_SP_INSTANCE:
317 reg_val = ACP_I2STDM_IRER;
320 val = acp_readl(rtd->acp5x_base + reg_val);
322 acp_writel(val, rtd->acp5x_base + reg_val);
324 if (!(acp_readl(rtd->acp5x_base + ACP_HSTDM_ITER) & BIT(0)) &&
325 !(acp_readl(rtd->acp5x_base + ACP_HSTDM_IRER) & BIT(0)))
326 acp_writel(0, rtd->acp5x_base + ACP_HSTDM_IER);
327 if (!(acp_readl(rtd->acp5x_base + ACP_I2STDM_ITER) & BIT(0)) &&
328 !(acp_readl(rtd->acp5x_base + ACP_I2STDM_IRER) & BIT(0)))
329 acp_writel(0, rtd->acp5x_base + ACP_I2STDM_IER);
339 static const struct snd_soc_dai_ops acp5x_i2s_dai_ops = {
340 .hw_params = acp5x_i2s_hwparams,
341 .trigger = acp5x_i2s_trigger,
342 .set_fmt = acp5x_i2s_set_fmt,
343 .set_tdm_slot = acp5x_i2s_set_tdm_slot,
346 static const struct snd_soc_component_driver acp5x_dai_component = {
348 .legacy_dai_naming = 1,
351 static struct snd_soc_dai_driver acp5x_i2s_dai = {
353 .rates = SNDRV_PCM_RATE_8000_96000,
354 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
355 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
362 .rates = SNDRV_PCM_RATE_8000_96000,
363 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
364 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
370 .ops = &acp5x_i2s_dai_ops,
373 static int acp5x_dai_probe(struct platform_device *pdev)
375 struct resource *res;
376 struct i2s_dev_data *adata;
379 adata = devm_kzalloc(&pdev->dev, sizeof(struct i2s_dev_data),
384 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
386 dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
389 adata->acp5x_base = devm_ioremap(&pdev->dev, res->start,
391 if (!adata->acp5x_base)
394 adata->master_mode = I2S_MASTER_MODE_ENABLE;
395 dev_set_drvdata(&pdev->dev, adata);
396 ret = devm_snd_soc_register_component(&pdev->dev,
397 &acp5x_dai_component,
400 dev_err(&pdev->dev, "Fail to register acp i2s dai\n");
404 static struct platform_driver acp5x_dai_driver = {
405 .probe = acp5x_dai_probe,
407 .name = "acp5x_i2s_playcap",
411 module_platform_driver(acp5x_dai_driver);
413 MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
414 MODULE_DESCRIPTION("AMD ACP5.x CPU DAI Driver");
415 MODULE_ALIAS("platform:" DRV_NAME);
416 MODULE_LICENSE("GPL v2");