Allow vchans to have their own volume control.
[dragonfly.git] / sys / dev / sound / pcm / vchan.c
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.4 2006/04/04 17:43:49 ariff Exp $
27  * $DragonFly: src/sys/dev/sound/pcm/vchan.c,v 1.6 2007/06/14 21:48:36 corecode 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.6 2007/06/14 21:48:36 corecode 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 static int
56 vchan_mix_s16(int16_t *to, int16_t *tmp, unsigned int count, int volume)
57 {
58         /*
59          * to is the output buffer, tmp is the input buffer
60          * count is the number of 16bit samples to mix
61          * volume is in range 0-100
62          */
63         int i;
64         int x;
65         int scale;
66         int doscale;
67
68         scale = (volume << 16) / 100;
69         doscale = volume != 100;
70
71         for(i = 0; i < count; i++) {
72                 x = to[i];
73                 if (doscale)
74                         x += ((int)tmp[i] * scale) >> 16;
75                 else
76                         x += tmp[i];
77                 if (x < -32768) {
78                         /* kprintf("%d + %d = %d (u)\n", to[i], tmp[i], x); */
79                         x = -32768;
80                 }
81                 if (x > 32767) {
82                         /* kprintf("%d + %d = %d (o)\n", to[i], tmp[i], x); */
83                         x = 32767;
84                 }
85                 to[i] = x & 0x0000ffff;
86         }
87         return 0;
88 }
89
90 static int
91 feed_vchan_s16(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source)
92 {
93         /* we're going to abuse things a bit */
94         struct snd_dbuf *src = source;
95         struct pcmchan_children *cce;
96         struct pcm_channel *ch;
97         uint32_t sz;
98         int16_t *tmp, *dst;
99         unsigned int cnt, rcnt = 0;
100         int volume;
101
102         #if 0
103         if (sndbuf_getsize(src) < count)
104                 panic("feed_vchan_s16(%s): tmp buffer size %d < count %d, flags = 0x%x",
105                     c->name, sndbuf_getsize(src), count, c->flags);
106         #endif
107         sz = sndbuf_getsize(src);
108         if (sz < count)
109                 count = sz;
110         count &= ~1;
111         if (count < 2)
112                 return 0;
113         bzero(b, count);
114
115         /*
116          * we are going to use our source as a temporary buffer since it's
117          * got no other purpose.  we obtain our data by traversing the channel
118          * list of children and calling vchan_mix_* to mix count bytes from each
119          * into our destination buffer, b
120          */
121         dst = (int16_t *)b;
122         tmp = (int16_t *)sndbuf_getbuf(src);
123         bzero(tmp, count);
124         SLIST_FOREACH(cce, &c->children, link) {
125                 ch = cce->channel;
126                 CHN_LOCK(ch);
127                 if (ch->flags & CHN_F_TRIGGERED) {
128                         if (ch->flags & CHN_F_MAPPED)
129                                 sndbuf_acquire(ch->bufsoft, NULL, sndbuf_getfree(ch->bufsoft));
130                         cnt = FEEDER_FEED(ch->feeder, ch, (u_int8_t *)tmp, count, ch->bufsoft);
131                         volume = ch->volume & 0xff;     /* XXX do special stereo processing? */
132                         volume += (ch->volume >> 8) & 0xff;
133                         volume >>= 1;
134                         vchan_mix_s16(dst, tmp, cnt >> 1, volume);
135                         if (cnt > rcnt)
136                                 rcnt = cnt;
137                 }
138                 CHN_UNLOCK(ch);
139         }
140
141         return rcnt & ~1;
142 }
143
144 static struct pcm_feederdesc feeder_vchan_s16_desc[] = {
145         {FEEDER_MIXER, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0},
146         {0},
147 };
148 static kobj_method_t feeder_vchan_s16_methods[] = {
149         KOBJMETHOD(feeder_feed,         feed_vchan_s16),
150         { 0, 0 }
151 };
152 FEEDER_DECLARE(feeder_vchan_s16, 2, NULL);
153
154 /************************************************************/
155
156 static void *
157 vchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
158 {
159         struct vchinfo *ch;
160         struct pcm_channel *parent = devinfo;
161
162         KASSERT(dir == PCMDIR_PLAY, ("vchan_init: bad direction"));
163         ch = kmalloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
164         if (!ch)
165                 return NULL;
166         ch->parent = parent;
167         ch->channel = c;
168         ch->fmt = AFMT_U8;
169         ch->spd = DSP_DEFAULT_SPEED;
170         ch->blksz = 2048;
171
172         c->flags |= CHN_F_VIRTUAL;
173
174         return ch;
175 }
176
177 static int
178 vchan_free(kobj_t obj, void *data)
179 {
180         return 0;
181 }
182
183 static int
184 vchan_setformat(kobj_t obj, void *data, u_int32_t format)
185 {
186         struct vchinfo *ch = data;
187         struct pcm_channel *parent = ch->parent;
188         struct pcm_channel *channel = ch->channel;
189
190         ch->fmt = format;
191         ch->bps = 1;
192         ch->bps <<= (ch->fmt & AFMT_STEREO)? 1 : 0;
193         if (ch->fmt & AFMT_16BIT)
194                 ch->bps <<= 1;
195         else if (ch->fmt & AFMT_24BIT)
196                 ch->bps *= 3;
197         else if (ch->fmt & AFMT_32BIT)
198                 ch->bps <<= 2;
199         CHN_UNLOCK(channel);
200         chn_notify(parent, CHN_N_FORMAT);
201         CHN_LOCK(channel);
202         sndbuf_setfmt(channel->bufsoft, format);
203         return 0;
204 }
205
206 static int
207 vchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
208 {
209         struct vchinfo *ch = data;
210         struct pcm_channel *parent = ch->parent;
211         struct pcm_channel *channel = ch->channel;
212
213         ch->spd = speed;
214         CHN_UNLOCK(channel);
215         CHN_LOCK(parent);
216         speed = sndbuf_getspd(parent->bufsoft);
217         CHN_UNLOCK(parent);
218         CHN_LOCK(channel);
219         return speed;
220 }
221
222 static int
223 vchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
224 {
225         struct vchinfo *ch = data;
226         struct pcm_channel *channel = ch->channel;
227         struct pcm_channel *parent = ch->parent;
228         /* struct pcm_channel *channel = ch->channel; */
229         int prate, crate;
230
231         ch->blksz = blocksize;
232         /* CHN_UNLOCK(channel); */
233         sndbuf_setblksz(channel->bufhard, blocksize);
234         chn_notify(parent, CHN_N_BLOCKSIZE);
235         CHN_LOCK(parent);
236         /* CHN_LOCK(channel); */
237
238         crate = ch->spd * ch->bps;
239         prate = sndbuf_getspd(parent->bufsoft) * sndbuf_getbps(parent->bufsoft);
240         blocksize = sndbuf_getblksz(parent->bufsoft);
241         CHN_UNLOCK(parent);
242         blocksize *= prate;
243         blocksize /= crate;
244
245         return blocksize;
246 }
247
248 static int
249 vchan_trigger(kobj_t obj, void *data, int go)
250 {
251         struct vchinfo *ch = data;
252         struct pcm_channel *parent = ch->parent;
253         struct pcm_channel *channel = ch->channel;
254
255         if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD)
256                 return 0;
257
258         ch->run = (go == PCMTRIG_START)? 1 : 0;
259         CHN_UNLOCK(channel);
260         chn_notify(parent, CHN_N_TRIGGER);
261         CHN_LOCK(channel);
262
263         return 0;
264 }
265
266 static struct pcmchan_caps *
267 vchan_getcaps(kobj_t obj, void *data)
268 {
269         struct vchinfo *ch = data;
270
271         ch->caps.minspeed = sndbuf_getspd(ch->parent->bufsoft);
272         ch->caps.maxspeed = ch->caps.minspeed;
273         ch->caps.fmtlist = vchan_fmt;
274         ch->caps.caps = 0;
275
276         return &ch->caps;
277 }
278
279 static kobj_method_t vchan_methods[] = {
280         KOBJMETHOD(channel_init,                vchan_init),
281         KOBJMETHOD(channel_free,                vchan_free),
282         KOBJMETHOD(channel_setformat,           vchan_setformat),
283         KOBJMETHOD(channel_setspeed,            vchan_setspeed),
284         KOBJMETHOD(channel_setblocksize,        vchan_setblocksize),
285         KOBJMETHOD(channel_trigger,             vchan_trigger),
286         KOBJMETHOD(channel_getcaps,             vchan_getcaps),
287         { 0, 0 }
288 };
289 CHANNEL_DECLARE(vchan);
290
291 #if 0
292 /* 
293  * On the fly vchan rate settings
294  */
295 #ifdef SND_DYNSYSCTL
296 static int
297 sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS)
298 {
299         struct snddev_info *d;
300         struct snddev_channel *sce;
301         struct pcm_channel *c, *ch = NULL, *fake;
302         struct pcmchan_caps *caps;
303         int err = 0;
304         int newspd = 0;
305
306         d = oidp->oid_arg1;
307         if (!(d->flags & SD_F_AUTOVCHAN) || d->vchancount < 1)
308                 return EINVAL;
309         if (pcm_inprog(d, 1) != 1 && req->newptr != NULL) {
310                 pcm_inprog(d, -1);
311                 return EINPROGRESS;
312         }
313         SLIST_FOREACH(sce, &d->channels, link) {
314                 c = sce->channel;
315                 CHN_LOCK(c);
316                 if (c->direction == PCMDIR_PLAY) {
317                         if (c->flags & CHN_F_VIRTUAL) {
318                                 /* Sanity check */
319                                 if (ch != NULL && ch != c->parentchannel) {
320                                         CHN_UNLOCK(c);
321                                         pcm_inprog(d, -1);
322                                         return EINVAL;
323                                 }
324                                 if (req->newptr != NULL &&
325                                                 (c->flags & CHN_F_BUSY)) {
326                                         CHN_UNLOCK(c);
327                                         pcm_inprog(d, -1);
328                                         return EBUSY;
329                                 }
330                         } else if (c->flags & CHN_F_HAS_VCHAN) {
331                                 /* No way!! */
332                                 if (ch != NULL) {
333                                         CHN_UNLOCK(c);
334                                         pcm_inprog(d, -1);
335                                         return EINVAL;
336                                 }
337                                 ch = c;
338                                 newspd = ch->speed;
339                         }
340                 }
341                 CHN_UNLOCK(c);
342         }
343         if (ch == NULL) {
344                 pcm_inprog(d, -1);
345                 return EINVAL;
346         }
347         err = sysctl_handle_int(oidp, &newspd, sizeof(newspd), req);
348         if (err == 0 && req->newptr != NULL) {
349                 if (newspd < 1 || newspd < feeder_rate_ratemin ||
350                                 newspd > feeder_rate_ratemax) {
351                         pcm_inprog(d, -1);
352                         return EINVAL;
353                 }
354                 CHN_LOCK(ch);
355                 caps = chn_getcaps(ch);
356                 if (caps == NULL || newspd < caps->minspeed ||
357                                 newspd > caps->maxspeed) {
358                         CHN_UNLOCK(ch);
359                         pcm_inprog(d, -1);
360                         return EINVAL;
361                 }
362                 if (newspd != ch->speed) {
363                         err = chn_setspeed(ch, newspd);
364                         /*
365                          * Try to avoid FEEDER_RATE on parent channel if the
366                          * requested value is not supported by the hardware.
367                          */
368                         if (!err && (ch->feederflags & (1 << FEEDER_RATE))) {
369                                 newspd = sndbuf_getspd(ch->bufhard);
370                                 err = chn_setspeed(ch, newspd);
371                         }
372                         CHN_UNLOCK(ch);
373                         if (err == 0) {
374                                 fake = pcm_getfakechan(d);
375                                 if (fake != NULL) {
376                                         CHN_LOCK(fake);
377                                         fake->speed = newspd;
378                                         CHN_UNLOCK(fake);
379                                 }
380                         }
381                 } else
382                         CHN_UNLOCK(ch);
383         }
384         pcm_inprog(d, -1);
385         return err;
386 }
387 #endif
388 #endif
389
390 /* virtual channel interface */
391
392 int
393 vchan_create(struct pcm_channel *parent)
394 {
395         struct snddev_info *d = parent->parentsnddev;
396         struct pcmchan_children *pce;
397         struct pcm_channel *child, *fake;
398         struct pcmchan_caps *parent_caps;
399         int err, first, speed = 0;
400
401         if (!(parent->flags & CHN_F_BUSY))
402                 return EBUSY;
403
404
405         CHN_UNLOCK(parent);
406
407         pce = kmalloc(sizeof(*pce), M_DEVBUF, M_WAITOK | M_ZERO);
408         if (!pce) {
409                 CHN_LOCK(parent);
410                 return ENOMEM;
411         }
412
413         /* create a new playback channel */
414         child = pcm_chn_create(d, parent, &vchan_class, PCMDIR_VIRTUAL, parent);
415         if (!child) {
416                 kfree(pce, M_DEVBUF);
417                 CHN_LOCK(parent);
418                 return ENODEV;
419         }
420         pce->channel = child;
421
422         /* add us to our grandparent's channel list */
423         /*
424          * XXX maybe we shouldn't always add the dev_t
425          */
426         err = pcm_chn_add(d, child);
427         if (err) {
428                 pcm_chn_destroy(child);
429                 kfree(pce, M_DEVBUF);
430                 CHN_LOCK(parent);
431                 return err;
432         }
433
434         CHN_LOCK(parent);
435         /* add us to our parent channel's children */
436         first = SLIST_EMPTY(&parent->children);
437         SLIST_INSERT_HEAD(&parent->children, pce, link);
438         parent->flags |= CHN_F_HAS_VCHAN;
439
440         if (first) {
441                 parent_caps = chn_getcaps(parent);
442                 if (parent_caps == NULL)
443                         err = EINVAL;
444
445                 if (!err)
446                         err = chn_reset(parent, AFMT_STEREO | AFMT_S16_LE);
447
448                 if (!err) {
449                         fake = pcm_getfakechan(d);
450                         if (fake != NULL) {
451                                 /*
452                                  * Avoid querying kernel hint, use saved value
453                                  * from fake channel.
454                                  */
455                                 CHN_UNLOCK(parent);
456                                 CHN_LOCK(fake);
457                                 speed = fake->speed;
458                                 CHN_UNLOCK(fake);
459                                 CHN_LOCK(parent);
460                         }
461
462                         /*
463                          * This is very sad. Few soundcards advertised as being
464                          * able to do (insanely) higher/lower speed, but in
465                          * reality, they simply can't. At least, we give user chance
466                          * to set sane value via kernel hints or sysctl.
467                          */
468                         if (speed < 1) {
469                                 int r;
470                                 CHN_UNLOCK(parent);
471                                 r = resource_int_value(device_get_name(parent->dev),
472                                                         device_get_unit(parent->dev),
473                                                                 "vchanrate", &speed);
474                                 CHN_LOCK(parent);
475                                 if (r != 0) {
476                                         /*
477                                          * Workaround for sb16 running
478                                          * poorly at 45k / 49k.
479                                          */
480                                         switch (parent_caps->maxspeed) {
481                                         case 45000:
482                                         case 49000:
483                                                 speed = 44100;
484                                                 break;
485                                         default:
486                                                 speed = VCHAN_DEFAULT_SPEED;
487                                                 break;
488                                         }
489                                 }
490                         }
491
492                         /*
493                          * Limit speed based on driver caps.
494                          * This is supposed to help fixed rate, non-VRA
495                          * AC97 cards, but.. (see below)
496                          */
497                         if (speed < parent_caps->minspeed)
498                                 speed = parent_caps->minspeed;
499                         if (speed > parent_caps->maxspeed)
500                                 speed = parent_caps->maxspeed;
501
502                         /*
503                          * We still need to limit the speed between
504                          * feeder_rate_ratemin <-> feeder_rate_ratemax. This is
505                          * just an escape goat if all of the above failed
506                          * miserably.
507                          */
508                         if (speed < feeder_rate_ratemin)
509                                 speed = feeder_rate_ratemin;
510                         if (speed > feeder_rate_ratemax)
511                                 speed = feeder_rate_ratemax;
512
513                         err = chn_setspeed(parent, speed);
514                         /*
515                          * Try to avoid FEEDER_RATE on parent channel if the
516                          * requested value is not supported by the hardware.
517                          */
518                         if (!err && (parent->feederflags & (1 << FEEDER_RATE))) {
519                                 speed = sndbuf_getspd(parent->bufhard);
520                                 err = chn_setspeed(parent, speed);
521                         }
522
523                         if (!err && fake != NULL) {
524                                 /*
525                                  * Save new value to fake channel.
526                                  */
527                                 CHN_UNLOCK(parent);
528                                 CHN_LOCK(fake);
529                                 fake->speed = speed;
530                                 CHN_UNLOCK(fake);
531                                 CHN_LOCK(parent);
532                         }
533                 }
534                 
535                 if (err) {
536                         SLIST_REMOVE(&parent->children, pce, pcmchan_children, link);
537                         parent->flags &= ~CHN_F_HAS_VCHAN;
538                         CHN_UNLOCK(parent);
539                         kfree(pce, M_DEVBUF);
540                         if (pcm_chn_remove(d, child) == 0)
541                                 pcm_chn_destroy(child);
542                         CHN_LOCK(parent);
543                         return err;
544                 }
545         }
546
547         return 0;
548 }
549
550 int
551 vchan_destroy(struct pcm_channel *c)
552 {
553         struct pcm_channel *parent = c->parentchannel;
554         struct snddev_info *d = parent->parentsnddev;
555         struct pcmchan_children *pce;
556         struct snddev_channel *sce;
557         uint32_t spd;
558         int err;
559
560         CHN_LOCK(parent);
561         if (!(parent->flags & CHN_F_BUSY)) {
562                 CHN_UNLOCK(parent);
563                 return EBUSY;
564         }
565         if (SLIST_EMPTY(&parent->children)) {
566                 CHN_UNLOCK(parent);
567                 return EINVAL;
568         }
569
570         /* remove us from our parent's children list */
571         SLIST_FOREACH(pce, &parent->children, link) {
572                 if (pce->channel == c)
573                         goto gotch;
574         }
575         CHN_UNLOCK(parent);
576         return EINVAL;
577 gotch:
578         SLIST_FOREACH(sce, &d->channels, link) {
579                 if (sce->channel == c) {
580                         if (sce->dsp_devt) {
581                                 destroy_dev(sce->dsp_devt);
582                                 sce->dsp_devt = NULL;
583                         }
584                         if (sce->dspW_devt) {
585                                 destroy_dev(sce->dspW_devt);
586                                 sce->dspW_devt = NULL;
587                         }
588                         if (sce->audio_devt) {
589                                 destroy_dev(sce->audio_devt);
590                                 sce->audio_devt = NULL;
591                         }
592                         if (sce->dspr_devt) {
593                                 destroy_dev(sce->dspr_devt);
594                                 sce->dspr_devt = NULL;
595                         }
596                         d->devcount--;
597                         break;
598                 }
599         }
600         SLIST_REMOVE(&parent->children, pce, pcmchan_children, link);
601         kfree(pce, M_DEVBUF);
602
603         if (SLIST_EMPTY(&parent->children)) {
604                 parent->flags &= ~(CHN_F_BUSY | CHN_F_HAS_VCHAN);
605                 spd = parent->speed;
606                 if (chn_reset(parent, parent->format) == 0)
607                         chn_setspeed(parent, spd);
608         }
609
610         /* remove us from our grandparent's channel list */
611         err = pcm_chn_remove(d, c);
612
613         CHN_UNLOCK(parent);
614         /* destroy ourselves */
615         if (!err)
616                 err = pcm_chn_destroy(c);
617
618         return err;
619 }
620
621 int
622 vchan_initsys(device_t dev)
623 {
624 #ifdef SND_DYNSYSCTL
625         struct snddev_info *d;
626
627         d = device_get_softc(dev);
628         SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
629             OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
630             sysctl_hw_snd_vchans, "I", "");
631 #if 0
632         SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
633             OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
634             sysctl_hw_snd_vchanrate, "I", "");
635 #endif
636 #endif
637
638         return 0;
639 }