~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/soc/bcm/bcm63xx-pcm-whistler.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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");
415 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php