2 * Copyright (c) 2001 Cameron Grant <cg@FreeBSD.org>
3 * Copyright (c) 2006 Ariff Abdullah <ariff@FreeBSD.org>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * Almost entirely rewritten to add multi-format/channels mixing support.
31 #include <dev/sound/pcm/sound.h>
32 #include <dev/sound/pcm/vchan.h>
33 #include "feeder_if.h"
35 SND_DECLARE_FILE("$FreeBSD$");
37 MALLOC_DEFINE(M_VCHANFEEDER, "vchanfeed", "pcm vchan feeder");
40 * Default speed / format
42 #define VCHAN_DEFAULT_SPEED 48000
43 #define VCHAN_DEFAULT_AFMT (AFMT_S16_LE | AFMT_STEREO)
44 #define VCHAN_DEFAULT_STRFMT "s16le"
46 typedef uint32_t (*feed_vchan_mixer)(uint8_t *, uint8_t *, uint32_t);
49 uint32_t spd, fmt, fmts[2], blksz, bps, run;
50 struct pcm_channel *channel, *parent;
51 struct pcmchan_caps caps;
54 /* support everything (mono / stereo), except a-law / mu-law */
55 static struct afmtstr_table vchan_supported_fmts[] = {
56 { "u8", AFMT_U8 }, { "s8", AFMT_S8 },
57 { "s16le", AFMT_S16_LE }, { "s16be", AFMT_S16_BE },
58 { "u16le", AFMT_U16_LE }, { "u16be", AFMT_U16_BE },
59 { "s24le", AFMT_S24_LE }, { "s24be", AFMT_S24_BE },
60 { "u24le", AFMT_U24_LE }, { "u24be", AFMT_U24_BE },
61 { "s32le", AFMT_S32_LE }, { "s32be", AFMT_S32_BE },
62 { "u32le", AFMT_U32_LE }, { "u32be", AFMT_U32_BE },
66 /* alias table, shorter. */
69 } vchan_fmtstralias[] = {
70 { "8", "u8" }, { "16", "s16le" },
71 { "24", "s24le" }, { "32", "s32le" },
75 #define vchan_valid_format(fmt) \
76 afmt2afmtstr(vchan_supported_fmts, fmt, NULL, 0, 0, \
77 AFMTSTR_STEREO_RETURN)
78 #define vchan_valid_strformat(strfmt) \
79 afmtstr2afmt(vchan_supported_fmts, strfmt, AFMTSTR_STEREO_RETURN);
82 * Need specialized WRITE macros since 32bit might involved saturation
83 * if calculation is done within 32bit arithmetic.
85 #define VCHAN_PCM_WRITE_S8_NE(b8, val) PCM_WRITE_S8(b8, val)
86 #define VCHAN_PCM_WRITE_S16_LE(b8, val) PCM_WRITE_S16_LE(b8, val)
87 #define VCHAN_PCM_WRITE_S24_LE(b8, val) PCM_WRITE_S24_LE(b8, val)
88 #define VCHAN_PCM_WRITE_S32_LE(b8, val) _PCM_WRITE_S32_LE(b8, val)
89 #define VCHAN_PCM_WRITE_S16_BE(b8, val) PCM_WRITE_S16_BE(b8, val)
90 #define VCHAN_PCM_WRITE_S24_BE(b8, val) PCM_WRITE_S24_BE(b8, val)
91 #define VCHAN_PCM_WRITE_S32_BE(b8, val) _PCM_WRITE_S32_BE(b8, val)
92 #define VCHAN_PCM_WRITE_U8_NE(b8, val) PCM_WRITE_U8(b8, val)
93 #define VCHAN_PCM_WRITE_U16_LE(b8, val) PCM_WRITE_U16_LE(b8, val)
94 #define VCHAN_PCM_WRITE_U24_LE(b8, val) PCM_WRITE_U24_LE(b8, val)
95 #define VCHAN_PCM_WRITE_U32_LE(b8, val) _PCM_WRITE_U32_LE(b8, val)
96 #define VCHAN_PCM_WRITE_U16_BE(b8, val) PCM_WRITE_U16_BE(b8, val)
97 #define VCHAN_PCM_WRITE_U24_BE(b8, val) PCM_WRITE_U24_BE(b8, val)
98 #define VCHAN_PCM_WRITE_U32_BE(b8, val) _PCM_WRITE_U32_BE(b8, val)
100 #define FEEDER_VCHAN_MIX(FMTBIT, VCHAN_INTCAST, SIGN, SIGNS, ENDIAN, ENDIANS) \
102 feed_vchan_mix_##SIGNS##FMTBIT##ENDIANS(uint8_t *to, uint8_t *tmp, \
114 tmp -= PCM_##FMTBIT##_BPS; \
115 to -= PCM_##FMTBIT##_BPS; \
116 i -= PCM_##FMTBIT##_BPS; \
117 x = PCM_READ_##SIGN##FMTBIT##_##ENDIAN(tmp); \
118 y = PCM_READ_##SIGN##FMTBIT##_##ENDIAN(to); \
119 z = (VCHAN_INTCAST)x + y; \
120 x = PCM_CLAMP_##SIGN##FMTBIT(z); \
121 VCHAN_PCM_WRITE_##SIGN##FMTBIT##_##ENDIAN(to, x); \
127 FEEDER_VCHAN_MIX(8, int32_t, S, s, NE, ne)
128 FEEDER_VCHAN_MIX(16, int32_t, S, s, LE, le)
129 FEEDER_VCHAN_MIX(24, int32_t, S, s, LE, le)
130 FEEDER_VCHAN_MIX(32, intpcm_t, S, s, LE, le)
131 FEEDER_VCHAN_MIX(16, int32_t, S, s, BE, be)
132 FEEDER_VCHAN_MIX(24, int32_t, S, s, BE, be)
133 FEEDER_VCHAN_MIX(32, intpcm_t, S, s, BE, be)
134 FEEDER_VCHAN_MIX(8, int32_t, U, u, NE, ne)
135 FEEDER_VCHAN_MIX(16, int32_t, U, u, LE, le)
136 FEEDER_VCHAN_MIX(24, int32_t, U, u, LE, le)
137 FEEDER_VCHAN_MIX(32, intpcm_t, U, u, LE, le)
138 FEEDER_VCHAN_MIX(16, int32_t, U, u, BE, be)
139 FEEDER_VCHAN_MIX(24, int32_t, U, u, BE, be)
140 FEEDER_VCHAN_MIX(32, intpcm_t, U, u, BE, be)
142 struct feed_vchan_info {
145 feed_vchan_mixer mix;
148 static struct feed_vchan_info feed_vchan_info_tbl[] = {
149 { AFMT_S8, PCM_8_BPS, feed_vchan_mix_s8ne },
150 { AFMT_S16_LE, PCM_16_BPS, feed_vchan_mix_s16le },
151 { AFMT_S24_LE, PCM_24_BPS, feed_vchan_mix_s24le },
152 { AFMT_S32_LE, PCM_32_BPS, feed_vchan_mix_s32le },
153 { AFMT_S16_BE, PCM_16_BPS, feed_vchan_mix_s16be },
154 { AFMT_S24_BE, PCM_24_BPS, feed_vchan_mix_s24be },
155 { AFMT_S32_BE, PCM_32_BPS, feed_vchan_mix_s32be },
156 { AFMT_U8, PCM_8_BPS, feed_vchan_mix_u8ne },
157 { AFMT_U16_LE, PCM_16_BPS, feed_vchan_mix_u16le },
158 { AFMT_U24_LE, PCM_24_BPS, feed_vchan_mix_u24le },
159 { AFMT_U32_LE, PCM_32_BPS, feed_vchan_mix_u32le },
160 { AFMT_U16_BE, PCM_16_BPS, feed_vchan_mix_u16be },
161 { AFMT_U24_BE, PCM_24_BPS, feed_vchan_mix_u24be },
162 { AFMT_U32_BE, PCM_32_BPS, feed_vchan_mix_u32be },
165 #define FVCHAN_DATA(i, c) ((intptr_t)((((i) & 0x1f) << 4) | ((c) & 0xf)))
166 #define FVCHAN_INFOIDX(m) (((m) >> 4) & 0x1f)
167 #define FVCHAN_CHANNELS(m) ((m) & 0xf)
170 feed_vchan_init(struct pcm_feeder *f)
174 if (f->desc->out != f->desc->in)
177 channels = (f->desc->out & AFMT_STEREO) ? 2 : 1;
179 for (i = 0; i < sizeof(feed_vchan_info_tbl) /
180 sizeof(feed_vchan_info_tbl[0]); i++) {
181 if ((f->desc->out & ~AFMT_STEREO) ==
182 feed_vchan_info_tbl[i].format) {
183 f->data = (void *)FVCHAN_DATA(i, channels);
192 feed_vchan(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
193 uint32_t count, void *source)
195 struct feed_vchan_info *info;
196 struct snd_dbuf *src = source;
197 struct pcmchan_children *cce;
198 struct pcm_channel *ch;
199 uint32_t cnt, mcnt, rcnt, sz;
202 sz = sndbuf_getsize(src);
206 info = &feed_vchan_info_tbl[FVCHAN_INFOIDX((intptr_t)f->data)];
207 sz = info->bps * FVCHAN_CHANNELS((intptr_t)f->data);
213 * we are going to use our source as a temporary buffer since it's
214 * got no other purpose. we obtain our data by traversing the channel
215 * list of children and calling vchan_mix_* to mix count bytes from
216 * each into our destination buffer, b
218 tmp = sndbuf_getbuf(src);
222 SLIST_FOREACH(cce, &c->children, link) {
225 if (!(ch->flags & CHN_F_TRIGGERED)) {
229 if ((ch->flags & CHN_F_MAPPED) && !(ch->flags & CHN_F_CLOSING))
230 sndbuf_acquire(ch->bufsoft, NULL,
231 sndbuf_getfree(ch->bufsoft));
233 rcnt = FEEDER_FEED(ch->feeder, ch, b, count,
238 cnt = FEEDER_FEED(ch->feeder, ch, tmp, count,
244 sndbuf_zerodata(f->desc->out),
248 cnt = info->mix(b, tmp, cnt);
256 if (++c->feedcount == 0)
262 static struct pcm_feederdesc feeder_vchan_desc[] = {
263 {FEEDER_MIXER, AFMT_S8, AFMT_S8, 0},
264 {FEEDER_MIXER, AFMT_S16_LE, AFMT_S16_LE, 0},
265 {FEEDER_MIXER, AFMT_S24_LE, AFMT_S24_LE, 0},
266 {FEEDER_MIXER, AFMT_S32_LE, AFMT_S32_LE, 0},
267 {FEEDER_MIXER, AFMT_S16_BE, AFMT_S16_BE, 0},
268 {FEEDER_MIXER, AFMT_S24_BE, AFMT_S24_BE, 0},
269 {FEEDER_MIXER, AFMT_S32_BE, AFMT_S32_BE, 0},
270 {FEEDER_MIXER, AFMT_S8 | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0},
271 {FEEDER_MIXER, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0},
272 {FEEDER_MIXER, AFMT_S24_LE | AFMT_STEREO, AFMT_S24_LE | AFMT_STEREO, 0},
273 {FEEDER_MIXER, AFMT_S32_LE | AFMT_STEREO, AFMT_S32_LE | AFMT_STEREO, 0},
274 {FEEDER_MIXER, AFMT_S16_BE | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0},
275 {FEEDER_MIXER, AFMT_S24_BE | AFMT_STEREO, AFMT_S24_BE | AFMT_STEREO, 0},
276 {FEEDER_MIXER, AFMT_S32_BE | AFMT_STEREO, AFMT_S32_BE | AFMT_STEREO, 0},
277 {FEEDER_MIXER, AFMT_U8, AFMT_U8, 0},
278 {FEEDER_MIXER, AFMT_U16_LE, AFMT_U16_LE, 0},
279 {FEEDER_MIXER, AFMT_U24_LE, AFMT_U24_LE, 0},
280 {FEEDER_MIXER, AFMT_U32_LE, AFMT_U32_LE, 0},
281 {FEEDER_MIXER, AFMT_U16_BE, AFMT_U16_BE, 0},
282 {FEEDER_MIXER, AFMT_U24_BE, AFMT_U24_BE, 0},
283 {FEEDER_MIXER, AFMT_U32_BE, AFMT_U32_BE, 0},
284 {FEEDER_MIXER, AFMT_U8 | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0},
285 {FEEDER_MIXER, AFMT_U16_LE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0},
286 {FEEDER_MIXER, AFMT_U24_LE | AFMT_STEREO, AFMT_U24_LE | AFMT_STEREO, 0},
287 {FEEDER_MIXER, AFMT_U32_LE | AFMT_STEREO, AFMT_U32_LE | AFMT_STEREO, 0},
288 {FEEDER_MIXER, AFMT_U16_BE | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0},
289 {FEEDER_MIXER, AFMT_U24_BE | AFMT_STEREO, AFMT_U24_BE | AFMT_STEREO, 0},
290 {FEEDER_MIXER, AFMT_U32_BE | AFMT_STEREO, AFMT_U32_BE | AFMT_STEREO, 0},
293 static kobj_method_t feeder_vchan_methods[] = {
294 KOBJMETHOD(feeder_init, feed_vchan_init),
295 KOBJMETHOD(feeder_feed, feed_vchan),
298 FEEDER_DECLARE(feeder_vchan, 2, NULL);
300 /************************************************************/
303 vchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
306 struct pcm_channel *parent = devinfo;
308 KASSERT(dir == PCMDIR_PLAY, ("vchan_init: bad direction"));
309 ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
313 ch->spd = DSP_DEFAULT_SPEED;
316 c->flags |= CHN_F_VIRTUAL;
322 vchan_free(kobj_t obj, void *data)
324 free(data, M_DEVBUF);
329 vchan_setformat(kobj_t obj, void *data, uint32_t format)
331 struct vchinfo *ch = data;
332 struct pcm_channel *parent = ch->parent;
333 struct pcm_channel *channel = ch->channel;
337 ch->bps <<= (ch->fmt & AFMT_STEREO)? 1 : 0;
338 if (ch->fmt & AFMT_16BIT)
340 else if (ch->fmt & AFMT_24BIT)
342 else if (ch->fmt & AFMT_32BIT)
345 chn_notify(parent, CHN_N_FORMAT);
347 sndbuf_setfmt(channel->bufsoft, format);
352 vchan_setspeed(kobj_t obj, void *data, uint32_t speed)
354 struct vchinfo *ch = data;
355 struct pcm_channel *parent = ch->parent;
356 struct pcm_channel *channel = ch->channel;
361 speed = sndbuf_getspd(parent->bufsoft);
368 vchan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
370 struct vchinfo *ch = data;
371 struct pcm_channel *channel = ch->channel;
372 struct pcm_channel *parent = ch->parent;
373 /* struct pcm_channel *channel = ch->channel; */
376 ch->blksz = blocksize;
377 /* CHN_UNLOCK(channel); */
378 sndbuf_setblksz(channel->bufhard, blocksize);
379 chn_notify(parent, CHN_N_BLOCKSIZE);
381 /* CHN_LOCK(channel); */
383 crate = ch->spd * ch->bps;
384 prate = sndbuf_getspd(parent->bufsoft) * sndbuf_getbps(parent->bufsoft);
385 blocksize = sndbuf_getblksz(parent->bufsoft);
389 blocksize += ch->bps;
391 while (blocksize >> prate)
393 blocksize = 1 << (prate - 1);
394 blocksize -= blocksize % ch->bps;
395 /* XXX screwed !@#$ */
396 if (blocksize < ch->bps)
397 blocksize = 4096 - (4096 % ch->bps);
403 vchan_trigger(kobj_t obj, void *data, int go)
405 struct vchinfo *ch = data;
406 struct pcm_channel *parent = ch->parent;
407 struct pcm_channel *channel = ch->channel;
409 if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD)
412 ch->run = (go == PCMTRIG_START)? 1 : 0;
414 chn_notify(parent, CHN_N_TRIGGER);
420 static struct pcmchan_caps *
421 vchan_getcaps(kobj_t obj, void *data)
423 struct vchinfo *ch = data;
426 ch->caps.minspeed = sndbuf_getspd(ch->parent->bufsoft);
427 ch->caps.maxspeed = ch->caps.minspeed;
430 fmt = sndbuf_getfmt(ch->parent->bufsoft);
431 if (fmt != vchan_valid_format(fmt)) {
432 device_printf(ch->parent->dev,
433 "%s: WARNING: invalid vchan format! (0x%08x)\n",
435 fmt = VCHAN_DEFAULT_AFMT;
438 ch->caps.fmtlist = ch->fmts;
443 static kobj_method_t vchan_methods[] = {
444 KOBJMETHOD(channel_init, vchan_init),
445 KOBJMETHOD(channel_free, vchan_free),
446 KOBJMETHOD(channel_setformat, vchan_setformat),
447 KOBJMETHOD(channel_setspeed, vchan_setspeed),
448 KOBJMETHOD(channel_setblocksize, vchan_setblocksize),
449 KOBJMETHOD(channel_trigger, vchan_trigger),
450 KOBJMETHOD(channel_getcaps, vchan_getcaps),
453 CHANNEL_DECLARE(vchan);
456 * On the fly vchan rate settings
460 sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS)
462 struct snddev_info *d;
463 struct snddev_channel *sce;
464 struct pcm_channel *c, *ch = NULL, *fake;
465 struct pcmchan_caps *caps;
470 if (!(d->flags & SD_F_AUTOVCHAN) || d->vchancount < 1)
472 if (pcm_inprog(d, 1) != 1 && req->newptr != NULL) {
476 SLIST_FOREACH(sce, &d->channels, link) {
479 if (c->direction == PCMDIR_PLAY) {
480 if (c->flags & CHN_F_VIRTUAL) {
482 if (ch != NULL && ch != c->parentchannel) {
487 if (req->newptr != NULL &&
488 (c->flags & CHN_F_BUSY)) {
493 } else if (c->flags & CHN_F_HAS_VCHAN) {
510 err = sysctl_handle_int(oidp, &newspd, sizeof(newspd), req);
511 if (err == 0 && req->newptr != NULL) {
512 if (newspd < 1 || newspd < feeder_rate_min ||
513 newspd > feeder_rate_max) {
518 if (feeder_rate_round) {
519 caps = chn_getcaps(ch);
520 if (caps == NULL || newspd < caps->minspeed ||
521 newspd > caps->maxspeed) {
527 if (newspd != ch->speed) {
528 err = chn_setspeed(ch, newspd);
530 * Try to avoid FEEDER_RATE on parent channel if the
531 * requested value is not supported by the hardware.
533 if (!err && feeder_rate_round &&
534 (ch->feederflags & (1 << FEEDER_RATE))) {
535 newspd = sndbuf_getspd(ch->bufhard);
536 err = chn_setspeed(ch, newspd);
540 fake = pcm_getfakechan(d);
543 fake->speed = newspd;
555 sysctl_hw_snd_vchanformat(SYSCTL_HANDLER_ARGS)
557 struct snddev_info *d;
558 struct snddev_channel *sce;
559 struct pcm_channel *c, *ch = NULL, *fake;
560 uint32_t newfmt, spd;
561 char fmtstr[AFMTSTR_MAXSZ];
565 if (!(d->flags & SD_F_AUTOVCHAN) || d->vchancount < 1)
567 if (pcm_inprog(d, 1) != 1 && req->newptr != NULL) {
571 SLIST_FOREACH(sce, &d->channels, link) {
574 if (c->direction == PCMDIR_PLAY) {
575 if (c->flags & CHN_F_VIRTUAL) {
577 if (ch != NULL && ch != c->parentchannel) {
582 if (req->newptr != NULL &&
583 (c->flags & CHN_F_BUSY)) {
588 } else if (c->flags & CHN_F_HAS_VCHAN) {
596 if (ch->format != afmt2afmtstr(vchan_supported_fmts,
597 ch->format, fmtstr, sizeof(fmtstr),
598 AFMTSTR_FULL, AFMTSTR_STEREO_RETURN)) {
599 strlcpy(fmtstr, VCHAN_DEFAULT_STRFMT, sizeof(fmtstr));
609 err = sysctl_handle_string(oidp, fmtstr, sizeof(fmtstr), req);
610 if (err == 0 && req->newptr != NULL) {
611 for (i = 0; vchan_fmtstralias[i].alias != NULL; i++) {
612 if (strcmp(fmtstr, vchan_fmtstralias[i].alias) == 0) {
613 strlcpy(fmtstr, vchan_fmtstralias[i].fmtstr, sizeof(fmtstr));
617 newfmt = vchan_valid_strformat(fmtstr);
623 if (newfmt != ch->format) {
624 /* Get channel speed, before chn_reset() screw it. */
626 err = chn_reset(ch, newfmt);
628 err = chn_setspeed(ch, spd);
631 fake = pcm_getfakechan(d);
634 fake->format = newfmt;
646 /* virtual channel interface */
649 vchan_create(struct pcm_channel *parent)
651 struct snddev_info *d = parent->parentsnddev;
652 struct pcmchan_children *pce;
653 struct pcm_channel *child, *fake;
654 struct pcmchan_caps *parent_caps;
655 uint32_t vchanfmt = 0;
656 int err, first, speed = 0, r;
658 if (!(parent->flags & CHN_F_BUSY))
664 pce = malloc(sizeof(*pce), M_DEVBUF, M_WAITOK | M_ZERO);
666 /* create a new playback channel */
667 child = pcm_chn_create(d, parent, &vchan_class, PCMDIR_VIRTUAL, parent);
673 pce->channel = child;
675 /* add us to our grandparent's channel list */
677 * XXX maybe we shouldn't always add the dev_t
679 err = pcm_chn_add(d, child);
681 pcm_chn_destroy(child);
688 /* add us to our parent channel's children */
689 first = SLIST_EMPTY(&parent->children);
690 SLIST_INSERT_HEAD(&parent->children, pce, link);
691 parent->flags |= CHN_F_HAS_VCHAN;
694 parent_caps = chn_getcaps(parent);
695 if (parent_caps == NULL)
698 fake = pcm_getfakechan(d);
700 if (!err && fake != NULL) {
702 * Avoid querying kernel hint, use saved value
708 vchanfmt = fake->format;
718 r = resource_string_value(device_get_name(parent->dev),
719 device_get_unit(parent->dev),
720 "vchanformat", &vfmt);
725 vchanfmt = vchan_valid_strformat(vfmt);
726 for (r = 0; vchanfmt == 0 &&
727 vchan_fmtstralias[r].alias != NULL;
729 if (strcmp(vfmt, vchan_fmtstralias[r].alias) == 0) {
730 vchanfmt = vchan_valid_strformat(vchan_fmtstralias[r].fmtstr);
736 vchanfmt = VCHAN_DEFAULT_AFMT;
738 err = chn_reset(parent, vchanfmt);
743 * This is very sad. Few soundcards advertised as being
744 * able to do (insanely) higher/lower speed, but in
745 * reality, they simply can't. At least, we give user chance
746 * to set sane value via kernel hints or sysctl.
750 r = resource_int_value(device_get_name(parent->dev),
751 device_get_unit(parent->dev),
752 "vchanrate", &speed);
756 * No saved value from fake channel,
759 * Workaround for sb16 running
760 * poorly at 45k / 49k.
762 switch (parent_caps->maxspeed) {
768 speed = VCHAN_DEFAULT_SPEED;
769 if (speed > parent_caps->maxspeed)
770 speed = parent_caps->maxspeed;
773 if (speed < parent_caps->minspeed)
774 speed = parent_caps->minspeed;
778 if (feeder_rate_round) {
780 * Limit speed based on driver caps.
781 * This is supposed to help fixed rate, non-VRA
782 * AC97 cards, but.. (see below)
784 if (speed < parent_caps->minspeed)
785 speed = parent_caps->minspeed;
786 if (speed > parent_caps->maxspeed)
787 speed = parent_caps->maxspeed;
791 * We still need to limit the speed between
792 * feeder_rate_min <-> feeder_rate_max. This is
793 * just an escape goat if all of the above failed
796 if (speed < feeder_rate_min)
797 speed = feeder_rate_min;
798 if (speed > feeder_rate_max)
799 speed = feeder_rate_max;
801 err = chn_setspeed(parent, speed);
803 * Try to avoid FEEDER_RATE on parent channel if the
804 * requested value is not supported by the hardware.
806 if (!err && feeder_rate_round &&
807 (parent->feederflags & (1 << FEEDER_RATE))) {
808 speed = sndbuf_getspd(parent->bufhard);
809 err = chn_setspeed(parent, speed);
812 if (!err && fake != NULL) {
814 * Save new value to fake channel.
819 fake->format = vchanfmt;
826 SLIST_REMOVE(&parent->children, pce, pcmchan_children, link);
827 parent->flags &= ~CHN_F_HAS_VCHAN;
830 if (pcm_chn_remove(d, child) == 0)
831 pcm_chn_destroy(child);
841 vchan_destroy(struct pcm_channel *c)
843 struct pcm_channel *parent = c->parentchannel;
844 struct snddev_info *d = parent->parentsnddev;
845 struct pcmchan_children *pce;
846 struct snddev_channel *sce;
851 if (!(parent->flags & CHN_F_BUSY)) {
855 if (SLIST_EMPTY(&parent->children)) {
860 /* remove us from our parent's children list */
861 SLIST_FOREACH(pce, &parent->children, link) {
862 if (pce->channel == c)
868 SLIST_FOREACH(sce, &d->channels, link) {
869 if (sce->channel == c) {
871 destroy_dev(sce->dsp_devt);
872 sce->dsp_devt = NULL;
874 if (sce->dspW_devt) {
875 destroy_dev(sce->dspW_devt);
876 sce->dspW_devt = NULL;
878 if (sce->audio_devt) {
879 destroy_dev(sce->audio_devt);
880 sce->audio_devt = NULL;
882 if (sce->dspHW_devt) {
883 destroy_dev(sce->dspHW_devt);
884 sce->dspHW_devt = NULL;
890 SLIST_REMOVE(&parent->children, pce, pcmchan_children, link);
893 if (SLIST_EMPTY(&parent->children)) {
894 parent->flags &= ~(CHN_F_BUSY | CHN_F_HAS_VCHAN);
896 if (chn_reset(parent, parent->format) == 0)
897 chn_setspeed(parent, spd);
900 /* remove us from our grandparent's channel list */
901 err = pcm_chn_remove(d, c);
904 /* destroy ourselves */
906 err = pcm_chn_destroy(c);
912 vchan_initsys(device_t dev)
915 struct snddev_info *d;
917 d = device_get_softc(dev);
918 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
919 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
920 OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
921 sysctl_hw_snd_vchans, "I", "total allocated virtual channel");
922 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
923 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
924 OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
925 sysctl_hw_snd_vchanrate, "I", "virtual channel mixing speed/rate");
926 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
927 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
928 OID_AUTO, "vchanformat", CTLTYPE_STRING | CTLFLAG_RW, d, sizeof(d),
929 sysctl_hw_snd_vchanformat, "A", "virtual channel format");