1139749Simp/*- 2193640Sariff * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org> 3193640Sariff * Portions Copyright (c) Ryan Beasley <ryan.beasley@gmail.com> - GSoC 2006 4193640Sariff * Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org> 5193640Sariff * Portions Copyright (c) Luigi Rizzo <luigi@FreeBSD.org> - 1997-99 650724Scg * All rights reserved. 750724Scg * 850724Scg * Redistribution and use in source and binary forms, with or without 950724Scg * modification, are permitted provided that the following conditions 1050724Scg * are met: 1150724Scg * 1. Redistributions of source code must retain the above copyright 1250724Scg * notice, this list of conditions and the following disclaimer. 1350724Scg * 2. Redistributions in binary form must reproduce the above copyright 1450724Scg * notice, this list of conditions and the following disclaimer in the 1550724Scg * documentation and/or other materials provided with the distribution. 1650724Scg * 1750724Scg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1850724Scg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1950724Scg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2050724Scg * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2150724Scg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2250724Scg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2350724Scg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2450724Scg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2550724Scg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2650724Scg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2750724Scg * SUCH DAMAGE. 2850724Scg */ 2950724Scg 30147274Smarius#include "opt_isa.h" 31147274Smarius 32193640Sariff#ifdef HAVE_KERNEL_OPTION_HEADERS 33193640Sariff#include "opt_snd.h" 34193640Sariff#endif 35193640Sariff 3653465Scg#include <dev/sound/pcm/sound.h> 37193640Sariff#include <dev/sound/pcm/vchan.h> 3850724Scg 3970134Scg#include "feeder_if.h" 4070134Scg 4182180ScgSND_DECLARE_FILE("$FreeBSD$"); 4282180Scg 43164614Sariffint report_soft_formats = 1; 44164614SariffSYSCTL_INT(_hw_snd, OID_AUTO, report_soft_formats, CTLFLAG_RW, 45164614Sariff &report_soft_formats, 1, "report software-emulated formats"); 4683613Scg 47193640Sariffint report_soft_matrix = 1; 48193640SariffSYSCTL_INT(_hw_snd, OID_AUTO, report_soft_matrix, CTLFLAG_RW, 49193640Sariff &report_soft_matrix, 1, "report software-emulated channel matrixing"); 50193640Sariff 51164614Sariffint chn_latency = CHN_LATENCY_DEFAULT; 52164614SariffTUNABLE_INT("hw.snd.latency", &chn_latency); 53164614Sariff 5483613Scgstatic int 55164614Sariffsysctl_hw_snd_latency(SYSCTL_HANDLER_ARGS) 5683613Scg{ 5783613Scg int err, val; 5883613Scg 59164614Sariff val = chn_latency; 60170289Sdwmalone err = sysctl_handle_int(oidp, &val, 0, req); 61168243Sariff if (err != 0 || req->newptr == NULL) 62168243Sariff return err; 63164614Sariff if (val < CHN_LATENCY_MIN || val > CHN_LATENCY_MAX) 6483613Scg err = EINVAL; 6583613Scg else 66164614Sariff chn_latency = val; 6783613Scg 6883613Scg return err; 6983613Scg} 70164614SariffSYSCTL_PROC(_hw_snd, OID_AUTO, latency, CTLTYPE_INT | CTLFLAG_RW, 71164614Sariff 0, sizeof(int), sysctl_hw_snd_latency, "I", 72164614Sariff "buffering latency (0=low ... 10=high)"); 7383613Scg 74164614Sariffint chn_latency_profile = CHN_LATENCY_PROFILE_DEFAULT; 75164614SariffTUNABLE_INT("hw.snd.latency_profile", &chn_latency_profile); 76164614Sariff 77164614Sariffstatic int 78164614Sariffsysctl_hw_snd_latency_profile(SYSCTL_HANDLER_ARGS) 79164614Sariff{ 80164614Sariff int err, val; 81164614Sariff 82164614Sariff val = chn_latency_profile; 83170289Sdwmalone err = sysctl_handle_int(oidp, &val, 0, req); 84168243Sariff if (err != 0 || req->newptr == NULL) 85168243Sariff return err; 86164614Sariff if (val < CHN_LATENCY_PROFILE_MIN || val > CHN_LATENCY_PROFILE_MAX) 87164614Sariff err = EINVAL; 88164614Sariff else 89164614Sariff chn_latency_profile = val; 90164614Sariff 91164614Sariff return err; 92164614Sariff} 93164614SariffSYSCTL_PROC(_hw_snd, OID_AUTO, latency_profile, CTLTYPE_INT | CTLFLAG_RW, 94164614Sariff 0, sizeof(int), sysctl_hw_snd_latency_profile, "I", 95164614Sariff "buffering latency profile (0=aggresive 1=safe)"); 96164614Sariff 97167645Sariffstatic int chn_timeout = CHN_TIMEOUT; 98168243SariffTUNABLE_INT("hw.snd.timeout", &chn_timeout); 99167645Sariff#ifdef SND_DEBUG 100167645Sariffstatic int 101167645Sariffsysctl_hw_snd_timeout(SYSCTL_HANDLER_ARGS) 102167645Sariff{ 103167645Sariff int err, val; 104167645Sariff 105167645Sariff val = chn_timeout; 106170289Sdwmalone err = sysctl_handle_int(oidp, &val, 0, req); 107168243Sariff if (err != 0 || req->newptr == NULL) 108168243Sariff return err; 109167645Sariff if (val < CHN_TIMEOUT_MIN || val > CHN_TIMEOUT_MAX) 110167645Sariff err = EINVAL; 111167645Sariff else 112167645Sariff chn_timeout = val; 113167645Sariff 114167645Sariff return err; 115167645Sariff} 116167645SariffSYSCTL_PROC(_hw_snd, OID_AUTO, timeout, CTLTYPE_INT | CTLFLAG_RW, 117167645Sariff 0, sizeof(int), sysctl_hw_snd_timeout, "I", 118168243Sariff "interrupt timeout (1 - 10) seconds"); 119167645Sariff#endif 120167645Sariff 121193640Sariffstatic int chn_vpc_autoreset = 1; 122193640SariffTUNABLE_INT("hw.snd.vpc_autoreset", &chn_vpc_autoreset); 123193640SariffSYSCTL_INT(_hw_snd, OID_AUTO, vpc_autoreset, CTLFLAG_RW, 124193640Sariff &chn_vpc_autoreset, 0, "automatically reset channels volume to 0db"); 125193640Sariff 126193640Sariffstatic int chn_vol_0db_pcm = SND_VOL_0DB_PCM; 127214332SmavTUNABLE_INT("hw.snd.vpc_0db", &chn_vol_0db_pcm); 128193640Sariff 129193640Sariffstatic void 130193640Sariffchn_vpc_proc(int reset, int db) 131193640Sariff{ 132193640Sariff struct snddev_info *d; 133193640Sariff struct pcm_channel *c; 134193640Sariff int i; 135193640Sariff 136193640Sariff for (i = 0; pcm_devclass != NULL && 137193640Sariff i < devclass_get_maxunit(pcm_devclass); i++) { 138193640Sariff d = devclass_get_softc(pcm_devclass, i); 139193640Sariff if (!PCM_REGISTERED(d)) 140193640Sariff continue; 141193640Sariff PCM_LOCK(d); 142193640Sariff PCM_WAIT(d); 143193640Sariff PCM_ACQUIRE(d); 144193640Sariff CHN_FOREACH(c, d, channels.pcm) { 145193640Sariff CHN_LOCK(c); 146193640Sariff CHN_SETVOLUME(c, SND_VOL_C_PCM, SND_CHN_T_VOL_0DB, db); 147193640Sariff if (reset != 0) 148193640Sariff chn_vpc_reset(c, SND_VOL_C_PCM, 1); 149193640Sariff CHN_UNLOCK(c); 150193640Sariff } 151193640Sariff PCM_RELEASE(d); 152193640Sariff PCM_UNLOCK(d); 153193640Sariff } 154193640Sariff} 155193640Sariff 156193640Sariffstatic int 157193640Sariffsysctl_hw_snd_vpc_0db(SYSCTL_HANDLER_ARGS) 158193640Sariff{ 159193640Sariff int err, val; 160193640Sariff 161193640Sariff val = chn_vol_0db_pcm; 162193640Sariff err = sysctl_handle_int(oidp, &val, 0, req); 163193640Sariff if (err != 0 || req->newptr == NULL) 164193640Sariff return (err); 165193640Sariff if (val < SND_VOL_0DB_MIN || val > SND_VOL_0DB_MAX) 166193640Sariff return (EINVAL); 167193640Sariff 168193640Sariff chn_vol_0db_pcm = val; 169193640Sariff chn_vpc_proc(0, val); 170193640Sariff 171193640Sariff return (0); 172193640Sariff} 173193640SariffSYSCTL_PROC(_hw_snd, OID_AUTO, vpc_0db, CTLTYPE_INT | CTLFLAG_RW, 174193640Sariff 0, sizeof(int), sysctl_hw_snd_vpc_0db, "I", 175193640Sariff "0db relative level"); 176193640Sariff 177193640Sariffstatic int 178193640Sariffsysctl_hw_snd_vpc_reset(SYSCTL_HANDLER_ARGS) 179193640Sariff{ 180193640Sariff int err, val; 181193640Sariff 182193640Sariff val = 0; 183193640Sariff err = sysctl_handle_int(oidp, &val, 0, req); 184193640Sariff if (err != 0 || req->newptr == NULL || val == 0) 185193640Sariff return (err); 186193640Sariff 187193640Sariff chn_vol_0db_pcm = SND_VOL_0DB_PCM; 188193640Sariff chn_vpc_proc(1, SND_VOL_0DB_PCM); 189193640Sariff 190193640Sariff return (0); 191193640Sariff} 192193640SariffSYSCTL_PROC(_hw_snd, OID_AUTO, vpc_reset, CTLTYPE_INT | CTLFLAG_RW, 193193640Sariff 0, sizeof(int), sysctl_hw_snd_vpc_reset, "I", 194193640Sariff "reset volume on all channels"); 195193640Sariff 196167645Sariffstatic int chn_usefrags = 0; 197167645SariffTUNABLE_INT("hw.snd.usefrags", &chn_usefrags); 198167645Sariffstatic int chn_syncdelay = -1; 199167645SariffTUNABLE_INT("hw.snd.syncdelay", &chn_syncdelay); 200167645Sariff#ifdef SND_DEBUG 201167645SariffSYSCTL_INT(_hw_snd, OID_AUTO, usefrags, CTLFLAG_RW, 202167645Sariff &chn_usefrags, 1, "prefer setfragments() over setblocksize()"); 203167645SariffSYSCTL_INT(_hw_snd, OID_AUTO, syncdelay, CTLFLAG_RW, 204167645Sariff &chn_syncdelay, 1, 205167645Sariff "append (0-1000) millisecond trailing buffer delay on each sync"); 206167645Sariff#endif 207167645Sariff 208162588Snetchild/** 209162588Snetchild * @brief Channel sync group lock 210162588Snetchild * 211162588Snetchild * Clients should acquire this lock @b without holding any channel locks 212162588Snetchild * before touching syncgroups or the main syncgroup list. 213162588Snetchild */ 214162588Snetchildstruct mtx snd_pcm_syncgroups_mtx; 215162588SnetchildMTX_SYSINIT(pcm_syncgroup, &snd_pcm_syncgroups_mtx, "PCM channel sync group lock", MTX_DEF); 216162588Snetchild/** 217162588Snetchild * @brief syncgroups' master list 218162588Snetchild * 219162588Snetchild * Each time a channel syncgroup is created, it's added to this list. This 220162588Snetchild * list should only be accessed with @sa snd_pcm_syncgroups_mtx held. 221162588Snetchild * 222162588Snetchild * See SNDCTL_DSP_SYNCGROUP for more information. 223162588Snetchild */ 224201145Santoinestruct pcm_synclist snd_pcm_syncgroups = SLIST_HEAD_INITIALIZER(snd_pcm_syncgroups); 225162588Snetchild 22674763Scgstatic void 227123156Smatkchn_lockinit(struct pcm_channel *c, int dir) 22874763Scg{ 229170815Sariff switch (dir) { 230126367Struckman case PCMDIR_PLAY: 231126367Struckman c->lock = snd_mtxcreate(c->name, "pcm play channel"); 232170815Sariff cv_init(&c->intr_cv, "pcmwr"); 233126367Struckman break; 234170161Sariff case PCMDIR_PLAY_VIRTUAL: 235170161Sariff c->lock = snd_mtxcreate(c->name, "pcm virtual play channel"); 236170815Sariff cv_init(&c->intr_cv, "pcmwrv"); 237170161Sariff break; 238126367Struckman case PCMDIR_REC: 239126367Struckman c->lock = snd_mtxcreate(c->name, "pcm record channel"); 240170815Sariff cv_init(&c->intr_cv, "pcmrd"); 241126367Struckman break; 242170161Sariff case PCMDIR_REC_VIRTUAL: 243170161Sariff c->lock = snd_mtxcreate(c->name, "pcm virtual record channel"); 244170815Sariff cv_init(&c->intr_cv, "pcmrdv"); 245126367Struckman break; 246193640Sariff default: 247193640Sariff panic("%s(): Invalid direction=%d", __func__, dir); 248126367Struckman break; 249126367Struckman } 250162588Snetchild 251170815Sariff cv_init(&c->cv, "pcmchn"); 25274763Scg} 25350724Scg 25474763Scgstatic void 25574763Scgchn_lockdestroy(struct pcm_channel *c) 25674763Scg{ 257170815Sariff CHN_LOCKASSERT(c); 258170815Sariff 259170815Sariff CHN_BROADCAST(&c->cv); 260170815Sariff CHN_BROADCAST(&c->intr_cv); 261170815Sariff 262170815Sariff cv_destroy(&c->cv); 263170815Sariff cv_destroy(&c->intr_cv); 264170815Sariff 26574763Scg snd_mtxfree(c->lock); 26674763Scg} 26750724Scg 268162588Snetchild/** 269162588Snetchild * @brief Determine channel is ready for I/O 270162588Snetchild * 271162588Snetchild * @retval 1 = ready for I/O 272162588Snetchild * @retval 0 = not ready for I/O 273162588Snetchild */ 27450724Scgstatic int 27574763Scgchn_polltrigger(struct pcm_channel *c) 27650724Scg{ 27774763Scg struct snd_dbuf *bs = c->bufsoft; 278193640Sariff u_int delta; 27950724Scg 28074763Scg CHN_LOCKASSERT(c); 281193640Sariff 282193640Sariff if (c->flags & CHN_F_MMAP) { 283193640Sariff if (sndbuf_getprevtotal(bs) < c->lw) 284193640Sariff delta = c->lw; 28574797Scg else 286193640Sariff delta = sndbuf_gettotal(bs) - sndbuf_getprevtotal(bs); 28774763Scg } else { 288193640Sariff if (c->direction == PCMDIR_PLAY) 289193640Sariff delta = sndbuf_getfree(bs); 290193640Sariff else 291193640Sariff delta = sndbuf_getready(bs); 29274763Scg } 293193640Sariff 294193640Sariff return ((delta < c->lw) ? 0 : 1); 29550724Scg} 29650724Scg 297193640Sariffstatic void 29874763Scgchn_pollreset(struct pcm_channel *c) 29950724Scg{ 30050724Scg 30174763Scg CHN_LOCKASSERT(c); 302193640Sariff sndbuf_updateprevtotal(c->bufsoft); 30350724Scg} 30450724Scg 30550724Scgstatic void 30674763Scgchn_wakeup(struct pcm_channel *c) 30750724Scg{ 308170815Sariff struct snd_dbuf *bs; 309170161Sariff struct pcm_channel *ch; 31050724Scg 31174763Scg CHN_LOCKASSERT(c); 312170815Sariff 313170815Sariff bs = c->bufsoft; 314170815Sariff 315170815Sariff if (CHN_EMPTY(c, children.busy)) { 316125982Smatk if (SEL_WAITING(sndbuf_getsel(bs)) && chn_polltrigger(c)) 317126030Smatk selwakeuppri(sndbuf_getsel(bs), PRIBIO); 318170815Sariff if (c->flags & CHN_F_SLEEPING) { 319170815Sariff /* 320170815Sariff * Ok, I can just panic it right here since it is 321170815Sariff * quite obvious that we never allow multiple waiters 322170815Sariff * from userland. I'm too generous... 323170815Sariff */ 324170815Sariff CHN_BROADCAST(&c->intr_cv); 325170161Sariff } 326125982Smatk } else { 327170161Sariff CHN_FOREACH(ch, c, children.busy) { 328170161Sariff CHN_LOCK(ch); 329170161Sariff chn_wakeup(ch); 330170161Sariff CHN_UNLOCK(ch); 331125982Smatk } 332125982Smatk } 33350724Scg} 33450724Scg 33574763Scgstatic int 336170815Sariffchn_sleep(struct pcm_channel *c, int timeout) 33754535Scg{ 33874763Scg int ret; 33954535Scg 34074763Scg CHN_LOCKASSERT(c); 341170161Sariff 342170815Sariff if (c->flags & CHN_F_DEAD) 343170815Sariff return (EINVAL); 344170815Sariff 345170161Sariff c->flags |= CHN_F_SLEEPING; 346170815Sariff ret = cv_timedwait_sig(&c->intr_cv, c->lock, timeout); 347170161Sariff c->flags &= ~CHN_F_SLEEPING; 34874763Scg 349170815Sariff return ((c->flags & CHN_F_DEAD) ? EINVAL : ret); 35054535Scg} 35154535Scg 35254535Scg/* 35350724Scg * chn_dmaupdate() tracks the status of a dma transfer, 354150825Snetchild * updating pointers. 35550724Scg */ 35654155Scg 35774763Scgstatic unsigned int 35874763Scgchn_dmaupdate(struct pcm_channel *c) 35950724Scg{ 36074763Scg struct snd_dbuf *b = c->bufhard; 36174763Scg unsigned int delta, old, hwptr, amt; 36250724Scg 36377269Scg KASSERT(sndbuf_getsize(b) > 0, ("bufsize == 0")); 36474763Scg CHN_LOCKASSERT(c); 36588382Scg 36674763Scg old = sndbuf_gethwptr(b); 36754155Scg hwptr = chn_getptr(c); 36874763Scg delta = (sndbuf_getsize(b) + hwptr - old) % sndbuf_getsize(b); 36974763Scg sndbuf_sethwptr(b, hwptr); 37074763Scg 37172136Scg if (c->direction == PCMDIR_PLAY) { 372167645Sariff amt = min(delta, sndbuf_getready(b)); 373193640Sariff amt -= amt % sndbuf_getalign(b); 37474763Scg if (amt > 0) 37574763Scg sndbuf_dispose(b, NULL, amt); 37650724Scg } else { 377167645Sariff amt = min(delta, sndbuf_getfree(b)); 378193640Sariff amt -= amt % sndbuf_getalign(b); 37974763Scg if (amt > 0) 38074763Scg sndbuf_acquire(b, NULL, amt); 38150724Scg } 382167645Sariff if (snd_verbose > 3 && CHN_STARTED(c) && delta == 0) { 383167645Sariff device_printf(c->dev, "WARNING: %s DMA completion " 384164614Sariff "too fast/slow ! hwptr=%u, old=%u " 385164614Sariff "delta=%u amt=%u ready=%u free=%u\n", 386167645Sariff CHN_DIRSTR(c), hwptr, old, delta, amt, 387164614Sariff sndbuf_getready(b), sndbuf_getfree(b)); 388164614Sariff } 38974763Scg 39074763Scg return delta; 39150724Scg} 39250724Scg 393193640Sariffstatic void 39474763Scgchn_wrfeed(struct pcm_channel *c) 39572136Scg{ 39674763Scg struct snd_dbuf *b = c->bufhard; 39774763Scg struct snd_dbuf *bs = c->bufsoft; 398230845Smav unsigned int amt, want, wasfree; 39972136Scg 40074763Scg CHN_LOCKASSERT(c); 40172136Scg 402193640Sariff if ((c->flags & CHN_F_MMAP) && !(c->flags & CHN_F_CLOSING)) 40388382Scg sndbuf_acquire(bs, NULL, sndbuf_getfree(bs)); 40488382Scg 405230845Smav wasfree = sndbuf_getfree(b); 406230845Smav want = min(sndbuf_getsize(b), 407230845Smav imax(0, sndbuf_xbytes(sndbuf_getsize(bs), bs, b) - 408230845Smav sndbuf_getready(b))); 409230845Smav amt = min(wasfree, want); 410193640Sariff if (amt > 0) 411193640Sariff sndbuf_feed(bs, b, c, c->feeder, amt); 41288382Scg 413153868Sariff /* 414153868Sariff * Possible xruns. There should be no empty space left in buffer. 415153868Sariff */ 416230845Smav if (sndbuf_getready(b) < want) 417153868Sariff c->xruns++; 418148606Snetchild 419230845Smav if (sndbuf_getfree(b) < wasfree) 42074763Scg chn_wakeup(c); 421193640Sariff} 42288382Scg 423193640Sariff#if 0 424193640Sariffstatic void 425193640Sariffchn_wrupdate(struct pcm_channel *c) 426193640Sariff{ 427193640Sariff 428193640Sariff CHN_LOCKASSERT(c); 429193640Sariff KASSERT(c->direction == PCMDIR_PLAY, ("%s(): bad channel", __func__)); 430193640Sariff 431193640Sariff if ((c->flags & (CHN_F_MMAP | CHN_F_VIRTUAL)) || CHN_STOPPED(c)) 432193640Sariff return; 433193640Sariff chn_dmaupdate(c); 434193640Sariff chn_wrfeed(c); 435193640Sariff /* tell the driver we've updated the primary buffer */ 436193640Sariff chn_trigger(c, PCMTRIG_EMLDMAWR); 43754535Scg} 438193640Sariff#endif 43954535Scg 44050724Scgstatic void 44174763Scgchn_wrintr(struct pcm_channel *c) 44250724Scg{ 44350724Scg 44474763Scg CHN_LOCKASSERT(c); 44574763Scg /* update pointers in primary buffer */ 44674797Scg chn_dmaupdate(c); 44774763Scg /* ...and feed from secondary to primary */ 448193640Sariff chn_wrfeed(c); 44974763Scg /* tell the driver we've updated the primary buffer */ 45074797Scg chn_trigger(c, PCMTRIG_EMLDMAWR); 45150724Scg} 45250724Scg 45350724Scg/* 45482479Scg * user write routine - uiomove data into secondary buffer, trigger if necessary 45574763Scg * if blocking, sleep, rinse and repeat. 45650724Scg * 45774763Scg * called externally, so must handle locking 45850724Scg */ 45950724Scg 46050724Scgint 46174763Scgchn_write(struct pcm_channel *c, struct uio *buf) 46250724Scg{ 46374763Scg struct snd_dbuf *bs = c->bufsoft; 464123013Smatk void *off; 465167645Sariff int ret, timeout, sz, t, p; 46650724Scg 46774763Scg CHN_LOCKASSERT(c); 46855204Scg 46974763Scg ret = 0; 470167645Sariff timeout = chn_timeout * hz; 471162588Snetchild 472167645Sariff while (ret == 0 && buf->uio_resid > 0) { 473167645Sariff sz = min(buf->uio_resid, sndbuf_getfree(bs)); 474167645Sariff if (sz > 0) { 475123013Smatk /* 476123013Smatk * The following assumes that the free space in 477123013Smatk * the buffer can never be less around the 478123013Smatk * unlock-uiomove-lock sequence. 479123013Smatk */ 480167645Sariff while (ret == 0 && sz > 0) { 481123013Smatk p = sndbuf_getfreeptr(bs); 482167645Sariff t = min(sz, sndbuf_getsize(bs) - p); 483123013Smatk off = sndbuf_getbufofs(bs, p); 484123013Smatk CHN_UNLOCK(c); 485123013Smatk ret = uiomove(off, t, buf); 486123013Smatk CHN_LOCK(c); 487167645Sariff sz -= t; 488167645Sariff sndbuf_acquire(bs, NULL, t); 489123013Smatk } 490123013Smatk ret = 0; 491174220Sariff if (CHN_STOPPED(c) && !(c->flags & CHN_F_NOTRIGGER)) { 492170815Sariff ret = chn_start(c, 0); 493170815Sariff if (ret != 0) 494170815Sariff c->flags |= CHN_F_DEAD; 495170815Sariff } 496167645Sariff } else if (c->flags & (CHN_F_NBIO | CHN_F_NOTRIGGER)) { 497167645Sariff /** 498167645Sariff * @todo Evaluate whether EAGAIN is truly desirable. 499167645Sariff * 4Front drivers behave like this, but I'm 500167645Sariff * not sure if it at all violates the "write 501167645Sariff * should be allowed to block" model. 502167645Sariff * 503167645Sariff * The idea is that, while set with CHN_F_NOTRIGGER, 504167645Sariff * a channel isn't playing, *but* without this we 505167645Sariff * end up with "interrupt timeout / channel dead". 506167645Sariff */ 507167645Sariff ret = EAGAIN; 508167645Sariff } else { 509170815Sariff ret = chn_sleep(c, timeout); 510167645Sariff if (ret == EAGAIN) { 511167645Sariff ret = EINVAL; 512167645Sariff c->flags |= CHN_F_DEAD; 513193640Sariff device_printf(c->dev, "%s(): %s: " 514193640Sariff "play interrupt timeout, channel dead\n", 515193640Sariff __func__, c->name); 516167645Sariff } else if (ret == ERESTART || ret == EINTR) 517167645Sariff c->flags |= CHN_F_ABORTING; 51874763Scg } 51974763Scg } 52072136Scg 521170815Sariff return (ret); 52250724Scg} 52350724Scg 52454535Scg/* 52582479Scg * Feed new data from the read buffer. Can be called in the bottom half. 52654535Scg */ 527193640Sariffstatic void 52874763Scgchn_rdfeed(struct pcm_channel *c) 52954535Scg{ 53074763Scg struct snd_dbuf *b = c->bufhard; 53174763Scg struct snd_dbuf *bs = c->bufsoft; 532193640Sariff unsigned int amt; 53354535Scg 53474763Scg CHN_LOCKASSERT(c); 53554535Scg 536193640Sariff if (c->flags & CHN_F_MMAP) 537170815Sariff sndbuf_dispose(bs, NULL, sndbuf_getready(bs)); 538170815Sariff 539149953Snetchild amt = sndbuf_getfree(bs); 540193640Sariff if (amt > 0) 541193640Sariff sndbuf_feed(b, bs, c, c->feeder, amt); 54274763Scg 54383081Scg amt = sndbuf_getready(b); 544153952Sariff if (amt > 0) { 545153952Sariff c->xruns++; 546153952Sariff sndbuf_dispose(b, NULL, amt); 547153952Sariff } 54874763Scg 549168243Sariff if (sndbuf_getready(bs) > 0) 550168243Sariff chn_wakeup(c); 55154535Scg} 55254535Scg 553193640Sariff#if 0 554193640Sariffstatic void 55574763Scgchn_rdupdate(struct pcm_channel *c) 55654535Scg{ 55754535Scg 55874763Scg CHN_LOCKASSERT(c); 55974763Scg KASSERT(c->direction == PCMDIR_REC, ("chn_rdupdate on bad channel")); 56054535Scg 561193640Sariff if ((c->flags & (CHN_F_MMAP | CHN_F_VIRTUAL)) || CHN_STOPPED(c)) 56274763Scg return; 56374763Scg chn_trigger(c, PCMTRIG_EMLDMARD); 56474763Scg chn_dmaupdate(c); 565193640Sariff chn_rdfeed(c); 56654535Scg} 567193640Sariff#endif 56854535Scg 56950724Scg/* read interrupt routine. Must be called with interrupts blocked. */ 57050724Scgstatic void 57174763Scgchn_rdintr(struct pcm_channel *c) 57250724Scg{ 57350724Scg 57474763Scg CHN_LOCKASSERT(c); 57582479Scg /* tell the driver to update the primary buffer if non-dma */ 57674797Scg chn_trigger(c, PCMTRIG_EMLDMARD); 57782492Scg /* update pointers in primary buffer */ 57874797Scg chn_dmaupdate(c); 57974763Scg /* ...and feed from primary to secondary */ 580193640Sariff chn_rdfeed(c); 58150724Scg} 58250724Scg 58350724Scg/* 58489689Scg * user read routine - trigger if necessary, uiomove data from secondary buffer 58574763Scg * if blocking, sleep, rinse and repeat. 58650724Scg * 58774763Scg * called externally, so must handle locking 58850724Scg */ 58950724Scg 59050724Scgint 59174763Scgchn_read(struct pcm_channel *c, struct uio *buf) 59250724Scg{ 593164614Sariff struct snd_dbuf *bs = c->bufsoft; 594123013Smatk void *off; 595167645Sariff int ret, timeout, sz, t, p; 59650724Scg 59774763Scg CHN_LOCKASSERT(c); 598167645Sariff 599174220Sariff if (CHN_STOPPED(c) && !(c->flags & CHN_F_NOTRIGGER)) { 600170815Sariff ret = chn_start(c, 0); 601170815Sariff if (ret != 0) { 602170815Sariff c->flags |= CHN_F_DEAD; 603170815Sariff return (ret); 604170815Sariff } 605170815Sariff } 60655204Scg 60774763Scg ret = 0; 608167645Sariff timeout = chn_timeout * hz; 60955204Scg 610167645Sariff while (ret == 0 && buf->uio_resid > 0) { 611167645Sariff sz = min(buf->uio_resid, sndbuf_getready(bs)); 61285406Scg if (sz > 0) { 613123013Smatk /* 614123013Smatk * The following assumes that the free space in 615123013Smatk * the buffer can never be less around the 616123013Smatk * unlock-uiomove-lock sequence. 617123013Smatk */ 618167645Sariff while (ret == 0 && sz > 0) { 619123013Smatk p = sndbuf_getreadyptr(bs); 620167645Sariff t = min(sz, sndbuf_getsize(bs) - p); 621123013Smatk off = sndbuf_getbufofs(bs, p); 622123013Smatk CHN_UNLOCK(c); 623123013Smatk ret = uiomove(off, t, buf); 624123013Smatk CHN_LOCK(c); 625167645Sariff sz -= t; 626167645Sariff sndbuf_dispose(bs, NULL, t); 627123013Smatk } 628123013Smatk ret = 0; 629167645Sariff } else if (c->flags & (CHN_F_NBIO | CHN_F_NOTRIGGER)) 630167645Sariff ret = EAGAIN; 631167645Sariff else { 632170815Sariff ret = chn_sleep(c, timeout); 633167645Sariff if (ret == EAGAIN) { 634167645Sariff ret = EINVAL; 635167645Sariff c->flags |= CHN_F_DEAD; 636193640Sariff device_printf(c->dev, "%s(): %s: " 637193640Sariff "record interrupt timeout, channel dead\n", 638193640Sariff __func__, c->name); 639167645Sariff } else if (ret == ERESTART || ret == EINTR) 640167645Sariff c->flags |= CHN_F_ABORTING; 64174763Scg } 64274763Scg } 64372136Scg 644170815Sariff return (ret); 64550724Scg} 64650724Scg 64750724Scgvoid 648193640Sariffchn_intr_locked(struct pcm_channel *c) 64950724Scg{ 650193640Sariff 651193640Sariff CHN_LOCKASSERT(c); 652193640Sariff 65382479Scg c->interrupts++; 654193640Sariff 65559324Scg if (c->direction == PCMDIR_PLAY) 65659324Scg chn_wrintr(c); 65759324Scg else 65859324Scg chn_rdintr(c); 659193640Sariff} 660193640Sariff 661193640Sariffvoid 662193640Sariffchn_intr(struct pcm_channel *c) 663193640Sariff{ 664193640Sariff 665193640Sariff if (CHN_LOCKOWNED(c)) { 666193640Sariff chn_intr_locked(c); 667193640Sariff return; 668184610Salfred } 669193640Sariff 670193640Sariff CHN_LOCK(c); 671193640Sariff chn_intr_locked(c); 672193640Sariff CHN_UNLOCK(c); 67350724Scg} 67450724Scg 67560961Scgu_int32_t 67674763Scgchn_start(struct pcm_channel *c, int force) 67760961Scg{ 67882492Scg u_int32_t i, j; 67974763Scg struct snd_dbuf *b = c->bufhard; 68074763Scg struct snd_dbuf *bs = c->bufsoft; 681170815Sariff int err; 68260961Scg 68374763Scg CHN_LOCKASSERT(c); 68474763Scg /* if we're running, or if we're prevented from triggering, bail */ 685167645Sariff if (CHN_STARTED(c) || ((c->flags & CHN_F_NOTRIGGER) && !force)) 686170815Sariff return (EINVAL); 68774763Scg 688170815Sariff err = 0; 689170815Sariff 690164614Sariff if (force) { 691164614Sariff i = 1; 692164614Sariff j = 0; 693164614Sariff } else { 694164614Sariff if (c->direction == PCMDIR_REC) { 695164614Sariff i = sndbuf_getfree(bs); 696167645Sariff j = (i > 0) ? 1 : sndbuf_getready(b); 697164614Sariff } else { 698167645Sariff if (sndbuf_getfree(bs) == 0) { 699167645Sariff i = 1; 700167645Sariff j = 0; 701167645Sariff } else { 702167645Sariff struct snd_dbuf *pb; 703164614Sariff 704170815Sariff pb = CHN_BUF_PARENT(c, b); 705167645Sariff i = sndbuf_xbytes(sndbuf_getready(bs), bs, pb); 706193640Sariff j = sndbuf_getalign(pb); 707167645Sariff } 708164614Sariff } 709170161Sariff if (snd_verbose > 3 && CHN_EMPTY(c, children)) 710193640Sariff device_printf(c->dev, "%s(): %s (%s) threshold " 711193640Sariff "i=%d j=%d\n", __func__, CHN_DIRSTR(c), 712193640Sariff (c->flags & CHN_F_VIRTUAL) ? "virtual" : 713193640Sariff "hardware", i, j); 714164614Sariff } 715164614Sariff 716164614Sariff if (i >= j) { 71774763Scg c->flags |= CHN_F_TRIGGERED; 71874763Scg sndbuf_setrun(b, 1); 719193640Sariff if (c->flags & CHN_F_CLOSING) 720193640Sariff c->feedcount = 2; 721193640Sariff else { 722193640Sariff c->feedcount = 0; 723193640Sariff c->interrupts = 0; 724193640Sariff c->xruns = 0; 725193640Sariff } 726193640Sariff if (c->parentchannel == NULL) { 727193640Sariff if (c->direction == PCMDIR_PLAY) 728230845Smav sndbuf_fillsilence_rl(b, 729230845Smav sndbuf_xbytes(sndbuf_getsize(bs), bs, b)); 730164614Sariff if (snd_verbose > 3) 731193640Sariff device_printf(c->dev, 732193640Sariff "%s(): %s starting! (%s/%s) " 733193640Sariff "(ready=%d force=%d i=%d j=%d " 734193640Sariff "intrtimeout=%u latency=%dms)\n", 735164614Sariff __func__, 736164614Sariff (c->flags & CHN_F_HAS_VCHAN) ? 737193640Sariff "VCHAN PARENT" : "HW", CHN_DIRSTR(c), 738164614Sariff (c->flags & CHN_F_CLOSING) ? "closing" : 739164614Sariff "running", 740164614Sariff sndbuf_getready(b), 741167645Sariff force, i, j, c->timeout, 742167645Sariff (sndbuf_getsize(b) * 1000) / 743193640Sariff (sndbuf_getalign(b) * sndbuf_getspd(b))); 744164614Sariff } 745170815Sariff err = chn_trigger(c, PCMTRIG_START); 74660961Scg } 74774763Scg 748170815Sariff return (err); 74960961Scg} 75060961Scg 75165340Scgvoid 75274763Scgchn_resetbuf(struct pcm_channel *c) 75350724Scg{ 75474763Scg struct snd_dbuf *b = c->bufhard; 75574763Scg struct snd_dbuf *bs = c->bufsoft; 75650724Scg 75759246Scg c->blocks = 0; 75870291Scg sndbuf_reset(b); 75970291Scg sndbuf_reset(bs); 76050724Scg} 76150724Scg 76250724Scg/* 76355204Scg * chn_sync waits until the space in the given channel goes above 76450724Scg * a threshold. The threshold is checked against fl or rl respectively. 76550724Scg * Assume that the condition can become true, do not check here... 76650724Scg */ 76750724Scgint 76874763Scgchn_sync(struct pcm_channel *c, int threshold) 76950724Scg{ 770164614Sariff struct snd_dbuf *b, *bs; 771167663Sariff int ret, count, hcount, minflush, resid, residp, syncdelay, blksz; 772167663Sariff u_int32_t cflag; 77350724Scg 77474763Scg CHN_LOCKASSERT(c); 775119095Scg 776170815Sariff if (c->direction != PCMDIR_PLAY) 777170815Sariff return (EINVAL); 778170815Sariff 779167645Sariff bs = c->bufsoft; 780167645Sariff 781167645Sariff if ((c->flags & (CHN_F_DEAD | CHN_F_ABORTING)) || 782167645Sariff (threshold < 1 && sndbuf_getready(bs) < 1)) 783170815Sariff return (0); 784164614Sariff 785119095Scg /* if we haven't yet started and nothing is buffered, else start*/ 786167645Sariff if (CHN_STOPPED(c)) { 787167645Sariff if (threshold > 0 || sndbuf_getready(bs) > 0) { 788119095Scg ret = chn_start(c, 1); 789170815Sariff if (ret != 0) 790170815Sariff return (ret); 791164614Sariff } else 792170815Sariff return (0); 793164614Sariff } 794164614Sariff 795170815Sariff b = CHN_BUF_PARENT(c, c->bufhard); 796164614Sariff 797167645Sariff minflush = threshold + sndbuf_xbytes(sndbuf_getready(b), b, bs); 798167645Sariff 799167645Sariff syncdelay = chn_syncdelay; 800167645Sariff 801167645Sariff if (syncdelay < 0 && (threshold > 0 || sndbuf_getready(bs) > 0)) 802167645Sariff minflush += sndbuf_xbytes(sndbuf_getsize(b), b, bs); 803167645Sariff 804167645Sariff /* 805167645Sariff * Append (0-1000) millisecond trailing buffer (if needed) 806167645Sariff * for slower / high latency hardwares (notably USB audio) 807167645Sariff * to avoid audible truncation. 808167645Sariff */ 809167645Sariff if (syncdelay > 0) 810193640Sariff minflush += (sndbuf_getalign(bs) * sndbuf_getspd(bs) * 811167645Sariff ((syncdelay > 1000) ? 1000 : syncdelay)) / 1000; 812167645Sariff 813193640Sariff minflush -= minflush % sndbuf_getalign(bs); 814164614Sariff 815164614Sariff if (minflush > 0) { 816164614Sariff threshold = min(minflush, sndbuf_getfree(bs)); 817164614Sariff sndbuf_clear(bs, threshold); 818164614Sariff sndbuf_acquire(bs, NULL, threshold); 819164614Sariff minflush -= threshold; 820164614Sariff } 821164614Sariff 822164614Sariff resid = sndbuf_getready(bs); 823164614Sariff residp = resid; 824167645Sariff blksz = sndbuf_getblksz(b); 825167645Sariff if (blksz < 1) { 826193640Sariff device_printf(c->dev, 827193640Sariff "%s(): WARNING: blksz < 1 ! maxsize=%d [%d/%d/%d]\n", 828167645Sariff __func__, sndbuf_getmaxsize(b), sndbuf_getsize(b), 829167645Sariff sndbuf_getblksz(b), sndbuf_getblkcnt(b)); 830167645Sariff if (sndbuf_getblkcnt(b) > 0) 831167645Sariff blksz = sndbuf_getsize(b) / sndbuf_getblkcnt(b); 832167645Sariff if (blksz < 1) 833167645Sariff blksz = 1; 834167645Sariff } 835167645Sariff count = sndbuf_xbytes(minflush + resid, bs, b) / blksz; 836164614Sariff hcount = count; 837164614Sariff ret = 0; 838164614Sariff 839167645Sariff if (snd_verbose > 3) 840193640Sariff device_printf(c->dev, "%s(): [begin] timeout=%d count=%d " 841167645Sariff "minflush=%d resid=%d\n", __func__, c->timeout, count, 842167645Sariff minflush, resid); 843167645Sariff 844167663Sariff cflag = c->flags & CHN_F_CLOSING; 845167663Sariff c->flags |= CHN_F_CLOSING; 846167645Sariff while (count > 0 && (resid > 0 || minflush > 0)) { 847170815Sariff ret = chn_sleep(c, c->timeout); 848164614Sariff if (ret == ERESTART || ret == EINTR) { 849164614Sariff c->flags |= CHN_F_ABORTING; 850164614Sariff break; 851170815Sariff } else if (ret == 0 || ret == EAGAIN) { 852164614Sariff resid = sndbuf_getready(bs); 853164614Sariff if (resid == residp) { 854164614Sariff --count; 855164614Sariff if (snd_verbose > 3) 856193640Sariff device_printf(c->dev, 857193640Sariff "%s(): [stalled] timeout=%d " 858164614Sariff "count=%d hcount=%d " 859164614Sariff "resid=%d minflush=%d\n", 860164614Sariff __func__, c->timeout, count, 861164614Sariff hcount, resid, minflush); 862164614Sariff } else if (resid < residp && count < hcount) { 863164614Sariff ++count; 864164614Sariff if (snd_verbose > 3) 865193640Sariff device_printf(c->dev, 866193640Sariff "%s((): [resume] timeout=%d " 867164614Sariff "count=%d hcount=%d " 868164614Sariff "resid=%d minflush=%d\n", 869164614Sariff __func__, c->timeout, count, 870164614Sariff hcount, resid, minflush); 871164614Sariff } 872164614Sariff if (minflush > 0 && sndbuf_getfree(bs) > 0) { 873164614Sariff threshold = min(minflush, 874164614Sariff sndbuf_getfree(bs)); 875164614Sariff sndbuf_clear(bs, threshold); 876164614Sariff sndbuf_acquire(bs, NULL, threshold); 877164614Sariff resid = sndbuf_getready(bs); 878164614Sariff minflush -= threshold; 879164614Sariff } 880164614Sariff residp = resid; 881170815Sariff } else 882170815Sariff break; 883119095Scg } 884167663Sariff c->flags &= ~CHN_F_CLOSING; 885167663Sariff c->flags |= cflag; 886119095Scg 887164614Sariff if (snd_verbose > 3) 888193640Sariff device_printf(c->dev, 889193640Sariff "%s(): timeout=%d count=%d hcount=%d resid=%d residp=%d " 890164614Sariff "minflush=%d ret=%d\n", 891164614Sariff __func__, c->timeout, count, hcount, resid, residp, 892164614Sariff minflush, ret); 893164614Sariff 894170815Sariff return (0); 89550724Scg} 89650724Scg 89774763Scg/* called externally, handle locking */ 89850724Scgint 89983366Sjulianchn_poll(struct pcm_channel *c, int ev, struct thread *td) 90050724Scg{ 90174763Scg struct snd_dbuf *bs = c->bufsoft; 90255204Scg int ret; 90355204Scg 90478214Scg CHN_LOCKASSERT(c); 905193640Sariff 906193640Sariff if (!(c->flags & (CHN_F_MMAP | CHN_F_TRIGGERED))) { 907170815Sariff ret = chn_start(c, 1); 908170815Sariff if (ret != 0) 909170815Sariff return (0); 910170815Sariff } 911193640Sariff 91255204Scg ret = 0; 913193640Sariff if (chn_polltrigger(c)) { 914193640Sariff chn_pollreset(c); 91555204Scg ret = ev; 916193640Sariff } else 91783805Sjhb selrecord(td, sndbuf_getsel(bs)); 918193640Sariff 919170815Sariff return (ret); 92050724Scg} 92150724Scg 92250724Scg/* 92374763Scg * chn_abort terminates a running dma transfer. it may sleep up to 200ms. 92474763Scg * it returns the number of bytes that have not been transferred. 92574763Scg * 92677269Scg * called from: dsp_close, dsp_ioctl, with channel locked 92750724Scg */ 92850724Scgint 92974763Scgchn_abort(struct pcm_channel *c) 93050724Scg{ 93178362Scg int missing = 0; 93274763Scg struct snd_dbuf *b = c->bufhard; 93374763Scg struct snd_dbuf *bs = c->bufsoft; 93450724Scg 93574763Scg CHN_LOCKASSERT(c); 936167645Sariff if (CHN_STOPPED(c)) 93761642Scg return 0; 93854792Scg c->flags |= CHN_F_ABORTING; 93974763Scg 94072136Scg c->flags &= ~CHN_F_TRIGGERED; 94174763Scg /* kill the channel */ 94254792Scg chn_trigger(c, PCMTRIG_ABORT); 94374763Scg sndbuf_setrun(b, 0); 94477882Scg if (!(c->flags & CHN_F_VIRTUAL)) 94577882Scg chn_dmaupdate(c); 946167645Sariff missing = sndbuf_getready(bs); 94774763Scg 94874763Scg c->flags &= ~CHN_F_ABORTING; 94974797Scg return missing; 95050724Scg} 95150724Scg 95250724Scg/* 95350724Scg * this routine tries to flush the dma transfer. It is called 954119095Scg * on a close of a playback channel. 955119095Scg * first, if there is data in the buffer, but the dma has not yet 956119095Scg * begun, we need to start it. 957119095Scg * next, we wait for the play buffer to drain 958119095Scg * finally, we stop the dma. 95974763Scg * 960119095Scg * called from: dsp_close, not valid for record channels. 96150724Scg */ 96250724Scg 96350724Scgint 96474763Scgchn_flush(struct pcm_channel *c) 96550724Scg{ 96674763Scg struct snd_dbuf *b = c->bufhard; 96750724Scg 96874763Scg CHN_LOCKASSERT(c); 969119095Scg KASSERT(c->direction == PCMDIR_PLAY, ("chn_flush on bad channel")); 970119095Scg DEB(printf("chn_flush: c->flags 0x%08x\n", c->flags)); 97168413Scg 97274797Scg c->flags |= CHN_F_CLOSING; 973164614Sariff chn_sync(c, 0); 97474763Scg c->flags &= ~CHN_F_TRIGGERED; 97574763Scg /* kill the channel */ 97674763Scg chn_trigger(c, PCMTRIG_ABORT); 97774763Scg sndbuf_setrun(b, 0); 97874763Scg 97950724Scg c->flags &= ~CHN_F_CLOSING; 98050724Scg return 0; 98150724Scg} 98250724Scg 98350724Scgint 984193640Sariffsnd_fmtvalid(uint32_t fmt, uint32_t *fmtlist) 98566308Scg{ 98666308Scg int i; 98766308Scg 988193640Sariff for (i = 0; fmtlist[i] != 0; i++) { 989193640Sariff if (fmt == fmtlist[i] || 990193640Sariff ((fmt & AFMT_PASSTHROUGH) && 991193640Sariff (AFMT_ENCODING(fmt) & fmtlist[i]))) 992193640Sariff return (1); 993193640Sariff } 994193640Sariff 995193640Sariff return (0); 99666308Scg} 99766308Scg 998193640Sariffstatic const struct { 999193640Sariff char *name, *alias1, *alias2; 1000193640Sariff uint32_t afmt; 1001193640Sariff} afmt_tab[] = { 1002193640Sariff { "alaw", NULL, NULL, AFMT_A_LAW }, 1003193640Sariff { "mulaw", NULL, NULL, AFMT_MU_LAW }, 1004193640Sariff { "u8", "8", NULL, AFMT_U8 }, 1005193640Sariff { "s8", NULL, NULL, AFMT_S8 }, 1006193640Sariff#if BYTE_ORDER == LITTLE_ENDIAN 1007193640Sariff { "s16le", "s16", "16", AFMT_S16_LE }, 1008193640Sariff { "s16be", NULL, NULL, AFMT_S16_BE }, 1009193640Sariff#else 1010193640Sariff { "s16le", NULL, NULL, AFMT_S16_LE }, 1011193640Sariff { "s16be", "s16", "16", AFMT_S16_BE }, 1012193640Sariff#endif 1013193640Sariff { "u16le", NULL, NULL, AFMT_U16_LE }, 1014193640Sariff { "u16be", NULL, NULL, AFMT_U16_BE }, 1015193640Sariff { "s24le", NULL, NULL, AFMT_S24_LE }, 1016193640Sariff { "s24be", NULL, NULL, AFMT_S24_BE }, 1017193640Sariff { "u24le", NULL, NULL, AFMT_U24_LE }, 1018193640Sariff { "u24be", NULL, NULL, AFMT_U24_BE }, 1019193640Sariff#if BYTE_ORDER == LITTLE_ENDIAN 1020193640Sariff { "s32le", "s32", "32", AFMT_S32_LE }, 1021193640Sariff { "s32be", NULL, NULL, AFMT_S32_BE }, 1022193640Sariff#else 1023193640Sariff { "s32le", NULL, NULL, AFMT_S32_LE }, 1024193640Sariff { "s32be", "s32", "32", AFMT_S32_BE }, 1025193640Sariff#endif 1026193640Sariff { "u32le", NULL, NULL, AFMT_U32_LE }, 1027193640Sariff { "u32be", NULL, NULL, AFMT_U32_BE }, 1028193640Sariff { "ac3", NULL, NULL, AFMT_AC3 }, 1029193640Sariff { NULL, NULL, NULL, 0 } 1030164614Sariff}; 1031164614Sariff 1032193640Sariffstatic const struct { 1033193640Sariff char *name, *alias1, *alias2; 1034193640Sariff int matrix_id; 1035193640Sariff} matrix_id_tab[] = { 1036193640Sariff { "1.0", "1", "mono", SND_CHN_MATRIX_1_0 }, 1037193640Sariff { "2.0", "2", "stereo", SND_CHN_MATRIX_2_0 }, 1038193640Sariff { "2.1", NULL, NULL, SND_CHN_MATRIX_2_1 }, 1039193640Sariff { "3.0", "3", NULL, SND_CHN_MATRIX_3_0 }, 1040243138Smav { "3.1", NULL, NULL, SND_CHN_MATRIX_3_1 }, 1041193640Sariff { "4.0", "4", "quad", SND_CHN_MATRIX_4_0 }, 1042193640Sariff { "4.1", NULL, NULL, SND_CHN_MATRIX_4_1 }, 1043193640Sariff { "5.0", "5", NULL, SND_CHN_MATRIX_5_0 }, 1044193640Sariff { "5.1", "6", NULL, SND_CHN_MATRIX_5_1 }, 1045193640Sariff { "6.0", NULL, NULL, SND_CHN_MATRIX_6_0 }, 1046193640Sariff { "6.1", "7", NULL, SND_CHN_MATRIX_6_1 }, 1047243138Smav { "7.0", NULL, NULL, SND_CHN_MATRIX_7_0 }, 1048193640Sariff { "7.1", "8", NULL, SND_CHN_MATRIX_7_1 }, 1049193640Sariff { NULL, NULL, NULL, SND_CHN_MATRIX_UNKNOWN } 1050193640Sariff}; 1051164614Sariff 1052193640Sariffuint32_t 1053193640Sariffsnd_str2afmt(const char *req) 1054164614Sariff{ 1055193640Sariff uint32_t i, afmt; 1056193640Sariff int matrix_id; 1057193640Sariff char b1[8], b2[8]; 1058164614Sariff 1059193640Sariff i = sscanf(req, "%5[^:]:%6s", b1, b2); 1060164614Sariff 1061193640Sariff if (i == 1) { 1062193640Sariff if (strlen(req) != strlen(b1)) 1063193640Sariff return (0); 1064193640Sariff strlcpy(b2, "2.0", sizeof(b2)); 1065193640Sariff } else if (i == 2) { 1066193640Sariff if (strlen(req) != (strlen(b1) + 1 + strlen(b2))) 1067193640Sariff return (0); 1068193640Sariff } else 1069193640Sariff return (0); 1070164614Sariff 1071193640Sariff afmt = 0; 1072193640Sariff matrix_id = SND_CHN_MATRIX_UNKNOWN; 1073164614Sariff 1074193640Sariff for (i = 0; afmt == 0 && afmt_tab[i].name != NULL; i++) { 1075193640Sariff if (strcasecmp(afmt_tab[i].name, b1) == 0 || 1076193640Sariff (afmt_tab[i].alias1 != NULL && 1077193640Sariff strcasecmp(afmt_tab[i].alias1, b1) == 0) || 1078193640Sariff (afmt_tab[i].alias2 != NULL && 1079193640Sariff strcasecmp(afmt_tab[i].alias2, b1) == 0)) { 1080193640Sariff afmt = afmt_tab[i].afmt; 1081193640Sariff strlcpy(b1, afmt_tab[i].name, sizeof(b1)); 1082193640Sariff } 1083193640Sariff } 1084164614Sariff 1085193640Sariff if (afmt == 0) 1086193640Sariff return (0); 1087193640Sariff 1088193640Sariff for (i = 0; matrix_id == SND_CHN_MATRIX_UNKNOWN && 1089193640Sariff matrix_id_tab[i].name != NULL; i++) { 1090193640Sariff if (strcmp(matrix_id_tab[i].name, b2) == 0 || 1091193640Sariff (matrix_id_tab[i].alias1 != NULL && 1092193640Sariff strcmp(matrix_id_tab[i].alias1, b2) == 0) || 1093193640Sariff (matrix_id_tab[i].alias2 != NULL && 1094193640Sariff strcasecmp(matrix_id_tab[i].alias2, b2) == 0)) { 1095193640Sariff matrix_id = matrix_id_tab[i].matrix_id; 1096193640Sariff strlcpy(b2, matrix_id_tab[i].name, sizeof(b2)); 1097164614Sariff } 1098164614Sariff } 1099164614Sariff 1100193640Sariff if (matrix_id == SND_CHN_MATRIX_UNKNOWN) 1101193640Sariff return (0); 1102193640Sariff 1103193640Sariff#ifndef _KERNEL 1104193640Sariff printf("Parse OK: '%s' -> '%s:%s' %d\n", req, b1, b2, 1105193640Sariff (int)(b2[0]) - '0' + (int)(b2[2]) - '0'); 1106193640Sariff#endif 1107193640Sariff 1108193640Sariff return (SND_FORMAT(afmt, b2[0] - '0' + b2[2] - '0', b2[2] - '0')); 1109164614Sariff} 1110164614Sariff 1111193640Sariffuint32_t 1112193640Sariffsnd_afmt2str(uint32_t afmt, char *buf, size_t len) 1113164614Sariff{ 1114193640Sariff uint32_t i, enc, ch, ext; 1115193640Sariff char tmp[AFMTSTR_LEN]; 1116164614Sariff 1117193640Sariff if (buf == NULL || len < AFMTSTR_LEN) 1118193640Sariff return (0); 1119164614Sariff 1120193640Sariff 1121193640Sariff bzero(tmp, sizeof(tmp)); 1122193640Sariff 1123193640Sariff enc = AFMT_ENCODING(afmt); 1124193640Sariff ch = AFMT_CHANNEL(afmt); 1125193640Sariff ext = AFMT_EXTCHANNEL(afmt); 1126193640Sariff 1127193640Sariff for (i = 0; afmt_tab[i].name != NULL; i++) { 1128193640Sariff if (enc == afmt_tab[i].afmt) { 1129193640Sariff strlcpy(tmp, afmt_tab[i].name, sizeof(tmp)); 1130193640Sariff strlcat(tmp, ":", sizeof(tmp)); 1131164614Sariff break; 1132193640Sariff } 1133164614Sariff } 1134164614Sariff 1135193640Sariff if (strlen(tmp) == 0) 1136193640Sariff return (0); 1137193640Sariff 1138193640Sariff for (i = 0; matrix_id_tab[i].name != NULL; i++) { 1139193640Sariff if (ch == (matrix_id_tab[i].name[0] - '0' + 1140193640Sariff matrix_id_tab[i].name[2] - '0') && 1141193640Sariff ext == (matrix_id_tab[i].name[2] - '0')) { 1142193640Sariff strlcat(tmp, matrix_id_tab[i].name, sizeof(tmp)); 1143164614Sariff break; 1144164614Sariff } 1145164614Sariff } 1146164614Sariff 1147193640Sariff if (strlen(tmp) == 0) 1148193640Sariff return (0); 1149193640Sariff 1150193640Sariff strlcpy(buf, tmp, len); 1151193640Sariff 1152193640Sariff return (snd_str2afmt(buf)); 1153164614Sariff} 1154164614Sariff 1155164614Sariffint 1156193640Sariffchn_reset(struct pcm_channel *c, uint32_t fmt, uint32_t spd) 115750724Scg{ 1158193640Sariff int r; 115959246Scg 116074763Scg CHN_LOCKASSERT(c); 1161164614Sariff c->feedcount = 0; 116250724Scg c->flags &= CHN_F_RESET; 116382479Scg c->interrupts = 0; 1164164614Sariff c->timeout = 1; 116582479Scg c->xruns = 0; 116689687Scg 1167193640Sariff c->flags |= (pcm_getflags(c->dev) & SD_F_BITPERFECT) ? 1168193640Sariff CHN_F_BITPERFECT : 0; 1169193640Sariff 117089687Scg r = CHANNEL_RESET(c->methods, c->devinfo); 1171193640Sariff if (r == 0 && fmt != 0 && spd != 0) { 1172193640Sariff r = chn_setparam(c, fmt, spd); 1173193640Sariff fmt = 0; 1174193640Sariff spd = 0; 117559324Scg } 1176193640Sariff if (r == 0 && fmt != 0) 1177193640Sariff r = chn_setformat(c, fmt); 1178193640Sariff if (r == 0 && spd != 0) 1179193640Sariff r = chn_setspeed(c, spd); 118089687Scg if (r == 0) 1181164614Sariff r = chn_setlatency(c, chn_latency); 118274763Scg if (r == 0) { 118374797Scg chn_resetbuf(c); 118489687Scg r = CHANNEL_RESETDONE(c->methods, c->devinfo); 118550724Scg } 118674763Scg return r; 118750724Scg} 118850724Scg 118950724Scgint 1190126367Struckmanchn_init(struct pcm_channel *c, void *devinfo, int dir, int direction) 119150724Scg{ 119270134Scg struct feeder_class *fc; 119374763Scg struct snd_dbuf *b, *bs; 1194193640Sariff int i, ret; 119554535Scg 1196168243Sariff if (chn_timeout < CHN_TIMEOUT_MIN || chn_timeout > CHN_TIMEOUT_MAX) 1197168243Sariff chn_timeout = CHN_TIMEOUT; 1198168243Sariff 1199123156Smatk chn_lockinit(c, dir); 120082479Scg 120189687Scg b = NULL; 120289687Scg bs = NULL; 1203170161Sariff CHN_INIT(c, children); 1204170161Sariff CHN_INIT(c, children.busy); 120589687Scg c->devinfo = NULL; 120670134Scg c->feeder = NULL; 1207164614Sariff c->latency = -1; 1208164614Sariff c->timeout = 1; 120989687Scg 1210125136Struckman ret = ENOMEM; 1211125136Struckman b = sndbuf_create(c->dev, c->name, "primary", c); 1212125136Struckman if (b == NULL) 1213125136Struckman goto out; 1214125136Struckman bs = sndbuf_create(c->dev, c->name, "secondary", c); 1215125136Struckman if (bs == NULL) 1216125136Struckman goto out; 1217125136Struckman 1218125136Struckman CHN_LOCK(c); 1219125136Struckman 122089687Scg ret = EINVAL; 122170134Scg fc = feeder_getclass(NULL); 122270134Scg if (fc == NULL) 122389687Scg goto out; 122470134Scg if (chn_addfeeder(c, fc, NULL)) 122589687Scg goto out; 122664881Scg 1227125136Struckman /* 1228125136Struckman * XXX - sndbuf_setup() & sndbuf_resize() expect to be called 1229125136Struckman * with the channel unlocked because they are also called 1230125136Struckman * from driver methods that don't know about locking 1231125136Struckman */ 1232125136Struckman CHN_UNLOCK(c); 123374763Scg sndbuf_setup(bs, NULL, 0); 1234125136Struckman CHN_LOCK(c); 123574763Scg c->bufhard = b; 123674763Scg c->bufsoft = bs; 123750724Scg c->flags = 0; 123866308Scg c->feederflags = 0; 1239162588Snetchild c->sm = NULL; 1240193640Sariff c->format = SND_FORMAT(AFMT_U8, 1, 0); 1241193640Sariff c->speed = DSP_DEFAULT_SPEED; 124289687Scg 1243193640Sariff c->matrix = *feeder_matrix_id_map(SND_CHN_MATRIX_1_0); 1244193640Sariff c->matrix.id = SND_CHN_MATRIX_PCMCHANNEL; 1245193640Sariff 1246193640Sariff for (i = 0; i < SND_CHN_T_MAX; i++) { 1247193640Sariff c->volume[SND_VOL_C_MASTER][i] = SND_VOL_0DB_MASTER; 1248193640Sariff } 1249193640Sariff 1250193640Sariff c->volume[SND_VOL_C_MASTER][SND_CHN_T_VOL_0DB] = SND_VOL_0DB_MASTER; 1251193640Sariff c->volume[SND_VOL_C_PCM][SND_CHN_T_VOL_0DB] = chn_vol_0db_pcm; 1252193640Sariff 1253193640Sariff chn_vpc_reset(c, SND_VOL_C_PCM, 1); 1254193640Sariff 125589687Scg ret = ENODEV; 1256125136Struckman CHN_UNLOCK(c); /* XXX - Unlock for CHANNEL_INIT() malloc() call */ 1257126367Struckman c->devinfo = CHANNEL_INIT(c->methods, devinfo, b, c, direction); 1258125136Struckman CHN_LOCK(c); 125989687Scg if (c->devinfo == NULL) 126089687Scg goto out; 126189687Scg 126289687Scg ret = ENOMEM; 126389687Scg if ((sndbuf_getsize(b) == 0) && ((c->flags & CHN_F_VIRTUAL) == 0)) 126489687Scg goto out; 126589687Scg 1266193640Sariff ret = 0; 1267193640Sariff c->direction = direction; 126889687Scg 1269193640Sariff sndbuf_setfmt(b, c->format); 1270193640Sariff sndbuf_setspd(b, c->speed); 1271193640Sariff sndbuf_setfmt(bs, c->format); 1272193640Sariff sndbuf_setspd(bs, c->speed); 127389687Scg 1274162588Snetchild /** 1275162588Snetchild * @todo Should this be moved somewhere else? The primary buffer 1276162588Snetchild * is allocated by the driver or via DMA map setup, and tmpbuf 1277162588Snetchild * seems to only come into existence in sndbuf_resize(). 1278162588Snetchild */ 1279162588Snetchild if (c->direction == PCMDIR_PLAY) { 1280162588Snetchild bs->sl = sndbuf_getmaxsize(bs); 1281162588Snetchild bs->shadbuf = malloc(bs->sl, M_DEVBUF, M_NOWAIT); 1282162588Snetchild if (bs->shadbuf == NULL) { 1283162588Snetchild ret = ENOMEM; 1284162588Snetchild goto out; 1285162588Snetchild } 1286162588Snetchild } 128789687Scg 128889687Scgout: 1289125136Struckman CHN_UNLOCK(c); 129089687Scg if (ret) { 129189687Scg if (c->devinfo) { 129289687Scg if (CHANNEL_FREE(c->methods, c->devinfo)) 129389687Scg sndbuf_free(b); 129489687Scg } 129589687Scg if (bs) 129689687Scg sndbuf_destroy(bs); 129789687Scg if (b) 129889687Scg sndbuf_destroy(b); 1299170815Sariff CHN_LOCK(c); 130089687Scg c->flags |= CHN_F_DEAD; 130189687Scg chn_lockdestroy(c); 130289687Scg 130389687Scg return ret; 130474763Scg } 130555204Scg 130650724Scg return 0; 130750724Scg} 130850724Scg 130950724Scgint 131074763Scgchn_kill(struct pcm_channel *c) 131165340Scg{ 131274763Scg struct snd_dbuf *b = c->bufhard; 131374763Scg struct snd_dbuf *bs = c->bufsoft; 131474763Scg 1315170161Sariff if (CHN_STARTED(c)) { 1316170161Sariff CHN_LOCK(c); 131765644Scg chn_trigger(c, PCMTRIG_ABORT); 1318170161Sariff CHN_UNLOCK(c); 1319170161Sariff } 1320170161Sariff while (chn_removefeeder(c) == 0) 1321170161Sariff ; 132270134Scg if (CHANNEL_FREE(c->methods, c->devinfo)) 132389770Scg sndbuf_free(b); 132474763Scg sndbuf_destroy(bs); 132574763Scg sndbuf_destroy(b); 1326170815Sariff CHN_LOCK(c); 1327170815Sariff c->flags |= CHN_F_DEAD; 132874763Scg chn_lockdestroy(c); 1329170815Sariff 1330170815Sariff return (0); 133165340Scg} 133265340Scg 1333193640Sariff/* XXX Obsolete. Use *_matrix() variant instead. */ 133465340Scgint 1335193640Sariffchn_setvolume(struct pcm_channel *c, int left, int right) 133650724Scg{ 1337193640Sariff int ret; 133855204Scg 1339193640Sariff ret = chn_setvolume_matrix(c, SND_VOL_C_MASTER, SND_CHN_T_FL, left); 1340193640Sariff ret |= chn_setvolume_matrix(c, SND_VOL_C_MASTER, SND_CHN_T_FR, 1341193640Sariff right) << 8; 1342193640Sariff 1343193640Sariff return (ret); 1344193640Sariff} 1345193640Sariff 1346193640Sariffint 1347193640Sariffchn_setvolume_multi(struct pcm_channel *c, int vc, int left, int right, 1348193640Sariff int center) 1349193640Sariff{ 1350193640Sariff int i, ret; 1351193640Sariff 1352193640Sariff ret = 0; 1353193640Sariff 1354193640Sariff for (i = 0; i < SND_CHN_T_MAX; i++) { 1355193640Sariff if ((1 << i) & SND_CHN_LEFT_MASK) 1356193640Sariff ret |= chn_setvolume_matrix(c, vc, i, left); 1357193640Sariff else if ((1 << i) & SND_CHN_RIGHT_MASK) 1358193640Sariff ret |= chn_setvolume_matrix(c, vc, i, right) << 8; 1359193640Sariff else 1360193640Sariff ret |= chn_setvolume_matrix(c, vc, i, center) << 16; 1361193640Sariff } 1362193640Sariff 1363193640Sariff return (ret); 1364193640Sariff} 1365193640Sariff 1366193640Sariffint 1367193640Sariffchn_setvolume_matrix(struct pcm_channel *c, int vc, int vt, int val) 1368193640Sariff{ 1369193640Sariff int i; 1370193640Sariff 1371193640Sariff KASSERT(c != NULL && vc >= SND_VOL_C_MASTER && vc < SND_VOL_C_MAX && 1372193640Sariff (vc == SND_VOL_C_MASTER || (vc & 1)) && 1373193640Sariff (vt == SND_CHN_T_VOL_0DB || (vt >= SND_CHN_T_BEGIN && 1374193640Sariff vt <= SND_CHN_T_END)) && (vt != SND_CHN_T_VOL_0DB || 1375193640Sariff (val >= SND_VOL_0DB_MIN && val <= SND_VOL_0DB_MAX)), 1376193640Sariff ("%s(): invalid volume matrix c=%p vc=%d vt=%d val=%d", 1377193640Sariff __func__, c, vc, vt, val)); 137874763Scg CHN_LOCKASSERT(c); 1379193640Sariff 1380193640Sariff if (val < 0) 1381193640Sariff val = 0; 1382193640Sariff if (val > 100) 1383193640Sariff val = 100; 1384193640Sariff 1385193640Sariff c->volume[vc][vt] = val; 1386193640Sariff 1387193640Sariff /* 1388193640Sariff * Do relative calculation here and store it into class + 1 1389193640Sariff * to ease the job of feeder_volume. 1390193640Sariff */ 1391193640Sariff if (vc == SND_VOL_C_MASTER) { 1392193640Sariff for (vc = SND_VOL_C_BEGIN; vc <= SND_VOL_C_END; 1393193640Sariff vc += SND_VOL_C_STEP) 1394193640Sariff c->volume[SND_VOL_C_VAL(vc)][vt] = 1395193640Sariff SND_VOL_CALC_VAL(c->volume, vc, vt); 1396193640Sariff } else if (vc & 1) { 1397193640Sariff if (vt == SND_CHN_T_VOL_0DB) 1398193640Sariff for (i = SND_CHN_T_BEGIN; i <= SND_CHN_T_END; 1399193640Sariff i += SND_CHN_T_STEP) { 1400193640Sariff c->volume[SND_VOL_C_VAL(vc)][i] = 1401193640Sariff SND_VOL_CALC_VAL(c->volume, vc, i); 1402193640Sariff } 1403193640Sariff else 1404193640Sariff c->volume[SND_VOL_C_VAL(vc)][vt] = 1405193640Sariff SND_VOL_CALC_VAL(c->volume, vc, vt); 1406193640Sariff } 1407193640Sariff 1408193640Sariff return (val); 140950724Scg} 141050724Scg 141150724Scgint 1412193640Sariffchn_getvolume_matrix(struct pcm_channel *c, int vc, int vt) 141350724Scg{ 1414193640Sariff KASSERT(c != NULL && vc >= SND_VOL_C_MASTER && vc < SND_VOL_C_MAX && 1415193640Sariff (vt == SND_CHN_T_VOL_0DB || 1416193640Sariff (vt >= SND_CHN_T_BEGIN && vt <= SND_CHN_T_END)), 1417193640Sariff ("%s(): invalid volume matrix c=%p vc=%d vt=%d", 1418193640Sariff __func__, c, vc, vt)); 141974763Scg CHN_LOCKASSERT(c); 1420193640Sariff 1421193640Sariff return (c->volume[vc][vt]); 142250724Scg} 142350724Scg 1424193640Sariffstruct pcmchan_matrix * 1425193640Sariffchn_getmatrix(struct pcm_channel *c) 1426193640Sariff{ 1427193640Sariff 1428193640Sariff KASSERT(c != NULL, ("%s(): NULL channel", __func__)); 1429193640Sariff CHN_LOCKASSERT(c); 1430193640Sariff 1431193640Sariff if (!(c->format & AFMT_CONVERTIBLE)) 1432193640Sariff return (NULL); 1433193640Sariff 1434193640Sariff return (&c->matrix); 1435193640Sariff} 1436193640Sariff 1437193640Sariffint 1438193640Sariffchn_setmatrix(struct pcm_channel *c, struct pcmchan_matrix *m) 1439193640Sariff{ 1440193640Sariff 1441193640Sariff KASSERT(c != NULL && m != NULL, 1442193640Sariff ("%s(): NULL channel or matrix", __func__)); 1443193640Sariff CHN_LOCKASSERT(c); 1444193640Sariff 1445193640Sariff if (!(c->format & AFMT_CONVERTIBLE)) 1446193640Sariff return (EINVAL); 1447193640Sariff 1448193640Sariff c->matrix = *m; 1449193640Sariff c->matrix.id = SND_CHN_MATRIX_PCMCHANNEL; 1450193640Sariff 1451193640Sariff return (chn_setformat(c, SND_FORMAT(c->format, m->channels, m->ext))); 1452193640Sariff} 1453193640Sariff 1454193640Sariff/* 1455193640Sariff * XXX chn_oss_* exists for the sake of compatibility. 1456193640Sariff */ 1457193640Sariffint 1458193640Sariffchn_oss_getorder(struct pcm_channel *c, unsigned long long *map) 1459193640Sariff{ 1460193640Sariff 1461193640Sariff KASSERT(c != NULL && map != NULL, 1462193640Sariff ("%s(): NULL channel or map", __func__)); 1463193640Sariff CHN_LOCKASSERT(c); 1464193640Sariff 1465193640Sariff if (!(c->format & AFMT_CONVERTIBLE)) 1466193640Sariff return (EINVAL); 1467193640Sariff 1468193640Sariff return (feeder_matrix_oss_get_channel_order(&c->matrix, map)); 1469193640Sariff} 1470193640Sariff 1471193640Sariffint 1472193640Sariffchn_oss_setorder(struct pcm_channel *c, unsigned long long *map) 1473193640Sariff{ 1474193640Sariff struct pcmchan_matrix m; 1475193640Sariff int ret; 1476193640Sariff 1477193640Sariff KASSERT(c != NULL && map != NULL, 1478193640Sariff ("%s(): NULL channel or map", __func__)); 1479193640Sariff CHN_LOCKASSERT(c); 1480193640Sariff 1481193640Sariff if (!(c->format & AFMT_CONVERTIBLE)) 1482193640Sariff return (EINVAL); 1483193640Sariff 1484193640Sariff m = c->matrix; 1485193640Sariff ret = feeder_matrix_oss_set_channel_order(&m, map); 1486193640Sariff if (ret != 0) 1487193640Sariff return (ret); 1488193640Sariff 1489193640Sariff return (chn_setmatrix(c, &m)); 1490193640Sariff} 1491193640Sariff 1492193640Sariff#define SND_CHN_OSS_FRONT (SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR) 1493193640Sariff#define SND_CHN_OSS_SURR (SND_CHN_T_MASK_SL | SND_CHN_T_MASK_SR) 1494193640Sariff#define SND_CHN_OSS_CENTER_LFE (SND_CHN_T_MASK_FC | SND_CHN_T_MASK_LF) 1495193640Sariff#define SND_CHN_OSS_REAR (SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR) 1496193640Sariff 1497193640Sariffint 1498193640Sariffchn_oss_getmask(struct pcm_channel *c, uint32_t *retmask) 1499193640Sariff{ 1500193640Sariff struct pcmchan_matrix *m; 1501193640Sariff struct pcmchan_caps *caps; 1502193640Sariff uint32_t i, format; 1503193640Sariff 1504193640Sariff KASSERT(c != NULL && retmask != NULL, 1505193640Sariff ("%s(): NULL channel or retmask", __func__)); 1506193640Sariff CHN_LOCKASSERT(c); 1507193640Sariff 1508193640Sariff caps = chn_getcaps(c); 1509193640Sariff if (caps == NULL || caps->fmtlist == NULL) 1510193640Sariff return (ENODEV); 1511193640Sariff 1512193640Sariff for (i = 0; caps->fmtlist[i] != 0; i++) { 1513193640Sariff format = caps->fmtlist[i]; 1514193640Sariff if (!(format & AFMT_CONVERTIBLE)) { 1515193640Sariff *retmask |= DSP_BIND_SPDIF; 1516193640Sariff continue; 1517193640Sariff } 1518193640Sariff m = CHANNEL_GETMATRIX(c->methods, c->devinfo, format); 1519193640Sariff if (m == NULL) 1520193640Sariff continue; 1521193640Sariff if (m->mask & SND_CHN_OSS_FRONT) 1522193640Sariff *retmask |= DSP_BIND_FRONT; 1523193640Sariff if (m->mask & SND_CHN_OSS_SURR) 1524193640Sariff *retmask |= DSP_BIND_SURR; 1525193640Sariff if (m->mask & SND_CHN_OSS_CENTER_LFE) 1526193640Sariff *retmask |= DSP_BIND_CENTER_LFE; 1527193640Sariff if (m->mask & SND_CHN_OSS_REAR) 1528193640Sariff *retmask |= DSP_BIND_REAR; 1529193640Sariff } 1530193640Sariff 1531193640Sariff /* report software-supported binding mask */ 1532193640Sariff if (!CHN_BITPERFECT(c) && report_soft_matrix) 1533193640Sariff *retmask |= DSP_BIND_FRONT | DSP_BIND_SURR | 1534193640Sariff DSP_BIND_CENTER_LFE | DSP_BIND_REAR; 1535193640Sariff 1536193640Sariff return (0); 1537193640Sariff} 1538193640Sariff 1539193640Sariffvoid 1540193640Sariffchn_vpc_reset(struct pcm_channel *c, int vc, int force) 1541193640Sariff{ 1542193640Sariff int i; 1543193640Sariff 1544193640Sariff KASSERT(c != NULL && vc >= SND_VOL_C_BEGIN && vc <= SND_VOL_C_END, 1545193640Sariff ("%s(): invalid reset c=%p vc=%d", __func__, c, vc)); 1546193640Sariff CHN_LOCKASSERT(c); 1547193640Sariff 1548193640Sariff if (force == 0 && chn_vpc_autoreset == 0) 1549193640Sariff return; 1550193640Sariff 1551193640Sariff for (i = SND_CHN_T_BEGIN; i <= SND_CHN_T_END; i += SND_CHN_T_STEP) 1552193640Sariff CHN_SETVOLUME(c, vc, i, c->volume[vc][SND_CHN_T_VOL_0DB]); 1553193640Sariff} 1554193640Sariff 1555164614Sariffstatic u_int32_t 1556164614Sariffround_pow2(u_int32_t v) 1557164614Sariff{ 1558164614Sariff u_int32_t ret; 1559164614Sariff 1560164614Sariff if (v < 2) 1561164614Sariff v = 2; 1562164614Sariff ret = 0; 1563164614Sariff while (v >> ret) 1564164614Sariff ret++; 1565164614Sariff ret = 1 << (ret - 1); 1566164614Sariff while (ret < v) 1567164614Sariff ret <<= 1; 1568164614Sariff return ret; 1569164614Sariff} 1570164614Sariff 1571167645Sariffstatic u_int32_t 1572167645Sariffround_blksz(u_int32_t v, int round) 1573167645Sariff{ 1574167645Sariff u_int32_t ret, tmp; 1575167645Sariff 1576167645Sariff if (round < 1) 1577167645Sariff round = 1; 1578167645Sariff 1579167645Sariff ret = min(round_pow2(v), CHN_2NDBUFMAXSIZE >> 1); 1580167645Sariff 1581167645Sariff if (ret > v && (ret >> 1) > 0 && (ret >> 1) >= ((v * 3) >> 2)) 1582167645Sariff ret >>= 1; 1583167645Sariff 1584167645Sariff tmp = ret - (ret % round); 1585167645Sariff while (tmp < 16 || tmp < round) { 1586167645Sariff ret <<= 1; 1587167645Sariff tmp = ret - (ret % round); 1588167645Sariff } 1589167645Sariff 1590167645Sariff return ret; 1591167645Sariff} 1592167645Sariff 1593164614Sariff/* 1594164614Sariff * 4Front call it DSP Policy, while we call it "Latency Profile". The idea 1595164614Sariff * is to keep 2nd buffer short so that it doesn't cause long queue during 1596164614Sariff * buffer transfer. 1597164614Sariff * 1598164614Sariff * Latency reference table for 48khz stereo 16bit: (PLAY) 1599164614Sariff * 1600164614Sariff * +---------+------------+-----------+------------+ 1601164614Sariff * | Latency | Blockcount | Blocksize | Buffersize | 1602164614Sariff * +---------+------------+-----------+------------+ 1603164614Sariff * | 0 | 2 | 64 | 128 | 1604164614Sariff * +---------+------------+-----------+------------+ 1605164614Sariff * | 1 | 4 | 128 | 512 | 1606164614Sariff * +---------+------------+-----------+------------+ 1607164614Sariff * | 2 | 8 | 512 | 4096 | 1608164614Sariff * +---------+------------+-----------+------------+ 1609164614Sariff * | 3 | 16 | 512 | 8192 | 1610164614Sariff * +---------+------------+-----------+------------+ 1611164614Sariff * | 4 | 32 | 512 | 16384 | 1612164614Sariff * +---------+------------+-----------+------------+ 1613164614Sariff * | 5 | 32 | 1024 | 32768 | 1614164614Sariff * +---------+------------+-----------+------------+ 1615164614Sariff * | 6 | 16 | 2048 | 32768 | 1616164614Sariff * +---------+------------+-----------+------------+ 1617164614Sariff * | 7 | 8 | 4096 | 32768 | 1618164614Sariff * +---------+------------+-----------+------------+ 1619164614Sariff * | 8 | 4 | 8192 | 32768 | 1620164614Sariff * +---------+------------+-----------+------------+ 1621164614Sariff * | 9 | 2 | 16384 | 32768 | 1622164614Sariff * +---------+------------+-----------+------------+ 1623164614Sariff * | 10 | 2 | 32768 | 65536 | 1624164614Sariff * +---------+------------+-----------+------------+ 1625164614Sariff * 1626164614Sariff * Recording need a different reference table. All we care is 1627164614Sariff * gobbling up everything within reasonable buffering threshold. 1628164614Sariff * 1629164614Sariff * Latency reference table for 48khz stereo 16bit: (REC) 1630164614Sariff * 1631164614Sariff * +---------+------------+-----------+------------+ 1632164614Sariff * | Latency | Blockcount | Blocksize | Buffersize | 1633164614Sariff * +---------+------------+-----------+------------+ 1634164614Sariff * | 0 | 512 | 32 | 16384 | 1635164614Sariff * +---------+------------+-----------+------------+ 1636164614Sariff * | 1 | 256 | 64 | 16384 | 1637164614Sariff * +---------+------------+-----------+------------+ 1638164614Sariff * | 2 | 128 | 128 | 16384 | 1639164614Sariff * +---------+------------+-----------+------------+ 1640164614Sariff * | 3 | 64 | 256 | 16384 | 1641164614Sariff * +---------+------------+-----------+------------+ 1642164614Sariff * | 4 | 32 | 512 | 16384 | 1643164614Sariff * +---------+------------+-----------+------------+ 1644164614Sariff * | 5 | 32 | 1024 | 32768 | 1645164614Sariff * +---------+------------+-----------+------------+ 1646164614Sariff * | 6 | 16 | 2048 | 32768 | 1647164614Sariff * +---------+------------+-----------+------------+ 1648164614Sariff * | 7 | 8 | 4096 | 32768 | 1649164614Sariff * +---------+------------+-----------+------------+ 1650164614Sariff * | 8 | 4 | 8192 | 32768 | 1651164614Sariff * +---------+------------+-----------+------------+ 1652164614Sariff * | 9 | 2 | 16384 | 32768 | 1653164614Sariff * +---------+------------+-----------+------------+ 1654164614Sariff * | 10 | 2 | 32768 | 65536 | 1655164614Sariff * +---------+------------+-----------+------------+ 1656164614Sariff * 1657164614Sariff * Calculations for other data rate are entirely based on these reference 1658164614Sariff * tables. For normal operation, Latency 5 seems give the best, well 1659164614Sariff * balanced performance for typical workload. Anything below 5 will 1660164614Sariff * eat up CPU to keep up with increasing context switches because of 1661164614Sariff * shorter buffer space and usually require the application to handle it 1662164614Sariff * aggresively through possibly real time programming technique. 1663164614Sariff * 1664164614Sariff */ 1665164614Sariff#define CHN_LATENCY_PBLKCNT_REF \ 1666164614Sariff {{1, 2, 3, 4, 5, 5, 4, 3, 2, 1, 1}, \ 1667164614Sariff {1, 2, 3, 4, 5, 5, 4, 3, 2, 1, 1}} 1668164614Sariff#define CHN_LATENCY_PBUFSZ_REF \ 1669164614Sariff {{7, 9, 12, 13, 14, 15, 15, 15, 15, 15, 16}, \ 1670164614Sariff {11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 17}} 1671164614Sariff 1672164614Sariff#define CHN_LATENCY_RBLKCNT_REF \ 1673164614Sariff {{9, 8, 7, 6, 5, 5, 4, 3, 2, 1, 1}, \ 1674164614Sariff {9, 8, 7, 6, 5, 5, 4, 3, 2, 1, 1}} 1675164614Sariff#define CHN_LATENCY_RBUFSZ_REF \ 1676164614Sariff {{14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 16}, \ 1677164614Sariff {15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 17}} 1678164614Sariff 1679164614Sariff#define CHN_LATENCY_DATA_REF 192000 /* 48khz stereo 16bit ~ 48000 x 2 x 2 */ 1680164614Sariff 168172457Scgstatic int 1682164614Sariffchn_calclatency(int dir, int latency, int bps, u_int32_t datarate, 1683164614Sariff u_int32_t max, int *rblksz, int *rblkcnt) 1684164614Sariff{ 1685167645Sariff static int pblkcnts[CHN_LATENCY_PROFILE_MAX + 1][CHN_LATENCY_MAX + 1] = 1686164614Sariff CHN_LATENCY_PBLKCNT_REF; 1687167645Sariff static int pbufszs[CHN_LATENCY_PROFILE_MAX + 1][CHN_LATENCY_MAX + 1] = 1688164614Sariff CHN_LATENCY_PBUFSZ_REF; 1689167645Sariff static int rblkcnts[CHN_LATENCY_PROFILE_MAX + 1][CHN_LATENCY_MAX + 1] = 1690164614Sariff CHN_LATENCY_RBLKCNT_REF; 1691167645Sariff static int rbufszs[CHN_LATENCY_PROFILE_MAX + 1][CHN_LATENCY_MAX + 1] = 1692164614Sariff CHN_LATENCY_RBUFSZ_REF; 1693167645Sariff u_int32_t bufsz; 1694167645Sariff int lprofile, blksz, blkcnt; 1695164614Sariff 1696167645Sariff if (latency < CHN_LATENCY_MIN || latency > CHN_LATENCY_MAX || 1697164614Sariff bps < 1 || datarate < 1 || 1698164614Sariff !(dir == PCMDIR_PLAY || dir == PCMDIR_REC)) { 1699164614Sariff if (rblksz != NULL) 1700164614Sariff *rblksz = CHN_2NDBUFMAXSIZE >> 1; 1701164614Sariff if (rblkcnt != NULL) 1702164614Sariff *rblkcnt = 2; 1703193640Sariff printf("%s(): FAILED dir=%d latency=%d bps=%d " 1704164614Sariff "datarate=%u max=%u\n", 1705164614Sariff __func__, dir, latency, bps, datarate, max); 1706164614Sariff return CHN_2NDBUFMAXSIZE; 1707164614Sariff } 1708164614Sariff 1709167645Sariff lprofile = chn_latency_profile; 1710167645Sariff 1711164614Sariff if (dir == PCMDIR_PLAY) { 1712167645Sariff blkcnt = pblkcnts[lprofile][latency]; 1713167645Sariff bufsz = pbufszs[lprofile][latency]; 1714164614Sariff } else { 1715167645Sariff blkcnt = rblkcnts[lprofile][latency]; 1716167645Sariff bufsz = rbufszs[lprofile][latency]; 1717164614Sariff } 1718167645Sariff 1719167645Sariff bufsz = round_pow2(snd_xbytes(1 << bufsz, CHN_LATENCY_DATA_REF, 1720167645Sariff datarate)); 1721164614Sariff if (bufsz > max) 1722164614Sariff bufsz = max; 1723167645Sariff blksz = round_blksz(bufsz >> blkcnt, bps); 1724167645Sariff 1725164614Sariff if (rblksz != NULL) 1726164614Sariff *rblksz = blksz; 1727164614Sariff if (rblkcnt != NULL) 1728164614Sariff *rblkcnt = 1 << blkcnt; 1729164614Sariff 1730164614Sariff return blksz << blkcnt; 1731164614Sariff} 1732164614Sariff 1733164614Sariffstatic int 1734164614Sariffchn_resizebuf(struct pcm_channel *c, int latency, 1735164614Sariff int blkcnt, int blksz) 1736164614Sariff{ 1737164614Sariff struct snd_dbuf *b, *bs, *pb; 1738230845Smav int sblksz, sblkcnt, hblksz, hblkcnt, limit = 0, nsblksz, nsblkcnt; 1739164614Sariff int ret; 1740164614Sariff 1741164614Sariff CHN_LOCKASSERT(c); 1742164614Sariff 1743193640Sariff if ((c->flags & (CHN_F_MMAP | CHN_F_TRIGGERED)) || 1744164614Sariff !(c->direction == PCMDIR_PLAY || c->direction == PCMDIR_REC)) 1745164614Sariff return EINVAL; 1746164614Sariff 1747164614Sariff if (latency == -1) { 1748164614Sariff c->latency = -1; 1749164614Sariff latency = chn_latency; 1750164614Sariff } else if (latency == -2) { 1751164614Sariff latency = c->latency; 1752164614Sariff if (latency < CHN_LATENCY_MIN || latency > CHN_LATENCY_MAX) 1753164614Sariff latency = chn_latency; 1754164614Sariff } else if (latency < CHN_LATENCY_MIN || latency > CHN_LATENCY_MAX) 1755164614Sariff return EINVAL; 1756164614Sariff else { 1757164614Sariff c->latency = latency; 1758164614Sariff } 1759164614Sariff 1760164614Sariff bs = c->bufsoft; 1761164614Sariff b = c->bufhard; 1762164614Sariff 1763164614Sariff if (!(blksz == 0 || blkcnt == -1) && 1764193640Sariff (blksz < 16 || blksz < sndbuf_getalign(bs) || blkcnt < 2 || 1765164614Sariff (blksz * blkcnt) > CHN_2NDBUFMAXSIZE)) 1766164614Sariff return EINVAL; 1767164614Sariff 1768193640Sariff chn_calclatency(c->direction, latency, sndbuf_getalign(bs), 1769193640Sariff sndbuf_getalign(bs) * sndbuf_getspd(bs), CHN_2NDBUFMAXSIZE, 1770164614Sariff &sblksz, &sblkcnt); 1771164614Sariff 1772164614Sariff if (blksz == 0 || blkcnt == -1) { 1773164614Sariff if (blkcnt == -1) 1774164614Sariff c->flags &= ~CHN_F_HAS_SIZE; 1775164614Sariff if (c->flags & CHN_F_HAS_SIZE) { 1776164614Sariff blksz = sndbuf_getblksz(bs); 1777164614Sariff blkcnt = sndbuf_getblkcnt(bs); 1778164614Sariff } 1779164614Sariff } else 1780164614Sariff c->flags |= CHN_F_HAS_SIZE; 1781164614Sariff 1782164614Sariff if (c->flags & CHN_F_HAS_SIZE) { 1783164614Sariff /* 1784164614Sariff * The application has requested their own blksz/blkcnt. 1785164614Sariff * Just obey with it, and let them toast alone. We can 1786164614Sariff * clamp it to the nearest latency profile, but that would 1787164614Sariff * defeat the purpose of having custom control. The least 1788164614Sariff * we can do is round it to the nearest ^2 and align it. 1789164614Sariff */ 1790193640Sariff sblksz = round_blksz(blksz, sndbuf_getalign(bs)); 1791167645Sariff sblkcnt = round_pow2(blkcnt); 1792164614Sariff } 1793164614Sariff 1794164614Sariff if (c->parentchannel != NULL) { 1795230845Smav pb = c->parentchannel->bufsoft; 1796164614Sariff CHN_UNLOCK(c); 1797170815Sariff CHN_LOCK(c->parentchannel); 1798164614Sariff chn_notify(c->parentchannel, CHN_N_BLOCKSIZE); 1799170815Sariff CHN_UNLOCK(c->parentchannel); 1800164614Sariff CHN_LOCK(c); 1801230845Smav if (c->direction == PCMDIR_PLAY) { 1802230845Smav limit = (pb != NULL) ? 1803230845Smav sndbuf_xbytes(sndbuf_getsize(pb), pb, bs) : 0; 1804230845Smav } else { 1805230845Smav limit = (pb != NULL) ? 1806230845Smav sndbuf_xbytes(sndbuf_getblksz(pb), pb, bs) * 2 : 0; 1807230845Smav } 1808164614Sariff } else { 1809167645Sariff hblkcnt = 2; 1810164614Sariff if (c->flags & CHN_F_HAS_SIZE) { 1811167645Sariff hblksz = round_blksz(sndbuf_xbytes(sblksz, bs, b), 1812193640Sariff sndbuf_getalign(b)); 1813167645Sariff hblkcnt = round_pow2(sndbuf_getblkcnt(bs)); 1814164614Sariff } else 1815164614Sariff chn_calclatency(c->direction, latency, 1816193640Sariff sndbuf_getalign(b), 1817193640Sariff sndbuf_getalign(b) * sndbuf_getspd(b), 1818167645Sariff CHN_2NDBUFMAXSIZE, &hblksz, &hblkcnt); 1819164614Sariff 1820164614Sariff if ((hblksz << 1) > sndbuf_getmaxsize(b)) 1821167645Sariff hblksz = round_blksz(sndbuf_getmaxsize(b) >> 1, 1822193640Sariff sndbuf_getalign(b)); 1823167645Sariff 1824167645Sariff while ((hblksz * hblkcnt) > sndbuf_getmaxsize(b)) { 1825167645Sariff if (hblkcnt < 4) 1826167645Sariff hblksz >>= 1; 1827167645Sariff else 1828167645Sariff hblkcnt >>= 1; 1829167645Sariff } 1830167645Sariff 1831193640Sariff hblksz -= hblksz % sndbuf_getalign(b); 1832164614Sariff 1833164614Sariff#if 0 1834164614Sariff hblksz = sndbuf_getmaxsize(b) >> 1; 1835193640Sariff hblksz -= hblksz % sndbuf_getalign(b); 1836167645Sariff hblkcnt = 2; 1837164614Sariff#endif 1838167645Sariff 1839164614Sariff CHN_UNLOCK(c); 1840167645Sariff if (chn_usefrags == 0 || 1841167645Sariff CHANNEL_SETFRAGMENTS(c->methods, c->devinfo, 1842193640Sariff hblksz, hblkcnt) != 0) 1843167645Sariff sndbuf_setblksz(b, CHANNEL_SETBLOCKSIZE(c->methods, 1844167645Sariff c->devinfo, hblksz)); 1845164614Sariff CHN_LOCK(c); 1846164614Sariff 1847170161Sariff if (!CHN_EMPTY(c, children)) { 1848230845Smav nsblksz = round_blksz( 1849230845Smav sndbuf_xbytes(sndbuf_getblksz(b), b, bs), 1850193640Sariff sndbuf_getalign(bs)); 1851230845Smav nsblkcnt = sndbuf_getblkcnt(b); 1852230845Smav if (c->direction == PCMDIR_PLAY) { 1853230845Smav do { 1854230845Smav nsblkcnt--; 1855230845Smav } while (nsblkcnt >= 2 && 1856230845Smav nsblksz * nsblkcnt >= sblksz * sblkcnt); 1857230845Smav nsblkcnt++; 1858230845Smav } 1859230845Smav sblksz = nsblksz; 1860230845Smav sblkcnt = nsblkcnt; 1861164614Sariff limit = 0; 1862230845Smav } else 1863230845Smav limit = sndbuf_xbytes(sndbuf_getblksz(b), b, bs) * 2; 1864164614Sariff } 1865164614Sariff 1866164614Sariff if (limit > CHN_2NDBUFMAXSIZE) 1867164614Sariff limit = CHN_2NDBUFMAXSIZE; 1868164614Sariff 1869167645Sariff#if 0 1870167645Sariff while (limit > 0 && (sblksz * sblkcnt) > limit) { 1871167645Sariff if (sblkcnt < 4) 1872164614Sariff break; 1873167645Sariff sblkcnt >>= 1; 1874164614Sariff } 1875167645Sariff#endif 1876164614Sariff 1877167645Sariff while ((sblksz * sblkcnt) < limit) 1878167645Sariff sblkcnt <<= 1; 1879167645Sariff 1880167645Sariff while ((sblksz * sblkcnt) > CHN_2NDBUFMAXSIZE) { 1881167645Sariff if (sblkcnt < 4) 1882167645Sariff sblksz >>= 1; 1883167645Sariff else 1884167645Sariff sblkcnt >>= 1; 1885167645Sariff } 1886167645Sariff 1887193640Sariff sblksz -= sblksz % sndbuf_getalign(bs); 1888167645Sariff 1889164614Sariff if (sndbuf_getblkcnt(bs) != sblkcnt || sndbuf_getblksz(bs) != sblksz || 1890164614Sariff sndbuf_getsize(bs) != (sblkcnt * sblksz)) { 1891164614Sariff ret = sndbuf_remalloc(bs, sblkcnt, sblksz); 1892164614Sariff if (ret != 0) { 1893193640Sariff device_printf(c->dev, "%s(): Failed: %d %d\n", 1894193640Sariff __func__, sblkcnt, sblksz); 1895164614Sariff return ret; 1896164614Sariff } 1897164614Sariff } 1898164614Sariff 1899164614Sariff /* 1900230845Smav * Interrupt timeout 1901230845Smav */ 1902230845Smav c->timeout = ((u_int64_t)hz * sndbuf_getsize(bs)) / 1903230845Smav ((u_int64_t)sndbuf_getspd(bs) * sndbuf_getalign(bs)); 1904230845Smav if (c->parentchannel != NULL) 1905230845Smav c->timeout = min(c->timeout, c->parentchannel->timeout); 1906230845Smav if (c->timeout < 1) 1907230845Smav c->timeout = 1; 1908230845Smav 1909230845Smav /* 1910164614Sariff * OSSv4 docs: "By default OSS will set the low water level equal 1911164614Sariff * to the fragment size which is optimal in most cases." 1912164614Sariff */ 1913164614Sariff c->lw = sndbuf_getblksz(bs); 1914164614Sariff chn_resetbuf(c); 1915164614Sariff 1916164614Sariff if (snd_verbose > 3) 1917193640Sariff device_printf(c->dev, "%s(): %s (%s) timeout=%u " 1918164614Sariff "b[%d/%d/%d] bs[%d/%d/%d] limit=%d\n", 1919167645Sariff __func__, CHN_DIRSTR(c), 1920164614Sariff (c->flags & CHN_F_VIRTUAL) ? "virtual" : "hardware", 1921164614Sariff c->timeout, 1922164614Sariff sndbuf_getsize(b), sndbuf_getblksz(b), 1923164614Sariff sndbuf_getblkcnt(b), 1924164614Sariff sndbuf_getsize(bs), sndbuf_getblksz(bs), 1925164614Sariff sndbuf_getblkcnt(bs), limit); 1926164614Sariff 1927164614Sariff return 0; 1928164614Sariff} 1929164614Sariff 1930164614Sariffint 1931164614Sariffchn_setlatency(struct pcm_channel *c, int latency) 1932164614Sariff{ 1933164614Sariff CHN_LOCKASSERT(c); 1934164614Sariff /* Destroy blksz/blkcnt, enforce latency profile. */ 1935164614Sariff return chn_resizebuf(c, latency, -1, 0); 1936164614Sariff} 1937164614Sariff 1938164614Sariffint 1939164614Sariffchn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz) 1940164614Sariff{ 1941164614Sariff CHN_LOCKASSERT(c); 1942164614Sariff /* Destroy latency profile, enforce blksz/blkcnt */ 1943164614Sariff return chn_resizebuf(c, -1, blkcnt, blksz); 1944164614Sariff} 1945164614Sariff 1946193640Sariffint 1947193640Sariffchn_setparam(struct pcm_channel *c, uint32_t format, uint32_t speed) 194850724Scg{ 1949193640Sariff struct pcmchan_caps *caps; 1950193640Sariff uint32_t hwspeed, delta; 1951193640Sariff int ret; 195266308Scg 195374763Scg CHN_LOCKASSERT(c); 195470291Scg 1955193640Sariff if (speed < 1 || format == 0 || CHN_STARTED(c)) 1956193640Sariff return (EINVAL); 195770291Scg 1958193640Sariff c->format = format; 1959193640Sariff c->speed = speed; 196070291Scg 1961193640Sariff caps = chn_getcaps(c); 196270291Scg 1963193640Sariff hwspeed = speed; 1964193640Sariff RANGE(hwspeed, caps->minspeed, caps->maxspeed); 196570291Scg 1966193640Sariff sndbuf_setspd(c->bufhard, CHANNEL_SETSPEED(c->methods, c->devinfo, 1967193640Sariff hwspeed)); 1968193640Sariff hwspeed = sndbuf_getspd(c->bufhard); 196970291Scg 1970193640Sariff delta = (hwspeed > speed) ? (hwspeed - speed) : (speed - hwspeed); 197170291Scg 1972193640Sariff if (delta <= feeder_rate_round) 1973193640Sariff c->speed = hwspeed; 1974193640Sariff 1975193640Sariff ret = feeder_chain(c); 1976193640Sariff 1977193640Sariff if (ret == 0) 1978193640Sariff ret = CHANNEL_SETFORMAT(c->methods, c->devinfo, 1979193640Sariff sndbuf_getfmt(c->bufhard)); 1980193640Sariff 1981193640Sariff if (ret == 0) 1982193640Sariff ret = chn_resizebuf(c, -2, 0, 0); 1983193640Sariff 1984193640Sariff return (ret); 198550724Scg} 198650724Scg 198750724Scgint 1988193640Sariffchn_setspeed(struct pcm_channel *c, uint32_t speed) 198950724Scg{ 1990193640Sariff uint32_t oldformat, oldspeed, format; 1991193640Sariff int ret; 199272457Scg 1993193640Sariff#if 0 1994193640Sariff /* XXX force 48k */ 1995193640Sariff if (c->format & AFMT_PASSTHROUGH) 1996193640Sariff speed = AFMT_PASSTHROUGH_RATE; 1997193640Sariff#endif 1998193640Sariff 1999193640Sariff oldformat = c->format; 2000193640Sariff oldspeed = c->speed; 2001193640Sariff format = oldformat; 2002193640Sariff 2003193640Sariff ret = chn_setparam(c, format, speed); 2004193640Sariff if (ret != 0) { 2005164614Sariff if (snd_verbose > 3) 2006193640Sariff device_printf(c->dev, 2007193640Sariff "%s(): Setting speed %d failed, " 2008193640Sariff "falling back to %d\n", 2009193640Sariff __func__, speed, oldspeed); 2010193640Sariff chn_setparam(c, c->format, oldspeed); 201172457Scg } 2012193640Sariff 2013193640Sariff return (ret); 201472457Scg} 201572457Scg 2016193640Sariffint 2017193640Sariffchn_setformat(struct pcm_channel *c, uint32_t format) 201872457Scg{ 2019193640Sariff uint32_t oldformat, oldspeed, speed; 2020193640Sariff int ret; 202160961Scg 2022193640Sariff /* XXX force stereo */ 2023230537Smav if ((format & AFMT_PASSTHROUGH) && AFMT_CHANNEL(format) < 2) { 2024193640Sariff format = SND_FORMAT(format, AFMT_PASSTHROUGH_CHANNEL, 2025193640Sariff AFMT_PASSTHROUGH_EXTCHANNEL); 2026230537Smav } 2027193640Sariff 2028193640Sariff oldformat = c->format; 2029193640Sariff oldspeed = c->speed; 2030193640Sariff speed = oldspeed; 2031193640Sariff 2032193640Sariff ret = chn_setparam(c, format, speed); 2033193640Sariff if (ret != 0) { 2034193640Sariff if (snd_verbose > 3) 2035193640Sariff device_printf(c->dev, 2036193640Sariff "%s(): Format change 0x%08x failed, " 2037193640Sariff "falling back to 0x%08x\n", 2038193640Sariff __func__, format, oldformat); 2039193640Sariff chn_setparam(c, oldformat, oldspeed); 2040193640Sariff } 2041193640Sariff 2042193640Sariff return (ret); 204350724Scg} 204450724Scg 2045193640Sariffvoid 2046193640Sariffchn_syncstate(struct pcm_channel *c) 204772457Scg{ 2048193640Sariff struct snddev_info *d; 2049193640Sariff struct snd_mixer *m; 205072457Scg 2051193640Sariff d = (c != NULL) ? c->parentsnddev : NULL; 2052193640Sariff m = (d != NULL && d->mixer_dev != NULL) ? d->mixer_dev->si_drv1 : 2053193640Sariff NULL; 2054193640Sariff 2055193640Sariff if (d == NULL || m == NULL) 2056193640Sariff return; 2057193640Sariff 2058193640Sariff CHN_LOCKASSERT(c); 2059193640Sariff 2060193640Sariff if (c->feederflags & (1 << FEEDER_VOLUME)) { 2061193640Sariff uint32_t parent; 2062193640Sariff int vol, pvol, left, right, center; 2063193640Sariff 2064193640Sariff if (c->direction == PCMDIR_PLAY && 2065193640Sariff (d->flags & SD_F_SOFTPCMVOL)) { 2066193640Sariff /* CHN_UNLOCK(c); */ 2067193640Sariff vol = mix_get(m, SOUND_MIXER_PCM); 2068193640Sariff parent = mix_getparent(m, SOUND_MIXER_PCM); 2069193640Sariff if (parent != SOUND_MIXER_NONE) 2070193640Sariff pvol = mix_get(m, parent); 2071193640Sariff else 2072193640Sariff pvol = 100 | (100 << 8); 2073193640Sariff /* CHN_LOCK(c); */ 2074193640Sariff } else { 2075193640Sariff vol = 100 | (100 << 8); 2076193640Sariff pvol = vol; 2077193640Sariff } 2078193640Sariff 2079193640Sariff if (vol == -1) { 2080193640Sariff device_printf(c->dev, 2081193640Sariff "Soft PCM Volume: Failed to read pcm " 2082193640Sariff "default value\n"); 2083193640Sariff vol = 100 | (100 << 8); 2084193640Sariff } 2085193640Sariff 2086193640Sariff if (pvol == -1) { 2087193640Sariff device_printf(c->dev, 2088193640Sariff "Soft PCM Volume: Failed to read parent " 2089193640Sariff "default value\n"); 2090193640Sariff pvol = 100 | (100 << 8); 2091193640Sariff } 2092193640Sariff 2093193640Sariff left = ((vol & 0x7f) * (pvol & 0x7f)) / 100; 2094193640Sariff right = (((vol >> 8) & 0x7f) * ((pvol >> 8) & 0x7f)) / 100; 2095193640Sariff center = (left + right) >> 1; 2096193640Sariff 2097193640Sariff chn_setvolume_multi(c, SND_VOL_C_MASTER, left, right, center); 209872457Scg } 2099193640Sariff 2100193640Sariff if (c->feederflags & (1 << FEEDER_EQ)) { 2101193640Sariff struct pcm_feeder *f; 2102193640Sariff int treble, bass, state; 2103193640Sariff 2104193640Sariff /* CHN_UNLOCK(c); */ 2105193640Sariff treble = mix_get(m, SOUND_MIXER_TREBLE); 2106193640Sariff bass = mix_get(m, SOUND_MIXER_BASS); 2107193640Sariff /* CHN_LOCK(c); */ 2108193640Sariff 2109193640Sariff if (treble == -1) 2110193640Sariff treble = 50; 2111193640Sariff else 2112193640Sariff treble = ((treble & 0x7f) + 2113193640Sariff ((treble >> 8) & 0x7f)) >> 1; 2114193640Sariff 2115193640Sariff if (bass == -1) 2116193640Sariff bass = 50; 2117193640Sariff else 2118193640Sariff bass = ((bass & 0x7f) + ((bass >> 8) & 0x7f)) >> 1; 2119193640Sariff 2120193640Sariff f = chn_findfeeder(c, FEEDER_EQ); 2121193640Sariff if (f != NULL) { 2122193640Sariff if (FEEDER_SET(f, FEEDEQ_TREBLE, treble) != 0) 2123193640Sariff device_printf(c->dev, 2124193640Sariff "EQ: Failed to set treble -- %d\n", 2125193640Sariff treble); 2126193640Sariff if (FEEDER_SET(f, FEEDEQ_BASS, bass) != 0) 2127193640Sariff device_printf(c->dev, 2128193640Sariff "EQ: Failed to set bass -- %d\n", 2129193640Sariff bass); 2130193640Sariff if (FEEDER_SET(f, FEEDEQ_PREAMP, d->eqpreamp) != 0) 2131193640Sariff device_printf(c->dev, 2132193640Sariff "EQ: Failed to set preamp -- %d\n", 2133193640Sariff d->eqpreamp); 2134193640Sariff if (d->flags & SD_F_EQ_BYPASSED) 2135193640Sariff state = FEEDEQ_BYPASS; 2136193640Sariff else if (d->flags & SD_F_EQ_ENABLED) 2137193640Sariff state = FEEDEQ_ENABLE; 2138193640Sariff else 2139193640Sariff state = FEEDEQ_DISABLE; 2140193640Sariff if (FEEDER_SET(f, FEEDEQ_STATE, state) != 0) 2141193640Sariff device_printf(c->dev, 2142193640Sariff "EQ: Failed to set state -- %d\n", state); 2143193640Sariff } 2144193640Sariff } 214572457Scg} 214672457Scg 214772457Scgint 214874763Scgchn_trigger(struct pcm_channel *c, int go) 214950724Scg{ 2150147274Smarius#ifdef DEV_ISA 215174763Scg struct snd_dbuf *b = c->bufhard; 2152147274Smarius#endif 2153170161Sariff struct snddev_info *d = c->parentsnddev; 215474763Scg int ret; 215574763Scg 215674763Scg CHN_LOCKASSERT(c); 2157147274Smarius#ifdef DEV_ISA 2158110499Snyan if (SND_DMA(b) && (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD)) 2159110499Snyan sndbuf_dmabounce(b); 2160147274Smarius#endif 2161170815Sariff if (!PCMTRIG_COMMON(go)) 2162170815Sariff return (CHANNEL_TRIGGER(c->methods, c->devinfo, go)); 2163170815Sariff 2164170815Sariff if (go == c->trigger) 2165170521Sariff return (0); 2166170161Sariff 216774763Scg ret = CHANNEL_TRIGGER(c->methods, c->devinfo, go); 2168170815Sariff if (ret != 0) 2169170815Sariff return (ret); 217074763Scg 2171170815Sariff switch (go) { 2172170815Sariff case PCMTRIG_START: 2173170815Sariff if (snd_verbose > 3) 2174170815Sariff device_printf(c->dev, 2175170815Sariff "%s() %s: calling go=0x%08x , " 2176170815Sariff "prev=0x%08x\n", __func__, c->name, go, 2177170815Sariff c->trigger); 2178170815Sariff if (c->trigger != PCMTRIG_START) { 2179170815Sariff c->trigger = go; 2180170815Sariff CHN_UNLOCK(c); 2181193640Sariff PCM_LOCK(d); 2182170815Sariff CHN_INSERT_HEAD(d, c, channels.pcm.busy); 2183193640Sariff PCM_UNLOCK(d); 2184170815Sariff CHN_LOCK(c); 2185193640Sariff chn_syncstate(c); 2186170161Sariff } 2187170815Sariff break; 2188170815Sariff case PCMTRIG_STOP: 2189170815Sariff case PCMTRIG_ABORT: 2190170815Sariff if (snd_verbose > 3) 2191170815Sariff device_printf(c->dev, 2192170815Sariff "%s() %s: calling go=0x%08x , " 2193170815Sariff "prev=0x%08x\n", __func__, c->name, go, 2194170815Sariff c->trigger); 2195170815Sariff if (c->trigger == PCMTRIG_START) { 2196170815Sariff c->trigger = go; 2197170815Sariff CHN_UNLOCK(c); 2198193640Sariff PCM_LOCK(d); 2199170815Sariff CHN_REMOVE(d, c, channels.pcm.busy); 2200193640Sariff PCM_UNLOCK(d); 2201170815Sariff CHN_LOCK(c); 2202170815Sariff } 2203170815Sariff break; 2204170815Sariff default: 2205170815Sariff break; 2206170161Sariff } 2207170161Sariff 2208170815Sariff return (0); 220950724Scg} 221050724Scg 2211162588Snetchild/** 2212162588Snetchild * @brief Queries sound driver for sample-aligned hardware buffer pointer index 2213162588Snetchild * 2214162588Snetchild * This function obtains the hardware pointer location, then aligns it to 2215162588Snetchild * the current bytes-per-sample value before returning. (E.g., a channel 2216162588Snetchild * running in 16 bit stereo mode would require 4 bytes per sample, so a 2217162588Snetchild * hwptr value ranging from 32-35 would be returned as 32.) 2218162588Snetchild * 2219162588Snetchild * @param c PCM channel context 2220162588Snetchild * @returns sample-aligned hardware buffer pointer index 2221162588Snetchild */ 222250724Scgint 222374763Scgchn_getptr(struct pcm_channel *c) 222450724Scg{ 222554155Scg int hwptr; 222654155Scg 222774763Scg CHN_LOCKASSERT(c); 2228167645Sariff hwptr = (CHN_STARTED(c)) ? CHANNEL_GETPTR(c->methods, c->devinfo) : 0; 2229193640Sariff return (hwptr - (hwptr % sndbuf_getalign(c->bufhard))); 223050724Scg} 223150724Scg 223274763Scgstruct pcmchan_caps * 223374763Scgchn_getcaps(struct pcm_channel *c) 223450724Scg{ 223574763Scg CHN_LOCKASSERT(c); 223670134Scg return CHANNEL_GETCAPS(c->methods, c->devinfo); 223750724Scg} 223864881Scg 223964881Scgu_int32_t 224074763Scgchn_getformats(struct pcm_channel *c) 224164881Scg{ 224264881Scg u_int32_t *fmtlist, fmts; 224364881Scg int i; 224464881Scg 224564881Scg fmtlist = chn_getcaps(c)->fmtlist; 224664881Scg fmts = 0; 224764881Scg for (i = 0; fmtlist[i]; i++) 224864881Scg fmts |= fmtlist[i]; 224964881Scg 225091186Sdes /* report software-supported formats */ 2251193640Sariff if (!CHN_BITPERFECT(c) && report_soft_formats) 2252193640Sariff fmts |= AFMT_CONVERTIBLE; 225391186Sdes 2254193640Sariff return (AFMT_ENCODING(fmts)); 225564881Scg} 225664881Scg 225777269Scgint 225877269Scgchn_notify(struct pcm_channel *c, u_int32_t flags) 225977269Scg{ 2260193640Sariff struct pcm_channel *ch; 2261193640Sariff struct pcmchan_caps *caps; 2262193640Sariff uint32_t bestformat, bestspeed, besthwformat, *vchanformat, *vchanrate; 2263193640Sariff uint32_t vpflags; 2264193640Sariff int dirty, err, run, nrun; 226577269Scg 2266170815Sariff CHN_LOCKASSERT(c); 2267125136Struckman 2268170815Sariff if (CHN_EMPTY(c, children)) 2269170815Sariff return (ENODEV); 227077269Scg 2271170815Sariff err = 0; 2272170815Sariff 227377882Scg /* 2274170815Sariff * If the hwchan is running, we can't change its rate, format or 227577882Scg * blocksize 227677882Scg */ 2277170815Sariff run = (CHN_STARTED(c)) ? 1 : 0; 227877882Scg if (run) 227977882Scg flags &= CHN_N_VOLUME | CHN_N_TRIGGER; 228077882Scg 228177269Scg if (flags & CHN_N_RATE) { 2282193640Sariff /* 2283193640Sariff * XXX I'll make good use of this someday. 2284193640Sariff * However this is currently being superseded by 2285193640Sariff * the availability of CHN_F_VCHAN_DYNAMIC. 2286193640Sariff */ 228777269Scg } 2288193640Sariff 228977269Scg if (flags & CHN_N_FORMAT) { 2290193640Sariff /* 2291193640Sariff * XXX I'll make good use of this someday. 2292193640Sariff * However this is currently being superseded by 2293193640Sariff * the availability of CHN_F_VCHAN_DYNAMIC. 2294193640Sariff */ 229577269Scg } 2296193640Sariff 229777269Scg if (flags & CHN_N_VOLUME) { 2298193640Sariff /* 2299193640Sariff * XXX I'll make good use of this someday, though 2300193640Sariff * soft volume control is currently pretty much 2301193640Sariff * integrated. 2302193640Sariff */ 230377269Scg } 2304193640Sariff 230577269Scg if (flags & CHN_N_BLOCKSIZE) { 230677269Scg /* 2307164614Sariff * Set to default latency profile 230877269Scg */ 2309164614Sariff chn_setlatency(c, chn_latency); 231077269Scg } 2311193640Sariff 2312193640Sariff if ((flags & CHN_N_TRIGGER) && !(c->flags & CHN_F_VCHAN_DYNAMIC)) { 2313170161Sariff nrun = CHN_EMPTY(c, children.busy) ? 0 : 1; 231477882Scg if (nrun && !run) 2315170815Sariff err = chn_start(c, 1); 231677882Scg if (!nrun && run) 231777269Scg chn_abort(c); 2318193640Sariff flags &= ~CHN_N_TRIGGER; 231977269Scg } 2320170815Sariff 2321193640Sariff if (flags & CHN_N_TRIGGER) { 2322193640Sariff if (c->direction == PCMDIR_PLAY) { 2323193640Sariff vchanformat = &c->parentsnddev->pvchanformat; 2324193640Sariff vchanrate = &c->parentsnddev->pvchanrate; 2325193640Sariff } else { 2326193640Sariff vchanformat = &c->parentsnddev->rvchanformat; 2327193640Sariff vchanrate = &c->parentsnddev->rvchanrate; 2328193640Sariff } 2329193640Sariff 2330193640Sariff /* Dynamic Virtual Channel */ 2331193640Sariff if (!(c->flags & CHN_F_VCHAN_ADAPTIVE)) { 2332193640Sariff bestformat = *vchanformat; 2333193640Sariff bestspeed = *vchanrate; 2334193640Sariff } else { 2335193640Sariff bestformat = 0; 2336193640Sariff bestspeed = 0; 2337193640Sariff } 2338193640Sariff 2339193640Sariff besthwformat = 0; 2340193640Sariff nrun = 0; 2341193640Sariff caps = chn_getcaps(c); 2342193640Sariff dirty = 0; 2343193640Sariff vpflags = 0; 2344193640Sariff 2345193640Sariff CHN_FOREACH(ch, c, children.busy) { 2346193640Sariff CHN_LOCK(ch); 2347193640Sariff if ((ch->format & AFMT_PASSTHROUGH) && 2348193640Sariff snd_fmtvalid(ch->format, caps->fmtlist)) { 2349193640Sariff bestformat = ch->format; 2350193640Sariff bestspeed = ch->speed; 2351193640Sariff CHN_UNLOCK(ch); 2352193640Sariff vpflags = CHN_F_PASSTHROUGH; 2353193640Sariff nrun++; 2354193640Sariff break; 2355193640Sariff } 2356193640Sariff if ((ch->flags & CHN_F_EXCLUSIVE) && vpflags == 0) { 2357193640Sariff if (c->flags & CHN_F_VCHAN_ADAPTIVE) { 2358193640Sariff bestspeed = ch->speed; 2359193640Sariff RANGE(bestspeed, caps->minspeed, 2360193640Sariff caps->maxspeed); 2361193640Sariff besthwformat = snd_fmtbest(ch->format, 2362193640Sariff caps->fmtlist); 2363193640Sariff if (besthwformat != 0) 2364193640Sariff bestformat = besthwformat; 2365193640Sariff } 2366193640Sariff CHN_UNLOCK(ch); 2367193640Sariff vpflags = CHN_F_EXCLUSIVE; 2368193640Sariff nrun++; 2369193640Sariff continue; 2370193640Sariff } 2371193640Sariff if (!(c->flags & CHN_F_VCHAN_ADAPTIVE) || 2372193640Sariff vpflags != 0) { 2373193640Sariff CHN_UNLOCK(ch); 2374193640Sariff nrun++; 2375193640Sariff continue; 2376193640Sariff } 2377193640Sariff if (ch->speed > bestspeed) { 2378193640Sariff bestspeed = ch->speed; 2379193640Sariff RANGE(bestspeed, caps->minspeed, 2380193640Sariff caps->maxspeed); 2381193640Sariff } 2382193640Sariff besthwformat = snd_fmtbest(ch->format, caps->fmtlist); 2383193640Sariff if (!(besthwformat & AFMT_VCHAN)) { 2384193640Sariff CHN_UNLOCK(ch); 2385193640Sariff nrun++; 2386193640Sariff continue; 2387193640Sariff } 2388193640Sariff if (AFMT_CHANNEL(besthwformat) > 2389193640Sariff AFMT_CHANNEL(bestformat)) 2390193640Sariff bestformat = besthwformat; 2391193640Sariff else if (AFMT_CHANNEL(besthwformat) == 2392193640Sariff AFMT_CHANNEL(bestformat) && 2393193640Sariff AFMT_BIT(besthwformat) > AFMT_BIT(bestformat)) 2394193640Sariff bestformat = besthwformat; 2395193640Sariff CHN_UNLOCK(ch); 2396193640Sariff nrun++; 2397193640Sariff } 2398193640Sariff 2399193640Sariff if (bestformat == 0) 2400193640Sariff bestformat = c->format; 2401193640Sariff if (bestspeed == 0) 2402193640Sariff bestspeed = c->speed; 2403193640Sariff 2404193640Sariff if (bestformat != c->format || bestspeed != c->speed) 2405193640Sariff dirty = 1; 2406193640Sariff 2407193640Sariff c->flags &= ~(CHN_F_PASSTHROUGH | CHN_F_EXCLUSIVE); 2408193640Sariff c->flags |= vpflags; 2409193640Sariff 2410193640Sariff if (nrun && !run) { 2411193640Sariff if (dirty) { 2412193640Sariff bestspeed = CHANNEL_SETSPEED(c->methods, 2413193640Sariff c->devinfo, bestspeed); 2414193640Sariff err = chn_reset(c, bestformat, bestspeed); 2415193640Sariff } 2416193640Sariff if (err == 0 && dirty) { 2417193640Sariff CHN_FOREACH(ch, c, children.busy) { 2418193640Sariff CHN_LOCK(ch); 2419193640Sariff if (VCHAN_SYNC_REQUIRED(ch)) 2420193640Sariff vchan_sync(ch); 2421193640Sariff CHN_UNLOCK(ch); 2422193640Sariff } 2423193640Sariff } 2424193640Sariff if (err == 0) { 2425193640Sariff if (dirty) 2426193640Sariff c->flags |= CHN_F_DIRTY; 2427193640Sariff err = chn_start(c, 1); 2428193640Sariff } 2429193640Sariff } 2430193640Sariff 2431193640Sariff if (nrun && run && dirty) { 2432193640Sariff chn_abort(c); 2433193640Sariff bestspeed = CHANNEL_SETSPEED(c->methods, c->devinfo, 2434193640Sariff bestspeed); 2435193640Sariff err = chn_reset(c, bestformat, bestspeed); 2436193640Sariff if (err == 0) { 2437193640Sariff CHN_FOREACH(ch, c, children.busy) { 2438193640Sariff CHN_LOCK(ch); 2439193640Sariff if (VCHAN_SYNC_REQUIRED(ch)) 2440193640Sariff vchan_sync(ch); 2441193640Sariff CHN_UNLOCK(ch); 2442193640Sariff } 2443193640Sariff } 2444193640Sariff if (err == 0) { 2445193640Sariff c->flags |= CHN_F_DIRTY; 2446193640Sariff err = chn_start(c, 1); 2447193640Sariff } 2448193640Sariff } 2449193640Sariff 2450193640Sariff if (err == 0 && !(bestformat & AFMT_PASSTHROUGH) && 2451193640Sariff (bestformat & AFMT_VCHAN)) { 2452193640Sariff *vchanformat = bestformat; 2453193640Sariff *vchanrate = bestspeed; 2454193640Sariff } 2455193640Sariff 2456193640Sariff if (!nrun && run) { 2457193640Sariff c->flags &= ~(CHN_F_PASSTHROUGH | CHN_F_EXCLUSIVE); 2458193640Sariff bestformat = *vchanformat; 2459193640Sariff bestspeed = *vchanrate; 2460193640Sariff chn_abort(c); 2461193640Sariff if (c->format != bestformat || c->speed != bestspeed) 2462193640Sariff chn_reset(c, bestformat, bestspeed); 2463193640Sariff } 2464193640Sariff } 2465193640Sariff 2466170815Sariff return (err); 246777269Scg} 2468125136Struckman 2469162588Snetchild/** 2470162588Snetchild * @brief Fetch array of supported discrete sample rates 2471162588Snetchild * 2472162588Snetchild * Wrapper for CHANNEL_GETRATES. Please see channel_if.m:getrates() for 2473162588Snetchild * detailed information. 2474162588Snetchild * 2475162588Snetchild * @note If the operation isn't supported, this function will just return 0 2476162588Snetchild * (no rates in the array), and *rates will be set to NULL. Callers 2477162588Snetchild * should examine rates @b only if this function returns non-zero. 2478162588Snetchild * 2479162588Snetchild * @param c pcm channel to examine 2480162588Snetchild * @param rates pointer to array of integers; rate table will be recorded here 2481162588Snetchild * 2482162588Snetchild * @return number of rates in the array pointed to be @c rates 2483162588Snetchild */ 2484162588Snetchildint 2485162588Snetchildchn_getrates(struct pcm_channel *c, int **rates) 2486162588Snetchild{ 2487162588Snetchild KASSERT(rates != NULL, ("rates is null")); 2488162588Snetchild CHN_LOCKASSERT(c); 2489162588Snetchild return CHANNEL_GETRATES(c->methods, c->devinfo, rates); 2490162588Snetchild} 2491162588Snetchild 2492162588Snetchild/** 2493162588Snetchild * @brief Remove channel from a sync group, if there is one. 2494162588Snetchild * 2495162588Snetchild * This function is initially intended for the following conditions: 2496162588Snetchild * - Starting a syncgroup (@c SNDCTL_DSP_SYNCSTART ioctl) 2497162588Snetchild * - Closing a device. (A channel can't be destroyed if it's still in use.) 2498162588Snetchild * 2499162588Snetchild * @note Before calling this function, the syncgroup list mutex must be 2500162588Snetchild * held. (Consider pcm_channel::sm protected by the SG list mutex 2501162588Snetchild * whether @c c is locked or not.) 2502162588Snetchild * 2503162588Snetchild * @param c channel device to be started or closed 2504162588Snetchild * @returns If this channel was the only member of a group, the group ID 2505162588Snetchild * is returned to the caller so that the caller can release it 2506162588Snetchild * via free_unr() after giving up the syncgroup lock. Else it 2507162588Snetchild * returns 0. 2508162588Snetchild */ 2509162588Snetchildint 2510162588Snetchildchn_syncdestroy(struct pcm_channel *c) 2511162588Snetchild{ 2512162588Snetchild struct pcmchan_syncmember *sm; 2513162588Snetchild struct pcmchan_syncgroup *sg; 2514162588Snetchild int sg_id; 2515162588Snetchild 2516162588Snetchild sg_id = 0; 2517162588Snetchild 2518162588Snetchild PCM_SG_LOCKASSERT(MA_OWNED); 2519162588Snetchild 2520162588Snetchild if (c->sm != NULL) { 2521162588Snetchild sm = c->sm; 2522162588Snetchild sg = sm->parent; 2523162588Snetchild c->sm = NULL; 2524162588Snetchild 2525162588Snetchild KASSERT(sg != NULL, ("syncmember has null parent")); 2526162588Snetchild 2527162588Snetchild SLIST_REMOVE(&sg->members, sm, pcmchan_syncmember, link); 2528162588Snetchild free(sm, M_DEVBUF); 2529162588Snetchild 2530162588Snetchild if (SLIST_EMPTY(&sg->members)) { 2531162588Snetchild SLIST_REMOVE(&snd_pcm_syncgroups, sg, pcmchan_syncgroup, link); 2532162588Snetchild sg_id = sg->id; 2533162588Snetchild free(sg, M_DEVBUF); 2534162588Snetchild } 2535162588Snetchild } 2536162588Snetchild 2537162588Snetchild return sg_id; 2538162588Snetchild} 2539162588Snetchild 2540162588Snetchild#ifdef OSSV4_EXPERIMENT 2541162588Snetchildint 2542162588Snetchildchn_getpeaks(struct pcm_channel *c, int *lpeak, int *rpeak) 2543162588Snetchild{ 2544162588Snetchild CHN_LOCKASSERT(c); 2545162588Snetchild return CHANNEL_GETPEAKS(c->methods, c->devinfo, lpeak, rpeak); 2546162588Snetchild} 2547162588Snetchild#endif 2548