xref: /dflybsd-src/sys/dev/sound/pcm/feeder_matrix.c (revision 2a1ad637466621af45d5a17185b33f3dcaaa1b1c)
1*2a1ad637SFrançois Tigeot /*-
2*2a1ad637SFrançois Tigeot  * Copyright (c) 2008-2009 Ariff Abdullah <ariff@FreeBSD.org>
3*2a1ad637SFrançois Tigeot  * All rights reserved.
4*2a1ad637SFrançois Tigeot  *
5*2a1ad637SFrançois Tigeot  * Redistribution and use in source and binary forms, with or without
6*2a1ad637SFrançois Tigeot  * modification, are permitted provided that the following conditions
7*2a1ad637SFrançois Tigeot  * are met:
8*2a1ad637SFrançois Tigeot  * 1. Redistributions of source code must retain the above copyright
9*2a1ad637SFrançois Tigeot  *    notice, this list of conditions and the following disclaimer.
10*2a1ad637SFrançois Tigeot  * 2. Redistributions in binary form must reproduce the above copyright
11*2a1ad637SFrançois Tigeot  *    notice, this list of conditions and the following disclaimer in the
12*2a1ad637SFrançois Tigeot  *    documentation and/or other materials provided with the distribution.
13*2a1ad637SFrançois Tigeot  *
14*2a1ad637SFrançois Tigeot  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15*2a1ad637SFrançois Tigeot  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16*2a1ad637SFrançois Tigeot  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17*2a1ad637SFrançois Tigeot  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18*2a1ad637SFrançois Tigeot  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19*2a1ad637SFrançois Tigeot  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20*2a1ad637SFrançois Tigeot  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21*2a1ad637SFrançois Tigeot  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22*2a1ad637SFrançois Tigeot  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23*2a1ad637SFrançois Tigeot  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24*2a1ad637SFrançois Tigeot  * SUCH DAMAGE.
25*2a1ad637SFrançois Tigeot  */
26*2a1ad637SFrançois Tigeot 
27*2a1ad637SFrançois Tigeot /*
28*2a1ad637SFrançois Tigeot  * feeder_matrix: Generic any-to-any channel matrixing. Probably not the
29*2a1ad637SFrançois Tigeot  *                accurate way of doing things, but it should be fast and
30*2a1ad637SFrançois Tigeot  *                transparent enough, not to mention capable of handling
31*2a1ad637SFrançois Tigeot  *                possible non-standard way of multichannel interleaving
32*2a1ad637SFrançois Tigeot  *                order. In other words, it is tough to break.
33*2a1ad637SFrançois Tigeot  *
34*2a1ad637SFrançois Tigeot  * The Good:
35*2a1ad637SFrançois Tigeot  * + very generic and compact, provided that the supplied matrix map is in a
36*2a1ad637SFrançois Tigeot  *   sane form.
37*2a1ad637SFrançois Tigeot  * + should be fast enough.
38*2a1ad637SFrançois Tigeot  *
39*2a1ad637SFrançois Tigeot  * The Bad:
40*2a1ad637SFrançois Tigeot  * + somebody might disagree with it.
41*2a1ad637SFrançois Tigeot  * + 'matrix' is kind of 0x7a69, due to prolong mental block.
42*2a1ad637SFrançois Tigeot  */
43*2a1ad637SFrançois Tigeot 
44*2a1ad637SFrançois Tigeot #ifdef _KERNEL
45*2a1ad637SFrançois Tigeot #ifdef HAVE_KERNEL_OPTION_HEADERS
46*2a1ad637SFrançois Tigeot #include "opt_snd.h"
47*2a1ad637SFrançois Tigeot #endif
48*2a1ad637SFrançois Tigeot #include <dev/sound/pcm/sound.h>
49*2a1ad637SFrançois Tigeot #include <dev/sound/pcm/pcm.h>
50*2a1ad637SFrançois Tigeot #include "feeder_if.h"
51*2a1ad637SFrançois Tigeot 
52*2a1ad637SFrançois Tigeot #define SND_USE_FXDIV
53*2a1ad637SFrançois Tigeot #include "snd_fxdiv_gen.h"
54*2a1ad637SFrançois Tigeot 
55*2a1ad637SFrançois Tigeot SND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/feeder_matrix.c 243138 2012-11-16 07:05:57Z mav $");
56*2a1ad637SFrançois Tigeot #endif
57*2a1ad637SFrançois Tigeot 
58*2a1ad637SFrançois Tigeot #define FEEDMATRIX_RESERVOIR	(SND_CHN_MAX * PCM_32_BPS)
59*2a1ad637SFrançois Tigeot 
60*2a1ad637SFrançois Tigeot #define SND_CHN_T_EOF		0x00e0fe0f
61*2a1ad637SFrançois Tigeot #define SND_CHN_T_NULL		0x0e0e0e0e
62*2a1ad637SFrançois Tigeot 
63*2a1ad637SFrançois Tigeot struct feed_matrix_info;
64*2a1ad637SFrançois Tigeot 
65*2a1ad637SFrançois Tigeot typedef void (*feed_matrix_t)(struct feed_matrix_info *, uint8_t *,
66*2a1ad637SFrançois Tigeot     uint8_t *, uint32_t);
67*2a1ad637SFrançois Tigeot 
68*2a1ad637SFrançois Tigeot struct feed_matrix_info {
69*2a1ad637SFrançois Tigeot 	uint32_t bps;
70*2a1ad637SFrançois Tigeot 	uint32_t ialign, oalign;
71*2a1ad637SFrançois Tigeot 	uint32_t in, out;
72*2a1ad637SFrançois Tigeot 	feed_matrix_t apply;
73*2a1ad637SFrançois Tigeot #ifdef FEEDMATRIX_GENERIC
74*2a1ad637SFrançois Tigeot 	intpcm_read_t *rd;
75*2a1ad637SFrançois Tigeot 	intpcm_write_t *wr;
76*2a1ad637SFrançois Tigeot #endif
77*2a1ad637SFrançois Tigeot 	struct {
78*2a1ad637SFrançois Tigeot 		int chn[SND_CHN_T_MAX + 1];
79*2a1ad637SFrançois Tigeot 		int mul, shift;
80*2a1ad637SFrançois Tigeot 	} matrix[SND_CHN_T_MAX + 1];
81*2a1ad637SFrançois Tigeot 	uint8_t reservoir[FEEDMATRIX_RESERVOIR];
82*2a1ad637SFrançois Tigeot };
83*2a1ad637SFrançois Tigeot 
84*2a1ad637SFrançois Tigeot static struct pcmchan_matrix feeder_matrix_maps[SND_CHN_MATRIX_MAX] = {
85*2a1ad637SFrançois Tigeot 	[SND_CHN_MATRIX_1_0] = SND_CHN_MATRIX_MAP_1_0,
86*2a1ad637SFrançois Tigeot 	[SND_CHN_MATRIX_2_0] = SND_CHN_MATRIX_MAP_2_0,
87*2a1ad637SFrançois Tigeot 	[SND_CHN_MATRIX_2_1] = SND_CHN_MATRIX_MAP_2_1,
88*2a1ad637SFrançois Tigeot 	[SND_CHN_MATRIX_3_0] = SND_CHN_MATRIX_MAP_3_0,
89*2a1ad637SFrançois Tigeot 	[SND_CHN_MATRIX_3_1] = SND_CHN_MATRIX_MAP_3_1,
90*2a1ad637SFrançois Tigeot 	[SND_CHN_MATRIX_4_0] = SND_CHN_MATRIX_MAP_4_0,
91*2a1ad637SFrançois Tigeot 	[SND_CHN_MATRIX_4_1] = SND_CHN_MATRIX_MAP_4_1,
92*2a1ad637SFrançois Tigeot 	[SND_CHN_MATRIX_5_0] = SND_CHN_MATRIX_MAP_5_0,
93*2a1ad637SFrançois Tigeot 	[SND_CHN_MATRIX_5_1] = SND_CHN_MATRIX_MAP_5_1,
94*2a1ad637SFrançois Tigeot 	[SND_CHN_MATRIX_6_0] = SND_CHN_MATRIX_MAP_6_0,
95*2a1ad637SFrançois Tigeot 	[SND_CHN_MATRIX_6_1] = SND_CHN_MATRIX_MAP_6_1,
96*2a1ad637SFrançois Tigeot 	[SND_CHN_MATRIX_7_0] = SND_CHN_MATRIX_MAP_7_0,
97*2a1ad637SFrançois Tigeot 	[SND_CHN_MATRIX_7_1] = SND_CHN_MATRIX_MAP_7_1
98*2a1ad637SFrançois Tigeot };
99*2a1ad637SFrançois Tigeot 
100*2a1ad637SFrançois Tigeot static int feeder_matrix_default_ids[9] = {
101*2a1ad637SFrançois Tigeot 	[0] = SND_CHN_MATRIX_UNKNOWN,
102*2a1ad637SFrançois Tigeot 	[1] = SND_CHN_MATRIX_1,
103*2a1ad637SFrançois Tigeot 	[2] = SND_CHN_MATRIX_2,
104*2a1ad637SFrançois Tigeot 	[3] = SND_CHN_MATRIX_3,
105*2a1ad637SFrançois Tigeot 	[4] = SND_CHN_MATRIX_4,
106*2a1ad637SFrançois Tigeot 	[5] = SND_CHN_MATRIX_5,
107*2a1ad637SFrançois Tigeot 	[6] = SND_CHN_MATRIX_6,
108*2a1ad637SFrançois Tigeot 	[7] = SND_CHN_MATRIX_7,
109*2a1ad637SFrançois Tigeot 	[8] = SND_CHN_MATRIX_8
110*2a1ad637SFrançois Tigeot };
111*2a1ad637SFrançois Tigeot 
112*2a1ad637SFrançois Tigeot #ifdef _KERNEL
113*2a1ad637SFrançois Tigeot #define FEEDMATRIX_CLIP_CHECK(...)
114*2a1ad637SFrançois Tigeot #else
115*2a1ad637SFrançois Tigeot #define FEEDMATRIX_CLIP_CHECK(v, BIT)	do {				\
116*2a1ad637SFrançois Tigeot 	if ((v) < PCM_S##BIT##_MIN || (v) > PCM_S##BIT##_MAX)		\
117*2a1ad637SFrançois Tigeot 	    errx(1, "\n\n%s(): Sample clipping: %jd\n",			\
118*2a1ad637SFrançois Tigeot 		__func__, (intmax_t)(v));				\
119*2a1ad637SFrançois Tigeot } while (0)
120*2a1ad637SFrançois Tigeot #endif
121*2a1ad637SFrançois Tigeot 
122*2a1ad637SFrançois Tigeot #define FEEDMATRIX_DECLARE(SIGN, BIT, ENDIAN)				\
123*2a1ad637SFrançois Tigeot static void								\
124*2a1ad637SFrançois Tigeot feed_matrix_##SIGN##BIT##ENDIAN(struct feed_matrix_info *info,		\
125*2a1ad637SFrançois Tigeot     uint8_t *src, uint8_t *dst, uint32_t count)				\
126*2a1ad637SFrançois Tigeot {									\
127*2a1ad637SFrançois Tigeot 	intpcm64_t accum;						\
128*2a1ad637SFrançois Tigeot 	intpcm_t v;							\
129*2a1ad637SFrançois Tigeot 	int i, j;							\
130*2a1ad637SFrançois Tigeot 									\
131*2a1ad637SFrançois Tigeot 	do {								\
132*2a1ad637SFrançois Tigeot 		for (i = 0; info->matrix[i].chn[0] != SND_CHN_T_EOF;	\
133*2a1ad637SFrançois Tigeot 		    i++) {						\
134*2a1ad637SFrançois Tigeot 			if (info->matrix[i].chn[0] == SND_CHN_T_NULL) {	\
135*2a1ad637SFrançois Tigeot 				_PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst,	\
136*2a1ad637SFrançois Tigeot 				    0);					\
137*2a1ad637SFrançois Tigeot 				dst += PCM_##BIT##_BPS;			\
138*2a1ad637SFrançois Tigeot 				continue;				\
139*2a1ad637SFrançois Tigeot 			} else if (info->matrix[i].chn[1] ==		\
140*2a1ad637SFrançois Tigeot 			    SND_CHN_T_EOF) {				\
141*2a1ad637SFrançois Tigeot 				v = _PCM_READ_##SIGN##BIT##_##ENDIAN(	\
142*2a1ad637SFrançois Tigeot 				    src + info->matrix[i].chn[0]);	\
143*2a1ad637SFrançois Tigeot 				_PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst,	\
144*2a1ad637SFrançois Tigeot 				    v);					\
145*2a1ad637SFrançois Tigeot 				dst += PCM_##BIT##_BPS;			\
146*2a1ad637SFrançois Tigeot 				continue;				\
147*2a1ad637SFrançois Tigeot 			}						\
148*2a1ad637SFrançois Tigeot 									\
149*2a1ad637SFrançois Tigeot 			accum = 0;					\
150*2a1ad637SFrançois Tigeot 			for (j = 0;					\
151*2a1ad637SFrançois Tigeot 			    info->matrix[i].chn[j] != SND_CHN_T_EOF;	\
152*2a1ad637SFrançois Tigeot 			    j++) {					\
153*2a1ad637SFrançois Tigeot 				v = _PCM_READ_##SIGN##BIT##_##ENDIAN(	\
154*2a1ad637SFrançois Tigeot 				    src + info->matrix[i].chn[j]);	\
155*2a1ad637SFrançois Tigeot 				accum += v;				\
156*2a1ad637SFrançois Tigeot 			}						\
157*2a1ad637SFrançois Tigeot 									\
158*2a1ad637SFrançois Tigeot 			accum = (accum * info->matrix[i].mul) >>	\
159*2a1ad637SFrançois Tigeot 			    info->matrix[i].shift;			\
160*2a1ad637SFrançois Tigeot 									\
161*2a1ad637SFrançois Tigeot 			FEEDMATRIX_CLIP_CHECK(accum, BIT);		\
162*2a1ad637SFrançois Tigeot 									\
163*2a1ad637SFrançois Tigeot 			v = (accum > PCM_S##BIT##_MAX) ?		\
164*2a1ad637SFrançois Tigeot 			    PCM_S##BIT##_MAX :				\
165*2a1ad637SFrançois Tigeot 			    ((accum < PCM_S##BIT##_MIN) ?		\
166*2a1ad637SFrançois Tigeot 			    PCM_S##BIT##_MIN :				\
167*2a1ad637SFrançois Tigeot 			    accum);					\
168*2a1ad637SFrançois Tigeot 			_PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, v);	\
169*2a1ad637SFrançois Tigeot 			dst += PCM_##BIT##_BPS;				\
170*2a1ad637SFrançois Tigeot 		}							\
171*2a1ad637SFrançois Tigeot 		src += info->ialign;					\
172*2a1ad637SFrançois Tigeot 	} while (--count != 0);						\
173*2a1ad637SFrançois Tigeot }
174*2a1ad637SFrançois Tigeot 
175*2a1ad637SFrançois Tigeot #if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
176*2a1ad637SFrançois Tigeot FEEDMATRIX_DECLARE(S, 16, LE)
177*2a1ad637SFrançois Tigeot FEEDMATRIX_DECLARE(S, 32, LE)
178*2a1ad637SFrançois Tigeot #endif
179*2a1ad637SFrançois Tigeot #if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
180*2a1ad637SFrançois Tigeot FEEDMATRIX_DECLARE(S, 16, BE)
181*2a1ad637SFrançois Tigeot FEEDMATRIX_DECLARE(S, 32, BE)
182*2a1ad637SFrançois Tigeot #endif
183*2a1ad637SFrançois Tigeot #ifdef SND_FEEDER_MULTIFORMAT
184*2a1ad637SFrançois Tigeot FEEDMATRIX_DECLARE(S,  8, NE)
185*2a1ad637SFrançois Tigeot FEEDMATRIX_DECLARE(S, 24, LE)
186*2a1ad637SFrançois Tigeot FEEDMATRIX_DECLARE(S, 24, BE)
187*2a1ad637SFrançois Tigeot FEEDMATRIX_DECLARE(U,  8, NE)
188*2a1ad637SFrançois Tigeot FEEDMATRIX_DECLARE(U, 16, LE)
189*2a1ad637SFrançois Tigeot FEEDMATRIX_DECLARE(U, 24, LE)
190*2a1ad637SFrançois Tigeot FEEDMATRIX_DECLARE(U, 32, LE)
191*2a1ad637SFrançois Tigeot FEEDMATRIX_DECLARE(U, 16, BE)
192*2a1ad637SFrançois Tigeot FEEDMATRIX_DECLARE(U, 24, BE)
193*2a1ad637SFrançois Tigeot FEEDMATRIX_DECLARE(U, 32, BE)
194*2a1ad637SFrançois Tigeot #endif
195*2a1ad637SFrançois Tigeot 
196*2a1ad637SFrançois Tigeot #define FEEDMATRIX_ENTRY(SIGN, BIT, ENDIAN)				\
197*2a1ad637SFrançois Tigeot 	{								\
198*2a1ad637SFrançois Tigeot 		AFMT_##SIGN##BIT##_##ENDIAN,				\
199*2a1ad637SFrançois Tigeot 		feed_matrix_##SIGN##BIT##ENDIAN				\
200*2a1ad637SFrançois Tigeot 	}
201*2a1ad637SFrançois Tigeot 
202*2a1ad637SFrançois Tigeot static const struct {
203*2a1ad637SFrançois Tigeot 	uint32_t format;
204*2a1ad637SFrançois Tigeot 	feed_matrix_t apply;
205*2a1ad637SFrançois Tigeot } feed_matrix_tab[] = {
206*2a1ad637SFrançois Tigeot #if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
207*2a1ad637SFrançois Tigeot 	FEEDMATRIX_ENTRY(S, 16, LE),
208*2a1ad637SFrançois Tigeot 	FEEDMATRIX_ENTRY(S, 32, LE),
209*2a1ad637SFrançois Tigeot #endif
210*2a1ad637SFrançois Tigeot #if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
211*2a1ad637SFrançois Tigeot 	FEEDMATRIX_ENTRY(S, 16, BE),
212*2a1ad637SFrançois Tigeot 	FEEDMATRIX_ENTRY(S, 32, BE),
213*2a1ad637SFrançois Tigeot #endif
214*2a1ad637SFrançois Tigeot #ifdef SND_FEEDER_MULTIFORMAT
215*2a1ad637SFrançois Tigeot 	FEEDMATRIX_ENTRY(S,  8, NE),
216*2a1ad637SFrançois Tigeot 	FEEDMATRIX_ENTRY(S, 24, LE),
217*2a1ad637SFrançois Tigeot 	FEEDMATRIX_ENTRY(S, 24, BE),
218*2a1ad637SFrançois Tigeot 	FEEDMATRIX_ENTRY(U,  8, NE),
219*2a1ad637SFrançois Tigeot 	FEEDMATRIX_ENTRY(U, 16, LE),
220*2a1ad637SFrançois Tigeot 	FEEDMATRIX_ENTRY(U, 24, LE),
221*2a1ad637SFrançois Tigeot 	FEEDMATRIX_ENTRY(U, 32, LE),
222*2a1ad637SFrançois Tigeot 	FEEDMATRIX_ENTRY(U, 16, BE),
223*2a1ad637SFrançois Tigeot 	FEEDMATRIX_ENTRY(U, 24, BE),
224*2a1ad637SFrançois Tigeot 	FEEDMATRIX_ENTRY(U, 32, BE)
225*2a1ad637SFrançois Tigeot #endif
226*2a1ad637SFrançois Tigeot };
227*2a1ad637SFrançois Tigeot 
228*2a1ad637SFrançois Tigeot static void
229*2a1ad637SFrançois Tigeot feed_matrix_reset(struct feed_matrix_info *info)
230*2a1ad637SFrançois Tigeot {
231*2a1ad637SFrançois Tigeot 	uint32_t i, j;
232*2a1ad637SFrançois Tigeot 
233*2a1ad637SFrançois Tigeot 	for (i = 0; i < (sizeof(info->matrix) / sizeof(info->matrix[0])); i++) {
234*2a1ad637SFrançois Tigeot 		for (j = 0;
235*2a1ad637SFrançois Tigeot 		    j < (sizeof(info->matrix[i].chn) /
236*2a1ad637SFrançois Tigeot 		    sizeof(info->matrix[i].chn[0])); j++) {
237*2a1ad637SFrançois Tigeot 			info->matrix[i].chn[j] = SND_CHN_T_EOF;
238*2a1ad637SFrançois Tigeot 		}
239*2a1ad637SFrançois Tigeot 		info->matrix[i].mul   = 1;
240*2a1ad637SFrançois Tigeot 		info->matrix[i].shift = 0;
241*2a1ad637SFrançois Tigeot 	}
242*2a1ad637SFrançois Tigeot }
243*2a1ad637SFrançois Tigeot 
244*2a1ad637SFrançois Tigeot #ifdef FEEDMATRIX_GENERIC
245*2a1ad637SFrançois Tigeot static void
246*2a1ad637SFrançois Tigeot feed_matrix_apply_generic(struct feed_matrix_info *info,
247*2a1ad637SFrançois Tigeot     uint8_t *src, uint8_t *dst, uint32_t count)
248*2a1ad637SFrançois Tigeot {
249*2a1ad637SFrançois Tigeot 	intpcm64_t accum;
250*2a1ad637SFrançois Tigeot 	intpcm_t v;
251*2a1ad637SFrançois Tigeot 	int i, j;
252*2a1ad637SFrançois Tigeot 
253*2a1ad637SFrançois Tigeot 	do {
254*2a1ad637SFrançois Tigeot 		for (i = 0; info->matrix[i].chn[0] != SND_CHN_T_EOF;
255*2a1ad637SFrançois Tigeot 		    i++) {
256*2a1ad637SFrançois Tigeot 			if (info->matrix[i].chn[0] == SND_CHN_T_NULL) {
257*2a1ad637SFrançois Tigeot 				info->wr(dst, 0);
258*2a1ad637SFrançois Tigeot 				dst += info->bps;
259*2a1ad637SFrançois Tigeot 				continue;
260*2a1ad637SFrançois Tigeot 			} else if (info->matrix[i].chn[1] ==
261*2a1ad637SFrançois Tigeot 			    SND_CHN_T_EOF) {
262*2a1ad637SFrançois Tigeot 				v = info->rd(src + info->matrix[i].chn[0]);
263*2a1ad637SFrançois Tigeot 				info->wr(dst, v);
264*2a1ad637SFrançois Tigeot 				dst += info->bps;
265*2a1ad637SFrançois Tigeot 				continue;
266*2a1ad637SFrançois Tigeot 			}
267*2a1ad637SFrançois Tigeot 
268*2a1ad637SFrançois Tigeot 			accum = 0;
269*2a1ad637SFrançois Tigeot 			for (j = 0;
270*2a1ad637SFrançois Tigeot 			    info->matrix[i].chn[j] != SND_CHN_T_EOF;
271*2a1ad637SFrançois Tigeot 			    j++) {
272*2a1ad637SFrançois Tigeot 				v = info->rd(src + info->matrix[i].chn[j]);
273*2a1ad637SFrançois Tigeot 				accum += v;
274*2a1ad637SFrançois Tigeot 			}
275*2a1ad637SFrançois Tigeot 
276*2a1ad637SFrançois Tigeot 			accum = (accum * info->matrix[i].mul) >>
277*2a1ad637SFrançois Tigeot 			    info->matrix[i].shift;
278*2a1ad637SFrançois Tigeot 
279*2a1ad637SFrançois Tigeot 			FEEDMATRIX_CLIP_CHECK(accum, 32);
280*2a1ad637SFrançois Tigeot 
281*2a1ad637SFrançois Tigeot 			v = (accum > PCM_S32_MAX) ? PCM_S32_MAX :
282*2a1ad637SFrançois Tigeot 			    ((accum < PCM_S32_MIN) ? PCM_S32_MIN : accum);
283*2a1ad637SFrançois Tigeot 			info->wr(dst, v);
284*2a1ad637SFrançois Tigeot 			dst += info->bps;
285*2a1ad637SFrançois Tigeot 		}
286*2a1ad637SFrançois Tigeot 		src += info->ialign;
287*2a1ad637SFrançois Tigeot 	} while (--count != 0);
288*2a1ad637SFrançois Tigeot }
289*2a1ad637SFrançois Tigeot #endif
290*2a1ad637SFrançois Tigeot 
291*2a1ad637SFrançois Tigeot static int
292*2a1ad637SFrançois Tigeot feed_matrix_setup(struct feed_matrix_info *info, struct pcmchan_matrix *m_in,
293*2a1ad637SFrançois Tigeot     struct pcmchan_matrix *m_out)
294*2a1ad637SFrançois Tigeot {
295*2a1ad637SFrançois Tigeot 	uint32_t i, j, ch, in_mask, merge_mask;
296*2a1ad637SFrançois Tigeot 	int mul, shift;
297*2a1ad637SFrançois Tigeot 
298*2a1ad637SFrançois Tigeot 
299*2a1ad637SFrançois Tigeot 	if (info == NULL || m_in == NULL || m_out == NULL ||
300*2a1ad637SFrançois Tigeot 	    AFMT_CHANNEL(info->in) != m_in->channels ||
301*2a1ad637SFrançois Tigeot 	    AFMT_CHANNEL(info->out) != m_out->channels ||
302*2a1ad637SFrançois Tigeot 	    m_in->channels < SND_CHN_MIN || m_in->channels > SND_CHN_MAX ||
303*2a1ad637SFrançois Tigeot 	    m_out->channels < SND_CHN_MIN || m_out->channels > SND_CHN_MAX)
304*2a1ad637SFrançois Tigeot 		return (EINVAL);
305*2a1ad637SFrançois Tigeot 
306*2a1ad637SFrançois Tigeot 	feed_matrix_reset(info);
307*2a1ad637SFrançois Tigeot 
308*2a1ad637SFrançois Tigeot 	/*
309*2a1ad637SFrançois Tigeot 	 * If both in and out are part of standard matrix and identical, skip
310*2a1ad637SFrançois Tigeot 	 * everything alltogether.
311*2a1ad637SFrançois Tigeot 	 */
312*2a1ad637SFrançois Tigeot 	if (m_in->id == m_out->id && !(m_in->id < SND_CHN_MATRIX_BEGIN ||
313*2a1ad637SFrançois Tigeot 	    m_in->id > SND_CHN_MATRIX_END))
314*2a1ad637SFrançois Tigeot 		return (0);
315*2a1ad637SFrançois Tigeot 
316*2a1ad637SFrançois Tigeot 	/*
317*2a1ad637SFrançois Tigeot 	 * Special case for mono input matrix. If the output supports
318*2a1ad637SFrançois Tigeot 	 * possible 'center' channel, route it there. Otherwise, let it be
319*2a1ad637SFrançois Tigeot 	 * matrixed to left/right.
320*2a1ad637SFrançois Tigeot 	 */
321*2a1ad637SFrançois Tigeot 	if (m_in->id == SND_CHN_MATRIX_1_0) {
322*2a1ad637SFrançois Tigeot 		if (m_out->id == SND_CHN_MATRIX_1_0)
323*2a1ad637SFrançois Tigeot 			in_mask = SND_CHN_T_MASK_FL;
324*2a1ad637SFrançois Tigeot 		else if (m_out->mask & SND_CHN_T_MASK_FC)
325*2a1ad637SFrançois Tigeot 			in_mask = SND_CHN_T_MASK_FC;
326*2a1ad637SFrançois Tigeot 		else
327*2a1ad637SFrançois Tigeot 			in_mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR;
328*2a1ad637SFrançois Tigeot 	} else
329*2a1ad637SFrançois Tigeot 		in_mask = m_in->mask;
330*2a1ad637SFrançois Tigeot 
331*2a1ad637SFrançois Tigeot 	/* Merge, reduce, expand all possibilites. */
332*2a1ad637SFrançois Tigeot 	for (ch = SND_CHN_T_BEGIN; ch <= SND_CHN_T_END &&
333*2a1ad637SFrançois Tigeot 	    m_out->map[ch].type != SND_CHN_T_MAX; ch += SND_CHN_T_STEP) {
334*2a1ad637SFrançois Tigeot 		merge_mask = m_out->map[ch].members & in_mask;
335*2a1ad637SFrançois Tigeot 		if (merge_mask == 0) {
336*2a1ad637SFrançois Tigeot 			info->matrix[ch].chn[0] = SND_CHN_T_NULL;
337*2a1ad637SFrançois Tigeot 			continue;
338*2a1ad637SFrançois Tigeot 		}
339*2a1ad637SFrançois Tigeot 
340*2a1ad637SFrançois Tigeot 		j = 0;
341*2a1ad637SFrançois Tigeot 		for (i = SND_CHN_T_BEGIN; i <= SND_CHN_T_END;
342*2a1ad637SFrançois Tigeot 		    i += SND_CHN_T_STEP) {
343*2a1ad637SFrançois Tigeot 			if (merge_mask & (1 << i)) {
344*2a1ad637SFrançois Tigeot 				if (m_in->offset[i] >= 0 &&
345*2a1ad637SFrançois Tigeot 				    m_in->offset[i] < (int)m_in->channels)
346*2a1ad637SFrançois Tigeot 					info->matrix[ch].chn[j++] =
347*2a1ad637SFrançois Tigeot 					    m_in->offset[i] * info->bps;
348*2a1ad637SFrançois Tigeot 				else {
349*2a1ad637SFrançois Tigeot 					info->matrix[ch].chn[j++] =
350*2a1ad637SFrançois Tigeot 					    SND_CHN_T_EOF;
351*2a1ad637SFrançois Tigeot 					break;
352*2a1ad637SFrançois Tigeot 				}
353*2a1ad637SFrançois Tigeot 			}
354*2a1ad637SFrançois Tigeot 		}
355*2a1ad637SFrançois Tigeot 
356*2a1ad637SFrançois Tigeot #define FEEDMATRIX_ATTN_SHIFT	16
357*2a1ad637SFrançois Tigeot 
358*2a1ad637SFrançois Tigeot 		if (j > 1) {
359*2a1ad637SFrançois Tigeot 			/*
360*2a1ad637SFrançois Tigeot 			 * XXX For channel that require accumulation from
361*2a1ad637SFrançois Tigeot 			 * multiple channels, apply a slight attenuation to
362*2a1ad637SFrançois Tigeot 			 * avoid clipping.
363*2a1ad637SFrançois Tigeot 			 */
364*2a1ad637SFrançois Tigeot 			mul   = (1 << (FEEDMATRIX_ATTN_SHIFT - 1)) + 143 - j;
365*2a1ad637SFrançois Tigeot 			shift = FEEDMATRIX_ATTN_SHIFT;
366*2a1ad637SFrançois Tigeot 			while ((mul & 1) == 0 && shift > 0) {
367*2a1ad637SFrançois Tigeot 				mul >>= 1;
368*2a1ad637SFrançois Tigeot 				shift--;
369*2a1ad637SFrançois Tigeot 			}
370*2a1ad637SFrançois Tigeot 			info->matrix[ch].mul   = mul;
371*2a1ad637SFrançois Tigeot 			info->matrix[ch].shift = shift;
372*2a1ad637SFrançois Tigeot 		}
373*2a1ad637SFrançois Tigeot 	}
374*2a1ad637SFrançois Tigeot 
375*2a1ad637SFrançois Tigeot #ifndef _KERNEL
376*2a1ad637SFrançois Tigeot 	fprintf(stderr, "Total: %d\n", ch);
377*2a1ad637SFrançois Tigeot 
378*2a1ad637SFrançois Tigeot 	for (i = 0; info->matrix[i].chn[0] != SND_CHN_T_EOF; i++) {
379*2a1ad637SFrançois Tigeot 		fprintf(stderr, "%d: [", i);
380*2a1ad637SFrançois Tigeot 		for (j = 0; info->matrix[i].chn[j] != SND_CHN_T_EOF; j++) {
381*2a1ad637SFrançois Tigeot 			if (j != 0)
382*2a1ad637SFrançois Tigeot 				fprintf(stderr, ", ");
383*2a1ad637SFrançois Tigeot 			fprintf(stderr, "%d",
384*2a1ad637SFrançois Tigeot 			    (info->matrix[i].chn[j] == SND_CHN_T_NULL) ?
385*2a1ad637SFrançois Tigeot 			    0xffffffff : info->matrix[i].chn[j] / info->bps);
386*2a1ad637SFrançois Tigeot 		}
387*2a1ad637SFrançois Tigeot 		fprintf(stderr, "] attn: (x * %d) >> %d\n",
388*2a1ad637SFrançois Tigeot 		    info->matrix[i].mul, info->matrix[i].shift);
389*2a1ad637SFrançois Tigeot 	}
390*2a1ad637SFrançois Tigeot #endif
391*2a1ad637SFrançois Tigeot 
392*2a1ad637SFrançois Tigeot 	return (0);
393*2a1ad637SFrançois Tigeot }
394*2a1ad637SFrançois Tigeot 
395*2a1ad637SFrançois Tigeot static int
396*2a1ad637SFrançois Tigeot feed_matrix_init(struct pcm_feeder *f)
397*2a1ad637SFrançois Tigeot {
398*2a1ad637SFrançois Tigeot 	struct feed_matrix_info *info;
399*2a1ad637SFrançois Tigeot 	struct pcmchan_matrix *m_in, *m_out;
400*2a1ad637SFrançois Tigeot 	uint32_t i;
401*2a1ad637SFrançois Tigeot 	int ret;
402*2a1ad637SFrançois Tigeot 
403*2a1ad637SFrançois Tigeot 	if (AFMT_ENCODING(f->desc->in) != AFMT_ENCODING(f->desc->out))
404*2a1ad637SFrançois Tigeot 		return (EINVAL);
405*2a1ad637SFrançois Tigeot 
406*2a1ad637SFrançois Tigeot 	info = malloc(sizeof(*info), M_DEVBUF, M_NOWAIT | M_ZERO);
407*2a1ad637SFrançois Tigeot 	if (info == NULL)
408*2a1ad637SFrançois Tigeot 		return (ENOMEM);
409*2a1ad637SFrançois Tigeot 
410*2a1ad637SFrançois Tigeot 	info->in = f->desc->in;
411*2a1ad637SFrançois Tigeot 	info->out = f->desc->out;
412*2a1ad637SFrançois Tigeot 	info->bps = AFMT_BPS(info->in);
413*2a1ad637SFrançois Tigeot 	info->ialign = AFMT_ALIGN(info->in);
414*2a1ad637SFrançois Tigeot 	info->oalign = AFMT_ALIGN(info->out);
415*2a1ad637SFrançois Tigeot 	info->apply = NULL;
416*2a1ad637SFrançois Tigeot 
417*2a1ad637SFrançois Tigeot 	for (i = 0; info->apply == NULL &&
418*2a1ad637SFrançois Tigeot 	    i < (sizeof(feed_matrix_tab) / sizeof(feed_matrix_tab[0])); i++) {
419*2a1ad637SFrançois Tigeot 		if (AFMT_ENCODING(info->in) == feed_matrix_tab[i].format)
420*2a1ad637SFrançois Tigeot 			info->apply = feed_matrix_tab[i].apply;
421*2a1ad637SFrançois Tigeot 	}
422*2a1ad637SFrançois Tigeot 
423*2a1ad637SFrançois Tigeot 	if (info->apply == NULL) {
424*2a1ad637SFrançois Tigeot #ifdef FEEDMATRIX_GENERIC
425*2a1ad637SFrançois Tigeot 		info->rd = feeder_format_read_op(info->in);
426*2a1ad637SFrançois Tigeot 		info->wr = feeder_format_write_op(info->out);
427*2a1ad637SFrançois Tigeot 		if (info->rd == NULL || info->wr == NULL) {
428*2a1ad637SFrançois Tigeot 			free(info, M_DEVBUF);
429*2a1ad637SFrançois Tigeot 			return (EINVAL);
430*2a1ad637SFrançois Tigeot 		}
431*2a1ad637SFrançois Tigeot 		info->apply = feed_matrix_apply_generic;
432*2a1ad637SFrançois Tigeot #else
433*2a1ad637SFrançois Tigeot 		free(info, M_DEVBUF);
434*2a1ad637SFrançois Tigeot 		return (EINVAL);
435*2a1ad637SFrançois Tigeot #endif
436*2a1ad637SFrançois Tigeot 	}
437*2a1ad637SFrançois Tigeot 
438*2a1ad637SFrançois Tigeot 	m_in  = feeder_matrix_format_map(info->in);
439*2a1ad637SFrançois Tigeot 	m_out = feeder_matrix_format_map(info->out);
440*2a1ad637SFrançois Tigeot 
441*2a1ad637SFrançois Tigeot 	ret = feed_matrix_setup(info, m_in, m_out);
442*2a1ad637SFrançois Tigeot 	if (ret != 0) {
443*2a1ad637SFrançois Tigeot 		free(info, M_DEVBUF);
444*2a1ad637SFrançois Tigeot 		return (ret);
445*2a1ad637SFrançois Tigeot 	}
446*2a1ad637SFrançois Tigeot 
447*2a1ad637SFrançois Tigeot 	f->data = info;
448*2a1ad637SFrançois Tigeot 
449*2a1ad637SFrançois Tigeot 	return (0);
450*2a1ad637SFrançois Tigeot }
451*2a1ad637SFrançois Tigeot 
452*2a1ad637SFrançois Tigeot static int
453*2a1ad637SFrançois Tigeot feed_matrix_free(struct pcm_feeder *f)
454*2a1ad637SFrançois Tigeot {
455*2a1ad637SFrançois Tigeot 	struct feed_matrix_info *info;
456*2a1ad637SFrançois Tigeot 
457*2a1ad637SFrançois Tigeot 	info = f->data;
458*2a1ad637SFrançois Tigeot 	if (info != NULL)
459*2a1ad637SFrançois Tigeot 		free(info, M_DEVBUF);
460*2a1ad637SFrançois Tigeot 
461*2a1ad637SFrançois Tigeot 	f->data = NULL;
462*2a1ad637SFrançois Tigeot 
463*2a1ad637SFrançois Tigeot 	return (0);
464*2a1ad637SFrançois Tigeot }
465*2a1ad637SFrançois Tigeot 
466*2a1ad637SFrançois Tigeot static int
467*2a1ad637SFrançois Tigeot feed_matrix_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
468*2a1ad637SFrançois Tigeot     uint32_t count, void *source)
469*2a1ad637SFrançois Tigeot {
470*2a1ad637SFrançois Tigeot 	struct feed_matrix_info *info;
471*2a1ad637SFrançois Tigeot 	uint32_t j, inmax;
472*2a1ad637SFrançois Tigeot 	uint8_t *src, *dst;
473*2a1ad637SFrançois Tigeot 
474*2a1ad637SFrançois Tigeot 	info = f->data;
475*2a1ad637SFrançois Tigeot 	if (info->matrix[0].chn[0] == SND_CHN_T_EOF)
476*2a1ad637SFrançois Tigeot 		return (FEEDER_FEED(f->source, c, b, count, source));
477*2a1ad637SFrançois Tigeot 
478*2a1ad637SFrançois Tigeot 	dst = b;
479*2a1ad637SFrançois Tigeot 	count = SND_FXROUND(count, info->oalign);
480*2a1ad637SFrançois Tigeot 	inmax = info->ialign + info->oalign;
481*2a1ad637SFrançois Tigeot 
482*2a1ad637SFrançois Tigeot 	/*
483*2a1ad637SFrançois Tigeot 	 * This loop might look simmilar to other feeder_* loops, but be
484*2a1ad637SFrançois Tigeot 	 * advised: matrixing might involve overlapping (think about
485*2a1ad637SFrançois Tigeot 	 * swapping end to front or something like that). In this regard it
486*2a1ad637SFrançois Tigeot 	 * might be simmilar to feeder_format, but feeder_format works on
487*2a1ad637SFrançois Tigeot 	 * 'sample' domain where it can be fitted into single 32bit integer
488*2a1ad637SFrançois Tigeot 	 * while matrixing works on 'sample frame' domain.
489*2a1ad637SFrançois Tigeot 	 */
490*2a1ad637SFrançois Tigeot 	do {
491*2a1ad637SFrançois Tigeot 		if (count < info->oalign)
492*2a1ad637SFrançois Tigeot 			break;
493*2a1ad637SFrançois Tigeot 
494*2a1ad637SFrançois Tigeot 		if (count < inmax) {
495*2a1ad637SFrançois Tigeot 			src = info->reservoir;
496*2a1ad637SFrançois Tigeot 			j = info->ialign;
497*2a1ad637SFrançois Tigeot 		} else {
498*2a1ad637SFrançois Tigeot 			if (info->ialign == info->oalign)
499*2a1ad637SFrançois Tigeot 				j = count - info->oalign;
500*2a1ad637SFrançois Tigeot 			else if (info->ialign > info->oalign)
501*2a1ad637SFrançois Tigeot 				j = SND_FXROUND(count - info->oalign,
502*2a1ad637SFrançois Tigeot 				    info->ialign);
503*2a1ad637SFrançois Tigeot 			else
504*2a1ad637SFrançois Tigeot 				j = (SND_FXDIV(count, info->oalign) - 1) *
505*2a1ad637SFrançois Tigeot 				    info->ialign;
506*2a1ad637SFrançois Tigeot 			src = dst + count - j;
507*2a1ad637SFrançois Tigeot 		}
508*2a1ad637SFrançois Tigeot 
509*2a1ad637SFrançois Tigeot 		j = SND_FXDIV(FEEDER_FEED(f->source, c, src, j, source),
510*2a1ad637SFrançois Tigeot 		    info->ialign);
511*2a1ad637SFrançois Tigeot 		if (j == 0)
512*2a1ad637SFrançois Tigeot 			break;
513*2a1ad637SFrançois Tigeot 
514*2a1ad637SFrançois Tigeot 		info->apply(info, src, dst, j);
515*2a1ad637SFrançois Tigeot 
516*2a1ad637SFrançois Tigeot 		j *= info->oalign;
517*2a1ad637SFrançois Tigeot 		dst += j;
518*2a1ad637SFrançois Tigeot 		count -= j;
519*2a1ad637SFrançois Tigeot 
520*2a1ad637SFrançois Tigeot 	} while (count != 0);
521*2a1ad637SFrançois Tigeot 
522*2a1ad637SFrançois Tigeot 	return (dst - b);
523*2a1ad637SFrançois Tigeot }
524*2a1ad637SFrançois Tigeot 
525*2a1ad637SFrançois Tigeot static struct pcm_feederdesc feeder_matrix_desc[] = {
526*2a1ad637SFrançois Tigeot 	{ FEEDER_MATRIX, 0, 0, 0, 0 },
527*2a1ad637SFrançois Tigeot 	{ 0, 0, 0, 0, 0 }
528*2a1ad637SFrançois Tigeot };
529*2a1ad637SFrançois Tigeot 
530*2a1ad637SFrançois Tigeot static kobj_method_t feeder_matrix_methods[] = {
531*2a1ad637SFrançois Tigeot 	KOBJMETHOD(feeder_init,		feed_matrix_init),
532*2a1ad637SFrançois Tigeot 	KOBJMETHOD(feeder_free,		feed_matrix_free),
533*2a1ad637SFrançois Tigeot 	KOBJMETHOD(feeder_feed,		feed_matrix_feed),
534*2a1ad637SFrançois Tigeot 	KOBJMETHOD_END
535*2a1ad637SFrançois Tigeot };
536*2a1ad637SFrançois Tigeot 
537*2a1ad637SFrançois Tigeot FEEDER_DECLARE(feeder_matrix, NULL);
538*2a1ad637SFrançois Tigeot 
539*2a1ad637SFrançois Tigeot /* External */
540*2a1ad637SFrançois Tigeot int
541*2a1ad637SFrançois Tigeot feeder_matrix_setup(struct pcm_feeder *f, struct pcmchan_matrix *m_in,
542*2a1ad637SFrançois Tigeot     struct pcmchan_matrix *m_out)
543*2a1ad637SFrançois Tigeot {
544*2a1ad637SFrançois Tigeot 
545*2a1ad637SFrançois Tigeot 	if (f == NULL || f->desc == NULL || f->desc->type != FEEDER_MATRIX ||
546*2a1ad637SFrançois Tigeot 	    f->data == NULL)
547*2a1ad637SFrançois Tigeot 		return (EINVAL);
548*2a1ad637SFrançois Tigeot 
549*2a1ad637SFrançois Tigeot 	return (feed_matrix_setup(f->data, m_in, m_out));
550*2a1ad637SFrançois Tigeot }
551*2a1ad637SFrançois Tigeot 
552*2a1ad637SFrançois Tigeot /*
553*2a1ad637SFrançois Tigeot  * feeder_matrix_default_id(): For a given number of channels, return
554*2a1ad637SFrançois Tigeot  *                             default prefered id (example: both 5.1 and
555*2a1ad637SFrançois Tigeot  *                             6.0 are simply 6 channels, but 5.1 is more
556*2a1ad637SFrançois Tigeot  *                             preferable).
557*2a1ad637SFrançois Tigeot  */
558*2a1ad637SFrançois Tigeot int
559*2a1ad637SFrançois Tigeot feeder_matrix_default_id(uint32_t ch)
560*2a1ad637SFrançois Tigeot {
561*2a1ad637SFrançois Tigeot 
562*2a1ad637SFrançois Tigeot 	if (ch < feeder_matrix_maps[SND_CHN_MATRIX_BEGIN].channels ||
563*2a1ad637SFrançois Tigeot 	    ch > feeder_matrix_maps[SND_CHN_MATRIX_END].channels)
564*2a1ad637SFrançois Tigeot 		return (SND_CHN_MATRIX_UNKNOWN);
565*2a1ad637SFrançois Tigeot 
566*2a1ad637SFrançois Tigeot 	return (feeder_matrix_maps[feeder_matrix_default_ids[ch]].id);
567*2a1ad637SFrançois Tigeot }
568*2a1ad637SFrançois Tigeot 
569*2a1ad637SFrançois Tigeot /*
570*2a1ad637SFrançois Tigeot  * feeder_matrix_default_channel_map(): Ditto, but return matrix map
571*2a1ad637SFrançois Tigeot  *                                      instead.
572*2a1ad637SFrançois Tigeot  */
573*2a1ad637SFrançois Tigeot struct pcmchan_matrix *
574*2a1ad637SFrançois Tigeot feeder_matrix_default_channel_map(uint32_t ch)
575*2a1ad637SFrançois Tigeot {
576*2a1ad637SFrançois Tigeot 
577*2a1ad637SFrançois Tigeot 	if (ch < feeder_matrix_maps[SND_CHN_MATRIX_BEGIN].channels ||
578*2a1ad637SFrançois Tigeot 	    ch > feeder_matrix_maps[SND_CHN_MATRIX_END].channels)
579*2a1ad637SFrançois Tigeot 		return (NULL);
580*2a1ad637SFrançois Tigeot 
581*2a1ad637SFrançois Tigeot 	return (&feeder_matrix_maps[feeder_matrix_default_ids[ch]]);
582*2a1ad637SFrançois Tigeot }
583*2a1ad637SFrançois Tigeot 
584*2a1ad637SFrançois Tigeot /*
585*2a1ad637SFrançois Tigeot  * feeder_matrix_default_format(): For a given audio format, return the
586*2a1ad637SFrançois Tigeot  *                                 proper audio format based on preferable
587*2a1ad637SFrançois Tigeot  *                                 matrix.
588*2a1ad637SFrançois Tigeot  */
589*2a1ad637SFrançois Tigeot uint32_t
590*2a1ad637SFrançois Tigeot feeder_matrix_default_format(uint32_t format)
591*2a1ad637SFrançois Tigeot {
592*2a1ad637SFrançois Tigeot 	struct pcmchan_matrix *m;
593*2a1ad637SFrançois Tigeot 	uint32_t i, ch, ext;
594*2a1ad637SFrançois Tigeot 
595*2a1ad637SFrançois Tigeot 	ch = AFMT_CHANNEL(format);
596*2a1ad637SFrançois Tigeot 	ext = AFMT_EXTCHANNEL(format);
597*2a1ad637SFrançois Tigeot 
598*2a1ad637SFrançois Tigeot 	if (ext != 0) {
599*2a1ad637SFrançois Tigeot 		for (i = SND_CHN_MATRIX_BEGIN; i <= SND_CHN_MATRIX_END; i++) {
600*2a1ad637SFrançois Tigeot 			if (feeder_matrix_maps[i].channels == ch &&
601*2a1ad637SFrançois Tigeot 			    feeder_matrix_maps[i].ext == ext)
602*2a1ad637SFrançois Tigeot 			return (SND_FORMAT(format, ch, ext));
603*2a1ad637SFrançois Tigeot 		}
604*2a1ad637SFrançois Tigeot 	}
605*2a1ad637SFrançois Tigeot 
606*2a1ad637SFrançois Tigeot 	m = feeder_matrix_default_channel_map(ch);
607*2a1ad637SFrançois Tigeot 	if (m == NULL)
608*2a1ad637SFrançois Tigeot 		return (0x00000000);
609*2a1ad637SFrançois Tigeot 
610*2a1ad637SFrançois Tigeot 	return (SND_FORMAT(format, ch, m->ext));
611*2a1ad637SFrançois Tigeot }
612*2a1ad637SFrançois Tigeot 
613*2a1ad637SFrançois Tigeot /*
614*2a1ad637SFrançois Tigeot  * feeder_matrix_format_id(): For a given audio format, return its matrix
615*2a1ad637SFrançois Tigeot  *                            id.
616*2a1ad637SFrançois Tigeot  */
617*2a1ad637SFrançois Tigeot int
618*2a1ad637SFrançois Tigeot feeder_matrix_format_id(uint32_t format)
619*2a1ad637SFrançois Tigeot {
620*2a1ad637SFrançois Tigeot 	uint32_t i, ch, ext;
621*2a1ad637SFrançois Tigeot 
622*2a1ad637SFrançois Tigeot 	ch = AFMT_CHANNEL(format);
623*2a1ad637SFrançois Tigeot 	ext = AFMT_EXTCHANNEL(format);
624*2a1ad637SFrançois Tigeot 
625*2a1ad637SFrançois Tigeot 	for (i = SND_CHN_MATRIX_BEGIN; i <= SND_CHN_MATRIX_END; i++) {
626*2a1ad637SFrançois Tigeot 		if (feeder_matrix_maps[i].channels == ch &&
627*2a1ad637SFrançois Tigeot 		    feeder_matrix_maps[i].ext == ext)
628*2a1ad637SFrançois Tigeot 			return (feeder_matrix_maps[i].id);
629*2a1ad637SFrançois Tigeot 	}
630*2a1ad637SFrançois Tigeot 
631*2a1ad637SFrançois Tigeot 	return (SND_CHN_MATRIX_UNKNOWN);
632*2a1ad637SFrançois Tigeot }
633*2a1ad637SFrançois Tigeot 
634*2a1ad637SFrançois Tigeot /*
635*2a1ad637SFrançois Tigeot  * feeder_matrix_format_map(): For a given audio format, return its matrix
636*2a1ad637SFrançois Tigeot  *                             map.
637*2a1ad637SFrançois Tigeot  */
638*2a1ad637SFrançois Tigeot struct pcmchan_matrix *
639*2a1ad637SFrançois Tigeot feeder_matrix_format_map(uint32_t format)
640*2a1ad637SFrançois Tigeot {
641*2a1ad637SFrançois Tigeot 	uint32_t i, ch, ext;
642*2a1ad637SFrançois Tigeot 
643*2a1ad637SFrançois Tigeot 	ch = AFMT_CHANNEL(format);
644*2a1ad637SFrançois Tigeot 	ext = AFMT_EXTCHANNEL(format);
645*2a1ad637SFrançois Tigeot 
646*2a1ad637SFrançois Tigeot 	for (i = SND_CHN_MATRIX_BEGIN; i <= SND_CHN_MATRIX_END; i++) {
647*2a1ad637SFrançois Tigeot 		if (feeder_matrix_maps[i].channels == ch &&
648*2a1ad637SFrançois Tigeot 		    feeder_matrix_maps[i].ext == ext)
649*2a1ad637SFrançois Tigeot 			return (&feeder_matrix_maps[i]);
650*2a1ad637SFrançois Tigeot 	}
651*2a1ad637SFrançois Tigeot 
652*2a1ad637SFrançois Tigeot 	return (NULL);
653*2a1ad637SFrançois Tigeot }
654*2a1ad637SFrançois Tigeot 
655*2a1ad637SFrançois Tigeot /*
656*2a1ad637SFrançois Tigeot  * feeder_matrix_id_map(): For a given matrix id, return its matrix map.
657*2a1ad637SFrançois Tigeot  */
658*2a1ad637SFrançois Tigeot struct pcmchan_matrix *
659*2a1ad637SFrançois Tigeot feeder_matrix_id_map(int id)
660*2a1ad637SFrançois Tigeot {
661*2a1ad637SFrançois Tigeot 
662*2a1ad637SFrançois Tigeot 	if (id < SND_CHN_MATRIX_BEGIN || id > SND_CHN_MATRIX_END)
663*2a1ad637SFrançois Tigeot 		return (NULL);
664*2a1ad637SFrançois Tigeot 
665*2a1ad637SFrançois Tigeot 	return (&feeder_matrix_maps[id]);
666*2a1ad637SFrançois Tigeot }
667*2a1ad637SFrançois Tigeot 
668*2a1ad637SFrançois Tigeot /*
669*2a1ad637SFrançois Tigeot  * feeder_matrix_compare(): Compare the simmilarities of matrices.
670*2a1ad637SFrançois Tigeot  */
671*2a1ad637SFrançois Tigeot int
672*2a1ad637SFrançois Tigeot feeder_matrix_compare(struct pcmchan_matrix *m_in, struct pcmchan_matrix *m_out)
673*2a1ad637SFrançois Tigeot {
674*2a1ad637SFrançois Tigeot 	uint32_t i;
675*2a1ad637SFrançois Tigeot 
676*2a1ad637SFrançois Tigeot 	if (m_in == m_out)
677*2a1ad637SFrançois Tigeot 		return (0);
678*2a1ad637SFrançois Tigeot 
679*2a1ad637SFrançois Tigeot 	if (m_in->channels != m_out->channels || m_in->ext != m_out->ext ||
680*2a1ad637SFrançois Tigeot 	    m_in->mask != m_out->mask)
681*2a1ad637SFrançois Tigeot 		return (1);
682*2a1ad637SFrançois Tigeot 
683*2a1ad637SFrançois Tigeot 	for (i = 0; i < (sizeof(m_in->map) / sizeof(m_in->map[0])); i++) {
684*2a1ad637SFrançois Tigeot 		if (m_in->map[i].type != m_out->map[i].type)
685*2a1ad637SFrançois Tigeot 			return (1);
686*2a1ad637SFrançois Tigeot 		if (m_in->map[i].type == SND_CHN_T_MAX)
687*2a1ad637SFrançois Tigeot 			break;
688*2a1ad637SFrançois Tigeot 		if (m_in->map[i].members != m_out->map[i].members)
689*2a1ad637SFrançois Tigeot 			return (1);
690*2a1ad637SFrançois Tigeot 		if (i <= SND_CHN_T_END) {
691*2a1ad637SFrançois Tigeot 			if (m_in->offset[m_in->map[i].type] !=
692*2a1ad637SFrançois Tigeot 			    m_out->offset[m_out->map[i].type])
693*2a1ad637SFrançois Tigeot 				return (1);
694*2a1ad637SFrançois Tigeot 		}
695*2a1ad637SFrançois Tigeot 	}
696*2a1ad637SFrançois Tigeot 
697*2a1ad637SFrançois Tigeot 	return (0);
698*2a1ad637SFrançois Tigeot }
699*2a1ad637SFrançois Tigeot 
700*2a1ad637SFrançois Tigeot /*
701*2a1ad637SFrançois Tigeot  * XXX 4front intepretation of "surround" is ambigous and sort of
702*2a1ad637SFrançois Tigeot  *     conflicting with "rear"/"back". Map it to "side". Well..
703*2a1ad637SFrançois Tigeot  *     who cares?
704*2a1ad637SFrançois Tigeot  */
705*2a1ad637SFrançois Tigeot static int snd_chn_to_oss[SND_CHN_T_MAX] = {
706*2a1ad637SFrançois Tigeot 	[SND_CHN_T_FL] = CHID_L,
707*2a1ad637SFrançois Tigeot 	[SND_CHN_T_FR] = CHID_R,
708*2a1ad637SFrançois Tigeot 	[SND_CHN_T_FC] = CHID_C,
709*2a1ad637SFrançois Tigeot 	[SND_CHN_T_LF] = CHID_LFE,
710*2a1ad637SFrançois Tigeot 	[SND_CHN_T_SL] = CHID_LS,
711*2a1ad637SFrançois Tigeot 	[SND_CHN_T_SR] = CHID_RS,
712*2a1ad637SFrançois Tigeot 	[SND_CHN_T_BL] = CHID_LR,
713*2a1ad637SFrançois Tigeot 	[SND_CHN_T_BR] = CHID_RR
714*2a1ad637SFrançois Tigeot };
715*2a1ad637SFrançois Tigeot 
716*2a1ad637SFrançois Tigeot #define SND_CHN_OSS_VALIDMASK						\
717*2a1ad637SFrançois Tigeot 			(SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR |	\
718*2a1ad637SFrançois Tigeot 			 SND_CHN_T_MASK_FC | SND_CHN_T_MASK_LF |	\
719*2a1ad637SFrançois Tigeot 			 SND_CHN_T_MASK_SL | SND_CHN_T_MASK_SR |	\
720*2a1ad637SFrançois Tigeot 			 SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR)
721*2a1ad637SFrançois Tigeot 
722*2a1ad637SFrançois Tigeot #define SND_CHN_OSS_MAX		8
723*2a1ad637SFrançois Tigeot #define SND_CHN_OSS_BEGIN	CHID_L
724*2a1ad637SFrançois Tigeot #define SND_CHN_OSS_END		CHID_RR
725*2a1ad637SFrançois Tigeot 
726*2a1ad637SFrançois Tigeot static int oss_to_snd_chn[SND_CHN_OSS_END + 1] = {
727*2a1ad637SFrançois Tigeot 	[CHID_L]   = SND_CHN_T_FL,
728*2a1ad637SFrançois Tigeot 	[CHID_R]   = SND_CHN_T_FR,
729*2a1ad637SFrançois Tigeot 	[CHID_C]   = SND_CHN_T_FC,
730*2a1ad637SFrançois Tigeot 	[CHID_LFE] = SND_CHN_T_LF,
731*2a1ad637SFrançois Tigeot 	[CHID_LS]  = SND_CHN_T_SL,
732*2a1ad637SFrançois Tigeot 	[CHID_RS]  = SND_CHN_T_SR,
733*2a1ad637SFrançois Tigeot 	[CHID_LR]  = SND_CHN_T_BL,
734*2a1ad637SFrançois Tigeot 	[CHID_RR]  = SND_CHN_T_BR
735*2a1ad637SFrançois Tigeot };
736*2a1ad637SFrançois Tigeot 
737*2a1ad637SFrançois Tigeot /*
738*2a1ad637SFrançois Tigeot  * Used by SNDCTL_DSP_GET_CHNORDER.
739*2a1ad637SFrançois Tigeot  */
740*2a1ad637SFrançois Tigeot int
741*2a1ad637SFrançois Tigeot feeder_matrix_oss_get_channel_order(struct pcmchan_matrix *m,
742*2a1ad637SFrançois Tigeot     unsigned long long *map)
743*2a1ad637SFrançois Tigeot {
744*2a1ad637SFrançois Tigeot 	unsigned long long tmpmap;
745*2a1ad637SFrançois Tigeot 	uint32_t i;
746*2a1ad637SFrançois Tigeot 
747*2a1ad637SFrançois Tigeot 	if (m == NULL || map == NULL || (m->mask & ~SND_CHN_OSS_VALIDMASK) ||
748*2a1ad637SFrançois Tigeot 	    m->channels > SND_CHN_OSS_MAX)
749*2a1ad637SFrançois Tigeot 		return (EINVAL);
750*2a1ad637SFrançois Tigeot 
751*2a1ad637SFrançois Tigeot 	tmpmap = 0x0000000000000000ULL;
752*2a1ad637SFrançois Tigeot 
753*2a1ad637SFrançois Tigeot 	for (i = 0; m->map[i].type != SND_CHN_T_MAX &&
754*2a1ad637SFrançois Tigeot 	    i < SND_CHN_OSS_MAX; i++) {
755*2a1ad637SFrançois Tigeot 		if ((1 << m->map[i].type) & ~SND_CHN_OSS_VALIDMASK)
756*2a1ad637SFrançois Tigeot 			return (EINVAL);
757*2a1ad637SFrançois Tigeot 		tmpmap |=
758*2a1ad637SFrançois Tigeot 		    (unsigned long long)snd_chn_to_oss[m->map[i].type] <<
759*2a1ad637SFrançois Tigeot 		    (i * 4);
760*2a1ad637SFrançois Tigeot 	}
761*2a1ad637SFrançois Tigeot 
762*2a1ad637SFrançois Tigeot 	*map = tmpmap;
763*2a1ad637SFrançois Tigeot 
764*2a1ad637SFrançois Tigeot 	return (0);
765*2a1ad637SFrançois Tigeot }
766*2a1ad637SFrançois Tigeot 
767*2a1ad637SFrançois Tigeot /*
768*2a1ad637SFrançois Tigeot  * Used by SNDCTL_DSP_SET_CHNORDER.
769*2a1ad637SFrançois Tigeot  */
770*2a1ad637SFrançois Tigeot int
771*2a1ad637SFrançois Tigeot feeder_matrix_oss_set_channel_order(struct pcmchan_matrix *m,
772*2a1ad637SFrançois Tigeot     unsigned long long *map)
773*2a1ad637SFrançois Tigeot {
774*2a1ad637SFrançois Tigeot 	struct pcmchan_matrix tmp;
775*2a1ad637SFrançois Tigeot 	uint32_t chmask, i;
776*2a1ad637SFrançois Tigeot 	int ch, cheof;
777*2a1ad637SFrançois Tigeot 
778*2a1ad637SFrançois Tigeot 	if (m == NULL || map == NULL || (m->mask & ~SND_CHN_OSS_VALIDMASK) ||
779*2a1ad637SFrançois Tigeot 	    m->channels > SND_CHN_OSS_MAX || (*map & 0xffffffff00000000ULL))
780*2a1ad637SFrançois Tigeot 		return (EINVAL);
781*2a1ad637SFrançois Tigeot 
782*2a1ad637SFrançois Tigeot 	tmp = *m;
783*2a1ad637SFrançois Tigeot 	tmp.channels = 0;
784*2a1ad637SFrançois Tigeot 	tmp.ext = 0;
785*2a1ad637SFrançois Tigeot 	tmp.mask = 0;
786*2a1ad637SFrançois Tigeot 	memset(tmp.offset, -1, sizeof(tmp.offset));
787*2a1ad637SFrançois Tigeot 	cheof = 0;
788*2a1ad637SFrançois Tigeot 
789*2a1ad637SFrançois Tigeot 	for (i = 0; i < SND_CHN_OSS_MAX; i++) {
790*2a1ad637SFrançois Tigeot 		ch = (*map >> (i * 4)) & 0xf;
791*2a1ad637SFrançois Tigeot 		if (ch < SND_CHN_OSS_BEGIN) {
792*2a1ad637SFrançois Tigeot 			if (cheof == 0 && m->map[i].type != SND_CHN_T_MAX)
793*2a1ad637SFrançois Tigeot 				return (EINVAL);
794*2a1ad637SFrançois Tigeot 			cheof++;
795*2a1ad637SFrançois Tigeot 			tmp.map[i] = m->map[i];
796*2a1ad637SFrançois Tigeot 			continue;
797*2a1ad637SFrançois Tigeot 		} else if (ch > SND_CHN_OSS_END)
798*2a1ad637SFrançois Tigeot 			return (EINVAL);
799*2a1ad637SFrançois Tigeot 		else if (cheof != 0)
800*2a1ad637SFrançois Tigeot 			return (EINVAL);
801*2a1ad637SFrançois Tigeot 		ch = oss_to_snd_chn[ch];
802*2a1ad637SFrançois Tigeot 		chmask = 1 << ch;
803*2a1ad637SFrançois Tigeot 		/* channel not exist in matrix */
804*2a1ad637SFrançois Tigeot 		if (!(chmask & m->mask))
805*2a1ad637SFrançois Tigeot 			return (EINVAL);
806*2a1ad637SFrançois Tigeot 		/* duplicated channel */
807*2a1ad637SFrançois Tigeot 		if (chmask & tmp.mask)
808*2a1ad637SFrançois Tigeot 			return (EINVAL);
809*2a1ad637SFrançois Tigeot 		tmp.map[i] = m->map[m->offset[ch]];
810*2a1ad637SFrançois Tigeot 		if (tmp.map[i].type != ch)
811*2a1ad637SFrançois Tigeot 			return (EINVAL);
812*2a1ad637SFrançois Tigeot 		tmp.offset[ch] = i;
813*2a1ad637SFrançois Tigeot 		tmp.mask |= chmask;
814*2a1ad637SFrançois Tigeot 		tmp.channels++;
815*2a1ad637SFrançois Tigeot 		if (chmask & SND_CHN_T_MASK_LF)
816*2a1ad637SFrançois Tigeot 			tmp.ext++;
817*2a1ad637SFrançois Tigeot 	}
818*2a1ad637SFrançois Tigeot 
819*2a1ad637SFrançois Tigeot 	if (tmp.channels != m->channels || tmp.ext != m->ext ||
820*2a1ad637SFrançois Tigeot 	    tmp.mask != m->mask ||
821*2a1ad637SFrançois Tigeot 	    tmp.map[m->channels].type != SND_CHN_T_MAX)
822*2a1ad637SFrançois Tigeot 		return (EINVAL);
823*2a1ad637SFrançois Tigeot 
824*2a1ad637SFrançois Tigeot 	*m = tmp;
825*2a1ad637SFrançois Tigeot 
826*2a1ad637SFrançois Tigeot 	return (0);
827*2a1ad637SFrançois Tigeot }
828