Blame src/pcm/pcm_softvol.c

Packit Service db8eaa
/**
Packit Service db8eaa
 * \file pcm/pcm_softvol.c
Packit Service db8eaa
 * \ingroup PCM_Plugins
Packit Service db8eaa
 * \brief PCM Soft Volume 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 - Soft Volume 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 <math.h>
Packit Service db8eaa
#include "pcm_local.h"
Packit Service db8eaa
#include "pcm_plugin.h"
Packit Service db8eaa
Packit Service db8eaa
#include <sound/tlv.h>
Packit Service db8eaa
Packit Service db8eaa
#ifndef PIC
Packit Service db8eaa
/* entry for static linking */
Packit Service db8eaa
const char *_snd_module_pcm_softvol = "";
Packit Service db8eaa
#endif
Packit Service db8eaa
Packit Service db8eaa
#ifndef DOC_HIDDEN
Packit Service db8eaa
Packit Service db8eaa
typedef struct {
Packit Service db8eaa
	/* This field need to be the first */
Packit Service db8eaa
	snd_pcm_plugin_t plug;
Packit Service db8eaa
	snd_pcm_format_t sformat;
Packit Service db8eaa
	unsigned int cchannels;
Packit Service db8eaa
	snd_ctl_t *ctl;
Packit Service db8eaa
	snd_ctl_elem_value_t elem;
Packit Service db8eaa
	unsigned int cur_vol[2];
Packit Service db8eaa
	unsigned int max_val;     /* max index */
Packit Service db8eaa
	unsigned int zero_dB_val; /* index at 0 dB */
Packit Service db8eaa
	double min_dB;
Packit Service db8eaa
	double max_dB;
Packit Service db8eaa
	unsigned int *dB_value;
Packit Service db8eaa
} snd_pcm_softvol_t;
Packit Service db8eaa
Packit Service db8eaa
#define VOL_SCALE_SHIFT		16
Packit Service db8eaa
#define VOL_SCALE_MASK          ((1 << VOL_SCALE_SHIFT) - 1)
Packit Service db8eaa
Packit Service db8eaa
#define PRESET_RESOLUTION	256
Packit Service db8eaa
#define PRESET_MIN_DB		-51.0
Packit Service db8eaa
#define ZERO_DB                  0.0
Packit Service db8eaa
/*
Packit Service db8eaa
 * The gain algorithm as it stands supports gain factors up to 32767, which
Packit Service db8eaa
 * is a fraction more than 90 dB, so set 90 dB as the maximum possible gain.
Packit Service db8eaa
 */
