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 ;
@@ -205,10 +250,9 @@ static int snd_bcm2835_playback_close(struct snd_pcm_substream *substream)
205
250
static int snd_bcm2835_pcm_hw_params (struct snd_pcm_substream * substream ,
206
251
struct snd_pcm_hw_params * params )
207
252
{
208
- int err ;
209
253
struct snd_pcm_runtime * runtime = substream -> runtime ;
210
- bcm2835_alsa_stream_t * alsa_stream =
211
- ( bcm2835_alsa_stream_t * ) runtime -> private_data ;
254
+ bcm2835_alsa_stream_t * alsa_stream = runtime -> private_data ;
255
+ int err ;
212
256
213
257
audio_info (" .. IN\n" );
214
258
@@ -219,19 +263,9 @@ static int snd_bcm2835_pcm_hw_params(struct snd_pcm_substream *substream,
219
263
return err ;
220
264
}
221
265
222
- err = bcm2835_audio_set_params (alsa_stream , params_channels (params ),
223
- params_rate (params ),
224
- snd_pcm_format_width (params_format
225
- (params )));
226
- if (err < 0 ) {
227
- audio_error (" error setting hw params\n" );
228
- }
229
-
230
- bcm2835_audio_setup (alsa_stream );
231
-
232
- /* in preparation of the stream, set the controls (volume level) of the stream */
233
- bcm2835_audio_set_ctls (alsa_stream -> chip );
234
-
266
+ alsa_stream -> channels = params_channels (params );
267
+ alsa_stream -> params_rate = params_rate (params );
268
+ alsa_stream -> pcm_format_width = snd_pcm_format_width (params_format (params ));
235
269
audio_info (" .. OUT\n" );
236
270
237
271
return err ;
@@ -247,11 +281,35 @@ static int snd_bcm2835_pcm_hw_free(struct snd_pcm_substream *substream)
247
281
/* prepare callback */
248
282
static int snd_bcm2835_pcm_prepare (struct snd_pcm_substream * substream )
249
283
{
284
+ bcm2835_chip_t * chip = snd_pcm_substream_chip (substream );
250
285
struct snd_pcm_runtime * runtime = substream -> runtime ;
251
286
bcm2835_alsa_stream_t * alsa_stream = runtime -> private_data ;
287
+ int channels ;
288
+ int err ;
252
289
253
290
audio_info (" .. IN\n" );
254
291
292
+ /* notify the vchiq that it should enter spdif passthrough mode by
293
+ * setting channels=0 (see
294
+ * https://github.com/raspberrypi/linux/issues/528) */
295
+ if (chip -> spdif_status & IEC958_AES0_NONAUDIO )
296
+ channels = 0 ;
297
+ else
298
+ channels = alsa_stream -> channels ;
299
+
300
+ err = bcm2835_audio_set_params (alsa_stream , channels ,
301
+ alsa_stream -> params_rate ,
302
+ alsa_stream -> pcm_format_width );
303
+ if (err < 0 ) {
304
+ audio_error (" error setting hw params\n" );
305
+ }
306
+
307
+ bcm2835_audio_setup (alsa_stream );
308
+
309
+ /* in preparation of the stream, set the controls (volume level) of the stream */
310
+ bcm2835_audio_set_ctls (alsa_stream -> chip );
311
+
312
+
255
313
memset (& alsa_stream -> pcm_indirect , 0 , sizeof (alsa_stream -> pcm_indirect ));
256
314
257
315
alsa_stream -> pcm_indirect .hw_buffer_size =
@@ -392,6 +450,18 @@ static struct snd_pcm_ops snd_bcm2835_playback_ops = {
392
450
.ack = snd_bcm2835_pcm_ack ,
393
451
};
394
452
453
+ static struct snd_pcm_ops snd_bcm2835_playback_spdif_ops = {
454
+ .open = snd_bcm2835_playback_spdif_open ,
455
+ .close = snd_bcm2835_playback_close ,
456
+ .ioctl = snd_bcm2835_pcm_lib_ioctl ,
457
+ .hw_params = snd_bcm2835_pcm_hw_params ,
458
+ .hw_free = snd_bcm2835_pcm_hw_free ,
459
+ .prepare = snd_bcm2835_pcm_prepare ,
460
+ .trigger = snd_bcm2835_pcm_trigger ,
461
+ .pointer = snd_bcm2835_pcm_pointer ,
462
+ .ack = snd_bcm2835_pcm_ack ,
463
+ };
464
+
395
465
/* create a pcm device */
396
466
int __devinit snd_bcm2835_new_pcm (bcm2835_chip_t * chip )
397
467
{
@@ -424,3 +494,25 @@ int __devinit snd_bcm2835_new_pcm(bcm2835_chip_t * chip)
424
494
425
495
return 0 ;
426
496
}
497
+
498
+ int __devinit snd_bcm2835_new_spdif_pcm (bcm2835_chip_t * chip )
499
+ {
500
+ struct snd_pcm * pcm ;
501
+ int err ;
502
+
503
+ err = snd_pcm_new (chip -> card , "bcm2835 ALSA" , 1 , 1 , 0 , & pcm );
504
+ if (err < 0 )
505
+ return err ;
506
+
507
+ pcm -> private_data = chip ;
508
+ strcpy (pcm -> name , "bcm2835 IEC958/HDMI" );
509
+ chip -> pcm_spdif = pcm ;
510
+ snd_pcm_set_ops (pcm , SNDRV_PCM_STREAM_PLAYBACK ,
511
+ & snd_bcm2835_playback_spdif_ops );
512
+
513
+ snd_pcm_lib_preallocate_pages_for_all (pcm , SNDRV_DMA_TYPE_CONTINUOUS ,
514
+ snd_dma_continuous_data (GFP_KERNEL ),
515
+ 64 * 1024 , 64 * 1024 );
516
+
517
+ return 0 ;
518
+ }
0 commit comments