Linux 6.7-rc7
[linux-modified.git] / sound / soc / bcm / bcm63xx-pcm-whistler.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 // linux/sound/bcm/bcm63xx-pcm-whistler.c
3 // BCM63xx whistler pcm interface
4 // Copyright (c) 2020 Broadcom Corporation
5 // Author: Kevin-Ke Li <kevin-ke.li@broadcom.com>
6
7 #include <linux/dma-mapping.h>
8 #include <linux/io.h>
9 #include <linux/irq.h>
10 #include <linux/module.h>
11 #include <sound/pcm_params.h>
12 #include <linux/regmap.h>
13 #include <linux/of_device.h>
14 #include <sound/soc.h>
15 #include "bcm63xx-i2s.h"
16
17
18 struct i2s_dma_desc {
19         unsigned char *dma_area;
20         dma_addr_t dma_addr;
21         unsigned int dma_len;
22 };
23
24 struct bcm63xx_runtime_data {
25         int dma_len;
26         dma_addr_t dma_addr;
27         dma_addr_t dma_addr_next;
28 };
29
30 static const struct snd_pcm_hardware bcm63xx_pcm_hardware = {
31         .info = SNDRV_PCM_INFO_MMAP |
32                 SNDRV_PCM_INFO_MMAP_VALID |
33                 SNDRV_PCM_INFO_INTERLEAVED |
34                 SNDRV_PCM_INFO_PAUSE |
35                 SNDRV_PCM_INFO_RESUME,
36         .formats = SNDRV_PCM_FMTBIT_S32_LE, /* support S32 only */
37         .period_bytes_max = 8192 - 32,
38         .periods_min = 1,
39         .periods_max = PAGE_SIZE/sizeof(struct i2s_dma_desc),
40         .buffer_bytes_max = 128 * 1024,
41         .fifo_size = 32,
42 };
43
44 static int bcm63xx_pcm_hw_params(struct snd_soc_component *component,
45                                  struct snd_pcm_substream *substream,
46                                  struct snd_pcm_hw_params *params)
47 {
48         struct i2s_dma_desc *dma_desc;
49         struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
50
51         dma_desc = kzalloc(sizeof(*dma_desc), GFP_NOWAIT);
52         if (!dma_desc)
53                 return -ENOMEM;
54
55         snd_soc_dai_set_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream, dma_desc);
56
57         return 0;
58 }
59
60 static int bcm63xx_pcm_hw_free(struct snd_soc_component *component,
61                         struct snd_pcm_substream *substream)
62 {
63         struct i2s_dma_desc     *dma_desc;
64         struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
65
66         dma_desc = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream);
67         kfree(dma_desc);
68
69         return 0;
70 }
71
72 static int bcm63xx_pcm_trigger(struct snd_soc_component *component,
73                                struct snd_pcm_substream *substream, int cmd)
74 {
75         int ret = 0;
76         struct snd_soc_pcm_runtime *rtd;
77         struct bcm_i2s_priv *i2s_priv;
78         struct regmap   *regmap_i2s;
79
80         rtd = snd_soc_substream_to_rtd(substream);
81         i2s_priv = dev_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0)->dev);
82         regmap_i2s = i2s_priv->regmap_i2s;
83
84         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
85                 switch (cmd) {
86                 case SNDRV_PCM_TRIGGER_START:
87                         regmap_update_bits(regmap_i2s,
88                                            I2S_TX_IRQ_EN,
89                                            I2S_TX_DESC_OFF_INTR_EN,
90                                            I2S_TX_DESC_OFF_INTR_EN);
91                         regmap_update_bits(regmap_i2s,
92                                            I2S_TX_CFG,
93                                            I2S_TX_ENABLE_MASK,
94                                            I2S_TX_ENABLE);
95                         break;
96                 case SNDRV_PCM_TRIGGER_STOP:
97                 case SNDRV_PCM_TRIGGER_SUSPEND:
98                 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
99                         regmap_write(regmap_i2s,
100                                      I2S_TX_IRQ_EN,
101                                      0);
102                         regmap_update_bits(regmap_i2s,
103                                            I2S_TX_CFG,
104                                            I2S_TX_ENABLE_MASK,
105                                            0);
106                         break;
107                 default:
108                         ret = -EINVAL;
109                 }
110         } else {
111                 switch (cmd) {
112                 case SNDRV_PCM_TRIGGER_START:
113                         regmap_update_bits(regmap_i2s,
114                                            I2S_RX_IRQ_EN,
115                                            I2S_RX_DESC_OFF_INTR_EN_MSK,
116                                            I2S_RX_DESC_OFF_INTR_EN);
117                         regmap_update_bits(regmap_i2s,
118                                            I2S_RX_CFG,
119                                            I2S_RX_ENABLE_MASK,
120                                            I2S_RX_ENABLE);
121                         break;
122                 case SNDRV_PCM_TRIGGER_STOP:
123                 case SNDRV_PCM_TRIGGER_SUSPEND:
124                 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
125                         regmap_update_bits(regmap_i2s,
126                                            I2S_RX_IRQ_EN,
127                                            I2S_RX_DESC_OFF_INTR_EN_MSK,
128                                            0);
129                         regmap_update_bits(regmap_i2s,
130                                            I2S_RX_CFG,
131                                            I2S_RX_ENABLE_MASK,
132                                            0);
133                         break;
134                 default:
135                         ret = -EINVAL;
136                 }
137         }
138         return ret;
139 }
140
141 static int bcm63xx_pcm_prepare(struct snd_soc_component *component,
142                         struct snd_pcm_substream *substream)
143 {
144         struct i2s_dma_desc     *dma_desc;
145         struct regmap           *regmap_i2s;
146         struct bcm_i2s_priv     *i2s_priv;
147         struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
148         struct snd_pcm_runtime *runtime = substream->runtime;
149         uint32_t regaddr_desclen, regaddr_descaddr;
150
151         dma_desc = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream);
152         dma_desc->dma_len  = snd_pcm_lib_period_bytes(substream);
153         dma_desc->dma_addr = runtime->dma_addr;
154         dma_desc->dma_area = runtime->dma_area;
155
156         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
157                 regaddr_desclen = I2S_TX_DESC_IFF_LEN;
158                 regaddr_descaddr = I2S_TX_DESC_IFF_ADDR;
159         } else {
160                 regaddr_desclen = I2S_RX_DESC_IFF_LEN;
161                 regaddr_descaddr = I2S_RX_DESC_IFF_ADDR;
162         }
163
164         i2s_priv = dev_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0)->dev);
165         regmap_i2s = i2s_priv->regmap_i2s;
166
167         regmap_write(regmap_i2s, regaddr_desclen, dma_desc->dma_len);
168         regmap_write(regmap_i2s, regaddr_descaddr, dma_desc->dma_addr);
169
170         return 0;
171 }
172
173 static snd_pcm_uframes_t
174 bcm63xx_pcm_pointer(struct snd_soc_component *component,
175                 struct snd_pcm_substream *substream)
176 {
177         snd_pcm_uframes_t x;
178         struct bcm63xx_runtime_data *prtd = substream->runtime->private_data;
179
180         if (!prtd->dma_addr_next)
181                 prtd->dma_addr_next = substream->runtime->dma_addr;
182
183         x = bytes_to_frames(substream->runtime,
184                 prtd->dma_addr_next - substream->runtime->dma_addr);
185
186         return x == substream->runtime->buffer_size ? 0 : x;
187 }
188
189 static int bcm63xx_pcm_open(struct snd_soc_component *component,
190                         struct snd_pcm_substream *substream)
191 {
192         int ret = 0;
193         struct snd_pcm_runtime *runtime = substream->runtime;
194         struct bcm63xx_runtime_data *prtd;
195
196         runtime->hw = bcm63xx_pcm_hardware;
197         ret = snd_pcm_hw_constraint_step(runtime, 0,
198                                          SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
199         if (ret)
200                 goto out;
201
202         ret = snd_pcm_hw_constraint_step(runtime, 0,
203                                          SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);
204         if (ret)
205                 goto out;
206
207         ret = snd_pcm_hw_constraint_integer(runtime,
208                                             SNDRV_PCM_HW_PARAM_PERIODS);
209         if (ret < 0)
210                 goto out;
211
212         ret = -ENOMEM;
213         prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
214         if (!prtd)
215                 goto out;
216
217         runtime->private_data = prtd;
218         return 0;
219 out:
220         return ret;
221 }
222
223 static int bcm63xx_pcm_close(struct snd_soc_component *component,
224                         struct snd_pcm_substream *substream)
225 {
226         struct snd_pcm_runtime *runtime = substream->runtime;
227         struct bcm63xx_runtime_data *prtd = runtime->private_data;
228
229         kfree(prtd);
230         return 0;
231 }
232
233 static irqreturn_t i2s_dma_isr(int irq, void *bcm_i2s_priv)
234 {
235         unsigned int availdepth, ifflevel, offlevel, int_status, val_1, val_2;
236         struct bcm63xx_runtime_data *prtd;
237         struct snd_pcm_substream *substream;
238         struct snd_pcm_runtime *runtime;
239         struct regmap *regmap_i2s;
240         struct i2s_dma_desc *dma_desc;
241         struct snd_soc_pcm_runtime *rtd;
242         struct bcm_i2s_priv *i2s_priv;
243
244         i2s_priv = (struct bcm_i2s_priv *)bcm_i2s_priv;
245         regmap_i2s = i2s_priv->regmap_i2s;
246
247         /* rx */
248         regmap_read(regmap_i2s, I2S_RX_IRQ_CTL, &int_status);
249
250         if (int_status & I2S_RX_DESC_OFF_INTR_EN_MSK) {
251                 substream = i2s_priv->capture_substream;
252                 runtime = substream->runtime;
253                 rtd = snd_soc_substream_to_rtd(substream);
254                 prtd = runtime->private_data;
255                 dma_desc = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream);
256
257                 offlevel = (int_status & I2S_RX_DESC_OFF_LEVEL_MASK) >>
258                            I2S_RX_DESC_OFF_LEVEL_SHIFT;
259                 while (offlevel) {
260                         regmap_read(regmap_i2s, I2S_RX_DESC_OFF_ADDR, &val_1);
261                         regmap_read(regmap_i2s, I2S_RX_DESC_OFF_LEN, &val_2);
262                         offlevel--;
263                 }
264                 prtd->dma_addr_next = val_1 + val_2;
265                 ifflevel = (int_status & I2S_RX_DESC_IFF_LEVEL_MASK) >>
266                            I2S_RX_DESC_IFF_LEVEL_SHIFT;
267
268                 availdepth = I2S_DESC_FIFO_DEPTH - ifflevel;
269                 while (availdepth) {
270                         dma_desc->dma_addr +=
271                                         snd_pcm_lib_period_bytes(substream);
272                         dma_desc->dma_area +=
273                                         snd_pcm_lib_period_bytes(substream);
274                         if (dma_desc->dma_addr - runtime->dma_addr >=
275                                                 runtime->dma_bytes) {
276                                 dma_desc->dma_addr = runtime->dma_addr;
277                                 dma_desc->dma_area = runtime->dma_area;
278                         }
279
280                         prtd->dma_addr = dma_desc->dma_addr;
281                         regmap_write(regmap_i2s, I2S_RX_DESC_IFF_LEN,
282                                      snd_pcm_lib_period_bytes(substream));
283                         regmap_write(regmap_i2s, I2S_RX_DESC_IFF_ADDR,
284                                      dma_desc->dma_addr);
285                         availdepth--;
286                 }
287
288                 snd_pcm_period_elapsed(substream);
289
290                 /* Clear interrupt by writing 0 */
291                 regmap_update_bits(regmap_i2s, I2S_RX_IRQ_CTL,
292                                    I2S_RX_INTR_MASK, 0);
293         }
294
295         /* tx */
296         regmap_read(regmap_i2s, I2S_TX_IRQ_CTL, &int_status);
297
298         if (int_status & I2S_TX_DESC_OFF_INTR_EN_MSK) {
299                 substream = i2s_priv->play_substream;
300                 runtime = substream->runtime;
301                 rtd = snd_soc_substream_to_rtd(substream);
302                 prtd = runtime->private_data;
303                 dma_desc = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream);
304
305                 offlevel = (int_status & I2S_TX_DESC_OFF_LEVEL_MASK) >>
306                            I2S_TX_DESC_OFF_LEVEL_SHIFT;
307                 while (offlevel) {
308                         regmap_read(regmap_i2s, I2S_TX_DESC_OFF_ADDR, &val_1);
309                         regmap_read(regmap_i2s, I2S_TX_DESC_OFF_LEN,  &val_2);
310                         prtd->dma_addr_next = val_1 + val_2;
311                         offlevel--;
312                 }
313
314                 ifflevel = (int_status & I2S_TX_DESC_IFF_LEVEL_MASK) >>
315                         I2S_TX_DESC_IFF_LEVEL_SHIFT;
316                 availdepth = I2S_DESC_FIFO_DEPTH - ifflevel;
317
318                 while (availdepth) {
319                         dma_desc->dma_addr +=
320                                         snd_pcm_lib_period_bytes(substream);
321                         dma_desc->dma_area +=
322                                         snd_pcm_lib_period_bytes(substream);
323
324                         if (dma_desc->dma_addr - runtime->dma_addr >=
325                                                         runtime->dma_bytes) {
326                                 dma_desc->dma_addr = runtime->dma_addr;
327                                 dma_desc->dma_area = runtime->dma_area;
328                         }
329
330                         prtd->dma_addr = dma_desc->dma_addr;
331                         regmap_write(regmap_i2s, I2S_TX_DESC_IFF_LEN,
332                                 snd_pcm_lib_period_bytes(substream));
333                         regmap_write(regmap_i2s, I2S_TX_DESC_IFF_ADDR,
334                                         dma_desc->dma_addr);
335                         availdepth--;
336                 }
337
338                 snd_pcm_period_elapsed(substream);
339
340                 /* Clear interrupt by writing 0 */
341                 regmap_update_bits(regmap_i2s, I2S_TX_IRQ_CTL,
342                                    I2S_TX_INTR_MASK, 0);
343         }
344
345         return IRQ_HANDLED;
346 }
347
348 static int bcm63xx_soc_pcm_new(struct snd_soc_component *component,
349                 struct snd_soc_pcm_runtime *rtd)
350 {
351         struct snd_pcm *pcm = rtd->pcm;
352         struct bcm_i2s_priv *i2s_priv;
353         int ret;
354
355         i2s_priv = dev_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0)->dev);
356
357         of_dma_configure(pcm->card->dev, pcm->card->dev->of_node, 1);
358
359         ret = dma_coerce_mask_and_coherent(pcm->card->dev, DMA_BIT_MASK(32));
360         if (ret)
361                 return ret;
362
363         if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream)
364                 i2s_priv->play_substream =
365                         pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
366         if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream)
367                 i2s_priv->capture_substream =
368                         pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
369
370         return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_WC,
371                                             pcm->card->dev,
372                                             bcm63xx_pcm_hardware.buffer_bytes_max);
373 }
374
375 static const struct snd_soc_component_driver bcm63xx_soc_platform = {
376         .open = bcm63xx_pcm_open,
377         .close = bcm63xx_pcm_close,
378         .hw_params = bcm63xx_pcm_hw_params,
379         .hw_free = bcm63xx_pcm_hw_free,
380         .prepare = bcm63xx_pcm_prepare,
381         .trigger = bcm63xx_pcm_trigger,
382         .pointer = bcm63xx_pcm_pointer,
383         .pcm_construct = bcm63xx_soc_pcm_new,
384 };
385
386 int bcm63xx_soc_platform_probe(struct platform_device *pdev,
387                                struct bcm_i2s_priv *i2s_priv)
388 {
389         int ret;
390
391         ret = platform_get_irq(pdev, 0);
392         if (ret < 0)
393                 return ret;
394
395         ret = devm_request_irq(&pdev->dev, ret, i2s_dma_isr,
396                                irq_get_trigger_type(ret), "i2s_dma", (void *)i2s_priv);
397         if (ret) {
398                 dev_err(&pdev->dev,
399                         "i2s_init: failed to request interrupt.ret=%d\n", ret);
400                 return ret;
401         }
402
403         return devm_snd_soc_register_component(&pdev->dev,
404                                         &bcm63xx_soc_platform, NULL, 0);
405 }
406
407 int bcm63xx_soc_platform_remove(struct platform_device *pdev)
408 {
409         return 0;
410 }
411
412 MODULE_AUTHOR("Kevin,Li <kevin-ke.li@broadcom.com>");
413 MODULE_DESCRIPTION("Broadcom DSL XPON ASOC PCM Interface");
414 MODULE_LICENSE("GPL v2");