Blame src/pcm/pcm_iec958.c

Packit Service db8eaa
/**
Packit Service db8eaa
 * \file pcm/pcm_iec958.c
Packit Service db8eaa
 * \ingroup PCM_Plugins
Packit Service db8eaa
 * \brief PCM IEC958 Subframe Conversion Plugin Interface
Packit Service db8eaa
 * \author Takashi Iwai <tiwai@suse.de>
Packit Service db8eaa
 * \date 2004
Packit Service db8eaa
 */
Packit Service db8eaa
/*
Packit Service db8eaa
 *  PCM - IEC958 Subframe Conversion Plugin
Packit Service db8eaa
 *  Copyright (c) 2004 by Takashi Iwai <tiwai@suse.de>
Packit Service db8eaa
 *
Packit Service db8eaa
 *
Packit Service db8eaa
 *   This library is free software; you can redistribute it and/or modify
Packit Service db8eaa
 *   it under the terms of the GNU Lesser General Public License as
Packit Service db8eaa
 *   published by the Free Software Foundation; either version 2.1 of
Packit Service db8eaa
 *   the License, or (at your option) any later version.
Packit Service db8eaa
 *
Packit Service db8eaa
 *   This program is distributed in the hope that it will be useful,
Packit Service db8eaa
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service db8eaa
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service db8eaa
 *   GNU Lesser General Public License for more details.
Packit Service db8eaa
 *
Packit Service db8eaa
 *   You should have received a copy of the GNU Lesser General Public
Packit Service db8eaa
 *   License along with this library; if not, write to the Free Software
Packit Service db8eaa
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
Packit Service db8eaa
 *
Packit Service db8eaa
 */
Packit Service db8eaa
  
Packit Service db8eaa
#include "bswap.h"
Packit Service db8eaa
#include "pcm_local.h"
Packit Service db8eaa
#include "pcm_plugin.h"
Packit Service db8eaa
Packit Service db8eaa
#include "plugin_ops.h"
Packit Service db8eaa
Packit Service db8eaa
#ifndef PIC
Packit Service db8eaa
/* entry for static linking */
Packit Service db8eaa
const char *_snd_module_pcm_iec958 = "";
Packit Service db8eaa
#endif
Packit Service db8eaa
Packit Service db8eaa
/*
Packit Service db8eaa
 */
Packit Service db8eaa
Packit Service db8eaa
#ifndef DOC_HIDDEN
Packit Service db8eaa
Packit Service db8eaa
typedef struct snd_pcm_iec958 snd_pcm_iec958_t;
Packit Service db8eaa
Packit Service db8eaa
typedef void (*iec958_f)(snd_pcm_iec958_t *iec,
Packit Service db8eaa
			 const snd_pcm_channel_area_t *dst_areas,
Packit Service db8eaa
			 snd_pcm_uframes_t dst_offset,
Packit Service db8eaa
			 const snd_pcm_channel_area_t *src_areas,
Packit Service db8eaa
			 snd_pcm_uframes_t src_offset,
Packit Service db8eaa
			 unsigned int channels, snd_pcm_uframes_t frames);
Packit Service db8eaa
Packit Service db8eaa
struct snd_pcm_iec958 {
Packit Service db8eaa
	/* This field need to be the first */
Packit Service db8eaa
	snd_pcm_plugin_t plug;
Packit Service db8eaa
	unsigned int getput_idx;
Packit Service db8eaa
	iec958_f func;
Packit Service db8eaa
	snd_pcm_format_t sformat;
Packit Service db8eaa
	snd_pcm_format_t format;
Packit Service db8eaa
	unsigned int counter;
Packit Service db8eaa
	unsigned char status[24];
Packit Service db8eaa
	unsigned int byteswap;
Packit Service db8eaa
	unsigned char preamble[3];	/* B/M/W or Z/X/Y */
Packit Service db8eaa
	snd_pcm_fast_ops_t fops;
Packit Service db8eaa
	int hdmi_mode;
Packit Service db8eaa
};
Packit Service db8eaa
Packit Service db8eaa
enum { PREAMBLE_Z, PREAMBLE_X, PREAMBLE_Y };
Packit Service db8eaa
Packit Service db8eaa
#endif /* DOC_HIDDEN */
Packit Service db8eaa
Packit Service db8eaa
/*
Packit Service db8eaa
 * Determine parity for time slots 4 upto 30
Packit Service db8eaa
 * to be sure that bit 4 upt 31 will carry
Packit Service db8eaa
 * an even number of ones and zeros.
Packit Service db8eaa
 */
