Merge from vendor branch FILE:
[dragonfly.git] / sys / dev / sound / pcm / channel.c
1 /*
2  * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
3  * Portions Copyright by Luigi Rizzo - 1997-99
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
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.
14  *
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
25  * SUCH DAMAGE.
26  *
27  * $FreeBSD: src/sys/dev/sound/pcm/channel.c,v 1.19.2.19 2003/03/11 15:15:41 orion Exp $
28  * $DragonFly: src/sys/dev/sound/pcm/channel.c,v 1.9 2006/07/28 02:17:38 dillon Exp $
29  */
30
31 #include <dev/sound/pcm/sound.h>
32 #include <sys/vnode.h>
33
34 #include "feeder_if.h"
35
36 SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/channel.c,v 1.9 2006/07/28 02:17:38 dillon Exp $");
37
38 #define MIN_CHUNK_SIZE          256     /* for uiomove etc. */
39 #define DMA_ALIGN_THRESHOLD     4
40 #define DMA_ALIGN_MASK          (~(DMA_ALIGN_THRESHOLD - 1))
41
42 #define CANCHANGE(c) (!(c->flags & CHN_F_TRIGGERED))
43
44 /*
45 #define DEB(x) x
46 */
47
48 static int chn_targetirqrate = 32;
49 TUNABLE_INT("hw.snd.targetirqrate", &chn_targetirqrate);
50
51 static int
52 sysctl_hw_snd_targetirqrate(SYSCTL_HANDLER_ARGS)
53 {
54         int err, val;
55
56         val = chn_targetirqrate;
57         err = sysctl_handle_int(oidp, &val, sizeof(val), req);
58         if (val < 16 || val > 512)
59                 err = EINVAL;
60         else
61                 chn_targetirqrate = val;
62
63         return err;
64 }
65 SYSCTL_PROC(_hw_snd, OID_AUTO, targetirqrate, CTLTYPE_INT | CTLFLAG_RW,
66         0, sizeof(int), sysctl_hw_snd_targetirqrate, "I", "");
67 static int report_soft_formats = 1;
68 SYSCTL_INT(_hw_snd, OID_AUTO, report_soft_formats, CTLFLAG_RW,
69         &report_soft_formats, 1, "report software-emulated formats");
70
71 static int chn_buildfeeder(struct pcm_channel *c);
72
73 static void
74 chn_lockinit(struct pcm_channel *c)
75 {
76         c->lock = snd_mtxcreate(c->name, "pcm channel");
77 }
78
79 static void
80 chn_lockdestroy(struct pcm_channel *c)
81 {
82         snd_mtxfree(c->lock);
83 }
84
85 static int
86 chn_polltrigger(struct pcm_channel *c)
87 {
88         struct snd_dbuf *bs = c->bufsoft;
89         unsigned amt, lim;
90
91         CHN_LOCKASSERT(c);
92         if (c->flags & CHN_F_MAPPED) {
93                 if (sndbuf_getprevblocks(bs) == 0)
94                         return 1;
95                 else
96                         return (sndbuf_getblocks(bs) > sndbuf_getprevblocks(bs))? 1 : 0;
97         } else {
98                 amt = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(bs) : sndbuf_getready(bs);
99                 lim = (c->flags & CHN_F_HAS_SIZE)? sndbuf_getblksz(bs) : 1;
100                 lim = 1;
101                 return (amt >= lim)? 1 : 0;
102         }
103         return 0;
104 }
105
106 static int
107 chn_pollreset(struct pcm_channel *c)
108 {
109         struct snd_dbuf *bs = c->bufsoft;
110
111         CHN_LOCKASSERT(c);
112         sndbuf_updateprevtotal(bs);
113         return 1;
114 }
115
116 static void
117 chn_wakeup(struct pcm_channel *c)
118 {
119         struct snd_dbuf *bs = c->bufsoft;
120
121         CHN_LOCKASSERT(c);
122         if (sndbuf_getsel(bs)->si_pid && chn_polltrigger(c))
123                 selwakeup(sndbuf_getsel(bs));
124         wakeup(bs);
125 }
126
127 static int
128 chn_sleep(struct pcm_channel *c, char *str, int timeout)
129 {
130         struct snd_dbuf *bs = c->bufsoft;
131         int ret;
132
133         CHN_LOCKASSERT(c);
134 #ifdef USING_MUTEX
135         ret = msleep(bs, c->lock, PCATCH, str, timeout);
136 #else
137         ret = tsleep(bs, PCATCH, str, timeout);
138 #endif
139
140         return ret;
141 }
142
143 /*
144  * chn_dmaupdate() tracks the status of a dma transfer,
145  * updating pointers. It must be called from a critical section.
146  */
147
148 static unsigned int
149 chn_dmaupdate(struct pcm_channel *c)
150 {
151         struct snd_dbuf *b = c->bufhard;
152         unsigned int delta, old, hwptr, amt;
153
154         KASSERT(sndbuf_getsize(b) > 0, ("bufsize == 0"));
155         CHN_LOCKASSERT(c);
156
157         old = sndbuf_gethwptr(b);
158         hwptr = chn_getptr(c);
159         delta = (sndbuf_getsize(b) + hwptr - old) % sndbuf_getsize(b);
160         sndbuf_sethwptr(b, hwptr);
161
162         DEB(
163         if (delta >= ((sndbuf_getsize(b) * 15) / 16)) {
164                 if (!(c->flags & (CHN_F_CLOSING | CHN_F_ABORTING)))
165                         device_printf(c->dev, "hwptr went backwards %d -> %d\n", old, hwptr);
166         }
167         );
168
169         if (c->direction == PCMDIR_PLAY) {
170                 amt = MIN(delta, sndbuf_getready(b));
171                 if (amt > 0)
172                         sndbuf_dispose(b, NULL, amt);
173         } else {
174                 amt = MIN(delta, sndbuf_getfree(b));
175                 if (amt > 0)
176                        sndbuf_acquire(b, NULL, amt);
177         }
178
179         return delta;
180 }
181
182 void
183 chn_wrupdate(struct pcm_channel *c)
184 {
185         int ret;
186
187         CHN_LOCKASSERT(c);
188         KASSERT(c->direction == PCMDIR_PLAY, ("chn_wrupdate on bad channel"));
189
190         if ((c->flags & (CHN_F_MAPPED | CHN_F_VIRTUAL)) || !(c->flags & CHN_F_TRIGGERED))
191                 return;
192         chn_dmaupdate(c);
193         ret = chn_wrfeed(c);
194         /* tell the driver we've updated the primary buffer */
195         chn_trigger(c, PCMTRIG_EMLDMAWR);
196         DEB(if (ret)
197                 printf("chn_wrupdate: chn_wrfeed returned %d\n", ret);)
198
199 }
200
201 int
202 chn_wrfeed(struct pcm_channel *c)
203 {
204         struct snd_dbuf *b = c->bufhard;
205         struct snd_dbuf *bs = c->bufsoft;
206         unsigned int ret, amt;
207
208         CHN_LOCKASSERT(c);
209         DEB(
210         if (c->flags & CHN_F_CLOSING) {
211                 sndbuf_dump(b, "b", 0x02);
212                 sndbuf_dump(bs, "bs", 0x02);
213         })
214
215         if (c->flags & CHN_F_MAPPED)
216                 sndbuf_acquire(bs, NULL, sndbuf_getfree(bs));
217
218         amt = sndbuf_getfree(b);
219         if (sndbuf_getready(bs) < amt)
220                 c->xruns++;
221
222         ret = (amt > 0)? sndbuf_feed(bs, b, c, c->feeder, amt) : ENOSPC;
223         if (ret == 0 && sndbuf_getfree(b) < amt)
224                 chn_wakeup(c);
225
226         return ret;
227 }
228
229 static void
230 chn_wrintr(struct pcm_channel *c)
231 {
232         int ret;
233
234         CHN_LOCKASSERT(c);
235         /* update pointers in primary buffer */
236         chn_dmaupdate(c);
237         /* ...and feed from secondary to primary */
238         ret = chn_wrfeed(c);
239         /* tell the driver we've updated the primary buffer */
240         chn_trigger(c, PCMTRIG_EMLDMAWR);
241         DEB(if (ret)
242                 printf("chn_wrintr: chn_wrfeed returned %d\n", ret);)
243 }
244
245 /*
246  * user write routine - uiomove data into secondary buffer, trigger if necessary
247  * if blocking, sleep, rinse and repeat.
248  *
249  * called externally, so must handle locking
250  */
251
252 int
253 chn_write(struct pcm_channel *c, struct uio *buf, int ioflags)
254 {
255         int ret, timeout, newsize, count, sz;
256         int nbio;
257         struct snd_dbuf *bs = c->bufsoft;
258
259         CHN_LOCKASSERT(c);
260         nbio = (c->flags & CHN_F_NBIO) || (ioflags & IO_NDELAY);
261         /*
262          * XXX Certain applications attempt to write larger size
263          * of pcm data than c->blocksize2nd without blocking,
264          * resulting partial write. Expand the block size so that
265          * the write operation avoids blocking.
266          */
267         if (nbio && buf->uio_resid > sndbuf_getblksz(bs)) {
268                 DEB(device_printf(c->dev, "broken app, nbio and tried to write %d bytes with fragsz %d\n",
269                         buf->uio_resid, sndbuf_getblksz(bs)));
270                 newsize = 16;
271                 while (newsize < min(buf->uio_resid, CHN_2NDBUFMAXSIZE / 2))
272                         newsize <<= 1;
273                 chn_setblocksize(c, sndbuf_getblkcnt(bs), newsize);
274                 DEB(device_printf(c->dev, "frags reset to %d x %d\n", sndbuf_getblkcnt(bs), sndbuf_getblksz(bs)));
275         }
276
277         ret = 0;
278         count = hz;
279         while (!ret && (buf->uio_resid > 0) && (count > 0)) {
280                 sz = sndbuf_getfree(bs);
281                 if (sz == 0) {
282                         if (nbio) {
283                                 ret = EWOULDBLOCK;
284                         } else {
285                                 timeout = (hz * sndbuf_getblksz(bs)) / (sndbuf_getspd(bs) * sndbuf_getbps(bs));
286                                 if (timeout < 1)
287                                         timeout = 1;
288                                 timeout = 1;
289                                 ret = chn_sleep(c, "pcmwr", timeout);
290                                 if (ret == EWOULDBLOCK) {
291                                         count -= timeout;
292                                         ret = 0;
293                                 } else if (ret == 0)
294                                         count = hz;
295                         }
296                 } else {
297                         sz = MIN(sz, buf->uio_resid);
298                         KASSERT(sz > 0, ("confusion in chn_write"));
299                         /* printf("sz: %d\n", sz); */
300                         ret = sndbuf_uiomove(bs, buf, sz);
301                         if (ret == 0 && !(c->flags & CHN_F_TRIGGERED))
302                                 chn_start(c, 0);
303                 }
304         }
305         /* printf("ret: %d left: %d\n", ret, buf->uio_resid); */
306
307         if (count <= 0) {
308                 c->flags |= CHN_F_DEAD;
309                 printf("%s: play interrupt timeout, channel dead\n", c->name);
310         }
311
312         return ret;
313 }
314
315 static int
316 chn_rddump(struct pcm_channel *c, unsigned int cnt)
317 {
318         struct snd_dbuf *b = c->bufhard;
319
320         CHN_LOCKASSERT(c);
321         sndbuf_setxrun(b, sndbuf_getxrun(b) + cnt);
322         return sndbuf_dispose(b, NULL, cnt);
323 }
324
325 /*
326  * Feed new data from the read buffer. Can be called in the bottom half.
327  * Hence must be called from a critical section.
328  */
329 int
330 chn_rdfeed(struct pcm_channel *c)
331 {
332         struct snd_dbuf *b = c->bufhard;
333         struct snd_dbuf *bs = c->bufsoft;
334         unsigned int ret, amt;
335
336         CHN_LOCKASSERT(c);
337         DEB(
338         if (c->flags & CHN_F_CLOSING) {
339                 sndbuf_dump(b, "b", 0x02);
340                 sndbuf_dump(bs, "bs", 0x02);
341         })
342
343         amt = sndbuf_getready(b);
344         if (sndbuf_getfree(bs) < amt) {
345                 c->xruns++;
346                 amt = sndbuf_getfree(bs);
347         }
348         ret = (amt > 0)? sndbuf_feed(b, bs, c, c->feeder, amt) : 0;
349
350         amt = sndbuf_getready(b);
351         if (amt > 0)
352                 chn_rddump(c, amt);
353
354         chn_wakeup(c);
355
356         return ret;
357 }
358
359 void
360 chn_rdupdate(struct pcm_channel *c)
361 {
362         int ret;
363
364         CHN_LOCKASSERT(c);
365         KASSERT(c->direction == PCMDIR_REC, ("chn_rdupdate on bad channel"));
366
367         if ((c->flags & CHN_F_MAPPED) || !(c->flags & CHN_F_TRIGGERED))
368                 return;
369         chn_trigger(c, PCMTRIG_EMLDMARD);
370         chn_dmaupdate(c);
371         ret = chn_rdfeed(c);
372         if (ret)
373                 printf("chn_rdfeed: %d\n", ret);
374
375 }
376
377 /* read interrupt routine. Must be called with interrupts blocked. */
378 static void
379 chn_rdintr(struct pcm_channel *c)
380 {
381         int ret;
382
383         CHN_LOCKASSERT(c);
384         /* tell the driver to update the primary buffer if non-dma */
385         chn_trigger(c, PCMTRIG_EMLDMARD);
386         /* update pointers in primary buffer */
387         chn_dmaupdate(c);
388         /* ...and feed from primary to secondary */
389         ret = chn_rdfeed(c);
390 }
391
392 /*
393  * user read routine - trigger if necessary, uiomove data from secondary buffer
394  * if blocking, sleep, rinse and repeat.
395  *
396  * called externally, so must handle locking
397  */
398
399 int
400 chn_read(struct pcm_channel *c, struct uio *buf, int ioflags)
401 {
402         int ret, timeout, sz, count;
403         int nbio;
404         struct snd_dbuf       *bs = c->bufsoft;
405
406         CHN_LOCKASSERT(c);
407         if (!(c->flags & CHN_F_TRIGGERED))
408                 chn_start(c, 0);
409
410         nbio = (c->flags & CHN_F_NBIO) || (ioflags & IO_NDELAY);
411         ret = 0;
412         count = hz;
413         while (!ret && (buf->uio_resid > 0) && (count > 0)) {
414                 sz = MIN(buf->uio_resid, sndbuf_getready(bs));
415
416                 if (sz > 0) {
417                         ret = sndbuf_uiomove(bs, buf, sz);
418                 } else {
419                         if (nbio) {
420                                 ret = EWOULDBLOCK;
421                         } else {
422                                 timeout = (hz * sndbuf_getblksz(bs)) / (sndbuf_getspd(bs) * sndbuf_getbps(bs));
423                                 if (timeout < 1)
424                                         timeout = 1;
425                                 ret = chn_sleep(c, "pcmrd", timeout);
426                                 if (ret == EWOULDBLOCK) {
427                                         count -= timeout;
428                                         ret = 0;
429                                 } else {
430                                         count = hz;
431                                 }
432
433                         }
434                 }
435         }
436
437         if (count <= 0) {
438                 c->flags |= CHN_F_DEAD;
439                 printf("%s: record interrupt timeout, channel dead\n", c->name);
440         }
441
442         return ret;
443 }
444
445 void
446 chn_intr(struct pcm_channel *c)
447 {
448         CHN_LOCK(c);
449         c->interrupts++;
450         if (c->direction == PCMDIR_PLAY)
451                 chn_wrintr(c);
452         else
453                 chn_rdintr(c);
454         CHN_UNLOCK(c);
455 }
456
457 u_int32_t
458 chn_start(struct pcm_channel *c, int force)
459 {
460         u_int32_t i, j;
461         struct snd_dbuf *b = c->bufhard;
462         struct snd_dbuf *bs = c->bufsoft;
463
464         CHN_LOCKASSERT(c);
465         /* if we're running, or if we're prevented from triggering, bail */
466         if ((c->flags & CHN_F_TRIGGERED) || ((c->flags & CHN_F_NOTRIGGER) && !force))
467                 return EINVAL;
468
469         i = (c->direction == PCMDIR_PLAY)? sndbuf_getready(bs) : sndbuf_getfree(bs);
470         j = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(b) : sndbuf_getready(b);
471         if (force || (i >= j)) {
472                 c->flags |= CHN_F_TRIGGERED;
473                 /*
474                  * if we're starting because a vchan started, don't feed any data
475                  * or it becomes impossible to start vchans synchronised with the
476                  * first one.  the hardbuf should be empty so we top it up with
477                  * silence to give it something to chew.  the real data will be
478                  * fed at the first irq.
479                  */
480                 if (c->direction == PCMDIR_PLAY) {
481                         if (SLIST_EMPTY(&c->children))
482                                 chn_wrfeed(c);
483                         else
484                                 sndbuf_fillsilence(b);
485                 }
486                 sndbuf_setrun(b, 1);
487                 c->xruns = 0;
488                 chn_trigger(c, PCMTRIG_START);
489                 return 0;
490         }
491
492         return 0;
493 }
494
495 void
496 chn_resetbuf(struct pcm_channel *c)
497 {
498         struct snd_dbuf *b = c->bufhard;
499         struct snd_dbuf *bs = c->bufsoft;
500
501         c->blocks = 0;
502         sndbuf_reset(b);
503         sndbuf_reset(bs);
504 }
505
506 /*
507  * chn_sync waits until the space in the given channel goes above
508  * a threshold. The threshold is checked against fl or rl respectively.
509  * Assume that the condition can become true, do not check here...
510  */
511 int
512 chn_sync(struct pcm_channel *c, int threshold)
513 {
514         u_long rdy;
515         int ret;
516         struct snd_dbuf *bs = c->bufsoft;
517
518         CHN_LOCKASSERT(c);
519         for (;;) {
520                 rdy = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(bs) : sndbuf_getready(bs);
521                 if (rdy <= threshold) {
522                         ret = chn_sleep(c, "pcmsyn", 1);
523                         if (ret == ERESTART || ret == EINTR) {
524                                 DEB(printf("chn_sync: tsleep returns %d\n", ret));
525                                 return -1;
526                         }
527                 } else
528                         break;
529         }
530         return 0;
531 }
532
533 /* called externally, handle locking */
534 int
535 chn_poll(struct pcm_channel *c, int ev)
536 {
537         struct snd_dbuf *bs = c->bufsoft;
538         int ret;
539
540         CHN_LOCKASSERT(c);
541         if (!(c->flags & CHN_F_MAPPED) && !(c->flags & CHN_F_TRIGGERED))
542                 chn_start(c, 1);
543         ret = 0;
544         if (chn_polltrigger(c) && chn_pollreset(c))
545                 ret = ev;
546         else
547                 selrecord(curthread, sndbuf_getsel(bs));
548         return ret;
549 }
550
551 /*
552  * chn_abort terminates a running dma transfer.  it may sleep up to 200ms.
553  * it returns the number of bytes that have not been transferred.
554  *
555  * called from: dsp_close, dsp_ioctl, with channel locked
556  */
557 int
558 chn_abort(struct pcm_channel *c)
559 {
560         int missing = 0;
561         struct snd_dbuf *b = c->bufhard;
562         struct snd_dbuf *bs = c->bufsoft;
563
564         CHN_LOCKASSERT(c);
565         if (!(c->flags & CHN_F_TRIGGERED))
566                 return 0;
567         c->flags |= CHN_F_ABORTING;
568
569         c->flags &= ~CHN_F_TRIGGERED;
570         /* kill the channel */
571         chn_trigger(c, PCMTRIG_ABORT);
572         sndbuf_setrun(b, 0);
573         if (!(c->flags & CHN_F_VIRTUAL))
574                 chn_dmaupdate(c);
575         missing = sndbuf_getready(bs) + sndbuf_getready(b);
576
577         c->flags &= ~CHN_F_ABORTING;
578         return missing;
579 }
580
581 /*
582  * this routine tries to flush the dma transfer. It is called
583  * on a close. We immediately abort any read DMA
584  * operation, and then wait for the play buffer to drain.
585  *
586  * called from: dsp_close
587  */
588
589 int
590 chn_flush(struct pcm_channel *c)
591 {
592         int ret, count, resid, resid_p;
593         struct snd_dbuf *b = c->bufhard;
594         struct snd_dbuf *bs = c->bufsoft;
595
596         CHN_LOCKASSERT(c);
597         KASSERT(c->direction == PCMDIR_PLAY, ("chn_wrupdate on bad channel"));
598         DEB(printf("chn_flush c->flags 0x%08x\n", c->flags));
599         if (!(c->flags & CHN_F_TRIGGERED))
600                 return 0;
601
602         c->flags |= CHN_F_CLOSING;
603         resid = sndbuf_getready(bs) + sndbuf_getready(b);
604         resid_p = resid;
605         count = 10;
606         ret = 0;
607         while ((count > 0) && (resid > sndbuf_getsize(b)) && (ret == 0)) {
608                 /* still pending output data. */
609                 ret = chn_sleep(c, "pcmflu", hz / 10);
610                 if (ret == EWOULDBLOCK)
611                         ret = 0;
612                 if (ret == 0) {
613                         resid = sndbuf_getready(bs) + sndbuf_getready(b);
614                         if (resid >= resid_p)
615                                 count--;
616                         resid_p = resid;
617                 }
618         }
619         if (count == 0)
620                 DEB(printf("chn_flush: timeout\n"));
621
622         c->flags &= ~CHN_F_TRIGGERED;
623         /* kill the channel */
624         chn_trigger(c, PCMTRIG_ABORT);
625         sndbuf_setrun(b, 0);
626
627         c->flags &= ~CHN_F_CLOSING;
628         return 0;
629 }
630
631 int
632 fmtvalid(u_int32_t fmt, u_int32_t *fmtlist)
633 {
634         int i;
635
636         for (i = 0; fmtlist[i]; i++)
637                 if (fmt == fmtlist[i])
638                         return 1;
639         return 0;
640 }
641
642 int
643 chn_reset(struct pcm_channel *c, u_int32_t fmt)
644 {
645         int hwspd, r;
646
647         CHN_LOCKASSERT(c);
648         c->flags &= CHN_F_RESET;
649         c->interrupts = 0;
650         c->xruns = 0;
651
652         r = CHANNEL_RESET(c->methods, c->devinfo);
653         if (fmt != 0) {
654                 hwspd = DSP_DEFAULT_SPEED;
655                 /* only do this on a record channel until feederbuilder works */
656                 if (c->direction == PCMDIR_REC)
657                         RANGE(hwspd, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed);
658                 c->speed = hwspd;
659
660                 if (r == 0)
661                         r = chn_setformat(c, fmt);
662                 if (r == 0)
663                         r = chn_setspeed(c, hwspd);
664                 if (r == 0)
665                         r = chn_setvolume(c, 100, 100);
666         }
667         if (r == 0)
668                 r = chn_setblocksize(c, 0, 0);
669         if (r == 0) {
670                 chn_resetbuf(c);
671                 r = CHANNEL_RESETDONE(c->methods, c->devinfo);
672         }
673         return r;
674 }
675
676 int
677 chn_init(struct pcm_channel *c, void *devinfo, int dir)
678 {
679         struct feeder_class *fc;
680         struct snd_dbuf *b, *bs;
681         int ret;
682
683         chn_lockinit(c);
684         CHN_LOCK(c);
685
686         b = NULL;
687         bs = NULL;
688         c->devinfo = NULL;
689         c->feeder = NULL;
690
691         ret = EINVAL;
692         fc = feeder_getclass(NULL);
693         if (fc == NULL)
694                 goto out;
695         if (chn_addfeeder(c, fc, NULL))
696                 goto out;
697
698         ret = ENOMEM;
699         b = sndbuf_create(c->dev, c->name, "primary");
700         if (b == NULL)
701                 goto out;
702         bs = sndbuf_create(c->dev, c->name, "secondary");
703         if (bs == NULL)
704                 goto out;
705         sndbuf_setup(bs, NULL, 0);
706         c->bufhard = b;
707         c->bufsoft = bs;
708         c->flags = 0;
709         c->feederflags = 0;
710
711         ret = ENODEV;
712         c->devinfo = CHANNEL_INIT(c->methods, devinfo, b, c, dir);
713         if (c->devinfo == NULL)
714                 goto out;
715
716         ret = ENOMEM;
717         if ((sndbuf_getsize(b) == 0) && ((c->flags & CHN_F_VIRTUAL) == 0))
718                 goto out;
719
720         ret = chn_setdir(c, dir);
721         if (ret)
722                 goto out;
723
724         ret = sndbuf_setfmt(b, AFMT_U8);
725         if (ret)
726                 goto out;
727
728         ret = sndbuf_setfmt(bs, AFMT_U8);
729         if (ret)
730                 goto out;
731
732
733 out:
734         if (ret) {
735                 if (c->devinfo) {
736                         if (CHANNEL_FREE(c->methods, c->devinfo))
737                                 sndbuf_free(b);
738                 }
739                 if (bs)
740                         sndbuf_destroy(bs);
741                 if (b)
742                         sndbuf_destroy(b);
743                 c->flags |= CHN_F_DEAD;
744                 chn_lockdestroy(c);
745
746                 return ret;
747         }
748
749         CHN_UNLOCK(c);
750         return 0;
751 }
752
753 int
754 chn_kill(struct pcm_channel *c)
755 {
756         struct snd_dbuf *b = c->bufhard;
757         struct snd_dbuf *bs = c->bufsoft;
758
759         CHN_LOCK(c);
760         if (c->flags & CHN_F_TRIGGERED)
761                 chn_trigger(c, PCMTRIG_ABORT);
762         while (chn_removefeeder(c) == 0);
763         if (CHANNEL_FREE(c->methods, c->devinfo))
764                 sndbuf_free(b);
765         c->flags |= CHN_F_DEAD;
766         sndbuf_destroy(bs);
767         sndbuf_destroy(b);
768         chn_lockdestroy(c);
769         return 0;
770 }
771
772 int
773 chn_setdir(struct pcm_channel *c, int dir)
774 {
775         struct snd_dbuf *b = c->bufhard;
776         int r;
777
778         CHN_LOCKASSERT(c);
779         c->direction = dir;
780         r = CHANNEL_SETDIR(c->methods, c->devinfo, c->direction);
781         if (!r && ISA_DMA(b))
782                 sndbuf_isadmasetdir(b, c->direction);
783         return r;
784 }
785
786 int
787 chn_setvolume(struct pcm_channel *c, int left, int right)
788 {
789         CHN_LOCKASSERT(c);
790         /* could add a feeder for volume changing if channel returns -1 */
791         c->volume = (left << 8) | right;
792         return 0;
793 }
794
795 static int
796 chn_tryspeed(struct pcm_channel *c, int speed)
797 {
798         struct pcm_feeder *f;
799         struct snd_dbuf *b = c->bufhard;
800         struct snd_dbuf *bs = c->bufsoft;
801         struct snd_dbuf *x;
802         int r, delta;
803
804         CHN_LOCKASSERT(c);
805         DEB(printf("setspeed, channel %s\n", c->name));
806         DEB(printf("want speed %d, ", speed));
807         if (speed <= 0)
808                 return EINVAL;
809         if (CANCHANGE(c)) {
810                 r = 0;
811                 c->speed = speed;
812                 sndbuf_setspd(bs, speed);
813                 RANGE(speed, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed);
814                 DEB(printf("try speed %d, ", speed));
815                 sndbuf_setspd(b, CHANNEL_SETSPEED(c->methods, c->devinfo, speed));
816                 DEB(printf("got speed %d\n", sndbuf_getspd(b)));
817
818                 delta = sndbuf_getspd(b) - sndbuf_getspd(bs);
819                 if (delta < 0)
820                         delta = -delta;
821
822                 c->feederflags &= ~(1 << FEEDER_RATE);
823                 if (delta > 500)
824                         c->feederflags |= 1 << FEEDER_RATE;
825                 else
826                         sndbuf_setspd(bs, sndbuf_getspd(b));
827
828                 r = chn_buildfeeder(c);
829                 DEB(printf("r = %d\n", r));
830                 if (r)
831                         goto out;
832
833                 r = chn_setblocksize(c, 0, 0);
834                 if (r)
835                         goto out;
836
837                 if (!(c->feederflags & (1 << FEEDER_RATE)))
838                         goto out;
839
840                 r = EINVAL;
841                 f = chn_findfeeder(c, FEEDER_RATE);
842                 DEB(printf("feedrate = %p\n", f));
843                 if (f == NULL)
844                         goto out;
845
846                 x = (c->direction == PCMDIR_REC)? b : bs;
847                 r = FEEDER_SET(f, FEEDRATE_SRC, sndbuf_getspd(x));
848                 DEB(printf("feeder_set(FEEDRATE_SRC, %d) = %d\n", sndbuf_getspd(x), r));
849                 if (r)
850                         goto out;
851
852                 x = (c->direction == PCMDIR_REC)? bs : b;
853                 r = FEEDER_SET(f, FEEDRATE_DST, sndbuf_getspd(x));
854                 DEB(printf("feeder_set(FEEDRATE_DST, %d) = %d\n", sndbuf_getspd(x), r));
855 out:
856                 DEB(printf("setspeed done, r = %d\n", r));
857                 return r;
858         } else
859                 return EINVAL;
860 }
861
862 int
863 chn_setspeed(struct pcm_channel *c, int speed)
864 {
865         int r, oldspeed = c->speed;
866
867         r = chn_tryspeed(c, speed);
868         if (r) {
869                 DEB(printf("Failed to set speed %d falling back to %d\n", speed, oldspeed));
870                 r = chn_tryspeed(c, oldspeed);
871         }
872         return r;
873 }
874
875 static int
876 chn_tryformat(struct pcm_channel *c, u_int32_t fmt)
877 {
878         struct snd_dbuf *b = c->bufhard;
879         struct snd_dbuf *bs = c->bufsoft;
880         int r;
881
882         CHN_LOCKASSERT(c);
883         if (CANCHANGE(c)) {
884                 DEB(printf("want format %d\n", fmt));
885                 c->format = fmt;
886                 r = chn_buildfeeder(c);
887                 if (r == 0) {
888                         sndbuf_setfmt(bs, c->format);
889                         chn_resetbuf(c);
890                         r = CHANNEL_SETFORMAT(c->methods, c->devinfo, sndbuf_getfmt(b));
891                         if (r == 0)
892                                 r = chn_tryspeed(c, c->speed);
893                 }
894                 return r;
895         } else
896                 return EINVAL;
897 }
898
899 int
900 chn_setformat(struct pcm_channel *c, u_int32_t fmt)
901 {
902         u_int32_t oldfmt = c->format;
903         int r;
904
905         r = chn_tryformat(c, fmt);
906         if (r) {
907                 DEB(printf("Format change %d failed, reverting to %d\n", fmt, oldfmt));
908                 chn_tryformat(c, oldfmt);
909         }
910         return r;
911 }
912
913 int
914 chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz)
915 {
916         struct snd_dbuf *b = c->bufhard;
917         struct snd_dbuf *bs = c->bufsoft;
918         int bufsz, irqhz, tmp, ret;
919
920         CHN_LOCKASSERT(c);
921         if (!CANCHANGE(c) || (c->flags & CHN_F_MAPPED))
922                 return EINVAL;
923
924         ret = 0;
925         DEB(printf("%s(%d, %d)\n", __func__, blkcnt, blksz));
926         if (blksz == 0 || blksz == -1) {
927                 if (blksz == -1)
928                         c->flags &= ~CHN_F_HAS_SIZE;
929                 if (!(c->flags & CHN_F_HAS_SIZE)) {
930                         blksz = (sndbuf_getbps(bs) * sndbuf_getspd(bs)) / chn_targetirqrate;
931                         tmp = 32;
932                         while (tmp <= blksz)
933                                 tmp <<= 1;
934                         tmp >>= 1;
935                         blksz = tmp;
936                         blkcnt = CHN_2NDBUFMAXSIZE / blksz;
937
938                         RANGE(blksz, 16, CHN_2NDBUFMAXSIZE / 2);
939                         RANGE(blkcnt, 2, CHN_2NDBUFMAXSIZE / blksz);
940                         DEB(printf("%s: defaulting to (%d, %d)\n", __func__, blkcnt, blksz));
941                 } else {
942                         blkcnt = sndbuf_getblkcnt(bs);
943                         blksz = sndbuf_getblksz(bs);
944                         DEB(printf("%s: updating (%d, %d)\n", __func__, blkcnt, blksz));
945                 }
946         } else {
947                 ret = EINVAL;
948                 if ((blksz < 16) || (blkcnt < 2) || (blkcnt * blksz > CHN_2NDBUFMAXSIZE))
949                         goto out;
950                 ret = 0;
951                 c->flags |= CHN_F_HAS_SIZE;
952         }
953
954         bufsz = blkcnt * blksz;
955
956         ret = ENOMEM;
957         if (sndbuf_remalloc(bs, blkcnt, blksz))
958                 goto out;
959         ret = 0;
960
961         /* adjust for different hw format/speed */
962         irqhz = (sndbuf_getbps(bs) * sndbuf_getspd(bs)) / sndbuf_getblksz(bs);
963         DEB(printf("%s: soft bps %d, spd %d, irqhz == %d\n", __func__, sndbuf_getbps(bs), sndbuf_getspd(bs), irqhz));
964         RANGE(irqhz, 16, 512);
965
966         sndbuf_setblksz(b, (sndbuf_getbps(b) * sndbuf_getspd(b)) / irqhz);
967
968         /* round down to 2^x */
969         blksz = 32;
970         while (blksz <= sndbuf_getblksz(b))
971                 blksz <<= 1;
972         blksz >>= 1;
973
974         /* round down to fit hw buffer size */
975         RANGE(blksz, 16, sndbuf_getmaxsize(b) / 2);
976         DEB(printf("%s: hard blksz requested %d (maxsize %d), ", __func__, blksz, sndbuf_getmaxsize(b)));
977
978         sndbuf_setblksz(b, CHANNEL_SETBLOCKSIZE(c->methods, c->devinfo, blksz));
979
980         irqhz = (sndbuf_getbps(b) * sndbuf_getspd(b)) / sndbuf_getblksz(b);
981         DEB(printf("got %d, irqhz == %d\n", sndbuf_getblksz(b), irqhz));
982
983         chn_resetbuf(c);
984 out:
985         return ret;
986 }
987
988 int
989 chn_trigger(struct pcm_channel *c, int go)
990 {
991         struct snd_dbuf *b = c->bufhard;
992         int ret;
993
994         CHN_LOCKASSERT(c);
995         if (ISA_DMA(b) && (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD))
996                 sndbuf_isadmabounce(b);
997         ret = CHANNEL_TRIGGER(c->methods, c->devinfo, go);
998
999         return ret;
1000 }
1001
1002 int
1003 chn_getptr(struct pcm_channel *c)
1004 {
1005         int hwptr;
1006         int a = (1 << c->align) - 1;
1007
1008         CHN_LOCKASSERT(c);
1009         hwptr = (c->flags & CHN_F_TRIGGERED)? CHANNEL_GETPTR(c->methods, c->devinfo) : 0;
1010         /* don't allow unaligned values in the hwa ptr */
1011 #if 1
1012         hwptr &= ~a ; /* Apply channel align mask */
1013 #endif
1014         hwptr &= DMA_ALIGN_MASK; /* Apply DMA align mask */
1015         return hwptr;
1016 }
1017
1018 struct pcmchan_caps *
1019 chn_getcaps(struct pcm_channel *c)
1020 {
1021         CHN_LOCKASSERT(c);
1022         return CHANNEL_GETCAPS(c->methods, c->devinfo);
1023 }
1024
1025 u_int32_t
1026 chn_getformats(struct pcm_channel *c)
1027 {
1028         u_int32_t *fmtlist, fmts;
1029         int i;
1030
1031         fmtlist = chn_getcaps(c)->fmtlist;
1032         fmts = 0;
1033         for (i = 0; fmtlist[i]; i++)
1034                 fmts |= fmtlist[i];
1035
1036         /* report software-supported formats */
1037         if (report_soft_formats)
1038                 fmts |= AFMT_MU_LAW|AFMT_A_LAW|AFMT_U16_LE|AFMT_U16_BE|
1039                     AFMT_S16_LE|AFMT_S16_BE|AFMT_U8|AFMT_S8;
1040
1041         return fmts;
1042 }
1043
1044 static int
1045 chn_buildfeeder(struct pcm_channel *c)
1046 {
1047         struct feeder_class *fc;
1048         struct pcm_feederdesc desc;
1049         u_int32_t tmp[2], type, flags, hwfmt;
1050         int err;
1051
1052         CHN_LOCKASSERT(c);
1053         while (chn_removefeeder(c) == 0);
1054         KASSERT((c->feeder == NULL), ("feeder chain not empty"));
1055
1056         c->align = sndbuf_getalign(c->bufsoft);
1057
1058         if (SLIST_EMPTY(&c->children)) {
1059                 fc = feeder_getclass(NULL);
1060                 KASSERT(fc != NULL, ("can't find root feeder"));
1061
1062                 err = chn_addfeeder(c, fc, NULL);
1063                 if (err) {
1064                         DEB(printf("can't add root feeder, err %d\n", err));
1065
1066                         return err;
1067                 }
1068                 c->feeder->desc->out = c->format;
1069         } else {
1070                 desc.type = FEEDER_MIXER;
1071                 desc.in = 0;
1072                 desc.out = c->format;
1073                 desc.flags = 0;
1074                 fc = feeder_getclass(&desc);
1075                 if (fc == NULL) {
1076                         DEB(printf("can't find vchan feeder\n"));
1077
1078                         return EOPNOTSUPP;
1079                 }
1080
1081                 err = chn_addfeeder(c, fc, &desc);
1082                 if (err) {
1083                         DEB(printf("can't add vchan feeder, err %d\n", err));
1084
1085                         return err;
1086                 }
1087         }
1088         flags = c->feederflags;
1089
1090         DEB(printf("not mapped, feederflags %x\n", flags));
1091
1092         for (type = FEEDER_RATE; type <= FEEDER_LAST; type++) {
1093                 if (flags & (1 << type)) {
1094                         desc.type = type;
1095                         desc.in = 0;
1096                         desc.out = 0;
1097                         desc.flags = 0;
1098                         DEB(printf("find feeder type %d, ", type));
1099                         fc = feeder_getclass(&desc);
1100                         DEB(printf("got %p\n", fc));
1101                         if (fc == NULL) {
1102                                 DEB(printf("can't find required feeder type %d\n", type));
1103
1104                                 return EOPNOTSUPP;
1105                         }
1106
1107                         if (c->feeder->desc->out != fc->desc->in) {
1108                                 DEB(printf("build fmtchain from %x to %x: ", c->feeder->desc->out, fc->desc->in));
1109                                 tmp[0] = fc->desc->in;
1110                                 tmp[1] = 0;
1111                                 if (chn_fmtchain(c, tmp) == 0) {
1112                                         DEB(printf("failed\n"));
1113
1114                                         return ENODEV;
1115                                 }
1116                                 DEB(printf("ok\n"));
1117                         }
1118
1119                         err = chn_addfeeder(c, fc, fc->desc);
1120                         if (err) {
1121                                 DEB(printf("can't add feeder %p, output %x, err %d\n", fc, fc->desc->out, err));
1122
1123                                 return err;
1124                         }
1125                         DEB(printf("added feeder %p, output %x\n", fc, c->feeder->desc->out));
1126                 }
1127         }
1128
1129         if (fmtvalid(c->feeder->desc->out, chn_getcaps(c)->fmtlist)) {
1130                 hwfmt = c->feeder->desc->out;
1131         } else {
1132                 if (c->direction == PCMDIR_REC) {
1133                         tmp[0] = c->format;
1134                         tmp[1] = NULL;
1135                         hwfmt = chn_fmtchain(c, tmp);
1136                 } else {
1137                         hwfmt = chn_fmtchain(c, chn_getcaps(c)->fmtlist);
1138                 }
1139         }
1140
1141         if (hwfmt == 0)
1142                 return ENODEV;
1143
1144         sndbuf_setfmt(c->bufhard, hwfmt);
1145
1146         return 0;
1147 }
1148
1149 int
1150 chn_notify(struct pcm_channel *c, u_int32_t flags)
1151 {
1152         struct pcmchan_children *pce;
1153         struct pcm_channel *child;
1154         int run;
1155
1156         if (SLIST_EMPTY(&c->children))
1157                 return ENODEV;
1158
1159         run = (c->flags & CHN_F_TRIGGERED)? 1 : 0;
1160         /*
1161          * if the hwchan is running, we can't change its rate, format or
1162          * blocksize
1163          */
1164         if (run)
1165                 flags &= CHN_N_VOLUME | CHN_N_TRIGGER;
1166
1167         if (flags & CHN_N_RATE) {
1168                 /*
1169                  * we could do something here, like scan children and decide on
1170                  * the most appropriate rate to mix at, but we don't for now
1171                  */
1172         }
1173         if (flags & CHN_N_FORMAT) {
1174                 /*
1175                  * we could do something here, like scan children and decide on
1176                  * the most appropriate mixer feeder to use, but we don't for now
1177                  */
1178         }
1179         if (flags & CHN_N_VOLUME) {
1180                 /*
1181                  * we could do something here but we don't for now
1182                  */
1183         }
1184         if (flags & CHN_N_BLOCKSIZE) {
1185                 int blksz;
1186                 /*
1187                  * scan the children, find the lowest blocksize and use that
1188                  * for the hard blocksize
1189                  */
1190                 blksz = sndbuf_getmaxsize(c->bufhard) / 2;
1191                 SLIST_FOREACH(pce, &c->children, link) {
1192                         child = pce->channel;
1193                         if (sndbuf_getblksz(child->bufhard) < blksz)
1194                                 blksz = sndbuf_getblksz(child->bufhard);
1195                 }
1196                 chn_setblocksize(c, 2, blksz);
1197         }
1198         if (flags & CHN_N_TRIGGER) {
1199                 int nrun;
1200                 /*
1201                  * scan the children, and figure out if any are running
1202                  * if so, we need to be running, otherwise we need to be stopped
1203                  * if we aren't in our target sstate, move to it
1204                  */
1205                 nrun = 0;
1206                 SLIST_FOREACH(pce, &c->children, link) {
1207                         child = pce->channel;
1208                         if (child->flags & CHN_F_TRIGGERED)
1209                                 nrun = 1;
1210                 }
1211                 if (nrun && !run)
1212                         chn_start(c, 1);
1213                 if (!nrun && run)
1214                         chn_abort(c);
1215         }
1216         return 0;
1217 }