xref: /dragonfly/sys/dev/sound/pcm/feeder_format.c (revision 4e8e900c)
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