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
;
237 ier_val
= mmACP_BTTDM_IER
;
239 case I2S_SP_INSTANCE
:
241 reg_val
= mmACP_I2STDM_ITER
;
242 ier_val
= mmACP_I2STDM_IER
;
246 switch (rtd
->i2s_instance
) {
247 case I2S_BT_INSTANCE
:
248 reg_val
= mmACP_BTTDM_IRER
;
249 ier_val
= mmACP_BTTDM_IER
;
251 case I2S_SP_INSTANCE
:
253 reg_val
= mmACP_I2STDM_IRER
;
254 ier_val
= mmACP_I2STDM_IER
;
257 val
= rv_readl(rtd
->acp3x_base
+ reg_val
);
259 rv_writel(val
, rtd
->acp3x_base
+ reg_val
);
260 rv_writel(0, rtd
->acp3x_base
+ ier_val
);
271 static struct snd_soc_dai_ops acp3x_i2s_dai_ops
= {
272 .hw_params
= acp3x_i2s_hwparams
,
273 .trigger
= acp3x_i2s_trigger
,
274 .set_fmt
= acp3x_i2s_set_fmt
,
275 .set_tdm_slot
= acp3x_i2s_set_tdm_slot
,
278 static const struct snd_soc_component_driver acp3x_dai_component
= {
282 static struct snd_soc_dai_driver acp3x_i2s_dai
= {
284 .rates
= SNDRV_PCM_RATE_8000_96000
,
285 .formats
= SNDRV_PCM_FMTBIT_S16_LE
| SNDRV_PCM_FMTBIT_S8
|
286 SNDRV_PCM_FMTBIT_U8
| SNDRV_PCM_FMTBIT_S24_LE
|
287 SNDRV_PCM_FMTBIT_S32_LE
,
294 .rates
= SNDRV_PCM_RATE_8000_48000
,
295 .formats
= SNDRV_PCM_FMTBIT_S16_LE
| SNDRV_PCM_FMTBIT_S8
|
296 SNDRV_PCM_FMTBIT_U8
| SNDRV_PCM_FMTBIT_S24_LE
|
297 SNDRV_PCM_FMTBIT_S32_LE
,
303 .ops
= &acp3x_i2s_dai_ops
,
306 static int acp3x_dai_probe(struct platform_device
*pdev
)
308 struct resource
*res
;
309 struct i2s_dev_data
*adata
;
312 adata
= devm_kzalloc(&pdev
->dev
, sizeof(struct i2s_dev_data
),
317 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
319 dev_err(&pdev
->dev
, "IORESOURCE_MEM FAILED\n");
322 adata
->acp3x_base
= devm_ioremap(&pdev
->dev
, res
->start
,
324 if (!adata
->acp3x_base
)
327 adata
->i2s_irq
= res
->start
;
328 dev_set_drvdata(&pdev
->dev
, adata
);
329 ret
= devm_snd_soc_register_component(&pdev
->dev
,
330 &acp3x_dai_component
, &acp3x_i2s_dai
, 1);
332 dev_err(&pdev
->dev
, "Fail to register acp i2s dai\n");
338 static int acp3x_dai_remove(struct platform_device
*pdev
)
340 /* As we use devm_ memory alloc there is nothing TBD here */
345 static struct platform_driver acp3x_dai_driver
= {
346 .probe
= acp3x_dai_probe
,
347 .remove
= acp3x_dai_remove
,
349 .name
= "acp3x_i2s_playcap",
353 module_platform_driver(acp3x_dai_driver
);
355 MODULE_AUTHOR("Vishnuvardhanrao.Ravulapati@amd.com");
356 MODULE_DESCRIPTION("AMD ACP 3.x PCM Driver");
357 MODULE_LICENSE("GPL v2");
358 MODULE_ALIAS("platform:" DRV_NAME
);