Blame a52/pcm_a52.c

Packit Service cd2a00
/*
Packit Service cd2a00
 * A52 Output Plugin
Packit Service cd2a00
 *
Packit Service cd2a00
 * Copyright (c) 2006 by Takashi Iwai <tiwai@suse.de>
Packit Service cd2a00
 *
Packit Service cd2a00
 * This library is free software; you can redistribute it and/or modify
Packit Service cd2a00
 * it under the terms of the GNU Lesser General Public License as
Packit Service cd2a00
 * published by the Free Software Foundation; either version 2.1 of
Packit Service cd2a00
 * the License, or (at your option) any later version.
Packit Service cd2a00
 *
Packit Service cd2a00
 * This program is distributed in the hope that it will be useful,
Packit Service cd2a00
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service cd2a00
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service cd2a00
 * GNU Lesser General Public License for more details.
Packit Service cd2a00
 *
Packit Service cd2a00
 * You should have received a copy of the GNU Lesser General Public
Packit Service cd2a00
 * License along with this library; if not, write to the Free Software
Packit Service cd2a00
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
Packit Service cd2a00
 */
Packit Service cd2a00
Packit Service cd2a00
#include <stdio.h>
Packit Service cd2a00
#include <string.h>
Packit Service cd2a00
#define __USE_XOPEN
Packit Service cd2a00
#include <unistd.h>
Packit Service cd2a00
#include <alsa/asoundlib.h>
Packit Service cd2a00
#include <alsa/pcm_external.h>
Packit Service cd2a00
#include <alsa/pcm_plugin.h>
Packit Service cd2a00
#include <libavcodec/avcodec.h>
Packit Service cd2a00
#include <libavutil/avutil.h>
Packit Service cd2a00
Packit Service cd2a00
/* some compatibility wrappers */
Packit Service cd2a00
#ifndef AV_VERSION_INT
Packit Service cd2a00
#define AV_VERSION_INT(a, b, c) (((a) << 16) | ((b) << 8) | (c))
Packit Service cd2a00
#endif
Packit Service cd2a00
#ifndef LIBAVCODEC_VERSION_INT
Packit Service cd2a00
#define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
Packit Service cd2a00
                                               LIBAVCODEC_VERSION_MINOR, \
Packit Service cd2a00
                                               LIBAVCODEC_VERSION_MICRO)
Packit Service cd2a00
#endif
Packit Service cd2a00
Packit Service cd2a00
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53, 34, 0)
Packit Service cd2a00
#include <libavutil/channel_layout.h>
Packit Service cd2a00
#include <libavutil/mem.h>
Packit Service cd2a00
#define USE_AVCODEC_FRAME
Packit Service cd2a00
#endif
Packit Service cd2a00
Packit Service cd2a00
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54, 0, 0)
Packit Service cd2a00
#ifndef AV_CH_LAYOUT_STEREO
Packit Service cd2a00
#define AV_CH_LAYOUT_STEREO	CH_LAYOUT_STEREO
Packit Service cd2a00
#define AV_CH_LAYOUT_QUAD	CH_LAYOUT_QUAD
Packit Service cd2a00
#define AV_CH_LAYOUT_5POINT1	CH_LAYOUT_5POINT1
Packit Service cd2a00
#endif
Packit Service cd2a00
#endif
Packit Service cd2a00
Packit Service cd2a00
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(52, 95, 0)
Packit Service cd2a00
#ifndef AV_SAMPLE_FMT_S16
Packit Service cd2a00
#define AV_SAMPLE_FMT_S16	SAMPLE_FMT_S16
Packit Service cd2a00
#endif
Packit Service cd2a00
#endif
Packit Service cd2a00
Packit Service cd2a00
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54, 25, 0)
Packit Service cd2a00
#define AV_CODEC_ID_AC3 CODEC_ID_AC3
Packit Service cd2a00
#endif
Packit Service cd2a00
Packit Service cd2a00
#if LIBAVCODEC_VERSION_INT < 0x371c01
Packit Service cd2a00
#define av_frame_alloc avcodec_alloc_frame
Packit Service cd2a00
#define av_frame_free avcodec_free_frame
Packit Service cd2a00
#endif
Packit Service cd2a00
Packit Service cd2a00
struct a52_ctx {
Packit Service cd2a00
	snd_pcm_ioplug_t io;
Packit Service cd2a00
	snd_pcm_t *slave;
Packit Service cd2a00
	AVCodec *codec;
Packit Service cd2a00
	AVCodecContext *avctx;
Packit Service cd2a00
	snd_pcm_format_t format;
Packit Service cd2a00
	int av_format;
Packit Service cd2a00
	unsigned int channels;
Packit Service cd2a00
	unsigned int rate;
Packit Service cd2a00
	unsigned int bitrate;
Packit Service cd2a00
	short *inbuf;
Packit Service cd2a00
	unsigned char *outbuf;
Packit Service cd2a00
	int outbuf_size;
Packit Service cd2a00
	snd_pcm_uframes_t transfer;
Packit Service cd2a00
	int remain;
Packit Service cd2a00
	int filled;
Packit Service cd2a00
	unsigned int slave_period_size;
Packit Service cd2a00
	unsigned int slave_buffer_size;
Packit Service cd2a00
	snd_pcm_hw_params_t *hw_params;
Packit Service cd2a00
#ifdef USE_AVCODEC_FRAME
Packit Service cd2a00
	AVFrame *frame;
Packit Service cd2a00
	int is_planar;
Packit Service cd2a00
#endif
Packit Service cd2a00
};
Packit Service cd2a00
Packit Service cd2a00
#ifdef USE_AVCODEC_FRAME
Packit Service cd2a00
#define use_planar(rec)		(rec)->is_planar
Packit Service cd2a00
#else
Packit Service cd2a00
#define use_planar(rec)		0
Packit Service cd2a00
#endif
Packit Service cd2a00
Packit Service cd2a00
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53, 34, 0)
Packit Service cd2a00
static int do_encode(struct a52_ctx *rec)
Packit Service cd2a00
{
Packit Service cd2a00
	AVPacket pkt = {
Packit Service cd2a00
		.data = rec->outbuf + 8,
Packit Service cd2a00
		.size = rec->outbuf_size - 8
Packit Service cd2a00
	};
Packit Service cd2a00
	int got_frame;
Packit Service cd2a00
Packit Service cd2a00
	avcodec_encode_audio2(rec->avctx, &pkt, rec->frame, &got_frame);
Packit Service cd2a00
	return pkt.size;
Packit Service cd2a00
}
Packit Service cd2a00
#else
Packit Service cd2a00
static int do_encode(struct a52_ctx *rec)
Packit Service cd2a00
{
Packit Service cd2a00
	return avcodec_encode_audio(rec->avctx, rec->outbuf + 8,
Packit Service cd2a00
				    rec->outbuf_size - 8,
Packit Service cd2a00
				    rec->inbuf);
Packit Service cd2a00
}
Packit Service cd2a00
#endif
Packit Service cd2a00
Packit Service cd2a00
/* convert the PCM data to A52 stream in IEC958 */
Packit Service cd2a00
static void convert_data(struct a52_ctx *rec)
Packit Service cd2a00
{
Packit Service cd2a00
	int out_bytes = do_encode(rec);
Packit Service cd2a00
Packit Service cd2a00
	rec->outbuf[0] = 0xf8; /* sync words */
Packit Service cd2a00
	rec->outbuf[1] = 0x72;
Packit Service cd2a00
	rec->outbuf[2] = 0x4e;
Packit Service cd2a00
	rec->outbuf[3] = 0x1f;
Packit Service cd2a00
	rec->outbuf[4] = rec->outbuf[13] & 7; /* bsmod */
Packit Service cd2a00
	rec->outbuf[5] = 0x01; /* data type */
Packit Service cd2a00
	rec->outbuf[6] = ((out_bytes * 8) >> 8) & 0xff;
Packit Service cd2a00
	rec->outbuf[7] = (out_bytes * 8) & 0xff;
Packit Service cd2a00
	/* swap bytes for little-endian 16bit */
Packit Service cd2a00
	if (rec->format == SND_PCM_FORMAT_S16_LE)
Packit Service cd2a00
		swab(rec->outbuf, rec->outbuf, out_bytes + 8);
Packit Service cd2a00
	memset(rec->outbuf +  8 + out_bytes, 0,
Packit Service cd2a00
	       rec->outbuf_size - 8 - out_bytes);
Packit Service cd2a00
	rec->remain = rec->outbuf_size / 4;
Packit Service cd2a00
	rec->filled = 0;
Packit Service cd2a00
}
Packit Service cd2a00
Packit Service cd2a00
/* write pending encoded data to the slave pcm */
Packit Service cd2a00
static int write_out_pending(snd_pcm_ioplug_t *io, struct a52_ctx *rec)
Packit Service cd2a00
{
Packit Service cd2a00
	int err, ofs = 0;
Packit Service cd2a00
Packit Service cd2a00
	if (! rec->remain)
Packit Service cd2a00
		return 0;
Packit Service cd2a00
Packit Service cd2a00
	while (rec->remain) {
Packit Service cd2a00
		err = snd_pcm_writei(rec->slave, rec->outbuf + ofs, rec->remain);
Packit Service cd2a00
		if (err < 0) {
Packit Service cd2a00
			if (err == -EPIPE)
Packit Service cd2a00
				io->state = SND_PCM_STATE_XRUN;
Packit Service cd2a00
			return err;
Packit Service cd2a00
		} else if (! err)
Packit Service cd2a00
			break;
Packit Service cd2a00
		if (err < rec->remain)
Packit Service cd2a00
			ofs += (rec->remain - err) * 4;
Packit Service cd2a00
		rec->remain -= err;
Packit Service cd2a00
	}
Packit Service cd2a00
	if (rec->remain && ofs)
Packit Service cd2a00
		memmove(rec->outbuf, rec->outbuf + ofs, rec->remain * 4);
Packit Service cd2a00
	return 0;
Packit Service cd2a00
}
Packit Service cd2a00
Packit Service cd2a00
/*
Packit Service cd2a00
 * drain callback
Packit Service cd2a00
 */
