summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2014-11-27 12:40:51 +0100
committerTakashi Iwai <tiwai@suse.de>2014-11-27 12:40:51 +0100
commitacf403ecc4155153e5e2c1640be90fc166e56ba7 (patch)
treee9655b84776cec00a4da077f5dd1e336dfd9c7c0 /sound
parent69eba10e606a80665f8573221fec589430d9d1cb (diff)
parentb61f90eac1ff9d1b30497e611aba4651d4066706 (diff)
Merge branch 'topic/usb-resume' into for-next
Merge the proper mixer resume support for quirk codes.
Diffstat (limited to 'sound')
-rw-r--r--sound/pci/hda/hda_intel.c4
-rw-r--r--sound/pci/hda/patch_conexant.c31
-rw-r--r--sound/pci/hda/patch_realtek.c145
-rw-r--r--sound/soc/codecs/cs42l51-i2c.c1
-rw-r--r--sound/soc/codecs/cs42l51.c4
-rw-r--r--sound/soc/codecs/cs42l51.h1
-rw-r--r--sound/soc/codecs/es8328-i2c.c2
-rw-r--r--sound/soc/codecs/max98090.c6
-rw-r--r--sound/soc/codecs/rt5645.c2
-rw-r--r--sound/soc/codecs/rt5670.c36
-rw-r--r--sound/soc/codecs/sgtl5000.c3
-rw-r--r--sound/soc/codecs/sgtl5000.h2
-rw-r--r--sound/soc/codecs/wm_adsp.c1
-rw-r--r--sound/soc/fsl/fsl_asrc.c26
-rw-r--r--sound/soc/rockchip/rockchip_i2s.c4
-rw-r--r--sound/soc/samsung/snow.c1
-rw-r--r--sound/soc/sh/fsi.c3
-rw-r--r--sound/soc/sh/rcar/core.c3
-rw-r--r--sound/soc/soc-core.c2
-rw-r--r--sound/soc/soc-pcm.c72
-rw-r--r--sound/usb/mixer.c200
-rw-r--r--sound/usb/mixer.h29
-rw-r--r--sound/usb/mixer_quirks.c784
-rw-r--r--sound/usb/mixer_scarlett.c73
-rw-r--r--sound/usb/quirks.c14
25 files changed, 871 insertions, 578 deletions
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 91fa959d05fe..966e6f98892d 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -219,6 +219,7 @@ MODULE_SUPPORTED_DEVICE("{{Intel, ICH6},"
"{Intel, LPT_LP},"
"{Intel, WPT_LP},"
"{Intel, SPT},"
+ "{Intel, SPT_LP},"
"{Intel, HPT},"
"{Intel, PBG},"
"{Intel, SCH},"
@@ -2003,6 +2004,9 @@ static const struct pci_device_id azx_ids[] = {
/* Sunrise Point */
{ PCI_DEVICE(0x8086, 0xa170),
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
+ /* Sunrise Point-LP */
+ { PCI_DEVICE(0x8086, 0x9d70),
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
/* Haswell */
{ PCI_DEVICE(0x8086, 0x0a0c),
.driver_data = AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL },
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 71e4bad06345..e9ebc7bd752c 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -43,6 +43,7 @@ struct conexant_spec {
unsigned int num_eapds;
hda_nid_t eapds[4];
bool dynamic_eapd;
+ hda_nid_t mute_led_eapd;
unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */
@@ -163,6 +164,17 @@ static void cx_auto_vmaster_hook(void *private_data, int enabled)
cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, enabled);
}
+/* turn on/off EAPD according to Master switch (inversely!) for mute LED */
+static void cx_auto_vmaster_hook_mute_led(void *private_data, int enabled)
+{
+ struct hda_codec *codec = private_data;
+ struct conexant_spec *spec = codec->spec;
+
+ snd_hda_codec_write(codec, spec->mute_led_eapd, 0,
+ AC_VERB_SET_EAPD_BTLENABLE,
+ enabled ? 0x00 : 0x02);
+}
+
static int cx_auto_build_controls(struct hda_codec *codec)
{
int err;
@@ -223,6 +235,7 @@ enum {
CXT_FIXUP_TOSHIBA_P105,
CXT_FIXUP_HP_530,
CXT_FIXUP_CAP_MIX_AMP_5047,
+ CXT_FIXUP_MUTE_LED_EAPD,
};
/* for hda_fixup_thinkpad_acpi() */
@@ -557,6 +570,18 @@ static void cxt_fixup_olpc_xo(struct hda_codec *codec,
}
}
+static void cxt_fixup_mute_led_eapd(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct conexant_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->mute_led_eapd = 0x1b;
+ spec->dynamic_eapd = 1;
+ spec->gen.vmaster_mute.hook = cx_auto_vmaster_hook_mute_led;
+ }
+}
+
/*
* Fix max input level on mixer widget to 0dB
* (originally it has 0x2b steps with 0dB offset 0x14)
@@ -705,6 +730,10 @@ static const struct hda_fixup cxt_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = cxt_fixup_cap_mix_amp_5047,
},
+ [CXT_FIXUP_MUTE_LED_EAPD] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cxt_fixup_mute_led_eapd,
+ },
};
static const struct snd_pci_quirk cxt5045_fixups[] = {
@@ -762,6 +791,7 @@ static const struct snd_pci_quirk cxt5066_fixups[] = {
SND_PCI_QUIRK(0x17aa, 0x21cf, "Lenovo T520", CXT_PINCFG_LENOVO_TP410),
SND_PCI_QUIRK(0x17aa, 0x21da, "Lenovo X220", CXT_PINCFG_LENOVO_TP410),
SND_PCI_QUIRK(0x17aa, 0x21db, "Lenovo X220-tablet", CXT_PINCFG_LENOVO_TP410),
+ SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo IdeaPad Z560", CXT_FIXUP_MUTE_LED_EAPD),
SND_PCI_QUIRK(0x17aa, 0x3975, "Lenovo U300s", CXT_FIXUP_STEREO_DMIC),
SND_PCI_QUIRK(0x17aa, 0x3977, "Lenovo IdeaPad U310", CXT_FIXUP_STEREO_DMIC),
SND_PCI_QUIRK(0x17aa, 0x397b, "Lenovo S205", CXT_FIXUP_STEREO_DMIC),
@@ -780,6 +810,7 @@ static const struct hda_model_fixup cxt5066_fixup_models[] = {
{ .id = CXT_PINCFG_LEMOTE_A1004, .name = "lemote-a1004" },
{ .id = CXT_PINCFG_LEMOTE_A1205, .name = "lemote-a1205" },
{ .id = CXT_FIXUP_OLPC_XO, .name = "olpc-xo" },
+ { .id = CXT_FIXUP_MUTE_LED_EAPD, .name = "mute-led-eapd" },
{}
};
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 3fcb7d951572..0c1cad5a1574 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -290,6 +290,80 @@ static void alc880_unsol_event(struct hda_codec *codec, unsigned int res)
snd_hda_jack_unsol_event(codec, res >> 2);
}
+/* Change EAPD to verb control */
+static void alc_fill_eapd_coef(struct hda_codec *codec)
+{
+ int coef;
+
+ coef = alc_get_coef0(codec);
+
+ switch (codec->vendor_id) {
+ case 0x10ec0262:
+ alc_update_coef_idx(codec, 0x7, 0, 1<<5);
+ break;
+ case 0x10ec0267:
+ case 0x10ec0268:
+ alc_update_coef_idx(codec, 0x7, 0, 1<<13);
+ break;
+ case 0x10ec0269:
+ if ((coef & 0x00f0) == 0x0010)
+ alc_update_coef_idx(codec, 0xd, 0, 1<<14);
+ if ((coef & 0x00f0) == 0x0020)
+ alc_update_coef_idx(codec, 0x4, 1<<15, 0);
+ if ((coef & 0x00f0) == 0x0030)
+ alc_update_coef_idx(codec, 0x10, 1<<9, 0);
+ break;
+ case 0x10ec0280:
+ case 0x10ec0284:
+ case 0x10ec0290:
+ case 0x10ec0292:
+ alc_update_coef_idx(codec, 0x4, 1<<15, 0);
+ break;
+ case 0x10ec0233:
+ case 0x10ec0255:
+ case 0x10ec0282:
+ case 0x10ec0283:
+ case 0x10ec0286:
+ case 0x10ec0288:
+ alc_update_coef_idx(codec, 0x10, 1<<9, 0);
+ break;
+ case 0x10ec0285:
+ case 0x10ec0293:
+ alc_update_coef_idx(codec, 0xa, 1<<13, 0);
+ break;
+ case 0x10ec0662:
+ if ((coef & 0x00f0) == 0x0030)
+ alc_update_coef_idx(codec, 0x4, 1<<10, 0); /* EAPD Ctrl */
+ break;
+ case 0x10ec0272:
+ case 0x10ec0273:
+ case 0x10ec0663:
+ case 0x10ec0665:
+ case 0x10ec0670:
+ case 0x10ec0671:
+ case 0x10ec0672:
+ alc_update_coef_idx(codec, 0xd, 0, 1<<14); /* EAPD Ctrl */
+ break;
+ case 0x10ec0668:
+ alc_update_coef_idx(codec, 0x7, 3<<13, 0);
+ break;
+ case 0x10ec0867:
+ alc_update_coef_idx(codec, 0x4, 1<<10, 0);
+ break;
+ case 0x10ec0888:
+ if ((coef & 0x00f0) == 0x0020 || (coef & 0x00f0) == 0x0030)
+ alc_update_coef_idx(codec, 0x7, 1<<5, 0);
+ break;
+ case 0x10ec0892:
+ alc_update_coef_idx(codec, 0x7, 1<<5, 0);
+ break;
+ case 0x10ec0899:
+ case 0x10ec0900:
+ alc_update_coef_idx(codec, 0x7, 1<<1, 0);
+ break;
+ }
+}
+
/* additional initialization for ALC888 variants */
static void alc888_coef_init(struct hda_codec *codec)
{
@@ -341,6 +415,7 @@ static void alc_eapd_shutup(struct hda_codec *codec)
/* generic EAPD initialization */
static void alc_auto_init_amp(struct hda_codec *codec, int type)
{
+ alc_fill_eapd_coef(codec);
alc_auto_setup_eapd(codec, true);
switch (type) {
case ALC_INIT_GPIO1:
@@ -4480,6 +4555,8 @@ static const struct hda_fixup alc269_fixups[] = {
[ALC269_FIXUP_HEADSET_MODE] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc_fixup_headset_mode,
+ .chained = true,
+ .chain_id = ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED
},
[ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC] = {
.type = HDA_FIXUP_FUNC,
@@ -4669,6 +4746,8 @@ static const struct hda_fixup alc269_fixups[] = {
[ALC255_FIXUP_HEADSET_MODE] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc_fixup_headset_mode_alc255,
+ .chained = true,
+ .chain_id = ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED
},
[ALC255_FIXUP_HEADSET_MODE_NO_HP_MIC] = {
.type = HDA_FIXUP_FUNC,
@@ -4704,8 +4783,6 @@ static const struct hda_fixup alc269_fixups[] = {
[ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc_fixup_dell_wmi,
- .chained_before = true,
- .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE
},
[ALC282_FIXUP_ASPIRE_V5_PINS] = {
.type = HDA_FIXUP_PINS,
@@ -4747,10 +4824,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1028, 0x05f4, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x05f5, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x05f6, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
- SND_PCI_QUIRK(0x1028, 0x0610, "Dell", ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED),
SND_PCI_QUIRK(0x1028, 0x0615, "Dell Vostro 5470", ALC290_FIXUP_SUBWOOFER_HSJACK),
SND_PCI_QUIRK(0x1028, 0x0616, "Dell Vostro 5470", ALC290_FIXUP_SUBWOOFER_HSJACK),
- SND_PCI_QUIRK(0x1028, 0x061f, "Dell", ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED),
SND_PCI_QUIRK(0x1028, 0x0638, "Dell Inspiron 5439", ALC290_FIXUP_MONO_SPEAKERS_HSJACK),
SND_PCI_QUIRK(0x1028, 0x064a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x064b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
@@ -5252,9 +5327,6 @@ static void alc269_fill_coef(struct hda_codec *codec)
}
}
- /* Class D */
- alc_update_coef_idx(codec, 0xd, 0, 1<<14);
-
/* HP */
alc_update_coef_idx(codec, 0x4, 0, 1<<11);
}
@@ -5695,6 +5767,35 @@ static void alc662_fixup_led_gpio1(struct hda_codec *codec,
}
}
+static struct coef_fw alc668_coefs[] = {
+ WRITE_COEF(0x01, 0xbebe), WRITE_COEF(0x02, 0xaaaa), WRITE_COEF(0x03, 0x0),
+ WRITE_COEF(0x04, 0x0180), WRITE_COEF(0x06, 0x0), WRITE_COEF(0x07, 0x0f80),
+ WRITE_COEF(0x08, 0x0031), WRITE_COEF(0x0a, 0x0060), WRITE_COEF(0x0b, 0x0),
+ WRITE_COEF(0x0c, 0x7cf7), WRITE_COEF(0x0d, 0x1080), WRITE_COEF(0x0e, 0x7f7f),
+ WRITE_COEF(0x0f, 0xcccc), WRITE_COEF(0x10, 0xddcc), WRITE_COEF(0x11, 0x0001),
+ WRITE_COEF(0x13, 0x0), WRITE_COEF(0x14, 0x2aa0), WRITE_COEF(0x17, 0xa940),
+ WRITE_COEF(0x19, 0x0), WRITE_COEF(0x1a, 0x0), WRITE_COEF(0x1b, 0x0),
+ WRITE_COEF(0x1c, 0x0), WRITE_COEF(0x1d, 0x0), WRITE_COEF(0x1e, 0x7418),
+ WRITE_COEF(0x1f, 0x0804), WRITE_COEF(0x20, 0x4200), WRITE_COEF(0x21, 0x0468),
+ WRITE_COEF(0x22, 0x8ccc), WRITE_COEF(0x23, 0x0250), WRITE_COEF(0x24, 0x7418),
+ WRITE_COEF(0x27, 0x0), WRITE_COEF(0x28, 0x8ccc), WRITE_COEF(0x2a, 0xff00),
+ WRITE_COEF(0x2b, 0x8000), WRITE_COEF(0xa7, 0xff00), WRITE_COEF(0xa8, 0x8000),
+ WRITE_COEF(0xaa, 0x2e17), WRITE_COEF(0xab, 0xa0c0), WRITE_COEF(0xac, 0x0),
+ WRITE_COEF(0xad, 0x0), WRITE_COEF(0xae, 0x2ac6), WRITE_COEF(0xaf, 0xa480),
+ WRITE_COEF(0xb0, 0x0), WRITE_COEF(0xb1, 0x0), WRITE_COEF(0xb2, 0x0),
+ WRITE_COEF(0xb3, 0x0), WRITE_COEF(0xb4, 0x0), WRITE_COEF(0xb5, 0x1040),
+ WRITE_COEF(0xb6, 0xd697), WRITE_COEF(0xb7, 0x902b), WRITE_COEF(0xb8, 0xd697),
+ WRITE_COEF(0xb9, 0x902b), WRITE_COEF(0xba, 0xb8ba), WRITE_COEF(0xbb, 0xaaab),
+ WRITE_COEF(0xbc, 0xaaaf), WRITE_COEF(0xbd, 0x6aaa), WRITE_COEF(0xbe, 0x1c02),
+ WRITE_COEF(0xc0, 0x00ff), WRITE_COEF(0xc1, 0x0fa6),
+ {}
+};
+
+static void alc668_restore_default_value(struct hda_codec *codec)
+{
+ alc_process_coef_fw(codec, alc668_coefs);
+}
+
enum {
ALC662_FIXUP_ASPIRE,
ALC662_FIXUP_LED_GPIO1,
@@ -6121,29 +6222,6 @@ static const struct snd_hda_pin_quirk alc662_pin_fixup_tbl[] = {
{}
};
-static void alc662_fill_coef(struct hda_codec *codec)
-{
- int coef;
-
- coef = alc_get_coef0(codec);
-
- switch (codec->vendor_id) {
- case 0x10ec0662:
- if ((coef & 0x00f0) == 0x0030)
- alc_update_coef_idx(codec, 0x4, 1<<10, 0); /* EAPD Ctrl */
- break;
- case 0x10ec0272:
- case 0x10ec0273:
- case 0x10ec0663:
- case 0x10ec0665:
- case 0x10ec0670:
- case 0x10ec0671:
- case 0x10ec0672:
- alc_update_coef_idx(codec, 0xd, 0, 1<<14); /* EAPD Ctrl */
- break;
- }
-}
-
/*
*/
static int patch_alc662(struct hda_codec *codec)
@@ -6162,8 +6240,11 @@ static int patch_alc662(struct hda_codec *codec)
alc_fix_pll_init(codec, 0x20, 0x04, 15);
- spec->init_hook = alc662_fill_coef;
- alc662_fill_coef(codec);
+ switch (codec->vendor_id) {
+ case 0x10ec0668:
+ spec->init_hook = alc668_restore_default_value;
+ break;
+ }
snd_hda_pick_fixup(codec, alc662_fixup_models,
alc662_fixup_tbl, alc662_fixups);
diff --git a/sound/soc/codecs/cs42l51-i2c.c b/sound/soc/codecs/cs42l51-i2c.c
index cee51ae177c1..c40428f25ba5 100644
--- a/sound/soc/codecs/cs42l51-i2c.c
+++ b/sound/soc/codecs/cs42l51-i2c.c
@@ -46,6 +46,7 @@ static struct i2c_driver cs42l51_i2c_driver = {
.driver = {
.name = "cs42l51",
.owner = THIS_MODULE,
+ .of_match_table = cs42l51_of_match,
},
.probe = cs42l51_i2c_probe,
.remove = cs42l51_i2c_remove,
diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c
index 09488d97de60..669c38fc3034 100644
--- a/sound/soc/codecs/cs42l51.c
+++ b/sound/soc/codecs/cs42l51.c
@@ -558,11 +558,13 @@ error:
}
EXPORT_SYMBOL_GPL(cs42l51_probe);
-static const struct of_device_id cs42l51_of_match[] = {
+const struct of_device_id cs42l51_of_match[] = {
{ .compatible = "cirrus,cs42l51", },
{ }
};
MODULE_DEVICE_TABLE(of, cs42l51_of_match);
+EXPORT_SYMBOL_GPL(cs42l51_of_match);
+
MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
MODULE_DESCRIPTION("Cirrus Logic CS42L51 ALSA SoC Codec Driver");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs42l51.h b/sound/soc/codecs/cs42l51.h
index 8c55bf384bc6..0ca805492ac4 100644
--- a/sound/soc/codecs/cs42l51.h
+++ b/sound/soc/codecs/cs42l51.h
@@ -22,6 +22,7 @@ struct device;
extern const struct regmap_config cs42l51_regmap;
int cs42l51_probe(struct device *dev, struct regmap *regmap);
+extern const struct of_device_id cs42l51_of_match[];
#define CS42L51_CHIP_ID 0x1B
#define CS42L51_CHIP_REV_A 0x00
diff --git a/sound/soc/codecs/es8328-i2c.c b/sound/soc/codecs/es8328-i2c.c
index aae410d122ee..2d05b5d3a6ce 100644
--- a/sound/soc/codecs/es8328-i2c.c
+++ b/sound/soc/codecs/es8328-i2c.c
@@ -19,7 +19,7 @@
#include "es8328.h"
static const struct i2c_device_id es8328_id[] = {
- { "everest,es8328", 0 },
+ { "es8328", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, es8328_id);
diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c
index d519294f57c7..1229554f1464 100644
--- a/sound/soc/codecs/max98090.c
+++ b/sound/soc/codecs/max98090.c
@@ -1941,13 +1941,13 @@ static int max98090_dai_set_sysclk(struct snd_soc_dai *dai,
* 0x02 (when master clk is 20MHz to 40MHz)..
* 0x03 (when master clk is 40MHz to 60MHz)..
*/
- if ((freq >= 10000000) && (freq < 20000000)) {
+ if ((freq >= 10000000) && (freq <= 20000000)) {
snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK,
M98090_PSCLK_DIV1);
- } else if ((freq >= 20000000) && (freq < 40000000)) {
+ } else if ((freq > 20000000) && (freq <= 40000000)) {
snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK,
M98090_PSCLK_DIV2);
- } else if ((freq >= 40000000) && (freq < 60000000)) {
+ } else if ((freq > 40000000) && (freq <= 60000000)) {
snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK,
M98090_PSCLK_DIV4);
} else {
diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c
index 3fb83bf09768..d16331e0b64d 100644
--- a/sound/soc/codecs/rt5645.c
+++ b/sound/soc/codecs/rt5645.c
@@ -139,6 +139,7 @@ static const struct reg_default rt5645_reg[] = {
{ 0x76, 0x000a },
{ 0x77, 0x0c00 },
{ 0x78, 0x0000 },
+ { 0x79, 0x0123 },
{ 0x80, 0x0000 },
{ 0x81, 0x0000 },
{ 0x82, 0x0000 },
@@ -334,6 +335,7 @@ static bool rt5645_readable_register(struct device *dev, unsigned int reg)
case RT5645_DMIC_CTRL2:
case RT5645_TDM_CTRL_1:
case RT5645_TDM_CTRL_2:
+ case RT5645_TDM_CTRL_3:
case RT5645_GLB_CLK:
case RT5645_PLL_CTRL1:
case RT5645_PLL_CTRL2:
diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c
index ba9d9b4d4857..9bd8b4f63303 100644
--- a/sound/soc/codecs/rt5670.c
+++ b/sound/soc/codecs/rt5670.c
@@ -100,18 +100,18 @@ static const struct reg_default rt5670_reg[] = {
{ 0x4c, 0x5380 },
{ 0x4f, 0x0073 },
{ 0x52, 0x00d3 },
- { 0x53, 0xf0f0 },
+ { 0x53, 0xf000 },
{ 0x61, 0x0000 },
{ 0x62, 0x0001 },
{ 0x63, 0x00c3 },
{ 0x64, 0x0000 },
- { 0x65, 0x0000 },
+ { 0x65, 0x0001 },
{ 0x66, 0x0000 },
{ 0x6f, 0x8000 },
{ 0x70, 0x8000 },
{ 0x71, 0x8000 },
{ 0x72, 0x8000 },
- { 0x73, 0x1110 },
+ { 0x73, 0x7770 },
{ 0x74, 0x0e00 },
{ 0x75, 0x1505 },
{ 0x76, 0x0015 },
@@ -125,21 +125,21 @@ static const struct reg_default rt5670_reg[] = {
{ 0x83, 0x0000 },
{ 0x84, 0x0000 },
{ 0x85, 0x0000 },
- { 0x86, 0x0008 },
+ { 0x86, 0x0004 },
{ 0x87, 0x0000 },
{ 0x88, 0x0000 },
{ 0x89, 0x0000 },
{ 0x8a, 0x0000 },
{ 0x8b, 0x0000 },
- { 0x8c, 0x0007 },
+ { 0x8c, 0x0003 },
{ 0x8d, 0x0000 },
{ 0x8e, 0x0004 },
{ 0x8f, 0x1100 },
{ 0x90, 0x0646 },
{ 0x91, 0x0c06 },
{ 0x93, 0x0000 },
- { 0x94, 0x0000 },
- { 0x95, 0x0000 },
+ { 0x94, 0x1270 },
+ { 0x95, 0x1000 },
{ 0x97, 0x0000 },
{ 0x98, 0x0000 },
{ 0x99, 0x0000 },
@@ -150,11 +150,11 @@ static const struct reg_default rt5670_reg[] = {
{ 0x9e, 0x0400 },
{ 0xae, 0x7000 },
{ 0xaf, 0x0000 },
- { 0xb0, 0x6000 },
+ { 0xb0, 0x7000 },
{ 0xb1, 0x0000 },
{ 0xb2, 0x0000 },
{ 0xb3, 0x001f },
- { 0xb4, 0x2206 },
+ { 0xb4, 0x220c },
{ 0xb5, 0x1f00 },
{ 0xb6, 0x0000 },
{ 0xb7, 0x0000 },
@@ -171,25 +171,25 @@ static const struct reg_default rt5670_reg[] = {
{ 0xcf, 0x1813 },
{ 0xd0, 0x0690 },
{ 0xd1, 0x1c17 },
- { 0xd3, 0xb320 },
+ { 0xd3, 0xa220 },
{ 0xd4, 0x0000 },
{ 0xd6, 0x0400 },
{ 0xd9, 0x0809 },
{ 0xda, 0x0000 },
{ 0xdb, 0x0001 },
{ 0xdc, 0x0049 },
- { 0xdd, 0x0009 },
+ { 0xdd, 0x0024 },
{ 0xe6, 0x8000 },
{ 0xe7, 0x0000 },
- { 0xec, 0xb300 },
+ { 0xec, 0xa200 },
{ 0xed, 0x0000 },
- { 0xee, 0xb300 },
+ { 0xee, 0xa200 },
{ 0xef, 0x0000 },
{ 0xf8, 0x0000 },
{ 0xf9, 0x0000 },
{ 0xfa, 0x8010 },
{ 0xfb, 0x0033 },
- { 0xfc, 0x0080 },
+ { 0xfc, 0x0100 },
};
static bool rt5670_volatile_register(struct device *dev, unsigned int reg)
@@ -1877,6 +1877,10 @@ static const struct snd_soc_dapm_route rt5670_dapm_routes[] = {
{ "DAC1 MIXR", "DAC1 Switch", "DAC1 R Mux" },
{ "DAC1 MIXR", NULL, "DAC Stereo1 Filter" },
+ { "DAC Stereo1 Filter", NULL, "PLL1", is_sys_clk_from_pll },
+ { "DAC Mono Left Filter", NULL, "PLL1", is_sys_clk_from_pll },
+ { "DAC Mono Right Filter", NULL, "PLL1", is_sys_clk_from_pll },
+
{ "DAC MIX", NULL, "DAC1 MIXL" },
{ "DAC MIX", NULL, "DAC1 MIXR" },
@@ -1926,14 +1930,10 @@ static const struct snd_soc_dapm_route rt5670_dapm_routes[] = {
{ "DAC L1", NULL, "DAC L1 Power" },
{ "DAC L1", NULL, "Stereo DAC MIXL" },
- { "DAC L1", NULL, "PLL1", is_sys_clk_from_pll },
{ "DAC R1", NULL, "DAC R1 Power" },
{ "DAC R1", NULL, "Stereo DAC MIXR" },
- { "DAC R1", NULL, "PLL1", is_sys_clk_from_pll },
{ "DAC L2", NULL, "Mono DAC MIXL" },
- { "DAC L2", NULL, "PLL1", is_sys_clk_from_pll },
{ "DAC R2", NULL, "Mono DAC MIXR" },
- { "DAC R2", NULL, "PLL1", is_sys_clk_from_pll },
{ "OUT MIXL", "BST1 Switch", "BST1" },
{ "OUT MIXL", "INL Switch", "INL VOL" },
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c
index 6bb77d76561b..dab9b15304af 100644
--- a/sound/soc/codecs/sgtl5000.c
+++ b/sound/soc/codecs/sgtl5000.c
@@ -1299,8 +1299,7 @@ static int sgtl5000_probe(struct snd_soc_codec *codec)
/* enable small pop, introduce 400ms delay in turning off */
snd_soc_update_bits(codec, SGTL5000_CHIP_REF_CTRL,
- SGTL5000_SMALL_POP,
- SGTL5000_SMALL_POP);
+ SGTL5000_SMALL_POP, 1);
/* disable short cut detector */
snd_soc_write(codec, SGTL5000_CHIP_SHORT_CTRL, 0);
diff --git a/sound/soc/codecs/sgtl5000.h b/sound/soc/codecs/sgtl5000.h
index 2f8c88931f69..bd7a344bf8c5 100644
--- a/sound/soc/codecs/sgtl5000.h
+++ b/sound/soc/codecs/sgtl5000.h
@@ -275,7 +275,7 @@
#define SGTL5000_BIAS_CTRL_MASK 0x000e
#define SGTL5000_BIAS_CTRL_SHIFT 1
#define SGTL5000_BIAS_CTRL_WIDTH 3
-#define SGTL5000_SMALL_POP 0x0001
+#define SGTL5000_SMALL_POP 0
/*
* SGTL5000_CHIP_MIC_CTRL
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index f412a9911a75..67124783558a 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -1355,6 +1355,7 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp)
file, blocks, pos - firmware->size);
out_fw:
+ regmap_async_complete(regmap);
release_firmware(firmware);
wm_adsp_buf_free(&buf_list);
out:
diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c
index ed866e9a2928..9deabdd2b1a2 100644
--- a/sound/soc/fsl/fsl_asrc.c
+++ b/sound/soc/fsl/fsl_asrc.c
@@ -684,12 +684,38 @@ static bool fsl_asrc_writeable_reg(struct device *dev, unsigned int reg)
}
}
+static struct reg_default fsl_asrc_reg[] = {
+ { REG_ASRCTR, 0x0000 }, { REG_ASRIER, 0x0000 },
+ { REG_ASRCNCR, 0x0000 }, { REG_ASRCFG, 0x0000 },
+ { REG_ASRCSR, 0x0000 }, { REG_ASRCDR1, 0x0000 },
+ { REG_ASRCDR2, 0x0000 }, { REG_ASRSTR, 0x0000 },
+ { REG_ASRRA, 0x0000 }, { REG_ASRRB, 0x0000 },
+ { REG_ASRRC, 0x0000 }, { REG_ASRPM1, 0x0000 },
+ { REG_ASRPM2, 0x0000 }, { REG_ASRPM3, 0x0000 },
+ { REG_ASRPM4, 0x0000 }, { REG_ASRPM5, 0x0000 },
+ { REG_ASRTFR1, 0x0000 }, { REG_ASRCCR, 0x0000 },
+ { REG_ASRDIA, 0x0000 }, { REG_ASRDOA, 0x0000 },
+ { REG_ASRDIB, 0x0000 }, { REG_ASRDOB, 0x0000 },
+ { REG_ASRDIC, 0x0000 }, { REG_ASRDOC, 0x0000 },
+ { REG_ASRIDRHA, 0x0000 }, { REG_ASRIDRLA, 0x0000 },
+ { REG_ASRIDRHB, 0x0000 }, { REG_ASRIDRLB, 0x0000 },
+ { REG_ASRIDRHC, 0x0000 }, { REG_ASRIDRLC, 0x0000 },
+ { REG_ASR76K, 0x0A47 }, { REG_ASR56K, 0x0DF3 },
+ { REG_ASRMCRA, 0x0000 }, { REG_ASRFSTA, 0x0000 },
+ { REG_ASRMCRB, 0x0000 }, { REG_ASRFSTB, 0x0000 },
+ { REG_ASRMCRC, 0x0000 }, { REG_ASRFSTC, 0x0000 },
+ { REG_ASRMCR1A, 0x0000 }, { REG_ASRMCR1B, 0x0000 },
+ { REG_ASRMCR1C, 0x0000 },
+};
+
static const struct regmap_config fsl_asrc_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = REG_ASRMCR1C,
+ .reg_defaults = fsl_asrc_reg,
+ .num_reg_defaults = ARRAY_SIZE(fsl_asrc_reg),
.readable_reg = fsl_asrc_readable_reg,
.volatile_reg = fsl_asrc_volatile_reg,
.writeable_reg = fsl_asrc_writeable_reg,
diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c
index f373e37f8305..c74ba37f862c 100644
--- a/sound/soc/rockchip/rockchip_i2s.c
+++ b/sound/soc/rockchip/rockchip_i2s.c
@@ -154,8 +154,10 @@ static void rockchip_snd_rxctrl(struct rk_i2s_dev *i2s, int on)
while (val) {
regmap_read(i2s->regmap, I2S_CLR, &val);
retry--;
- if (!retry)
+ if (!retry) {
dev_warn(i2s->dev, "fail to clear\n");
+ break;
+ }
}
}
}
diff --git a/sound/soc/samsung/snow.c b/sound/soc/samsung/snow.c
index 0acf5d0eed53..72118a77dd5b 100644
--- a/sound/soc/samsung/snow.c
+++ b/sound/soc/samsung/snow.c
@@ -110,6 +110,7 @@ static const struct of_device_id snow_of_match[] = {
{ .compatible = "google,snow-audio-max98095", },
{},
};
+MODULE_DEVICE_TABLE(of, snow_of_match);
static struct platform_driver snow_driver = {
.driver = {
diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c
index 66fddec9543d..88e5df474ccf 100644
--- a/sound/soc/sh/fsi.c
+++ b/sound/soc/sh/fsi.c
@@ -1711,8 +1711,7 @@ static const struct snd_soc_dai_ops fsi_dai_ops = {
static struct snd_pcm_hardware fsi_pcm_hardware = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_PAUSE,
+ SNDRV_PCM_INFO_MMAP_VALID,
.buffer_bytes_max = 64 * 1024,
.period_bytes_min = 32,
.period_bytes_max = 8192,
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c
index 1922ec57d10a..70042197f9e2 100644
--- a/sound/soc/sh/rcar/core.c
+++ b/sound/soc/sh/rcar/core.c
@@ -886,8 +886,7 @@ static int rsnd_dai_probe(struct platform_device *pdev,
static struct snd_pcm_hardware rsnd_pcm_hardware = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_PAUSE,
+ SNDRV_PCM_INFO_MMAP_VALID,
.buffer_bytes_max = 64 * 1024,
.period_bytes_min = 32,
.period_bytes_max = 8192,
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 4c8f8a23a0e9..b60ff56ebc0f 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -884,7 +884,7 @@ static struct snd_soc_dai *snd_soc_find_dai(
list_for_each_entry(component, &component_list, list) {
if (dlc->of_node && component->dev->of_node != dlc->of_node)
continue;
- if (dlc->name && strcmp(dev_name(component->dev), dlc->name))
+ if (dlc->name && strcmp(component->name, dlc->name))
continue;
list_for_each_entry(dai, &component->dai_list, list) {
if (dlc->dai_name && strcmp(dai->name, dlc->dai_name))
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index 002311afdeaa..57277dd79e11 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -1522,13 +1522,36 @@ static void dpcm_set_fe_runtime(struct snd_pcm_substream *substream)
dpcm_init_runtime_hw(runtime, &cpu_dai_drv->capture);
}
+static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd);
+
+/* Set FE's runtime_update state; the state is protected via PCM stream lock
+ * for avoiding the race with trigger callback.
+ * If the state is unset and a trigger is pending while the previous operation,
+ * process the pending trigger action here.
+ */
+static void dpcm_set_fe_update_state(struct snd_soc_pcm_runtime *fe,
+ int stream, enum snd_soc_dpcm_update state)
+{
+ struct snd_pcm_substream *substream =
+ snd_soc_dpcm_get_substream(fe, stream);
+
+ snd_pcm_stream_lock_irq(substream);
+ if (state == SND_SOC_DPCM_UPDATE_NO && fe->dpcm[stream].trigger_pending) {
+ dpcm_fe_dai_do_trigger(substream,
+ fe->dpcm[stream].trigger_pending - 1);
+ fe->dpcm[stream].trigger_pending = 0;
+ }
+ fe->dpcm[stream].runtime_update = state;
+ snd_pcm_stream_unlock_irq(substream);
+}
+
static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream)
{
struct snd_soc_pcm_runtime *fe = fe_substream->private_data;
struct snd_pcm_runtime *runtime = fe_substream->runtime;
int stream = fe_substream->stream, ret = 0;
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
ret = dpcm_be_dai_startup(fe, fe_substream->stream);
if (ret < 0) {
@@ -1550,13 +1573,13 @@ static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream)
dpcm_set_fe_runtime(fe_substream);
snd_pcm_limit_hw_rates(runtime);
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
return 0;
unwind:
dpcm_be_dai_startup_unwind(fe, fe_substream->stream);
be_err:
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
return ret;
}
@@ -1603,7 +1626,7 @@ static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream)
struct snd_soc_pcm_runtime *fe = substream->private_data;
int stream = substream->stream;
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
/* shutdown the BEs */
dpcm_be_dai_shutdown(fe, substream->stream);
@@ -1617,7 +1640,7 @@ static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream)
dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP);
fe->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
return 0;
}
@@ -1665,7 +1688,7 @@ static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream)
int err, stream = substream->stream;
mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
dev_dbg(fe->dev, "ASoC: hw_free FE %s\n", fe->dai_link->name);
@@ -1680,7 +1703,7 @@ static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream)
err = dpcm_be_dai_hw_free(fe, stream);
fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE;
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
mutex_unlock(&fe->card->mutex);
return 0;
@@ -1773,7 +1796,7 @@ static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream,
int ret, stream = substream->stream;
mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
memcpy(&fe->dpcm[substream->stream].hw_params, params,
sizeof(struct snd_pcm_hw_params));
@@ -1796,7 +1819,7 @@ static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream,
fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_PARAMS;
out:
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
mutex_unlock(&fe->card->mutex);
return ret;
}
@@ -1910,7 +1933,7 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
}
EXPORT_SYMBOL_GPL(dpcm_be_dai_trigger);
-static int dpcm_fe_dai_trigger(struct snd_pcm_substream *substream, int cmd)
+static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_soc_pcm_runtime *fe = substream->private_data;
int stream = substream->stream, ret;
@@ -1984,6 +2007,23 @@ out:
return ret;
}
+static int dpcm_fe_dai_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_soc_pcm_runtime *fe = substream->private_data;
+ int stream = substream->stream;
+
+ /* if FE's runtime_update is already set, we're in race;
+ * process this trigger later at exit
+ */
+ if (fe->dpcm[stream].runtime_update != SND_SOC_DPCM_UPDATE_NO) {
+ fe->dpcm[stream].trigger_pending = cmd + 1;
+ return 0; /* delayed, assuming it's successful */
+ }
+
+ /* we're alone, let's trigger */
+ return dpcm_fe_dai_do_trigger(substream, cmd);
+}
+
int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream)
{
struct snd_soc_dpcm *dpcm;
@@ -2027,7 +2067,7 @@ static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream)
dev_dbg(fe->dev, "ASoC: prepare FE %s\n", fe->dai_link->name);
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
/* there is no point preparing this FE if there are no BEs */
if (list_empty(&fe->dpcm[stream].be_clients)) {
@@ -2054,7 +2094,7 @@ static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream)
fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE;
out:
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
mutex_unlock(&fe->card->mutex);
return ret;
@@ -2201,11 +2241,11 @@ static int dpcm_run_new_update(struct snd_soc_pcm_runtime *fe, int stream)
{
int ret;
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_BE);
ret = dpcm_run_update_startup(fe, stream);
if (ret < 0)
dev_err(fe->dev, "ASoC: failed to startup some BEs\n");
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
return ret;
}
@@ -2214,11 +2254,11 @@ static int dpcm_run_old_update(struct snd_soc_pcm_runtime *fe, int stream)
{
int ret;
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_BE);
ret = dpcm_run_update_shutdown(fe, stream);
if (ret < 0)
dev_err(fe->dev, "ASoC: failed to shutdown some BEs\n");
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
return ret;
}
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 14e1455ca9de..41650d5b93b7 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)->head.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)
@@ -286,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;
@@ -300,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) {
@@ -316,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;
@@ -347,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);
@@ -392,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);
}
@@ -423,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;
@@ -441,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 */
@@ -472,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,
@@ -506,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;
@@ -565,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))
@@ -578,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;
}
@@ -829,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 */
@@ -954,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,
@@ -1061,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);
}
@@ -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) {
@@ -1231,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;
@@ -1343,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);
}
/*
@@ -1524,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++) {
@@ -1557,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);
}
/*
@@ -1628,12 +1630,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 +1647,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) {
@@ -1813,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;
@@ -1867,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;
}
@@ -1923,11 +1919,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 +1935,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) {
@@ -2023,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;
@@ -2032,10 +2021,11 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid,
cval->res = 1;
cval->initialized = 1;
- if (desc->bDescriptorSubtype == UAC2_CLOCK_SELECTOR)
- cval->control = UAC2_CX_CLOCK_SELECTOR;
- else
+ if (state->mixer->protocol == UAC_VERSION_1)
cval->control = 0;
+ else /* UAC_VERSION_2 */
+ cval->control = (desc->bDescriptorSubtype == UAC2_CLOCK_SELECTOR) ?
+ UAC2_CX_CLOCK_SELECTOR : UAC2_SU_SELECTOR;
namelist = kmalloc(sizeof(char *) * desc->bNrInPins, GFP_KERNEL);
if (!namelist) {
@@ -2094,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);
}
/*
@@ -2243,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",
@@ -2273,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) {
@@ -2283,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);
+ }
}
}
}
@@ -2293,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;
@@ -2305,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;
@@ -2318,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:
@@ -2516,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) {
@@ -2547,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;
+ }
}
}
}
@@ -2567,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 2478a844a322..d3268f0ee2b3 100644
--- a/sound/usb/mixer.h
+++ b/sound/usb/mixer.h
@@ -1,6 +1,8 @@
#ifndef __USBMIXER_H
#define __USBMIXER_H
+#include <sound/info.h>
+
struct usb_mixer_interface {
struct snd_usb_audio *chip;
struct usb_host_interface *hostif;
@@ -8,7 +10,7 @@ struct usb_mixer_interface {
unsigned int ignore_ctl_error;
struct urb *urb;
/* array[MAX_ID_ELEMS], indexed by unit id */
- struct usb_mixer_elem_info **id_elems;
+ struct usb_mixer_elem_list **id_elems;
/* the usb audio specification version this interface complies to */
int protocol;
@@ -20,9 +22,6 @@ struct usb_mixer_interface {
struct urb *rc_urb;
struct usb_ctrlrequest *rc_setup_packet;
u8 rc_buffer[6];
-
- u8 audigy2nx_leds[3];
- u8 xonar_u1_status;
};
#define MAX_CHANNELS 16 /* max logical channels */
@@ -36,11 +35,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 +74,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 7b9331a17026..dc9df007d3e3 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,
@@ -150,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
*
@@ -277,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;
@@ -367,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;
}
@@ -443,100 +473,122 @@ 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,
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 = {
@@ -545,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) */
@@ -568,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;
@@ -620,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;
@@ -649,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;
}
@@ -667,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",
@@ -680,72 +733,77 @@ 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 */
#define _MAKE_NI_CONTROL(bRequest,wIndex) ((bRequest) << 16 | (wIndex))
-static int snd_nativeinstruments_control_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int snd_ni_control_init_val(struct usb_mixer_interface *mixer,
+ struct snd_kcontrol *kctl)
{
- struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
struct usb_device *dev = mixer->chip->dev;
- u8 bRequest = (kcontrol->private_value >> 16) & 0xff;
- u16 wIndex = kcontrol->private_value & 0xffff;
- u8 tmp;
- int ret;
-
- down_read(&mixer->chip->shutdown_rwsem);
- if (mixer->chip->shutdown)
- ret = -ENODEV;
- else
- ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), bRequest,
- USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
- 0, wIndex,
- &tmp, sizeof(tmp), 1000);
- up_read(&mixer->chip->shutdown_rwsem);
+ unsigned int pval = kctl->private_value;
+ u8 value;
+ int err;
- if (ret < 0) {
+ err = 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_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = kcontrol->private_value >> 24;
return 0;
}
+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
+ 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;
+}
+
static int snd_nativeinstruments_control_put(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;
-
- down_read(&mixer->chip->shutdown_rwsem);
- if (mixer->chip->shutdown)
- ret = -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);
+ 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;
- if (ret < 0) {
- dev_err(&dev->dev,
- "unable to issue vendor write request (ret = %d)", ret);
- return ret;
- }
+ if (oldval == newval)
+ return 0;
- 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[] = {
@@ -816,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;
@@ -833,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)
{
@@ -852,133 +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;
+ 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,
@@ -993,32 +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;
- }
-
- 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;
}
@@ -1200,7 +1179,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,
@@ -1530,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;
@@ -1538,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,
@@ -1563,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,
@@ -1588,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,
@@ -1606,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,
@@ -1637,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,
@@ -1653,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,
@@ -1691,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;
}
diff --git a/sound/usb/mixer_scarlett.c b/sound/usb/mixer_scarlett.c
index a0a874507de5..9109652b88b9 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,14 +445,24 @@ 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)
{
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,
@@ -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,
@@ -528,10 +552,11 @@ static int add_new_ctl(struct usb_mixer_interface *mixer,
if (!elem)
return -ENOMEM;
- elem->mixer = mixer;
+ elem->head.mixer = mixer;
+ elem->head.resume = resume;
elem->control = offset;
elem->idx_off = num;
- elem->id = index;
+ elem->head.id = index;
elem->val_type = val_type;
elem->channels = channels;
@@ -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)
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 809d7fab4633..2c1018e447b1 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -1155,6 +1155,20 @@ void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe,
if ((le16_to_cpu(dev->descriptor.idVendor) == 0x23ba) &&
(requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS)
mdelay(20);
+
+ /* Marantz/Denon devices with USB DAC functionality need a delay
+ * after each class compliant request
+ */
+ if ((le16_to_cpu(dev->descriptor.idVendor) == 0x154e) &&
+ (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS) {
+
+ switch (le16_to_cpu(dev->descriptor.idProduct)) {
+ case 0x3005: /* Marantz HD-DAC1 */
+ case 0x3006: /* Marantz SA-14S1 */
+ mdelay(20);
+ break;
+ }
+ }
}
/*