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