190da2b28SAriff Abdullah /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni *
490da2b28SAriff Abdullah * Copyright (c) 2008-2009 Ariff Abdullah <ariff@FreeBSD.org>
590da2b28SAriff Abdullah * All rights reserved.
690da2b28SAriff Abdullah *
790da2b28SAriff Abdullah * Redistribution and use in source and binary forms, with or without
890da2b28SAriff Abdullah * modification, are permitted provided that the following conditions
990da2b28SAriff Abdullah * are met:
1090da2b28SAriff Abdullah * 1. Redistributions of source code must retain the above copyright
1190da2b28SAriff Abdullah * notice, this list of conditions and the following disclaimer.
1290da2b28SAriff Abdullah * 2. Redistributions in binary form must reproduce the above copyright
1390da2b28SAriff Abdullah * notice, this list of conditions and the following disclaimer in the
1490da2b28SAriff Abdullah * documentation and/or other materials provided with the distribution.
1590da2b28SAriff Abdullah *
1690da2b28SAriff Abdullah * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1790da2b28SAriff Abdullah * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1890da2b28SAriff Abdullah * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1990da2b28SAriff Abdullah * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2090da2b28SAriff Abdullah * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2190da2b28SAriff Abdullah * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2290da2b28SAriff Abdullah * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2390da2b28SAriff Abdullah * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2490da2b28SAriff Abdullah * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2590da2b28SAriff Abdullah * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2690da2b28SAriff Abdullah * SUCH DAMAGE.
2790da2b28SAriff Abdullah */
2890da2b28SAriff Abdullah
2990da2b28SAriff Abdullah /*
3090da2b28SAriff Abdullah * feeder_matrix: Generic any-to-any channel matrixing. Probably not the
3190da2b28SAriff Abdullah * accurate way of doing things, but it should be fast and
3290da2b28SAriff Abdullah * transparent enough, not to mention capable of handling
3390da2b28SAriff Abdullah * possible non-standard way of multichannel interleaving
3490da2b28SAriff Abdullah * order. In other words, it is tough to break.
3590da2b28SAriff Abdullah *
3690da2b28SAriff Abdullah * The Good:
3790da2b28SAriff Abdullah * + very generic and compact, provided that the supplied matrix map is in a
3890da2b28SAriff Abdullah * sane form.
3990da2b28SAriff Abdullah * + should be fast enough.
4090da2b28SAriff Abdullah *
4190da2b28SAriff Abdullah * The Bad:
4290da2b28SAriff Abdullah * + somebody might disagree with it.
4390da2b28SAriff Abdullah * + 'matrix' is kind of 0x7a69, due to prolong mental block.
4490da2b28SAriff Abdullah */
4590da2b28SAriff Abdullah
4690da2b28SAriff Abdullah #ifdef _KERNEL
4790da2b28SAriff Abdullah #ifdef HAVE_KERNEL_OPTION_HEADERS
4890da2b28SAriff Abdullah #include "opt_snd.h"
4990da2b28SAriff Abdullah #endif
5090da2b28SAriff Abdullah #include <dev/sound/pcm/sound.h>
5190da2b28SAriff Abdullah #include <dev/sound/pcm/pcm.h>
5290da2b28SAriff Abdullah #include "feeder_if.h"
5390da2b28SAriff Abdullah
5490da2b28SAriff Abdullah #define SND_USE_FXDIV
5590da2b28SAriff Abdullah #include "snd_fxdiv_gen.h"
5690da2b28SAriff Abdullah #endif
5790da2b28SAriff Abdullah
5890da2b28SAriff Abdullah #define FEEDMATRIX_RESERVOIR (SND_CHN_MAX * PCM_32_BPS)
5990da2b28SAriff Abdullah
6090da2b28SAriff Abdullah #define SND_CHN_T_EOF 0x00e0fe0f
6190da2b28SAriff Abdullah #define SND_CHN_T_NULL 0x0e0e0e0e
6290da2b28SAriff Abdullah
6390da2b28SAriff Abdullah struct feed_matrix_info;
6490da2b28SAriff Abdullah
6590da2b28SAriff Abdullah typedef void (*feed_matrix_t)(struct feed_matrix_info *, uint8_t *,
6690da2b28SAriff Abdullah uint8_t *, uint32_t);
6790da2b28SAriff Abdullah
6890da2b28SAriff Abdullah struct feed_matrix_info {
6990da2b28SAriff Abdullah uint32_t bps;
7090da2b28SAriff Abdullah uint32_t ialign, oalign;
7190da2b28SAriff Abdullah uint32_t in, out;
7290da2b28SAriff Abdullah feed_matrix_t apply;
7390da2b28SAriff Abdullah #ifdef FEEDMATRIX_GENERIC
7490da2b28SAriff Abdullah intpcm_read_t *rd;
7590da2b28SAriff Abdullah intpcm_write_t *wr;
7690da2b28SAriff Abdullah #endif
7790da2b28SAriff Abdullah struct {
7890da2b28SAriff Abdullah int chn[SND_CHN_T_MAX + 1];
7990da2b28SAriff Abdullah int mul, shift;
8090da2b28SAriff Abdullah } matrix[SND_CHN_T_MAX + 1];
8190da2b28SAriff Abdullah uint8_t reservoir[FEEDMATRIX_RESERVOIR];
8290da2b28SAriff Abdullah };
8390da2b28SAriff Abdullah
8490da2b28SAriff Abdullah static struct pcmchan_matrix feeder_matrix_maps[SND_CHN_MATRIX_MAX] = {
8590da2b28SAriff Abdullah [SND_CHN_MATRIX_1_0] = SND_CHN_MATRIX_MAP_1_0,
8690da2b28SAriff Abdullah [SND_CHN_MATRIX_2_0] = SND_CHN_MATRIX_MAP_2_0,
8790da2b28SAriff Abdullah [SND_CHN_MATRIX_2_1] = SND_CHN_MATRIX_MAP_2_1,
8890da2b28SAriff Abdullah [SND_CHN_MATRIX_3_0] = SND_CHN_MATRIX_MAP_3_0,
891fc2a614SAlexander Motin [SND_CHN_MATRIX_3_1] = SND_CHN_MATRIX_MAP_3_1,
9090da2b28SAriff Abdullah [SND_CHN_MATRIX_4_0] = SND_CHN_MATRIX_MAP_4_0,
9190da2b28SAriff Abdullah [SND_CHN_MATRIX_4_1] = SND_CHN_MATRIX_MAP_4_1,
9290da2b28SAriff Abdullah [SND_CHN_MATRIX_5_0] = SND_CHN_MATRIX_MAP_5_0,
9390da2b28SAriff Abdullah [SND_CHN_MATRIX_5_1] = SND_CHN_MATRIX_MAP_5_1,
9490da2b28SAriff Abdullah [SND_CHN_MATRIX_6_0] = SND_CHN_MATRIX_MAP_6_0,
9590da2b28SAriff Abdullah [SND_CHN_MATRIX_6_1] = SND_CHN_MATRIX_MAP_6_1,
961fc2a614SAlexander Motin [SND_CHN_MATRIX_7_0] = SND_CHN_MATRIX_MAP_7_0,
9790da2b28SAriff Abdullah [SND_CHN_MATRIX_7_1] = SND_CHN_MATRIX_MAP_7_1
9890da2b28SAriff Abdullah };
9990da2b28SAriff Abdullah
10090da2b28SAriff Abdullah static int feeder_matrix_default_ids[9] = {
10190da2b28SAriff Abdullah [0] = SND_CHN_MATRIX_UNKNOWN,
10290da2b28SAriff Abdullah [1] = SND_CHN_MATRIX_1,
10390da2b28SAriff Abdullah [2] = SND_CHN_MATRIX_2,
10490da2b28SAriff Abdullah [3] = SND_CHN_MATRIX_3,
10590da2b28SAriff Abdullah [4] = SND_CHN_MATRIX_4,
10690da2b28SAriff Abdullah [5] = SND_CHN_MATRIX_5,
10790da2b28SAriff Abdullah [6] = SND_CHN_MATRIX_6,
10890da2b28SAriff Abdullah [7] = SND_CHN_MATRIX_7,
10990da2b28SAriff Abdullah [8] = SND_CHN_MATRIX_8
11090da2b28SAriff Abdullah };
11190da2b28SAriff Abdullah
11290da2b28SAriff Abdullah #ifdef _KERNEL
11390da2b28SAriff Abdullah #define FEEDMATRIX_CLIP_CHECK(...)
11490da2b28SAriff Abdullah #else
11590da2b28SAriff Abdullah #define FEEDMATRIX_CLIP_CHECK(v, BIT) do { \
11690da2b28SAriff Abdullah if ((v) < PCM_S##BIT##_MIN || (v) > PCM_S##BIT##_MAX) \
11790da2b28SAriff Abdullah errx(1, "\n\n%s(): Sample clipping: %jd\n", \
11890da2b28SAriff Abdullah __func__, (intmax_t)(v)); \
11990da2b28SAriff Abdullah } while (0)
12090da2b28SAriff Abdullah #endif
12190da2b28SAriff Abdullah
12290da2b28SAriff Abdullah #define FEEDMATRIX_DECLARE(SIGN, BIT, ENDIAN) \
12390da2b28SAriff Abdullah static void \
12490da2b28SAriff Abdullah feed_matrix_##SIGN##BIT##ENDIAN(struct feed_matrix_info *info, \
12590da2b28SAriff Abdullah uint8_t *src, uint8_t *dst, uint32_t count) \
12690da2b28SAriff Abdullah { \
12790da2b28SAriff Abdullah intpcm64_t accum; \
12890da2b28SAriff Abdullah intpcm_t v; \
12990da2b28SAriff Abdullah int i, j; \
13090da2b28SAriff Abdullah \
13190da2b28SAriff Abdullah do { \
13290da2b28SAriff Abdullah for (i = 0; info->matrix[i].chn[0] != SND_CHN_T_EOF; \
13390da2b28SAriff Abdullah i++) { \
13490da2b28SAriff Abdullah if (info->matrix[i].chn[0] == SND_CHN_T_NULL) { \
13590da2b28SAriff Abdullah _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, \
13690da2b28SAriff Abdullah 0); \
13790da2b28SAriff Abdullah dst += PCM_##BIT##_BPS; \
13890da2b28SAriff Abdullah continue; \
13990da2b28SAriff Abdullah } else if (info->matrix[i].chn[1] == \
14090da2b28SAriff Abdullah SND_CHN_T_EOF) { \
14190da2b28SAriff Abdullah v = _PCM_READ_##SIGN##BIT##_##ENDIAN( \
14290da2b28SAriff Abdullah src + info->matrix[i].chn[0]); \
14390da2b28SAriff Abdullah _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, \
14490da2b28SAriff Abdullah v); \
14590da2b28SAriff Abdullah dst += PCM_##BIT##_BPS; \
14690da2b28SAriff Abdullah continue; \
14790da2b28SAriff Abdullah } \
14890da2b28SAriff Abdullah \
14990da2b28SAriff Abdullah accum = 0; \
15090da2b28SAriff Abdullah for (j = 0; \
15190da2b28SAriff Abdullah info->matrix[i].chn[j] != SND_CHN_T_EOF; \
15290da2b28SAriff Abdullah j++) { \
15390da2b28SAriff Abdullah v = _PCM_READ_##SIGN##BIT##_##ENDIAN( \
15490da2b28SAriff Abdullah src + info->matrix[i].chn[j]); \
15590da2b28SAriff Abdullah accum += v; \
15690da2b28SAriff Abdullah } \
15790da2b28SAriff Abdullah \
15890da2b28SAriff Abdullah accum = (accum * info->matrix[i].mul) >> \
15990da2b28SAriff Abdullah info->matrix[i].shift; \
16090da2b28SAriff Abdullah \
16190da2b28SAriff Abdullah FEEDMATRIX_CLIP_CHECK(accum, BIT); \
16290da2b28SAriff Abdullah \
16390da2b28SAriff Abdullah v = (accum > PCM_S##BIT##_MAX) ? \
16490da2b28SAriff Abdullah PCM_S##BIT##_MAX : \
16590da2b28SAriff Abdullah ((accum < PCM_S##BIT##_MIN) ? \
16690da2b28SAriff Abdullah PCM_S##BIT##_MIN : \
16790da2b28SAriff Abdullah accum); \
16890da2b28SAriff Abdullah _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, v); \
16990da2b28SAriff Abdullah dst += PCM_##BIT##_BPS; \
17090da2b28SAriff Abdullah } \
17190da2b28SAriff Abdullah src += info->ialign; \
17290da2b28SAriff Abdullah } while (--count != 0); \
17390da2b28SAriff Abdullah }
17490da2b28SAriff Abdullah
17590da2b28SAriff Abdullah #if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
17690da2b28SAriff Abdullah FEEDMATRIX_DECLARE(S, 16, LE)
17790da2b28SAriff Abdullah FEEDMATRIX_DECLARE(S, 32, LE)
17890da2b28SAriff Abdullah #endif
17990da2b28SAriff Abdullah #if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
18090da2b28SAriff Abdullah FEEDMATRIX_DECLARE(S, 16, BE)
18190da2b28SAriff Abdullah FEEDMATRIX_DECLARE(S, 32, BE)
18290da2b28SAriff Abdullah #endif
18390da2b28SAriff Abdullah #ifdef SND_FEEDER_MULTIFORMAT
18490da2b28SAriff Abdullah FEEDMATRIX_DECLARE(S, 8, NE)
18590da2b28SAriff Abdullah FEEDMATRIX_DECLARE(S, 24, LE)
18690da2b28SAriff Abdullah FEEDMATRIX_DECLARE(S, 24, BE)
18790da2b28SAriff Abdullah FEEDMATRIX_DECLARE(U, 8, NE)
18890da2b28SAriff Abdullah FEEDMATRIX_DECLARE(U, 16, LE)
18990da2b28SAriff Abdullah FEEDMATRIX_DECLARE(U, 24, LE)
19090da2b28SAriff Abdullah FEEDMATRIX_DECLARE(U, 32, LE)
19190da2b28SAriff Abdullah FEEDMATRIX_DECLARE(U, 16, BE)
19290da2b28SAriff Abdullah FEEDMATRIX_DECLARE(U, 24, BE)
19390da2b28SAriff Abdullah FEEDMATRIX_DECLARE(U, 32, BE)
19490da2b28SAriff Abdullah #endif
19590da2b28SAriff Abdullah
19690da2b28SAriff Abdullah #define FEEDMATRIX_ENTRY(SIGN, BIT, ENDIAN) \
19790da2b28SAriff Abdullah { \
19890da2b28SAriff Abdullah AFMT_##SIGN##BIT##_##ENDIAN, \
19990da2b28SAriff Abdullah feed_matrix_##SIGN##BIT##ENDIAN \
20090da2b28SAriff Abdullah }
20190da2b28SAriff Abdullah
20290da2b28SAriff Abdullah static const struct {
20390da2b28SAriff Abdullah uint32_t format;
20490da2b28SAriff Abdullah feed_matrix_t apply;
20590da2b28SAriff Abdullah } feed_matrix_tab[] = {
20690da2b28SAriff Abdullah #if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
20790da2b28SAriff Abdullah FEEDMATRIX_ENTRY(S, 16, LE),
20890da2b28SAriff Abdullah FEEDMATRIX_ENTRY(S, 32, LE),
20990da2b28SAriff Abdullah #endif
21090da2b28SAriff Abdullah #if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
21190da2b28SAriff Abdullah FEEDMATRIX_ENTRY(S, 16, BE),
21290da2b28SAriff Abdullah FEEDMATRIX_ENTRY(S, 32, BE),
21390da2b28SAriff Abdullah #endif
21490da2b28SAriff Abdullah #ifdef SND_FEEDER_MULTIFORMAT
21590da2b28SAriff Abdullah FEEDMATRIX_ENTRY(S, 8, NE),
21690da2b28SAriff Abdullah FEEDMATRIX_ENTRY(S, 24, LE),
21790da2b28SAriff Abdullah FEEDMATRIX_ENTRY(S, 24, BE),
21890da2b28SAriff Abdullah FEEDMATRIX_ENTRY(U, 8, NE),
21990da2b28SAriff Abdullah FEEDMATRIX_ENTRY(U, 16, LE),
22090da2b28SAriff Abdullah FEEDMATRIX_ENTRY(U, 24, LE),
22190da2b28SAriff Abdullah FEEDMATRIX_ENTRY(U, 32, LE),
22290da2b28SAriff Abdullah FEEDMATRIX_ENTRY(U, 16, BE),
22390da2b28SAriff Abdullah FEEDMATRIX_ENTRY(U, 24, BE),
22490da2b28SAriff Abdullah FEEDMATRIX_ENTRY(U, 32, BE)
22590da2b28SAriff Abdullah #endif
22690da2b28SAriff Abdullah };
22790da2b28SAriff Abdullah
22890da2b28SAriff Abdullah static void
feed_matrix_reset(struct feed_matrix_info * info)22990da2b28SAriff Abdullah feed_matrix_reset(struct feed_matrix_info *info)
23090da2b28SAriff Abdullah {
23190da2b28SAriff Abdullah uint32_t i, j;
23290da2b28SAriff Abdullah
233*c597c557SChristos Margiolis for (i = 0; i < nitems(info->matrix); i++) {
23490da2b28SAriff Abdullah for (j = 0;
23590da2b28SAriff Abdullah j < (sizeof(info->matrix[i].chn) /
23690da2b28SAriff Abdullah sizeof(info->matrix[i].chn[0])); j++) {
23790da2b28SAriff Abdullah info->matrix[i].chn[j] = SND_CHN_T_EOF;
23890da2b28SAriff Abdullah }
23990da2b28SAriff Abdullah info->matrix[i].mul = 1;
24090da2b28SAriff Abdullah info->matrix[i].shift = 0;
24190da2b28SAriff Abdullah }
24290da2b28SAriff Abdullah }
24390da2b28SAriff Abdullah
24490da2b28SAriff Abdullah #ifdef FEEDMATRIX_GENERIC
24590da2b28SAriff Abdullah static void
feed_matrix_apply_generic(struct feed_matrix_info * info,uint8_t * src,uint8_t * dst,uint32_t count)24690da2b28SAriff Abdullah feed_matrix_apply_generic(struct feed_matrix_info *info,
24790da2b28SAriff Abdullah uint8_t *src, uint8_t *dst, uint32_t count)
24890da2b28SAriff Abdullah {
24990da2b28SAriff Abdullah intpcm64_t accum;
25090da2b28SAriff Abdullah intpcm_t v;
25190da2b28SAriff Abdullah int i, j;
25290da2b28SAriff Abdullah
25390da2b28SAriff Abdullah do {
25490da2b28SAriff Abdullah for (i = 0; info->matrix[i].chn[0] != SND_CHN_T_EOF;
25590da2b28SAriff Abdullah i++) {
25690da2b28SAriff Abdullah if (info->matrix[i].chn[0] == SND_CHN_T_NULL) {
25790da2b28SAriff Abdullah info->wr(dst, 0);
25890da2b28SAriff Abdullah dst += info->bps;
25990da2b28SAriff Abdullah continue;
26090da2b28SAriff Abdullah } else if (info->matrix[i].chn[1] ==
26190da2b28SAriff Abdullah SND_CHN_T_EOF) {
26290da2b28SAriff Abdullah v = info->rd(src + info->matrix[i].chn[0]);
26390da2b28SAriff Abdullah info->wr(dst, v);
26490da2b28SAriff Abdullah dst += info->bps;
26590da2b28SAriff Abdullah continue;
26690da2b28SAriff Abdullah }
26790da2b28SAriff Abdullah
26890da2b28SAriff Abdullah accum = 0;
26990da2b28SAriff Abdullah for (j = 0;
27090da2b28SAriff Abdullah info->matrix[i].chn[j] != SND_CHN_T_EOF;
27190da2b28SAriff Abdullah j++) {
27290da2b28SAriff Abdullah v = info->rd(src + info->matrix[i].chn[j]);
27390da2b28SAriff Abdullah accum += v;
27490da2b28SAriff Abdullah }
27590da2b28SAriff Abdullah
27690da2b28SAriff Abdullah accum = (accum * info->matrix[i].mul) >>
27790da2b28SAriff Abdullah info->matrix[i].shift;
27890da2b28SAriff Abdullah
27990da2b28SAriff Abdullah FEEDMATRIX_CLIP_CHECK(accum, 32);
28090da2b28SAriff Abdullah
28190da2b28SAriff Abdullah v = (accum > PCM_S32_MAX) ? PCM_S32_MAX :
28290da2b28SAriff Abdullah ((accum < PCM_S32_MIN) ? PCM_S32_MIN : accum);
28390da2b28SAriff Abdullah info->wr(dst, v);
28490da2b28SAriff Abdullah dst += info->bps;
28590da2b28SAriff Abdullah }
28690da2b28SAriff Abdullah src += info->ialign;
28790da2b28SAriff Abdullah } while (--count != 0);
28890da2b28SAriff Abdullah }
28990da2b28SAriff Abdullah #endif
29090da2b28SAriff Abdullah
29190da2b28SAriff Abdullah static int
feed_matrix_setup(struct feed_matrix_info * info,struct pcmchan_matrix * m_in,struct pcmchan_matrix * m_out)29290da2b28SAriff Abdullah feed_matrix_setup(struct feed_matrix_info *info, struct pcmchan_matrix *m_in,
29390da2b28SAriff Abdullah struct pcmchan_matrix *m_out)
29490da2b28SAriff Abdullah {
29590da2b28SAriff Abdullah uint32_t i, j, ch, in_mask, merge_mask;
29690da2b28SAriff Abdullah int mul, shift;
29790da2b28SAriff Abdullah
29890da2b28SAriff Abdullah if (info == NULL || m_in == NULL || m_out == NULL ||
29990da2b28SAriff Abdullah AFMT_CHANNEL(info->in) != m_in->channels ||
30090da2b28SAriff Abdullah AFMT_CHANNEL(info->out) != m_out->channels ||
30190da2b28SAriff Abdullah m_in->channels < SND_CHN_MIN || m_in->channels > SND_CHN_MAX ||
30290da2b28SAriff Abdullah m_out->channels < SND_CHN_MIN || m_out->channels > SND_CHN_MAX)
30390da2b28SAriff Abdullah return (EINVAL);
30490da2b28SAriff Abdullah
30590da2b28SAriff Abdullah feed_matrix_reset(info);
30690da2b28SAriff Abdullah
30790da2b28SAriff Abdullah /*
30890da2b28SAriff Abdullah * If both in and out are part of standard matrix and identical, skip
30970311ccfSGordon Bergling * everything altogether.
31090da2b28SAriff Abdullah */
31190da2b28SAriff Abdullah if (m_in->id == m_out->id && !(m_in->id < SND_CHN_MATRIX_BEGIN ||
31290da2b28SAriff Abdullah m_in->id > SND_CHN_MATRIX_END))
31390da2b28SAriff Abdullah return (0);
31490da2b28SAriff Abdullah
31590da2b28SAriff Abdullah /*
31690da2b28SAriff Abdullah * Special case for mono input matrix. If the output supports
31790da2b28SAriff Abdullah * possible 'center' channel, route it there. Otherwise, let it be
31890da2b28SAriff Abdullah * matrixed to left/right.
31990da2b28SAriff Abdullah */
32090da2b28SAriff Abdullah if (m_in->id == SND_CHN_MATRIX_1_0) {
32190da2b28SAriff Abdullah if (m_out->id == SND_CHN_MATRIX_1_0)
32290da2b28SAriff Abdullah in_mask = SND_CHN_T_MASK_FL;
32390da2b28SAriff Abdullah else if (m_out->mask & SND_CHN_T_MASK_FC)
32490da2b28SAriff Abdullah in_mask = SND_CHN_T_MASK_FC;
32590da2b28SAriff Abdullah else
32690da2b28SAriff Abdullah in_mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR;
32790da2b28SAriff Abdullah } else
32890da2b28SAriff Abdullah in_mask = m_in->mask;
32990da2b28SAriff Abdullah
33090da2b28SAriff Abdullah /* Merge, reduce, expand all possibilites. */
33190da2b28SAriff Abdullah for (ch = SND_CHN_T_BEGIN; ch <= SND_CHN_T_END &&
33290da2b28SAriff Abdullah m_out->map[ch].type != SND_CHN_T_MAX; ch += SND_CHN_T_STEP) {
33390da2b28SAriff Abdullah merge_mask = m_out->map[ch].members & in_mask;
33490da2b28SAriff Abdullah if (merge_mask == 0) {
33590da2b28SAriff Abdullah info->matrix[ch].chn[0] = SND_CHN_T_NULL;
33690da2b28SAriff Abdullah continue;
33790da2b28SAriff Abdullah }
33890da2b28SAriff Abdullah
33990da2b28SAriff Abdullah j = 0;
34090da2b28SAriff Abdullah for (i = SND_CHN_T_BEGIN; i <= SND_CHN_T_END;
34190da2b28SAriff Abdullah i += SND_CHN_T_STEP) {
34290da2b28SAriff Abdullah if (merge_mask & (1 << i)) {
34390da2b28SAriff Abdullah if (m_in->offset[i] >= 0 &&
34490da2b28SAriff Abdullah m_in->offset[i] < (int)m_in->channels)
34590da2b28SAriff Abdullah info->matrix[ch].chn[j++] =
34690da2b28SAriff Abdullah m_in->offset[i] * info->bps;
34790da2b28SAriff Abdullah else {
34890da2b28SAriff Abdullah info->matrix[ch].chn[j++] =
34990da2b28SAriff Abdullah SND_CHN_T_EOF;
35090da2b28SAriff Abdullah break;
35190da2b28SAriff Abdullah }
35290da2b28SAriff Abdullah }
35390da2b28SAriff Abdullah }
35490da2b28SAriff Abdullah
35590da2b28SAriff Abdullah #define FEEDMATRIX_ATTN_SHIFT 16
35690da2b28SAriff Abdullah
35790da2b28SAriff Abdullah if (j > 1) {
35890da2b28SAriff Abdullah /*
35990da2b28SAriff Abdullah * XXX For channel that require accumulation from
36090da2b28SAriff Abdullah * multiple channels, apply a slight attenuation to
36190da2b28SAriff Abdullah * avoid clipping.
36290da2b28SAriff Abdullah */
36390da2b28SAriff Abdullah mul = (1 << (FEEDMATRIX_ATTN_SHIFT - 1)) + 143 - j;
36490da2b28SAriff Abdullah shift = FEEDMATRIX_ATTN_SHIFT;
36590da2b28SAriff Abdullah while ((mul & 1) == 0 && shift > 0) {
36690da2b28SAriff Abdullah mul >>= 1;
36790da2b28SAriff Abdullah shift--;
36890da2b28SAriff Abdullah }
36990da2b28SAriff Abdullah info->matrix[ch].mul = mul;
37090da2b28SAriff Abdullah info->matrix[ch].shift = shift;
37190da2b28SAriff Abdullah }
37290da2b28SAriff Abdullah }
37390da2b28SAriff Abdullah
37490da2b28SAriff Abdullah #ifndef _KERNEL
37590da2b28SAriff Abdullah fprintf(stderr, "Total: %d\n", ch);
37690da2b28SAriff Abdullah
37790da2b28SAriff Abdullah for (i = 0; info->matrix[i].chn[0] != SND_CHN_T_EOF; i++) {
37890da2b28SAriff Abdullah fprintf(stderr, "%d: [", i);
37990da2b28SAriff Abdullah for (j = 0; info->matrix[i].chn[j] != SND_CHN_T_EOF; j++) {
38090da2b28SAriff Abdullah if (j != 0)
38190da2b28SAriff Abdullah fprintf(stderr, ", ");
38290da2b28SAriff Abdullah fprintf(stderr, "%d",
38390da2b28SAriff Abdullah (info->matrix[i].chn[j] == SND_CHN_T_NULL) ?
38490da2b28SAriff Abdullah 0xffffffff : info->matrix[i].chn[j] / info->bps);
38590da2b28SAriff Abdullah }
38690da2b28SAriff Abdullah fprintf(stderr, "] attn: (x * %d) >> %d\n",
38790da2b28SAriff Abdullah info->matrix[i].mul, info->matrix[i].shift);
38890da2b28SAriff Abdullah }
38990da2b28SAriff Abdullah #endif
39090da2b28SAriff Abdullah
39190da2b28SAriff Abdullah return (0);
39290da2b28SAriff Abdullah }
39390da2b28SAriff Abdullah
39490da2b28SAriff Abdullah static int
feed_matrix_init(struct pcm_feeder * f)39590da2b28SAriff Abdullah feed_matrix_init(struct pcm_feeder *f)
39690da2b28SAriff Abdullah {
39790da2b28SAriff Abdullah struct feed_matrix_info *info;
39890da2b28SAriff Abdullah struct pcmchan_matrix *m_in, *m_out;
39990da2b28SAriff Abdullah uint32_t i;
40090da2b28SAriff Abdullah int ret;
40190da2b28SAriff Abdullah
40290da2b28SAriff Abdullah if (AFMT_ENCODING(f->desc->in) != AFMT_ENCODING(f->desc->out))
40390da2b28SAriff Abdullah return (EINVAL);
40490da2b28SAriff Abdullah
40590da2b28SAriff Abdullah info = malloc(sizeof(*info), M_DEVBUF, M_NOWAIT | M_ZERO);
40690da2b28SAriff Abdullah if (info == NULL)
40790da2b28SAriff Abdullah return (ENOMEM);
40890da2b28SAriff Abdullah
40990da2b28SAriff Abdullah info->in = f->desc->in;
41090da2b28SAriff Abdullah info->out = f->desc->out;
41190da2b28SAriff Abdullah info->bps = AFMT_BPS(info->in);
41290da2b28SAriff Abdullah info->ialign = AFMT_ALIGN(info->in);
41390da2b28SAriff Abdullah info->oalign = AFMT_ALIGN(info->out);
41490da2b28SAriff Abdullah info->apply = NULL;
41590da2b28SAriff Abdullah
41690da2b28SAriff Abdullah for (i = 0; info->apply == NULL &&
41790da2b28SAriff Abdullah i < (sizeof(feed_matrix_tab) / sizeof(feed_matrix_tab[0])); i++) {
41890da2b28SAriff Abdullah if (AFMT_ENCODING(info->in) == feed_matrix_tab[i].format)
41990da2b28SAriff Abdullah info->apply = feed_matrix_tab[i].apply;
42090da2b28SAriff Abdullah }
42190da2b28SAriff Abdullah
42290da2b28SAriff Abdullah if (info->apply == NULL) {
42390da2b28SAriff Abdullah #ifdef FEEDMATRIX_GENERIC
42490da2b28SAriff Abdullah info->rd = feeder_format_read_op(info->in);
42590da2b28SAriff Abdullah info->wr = feeder_format_write_op(info->out);
42690da2b28SAriff Abdullah if (info->rd == NULL || info->wr == NULL) {
42790da2b28SAriff Abdullah free(info, M_DEVBUF);
42890da2b28SAriff Abdullah return (EINVAL);
42990da2b28SAriff Abdullah }
43090da2b28SAriff Abdullah info->apply = feed_matrix_apply_generic;
43190da2b28SAriff Abdullah #else
43290da2b28SAriff Abdullah free(info, M_DEVBUF);
43390da2b28SAriff Abdullah return (EINVAL);
43490da2b28SAriff Abdullah #endif
43590da2b28SAriff Abdullah }
43690da2b28SAriff Abdullah
43790da2b28SAriff Abdullah m_in = feeder_matrix_format_map(info->in);
43890da2b28SAriff Abdullah m_out = feeder_matrix_format_map(info->out);
43990da2b28SAriff Abdullah
44090da2b28SAriff Abdullah ret = feed_matrix_setup(info, m_in, m_out);
44190da2b28SAriff Abdullah if (ret != 0) {
44290da2b28SAriff Abdullah free(info, M_DEVBUF);
44390da2b28SAriff Abdullah return (ret);
44490da2b28SAriff Abdullah }
44590da2b28SAriff Abdullah
44690da2b28SAriff Abdullah f->data = info;
44790da2b28SAriff Abdullah
44890da2b28SAriff Abdullah return (0);
44990da2b28SAriff Abdullah }
45090da2b28SAriff Abdullah
45190da2b28SAriff Abdullah static int
feed_matrix_free(struct pcm_feeder * f)45290da2b28SAriff Abdullah feed_matrix_free(struct pcm_feeder *f)
45390da2b28SAriff Abdullah {
45490da2b28SAriff Abdullah struct feed_matrix_info *info;
45590da2b28SAriff Abdullah
45690da2b28SAriff Abdullah info = f->data;
45790da2b28SAriff Abdullah if (info != NULL)
45890da2b28SAriff Abdullah free(info, M_DEVBUF);
45990da2b28SAriff Abdullah
46090da2b28SAriff Abdullah f->data = NULL;
46190da2b28SAriff Abdullah
46290da2b28SAriff Abdullah return (0);
46390da2b28SAriff Abdullah }
46490da2b28SAriff Abdullah
46590da2b28SAriff Abdullah static int
feed_matrix_feed(struct pcm_feeder * f,struct pcm_channel * c,uint8_t * b,uint32_t count,void * source)46690da2b28SAriff Abdullah feed_matrix_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
46790da2b28SAriff Abdullah uint32_t count, void *source)
46890da2b28SAriff Abdullah {
46990da2b28SAriff Abdullah struct feed_matrix_info *info;
47090da2b28SAriff Abdullah uint32_t j, inmax;
47190da2b28SAriff Abdullah uint8_t *src, *dst;
47290da2b28SAriff Abdullah
47390da2b28SAriff Abdullah info = f->data;
47490da2b28SAriff Abdullah if (info->matrix[0].chn[0] == SND_CHN_T_EOF)
47590da2b28SAriff Abdullah return (FEEDER_FEED(f->source, c, b, count, source));
47690da2b28SAriff Abdullah
47790da2b28SAriff Abdullah dst = b;
47890da2b28SAriff Abdullah count = SND_FXROUND(count, info->oalign);
47990da2b28SAriff Abdullah inmax = info->ialign + info->oalign;
48090da2b28SAriff Abdullah
48190da2b28SAriff Abdullah /*
48290da2b28SAriff Abdullah * This loop might look simmilar to other feeder_* loops, but be
48390da2b28SAriff Abdullah * advised: matrixing might involve overlapping (think about
48490da2b28SAriff Abdullah * swapping end to front or something like that). In this regard it
48590da2b28SAriff Abdullah * might be simmilar to feeder_format, but feeder_format works on
48690da2b28SAriff Abdullah * 'sample' domain where it can be fitted into single 32bit integer
48790da2b28SAriff Abdullah * while matrixing works on 'sample frame' domain.
48890da2b28SAriff Abdullah */
48990da2b28SAriff Abdullah do {
49090da2b28SAriff Abdullah if (count < info->oalign)
49190da2b28SAriff Abdullah break;
49290da2b28SAriff Abdullah
49390da2b28SAriff Abdullah if (count < inmax) {
49490da2b28SAriff Abdullah src = info->reservoir;
49590da2b28SAriff Abdullah j = info->ialign;
49690da2b28SAriff Abdullah } else {
49790da2b28SAriff Abdullah if (info->ialign == info->oalign)
49890da2b28SAriff Abdullah j = count - info->oalign;
49990da2b28SAriff Abdullah else if (info->ialign > info->oalign)
50090da2b28SAriff Abdullah j = SND_FXROUND(count - info->oalign,
50190da2b28SAriff Abdullah info->ialign);
50290da2b28SAriff Abdullah else
50390da2b28SAriff Abdullah j = (SND_FXDIV(count, info->oalign) - 1) *
50490da2b28SAriff Abdullah info->ialign;
50590da2b28SAriff Abdullah src = dst + count - j;
50690da2b28SAriff Abdullah }
50790da2b28SAriff Abdullah
50890da2b28SAriff Abdullah j = SND_FXDIV(FEEDER_FEED(f->source, c, src, j, source),
50990da2b28SAriff Abdullah info->ialign);
51090da2b28SAriff Abdullah if (j == 0)
51190da2b28SAriff Abdullah break;
51290da2b28SAriff Abdullah
51390da2b28SAriff Abdullah info->apply(info, src, dst, j);
51490da2b28SAriff Abdullah
51590da2b28SAriff Abdullah j *= info->oalign;
51690da2b28SAriff Abdullah dst += j;
51790da2b28SAriff Abdullah count -= j;
51890da2b28SAriff Abdullah
51990da2b28SAriff Abdullah } while (count != 0);
52090da2b28SAriff Abdullah
52190da2b28SAriff Abdullah return (dst - b);
52290da2b28SAriff Abdullah }
52390da2b28SAriff Abdullah
52490da2b28SAriff Abdullah static struct pcm_feederdesc feeder_matrix_desc[] = {
52590da2b28SAriff Abdullah { FEEDER_MATRIX, 0, 0, 0, 0 },
52690da2b28SAriff Abdullah { 0, 0, 0, 0, 0 }
52790da2b28SAriff Abdullah };
52890da2b28SAriff Abdullah
52990da2b28SAriff Abdullah static kobj_method_t feeder_matrix_methods[] = {
53090da2b28SAriff Abdullah KOBJMETHOD(feeder_init, feed_matrix_init),
53190da2b28SAriff Abdullah KOBJMETHOD(feeder_free, feed_matrix_free),
53290da2b28SAriff Abdullah KOBJMETHOD(feeder_feed, feed_matrix_feed),
53390da2b28SAriff Abdullah KOBJMETHOD_END
53490da2b28SAriff Abdullah };
53590da2b28SAriff Abdullah
53690da2b28SAriff Abdullah FEEDER_DECLARE(feeder_matrix, NULL);
53790da2b28SAriff Abdullah
53890da2b28SAriff Abdullah /* External */
53990da2b28SAriff Abdullah int
feeder_matrix_setup(struct pcm_feeder * f,struct pcmchan_matrix * m_in,struct pcmchan_matrix * m_out)54090da2b28SAriff Abdullah feeder_matrix_setup(struct pcm_feeder *f, struct pcmchan_matrix *m_in,
54190da2b28SAriff Abdullah struct pcmchan_matrix *m_out)
54290da2b28SAriff Abdullah {
54390da2b28SAriff Abdullah
54490da2b28SAriff Abdullah if (f == NULL || f->desc == NULL || f->desc->type != FEEDER_MATRIX ||
54590da2b28SAriff Abdullah f->data == NULL)
54690da2b28SAriff Abdullah return (EINVAL);
54790da2b28SAriff Abdullah
54890da2b28SAriff Abdullah return (feed_matrix_setup(f->data, m_in, m_out));
54990da2b28SAriff Abdullah }
55090da2b28SAriff Abdullah
55190da2b28SAriff Abdullah /*
55290da2b28SAriff Abdullah * feeder_matrix_default_id(): For a given number of channels, return
553513ee901SGordon Bergling * default preferred id (example: both 5.1 and
55490da2b28SAriff Abdullah * 6.0 are simply 6 channels, but 5.1 is more
55590da2b28SAriff Abdullah * preferable).
55690da2b28SAriff Abdullah */
55790da2b28SAriff Abdullah int
feeder_matrix_default_id(uint32_t ch)55890da2b28SAriff Abdullah feeder_matrix_default_id(uint32_t ch)
55990da2b28SAriff Abdullah {
56090da2b28SAriff Abdullah
56190da2b28SAriff Abdullah if (ch < feeder_matrix_maps[SND_CHN_MATRIX_BEGIN].channels ||
56290da2b28SAriff Abdullah ch > feeder_matrix_maps[SND_CHN_MATRIX_END].channels)
56390da2b28SAriff Abdullah return (SND_CHN_MATRIX_UNKNOWN);
56490da2b28SAriff Abdullah
56590da2b28SAriff Abdullah return (feeder_matrix_maps[feeder_matrix_default_ids[ch]].id);
56690da2b28SAriff Abdullah }
56790da2b28SAriff Abdullah
56890da2b28SAriff Abdullah /*
56990da2b28SAriff Abdullah * feeder_matrix_default_channel_map(): Ditto, but return matrix map
57090da2b28SAriff Abdullah * instead.
57190da2b28SAriff Abdullah */
57290da2b28SAriff Abdullah struct pcmchan_matrix *
feeder_matrix_default_channel_map(uint32_t ch)57390da2b28SAriff Abdullah feeder_matrix_default_channel_map(uint32_t ch)
57490da2b28SAriff Abdullah {
57590da2b28SAriff Abdullah
57690da2b28SAriff Abdullah if (ch < feeder_matrix_maps[SND_CHN_MATRIX_BEGIN].channels ||
57790da2b28SAriff Abdullah ch > feeder_matrix_maps[SND_CHN_MATRIX_END].channels)
57890da2b28SAriff Abdullah return (NULL);
57990da2b28SAriff Abdullah
58090da2b28SAriff Abdullah return (&feeder_matrix_maps[feeder_matrix_default_ids[ch]]);
58190da2b28SAriff Abdullah }
58290da2b28SAriff Abdullah
58390da2b28SAriff Abdullah /*
58490da2b28SAriff Abdullah * feeder_matrix_default_format(): For a given audio format, return the
58590da2b28SAriff Abdullah * proper audio format based on preferable
58690da2b28SAriff Abdullah * matrix.
58790da2b28SAriff Abdullah */
58890da2b28SAriff Abdullah uint32_t
feeder_matrix_default_format(uint32_t format)58990da2b28SAriff Abdullah feeder_matrix_default_format(uint32_t format)
59090da2b28SAriff Abdullah {
59190da2b28SAriff Abdullah struct pcmchan_matrix *m;
59290da2b28SAriff Abdullah uint32_t i, ch, ext;
59390da2b28SAriff Abdullah
59490da2b28SAriff Abdullah ch = AFMT_CHANNEL(format);
59590da2b28SAriff Abdullah ext = AFMT_EXTCHANNEL(format);
59690da2b28SAriff Abdullah
59790da2b28SAriff Abdullah if (ext != 0) {
59890da2b28SAriff Abdullah for (i = SND_CHN_MATRIX_BEGIN; i <= SND_CHN_MATRIX_END; i++) {
59990da2b28SAriff Abdullah if (feeder_matrix_maps[i].channels == ch &&
60090da2b28SAriff Abdullah feeder_matrix_maps[i].ext == ext)
60190da2b28SAriff Abdullah return (SND_FORMAT(format, ch, ext));
60290da2b28SAriff Abdullah }
60390da2b28SAriff Abdullah }
60490da2b28SAriff Abdullah
60590da2b28SAriff Abdullah m = feeder_matrix_default_channel_map(ch);
60690da2b28SAriff Abdullah if (m == NULL)
60790da2b28SAriff Abdullah return (0x00000000);
60890da2b28SAriff Abdullah
60990da2b28SAriff Abdullah return (SND_FORMAT(format, ch, m->ext));
61090da2b28SAriff Abdullah }
61190da2b28SAriff Abdullah
61290da2b28SAriff Abdullah /*
61390da2b28SAriff Abdullah * feeder_matrix_format_id(): For a given audio format, return its matrix
61490da2b28SAriff Abdullah * id.
61590da2b28SAriff Abdullah */
61690da2b28SAriff Abdullah int
feeder_matrix_format_id(uint32_t format)61790da2b28SAriff Abdullah feeder_matrix_format_id(uint32_t format)
61890da2b28SAriff Abdullah {
61990da2b28SAriff Abdullah uint32_t i, ch, ext;
62090da2b28SAriff Abdullah
62190da2b28SAriff Abdullah ch = AFMT_CHANNEL(format);
62290da2b28SAriff Abdullah ext = AFMT_EXTCHANNEL(format);
62390da2b28SAriff Abdullah
62490da2b28SAriff Abdullah for (i = SND_CHN_MATRIX_BEGIN; i <= SND_CHN_MATRIX_END; i++) {
62590da2b28SAriff Abdullah if (feeder_matrix_maps[i].channels == ch &&
62690da2b28SAriff Abdullah feeder_matrix_maps[i].ext == ext)
62790da2b28SAriff Abdullah return (feeder_matrix_maps[i].id);
62890da2b28SAriff Abdullah }
62990da2b28SAriff Abdullah
63090da2b28SAriff Abdullah return (SND_CHN_MATRIX_UNKNOWN);
63190da2b28SAriff Abdullah }
63290da2b28SAriff Abdullah
63390da2b28SAriff Abdullah /*
63490da2b28SAriff Abdullah * feeder_matrix_format_map(): For a given audio format, return its matrix
63590da2b28SAriff Abdullah * map.
63690da2b28SAriff Abdullah */
63790da2b28SAriff Abdullah struct pcmchan_matrix *
feeder_matrix_format_map(uint32_t format)63890da2b28SAriff Abdullah feeder_matrix_format_map(uint32_t format)
63990da2b28SAriff Abdullah {
64090da2b28SAriff Abdullah uint32_t i, ch, ext;
64190da2b28SAriff Abdullah
64290da2b28SAriff Abdullah ch = AFMT_CHANNEL(format);
64390da2b28SAriff Abdullah ext = AFMT_EXTCHANNEL(format);
64490da2b28SAriff Abdullah
64590da2b28SAriff Abdullah for (i = SND_CHN_MATRIX_BEGIN; i <= SND_CHN_MATRIX_END; i++) {
64690da2b28SAriff Abdullah if (feeder_matrix_maps[i].channels == ch &&
64790da2b28SAriff Abdullah feeder_matrix_maps[i].ext == ext)
64890da2b28SAriff Abdullah return (&feeder_matrix_maps[i]);
64990da2b28SAriff Abdullah }
65090da2b28SAriff Abdullah
65190da2b28SAriff Abdullah return (NULL);
65290da2b28SAriff Abdullah }
65390da2b28SAriff Abdullah
65490da2b28SAriff Abdullah /*
65590da2b28SAriff Abdullah * feeder_matrix_id_map(): For a given matrix id, return its matrix map.
65690da2b28SAriff Abdullah */
65790da2b28SAriff Abdullah struct pcmchan_matrix *
feeder_matrix_id_map(int id)65890da2b28SAriff Abdullah feeder_matrix_id_map(int id)
65990da2b28SAriff Abdullah {
66090da2b28SAriff Abdullah
66190da2b28SAriff Abdullah if (id < SND_CHN_MATRIX_BEGIN || id > SND_CHN_MATRIX_END)
66290da2b28SAriff Abdullah return (NULL);
66390da2b28SAriff Abdullah
66490da2b28SAriff Abdullah return (&feeder_matrix_maps[id]);
66590da2b28SAriff Abdullah }
66690da2b28SAriff Abdullah
66790da2b28SAriff Abdullah /*
66890da2b28SAriff Abdullah * feeder_matrix_compare(): Compare the simmilarities of matrices.
66990da2b28SAriff Abdullah */
67090da2b28SAriff Abdullah int
feeder_matrix_compare(struct pcmchan_matrix * m_in,struct pcmchan_matrix * m_out)67190da2b28SAriff Abdullah feeder_matrix_compare(struct pcmchan_matrix *m_in, struct pcmchan_matrix *m_out)
67290da2b28SAriff Abdullah {
67390da2b28SAriff Abdullah uint32_t i;
67490da2b28SAriff Abdullah
67590da2b28SAriff Abdullah if (m_in == m_out)
67690da2b28SAriff Abdullah return (0);
67790da2b28SAriff Abdullah
67890da2b28SAriff Abdullah if (m_in->channels != m_out->channels || m_in->ext != m_out->ext ||
67990da2b28SAriff Abdullah m_in->mask != m_out->mask)
68090da2b28SAriff Abdullah return (1);
68190da2b28SAriff Abdullah
682*c597c557SChristos Margiolis for (i = 0; i < nitems(m_in->map); i++) {
68390da2b28SAriff Abdullah if (m_in->map[i].type != m_out->map[i].type)
68490da2b28SAriff Abdullah return (1);
68590da2b28SAriff Abdullah if (m_in->map[i].type == SND_CHN_T_MAX)
68690da2b28SAriff Abdullah break;
68790da2b28SAriff Abdullah if (m_in->map[i].members != m_out->map[i].members)
68890da2b28SAriff Abdullah return (1);
68990da2b28SAriff Abdullah if (i <= SND_CHN_T_END) {
69090da2b28SAriff Abdullah if (m_in->offset[m_in->map[i].type] !=
69190da2b28SAriff Abdullah m_out->offset[m_out->map[i].type])
69290da2b28SAriff Abdullah return (1);
69390da2b28SAriff Abdullah }
69490da2b28SAriff Abdullah }
69590da2b28SAriff Abdullah
69690da2b28SAriff Abdullah return (0);
69790da2b28SAriff Abdullah }
69890da2b28SAriff Abdullah
69990da2b28SAriff Abdullah /*
70058d868c8SGordon Bergling * XXX 4front interpretation of "surround" is ambigous and sort of
70190da2b28SAriff Abdullah * conflicting with "rear"/"back". Map it to "side". Well..
70290da2b28SAriff Abdullah * who cares?
70390da2b28SAriff Abdullah */
70490da2b28SAriff Abdullah static int snd_chn_to_oss[SND_CHN_T_MAX] = {
70590da2b28SAriff Abdullah [SND_CHN_T_FL] = CHID_L,
70690da2b28SAriff Abdullah [SND_CHN_T_FR] = CHID_R,
70790da2b28SAriff Abdullah [SND_CHN_T_FC] = CHID_C,
70890da2b28SAriff Abdullah [SND_CHN_T_LF] = CHID_LFE,
70990da2b28SAriff Abdullah [SND_CHN_T_SL] = CHID_LS,
71090da2b28SAriff Abdullah [SND_CHN_T_SR] = CHID_RS,
71190da2b28SAriff Abdullah [SND_CHN_T_BL] = CHID_LR,
71290da2b28SAriff Abdullah [SND_CHN_T_BR] = CHID_RR
71390da2b28SAriff Abdullah };
71490da2b28SAriff Abdullah
71590da2b28SAriff Abdullah #define SND_CHN_OSS_VALIDMASK \
71690da2b28SAriff Abdullah (SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \
71790da2b28SAriff Abdullah SND_CHN_T_MASK_FC | SND_CHN_T_MASK_LF | \
71890da2b28SAriff Abdullah SND_CHN_T_MASK_SL | SND_CHN_T_MASK_SR | \
71990da2b28SAriff Abdullah SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR)
72090da2b28SAriff Abdullah
72190da2b28SAriff Abdullah #define SND_CHN_OSS_MAX 8
72290da2b28SAriff Abdullah #define SND_CHN_OSS_BEGIN CHID_L
72390da2b28SAriff Abdullah #define SND_CHN_OSS_END CHID_RR
72490da2b28SAriff Abdullah
72590da2b28SAriff Abdullah static int oss_to_snd_chn[SND_CHN_OSS_END + 1] = {
72690da2b28SAriff Abdullah [CHID_L] = SND_CHN_T_FL,
72790da2b28SAriff Abdullah [CHID_R] = SND_CHN_T_FR,
72890da2b28SAriff Abdullah [CHID_C] = SND_CHN_T_FC,
72990da2b28SAriff Abdullah [CHID_LFE] = SND_CHN_T_LF,
73090da2b28SAriff Abdullah [CHID_LS] = SND_CHN_T_SL,
73190da2b28SAriff Abdullah [CHID_RS] = SND_CHN_T_SR,
73290da2b28SAriff Abdullah [CHID_LR] = SND_CHN_T_BL,
73390da2b28SAriff Abdullah [CHID_RR] = SND_CHN_T_BR
73490da2b28SAriff Abdullah };
73590da2b28SAriff Abdullah
73690da2b28SAriff Abdullah /*
73790da2b28SAriff Abdullah * Used by SNDCTL_DSP_GET_CHNORDER.
73890da2b28SAriff Abdullah */
73990da2b28SAriff Abdullah int
feeder_matrix_oss_get_channel_order(struct pcmchan_matrix * m,unsigned long long * map)74090da2b28SAriff Abdullah feeder_matrix_oss_get_channel_order(struct pcmchan_matrix *m,
74190da2b28SAriff Abdullah unsigned long long *map)
74290da2b28SAriff Abdullah {
74390da2b28SAriff Abdullah unsigned long long tmpmap;
74490da2b28SAriff Abdullah uint32_t i;
74590da2b28SAriff Abdullah
74690da2b28SAriff Abdullah if (m == NULL || map == NULL || (m->mask & ~SND_CHN_OSS_VALIDMASK) ||
74790da2b28SAriff Abdullah m->channels > SND_CHN_OSS_MAX)
74890da2b28SAriff Abdullah return (EINVAL);
74990da2b28SAriff Abdullah
75090da2b28SAriff Abdullah tmpmap = 0x0000000000000000ULL;
75190da2b28SAriff Abdullah
752336c5fb5SPedro F. Giffuni for (i = 0; i < SND_CHN_OSS_MAX && m->map[i].type != SND_CHN_T_MAX;
753336c5fb5SPedro F. Giffuni i++) {
75490da2b28SAriff Abdullah if ((1 << m->map[i].type) & ~SND_CHN_OSS_VALIDMASK)
75590da2b28SAriff Abdullah return (EINVAL);
75690da2b28SAriff Abdullah tmpmap |=
75790da2b28SAriff Abdullah (unsigned long long)snd_chn_to_oss[m->map[i].type] <<
75890da2b28SAriff Abdullah (i * 4);
75990da2b28SAriff Abdullah }
76090da2b28SAriff Abdullah
76190da2b28SAriff Abdullah *map = tmpmap;
76290da2b28SAriff Abdullah
76390da2b28SAriff Abdullah return (0);
76490da2b28SAriff Abdullah }
76590da2b28SAriff Abdullah
76690da2b28SAriff Abdullah /*
76790da2b28SAriff Abdullah * Used by SNDCTL_DSP_SET_CHNORDER.
76890da2b28SAriff Abdullah */
76990da2b28SAriff Abdullah int
feeder_matrix_oss_set_channel_order(struct pcmchan_matrix * m,unsigned long long * map)77090da2b28SAriff Abdullah feeder_matrix_oss_set_channel_order(struct pcmchan_matrix *m,
77190da2b28SAriff Abdullah unsigned long long *map)
77290da2b28SAriff Abdullah {
77390da2b28SAriff Abdullah struct pcmchan_matrix tmp;
77490da2b28SAriff Abdullah uint32_t chmask, i;
77590da2b28SAriff Abdullah int ch, cheof;
77690da2b28SAriff Abdullah
77790da2b28SAriff Abdullah if (m == NULL || map == NULL || (m->mask & ~SND_CHN_OSS_VALIDMASK) ||
77890da2b28SAriff Abdullah m->channels > SND_CHN_OSS_MAX || (*map & 0xffffffff00000000ULL))
77990da2b28SAriff Abdullah return (EINVAL);
78090da2b28SAriff Abdullah
78190da2b28SAriff Abdullah tmp = *m;
78290da2b28SAriff Abdullah tmp.channels = 0;
78390da2b28SAriff Abdullah tmp.ext = 0;
78490da2b28SAriff Abdullah tmp.mask = 0;
78590da2b28SAriff Abdullah memset(tmp.offset, -1, sizeof(tmp.offset));
78690da2b28SAriff Abdullah cheof = 0;
78790da2b28SAriff Abdullah
78890da2b28SAriff Abdullah for (i = 0; i < SND_CHN_OSS_MAX; i++) {
78990da2b28SAriff Abdullah ch = (*map >> (i * 4)) & 0xf;
79090da2b28SAriff Abdullah if (ch < SND_CHN_OSS_BEGIN) {
79190da2b28SAriff Abdullah if (cheof == 0 && m->map[i].type != SND_CHN_T_MAX)
79290da2b28SAriff Abdullah return (EINVAL);
79390da2b28SAriff Abdullah cheof++;
79490da2b28SAriff Abdullah tmp.map[i] = m->map[i];
79590da2b28SAriff Abdullah continue;
79690da2b28SAriff Abdullah } else if (ch > SND_CHN_OSS_END)
79790da2b28SAriff Abdullah return (EINVAL);
79890da2b28SAriff Abdullah else if (cheof != 0)
79990da2b28SAriff Abdullah return (EINVAL);
80090da2b28SAriff Abdullah ch = oss_to_snd_chn[ch];
80190da2b28SAriff Abdullah chmask = 1 << ch;
80290da2b28SAriff Abdullah /* channel not exist in matrix */
80390da2b28SAriff Abdullah if (!(chmask & m->mask))
80490da2b28SAriff Abdullah return (EINVAL);
80590da2b28SAriff Abdullah /* duplicated channel */
80690da2b28SAriff Abdullah if (chmask & tmp.mask)
80790da2b28SAriff Abdullah return (EINVAL);
80890da2b28SAriff Abdullah tmp.map[i] = m->map[m->offset[ch]];
80990da2b28SAriff Abdullah if (tmp.map[i].type != ch)
81090da2b28SAriff Abdullah return (EINVAL);
81190da2b28SAriff Abdullah tmp.offset[ch] = i;
81290da2b28SAriff Abdullah tmp.mask |= chmask;
81390da2b28SAriff Abdullah tmp.channels++;
81490da2b28SAriff Abdullah if (chmask & SND_CHN_T_MASK_LF)
81590da2b28SAriff Abdullah tmp.ext++;
81690da2b28SAriff Abdullah }
81790da2b28SAriff Abdullah
81890da2b28SAriff Abdullah if (tmp.channels != m->channels || tmp.ext != m->ext ||
81990da2b28SAriff Abdullah tmp.mask != m->mask ||
82090da2b28SAriff Abdullah tmp.map[m->channels].type != SND_CHN_T_MAX)
82190da2b28SAriff Abdullah return (EINVAL);
82290da2b28SAriff Abdullah
82390da2b28SAriff Abdullah *m = tmp;
82490da2b28SAriff Abdullah
82590da2b28SAriff Abdullah return (0);
82690da2b28SAriff Abdullah }
827