ALSA: usb-audio: export UAC2 clock selectors as mixer controls
[safe/jmp/linux-2.6] / sound / usb / mixer.c
index cb34536..a060d00 100644 (file)
@@ -313,8 +313,8 @@ static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request, int v
                              buf, sizeof(buf), 1000);
 
        if (ret < 0) {
-               snd_printdd(KERN_ERR "cannot get ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n",
-                           request, validx, cval->mixer->ctrlif | (cval->id << 8), cval->val_type);
+               snd_printk(KERN_ERR "cannot get ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n",
+                          request, validx, cval->mixer->ctrlif | (cval->id << 8), cval->val_type);
                return ret;
        }
 
@@ -610,6 +610,7 @@ static int get_term_name(struct mixer_build *state, struct usb_audio_term *iterm
  */
 static int check_input_term(struct mixer_build *state, int id, struct usb_audio_term *term)
 {
+       int err;
        void *p1;
 
        memset(term, 0, sizeof(*term));
@@ -630,6 +631,11 @@ static int check_input_term(struct mixer_build *state, int id, struct usb_audio_
                                term->channels = d->bNrChannels;
                                term->chconfig = le32_to_cpu(d->bmChannelConfig);
                                term->name = d->iTerminal;
+
+                               /* call recursively to get the clock selectors */
+                               err = check_input_term(state, d->bCSourceID, term);
+                               if (err < 0)
+                                       return err;
                        }
                        return 0;
                case UAC_FEATURE_UNIT: {
@@ -646,7 +652,8 @@ static int check_input_term(struct mixer_build *state, int id, struct usb_audio_
                        term->name = uac_mixer_unit_iMixer(d);
                        return 0;
                }
-               case UAC_SELECTOR_UNIT: {
+               case UAC_SELECTOR_UNIT:
+               case UAC2_CLOCK_SELECTOR: {
                        struct uac_selector_unit_descriptor *d = p1;
                        /* call recursively to retrieve the channel info */
                        if (check_input_term(state, d->baSourceID[0], term) < 0)
@@ -669,6 +676,13 @@ static int check_input_term(struct mixer_build *state, int id, struct usb_audio_
                        term->name = uac_processing_unit_iProcessing(d, state->mixer->protocol);
                        return 0;
                }
+               case UAC2_CLOCK_SOURCE: {
+                       struct uac_clock_source_descriptor *d = p1;
+                       term->type = d->bDescriptorSubtype << 16; /* virtual type */
+                       term->id = id;
+                       term->name = d->iClockSource;
+                       return 0;
+               }
                default:
                        return -ENODEV;
                }
@@ -1610,7 +1624,7 @@ static int mixer_ctl_selector_get(struct snd_kcontrol *kcontrol, struct snd_ctl_
        struct usb_mixer_elem_info *cval = kcontrol->private_data;
        int val, err;
 
-       err = get_cur_ctl_value(cval, 0, &val);
+       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;
@@ -1629,7 +1643,7 @@ static int mixer_ctl_selector_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
        struct usb_mixer_elem_info *cval = kcontrol->private_data;
        int val, oval, err;
 
-       err = get_cur_ctl_value(cval, 0, &oval);
+       err = get_cur_ctl_value(cval, cval->control << 8, &oval);
        if (err < 0) {
                if (cval->mixer->ignore_ctl_error)
                        return 0;
@@ -1638,7 +1652,7 @@ static int mixer_ctl_selector_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
        val = ucontrol->value.enumerated.item[0];
        val = get_abs_value(cval, val);
        if (val != oval) {
-               set_cur_ctl_value(cval, 0, val);
+               set_cur_ctl_value(cval, cval->control << 8, val);
                return 1;
        }
        return 0;
@@ -1720,6 +1734,11 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, void
        cval->res = 1;
        cval->initialized = 1;
 
+       if (desc->bDescriptorSubtype == UAC2_CLOCK_SELECTOR)
+               cval->control = UAC2_CX_CLOCK_SELECTOR;
+       else
+               cval->control = 0;
+
        namelist = kmalloc(sizeof(char *) * desc->bNrInPins, GFP_KERNEL);
        if (! namelist) {
                snd_printk(KERN_ERR "cannot malloc\n");
@@ -1769,7 +1788,9 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, void
                if (! len)
                        strlcpy(kctl->id.name, "USB", sizeof(kctl->id.name));
 
-               if ((state->oterm.type & 0xff00) == 0x0100)
+               if (desc->bDescriptorSubtype == UAC2_CLOCK_SELECTOR)
+                       append_ctl_name(kctl, " Clock Source");
+               else if ((state->oterm.type & 0xff00) == 0x0100)
                        append_ctl_name(kctl, " Capture Source");
                else
                        append_ctl_name(kctl, " Playback Source");
@@ -1803,10 +1824,12 @@ static int parse_audio_unit(struct mixer_build *state, int unitid)
 
        switch (p1[2]) {
        case UAC_INPUT_TERMINAL:
+       case UAC2_CLOCK_SOURCE:
                return 0; /* NOP */
        case UAC_MIXER_UNIT:
                return parse_audio_mixer_unit(state, unitid, p1);
        case UAC_SELECTOR_UNIT:
+       case UAC2_CLOCK_SELECTOR:
                return parse_audio_selector_unit(state, unitid, p1);
        case UAC_FEATURE_UNIT:
                return parse_audio_feature_unit(state, unitid, p1);
@@ -1903,6 +1926,11 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
                        err = parse_audio_unit(&state, desc->bSourceID);
                        if (err < 0)
                                return err;
+
+                       /* for UAC2, use the same approach to also add the clock selectors */
+                       err = parse_audio_unit(&state, desc->bCSourceID);
+                       if (err < 0)
+                               return err;
                }
        }