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 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 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 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 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 406*67931cc4SFrançois Tigeot info = kmalloc(sizeof(*info), M_DEVBUF, M_NOWAIT | 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) { 428*67931cc4SFranç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 433*67931cc4SFranç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) { 443*67931cc4SFranç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 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) 459*67931cc4SFranç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 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 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 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 * 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 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 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 * 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 * 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 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 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 7532a1ad637SFrançois Tigeot for (i = 0; m->map[i].type != SND_CHN_T_MAX && 7542a1ad637SFrançois Tigeot i < SND_CHN_OSS_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 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