4b6dcbc18ec946f187de7dac4ba78fa76f6809a9
[openwrt/openwrt.git] / target / linux / brcm2708 / patches-3.14 / 0032-snd-bcm2835-Add-support-for-spdif-hdmi-passthrough.patch
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
5
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.
10 ---
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(-)
16
17 diff --git a/sound/arm/bcm2835-ctl.c b/sound/arm/bcm2835-ctl.c
18 index 8c5334a..aad905f 100755
19 --- a/sound/arm/bcm2835-ctl.c
20 +++ b/sound/arm/bcm2835-ctl.c
21 @@ -30,6 +30,7 @@
22 #include <sound/rawmidi.h>
23 #include <sound/initval.h>
24 #include <sound/tlv.h>
25 +#include <sound/asoundef.h>
26
27 #include "bcm2835.h"
28
29 @@ -183,6 +184,122 @@ static struct snd_kcontrol_new snd_bcm2835_ctl[] = {
30 },
31 };
32
33 +static int snd_bcm2835_spdif_default_info(struct snd_kcontrol *kcontrol,
34 + struct snd_ctl_elem_info *uinfo)
35 +{
36 + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
37 + uinfo->count = 1;
38 + return 0;
39 +}
40 +
41 +static int snd_bcm2835_spdif_default_get(struct snd_kcontrol *kcontrol,
42 + struct snd_ctl_elem_value *ucontrol)
43 +{
44 + struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
45 + int i;
46 +
47 + for (i = 0; i < 4; i++)
48 + ucontrol->value.iec958.status[i] =
49 + (chip->spdif_status >> (i * 8)) && 0xff;
50 +
51 + return 0;
52 +}
53 +
54 +static int snd_bcm2835_spdif_default_put(struct snd_kcontrol *kcontrol,
55 + struct snd_ctl_elem_value *ucontrol)
56 +{
57 + struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
58 + unsigned int val = 0;
59 + int i, change;
60 +
61 + for (i = 0; i < 4; i++)
62 + val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8);
63 +
64 + change = val != chip->spdif_status;
65 + chip->spdif_status = val;
66 +
67 + return change;
68 +}
69 +
70 +static int snd_bcm2835_spdif_mask_info(struct snd_kcontrol *kcontrol,
71 + struct snd_ctl_elem_info *uinfo)
72 +{
73 + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
74 + uinfo->count = 1;
75 + return 0;
76 +}
77 +
78 +static int snd_bcm2835_spdif_mask_get(struct snd_kcontrol *kcontrol,
79 + struct snd_ctl_elem_value *ucontrol)
80 +{
81 + /* bcm2835 supports only consumer mode and sets all other format flags
82 + * automatically. So the only thing left is signalling non-audio
83 + * content */
84 + ucontrol->value.iec958.status[0] = IEC958_AES0_NONAUDIO;
85 + return 0;
86 +}
87 +
88 +static int snd_bcm2835_spdif_stream_info(struct snd_kcontrol *kcontrol,
89 + struct snd_ctl_elem_info *uinfo)
90 +{
91 + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
92 + uinfo->count = 1;
93 + return 0;
94 +}
95 +
96 +static int snd_bcm2835_spdif_stream_get(struct snd_kcontrol *kcontrol,
97 + struct snd_ctl_elem_value *ucontrol)
98 +{
99 + struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
100 + int i;
101 +
102 + for (i = 0; i < 4; i++)
103 + ucontrol->value.iec958.status[i] =
104 + (chip->spdif_status >> (i * 8)) & 0xff;
105 + return 0;
106 +}
107 +
108 +static int snd_bcm2835_spdif_stream_put(struct snd_kcontrol *kcontrol,
109 + struct snd_ctl_elem_value *ucontrol)
110 +{
111 + struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
112 + unsigned int val = 0;
113 + int i, change;
114 +
115 + for (i = 0; i < 4; i++)
116 + val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8);
117 + change = val != chip->spdif_status;
118 + chip->spdif_status = val;
119 +
120 + return change;
121 +}
122 +
123 +static struct snd_kcontrol_new snd_bcm2835_spdif[] = {
124 + {
125 + .iface = SNDRV_CTL_ELEM_IFACE_PCM,
126 + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
127 + .info = snd_bcm2835_spdif_default_info,
128 + .get = snd_bcm2835_spdif_default_get,
129 + .put = snd_bcm2835_spdif_default_put
130 + },
131 + {
132 + .access = SNDRV_CTL_ELEM_ACCESS_READ,
133 + .iface = SNDRV_CTL_ELEM_IFACE_PCM,
134 + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK),
135 + .info = snd_bcm2835_spdif_mask_info,
136 + .get = snd_bcm2835_spdif_mask_get,
137 + },
138 + {
139 + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
140 + SNDRV_CTL_ELEM_ACCESS_INACTIVE,
141 + .iface = SNDRV_CTL_ELEM_IFACE_PCM,
142 + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM),
143 + .info = snd_bcm2835_spdif_stream_info,
144 + .get = snd_bcm2835_spdif_stream_get,
145 + .put = snd_bcm2835_spdif_stream_put,
146 + },
147 +};
148 +
149 int snd_bcm2835_new_ctl(bcm2835_chip_t * chip)
150 {
151 int err;
152 @@ -196,5 +313,11 @@ int snd_bcm2835_new_ctl(bcm2835_chip_t * chip)
153 if (err < 0)
154 return err;
155 }
156 + for (idx = 0; idx < ARRAY_SIZE(snd_bcm2835_spdif); idx++) {
157 + err = snd_ctl_add(chip->card,
158 + snd_ctl_new1(&snd_bcm2835_spdif[idx], chip));
159 + if (err < 0)
160 + return err;
161 + }
162 return 0;
163 }
164 diff --git a/sound/arm/bcm2835-pcm.c b/sound/arm/bcm2835-pcm.c
165 index b4084bb..ebd3f62 100755
166 --- a/sound/arm/bcm2835-pcm.c
167 +++ b/sound/arm/bcm2835-pcm.c
168 @@ -15,6 +15,8 @@
169 #include <linux/interrupt.h>
170 #include <linux/slab.h>
171
172 +#include <sound/asoundef.h>
173 +
174 #include "bcm2835.h"
175
176 /* hardware definition */
177 @@ -34,6 +36,23 @@ static struct snd_pcm_hardware snd_bcm2835_playback_hw = {
178 .periods_max = 128,
179 };
180
181 +static struct snd_pcm_hardware snd_bcm2835_playback_spdif_hw = {
182 + .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
183 + SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
184 + .formats = SNDRV_PCM_FMTBIT_S16_LE,
185 + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_44100 |
186 + SNDRV_PCM_RATE_48000,
187 + .rate_min = 44100,
188 + .rate_max = 48000,
189 + .channels_min = 2,
190 + .channels_max = 2,
191 + .buffer_bytes_max = 128 * 1024,
192 + .period_bytes_min = 1 * 1024,
193 + .period_bytes_max = 128 * 1024,
194 + .periods_min = 1,
195 + .periods_max = 128,
196 +};
197 +
198 static void snd_bcm2835_playback_free(struct snd_pcm_runtime *runtime)
199 {
200 audio_info("Freeing up alsa stream here ..\n");
201 @@ -89,7 +108,8 @@ static irqreturn_t bcm2835_playback_fifo_irq(int irq, void *dev_id)
202 }
203
204 /* open callback */
205 -static int snd_bcm2835_playback_open(struct snd_pcm_substream *substream)
206 +static int snd_bcm2835_playback_open_generic(
207 + struct snd_pcm_substream *substream, int spdif)
208 {
209 bcm2835_chip_t *chip = snd_pcm_substream_chip(substream);
210 struct snd_pcm_runtime *runtime = substream->runtime;
211 @@ -102,6 +122,11 @@ static int snd_bcm2835_playback_open(struct snd_pcm_substream *substream)
212 audio_info("Alsa open (%d)\n", substream->number);
213 idx = substream->number;
214
215 + if (spdif && chip->opened != 0)
216 + return -EBUSY;
217 + else if (!spdif && (chip->opened & (1 << idx)))
218 + return -EBUSY;
219 +
220 if (idx > MAX_SUBSTREAMS) {
221 audio_error
222 ("substream(%d) device doesn't exist max(%d) substreams allowed\n",
223 @@ -143,13 +168,20 @@ static int snd_bcm2835_playback_open(struct snd_pcm_substream *substream)
224 }
225 runtime->private_data = alsa_stream;
226 runtime->private_free = snd_bcm2835_playback_free;
227 - runtime->hw = snd_bcm2835_playback_hw;
228 + if (spdif) {
229 + runtime->hw = snd_bcm2835_playback_spdif_hw;
230 + } else {
231 + /* clear spdif status, as we are not in spdif mode */
232 + chip->spdif_status = 0;
233 + runtime->hw = snd_bcm2835_playback_hw;
234 + }
235 /* minimum 16 bytes alignment (for vchiq bulk transfers) */
236 snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
237 16);
238
239 chip->alsa_stream[idx] = alsa_stream;
240
241 + chip->opened |= (1 << idx);
242 alsa_stream->open = 1;
243 alsa_stream->draining = 1;
244
245 @@ -159,6 +191,16 @@ out:
246 return err;
247 }
248
249 +static int snd_bcm2835_playback_open(struct snd_pcm_substream *substream)
250 +{
251 + return snd_bcm2835_playback_open_generic(substream, 0);
252 +}
253 +
254 +static int snd_bcm2835_playback_spdif_open(struct snd_pcm_substream *substream)
255 +{
256 + return snd_bcm2835_playback_open_generic(substream, 1);
257 +}
258 +
259 /* close callback */
260 static int snd_bcm2835_playback_close(struct snd_pcm_substream *substream)
261 {
262 @@ -166,6 +208,7 @@ static int snd_bcm2835_playback_close(struct snd_pcm_substream *substream)
263
264 struct snd_pcm_runtime *runtime = substream->runtime;
265 bcm2835_alsa_stream_t *alsa_stream = runtime->private_data;
266 + bcm2835_chip_t *chip = snd_pcm_substream_chip(substream);
267
268 audio_info(" .. IN\n");
269 audio_info("Alsa close\n");
270 @@ -196,6 +239,8 @@ static int snd_bcm2835_playback_close(struct snd_pcm_substream *substream)
271 * runtime->private_free callback we registered in *_open above
272 */
273
274 + chip->opened &= ~(1 << substream->number);
275 +
276 audio_info(" .. OUT\n");
277
278 return 0;
279 @@ -205,10 +250,9 @@ static int snd_bcm2835_playback_close(struct snd_pcm_substream *substream)
280 static int snd_bcm2835_pcm_hw_params(struct snd_pcm_substream *substream,
281 struct snd_pcm_hw_params *params)
282 {
283 - int err;
284 struct snd_pcm_runtime *runtime = substream->runtime;
285 - bcm2835_alsa_stream_t *alsa_stream =
286 - (bcm2835_alsa_stream_t *) runtime->private_data;
287 + bcm2835_alsa_stream_t *alsa_stream = runtime->private_data;
288 + int err;
289
290 audio_info(" .. IN\n");
291
292 @@ -219,19 +263,9 @@ static int snd_bcm2835_pcm_hw_params(struct snd_pcm_substream *substream,
293 return err;
294 }
295
296 - err = bcm2835_audio_set_params(alsa_stream, params_channels(params),
297 - params_rate(params),
298 - snd_pcm_format_width(params_format
299 - (params)));
300 - if (err < 0) {
301 - audio_error(" error setting hw params\n");
302 - }
303 -
304 - bcm2835_audio_setup(alsa_stream);
305 -
306 - /* in preparation of the stream, set the controls (volume level) of the stream */
307 - bcm2835_audio_set_ctls(alsa_stream->chip);
308 -
309 + alsa_stream->channels = params_channels(params);
310 + alsa_stream->params_rate = params_rate(params);
311 + alsa_stream->pcm_format_width = snd_pcm_format_width(params_format (params));
312 audio_info(" .. OUT\n");
313
314 return err;
315 @@ -247,11 +281,35 @@ static int snd_bcm2835_pcm_hw_free(struct snd_pcm_substream *substream)
316 /* prepare callback */
317 static int snd_bcm2835_pcm_prepare(struct snd_pcm_substream *substream)
318 {
319 + bcm2835_chip_t *chip = snd_pcm_substream_chip(substream);
320 struct snd_pcm_runtime *runtime = substream->runtime;
321 bcm2835_alsa_stream_t *alsa_stream = runtime->private_data;
322 + int channels;
323 + int err;
324
325 audio_info(" .. IN\n");
326
327 + /* notify the vchiq that it should enter spdif passthrough mode by
328 + * setting channels=0 (see
329 + * https://github.com/raspberrypi/linux/issues/528) */
330 + if (chip->spdif_status & IEC958_AES0_NONAUDIO)
331 + channels = 0;
332 + else
333 + channels = alsa_stream->channels;
334 +
335 + err = bcm2835_audio_set_params(alsa_stream, channels,
336 + alsa_stream->params_rate,
337 + alsa_stream->pcm_format_width);
338 + if (err < 0) {
339 + audio_error(" error setting hw params\n");
340 + }
341 +
342 + bcm2835_audio_setup(alsa_stream);
343 +
344 + /* in preparation of the stream, set the controls (volume level) of the stream */
345 + bcm2835_audio_set_ctls(alsa_stream->chip);
346 +
347 +
348 memset(&alsa_stream->pcm_indirect, 0, sizeof(alsa_stream->pcm_indirect));
349
350 alsa_stream->pcm_indirect.hw_buffer_size =
351 @@ -392,6 +450,18 @@ static struct snd_pcm_ops snd_bcm2835_playback_ops = {
352 .ack = snd_bcm2835_pcm_ack,
353 };
354
355 +static struct snd_pcm_ops snd_bcm2835_playback_spdif_ops = {
356 + .open = snd_bcm2835_playback_spdif_open,
357 + .close = snd_bcm2835_playback_close,
358 + .ioctl = snd_bcm2835_pcm_lib_ioctl,
359 + .hw_params = snd_bcm2835_pcm_hw_params,
360 + .hw_free = snd_bcm2835_pcm_hw_free,
361 + .prepare = snd_bcm2835_pcm_prepare,
362 + .trigger = snd_bcm2835_pcm_trigger,
363 + .pointer = snd_bcm2835_pcm_pointer,
364 + .ack = snd_bcm2835_pcm_ack,
365 +};
366 +
367 /* create a pcm device */
368 int snd_bcm2835_new_pcm(bcm2835_chip_t * chip)
369 {
370 @@ -424,3 +494,25 @@ int snd_bcm2835_new_pcm(bcm2835_chip_t * chip)
371
372 return 0;
373 }
374 +
375 +int snd_bcm2835_new_spdif_pcm(bcm2835_chip_t * chip)
376 +{
377 + struct snd_pcm *pcm;
378 + int err;
379 +
380 + err = snd_pcm_new(chip->card, "bcm2835 ALSA", 1, 1, 0, &pcm);
381 + if (err < 0)
382 + return err;
383 +
384 + pcm->private_data = chip;
385 + strcpy(pcm->name, "bcm2835 IEC958/HDMI");
386 + chip->pcm_spdif = pcm;
387 + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
388 + &snd_bcm2835_playback_spdif_ops);
389 +
390 + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
391 + snd_dma_continuous_data (GFP_KERNEL),
392 + 64 * 1024, 64 * 1024);
393 +
394 + return 0;
395 +}
396 diff --git a/sound/arm/bcm2835.c b/sound/arm/bcm2835.c
397 index e2047a7..4136760 100755
398 --- a/sound/arm/bcm2835.c
399 +++ b/sound/arm/bcm2835.c
400 @@ -104,7 +104,7 @@ static int snd_bcm2835_alsa_probe(struct platform_device *pdev)
401 goto out;
402
403 snd_card_set_dev(g_card, &pdev->dev);
404 - strcpy(g_card->driver, "BRCM bcm2835 ALSA Driver");
405 + strcpy(g_card->driver, "bcm2835");
406 strcpy(g_card->shortname, "bcm2835 ALSA");
407 sprintf(g_card->longname, "%s", g_card->shortname);
408
409 @@ -121,6 +121,12 @@ static int snd_bcm2835_alsa_probe(struct platform_device *pdev)
410 goto out_bcm2835_new_pcm;
411 }
412
413 + err = snd_bcm2835_new_spdif_pcm(chip);
414 + if (err < 0) {
415 + dev_err(&pdev->dev, "Failed to create new BCM2835 spdif pcm device\n");
416 + goto out_bcm2835_new_spdif;
417 + }
418 +
419 err = snd_bcm2835_new_ctl(chip);
420 if (err < 0) {
421 dev_err(&pdev->dev, "Failed to create new BCM2835 ctl\n");
422 @@ -156,6 +162,7 @@ add_register_map:
423
424 out_card_register:
425 out_bcm2835_new_ctl:
426 +out_bcm2835_new_spdif:
427 out_bcm2835_new_pcm:
428 out_bcm2835_create:
429 BUG_ON(!g_card);
430 diff --git a/sound/arm/bcm2835.h b/sound/arm/bcm2835.h
431 index 36afee3..8c2fe26 100755
432 --- a/sound/arm/bcm2835.h
433 +++ b/sound/arm/bcm2835.h
434 @@ -97,6 +97,7 @@ typedef enum {
435 typedef struct bcm2835_chip {
436 struct snd_card *card;
437 struct snd_pcm *pcm;
438 + struct snd_pcm *pcm_spdif;
439 /* Bitmat for valid reg_base and irq numbers */
440 uint32_t avail_substreams;
441 struct platform_device *pdev[MAX_SUBSTREAMS];
442 @@ -106,6 +107,9 @@ typedef struct bcm2835_chip {
443 int old_volume; /* stores the volume value whist muted */
444 int dest;
445 int mute;
446 +
447 + unsigned int opened;
448 + unsigned int spdif_status;
449 } bcm2835_chip_t;
450
451 typedef struct bcm2835_alsa_stream {
452 @@ -123,6 +127,10 @@ typedef struct bcm2835_alsa_stream {
453 int running;
454 int draining;
455
456 + int channels;
457 + int params_rate;
458 + int pcm_format_width;
459 +
460 unsigned int pos;
461 unsigned int buffer_size;
462 unsigned int period_size;
463 @@ -138,6 +146,7 @@ typedef struct bcm2835_alsa_stream {
464
465 int snd_bcm2835_new_ctl(bcm2835_chip_t * chip);
466 int snd_bcm2835_new_pcm(bcm2835_chip_t * chip);
467 +int snd_bcm2835_new_spdif_pcm(bcm2835_chip_t * chip);
468
469 int bcm2835_audio_open(bcm2835_alsa_stream_t * alsa_stream);
470 int bcm2835_audio_close(bcm2835_alsa_stream_t * alsa_stream);
471 --
472 1.9.1
473