[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>
diff --git a/sound/usb/usbmixer.c b/sound/usb/usbmixer.c
index 8d08b34..ce86283 100644
--- a/sound/usb/usbmixer.c
+++ b/sound/usb/usbmixer.c
@@ -306,8 +306,8 @@
 		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 @@
 		}
 		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 @@
 		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;
 }