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

Commit 0b4e9cfc authored by Takashi Iwai's avatar Takashi Iwai
Browse files

ALSA: usb-audio: Add resume support for FTU controls



A few FTU mixer controls have the own value handling, so they have to
be rewritten to follow the support for resume callbacks.  This ended
up in a fair amount of refactoring.  Its own struct is now removed,
instead the values are embedded in kctl private_value totally.

Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent da6d2769
Loading
Loading
Loading
Loading
+54 −140
Original line number Diff line number Diff line
@@ -892,14 +892,6 @@ static int snd_nativeinstruments_create_mixer(struct usb_mixer_interface *mixer,

/* M-Audio FastTrack Ultra quirks */
/* FTU Effect switch (also used by C400/C600) */
struct snd_ftu_eff_switch_priv_val {
	struct usb_mixer_interface *mixer;
	int cached_value;
	int is_cached;
	int bUnitID;
	int validx;
};

static int snd_ftu_eff_switch_info(struct snd_kcontrol *kcontrol,
					struct snd_ctl_elem_info *uinfo)
{
@@ -911,138 +903,77 @@ static int snd_ftu_eff_switch_info(struct snd_kcontrol *kcontrol,
	return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts);
}

static int snd_ftu_eff_switch_get(struct snd_kcontrol *kctl,
					struct snd_ctl_elem_value *ucontrol)
static int snd_ftu_eff_switch_init(struct usb_mixer_interface *mixer,
				   struct snd_kcontrol *kctl)
{
	struct snd_usb_audio *chip;
	struct usb_mixer_interface *mixer;
	struct snd_ftu_eff_switch_priv_val *pval;
	struct usb_device *dev = mixer->chip->dev;
	unsigned int pval = kctl->private_value;
	int err;
	unsigned char value[2];
	int id, validx;

	const int val_len = 2;

	value[0] = 0x00;
	value[1] = 0x00;

	pval = (struct snd_ftu_eff_switch_priv_val *)
		kctl->private_value;

	if (pval->is_cached) {
		ucontrol->value.enumerated.item[0] = pval->cached_value;
		return 0;
	}

	mixer = (struct usb_mixer_interface *) pval->mixer;
	if (snd_BUG_ON(!mixer))
		return -EINVAL;

	chip = (struct snd_usb_audio *) mixer->chip;
	if (snd_BUG_ON(!chip))
		return -EINVAL;

	id = pval->bUnitID;
	validx = pval->validx;

	down_read(&mixer->chip->shutdown_rwsem);
	if (mixer->chip->shutdown)
		err = -ENODEV;
	else
		err = snd_usb_ctl_msg(chip->dev,
			usb_rcvctrlpipe(chip->dev, 0), UAC_GET_CUR,
	err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR,
			      USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
			validx << 8, snd_usb_ctrl_intf(chip) | (id << 8),
			value, val_len);
	up_read(&mixer->chip->shutdown_rwsem);
			      pval & 0xff00,
			      snd_usb_ctrl_intf(mixer->chip) | ((pval & 0xff) << 8),
			      value, 2);
	if (err < 0)
		return err;

	ucontrol->value.enumerated.item[0] = value[0];
	pval->cached_value = value[0];
	pval->is_cached = 1;

	kctl->private_value |= value[0] << 24;
	return 0;
}

