ALSA: usb-audio: Add resume support for MicroII SPDIF ctls

Like the previous fixes, the mixer accessors are converted to use
usb_mixer_elem_list objects.  In addition, the proper shutdown check
are put in get and put callbacks.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Takashi Iwai 2014-11-18 18:06:17 +01:00
parent 0b4e9cfcef
commit 288673beae

View file

@ -1509,7 +1509,8 @@ static int snd_microii_spdif_info(struct snd_kcontrol *kcontrol,
static int snd_microii_spdif_default_get(struct snd_kcontrol *kcontrol, static int snd_microii_spdif_default_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) struct snd_ctl_elem_value *ucontrol)
{ {
struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
struct snd_usb_audio *chip = list->mixer->chip;
int err; int err;
struct usb_interface *iface; struct usb_interface *iface;
struct usb_host_interface *alts; struct usb_host_interface *alts;
@ -1517,17 +1518,23 @@ static int snd_microii_spdif_default_get(struct snd_kcontrol *kcontrol,
unsigned char data[3]; unsigned char data[3];
int rate; int rate;
down_read(&chip->shutdown_rwsem);
if (chip->shutdown) {
err = -ENODEV;
goto end;
}
ucontrol->value.iec958.status[0] = kcontrol->private_value & 0xff; ucontrol->value.iec958.status[0] = kcontrol->private_value & 0xff;
ucontrol->value.iec958.status[1] = (kcontrol->private_value >> 8) & 0xff; ucontrol->value.iec958.status[1] = (kcontrol->private_value >> 8) & 0xff;
ucontrol->value.iec958.status[2] = 0x00; ucontrol->value.iec958.status[2] = 0x00;
/* use known values for that card: interface#1 altsetting#1 */ /* use known values for that card: interface#1 altsetting#1 */
iface = usb_ifnum_to_if(mixer->chip->dev, 1); iface = usb_ifnum_to_if(chip->dev, 1);
alts = &iface->altsetting[1]; alts = &iface->altsetting[1];
ep = get_endpoint(alts, 0)->bEndpointAddress; ep = get_endpoint(alts, 0)->bEndpointAddress;
err = snd_usb_ctl_msg(mixer->chip->dev, err = snd_usb_ctl_msg(chip->dev,
usb_rcvctrlpipe(mixer->chip->dev, 0), usb_rcvctrlpipe(chip->dev, 0),
UAC_GET_CUR, UAC_GET_CUR,
USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_IN, USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_IN,
UAC_EP_CS_ATTR_SAMPLE_RATE << 8, UAC_EP_CS_ATTR_SAMPLE_RATE << 8,
@ -1542,22 +1549,27 @@ static int snd_microii_spdif_default_get(struct snd_kcontrol *kcontrol,
IEC958_AES3_CON_FS_48000 : IEC958_AES3_CON_FS_44100; IEC958_AES3_CON_FS_48000 : IEC958_AES3_CON_FS_44100;
err = 0; err = 0;
end: end:
up_read(&chip->shutdown_rwsem);
return err; return err;
} }
static int snd_microii_spdif_default_put(struct snd_kcontrol *kcontrol, static int snd_microii_spdif_default_update(struct usb_mixer_elem_list *list)
struct snd_ctl_elem_value *ucontrol)
{ {
struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); struct snd_usb_audio *chip = list->mixer->chip;
int err; unsigned int pval = list->kctl->private_value;
u8 reg; u8 reg;
unsigned long priv_backup = kcontrol->private_value; int err;
reg = ((ucontrol->value.iec958.status[1] & 0x0f) << 4) | down_read(&chip->shutdown_rwsem);
(ucontrol->value.iec958.status[0] & 0x0f); if (chip->shutdown) {
err = snd_usb_ctl_msg(mixer->chip->dev, err = -ENODEV;
usb_sndctrlpipe(mixer->chip->dev, 0), goto end;
}
reg = ((pval >> 4) & 0xf0) | (pval & 0x0f);
err = snd_usb_ctl_msg(chip->dev,
usb_sndctrlpipe(chip->dev, 0),
UAC_SET_CUR, UAC_SET_CUR,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
reg, reg,
@ -1567,15 +1579,10 @@ static int snd_microii_spdif_default_put(struct snd_kcontrol *kcontrol,
if (err < 0) if (err < 0)
goto end; goto end;
kcontrol->private_value &= 0xfffff0f0; reg = (pval & IEC958_AES0_NONAUDIO) ? 0xa0 : 0x20;
kcontrol->private_value |= (ucontrol->value.iec958.status[1] & 0x0f) << 8; reg |= (pval >> 12) & 0x0f;
kcontrol->private_value |= (ucontrol->value.iec958.status[0] & 0x0f); err = snd_usb_ctl_msg(chip->dev,
usb_sndctrlpipe(chip->dev, 0),
reg = (ucontrol->value.iec958.status[0] & IEC958_AES0_NONAUDIO) ?
0xa0 : 0x20;
reg |= (ucontrol->value.iec958.status[1] >> 4) & 0x0f;
err = snd_usb_ctl_msg(mixer->chip->dev,
usb_sndctrlpipe(mixer->chip->dev, 0),
UAC_SET_CUR, UAC_SET_CUR,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
reg, reg,
@ -1585,16 +1592,36 @@ static int snd_microii_spdif_default_put(struct snd_kcontrol *kcontrol,
if (err < 0) if (err < 0)
goto end; goto end;
kcontrol->private_value &= 0xffff0fff; end:
kcontrol->private_value |= (ucontrol->value.iec958.status[1] & 0xf0) << 8; up_read(&chip->shutdown_rwsem);
return err;
}
static int snd_microii_spdif_default_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
unsigned int pval, pval_old;
int err;
pval = pval_old = kcontrol->private_value;
pval &= 0xfffff0f0;
pval |= (ucontrol->value.iec958.status[1] & 0x0f) << 8;
pval |= (ucontrol->value.iec958.status[0] & 0x0f);
pval &= 0xffff0fff;
pval |= (ucontrol->value.iec958.status[1] & 0xf0) << 8;
/* The frequency bits in AES3 cannot be set via register access. */ /* The frequency bits in AES3 cannot be set via register access. */
/* Silently ignore any bits from the request that cannot be set. */ /* Silently ignore any bits from the request that cannot be set. */
err = (priv_backup != kcontrol->private_value); if (pval == pval_old)
end: return 0;
return err;
kcontrol->private_value = pval;
err = snd_microii_spdif_default_update(list);
return err < 0 ? err : 1;
} }
static int snd_microii_spdif_mask_get(struct snd_kcontrol *kcontrol, static int snd_microii_spdif_mask_get(struct snd_kcontrol *kcontrol,
@ -1616,15 +1643,20 @@ static int snd_microii_spdif_switch_get(struct snd_kcontrol *kcontrol,
return 0; return 0;
} }
static int snd_microii_spdif_switch_put(struct snd_kcontrol *kcontrol, static int snd_microii_spdif_switch_update(struct usb_mixer_elem_list *list)
struct snd_ctl_elem_value *ucontrol)
{ {
struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); struct snd_usb_audio *chip = list->mixer->chip;
u8 reg = list->kctl->private_value;
int err; int err;
u8 reg = ucontrol->value.integer.value[0] ? 0x28 : 0x2a;
err = snd_usb_ctl_msg(mixer->chip->dev, down_read(&chip->shutdown_rwsem);
usb_sndctrlpipe(mixer->chip->dev, 0), if (chip->shutdown) {
err = -ENODEV;
goto end;
}
err = snd_usb_ctl_msg(chip->dev,
usb_sndctrlpipe(chip->dev, 0),
UAC_SET_CUR, UAC_SET_CUR,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
reg, reg,
@ -1632,15 +1664,27 @@ static int snd_microii_spdif_switch_put(struct snd_kcontrol *kcontrol,
NULL, NULL,
0); 0);
if (!err) { end:
err = (reg != (kcontrol->private_value & 0x0ff)); up_read(&chip->shutdown_rwsem);
if (err)
kcontrol->private_value = reg;
}
return err; return err;
} }
static int snd_microii_spdif_switch_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
u8 reg;
int err;
reg = ucontrol->value.integer.value[0] ? 0x28 : 0x2a;
if (reg != list->kctl->private_value)
return 0;
kcontrol->private_value = reg;
err = snd_microii_spdif_switch_update(list);
return err < 0 ? err : 1;
}
static struct snd_kcontrol_new snd_microii_mixer_spdif[] = { static struct snd_kcontrol_new snd_microii_mixer_spdif[] = {
{ {
.iface = SNDRV_CTL_ELEM_IFACE_PCM, .iface = SNDRV_CTL_ELEM_IFACE_PCM,
@ -1670,10 +1714,17 @@ static struct snd_kcontrol_new snd_microii_mixer_spdif[] = {
static int snd_microii_controls_create(struct usb_mixer_interface *mixer) static int snd_microii_controls_create(struct usb_mixer_interface *mixer)
{ {
int err, i; int err, i;
static usb_mixer_elem_resume_func_t resume_funcs[] = {
snd_microii_spdif_default_update,
NULL,
snd_microii_spdif_switch_update
};
for (i = 0; i < ARRAY_SIZE(snd_microii_mixer_spdif); ++i) { for (i = 0; i < ARRAY_SIZE(snd_microii_mixer_spdif); ++i) {
err = snd_ctl_add(mixer->chip->card, err = add_single_ctl_with_resume(mixer, 0,
snd_ctl_new1(&snd_microii_mixer_spdif[i], mixer)); resume_funcs[i],
&snd_microii_mixer_spdif[i],
NULL);
if (err < 0) if (err < 0)
return err; return err;
} }