Packit Service db8eaa
static unsigned int iec958_parity(unsigned int data)
Packit Service db8eaa
{
Packit Service db8eaa
	unsigned int parity;
Packit Service db8eaa
	int bit;
Packit Service db8eaa
Packit Service db8eaa
	data >>= 4;     /* start from bit 4 */
Packit Service db8eaa
	parity = 0;
Packit Service db8eaa
	for (bit = 4; bit <= 30; bit++) {
Packit Service db8eaa
		if (data & 1)
Packit Service db8eaa
			parity++;
Packit Service db8eaa
		data >>= 1;
Packit Service db8eaa
	}
Packit Service db8eaa
	return (parity & 1);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/*
Packit Service db8eaa
 * Compose 32bit IEC958 subframe, two sub frames
Packit Service db8eaa
 * build one frame with two channels.
Packit Service db8eaa
 *
Packit Service db8eaa
 * bit 0-3  = preamble
Packit Service db8eaa
 *     4-7  = AUX (=0)
Packit Service db8eaa
 *     8-27 = data (12-27 for 16bit, 8-27 for 20bit, and 24bit without AUX)
Packit Service db8eaa
 *     28   = validity (0 for valid data, else 'in error')
Packit Service db8eaa
 *     29   = user data (0)
Packit Service db8eaa
 *     30   = channel status (24 bytes for 192 frames)
Packit Service db8eaa
 *     31   = parity
Packit Service db8eaa
 */
Packit Service db8eaa
Packit Service db8eaa
static inline uint32_t iec958_subframe(snd_pcm_iec958_t *iec, uint32_t data, int channel)
Packit Service db8eaa
{
Packit Service db8eaa
	unsigned int byte = iec->counter >> 3;
Packit Service db8eaa
	unsigned int mask = 1 << (iec->counter - (byte << 3));
Packit Service db8eaa
Packit Service db8eaa
	/* bit 4-27 */
Packit Service db8eaa
	data >>= 4;
Packit Service db8eaa
	data &= ~0xf;
Packit Service db8eaa
Packit Service db8eaa
	/* set IEC status bits (up to 192 bits) */
Packit Service db8eaa
	if (iec->status[byte] & mask)
Packit Service db8eaa
		data |= 0x40000000;
Packit Service db8eaa
Packit Service db8eaa
	if (iec958_parity(data))	/* parity bit 4-30 */
Packit Service db8eaa
		data |= 0x80000000;
Packit Service db8eaa
Packit Service db8eaa
	/* Preamble */
Packit Service db8eaa
	if (channel)
Packit Service db8eaa
		data |= iec->preamble[PREAMBLE_Y];	/* odd sub frame, 'Y' */
Packit Service db8eaa
	else if (! iec->counter)
Packit Service db8eaa
		data |= iec->preamble[PREAMBLE_Z];	/* Block start, 'Z' */
Packit Service db8eaa
	else
Packit Service db8eaa
		data |= iec->preamble[PREAMBLE_X];	/* even sub frame, 'X' */
Packit Service db8eaa
Packit Service db8eaa
	if (iec->byteswap)
Packit Service db8eaa
		data = bswap_32(data);
Packit Service db8eaa
Packit Service db8eaa
	return data;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static inline int32_t iec958_to_s32(snd_pcm_iec958_t *iec, uint32_t data)
Packit Service db8eaa
{
Packit Service db8eaa
	if (iec->byteswap)
Packit Service db8eaa
		data = bswap_32(data);
Packit Service db8eaa
	data &= ~0xf;
Packit Service db8eaa
	data <<= 4;
Packit Service db8eaa
	return (int32_t)data;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
#ifndef DOC_HIDDEN
Packit Service db8eaa
static void snd_pcm_iec958_decode(snd_pcm_iec958_t *iec,
Packit Service db8eaa
				  const snd_pcm_channel_area_t *dst_areas,
Packit Service db8eaa
				  snd_pcm_uframes_t dst_offset,
Packit Service db8eaa
				  const snd_pcm_channel_area_t *src_areas,
Packit Service db8eaa
				  snd_pcm_uframes_t src_offset,
Packit Service db8eaa
				  unsigned int channels, snd_pcm_uframes_t frames)
Packit Service db8eaa
{
Packit Service db8eaa
#define PUT32_LABELS
Packit Service db8eaa
#include "plugin_ops.h"
Packit Service db8eaa
#undef PUT32_LABELS
Packit Service db8eaa
	void *put = put32_labels[iec->getput_idx];
Packit Service db8eaa
	unsigned int channel;
Packit Service db8eaa
	for (channel = 0; channel < channels; ++channel) {
Packit Service db8eaa
		const uint32_t *src;
Packit Service db8eaa
		char *dst;
Packit Service db8eaa
		int src_step, dst_step;
Packit Service db8eaa
		snd_pcm_uframes_t frames1;
Packit Service db8eaa
		const snd_pcm_channel_area_t *src_area = &src_areas[channel];
Packit Service db8eaa
		const snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
Packit Service db8eaa
		src = snd_pcm_channel_area_addr(src_area, src_offset);
Packit Service db8eaa
		dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
Packit Service db8eaa
		src_step = snd_pcm_channel_area_step(src_area) / sizeof(uint32_t);
Packit Service db8eaa
		dst_step = snd_pcm_channel_area_step(dst_area);
Packit Service db8eaa
		frames1 = frames;
Packit Service db8eaa
		while (frames1-- > 0) {
Packit Service db8eaa
			int32_t sample = iec958_to_s32(iec, *src);
Packit Service db8eaa
			goto *put;
Packit Service db8eaa
#define PUT32_END after
Packit Service db8eaa
#include "plugin_ops.h"
Packit Service db8eaa
#undef PUT32_END
Packit Service db8eaa
		after:
Packit Service db8eaa
			src += src_step;
Packit Service db8eaa
			dst += dst_step;
Packit Service db8eaa
		}
Packit Service db8eaa
	}
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static void snd_pcm_iec958_encode(snd_pcm_iec958_t *iec,
Packit Service db8eaa
				  const snd_pcm_channel_area_t *dst_areas,
Packit Service db8eaa
				  snd_pcm_uframes_t dst_offset,
Packit Service db8eaa
				  const snd_pcm_channel_area_t *src_areas,
Packit Service db8eaa
				  snd_pcm_uframes_t src_offset,
Packit Service db8eaa
				  unsigned int channels, snd_pcm_uframes_t frames)
Packit Service db8eaa
{
Packit Service db8eaa
#define GET32_LABELS
Packit Service db8eaa
#include "plugin_ops.h"
Packit Service db8eaa
#undef GET32_LABELS
Packit Service db8eaa
	void *get = get32_labels[iec->getput_idx];
Packit Service db8eaa
	unsigned int channel;
Packit Service db8eaa
	int32_t sample = 0;
Packit Service db8eaa
	int counter = iec->counter;
Packit Service db8eaa
	int single_stream = iec->hdmi_mode &&
Packit Service db8eaa
			    (iec->status[0] & IEC958_AES0_NONAUDIO) &&
Packit Service db8eaa
			    (channels == 8);
Packit Service db8eaa
	int counter_step = single_stream ? ((channels + 1) >> 1) : 1;
Packit Service db8eaa
	for (channel = 0; channel < channels; ++channel) {
Packit Service db8eaa
		const char *src;
Packit Service db8eaa
		uint32_t *dst;
Packit Service db8eaa
		int src_step, dst_step;
Packit Service db8eaa
		snd_pcm_uframes_t frames1;
Packit Service db8eaa
		const snd_pcm_channel_area_t *src_area = &src_areas[channel];
Packit Service db8eaa
		const snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
Packit Service db8eaa
		src = snd_pcm_channel_area_addr(src_area, src_offset);
Packit Service db8eaa
		dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
Packit Service db8eaa
		src_step = snd_pcm_channel_area_step(src_area);
Packit Service db8eaa
		dst_step = snd_pcm_channel_area_step(dst_area) / sizeof(uint32_t);
Packit Service db8eaa
		frames1 = frames;
Packit Service db8eaa
Packit Service db8eaa
		if (single_stream)
Packit Service db8eaa
			iec->counter = (counter + (channel >> 1)) % 192;
Packit Service db8eaa
		else
Packit Service db8eaa
			iec->counter = counter;
Packit Service db8eaa
Packit Service db8eaa
		while (frames1-- > 0) {
Packit Service db8eaa
			goto *get;
Packit Service db8eaa
#define GET32_END after
Packit Service db8eaa
#include "plugin_ops.h"
Packit Service db8eaa
#undef GET32_END
Packit Service db8eaa
		after:
Packit Service db8eaa
			sample = iec958_subframe(iec, sample, channel);
Packit Service db8eaa
			// fprintf(stderr, "%d:%08x\n", frames1, sample);
Packit Service db8eaa
			*dst = sample;
Packit Service db8eaa
			src += src_step;
Packit Service db8eaa
			dst += dst_step;
Packit Service db8eaa
			iec->counter += counter_step;
Packit Service db8eaa
			iec->counter %= 192;
Packit Service db8eaa
		}
Packit Service db8eaa
		if (single_stream) /* set counter to ch0 value for next iteration */
Packit Service db8eaa
			iec->counter = (counter + frames * counter_step) % 192;
Packit Service db8eaa
	}
Packit Service db8eaa
}
Packit Service db8eaa
#endif /* DOC_HIDDEN */
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_iec958_hw_refine_cprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_iec958_t *iec = pcm->private_data;
Packit Service db8eaa
	int err;
Packit Service db8eaa
	snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM };
Packit Service db8eaa
	err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
Packit Service db8eaa
					 &access_mask);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	if (iec->sformat == SND_PCM_FORMAT_IEC958_SUBFRAME_LE ||
Packit Service db8eaa
	    iec->sformat == SND_PCM_FORMAT_IEC958_SUBFRAME_BE) {
Packit Service db8eaa
		snd_pcm_format_mask_t format_mask = { SND_PCM_FMTBIT_LINEAR };
Packit Service db8eaa
		err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT,
Packit Service db8eaa
						 &format_mask);
Packit Service db8eaa
	} else {
Packit Service db8eaa
		snd_pcm_format_mask_t format_mask = {
Packit Service db8eaa
			{ (1U << SND_PCM_FORMAT_IEC958_SUBFRAME_LE) |
Packit Service db8eaa
			  (1U << SND_PCM_FORMAT_IEC958_SUBFRAME_BE) }
Packit Service db8eaa
		};
Packit Service db8eaa
		err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT,
Packit Service db8eaa
						 &format_mask);
Packit Service db8eaa
	}
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	err = _snd_pcm_hw_params_set_subformat(params, SND_PCM_SUBFORMAT_STD);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_iec958_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_iec958_t *iec = pcm->private_data;
Packit Service db8eaa
	snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