Packit Service db8eaa
#define MAX_DB_UPPER_LIMIT      90
Packit Service db8eaa
Packit Service db8eaa
static const unsigned int preset_dB_value[PRESET_RESOLUTION] = {
Packit Service db8eaa
	0x00b8, 0x00bd, 0x00c1, 0x00c5, 0x00ca, 0x00cf, 0x00d4, 0x00d9,
Packit Service db8eaa
	0x00de, 0x00e3, 0x00e8, 0x00ed, 0x00f3, 0x00f9, 0x00fe, 0x0104,
Packit Service db8eaa
	0x010a, 0x0111, 0x0117, 0x011e, 0x0124, 0x012b, 0x0132, 0x0139,
Packit Service db8eaa
	0x0140, 0x0148, 0x0150, 0x0157, 0x015f, 0x0168, 0x0170, 0x0179,
Packit Service db8eaa
	0x0181, 0x018a, 0x0194, 0x019d, 0x01a7, 0x01b0, 0x01bb, 0x01c5,
Packit Service db8eaa
	0x01cf, 0x01da, 0x01e5, 0x01f1, 0x01fc, 0x0208, 0x0214, 0x0221,
Packit Service db8eaa
	0x022d, 0x023a, 0x0248, 0x0255, 0x0263, 0x0271, 0x0280, 0x028f,
Packit Service db8eaa
	0x029e, 0x02ae, 0x02be, 0x02ce, 0x02df, 0x02f0, 0x0301, 0x0313,
Packit Service db8eaa
	0x0326, 0x0339, 0x034c, 0x035f, 0x0374, 0x0388, 0x039d, 0x03b3,
Packit Service db8eaa
	0x03c9, 0x03df, 0x03f7, 0x040e, 0x0426, 0x043f, 0x0458, 0x0472,
Packit Service db8eaa
	0x048d, 0x04a8, 0x04c4, 0x04e0, 0x04fd, 0x051b, 0x053a, 0x0559,
Packit Service db8eaa
	0x0579, 0x0599, 0x05bb, 0x05dd, 0x0600, 0x0624, 0x0648, 0x066e,
Packit Service db8eaa
	0x0694, 0x06bb, 0x06e3, 0x070c, 0x0737, 0x0762, 0x078e, 0x07bb,
Packit Service db8eaa
	0x07e9, 0x0818, 0x0848, 0x087a, 0x08ac, 0x08e0, 0x0915, 0x094b,
Packit Service db8eaa
	0x0982, 0x09bb, 0x09f5, 0x0a30, 0x0a6d, 0x0aab, 0x0aeb, 0x0b2c,
Packit Service db8eaa
	0x0b6f, 0x0bb3, 0x0bf9, 0x0c40, 0x0c89, 0x0cd4, 0x0d21, 0x0d6f,
Packit Service db8eaa
	0x0dbf, 0x0e11, 0x0e65, 0x0ebb, 0x0f12, 0x0f6c, 0x0fc8, 0x1026,
Packit Service db8eaa
	0x1087, 0x10e9, 0x114e, 0x11b5, 0x121f, 0x128b, 0x12fa, 0x136b,
Packit Service db8eaa
	0x13df, 0x1455, 0x14ce, 0x154a, 0x15c9, 0x164b, 0x16d0, 0x1758,
Packit Service db8eaa
	0x17e4, 0x1872, 0x1904, 0x1999, 0x1a32, 0x1ace, 0x1b6e, 0x1c11,
Packit Service db8eaa
	0x1cb9, 0x1d64, 0x1e13, 0x1ec7, 0x1f7e, 0x203a, 0x20fa, 0x21bf,
Packit Service db8eaa
	0x2288, 0x2356, 0x2429, 0x2500, 0x25dd, 0x26bf, 0x27a6, 0x2892,
Packit Service db8eaa
	0x2984, 0x2a7c, 0x2b79, 0x2c7c, 0x2d85, 0x2e95, 0x2fab, 0x30c7,
Packit Service db8eaa
	0x31ea, 0x3313, 0x3444, 0x357c, 0x36bb, 0x3801, 0x394f, 0x3aa5,
Packit Service db8eaa
	0x3c02, 0x3d68, 0x3ed6, 0x404d, 0x41cd, 0x4355, 0x44e6, 0x4681,
Packit Service db8eaa
	0x4826, 0x49d4, 0x4b8c, 0x4d4f, 0x4f1c, 0x50f3, 0x52d6, 0x54c4,
Packit Service db8eaa
	0x56be, 0x58c3, 0x5ad4, 0x5cf2, 0x5f1c, 0x6153, 0x6398, 0x65e9,
Packit Service db8eaa
	0x6849, 0x6ab7, 0x6d33, 0x6fbf, 0x7259, 0x7503, 0x77bd, 0x7a87,
Packit Service db8eaa
	0x7d61, 0x804d, 0x834a, 0x8659, 0x897a, 0x8cae, 0x8ff5, 0x934f,
Packit Service db8eaa
	0x96bd, 0x9a40, 0x9dd8, 0xa185, 0xa548, 0xa922, 0xad13, 0xb11b,
Packit Service db8eaa
	0xb53b, 0xb973, 0xbdc5, 0xc231, 0xc6b7, 0xcb58, 0xd014, 0xd4ed,
Packit Service db8eaa
	0xd9e3, 0xdef6, 0xe428, 0xe978, 0xeee8, 0xf479, 0xfa2b, 0xffff,
Packit Service db8eaa
};
Packit Service db8eaa
Packit Service db8eaa
/* (32bit x 16bit) >> 16 */
Packit Service db8eaa
typedef union {
Packit Service db8eaa
	int i;
Packit Service db8eaa
	short s[2];
Packit Service db8eaa
} val_t;
Packit Service db8eaa
static inline int MULTI_DIV_32x16(int a, unsigned short b)
Packit Service db8eaa
{
Packit Service db8eaa
	val_t v, x, y;
Packit Service db8eaa
	v.i = a;
Packit Service db8eaa
	y.i = 0;
Packit Service db8eaa
#if __BYTE_ORDER == __LITTLE_ENDIAN
Packit Service db8eaa
	x.i = (unsigned short)v.s[0];
Packit Service db8eaa
	x.i *= b;
Packit Service db8eaa
	y.s[0] = x.s[1];
Packit Service db8eaa
	y.i += (int)v.s[1] * b;
Packit Service db8eaa
#else
Packit Service db8eaa
	x.i = (unsigned int)v.s[1] * b;
Packit Service db8eaa
	y.s[1] = x.s[0];
Packit Service db8eaa
	y.i += (int)v.s[0] * b;
Packit Service db8eaa
#endif
Packit Service db8eaa
	return y.i;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static inline int MULTI_DIV_int(int a, unsigned int b, int swap)
Packit Service db8eaa
{
Packit Service db8eaa
	unsigned int gain = (b >> VOL_SCALE_SHIFT);
Packit Service db8eaa
	int fraction;
Packit Service db8eaa
	a = swap ? (int)bswap_32(a) : a;
Packit Service db8eaa
	fraction = MULTI_DIV_32x16(a, b & VOL_SCALE_MASK);
Packit Service db8eaa
	if (gain) {
Packit Service db8eaa
		long long amp = (long long)a * gain + fraction;
Packit Service db8eaa
		if (amp > (int)0x7fffffff)
Packit Service db8eaa
			amp = (int)0x7fffffff;
Packit Service db8eaa
		else if (amp < (int)0x80000000)
Packit Service db8eaa
			amp = (int)0x80000000;
Packit Service db8eaa
		return swap ? (int)bswap_32((int)amp) : (int)amp;
Packit Service db8eaa
	}
Packit Service db8eaa
	return swap ? (int)bswap_32(fraction) : fraction;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/* always little endian */
Packit Service db8eaa
static inline int MULTI_DIV_24(int a, unsigned int b)
Packit Service db8eaa
{
Packit Service db8eaa
	unsigned int gain = b >> VOL_SCALE_SHIFT;
Packit Service db8eaa
	int fraction;
Packit Service db8eaa
	fraction = MULTI_DIV_32x16(a, b & VOL_SCALE_MASK);
Packit Service db8eaa
	if (gain) {
Packit Service db8eaa
		long long amp = (long long)a * gain + fraction;
Packit Service db8eaa
		if (amp > (int)0x7fffff)
Packit Service db8eaa
			amp = (int)0x7fffff;
Packit Service db8eaa
		else if (amp < (int)0x800000)
Packit Service db8eaa
			amp = (int)0x800000;
Packit Service db8eaa
		return (int)amp;
Packit Service db8eaa
	}
Packit Service db8eaa
	return fraction;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static inline short MULTI_DIV_short(short a, unsigned int b, int swap)
Packit Service db8eaa
{
Packit Service db8eaa
	unsigned int gain = b >> VOL_SCALE_SHIFT;
Packit Service db8eaa
	int fraction;
Packit Service db8eaa
	a = swap ? (short)bswap_16(a) : a;
Packit Service db8eaa
	fraction = (int)(a * (b & VOL_SCALE_MASK)) >> VOL_SCALE_SHIFT;
Packit Service db8eaa
	if (gain) {
Packit Service db8eaa
		int amp = a * gain + fraction;
Packit Service db8eaa
		if (abs(amp) > 0x7fff)
Packit Service db8eaa
			amp = (a<0) ? (short)0x8000 : (short)0x7fff;
Packit Service db8eaa
		return swap ? (short)bswap_16((short)amp) : (short)amp;
Packit Service db8eaa
	}
Packit Service db8eaa
	return swap ? (short)bswap_16((short)fraction) : (short)fraction;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
#endif /* DOC_HIDDEN */
Packit Service db8eaa
Packit Service db8eaa
/*
Packit Service db8eaa
 * apply volumue attenuation
Packit Service db8eaa
 *
Packit Service db8eaa
 * TODO: use SIMD operations
Packit Service db8eaa
 */
Packit Service db8eaa
Packit Service db8eaa
#ifndef DOC_HIDDEN
Packit Service db8eaa
#define CONVERT_AREA(TYPE, swap) do {	\
Packit Service db8eaa
	unsigned int ch, fr; \
Packit Service db8eaa
	TYPE *src, *dst; \
Packit Service db8eaa
	for (ch = 0; ch < channels; ch++) { \
Packit Service db8eaa
		src_area = &src_areas[ch]; \
Packit Service db8eaa
		dst_area = &dst_areas[ch]; \
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(TYPE); \
Packit Service db8eaa
		dst_step = snd_pcm_channel_area_step(dst_area) / sizeof(TYPE); \
Packit Service db8eaa
		GET_VOL_SCALE; \
Packit Service db8eaa
		fr = frames; \
Packit Service db8eaa
		if (! vol_scale) { \
Packit Service db8eaa
			while (fr--) { \
Packit Service db8eaa
				*dst = 0; \
Packit Service db8eaa
				dst += dst_step; \
Packit Service db8eaa
			} \
Packit Service db8eaa
		} else if (vol_scale == 0xffff) { \
Packit Service db8eaa
			while (fr--) { \
Packit Service db8eaa
				*dst = *src; \
Packit Service db8eaa
				src += src_step; \
Packit Service db8eaa
				dst += dst_step; \
Packit Service db8eaa
			} \
Packit Service db8eaa
		} else { \
Packit Service db8eaa
			while (fr--) { \
Packit Service db8eaa
				*dst = (TYPE) MULTI_DIV_##TYPE(*src, vol_scale, swap); \
Packit Service db8eaa
				src += src_step; \
Packit Service db8eaa
				dst += dst_step; \
Packit Service db8eaa
			} \
Packit Service db8eaa
		} \
Packit Service db8eaa
	} \
Packit Service db8eaa
} while (0)
Packit Service db8eaa
Packit Service db8eaa
#define CONVERT_AREA_S24_3LE() do {					\
Packit Service db8eaa
	unsigned int ch, fr;						\
Packit Service db8eaa
	unsigned char *src, *dst;					\
Packit Service db8eaa
	int tmp;							\
Packit Service db8eaa
	for (ch = 0; ch < channels; ch++) {				\
Packit Service db8eaa
		src_area = &src_areas[ch];				\
Packit Service db8eaa
		dst_area = &dst_areas[ch];				\
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);		\
Packit Service db8eaa
		GET_VOL_SCALE;						\
Packit Service db8eaa
		fr = frames;						\
Packit Service db8eaa
		if (! vol_scale) {					\
Packit Service db8eaa
			while (fr--) {					\
Packit Service db8eaa
				dst[0] = dst[1] = dst[2] = 0;		\
Packit Service db8eaa
				dst += dst_step;			\
Packit Service db8eaa
			}						\
Packit Service db8eaa
		} else if (vol_scale == 0xffff) {			\
Packit Service db8eaa
			while (fr--) {					\
Packit Service db8eaa
				dst[0] = src[0];			\
Packit Service db8eaa
				dst[1] = src[1];			\
Packit Service db8eaa
				dst[2] = src[2];			\
Packit Service db8eaa
				src += dst_step;			\
Packit Service db8eaa
				dst += src_step;			\
Packit Service db8eaa
			}						\
Packit Service db8eaa
		} else {						\
Packit Service db8eaa
			while (fr--) {					\
Packit Service db8eaa
				tmp = src[0] |				\
Packit Service db8eaa
				      (src[1] << 8) |			\
Packit Service db8eaa
				      (((signed char *) src)[2] << 16);	\
Packit Service db8eaa
				tmp = MULTI_DIV_24(tmp, vol_scale);	\
Packit Service db8eaa
				dst[0] = tmp;				\
Packit Service db8eaa
				dst[1] = tmp >> 8;			\
Packit Service db8eaa
				dst[2] = tmp >> 16;			\
Packit Service db8eaa
				src += dst_step;			\
Packit Service db8eaa
				dst += src_step;			\
Packit Service db8eaa
			}						\
Packit Service db8eaa
		}							\
Packit Service db8eaa
	}								\
Packit Service db8eaa
} while (0)
Packit Service db8eaa
Packit Service db8eaa
#define CONVERT_AREA_S24_LE() do {					\
Packit Service db8eaa
	unsigned int ch, fr;						\
Packit Service db8eaa
	int *src, *dst;							\
Packit Service db8eaa
	int tmp;							\
Packit Service db8eaa
	for (ch = 0; ch < channels; ch++) {				\
Packit Service db8eaa
		src_area = &src_areas[ch];				\
Packit Service db8eaa
		dst_area = &dst_areas[ch];				\
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
				/ sizeof(int);				\
Packit Service db8eaa
		dst_step = snd_pcm_channel_area_step(dst_area)		\
Packit Service db8eaa
				/ sizeof(int);				\
Packit Service db8eaa
		GET_VOL_SCALE;						\
Packit Service db8eaa
		fr = frames;						\
Packit Service db8eaa
		if (! vol_scale) {					\
Packit Service db8eaa
			while (fr--) {					\
Packit Service db8eaa
				*dst = 0;				\
Packit Service db8eaa
				dst += dst_step;			\
Packit Service db8eaa
			}						\
Packit Service db8eaa
		} else if (vol_scale == 0xffff) {			\
Packit Service db8eaa
			while (fr--) {					\
Packit Service db8eaa
				*dst = *src;				\
Packit Service db8eaa
				src += dst_step;			\
Packit Service db8eaa
				dst += src_step;			\
Packit Service db8eaa
			}						\
Packit Service db8eaa
		} else {						\
Packit Service db8eaa
			while (fr--) {					\
Packit Service db8eaa
				tmp = *src << 8;			\
Packit Service db8eaa
				tmp = (signed int) tmp >> 8;		\
Packit Service db8eaa
				*dst = MULTI_DIV_24(tmp, vol_scale);	\
Packit Service db8eaa
				src += dst_step;			\
Packit Service db8eaa
				dst += src_step;			\
Packit Service db8eaa
			}						\
Packit Service db8eaa
		}							\
Packit Service db8eaa
	}								\
Packit Service db8eaa
} while (0)
Packit Service db8eaa
		
