1 /* $NetBSD: bcm2835_vcaudio.c,v 1.6 2014/09/02 21:46:35 jmcneill Exp $ */ 2 3 /*- 4 * Copyright (c) 2013 Jared D. McNeill <jmcneill@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.6 2014/09/02 21:46:35 jmcneill 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 82 kmutex_t sc_msglock; 83 kcondvar_t sc_msgcv; 84 85 struct audio_format sc_format; 86 struct audio_encoding_set *sc_encodings; 87 88 void (*sc_pint)(void *); 89 void *sc_pintarg; 90 audio_params_t sc_pparam; 91 bool sc_started; 92 int sc_pbytes; 93 off_t sc_ppos; 94 void *sc_pstart; 95 void *sc_pend; 96 int sc_pblksize; 97 98 bool sc_msgdone; 99 int sc_success; 100 101 VCHI_INSTANCE_T sc_instance; 102 VCHI_CONNECTION_T sc_connection; 103 VCHI_SERVICE_HANDLE_T sc_service; 104 105 short sc_peer_version; 106 107 struct workqueue *sc_wq; 108 struct vcaudio_work sc_work; 109 110 int sc_volume; 111 enum vcaudio_dest sc_dest; 112 }; 113 114 static int vcaudio_match(device_t, cfdata_t, void *); 115 static void vcaudio_attach(device_t, device_t, void *); 116 static int vcaudio_rescan(device_t, const char *, const int *); 117 static void vcaudio_childdet(device_t, device_t); 118 119 static int vcaudio_init(struct vcaudio_softc *); 120 static void vcaudio_service_callback(void *, 121 const VCHI_CALLBACK_REASON_T, 122 void *); 123 static int vcaudio_msg_sync(struct vcaudio_softc *, VC_AUDIO_MSG_T *, size_t); 124 static void vcaudio_worker(struct work *, void *); 125 126 static int vcaudio_open(void *, int); 127 static void vcaudio_close(void *); 128 static int vcaudio_query_encoding(void *, struct audio_encoding *); 129 static int vcaudio_set_params(void *, int, int, 130 audio_params_t *, audio_params_t *, 131 stream_filter_list_t *, 132 stream_filter_list_t *); 133 static int vcaudio_halt_output(void *); 134 static int vcaudio_halt_input(void *); 135 static int vcaudio_set_port(void *, mixer_ctrl_t *); 136 static int vcaudio_get_port(void *, mixer_ctrl_t *); 137 static int vcaudio_query_devinfo(void *, mixer_devinfo_t *); 138 static int vcaudio_getdev(void *, struct audio_device *); 139 static int vcaudio_get_props(void *); 140 static int vcaudio_round_blocksize(void *, int, int, const audio_params_t *); 141 static size_t vcaudio_round_buffersize(void *, int, size_t); 142 static int vcaudio_trigger_output(void *, void *, void *, int, 143 void (*)(void *), void *, 144 const audio_params_t *); 145 static int vcaudio_trigger_input(void *, void *, void *, int, 146 void (*)(void *), void *, 147 const audio_params_t *); 148 static void vcaudio_get_locks(void *, kmutex_t **, kmutex_t **); 149 150 static const struct audio_hw_if vcaudio_hw_if = { 151 .open = vcaudio_open, 152 .close = vcaudio_close, 153 .query_encoding = vcaudio_query_encoding, 154 .set_params = vcaudio_set_params, 155 .halt_output = vcaudio_halt_output, 156 .halt_input = vcaudio_halt_input, 157 .getdev = vcaudio_getdev, 158 .set_port = vcaudio_set_port, 159 .get_port = vcaudio_get_port, 160 .query_devinfo = vcaudio_query_devinfo, 161 .get_props = vcaudio_get_props, 162 .round_blocksize = vcaudio_round_blocksize, 163 .round_buffersize = vcaudio_round_buffersize, 164 .trigger_output = vcaudio_trigger_output, 165 .trigger_input = vcaudio_trigger_input, 166 .get_locks = vcaudio_get_locks, 167 }; 168 169 CFATTACH_DECL2_NEW(vcaudio, sizeof(struct vcaudio_softc), 170 vcaudio_match, vcaudio_attach, NULL, NULL, vcaudio_rescan, vcaudio_childdet); 171 172 static int 173 vcaudio_match(device_t parent, cfdata_t match, void *aux) 174 { 175 struct vchiq_attach_args *vaa = aux; 176 177 return !strcmp(vaa->vaa_name, "AUDS"); 178 } 179 180 static void 181 vcaudio_attach(device_t parent, device_t self, void *aux) 182 { 183 struct vcaudio_softc *sc = device_private(self); 184 int error; 185 186 sc->sc_dev = self; 187 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE); 188 mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_NONE); 189 mutex_init(&sc->sc_msglock, MUTEX_DEFAULT, IPL_NONE); 190 cv_init(&sc->sc_msgcv, "vcaudiocv"); 191 sc->sc_success = -1; 192 error = workqueue_create(&sc->sc_wq, "vcaudiowq", vcaudio_worker, 193 sc, PRI_BIO, IPL_SCHED, WQ_MPSAFE); 194 if (error) { 195 aprint_error(": couldn't create workqueue (%d)\n", error); 196 return; 197 } 198 199 aprint_naive("\n"); 200 aprint_normal(": AUDS\n"); 201 202 vcaudio_rescan(self, NULL, NULL); 203 } 204 205 static int 206 vcaudio_rescan(device_t self, const char *ifattr, const int *locs) 207 { 208 struct vcaudio_softc *sc = device_private(self); 209 int error; 210 211 if (ifattr_match(ifattr, "audiobus") && sc->sc_audiodev == NULL) { 212 error = vcaudio_init(sc); 213 if (error) 214 return error; 215 216 sc->sc_audiodev = audio_attach_mi(&vcaudio_hw_if, 217 sc, sc->sc_dev); 218 } 219 return 0; 220 } 221 222 static void 223 vcaudio_childdet(device_t self, device_t child) 224 { 225 struct vcaudio_softc *sc = device_private(self); 226 227 if (sc->sc_audiodev == child) 228 sc->sc_audiodev = NULL; 229 } 230 231 static int 232 vcaudio_init(struct vcaudio_softc *sc) 233 { 234 VC_AUDIO_MSG_T msg; 235 int error; 236 237 sc->sc_volume = 128; 238 sc->sc_dest = VCAUDIO_DEST_AUTO; 239 240 sc->sc_format.mode = AUMODE_PLAY|AUMODE_RECORD; 241 sc->sc_format.encoding = AUDIO_ENCODING_SLINEAR_LE; 242 sc->sc_format.validbits = 16; 243 sc->sc_format.precision = 16; 244 sc->sc_format.channels = 2; 245 sc->sc_format.channel_mask = AUFMT_STEREO; 246 sc->sc_format.frequency_type = 0; 247 sc->sc_format.frequency[0] = 48000; 248 sc->sc_format.frequency[1] = 48000; 249 250 error = auconv_create_encodings(&sc->sc_format, 1, &sc->sc_encodings); 251 if (error) { 252 aprint_error_dev(sc->sc_dev, 253 "couldn't create encodings (error=%d)\n", error); 254 return error; 255 } 256 257 error = vchi_initialise(&sc->sc_instance); 258 if (error) { 259 device_printf(sc->sc_dev, "couldn't init vchi instance (%d)\n", 260 error); 261 return EIO; 262 } 263 264 error = vchi_connect(NULL, 0, sc->sc_instance); 265 if (error) { 266 device_printf(sc->sc_dev, "couldn't connect vchi (%d)\n", 267 error); 268 return EIO; 269 } 270 271 SERVICE_CREATION_T setup = { 272 .version = VCHI_VERSION(VC_AUDIOSERV_VER), 273 .service_id = VC_AUDIO_SERVER_NAME, 274 .connection = &sc->sc_connection, 275 .rx_fifo_size = 0, 276 .tx_fifo_size = 0, 277 .callback = vcaudio_service_callback, 278 .callback_param = sc, 279 .want_unaligned_bulk_rx = 1, 280 .want_unaligned_bulk_tx = 1, 281 .want_crc = 0, 282 }; 283 284 error = vchi_service_open(sc->sc_instance, &setup, &sc->sc_service); 285 if (error) { 286 device_printf(sc->sc_dev, "couldn't open service (%d)\n", 287 error); 288 return EIO; 289 } 290 291 vchi_get_peer_version(sc->sc_service, &sc->sc_peer_version); 292 293 if (sc->sc_peer_version < 2) { 294 aprint_error_dev(sc->sc_dev, 295 "peer version (%d) is less than the required version (2)\n", 296 sc->sc_peer_version); 297 return EINVAL; 298 } 299 300 memset(&msg, 0, sizeof(msg)); 301 msg.type = VC_AUDIO_MSG_TYPE_OPEN; 302 error = vchi_msg_queue(sc->sc_service, &msg, sizeof(msg), 303 VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); 304 if (error) { 305 device_printf(sc->sc_dev, "couldn't send OPEN message (%d)\n", 306 error); 307 } 308 309 memset(&msg, 0, sizeof(msg)); 310 msg.type = VC_AUDIO_MSG_TYPE_CONFIG; 311 msg.u.config.channels = 2; 312 msg.u.config.samplerate = 48000; 313 msg.u.config.bps = 16; 314 error = vcaudio_msg_sync(sc, &msg, sizeof(msg)); 315 if (error) { 316 device_printf(sc->sc_dev, "couldn't send CONFIG message (%d)\n", 317 error); 318 } 319 320 memset(&msg, 0, sizeof(msg)); 321 msg.type = VC_AUDIO_MSG_TYPE_CONTROL; 322 msg.u.control.volume = vol2pct(sc->sc_volume); 323 msg.u.control.dest = VCAUDIO_DEST_AUTO; 324 error = vcaudio_msg_sync(sc, &msg, sizeof(msg)); 325 if (error) { 326 device_printf(sc->sc_dev, "couldn't send CONTROL message (%d)\n", error); 327 } 328 vchi_service_release(sc->sc_service); 329 330 return 0; 331 } 332 333 static void 334 vcaudio_service_callback(void *priv, const VCHI_CALLBACK_REASON_T reason, 335 void *msg_handle) 336 { 337 struct vcaudio_softc *sc = priv; 338 VC_AUDIO_MSG_T msg; 339 int32_t msglen = 0; 340 int error; 341 void (*intr)(void *) = NULL; 342 void *intrarg = NULL; 343 344 if (sc == NULL || reason != VCHI_CALLBACK_MSG_AVAILABLE) 345 return; 346 347 memset(&msg, 0, sizeof(msg)); 348 error = vchi_msg_dequeue(sc->sc_service, &msg, sizeof(msg), &msglen, 349 VCHI_FLAGS_NONE); 350 if (error) { 351 device_printf(sc->sc_dev, "couldn't dequeue msg (%d)\n", 352 error); 353 return; 354 } 355 356 switch (msg.type) { 357 case VC_AUDIO_MSG_TYPE_RESULT: 358 mutex_enter(&sc->sc_msglock); 359 sc->sc_success = msg.u.result.success; 360 sc->sc_msgdone = true; 361 cv_broadcast(&sc->sc_msgcv); 362 mutex_exit(&sc->sc_msglock); 363 break; 364 case VC_AUDIO_MSG_TYPE_COMPLETE: 365 intr = msg.u.complete.callback; 366 intrarg = msg.u.complete.cookie; 367 if (intr && intrarg) { 368 int count = msg.u.complete.count & 0xffff; 369 int perr = (msg.u.complete.count & 0x40000000) != 0; 370 bool sched = false; 371 mutex_enter(&sc->sc_intr_lock); 372 if (count > 0) { 373 sc->sc_pbytes += count; 374 } 375 if (perr && sc->sc_started) { 376 #ifdef VCAUDIO_DEBUG 377 device_printf(sc->sc_dev, "underrun\n"); 378 #endif 379 sched = true; 380 } 381 if (sc->sc_pbytes >= sc->sc_pblksize) { 382 sc->sc_pbytes -= sc->sc_pblksize; 383 sched = true; 384 } 385 386 if (sched) { 387 intr(intrarg); 388 workqueue_enqueue(sc->sc_wq, (struct work *)&sc->sc_work, NULL); 389 } 390 mutex_exit(&sc->sc_intr_lock); 391 } 392 break; 393 default: 394 break; 395 } 396 } 397 398 static void 399 vcaudio_worker(struct work *wk, void *priv) 400 { 401 struct vcaudio_softc *sc = priv; 402 VC_AUDIO_MSG_T msg; 403 void (*intr)(void *); 404 void *intrarg; 405 void *block; 406 int error, resid, off, nb, count; 407 408 mutex_enter(&sc->sc_intr_lock); 409 intr = sc->sc_pint; 410 intrarg = sc->sc_pintarg; 411 412 if (intr == NULL || intrarg == NULL) { 413 mutex_exit(&sc->sc_intr_lock); 414 return; 415 } 416 417 vchi_service_use(sc->sc_service); 418 419 if (sc->sc_started == false) { 420 #ifdef VCAUDIO_DEBUG 421 device_printf(sc->sc_dev, "starting output\n"); 422 #endif 423 424 memset(&msg, 0, sizeof(msg)); 425 msg.type = VC_AUDIO_MSG_TYPE_START; 426 error = vchi_msg_queue(sc->sc_service, &msg, sizeof(msg), 427 VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); 428 if (error) { 429 printf("%s: failed to start (%d)\n", __func__, error); 430 goto done; 431 } 432 433 sc->sc_started = true; 434 sc->sc_pbytes = 0; 435 sc->sc_ppos = 0; 436 437 count = sc->sc_pblksize * 2; 438 } else { 439 count = sc->sc_pblksize; 440 } 441 442 memset(&msg, 0, sizeof(msg)); 443 msg.type = VC_AUDIO_MSG_TYPE_WRITE; 444 msg.u.write.max_packet = 4000; 445 msg.u.write.count = count; 446 msg.u.write.callback = intr; 447 msg.u.write.cookie = intrarg; 448 msg.u.write.silence = 0; 449 450 error = vchi_msg_queue(sc->sc_service, &msg, sizeof(msg), 451 VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); 452 if (error) { 453 printf("%s: failed to write (%d)\n", __func__, error); 454 goto done; 455 } 456 457 block = (uint8_t *)sc->sc_pstart + sc->sc_ppos; 458 resid = count; 459 off = 0; 460 while (resid > 0) { 461 nb = min(resid, msg.u.write.max_packet); 462 error = vchi_msg_queue(sc->sc_service, 463 (char *)block + off, nb, 464 VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); 465 if (error) { 466 printf("%s: failed to queue data (%d)\n", __func__, error); 467 goto done; 468 } 469 off += nb; 470 resid -= nb; 471 } 472 473 sc->sc_ppos += count; 474 if ((uint8_t *)sc->sc_pstart + sc->sc_ppos >= (uint8_t *)sc->sc_pend) 475 sc->sc_ppos = 0; 476 477 done: 478 mutex_exit(&sc->sc_intr_lock); 479 vchi_service_release(sc->sc_service); 480 } 481 482 static int 483 vcaudio_msg_sync(struct vcaudio_softc *sc, VC_AUDIO_MSG_T *msg, size_t msglen) 484 { 485 int error = 0; 486 487 mutex_enter(&sc->sc_msglock); 488 489 sc->sc_success = -1; 490 sc->sc_msgdone = false; 491 492 error = vchi_msg_queue(sc->sc_service, msg, msglen, 493 VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); 494 if (error) { 495 printf("%s: failed to queue message (%d)\n", __func__, error); 496 goto done; 497 } 498 499 while (!sc->sc_msgdone) { 500 error = cv_wait_sig(&sc->sc_msgcv, &sc->sc_msglock); 501 if (error) 502 break; 503 } 504 if (sc->sc_success != 0) 505 error = EIO; 506 done: 507 mutex_exit(&sc->sc_msglock); 508 509 return error; 510 } 511 512 static int 513 vcaudio_open(void *priv, int flags) 514 { 515 return 0; 516 } 517 518 static void 519 vcaudio_close(void *priv) 520 { 521 } 522 523 static int 524 vcaudio_query_encoding(void *priv, struct audio_encoding *ae) 525 { 526 struct vcaudio_softc *sc = priv; 527 528 return auconv_query_encoding(sc->sc_encodings, ae); 529 } 530 531 static int 532 vcaudio_set_params(void *priv, int setmode, int usemode, 533 audio_params_t *play, audio_params_t *rec, 534 stream_filter_list_t *pfil, stream_filter_list_t *rfil) 535 { 536 struct vcaudio_softc *sc = priv; 537 int index; 538 539 if (play && (setmode & AUMODE_PLAY)) { 540 index = auconv_set_converter(&sc->sc_format, 1, 541 AUMODE_PLAY, play, true, pfil); 542 if (index < 0) 543 return EINVAL; 544 } 545 546 return 0; 547 } 548 549 static int 550 vcaudio_halt_output(void *priv) 551 { 552 struct vcaudio_softc *sc = priv; 553 VC_AUDIO_MSG_T msg; 554 int error = 0; 555 556 vchi_service_use(sc->sc_service); 557 memset(&msg, 0, sizeof(msg)); 558 msg.type = VC_AUDIO_MSG_TYPE_STOP; 559 msg.u.stop.draining = 1; 560 error = vchi_msg_queue(sc->sc_service, &msg, sizeof(msg), 561 VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); 562 if (error) { 563 device_printf(sc->sc_dev, "couldn't send STOP message (%d)\n", 564 error); 565 } 566 vchi_service_release(sc->sc_service); 567 568 sc->sc_pint = NULL; 569 sc->sc_pintarg = NULL; 570 sc->sc_started = false; 571 572 #ifdef VCAUDIO_DEBUG 573 device_printf(sc->sc_dev, "halting output\n"); 574 #endif 575 576 return error; 577 } 578 579 static int 580 vcaudio_halt_input(void *priv) 581 { 582 return EINVAL; 583 } 584 585 static int 586 vcaudio_set_port(void *priv, mixer_ctrl_t *mc) 587 { 588 struct vcaudio_softc *sc = priv; 589 VC_AUDIO_MSG_T msg; 590 int error; 591 592 switch (mc->dev) { 593 case VCAUDIO_OUTPUT_MASTER_VOLUME: 594 case VCAUDIO_INPUT_DAC_VOLUME: 595 sc->sc_volume = mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT]; 596 memset(&msg, 0, sizeof(msg)); 597 msg.type = VC_AUDIO_MSG_TYPE_CONTROL; 598 msg.u.control.volume = vol2pct(sc->sc_volume); 599 msg.u.control.dest = VCAUDIO_DEST_AUTO; 600 vchi_service_use(sc->sc_service); 601 error = vcaudio_msg_sync(sc, &msg, sizeof(msg)); 602 if (error) { 603 device_printf(sc->sc_dev, "couldn't send CONTROL message (%d)\n", error); 604 } 605 vchi_service_release(sc->sc_service); 606 607 return error; 608 } 609 return ENXIO; 610 } 611 612 static int 613 vcaudio_get_port(void *priv, mixer_ctrl_t *mc) 614 { 615 struct vcaudio_softc *sc = priv; 616 617 switch (mc->dev) { 618 case VCAUDIO_OUTPUT_MASTER_VOLUME: 619 case VCAUDIO_INPUT_DAC_VOLUME: 620 mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = 621 mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = 622 sc->sc_volume; 623 return 0; 624 } 625 return ENXIO; 626 } 627 628 static int 629 vcaudio_query_devinfo(void *priv, mixer_devinfo_t *di) 630 { 631 switch (di->index) { 632 case VCAUDIO_OUTPUT_CLASS: 633 di->mixer_class = VCAUDIO_OUTPUT_CLASS; 634 strcpy(di->label.name, AudioCoutputs); 635 di->type = AUDIO_MIXER_CLASS; 636 di->next = di->prev = AUDIO_MIXER_LAST; 637 return 0; 638 case VCAUDIO_INPUT_CLASS: 639 di->mixer_class = VCAUDIO_INPUT_CLASS; 640 strcpy(di->label.name, AudioCinputs); 641 di->type = AUDIO_MIXER_CLASS; 642 di->next = di->prev = AUDIO_MIXER_LAST; 643 return 0; 644 case VCAUDIO_OUTPUT_MASTER_VOLUME: 645 di->mixer_class = VCAUDIO_OUTPUT_CLASS; 646 strcpy(di->label.name, AudioNmaster); 647 di->type = AUDIO_MIXER_VALUE; 648 di->next = di->prev = AUDIO_MIXER_LAST; 649 di->un.v.num_channels = 2; 650 strcpy(di->un.v.units.name, AudioNvolume); 651 return 0; 652 case VCAUDIO_INPUT_DAC_VOLUME: 653 di->mixer_class = VCAUDIO_INPUT_CLASS; 654 strcpy(di->label.name, AudioNdac); 655 di->type = AUDIO_MIXER_VALUE; 656 di->next = di->prev = AUDIO_MIXER_LAST; 657 di->un.v.num_channels = 2; 658 strcpy(di->un.v.units.name, AudioNvolume); 659 return 0; 660 } 661 662 return ENXIO; 663 } 664 665 static int 666 vcaudio_getdev(void *priv, struct audio_device *audiodev) 667 { 668 struct vcaudio_softc *sc = priv; 669 670 snprintf(audiodev->name, sizeof(audiodev->name), "VCHIQ AUDS"); 671 snprintf(audiodev->version, sizeof(audiodev->version), 672 "%d", sc->sc_peer_version); 673 snprintf(audiodev->config, sizeof(audiodev->config), "vcaudio"); 674 675 return 0; 676 } 677 678 static int 679 vcaudio_get_props(void *priv) 680 { 681 return AUDIO_PROP_PLAYBACK|AUDIO_PROP_CAPTURE|AUDIO_PROP_INDEPENDENT; 682 } 683 684 static int 685 vcaudio_round_blocksize(void *priv, int bs, int mode, 686 const audio_params_t *params) 687 { 688 return PAGE_SIZE; 689 } 690 691 static size_t 692 vcaudio_round_buffersize(void *priv, int direction, size_t bufsize) 693 { 694 size_t sz; 695 696 sz = (bufsize + 0x3ff) & ~0x3ff; 697 if (sz > 32768) 698 sz = 32768; 699 700 return sz; 701 } 702 703 static int 704 vcaudio_trigger_output(void *priv, void *start, void *end, int blksize, 705 void (*intr)(void *), void *intrarg, const audio_params_t *params) 706 { 707 struct vcaudio_softc *sc = priv; 708 709 sc->sc_pparam = *params; 710 sc->sc_pint = intr; 711 sc->sc_pintarg = intrarg; 712 sc->sc_ppos = 0; 713 sc->sc_pstart = start; 714 sc->sc_pend = end; 715 sc->sc_pblksize = blksize; 716 workqueue_enqueue(sc->sc_wq, (struct work *)&sc->sc_work, NULL); 717 718 return 0; 719 } 720 721 static int 722 vcaudio_trigger_input(void *priv, void *start, void *end, int blksize, 723 void (*intr)(void *), void *intrarg, const audio_params_t *params) 724 { 725 return EINVAL; 726 } 727 728 static void 729 vcaudio_get_locks(void *priv, kmutex_t **intr, kmutex_t **thread) 730 { 731 struct vcaudio_softc *sc = priv; 732 733 *intr = &sc->sc_intr_lock; 734 *thread = &sc->sc_lock; 735 } 736