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