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