[alsa-devel] [PATCH RFC 00/10] Add proper resume support to USB-audio quirks
Hi,
here is a patch series to add proper resume support to USB-audio quirks. So far, the control elements created directly by quirk functions aren't resumed restore_mixer_value(). This patch series try to address it.
[PATCH RFC 01/10] ALSA: usb-audio: Refactor ignore_ctl_error checks [PATCH RFC 02/10] ALSA: usb-audio: Allow quirks to handle own resume [PATCH RFC 03/10] ALSA: usb-audio: Add audigy2nx resume support [PATCH RFC 04/10] ALSA: usb-audio: Add Emu0204 channel switch resume [PATCH RFC 05/10] ALSA: usb-audio: Add Xonar U1 resume support [PATCH RFC 06/10] ALSA: usb-audio: Add Digidesign Mbox 1 resume [PATCH RFC 07/10] ALSA: usb-audio: Add resume support for Native [PATCH RFC 08/10] ALSA: usb-audio: Add resume support for FTU [PATCH RFC 09/10] ALSA: usb-audio: Add resume support for MicroII [PATCH RFC 10/10] ALSA: usb-audio: Add resume support for Scarlett
I have only compile-tested this, so far. It'd be appreciated if anyone with such devices can test with real devices.
thanks,
Takashi
Introduce an internal helper macro for avoiding many open codes.
The only slight behavior change is in a couple of get ballcks where the value is reset at error no matter whether ignore_ctl_error is set or not. Actually this is even safer than before.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/usb/mixer.c | 39 ++++++++++++++++----------------------- 1 file changed, 16 insertions(+), 23 deletions(-)
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 14e1455ca9de..7756fcf877e0 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -136,6 +136,10 @@ check_mapped_name(const struct usbmix_name_map *p, char *buf, int buflen) return strlcpy(buf, p->name, buflen); }
+/* ignore the error value if ignore_ctl_error flag is set */ +#define filter_error(cval, err) \ + ((cval)->mixer->ignore_ctl_error ? 0 : (err)) + /* check whether the control should be ignored */ static inline int check_ignored_ctl(const struct usbmix_name_map *p) @@ -1088,7 +1092,7 @@ static int mixer_ctl_feature_get(struct snd_kcontrol *kcontrol, continue; err = snd_usb_get_cur_mix_value(cval, c + 1, cnt, &val); if (err < 0) - return cval->mixer->ignore_ctl_error ? 0 : err; + return filter_error(cval, err); val = get_relative_value(cval, val); ucontrol->value.integer.value[cnt] = val; cnt++; @@ -1098,7 +1102,7 @@ static int mixer_ctl_feature_get(struct snd_kcontrol *kcontrol, /* master channel */ err = snd_usb_get_cur_mix_value(cval, 0, 0, &val); if (err < 0) - return cval->mixer->ignore_ctl_error ? 0 : err; + return filter_error(cval, err); val = get_relative_value(cval, val); ucontrol->value.integer.value[0] = val; } @@ -1120,7 +1124,7 @@ static int mixer_ctl_feature_put(struct snd_kcontrol *kcontrol, continue; err = snd_usb_get_cur_mix_value(cval, c + 1, cnt, &oval); if (err < 0) - return cval->mixer->ignore_ctl_error ? 0 : err; + return filter_error(cval, err); val = ucontrol->value.integer.value[cnt]; val = get_abs_value(cval, val); if (oval != val) { @@ -1133,7 +1137,7 @@ static int mixer_ctl_feature_put(struct snd_kcontrol *kcontrol, /* master channel */ err = snd_usb_get_cur_mix_value(cval, 0, 0, &oval); if (err < 0) - return cval->mixer->ignore_ctl_error ? 0 : err; + return filter_error(cval, err); val = ucontrol->value.integer.value[0]; val = get_abs_value(cval, val); if (val != oval) { @@ -1628,12 +1632,10 @@ static int mixer_ctl_procunit_get(struct snd_kcontrol *kcontrol, int err, val;
err = get_cur_ctl_value(cval, cval->control << 8, &val); - if (err < 0 && cval->mixer->ignore_ctl_error) { + if (err < 0) { ucontrol->value.integer.value[0] = cval->min; - return 0; + return filter_error(cval, err); } - if (err < 0) - return err; val = get_relative_value(cval, val); ucontrol->value.integer.value[0] = val; return 0; @@ -1647,11 +1649,8 @@ static int mixer_ctl_procunit_put(struct snd_kcontrol *kcontrol, int val, oval, err;
err = get_cur_ctl_value(cval, cval->control << 8, &oval); - if (err < 0) { - if (cval->mixer->ignore_ctl_error) - return 0; - return err; - } + if (err < 0) + return filter_error(cval, err); val = ucontrol->value.integer.value[0]; val = get_abs_value(cval, val); if (val != oval) { @@ -1923,11 +1922,8 @@ static int mixer_ctl_selector_get(struct snd_kcontrol *kcontrol,
err = get_cur_ctl_value(cval, cval->control << 8, &val); if (err < 0) { - if (cval->mixer->ignore_ctl_error) { - ucontrol->value.enumerated.item[0] = 0; - return 0; - } - return err; + ucontrol->value.enumerated.item[0] = 0; + return filter_error(cval, err); } val = get_relative_value(cval, val); ucontrol->value.enumerated.item[0] = val; @@ -1942,11 +1938,8 @@ static int mixer_ctl_selector_put(struct snd_kcontrol *kcontrol, int val, oval, err;
err = get_cur_ctl_value(cval, cval->control << 8, &oval); - if (err < 0) { - if (cval->mixer->ignore_ctl_error) - return 0; - return err; - } + if (err < 0) + return filter_error(cval, err); val = ucontrol->value.enumerated.item[0]; val = get_abs_value(cval, val); if (val != oval) {
So far, we blindly assumed that the all usb-audio mixer elements follow the standard and apply the standard resume method for the registered elements in the id_elems[] list. However, some quirks really need the own resume and it's incomplete for now.
This patch enhances the resume handling in two folds: - split some fields in struct usb_mixer_elem_info into a smaller header struct (usb_mixer_elem_list) for keeping the minimal information in the linked-list; the usb_mixer_elem_info embeds this header struct instead - add resume and dump callbacks to usb_mixer_elem_list struct to allow quirks providing the own methods
For the standard mixer elements, these new callbacks are set to the standard ones as default, thus there is no functional change by this patch yet.
The dump and resume callbacks are typedef'ed for ease of later patches using arrays of such function pointers.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/usb/mixer.c | 156 +++++++++++++++++++++++++-------------------- sound/usb/mixer.h | 26 ++++++-- sound/usb/mixer_quirks.c | 12 +--- sound/usb/mixer_scarlett.c | 8 +-- 4 files changed, 114 insertions(+), 88 deletions(-)
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 7756fcf877e0..7beb0939790b 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -138,7 +138,7 @@ check_mapped_name(const struct usbmix_name_map *p, char *buf, int buflen)
/* ignore the error value if ignore_ctl_error flag is set */ #define filter_error(cval, err) \ - ((cval)->mixer->ignore_ctl_error ? 0 : (err)) + ((cval)->head.mixer->ignore_ctl_error ? 0 : (err))
/* check whether the control should be ignored */ static inline int @@ -290,13 +290,13 @@ static int get_abs_value(struct usb_mixer_elem_info *cval, int val) static int get_ctl_value_v1(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret) { - struct snd_usb_audio *chip = cval->mixer->chip; + struct snd_usb_audio *chip = cval->head.mixer->chip; unsigned char buf[2]; int val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1; int timeout = 10; int idx = 0, err;
- err = snd_usb_autoresume(cval->mixer->chip); + err = snd_usb_autoresume(chip); if (err < 0) return -EIO;
@@ -304,7 +304,7 @@ static int get_ctl_value_v1(struct usb_mixer_elem_info *cval, int request, while (timeout-- > 0) { if (chip->shutdown) break; - idx = snd_usb_ctrl_intf(chip) | (cval->id << 8); + idx = snd_usb_ctrl_intf(chip) | (cval->head.id << 8); if (snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), request, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, validx, idx, buf, val_len) >= val_len) { @@ -320,14 +320,14 @@ static int get_ctl_value_v1(struct usb_mixer_elem_info *cval, int request,
out: up_read(&chip->shutdown_rwsem); - snd_usb_autosuspend(cval->mixer->chip); + snd_usb_autosuspend(chip); return err; }
static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret) { - struct snd_usb_audio *chip = cval->mixer->chip; + struct snd_usb_audio *chip = cval->head.mixer->chip; unsigned char buf[2 + 3 * sizeof(__u16)]; /* enough space for one range */ unsigned char *val; int idx = 0, ret, size; @@ -351,7 +351,7 @@ static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request, if (chip->shutdown) { ret = -ENODEV; } else { - idx = snd_usb_ctrl_intf(chip) | (cval->id << 8); + idx = snd_usb_ctrl_intf(chip) | (cval->head.id << 8); ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), bRequest, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, validx, idx, buf, size); @@ -396,7 +396,7 @@ static int get_ctl_value(struct usb_mixer_elem_info *cval, int request, { validx += cval->idx_off;
- return (cval->mixer->protocol == UAC_VERSION_1) ? + return (cval->head.mixer->protocol == UAC_VERSION_1) ? get_ctl_value_v1(cval, request, validx, value_ret) : get_ctl_value_v2(cval, request, validx, value_ret); } @@ -427,8 +427,8 @@ int snd_usb_get_cur_mix_value(struct usb_mixer_elem_info *cval, } err = get_cur_mix_raw(cval, channel, value); if (err < 0) { - if (!cval->mixer->ignore_ctl_error) - usb_audio_dbg(cval->mixer->chip, + if (!cval->head.mixer->ignore_ctl_error) + usb_audio_dbg(cval->head.mixer->chip, "cannot get current value for control %d ch %d: err = %d\n", cval->control, channel, err); return err; @@ -445,13 +445,13 @@ int snd_usb_get_cur_mix_value(struct usb_mixer_elem_info *cval, int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval, int request, int validx, int value_set) { - struct snd_usb_audio *chip = cval->mixer->chip; + struct snd_usb_audio *chip = cval->head.mixer->chip; unsigned char buf[2]; int idx = 0, val_len, err, timeout = 10;
validx += cval->idx_off;
- if (cval->mixer->protocol == UAC_VERSION_1) { + if (cval->head.mixer->protocol == UAC_VERSION_1) { val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1; } else { /* UAC_VERSION_2 */ /* audio class v2 controls are always 2 bytes in size */ @@ -476,7 +476,7 @@ int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval, while (timeout-- > 0) { if (chip->shutdown) break; - idx = snd_usb_ctrl_intf(chip) | (cval->id << 8); + idx = snd_usb_ctrl_intf(chip) | (cval->head.id << 8); if (snd_usb_ctl_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0), request, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, @@ -510,7 +510,7 @@ int snd_usb_set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel, cval->ch_readonly & (1 << (channel - 1));
if (read_only) { - usb_audio_dbg(cval->mixer->chip, + usb_audio_dbg(cval->head.mixer->chip, "%s(): channel %d of control %d is read_only\n", __func__, channel, cval->control); return 0; @@ -569,10 +569,10 @@ static int check_matrix_bitmap(unsigned char *bmap, * if failed, give up and free the control instance. */
-int snd_usb_mixer_add_control(struct usb_mixer_interface *mixer, +int snd_usb_mixer_add_control(struct usb_mixer_elem_list *list, struct snd_kcontrol *kctl) { - struct usb_mixer_elem_info *cval = kctl->private_data; + struct usb_mixer_interface *mixer = list->mixer; int err;
while (snd_ctl_find_id(mixer->chip->card, &kctl->id)) @@ -582,9 +582,9 @@ int snd_usb_mixer_add_control(struct usb_mixer_interface *mixer, err); return err; } - cval->elem_id = &kctl->id; - cval->next_id_elem = mixer->id_elems[cval->id]; - mixer->id_elems[cval->id] = cval; + list->kctl = kctl; + list->next_id_elem = mixer->id_elems[list->id]; + mixer->id_elems[list->id] = list; return 0; }
@@ -833,7 +833,7 @@ void snd_usb_mixer_elem_free(struct snd_kcontrol *kctl) static void volume_control_quirks(struct usb_mixer_elem_info *cval, struct snd_kcontrol *kctl) { - struct snd_usb_audio *chip = cval->mixer->chip; + struct snd_usb_audio *chip = cval->head.mixer->chip; switch (chip->usb_id) { case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */ case USB_ID(0x0763, 0x2031): /* M-Audio Fast Track C600 */ @@ -958,10 +958,10 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval, } if (get_ctl_value(cval, UAC_GET_MAX, (cval->control << 8) | minchn, &cval->max) < 0 || get_ctl_value(cval, UAC_GET_MIN, (cval->control << 8) | minchn, &cval->min) < 0) { - usb_audio_err(cval->mixer->chip, + usb_audio_err(cval->head.mixer->chip, "%d:%d: cannot get min/max values for control %d (id %d)\n", - cval->id, snd_usb_ctrl_intf(cval->mixer->chip), - cval->control, cval->id); + cval->head.id, snd_usb_ctrl_intf(cval->head.mixer->chip), + cval->control, cval->head.id); return -EINVAL; } if (get_ctl_value(cval, UAC_GET_RES, @@ -1065,7 +1065,7 @@ static int mixer_ctl_feature_info(struct snd_kcontrol *kcontrol, kcontrol->vd[0].access &= ~(SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK); - snd_ctl_notify(cval->mixer->chip->card, + snd_ctl_notify(cval->head.mixer->chip->card, SNDRV_CTL_EVENT_MASK_INFO, &kcontrol->id); } @@ -1235,8 +1235,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, cval = kzalloc(sizeof(*cval), GFP_KERNEL); if (!cval) return; - cval->mixer = state->mixer; - cval->id = unitid; + snd_usb_mixer_elem_init_std(&cval->head, state->mixer, unitid); cval->control = control; cval->cmask = ctl_mask; cval->val_type = audio_feature_info[control-1].type; @@ -1347,14 +1346,14 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, range); usb_audio_warn(state->chip, "[%d] FU [%s] ch = %d, val = %d/%d/%d", - cval->id, kctl->id.name, cval->channels, + cval->head.id, kctl->id.name, cval->channels, cval->min, cval->max, cval->res); }
usb_audio_dbg(state->chip, "[%d] FU [%s] ch = %d, val = %d/%d/%d\n", - cval->id, kctl->id.name, cval->channels, + cval->head.id, kctl->id.name, cval->channels, cval->min, cval->max, cval->res); - snd_usb_mixer_add_control(state->mixer, kctl); + snd_usb_mixer_add_control(&cval->head, kctl); }
/* @@ -1528,8 +1527,7 @@ static void build_mixer_unit_ctl(struct mixer_build *state, if (!cval) return;
- cval->mixer = state->mixer; - cval->id = unitid; + snd_usb_mixer_elem_init_std(&cval->head, state->mixer, unitid); cval->control = in_ch + 1; /* based on 1 */ cval->val_type = USB_MIXER_S16; for (i = 0; i < num_outs; i++) { @@ -1561,8 +1559,8 @@ static void build_mixer_unit_ctl(struct mixer_build *state, append_ctl_name(kctl, " Volume");
usb_audio_dbg(state->chip, "[%d] MU [%s] ch = %d, val = %d/%d\n", - cval->id, kctl->id.name, cval->channels, cval->min, cval->max); - snd_usb_mixer_add_control(state->mixer, kctl); + cval->head.id, kctl->id.name, cval->channels, cval->min, cval->max); + snd_usb_mixer_add_control(&cval->head, kctl); }
/* @@ -1812,8 +1810,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, cval = kzalloc(sizeof(*cval), GFP_KERNEL); if (!cval) return -ENOMEM; - cval->mixer = state->mixer; - cval->id = unitid; + snd_usb_mixer_elem_init_std(&cval->head, state->mixer, unitid); cval->control = valinfo->control; cval->val_type = valinfo->val_type; cval->channels = 1; @@ -1866,10 +1863,10 @@ static int build_audio_procunit(struct mixer_build *state, int unitid,
usb_audio_dbg(state->chip, "[%d] PU [%s] ch = %d, val = %d/%d\n", - cval->id, kctl->id.name, cval->channels, + cval->head.id, kctl->id.name, cval->channels, cval->min, cval->max);
- err = snd_usb_mixer_add_control(state->mixer, kctl); + err = snd_usb_mixer_add_control(&cval->head, kctl); if (err < 0) return err; } @@ -2016,8 +2013,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, cval = kzalloc(sizeof(*cval), GFP_KERNEL); if (!cval) return -ENOMEM; - cval->mixer = state->mixer; - cval->id = unitid; + snd_usb_mixer_elem_init_std(&cval->head, state->mixer, unitid); cval->val_type = USB_MIXER_U8; cval->channels = 1; cval->min = 1; @@ -2087,11 +2083,8 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, }
usb_audio_dbg(state->chip, "[%d] SU [%s] items = %d\n", - cval->id, kctl->id.name, desc->bNrInPins); - if ((err = snd_usb_mixer_add_control(state->mixer, kctl)) < 0) - return err; - - return 0; + cval->head.id, kctl->id.name, desc->bNrInPins); + return snd_usb_mixer_add_control(&cval->head, kctl); }
/* @@ -2236,25 +2229,21 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid) { - struct usb_mixer_elem_info *info; + struct usb_mixer_elem_list *list;
- for (info = mixer->id_elems[unitid]; info; info = info->next_id_elem) + for (list = mixer->id_elems[unitid]; list; list = list->next_id_elem) snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, - info->elem_id); + &list->kctl->id); }
static void snd_usb_mixer_dump_cval(struct snd_info_buffer *buffer, - int unitid, - struct usb_mixer_elem_info *cval) + struct usb_mixer_elem_list *list) { + struct usb_mixer_elem_info *cval = (struct usb_mixer_elem_info *)list; static char *val_types[] = {"BOOLEAN", "INV_BOOLEAN", "S8", "U8", "S16", "U16"}; - snd_iprintf(buffer, " Unit: %i\n", unitid); - if (cval->elem_id) - snd_iprintf(buffer, " Control: name="%s", index=%i\n", - cval->elem_id->name, cval->elem_id->index); snd_iprintf(buffer, " Info: id=%i, control=%i, cmask=0x%x, " - "channels=%i, type="%s"\n", cval->id, + "channels=%i, type="%s"\n", cval->head.id, cval->control, cval->cmask, cval->channels, val_types[cval->val_type]); snd_iprintf(buffer, " Volume: min=%i, max=%i, dBmin=%i, dBmax=%i\n", @@ -2266,7 +2255,7 @@ static void snd_usb_mixer_proc_read(struct snd_info_entry *entry, { struct snd_usb_audio *chip = entry->private_data; struct usb_mixer_interface *mixer; - struct usb_mixer_elem_info *cval; + struct usb_mixer_elem_list *list; int unitid;
list_for_each_entry(mixer, &chip->mixer_list, list) { @@ -2276,9 +2265,17 @@ static void snd_usb_mixer_proc_read(struct snd_info_entry *entry, mixer->ignore_ctl_error); snd_iprintf(buffer, "Card: %s\n", chip->card->longname); for (unitid = 0; unitid < MAX_ID_ELEMS; unitid++) { - for (cval = mixer->id_elems[unitid]; cval; - cval = cval->next_id_elem) - snd_usb_mixer_dump_cval(buffer, unitid, cval); + for (list = mixer->id_elems[unitid]; list; + list = list->next_id_elem) { + snd_iprintf(buffer, " Unit: %i\n", list->id); + if (list->kctl) + snd_iprintf(buffer, + " Control: name="%s", index=%i\n", + list->kctl->id.name, + list->kctl->id.index); + if (list->dump) + list->dump(buffer, list); + } } } } @@ -2286,7 +2283,7 @@ static void snd_usb_mixer_proc_read(struct snd_info_entry *entry, static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer, int attribute, int value, int index) { - struct usb_mixer_elem_info *info; + struct usb_mixer_elem_list *list; __u8 unitid = (index >> 8) & 0xff; __u8 control = (value >> 8) & 0xff; __u8 channel = value & 0xff; @@ -2298,7 +2295,13 @@ static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer, return; }
- for (info = mixer->id_elems[unitid]; info; info = info->next_id_elem) { + for (list = mixer->id_elems[unitid]; list; list = list->next_id_elem) { + struct usb_mixer_elem_info *info; + + if (!list->kctl) + continue; + + info = (struct usb_mixer_elem_info *)list; if (info->control != control) continue;
@@ -2311,7 +2314,7 @@ static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer, info->cached = 0;
snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, - info->elem_id); + &info->head.kctl->id); break;
case UAC2_CS_RANGE: @@ -2509,8 +2512,9 @@ int snd_usb_mixer_suspend(struct usb_mixer_interface *mixer) return 0; }
-static int restore_mixer_value(struct usb_mixer_elem_info *cval) +static int restore_mixer_value(struct usb_mixer_elem_list *list) { + struct usb_mixer_elem_info *cval = (struct usb_mixer_elem_info *)list; int c, err, idx;
if (cval->cmask) { @@ -2540,19 +2544,19 @@ static int restore_mixer_value(struct usb_mixer_elem_info *cval)
int snd_usb_mixer_resume(struct usb_mixer_interface *mixer, bool reset_resume) { - struct usb_mixer_elem_info *cval; + struct usb_mixer_elem_list *list; int id, err;
- /* FIXME: any mixer quirks? */ - if (reset_resume) { /* restore cached mixer values */ for (id = 0; id < MAX_ID_ELEMS; id++) { - for (cval = mixer->id_elems[id]; cval; - cval = cval->next_id_elem) { - err = restore_mixer_value(cval); - if (err < 0) - return err; + for (list = mixer->id_elems[id]; list; + list = list->next_id_elem) { + if (list->resume) { + err = list->resume(list); + if (err < 0) + return err; + } } } } @@ -2560,3 +2564,15 @@ int snd_usb_mixer_resume(struct usb_mixer_interface *mixer, bool reset_resume) return snd_usb_mixer_activate(mixer); } #endif + +void snd_usb_mixer_elem_init_std(struct usb_mixer_elem_list *list, + struct usb_mixer_interface *mixer, + int unitid) +{ + list->mixer = mixer; + list->id = unitid; + list->dump = snd_usb_mixer_dump_cval; +#ifdef CONFIG_PM + list->resume = restore_mixer_value; +#endif +} diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h index 2478a844a322..05d812f7d5e5 100644 --- a/sound/usb/mixer.h +++ b/sound/usb/mixer.h @@ -1,6 +1,8 @@ #ifndef __USBMIXER_H #define __USBMIXER_H
+#include <sound/info.h> + struct usb_mixer_interface { struct snd_usb_audio *chip; struct usb_host_interface *hostif; @@ -8,7 +10,7 @@ struct usb_mixer_interface { unsigned int ignore_ctl_error; struct urb *urb; /* array[MAX_ID_ELEMS], indexed by unit id */ - struct usb_mixer_elem_info **id_elems; + struct usb_mixer_elem_list **id_elems;
/* the usb audio specification version this interface complies to */ int protocol; @@ -36,11 +38,21 @@ enum { USB_MIXER_U16, };
-struct usb_mixer_elem_info { +typedef void (*usb_mixer_elem_dump_func)(struct snd_info_buffer *buffer, + struct usb_mixer_elem_list *list); +typedef int (*usb_mixer_elem_resume_func)(struct usb_mixer_elem_list *elem); + +struct usb_mixer_elem_list { struct usb_mixer_interface *mixer; - struct usb_mixer_elem_info *next_id_elem; /* list of controls with same id */ - struct snd_ctl_elem_id *elem_id; + struct usb_mixer_elem_list *next_id_elem; /* list of controls with same id */ + struct snd_kcontrol *kctl; unsigned int id; + usb_mixer_elem_dump_func dump; + usb_mixer_elem_resume_func resume; +}; + +struct usb_mixer_elem_info { + struct usb_mixer_elem_list head; unsigned int control; /* CS or ICN (high byte) */ unsigned int cmask; /* channel mask bitmap: 0 = master */ unsigned int idx_off; /* Control index offset */ @@ -65,9 +77,13 @@ void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid); int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval, int request, int validx, int value_set);
-int snd_usb_mixer_add_control(struct usb_mixer_interface *mixer, +int snd_usb_mixer_add_control(struct usb_mixer_elem_list *list, struct snd_kcontrol *kctl);
+void snd_usb_mixer_elem_init_std(struct usb_mixer_elem_list *list, + struct usb_mixer_interface *mixer, + int unitid); + int snd_usb_mixer_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag, unsigned int size, unsigned int __user *_tlv);
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 03405b93b438..4cc32892dc6f 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -69,7 +69,6 @@ static int snd_create_std_mono_ctl_offset(struct usb_mixer_interface *mixer, const char *name, snd_kcontrol_tlv_rw_t *tlv_callback) { - int err; struct usb_mixer_elem_info *cval; struct snd_kcontrol *kctl;
@@ -77,8 +76,7 @@ static int snd_create_std_mono_ctl_offset(struct usb_mixer_interface *mixer, if (!cval) return -ENOMEM;
- cval->id = unitid; - cval->mixer = mixer; + snd_usb_mixer_elem_init_std(&cval->head, mixer, unitid); cval->val_type = val_type; cval->channels = 1; cval->control = control; @@ -112,11 +110,7 @@ static int snd_create_std_mono_ctl_offset(struct usb_mixer_interface *mixer, SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; } /* Add control to mixer */ - err = snd_usb_mixer_add_control(mixer, kctl); - if (err < 0) - return err; - - return 0; + return snd_usb_mixer_add_control(&cval->head, kctl); }
static int snd_create_std_mono_ctl(struct usb_mixer_interface *mixer, @@ -1206,7 +1200,7 @@ void snd_emuusb_set_samplerate(struct snd_usb_audio *chip, int unitid = 12; /* SamleRate ExtensionUnit ID */
list_for_each_entry(mixer, &chip->mixer_list, list) { - cval = mixer->id_elems[unitid]; + cval = (struct usb_mixer_elem_info *)mixer->id_elems[unitid]; if (cval) { snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR, cval->control << 8, diff --git a/sound/usb/mixer_scarlett.c b/sound/usb/mixer_scarlett.c index a0a874507de5..92dba35660b3 100644 --- a/sound/usb/mixer_scarlett.c +++ b/sound/usb/mixer_scarlett.c @@ -436,10 +436,10 @@ static int scarlett_ctl_meter_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) { struct usb_mixer_elem_info *elem = kctl->private_data; - struct snd_usb_audio *chip = elem->mixer->chip; + struct snd_usb_audio *chip = elem->head.mixer->chip; unsigned char buf[2 * MAX_CHANNELS] = {0, }; int wValue = (elem->control << 8) | elem->idx_off; - int idx = snd_usb_ctrl_intf(chip) | (elem->id << 8); + int idx = snd_usb_ctrl_intf(chip) | (elem->head.id << 8); int err;
err = snd_usb_ctl_msg(chip->dev, @@ -528,10 +528,10 @@ static int add_new_ctl(struct usb_mixer_interface *mixer, if (!elem) return -ENOMEM;
- elem->mixer = mixer; + elem->head.mixer = mixer; elem->control = offset; elem->idx_off = num; - elem->id = index; + elem->head.id = index; elem->val_type = val_type;
elem->channels = channels;
Rewrite the code to handle LEDs on audigy2nx and co for supporting the proper resume. A new internal helper function add_single_ctl_with_resume() is introduced to manage the usb_mixer_elem_list more easily.
Also while we're at it, move audigy2nx_leds[] in usb_mixer_interface struct into the private_value of each kctl, too.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/usb/mixer.h | 1 - sound/usb/mixer_quirks.c | 148 +++++++++++++++++++++++++++++------------------ 2 files changed, 92 insertions(+), 57 deletions(-)
diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h index 05d812f7d5e5..a9697fe93aef 100644 --- a/sound/usb/mixer.h +++ b/sound/usb/mixer.h @@ -23,7 +23,6 @@ struct usb_mixer_interface { struct usb_ctrlrequest *rc_setup_packet; u8 rc_buffer[6];
- u8 audigy2nx_leds[3]; u8 xonar_u1_status; };
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 4cc32892dc6f..b412d085e593 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -144,6 +144,32 @@ static int snd_create_std_mono_table(struct usb_mixer_interface *mixer, return 0; }
+static int add_single_ctl_with_resume(struct usb_mixer_interface *mixer, + int id, + usb_mixer_elem_resume_func resume, + const struct snd_kcontrol_new *knew, + struct usb_mixer_elem_list **listp) +{ + struct usb_mixer_elem_list *list; + struct snd_kcontrol *kctl; + + list = kzalloc(sizeof(*list), GFP_KERNEL); + if (!list) + return -ENOMEM; + if (listp) + *listp = list; + list->mixer = mixer; + list->id = id; + list->resume = resume; + kctl = snd_ctl_new1(knew, list); + if (!kctl) { + kfree(list); + return -ENOMEM; + } + kctl->private_free = snd_usb_mixer_elem_free; + return snd_usb_mixer_add_control(list, kctl); +} + /* * Sound Blaster remote control configuration * @@ -271,84 +297,90 @@ static int snd_usb_soundblaster_remote_init(struct usb_mixer_interface *mixer)
static int snd_audigy2nx_led_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); - int index = kcontrol->private_value; - - ucontrol->value.integer.value[0] = mixer->audigy2nx_leds[index]; + ucontrol->value.integer.value[0] = kcontrol->private_value >> 8; return 0; }
-static int snd_audigy2nx_led_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int snd_audigy2nx_led_update(struct usb_mixer_interface *mixer, + int value, int index) { - struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); - int index = kcontrol->private_value; - int value = ucontrol->value.integer.value[0]; - int err, changed; + struct snd_usb_audio *chip = mixer->chip; + int err;
- if (value > 1) - return -EINVAL; - changed = value != mixer->audigy2nx_leds[index]; - down_read(&mixer->chip->shutdown_rwsem); - if (mixer->chip->shutdown) { + down_read(&chip->shutdown_rwsem); + if (chip->shutdown) { err = -ENODEV; goto out; } - if (mixer->chip->usb_id == USB_ID(0x041e, 0x3042)) - err = snd_usb_ctl_msg(mixer->chip->dev, - usb_sndctrlpipe(mixer->chip->dev, 0), 0x24, + if (chip->usb_id == USB_ID(0x041e, 0x3042)) + err = snd_usb_ctl_msg(chip->dev, + usb_sndctrlpipe(chip->dev, 0), 0x24, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, !value, 0, NULL, 0); /* USB X-Fi S51 Pro */ - if (mixer->chip->usb_id == USB_ID(0x041e, 0x30df)) - err = snd_usb_ctl_msg(mixer->chip->dev, - usb_sndctrlpipe(mixer->chip->dev, 0), 0x24, + if (chip->usb_id == USB_ID(0x041e, 0x30df)) + err = snd_usb_ctl_msg(chip->dev, + usb_sndctrlpipe(chip->dev, 0), 0x24, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, !value, 0, NULL, 0); else - err = snd_usb_ctl_msg(mixer->chip->dev, - usb_sndctrlpipe(mixer->chip->dev, 0), 0x24, + err = snd_usb_ctl_msg(chip->dev, + usb_sndctrlpipe(chip->dev, 0), 0x24, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, value, index + 2, NULL, 0); out: - up_read(&mixer->chip->shutdown_rwsem); - if (err < 0) - return err; - mixer->audigy2nx_leds[index] = value; - return changed; + up_read(&chip->shutdown_rwsem); + return err; }
-static struct snd_kcontrol_new snd_audigy2nx_controls[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "CMSS LED Switch", - .info = snd_audigy2nx_led_info, - .get = snd_audigy2nx_led_get, - .put = snd_audigy2nx_led_put, - .private_value = 0, - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Power LED Switch", - .info = snd_audigy2nx_led_info, - .get = snd_audigy2nx_led_get, - .put = snd_audigy2nx_led_put, - .private_value = 1, - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Dolby Digital LED Switch", - .info = snd_audigy2nx_led_info, - .get = snd_audigy2nx_led_get, - .put = snd_audigy2nx_led_put, - .private_value = 2, - }, +static int snd_audigy2nx_led_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol); + struct usb_mixer_interface *mixer = list->mixer; + int index = kcontrol->private_value & 0xff; + int value = ucontrol->value.integer.value[0]; + int old_value = kcontrol->private_value >> 8; + int err; + + if (value > 1) + return -EINVAL; + if (value == old_value) + return 0; + kcontrol->private_value = (value << 8) | index; + err = snd_audigy2nx_led_update(mixer, value, index); + return err < 0 ? err : 1; +} + +static int snd_audigy2nx_led_resume(struct usb_mixer_elem_list *list) +{ + int priv_value = list->kctl->private_value; + + return snd_audigy2nx_led_update(list->mixer, priv_value >> 8, + priv_value & 0xff); +} + +/* name and private_value are set dynamically */ +static struct snd_kcontrol_new snd_audigy2nx_control = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_audigy2nx_led_info, + .get = snd_audigy2nx_led_get, + .put = snd_audigy2nx_led_put, +}; + +static const char * const snd_audigy2nx_led_names[] = { + "CMSS LED Switch", + "Power LED Switch", + "Dolby Digital LED Switch", };
static int snd_audigy2nx_controls_create(struct usb_mixer_interface *mixer) { int i, err;
- for (i = 0; i < ARRAY_SIZE(snd_audigy2nx_controls); ++i) { + for (i = 0; i < ARRAY_SIZE(snd_audigy2nx_led_names); ++i) { + struct snd_kcontrol_new knew; + /* USB X-Fi S51 doesn't have a CMSS LED */ if ((mixer->chip->usb_id == USB_ID(0x041e, 0x3042)) && i == 0) continue; @@ -361,12 +393,16 @@ static int snd_audigy2nx_controls_create(struct usb_mixer_interface *mixer) mixer->chip->usb_id == USB_ID(0x041e, 0x30df) || mixer->chip->usb_id == USB_ID(0x041e, 0x3048))) break; - err = snd_ctl_add(mixer->chip->card, - snd_ctl_new1(&snd_audigy2nx_controls[i], mixer)); + + knew = snd_audigy2nx_control; + knew.name = snd_audigy2nx_led_names[i]; + knew.private_value = (1 << 8) | i; /* LED on as default */ + err = add_single_ctl_with_resume(mixer, 0, + snd_audigy2nx_led_resume, + &knew, NULL); if (err < 0) return err; } - mixer->audigy2nx_leds[1] = 1; /* Power LED is on by default */ return 0; }
Similar as the previous fix, this adds the proper resume support to Emu0202 "Front Jack Channels" enum mixer element.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/usb/mixer_quirks.c | 84 ++++++++++++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 38 deletions(-)
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index b412d085e593..870d8975ae2e 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -473,63 +473,71 @@ static int snd_emu0204_ch_switch_get(struct snd_kcontrol *kcontrol, return 0; }
-static int snd_emu0204_ch_switch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int snd_emu0204_ch_switch_update(struct usb_mixer_interface *mixer, + int value) { - struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); - unsigned int value = ucontrol->value.enumerated.item[0]; - int err, changed; + struct snd_usb_audio *chip = mixer->chip; + int err; unsigned char buf[2];
- if (value > 1) - return -EINVAL; - - buf[0] = 0x01; - buf[1] = value ? 0x02 : 0x01; - - changed = value != kcontrol->private_value; - down_read(&mixer->chip->shutdown_rwsem); + down_read(&chip->shutdown_rwsem); if (mixer->chip->shutdown) { err = -ENODEV; goto out; } - err = snd_usb_ctl_msg(mixer->chip->dev, - usb_sndctrlpipe(mixer->chip->dev, 0), UAC_SET_CUR, + + buf[0] = 0x01; + buf[1] = value ? 0x02 : 0x01; + err = snd_usb_ctl_msg(chip->dev, + usb_sndctrlpipe(chip->dev, 0), UAC_SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, 0x0400, 0x0e00, buf, 2); out: - up_read(&mixer->chip->shutdown_rwsem); - if (err < 0) - return err; + up_read(&chip->shutdown_rwsem); + return err; +} + +static int snd_emu0204_ch_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol); + struct usb_mixer_interface *mixer = list->mixer; + unsigned int value = ucontrol->value.enumerated.item[0]; + int err; + + if (value > 1) + return -EINVAL; + + if (value == kcontrol->private_value) + return 0; + kcontrol->private_value = value; - return changed; + err = snd_emu0204_ch_switch_update(mixer, value); + return err < 0 ? err : 1; }
+static int snd_emu0204_ch_switch_resume(struct usb_mixer_elem_list *list) +{ + return snd_emu0204_ch_switch_update(list->mixer, + list->kctl->private_value); +}
-static struct snd_kcontrol_new snd_emu0204_controls[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Front Jack Channels", - .info = snd_emu0204_ch_switch_info, - .get = snd_emu0204_ch_switch_get, - .put = snd_emu0204_ch_switch_put, - .private_value = 0, - }, +static struct snd_kcontrol_new snd_emu0204_control = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Front Jack Channels", + .info = snd_emu0204_ch_switch_info, + .get = snd_emu0204_ch_switch_get, + .put = snd_emu0204_ch_switch_put, + .private_value = 0, };
static int snd_emu0204_controls_create(struct usb_mixer_interface *mixer) { - int i, err; - - for (i = 0; i < ARRAY_SIZE(snd_emu0204_controls); ++i) { - err = snd_ctl_add(mixer->chip->card, - snd_ctl_new1(&snd_emu0204_controls[i], mixer)); - if (err < 0) - return err; - } - - return 0; + return add_single_ctl_with_resume(mixer, 0, + snd_emu0204_ch_switch_resume, + &snd_emu0204_control, NULL); } + /* ASUS Xonar U1 / U3 controls */
static int snd_xonar_u1_switch_get(struct snd_kcontrol *kcontrol,
This time it's about Xonar U1: add the proper resume support for "Digital Playback Switch" element.
Also, the status is moved into kcontrol private_value from usb_mixer_interface struct field. One more cut.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/usb/mixer.h | 2 -- sound/usb/mixer_quirks.c | 66 ++++++++++++++++++++++++++++-------------------- 2 files changed, 38 insertions(+), 30 deletions(-)
diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h index a9697fe93aef..c2a5b923f9bc 100644 --- a/sound/usb/mixer.h +++ b/sound/usb/mixer.h @@ -22,8 +22,6 @@ struct usb_mixer_interface { struct urb *rc_urb; struct usb_ctrlrequest *rc_setup_packet; u8 rc_buffer[6]; - - u8 xonar_u1_status; };
#define MAX_CHANNELS 16 /* max logical channels */ diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 870d8975ae2e..e7519770632d 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -543,38 +543,52 @@ static int snd_emu0204_controls_create(struct usb_mixer_interface *mixer) static int snd_xonar_u1_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); - - ucontrol->value.integer.value[0] = !!(mixer->xonar_u1_status & 0x02); + ucontrol->value.integer.value[0] = !!(kcontrol->private_value & 0x02); return 0; }
+static int snd_xonar_u1_switch_update(struct usb_mixer_interface *mixer, + unsigned char status) +{ + struct snd_usb_audio *chip = mixer->chip; + int err; + + down_read(&chip->shutdown_rwsem); + if (chip->shutdown) + err = -ENODEV; + else + err = snd_usb_ctl_msg(chip->dev, + usb_sndctrlpipe(chip->dev, 0), 0x08, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, + 50, 0, &status, 1); + up_read(&chip->shutdown_rwsem); + return err; +} + static int snd_xonar_u1_switch_put(struct snd_kcontrol *kcontrol, 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); u8 old_status, new_status; - int err, changed; + int err;
- old_status = mixer->xonar_u1_status; + old_status = kcontrol->private_value; if (ucontrol->value.integer.value[0]) new_status = old_status | 0x02; else new_status = old_status & ~0x02; - changed = new_status != old_status; - down_read(&mixer->chip->shutdown_rwsem); - if (mixer->chip->shutdown) - err = -ENODEV; - else - err = snd_usb_ctl_msg(mixer->chip->dev, - usb_sndctrlpipe(mixer->chip->dev, 0), 0x08, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, - 50, 0, &new_status, 1); - up_read(&mixer->chip->shutdown_rwsem); - if (err < 0) - return err; - mixer->xonar_u1_status = new_status; - return changed; + if (new_status == old_status) + return 0; + + kcontrol->private_value = new_status; + err = snd_xonar_u1_switch_update(list->mixer, new_status); + return err < 0 ? err : 1; +} + +static int snd_xonar_u1_switch_resume(struct usb_mixer_elem_list *list) +{ + return snd_xonar_u1_switch_update(list->mixer, + list->kctl->private_value); }
static struct snd_kcontrol_new snd_xonar_u1_output_switch = { @@ -583,18 +597,14 @@ static struct snd_kcontrol_new snd_xonar_u1_output_switch = { .info = snd_ctl_boolean_mono_info, .get = snd_xonar_u1_switch_get, .put = snd_xonar_u1_switch_put, + .private_value = 0x05, };
static int snd_xonar_u1_controls_create(struct usb_mixer_interface *mixer) { - int err; - - err = snd_ctl_add(mixer->chip->card, - snd_ctl_new1(&snd_xonar_u1_output_switch, mixer)); - if (err < 0) - return err; - mixer->xonar_u1_status = 0x05; - return 0; + return add_single_ctl_with_resume(mixer, 0, + snd_xonar_u1_switch_resume, + &snd_xonar_u1_output_switch, NULL); }
/* Digidesign Mbox 1 clock source switch (internal/spdif) */
Again another quirk fix, just convert to usb_mixer_elem_list with the resume callback for Mbox 1 stuff.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/usb/mixer_quirks.c | 52 +++++++++++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 23 deletions(-)
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index e7519770632d..5491c68efbc0 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -616,29 +616,12 @@ static int snd_mbox1_switch_get(struct snd_kcontrol *kctl, return 0; }
-static int snd_mbox1_switch_put(struct snd_kcontrol *kctl, - struct snd_ctl_elem_value *ucontrol) +static int snd_mbox1_switch_update(struct usb_mixer_interface *mixer, int val) { - struct snd_usb_audio *chip; - struct usb_mixer_interface *mixer; + struct snd_usb_audio *chip = mixer->chip; int err; - bool cur_val, new_val; unsigned char buff[3];
- cur_val = kctl->private_value; - new_val = ucontrol->value.enumerated.item[0]; - - mixer = snd_kcontrol_chip(kctl); - if (snd_BUG_ON(!mixer)) - return -EINVAL; - - chip = mixer->chip; - if (snd_BUG_ON(!chip)) - return -EINVAL; - - if (cur_val == new_val) - return 0; - down_read(&chip->shutdown_rwsem); if (chip->shutdown) { err = -ENODEV; @@ -668,7 +651,7 @@ static int snd_mbox1_switch_put(struct snd_kcontrol *kctl, * while S/PDIF sync is enabled and confusing * this configuration. */ - if (new_val == 0) { + if (val == 0) { buff[0] = 0x80; buff[1] = 0xbb; buff[2] = 0x00; @@ -697,10 +680,27 @@ static int snd_mbox1_switch_put(struct snd_kcontrol *kctl, USB_RECIP_ENDPOINT, 0x100, 0x2, buff, 3); if (err < 0) goto err; - kctl->private_value = new_val;
err: up_read(&chip->shutdown_rwsem); + return err; +} + +static int snd_mbox1_switch_put(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl); + struct usb_mixer_interface *mixer = list->mixer; + int err; + bool cur_val, new_val; + + cur_val = kctl->private_value; + new_val = ucontrol->value.enumerated.item[0]; + if (cur_val == new_val) + return 0; + + kctl->private_value = new_val; + err = snd_mbox1_switch_update(mixer, new_val); return err < 0 ? err : 1; }
@@ -715,6 +715,11 @@ static int snd_mbox1_switch_info(struct snd_kcontrol *kcontrol, return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts); }
+static int snd_mbox1_switch_resume(struct usb_mixer_elem_list *list) +{ + return snd_mbox1_switch_update(list->mixer, list->kctl->private_value); +} + static struct snd_kcontrol_new snd_mbox1_switch = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Clock Source", @@ -728,8 +733,9 @@ static struct snd_kcontrol_new snd_mbox1_switch = {
static int snd_mbox1_create_sync_switch(struct usb_mixer_interface *mixer) { - return snd_ctl_add(mixer->chip->card, - snd_ctl_new1(&snd_mbox1_switch, mixer)); + return add_single_ctl_with_resume(mixer, 0, + snd_mbox1_switch_resume, + &snd_mbox1_switch, NULL); }
/* Native Instruments device quirks */
The changes at this time are a bit more wider than previous ones. Firstly, the NI controls didn't cache the values, so I had to implement the caching. It's stored in bit 24 of private_value. In addition to that, the initial values have to be read from registers.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/usb/mixer_quirks.c | 99 +++++++++++++++++++++++++----------------------- 1 file changed, 52 insertions(+), 47 deletions(-)
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 5491c68efbc0..29324f4b3a7b 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -742,64 +742,68 @@ static int snd_mbox1_create_sync_switch(struct usb_mixer_interface *mixer)
#define _MAKE_NI_CONTROL(bRequest,wIndex) ((bRequest) << 16 | (wIndex))
-static int snd_nativeinstruments_control_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int snd_ni_control_init_val(struct usb_mixer_interface *mixer, + struct snd_kcontrol *kctl) { - struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); struct usb_device *dev = mixer->chip->dev; - u8 bRequest = (kcontrol->private_value >> 16) & 0xff; - u16 wIndex = kcontrol->private_value & 0xffff; - u8 tmp; - int ret; - - down_read(&mixer->chip->shutdown_rwsem); - if (mixer->chip->shutdown) - ret = -ENODEV; - else - ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), bRequest, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, - 0, wIndex, - &tmp, sizeof(tmp), 1000); - up_read(&mixer->chip->shutdown_rwsem); + unsigned int pval = kctl->private_value; + u8 value; + int err;
- if (ret < 0) { + err = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + (pval >> 16) & 0xff, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0, pval & 0xffff, &value, 1, 1000); + if (err < 0) { dev_err(&dev->dev, - "unable to issue vendor read request (ret = %d)", ret); - return ret; + "unable to issue vendor read request (ret = %d)", err); + return err; }
- ucontrol->value.integer.value[0] = tmp; - + kctl->private_value |= (value << 24); return 0; }
-static int snd_nativeinstruments_control_put(struct snd_kcontrol *kcontrol, +static int snd_nativeinstruments_control_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); - struct usb_device *dev = mixer->chip->dev; - u8 bRequest = (kcontrol->private_value >> 16) & 0xff; - u16 wIndex = kcontrol->private_value & 0xffff; - u16 wValue = ucontrol->value.integer.value[0]; - int ret; + ucontrol->value.integer.value[0] = kcontrol->private_value >> 24; + return 0; +}
- down_read(&mixer->chip->shutdown_rwsem); - if (mixer->chip->shutdown) - ret = -ENODEV; +static int snd_ni_update_cur_val(struct usb_mixer_elem_list *list) +{ + struct snd_usb_audio *chip = list->mixer->chip; + unsigned int pval = list->kctl->private_value; + int err; + + down_read(&chip->shutdown_rwsem); + if (chip->shutdown) + err = -ENODEV; else - ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), bRequest, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, - wValue, wIndex, - NULL, 0, 1000); - up_read(&mixer->chip->shutdown_rwsem); + err = usb_control_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0), + (pval >> 16) & 0xff, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, + pval >> 24, pval & 0xffff, NULL, 0, 1000); + up_read(&chip->shutdown_rwsem); + return err; +}
- if (ret < 0) { - dev_err(&dev->dev, - "unable to issue vendor write request (ret = %d)", ret); - return ret; - } +static int snd_nativeinstruments_control_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol); + u8 oldval = (kcontrol->private_value >> 24) & 0xff; + u8 newval = ucontrol->value.integer.value[0]; + int err;
- return 0; + if (oldval == newval) + return 0; + + kcontrol->private_value &= ~(0xff << 24); + kcontrol->private_value |= newval; + err = snd_ni_update_cur_val(list); + return err < 0 ? err : 1; }
static struct snd_kcontrol_new snd_nativeinstruments_ta6_mixers[] = { @@ -870,16 +874,17 @@ static int snd_nativeinstruments_create_mixer(struct usb_mixer_interface *mixer, };
for (i = 0; i < count; i++) { - struct snd_kcontrol *c; + struct usb_mixer_elem_list *list;
template.name = kc[i].name; template.private_value = kc[i].private_value;
- c = snd_ctl_new1(&template, mixer); - err = snd_ctl_add(mixer->chip->card, c); - + err = add_single_ctl_with_resume(mixer, 0, + snd_ni_update_cur_val, + &template, &list); if (err < 0) break; + snd_ni_control_init_val(mixer, list->kctl); }
return err;
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: Takashi Iwai tiwai@suse.de --- sound/usb/mixer_quirks.c | 194 +++++++++++++---------------------------------- 1 file changed, 54 insertions(+), 140 deletions(-)
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 29324f4b3a7b..29afad6aac04 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -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; + err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + pval & 0xff00, + snd_usb_ctrl_intf(mixer->chip) | ((pval & 0xff) << 8), + value, 2); + if (err < 0) + return err;
- if (pval->is_cached) { - ucontrol->value.enumerated.item[0] = pval->cached_value; - return 0; - } + kctl->private_value |= value[0] << 24; + return 0; +}
- mixer = (struct usb_mixer_interface *) pval->mixer; - if (snd_BUG_ON(!mixer)) - return -EINVAL; +static int snd_ftu_eff_switch_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.enumerated.item[0] = kctl->private_value >> 24; + return 0; +}
- chip = (struct snd_usb_audio *) mixer->chip; - if (snd_BUG_ON(!chip)) - return -EINVAL; +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 err;
- id = pval->bUnitID; - validx = pval->validx; + 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_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; - - ucontrol->value.enumerated.item[0] = value[0]; - pval->cached_value = value[0]; - pval->is_cached = 1; - - return 0; + usb_sndctrlpipe(chip->dev, 0), + UAC_SET_CUR, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, + pval & 0xff00, + snd_usb_ctrl_intf(chip) | ((pval & 0xff) << 8), + value, 2); + up_read(&chip->shutdown_rwsem); + return err; }
static int snd_ftu_eff_switch_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) { - struct snd_usb_audio *chip; - struct snd_ftu_eff_switch_priv_val *pval; - - struct usb_mixer_interface *mixer; - int changed, cur_val, err, new_val; - unsigned char value[2]; - int id, validx; - - const int val_len = 2; - - changed = 0; + struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl); + unsigned int pval = list->kctl->private_value; + int cur_val, err, new_val;
- pval = (struct snd_ftu_eff_switch_priv_val *) - kctl->private_value; - cur_val = pval->cached_value; + cur_val = pval >> 24; new_val = ucontrol->value.enumerated.item[0]; + if (cur_val == new_val) + 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; - - 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; - - 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[1] = 0; - down_read(&mixer->chip->shutdown_rwsem); - if (mixer->chip->shutdown) - err = -ENODEV; - else - err = snd_usb_ctl_msg(chip->dev, - 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) - 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) -{ - kfree((void *)kctl->private_value); + 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; }
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 --- sound/usb/mixer_quirks.c | 135 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 93 insertions(+), 42 deletions(-)
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 29afad6aac04..1fae956d6692 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -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, 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; struct usb_interface *iface; struct usb_host_interface *alts; @@ -1517,17 +1518,23 @@ static int snd_microii_spdif_default_get(struct snd_kcontrol *kcontrol, unsigned char data[3]; 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[1] = (kcontrol->private_value >> 8) & 0xff; ucontrol->value.iec958.status[2] = 0x00;
/* 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]; ep = get_endpoint(alts, 0)->bEndpointAddress;
- err = snd_usb_ctl_msg(mixer->chip->dev, - usb_rcvctrlpipe(mixer->chip->dev, 0), + err = snd_usb_ctl_msg(chip->dev, + usb_rcvctrlpipe(chip->dev, 0), UAC_GET_CUR, USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_IN, 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;
err = 0; -end: + end: + up_read(&chip->shutdown_rwsem); return err; }
-static int snd_microii_spdif_default_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int snd_microii_spdif_default_update(struct usb_mixer_elem_list *list) { - struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); - int err; + struct snd_usb_audio *chip = list->mixer->chip; + unsigned int pval = list->kctl->private_value; u8 reg; - unsigned long priv_backup = kcontrol->private_value; + int err; + + down_read(&chip->shutdown_rwsem); + if (chip->shutdown) { + err = -ENODEV; + goto end; + }
- reg = ((ucontrol->value.iec958.status[1] & 0x0f) << 4) | - (ucontrol->value.iec958.status[0] & 0x0f); - err = snd_usb_ctl_msg(mixer->chip->dev, - usb_sndctrlpipe(mixer->chip->dev, 0), + reg = ((pval >> 4) & 0xf0) | (pval & 0x0f); + err = snd_usb_ctl_msg(chip->dev, + usb_sndctrlpipe(chip->dev, 0), UAC_SET_CUR, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, reg, @@ -1567,15 +1579,10 @@ static int snd_microii_spdif_default_put(struct snd_kcontrol *kcontrol, if (err < 0) goto end;
- kcontrol->private_value &= 0xfffff0f0; - kcontrol->private_value |= (ucontrol->value.iec958.status[1] & 0x0f) << 8; - kcontrol->private_value |= (ucontrol->value.iec958.status[0] & 0x0f); - - 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), + reg = (pval & IEC958_AES0_NONAUDIO) ? 0xa0 : 0x20; + reg |= (pval >> 12) & 0x0f; + err = snd_usb_ctl_msg(chip->dev, + usb_sndctrlpipe(chip->dev, 0), UAC_SET_CUR, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, reg, @@ -1585,16 +1592,36 @@ static int snd_microii_spdif_default_put(struct snd_kcontrol *kcontrol, if (err < 0) goto end;
- kcontrol->private_value &= 0xffff0fff; - kcontrol->private_value |= (ucontrol->value.iec958.status[1] & 0xf0) << 8; + end: + 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. */
/* Silently ignore any bits from the request that cannot be set. */
- err = (priv_backup != kcontrol->private_value); -end: - return err; + if (pval == pval_old) + return 0; + + 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, @@ -1616,15 +1643,20 @@ static int snd_microii_spdif_switch_get(struct snd_kcontrol *kcontrol, return 0; }
-static int snd_microii_spdif_switch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int snd_microii_spdif_switch_update(struct usb_mixer_elem_list *list) { - 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; - u8 reg = ucontrol->value.integer.value[0] ? 0x28 : 0x2a;
- err = snd_usb_ctl_msg(mixer->chip->dev, - usb_sndctrlpipe(mixer->chip->dev, 0), + down_read(&chip->shutdown_rwsem); + if (chip->shutdown) { + err = -ENODEV; + goto end; + } + + err = snd_usb_ctl_msg(chip->dev, + usb_sndctrlpipe(chip->dev, 0), UAC_SET_CUR, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, reg, @@ -1632,15 +1664,27 @@ static int snd_microii_spdif_switch_put(struct snd_kcontrol *kcontrol, NULL, 0);
- if (!err) { - err = (reg != (kcontrol->private_value & 0x0ff)); - if (err) - kcontrol->private_value = reg; - } - + end: + up_read(&chip->shutdown_rwsem); 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[] = { { .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) { int err, i; + static usb_mixer_elem_resume_func resume_funcs[] = { + snd_microii_spdif_default_update, + NULL, + snd_microii_spdif_switch_update + };
for (i = 0; i < ARRAY_SIZE(snd_microii_mixer_spdif); ++i) { - err = snd_ctl_add(mixer->chip->card, - snd_ctl_new1(&snd_microii_mixer_spdif[i], mixer)); + err = add_single_ctl_with_resume(mixer, 0, + resume_funcs[i], + &snd_microii_mixer_spdif[i], + NULL); if (err < 0) return err; }
Scarlett driver uses almost compatible usb_mixer_elem_info struct, so we just need to add a couple of simple resume callbacks to handle them accordingly.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/usb/mixer_scarlett.c | 65 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 51 insertions(+), 14 deletions(-)
diff --git a/sound/usb/mixer_scarlett.c b/sound/usb/mixer_scarlett.c index 92dba35660b3..2ef312648aaa 100644 --- a/sound/usb/mixer_scarlett.c +++ b/sound/usb/mixer_scarlett.c @@ -285,6 +285,19 @@ static int scarlett_ctl_switch_put(struct snd_kcontrol *kctl, return changed; }
+static int scarlett_ctl_resume(struct usb_mixer_elem_list *list) +{ + struct usb_mixer_elem_info *elem = + container_of(list, struct usb_mixer_elem_info, head); + int i; + + for (i = 0; i < elem->channels; i++) + if (elem->cached & (1 << i)) + snd_usb_set_cur_mix_value(elem, i, i, + elem->cache_val[i]); + return 0; +} + static int scarlett_ctl_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) { @@ -432,6 +445,16 @@ static int scarlett_ctl_enum_put(struct snd_kcontrol *kctl, return 0; }
+static int scarlett_ctl_enum_resume(struct usb_mixer_elem_list *list) +{ + struct usb_mixer_elem_info *elem = + container_of(list, struct usb_mixer_elem_info, head); + + if (elem->cached) + snd_usb_set_cur_mix_value(elem, 0, 0, *elem->cache_val); + return 0; +} + static int scarlett_ctl_meter_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) { @@ -514,6 +537,7 @@ static struct snd_kcontrol_new usb_scarlett_ctl_sync = {
static int add_new_ctl(struct usb_mixer_interface *mixer, const struct snd_kcontrol_new *ncontrol, + usb_mixer_elem_resume_func resume, int index, int offset, int num, int val_type, int channels, const char *name, const struct scarlett_mixer_elem_enum_info *opt, @@ -529,6 +553,7 @@ static int add_new_ctl(struct usb_mixer_interface *mixer, return -ENOMEM;
elem->head.mixer = mixer; + elem->head.resume = resume; elem->control = offset; elem->idx_off = num; elem->head.id = index; @@ -548,7 +573,7 @@ static int add_new_ctl(struct usb_mixer_interface *mixer,
strlcpy(kctl->id.name, name, sizeof(kctl->id.name));
- err = snd_ctl_add(mixer->chip->card, kctl); + err = snd_usb_mixer_add_control(&elem->head, kctl); if (err < 0) return err;
@@ -569,7 +594,8 @@ static int add_output_ctls(struct usb_mixer_interface *mixer, /* Add mute switch */ snprintf(mx, sizeof(mx), "Master %d (%s) Playback Switch", index + 1, name); - err = add_new_ctl(mixer, &usb_scarlett_ctl_switch, 0x0a, 0x01, + err = add_new_ctl(mixer, &usb_scarlett_ctl_switch, + scarlett_ctl_resume, 0x0a, 0x01, 2*index+1, USB_MIXER_S16, 2, mx, NULL, &elem); if (err < 0) return err; @@ -577,7 +603,8 @@ static int add_output_ctls(struct usb_mixer_interface *mixer, /* Add volume control and initialize to 0 */ snprintf(mx, sizeof(mx), "Master %d (%s) Playback Volume", index + 1, name); - err = add_new_ctl(mixer, &usb_scarlett_ctl_master, 0x0a, 0x02, + err = add_new_ctl(mixer, &usb_scarlett_ctl_master, + scarlett_ctl_resume, 0x0a, 0x02, 2*index+1, USB_MIXER_S16, 2, mx, NULL, &elem); if (err < 0) return err; @@ -585,7 +612,8 @@ static int add_output_ctls(struct usb_mixer_interface *mixer, /* Add L channel source playback enumeration */ snprintf(mx, sizeof(mx), "Master %dL (%s) Source Playback Enum", index + 1, name); - err = add_new_ctl(mixer, &usb_scarlett_ctl_dynamic_enum, 0x33, 0x00, + err = add_new_ctl(mixer, &usb_scarlett_ctl_dynamic_enum, + scarlett_ctl_enum_resume, 0x33, 0x00, 2*index, USB_MIXER_S16, 1, mx, &info->opt_master, &elem); if (err < 0) @@ -594,7 +622,8 @@ static int add_output_ctls(struct usb_mixer_interface *mixer, /* Add R channel source playback enumeration */ snprintf(mx, sizeof(mx), "Master %dR (%s) Source Playback Enum", index + 1, name); - err = add_new_ctl(mixer, &usb_scarlett_ctl_dynamic_enum, 0x33, 0x00, + err = add_new_ctl(mixer, &usb_scarlett_ctl_dynamic_enum, + scarlett_ctl_enum_resume, 0x33, 0x00, 2*index+1, USB_MIXER_S16, 1, mx, &info->opt_master, &elem); if (err < 0) @@ -824,13 +853,15 @@ static int scarlett_controls_create_generic(struct usb_mixer_interface *mixer, struct usb_mixer_elem_info *elem;
/* create master switch and playback volume */ - err = add_new_ctl(mixer, &usb_scarlett_ctl_switch, 0x0a, 0x01, 0, + err = add_new_ctl(mixer, &usb_scarlett_ctl_switch, + scarlett_ctl_resume, 0x0a, 0x01, 0, USB_MIXER_S16, 1, "Master Playback Switch", NULL, &elem); if (err < 0) return err;
- err = add_new_ctl(mixer, &usb_scarlett_ctl_master, 0x0a, 0x02, 0, + err = add_new_ctl(mixer, &usb_scarlett_ctl_master, + scarlett_ctl_resume, 0x0a, 0x02, 0, USB_MIXER_S16, 1, "Master Playback Volume", NULL, &elem); if (err < 0) @@ -848,7 +879,8 @@ static int scarlett_controls_create_generic(struct usb_mixer_interface *mixer, break; case SCARLETT_SWITCH_IMPEDANCE: sprintf(mx, "Input %d Impedance Switch", ctl->num); - err = add_new_ctl(mixer, &usb_scarlett_ctl_enum, 0x01, + err = add_new_ctl(mixer, &usb_scarlett_ctl_enum, + scarlett_ctl_enum_resume, 0x01, 0x09, ctl->num, USB_MIXER_S16, 1, mx, &opt_impedance, &elem); if (err < 0) @@ -856,7 +888,8 @@ static int scarlett_controls_create_generic(struct usb_mixer_interface *mixer, break; case SCARLETT_SWITCH_PAD: sprintf(mx, "Input %d Pad Switch", ctl->num); - err = add_new_ctl(mixer, &usb_scarlett_ctl_enum, 0x01, + err = add_new_ctl(mixer, &usb_scarlett_ctl_enum, + scarlett_ctl_enum_resume, 0x01, 0x0b, ctl->num, USB_MIXER_S16, 1, mx, &opt_pad, &elem); if (err < 0) @@ -912,7 +945,8 @@ int snd_scarlett_controls_create(struct usb_mixer_interface *mixer) for (i = 0; i < info->matrix_in; i++) { snprintf(mx, sizeof(mx), "Matrix %02d Input Playback Route", i+1); - err = add_new_ctl(mixer, &usb_scarlett_ctl_dynamic_enum, 0x32, + err = add_new_ctl(mixer, &usb_scarlett_ctl_dynamic_enum, + scarlett_ctl_enum_resume, 0x32, 0x06, i, USB_MIXER_S16, 1, mx, &info->opt_matrix, &elem); if (err < 0) @@ -921,7 +955,8 @@ int snd_scarlett_controls_create(struct usb_mixer_interface *mixer) for (o = 0; o < info->matrix_out; o++) { sprintf(mx, "Matrix %02d Mix %c Playback Volume", i+1, o+'A'); - err = add_new_ctl(mixer, &usb_scarlett_ctl, 0x3c, 0x00, + err = add_new_ctl(mixer, &usb_scarlett_ctl, + scarlett_ctl_resume, 0x3c, 0x00, (i << 3) + (o & 0x07), USB_MIXER_S16, 1, mx, NULL, &elem); if (err < 0) @@ -933,7 +968,8 @@ int snd_scarlett_controls_create(struct usb_mixer_interface *mixer) for (i = 0; i < info->input_len; i++) { snprintf(mx, sizeof(mx), "Input Source %02d Capture Route", i+1); - err = add_new_ctl(mixer, &usb_scarlett_ctl_dynamic_enum, 0x34, + err = add_new_ctl(mixer, &usb_scarlett_ctl_dynamic_enum, + scarlett_ctl_enum_resume, 0x34, 0x00, i, USB_MIXER_S16, 1, mx, &info->opt_master, &elem); if (err < 0) @@ -941,14 +977,15 @@ int snd_scarlett_controls_create(struct usb_mixer_interface *mixer) }
/* val_len == 1 needed here */ - err = add_new_ctl(mixer, &usb_scarlett_ctl_enum, 0x28, 0x01, 0, + err = add_new_ctl(mixer, &usb_scarlett_ctl_enum, + scarlett_ctl_enum_resume, 0x28, 0x01, 0, USB_MIXER_U8, 1, "Sample Clock Source", &opt_clock, &elem); if (err < 0) return err;
/* val_len == 1 and UAC2_CS_MEM */ - err = add_new_ctl(mixer, &usb_scarlett_ctl_sync, 0x3c, 0x00, 2, + err = add_new_ctl(mixer, &usb_scarlett_ctl_sync, NULL, 0x3c, 0x00, 2, USB_MIXER_U8, 1, "Sample Clock Sync Status", &opt_sync, &elem); if (err < 0)
participants (1)
-
Takashi Iwai