summary refs log tree commit diff
path: root/sound
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2010-02-12 10:42:38 +0100
committerTakashi Iwai <tiwai@suse.de>2010-02-12 10:42:38 +0100
commita540e13386e90f8c833c5cd0d16d877b8a277af1 (patch)
treebfa3fa455ac1302e7355122f9979531c7aa476e7 /sound
parentcebe41d4b8f8092359de31e241815fcb4b4dc0be (diff)
parentc3a3e040f01457d2ea4f199f75ca205401001a3b (diff)
downloadlinux-a540e13386e90f8c833c5cd0d16d877b8a277af1.tar.gz
Merge remote branch 'alsa/devel' into topic/misc
Diffstat (limited to 'sound')
-rw-r--r--sound/drivers/dummy.c290
-rw-r--r--sound/usb/usbmixer.c125
-rw-r--r--sound/usb/usbmixer_maps.c23
3 files changed, 273 insertions, 165 deletions
diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c
index 252e04ce602f..7f41990ed68b 100644
--- a/sound/drivers/dummy.c
+++ b/sound/drivers/dummy.c
@@ -45,109 +45,23 @@ MODULE_SUPPORTED_DEVICE("{{ALSA,Dummy soundcard}}");
 #define MAX_PCM_SUBSTREAMS	128
 #define MAX_MIDI_DEVICES	2
 
-#if 0 /* emu10k1 emulation */
-#define MAX_BUFFER_SIZE		(128 * 1024)
-static int emu10k1_playback_constraints(struct snd_pcm_runtime *runtime)
-{
-	int err;
-	err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
-	if (err < 0)
-		return err;
-	err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 256, UINT_MAX);
-	if (err < 0)
-		return err;
-	return 0;
-}
-#define add_playback_constraints emu10k1_playback_constraints
-#endif
-
-#if 0 /* RME9652 emulation */
-#define MAX_BUFFER_SIZE		(26 * 64 * 1024)
-#define USE_FORMATS		SNDRV_PCM_FMTBIT_S32_LE
-#define USE_CHANNELS_MIN	26
-#define USE_CHANNELS_MAX	26
-#define USE_PERIODS_MIN		2
-#define USE_PERIODS_MAX		2
-#endif
-
-#if 0 /* ICE1712 emulation */
-#define MAX_BUFFER_SIZE		(256 * 1024)
-#define USE_FORMATS		SNDRV_PCM_FMTBIT_S32_LE
-#define USE_CHANNELS_MIN	10
-#define USE_CHANNELS_MAX	10
-#define USE_PERIODS_MIN		1
-#define USE_PERIODS_MAX		1024
-#endif
-
-#if 0 /* UDA1341 emulation */
-#define MAX_BUFFER_SIZE		(16380)
-#define USE_FORMATS		SNDRV_PCM_FMTBIT_S16_LE
-#define USE_CHANNELS_MIN	2
-#define USE_CHANNELS_MAX	2
-#define USE_PERIODS_MIN		2
-#define USE_PERIODS_MAX		255
-#endif
-
-#if 0 /* simple AC97 bridge (intel8x0) with 48kHz AC97 only codec */
-#define USE_FORMATS		SNDRV_PCM_FMTBIT_S16_LE
-#define USE_CHANNELS_MIN	2
-#define USE_CHANNELS_MAX	2
-#define USE_RATE		SNDRV_PCM_RATE_48000
-#define USE_RATE_MIN		48000
-#define USE_RATE_MAX		48000
-#endif
-
-#if 0 /* CA0106 */
-#define USE_FORMATS		SNDRV_PCM_FMTBIT_S16_LE
-#define USE_CHANNELS_MIN	2
-#define USE_CHANNELS_MAX	2
-#define USE_RATE		(SNDRV_PCM_RATE_48000|SNDRV_PCM_RATE_96000|SNDRV_PCM_RATE_192000) 
-#define USE_RATE_MIN		48000 
-#define USE_RATE_MAX		192000
-#define MAX_BUFFER_SIZE		((65536-64)*8)
-#define MAX_PERIOD_SIZE		(65536-64)
-#define USE_PERIODS_MIN		2
-#define USE_PERIODS_MAX		8
-#endif
-
-
 /* defaults */