Packit Service cd2a00
#ifdef USE_AVCODEC_FRAME
Packit Service cd2a00
static void clear_remaining_planar_data(snd_pcm_ioplug_t *io)
Packit Service cd2a00
{
Packit Service cd2a00
	struct a52_ctx *rec = io->private_data;
Packit Service cd2a00
	unsigned int i;
Packit Service cd2a00
Packit Service cd2a00
	for (i = 0; i < io->channels; i++)
Packit Service cd2a00
		memset(rec->frame->data[i] + rec->filled * 2, 0,
Packit Service cd2a00
		       (rec->avctx->frame_size - rec->filled) * 2);
Packit Service cd2a00
}
Packit Service cd2a00
#else
Packit Service cd2a00
#define clear_remaining_planar_data(io) /*NOP*/
Packit Service cd2a00
#endif
Packit Service cd2a00
Packit Service cd2a00
static int a52_drain(snd_pcm_ioplug_t *io)
Packit Service cd2a00
{
Packit Service cd2a00
	struct a52_ctx *rec = io->private_data;
Packit Service cd2a00
	int err;
Packit Service cd2a00
Packit Service cd2a00
	if (rec->filled) {
Packit Service cd2a00
		if ((err = write_out_pending(io, rec)) < 0)
Packit Service cd2a00
			return err;
Packit Service cd2a00
		/* remaining data must be converted and sent out */
Packit Service cd2a00
		if (use_planar(rec))
Packit Service cd2a00
			clear_remaining_planar_data(io);
Packit Service cd2a00
		else {
Packit Service cd2a00
			memset(rec->inbuf + rec->filled * io->channels, 0,
Packit Service cd2a00
			       (rec->avctx->frame_size - rec->filled) * io->channels * 2);
Packit Service cd2a00
		}
Packit Service cd2a00
		convert_data(rec);
Packit Service cd2a00
	}
Packit Service cd2a00
	err = write_out_pending(io, rec);
Packit Service cd2a00
	if (err < 0)
Packit Service cd2a00
		return err;
Packit Service cd2a00
Packit Service cd2a00
	return snd_pcm_drain(rec->slave);
Packit Service cd2a00
}
Packit Service cd2a00
Packit Service cd2a00
/* check whether the areas consist of a continuous interleaved stream */
Packit Service cd2a00
static int check_interleaved(const snd_pcm_channel_area_t *areas,
Packit Service cd2a00
			     unsigned int channels)
Packit Service cd2a00
{
Packit Service cd2a00
	unsigned int ch;
Packit Service cd2a00
Packit Service cd2a00
	if (channels > 4) /* we need re-routing for 6 channels */
Packit Service cd2a00
		return 0;
Packit Service cd2a00
Packit Service cd2a00
	for (ch = 0; ch < channels; ch++) {
Packit Service cd2a00
		if (areas[ch].addr != areas[0].addr ||
Packit Service cd2a00
		    areas[ch].first != ch * 16 ||
Packit Service cd2a00
		    areas[ch].step != channels * 16)
Packit Service cd2a00
			return 0;
Packit Service cd2a00
	}
Packit Service cd2a00
	return 1;
Packit Service cd2a00
}
Packit Service cd2a00
Packit Service cd2a00
/* Fill the input PCM to the internal buffer until a52 frames,
Packit Service cd2a00
 * then covert and write it out.
Packit Service cd2a00
 *
Packit Service cd2a00
 * Returns the number of processed frames.
Packit Service cd2a00
 */
Packit Service cd2a00
static int fill_data(snd_pcm_ioplug_t *io,
Packit Service cd2a00
		     const snd_pcm_channel_area_t *areas,
Packit Service cd2a00
		     unsigned int offset, unsigned int size,
Packit Service cd2a00
		     int interleaved)
Packit Service cd2a00
{
Packit Service cd2a00
	struct a52_ctx *rec = io->private_data;
Packit Service cd2a00
	unsigned int len = rec->avctx->frame_size - rec->filled;
Packit Service cd2a00
	short *src, *dst;
Packit Service cd2a00
	unsigned int src_step;
Packit Service cd2a00
	int err;
Packit Service cd2a00
	static unsigned int ch_index[3][6] = {
Packit Service cd2a00
		{ 0, 1 },
Packit Service cd2a00
		{ 0, 1, 2, 3 },
Packit Service cd2a00
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52, 26, 0)
Packit Service cd2a00
		/* current libavcodec expects SMPTE order */
Packit Service cd2a00
		{ 0, 1, 4, 5, 2, 3 },
Packit Service cd2a00
#else
Packit Service cd2a00
		/* libavcodec older than r18540 expects A52 order */
Packit Service cd2a00
		{ 0, 4, 1, 2, 3, 5 },
Packit Service cd2a00
#endif
Packit Service cd2a00
	};
