summaryrefslogtreecommitdiffstats
path: root/sound/usb/mixer_quirks.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-05-23 13:05:43 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2012-05-23 13:05:43 -0700
commit2e341ca686042aa464efa755447e7bcee91d1eb6 (patch)
treec6b16b6b6a6e871fa04396cb2c7eb759bcad5be3 /sound/usb/mixer_quirks.c
parent927ad551031798d4cba49766549600bbb33872d7 (diff)
parent85e184e4c3cd3e2285ceab91ff8f0cac094e8a85 (diff)
downloadlinux-2e341ca686042aa464efa755447e7bcee91d1eb6.tar.gz
linux-2e341ca686042aa464efa755447e7bcee91d1eb6.tar.bz2
linux-2e341ca686042aa464efa755447e7bcee91d1eb6.zip
Merge tag 'sound-3.5' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound updates from Takashi Iwai: "This is the first big chunk for 3.5 merges of sound stuff. There are a few big changes in different areas. First off, the streaming logic of USB-audio endpoints has been largely rewritten for the better support of "implicit feedback". If anything about USB got broken, this change has to be checked. For HD-audio, the resume procedure was changed; instead of delaying the resume of the hardware until the first use, now waking up immediately at resume. This is for buggy BIOS. For ASoC, dynamic PCM support and the improved support for digital links between off-SoC devices are major framework changes. Some highlights are below: * HD-audio - Avoid accesses of invalid pin-control bits that may stall the codec - V-ref setup cleanups - Fix the races in power-saving code - Fix the races in codec cache hashes and connection lists - Split some common codes for BIOS auto-parser to hda_auto_parser.c - Changed the PM resume code to wake up immediately for buggy BIOS - Creative SoundCore3D support - Add Conexant CX20751/2/3/4 codec support * ASoC - Dynamic PCM support, allowing support for SoCs with internal routing through components with tight sequencing and formatting constraints within their internal paths or where there are multiple components connected with CPU managed DMA controllers inside the SoC. - Greatly improved support for direct digital links between off-SoC devices, providing a much simpler way of connecting things like digital basebands to CODECs. - Much more fine grained and robust locking, cleaning up some of the confusion that crept in with multi-component. - CPU support for nVidia Tegra 30 I2S and audio hub controllers and ST-Ericsson MSP I2S controolers - New CODEC drivers for Cirrus CS42L52, LAPIS Semiconductor ML26124, Texas Instruments LM49453. - Some regmap changes needed by the Tegra I2S driver. - mc13783 audio support. * Misc - Rewrite with module_pci_driver() - Xonar DGX support for snd-oxygen - Improvement of packet handling in snd-firewire driver - New USB-endpoint streaming logic - Enhanced M-audio FTU quirks and relevant cleanups - Increment the support of OSS devices to 256 - snd-aloop accuracy improvement There are a few more pending changes for 3.5, but they will be sent slightly later as partly depending on the changes of DRM." Fix up conflicts in regmap (due to duplicate patches, with some further updates then having already come in from the regmap tree). Also some fairly trivial context conflicts in the imx and mcx soc drivers. * tag 'sound-3.5' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (280 commits) ALSA: snd-usb: fix stream info output in /proc ALSA: pcm - Add proper state checks to snd_pcm_drain() ALSA: sh: Fix up namespace collision in sh_dac_audio. ALSA: hda/realtek - Fix unused variable compile warning ASoC: sh: fsi: enable chip specific data transfer mode ASoC: sh: fsi: call fsi_hw_startup/shutdown from fsi_dai_trigger() ASoC: sh: fsi: use same format for IN/OUT ASoC: sh: fsi: add fsi_version() and removed meaningless version check ASoC: sh: fsi: use register field macro name on IN/OUT_DMAC ASoC: tegra: Add machine driver for WM8753 codec ALSA: hda - Fix possible races of accesses to connection list array ASoC: OMAP: HDMI: Introduce codec ARM: mx31_3ds: Add sound support ASoC: imx-mc13783 cleanup mx31moboard: Add sound support ASoC: mc13783 codec cleanups ASoC: add imx-mc13783 sound support ASoC: Add mc13783 codec mfd: mc13xxx: add codec platform data ASoC: don't flip master of DT-instantiated DAI links ...
Diffstat (limited to 'sound/usb/mixer_quirks.c')
-rw-r--r--sound/usb/mixer_quirks.c472
1 files changed, 445 insertions, 27 deletions
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index ab125ee0b0f0..41f4b6911920 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -42,6 +42,77 @@
extern struct snd_kcontrol_new *snd_usb_feature_unit_ctl;
+/* 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.
+ *
+ * Since there doesn't seem to be a devices that needs a multichannel
+ * version, we keep it mono for simplicity.
+ */
+static int snd_create_std_mono_ctl(struct usb_mixer_interface *mixer,
+ unsigned int unitid,
+ unsigned int control,
+ unsigned int cmask,
+ int val_type,
+ const char *name,
+ snd_kcontrol_tlv_rw_t *tlv_callback)
+{
+ int err;
+ struct usb_mixer_elem_info *cval;
+ struct snd_kcontrol *kctl;
+
+ cval = kzalloc(sizeof(*cval), GFP_KERNEL);
+ if (!cval)
+ return -ENOMEM;
+
+ cval->id = unitid;
+ cval->mixer = mixer;
+ cval->val_type = val_type;
+ cval->channels = 1;
+ cval->control = control;
+ cval->cmask = cmask;
+
+ /* get_min_max() is called only for integer volumes later,
+ * so provide a short-cut for booleans */
+ cval->min = 0;
+ cval->max = 1;
+ cval->res = 0;
+ cval->dBmin = 0;
+ cval->dBmax = 0;
+
+ /* Create control */
+ kctl = snd_ctl_new1(snd_usb_feature_unit_ctl, cval);
+ if (!kctl) {
+ kfree(cval);
+ return -ENOMEM;
+ }
+
+ /* Set name */
+ snprintf(kctl->id.name, sizeof(kctl->id.name), name);
+ kctl->private_free = usb_mixer_elem_free;
+
+ /* set TLV */
+ if (tlv_callback) {
+ kctl->tlv.c = tlv_callback;
+ kctl->vd[0].access |=
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ 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;
+}
+
/*
* Sound Blaster remote control configuration
*
@@ -495,60 +566,218 @@ static int snd_nativeinstruments_create_mixer(struct usb_mixer_interface *mixer,
}
/* M-Audio FastTrack Ultra quirks */
+/* FTU Effect switch */
+struct snd_ftu_eff_switch_priv_val {
+ struct usb_mixer_interface *mixer;
+ int cached_value;
+ int is_cached;
+};
-/* private_free callback */
-static void usb_mixer_elem_free(struct snd_kcontrol *kctl)
+static int snd_ftu_eff_switch_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
- kfree(kctl->private_data);
- kctl->private_data = NULL;
+ static const char *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;
}
-static int snd_maudio_ftu_create_ctl(struct usb_mixer_interface *mixer,
- int in, int out, const char *name)
+static int snd_ftu_eff_switch_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
{
- struct usb_mixer_elem_info *cval;
+ struct snd_usb_audio *chip;
+ struct usb_mixer_interface *mixer;
+ struct snd_ftu_eff_switch_priv_val *pval;
+ int err;
+ unsigned char value[2];
+
+ const int id = 6;
+ const int validx = 1;
+ const int val_len = 2;
+
+ value[0] = 0x00;
+ value[1] = 0x00;
+
+ pval = (struct snd_ftu_eff_switch_priv_val *)
+ kctl->private_value;
+
+ if (pval->is_cached) {
+ ucontrol->value.enumerated.item[0] = pval->cached_value;
+ 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;
+
+
+ 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);
+ if (err < 0)
+ return err;
+
+ ucontrol->value.enumerated.item[0] = value[0];
+ pval->cached_value = value[0];
+ pval->is_cached = 1;
+
+ return 0;
+}
+
+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];
+
+
+ const int id = 6;
+ const int validx = 1;
+ const int val_len = 2;
+
+ changed = 0;
+
+ pval = (struct snd_ftu_eff_switch_priv_val *)
+ kctl->private_value;
+ cur_val = pval->cached_value;
+ new_val = ucontrol->value.enumerated.item[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;
+
+ if (!pval->is_cached) {
+ /* Read current value */
+ 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);
+ 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;
+ 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);
+ if (err < 0)
+ return err;
+
+ pval->cached_value = new_val;
+ pval->is_cached = 1;
+ changed = 1;
+ }
+
+ return changed;
+}
+
+static int snd_ftu_create_effect_switch(struct usb_mixer_interface *mixer)
+{
+ static struct snd_kcontrol_new template = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Effect Program Switch",
+ .index = 0,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_ftu_eff_switch_info,
+ .get = snd_ftu_eff_switch_get,
+ .put = snd_ftu_eff_switch_put
+ };
+
+ int err;
struct snd_kcontrol *kctl;
+ struct snd_ftu_eff_switch_priv_val *pval;
- cval = kzalloc(sizeof(*cval), GFP_KERNEL);
- if (!cval)
+ pval = kzalloc(sizeof(*pval), GFP_KERNEL);
+ if (!pval)
return -ENOMEM;
- cval->id = 5;
- cval->mixer = mixer;
- cval->val_type = USB_MIXER_S16;
- cval->channels = 1;
- cval->control = out + 1;
- cval->cmask = 1 << in;
+ pval->cached_value = 0;
+ pval->is_cached = 0;
+ pval->mixer = mixer;
- kctl = snd_ctl_new1(snd_usb_feature_unit_ctl, cval);
+ template.private_value = (unsigned long) pval;
+ kctl = snd_ctl_new1(&template, mixer->chip);
if (!kctl) {
- kfree(cval);
+ kfree(pval);
return -ENOMEM;
}
- snprintf(kctl->id.name, sizeof(kctl->id.name), name);
- kctl->private_free = usb_mixer_elem_free;
- return snd_usb_mixer_add_control(mixer, kctl);
+ err = snd_ctl_add(mixer->chip->card, kctl);
+ if (err < 0)
+ return err;
+
+ return 0;
}
-static int snd_maudio_ftu_create_mixer(struct usb_mixer_interface *mixer)
+/* Create volume controls for FTU devices*/
+static int snd_ftu_create_volume_ctls(struct usb_mixer_interface *mixer)
{
char name[64];
+ unsigned int control, cmask;
int in, out, err;
+ const unsigned int id = 5;
+ const int val_type = USB_MIXER_S16;
+
for (out = 0; out < 8; out++) {
+ control = out + 1;
for (in = 0; in < 8; in++) {
+ cmask = 1 << in;
snprintf(name, sizeof(name),
- "AIn%d - Out%d Capture Volume", in + 1, out + 1);
- err = snd_maudio_ftu_create_ctl(mixer, in, out, name);
+ "AIn%d - Out%d Capture Volume",
+ in + 1, out + 1);
+ err = snd_create_std_mono_ctl(mixer, id, control,
+ cmask, val_type, name,
+ &snd_usb_mixer_vol_tlv);
if (err < 0)
return err;
}
-
for (in = 8; in < 16; in++) {
+ cmask = 1 << in;
snprintf(name, sizeof(name),
- "DIn%d - Out%d Playback Volume", in - 7, out + 1);
- err = snd_maudio_ftu_create_ctl(mixer, in, out, name);
+ "DIn%d - Out%d Playback Volume",
+ in - 7, out + 1);
+ err = snd_create_std_mono_ctl(mixer, id, control,
+ cmask, val_type, name,
+ &snd_usb_mixer_vol_tlv);
if (err < 0)
return err;
}
@@ -557,6 +786,191 @@ static int snd_maudio_ftu_create_mixer(struct usb_mixer_interface *mixer)
return 0;
}
+/* This control needs a volume quirk, see mixer.c */
+static int snd_ftu_create_effect_volume_ctl(struct usb_mixer_interface *mixer)
+{
+ static const char name[] = "Effect Volume";
+ const unsigned int id = 6;
+ const int val_type = USB_MIXER_U8;
+ const unsigned int control = 2;
+ const unsigned int cmask = 0;
+
+ return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type,
+ name, snd_usb_mixer_vol_tlv);
+}
+
+/* This control needs a volume quirk, see mixer.c */
+static int snd_ftu_create_effect_duration_ctl(struct usb_mixer_interface *mixer)
+{
+ static const char name[] = "Effect Duration";
+ const unsigned int id = 6;
+ const int val_type = USB_MIXER_S16;
+ const unsigned int control = 3;
+ const unsigned int cmask = 0;
+
+ return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type,
+ name, snd_usb_mixer_vol_tlv);
+}
+
+/* This control needs a volume quirk, see mixer.c */
+static int snd_ftu_create_effect_feedback_ctl(struct usb_mixer_interface *mixer)
+{
+ static const char name[] = "Effect Feedback Volume";
+ const unsigned int id = 6;
+ const int val_type = USB_MIXER_U8;
+ const unsigned int control = 4;
+ const unsigned int cmask = 0;
+
+ return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type,
+ name, NULL);
+}
+
+static int snd_ftu_create_effect_return_ctls(struct usb_mixer_interface *mixer)
+{
+ unsigned int cmask;
+ int err, ch;
+ char name[48];
+
+ const unsigned int id = 7;
+ const int val_type = USB_MIXER_S16;
+ const unsigned int control = 7;
+
+ for (ch = 0; ch < 4; ++ch) {
+ cmask = 1 << ch;
+ snprintf(name, sizeof(name),
+ "Effect Return %d Volume", ch + 1);
+ err = snd_create_std_mono_ctl(mixer, id, control,
+ cmask, val_type, name,
+ snd_usb_mixer_vol_tlv);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static int snd_ftu_create_effect_send_ctls(struct usb_mixer_interface *mixer)
+{
+ unsigned int cmask;
+ int err, ch;
+ char name[48];
+
+ const unsigned int id = 5;
+ const int val_type = USB_MIXER_S16;
+ const unsigned int control = 9;
+
+ for (ch = 0; ch < 8; ++ch) {
+ cmask = 1 << ch;
+ snprintf(name, sizeof(name),
+ "Effect Send AIn%d Volume", ch + 1);
+ err = snd_create_std_mono_ctl(mixer, id, control, cmask,
+ val_type, name,
+ snd_usb_mixer_vol_tlv);
+ if (err < 0)
+ return err;
+ }
+ for (ch = 8; ch < 16; ++ch) {
+ cmask = 1 << ch;
+ snprintf(name, sizeof(name),
+ "Effect Send DIn%d Volume", ch - 7);
+ err = snd_create_std_mono_ctl(mixer, id, control, cmask,
+ val_type, name,
+ snd_usb_mixer_vol_tlv);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+static int snd_ftu_create_mixer(struct usb_mixer_interface *mixer)
+{
+ int err;
+
+ err = snd_ftu_create_volume_ctls(mixer);
+ if (err < 0)
+ return err;
+
+ err = snd_ftu_create_effect_switch(mixer);
+ if (err < 0)
+ return err;
+ err = snd_ftu_create_effect_volume_ctl(mixer);
+ if (err < 0)
+ return err;
+
+ err = snd_ftu_create_effect_duration_ctl(mixer);
+ if (err < 0)
+ return err;
+
+ err = snd_ftu_create_effect_feedback_ctl(mixer);
+ if (err < 0)
+ return err;
+
+ err = snd_ftu_create_effect_return_ctls(mixer);
+ if (err < 0)
+ return err;
+
+ err = snd_ftu_create_effect_send_ctls(mixer);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+
+/*
+ * Create mixer for Electrix Ebox-44
+ *
+ * The mixer units from this device are corrupt, and even where they
+ * are valid they presents mono controls as L and R channels of
+ * stereo. So we create a good mixer in code.
+ */
+
+static int snd_ebox44_create_mixer(struct usb_mixer_interface *mixer)
+{
+ int err;
+
+ err = snd_create_std_mono_ctl(mixer, 4, 1, 0x0, USB_MIXER_INV_BOOLEAN,
+ "Headphone Playback Switch", NULL);
+ if (err < 0)
+ return err;
+ err = snd_create_std_mono_ctl(mixer, 4, 2, 0x1, USB_MIXER_S16,
+ "Headphone A Mix Playback Volume", NULL);
+ if (err < 0)
+ return err;
+ err = snd_create_std_mono_ctl(mixer, 4, 2, 0x2, USB_MIXER_S16,
+ "Headphone B Mix Playback Volume", NULL);
+ if (err < 0)
+ return err;
+
+ err = snd_create_std_mono_ctl(mixer, 7, 1, 0x0, USB_MIXER_INV_BOOLEAN,
+ "Output Playback Switch", NULL);
+ if (err < 0)
+ return err;
+ err = snd_create_std_mono_ctl(mixer, 7, 2, 0x1, USB_MIXER_S16,
+ "Output A Playback Volume", NULL);
+ if (err < 0)
+ return err;
+ err = snd_create_std_mono_ctl(mixer, 7, 2, 0x2, USB_MIXER_S16,
+ "Output B Playback Volume", NULL);
+ if (err < 0)
+ return err;
+
+ err = snd_create_std_mono_ctl(mixer, 10, 1, 0x0, USB_MIXER_INV_BOOLEAN,
+ "Input Capture Switch", NULL);
+ if (err < 0)
+ return err;
+ err = snd_create_std_mono_ctl(mixer, 10, 2, 0x1, USB_MIXER_S16,
+ "Input A Capture Volume", NULL);
+ if (err < 0)
+ return err;
+ err = snd_create_std_mono_ctl(mixer, 10, 2, 0x2, USB_MIXER_S16,
+ "Input B Capture Volume", NULL);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
void snd_emuusb_set_samplerate(struct snd_usb_audio *chip,
unsigned char samplerate_id)
{
@@ -600,7 +1014,7 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
case USB_ID(0x0763, 0x2080): /* M-Audio Fast Track Ultra */
case USB_ID(0x0763, 0x2081): /* M-Audio Fast Track Ultra 8R */
- err = snd_maudio_ftu_create_mixer(mixer);
+ err = snd_ftu_create_mixer(mixer);
break;
case USB_ID(0x0b05, 0x1739):
@@ -619,6 +1033,10 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
snd_nativeinstruments_ta10_mixers,
ARRAY_SIZE(snd_nativeinstruments_ta10_mixers));
break;
+
+ case USB_ID(0x200c, 0x1018): /* Electrix Ebox-44 */
+ err = snd_ebox44_create_mixer(mixer);
+ break;
}
return err;