Skip to content

Commit b0d3971

Browse files
committed
WIP: snd-bcm2835: Add support for spdif/hdmi passthrough
This adds a dedicated subdevice which can be used for passthrough of non-audio formats (ie encoded a52) through the hdmi audio link. In addition to this driver extension an appropriate card config is required to make alsa-lib support the AES parameters for this device.
1 parent 2a8d45e commit b0d3971

File tree

4 files changed

+222
-4
lines changed

4 files changed

+222
-4
lines changed

sound/arm/bcm2835-ctl.c

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include <sound/rawmidi.h>
3131
#include <sound/initval.h>
3232
#include <sound/tlv.h>
33+
#include <sound/asoundef.h>
3334

3435
#include "bcm2835.h"
3536

@@ -183,6 +184,116 @@ static struct snd_kcontrol_new snd_bcm2835_ctl[] __devinitdata = {
183184
},
184185
};
185186

187+
static int snd_bcm2835_spdif_default_info(struct snd_kcontrol *kcontrol,
188+
struct snd_ctl_elem_info *uinfo)
189+
{
190+
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
191+
uinfo->count = 1;
192+
return 0;
193+
}
194+
195+
static int snd_bcm2835_spdif_default_get(struct snd_kcontrol *kcontrol,
196+
struct snd_ctl_elem_value *ucontrol)
197+
{
198+
struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
199+
int i;
200+
201+
for (i = 0; i < 4; i++)
202+
ucontrol->value.iec958.status[i] =
203+
(chip->spdif_status >> (i * 8)) && 0xff;
204+
205+
return 0;
206+
}
207+
208+
static int snd_bcm2835_spdif_default_put(struct snd_kcontrol *kcontrol,
209+
struct snd_ctl_elem_value *ucontrol)
210+
{
211+
struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
212+
int i, change, val = 0;
213+
214+
for (i = 0; i < 4; i++)
215+
val |= ucontrol->value.iec958.status[i] << (i * 8);
216+
change = val != chip->spdif_status;
217+
218+
return change;
219+
}
220+
221+
static int snd_bcm2835_spdif_mask_info(struct snd_kcontrol *kcontrol,
222+
struct snd_ctl_elem_info *uinfo)
223+
{
224+
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
225+
uinfo->count = 1;
226+
return 0;
227+
}
228+
229+
static int snd_bcm2835_spdif_mask_get(struct snd_kcontrol *kcontrol,
230+
struct snd_ctl_elem_value *ucontrol)
231+
{
232+
ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL |
233+
IEC958_AES0_NONAUDIO |
234+
IEC958_AES0_PRO_EMPHASIS_5015;
235+
return 0;
236+
}
237+
238+
static int snd_bcm2835_spdif_stream_info(struct snd_kcontrol *kcontrol,
239+
struct snd_ctl_elem_info *uinfo)
240+
{
241+
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
242+
uinfo->count = 1;
243+
return 0;
244+
}
245+
246+
static int snd_bcm2835_spdif_stream_get(struct snd_kcontrol *kcontrol,
247+
struct snd_ctl_elem_value *ucontrol)
248+
{
249+
struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
250+
int i;
251+
252+
for (i = 0; i < 4; i++)
253+
ucontrol->value.iec958.status[i] =
254+
(chip->spdif_status >> (i * 8)) & 0xff;
255+
return 0;
256+
}
257+
258+
static int snd_bcm2835_spdif_stream_put(struct snd_kcontrol *kcontrol,
259+
struct snd_ctl_elem_value *ucontrol)
260+
{
261+
struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
262+
int i, change, val = 0;
263+
264+
for (i = 0; i < 4; i++)
265+
val |= ucontrol->value.iec958.status[i] << (i * 8);
266+
change = val != chip->spdif_status;
267+
268+
return change;
269+
}
270+
271+
static struct snd_kcontrol_new snd_bcm2835_spdif[] __devinitdata = {
272+
{
273+
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
274+
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
275+
.info = snd_bcm2835_spdif_default_info,
276+
.get = snd_bcm2835_spdif_default_get,
277+
.put = snd_bcm2835_spdif_default_put
278+
},
279+
{
280+
.access = SNDRV_CTL_ELEM_ACCESS_READ,
281+
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
282+
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK),
283+
.info = snd_bcm2835_spdif_mask_info,
284+
.get = snd_bcm2835_spdif_mask_get,
285+
},
286+
{
287+
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
288+
SNDRV_CTL_ELEM_ACCESS_INACTIVE,
289+
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
290+
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM),
291+
.info = snd_bcm2835_spdif_stream_info,
292+
.get = snd_bcm2835_spdif_stream_get,
293+
.put = snd_bcm2835_spdif_stream_put,
294+
},
295+
};
296+
186297
int __devinit snd_bcm2835_new_ctl(bcm2835_chip_t * chip)
187298
{
188299
int err;
@@ -196,5 +307,11 @@ int __devinit snd_bcm2835_new_ctl(bcm2835_chip_t * chip)
196307
if (err < 0)
197308
return err;
198309
}
310+
for (idx = 0; idx < ARRAY_SIZE(snd_bcm2835_spdif); idx++) {
311+
err = snd_ctl_add(chip->card,
312+
snd_ctl_new1(&snd_bcm2835_spdif[idx], chip));
313+
if (err < 0)
314+
return err;
315+
}
199316
return 0;
200317
}