Packit Service cd2a00
Packit Service cd2a00
	if ((err = write_out_pending(io, rec)) < 0)
Packit Service cd2a00
		return err;
Packit Service cd2a00
Packit Service cd2a00
	if (size > len)
Packit Service cd2a00
		size = len;
Packit Service cd2a00
Packit Service cd2a00
	dst = rec->inbuf + rec->filled * io->channels;
Packit Service cd2a00
	if (!use_planar(rec) && interleaved) {
Packit Service cd2a00
		memcpy(dst, areas->addr + offset * io->channels * 2,
Packit Service cd2a00
		       size * io->channels * 2);
Packit Service cd2a00
	} else {
Packit Service cd2a00
		unsigned int i, ch, dst_step;
Packit Service cd2a00
		short *dst1;
Packit Service cd2a00
Packit Service cd2a00
		/* flatten copy to n-channel interleaved */
Packit Service cd2a00
		dst_step = io->channels;
Packit Service cd2a00
		for (ch = 0; ch < io->channels; ch++, dst++) {
Packit Service cd2a00
			const snd_pcm_channel_area_t *ap;
Packit Service cd2a00
			ap = &areas[ch_index[io->channels / 2 - 1][ch]];
Packit Service cd2a00
			src = (short *)(ap->addr +
Packit Service cd2a00
					(ap->first + offset * ap->step) / 8);
Packit Service cd2a00
Packit Service cd2a00
#ifdef USE_AVCODEC_FRAME
Packit Service cd2a00
			if (use_planar(rec)) {
Packit Service cd2a00
				memcpy(rec->frame->data[ch], src, size * 2);
Packit Service cd2a00
				continue;
Packit Service cd2a00
			}
Packit Service cd2a00
#endif
Packit Service cd2a00
			dst1 = dst;
Packit Service cd2a00
			src_step = ap->step / 16; /* in word */
Packit Service cd2a00
			for (i = 0; i < size; i++) {
Packit Service cd2a00
				*dst1 = *src;
Packit Service cd2a00
				src += src_step;
Packit Service cd2a00
				dst1 += dst_step;
Packit Service cd2a00
			}
Packit Service cd2a00
		}
Packit Service cd2a00
	}
Packit Service cd2a00
	rec->filled += size;
Packit Service cd2a00
	if (rec->filled == rec->avctx->frame_size) {
Packit Service cd2a00
		convert_data(rec);
Packit Service cd2a00
		write_out_pending(io, rec);
Packit Service cd2a00
	}
Packit Service cd2a00
	return (int)size;
Packit Service cd2a00
}
Packit Service cd2a00
Packit Service cd2a00
/*
Packit Service cd2a00
 * transfer callback
Packit Service cd2a00
 */
Packit Service cd2a00
static snd_pcm_sframes_t a52_transfer(snd_pcm_ioplug_t *io,
Packit Service cd2a00
				      const snd_pcm_channel_area_t *areas,
Packit Service cd2a00
				      snd_pcm_uframes_t offset,
Packit Service cd2a00
				      snd_pcm_uframes_t size)
Packit Service cd2a00
{
Packit Service cd2a00
	struct a52_ctx *rec = io->private_data;
Packit Service cd2a00
	snd_pcm_sframes_t result = 0;
Packit Service cd2a00
	int err = 0;
Packit Service cd2a00
	int interleaved = check_interleaved(areas, io->channels);
Packit Service cd2a00
Packit Service cd2a00
	do {
Packit Service cd2a00
		err = fill_data(io, areas, offset, size, interleaved);
Packit Service cd2a00
		if (err < 0)
Packit Service cd2a00
			break;
Packit Service cd2a00
		offset += (unsigned int)err;
Packit Service cd2a00
		size -= (unsigned int)err;
Packit Service cd2a00
		result += err;
Packit Service cd2a00
		rec->transfer += err;
Packit Service cd2a00
	} while (size);
Packit Service cd2a00
	return result > 0 ? result : err;
Packit Service cd2a00
}
Packit Service cd2a00
Packit Service cd2a00
/*
Packit Service cd2a00
 * pointer callback
Packit Service cd2a00
 *
Packit Service cd2a00
 * Calculate the current position from the delay of slave PCM
Packit Service cd2a00
 */
