From 49fd46d2ffc92de3f4bd2e337f60b8b6052dc8b6 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Sun, 19 Oct 2014 09:11:25 +0200 Subject: ALSA: snd-usb: drop unused varible assigments Don't assign 'len' in cases where we don't make use of the returned value. Signed-off-by: Daniel Mack Signed-off-by: Takashi Iwai --- sound/usb/mixer.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'sound/usb') diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 2e4a9db..63a8adb 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -1290,9 +1290,8 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, kctl->id.name, sizeof(kctl->id.name), 1); if (!len) - len = snprintf(kctl->id.name, - sizeof(kctl->id.name), - "Feature %d", unitid); + snprintf(kctl->id.name, sizeof(kctl->id.name), + "Feature %d", unitid); } if (!mapped_name) @@ -1305,9 +1304,9 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, */ if (!mapped_name && !(state->oterm.type >> 16)) { if ((state->oterm.type & 0xff00) == 0x0100) - len = append_ctl_name(kctl, " Capture"); + append_ctl_name(kctl, " Capture"); else - len = append_ctl_name(kctl, " Playback"); + append_ctl_name(kctl, " Playback"); } append_ctl_name(kctl, control == UAC_FU_MUTE ? " Switch" : " Volume"); -- cgit v1.1 From c8dd33fc80cd344d28a1f6a7e1f0af1cf7a2ffd1 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Oct 2014 18:21:27 +0200 Subject: ALSA: 6fire: Use snd_ctl_enum_info() ... and reduce the open codes. Also add missing const to text arrays. Signed-off-by: Takashi Iwai --- sound/usb/6fire/control.c | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) (limited to 'sound/usb') diff --git a/sound/usb/6fire/control.c b/sound/usb/6fire/control.c index 184e398..54656ee 100644 --- a/sound/usb/6fire/control.c +++ b/sound/usb/6fire/control.c @@ -25,8 +25,8 @@ #include "comm.h" #include "chip.h" -static char *opt_coax_texts[2] = { "Optical", "Coax" }; -static char *line_phono_texts[2] = { "Line", "Phono" }; +static const char * const opt_coax_texts[2] = { "Optical", "Coax" }; +static const char * const line_phono_texts[2] = { "Line", "Phono" }; /* * data that needs to be sent to device. sets up card internal stuff. @@ -327,14 +327,7 @@ static int usb6fire_control_input_vol_get(struct snd_kcontrol *kcontrol, static int usb6fire_control_line_phono_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item > 1) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, - line_phono_texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, line_phono_texts); } static int usb6fire_control_line_phono_put(struct snd_kcontrol *kcontrol, @@ -361,14 +354,7 @@ static int usb6fire_control_line_phono_get(struct snd_kcontrol *kcontrol, static int usb6fire_control_opt_coax_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item > 1) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, - opt_coax_texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, opt_coax_texts); } static int usb6fire_control_opt_coax_put(struct snd_kcontrol *kcontrol, -- cgit v1.1 From 7bbd03e0143b562ff7d96f7e71c016104020b550 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Oct 2014 18:21:42 +0200 Subject: ALSA: usb-audio: Use snd_ctl_enum_info() ... and reduce the open codes. Also add missing const to text arrays. Signed-off-by: Takashi Iwai --- sound/usb/mixer_quirks.c | 35 ++++++----------------------------- 1 file changed, 6 insertions(+), 29 deletions(-) (limited to 'sound/usb') diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index f119a41..3980bf5 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -437,19 +437,9 @@ static void snd_audigy2nx_proc_read(struct snd_info_entry *entry, static int snd_emu0204_ch_switch_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static const char *texts[2] = {"1/2", - "3/4" - }; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item > 1) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); + static const char * const texts[2] = {"1/2", "3/4"}; - return 0; + return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts); } static int snd_emu0204_ch_switch_get(struct snd_kcontrol *kcontrol, @@ -735,25 +725,12 @@ struct snd_ftu_eff_switch_priv_val { static int snd_ftu_eff_switch_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static const char *texts[8] = {"Room 1", - "Room 2", - "Room 3", - "Hall 1", - "Hall 2", - "Plate", - "Delay", - "Echo" + static const char *const texts[8] = { + "Room 1", "Room 2", "Room 3", "Hall 1", + "Hall 2", "Plate", "Delay", "Echo" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 8; - if (uinfo->value.enumerated.item > 7) - uinfo->value.enumerated.item = 7; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - - return 0; + return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts); } static int snd_ftu_eff_switch_get(struct snd_kcontrol *kctl, -- cgit v1.1 From 4c8c3a4fcc021677c9a363b4e77f61dd09dbfd1a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 31 Oct 2014 11:00:23 +0100 Subject: ALSA: usb-audio: Flatten probe and disconnect functions The usb-audio probe and disconnect functions have been split just for adapting the (new!) API at 2.5 kernel time. We left them until now, partly because we wanted to build with the pretty old kernels in the external alsa-driver tree. But the support of such old kernels has been longly stopped, so it's good time to clean up this mess. One good point by this cleanup is that now the probe function returns a proper error code instead of only -EIO. Signed-off-by: Takashi Iwai --- sound/usb/card.c | 75 +++++++++++++++++++++++--------------------------------- 1 file changed, 30 insertions(+), 45 deletions(-) (limited to 'sound/usb') diff --git a/sound/usb/card.c b/sound/usb/card.c index 7ecd0e8..be16bdc 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -112,7 +112,7 @@ static struct usb_driver usb_audio_driver; /* * disconnect streams - * called from snd_usb_audio_disconnect() + * called from usb_audio_disconnect() */ static void snd_usb_stream_disconnect(struct list_head *head) { @@ -475,14 +475,14 @@ static int snd_usb_audio_create(struct usb_interface *intf, * only at the first time. the successive calls of this function will * append the pcm interface to the corresponding card. */ -static struct snd_usb_audio * -snd_usb_audio_probe(struct usb_device *dev, - struct usb_interface *intf, - const struct usb_device_id *usb_id) +static int usb_audio_probe(struct usb_interface *intf, + const struct usb_device_id *usb_id) { - const struct snd_usb_audio_quirk *quirk = (const struct snd_usb_audio_quirk *)usb_id->driver_info; - int i, err; + struct usb_device *dev = interface_to_usbdev(intf); + const struct snd_usb_audio_quirk *quirk = + (const struct snd_usb_audio_quirk *)usb_id->driver_info; struct snd_usb_audio *chip; + int i, err; struct usb_host_interface *alts; int ifnum; u32 id; @@ -492,10 +492,11 @@ snd_usb_audio_probe(struct usb_device *dev, id = USB_ID(le16_to_cpu(dev->descriptor.idVendor), le16_to_cpu(dev->descriptor.idProduct)); if (quirk && quirk->ifnum >= 0 && ifnum != quirk->ifnum) - goto __err_val; + return -ENXIO; - if (snd_usb_apply_boot_quirk(dev, intf, quirk) < 0) - goto __err_val; + err = snd_usb_apply_boot_quirk(dev, intf, quirk); + if (err < 0) + return err; /* * found a config. now register to ALSA @@ -508,6 +509,7 @@ snd_usb_audio_probe(struct usb_device *dev, if (usb_chip[i] && usb_chip[i]->dev == dev) { if (usb_chip[i]->shutdown) { dev_err(&dev->dev, "USB device is in the shutdown state, cannot create a card instance\n"); + err = -EIO; goto __error; } chip = usb_chip[i]; @@ -523,15 +525,16 @@ snd_usb_audio_probe(struct usb_device *dev, if (enable[i] && ! usb_chip[i] && (vid[i] == -1 || vid[i] == USB_ID_VENDOR(id)) && (pid[i] == -1 || pid[i] == USB_ID_PRODUCT(id))) { - if (snd_usb_audio_create(intf, dev, i, quirk, - &chip) < 0) { + err = snd_usb_audio_create(intf, dev, i, quirk, + &chip); + if (err < 0) goto __error; - } chip->pm_intf = intf; break; } if (!chip) { dev_err(&dev->dev, "no available usb audio device\n"); + err = -ENODEV; goto __error; } } @@ -548,28 +551,32 @@ snd_usb_audio_probe(struct usb_device *dev, err = 1; /* continue */ if (quirk && quirk->ifnum != QUIRK_NO_INTERFACE) { /* need some special handlings */ - if ((err = snd_usb_create_quirk(chip, intf, &usb_audio_driver, quirk)) < 0) + err = snd_usb_create_quirk(chip, intf, &usb_audio_driver, quirk); + if (err < 0) goto __error; } if (err > 0) { /* create normal USB audio interfaces */ - if (snd_usb_create_streams(chip, ifnum) < 0 || - snd_usb_create_mixer(chip, ifnum, ignore_ctl_error) < 0) { + err = snd_usb_create_streams(chip, ifnum); + if (err < 0) + goto __error; + err = snd_usb_create_mixer(chip, ifnum, ignore_ctl_error); + if (err < 0) goto __error; - } } /* we are allowed to call snd_card_register() many times */ - if (snd_card_register(chip->card) < 0) { + err = snd_card_register(chip->card); + if (err < 0) goto __error; - } usb_chip[chip->index] = chip; chip->num_interfaces++; chip->probing = 0; + usb_set_intfdata(intf, chip); mutex_unlock(®ister_mutex); - return chip; + return 0; __error: if (chip) { @@ -578,17 +585,16 @@ snd_usb_audio_probe(struct usb_device *dev, chip->probing = 0; } mutex_unlock(®ister_mutex); - __err_val: - return NULL; + return err; } /* * we need to take care of counter, since disconnection can be called also * many times as well as usb_audio_probe(). */ -static void snd_usb_audio_disconnect(struct usb_device *dev, - struct snd_usb_audio *chip) +static void usb_audio_disconnect(struct usb_interface *intf) { + struct snd_usb_audio *chip = usb_get_intfdata(intf); struct snd_card *card; struct list_head *p; @@ -630,27 +636,6 @@ static void snd_usb_audio_disconnect(struct usb_device *dev, } } -/* - * new 2.5 USB kernel API - */ -static int usb_audio_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct snd_usb_audio *chip; - chip = snd_usb_audio_probe(interface_to_usbdev(intf), intf, id); - if (chip) { - usb_set_intfdata(intf, chip); - return 0; - } else - return -EIO; -} - -static void usb_audio_disconnect(struct usb_interface *intf) -{ - snd_usb_audio_disconnect(interface_to_usbdev(intf), - usb_get_intfdata(intf)); -} - #ifdef CONFIG_PM int snd_usb_autoresume(struct snd_usb_audio *chip) -- cgit v1.1 From a6cece9d81990e729c1f9da2a5bff2d29f7df649 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 31 Oct 2014 11:24:32 +0100 Subject: ALSA: usb-audio: Pass direct struct pointer instead of list_head Some functions in mixer.c and endpoint.c receive list_head instead of the object itself. This is not obvious and rather error-prone. Let's pass the proper object directly instead. The functions in midi.c still receive list_head and this can't be changed since the object definition isn't exposed to the outside of midi.c, so left as is. Signed-off-by: Takashi Iwai --- sound/usb/card.c | 20 ++++++++++---------- sound/usb/endpoint.c | 7 ++----- sound/usb/endpoint.h | 2 +- sound/usb/mixer.c | 5 +---- sound/usb/mixer.h | 2 +- 5 files changed, 15 insertions(+), 21 deletions(-) (limited to 'sound/usb') diff --git a/sound/usb/card.c b/sound/usb/card.c index be16bdc..fa6c097 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -114,13 +114,11 @@ static struct usb_driver usb_audio_driver; * disconnect streams * called from usb_audio_disconnect() */ -static void snd_usb_stream_disconnect(struct list_head *head) +static void snd_usb_stream_disconnect(struct snd_usb_stream *as) { int idx; - struct snd_usb_stream *as; struct snd_usb_substream *subs; - as = list_entry(head, struct snd_usb_stream, list); for (idx = 0; idx < 2; idx++) { subs = &as->substream[idx]; if (!subs->num_formats) @@ -307,10 +305,10 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif) static int snd_usb_audio_free(struct snd_usb_audio *chip) { - struct list_head *p, *n; + struct snd_usb_endpoint *ep, *n; - list_for_each_safe(p, n, &chip->ep_list) - snd_usb_endpoint_free(p); + list_for_each_entry_safe(ep, n, &chip->ep_list, list) + snd_usb_endpoint_free(ep); mutex_destroy(&chip->mutex); kfree(chip); @@ -609,12 +607,14 @@ static void usb_audio_disconnect(struct usb_interface *intf) mutex_lock(®ister_mutex); chip->num_interfaces--; if (chip->num_interfaces <= 0) { + struct snd_usb_stream *as; struct snd_usb_endpoint *ep; + struct usb_mixer_interface *mixer; snd_card_disconnect(card); /* release the pcm resources */ - list_for_each(p, &chip->pcm_list) { - snd_usb_stream_disconnect(p); + list_for_each_entry(as, &chip->pcm_list, list) { + snd_usb_stream_disconnect(as); } /* release the endpoint resources */ list_for_each_entry(ep, &chip->ep_list, list) { @@ -625,8 +625,8 @@ static void usb_audio_disconnect(struct usb_interface *intf) snd_usbmidi_disconnect(p); } /* release mixer resources */ - list_for_each(p, &chip->mixer_list) { - snd_usb_mixer_disconnect(p); + list_for_each_entry(mixer, &chip->mixer_list, list) { + snd_usb_mixer_disconnect(mixer); } usb_chip[chip->index] = NULL; mutex_unlock(®ister_mutex); diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 114e3e7..167d0c1 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -1002,15 +1002,12 @@ void snd_usb_endpoint_release(struct snd_usb_endpoint *ep) /** * snd_usb_endpoint_free: Free the resources of an snd_usb_endpoint * - * @ep: the list header of the endpoint to free + * @ep: the endpoint to free * * This free all resources of the given ep. */ -void snd_usb_endpoint_free(struct list_head *head) +void snd_usb_endpoint_free(struct snd_usb_endpoint *ep) { - struct snd_usb_endpoint *ep; - - ep = list_entry(head, struct snd_usb_endpoint, list); kfree(ep); } diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h index e61ee5c..6428392 100644 --- a/sound/usb/endpoint.h +++ b/sound/usb/endpoint.h @@ -24,7 +24,7 @@ void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep); int snd_usb_endpoint_activate(struct snd_usb_endpoint *ep); void snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep); void snd_usb_endpoint_release(struct snd_usb_endpoint *ep); -void snd_usb_endpoint_free(struct list_head *head); +void snd_usb_endpoint_free(struct snd_usb_endpoint *ep); int snd_usb_endpoint_implicit_feedback_sink(struct snd_usb_endpoint *ep); int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep); diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 63a8adb..e4aaa21 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -2483,11 +2483,8 @@ _error: return err; } -void snd_usb_mixer_disconnect(struct list_head *p) +void snd_usb_mixer_disconnect(struct usb_mixer_interface *mixer) { - struct usb_mixer_interface *mixer; - - mixer = list_entry(p, struct usb_mixer_interface, list); usb_kill_urb(mixer->urb); usb_kill_urb(mixer->rc_urb); } diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h index 73b1f64..2c7b9c9 100644 --- a/sound/usb/mixer.h +++ b/sound/usb/mixer.h @@ -57,7 +57,7 @@ struct usb_mixer_elem_info { int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif, int ignore_error); -void snd_usb_mixer_disconnect(struct list_head *p); +void snd_usb_mixer_disconnect(struct usb_mixer_interface *mixer); void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid); -- cgit v1.1 From ae366c2049b48b54e5250cb57438bbebd1dbe610 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 31 Oct 2014 11:32:19 +0100 Subject: ALSA: usb-audio: Use strim() instead of open code Signed-off-by: Takashi Iwai --- sound/usb/card.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) (limited to 'sound/usb') diff --git a/sound/usb/card.c b/sound/usb/card.c index fa6c097..69725d5 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -321,16 +321,6 @@ static int snd_usb_audio_dev_free(struct snd_device *device) return snd_usb_audio_free(chip); } -static void remove_trailing_spaces(char *str) -{ - char *p; - - if (!*str) - return; - for (p = str + strlen(str) - 1; p >= str && isspace(*p); p--) - *p = 0; -} - /* * create a chip instance and set its names. */ @@ -414,7 +404,7 @@ static int snd_usb_audio_create(struct usb_interface *intf, USB_ID_PRODUCT(chip->usb_id)); } } - remove_trailing_spaces(card->shortname); + strim(card->shortname); /* retrieve the vendor and device strings as longname */ if (quirk && quirk->vendor_name && *quirk->vendor_name) { @@ -428,7 +418,7 @@ static int snd_usb_audio_create(struct usb_interface *intf, /* we don't really care if there isn't any vendor string */ } if (len > 0) { - remove_trailing_spaces(card->longname); + strim(card->longname); if (*card->longname) strlcat(card->longname, " ", sizeof(card->longname)); } -- cgit v1.1 From 67e225009bb15403341d313f51326113c61af7df Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Nov 2014 13:04:49 +0100 Subject: ALSA: usb-audio: Trigger PCM XRUN at XRUN The usb-audio driver detects XRUN at its complete callback, but the actual code to trigger PCM XRUN is commented out because it caused deadlock in the past. This patch revives the PCM trigger properly. It resulted in more than just enabling snd_pcm_stop(), but it had to deduce the PCM substream with proper NULL checks and holds the stream lock around the call. Signed-off-by: Takashi Iwai --- sound/usb/endpoint.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'sound/usb') diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 167d0c1..a467991 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -348,6 +348,8 @@ static void snd_complete_urb(struct urb *urb) { struct snd_urb_ctx *ctx = urb->context; struct snd_usb_endpoint *ep = ctx->ep; + struct snd_pcm_substream *substream; + unsigned long flags; int err; if (unlikely(urb->status == -ENOENT || /* unlinked */ @@ -364,8 +366,6 @@ static void snd_complete_urb(struct urb *urb) goto exit_clear; if (snd_usb_endpoint_implicit_feedback_sink(ep)) { - unsigned long flags; - spin_lock_irqsave(&ep->lock, flags); list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs); spin_unlock_irqrestore(&ep->lock, flags); @@ -389,7 +389,12 @@ static void snd_complete_urb(struct urb *urb) return; usb_audio_err(ep->chip, "cannot submit urb (err = %d)\n", err); - //snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); + if (ep->data_subs && ep->data_subs->pcm_substream) { + substream = ep->data_subs->pcm_substream; + snd_pcm_stream_lock_irqsave(substream, flags); + snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); + snd_pcm_stream_unlock_irqrestore(substream, flags); + } exit_clear: clear_bit(ctx->index, &ep->active_mask); -- cgit v1.1 From 1fb8510cdb5b7befe8a59f533c7fc12ef0dac73e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 7 Nov 2014 17:08:28 +0100 Subject: ALSA: pcm: Add snd_pcm_stop_xrun() helper Add a new helper function snd_pcm_stop_xrun() to the standard sequnce lock/snd_pcm_stop(XRUN)/unlock by a single call, and replace the existing open codes with this helper. The function checks the PCM running state to prevent setting the wrong state, too, for more safety. Signed-off-by: Takashi Iwai --- sound/usb/6fire/pcm.c | 17 ++++------------- sound/usb/endpoint.c | 4 +--- sound/usb/misc/ua101.c | 18 ++++-------------- sound/usb/usx2y/usbusx2yaudio.c | 9 ++------- 4 files changed, 11 insertions(+), 37 deletions(-) (limited to 'sound/usb') diff --git a/sound/usb/6fire/pcm.c b/sound/usb/6fire/pcm.c index ba40489..36f4115 100644 --- a/sound/usb/6fire/pcm.c +++ b/sound/usb/6fire/pcm.c @@ -679,25 +679,16 @@ int usb6fire_pcm_init(struct sfire_chip *chip) void usb6fire_pcm_abort(struct sfire_chip *chip) { struct pcm_runtime *rt = chip->pcm; - unsigned long flags; int i; if (rt) { rt->panic = true; - if (rt->playback.instance) { - snd_pcm_stream_lock_irqsave(rt->playback.instance, flags); - snd_pcm_stop(rt->playback.instance, - SNDRV_PCM_STATE_XRUN); - snd_pcm_stream_unlock_irqrestore(rt->playback.instance, flags); - } + if (rt->playback.instance) + snd_pcm_stop_xrun(rt->playback.instance); - if (rt->capture.instance) { - snd_pcm_stream_lock_irqsave(rt->capture.instance, flags); - snd_pcm_stop(rt->capture.instance, - SNDRV_PCM_STATE_XRUN); - snd_pcm_stream_unlock_irqrestore(rt->capture.instance, flags); - } + if (rt->capture.instance) + snd_pcm_stop_xrun(rt->capture.instance); for (i = 0; i < PCM_N_URBS; i++) { usb_poison_urb(&rt->in_urbs[i].instance); diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index a467991..03b0744 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -391,9 +391,7 @@ static void snd_complete_urb(struct urb *urb) usb_audio_err(ep->chip, "cannot submit urb (err = %d)\n", err); if (ep->data_subs && ep->data_subs->pcm_substream) { substream = ep->data_subs->pcm_substream; - snd_pcm_stream_lock_irqsave(substream, flags); - snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); - snd_pcm_stream_unlock_irqrestore(substream, flags); + snd_pcm_stop_xrun(substream); } exit_clear: diff --git a/sound/usb/misc/ua101.c b/sound/usb/misc/ua101.c index a1bab14..9581089 100644 --- a/sound/usb/misc/ua101.c +++ b/sound/usb/misc/ua101.c @@ -613,24 +613,14 @@ static int start_usb_playback(struct ua101 *ua) static void abort_alsa_capture(struct ua101 *ua) { - unsigned long flags; - - if (test_bit(ALSA_CAPTURE_RUNNING, &ua->states)) { - snd_pcm_stream_lock_irqsave(ua->capture.substream, flags); - snd_pcm_stop(ua->capture.substream, SNDRV_PCM_STATE_XRUN); - snd_pcm_stream_unlock_irqrestore(ua->capture.substream, flags); - } + if (test_bit(ALSA_CAPTURE_RUNNING, &ua->states)) + snd_pcm_stop_xrun(ua->capture.substream); } static void abort_alsa_playback(struct ua101 *ua) { - unsigned long flags; - - if (test_bit(ALSA_PLAYBACK_RUNNING, &ua->states)) { - snd_pcm_stream_lock_irqsave(ua->playback.substream, flags); - snd_pcm_stop(ua->playback.substream, SNDRV_PCM_STATE_XRUN); - snd_pcm_stream_unlock_irqrestore(ua->playback.substream, flags); - } + if (test_bit(ALSA_PLAYBACK_RUNNING, &ua->states)) + snd_pcm_stop_xrun(ua->playback.substream); } static int set_stream_hw(struct ua101 *ua, struct snd_pcm_substream *substream, diff --git a/sound/usb/usx2y/usbusx2yaudio.c b/sound/usb/usx2y/usbusx2yaudio.c index a63330d..61d5dc2 100644 --- a/sound/usb/usx2y/usbusx2yaudio.c +++ b/sound/usb/usx2y/usbusx2yaudio.c @@ -272,13 +272,8 @@ static void usX2Y_clients_stop(struct usX2Ydev *usX2Y) for (s = 0; s < 4; s++) { struct snd_usX2Y_substream *subs = usX2Y->subs[s]; if (subs) { - if (atomic_read(&subs->state) >= state_PRERUNNING) { - unsigned long flags; - - snd_pcm_stream_lock_irqsave(subs->pcm_substream, flags); - snd_pcm_stop(subs->pcm_substream, SNDRV_PCM_STATE_XRUN); - snd_pcm_stream_unlock_irqrestore(subs->pcm_substream, flags); - } + if (atomic_read(&subs->state) >= state_PRERUNNING) + snd_pcm_stop_xrun(subs->pcm_substream); for (u = 0; u < NRURBS; u++) { struct urb *urb = subs->urb[u]; if (NULL != urb) -- cgit v1.1 From d4b8fc66f770e9b79830cfe6c342846293b99fda Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 9 Nov 2014 18:21:23 +0100 Subject: ALSA: usb-audio: Allow multiple entries for the same iface in composite quirk Currently the composite quirk doesn't work when multiple entries are assigned to the same interface because it marks the interface as claimed then checks whether the interface has been already claimed for the secondary entry. But, if you look at the code, you'll notice that multiple entries are allowed if the entry is the current interface; i.e. the current behavior is anyway inconsistent, and this is an unintended shortcoming. This patch fixes the problem by marking the relevant interfaces as claimed after applying the all composite entries. This fix will be needed for the upcoming enhancements for Digidesign Mbox 1 quirks. Reviewed-by: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/usb/quirks.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'sound/usb') diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index d2aa45a..e9ff3a6 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -58,9 +58,17 @@ static int create_composite_quirk(struct snd_usb_audio *chip, err = snd_usb_create_quirk(chip, iface, driver, quirk); if (err < 0) return err; - if (quirk->ifnum != probed_ifnum) + } + + for (quirk = quirk->data; quirk->ifnum >= 0; ++quirk) { + iface = usb_ifnum_to_if(chip->dev, quirk->ifnum); + if (!iface) + continue; + if (quirk->ifnum != probed_ifnum && + !usb_interface_claimed(iface)) usb_driver_claim_interface(driver, iface, (void *)-1L); } + return 0; } -- cgit v1.1 From 85a8181329a919d58b7ef99211251f47d5e1049e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 10 Nov 2014 07:41:59 +0100 Subject: ALSA: usb-audio: Fix Oops by composite quirk enhancement The quirk argument itself was used as iterator, so it cannot be taken back to the original value, obviously. Fixes: d4b8fc66f770 ('ALSA: usb-audio: Allow multiple entries for the same iface in composite quirk') Signed-off-by: Takashi Iwai --- sound/usb/quirks.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'sound/usb') diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index e9ff3a6..809d7fa 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -43,12 +43,13 @@ static int create_composite_quirk(struct snd_usb_audio *chip, struct usb_interface *iface, struct usb_driver *driver, - const struct snd_usb_audio_quirk *quirk) + const struct snd_usb_audio_quirk *quirk_comp) { int probed_ifnum = get_iface_desc(iface->altsetting)->bInterfaceNumber; + const struct snd_usb_audio_quirk *quirk; int err; - for (quirk = quirk->data; quirk->ifnum >= 0; ++quirk) { + for (quirk = quirk_comp->data; quirk->ifnum >= 0; ++quirk) { iface = usb_ifnum_to_if(chip->dev, quirk->ifnum); if (!iface) continue; @@ -60,7 +61,7 @@ static int create_composite_quirk(struct snd_usb_audio *chip, return err; } - for (quirk = quirk->data; quirk->ifnum >= 0; ++quirk) { + for (quirk = quirk_comp->data; quirk->ifnum >= 0; ++quirk) { iface = usb_ifnum_to_if(chip->dev, quirk->ifnum); if (!iface) continue; -- cgit v1.1 From d497a82fb18ed4b73c08f8b5a0935f937e2ea1fb Mon Sep 17 00:00:00 2001 From: Damien Zammit Date: Wed, 12 Nov 2014 01:09:54 +1100 Subject: ALSA: usb-audio: Add mixer control for Digidesign Mbox 1 clock source This patch provides the infrastructure for the Digidesign Mbox 1 to have a mixer control for selecting the clock source. Valid options are Internal and S/PDIF external sync. A non-documented command is sent to the device to enable this feature found by reverse engineering and bus snooping. Signed-off-by: Damien Zammit Signed-off-by: Takashi Iwai --- sound/usb/mixer_maps.c | 9 ++++ sound/usb/mixer_quirks.c | 129 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 138 insertions(+) (limited to 'sound/usb') diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c index d1d72ff..1994d41 100644 --- a/sound/usb/mixer_maps.c +++ b/sound/usb/mixer_maps.c @@ -179,6 +179,11 @@ static struct usbmix_name_map audigy2nx_map[] = { { 0 } /* terminator */ }; +static struct usbmix_name_map mbox1_map[] = { + { 1, "Clock" }, + { 0 } /* terminator */ +}; + static struct usbmix_selector_map c400_selectors[] = { { .id = 0x80, @@ -416,6 +421,10 @@ static struct usbmix_ctl_map usbmix_ctl_maps[] = { .map = aureon_51_2_map, }, { + .id = USB_ID(0x0dba, 0x1000), + .map = mbox1_map, + }, + { .id = USB_ID(0x13e5, 0x0001), .map = scratch_live_map, .ignore_ctl_error = 1, diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 3980bf5..4520316 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -565,6 +565,131 @@ static int snd_xonar_u1_controls_create(struct usb_mixer_interface *mixer) return 0; } +/* Digidesign Mbox 1 clock source switch (internal/spdif) */ + +static int snd_mbox1_switch_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.enumerated.item[0] = kctl->private_value; + return 0; +} + +static int snd_mbox1_switch_put(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_usb_audio *chip; + struct usb_mixer_interface *mixer; + 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; + goto err; + } + + /* Prepare for magic command to toggle clock source */ + err = snd_usb_ctl_msg(chip->dev, + usb_rcvctrlpipe(chip->dev, 0), 0x81, + USB_DIR_IN | + USB_TYPE_CLASS | + USB_RECIP_INTERFACE, 0x00, 0x500, buff, 1); + if (err < 0) + goto err; + err = snd_usb_ctl_msg(chip->dev, + usb_rcvctrlpipe(chip->dev, 0), 0x81, + USB_DIR_IN | + USB_TYPE_CLASS | + USB_RECIP_ENDPOINT, 0x100, 0x81, buff, 3); + if (err < 0) + goto err; + + /* 2 possibilities: Internal -> send sample rate + * S/PDIF sync -> send zeroes + * NB: Sample rate locked to 48kHz on purpose to + * prevent user from resetting the sample rate + * while S/PDIF sync is enabled and confusing + * this configuration. + */ + if (new_val == 0) { + buff[0] = 0x80; + buff[1] = 0xbb; + buff[2] = 0x00; + } else { + buff[0] = buff[1] = buff[2] = 0x00; + } + + /* Send the magic command to toggle the clock source */ + err = snd_usb_ctl_msg(chip->dev, + usb_sndctrlpipe(chip->dev, 0), 0x1, + USB_TYPE_CLASS | + USB_RECIP_ENDPOINT, 0x100, 0x81, buff, 3); + if (err < 0) + goto err; + err = snd_usb_ctl_msg(chip->dev, + usb_rcvctrlpipe(chip->dev, 0), 0x81, + USB_DIR_IN | + USB_TYPE_CLASS | + USB_RECIP_ENDPOINT, 0x100, 0x81, buff, 3); + if (err < 0) + goto err; + err = snd_usb_ctl_msg(chip->dev, + usb_rcvctrlpipe(chip->dev, 0), 0x81, + USB_DIR_IN | + USB_TYPE_CLASS | + 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 < 0 ? err : 1; +} + +static int snd_mbox1_switch_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static const char *const texts[2] = { + "Internal", + "S/PDIF" + }; + + return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts); +} + +static struct snd_kcontrol_new snd_mbox1_switch = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Clock Source", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_mbox1_switch_info, + .get = snd_mbox1_switch_get, + .put = snd_mbox1_switch_put, + .private_value = 0 +}; + +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)); +} + /* Native Instruments device quirks */ #define _MAKE_NI_CONTROL(bRequest,wIndex) ((bRequest) << 16 | (wIndex)) @@ -1632,6 +1757,10 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) err = snd_microii_controls_create(mixer); break; + case USB_ID(0x0dba, 0x1000): /* Digidesign Mbox 1 */ + err = snd_mbox1_create_sync_switch(mixer); + break; + case USB_ID(0x17cc, 0x1011): /* Traktor Audio 6 */ err = snd_nativeinstruments_create_mixer(mixer, snd_nativeinstruments_ta6_mixers, -- cgit v1.1 From c63fcb9b67777b906c4515a868afbd96bae4e799 Mon Sep 17 00:00:00 2001 From: Damien Zammit Date: Wed, 12 Nov 2014 01:09:55 +1100 Subject: ALSA: usb-audio: Add duplex mode for Digidesign Mbox 1 and enable mixer This patch provides duplex support for the Digidesign Mbox 1 sound card and has been a work in progress for about a year. Users have confirmed on my website that previous versions of this patch have worked on the hardware and I have been testing extensively. It also enables the mixer control for providing clock source selector based on the previous patch. The sample rate has been hardcoded to 48kHz because it works better with the S/PDIF sync mode when the sample rate is locked. This is the highest rate that the device supports and no loss of functionality is observed by restricting the sample rate apart from the inability to selec a lower rate. Signed-off-by: Damien Zammit Signed-off-by: Takashi Iwai --- sound/usb/quirks-table.h | 41 ++++++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 9 deletions(-) (limited to 'sound/usb') diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index c657752..13f44fd 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -2944,7 +2944,7 @@ YAMAHA_DEVICE(0x7010, "UB99"), .data = (const struct snd_usb_audio_quirk[]){ { .ifnum = 0, - .type = QUIRK_IGNORE_INTERFACE, + .type = QUIRK_AUDIO_STANDARD_MIXER, }, { .ifnum = 1, @@ -2955,16 +2955,40 @@ YAMAHA_DEVICE(0x7010, "UB99"), .iface = 1, .altsetting = 1, .altset_idx = 1, - .attributes = UAC_EP_CS_ATTR_SAMPLE_RATE, + .attributes = 0x4, .endpoint = 0x02, - .ep_attr = 0x01, - .rates = SNDRV_PCM_RATE_44100 | - SNDRV_PCM_RATE_48000, - .rate_min = 44100, + .ep_attr = USB_ENDPOINT_XFER_ISOC | + USB_ENDPOINT_SYNC_SYNC, + .maxpacksize = 0x130, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, .rate_max = 48000, - .nr_rates = 2, + .nr_rates = 1, .rate_table = (unsigned int[]) { - 44100, 48000 + 48000 + } + } + }, + { + .ifnum = 1, + .type = QUIRK_AUDIO_FIXED_ENDPOINT, + .data = &(const struct audioformat) { + .formats = SNDRV_PCM_FMTBIT_S24_3BE, + .channels = 2, + .iface = 1, + .altsetting = 1, + .altset_idx = 1, + .attributes = 0x4, + .endpoint = 0x81, + .ep_attr = USB_ENDPOINT_XFER_ISOC | + USB_ENDPOINT_SYNC_ASYNC, + .maxpacksize = 0x130, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .nr_rates = 1, + .rate_table = (unsigned int[]) { + 48000 } } }, @@ -2972,7 +2996,6 @@ YAMAHA_DEVICE(0x7010, "UB99"), .ifnum = -1 } } - } }, -- cgit v1.1 From ef9566a3a1c0e46dadfa6c722e8a685ac0cea081 Mon Sep 17 00:00:00 2001 From: Chris J Arges Date: Wed, 12 Nov 2014 12:06:59 -0600 Subject: Revert "ALSA: usb-audio: Add quirk for Focusrite Scarlett This reverts commit 1762a59d8e8b5e99f6f4a0f292b40f3cacb108ba. This quirk is not needed because support for the Scarlett mixers will be added. Signed-off-by: Chris J Arges Signed-off-by: Takashi Iwai --- sound/usb/quirks-table.h | 51 ------------------------------------------------ 1 file changed, 51 deletions(-) (limited to 'sound/usb') diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 13f44fd..013cba8 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -2667,57 +2667,6 @@ YAMAHA_DEVICE(0x7010, "UB99"), .type = QUIRK_MIDI_NOVATION } }, -{ - /* - * Focusrite Scarlett 18i6 - * - * Avoid mixer creation, which otherwise fails because some of - * the interface descriptor subtypes for interface 0 are - * unknown. That should be fixed or worked-around but this at - * least allows the device to be used successfully with a DAW - * and an external mixer. See comments below about other - * ignored interfaces. - */ - USB_DEVICE(0x1235, 0x8004), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Focusrite", - .product_name = "Scarlett 18i6", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = & (const struct snd_usb_audio_quirk[]) { - { - /* InterfaceSubClass 1 (Control Device) */ - .ifnum = 0, - .type = QUIRK_IGNORE_INTERFACE - }, - { - .ifnum = 1, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 2, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - /* InterfaceSubClass 1 (Control Device) */ - .ifnum = 3, - .type = QUIRK_IGNORE_INTERFACE - }, - { - .ifnum = 4, - .type = QUIRK_MIDI_STANDARD_INTERFACE - }, - { - /* InterfaceSubClass 1 (Device Firmware Update) */ - .ifnum = 5, - .type = QUIRK_IGNORE_INTERFACE - }, - { - .ifnum = -1 - } - } - } -}, /* Access Music devices */ { -- cgit v1.1 From f41d6049d18694e8b3d938464432d0e51f671089 Mon Sep 17 00:00:00 2001 From: Chris J Arges Date: Wed, 12 Nov 2014 12:07:00 -0600 Subject: ALSA: usb-audio: Add private_data pointer to usb_mixer_elem_info Add a private_data pointer to usb_mixer_elem_info to allow other mixer implementations to extend the structure as necessary. Signed-off-by: Chris J Arges Signed-off-by: Takashi Iwai --- sound/usb/mixer.h | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/usb') diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h index 2c7b9c9..7423f99 100644 --- a/sound/usb/mixer.h +++ b/sound/usb/mixer.h @@ -53,6 +53,7 @@ struct usb_mixer_elem_info { int cached; int cache_val[MAX_CHANNELS]; u8 initialized; + void *private_data; }; int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif, -- cgit v1.1 From eef90451605d79a5703756505087e0ef16da9077 Mon Sep 17 00:00:00 2001 From: Chris J Arges Date: Wed, 12 Nov 2014 12:07:01 -0600 Subject: ALSA: usb-audio: make set_*_mix_values functions public Make the functions set_cur_mix_value and get_cur_mix_value accessible by files that include mixer.h. In addition make usb_mixer_elem_free accessible. This allows reuse of these functions by mixers that may require quirks. The following summarizes the renamed functions: - set_cur_mix_value -> snd_usb_set_cur_mix_value - get_cur_mix_value -> snd_usb_get_cur_mix_value - usb_mixer_elem_free -> snd_usb_mixer_elem_free Signed-off-by: Chris J Arges Signed-off-by: Takashi Iwai --- sound/usb/mixer.c | 34 +++++++++++++++++----------------- sound/usb/mixer.h | 8 ++++++++ sound/usb/mixer_quirks.c | 9 +-------- 3 files changed, 26 insertions(+), 25 deletions(-) (limited to 'sound/usb') diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index e4aaa21..14e1455 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -412,7 +412,7 @@ static inline int get_cur_mix_raw(struct usb_mixer_elem_info *cval, value); } -static int get_cur_mix_value(struct usb_mixer_elem_info *cval, +int snd_usb_get_cur_mix_value(struct usb_mixer_elem_info *cval, int channel, int index, int *value) { int err; @@ -497,7 +497,7 @@ static int set_cur_ctl_value(struct usb_mixer_elem_info *cval, return snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR, validx, value); } -static int set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel, +int snd_usb_set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel, int index, int value) { int err; @@ -815,7 +815,7 @@ static struct usb_feature_control_info audio_feature_info[] = { }; /* private_free callback */ -static void usb_mixer_elem_free(struct snd_kcontrol *kctl) +void snd_usb_mixer_elem_free(struct snd_kcontrol *kctl) { kfree(kctl->private_data); kctl->private_data = NULL; @@ -998,7 +998,7 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval, else test -= cval->res; if (test < cval->min || test > cval->max || - set_cur_mix_value(cval, minchn, 0, test) || + snd_usb_set_cur_mix_value(cval, minchn, 0, test) || get_cur_mix_raw(cval, minchn, &check)) { cval->res = last_valid_res; break; @@ -1007,7 +1007,7 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval, break; cval->res *= 2; } - set_cur_mix_value(cval, minchn, 0, saved); + snd_usb_set_cur_mix_value(cval, minchn, 0, saved); } cval->initialized = 1; @@ -1086,7 +1086,7 @@ static int mixer_ctl_feature_get(struct snd_kcontrol *kcontrol, for (c = 0; c < MAX_CHANNELS; c++) { if (!(cval->cmask & (1 << c))) continue; - err = get_cur_mix_value(cval, c + 1, cnt, &val); + err = snd_usb_get_cur_mix_value(cval, c + 1, cnt, &val); if (err < 0) return cval->mixer->ignore_ctl_error ? 0 : err; val = get_relative_value(cval, val); @@ -1096,7 +1096,7 @@ static int mixer_ctl_feature_get(struct snd_kcontrol *kcontrol, return 0; } else { /* master channel */ - err = get_cur_mix_value(cval, 0, 0, &val); + err = snd_usb_get_cur_mix_value(cval, 0, 0, &val); if (err < 0) return cval->mixer->ignore_ctl_error ? 0 : err; val = get_relative_value(cval, val); @@ -1118,26 +1118,26 @@ static int mixer_ctl_feature_put(struct snd_kcontrol *kcontrol, for (c = 0; c < MAX_CHANNELS; c++) { if (!(cval->cmask & (1 << c))) continue; - err = get_cur_mix_value(cval, c + 1, cnt, &oval); + err = snd_usb_get_cur_mix_value(cval, c + 1, cnt, &oval); if (err < 0) return cval->mixer->ignore_ctl_error ? 0 : err; val = ucontrol->value.integer.value[cnt]; val = get_abs_value(cval, val); if (oval != val) { - set_cur_mix_value(cval, c + 1, cnt, val); + snd_usb_set_cur_mix_value(cval, c + 1, cnt, val); changed = 1; } cnt++; } } else { /* master channel */ - err = get_cur_mix_value(cval, 0, 0, &oval); + err = snd_usb_get_cur_mix_value(cval, 0, 0, &oval); if (err < 0) return cval->mixer->ignore_ctl_error ? 0 : err; val = ucontrol->value.integer.value[0]; val = get_abs_value(cval, val); if (val != oval) { - set_cur_mix_value(cval, 0, 0, val); + snd_usb_set_cur_mix_value(cval, 0, 0, val); changed = 1; } } @@ -1250,7 +1250,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, /* * If all channels in the mask are marked read-only, make the control - * read-only. set_cur_mix_value() will check the mask again and won't + * read-only. snd_usb_set_cur_mix_value() will check the mask again and won't * issue write commands to read-only channels. */ if (cval->channels == readonly_mask) @@ -1263,7 +1263,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, kfree(cval); return; } - kctl->private_free = usb_mixer_elem_free; + kctl->private_free = snd_usb_mixer_elem_free; len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name)); mapped_name = len != 0; @@ -1546,7 +1546,7 @@ static void build_mixer_unit_ctl(struct mixer_build *state, kfree(cval); return; } - kctl->private_free = usb_mixer_elem_free; + kctl->private_free = snd_usb_mixer_elem_free; len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name)); if (!len) @@ -1846,7 +1846,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, kfree(cval); return -ENOMEM; } - kctl->private_free = usb_mixer_elem_free; + kctl->private_free = snd_usb_mixer_elem_free; if (check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name))) { /* nothing */ ; @@ -2526,7 +2526,7 @@ static int restore_mixer_value(struct usb_mixer_elem_info *cval) if (!(cval->cmask & (1 << c))) continue; if (cval->cached & (1 << c)) { - err = set_cur_mix_value(cval, c + 1, idx, + err = snd_usb_set_cur_mix_value(cval, c + 1, idx, cval->cache_val[idx]); if (err < 0) return err; @@ -2536,7 +2536,7 @@ static int restore_mixer_value(struct usb_mixer_elem_info *cval) } else { /* master */ if (cval->cached) { - err = set_cur_mix_value(cval, 0, 0, *cval->cache_val); + err = snd_usb_set_cur_mix_value(cval, 0, 0, *cval->cache_val); if (err < 0) return err; } diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h index 7423f99..2478a84 100644 --- a/sound/usb/mixer.h +++ b/sound/usb/mixer.h @@ -76,4 +76,12 @@ int snd_usb_mixer_suspend(struct usb_mixer_interface *mixer); int snd_usb_mixer_resume(struct usb_mixer_interface *mixer, bool reset_resume); #endif +int snd_usb_set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel, + int index, int value); + +int snd_usb_get_cur_mix_value(struct usb_mixer_elem_info *cval, + int channel, int index, int *value); + +extern void snd_usb_mixer_elem_free(struct snd_kcontrol *kctl); + #endif /* __USBMIXER_H */ diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 4520316..b8b1f48 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -52,13 +52,6 @@ struct std_mono_table { snd_kcontrol_tlv_rw_t *tlv_callback; }; -/* private_free callback */ -static void usb_mixer_elem_free(struct snd_kcontrol *kctl) -{ - kfree(kctl->private_data); - kctl->private_data = NULL; -} - /* This function allows for the creation of standard UAC controls. * See the quirks for M-Audio FTUs or Ebox-44. * If you don't want to set a TLV callback pass NULL. @@ -108,7 +101,7 @@ static int snd_create_std_mono_ctl_offset(struct usb_mixer_interface *mixer, /* Set name */ snprintf(kctl->id.name, sizeof(kctl->id.name), name); - kctl->private_free = usb_mixer_elem_free; + kctl->private_free = snd_usb_mixer_elem_free; /* set TLV */ if (tlv_callback) { -- cgit v1.1 From 76b188c4b370876018e3a778ec11a94a5316dbe4 Mon Sep 17 00:00:00 2001 From: Chris J Arges Date: Wed, 12 Nov 2014 12:07:02 -0600 Subject: ALSA: usb-audio: Scarlett mixer interface for 6i6, 18i6, 18i8 and 18i20 This code contains the Scarlett mixer interface code that was originally written by Tobias Hoffman and Robin Gareus. Because the device doesn't properly implement UAC2 this code adds a mixer quirk for the device. Changes from the original code include removing the metering code along with dead code and comments. Compiler warnings were fixed. The code to initialize the sampling rate was causing a crash this was fixed as discussed on the mailing list. Error, and info messages were convered to dev_err and dev_info interfaces. The custom scarlett_mixer_elem_info struct was replaced with the more generic usb_mixer_elem_info to be able to recycle more code from mixer.c. This patch also makes additional modifications based on upstream comments. Individual control creation functions are removed and a generic function is no used. Macros for function calls are removed to improve readability. Hardcoded control initialization is removed. Save to HW functionality has been removed. Strings for enums are created dynamically for the mixer. Strings used for controls are now SNDRV_CTL_ELEM_ID_NAME_MAXLEN length. Signed-off-by: Chris J Arges Signed-off-by: Takashi Iwai --- sound/usb/Makefile | 1 + sound/usb/mixer_quirks.c | 9 + sound/usb/mixer_scarlett.c | 967 +++++++++++++++++++++++++++++++++++++++++++++ sound/usb/mixer_scarlett.h | 6 + 4 files changed, 983 insertions(+) create mode 100644 sound/usb/mixer_scarlett.c create mode 100644 sound/usb/mixer_scarlett.h (limited to 'sound/usb') diff --git a/sound/usb/Makefile b/sound/usb/Makefile index 2b92f0d..bcee406 100644 --- a/sound/usb/Makefile +++ b/sound/usb/Makefile @@ -9,6 +9,7 @@ snd-usb-audio-objs := card.o \ helper.o \ mixer.o \ mixer_quirks.o \ + mixer_scarlett.o \ pcm.o \ proc.o \ quirks.o \ diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index b8b1f48..7b9331a 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -41,6 +41,7 @@ #include "usbaudio.h" #include "mixer.h" #include "mixer_quirks.h" +#include "mixer_scarlett.h" #include "helper.h" extern struct snd_kcontrol_new *snd_usb_feature_unit_ctl; @@ -1770,6 +1771,14 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) /* detection is disabled in mixer_maps.c */ err = snd_create_std_mono_table(mixer, ebox44_table); break; + + case USB_ID(0x1235, 0x8012): /* Focusrite Scarlett 6i6 */ + case USB_ID(0x1235, 0x8002): /* Focusrite Scarlett 8i6 */ + case USB_ID(0x1235, 0x8004): /* Focusrite Scarlett 18i6 */ + case USB_ID(0x1235, 0x8014): /* Focusrite Scarlett 18i8 */ + case USB_ID(0x1235, 0x800c): /* Focusrite Scarlett 18i20 */ + err = snd_scarlett_controls_create(mixer); + break; } return err; diff --git a/sound/usb/mixer_scarlett.c b/sound/usb/mixer_scarlett.c new file mode 100644 index 0000000..a0a8745 --- /dev/null +++ b/sound/usb/mixer_scarlett.c @@ -0,0 +1,967 @@ +/* + * Scarlett Driver for ALSA + * + * Copyright (c) 2013 by Tobias Hoffmann + * Copyright (c) 2013 by Robin Gareus + * Copyright (c) 2002 by Takashi Iwai + * Copyright (c) 2014 by Chris J Arges + * + * Many codes borrowed from audio.c by + * Alan Cox (alan at lxorguk.ukuu.org.uk) + * Thomas Sailer (sailer at ife.ee.ethz.ch) + * + * Code cleanup: + * David Henningsson + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/* + * Rewritten and extended to support more models, e.g. Scarlett 18i8. + * + * Auto-detection via UAC2 is not feasible to properly discover the vast + * majority of features. It's related to both Linux/ALSA's UAC2 as well as + * Focusrite's implementation of it. Eventually quirks may be sufficient but + * right now it's a major headache to work arount these things. + * + * NB. Neither the OSX nor the win driver provided by Focusrite performs + * discovery, they seem to operate the same as this driver. + */ + +/* Mixer Interface for the Focusrite Scarlett 18i6 audio interface. + * + * The protocol was reverse engineered by looking at communication between + * Scarlett MixControl (v 1.2.128.0) and the Focusrite(R) Scarlett 18i6 + * (firmware v305) using wireshark and usbmon in January 2013. + * Extended in July 2013. + * + * this mixer gives complete access to all features of the device: + * - change Impedance of inputs (Line-in, Mic / Instrument, Hi-Z) + * - select clock source + * - dynamic input to mixer-matrix assignment + * - 18 x 6 mixer-matrix gain stages + * - bus routing & volume control + * - automatic re-initialization on connect if device was power-cycled + * + * USB URB commands overview (bRequest = 0x01 = UAC2_CS_CUR) + * wIndex + * 0x01 Analog Input line/instrument impedance switch, wValue=0x0901 + + * channel, data=Line/Inst (2bytes) + * pad (-10dB) switch, wValue=0x0b01 + channel, data=Off/On (2bytes) + * ?? wValue=0x0803/04, ?? (2bytes) + * 0x0a Master Volume, wValue=0x0200+bus[0:all + only 1..4?] data(2bytes) + * Bus Mute/Unmute wValue=0x0100+bus[0:all + only 1..4?], data(2bytes) + * 0x28 Clock source, wValue=0x0100, data={1:int,2:spdif,3:adat} (1byte) + * 0x29 Set Sample-rate, wValue=0x0100, data=sample-rate(4bytes) + * 0x32 Mixer mux, wValue=0x0600 + mixer-channel, data=input-to-connect(2bytes) + * 0x33 Output mux, wValue=bus, data=input-to-connect(2bytes) + * 0x34 Capture mux, wValue=0...18, data=input-to-connect(2bytes) + * 0x3c Matrix Mixer gains, wValue=mixer-node data=gain(2bytes) + * ?? [sometimes](4bytes, e.g 0x000003be 0x000003bf ...03ff) + * + * USB reads: (i.e. actually issued by original software) + * 0x01 wValue=0x0901+channel (1byte!!), wValue=0x0b01+channed (1byte!!) + * 0x29 wValue=0x0100 sample-rate(4bytes) + * wValue=0x0200 ?? 1byte (only once) + * 0x2a wValue=0x0100 ?? 4bytes, sample-rate2 ?? + * + * USB reads with bRequest = 0x03 = UAC2_CS_MEM + * 0x3c wValue=0x0002 1byte: sync status (locked=1) + * wValue=0x0000 18*2byte: peak meter (inputs) + * wValue=0x0001 8(?)*2byte: peak meter (mix) + * wValue=0x0003 6*2byte: peak meter (pcm/daw) + * + * USB write with bRequest = 0x03 + * 0x3c Save settings to hardware: wValue=0x005a, data=0xa5 + * + * + * + * /--------------\ 18chn 6chn /--------------\ + * | Hardware in +--+-------\ /------+--+ ALSA PCM out | + * \--------------/ | | | | \--------------/ + * | | | | + * | v v | + * | +---------------+ | + * | \ Matrix Mux / | + * | +-----+-----+ | + * | | | + * | | 18chn | + * | v | + * | +-----------+ | + * | | Mixer | | + * | | Matrix | | + * | | | | + * | | 18x6 Gain | | + * | | stages | | + * | +-----+-----+ | + * | | | + * | | | + * | 18chn | 6chn | 6chn + * v v v + * ========================= + * +---------------+ +--—------------+ + * \ Output Mux / \ Capture Mux / + * +-----+-----+ +-----+-----+ + * | | + * | 6chn | + * v | + * +-------------+ | + * | Master Gain | | + * +------+------+ | + * | | + * | 6chn | 18chn + * | (3 stereo pairs) | + * /--------------\ | | /--------------\ + * | Hardware out |<--/ \-->| ALSA PCM in | + * \--------------/ \--------------/ + * + * + */ + +#include +#include +#include + +#include +#include +#include + +#include "usbaudio.h" +#include "mixer.h" +#include "helper.h" +#include "power.h" + +#include "mixer_scarlett.h" + +/* some gui mixers can't handle negative ctl values */ +#define SND_SCARLETT_LEVEL_BIAS 128 +#define SND_SCARLETT_MATRIX_IN_MAX 18 +#define SND_SCARLETT_CONTROLS_MAX 10 +#define SND_SCARLETT_OFFSETS_MAX 5 + +enum { + SCARLETT_OUTPUTS, + SCARLETT_SWITCH_IMPEDANCE, + SCARLETT_SWITCH_PAD, +}; + +enum { + SCARLETT_OFFSET_PCM = 0, + SCARLETT_OFFSET_ANALOG = 1, + SCARLETT_OFFSET_SPDIF = 2, + SCARLETT_OFFSET_ADAT = 3, + SCARLETT_OFFSET_MIX = 4, +}; + +struct scarlett_mixer_elem_enum_info { + int start; + int len; + int offsets[SND_SCARLETT_OFFSETS_MAX]; + char const * const *names; +}; + +struct scarlett_mixer_control { + unsigned char num; + unsigned char type; + const char *name; +}; + +struct scarlett_device_info { + int matrix_in; + int matrix_out; + int input_len; + int output_len; + + struct scarlett_mixer_elem_enum_info opt_master; + struct scarlett_mixer_elem_enum_info opt_matrix; + + /* initial values for matrix mux */ + int matrix_mux_init[SND_SCARLETT_MATRIX_IN_MAX]; + + int num_controls; /* number of items in controls */ + const struct scarlett_mixer_control controls[SND_SCARLETT_CONTROLS_MAX]; +}; + +/********************** Enum Strings *************************/ + +static const struct scarlett_mixer_elem_enum_info opt_pad = { + .start = 0, + .len = 2, + .offsets = {}, + .names = (char const * const []){ + "0dB", "-10dB" + } +}; + +static const struct scarlett_mixer_elem_enum_info opt_impedance = { + .start = 0, + .len = 2, + .offsets = {}, + .names = (char const * const []){ + "Line", "Hi-Z" + } +}; + +static const struct scarlett_mixer_elem_enum_info opt_clock = { + .start = 1, + .len = 3, + .offsets = {}, + .names = (char const * const []){ + "Internal", "SPDIF", "ADAT" + } +}; + +static const struct scarlett_mixer_elem_enum_info opt_sync = { + .start = 0, + .len = 2, + .offsets = {}, + .names = (char const * const []){ + "No Lock", "Locked" + } +}; + +static int scarlett_ctl_switch_info(struct snd_kcontrol *kctl, + struct snd_ctl_elem_info *uinfo) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = elem->channels; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int scarlett_ctl_switch_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + int i, err, val; + + for (i = 0; i < elem->channels; i++) { + err = snd_usb_get_cur_mix_value(elem, i, i, &val); + if (err < 0) + return err; + + val = !val; /* invert mute logic for mixer */ + ucontrol->value.integer.value[i] = val; + } + + return 0; +} + +static int scarlett_ctl_switch_put(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + int i, changed = 0; + int err, oval, val; + + for (i = 0; i < elem->channels; i++) { + err = snd_usb_get_cur_mix_value(elem, i, i, &oval); + if (err < 0) + return err; + + val = ucontrol->value.integer.value[i]; + val = !val; + if (oval != val) { + err = snd_usb_set_cur_mix_value(elem, i, i, val); + if (err < 0) + return err; + + changed = 1; + } + } + + return changed; +} + +static int scarlett_ctl_info(struct snd_kcontrol *kctl, + struct snd_ctl_elem_info *uinfo) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = elem->channels; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = (int)kctl->private_value + + SND_SCARLETT_LEVEL_BIAS; + uinfo->value.integer.step = 1; + return 0; +} + +static int scarlett_ctl_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + int i, err, val; + + for (i = 0; i < elem->channels; i++) { + err = snd_usb_get_cur_mix_value(elem, i, i, &val); + if (err < 0) + return err; + + val = clamp(val / 256, -128, (int)kctl->private_value) + + SND_SCARLETT_LEVEL_BIAS; + ucontrol->value.integer.value[i] = val; + } + + return 0; +} + +static int scarlett_ctl_put(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + int i, changed = 0; + int err, oval, val; + + for (i = 0; i < elem->channels; i++) { + err = snd_usb_get_cur_mix_value(elem, i, i, &oval); + if (err < 0) + return err; + + val = ucontrol->value.integer.value[i] - + SND_SCARLETT_LEVEL_BIAS; + val = val * 256; + if (oval != val) { + err = snd_usb_set_cur_mix_value(elem, i, i, val); + if (err < 0) + return err; + + changed = 1; + } + } + + return changed; +} + +static void scarlett_generate_name(int i, char *dst, int offsets[]) +{ + if (i > offsets[SCARLETT_OFFSET_MIX]) + sprintf(dst, "Mix %c", + 'A'+(i - offsets[SCARLETT_OFFSET_MIX] - 1)); + else if (i > offsets[SCARLETT_OFFSET_ADAT]) + sprintf(dst, "ADAT %d", i - offsets[SCARLETT_OFFSET_ADAT]); + else if (i > offsets[SCARLETT_OFFSET_SPDIF]) + sprintf(dst, "SPDIF %d", i - offsets[SCARLETT_OFFSET_SPDIF]); + else if (i > offsets[SCARLETT_OFFSET_ANALOG]) + sprintf(dst, "Analog %d", i - offsets[SCARLETT_OFFSET_ANALOG]); + else if (i > offsets[SCARLETT_OFFSET_PCM]) + sprintf(dst, "PCM %d", i - offsets[SCARLETT_OFFSET_PCM]); + else + sprintf(dst, "Off"); +} + +static int scarlett_ctl_enum_dynamic_info(struct snd_kcontrol *kctl, + struct snd_ctl_elem_info *uinfo) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct scarlett_mixer_elem_enum_info *opt = elem->private_data; + unsigned int items = opt->len; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = elem->channels; + uinfo->value.enumerated.items = items; + + if (uinfo->value.enumerated.item >= items) + uinfo->value.enumerated.item = items - 1; + + /* generate name dynamically based on item number and offset info */ + scarlett_generate_name(uinfo->value.enumerated.item, + uinfo->value.enumerated.name, + opt->offsets); + + return 0; +} + +static int scarlett_ctl_enum_info(struct snd_kcontrol *kctl, + struct snd_ctl_elem_info *uinfo) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct scarlett_mixer_elem_enum_info *opt = elem->private_data; + + return snd_ctl_enum_info(uinfo, elem->channels, opt->len, + (const char * const *)opt->names); +} + +static int scarlett_ctl_enum_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct scarlett_mixer_elem_enum_info *opt = elem->private_data; + int err, val; + + err = snd_usb_get_cur_mix_value(elem, 0, 0, &val); + if (err < 0) + return err; + + val = clamp(val - opt->start, 0, opt->len-1); + + ucontrol->value.enumerated.item[0] = val; + + return 0; +} + +static int scarlett_ctl_enum_put(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct scarlett_mixer_elem_enum_info *opt = elem->private_data; + int err, oval, val; + + err = snd_usb_get_cur_mix_value(elem, 0, 0, &oval); + if (err < 0) + return err; + + val = ucontrol->value.integer.value[0]; + val = val + opt->start; + if (val != oval) { + snd_usb_set_cur_mix_value(elem, 0, 0, val); + return 1; + } + return 0; +} + +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; + 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 err; + + err = snd_usb_ctl_msg(chip->dev, + usb_rcvctrlpipe(chip->dev, 0), + UAC2_CS_MEM, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | + USB_DIR_IN, wValue, idx, buf, elem->channels); + if (err < 0) + return err; + + ucontrol->value.enumerated.item[0] = clamp((int)buf[0], 0, 1); + return 0; +} + +static struct snd_kcontrol_new usb_scarlett_ctl_switch = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", + .info = scarlett_ctl_switch_info, + .get = scarlett_ctl_switch_get, + .put = scarlett_ctl_switch_put, +}; + +static const DECLARE_TLV_DB_SCALE(db_scale_scarlett_gain, -12800, 100, 0); + +static struct snd_kcontrol_new usb_scarlett_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .name = "", + .info = scarlett_ctl_info, + .get = scarlett_ctl_get, + .put = scarlett_ctl_put, + .private_value = 6, /* max value */ + .tlv = { .p = db_scale_scarlett_gain } +}; + +static struct snd_kcontrol_new usb_scarlett_ctl_master = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .name = "", + .info = scarlett_ctl_info, + .get = scarlett_ctl_get, + .put = scarlett_ctl_put, + .private_value = 6, /* max value */ + .tlv = { .p = db_scale_scarlett_gain } +}; + +static struct snd_kcontrol_new usb_scarlett_ctl_enum = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", + .info = scarlett_ctl_enum_info, + .get = scarlett_ctl_enum_get, + .put = scarlett_ctl_enum_put, +}; + +static struct snd_kcontrol_new usb_scarlett_ctl_dynamic_enum = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", + .info = scarlett_ctl_enum_dynamic_info, + .get = scarlett_ctl_enum_get, + .put = scarlett_ctl_enum_put, +}; + +static struct snd_kcontrol_new usb_scarlett_ctl_sync = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .name = "", + .info = scarlett_ctl_enum_info, + .get = scarlett_ctl_meter_get, +}; + +static int add_new_ctl(struct usb_mixer_interface *mixer, + const struct snd_kcontrol_new *ncontrol, + int index, int offset, int num, + int val_type, int channels, const char *name, + const struct scarlett_mixer_elem_enum_info *opt, + struct usb_mixer_elem_info **elem_ret +) +{ + struct snd_kcontrol *kctl; + struct usb_mixer_elem_info *elem; + int err; + + elem = kzalloc(sizeof(*elem), GFP_KERNEL); + if (!elem) + return -ENOMEM; + + elem->mixer = mixer; + elem->control = offset; + elem->idx_off = num; + elem->id = index; + elem->val_type = val_type; + + elem->channels = channels; + + /* add scarlett_mixer_elem_enum_info struct */ + elem->private_data = (void *)opt; + + kctl = snd_ctl_new1(ncontrol, elem); + if (!kctl) { + kfree(elem); + return -ENOMEM; + } + kctl->private_free = snd_usb_mixer_elem_free; + + strlcpy(kctl->id.name, name, sizeof(kctl->id.name)); + + err = snd_ctl_add(mixer->chip->card, kctl); + if (err < 0) + return err; + + if (elem_ret) + *elem_ret = elem; + + return 0; +} + +static int add_output_ctls(struct usb_mixer_interface *mixer, + int index, const char *name, + const struct scarlett_device_info *info) +{ + int err; + char mx[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + struct usb_mixer_elem_info *elem; + + /* 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, + 2*index+1, USB_MIXER_S16, 2, mx, NULL, &elem); + if (err < 0) + return err; + + /* 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, + 2*index+1, USB_MIXER_S16, 2, mx, NULL, &elem); + if (err < 0) + return err; + + /* 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, + 2*index, USB_MIXER_S16, 1, mx, &info->opt_master, + &elem); + if (err < 0) + return err; + + /* 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, + 2*index+1, USB_MIXER_S16, 1, mx, &info->opt_master, + &elem); + if (err < 0) + return err; + + return 0; +} + +/********************** device-specific config *************************/ + +/* untested... */ +static struct scarlett_device_info s6i6_info = { + .matrix_in = 18, + .matrix_out = 8, + .input_len = 6, + .output_len = 6, + + .opt_master = { + .start = -1, + .len = 27, + .offsets = {0, 12, 16, 18, 18}, + .names = NULL + }, + + .opt_matrix = { + .start = -1, + .len = 19, + .offsets = {0, 12, 16, 18, 18}, + .names = NULL + }, + + .num_controls = 0, + .controls = { + { .num = 0, .type = SCARLETT_OUTPUTS, .name = "Monitor" }, + { .num = 1, .type = SCARLETT_OUTPUTS, .name = "Headphone" }, + { .num = 2, .type = SCARLETT_OUTPUTS, .name = "SPDIF" }, + { .num = 1, .type = SCARLETT_SWITCH_IMPEDANCE, .name = NULL}, + { .num = 1, .type = SCARLETT_SWITCH_PAD, .name = NULL}, + { .num = 2, .type = SCARLETT_SWITCH_IMPEDANCE, .name = NULL}, + { .num = 2, .type = SCARLETT_SWITCH_PAD, .name = NULL}, + { .num = 3, .type = SCARLETT_SWITCH_PAD, .name = NULL}, + { .num = 4, .type = SCARLETT_SWITCH_PAD, .name = NULL}, + }, + + .matrix_mux_init = { + 12, 13, 14, 15, /* Analog -> 1..4 */ + 16, 17, /* SPDIF -> 5,6 */ + 0, 1, 2, 3, 4, 5, 6, 7, /* PCM[1..12] -> 7..18 */ + 8, 9, 10, 11 + } +}; + +/* untested... */ +static struct scarlett_device_info s8i6_info = { + .matrix_in = 18, + .matrix_out = 6, + .input_len = 8, + .output_len = 6, + + .opt_master = { + .start = -1, + .len = 25, + .offsets = {0, 12, 16, 18, 18}, + .names = NULL + }, + + .opt_matrix = { + .start = -1, + .len = 19, + .offsets = {0, 12, 16, 18, 18}, + .names = NULL + }, + + .num_controls = 7, + .controls = { + { .num = 0, .type = SCARLETT_OUTPUTS, .name = "Monitor" }, + { .num = 1, .type = SCARLETT_OUTPUTS, .name = "Headphone" }, + { .num = 2, .type = SCARLETT_OUTPUTS, .name = "SPDIF" }, + { .num = 1, .type = SCARLETT_SWITCH_IMPEDANCE, .name = NULL}, + { .num = 2, .type = SCARLETT_SWITCH_IMPEDANCE, .name = NULL}, + { .num = 3, .type = SCARLETT_SWITCH_PAD, .name = NULL}, + { .num = 4, .type = SCARLETT_SWITCH_PAD, .name = NULL}, + }, + + .matrix_mux_init = { + 12, 13, 14, 15, /* Analog -> 1..4 */ + 16, 17, /* SPDIF -> 5,6 */ + 0, 1, 2, 3, 4, 5, 6, 7, /* PCM[1..12] -> 7..18 */ + 8, 9, 10, 11 + } +}; + +static struct scarlett_device_info s18i6_info = { + .matrix_in = 18, + .matrix_out = 6, + .input_len = 18, + .output_len = 6, + + .opt_master = { + .start = -1, + .len = 31, + .offsets = {0, 6, 14, 16, 24}, + .names = NULL, + }, + + .opt_matrix = { + .start = -1, + .len = 25, + .offsets = {0, 6, 14, 16, 24}, + .names = NULL, + }, + + .num_controls = 5, + .controls = { + { .num = 0, .type = SCARLETT_OUTPUTS, .name = "Monitor" }, + { .num = 1, .type = SCARLETT_OUTPUTS, .name = "Headphone" }, + { .num = 2, .type = SCARLETT_OUTPUTS, .name = "SPDIF" }, + { .num = 1, .type = SCARLETT_SWITCH_IMPEDANCE, .name = NULL}, + { .num = 2, .type = SCARLETT_SWITCH_IMPEDANCE, .name = NULL}, + }, + + .matrix_mux_init = { + 6, 7, 8, 9, 10, 11, 12, 13, /* Analog -> 1..8 */ + 16, 17, 18, 19, 20, 21, /* ADAT[1..6] -> 9..14 */ + 14, 15, /* SPDIF -> 15,16 */ + 0, 1 /* PCM[1,2] -> 17,18 */ + } +}; + +static struct scarlett_device_info s18i8_info = { + .matrix_in = 18, + .matrix_out = 8, + .input_len = 18, + .output_len = 8, + + .opt_master = { + .start = -1, + .len = 35, + .offsets = {0, 8, 16, 18, 26}, + .names = NULL + }, + + .opt_matrix = { + .start = -1, + .len = 27, + .offsets = {0, 8, 16, 18, 26}, + .names = NULL + }, + + .num_controls = 10, + .controls = { + { .num = 0, .type = SCARLETT_OUTPUTS, .name = "Monitor" }, + { .num = 1, .type = SCARLETT_OUTPUTS, .name = "Headphone 1" }, + { .num = 2, .type = SCARLETT_OUTPUTS, .name = "Headphone 2" }, + { .num = 3, .type = SCARLETT_OUTPUTS, .name = "SPDIF" }, + { .num = 1, .type = SCARLETT_SWITCH_IMPEDANCE, .name = NULL}, + { .num = 1, .type = SCARLETT_SWITCH_PAD, .name = NULL}, + { .num = 2, .type = SCARLETT_SWITCH_IMPEDANCE, .name = NULL}, + { .num = 2, .type = SCARLETT_SWITCH_PAD, .name = NULL}, + { .num = 3, .type = SCARLETT_SWITCH_PAD, .name = NULL}, + { .num = 4, .type = SCARLETT_SWITCH_PAD, .name = NULL}, + }, + + .matrix_mux_init = { + 8, 9, 10, 11, 12, 13, 14, 15, /* Analog -> 1..8 */ + 18, 19, 20, 21, 22, 23, /* ADAT[1..6] -> 9..14 */ + 16, 17, /* SPDIF -> 15,16 */ + 0, 1 /* PCM[1,2] -> 17,18 */ + } +}; + +static struct scarlett_device_info s18i20_info = { + .matrix_in = 18, + .matrix_out = 8, + .input_len = 18, + .output_len = 20, + + .opt_master = { + .start = -1, + .len = 47, + .offsets = {0, 20, 28, 30, 38}, + .names = NULL + }, + + .opt_matrix = { + .start = -1, + .len = 39, + .offsets = {0, 20, 28, 30, 38}, + .names = NULL + }, + + .num_controls = 10, + .controls = { + { .num = 0, .type = SCARLETT_OUTPUTS, .name = "Monitor" }, + { .num = 1, .type = SCARLETT_OUTPUTS, .name = "Line 3/4" }, + { .num = 2, .type = SCARLETT_OUTPUTS, .name = "Line 5/6" }, + { .num = 3, .type = SCARLETT_OUTPUTS, .name = "Line 7/8" }, + { .num = 4, .type = SCARLETT_OUTPUTS, .name = "Line 9/10" }, + { .num = 5, .type = SCARLETT_OUTPUTS, .name = "SPDIF" }, + { .num = 6, .type = SCARLETT_OUTPUTS, .name = "ADAT 1/2" }, + { .num = 7, .type = SCARLETT_OUTPUTS, .name = "ADAT 3/4" }, + { .num = 8, .type = SCARLETT_OUTPUTS, .name = "ADAT 5/6" }, + { .num = 9, .type = SCARLETT_OUTPUTS, .name = "ADAT 7/8" }, + /*{ .num = 1, .type = SCARLETT_SWITCH_IMPEDANCE, .name = NULL}, + { .num = 1, .type = SCARLETT_SWITCH_PAD, .name = NULL}, + { .num = 2, .type = SCARLETT_SWITCH_IMPEDANCE, .name = NULL}, + { .num = 2, .type = SCARLETT_SWITCH_PAD, .name = NULL}, + { .num = 3, .type = SCARLETT_SWITCH_PAD, .name = NULL}, + { .num = 4, .type = SCARLETT_SWITCH_PAD, .name = NULL},*/ + }, + + .matrix_mux_init = { + 20, 21, 22, 23, 24, 25, 26, 27, /* Analog -> 1..8 */ + 30, 31, 32, 33, 34, 35, /* ADAT[1..6] -> 9..14 */ + 28, 29, /* SPDIF -> 15,16 */ + 0, 1 /* PCM[1,2] -> 17,18 */ + } +}; + + +static int scarlett_controls_create_generic(struct usb_mixer_interface *mixer, + struct scarlett_device_info *info) +{ + int i, err; + char mx[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + const struct scarlett_mixer_control *ctl; + struct usb_mixer_elem_info *elem; + + /* create master switch and playback volume */ + err = add_new_ctl(mixer, &usb_scarlett_ctl_switch, 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, + USB_MIXER_S16, 1, "Master Playback Volume", NULL, + &elem); + if (err < 0) + return err; + + /* iterate through controls in info struct and create each one */ + for (i = 0; i < info->num_controls; i++) { + ctl = &info->controls[i]; + + switch (ctl->type) { + case SCARLETT_OUTPUTS: + err = add_output_ctls(mixer, ctl->num, ctl->name, info); + if (err < 0) + return err; + break; + case SCARLETT_SWITCH_IMPEDANCE: + sprintf(mx, "Input %d Impedance Switch", ctl->num); + err = add_new_ctl(mixer, &usb_scarlett_ctl_enum, 0x01, + 0x09, ctl->num, USB_MIXER_S16, 1, mx, + &opt_impedance, &elem); + if (err < 0) + return err; + break; + case SCARLETT_SWITCH_PAD: + sprintf(mx, "Input %d Pad Switch", ctl->num); + err = add_new_ctl(mixer, &usb_scarlett_ctl_enum, 0x01, + 0x0b, ctl->num, USB_MIXER_S16, 1, mx, + &opt_pad, &elem); + if (err < 0) + return err; + break; + } + } + + return 0; +} + +/* + * Create and initialize a mixer for the Focusrite(R) Scarlett + */ +int snd_scarlett_controls_create(struct usb_mixer_interface *mixer) +{ + int err, i, o; + char mx[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + struct scarlett_device_info *info; + struct usb_mixer_elem_info *elem; + static char sample_rate_buffer[4] = { '\x80', '\xbb', '\x00', '\x00' }; + + /* only use UAC_VERSION_2 */ + if (!mixer->protocol) + return 0; + + switch (mixer->chip->usb_id) { + case USB_ID(0x1235, 0x8012): + info = &s6i6_info; + break; + case USB_ID(0x1235, 0x8002): + info = &s8i6_info; + break; + case USB_ID(0x1235, 0x8004): + info = &s18i6_info; + break; + case USB_ID(0x1235, 0x8014): + info = &s18i8_info; + break; + case USB_ID(0x1235, 0x800c): + info = &s18i20_info; + break; + default: /* device not (yet) supported */ + return -EINVAL; + } + + /* generic function to create controls */ + err = scarlett_controls_create_generic(mixer, info); + if (err < 0) + return err; + + /* setup matrix controls */ + 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, + 0x06, i, USB_MIXER_S16, 1, mx, + &info->opt_matrix, &elem); + if (err < 0) + return err; + + 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, + (i << 3) + (o & 0x07), USB_MIXER_S16, + 1, mx, NULL, &elem); + if (err < 0) + return err; + + } + } + + 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, + 0x00, i, USB_MIXER_S16, 1, mx, + &info->opt_master, &elem); + if (err < 0) + return err; + } + + /* val_len == 1 needed here */ + err = add_new_ctl(mixer, &usb_scarlett_ctl_enum, 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, + USB_MIXER_U8, 1, "Sample Clock Sync Status", + &opt_sync, &elem); + if (err < 0) + return err; + + /* initialize sampling rate to 48000 */ + err = snd_usb_ctl_msg(mixer->chip->dev, + usb_sndctrlpipe(mixer->chip->dev, 0), UAC2_CS_CUR, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | + USB_DIR_OUT, 0x0100, snd_usb_ctrl_intf(mixer->chip) | + (0x29 << 8), sample_rate_buffer, 4); + if (err < 0) + return err; + + return err; +} diff --git a/sound/usb/mixer_scarlett.h b/sound/usb/mixer_scarlett.h new file mode 100644 index 0000000..19c592a --- /dev/null +++ b/sound/usb/mixer_scarlett.h @@ -0,0 +1,6 @@ +#ifndef __USB_MIXER_SCARLETT_H +#define __USB_MIXER_SCARLETT_H + +int snd_scarlett_controls_create(struct usb_mixer_interface *mixer); + +#endif /* __USB_MIXER_SCARLETT_H */ -- cgit v1.1 From 9547c0999e50fd624cab52f94a79f0fd27a7cb84 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 28 Oct 2014 14:22:49 -0700 Subject: ALSA: 6fire: Convert byte_rev_table uses to bitrev8 Use the inline function instead of directly indexing the array. This allows some architectures with hardware instructions for bit reversals to eliminate the array. Signed-off-by: Joe Perches Signed-off-by: Takashi Iwai --- sound/usb/6fire/firmware.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/usb') diff --git a/sound/usb/6fire/firmware.c b/sound/usb/6fire/firmware.c index 3b02e54..62c25e7 100644 --- a/sound/usb/6fire/firmware.c +++ b/sound/usb/6fire/firmware.c @@ -316,7 +316,7 @@ static int usb6fire_fw_fpga_upload( while (c != end) { for (i = 0; c != end && i < FPGA_BUFSIZE; i++, c++) - buffer[i] = byte_rev_table[(u8) *c]; + buffer[i] = bitrev8((u8)*c); ret = usb6fire_fw_fpga_write(device, buffer, i); if (ret < 0) { -- cgit v1.1 From 5aeee3424fa5926a53885b9defb593971e446d77 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Nov 2014 11:02:14 +0100 Subject: ALSA: usb-audio: Refactor ignore_ctl_error checks 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 --- sound/usb/mixer.c | 39 ++++++++++++++++----------------------- 1 file changed, 16 insertions(+), 23 deletions(-) (limited to 'sound/usb') diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index c6209ce..a4fbfff 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) { -- cgit v1.1 From 3360b84b8ed1f08bfb39743465b858a04492fcc3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Nov 2014 11:47:04 +0100 Subject: ALSA: usb-audio: Allow quirks to handle own resume and proc dump 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 --- 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(-) (limited to 'sound/usb') diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index a4fbfff..41650d5 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; @@ -2088,11 +2084,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); } /* @@ -2237,25 +2230,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", @@ -2267,7 +2256,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) { @@ -2277,9 +2266,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); + } } } } @@ -2287,7 +2284,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; @@ -2299,7 +2296,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; @@ -2312,7 +2315,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: @@ -2510,8 +2513,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) { @@ -2541,19 +2545,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; + } } } } @@ -2561,3 +2565,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 2478a84..0fe87b7 100644 --- a/sound/usb/mixer.h +++ b/sound/usb/mixer.h @@ -1,6 +1,8 @@ #ifndef __USBMIXER_H #define __USBMIXER_H +#include + 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_t)(struct snd_info_buffer *buffer, + struct usb_mixer_elem_list *list); +typedef int (*usb_mixer_elem_resume_func_t)(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_t dump; + usb_mixer_elem_resume_func_t 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 8b55c06..88a408c 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 a0a8745..92dba35 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; -- cgit v1.1 From 9cf3689bfe0784b6f6afb301bece95d3fc3eeb64 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Nov 2014 12:58:51 +0100 Subject: ALSA: usb-audio: Add audigy2nx resume support 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 --- sound/usb/mixer.h | 1 - sound/usb/mixer_quirks.c | 148 +++++++++++++++++++++++++++++------------------ 2 files changed, 92 insertions(+), 57 deletions(-) (limited to 'sound/usb') diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h index 0fe87b7..0b3740e 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 88a408c..41cacf8 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_t 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; } -- cgit v1.1 From 5f503ee9e270f8251ba9024bafa8d698050066cb Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Nov 2014 16:11:37 +0100 Subject: ALSA: usb-audio: Add Emu0204 channel switch resume support Similar as the previous fix, this adds the proper resume support to Emu0202 "Front Jack Channels" enum mixer element. Signed-off-by: Takashi Iwai --- sound/usb/mixer_quirks.c | 84 ++++++++++++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 38 deletions(-) (limited to 'sound/usb') diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 41cacf8..f2b1c0d 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, -- cgit v1.1 From 2bfb14c3b8fbc787ff4478f9d77ecee78cb922fe Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Nov 2014 16:18:15 +0100 Subject: ALSA: usb-audio: Add Xonar U1 resume support 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 --- sound/usb/mixer.h | 2 -- sound/usb/mixer_quirks.c | 66 ++++++++++++++++++++++++++++-------------------- 2 files changed, 38 insertions(+), 30 deletions(-) (limited to 'sound/usb') diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h index 0b3740e..d3268f0 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 f2b1c0d..4afcf09 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) */ -- cgit v1.1 From 25a9a4f91b909822fa07cbc9939c99a8c67d8960 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Nov 2014 16:31:35 +0100 Subject: ALSA: usb-audio: Add Digidesign Mbox 1 resume support Again another quirk fix, just convert to usb_mixer_elem_list with the resume callback for Mbox 1 stuff. Signed-off-by: Takashi Iwai --- sound/usb/mixer_quirks.c | 52 +++++++++++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 23 deletions(-) (limited to 'sound/usb') diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 4afcf09..f7ad207 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 */ -- cgit v1.1 From da6d276957ea56b9514aa5c8d885edf22f0b3e65 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Nov 2014 16:59:47 +0100 Subject: ALSA: usb-audio: Add resume support for Native Instruments controls 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 --- sound/usb/mixer_quirks.c | 99 +++++++++++++++++++++++++----------------------- 1 file changed, 52 insertions(+), 47 deletions(-) (limited to 'sound/usb') diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index f7ad207..e11b4f3 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 = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), bRequest, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, - 0, wIndex, - &tmp, sizeof(tmp)); - up_read(&mixer->chip->shutdown_rwsem); + unsigned int pval = kctl->private_value; + u8 value; + int err; - if (ret < 0) { + err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), + (pval >> 16) & 0xff, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0, pval & 0xffff, &value, 1); + 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; -- cgit v1.1 From 0b4e9cfcef055a1be9bee5a47262e9cbcf17e8cd Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Nov 2014 17:37:40 +0100 Subject: 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: Takashi Iwai --- sound/usb/mixer_quirks.c | 194 +++++++++++++---------------------------------- 1 file changed, 54 insertions(+), 140 deletions(-) (limited to 'sound/usb') diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index e11b4f3..fcb7ae5 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; } -- cgit v1.1 From 288673beae6cbd8198be94284adbaeb5cba7dbda Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Nov 2014 18:06:17 +0100 Subject: 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 --- sound/usb/mixer_quirks.c | 135 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 93 insertions(+), 42 deletions(-) (limited to 'sound/usb') diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index fcb7ae5..dc9df00 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_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) { - 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; } -- cgit v1.1 From b61f90eac1ff9d1b30497e611aba4651d4066706 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 20 Nov 2014 17:20:46 +0100 Subject: ALSA: usb-audio: Add resume support for Scarlett mixers 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 --- sound/usb/mixer_scarlett.c | 65 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 51 insertions(+), 14 deletions(-) (limited to 'sound/usb') diff --git a/sound/usb/mixer_scarlett.c b/sound/usb/mixer_scarlett.c index 92dba35..9109652 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_t 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) -- cgit v1.1 From 7a2e9ddc903225d8fb3a510a842144a239017ee4 Mon Sep 17 00:00:00 2001 From: Jurgen Kramer Date: Fri, 28 Nov 2014 17:32:53 +0100 Subject: ALSA: usb-audio: Add native DSD support for Denon/Marantz DACs This patch adds native DSD support for the following devices: - Marantz SA-14S1 - Marants HD-DAC1 Signed-off-by: Jurgen Kramer Signed-off-by: Takashi Iwai --- sound/usb/quirks.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'sound/usb') diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 2c1018e..a9d4add 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -1213,5 +1213,16 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip, break; } + /* Denon/Marantz devices with USB DAC functionality */ + switch (chip->usb_id) { + case USB_ID(0x154e, 0x3005): /* Marantz HD-DAC1 */ + case USB_ID(0x154e, 0x3006): /* Marantz SA-14S1 */ + if (fp->altsetting == 2) + return SNDRV_PCM_FMTBIT_DSD_U32_BE; + break; + default: + break; + } + return 0; } -- cgit v1.1 From 6874daad4b0fbed5b2f9bef7f4d3f2b895463a95 Mon Sep 17 00:00:00 2001 From: Jurgen Kramer Date: Fri, 28 Nov 2014 17:32:54 +0100 Subject: ALSA: usb-audio: Add mode select quirk for Denon/Marantz DACs Denon/Marantz USB DACs need a specific vendor command to switch between PCM and DSD mode. This patch adds a new quirk function to switch between the two modes using the specific USB vendor command. This patch applies to the following devices: - Marantz SA-14S1 - Marantz HD-DAC1 Signed-off-by: Jurgen Kramer Signed-off-by: Takashi Iwai --- sound/usb/pcm.c | 5 +++++ sound/usb/quirks.c | 38 ++++++++++++++++++++++++++++++++++++++ sound/usb/quirks.h | 3 +++ 3 files changed, 46 insertions(+) (limited to 'sound/usb') diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index c62a165..0d8aba5 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -482,6 +482,11 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) /* set interface */ if (subs->interface != fmt->iface || subs->altset_idx != fmt->altset_idx) { + + err = snd_usb_select_mode_quirk(subs, fmt); + if (err < 0) + return -EIO; + err = usb_set_interface(dev, fmt->iface, fmt->altsetting); if (err < 0) { dev_err(&dev->dev, diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index a9d4add..e0cde74 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -1111,6 +1111,44 @@ void snd_usb_set_format_quirk(struct snd_usb_substream *subs, } } + +/* Marantz/Denon USB DACs need a vendor cmd to switch + * between PCM and native DSD mode + */ +int snd_usb_select_mode_quirk(struct snd_usb_substream *subs, + struct audioformat *fmt) +{ + struct usb_device *dev = subs->dev; + int err; + + switch (subs->stream->chip->usb_id) { + case USB_ID(0x154e, 0x3005): /* Marantz HD-DAC1 */ + case USB_ID(0x154e, 0x3006): /* Marantz SA-14S1 */ + + /* First switch to alt set 0, otherwise the mode switch cmd + * will not be accepted by the DAC + */ + err = usb_set_interface(dev, fmt->iface, 0); + if (err < 0) + return err; + + mdelay(20); /* Delay needed after setting the interface */ + + switch (fmt->altsetting) { + case 2: /* DSD mode requested */ + case 1: /* PCM mode requested */ + err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), 0, + USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE, + fmt->altsetting - 1, 1, NULL, 0); + if (err < 0) + return err; + break; + } + mdelay(20); + } + return 0; +} + void snd_usb_endpoint_start_quirk(struct snd_usb_endpoint *ep) { /* diff --git a/sound/usb/quirks.h b/sound/usb/quirks.h index 665e972..1b86238 100644 --- a/sound/usb/quirks.h +++ b/sound/usb/quirks.h @@ -31,6 +31,9 @@ void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, __u16 value, __u16 index, void *data, __u16 size); +int snd_usb_select_mode_quirk(struct snd_usb_substream *subs, + struct audioformat *fmt); + u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip, struct audioformat *fp, unsigned int sample_bytes); -- cgit v1.1 From dacacb0aa0cb6fdeb69313db6acfc82456945d7e Mon Sep 17 00:00:00 2001 From: Panu Matilainen Date: Sun, 30 Nov 2014 18:45:40 +0200 Subject: ALSA: usb-audio: Add support for Zoom R16/24 capture and midi interfaces This makes the midi interface and capture work out of the box with R16 (and presumably R24 too but untested). Playback stream would also seem to function fine except for one caveat: no sound is produced, so it is disabled for now. Mixer descriptors are garbage and will require further quirks to enable functionality, also disabled here. Signed-off-by: Panu Matilainen Signed-off-by: Takashi Iwai --- sound/usb/quirks-table.h | 40 ++++++++++++++++++++++++++++++++++++++++ sound/usb/quirks.c | 8 ++++++++ 2 files changed, 48 insertions(+) (limited to 'sound/usb') diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 013cba8..73d2ba4 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -3175,6 +3175,46 @@ YAMAHA_DEVICE(0x7010, "UB99"), { /* + * ZOOM R16/24 in audio interface mode. + * Mixer descriptors are garbage, further quirks will be needed + * to make any of it functional, thus disabled for now. + * Playback stream appears to start and run fine but no sound + * is produced, so also disabled for now. + */ + USB_DEVICE(0x1686, 0x00dd), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const struct snd_usb_audio_quirk[]) { + { + /* Mixer */ + .ifnum = 0, + .type = QUIRK_IGNORE_INTERFACE, + }, + { + /* Playback */ + .ifnum = 1, + .type = QUIRK_IGNORE_INTERFACE, + }, + { + /* Capture */ + .ifnum = 2, + .type = QUIRK_AUDIO_STANDARD_INTERFACE, + }, + { + /* Midi */ + .ifnum = 3, + .type = QUIRK_MIDI_STANDARD_INTERFACE + }, + { + .ifnum = -1 + }, + } + } +}, + +{ + /* * Some USB MIDI devices don't have an audio control interface, * so we have to grab MIDI streaming interfaces here. */ diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index e45cc3a..4dbfb3d 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -1207,6 +1207,14 @@ void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe, break; } } + + /* Zoom R16/24 needs a tiny delay here, otherwise requests like + * get/set frequency return as failed despite actually succeeding. + */ + if ((le16_to_cpu(dev->descriptor.idVendor) == 0x1686) && + (le16_to_cpu(dev->descriptor.idProduct) == 0x00dd) && + (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS) + mdelay(1); } /* -- cgit v1.1 From 66139a48cee1530c91f37c145384b4ee7043f0b7 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sat, 6 Dec 2014 18:02:55 +0100 Subject: ALSA: usb-audio: Don't resubmit pending URBs at MIDI error recovery In snd_usbmidi_error_timer(), the driver tries to resubmit MIDI input URBs to reactivate the MIDI stream, but this causes the error when some of URBs are still pending like: WARNING: CPU: 0 PID: 0 at ../drivers/usb/core/urb.c:339 usb_submit_urb+0x5f/0x70() URB ef705c40 submitted while active CPU: 0 PID: 0 Comm: swapper/0 Not tainted 3.16.6-2-desktop #1 Hardware name: FOXCONN TPS01/TPS01, BIOS 080015 03/23/2010 c0984bfa f4009ed4 c078deaf f4009ee4 c024c884 c09a135c f4009f00 00000000 c0984bfa 00000153 c061ac4f c061ac4f 00000009 00000001 ef705c40 e854d1c0 f4009eec c024c8d3 00000009 f4009ee4 c09a135c f4009f00 f4009f04 c061ac4f Call Trace: [] try_stack_unwind+0x156/0x170 [] dump_trace+0x5a/0x1b0 [] show_trace_log_lvl+0x46/0x50 [] show_stack_log_lvl+0x51/0xe0 [] show_stack+0x27/0x50 [] dump_stack+0x45/0x65 [] warn_slowpath_common+0x84/0xa0 [] warn_slowpath_fmt+0x33/0x40 [] usb_submit_urb+0x5f/0x70 [] snd_usbmidi_submit_urb+0x14/0x60 [snd_usbmidi_lib] [] snd_usbmidi_error_timer+0x6a/0xa0 [snd_usbmidi_lib] [] call_timer_fn+0x30/0x130 [] run_timer_softirq+0x1c2/0x260 [] __do_softirq+0xc3/0x270 [] do_softirq_own_stack+0x22/0x30 [] irq_exit+0x8d/0xa0 [] smp_apic_timer_interrupt+0x38/0x50 [] apic_timer_interrupt+0x34/0x3c [] cpuidle_enter_state+0x3e/0xd0 [] cpu_idle_loop+0x29d/0x3e0 [] cpu_startup_entry+0x53/0x60 [] start_kernel+0x415/0x41a For avoiding these errors, check the pending URBs and skip resubmitting such ones. Reported-and-tested-by: Stefan Seyfried Acked-by: Clemens Ladisch Cc: Signed-off-by: Takashi Iwai --- sound/usb/midi.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound/usb') diff --git a/sound/usb/midi.c b/sound/usb/midi.c index d3d4952..5bfb695 100644 --- a/sound/usb/midi.c +++ b/sound/usb/midi.c @@ -365,6 +365,8 @@ static void snd_usbmidi_error_timer(unsigned long data) if (in && in->error_resubmit) { in->error_resubmit = 0; for (j = 0; j < INPUT_URBS; ++j) { + if (atomic_read(&in->urbs[j]->use_count)) + continue; in->urbs[j]->dev = umidi->dev; snd_usbmidi_submit_urb(in->urbs[j], GFP_ATOMIC); } -- cgit v1.1