sound/arm/bcm2835-pcm.c

Lines changed: 92 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
#include <linux/interrupt.h>
1616
#include <linux/slab.h>
1717

18+
#include <sound/asoundef.h>
19+
1820
#include "bcm2835.h"
1921

2022
/* hardware definition */
@@ -34,6 +36,23 @@ static struct snd_pcm_hardware snd_bcm2835_playback_hw = {
3436
.periods_max = 128,
3537
};
3638

39+
static struct snd_pcm_hardware snd_bcm2835_playback_spdif_hw = {
40+
.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
41+
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
42+
.formats = SNDRV_PCM_FMTBIT_S16_LE,
43+
.rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_44100 |
44+
SNDRV_PCM_RATE_48000,
45+
.rate_min = 44100,
46+
.rate_max = 48000,
47+
.channels_min = 2,
48+
.channels_max = 2,
49+
.buffer_bytes_max = 128 * 1024,
50+
.period_bytes_min = 1 * 1024,
51+
.period_bytes_max = 128 * 1024,
52+
.periods_min = 1,
53+
.periods_max = 128,
54+
};
55+
3756
static void snd_bcm2835_playback_free(struct snd_pcm_runtime *runtime)
3857
{
3958
audio_info("Freeing up alsa stream here ..\n");
@@ -89,7 +108,8 @@ static irqreturn_t bcm2835_playback_fifo_irq(int irq, void *dev_id)
89108
}
90109

91110
/* open callback */
92-
static int snd_bcm2835_playback_open(struct snd_pcm_substream *substream)
111+
static int snd_bcm2835_playback_open_generic(
112+
struct snd_pcm_substream *substream, int spdif)
93113
{
94114
bcm2835_chip_t *chip = snd_pcm_substream_chip(substream);
95115
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -102,6 +122,11 @@ static int snd_bcm2835_playback_open(struct snd_pcm_substream *substream)
102122
audio_info("Alsa open (%d)\n", substream->number);
103123
idx = substream->number;
104124

125+
if (spdif && chip->opened != 0)
126+
return -EBUSY;
127+
else if (!spdif && (chip->opened & (1 << idx)))
128+
return -EBUSY;
129+
105130
if (idx > MAX_SUBSTREAMS) {
106131
audio_error
107132
("substream(%d) device doesn't exist max(%d) substreams allowed\n",
@@ -139,7 +164,13 @@ static int snd_bcm2835_playback_open(struct snd_pcm_substream *substream)
139164

140165
runtime->private_data = alsa_stream;
141166
runtime->private_free = snd_bcm2835_playback_free;
142-
runtime->hw = snd_bcm2835_playback_hw;
167+
if (spdif) {
168+
runtime->hw = snd_bcm2835_playback_spdif_hw;
169+
} else {
170+
/* clear spdif status, as we are not in spdif mode */
171+
chip->spdif_status = 0;
172+
runtime->hw = snd_bcm2835_playback_hw;
173+
}
143174
/* minimum 16 bytes alignment (for vchiq bulk transfers) */
144175
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
145176
16);
@@ -150,6 +181,7 @@ static int snd_bcm2835_playback_open(struct snd_pcm_substream *substream)
150181
return err;
151182
}
152183

184+
chip->opened |= (1 << idx);
153185
alsa_stream->open = 1;
154186
alsa_stream->draining = 1;
155187

@@ -159,13 +191,24 @@ static int snd_bcm2835_playback_open(struct snd_pcm_substream *substream)
159191
return err;
160192
}
161193

