12061Sjkh/*-
250479Speter * SPDX-License-Identifier: BSD-2-Clause
32061Sjkh *
438666Sjb * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org>
532427Sjb * All rights reserved.
6111131Sru *
7111131Sru * Redistribution and use in source and binary forms, with or without
8217733Sbz * modification, are permitted provided that the following conditions
9217733Sbz * are met:
1038666Sjb * 1. Redistributions of source code must retain the above copyright
1138666Sjb *    notice, this list of conditions and the following disclaimer.
1238666Sjb * 2. Redistributions in binary form must reproduce the above copyright
13159363Strhodes *    notice, this list of conditions and the following disclaimer in the
1464049Salex *    documentation and/or other materials provided with the distribution.
1564049Salex *
16116679Ssimokawa * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1766071Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18116679Ssimokawa * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1973504Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20204661Simp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21232907Sjmallett * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22158962Snetchild * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23223148Sru * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24169597Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25169597Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26169597Sdes * SUCH DAMAGE.
27169597Sdes */
28231821Spluknet
29169597Sdes/* feeder_volume, a long 'Lost Technology' rather than a new feature. */
30169597Sdes
31169597Sdes#ifdef _KERNEL
32217815Sbz#ifdef HAVE_KERNEL_OPTION_HEADERS
33217815Sbz#include "opt_snd.h"
34218524Sjhb#endif
35264811Sbrueffer#include <dev/sound/pcm/sound.h>
36264811Sbrueffer#include <dev/sound/pcm/pcm.h>
37264811Sbrueffer#include "feeder_if.h"
38264811Sbrueffer
39253002Salfred#define SND_USE_FXDIV
40253002Salfred#include "snd_fxdiv_gen.h"
41253002Salfred#endif
42253002Salfred
43253002Salfredtypedef void (*feed_volume_t)(int *, int *, uint32_t, uint8_t *, uint32_t);
44253003Salfred
4532427Sjb#define FEEDVOLUME_CALC8(s, v)	(SND_VOL_CALC_SAMPLE((intpcm_t)		\
4638666Sjb				 (s) << 8, v) >> 8)
47108451Sschweikh#define FEEDVOLUME_CALC16(s, v)	SND_VOL_CALC_SAMPLE((intpcm_t)(s), v)
4838666Sjb#define FEEDVOLUME_CALC24(s, v)	SND_VOL_CALC_SAMPLE((intpcm64_t)(s), v)
4938666Sjb#define FEEDVOLUME_CALC32(s, v)	SND_VOL_CALC_SAMPLE((intpcm64_t)(s), v)
5038666Sjb
5138666Sjb#define FEEDVOLUME_DECLARE(SIGN, BIT, ENDIAN)				\
5217308Speterstatic void								\
53217273Simpfeed_volume_##SIGN##BIT##ENDIAN(int *vol, int *matrix,			\
54217294Simp    uint32_t channels, uint8_t *dst, uint32_t count)			\
5519175Sbde{									\
5696205Sjwd	intpcm##BIT##_t v;						\
57217297Simp	intpcm_t x;							\
58217297Simp	uint32_t i;							\
5938042Sbde									\
6096205Sjwd	dst += count * PCM_##BIT##_BPS * channels;			\
6196205Sjwd	do {								\
6238042Sbde		i = channels;						\
6396205Sjwd		do {							\
64159363Strhodes			dst -= PCM_##BIT##_BPS;				\
65159363Strhodes			i--;						\
6617308Speter			x = PCM_READ_##SIGN##BIT##_##ENDIAN(dst);	\
6796205Sjwd			v = FEEDVOLUME_CALC##BIT(x, vol[matrix[i]]);	\
6896205Sjwd			x = PCM_CLAMP_##SIGN##BIT(v);			\
6917308Speter			_PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, x);	\
70148330Snetchild		} while (i != 0);					\
71148330Snetchild	} while (--count != 0);						\
72148330Snetchild}
73148330Snetchild
74159831Sobrien#if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
75148330SnetchildFEEDVOLUME_DECLARE(S, 16, LE)
76148330SnetchildFEEDVOLUME_DECLARE(S, 32, LE)
77148330Snetchild#endif
78251107Screes#if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
79251107ScreesFEEDVOLUME_DECLARE(S, 16, BE)
80148330SnetchildFEEDVOLUME_DECLARE(S, 32, BE)
81148330Snetchild#endif
8296205Sjwd#ifdef SND_FEEDER_MULTIFORMAT
8396205SjwdFEEDVOLUME_DECLARE(S,  8, NE)
8496205SjwdFEEDVOLUME_DECLARE(S, 24, LE)
85162147SruFEEDVOLUME_DECLARE(S, 24, BE)
86162147SruFEEDVOLUME_DECLARE(U,  8, NE)
8798723SdillonFEEDVOLUME_DECLARE(U, 16, LE)
8898723SdillonFEEDVOLUME_DECLARE(U, 24, LE)
8998723SdillonFEEDVOLUME_DECLARE(U, 32, LE)
9038666SjbFEEDVOLUME_DECLARE(U, 16, BE)
9138666SjbFEEDVOLUME_DECLARE(U, 24, BE)
9217308SpeterFEEDVOLUME_DECLARE(U, 32, BE)
93123311Speter#endif
94123311Speter
95123311Speterstruct feed_volume_info {
96123311Speter	uint32_t bps, channels;
97175833Sjhb	feed_volume_t apply;
98175833Sjhb	int volume_class;
99169597Sdes	int state;
100169597Sdes	int matrix[SND_CHN_MAX];
101169597Sdes};
102169597Sdes
103219177Snwhitehorn#define FEEDVOLUME_ENTRY(SIGN, BIT, ENDIAN)				\
104219177Snwhitehorn	{								\
105238051Sobrien		AFMT_##SIGN##BIT##_##ENDIAN,				\
106219177Snwhitehorn		feed_volume_##SIGN##BIT##ENDIAN				\
107219177Snwhitehorn	}
108158962Snetchild
109156840Srustatic const struct {
110123311Speter	uint32_t format;
111137288Speter	feed_volume_t apply;
112209128Sraj} feed_volume_info_tab[] = {
113209128Sraj#if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
114156740Sru	FEEDVOLUME_ENTRY(S, 16, LE),
1152061Sjkh	FEEDVOLUME_ENTRY(S, 32, LE),
11697769Sru#endif
11797252Sru#if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
118119579Sru	FEEDVOLUME_ENTRY(S, 16, BE),
11997252Sru	FEEDVOLUME_ENTRY(S, 32, BE),
12095730Sru#endif
12195793Sru#ifdef SND_FEEDER_MULTIFORMAT
122111617Sru	FEEDVOLUME_ENTRY(S,  8, NE),
12395730Sru	FEEDVOLUME_ENTRY(S, 24, LE),
124116679Ssimokawa	FEEDVOLUME_ENTRY(S, 24, BE),
12595730Sru	FEEDVOLUME_ENTRY(U,  8, NE),
126116679Ssimokawa	FEEDVOLUME_ENTRY(U, 16, LE),
12795730Sru	FEEDVOLUME_ENTRY(U, 24, LE),
128110035Sru	FEEDVOLUME_ENTRY(U, 32, LE),
129107516Sru	FEEDVOLUME_ENTRY(U, 16, BE),
130138921Sru	FEEDVOLUME_ENTRY(U, 24, BE),
131156145Syar	FEEDVOLUME_ENTRY(U, 32, BE)
132138921Sru#endif
133133942Sru};
134133942Sru
135156145Syar#define FEEDVOLUME_TAB_SIZE	((int32_t)				\
136133942Sru				 (sizeof(feed_volume_info_tab) /	\
137253616Ssjg				  sizeof(feed_volume_info_tab[0])))
138253616Ssjg
139253616Ssjgstatic int
140253616Ssjgfeed_volume_init(struct pcm_feeder *f)
141253616Ssjg{
142253616Ssjg	struct feed_volume_info *info;
143253616Ssjg	struct pcmchan_matrix *m;
144253616Ssjg	uint32_t i;
145253616Ssjg	int ret;
146253616Ssjg
147253616Ssjg	if (f->desc->in != f->desc->out ||
148253616Ssjg	    AFMT_CHANNEL(f->desc->in) > SND_CHN_MAX)
149253616Ssjg		return (EINVAL);
150253616Ssjg
151253616Ssjg	for (i = 0; i < FEEDVOLUME_TAB_SIZE; i++) {
152253616Ssjg		if (AFMT_ENCODING(f->desc->in) ==
153253616Ssjg		    feed_volume_info_tab[i].format) {
154253616Ssjg			info = malloc(sizeof(*info), M_DEVBUF,
155253616Ssjg			    M_NOWAIT | M_ZERO);
156253616Ssjg			if (info == NULL)
157253616Ssjg				return (ENOMEM);
158253616Ssjg
159117229Sru			info->bps = AFMT_BPS(f->desc->in);
160253616Ssjg			info->channels = AFMT_CHANNEL(f->desc->in);
161253616Ssjg			info->apply = feed_volume_info_tab[i].apply;
162253616Ssjg			info->volume_class = SND_VOL_C_PCM;
16354324Smarcel			info->state = FEEDVOLUME_ENABLE;
164253616Ssjg
165253616Ssjg			f->data = info;
166218130Simp			m = feeder_matrix_default_channel_map(info->channels);
167218130Simp			if (m == NULL) {
168233644Sjmallett				free(info, M_DEVBUF);
169218130Simp				return (EINVAL);
170218130Simp			}
171239272Sgonzo
172218130Simp			ret = feeder_volume_apply_matrix(f, m);
173233644Sjmallett			if (ret != 0)
174218130Simp				free(info, M_DEVBUF);
175233644Sjmallett
176233644Sjmallett			return (ret);
177233644Sjmallett		}
178218130Simp	}
179233644Sjmallett
180233644Sjmallett	return (EINVAL);
181218130Simp}
182218130Simp
183218130Simpstatic int
184218130Simpfeed_volume_free(struct pcm_feeder *f)
185218130Simp{
186218130Simp	struct feed_volume_info *info;
187218130Simp
188218130Simp	info = f->data;
189218130Simp	if (info != NULL)
190218130Simp		free(info, M_DEVBUF);
191218130Simp
192218130Simp	f->data = NULL;
193218130Simp
194218130Simp	return (0);
195218130Simp}
196218130Simp
19717308Speterstatic int
198119519Smarcelfeed_volume_set(struct pcm_feeder *f, int what, int value)
199119519Smarcel{
200119519Smarcel	struct feed_volume_info *info;
201119519Smarcel	struct pcmchan_matrix *m;
202119519Smarcel	int ret;
203119519Smarcel
204119579Sru	info = f->data;
205119519Smarcel	ret = 0;
206119519Smarcel
207119519Smarcel	switch (what) {
208119519Smarcel	case FEEDVOLUME_CLASS:
209119519Smarcel		if (value < SND_VOL_C_BEGIN || value > SND_VOL_C_END)
210126031Sgad			return (EINVAL);
211126024Sgad		info->volume_class = value;
212126024Sgad		break;
213126024Sgad	case FEEDVOLUME_CHANNELS:
214126024Sgad		if (value < SND_CHN_MIN || value > SND_CHN_MAX)
215126024Sgad			return (EINVAL);
216126024Sgad		m = feeder_matrix_default_channel_map(value);
217126024Sgad		if (m == NULL)
218227771Sgjb			return (EINVAL);
219126024Sgad		ret = feeder_volume_apply_matrix(f, m);
220126024Sgad		break;
221227769Sgjb	case FEEDVOLUME_STATE:
222227771Sgjb		if (!(value == FEEDVOLUME_ENABLE || value == FEEDVOLUME_BYPASS))
223227771Sgjb			return (EINVAL);
224126024Sgad		info->state = value;
225126024Sgad		break;
226126031Sgad	default:
227126024Sgad		return (EINVAL);
228126024Sgad		break;
229126024Sgad	}
230172744Sdelphij
231126024Sgad	return (ret);
232126024Sgad}
233126024Sgad
234133376Shartistatic int
235126024Sgadfeed_volume_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
236126024Sgad    uint32_t count, void *source)
237172744Sdelphij{
238126024Sgad	int temp_vol[SND_CHN_T_VOL_MAX];
239126024Sgad	struct feed_volume_info *info;
240125885Sgad	uint32_t j, align;
241125885Sgad	int i, *matrix;
24238666Sjb	uint8_t *dst;
24317308Speter	const int16_t *vol;
244119519Smarcel	const int8_t *muted;
245251750Ssjg
246251750Ssjg	/*
247251750Ssjg	 * Fetch filter data operation.
248251750Ssjg	 */
249254417Ssjg	info = f->data;
250251750Ssjg
251251750Ssjg	if (info->state == FEEDVOLUME_BYPASS)
252119579Sru		return (FEEDER_FEED(f->source, c, b, count, source));
253218206Simp
2542302Spaul	vol = c->volume[SND_VOL_C_VAL(info->volume_class)];
255265449Sbrooks	muted = c->muted[SND_VOL_C_VAL(info->volume_class)];
256265449Sbrooks	matrix = info->matrix;
257265449Sbrooks
258265449Sbrooks	/*
259265449Sbrooks	 * First, let see if we really need to apply gain at all.
26039206Sjkh	 */
261265449Sbrooks	j = 0;
262265449Sbrooks	i = info->channels;
263265449Sbrooks	while (i--) {
264265449Sbrooks		if (vol[matrix[i]] != SND_VOL_FLAT ||
265265449Sbrooks		    muted[matrix[i]] != 0) {
266265449Sbrooks			j = 1;
267133945Sru			break;
268240403Sobrien		}
269177609Sru	}
270177609Sru
271177609Sru	/* Nope, just bypass entirely. */
272133945Sru	if (j == 0)
273132358Smarkm		return (FEEDER_FEED(f->source, c, b, count, source));
27417308Speter
27554324Smarcel	/* Check if any controls are muted. */
27654324Smarcel	for (j = 0; j != SND_CHN_T_VOL_MAX; j++)
277132234Smarcel		temp_vol[j] = muted[j] ? 0 : vol[j];
278132234Smarcel
279132234Smarcel	dst = b;
280132234Smarcel	align = info->bps * info->channels;
28154324Smarcel
28254324Smarcel	do {
28354324Smarcel		if (count < align)
284118531Sru			break;
28554324Smarcel
28654324Smarcel		j = SND_FXDIV(FEEDER_FEED(f->source, c, dst, count, source),
28754324Smarcel		    align);
28854324Smarcel		if (j == 0)
28954324Smarcel			break;
29054324Smarcel
291133376Sharti		info->apply(temp_vol, matrix, info->channels, dst, j);
29254324Smarcel
293133376Sharti		j *= align;
294133376Sharti		dst += j;
29554324Smarcel		count -= j;
29654324Smarcel
29754324Smarcel	} while (count != 0);
29854324Smarcel
29954324Smarcel	return (dst - b);
300133376Sharti}
30154324Smarcel
30254324Smarcelstatic struct pcm_feederdesc feeder_volume_desc[] = {
30354324Smarcel	{ FEEDER_VOLUME, 0, 0, 0, 0 },
304118531Sru	{ 0, 0, 0, 0, 0 }
305118531Sru};
30654324Smarcel
307132234Smarcelstatic kobj_method_t feeder_volume_methods[] = {
308132234Smarcel	KOBJMETHOD(feeder_init,		feed_volume_init),
309132234Smarcel	KOBJMETHOD(feeder_free,		feed_volume_free),
310132234Smarcel	KOBJMETHOD(feeder_set,		feed_volume_set),
311132234Smarcel	KOBJMETHOD(feeder_feed,		feed_volume_feed),
312132588Skensmith	KOBJMETHOD_END
313132358Smarkm};
314132234Smarcel
315132358SmarkmFEEDER_DECLARE(feeder_volume, NULL);
316132234Smarcel
317132234Smarcel/* Extern */
318132234Smarcel
31954324Smarcel/*
32054324Smarcel * feeder_volume_apply_matrix(): For given matrix map, apply its configuration
32195730Sru *                               to feeder_volume matrix structure. There are
32295730Sru *                               possibilites that feeder_volume be inserted
32395730Sru *                               before or after feeder_matrix, which in this
32495730Sru *                               case feeder_volume must be in a good terms
32595730Sru *                               with _current_ matrix.
32695730Sru */
32795730Sruint
32838666Sjbfeeder_volume_apply_matrix(struct pcm_feeder *f, struct pcmchan_matrix *m)
329107374Sru{
33017308Speter	struct feed_volume_info *info;
331253616Ssjg	uint32_t i;
332253616Ssjg
333253616Ssjg	if (f == NULL || f->desc == NULL || f->desc->type != FEEDER_VOLUME ||
33455678Smarcel	    f->data == NULL || m == NULL || m->channels < SND_CHN_MIN ||
335253616Ssjg	    m->channels > SND_CHN_MAX)
336253616Ssjg		return (EINVAL);
337253616Ssjg
338143032Sharti	info = f->data;
339138515Sharti
340117793Sru	for (i = 0; i < nitems(info->matrix); i++) {
341110035Sru		if (i < m->channels)
342174564Simp			info->matrix[i] = m->map[i].type;
343110035Sru		else
344241298Smarcel			info->matrix[i] = SND_CHN_T_FL;
3452061Sjkh	}
34617308Speter
347107516Sru	info->channels = m->channels;
348174539Simp
349174539Simp	return (0);
35055678Smarcel}
351253616Ssjg