|
@@ -78,39 +78,6 @@ enum {
|
|
|
USB_MIXER_U16,
|
|
|
};
|
|
|
|
|
|
-enum {
|
|
|
- USB_PROC_UPDOWN = 1,
|
|
|
- USB_PROC_UPDOWN_SWITCH = 1,
|
|
|
- USB_PROC_UPDOWN_MODE_SEL = 2,
|
|
|
-
|
|
|
- USB_PROC_PROLOGIC = 2,
|
|
|
- USB_PROC_PROLOGIC_SWITCH = 1,
|
|
|
- USB_PROC_PROLOGIC_MODE_SEL = 2,
|
|
|
-
|
|
|
- USB_PROC_3DENH = 3,
|
|
|
- USB_PROC_3DENH_SWITCH = 1,
|
|
|
- USB_PROC_3DENH_SPACE = 2,
|
|
|
-
|
|
|
- USB_PROC_REVERB = 4,
|
|
|
- USB_PROC_REVERB_SWITCH = 1,
|
|
|
- USB_PROC_REVERB_LEVEL = 2,
|
|
|
- USB_PROC_REVERB_TIME = 3,
|
|
|
- USB_PROC_REVERB_DELAY = 4,
|
|
|
-
|
|
|
- USB_PROC_CHORUS = 5,
|
|
|
- USB_PROC_CHORUS_SWITCH = 1,
|
|
|
- USB_PROC_CHORUS_LEVEL = 2,
|
|
|
- USB_PROC_CHORUS_RATE = 3,
|
|
|
- USB_PROC_CHORUS_DEPTH = 4,
|
|
|
-
|
|
|
- USB_PROC_DCR = 6,
|
|
|
- USB_PROC_DCR_SWITCH = 1,
|
|
|
- USB_PROC_DCR_RATIO = 2,
|
|
|
- USB_PROC_DCR_MAX_AMP = 3,
|
|
|
- USB_PROC_DCR_THRESHOLD = 4,
|
|
|
- USB_PROC_DCR_ATTACK = 5,
|
|
|
- USB_PROC_DCR_RELEASE = 6,
|
|
|
-};
|
|
|
|
|
|
/*E-mu 0202(0404) eXtension Unit(XU) control*/
|
|
|
enum {
|
|
@@ -198,22 +165,24 @@ static int check_mapped_selector_name(struct mixer_build *state, int unitid,
|
|
|
|
|
|
/*
|
|
|
* find an audio control unit with the given unit id
|
|
|
- * this doesn't return any clock related units, so they need to be handled elsewhere
|
|
|
*/
|
|
|
static void *find_audio_control_unit(struct mixer_build *state, unsigned char unit)
|
|
|
{
|
|
|
- unsigned char *p;
|
|
|
+ /* we just parse the header */
|
|
|
+ struct uac_feature_unit_descriptor *hdr = NULL;
|
|
|
|
|
|
- p = NULL;
|
|
|
- while ((p = snd_usb_find_desc(state->buffer, state->buflen, p,
|
|
|
- USB_DT_CS_INTERFACE)) != NULL) {
|
|
|
- if (p[0] >= 4 && p[2] >= UAC_INPUT_TERMINAL && p[2] <= UAC2_EXTENSION_UNIT_V2 && p[3] == unit)
|
|
|
- return p;
|
|
|
+ while ((hdr = snd_usb_find_desc(state->buffer, state->buflen, hdr,
|
|
|
+ USB_DT_CS_INTERFACE)) != NULL) {
|
|
|
+ if (hdr->bLength >= 4 &&
|
|
|
+ hdr->bDescriptorSubtype >= UAC_INPUT_TERMINAL &&
|
|
|
+ hdr->bDescriptorSubtype <= UAC2_SAMPLE_RATE_CONVERTER &&
|
|
|
+ hdr->bUnitID == unit)
|
|
|
+ return hdr;
|
|
|
}
|
|
|
+
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
/*
|
|
|
* copy a string with the given id
|
|
|
*/
|
|
@@ -344,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;
|
|
|
}
|
|
|
|
|
@@ -462,6 +431,16 @@ static int set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel,
|
|
|
int index, int value)
|
|
|
{
|
|
|
int err;
|
|
|
+ unsigned int read_only = (channel == 0) ?
|
|
|
+ cval->master_readonly :
|
|
|
+ cval->ch_readonly & (1 << (channel - 1));
|
|
|
+
|
|
|
+ if (read_only) {
|
|
|
+ snd_printdd(KERN_INFO "%s(): channel %d of control %d is read_only\n",
|
|
|
+ __func__, channel, cval->control);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
err = snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR, (cval->control << 8) | channel,
|
|
|
value);
|
|
|
if (err < 0)
|
|
@@ -631,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));
|
|
@@ -651,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: {
|
|
@@ -667,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)
|
|
@@ -690,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;
|
|
|
}
|
|
@@ -709,16 +702,20 @@ struct usb_feature_control_info {
|
|
|
};
|
|
|
|
|
|
static struct usb_feature_control_info audio_feature_info[] = {
|
|
|
- { "Mute", USB_MIXER_INV_BOOLEAN },
|
|
|
- { "Volume", USB_MIXER_S16 },
|
|
|
+ { "Mute", USB_MIXER_INV_BOOLEAN },
|
|
|
+ { "Volume", USB_MIXER_S16 },
|
|
|
{ "Tone Control - Bass", USB_MIXER_S8 },
|
|
|
{ "Tone Control - Mid", USB_MIXER_S8 },
|
|
|
{ "Tone Control - Treble", USB_MIXER_S8 },
|
|
|
{ "Graphic Equalizer", USB_MIXER_S8 }, /* FIXME: not implemeted yet */
|
|
|
- { "Auto Gain Control", USB_MIXER_BOOLEAN },
|
|
|
- { "Delay Control", USB_MIXER_U16 },
|
|
|
- { "Bass Boost", USB_MIXER_BOOLEAN },
|
|
|
- { "Loudness", USB_MIXER_BOOLEAN },
|
|
|
+ { "Auto Gain Control", USB_MIXER_BOOLEAN },
|
|
|
+ { "Delay Control", USB_MIXER_U16 },
|
|
|
+ { "Bass Boost", USB_MIXER_BOOLEAN },
|
|
|
+ { "Loudness", USB_MIXER_BOOLEAN },
|
|
|
+ /* UAC2 specific */
|
|
|
+ { "Input Gain Control", USB_MIXER_U16 },
|
|
|
+ { "Input Gain Pad Control", USB_MIXER_BOOLEAN },
|
|
|
+ { "Phase Inverter Control", USB_MIXER_BOOLEAN },
|
|
|
};
|
|
|
|
|
|
|
|
@@ -958,7 +955,7 @@ static size_t append_ctl_name(struct snd_kcontrol *kctl, const char *str)
|
|
|
static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
|
|
|
unsigned int ctl_mask, int control,
|
|
|
struct usb_audio_term *iterm, int unitid,
|
|
|
- int read_only)
|
|
|
+ int readonly_mask)
|
|
|
{
|
|
|
struct uac_feature_unit_descriptor *desc = raw_desc;
|
|
|
unsigned int len = 0;
|
|
@@ -970,7 +967,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
|
|
|
|
|
|
control++; /* change from zero-based to 1-based value */
|
|
|
|
|
|
- if (control == UAC_GRAPHIC_EQUALIZER_CONTROL) {
|
|
|
+ if (control == UAC_FU_GRAPHIC_EQUALIZER) {
|
|
|
/* FIXME: not supported yet */
|
|
|
return;
|
|
|
}
|
|
@@ -989,20 +986,25 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
|
|
|
cval->control = control;
|
|
|
cval->cmask = ctl_mask;
|
|
|
cval->val_type = audio_feature_info[control-1].type;
|
|
|
- if (ctl_mask == 0)
|
|
|
+ if (ctl_mask == 0) {
|
|
|
cval->channels = 1; /* master channel */
|
|
|
- else {
|
|
|
+ cval->master_readonly = readonly_mask;
|
|
|
+ } else {
|
|
|
int i, c = 0;
|
|
|
for (i = 0; i < 16; i++)
|
|
|
if (ctl_mask & (1 << i))
|
|
|
c++;
|
|
|
cval->channels = c;
|
|
|
+ cval->ch_readonly = readonly_mask;
|
|
|
}
|
|
|
|
|
|
/* get min/max values */
|
|
|
get_min_max(cval, 0);
|
|
|
|
|
|
- if (read_only)
|
|
|
+ /* 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
|
|
|
+ * issue write commands to read-only channels. */
|
|
|
+ if (cval->channels == readonly_mask)
|
|
|
kctl = snd_ctl_new1(&usb_feature_unit_ctl_ro, cval);
|
|
|
else
|
|
|
kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
|
|
@@ -1021,8 +1023,8 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
|
|
|
kctl->id.name, sizeof(kctl->id.name));
|
|
|
|
|
|
switch (control) {
|
|
|
- case UAC_MUTE_CONTROL:
|
|
|
- case UAC_VOLUME_CONTROL:
|
|
|
+ case UAC_FU_MUTE:
|
|
|
+ case UAC_FU_VOLUME:
|
|
|
/* determine the control name. the rule is:
|
|
|
* - if a name id is given in descriptor, use it.
|
|
|
* - if the connected input can be determined, then use the name
|
|
@@ -1049,9 +1051,9 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
|
|
|
len = append_ctl_name(kctl, " Playback");
|
|
|
}
|
|
|
}
|
|
|
- append_ctl_name(kctl, control == UAC_MUTE_CONTROL ?
|
|
|
+ append_ctl_name(kctl, control == UAC_FU_MUTE ?
|
|
|
" Switch" : " Volume");
|
|
|
- if (control == UAC_VOLUME_CONTROL) {
|
|
|
+ if (control == UAC_FU_VOLUME) {
|
|
|
kctl->tlv.c = mixer_vol_tlv;
|
|
|
kctl->vd[0].access |=
|
|
|
SNDRV_CTL_ELEM_ACCESS_TLV_READ |
|
|
@@ -1150,7 +1152,7 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void
|
|
|
snd_printk(KERN_INFO
|
|
|
"usbmixer: master volume quirk for PCM2702 chip\n");
|
|
|
/* disable non-functional volume control */
|
|
|
- master_bits &= ~UAC_FU_VOLUME;
|
|
|
+ master_bits &= ~UAC_CONTROL_BIT(UAC_FU_VOLUME);
|
|
|
break;
|
|
|
}
|
|
|
if (channels > 0)
|
|
@@ -1188,19 +1190,22 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void
|
|
|
|
|
|
for (j = 0; j < channels; j++) {
|
|
|
unsigned int mask = snd_usb_combine_bytes(bmaControls + csize * (j+1), csize);
|
|
|
- if (mask & (1 << (i * 2))) {
|
|
|
+ if (uac2_control_is_readable(mask, i)) {
|
|
|
ch_bits |= (1 << j);
|
|
|
- if (~mask & (1 << ((i * 2) + 1)))
|
|
|
+ if (!uac2_control_is_writeable(mask, i))
|
|
|
ch_read_only |= (1 << j);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /* FIXME: the whole unit is read-only if any of the channels is marked read-only */
|
|
|
+ /* NOTE: build_feature_ctl() will mark the control read-only if all channels
|
|
|
+ * are marked read-only in the descriptors. Otherwise, the control will be
|
|
|
+ * reported as writeable, but the driver will not actually issue a write
|
|
|
+ * command for read-only channels */
|
|
|
if (ch_bits & 1) /* the first channel must be set (for ease of programming) */
|
|
|
- build_feature_ctl(state, _ftr, ch_bits, i, &iterm, unitid, !!ch_read_only);
|
|
|
- if (master_bits & (1 << i * 2))
|
|
|
+ build_feature_ctl(state, _ftr, ch_bits, i, &iterm, unitid, ch_read_only);
|
|
|
+ if (uac2_control_is_readable(master_bits, i))
|
|
|
build_feature_ctl(state, _ftr, 0, i, &iterm, unitid,
|
|
|
- ~master_bits & (1 << ((i * 2) + 1)));
|
|
|
+ !uac2_control_is_writeable(master_bits, i));
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -1392,51 +1397,51 @@ struct procunit_info {
|
|
|
};
|
|
|
|
|
|
static struct procunit_value_info updown_proc_info[] = {
|
|
|
- { USB_PROC_UPDOWN_SWITCH, "Switch", USB_MIXER_BOOLEAN },
|
|
|
- { USB_PROC_UPDOWN_MODE_SEL, "Mode Select", USB_MIXER_U8, 1 },
|
|
|
+ { UAC_UD_ENABLE, "Switch", USB_MIXER_BOOLEAN },
|
|
|
+ { UAC_UD_MODE_SELECT, "Mode Select", USB_MIXER_U8, 1 },
|
|
|
{ 0 }
|
|
|
};
|
|
|
static struct procunit_value_info prologic_proc_info[] = {
|
|
|
- { USB_PROC_PROLOGIC_SWITCH, "Switch", USB_MIXER_BOOLEAN },
|
|
|
- { USB_PROC_PROLOGIC_MODE_SEL, "Mode Select", USB_MIXER_U8, 1 },
|
|
|
+ { UAC_DP_ENABLE, "Switch", USB_MIXER_BOOLEAN },
|
|
|
+ { UAC_DP_MODE_SELECT, "Mode Select", USB_MIXER_U8, 1 },
|
|
|
{ 0 }
|
|
|
};
|
|
|
static struct procunit_value_info threed_enh_proc_info[] = {
|
|
|
- { USB_PROC_3DENH_SWITCH, "Switch", USB_MIXER_BOOLEAN },
|
|
|
- { USB_PROC_3DENH_SPACE, "Spaciousness", USB_MIXER_U8 },
|
|
|
+ { UAC_3D_ENABLE, "Switch", USB_MIXER_BOOLEAN },
|
|
|
+ { UAC_3D_SPACE, "Spaciousness", USB_MIXER_U8 },
|
|
|
{ 0 }
|
|
|
};
|
|
|
static struct procunit_value_info reverb_proc_info[] = {
|
|
|
- { USB_PROC_REVERB_SWITCH, "Switch", USB_MIXER_BOOLEAN },
|
|
|
- { USB_PROC_REVERB_LEVEL, "Level", USB_MIXER_U8 },
|
|
|
- { USB_PROC_REVERB_TIME, "Time", USB_MIXER_U16 },
|
|
|
- { USB_PROC_REVERB_DELAY, "Delay", USB_MIXER_U8 },
|
|
|
+ { UAC_REVERB_ENABLE, "Switch", USB_MIXER_BOOLEAN },
|
|
|
+ { UAC_REVERB_LEVEL, "Level", USB_MIXER_U8 },
|
|
|
+ { UAC_REVERB_TIME, "Time", USB_MIXER_U16 },
|
|
|
+ { UAC_REVERB_FEEDBACK, "Feedback", USB_MIXER_U8 },
|
|
|
{ 0 }
|
|
|
};
|
|
|
static struct procunit_value_info chorus_proc_info[] = {
|
|
|
- { USB_PROC_CHORUS_SWITCH, "Switch", USB_MIXER_BOOLEAN },
|
|
|
- { USB_PROC_CHORUS_LEVEL, "Level", USB_MIXER_U8 },
|
|
|
- { USB_PROC_CHORUS_RATE, "Rate", USB_MIXER_U16 },
|
|
|
- { USB_PROC_CHORUS_DEPTH, "Depth", USB_MIXER_U16 },
|
|
|
+ { UAC_CHORUS_ENABLE, "Switch", USB_MIXER_BOOLEAN },
|
|
|
+ { UAC_CHORUS_LEVEL, "Level", USB_MIXER_U8 },
|
|
|
+ { UAC_CHORUS_RATE, "Rate", USB_MIXER_U16 },
|
|
|
+ { UAC_CHORUS_DEPTH, "Depth", USB_MIXER_U16 },
|
|
|
{ 0 }
|
|
|
};
|
|
|
static struct procunit_value_info dcr_proc_info[] = {
|
|
|
- { USB_PROC_DCR_SWITCH, "Switch", USB_MIXER_BOOLEAN },
|
|
|
- { USB_PROC_DCR_RATIO, "Ratio", USB_MIXER_U16 },
|
|
|
- { USB_PROC_DCR_MAX_AMP, "Max Amp", USB_MIXER_S16 },
|
|
|
- { USB_PROC_DCR_THRESHOLD, "Threshold", USB_MIXER_S16 },
|
|
|
- { USB_PROC_DCR_ATTACK, "Attack Time", USB_MIXER_U16 },
|
|
|
- { USB_PROC_DCR_RELEASE, "Release Time", USB_MIXER_U16 },
|
|
|
+ { UAC_DCR_ENABLE, "Switch", USB_MIXER_BOOLEAN },
|
|
|
+ { UAC_DCR_RATE, "Ratio", USB_MIXER_U16 },
|
|
|
+ { UAC_DCR_MAXAMPL, "Max Amp", USB_MIXER_S16 },
|
|
|
+ { UAC_DCR_THRESHOLD, "Threshold", USB_MIXER_S16 },
|
|
|
+ { UAC_DCR_ATTACK_TIME, "Attack Time", USB_MIXER_U16 },
|
|
|
+ { UAC_DCR_RELEASE_TIME, "Release Time", USB_MIXER_U16 },
|
|
|
{ 0 }
|
|
|
};
|
|
|
|
|
|
static struct procunit_info procunits[] = {
|
|
|
- { USB_PROC_UPDOWN, "Up Down", updown_proc_info },
|
|
|
- { USB_PROC_PROLOGIC, "Dolby Prologic", prologic_proc_info },
|
|
|
- { USB_PROC_3DENH, "3D Stereo Extender", threed_enh_proc_info },
|
|
|
- { USB_PROC_REVERB, "Reverb", reverb_proc_info },
|
|
|
- { USB_PROC_CHORUS, "Chorus", chorus_proc_info },
|
|
|
- { USB_PROC_DCR, "DCR", dcr_proc_info },
|
|
|
+ { UAC_PROCESS_UP_DOWNMIX, "Up Down", updown_proc_info },
|
|
|
+ { UAC_PROCESS_DOLBY_PROLOGIC, "Dolby Prologic", prologic_proc_info },
|
|
|
+ { UAC_PROCESS_STEREO_EXTENDER, "3D Stereo Extender", threed_enh_proc_info },
|
|
|
+ { UAC_PROCESS_REVERB, "Reverb", reverb_proc_info },
|
|
|
+ { UAC_PROCESS_CHORUS, "Chorus", chorus_proc_info },
|
|
|
+ { UAC_PROCESS_DYN_RANGE_COMP, "DCR", dcr_proc_info },
|
|
|
{ 0 },
|
|
|
};
|
|
|
/*
|
|
@@ -1524,7 +1529,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, void *raw
|
|
|
cval->channels = 1;
|
|
|
|
|
|
/* get min/max values */
|
|
|
- if (type == USB_PROC_UPDOWN && cval->control == USB_PROC_UPDOWN_MODE_SEL) {
|
|
|
+ if (type == UAC_PROCESS_UP_DOWNMIX && cval->control == UAC_UD_MODE_SELECT) {
|
|
|
__u8 *control_spec = uac_processing_unit_specific(desc, state->mixer->protocol);
|
|
|
/* FIXME: hard-coded */
|
|
|
cval->min = 1;
|
|
@@ -1619,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;
|
|
@@ -1638,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;
|
|
@@ -1647,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;
|
|
@@ -1729,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");
|
|
@@ -1778,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");
|
|
@@ -1812,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);
|
|
@@ -1912,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;
|
|
|
}
|
|
|
}
|
|
|
|