xref: /dragonfly/sys/dev/sound/pcm/feeder_volume.c (revision 4e8e900c)
1558a398bSSimon Schubert /*-
22a1ad637SFrançois Tigeot  * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org>
3558a398bSSimon Schubert  * All rights reserved.
4558a398bSSimon Schubert  *
5558a398bSSimon Schubert  * Redistribution and use in source and binary forms, with or without
6558a398bSSimon Schubert  * modification, are permitted provided that the following conditions
7558a398bSSimon Schubert  * are met:
8558a398bSSimon Schubert  * 1. Redistributions of source code must retain the above copyright
9558a398bSSimon Schubert  *    notice, this list of conditions and the following disclaimer.
10558a398bSSimon Schubert  * 2. Redistributions in binary form must reproduce the above copyright
11558a398bSSimon Schubert  *    notice, this list of conditions and the following disclaimer in the
12558a398bSSimon Schubert  *    documentation and/or other materials provided with the distribution.
13558a398bSSimon Schubert  *
14558a398bSSimon Schubert  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15558a398bSSimon Schubert  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16558a398bSSimon Schubert  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17558a398bSSimon Schubert  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18558a398bSSimon Schubert  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19558a398bSSimon Schubert  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20558a398bSSimon Schubert  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21558a398bSSimon Schubert  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22558a398bSSimon Schubert  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23558a398bSSimon Schubert  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24558a398bSSimon Schubert  * SUCH DAMAGE.
25558a398bSSimon Schubert  */
26558a398bSSimon Schubert 
272a1ad637SFrançois Tigeot /* feeder_volume, a long 'Lost Technology' rather than a new feature. */
282a1ad637SFrançois Tigeot 
292a1ad637SFrançois Tigeot #ifdef _KERNEL
302a1ad637SFrançois Tigeot #ifdef HAVE_KERNEL_OPTION_HEADERS
312a1ad637SFrançois Tigeot #include "opt_snd.h"
322a1ad637SFrançois Tigeot #endif
33558a398bSSimon Schubert #include <dev/sound/pcm/sound.h>
342a1ad637SFrançois Tigeot #include <dev/sound/pcm/pcm.h>
35558a398bSSimon Schubert #include "feeder_if.h"
36558a398bSSimon Schubert 
372a1ad637SFrançois Tigeot #define SND_USE_FXDIV
382a1ad637SFrançois Tigeot #include "snd_fxdiv_gen.h"
392a1ad637SFrançois Tigeot 
402a1ad637SFrançois Tigeot SND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/feeder_volume.c 193640 2009-06-07 19:12:08Z ariff $");
412a1ad637SFrançois Tigeot #endif
422a1ad637SFrançois Tigeot 
432a1ad637SFrançois Tigeot typedef void (*feed_volume_t)(int *, int *, uint32_t, uint8_t *, uint32_t);
442a1ad637SFrançois Tigeot 
452a1ad637SFrançois Tigeot #define FEEDVOLUME_CALC8(s, v)	(SND_VOL_CALC_SAMPLE((intpcm_t)		\
462a1ad637SFrançois Tigeot 				 (s) << 8, v) >> 8)
472a1ad637SFrançois Tigeot #define FEEDVOLUME_CALC16(s, v)	SND_VOL_CALC_SAMPLE((intpcm_t)(s), v)
482a1ad637SFrançois Tigeot #define FEEDVOLUME_CALC24(s, v)	SND_VOL_CALC_SAMPLE((intpcm64_t)(s), v)
492a1ad637SFrançois Tigeot #define FEEDVOLUME_CALC32(s, v)	SND_VOL_CALC_SAMPLE((intpcm64_t)(s), v)
502a1ad637SFrançois Tigeot 
512a1ad637SFrançois Tigeot #define FEEDVOLUME_DECLARE(SIGN, BIT, ENDIAN)				\
522a1ad637SFrançois Tigeot static void								\
532a1ad637SFrançois Tigeot feed_volume_##SIGN##BIT##ENDIAN(int *vol, int *matrix,			\
542a1ad637SFrançois Tigeot     uint32_t channels, uint8_t *dst, uint32_t count)			\
552a1ad637SFrançois Tigeot {									\
562a1ad637SFrançois Tigeot 	intpcm##BIT##_t v;						\
572a1ad637SFrançois Tigeot 	intpcm_t x;							\
582a1ad637SFrançois Tigeot 	uint32_t i;							\
592a1ad637SFrançois Tigeot 									\
602a1ad637SFrançois Tigeot 	dst += count * PCM_##BIT##_BPS * channels;			\
612a1ad637SFrançois Tigeot 	do {								\
622a1ad637SFrançois Tigeot 		i = channels;						\
632a1ad637SFrançois Tigeot 		do {							\
642a1ad637SFrançois Tigeot 			dst -= PCM_##BIT##_BPS;				\
652a1ad637SFrançois Tigeot 			i--;						\
662a1ad637SFrançois Tigeot 			x = PCM_READ_##SIGN##BIT##_##ENDIAN(dst);	\
672a1ad637SFrançois Tigeot 			v = FEEDVOLUME_CALC##BIT(x, vol[matrix[i]]);	\
682a1ad637SFrançois Tigeot 			x = PCM_CLAMP_##SIGN##BIT(v);			\
692a1ad637SFrançois Tigeot 			_PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, x);	\
702a1ad637SFrançois Tigeot 		} while (i != 0);					\
712a1ad637SFrançois Tigeot 	} while (--count != 0);						\
722a1ad637SFrançois Tigeot }
732a1ad637SFrançois Tigeot 
742a1ad637SFrançois Tigeot #if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
752a1ad637SFrançois Tigeot FEEDVOLUME_DECLARE(S, 16, LE)
762a1ad637SFrançois Tigeot FEEDVOLUME_DECLARE(S, 32, LE)
772a1ad637SFrançois Tigeot #endif
782a1ad637SFrançois Tigeot #if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
792a1ad637SFrançois Tigeot FEEDVOLUME_DECLARE(S, 16, BE)
802a1ad637SFrançois Tigeot FEEDVOLUME_DECLARE(S, 32, BE)
812a1ad637SFrançois Tigeot #endif
822a1ad637SFrançois Tigeot #ifdef SND_FEEDER_MULTIFORMAT
832a1ad637SFrançois Tigeot FEEDVOLUME_DECLARE(S,  8, NE)
842a1ad637SFrançois Tigeot FEEDVOLUME_DECLARE(S, 24, LE)
852a1ad637SFrançois Tigeot FEEDVOLUME_DECLARE(S, 24, BE)
862a1ad637SFrançois Tigeot FEEDVOLUME_DECLARE(U,  8, NE)
872a1ad637SFrançois Tigeot FEEDVOLUME_DECLARE(U, 16, LE)
882a1ad637SFrançois Tigeot FEEDVOLUME_DECLARE(U, 24, LE)
892a1ad637SFrançois Tigeot FEEDVOLUME_DECLARE(U, 32, LE)
902a1ad637SFrançois Tigeot FEEDVOLUME_DECLARE(U, 16, BE)
912a1ad637SFrançois Tigeot FEEDVOLUME_DECLARE(U, 24, BE)
922a1ad637SFrançois Tigeot FEEDVOLUME_DECLARE(U, 32, BE)
932a1ad637SFrançois Tigeot #endif
942a1ad637SFrançois Tigeot 
952a1ad637SFrançois Tigeot struct feed_volume_info {
962a1ad637SFrançois Tigeot 	uint32_t bps, channels;
972a1ad637SFrançois Tigeot 	feed_volume_t apply;
982a1ad637SFrançois Tigeot 	int volume_class;
992a1ad637SFrançois Tigeot 	int state;
1002a1ad637SFrançois Tigeot 	int matrix[SND_CHN_MAX];
1012a1ad637SFrançois Tigeot };
1022a1ad637SFrançois Tigeot 
1032a1ad637SFrançois Tigeot #define FEEDVOLUME_ENTRY(SIGN, BIT, ENDIAN)				\
1042a1ad637SFrançois Tigeot 	{								\
1052a1ad637SFrançois Tigeot 		AFMT_##SIGN##BIT##_##ENDIAN,				\
1062a1ad637SFrançois Tigeot 		feed_volume_##SIGN##BIT##ENDIAN				\
1072a1ad637SFrançois Tigeot 	}
1082a1ad637SFrançois Tigeot 
1092a1ad637SFrançois Tigeot static const struct {
1102a1ad637SFrançois Tigeot 	uint32_t format;
1112a1ad637SFrançois Tigeot 	feed_volume_t apply;
1122a1ad637SFrançois Tigeot } feed_volume_info_tab[] = {
1132a1ad637SFrançois Tigeot #if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
1142a1ad637SFrançois Tigeot 	FEEDVOLUME_ENTRY(S, 16, LE),
1152a1ad637SFrançois Tigeot 	FEEDVOLUME_ENTRY(S, 32, LE),
1162a1ad637SFrançois Tigeot #endif
1172a1ad637SFrançois Tigeot #if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
1182a1ad637SFrançois Tigeot 	FEEDVOLUME_ENTRY(S, 16, BE),
1192a1ad637SFrançois Tigeot 	FEEDVOLUME_ENTRY(S, 32, BE),
1202a1ad637SFrançois Tigeot #endif
1212a1ad637SFrançois Tigeot #ifdef SND_FEEDER_MULTIFORMAT
1222a1ad637SFrançois Tigeot 	FEEDVOLUME_ENTRY(S,  8, NE),
1232a1ad637SFrançois Tigeot 	FEEDVOLUME_ENTRY(S, 24, LE),
1242a1ad637SFrançois Tigeot 	FEEDVOLUME_ENTRY(S, 24, BE),
1252a1ad637SFrançois Tigeot 	FEEDVOLUME_ENTRY(U,  8, NE),
1262a1ad637SFrançois Tigeot 	FEEDVOLUME_ENTRY(U, 16, LE),
1272a1ad637SFrançois Tigeot 	FEEDVOLUME_ENTRY(U, 24, LE),
1282a1ad637SFrançois Tigeot 	FEEDVOLUME_ENTRY(U, 32, LE),
1292a1ad637SFrançois Tigeot 	FEEDVOLUME_ENTRY(U, 16, BE),
1302a1ad637SFrançois Tigeot 	FEEDVOLUME_ENTRY(U, 24, BE),
1312a1ad637SFrançois Tigeot 	FEEDVOLUME_ENTRY(U, 32, BE)
1322a1ad637SFrançois Tigeot #endif
1332a1ad637SFrançois Tigeot };
1342a1ad637SFrançois Tigeot 
1352a1ad637SFrançois Tigeot #define FEEDVOLUME_TAB_SIZE	((int32_t)				\
1362a1ad637SFrançois Tigeot 				 (sizeof(feed_volume_info_tab) /	\
1372a1ad637SFrançois Tigeot 				  sizeof(feed_volume_info_tab[0])))
138558a398bSSimon Schubert 
139558a398bSSimon Schubert static int
feed_volume_init(struct pcm_feeder * f)1402a1ad637SFrançois Tigeot feed_volume_init(struct pcm_feeder *f)
1412a1ad637SFrançois Tigeot {
1422a1ad637SFrançois Tigeot 	struct feed_volume_info *info;
1432a1ad637SFrançois Tigeot 	struct pcmchan_matrix *m;
1442a1ad637SFrançois Tigeot 	uint32_t i;
1452a1ad637SFrançois Tigeot 	int ret;
1462a1ad637SFrançois Tigeot 
1472a1ad637SFrançois Tigeot 	if (f->desc->in != f->desc->out ||
1482a1ad637SFrançois Tigeot 	    AFMT_CHANNEL(f->desc->in) > SND_CHN_MAX)
1492a1ad637SFrançois Tigeot 		return (EINVAL);
1502a1ad637SFrançois Tigeot 
1512a1ad637SFrançois Tigeot 	for (i = 0; i < FEEDVOLUME_TAB_SIZE; i++) {
1522a1ad637SFrançois Tigeot 		if (AFMT_ENCODING(f->desc->in) ==
1532a1ad637SFrançois Tigeot 		    feed_volume_info_tab[i].format) {
15467931cc4SFrançois Tigeot 			info = kmalloc(sizeof(*info), M_DEVBUF,
155*4e8e900cSMatthew Dillon 				       M_WAITOK | M_ZERO);
1562a1ad637SFrançois Tigeot 			if (info == NULL)
1572a1ad637SFrançois Tigeot 				return (ENOMEM);
1582a1ad637SFrançois Tigeot 
1592a1ad637SFrançois Tigeot 			info->bps = AFMT_BPS(f->desc->in);
1602a1ad637SFrançois Tigeot 			info->channels = AFMT_CHANNEL(f->desc->in);
1612a1ad637SFrançois Tigeot 			info->apply = feed_volume_info_tab[i].apply;
1622a1ad637SFrançois Tigeot 			info->volume_class = SND_VOL_C_PCM;
1632a1ad637SFrançois Tigeot 			info->state = FEEDVOLUME_ENABLE;
1642a1ad637SFrançois Tigeot 
1652a1ad637SFrançois Tigeot 			f->data = info;
1662a1ad637SFrançois Tigeot 			m = feeder_matrix_default_channel_map(info->channels);
1672a1ad637SFrançois Tigeot 			if (m == NULL) {
16867931cc4SFrançois Tigeot 				kfree(info, M_DEVBUF);
1692a1ad637SFrançois Tigeot 				return (EINVAL);
1702a1ad637SFrançois Tigeot 			}
1712a1ad637SFrançois Tigeot 
1722a1ad637SFrançois Tigeot 			ret = feeder_volume_apply_matrix(f, m);
1732a1ad637SFrançois Tigeot 			if (ret != 0)
17467931cc4SFrançois Tigeot 				kfree(info, M_DEVBUF);
1752a1ad637SFrançois Tigeot 
1762a1ad637SFrançois Tigeot 			return (ret);
1772a1ad637SFrançois Tigeot 		}
1782a1ad637SFrançois Tigeot 	}
1792a1ad637SFrançois Tigeot 
1802a1ad637SFrançois Tigeot 	return (EINVAL);
1812a1ad637SFrançois Tigeot }
1822a1ad637SFrançois Tigeot 
1832a1ad637SFrançois Tigeot static int
feed_volume_free(struct pcm_feeder * f)1842a1ad637SFrançois Tigeot feed_volume_free(struct pcm_feeder *f)
1852a1ad637SFrançois Tigeot {
1862a1ad637SFrançois Tigeot 	struct feed_volume_info *info;
1872a1ad637SFrançois Tigeot 
1882a1ad637SFrançois Tigeot 	info = f->data;
1892a1ad637SFrançois Tigeot 	if (info != NULL)
19067931cc4SFrançois Tigeot 		kfree(info, M_DEVBUF);
1912a1ad637SFrançois Tigeot 
1922a1ad637SFrançois Tigeot 	f->data = NULL;
1932a1ad637SFrançois Tigeot 
1942a1ad637SFrançois Tigeot 	return (0);
1952a1ad637SFrançois Tigeot }
1962a1ad637SFrançois Tigeot 
1972a1ad637SFrançois Tigeot static int
feed_volume_set(struct pcm_feeder * f,int what,int value)1982a1ad637SFrançois Tigeot feed_volume_set(struct pcm_feeder *f, int what, int value)
1992a1ad637SFrançois Tigeot {
2002a1ad637SFrançois Tigeot 	struct feed_volume_info *info;
2012a1ad637SFrançois Tigeot 	struct pcmchan_matrix *m;
2022a1ad637SFrançois Tigeot 	int ret;
2032a1ad637SFrançois Tigeot 
2042a1ad637SFrançois Tigeot 	info = f->data;
2052a1ad637SFrançois Tigeot 	ret = 0;
2062a1ad637SFrançois Tigeot 
2072a1ad637SFrançois Tigeot 	switch (what) {
2082a1ad637SFrançois Tigeot 	case FEEDVOLUME_CLASS:
2092a1ad637SFrançois Tigeot 		if (value < SND_VOL_C_BEGIN || value > SND_VOL_C_END)
2102a1ad637SFrançois Tigeot 			return (EINVAL);
2112a1ad637SFrançois Tigeot 		info->volume_class = value;
2122a1ad637SFrançois Tigeot 		break;
2132a1ad637SFrançois Tigeot 	case FEEDVOLUME_CHANNELS:
2142a1ad637SFrançois Tigeot 		if (value < SND_CHN_MIN || value > SND_CHN_MAX)
2152a1ad637SFrançois Tigeot 			return (EINVAL);
2162a1ad637SFrançois Tigeot 		m = feeder_matrix_default_channel_map(value);
2172a1ad637SFrançois Tigeot 		if (m == NULL)
2182a1ad637SFrançois Tigeot 			return (EINVAL);
2192a1ad637SFrançois Tigeot 		ret = feeder_volume_apply_matrix(f, m);
2202a1ad637SFrançois Tigeot 		break;
2212a1ad637SFrançois Tigeot 	case FEEDVOLUME_STATE:
2222a1ad637SFrançois Tigeot 		if (!(value == FEEDVOLUME_ENABLE || value == FEEDVOLUME_BYPASS))
2232a1ad637SFrançois Tigeot 			return (EINVAL);
2242a1ad637SFrançois Tigeot 		info->state = value;
2252a1ad637SFrançois Tigeot 		break;
2262a1ad637SFrançois Tigeot 	default:
2272a1ad637SFrançois Tigeot 		return (EINVAL);
2282a1ad637SFrançois Tigeot 		break;
2292a1ad637SFrançois Tigeot 	}
2302a1ad637SFrançois Tigeot 
2312a1ad637SFrançois Tigeot 	return (ret);
2322a1ad637SFrançois Tigeot }
2332a1ad637SFrançois Tigeot 
2342a1ad637SFrançois Tigeot static int
feed_volume_feed(struct pcm_feeder * f,struct pcm_channel * c,uint8_t * b,uint32_t count,void * source)2352a1ad637SFrançois Tigeot feed_volume_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
236558a398bSSimon Schubert     uint32_t count, void *source)
237558a398bSSimon Schubert {
2382a1ad637SFrançois Tigeot 	struct feed_volume_info *info;
2392a1ad637SFrançois Tigeot 	uint32_t j, align;
2402a1ad637SFrançois Tigeot 	int i, *vol, *matrix;
2412a1ad637SFrançois Tigeot 	uint8_t *dst;
242558a398bSSimon Schubert 
2432a1ad637SFrançois Tigeot 	/*
2442a1ad637SFrançois Tigeot 	 * Fetch filter data operation.
2452a1ad637SFrançois Tigeot 	 */
2462a1ad637SFrançois Tigeot 	info = f->data;
2472a1ad637SFrançois Tigeot 
2482a1ad637SFrançois Tigeot 	if (info->state == FEEDVOLUME_BYPASS)
2492a1ad637SFrançois Tigeot 		return (FEEDER_FEED(f->source, c, b, count, source));
2502a1ad637SFrançois Tigeot 
2512a1ad637SFrançois Tigeot 	vol = c->volume[SND_VOL_C_VAL(info->volume_class)];
2522a1ad637SFrançois Tigeot 	matrix = info->matrix;
2532a1ad637SFrançois Tigeot 
2542a1ad637SFrançois Tigeot 	/*
2552a1ad637SFrançois Tigeot 	 * First, let see if we really need to apply gain at all.
2562a1ad637SFrançois Tigeot 	 */
2572a1ad637SFrançois Tigeot 	j = 0;
2582a1ad637SFrançois Tigeot 	i = info->channels;
2592a1ad637SFrançois Tigeot 	do {
2602a1ad637SFrançois Tigeot 		if (vol[matrix[--i]] != SND_VOL_FLAT) {
2612a1ad637SFrançois Tigeot 			j = 1;
2622a1ad637SFrançois Tigeot 			break;
263558a398bSSimon Schubert 		}
2642a1ad637SFrançois Tigeot 	} while (i != 0);
2652a1ad637SFrançois Tigeot 
2662a1ad637SFrançois Tigeot 	/* Nope, just bypass entirely. */
2672a1ad637SFrançois Tigeot 	if (j == 0)
2682a1ad637SFrançois Tigeot 		return (FEEDER_FEED(f->source, c, b, count, source));
2692a1ad637SFrançois Tigeot 
2702a1ad637SFrançois Tigeot 	dst = b;
2712a1ad637SFrançois Tigeot 	align = info->bps * info->channels;
2722a1ad637SFrançois Tigeot 
2732a1ad637SFrançois Tigeot 	do {
2742a1ad637SFrançois Tigeot 		if (count < align)
2752a1ad637SFrançois Tigeot 			break;
2762a1ad637SFrançois Tigeot 
2772a1ad637SFrançois Tigeot 		j = SND_FXDIV(FEEDER_FEED(f->source, c, dst, count, source),
2782a1ad637SFrançois Tigeot 		    align);
2792a1ad637SFrançois Tigeot 		if (j == 0)
2802a1ad637SFrançois Tigeot 			break;
2812a1ad637SFrançois Tigeot 
2822a1ad637SFrançois Tigeot 		info->apply(vol, matrix, info->channels, dst, j);
2832a1ad637SFrançois Tigeot 
2842a1ad637SFrançois Tigeot 		j *= align;
2852a1ad637SFrançois Tigeot 		dst += j;
2862a1ad637SFrançois Tigeot 		count -= j;
2872a1ad637SFrançois Tigeot 
2882a1ad637SFrançois Tigeot 	} while (count != 0);
2892a1ad637SFrançois Tigeot 
2902a1ad637SFrançois Tigeot 	return (dst - b);
291558a398bSSimon Schubert }
292558a398bSSimon Schubert 
2932a1ad637SFrançois Tigeot static struct pcm_feederdesc feeder_volume_desc[] = {
2942a1ad637SFrançois Tigeot 	{ FEEDER_VOLUME, 0, 0, 0, 0 },
2952a1ad637SFrançois Tigeot 	{ 0, 0, 0, 0, 0 }
296558a398bSSimon Schubert };
2972a1ad637SFrançois Tigeot 
2982a1ad637SFrançois Tigeot static kobj_method_t feeder_volume_methods[] = {
2992a1ad637SFrançois Tigeot 	KOBJMETHOD(feeder_init,		feed_volume_init),
3002a1ad637SFrançois Tigeot 	KOBJMETHOD(feeder_free,		feed_volume_free),
3012a1ad637SFrançois Tigeot 	KOBJMETHOD(feeder_set,		feed_volume_set),
3022a1ad637SFrançois Tigeot 	KOBJMETHOD(feeder_feed,		feed_volume_feed),
3037774cda2SSascha Wildner 	KOBJMETHOD_END
304558a398bSSimon Schubert };
3052a1ad637SFrançois Tigeot 
3062a1ad637SFrançois Tigeot FEEDER_DECLARE(feeder_volume, NULL);
3072a1ad637SFrançois Tigeot 
3082a1ad637SFrançois Tigeot /* Extern */
3092a1ad637SFrançois Tigeot 
3102a1ad637SFrançois Tigeot /*
3112a1ad637SFrançois Tigeot  * feeder_volume_apply_matrix(): For given matrix map, apply its configuration
3122a1ad637SFrançois Tigeot  *                               to feeder_volume matrix structure. There are
3132a1ad637SFrançois Tigeot  *                               possibilites that feeder_volume be inserted
3142a1ad637SFrançois Tigeot  *                               before or after feeder_matrix, which in this
3152a1ad637SFrançois Tigeot  *                               case feeder_volume must be in a good terms
3162a1ad637SFrançois Tigeot  *                               with _current_ matrix.
3172a1ad637SFrançois Tigeot  */
3182a1ad637SFrançois Tigeot int
feeder_volume_apply_matrix(struct pcm_feeder * f,struct pcmchan_matrix * m)3192a1ad637SFrançois Tigeot feeder_volume_apply_matrix(struct pcm_feeder *f, struct pcmchan_matrix *m)
3202a1ad637SFrançois Tigeot {
3212a1ad637SFrançois Tigeot 	struct feed_volume_info *info;
3222a1ad637SFrançois Tigeot 	uint32_t i;
3232a1ad637SFrançois Tigeot 
3242a1ad637SFrançois Tigeot 	if (f == NULL || f->desc == NULL || f->desc->type != FEEDER_VOLUME ||
3252a1ad637SFrançois Tigeot 	    f->data == NULL || m == NULL || m->channels < SND_CHN_MIN ||
3262a1ad637SFrançois Tigeot 	    m->channels > SND_CHN_MAX)
3272a1ad637SFrançois Tigeot 		return (EINVAL);
3282a1ad637SFrançois Tigeot 
3292a1ad637SFrançois Tigeot 	info = f->data;
3302a1ad637SFrançois Tigeot 
3312a1ad637SFrançois Tigeot 	for (i = 0; i < (sizeof(info->matrix) / sizeof(info->matrix[0])); i++) {
3322a1ad637SFrançois Tigeot 		if (i < m->channels)
3332a1ad637SFrançois Tigeot 			info->matrix[i] = m->map[i].type;
3342a1ad637SFrançois Tigeot 		else
3352a1ad637SFrançois Tigeot 			info->matrix[i] = SND_CHN_T_FL;
3362a1ad637SFrançois Tigeot 	}
3372a1ad637SFrançois Tigeot 
3382a1ad637SFrançois Tigeot 	info->channels = m->channels;
3392a1ad637SFrançois Tigeot 
3402a1ad637SFrançois Tigeot 	return (0);
3412a1ad637SFrançois Tigeot }
342