bcm2835_vcaudio.c revision 1.2.4.2 1 /* $NetBSD: bcm2835_vcaudio.c,v 1.2.4.2 2013/06/23 06:20:00 tls Exp $ */
2
3 /*-
4 * Copyright (c) 2013 Jared D. McNeill <jmcneill (at) invisible.ca>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 /*
30 * VideoCore audio interface
31 */
32
33 #include <sys/cdefs.h>
34 __KERNEL_RCSID(0, "$NetBSD: bcm2835_vcaudio.c,v 1.2.4.2 2013/06/23 06:20:00 tls Exp $");
35
36 #include <sys/param.h>
37 #include <sys/types.h>
38 #include <sys/systm.h>
39 #include <sys/device.h>
40 #include <sys/conf.h>
41 #include <sys/bus.h>
42 #include <sys/kmem.h>
43 #include <sys/workqueue.h>
44
45 #include <sys/audioio.h>
46 #include <dev/audio_if.h>
47 #include <dev/auconv.h>
48
49 #include <interface/compat/vchi_bsd.h>
50 #include <interface/vchiq_arm/vchiq_netbsd.h>
51 #include <interface/vchi/vchi.h>
52
53 #include "bcm2835_vcaudioreg.h"
54
55 #define vol2pct(vol) (((vol) * 100) / 255)
56
57 enum {
58 VCAUDIO_OUTPUT_CLASS,
59 VCAUDIO_INPUT_CLASS,
60 VCAUDIO_OUTPUT_MASTER_VOLUME,
61 VCAUDIO_INPUT_DAC_VOLUME,
62 VCAUDIO_ENUM_LAST,
63 };
64
65 enum vcaudio_dest {
66 VCAUDIO_DEST_AUTO = 0,
67 VCAUDIO_DEST_HP = 1,
68 VCAUDIO_DEST_HDMI = 2,
69 };
70
71 struct vcaudio_work {
72 struct work vw_wk;
73 };
74
75 struct vcaudio_softc {
76 device_t sc_dev;
77 device_t sc_audiodev;
78
79 kmutex_t sc_lock;
80 kmutex_t sc_intr_lock;
81 kcondvar_t sc_cv;
82
83 struct audio_format sc_format;
84 struct audio_encoding_set *sc_encodings;
85
86 void (*sc_pint)(void *);
87 void *sc_pintarg;
88 audio_params_t sc_pparam;
89 bool sc_started;
90 int sc_pbytes;
91 off_t sc_ppos;
92 void *sc_pstart;
93 void *sc_pend;
94 int sc_pblksize;
95
96 int sc_success;
97
98 VCHI_INSTANCE_T sc_instance;
99 VCHI_CONNECTION_T sc_connection;
100 VCHI_SERVICE_HANDLE_T sc_service;
101
102 struct workqueue *sc_wq;
103 struct vcaudio_work sc_work;
104
105 int sc_volume;
106 enum vcaudio_dest sc_dest;
107 };
108
109 static int vcaudio_match(device_t, cfdata_t, void *);
110 static void vcaudio_attach(device_t, device_t, void *);
111 static int vcaudio_rescan(device_t, const char *, const int *);
112 static void vcaudio_childdet(device_t, device_t);
113
114 static int vcaudio_init(struct vcaudio_softc *);
115 static void vcaudio_service_callback(void *,
116 const VCHI_CALLBACK_REASON_T,
117 void *);
118 static int vcaudio_msg_sync(struct vcaudio_softc *, VC_AUDIO_MSG_T *, size_t);
119 static void vcaudio_worker(struct work *, void *);
120
121 static int vcaudio_open(void *, int);
122 static void vcaudio_close(void *);
123 static int vcaudio_query_encoding(void *, struct audio_encoding *);
124 static int vcaudio_set_params(void *, int, int,
125 audio_params_t *, audio_params_t *,
126 stream_filter_list_t *,
127 stream_filter_list_t *);
128 static int vcaudio_halt_output(void *);
129 static int vcaudio_halt_input(void *);
130 static int vcaudio_set_port(void *, mixer_ctrl_t *);
131 static int vcaudio_get_port(void *, mixer_ctrl_t *);
132 static int vcaudio_query_devinfo(void *, mixer_devinfo_t *);
133 static int vcaudio_getdev(void *, struct audio_device *);
134 static int vcaudio_get_props(void *);
135 static int vcaudio_round_blocksize(void *, int, int, const audio_params_t *);
136 static size_t vcaudio_round_buffersize(void *, int, size_t);
137 static int vcaudio_trigger_output(void *, void *, void *, int,
138 void (*)(void *), void *,
139 const audio_params_t *);
140 static int vcaudio_trigger_input(void *, void *, void *, int,
141 void (*)(void *), void *,
142 const audio_params_t *);
143 static void vcaudio_get_locks(void *, kmutex_t **, kmutex_t **);
144
145 static const struct audio_hw_if vcaudio_hw_if = {
146 .open = vcaudio_open,
147 .close = vcaudio_close,
148 .query_encoding = vcaudio_query_encoding,
149 .set_params = vcaudio_set_params,
150 .halt_output = vcaudio_halt_output,
151 .halt_input = vcaudio_halt_input,
152 .getdev = vcaudio_getdev,
153 .set_port = vcaudio_set_port,
154 .get_port = vcaudio_get_port,
155 .query_devinfo = vcaudio_query_devinfo,
156 .get_props = vcaudio_get_props,
157 .round_blocksize = vcaudio_round_blocksize,
158 .round_buffersize = vcaudio_round_buffersize,
159 .trigger_output = vcaudio_trigger_output,
160 .trigger_input = vcaudio_trigger_input,
161 .get_locks = vcaudio_get_locks,
162 };
163
164 CFATTACH_DECL2_NEW(vcaudio, sizeof(struct vcaudio_softc),
165 vcaudio_match, vcaudio_attach, NULL, NULL, vcaudio_rescan, vcaudio_childdet);
166
167 static int
168 vcaudio_match(device_t parent, cfdata_t match, void *aux)
169 {
170 struct vchiq_attach_args *vaa = aux;
171
172 return !strcmp(vaa->vaa_name, "AUDS");
173 }
174
175 static void
176 vcaudio_attach(device_t parent, device_t self, void *aux)
177 {
178 struct vcaudio_softc *sc = device_private(self);
179 int error;
180
181 sc->sc_dev = self;
182 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
183 mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SCHED);
184 cv_init(&sc->sc_cv, "vcaudiocv");
185 error = workqueue_create(&sc->sc_wq, "vcaudiowq", vcaudio_worker,
186 sc, PRI_BIO, IPL_SCHED, WQ_MPSAFE);
187 if (error) {
188 aprint_error(": couldn't create workqueue (%d)\n", error);
189 return;
190 }
191
192 aprint_naive("\n");
193 aprint_normal(": AUDS\n");
194
195 if (vcaudio_init(sc) != 0) {
196 aprint_error_dev(self, "not configured\n");
197 return;
198 }
199
200 vcaudio_rescan(self, NULL, NULL);
201 }
202
203 static int
204 vcaudio_rescan(device_t self, const char *ifattr, const int *locs)
205 {
206 struct vcaudio_softc *sc = device_private(self);
207
208 if (ifattr_match(ifattr, "audiobus") && sc->sc_audiodev == NULL) {
209 sc->sc_audiodev = audio_attach_mi(&vcaudio_hw_if,
210 sc, sc->sc_dev);
211 }
212 return 0;
213 }
214
215 static void
216 vcaudio_childdet(device_t self, device_t child)
217 {
218 struct vcaudio_softc *sc = device_private(self);
219
220 if (sc->sc_audiodev == child)
221 sc->sc_audiodev = NULL;
222 }
223
224 static int
225 vcaudio_init(struct vcaudio_softc *sc)
226 {
227 VC_AUDIO_MSG_T msg;
228 int error;
229
230 sc->sc_volume = 128;
231 sc->sc_dest = VCAUDIO_DEST_AUTO;
232
233 sc->sc_format.mode = AUMODE_PLAY|AUMODE_RECORD;
234 sc->sc_format.encoding = AUDIO_ENCODING_SLINEAR_LE;
235 sc->sc_format.validbits = 16;
236 sc->sc_format.precision = 16;
237 sc->sc_format.channels = 2;
238 sc->sc_format.channel_mask = AUFMT_STEREO;
239 sc->sc_format.frequency_type = 0;
240 sc->sc_format.frequency[0] = 8000;
241 sc->sc_format.frequency[1] = 48000;
242
243 error = auconv_create_encodings(&sc->sc_format, 1, &sc->sc_encodings);
244 if (error) {
245 aprint_error_dev(sc->sc_dev,
246 "couldn't create encodings (error=%d)\n", error);
247 return error;
248 }
249
250 error = vchi_initialise(&sc->sc_instance);
251 if (error) {
252 device_printf(sc->sc_dev, "couldn't init vchi instance (%d)\n",
253 error);
254 return EIO;
255 }
256
257 error = vchi_connect(NULL, 0, sc->sc_instance);
258 if (error) {
259 device_printf(sc->sc_dev, "couldn't connect vchi (%d)\n",
260 error);
261 return EIO;
262 }
263
264 SERVICE_CREATION_T setup = {
265 .version = VCHI_VERSION(VC_AUDIOSERV_VER),
266 .service_id = VC_AUDIO_SERVER_NAME,
267 .connection = &sc->sc_connection,
268 .rx_fifo_size = 0,
269 .tx_fifo_size = 0,
270 .callback = vcaudio_service_callback,
271 .callback_param = sc,
272 .want_unaligned_bulk_rx = 1,
273 .want_unaligned_bulk_tx = 1,
274 .want_crc = 0,
275 };
276
277 error = vchi_service_open(sc->sc_instance, &setup, &sc->sc_service);
278 if (error) {
279 device_printf(sc->sc_dev, "couldn't open service (%d)\n",
280 error);
281 return EIO;
282 }
283 vchi_service_release(sc->sc_service);
284
285 vchi_service_use(sc->sc_service);
286 memset(&msg, 0, sizeof(msg));
287 msg.type = VC_AUDIO_MSG_TYPE_OPEN;
288 error = vchi_msg_queue(sc->sc_service, &msg, sizeof(msg),
289 VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
290 if (error) {
291 device_printf(sc->sc_dev, "couldn't send OPEN message (%d)\n",
292 error);
293 }
294
295 memset(&msg, 0, sizeof(msg));
296 msg.type = VC_AUDIO_MSG_TYPE_CONTROL;
297 msg.u.control.volume = vol2pct(sc->sc_volume);
298 msg.u.control.dest = VCAUDIO_DEST_AUTO;
299 error = vcaudio_msg_sync(sc, &msg, sizeof(msg));
300 if (error) {
301 device_printf(sc->sc_dev, "couldn't send CONTROL message (%d)\n", error);
302 }
303 vchi_service_release(sc->sc_service);
304
305 return 0;
306 }
307
308 static void
309 vcaudio_service_callback(void *priv, const VCHI_CALLBACK_REASON_T reason,
310 void *msg_handle)
311 {
312 struct vcaudio_softc *sc = priv;
313 VC_AUDIO_MSG_T msg;
314 int32_t msglen = 0;
315 int error;
316 void (*intr)(void *) = NULL;
317 void *intrarg = NULL;
318
319 if (sc == NULL || reason != VCHI_CALLBACK_MSG_AVAILABLE)
320 return;
321
322 memset(&msg, 0, sizeof(msg));
323 error = vchi_msg_dequeue(sc->sc_service, &msg, sizeof(msg), &msglen,
324 VCHI_FLAGS_NONE);
325 if (error) {
326 device_printf(sc->sc_dev, "couldn't dequeue msg (%d)\n",
327 error);
328 return;
329 }
330
331 switch (msg.type) {
332 case VC_AUDIO_MSG_TYPE_RESULT:
333 mutex_enter(&sc->sc_intr_lock);
334 sc->sc_success = msg.u.result.success;
335 cv_broadcast(&sc->sc_cv);
336 mutex_exit(&sc->sc_intr_lock);
337 break;
338 case VC_AUDIO_MSG_TYPE_COMPLETE:
339 intr = msg.u.complete.callback;
340 intrarg = msg.u.complete.cookie;
341 if (intr && intrarg) {
342 if (msg.u.complete.count > 0 && msg.u.complete.count <= sc->sc_pblksize) {
343 sc->sc_pbytes += msg.u.complete.count;
344 } else {
345 if (sc->sc_started) {
346 device_printf(sc->sc_dev, "WARNING: count = %d\n", msg.u.complete.count);
347 }
348 }
349 if (sc->sc_pbytes >= sc->sc_pblksize) {
350 sc->sc_pbytes -= sc->sc_pblksize;
351 mutex_enter(&sc->sc_intr_lock);
352 intr(intrarg);
353 mutex_exit(&sc->sc_intr_lock);
354 workqueue_enqueue(sc->sc_wq, (struct work *)&sc->sc_work, NULL);
355 }
356 }
357 break;
358 default:
359 break;
360 }
361 }
362
363 static void
364 vcaudio_worker(struct work *wk, void *priv)
365 {
366 struct vcaudio_softc *sc = priv;
367 VC_AUDIO_MSG_T msg;
368 void (*intr)(void *);
369 void *intrarg;
370 void *block;
371 int error, resid, off, nb, count;
372
373 mutex_enter(&sc->sc_intr_lock);
374 intr = sc->sc_pint;
375 intrarg = sc->sc_pintarg;
376 mutex_exit(&sc->sc_intr_lock);
377
378 if (intr == NULL || intrarg == NULL)
379 return;
380
381 vchi_service_use(sc->sc_service);
382
383 if (sc->sc_started == false) {
384 #ifdef VCAUDIO_DEBUG
385 device_printf(sc->sc_dev, "starting output\n");
386 #endif
387
388 memset(&msg, 0, sizeof(msg));
389 msg.type = VC_AUDIO_MSG_TYPE_CONFIG;
390 msg.u.config.channels = sc->sc_pparam.channels;
391 msg.u.config.samplerate = sc->sc_pparam.sample_rate;
392 msg.u.config.bps = sc->sc_pparam.precision;
393 error = vcaudio_msg_sync(sc, &msg, sizeof(msg));
394 if (error) {
395 printf("%s: failed to config (%d)\n", __func__, error);
396 goto done;
397 }
398
399 memset(&msg, 0, sizeof(msg));
400 msg.type = VC_AUDIO_MSG_TYPE_START;
401 error = vchi_msg_queue(sc->sc_service, &msg, sizeof(msg),
402 VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
403 if (error) {
404 printf("%s: failed to start (%d)\n", __func__, error);
405 goto done;
406 }
407
408 sc->sc_started = true;
409 sc->sc_pbytes = 0;
410 sc->sc_ppos = 0;
411 sc->sc_pblksize = sc->sc_pblksize;
412 count = (uintptr_t)sc->sc_pend - (uintptr_t)sc->sc_pstart;
413
414 /* initial silence */
415 memset(&msg, 0, sizeof(msg));
416 msg.type = VC_AUDIO_MSG_TYPE_WRITE;
417 msg.u.write.count = PAGE_SIZE * 3;
418 msg.u.write.callback = NULL;
419 msg.u.write.cookie = NULL;
420 msg.u.write.silence = 1;
421 msg.u.write.max_packet = 0;
422 error = vchi_msg_queue(sc->sc_service, &msg, sizeof(msg),
423 VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
424 if (error) {
425 printf("%s: failed to write (%d)\n", __func__, error);
426 mutex_exit(&sc->sc_intr_lock);
427 goto done;
428 }
429 } else {
430 count = sc->sc_pblksize;
431 }
432
433 memset(&msg, 0, sizeof(msg));
434 msg.type = VC_AUDIO_MSG_TYPE_WRITE;
435 msg.u.write.max_packet = 4000;
436 msg.u.write.count = count;
437 msg.u.write.callback = intr;
438 msg.u.write.cookie = intrarg;
439 msg.u.write.silence = 0;
440
441 error = vchi_msg_queue(sc->sc_service, &msg, sizeof(msg),
442 VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
443 if (error) {
444 printf("%s: failed to write (%d)\n", __func__, error);
445 mutex_exit(&sc->sc_intr_lock);
446 goto done;
447 }
448
449 block = (uint8_t *)sc->sc_pstart + sc->sc_ppos;
450 resid = count;
451 off = 0;
452 while (resid > 0) {
453 nb = min(resid, msg.u.write.max_packet);
454 error = vchi_msg_queue(sc->sc_service,
455 (char *)block + off, nb,
456 VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
457 if (error) {
458 printf("%s: failed to queue data (%d)\n", __func__, error);
459 goto done;
460 }
461 off += nb;
462 resid -= nb;
463 }
464
465 sc->sc_ppos += count;
466 if ((uint8_t *)sc->sc_pstart + sc->sc_ppos >= (uint8_t *)sc->sc_pend)
467 sc->sc_ppos = 0;
468
469 done:
470 vchi_service_release(sc->sc_service);
471 }
472
473 static int
474 vcaudio_msg_sync(struct vcaudio_softc *sc, VC_AUDIO_MSG_T *msg, size_t msglen)
475 {
476 int error = 0;
477
478 mutex_enter(&sc->sc_intr_lock);
479 sc->sc_success = -1;
480 error = vchi_msg_queue(sc->sc_service, msg, msglen,
481 VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
482 if (error) {
483 printf("%s: failed to queue message (%d)\n", __func__, error);
484 goto done;
485 }
486 while (sc->sc_success == -1) {
487 error = cv_wait_sig(&sc->sc_cv, &sc->sc_intr_lock);
488 if (error)
489 break;
490 }
491 if (sc->sc_success > 0)
492 error = EIO;
493
494 done:
495 mutex_exit(&sc->sc_intr_lock);
496
497 return error;
498 }
499
500 static int
501 vcaudio_open(void *priv, int flags)
502 {
503 return 0;
504 }
505
506 static void
507 vcaudio_close(void *priv)
508 {
509 }
510
511 static int
512 vcaudio_query_encoding(void *priv, struct audio_encoding *ae)
513 {
514 struct vcaudio_softc *sc = priv;
515
516 return auconv_query_encoding(sc->sc_encodings, ae);
517 }
518
519 static int
520 vcaudio_set_params(void *priv, int setmode, int usemode,
521 audio_params_t *play, audio_params_t *rec,
522 stream_filter_list_t *pfil, stream_filter_list_t *rfil)
523 {
524 struct vcaudio_softc *sc = priv;
525 int index;
526
527 if (play && (setmode & AUMODE_PLAY)) {
528 index = auconv_set_converter(&sc->sc_format, 1,
529 AUMODE_PLAY, play, true, pfil);
530 if (index < 0)
531 return EINVAL;
532 }
533
534 return 0;
535 }
536
537 static int
538 vcaudio_halt_output(void *priv)
539 {
540 struct vcaudio_softc *sc = priv;
541 VC_AUDIO_MSG_T msg;
542 int error = 0;
543
544 vchi_service_use(sc->sc_service);
545 memset(&msg, 0, sizeof(msg));
546 msg.type = VC_AUDIO_MSG_TYPE_STOP;
547 msg.u.stop.draining = 1;
548 error = vchi_msg_queue(sc->sc_service, &msg, sizeof(msg),
549 VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
550 if (error) {
551 device_printf(sc->sc_dev, "couldn't send STOP message (%d)\n",
552 error);
553 }
554 vchi_service_release(sc->sc_service);
555
556 sc->sc_pint = NULL;
557 sc->sc_pintarg = NULL;
558 sc->sc_started = false;
559
560 #ifdef VCAUDIO_DEBUG
561 device_printf(sc->sc_dev, "halting output\n");
562 #endif
563
564 return error;
565 }
566
567 static int
568 vcaudio_halt_input(void *priv)
569 {
570 return EINVAL;
571 }
572
573 static int
574 vcaudio_set_port(void *priv, mixer_ctrl_t *mc)
575 {
576 struct vcaudio_softc *sc = priv;
577 VC_AUDIO_MSG_T msg;
578 int error;
579
580 switch (mc->dev) {
581 case VCAUDIO_OUTPUT_MASTER_VOLUME:
582 case VCAUDIO_INPUT_DAC_VOLUME:
583 sc->sc_volume = mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
584 memset(&msg, 0, sizeof(msg));
585 msg.type = VC_AUDIO_MSG_TYPE_CONTROL;
586 msg.u.control.volume = vol2pct(sc->sc_volume);
587 msg.u.control.dest = VCAUDIO_DEST_AUTO;
588 vchi_service_use(sc->sc_service);
589 error = vcaudio_msg_sync(sc, &msg, sizeof(msg));
590 if (error) {
591 device_printf(sc->sc_dev, "couldn't send CONTROL message (%d)\n", error);
592 }
593 vchi_service_release(sc->sc_service);
594
595 return error;
596 }
597 return ENXIO;
598 }
599
600 static int
601 vcaudio_get_port(void *priv, mixer_ctrl_t *mc)
602 {
603 struct vcaudio_softc *sc = priv;
604
605 switch (mc->dev) {
606 case VCAUDIO_OUTPUT_MASTER_VOLUME:
607 case VCAUDIO_INPUT_DAC_VOLUME:
608 mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] =
609 mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] =
610 sc->sc_volume;
611 return 0;
612 }
613 return ENXIO;
614 }
615
616 static int
617 vcaudio_query_devinfo(void *priv, mixer_devinfo_t *di)
618 {
619 switch (di->index) {
620 case VCAUDIO_OUTPUT_CLASS:
621 di->mixer_class = VCAUDIO_OUTPUT_CLASS;
622 strcpy(di->label.name, AudioCoutputs);
623 di->type = AUDIO_MIXER_CLASS;
624 di->next = di->prev = AUDIO_MIXER_LAST;
625 return 0;
626 case VCAUDIO_INPUT_CLASS:
627 di->mixer_class = VCAUDIO_INPUT_CLASS;
628 strcpy(di->label.name, AudioCinputs);
629 di->type = AUDIO_MIXER_CLASS;
630 di->next = di->prev = AUDIO_MIXER_LAST;
631 return 0;
632 case VCAUDIO_OUTPUT_MASTER_VOLUME:
633 di->mixer_class = VCAUDIO_OUTPUT_CLASS;
634 strcpy(di->label.name, AudioNmaster);
635 di->type = AUDIO_MIXER_VALUE;
636 di->next = di->prev = AUDIO_MIXER_LAST;
637 di->un.v.num_channels = 2;
638 strcpy(di->un.v.units.name, AudioNvolume);
639 return 0;
640 case VCAUDIO_INPUT_DAC_VOLUME:
641 di->mixer_class = VCAUDIO_INPUT_CLASS;
642 strcpy(di->label.name, AudioNdac);
643 di->type = AUDIO_MIXER_VALUE;
644 di->next = di->prev = AUDIO_MIXER_LAST;
645 di->un.v.num_channels = 2;
646 strcpy(di->un.v.units.name, AudioNvolume);
647 return 0;
648 }
649
650 return ENXIO;
651 }
652
653 static int
654 vcaudio_getdev(void *priv, struct audio_device *audiodev)
655 {
656 snprintf(audiodev->name, sizeof(audiodev->name), "VCHIQ AUDS");
657 snprintf(audiodev->version, sizeof(audiodev->version), "");
658 snprintf(audiodev->config, sizeof(audiodev->config), "vcaudio");
659 return 0;
660 }
661
662 static int
663 vcaudio_get_props(void *priv)
664 {
665 return AUDIO_PROP_PLAYBACK|AUDIO_PROP_CAPTURE|AUDIO_PROP_INDEPENDENT;
666 }
667
668 static int
669 vcaudio_round_blocksize(void *priv, int bs, int mode,
670 const audio_params_t *params)
671 {
672 return PAGE_SIZE;
673 }
674
675 static size_t
676 vcaudio_round_buffersize(void *priv, int direction, size_t bufsize)
677 {
678 size_t sz;
679
680 sz = (bufsize + 0x3ff) & ~0x3ff;
681 if (sz > 32768)
682 sz = 32768;
683
684 return sz;
685 }
686
687 static int
688 vcaudio_trigger_output(void *priv, void *start, void *end, int blksize,
689 void (*intr)(void *), void *intrarg, const audio_params_t *params)
690 {
691 struct vcaudio_softc *sc = priv;
692
693 sc->sc_pparam = *params;
694 sc->sc_pint = intr;
695 sc->sc_pintarg = intrarg;
696 sc->sc_ppos = 0;
697 sc->sc_pstart = start;
698 sc->sc_pend = end;
699 sc->sc_pblksize = blksize;
700 workqueue_enqueue(sc->sc_wq, (struct work *)&sc->sc_work, NULL);
701
702 return 0;
703 }
704
705 static int
706 vcaudio_trigger_input(void *priv, void *start, void *end, int blksize,
707 void (*intr)(void *), void *intrarg, const audio_params_t *params)
708 {
709 return EINVAL;
710 }
711
712 static void
713 vcaudio_get_locks(void *priv, kmutex_t **intr, kmutex_t **thread)
714 {
715 struct vcaudio_softc *sc = priv;
716
717 *intr = &sc->sc_intr_lock;
718 *thread = &sc->sc_lock;
719 }
720