-#ifndef MAX_BUFFER_SIZE
 #define MAX_BUFFER_SIZE		(64*1024)
-#endif
-#ifndef MAX_PERIOD_SIZE
+#define MIN_PERIOD_SIZE		64
 #define MAX_PERIOD_SIZE		MAX_BUFFER_SIZE
-#endif
-#ifndef USE_FORMATS
 #define USE_FORMATS 		(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE)
-#endif
-#ifndef USE_RATE
 #define USE_RATE		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000
 #define USE_RATE_MIN		5500
 #define USE_RATE_MAX		48000
-#endif
-#ifndef USE_CHANNELS_MIN
 #define USE_CHANNELS_MIN 	1
-#endif
-#ifndef USE_CHANNELS_MAX
 #define USE_CHANNELS_MAX 	2
-#endif
-#ifndef USE_PERIODS_MIN
 #define USE_PERIODS_MIN 	1
-#endif
-#ifndef USE_PERIODS_MAX
 #define USE_PERIODS_MAX 	1024
-#endif
-#ifndef add_playback_constraints
-#define add_playback_constraints(x) 0
-#endif
-#ifndef add_capture_constraints
-#define add_capture_constraints(x) 0
-#endif
 
 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
 static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
 static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0};
+static char *model[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = NULL};
 static int pcm_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
 static int pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8};
 //static int midi_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
@@ -162,6 +76,8 @@ module_param_array(id, charp, NULL, 0444);
 MODULE_PARM_DESC(id, "ID string for dummy soundcard.");
 module_param_array(enable, bool, NULL, 0444);
 MODULE_PARM_DESC(enable, "Enable this dummy soundcard.");
+module_param_array(model, charp, NULL, 0444);
+MODULE_PARM_DESC(model, "Soundcard model.");
 module_param_array(pcm_devs, int, NULL, 0444);
 MODULE_PARM_DESC(pcm_devs, "PCM devices # (0-4) for dummy driver.");
 module_param_array(pcm_substreams, int, NULL, 0444);
@@ -193,9 +109,28 @@ struct dummy_timer_ops {
 	snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *);
 };
 
+struct dummy_model {
+	const char *name;
+	int (*playback_constraints)(struct snd_pcm_runtime *runtime);
+	int (*capture_constraints)(struct snd_pcm_runtime *runtime);
+	u64 formats;
+	size_t buffer_bytes_max;
+	size_t period_bytes_min;
+	size_t period_bytes_max;
+	unsigned int periods_min;
+	unsigned int periods_max;
+	unsigned int rates;
+	unsigned int rate_min;
+	unsigned int rate_max;
+	unsigned int channels_min;
+	unsigned int channels_max;
+};
+
 struct snd_dummy {
 	struct snd_card *card;
+	struct dummy_model *model;
 	struct snd_pcm *pcm;
+	struct snd_pcm_hardware pcm_hw;
 	spinlock_t mixer_lock;
 	int mixer_volume[MIXER_ADDR_LAST+1][2];
 	int capture_source[MIXER_ADDR_LAST+1][2];
@@ -203,6 +138,92 @@ struct snd_dummy {
 };
 
 /*
+ * card models
+ */
+
+static int emu10k1_playback_constraints(struct snd_pcm_runtime *runtime)
+{
+	int err;
+	err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+	if (err < 0)
+		return err;
+	err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 256, UINT_MAX);
+	if (err < 0)
+		return err;
+	return 0;
+}
+
+struct dummy_model model_emu10k1 = {
+	.name = "emu10k1",
+	.playback_constraints = emu10k1_playback_constraints,
+	.buffer_bytes_max = 128 * 1024,
+};
+
+struct dummy_model model_rme9652 = {
+	.name = "rme9652",
+	.buffer_bytes_max = 26 * 64 * 1024,
+	.formats = SNDRV_PCM_FMTBIT_S32_LE,
+	.channels_min = 26,
+	.channels_max = 26,
+	.periods_min = 2,
+	.periods_max = 2,
+};
+
+struct dummy_model model_ice1712 = {
+	.name = "ice1712",
+	.buffer_bytes_max = 256 * 1024,
+	.formats = SNDRV_PCM_FMTBIT_S32_LE,
+	.channels_min = 10,
+	.channels_max = 10,
+	.periods_min = 1,
+	.periods_max = 1024,
+};
+
+struct dummy_model model_uda1341 = {
+	.name = "uda1341",
+	.buffer_bytes_max = 16380,
+	.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	.channels_min = 2,
+	.channels_max = 2,
+	.periods_min = 2,
+	.periods_max = 255,
+};
+
+struct dummy_model model_ac97 = {
+	.name = "ac97",
+	.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	.channels_min = 2,
+	.channels_max = 2,
+	.rates = SNDRV_PCM_RATE_48000,
+	.rate_min = 48000,
+	.rate_max = 48000,
+};
+
+struct dummy_model model_ca0106 = {
+	.name = "ca0106",
+	.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	.buffer_bytes_max = ((65536-64)*8),
+	.period_bytes_max = (65536-64),
+	.periods_min = 2,
+	.periods_max = 8,
+	.channels_min = 2,
+	.channels_max = 2,
+	.rates = SNDRV_PCM_RATE_48000|SNDRV_PCM_RATE_96000|SNDRV_PCM_RATE_192000,
+	.rate_min = 48000,
+	.rate_max = 192000,
+};
+
+struct dummy_model *dummy_models[] = {
+	&model_emu10k1,
+	&model_rme9652,
+	&model_ice1712,
+	&model_uda1341,
+	&model_ac97,
+	&model_ca0106,
+	NULL
+};
+
+/*
  * system timer interface
  */
 
