15
15
#include <linux/interrupt.h>
16
16
#include <linux/slab.h>
17
17
18
+ #include <sound/asoundef.h>
19
+
18
20
#include "bcm2835.h"
19
21
20
22
/* hardware definition */
@@ -34,6 +36,23 @@ static struct snd_pcm_hardware snd_bcm2835_playback_hw = {
34
36
.periods_max = 128 ,
35
37
};
36
38
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
+
37
56
static void snd_bcm2835_playback_free (struct snd_pcm_runtime * runtime )
38
57
{
39
58
audio_info ("Freeing up alsa stream here ..\n" );
@@ -89,7 +108,8 @@ static irqreturn_t bcm2835_playback_fifo_irq(int irq, void *dev_id)
89
108
}
90
109
91
110
/* 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 )
93
113
{
94
114
bcm2835_chip_t * chip = snd_pcm_substream_chip (substream );
95
115
struct snd_pcm_runtime * runtime = substream -> runtime ;
@@ -102,6 +122,11 @@ static int snd_bcm2835_playback_open(struct snd_pcm_substream *substream)
102
122
audio_info ("Alsa open (%d)\n" , substream -> number );
103
123
idx = substream -> number ;
104
124
125
+ if (spdif && chip -> opened != 0 )
126
+ return - EBUSY ;
127
+ else if (!spdif && (chip -> opened & (1 << idx )))
128
+ return - EBUSY ;
129
+
105
130
if (idx > MAX_SUBSTREAMS ) {
106
131
audio_error
107
132
("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)
139
164
140
165
runtime -> private_data = alsa_stream ;
141
166
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
+ }
143
174
/* minimum 16 bytes alignment (for vchiq bulk transfers) */
144
175
snd_pcm_hw_constraint_step (runtime , 0 , SNDRV_PCM_HW_PARAM_PERIOD_BYTES ,
145
176
16 );
@@ -150,6 +181,7 @@ static int snd_bcm2835_playback_open(struct snd_pcm_substream *substream)
150
181
return err ;
151
182
}
152
183
184
+ chip -> opened |= (1 << idx );
153
185
alsa_stream -> open = 1 ;
154
186
alsa_stream -> draining = 1 ;
155
187
@@ -159,13 +191,24 @@ static int snd_bcm2835_playback_open(struct snd_pcm_substream *substream)
159
191
return err ;
160
192
}
161
193
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
+
162
204
/* close callback */
163
205
static int snd_bcm2835_playback_close (struct snd_pcm_substream * substream )
164
206
{
165
207
/* the hardware-specific codes will be here */
166
208
167
209
struct snd_pcm_runtime * runtime = substream -> runtime ;
168
210
bcm2835_alsa_stream_t * alsa_stream = runtime -> private_data ;
211
+ bcm2835_chip_t * chip = snd_pcm_substream_chip (substream );
169
212
170
213
audio_info (" .. IN\n" );
171
214
audio_info ("Alsa close\n" );
@@ -196,6 +239,8 @@ static int snd_bcm2835_playback_close(struct snd_pcm_substream *substream)
196
239
* runtime->private_free callback we registered in *_open above
197
240
*/
198
241
242
+ chip -> opened &= ~(1 << substream -> number );
243
+
199
244
audio_info (" .. OUT\n" );
200
245
201
246
return 0 ;
@@ -206,9 +251,11 @@ static int snd_bcm2835_pcm_hw_params(struct snd_pcm_substream *substream,
206
251
struct snd_pcm_hw_params * params )
207
252
{
208
253
int err ;
254
+ bcm2835_chip_t * chip = snd_pcm_substream_chip (substream );
209
255
struct snd_pcm_runtime * runtime = substream -> runtime ;
210
256
bcm2835_alsa_stream_t * alsa_stream =
211
257
(bcm2835_alsa_stream_t * ) runtime -> private_data ;
258
+ int channels ;
212
259
213
260
audio_info (" .. IN\n" );
214
261
@@ -219,7 +266,15 @@ static int snd_bcm2835_pcm_hw_params(struct snd_pcm_substream *substream,
219
266
return err ;
220
267
}
221
268
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 ,
223
278
params_rate (params ),
224
279
snd_pcm_format_width (params_format
225
280
(params )));
@@ -392,6 +447,18 @@ static struct snd_pcm_ops snd_bcm2835_playback_ops = {
392
447
.ack = snd_bcm2835_pcm_ack ,
393
448
};
394
449
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
+
395
462
/* create a pcm device */
396
463
int __devinit snd_bcm2835_new_pcm (bcm2835_chip_t * chip )
397
464
{
@@ -424,3 +491,25 @@ int __devinit snd_bcm2835_new_pcm(bcm2835_chip_t * chip)
424
491
425
492
return 0 ;
426
493
}
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
+ }
0 commit comments