1 // SPDX-License-Identifier: GPL-2.0+
3 // AMD ALSA SoC PCM Driver
5 //Copyright 2016 Advanced Micro Devices, Inc.
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 "acp3x-i2s"
20 static int acp3x_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;
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)
44 struct i2s_dev_data *adata;
45 u32 val, reg_val, frmt_reg, frm_len;
48 adata = snd_soc_dai_get_drvdata(cpu_dai);
50 /* These values are as per Hardware Spec */
68 /* Enable I2S/BT channels TDM, respective TX/RX frame lengths.*/
70 frm_len = FRM_LEN | (slots << 15) | (slot_len << 18);
71 if (adata->substream_type == SNDRV_PCM_STREAM_PLAYBACK) {
72 switch (adata->i2s_instance) {
74 reg_val = mmACP_BTTDM_ITER;
75 frmt_reg = mmACP_BTTDM_TXFRMT;
79 reg_val = mmACP_I2STDM_ITER;
80 frmt_reg = mmACP_I2STDM_TXFRMT;
83 switch (adata->i2s_instance) {
85 reg_val = mmACP_BTTDM_IRER;
86 frmt_reg = mmACP_BTTDM_RXFRMT;
90 reg_val = mmACP_I2STDM_IRER;
91 frmt_reg = mmACP_I2STDM_RXFRMT;
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;
101 static int acp3x_i2s_hwparams(struct snd_pcm_substream *substream,
102 struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
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;
111 prtd = substream->private_data;
112 rtd = substream->runtime->private_data;
114 pinfo = snd_soc_card_get_drvdata(card);
116 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
117 rtd->i2s_instance = pinfo->play_i2s_instance;
119 rtd->i2s_instance = pinfo->cap_i2s_instance;
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;
128 case SNDRV_PCM_FORMAT_S16_LE:
129 rtd->xfer_resolution = 0x02;
131 case SNDRV_PCM_FORMAT_S24_LE:
132 rtd->xfer_resolution = 0x04;
134 case SNDRV_PCM_FORMAT_S32_LE:
135 rtd->xfer_resolution = 0x05;
140 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
141 switch (rtd->i2s_instance) {
142 case I2S_BT_INSTANCE:
143 reg_val = mmACP_BTTDM_ITER;
145 case I2S_SP_INSTANCE:
147 reg_val = mmACP_I2STDM_ITER;
150 switch (rtd->i2s_instance) {
151 case I2S_BT_INSTANCE:
152 reg_val = mmACP_BTTDM_IRER;
154 case I2S_SP_INSTANCE:
156 reg_val = mmACP_I2STDM_IRER;
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);
165 static int acp3x_i2s_trigger(struct snd_pcm_substream *substream,
166 int cmd, struct snd_soc_dai *dai)
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;
174 prtd = substream->private_data;
175 rtd = substream->runtime->private_data;
177 pinfo = snd_soc_card_get_drvdata(card);
179 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
180 rtd->i2s_instance = pinfo->play_i2s_instance;
182 rtd->i2s_instance = pinfo->cap_i2s_instance;
184 period_bytes = frames_to_bytes(substream->runtime,
185 substream->runtime->period_size);
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,
192 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
193 switch (rtd->i2s_instance) {
194 case I2S_BT_INSTANCE:
196 mmACP_BT_TX_INTR_WATERMARK_SIZE;
197 reg_val = mmACP_BTTDM_ITER;
198 ier_val = mmACP_BTTDM_IER;
200 case I2S_SP_INSTANCE:
203 mmACP_I2S_TX_INTR_WATERMARK_SIZE;
204 reg_val = mmACP_I2STDM_ITER;
205 ier_val = mmACP_I2STDM_IER;
208 switch (rtd->i2s_instance) {
209 case I2S_BT_INSTANCE:
211 mmACP_BT_RX_INTR_WATERMARK_SIZE;
212 reg_val = mmACP_BTTDM_IRER;
213 ier_val = mmACP_BTTDM_IER;
215 case I2S_SP_INSTANCE:
218 mmACP_I2S_RX_INTR_WATERMARK_SIZE;
219 reg_val = mmACP_I2STDM_IRER;
220 ier_val = mmACP_I2STDM_IER;
223 rv_writel(period_bytes, rtd->acp3x_base + water_val);
224 val = rv_readl(rtd->acp3x_base + reg_val);
226 rv_writel(val, rtd->acp3x_base + reg_val);
227 rv_writel(1, rtd->acp3x_base + ier_val);
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;
238 case I2S_SP_INSTANCE:
240 reg_val = mmACP_I2STDM_ITER;
244 switch (rtd->i2s_instance) {
245 case I2S_BT_INSTANCE:
246 reg_val = mmACP_BTTDM_IRER;
248 case I2S_SP_INSTANCE:
250 reg_val = mmACP_I2STDM_IRER;
253 val = rv_readl(rtd->acp3x_base + reg_val);
255 rv_writel(val, rtd->acp3x_base + reg_val);
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);
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,
280 static const struct snd_soc_component_driver acp3x_dai_component = {
284 static struct snd_soc_dai_driver acp3x_i2s_dai = {
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,
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,
305 .ops = &acp3x_i2s_dai_ops,
308 static int acp3x_dai_probe(struct platform_device *pdev)
310 struct resource *res;
311 struct i2s_dev_data *adata;
314 adata = devm_kzalloc(&pdev->dev, sizeof(struct i2s_dev_data),
319 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
321 dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
324 adata->acp3x_base = devm_ioremap(&pdev->dev, res->start,
326 if (!adata->acp3x_base)
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);
334 dev_err(&pdev->dev, "Fail to register acp i2s dai\n");
340 static int acp3x_dai_remove(struct platform_device *pdev)
342 /* As we use devm_ memory alloc there is nothing TBD here */
347 static struct platform_driver acp3x_dai_driver = {
348 .probe = acp3x_dai_probe,
349 .remove = acp3x_dai_remove,
351 .name = "acp3x_i2s_playcap",
355 module_platform_driver(acp3x_dai_driver);
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);