1 /* $NetBSD: bcm2835_vcaudio.c,v 1.23 2026/08/16 19:23:06 skrll 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.23 2026/08/16 19:23:06 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 44 #include <sys/audioio.h> 45 #include <dev/audio/audio_if.h> 46 47 #include <interface/compat/vchi_bsd.h> 48 #include <interface/vchiq_arm/vchiq_netbsd.h> 49 #include <interface/vchi/vchi.h> 50 51 #include "bcm2835_vcaudioreg.h" 52 53 /* levels with 5% volume step */ 54 static int vcaudio_levels[] = { 55 -10239, -4605, -3794, -3218, -2772, 56 -2407, -2099, -1832, -1597, -1386, 57 -1195, -1021, -861, -713, -575, 58 -446, -325, -210, -102, 0, 59 }; 60 61 #define vol2db(vol) vcaudio_levels[((vol) * 20) >> 8] 62 #define vol2vc(vol) ((uint32_t)(-(vol2db((vol)) << 8) / 100)) 63 64 enum { 65 VCAUDIO_OUTPUT_CLASS, 66 VCAUDIO_INPUT_CLASS, 67 VCAUDIO_OUTPUT_MASTER_VOLUME, 68 VCAUDIO_INPUT_DAC_VOLUME, 69 VCAUDIO_OUTPUT_AUTO_VOLUME, 70 VCAUDIO_OUTPUT_HEADPHONE_VOLUME, 71 VCAUDIO_OUTPUT_HDMI_VOLUME, 72 VCAUDIO_OUTPUT_SELECT, 73 VCAUDIO_ENUM_LAST, 74 }; 75 76 enum vcaudio_dest { 77 VCAUDIO_DEST_AUTO = 0, 78 VCAUDIO_DEST_HP = 1, 79 VCAUDIO_DEST_HDMI = 2, 80 }; 81 82 /* 83 * Maximum message size is 4000 bytes and VCHIQ can accept 16 messages. 84 * 85 * 4000 bytes of 16bit 48kHz stereo is approximately 21ms. 86 * 87 * We get complete messages at ~10ms intervals. 88 * 89 * Setting blocksize to 4 x 1600 means that we send approx 33ms of audio. We 90 * prefill by two blocks before starting audio meaning we have 50ms of latency. 91 * 92 * Six messages of 1600 bytes was chosen working back from a desired latency of 93 * 50ms. 94 */ 95 96 /* 40ms block of 16bit 48kHz stereo is 7680 bytes. */ 97 #define VCAUDIO_MSGSIZE 1920 98 #define VCAUDIO_NUMMSGS 4 99 #define VCAUDIO_BLOCKSIZE (VCAUDIO_MSGSIZE * VCAUDIO_NUMMSGS) 100 /* The driver seems to have no buffer size restrictions. */ 101 #define VCAUDIO_PREFILLCOUNT 2 102 103 struct vcaudio_softc { 104 device_t sc_dev; 105 device_t sc_audiodev; 106 107 lwp_t *sc_lwp; 108 109 kmutex_t sc_lock; 110 kmutex_t sc_intr_lock; 111 kcondvar_t sc_datacv; 112 113 kmutex_t sc_msglock; 114 kcondvar_t sc_msgcv; 115 116 struct audio_format sc_format; 117 118 void (*sc_pint)(void *); 119 void *sc_pintarg; 120 audio_params_t sc_pparam; 121 bool sc_started; 122 int sc_pblkcnt; // prefill block count 123 int sc_abytes; // available bytes 124 int sc_pbytes; // played bytes 125 off_t sc_ppos; 126 void *sc_pstart; 127 void *sc_pend; 128 int sc_pblksize; 129 130 bool sc_msgdone; 131 int sc_success; 132 133 VCHI_INSTANCE_T sc_instance; 134 VCHI_CONNECTION_T sc_connection; 135 VCHI_SERVICE_HANDLE_T sc_service; 136 137 short sc_peer_version; 138 139 int sc_hwvol[3]; 140 enum vcaudio_dest sc_dest; 141 142 uint8_t sc_swvol; 143 }; 144 145 static int vcaudio_match(device_t, cfdata_t, void *); 146 static void vcaudio_attach(device_t, device_t, void *); 147 static int vcaudio_rescan(device_t, const char *, const int *); 148 static void vcaudio_childdet(device_t, device_t); 149 150 static int vcaudio_init(struct vcaudio_softc *); 151 static void vcaudio_service_callback(void *, 152 const VCHI_CALLBACK_REASON_T, void *); 153 static int vcaudio_msg_sync(struct vcaudio_softc *, VC_AUDIO_MSG_T *, 154 size_t); 155 static void vcaudio_worker(void *); 156 157 static int vcaudio_query_format(void *, audio_format_query_t *); 158 static int vcaudio_set_format(void *, int, 159 const audio_params_t *, const audio_params_t *, 160 audio_filter_reg_t *, audio_filter_reg_t *); 161 static int vcaudio_halt_output(void *); 162 static int vcaudio_set_port(void *, mixer_ctrl_t *); 163 static int vcaudio_get_port(void *, mixer_ctrl_t *); 164 static int vcaudio_query_devinfo(void *, mixer_devinfo_t *); 165 static int vcaudio_getdev(void *, struct audio_device *); 166 static int vcaudio_get_props(void *); 167 168 static int vcaudio_round_blocksize(void *, int, int, 169 const audio_params_t *); 170 171 static int vcaudio_trigger_output(void *, void *, void *, int, 172 void (*)(void *), void *, const audio_params_t *); 173 174 static void vcaudio_get_locks(void *, kmutex_t **, kmutex_t **); 175 176 static void vcaudio_swvol_codec(audio_filter_arg_t *); 177 178 static const struct audio_hw_if vcaudio_hw_if = { 179 .query_format = vcaudio_query_format, 180 .set_format = vcaudio_set_format, 181 .halt_output = vcaudio_halt_output, 182 .getdev = vcaudio_getdev, 183 .set_port = vcaudio_set_port, 184 .get_port = vcaudio_get_port, 185 .query_devinfo = vcaudio_query_devinfo, 186 .get_props = vcaudio_get_props, 187 .round_blocksize = vcaudio_round_blocksize, 188 .trigger_output = vcaudio_trigger_output, 189 .get_locks = vcaudio_get_locks, 190 }; 191 192 CFATTACH_DECL2_NEW(vcaudio, sizeof(struct vcaudio_softc), 193 vcaudio_match, vcaudio_attach, NULL, NULL, vcaudio_rescan, 194 vcaudio_childdet); 195 196 static int 197 vcaudio_match(device_t parent, cfdata_t match, void *aux) 198 { 199 struct vchiq_attach_args *vaa = aux; 200 201 return !strcmp(vaa->vaa_name, "AUDS"); 202 } 203 204 static void 205 vcaudio_attach(device_t parent, device_t self, void *aux) 206 { 207 struct vcaudio_softc *sc = device_private(self); 208 int error; 209 210 sc->sc_dev = self; 211 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE); 212 mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_NONE); 213 mutex_init(&sc->sc_msglock, MUTEX_DEFAULT, IPL_NONE); 214 cv_init(&sc->sc_msgcv, "msg"); 215 cv_init(&sc->sc_datacv, "data"); 216 sc->sc_success = -1; 217 218 error = kthread_create(PRI_BIO, KTHREAD_MPSAFE, NULL, vcaudio_worker, 219 sc, &sc->sc_lwp, "vcaudio"); 220 if (error) { 221 aprint_error(": couldn't create thread (%d)\n", error); 222 return; 223 } 224 225 aprint_naive("\n"); 226 aprint_normal(": auds\n"); 227 228 error = vcaudio_rescan(self, NULL, NULL); 229 if (error) 230 aprint_error_dev(self, "not configured\n"); 231 232 } 233 234 static int 235 vcaudio_rescan(device_t self, const char *ifattr, const int *locs) 236 { 237 struct vcaudio_softc *sc = device_private(self); 238 int error; 239 240 if (sc->sc_audiodev == NULL) { 241 error = vcaudio_init(sc); 242 if (error) { 243 return error; 244 } 245 246 sc->sc_audiodev = audio_attach_mi(&vcaudio_hw_if, 247 sc, sc->sc_dev); 248 } 249 return 0; 250 } 251 252 static void 253 vcaudio_childdet(device_t self, device_t child) 254 { 255 struct vcaudio_softc *sc = device_private(self); 256 257 if (sc->sc_audiodev == child) 258 sc->sc_audiodev = NULL; 259 } 260 261 static ssize_t 262 vcaudio_memcpy_wrapper(void *_src, void *_dst, size_t offset, size_t size) 263 { 264 uint8_t *src = _src; 265 uint8_t *dst = _dst; 266 267 memcpy(dst + offset, src + offset, size); 268 269 return size; 270 } 271 272 static int 273 vcaudio_init(struct vcaudio_softc *sc) 274 { 275 VC_AUDIO_MSG_T msg; 276 int error; 277 278 sc->sc_swvol = 255; 279 sc->sc_hwvol[VCAUDIO_DEST_AUTO] = 255; 280 sc->sc_hwvol[VCAUDIO_DEST_HP] = 255; 281 sc->sc_hwvol[VCAUDIO_DEST_HDMI] = 255; 282 sc->sc_dest = VCAUDIO_DEST_AUTO; 283 284 sc->sc_format.mode = AUMODE_PLAY; 285 sc->sc_format.encoding = AUDIO_ENCODING_SLINEAR_LE; 286 sc->sc_format.validbits = 16; 287 sc->sc_format.precision = 16; 288 sc->sc_format.channels = 2; 289 sc->sc_format.channel_mask = AUFMT_STEREO; 290 sc->sc_format.frequency_type = 0; 291 sc->sc_format.frequency[0] = 48000; 292 sc->sc_format.frequency[1] = 48000; 293 294 error = vchi_initialise(&sc->sc_instance); 295 if (error) { 296 aprint_error_dev(sc->sc_dev, 297 "couldn't init vchi instance (%d)\n", error); 298 return EIO; 299 } 300 301 error = vchi_connect(NULL, 0, sc->sc_instance); 302 if (error) { 303 aprint_error_dev(sc->sc_dev, 304 "couldn't connect vchi (%d)\n", error); 305 return EIO; 306 } 307 308 SERVICE_CREATION_T setup = { 309 .version = VCHI_VERSION(VC_AUDIOSERV_VER), 310 .service_id = VC_AUDIO_SERVER_NAME, 311 .connection = &sc->sc_connection, 312 .rx_fifo_size = 0, 313 .tx_fifo_size = 0, 314 .callback = vcaudio_service_callback, 315 .callback_param = sc, 316 .want_unaligned_bulk_rx = 1, 317 .want_unaligned_bulk_tx = 1, 318 .want_crc = 0, 319 }; 320 321 error = vchi_service_open(sc->sc_instance, &setup, &sc->sc_service); 322 if (error) { 323 aprint_error_dev(sc->sc_dev, "couldn't open service (%d)\n", 324 error); 325 return EIO; 326 } 327 328 vchi_get_peer_version(sc->sc_service, &sc->sc_peer_version); 329 330 if (sc->sc_peer_version < 2) { 331 aprint_error_dev(sc->sc_dev, 332 "peer version (%d) is less than the required version (2)\n", 333 sc->sc_peer_version); 334 return EINVAL; 335 } 336 337 memset(&msg, 0, sizeof(msg)); 338 msg.type = htole32(VC_AUDIO_MSG_TYPE_OPEN); 339 error = vchi_msg_queue(sc->sc_service, vcaudio_memcpy_wrapper, 340 &msg, sizeof(msg)); 341 if (error) { 342 aprint_error_dev(sc->sc_dev, 343 "couldn't send OPEN message (%d)\n", error); 344 } 345 346 memset(&msg, 0, sizeof(msg)); 347 msg.type = htole32(VC_AUDIO_MSG_TYPE_CONFIG); 348 msg.u.config.channels = htole32(2); 349 msg.u.config.samplerate = htole32(48000); 350 msg.u.config.bps = htole32(16); 351 error = vcaudio_msg_sync(sc, &msg, sizeof(msg)); 352 if (error) { 353 aprint_error_dev(sc->sc_dev, 354 "couldn't send CONFIG message (%d)\n", error); 355 } 356 357 memset(&msg, 0, sizeof(msg)); 358 msg.type = htole32(VC_AUDIO_MSG_TYPE_CONTROL); 359 msg.u.control.volume = htole32(vol2vc(sc->sc_hwvol[sc->sc_dest])); 360 msg.u.control.dest = htole32(sc->sc_dest); 361 error = vcaudio_msg_sync(sc, &msg, sizeof(msg)); 362 if (error) { 363 aprint_error_dev(sc->sc_dev, 364 "couldn't send CONTROL message (%d)\n", error); 365 } 366 367 vchi_service_release(sc->sc_service); 368 369 return 0; 370 } 371 372 static void 373 vcaudio_service_callback(void *priv, const VCHI_CALLBACK_REASON_T reason, 374 void *msg_handle) 375 { 376 struct vcaudio_softc *sc = priv; 377 VC_AUDIO_MSG_T msg; 378 int32_t msglen = 0; 379 int error; 380 381 if (sc == NULL || reason != VCHI_CALLBACK_MSG_AVAILABLE) 382 return; 383 384 memset(&msg, 0, sizeof(msg)); 385 error = vchi_msg_dequeue(sc->sc_service, &msg, sizeof(msg), &msglen, 386 VCHI_FLAGS_NONE); 387 if (error) { 388 device_printf(sc->sc_dev, "couldn't dequeue msg (%d)\n", 389 error); 390 return; 391 } 392 393 switch (le32toh(msg.type)) { 394 case VC_AUDIO_MSG_TYPE_RESULT: 395 mutex_enter(&sc->sc_msglock); 396 sc->sc_success = le32toh(msg.u.result.success); 397 sc->sc_msgdone = true; 398 cv_broadcast(&sc->sc_msgcv); 399 mutex_exit(&sc->sc_msglock); 400 break; 401 402 case VC_AUDIO_MSG_TYPE_COMPLETE: 403 if (le32toh(msg.u.complete.cookie1) == VC_AUDIO_WRITE_COOKIE1 && 404 le32toh(msg.u.complete.cookie2) == VC_AUDIO_WRITE_COOKIE2) { 405 int count = le32toh(msg.u.complete.count) & 0xffff; 406 int perr = (le32toh(msg.u.complete.count) & __BIT(30)) != 0; 407 bool sched = false; 408 409 mutex_enter(&sc->sc_intr_lock); 410 411 if (count > 0) { 412 sc->sc_pbytes += count; 413 } 414 if (perr && sc->sc_started) { 415 #ifdef VCAUDIO_DEBUG 416 device_printf(sc->sc_dev, "underrun\n"); 417 #endif 418 sched = true; 419 } 420 if (sc->sc_pbytes >= sc->sc_pblksize) { 421 sc->sc_pbytes -= sc->sc_pblksize; 422 sched = true; 423 } 424 425 if (sched && sc->sc_pint) { 426 sc->sc_pint(sc->sc_pintarg); 427 sc->sc_abytes += sc->sc_pblksize; 428 cv_signal(&sc->sc_datacv); 429 } 430 mutex_exit(&sc->sc_intr_lock); 431 } 432 break; 433 default: 434 break; 435 } 436 } 437 438 static void 439 vcaudio_worker(void *priv) 440 { 441 struct vcaudio_softc *sc = priv; 442 VC_AUDIO_MSG_T msg; 443 void (*intr)(void *); 444 void *intrarg; 445 void *block; 446 int error, resid, off, nb, count; 447 448 mutex_enter(&sc->sc_intr_lock); 449 450 while (true) { 451 intr = sc->sc_pint; 452 intrarg = sc->sc_pintarg; 453 454 if (intr == NULL || intrarg == NULL) { 455 cv_wait_sig(&sc->sc_datacv, &sc->sc_intr_lock); 456 continue; 457 } 458 459 KASSERT(sc->sc_pblksize != 0); 460 461 if (sc->sc_abytes < sc->sc_pblksize) { 462 cv_wait_sig(&sc->sc_datacv, &sc->sc_intr_lock); 463 continue; 464 } 465 count = sc->sc_pblksize; 466 467 memset(&msg, 0, sizeof(msg)); 468 msg.type = htole32(VC_AUDIO_MSG_TYPE_WRITE); 469 msg.u.write.max_packet = htole16(VCAUDIO_MSGSIZE); 470 msg.u.write.count = htole32(count); 471 msg.u.write.cookie1 = htole32(VC_AUDIO_WRITE_COOKIE1); 472 msg.u.write.cookie2 = htole32(VC_AUDIO_WRITE_COOKIE2); 473 msg.u.write.silence = htole16(0); 474 475 block = (uint8_t *)sc->sc_pstart + sc->sc_ppos; 476 resid = count; 477 off = 0; 478 479 vchi_service_use(sc->sc_service); 480 481 error = vchi_msg_queue(sc->sc_service, vcaudio_memcpy_wrapper, &msg, sizeof(msg)); 482 if (error) { 483 printf("%s: failed to write (%d)\n", __func__, error); 484 goto done; 485 } 486 487 while (resid > 0) { 488 nb = uimin(resid, le16toh(msg.u.write.max_packet)); 489 error = vchi_msg_queue(sc->sc_service, 490 vcaudio_memcpy_wrapper, (char *)block + off, nb); 491 if (error) { 492 /* XXX What to do here? */ 493 device_printf(sc->sc_dev, 494 "failed to queue data (%d)\n", error); 495 goto done; 496 } 497 off += nb; 498 resid -= nb; 499 } 500 501 sc->sc_abytes -= count; 502 sc->sc_ppos += count; 503 if ((uint8_t *)sc->sc_pstart + sc->sc_ppos >= 504 (uint8_t *)sc->sc_pend) 505 sc->sc_ppos = 0; 506 507 if (!sc->sc_started) { 508 ++sc->sc_pblkcnt; 509 510 if (sc->sc_pblkcnt == VCAUDIO_PREFILLCOUNT) { 511 512 memset(&msg, 0, sizeof(msg)); 513 msg.type = htole32(VC_AUDIO_MSG_TYPE_START); 514 error = vchi_msg_queue(sc->sc_service, 515 vcaudio_memcpy_wrapper, &msg, sizeof(msg)); 516 if (error) { 517 device_printf(sc->sc_dev, 518 "failed to start (%d)\n", error); 519 goto done; 520 } 521 sc->sc_started = true; 522 sc->sc_pbytes = 0; 523 } else { 524 intr(intrarg); 525 sc->sc_abytes += sc->sc_pblksize; 526 } 527 } 528 529 done: 530 vchi_service_release(sc->sc_service); 531 } 532 } 533 534 static int 535 vcaudio_msg_sync(struct vcaudio_softc *sc, VC_AUDIO_MSG_T *msg, size_t msglen) 536 { 537 int error = 0; 538 539 mutex_enter(&sc->sc_msglock); 540 541 sc->sc_success = -1; 542 sc->sc_msgdone = false; 543 544 error = vchi_msg_queue(sc->sc_service, vcaudio_memcpy_wrapper, 545 msg, msglen); 546 if (error) { 547 printf("%s: failed to queue message (%d)\n", __func__, error); 548 goto done; 549 } 550 551 while (!sc->sc_msgdone) { 552 error = cv_wait_sig(&sc->sc_msgcv, &sc->sc_msglock); 553 if (error) 554 break; 555 } 556 if (sc->sc_success != 0) 557 error = EIO; 558 done: 559 mutex_exit(&sc->sc_msglock); 560 561 return error; 562 } 563 564 static int 565 vcaudio_query_format(void *priv, audio_format_query_t *afp) 566 { 567 struct vcaudio_softc *sc = priv; 568 569 return audio_query_format(&sc->sc_format, 1, afp); 570 } 571 572 static int 573 vcaudio_set_format(void *priv, int setmode, 574 const audio_params_t *play, const audio_params_t *rec, 575 audio_filter_reg_t *pfil, audio_filter_reg_t *rfil) 576 { 577 struct vcaudio_softc *sc = priv; 578 579 pfil->codec = vcaudio_swvol_codec; 580 pfil->context = sc; 581 582 return 0; 583 } 584 585 static int 586 vcaudio_halt_output(void *priv) 587 { 588 struct vcaudio_softc *sc = priv; 589 VC_AUDIO_MSG_T msg; 590 int error = 0; 591 592 KASSERT(mutex_owned(&sc->sc_intr_lock)); 593 594 vchi_service_use(sc->sc_service); 595 memset(&msg, 0, sizeof(msg)); 596 msg.type = htole32(VC_AUDIO_MSG_TYPE_STOP); 597 msg.u.stop.draining = htole32(0); 598 error = vchi_msg_queue(sc->sc_service, vcaudio_memcpy_wrapper, 599 &msg, sizeof(msg)); 600 if (error) { 601 device_printf(sc->sc_dev, "couldn't send STOP message (%d)\n", 602 error); 603 } 604 vchi_service_release(sc->sc_service); 605 606 sc->sc_pint = NULL; 607 sc->sc_pintarg = NULL; 608 sc->sc_started = false; 609 610 #ifdef VCAUDIO_DEBUG 611 device_printf(sc->sc_dev, "halting output\n"); 612 #endif 613 614 return error; 615 } 616 617 static int 618 vcaudio_set_volume(struct vcaudio_softc *sc, enum vcaudio_dest dest, 619 int hwvol) 620 { 621 VC_AUDIO_MSG_T msg; 622 int error; 623 624 sc->sc_hwvol[dest] = hwvol; 625 if (dest != sc->sc_dest) 626 return 0; 627 628 vchi_service_use(sc->sc_service); 629 630 memset(&msg, 0, sizeof(msg)); 631 msg.type = htole32(VC_AUDIO_MSG_TYPE_CONTROL); 632 msg.u.control.volume = htole32(vol2vc(hwvol)); 633 msg.u.control.dest = htole32(dest); 634 635 error = vcaudio_msg_sync(sc, &msg, sizeof(msg)); 636 if (error) { 637 device_printf(sc->sc_dev, 638 "couldn't send CONTROL message (%d)\n", error); 639 } 640 641 vchi_service_release(sc->sc_service); 642 643 return error; 644 } 645 646 static int 647 vcaudio_set_port(void *priv, mixer_ctrl_t *mc) 648 { 649 struct vcaudio_softc *sc = priv; 650 651 switch (mc->dev) { 652 case VCAUDIO_OUTPUT_MASTER_VOLUME: 653 case VCAUDIO_INPUT_DAC_VOLUME: 654 sc->sc_swvol = mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT]; 655 return 0; 656 case VCAUDIO_OUTPUT_AUTO_VOLUME: 657 return vcaudio_set_volume(sc, VCAUDIO_DEST_AUTO, 658 mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT]); 659 case VCAUDIO_OUTPUT_HEADPHONE_VOLUME: 660 return vcaudio_set_volume(sc, VCAUDIO_DEST_HP, 661 mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT]); 662 case VCAUDIO_OUTPUT_HDMI_VOLUME: 663 return vcaudio_set_volume(sc, VCAUDIO_DEST_HDMI, 664 mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT]); 665 case VCAUDIO_OUTPUT_SELECT: 666 if (mc->un.ord < 0 || mc->un.ord > 2) 667 return EINVAL; 668 sc->sc_dest = mc->un.ord; 669 return vcaudio_set_volume(sc, mc->un.ord, 670 sc->sc_hwvol[mc->un.ord]); 671 } 672 return ENXIO; 673 } 674 675 static int 676 vcaudio_get_port(void *priv, mixer_ctrl_t *mc) 677 { 678 struct vcaudio_softc *sc = priv; 679 680 switch (mc->dev) { 681 case VCAUDIO_OUTPUT_MASTER_VOLUME: 682 case VCAUDIO_INPUT_DAC_VOLUME: 683 mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = 684 mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = 685 sc->sc_swvol; 686 return 0; 687 case VCAUDIO_OUTPUT_AUTO_VOLUME: 688 mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = 689 mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = 690 sc->sc_hwvol[VCAUDIO_DEST_AUTO]; 691 return 0; 692 case VCAUDIO_OUTPUT_HEADPHONE_VOLUME: 693 mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = 694 mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = 695 sc->sc_hwvol[VCAUDIO_DEST_HP]; 696 return 0; 697 case VCAUDIO_OUTPUT_HDMI_VOLUME: 698 mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = 699 mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = 700 sc->sc_hwvol[VCAUDIO_DEST_HDMI]; 701 return 0; 702 case VCAUDIO_OUTPUT_SELECT: 703 mc->un.ord = sc->sc_dest; 704 return 0; 705 } 706 return ENXIO; 707 } 708 709 static int 710 vcaudio_query_devinfo(void *priv, mixer_devinfo_t *di) 711 { 712 switch (di->index) { 713 case VCAUDIO_OUTPUT_CLASS: 714 di->mixer_class = VCAUDIO_OUTPUT_CLASS; 715 strcpy(di->label.name, AudioCoutputs); 716 di->type = AUDIO_MIXER_CLASS; 717 di->next = di->prev = AUDIO_MIXER_LAST; 718 return 0; 719 case VCAUDIO_INPUT_CLASS: 720 di->mixer_class = VCAUDIO_INPUT_CLASS; 721 strcpy(di->label.name, AudioCinputs); 722 di->type = AUDIO_MIXER_CLASS; 723 di->next = di->prev = AUDIO_MIXER_LAST; 724 return 0; 725 case VCAUDIO_OUTPUT_MASTER_VOLUME: 726 di->mixer_class = VCAUDIO_OUTPUT_CLASS; 727 strcpy(di->label.name, AudioNmaster); 728 di->type = AUDIO_MIXER_VALUE; 729 di->next = di->prev = AUDIO_MIXER_LAST; 730 di->un.v.num_channels = 2; 731 strcpy(di->un.v.units.name, AudioNvolume); 732 return 0; 733 case VCAUDIO_OUTPUT_AUTO_VOLUME: 734 di->mixer_class = VCAUDIO_OUTPUT_CLASS; 735 strcpy(di->label.name, "auto"); 736 di->type = AUDIO_MIXER_VALUE; 737 di->next = di->prev = AUDIO_MIXER_LAST; 738 di->un.v.num_channels = 2; 739 di->un.v.delta = 13; 740 strcpy(di->un.v.units.name, AudioNvolume); 741 return 0; 742 case VCAUDIO_OUTPUT_HEADPHONE_VOLUME: 743 di->mixer_class = VCAUDIO_OUTPUT_CLASS; 744 strcpy(di->label.name, AudioNheadphone); 745 di->type = AUDIO_MIXER_VALUE; 746 di->next = di->prev = AUDIO_MIXER_LAST; 747 di->un.v.num_channels = 2; 748 di->un.v.delta = 13; 749 strcpy(di->un.v.units.name, AudioNvolume); 750 return 0; 751 case VCAUDIO_OUTPUT_HDMI_VOLUME: 752 di->mixer_class = VCAUDIO_OUTPUT_CLASS; 753 strcpy(di->label.name, "hdmi"); 754 di->type = AUDIO_MIXER_VALUE; 755 di->next = di->prev = AUDIO_MIXER_LAST; 756 di->un.v.num_channels = 2; 757 di->un.v.delta = 13; 758 strcpy(di->un.v.units.name, AudioNvolume); 759 return 0; 760 case VCAUDIO_INPUT_DAC_VOLUME: 761 di->mixer_class = VCAUDIO_INPUT_CLASS; 762 strcpy(di->label.name, AudioNdac); 763 di->type = AUDIO_MIXER_VALUE; 764 di->next = di->prev = AUDIO_MIXER_LAST; 765 di->un.v.num_channels = 2; 766 strcpy(di->un.v.units.name, AudioNvolume); 767 return 0; 768 case VCAUDIO_OUTPUT_SELECT: 769 di->mixer_class = VCAUDIO_OUTPUT_CLASS; 770 strcpy(di->label.name, AudioNselect); 771 di->type = AUDIO_MIXER_ENUM; 772 di->next = di->prev = AUDIO_MIXER_LAST; 773 di->un.e.num_mem = 3; 774 di->un.e.member[0].ord = 0; 775 strcpy(di->un.e.member[0].label.name, "auto"); 776 di->un.e.member[1].ord = 1; 777 strcpy(di->un.e.member[1].label.name, AudioNheadphone); 778 di->un.e.member[2].ord = 2; 779 strcpy(di->un.e.member[2].label.name, "hdmi"); 780 return 0; 781 } 782 783 return ENXIO; 784 } 785 786 static int 787 vcaudio_getdev(void *priv, struct audio_device *audiodev) 788 { 789 struct vcaudio_softc *sc = priv; 790 791 snprintf(audiodev->name, sizeof(audiodev->name), "vchiq auds"); 792 snprintf(audiodev->version, sizeof(audiodev->version), 793 "%d", sc->sc_peer_version); 794 snprintf(audiodev->config, sizeof(audiodev->config), "vcaudio"); 795 796 return 0; 797 } 798 799 static int 800 vcaudio_get_props(void *priv) 801 { 802 return AUDIO_PROP_PLAYBACK; 803 } 804 805 static int 806 vcaudio_round_blocksize(void *priv, int bs, int mode, 807 const audio_params_t *params) 808 { 809 return VCAUDIO_BLOCKSIZE; 810 } 811 812 static int 813 vcaudio_trigger_output(void *priv, void *start, void *end, int blksize, 814 void (*intr)(void *), void *intrarg, const audio_params_t *params) 815 { 816 struct vcaudio_softc *sc = priv; 817 818 ASSERT_SLEEPABLE(); 819 KASSERT(mutex_owned(&sc->sc_intr_lock)); 820 821 sc->sc_pparam = *params; 822 sc->sc_pint = intr; 823 sc->sc_pintarg = intrarg; 824 sc->sc_ppos = 0; 825 sc->sc_pstart = start; 826 sc->sc_pend = end; 827 sc->sc_pblksize = blksize; 828 sc->sc_pblkcnt = 0; 829 sc->sc_pbytes = 0; 830 sc->sc_abytes = blksize; 831 832 cv_signal(&sc->sc_datacv); 833 834 return 0; 835 } 836 837 static void 838 vcaudio_get_locks(void *priv, kmutex_t **intr, kmutex_t **thread) 839 { 840 struct vcaudio_softc *sc = priv; 841 842 *intr = &sc->sc_intr_lock; 843 *thread = &sc->sc_lock; 844 } 845 846 static void 847 vcaudio_swvol_codec(audio_filter_arg_t *arg) 848 { 849 struct vcaudio_softc *sc = arg->context; 850 const aint_t *src; 851 int16_t *dst; 852 u_int sample_count; 853 u_int i; 854 855 src = arg->src; 856 dst = arg->dst; 857 sample_count = arg->count * arg->srcfmt->channels; 858 for (i = 0; i < sample_count; i++) { 859 /* assume SLINEAR_NE:16 is preferred on playback */ 860 aint2_t v = (aint2_t)(*src++); 861 v = v * sc->sc_swvol / 255; 862 /* output vcaudio requires SLINEAR_LE:16 */ 863 *dst++ = (int16_t)htole16((uint16_t)(aint_t)v); 864 } 865 } 866