Packit Service db8eaa
#define GET_VOL_SCALE \
Packit Service db8eaa
	switch (ch) { \
Packit Service db8eaa
	case 0: \
Packit Service db8eaa
	case 2: \
Packit Service db8eaa
		vol_scale = (channels == ch + 1) ? vol_c : vol[0]; \
Packit Service db8eaa
		break; \
Packit Service db8eaa
	case 4: \
Packit Service db8eaa
	case 5: \
Packit Service db8eaa
		vol_scale = vol_c; \
Packit Service db8eaa
		break; \
Packit Service db8eaa
	default: \
Packit Service db8eaa
		vol_scale = vol[ch & 1]; \
Packit Service db8eaa
		break; \
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
#endif /* DOC_HIDDEN */
Packit Service db8eaa
Packit Service db8eaa
/* 2-channel stereo control */
Packit Service db8eaa
static void softvol_convert_stereo_vol(snd_pcm_softvol_t *svol,
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,
Packit Service db8eaa
				       snd_pcm_uframes_t frames)
Packit Service db8eaa
{
Packit Service db8eaa
	const snd_pcm_channel_area_t *dst_area, *src_area;
Packit Service db8eaa
	unsigned int src_step, dst_step;
Packit Service db8eaa
	unsigned int vol_scale, vol[2], vol_c;
Packit Service db8eaa
Packit Service db8eaa
	if (svol->cur_vol[0] == 0 && svol->cur_vol[1] == 0) {
Packit Service db8eaa
		snd_pcm_areas_silence(dst_areas, dst_offset, channels, frames,
Packit Service db8eaa
				      svol->sformat);
Packit Service db8eaa
		return;
Packit Service db8eaa
	} else if (svol->zero_dB_val && svol->cur_vol[0] == svol->zero_dB_val &&
Packit Service db8eaa
		   svol->cur_vol[1] == svol->zero_dB_val) {
Packit Service db8eaa
		snd_pcm_areas_copy(dst_areas, dst_offset, src_areas, src_offset,
Packit Service db8eaa
				   channels, frames, svol->sformat);
Packit Service db8eaa
		return;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	if (svol->max_val == 1) {
Packit Service db8eaa
		vol[0] = svol->cur_vol[0] ? 0xffff : 0;
Packit Service db8eaa
		vol[1] = svol->cur_vol[1] ? 0xffff : 0;
Packit Service db8eaa
		vol_c = vol[0] | vol[1];
Packit Service db8eaa
	} else {
Packit Service db8eaa
		vol[0] = svol->dB_value[svol->cur_vol[0]];
Packit Service db8eaa
		vol[1] = svol->dB_value[svol->cur_vol[1]];
Packit Service db8eaa
		vol_c = svol->dB_value[(svol->cur_vol[0] + svol->cur_vol[1]) / 2];
Packit Service db8eaa
	}
Packit Service db8eaa
	switch (svol->sformat) {
Packit Service db8eaa
	case SND_PCM_FORMAT_S16_LE:
Packit Service db8eaa
	case SND_PCM_FORMAT_S16_BE:
Packit Service db8eaa
		/* 16bit samples */
Packit Service db8eaa
		CONVERT_AREA(short, 
Packit Service db8eaa
			     !snd_pcm_format_cpu_endian(svol->sformat));
Packit Service db8eaa
		break;
Packit Service db8eaa
	case SND_PCM_FORMAT_S32_LE:
Packit Service db8eaa
	case SND_PCM_FORMAT_S32_BE:
Packit Service db8eaa
		/* 32bit samples */
Packit Service db8eaa
		CONVERT_AREA(int,
Packit Service db8eaa
			     !snd_pcm_format_cpu_endian(svol->sformat));
Packit Service db8eaa
		break;
Packit Service db8eaa
	case SND_PCM_FORMAT_S24_LE:
Packit Service db8eaa
		/* 24bit samples */
Packit Service db8eaa
		CONVERT_AREA_S24_LE();
Packit Service db8eaa
		break;
Packit Service db8eaa
	case SND_PCM_FORMAT_S24_3LE:
Packit Service db8eaa
		CONVERT_AREA_S24_3LE();
Packit Service db8eaa
		break;
Packit Service db8eaa
	default:
Packit Service db8eaa
		break;
Packit Service db8eaa
	}
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
#undef GET_VOL_SCALE
Packit Service db8eaa
#define GET_VOL_SCALE
Packit Service db8eaa
Packit Service db8eaa
/* mono control */
Packit Service db8eaa
static void softvol_convert_mono_vol(snd_pcm_softvol_t *svol,
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,
Packit Service db8eaa
				     snd_pcm_uframes_t frames)
Packit Service db8eaa
{
Packit Service db8eaa
	const snd_pcm_channel_area_t *dst_area, *src_area;
Packit Service db8eaa
	unsigned int src_step, dst_step;
Packit Service db8eaa
	unsigned int vol_scale;
Packit Service db8eaa
Packit Service db8eaa
	if (svol->cur_vol[0] == 0) {
Packit Service db8eaa
		snd_pcm_areas_silence(dst_areas, dst_offset, channels, frames,
Packit Service db8eaa
				      svol->sformat);
Packit Service db8eaa
		return;
Packit Service db8eaa
	} else if (svol->zero_dB_val && svol->cur_vol[0] == svol->zero_dB_val) {
Packit Service db8eaa
		snd_pcm_areas_copy(dst_areas, dst_offset, src_areas, src_offset,
Packit Service db8eaa
				   channels, frames, svol->sformat);
Packit Service db8eaa
		return;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	if (svol->max_val == 1)
Packit Service db8eaa
		vol_scale = svol->cur_vol[0] ? 0xffff : 0;
Packit Service db8eaa
	else
Packit Service db8eaa
		vol_scale = svol->dB_value[svol->cur_vol[0]];
Packit Service db8eaa
	switch (svol->sformat) {
Packit Service db8eaa
	case SND_PCM_FORMAT_S16_LE:
Packit Service db8eaa
	case SND_PCM_FORMAT_S16_BE:
Packit Service db8eaa
		/* 16bit samples */
Packit Service db8eaa
		CONVERT_AREA(short, 
Packit Service db8eaa
			     !snd_pcm_format_cpu_endian(svol->sformat));
Packit Service db8eaa
		break;
Packit Service db8eaa
	case SND_PCM_FORMAT_S32_LE:
Packit Service db8eaa
	case SND_PCM_FORMAT_S32_BE:
Packit Service db8eaa
		/* 32bit samples */
Packit Service db8eaa
		CONVERT_AREA(int,
Packit Service db8eaa
			     !snd_pcm_format_cpu_endian(svol->sformat));
Packit Service db8eaa
		break;
Packit Service db8eaa
	case SND_PCM_FORMAT_S24_LE:
Packit Service db8eaa
		/* 24bit samples */
Packit Service db8eaa
		CONVERT_AREA_S24_LE();
Packit Service db8eaa
		break;
Packit Service db8eaa
	case SND_PCM_FORMAT_S24_3LE:
Packit Service db8eaa
		CONVERT_AREA_S24_3LE();
Packit Service db8eaa
		break;
Packit Service db8eaa
	default:
Packit Service db8eaa
		break;
Packit Service db8eaa
	}
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/*
Packit Service db8eaa
 * get the current volume value from driver
Packit Service db8eaa
 *
Packit Service db8eaa
 * TODO: mmap support?
Packit Service db8eaa
 */
Packit Service db8eaa
static void get_current_volume(snd_pcm_softvol_t *svol)
Packit Service db8eaa
{
Packit Service db8eaa
	unsigned int val;
Packit Service db8eaa
	unsigned int i;
Packit Service db8eaa
Packit Service db8eaa
	if (snd_ctl_elem_read(svol->ctl, &svol->elem) < 0)
Packit Service db8eaa
		return;
Packit Service db8eaa
	for (i = 0; i < svol->cchannels; i++) {
Packit Service db8eaa
		val = svol->elem.value.integer.value[i];
Packit Service db8eaa
		if (val > svol->max_val)
Packit Service db8eaa
			val = svol->max_val;
Packit Service db8eaa
		svol->cur_vol[i] = val;
Packit Service db8eaa
	}
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static void softvol_free(snd_pcm_softvol_t *svol)
Packit Service db8eaa
{
Packit Service db8eaa
	if (svol->plug.gen.close_slave)
Packit Service db8eaa
		snd_pcm_close(svol->plug.gen.slave);
Packit Service db8eaa
	if (svol->ctl)
Packit Service db8eaa
		snd_ctl_close(svol->ctl);
Packit Service db8eaa
	if (svol->dB_value && svol->dB_value != preset_dB_value)
Packit Service db8eaa
		free(svol->dB_value);
Packit Service db8eaa
	free(svol);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_softvol_close(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_softvol_t *svol = pcm->private_data;
Packit Service db8eaa
	softvol_free(svol);
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_softvol_hw_refine_cprepare(snd_pcm_t *pcm,
Packit Service db8eaa
					      snd_pcm_hw_params_t *params)
Packit Service db8eaa
{
Packit Service db8eaa
	int err;
Packit Service db8eaa
	snd_pcm_softvol_t *svol = pcm->private_data;
Packit Service db8eaa
	snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM };
Packit Service db8eaa
	snd_pcm_format_mask_t format_mask = {
Packit Service db8eaa
		{
Packit Service db8eaa
			(1ULL << SND_PCM_FORMAT_S16_LE) |
Packit Service db8eaa
			(1ULL << SND_PCM_FORMAT_S16_BE) |
Packit Service db8eaa
			(1ULL << SND_PCM_FORMAT_S24_LE) |
Packit Service db8eaa
			(1ULL << SND_PCM_FORMAT_S32_LE) |
Packit Service db8eaa
 			(1ULL << SND_PCM_FORMAT_S32_BE),
Packit Service db8eaa
			(1ULL << (SND_PCM_FORMAT_S24_3LE - 32))
Packit Service db8eaa
		}
Packit Service db8eaa
	};
Packit Service db8eaa
	if (svol->sformat != SND_PCM_FORMAT_UNKNOWN) {
Packit Service db8eaa
		snd_pcm_format_mask_none(&format_mask);
Packit Service db8eaa
		snd_pcm_format_mask_set(&format_mask, svol->sformat);
Packit Service db8eaa
	}
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
	err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT,
Packit Service db8eaa
					 &format_mask);
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
	err = _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_CHANNELS, 1, 0);
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_softvol_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_softvol_t *svol = 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
	if (svol->sformat != SND_PCM_FORMAT_UNKNOWN) {
Packit Service db8eaa
		_snd_pcm_hw_params_set_format(sparams, svol->sformat);
Packit Service db8eaa
		_snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
Packit Service db8eaa
	}
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/*
Packit Service db8eaa
 * refine the access mask
Packit Service db8eaa
 */
Packit Service db8eaa
static int check_access_mask(snd_pcm_hw_params_t *src,
Packit Service db8eaa
			     snd_pcm_hw_params_t *dst)
Packit Service db8eaa
{
Packit Service db8eaa
	const snd_pcm_access_mask_t *mask;
Packit Service db8eaa
	snd_pcm_access_mask_t smask;
Packit Service db8eaa
Packit Service db8eaa
	mask = snd_pcm_hw_param_get_mask(src, SND_PCM_HW_PARAM_ACCESS);
Packit Service db8eaa
	snd_mask_none(&smask;;
Packit Service db8eaa
	if (snd_pcm_access_mask_test(mask, SND_PCM_ACCESS_RW_INTERLEAVED) ||
Packit Service db8eaa
	    snd_pcm_access_mask_test(mask, SND_PCM_ACCESS_MMAP_INTERLEAVED)) {
Packit Service db8eaa
		snd_pcm_access_mask_set(&smask,
Packit Service db8eaa
					SND_PCM_ACCESS_RW_INTERLEAVED);
Packit Service db8eaa
		snd_pcm_access_mask_set(&smask,
Packit Service db8eaa
					SND_PCM_ACCESS_MMAP_INTERLEAVED);
Packit Service db8eaa
	}
Packit Service db8eaa
	if (snd_pcm_access_mask_test(mask, SND_PCM_ACCESS_RW_NONINTERLEAVED) ||
Packit Service db8eaa
	    snd_pcm_access_mask_test(mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED))  {
Packit Service db8eaa
		snd_pcm_access_mask_set(&smask,
Packit Service db8eaa
					SND_PCM_ACCESS_RW_NONINTERLEAVED);
Packit Service db8eaa
		snd_pcm_access_mask_set(&smask,
Packit Service db8eaa
					SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
Packit Service db8eaa
	}
Packit Service db8eaa
	if (snd_pcm_access_mask_test(mask, SND_PCM_ACCESS_MMAP_COMPLEX))
Packit Service db8eaa
		snd_pcm_access_mask_set(&smask,
Packit Service db8eaa
					SND_PCM_ACCESS_MMAP_COMPLEX);
Packit Service db8eaa
Packit Service db8eaa
	return _snd_pcm_hw_param_set_mask(dst, SND_PCM_HW_PARAM_ACCESS, &smask;;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_softvol_hw_refine_schange(snd_pcm_t *pcm,
Packit Service db8eaa
					     snd_pcm_hw_params_t *params,
Packit Service db8eaa
					     snd_pcm_hw_params_t *sparams)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_softvol_t *svol = pcm->private_data;
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_PERIODS |
Packit Service db8eaa
			      SND_PCM_HW_PARBIT_PERIOD_SIZE |
Packit Service db8eaa
			      SND_PCM_HW_PARBIT_PERIOD_TIME |
Packit Service db8eaa
			      SND_PCM_HW_PARBIT_BUFFER_SIZE |
Packit Service db8eaa
			      SND_PCM_HW_PARBIT_BUFFER_TIME |
Packit Service db8eaa
			      SND_PCM_HW_PARBIT_TICK_TIME);
Packit Service db8eaa
	if (svol->sformat == SND_PCM_FORMAT_UNKNOWN)
Packit Service db8eaa
		links |= (SND_PCM_HW_PARBIT_FORMAT | 
Packit Service db8eaa
			  SND_PCM_HW_PARBIT_SUBFORMAT |
Packit Service db8eaa
			  SND_PCM_HW_PARBIT_SAMPLE_BITS);
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
Packit Service db8eaa
	err = check_access_mask(params, sparams);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
	
Packit Service db8eaa
static int snd_pcm_softvol_hw_refine_cchange(snd_pcm_t *pcm,
Packit Service db8eaa
					     snd_pcm_hw_params_t *params,
Packit Service db8eaa
					    snd_pcm_hw_params_t *sparams)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_softvol_t *svol = pcm->private_data;
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_PERIODS |
Packit Service db8eaa
			      SND_PCM_HW_PARBIT_PERIOD_SIZE |
Packit Service db8eaa
			      SND_PCM_HW_PARBIT_PERIOD_TIME |
Packit Service db8eaa
			      SND_PCM_HW_PARBIT_BUFFER_SIZE |
Packit Service db8eaa
			      SND_PCM_HW_PARBIT_BUFFER_TIME |
Packit Service db8eaa
			      SND_PCM_HW_PARBIT_TICK_TIME);
Packit Service db8eaa
	if (svol->sformat == SND_PCM_FORMAT_UNKNOWN)
Packit Service db8eaa
		links |= (SND_PCM_HW_PARBIT_FORMAT | 
Packit Service db8eaa
			  SND_PCM_HW_PARBIT_SUBFORMAT |
Packit Service db8eaa
			  SND_PCM_HW_PARBIT_SAMPLE_BITS);
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
Packit Service db8eaa
	err = check_access_mask(sparams, params);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_softvol_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_softvol_hw_refine_cprepare,
Packit Service db8eaa
				       snd_pcm_softvol_hw_refine_cchange,
Packit Service db8eaa
				       snd_pcm_softvol_hw_refine_sprepare,
Packit Service db8eaa
				       snd_pcm_softvol_hw_refine_schange,
Packit Service db8eaa
				       snd_pcm_generic_hw_refine);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_softvol_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_softvol_t *svol = pcm->private_data;
Packit Service db8eaa
	snd_pcm_t *slave = svol->plug.gen.slave;
Packit Service db8eaa
	int err = snd_pcm_hw_params_slave(pcm, params,
Packit Service db8eaa
					  snd_pcm_softvol_hw_refine_cchange,
Packit Service db8eaa
					  snd_pcm_softvol_hw_refine_sprepare,
Packit Service db8eaa
					  snd_pcm_softvol_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
	if (slave->format != SND_PCM_FORMAT_S16_LE &&
Packit Service db8eaa
	    slave->format != SND_PCM_FORMAT_S16_BE &&
Packit Service db8eaa
	    slave->format != SND_PCM_FORMAT_S24_3LE && 
Packit Service db8eaa
	    slave->format != SND_PCM_FORMAT_S24_LE &&
Packit Service db8eaa
	    slave->format != SND_PCM_FORMAT_S32_LE &&
Packit Service db8eaa
	    slave->format != SND_PCM_FORMAT_S32_BE) {
Packit Service db8eaa
		SNDERR("softvol supports only S16_LE, S16_BE, S24_LE, S24_3LE, "
Packit Service db8eaa
		       "S32_LE or S32_BE");
Packit Service db8eaa
		return -EINVAL;
Packit Service db8eaa
	}
Packit Service db8eaa
	svol->sformat = slave->format;
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static snd_pcm_uframes_t
Packit Service db8eaa
snd_pcm_softvol_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_softvol_t *svol = pcm->private_data;
Packit Service db8eaa
	if (size > *slave_sizep)
Packit Service db8eaa
		size = *slave_sizep;
Packit Service db8eaa
	get_current_volume(svol);
Packit Service db8eaa
	if (svol->cchannels == 1)
Packit Service db8eaa
		softvol_convert_mono_vol(svol, slave_areas, slave_offset,
Packit Service db8eaa
					 areas, offset, pcm->channels, size);
Packit Service db8eaa
	else
Packit Service db8eaa
		softvol_convert_stereo_vol(svol, slave_areas, slave_offset,
Packit Service db8eaa
					   areas, offset, 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_softvol_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_softvol_t *svol = pcm->private_data;
Packit Service db8eaa
	if (size > *slave_sizep)
Packit Service db8eaa
		size = *slave_sizep;
Packit Service db8eaa
	get_current_volume(svol);
Packit Service db8eaa
	if (svol->cchannels == 1)
Packit Service db8eaa
		softvol_convert_mono_vol(svol, areas, offset, slave_areas,
Packit Service db8eaa
					 slave_offset, pcm->channels, size);
Packit Service db8eaa
	else
Packit Service db8eaa
		softvol_convert_stereo_vol(svol, areas, offset, slave_areas,
Packit Service db8eaa
					   slave_offset, pcm->channels, size);
Packit Service db8eaa
	*slave_sizep = size;
Packit Service db8eaa
	return size;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static void snd_pcm_softvol_dump(snd_pcm_t *pcm, snd_output_t *out)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_softvol_t *svol = pcm->private_data;
Packit Service db8eaa
	snd_output_printf(out, "Soft volume PCM\n");
Packit Service db8eaa
	snd_output_printf(out, "Control: %s\n", svol->elem.id.name);
Packit Service db8eaa
	if (svol->max_val == 1)
Packit Service db8eaa
		snd_output_printf(out, "boolean\n");
Packit Service db8eaa
	else {
Packit Service db8eaa
		snd_output_printf(out, "min_dB: %g\n", svol->min_dB);
Packit Service db8eaa
		snd_output_printf(out, "max_dB: %g\n", svol->max_dB);
Packit Service db8eaa
		snd_output_printf(out, "resolution: %d\n", svol->max_val + 1);
Packit Service db8eaa
	}
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(svol->plug.gen.slave, out);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int add_tlv_info(snd_pcm_softvol_t *svol, snd_ctl_elem_info_t *cinfo)
Packit Service db8eaa
{
Packit Service db8eaa
	unsigned int tlv[4];
Packit Service db8eaa
	tlv[SNDRV_CTL_TLVO_TYPE] = SND_CTL_TLVT_DB_SCALE;
Packit Service db8eaa
	tlv[SNDRV_CTL_TLVO_LEN] = 2 * sizeof(int);
Packit Service db8eaa
	tlv[SNDRV_CTL_TLVO_DB_SCALE_MIN] = (int)(svol->min_dB * 100);
Packit Service db8eaa
	tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] =
Packit Service db8eaa
		(int)((svol->max_dB - svol->min_dB) * 100 / svol->max_val);
Packit Service db8eaa
	return snd_ctl_elem_tlv_write(svol->ctl, &cinfo->id, tlv);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int add_user_ctl(snd_pcm_softvol_t *svol, snd_ctl_elem_info_t *cinfo,
Packit Service db8eaa
			int count)
Packit Service db8eaa
{
Packit Service db8eaa
	int err;
Packit Service db8eaa
	int i;
Packit Service db8eaa
	unsigned int def_val;
Packit Service db8eaa
	
Packit Service db8eaa
	if (svol->max_val == 1)
Packit Service db8eaa
		err = snd_ctl_add_boolean_elem_set(svol->ctl, cinfo, 1, count);
Packit Service db8eaa
	else
Packit Service db8eaa
		err = snd_ctl_add_integer_elem_set(svol->ctl, cinfo, 1, count,
Packit Service db8eaa
						   0, svol->max_val, 0);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	if (svol->max_val == 1)
Packit Service db8eaa
		def_val = 1;
Packit Service db8eaa
	else {
Packit Service db8eaa
		add_tlv_info(svol, cinfo);
Packit Service db8eaa
		/* set zero dB value as default, or max_val if
Packit Service db8eaa
		   there is no 0 dB setting */
Packit Service db8eaa
		def_val = svol->zero_dB_val ? svol->zero_dB_val : svol->max_val;
Packit Service db8eaa
	}
Packit Service db8eaa
	for (i = 0; i < count; i++)
Packit Service db8eaa
		svol->elem.value.integer.value[i] = def_val;
Packit Service db8eaa
	return snd_ctl_elem_write(svol->ctl, &svol->elem);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/*
Packit Service db8eaa
 * load and set up user-control
Packit Service db8eaa
 * returns 0 if the user-control is found or created,
Packit Service db8eaa
 * returns 1 if the control is a hw control,
Packit Service db8eaa
 * or a negative error code
Packit Service db8eaa
 */
Packit Service db8eaa
static int softvol_load_control(snd_pcm_t *pcm, snd_pcm_softvol_t *svol,
Packit Service db8eaa
				int ctl_card, snd_ctl_elem_id_t *ctl_id,
Packit Service db8eaa
				int cchannels, double min_dB, double max_dB,
Packit Service db8eaa
				int resolution)
Packit Service db8eaa
{
Packit Service db8eaa
	char tmp_name[32];
Packit Service db8eaa
	snd_pcm_info_t info = {0};
Packit Service db8eaa
	snd_ctl_elem_info_t cinfo = {0};
Packit Service db8eaa
	int err;
Packit Service db8eaa
	unsigned int i;
Packit Service db8eaa
Packit Service db8eaa
	if (ctl_card < 0) {
Packit Service db8eaa
		err = snd_pcm_info(pcm, &info;;
Packit Service db8eaa
		if (err < 0)
Packit Service db8eaa
			return err;
Packit Service db8eaa
		ctl_card = snd_pcm_info_get_card(&info;;
Packit Service db8eaa
		if (ctl_card < 0) {
Packit Service db8eaa
			SNDERR("No card defined for softvol control");
Packit Service db8eaa
			return -EINVAL;
Packit Service db8eaa
		}
Packit Service db8eaa
	}
Packit Service db8eaa
	sprintf(tmp_name, "hw:%d", ctl_card);
Packit Service db8eaa
	err = snd_ctl_open(&svol->ctl, tmp_name, 0);
Packit Service db8eaa
	if (err < 0) {
Packit Service db8eaa
		SNDERR("Cannot open CTL %s", tmp_name);
Packit Service db8eaa
		return err;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	svol->elem.id = *ctl_id;
Packit Service db8eaa
	svol->max_val = resolution - 1;
Packit Service db8eaa
	svol->min_dB = min_dB;
Packit Service db8eaa
	svol->max_dB = max_dB;
Packit Service db8eaa
	if (svol->max_val == 1 || svol->max_dB == ZERO_DB)
Packit Service db8eaa
		svol->zero_dB_val = svol->max_val;
Packit Service db8eaa
	else if (svol->max_dB < 0)
Packit Service db8eaa
		svol->zero_dB_val = 0; /* there is no 0 dB setting */
Packit Service db8eaa
	else
Packit Service db8eaa
		svol->zero_dB_val = (min_dB / (min_dB - max_dB)) *
Packit Service db8eaa
								svol->max_val;
Packit Service db8eaa
		
Packit Service db8eaa
	snd_ctl_elem_info_set_id(&cinfo, ctl_id);
Packit Service db8eaa
	if ((err = snd_ctl_elem_info(svol->ctl, &cinfo)) < 0) {
Packit Service db8eaa
		if (err != -ENOENT) {
Packit Service db8eaa
			SNDERR("Cannot get info for CTL %s", tmp_name);
Packit Service db8eaa
			return err;
Packit Service db8eaa
		}
Packit Service db8eaa
		err = add_user_ctl(svol, &cinfo, cchannels);
Packit Service db8eaa
		if (err < 0) {
Packit Service db8eaa
			SNDERR("Cannot add a control");
Packit Service db8eaa
			return err;
Packit Service db8eaa
		}
Packit Service db8eaa
	} else {
Packit Service db8eaa
		if (! (cinfo.access & SNDRV_CTL_ELEM_ACCESS_USER)) {
Packit Service db8eaa
			/* hardware control exists */
Packit Service db8eaa
			return 1; /* notify */
Packit Service db8eaa
Packit Service db8eaa
		} else if ((cinfo.type != SND_CTL_ELEM_TYPE_INTEGER &&
Packit Service db8eaa
			    cinfo.type != SND_CTL_ELEM_TYPE_BOOLEAN) ||
Packit Service db8eaa
			   cinfo.count != (unsigned int)cchannels ||
Packit Service db8eaa
			   cinfo.value.integer.min != 0 ||
Packit Service db8eaa
			   cinfo.value.integer.max != resolution - 1) {
Packit Service db8eaa
			err = snd_ctl_elem_remove(svol->ctl, &cinfo.id);
Packit Service db8eaa
			if (err < 0) {
Packit Service db8eaa
				SNDERR("Control %s mismatch", tmp_name);
Packit Service db8eaa
				return err;
Packit Service db8eaa
			}
Packit Service db8eaa
			/* reset numid */
Packit Service db8eaa
			snd_ctl_elem_info_set_id(&cinfo, ctl_id);
Packit Service db8eaa
			if ((err = add_user_ctl(svol, &cinfo, cchannels)) < 0) {
Packit Service db8eaa
				SNDERR("Cannot add a control");
Packit Service db8eaa
				return err;
Packit Service db8eaa
			}
Packit Service db8eaa
		} else if (svol->max_val > 1) {
Packit Service db8eaa
			/* check TLV availability */
Packit Service db8eaa
			unsigned int tlv[4];
Packit Service db8eaa
			err = snd_ctl_elem_tlv_read(svol->ctl, &cinfo.id, tlv,
Packit Service db8eaa
						    sizeof(tlv));
Packit Service db8eaa
			if (err < 0)
Packit Service db8eaa
				add_tlv_info(svol, &cinfo);
Packit Service db8eaa
		}
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	if (svol->max_val == 1)
Packit Service db8eaa
		return 0;
Packit Service db8eaa
Packit Service db8eaa
	/* set up dB table */
Packit Service db8eaa
	if (min_dB == PRESET_MIN_DB && max_dB == ZERO_DB &&
Packit Service db8eaa
						resolution == PRESET_RESOLUTION)
Packit Service db8eaa
		svol->dB_value = (unsigned int*)preset_dB_value;
Packit Service db8eaa
	else {
Packit Service db8eaa
#ifndef HAVE_SOFT_FLOAT
Packit Service db8eaa
		svol->dB_value = calloc(resolution, sizeof(unsigned int));
Packit Service db8eaa
		if (! svol->dB_value) {
Packit Service db8eaa
			SNDERR("cannot allocate dB table");
Packit Service db8eaa
			return -ENOMEM;
Packit Service db8eaa
		}
Packit Service db8eaa
		svol->min_dB = min_dB;
Packit Service db8eaa
		svol->max_dB = max_dB;
Packit Service db8eaa
		for (i = 0; i <= svol->max_val; i++) {
Packit Service db8eaa
			double db = svol->min_dB +
Packit Service db8eaa
				(i * (svol->max_dB - svol->min_dB)) /
Packit Service db8eaa
					svol->max_val;
Packit Service db8eaa
			double v = (pow(10.0, db / 20.0) *
Packit Service db8eaa
					(double)(1 << VOL_SCALE_SHIFT));
Packit Service db8eaa
			svol->dB_value[i] = (unsigned int)v;
Packit Service db8eaa
		}
Packit Service db8eaa
		if (svol->zero_dB_val)
Packit Service db8eaa
			svol->dB_value[svol->zero_dB_val] = 65535;
Packit Service db8eaa
#else
Packit Service db8eaa
		SNDERR("Cannot handle the given dB range and resolution");
Packit Service db8eaa
		return -EINVAL;
Packit Service db8eaa
#endif
Packit Service db8eaa
	}
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static const snd_pcm_ops_t snd_pcm_softvol_ops = {
Packit Service db8eaa
	.close = snd_pcm_softvol_close,
Packit Service db8eaa
	.info = snd_pcm_generic_info,
Packit Service db8eaa
	.hw_refine = snd_pcm_softvol_hw_refine,
Packit Service db8eaa
	.hw_params = snd_pcm_softvol_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_softvol_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 SoftVolume PCM
Packit Service db8eaa
 * \param pcmp Returns created PCM handle
Packit Service db8eaa
 * \param name Name of PCM
Packit Service db8eaa
 * \param sformat Slave format
Packit Service db8eaa
 * \param ctl_card card index of the control
Packit Service db8eaa
 * \param ctl_id The control element
Packit Service db8eaa
 * \param cchannels PCM channels
Packit Service db8eaa
 * \param min_dB minimal dB value
Packit Service db8eaa
 * \param max_dB maximal dB value
Packit Service db8eaa
 * \param resolution resolution of control
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
 * \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_softvol_open(snd_pcm_t **pcmp, const char *name,
Packit Service db8eaa
			 snd_pcm_format_t sformat,
Packit Service db8eaa
			 int ctl_card, snd_ctl_elem_id_t *ctl_id,
Packit Service db8eaa
			 int cchannels,
Packit Service db8eaa
			 double min_dB, double max_dB, int resolution,
Packit Service db8eaa
			 snd_pcm_t *slave, int close_slave)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_t *pcm;
Packit Service db8eaa
	snd_pcm_softvol_t *svol;
Packit Service db8eaa
	int err;
Packit Service db8eaa
	assert(pcmp && slave);
Packit Service db8eaa
	if (sformat != SND_PCM_FORMAT_UNKNOWN &&
Packit Service db8eaa
	    sformat != SND_PCM_FORMAT_S16_LE &&
Packit Service db8eaa
	    sformat != SND_PCM_FORMAT_S16_BE &&
Packit Service db8eaa
	    sformat != SND_PCM_FORMAT_S24_3LE && 
Packit Service db8eaa
	    sformat != SND_PCM_FORMAT_S24_LE &&
Packit Service db8eaa
	    sformat != SND_PCM_FORMAT_S32_LE &&
Packit Service db8eaa
	    sformat != SND_PCM_FORMAT_S32_BE)
Packit Service db8eaa
		return -EINVAL;
Packit Service db8eaa
	svol = calloc(1, sizeof(*svol));
Packit Service db8eaa
	if (! svol)
Packit Service db8eaa
		return -ENOMEM;
Packit Service db8eaa
	err = softvol_load_control(slave, svol, ctl_card, ctl_id, cchannels,
Packit Service db8eaa
				   min_dB, max_dB, resolution);
Packit Service db8eaa
	if (err < 0) {
Packit Service db8eaa
		softvol_free(svol);
Packit Service db8eaa
		return err;
Packit Service db8eaa
	}
Packit Service db8eaa
	if (err > 0) { /* hardware control - no need for softvol! */
Packit Service db8eaa
		softvol_free(svol);
Packit Service db8eaa
		*pcmp = slave; /* just pass the slave */
Packit Service db8eaa
		if (!slave->name && name)
Packit Service db8eaa
			slave->name = strdup(name);
Packit Service db8eaa
		return 0;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	/* do softvol */
Packit Service db8eaa
	snd_pcm_plugin_init(&svol->plug);
Packit Service db8eaa
	svol->sformat = sformat;
Packit Service db8eaa
	svol->cchannels = cchannels;
Packit Service db8eaa
	svol->plug.read = snd_pcm_softvol_read_areas;
Packit Service db8eaa
	svol->plug.write = snd_pcm_softvol_write_areas;
Packit Service db8eaa
	svol->plug.undo_read = snd_pcm_plugin_undo_read_generic;
Packit Service db8eaa
	svol->plug.undo_write = snd_pcm_plugin_undo_write_generic;
Packit Service db8eaa
	svol->plug.gen.slave = slave;
Packit Service db8eaa
	svol->plug.gen.close_slave = close_slave;
Packit Service db8eaa
Packit Service db8eaa
	err = snd_pcm_new(&pcm, SND_PCM_TYPE_SOFTVOL, name, slave->stream, slave->mode);
Packit Service db8eaa
	if (err < 0) {
Packit Service db8eaa
		softvol_free(svol);
Packit Service db8eaa
		return err;
Packit Service db8eaa
	}
Packit Service db8eaa
	pcm->ops = &snd_pcm_softvol_ops;
Packit Service db8eaa
	pcm->fast_ops = &snd_pcm_plugin_fast_ops;
Packit Service db8eaa
	pcm->private_data = svol;
Packit Service db8eaa
	pcm->poll_fd = slave->poll_fd;
Packit Service db8eaa
	pcm->poll_events = slave->poll_events;
Packit Service db8eaa
	/*
Packit Service db8eaa
	 * Since the softvol converts on the place, and the format/channels
Packit Service db8eaa
	 * must be identical between source and destination, we don't need
Packit Service db8eaa
	 * an extra buffer.
Packit Service db8eaa
	 */
Packit Service db8eaa
	pcm->mmap_shadow = 1;
Packit Service db8eaa
	pcm->tstamp_type = slave->tstamp_type;
Packit Service db8eaa
	snd_pcm_set_hw_ptr(pcm, &svol->plug.hw_ptr, -1, 0);
Packit Service db8eaa
	snd_pcm_set_appl_ptr(pcm, &svol->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
/* in pcm_misc.c */
Packit Service db8eaa
int snd_pcm_parse_control_id(snd_config_t *conf, snd_ctl_elem_id_t *ctl_id, int *cardp,
Packit Service db8eaa
			     int *cchannelsp, int *hwctlp);
Packit Service db8eaa
Packit Service db8eaa
/*! \page pcm_plugins
Packit Service db8eaa
Packit Service db8eaa
\section pcm_plugins_softvol Plugin: Soft Volume
Packit Service db8eaa
Packit Service db8eaa
This plugin applies the software volume attenuation.
Packit Service db8eaa
The format, rate and channels must match for both of source and destination.
Packit Service db8eaa
Packit Service db8eaa
When the control is stereo (count=2), the channels are assumed to be either
Packit Service db8eaa
mono, 2.0, 2.1, 4.0, 4.1, 5.1 or 7.1.
Packit Service db8eaa
Packit Service db8eaa
If the control already exists and it's a system control (i.e. no
Packit Service db8eaa
user-defined control), the plugin simply passes its slave without
Packit Service db8eaa
any changes.
Packit Service db8eaa
Packit Service db8eaa
\code
Packit Service db8eaa
pcm.name {
Packit Service db8eaa
        type softvol            # Soft Volume 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
                [format STR]    # Slave format
Packit Service db8eaa
        }
Packit Service db8eaa
        control {
Packit Service db8eaa
	        name STR        # control element id string
Packit Service db8eaa
		[card STR]      # control card index
Packit Service db8eaa
		[iface STR]     # interface of the element
Packit Service db8eaa
		[index INT]     # index of the element
Packit Service db8eaa
		[device INT]    # device number of the element
Packit Service db8eaa
		[subdevice INT] # subdevice number of the element
Packit Service db8eaa
		[count INT]     # control channels 1 or 2 (default: 2)
Packit Service db8eaa
	}
Packit Service db8eaa
	[min_dB REAL]           # minimal dB value (default: -51.0)
Packit Service db8eaa
	[max_dB REAL]           # maximal dB value (default:   0.0)
Packit Service db8eaa
	[resolution INT]        # resolution (default: 256)
Packit Service db8eaa
				# resolution = 2 means a mute switch
Packit Service db8eaa
}
Packit Service db8eaa
\endcode
Packit Service db8eaa
Packit Service db8eaa
\subsection pcm_plugins_softvol_funcref Function reference
Packit Service db8eaa
Packit Service db8eaa
    Packit Service db8eaa
      
  • snd_pcm_softvol_open()
  • Packit Service db8eaa
      
  • _snd_pcm_softvol_open()
  • Packit Service db8eaa
    Packit Service db8eaa
    Packit Service db8eaa
    */
    Packit Service db8eaa
    Packit Service db8eaa
    /**
    Packit Service db8eaa
     * \brief Creates a new Soft Volume 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 Soft Volume 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_softvol_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 *control = NULL;
    Packit Service db8eaa
    	snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
    Packit Service db8eaa
    	snd_ctl_elem_id_t ctl_id = {0};
    Packit Service db8eaa
    	int resolution = PRESET_RESOLUTION;
    Packit Service db8eaa
    	double min_dB = PRESET_MIN_DB;
    Packit Service db8eaa
    	double max_dB = ZERO_DB;
    Packit Service db8eaa
    	int card = -1, cchannels = 2;
    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, "control") == 0) {
    Packit Service db8eaa
    			control = n;
    Packit Service db8eaa
    			continue;
    Packit Service db8eaa
    		}
    Packit Service db8eaa
    		if (strcmp(id, "resolution") == 0) {
    Packit Service db8eaa
    			long v;
    Packit Service db8eaa
    			err = snd_config_get_integer(n, &v);
    Packit Service db8eaa
    			if (err < 0) {
    Packit Service db8eaa
    				SNDERR("Invalid resolution value");
    Packit Service db8eaa
    				return err;
    Packit Service db8eaa
    			}
    Packit Service db8eaa
    			resolution = v;
    Packit Service db8eaa
    			continue;
    Packit Service db8eaa
    		}
    Packit Service db8eaa
    		if (strcmp(id, "min_dB") == 0) {
    Packit Service db8eaa
    			err = snd_config_get_real(n, &min_dB);
    Packit Service db8eaa
    			if (err < 0) {
    Packit Service db8eaa
    				SNDERR("Invalid min_dB value");
    Packit Service db8eaa
    				return err;
    Packit Service db8eaa
    			}
    Packit Service db8eaa
    			continue;
    Packit Service db8eaa
    		}
    Packit Service db8eaa
    		if (strcmp(id, "max_dB") == 0) {
    Packit Service db8eaa
    			err = snd_config_get_real(n, &max_dB);
    Packit Service db8eaa
    			if (err < 0) {
    Packit Service db8eaa
    				SNDERR("Invalid max_dB value");
    Packit Service db8eaa
    				return err;
    Packit Service db8eaa
    			}
    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
    	if (!slave) {
    Packit Service db8eaa
    		SNDERR("slave is not defined");
    Packit Service db8eaa
    		return -EINVAL;
    Packit Service db8eaa
    	}
    Packit Service db8eaa
    	if (!control) {
    Packit Service db8eaa
    		SNDERR("control is not defined");
    Packit Service db8eaa
    		return -EINVAL;
    Packit Service db8eaa
    	}
    Packit Service db8eaa
    	if (min_dB >= 0) {
    Packit Service db8eaa
    		SNDERR("min_dB must be a negative value");
    Packit Service db8eaa
    		return -EINVAL;
    Packit Service db8eaa
    	}
    Packit Service db8eaa
    	if (max_dB <= min_dB || max_dB > MAX_DB_UPPER_LIMIT) {
    Packit Service db8eaa
    		SNDERR("max_dB must be larger than min_dB and less than %d dB",
    Packit Service db8eaa
    		       MAX_DB_UPPER_LIMIT);
    Packit Service db8eaa
    		return -EINVAL;
    Packit Service db8eaa
    	}
    Packit Service db8eaa
    	if (resolution <= 1 || resolution > 1024) {
    Packit Service db8eaa
    		SNDERR("Invalid resolution value %d", resolution);
    Packit Service db8eaa
    		return -EINVAL;
    Packit Service db8eaa
    	}
    Packit Service db8eaa
    	if (mode & SND_PCM_NO_SOFTVOL) {
    Packit Service db8eaa
    		err = snd_pcm_slave_conf(root, slave, &sconf, 0);
    Packit Service db8eaa
    		if (err < 0)
    Packit Service db8eaa
    			return err;
    Packit Service db8eaa
    		err = snd_pcm_open_named_slave(pcmp, name, root, sconf, stream,
    Packit Service db8eaa
    					       mode, conf);
    Packit Service db8eaa
    		snd_config_delete(sconf);
    Packit Service db8eaa
    	} else {
    Packit Service db8eaa
    		err = snd_pcm_slave_conf(root, slave, &sconf, 1,
    Packit Service db8eaa
    					 SND_PCM_HW_PARAM_FORMAT, 0, &sformat);
    Packit Service db8eaa
    		if (err < 0)
    Packit Service db8eaa
    			return err;
    Packit Service db8eaa
    		if (sformat != SND_PCM_FORMAT_UNKNOWN &&
    Packit Service db8eaa
    		    sformat != SND_PCM_FORMAT_S16_LE &&
    Packit Service db8eaa
    		    sformat != SND_PCM_FORMAT_S16_BE &&
    Packit Service db8eaa
    		    sformat != SND_PCM_FORMAT_S24_3LE && 
    Packit Service db8eaa
    		    sformat != SND_PCM_FORMAT_S24_LE &&
    Packit Service db8eaa
    		    sformat != SND_PCM_FORMAT_S32_LE &&
    Packit Service db8eaa
    		    sformat != SND_PCM_FORMAT_S32_BE) {
    Packit Service db8eaa
    			SNDERR("only S16_LE, S16_BE, S24_LE, S24_3LE, S32_LE or S32_BE format is supported");
    Packit Service db8eaa
    			snd_config_delete(sconf);
    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_parse_control_id(control, &ctl_id, &card,
    Packit Service db8eaa
    					       &cchannels, NULL);
    Packit Service db8eaa
    		if (err < 0) {
    Packit Service db8eaa
    			snd_pcm_close(spcm);
    Packit Service db8eaa
    			return err;
    Packit Service db8eaa
    		}
    Packit Service db8eaa
    		err = snd_pcm_softvol_open(pcmp, name, sformat, card, &ctl_id,
    Packit Service db8eaa
    					   cchannels, min_dB, max_dB,
    Packit Service db8eaa
    					   resolution, spcm, 1);
    Packit Service db8eaa
    		if (err < 0)
    Packit Service db8eaa
    			snd_pcm_close(spcm);
    Packit Service db8eaa
    	}
    Packit Service db8eaa
    	return err;
    Packit Service db8eaa
    }
    Packit Service db8eaa
    #ifndef DOC_HIDDEN
    Packit Service db8eaa
    SND_DLSYM_BUILD_VERSION(_snd_pcm_softvol_open, SND_PCM_DLSYM_VERSION);
    Packit Service db8eaa
    #endif