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>
7 #include <linux/dma-mapping.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"
19 unsigned char *dma_area
;
24 struct bcm63xx_runtime_data
{
27 dma_addr_t dma_addr_next
;
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,
39 .periods_max
= PAGE_SIZE
/sizeof(struct i2s_dma_desc
),
40 .buffer_bytes_max
= 128 * 1024,
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
)
48 struct i2s_dma_desc
*dma_desc
;
49 struct snd_soc_pcm_runtime
*rtd
= snd_soc_substream_to_rtd(substream
);
51 dma_desc
= kzalloc(sizeof(*dma_desc
), GFP_NOWAIT
);
55 snd_soc_dai_set_dma_data(snd_soc_rtd_to_cpu(rtd
, 0), substream
, dma_desc
);
60 static int bcm63xx_pcm_hw_free(struct snd_soc_component
*component
,
61 struct snd_pcm_substream
*substream
)
63 struct i2s_dma_desc
*dma_desc
;
64 struct snd_soc_pcm_runtime
*rtd
= snd_soc_substream_to_rtd(substream
);
66 dma_desc
= snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd
, 0), substream
);
72 static int bcm63xx_pcm_trigger(struct snd_soc_component
*component
,
73 struct snd_pcm_substream
*substream
, int cmd
)
76 struct snd_soc_pcm_runtime
*rtd
;
77 struct bcm_i2s_priv
*i2s_priv
;
78 struct regmap
*regmap_i2s
;
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
;
84 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
) {
86 case SNDRV_PCM_TRIGGER_START
:
87 regmap_update_bits(regmap_i2s
,
89 I2S_TX_DESC_OFF_INTR_EN
,
90 I2S_TX_DESC_OFF_INTR_EN
);
91 regmap_update_bits(regmap_i2s
,
96 case SNDRV_PCM_TRIGGER_STOP
:
97 case SNDRV_PCM_TRIGGER_SUSPEND
:
98 case SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
99 regmap_write(regmap_i2s
,
102 regmap_update_bits(regmap_i2s
,
112 case SNDRV_PCM_TRIGGER_START
:
113 regmap_update_bits(regmap_i2s
,
115 I2S_RX_DESC_OFF_INTR_EN_MSK
,
116 I2S_RX_DESC_OFF_INTR_EN
);
117 regmap_update_bits(regmap_i2s
,
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
,
127 I2S_RX_DESC_OFF_INTR_EN_MSK
,
129 regmap_update_bits(regmap_i2s
,
141 static int bcm63xx_pcm_prepare(struct snd_soc_component
*component
,
142 struct snd_pcm_substream
*substream
)
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
;
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
;
156 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
) {
157 regaddr_desclen
= I2S_TX_DESC_IFF_LEN
;
158 regaddr_descaddr
= I2S_TX_DESC_IFF_ADDR
;
160 regaddr_desclen
= I2S_RX_DESC_IFF_LEN
;
161 regaddr_descaddr
= I2S_RX_DESC_IFF_ADDR
;
164 i2s_priv
= dev_get_drvdata(snd_soc_rtd_to_cpu(rtd
, 0)->dev
);
165 regmap_i2s
= i2s_priv
->regmap_i2s
;
167 regmap_write(regmap_i2s
, regaddr_desclen
, dma_desc
->dma_len
);
168 regmap_write(regmap_i2s
, regaddr_descaddr
, dma_desc
->dma_addr
);
173 static snd_pcm_uframes_t
174 bcm63xx_pcm_pointer(struct snd_soc_component
*component
,
175 struct snd_pcm_substream
*substream
)
178 struct bcm63xx_runtime_data
*prtd
= substream
->runtime
->private_data
;
180 if (!prtd
->dma_addr_next
)
181 prtd
->dma_addr_next
= substream
->runtime
->dma_addr
;
183 x
= bytes_to_frames(substream
->runtime
,
184 prtd
->dma_addr_next
- substream
->runtime
->dma_addr
);
186 return x
== substream
->runtime
->buffer_size
? 0 : x
;
189 static int bcm63xx_pcm_open(struct snd_soc_component
*component
,
190 struct snd_pcm_substream
*substream
)
193 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
194 struct bcm63xx_runtime_data
*prtd
;
196 runtime
->hw
= bcm63xx_pcm_hardware
;
197 ret
= snd_pcm_hw_constraint_step(runtime
, 0,
198 SNDRV_PCM_HW_PARAM_PERIOD_BYTES
, 32);
202 ret
= snd_pcm_hw_constraint_step(runtime
, 0,
203 SNDRV_PCM_HW_PARAM_BUFFER_BYTES
, 32);
207 ret
= snd_pcm_hw_constraint_integer(runtime
,
208 SNDRV_PCM_HW_PARAM_PERIODS
);
213 prtd
= kzalloc(sizeof(*prtd
), GFP_KERNEL
);
217 runtime
->private_data
= prtd
;
223 static int bcm63xx_pcm_close(struct snd_soc_component
*component
,
224 struct snd_pcm_substream
*substream
)
226 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
227 struct bcm63xx_runtime_data
*prtd
= runtime
->private_data
;
233 static irqreturn_t
i2s_dma_isr(int irq
, void *bcm_i2s_priv
)
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
;
244 i2s_priv
= (struct bcm_i2s_priv
*)bcm_i2s_priv
;
245 regmap_i2s
= i2s_priv
->regmap_i2s
;
248 regmap_read(regmap_i2s
, I2S_RX_IRQ_CTL
, &int_status
);
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
);
257 offlevel
= (int_status
& I2S_RX_DESC_OFF_LEVEL_MASK
) >>
258 I2S_RX_DESC_OFF_LEVEL_SHIFT
;
259 bool val_read
= false;
261 regmap_read(regmap_i2s
, I2S_RX_DESC_OFF_ADDR
, &val_1
);
262 regmap_read(regmap_i2s
, I2S_RX_DESC_OFF_LEN
, &val_2
);
267 prtd
->dma_addr_next
= val_1
+ val_2
;
269 ifflevel
= (int_status
& I2S_RX_DESC_IFF_LEVEL_MASK
) >>
270 I2S_RX_DESC_IFF_LEVEL_SHIFT
;
272 availdepth
= I2S_DESC_FIFO_DEPTH
- ifflevel
;
274 dma_desc
->dma_addr
+=
275 snd_pcm_lib_period_bytes(substream
);
276 dma_desc
->dma_area
+=
277 snd_pcm_lib_period_bytes(substream
);
278 if (dma_desc
->dma_addr
- runtime
->dma_addr
>=
279 runtime
->dma_bytes
) {
280 dma_desc
->dma_addr
= runtime
->dma_addr
;
281 dma_desc
->dma_area
= runtime
->dma_area
;
284 prtd
->dma_addr
= dma_desc
->dma_addr
;
285 regmap_write(regmap_i2s
, I2S_RX_DESC_IFF_LEN
,
286 snd_pcm_lib_period_bytes(substream
));
287 regmap_write(regmap_i2s
, I2S_RX_DESC_IFF_ADDR
,
292 snd_pcm_period_elapsed(substream
);
294 /* Clear interrupt by writing 0 */
295 regmap_update_bits(regmap_i2s
, I2S_RX_IRQ_CTL
,
296 I2S_RX_INTR_MASK
, 0);
300 regmap_read(regmap_i2s
, I2S_TX_IRQ_CTL
, &int_status
);
302 if (int_status
& I2S_TX_DESC_OFF_INTR_EN_MSK
) {
303 substream
= i2s_priv
->play_substream
;
304 runtime
= substream
->runtime
;
305 rtd
= snd_soc_substream_to_rtd(substream
);
306 prtd
= runtime
->private_data
;
307 dma_desc
= snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd
, 0), substream
);
309 offlevel
= (int_status
& I2S_TX_DESC_OFF_LEVEL_MASK
) >>
310 I2S_TX_DESC_OFF_LEVEL_SHIFT
;
312 regmap_read(regmap_i2s
, I2S_TX_DESC_OFF_ADDR
, &val_1
);
313 regmap_read(regmap_i2s
, I2S_TX_DESC_OFF_LEN
, &val_2
);
314 prtd
->dma_addr_next
= val_1
+ val_2
;
318 ifflevel
= (int_status
& I2S_TX_DESC_IFF_LEVEL_MASK
) >>
319 I2S_TX_DESC_IFF_LEVEL_SHIFT
;
320 availdepth
= I2S_DESC_FIFO_DEPTH
- ifflevel
;
323 dma_desc
->dma_addr
+=
324 snd_pcm_lib_period_bytes(substream
);
325 dma_desc
->dma_area
+=
326 snd_pcm_lib_period_bytes(substream
);
328 if (dma_desc
->dma_addr
- runtime
->dma_addr
>=
329 runtime
->dma_bytes
) {
330 dma_desc
->dma_addr
= runtime
->dma_addr
;
331 dma_desc
->dma_area
= runtime
->dma_area
;
334 prtd
->dma_addr
= dma_desc
->dma_addr
;
335 regmap_write(regmap_i2s
, I2S_TX_DESC_IFF_LEN
,
336 snd_pcm_lib_period_bytes(substream
));
337 regmap_write(regmap_i2s
, I2S_TX_DESC_IFF_ADDR
,
342 snd_pcm_period_elapsed(substream
);
344 /* Clear interrupt by writing 0 */
345 regmap_update_bits(regmap_i2s
, I2S_TX_IRQ_CTL
,
346 I2S_TX_INTR_MASK
, 0);
352 static int bcm63xx_soc_pcm_new(struct snd_soc_component
*component
,
353 struct snd_soc_pcm_runtime
*rtd
)
355 struct snd_pcm
*pcm
= rtd
->pcm
;
356 struct bcm_i2s_priv
*i2s_priv
;
359 i2s_priv
= dev_get_drvdata(snd_soc_rtd_to_cpu(rtd
, 0)->dev
);
361 of_dma_configure(pcm
->card
->dev
, pcm
->card
->dev
->of_node
, 1);
363 ret
= dma_coerce_mask_and_coherent(pcm
->card
->dev
, DMA_BIT_MASK(32));
367 if (pcm
->streams
[SNDRV_PCM_STREAM_PLAYBACK
].substream
)
368 i2s_priv
->play_substream
=
369 pcm
->streams
[SNDRV_PCM_STREAM_PLAYBACK
].substream
;
370 if (pcm
->streams
[SNDRV_PCM_STREAM_CAPTURE
].substream
)
371 i2s_priv
->capture_substream
=
372 pcm
->streams
[SNDRV_PCM_STREAM_CAPTURE
].substream
;
374 return snd_pcm_set_fixed_buffer_all(pcm
, SNDRV_DMA_TYPE_DEV_WC
,
376 bcm63xx_pcm_hardware
.buffer_bytes_max
);
379 static const struct snd_soc_component_driver bcm63xx_soc_platform
= {
380 .open
= bcm63xx_pcm_open
,
381 .close
= bcm63xx_pcm_close
,
382 .hw_params
= bcm63xx_pcm_hw_params
,
383 .hw_free
= bcm63xx_pcm_hw_free
,
384 .prepare
= bcm63xx_pcm_prepare
,
385 .trigger
= bcm63xx_pcm_trigger
,
386 .pointer
= bcm63xx_pcm_pointer
,
387 .pcm_construct
= bcm63xx_soc_pcm_new
,
390 int bcm63xx_soc_platform_probe(struct platform_device
*pdev
,
391 struct bcm_i2s_priv
*i2s_priv
)
395 ret
= platform_get_irq(pdev
, 0);
399 ret
= devm_request_irq(&pdev
->dev
, ret
, i2s_dma_isr
,
400 irq_get_trigger_type(ret
), "i2s_dma", (void *)i2s_priv
);
403 "i2s_init: failed to request interrupt.ret=%d\n", ret
);
407 return devm_snd_soc_register_component(&pdev
->dev
,
408 &bcm63xx_soc_platform
, NULL
, 0);
411 int bcm63xx_soc_platform_remove(struct platform_device
*pdev
)
416 MODULE_AUTHOR("Kevin,Li <kevin-ke.li@broadcom.com>");
417 MODULE_DESCRIPTION("Broadcom DSL XPON ASOC PCM Interface");
418 MODULE_LICENSE("GPL v2");