ALSA: usb-audio - Cache mixer values

Cache mixer values in usb-audio driver to reduce too excessive
accesses to the hardware.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Takashi Iwai 2009-01-15 17:05:24 +01:00
Родитель 37a76bd4f1
Коммит 641b487944
1 изменённых файлов: 70 добавлений и 52 удалений

Просмотреть файл

@ -110,6 +110,8 @@ struct mixer_build {
const struct usbmix_selector_map *selector_map; const struct usbmix_selector_map *selector_map;
}; };
#define MAX_CHANNELS 10 /* max logical channels */
struct usb_mixer_elem_info { struct usb_mixer_elem_info {
struct usb_mixer_interface *mixer; struct usb_mixer_interface *mixer;
struct usb_mixer_elem_info *next_id_elem; /* list of controls with same id */ 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 channels;
int val_type; int val_type;
int min, max, res; int min, max, res;
int cached;
int cache_val[MAX_CHANNELS];
u8 initialized; u8 initialized;
}; };
@ -181,8 +185,6 @@ enum {
USB_PROC_DCR_RELEASE = 6, USB_PROC_DCR_RELEASE = 6,
}; };
#define MAX_CHANNELS 10 /* max logical channels */
/* /*
* manual mapping of mixer names * 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 */ /* 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); 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 * 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); 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) { if (cval->min + cval->res < cval->max) {
int last_valid_res = cval->res; int last_valid_res = cval->res;
int saved, test, check; int saved, test, check;
get_cur_mix_value(cval, minchn, &saved); get_cur_mix_raw(cval, minchn, &saved);
for (;;) { for (;;) {
test = saved; test = saved;
if (test < cval->max) if (test < cval->max)
@ -726,8 +760,8 @@ static int get_min_max(struct usb_mixer_elem_info *cval, int default_min)
else else
test -= cval->res; test -= cval->res;
if (test < cval->min || test > cval->max || if (test < cval->min || test > cval->max ||
set_cur_mix_value(cval, minchn, test) || set_cur_mix_value(cval, minchn, 0, test) ||
get_cur_mix_value(cval, minchn, &check)) { get_cur_mix_raw(cval, minchn, &check)) {
cval->res = last_valid_res; cval->res = last_valid_res;
break; break;
} }
@ -735,7 +769,7 @@ static int get_min_max(struct usb_mixer_elem_info *cval, int default_min)
break; break;
cval->res *= 2; cval->res *= 2;
} }
set_cur_mix_value(cval, minchn, saved); set_cur_mix_value(cval, minchn, 0, saved);
} }
cval->initialized = 1; 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; struct usb_mixer_elem_info *cval = kcontrol->private_data;
int c, cnt, val, err; int c, cnt, val, err;
ucontrol->value.integer.value[0] = cval->min;
if (cval->cmask) { if (cval->cmask) {
cnt = 0; cnt = 0;
for (c = 0; c < MAX_CHANNELS; c++) { for (c = 0; c < MAX_CHANNELS; c++) {
if (cval->cmask & (1 << c)) { if (!(cval->cmask & (1 << c)))
err = get_cur_mix_value(cval, c + 1, &val); continue;
if (err < 0) { err = get_cur_mix_value(cval, c + 1, cnt, &val);
if (cval->mixer->ignore_ctl_error) { if (err < 0)
ucontrol->value.integer.value[0] = cval->min; return cval->mixer->ignore_ctl_error ? 0 : err;
return 0; val = get_relative_value(cval, val);
} ucontrol->value.integer.value[cnt] = val;
snd_printd(KERN_ERR "cannot get current value for control %d ch %d: err = %d\n", cval->control, c + 1, err); cnt++;
return err;
}
val = get_relative_value(cval, val);
ucontrol->value.integer.value[cnt] = val;
cnt++;
}
} }
return 0;
} else { } else {
/* master channel */ /* master channel */
err = get_cur_mix_value(cval, 0, &val); err = get_cur_mix_value(cval, 0, 0, &val);
if (err < 0) { if (err < 0)
if (cval->mixer->ignore_ctl_error) { return cval->mixer->ignore_ctl_error ? 0 : err;
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;
}
val = get_relative_value(cval, val); val = get_relative_value(cval, val);
ucontrol->value.integer.value[0] = 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) { if (cval->cmask) {
cnt = 0; cnt = 0;
for (c = 0; c < MAX_CHANNELS; c++) { for (c = 0; c < MAX_CHANNELS; c++) {
if (cval->cmask & (1 << c)) { if (!(cval->cmask & (1 << c)))
err = get_cur_mix_value(cval, c + 1, &oval); continue;
if (err < 0) { err = get_cur_mix_value(cval, c + 1, cnt, &oval);
if (cval->mixer->ignore_ctl_error) if (err < 0)
return 0; return cval->mixer->ignore_ctl_error ? 0 : err;
return err; val = ucontrol->value.integer.value[cnt];
} val = get_abs_value(cval, val);
val = ucontrol->value.integer.value[cnt]; if (oval != val) {
val = get_abs_value(cval, val); set_cur_mix_value(cval, c + 1, cnt, val);
if (oval != val) { changed = 1;
set_cur_mix_value(cval, c + 1, val);
changed = 1;
}
get_cur_mix_value(cval, c + 1, &val);
cnt++;
} }
cnt++;
} }
} else { } else {
/* master channel */ /* master channel */
err = get_cur_mix_value(cval, 0, &oval); err = get_cur_mix_value(cval, 0, 0, &oval);
if (err < 0 && cval->mixer->ignore_ctl_error)
return 0;
if (err < 0) if (err < 0)
return err; return cval->mixer->ignore_ctl_error ? 0 : err;
val = ucontrol->value.integer.value[0]; val = ucontrol->value.integer.value[0];
val = get_abs_value(cval, val); val = get_abs_value(cval, val);
if (val != oval) { if (val != oval) {
set_cur_mix_value(cval, 0, val); set_cur_mix_value(cval, 0, 0, val);
changed = 1; changed = 1;
} }
} }