static int snd_ftu_eff_switch_put(struct snd_kcontrol *kctl,
static int snd_ftu_eff_switch_get(struct snd_kcontrol *kctl,
					struct snd_ctl_elem_value *ucontrol)
{
	struct snd_usb_audio *chip;
	struct snd_ftu_eff_switch_priv_val *pval;
	ucontrol->value.enumerated.item[0] = kctl->private_value >> 24;
	return 0;
}

	struct usb_mixer_interface *mixer;
	int changed, cur_val, err, new_val;
static int snd_ftu_eff_switch_update(struct usb_mixer_elem_list *list)
{
	struct snd_usb_audio *chip = list->mixer->chip;
	unsigned int pval = list->kctl->private_value;
	unsigned char value[2];
	int id, validx;

	const int val_len = 2;

	changed = 0;

	pval = (struct snd_ftu_eff_switch_priv_val *)
		kctl->private_value;
	cur_val = pval->cached_value;
	new_val = ucontrol->value.enumerated.item[0];

	mixer = (struct usb_mixer_interface *) pval->mixer;
	if (snd_BUG_ON(!mixer))
		return -EINVAL;

	chip = (struct snd_usb_audio *) mixer->chip;
	if (snd_BUG_ON(!chip))
		return -EINVAL;

	id = pval->bUnitID;
	validx = pval->validx;

	if (!pval->is_cached) {
		/* Read current value */
		down_read(&mixer->chip->shutdown_rwsem);
		if (mixer->chip->shutdown)
			err = -ENODEV;
		else
			err = snd_usb_ctl_msg(chip->dev,
				usb_rcvctrlpipe(chip->dev, 0), UAC_GET_CUR,
				USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
				validx << 8, snd_usb_ctrl_intf(chip) | (id << 8),
				value, val_len);
		up_read(&mixer->chip->shutdown_rwsem);
		if (err < 0)
			return err;
	int err;

		cur_val = value[0];
		pval->cached_value = cur_val;
		pval->is_cached = 1;
	}
	/* update value if needed */
	if (cur_val != new_val) {
		value[0] = new_val;
	value[0] = pval >> 24;
	value[1] = 0;
		down_read(&mixer->chip->shutdown_rwsem);
		if (mixer->chip->shutdown)

	down_read(&chip->shutdown_rwsem);
	if (chip->shutdown)
		err = -ENODEV;
	else
		err = snd_usb_ctl_msg(chip->dev,
				usb_sndctrlpipe(chip->dev, 0), UAC_SET_CUR,
				      usb_sndctrlpipe(chip->dev, 0),
				      UAC_SET_CUR,
				      USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
				validx << 8, snd_usb_ctrl_intf(chip) | (id << 8),
				value, val_len);
		up_read(&mixer->chip->shutdown_rwsem);
		if (err < 0)
				      pval & 0xff00,
				      snd_usb_ctrl_intf(chip) | ((pval & 0xff) << 8),
				      value, 2);
	up_read(&chip->shutdown_rwsem);
	return err;

		pval->cached_value = new_val;
		pval->is_cached = 1;
		changed = 1;
	}

	return changed;
}

static void kctl_private_value_free(struct snd_kcontrol *kctl)
static int snd_ftu_eff_switch_put(struct snd_kcontrol *kctl,
					struct snd_ctl_elem_value *ucontrol)
{
	kfree((void *)kctl->private_value);
	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl);
	unsigned int pval = list->kctl->private_value;
	int cur_val, err, new_val;

	cur_val = pval >> 24;
	new_val = ucontrol->value.enumerated.item[0];
	if (cur_val == new_val)
		return 0;

	kctl->private_value &= ~(0xff << 24);
	kctl->private_value |= new_val << 24;
	err = snd_ftu_eff_switch_update(list);
	return err < 0 ? err : 1;
}

static int snd_ftu_create_effect_switch(struct usb_mixer_interface *mixer,
@@ -1057,33 +988,16 @@ static int snd_ftu_create_effect_switch(struct usb_mixer_interface *mixer,
		.get = snd_ftu_eff_switch_get,
		.put = snd_ftu_eff_switch_put
	};

	struct usb_mixer_elem_list *list;
	int err;
	struct snd_kcontrol *kctl;
	struct snd_ftu_eff_switch_priv_val *pval;

	pval = kzalloc(sizeof(*pval), GFP_KERNEL);
	if (!pval)
		return -ENOMEM;

	pval->cached_value = 0;
	pval->is_cached = 0;
	pval->mixer = mixer;
	pval->bUnitID = bUnitID;
	pval->validx = validx;

	template.private_value = (unsigned long) pval;
	kctl = snd_ctl_new1(&template, mixer->chip);
	if (!kctl) {
		kfree(pval);
		return -ENOMEM;
	}

	kctl->private_free = kctl_private_value_free;
	err = snd_ctl_add(mixer->chip->card, kctl);
	err = add_single_ctl_with_resume(mixer, bUnitID,
					 snd_ftu_eff_switch_update,
					 &template, &list);
	if (err < 0)
		return err;

	list->kctl->private_value = (validx << 8) | bUnitID;
	snd_ftu_eff_switch_init(mixer, list->kctl);
	return 0;
}