summaryrefslogtreecommitdiffstats
path: root/sound/usb/usbmixer.c
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2006-03-28 17:58:28 +0200
committerJaroslav Kysela <perex@suse.cz>2006-03-31 17:59:00 +0200
commit14790f1c73cfa4d4a22ac10b4501b4831380683c (patch)
tree7c5ac876676e480df59475fd99da746467bfa404 /sound/usb/usbmixer.c
parent0b2dcd5d6a9a3e27fdd67053e526388f9f2ea33b (diff)
downloadlinux-14790f1c73cfa4d4a22ac10b4501b4831380683c.tar.bz2
[ALSA] Test volume resolution of usb audio at initialization
Test the volume of usb audio whether actually it works and adjusts the resolution value according to it. Some USB audio devices report a lower resolution than it reacts. The only possible check is to write and read a volume value. Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/usb/usbmixer.c')
-rw-r--r--sound/usb/usbmixer.c37
1 files changed, 34 insertions, 3 deletions
diff --git a/sound/usb/usbmixer.c b/sound/usb/usbmixer.c
index 8d08b34a1cb5..ce86283ee0fa 100644
--- a/sound/usb/usbmixer.c
+++ b/sound/usb/usbmixer.c
@@ -306,8 +306,8 @@ static int get_relative_value(struct usb_mixer_elem_info *cval, int val)
cval->res = 1;
if (val < cval->min)
return 0;
- else if (val > cval->max)
- return (cval->max - cval->min) / cval->res;
+ else if (val >= cval->max)
+ return (cval->max - cval->min + cval->res - 1) / cval->res;
else
return (val - cval->min) / cval->res;
}
@@ -670,6 +670,36 @@ static int get_min_max(struct usb_mixer_elem_info *cval, int default_min)
}
if (cval->res == 0)
cval->res = 1;
+
+ /* Additional checks for the proper resolution
+ *
+ * Some devices report smaller resolutions than actually
+ * reacting. They don't return errors but simply clip
+ * to the lower aligned value.
+ */
+ if (cval->min + cval->res < cval->max) {
+ int last_valid_res = cval->res;
+ int saved, test, check;
+ get_cur_mix_value(cval, minchn, &saved);
+ for (;;) {
+ test = saved;
+ if (test < cval->max)
+ test += cval->res;
+ else
+ test -= cval->res;
+ if (test < cval->min || test > cval->max ||
+ set_cur_mix_value(cval, minchn, test) ||
+ get_cur_mix_value(cval, minchn, &check)) {
+ cval->res = last_valid_res;
+ break;
+ }
+ if (test == check)
+ break;
+ cval->res *= 2;
+ }
+ set_cur_mix_value(cval, minchn, saved);
+ }
+
cval->initialized = 1;
}
return 0;
@@ -695,7 +725,8 @@ static int mixer_ctl_feature_info(struct snd_kcontrol *kcontrol, struct snd_ctl_
if (! cval->initialized)
get_min_max(cval, 0);
uinfo->value.integer.min = 0;
- uinfo->value.integer.max = (cval->max - cval->min) / cval->res;
+ uinfo->value.integer.max =
+ (cval->max - cval->min + cval->res - 1) / cval->res;
}
return 0;
}