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_format: New generation of generic, any-to-any format converter, as
292a1ad637SFrançois Tigeot * long as the sample values can be read _and_ write.
302a1ad637SFrançois Tigeot */
312a1ad637SFrançois Tigeot
322a1ad637SFrançois Tigeot #ifdef _KERNEL
332a1ad637SFrançois Tigeot #ifdef HAVE_KERNEL_OPTION_HEADERS
342a1ad637SFrançois Tigeot #include "opt_snd.h"
352a1ad637SFrançois Tigeot #endif
362a1ad637SFrançois Tigeot #include <dev/sound/pcm/sound.h>
372a1ad637SFrançois Tigeot #include <dev/sound/pcm/pcm.h>
382a1ad637SFrançois Tigeot #include <dev/sound/pcm/g711.h>
392a1ad637SFrançois Tigeot #include <dev/sound/pcm/intpcm.h>
402a1ad637SFrançois Tigeot #include "feeder_if.h"
412a1ad637SFrançois Tigeot
422a1ad637SFrançois Tigeot #define SND_USE_FXDIV
432a1ad637SFrançois Tigeot #include "snd_fxdiv_gen.h"
442a1ad637SFrançois Tigeot
452a1ad637SFrançois Tigeot SND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/feeder_format.c 193640 2009-06-07 19:12:08Z ariff $");
462a1ad637SFrançois Tigeot #endif
472a1ad637SFrançois Tigeot
482a1ad637SFrançois Tigeot #define FEEDFORMAT_RESERVOIR (SND_CHN_MAX * PCM_32_BPS)
492a1ad637SFrançois Tigeot
502a1ad637SFrançois Tigeot INTPCM_DECLARE(intpcm_conv_tables)
512a1ad637SFrançois Tigeot
522a1ad637SFrançois Tigeot struct feed_format_info {
532a1ad637SFrançois Tigeot uint32_t ibps, obps;
542a1ad637SFrançois Tigeot uint32_t ialign, oalign, channels;
552a1ad637SFrançois Tigeot intpcm_read_t *read;
562a1ad637SFrançois Tigeot intpcm_write_t *write;
572a1ad637SFrançois Tigeot uint8_t reservoir[FEEDFORMAT_RESERVOIR];
582a1ad637SFrançois Tigeot };
592a1ad637SFrançois Tigeot
602a1ad637SFrançois Tigeot /*
612a1ad637SFrançois Tigeot * dummy ac3/dts passthrough, etc.
622a1ad637SFrançois Tigeot * XXX assume as s16le.
632a1ad637SFrançois Tigeot */
642a1ad637SFrançois Tigeot static __inline intpcm_t
intpcm_read_null(uint8_t * src __unused)652a1ad637SFrançois Tigeot intpcm_read_null(uint8_t *src __unused)
662a1ad637SFrançois Tigeot {
672a1ad637SFrançois Tigeot
682a1ad637SFrançois Tigeot return (0);
692a1ad637SFrançois Tigeot }
702a1ad637SFrançois Tigeot
712a1ad637SFrançois Tigeot static __inline void
intpcm_write_null(uint8_t * dst,intpcm_t v __unused)722a1ad637SFrançois Tigeot intpcm_write_null(uint8_t *dst, intpcm_t v __unused)
732a1ad637SFrançois Tigeot {
742a1ad637SFrançois Tigeot
752a1ad637SFrançois Tigeot _PCM_WRITE_S16_LE(dst, 0);
762a1ad637SFrançois Tigeot }
772a1ad637SFrançois Tigeot
782a1ad637SFrançois Tigeot #define FEEDFORMAT_ENTRY(SIGN, BIT, ENDIAN) \
792a1ad637SFrançois Tigeot { \
802a1ad637SFrançois Tigeot AFMT_##SIGN##BIT##_##ENDIAN, \
812a1ad637SFrançois Tigeot intpcm_read_##SIGN##BIT##ENDIAN, \
822a1ad637SFrançois Tigeot intpcm_write_##SIGN##BIT##ENDIAN \
832a1ad637SFrançois Tigeot }
842a1ad637SFrançois Tigeot
852a1ad637SFrançois Tigeot static const struct {
862a1ad637SFrançois Tigeot uint32_t format;
872a1ad637SFrançois Tigeot intpcm_read_t *read;
882a1ad637SFrançois Tigeot intpcm_write_t *write;
892a1ad637SFrançois Tigeot } feed_format_ops[] = {
902a1ad637SFrançois Tigeot FEEDFORMAT_ENTRY(S, 8, NE),
912a1ad637SFrançois Tigeot FEEDFORMAT_ENTRY(S, 16, LE),
922a1ad637SFrançois Tigeot FEEDFORMAT_ENTRY(S, 24, LE),
932a1ad637SFrançois Tigeot FEEDFORMAT_ENTRY(S, 32, LE),
942a1ad637SFrançois Tigeot FEEDFORMAT_ENTRY(S, 16, BE),
952a1ad637SFrançois Tigeot FEEDFORMAT_ENTRY(S, 24, BE),
962a1ad637SFrançois Tigeot FEEDFORMAT_ENTRY(S, 32, BE),
972a1ad637SFrançois Tigeot FEEDFORMAT_ENTRY(U, 8, NE),
982a1ad637SFrançois Tigeot FEEDFORMAT_ENTRY(U, 16, LE),
992a1ad637SFrançois Tigeot FEEDFORMAT_ENTRY(U, 24, LE),
1002a1ad637SFrançois Tigeot FEEDFORMAT_ENTRY(U, 32, LE),
1012a1ad637SFrançois Tigeot FEEDFORMAT_ENTRY(U, 16, BE),
1022a1ad637SFrançois Tigeot FEEDFORMAT_ENTRY(U, 24, BE),
1032a1ad637SFrançois Tigeot FEEDFORMAT_ENTRY(U, 32, BE),
1042a1ad637SFrançois Tigeot {
1052a1ad637SFrançois Tigeot AFMT_MU_LAW,
1062a1ad637SFrançois Tigeot intpcm_read_ulaw, intpcm_write_ulaw
1072a1ad637SFrançois Tigeot },
1082a1ad637SFrançois Tigeot {
1092a1ad637SFrançois Tigeot AFMT_A_LAW,
1102a1ad637SFrançois Tigeot intpcm_read_alaw, intpcm_write_alaw
1112a1ad637SFrançois Tigeot },
1122a1ad637SFrançois Tigeot {
1132a1ad637SFrançois Tigeot AFMT_AC3,
1142a1ad637SFrançois Tigeot intpcm_read_null, intpcm_write_null
1152a1ad637SFrançois Tigeot }
1162a1ad637SFrançois Tigeot };
1172a1ad637SFrançois Tigeot
1182a1ad637SFrançois Tigeot #define FEEDFORMAT_TAB_SIZE \
1192a1ad637SFrançois Tigeot ((int32_t)(sizeof(feed_format_ops) / sizeof(feed_format_ops[0])))
1202a1ad637SFrançois Tigeot
1212a1ad637SFrançois Tigeot static int
feed_format_init(struct pcm_feeder * f)1222a1ad637SFrançois Tigeot feed_format_init(struct pcm_feeder *f)
1232a1ad637SFrançois Tigeot {
1242a1ad637SFrançois Tigeot struct feed_format_info *info;
1252a1ad637SFrançois Tigeot intpcm_read_t *rd_op;
1262a1ad637SFrançois Tigeot intpcm_write_t *wr_op;
1272a1ad637SFrançois Tigeot int i;
1282a1ad637SFrançois Tigeot
1292a1ad637SFrançois Tigeot if (f->desc->in == f->desc->out ||
1302a1ad637SFrançois Tigeot AFMT_CHANNEL(f->desc->in) != AFMT_CHANNEL(f->desc->out))
1312a1ad637SFrançois Tigeot return (EINVAL);
1322a1ad637SFrançois Tigeot
1332a1ad637SFrançois Tigeot rd_op = NULL;
1342a1ad637SFrançois Tigeot wr_op = NULL;
1352a1ad637SFrançois Tigeot
1362a1ad637SFrançois Tigeot for (i = 0; i < FEEDFORMAT_TAB_SIZE &&
1372a1ad637SFrançois Tigeot (rd_op == NULL || wr_op == NULL); i++) {
1382a1ad637SFrançois Tigeot if (rd_op == NULL &&
1392a1ad637SFrançois Tigeot AFMT_ENCODING(f->desc->in) == feed_format_ops[i].format)
1402a1ad637SFrançois Tigeot rd_op = feed_format_ops[i].read;
1412a1ad637SFrançois Tigeot if (wr_op == NULL &&
1422a1ad637SFrançois Tigeot AFMT_ENCODING(f->desc->out) == feed_format_ops[i].format)
1432a1ad637SFrançois Tigeot wr_op = feed_format_ops[i].write;
1442a1ad637SFrançois Tigeot }
1452a1ad637SFrançois Tigeot
1462a1ad637SFrançois Tigeot if (rd_op == NULL || wr_op == NULL) {
14767931cc4SFrançois Tigeot kprintf("%s(): failed to initialize io ops "
1482a1ad637SFrançois Tigeot "in=0x%08x out=0x%08x\n",
1492a1ad637SFrançois Tigeot __func__, f->desc->in, f->desc->out);
1502a1ad637SFrançois Tigeot return (EINVAL);
1512a1ad637SFrançois Tigeot }
1522a1ad637SFrançois Tigeot
153*4e8e900cSMatthew Dillon info = kmalloc(sizeof(*info), M_DEVBUF, M_WAITOK | M_ZERO);
1542a1ad637SFrançois Tigeot if (info == NULL)
1552a1ad637SFrançois Tigeot return (ENOMEM);
1562a1ad637SFrançois Tigeot
1572a1ad637SFrançois Tigeot info->channels = AFMT_CHANNEL(f->desc->in);
1582a1ad637SFrançois Tigeot
1592a1ad637SFrançois Tigeot info->ibps = AFMT_BPS(f->desc->in);
1602a1ad637SFrançois Tigeot info->ialign = info->ibps * info->channels;
1612a1ad637SFrançois Tigeot info->read = rd_op;
1622a1ad637SFrançois Tigeot
1632a1ad637SFrançois Tigeot info->obps = AFMT_BPS(f->desc->out);
1642a1ad637SFrançois Tigeot info->oalign = info->obps * info->channels;
1652a1ad637SFrançois Tigeot info->write = wr_op;
1662a1ad637SFrançois Tigeot
1672a1ad637SFrançois Tigeot f->data = info;
1682a1ad637SFrançois Tigeot
1692a1ad637SFrançois Tigeot return (0);
1702a1ad637SFrançois Tigeot }
1712a1ad637SFrançois Tigeot
1722a1ad637SFrançois Tigeot static int
feed_format_free(struct pcm_feeder * f)1732a1ad637SFrançois Tigeot feed_format_free(struct pcm_feeder *f)
1742a1ad637SFrançois Tigeot {
1752a1ad637SFrançois Tigeot struct feed_format_info *info;
1762a1ad637SFrançois Tigeot
1772a1ad637SFrançois Tigeot info = f->data;
1782a1ad637SFrançois Tigeot if (info != NULL)
17967931cc4SFrançois Tigeot kfree(info, M_DEVBUF);
1802a1ad637SFrançois Tigeot
1812a1ad637SFrançois Tigeot f->data = NULL;
1822a1ad637SFrançois Tigeot
1832a1ad637SFrançois Tigeot return (0);
1842a1ad637SFrançois Tigeot }
1852a1ad637SFrançois Tigeot
1862a1ad637SFrançois Tigeot static int
feed_format_set(struct pcm_feeder * f,int what,int value)1872a1ad637SFrançois Tigeot feed_format_set(struct pcm_feeder *f, int what, int value)
1882a1ad637SFrançois Tigeot {
1892a1ad637SFrançois Tigeot struct feed_format_info *info;
1902a1ad637SFrançois Tigeot
1912a1ad637SFrançois Tigeot info = f->data;
1922a1ad637SFrançois Tigeot
1932a1ad637SFrançois Tigeot switch (what) {
1942a1ad637SFrançois Tigeot case FEEDFORMAT_CHANNELS:
1952a1ad637SFrançois Tigeot if (value < SND_CHN_MIN || value > SND_CHN_MAX)
1962a1ad637SFrançois Tigeot return (EINVAL);
1972a1ad637SFrançois Tigeot info->channels = (uint32_t)value;
1982a1ad637SFrançois Tigeot info->ialign = info->ibps * info->channels;
1992a1ad637SFrançois Tigeot info->oalign = info->obps * info->channels;
2002a1ad637SFrançois Tigeot break;
2012a1ad637SFrançois Tigeot default:
2022a1ad637SFrançois Tigeot return (EINVAL);
2032a1ad637SFrançois Tigeot break;
2042a1ad637SFrançois Tigeot }
2052a1ad637SFrançois Tigeot
2062a1ad637SFrançois Tigeot return (0);
2072a1ad637SFrançois Tigeot }
2082a1ad637SFrançois Tigeot
2092a1ad637SFrançois Tigeot static int
feed_format_feed(struct pcm_feeder * f,struct pcm_channel * c,uint8_t * b,uint32_t count,void * source)2102a1ad637SFrançois Tigeot feed_format_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
2112a1ad637SFrançois Tigeot uint32_t count, void *source)
2122a1ad637SFrançois Tigeot {
2132a1ad637SFrançois Tigeot struct feed_format_info *info;
2142a1ad637SFrançois Tigeot intpcm_t v;
2152a1ad637SFrançois Tigeot uint32_t j;
2162a1ad637SFrançois Tigeot uint8_t *src, *dst;
2172a1ad637SFrançois Tigeot
2182a1ad637SFrançois Tigeot info = f->data;
2192a1ad637SFrançois Tigeot dst = b;
2202a1ad637SFrançois Tigeot count = SND_FXROUND(count, info->oalign);
2212a1ad637SFrançois Tigeot
2222a1ad637SFrançois Tigeot do {
2232a1ad637SFrançois Tigeot if (count < info->oalign)
2242a1ad637SFrançois Tigeot break;
2252a1ad637SFrançois Tigeot
2262a1ad637SFrançois Tigeot if (count < info->ialign) {
2272a1ad637SFrançois Tigeot src = info->reservoir;
2282a1ad637SFrançois Tigeot j = info->ialign;
2292a1ad637SFrançois Tigeot } else {
2302a1ad637SFrançois Tigeot if (info->ialign == info->oalign)
2312a1ad637SFrançois Tigeot j = count;
2322a1ad637SFrançois Tigeot else if (info->ialign > info->oalign)
2332a1ad637SFrançois Tigeot j = SND_FXROUND(count, info->ialign);
2342a1ad637SFrançois Tigeot else
2352a1ad637SFrançois Tigeot j = SND_FXDIV(count, info->oalign) *
2362a1ad637SFrançois Tigeot info->ialign;
2372a1ad637SFrançois Tigeot src = dst + count - j;
2382a1ad637SFrançois Tigeot }
2392a1ad637SFrançois Tigeot
2402a1ad637SFrançois Tigeot j = SND_FXDIV(FEEDER_FEED(f->source, c, src, j, source),
2412a1ad637SFrançois Tigeot info->ialign);
2422a1ad637SFrançois Tigeot if (j == 0)
2432a1ad637SFrançois Tigeot break;
2442a1ad637SFrançois Tigeot
2452a1ad637SFrançois Tigeot j *= info->channels;
2462a1ad637SFrançois Tigeot count -= j * info->obps;
2472a1ad637SFrançois Tigeot
2482a1ad637SFrançois Tigeot do {
2492a1ad637SFrançois Tigeot v = info->read(src);
2502a1ad637SFrançois Tigeot info->write(dst, v);
2512a1ad637SFrançois Tigeot dst += info->obps;
2522a1ad637SFrançois Tigeot src += info->ibps;
2532a1ad637SFrançois Tigeot } while (--j != 0);
2542a1ad637SFrançois Tigeot
2552a1ad637SFrançois Tigeot } while (count != 0);
2562a1ad637SFrançois Tigeot
2572a1ad637SFrançois Tigeot return (dst - b);
2582a1ad637SFrançois Tigeot }
2592a1ad637SFrançois Tigeot
2602a1ad637SFrançois Tigeot static struct pcm_feederdesc feeder_format_desc[] = {
2612a1ad637SFrançois Tigeot { FEEDER_FORMAT, 0, 0, 0, 0 },
2622a1ad637SFrançois Tigeot { 0, 0, 0, 0, 0 }
2632a1ad637SFrançois Tigeot };
2642a1ad637SFrançois Tigeot
2652a1ad637SFrançois Tigeot static kobj_method_t feeder_format_methods[] = {
2662a1ad637SFrançois Tigeot KOBJMETHOD(feeder_init, feed_format_init),
2672a1ad637SFrançois Tigeot KOBJMETHOD(feeder_free, feed_format_free),
2682a1ad637SFrançois Tigeot KOBJMETHOD(feeder_set, feed_format_set),
2692a1ad637SFrançois Tigeot KOBJMETHOD(feeder_feed, feed_format_feed),
2702a1ad637SFrançois Tigeot KOBJMETHOD_END
2712a1ad637SFrançois Tigeot };
2722a1ad637SFrançois Tigeot
2732a1ad637SFrançois Tigeot FEEDER_DECLARE(feeder_format, NULL);
2742a1ad637SFrançois Tigeot
2752a1ad637SFrançois Tigeot /* Extern */
2762a1ad637SFrançois Tigeot intpcm_read_t *
feeder_format_read_op(uint32_t format)2772a1ad637SFrançois Tigeot feeder_format_read_op(uint32_t format)
2782a1ad637SFrançois Tigeot {
2792a1ad637SFrançois Tigeot int i;
2802a1ad637SFrançois Tigeot
2812a1ad637SFrançois Tigeot for (i = 0; i < FEEDFORMAT_TAB_SIZE; i++) {
2822a1ad637SFrançois Tigeot if (AFMT_ENCODING(format) == feed_format_ops[i].format)
2832a1ad637SFrançois Tigeot return (feed_format_ops[i].read);
2842a1ad637SFrançois Tigeot }
2852a1ad637SFrançois Tigeot
2862a1ad637SFrançois Tigeot return (NULL);
2872a1ad637SFrançois Tigeot }
2882a1ad637SFrançois Tigeot
2892a1ad637SFrançois Tigeot intpcm_write_t *
feeder_format_write_op(uint32_t format)2902a1ad637SFrançois Tigeot feeder_format_write_op(uint32_t format)
2912a1ad637SFrançois Tigeot {
2922a1ad637SFrançois Tigeot int i;
2932a1ad637SFrançois Tigeot
2942a1ad637SFrançois Tigeot for (i = 0; i < FEEDFORMAT_TAB_SIZE; i++) {
2952a1ad637SFrançois Tigeot if (AFMT_ENCODING(format) == feed_format_ops[i].format)
2962a1ad637SFrançois Tigeot return (feed_format_ops[i].write);
2972a1ad637SFrançois Tigeot }
2982a1ad637SFrançois Tigeot
2992a1ad637SFrançois Tigeot return (NULL);
3002a1ad637SFrançois Tigeot }
301