@@ -509,7 +530,7 @@ static struct snd_pcm_hardware dummy_pcm_hardware = {
 	.channels_min =		USE_CHANNELS_MIN,
 	.channels_max =		USE_CHANNELS_MAX,
 	.buffer_bytes_max =	MAX_BUFFER_SIZE,
-	.period_bytes_min =	64,
+	.period_bytes_min =	MIN_PERIOD_SIZE,
 	.period_bytes_max =	MAX_PERIOD_SIZE,
 	.periods_min =		USE_PERIODS_MIN,
 	.periods_max =		USE_PERIODS_MAX,
@@ -538,6 +559,7 @@ static int dummy_pcm_hw_free(struct snd_pcm_substream *substream)
 static int dummy_pcm_open(struct snd_pcm_substream *substream)
 {
 	struct snd_dummy *dummy = snd_pcm_substream_chip(substream);
+	struct dummy_model *model = dummy->model;
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	int err;
 
@@ -551,7 +573,7 @@ static int dummy_pcm_open(struct snd_pcm_substream *substream)
 	if (err < 0)
 		return err;
 
-	runtime->hw = dummy_pcm_hardware;
+	runtime->hw = dummy->pcm_hw;
 	if (substream->pcm->device & 1) {
 		runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED;
 		runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED;
@@ -560,10 +582,16 @@ static int dummy_pcm_open(struct snd_pcm_substream *substream)
 		runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP |
 				      SNDRV_PCM_INFO_MMAP_VALID);
 
-	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-		err = add_playback_constraints(substream->runtime);
-	else
-		err = add_capture_constraints(substream->runtime);
+	if (model == NULL)
+		return 0;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		if (model->playback_constraints)
+			err = model->playback_constraints(substream->runtime);
+	} else {
+		if (model->capture_constraints)
+			err = model->capture_constraints(substream->runtime);
+	}
 	if (err < 0) {
 		dummy->timer_ops->free(substream);
 		return err;
@@ -823,17 +851,19 @@ static int __devinit snd_card_dummy_new_mixer(struct snd_dummy *dummy)
 /*
  * proc interface
  */
-static void print_formats(struct snd_info_buffer *buffer)
+static void print_formats(struct snd_dummy *dummy,
+			  struct snd_info_buffer *buffer)
 {
 	int i;
 
 	for (i = 0; i < SNDRV_PCM_FORMAT_LAST; i++) {
-		if (dummy_pcm_hardware.formats & (1ULL << i))
+		if (dummy->pcm_hw.formats & (1ULL << i))
 			snd_iprintf(buffer, " %s", snd_pcm_format_name(i));
 	}
 }
 
-static void print_rates(struct snd_info_buffer *buffer)
+static void print_rates(struct snd_dummy *dummy,
+			struct snd_info_buffer *buffer)
 {
 	static int rates[] = {
 		5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000,
@@ -841,19 +871,19 @@ static void print_rates(struct snd_info_buffer *buffer)
 	};
 	int i;
 
-	if (dummy_pcm_hardware.rates & SNDRV_PCM_RATE_CONTINUOUS)
+	if (dummy->pcm_hw.rates & SNDRV_PCM_RATE_CONTINUOUS)
 		snd_iprintf(buffer, " continuous");
-	if (dummy_pcm_hardware.rates & SNDRV_PCM_RATE_KNOT)
+	if (dummy->pcm_hw.rates & SNDRV_PCM_RATE_KNOT)
 		snd_iprintf(buffer, " knot");
 	for (i = 0; i < ARRAY_SIZE(rates); i++)
-		if (dummy_pcm_hardware.rates & (1 << i))
+		if (dummy->pcm_hw.rates & (1 << i))
 			snd_iprintf(buffer, " %d", rates[i]);
 }
 
-#define get_dummy_int_ptr(ofs) \
-	(unsigned int *)((char *)&dummy_pcm_hardware + (ofs))
-#define get_dummy_ll_ptr(ofs) \
-	(unsigned long long *)((char *)&dummy_pcm_hardware + (ofs))
+#define get_dummy_int_ptr(dummy, ofs) \
+	(unsigned int *)((char *)&((dummy)->pcm_hw) + (ofs))
+#define get_dummy_ll_ptr(dummy, ofs) \
+	(unsigned long long *)((char *)&((dummy)->pcm_hw) + (ofs))
 
 struct dummy_hw_field {
 	const char *name;
@@ -884,20 +914,21 @@ static struct dummy_hw_field fields[] = {
 static void dummy_proc_read(struct snd_info_entry *entry,
 			    struct snd_info_buffer *buffer)
 {
+	struct snd_dummy *dummy = entry->private_data;
 	int i;
 
 	for (i = 0; i < ARRAY_SIZE(fields); i++) {
 		snd_iprintf(buffer, "%s ", fields[i].name);
 		if (fields[i].size == sizeof(int))
 			snd_iprintf(buffer, fields[i].format,
-				    *get_dummy_int_ptr(fields[i].offset));
+				*get_dummy_int_ptr(dummy, fields[i].offset));
 		else
 			snd_iprintf(buffer, fields[i].format,
-				    *get_dummy_ll_ptr(fields[i].offset));
+				*get_dummy_ll_ptr(dummy, fields[i].offset));
 		if (!strcmp(fields[i].name, "formats"))
-			print_formats(buffer);
+			print_formats(dummy, buffer);
 		else if (!strcmp(fields[i].name, "rates"))
-			print_rates(buffer);
+			print_rates(dummy, buffer);
 		snd_iprintf(buffer, "\n");
 	}
 }
@@ -905,6 +936,7 @@ static void dummy_proc_read(struct snd_info_entry *entry,
 static void dummy_proc_write(struct snd_info_entry *entry,
 			     struct snd_info_buffer *buffer)
 {
+	struct snd_dummy *dummy = entry->private_data;
 	char line[64];
 
 	while (!snd_info_get_line(buffer, line, sizeof(line))) {
@@ -924,9 +956,9 @@ static void dummy_proc_write(struct snd_info_entry *entry,
 		if (strict_strtoull(item, 0, &val))
 			continue;
 		if (fields[i].size == sizeof(int))
-			*get_dummy_int_ptr(fields[i].offset) = val;
+			*get_dummy_int_ptr(dummy, fields[i].offset) = val;
 		else
-			*get_dummy_ll_ptr(fields[i].offset) = val;
+			*get_dummy_ll_ptr(dummy, fields[i].offset) = val;
 	}
 }
 
@@ -938,6 +970,7 @@ static void __devinit dummy_proc_init(struct snd_dummy *chip)
 		snd_info_set_text_ops(entry, chip, dummy_proc_read);
 		entry->c.text.write = dummy_proc_write;
 		entry->mode |= S_IWUSR;
+		entry->private_data = chip;
 	}
 }
 #else
@@ -948,6 +981,7 @@ static int __devinit snd_dummy_probe(struct platform_device *devptr)
 {
 	struct snd_card *card;
 	struct snd_dummy *dummy;
+	struct dummy_model *m = NULL, **mdl;
 	int idx, err;
 	int dev = devptr->id;
 
@@ -957,6 +991,15 @@ static int __devinit snd_dummy_probe(struct platform_device *devptr)
 		return err;
 	dummy = card->private_data;
 	dummy->card = card;
+	for (mdl = dummy_models; *mdl && model[dev]; mdl++) {
+		if (strcmp(model[dev], (*mdl)->name) == 0) {
+			printk(KERN_INFO
+				"snd-dummy: Using model '%s' for card %i\n",
+				(*mdl)->name, card->number);
+			m = dummy->model = *mdl;
+			break;
+		}
+	}
 	for (idx = 0; idx < MAX_PCM_DEVICES && idx < pcm_devs[dev]; idx++) {
 		if (pcm_substreams[dev] < 1)
 			pcm_substreams[dev] = 1;
@@ -966,6 +1009,33 @@ static int __devinit snd_dummy_probe(struct platform_device *devptr)
 		if (err < 0)
 			goto __nodev;
 	}
+
+	dummy->pcm_hw = dummy_pcm_hardware;
+	if (m) {
+		if (m->formats)
+			dummy->pcm_hw.formats = m->formats;
+		if (m->buffer_bytes_max)
+			dummy->pcm_hw.buffer_bytes_max = m->buffer_bytes_max;
+		if (m->period_bytes_min)
+			dummy->pcm_hw.period_bytes_min = m->period_bytes_min;
+		if (m->period_bytes_max)
+			dummy->pcm_hw.period_bytes_max = m->period_bytes_max;
+		if (m->periods_min)
+			dummy->pcm_hw.periods_min = m->periods_min;
+		if (m->periods_max)
+			dummy->pcm_hw.periods_max = m->periods_max;
+		if (m->rates)
+			dummy->pcm_hw.rates = m->rates;
+		if (m->rate_min)
+			dummy->pcm_hw.rate_min = m->rate_min;
+		if (m->rate_max)
+			dummy->pcm_hw.rate_max = m->rate_max;
+		if (m->channels_min)
+			dummy->pcm_hw.channels_min = m->channels_min;
+		if (m->channels_max)
+			dummy->pcm_hw.channels_max = m->channels_max;
+	}
+
 	err = snd_card_dummy_new_mixer(dummy);
 	if (err < 0)
 		goto __nodev;
diff --git a/sound/usb/usbmixer.c b/sound/usb/usbmixer.c
index f5596cfdbde1..dd0c1d7bf3ed 100644
--- a/sound/usb/usbmixer.c
+++ b/sound/usb/usbmixer.c
@@ -123,6 +123,7 @@ struct usb_mixer_elem_info {
 	int channels;
 	int val_type;
 	int min, max, res;
+	int dBmin, dBmax;
 	int cached;
 	int cache_val[MAX_CHANNELS];
 	u8 initialized;
@@ -209,42 +210,50 @@ enum {
  */
 #include "usbmixer_maps.c"
 
-/* get the mapped name if the unit matches */
-static int check_mapped_name(struct mixer_build *state, int unitid, int control, char *buf, int buflen)
+static const struct usbmix_name_map *
+find_map(struct mixer_build *state, int unitid, int control)
 {
-	const struct usbmix_name_map *p;
+	const struct usbmix_name_map *p = state->map;
 
-	if (! state->map)
-		return 0;
+	if (!p)
+		return NULL;
 
 	for (p = state->map; p->id; p++) {
-		if (p->id == unitid && p->name &&
-		    (! control || ! p->control || control == p->control)) {
-			buflen--;
-			return strlcpy(buf, p->name, buflen);
-		}
+		if (p->id == unitid &&
+		    (!control || !p->control || control == p->control))
+			return p;
 	}
-	return 0;
+	return NULL;
 }
 
-/* check whether the control should be ignored */
-static int check_ignored_ctl(struct mixer_build *state, int unitid, int control)
+/* get the mapped name if the unit matches */
+static int
+check_mapped_name(const struct usbmix_name_map *p, char *buf, int buflen)
 {
-	const struct usbmix_name_map *p;
+	if (!p || !p->name)
+		return 0;
 
-	if (! state->map)
+	buflen--;
+	return strlcpy(buf, p->name, buflen);
+}
+
+/* check whether the control should be ignored */
+static inline int
+check_ignored_ctl(const struct usbmix_name_map *p)
+{
+	if (!p || p->name || p->dB)
 		return 0;
-	for (p = state->map; p->id; p++) {
-		if (p->id == unitid && ! p->name &&
-		    (! control || ! p->control || control == p->control)) {
-			/*
-			printk(KERN_DEBUG "ignored control %d:%d\n",
-			       unitid, control);
-			*/
-			return 1;
-		}
+	return 1;
+}
+
+/* dB mapping */
+static inline void check_mapped_dB(const struct usbmix_name_map *p,
+				   struct usb_mixer_elem_info *cval)
+{
+	if (p && p->dB) {
+		cval->dBmin = p->dB->min;
+		cval->dBmax = p->dB->max;
 	}
-	return 0;
 }
 
 /* get the mapped selector source name */
@@ -481,20 +490,8 @@ static int mixer_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag,
 
 	if (size < sizeof(scale))
 		return -ENOMEM;
-	/* USB descriptions contain the dB scale in 1/256 dB unit
-	 * while ALSA TLV contains in 1/100 dB unit
-	 */
-	scale[2] = (convert_signed_value(cval, cval->min) * 100) / 256;
-	scale[3] = (convert_signed_value(cval, cval->max) * 100) / 256;
-	if (scale[3] <= scale[2]) {
-		/* something is wrong; assume it's either from/to 0dB */
-		if (scale[2] < 0)
-			scale[3] = 0;
-		else if (scale[2] > 0)
-			scale[2] = 0;
-		else /* totally crap, return an error */
-			return -EINVAL;
-	}
+	scale[2] = cval->dBmin;
+	scale[3] = cval->dBmax;
 	if (copy_to_user(_tlv, scale, sizeof(scale)))
 		return -EFAULT;
 	return 0;
@@ -735,6 +732,7 @@ static int get_min_max(struct usb_mixer_elem_info *cval, int default_min)
 	cval->min = default_min;
 	cval->max = cval->min + 1;
 	cval->res = 1;
+	cval->dBmin = cval->dBmax = 0;
 
 	if (cval->val_type == USB_MIXER_BOOLEAN ||
 	    cval->val_type == USB_MIXER_INV_BOOLEAN) {
@@ -802,6 +800,24 @@ static int get_min_max(struct usb_mixer_elem_info *cval, int default_min)
 
 		cval->initialized = 1;
 	}
+
+	/* USB descriptions contain the dB scale in 1/256 dB unit
+	 * while ALSA TLV contains in 1/100 dB unit
+	 */
+	cval->dBmin = (convert_signed_value(cval, cval->min) * 100) / 256;
+	cval->dBmax = (convert_signed_value(cval, cval->max) * 100) / 256;
+	if (cval->dBmin > cval->dBmax) {
+		/* something is wrong; assume it's either from/to 0dB */
+		if (cval->dBmin < 0)
+			cval->dBmax = 0;
+		else if (cval->dBmin > 0)
+			cval->dBmin = 0;
+		if (cval->dBmin > cval->dBmax) {
+			/* totally crap, return an error */
+			return -EINVAL;
+		}
+	}
+
 	return 0;
 }
 
@@ -927,6 +943,7 @@ static void build_feature_ctl(struct mixer_build *state, unsigned char *desc,
 	int nameid = desc[desc[0] - 1];
 	struct snd_kcontrol *kctl;
 	struct usb_mixer_elem_info *cval;
+	const struct usbmix_name_map *map;
 
 	control++; /* change from zero-based to 1-based value */
 
@@ -935,7 +952,8 @@ static void build_feature_ctl(struct mixer_build *state, unsigned char *desc,
 		return;
 	}
 
-	if (check_ignored_ctl(state, unitid, control))
+	map = find_map(state, unitid, control);
+	if (check_ignored_ctl(map))
 		return;
 
 	cval = kzalloc(sizeof(*cval), GFP_KERNEL);
@@ -969,10 +987,11 @@ static void build_feature_ctl(struct mixer_build *state, unsigned char *desc,
 	}
 	kctl->private_free = usb_mixer_elem_free;
 
-	len = check_mapped_name(state, unitid, control, kctl->id.name, sizeof(kctl->id.name));
+	len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name));
 	mapped_name = len != 0;
 	if (! len && nameid)
-		len = snd_usb_copy_string_desc(state, nameid, kctl->id.name, sizeof(kctl->id.name));
+		len = snd_usb_copy_string_desc(state, nameid,
+				kctl->id.name, sizeof(kctl->id.name));
 
 	switch (control) {
 	case USB_FEATURE_MUTE:
@@ -1010,6 +1029,7 @@ static void build_feature_ctl(struct mixer_build *state, unsigned char *desc,
 			kctl->vd[0].access |= 
 				SNDRV_CTL_ELEM_ACCESS_TLV_READ |
 				SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
+			check_mapped_dB(map, cval);
 		}
 		break;
 
@@ -1137,8 +1157,10 @@ static void build_mixer_unit_ctl(struct mixer_build *state, unsigned char *desc,
 	unsigned int num_outs = desc[5 + input_pins];
 	unsigned int i, len;
 	struct snd_kcontrol *kctl;
+	const struct usbmix_name_map *map;
 
-	if (check_ignored_ctl(state, unitid, 0))
+	map = find_map(state, unitid, 0);
+	if (check_ignored_ctl(map))
 		return;
 
 	cval = kzalloc(sizeof(*cval), GFP_KERNEL);
@@ -1167,7 +1189,7 @@ static void build_mixer_unit_ctl(struct mixer_build *state, unsigned char *desc,
 	}
 	kctl->private_free = usb_mixer_elem_free;
 
-	len = check_mapped_name(state, unitid, 0, kctl->id.name, sizeof(kctl->id.name));
+	len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name));
 	if (! len)
 		len = get_term_name(state, iterm, kctl->id.name, sizeof(kctl->id.name), 0);
 	if (! len)
@@ -1382,6 +1404,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned
 	int i, err, nameid, type, len;
 	struct procunit_info *info;
 	struct procunit_value_info *valinfo;
+	const struct usbmix_name_map *map;
 	static struct procunit_value_info default_value_info[] = {
 		{ 0x01, "Switch", USB_MIXER_BOOLEAN },
 		{ 0 }
@@ -1411,7 +1434,8 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned
 		/* FIXME: bitmap might be longer than 8bit */
 		if (! (dsc[12 + num_ins] & (1 << (valinfo->control - 1))))
 			continue;
-		if (check_ignored_ctl(state, unitid, valinfo->control))
+		map = find_map(state, unitid, valinfo->control);
+		if (check_ignored_ctl(map))
 			continue;
 		cval = kzalloc(sizeof(*cval), GFP_KERNEL);
 		if (! cval) {
@@ -1452,8 +1476,9 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned
 		}
 		kctl->private_free = usb_mixer_elem_free;
 
-		if (check_mapped_name(state, unitid, cval->control, kctl->id.name, sizeof(kctl->id.name)))
-			;
+		if (check_mapped_name(map, kctl->id.name,
+						sizeof(kctl->id.name)))
+			/* nothing */ ;
 		else if (info->name)
 			strlcpy(kctl->id.name, info->name, sizeof(kctl->id.name));
 		else {
@@ -1592,6 +1617,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, unsi
 	int err;
 	struct usb_mixer_elem_info *cval;
 	struct snd_kcontrol *kctl;
+	const struct usbmix_name_map *map;
 	char **namelist;
 
 	if (! num_ins || desc[0] < 5 + num_ins) {
@@ -1607,7 +1633,8 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, unsi
 	if (num_ins == 1) /* only one ? nonsense! */
 		return 0;
 
-	if (check_ignored_ctl(state, unitid, 0))
+	map = find_map(state, unitid, 0);
+	if (check_ignored_ctl(map))
 		return 0;
 
 	cval = kzalloc(sizeof(*cval), GFP_KERNEL);
@@ -1662,7 +1689,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, unsi
 	kctl->private_free = usb_mixer_selector_elem_free;
 
 	nameid = desc[desc[0] - 1];
-	len = check_mapped_name(state, unitid, 0, kctl->id.name, sizeof(kctl->id.name));
+	len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name));
 	if (len)
 		;
 	else if (nameid)
diff --git a/sound/usb/usbmixer_maps.c b/sound/usb/usbmixer_maps.c
index 77c35885e21c..79e903a60862 100644
--- a/sound/usb/usbmixer_maps.c
+++ b/sound/usb/usbmixer_maps.c
@@ -19,11 +19,16 @@
  *
  */
 
+struct usbmix_dB_map {
+	u32 min;
+	u32 max;
+};
 
 struct usbmix_name_map {
 	int id;
 	const char *name;
 	int control;
+	struct usbmix_dB_map *dB;
 };
 
 struct usbmix_selector_map {
@@ -72,7 +77,7 @@ static struct usbmix_name_map extigy_map[] = {
 	{ 8, "Line Playback" }, /* FU */
 	/* 9: IT mic */
 	{ 10, "Mic Playback" }, /* FU */
-	{ 11, "Capture Input Source" }, /* SU */
+	{ 11, "Capture Source" }, /* SU */
 	{ 12, "Capture" }, /* FU */
 	/* 13: OT pcm capture */
 	/* 14: MU (w/o controls) */
@@ -102,6 +107,9 @@ static struct usbmix_name_map extigy_map[] = {
  * e.g. no Master and fake PCM volume
  *			Pavel Mihaylov <bin@bash.info>
  */
+static struct usbmix_dB_map mp3plus_dB_1 = {-4781, 0};	/* just guess */
+static struct usbmix_dB_map mp3plus_dB_2 = {-1781, 618}; /* just guess */
+
 static struct usbmix_name_map mp3plus_map[] = {
 	/* 1: IT pcm */
 	/* 2: IT mic */
@@ -110,16 +118,19 @@ static struct usbmix_name_map mp3plus_map[] = {
 	/* 5: OT digital out */
 	/* 6: OT speaker */
 	/* 7: OT pcm capture */
-	{ 8, "Capture Input Source" }, /* FU, default PCM Capture Source */
+	{ 8, "Capture Source" }, /* FU, default PCM Capture Source */
 		/* (Mic, Input 1 = Line input, Input 2 = Optical input) */
 	{ 9, "Master Playback" }, /* FU, default Speaker 1 */
 	/* { 10, "Mic Capture", 1 }, */ /* FU, Mic Capture */
-	/* { 10, "Mic Capture", 2 }, */ /* FU, Mic Capture */
+	{ 10, /* "Mic Capture", */ NULL, 2, .dB = &mp3plus_dB_2 },
+		/* FU, Mic Capture */
 	{ 10, "Mic Boost", 7 }, /* FU, default Auto Gain Input */
-	{ 11, "Line Capture" }, /* FU, default PCM Capture */
+	{ 11, "Line Capture", .dB = &mp3plus_dB_2 },
+		/* FU, default PCM Capture */
 	{ 12, "Digital In Playback" }, /* FU, default PCM 1 */
-	/* { 13, "Mic Playback" }, */ /* FU, default Mic Playback */
-	{ 14, "Line Playback" }, /* FU, default Speaker */
+	{ 13, /* "Mic Playback", */ .dB = &mp3plus_dB_1 },
+		/* FU, default Mic Playback */
+	{ 14, "Line Playback", .dB = &mp3plus_dB_1 }, /* FU, default Speaker */
 	/* 15: MU */
 	{ 0 } /* terminator */
 };