1 // SPDX-License-Identifier: GPL-2.0-or-later
5 * (c) 2010 Arnaud Patard <apatard@mandriva.com>
6 * (c) 2010 Arnaud Patard <arnaud.patard@rtp-net.org>
9 #include <linux/init.h>
10 #include <linux/module.h>
11 #include <linux/device.h>
13 #include <linux/slab.h>
14 #include <linux/interrupt.h>
15 #include <linux/dma-mapping.h>
16 #include <linux/mbus.h>
17 #include <sound/soc.h>
20 static struct kirkwood_dma_data
*kirkwood_priv(struct snd_pcm_substream
*subs
)
22 struct snd_soc_pcm_runtime
*soc_runtime
= snd_soc_substream_to_rtd(subs
);
23 return snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(soc_runtime
, 0));
26 static const struct snd_pcm_hardware kirkwood_dma_snd_hw
= {
27 .info
= SNDRV_PCM_INFO_INTERLEAVED
|
29 SNDRV_PCM_INFO_MMAP_VALID
|
30 SNDRV_PCM_INFO_BLOCK_TRANSFER
|
31 SNDRV_PCM_INFO_PAUSE
|
32 SNDRV_PCM_INFO_NO_PERIOD_WAKEUP
,
33 .buffer_bytes_max
= KIRKWOOD_SND_MAX_BUFFER_BYTES
,
34 .period_bytes_min
= KIRKWOOD_SND_MIN_PERIOD_BYTES
,
35 .period_bytes_max
= KIRKWOOD_SND_MAX_PERIOD_BYTES
,
36 .periods_min
= KIRKWOOD_SND_MIN_PERIODS
,
37 .periods_max
= KIRKWOOD_SND_MAX_PERIODS
,
41 static irqreturn_t
kirkwood_dma_irq(int irq
, void *dev_id
)
43 struct kirkwood_dma_data
*priv
= dev_id
;
44 unsigned long mask
, status
, cause
;
46 mask
= readl(priv
->io
+ KIRKWOOD_INT_MASK
);
47 status
= readl(priv
->io
+ KIRKWOOD_INT_CAUSE
) & mask
;
49 cause
= readl(priv
->io
+ KIRKWOOD_ERR_CAUSE
);
50 if (unlikely(cause
)) {
51 printk(KERN_WARNING
"%s: got err interrupt 0x%lx\n",
53 writel(cause
, priv
->io
+ KIRKWOOD_ERR_CAUSE
);
56 /* we've enabled only bytes interrupts ... */
57 if (status
& ~(KIRKWOOD_INT_CAUSE_PLAY_BYTES
| \
58 KIRKWOOD_INT_CAUSE_REC_BYTES
)) {
59 printk(KERN_WARNING
"%s: unexpected interrupt %lx\n",
65 writel(status
, priv
->io
+ KIRKWOOD_INT_CAUSE
);
67 if (status
& KIRKWOOD_INT_CAUSE_PLAY_BYTES
)
68 snd_pcm_period_elapsed(priv
->substream_play
);
70 if (status
& KIRKWOOD_INT_CAUSE_REC_BYTES
)
71 snd_pcm_period_elapsed(priv
->substream_rec
);
77 kirkwood_dma_conf_mbus_windows(void __iomem
*base
, int win
,
79 const struct mbus_dram_target_info
*dram
)
83 /* First disable and clear windows */
84 writel(0, base
+ KIRKWOOD_AUDIO_WIN_CTRL_REG(win
));
85 writel(0, base
+ KIRKWOOD_AUDIO_WIN_BASE_REG(win
));
87 /* try to find matching cs for current dma address */
88 for (i
= 0; i
< dram
->num_cs
; i
++) {
89 const struct mbus_dram_window
*cs
= &dram
->cs
[i
];
90 if ((cs
->base
& 0xffff0000) < (dma
& 0xffff0000)) {
91 writel(cs
->base
& 0xffff0000,
92 base
+ KIRKWOOD_AUDIO_WIN_BASE_REG(win
));
93 writel(((cs
->size
- 1) & 0xffff0000) |
94 (cs
->mbus_attr
<< 8) |
95 (dram
->mbus_dram_target_id
<< 4) | 1,
96 base
+ KIRKWOOD_AUDIO_WIN_CTRL_REG(win
));
101 static int kirkwood_dma_open(struct snd_soc_component
*component
,
102 struct snd_pcm_substream
*substream
)
105 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
106 struct kirkwood_dma_data
*priv
= kirkwood_priv(substream
);
108 snd_soc_set_runtime_hwparams(substream
, &kirkwood_dma_snd_hw
);
110 /* Ensure that all constraints linked to dma burst are fulfilled */
111 err
= snd_pcm_hw_constraint_minmax(runtime
,
112 SNDRV_PCM_HW_PARAM_BUFFER_BYTES
,
114 KIRKWOOD_AUDIO_BUF_MAX
-1);
118 err
= snd_pcm_hw_constraint_step(runtime
, 0,
119 SNDRV_PCM_HW_PARAM_BUFFER_BYTES
,
124 err
= snd_pcm_hw_constraint_step(substream
->runtime
, 0,
125 SNDRV_PCM_HW_PARAM_PERIOD_BYTES
,
130 if (!priv
->substream_play
&& !priv
->substream_rec
) {
131 err
= request_irq(priv
->irq
, kirkwood_dma_irq
, IRQF_SHARED
,
132 "kirkwood-i2s", priv
);
137 * Enable Error interrupts. We're only ack'ing them but
138 * it's useful for diagnostics
140 writel((unsigned int)-1, priv
->io
+ KIRKWOOD_ERR_MASK
);
143 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
) {
144 if (priv
->substream_play
)
146 priv
->substream_play
= substream
;
148 if (priv
->substream_rec
)
150 priv
->substream_rec
= substream
;
156 static int kirkwood_dma_close(struct snd_soc_component
*component
,
157 struct snd_pcm_substream
*substream
)
159 struct kirkwood_dma_data
*priv
= kirkwood_priv(substream
);
164 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
)
165 priv
->substream_play
= NULL
;
167 priv
->substream_rec
= NULL
;
169 if (!priv
->substream_play
&& !priv
->substream_rec
) {
170 writel(0, priv
->io
+ KIRKWOOD_ERR_MASK
);
171 free_irq(priv
->irq
, priv
);
177 static int kirkwood_dma_hw_params(struct snd_soc_component
*component
,
178 struct snd_pcm_substream
*substream
,
179 struct snd_pcm_hw_params
*params
)
181 struct kirkwood_dma_data
*priv
= kirkwood_priv(substream
);
182 const struct mbus_dram_target_info
*dram
= mv_mbus_dram_info();
183 unsigned long addr
= substream
->runtime
->dma_addr
;
188 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
)
189 kirkwood_dma_conf_mbus_windows(priv
->io
,
190 KIRKWOOD_PLAYBACK_WIN
, addr
, dram
);
192 kirkwood_dma_conf_mbus_windows(priv
->io
,
193 KIRKWOOD_RECORD_WIN
, addr
, dram
);
197 static int kirkwood_dma_prepare(struct snd_soc_component
*component
,
198 struct snd_pcm_substream
*substream
)
200 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
201 struct kirkwood_dma_data
*priv
= kirkwood_priv(substream
);
202 unsigned long size
, count
;
204 /* compute buffer size in term of "words" as requested in specs */
205 size
= frames_to_bytes(runtime
, runtime
->buffer_size
);
207 count
= snd_pcm_lib_period_bytes(substream
);
209 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
) {
210 writel(count
, priv
->io
+ KIRKWOOD_PLAY_BYTE_INT_COUNT
);
211 writel(runtime
->dma_addr
, priv
->io
+ KIRKWOOD_PLAY_BUF_ADDR
);
212 writel(size
, priv
->io
+ KIRKWOOD_PLAY_BUF_SIZE
);
214 writel(count
, priv
->io
+ KIRKWOOD_REC_BYTE_INT_COUNT
);
215 writel(runtime
->dma_addr
, priv
->io
+ KIRKWOOD_REC_BUF_ADDR
);
216 writel(size
, priv
->io
+ KIRKWOOD_REC_BUF_SIZE
);
223 static snd_pcm_uframes_t
kirkwood_dma_pointer(
224 struct snd_soc_component
*component
,
225 struct snd_pcm_substream
*substream
)
227 struct kirkwood_dma_data
*priv
= kirkwood_priv(substream
);
228 snd_pcm_uframes_t count
;
230 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
)
231 count
= bytes_to_frames(substream
->runtime
,
232 readl(priv
->io
+ KIRKWOOD_PLAY_BYTE_COUNT
));
234 count
= bytes_to_frames(substream
->runtime
,
235 readl(priv
->io
+ KIRKWOOD_REC_BYTE_COUNT
));
240 static int kirkwood_dma_new(struct snd_soc_component
*component
,
241 struct snd_soc_pcm_runtime
*rtd
)
243 size_t size
= kirkwood_dma_snd_hw
.buffer_bytes_max
;
244 struct snd_card
*card
= rtd
->card
->snd_card
;
247 ret
= dma_coerce_mask_and_coherent(card
->dev
, DMA_BIT_MASK(32));
251 snd_pcm_set_managed_buffer_all(rtd
->pcm
, SNDRV_DMA_TYPE_DEV
,
252 card
->dev
, size
, size
);
257 const struct snd_soc_component_driver kirkwood_soc_component
= {
259 .open
= kirkwood_dma_open
,
260 .close
= kirkwood_dma_close
,
261 .hw_params
= kirkwood_dma_hw_params
,
262 .prepare
= kirkwood_dma_prepare
,
263 .pointer
= kirkwood_dma_pointer
,
264 .pcm_construct
= kirkwood_dma_new
,