194+
static int snd_bcm2835_playback_open(struct snd_pcm_substream *substream)
195+
{
196+
return snd_bcm2835_playback_open_generic(substream, 0);
197+
}
198+
199+
static int snd_bcm2835_playback_spdif_open(struct snd_pcm_substream *substream)
200+
{
201+
return snd_bcm2835_playback_open_generic(substream, 1);
202+
}
203+
162204
/* close callback */
163205
static int snd_bcm2835_playback_close(struct snd_pcm_substream *substream)
164206
{
165207
/* the hardware-specific codes will be here */
166208

167209
struct snd_pcm_runtime *runtime = substream->runtime;
168210
bcm2835_alsa_stream_t *alsa_stream = runtime->private_data;
211+
bcm2835_chip_t *chip = snd_pcm_substream_chip(substream);
169212

170213
audio_info(" .. IN\n");
171214
audio_info("Alsa close\n");
@@ -196,6 +239,8 @@ static int snd_bcm2835_playback_close(struct snd_pcm_substream *substream)
196239
* runtime->private_free callback we registered in *_open above
197240
*/
198241

242+
chip->opened &= ~(1 << substream->number);
243+
199244
audio_info(" .. OUT\n");
200245

201246
return 0;
@@ -206,9 +251,11 @@ static int snd_bcm2835_pcm_hw_params(struct snd_pcm_substream *substream,
206251
struct snd_pcm_hw_params *params)
207252
{
208253
int err;
254+
bcm2835_chip_t *chip = snd_pcm_substream_chip(substream);
209255
struct snd_pcm_runtime *runtime = substream->runtime;
210256
bcm2835_alsa_stream_t *alsa_stream =
211257
(bcm2835_alsa_stream_t *) runtime->private_data;
258+
int channels;
212259

213260
audio_info(" .. IN\n");
214261

@@ -219,7 +266,15 @@ static int snd_bcm2835_pcm_hw_params(struct snd_pcm_substream *substream,
219266
return err;
220267
}
221268

222-
err = bcm2835_audio_set_params(alsa_stream, params_channels(params),
269+
/* notify the vchiq that it should enter spdif passthrough mode by
270+
* setting channels=0 (see
271+
* https://github.com/raspberrypi/linux/issues/528) */
272+
if (chip->spdif_status & IEC958_AES0_NONAUDIO)
273+
channels = 0;
274+
else
275+
channels = params_channels(params);
276+
277+
err = bcm2835_audio_set_params(alsa_stream, channels,
223278
params_rate(params),
224279
snd_pcm_format_width(params_format
225280
(params)));
@@ -392,6 +447,18 @@ static struct snd_pcm_ops snd_bcm2835_playback_ops = {
392447
.ack = snd_bcm2835_pcm_ack,
393448
};
394449

450+
static struct snd_pcm_ops snd_bcm2835_playback_spdif_ops = {
451+
.open = snd_bcm2835_playback_spdif_open,
452+
.close = snd_bcm2835_playback_close,
453+
.ioctl = snd_bcm2835_pcm_lib_ioctl,
454+
.hw_params = snd_bcm2835_pcm_hw_params,
455+
.hw_free = snd_bcm2835_pcm_hw_free,
456+
.prepare = snd_bcm2835_pcm_prepare,
457+
.trigger = snd_bcm2835_pcm_trigger,
458+
.pointer = snd_bcm2835_pcm_pointer,
459+
.ack = snd_bcm2835_pcm_ack,
460+
};
461+
395462
/* create a pcm device */
396463
int __devinit snd_bcm2835_new_pcm(bcm2835_chip_t * chip)
397464
{
@@ -424,3 +491,25 @@ int __devinit snd_bcm2835_new_pcm(bcm2835_chip_t * chip)
424491

425492
return 0;
426493
}
494+
495+
int __devinit snd_bcm2835_new_spdif_pcm(bcm2835_chip_t * chip)
496+
{
497+
struct snd_pcm *pcm;
498+
int err;
499+
500+
err = snd_pcm_new(chip->card, "bcm2835 ALSA", 1, 1, 0, &pcm);
501+
if (err < 0)
502+
return err;
503+
504+
pcm->private_data = chip;
505+
strcpy(pcm->name, "bcm2835 IEC958/HDMI");
506+
chip->pcm_spdif = pcm;
507+
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
508+
&snd_bcm2835_playback_spdif_ops);
509+
510+
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
511+
snd_dma_continuous_data (GFP_KERNEL),
512+
64 * 1024, 64 * 1024);
513+
514+
return 0;
515+
}

sound/arm/bcm2835.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ static int __devinit snd_bcm2835_alsa_probe(struct platform_device *pdev)
104104
goto out;
105105

106106
snd_card_set_dev(g_card, &pdev->dev);
107-
strcpy(g_card->driver, "BRCM bcm2835 ALSA Driver");
107+
strcpy(g_card->driver, "bcm2835");
108108
strcpy(g_card->shortname, "bcm2835 ALSA");
109109
sprintf(g_card->longname, "%s", g_card->shortname);
110110

@@ -121,6 +121,12 @@ static int __devinit snd_bcm2835_alsa_probe(struct platform_device *pdev)
121121
goto out_bcm2835_new_pcm;
122122
}
123123

