1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2008-2009 Ariff Abdullah <ariff@FreeBSD.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #ifdef _KERNEL 30 #ifdef HAVE_KERNEL_OPTION_HEADERS 31 #include "opt_snd.h" 32 #endif 33 #include <dev/sound/pcm/sound.h> 34 #include <dev/sound/pcm/pcm.h> 35 #include <dev/sound/pcm/vchan.h> 36 #include "feeder_if.h" 37 38 #define SND_USE_FXDIV 39 #include "snd_fxdiv_gen.h" 40 41 SND_DECLARE_FILE("$FreeBSD$"); 42 #endif 43 44 #undef SND_FEEDER_MULTIFORMAT 45 #define SND_FEEDER_MULTIFORMAT 1 46 47 typedef void (*feed_mixer_t)(uint8_t *, uint8_t *, uint32_t); 48 49 #define FEEDMIXER_DECLARE(SIGN, BIT, ENDIAN) \ 50 static void \ 51 feed_mixer_##SIGN##BIT##ENDIAN(uint8_t *src, uint8_t *dst, \ 52 uint32_t count) \ 53 { \ 54 intpcm##BIT##_t z; \ 55 intpcm_t x, y; \ 56 \ 57 src += count; \ 58 dst += count; \ 59 \ 60 do { \ 61 src -= PCM_##BIT##_BPS; \ 62 dst -= PCM_##BIT##_BPS; \ 63 count -= PCM_##BIT##_BPS; \ 64 x = PCM_READ_##SIGN##BIT##_##ENDIAN(src); \ 65 y = PCM_READ_##SIGN##BIT##_##ENDIAN(dst); \ 66 z = INTPCM##BIT##_T(x) + y; \ 67 x = PCM_CLAMP_##SIGN##BIT(z); \ 68 _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, x); \ 69 } while (count != 0); \ 70 } 71 72 #if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT) 73 FEEDMIXER_DECLARE(S, 16, LE) 74 FEEDMIXER_DECLARE(S, 32, LE) 75 #endif 76 #if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT) 77 FEEDMIXER_DECLARE(S, 16, BE) 78 FEEDMIXER_DECLARE(S, 32, BE) 79 #endif 80 #ifdef SND_FEEDER_MULTIFORMAT 81 FEEDMIXER_DECLARE(S, 8, NE) 82 FEEDMIXER_DECLARE(S, 24, LE) 83 FEEDMIXER_DECLARE(S, 24, BE) 84 FEEDMIXER_DECLARE(U, 8, NE) 85 FEEDMIXER_DECLARE(U, 16, LE) 86 FEEDMIXER_DECLARE(U, 24, LE) 87 FEEDMIXER_DECLARE(U, 32, LE) 88 FEEDMIXER_DECLARE(U, 16, BE) 89 FEEDMIXER_DECLARE(U, 24, BE) 90 FEEDMIXER_DECLARE(U, 32, BE) 91 #endif 92 93 struct feed_mixer_info { 94 uint32_t format; 95 int bps; 96 feed_mixer_t mix; 97 }; 98 99 #define FEEDMIXER_ENTRY(SIGN, BIT, ENDIAN) \ 100 { \ 101 AFMT_##SIGN##BIT##_##ENDIAN, PCM_##BIT##_BPS, \ 102 feed_mixer_##SIGN##BIT##ENDIAN \ 103 } 104 105 static struct feed_mixer_info feed_mixer_info_tab[] = { 106 FEEDMIXER_ENTRY(S, 8, NE), 107 #if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT) 108 FEEDMIXER_ENTRY(S, 16, LE), 109 FEEDMIXER_ENTRY(S, 32, LE), 110 #endif 111 #if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT) 112 FEEDMIXER_ENTRY(S, 16, BE), 113 FEEDMIXER_ENTRY(S, 32, BE), 114 #endif 115 #ifdef SND_FEEDER_MULTIFORMAT 116 FEEDMIXER_ENTRY(S, 24, LE), 117 FEEDMIXER_ENTRY(S, 24, BE), 118 FEEDMIXER_ENTRY(U, 8, NE), 119 FEEDMIXER_ENTRY(U, 16, LE), 120 FEEDMIXER_ENTRY(U, 24, LE), 121 FEEDMIXER_ENTRY(U, 32, LE), 122 FEEDMIXER_ENTRY(U, 16, BE), 123 FEEDMIXER_ENTRY(U, 24, BE), 124 FEEDMIXER_ENTRY(U, 32, BE), 125 #endif 126 { AFMT_AC3, PCM_16_BPS, NULL }, 127 { AFMT_MU_LAW, PCM_8_BPS, feed_mixer_U8NE }, /* dummy */ 128 { AFMT_A_LAW, PCM_8_BPS, feed_mixer_U8NE } /* dummy */ 129 }; 130 131 #define FEEDMIXER_TAB_SIZE ((int32_t) \ 132 (sizeof(feed_mixer_info_tab) / \ 133 sizeof(feed_mixer_info_tab[0]))) 134 135 #define FEEDMIXER_DATA(i, c) ((void *) \ 136 ((uintptr_t)((((i) & 0x1f) << 7) | \ 137 ((c) & 0x7f)))) 138 #define FEEDMIXER_INFOIDX(d) ((uint32_t)((uintptr_t)(d) >> 7) & 0x1f) 139 #define FEEDMIXER_CHANNELS(d) ((uint32_t)((uintptr_t)(d)) & 0x7f) 140 141 static int 142 feed_mixer_init(struct pcm_feeder *f) 143 { 144 int i; 145 146 if (f->desc->in != f->desc->out) 147 return (EINVAL); 148 149 for (i = 0; i < FEEDMIXER_TAB_SIZE; i++) { 150 if (AFMT_ENCODING(f->desc->in) == 151 feed_mixer_info_tab[i].format) { 152 f->data = 153 FEEDMIXER_DATA(i, AFMT_CHANNEL(f->desc->in)); 154 return (0); 155 } 156 } 157 158 return (EINVAL); 159 } 160 161 static int 162 feed_mixer_set(struct pcm_feeder *f, int what, int value) 163 { 164 165 switch (what) { 166 case FEEDMIXER_CHANNELS: 167 if (value < SND_CHN_MIN || value > SND_CHN_MAX) 168 return (EINVAL); 169 f->data = FEEDMIXER_DATA(FEEDMIXER_INFOIDX(f->data), value); 170 break; 171 default: 172 return (EINVAL); 173 break; 174 } 175 176 return (0); 177 } 178 179 static __inline int 180 feed_mixer_rec(struct pcm_channel *c) 181 { 182 struct pcm_channel *ch; 183 struct snd_dbuf *b, *bs; 184 uint32_t cnt, maxfeed; 185 int rdy; 186 187 /* 188 * Reset ready and moving pointer. We're not using bufsoft 189 * anywhere since its sole purpose is to become the primary 190 * distributor for the recorded buffer and also as an interrupt 191 * threshold progress indicator. 192 */ 193 b = c->bufsoft; 194 b->rp = 0; 195 b->rl = 0; 196 cnt = sndbuf_getsize(b); 197 maxfeed = SND_FXROUND(SND_FXDIV_MAX, sndbuf_getalign(b)); 198 199 do { 200 cnt = FEEDER_FEED(c->feeder->source, c, b->tmpbuf, 201 min(cnt, maxfeed), c->bufhard); 202 if (cnt != 0) { 203 sndbuf_acquire(b, b->tmpbuf, cnt); 204 cnt = sndbuf_getfree(b); 205 } 206 } while (cnt != 0); 207 208 /* Not enough data */ 209 if (b->rl < sndbuf_getalign(b)) { 210 b->rl = 0; 211 return (0); 212 } 213 214 /* 215 * Keep track of ready and moving pointer since we will use 216 * bufsoft over and over again, pretending nothing has happened. 217 */ 218 rdy = b->rl; 219 220 CHN_FOREACH(ch, c, children.busy) { 221 CHN_LOCK(ch); 222 if (CHN_STOPPED(ch) || (ch->flags & CHN_F_DIRTY)) { 223 CHN_UNLOCK(ch); 224 continue; 225 } 226 #ifdef SND_DEBUG 227 if ((c->flags & CHN_F_DIRTY) && VCHAN_SYNC_REQUIRED(ch)) { 228 if (vchan_sync(ch) != 0) { 229 CHN_UNLOCK(ch); 230 continue; 231 } 232 } 233 #endif 234 bs = ch->bufsoft; 235 if (ch->flags & CHN_F_MMAP) 236 sndbuf_dispose(bs, NULL, sndbuf_getready(bs)); 237 cnt = sndbuf_getfree(bs); 238 if (cnt < sndbuf_getalign(bs)) { 239 CHN_UNLOCK(ch); 240 continue; 241 } 242 maxfeed = SND_FXROUND(SND_FXDIV_MAX, sndbuf_getalign(bs)); 243 do { 244 cnt = FEEDER_FEED(ch->feeder, ch, bs->tmpbuf, 245 min(cnt, maxfeed), b); 246 if (cnt != 0) { 247 sndbuf_acquire(bs, bs->tmpbuf, cnt); 248 cnt = sndbuf_getfree(bs); 249 } 250 } while (cnt != 0); 251 /* 252 * Not entirely flushed out... 253 */ 254 if (b->rl != 0) 255 ch->xruns++; 256 CHN_UNLOCK(ch); 257 /* 258 * Rewind buffer position for next virtual channel. 259 */ 260 b->rp = 0; 261 b->rl = rdy; 262 } 263 264 /* 265 * Set ready pointer to indicate that our children are ready 266 * to be woken up, also as an interrupt threshold progress 267 * indicator. 268 */ 269 b->rl = 1; 270 271 c->flags &= ~CHN_F_DIRTY; 272 273 /* 274 * Return 0 to bail out early from sndbuf_feed() loop. 275 * No need to increase feedcount counter since part of this 276 * feeder chains already include feed_root(). 277 */ 278 return (0); 279 } 280 281 static int 282 feed_mixer_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, 283 uint32_t count, void *source) 284 { 285 struct feed_mixer_info *info; 286 struct snd_dbuf *src = source; 287 struct pcm_channel *ch; 288 uint32_t cnt, mcnt, rcnt, sz; 289 int passthrough; 290 uint8_t *tmp; 291 292 if (c->direction == PCMDIR_REC) 293 return (feed_mixer_rec(c)); 294 295 sz = sndbuf_getsize(src); 296 if (sz < count) 297 count = sz; 298 299 info = &feed_mixer_info_tab[FEEDMIXER_INFOIDX(f->data)]; 300 sz = info->bps * FEEDMIXER_CHANNELS(f->data); 301 count = SND_FXROUND(count, sz); 302 if (count < sz) 303 return (0); 304 305 /* 306 * We are going to use our source as a temporary buffer since it's 307 * got no other purpose. We obtain our data by traversing the channel 308 * list of children and calling mixer function to mix count bytes from 309 * each into our destination buffer, b. 310 */ 311 tmp = sndbuf_getbuf(src); 312 rcnt = 0; 313 mcnt = 0; 314 passthrough = 0; /* 'passthrough' / 'exclusive' marker */ 315 316 CHN_FOREACH(ch, c, children.busy) { 317 CHN_LOCK(ch); 318 if (CHN_STOPPED(ch) || (ch->flags & CHN_F_DIRTY)) { 319 CHN_UNLOCK(ch); 320 continue; 321 } 322 #ifdef SND_DEBUG 323 if ((c->flags & CHN_F_DIRTY) && VCHAN_SYNC_REQUIRED(ch)) { 324 if (vchan_sync(ch) != 0) { 325 CHN_UNLOCK(ch); 326 continue; 327 } 328 } 329 #endif 330 if ((ch->flags & CHN_F_MMAP) && !(ch->flags & CHN_F_CLOSING)) 331 sndbuf_acquire(ch->bufsoft, NULL, 332 sndbuf_getfree(ch->bufsoft)); 333 if (info->mix == NULL) { 334 /* 335 * Passthrough. Dump the first digital/passthrough 336 * channel into destination buffer, and the rest into 337 * nothingness (mute effect). 338 */ 339 if (passthrough == 0 && 340 (ch->format & AFMT_PASSTHROUGH)) { 341 rcnt = SND_FXROUND(FEEDER_FEED(ch->feeder, ch, 342 b, count, ch->bufsoft), sz); 343 passthrough = 1; 344 } else 345 FEEDER_FEED(ch->feeder, ch, tmp, count, 346 ch->bufsoft); 347 } else if (c->flags & CHN_F_EXCLUSIVE) { 348 /* 349 * Exclusive. Dump the first 'exclusive' channel into 350 * destination buffer, and the rest into nothingness 351 * (mute effect). 352 */ 353 if (passthrough == 0 && (ch->flags & CHN_F_EXCLUSIVE)) { 354 rcnt = SND_FXROUND(FEEDER_FEED(ch->feeder, ch, 355 b, count, ch->bufsoft), sz); 356 passthrough = 1; 357 } else 358 FEEDER_FEED(ch->feeder, ch, tmp, count, 359 ch->bufsoft); 360 } else { 361 if (rcnt == 0) { 362 rcnt = SND_FXROUND(FEEDER_FEED(ch->feeder, ch, 363 b, count, ch->bufsoft), sz); 364 mcnt = count - rcnt; 365 } else { 366 cnt = SND_FXROUND(FEEDER_FEED(ch->feeder, ch, 367 tmp, count, ch->bufsoft), sz); 368 if (cnt != 0) { 369 if (mcnt != 0) { 370 memset(b + rcnt, 371 sndbuf_zerodata( 372 f->desc->out), mcnt); 373 mcnt = 0; 374 } 375 info->mix(tmp, b, cnt); 376 if (cnt > rcnt) 377 rcnt = cnt; 378 } 379 } 380 } 381 CHN_UNLOCK(ch); 382 } 383 384 if (++c->feedcount == 0) 385 c->feedcount = 2; 386 387 c->flags &= ~CHN_F_DIRTY; 388 389 return (rcnt); 390 } 391 392 static struct pcm_feederdesc feeder_mixer_desc[] = { 393 { FEEDER_MIXER, 0, 0, 0, 0 }, 394 { 0, 0, 0, 0, 0 } 395 }; 396 397 static kobj_method_t feeder_mixer_methods[] = { 398 KOBJMETHOD(feeder_init, feed_mixer_init), 399 KOBJMETHOD(feeder_set, feed_mixer_set), 400 KOBJMETHOD(feeder_feed, feed_mixer_feed), 401 KOBJMETHOD_END 402 }; 403 404 FEEDER_DECLARE(feeder_mixer, NULL); 405