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