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
41SND_DECLARE_FILE("$FreeBSD$");
42#endif
43
44#undef SND_FEEDER_MULTIFORMAT
45#define SND_FEEDER_MULTIFORMAT	1
46
47typedef void (*feed_mixer_t)(uint8_t *, uint8_t *, uint32_t);
48
49#define FEEDMIXER_DECLARE(SIGN, BIT, ENDIAN)				\
50static void								\
51feed_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)
73FEEDMIXER_DECLARE(S, 16, LE)
74FEEDMIXER_DECLARE(S, 32, LE)
75#endif
76#if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
77FEEDMIXER_DECLARE(S, 16, BE)
78FEEDMIXER_DECLARE(S, 32, BE)
79#endif
80#ifdef SND_FEEDER_MULTIFORMAT
81FEEDMIXER_DECLARE(S,  8, NE)
82FEEDMIXER_DECLARE(S, 24, LE)
83FEEDMIXER_DECLARE(S, 24, BE)
84FEEDMIXER_DECLARE(U,  8, NE)
85FEEDMIXER_DECLARE(U, 16, LE)
86FEEDMIXER_DECLARE(U, 24, LE)
87FEEDMIXER_DECLARE(U, 32, LE)
88FEEDMIXER_DECLARE(U, 16, BE)
89FEEDMIXER_DECLARE(U, 24, BE)
90FEEDMIXER_DECLARE(U, 32, BE)
91#endif
92
93struct 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
105static 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
141static int
142feed_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
161static int
162feed_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
179static __inline int
180feed_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
281static int
282feed_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
392static struct pcm_feederdesc feeder_mixer_desc[] = {
393	{ FEEDER_MIXER, 0, 0, 0, 0 },
394	{ 0, 0, 0, 0, 0 }
395};
396
397static 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
404FEEDER_DECLARE(feeder_mixer, NULL);
405