1193640Sariff/*- 2193640Sariff * Copyright (c) 2008-2009 Ariff Abdullah <ariff@FreeBSD.org> 3193640Sariff * All rights reserved. 4193640Sariff * 5193640Sariff * Redistribution and use in source and binary forms, with or without 6193640Sariff * modification, are permitted provided that the following conditions 7193640Sariff * are met: 8193640Sariff * 1. Redistributions of source code must retain the above copyright 9193640Sariff * notice, this list of conditions and the following disclaimer. 10193640Sariff * 2. Redistributions in binary form must reproduce the above copyright 11193640Sariff * notice, this list of conditions and the following disclaimer in the 12193640Sariff * documentation and/or other materials provided with the distribution. 13193640Sariff * 14193640Sariff * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15193640Sariff * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16193640Sariff * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17193640Sariff * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18193640Sariff * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19193640Sariff * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20193640Sariff * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21193640Sariff * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22193640Sariff * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23193640Sariff * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24193640Sariff * SUCH DAMAGE. 25193640Sariff */ 26193640Sariff 27193640Sariff#ifdef _KERNEL 28193640Sariff#ifdef HAVE_KERNEL_OPTION_HEADERS 29193640Sariff#include "opt_snd.h" 30193640Sariff#endif 31193640Sariff#include <dev/sound/pcm/sound.h> 32193640Sariff#include <dev/sound/pcm/pcm.h> 33193640Sariff#include <dev/sound/pcm/vchan.h> 34193640Sariff#include "feeder_if.h" 35193640Sariff 36193640Sariff#define SND_USE_FXDIV 37193640Sariff#include "snd_fxdiv_gen.h" 38193640Sariff 39193640SariffSND_DECLARE_FILE("$FreeBSD: stable/11/sys/dev/sound/pcm/feeder_mixer.c 318978 2017-05-27 08:25:08Z hselasky $"); 40193640Sariff#endif 41193640Sariff 42193640Sariff#undef SND_FEEDER_MULTIFORMAT 43193640Sariff#define SND_FEEDER_MULTIFORMAT 1 44193640Sariff 45193640Sarifftypedef void (*feed_mixer_t)(uint8_t *, uint8_t *, uint32_t); 46193640Sariff 47193640Sariff#define FEEDMIXER_DECLARE(SIGN, BIT, ENDIAN) \ 48193640Sariffstatic void \ 49193640Sarifffeed_mixer_##SIGN##BIT##ENDIAN(uint8_t *src, uint8_t *dst, \ 50193640Sariff uint32_t count) \ 51193640Sariff{ \ 52193640Sariff intpcm##BIT##_t z; \ 53193640Sariff intpcm_t x, y; \ 54193640Sariff \ 55193640Sariff src += count; \ 56193640Sariff dst += count; \ 57193640Sariff \ 58193640Sariff do { \ 59193640Sariff src -= PCM_##BIT##_BPS; \ 60193640Sariff dst -= PCM_##BIT##_BPS; \ 61193640Sariff count -= PCM_##BIT##_BPS; \ 62193640Sariff x = PCM_READ_##SIGN##BIT##_##ENDIAN(src); \ 63193640Sariff y = PCM_READ_##SIGN##BIT##_##ENDIAN(dst); \ 64193640Sariff z = INTPCM##BIT##_T(x) + y; \ 65193640Sariff x = PCM_CLAMP_##SIGN##BIT(z); \ 66193640Sariff _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, x); \ 67193640Sariff } while (count != 0); \ 68193640Sariff} 69193640Sariff 70193640Sariff#if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT) 71193640SariffFEEDMIXER_DECLARE(S, 16, LE) 72193640SariffFEEDMIXER_DECLARE(S, 32, LE) 73193640Sariff#endif 74193640Sariff#if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT) 75193640SariffFEEDMIXER_DECLARE(S, 16, BE) 76193640SariffFEEDMIXER_DECLARE(S, 32, BE) 77193640Sariff#endif 78193640Sariff#ifdef SND_FEEDER_MULTIFORMAT 79193640SariffFEEDMIXER_DECLARE(S, 8, NE) 80193640SariffFEEDMIXER_DECLARE(S, 24, LE) 81193640SariffFEEDMIXER_DECLARE(S, 24, BE) 82193640SariffFEEDMIXER_DECLARE(U, 8, NE) 83193640SariffFEEDMIXER_DECLARE(U, 16, LE) 84193640SariffFEEDMIXER_DECLARE(U, 24, LE) 85193640SariffFEEDMIXER_DECLARE(U, 32, LE) 86193640SariffFEEDMIXER_DECLARE(U, 16, BE) 87193640SariffFEEDMIXER_DECLARE(U, 24, BE) 88193640SariffFEEDMIXER_DECLARE(U, 32, BE) 89193640Sariff#endif 90193640Sariff 91193640Sariffstruct feed_mixer_info { 92193640Sariff uint32_t format; 93193640Sariff int bps; 94193640Sariff feed_mixer_t mix; 95193640Sariff}; 96193640Sariff 97193640Sariff#define FEEDMIXER_ENTRY(SIGN, BIT, ENDIAN) \ 98193640Sariff { \ 99193640Sariff AFMT_##SIGN##BIT##_##ENDIAN, PCM_##BIT##_BPS, \ 100193640Sariff feed_mixer_##SIGN##BIT##ENDIAN \ 101193640Sariff } 102193640Sariff 103193640Sariffstatic struct feed_mixer_info feed_mixer_info_tab[] = { 104193640Sariff FEEDMIXER_ENTRY(S, 8, NE), 105193640Sariff#if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT) 106193640Sariff FEEDMIXER_ENTRY(S, 16, LE), 107193640Sariff FEEDMIXER_ENTRY(S, 32, LE), 108193640Sariff#endif 109193640Sariff#if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT) 110193640Sariff FEEDMIXER_ENTRY(S, 16, BE), 111193640Sariff FEEDMIXER_ENTRY(S, 32, BE), 112193640Sariff#endif 113193640Sariff#ifdef SND_FEEDER_MULTIFORMAT 114193640Sariff FEEDMIXER_ENTRY(S, 24, LE), 115193640Sariff FEEDMIXER_ENTRY(S, 24, BE), 116193640Sariff FEEDMIXER_ENTRY(U, 8, NE), 117193640Sariff FEEDMIXER_ENTRY(U, 16, LE), 118193640Sariff FEEDMIXER_ENTRY(U, 24, LE), 119193640Sariff FEEDMIXER_ENTRY(U, 32, LE), 120193640Sariff FEEDMIXER_ENTRY(U, 16, BE), 121193640Sariff FEEDMIXER_ENTRY(U, 24, BE), 122193640Sariff FEEDMIXER_ENTRY(U, 32, BE), 123193640Sariff#endif 124193640Sariff { AFMT_AC3, PCM_16_BPS, NULL }, 125193640Sariff { AFMT_MU_LAW, PCM_8_BPS, feed_mixer_U8NE }, /* dummy */ 126193640Sariff { AFMT_A_LAW, PCM_8_BPS, feed_mixer_U8NE } /* dummy */ 127193640Sariff}; 128193640Sariff 129193640Sariff#define FEEDMIXER_TAB_SIZE ((int32_t) \ 130193640Sariff (sizeof(feed_mixer_info_tab) / \ 131193640Sariff sizeof(feed_mixer_info_tab[0]))) 132193640Sariff 133193640Sariff#define FEEDMIXER_DATA(i, c) ((void *) \ 134318978Shselasky ((uintptr_t)((((i) & 0x1f) << 7) | \ 135318978Shselasky ((c) & 0x7f)))) 136318978Shselasky#define FEEDMIXER_INFOIDX(d) ((uint32_t)((uintptr_t)(d) >> 7) & 0x1f) 137318978Shselasky#define FEEDMIXER_CHANNELS(d) ((uint32_t)((uintptr_t)(d)) & 0x7f) 138193640Sariff 139193640Sariffstatic int 140193640Sarifffeed_mixer_init(struct pcm_feeder *f) 141193640Sariff{ 142193640Sariff int i; 143193640Sariff 144193640Sariff if (f->desc->in != f->desc->out) 145193640Sariff return (EINVAL); 146193640Sariff 147193640Sariff for (i = 0; i < FEEDMIXER_TAB_SIZE; i++) { 148193640Sariff if (AFMT_ENCODING(f->desc->in) == 149193640Sariff feed_mixer_info_tab[i].format) { 150193640Sariff f->data = 151193640Sariff FEEDMIXER_DATA(i, AFMT_CHANNEL(f->desc->in)); 152193640Sariff return (0); 153193640Sariff } 154193640Sariff } 155193640Sariff 156193640Sariff return (EINVAL); 157193640Sariff} 158193640Sariff 159193640Sariffstatic int 160193640Sarifffeed_mixer_set(struct pcm_feeder *f, int what, int value) 161193640Sariff{ 162193640Sariff 163193640Sariff switch (what) { 164193640Sariff case FEEDMIXER_CHANNELS: 165193640Sariff if (value < SND_CHN_MIN || value > SND_CHN_MAX) 166193640Sariff return (EINVAL); 167193640Sariff f->data = FEEDMIXER_DATA(FEEDMIXER_INFOIDX(f->data), value); 168193640Sariff break; 169193640Sariff default: 170193640Sariff return (EINVAL); 171193640Sariff break; 172193640Sariff } 173193640Sariff 174193640Sariff return (0); 175193640Sariff} 176193640Sariff 177193640Sariffstatic __inline int 178193640Sarifffeed_mixer_rec(struct pcm_channel *c) 179193640Sariff{ 180193640Sariff struct pcm_channel *ch; 181193640Sariff struct snd_dbuf *b, *bs; 182193640Sariff uint32_t cnt, maxfeed; 183193640Sariff int rdy; 184193640Sariff 185193640Sariff /* 186193640Sariff * Reset ready and moving pointer. We're not using bufsoft 187193640Sariff * anywhere since its sole purpose is to become the primary 188193640Sariff * distributor for the recorded buffer and also as an interrupt 189193640Sariff * threshold progress indicator. 190193640Sariff */ 191193640Sariff b = c->bufsoft; 192193640Sariff b->rp = 0; 193193640Sariff b->rl = 0; 194193640Sariff cnt = sndbuf_getsize(b); 195193640Sariff maxfeed = SND_FXROUND(SND_FXDIV_MAX, sndbuf_getalign(b)); 196193640Sariff 197193640Sariff do { 198193640Sariff cnt = FEEDER_FEED(c->feeder->source, c, b->tmpbuf, 199193640Sariff min(cnt, maxfeed), c->bufhard); 200193640Sariff if (cnt != 0) { 201193640Sariff sndbuf_acquire(b, b->tmpbuf, cnt); 202193640Sariff cnt = sndbuf_getfree(b); 203193640Sariff } 204193640Sariff } while (cnt != 0); 205193640Sariff 206193640Sariff /* Not enough data */ 207193640Sariff if (b->rl < sndbuf_getalign(b)) { 208193640Sariff b->rl = 0; 209193640Sariff return (0); 210193640Sariff } 211193640Sariff 212193640Sariff /* 213193640Sariff * Keep track of ready and moving pointer since we will use 214193640Sariff * bufsoft over and over again, pretending nothing has happened. 215193640Sariff */ 216193640Sariff rdy = b->rl; 217193640Sariff 218193640Sariff CHN_FOREACH(ch, c, children.busy) { 219193640Sariff CHN_LOCK(ch); 220193640Sariff if (CHN_STOPPED(ch) || (ch->flags & CHN_F_DIRTY)) { 221193640Sariff CHN_UNLOCK(ch); 222193640Sariff continue; 223193640Sariff } 224193640Sariff#ifdef SND_DEBUG 225193640Sariff if ((c->flags & CHN_F_DIRTY) && VCHAN_SYNC_REQUIRED(ch)) { 226193640Sariff if (vchan_sync(ch) != 0) { 227193640Sariff CHN_UNLOCK(ch); 228193640Sariff continue; 229193640Sariff } 230193640Sariff } 231193640Sariff#endif 232193640Sariff bs = ch->bufsoft; 233193640Sariff if (ch->flags & CHN_F_MMAP) 234193640Sariff sndbuf_dispose(bs, NULL, sndbuf_getready(bs)); 235193640Sariff cnt = sndbuf_getfree(bs); 236193640Sariff if (cnt < sndbuf_getalign(bs)) { 237193640Sariff CHN_UNLOCK(ch); 238193640Sariff continue; 239193640Sariff } 240193640Sariff maxfeed = SND_FXROUND(SND_FXDIV_MAX, sndbuf_getalign(bs)); 241193640Sariff do { 242193640Sariff cnt = FEEDER_FEED(ch->feeder, ch, bs->tmpbuf, 243193640Sariff min(cnt, maxfeed), b); 244193640Sariff if (cnt != 0) { 245193640Sariff sndbuf_acquire(bs, bs->tmpbuf, cnt); 246193640Sariff cnt = sndbuf_getfree(bs); 247193640Sariff } 248193640Sariff } while (cnt != 0); 249193640Sariff /* 250193640Sariff * Not entirely flushed out... 251193640Sariff */ 252193640Sariff if (b->rl != 0) 253193640Sariff ch->xruns++; 254193640Sariff CHN_UNLOCK(ch); 255193640Sariff /* 256193640Sariff * Rewind buffer position for next virtual channel. 257193640Sariff */ 258193640Sariff b->rp = 0; 259193640Sariff b->rl = rdy; 260193640Sariff } 261193640Sariff 262193640Sariff /* 263193640Sariff * Set ready pointer to indicate that our children are ready 264193640Sariff * to be woken up, also as an interrupt threshold progress 265193640Sariff * indicator. 266193640Sariff */ 267193640Sariff b->rl = 1; 268193640Sariff 269193640Sariff c->flags &= ~CHN_F_DIRTY; 270193640Sariff 271193640Sariff /* 272193640Sariff * Return 0 to bail out early from sndbuf_feed() loop. 273193640Sariff * No need to increase feedcount counter since part of this 274193640Sariff * feeder chains already include feed_root(). 275193640Sariff */ 276193640Sariff return (0); 277193640Sariff} 278193640Sariff 279193640Sariffstatic int 280193640Sarifffeed_mixer_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, 281193640Sariff uint32_t count, void *source) 282193640Sariff{ 283193640Sariff struct feed_mixer_info *info; 284193640Sariff struct snd_dbuf *src = source; 285193640Sariff struct pcm_channel *ch; 286193640Sariff uint32_t cnt, mcnt, rcnt, sz; 287193640Sariff int passthrough; 288193640Sariff uint8_t *tmp; 289193640Sariff 290193640Sariff if (c->direction == PCMDIR_REC) 291193640Sariff return (feed_mixer_rec(c)); 292193640Sariff 293193640Sariff sz = sndbuf_getsize(src); 294193640Sariff if (sz < count) 295193640Sariff count = sz; 296193640Sariff 297193640Sariff info = &feed_mixer_info_tab[FEEDMIXER_INFOIDX(f->data)]; 298193640Sariff sz = info->bps * FEEDMIXER_CHANNELS(f->data); 299193640Sariff count = SND_FXROUND(count, sz); 300193640Sariff if (count < sz) 301193640Sariff return (0); 302193640Sariff 303193640Sariff /* 304193640Sariff * We are going to use our source as a temporary buffer since it's 305193640Sariff * got no other purpose. We obtain our data by traversing the channel 306193640Sariff * list of children and calling mixer function to mix count bytes from 307193640Sariff * each into our destination buffer, b. 308193640Sariff */ 309193640Sariff tmp = sndbuf_getbuf(src); 310193640Sariff rcnt = 0; 311193640Sariff mcnt = 0; 312193640Sariff passthrough = 0; /* 'passthrough' / 'exclusive' marker */ 313193640Sariff 314193640Sariff CHN_FOREACH(ch, c, children.busy) { 315193640Sariff CHN_LOCK(ch); 316193640Sariff if (CHN_STOPPED(ch) || (ch->flags & CHN_F_DIRTY)) { 317193640Sariff CHN_UNLOCK(ch); 318193640Sariff continue; 319193640Sariff } 320193640Sariff#ifdef SND_DEBUG 321193640Sariff if ((c->flags & CHN_F_DIRTY) && VCHAN_SYNC_REQUIRED(ch)) { 322193640Sariff if (vchan_sync(ch) != 0) { 323193640Sariff CHN_UNLOCK(ch); 324193640Sariff continue; 325193640Sariff } 326193640Sariff } 327193640Sariff#endif 328193640Sariff if ((ch->flags & CHN_F_MMAP) && !(ch->flags & CHN_F_CLOSING)) 329193640Sariff sndbuf_acquire(ch->bufsoft, NULL, 330193640Sariff sndbuf_getfree(ch->bufsoft)); 331193640Sariff if (info->mix == NULL) { 332193640Sariff /* 333193640Sariff * Passthrough. Dump the first digital/passthrough 334193640Sariff * channel into destination buffer, and the rest into 335193640Sariff * nothingness (mute effect). 336193640Sariff */ 337193640Sariff if (passthrough == 0 && 338193640Sariff (ch->format & AFMT_PASSTHROUGH)) { 339193640Sariff rcnt = SND_FXROUND(FEEDER_FEED(ch->feeder, ch, 340193640Sariff b, count, ch->bufsoft), sz); 341193640Sariff passthrough = 1; 342193640Sariff } else 343193640Sariff FEEDER_FEED(ch->feeder, ch, tmp, count, 344193640Sariff ch->bufsoft); 345193640Sariff } else if (c->flags & CHN_F_EXCLUSIVE) { 346193640Sariff /* 347193640Sariff * Exclusive. Dump the first 'exclusive' channel into 348193640Sariff * destination buffer, and the rest into nothingness 349193640Sariff * (mute effect). 350193640Sariff */ 351193640Sariff if (passthrough == 0 && (ch->flags & CHN_F_EXCLUSIVE)) { 352193640Sariff rcnt = SND_FXROUND(FEEDER_FEED(ch->feeder, ch, 353193640Sariff b, count, ch->bufsoft), sz); 354193640Sariff passthrough = 1; 355193640Sariff } else 356193640Sariff FEEDER_FEED(ch->feeder, ch, tmp, count, 357193640Sariff ch->bufsoft); 358193640Sariff } else { 359193640Sariff if (rcnt == 0) { 360193640Sariff rcnt = SND_FXROUND(FEEDER_FEED(ch->feeder, ch, 361193640Sariff b, count, ch->bufsoft), sz); 362193640Sariff mcnt = count - rcnt; 363193640Sariff } else { 364193640Sariff cnt = SND_FXROUND(FEEDER_FEED(ch->feeder, ch, 365193640Sariff tmp, count, ch->bufsoft), sz); 366193640Sariff if (cnt != 0) { 367193640Sariff if (mcnt != 0) { 368193640Sariff memset(b + rcnt, 369193640Sariff sndbuf_zerodata( 370193640Sariff f->desc->out), mcnt); 371193640Sariff mcnt = 0; 372193640Sariff } 373193640Sariff info->mix(tmp, b, cnt); 374193640Sariff if (cnt > rcnt) 375193640Sariff rcnt = cnt; 376193640Sariff } 377193640Sariff } 378193640Sariff } 379193640Sariff CHN_UNLOCK(ch); 380193640Sariff } 381193640Sariff 382193640Sariff if (++c->feedcount == 0) 383193640Sariff c->feedcount = 2; 384193640Sariff 385193640Sariff c->flags &= ~CHN_F_DIRTY; 386193640Sariff 387193640Sariff return (rcnt); 388193640Sariff} 389193640Sariff 390193640Sariffstatic struct pcm_feederdesc feeder_mixer_desc[] = { 391193640Sariff { FEEDER_MIXER, 0, 0, 0, 0 }, 392193640Sariff { 0, 0, 0, 0, 0 } 393193640Sariff}; 394193640Sariff 395193640Sariffstatic kobj_method_t feeder_mixer_methods[] = { 396193640Sariff KOBJMETHOD(feeder_init, feed_mixer_init), 397193640Sariff KOBJMETHOD(feeder_set, feed_mixer_set), 398193640Sariff KOBJMETHOD(feeder_feed, feed_mixer_feed), 399193640Sariff KOBJMETHOD_END 400193640Sariff}; 401193640Sariff 402193640SariffFEEDER_DECLARE(feeder_mixer, NULL); 403