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 */
12static 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
29static 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
47static void snd_bcm2835_playback_free(struct snd_pcm_runtime *runtime)
48{
49	kfree(runtime->private_data);
50}
51
52void 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			     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(&alsa_stream->pos);
72	pos += bytes;
73	pos %= alsa_stream->buffer_size;
74	atomic_set(&alsa_stream->pos, 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 */
85static 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(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(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				   0,
139				   SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
140				   16);
141
142	/* position update is in 10ms order */
143	snd_pcm_hw_constraint_minmax(runtime,
144				     SNDRV_PCM_HW_PARAM_PERIOD_TIME,
145				     10 * 1000, UINT_MAX);
146
147	chip->alsa_stream[idx] = alsa_stream;
148
149	chip->opened |= (1 << idx);
150
151out:
152	mutex_unlock(&chip->audio_mutex);
153
154	return err;
155}
156
157static int snd_bcm2835_playback_open(struct snd_pcm_substream *substream)
158{
159	return snd_bcm2835_playback_open_generic(substream, 0);
160}
161
162static int snd_bcm2835_playback_spdif_open(struct snd_pcm_substream *substream)
163{
164	return snd_bcm2835_playback_open_generic(substream, 1);
165}
166
167static 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(&chip->audio_mutex);
191
192	return 0;
193}
194
195static 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				       runtime->rate,
214				       snd_pcm_format_width(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(&alsa_stream->pos, 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
234static 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, bytes, src);
242}
243
244static 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, pcm_indirect,
251						  snd_bcm2835_pcm_transfer);
252}
253
254/* trigger callback */
255static 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 */
274static snd_pcm_uframes_t
275snd_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(alsa_stream->interpolate_start)) &&
287	    (ktime_compare(alsa_stream->interpolate_start, 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((interval * runtime->rate), 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		&alsa_stream->pcm_indirect,
300		atomic_read(&alsa_stream->pos));
301}
302
303/* operators */
304static 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
313static 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 */
323int 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(chip->card, name, idx, numchannels, 0, &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, SNDRV_PCM_STREAM_PLAYBACK,
344			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				       chip->card->dev, 128 * 1024, 128 * 1024);
349
350	if (spdif)
351		chip->pcm_spdif = pcm;
352	else
353		chip->pcm = pcm;
354	return 0;
355}
356