1098ca2bdSWarner Losh /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni *
490da2b28SAriff Abdullah * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org>
590da2b28SAriff Abdullah * Portions Copyright (c) Ryan Beasley <ryan.beasley@gmail.com> - GSoC 2006
690da2b28SAriff Abdullah * Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org>
790da2b28SAriff Abdullah * Portions Copyright (c) Luigi Rizzo <luigi@FreeBSD.org> - 1997-99
8987e5972SCameron Grant * All rights reserved.
9c824383bSChristos Margiolis * Copyright (c) 2024-2025 The FreeBSD Foundation
10c824383bSChristos Margiolis *
11c824383bSChristos Margiolis * Portions of this software were developed by Christos Margiolis
12c824383bSChristos Margiolis * <christos@FreeBSD.org> under sponsorship from the FreeBSD Foundation.
13987e5972SCameron Grant *
14987e5972SCameron Grant * Redistribution and use in source and binary forms, with or without
15987e5972SCameron Grant * modification, are permitted provided that the following conditions
16987e5972SCameron Grant * are met:
17987e5972SCameron Grant * 1. Redistributions of source code must retain the above copyright
18987e5972SCameron Grant * notice, this list of conditions and the following disclaimer.
19987e5972SCameron Grant * 2. Redistributions in binary form must reproduce the above copyright
20987e5972SCameron Grant * notice, this list of conditions and the following disclaimer in the
21987e5972SCameron Grant * documentation and/or other materials provided with the distribution.
22987e5972SCameron Grant *
23987e5972SCameron Grant * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
24987e5972SCameron Grant * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25987e5972SCameron Grant * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26987e5972SCameron Grant * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
27987e5972SCameron Grant * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28987e5972SCameron Grant * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29987e5972SCameron Grant * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30987e5972SCameron Grant * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31987e5972SCameron Grant * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32987e5972SCameron Grant * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33987e5972SCameron Grant * SUCH DAMAGE.
34987e5972SCameron Grant */
35987e5972SCameron Grant
3690da2b28SAriff Abdullah #ifdef HAVE_KERNEL_OPTION_HEADERS
3790da2b28SAriff Abdullah #include "opt_snd.h"
3890da2b28SAriff Abdullah #endif
3990da2b28SAriff Abdullah
40ef9308b1SCameron Grant #include <dev/sound/pcm/sound.h>
4190da2b28SAriff Abdullah #include <dev/sound/pcm/vchan.h>
42987e5972SCameron Grant
430f55ac6cSCameron Grant #include "feeder_if.h"
440f55ac6cSCameron Grant
45a580b31aSAriff Abdullah int report_soft_formats = 1;
46a580b31aSAriff Abdullah SYSCTL_INT(_hw_snd, OID_AUTO, report_soft_formats, CTLFLAG_RW,
4732a0e5d5SHans Petter Selasky &report_soft_formats, 0, "report software-emulated formats");
48a580b31aSAriff Abdullah
4990da2b28SAriff Abdullah int report_soft_matrix = 1;
5090da2b28SAriff Abdullah SYSCTL_INT(_hw_snd, OID_AUTO, report_soft_matrix, CTLFLAG_RW,
5132a0e5d5SHans Petter Selasky &report_soft_matrix, 0, "report software-emulated channel matrixing");
5290da2b28SAriff Abdullah
53a580b31aSAriff Abdullah int chn_latency = CHN_LATENCY_DEFAULT;
54a3a1ce30SCameron Grant
55a3a1ce30SCameron Grant static int
sysctl_hw_snd_latency(SYSCTL_HANDLER_ARGS)56a580b31aSAriff Abdullah sysctl_hw_snd_latency(SYSCTL_HANDLER_ARGS)
57a3a1ce30SCameron Grant {
58a3a1ce30SCameron Grant int err, val;
59a3a1ce30SCameron Grant
60a580b31aSAriff Abdullah val = chn_latency;
61041b706bSDavid Malone err = sysctl_handle_int(oidp, &val, 0, req);
6272e9d07fSAriff Abdullah if (err != 0 || req->newptr == NULL)
6372e9d07fSAriff Abdullah return err;
64a580b31aSAriff Abdullah if (val < CHN_LATENCY_MIN || val > CHN_LATENCY_MAX)
65a3a1ce30SCameron Grant err = EINVAL;
66a3a1ce30SCameron Grant else
67a580b31aSAriff Abdullah chn_latency = val;
68a3a1ce30SCameron Grant
69a3a1ce30SCameron Grant return err;
70a3a1ce30SCameron Grant }
717029da5cSPawel Biernacki SYSCTL_PROC(_hw_snd, OID_AUTO, latency,
723b4c5433SAlexander Motin CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 0, sizeof(int),
737029da5cSPawel Biernacki sysctl_hw_snd_latency, "I",
74a580b31aSAriff Abdullah "buffering latency (0=low ... 10=high)");
75a580b31aSAriff Abdullah
76a580b31aSAriff Abdullah int chn_latency_profile = CHN_LATENCY_PROFILE_DEFAULT;
77a580b31aSAriff Abdullah
78a580b31aSAriff Abdullah static int
sysctl_hw_snd_latency_profile(SYSCTL_HANDLER_ARGS)79a580b31aSAriff Abdullah sysctl_hw_snd_latency_profile(SYSCTL_HANDLER_ARGS)
80a580b31aSAriff Abdullah {
81a580b31aSAriff Abdullah int err, val;
82a580b31aSAriff Abdullah
83a580b31aSAriff Abdullah val = chn_latency_profile;
84041b706bSDavid Malone err = sysctl_handle_int(oidp, &val, 0, req);
8572e9d07fSAriff Abdullah if (err != 0 || req->newptr == NULL)
8672e9d07fSAriff Abdullah return err;
87a580b31aSAriff Abdullah if (val < CHN_LATENCY_PROFILE_MIN || val > CHN_LATENCY_PROFILE_MAX)
88a580b31aSAriff Abdullah err = EINVAL;
89a580b31aSAriff Abdullah else
90a580b31aSAriff Abdullah chn_latency_profile = val;
91a580b31aSAriff Abdullah
92a580b31aSAriff Abdullah return err;
93a580b31aSAriff Abdullah }
947029da5cSPawel Biernacki SYSCTL_PROC(_hw_snd, OID_AUTO, latency_profile,
953b4c5433SAlexander Motin CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 0, sizeof(int),
967029da5cSPawel Biernacki sysctl_hw_snd_latency_profile, "I",
976455cdfbSBaptiste Daroussin "buffering latency profile (0=aggressive 1=safe)");
98a3a1ce30SCameron Grant
99fd1475d3SAriff Abdullah static int chn_timeout = CHN_TIMEOUT;
10032a0e5d5SHans Petter Selasky
101fd1475d3SAriff Abdullah static int
sysctl_hw_snd_timeout(SYSCTL_HANDLER_ARGS)102fd1475d3SAriff Abdullah sysctl_hw_snd_timeout(SYSCTL_HANDLER_ARGS)
103fd1475d3SAriff Abdullah {
104fd1475d3SAriff Abdullah int err, val;
105fd1475d3SAriff Abdullah
106fd1475d3SAriff Abdullah val = chn_timeout;
107041b706bSDavid Malone err = sysctl_handle_int(oidp, &val, 0, req);
10872e9d07fSAriff Abdullah if (err != 0 || req->newptr == NULL)
10972e9d07fSAriff Abdullah return err;
110fd1475d3SAriff Abdullah if (val < CHN_TIMEOUT_MIN || val > CHN_TIMEOUT_MAX)
111fd1475d3SAriff Abdullah err = EINVAL;
112fd1475d3SAriff Abdullah else
113fd1475d3SAriff Abdullah chn_timeout = val;
114fd1475d3SAriff Abdullah
115fd1475d3SAriff Abdullah return err;
116fd1475d3SAriff Abdullah }
1177029da5cSPawel Biernacki SYSCTL_PROC(_hw_snd, OID_AUTO, timeout,
1183b4c5433SAlexander Motin CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 0, sizeof(int),
1197029da5cSPawel Biernacki sysctl_hw_snd_timeout, "I",
12072e9d07fSAriff Abdullah "interrupt timeout (1 - 10) seconds");
121fd1475d3SAriff Abdullah
12290da2b28SAriff Abdullah static int chn_vpc_autoreset = 1;
123af3b2549SHans Petter Selasky SYSCTL_INT(_hw_snd, OID_AUTO, vpc_autoreset, CTLFLAG_RWTUN,
12490da2b28SAriff Abdullah &chn_vpc_autoreset, 0, "automatically reset channels volume to 0db");
12590da2b28SAriff Abdullah
12690da2b28SAriff Abdullah static int chn_vol_0db_pcm = SND_VOL_0DB_PCM;
12790da2b28SAriff Abdullah
12890da2b28SAriff Abdullah static void
chn_vpc_proc(int reset,int db)12990da2b28SAriff Abdullah chn_vpc_proc(int reset, int db)
13090da2b28SAriff Abdullah {
13190da2b28SAriff Abdullah struct snddev_info *d;
13290da2b28SAriff Abdullah struct pcm_channel *c;
13390da2b28SAriff Abdullah int i;
13490da2b28SAriff Abdullah
135*35400672SChristos Margiolis bus_topo_lock();
13690da2b28SAriff Abdullah for (i = 0; pcm_devclass != NULL &&
13790da2b28SAriff Abdullah i < devclass_get_maxunit(pcm_devclass); i++) {
13890da2b28SAriff Abdullah d = devclass_get_softc(pcm_devclass, i);
13990da2b28SAriff Abdullah if (!PCM_REGISTERED(d))
14090da2b28SAriff Abdullah continue;
14190da2b28SAriff Abdullah PCM_LOCK(d);
14290da2b28SAriff Abdullah PCM_WAIT(d);
14390da2b28SAriff Abdullah PCM_ACQUIRE(d);
14490da2b28SAriff Abdullah CHN_FOREACH(c, d, channels.pcm) {
14590da2b28SAriff Abdullah CHN_LOCK(c);
14690da2b28SAriff Abdullah CHN_SETVOLUME(c, SND_VOL_C_PCM, SND_CHN_T_VOL_0DB, db);
14790da2b28SAriff Abdullah if (reset != 0)
14890da2b28SAriff Abdullah chn_vpc_reset(c, SND_VOL_C_PCM, 1);
14990da2b28SAriff Abdullah CHN_UNLOCK(c);
15090da2b28SAriff Abdullah }
15190da2b28SAriff Abdullah PCM_RELEASE(d);
15290da2b28SAriff Abdullah PCM_UNLOCK(d);
15390da2b28SAriff Abdullah }
154*35400672SChristos Margiolis bus_topo_unlock();
15590da2b28SAriff Abdullah }
15690da2b28SAriff Abdullah
15790da2b28SAriff Abdullah static int
sysctl_hw_snd_vpc_0db(SYSCTL_HANDLER_ARGS)15890da2b28SAriff Abdullah sysctl_hw_snd_vpc_0db(SYSCTL_HANDLER_ARGS)
15990da2b28SAriff Abdullah {
16090da2b28SAriff Abdullah int err, val;
16190da2b28SAriff Abdullah
16290da2b28SAriff Abdullah val = chn_vol_0db_pcm;
16390da2b28SAriff Abdullah err = sysctl_handle_int(oidp, &val, 0, req);
16490da2b28SAriff Abdullah if (err != 0 || req->newptr == NULL)
16590da2b28SAriff Abdullah return (err);
16690da2b28SAriff Abdullah if (val < SND_VOL_0DB_MIN || val > SND_VOL_0DB_MAX)
16790da2b28SAriff Abdullah return (EINVAL);
16890da2b28SAriff Abdullah
16990da2b28SAriff Abdullah chn_vol_0db_pcm = val;
17090da2b28SAriff Abdullah chn_vpc_proc(0, val);
17190da2b28SAriff Abdullah
17290da2b28SAriff Abdullah return (0);
17390da2b28SAriff Abdullah }
1747029da5cSPawel Biernacki SYSCTL_PROC(_hw_snd, OID_AUTO, vpc_0db,
175*35400672SChristos Margiolis CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 0, sizeof(int),
1767029da5cSPawel Biernacki sysctl_hw_snd_vpc_0db, "I",
17790da2b28SAriff Abdullah "0db relative level");
17890da2b28SAriff Abdullah
17990da2b28SAriff Abdullah static int
sysctl_hw_snd_vpc_reset(SYSCTL_HANDLER_ARGS)18090da2b28SAriff Abdullah sysctl_hw_snd_vpc_reset(SYSCTL_HANDLER_ARGS)
18190da2b28SAriff Abdullah {
18290da2b28SAriff Abdullah int err, val;
18390da2b28SAriff Abdullah
18490da2b28SAriff Abdullah val = 0;
18590da2b28SAriff Abdullah err = sysctl_handle_int(oidp, &val, 0, req);
18690da2b28SAriff Abdullah if (err != 0 || req->newptr == NULL || val == 0)
18790da2b28SAriff Abdullah return (err);
18890da2b28SAriff Abdullah
18990da2b28SAriff Abdullah chn_vol_0db_pcm = SND_VOL_0DB_PCM;
19090da2b28SAriff Abdullah chn_vpc_proc(1, SND_VOL_0DB_PCM);
19190da2b28SAriff Abdullah
19290da2b28SAriff Abdullah return (0);
19390da2b28SAriff Abdullah }
1947029da5cSPawel Biernacki SYSCTL_PROC(_hw_snd, OID_AUTO, vpc_reset,
195*35400672SChristos Margiolis CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, 0, sizeof(int),
1967029da5cSPawel Biernacki sysctl_hw_snd_vpc_reset, "I",
19790da2b28SAriff Abdullah "reset volume on all channels");
19890da2b28SAriff Abdullah
199fd1475d3SAriff Abdullah static int chn_usefrags = 0;
200fd1475d3SAriff Abdullah static int chn_syncdelay = -1;
20132a0e5d5SHans Petter Selasky
20232a0e5d5SHans Petter Selasky SYSCTL_INT(_hw_snd, OID_AUTO, usefrags, CTLFLAG_RWTUN,
20332a0e5d5SHans Petter Selasky &chn_usefrags, 0, "prefer setfragments() over setblocksize()");
20432a0e5d5SHans Petter Selasky SYSCTL_INT(_hw_snd, OID_AUTO, syncdelay, CTLFLAG_RWTUN,
20532a0e5d5SHans Petter Selasky &chn_syncdelay, 0,
206fd1475d3SAriff Abdullah "append (0-1000) millisecond trailing buffer delay on each sync");
207fd1475d3SAriff Abdullah
208b611c801SAlexander Leidinger /**
209b611c801SAlexander Leidinger * @brief Channel sync group lock
210b611c801SAlexander Leidinger *
211b611c801SAlexander Leidinger * Clients should acquire this lock @b without holding any channel locks
212b611c801SAlexander Leidinger * before touching syncgroups or the main syncgroup list.
213b611c801SAlexander Leidinger */
214b611c801SAlexander Leidinger struct mtx snd_pcm_syncgroups_mtx;
215b611c801SAlexander Leidinger MTX_SYSINIT(pcm_syncgroup, &snd_pcm_syncgroups_mtx, "PCM channel sync group lock", MTX_DEF);
216b611c801SAlexander Leidinger /**
217b611c801SAlexander Leidinger * @brief syncgroups' master list
218b611c801SAlexander Leidinger *
219b611c801SAlexander Leidinger * Each time a channel syncgroup is created, it's added to this list. This
220b611c801SAlexander Leidinger * list should only be accessed with @sa snd_pcm_syncgroups_mtx held.
221b611c801SAlexander Leidinger *
222b611c801SAlexander Leidinger * See SNDCTL_DSP_SYNCGROUP for more information.
223b611c801SAlexander Leidinger */
22413e403fdSAntoine Brodin struct pcm_synclist snd_pcm_syncgroups = SLIST_HEAD_INITIALIZER(snd_pcm_syncgroups);
225b611c801SAlexander Leidinger
22666ef8af5SCameron Grant static void
chn_lockinit(struct pcm_channel * c,int dir)227bc38932aSMathew Kanner chn_lockinit(struct pcm_channel *c, int dir)
228987e5972SCameron Grant {
229a3193a9cSDon Lewis switch (dir) {
230a3193a9cSDon Lewis case PCMDIR_PLAY:
231a3193a9cSDon Lewis c->lock = snd_mtxcreate(c->name, "pcm play channel");
232e4e61333SAriff Abdullah cv_init(&c->intr_cv, "pcmwr");
233a3193a9cSDon Lewis break;
234bba4862cSAriff Abdullah case PCMDIR_PLAY_VIRTUAL:
235bba4862cSAriff Abdullah c->lock = snd_mtxcreate(c->name, "pcm virtual play channel");
236e4e61333SAriff Abdullah cv_init(&c->intr_cv, "pcmwrv");
237bba4862cSAriff Abdullah break;
238a3193a9cSDon Lewis case PCMDIR_REC:
239a3193a9cSDon Lewis c->lock = snd_mtxcreate(c->name, "pcm record channel");
240e4e61333SAriff Abdullah cv_init(&c->intr_cv, "pcmrd");
241a3193a9cSDon Lewis break;
242bba4862cSAriff Abdullah case PCMDIR_REC_VIRTUAL:
243bba4862cSAriff Abdullah c->lock = snd_mtxcreate(c->name, "pcm virtual record channel");
244e4e61333SAriff Abdullah cv_init(&c->intr_cv, "pcmrdv");
245a3193a9cSDon Lewis break;
24690da2b28SAriff Abdullah default:
24790da2b28SAriff Abdullah panic("%s(): Invalid direction=%d", __func__, dir);
248a3193a9cSDon Lewis break;
249a3193a9cSDon Lewis }
250b611c801SAlexander Leidinger
251e4e61333SAriff Abdullah cv_init(&c->cv, "pcmchn");
25266ef8af5SCameron Grant }
253987e5972SCameron Grant
25466ef8af5SCameron Grant static void
chn_lockdestroy(struct pcm_channel * c)25566ef8af5SCameron Grant chn_lockdestroy(struct pcm_channel *c)
25666ef8af5SCameron Grant {
257e4e61333SAriff Abdullah CHN_LOCKASSERT(c);
258e4e61333SAriff Abdullah
259e4e61333SAriff Abdullah CHN_BROADCAST(&c->cv);
260e4e61333SAriff Abdullah CHN_BROADCAST(&c->intr_cv);
261e4e61333SAriff Abdullah
262b611c801SAlexander Leidinger cv_destroy(&c->cv);
263e4e61333SAriff Abdullah cv_destroy(&c->intr_cv);
264e4e61333SAriff Abdullah
265e4e61333SAriff Abdullah snd_mtxfree(c->lock);
266987e5972SCameron Grant }
267987e5972SCameron Grant
268b611c801SAlexander Leidinger /**
269b611c801SAlexander Leidinger * @brief Determine channel is ready for I/O
270b611c801SAlexander Leidinger *
271b611c801SAlexander Leidinger * @retval 1 = ready for I/O
272b611c801SAlexander Leidinger * @retval 0 = not ready for I/O
273b611c801SAlexander Leidinger */
274987e5972SCameron Grant static int
chn_polltrigger(struct pcm_channel * c)27566ef8af5SCameron Grant chn_polltrigger(struct pcm_channel *c)
276987e5972SCameron Grant {
27766ef8af5SCameron Grant struct snd_dbuf *bs = c->bufsoft;
27890da2b28SAriff Abdullah u_int delta;
279987e5972SCameron Grant
28066ef8af5SCameron Grant CHN_LOCKASSERT(c);
28190da2b28SAriff Abdullah
28290da2b28SAriff Abdullah if (c->flags & CHN_F_MMAP) {
28390da2b28SAriff Abdullah if (sndbuf_getprevtotal(bs) < c->lw)
28490da2b28SAriff Abdullah delta = c->lw;
28566ef8af5SCameron Grant else
28690da2b28SAriff Abdullah delta = sndbuf_gettotal(bs) - sndbuf_getprevtotal(bs);
28766ef8af5SCameron Grant } else {
28890da2b28SAriff Abdullah if (c->direction == PCMDIR_PLAY)
28990da2b28SAriff Abdullah delta = sndbuf_getfree(bs);
29090da2b28SAriff Abdullah else
29190da2b28SAriff Abdullah delta = sndbuf_getready(bs);
29266ef8af5SCameron Grant }
29366ef8af5SCameron Grant
29490da2b28SAriff Abdullah return ((delta < c->lw) ? 0 : 1);
29590da2b28SAriff Abdullah }
29690da2b28SAriff Abdullah
29790da2b28SAriff Abdullah static void
chn_pollreset(struct pcm_channel * c)29866ef8af5SCameron Grant chn_pollreset(struct pcm_channel *c)
29966ef8af5SCameron Grant {
30066ef8af5SCameron Grant
30166ef8af5SCameron Grant CHN_LOCKASSERT(c);
30290da2b28SAriff Abdullah sndbuf_updateprevtotal(c->bufsoft);
303987e5972SCameron Grant }
304987e5972SCameron Grant
305987e5972SCameron Grant static void
chn_wakeup(struct pcm_channel * c)30666ef8af5SCameron Grant chn_wakeup(struct pcm_channel *c)
307987e5972SCameron Grant {
308e4e61333SAriff Abdullah struct snd_dbuf *bs;
309bba4862cSAriff Abdullah struct pcm_channel *ch;
310987e5972SCameron Grant
31166ef8af5SCameron Grant CHN_LOCKASSERT(c);
312e4e61333SAriff Abdullah
313e4e61333SAriff Abdullah bs = c->bufsoft;
314e4e61333SAriff Abdullah
315e4e61333SAriff Abdullah if (CHN_EMPTY(c, children.busy)) {
31685f190e4SAlfred Perlstein if (SEL_WAITING(sndbuf_getsel(bs)) && chn_polltrigger(c))
3170a2a8111SMathew Kanner selwakeuppri(sndbuf_getsel(bs), PRIBIO);
318e4e61333SAriff Abdullah CHN_BROADCAST(&c->intr_cv);
319e0b6c8a1SMathew Kanner } else {
320bba4862cSAriff Abdullah CHN_FOREACH(ch, c, children.busy) {
321bba4862cSAriff Abdullah CHN_LOCK(ch);
322bba4862cSAriff Abdullah chn_wakeup(ch);
323bba4862cSAriff Abdullah CHN_UNLOCK(ch);
324e0b6c8a1SMathew Kanner }
325e0b6c8a1SMathew Kanner }
326987e5972SCameron Grant }
327987e5972SCameron Grant
32866ef8af5SCameron Grant static int
chn_sleep(struct pcm_channel * c,int timeout)329e4e61333SAriff Abdullah chn_sleep(struct pcm_channel *c, int timeout)
3305f070b67SCameron Grant {
33166ef8af5SCameron Grant int ret;
3325f070b67SCameron Grant
33366ef8af5SCameron Grant CHN_LOCKASSERT(c);
334bba4862cSAriff Abdullah
335e4e61333SAriff Abdullah if (c->flags & CHN_F_DEAD)
336e4e61333SAriff Abdullah return (EINVAL);
337e4e61333SAriff Abdullah
33846a97b9cSChristos Margiolis c->sleeping++;
339e4e61333SAriff Abdullah ret = cv_timedwait_sig(&c->intr_cv, c->lock, timeout);
34046a97b9cSChristos Margiolis c->sleeping--;
34166ef8af5SCameron Grant
342e4e61333SAriff Abdullah return ((c->flags & CHN_F_DEAD) ? EINVAL : ret);
3435f070b67SCameron Grant }
3445f070b67SCameron Grant
3455f070b67SCameron Grant /*
346987e5972SCameron Grant * chn_dmaupdate() tracks the status of a dma transfer,
347cb44f623SAlexander Leidinger * updating pointers.
348987e5972SCameron Grant */
3490e25481fSCameron Grant
35066ef8af5SCameron Grant static unsigned int
chn_dmaupdate(struct pcm_channel * c)35166ef8af5SCameron Grant chn_dmaupdate(struct pcm_channel *c)
352987e5972SCameron Grant {
35366ef8af5SCameron Grant struct snd_dbuf *b = c->bufhard;
35466ef8af5SCameron Grant unsigned int delta, old, hwptr, amt;
355987e5972SCameron Grant
356285648f9SCameron Grant KASSERT(sndbuf_getsize(b) > 0, ("bufsize == 0"));
35766ef8af5SCameron Grant CHN_LOCKASSERT(c);
358b83a42d3SCameron Grant
35966ef8af5SCameron Grant old = sndbuf_gethwptr(b);
3600e25481fSCameron Grant hwptr = chn_getptr(c);
36166ef8af5SCameron Grant delta = (sndbuf_getsize(b) + hwptr - old) % sndbuf_getsize(b);
36266ef8af5SCameron Grant sndbuf_sethwptr(b, hwptr);
36366ef8af5SCameron Grant
364987e5972SCameron Grant if (c->direction == PCMDIR_PLAY) {
365fd1475d3SAriff Abdullah amt = min(delta, sndbuf_getready(b));
36690da2b28SAriff Abdullah amt -= amt % sndbuf_getalign(b);
36766ef8af5SCameron Grant if (amt > 0)
36866ef8af5SCameron Grant sndbuf_dispose(b, NULL, amt);
369987e5972SCameron Grant } else {
370fd1475d3SAriff Abdullah amt = min(delta, sndbuf_getfree(b));
37190da2b28SAriff Abdullah amt -= amt % sndbuf_getalign(b);
37266ef8af5SCameron Grant if (amt > 0)
37366ef8af5SCameron Grant sndbuf_acquire(b, NULL, amt);
374987e5972SCameron Grant }
375fd1475d3SAriff Abdullah if (snd_verbose > 3 && CHN_STARTED(c) && delta == 0) {
376fd1475d3SAriff Abdullah device_printf(c->dev, "WARNING: %s DMA completion "
377a580b31aSAriff Abdullah "too fast/slow ! hwptr=%u, old=%u "
378a580b31aSAriff Abdullah "delta=%u amt=%u ready=%u free=%u\n",
379fd1475d3SAriff Abdullah CHN_DIRSTR(c), hwptr, old, delta, amt,
380a580b31aSAriff Abdullah sndbuf_getready(b), sndbuf_getfree(b));
381a580b31aSAriff Abdullah }
382987e5972SCameron Grant
38366ef8af5SCameron Grant return delta;
38466ef8af5SCameron Grant }
38566ef8af5SCameron Grant
38690da2b28SAriff Abdullah static void
chn_wrfeed(struct pcm_channel * c)38766ef8af5SCameron Grant chn_wrfeed(struct pcm_channel *c)
38866ef8af5SCameron Grant {
38966ef8af5SCameron Grant struct snd_dbuf *b = c->bufhard;
39066ef8af5SCameron Grant struct snd_dbuf *bs = c->bufsoft;
391fd578f65SAlexander Motin unsigned int amt, want, wasfree;
39266ef8af5SCameron Grant
39366ef8af5SCameron Grant CHN_LOCKASSERT(c);
39466ef8af5SCameron Grant
39590da2b28SAriff Abdullah if ((c->flags & CHN_F_MMAP) && !(c->flags & CHN_F_CLOSING))
396b83a42d3SCameron Grant sndbuf_acquire(bs, NULL, sndbuf_getfree(bs));
397b83a42d3SCameron Grant
398fd578f65SAlexander Motin wasfree = sndbuf_getfree(b);
399fd578f65SAlexander Motin want = min(sndbuf_getsize(b),
400fd578f65SAlexander Motin imax(0, sndbuf_xbytes(sndbuf_getsize(bs), bs, b) -
401fd578f65SAlexander Motin sndbuf_getready(b)));
402fd578f65SAlexander Motin amt = min(wasfree, want);
40390da2b28SAriff Abdullah if (amt > 0)
40490da2b28SAriff Abdullah sndbuf_feed(bs, b, c, c->feeder, amt);
40587506547SAlexander Leidinger
40687506547SAlexander Leidinger /*
40776490732SAriff Abdullah * Possible xruns. There should be no empty space left in buffer.
40887506547SAlexander Leidinger */
409fd578f65SAlexander Motin if (sndbuf_getready(b) < want)
41087506547SAlexander Leidinger c->xruns++;
41187506547SAlexander Leidinger
412fd578f65SAlexander Motin if (sndbuf_getfree(b) < wasfree)
413fd1475d3SAriff Abdullah chn_wakeup(c);
414d55a1446SCameron Grant }
41566ef8af5SCameron Grant
41666ef8af5SCameron Grant static void
chn_wrintr(struct pcm_channel * c)41766ef8af5SCameron Grant chn_wrintr(struct pcm_channel *c)
41866ef8af5SCameron Grant {
41966ef8af5SCameron Grant
42066ef8af5SCameron Grant CHN_LOCKASSERT(c);
42166ef8af5SCameron Grant /* update pointers in primary buffer */
42266ef8af5SCameron Grant chn_dmaupdate(c);
42366ef8af5SCameron Grant /* ...and feed from secondary to primary */
42490da2b28SAriff Abdullah chn_wrfeed(c);
42566ef8af5SCameron Grant /* tell the driver we've updated the primary buffer */
42666ef8af5SCameron Grant chn_trigger(c, PCMTRIG_EMLDMAWR);
427d55a1446SCameron Grant }
428d55a1446SCameron Grant
429d55a1446SCameron Grant /*
430a3285889SCameron Grant * user write routine - uiomove data into secondary buffer, trigger if necessary
43166ef8af5SCameron Grant * if blocking, sleep, rinse and repeat.
432d55a1446SCameron Grant *
43366ef8af5SCameron Grant * called externally, so must handle locking
434987e5972SCameron Grant */
435987e5972SCameron Grant
436987e5972SCameron Grant int
chn_write(struct pcm_channel * c,struct uio * buf)43766ef8af5SCameron Grant chn_write(struct pcm_channel *c, struct uio *buf)
438987e5972SCameron Grant {
43966ef8af5SCameron Grant struct snd_dbuf *bs = c->bufsoft;
4408e2d74a4SMathew Kanner void *off;
441fd1475d3SAriff Abdullah int ret, timeout, sz, t, p;
442987e5972SCameron Grant
44366ef8af5SCameron Grant CHN_LOCKASSERT(c);
444a0b57fb7SCameron Grant
44566ef8af5SCameron Grant ret = 0;
446fd1475d3SAriff Abdullah timeout = chn_timeout * hz;
447b611c801SAlexander Leidinger
448fd1475d3SAriff Abdullah while (ret == 0 && buf->uio_resid > 0) {
449fd1475d3SAriff Abdullah sz = min(buf->uio_resid, sndbuf_getfree(bs));
450fd1475d3SAriff Abdullah if (sz > 0) {
451fd1475d3SAriff Abdullah /*
452fd1475d3SAriff Abdullah * The following assumes that the free space in
453fd1475d3SAriff Abdullah * the buffer can never be less around the
454fd1475d3SAriff Abdullah * unlock-uiomove-lock sequence.
455fd1475d3SAriff Abdullah */
456fd1475d3SAriff Abdullah while (ret == 0 && sz > 0) {
457fd1475d3SAriff Abdullah p = sndbuf_getfreeptr(bs);
458fd1475d3SAriff Abdullah t = min(sz, sndbuf_getsize(bs) - p);
459fd1475d3SAriff Abdullah off = sndbuf_getbufofs(bs, p);
460fd1475d3SAriff Abdullah CHN_UNLOCK(c);
461fd1475d3SAriff Abdullah ret = uiomove(off, t, buf);
462fd1475d3SAriff Abdullah CHN_LOCK(c);
463fd1475d3SAriff Abdullah sz -= t;
464fd1475d3SAriff Abdullah sndbuf_acquire(bs, NULL, t);
465fd1475d3SAriff Abdullah }
466fd1475d3SAriff Abdullah ret = 0;
46784793af6SAriff Abdullah if (CHN_STOPPED(c) && !(c->flags & CHN_F_NOTRIGGER)) {
468e4e61333SAriff Abdullah ret = chn_start(c, 0);
469e4e61333SAriff Abdullah if (ret != 0)
470e4e61333SAriff Abdullah c->flags |= CHN_F_DEAD;
471e4e61333SAriff Abdullah }
472fd1475d3SAriff Abdullah } else if (c->flags & (CHN_F_NBIO | CHN_F_NOTRIGGER)) {
473b611c801SAlexander Leidinger /**
474b611c801SAlexander Leidinger * @todo Evaluate whether EAGAIN is truly desirable.
475b611c801SAlexander Leidinger * 4Front drivers behave like this, but I'm
476b611c801SAlexander Leidinger * not sure if it at all violates the "write
477b611c801SAlexander Leidinger * should be allowed to block" model.
478b611c801SAlexander Leidinger *
479b611c801SAlexander Leidinger * The idea is that, while set with CHN_F_NOTRIGGER,
480b611c801SAlexander Leidinger * a channel isn't playing, *but* without this we
481b611c801SAlexander Leidinger * end up with "interrupt timeout / channel dead".
482b611c801SAlexander Leidinger */
483b611c801SAlexander Leidinger ret = EAGAIN;
484b611c801SAlexander Leidinger } else {
485e4e61333SAriff Abdullah ret = chn_sleep(c, timeout);
486fd1475d3SAriff Abdullah if (ret == EAGAIN) {
487fd1475d3SAriff Abdullah ret = EINVAL;
488e4d5b250SCameron Grant c->flags |= CHN_F_DEAD;
48990da2b28SAriff Abdullah device_printf(c->dev, "%s(): %s: "
49090da2b28SAriff Abdullah "play interrupt timeout, channel dead\n",
49190da2b28SAriff Abdullah __func__, c->name);
492fd1475d3SAriff Abdullah } else if (ret == ERESTART || ret == EINTR)
493fd1475d3SAriff Abdullah c->flags |= CHN_F_ABORTING;
494fd1475d3SAriff Abdullah }
495e4d5b250SCameron Grant }
49666ef8af5SCameron Grant
497e4e61333SAriff Abdullah return (ret);
498987e5972SCameron Grant }
499987e5972SCameron Grant
5005f070b67SCameron Grant /*
501a3285889SCameron Grant * Feed new data from the read buffer. Can be called in the bottom half.
5025f070b67SCameron Grant */
50390da2b28SAriff Abdullah static void
chn_rdfeed(struct pcm_channel * c)50466ef8af5SCameron Grant chn_rdfeed(struct pcm_channel *c)
5055f070b67SCameron Grant {
50666ef8af5SCameron Grant struct snd_dbuf *b = c->bufhard;
50766ef8af5SCameron Grant struct snd_dbuf *bs = c->bufsoft;
50890da2b28SAriff Abdullah unsigned int amt;
5095f070b67SCameron Grant
51066ef8af5SCameron Grant CHN_LOCKASSERT(c);
51166ef8af5SCameron Grant
51290da2b28SAriff Abdullah if (c->flags & CHN_F_MMAP)
513e4e61333SAriff Abdullah sndbuf_dispose(bs, NULL, sndbuf_getready(bs));
514e4e61333SAriff Abdullah
51597d69a96SAlexander Leidinger amt = sndbuf_getfree(bs);
51690da2b28SAriff Abdullah if (amt > 0)
51790da2b28SAriff Abdullah sndbuf_feed(b, bs, c, c->feeder, amt);
51866ef8af5SCameron Grant
519c2e6dd76SCameron Grant amt = sndbuf_getready(b);
520cc6882e1SAriff Abdullah if (amt > 0) {
521cc6882e1SAriff Abdullah c->xruns++;
522cc6882e1SAriff Abdullah sndbuf_dispose(b, NULL, amt);
523cc6882e1SAriff Abdullah }
524a3285889SCameron Grant
52572e9d07fSAriff Abdullah if (sndbuf_getready(bs) > 0)
52666ef8af5SCameron Grant chn_wakeup(c);
5275f070b67SCameron Grant }
5285f070b67SCameron Grant
529987e5972SCameron Grant /* read interrupt routine. Must be called with interrupts blocked. */
530987e5972SCameron Grant static void
chn_rdintr(struct pcm_channel * c)53166ef8af5SCameron Grant chn_rdintr(struct pcm_channel *c)
532987e5972SCameron Grant {
533987e5972SCameron Grant
53466ef8af5SCameron Grant CHN_LOCKASSERT(c);
535a3285889SCameron Grant /* tell the driver to update the primary buffer if non-dma */
536d55a1446SCameron Grant chn_trigger(c, PCMTRIG_EMLDMARD);
537edecdda7SCameron Grant /* update pointers in primary buffer */
538d55a1446SCameron Grant chn_dmaupdate(c);
53966ef8af5SCameron Grant /* ...and feed from primary to secondary */
54090da2b28SAriff Abdullah chn_rdfeed(c);
541987e5972SCameron Grant }
542987e5972SCameron Grant
543987e5972SCameron Grant /*
54423bc171bSCameron Grant * user read routine - trigger if necessary, uiomove data from secondary buffer
54566ef8af5SCameron Grant * if blocking, sleep, rinse and repeat.
546987e5972SCameron Grant *
54766ef8af5SCameron Grant * called externally, so must handle locking
548987e5972SCameron Grant */
549987e5972SCameron Grant
550987e5972SCameron Grant int
chn_read(struct pcm_channel * c,struct uio * buf)55166ef8af5SCameron Grant chn_read(struct pcm_channel *c, struct uio *buf)
552987e5972SCameron Grant {
55366ef8af5SCameron Grant struct snd_dbuf *bs = c->bufsoft;
5548e2d74a4SMathew Kanner void *off;
555fd1475d3SAriff Abdullah int ret, timeout, sz, t, p;
556987e5972SCameron Grant
55766ef8af5SCameron Grant CHN_LOCKASSERT(c);
558fd1475d3SAriff Abdullah
55984793af6SAriff Abdullah if (CHN_STOPPED(c) && !(c->flags & CHN_F_NOTRIGGER)) {
560e4e61333SAriff Abdullah ret = chn_start(c, 0);
561e4e61333SAriff Abdullah if (ret != 0) {
562e4e61333SAriff Abdullah c->flags |= CHN_F_DEAD;
563e4e61333SAriff Abdullah return (ret);
564e4e61333SAriff Abdullah }
565e4e61333SAriff Abdullah }
566a0b57fb7SCameron Grant
56766ef8af5SCameron Grant ret = 0;
568fd1475d3SAriff Abdullah timeout = chn_timeout * hz;
569a0b57fb7SCameron Grant
570fd1475d3SAriff Abdullah while (ret == 0 && buf->uio_resid > 0) {
571fd1475d3SAriff Abdullah sz = min(buf->uio_resid, sndbuf_getready(bs));
5728309fd9aSCameron Grant if (sz > 0) {
5738e2d74a4SMathew Kanner /*
5748e2d74a4SMathew Kanner * The following assumes that the free space in
5758e2d74a4SMathew Kanner * the buffer can never be less around the
5768e2d74a4SMathew Kanner * unlock-uiomove-lock sequence.
5778e2d74a4SMathew Kanner */
578fd1475d3SAriff Abdullah while (ret == 0 && sz > 0) {
5798e2d74a4SMathew Kanner p = sndbuf_getreadyptr(bs);
580fd1475d3SAriff Abdullah t = min(sz, sndbuf_getsize(bs) - p);
5818e2d74a4SMathew Kanner off = sndbuf_getbufofs(bs, p);
5828e2d74a4SMathew Kanner CHN_UNLOCK(c);
5838e2d74a4SMathew Kanner ret = uiomove(off, t, buf);
5848e2d74a4SMathew Kanner CHN_LOCK(c);
585fd1475d3SAriff Abdullah sz -= t;
586fd1475d3SAriff Abdullah sndbuf_dispose(bs, NULL, t);
5878e2d74a4SMathew Kanner }
5888e2d74a4SMathew Kanner ret = 0;
589fd1475d3SAriff Abdullah } else if (c->flags & (CHN_F_NBIO | CHN_F_NOTRIGGER))
590fd1475d3SAriff Abdullah ret = EAGAIN;
591a580b31aSAriff Abdullah else {
592e4e61333SAriff Abdullah ret = chn_sleep(c, timeout);
593fd1475d3SAriff Abdullah if (ret == EAGAIN) {
594fd1475d3SAriff Abdullah ret = EINVAL;
595946e6086SCameron Grant c->flags |= CHN_F_DEAD;
59690da2b28SAriff Abdullah device_printf(c->dev, "%s(): %s: "
59790da2b28SAriff Abdullah "record interrupt timeout, channel dead\n",
59890da2b28SAriff Abdullah __func__, c->name);
599fd1475d3SAriff Abdullah } else if (ret == ERESTART || ret == EINTR)
600fd1475d3SAriff Abdullah c->flags |= CHN_F_ABORTING;
601fd1475d3SAriff Abdullah }
602946e6086SCameron Grant }
60320cdda00SCameron Grant
604e4e61333SAriff Abdullah return (ret);
605987e5972SCameron Grant }
606987e5972SCameron Grant
607987e5972SCameron Grant void
chn_intr_locked(struct pcm_channel * c)60890da2b28SAriff Abdullah chn_intr_locked(struct pcm_channel *c)
609987e5972SCameron Grant {
61090da2b28SAriff Abdullah
61190da2b28SAriff Abdullah CHN_LOCKASSERT(c);
61290da2b28SAriff Abdullah
613a3285889SCameron Grant c->interrupts++;
61490da2b28SAriff Abdullah
6158ae4c159SCameron Grant if (c->direction == PCMDIR_PLAY)
6168ae4c159SCameron Grant chn_wrintr(c);
6178ae4c159SCameron Grant else
6188ae4c159SCameron Grant chn_rdintr(c);
619987e5972SCameron Grant }
62090da2b28SAriff Abdullah
62190da2b28SAriff Abdullah void
chn_intr(struct pcm_channel * c)62290da2b28SAriff Abdullah chn_intr(struct pcm_channel *c)
62390da2b28SAriff Abdullah {
62490da2b28SAriff Abdullah
62590da2b28SAriff Abdullah if (CHN_LOCKOWNED(c)) {
62690da2b28SAriff Abdullah chn_intr_locked(c);
627eabe30fcSAlfred Perlstein return;
628eabe30fcSAlfred Perlstein }
629987e5972SCameron Grant
63090da2b28SAriff Abdullah CHN_LOCK(c);
63190da2b28SAriff Abdullah chn_intr_locked(c);
63290da2b28SAriff Abdullah CHN_UNLOCK(c);
63390da2b28SAriff Abdullah }
63490da2b28SAriff Abdullah
635d28089a1SCameron Grant u_int32_t
chn_start(struct pcm_channel * c,int force)63666ef8af5SCameron Grant chn_start(struct pcm_channel *c, int force)
637d28089a1SCameron Grant {
638edecdda7SCameron Grant u_int32_t i, j;
63966ef8af5SCameron Grant struct snd_dbuf *b = c->bufhard;
64066ef8af5SCameron Grant struct snd_dbuf *bs = c->bufsoft;
641e4e61333SAriff Abdullah int err;
642d28089a1SCameron Grant
64366ef8af5SCameron Grant CHN_LOCKASSERT(c);
64466ef8af5SCameron Grant /* if we're running, or if we're prevented from triggering, bail */
645fd1475d3SAriff Abdullah if (CHN_STARTED(c) || ((c->flags & CHN_F_NOTRIGGER) && !force))
646e4e61333SAriff Abdullah return (EINVAL);
647e4e61333SAriff Abdullah
648e4e61333SAriff Abdullah err = 0;
64966ef8af5SCameron Grant
650a580b31aSAriff Abdullah if (force) {
651a580b31aSAriff Abdullah i = 1;
652a580b31aSAriff Abdullah j = 0;
653a580b31aSAriff Abdullah } else {
654a580b31aSAriff Abdullah if (c->direction == PCMDIR_REC) {
655a580b31aSAriff Abdullah i = sndbuf_getfree(bs);
656fd1475d3SAriff Abdullah j = (i > 0) ? 1 : sndbuf_getready(b);
657fd1475d3SAriff Abdullah } else {
658fd1475d3SAriff Abdullah if (sndbuf_getfree(bs) == 0) {
659fd1475d3SAriff Abdullah i = 1;
660fd1475d3SAriff Abdullah j = 0;
661a580b31aSAriff Abdullah } else {
662a580b31aSAriff Abdullah struct snd_dbuf *pb;
663a580b31aSAriff Abdullah
664e4e61333SAriff Abdullah pb = CHN_BUF_PARENT(c, b);
665fd1475d3SAriff Abdullah i = sndbuf_xbytes(sndbuf_getready(bs), bs, pb);
66690da2b28SAriff Abdullah j = sndbuf_getalign(pb);
667fd1475d3SAriff Abdullah }
668285648f9SCameron Grant }
669bba4862cSAriff Abdullah if (snd_verbose > 3 && CHN_EMPTY(c, children))
67090da2b28SAriff Abdullah device_printf(c->dev, "%s(): %s (%s) threshold "
67190da2b28SAriff Abdullah "i=%d j=%d\n", __func__, CHN_DIRSTR(c),
67290da2b28SAriff Abdullah (c->flags & CHN_F_VIRTUAL) ? "virtual" :
67390da2b28SAriff Abdullah "hardware", i, j);
674a580b31aSAriff Abdullah }
675a580b31aSAriff Abdullah
676a580b31aSAriff Abdullah if (i >= j) {
677a580b31aSAriff Abdullah c->flags |= CHN_F_TRIGGERED;
67866ef8af5SCameron Grant sndbuf_setrun(b, 1);
67990da2b28SAriff Abdullah if (c->flags & CHN_F_CLOSING)
68090da2b28SAriff Abdullah c->feedcount = 2;
68190da2b28SAriff Abdullah else {
68290da2b28SAriff Abdullah c->feedcount = 0;
683a580b31aSAriff Abdullah c->interrupts = 0;
684edecdda7SCameron Grant c->xruns = 0;
68590da2b28SAriff Abdullah }
68690da2b28SAriff Abdullah if (c->parentchannel == NULL) {
68790da2b28SAriff Abdullah if (c->direction == PCMDIR_PLAY)
688fd578f65SAlexander Motin sndbuf_fillsilence_rl(b,
689fd578f65SAlexander Motin sndbuf_xbytes(sndbuf_getsize(bs), bs, b));
690a580b31aSAriff Abdullah if (snd_verbose > 3)
69190da2b28SAriff Abdullah device_printf(c->dev,
69290da2b28SAriff Abdullah "%s(): %s starting! (%s/%s) "
69390da2b28SAriff Abdullah "(ready=%d force=%d i=%d j=%d "
69490da2b28SAriff Abdullah "intrtimeout=%u latency=%dms)\n",
695a580b31aSAriff Abdullah __func__,
696a580b31aSAriff Abdullah (c->flags & CHN_F_HAS_VCHAN) ?
69790da2b28SAriff Abdullah "VCHAN PARENT" : "HW", CHN_DIRSTR(c),
698a580b31aSAriff Abdullah (c->flags & CHN_F_CLOSING) ? "closing" :
699a580b31aSAriff Abdullah "running",
700a580b31aSAriff Abdullah sndbuf_getready(b),
701fd1475d3SAriff Abdullah force, i, j, c->timeout,
702fd1475d3SAriff Abdullah (sndbuf_getsize(b) * 1000) /
70390da2b28SAriff Abdullah (sndbuf_getalign(b) * sndbuf_getspd(b)));
704a580b31aSAriff Abdullah }
705e4e61333SAriff Abdullah err = chn_trigger(c, PCMTRIG_START);
706d28089a1SCameron Grant }
70766ef8af5SCameron Grant
708e4e61333SAriff Abdullah return (err);
709d28089a1SCameron Grant }
710d28089a1SCameron Grant
711987e5972SCameron Grant void
chn_resetbuf(struct pcm_channel * c)71266ef8af5SCameron Grant chn_resetbuf(struct pcm_channel *c)
713987e5972SCameron Grant {
71466ef8af5SCameron Grant struct snd_dbuf *b = c->bufhard;
71566ef8af5SCameron Grant struct snd_dbuf *bs = c->bufsoft;
716987e5972SCameron Grant
717603ddb6dSCameron Grant c->blocks = 0;
718350a5fafSCameron Grant sndbuf_reset(b);
719350a5fafSCameron Grant sndbuf_reset(bs);
720987e5972SCameron Grant }
721987e5972SCameron Grant
722987e5972SCameron Grant /*
723a0b57fb7SCameron Grant * chn_sync waits until the space in the given channel goes above
724987e5972SCameron Grant * a threshold. The threshold is checked against fl or rl respectively.
725987e5972SCameron Grant * Assume that the condition can become true, do not check here...
726987e5972SCameron Grant */
727987e5972SCameron Grant int
chn_sync(struct pcm_channel * c,int threshold)72866ef8af5SCameron Grant chn_sync(struct pcm_channel *c, int threshold)
729987e5972SCameron Grant {
730a580b31aSAriff Abdullah struct snd_dbuf *b, *bs;
73142a3b81eSAriff Abdullah int ret, count, hcount, minflush, resid, residp, syncdelay, blksz;
73242a3b81eSAriff Abdullah u_int32_t cflag;
733987e5972SCameron Grant
73466ef8af5SCameron Grant CHN_LOCKASSERT(c);
735a5dc5888SCameron Grant
736e4e61333SAriff Abdullah if (c->direction != PCMDIR_PLAY)
737e4e61333SAriff Abdullah return (EINVAL);
738e4e61333SAriff Abdullah
739fd1475d3SAriff Abdullah bs = c->bufsoft;
740fd1475d3SAriff Abdullah
741fd1475d3SAriff Abdullah if ((c->flags & (CHN_F_DEAD | CHN_F_ABORTING)) ||
742fd1475d3SAriff Abdullah (threshold < 1 && sndbuf_getready(bs) < 1))
743e4e61333SAriff Abdullah return (0);
744a580b31aSAriff Abdullah
745a5dc5888SCameron Grant /* if we haven't yet started and nothing is buffered, else start*/
746fd1475d3SAriff Abdullah if (CHN_STOPPED(c)) {
747fd1475d3SAriff Abdullah if (threshold > 0 || sndbuf_getready(bs) > 0) {
748a5dc5888SCameron Grant ret = chn_start(c, 1);
749e4e61333SAriff Abdullah if (ret != 0)
750e4e61333SAriff Abdullah return (ret);
751a580b31aSAriff Abdullah } else
752e4e61333SAriff Abdullah return (0);
753a5dc5888SCameron Grant }
754a580b31aSAriff Abdullah
755e4e61333SAriff Abdullah b = CHN_BUF_PARENT(c, c->bufhard);
756a580b31aSAriff Abdullah
757fd1475d3SAriff Abdullah minflush = threshold + sndbuf_xbytes(sndbuf_getready(b), b, bs);
758fd1475d3SAriff Abdullah
759fd1475d3SAriff Abdullah syncdelay = chn_syncdelay;
760fd1475d3SAriff Abdullah
761fd1475d3SAriff Abdullah if (syncdelay < 0 && (threshold > 0 || sndbuf_getready(bs) > 0))
762fd1475d3SAriff Abdullah minflush += sndbuf_xbytes(sndbuf_getsize(b), b, bs);
763fd1475d3SAriff Abdullah
764fd1475d3SAriff Abdullah /*
765fd1475d3SAriff Abdullah * Append (0-1000) millisecond trailing buffer (if needed)
766fd1475d3SAriff Abdullah * for slower / high latency hardwares (notably USB audio)
767fd1475d3SAriff Abdullah * to avoid audible truncation.
768fd1475d3SAriff Abdullah */
769fd1475d3SAriff Abdullah if (syncdelay > 0)
77090da2b28SAriff Abdullah minflush += (sndbuf_getalign(bs) * sndbuf_getspd(bs) *
771fd1475d3SAriff Abdullah ((syncdelay > 1000) ? 1000 : syncdelay)) / 1000;
772fd1475d3SAriff Abdullah
77390da2b28SAriff Abdullah minflush -= minflush % sndbuf_getalign(bs);
774a580b31aSAriff Abdullah
775a580b31aSAriff Abdullah if (minflush > 0) {
776a580b31aSAriff Abdullah threshold = min(minflush, sndbuf_getfree(bs));
777a580b31aSAriff Abdullah sndbuf_clear(bs, threshold);
778a580b31aSAriff Abdullah sndbuf_acquire(bs, NULL, threshold);
779a580b31aSAriff Abdullah minflush -= threshold;
780a580b31aSAriff Abdullah }
781a580b31aSAriff Abdullah
782a580b31aSAriff Abdullah resid = sndbuf_getready(bs);
783a580b31aSAriff Abdullah residp = resid;
784fd1475d3SAriff Abdullah blksz = sndbuf_getblksz(b);
785fd1475d3SAriff Abdullah if (blksz < 1) {
78690da2b28SAriff Abdullah device_printf(c->dev,
78790da2b28SAriff Abdullah "%s(): WARNING: blksz < 1 ! maxsize=%d [%d/%d/%d]\n",
788fd1475d3SAriff Abdullah __func__, sndbuf_getmaxsize(b), sndbuf_getsize(b),
789fd1475d3SAriff Abdullah sndbuf_getblksz(b), sndbuf_getblkcnt(b));
790fd1475d3SAriff Abdullah if (sndbuf_getblkcnt(b) > 0)
791fd1475d3SAriff Abdullah blksz = sndbuf_getsize(b) / sndbuf_getblkcnt(b);
792fd1475d3SAriff Abdullah if (blksz < 1)
793fd1475d3SAriff Abdullah blksz = 1;
794fd1475d3SAriff Abdullah }
795fd1475d3SAriff Abdullah count = sndbuf_xbytes(minflush + resid, bs, b) / blksz;
796a580b31aSAriff Abdullah hcount = count;
797a580b31aSAriff Abdullah ret = 0;
798a580b31aSAriff Abdullah
799fd1475d3SAriff Abdullah if (snd_verbose > 3)
80090da2b28SAriff Abdullah device_printf(c->dev, "%s(): [begin] timeout=%d count=%d "
801fd1475d3SAriff Abdullah "minflush=%d resid=%d\n", __func__, c->timeout, count,
802fd1475d3SAriff Abdullah minflush, resid);
803fd1475d3SAriff Abdullah
80442a3b81eSAriff Abdullah cflag = c->flags & CHN_F_CLOSING;
80542a3b81eSAriff Abdullah c->flags |= CHN_F_CLOSING;
806fd1475d3SAriff Abdullah while (count > 0 && (resid > 0 || minflush > 0)) {
807e4e61333SAriff Abdullah ret = chn_sleep(c, c->timeout);
808a580b31aSAriff Abdullah if (ret == ERESTART || ret == EINTR) {
809a580b31aSAriff Abdullah c->flags |= CHN_F_ABORTING;
810a580b31aSAriff Abdullah break;
811e4e61333SAriff Abdullah } else if (ret == 0 || ret == EAGAIN) {
812a580b31aSAriff Abdullah resid = sndbuf_getready(bs);
813a580b31aSAriff Abdullah if (resid == residp) {
814a580b31aSAriff Abdullah --count;
815a580b31aSAriff Abdullah if (snd_verbose > 3)
81690da2b28SAriff Abdullah device_printf(c->dev,
81790da2b28SAriff Abdullah "%s(): [stalled] timeout=%d "
818a580b31aSAriff Abdullah "count=%d hcount=%d "
819a580b31aSAriff Abdullah "resid=%d minflush=%d\n",
820a580b31aSAriff Abdullah __func__, c->timeout, count,
821a580b31aSAriff Abdullah hcount, resid, minflush);
822a580b31aSAriff Abdullah } else if (resid < residp && count < hcount) {
823a580b31aSAriff Abdullah ++count;
824a580b31aSAriff Abdullah if (snd_verbose > 3)
82590da2b28SAriff Abdullah device_printf(c->dev,
82690da2b28SAriff Abdullah "%s((): [resume] timeout=%d "
827a580b31aSAriff Abdullah "count=%d hcount=%d "
828a580b31aSAriff Abdullah "resid=%d minflush=%d\n",
829a580b31aSAriff Abdullah __func__, c->timeout, count,
830a580b31aSAriff Abdullah hcount, resid, minflush);
831a580b31aSAriff Abdullah }
832a580b31aSAriff Abdullah if (minflush > 0 && sndbuf_getfree(bs) > 0) {
833a580b31aSAriff Abdullah threshold = min(minflush,
834a580b31aSAriff Abdullah sndbuf_getfree(bs));
835a580b31aSAriff Abdullah sndbuf_clear(bs, threshold);
836a580b31aSAriff Abdullah sndbuf_acquire(bs, NULL, threshold);
837a580b31aSAriff Abdullah resid = sndbuf_getready(bs);
838a580b31aSAriff Abdullah minflush -= threshold;
839a580b31aSAriff Abdullah }
840a580b31aSAriff Abdullah residp = resid;
841e4e61333SAriff Abdullah } else
842e4e61333SAriff Abdullah break;
843a5dc5888SCameron Grant }
84442a3b81eSAriff Abdullah c->flags &= ~CHN_F_CLOSING;
84542a3b81eSAriff Abdullah c->flags |= cflag;
846a5dc5888SCameron Grant
847a580b31aSAriff Abdullah if (snd_verbose > 3)
84890da2b28SAriff Abdullah device_printf(c->dev,
84990da2b28SAriff Abdullah "%s(): timeout=%d count=%d hcount=%d resid=%d residp=%d "
850a580b31aSAriff Abdullah "minflush=%d ret=%d\n",
851a580b31aSAriff Abdullah __func__, c->timeout, count, hcount, resid, residp,
852a580b31aSAriff Abdullah minflush, ret);
853a580b31aSAriff Abdullah
854e4e61333SAriff Abdullah return (0);
855987e5972SCameron Grant }
856987e5972SCameron Grant
85766ef8af5SCameron Grant /* called externally, handle locking */
858987e5972SCameron Grant int
chn_poll(struct pcm_channel * c,int ev,struct thread * td)859b40ce416SJulian Elischer chn_poll(struct pcm_channel *c, int ev, struct thread *td)
860987e5972SCameron Grant {
86166ef8af5SCameron Grant struct snd_dbuf *bs = c->bufsoft;
862a0b57fb7SCameron Grant int ret;
863a0b57fb7SCameron Grant
864b8f0d9e0SCameron Grant CHN_LOCKASSERT(c);
86590da2b28SAriff Abdullah
86690da2b28SAriff Abdullah if (!(c->flags & (CHN_F_MMAP | CHN_F_TRIGGERED))) {
867e4e61333SAriff Abdullah ret = chn_start(c, 1);
868e4e61333SAriff Abdullah if (ret != 0)
869e4e61333SAriff Abdullah return (0);
870e4e61333SAriff Abdullah }
87190da2b28SAriff Abdullah
872a0b57fb7SCameron Grant ret = 0;
87390da2b28SAriff Abdullah if (chn_polltrigger(c)) {
87490da2b28SAriff Abdullah chn_pollreset(c);
875a0b57fb7SCameron Grant ret = ev;
87690da2b28SAriff Abdullah } else
877ed01445dSJohn Baldwin selrecord(td, sndbuf_getsel(bs));
87890da2b28SAriff Abdullah
879e4e61333SAriff Abdullah return (ret);
880987e5972SCameron Grant }
881987e5972SCameron Grant
882987e5972SCameron Grant /*
88366ef8af5SCameron Grant * chn_abort terminates a running dma transfer. it may sleep up to 200ms.
88466ef8af5SCameron Grant * it returns the number of bytes that have not been transferred.
88566ef8af5SCameron Grant *
886285648f9SCameron Grant * called from: dsp_close, dsp_ioctl, with channel locked
887987e5972SCameron Grant */
888987e5972SCameron Grant int
chn_abort(struct pcm_channel * c)88966ef8af5SCameron Grant chn_abort(struct pcm_channel *c)
890987e5972SCameron Grant {
891d95502a8SCameron Grant int missing = 0;
89266ef8af5SCameron Grant struct snd_dbuf *b = c->bufhard;
89366ef8af5SCameron Grant struct snd_dbuf *bs = c->bufsoft;
894987e5972SCameron Grant
89566ef8af5SCameron Grant CHN_LOCKASSERT(c);
896fd1475d3SAriff Abdullah if (CHN_STOPPED(c))
89720a874f1SCameron Grant return 0;
898f4a31ab8SCameron Grant c->flags |= CHN_F_ABORTING;
89966ef8af5SCameron Grant
90066ef8af5SCameron Grant c->flags &= ~CHN_F_TRIGGERED;
90166ef8af5SCameron Grant /* kill the channel */
902f4a31ab8SCameron Grant chn_trigger(c, PCMTRIG_ABORT);
90366ef8af5SCameron Grant sndbuf_setrun(b, 0);
90449c5e6e2SCameron Grant if (!(c->flags & CHN_F_VIRTUAL))
905a0b57fb7SCameron Grant chn_dmaupdate(c);
906fd1475d3SAriff Abdullah missing = sndbuf_getready(bs);
90766ef8af5SCameron Grant
90866ef8af5SCameron Grant c->flags &= ~CHN_F_ABORTING;
909987e5972SCameron Grant return missing;
910987e5972SCameron Grant }
911987e5972SCameron Grant
912987e5972SCameron Grant /*
913987e5972SCameron Grant * this routine tries to flush the dma transfer. It is called
914a5dc5888SCameron Grant * on a close of a playback channel.
915a5dc5888SCameron Grant * first, if there is data in the buffer, but the dma has not yet
916a5dc5888SCameron Grant * begun, we need to start it.
917a5dc5888SCameron Grant * next, we wait for the play buffer to drain
918a5dc5888SCameron Grant * finally, we stop the dma.
91966ef8af5SCameron Grant *
920a5dc5888SCameron Grant * called from: dsp_close, not valid for record channels.
921987e5972SCameron Grant */
922987e5972SCameron Grant
923987e5972SCameron Grant int
chn_flush(struct pcm_channel * c)92466ef8af5SCameron Grant chn_flush(struct pcm_channel *c)
925987e5972SCameron Grant {
92666ef8af5SCameron Grant struct snd_dbuf *b = c->bufhard;
927987e5972SCameron Grant
92866ef8af5SCameron Grant CHN_LOCKASSERT(c);
929a5dc5888SCameron Grant KASSERT(c->direction == PCMDIR_PLAY, ("chn_flush on bad channel"));
930a5dc5888SCameron Grant DEB(printf("chn_flush: c->flags 0x%08x\n", c->flags));
931a5dc5888SCameron Grant
932987e5972SCameron Grant c->flags |= CHN_F_CLOSING;
933a580b31aSAriff Abdullah chn_sync(c, 0);
93466ef8af5SCameron Grant c->flags &= ~CHN_F_TRIGGERED;
93566ef8af5SCameron Grant /* kill the channel */
93666ef8af5SCameron Grant chn_trigger(c, PCMTRIG_ABORT);
93766ef8af5SCameron Grant sndbuf_setrun(b, 0);
93866ef8af5SCameron Grant
93920a874f1SCameron Grant c->flags &= ~CHN_F_CLOSING;
940987e5972SCameron Grant return 0;
941987e5972SCameron Grant }
942987e5972SCameron Grant
943987e5972SCameron Grant int
snd_fmtvalid(uint32_t fmt,uint32_t * fmtlist)94490da2b28SAriff Abdullah snd_fmtvalid(uint32_t fmt, uint32_t *fmtlist)
945c9c6ba09SCameron Grant {
946c9c6ba09SCameron Grant int i;
947c9c6ba09SCameron Grant
94890da2b28SAriff Abdullah for (i = 0; fmtlist[i] != 0; i++) {
94990da2b28SAriff Abdullah if (fmt == fmtlist[i] ||
95090da2b28SAriff Abdullah ((fmt & AFMT_PASSTHROUGH) &&
95190da2b28SAriff Abdullah (AFMT_ENCODING(fmt) & fmtlist[i])))
95290da2b28SAriff Abdullah return (1);
953c9c6ba09SCameron Grant }
954c9c6ba09SCameron Grant
95590da2b28SAriff Abdullah return (0);
95690da2b28SAriff Abdullah }
95790da2b28SAriff Abdullah
95890da2b28SAriff Abdullah static const struct {
95990da2b28SAriff Abdullah char *name, *alias1, *alias2;
96090da2b28SAriff Abdullah uint32_t afmt;
96190da2b28SAriff Abdullah } afmt_tab[] = {
96290da2b28SAriff Abdullah { "alaw", NULL, NULL, AFMT_A_LAW },
96390da2b28SAriff Abdullah { "mulaw", NULL, NULL, AFMT_MU_LAW },
96490da2b28SAriff Abdullah { "u8", "8", NULL, AFMT_U8 },
96590da2b28SAriff Abdullah { "s8", NULL, NULL, AFMT_S8 },
966a4aff024SChristos Margiolis { "ac3", NULL, NULL, AFMT_AC3 },
96790da2b28SAriff Abdullah #if BYTE_ORDER == LITTLE_ENDIAN
96890da2b28SAriff Abdullah { "s16le", "s16", "16", AFMT_S16_LE },
96990da2b28SAriff Abdullah { "s16be", NULL, NULL, AFMT_S16_BE },
970a4aff024SChristos Margiolis { "s24le", "s24", "24", AFMT_S24_LE },
97190da2b28SAriff Abdullah { "s24be", NULL, NULL, AFMT_S24_BE },
97290da2b28SAriff Abdullah { "s32le", "s32", "32", AFMT_S32_LE },
97390da2b28SAriff Abdullah { "s32be", NULL, NULL, AFMT_S32_BE },
974e1bbaa71SChristos Margiolis { "f32le", "f32", NULL, AFMT_F32_LE },
975e1bbaa71SChristos Margiolis { "f32be", NULL, NULL, AFMT_F32_BE },
976a4aff024SChristos Margiolis { "u16le", "u16", NULL, AFMT_U16_LE },
977a4aff024SChristos Margiolis { "u16be", NULL, NULL, AFMT_U16_BE },
978a4aff024SChristos Margiolis { "u24le", "u24", NULL, AFMT_U24_LE },
979a4aff024SChristos Margiolis { "u24be", NULL, NULL, AFMT_U24_BE },
980a4aff024SChristos Margiolis { "u32le", "u32", NULL, AFMT_U32_LE },
981a4aff024SChristos Margiolis { "u32be", NULL, NULL, AFMT_U32_BE },
98290da2b28SAriff Abdullah #else
983a4aff024SChristos Margiolis { "s16le", NULL, NULL, AFMT_S16_LE },
984a4aff024SChristos Margiolis { "s16be", "s16", "16", AFMT_S16_BE },
985a4aff024SChristos Margiolis { "s24le", NULL, NULL, AFMT_S24_LE },
986a4aff024SChristos Margiolis { "s24be", "s24", "24", AFMT_S24_BE },
98790da2b28SAriff Abdullah { "s32le", NULL, NULL, AFMT_S32_LE },
98890da2b28SAriff Abdullah { "s32be", "s32", "32", AFMT_S32_BE },
989e1bbaa71SChristos Margiolis { "f32le", NULL, NULL, AFMT_F32_LE },
990e1bbaa71SChristos Margiolis { "f32be", "f32", NULL, AFMT_F32_BE },
991a4aff024SChristos Margiolis { "u16le", NULL, NULL, AFMT_U16_LE },
992a4aff024SChristos Margiolis { "u16be", "u16", NULL, AFMT_U16_BE },
993a4aff024SChristos Margiolis { "u24le", NULL, NULL, AFMT_U24_LE },
994a4aff024SChristos Margiolis { "u24be", "u24", NULL, AFMT_U24_BE },
99590da2b28SAriff Abdullah { "u32le", NULL, NULL, AFMT_U32_LE },
996a4aff024SChristos Margiolis { "u32be", "u32", NULL, AFMT_U32_BE },
997a4aff024SChristos Margiolis #endif
99890da2b28SAriff Abdullah { NULL, NULL, NULL, 0 }
999a580b31aSAriff Abdullah };
1000a580b31aSAriff Abdullah
100190da2b28SAriff Abdullah uint32_t
snd_str2afmt(const char * req)100290da2b28SAriff Abdullah snd_str2afmt(const char *req)
1003a580b31aSAriff Abdullah {
10044ece1a88SHans Petter Selasky int ext;
10054ece1a88SHans Petter Selasky int ch;
10064ece1a88SHans Petter Selasky int i;
10074ece1a88SHans Petter Selasky char b1[8];
10084ece1a88SHans Petter Selasky char b2[8];
10094ece1a88SHans Petter Selasky
10104ece1a88SHans Petter Selasky memset(b1, 0, sizeof(b1));
10114ece1a88SHans Petter Selasky memset(b2, 0, sizeof(b2));
101290da2b28SAriff Abdullah
101390da2b28SAriff Abdullah i = sscanf(req, "%5[^:]:%6s", b1, b2);
101490da2b28SAriff Abdullah
101590da2b28SAriff Abdullah if (i == 1) {
101690da2b28SAriff Abdullah if (strlen(req) != strlen(b1))
101790da2b28SAriff Abdullah return (0);
101890da2b28SAriff Abdullah strlcpy(b2, "2.0", sizeof(b2));
101990da2b28SAriff Abdullah } else if (i == 2) {
102090da2b28SAriff Abdullah if (strlen(req) != (strlen(b1) + 1 + strlen(b2)))
102190da2b28SAriff Abdullah return (0);
102290da2b28SAriff Abdullah } else
102390da2b28SAriff Abdullah return (0);
102490da2b28SAriff Abdullah
10254ece1a88SHans Petter Selasky i = sscanf(b2, "%d.%d", &ch, &ext);
102690da2b28SAriff Abdullah
10274ece1a88SHans Petter Selasky if (i == 0) {
10284ece1a88SHans Petter Selasky if (strcasecmp(b2, "mono") == 0) {
10294ece1a88SHans Petter Selasky ch = 1;
10304ece1a88SHans Petter Selasky ext = 0;
10314ece1a88SHans Petter Selasky } else if (strcasecmp(b2, "stereo") == 0) {
10324ece1a88SHans Petter Selasky ch = 2;
10334ece1a88SHans Petter Selasky ext = 0;
10344ece1a88SHans Petter Selasky } else if (strcasecmp(b2, "quad") == 0) {
10354ece1a88SHans Petter Selasky ch = 4;
10364ece1a88SHans Petter Selasky ext = 0;
10374ece1a88SHans Petter Selasky } else
10384ece1a88SHans Petter Selasky return (0);
10394ece1a88SHans Petter Selasky } else if (i == 1) {
10404ece1a88SHans Petter Selasky if (ch < 1 || ch > AFMT_CHANNEL_MAX)
10414ece1a88SHans Petter Selasky return (0);
10424ece1a88SHans Petter Selasky ext = 0;
10434ece1a88SHans Petter Selasky } else if (i == 2) {
10444ece1a88SHans Petter Selasky if (ext < 0 || ext > AFMT_EXTCHANNEL_MAX)
10454ece1a88SHans Petter Selasky return (0);
10464ece1a88SHans Petter Selasky if (ch < 1 || (ch + ext) > AFMT_CHANNEL_MAX)
10474ece1a88SHans Petter Selasky return (0);
10484ece1a88SHans Petter Selasky } else
104990da2b28SAriff Abdullah return (0);
105090da2b28SAriff Abdullah
10514ece1a88SHans Petter Selasky for (i = 0; afmt_tab[i].name != NULL; i++) {
10524ece1a88SHans Petter Selasky if (strcasecmp(afmt_tab[i].name, b1) != 0) {
10534ece1a88SHans Petter Selasky if (afmt_tab[i].alias1 == NULL)
10544ece1a88SHans Petter Selasky continue;
10554ece1a88SHans Petter Selasky if (strcasecmp(afmt_tab[i].alias1, b1) != 0) {
10564ece1a88SHans Petter Selasky if (afmt_tab[i].alias2 == NULL)
10574ece1a88SHans Petter Selasky continue;
10584ece1a88SHans Petter Selasky if (strcasecmp(afmt_tab[i].alias2, b1) != 0)
10594ece1a88SHans Petter Selasky continue;
106090da2b28SAriff Abdullah }
106190da2b28SAriff Abdullah }
10624ece1a88SHans Petter Selasky /* found a match */
10634ece1a88SHans Petter Selasky return (SND_FORMAT(afmt_tab[i].afmt, ch + ext, ext));
10644ece1a88SHans Petter Selasky }
10654ece1a88SHans Petter Selasky /* not a valid format */
106690da2b28SAriff Abdullah return (0);
106790da2b28SAriff Abdullah }
106890da2b28SAriff Abdullah
106990da2b28SAriff Abdullah uint32_t
snd_afmt2str(uint32_t afmt,char * buf,size_t len)107090da2b28SAriff Abdullah snd_afmt2str(uint32_t afmt, char *buf, size_t len)
107190da2b28SAriff Abdullah {
10724ece1a88SHans Petter Selasky uint32_t enc;
10734ece1a88SHans Petter Selasky uint32_t ext;
10744ece1a88SHans Petter Selasky uint32_t ch;
10754ece1a88SHans Petter Selasky int i;
107690da2b28SAriff Abdullah
107790da2b28SAriff Abdullah if (buf == NULL || len < AFMTSTR_LEN)
107890da2b28SAriff Abdullah return (0);
107990da2b28SAriff Abdullah
10804ece1a88SHans Petter Selasky memset(buf, 0, len);
108190da2b28SAriff Abdullah
108290da2b28SAriff Abdullah enc = AFMT_ENCODING(afmt);
108390da2b28SAriff Abdullah ch = AFMT_CHANNEL(afmt);
108490da2b28SAriff Abdullah ext = AFMT_EXTCHANNEL(afmt);
10854ece1a88SHans Petter Selasky /* check there is at least one channel */
10864ece1a88SHans Petter Selasky if (ch <= ext)
10874ece1a88SHans Petter Selasky return (0);
108890da2b28SAriff Abdullah for (i = 0; afmt_tab[i].name != NULL; i++) {
10894ece1a88SHans Petter Selasky if (enc != afmt_tab[i].afmt)
10904ece1a88SHans Petter Selasky continue;
10914ece1a88SHans Petter Selasky /* found a match */
10924ece1a88SHans Petter Selasky snprintf(buf, len, "%s:%d.%d",
10934ece1a88SHans Petter Selasky afmt_tab[i].name, ch - ext, ext);
10944ece1a88SHans Petter Selasky return (SND_FORMAT(enc, ch, ext));
109590da2b28SAriff Abdullah }
109690da2b28SAriff Abdullah return (0);
1097a580b31aSAriff Abdullah }
1098a580b31aSAriff Abdullah
1099a580b31aSAriff Abdullah int
chn_reset(struct pcm_channel * c,uint32_t fmt,uint32_t spd)110090da2b28SAriff Abdullah chn_reset(struct pcm_channel *c, uint32_t fmt, uint32_t spd)
1101a580b31aSAriff Abdullah {
110290da2b28SAriff Abdullah int r;
1103603ddb6dSCameron Grant
110466ef8af5SCameron Grant CHN_LOCKASSERT(c);
1105a580b31aSAriff Abdullah c->feedcount = 0;
1106987e5972SCameron Grant c->flags &= CHN_F_RESET;
1107a3285889SCameron Grant c->interrupts = 0;
1108a580b31aSAriff Abdullah c->timeout = 1;
1109a3285889SCameron Grant c->xruns = 0;
11101e59d53cSCameron Grant
111190da2b28SAriff Abdullah c->flags |= (pcm_getflags(c->dev) & SD_F_BITPERFECT) ?
111290da2b28SAriff Abdullah CHN_F_BITPERFECT : 0;
1113612276f4SCameron Grant
111490da2b28SAriff Abdullah r = CHANNEL_RESET(c->methods, c->devinfo);
111590da2b28SAriff Abdullah if (r == 0 && fmt != 0 && spd != 0) {
111690da2b28SAriff Abdullah r = chn_setparam(c, fmt, spd);
111790da2b28SAriff Abdullah fmt = 0;
111890da2b28SAriff Abdullah spd = 0;
11198ae4c159SCameron Grant }
112090da2b28SAriff Abdullah if (r == 0 && fmt != 0)
112190da2b28SAriff Abdullah r = chn_setformat(c, fmt);
112290da2b28SAriff Abdullah if (r == 0 && spd != 0)
112390da2b28SAriff Abdullah r = chn_setspeed(c, spd);
11241e59d53cSCameron Grant if (r == 0)
1125a580b31aSAriff Abdullah r = chn_setlatency(c, chn_latency);
112666ef8af5SCameron Grant if (r == 0) {
1127987e5972SCameron Grant chn_resetbuf(c);
11281e59d53cSCameron Grant r = CHANNEL_RESETDONE(c->methods, c->devinfo);
112966ef8af5SCameron Grant }
113066ef8af5SCameron Grant return r;
1131987e5972SCameron Grant }
1132d55a1446SCameron Grant
1133ad4c8671SChristos Margiolis static struct unrhdr *
chn_getunr(struct snddev_info * d,int type)1134ad4c8671SChristos Margiolis chn_getunr(struct snddev_info *d, int type)
1135ad4c8671SChristos Margiolis {
1136ad4c8671SChristos Margiolis switch (type) {
1137802c78f5SChristos Margiolis case PCMDIR_PLAY:
1138ad4c8671SChristos Margiolis return (d->p_unr);
1139802c78f5SChristos Margiolis case PCMDIR_PLAY_VIRTUAL:
1140ad4c8671SChristos Margiolis return (d->vp_unr);
1141802c78f5SChristos Margiolis case PCMDIR_REC:
1142ad4c8671SChristos Margiolis return (d->r_unr);
1143802c78f5SChristos Margiolis case PCMDIR_REC_VIRTUAL:
1144ad4c8671SChristos Margiolis return (d->vr_unr);
1145ad4c8671SChristos Margiolis default:
1146ad4c8671SChristos Margiolis __assert_unreachable();
1147ad4c8671SChristos Margiolis }
1148ad4c8671SChristos Margiolis
1149ad4c8671SChristos Margiolis }
1150ad4c8671SChristos Margiolis
1151802c78f5SChristos Margiolis char *
chn_mkname(char * buf,size_t len,struct pcm_channel * c)1152802c78f5SChristos Margiolis chn_mkname(char *buf, size_t len, struct pcm_channel *c)
1153802c78f5SChristos Margiolis {
1154802c78f5SChristos Margiolis const char *str;
1155802c78f5SChristos Margiolis
1156802c78f5SChristos Margiolis KASSERT(buf != NULL && len != 0,
1157b1bb6934SChristos Margiolis ("%s(): bogus buf=%p len=%zu", __func__, buf, len));
1158802c78f5SChristos Margiolis
1159802c78f5SChristos Margiolis switch (c->type) {
1160802c78f5SChristos Margiolis case PCMDIR_PLAY:
1161802c78f5SChristos Margiolis str = "play";
1162802c78f5SChristos Margiolis break;
1163802c78f5SChristos Margiolis case PCMDIR_PLAY_VIRTUAL:
1164802c78f5SChristos Margiolis str = "virtual_play";
1165802c78f5SChristos Margiolis break;
1166802c78f5SChristos Margiolis case PCMDIR_REC:
1167802c78f5SChristos Margiolis str = "record";
1168802c78f5SChristos Margiolis break;
1169802c78f5SChristos Margiolis case PCMDIR_REC_VIRTUAL:
1170802c78f5SChristos Margiolis str = "virtual_record";
1171802c78f5SChristos Margiolis break;
1172802c78f5SChristos Margiolis default:
1173802c78f5SChristos Margiolis __assert_unreachable();
1174802c78f5SChristos Margiolis }
1175802c78f5SChristos Margiolis
1176802c78f5SChristos Margiolis snprintf(buf, len, "dsp%d.%s.%d",
1177802c78f5SChristos Margiolis device_get_unit(c->dev), str, c->unit);
1178802c78f5SChristos Margiolis
1179802c78f5SChristos Margiolis return (buf);
1180802c78f5SChristos Margiolis }
1181802c78f5SChristos Margiolis
11822e9962efSChristos Margiolis struct pcm_channel *
chn_init(struct snddev_info * d,struct pcm_channel * parent,kobj_class_t cls,int dir,void * devinfo)11832e9962efSChristos Margiolis chn_init(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls,
11843af2beb8SChristos Margiolis int dir, void *devinfo)
1185987e5972SCameron Grant {
11862e9962efSChristos Margiolis struct pcm_channel *c;
11870f55ac6cSCameron Grant struct feeder_class *fc;
118866ef8af5SCameron Grant struct snd_dbuf *b, *bs;
1189c30f531dSChristos Margiolis char buf[CHN_NAMELEN];
1190e372211bSChristos Margiolis int err, i, direction, *vchanrate, *vchanformat;
11915f070b67SCameron Grant
11922e9962efSChristos Margiolis PCM_BUSYASSERT(d);
11932e9962efSChristos Margiolis PCM_LOCKASSERT(d);
11942e9962efSChristos Margiolis
11952e9962efSChristos Margiolis switch (dir) {
11962e9962efSChristos Margiolis case PCMDIR_PLAY:
1197bc7e65e9SChristos Margiolis d->playcount++;
1198bc7e65e9SChristos Margiolis /* FALLTHROUGH */
11992e9962efSChristos Margiolis case PCMDIR_PLAY_VIRTUAL:
1200bc7e65e9SChristos Margiolis if (dir == PCMDIR_PLAY_VIRTUAL)
1201bc7e65e9SChristos Margiolis d->pvchancount++;
12022e9962efSChristos Margiolis direction = PCMDIR_PLAY;
1203e372211bSChristos Margiolis vchanrate = &d->pvchanrate;
1204e372211bSChristos Margiolis vchanformat = &d->pvchanformat;
12052e9962efSChristos Margiolis break;
12062e9962efSChristos Margiolis case PCMDIR_REC:
1207bc7e65e9SChristos Margiolis d->reccount++;
1208bc7e65e9SChristos Margiolis /* FALLTHROUGH */
12092e9962efSChristos Margiolis case PCMDIR_REC_VIRTUAL:
1210bc7e65e9SChristos Margiolis if (dir == PCMDIR_REC_VIRTUAL)
1211bc7e65e9SChristos Margiolis d->rvchancount++;
12122e9962efSChristos Margiolis direction = PCMDIR_REC;
1213e372211bSChristos Margiolis vchanrate = &d->rvchanrate;
1214e372211bSChristos Margiolis vchanformat = &d->rvchanformat;
12152e9962efSChristos Margiolis break;
12162e9962efSChristos Margiolis default:
12172e9962efSChristos Margiolis device_printf(d->dev,
12182e9962efSChristos Margiolis "%s(): invalid channel direction: %d\n",
12192e9962efSChristos Margiolis __func__, dir);
12205b209e15SChristos Margiolis return (NULL);
12215b209e15SChristos Margiolis }
12225b209e15SChristos Margiolis
12232e9962efSChristos Margiolis PCM_UNLOCK(d);
12245d1a5d6fSChristos Margiolis b = NULL;
12255d1a5d6fSChristos Margiolis bs = NULL;
12265b209e15SChristos Margiolis
12272e9962efSChristos Margiolis c = malloc(sizeof(*c), M_DEVBUF, M_WAITOK | M_ZERO);
12282e9962efSChristos Margiolis c->methods = kobj_create(cls, M_DEVBUF, M_WAITOK | M_ZERO);
12295b209e15SChristos Margiolis chn_lockinit(c, dir);
12305b209e15SChristos Margiolis CHN_INIT(c, children);
12315b209e15SChristos Margiolis CHN_INIT(c, children.busy);
12325b209e15SChristos Margiolis c->direction = direction;
1233802c78f5SChristos Margiolis c->type = dir;
1234ad4c8671SChristos Margiolis c->unit = alloc_unr(chn_getunr(d, c->type));
1235e89ee05bSChristos Margiolis c->format = SND_FORMAT(AFMT_S16_LE, 2, 0);
1236e89ee05bSChristos Margiolis c->speed = 48000;
12372e9962efSChristos Margiolis c->pid = -1;
12385b209e15SChristos Margiolis c->latency = -1;
12395b209e15SChristos Margiolis c->timeout = 1;
12402e9962efSChristos Margiolis strlcpy(c->comm, CHN_COMM_UNUSED, sizeof(c->comm));
12412e9962efSChristos Margiolis c->parentsnddev = d;
12422e9962efSChristos Margiolis c->parentchannel = parent;
12432e9962efSChristos Margiolis c->dev = d->dev;
12442e9962efSChristos Margiolis c->trigger = PCMTRIG_STOP;
1245802c78f5SChristos Margiolis strlcpy(c->name, chn_mkname(buf, sizeof(buf), c), sizeof(c->name));
124690da2b28SAriff Abdullah
124790da2b28SAriff Abdullah c->matrix = *feeder_matrix_id_map(SND_CHN_MATRIX_1_0);
124890da2b28SAriff Abdullah c->matrix.id = SND_CHN_MATRIX_PCMCHANNEL;
124990da2b28SAriff Abdullah
12505b209e15SChristos Margiolis for (i = 0; i < SND_CHN_T_MAX; i++)
125190da2b28SAriff Abdullah c->volume[SND_VOL_C_MASTER][i] = SND_VOL_0DB_MASTER;
125290da2b28SAriff Abdullah
125390da2b28SAriff Abdullah c->volume[SND_VOL_C_MASTER][SND_CHN_T_VOL_0DB] = SND_VOL_0DB_MASTER;
125490da2b28SAriff Abdullah c->volume[SND_VOL_C_PCM][SND_CHN_T_VOL_0DB] = chn_vol_0db_pcm;
125590da2b28SAriff Abdullah
125612e524a2SDon Lewis CHN_LOCK(c);
12575b209e15SChristos Margiolis chn_vpc_reset(c, SND_VOL_C_PCM, 1);
12585b209e15SChristos Margiolis CHN_UNLOCK(c);
12595b209e15SChristos Margiolis
12605b209e15SChristos Margiolis fc = feeder_getclass(NULL);
12615b209e15SChristos Margiolis if (fc == NULL) {
12625b209e15SChristos Margiolis device_printf(d->dev, "%s(): failed to get feeder class\n",
12635b209e15SChristos Margiolis __func__);
12645b209e15SChristos Margiolis goto fail;
12655b209e15SChristos Margiolis }
12665b209e15SChristos Margiolis if (feeder_add(c, fc, NULL)) {
12675b209e15SChristos Margiolis device_printf(d->dev, "%s(): failed to add feeder\n", __func__);
12685b209e15SChristos Margiolis goto fail;
12692e9962efSChristos Margiolis }
12701e59d53cSCameron Grant
12715b209e15SChristos Margiolis b = sndbuf_create(c->dev, c->name, "primary", c);
12725b209e15SChristos Margiolis bs = sndbuf_create(c->dev, c->name, "secondary", c);
12735b209e15SChristos Margiolis if (b == NULL || bs == NULL) {
12745b209e15SChristos Margiolis device_printf(d->dev, "%s(): failed to create %s buffer\n",
12755b209e15SChristos Margiolis __func__, b == NULL ? "hardware" : "software");
12765b209e15SChristos Margiolis goto fail;
12775b209e15SChristos Margiolis }
12785b209e15SChristos Margiolis c->bufhard = b;
12795b209e15SChristos Margiolis c->bufsoft = bs;
12805b209e15SChristos Margiolis
12815b209e15SChristos Margiolis c->devinfo = CHANNEL_INIT(c->methods, devinfo, b, c, direction);
12825b209e15SChristos Margiolis if (c->devinfo == NULL) {
12835b209e15SChristos Margiolis device_printf(d->dev, "%s(): CHANNEL_INIT() failed\n", __func__);
12845b209e15SChristos Margiolis goto fail;
12855b209e15SChristos Margiolis }
12865b209e15SChristos Margiolis
12872e9962efSChristos Margiolis if ((sndbuf_getsize(b) == 0) && ((c->flags & CHN_F_VIRTUAL) == 0)) {
12882e9962efSChristos Margiolis device_printf(d->dev, "%s(): hardware buffer's size is 0\n",
12892e9962efSChristos Margiolis __func__);
12905b209e15SChristos Margiolis goto fail;
12912e9962efSChristos Margiolis }
12921e59d53cSCameron Grant
129390da2b28SAriff Abdullah sndbuf_setfmt(b, c->format);
129490da2b28SAriff Abdullah sndbuf_setspd(b, c->speed);
129590da2b28SAriff Abdullah sndbuf_setfmt(bs, c->format);
129690da2b28SAriff Abdullah sndbuf_setspd(bs, c->speed);
12975b209e15SChristos Margiolis sndbuf_setup(bs, NULL, 0);
12981e59d53cSCameron Grant
1299b611c801SAlexander Leidinger /**
1300b611c801SAlexander Leidinger * @todo Should this be moved somewhere else? The primary buffer
1301b611c801SAlexander Leidinger * is allocated by the driver or via DMA map setup, and tmpbuf
1302b611c801SAlexander Leidinger * seems to only come into existence in sndbuf_resize().
1303b611c801SAlexander Leidinger */
1304b611c801SAlexander Leidinger if (c->direction == PCMDIR_PLAY) {
1305b611c801SAlexander Leidinger bs->sl = sndbuf_getmaxsize(bs);
13065e33eca8SChristos Margiolis bs->shadbuf = malloc(bs->sl, M_DEVBUF, M_WAITOK);
1307b611c801SAlexander Leidinger }
13081e59d53cSCameron Grant
130902d4eeabSChristos Margiolis if ((c->flags & CHN_F_VIRTUAL) == 0) {
131002d4eeabSChristos Margiolis CHN_LOCK(c);
131102d4eeabSChristos Margiolis err = chn_reset(c, c->format, c->speed);
131202d4eeabSChristos Margiolis CHN_UNLOCK(c);
131302d4eeabSChristos Margiolis if (err != 0)
131402d4eeabSChristos Margiolis goto fail;
131502d4eeabSChristos Margiolis }
131602d4eeabSChristos Margiolis
13172e9962efSChristos Margiolis PCM_LOCK(d);
13189263f854SChristos Margiolis CHN_INSERT_SORT_ASCEND(d, c, channels.pcm);
1319e372211bSChristos Margiolis if ((c->flags & CHN_F_VIRTUAL) == 0) {
132002d4eeabSChristos Margiolis CHN_INSERT_SORT_ASCEND(d, c, channels.pcm.primary);
1321e372211bSChristos Margiolis /* Initialize the *vchanrate/vchanformat parameters. */
1322e372211bSChristos Margiolis *vchanrate = sndbuf_getspd(c->bufsoft);
1323e372211bSChristos Margiolis *vchanformat = sndbuf_getfmt(c->bufsoft);
1324e372211bSChristos Margiolis }
13259263f854SChristos Margiolis
13262e9962efSChristos Margiolis return (c);
13275b209e15SChristos Margiolis
13285b209e15SChristos Margiolis fail:
1329bc7e65e9SChristos Margiolis chn_kill(c);
13305b209e15SChristos Margiolis PCM_LOCK(d);
13315b209e15SChristos Margiolis
13322e9962efSChristos Margiolis return (NULL);
1333987e5972SCameron Grant }
1334987e5972SCameron Grant
1335b3ea087cSChristos Margiolis void
chn_kill(struct pcm_channel * c)133666ef8af5SCameron Grant chn_kill(struct pcm_channel *c)
133733dbf14aSCameron Grant {
13389263f854SChristos Margiolis struct snddev_info *d = c->parentsnddev;
133966ef8af5SCameron Grant struct snd_dbuf *b = c->bufhard;
134066ef8af5SCameron Grant struct snd_dbuf *bs = c->bufsoft;
134166ef8af5SCameron Grant
1342b3ea087cSChristos Margiolis PCM_BUSYASSERT(c->parentsnddev);
1343b3ea087cSChristos Margiolis
13449263f854SChristos Margiolis PCM_LOCK(d);
13459263f854SChristos Margiolis CHN_REMOVE(d, c, channels.pcm);
134602d4eeabSChristos Margiolis if ((c->flags & CHN_F_VIRTUAL) == 0)
134702d4eeabSChristos Margiolis CHN_REMOVE(d, c, channels.pcm.primary);
13489263f854SChristos Margiolis
13499263f854SChristos Margiolis switch (c->type) {
1350802c78f5SChristos Margiolis case PCMDIR_PLAY:
13519263f854SChristos Margiolis d->playcount--;
13529263f854SChristos Margiolis break;
1353802c78f5SChristos Margiolis case PCMDIR_PLAY_VIRTUAL:
13549263f854SChristos Margiolis d->pvchancount--;
13559263f854SChristos Margiolis break;
1356802c78f5SChristos Margiolis case PCMDIR_REC:
13579263f854SChristos Margiolis d->reccount--;
13589263f854SChristos Margiolis break;
1359802c78f5SChristos Margiolis case PCMDIR_REC_VIRTUAL:
13609263f854SChristos Margiolis d->rvchancount--;
13619263f854SChristos Margiolis break;
13629263f854SChristos Margiolis default:
13639263f854SChristos Margiolis __assert_unreachable();
13649263f854SChristos Margiolis }
13659263f854SChristos Margiolis PCM_UNLOCK(d);
13669263f854SChristos Margiolis
1367bba4862cSAriff Abdullah if (CHN_STARTED(c)) {
1368bba4862cSAriff Abdullah CHN_LOCK(c);
136933dbf14aSCameron Grant chn_trigger(c, PCMTRIG_ABORT);
1370bba4862cSAriff Abdullah CHN_UNLOCK(c);
1371bba4862cSAriff Abdullah }
1372bc7e65e9SChristos Margiolis free_unr(chn_getunr(d, c->type), c->unit);
137300172d20SChristos Margiolis feeder_remove(c);
1374bc7e65e9SChristos Margiolis if (c->devinfo && CHANNEL_FREE(c->methods, c->devinfo))
1375f8db81f3SCameron Grant sndbuf_free(b);
1376bc7e65e9SChristos Margiolis if (bs)
137766ef8af5SCameron Grant sndbuf_destroy(bs);
1378bc7e65e9SChristos Margiolis if (b)
137966ef8af5SCameron Grant sndbuf_destroy(b);
1380e4e61333SAriff Abdullah CHN_LOCK(c);
1381e4e61333SAriff Abdullah c->flags |= CHN_F_DEAD;
138266ef8af5SCameron Grant chn_lockdestroy(c);
1383b3ea087cSChristos Margiolis kobj_delete(c->methods, M_DEVBUF);
1384b3ea087cSChristos Margiolis free(c, M_DEVBUF);
138533dbf14aSCameron Grant }
138633dbf14aSCameron Grant
138703614fcbSChristos Margiolis void
chn_shutdown(struct pcm_channel * c)138803614fcbSChristos Margiolis chn_shutdown(struct pcm_channel *c)
138903614fcbSChristos Margiolis {
139003614fcbSChristos Margiolis CHN_LOCKASSERT(c);
139103614fcbSChristos Margiolis
139203614fcbSChristos Margiolis chn_wakeup(c);
139303614fcbSChristos Margiolis c->flags |= CHN_F_DEAD;
139403614fcbSChristos Margiolis }
139503614fcbSChristos Margiolis
1396a24050e2SChristos Margiolis /* release a locked channel and unlock it */
1397a24050e2SChristos Margiolis int
chn_release(struct pcm_channel * c)1398a24050e2SChristos Margiolis chn_release(struct pcm_channel *c)
1399a24050e2SChristos Margiolis {
1400a24050e2SChristos Margiolis PCM_BUSYASSERT(c->parentsnddev);
1401a24050e2SChristos Margiolis CHN_LOCKASSERT(c);
1402a24050e2SChristos Margiolis
1403a24050e2SChristos Margiolis c->flags &= ~CHN_F_BUSY;
1404a24050e2SChristos Margiolis c->pid = -1;
1405a24050e2SChristos Margiolis strlcpy(c->comm, CHN_COMM_UNUSED, sizeof(c->comm));
1406a24050e2SChristos Margiolis CHN_UNLOCK(c);
1407a24050e2SChristos Margiolis
1408a24050e2SChristos Margiolis return (0);
1409a24050e2SChristos Margiolis }
1410a24050e2SChristos Margiolis
1411a24050e2SChristos Margiolis int
chn_setvolume_multi(struct pcm_channel * c,int vc,int left,int right,int center)141290da2b28SAriff Abdullah chn_setvolume_multi(struct pcm_channel *c, int vc, int left, int right,
141390da2b28SAriff Abdullah int center)
141490da2b28SAriff Abdullah {
141590da2b28SAriff Abdullah int i, ret;
141690da2b28SAriff Abdullah
141790da2b28SAriff Abdullah ret = 0;
141890da2b28SAriff Abdullah
141990da2b28SAriff Abdullah for (i = 0; i < SND_CHN_T_MAX; i++) {
142090da2b28SAriff Abdullah if ((1 << i) & SND_CHN_LEFT_MASK)
142190da2b28SAriff Abdullah ret |= chn_setvolume_matrix(c, vc, i, left);
142290da2b28SAriff Abdullah else if ((1 << i) & SND_CHN_RIGHT_MASK)
142390da2b28SAriff Abdullah ret |= chn_setvolume_matrix(c, vc, i, right) << 8;
142490da2b28SAriff Abdullah else
142590da2b28SAriff Abdullah ret |= chn_setvolume_matrix(c, vc, i, center) << 16;
142690da2b28SAriff Abdullah }
142790da2b28SAriff Abdullah
142890da2b28SAriff Abdullah return (ret);
142990da2b28SAriff Abdullah }
143090da2b28SAriff Abdullah
143190da2b28SAriff Abdullah int
chn_setvolume_matrix(struct pcm_channel * c,int vc,int vt,int val)143290da2b28SAriff Abdullah chn_setvolume_matrix(struct pcm_channel *c, int vc, int vt, int val)
143390da2b28SAriff Abdullah {
143490da2b28SAriff Abdullah int i;
143590da2b28SAriff Abdullah
143690da2b28SAriff Abdullah KASSERT(c != NULL && vc >= SND_VOL_C_MASTER && vc < SND_VOL_C_MAX &&
143790da2b28SAriff Abdullah (vc == SND_VOL_C_MASTER || (vc & 1)) &&
143890da2b28SAriff Abdullah (vt == SND_CHN_T_VOL_0DB || (vt >= SND_CHN_T_BEGIN &&
143990da2b28SAriff Abdullah vt <= SND_CHN_T_END)) && (vt != SND_CHN_T_VOL_0DB ||
144090da2b28SAriff Abdullah (val >= SND_VOL_0DB_MIN && val <= SND_VOL_0DB_MAX)),
144190da2b28SAriff Abdullah ("%s(): invalid volume matrix c=%p vc=%d vt=%d val=%d",
144290da2b28SAriff Abdullah __func__, c, vc, vt, val));
144366ef8af5SCameron Grant CHN_LOCKASSERT(c);
144490da2b28SAriff Abdullah
144590da2b28SAriff Abdullah if (val < 0)
144690da2b28SAriff Abdullah val = 0;
144790da2b28SAriff Abdullah if (val > 100)
144890da2b28SAriff Abdullah val = 100;
144990da2b28SAriff Abdullah
145090da2b28SAriff Abdullah c->volume[vc][vt] = val;
145190da2b28SAriff Abdullah
145290da2b28SAriff Abdullah /*
145390da2b28SAriff Abdullah * Do relative calculation here and store it into class + 1
145490da2b28SAriff Abdullah * to ease the job of feeder_volume.
145590da2b28SAriff Abdullah */
145690da2b28SAriff Abdullah if (vc == SND_VOL_C_MASTER) {
145790da2b28SAriff Abdullah for (vc = SND_VOL_C_BEGIN; vc <= SND_VOL_C_END;
145890da2b28SAriff Abdullah vc += SND_VOL_C_STEP)
145990da2b28SAriff Abdullah c->volume[SND_VOL_C_VAL(vc)][vt] =
146090da2b28SAriff Abdullah SND_VOL_CALC_VAL(c->volume, vc, vt);
146190da2b28SAriff Abdullah } else if (vc & 1) {
146290da2b28SAriff Abdullah if (vt == SND_CHN_T_VOL_0DB)
146390da2b28SAriff Abdullah for (i = SND_CHN_T_BEGIN; i <= SND_CHN_T_END;
146490da2b28SAriff Abdullah i += SND_CHN_T_STEP) {
146590da2b28SAriff Abdullah c->volume[SND_VOL_C_VAL(vc)][i] =
146690da2b28SAriff Abdullah SND_VOL_CALC_VAL(c->volume, vc, i);
146790da2b28SAriff Abdullah }
146890da2b28SAriff Abdullah else
146990da2b28SAriff Abdullah c->volume[SND_VOL_C_VAL(vc)][vt] =
147090da2b28SAriff Abdullah SND_VOL_CALC_VAL(c->volume, vc, vt);
147190da2b28SAriff Abdullah }
147290da2b28SAriff Abdullah
147390da2b28SAriff Abdullah return (val);
147490da2b28SAriff Abdullah }
147590da2b28SAriff Abdullah
147690da2b28SAriff Abdullah int
chn_getvolume_matrix(struct pcm_channel * c,int vc,int vt)147790da2b28SAriff Abdullah chn_getvolume_matrix(struct pcm_channel *c, int vc, int vt)
147890da2b28SAriff Abdullah {
147990da2b28SAriff Abdullah KASSERT(c != NULL && vc >= SND_VOL_C_MASTER && vc < SND_VOL_C_MAX &&
148090da2b28SAriff Abdullah (vt == SND_CHN_T_VOL_0DB ||
148190da2b28SAriff Abdullah (vt >= SND_CHN_T_BEGIN && vt <= SND_CHN_T_END)),
148290da2b28SAriff Abdullah ("%s(): invalid volume matrix c=%p vc=%d vt=%d",
148390da2b28SAriff Abdullah __func__, c, vc, vt));
148490da2b28SAriff Abdullah CHN_LOCKASSERT(c);
148590da2b28SAriff Abdullah
148690da2b28SAriff Abdullah return (c->volume[vc][vt]);
148790da2b28SAriff Abdullah }
148890da2b28SAriff Abdullah
14894a83ca10SHans Petter Selasky int
chn_setmute_multi(struct pcm_channel * c,int vc,int mute)14904a83ca10SHans Petter Selasky chn_setmute_multi(struct pcm_channel *c, int vc, int mute)
14914a83ca10SHans Petter Selasky {
14924a83ca10SHans Petter Selasky int i, ret;
14934a83ca10SHans Petter Selasky
14944a83ca10SHans Petter Selasky ret = 0;
14954a83ca10SHans Petter Selasky
14964a83ca10SHans Petter Selasky for (i = 0; i < SND_CHN_T_MAX; i++) {
14974a83ca10SHans Petter Selasky if ((1 << i) & SND_CHN_LEFT_MASK)
14984a83ca10SHans Petter Selasky ret |= chn_setmute_matrix(c, vc, i, mute);
14994a83ca10SHans Petter Selasky else if ((1 << i) & SND_CHN_RIGHT_MASK)
15004a83ca10SHans Petter Selasky ret |= chn_setmute_matrix(c, vc, i, mute) << 8;
15014a83ca10SHans Petter Selasky else
15024a83ca10SHans Petter Selasky ret |= chn_setmute_matrix(c, vc, i, mute) << 16;
15034a83ca10SHans Petter Selasky }
15044a83ca10SHans Petter Selasky return (ret);
15054a83ca10SHans Petter Selasky }
15064a83ca10SHans Petter Selasky
15074a83ca10SHans Petter Selasky int
chn_setmute_matrix(struct pcm_channel * c,int vc,int vt,int mute)15084a83ca10SHans Petter Selasky chn_setmute_matrix(struct pcm_channel *c, int vc, int vt, int mute)
15094a83ca10SHans Petter Selasky {
15104a83ca10SHans Petter Selasky int i;
15114a83ca10SHans Petter Selasky
15124a83ca10SHans Petter Selasky KASSERT(c != NULL && vc >= SND_VOL_C_MASTER && vc < SND_VOL_C_MAX &&
15134a83ca10SHans Petter Selasky (vc == SND_VOL_C_MASTER || (vc & 1)) &&
15144a83ca10SHans Petter Selasky (vt == SND_CHN_T_VOL_0DB || (vt >= SND_CHN_T_BEGIN && vt <= SND_CHN_T_END)),
15154a83ca10SHans Petter Selasky ("%s(): invalid mute matrix c=%p vc=%d vt=%d mute=%d",
15164a83ca10SHans Petter Selasky __func__, c, vc, vt, mute));
15174a83ca10SHans Petter Selasky
15184a83ca10SHans Petter Selasky CHN_LOCKASSERT(c);
15194a83ca10SHans Petter Selasky
15204a83ca10SHans Petter Selasky mute = (mute != 0);
15214a83ca10SHans Petter Selasky
15224a83ca10SHans Petter Selasky c->muted[vc][vt] = mute;
15234a83ca10SHans Petter Selasky
15244a83ca10SHans Petter Selasky /*
15254a83ca10SHans Petter Selasky * Do relative calculation here and store it into class + 1
15264a83ca10SHans Petter Selasky * to ease the job of feeder_volume.
15274a83ca10SHans Petter Selasky */
15284a83ca10SHans Petter Selasky if (vc == SND_VOL_C_MASTER) {
15294a83ca10SHans Petter Selasky for (vc = SND_VOL_C_BEGIN; vc <= SND_VOL_C_END;
15304a83ca10SHans Petter Selasky vc += SND_VOL_C_STEP)
15314a83ca10SHans Petter Selasky c->muted[SND_VOL_C_VAL(vc)][vt] = mute;
15324a83ca10SHans Petter Selasky } else if (vc & 1) {
15334a83ca10SHans Petter Selasky if (vt == SND_CHN_T_VOL_0DB) {
15344a83ca10SHans Petter Selasky for (i = SND_CHN_T_BEGIN; i <= SND_CHN_T_END;
15354a83ca10SHans Petter Selasky i += SND_CHN_T_STEP) {
15364a83ca10SHans Petter Selasky c->muted[SND_VOL_C_VAL(vc)][i] = mute;
15374a83ca10SHans Petter Selasky }
15384a83ca10SHans Petter Selasky } else {
15394a83ca10SHans Petter Selasky c->muted[SND_VOL_C_VAL(vc)][vt] = mute;
15404a83ca10SHans Petter Selasky }
15414a83ca10SHans Petter Selasky }
15424a83ca10SHans Petter Selasky return (mute);
15434a83ca10SHans Petter Selasky }
15444a83ca10SHans Petter Selasky
15454a83ca10SHans Petter Selasky int
chn_getmute_matrix(struct pcm_channel * c,int vc,int vt)15464a83ca10SHans Petter Selasky chn_getmute_matrix(struct pcm_channel *c, int vc, int vt)
15474a83ca10SHans Petter Selasky {
15484a83ca10SHans Petter Selasky KASSERT(c != NULL && vc >= SND_VOL_C_MASTER && vc < SND_VOL_C_MAX &&
15494a83ca10SHans Petter Selasky (vt == SND_CHN_T_VOL_0DB ||
15504a83ca10SHans Petter Selasky (vt >= SND_CHN_T_BEGIN && vt <= SND_CHN_T_END)),
15514a83ca10SHans Petter Selasky ("%s(): invalid mute matrix c=%p vc=%d vt=%d",
15524a83ca10SHans Petter Selasky __func__, c, vc, vt));
15534a83ca10SHans Petter Selasky CHN_LOCKASSERT(c);
15544a83ca10SHans Petter Selasky
15554a83ca10SHans Petter Selasky return (c->muted[vc][vt]);
15564a83ca10SHans Petter Selasky }
15574a83ca10SHans Petter Selasky
155890da2b28SAriff Abdullah struct pcmchan_matrix *
chn_getmatrix(struct pcm_channel * c)155990da2b28SAriff Abdullah chn_getmatrix(struct pcm_channel *c)
156090da2b28SAriff Abdullah {
156190da2b28SAriff Abdullah
156290da2b28SAriff Abdullah KASSERT(c != NULL, ("%s(): NULL channel", __func__));
156390da2b28SAriff Abdullah CHN_LOCKASSERT(c);
156490da2b28SAriff Abdullah
156590da2b28SAriff Abdullah if (!(c->format & AFMT_CONVERTIBLE))
156690da2b28SAriff Abdullah return (NULL);
156790da2b28SAriff Abdullah
156890da2b28SAriff Abdullah return (&c->matrix);
156990da2b28SAriff Abdullah }
157090da2b28SAriff Abdullah
157190da2b28SAriff Abdullah int
chn_setmatrix(struct pcm_channel * c,struct pcmchan_matrix * m)157290da2b28SAriff Abdullah chn_setmatrix(struct pcm_channel *c, struct pcmchan_matrix *m)
157390da2b28SAriff Abdullah {
157490da2b28SAriff Abdullah
157590da2b28SAriff Abdullah KASSERT(c != NULL && m != NULL,
157690da2b28SAriff Abdullah ("%s(): NULL channel or matrix", __func__));
157790da2b28SAriff Abdullah CHN_LOCKASSERT(c);
157890da2b28SAriff Abdullah
157990da2b28SAriff Abdullah if (!(c->format & AFMT_CONVERTIBLE))
158090da2b28SAriff Abdullah return (EINVAL);
158190da2b28SAriff Abdullah
158290da2b28SAriff Abdullah c->matrix = *m;
158390da2b28SAriff Abdullah c->matrix.id = SND_CHN_MATRIX_PCMCHANNEL;
158490da2b28SAriff Abdullah
158590da2b28SAriff Abdullah return (chn_setformat(c, SND_FORMAT(c->format, m->channels, m->ext)));
158690da2b28SAriff Abdullah }
158790da2b28SAriff Abdullah
158890da2b28SAriff Abdullah /*
158990da2b28SAriff Abdullah * XXX chn_oss_* exists for the sake of compatibility.
159090da2b28SAriff Abdullah */
159190da2b28SAriff Abdullah int
chn_oss_getorder(struct pcm_channel * c,unsigned long long * map)159290da2b28SAriff Abdullah chn_oss_getorder(struct pcm_channel *c, unsigned long long *map)
159390da2b28SAriff Abdullah {
159490da2b28SAriff Abdullah
159590da2b28SAriff Abdullah KASSERT(c != NULL && map != NULL,
159690da2b28SAriff Abdullah ("%s(): NULL channel or map", __func__));
159790da2b28SAriff Abdullah CHN_LOCKASSERT(c);
159890da2b28SAriff Abdullah
159990da2b28SAriff Abdullah if (!(c->format & AFMT_CONVERTIBLE))
160090da2b28SAriff Abdullah return (EINVAL);
160190da2b28SAriff Abdullah
160290da2b28SAriff Abdullah return (feeder_matrix_oss_get_channel_order(&c->matrix, map));
160390da2b28SAriff Abdullah }
160490da2b28SAriff Abdullah
160590da2b28SAriff Abdullah int
chn_oss_setorder(struct pcm_channel * c,unsigned long long * map)160690da2b28SAriff Abdullah chn_oss_setorder(struct pcm_channel *c, unsigned long long *map)
160790da2b28SAriff Abdullah {
160890da2b28SAriff Abdullah struct pcmchan_matrix m;
160990da2b28SAriff Abdullah int ret;
161090da2b28SAriff Abdullah
161190da2b28SAriff Abdullah KASSERT(c != NULL && map != NULL,
161290da2b28SAriff Abdullah ("%s(): NULL channel or map", __func__));
161390da2b28SAriff Abdullah CHN_LOCKASSERT(c);
161490da2b28SAriff Abdullah
161590da2b28SAriff Abdullah if (!(c->format & AFMT_CONVERTIBLE))
161690da2b28SAriff Abdullah return (EINVAL);
161790da2b28SAriff Abdullah
161890da2b28SAriff Abdullah m = c->matrix;
161990da2b28SAriff Abdullah ret = feeder_matrix_oss_set_channel_order(&m, map);
162090da2b28SAriff Abdullah if (ret != 0)
162190da2b28SAriff Abdullah return (ret);
162290da2b28SAriff Abdullah
162390da2b28SAriff Abdullah return (chn_setmatrix(c, &m));
162490da2b28SAriff Abdullah }
162590da2b28SAriff Abdullah
162690da2b28SAriff Abdullah #define SND_CHN_OSS_FRONT (SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR)
162790da2b28SAriff Abdullah #define SND_CHN_OSS_SURR (SND_CHN_T_MASK_SL | SND_CHN_T_MASK_SR)
162890da2b28SAriff Abdullah #define SND_CHN_OSS_CENTER_LFE (SND_CHN_T_MASK_FC | SND_CHN_T_MASK_LF)
162990da2b28SAriff Abdullah #define SND_CHN_OSS_REAR (SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR)
163090da2b28SAriff Abdullah
163190da2b28SAriff Abdullah int
chn_oss_getmask(struct pcm_channel * c,uint32_t * retmask)163290da2b28SAriff Abdullah chn_oss_getmask(struct pcm_channel *c, uint32_t *retmask)
163390da2b28SAriff Abdullah {
163490da2b28SAriff Abdullah struct pcmchan_matrix *m;
163590da2b28SAriff Abdullah struct pcmchan_caps *caps;
163690da2b28SAriff Abdullah uint32_t i, format;
163790da2b28SAriff Abdullah
163890da2b28SAriff Abdullah KASSERT(c != NULL && retmask != NULL,
163990da2b28SAriff Abdullah ("%s(): NULL channel or retmask", __func__));
164090da2b28SAriff Abdullah CHN_LOCKASSERT(c);
164190da2b28SAriff Abdullah
164290da2b28SAriff Abdullah caps = chn_getcaps(c);
164390da2b28SAriff Abdullah if (caps == NULL || caps->fmtlist == NULL)
164490da2b28SAriff Abdullah return (ENODEV);
164590da2b28SAriff Abdullah
164690da2b28SAriff Abdullah for (i = 0; caps->fmtlist[i] != 0; i++) {
164790da2b28SAriff Abdullah format = caps->fmtlist[i];
164890da2b28SAriff Abdullah if (!(format & AFMT_CONVERTIBLE)) {
164990da2b28SAriff Abdullah *retmask |= DSP_BIND_SPDIF;
165090da2b28SAriff Abdullah continue;
165190da2b28SAriff Abdullah }
165290da2b28SAriff Abdullah m = CHANNEL_GETMATRIX(c->methods, c->devinfo, format);
165390da2b28SAriff Abdullah if (m == NULL)
165490da2b28SAriff Abdullah continue;
165590da2b28SAriff Abdullah if (m->mask & SND_CHN_OSS_FRONT)
165690da2b28SAriff Abdullah *retmask |= DSP_BIND_FRONT;
165790da2b28SAriff Abdullah if (m->mask & SND_CHN_OSS_SURR)
165890da2b28SAriff Abdullah *retmask |= DSP_BIND_SURR;
165990da2b28SAriff Abdullah if (m->mask & SND_CHN_OSS_CENTER_LFE)
166090da2b28SAriff Abdullah *retmask |= DSP_BIND_CENTER_LFE;
166190da2b28SAriff Abdullah if (m->mask & SND_CHN_OSS_REAR)
166290da2b28SAriff Abdullah *retmask |= DSP_BIND_REAR;
166390da2b28SAriff Abdullah }
166490da2b28SAriff Abdullah
166590da2b28SAriff Abdullah /* report software-supported binding mask */
166690da2b28SAriff Abdullah if (!CHN_BITPERFECT(c) && report_soft_matrix)
166790da2b28SAriff Abdullah *retmask |= DSP_BIND_FRONT | DSP_BIND_SURR |
166890da2b28SAriff Abdullah DSP_BIND_CENTER_LFE | DSP_BIND_REAR;
166990da2b28SAriff Abdullah
167090da2b28SAriff Abdullah return (0);
167190da2b28SAriff Abdullah }
167290da2b28SAriff Abdullah
167390da2b28SAriff Abdullah void
chn_vpc_reset(struct pcm_channel * c,int vc,int force)167490da2b28SAriff Abdullah chn_vpc_reset(struct pcm_channel *c, int vc, int force)
167590da2b28SAriff Abdullah {
167690da2b28SAriff Abdullah int i;
167790da2b28SAriff Abdullah
167890da2b28SAriff Abdullah KASSERT(c != NULL && vc >= SND_VOL_C_BEGIN && vc <= SND_VOL_C_END,
167990da2b28SAriff Abdullah ("%s(): invalid reset c=%p vc=%d", __func__, c, vc));
168090da2b28SAriff Abdullah CHN_LOCKASSERT(c);
168190da2b28SAriff Abdullah
168290da2b28SAriff Abdullah if (force == 0 && chn_vpc_autoreset == 0)
168390da2b28SAriff Abdullah return;
168490da2b28SAriff Abdullah
168590da2b28SAriff Abdullah for (i = SND_CHN_T_BEGIN; i <= SND_CHN_T_END; i += SND_CHN_T_STEP)
168690da2b28SAriff Abdullah CHN_SETVOLUME(c, vc, i, c->volume[vc][SND_CHN_T_VOL_0DB]);
1687987e5972SCameron Grant }
1688987e5972SCameron Grant
1689a580b31aSAriff Abdullah static u_int32_t
round_pow2(u_int32_t v)1690a580b31aSAriff Abdullah round_pow2(u_int32_t v)
1691a580b31aSAriff Abdullah {
1692a580b31aSAriff Abdullah u_int32_t ret;
1693a580b31aSAriff Abdullah
1694a580b31aSAriff Abdullah if (v < 2)
1695a580b31aSAriff Abdullah v = 2;
1696a580b31aSAriff Abdullah ret = 0;
1697a580b31aSAriff Abdullah while (v >> ret)
1698a580b31aSAriff Abdullah ret++;
1699a580b31aSAriff Abdullah ret = 1 << (ret - 1);
1700a580b31aSAriff Abdullah while (ret < v)
1701a580b31aSAriff Abdullah ret <<= 1;
1702a580b31aSAriff Abdullah return ret;
1703a580b31aSAriff Abdullah }
1704a580b31aSAriff Abdullah
1705fd1475d3SAriff Abdullah static u_int32_t
round_blksz(u_int32_t v,int round)1706fd1475d3SAriff Abdullah round_blksz(u_int32_t v, int round)
1707fd1475d3SAriff Abdullah {
1708fd1475d3SAriff Abdullah u_int32_t ret, tmp;
1709fd1475d3SAriff Abdullah
1710fd1475d3SAriff Abdullah if (round < 1)
1711fd1475d3SAriff Abdullah round = 1;
1712fd1475d3SAriff Abdullah
1713fd1475d3SAriff Abdullah ret = min(round_pow2(v), CHN_2NDBUFMAXSIZE >> 1);
1714fd1475d3SAriff Abdullah
1715fd1475d3SAriff Abdullah if (ret > v && (ret >> 1) > 0 && (ret >> 1) >= ((v * 3) >> 2))
1716fd1475d3SAriff Abdullah ret >>= 1;
1717fd1475d3SAriff Abdullah
1718fd1475d3SAriff Abdullah tmp = ret - (ret % round);
1719fd1475d3SAriff Abdullah while (tmp < 16 || tmp < round) {
1720fd1475d3SAriff Abdullah ret <<= 1;
1721fd1475d3SAriff Abdullah tmp = ret - (ret % round);
1722fd1475d3SAriff Abdullah }
1723fd1475d3SAriff Abdullah
1724fd1475d3SAriff Abdullah return ret;
1725fd1475d3SAriff Abdullah }
1726fd1475d3SAriff Abdullah
1727a580b31aSAriff Abdullah /*
1728a580b31aSAriff Abdullah * 4Front call it DSP Policy, while we call it "Latency Profile". The idea
1729a580b31aSAriff Abdullah * is to keep 2nd buffer short so that it doesn't cause long queue during
1730a580b31aSAriff Abdullah * buffer transfer.
1731a580b31aSAriff Abdullah *
1732a580b31aSAriff Abdullah * Latency reference table for 48khz stereo 16bit: (PLAY)
1733a580b31aSAriff Abdullah *
1734a580b31aSAriff Abdullah * +---------+------------+-----------+------------+
1735a580b31aSAriff Abdullah * | Latency | Blockcount | Blocksize | Buffersize |
1736a580b31aSAriff Abdullah * +---------+------------+-----------+------------+
1737a580b31aSAriff Abdullah * | 0 | 2 | 64 | 128 |
1738a580b31aSAriff Abdullah * +---------+------------+-----------+------------+
1739a580b31aSAriff Abdullah * | 1 | 4 | 128 | 512 |
1740a580b31aSAriff Abdullah * +---------+------------+-----------+------------+
1741a580b31aSAriff Abdullah * | 2 | 8 | 512 | 4096 |
1742a580b31aSAriff Abdullah * +---------+------------+-----------+------------+
1743a580b31aSAriff Abdullah * | 3 | 16 | 512 | 8192 |
1744a580b31aSAriff Abdullah * +---------+------------+-----------+------------+
1745a580b31aSAriff Abdullah * | 4 | 32 | 512 | 16384 |
1746a580b31aSAriff Abdullah * +---------+------------+-----------+------------+
1747a580b31aSAriff Abdullah * | 5 | 32 | 1024 | 32768 |
1748a580b31aSAriff Abdullah * +---------+------------+-----------+------------+
1749a580b31aSAriff Abdullah * | 6 | 16 | 2048 | 32768 |
1750a580b31aSAriff Abdullah * +---------+------------+-----------+------------+
1751a580b31aSAriff Abdullah * | 7 | 8 | 4096 | 32768 |
1752a580b31aSAriff Abdullah * +---------+------------+-----------+------------+
1753a580b31aSAriff Abdullah * | 8 | 4 | 8192 | 32768 |
1754a580b31aSAriff Abdullah * +---------+------------+-----------+------------+
1755a580b31aSAriff Abdullah * | 9 | 2 | 16384 | 32768 |
1756a580b31aSAriff Abdullah * +---------+------------+-----------+------------+
1757a580b31aSAriff Abdullah * | 10 | 2 | 32768 | 65536 |
1758a580b31aSAriff Abdullah * +---------+------------+-----------+------------+
1759a580b31aSAriff Abdullah *
1760a580b31aSAriff Abdullah * Recording need a different reference table. All we care is
1761a580b31aSAriff Abdullah * gobbling up everything within reasonable buffering threshold.
1762a580b31aSAriff Abdullah *
1763a580b31aSAriff Abdullah * Latency reference table for 48khz stereo 16bit: (REC)
1764a580b31aSAriff Abdullah *
1765a580b31aSAriff Abdullah * +---------+------------+-----------+------------+
1766a580b31aSAriff Abdullah * | Latency | Blockcount | Blocksize | Buffersize |
1767a580b31aSAriff Abdullah * +---------+------------+-----------+------------+
1768a580b31aSAriff Abdullah * | 0 | 512 | 32 | 16384 |
1769a580b31aSAriff Abdullah * +---------+------------+-----------+------------+
1770a580b31aSAriff Abdullah * | 1 | 256 | 64 | 16384 |
1771a580b31aSAriff Abdullah * +---------+------------+-----------+------------+
1772a580b31aSAriff Abdullah * | 2 | 128 | 128 | 16384 |
1773a580b31aSAriff Abdullah * +---------+------------+-----------+------------+
1774a580b31aSAriff Abdullah * | 3 | 64 | 256 | 16384 |
1775a580b31aSAriff Abdullah * +---------+------------+-----------+------------+
1776a580b31aSAriff Abdullah * | 4 | 32 | 512 | 16384 |
1777a580b31aSAriff Abdullah * +---------+------------+-----------+------------+
1778a580b31aSAriff Abdullah * | 5 | 32 | 1024 | 32768 |
1779a580b31aSAriff Abdullah * +---------+------------+-----------+------------+
1780a580b31aSAriff Abdullah * | 6 | 16 | 2048 | 32768 |
1781a580b31aSAriff Abdullah * +---------+------------+-----------+------------+
1782a580b31aSAriff Abdullah * | 7 | 8 | 4096 | 32768 |
1783a580b31aSAriff Abdullah * +---------+------------+-----------+------------+
1784a580b31aSAriff Abdullah * | 8 | 4 | 8192 | 32768 |
1785a580b31aSAriff Abdullah * +---------+------------+-----------+------------+
1786a580b31aSAriff Abdullah * | 9 | 2 | 16384 | 32768 |
1787a580b31aSAriff Abdullah * +---------+------------+-----------+------------+
1788a580b31aSAriff Abdullah * | 10 | 2 | 32768 | 65536 |
1789a580b31aSAriff Abdullah * +---------+------------+-----------+------------+
1790a580b31aSAriff Abdullah *
1791a580b31aSAriff Abdullah * Calculations for other data rate are entirely based on these reference
1792a580b31aSAriff Abdullah * tables. For normal operation, Latency 5 seems give the best, well
1793a580b31aSAriff Abdullah * balanced performance for typical workload. Anything below 5 will
1794a580b31aSAriff Abdullah * eat up CPU to keep up with increasing context switches because of
1795a580b31aSAriff Abdullah * shorter buffer space and usually require the application to handle it
1796d34632a2SGordon Bergling * aggressively through possibly real time programming technique.
1797a580b31aSAriff Abdullah *
1798a580b31aSAriff Abdullah */
1799a580b31aSAriff Abdullah #define CHN_LATENCY_PBLKCNT_REF \
1800a580b31aSAriff Abdullah {{1, 2, 3, 4, 5, 5, 4, 3, 2, 1, 1}, \
1801a580b31aSAriff Abdullah {1, 2, 3, 4, 5, 5, 4, 3, 2, 1, 1}}
1802a580b31aSAriff Abdullah #define CHN_LATENCY_PBUFSZ_REF \
1803a580b31aSAriff Abdullah {{7, 9, 12, 13, 14, 15, 15, 15, 15, 15, 16}, \
1804a580b31aSAriff Abdullah {11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 17}}
1805a580b31aSAriff Abdullah
1806a580b31aSAriff Abdullah #define CHN_LATENCY_RBLKCNT_REF \
1807a580b31aSAriff Abdullah {{9, 8, 7, 6, 5, 5, 4, 3, 2, 1, 1}, \
1808a580b31aSAriff Abdullah {9, 8, 7, 6, 5, 5, 4, 3, 2, 1, 1}}
1809a580b31aSAriff Abdullah #define CHN_LATENCY_RBUFSZ_REF \
1810a580b31aSAriff Abdullah {{14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 16}, \
1811a580b31aSAriff Abdullah {15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 17}}
1812a580b31aSAriff Abdullah
1813a580b31aSAriff Abdullah #define CHN_LATENCY_DATA_REF 192000 /* 48khz stereo 16bit ~ 48000 x 2 x 2 */
1814a580b31aSAriff Abdullah
1815a580b31aSAriff Abdullah static int
chn_calclatency(int dir,int latency,int bps,u_int32_t datarate,u_int32_t max,int * rblksz,int * rblkcnt)1816a580b31aSAriff Abdullah chn_calclatency(int dir, int latency, int bps, u_int32_t datarate,
1817a580b31aSAriff Abdullah u_int32_t max, int *rblksz, int *rblkcnt)
1818a580b31aSAriff Abdullah {
1819a580b31aSAriff Abdullah static int pblkcnts[CHN_LATENCY_PROFILE_MAX + 1][CHN_LATENCY_MAX + 1] =
1820a580b31aSAriff Abdullah CHN_LATENCY_PBLKCNT_REF;
1821a580b31aSAriff Abdullah static int pbufszs[CHN_LATENCY_PROFILE_MAX + 1][CHN_LATENCY_MAX + 1] =
1822a580b31aSAriff Abdullah CHN_LATENCY_PBUFSZ_REF;
1823a580b31aSAriff Abdullah static int rblkcnts[CHN_LATENCY_PROFILE_MAX + 1][CHN_LATENCY_MAX + 1] =
1824a580b31aSAriff Abdullah CHN_LATENCY_RBLKCNT_REF;
1825a580b31aSAriff Abdullah static int rbufszs[CHN_LATENCY_PROFILE_MAX + 1][CHN_LATENCY_MAX + 1] =
1826a580b31aSAriff Abdullah CHN_LATENCY_RBUFSZ_REF;
1827fd1475d3SAriff Abdullah u_int32_t bufsz;
1828fd1475d3SAriff Abdullah int lprofile, blksz, blkcnt;
1829a580b31aSAriff Abdullah
1830fd1475d3SAriff Abdullah if (latency < CHN_LATENCY_MIN || latency > CHN_LATENCY_MAX ||
1831a580b31aSAriff Abdullah bps < 1 || datarate < 1 ||
1832a580b31aSAriff Abdullah !(dir == PCMDIR_PLAY || dir == PCMDIR_REC)) {
1833a580b31aSAriff Abdullah if (rblksz != NULL)
1834a580b31aSAriff Abdullah *rblksz = CHN_2NDBUFMAXSIZE >> 1;
1835a580b31aSAriff Abdullah if (rblkcnt != NULL)
1836a580b31aSAriff Abdullah *rblkcnt = 2;
183790da2b28SAriff Abdullah printf("%s(): FAILED dir=%d latency=%d bps=%d "
1838a580b31aSAriff Abdullah "datarate=%u max=%u\n",
1839a580b31aSAriff Abdullah __func__, dir, latency, bps, datarate, max);
1840a580b31aSAriff Abdullah return CHN_2NDBUFMAXSIZE;
1841a580b31aSAriff Abdullah }
1842a580b31aSAriff Abdullah
1843fd1475d3SAriff Abdullah lprofile = chn_latency_profile;
1844fd1475d3SAriff Abdullah
1845a580b31aSAriff Abdullah if (dir == PCMDIR_PLAY) {
1846fd1475d3SAriff Abdullah blkcnt = pblkcnts[lprofile][latency];
1847fd1475d3SAriff Abdullah bufsz = pbufszs[lprofile][latency];
1848a580b31aSAriff Abdullah } else {
1849fd1475d3SAriff Abdullah blkcnt = rblkcnts[lprofile][latency];
1850fd1475d3SAriff Abdullah bufsz = rbufszs[lprofile][latency];
1851a580b31aSAriff Abdullah }
1852fd1475d3SAriff Abdullah
1853fd1475d3SAriff Abdullah bufsz = round_pow2(snd_xbytes(1 << bufsz, CHN_LATENCY_DATA_REF,
1854fd1475d3SAriff Abdullah datarate));
1855a580b31aSAriff Abdullah if (bufsz > max)
1856a580b31aSAriff Abdullah bufsz = max;
1857fd1475d3SAriff Abdullah blksz = round_blksz(bufsz >> blkcnt, bps);
1858fd1475d3SAriff Abdullah
1859a580b31aSAriff Abdullah if (rblksz != NULL)
1860a580b31aSAriff Abdullah *rblksz = blksz;
1861a580b31aSAriff Abdullah if (rblkcnt != NULL)
1862a580b31aSAriff Abdullah *rblkcnt = 1 << blkcnt;
1863a580b31aSAriff Abdullah
1864a580b31aSAriff Abdullah return blksz << blkcnt;
1865a580b31aSAriff Abdullah }
1866a580b31aSAriff Abdullah
1867a580b31aSAriff Abdullah static int
chn_resizebuf(struct pcm_channel * c,int latency,int blkcnt,int blksz)1868a580b31aSAriff Abdullah chn_resizebuf(struct pcm_channel *c, int latency,
1869a580b31aSAriff Abdullah int blkcnt, int blksz)
1870a580b31aSAriff Abdullah {
1871a580b31aSAriff Abdullah struct snd_dbuf *b, *bs, *pb;
1872fd578f65SAlexander Motin int sblksz, sblkcnt, hblksz, hblkcnt, limit = 0, nsblksz, nsblkcnt;
1873a580b31aSAriff Abdullah int ret;
1874a580b31aSAriff Abdullah
1875a580b31aSAriff Abdullah CHN_LOCKASSERT(c);
1876a580b31aSAriff Abdullah
187790da2b28SAriff Abdullah if ((c->flags & (CHN_F_MMAP | CHN_F_TRIGGERED)) ||
1878a580b31aSAriff Abdullah !(c->direction == PCMDIR_PLAY || c->direction == PCMDIR_REC))
1879a580b31aSAriff Abdullah return EINVAL;
1880a580b31aSAriff Abdullah
1881a580b31aSAriff Abdullah if (latency == -1) {
1882a580b31aSAriff Abdullah c->latency = -1;
1883a580b31aSAriff Abdullah latency = chn_latency;
1884a580b31aSAriff Abdullah } else if (latency == -2) {
1885a580b31aSAriff Abdullah latency = c->latency;
1886a580b31aSAriff Abdullah if (latency < CHN_LATENCY_MIN || latency > CHN_LATENCY_MAX)
1887a580b31aSAriff Abdullah latency = chn_latency;
1888a580b31aSAriff Abdullah } else if (latency < CHN_LATENCY_MIN || latency > CHN_LATENCY_MAX)
1889a580b31aSAriff Abdullah return EINVAL;
1890a580b31aSAriff Abdullah else {
1891a580b31aSAriff Abdullah c->latency = latency;
1892a580b31aSAriff Abdullah }
1893a580b31aSAriff Abdullah
1894a580b31aSAriff Abdullah bs = c->bufsoft;
1895a580b31aSAriff Abdullah b = c->bufhard;
1896a580b31aSAriff Abdullah
1897a580b31aSAriff Abdullah if (!(blksz == 0 || blkcnt == -1) &&
189890da2b28SAriff Abdullah (blksz < 16 || blksz < sndbuf_getalign(bs) || blkcnt < 2 ||
1899a580b31aSAriff Abdullah (blksz * blkcnt) > CHN_2NDBUFMAXSIZE))
1900a580b31aSAriff Abdullah return EINVAL;
1901a580b31aSAriff Abdullah
190290da2b28SAriff Abdullah chn_calclatency(c->direction, latency, sndbuf_getalign(bs),
190390da2b28SAriff Abdullah sndbuf_getalign(bs) * sndbuf_getspd(bs), CHN_2NDBUFMAXSIZE,
1904a580b31aSAriff Abdullah &sblksz, &sblkcnt);
1905a580b31aSAriff Abdullah
1906a580b31aSAriff Abdullah if (blksz == 0 || blkcnt == -1) {
1907a580b31aSAriff Abdullah if (blkcnt == -1)
1908a580b31aSAriff Abdullah c->flags &= ~CHN_F_HAS_SIZE;
1909a580b31aSAriff Abdullah if (c->flags & CHN_F_HAS_SIZE) {
1910a580b31aSAriff Abdullah blksz = sndbuf_getblksz(bs);
1911a580b31aSAriff Abdullah blkcnt = sndbuf_getblkcnt(bs);
1912a580b31aSAriff Abdullah }
1913a580b31aSAriff Abdullah } else
1914a580b31aSAriff Abdullah c->flags |= CHN_F_HAS_SIZE;
1915a580b31aSAriff Abdullah
1916a580b31aSAriff Abdullah if (c->flags & CHN_F_HAS_SIZE) {
1917a580b31aSAriff Abdullah /*
1918a580b31aSAriff Abdullah * The application has requested their own blksz/blkcnt.
1919a580b31aSAriff Abdullah * Just obey with it, and let them toast alone. We can
1920a580b31aSAriff Abdullah * clamp it to the nearest latency profile, but that would
1921a580b31aSAriff Abdullah * defeat the purpose of having custom control. The least
1922a580b31aSAriff Abdullah * we can do is round it to the nearest ^2 and align it.
1923a580b31aSAriff Abdullah */
192490da2b28SAriff Abdullah sblksz = round_blksz(blksz, sndbuf_getalign(bs));
1925fd1475d3SAriff Abdullah sblkcnt = round_pow2(blkcnt);
1926a580b31aSAriff Abdullah }
1927a580b31aSAriff Abdullah
1928a580b31aSAriff Abdullah if (c->parentchannel != NULL) {
1929fd578f65SAlexander Motin pb = c->parentchannel->bufsoft;
1930a580b31aSAriff Abdullah CHN_UNLOCK(c);
1931e4e61333SAriff Abdullah CHN_LOCK(c->parentchannel);
1932a580b31aSAriff Abdullah chn_notify(c->parentchannel, CHN_N_BLOCKSIZE);
1933e4e61333SAriff Abdullah CHN_UNLOCK(c->parentchannel);
1934a580b31aSAriff Abdullah CHN_LOCK(c);
1935fd578f65SAlexander Motin if (c->direction == PCMDIR_PLAY) {
1936fd578f65SAlexander Motin limit = (pb != NULL) ?
1937a580b31aSAriff Abdullah sndbuf_xbytes(sndbuf_getsize(pb), pb, bs) : 0;
1938fd578f65SAlexander Motin } else {
1939fd578f65SAlexander Motin limit = (pb != NULL) ?
1940fd578f65SAlexander Motin sndbuf_xbytes(sndbuf_getblksz(pb), pb, bs) * 2 : 0;
1941fd578f65SAlexander Motin }
1942a580b31aSAriff Abdullah } else {
1943fd1475d3SAriff Abdullah hblkcnt = 2;
1944a580b31aSAriff Abdullah if (c->flags & CHN_F_HAS_SIZE) {
1945fd1475d3SAriff Abdullah hblksz = round_blksz(sndbuf_xbytes(sblksz, bs, b),
194690da2b28SAriff Abdullah sndbuf_getalign(b));
1947fd1475d3SAriff Abdullah hblkcnt = round_pow2(sndbuf_getblkcnt(bs));
1948a580b31aSAriff Abdullah } else
1949a580b31aSAriff Abdullah chn_calclatency(c->direction, latency,
195090da2b28SAriff Abdullah sndbuf_getalign(b),
195190da2b28SAriff Abdullah sndbuf_getalign(b) * sndbuf_getspd(b),
1952fd1475d3SAriff Abdullah CHN_2NDBUFMAXSIZE, &hblksz, &hblkcnt);
1953a580b31aSAriff Abdullah
1954a580b31aSAriff Abdullah if ((hblksz << 1) > sndbuf_getmaxsize(b))
1955fd1475d3SAriff Abdullah hblksz = round_blksz(sndbuf_getmaxsize(b) >> 1,
195690da2b28SAriff Abdullah sndbuf_getalign(b));
1957fd1475d3SAriff Abdullah
1958fd1475d3SAriff Abdullah while ((hblksz * hblkcnt) > sndbuf_getmaxsize(b)) {
1959fd1475d3SAriff Abdullah if (hblkcnt < 4)
1960fd1475d3SAriff Abdullah hblksz >>= 1;
1961fd1475d3SAriff Abdullah else
1962fd1475d3SAriff Abdullah hblkcnt >>= 1;
1963fd1475d3SAriff Abdullah }
1964fd1475d3SAriff Abdullah
196590da2b28SAriff Abdullah hblksz -= hblksz % sndbuf_getalign(b);
1966a580b31aSAriff Abdullah
1967a580b31aSAriff Abdullah CHN_UNLOCK(c);
1968fd1475d3SAriff Abdullah if (chn_usefrags == 0 ||
1969fd1475d3SAriff Abdullah CHANNEL_SETFRAGMENTS(c->methods, c->devinfo,
197090da2b28SAriff Abdullah hblksz, hblkcnt) != 0)
1971a580b31aSAriff Abdullah sndbuf_setblksz(b, CHANNEL_SETBLOCKSIZE(c->methods,
1972a580b31aSAriff Abdullah c->devinfo, hblksz));
1973a580b31aSAriff Abdullah CHN_LOCK(c);
1974a580b31aSAriff Abdullah
1975bba4862cSAriff Abdullah if (!CHN_EMPTY(c, children)) {
1976fd578f65SAlexander Motin nsblksz = round_blksz(
1977fd578f65SAlexander Motin sndbuf_xbytes(sndbuf_getblksz(b), b, bs),
197890da2b28SAriff Abdullah sndbuf_getalign(bs));
1979fd578f65SAlexander Motin nsblkcnt = sndbuf_getblkcnt(b);
1980fd578f65SAlexander Motin if (c->direction == PCMDIR_PLAY) {
1981fd578f65SAlexander Motin do {
1982fd578f65SAlexander Motin nsblkcnt--;
1983fd578f65SAlexander Motin } while (nsblkcnt >= 2 &&
1984fd578f65SAlexander Motin nsblksz * nsblkcnt >= sblksz * sblkcnt);
1985fd578f65SAlexander Motin nsblkcnt++;
1986fd578f65SAlexander Motin }
1987fd578f65SAlexander Motin sblksz = nsblksz;
1988fd578f65SAlexander Motin sblkcnt = nsblkcnt;
1989a580b31aSAriff Abdullah limit = 0;
1990fd578f65SAlexander Motin } else
1991fd578f65SAlexander Motin limit = sndbuf_xbytes(sndbuf_getblksz(b), b, bs) * 2;
1992a580b31aSAriff Abdullah }
1993a580b31aSAriff Abdullah
1994a580b31aSAriff Abdullah if (limit > CHN_2NDBUFMAXSIZE)
1995a580b31aSAriff Abdullah limit = CHN_2NDBUFMAXSIZE;
1996a580b31aSAriff Abdullah
1997fd1475d3SAriff Abdullah while ((sblksz * sblkcnt) < limit)
1998fd1475d3SAriff Abdullah sblkcnt <<= 1;
1999fd1475d3SAriff Abdullah
2000fd1475d3SAriff Abdullah while ((sblksz * sblkcnt) > CHN_2NDBUFMAXSIZE) {
2001fd1475d3SAriff Abdullah if (sblkcnt < 4)
2002fd1475d3SAriff Abdullah sblksz >>= 1;
2003fd1475d3SAriff Abdullah else
2004fd1475d3SAriff Abdullah sblkcnt >>= 1;
2005a580b31aSAriff Abdullah }
2006a580b31aSAriff Abdullah
200790da2b28SAriff Abdullah sblksz -= sblksz % sndbuf_getalign(bs);
2008fd1475d3SAriff Abdullah
2009a580b31aSAriff Abdullah if (sndbuf_getblkcnt(bs) != sblkcnt || sndbuf_getblksz(bs) != sblksz ||
2010a580b31aSAriff Abdullah sndbuf_getsize(bs) != (sblkcnt * sblksz)) {
2011a580b31aSAriff Abdullah ret = sndbuf_remalloc(bs, sblkcnt, sblksz);
2012a580b31aSAriff Abdullah if (ret != 0) {
201390da2b28SAriff Abdullah device_printf(c->dev, "%s(): Failed: %d %d\n",
201490da2b28SAriff Abdullah __func__, sblkcnt, sblksz);
2015a580b31aSAriff Abdullah return ret;
2016a580b31aSAriff Abdullah }
2017a580b31aSAriff Abdullah }
2018a580b31aSAriff Abdullah
2019a580b31aSAriff Abdullah /*
2020fd578f65SAlexander Motin * Interrupt timeout
2021fd578f65SAlexander Motin */
2022fd578f65SAlexander Motin c->timeout = ((u_int64_t)hz * sndbuf_getsize(bs)) /
2023fd578f65SAlexander Motin ((u_int64_t)sndbuf_getspd(bs) * sndbuf_getalign(bs));
2024fd578f65SAlexander Motin if (c->parentchannel != NULL)
2025fd578f65SAlexander Motin c->timeout = min(c->timeout, c->parentchannel->timeout);
2026fd578f65SAlexander Motin if (c->timeout < 1)
2027fd578f65SAlexander Motin c->timeout = 1;
2028fd578f65SAlexander Motin
2029fd578f65SAlexander Motin /*
2030a580b31aSAriff Abdullah * OSSv4 docs: "By default OSS will set the low water level equal
2031a580b31aSAriff Abdullah * to the fragment size which is optimal in most cases."
2032a580b31aSAriff Abdullah */
2033a580b31aSAriff Abdullah c->lw = sndbuf_getblksz(bs);
2034a580b31aSAriff Abdullah chn_resetbuf(c);
2035a580b31aSAriff Abdullah
2036a580b31aSAriff Abdullah if (snd_verbose > 3)
203790da2b28SAriff Abdullah device_printf(c->dev, "%s(): %s (%s) timeout=%u "
2038a580b31aSAriff Abdullah "b[%d/%d/%d] bs[%d/%d/%d] limit=%d\n",
2039fd1475d3SAriff Abdullah __func__, CHN_DIRSTR(c),
2040a580b31aSAriff Abdullah (c->flags & CHN_F_VIRTUAL) ? "virtual" : "hardware",
2041a580b31aSAriff Abdullah c->timeout,
2042a580b31aSAriff Abdullah sndbuf_getsize(b), sndbuf_getblksz(b),
2043a580b31aSAriff Abdullah sndbuf_getblkcnt(b),
2044a580b31aSAriff Abdullah sndbuf_getsize(bs), sndbuf_getblksz(bs),
2045a580b31aSAriff Abdullah sndbuf_getblkcnt(bs), limit);
2046a580b31aSAriff Abdullah
2047a580b31aSAriff Abdullah return 0;
2048a580b31aSAriff Abdullah }
2049a580b31aSAriff Abdullah
2050a580b31aSAriff Abdullah int
chn_setlatency(struct pcm_channel * c,int latency)2051a580b31aSAriff Abdullah chn_setlatency(struct pcm_channel *c, int latency)
2052a580b31aSAriff Abdullah {
2053a580b31aSAriff Abdullah CHN_LOCKASSERT(c);
2054a580b31aSAriff Abdullah /* Destroy blksz/blkcnt, enforce latency profile. */
2055a580b31aSAriff Abdullah return chn_resizebuf(c, latency, -1, 0);
2056a580b31aSAriff Abdullah }
2057a580b31aSAriff Abdullah
2058a580b31aSAriff Abdullah int
chn_setblocksize(struct pcm_channel * c,int blkcnt,int blksz)2059a580b31aSAriff Abdullah chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz)
2060a580b31aSAriff Abdullah {
2061a580b31aSAriff Abdullah CHN_LOCKASSERT(c);
2062a580b31aSAriff Abdullah /* Destroy latency profile, enforce blksz/blkcnt */
2063a580b31aSAriff Abdullah return chn_resizebuf(c, -1, blkcnt, blksz);
2064a580b31aSAriff Abdullah }
2065a580b31aSAriff Abdullah
206690da2b28SAriff Abdullah int
chn_setparam(struct pcm_channel * c,uint32_t format,uint32_t speed)206790da2b28SAriff Abdullah chn_setparam(struct pcm_channel *c, uint32_t format, uint32_t speed)
2068987e5972SCameron Grant {
206990da2b28SAriff Abdullah struct pcmchan_caps *caps;
207090da2b28SAriff Abdullah uint32_t hwspeed, delta;
207190da2b28SAriff Abdullah int ret;
2072c9c6ba09SCameron Grant
207366ef8af5SCameron Grant CHN_LOCKASSERT(c);
207490da2b28SAriff Abdullah
207590da2b28SAriff Abdullah if (speed < 1 || format == 0 || CHN_STARTED(c))
207690da2b28SAriff Abdullah return (EINVAL);
207790da2b28SAriff Abdullah
207890da2b28SAriff Abdullah c->format = format;
2079c9c6ba09SCameron Grant c->speed = speed;
2080350a5fafSCameron Grant
208190da2b28SAriff Abdullah caps = chn_getcaps(c);
2082350a5fafSCameron Grant
208390da2b28SAriff Abdullah hwspeed = speed;
208490da2b28SAriff Abdullah RANGE(hwspeed, caps->minspeed, caps->maxspeed);
2085350a5fafSCameron Grant
208690da2b28SAriff Abdullah sndbuf_setspd(c->bufhard, CHANNEL_SETSPEED(c->methods, c->devinfo,
208790da2b28SAriff Abdullah hwspeed));
208890da2b28SAriff Abdullah hwspeed = sndbuf_getspd(c->bufhard);
2089350a5fafSCameron Grant
209090da2b28SAriff Abdullah delta = (hwspeed > speed) ? (hwspeed - speed) : (speed - hwspeed);
2091350a5fafSCameron Grant
209290da2b28SAriff Abdullah if (delta <= feeder_rate_round)
209390da2b28SAriff Abdullah c->speed = hwspeed;
209466ef8af5SCameron Grant
209590da2b28SAriff Abdullah ret = feeder_chain(c);
209666ef8af5SCameron Grant
209790da2b28SAriff Abdullah if (ret == 0)
209890da2b28SAriff Abdullah ret = CHANNEL_SETFORMAT(c->methods, c->devinfo,
209990da2b28SAriff Abdullah sndbuf_getfmt(c->bufhard));
210090da2b28SAriff Abdullah
210190da2b28SAriff Abdullah if (ret == 0)
210290da2b28SAriff Abdullah ret = chn_resizebuf(c, -2, 0, 0);
210390da2b28SAriff Abdullah
210490da2b28SAriff Abdullah return (ret);
2105987e5972SCameron Grant }
2106987e5972SCameron Grant
2107987e5972SCameron Grant int
chn_setspeed(struct pcm_channel * c,uint32_t speed)210890da2b28SAriff Abdullah chn_setspeed(struct pcm_channel *c, uint32_t speed)
21097ec3c462SCameron Grant {
21104e1b75beSChristos Margiolis uint32_t oldformat, oldspeed;
211190da2b28SAriff Abdullah int ret;
21127ec3c462SCameron Grant
211390da2b28SAriff Abdullah oldformat = c->format;
211490da2b28SAriff Abdullah oldspeed = c->speed;
211590da2b28SAriff Abdullah
211640616b7eSChristos Margiolis if (c->speed == speed)
211740616b7eSChristos Margiolis return (0);
211840616b7eSChristos Margiolis
21194e1b75beSChristos Margiolis ret = chn_setparam(c, c->format, speed);
212090da2b28SAriff Abdullah if (ret != 0) {
2121a580b31aSAriff Abdullah if (snd_verbose > 3)
212290da2b28SAriff Abdullah device_printf(c->dev,
212390da2b28SAriff Abdullah "%s(): Setting speed %d failed, "
212490da2b28SAriff Abdullah "falling back to %d\n",
212590da2b28SAriff Abdullah __func__, speed, oldspeed);
21264e1b75beSChristos Margiolis chn_setparam(c, oldformat, oldspeed);
21277ec3c462SCameron Grant }
21287ec3c462SCameron Grant
212990da2b28SAriff Abdullah return (ret);
213090da2b28SAriff Abdullah }
213190da2b28SAriff Abdullah
213290da2b28SAriff Abdullah int
chn_setformat(struct pcm_channel * c,uint32_t format)213390da2b28SAriff Abdullah chn_setformat(struct pcm_channel *c, uint32_t format)
2134987e5972SCameron Grant {
21354e1b75beSChristos Margiolis uint32_t oldformat, oldspeed;
213690da2b28SAriff Abdullah int ret;
213790da2b28SAriff Abdullah
213890da2b28SAriff Abdullah /* XXX force stereo */
21393ba9d9e9SAlexander Motin if ((format & AFMT_PASSTHROUGH) && AFMT_CHANNEL(format) < 2) {
214090da2b28SAriff Abdullah format = SND_FORMAT(format, AFMT_PASSTHROUGH_CHANNEL,
214190da2b28SAriff Abdullah AFMT_PASSTHROUGH_EXTCHANNEL);
21423ba9d9e9SAlexander Motin }
214390da2b28SAriff Abdullah
214490da2b28SAriff Abdullah oldformat = c->format;
214590da2b28SAriff Abdullah oldspeed = c->speed;
214690da2b28SAriff Abdullah
214740616b7eSChristos Margiolis if (c->format == format)
214840616b7eSChristos Margiolis return (0);
214940616b7eSChristos Margiolis
21504e1b75beSChristos Margiolis ret = chn_setparam(c, format, c->speed);
215190da2b28SAriff Abdullah if (ret != 0) {
215290da2b28SAriff Abdullah if (snd_verbose > 3)
215390da2b28SAriff Abdullah device_printf(c->dev,
215490da2b28SAriff Abdullah "%s(): Format change 0x%08x failed, "
215590da2b28SAriff Abdullah "falling back to 0x%08x\n",
215690da2b28SAriff Abdullah __func__, format, oldformat);
215790da2b28SAriff Abdullah chn_setparam(c, oldformat, oldspeed);
215890da2b28SAriff Abdullah }
215990da2b28SAriff Abdullah
216090da2b28SAriff Abdullah return (ret);
216190da2b28SAriff Abdullah }
216290da2b28SAriff Abdullah
216390da2b28SAriff Abdullah void
chn_syncstate(struct pcm_channel * c)216490da2b28SAriff Abdullah chn_syncstate(struct pcm_channel *c)
216590da2b28SAriff Abdullah {
216690da2b28SAriff Abdullah struct snddev_info *d;
216790da2b28SAriff Abdullah struct snd_mixer *m;
216890da2b28SAriff Abdullah
216990da2b28SAriff Abdullah d = (c != NULL) ? c->parentsnddev : NULL;
217090da2b28SAriff Abdullah m = (d != NULL && d->mixer_dev != NULL) ? d->mixer_dev->si_drv1 :
217190da2b28SAriff Abdullah NULL;
217290da2b28SAriff Abdullah
217390da2b28SAriff Abdullah if (d == NULL || m == NULL)
217490da2b28SAriff Abdullah return;
2175350a5fafSCameron Grant
217666ef8af5SCameron Grant CHN_LOCKASSERT(c);
217790da2b28SAriff Abdullah
217890da2b28SAriff Abdullah if (c->feederflags & (1 << FEEDER_VOLUME)) {
217990da2b28SAriff Abdullah uint32_t parent;
218090da2b28SAriff Abdullah int vol, pvol, left, right, center;
218190da2b28SAriff Abdullah
218290da2b28SAriff Abdullah if (c->direction == PCMDIR_PLAY &&
218390da2b28SAriff Abdullah (d->flags & SD_F_SOFTPCMVOL)) {
218490da2b28SAriff Abdullah /* CHN_UNLOCK(c); */
218590da2b28SAriff Abdullah vol = mix_get(m, SOUND_MIXER_PCM);
218690da2b28SAriff Abdullah parent = mix_getparent(m, SOUND_MIXER_PCM);
218790da2b28SAriff Abdullah if (parent != SOUND_MIXER_NONE)
218890da2b28SAriff Abdullah pvol = mix_get(m, parent);
218990da2b28SAriff Abdullah else
219090da2b28SAriff Abdullah pvol = 100 | (100 << 8);
219190da2b28SAriff Abdullah /* CHN_LOCK(c); */
219290da2b28SAriff Abdullah } else {
219390da2b28SAriff Abdullah vol = 100 | (100 << 8);
219490da2b28SAriff Abdullah pvol = vol;
2195987e5972SCameron Grant }
2196987e5972SCameron Grant
219790da2b28SAriff Abdullah if (vol == -1) {
219890da2b28SAriff Abdullah device_printf(c->dev,
219990da2b28SAriff Abdullah "Soft PCM Volume: Failed to read pcm "
220090da2b28SAriff Abdullah "default value\n");
220190da2b28SAriff Abdullah vol = 100 | (100 << 8);
22027ec3c462SCameron Grant }
220390da2b28SAriff Abdullah
220490da2b28SAriff Abdullah if (pvol == -1) {
220590da2b28SAriff Abdullah device_printf(c->dev,
220690da2b28SAriff Abdullah "Soft PCM Volume: Failed to read parent "
220790da2b28SAriff Abdullah "default value\n");
220890da2b28SAriff Abdullah pvol = 100 | (100 << 8);
220990da2b28SAriff Abdullah }
221090da2b28SAriff Abdullah
221190da2b28SAriff Abdullah left = ((vol & 0x7f) * (pvol & 0x7f)) / 100;
221290da2b28SAriff Abdullah right = (((vol >> 8) & 0x7f) * ((pvol >> 8) & 0x7f)) / 100;
221390da2b28SAriff Abdullah center = (left + right) >> 1;
221490da2b28SAriff Abdullah
221590da2b28SAriff Abdullah chn_setvolume_multi(c, SND_VOL_C_MASTER, left, right, center);
221690da2b28SAriff Abdullah }
221790da2b28SAriff Abdullah
221890da2b28SAriff Abdullah if (c->feederflags & (1 << FEEDER_EQ)) {
221990da2b28SAriff Abdullah struct pcm_feeder *f;
222090da2b28SAriff Abdullah int treble, bass, state;
222190da2b28SAriff Abdullah
222290da2b28SAriff Abdullah /* CHN_UNLOCK(c); */
222390da2b28SAriff Abdullah treble = mix_get(m, SOUND_MIXER_TREBLE);
222490da2b28SAriff Abdullah bass = mix_get(m, SOUND_MIXER_BASS);
222590da2b28SAriff Abdullah /* CHN_LOCK(c); */
222690da2b28SAriff Abdullah
222790da2b28SAriff Abdullah if (treble == -1)
222890da2b28SAriff Abdullah treble = 50;
222990da2b28SAriff Abdullah else
223090da2b28SAriff Abdullah treble = ((treble & 0x7f) +
223190da2b28SAriff Abdullah ((treble >> 8) & 0x7f)) >> 1;
223290da2b28SAriff Abdullah
223390da2b28SAriff Abdullah if (bass == -1)
223490da2b28SAriff Abdullah bass = 50;
223590da2b28SAriff Abdullah else
223690da2b28SAriff Abdullah bass = ((bass & 0x7f) + ((bass >> 8) & 0x7f)) >> 1;
223790da2b28SAriff Abdullah
223829ff7b08SChristos Margiolis f = feeder_find(c, FEEDER_EQ);
223990da2b28SAriff Abdullah if (f != NULL) {
224090da2b28SAriff Abdullah if (FEEDER_SET(f, FEEDEQ_TREBLE, treble) != 0)
224190da2b28SAriff Abdullah device_printf(c->dev,
224290da2b28SAriff Abdullah "EQ: Failed to set treble -- %d\n",
224390da2b28SAriff Abdullah treble);
224490da2b28SAriff Abdullah if (FEEDER_SET(f, FEEDEQ_BASS, bass) != 0)
224590da2b28SAriff Abdullah device_printf(c->dev,
224690da2b28SAriff Abdullah "EQ: Failed to set bass -- %d\n",
224790da2b28SAriff Abdullah bass);
224890da2b28SAriff Abdullah if (FEEDER_SET(f, FEEDEQ_PREAMP, d->eqpreamp) != 0)
224990da2b28SAriff Abdullah device_printf(c->dev,
225090da2b28SAriff Abdullah "EQ: Failed to set preamp -- %d\n",
225190da2b28SAriff Abdullah d->eqpreamp);
225290da2b28SAriff Abdullah if (d->flags & SD_F_EQ_BYPASSED)
225390da2b28SAriff Abdullah state = FEEDEQ_BYPASS;
225490da2b28SAriff Abdullah else if (d->flags & SD_F_EQ_ENABLED)
225590da2b28SAriff Abdullah state = FEEDEQ_ENABLE;
225690da2b28SAriff Abdullah else
225790da2b28SAriff Abdullah state = FEEDEQ_DISABLE;
225890da2b28SAriff Abdullah if (FEEDER_SET(f, FEEDEQ_STATE, state) != 0)
225990da2b28SAriff Abdullah device_printf(c->dev,
226090da2b28SAriff Abdullah "EQ: Failed to set state -- %d\n", state);
226190da2b28SAriff Abdullah }
226290da2b28SAriff Abdullah }
22637ec3c462SCameron Grant }
22647ec3c462SCameron Grant
2265987e5972SCameron Grant int
chn_trigger(struct pcm_channel * c,int go)226666ef8af5SCameron Grant chn_trigger(struct pcm_channel *c, int go)
2267987e5972SCameron Grant {
2268bba4862cSAriff Abdullah struct snddev_info *d = c->parentsnddev;
226966ef8af5SCameron Grant int ret;
227066ef8af5SCameron Grant
227166ef8af5SCameron Grant CHN_LOCKASSERT(c);
2272e4e61333SAriff Abdullah if (!PCMTRIG_COMMON(go))
2273e4e61333SAriff Abdullah return (CHANNEL_TRIGGER(c->methods, c->devinfo, go));
2274e4e61333SAriff Abdullah
2275e4e61333SAriff Abdullah if (go == c->trigger)
2276bdfbdcecSAriff Abdullah return (0);
2277bba4862cSAriff Abdullah
22785ac39263SChristos Margiolis if (snd_verbose > 3) {
22795ac39263SChristos Margiolis device_printf(c->dev, "%s() %s: calling go=0x%08x , "
22805ac39263SChristos Margiolis "prev=0x%08x\n", __func__, c->name, go, c->trigger);
22815ac39263SChristos Margiolis }
22825ac39263SChristos Margiolis
22835ac39263SChristos Margiolis c->trigger = go;
228466ef8af5SCameron Grant ret = CHANNEL_TRIGGER(c->methods, c->devinfo, go);
2285e4e61333SAriff Abdullah if (ret != 0)
2286e4e61333SAriff Abdullah return (ret);
228766ef8af5SCameron Grant
2288bba4862cSAriff Abdullah CHN_UNLOCK(c);
228990da2b28SAriff Abdullah PCM_LOCK(d);
2290bba4862cSAriff Abdullah CHN_LOCK(c);
22915ac39263SChristos Margiolis
22925ac39263SChristos Margiolis /*
22935ac39263SChristos Margiolis * Do nothing if another thread set a different trigger while we had
22945ac39263SChristos Margiolis * dropped the mutex.
22955ac39263SChristos Margiolis */
22965ac39263SChristos Margiolis if (go != c->trigger) {
22975ac39263SChristos Margiolis PCM_UNLOCK(d);
22985ac39263SChristos Margiolis return (0);
2299bba4862cSAriff Abdullah }
23005ac39263SChristos Margiolis
23015ac39263SChristos Margiolis /*
23025ac39263SChristos Margiolis * Use the SAFE variants to prevent inserting/removing an already
23035ac39263SChristos Margiolis * existing/missing element.
23045ac39263SChristos Margiolis */
23055ac39263SChristos Margiolis switch (go) {
23065ac39263SChristos Margiolis case PCMTRIG_START:
23075ac39263SChristos Margiolis CHN_INSERT_HEAD_SAFE(d, c, channels.pcm.busy);
23085ac39263SChristos Margiolis PCM_UNLOCK(d);
23095ac39263SChristos Margiolis chn_syncstate(c);
2310bba4862cSAriff Abdullah break;
2311bba4862cSAriff Abdullah case PCMTRIG_STOP:
2312bba4862cSAriff Abdullah case PCMTRIG_ABORT:
2313ffcefe53SChristos Margiolis CHN_REMOVE(d, c, channels.pcm.busy);
231490da2b28SAriff Abdullah PCM_UNLOCK(d);
2315bba4862cSAriff Abdullah break;
2316bba4862cSAriff Abdullah default:
23175ac39263SChristos Margiolis PCM_UNLOCK(d);
2318bba4862cSAriff Abdullah break;
2319bba4862cSAriff Abdullah }
2320bba4862cSAriff Abdullah
2321e4e61333SAriff Abdullah return (0);
2322987e5972SCameron Grant }
2323987e5972SCameron Grant
2324b611c801SAlexander Leidinger /**
2325b611c801SAlexander Leidinger * @brief Queries sound driver for sample-aligned hardware buffer pointer index
2326b611c801SAlexander Leidinger *
2327b611c801SAlexander Leidinger * This function obtains the hardware pointer location, then aligns it to
2328b611c801SAlexander Leidinger * the current bytes-per-sample value before returning. (E.g., a channel
2329b611c801SAlexander Leidinger * running in 16 bit stereo mode would require 4 bytes per sample, so a
2330b611c801SAlexander Leidinger * hwptr value ranging from 32-35 would be returned as 32.)
2331b611c801SAlexander Leidinger *
2332b611c801SAlexander Leidinger * @param c PCM channel context
2333b611c801SAlexander Leidinger * @returns sample-aligned hardware buffer pointer index
2334b611c801SAlexander Leidinger */
2335987e5972SCameron Grant int
chn_getptr(struct pcm_channel * c)233666ef8af5SCameron Grant chn_getptr(struct pcm_channel *c)
2337987e5972SCameron Grant {
2338c17cb0c6SAriff Abdullah int hwptr;
2339c17cb0c6SAriff Abdullah
2340c17cb0c6SAriff Abdullah CHN_LOCKASSERT(c);
2341fd1475d3SAriff Abdullah hwptr = (CHN_STARTED(c)) ? CHANNEL_GETPTR(c->methods, c->devinfo) : 0;
234290da2b28SAriff Abdullah return (hwptr - (hwptr % sndbuf_getalign(c->bufhard)));
2343987e5972SCameron Grant }
2344987e5972SCameron Grant
234566ef8af5SCameron Grant struct pcmchan_caps *
chn_getcaps(struct pcm_channel * c)234666ef8af5SCameron Grant chn_getcaps(struct pcm_channel *c)
2347987e5972SCameron Grant {
234866ef8af5SCameron Grant CHN_LOCKASSERT(c);
23490f55ac6cSCameron Grant return CHANNEL_GETCAPS(c->methods, c->devinfo);
2350987e5972SCameron Grant }
2351513693beSCameron Grant
2352513693beSCameron Grant u_int32_t
chn_getformats(struct pcm_channel * c)235366ef8af5SCameron Grant chn_getformats(struct pcm_channel *c)
2354513693beSCameron Grant {
2355513693beSCameron Grant u_int32_t *fmtlist, fmts;
2356513693beSCameron Grant int i;
2357513693beSCameron Grant
2358513693beSCameron Grant fmtlist = chn_getcaps(c)->fmtlist;
2359513693beSCameron Grant fmts = 0;
2360513693beSCameron Grant for (i = 0; fmtlist[i]; i++)
2361513693beSCameron Grant fmts |= fmtlist[i];
2362513693beSCameron Grant
2363a0eed7bcSDag-Erling Smørgrav /* report software-supported formats */
236490da2b28SAriff Abdullah if (!CHN_BITPERFECT(c) && report_soft_formats)
236590da2b28SAriff Abdullah fmts |= AFMT_CONVERTIBLE;
2366a0eed7bcSDag-Erling Smørgrav
236790da2b28SAriff Abdullah return (AFMT_ENCODING(fmts));
2368c9c6ba09SCameron Grant }
236966ef8af5SCameron Grant
2370285648f9SCameron Grant int
chn_notify(struct pcm_channel * c,u_int32_t flags)2371285648f9SCameron Grant chn_notify(struct pcm_channel *c, u_int32_t flags)
2372285648f9SCameron Grant {
237390da2b28SAriff Abdullah struct pcm_channel *ch;
237490da2b28SAriff Abdullah struct pcmchan_caps *caps;
237590da2b28SAriff Abdullah uint32_t bestformat, bestspeed, besthwformat, *vchanformat, *vchanrate;
237690da2b28SAriff Abdullah uint32_t vpflags;
237790da2b28SAriff Abdullah int dirty, err, run, nrun;
2378285648f9SCameron Grant
2379e4e61333SAriff Abdullah CHN_LOCKASSERT(c);
238012e524a2SDon Lewis
2381e4e61333SAriff Abdullah if (CHN_EMPTY(c, children))
2382164651f1SChristos Margiolis return (0);
2383285648f9SCameron Grant
2384e4e61333SAriff Abdullah err = 0;
2385e4e61333SAriff Abdullah
238649c5e6e2SCameron Grant /*
2387e4e61333SAriff Abdullah * If the hwchan is running, we can't change its rate, format or
238849c5e6e2SCameron Grant * blocksize
238949c5e6e2SCameron Grant */
2390e4e61333SAriff Abdullah run = (CHN_STARTED(c)) ? 1 : 0;
239149c5e6e2SCameron Grant if (run)
239249c5e6e2SCameron Grant flags &= CHN_N_VOLUME | CHN_N_TRIGGER;
239349c5e6e2SCameron Grant
2394285648f9SCameron Grant if (flags & CHN_N_RATE) {
239590da2b28SAriff Abdullah /*
239690da2b28SAriff Abdullah * XXX I'll make good use of this someday.
239790da2b28SAriff Abdullah * However this is currently being superseded by
239890da2b28SAriff Abdullah * the availability of CHN_F_VCHAN_DYNAMIC.
239990da2b28SAriff Abdullah */
2400285648f9SCameron Grant }
240190da2b28SAriff Abdullah
2402285648f9SCameron Grant if (flags & CHN_N_FORMAT) {
240390da2b28SAriff Abdullah /*
240490da2b28SAriff Abdullah * XXX I'll make good use of this someday.
240590da2b28SAriff Abdullah * However this is currently being superseded by
240690da2b28SAriff Abdullah * the availability of CHN_F_VCHAN_DYNAMIC.
240790da2b28SAriff Abdullah */
2408285648f9SCameron Grant }
240990da2b28SAriff Abdullah
2410285648f9SCameron Grant if (flags & CHN_N_VOLUME) {
241190da2b28SAriff Abdullah /*
241290da2b28SAriff Abdullah * XXX I'll make good use of this someday, though
241390da2b28SAriff Abdullah * soft volume control is currently pretty much
241490da2b28SAriff Abdullah * integrated.
241590da2b28SAriff Abdullah */
2416285648f9SCameron Grant }
241790da2b28SAriff Abdullah
2418285648f9SCameron Grant if (flags & CHN_N_BLOCKSIZE) {
2419285648f9SCameron Grant /*
2420a580b31aSAriff Abdullah * Set to default latency profile
2421285648f9SCameron Grant */
2422a580b31aSAriff Abdullah chn_setlatency(c, chn_latency);
2423285648f9SCameron Grant }
242490da2b28SAriff Abdullah
242590da2b28SAriff Abdullah if ((flags & CHN_N_TRIGGER) && !(c->flags & CHN_F_VCHAN_DYNAMIC)) {
2426bba4862cSAriff Abdullah nrun = CHN_EMPTY(c, children.busy) ? 0 : 1;
242749c5e6e2SCameron Grant if (nrun && !run)
2428e4e61333SAriff Abdullah err = chn_start(c, 1);
242949c5e6e2SCameron Grant if (!nrun && run)
2430285648f9SCameron Grant chn_abort(c);
243190da2b28SAriff Abdullah flags &= ~CHN_N_TRIGGER;
243290da2b28SAriff Abdullah }
243390da2b28SAriff Abdullah
243490da2b28SAriff Abdullah if (flags & CHN_N_TRIGGER) {
243590da2b28SAriff Abdullah if (c->direction == PCMDIR_PLAY) {
243690da2b28SAriff Abdullah vchanformat = &c->parentsnddev->pvchanformat;
243790da2b28SAriff Abdullah vchanrate = &c->parentsnddev->pvchanrate;
243890da2b28SAriff Abdullah } else {
243990da2b28SAriff Abdullah vchanformat = &c->parentsnddev->rvchanformat;
244090da2b28SAriff Abdullah vchanrate = &c->parentsnddev->rvchanrate;
244190da2b28SAriff Abdullah }
244290da2b28SAriff Abdullah
244390da2b28SAriff Abdullah /* Dynamic Virtual Channel */
244490da2b28SAriff Abdullah if (!(c->flags & CHN_F_VCHAN_ADAPTIVE)) {
244590da2b28SAriff Abdullah bestformat = *vchanformat;
244690da2b28SAriff Abdullah bestspeed = *vchanrate;
244790da2b28SAriff Abdullah } else {
244890da2b28SAriff Abdullah bestformat = 0;
244990da2b28SAriff Abdullah bestspeed = 0;
245090da2b28SAriff Abdullah }
245190da2b28SAriff Abdullah
245290da2b28SAriff Abdullah besthwformat = 0;
245390da2b28SAriff Abdullah nrun = 0;
245490da2b28SAriff Abdullah caps = chn_getcaps(c);
245590da2b28SAriff Abdullah dirty = 0;
245690da2b28SAriff Abdullah vpflags = 0;
245790da2b28SAriff Abdullah
245890da2b28SAriff Abdullah CHN_FOREACH(ch, c, children.busy) {
245990da2b28SAriff Abdullah CHN_LOCK(ch);
246090da2b28SAriff Abdullah if ((ch->format & AFMT_PASSTHROUGH) &&
246190da2b28SAriff Abdullah snd_fmtvalid(ch->format, caps->fmtlist)) {
246290da2b28SAriff Abdullah bestformat = ch->format;
246390da2b28SAriff Abdullah bestspeed = ch->speed;
246490da2b28SAriff Abdullah CHN_UNLOCK(ch);
246590da2b28SAriff Abdullah vpflags = CHN_F_PASSTHROUGH;
246690da2b28SAriff Abdullah nrun++;
246790da2b28SAriff Abdullah break;
246890da2b28SAriff Abdullah }
246990da2b28SAriff Abdullah if ((ch->flags & CHN_F_EXCLUSIVE) && vpflags == 0) {
247090da2b28SAriff Abdullah if (c->flags & CHN_F_VCHAN_ADAPTIVE) {
247190da2b28SAriff Abdullah bestspeed = ch->speed;
247290da2b28SAriff Abdullah RANGE(bestspeed, caps->minspeed,
247390da2b28SAriff Abdullah caps->maxspeed);
247490da2b28SAriff Abdullah besthwformat = snd_fmtbest(ch->format,
247590da2b28SAriff Abdullah caps->fmtlist);
247690da2b28SAriff Abdullah if (besthwformat != 0)
247790da2b28SAriff Abdullah bestformat = besthwformat;
247890da2b28SAriff Abdullah }
247990da2b28SAriff Abdullah CHN_UNLOCK(ch);
248090da2b28SAriff Abdullah vpflags = CHN_F_EXCLUSIVE;
248190da2b28SAriff Abdullah nrun++;
248290da2b28SAriff Abdullah continue;
248390da2b28SAriff Abdullah }
248490da2b28SAriff Abdullah if (!(c->flags & CHN_F_VCHAN_ADAPTIVE) ||
248590da2b28SAriff Abdullah vpflags != 0) {
248690da2b28SAriff Abdullah CHN_UNLOCK(ch);
248790da2b28SAriff Abdullah nrun++;
248890da2b28SAriff Abdullah continue;
248990da2b28SAriff Abdullah }
249090da2b28SAriff Abdullah if (ch->speed > bestspeed) {
249190da2b28SAriff Abdullah bestspeed = ch->speed;
249290da2b28SAriff Abdullah RANGE(bestspeed, caps->minspeed,
249390da2b28SAriff Abdullah caps->maxspeed);
249490da2b28SAriff Abdullah }
249590da2b28SAriff Abdullah besthwformat = snd_fmtbest(ch->format, caps->fmtlist);
249690da2b28SAriff Abdullah if (!(besthwformat & AFMT_VCHAN)) {
249790da2b28SAriff Abdullah CHN_UNLOCK(ch);
249890da2b28SAriff Abdullah nrun++;
249990da2b28SAriff Abdullah continue;
250090da2b28SAriff Abdullah }
250190da2b28SAriff Abdullah if (AFMT_CHANNEL(besthwformat) >
250290da2b28SAriff Abdullah AFMT_CHANNEL(bestformat))
250390da2b28SAriff Abdullah bestformat = besthwformat;
250490da2b28SAriff Abdullah else if (AFMT_CHANNEL(besthwformat) ==
250590da2b28SAriff Abdullah AFMT_CHANNEL(bestformat) &&
250690da2b28SAriff Abdullah AFMT_BIT(besthwformat) > AFMT_BIT(bestformat))
250790da2b28SAriff Abdullah bestformat = besthwformat;
250890da2b28SAriff Abdullah CHN_UNLOCK(ch);
250990da2b28SAriff Abdullah nrun++;
251090da2b28SAriff Abdullah }
251190da2b28SAriff Abdullah
251290da2b28SAriff Abdullah if (bestformat == 0)
251390da2b28SAriff Abdullah bestformat = c->format;
251490da2b28SAriff Abdullah if (bestspeed == 0)
251590da2b28SAriff Abdullah bestspeed = c->speed;
251690da2b28SAriff Abdullah
251790da2b28SAriff Abdullah if (bestformat != c->format || bestspeed != c->speed)
251890da2b28SAriff Abdullah dirty = 1;
251990da2b28SAriff Abdullah
252090da2b28SAriff Abdullah c->flags &= ~(CHN_F_PASSTHROUGH | CHN_F_EXCLUSIVE);
252190da2b28SAriff Abdullah c->flags |= vpflags;
252290da2b28SAriff Abdullah
252390da2b28SAriff Abdullah if (nrun && !run) {
252490da2b28SAriff Abdullah if (dirty) {
252590da2b28SAriff Abdullah bestspeed = CHANNEL_SETSPEED(c->methods,
252690da2b28SAriff Abdullah c->devinfo, bestspeed);
252790da2b28SAriff Abdullah err = chn_reset(c, bestformat, bestspeed);
252890da2b28SAriff Abdullah }
252990da2b28SAriff Abdullah if (err == 0 && dirty) {
253090da2b28SAriff Abdullah CHN_FOREACH(ch, c, children.busy) {
253190da2b28SAriff Abdullah CHN_LOCK(ch);
253290da2b28SAriff Abdullah if (VCHAN_SYNC_REQUIRED(ch))
253390da2b28SAriff Abdullah vchan_sync(ch);
253490da2b28SAriff Abdullah CHN_UNLOCK(ch);
253590da2b28SAriff Abdullah }
253690da2b28SAriff Abdullah }
253790da2b28SAriff Abdullah if (err == 0) {
253890da2b28SAriff Abdullah if (dirty)
253990da2b28SAriff Abdullah c->flags |= CHN_F_DIRTY;
254090da2b28SAriff Abdullah err = chn_start(c, 1);
254190da2b28SAriff Abdullah }
254290da2b28SAriff Abdullah }
254390da2b28SAriff Abdullah
254490da2b28SAriff Abdullah if (nrun && run && dirty) {
254590da2b28SAriff Abdullah chn_abort(c);
254690da2b28SAriff Abdullah bestspeed = CHANNEL_SETSPEED(c->methods, c->devinfo,
254790da2b28SAriff Abdullah bestspeed);
254890da2b28SAriff Abdullah err = chn_reset(c, bestformat, bestspeed);
254990da2b28SAriff Abdullah if (err == 0) {
255090da2b28SAriff Abdullah CHN_FOREACH(ch, c, children.busy) {
255190da2b28SAriff Abdullah CHN_LOCK(ch);
255290da2b28SAriff Abdullah if (VCHAN_SYNC_REQUIRED(ch))
255390da2b28SAriff Abdullah vchan_sync(ch);
255490da2b28SAriff Abdullah CHN_UNLOCK(ch);
255590da2b28SAriff Abdullah }
255690da2b28SAriff Abdullah }
255790da2b28SAriff Abdullah if (err == 0) {
255890da2b28SAriff Abdullah c->flags |= CHN_F_DIRTY;
255990da2b28SAriff Abdullah err = chn_start(c, 1);
256090da2b28SAriff Abdullah }
256190da2b28SAriff Abdullah }
256290da2b28SAriff Abdullah
256390da2b28SAriff Abdullah if (err == 0 && !(bestformat & AFMT_PASSTHROUGH) &&
256490da2b28SAriff Abdullah (bestformat & AFMT_VCHAN)) {
256590da2b28SAriff Abdullah *vchanformat = bestformat;
256690da2b28SAriff Abdullah *vchanrate = bestspeed;
256790da2b28SAriff Abdullah }
256890da2b28SAriff Abdullah
256990da2b28SAriff Abdullah if (!nrun && run) {
257090da2b28SAriff Abdullah c->flags &= ~(CHN_F_PASSTHROUGH | CHN_F_EXCLUSIVE);
257190da2b28SAriff Abdullah bestformat = *vchanformat;
257290da2b28SAriff Abdullah bestspeed = *vchanrate;
257390da2b28SAriff Abdullah chn_abort(c);
257490da2b28SAriff Abdullah if (c->format != bestformat || c->speed != bestspeed)
257590da2b28SAriff Abdullah chn_reset(c, bestformat, bestspeed);
257690da2b28SAriff Abdullah }
2577285648f9SCameron Grant }
2578e4e61333SAriff Abdullah
2579e4e61333SAriff Abdullah return (err);
2580285648f9SCameron Grant }
258112e524a2SDon Lewis
2582b611c801SAlexander Leidinger /**
2583b611c801SAlexander Leidinger * @brief Fetch array of supported discrete sample rates
2584b611c801SAlexander Leidinger *
2585b611c801SAlexander Leidinger * Wrapper for CHANNEL_GETRATES. Please see channel_if.m:getrates() for
2586b611c801SAlexander Leidinger * detailed information.
2587b611c801SAlexander Leidinger *
2588b611c801SAlexander Leidinger * @note If the operation isn't supported, this function will just return 0
2589b611c801SAlexander Leidinger * (no rates in the array), and *rates will be set to NULL. Callers
2590b611c801SAlexander Leidinger * should examine rates @b only if this function returns non-zero.
2591b611c801SAlexander Leidinger *
2592b611c801SAlexander Leidinger * @param c pcm channel to examine
2593b611c801SAlexander Leidinger * @param rates pointer to array of integers; rate table will be recorded here
2594b611c801SAlexander Leidinger *
2595b611c801SAlexander Leidinger * @return number of rates in the array pointed to be @c rates
2596b611c801SAlexander Leidinger */
2597b611c801SAlexander Leidinger int
chn_getrates(struct pcm_channel * c,int ** rates)2598b611c801SAlexander Leidinger chn_getrates(struct pcm_channel *c, int **rates)
2599b611c801SAlexander Leidinger {
2600b611c801SAlexander Leidinger KASSERT(rates != NULL, ("rates is null"));
2601b611c801SAlexander Leidinger CHN_LOCKASSERT(c);
2602b611c801SAlexander Leidinger return CHANNEL_GETRATES(c->methods, c->devinfo, rates);
2603b611c801SAlexander Leidinger }
2604b611c801SAlexander Leidinger
2605b611c801SAlexander Leidinger /**
2606b611c801SAlexander Leidinger * @brief Remove channel from a sync group, if there is one.
2607b611c801SAlexander Leidinger *
2608b611c801SAlexander Leidinger * This function is initially intended for the following conditions:
2609b611c801SAlexander Leidinger * - Starting a syncgroup (@c SNDCTL_DSP_SYNCSTART ioctl)
2610b611c801SAlexander Leidinger * - Closing a device. (A channel can't be destroyed if it's still in use.)
2611b611c801SAlexander Leidinger *
2612b611c801SAlexander Leidinger * @note Before calling this function, the syncgroup list mutex must be
2613b611c801SAlexander Leidinger * held. (Consider pcm_channel::sm protected by the SG list mutex
2614b611c801SAlexander Leidinger * whether @c c is locked or not.)
2615b611c801SAlexander Leidinger *
2616b611c801SAlexander Leidinger * @param c channel device to be started or closed
2617b611c801SAlexander Leidinger * @returns If this channel was the only member of a group, the group ID
2618b611c801SAlexander Leidinger * is returned to the caller so that the caller can release it
2619b611c801SAlexander Leidinger * via free_unr() after giving up the syncgroup lock. Else it
2620b611c801SAlexander Leidinger * returns 0.
2621b611c801SAlexander Leidinger */
2622b611c801SAlexander Leidinger int
chn_syncdestroy(struct pcm_channel * c)2623b611c801SAlexander Leidinger chn_syncdestroy(struct pcm_channel *c)
2624b611c801SAlexander Leidinger {
2625b611c801SAlexander Leidinger struct pcmchan_syncmember *sm;
2626b611c801SAlexander Leidinger struct pcmchan_syncgroup *sg;
2627b611c801SAlexander Leidinger int sg_id;
2628b611c801SAlexander Leidinger
2629b611c801SAlexander Leidinger sg_id = 0;
2630b611c801SAlexander Leidinger
2631b611c801SAlexander Leidinger PCM_SG_LOCKASSERT(MA_OWNED);
2632b611c801SAlexander Leidinger
2633b611c801SAlexander Leidinger if (c->sm != NULL) {
2634b611c801SAlexander Leidinger sm = c->sm;
2635b611c801SAlexander Leidinger sg = sm->parent;
2636b611c801SAlexander Leidinger c->sm = NULL;
2637b611c801SAlexander Leidinger
2638b611c801SAlexander Leidinger KASSERT(sg != NULL, ("syncmember has null parent"));
2639b611c801SAlexander Leidinger
2640b611c801SAlexander Leidinger SLIST_REMOVE(&sg->members, sm, pcmchan_syncmember, link);
2641b611c801SAlexander Leidinger free(sm, M_DEVBUF);
2642b611c801SAlexander Leidinger
2643b611c801SAlexander Leidinger if (SLIST_EMPTY(&sg->members)) {
2644b611c801SAlexander Leidinger SLIST_REMOVE(&snd_pcm_syncgroups, sg, pcmchan_syncgroup, link);
2645b611c801SAlexander Leidinger sg_id = sg->id;
2646b611c801SAlexander Leidinger free(sg, M_DEVBUF);
2647b611c801SAlexander Leidinger }
2648b611c801SAlexander Leidinger }
2649b611c801SAlexander Leidinger
2650b611c801SAlexander Leidinger return sg_id;
2651b611c801SAlexander Leidinger }
2652b611c801SAlexander Leidinger
2653b611c801SAlexander Leidinger #ifdef OSSV4_EXPERIMENT
2654b611c801SAlexander Leidinger int
chn_getpeaks(struct pcm_channel * c,int * lpeak,int * rpeak)2655b611c801SAlexander Leidinger chn_getpeaks(struct pcm_channel *c, int *lpeak, int *rpeak)
2656b611c801SAlexander Leidinger {
2657b611c801SAlexander Leidinger CHN_LOCKASSERT(c);
2658b611c801SAlexander Leidinger return CHANNEL_GETPEAKS(c->methods, c->devinfo, lpeak, rpeak);
2659b611c801SAlexander Leidinger }
2660b611c801SAlexander Leidinger #endif
2661