Packit Service db8eaa
	_snd_pcm_hw_params_any(sparams);
Packit Service db8eaa
	_snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
Packit Service db8eaa
				   &saccess_mask);
Packit Service db8eaa
	_snd_pcm_hw_params_set_format(sparams, iec->sformat);
Packit Service db8eaa
	_snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_iec958_hw_refine_schange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params,
Packit Service db8eaa
					    snd_pcm_hw_params_t *sparams)
Packit Service db8eaa
{
Packit Service db8eaa
	int err;
Packit Service db8eaa
	unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
Packit Service db8eaa
			      SND_PCM_HW_PARBIT_RATE |
Packit Service db8eaa
			      SND_PCM_HW_PARBIT_PERIOD_SIZE |
Packit Service db8eaa
			      SND_PCM_HW_PARBIT_BUFFER_SIZE |
Packit Service db8eaa
			      SND_PCM_HW_PARBIT_PERIODS |
Packit Service db8eaa
			      SND_PCM_HW_PARBIT_PERIOD_TIME |
Packit Service db8eaa
			      SND_PCM_HW_PARBIT_BUFFER_TIME |
Packit Service db8eaa
			      SND_PCM_HW_PARBIT_TICK_TIME);
Packit Service db8eaa
	err = _snd_pcm_hw_params_refine(sparams, links, params);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
	
Packit Service db8eaa
static int snd_pcm_iec958_hw_refine_cchange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params,
Packit Service db8eaa
					    snd_pcm_hw_params_t *sparams)
Packit Service db8eaa
{
Packit Service db8eaa
	int err;
Packit Service db8eaa
	unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
Packit Service db8eaa
			      SND_PCM_HW_PARBIT_RATE |
Packit Service db8eaa
			      SND_PCM_HW_PARBIT_PERIOD_SIZE |
Packit Service db8eaa
			      SND_PCM_HW_PARBIT_BUFFER_SIZE |
Packit Service db8eaa
			      SND_PCM_HW_PARBIT_PERIODS |
Packit Service db8eaa
			      SND_PCM_HW_PARBIT_PERIOD_TIME |
Packit Service db8eaa
			      SND_PCM_HW_PARBIT_BUFFER_TIME |
Packit Service db8eaa
			      SND_PCM_HW_PARBIT_TICK_TIME);
Packit Service db8eaa
	err = _snd_pcm_hw_params_refine(params, links, sparams);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_iec958_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
