Commit c3a3e040 authored by Jaroslav Kysela's avatar Jaroslav Kysela

ALSA: usbmixer - add possibility to remap dB values

USB devices tends to represent dB ranges in different way than ALSA expects.
Add possibility to override these values and add guessed values for
SoundBlaster MP3+.

Also rename 'Capture Input Source' control to 'Capture Source' for
SoundBlaster MP3+ and Extigy.
Signed-off-by: default avatarJaroslav Kysela <perex@perex.cz>
parent d5e1ca05
...@@ -123,6 +123,7 @@ struct usb_mixer_elem_info { ...@@ -123,6 +123,7 @@ struct usb_mixer_elem_info {
int channels; int channels;
int val_type; int val_type;
int min, max, res; int min, max, res;
int dBmin, dBmax;
int cached; int cached;
int cache_val[MAX_CHANNELS]; int cache_val[MAX_CHANNELS];
u8 initialized; u8 initialized;
...@@ -194,42 +195,50 @@ enum { ...@@ -194,42 +195,50 @@ enum {
*/ */
#include "usbmixer_maps.c" #include "usbmixer_maps.c"
/* get the mapped name if the unit matches */ static const struct usbmix_name_map *
static int check_mapped_name(struct mixer_build *state, int unitid, int control, char *buf, int buflen) 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) if (!p)
return 0; return NULL;
for (p = state->map; p->id; p++) { for (p = state->map; p->id; p++) {
if (p->id == unitid && p->name && if (p->id == unitid &&
(! control || ! p->control || control == p->control)) { (!control || !p->control || control == p->control))
buflen--; return p;
return strlcpy(buf, p->name, buflen);
}
} }
return NULL;
}
/* get the mapped name if the unit matches */
static int
check_mapped_name(const struct usbmix_name_map *p, char *buf, int buflen)
{
if (!p || !p->name)
return 0; return 0;
buflen--;
return strlcpy(buf, p->name, buflen);
} }
/* check whether the control should be ignored */ /* check whether the control should be ignored */
static int check_ignored_ctl(struct mixer_build *state, int unitid, int control) static inline int
check_ignored_ctl(const struct usbmix_name_map *p)
{ {
const struct usbmix_name_map *p; if (!p || p->name || p->dB)
if (! state->map)
return 0; 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 */ /* get the mapped selector source name */
...@@ -466,20 +475,8 @@ static int mixer_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag, ...@@ -466,20 +475,8 @@ static int mixer_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag,
if (size < sizeof(scale)) if (size < sizeof(scale))
return -ENOMEM; return -ENOMEM;
/* USB descriptions contain the dB scale in 1/256 dB unit scale[2] = cval->dBmin;
* while ALSA TLV contains in 1/100 dB unit scale[3] = cval->dBmax;
*/
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;
}
if (copy_to_user(_tlv, scale, sizeof(scale))) if (copy_to_user(_tlv, scale, sizeof(scale)))
return -EFAULT; return -EFAULT;
return 0; return 0;
...@@ -720,6 +717,7 @@ static int get_min_max(struct usb_mixer_elem_info *cval, int default_min) ...@@ -720,6 +717,7 @@ static int get_min_max(struct usb_mixer_elem_info *cval, int default_min)
cval->min = default_min; cval->min = default_min;
cval->max = cval->min + 1; cval->max = cval->min + 1;
cval->res = 1; cval->res = 1;
cval->dBmin = cval->dBmax = 0;
if (cval->val_type == USB_MIXER_BOOLEAN || if (cval->val_type == USB_MIXER_BOOLEAN ||
cval->val_type == USB_MIXER_INV_BOOLEAN) { cval->val_type == USB_MIXER_INV_BOOLEAN) {
...@@ -787,6 +785,24 @@ static int get_min_max(struct usb_mixer_elem_info *cval, int default_min) ...@@ -787,6 +785,24 @@ static int get_min_max(struct usb_mixer_elem_info *cval, int default_min)
cval->initialized = 1; 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; return 0;
} }
...@@ -912,6 +928,7 @@ static void build_feature_ctl(struct mixer_build *state, unsigned char *desc, ...@@ -912,6 +928,7 @@ static void build_feature_ctl(struct mixer_build *state, unsigned char *desc,
int nameid = desc[desc[0] - 1]; int nameid = desc[desc[0] - 1];
struct snd_kcontrol *kctl; struct snd_kcontrol *kctl;
struct usb_mixer_elem_info *cval; struct usb_mixer_elem_info *cval;
const struct usbmix_name_map *map;
control++; /* change from zero-based to 1-based value */ control++; /* change from zero-based to 1-based value */
...@@ -920,7 +937,8 @@ static void build_feature_ctl(struct mixer_build *state, unsigned char *desc, ...@@ -920,7 +937,8 @@ static void build_feature_ctl(struct mixer_build *state, unsigned char *desc,
return; return;
} }
if (check_ignored_ctl(state, unitid, control)) map = find_map(state, unitid, control);
if (check_ignored_ctl(map))
return; return;
cval = kzalloc(sizeof(*cval), GFP_KERNEL); cval = kzalloc(sizeof(*cval), GFP_KERNEL);
...@@ -954,10 +972,11 @@ static void build_feature_ctl(struct mixer_build *state, unsigned char *desc, ...@@ -954,10 +972,11 @@ static void build_feature_ctl(struct mixer_build *state, unsigned char *desc,
} }
kctl->private_free = usb_mixer_elem_free; 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; mapped_name = len != 0;
if (! len && nameid) 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) { switch (control) {
case USB_FEATURE_MUTE: case USB_FEATURE_MUTE:
...@@ -995,6 +1014,7 @@ static void build_feature_ctl(struct mixer_build *state, unsigned char *desc, ...@@ -995,6 +1014,7 @@ static void build_feature_ctl(struct mixer_build *state, unsigned char *desc,
kctl->vd[0].access |= kctl->vd[0].access |=
SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_TLV_READ |
SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
check_mapped_dB(map, cval);
} }
break; break;
...@@ -1122,8 +1142,10 @@ static void build_mixer_unit_ctl(struct mixer_build *state, unsigned char *desc, ...@@ -1122,8 +1142,10 @@ static void build_mixer_unit_ctl(struct mixer_build *state, unsigned char *desc,
unsigned int num_outs = desc[5 + input_pins]; unsigned int num_outs = desc[5 + input_pins];
unsigned int i, len; unsigned int i, len;
struct snd_kcontrol *kctl; 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; return;
cval = kzalloc(sizeof(*cval), GFP_KERNEL); cval = kzalloc(sizeof(*cval), GFP_KERNEL);
...@@ -1152,7 +1174,7 @@ static void build_mixer_unit_ctl(struct mixer_build *state, unsigned char *desc, ...@@ -1152,7 +1174,7 @@ static void build_mixer_unit_ctl(struct mixer_build *state, unsigned char *desc,
} }
kctl->private_free = usb_mixer_elem_free; 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) if (! len)
len = get_term_name(state, iterm, kctl->id.name, sizeof(kctl->id.name), 0); len = get_term_name(state, iterm, kctl->id.name, sizeof(kctl->id.name), 0);
if (! len) if (! len)
...@@ -1342,6 +1364,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned ...@@ -1342,6 +1364,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned
int i, err, nameid, type, len; int i, err, nameid, type, len;
struct procunit_info *info; struct procunit_info *info;
struct procunit_value_info *valinfo; struct procunit_value_info *valinfo;
const struct usbmix_name_map *map;
static struct procunit_value_info default_value_info[] = { static struct procunit_value_info default_value_info[] = {
{ 0x01, "Switch", USB_MIXER_BOOLEAN }, { 0x01, "Switch", USB_MIXER_BOOLEAN },
{ 0 } { 0 }
...@@ -1371,7 +1394,8 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned ...@@ -1371,7 +1394,8 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned
/* FIXME: bitmap might be longer than 8bit */ /* FIXME: bitmap might be longer than 8bit */
if (! (dsc[12 + num_ins] & (1 << (valinfo->control - 1)))) if (! (dsc[12 + num_ins] & (1 << (valinfo->control - 1))))
continue; continue;
if (check_ignored_ctl(state, unitid, valinfo->control)) map = find_map(state, unitid, valinfo->control);
if (check_ignored_ctl(map))
continue; continue;
cval = kzalloc(sizeof(*cval), GFP_KERNEL); cval = kzalloc(sizeof(*cval), GFP_KERNEL);
if (! cval) { if (! cval) {
...@@ -1402,8 +1426,9 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned ...@@ -1402,8 +1426,9 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned
} }
kctl->private_free = usb_mixer_elem_free; 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) else if (info->name)
strlcpy(kctl->id.name, info->name, sizeof(kctl->id.name)); strlcpy(kctl->id.name, info->name, sizeof(kctl->id.name));
else { else {
...@@ -1542,6 +1567,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, unsi ...@@ -1542,6 +1567,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, unsi
int err; int err;
struct usb_mixer_elem_info *cval; struct usb_mixer_elem_info *cval;
struct snd_kcontrol *kctl; struct snd_kcontrol *kctl;
const struct usbmix_name_map *map;
char **namelist; char **namelist;
if (! num_ins || desc[0] < 5 + num_ins) { if (! num_ins || desc[0] < 5 + num_ins) {
...@@ -1557,7 +1583,8 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, unsi ...@@ -1557,7 +1583,8 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, unsi
if (num_ins == 1) /* only one ? nonsense! */ if (num_ins == 1) /* only one ? nonsense! */
return 0; return 0;
if (check_ignored_ctl(state, unitid, 0)) map = find_map(state, unitid, 0);
if (check_ignored_ctl(map))
return 0; return 0;
cval = kzalloc(sizeof(*cval), GFP_KERNEL); cval = kzalloc(sizeof(*cval), GFP_KERNEL);
...@@ -1612,7 +1639,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, unsi ...@@ -1612,7 +1639,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, unsi
kctl->private_free = usb_mixer_selector_elem_free; kctl->private_free = usb_mixer_selector_elem_free;
nameid = desc[desc[0] - 1]; 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) if (len)
; ;
else if (nameid) else if (nameid)
......
...@@ -19,11 +19,16 @@ ...@@ -19,11 +19,16 @@
* *
*/ */
struct usbmix_dB_map {
u32 min;
u32 max;
};
struct usbmix_name_map { struct usbmix_name_map {
int id; int id;
const char *name; const char *name;
int control; int control;
struct usbmix_dB_map *dB;
}; };
struct usbmix_selector_map { struct usbmix_selector_map {
...@@ -72,7 +77,7 @@ static struct usbmix_name_map extigy_map[] = { ...@@ -72,7 +77,7 @@ static struct usbmix_name_map extigy_map[] = {
{ 8, "Line Playback" }, /* FU */ { 8, "Line Playback" }, /* FU */
/* 9: IT mic */ /* 9: IT mic */
{ 10, "Mic Playback" }, /* FU */ { 10, "Mic Playback" }, /* FU */
{ 11, "Capture Input Source" }, /* SU */ { 11, "Capture Source" }, /* SU */
{ 12, "Capture" }, /* FU */ { 12, "Capture" }, /* FU */
/* 13: OT pcm capture */ /* 13: OT pcm capture */
/* 14: MU (w/o controls) */ /* 14: MU (w/o controls) */
...@@ -102,6 +107,9 @@ static struct usbmix_name_map extigy_map[] = { ...@@ -102,6 +107,9 @@ static struct usbmix_name_map extigy_map[] = {
* e.g. no Master and fake PCM volume * e.g. no Master and fake PCM volume
* Pavel Mihaylov <bin@bash.info> * 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[] = { static struct usbmix_name_map mp3plus_map[] = {
/* 1: IT pcm */ /* 1: IT pcm */
/* 2: IT mic */ /* 2: IT mic */
...@@ -110,16 +118,19 @@ static struct usbmix_name_map mp3plus_map[] = { ...@@ -110,16 +118,19 @@ static struct usbmix_name_map mp3plus_map[] = {
/* 5: OT digital out */ /* 5: OT digital out */
/* 6: OT speaker */ /* 6: OT speaker */
/* 7: OT pcm capture */ /* 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) */ /* (Mic, Input 1 = Line input, Input 2 = Optical input) */
{ 9, "Master Playback" }, /* FU, default Speaker 1 */ { 9, "Master Playback" }, /* FU, default Speaker 1 */
/* { 10, "Mic Capture", 1 }, */ /* FU, Mic Capture */ /* { 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 */ { 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 */ { 12, "Digital In Playback" }, /* FU, default PCM 1 */
/* { 13, "Mic Playback" }, */ /* FU, default Mic Playback */ { 13, /* "Mic Playback", */ .dB = &mp3plus_dB_1 },
{ 14, "Line Playback" }, /* FU, default Speaker */ /* FU, default Mic Playback */
{ 14, "Line Playback", .dB = &mp3plus_dB_1 }, /* FU, default Speaker */
/* 15: MU */ /* 15: MU */
{ 0 } /* terminator */ { 0 } /* terminator */
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment