2 * Copyright (c) 2001 Cameron Grant <cg@freebsd.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * $FreeBSD: src/sys/dev/sound/pcm/vchan.c,v 1.17.2.5 2007/02/04 06:17:14 ariff Exp $
27 * $DragonFly: src/sys/dev/sound/pcm/vchan.c,v 1.8 2008/01/06 16:55:51 swildner Exp $
30 #include <dev/sound/pcm/sound.h>
31 #include <dev/sound/pcm/vchan.h>
32 #include "feeder_if.h"
34 SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/vchan.c,v 1.8 2008/01/06 16:55:51 swildner Exp $");
39 #define VCHAN_DEFAULT_SPEED 48000
41 extern int feeder_rate_ratemin
;
42 extern int feeder_rate_ratemax
;
45 u_int32_t spd
, fmt
, blksz
, bps
, run
;
46 struct pcm_channel
*channel
, *parent
;
47 struct pcmchan_caps caps
;
50 static u_int32_t vchan_fmt
[] = {
51 AFMT_STEREO
| AFMT_S16_LE
,
56 * These values are: (1.022**v-1)*65536/(1.022**100-1)
58 * Inspired by inverting opensound (BSDL'd)'s mix_cvt.
60 static int log_mix_cvt
[101] = {
61 0, 184, 373, 565, 762, 964, 1170, 1380, 1595, 1814, 2039, 2268, 2503, 2742,
62 2987, 3238, 3493, 3755, 4022, 4295, 4574, 4859, 5151, 5449, 5753, 6064, 6382,
63 6707, 7039, 7379, 7726, 8080, 8442, 8813, 9191, 9578, 9973, 10377, 10790,
64 11212, 11643, 12084, 12534, 12995, 13465, 13946, 14437, 14940, 15453, 15977,
65 16513, 17061, 17621, 18193, 18778, 19376, 19987, 20611, 21249, 21901, 22567,
66 23248, 23944, 24656, 25383, 26126, 26885, 27661, 28454, 29265, 30093, 30940,
67 31805, 32689, 33593, 34517, 35461, 36425, 37411, 38419, 39449, 40501, 41577,
68 42676, 43799, 44947, 46121, 47320, 48546, 49798, 51078, 52387, 53724, 55090,
69 56487, 57914, 59373, 60864, 62387, 63944, 65536
73 vchan_mix_s16(int16_t *to
, int16_t *tmp
, unsigned int count
, int volume
)
76 * to is the output buffer, tmp is the input buffer
77 * count is the number of 16bit samples to mix
78 * volume is in range 0-100, (left || (right << 8))
85 scale
[0] = log_mix_cvt
[volume
& 0xff];
86 scale
[1] = log_mix_cvt
[(volume
>> 8) & 0xff];
87 doscale
= volume
!= (100 || (100 << 8));
89 for(i
= 0; i
< count
; i
++) {
92 x
+= ((int)tmp
[i
] * scale
[i
% 2]) >> 16;
96 /* kprintf("%d + %d = %d (u)\n", to[i], tmp[i], x); */
100 /* kprintf("%d + %d = %d (o)\n", to[i], tmp[i], x); */
103 to
[i
] = x
& 0x0000ffff;
109 feed_vchan_s16(struct pcm_feeder
*f
, struct pcm_channel
*c
, u_int8_t
*b
, u_int32_t count
, void *source
)
111 /* we're going to abuse things a bit */
112 struct snd_dbuf
*src
= source
;
113 struct pcmchan_children
*cce
;
114 struct pcm_channel
*ch
;
117 unsigned int cnt
, rcnt
= 0;
120 if (sndbuf_getsize(src
) < count
)
121 panic("feed_vchan_s16(%s): tmp buffer size %d < count %d, flags = 0x%x",
122 c
->name
, sndbuf_getsize(src
), count
, c
->flags
);
124 sz
= sndbuf_getsize(src
);
133 * we are going to use our source as a temporary buffer since it's
134 * got no other purpose. we obtain our data by traversing the channel
135 * list of children and calling vchan_mix_* to mix count bytes from each
136 * into our destination buffer, b
139 tmp
= (int16_t *)sndbuf_getbuf(src
);
141 SLIST_FOREACH(cce
, &c
->children
, link
) {
144 if (ch
->flags
& CHN_F_TRIGGERED
) {
145 if (ch
->flags
& CHN_F_MAPPED
)
146 sndbuf_acquire(ch
->bufsoft
, NULL
, sndbuf_getfree(ch
->bufsoft
));
147 cnt
= FEEDER_FEED(ch
->feeder
, ch
, (u_int8_t
*)tmp
, count
, ch
->bufsoft
);
148 vchan_mix_s16(dst
, tmp
, cnt
>> 1, ch
->volume
);
158 static struct pcm_feederdesc feeder_vchan_s16_desc
[] = {
159 {FEEDER_MIXER
, AFMT_S16_LE
| AFMT_STEREO
, AFMT_S16_LE
| AFMT_STEREO
, 0},
162 static kobj_method_t feeder_vchan_s16_methods
[] = {
163 KOBJMETHOD(feeder_feed
, feed_vchan_s16
),
166 FEEDER_DECLARE(feeder_vchan_s16
, 2, NULL
);
168 /************************************************************/
171 vchan_init(kobj_t obj
, void *devinfo
, struct snd_dbuf
*b
, struct pcm_channel
*c
, int dir
)
174 struct pcm_channel
*parent
= devinfo
;
176 KASSERT(dir
== PCMDIR_PLAY
, ("vchan_init: bad direction"));
177 ch
= kmalloc(sizeof(*ch
), M_DEVBUF
, M_WAITOK
| M_ZERO
);
181 ch
->spd
= DSP_DEFAULT_SPEED
;
184 c
->flags
|= CHN_F_VIRTUAL
;
190 vchan_free(kobj_t obj
, void *data
)
192 kfree(data
, M_DEVBUF
);
197 vchan_setformat(kobj_t obj
, void *data
, u_int32_t format
)
199 struct vchinfo
*ch
= data
;
200 struct pcm_channel
*parent
= ch
->parent
;
201 struct pcm_channel
*channel
= ch
->channel
;
205 ch
->bps
<<= (ch
->fmt
& AFMT_STEREO
)? 1 : 0;
206 if (ch
->fmt
& AFMT_16BIT
)
208 else if (ch
->fmt
& AFMT_24BIT
)
210 else if (ch
->fmt
& AFMT_32BIT
)
213 chn_notify(parent
, CHN_N_FORMAT
);
215 sndbuf_setfmt(channel
->bufsoft
, format
);
220 vchan_setspeed(kobj_t obj
, void *data
, u_int32_t speed
)
222 struct vchinfo
*ch
= data
;
223 struct pcm_channel
*parent
= ch
->parent
;
224 struct pcm_channel
*channel
= ch
->channel
;
229 speed
= sndbuf_getspd(parent
->bufsoft
);
236 vchan_setblocksize(kobj_t obj
, void *data
, u_int32_t blocksize
)
238 struct vchinfo
*ch
= data
;
239 struct pcm_channel
*channel
= ch
->channel
;
240 struct pcm_channel
*parent
= ch
->parent
;
241 /* struct pcm_channel *channel = ch->channel; */
244 ch
->blksz
= blocksize
;
245 /* CHN_UNLOCK(channel); */
246 sndbuf_setblksz(channel
->bufhard
, blocksize
);
247 chn_notify(parent
, CHN_N_BLOCKSIZE
);
249 /* CHN_LOCK(channel); */
251 crate
= ch
->spd
* ch
->bps
;
252 prate
= sndbuf_getspd(parent
->bufsoft
) * sndbuf_getbps(parent
->bufsoft
);
253 blocksize
= sndbuf_getblksz(parent
->bufsoft
);
262 vchan_trigger(kobj_t obj
, void *data
, int go
)
264 struct vchinfo
*ch
= data
;
265 struct pcm_channel
*parent
= ch
->parent
;
266 struct pcm_channel
*channel
= ch
->channel
;
268 if (go
== PCMTRIG_EMLDMAWR
|| go
== PCMTRIG_EMLDMARD
)
271 ch
->run
= (go
== PCMTRIG_START
)? 1 : 0;
273 chn_notify(parent
, CHN_N_TRIGGER
);
279 static struct pcmchan_caps
*
280 vchan_getcaps(kobj_t obj
, void *data
)
282 struct vchinfo
*ch
= data
;
284 ch
->caps
.minspeed
= sndbuf_getspd(ch
->parent
->bufsoft
);
285 ch
->caps
.maxspeed
= ch
->caps
.minspeed
;
286 ch
->caps
.fmtlist
= vchan_fmt
;
292 static kobj_method_t vchan_methods
[] = {
293 KOBJMETHOD(channel_init
, vchan_init
),
294 KOBJMETHOD(channel_free
, vchan_free
),
295 KOBJMETHOD(channel_setformat
, vchan_setformat
),
296 KOBJMETHOD(channel_setspeed
, vchan_setspeed
),
297 KOBJMETHOD(channel_setblocksize
, vchan_setblocksize
),
298 KOBJMETHOD(channel_trigger
, vchan_trigger
),
299 KOBJMETHOD(channel_getcaps
, vchan_getcaps
),
302 CHANNEL_DECLARE(vchan
);
306 * On the fly vchan rate settings
310 sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS
)
312 struct snddev_info
*d
;
313 struct snddev_channel
*sce
;
314 struct pcm_channel
*c
, *ch
= NULL
, *fake
;
315 struct pcmchan_caps
*caps
;
320 if (!(d
->flags
& SD_F_AUTOVCHAN
) || d
->vchancount
< 1)
322 if (pcm_inprog(d
, 1) != 1 && req
->newptr
!= NULL
) {
326 SLIST_FOREACH(sce
, &d
->channels
, link
) {
329 if (c
->direction
== PCMDIR_PLAY
) {
330 if (c
->flags
& CHN_F_VIRTUAL
) {
332 if (ch
!= NULL
&& ch
!= c
->parentchannel
) {
337 if (req
->newptr
!= NULL
&&
338 (c
->flags
& CHN_F_BUSY
)) {
343 } else if (c
->flags
& CHN_F_HAS_VCHAN
) {
360 err
= sysctl_handle_int(oidp
, &newspd
, sizeof(newspd
), req
);
361 if (err
== 0 && req
->newptr
!= NULL
) {
362 if (newspd
< 1 || newspd
< feeder_rate_ratemin
||
363 newspd
> feeder_rate_ratemax
) {
368 caps
= chn_getcaps(ch
);
369 if (caps
== NULL
|| newspd
< caps
->minspeed
||
370 newspd
> caps
->maxspeed
) {
375 if (newspd
!= ch
->speed
) {
376 err
= chn_setspeed(ch
, newspd
);
378 * Try to avoid FEEDER_RATE on parent channel if the
379 * requested value is not supported by the hardware.
381 if (!err
&& (ch
->feederflags
& (1 << FEEDER_RATE
))) {
382 newspd
= sndbuf_getspd(ch
->bufhard
);
383 err
= chn_setspeed(ch
, newspd
);
387 fake
= pcm_getfakechan(d
);
390 fake
->speed
= newspd
;
403 /* virtual channel interface */
406 vchan_create(struct pcm_channel
*parent
)
408 struct snddev_info
*d
= parent
->parentsnddev
;
409 struct pcmchan_children
*pce
;
410 struct pcm_channel
*child
, *fake
;
411 struct pcmchan_caps
*parent_caps
;
412 int err
, first
, speed
= 0;
414 if (!(parent
->flags
& CHN_F_BUSY
))
420 pce
= kmalloc(sizeof(*pce
), M_DEVBUF
, M_WAITOK
| M_ZERO
);
422 /* create a new playback channel */
423 child
= pcm_chn_create(d
, parent
, &vchan_class
, PCMDIR_VIRTUAL
, parent
);
425 kfree(pce
, M_DEVBUF
);
429 pce
->channel
= child
;
431 /* add us to our grandparent's channel list */
433 * XXX maybe we shouldn't always add the dev_t
435 err
= pcm_chn_add(d
, child
);
437 pcm_chn_destroy(child
);
438 kfree(pce
, M_DEVBUF
);
444 /* add us to our parent channel's children */
445 first
= SLIST_EMPTY(&parent
->children
);
446 SLIST_INSERT_HEAD(&parent
->children
, pce
, link
);
447 parent
->flags
|= CHN_F_HAS_VCHAN
;
450 parent_caps
= chn_getcaps(parent
);
451 if (parent_caps
== NULL
)
455 err
= chn_reset(parent
, AFMT_STEREO
| AFMT_S16_LE
);
458 fake
= pcm_getfakechan(d
);
461 * Avoid querying kernel hint, use saved value
472 * This is very sad. Few soundcards advertised as being
473 * able to do (insanely) higher/lower speed, but in
474 * reality, they simply can't. At least, we give user chance
475 * to set sane value via kernel hints or sysctl.
480 r
= resource_int_value(device_get_name(parent
->dev
),
481 device_get_unit(parent
->dev
),
482 "vchanrate", &speed
);
486 * Workaround for sb16 running
487 * poorly at 45k / 49k.
489 switch (parent_caps
->maxspeed
) {
495 speed
= VCHAN_DEFAULT_SPEED
;
502 * Limit speed based on driver caps.
503 * This is supposed to help fixed rate, non-VRA
504 * AC97 cards, but.. (see below)
506 if (speed
< parent_caps
->minspeed
)
507 speed
= parent_caps
->minspeed
;
508 if (speed
> parent_caps
->maxspeed
)
509 speed
= parent_caps
->maxspeed
;
512 * We still need to limit the speed between
513 * feeder_rate_ratemin <-> feeder_rate_ratemax. This is
514 * just an escape goat if all of the above failed
517 if (speed
< feeder_rate_ratemin
)
518 speed
= feeder_rate_ratemin
;
519 if (speed
> feeder_rate_ratemax
)
520 speed
= feeder_rate_ratemax
;
522 err
= chn_setspeed(parent
, speed
);
524 * Try to avoid FEEDER_RATE on parent channel if the
525 * requested value is not supported by the hardware.
527 if (!err
&& (parent
->feederflags
& (1 << FEEDER_RATE
))) {
528 speed
= sndbuf_getspd(parent
->bufhard
);
529 err
= chn_setspeed(parent
, speed
);
532 if (!err
&& fake
!= NULL
) {
534 * Save new value to fake channel.
545 SLIST_REMOVE(&parent
->children
, pce
, pcmchan_children
, link
);
546 parent
->flags
&= ~CHN_F_HAS_VCHAN
;
548 kfree(pce
, M_DEVBUF
);
549 if (pcm_chn_remove(d
, child
) == 0)
550 pcm_chn_destroy(child
);
560 vchan_destroy(struct pcm_channel
*c
)
562 struct pcm_channel
*parent
= c
->parentchannel
;
563 struct snddev_info
*d
= parent
->parentsnddev
;
564 struct pcmchan_children
*pce
;
565 struct snddev_channel
*sce
;
570 if (!(parent
->flags
& CHN_F_BUSY
)) {
574 if (SLIST_EMPTY(&parent
->children
)) {
579 /* remove us from our parent's children list */
580 SLIST_FOREACH(pce
, &parent
->children
, link
) {
581 if (pce
->channel
== c
)
587 SLIST_FOREACH(sce
, &d
->channels
, link
) {
588 if (sce
->channel
== c
) {
590 destroy_dev(sce
->dsp_dev
);
597 SLIST_REMOVE(&parent
->children
, pce
, pcmchan_children
, link
);
598 kfree(pce
, M_DEVBUF
);
600 if (SLIST_EMPTY(&parent
->children
)) {
601 parent
->flags
&= ~(CHN_F_BUSY
| CHN_F_HAS_VCHAN
);
603 if (chn_reset(parent
, parent
->format
) == 0)
604 chn_setspeed(parent
, spd
);
607 /* remove us from our grandparent's channel list */
608 err
= pcm_chn_remove(d
, c
);
611 /* destroy ourselves */
613 err
= pcm_chn_destroy(c
);
619 vchan_initsys(device_t dev
)
622 struct snddev_info
*d
;
624 d
= device_get_softc(dev
);
625 SYSCTL_ADD_PROC(snd_sysctl_tree(dev
), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev
)),
626 OID_AUTO
, "vchans", CTLTYPE_INT
| CTLFLAG_RW
, d
, sizeof(d
),
627 sysctl_hw_snd_vchans
, "I", "");
629 SYSCTL_ADD_PROC(snd_sysctl_tree(dev
), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev
)),
630 OID_AUTO
, "vchanrate", CTLTYPE_INT
| CTLFLAG_RW
, d
, sizeof(d
),
631 sysctl_hw_snd_vchanrate
, "I", "");