Packit Service db8eaa
{
Packit Service db8eaa
	return snd_pcm_hw_refine_slave(pcm, params,
Packit Service db8eaa
				       snd_pcm_iec958_hw_refine_cprepare,
Packit Service db8eaa
				       snd_pcm_iec958_hw_refine_cchange,
Packit Service db8eaa
				       snd_pcm_iec958_hw_refine_sprepare,
Packit Service db8eaa
				       snd_pcm_iec958_hw_refine_schange,
Packit Service db8eaa
				       snd_pcm_generic_hw_refine);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_iec958_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_iec958_t *iec = pcm->private_data;
Packit Service db8eaa
	snd_pcm_format_t format;
Packit Service db8eaa
	int err = snd_pcm_hw_params_slave(pcm, params,
Packit Service db8eaa
					  snd_pcm_iec958_hw_refine_cchange,
Packit Service db8eaa
					  snd_pcm_iec958_hw_refine_sprepare,
Packit Service db8eaa
					  snd_pcm_iec958_hw_refine_schange,
Packit Service db8eaa
					  snd_pcm_generic_hw_params);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
Packit Service db8eaa
	err = INTERNAL(snd_pcm_hw_params_get_format)(params, &format);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
Packit Service db8eaa
	iec->format = format;
Packit Service db8eaa
	if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
Packit Service db8eaa
		if (iec->sformat == SND_PCM_FORMAT_IEC958_SUBFRAME_LE ||
Packit Service db8eaa
		    iec->sformat == SND_PCM_FORMAT_IEC958_SUBFRAME_BE) {
Packit Service db8eaa
			iec->getput_idx = snd_pcm_linear_get_index(format, SND_PCM_FORMAT_S32);
Packit Service db8eaa
			iec->func = snd_pcm_iec958_encode;
Packit Service db8eaa
			iec->byteswap = iec->sformat != SND_PCM_FORMAT_IEC958_SUBFRAME;
Packit Service db8eaa
		} else {
Packit Service db8eaa
			iec->getput_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S32, iec->sformat);
Packit Service db8eaa
			iec->func = snd_pcm_iec958_decode;
Packit Service db8eaa
			iec->byteswap = format != SND_PCM_FORMAT_IEC958_SUBFRAME;
Packit Service db8eaa
		}