Packit Service cd2a00
static snd_pcm_sframes_t a52_pointer(snd_pcm_ioplug_t *io)
Packit Service cd2a00
{
Packit Service cd2a00
	struct a52_ctx *rec = io->private_data;
Packit Service cd2a00
	snd_pcm_sframes_t delay;
Packit Service cd2a00
	snd_pcm_state_t state;
Packit Service cd2a00
	int err;
Packit Service cd2a00
Packit Service cd2a00
	state = snd_pcm_state(rec->slave);
Packit Service cd2a00
	switch (state) {
Packit Service cd2a00
	case SND_PCM_STATE_RUNNING:
Packit Service cd2a00
	case SND_PCM_STATE_DRAINING:
Packit Service cd2a00
		if ((err = snd_pcm_delay(rec->slave, &delay)) < 0)
Packit Service cd2a00
			return err;
Packit Service cd2a00
		break;
Packit Service cd2a00
	case SND_PCM_STATE_XRUN:
Packit Service cd2a00
	case SND_PCM_STATE_SUSPENDED:
Packit Service cd2a00
		return -EPIPE;
Packit Service cd2a00
	default:
Packit Service cd2a00
		return 0;
Packit Service cd2a00
	}
Packit Service cd2a00
Packit Service cd2a00
	if (delay < 0 || delay >= (snd_pcm_sframes_t)rec->slave_buffer_size)
Packit Service cd2a00
		delay = 0;
Packit Service cd2a00
	delay = (snd_pcm_sframes_t)io->appl_ptr - delay;
Packit Service cd2a00
	if (delay < 0) {
Packit Service cd2a00
		delay += io->buffer_size;
Packit Service cd2a00
		if (delay < 0)
Packit Service cd2a00
			delay = 0;
Packit Service cd2a00
	}
Packit Service cd2a00
	delay %= io->buffer_size;
Packit Service cd2a00
	return delay;
Packit Service cd2a00
}
Packit Service cd2a00
Packit Service cd2a00
/* set up the fixed parameters of slave PCM hw_parmas */
Packit Service cd2a00
static int a52_slave_hw_params_half(struct a52_ctx *rec)
Packit Service cd2a00
{
Packit Service cd2a00
	int err;
Packit Service cd2a00
Packit Service cd2a00
	if ((err = snd_pcm_hw_params_malloc(&rec->hw_params)) < 0)
Packit Service cd2a00
		return err;
Packit Service cd2a00
Packit Service cd2a00
	if ((err = snd_pcm_hw_params_any(rec->slave, rec->hw_params)) < 0) {
Packit Service cd2a00
		SNDERR("Cannot get slave hw_params");
Packit Service cd2a00
		goto out;
Packit Service cd2a00
	}
Packit Service cd2a00
	if ((err = snd_pcm_hw_params_set_access(rec->slave, rec->hw_params,
Packit Service cd2a00
						SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
Packit Service cd2a00
		SNDERR("Cannot set slave access RW_INTERLEAVED");
Packit Service cd2a00
		goto out;
Packit Service cd2a00
	}
Packit Service cd2a00
	if ((err = snd_pcm_hw_params_set_channels(rec->slave, rec->hw_params, 2)) < 0) {
Packit Service cd2a00
		SNDERR("Cannot set slave channels 2");
Packit Service cd2a00
		goto out;
Packit Service cd2a00
	}
Packit Service cd2a00
	if ((err = snd_pcm_hw_params_set_format(rec->slave, rec->hw_params,
Packit Service cd2a00
						rec->format)) < 0) {
Packit Service cd2a00
		SNDERR("Cannot set slave format");
Packit Service cd2a00
		goto out;
Packit Service cd2a00
	}
Packit Service cd2a00
	if ((err = snd_pcm_hw_params_set_rate(rec->slave, rec->hw_params, rec->rate, 0)) < 0) {
Packit Service cd2a00
		SNDERR("Cannot set slave rate %d", rec->rate);
Packit Service cd2a00
		goto out;
Packit Service cd2a00
	}
Packit Service cd2a00
	return 0;
Packit Service cd2a00
Packit Service cd2a00
 out:
Packit Service cd2a00
	free(rec->hw_params);
Packit Service cd2a00
	rec->hw_params = NULL;
Packit Service cd2a00
	return err;
Packit Service cd2a00
}
Packit Service cd2a00
Packit Service cd2a00
/*
Packit Service cd2a00
 * hw_params callback
Packit Service cd2a00
 *
Packit Service cd2a00
 * Set up slave PCM according to the current parameters
Packit Service cd2a00
 */
Packit Service cd2a00
static int a52_hw_params(snd_pcm_ioplug_t *io,
Packit Service cd2a00
			 snd_pcm_hw_params_t *params ATTRIBUTE_UNUSED)
Packit Service cd2a00
{
Packit Service cd2a00
	struct a52_ctx *rec = io->private_data;
Packit Service cd2a00
	snd_pcm_uframes_t period_size;
Packit Service cd2a00
	snd_pcm_uframes_t buffer_size;
Packit Service cd2a00
	int err;
Packit Service cd2a00
Packit Service cd2a00
	if (! rec->hw_params) {
Packit Service cd2a00
		err = a52_slave_hw_params_half(rec);
Packit Service cd2a00
		if (err < 0)
Packit Service cd2a00
			return err;
Packit Service cd2a00
	}
Packit Service cd2a00
	period_size = io->period_size;
Packit Service cd2a00
	if ((err = snd_pcm_hw_params_set_period_size_near(rec->slave, rec->hw_params,
Packit Service cd2a00
							  &period_size, NULL)) < 0) {
Packit Service cd2a00
		SNDERR("Cannot set slave period size %ld", period_size);
Packit Service cd2a00
		return err;
Packit Service cd2a00
	}
Packit Service cd2a00
	buffer_size = io->buffer_size;
Packit Service cd2a00
	if ((err = snd_pcm_hw_params_set_buffer_size_near(rec->slave, rec->hw_params,
Packit Service cd2a00
							  &buffer_size)) < 0) {
Packit Service cd2a00
		SNDERR("Cannot set slave buffer size %ld", buffer_size);
Packit Service cd2a00
		return err;
Packit Service cd2a00
	}
Packit Service cd2a00
	if ((err = snd_pcm_hw_params(rec->slave, rec->hw_params)) < 0) {
Packit Service cd2a00
		SNDERR("Cannot set slave hw_params");
Packit Service cd2a00
		return err;
Packit Service cd2a00
	}
Packit Service cd2a00
	rec->slave_period_size = period_size;
Packit Service cd2a00
	rec->slave_buffer_size = buffer_size;
Packit Service cd2a00
Packit Service cd2a00
	return 0;
Packit Service cd2a00
}
Packit Service cd2a00
Packit Service cd2a00
/*
Packit Service cd2a00
 * hw_free callback
Packit Service cd2a00
 */
Packit Service cd2a00
static int a52_hw_free(snd_pcm_ioplug_t *io)
Packit Service cd2a00
{
Packit Service cd2a00
	struct a52_ctx *rec = io->private_data;
Packit Service cd2a00
Packit Service cd2a00
	free(rec->hw_params);
Packit Service cd2a00
	rec->hw_params = NULL;
Packit Service cd2a00
	return snd_pcm_hw_free(rec->slave);
Packit Service cd2a00
}
Packit Service cd2a00
Packit Service cd2a00
/*
Packit Service cd2a00
 * sw_params callback
Packit Service cd2a00
 *
Packit Service cd2a00
 * Set up slave PCM sw_params
Packit Service cd2a00
 */
Packit Service cd2a00
static int a52_sw_params(snd_pcm_ioplug_t *io, snd_pcm_sw_params_t *params)
Packit Service cd2a00
{
Packit Service cd2a00
	struct a52_ctx *rec = io->private_data;
Packit Service cd2a00
	snd_pcm_sw_params_t *sparams;
Packit Service cd2a00
	snd_pcm_uframes_t avail_min, start_threshold;
Packit Service cd2a00
	int len;
Packit Service cd2a00
Packit Service cd2a00
	snd_pcm_sw_params_get_avail_min(params, &avail_min);
Packit Service cd2a00
	snd_pcm_sw_params_get_start_threshold(params, &start_threshold);
Packit Service cd2a00
Packit Service cd2a00
	len = avail_min;
Packit Service cd2a00
	len += (int)rec->slave_buffer_size - (int)io->buffer_size;
Packit Service cd2a00
	if (len < 0)
Packit Service cd2a00
		avail_min = 1;
Packit Service cd2a00
	else
Packit Service cd2a00
		avail_min = len;
Packit Service cd2a00
	snd_pcm_sw_params_alloca(&sparams);
Packit Service cd2a00
	snd_pcm_sw_params_current(rec->slave, sparams);
Packit Service cd2a00
	snd_pcm_sw_params_set_avail_min(rec->slave, sparams, avail_min);
Packit Service cd2a00
	snd_pcm_sw_params_set_start_threshold(rec->slave, sparams,
Packit Service cd2a00
					      start_threshold);
Packit Service cd2a00
Packit Service cd2a00
	return snd_pcm_sw_params(rec->slave, sparams);
Packit Service cd2a00
}
Packit Service cd2a00
Packit Service cd2a00
/*
Packit Service cd2a00
 * start and stop callbacks - just trigger slave PCM
Packit Service cd2a00
 */
Packit Service cd2a00
static int a52_start(snd_pcm_ioplug_t *io)
Packit Service cd2a00
{
Packit Service cd2a00
	struct a52_ctx *rec = io->private_data;
Packit Service cd2a00
Packit Service cd2a00
	/* When trying to start a PCM that's already running, the result is
Packit Service cd2a00
	   EBADFD. We might have implicitly started the buffer by filling it
Packit Service cd2a00
	   up, so just ignore this request if we're already running. */
Packit Service cd2a00
	if (snd_pcm_state(rec->slave) == SND_PCM_STATE_RUNNING)
Packit Service cd2a00
		return 0;
Packit Service cd2a00
Packit Service cd2a00
	return snd_pcm_start(rec->slave);
Packit Service cd2a00
}
Packit Service cd2a00
Packit Service cd2a00
static int a52_stop(snd_pcm_ioplug_t *io)
Packit Service cd2a00
{
Packit Service cd2a00
	struct a52_ctx *rec = io->private_data;
Packit Service cd2a00
Packit Service cd2a00
	return snd_pcm_drop(rec->slave);
Packit Service cd2a00
}
Packit Service cd2a00
Packit Service cd2a00
/* release resources */
Packit Service cd2a00
static void a52_free(struct a52_ctx *rec)
Packit Service cd2a00
{
Packit Service cd2a00
	if (rec->avctx) {
Packit Service cd2a00
		avcodec_close(rec->avctx);
Packit Service cd2a00
		av_free(rec->avctx);
Packit Service cd2a00
		rec->avctx = NULL;
Packit Service cd2a00
	}
Packit Service cd2a00
Packit Service cd2a00
#ifdef USE_AVCODEC_FRAME
Packit Service cd2a00
	if (rec->frame) {
Packit Service cd2a00
		av_freep(&rec->frame->data[0]);
Packit Service cd2a00
		rec->inbuf = NULL;
Packit Service cd2a00
	}
Packit Service cd2a00
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(54, 28, 0)
Packit Service cd2a00
	av_frame_free(&rec->frame);
Packit Service cd2a00
#else
Packit Service cd2a00
	av_freep(&rec->frame);
Packit Service cd2a00
#endif
Packit Service cd2a00
#endif
Packit Service cd2a00
Packit Service cd2a00
	free(rec->inbuf);
Packit Service cd2a00
	rec->inbuf = NULL;
Packit Service cd2a00
	free(rec->outbuf);
Packit Service cd2a00
	rec->outbuf = NULL;
Packit Service cd2a00
}
Packit Service cd2a00
Packit Service cd2a00
/*
Packit Service cd2a00
 * prepare callback
Packit Service cd2a00
 *
Packit Service cd2a00
 * Allocate internal buffers and set up libavcodec
Packit Service cd2a00
 */
Packit Service cd2a00
Packit Service cd2a00
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52, 3, 0)
Packit Service cd2a00
static void set_channel_layout(snd_pcm_ioplug_t *io)
Packit Service cd2a00
{
Packit Service cd2a00
	struct a52_ctx *rec = io->private_data;
Packit Service cd2a00
	switch (io->channels) {
Packit Service cd2a00
	case 2:
Packit Service cd2a00
		rec->avctx->channel_layout = AV_CH_LAYOUT_STEREO;
Packit Service cd2a00
		break;
Packit Service cd2a00
	case 4:
Packit Service cd2a00
		rec->avctx->channel_layout = AV_CH_LAYOUT_QUAD;
Packit Service cd2a00
		break;
Packit Service cd2a00
	case 6:
Packit Service cd2a00
		rec->avctx->channel_layout = AV_CH_LAYOUT_5POINT1;
Packit Service cd2a00
		break;
Packit Service cd2a00
	default:
Packit Service cd2a00
		break;
Packit Service cd2a00
	}
Packit Service cd2a00
}
Packit Service cd2a00
#else
Packit Service cd2a00
#define set_channel_layout(io) /* NOP */
Packit Service cd2a00
#endif
Packit Service cd2a00
Packit Service cd2a00
static int alloc_input_buffer(snd_pcm_ioplug_t *io)
Packit Service cd2a00
{
Packit Service cd2a00
	struct a52_ctx *rec = io->private_data;
Packit Service cd2a00
#ifdef USE_AVCODEC_FRAME
Packit Service cd2a00
	rec->frame = av_frame_alloc();
Packit Service cd2a00
	if (!rec->frame)
Packit Service cd2a00
		return -ENOMEM;
Packit Service cd2a00
	if (av_samples_alloc(rec->frame->data, rec->frame->linesize,
Packit Service cd2a00
			     io->channels, rec->avctx->frame_size,
Packit Service cd2a00
			     rec->avctx->sample_fmt, 0) < 0)
Packit Service cd2a00
		return -ENOMEM;
Packit Service cd2a00
	rec->frame->nb_samples = rec->avctx->frame_size;
Packit Service cd2a00
	rec->inbuf = (short *)rec->frame->data[0];
Packit Service cd2a00
#else
Packit Service cd2a00
	rec->inbuf = malloc(rec->avctx->frame_size * 2 * io->channels);
Packit Service cd2a00
#endif
Packit Service cd2a00
	if (!rec->inbuf)
Packit Service cd2a00
		return -ENOMEM;
Packit Service cd2a00
	return 0;
Packit Service cd2a00
}
Packit Service cd2a00
Packit Service cd2a00
static int a52_prepare(snd_pcm_ioplug_t *io)
Packit Service cd2a00
{
Packit Service cd2a00
	struct a52_ctx *rec = io->private_data;
Packit Service cd2a00
	int err;
Packit Service cd2a00
Packit Service cd2a00
	a52_free(rec);
Packit Service cd2a00
Packit Service cd2a00
#ifdef USE_AVCODEC_FRAME
Packit Service cd2a00
	rec->avctx = avcodec_alloc_context3(rec->codec);
Packit Service cd2a00
#else
Packit Service cd2a00
	rec->avctx = avcodec_alloc_context();
Packit Service cd2a00
#endif
Packit Service cd2a00
	if (!rec->avctx)
Packit Service cd2a00
		return -ENOMEM;
Packit Service cd2a00
Packit Service cd2a00
	rec->avctx->bit_rate = rec->bitrate * 1000;
Packit Service cd2a00
	rec->avctx->sample_rate = io->rate;
Packit Service cd2a00
	rec->avctx->channels = io->channels;
Packit Service cd2a00
	rec->avctx->sample_fmt = rec->av_format;
Packit Service cd2a00
Packit Service cd2a00
	set_channel_layout(io);
Packit Service cd2a00
Packit Service cd2a00
Packit Service cd2a00
#ifdef USE_AVCODEC_FRAME
Packit Service cd2a00
	err = avcodec_open2(rec->avctx, rec->codec, NULL);
Packit Service cd2a00
#else
Packit Service cd2a00
	err = avcodec_open(rec->avctx, rec->codec);
Packit Service cd2a00
#endif
Packit Service cd2a00
	if (err < 0)
Packit Service cd2a00
		return -EINVAL;
Packit Service cd2a00
Packit Service cd2a00
	rec->outbuf_size = rec->avctx->frame_size * 4;
Packit Service cd2a00
	rec->outbuf = malloc(rec->outbuf_size);
Packit Service cd2a00
	if (! rec->outbuf)
Packit Service cd2a00
		return -ENOMEM;
Packit Service cd2a00
Packit Service cd2a00
	if (alloc_input_buffer(io))
Packit Service cd2a00
		return -ENOMEM;
Packit Service cd2a00
Packit Service cd2a00
	rec->transfer = 0;
Packit Service cd2a00
	rec->remain = 0;
Packit Service cd2a00
	rec->filled = 0;
Packit Service cd2a00
Packit Service cd2a00
	return snd_pcm_prepare(rec->slave);
Packit Service cd2a00
}
Packit Service cd2a00
Packit Service cd2a00
/*
Packit Service cd2a00
 * poll-related callbacks - just pass to slave PCM
Packit Service cd2a00
 */
Packit Service cd2a00
static int a52_poll_descriptors_count(snd_pcm_ioplug_t *io)
Packit Service cd2a00
{
Packit Service cd2a00
	struct a52_ctx *rec = io->private_data;
Packit Service cd2a00
	return snd_pcm_poll_descriptors_count(rec->slave);
Packit Service cd2a00
}
Packit Service cd2a00
Packit Service cd2a00
static int a52_poll_descriptors(snd_pcm_ioplug_t *io, struct pollfd *pfd,
Packit Service cd2a00
				unsigned int space)
Packit Service cd2a00
{
Packit Service cd2a00
	struct a52_ctx *rec = io->private_data;
Packit Service cd2a00
	return snd_pcm_poll_descriptors(rec->slave, pfd, space);
Packit Service cd2a00
}
Packit Service cd2a00
Packit Service cd2a00
static int a52_poll_revents(snd_pcm_ioplug_t *io, struct pollfd *pfd,
Packit Service cd2a00
			    unsigned int nfds, unsigned short *revents)
Packit Service cd2a00
{
Packit Service cd2a00
	struct a52_ctx *rec = io->private_data;
Packit Service cd2a00
	return snd_pcm_poll_descriptors_revents(rec->slave, pfd, nfds, revents);
Packit Service cd2a00
}
Packit Service cd2a00
Packit Service cd2a00
/*
Packit Service cd2a00
 * close callback
Packit Service cd2a00
 */
Packit Service cd2a00
static int a52_close(snd_pcm_ioplug_t *io)
Packit Service cd2a00
{
Packit Service cd2a00
	struct a52_ctx *rec = io->private_data;
Packit Service cd2a00
	snd_pcm_t *slave = rec->slave;
Packit Service cd2a00
Packit Service cd2a00
	a52_free(rec);
Packit Service cd2a00
	if (slave) {
Packit Service cd2a00
		rec->slave = NULL;
Packit Service cd2a00
		return snd_pcm_close(slave);
Packit Service cd2a00
	}
Packit Service cd2a00
	return 0;
Packit Service cd2a00
}
Packit Service cd2a00
			      
Packit Service cd2a00
#if SND_PCM_IOPLUG_VERSION >= 0x10002
Packit Service cd2a00
static unsigned int chmap4[4] = {
Packit Service cd2a00
	SND_CHMAP_FL, SND_CHMAP_FR,
Packit Service cd2a00
	SND_CHMAP_RL, SND_CHMAP_RR,
Packit Service cd2a00
};
Packit Service cd2a00
static unsigned int chmap6[6] = {
Packit Service cd2a00
	SND_CHMAP_FL, SND_CHMAP_FR,
Packit Service cd2a00
	SND_CHMAP_RL, SND_CHMAP_RR,
Packit Service cd2a00
	SND_CHMAP_FC, SND_CHMAP_LFE,
Packit Service cd2a00
};
Packit Service cd2a00
Packit Service cd2a00
static snd_pcm_chmap_query_t **a52_query_chmaps(snd_pcm_ioplug_t *io ATTRIBUTE_UNUSED)
Packit Service cd2a00
{
Packit Service cd2a00
	snd_pcm_chmap_query_t **maps;
Packit Service cd2a00
	int i;
Packit Service cd2a00
Packit Service cd2a00
	maps = calloc(4, sizeof(void *));
Packit Service cd2a00
	if (!maps)
Packit Service cd2a00
		return NULL;
Packit Service cd2a00
	for (i = 0; i < 3; i++) {
Packit Service cd2a00
		snd_pcm_chmap_query_t *p;
Packit Service cd2a00
		p = maps[i] = calloc((i + 1) * 2 + 2, sizeof(int));
Packit Service cd2a00
		if (!p) {
Packit Service cd2a00
			snd_pcm_free_chmaps(maps);
Packit Service cd2a00
			return NULL;
Packit Service cd2a00
		}
Packit Service cd2a00
		p->type = SND_CHMAP_TYPE_FIXED;
Packit Service cd2a00
		p->map.channels = (i + 1) * 2;
Packit Service cd2a00
		memcpy(p->map.pos, i < 2 ? chmap4 : chmap6,
Packit Service cd2a00
		       (i + 1) * 2 * sizeof(int));
Packit Service cd2a00
	}
Packit Service cd2a00
	return maps;
Packit Service cd2a00
}
Packit Service cd2a00
Packit Service cd2a00
static snd_pcm_chmap_t *a52_get_chmap(snd_pcm_ioplug_t *io)
Packit Service cd2a00
{
Packit Service cd2a00
	snd_pcm_chmap_t *map;
Packit Service cd2a00
Packit Service cd2a00
	if ((io->channels % 2) || io->channels < 2 || io->channels > 6)
Packit Service cd2a00
		return NULL;
Packit Service cd2a00
	map = malloc((io->channels + 1) * sizeof(int));
Packit Service cd2a00
	if (!map)
Packit Service cd2a00
		return NULL;
Packit Service cd2a00
	map->channels = io->channels;
Packit Service cd2a00
	memcpy(map->pos, io->channels < 6 ? chmap4 : chmap6,
Packit Service cd2a00
	       io->channels * sizeof(int));
Packit Service cd2a00
	return map;
Packit Service cd2a00
}
Packit Service cd2a00
#endif /* SND_PCM_IOPLUG_VERSION >= 0x10002 */
Packit Service cd2a00
Packit Service cd2a00
/*
Packit Service cd2a00
 * callback table
Packit Service cd2a00
 */
Packit Service cd2a00
static snd_pcm_ioplug_callback_t a52_ops = {
Packit Service cd2a00
	.start = a52_start,
Packit Service cd2a00
	.stop = a52_stop,
Packit Service cd2a00
	.pointer = a52_pointer,
Packit Service cd2a00
	.transfer = a52_transfer,
Packit Service cd2a00
	.close = a52_close,
Packit Service cd2a00
	.hw_params = a52_hw_params,
Packit Service cd2a00
	.hw_free = a52_hw_free,
Packit Service cd2a00
	.sw_params = a52_sw_params,
Packit Service cd2a00
	.prepare = a52_prepare,
Packit Service cd2a00
	.drain = a52_drain,
Packit Service cd2a00
	.poll_descriptors_count = a52_poll_descriptors_count,
Packit Service cd2a00
	.poll_descriptors = a52_poll_descriptors,
Packit Service cd2a00
	.poll_revents = a52_poll_revents,
Packit Service cd2a00
#if SND_PCM_IOPLUG_VERSION >= 0x10002
Packit Service cd2a00
	.query_chmaps = a52_query_chmaps,
Packit Service cd2a00
	.get_chmap = a52_get_chmap,
Packit Service cd2a00
#endif /* SND_PCM_IOPLUG_VERSION >= 0x10002 */
Packit Service cd2a00
};
Packit Service cd2a00
Packit Service cd2a00
/*
Packit Service cd2a00
 * set up h/w constraints
Packit Service cd2a00
 *
Packit Service cd2a00
 * set the period size identical with A52 frame size.
Packit Service cd2a00
 * the max buffer size is calculated from the max buffer size
Packit Service cd2a00
 * of the slave PCM
Packit Service cd2a00
 */
Packit Service cd2a00
Packit Service cd2a00
#define A52_FRAME_SIZE	1536
Packit Service cd2a00
Packit Service cd2a00
#define ARRAY_SIZE(ary)	(sizeof(ary)/sizeof(ary[0]))
Packit Service cd2a00
Packit Service cd2a00
static int a52_set_hw_constraint(struct a52_ctx *rec)
Packit Service cd2a00
{
Packit Service cd2a00
	static unsigned int accesses[] = {
Packit Service cd2a00
		SND_PCM_ACCESS_MMAP_INTERLEAVED,
Packit Service cd2a00
		SND_PCM_ACCESS_MMAP_NONINTERLEAVED,
Packit Service cd2a00
		SND_PCM_ACCESS_RW_INTERLEAVED,
Packit Service cd2a00
		SND_PCM_ACCESS_RW_NONINTERLEAVED
Packit Service cd2a00
	};
Packit Service cd2a00
	static unsigned int accesses_planar[] = {
Packit Service cd2a00
		SND_PCM_ACCESS_MMAP_NONINTERLEAVED,
Packit Service cd2a00
		SND_PCM_ACCESS_RW_NONINTERLEAVED
Packit Service cd2a00
	};
Packit Service cd2a00
	unsigned int formats[] = { SND_PCM_FORMAT_S16 };
Packit Service cd2a00
	int err;
Packit Service cd2a00
	snd_pcm_uframes_t buffer_max;
Packit Service cd2a00
	unsigned int period_bytes, max_periods;
Packit Service cd2a00
Packit Service cd2a00
	if (use_planar(rec))
Packit Service cd2a00
		err = snd_pcm_ioplug_set_param_list(&rec->io,
Packit Service cd2a00
						    SND_PCM_IOPLUG_HW_ACCESS,
Packit Service cd2a00
						    ARRAY_SIZE(accesses_planar),
Packit Service cd2a00
						    accesses_planar);
Packit Service cd2a00
	else
Packit Service cd2a00
		err = snd_pcm_ioplug_set_param_list(&rec->io,
Packit Service cd2a00
						    SND_PCM_IOPLUG_HW_ACCESS,
Packit Service cd2a00
						    ARRAY_SIZE(accesses),
Packit Service cd2a00
						    accesses);
Packit Service cd2a00
	if (err < 0)
Packit Service cd2a00
		return err;
Packit Service cd2a00
Packit Service cd2a00
	if ((err = snd_pcm_ioplug_set_param_list(&rec->io, SND_PCM_IOPLUG_HW_FORMAT,
Packit Service cd2a00
						 ARRAY_SIZE(formats), formats)) < 0 ||
Packit Service cd2a00
	    (err = snd_pcm_ioplug_set_param_minmax(&rec->io, SND_PCM_IOPLUG_HW_CHANNELS,
Packit Service cd2a00
						   rec->channels, rec->channels)) < 0 ||
Packit Service cd2a00
	    (err = snd_pcm_ioplug_set_param_minmax(&rec->io, SND_PCM_IOPLUG_HW_RATE,
Packit Service cd2a00
						   rec->rate, rec->rate)) < 0)
Packit Service cd2a00
		return err;
Packit Service cd2a00
Packit Service cd2a00
	if ((err = a52_slave_hw_params_half(rec)) < 0)
Packit Service cd2a00
		return err;
Packit Service cd2a00
Packit Service cd2a00
	snd_pcm_hw_params_get_buffer_size_max(rec->hw_params, &buffer_max);
Packit Service cd2a00
	period_bytes = A52_FRAME_SIZE * 2 * rec->channels;
Packit Service cd2a00
	max_periods = buffer_max / A52_FRAME_SIZE;
Packit Service cd2a00
Packit Service cd2a00
	if ((err = snd_pcm_ioplug_set_param_minmax(&rec->io, SND_PCM_IOPLUG_HW_PERIOD_BYTES,
Packit Service cd2a00
						   period_bytes, period_bytes)) < 0 ||
Packit Service cd2a00
	    (err = snd_pcm_ioplug_set_param_minmax(&rec->io, SND_PCM_IOPLUG_HW_PERIODS,
Packit Service cd2a00
						   2, max_periods)) < 0)
Packit Service cd2a00
		return err;
Packit Service cd2a00
Packit Service cd2a00
	return 0;
Packit Service cd2a00
}
Packit Service cd2a00
Packit Service cd2a00
/*
Packit Service cd2a00
 * Main entry point
Packit Service cd2a00
 */
Packit Service cd2a00
SND_PCM_PLUGIN_DEFINE_FUNC(a52)
Packit Service cd2a00
{
Packit Service cd2a00
	snd_config_iterator_t i, next;
Packit Service cd2a00
	int err;
Packit Service cd2a00
	const char *card = NULL;
Packit Service cd2a00
	const char *pcm_string = NULL;
Packit Service cd2a00
	unsigned int rate = 48000;
Packit Service cd2a00
	unsigned int bitrate = 448;
Packit Service cd2a00
	unsigned int channels = 6;
Packit Service cd2a00
	snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE;
Packit Service cd2a00
	char devstr[128], tmpcard[8];
Packit Service cd2a00
	struct a52_ctx *rec;
Packit Service cd2a00
	
Packit Service cd2a00
	if (stream != SND_PCM_STREAM_PLAYBACK) {
Packit Service cd2a00
		SNDERR("a52 is only for playback");
Packit Service cd2a00
		return -EINVAL;
Packit Service cd2a00
	}
Packit Service cd2a00
Packit Service cd2a00
	snd_config_for_each(i, next, conf) {
Packit Service cd2a00
		snd_config_t *n = snd_config_iterator_entry(i);
Packit Service cd2a00
		const char *id;
Packit Service cd2a00
		if (snd_config_get_id(n, &id) < 0)
Packit Service cd2a00
			continue;
Packit Service cd2a00
		if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0 || strcmp(id, "hint") == 0)
Packit Service cd2a00
			continue;
Packit Service cd2a00
		if (strcmp(id, "card") == 0) {
Packit Service cd2a00
			if (snd_config_get_string(n, &card) < 0) {
Packit Service cd2a00
				long val;
Packit Service cd2a00
				err = snd_config_get_integer(n, &val;;
Packit Service cd2a00
				if (err < 0) {
Packit Service cd2a00
					SNDERR("Invalid type for %s", id);
Packit Service cd2a00
					return -EINVAL;
Packit Service cd2a00
				}
Packit Service cd2a00
				snprintf(tmpcard, sizeof(tmpcard), "%ld", val);
Packit Service cd2a00
				card = tmpcard;
Packit Service cd2a00
			}
Packit Service cd2a00
			continue;
Packit Service cd2a00
		}
Packit Service cd2a00
		if (strcmp(id, "slavepcm") == 0) {
Packit Service cd2a00
			if (snd_config_get_string(n, &pcm_string) < 0) {
Packit Service cd2a00
				SNDERR("a52 slavepcm must be a string");
Packit Service cd2a00
				return -EINVAL;
Packit Service cd2a00
			}
Packit Service cd2a00
			continue;
Packit Service cd2a00
		}
Packit Service cd2a00
		if (strcmp(id, "rate") == 0) {
Packit Service cd2a00
			long val;
Packit Service cd2a00
			if (snd_config_get_integer(n, &val) < 0) {
Packit Service cd2a00
				SNDERR("Invalid type for %s", id);
Packit Service cd2a00
				return -EINVAL;
Packit Service cd2a00
			}
Packit Service cd2a00
			rate = val;
Packit Service cd2a00
			if (rate != 44100 && rate != 48000) {
Packit Service cd2a00
				SNDERR("rate must be 44100 or 48000");
Packit Service cd2a00
				return -EINVAL;
Packit Service cd2a00
			}
Packit Service cd2a00
			continue;
Packit Service cd2a00
		}
Packit Service cd2a00
		if (strcmp(id, "bitrate") == 0) {
Packit Service cd2a00
			long val;
Packit Service cd2a00
			if (snd_config_get_integer(n, &val) < 0) {
Packit Service cd2a00
				SNDERR("Invalid type for %s", id);
Packit Service cd2a00
				return -EINVAL;
Packit Service cd2a00
			}
Packit Service cd2a00
			bitrate = val;
Packit Service cd2a00
			if (bitrate < 128 || bitrate > 1000) {
Packit Service cd2a00
				SNDERR("Invalid bitrate value %d", bitrate);
Packit Service cd2a00
				return -EINVAL;
Packit Service cd2a00
			}
Packit Service cd2a00
			continue;
Packit Service cd2a00
		}
Packit Service cd2a00
		if (strcmp(id, "channels") == 0) {
Packit Service cd2a00
			long val;
Packit Service cd2a00
			if (snd_config_get_integer(n, &val) < 0) {
Packit Service cd2a00
				SNDERR("Invalid type for %s", id);
Packit Service cd2a00
				return -EINVAL;
Packit Service cd2a00
			}
Packit Service cd2a00
			channels = val;
Packit Service cd2a00
			if (channels != 2 && channels != 4 && channels != 6) {
Packit Service cd2a00
				SNDERR("channels must be 2, 4 or 6");
Packit Service cd2a00
				return -EINVAL;
Packit Service cd2a00
			}
Packit Service cd2a00
			continue;
Packit Service cd2a00
		}
Packit Service cd2a00
		if (strcmp(id, "format") == 0) {
Packit Service cd2a00
			const char *str;
Packit Service cd2a00
			err = snd_config_get_string(n, &str);
Packit Service cd2a00
			if (err < 0) {
Packit Service cd2a00
				SNDERR("invalid type for %s", id);
Packit Service cd2a00
				return -EINVAL;
Packit Service cd2a00
			}
Packit Service cd2a00
			format = snd_pcm_format_value(str);
Packit Service cd2a00
			if (format == SND_PCM_FORMAT_UNKNOWN) {
Packit Service cd2a00
				SNDERR("unknown format %s", str);
Packit Service cd2a00
				return -EINVAL;
Packit Service cd2a00
			}
Packit Service cd2a00
			if (format != SND_PCM_FORMAT_S16_LE &&
Packit Service cd2a00
			    format != SND_PCM_FORMAT_S16_BE) {
Packit Service cd2a00
				SNDERR("Only S16_LE/BE formats are allowed");
Packit Service cd2a00
				return -EINVAL;
Packit Service cd2a00
			}
Packit Service cd2a00
			continue;
Packit Service cd2a00
		}
Packit Service cd2a00
		SNDERR("Unknown field %s", id);
Packit Service cd2a00
		return -EINVAL;
Packit Service cd2a00
	}
Packit Service cd2a00
Packit Service cd2a00
	rec = calloc(1, sizeof(*rec));
Packit Service cd2a00
	if (! rec) {
Packit Service cd2a00
		SNDERR("cannot allocate");
Packit Service cd2a00
		return -ENOMEM;
Packit Service cd2a00
	}
Packit Service cd2a00
Packit Service cd2a00
	rec->rate = rate;
Packit Service cd2a00
	rec->bitrate = bitrate;
Packit Service cd2a00
	rec->channels = channels;
Packit Service cd2a00
	rec->format = format;
Packit Service cd2a00
Packit Service cd2a00
#ifndef USE_AVCODEC_FRAME
Packit Service cd2a00
	avcodec_init();
Packit Service cd2a00
#endif
Packit Service cd2a00
	avcodec_register_all();
Packit Service cd2a00
Packit Service cd2a00
	rec->codec = avcodec_find_encoder_by_name("ac3_fixed");
Packit Service cd2a00
	if (rec->codec == NULL)
Packit Service cd2a00
		rec->codec = avcodec_find_encoder_by_name("ac3");
Packit Service cd2a00
	if (rec->codec == NULL) 
Packit Service cd2a00
		rec->codec = avcodec_find_encoder(AV_CODEC_ID_AC3);
Packit Service cd2a00
	if (rec->codec == NULL) {
Packit Service cd2a00
		SNDERR("Cannot find codec engine");
Packit Service cd2a00
		err = -EINVAL;
Packit Service cd2a00
		goto error;
Packit Service cd2a00
	}
Packit Service cd2a00
Packit Service cd2a00
	if (! pcm_string || pcm_string[0] == '\0') {
Packit Service cd2a00
		snprintf(devstr, sizeof(devstr),
Packit Service cd2a00
			 "iec958:{AES0 0x%x AES1 0x%x AES2 0x%x AES3 0x%x %s%s}",
Packit Service cd2a00
			 IEC958_AES0_CON_EMPHASIS_NONE | IEC958_AES0_NONAUDIO |
Packit Service cd2a00
			 IEC958_AES0_CON_NOT_COPYRIGHT,
Packit Service cd2a00
			 IEC958_AES1_CON_ORIGINAL | IEC958_AES1_CON_PCM_CODER,
Packit Service cd2a00
			 0, rate == 48000 ? IEC958_AES3_CON_FS_48000 : IEC958_AES3_CON_FS_44100,
Packit Service cd2a00
			 card ? " CARD " : "",
Packit Service cd2a00
			 card ? card : "");
Packit Service cd2a00
		err = snd_pcm_open(&rec->slave, devstr, stream, mode);
Packit Service cd2a00
		if (err < 0)
Packit Service cd2a00
			goto error;
Packit Service cd2a00
		/* in case the slave doesn't support S16 format */
Packit Service cd2a00
		err = snd_pcm_linear_open(&rec->slave, NULL, SND_PCM_FORMAT_S16,
Packit Service cd2a00
					  rec->slave, 1);
Packit Service cd2a00
		if (err < 0)
Packit Service cd2a00
			goto error;
Packit Service cd2a00
	} else {
Packit Service cd2a00
		err = snd_pcm_open(&rec->slave, pcm_string, stream, mode);
Packit Service cd2a00
		if (err < 0)
Packit Service cd2a00
			goto error;
Packit Service cd2a00
	}
Packit Service cd2a00
Packit Service cd2a00
	rec->io.version = SND_PCM_IOPLUG_VERSION;
Packit Service cd2a00
	rec->io.name = "A52 Output Plugin";
Packit Service cd2a00
	rec->io.mmap_rw = 0;
Packit Service cd2a00
	rec->io.callback = &a52_ops;
Packit Service cd2a00
	rec->io.private_data = rec;
Packit Service cd2a00
#ifdef USE_AVCODEC_FRAME
Packit Service cd2a00
	rec->av_format = rec->codec->sample_fmts[0];
Packit Service cd2a00
	rec->is_planar = av_sample_fmt_is_planar(rec->av_format);
Packit Service cd2a00
#else
Packit Service cd2a00
	rec->av_format = AV_SAMPLE_FMT_S16;
Packit Service cd2a00
#endif
Packit Service cd2a00
Packit Service cd2a00
	err = snd_pcm_ioplug_create(&rec->io, name, stream, mode);
Packit Service cd2a00
	if (err < 0)
Packit Service cd2a00
		goto error;
Packit Service cd2a00
Packit Service cd2a00
	if ((err = a52_set_hw_constraint(rec)) < 0) {
Packit Service cd2a00
		snd_pcm_ioplug_delete(&rec->io);
Packit Service cd2a00
		goto error;
Packit Service cd2a00
	}
Packit Service cd2a00
Packit Service cd2a00
	*pcmp = rec->io.pcm;
Packit Service cd2a00
	return 0;
Packit Service cd2a00
Packit Service cd2a00
 error:
Packit Service cd2a00
	if (rec->slave)
Packit Service cd2a00
		snd_pcm_close(rec->slave);
Packit Service cd2a00
	free(rec);
Packit Service cd2a00
	return err;
Packit Service cd2a00
}
Packit Service cd2a00
Packit Service cd2a00
SND_PCM_PLUGIN_SYMBOL(a52);