Home | History | Annotate | Download | only in pcm
      1 /**
      2  * \file pcm/pcm_softvol.c
      3  * \ingroup PCM_Plugins
      4  * \brief PCM Soft Volume Plugin Interface
      5  * \author Takashi Iwai <tiwai (at) suse.de>
      6  * \date 2004
      7  */
      8 /*
      9  *  PCM - Soft Volume Plugin
     10  *  Copyright (c) 2004 by Takashi Iwai <tiwai (at) suse.de>
     11  *
     12  *
     13  *   This library is free software; you can redistribute it and/or modify
     14  *   it under the terms of the GNU Lesser General Public License as
     15  *   published by the Free Software Foundation; either version 2.1 of
     16  *   the License, or (at your option) any later version.
     17  *
     18  *   This program is distributed in the hope that it will be useful,
     19  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
     20  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     21  *   GNU Lesser General Public License for more details.
     22  *
     23  *   You should have received a copy of the GNU Lesser General Public
     24  *   License along with this library; if not, write to the Free Software
     25  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
     26  *
     27  */
     28 
     29 #include <byteswap.h>
     30 #include <math.h>
     31 #include "pcm_local.h"
     32 #include "pcm_plugin.h"
     33 
     34 #ifndef PIC
     35 /* entry for static linking */
     36 const char *_snd_module_pcm_softvol = "";
     37 #endif
     38 
     39 #ifndef DOC_HIDDEN
     40 
     41 typedef struct {
     42 	/* This field need to be the first */
     43 	snd_pcm_plugin_t plug;
     44 	snd_pcm_format_t sformat;
     45 	unsigned int cchannels;
     46 	snd_ctl_t *ctl;
     47 	snd_ctl_elem_value_t elem;
     48 	unsigned int cur_vol[2];
     49 	unsigned int max_val;     /* max index */
     50 	unsigned int zero_dB_val; /* index at 0 dB */
     51 	double min_dB;
     52 	double max_dB;
     53 	unsigned int *dB_value;
     54 } snd_pcm_softvol_t;
     55 
     56 #define VOL_SCALE_SHIFT		16
     57 #define VOL_SCALE_MASK          ((1 << VOL_SCALE_SHIFT) - 1)
     58 
     59 #define PRESET_RESOLUTION	256
     60 #define PRESET_MIN_DB		-51.0
     61 #define ZERO_DB                  0.0
     62 #define MAX_DB_UPPER_LIMIT      50
     63 
     64 static const unsigned int preset_dB_value[PRESET_RESOLUTION] = {
     65 	0x00b8, 0x00bd, 0x00c1, 0x00c5, 0x00ca, 0x00cf, 0x00d4, 0x00d9,
     66 	0x00de, 0x00e3, 0x00e8, 0x00ed, 0x00f3, 0x00f9, 0x00fe, 0x0104,
     67 	0x010a, 0x0111, 0x0117, 0x011e, 0x0124, 0x012b, 0x0132, 0x0139,
     68 	0x0140, 0x0148, 0x0150, 0x0157, 0x015f, 0x0168, 0x0170, 0x0179,
     69 	0x0181, 0x018a, 0x0194, 0x019d, 0x01a7, 0x01b0, 0x01bb, 0x01c5,
     70 	0x01cf, 0x01da, 0x01e5, 0x01f1, 0x01fc, 0x0208, 0x0214, 0x0221,
     71 	0x022d, 0x023a, 0x0248, 0x0255, 0x0263, 0x0271, 0x0280, 0x028f,
     72 	0x029e, 0x02ae, 0x02be, 0x02ce, 0x02df, 0x02f0, 0x0301, 0x0313,
     73 	0x0326, 0x0339, 0x034c, 0x035f, 0x0374, 0x0388, 0x039d, 0x03b3,
     74 	0x03c9, 0x03df, 0x03f7, 0x040e, 0x0426, 0x043f, 0x0458, 0x0472,
     75 	0x048d, 0x04a8, 0x04c4, 0x04e0, 0x04fd, 0x051b, 0x053a, 0x0559,
     76 	0x0579, 0x0599, 0x05bb, 0x05dd, 0x0600, 0x0624, 0x0648, 0x066e,
     77 	0x0694, 0x06bb, 0x06e3, 0x070c, 0x0737, 0x0762, 0x078e, 0x07bb,
     78 	0x07e9, 0x0818, 0x0848, 0x087a, 0x08ac, 0x08e0, 0x0915, 0x094b,
     79 	0x0982, 0x09bb, 0x09f5, 0x0a30, 0x0a6d, 0x0aab, 0x0aeb, 0x0b2c,
     80 	0x0b6f, 0x0bb3, 0x0bf9, 0x0c40, 0x0c89, 0x0cd4, 0x0d21, 0x0d6f,
     81 	0x0dbf, 0x0e11, 0x0e65, 0x0ebb, 0x0f12, 0x0f6c, 0x0fc8, 0x1026,
     82 	0x1087, 0x10e9, 0x114e, 0x11b5, 0x121f, 0x128b, 0x12fa, 0x136b,
     83 	0x13df, 0x1455, 0x14ce, 0x154a, 0x15c9, 0x164b, 0x16d0, 0x1758,
     84 	0x17e4, 0x1872, 0x1904, 0x1999, 0x1a32, 0x1ace, 0x1b6e, 0x1c11,
     85 	0x1cb9, 0x1d64, 0x1e13, 0x1ec7, 0x1f7e, 0x203a, 0x20fa, 0x21bf,
     86 	0x2288, 0x2356, 0x2429, 0x2500, 0x25dd, 0x26bf, 0x27a6, 0x2892,
     87 	0x2984, 0x2a7c, 0x2b79, 0x2c7c, 0x2d85, 0x2e95, 0x2fab, 0x30c7,
     88 	0x31ea, 0x3313, 0x3444, 0x357c, 0x36bb, 0x3801, 0x394f, 0x3aa5,
     89 	0x3c02, 0x3d68, 0x3ed6, 0x404d, 0x41cd, 0x4355, 0x44e6, 0x4681,
     90 	0x4826, 0x49d4, 0x4b8c, 0x4d4f, 0x4f1c, 0x50f3, 0x52d6, 0x54c4,
     91 	0x56be, 0x58c3, 0x5ad4, 0x5cf2, 0x5f1c, 0x6153, 0x6398, 0x65e9,
     92 	0x6849, 0x6ab7, 0x6d33, 0x6fbf, 0x7259, 0x7503, 0x77bd, 0x7a87,
     93 	0x7d61, 0x804d, 0x834a, 0x8659, 0x897a, 0x8cae, 0x8ff5, 0x934f,
     94 	0x96bd, 0x9a40, 0x9dd8, 0xa185, 0xa548, 0xa922, 0xad13, 0xb11b,
     95 	0xb53b, 0xb973, 0xbdc5, 0xc231, 0xc6b7, 0xcb58, 0xd014, 0xd4ed,
     96 	0xd9e3, 0xdef6, 0xe428, 0xe978, 0xeee8, 0xf479, 0xfa2b, 0xffff,
     97 };
     98 
     99 /* (32bit x 16bit) >> 16 */
    100 typedef union {
    101 	int i;
    102 	short s[2];
    103 } val_t;
    104 static inline int MULTI_DIV_32x16(int a, unsigned short b)
    105 {
    106 	val_t v, x, y;
    107 	v.i = a;
    108 	y.i = 0;
    109 #if __BYTE_ORDER == __LITTLE_ENDIAN
    110 	x.i = (unsigned int)v.s[0] * b;
    111 	y.s[0] = x.s[1];
    112 	y.i += (int)v.s[1] * b;
    113 #else
    114 	x.i = (unsigned int)v.s[1] * b;
    115 	y.s[1] = x.s[0];
    116 	y.i += (int)v.s[0] * b;
    117 #endif
    118 	return y.i;
    119 }
    120 
    121 static inline int MULTI_DIV_int(int a, unsigned int b, int swap)
    122 {
    123 	unsigned int gain = (b >> VOL_SCALE_SHIFT);
    124 	int fraction;
    125 	a = swap ? (int)bswap_32(a) : a;
    126 	fraction = MULTI_DIV_32x16(a, b & VOL_SCALE_MASK);
    127 	if (gain) {
    128 		long long amp = (long long)a * gain + fraction;
    129 		if (amp > (int)0x7fffffff)
    130 			amp = (int)0x7fffffff;
    131 		else if (amp < (int)0x80000000)
    132 			amp = (int)0x80000000;
    133 		return swap ? (int)bswap_32((int)amp) : (int)amp;
    134 	}
    135 	return swap ? (int)bswap_32(fraction) : fraction;
    136 }
    137 
    138 static inline short MULTI_DIV_short(short a, unsigned int b, int swap)
    139 {
    140 	unsigned int gain = b >> VOL_SCALE_SHIFT;
    141 	int fraction;
    142 	a = swap ? (short)bswap_16(a) : a;
    143 	fraction = (int)(a * (b & VOL_SCALE_MASK)) >> VOL_SCALE_SHIFT;
    144 	if (gain) {
    145 		int amp = a * gain + fraction;
    146 		if (abs(amp) > 0x7fff)
    147 			amp = (a<0) ? (short)0x8000 : (short)0x7fff;
    148 		return swap ? (short)bswap_16((short)amp) : (short)amp;
    149 	}
    150 	return swap ? (short)bswap_16((short)fraction) : (short)fraction;
    151 }
    152 
    153 #endif /* DOC_HIDDEN */
    154 
    155 /*
    156  * apply volumue attenuation
    157  *
    158  * TODO: use SIMD operations
    159  */
    160 
    161 #ifndef DOC_HIDDEN
    162 #define CONVERT_AREA(TYPE, swap) do {	\
    163 	unsigned int ch, fr; \
    164 	TYPE *src, *dst; \
    165 	for (ch = 0; ch < channels; ch++) { \
    166 		src_area = &src_areas[ch]; \
    167 		dst_area = &dst_areas[ch]; \
    168 		src = snd_pcm_channel_area_addr(src_area, src_offset); \
    169 		dst = snd_pcm_channel_area_addr(dst_area, dst_offset); \
    170 		src_step = snd_pcm_channel_area_step(src_area) / sizeof(TYPE); \
    171 		dst_step = snd_pcm_channel_area_step(dst_area) / sizeof(TYPE); \
    172 		GET_VOL_SCALE; \
    173 		fr = frames; \
    174 		if (! vol_scale) { \
    175 			while (fr--) { \
    176 				*dst = 0; \
    177 				dst += dst_step; \
    178 			} \
    179 		} else if (vol_scale == 0xffff) { \
    180 			while (fr--) { \
    181 				*dst = *src; \
    182 				src += src_step; \
    183 				dst += dst_step; \
    184 			} \
    185 		} else { \
    186 			while (fr--) { \
    187 				*dst = (TYPE) MULTI_DIV_##TYPE(*src, vol_scale, swap); \
    188 				src += src_step; \
    189 				dst += dst_step; \
    190 			} \
    191 		} \
    192 	} \
    193 } while (0)
    194 
    195 #define CONVERT_AREA_S24_3LE() do {					\
    196 	unsigned int ch, fr;						\
    197 	unsigned char *src, *dst;					\
    198 	int tmp;							\
    199 	for (ch = 0; ch < channels; ch++) {				\
    200 		src_area = &src_areas[ch];				\
    201 		dst_area = &dst_areas[ch];				\
    202 		src = snd_pcm_channel_area_addr(src_area, src_offset);	\
    203 		dst = snd_pcm_channel_area_addr(dst_area, dst_offset);	\
    204 		src_step = snd_pcm_channel_area_step(src_area);		\
    205 		dst_step = snd_pcm_channel_area_step(dst_area);		\
    206 		GET_VOL_SCALE;						\
    207 		fr = frames;						\
    208 		if (! vol_scale) {					\
    209 			while (fr--) {					\
    210 				dst[0] = dst[1] = dst[2] = 0;		\
    211 				dst += dst_step;			\
    212 			}						\
    213 		} else if (vol_scale == 0xffff) {			\
    214 			while (fr--) {					\
    215 				dst[0] = src[0];			\
    216 				dst[1] = src[1];			\
    217 				dst[2] = src[2];			\
    218 				src += dst_step;			\
    219 				dst += src_step;			\
    220 			}						\
    221 		} else {						\
    222 			while (fr--) {					\
    223 				tmp = src[0] |				\
    224 				      (src[1] << 8) |			\
    225 				      (((signed char *) src)[2] << 16);	\
    226 				tmp = MULTI_DIV_int(tmp, vol_scale, 0);	\
    227 				dst[0] = tmp;				\
    228 				dst[1] = tmp >> 8;			\
    229 				dst[2] = tmp >> 16;			\
    230 				src += dst_step;			\
    231 				dst += src_step;			\
    232 			}						\
    233 		}							\
    234 	}								\
    235 } while (0)
    236 
    237 #define GET_VOL_SCALE \
    238 	switch (ch) { \
    239 	case 0: \
    240 	case 2: \
    241 		vol_scale = (channels == ch + 1) ? vol_c : vol[0]; \
    242 		break; \
    243 	case 4: \
    244 	case 5: \
    245 		vol_scale = vol_c; \
    246 		break; \
    247 	default: \
    248 		vol_scale = vol[ch & 1]; \
    249 		break; \
    250 	}
    251 
    252 #endif /* DOC_HIDDEN */
    253 
    254 /* 2-channel stereo control */
    255 static void softvol_convert_stereo_vol(snd_pcm_softvol_t *svol,
    256 				       const snd_pcm_channel_area_t *dst_areas,
    257 				       snd_pcm_uframes_t dst_offset,
    258 				       const snd_pcm_channel_area_t *src_areas,
    259 				       snd_pcm_uframes_t src_offset,
    260 				       unsigned int channels,
    261 				       snd_pcm_uframes_t frames)
    262 {
    263 	const snd_pcm_channel_area_t *dst_area, *src_area;
    264 	unsigned int src_step, dst_step;
    265 	unsigned int vol_scale, vol[2], vol_c;
    266 
    267 	if (svol->cur_vol[0] == 0 && svol->cur_vol[1] == 0) {
    268 		snd_pcm_areas_silence(dst_areas, dst_offset, channels, frames,
    269 				      svol->sformat);
    270 		return;
    271 	} else if (svol->zero_dB_val && svol->cur_vol[0] == svol->zero_dB_val &&
    272 		   svol->cur_vol[1] == svol->zero_dB_val) {
    273 		snd_pcm_areas_copy(dst_areas, dst_offset, src_areas, src_offset,
    274 				   channels, frames, svol->sformat);
    275 		return;
    276 	}
    277 
    278 	if (svol->max_val == 1) {
    279 		vol[0] = svol->cur_vol[0] ? 0xffff : 0;
    280 		vol[1] = svol->cur_vol[1] ? 0xffff : 0;
    281 		vol_c = vol[0] | vol[1];
    282 	} else {
    283 		vol[0] = svol->dB_value[svol->cur_vol[0]];
    284 		vol[1] = svol->dB_value[svol->cur_vol[1]];
    285 		vol_c = svol->dB_value[(svol->cur_vol[0] + svol->cur_vol[1]) / 2];
    286 	}
    287 	switch (svol->sformat) {
    288 	case SND_PCM_FORMAT_S16_LE:
    289 	case SND_PCM_FORMAT_S16_BE:
    290 		/* 16bit samples */
    291 		CONVERT_AREA(short,
    292 			     !snd_pcm_format_cpu_endian(svol->sformat));
    293 		break;
    294 	case SND_PCM_FORMAT_S32_LE:
    295 	case SND_PCM_FORMAT_S32_BE:
    296 		/* 32bit samples */
    297 		CONVERT_AREA(int,
    298 			     !snd_pcm_format_cpu_endian(svol->sformat));
    299 		break;
    300 	case SND_PCM_FORMAT_S24_3LE:
    301 		CONVERT_AREA_S24_3LE();
    302 		break;
    303 	default:
    304 		break;
    305 	}
    306 }
    307 
    308 #undef GET_VOL_SCALE
    309 #define GET_VOL_SCALE
    310 
    311 /* mono control */
    312 static void softvol_convert_mono_vol(snd_pcm_softvol_t *svol,
    313 				     const snd_pcm_channel_area_t *dst_areas,
    314 				     snd_pcm_uframes_t dst_offset,
    315 				     const snd_pcm_channel_area_t *src_areas,
    316 				     snd_pcm_uframes_t src_offset,
    317 				     unsigned int channels,
    318 				     snd_pcm_uframes_t frames)
    319 {
    320 	const snd_pcm_channel_area_t *dst_area, *src_area;
    321 	unsigned int src_step, dst_step;
    322 	unsigned int vol_scale;
    323 
    324 	if (svol->cur_vol[0] == 0) {
    325 		snd_pcm_areas_silence(dst_areas, dst_offset, channels, frames,
    326 				      svol->sformat);
    327 		return;
    328 	} else if (svol->zero_dB_val && svol->cur_vol[0] == svol->zero_dB_val) {
    329 		snd_pcm_areas_copy(dst_areas, dst_offset, src_areas, src_offset,
    330 				   channels, frames, svol->sformat);
    331 		return;
    332 	}
    333 
    334 	if (svol->max_val == 1)
    335 		vol_scale = svol->cur_vol[0] ? 0xffff : 0;
    336 	else
    337 		vol_scale = svol->dB_value[svol->cur_vol[0]];
    338 	switch (svol->sformat) {
    339 	case SND_PCM_FORMAT_S16_LE:
    340 	case SND_PCM_FORMAT_S16_BE:
    341 		/* 16bit samples */
    342 		CONVERT_AREA(short,
    343 			     !snd_pcm_format_cpu_endian(svol->sformat));
    344 		break;
    345 	case SND_PCM_FORMAT_S32_LE:
    346 	case SND_PCM_FORMAT_S32_BE:
    347 		/* 32bit samples */
    348 		CONVERT_AREA(int,
    349 			     !snd_pcm_format_cpu_endian(svol->sformat));
    350 		break;
    351 	case SND_PCM_FORMAT_S24_3LE:
    352 		CONVERT_AREA_S24_3LE();
    353 		break;
    354 	default:
    355 		break;
    356 	}
    357 }
    358 
    359 /*
    360  * get the current volume value from driver
    361  *
    362  * TODO: mmap support?
    363  */
    364 static void get_current_volume(snd_pcm_softvol_t *svol)
    365 {
    366 	unsigned int val;
    367 	unsigned int i;
    368 
    369 	if (snd_ctl_elem_read(svol->ctl, &svol->elem) < 0)
    370 		return;
    371 	for (i = 0; i < svol->cchannels; i++) {
    372 		val = svol->elem.value.integer.value[i];
    373 		if (val > svol->max_val)
    374 			val = svol->max_val;
    375 		svol->cur_vol[i] = val;
    376 	}
    377 }
    378 
    379 static void softvol_free(snd_pcm_softvol_t *svol)
    380 {
    381 	if (svol->plug.gen.close_slave)
    382 		snd_pcm_close(svol->plug.gen.slave);
    383 	if (svol->ctl)
    384 		snd_ctl_close(svol->ctl);
    385 	if (svol->dB_value && svol->dB_value != preset_dB_value)
    386 		free(svol->dB_value);
    387 	free(svol);
    388 }
    389 
    390 static int snd_pcm_softvol_close(snd_pcm_t *pcm)
    391 {
    392 	snd_pcm_softvol_t *svol = pcm->private_data;
    393 	softvol_free(svol);
    394 	return 0;
    395 }
    396 
    397 static int snd_pcm_softvol_hw_refine_cprepare(snd_pcm_t *pcm,
    398 					      snd_pcm_hw_params_t *params)
    399 {
    400 	int err;
    401 	snd_pcm_softvol_t *svol = pcm->private_data;
    402 	snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM };
    403 	snd_pcm_format_mask_t format_mask = {
    404 		{
    405 			(1ULL << SND_PCM_FORMAT_S16_LE) |
    406 			(1ULL << SND_PCM_FORMAT_S16_BE) |
    407 			(1ULL << SND_PCM_FORMAT_S32_LE) |
    408  			(1ULL << SND_PCM_FORMAT_S32_BE),
    409 			(1ULL << (SND_PCM_FORMAT_S24_3LE - 32))
    410 		}
    411 	};
    412 	if (svol->sformat != SND_PCM_FORMAT_UNKNOWN) {
    413 		snd_pcm_format_mask_none(&format_mask);
    414 		snd_pcm_format_mask_set(&format_mask, svol->sformat);
    415 	}
    416 	err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
    417 					 &access_mask);
    418 	if (err < 0)
    419 		return err;
    420 	err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT,
    421 					 &format_mask);
    422 	if (err < 0)
    423 		return err;
    424 	err = _snd_pcm_hw_params_set_subformat(params, SND_PCM_SUBFORMAT_STD);
    425 	if (err < 0)
    426 		return err;
    427 	err = _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_CHANNELS, 1, 0);
    428 	if (err < 0)
    429 		return err;
    430 	params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
    431 	return 0;
    432 }
    433 
    434 static int snd_pcm_softvol_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
    435 {
    436 	snd_pcm_softvol_t *svol = pcm->private_data;
    437 	snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
    438 	_snd_pcm_hw_params_any(sparams);
    439 	_snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
    440 				   &saccess_mask);
    441 	if (svol->sformat != SND_PCM_FORMAT_UNKNOWN) {
    442 		_snd_pcm_hw_params_set_format(sparams, svol->sformat);
    443 		_snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
    444 	}
    445 	return 0;
    446 }
    447 
    448 /*
    449  * refine the access mask
    450  */
    451 static int check_access_mask(snd_pcm_hw_params_t *src,
    452 			     snd_pcm_hw_params_t *dst)
    453 {
    454 	const snd_pcm_access_mask_t *mask;
    455 	snd_pcm_access_mask_t smask;
    456 
    457 	mask = snd_pcm_hw_param_get_mask(src, SND_PCM_HW_PARAM_ACCESS);
    458 	snd_mask_none(&smask);
    459 	if (snd_pcm_access_mask_test(mask, SND_PCM_ACCESS_RW_INTERLEAVED) ||
    460 	    snd_pcm_access_mask_test(mask, SND_PCM_ACCESS_MMAP_INTERLEAVED)) {
    461 		snd_pcm_access_mask_set(&smask,
    462 					SND_PCM_ACCESS_RW_INTERLEAVED);
    463 		snd_pcm_access_mask_set(&smask,
    464 					SND_PCM_ACCESS_MMAP_INTERLEAVED);
    465 	}
    466 	if (snd_pcm_access_mask_test(mask, SND_PCM_ACCESS_RW_NONINTERLEAVED) ||
    467 	    snd_pcm_access_mask_test(mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED))  {
    468 		snd_pcm_access_mask_set(&smask,
    469 					SND_PCM_ACCESS_RW_NONINTERLEAVED);
    470 		snd_pcm_access_mask_set(&smask,
    471 					SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
    472 	}
    473 	if (snd_pcm_access_mask_test(mask, SND_PCM_ACCESS_MMAP_COMPLEX))
    474 		snd_pcm_access_mask_set(&smask,
    475 					SND_PCM_ACCESS_MMAP_COMPLEX);
    476 
    477 	return _snd_pcm_hw_param_set_mask(dst, SND_PCM_HW_PARAM_ACCESS, &smask);
    478 }
    479 
    480 static int snd_pcm_softvol_hw_refine_schange(snd_pcm_t *pcm,
    481 					     snd_pcm_hw_params_t *params,
    482 					     snd_pcm_hw_params_t *sparams)
    483 {
    484 	snd_pcm_softvol_t *svol = pcm->private_data;
    485 	int err;
    486 	unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
    487 			      SND_PCM_HW_PARBIT_RATE |
    488 			      SND_PCM_HW_PARBIT_PERIODS |
    489 			      SND_PCM_HW_PARBIT_PERIOD_SIZE |
    490 			      SND_PCM_HW_PARBIT_PERIOD_TIME |
    491 			      SND_PCM_HW_PARBIT_BUFFER_SIZE |
    492 			      SND_PCM_HW_PARBIT_BUFFER_TIME |
    493 			      SND_PCM_HW_PARBIT_TICK_TIME);
    494 	if (svol->sformat == SND_PCM_FORMAT_UNKNOWN)
    495 		links |= (SND_PCM_HW_PARBIT_FORMAT |
    496 			  SND_PCM_HW_PARBIT_SUBFORMAT |
    497 			  SND_PCM_HW_PARBIT_SAMPLE_BITS);
    498 	err = _snd_pcm_hw_params_refine(sparams, links, params);
    499 	if (err < 0)
    500 		return err;
    501 
    502 	err = check_access_mask(params, sparams);
    503 	if (err < 0)
    504 		return err;
    505 
    506 	return 0;
    507 }
    508 
    509 static int snd_pcm_softvol_hw_refine_cchange(snd_pcm_t *pcm,
    510 					     snd_pcm_hw_params_t *params,
    511 					    snd_pcm_hw_params_t *sparams)
    512 {
    513 	snd_pcm_softvol_t *svol = pcm->private_data;
    514 	int err;
    515 	unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
    516 			      SND_PCM_HW_PARBIT_RATE |
    517 			      SND_PCM_HW_PARBIT_PERIODS |
    518 			      SND_PCM_HW_PARBIT_PERIOD_SIZE |
    519 			      SND_PCM_HW_PARBIT_PERIOD_TIME |
    520 			      SND_PCM_HW_PARBIT_BUFFER_SIZE |
    521 			      SND_PCM_HW_PARBIT_BUFFER_TIME |
    522 			      SND_PCM_HW_PARBIT_TICK_TIME);
    523 	if (svol->sformat == SND_PCM_FORMAT_UNKNOWN)
    524 		links |= (SND_PCM_HW_PARBIT_FORMAT |
    525 			  SND_PCM_HW_PARBIT_SUBFORMAT |
    526 			  SND_PCM_HW_PARBIT_SAMPLE_BITS);
    527 	err = _snd_pcm_hw_params_refine(params, links, sparams);
    528 	if (err < 0)
    529 		return err;
    530 
    531 	err = check_access_mask(sparams, params);
    532 	if (err < 0)
    533 		return err;
    534 
    535 	return 0;
    536 }
    537 
    538 static int snd_pcm_softvol_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
    539 {
    540 	return snd_pcm_hw_refine_slave(pcm, params,
    541 				       snd_pcm_softvol_hw_refine_cprepare,
    542 				       snd_pcm_softvol_hw_refine_cchange,
    543 				       snd_pcm_softvol_hw_refine_sprepare,
    544 				       snd_pcm_softvol_hw_refine_schange,
    545 				       snd_pcm_generic_hw_refine);
    546 }
    547 
    548 static int snd_pcm_softvol_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
    549 {
    550 	snd_pcm_softvol_t *svol = pcm->private_data;
    551 	snd_pcm_t *slave = svol->plug.gen.slave;
    552 	int err = snd_pcm_hw_params_slave(pcm, params,
    553 					  snd_pcm_softvol_hw_refine_cchange,
    554 					  snd_pcm_softvol_hw_refine_sprepare,
    555 					  snd_pcm_softvol_hw_refine_schange,
    556 					  snd_pcm_generic_hw_params);
    557 	if (err < 0)
    558 		return err;
    559 	if (slave->format != SND_PCM_FORMAT_S16_LE &&
    560 	    slave->format != SND_PCM_FORMAT_S16_BE &&
    561 	    slave->format != SND_PCM_FORMAT_S24_3LE &&
    562 	    slave->format != SND_PCM_FORMAT_S32_LE &&
    563 	    slave->format != SND_PCM_FORMAT_S32_BE) {
    564 		SNDERR("softvol supports only S16_LE, S16_BE, S24_3LE, S32_LE "
    565 		       " or S32_BE");
    566 		return -EINVAL;
    567 	}
    568 	svol->sformat = slave->format;
    569 	return 0;
    570 }
    571 
    572 static snd_pcm_uframes_t
    573 snd_pcm_softvol_write_areas(snd_pcm_t *pcm,
    574 			    const snd_pcm_channel_area_t *areas,
    575 			    snd_pcm_uframes_t offset,
    576 			    snd_pcm_uframes_t size,
    577 			    const snd_pcm_channel_area_t *slave_areas,
    578 			    snd_pcm_uframes_t slave_offset,
    579 			    snd_pcm_uframes_t *slave_sizep)
    580 {
    581 	snd_pcm_softvol_t *svol = pcm->private_data;
    582 	if (size > *slave_sizep)
    583 		size = *slave_sizep;
    584 	get_current_volume(svol);
    585 	if (svol->cchannels == 1)
    586 		softvol_convert_mono_vol(svol, slave_areas, slave_offset,
    587 					 areas, offset, pcm->channels, size);
    588 	else
    589 		softvol_convert_stereo_vol(svol, slave_areas, slave_offset,
    590 					   areas, offset, pcm->channels, size);
    591 	*slave_sizep = size;
    592 	return size;
    593 }
    594 
    595 static snd_pcm_uframes_t
    596 snd_pcm_softvol_read_areas(snd_pcm_t *pcm,
    597 			   const snd_pcm_channel_area_t *areas,
    598 			   snd_pcm_uframes_t offset,
    599 			   snd_pcm_uframes_t size,
    600 			   const snd_pcm_channel_area_t *slave_areas,
    601 			   snd_pcm_uframes_t slave_offset,
    602 			   snd_pcm_uframes_t *slave_sizep)
    603 {
    604 	snd_pcm_softvol_t *svol = pcm->private_data;
    605 	if (size > *slave_sizep)
    606 		size = *slave_sizep;
    607 	get_current_volume(svol);
    608 	if (svol->cchannels == 1)
    609 		softvol_convert_mono_vol(svol, areas, offset, slave_areas,
    610 					 slave_offset, pcm->channels, size);
    611 	else
    612 		softvol_convert_stereo_vol(svol, areas, offset, slave_areas,
    613 					   slave_offset, pcm->channels, size);
    614 	*slave_sizep = size;
    615 	return size;
    616 }
    617 
    618 static void snd_pcm_softvol_dump(snd_pcm_t *pcm, snd_output_t *out)
    619 {
    620 	snd_pcm_softvol_t *svol = pcm->private_data;
    621 	snd_output_printf(out, "Soft volume PCM\n");
    622 	snd_output_printf(out, "Control: %s\n", svol->elem.id.name);
    623 	if (svol->max_val == 1)
    624 		snd_output_printf(out, "boolean\n");
    625 	else {
    626 		snd_output_printf(out, "min_dB: %g\n", svol->min_dB);
    627 		snd_output_printf(out, "max_dB: %g\n", svol->max_dB);
    628 		snd_output_printf(out, "resolution: %d\n", svol->max_val + 1);
    629 	}
    630 	if (pcm->setup) {
    631 		snd_output_printf(out, "Its setup is:\n");
    632 		snd_pcm_dump_setup(pcm, out);
    633 	}
    634 	snd_output_printf(out, "Slave: ");
    635 	snd_pcm_dump(svol->plug.gen.slave, out);
    636 }
    637 
    638 static int add_tlv_info(snd_pcm_softvol_t *svol, snd_ctl_elem_info_t *cinfo)
    639 {
    640 	unsigned int tlv[4];
    641 	tlv[0] = SND_CTL_TLVT_DB_SCALE;
    642 	tlv[1] = 2 * sizeof(int);
    643 	tlv[2] = svol->min_dB * 100;
    644 	tlv[3] = (svol->max_dB - svol->min_dB) * 100 / svol->max_val;
    645 	return snd_ctl_elem_tlv_write(svol->ctl, &cinfo->id, tlv);
    646 }
    647 
    648 static int add_user_ctl(snd_pcm_softvol_t *svol, snd_ctl_elem_info_t *cinfo, int count)
    649 {
    650 	int err;
    651 	int i;
    652 	unsigned int def_val;
    653 
    654 	if (svol->max_val == 1)
    655 		err = snd_ctl_elem_add_boolean(svol->ctl, &cinfo->id, count);
    656 	else
    657 		err = snd_ctl_elem_add_integer(svol->ctl, &cinfo->id, count,
    658 					       0, svol->max_val, 0);
    659 	if (err < 0)
    660 		return err;
    661 	if (svol->max_val == 1)
    662 		def_val = 1;
    663 	else {
    664 		add_tlv_info(svol, cinfo);
    665 		/* set zero dB value as default, or max_val if
    666 		   there is no 0 dB setting */
    667 		def_val = svol->zero_dB_val ? svol->zero_dB_val : svol->max_val;
    668 	}
    669 	for (i = 0; i < count; i++)
    670 		svol->elem.value.integer.value[i] = def_val;
    671 	return snd_ctl_elem_write(svol->ctl, &svol->elem);
    672 }
    673 
    674 /*
    675  * load and set up user-control
    676  * returns 0 if the user-control is found or created,
    677  * returns 1 if the control is a hw control,
    678  * or a negative error code
    679  */
    680 static int softvol_load_control(snd_pcm_t *pcm, snd_pcm_softvol_t *svol,
    681 				int ctl_card, snd_ctl_elem_id_t *ctl_id,
    682 				int cchannels, double min_dB, double max_dB,
    683 				int resolution)
    684 {
    685 	char tmp_name[32];
    686 	snd_pcm_info_t *info;
    687 	snd_ctl_elem_info_t *cinfo;
    688 	int err;
    689 	unsigned int i;
    690 
    691 	if (ctl_card < 0) {
    692 		snd_pcm_info_alloca(&info);
    693 		err = snd_pcm_info(pcm, info);
    694 		if (err < 0)
    695 			return err;
    696 		ctl_card = snd_pcm_info_get_card(info);
    697 		if (ctl_card < 0) {
    698 			SNDERR("No card defined for softvol control");
    699 			return -EINVAL;
    700 		}
    701 	}
    702 	sprintf(tmp_name, "hw:%d", ctl_card);
    703 	err = snd_ctl_open(&svol->ctl, tmp_name, 0);
    704 	if (err < 0) {
    705 		SNDERR("Cannot open CTL %s", tmp_name);
    706 		return err;
    707 	}
    708 
    709 	svol->elem.id = *ctl_id;
    710 	svol->max_val = resolution - 1;
    711 	svol->min_dB = min_dB;
    712 	svol->max_dB = max_dB;
    713 	if (svol->max_val == 1 || svol->max_dB == ZERO_DB)
    714 		svol->zero_dB_val = svol->max_val;
    715 	else if (svol->max_dB < 0)
    716 		svol->zero_dB_val = 0; /* there is no 0 dB setting */
    717 	else
    718 		svol->zero_dB_val = (min_dB / (min_dB - max_dB)) * svol->max_val;
    719 
    720 	snd_ctl_elem_info_alloca(&cinfo);
    721 	snd_ctl_elem_info_set_id(cinfo, ctl_id);
    722 	if ((err = snd_ctl_elem_info(svol->ctl, cinfo)) < 0) {
    723 		if (err != -ENOENT) {
    724 			SNDERR("Cannot get info for CTL %s", tmp_name);
    725 			return err;
    726 		}
    727 		err = add_user_ctl(svol, cinfo, cchannels);
    728 		if (err < 0) {
    729 			SNDERR("Cannot add a control");
    730 			return err;
    731 		}
    732 	} else {
    733 		if (! (cinfo->access & SNDRV_CTL_ELEM_ACCESS_USER)) {
    734 			/* hardware control exists */
    735 			return 1; /* notify */
    736 
    737 		} else if ((cinfo->type != SND_CTL_ELEM_TYPE_INTEGER &&
    738 			    cinfo->type != SND_CTL_ELEM_TYPE_BOOLEAN) ||
    739 			   cinfo->count != (unsigned int)cchannels ||
    740 			   cinfo->value.integer.min != 0 ||
    741 			   cinfo->value.integer.max != resolution - 1) {
    742 			if ((err = snd_ctl_elem_remove(svol->ctl, &cinfo->id)) < 0) {
    743 				SNDERR("Control %s mismatch", tmp_name);
    744 				return err;
    745 			}
    746 			snd_ctl_elem_info_set_id(cinfo, ctl_id); /* reset numid */
    747 			if ((err = add_user_ctl(svol, cinfo, cchannels)) < 0) {
    748 				SNDERR("Cannot add a control");
    749 				return err;
    750 			}
    751 		} else if (svol->max_val > 1) {
    752 			/* check TLV availability */
    753 			unsigned int tlv[4];
    754 			err = snd_ctl_elem_tlv_read(svol->ctl, &cinfo->id, tlv, sizeof(tlv));
    755 			if (err < 0)
    756 				add_tlv_info(svol, cinfo);
    757 		}
    758 	}
    759 
    760 	if (svol->max_val == 1)
    761 		return 0;
    762 
    763 	/* set up dB table */
    764 	if (min_dB == PRESET_MIN_DB && max_dB == ZERO_DB && resolution == PRESET_RESOLUTION)
    765 		svol->dB_value = (unsigned int*)preset_dB_value;
    766 	else {
    767 #ifndef HAVE_SOFT_FLOAT
    768 		svol->dB_value = calloc(resolution, sizeof(unsigned int));
    769 		if (! svol->dB_value) {
    770 			SNDERR("cannot allocate dB table");
    771 			return -ENOMEM;
    772 		}
    773 		svol->min_dB = min_dB;
    774 		svol->max_dB = max_dB;
    775 		for (i = 0; i <= svol->max_val; i++) {
    776 			double db = svol->min_dB + (i * (svol->max_dB - svol->min_dB)) / svol->max_val;
    777 			double v = (pow(10.0, db / 20.0) * (double)(1 << VOL_SCALE_SHIFT));
    778 			svol->dB_value[i] = (unsigned int)v;
    779 		}
    780 		if (svol->zero_dB_val)
    781 			svol->dB_value[svol->zero_dB_val] = 65535;
    782 #else
    783 		SNDERR("Cannot handle the given dB range and resolution");
    784 		return -EINVAL;
    785 #endif
    786 	}
    787 	return 0;
    788 }
    789 
    790 static const snd_pcm_ops_t snd_pcm_softvol_ops = {
    791 	.close = snd_pcm_softvol_close,
    792 	.info = snd_pcm_generic_info,
    793 	.hw_refine = snd_pcm_softvol_hw_refine,
    794 	.hw_params = snd_pcm_softvol_hw_params,
    795 	.hw_free = snd_pcm_generic_hw_free,
    796 	.sw_params = snd_pcm_generic_sw_params,
    797 	.channel_info = snd_pcm_generic_channel_info,
    798 	.dump = snd_pcm_softvol_dump,
    799 	.nonblock = snd_pcm_generic_nonblock,
    800 	.async = snd_pcm_generic_async,
    801 	.mmap = snd_pcm_generic_mmap,
    802 	.munmap = snd_pcm_generic_munmap,
    803 };
    804 
    805 /**
    806  * \brief Creates a new SoftVolume PCM
    807  * \param pcmp Returns created PCM handle
    808  * \param name Name of PCM
    809  * \param sformat Slave format
    810  * \param ctl_card card index of the control
    811  * \param ctl_id The control element
    812  * \param cchannels PCM channels
    813  * \param min_dB minimal dB value
    814  * \param max_dB maximal dB value
    815  * \param resolution resolution of control
    816  * \param slave Slave PCM handle
    817  * \param close_slave When set, the slave PCM handle is closed with copy PCM
    818  * \retval zero on success otherwise a negative error code
    819  * \warning Using of this function might be dangerous in the sense
    820  *          of compatibility reasons. The prototype might be freely
    821  *          changed in future.
    822  */
    823 int snd_pcm_softvol_open(snd_pcm_t **pcmp, const char *name,
    824 			 snd_pcm_format_t sformat,
    825 			 int ctl_card, snd_ctl_elem_id_t *ctl_id,
    826 			 int cchannels,
    827 			 double min_dB, double max_dB, int resolution,
    828 			 snd_pcm_t *slave, int close_slave)
    829 {
    830 	snd_pcm_t *pcm;
    831 	snd_pcm_softvol_t *svol;
    832 	int err;
    833 	assert(pcmp && slave);
    834 	if (sformat != SND_PCM_FORMAT_UNKNOWN &&
    835 	    sformat != SND_PCM_FORMAT_S16_LE &&
    836 	    sformat != SND_PCM_FORMAT_S16_BE &&
    837 	    sformat != SND_PCM_FORMAT_S24_3LE &&
    838 	    sformat != SND_PCM_FORMAT_S32_LE &&
    839 	    sformat != SND_PCM_FORMAT_S32_BE)
    840 		return -EINVAL;
    841 	svol = calloc(1, sizeof(*svol));
    842 	if (! svol)
    843 		return -ENOMEM;
    844 	err = softvol_load_control(slave, svol, ctl_card, ctl_id, cchannels,
    845 				   min_dB, max_dB, resolution);
    846 	if (err < 0) {
    847 		softvol_free(svol);
    848 		return err;
    849 	}
    850 	if (err > 0) { /* hardware control - no need for softvol! */
    851 		softvol_free(svol);
    852 		*pcmp = slave; /* just pass the slave */
    853 		if (!slave->name && name)
    854 			slave->name = strdup(name);
    855 		return 0;
    856 	}
    857 
    858 	/* do softvol */
    859 	snd_pcm_plugin_init(&svol->plug);
    860 	svol->sformat = sformat;
    861 	svol->cchannels = cchannels;
    862 	svol->plug.read = snd_pcm_softvol_read_areas;
    863 	svol->plug.write = snd_pcm_softvol_write_areas;
    864 	svol->plug.undo_read = snd_pcm_plugin_undo_read_generic;
    865 	svol->plug.undo_write = snd_pcm_plugin_undo_write_generic;
    866 	svol->plug.gen.slave = slave;
    867 	svol->plug.gen.close_slave = close_slave;
    868 
    869 	err = snd_pcm_new(&pcm, SND_PCM_TYPE_SOFTVOL, name, slave->stream, slave->mode);
    870 	if (err < 0) {
    871 		softvol_free(svol);
    872 		return err;
    873 	}
    874 	pcm->ops = &snd_pcm_softvol_ops;
    875 	pcm->fast_ops = &snd_pcm_plugin_fast_ops;
    876 	pcm->private_data = svol;
    877 	pcm->poll_fd = slave->poll_fd;
    878 	pcm->poll_events = slave->poll_events;
    879 	/*
    880 	 * Since the softvol converts on the place, and the format/channels
    881 	 * must be identical between source and destination, we don't need
    882 	 * an extra buffer.
    883 	 */
    884 	pcm->mmap_shadow = 1;
    885 	pcm->monotonic = slave->monotonic;
    886 	snd_pcm_set_hw_ptr(pcm, &svol->plug.hw_ptr, -1, 0);
    887 	snd_pcm_set_appl_ptr(pcm, &svol->plug.appl_ptr, -1, 0);
    888 	*pcmp = pcm;
    889 
    890 	return 0;
    891 }
    892 
    893 /* in pcm_misc.c */
    894 int snd_pcm_parse_control_id(snd_config_t *conf, snd_ctl_elem_id_t *ctl_id, int *cardp,
    895 			     int *cchannelsp, int *hwctlp);
    896 
    897 /*! \page pcm_plugins
    898 
    899 \section pcm_plugins_softvol Plugin: Soft Volume
    900 
    901 This plugin applies the software volume attenuation.
    902 The format, rate and channels must match for both of source and destination.
    903 
    904 When the control is stereo (count=2), the channels are assumed to be either
    905 mono, 2.0, 2.1, 4.0, 4.1, 5.1 or 7.1.
    906 
    907 If the control already exists and it's a system control (i.e. no
    908 user-defined control), the plugin simply passes its slave without
    909 any changes.
    910 
    911 \code
    912 pcm.name {
    913         type softvol            # Soft Volume conversion PCM
    914         slave STR               # Slave name
    915         # or
    916         slave {                 # Slave definition
    917                 pcm STR         # Slave PCM name
    918                 # or
    919                 pcm { }         # Slave PCM definition
    920                 [format STR]    # Slave format
    921         }
    922         control {
    923 	        name STR        # control element id string
    924 		[card STR]      # control card index
    925 		[iface STR]     # interface of the element
    926 		[index INT]     # index of the element
    927 		[device INT]    # device number of the element
    928 		[subdevice INT] # subdevice number of the element
    929 		[count INT]     # control channels 1 or 2 (default: 2)
    930 	}
    931 	[min_dB REAL]           # minimal dB value (default: -51.0)
    932 	[max_dB REAL]           # maximal dB value (default:   0.0)
    933 	[resolution INT]        # resolution (default: 256)
    934 				# resolution = 2 means a mute switch
    935 }
    936 \endcode
    937 
    938 \subsection pcm_plugins_softvol_funcref Function reference
    939 
    940 <UL>
    941   <LI>snd_pcm_softvol_open()
    942   <LI>_snd_pcm_softvol_open()
    943 </UL>
    944 
    945 */
    946 
    947 /**
    948  * \brief Creates a new Soft Volume PCM
    949  * \param pcmp Returns created PCM handle
    950  * \param name Name of PCM
    951  * \param root Root configuration node
    952  * \param conf Configuration node with Soft Volume PCM description
    953  * \param stream Stream type
    954  * \param mode Stream mode
    955  * \retval zero on success otherwise a negative error code
    956  * \warning Using of this function might be dangerous in the sense
    957  *          of compatibility reasons. The prototype might be freely
    958  *          changed in future.
    959  */
    960 int _snd_pcm_softvol_open(snd_pcm_t **pcmp, const char *name,
    961 			  snd_config_t *root, snd_config_t *conf,
    962 			  snd_pcm_stream_t stream, int mode)
    963 {
    964 	snd_config_iterator_t i, next;
    965 	int err;
    966 	snd_pcm_t *spcm;
    967 	snd_config_t *slave = NULL, *sconf;
    968 	snd_config_t *control = NULL;
    969 	snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
    970 	snd_ctl_elem_id_t *ctl_id;
    971 	int resolution = PRESET_RESOLUTION;
    972 	double min_dB = PRESET_MIN_DB;
    973 	double max_dB = ZERO_DB;
    974 	int card = -1, cchannels = 2;
    975 
    976 	snd_config_for_each(i, next, conf) {
    977 		snd_config_t *n = snd_config_iterator_entry(i);
    978 		const char *id;
    979 		if (snd_config_get_id(n, &id) < 0)
    980 			continue;
    981 		if (snd_pcm_conf_generic_id(id))
    982 			continue;
    983 		if (strcmp(id, "slave") == 0) {
    984 			slave = n;
    985 			continue;
    986 		}
    987 		if (strcmp(id, "control") == 0) {
    988 			control = n;
    989 			continue;
    990 		}
    991 		if (strcmp(id, "resolution") == 0) {
    992 			long v;
    993 			err = snd_config_get_integer(n, &v);
    994 			if (err < 0) {
    995 				SNDERR("Invalid resolution value");
    996 				return err;
    997 			}
    998 			resolution = v;
    999 			continue;
   1000 		}
   1001 		if (strcmp(id, "min_dB") == 0) {
   1002 			err = snd_config_get_real(n, &min_dB);
   1003 			if (err < 0) {
   1004 				SNDERR("Invalid min_dB value");
   1005 				return err;
   1006 			}
   1007 			continue;
   1008 		}
   1009 		if (strcmp(id, "max_dB") == 0) {
   1010 			err = snd_config_get_real(n, &max_dB);
   1011 			if (err < 0) {
   1012 				SNDERR("Invalid max_dB value");
   1013 				return err;
   1014 			}
   1015 			continue;
   1016 		}
   1017 		SNDERR("Unknown field %s", id);
   1018 		return -EINVAL;
   1019 	}
   1020 	if (!slave) {
   1021 		SNDERR("slave is not defined");
   1022 		return -EINVAL;
   1023 	}
   1024 	if (!control) {
   1025 		SNDERR("control is not defined");
   1026 		return -EINVAL;
   1027 	}
   1028 	if (min_dB >= 0) {
   1029 		SNDERR("min_dB must be a negative value");
   1030 		return -EINVAL;
   1031 	}
   1032 	if (max_dB <= min_dB || max_dB > MAX_DB_UPPER_LIMIT) {
   1033 		SNDERR("max_dB must be larger than min_dB and less than %d dB",
   1034 		       MAX_DB_UPPER_LIMIT);
   1035 		return -EINVAL;
   1036 	}
   1037 	if (resolution <= 1 || resolution > 1024) {
   1038 		SNDERR("Invalid resolution value %d", resolution);
   1039 		return -EINVAL;
   1040 	}
   1041 	if (mode & SND_PCM_NO_SOFTVOL) {
   1042 		err = snd_pcm_slave_conf(root, slave, &sconf, 0);
   1043 		if (err < 0)
   1044 			return err;
   1045 		err = snd_pcm_open_named_slave(pcmp, name, root, sconf, stream,
   1046 					       mode, conf);
   1047 		snd_config_delete(sconf);
   1048 	} else {
   1049 		snd_ctl_elem_id_alloca(&ctl_id);
   1050 		err = snd_pcm_slave_conf(root, slave, &sconf, 1,
   1051 					 SND_PCM_HW_PARAM_FORMAT, 0, &sformat);
   1052 		if (err < 0)
   1053 			return err;
   1054 		if (sformat != SND_PCM_FORMAT_UNKNOWN &&
   1055 		    sformat != SND_PCM_FORMAT_S16_LE &&
   1056 		    sformat != SND_PCM_FORMAT_S16_BE &&
   1057 		    sformat != SND_PCM_FORMAT_S24_3LE &&
   1058 		    sformat != SND_PCM_FORMAT_S32_LE &&
   1059 		    sformat != SND_PCM_FORMAT_S32_BE) {
   1060 			SNDERR("only S16_LE, S16_BE, S24_3LE, S32_LE or S32_BE format "
   1061 			       "is supported");
   1062 			snd_config_delete(sconf);
   1063 			return -EINVAL;
   1064 		}
   1065 		err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
   1066 		snd_config_delete(sconf);
   1067 		if (err < 0)
   1068 			return err;
   1069 		if ((err = snd_pcm_parse_control_id(control, ctl_id, &card, &cchannels, NULL)) < 0) {
   1070 			snd_pcm_close(spcm);
   1071 			return err;
   1072 		}
   1073 		err = snd_pcm_softvol_open(pcmp, name, sformat, card, ctl_id, cchannels,
   1074 					   min_dB, max_dB, resolution, spcm, 1);
   1075 		if (err < 0)
   1076 			snd_pcm_close(spcm);
   1077 	}
   1078 	return err;
   1079 }
   1080 #ifndef DOC_HIDDEN
   1081 SND_DLSYM_BUILD_VERSION(_snd_pcm_softvol_open, SND_PCM_DLSYM_VERSION);
   1082 #endif
   1083