Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 641b4879 authored by Takashi Iwai's avatar Takashi Iwai
Browse files

ALSA: usb-audio - Cache mixer values



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

Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 37a76bd4
Loading
Loading
Loading
Loading
+70 −52
Original line number Original line Diff line number Diff line
@@ -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;
					}
					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);
			val = get_relative_value(cval, val);
			ucontrol->value.integer.value[cnt] = val;
			ucontrol->value.integer.value[cnt] = val;
			cnt++;
			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 = ucontrol->value.integer.value[cnt];
			val = get_abs_value(cval, val);
			val = get_abs_value(cval, val);
			if (oval != val) {
			if (oval != val) {
					set_cur_mix_value(cval, c + 1, val);
				set_cur_mix_value(cval, c + 1, cnt, val);
				changed = 1;
				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;
		}
		}
	}
	}