Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/arm/broadcom/bcm2835/bcm2835_audio.c
39566 views
1
/*-
2
* Copyright (c) 2015 Oleksandr Tymoshenko <[email protected]>
3
*
4
* Redistribution and use in source and binary forms, with or without
5
* modification, are permitted provided that the following conditions
6
* are met:
7
* 1. Redistributions of source code must retain the above copyright
8
* notice, this list of conditions and the following disclaimer.
9
* 2. Redistributions in binary form must reproduce the above copyright
10
* notice, this list of conditions and the following disclaimer in the
11
* documentation and/or other materials provided with the distribution.
12
*
13
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23
* SUCH DAMAGE.
24
*/
25
26
#ifdef HAVE_KERNEL_OPTION_HEADERS
27
#include "opt_snd.h"
28
#endif
29
30
#include <dev/sound/pcm/sound.h>
31
32
#include "mixer_if.h"
33
34
#include "interface/compat/vchi_bsd.h"
35
#include "interface/vchi/vchi.h"
36
#include "interface/vchiq_arm/vchiq.h"
37
38
#include "vc_vchi_audioserv_defs.h"
39
40
/* Audio destination */
41
#define DEST_AUTO 0
42
#define DEST_HEADPHONES 1
43
#define DEST_HDMI 2
44
45
/* Playback state */
46
#define PLAYBACK_IDLE 0
47
#define PLAYBACK_PLAYING 1
48
#define PLAYBACK_STOPPING 2
49
50
/* Worker thread state */
51
#define WORKER_RUNNING 0
52
#define WORKER_STOPPING 1
53
#define WORKER_STOPPED 2
54
55
/*
56
* Worker thread flags, set to 1 in flags_pending
57
* when driver requests one or another operation
58
* from worker. Cleared to 0 once worker performs
59
* the operations.
60
*/
61
#define AUDIO_PARAMS (1 << 0)
62
#define AUDIO_PLAY (1 << 1)
63
#define AUDIO_STOP (1 << 2)
64
65
#define VCHIQ_AUDIO_PACKET_SIZE 4000
66
#define VCHIQ_AUDIO_BUFFER_SIZE 10*VCHIQ_AUDIO_PACKET_SIZE
67
68
#define VCHIQ_AUDIO_MAX_VOLUME
69
/* volume in terms of 0.01dB */
70
#define VCHIQ_AUDIO_VOLUME_MIN -10239
71
#define VCHIQ_AUDIO_VOLUME(db100) (uint32_t)(-((db100) << 8)/100)
72
73
/* dB levels with 5% volume step */
74
static int db_levels[] = {
75
VCHIQ_AUDIO_VOLUME_MIN, -4605, -3794, -3218, -2772,
76
-2407, -2099, -1832, -1597, -1386,
77
-1195, -1021, -861, -713, -575,
78
-446, -325, -210, -102, 0,
79
};
80
81
static uint32_t bcm2835_audio_playfmt[] = {
82
SND_FORMAT(AFMT_U8, 1, 0),
83
SND_FORMAT(AFMT_U8, 2, 0),
84
SND_FORMAT(AFMT_S8, 1, 0),
85
SND_FORMAT(AFMT_S8, 2, 0),
86
SND_FORMAT(AFMT_S16_LE, 1, 0),
87
SND_FORMAT(AFMT_S16_LE, 2, 0),
88
SND_FORMAT(AFMT_U16_LE, 1, 0),
89
SND_FORMAT(AFMT_U16_LE, 2, 0),
90
0
91
};
92
93
static struct pcmchan_caps bcm2835_audio_playcaps = {8000, 48000, bcm2835_audio_playfmt, 0};
94
95
struct bcm2835_audio_info;
96
97
struct bcm2835_audio_chinfo {
98
struct bcm2835_audio_info *parent;
99
struct pcm_channel *channel;
100
struct snd_dbuf *buffer;
101
uint32_t fmt, spd, blksz;
102
103
/* Pointer to first unsubmitted sample */
104
uint32_t unsubmittedptr;
105
/*
106
* Number of bytes in "submitted but not played"
107
* pseudo-buffer
108
*/
109
int available_space;
110
int playback_state;
111
uint64_t callbacks;
112
uint64_t submitted_samples;
113
uint64_t retrieved_samples;
114
uint64_t underruns;
115
int starved;
116
};
117
118
struct bcm2835_audio_info {
119
device_t dev;
120
unsigned int bufsz;
121
struct bcm2835_audio_chinfo pch;
122
uint32_t dest, volume;
123
struct intr_config_hook intr_hook;
124
125
/* VCHI data */
126
VCHI_INSTANCE_T vchi_instance;
127
VCHI_CONNECTION_T *vchi_connection;
128
VCHI_SERVICE_HANDLE_T vchi_handle;
129
130
struct mtx lock;
131
struct cv worker_cv;
132
133
uint32_t flags_pending;
134
135
/* Worker thread state */
136
int worker_state;
137
};
138
139
#define BCM2835_AUDIO_LOCK(sc) mtx_lock(&(sc)->lock)
140
#define BCM2835_AUDIO_LOCKED(sc) mtx_assert(&(sc)->lock, MA_OWNED)
141
#define BCM2835_AUDIO_UNLOCK(sc) mtx_unlock(&(sc)->lock)
142
143
static const char *
144
dest_description(uint32_t dest)
145
{
146
switch (dest) {
147
case DEST_AUTO:
148
return "AUTO";
149
break;
150
151
case DEST_HEADPHONES:
152
return "HEADPHONES";
153
break;
154
155
case DEST_HDMI:
156
return "HDMI";
157
break;
158
default:
159
return "UNKNOWN";
160
break;
161
}
162
}
163
164
static void
165
bcm2835_worker_update_params(struct bcm2835_audio_info *sc)
166
{
167
168
BCM2835_AUDIO_LOCKED(sc);
169
170
sc->flags_pending |= AUDIO_PARAMS;
171
cv_signal(&sc->worker_cv);
172
}
173
174
static void
175
bcm2835_worker_play_start(struct bcm2835_audio_info *sc)
176
{
177
BCM2835_AUDIO_LOCK(sc);
178
sc->flags_pending &= ~(AUDIO_STOP);
179
sc->flags_pending |= AUDIO_PLAY;
180
cv_signal(&sc->worker_cv);
181
BCM2835_AUDIO_UNLOCK(sc);
182
}
183
184
static void
185
bcm2835_worker_play_stop(struct bcm2835_audio_info *sc)
186
{
187
BCM2835_AUDIO_LOCK(sc);
188
sc->flags_pending &= ~(AUDIO_PLAY);
189
sc->flags_pending |= AUDIO_STOP;
190
cv_signal(&sc->worker_cv);
191
BCM2835_AUDIO_UNLOCK(sc);
192
}
193
194
static void
195
bcm2835_audio_callback(void *param, const VCHI_CALLBACK_REASON_T reason, void *msg_handle)
196
{
197
struct bcm2835_audio_info *sc = (struct bcm2835_audio_info *)param;
198
int32_t status;
199
uint32_t msg_len;
200
VC_AUDIO_MSG_T m;
201
202
if (reason != VCHI_CALLBACK_MSG_AVAILABLE)
203
return;
204
205
status = vchi_msg_dequeue(sc->vchi_handle,
206
&m, sizeof m, &msg_len, VCHI_FLAGS_NONE);
207
if (status != 0)
208
return;
209
if (m.type == VC_AUDIO_MSG_TYPE_RESULT) {
210
if (m.u.result.success) {
211
device_printf(sc->dev,
212
"msg type %08x failed\n",
213
m.type);
214
}
215
} else if (m.type == VC_AUDIO_MSG_TYPE_COMPLETE) {
216
struct bcm2835_audio_chinfo *ch = m.u.complete.cookie;
217
218
int count = m.u.complete.count & 0xffff;
219
int perr = (m.u.complete.count & (1U << 30)) != 0;
220
ch->callbacks++;
221
if (perr)
222
ch->underruns++;
223
224
BCM2835_AUDIO_LOCK(sc);
225
if (ch->playback_state != PLAYBACK_IDLE) {
226
/* Prevent LOR */
227
BCM2835_AUDIO_UNLOCK(sc);
228
chn_intr(sc->pch.channel);
229
BCM2835_AUDIO_LOCK(sc);
230
}
231
/* We should check again, state might have changed */
232
if (ch->playback_state != PLAYBACK_IDLE) {
233
if (!perr) {
234
if ((ch->available_space + count)> VCHIQ_AUDIO_BUFFER_SIZE) {
235
device_printf(sc->dev, "inconsistent data in callback:\n");
236
device_printf(sc->dev, "available_space == %d, count = %d, perr=%d\n",
237
ch->available_space, count, perr);
238
device_printf(sc->dev,
239
"retrieved_samples = %lld, submitted_samples = %lld\n",
240
ch->retrieved_samples, ch->submitted_samples);
241
}
242
ch->available_space += count;
243
ch->retrieved_samples += count;
244
}
245
if (perr || (ch->available_space >= VCHIQ_AUDIO_PACKET_SIZE))
246
cv_signal(&sc->worker_cv);
247
}
248
BCM2835_AUDIO_UNLOCK(sc);
249
} else
250
printf("%s: unknown m.type: %d\n", __func__, m.type);
251
}
252
253
/* VCHIQ stuff */
254
static void
255
bcm2835_audio_init(struct bcm2835_audio_info *sc)
256
{
257
int status;
258
259
/* Initialize and create a VCHI connection */
260
status = vchi_initialise(&sc->vchi_instance);
261
if (status != 0) {
262
printf("vchi_initialise failed: %d\n", status);
263
return;
264
}
265
266
status = vchi_connect(NULL, 0, sc->vchi_instance);
267
if (status != 0) {
268
printf("vchi_connect failed: %d\n", status);
269
return;
270
}
271
272
SERVICE_CREATION_T params = {
273
VCHI_VERSION_EX(VC_AUDIOSERV_VER, VC_AUDIOSERV_MIN_VER),
274
VC_AUDIO_SERVER_NAME, /* 4cc service code */
275
sc->vchi_connection, /* passed in fn pointers */
276
0, /* rx fifo size */
277
0, /* tx fifo size */
278
bcm2835_audio_callback, /* service callback */
279
sc, /* service callback parameter */
280
1,
281
1,
282
0 /* want crc check on bulk transfers */
283
};
284
285
status = vchi_service_open(sc->vchi_instance, &params,
286
&sc->vchi_handle);
287
288
if (status != 0)
289
sc->vchi_handle = VCHIQ_SERVICE_HANDLE_INVALID;
290
}
291
292
static void
293
bcm2835_audio_release(struct bcm2835_audio_info *sc)
294
{
295
int success;
296
297
if (sc->vchi_handle != VCHIQ_SERVICE_HANDLE_INVALID) {
298
success = vchi_service_close(sc->vchi_handle);
299
if (success != 0)
300
printf("vchi_service_close failed: %d\n", success);
301
vchi_service_release(sc->vchi_handle);
302
sc->vchi_handle = VCHIQ_SERVICE_HANDLE_INVALID;
303
}
304
305
vchi_disconnect(sc->vchi_instance);
306
}
307
308
static void
309
bcm2835_audio_reset_channel(struct bcm2835_audio_chinfo *ch)
310
{
311
312
ch->available_space = VCHIQ_AUDIO_BUFFER_SIZE;
313
ch->unsubmittedptr = 0;
314
sndbuf_reset(ch->buffer);
315
}
316
317
static void
318
bcm2835_audio_start(struct bcm2835_audio_chinfo *ch)
319
{
320
VC_AUDIO_MSG_T m;
321
int ret;
322
struct bcm2835_audio_info *sc = ch->parent;
323
324
if (sc->vchi_handle != VCHIQ_SERVICE_HANDLE_INVALID) {
325
m.type = VC_AUDIO_MSG_TYPE_START;
326
ret = vchi_msg_queue(sc->vchi_handle,
327
&m, sizeof m, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
328
329
if (ret != 0)
330
printf("%s: vchi_msg_queue failed (err %d)\n", __func__, ret);
331
}
332
}
333
334
static void
335
bcm2835_audio_stop(struct bcm2835_audio_chinfo *ch)
336
{
337
VC_AUDIO_MSG_T m;
338
int ret;
339
struct bcm2835_audio_info *sc = ch->parent;
340
341
if (sc->vchi_handle != VCHIQ_SERVICE_HANDLE_INVALID) {
342
m.type = VC_AUDIO_MSG_TYPE_STOP;
343
m.u.stop.draining = 0;
344
345
ret = vchi_msg_queue(sc->vchi_handle,
346
&m, sizeof m, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
347
348
if (ret != 0)
349
printf("%s: vchi_msg_queue failed (err %d)\n", __func__, ret);
350
}
351
}
352
353
static void
354
bcm2835_audio_open(struct bcm2835_audio_info *sc)
355
{
356
VC_AUDIO_MSG_T m;
357
int ret;
358
359
if (sc->vchi_handle != VCHIQ_SERVICE_HANDLE_INVALID) {
360
m.type = VC_AUDIO_MSG_TYPE_OPEN;
361
ret = vchi_msg_queue(sc->vchi_handle,
362
&m, sizeof m, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
363
364
if (ret != 0)
365
printf("%s: vchi_msg_queue failed (err %d)\n", __func__, ret);
366
}
367
}
368
369
static void
370
bcm2835_audio_update_controls(struct bcm2835_audio_info *sc, uint32_t volume, uint32_t dest)
371
{
372
VC_AUDIO_MSG_T m;
373
int ret, db;
374
375
if (sc->vchi_handle != VCHIQ_SERVICE_HANDLE_INVALID) {
376
m.type = VC_AUDIO_MSG_TYPE_CONTROL;
377
m.u.control.dest = dest;
378
if (volume > 99)
379
volume = 99;
380
db = db_levels[volume/5];
381
m.u.control.volume = VCHIQ_AUDIO_VOLUME(db);
382
383
ret = vchi_msg_queue(sc->vchi_handle,
384
&m, sizeof m, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
385
386
if (ret != 0)
387
printf("%s: vchi_msg_queue failed (err %d)\n", __func__, ret);
388
}
389
}
390
391
static void
392
bcm2835_audio_update_params(struct bcm2835_audio_info *sc, uint32_t fmt, uint32_t speed)
393
{
394
VC_AUDIO_MSG_T m;
395
int ret;
396
397
if (sc->vchi_handle != VCHIQ_SERVICE_HANDLE_INVALID) {
398
m.type = VC_AUDIO_MSG_TYPE_CONFIG;
399
m.u.config.channels = AFMT_CHANNEL(fmt);
400
m.u.config.samplerate = speed;
401
m.u.config.bps = AFMT_BIT(fmt);
402
403
ret = vchi_msg_queue(sc->vchi_handle,
404
&m, sizeof m, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
405
406
if (ret != 0)
407
printf("%s: vchi_msg_queue failed (err %d)\n", __func__, ret);
408
}
409
}
410
411
static bool
412
bcm2835_audio_buffer_should_sleep(struct bcm2835_audio_chinfo *ch)
413
{
414
415
if (ch->playback_state != PLAYBACK_PLAYING)
416
return (true);
417
418
/* Not enough data */
419
if (sndbuf_getready(ch->buffer) < VCHIQ_AUDIO_PACKET_SIZE) {
420
printf("starve\n");
421
ch->starved++;
422
return (true);
423
}
424
425
/* Not enough free space */
426
if (ch->available_space < VCHIQ_AUDIO_PACKET_SIZE) {
427
return (true);
428
}
429
430
return (false);
431
}
432
433
static void
434
bcm2835_audio_write_samples(struct bcm2835_audio_chinfo *ch, void *buf, uint32_t count)
435
{
436
struct bcm2835_audio_info *sc = ch->parent;
437
VC_AUDIO_MSG_T m;
438
int ret;
439
440
if (sc->vchi_handle == VCHIQ_SERVICE_HANDLE_INVALID) {
441
return;
442
}
443
444
m.type = VC_AUDIO_MSG_TYPE_WRITE;
445
m.u.write.count = count;
446
m.u.write.max_packet = VCHIQ_AUDIO_PACKET_SIZE;
447
m.u.write.callback = NULL;
448
m.u.write.cookie = ch;
449
m.u.write.silence = 0;
450
451
ret = vchi_msg_queue(sc->vchi_handle,
452
&m, sizeof m, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
453
454
if (ret != 0)
455
printf("%s: vchi_msg_queue failed (err %d)\n", __func__, ret);
456
457
while (count > 0) {
458
int bytes = MIN((int)m.u.write.max_packet, (int)count);
459
ret = vchi_msg_queue(sc->vchi_handle,
460
buf, bytes, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
461
if (ret != 0)
462
printf("%s: vchi_msg_queue failed: %d\n",
463
__func__, ret);
464
buf = (char *)buf + bytes;
465
count -= bytes;
466
}
467
}
468
469
static void
470
bcm2835_audio_worker(void *data)
471
{
472
struct bcm2835_audio_info *sc = (struct bcm2835_audio_info *)data;
473
struct bcm2835_audio_chinfo *ch = &sc->pch;
474
uint32_t speed, format;
475
uint32_t volume, dest;
476
uint32_t flags;
477
uint32_t count, size, readyptr;
478
uint8_t *buf;
479
480
ch->playback_state = PLAYBACK_IDLE;
481
482
while (1) {
483
if (sc->worker_state != WORKER_RUNNING)
484
break;
485
486
BCM2835_AUDIO_LOCK(sc);
487
/*
488
* wait until there are flags set or buffer is ready
489
* to consume more samples
490
*/
491
while ((sc->flags_pending == 0) &&
492
bcm2835_audio_buffer_should_sleep(ch)) {
493
cv_wait_sig(&sc->worker_cv, &sc->lock);
494
}
495
flags = sc->flags_pending;
496
/* Clear pending flags */
497
sc->flags_pending = 0;
498
BCM2835_AUDIO_UNLOCK(sc);
499
500
/* Requested to change parameters */
501
if (flags & AUDIO_PARAMS) {
502
BCM2835_AUDIO_LOCK(sc);
503
speed = ch->spd;
504
format = ch->fmt;
505
volume = sc->volume;
506
dest = sc->dest;
507
BCM2835_AUDIO_UNLOCK(sc);
508
if (ch->playback_state == PLAYBACK_IDLE)
509
bcm2835_audio_update_params(sc, format, speed);
510
bcm2835_audio_update_controls(sc, volume, dest);
511
}
512
513
/* Requested to stop playback */
514
if ((flags & AUDIO_STOP) &&
515
(ch->playback_state == PLAYBACK_PLAYING)) {
516
bcm2835_audio_stop(ch);
517
BCM2835_AUDIO_LOCK(sc);
518
bcm2835_audio_reset_channel(&sc->pch);
519
ch->playback_state = PLAYBACK_IDLE;
520
BCM2835_AUDIO_UNLOCK(sc);
521
continue;
522
}
523
524
/* Requested to start playback */
525
if ((flags & AUDIO_PLAY) &&
526
(ch->playback_state == PLAYBACK_IDLE)) {
527
BCM2835_AUDIO_LOCK(sc);
528
ch->playback_state = PLAYBACK_PLAYING;
529
BCM2835_AUDIO_UNLOCK(sc);
530
bcm2835_audio_start(ch);
531
}
532
533
if (ch->playback_state == PLAYBACK_IDLE)
534
continue;
535
536
if (sndbuf_getready(ch->buffer) == 0)
537
continue;
538
539
count = sndbuf_getready(ch->buffer);
540
size = sndbuf_getsize(ch->buffer);
541
readyptr = sndbuf_getreadyptr(ch->buffer);
542
543
BCM2835_AUDIO_LOCK(sc);
544
if (readyptr + count > size)
545
count = size - readyptr;
546
count = min(count, ch->available_space);
547
count -= (count % VCHIQ_AUDIO_PACKET_SIZE);
548
BCM2835_AUDIO_UNLOCK(sc);
549
550
if (count < VCHIQ_AUDIO_PACKET_SIZE)
551
continue;
552
553
buf = (uint8_t*)sndbuf_getbuf(ch->buffer) + readyptr;
554
555
bcm2835_audio_write_samples(ch, buf, count);
556
BCM2835_AUDIO_LOCK(sc);
557
ch->unsubmittedptr = (ch->unsubmittedptr + count) % sndbuf_getsize(ch->buffer);
558
ch->available_space -= count;
559
ch->submitted_samples += count;
560
KASSERT(ch->available_space >= 0, ("ch->available_space == %d\n", ch->available_space));
561
BCM2835_AUDIO_UNLOCK(sc);
562
}
563
564
BCM2835_AUDIO_LOCK(sc);
565
sc->worker_state = WORKER_STOPPED;
566
cv_signal(&sc->worker_cv);
567
BCM2835_AUDIO_UNLOCK(sc);
568
569
kproc_exit(0);
570
}
571
572
static void
573
bcm2835_audio_create_worker(struct bcm2835_audio_info *sc)
574
{
575
struct proc *newp;
576
577
sc->worker_state = WORKER_RUNNING;
578
if (kproc_create(bcm2835_audio_worker, (void*)sc, &newp, 0, 0,
579
"bcm2835_audio_worker") != 0) {
580
printf("failed to create bcm2835_audio_worker\n");
581
}
582
}
583
584
/* -------------------------------------------------------------------- */
585
/* channel interface for VCHI audio */
586
static void *
587
bcmchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
588
{
589
struct bcm2835_audio_info *sc = devinfo;
590
struct bcm2835_audio_chinfo *ch = &sc->pch;
591
void *buffer;
592
593
if (dir == PCMDIR_REC)
594
return NULL;
595
596
ch->parent = sc;
597
ch->channel = c;
598
ch->buffer = b;
599
600
/* default values */
601
ch->spd = 44100;
602
ch->fmt = SND_FORMAT(AFMT_S16_LE, 2, 0);
603
ch->blksz = VCHIQ_AUDIO_PACKET_SIZE;
604
605
buffer = malloc(sc->bufsz, M_DEVBUF, M_WAITOK | M_ZERO);
606
607
if (sndbuf_setup(ch->buffer, buffer, sc->bufsz) != 0) {
608
device_printf(sc->dev, "sndbuf_setup failed\n");
609
free(buffer, M_DEVBUF);
610
return NULL;
611
}
612
613
BCM2835_AUDIO_LOCK(sc);
614
bcm2835_worker_update_params(sc);
615
BCM2835_AUDIO_UNLOCK(sc);
616
617
return ch;
618
}
619
620
static int
621
bcmchan_free(kobj_t obj, void *data)
622
{
623
struct bcm2835_audio_chinfo *ch = data;
624
void *buffer;
625
626
buffer = sndbuf_getbuf(ch->buffer);
627
if (buffer)
628
free(buffer, M_DEVBUF);
629
630
return (0);
631
}
632
633
static int
634
bcmchan_setformat(kobj_t obj, void *data, uint32_t format)
635
{
636
struct bcm2835_audio_chinfo *ch = data;
637
struct bcm2835_audio_info *sc = ch->parent;
638
639
BCM2835_AUDIO_LOCK(sc);
640
ch->fmt = format;
641
bcm2835_worker_update_params(sc);
642
BCM2835_AUDIO_UNLOCK(sc);
643
644
return 0;
645
}
646
647
static uint32_t
648
bcmchan_setspeed(kobj_t obj, void *data, uint32_t speed)
649
{
650
struct bcm2835_audio_chinfo *ch = data;
651
struct bcm2835_audio_info *sc = ch->parent;
652
653
BCM2835_AUDIO_LOCK(sc);
654
ch->spd = speed;
655
bcm2835_worker_update_params(sc);
656
BCM2835_AUDIO_UNLOCK(sc);
657
658
return ch->spd;
659
}
660
661
static uint32_t
662
bcmchan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
663
{
664
struct bcm2835_audio_chinfo *ch = data;
665
666
return ch->blksz;
667
}
668
669
static int
670
bcmchan_trigger(kobj_t obj, void *data, int go)
671
{
672
struct bcm2835_audio_chinfo *ch = data;
673
struct bcm2835_audio_info *sc = ch->parent;
674
675
if (!PCMTRIG_COMMON(go))
676
return (0);
677
678
switch (go) {
679
case PCMTRIG_START:
680
/* kickstart data flow */
681
chn_intr(sc->pch.channel);
682
ch->submitted_samples = 0;
683
ch->retrieved_samples = 0;
684
bcm2835_worker_play_start(sc);
685
break;
686
687
case PCMTRIG_STOP:
688
case PCMTRIG_ABORT:
689
bcm2835_worker_play_stop(sc);
690
break;
691
692
default:
693
break;
694
}
695
return 0;
696
}
697
698
static uint32_t
699
bcmchan_getptr(kobj_t obj, void *data)
700
{
701
struct bcm2835_audio_chinfo *ch = data;
702
struct bcm2835_audio_info *sc = ch->parent;
703
uint32_t ret;
704
705
BCM2835_AUDIO_LOCK(sc);
706
ret = ch->unsubmittedptr;
707
BCM2835_AUDIO_UNLOCK(sc);
708
709
return ret;
710
}
711
712
static struct pcmchan_caps *
713
bcmchan_getcaps(kobj_t obj, void *data)
714
{
715
716
return &bcm2835_audio_playcaps;
717
}
718
719
static kobj_method_t bcmchan_methods[] = {
720
KOBJMETHOD(channel_init, bcmchan_init),
721
KOBJMETHOD(channel_free, bcmchan_free),
722
KOBJMETHOD(channel_setformat, bcmchan_setformat),
723
KOBJMETHOD(channel_setspeed, bcmchan_setspeed),
724
KOBJMETHOD(channel_setblocksize, bcmchan_setblocksize),
725
KOBJMETHOD(channel_trigger, bcmchan_trigger),
726
KOBJMETHOD(channel_getptr, bcmchan_getptr),
727
KOBJMETHOD(channel_getcaps, bcmchan_getcaps),
728
KOBJMETHOD_END
729
};
730
CHANNEL_DECLARE(bcmchan);
731
732
/************************************************************/
733
734
static int
735
bcmmix_init(struct snd_mixer *m)
736
{
737
738
mix_setdevs(m, SOUND_MASK_VOLUME);
739
740
return (0);
741
}
742
743
static int
744
bcmmix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
745
{
746
struct bcm2835_audio_info *sc = mix_getdevinfo(m);
747
748
switch (dev) {
749
case SOUND_MIXER_VOLUME:
750
BCM2835_AUDIO_LOCK(sc);
751
sc->volume = left;
752
bcm2835_worker_update_params(sc);
753
BCM2835_AUDIO_UNLOCK(sc);
754
755
break;
756
757
default:
758
break;
759
}
760
761
return left | (left << 8);
762
}
763
764
static kobj_method_t bcmmixer_methods[] = {
765
KOBJMETHOD(mixer_init, bcmmix_init),
766
KOBJMETHOD(mixer_set, bcmmix_set),
767
KOBJMETHOD_END
768
};
769
770
MIXER_DECLARE(bcmmixer);
771
772
static int
773
sysctl_bcm2835_audio_dest(SYSCTL_HANDLER_ARGS)
774
{
775
struct bcm2835_audio_info *sc = arg1;
776
int val;
777
int err;
778
779
val = sc->dest;
780
err = sysctl_handle_int(oidp, &val, 0, req);
781
if (err || !req->newptr) /* error || read request */
782
return (err);
783
784
if ((val < 0) || (val > 2))
785
return (EINVAL);
786
787
BCM2835_AUDIO_LOCK(sc);
788
sc->dest = val;
789
bcm2835_worker_update_params(sc);
790
BCM2835_AUDIO_UNLOCK(sc);
791
792
if (bootverbose)
793
device_printf(sc->dev, "destination set to %s\n", dest_description(val));
794
795
return (0);
796
}
797
798
static void
799
vchi_audio_sysctl_init(struct bcm2835_audio_info *sc)
800
{
801
struct sysctl_ctx_list *ctx;
802
struct sysctl_oid *tree_node;
803
struct sysctl_oid_list *tree;
804
805
/*
806
* Add system sysctl tree/handlers.
807
*/
808
ctx = device_get_sysctl_ctx(sc->dev);
809
tree_node = device_get_sysctl_tree(sc->dev);
810
tree = SYSCTL_CHILDREN(tree_node);
811
SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "dest",
812
CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_NEEDGIANT, sc, sizeof(*sc),
813
sysctl_bcm2835_audio_dest, "IU", "audio destination, "
814
"0 - auto, 1 - headphones, 2 - HDMI");
815
SYSCTL_ADD_UQUAD(ctx, tree, OID_AUTO, "callbacks",
816
CTLFLAG_RD, &sc->pch.callbacks,
817
"callbacks total");
818
SYSCTL_ADD_UQUAD(ctx, tree, OID_AUTO, "submitted",
819
CTLFLAG_RD, &sc->pch.submitted_samples,
820
"last play submitted samples");
821
SYSCTL_ADD_UQUAD(ctx, tree, OID_AUTO, "retrieved",
822
CTLFLAG_RD, &sc->pch.retrieved_samples,
823
"last play retrieved samples");
824
SYSCTL_ADD_UQUAD(ctx, tree, OID_AUTO, "underruns",
825
CTLFLAG_RD, &sc->pch.underruns,
826
"callback underruns");
827
SYSCTL_ADD_INT(ctx, tree, OID_AUTO, "freebuffer",
828
CTLFLAG_RD, &sc->pch.available_space,
829
sc->pch.available_space, "callbacks total");
830
SYSCTL_ADD_INT(ctx, tree, OID_AUTO, "starved",
831
CTLFLAG_RD, &sc->pch.starved,
832
sc->pch.starved, "number of starved conditions");
833
}
834
835
static void
836
bcm2835_audio_identify(driver_t *driver, device_t parent)
837
{
838
839
BUS_ADD_CHILD(parent, 0, "pcm", 0);
840
}
841
842
static int
843
bcm2835_audio_probe(device_t dev)
844
{
845
846
device_set_desc(dev, "VCHIQ audio");
847
return (BUS_PROBE_DEFAULT);
848
}
849
850
static void
851
bcm2835_audio_delayed_init(void *xsc)
852
{
853
struct bcm2835_audio_info *sc;
854
char status[SND_STATUSLEN];
855
856
sc = xsc;
857
858
config_intrhook_disestablish(&sc->intr_hook);
859
860
bcm2835_audio_init(sc);
861
bcm2835_audio_open(sc);
862
sc->volume = 75;
863
sc->dest = DEST_AUTO;
864
865
if (mixer_init(sc->dev, &bcmmixer_class, sc)) {
866
device_printf(sc->dev, "mixer_init failed\n");
867
goto no;
868
}
869
870
pcm_init(sc->dev, sc);
871
872
pcm_addchan(sc->dev, PCMDIR_PLAY, &bcmchan_class, sc);
873
snprintf(status, SND_STATUSLEN, "at VCHIQ");
874
if (pcm_register(sc->dev, status)) {
875
device_printf(sc->dev, "pcm_register failed\n");
876
goto no;
877
}
878
879
bcm2835_audio_reset_channel(&sc->pch);
880
bcm2835_audio_create_worker(sc);
881
882
vchi_audio_sysctl_init(sc);
883
884
no:
885
;
886
}
887
888
static int
889
bcm2835_audio_attach(device_t dev)
890
{
891
struct bcm2835_audio_info *sc;
892
893
sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO);
894
895
sc->dev = dev;
896
sc->bufsz = VCHIQ_AUDIO_BUFFER_SIZE;
897
898
mtx_init(&sc->lock, device_get_nameunit(dev),
899
"bcm_audio_lock", MTX_DEF);
900
cv_init(&sc->worker_cv, "worker_cv");
901
sc->vchi_handle = VCHIQ_SERVICE_HANDLE_INVALID;
902
903
/*
904
* We need interrupts enabled for VCHI to work properly,
905
* so delay initialization until it happens.
906
*/
907
sc->intr_hook.ich_func = bcm2835_audio_delayed_init;
908
sc->intr_hook.ich_arg = sc;
909
910
if (config_intrhook_establish(&sc->intr_hook) != 0)
911
goto no;
912
913
return 0;
914
915
no:
916
return ENXIO;
917
}
918
919
static int
920
bcm2835_audio_detach(device_t dev)
921
{
922
int r;
923
struct bcm2835_audio_info *sc;
924
sc = pcm_getdevinfo(dev);
925
926
/* Stop worker thread */
927
BCM2835_AUDIO_LOCK(sc);
928
sc->worker_state = WORKER_STOPPING;
929
cv_signal(&sc->worker_cv);
930
/* Wait for thread to exit */
931
while (sc->worker_state != WORKER_STOPPED)
932
cv_wait_sig(&sc->worker_cv, &sc->lock);
933
BCM2835_AUDIO_UNLOCK(sc);
934
935
r = pcm_unregister(dev);
936
if (r)
937
return r;
938
939
mtx_destroy(&sc->lock);
940
cv_destroy(&sc->worker_cv);
941
942
bcm2835_audio_release(sc);
943
944
free(sc, M_DEVBUF);
945
946
return 0;
947
}
948
949
static device_method_t bcm2835_audio_methods[] = {
950
/* Device interface */
951
DEVMETHOD(device_identify, bcm2835_audio_identify),
952
DEVMETHOD(device_probe, bcm2835_audio_probe),
953
DEVMETHOD(device_attach, bcm2835_audio_attach),
954
DEVMETHOD(device_detach, bcm2835_audio_detach),
955
{ 0, 0 }
956
};
957
958
static driver_t bcm2835_audio_driver = {
959
"pcm",
960
bcm2835_audio_methods,
961
PCM_SOFTC_SIZE,
962
};
963
964
DRIVER_MODULE(bcm2835_audio, vchiq, bcm2835_audio_driver, 0, 0);
965
MODULE_DEPEND(bcm2835_audio, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
966
MODULE_DEPEND(bcm2835_audio, vchiq, 1, 1, 1);
967
MODULE_VERSION(bcm2835_audio, 1);
968
969