124+
err = snd_bcm2835_new_spdif_pcm(chip);
125+
if (err < 0) {
126+
dev_err(&pdev->dev, "Failed to create new BCM2835 spdif pcm device\n");
127+
goto out_bcm2835_new_spdif;
128+
}
129+
124130
err = snd_bcm2835_new_ctl(chip);
125131
if (err < 0) {
126132
dev_err(&pdev->dev, "Failed to create new BCM2835 ctl\n");
@@ -156,6 +162,7 @@ static int __devinit snd_bcm2835_alsa_probe(struct platform_device *pdev)
156162

157163
out_card_register:
158164
out_bcm2835_new_ctl:
165+
out_bcm2835_new_spdif:
159166
out_bcm2835_new_pcm:
160167
out_bcm2835_create:
161168
BUG_ON(!g_card);

sound/arm/bcm2835.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ typedef enum {
9797
typedef struct bcm2835_chip {
9898
struct snd_card *card;
9999
struct snd_pcm *pcm;
100+
struct snd_pcm *pcm_spdif;
100101
/* Bitmat for valid reg_base and irq numbers */
101102
uint32_t avail_substreams;
102103
struct platform_device *pdev[MAX_SUBSTREAMS];
@@ -106,6 +107,9 @@ typedef struct bcm2835_chip {
106107
int old_volume; /* stores the volume value whist muted */
107108
int dest;
108109
int mute;
110+
111+
unsigned int opened;
112+
unsigned int spdif_status;
109113
} bcm2835_chip_t;
110114

111115
typedef struct bcm2835_alsa_stream {
@@ -138,6 +142,7 @@ typedef struct bcm2835_alsa_stream {
138142

139143
int snd_bcm2835_new_ctl(bcm2835_chip_t * chip);
140144
int snd_bcm2835_new_pcm(bcm2835_chip_t * chip);
145+
int snd_bcm2835_new_spdif_pcm(bcm2835_chip_t * chip);
141146

142147
int bcm2835_audio_open(bcm2835_alsa_stream_t * alsa_stream);
143148
int bcm2835_audio_close(bcm2835_alsa_stream_t * alsa_stream);

0 commit comments

Comments
 (0)