xref: /dflybsd-src/sys/dev/sound/pcm/feeder_matrix.c (revision 799ba435edf825f35aa8f08bc8353a878dca2116)
12a1ad637SFrançois Tigeot /*-
22a1ad637SFrançois Tigeot  * Copyright (c) 2008-2009 Ariff Abdullah <ariff@FreeBSD.org>
32a1ad637SFrançois Tigeot  * All rights reserved.
42a1ad637SFrançois Tigeot  *
52a1ad637SFrançois Tigeot  * Redistribution and use in source and binary forms, with or without
62a1ad637SFrançois Tigeot  * modification, are permitted provided that the following conditions
72a1ad637SFrançois Tigeot  * are met:
82a1ad637SFrançois Tigeot  * 1. Redistributions of source code must retain the above copyright
92a1ad637SFrançois Tigeot  *    notice, this list of conditions and the following disclaimer.
102a1ad637SFrançois Tigeot  * 2. Redistributions in binary form must reproduce the above copyright
112a1ad637SFrançois Tigeot  *    notice, this list of conditions and the following disclaimer in the
122a1ad637SFrançois Tigeot  *    documentation and/or other materials provided with the distribution.
132a1ad637SFrançois Tigeot  *
142a1ad637SFrançois Tigeot  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
152a1ad637SFrançois Tigeot  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
162a1ad637SFrançois Tigeot  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
172a1ad637SFrançois Tigeot  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
182a1ad637SFrançois Tigeot  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
192a1ad637SFrançois Tigeot  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
202a1ad637SFrançois Tigeot  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
212a1ad637SFrançois Tigeot  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
222a1ad637SFrançois Tigeot  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
232a1ad637SFrançois Tigeot  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
242a1ad637SFrançois Tigeot  * SUCH DAMAGE.
252a1ad637SFrançois Tigeot  */
262a1ad637SFrançois Tigeot 
272a1ad637SFrançois Tigeot /*
282a1ad637SFrançois Tigeot  * feeder_matrix: Generic any-to-any channel matrixing. Probably not the
292a1ad637SFrançois Tigeot  *                accurate way of doing things, but it should be fast and
302a1ad637SFrançois Tigeot  *                transparent enough, not to mention capable of handling
312a1ad637SFrançois Tigeot  *                possible non-standard way of multichannel interleaving
322a1ad637SFrançois Tigeot  *                order. In other words, it is tough to break.
332a1ad637SFrançois Tigeot  *
342a1ad637SFrançois Tigeot  * The Good:
352a1ad637SFrançois Tigeot  * + very generic and compact, provided that the supplied matrix map is in a
362a1ad637SFrançois Tigeot  *   sane form.
372a1ad637SFrançois Tigeot  * + should be fast enough.
382a1ad637SFrançois Tigeot  *
392a1ad637SFrançois Tigeot  * The Bad:
402a1ad637SFrançois Tigeot  * + somebody might disagree with it.
412a1ad637SFrançois Tigeot  * + 'matrix' is kind of 0x7a69, due to prolong mental block.
422a1ad637SFrançois Tigeot  */
432a1ad637SFrançois Tigeot 
442a1ad637SFrançois Tigeot #ifdef _KERNEL
452a1ad637SFrançois Tigeot #ifdef HAVE_KERNEL_OPTION_HEADERS
462a1ad637SFrançois Tigeot #include "opt_snd.h"
472a1ad637SFrançois Tigeot #endif
482a1ad637SFrançois Tigeot #include <dev/sound/pcm/sound.h>
492a1ad637SFrançois Tigeot #include <dev/sound/pcm/pcm.h>
502a1ad637SFrançois Tigeot #include "feeder_if.h"
512a1ad637SFrançois Tigeot 
522a1ad637SFrançois Tigeot #define SND_USE_FXDIV
532a1ad637SFrançois Tigeot #include "snd_fxdiv_gen.h"
542a1ad637SFrançois Tigeot 
552a1ad637SFrançois Tigeot SND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/feeder_matrix.c 243138 2012-11-16 07:05:57Z mav $");
562a1ad637SFrançois Tigeot #endif
572a1ad637SFrançois Tigeot 
582a1ad637SFrançois Tigeot #define FEEDMATRIX_RESERVOIR	(SND_CHN_MAX * PCM_32_BPS)
592a1ad637SFrançois Tigeot 
602a1ad637SFrançois Tigeot #define SND_CHN_T_EOF		0x00e0fe0f
612a1ad637SFrançois Tigeot #define SND_CHN_T_NULL		0x0e0e0e0e
622a1ad637SFrançois Tigeot 
632a1ad637SFrançois Tigeot struct feed_matrix_info;
642a1ad637SFrançois Tigeot 
652a1ad637SFrançois Tigeot typedef void (*feed_matrix_t)(struct feed_matrix_info *, uint8_t *,
662a1ad637SFrançois Tigeot     uint8_t *, uint32_t);
672a1ad637SFrançois Tigeot 
682a1ad637SFrançois Tigeot struct feed_matrix_info {
692a1ad637SFrançois Tigeot 	uint32_t bps;
702a1ad637SFrançois Tigeot 	uint32_t ialign, oalign;
712a1ad637SFrançois Tigeot 	uint32_t in, out;
722a1ad637SFrançois Tigeot 	feed_matrix_t apply;
732a1ad637SFrançois Tigeot #ifdef FEEDMATRIX_GENERIC
742a1ad637SFrançois Tigeot 	intpcm_read_t *rd;
752a1ad637SFrançois Tigeot 	intpcm_write_t *wr;
762a1ad637SFrançois Tigeot #endif
772a1ad637SFrançois Tigeot 	struct {
782a1ad637SFrançois Tigeot 		int chn[SND_CHN_T_MAX + 1];
792a1ad637SFrançois Tigeot 		int mul, shift;
802a1ad637SFrançois Tigeot 	} matrix[SND_CHN_T_MAX + 1];
812a1ad637SFrançois Tigeot 	uint8_t reservoir[FEEDMATRIX_RESERVOIR];
822a1ad637SFrançois Tigeot };
832a1ad637SFrançois Tigeot 
842a1ad637SFrançois Tigeot static struct pcmchan_matrix feeder_matrix_maps[SND_CHN_MATRIX_MAX] = {
852a1ad637SFrançois Tigeot 	[SND_CHN_MATRIX_1_0] = SND_CHN_MATRIX_MAP_1_0,
862a1ad637SFrançois Tigeot 	[SND_CHN_MATRIX_2_0] = SND_CHN_MATRIX_MAP_2_0,
872a1ad637SFrançois Tigeot 	[SND_CHN_MATRIX_2_1] = SND_CHN_MATRIX_MAP_2_1,
882a1ad637SFrançois Tigeot 	[SND_CHN_MATRIX_3_0] = SND_CHN_MATRIX_MAP_3_0,
892a1ad637SFrançois Tigeot 	[SND_CHN_MATRIX_3_1] = SND_CHN_MATRIX_MAP_3_1,
902a1ad637SFrançois Tigeot 	[SND_CHN_MATRIX_4_0] = SND_CHN_MATRIX_MAP_4_0,
912a1ad637SFrançois Tigeot 	[SND_CHN_MATRIX_4_1] = SND_CHN_MATRIX_MAP_4_1,
922a1ad637SFrançois Tigeot 	[SND_CHN_MATRIX_5_0] = SND_CHN_MATRIX_MAP_5_0,
932a1ad637SFrançois Tigeot 	[SND_CHN_MATRIX_5_1] = SND_CHN_MATRIX_MAP_5_1,
942a1ad637SFrançois Tigeot 	[SND_CHN_MATRIX_6_0] = SND_CHN_MATRIX_MAP_6_0,
952a1ad637SFrançois Tigeot 	[SND_CHN_MATRIX_6_1] = SND_CHN_MATRIX_MAP_6_1,
962a1ad637SFrançois Tigeot 	[SND_CHN_MATRIX_7_0] = SND_CHN_MATRIX_MAP_7_0,
972a1ad637SFrançois Tigeot 	[SND_CHN_MATRIX_7_1] = SND_CHN_MATRIX_MAP_7_1
982a1ad637SFrançois Tigeot };
992a1ad637SFrançois Tigeot 
1002a1ad637SFrançois Tigeot static int feeder_matrix_default_ids[9] = {
1012a1ad637SFrançois Tigeot 	[0] = SND_CHN_MATRIX_UNKNOWN,
1022a1ad637SFrançois Tigeot 	[1] = SND_CHN_MATRIX_1,
1032a1ad637SFrançois Tigeot 	[2] = SND_CHN_MATRIX_2,
1042a1ad637SFrançois Tigeot 	[3] = SND_CHN_MATRIX_3,
1052a1ad637SFrançois Tigeot 	[4] = SND_CHN_MATRIX_4,
1062a1ad637SFrançois Tigeot 	[5] = SND_CHN_MATRIX_5,
1072a1ad637SFrançois Tigeot 	[6] = SND_CHN_MATRIX_6,
1082a1ad637SFrançois Tigeot 	[7] = SND_CHN_MATRIX_7,
1092a1ad637SFrançois Tigeot 	[8] = SND_CHN_MATRIX_8
1102a1ad637SFrançois Tigeot };
1112a1ad637SFrançois Tigeot 
1122a1ad637SFrançois Tigeot #ifdef _KERNEL
1132a1ad637SFrançois Tigeot #define FEEDMATRIX_CLIP_CHECK(...)
1142a1ad637SFrançois Tigeot #else
1152a1ad637SFrançois Tigeot #define FEEDMATRIX_CLIP_CHECK(v, BIT)	do {				\
1162a1ad637SFrançois Tigeot 	if ((v) < PCM_S##BIT##_MIN || (v) > PCM_S##BIT##_MAX)		\
1172a1ad637SFrançois Tigeot 	    errx(1, "\n\n%s(): Sample clipping: %jd\n",			\
1182a1ad637SFrançois Tigeot 		__func__, (intmax_t)(v));				\
1192a1ad637SFrançois Tigeot } while (0)
1202a1ad637SFrançois Tigeot #endif
1212a1ad637SFrançois Tigeot 
1222a1ad637SFrançois Tigeot #define FEEDMATRIX_DECLARE(SIGN, BIT, ENDIAN)				\
1232a1ad637SFrançois Tigeot static void								\
1242a1ad637SFrançois Tigeot feed_matrix_##SIGN##BIT##ENDIAN(struct feed_matrix_info *info,		\
1252a1ad637SFrançois Tigeot     uint8_t *src, uint8_t *dst, uint32_t count)				\
1262a1ad637SFrançois Tigeot {									\
1272a1ad637SFrançois Tigeot 	intpcm64_t accum;						\
1282a1ad637SFrançois Tigeot 	intpcm_t v;							\
1292a1ad637SFrançois Tigeot 	int i, j;							\
1302a1ad637SFrançois Tigeot 									\
1312a1ad637SFrançois Tigeot 	do {								\
1322a1ad637SFrançois Tigeot 		for (i = 0; info->matrix[i].chn[0] != SND_CHN_T_EOF;	\
1332a1ad637SFrançois Tigeot 		    i++) {						\
1342a1ad637SFrançois Tigeot 			if (info->matrix[i].chn[0] == SND_CHN_T_NULL) {	\
1352a1ad637SFrançois Tigeot 				_PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst,	\
1362a1ad637SFrançois Tigeot 				    0);					\
1372a1ad637SFrançois Tigeot 				dst += PCM_##BIT##_BPS;			\
1382a1ad637SFrançois Tigeot 				continue;				\
1392a1ad637SFrançois Tigeot 			} else if (info->matrix[i].chn[1] ==		\
1402a1ad637SFrançois Tigeot 			    SND_CHN_T_EOF) {				\
1412a1ad637SFrançois Tigeot 				v = _PCM_READ_##SIGN##BIT##_##ENDIAN(	\
1422a1ad637SFrançois Tigeot 				    src + info->matrix[i].chn[0]);	\
1432a1ad637SFrançois Tigeot 				_PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst,	\
1442a1ad637SFrançois Tigeot 				    v);					\
1452a1ad637SFrançois Tigeot 				dst += PCM_##BIT##_BPS;			\
1462a1ad637SFrançois Tigeot 				continue;				\
1472a1ad637SFrançois Tigeot 			}						\
1482a1ad637SFrançois Tigeot 									\
1492a1ad637SFrançois Tigeot 			accum = 0;					\
1502a1ad637SFrançois Tigeot 			for (j = 0;					\
1512a1ad637SFrançois Tigeot 			    info->matrix[i].chn[j] != SND_CHN_T_EOF;	\
1522a1ad637SFrançois Tigeot 			    j++) {					\
1532a1ad637SFrançois Tigeot 				v = _PCM_READ_##SIGN##BIT##_##ENDIAN(	\
1542a1ad637SFrançois Tigeot 				    src + info->matrix[i].chn[j]);	\
1552a1ad637SFrançois Tigeot 				accum += v;				\
1562a1ad637SFrançois Tigeot 			}						\
1572a1ad637SFrançois Tigeot 									\
1582a1ad637SFrançois Tigeot 			accum = (accum * info->matrix[i].mul) >>	\
1592a1ad637SFrançois Tigeot 			    info->matrix[i].shift;			\
1602a1ad637SFrançois Tigeot 									\
1612a1ad637SFrançois Tigeot 			FEEDMATRIX_CLIP_CHECK(accum, BIT);		\
1622a1ad637SFrançois Tigeot 									\
1632a1ad637SFrançois Tigeot 			v = (accum > PCM_S##BIT##_MAX) ?		\
1642a1ad637SFrançois Tigeot 			    PCM_S##BIT##_MAX :				\
1652a1ad637SFrançois Tigeot 			    ((accum < PCM_S##BIT##_MIN) ?		\
1662a1ad637SFrançois Tigeot 			    PCM_S##BIT##_MIN :				\
1672a1ad637SFrançois Tigeot 			    accum);					\
1682a1ad637SFrançois Tigeot 			_PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, v);	\
1692a1ad637SFrançois Tigeot 			dst += PCM_##BIT##_BPS;				\
1702a1ad637SFrançois Tigeot 		}							\
1712a1ad637SFrançois Tigeot 		src += info->ialign;					\
1722a1ad637SFrançois Tigeot 	} while (--count != 0);						\
1732a1ad637SFrançois Tigeot }
1742a1ad637SFrançois Tigeot 
1752a1ad637SFrançois Tigeot #if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
1762a1ad637SFrançois Tigeot FEEDMATRIX_DECLARE(S, 16, LE)
1772a1ad637SFrançois Tigeot FEEDMATRIX_DECLARE(S, 32, LE)
1782a1ad637SFrançois Tigeot #endif
1792a1ad637SFrançois Tigeot #if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
1802a1ad637SFrançois Tigeot FEEDMATRIX_DECLARE(S, 16, BE)
1812a1ad637SFrançois Tigeot FEEDMATRIX_DECLARE(S, 32, BE)
1822a1ad637SFrançois Tigeot #endif
1832a1ad637SFrançois Tigeot #ifdef SND_FEEDER_MULTIFORMAT
1842a1ad637SFrançois Tigeot FEEDMATRIX_DECLARE(S,  8, NE)
1852a1ad637SFrançois Tigeot FEEDMATRIX_DECLARE(S, 24, LE)
1862a1ad637SFrançois Tigeot FEEDMATRIX_DECLARE(S, 24, BE)
1872a1ad637SFrançois Tigeot FEEDMATRIX_DECLARE(U,  8, NE)
1882a1ad637SFrançois Tigeot FEEDMATRIX_DECLARE(U, 16, LE)
1892a1ad637SFrançois Tigeot FEEDMATRIX_DECLARE(U, 24, LE)
1902a1ad637SFrançois Tigeot FEEDMATRIX_DECLARE(U, 32, LE)
1912a1ad637SFrançois Tigeot FEEDMATRIX_DECLARE(U, 16, BE)
1922a1ad637SFrançois Tigeot FEEDMATRIX_DECLARE(U, 24, BE)
1932a1ad637SFrançois Tigeot FEEDMATRIX_DECLARE(U, 32, BE)
1942a1ad637SFrançois Tigeot #endif
1952a1ad637SFrançois Tigeot 
1962a1ad637SFrançois Tigeot #define FEEDMATRIX_ENTRY(SIGN, BIT, ENDIAN)				\
1972a1ad637SFrançois Tigeot 	{								\
1982a1ad637SFrançois Tigeot 		AFMT_##SIGN##BIT##_##ENDIAN,				\
1992a1ad637SFrançois Tigeot 		feed_matrix_##SIGN##BIT##ENDIAN				\
2002a1ad637SFrançois Tigeot 	}
2012a1ad637SFrançois Tigeot 
2022a1ad637SFrançois Tigeot static const struct {
2032a1ad637SFrançois Tigeot 	uint32_t format;
2042a1ad637SFrançois Tigeot 	feed_matrix_t apply;
2052a1ad637SFrançois Tigeot } feed_matrix_tab[] = {
2062a1ad637SFrançois Tigeot #if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
2072a1ad637SFrançois Tigeot 	FEEDMATRIX_ENTRY(S, 16, LE),
2082a1ad637SFrançois Tigeot 	FEEDMATRIX_ENTRY(S, 32, LE),
2092a1ad637SFrançois Tigeot #endif
2102a1ad637SFrançois Tigeot #if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
2112a1ad637SFrançois Tigeot 	FEEDMATRIX_ENTRY(S, 16, BE),
2122a1ad637SFrançois Tigeot 	FEEDMATRIX_ENTRY(S, 32, BE),
2132a1ad637SFrançois Tigeot #endif
2142a1ad637SFrançois Tigeot #ifdef SND_FEEDER_MULTIFORMAT
2152a1ad637SFrançois Tigeot 	FEEDMATRIX_ENTRY(S,  8, NE),
2162a1ad637SFrançois Tigeot 	FEEDMATRIX_ENTRY(S, 24, LE),
2172a1ad637SFrançois Tigeot 	FEEDMATRIX_ENTRY(S, 24, BE),
2182a1ad637SFrançois Tigeot 	FEEDMATRIX_ENTRY(U,  8, NE),
2192a1ad637SFrançois Tigeot 	FEEDMATRIX_ENTRY(U, 16, LE),
2202a1ad637SFrançois Tigeot 	FEEDMATRIX_ENTRY(U, 24, LE),
2212a1ad637SFrançois Tigeot 	FEEDMATRIX_ENTRY(U, 32, LE),
2222a1ad637SFrançois Tigeot 	FEEDMATRIX_ENTRY(U, 16, BE),
2232a1ad637SFrançois Tigeot 	FEEDMATRIX_ENTRY(U, 24, BE),
2242a1ad637SFrançois Tigeot 	FEEDMATRIX_ENTRY(U, 32, BE)
2252a1ad637SFrançois Tigeot #endif
2262a1ad637SFrançois Tigeot };
2272a1ad637SFrançois Tigeot 
2282a1ad637SFrançois Tigeot static void
feed_matrix_reset(struct feed_matrix_info * info)2292a1ad637SFrançois Tigeot feed_matrix_reset(struct feed_matrix_info *info)
2302a1ad637SFrançois Tigeot {
2312a1ad637SFrançois Tigeot 	uint32_t i, j;
2322a1ad637SFrançois Tigeot 
2332a1ad637SFrançois Tigeot 	for (i = 0; i < (sizeof(info->matrix) / sizeof(info->matrix[0])); i++) {
2342a1ad637SFrançois Tigeot 		for (j = 0;
2352a1ad637SFrançois Tigeot 		    j < (sizeof(info->matrix[i].chn) /
2362a1ad637SFrançois Tigeot 		    sizeof(info->matrix[i].chn[0])); j++) {
2372a1ad637SFrançois Tigeot 			info->matrix[i].chn[j] = SND_CHN_T_EOF;
2382a1ad637SFrançois Tigeot 		}
2392a1ad637SFrançois Tigeot 		info->matrix[i].mul   = 1;
2402a1ad637SFrançois Tigeot 		info->matrix[i].shift = 0;
2412a1ad637SFrançois Tigeot 	}
2422a1ad637SFrançois Tigeot }
2432a1ad637SFrançois Tigeot 
2442a1ad637SFrançois Tigeot #ifdef FEEDMATRIX_GENERIC
2452a1ad637SFrançois Tigeot static void
feed_matrix_apply_generic(struct feed_matrix_info * info,uint8_t * src,uint8_t * dst,uint32_t count)2462a1ad637SFrançois Tigeot feed_matrix_apply_generic(struct feed_matrix_info *info,
2472a1ad637SFrançois Tigeot     uint8_t *src, uint8_t *dst, uint32_t count)
2482a1ad637SFrançois Tigeot {
2492a1ad637SFrançois Tigeot 	intpcm64_t accum;
2502a1ad637SFrançois Tigeot 	intpcm_t v;
2512a1ad637SFrançois Tigeot 	int i, j;
2522a1ad637SFrançois Tigeot 
2532a1ad637SFrançois Tigeot 	do {
2542a1ad637SFrançois Tigeot 		for (i = 0; info->matrix[i].chn[0] != SND_CHN_T_EOF;
2552a1ad637SFrançois Tigeot 		    i++) {
2562a1ad637SFrançois Tigeot 			if (info->matrix[i].chn[0] == SND_CHN_T_NULL) {
2572a1ad637SFrançois Tigeot 				info->wr(dst, 0);
2582a1ad637SFrançois Tigeot 				dst += info->bps;
2592a1ad637SFrançois Tigeot 				continue;
2602a1ad637SFrançois Tigeot 			} else if (info->matrix[i].chn[1] ==
2612a1ad637SFrançois Tigeot 			    SND_CHN_T_EOF) {
2622a1ad637SFrançois Tigeot 				v = info->rd(src + info->matrix[i].chn[0]);
2632a1ad637SFrançois Tigeot 				info->wr(dst, v);
2642a1ad637SFrançois Tigeot 				dst += info->bps;
2652a1ad637SFrançois Tigeot 				continue;
2662a1ad637SFrançois Tigeot 			}
2672a1ad637SFrançois Tigeot 
2682a1ad637SFrançois Tigeot 			accum = 0;
2692a1ad637SFrançois Tigeot 			for (j = 0;
2702a1ad637SFrançois Tigeot 			    info->matrix[i].chn[j] != SND_CHN_T_EOF;
2712a1ad637SFrançois Tigeot 			    j++) {
2722a1ad637SFrançois Tigeot 				v = info->rd(src + info->matrix[i].chn[j]);
2732a1ad637SFrançois Tigeot 				accum += v;
2742a1ad637SFrançois Tigeot 			}
2752a1ad637SFrançois Tigeot 
2762a1ad637SFrançois Tigeot 			accum = (accum * info->matrix[i].mul) >>
2772a1ad637SFrançois Tigeot 			    info->matrix[i].shift;
2782a1ad637SFrançois Tigeot 
2792a1ad637SFrançois Tigeot 			FEEDMATRIX_CLIP_CHECK(accum, 32);
2802a1ad637SFrançois Tigeot 
2812a1ad637SFrançois Tigeot 			v = (accum > PCM_S32_MAX) ? PCM_S32_MAX :
2822a1ad637SFrançois Tigeot 			    ((accum < PCM_S32_MIN) ? PCM_S32_MIN : accum);
2832a1ad637SFrançois Tigeot 			info->wr(dst, v);
2842a1ad637SFrançois Tigeot 			dst += info->bps;
2852a1ad637SFrançois Tigeot 		}
2862a1ad637SFrançois Tigeot 		src += info->ialign;
2872a1ad637SFrançois Tigeot 	} while (--count != 0);
2882a1ad637SFrançois Tigeot }
2892a1ad637SFrançois Tigeot #endif
2902a1ad637SFrançois Tigeot 
2912a1ad637SFrançois Tigeot static int
feed_matrix_setup(struct feed_matrix_info * info,struct pcmchan_matrix * m_in,struct pcmchan_matrix * m_out)2922a1ad637SFrançois Tigeot feed_matrix_setup(struct feed_matrix_info *info, struct pcmchan_matrix *m_in,
2932a1ad637SFrançois Tigeot     struct pcmchan_matrix *m_out)
2942a1ad637SFrançois Tigeot {
2952a1ad637SFrançois Tigeot 	uint32_t i, j, ch, in_mask, merge_mask;
2962a1ad637SFrançois Tigeot 	int mul, shift;
2972a1ad637SFrançois Tigeot 
2982a1ad637SFrançois Tigeot 
2992a1ad637SFrançois Tigeot 	if (info == NULL || m_in == NULL || m_out == NULL ||
3002a1ad637SFrançois Tigeot 	    AFMT_CHANNEL(info->in) != m_in->channels ||
3012a1ad637SFrançois Tigeot 	    AFMT_CHANNEL(info->out) != m_out->channels ||
3022a1ad637SFrançois Tigeot 	    m_in->channels < SND_CHN_MIN || m_in->channels > SND_CHN_MAX ||
3032a1ad637SFrançois Tigeot 	    m_out->channels < SND_CHN_MIN || m_out->channels > SND_CHN_MAX)
3042a1ad637SFrançois Tigeot 		return (EINVAL);
3052a1ad637SFrançois Tigeot 
3062a1ad637SFrançois Tigeot 	feed_matrix_reset(info);
3072a1ad637SFrançois Tigeot 
3082a1ad637SFrançois Tigeot 	/*
3092a1ad637SFrançois Tigeot 	 * If both in and out are part of standard matrix and identical, skip
3102a1ad637SFrançois Tigeot 	 * everything alltogether.
3112a1ad637SFrançois Tigeot 	 */
3122a1ad637SFrançois Tigeot 	if (m_in->id == m_out->id && !(m_in->id < SND_CHN_MATRIX_BEGIN ||
3132a1ad637SFrançois Tigeot 	    m_in->id > SND_CHN_MATRIX_END))
3142a1ad637SFrançois Tigeot 		return (0);
3152a1ad637SFrançois Tigeot 
3162a1ad637SFrançois Tigeot 	/*
3172a1ad637SFrançois Tigeot 	 * Special case for mono input matrix. If the output supports
3182a1ad637SFrançois Tigeot 	 * possible 'center' channel, route it there. Otherwise, let it be
3192a1ad637SFrançois Tigeot 	 * matrixed to left/right.
3202a1ad637SFrançois Tigeot 	 */
3212a1ad637SFrançois Tigeot 	if (m_in->id == SND_CHN_MATRIX_1_0) {
3222a1ad637SFrançois Tigeot 		if (m_out->id == SND_CHN_MATRIX_1_0)
3232a1ad637SFrançois Tigeot 			in_mask = SND_CHN_T_MASK_FL;
3242a1ad637SFrançois Tigeot 		else if (m_out->mask & SND_CHN_T_MASK_FC)
3252a1ad637SFrançois Tigeot 			in_mask = SND_CHN_T_MASK_FC;
3262a1ad637SFrançois Tigeot 		else
3272a1ad637SFrançois Tigeot 			in_mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR;
3282a1ad637SFrançois Tigeot 	} else
3292a1ad637SFrançois Tigeot 		in_mask = m_in->mask;
3302a1ad637SFrançois Tigeot 
3312a1ad637SFrançois Tigeot 	/* Merge, reduce, expand all possibilites. */
3322a1ad637SFrançois Tigeot 	for (ch = SND_CHN_T_BEGIN; ch <= SND_CHN_T_END &&
3332a1ad637SFrançois Tigeot 	    m_out->map[ch].type != SND_CHN_T_MAX; ch += SND_CHN_T_STEP) {
3342a1ad637SFrançois Tigeot 		merge_mask = m_out->map[ch].members & in_mask;
3352a1ad637SFrançois Tigeot 		if (merge_mask == 0) {
3362a1ad637SFrançois Tigeot 			info->matrix[ch].chn[0] = SND_CHN_T_NULL;
3372a1ad637SFrançois Tigeot 			continue;
3382a1ad637SFrançois Tigeot 		}
3392a1ad637SFrançois Tigeot 
3402a1ad637SFrançois Tigeot 		j = 0;
3412a1ad637SFrançois Tigeot 		for (i = SND_CHN_T_BEGIN; i <= SND_CHN_T_END;
3422a1ad637SFrançois Tigeot 		    i += SND_CHN_T_STEP) {
3432a1ad637SFrançois Tigeot 			if (merge_mask & (1 << i)) {
3442a1ad637SFrançois Tigeot 				if (m_in->offset[i] >= 0 &&
3452a1ad637SFrançois Tigeot 				    m_in->offset[i] < (int)m_in->channels)
3462a1ad637SFrançois Tigeot 					info->matrix[ch].chn[j++] =
3472a1ad637SFrançois Tigeot 					    m_in->offset[i] * info->bps;
3482a1ad637SFrançois Tigeot 				else {
3492a1ad637SFrançois Tigeot 					info->matrix[ch].chn[j++] =
3502a1ad637SFrançois Tigeot 					    SND_CHN_T_EOF;
3512a1ad637SFrançois Tigeot 					break;
3522a1ad637SFrançois Tigeot 				}
3532a1ad637SFrançois Tigeot 			}
3542a1ad637SFrançois Tigeot 		}
3552a1ad637SFrançois Tigeot 
3562a1ad637SFrançois Tigeot #define FEEDMATRIX_ATTN_SHIFT	16
3572a1ad637SFrançois Tigeot 
3582a1ad637SFrançois Tigeot 		if (j > 1) {
3592a1ad637SFrançois Tigeot 			/*
3602a1ad637SFrançois Tigeot 			 * XXX For channel that require accumulation from
3612a1ad637SFrançois Tigeot 			 * multiple channels, apply a slight attenuation to
3622a1ad637SFrançois Tigeot 			 * avoid clipping.
3632a1ad637SFrançois Tigeot 			 */
3642a1ad637SFrançois Tigeot 			mul   = (1 << (FEEDMATRIX_ATTN_SHIFT - 1)) + 143 - j;
3652a1ad637SFrançois Tigeot 			shift = FEEDMATRIX_ATTN_SHIFT;
3662a1ad637SFrançois Tigeot 			while ((mul & 1) == 0 && shift > 0) {
3672a1ad637SFrançois Tigeot 				mul >>= 1;
3682a1ad637SFrançois Tigeot 				shift--;
3692a1ad637SFrançois Tigeot 			}
3702a1ad637SFrançois Tigeot 			info->matrix[ch].mul   = mul;
3712a1ad637SFrançois Tigeot 			info->matrix[ch].shift = shift;
3722a1ad637SFrançois Tigeot 		}
3732a1ad637SFrançois Tigeot 	}
3742a1ad637SFrançois Tigeot 
3752a1ad637SFrançois Tigeot #ifndef _KERNEL
3762a1ad637SFrançois Tigeot 	fprintf(stderr, "Total: %d\n", ch);
3772a1ad637SFrançois Tigeot 
3782a1ad637SFrançois Tigeot 	for (i = 0; info->matrix[i].chn[0] != SND_CHN_T_EOF; i++) {
3792a1ad637SFrançois Tigeot 		fprintf(stderr, "%d: [", i);
3802a1ad637SFrançois Tigeot 		for (j = 0; info->matrix[i].chn[j] != SND_CHN_T_EOF; j++) {
3812a1ad637SFrançois Tigeot 			if (j != 0)
3822a1ad637SFrançois Tigeot 				fprintf(stderr, ", ");
3832a1ad637SFrançois Tigeot 			fprintf(stderr, "%d",
3842a1ad637SFrançois Tigeot 			    (info->matrix[i].chn[j] == SND_CHN_T_NULL) ?
3852a1ad637SFrançois Tigeot 			    0xffffffff : info->matrix[i].chn[j] / info->bps);
3862a1ad637SFrançois Tigeot 		}
3872a1ad637SFrançois Tigeot 		fprintf(stderr, "] attn: (x * %d) >> %d\n",
3882a1ad637SFrançois Tigeot 		    info->matrix[i].mul, info->matrix[i].shift);
3892a1ad637SFrançois Tigeot 	}
3902a1ad637SFrançois Tigeot #endif
3912a1ad637SFrançois Tigeot 
3922a1ad637SFrançois Tigeot 	return (0);
3932a1ad637SFrançois Tigeot }
3942a1ad637SFrançois Tigeot 
3952a1ad637SFrançois Tigeot static int
feed_matrix_init(struct pcm_feeder * f)3962a1ad637SFrançois Tigeot feed_matrix_init(struct pcm_feeder *f)
3972a1ad637SFrançois Tigeot {
3982a1ad637SFrançois Tigeot 	struct feed_matrix_info *info;
3992a1ad637SFrançois Tigeot 	struct pcmchan_matrix *m_in, *m_out;
4002a1ad637SFrançois Tigeot 	uint32_t i;
4012a1ad637SFrançois Tigeot 	int ret;
4022a1ad637SFrançois Tigeot 
4032a1ad637SFrançois Tigeot 	if (AFMT_ENCODING(f->desc->in) != AFMT_ENCODING(f->desc->out))
4042a1ad637SFrançois Tigeot 		return (EINVAL);
4052a1ad637SFrançois Tigeot 
4064e8e900cSMatthew Dillon 	info = kmalloc(sizeof(*info), M_DEVBUF, M_WAITOK | M_ZERO);
4072a1ad637SFrançois Tigeot 	if (info == NULL)
4082a1ad637SFrançois Tigeot 		return (ENOMEM);
4092a1ad637SFrançois Tigeot 
4102a1ad637SFrançois Tigeot 	info->in = f->desc->in;
4112a1ad637SFrançois Tigeot 	info->out = f->desc->out;
4122a1ad637SFrançois Tigeot 	info->bps = AFMT_BPS(info->in);
4132a1ad637SFrançois Tigeot 	info->ialign = AFMT_ALIGN(info->in);
4142a1ad637SFrançois Tigeot 	info->oalign = AFMT_ALIGN(info->out);
4152a1ad637SFrançois Tigeot 	info->apply = NULL;
4162a1ad637SFrançois Tigeot 
4172a1ad637SFrançois Tigeot 	for (i = 0; info->apply == NULL &&
4182a1ad637SFrançois Tigeot 	    i < (sizeof(feed_matrix_tab) / sizeof(feed_matrix_tab[0])); i++) {
4192a1ad637SFrançois Tigeot 		if (AFMT_ENCODING(info->in) == feed_matrix_tab[i].format)
4202a1ad637SFrançois Tigeot 			info->apply = feed_matrix_tab[i].apply;
4212a1ad637SFrançois Tigeot 	}
4222a1ad637SFrançois Tigeot 
4232a1ad637SFrançois Tigeot 	if (info->apply == NULL) {
4242a1ad637SFrançois Tigeot #ifdef FEEDMATRIX_GENERIC
4252a1ad637SFrançois Tigeot 		info->rd = feeder_format_read_op(info->in);
4262a1ad637SFrançois Tigeot 		info->wr = feeder_format_write_op(info->out);
4272a1ad637SFrançois Tigeot 		if (info->rd == NULL || info->wr == NULL) {
42867931cc4SFrançois Tigeot 			kfree(info, M_DEVBUF);
4292a1ad637SFrançois Tigeot 			return (EINVAL);
4302a1ad637SFrançois Tigeot 		}
4312a1ad637SFrançois Tigeot 		info->apply = feed_matrix_apply_generic;
4322a1ad637SFrançois Tigeot #else
43367931cc4SFrançois Tigeot 		kfree(info, M_DEVBUF);
4342a1ad637SFrançois Tigeot 		return (EINVAL);
4352a1ad637SFrançois Tigeot #endif
4362a1ad637SFrançois Tigeot 	}
4372a1ad637SFrançois Tigeot 
4382a1ad637SFrançois Tigeot 	m_in  = feeder_matrix_format_map(info->in);
4392a1ad637SFrançois Tigeot 	m_out = feeder_matrix_format_map(info->out);
4402a1ad637SFrançois Tigeot 
4412a1ad637SFrançois Tigeot 	ret = feed_matrix_setup(info, m_in, m_out);
4422a1ad637SFrançois Tigeot 	if (ret != 0) {
44367931cc4SFrançois Tigeot 		kfree(info, M_DEVBUF);
4442a1ad637SFrançois Tigeot 		return (ret);
4452a1ad637SFrançois Tigeot 	}
4462a1ad637SFrançois Tigeot 
4472a1ad637SFrançois Tigeot 	f->data = info;
4482a1ad637SFrançois Tigeot 
4492a1ad637SFrançois Tigeot 	return (0);
4502a1ad637SFrançois Tigeot }
4512a1ad637SFrançois Tigeot 
4522a1ad637SFrançois Tigeot static int
feed_matrix_free(struct pcm_feeder * f)4532a1ad637SFrançois Tigeot feed_matrix_free(struct pcm_feeder *f)
4542a1ad637SFrançois Tigeot {
4552a1ad637SFrançois Tigeot 	struct feed_matrix_info *info;
4562a1ad637SFrançois Tigeot 
4572a1ad637SFrançois Tigeot 	info = f->data;
4582a1ad637SFrançois Tigeot 	if (info != NULL)
45967931cc4SFrançois Tigeot 		kfree(info, M_DEVBUF);
4602a1ad637SFrançois Tigeot 
4612a1ad637SFrançois Tigeot 	f->data = NULL;
4622a1ad637SFrançois Tigeot 
4632a1ad637SFrançois Tigeot 	return (0);
4642a1ad637SFrançois Tigeot }
4652a1ad637SFrançois Tigeot 
4662a1ad637SFrançois Tigeot static int
feed_matrix_feed(struct pcm_feeder * f,struct pcm_channel * c,uint8_t * b,uint32_t count,void * source)4672a1ad637SFrançois Tigeot feed_matrix_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
4682a1ad637SFrançois Tigeot     uint32_t count, void *source)
4692a1ad637SFrançois Tigeot {
4702a1ad637SFrançois Tigeot 	struct feed_matrix_info *info;
4712a1ad637SFrançois Tigeot 	uint32_t j, inmax;
4722a1ad637SFrançois Tigeot 	uint8_t *src, *dst;
4732a1ad637SFrançois Tigeot 
4742a1ad637SFrançois Tigeot 	info = f->data;
4752a1ad637SFrançois Tigeot 	if (info->matrix[0].chn[0] == SND_CHN_T_EOF)
4762a1ad637SFrançois Tigeot 		return (FEEDER_FEED(f->source, c, b, count, source));
4772a1ad637SFrançois Tigeot 
4782a1ad637SFrançois Tigeot 	dst = b;
4792a1ad637SFrançois Tigeot 	count = SND_FXROUND(count, info->oalign);
4802a1ad637SFrançois Tigeot 	inmax = info->ialign + info->oalign;
4812a1ad637SFrançois Tigeot 
4822a1ad637SFrançois Tigeot 	/*
4832a1ad637SFrançois Tigeot 	 * This loop might look simmilar to other feeder_* loops, but be
4842a1ad637SFrançois Tigeot 	 * advised: matrixing might involve overlapping (think about
4852a1ad637SFrançois Tigeot 	 * swapping end to front or something like that). In this regard it
4862a1ad637SFrançois Tigeot 	 * might be simmilar to feeder_format, but feeder_format works on
4872a1ad637SFrançois Tigeot 	 * 'sample' domain where it can be fitted into single 32bit integer
4882a1ad637SFrançois Tigeot 	 * while matrixing works on 'sample frame' domain.
4892a1ad637SFrançois Tigeot 	 */
4902a1ad637SFrançois Tigeot 	do {
4912a1ad637SFrançois Tigeot 		if (count < info->oalign)
4922a1ad637SFrançois Tigeot 			break;
4932a1ad637SFrançois Tigeot 
4942a1ad637SFrançois Tigeot 		if (count < inmax) {
4952a1ad637SFrançois Tigeot 			src = info->reservoir;
4962a1ad637SFrançois Tigeot 			j = info->ialign;
4972a1ad637SFrançois Tigeot 		} else {
4982a1ad637SFrançois Tigeot 			if (info->ialign == info->oalign)
4992a1ad637SFrançois Tigeot 				j = count - info->oalign;
5002a1ad637SFrançois Tigeot 			else if (info->ialign > info->oalign)
5012a1ad637SFrançois Tigeot 				j = SND_FXROUND(count - info->oalign,
5022a1ad637SFrançois Tigeot 				    info->ialign);
5032a1ad637SFrançois Tigeot 			else
5042a1ad637SFrançois Tigeot 				j = (SND_FXDIV(count, info->oalign) - 1) *
5052a1ad637SFrançois Tigeot 				    info->ialign;
5062a1ad637SFrançois Tigeot 			src = dst + count - j;
5072a1ad637SFrançois Tigeot 		}
5082a1ad637SFrançois Tigeot 
5092a1ad637SFrançois Tigeot 		j = SND_FXDIV(FEEDER_FEED(f->source, c, src, j, source),
5102a1ad637SFrançois Tigeot 		    info->ialign);
5112a1ad637SFrançois Tigeot 		if (j == 0)
5122a1ad637SFrançois Tigeot 			break;
5132a1ad637SFrançois Tigeot 
5142a1ad637SFrançois Tigeot 		info->apply(info, src, dst, j);
5152a1ad637SFrançois Tigeot 
5162a1ad637SFrançois Tigeot 		j *= info->oalign;
5172a1ad637SFrançois Tigeot 		dst += j;
5182a1ad637SFrançois Tigeot 		count -= j;
5192a1ad637SFrançois Tigeot 
5202a1ad637SFrançois Tigeot 	} while (count != 0);
5212a1ad637SFrançois Tigeot 
5222a1ad637SFrançois Tigeot 	return (dst - b);
5232a1ad637SFrançois Tigeot }
5242a1ad637SFrançois Tigeot 
5252a1ad637SFrançois Tigeot static struct pcm_feederdesc feeder_matrix_desc[] = {
5262a1ad637SFrançois Tigeot 	{ FEEDER_MATRIX, 0, 0, 0, 0 },
5272a1ad637SFrançois Tigeot 	{ 0, 0, 0, 0, 0 }
5282a1ad637SFrançois Tigeot };
5292a1ad637SFrançois Tigeot 
5302a1ad637SFrançois Tigeot static kobj_method_t feeder_matrix_methods[] = {
5312a1ad637SFrançois Tigeot 	KOBJMETHOD(feeder_init,		feed_matrix_init),
5322a1ad637SFrançois Tigeot 	KOBJMETHOD(feeder_free,		feed_matrix_free),
5332a1ad637SFrançois Tigeot 	KOBJMETHOD(feeder_feed,		feed_matrix_feed),
5342a1ad637SFrançois Tigeot 	KOBJMETHOD_END
5352a1ad637SFrançois Tigeot };
5362a1ad637SFrançois Tigeot 
5372a1ad637SFrançois Tigeot FEEDER_DECLARE(feeder_matrix, NULL);
5382a1ad637SFrançois Tigeot 
5392a1ad637SFrançois Tigeot /* External */
5402a1ad637SFrançois Tigeot int
feeder_matrix_setup(struct pcm_feeder * f,struct pcmchan_matrix * m_in,struct pcmchan_matrix * m_out)5412a1ad637SFrançois Tigeot feeder_matrix_setup(struct pcm_feeder *f, struct pcmchan_matrix *m_in,
5422a1ad637SFrançois Tigeot     struct pcmchan_matrix *m_out)
5432a1ad637SFrançois Tigeot {
5442a1ad637SFrançois Tigeot 
5452a1ad637SFrançois Tigeot 	if (f == NULL || f->desc == NULL || f->desc->type != FEEDER_MATRIX ||
5462a1ad637SFrançois Tigeot 	    f->data == NULL)
5472a1ad637SFrançois Tigeot 		return (EINVAL);
5482a1ad637SFrançois Tigeot 
5492a1ad637SFrançois Tigeot 	return (feed_matrix_setup(f->data, m_in, m_out));
5502a1ad637SFrançois Tigeot }
5512a1ad637SFrançois Tigeot 
5522a1ad637SFrançois Tigeot /*
5532a1ad637SFrançois Tigeot  * feeder_matrix_default_id(): For a given number of channels, return
5542a1ad637SFrançois Tigeot  *                             default prefered id (example: both 5.1 and
5552a1ad637SFrançois Tigeot  *                             6.0 are simply 6 channels, but 5.1 is more
5562a1ad637SFrançois Tigeot  *                             preferable).
5572a1ad637SFrançois Tigeot  */
5582a1ad637SFrançois Tigeot int
feeder_matrix_default_id(uint32_t ch)5592a1ad637SFrançois Tigeot feeder_matrix_default_id(uint32_t ch)
5602a1ad637SFrançois Tigeot {
5612a1ad637SFrançois Tigeot 
5622a1ad637SFrançois Tigeot 	if (ch < feeder_matrix_maps[SND_CHN_MATRIX_BEGIN].channels ||
5632a1ad637SFrançois Tigeot 	    ch > feeder_matrix_maps[SND_CHN_MATRIX_END].channels)
5642a1ad637SFrançois Tigeot 		return (SND_CHN_MATRIX_UNKNOWN);
5652a1ad637SFrançois Tigeot 
5662a1ad637SFrançois Tigeot 	return (feeder_matrix_maps[feeder_matrix_default_ids[ch]].id);
5672a1ad637SFrançois Tigeot }
5682a1ad637SFrançois Tigeot 
5692a1ad637SFrançois Tigeot /*
5702a1ad637SFrançois Tigeot  * feeder_matrix_default_channel_map(): Ditto, but return matrix map
5712a1ad637SFrançois Tigeot  *                                      instead.
5722a1ad637SFrançois Tigeot  */
5732a1ad637SFrançois Tigeot struct pcmchan_matrix *
feeder_matrix_default_channel_map(uint32_t ch)5742a1ad637SFrançois Tigeot feeder_matrix_default_channel_map(uint32_t ch)
5752a1ad637SFrançois Tigeot {
5762a1ad637SFrançois Tigeot 
5772a1ad637SFrançois Tigeot 	if (ch < feeder_matrix_maps[SND_CHN_MATRIX_BEGIN].channels ||
5782a1ad637SFrançois Tigeot 	    ch > feeder_matrix_maps[SND_CHN_MATRIX_END].channels)
5792a1ad637SFrançois Tigeot 		return (NULL);
5802a1ad637SFrançois Tigeot 
5812a1ad637SFrançois Tigeot 	return (&feeder_matrix_maps[feeder_matrix_default_ids[ch]]);
5822a1ad637SFrançois Tigeot }
5832a1ad637SFrançois Tigeot 
5842a1ad637SFrançois Tigeot /*
5852a1ad637SFrançois Tigeot  * feeder_matrix_default_format(): For a given audio format, return the
5862a1ad637SFrançois Tigeot  *                                 proper audio format based on preferable
5872a1ad637SFrançois Tigeot  *                                 matrix.
5882a1ad637SFrançois Tigeot  */
5892a1ad637SFrançois Tigeot uint32_t
feeder_matrix_default_format(uint32_t format)5902a1ad637SFrançois Tigeot feeder_matrix_default_format(uint32_t format)
5912a1ad637SFrançois Tigeot {
5922a1ad637SFrançois Tigeot 	struct pcmchan_matrix *m;
5932a1ad637SFrançois Tigeot 	uint32_t i, ch, ext;
5942a1ad637SFrançois Tigeot 
5952a1ad637SFrançois Tigeot 	ch = AFMT_CHANNEL(format);
5962a1ad637SFrançois Tigeot 	ext = AFMT_EXTCHANNEL(format);
5972a1ad637SFrançois Tigeot 
5982a1ad637SFrançois Tigeot 	if (ext != 0) {
5992a1ad637SFrançois Tigeot 		for (i = SND_CHN_MATRIX_BEGIN; i <= SND_CHN_MATRIX_END; i++) {
6002a1ad637SFrançois Tigeot 			if (feeder_matrix_maps[i].channels == ch &&
6012a1ad637SFrançois Tigeot 			    feeder_matrix_maps[i].ext == ext)
6022a1ad637SFrançois Tigeot 			return (SND_FORMAT(format, ch, ext));
6032a1ad637SFrançois Tigeot 		}
6042a1ad637SFrançois Tigeot 	}
6052a1ad637SFrançois Tigeot 
6062a1ad637SFrançois Tigeot 	m = feeder_matrix_default_channel_map(ch);
6072a1ad637SFrançois Tigeot 	if (m == NULL)
6082a1ad637SFrançois Tigeot 		return (0x00000000);
6092a1ad637SFrançois Tigeot 
6102a1ad637SFrançois Tigeot 	return (SND_FORMAT(format, ch, m->ext));
6112a1ad637SFrançois Tigeot }
6122a1ad637SFrançois Tigeot 
6132a1ad637SFrançois Tigeot /*
6142a1ad637SFrançois Tigeot  * feeder_matrix_format_id(): For a given audio format, return its matrix
6152a1ad637SFrançois Tigeot  *                            id.
6162a1ad637SFrançois Tigeot  */
6172a1ad637SFrançois Tigeot int
feeder_matrix_format_id(uint32_t format)6182a1ad637SFrançois Tigeot feeder_matrix_format_id(uint32_t format)
6192a1ad637SFrançois Tigeot {
6202a1ad637SFrançois Tigeot 	uint32_t i, ch, ext;
6212a1ad637SFrançois Tigeot 
6222a1ad637SFrançois Tigeot 	ch = AFMT_CHANNEL(format);
6232a1ad637SFrançois Tigeot 	ext = AFMT_EXTCHANNEL(format);
6242a1ad637SFrançois Tigeot 
6252a1ad637SFrançois Tigeot 	for (i = SND_CHN_MATRIX_BEGIN; i <= SND_CHN_MATRIX_END; i++) {
6262a1ad637SFrançois Tigeot 		if (feeder_matrix_maps[i].channels == ch &&
6272a1ad637SFrançois Tigeot 		    feeder_matrix_maps[i].ext == ext)
6282a1ad637SFrançois Tigeot 			return (feeder_matrix_maps[i].id);
6292a1ad637SFrançois Tigeot 	}
6302a1ad637SFrançois Tigeot 
6312a1ad637SFrançois Tigeot 	return (SND_CHN_MATRIX_UNKNOWN);
6322a1ad637SFrançois Tigeot }
6332a1ad637SFrançois Tigeot 
6342a1ad637SFrançois Tigeot /*
6352a1ad637SFrançois Tigeot  * feeder_matrix_format_map(): For a given audio format, return its matrix
6362a1ad637SFrançois Tigeot  *                             map.
6372a1ad637SFrançois Tigeot  */
6382a1ad637SFrançois Tigeot struct pcmchan_matrix *
feeder_matrix_format_map(uint32_t format)6392a1ad637SFrançois Tigeot feeder_matrix_format_map(uint32_t format)
6402a1ad637SFrançois Tigeot {
6412a1ad637SFrançois Tigeot 	uint32_t i, ch, ext;
6422a1ad637SFrançois Tigeot 
6432a1ad637SFrançois Tigeot 	ch = AFMT_CHANNEL(format);
6442a1ad637SFrançois Tigeot 	ext = AFMT_EXTCHANNEL(format);
6452a1ad637SFrançois Tigeot 
6462a1ad637SFrançois Tigeot 	for (i = SND_CHN_MATRIX_BEGIN; i <= SND_CHN_MATRIX_END; i++) {
6472a1ad637SFrançois Tigeot 		if (feeder_matrix_maps[i].channels == ch &&
6482a1ad637SFrançois Tigeot 		    feeder_matrix_maps[i].ext == ext)
6492a1ad637SFrançois Tigeot 			return (&feeder_matrix_maps[i]);
6502a1ad637SFrançois Tigeot 	}
6512a1ad637SFrançois Tigeot 
6522a1ad637SFrançois Tigeot 	return (NULL);
6532a1ad637SFrançois Tigeot }
6542a1ad637SFrançois Tigeot 
6552a1ad637SFrançois Tigeot /*
6562a1ad637SFrançois Tigeot  * feeder_matrix_id_map(): For a given matrix id, return its matrix map.
6572a1ad637SFrançois Tigeot  */
6582a1ad637SFrançois Tigeot struct pcmchan_matrix *
feeder_matrix_id_map(int id)6592a1ad637SFrançois Tigeot feeder_matrix_id_map(int id)
6602a1ad637SFrançois Tigeot {
6612a1ad637SFrançois Tigeot 
6622a1ad637SFrançois Tigeot 	if (id < SND_CHN_MATRIX_BEGIN || id > SND_CHN_MATRIX_END)
6632a1ad637SFrançois Tigeot 		return (NULL);
6642a1ad637SFrançois Tigeot 
6652a1ad637SFrançois Tigeot 	return (&feeder_matrix_maps[id]);
6662a1ad637SFrançois Tigeot }
6672a1ad637SFrançois Tigeot 
6682a1ad637SFrançois Tigeot /*
6692a1ad637SFrançois Tigeot  * feeder_matrix_compare(): Compare the simmilarities of matrices.
6702a1ad637SFrançois Tigeot  */
6712a1ad637SFrançois Tigeot int
feeder_matrix_compare(struct pcmchan_matrix * m_in,struct pcmchan_matrix * m_out)6722a1ad637SFrançois Tigeot feeder_matrix_compare(struct pcmchan_matrix *m_in, struct pcmchan_matrix *m_out)
6732a1ad637SFrançois Tigeot {
6742a1ad637SFrançois Tigeot 	uint32_t i;
6752a1ad637SFrançois Tigeot 
6762a1ad637SFrançois Tigeot 	if (m_in == m_out)
6772a1ad637SFrançois Tigeot 		return (0);
6782a1ad637SFrançois Tigeot 
6792a1ad637SFrançois Tigeot 	if (m_in->channels != m_out->channels || m_in->ext != m_out->ext ||
6802a1ad637SFrançois Tigeot 	    m_in->mask != m_out->mask)
6812a1ad637SFrançois Tigeot 		return (1);
6822a1ad637SFrançois Tigeot 
6832a1ad637SFrançois Tigeot 	for (i = 0; i < (sizeof(m_in->map) / sizeof(m_in->map[0])); i++) {
6842a1ad637SFrançois Tigeot 		if (m_in->map[i].type != m_out->map[i].type)
6852a1ad637SFrançois Tigeot 			return (1);
6862a1ad637SFrançois Tigeot 		if (m_in->map[i].type == SND_CHN_T_MAX)
6872a1ad637SFrançois Tigeot 			break;
6882a1ad637SFrançois Tigeot 		if (m_in->map[i].members != m_out->map[i].members)
6892a1ad637SFrançois Tigeot 			return (1);
6902a1ad637SFrançois Tigeot 		if (i <= SND_CHN_T_END) {
6912a1ad637SFrançois Tigeot 			if (m_in->offset[m_in->map[i].type] !=
6922a1ad637SFrançois Tigeot 			    m_out->offset[m_out->map[i].type])
6932a1ad637SFrançois Tigeot 				return (1);
6942a1ad637SFrançois Tigeot 		}
6952a1ad637SFrançois Tigeot 	}
6962a1ad637SFrançois Tigeot 
6972a1ad637SFrançois Tigeot 	return (0);
6982a1ad637SFrançois Tigeot }
6992a1ad637SFrançois Tigeot 
7002a1ad637SFrançois Tigeot /*
7012a1ad637SFrançois Tigeot  * XXX 4front intepretation of "surround" is ambigous and sort of
7022a1ad637SFrançois Tigeot  *     conflicting with "rear"/"back". Map it to "side". Well..
7032a1ad637SFrançois Tigeot  *     who cares?
7042a1ad637SFrançois Tigeot  */
7052a1ad637SFrançois Tigeot static int snd_chn_to_oss[SND_CHN_T_MAX] = {
7062a1ad637SFrançois Tigeot 	[SND_CHN_T_FL] = CHID_L,
7072a1ad637SFrançois Tigeot 	[SND_CHN_T_FR] = CHID_R,
7082a1ad637SFrançois Tigeot 	[SND_CHN_T_FC] = CHID_C,
7092a1ad637SFrançois Tigeot 	[SND_CHN_T_LF] = CHID_LFE,
7102a1ad637SFrançois Tigeot 	[SND_CHN_T_SL] = CHID_LS,
7112a1ad637SFrançois Tigeot 	[SND_CHN_T_SR] = CHID_RS,
7122a1ad637SFrançois Tigeot 	[SND_CHN_T_BL] = CHID_LR,
7132a1ad637SFrançois Tigeot 	[SND_CHN_T_BR] = CHID_RR
7142a1ad637SFrançois Tigeot };
7152a1ad637SFrançois Tigeot 
7162a1ad637SFrançois Tigeot #define SND_CHN_OSS_VALIDMASK						\
7172a1ad637SFrançois Tigeot 			(SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR |	\
7182a1ad637SFrançois Tigeot 			 SND_CHN_T_MASK_FC | SND_CHN_T_MASK_LF |	\
7192a1ad637SFrançois Tigeot 			 SND_CHN_T_MASK_SL | SND_CHN_T_MASK_SR |	\
7202a1ad637SFrançois Tigeot 			 SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR)
7212a1ad637SFrançois Tigeot 
7222a1ad637SFrançois Tigeot #define SND_CHN_OSS_MAX		8
7232a1ad637SFrançois Tigeot #define SND_CHN_OSS_BEGIN	CHID_L
7242a1ad637SFrançois Tigeot #define SND_CHN_OSS_END		CHID_RR
7252a1ad637SFrançois Tigeot 
7262a1ad637SFrançois Tigeot static int oss_to_snd_chn[SND_CHN_OSS_END + 1] = {
7272a1ad637SFrançois Tigeot 	[CHID_L]   = SND_CHN_T_FL,
7282a1ad637SFrançois Tigeot 	[CHID_R]   = SND_CHN_T_FR,
7292a1ad637SFrançois Tigeot 	[CHID_C]   = SND_CHN_T_FC,
7302a1ad637SFrançois Tigeot 	[CHID_LFE] = SND_CHN_T_LF,
7312a1ad637SFrançois Tigeot 	[CHID_LS]  = SND_CHN_T_SL,
7322a1ad637SFrançois Tigeot 	[CHID_RS]  = SND_CHN_T_SR,
7332a1ad637SFrançois Tigeot 	[CHID_LR]  = SND_CHN_T_BL,
7342a1ad637SFrançois Tigeot 	[CHID_RR]  = SND_CHN_T_BR
7352a1ad637SFrançois Tigeot };
7362a1ad637SFrançois Tigeot 
7372a1ad637SFrançois Tigeot /*
7382a1ad637SFrançois Tigeot  * Used by SNDCTL_DSP_GET_CHNORDER.
7392a1ad637SFrançois Tigeot  */
7402a1ad637SFrançois Tigeot int
feeder_matrix_oss_get_channel_order(struct pcmchan_matrix * m,unsigned long long * map)7412a1ad637SFrançois Tigeot feeder_matrix_oss_get_channel_order(struct pcmchan_matrix *m,
7422a1ad637SFrançois Tigeot     unsigned long long *map)
7432a1ad637SFrançois Tigeot {
7442a1ad637SFrançois Tigeot 	unsigned long long tmpmap;
7452a1ad637SFrançois Tigeot 	uint32_t i;
7462a1ad637SFrançois Tigeot 
7472a1ad637SFrançois Tigeot 	if (m == NULL || map == NULL || (m->mask & ~SND_CHN_OSS_VALIDMASK) ||
7482a1ad637SFrançois Tigeot 	    m->channels > SND_CHN_OSS_MAX)
7492a1ad637SFrançois Tigeot 		return (EINVAL);
7502a1ad637SFrançois Tigeot 
7512a1ad637SFrançois Tigeot 	tmpmap = 0x0000000000000000ULL;
7522a1ad637SFrançois Tigeot 
753*799ba435SSascha Wildner 	for (i = 0; i < SND_CHN_OSS_MAX &&
754*799ba435SSascha Wildner 	     m->map[i].type != SND_CHN_T_MAX; i++) {
7552a1ad637SFrançois Tigeot 		if ((1 << m->map[i].type) & ~SND_CHN_OSS_VALIDMASK)
7562a1ad637SFrançois Tigeot 			return (EINVAL);
7572a1ad637SFrançois Tigeot 		tmpmap |=
7582a1ad637SFrançois Tigeot 		    (unsigned long long)snd_chn_to_oss[m->map[i].type] <<
7592a1ad637SFrançois Tigeot 		    (i * 4);
7602a1ad637SFrançois Tigeot 	}
7612a1ad637SFrançois Tigeot 
7622a1ad637SFrançois Tigeot 	*map = tmpmap;
7632a1ad637SFrançois Tigeot 
7642a1ad637SFrançois Tigeot 	return (0);
7652a1ad637SFrançois Tigeot }
7662a1ad637SFrançois Tigeot 
7672a1ad637SFrançois Tigeot /*
7682a1ad637SFrançois Tigeot  * Used by SNDCTL_DSP_SET_CHNORDER.
7692a1ad637SFrançois Tigeot  */
7702a1ad637SFrançois Tigeot int
feeder_matrix_oss_set_channel_order(struct pcmchan_matrix * m,unsigned long long * map)7712a1ad637SFrançois Tigeot feeder_matrix_oss_set_channel_order(struct pcmchan_matrix *m,
7722a1ad637SFrançois Tigeot     unsigned long long *map)
7732a1ad637SFrançois Tigeot {
7742a1ad637SFrançois Tigeot 	struct pcmchan_matrix tmp;
7752a1ad637SFrançois Tigeot 	uint32_t chmask, i;
7762a1ad637SFrançois Tigeot 	int ch, cheof;
7772a1ad637SFrançois Tigeot 
7782a1ad637SFrançois Tigeot 	if (m == NULL || map == NULL || (m->mask & ~SND_CHN_OSS_VALIDMASK) ||
7792a1ad637SFrançois Tigeot 	    m->channels > SND_CHN_OSS_MAX || (*map & 0xffffffff00000000ULL))
7802a1ad637SFrançois Tigeot 		return (EINVAL);
7812a1ad637SFrançois Tigeot 
7822a1ad637SFrançois Tigeot 	tmp = *m;
7832a1ad637SFrançois Tigeot 	tmp.channels = 0;
7842a1ad637SFrançois Tigeot 	tmp.ext = 0;
7852a1ad637SFrançois Tigeot 	tmp.mask = 0;
7862a1ad637SFrançois Tigeot 	memset(tmp.offset, -1, sizeof(tmp.offset));
7872a1ad637SFrançois Tigeot 	cheof = 0;
7882a1ad637SFrançois Tigeot 
7892a1ad637SFrançois Tigeot 	for (i = 0; i < SND_CHN_OSS_MAX; i++) {
7902a1ad637SFrançois Tigeot 		ch = (*map >> (i * 4)) & 0xf;
7912a1ad637SFrançois Tigeot 		if (ch < SND_CHN_OSS_BEGIN) {
7922a1ad637SFrançois Tigeot 			if (cheof == 0 && m->map[i].type != SND_CHN_T_MAX)
7932a1ad637SFrançois Tigeot 				return (EINVAL);
7942a1ad637SFrançois Tigeot 			cheof++;
7952a1ad637SFrançois Tigeot 			tmp.map[i] = m->map[i];
7962a1ad637SFrançois Tigeot 			continue;
7972a1ad637SFrançois Tigeot 		} else if (ch > SND_CHN_OSS_END)
7982a1ad637SFrançois Tigeot 			return (EINVAL);
7992a1ad637SFrançois Tigeot 		else if (cheof != 0)
8002a1ad637SFrançois Tigeot 			return (EINVAL);
8012a1ad637SFrançois Tigeot 		ch = oss_to_snd_chn[ch];
8022a1ad637SFrançois Tigeot 		chmask = 1 << ch;
8032a1ad637SFrançois Tigeot 		/* channel not exist in matrix */
8042a1ad637SFrançois Tigeot 		if (!(chmask & m->mask))
8052a1ad637SFrançois Tigeot 			return (EINVAL);
8062a1ad637SFrançois Tigeot 		/* duplicated channel */
8072a1ad637SFrançois Tigeot 		if (chmask & tmp.mask)
8082a1ad637SFrançois Tigeot 			return (EINVAL);
8092a1ad637SFrançois Tigeot 		tmp.map[i] = m->map[m->offset[ch]];
8102a1ad637SFrançois Tigeot 		if (tmp.map[i].type != ch)
8112a1ad637SFrançois Tigeot 			return (EINVAL);
8122a1ad637SFrançois Tigeot 		tmp.offset[ch] = i;
8132a1ad637SFrançois Tigeot 		tmp.mask |= chmask;
8142a1ad637SFrançois Tigeot 		tmp.channels++;
8152a1ad637SFrançois Tigeot 		if (chmask & SND_CHN_T_MASK_LF)
8162a1ad637SFrançois Tigeot 			tmp.ext++;
8172a1ad637SFrançois Tigeot 	}
8182a1ad637SFrançois Tigeot 
8192a1ad637SFrançois Tigeot 	if (tmp.channels != m->channels || tmp.ext != m->ext ||
8202a1ad637SFrançois Tigeot 	    tmp.mask != m->mask ||
8212a1ad637SFrançois Tigeot 	    tmp.map[m->channels].type != SND_CHN_T_MAX)
8222a1ad637SFrançois Tigeot 		return (EINVAL);
8232a1ad637SFrançois Tigeot 
8242a1ad637SFrançois Tigeot 	*m = tmp;
8252a1ad637SFrançois Tigeot 
8262a1ad637SFrançois Tigeot 	return (0);
8272a1ad637SFrançois Tigeot }
828