1 From 2172c6578ef13acb8fcf5cac643cb1ee8e824117 Mon Sep 17 00:00:00 2001
2 From: Julian Scheel <julian@jusst.de>
3 Date: Wed, 19 Feb 2014 16:06:59 +0100
4 Subject: [PATCH 32/54] snd-bcm2835: Add support for spdif/hdmi passthrough
6 This adds a dedicated subdevice which can be used for passthrough of non-audio
7 formats (ie encoded a52) through the hdmi audio link. In addition to this
8 driver extension an appropriate card config is required to make alsa-lib
9 support the AES parameters for this device.
11 sound/arm/bcm2835-ctl.c | 123 ++++++++++++++++++++++++++++++++++++++++++++++
12 sound/arm/bcm2835-pcm.c | 128 +++++++++++++++++++++++++++++++++++++++++-------
13 sound/arm/bcm2835.c | 9 +++-
14 sound/arm/bcm2835.h | 9 ++++
15 4 files changed, 250 insertions(+), 19 deletions(-)
17 --- a/sound/arm/bcm2835-ctl.c
18 +++ b/sound/arm/bcm2835-ctl.c
20 #include <sound/rawmidi.h>
21 #include <sound/initval.h>
22 #include <sound/tlv.h>
23 +#include <sound/asoundef.h>
27 @@ -183,6 +184,122 @@ static struct snd_kcontrol_new snd_bcm28
31 +static int snd_bcm2835_spdif_default_info(struct snd_kcontrol *kcontrol,
32 + struct snd_ctl_elem_info *uinfo)
34 + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
39 +static int snd_bcm2835_spdif_default_get(struct snd_kcontrol *kcontrol,
40 + struct snd_ctl_elem_value *ucontrol)
42 + struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
45 + for (i = 0; i < 4; i++)
46 + ucontrol->value.iec958.status[i] =
47 + (chip->spdif_status >> (i * 8)) && 0xff;
52 +static int snd_bcm2835_spdif_default_put(struct snd_kcontrol *kcontrol,
53 + struct snd_ctl_elem_value *ucontrol)
55 + struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
56 + unsigned int val = 0;
59 + for (i = 0; i < 4; i++)
60 + val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8);
62 + change = val != chip->spdif_status;
63 + chip->spdif_status = val;
68 +static int snd_bcm2835_spdif_mask_info(struct snd_kcontrol *kcontrol,
69 + struct snd_ctl_elem_info *uinfo)
71 + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
76 +static int snd_bcm2835_spdif_mask_get(struct snd_kcontrol *kcontrol,
77 + struct snd_ctl_elem_value *ucontrol)
79 + /* bcm2835 supports only consumer mode and sets all other format flags
80 + * automatically. So the only thing left is signalling non-audio
82 + ucontrol->value.iec958.status[0] = IEC958_AES0_NONAUDIO;
86 +static int snd_bcm2835_spdif_stream_info(struct snd_kcontrol *kcontrol,
87 + struct snd_ctl_elem_info *uinfo)
89 + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
94 +static int snd_bcm2835_spdif_stream_get(struct snd_kcontrol *kcontrol,
95 + struct snd_ctl_elem_value *ucontrol)
97 + struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
100 + for (i = 0; i < 4; i++)
101 + ucontrol->value.iec958.status[i] =
102 + (chip->spdif_status >> (i * 8)) & 0xff;
106 +static int snd_bcm2835_spdif_stream_put(struct snd_kcontrol *kcontrol,
107 + struct snd_ctl_elem_value *ucontrol)
109 + struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
110 + unsigned int val = 0;
113 + for (i = 0; i < 4; i++)
114 + val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8);
115 + change = val != chip->spdif_status;
116 + chip->spdif_status = val;
121 +static struct snd_kcontrol_new snd_bcm2835_spdif[] = {
123 + .iface = SNDRV_CTL_ELEM_IFACE_PCM,
124 + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
125 + .info = snd_bcm2835_spdif_default_info,
126 + .get = snd_bcm2835_spdif_default_get,
127 + .put = snd_bcm2835_spdif_default_put
130 + .access = SNDRV_CTL_ELEM_ACCESS_READ,
131 + .iface = SNDRV_CTL_ELEM_IFACE_PCM,
132 + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK),
133 + .info = snd_bcm2835_spdif_mask_info,
134 + .get = snd_bcm2835_spdif_mask_get,
137 + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
138 + SNDRV_CTL_ELEM_ACCESS_INACTIVE,
139 + .iface = SNDRV_CTL_ELEM_IFACE_PCM,
140 + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM),
141 + .info = snd_bcm2835_spdif_stream_info,
142 + .get = snd_bcm2835_spdif_stream_get,
143 + .put = snd_bcm2835_spdif_stream_put,
147 int snd_bcm2835_new_ctl(bcm2835_chip_t * chip)
150 @@ -196,5 +313,11 @@ int snd_bcm2835_new_ctl(bcm2835_chip_t *
154 + for (idx = 0; idx < ARRAY_SIZE(snd_bcm2835_spdif); idx++) {
155 + err = snd_ctl_add(chip->card,
156 + snd_ctl_new1(&snd_bcm2835_spdif[idx], chip));
162 --- a/sound/arm/bcm2835-pcm.c
163 +++ b/sound/arm/bcm2835-pcm.c
165 #include <linux/interrupt.h>
166 #include <linux/slab.h>
168 +#include <sound/asoundef.h>
172 /* hardware definition */
173 @@ -34,6 +36,23 @@ static struct snd_pcm_hardware snd_bcm28
177 +static struct snd_pcm_hardware snd_bcm2835_playback_spdif_hw = {
178 + .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
179 + SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
180 + .formats = SNDRV_PCM_FMTBIT_S16_LE,
181 + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_44100 |
182 + SNDRV_PCM_RATE_48000,
187 + .buffer_bytes_max = 128 * 1024,
188 + .period_bytes_min = 1 * 1024,
189 + .period_bytes_max = 128 * 1024,
191 + .periods_max = 128,
194 static void snd_bcm2835_playback_free(struct snd_pcm_runtime *runtime)
196 audio_info("Freeing up alsa stream here ..\n");
197 @@ -89,7 +108,8 @@ static irqreturn_t bcm2835_playback_fifo
201 -static int snd_bcm2835_playback_open(struct snd_pcm_substream *substream)
202 +static int snd_bcm2835_playback_open_generic(
203 + struct snd_pcm_substream *substream, int spdif)
205 bcm2835_chip_t *chip = snd_pcm_substream_chip(substream);
206 struct snd_pcm_runtime *runtime = substream->runtime;
207 @@ -102,6 +122,11 @@ static int snd_bcm2835_playback_open(str
208 audio_info("Alsa open (%d)\n", substream->number);
209 idx = substream->number;
211 + if (spdif && chip->opened != 0)
213 + else if (!spdif && (chip->opened & (1 << idx)))
216 if (idx > MAX_SUBSTREAMS) {
218 ("substream(%d) device doesn't exist max(%d) substreams allowed\n",
219 @@ -143,13 +168,20 @@ static int snd_bcm2835_playback_open(str
221 runtime->private_data = alsa_stream;
222 runtime->private_free = snd_bcm2835_playback_free;
223 - runtime->hw = snd_bcm2835_playback_hw;
225 + runtime->hw = snd_bcm2835_playback_spdif_hw;
227 + /* clear spdif status, as we are not in spdif mode */
228 + chip->spdif_status = 0;
229 + runtime->hw = snd_bcm2835_playback_hw;
231 /* minimum 16 bytes alignment (for vchiq bulk transfers) */
232 snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
235 chip->alsa_stream[idx] = alsa_stream;
237 + chip->opened |= (1 << idx);
238 alsa_stream->open = 1;
239 alsa_stream->draining = 1;
241 @@ -159,6 +191,16 @@ out:
245 +static int snd_bcm2835_playback_open(struct snd_pcm_substream *substream)
247 + return snd_bcm2835_playback_open_generic(substream, 0);
250 +static int snd_bcm2835_playback_spdif_open(struct snd_pcm_substream *substream)
252 + return snd_bcm2835_playback_open_generic(substream, 1);
256 static int snd_bcm2835_playback_close(struct snd_pcm_substream *substream)
258 @@ -166,6 +208,7 @@ static int snd_bcm2835_playback_close(st
260 struct snd_pcm_runtime *runtime = substream->runtime;
261 bcm2835_alsa_stream_t *alsa_stream = runtime->private_data;
262 + bcm2835_chip_t *chip = snd_pcm_substream_chip(substream);
264 audio_info(" .. IN\n");
265 audio_info("Alsa close\n");
266 @@ -196,6 +239,8 @@ static int snd_bcm2835_playback_close(st
267 * runtime->private_free callback we registered in *_open above
270 + chip->opened &= ~(1 << substream->number);
272 audio_info(" .. OUT\n");
275 @@ -205,10 +250,9 @@ static int snd_bcm2835_playback_close(st
276 static int snd_bcm2835_pcm_hw_params(struct snd_pcm_substream *substream,
277 struct snd_pcm_hw_params *params)
280 struct snd_pcm_runtime *runtime = substream->runtime;
281 - bcm2835_alsa_stream_t *alsa_stream =
282 - (bcm2835_alsa_stream_t *) runtime->private_data;
283 + bcm2835_alsa_stream_t *alsa_stream = runtime->private_data;
286 audio_info(" .. IN\n");
288 @@ -219,19 +263,9 @@ static int snd_bcm2835_pcm_hw_params(str
292 - err = bcm2835_audio_set_params(alsa_stream, params_channels(params),
293 - params_rate(params),
294 - snd_pcm_format_width(params_format
297 - audio_error(" error setting hw params\n");
300 - bcm2835_audio_setup(alsa_stream);
302 - /* in preparation of the stream, set the controls (volume level) of the stream */
303 - bcm2835_audio_set_ctls(alsa_stream->chip);
305 + alsa_stream->channels = params_channels(params);
306 + alsa_stream->params_rate = params_rate(params);
307 + alsa_stream->pcm_format_width = snd_pcm_format_width(params_format (params));
308 audio_info(" .. OUT\n");
311 @@ -247,11 +281,35 @@ static int snd_bcm2835_pcm_hw_free(struc
312 /* prepare callback */
313 static int snd_bcm2835_pcm_prepare(struct snd_pcm_substream *substream)
315 + bcm2835_chip_t *chip = snd_pcm_substream_chip(substream);
316 struct snd_pcm_runtime *runtime = substream->runtime;
317 bcm2835_alsa_stream_t *alsa_stream = runtime->private_data;
321 audio_info(" .. IN\n");
323 + /* notify the vchiq that it should enter spdif passthrough mode by
324 + * setting channels=0 (see
325 + * https://github.com/raspberrypi/linux/issues/528) */
326 + if (chip->spdif_status & IEC958_AES0_NONAUDIO)
329 + channels = alsa_stream->channels;
331 + err = bcm2835_audio_set_params(alsa_stream, channels,
332 + alsa_stream->params_rate,
333 + alsa_stream->pcm_format_width);
335 + audio_error(" error setting hw params\n");
338 + bcm2835_audio_setup(alsa_stream);
340 + /* in preparation of the stream, set the controls (volume level) of the stream */
341 + bcm2835_audio_set_ctls(alsa_stream->chip);
344 memset(&alsa_stream->pcm_indirect, 0, sizeof(alsa_stream->pcm_indirect));
346 alsa_stream->pcm_indirect.hw_buffer_size =
347 @@ -392,6 +450,18 @@ static struct snd_pcm_ops snd_bcm2835_pl
348 .ack = snd_bcm2835_pcm_ack,
351 +static struct snd_pcm_ops snd_bcm2835_playback_spdif_ops = {
352 + .open = snd_bcm2835_playback_spdif_open,
353 + .close = snd_bcm2835_playback_close,
354 + .ioctl = snd_bcm2835_pcm_lib_ioctl,
355 + .hw_params = snd_bcm2835_pcm_hw_params,
356 + .hw_free = snd_bcm2835_pcm_hw_free,
357 + .prepare = snd_bcm2835_pcm_prepare,
358 + .trigger = snd_bcm2835_pcm_trigger,
359 + .pointer = snd_bcm2835_pcm_pointer,
360 + .ack = snd_bcm2835_pcm_ack,
363 /* create a pcm device */
364 int snd_bcm2835_new_pcm(bcm2835_chip_t * chip)
366 @@ -424,3 +494,25 @@ int snd_bcm2835_new_pcm(bcm2835_chip_t *
371 +int snd_bcm2835_new_spdif_pcm(bcm2835_chip_t * chip)
373 + struct snd_pcm *pcm;
376 + err = snd_pcm_new(chip->card, "bcm2835 ALSA", 1, 1, 0, &pcm);
380 + pcm->private_data = chip;
381 + strcpy(pcm->name, "bcm2835 IEC958/HDMI");
382 + chip->pcm_spdif = pcm;
383 + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
384 + &snd_bcm2835_playback_spdif_ops);
386 + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
387 + snd_dma_continuous_data (GFP_KERNEL),
388 + 64 * 1024, 64 * 1024);
392 --- a/sound/arm/bcm2835.c
393 +++ b/sound/arm/bcm2835.c
394 @@ -104,7 +104,7 @@ static int snd_bcm2835_alsa_probe(struct
397 snd_card_set_dev(g_card, &pdev->dev);
398 - strcpy(g_card->driver, "BRCM bcm2835 ALSA Driver");
399 + strcpy(g_card->driver, "bcm2835");
400 strcpy(g_card->shortname, "bcm2835 ALSA");
401 sprintf(g_card->longname, "%s", g_card->shortname);
403 @@ -121,6 +121,12 @@ static int snd_bcm2835_alsa_probe(struct
404 goto out_bcm2835_new_pcm;
407 + err = snd_bcm2835_new_spdif_pcm(chip);
409 + dev_err(&pdev->dev, "Failed to create new BCM2835 spdif pcm device\n");
410 + goto out_bcm2835_new_spdif;
413 err = snd_bcm2835_new_ctl(chip);
415 dev_err(&pdev->dev, "Failed to create new BCM2835 ctl\n");
416 @@ -156,6 +162,7 @@ add_register_map:
420 +out_bcm2835_new_spdif:
424 --- a/sound/arm/bcm2835.h
425 +++ b/sound/arm/bcm2835.h
426 @@ -97,6 +97,7 @@ typedef enum {
427 typedef struct bcm2835_chip {
428 struct snd_card *card;
430 + struct snd_pcm *pcm_spdif;
431 /* Bitmat for valid reg_base and irq numbers */
432 uint32_t avail_substreams;
433 struct platform_device *pdev[MAX_SUBSTREAMS];
434 @@ -106,6 +107,9 @@ typedef struct bcm2835_chip {
435 int old_volume; /* stores the volume value whist muted */
439 + unsigned int opened;
440 + unsigned int spdif_status;
443 typedef struct bcm2835_alsa_stream {
444 @@ -123,6 +127,10 @@ typedef struct bcm2835_alsa_stream {
450 + int pcm_format_width;
453 unsigned int buffer_size;
454 unsigned int period_size;
455 @@ -138,6 +146,7 @@ typedef struct bcm2835_alsa_stream {
457 int snd_bcm2835_new_ctl(bcm2835_chip_t * chip);
458 int snd_bcm2835_new_pcm(bcm2835_chip_t * chip);
459 +int snd_bcm2835_new_spdif_pcm(bcm2835_chip_t * chip);
461 int bcm2835_audio_open(bcm2835_alsa_stream_t * alsa_stream);
462 int bcm2835_audio_close(bcm2835_alsa_stream_t * alsa_stream);