|
@@ -110,6 +110,8 @@ struct mixer_build {
|
|
|
const struct usbmix_selector_map *selector_map;
|
|
|
};
|
|
|
|
|
|
+#define MAX_CHANNELS 10 /* max logical channels */
|
|
|
+
|
|
|
struct usb_mixer_elem_info {
|
|
|
struct usb_mixer_interface *mixer;
|
|
|
struct usb_mixer_elem_info *next_id_elem; /* list of controls with same id */
|
|
@@ -120,6 +122,8 @@ struct usb_mixer_elem_info {
|
|
|
int channels;
|
|
|
int val_type;
|
|
|
int min, max, res;
|
|
|
+ int cached;
|
|
|
+ int cache_val[MAX_CHANNELS];
|
|
|
u8 initialized;
|
|
|
};
|
|
|
|
|
@@ -181,8 +185,6 @@ enum {
|
|
|
USB_PROC_DCR_RELEASE = 6,
|
|
|
};
|
|
|
|
|
|
-#define MAX_CHANNELS 10 /* max logical channels */
|
|
|
-
|
|
|
|
|
|
/*
|
|
|
* manual mapping of mixer names
|
|
@@ -376,11 +378,35 @@ static int get_cur_ctl_value(struct usb_mixer_elem_info *cval, int validx, int *
|
|
|
}
|
|
|
|
|
|
/* channel = 0: master, 1 = first channel */
|
|
|
-static inline int get_cur_mix_value(struct usb_mixer_elem_info *cval, int channel, int *value)
|
|
|
+static inline int get_cur_mix_raw(struct usb_mixer_elem_info *cval,
|
|
|
+ int channel, int *value)
|
|
|
{
|
|
|
return get_ctl_value(cval, GET_CUR, (cval->control << 8) | channel, value);
|
|
|
}
|
|
|
|
|
|
+static int get_cur_mix_value(struct usb_mixer_elem_info *cval,
|
|
|
+ int channel, int index, int *value)
|
|
|
+{
|
|
|
+ int err;
|
|
|
+
|
|
|
+ if (cval->cached & (1 << channel)) {
|
|
|
+ *value = cval->cache_val[index];
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ err = get_cur_mix_raw(cval, channel, value);
|
|
|
+ if (err < 0) {
|
|
|
+ if (!cval->mixer->ignore_ctl_error)
|
|
|
+ snd_printd(KERN_ERR "cannot get current value for "
|
|
|
+ "control %d ch %d: err = %d\n",
|
|
|
+ cval->control, channel, err);
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+ cval->cached |= 1 << channel;
|
|
|
+ cval->cache_val[index] = *value;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
/*
|
|
|
* set a mixer value
|
|
|
*/
|
|
@@ -412,9 +438,17 @@ static int set_cur_ctl_value(struct usb_mixer_elem_info *cval, int validx, int v
|
|
|
return set_ctl_value(cval, SET_CUR, validx, value);
|
|
|
}
|
|
|
|
|
|
-static inline int set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel, int value)
|
|
|
+static int set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel,
|
|
|
+ int index, int value)
|
|
|
{
|
|
|
- return set_ctl_value(cval, SET_CUR, (cval->control << 8) | channel, value);
|
|
|
+ int err;
|
|
|
+ err = set_ctl_value(cval, SET_CUR, (cval->control << 8) | channel,
|
|
|
+ value);
|
|
|
+ if (err < 0)
|
|
|
+ return err;
|
|
|
+ cval->cached |= 1 << channel;
|
|
|
+ cval->cache_val[index] = value;
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -718,7 +752,7 @@ static int get_min_max(struct usb_mixer_elem_info *cval, int default_min)
|
|
|
if (cval->min + cval->res < cval->max) {
|
|
|
int last_valid_res = cval->res;
|
|
|
int saved, test, check;
|
|
|
- get_cur_mix_value(cval, minchn, &saved);
|
|
|
+ get_cur_mix_raw(cval, minchn, &saved);
|
|
|
for (;;) {
|
|
|
test = saved;
|
|
|
if (test < cval->max)
|
|
@@ -726,8 +760,8 @@ static int get_min_max(struct usb_mixer_elem_info *cval, int default_min)
|
|
|
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)) {
|
|
|
+ set_cur_mix_value(cval, minchn, 0, test) ||
|
|
|
+ get_cur_mix_raw(cval, minchn, &check)) {
|
|
|
cval->res = last_valid_res;
|
|
|
break;
|
|
|
}
|
|
@@ -735,7 +769,7 @@ static int get_min_max(struct usb_mixer_elem_info *cval, int default_min)
|
|
|
break;
|
|
|
cval->res *= 2;
|
|
|
}
|
|
|
- set_cur_mix_value(cval, minchn, saved);
|
|
|
+ set_cur_mix_value(cval, minchn, 0, saved);
|
|
|
}
|
|
|
|
|
|
cval->initialized = 1;
|
|
@@ -775,35 +809,25 @@ static int mixer_ctl_feature_get(struct snd_kcontrol *kcontrol, struct snd_ctl_e
|
|
|
struct usb_mixer_elem_info *cval = kcontrol->private_data;
|
|
|
int c, cnt, val, err;
|
|
|
|
|
|
+ ucontrol->value.integer.value[0] = cval->min;
|
|
|
if (cval->cmask) {
|
|
|
cnt = 0;
|
|
|
for (c = 0; c < MAX_CHANNELS; c++) {
|
|
|
- if (cval->cmask & (1 << c)) {
|
|
|
- err = get_cur_mix_value(cval, c + 1, &val);
|
|
|
- if (err < 0) {
|
|
|
- if (cval->mixer->ignore_ctl_error) {
|
|
|
- ucontrol->value.integer.value[0] = cval->min;
|
|
|
- return 0;
|
|
|
- }
|
|
|
- snd_printd(KERN_ERR "cannot get current value for control %d ch %d: err = %d\n", cval->control, c + 1, err);
|
|
|
- return err;
|
|
|
- }
|
|
|
- val = get_relative_value(cval, val);
|
|
|
- ucontrol->value.integer.value[cnt] = val;
|
|
|
- cnt++;
|
|
|
- }
|
|
|
+ if (!(cval->cmask & (1 << c)))
|
|
|
+ continue;
|
|
|
+ err = get_cur_mix_value(cval, c + 1, cnt, &val);
|
|
|
+ if (err < 0)
|
|
|
+ return cval->mixer->ignore_ctl_error ? 0 : err;
|
|
|
+ val = get_relative_value(cval, val);
|
|
|
+ ucontrol->value.integer.value[cnt] = val;
|
|
|
+ cnt++;
|
|
|
}
|
|
|
+ return 0;
|
|
|
} else {
|
|
|
/* master channel */
|
|
|
- err = get_cur_mix_value(cval, 0, &val);
|
|
|
- if (err < 0) {
|
|
|
- if (cval->mixer->ignore_ctl_error) {
|
|
|
- ucontrol->value.integer.value[0] = cval->min;
|
|
|
- return 0;
|
|
|
- }
|
|
|
- snd_printd(KERN_ERR "cannot get current value for control %d master ch: err = %d\n", cval->control, err);
|
|
|
- return err;
|
|
|
- }
|
|
|
+ err = get_cur_mix_value(cval, 0, 0, &val);
|
|
|
+ if (err < 0)
|
|
|
+ return cval->mixer->ignore_ctl_error ? 0 : err;
|
|
|
val = get_relative_value(cval, val);
|
|
|
ucontrol->value.integer.value[0] = val;
|
|
|
}
|
|
@@ -820,34 +844,28 @@ static int mixer_ctl_feature_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
|
|
|
if (cval->cmask) {
|
|
|
cnt = 0;
|
|
|
for (c = 0; c < MAX_CHANNELS; c++) {
|
|
|
- if (cval->cmask & (1 << c)) {
|
|
|
- err = get_cur_mix_value(cval, c + 1, &oval);
|
|
|
- if (err < 0) {
|
|
|
- if (cval->mixer->ignore_ctl_error)
|
|
|
- return 0;
|
|
|
- return err;
|
|
|
- }
|
|
|
- val = ucontrol->value.integer.value[cnt];
|
|
|
- val = get_abs_value(cval, val);
|
|
|
- if (oval != val) {
|
|
|
- set_cur_mix_value(cval, c + 1, val);
|
|
|
- changed = 1;
|
|
|
- }
|
|
|
- get_cur_mix_value(cval, c + 1, &val);
|
|
|
- cnt++;
|
|
|
+ if (!(cval->cmask & (1 << c)))
|
|
|
+ continue;
|
|
|
+ err = get_cur_mix_value(cval, c + 1, cnt, &oval);
|
|
|
+ if (err < 0)
|
|
|
+ return cval->mixer->ignore_ctl_error ? 0 : err;
|
|
|
+ val = ucontrol->value.integer.value[cnt];
|
|
|
+ val = get_abs_value(cval, val);
|
|
|
+ if (oval != val) {
|
|
|
+ set_cur_mix_value(cval, c + 1, cnt, val);
|
|
|
+ changed = 1;
|
|
|
}
|
|
|
+ cnt++;
|
|
|
}
|
|
|
} else {
|
|
|
/* master channel */
|
|
|
- err = get_cur_mix_value(cval, 0, &oval);
|
|
|
- if (err < 0 && cval->mixer->ignore_ctl_error)
|
|
|
- return 0;
|
|
|
+ err = get_cur_mix_value(cval, 0, 0, &oval);
|
|
|
if (err < 0)
|
|
|
- return err;
|
|
|
+ return cval->mixer->ignore_ctl_error ? 0 : err;
|
|
|
val = ucontrol->value.integer.value[0];
|
|
|
val = get_abs_value(cval, val);
|
|
|
if (val != oval) {
|
|
|
- set_cur_mix_value(cval, 0, val);
|
|
|
+ set_cur_mix_value(cval, 0, 0, val);
|
|
|
changed = 1;
|
|
|
}
|
|
|
}
|