treewide: remove redundant IS_ERR() before error code check
[linux/fpc-iii.git] / sound / soc / amd / raven / acp3x-i2s.c
blobbf51cadf86821b8f25f7dcae16910e07dccc504f
1 // SPDX-License-Identifier: GPL-2.0+
2 //
3 // AMD ALSA SoC PCM Driver
4 //
5 //Copyright 2016 Advanced Micro Devices, Inc.
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>
16 #include "acp3x.h"
18 #define DRV_NAME "acp3x-i2s"
20 static int acp3x_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
21 unsigned int fmt)
23 struct i2s_dev_data *adata;
24 int mode;
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;
38 return 0;
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;
46 u16 slot_len;
48 adata = snd_soc_dai_get_drvdata(cpu_dai);
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;
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) {
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;
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;
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;
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;
108 u32 val;
109 u32 reg_val;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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 ier_val = mmACP_BTTDM_IER;
238 break;
239 case I2S_SP_INSTANCE:
240 default:
241 reg_val = mmACP_I2STDM_ITER;
242 ier_val = mmACP_I2STDM_IER;
245 } else {
246 switch (rtd->i2s_instance) {
247 case I2S_BT_INSTANCE:
248 reg_val = mmACP_BTTDM_IRER;
249 ier_val = mmACP_BTTDM_IER;
250 break;
251 case I2S_SP_INSTANCE:
252 default:
253 reg_val = mmACP_I2STDM_IRER;
254 ier_val = mmACP_I2STDM_IER;
257 val = rv_readl(rtd->acp3x_base + reg_val);
258 val = val & ~BIT(0);
259 rv_writel(val, rtd->acp3x_base + reg_val);
260 rv_writel(0, rtd->acp3x_base + ier_val);
261 ret = 0;
262 break;
263 default:
264 ret = -EINVAL;
265 break;
268 return ret;
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 = {
279 .name = "acp3x-i2s",
282 static struct snd_soc_dai_driver acp3x_i2s_dai = {
283 .playback = {
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,
288 .channels_min = 2,
289 .channels_max = 8,
290 .rate_min = 8000,
291 .rate_max = 96000,
293 .capture = {
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,
298 .channels_min = 2,
299 .channels_max = 2,
300 .rate_min = 8000,
301 .rate_max = 48000,
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;
310 int ret;
312 adata = devm_kzalloc(&pdev->dev, sizeof(struct i2s_dev_data),
313 GFP_KERNEL);
314 if (!adata)
315 return -ENOMEM;
317 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
318 if (!res) {
319 dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
320 return -ENOMEM;
322 adata->acp3x_base = devm_ioremap(&pdev->dev, res->start,
323 resource_size(res));
324 if (!adata->acp3x_base)
325 return -ENOMEM;
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);
331 if (ret) {
332 dev_err(&pdev->dev, "Fail to register acp i2s dai\n");
333 return -ENODEV;
335 return 0;
338 static int acp3x_dai_remove(struct platform_device *pdev)
340 /* As we use devm_ memory alloc there is nothing TBD here */
342 return 0;
345 static struct platform_driver acp3x_dai_driver = {
346 .probe = acp3x_dai_probe,
347 .remove = acp3x_dai_remove,
348 .driver = {
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);