Packit Service db8eaa
	} else {
Packit Service db8eaa
		if (iec->sformat == SND_PCM_FORMAT_IEC958_SUBFRAME_LE ||
Packit Service db8eaa
		    iec->sformat == SND_PCM_FORMAT_IEC958_SUBFRAME_BE) {
Packit Service db8eaa
			iec->getput_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S32, format);
Packit Service db8eaa
			iec->func = snd_pcm_iec958_decode;
Packit Service db8eaa
			iec->byteswap = iec->sformat != SND_PCM_FORMAT_IEC958_SUBFRAME;
Packit Service db8eaa
		} else {
Packit Service db8eaa
			iec->getput_idx = snd_pcm_linear_get_index(iec->sformat, SND_PCM_FORMAT_S32);
Packit Service db8eaa
			iec->func = snd_pcm_iec958_encode;
Packit Service db8eaa
			iec->byteswap = format != SND_PCM_FORMAT_IEC958_SUBFRAME;
Packit Service db8eaa
		}
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	if ((iec->status[0] & IEC958_AES0_PROFESSIONAL) == 0) {
Packit Service db8eaa
		if ((iec->status[3] & IEC958_AES3_CON_FS) == IEC958_AES3_CON_FS_NOTID) {
Packit Service db8eaa
			unsigned int rate = 0;
Packit Service db8eaa
			unsigned char fs;
Packit Service db8eaa
Packit Service db8eaa
			err = INTERNAL(snd_pcm_hw_params_get_rate)(params, &rate, 0);
Packit Service db8eaa
			if (err < 0)
Packit Service db8eaa
				rate = 0;
Packit Service db8eaa
Packit Service db8eaa
			switch (rate) {
Packit Service db8eaa
			case 22050:
Packit Service db8eaa
				fs = IEC958_AES3_CON_FS_22050;
Packit Service db8eaa
				break;
Packit Service db8eaa
			case 24000:
Packit Service db8eaa
				fs = IEC958_AES3_CON_FS_24000;
Packit Service db8eaa
				break;
Packit Service db8eaa
			case 32000:
Packit Service db8eaa
				fs = IEC958_AES3_CON_FS_32000;
Packit Service db8eaa
				break;
Packit Service db8eaa
			case 44100:
Packit Service db8eaa
				fs = IEC958_AES3_CON_FS_44100;
Packit Service db8eaa
				break;
Packit Service db8eaa
			case 48000:
Packit Service db8eaa
				fs = IEC958_AES3_CON_FS_48000;
Packit Service db8eaa
				break;
Packit Service db8eaa
			case 88200:
Packit Service db8eaa
				fs = IEC958_AES3_CON_FS_88200;
Packit Service db8eaa
				break;
Packit Service db8eaa
			case 96000:
Packit Service db8eaa
				fs = IEC958_AES3_CON_FS_96000;
Packit Service db8eaa
				break;
Packit Service db8eaa
			case 176400:
Packit Service db8eaa
				fs = IEC958_AES3_CON_FS_176400;
Packit Service db8eaa
				break;
Packit Service db8eaa
			case 192000:
Packit Service db8eaa
				fs = IEC958_AES3_CON_FS_192000;
Packit Service db8eaa
				break;
Packit Service db8eaa
			case 768000:
Packit Service db8eaa
				fs = IEC958_AES3_CON_FS_768000;
Packit Service db8eaa
				break;
Packit Service db8eaa
			default:
Packit Service db8eaa
				fs = IEC958_AES3_CON_FS_NOTID;
Packit Service db8eaa
				break;
Packit Service db8eaa
			}
Packit Service db8eaa
Packit Service db8eaa
			iec->status[3] &= ~IEC958_AES3_CON_FS;
Packit Service db8eaa
			iec->status[3] |= fs;
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		if ((iec->status[4] & IEC958_AES4_CON_WORDLEN) == IEC958_AES4_CON_WORDLEN_NOTID) {
Packit Service db8eaa
			unsigned char ws;
Packit Service db8eaa
			switch (snd_pcm_format_width(format)) {
Packit Service db8eaa
			case 16:
Packit Service db8eaa
				ws = IEC958_AES4_CON_WORDLEN_20_16;
Packit Service db8eaa
				break;
Packit Service db8eaa
			case 18:
Packit Service db8eaa
				ws = IEC958_AES4_CON_WORDLEN_22_18;
Packit Service db8eaa
				break;
Packit Service db8eaa
			case 20:
Packit Service db8eaa
				ws = IEC958_AES4_CON_WORDLEN_20_16 | IEC958_AES4_CON_MAX_WORDLEN_24;
Packit Service db8eaa
				break;
Packit Service db8eaa
			case 24:
Packit Service db8eaa
			case 32: /* Assume 24-bit width for 32-bit samples. */
Packit Service db8eaa
				ws = IEC958_AES4_CON_WORDLEN_24_20 | IEC958_AES4_CON_MAX_WORDLEN_24;
Packit Service db8eaa
				break;
Packit Service db8eaa
			default:
Packit Service db8eaa
				ws = IEC958_AES4_CON_WORDLEN_NOTID;
Packit Service db8eaa
				break;
Packit Service db8eaa
			}
Packit Service db8eaa
			iec->status[4] &= ~(IEC958_AES4_CON_MAX_WORDLEN_24 | IEC958_AES4_CON_WORDLEN);
Packit Service db8eaa
			iec->status[4] |= ws;
Packit Service db8eaa
		}
Packit Service db8eaa
	}
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static snd_pcm_uframes_t
Packit Service db8eaa
snd_pcm_iec958_write_areas(snd_pcm_t *pcm,
Packit Service db8eaa
			   const snd_pcm_channel_area_t *areas,
Packit Service db8eaa
			   snd_pcm_uframes_t offset,
Packit Service db8eaa
			   snd_pcm_uframes_t size,
Packit Service db8eaa
			   const snd_pcm_channel_area_t *slave_areas,
Packit Service db8eaa
			   snd_pcm_uframes_t slave_offset,
Packit Service db8eaa
			   snd_pcm_uframes_t *slave_sizep)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_iec958_t *iec = pcm->private_data;
Packit Service db8eaa
	if (size > *slave_sizep)
Packit Service db8eaa
		size = *slave_sizep;
Packit Service db8eaa
	iec->func(iec, slave_areas, slave_offset,
Packit Service db8eaa
		  areas, offset, 
Packit Service db8eaa
		  pcm->channels, size);
Packit Service db8eaa
	*slave_sizep = size;
Packit Service db8eaa
	return size;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static snd_pcm_uframes_t
Packit Service db8eaa
snd_pcm_iec958_read_areas(snd_pcm_t *pcm,
Packit Service db8eaa
			  const snd_pcm_channel_area_t *areas,
Packit Service db8eaa
			  snd_pcm_uframes_t offset,
Packit Service db8eaa
			  snd_pcm_uframes_t size,
Packit Service db8eaa
			  const snd_pcm_channel_area_t *slave_areas,
Packit Service db8eaa
			  snd_pcm_uframes_t slave_offset,
Packit Service db8eaa
			  snd_pcm_uframes_t *slave_sizep)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_iec958_t *iec = pcm->private_data;
Packit Service db8eaa
	if (size > *slave_sizep)
Packit Service db8eaa
		size = *slave_sizep;
Packit Service db8eaa
	iec->func(iec, areas, offset, 
Packit Service db8eaa
		  slave_areas, slave_offset,
Packit Service db8eaa
		  pcm->channels, size);
Packit Service db8eaa
	*slave_sizep = size;
Packit Service db8eaa
	return size;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_iec958_init(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_iec958_t *iec = pcm->private_data;
Packit Service db8eaa
	iec->counter = 0;
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static void snd_pcm_iec958_dump(snd_pcm_t *pcm, snd_output_t *out)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_iec958_t *iec = pcm->private_data;
Packit Service db8eaa
	snd_output_printf(out, "IEC958 subframe conversion PCM (%s)\n", 
Packit Service db8eaa
			  snd_pcm_format_name(iec->sformat));
Packit Service db8eaa
	if (pcm->setup) {
Packit Service db8eaa
		snd_output_printf(out, "Its setup is:\n");
Packit Service db8eaa
		snd_pcm_dump_setup(pcm, out);
Packit Service db8eaa
	}
Packit Service db8eaa
	snd_output_printf(out, "Slave: ");
Packit Service db8eaa
	snd_pcm_dump(iec->plug.gen.slave, out);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static snd_pcm_sframes_t snd_pcm_iec958_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
Packit Service db8eaa
{
Packit Service db8eaa
	unsigned int counter_decrement;
Packit Service db8eaa
	snd_pcm_iec958_t *iec = pcm->private_data;
Packit Service db8eaa
	snd_pcm_sframes_t result = snd_pcm_plugin_rewind(pcm, frames);
Packit Service db8eaa
	if (result <= 0)
Packit Service db8eaa
		return result;
Packit Service db8eaa
Packit Service db8eaa
	counter_decrement = result % 192;
Packit Service db8eaa
	iec->counter += 192 - counter_decrement;
Packit Service db8eaa
	iec->counter %= 192;
Packit Service db8eaa
	return result;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static snd_pcm_sframes_t snd_pcm_iec958_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
Packit Service db8eaa
Packit Service db8eaa
{
Packit Service db8eaa
	unsigned int counter_increment;
Packit Service db8eaa
	snd_pcm_iec958_t *iec = pcm->private_data;
Packit Service db8eaa
	snd_pcm_sframes_t result = snd_pcm_plugin_rewind(pcm, frames);
Packit Service db8eaa
	if (result <= 0)
Packit Service db8eaa
		return result;
Packit Service db8eaa
Packit Service db8eaa
	counter_increment = result % 192;
Packit Service db8eaa
	iec->counter += counter_increment;
Packit Service db8eaa
	iec->counter %= 192;
Packit Service db8eaa
	return result;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static const snd_pcm_ops_t snd_pcm_iec958_ops = {
Packit Service db8eaa
	.close = snd_pcm_generic_close,
Packit Service db8eaa
	.info = snd_pcm_generic_info,
Packit Service db8eaa
	.hw_refine = snd_pcm_iec958_hw_refine,
Packit Service db8eaa
	.hw_params = snd_pcm_iec958_hw_params,
Packit Service db8eaa
	.hw_free = snd_pcm_generic_hw_free,
Packit Service db8eaa
	.sw_params = snd_pcm_generic_sw_params,
Packit Service db8eaa
	.channel_info = snd_pcm_generic_channel_info,
Packit Service db8eaa
	.dump = snd_pcm_iec958_dump,
Packit Service db8eaa
	.nonblock = snd_pcm_generic_nonblock,
Packit Service db8eaa
	.async = snd_pcm_generic_async,
Packit Service db8eaa
	.mmap = snd_pcm_generic_mmap,
Packit Service db8eaa
	.munmap = snd_pcm_generic_munmap,
Packit Service db8eaa
	.query_chmaps = snd_pcm_generic_query_chmaps,
Packit Service db8eaa
	.get_chmap = snd_pcm_generic_get_chmap,
Packit Service db8eaa
	.set_chmap = snd_pcm_generic_set_chmap,
Packit Service db8eaa
};
Packit Service db8eaa
Packit Service db8eaa
/**
Packit Service db8eaa
 * \brief Creates a new IEC958 subframe conversion PCM
Packit Service db8eaa
 * \param pcmp Returns created PCM handle
Packit Service db8eaa
 * \param name Name of PCM
Packit Service db8eaa
 * \param sformat Slave (destination) format
Packit Service db8eaa
 * \param slave Slave PCM handle
Packit Service db8eaa
 * \param close_slave When set, the slave PCM handle is closed with copy PCM
Packit Service db8eaa
 * \param status_bits The IEC958 status bits
Packit Service db8eaa
 * \param preamble_vals The preamble byte values
Packit Service db8eaa
 * \param hdmi_mode When set, enable HDMI compliant formatting
Packit Service db8eaa
 * \retval zero on success otherwise a negative error code
Packit Service db8eaa
 * \warning Using of this function might be dangerous in the sense
Packit Service db8eaa
 *          of compatibility reasons. The prototype might be freely
Packit Service db8eaa
 *          changed in future.
Packit Service db8eaa
 */           
Packit Service db8eaa
int snd_pcm_iec958_open(snd_pcm_t **pcmp, const char *name, snd_pcm_format_t sformat,
Packit Service db8eaa
			snd_pcm_t *slave, int close_slave,
Packit Service db8eaa
			const unsigned char *status_bits,
Packit Service db8eaa
			const unsigned char *preamble_vals,
Packit Service db8eaa
		        int hdmi_mode)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_t *pcm;
Packit Service db8eaa
	snd_pcm_iec958_t *iec;
Packit Service db8eaa
	int err;
Packit Service db8eaa
	static const unsigned char default_status_bits[] = {
Packit Service db8eaa
		IEC958_AES0_CON_EMPHASIS_NONE,
Packit Service db8eaa
		IEC958_AES1_CON_ORIGINAL | IEC958_AES1_CON_PCM_CODER,
Packit Service db8eaa
		0,
Packit Service db8eaa
		IEC958_AES3_CON_FS_NOTID, /* will be set in hwparams */
Packit Service db8eaa
		IEC958_AES4_CON_WORDLEN_NOTID /* will be set in hwparams */
Packit Service db8eaa
	};
Packit Service db8eaa
Packit Service db8eaa
	assert(pcmp && slave);
Packit Service db8eaa
	if (snd_pcm_format_linear(sformat) != 1 &&
Packit Service db8eaa
	    sformat != SND_PCM_FORMAT_IEC958_SUBFRAME_LE &&
Packit Service db8eaa
	    sformat != SND_PCM_FORMAT_IEC958_SUBFRAME_BE)
Packit Service db8eaa
		return -EINVAL;
Packit Service db8eaa
	iec = calloc(1, sizeof(snd_pcm_iec958_t));
Packit Service db8eaa
	if (!iec) {
Packit Service db8eaa
		return -ENOMEM;
Packit Service db8eaa
	}
Packit Service db8eaa
	snd_pcm_plugin_init(&iec->plug);
Packit Service db8eaa
	iec->sformat = sformat;
Packit Service db8eaa
	iec->plug.read = snd_pcm_iec958_read_areas;
Packit Service db8eaa
	iec->plug.write = snd_pcm_iec958_write_areas;
Packit Service db8eaa
	iec->plug.init = snd_pcm_iec958_init;
Packit Service db8eaa
	iec->plug.undo_read = snd_pcm_plugin_undo_read_generic;
Packit Service db8eaa
	iec->plug.undo_write = snd_pcm_plugin_undo_write_generic;
Packit Service db8eaa
	iec->plug.gen.slave = slave;
Packit Service db8eaa
	iec->plug.gen.close_slave = close_slave;
Packit Service db8eaa
Packit Service db8eaa
	if (status_bits)
Packit Service db8eaa
		memcpy(iec->status, status_bits, sizeof(iec->status));
Packit Service db8eaa
	else
Packit Service db8eaa
		memcpy(iec->status, default_status_bits, sizeof(default_status_bits));
Packit Service db8eaa
Packit Service db8eaa
	memcpy(iec->preamble, preamble_vals, 3);
Packit Service db8eaa
Packit Service db8eaa
	iec->hdmi_mode = hdmi_mode;
Packit Service db8eaa
Packit Service db8eaa
	err = snd_pcm_new(&pcm, SND_PCM_TYPE_IEC958, name, slave->stream, slave->mode);
Packit Service db8eaa
	if (err < 0) {
Packit Service db8eaa
		free(iec);
Packit Service db8eaa
		return err;
Packit Service db8eaa
	}
Packit Service db8eaa
	pcm->ops = &snd_pcm_iec958_ops;
Packit Service db8eaa
Packit Service db8eaa
	iec->fops = snd_pcm_plugin_fast_ops;
Packit Service db8eaa
	iec->fops.rewind = snd_pcm_iec958_rewind;
Packit Service db8eaa
	iec->fops.forward = snd_pcm_iec958_forward;
Packit Service db8eaa
	pcm->fast_ops = &iec->fops;
Packit Service db8eaa
Packit Service db8eaa
	pcm->private_data = iec;
Packit Service db8eaa
	pcm->poll_fd = slave->poll_fd;
Packit Service db8eaa
	pcm->poll_events = slave->poll_events;
Packit Service db8eaa
	pcm->tstamp_type = slave->tstamp_type;
Packit Service db8eaa
	snd_pcm_set_hw_ptr(pcm, &iec->plug.hw_ptr, -1, 0);
Packit Service db8eaa
	snd_pcm_set_appl_ptr(pcm, &iec->plug.appl_ptr, -1, 0);
Packit Service db8eaa
	*pcmp = pcm;
Packit Service db8eaa
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/*! \page pcm_plugins
Packit Service db8eaa
Packit Service db8eaa
\section pcm_plugins_iec958 Plugin: IEC958
Packit Service db8eaa
Packit Service db8eaa
This plugin converts 32bit IEC958 subframe samples to linear, or linear to
Packit Service db8eaa
32bit IEC958 subframe samples.
Packit Service db8eaa
Packit Service db8eaa
\code
Packit Service db8eaa
pcm.name {
Packit Service db8eaa
        type iec958             # IEC958 subframe conversion PCM
Packit Service db8eaa
        slave STR               # Slave name
Packit Service db8eaa
        # or
Packit Service db8eaa
        slave {                 # Slave definition
Packit Service db8eaa
                pcm STR         # Slave PCM name
Packit Service db8eaa
                # or
Packit Service db8eaa
                pcm { }         # Slave PCM definition
Packit Service db8eaa
        }
Packit Service db8eaa
	[status status-bytes]	# IEC958 status bits (given in byte array)
Packit Service db8eaa
	# IEC958 preamble bits definitions
Packit Service db8eaa
	# B/M/W or Z/X/Y, B = block start, M = even subframe, W = odd subframe
Packit Service db8eaa
	# As default, Z = 0x08, Y = 0x04, X = 0x02
Packit Service db8eaa
	[preamble.z or preamble.b val]
Packit Service db8eaa
	[preamble.x or preamble.m val]
Packit Service db8eaa
	[preamble.y or preamble.w val]
Packit Service db8eaa
	[hdmi_mode true]
Packit Service db8eaa
}
Packit Service db8eaa
\endcode
Packit Service db8eaa
Packit Service db8eaa
When hdmi_mode is true, 8-channel compressed data is
Packit Service db8eaa
formatted as 4 contiguous frames of a single IEC958 stream as required
Packit Service db8eaa
by the HDMI HBR specification.
Packit Service db8eaa
Packit Service db8eaa
\subsection pcm_plugins_iec958_funcref Function reference
Packit Service db8eaa
Packit Service db8eaa
    Packit Service db8eaa
      
  • snd_pcm_iec958_open()
  • Packit Service db8eaa
      
  • _snd_pcm_iec958_open()
  • Packit Service db8eaa
    Packit Service db8eaa
    Packit Service db8eaa
    */
    Packit Service db8eaa
    Packit Service db8eaa
    /**
    Packit Service db8eaa
     * \brief Creates a new IEC958 subframe conversion PCM
    Packit Service db8eaa
     * \param pcmp Returns created PCM handle
    Packit Service db8eaa
     * \param name Name of PCM
    Packit Service db8eaa
     * \param root Root configuration node
    Packit Service db8eaa
     * \param conf Configuration node with copy PCM description
    Packit Service db8eaa
     * \param stream Stream type
    Packit Service db8eaa
     * \param mode Stream mode
    Packit Service db8eaa
     * \retval zero on success otherwise a negative error code
    Packit Service db8eaa
     * \warning Using of this function might be dangerous in the sense
    Packit Service db8eaa
     *          of compatibility reasons. The prototype might be freely
    Packit Service db8eaa
     *          changed in future.
    Packit Service db8eaa
     */
    Packit Service db8eaa
    int _snd_pcm_iec958_open(snd_pcm_t **pcmp, const char *name,
    Packit Service db8eaa
    			 snd_config_t *root, snd_config_t *conf, 
    Packit Service db8eaa
    			 snd_pcm_stream_t stream, int mode)
    Packit Service db8eaa
    {
    Packit Service db8eaa
    	snd_config_iterator_t i, next;
    Packit Service db8eaa
    	int err;
    Packit Service db8eaa
    	snd_pcm_t *spcm;
    Packit Service db8eaa
    	snd_config_t *slave = NULL, *sconf;
    Packit Service db8eaa
    	snd_config_t *status = NULL, *preamble = NULL;
    Packit Service db8eaa
    	snd_pcm_format_t sformat;
    Packit Service db8eaa
    	unsigned char status_bits[24];
    Packit Service db8eaa
    	unsigned char preamble_vals[3] = {
    Packit Service db8eaa
    		0x08, 0x02, 0x04 /* Z, X, Y */
    Packit Service db8eaa
    	};
    Packit Service db8eaa
    	int hdmi_mode = 0;
    Packit Service db8eaa
    Packit Service db8eaa
    	snd_config_for_each(i, next, conf) {
    Packit Service db8eaa
    		snd_config_t *n = snd_config_iterator_entry(i);
    Packit Service db8eaa
    		const char *id;
    Packit Service db8eaa
    		if (snd_config_get_id(n, &id) < 0)
    Packit Service db8eaa
    			continue;
    Packit Service db8eaa
    		if (snd_pcm_conf_generic_id(id))
    Packit Service db8eaa
    			continue;
    Packit Service db8eaa
    		if (strcmp(id, "slave") == 0) {
    Packit Service db8eaa
    			slave = n;
    Packit Service db8eaa
    			continue;
    Packit Service db8eaa
    		}
    Packit Service db8eaa
    		if (strcmp(id, "status") == 0) {
    Packit Service db8eaa
    			if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
    Packit Service db8eaa
    				SNDERR("Invalid type for %s", id);
    Packit Service db8eaa
    				return -EINVAL;
    Packit Service db8eaa
    			}
    Packit Service db8eaa
    			status = n;
    Packit Service db8eaa
    			continue;
    Packit Service db8eaa
    		}
    Packit Service db8eaa
    		if (strcmp(id, "preamble") == 0) {
    Packit Service db8eaa
    			if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
    Packit Service db8eaa
    				SNDERR("Invalid type for %s", id);
    Packit Service db8eaa
    				return -EINVAL;
    Packit Service db8eaa
    			}
    Packit Service db8eaa
    			preamble = n;
    Packit Service db8eaa
    			continue;
    Packit Service db8eaa
    		}
    Packit Service db8eaa
    		if (strcmp(id, "hdmi_mode") == 0) {
    Packit Service db8eaa
    			err = snd_config_get_bool(n);
    Packit Service db8eaa
    			if (err < 0)
    Packit Service db8eaa
    				continue;
    Packit Service db8eaa
    			hdmi_mode = err;
    Packit Service db8eaa
    			continue;
    Packit Service db8eaa
    		}
    Packit Service db8eaa
    		SNDERR("Unknown field %s", id);
    Packit Service db8eaa
    		return -EINVAL;
    Packit Service db8eaa
    	}
    Packit Service db8eaa
    	memset(status_bits, 0, sizeof(status_bits));
    Packit Service db8eaa
    	if (status) {
    Packit Service db8eaa
    		snd_config_iterator_t i, inext;
    Packit Service db8eaa
    		int bytes = 0;
    Packit Service db8eaa
    		snd_config_for_each(i, inext, status) {
    Packit Service db8eaa
    			long val;
    Packit Service db8eaa
    			snd_config_t *n = snd_config_iterator_entry(i);
    Packit Service db8eaa
    			if (snd_config_get_type(n) != SND_CONFIG_TYPE_INTEGER) {
    Packit Service db8eaa
    				SNDERR("invalid IEC958 status bits");
    Packit Service db8eaa
    				return -EINVAL;
    Packit Service db8eaa
    			}
    Packit Service db8eaa
    			err = snd_config_get_integer(n, &val;;
    Packit Service db8eaa
    			if (err < 0) {
    Packit Service db8eaa
    				SNDERR("invalid IEC958 status bits");
    Packit Service db8eaa
    				return err;
    Packit Service db8eaa
    			}
    Packit Service db8eaa
    			status_bits[bytes] = val;
    Packit Service db8eaa
    			bytes++;
    Packit Service db8eaa
    			if (bytes >= (int)sizeof(status_bits))
    Packit Service db8eaa
    				break;
    Packit Service db8eaa
    		}
    Packit Service db8eaa
    		// fprintf(stderr, "STATUS bits: %02x %02x %02x %02x\n", status_bits[0], status_bits[1], status_bits[2], status_bits[3]);
    Packit Service db8eaa
    	}
    Packit Service db8eaa
    	if (preamble) {
    Packit Service db8eaa
    		snd_config_iterator_t i, inext;
    Packit Service db8eaa
    		snd_config_for_each(i, inext, preamble) {
    Packit Service db8eaa
    			long val;
    Packit Service db8eaa
    			snd_config_t *n = snd_config_iterator_entry(i);
    Packit Service db8eaa
    			const char *id;
    Packit Service db8eaa
    			int idx;
    Packit Service db8eaa
    			if (snd_config_get_id(n, &id) < 0)
    Packit Service db8eaa
    				continue;
    Packit Service db8eaa
    			if (strcmp(id, "b") == 0 || strcmp(id, "z") == 0)
    Packit Service db8eaa
    				idx = PREAMBLE_Z;
    Packit Service db8eaa
    			else if (strcmp(id, "m") == 0 || strcmp(id, "x") == 0)
    Packit Service db8eaa
    				idx = PREAMBLE_X;
    Packit Service db8eaa
    			else if (strcmp(id, "w") == 0 || strcmp(id, "y") == 0)
    Packit Service db8eaa
    				idx = PREAMBLE_Y;
    Packit Service db8eaa
    			else {
    Packit Service db8eaa
    				SNDERR("invalid IEC958 preamble type %s", id);
    Packit Service db8eaa
    				return -EINVAL;
    Packit Service db8eaa
    			}
    Packit Service db8eaa
    			err = snd_config_get_integer(n, &val;;
    Packit Service db8eaa
    			if (err < 0) {
    Packit Service db8eaa
    				SNDERR("invalid IEC958 preamble value");
    Packit Service db8eaa
    				return err;
    Packit Service db8eaa
    			}
    Packit Service db8eaa
    			preamble_vals[idx] = val;
    Packit Service db8eaa
    		}
    Packit Service db8eaa
    	}
    Packit Service db8eaa
    	if (!slave) {
    Packit Service db8eaa
    		SNDERR("slave is not defined");
    Packit Service db8eaa
    		return -EINVAL;
    Packit Service db8eaa
    	}
    Packit Service db8eaa
    	err = snd_pcm_slave_conf(root, slave, &sconf, 1,
    Packit Service db8eaa
    				 SND_PCM_HW_PARAM_FORMAT, SCONF_MANDATORY, &sformat);
    Packit Service db8eaa
    	if (err < 0)
    Packit Service db8eaa
    		return err;
    Packit Service db8eaa
    	if (snd_pcm_format_linear(sformat) != 1 &&
    Packit Service db8eaa
    	    sformat != SND_PCM_FORMAT_IEC958_SUBFRAME_LE &&
    Packit Service db8eaa
    	    sformat != SND_PCM_FORMAT_IEC958_SUBFRAME_BE) {
    Packit Service db8eaa
    	    	snd_config_delete(sconf);
    Packit Service db8eaa
    		SNDERR("invalid slave format");
    Packit Service db8eaa
    		return -EINVAL;
    Packit Service db8eaa
    	}
    Packit Service db8eaa
    	err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
    Packit Service db8eaa
    	snd_config_delete(sconf);
    Packit Service db8eaa
    	if (err < 0)
    Packit Service db8eaa
    		return err;
    Packit Service db8eaa
    	err = snd_pcm_iec958_open(pcmp, name, sformat, spcm, 1,
    Packit Service db8eaa
    				  status ? status_bits : NULL,
    Packit Service db8eaa
    				  preamble_vals, hdmi_mode);
    Packit Service db8eaa
    	if (err < 0)
    Packit Service db8eaa
    		snd_pcm_close(spcm);
    Packit Service db8eaa
    	return err;
    Packit Service db8eaa
    }
    Packit Service db8eaa
    #ifndef DOC_HIDDEN
    Packit Service db8eaa
    SND_DLSYM_BUILD_VERSION(_snd_pcm_iec958_open, SND_PCM_DLSYM_VERSION);
    Packit Service db8eaa
    #endif