1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Copyright 2011 Broadcom Corporation. All rights reserved. */ |
3 | |
4 | #include <linux/interrupt.h> |
5 | #include <linux/slab.h> |
6 | |
7 | #include <sound/asoundef.h> |
8 | |
9 | #include "bcm2835.h" |
10 | |
11 | /* hardware definition */ |
12 | static const struct snd_pcm_hardware snd_bcm2835_playback_hw = { |
13 | .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | |
14 | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | |
15 | SNDRV_PCM_INFO_SYNC_APPLPTR | SNDRV_PCM_INFO_BATCH), |
16 | .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, |
17 | .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_192000, |
18 | .rate_min = 8000, |
19 | .rate_max = 192000, |
20 | .channels_min = 1, |
21 | .channels_max = 8, |
22 | .buffer_bytes_max = 512 * 1024, |
23 | .period_bytes_min = 1 * 1024, |
24 | .period_bytes_max = 512 * 1024, |
25 | .periods_min = 1, |
26 | .periods_max = 128, |
27 | }; |
28 | |
29 | static const struct snd_pcm_hardware snd_bcm2835_playback_spdif_hw = { |
30 | .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | |
31 | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | |
32 | SNDRV_PCM_INFO_SYNC_APPLPTR | SNDRV_PCM_INFO_BATCH), |
33 | .formats = SNDRV_PCM_FMTBIT_S16_LE, |
34 | .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_44100 | |
35 | SNDRV_PCM_RATE_48000, |
36 | .rate_min = 44100, |
37 | .rate_max = 48000, |
38 | .channels_min = 2, |
39 | .channels_max = 2, |
40 | .buffer_bytes_max = 128 * 1024, |
41 | .period_bytes_min = 1 * 1024, |
42 | .period_bytes_max = 128 * 1024, |
43 | .periods_min = 1, |
44 | .periods_max = 128, |
45 | }; |
46 | |
47 | static void snd_bcm2835_playback_free(struct snd_pcm_runtime *runtime) |
48 | { |
49 | kfree(objp: runtime->private_data); |
50 | } |
51 | |
52 | void bcm2835_playback_fifo(struct bcm2835_alsa_stream *alsa_stream, |
53 | unsigned int bytes) |
54 | { |
55 | struct snd_pcm_substream *substream = alsa_stream->substream; |
56 | unsigned int pos; |
57 | |
58 | if (!alsa_stream->period_size) |
59 | return; |
60 | |
61 | if (bytes >= alsa_stream->buffer_size) { |
62 | snd_pcm_stream_lock(substream); |
63 | snd_pcm_stop(substream, |
64 | status: alsa_stream->draining ? |
65 | SNDRV_PCM_STATE_SETUP : |
66 | SNDRV_PCM_STATE_XRUN); |
67 | snd_pcm_stream_unlock(substream); |
68 | return; |
69 | } |
70 | |
71 | pos = atomic_read(v: &alsa_stream->pos); |
72 | pos += bytes; |
73 | pos %= alsa_stream->buffer_size; |
74 | atomic_set(v: &alsa_stream->pos, i: pos); |
75 | |
76 | alsa_stream->period_offset += bytes; |
77 | alsa_stream->interpolate_start = ktime_get(); |
78 | if (alsa_stream->period_offset >= alsa_stream->period_size) { |
79 | alsa_stream->period_offset %= alsa_stream->period_size; |
80 | snd_pcm_period_elapsed(substream); |
81 | } |
82 | } |
83 | |
84 | /* open callback */ |
85 | static int snd_bcm2835_playback_open_generic(struct snd_pcm_substream *substream, int spdif) |
86 | { |
87 | struct bcm2835_chip *chip = snd_pcm_substream_chip(substream); |
88 | struct snd_pcm_runtime *runtime = substream->runtime; |
89 | struct bcm2835_alsa_stream *alsa_stream; |
90 | int idx; |
91 | int err; |
92 | |
93 | mutex_lock(&chip->audio_mutex); |
94 | idx = substream->number; |
95 | |
96 | if (spdif && chip->opened) { |
97 | err = -EBUSY; |
98 | goto out; |
99 | } else if (!spdif && (chip->opened & (1 << idx))) { |
100 | err = -EBUSY; |
101 | goto out; |
102 | } |
103 | if (idx >= MAX_SUBSTREAMS) { |
104 | dev_err(chip->dev, |
105 | "substream(%d) device doesn't exist max(%d) substreams allowed\n" , |
106 | idx, MAX_SUBSTREAMS); |
107 | err = -ENODEV; |
108 | goto out; |
109 | } |
110 | |
111 | alsa_stream = kzalloc(size: sizeof(*alsa_stream), GFP_KERNEL); |
112 | if (!alsa_stream) { |
113 | err = -ENOMEM; |
114 | goto out; |
115 | } |
116 | |
117 | /* Initialise alsa_stream */ |
118 | alsa_stream->chip = chip; |
119 | alsa_stream->substream = substream; |
120 | alsa_stream->idx = idx; |
121 | |
122 | err = bcm2835_audio_open(alsa_stream); |
123 | if (err) { |
124 | kfree(objp: alsa_stream); |
125 | goto out; |
126 | } |
127 | runtime->private_data = alsa_stream; |
128 | runtime->private_free = snd_bcm2835_playback_free; |
129 | if (spdif) { |
130 | runtime->hw = snd_bcm2835_playback_spdif_hw; |
131 | } else { |
132 | /* clear spdif status, as we are not in spdif mode */ |
133 | chip->spdif_status = 0; |
134 | runtime->hw = snd_bcm2835_playback_hw; |
135 | } |
136 | /* minimum 16 bytes alignment (for vchiq bulk transfers) */ |
137 | snd_pcm_hw_constraint_step(runtime, |
138 | cond: 0, |
139 | SNDRV_PCM_HW_PARAM_PERIOD_BYTES, |
140 | step: 16); |
141 | |
142 | /* position update is in 10ms order */ |
143 | snd_pcm_hw_constraint_minmax(runtime, |
144 | SNDRV_PCM_HW_PARAM_PERIOD_TIME, |
145 | min: 10 * 1000, UINT_MAX); |
146 | |
147 | chip->alsa_stream[idx] = alsa_stream; |
148 | |
149 | chip->opened |= (1 << idx); |
150 | |
151 | out: |
152 | mutex_unlock(lock: &chip->audio_mutex); |
153 | |
154 | return err; |
155 | } |
156 | |
157 | static int snd_bcm2835_playback_open(struct snd_pcm_substream *substream) |
158 | { |
159 | return snd_bcm2835_playback_open_generic(substream, spdif: 0); |
160 | } |
161 | |
162 | static int snd_bcm2835_playback_spdif_open(struct snd_pcm_substream *substream) |
163 | { |
164 | return snd_bcm2835_playback_open_generic(substream, spdif: 1); |
165 | } |
166 | |
167 | static int snd_bcm2835_playback_close(struct snd_pcm_substream *substream) |
168 | { |
169 | struct bcm2835_alsa_stream *alsa_stream; |
170 | struct snd_pcm_runtime *runtime; |
171 | struct bcm2835_chip *chip; |
172 | |
173 | chip = snd_pcm_substream_chip(substream); |
174 | mutex_lock(&chip->audio_mutex); |
175 | runtime = substream->runtime; |
176 | alsa_stream = runtime->private_data; |
177 | |
178 | alsa_stream->period_size = 0; |
179 | alsa_stream->buffer_size = 0; |
180 | |
181 | bcm2835_audio_close(alsa_stream); |
182 | alsa_stream->chip->alsa_stream[alsa_stream->idx] = NULL; |
183 | /* |
184 | * Do not free up alsa_stream here, it will be freed up by |
185 | * runtime->private_free callback we registered in *_open above |
186 | */ |
187 | |
188 | chip->opened &= ~(1 << substream->number); |
189 | |
190 | mutex_unlock(lock: &chip->audio_mutex); |
191 | |
192 | return 0; |
193 | } |
194 | |
195 | static int snd_bcm2835_pcm_prepare(struct snd_pcm_substream *substream) |
196 | { |
197 | struct bcm2835_chip *chip = snd_pcm_substream_chip(substream); |
198 | struct snd_pcm_runtime *runtime = substream->runtime; |
199 | struct bcm2835_alsa_stream *alsa_stream = runtime->private_data; |
200 | int channels; |
201 | int err; |
202 | |
203 | /* notify the vchiq that it should enter spdif passthrough mode by |
204 | * setting channels=0 (see |
205 | * https://github.com/raspberrypi/linux/issues/528) |
206 | */ |
207 | if (chip->spdif_status & IEC958_AES0_NONAUDIO) |
208 | channels = 0; |
209 | else |
210 | channels = runtime->channels; |
211 | |
212 | err = bcm2835_audio_set_params(alsa_stream, channels, |
213 | samplerate: runtime->rate, |
214 | bps: snd_pcm_format_width(format: runtime->format)); |
215 | if (err < 0) |
216 | return err; |
217 | |
218 | memset(&alsa_stream->pcm_indirect, 0, sizeof(alsa_stream->pcm_indirect)); |
219 | |
220 | alsa_stream->pcm_indirect.hw_buffer_size = |
221 | alsa_stream->pcm_indirect.sw_buffer_size = |
222 | snd_pcm_lib_buffer_bytes(substream); |
223 | |
224 | alsa_stream->buffer_size = snd_pcm_lib_buffer_bytes(substream); |
225 | alsa_stream->period_size = snd_pcm_lib_period_bytes(substream); |
226 | atomic_set(v: &alsa_stream->pos, i: 0); |
227 | alsa_stream->period_offset = 0; |
228 | alsa_stream->draining = false; |
229 | alsa_stream->interpolate_start = ktime_get(); |
230 | |
231 | return 0; |
232 | } |
233 | |
234 | static void snd_bcm2835_pcm_transfer(struct snd_pcm_substream *substream, |
235 | struct snd_pcm_indirect *rec, size_t bytes) |
236 | { |
237 | struct snd_pcm_runtime *runtime = substream->runtime; |
238 | struct bcm2835_alsa_stream *alsa_stream = runtime->private_data; |
239 | void *src = (void *)(substream->runtime->dma_area + rec->sw_data); |
240 | |
241 | bcm2835_audio_write(alsa_stream, count: bytes, src); |
242 | } |
243 | |
244 | static int snd_bcm2835_pcm_ack(struct snd_pcm_substream *substream) |
245 | { |
246 | struct snd_pcm_runtime *runtime = substream->runtime; |
247 | struct bcm2835_alsa_stream *alsa_stream = runtime->private_data; |
248 | struct snd_pcm_indirect *pcm_indirect = &alsa_stream->pcm_indirect; |
249 | |
250 | return snd_pcm_indirect_playback_transfer(substream, rec: pcm_indirect, |
251 | copy: snd_bcm2835_pcm_transfer); |
252 | } |
253 | |
254 | /* trigger callback */ |
255 | static int snd_bcm2835_pcm_trigger(struct snd_pcm_substream *substream, int cmd) |
256 | { |
257 | struct snd_pcm_runtime *runtime = substream->runtime; |
258 | struct bcm2835_alsa_stream *alsa_stream = runtime->private_data; |
259 | |
260 | switch (cmd) { |
261 | case SNDRV_PCM_TRIGGER_START: |
262 | return bcm2835_audio_start(alsa_stream); |
263 | case SNDRV_PCM_TRIGGER_DRAIN: |
264 | alsa_stream->draining = true; |
265 | return bcm2835_audio_drain(alsa_stream); |
266 | case SNDRV_PCM_TRIGGER_STOP: |
267 | return bcm2835_audio_stop(alsa_stream); |
268 | default: |
269 | return -EINVAL; |
270 | } |
271 | } |
272 | |
273 | /* pointer callback */ |
274 | static snd_pcm_uframes_t |
275 | snd_bcm2835_pcm_pointer(struct snd_pcm_substream *substream) |
276 | { |
277 | struct snd_pcm_runtime *runtime = substream->runtime; |
278 | struct bcm2835_alsa_stream *alsa_stream = runtime->private_data; |
279 | ktime_t now = ktime_get(); |
280 | |
281 | /* Give userspace better delay reporting by interpolating between GPU |
282 | * notifications, assuming audio speed is close enough to the clock |
283 | * used for ktime |
284 | */ |
285 | |
286 | if ((ktime_to_ns(kt: alsa_stream->interpolate_start)) && |
287 | (ktime_compare(cmp1: alsa_stream->interpolate_start, cmp2: now) < 0)) { |
288 | u64 interval = |
289 | (ktime_to_ns(ktime_sub(now, |
290 | alsa_stream->interpolate_start))); |
291 | u64 frames_output_in_interval = |
292 | div_u64(dividend: (interval * runtime->rate), divisor: 1000000000); |
293 | snd_pcm_sframes_t frames_output_in_interval_sized = |
294 | -frames_output_in_interval; |
295 | runtime->delay = frames_output_in_interval_sized; |
296 | } |
297 | |
298 | return snd_pcm_indirect_playback_pointer(substream, |
299 | rec: &alsa_stream->pcm_indirect, |
300 | ptr: atomic_read(v: &alsa_stream->pos)); |
301 | } |
302 | |
303 | /* operators */ |
304 | static const struct snd_pcm_ops snd_bcm2835_playback_ops = { |
305 | .open = snd_bcm2835_playback_open, |
306 | .close = snd_bcm2835_playback_close, |
307 | .prepare = snd_bcm2835_pcm_prepare, |
308 | .trigger = snd_bcm2835_pcm_trigger, |
309 | .pointer = snd_bcm2835_pcm_pointer, |
310 | .ack = snd_bcm2835_pcm_ack, |
311 | }; |
312 | |
313 | static const struct snd_pcm_ops snd_bcm2835_playback_spdif_ops = { |
314 | .open = snd_bcm2835_playback_spdif_open, |
315 | .close = snd_bcm2835_playback_close, |
316 | .prepare = snd_bcm2835_pcm_prepare, |
317 | .trigger = snd_bcm2835_pcm_trigger, |
318 | .pointer = snd_bcm2835_pcm_pointer, |
319 | .ack = snd_bcm2835_pcm_ack, |
320 | }; |
321 | |
322 | /* create a pcm device */ |
323 | int snd_bcm2835_new_pcm(struct bcm2835_chip *chip, const char *name, |
324 | int idx, enum snd_bcm2835_route route, |
325 | u32 numchannels, bool spdif) |
326 | { |
327 | struct snd_pcm *pcm; |
328 | int err; |
329 | |
330 | err = snd_pcm_new(card: chip->card, id: name, device: idx, playback_count: numchannels, capture_count: 0, rpcm: &pcm); |
331 | if (err) |
332 | return err; |
333 | |
334 | pcm->private_data = chip; |
335 | pcm->nonatomic = true; |
336 | strscpy(pcm->name, name, sizeof(pcm->name)); |
337 | if (!spdif) { |
338 | chip->dest = route; |
339 | chip->volume = 0; |
340 | chip->mute = CTRL_VOL_UNMUTE; |
341 | } |
342 | |
343 | snd_pcm_set_ops(pcm, direction: SNDRV_PCM_STREAM_PLAYBACK, |
344 | ops: spdif ? &snd_bcm2835_playback_spdif_ops : |
345 | &snd_bcm2835_playback_ops); |
346 | |
347 | snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, |
348 | data: chip->card->dev, size: 128 * 1024, max: 128 * 1024); |
349 | |
350 | if (spdif) |
351 | chip->pcm_spdif = pcm; |
352 | else |
353 | chip->pcm = pcm; |
354 | return 0; |
355 | } |
356 | |