1 /*- 2 * Copyright (c) 2001 Cameron Grant <cg@freebsd.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 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. 13 * 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 24 * SUCH DAMAGE. 25 * 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 $ 28 */ 29 30 #include <dev/sound/pcm/sound.h> 31 #include <dev/sound/pcm/vchan.h> 32 #include "feeder_if.h" 33 34 SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/vchan.c,v 1.8 2008/01/06 16:55:51 swildner Exp $"); 35 36 /* 37 * Default speed 38 */ 39 #define VCHAN_DEFAULT_SPEED 48000 40 41 extern int feeder_rate_ratemin; 42 extern int feeder_rate_ratemax; 43 44 struct vchinfo { 45 u_int32_t spd, fmt, blksz, bps, run; 46 struct pcm_channel *channel, *parent; 47 struct pcmchan_caps caps; 48 }; 49 50 static u_int32_t vchan_fmt[] = { 51 AFMT_STEREO | AFMT_S16_LE, 52 0 53 }; 54 55 /* 56 * These values are: (1.022**v-1)*65536/(1.022**100-1) 57 * 58 * Inspired by inverting opensound (BSDL'd)'s mix_cvt. 59 */ 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 70 }; 71 72 static int 73 vchan_mix_s16(int16_t *to, int16_t *tmp, unsigned int count, int volume) 74 { 75 /* 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)) 79 */ 80 int i; 81 int x; 82 int scale[2]; 83 int doscale; 84 85 scale[0] = log_mix_cvt[volume & 0xff]; 86 scale[1] = log_mix_cvt[(volume >> 8) & 0xff]; 87 doscale = volume != (100 | (100 << 8)); 88 89 for(i = 0; i < count; i++) { 90 x = to[i]; 91 if (doscale) 92 x += ((int)tmp[i] * scale[i % 2]) >> 16; 93 else 94 x += tmp[i]; 95 if (x < -32768) { 96 /* kprintf("%d + %d = %d (u)\n", to[i], tmp[i], x); */ 97 x = -32768; 98 } 99 if (x > 32767) { 100 /* kprintf("%d + %d = %d (o)\n", to[i], tmp[i], x); */ 101 x = 32767; 102 } 103 to[i] = x & 0x0000ffff; 104 } 105 return 0; 106 } 107 108 static int 109 feed_vchan_s16(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source) 110 { 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; 115 uint32_t sz; 116 int16_t *tmp, *dst; 117 unsigned int cnt, rcnt = 0; 118 119 #if 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); 123 #endif 124 sz = sndbuf_getsize(src); 125 if (sz < count) 126 count = sz; 127 count &= ~1; 128 if (count < 2) 129 return 0; 130 bzero(b, count); 131 132 /* 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 137 */ 138 dst = (int16_t *)b; 139 tmp = (int16_t *)sndbuf_getbuf(src); 140 bzero(tmp, count); 141 SLIST_FOREACH(cce, &c->children, link) { 142 ch = cce->channel; 143 CHN_LOCK(ch); 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); 149 if (cnt > rcnt) 150 rcnt = cnt; 151 } 152 CHN_UNLOCK(ch); 153 } 154 155 return rcnt & ~1; 156 } 157 158 static struct pcm_feederdesc feeder_vchan_s16_desc[] = { 159 {FEEDER_MIXER, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, 160 {0}, 161 }; 162 static kobj_method_t feeder_vchan_s16_methods[] = { 163 KOBJMETHOD(feeder_feed, feed_vchan_s16), 164 { 0, 0 } 165 }; 166 FEEDER_DECLARE(feeder_vchan_s16, 2, NULL); 167 168 /************************************************************/ 169 170 static void * 171 vchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) 172 { 173 struct vchinfo *ch; 174 struct pcm_channel *parent = devinfo; 175 176 KASSERT(dir == PCMDIR_PLAY, ("vchan_init: bad direction")); 177 ch = kmalloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 178 ch->parent = parent; 179 ch->channel = c; 180 ch->fmt = AFMT_U8; 181 ch->spd = DSP_DEFAULT_SPEED; 182 ch->blksz = 2048; 183 184 c->flags |= CHN_F_VIRTUAL; 185 186 return ch; 187 } 188 189 static int 190 vchan_free(kobj_t obj, void *data) 191 { 192 kfree(data, M_DEVBUF); 193 return 0; 194 } 195 196 static int 197 vchan_setformat(kobj_t obj, void *data, u_int32_t format) 198 { 199 struct vchinfo *ch = data; 200 struct pcm_channel *parent = ch->parent; 201 struct pcm_channel *channel = ch->channel; 202 203 ch->fmt = format; 204 ch->bps = 1; 205 ch->bps <<= (ch->fmt & AFMT_STEREO)? 1 : 0; 206 if (ch->fmt & AFMT_16BIT) 207 ch->bps <<= 1; 208 else if (ch->fmt & AFMT_24BIT) 209 ch->bps *= 3; 210 else if (ch->fmt & AFMT_32BIT) 211 ch->bps <<= 2; 212 CHN_UNLOCK(channel); 213 chn_notify(parent, CHN_N_FORMAT); 214 CHN_LOCK(channel); 215 sndbuf_setfmt(channel->bufsoft, format); 216 return 0; 217 } 218 219 static int 220 vchan_setspeed(kobj_t obj, void *data, u_int32_t speed) 221 { 222 struct vchinfo *ch = data; 223 struct pcm_channel *parent = ch->parent; 224 struct pcm_channel *channel = ch->channel; 225 226 ch->spd = speed; 227 CHN_UNLOCK(channel); 228 CHN_LOCK(parent); 229 speed = sndbuf_getspd(parent->bufsoft); 230 CHN_UNLOCK(parent); 231 CHN_LOCK(channel); 232 return speed; 233 } 234 235 static int 236 vchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) 237 { 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; */ 242 int prate, crate; 243 244 ch->blksz = blocksize; 245 /* CHN_UNLOCK(channel); */ 246 sndbuf_setblksz(channel->bufhard, blocksize); 247 chn_notify(parent, CHN_N_BLOCKSIZE); 248 CHN_LOCK(parent); 249 /* CHN_LOCK(channel); */ 250 251 crate = ch->spd * ch->bps; 252 prate = sndbuf_getspd(parent->bufsoft) * sndbuf_getbps(parent->bufsoft); 253 blocksize = sndbuf_getblksz(parent->bufsoft); 254 CHN_UNLOCK(parent); 255 blocksize *= prate; 256 blocksize /= crate; 257 258 return blocksize; 259 } 260 261 static int 262 vchan_trigger(kobj_t obj, void *data, int go) 263 { 264 struct vchinfo *ch = data; 265 struct pcm_channel *parent = ch->parent; 266 struct pcm_channel *channel = ch->channel; 267 268 if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) 269 return 0; 270 271 ch->run = (go == PCMTRIG_START)? 1 : 0; 272 CHN_UNLOCK(channel); 273 chn_notify(parent, CHN_N_TRIGGER); 274 CHN_LOCK(channel); 275 276 return 0; 277 } 278 279 static struct pcmchan_caps * 280 vchan_getcaps(kobj_t obj, void *data) 281 { 282 struct vchinfo *ch = data; 283 284 ch->caps.minspeed = sndbuf_getspd(ch->parent->bufsoft); 285 ch->caps.maxspeed = ch->caps.minspeed; 286 ch->caps.fmtlist = vchan_fmt; 287 ch->caps.caps = 0; 288 289 return &ch->caps; 290 } 291 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), 300 { 0, 0 } 301 }; 302 CHANNEL_DECLARE(vchan); 303 304 #if 0 305 /* 306 * On the fly vchan rate settings 307 */ 308 #ifdef SND_DYNSYSCTL 309 static int 310 sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS) 311 { 312 struct snddev_info *d; 313 struct snddev_channel *sce; 314 struct pcm_channel *c, *ch = NULL, *fake; 315 struct pcmchan_caps *caps; 316 int err = 0; 317 int newspd = 0; 318 319 d = oidp->oid_arg1; 320 if (!(d->flags & SD_F_AUTOVCHAN) || d->vchancount < 1) 321 return EINVAL; 322 if (pcm_inprog(d, 1) != 1 && req->newptr != NULL) { 323 pcm_inprog(d, -1); 324 return EINPROGRESS; 325 } 326 SLIST_FOREACH(sce, &d->channels, link) { 327 c = sce->channel; 328 CHN_LOCK(c); 329 if (c->direction == PCMDIR_PLAY) { 330 if (c->flags & CHN_F_VIRTUAL) { 331 /* Sanity check */ 332 if (ch != NULL && ch != c->parentchannel) { 333 CHN_UNLOCK(c); 334 pcm_inprog(d, -1); 335 return EINVAL; 336 } 337 if (req->newptr != NULL && 338 (c->flags & CHN_F_BUSY)) { 339 CHN_UNLOCK(c); 340 pcm_inprog(d, -1); 341 return EBUSY; 342 } 343 } else if (c->flags & CHN_F_HAS_VCHAN) { 344 /* No way!! */ 345 if (ch != NULL) { 346 CHN_UNLOCK(c); 347 pcm_inprog(d, -1); 348 return EINVAL; 349 } 350 ch = c; 351 newspd = ch->speed; 352 } 353 } 354 CHN_UNLOCK(c); 355 } 356 if (ch == NULL) { 357 pcm_inprog(d, -1); 358 return EINVAL; 359 } 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) { 364 pcm_inprog(d, -1); 365 return EINVAL; 366 } 367 CHN_LOCK(ch); 368 caps = chn_getcaps(ch); 369 if (caps == NULL || newspd < caps->minspeed || 370 newspd > caps->maxspeed) { 371 CHN_UNLOCK(ch); 372 pcm_inprog(d, -1); 373 return EINVAL; 374 } 375 if (newspd != ch->speed) { 376 err = chn_setspeed(ch, newspd); 377 /* 378 * Try to avoid FEEDER_RATE on parent channel if the 379 * requested value is not supported by the hardware. 380 */ 381 if (!err && (ch->feederflags & (1 << FEEDER_RATE))) { 382 newspd = sndbuf_getspd(ch->bufhard); 383 err = chn_setspeed(ch, newspd); 384 } 385 CHN_UNLOCK(ch); 386 if (err == 0) { 387 fake = pcm_getfakechan(d); 388 if (fake != NULL) { 389 CHN_LOCK(fake); 390 fake->speed = newspd; 391 CHN_UNLOCK(fake); 392 } 393 } 394 } else 395 CHN_UNLOCK(ch); 396 } 397 pcm_inprog(d, -1); 398 return err; 399 } 400 #endif 401 #endif 402 403 /* virtual channel interface */ 404 405 int 406 vchan_create(struct pcm_channel *parent) 407 { 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; 413 414 if (!(parent->flags & CHN_F_BUSY)) 415 return EBUSY; 416 417 418 CHN_UNLOCK(parent); 419 420 pce = kmalloc(sizeof(*pce), M_DEVBUF, M_WAITOK | M_ZERO); 421 422 /* create a new playback channel */ 423 child = pcm_chn_create(d, parent, &vchan_class, PCMDIR_VIRTUAL, parent); 424 if (!child) { 425 kfree(pce, M_DEVBUF); 426 CHN_LOCK(parent); 427 return ENODEV; 428 } 429 pce->channel = child; 430 431 /* add us to our grandparent's channel list */ 432 /* 433 * XXX maybe we shouldn't always add the dev_t 434 */ 435 err = pcm_chn_add(d, child); 436 if (err) { 437 pcm_chn_destroy(child); 438 kfree(pce, M_DEVBUF); 439 CHN_LOCK(parent); 440 return err; 441 } 442 443 CHN_LOCK(parent); 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; 448 449 if (first) { 450 parent_caps = chn_getcaps(parent); 451 if (parent_caps == NULL) 452 err = EINVAL; 453 454 if (!err) 455 err = chn_reset(parent, AFMT_STEREO | AFMT_S16_LE); 456 457 if (!err) { 458 fake = pcm_getfakechan(d); 459 if (fake != NULL) { 460 /* 461 * Avoid querying kernel hint, use saved value 462 * from fake channel. 463 */ 464 CHN_UNLOCK(parent); 465 CHN_LOCK(fake); 466 speed = fake->speed; 467 CHN_UNLOCK(fake); 468 CHN_LOCK(parent); 469 } 470 471 /* 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. 476 */ 477 if (speed < 1) { 478 int r; 479 CHN_UNLOCK(parent); 480 r = resource_int_value(device_get_name(parent->dev), 481 device_get_unit(parent->dev), 482 "vchanrate", &speed); 483 CHN_LOCK(parent); 484 if (r != 0) { 485 /* 486 * Workaround for sb16 running 487 * poorly at 45k / 49k. 488 */ 489 switch (parent_caps->maxspeed) { 490 case 45000: 491 case 49000: 492 speed = 44100; 493 break; 494 default: 495 speed = VCHAN_DEFAULT_SPEED; 496 break; 497 } 498 } 499 } 500 501 /* 502 * Limit speed based on driver caps. 503 * This is supposed to help fixed rate, non-VRA 504 * AC97 cards, but.. (see below) 505 */ 506 if (speed < parent_caps->minspeed) 507 speed = parent_caps->minspeed; 508 if (speed > parent_caps->maxspeed) 509 speed = parent_caps->maxspeed; 510 511 /* 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 515 * miserably. 516 */ 517 if (speed < feeder_rate_ratemin) 518 speed = feeder_rate_ratemin; 519 if (speed > feeder_rate_ratemax) 520 speed = feeder_rate_ratemax; 521 522 err = chn_setspeed(parent, speed); 523 /* 524 * Try to avoid FEEDER_RATE on parent channel if the 525 * requested value is not supported by the hardware. 526 */ 527 if (!err && (parent->feederflags & (1 << FEEDER_RATE))) { 528 speed = sndbuf_getspd(parent->bufhard); 529 err = chn_setspeed(parent, speed); 530 } 531 532 if (!err && fake != NULL) { 533 /* 534 * Save new value to fake channel. 535 */ 536 CHN_UNLOCK(parent); 537 CHN_LOCK(fake); 538 fake->speed = speed; 539 CHN_UNLOCK(fake); 540 CHN_LOCK(parent); 541 } 542 } 543 544 if (err) { 545 SLIST_REMOVE(&parent->children, pce, pcmchan_children, link); 546 parent->flags &= ~CHN_F_HAS_VCHAN; 547 CHN_UNLOCK(parent); 548 kfree(pce, M_DEVBUF); 549 if (pcm_chn_remove(d, child) == 0) 550 pcm_chn_destroy(child); 551 CHN_LOCK(parent); 552 return err; 553 } 554 } 555 556 return 0; 557 } 558 559 int 560 vchan_destroy(struct pcm_channel *c) 561 { 562 struct pcm_channel *parent = c->parentchannel; 563 struct snddev_info *d = parent->parentsnddev; 564 struct pcmchan_children *pce; 565 struct snddev_channel *sce; 566 uint32_t spd; 567 int err; 568 569 CHN_LOCK(parent); 570 if (!(parent->flags & CHN_F_BUSY)) { 571 CHN_UNLOCK(parent); 572 return EBUSY; 573 } 574 if (SLIST_EMPTY(&parent->children)) { 575 CHN_UNLOCK(parent); 576 return EINVAL; 577 } 578 579 /* remove us from our parent's children list */ 580 SLIST_FOREACH(pce, &parent->children, link) { 581 if (pce->channel == c) 582 goto gotch; 583 } 584 CHN_UNLOCK(parent); 585 return EINVAL; 586 gotch: 587 SLIST_FOREACH(sce, &d->channels, link) { 588 if (sce->channel == c) { 589 if (sce->dsp_dev) { 590 destroy_dev(sce->dsp_dev); 591 sce->dsp_dev = NULL; 592 } 593 d->devcount--; 594 break; 595 } 596 } 597 SLIST_REMOVE(&parent->children, pce, pcmchan_children, link); 598 kfree(pce, M_DEVBUF); 599 600 if (SLIST_EMPTY(&parent->children)) { 601 parent->flags &= ~(CHN_F_BUSY | CHN_F_HAS_VCHAN); 602 spd = parent->speed; 603 if (chn_reset(parent, parent->format) == 0) 604 chn_setspeed(parent, spd); 605 } 606 607 /* remove us from our grandparent's channel list */ 608 err = pcm_chn_remove(d, c); 609 610 CHN_UNLOCK(parent); 611 /* destroy ourselves */ 612 if (!err) 613 err = pcm_chn_destroy(c); 614 615 return err; 616 } 617 618 int 619 vchan_initsys(device_t dev) 620 { 621 #ifdef SND_DYNSYSCTL 622 struct snddev_info *d; 623 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", ""); 628 #if 0 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", ""); 632 #endif 633 #endif 634 635 return 0; 636 } 637