summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/sound/tdm-slot.txt4
-rw-r--r--sound/core/pcm_native.c20
-rw-r--r--sound/pci/emu10k1/emu10k1x.c2
-rw-r--r--sound/pci/hda/patch_realtek.c6
-rw-r--r--sound/soc/codecs/max98390.c26
-rw-r--r--sound/soc/codecs/max98390.h3
-rw-r--r--sound/soc/codecs/rl6231.c4
-rw-r--r--sound/soc/codecs/rt5645.c14
-rw-r--r--sound/soc/fsl/fsl-asoc-card.c2
-rw-r--r--sound/soc/intel/boards/bytcr_rt5640.c12
-rw-r--r--sound/soc/intel/boards/glk_rt5682_max98357a.c2
-rw-r--r--sound/soc/intel/boards/kbl_da7219_max98927.c4
-rw-r--r--sound/soc/intel/boards/kbl_rt5663_max98927.c2
-rw-r--r--sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c2
-rw-r--r--sound/soc/meson/axg-fifo.c10
-rw-r--r--sound/soc/meson/meson-card-utils.c17
-rw-r--r--sound/soc/soc-core.c22
-rw-r--r--sound/soc/soc-pcm.c44
-rw-r--r--sound/soc/sof/nocodec.c6
-rw-r--r--sound/usb/card.c54
-rw-r--r--sound/usb/quirks-table.h12
-rw-r--r--sound/usb/usbaudio.h6
22 files changed, 219 insertions, 55 deletions
diff --git a/Documentation/devicetree/bindings/sound/tdm-slot.txt b/Documentation/devicetree/bindings/sound/tdm-slot.txt
index 34cf70e2cbc4..4bb513ae62fc 100644
--- a/Documentation/devicetree/bindings/sound/tdm-slot.txt
+++ b/Documentation/devicetree/bindings/sound/tdm-slot.txt
@@ -14,8 +14,8 @@ For instance:
dai-tdm-slot-tx-mask = <0 1>;
dai-tdm-slot-rx-mask = <1 0>;
-And for each spcified driver, there could be one .of_xlate_tdm_slot_mask()
-to specify a explicit mapping of the channels and the slots. If it's absent
+And for each specified driver, there could be one .of_xlate_tdm_slot_mask()
+to specify an explicit mapping of the channels and the slots. If it's absent
the default snd_soc_of_xlate_tdm_slot_mask() will be used to generating the
tx and rx masks.
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 47838f57a647..9630d2523948 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -138,6 +138,16 @@ void snd_pcm_stream_lock_irq(struct snd_pcm_substream *substream)
}
EXPORT_SYMBOL_GPL(snd_pcm_stream_lock_irq);
+static void snd_pcm_stream_lock_nested(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_group *group = &substream->self_group;
+
+ if (substream->pcm->nonatomic)
+ mutex_lock_nested(&group->mutex, SINGLE_DEPTH_NESTING);
+ else
+ spin_lock_nested(&group->lock, SINGLE_DEPTH_NESTING);
+}
+
/**
* snd_pcm_stream_unlock_irq - Unlock the PCM stream
* @substream: PCM substream
@@ -2166,6 +2176,12 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
}
pcm_file = f.file->private_data;
substream1 = pcm_file->substream;
+
+ if (substream == substream1) {
+ res = -EINVAL;
+ goto _badf;
+ }
+
group = kzalloc(sizeof(*group), GFP_KERNEL);
if (!group) {
res = -ENOMEM;
@@ -2194,7 +2210,7 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
snd_pcm_stream_unlock_irq(substream);
snd_pcm_group_lock_irq(target_group, nonatomic);
- snd_pcm_stream_lock(substream1);
+ snd_pcm_stream_lock_nested(substream1);
snd_pcm_group_assign(substream1, target_group);
refcount_inc(&target_group->refs);
snd_pcm_stream_unlock(substream1);
@@ -2210,7 +2226,7 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
static void relink_to_local(struct snd_pcm_substream *substream)
{
- snd_pcm_stream_lock(substream);
+ snd_pcm_stream_lock_nested(substream);
snd_pcm_group_assign(substream, &substream->self_group);
snd_pcm_stream_unlock(substream);
}
diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c
index ddb7c2ce3f7c..def8161cde4c 100644
--- a/sound/pci/emu10k1/emu10k1x.c
+++ b/sound/pci/emu10k1/emu10k1x.c
@@ -1040,7 +1040,7 @@ static void snd_emu10k1x_proc_reg_write(struct snd_info_entry *entry,
if (sscanf(line, "%x %x %x", &reg, &channel_id, &val) != 3)
continue;
- if (reg < 0x49 && val <= 0xffffffff && channel_id <= 2)
+ if (reg < 0x49 && channel_id <= 2)
snd_emu10k1x_ptr_write(emu, reg, channel_id, val);
}
}
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 0aa778ff7f2b..6d73f8beadb6 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -8161,6 +8161,12 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
ALC225_STANDARD_PINS,
{0x12, 0xb7a60130},
{0x17, 0x90170110}),
+ SND_HDA_PIN_QUIRK(0x10ec0623, 0x17aa, "Lenovo", ALC283_FIXUP_HEADSET_MIC,
+ {0x14, 0x01014010},
+ {0x17, 0x90170120},
+ {0x18, 0x02a11030},
+ {0x19, 0x02a1103f},
+ {0x21, 0x0221101f}),
{}
};
diff --git a/sound/soc/codecs/max98390.c b/sound/soc/codecs/max98390.c
index b9ce44dda886..0d63ebfbff2f 100644
--- a/sound/soc/codecs/max98390.c
+++ b/sound/soc/codecs/max98390.c
@@ -754,6 +754,7 @@ static struct snd_soc_dai_driver max98390_dai[] = {
static int max98390_dsm_init(struct snd_soc_component *component)
{
int ret;
+ int param_size, param_start_addr;
char filename[128];
const char *vendor, *product;
struct max98390_priv *max98390 =
@@ -778,16 +779,31 @@ static int max98390_dsm_init(struct snd_soc_component *component)
}
dev_dbg(component->dev,
- "max98390: param fw size %ld\n",
+ "max98390: param fw size %zd\n",
fw->size);
+ if (fw->size < MAX98390_DSM_PARAM_MIN_SIZE) {
+ dev_err(component->dev,
+ "param fw is invalid.\n");
+ goto err_alloc;
+ }
dsm_param = (char *)fw->data;
+ param_start_addr = (dsm_param[0] & 0xff) | (dsm_param[1] & 0xff) << 8;
+ param_size = (dsm_param[2] & 0xff) | (dsm_param[3] & 0xff) << 8;
+ if (param_size > MAX98390_DSM_PARAM_MAX_SIZE ||
+ param_start_addr < DSM_STBASS_HPF_B0_BYTE0 ||
+ fw->size < param_size + MAX98390_DSM_PAYLOAD_OFFSET) {
+ dev_err(component->dev,
+ "param fw is invalid.\n");
+ goto err_alloc;
+ }
+ regmap_write(max98390->regmap, MAX98390_R203A_AMP_EN, 0x80);
dsm_param += MAX98390_DSM_PAYLOAD_OFFSET;
- regmap_bulk_write(max98390->regmap, DSM_EQ_BQ1_B0_BYTE0,
- dsm_param,
- fw->size - MAX98390_DSM_PAYLOAD_OFFSET);
- release_firmware(fw);
+ regmap_bulk_write(max98390->regmap, param_start_addr,
+ dsm_param, param_size);
regmap_write(max98390->regmap, MAX98390_R23E1_DSP_GLOBAL_EN, 0x01);
+err_alloc:
+ release_firmware(fw);
err:
return ret;
}
diff --git a/sound/soc/codecs/max98390.h b/sound/soc/codecs/max98390.h
index f59cb114d957..5f444e7779b0 100644
--- a/sound/soc/codecs/max98390.h
+++ b/sound/soc/codecs/max98390.h
@@ -650,7 +650,8 @@
/* DSM register offset */
#define MAX98390_DSM_PAYLOAD_OFFSET 16
-#define MAX98390_DSM_PAYLOAD_OFFSET_2 495
+#define MAX98390_DSM_PARAM_MAX_SIZE 770
+#define MAX98390_DSM_PARAM_MIN_SIZE 670
struct max98390_priv {
struct regmap *regmap;
diff --git a/sound/soc/codecs/rl6231.c b/sound/soc/codecs/rl6231.c
index 2586d1cafc0c..8c9daf32bab8 100644
--- a/sound/soc/codecs/rl6231.c
+++ b/sound/soc/codecs/rl6231.c
@@ -80,8 +80,8 @@ int rl6231_calc_dmic_clk(int rate)
for (i = 0; i < ARRAY_SIZE(div); i++) {
if ((div[i] % 3) == 0)
continue;
- /* find divider that gives DMIC frequency below 3.072MHz */
- if (3072000 * div[i] >= rate)
+ /* find divider that gives DMIC frequency below 1.536MHz */
+ if (1536000 * div[i] >= rate)
return i;
}
diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c
index 6ba1849a77b0..e2e1d5b03b38 100644
--- a/sound/soc/codecs/rt5645.c
+++ b/sound/soc/codecs/rt5645.c
@@ -3625,6 +3625,12 @@ static const struct rt5645_platform_data asus_t100ha_platform_data = {
.inv_jd1_1 = true,
};
+static const struct rt5645_platform_data asus_t101ha_platform_data = {
+ .dmic1_data_pin = RT5645_DMIC_DATA_IN2N,
+ .dmic2_data_pin = RT5645_DMIC2_DISABLE,
+ .jd_mode = 3,
+};
+
static const struct rt5645_platform_data lenovo_ideapad_miix_310_pdata = {
.jd_mode = 3,
.in2_diff = true,
@@ -3709,6 +3715,14 @@ static const struct dmi_system_id dmi_platform_data[] = {
.driver_data = (void *)&asus_t100ha_platform_data,
},
{
+ .ident = "ASUS T101HA",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "T101HA"),
+ },
+ .driver_data = (void *)&asus_t101ha_platform_data,
+ },
+ {
.ident = "MINIX Z83-4",
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "MINIX"),
diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c
index cf4feb835743..00be73900888 100644
--- a/sound/soc/fsl/fsl-asoc-card.c
+++ b/sound/soc/fsl/fsl-asoc-card.c
@@ -581,7 +581,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
if (!fsl_asoc_card_is_ac97(priv) && !codec_dev) {
dev_err(&pdev->dev, "failed to find codec device\n");
- ret = -EINVAL;
+ ret = -EPROBE_DEFER;
goto asrc_fail;
}
diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c
index 30f70bbdf89c..1fdb70b9e478 100644
--- a/sound/soc/intel/boards/bytcr_rt5640.c
+++ b/sound/soc/intel/boards/bytcr_rt5640.c
@@ -754,6 +754,18 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = {
BYT_RT5640_JD_NOT_INV |
BYT_RT5640_MCLK_EN),
},
+ { /* Toshiba Encore WT10-A */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "TOSHIBA WT10-A-103"),
+ },
+ .driver_data = (void *)(BYT_RT5640_DMIC1_MAP |
+ BYT_RT5640_JD_SRC_JD1_IN4P |
+ BYT_RT5640_OVCD_TH_2000UA |
+ BYT_RT5640_OVCD_SF_0P75 |
+ BYT_RT5640_SSP0_AIF2 |
+ BYT_RT5640_MCLK_EN),
+ },
{ /* Catch-all for generic Insyde tablets, must be last */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
diff --git a/sound/soc/intel/boards/glk_rt5682_max98357a.c b/sound/soc/intel/boards/glk_rt5682_max98357a.c
index 48eda1a8aa6c..954ab01f695b 100644
--- a/sound/soc/intel/boards/glk_rt5682_max98357a.c
+++ b/sound/soc/intel/boards/glk_rt5682_max98357a.c
@@ -407,7 +407,7 @@ static struct snd_soc_dai_link geminilake_dais[] = {
.name = "Glk Audio Echo Reference cap",
.stream_name = "Echoreference Capture",
.init = NULL,
- .capture_only = 1,
+ .dpcm_capture = 1,
.nonatomic = 1,
.dynamic = 1,
SND_SOC_DAILINK_REG(echoref, dummy, platform),
diff --git a/sound/soc/intel/boards/kbl_da7219_max98927.c b/sound/soc/intel/boards/kbl_da7219_max98927.c
index cc9b5eab8b4a..e29c31ffd241 100644
--- a/sound/soc/intel/boards/kbl_da7219_max98927.c
+++ b/sound/soc/intel/boards/kbl_da7219_max98927.c
@@ -692,7 +692,7 @@ static struct snd_soc_dai_link kabylake_dais[] = {
.name = "Kbl Audio Echo Reference cap",
.stream_name = "Echoreference Capture",
.init = NULL,
- .capture_only = 1,
+ .dpcm_capture = 1,
.nonatomic = 1,
SND_SOC_DAILINK_REG(echoref, dummy, platform),
},
@@ -858,7 +858,7 @@ static struct snd_soc_dai_link kabylake_max98_927_373_dais[] = {
.name = "Kbl Audio Echo Reference cap",
.stream_name = "Echoreference Capture",
.init = NULL,
- .capture_only = 1,
+ .dpcm_capture = 1,
.nonatomic = 1,
SND_SOC_DAILINK_REG(echoref, dummy, platform),
},
diff --git a/sound/soc/intel/boards/kbl_rt5663_max98927.c b/sound/soc/intel/boards/kbl_rt5663_max98927.c
index 658a9da3a40f..09ba55fc36d5 100644
--- a/sound/soc/intel/boards/kbl_rt5663_max98927.c
+++ b/sound/soc/intel/boards/kbl_rt5663_max98927.c
@@ -672,7 +672,7 @@ static struct snd_soc_dai_link kabylake_dais[] = {
.name = "Kbl Audio Echo Reference cap",
.stream_name = "Echoreference Capture",
.init = NULL,
- .capture_only = 1,
+ .dpcm_capture = 1,
.nonatomic = 1,
SND_SOC_DAILINK_REG(echoref, dummy, platform),
},
diff --git a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c
index 1b1f8d7a4ea3..b34cf6cf1139 100644
--- a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c
+++ b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c
@@ -566,7 +566,7 @@ static struct snd_soc_dai_link kabylake_dais[] = {
.name = "Kbl Audio Echo Reference cap",
.stream_name = "Echoreference Capture",
.init = NULL,
- .capture_only = 1,
+ .dpcm_capture = 1,
.nonatomic = 1,
SND_SOC_DAILINK_REG(echoref, dummy, platform),
},
diff --git a/sound/soc/meson/axg-fifo.c b/sound/soc/meson/axg-fifo.c
index 2e9b56b29d31..b2e867113226 100644
--- a/sound/soc/meson/axg-fifo.c
+++ b/sound/soc/meson/axg-fifo.c
@@ -249,7 +249,7 @@ int axg_fifo_pcm_open(struct snd_soc_component *component,
/* Enable pclk to access registers and clock the fifo ip */
ret = clk_prepare_enable(fifo->pclk);
if (ret)
- return ret;
+ goto free_irq;
/* Setup status2 so it reports the memory pointer */
regmap_update_bits(fifo->map, FIFO_CTRL1,
@@ -269,8 +269,14 @@ int axg_fifo_pcm_open(struct snd_soc_component *component,
/* Take memory arbitror out of reset */
ret = reset_control_deassert(fifo->arb);
if (ret)
- clk_disable_unprepare(fifo->pclk);
+ goto free_clk;
+
+ return 0;
+free_clk:
+ clk_disable_unprepare(fifo->pclk);
+free_irq:
+ free_irq(fifo->irq, ss);
return ret;
}
EXPORT_SYMBOL_GPL(axg_fifo_pcm_open);
diff --git a/sound/soc/meson/meson-card-utils.c b/sound/soc/meson/meson-card-utils.c
index 2ca8c98e204f..5a4a91c88734 100644
--- a/sound/soc/meson/meson-card-utils.c
+++ b/sound/soc/meson/meson-card-utils.c
@@ -49,19 +49,26 @@ int meson_card_reallocate_links(struct snd_soc_card *card,
links = krealloc(priv->card.dai_link,
num_links * sizeof(*priv->card.dai_link),
GFP_KERNEL | __GFP_ZERO);
+ if (!links)
+ goto err_links;
+
ldata = krealloc(priv->link_data,
num_links * sizeof(*priv->link_data),
GFP_KERNEL | __GFP_ZERO);
-
- if (!links || !ldata) {
- dev_err(priv->card.dev, "failed to allocate links\n");
- return -ENOMEM;
- }
+ if (!ldata)
+ goto err_ldata;
priv->card.dai_link = links;
priv->link_data = ldata;
priv->card.num_links = num_links;
return 0;
+
+err_ldata:
+ kfree(links);
+err_links:
+ dev_err(priv->card.dev, "failed to allocate links\n");
+ return -ENOMEM;
+
}
EXPORT_SYMBOL_GPL(meson_card_reallocate_links);
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index b07eca2c6ccc..7b387202c5db 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -1648,9 +1648,25 @@ match:
dai_link->platforms->name = component->name;
/* convert non BE into BE */
- dai_link->no_pcm = 1;
- dai_link->dpcm_playback = 1;
- dai_link->dpcm_capture = 1;
+ if (!dai_link->no_pcm) {
+ dai_link->no_pcm = 1;
+
+ if (dai_link->dpcm_playback)
+ dev_warn(card->dev,
+ "invalid configuration, dailink %s has flags no_pcm=0 and dpcm_playback=1\n",
+ dai_link->name);
+ if (dai_link->dpcm_capture)
+ dev_warn(card->dev,
+ "invalid configuration, dailink %s has flags no_pcm=0 and dpcm_capture=1\n",
+ dai_link->name);
+
+ /* convert normal link into DPCM one */
+ if (!(dai_link->dpcm_playback ||
+ dai_link->dpcm_capture)) {
+ dai_link->dpcm_playback = !dai_link->capture_only;
+ dai_link->dpcm_capture = !dai_link->playback_only;
+ }
+ }
/*
* override any BE fixups
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index 276505fb9d50..2c114b4542ce 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -2789,20 +2789,44 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
struct snd_pcm *pcm;
char new_name[64];
int ret = 0, playback = 0, capture = 0;
+ int stream;
int i;
+ if (rtd->dai_link->dynamic && rtd->num_cpus > 1) {
+ dev_err(rtd->dev,
+ "DPCM doesn't support Multi CPU for Front-Ends yet\n");
+ return -EINVAL;
+ }
+
if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm) {
- cpu_dai = asoc_rtd_to_cpu(rtd, 0);
- if (rtd->num_cpus > 1) {
- dev_err(rtd->dev,
- "DPCM doesn't support Multi CPU yet\n");
- return -EINVAL;
+ if (rtd->dai_link->dpcm_playback) {
+ stream = SNDRV_PCM_STREAM_PLAYBACK;
+
+ for_each_rtd_cpu_dais(rtd, i, cpu_dai)
+ if (!snd_soc_dai_stream_valid(cpu_dai,
+ stream)) {
+ dev_err(rtd->card->dev,
+ "CPU DAI %s for rtd %s does not support playback\n",
+ cpu_dai->name,
+ rtd->dai_link->stream_name);
+ return -EINVAL;
+ }
+ playback = 1;
+ }
+ if (rtd->dai_link->dpcm_capture) {
+ stream = SNDRV_PCM_STREAM_CAPTURE;
+
+ for_each_rtd_cpu_dais(rtd, i, cpu_dai)
+ if (!snd_soc_dai_stream_valid(cpu_dai,
+ stream)) {
+ dev_err(rtd->card->dev,
+ "CPU DAI %s for rtd %s does not support capture\n",
+ cpu_dai->name,
+ rtd->dai_link->stream_name);
+ return -EINVAL;
+ }
+ capture = 1;
}
-
- playback = rtd->dai_link->dpcm_playback &&
- snd_soc_dai_stream_valid(cpu_dai, SNDRV_PCM_STREAM_PLAYBACK);
- capture = rtd->dai_link->dpcm_capture &&
- snd_soc_dai_stream_valid(cpu_dai, SNDRV_PCM_STREAM_CAPTURE);
} else {
/* Adapt stream for codec2codec links */
int cpu_capture = rtd->dai_link->params ?
diff --git a/sound/soc/sof/nocodec.c b/sound/soc/sof/nocodec.c
index ce053ba8f2e8..d03b5be31255 100644
--- a/sound/soc/sof/nocodec.c
+++ b/sound/soc/sof/nocodec.c
@@ -52,8 +52,10 @@ static int sof_nocodec_bes_setup(struct device *dev,
links[i].platforms->name = dev_name(dev);
links[i].codecs->dai_name = "snd-soc-dummy-dai";
links[i].codecs->name = "snd-soc-dummy";
- links[i].dpcm_playback = 1;
- links[i].dpcm_capture = 1;
+ if (ops->drv[i].playback.channels_min)
+ links[i].dpcm_playback = 1;
+ if (ops->drv[i].capture.channels_min)
+ links[i].dpcm_capture = 1;
}
card->dai_link = links;
diff --git a/sound/usb/card.c b/sound/usb/card.c
index fd6fd1726ea0..162bdd6eb4d4 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -634,7 +634,6 @@ static int usb_audio_probe(struct usb_interface *intf,
id, &chip);
if (err < 0)
goto __error;
- chip->pm_intf = intf;
break;
} else if (vid[i] != -1 || pid[i] != -1) {
dev_info(&dev->dev,
@@ -651,6 +650,13 @@ static int usb_audio_probe(struct usb_interface *intf,
goto __error;
}
}
+
+ if (chip->num_interfaces >= MAX_CARD_INTERFACES) {
+ dev_info(&dev->dev, "Too many interfaces assigned to the single USB-audio card\n");
+ err = -EINVAL;
+ goto __error;
+ }
+
dev_set_drvdata(&dev->dev, chip);
/*
@@ -703,6 +709,7 @@ static int usb_audio_probe(struct usb_interface *intf,
}
usb_chip[chip->index] = chip;
+ chip->intf[chip->num_interfaces] = intf;
chip->num_interfaces++;
usb_set_intfdata(intf, chip);
atomic_dec(&chip->active);
@@ -818,19 +825,37 @@ void snd_usb_unlock_shutdown(struct snd_usb_audio *chip)
int snd_usb_autoresume(struct snd_usb_audio *chip)
{
+ int i, err;
+
if (atomic_read(&chip->shutdown))
return -EIO;
- if (atomic_inc_return(&chip->active) == 1)
- return usb_autopm_get_interface(chip->pm_intf);
+ if (atomic_inc_return(&chip->active) != 1)
+ return 0;
+
+ for (i = 0; i < chip->num_interfaces; i++) {
+ err = usb_autopm_get_interface(chip->intf[i]);
+ if (err < 0) {
+ /* rollback */
+ while (--i >= 0)
+ usb_autopm_put_interface(chip->intf[i]);
+ atomic_dec(&chip->active);
+ return err;
+ }
+ }
return 0;
}
void snd_usb_autosuspend(struct snd_usb_audio *chip)
{
+ int i;
+
if (atomic_read(&chip->shutdown))
return;
- if (atomic_dec_and_test(&chip->active))
- usb_autopm_put_interface(chip->pm_intf);
+ if (!atomic_dec_and_test(&chip->active))
+ return;
+
+ for (i = 0; i < chip->num_interfaces; i++)
+ usb_autopm_put_interface(chip->intf[i]);
}
static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message)
@@ -843,9 +868,6 @@ static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message)
if (chip == (void *)-1L)
return 0;
- chip->autosuspended = !!PMSG_IS_AUTO(message);
- if (!chip->autosuspended)
- snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot);
if (!chip->num_suspended_intf++) {
list_for_each_entry(as, &chip->pcm_list, list) {
snd_usb_pcm_suspend(as);
@@ -858,6 +880,11 @@ static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message)
snd_usb_mixer_suspend(mixer);
}
+ if (!PMSG_IS_AUTO(message) && !chip->system_suspend) {
+ snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot);
+ chip->system_suspend = chip->num_suspended_intf;
+ }
+
return 0;
}
@@ -871,10 +898,10 @@ static int __usb_audio_resume(struct usb_interface *intf, bool reset_resume)
if (chip == (void *)-1L)
return 0;
- if (--chip->num_suspended_intf)
- return 0;
atomic_inc(&chip->active); /* avoid autopm */
+ if (chip->num_suspended_intf > 1)
+ goto out;
list_for_each_entry(as, &chip->pcm_list, list) {
err = snd_usb_pcm_resume(as);
@@ -896,9 +923,12 @@ static int __usb_audio_resume(struct usb_interface *intf, bool reset_resume)
snd_usbmidi_resume(p);
}
- if (!chip->autosuspended)
+ out:
+ if (chip->num_suspended_intf == chip->system_suspend) {
snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0);
- chip->autosuspended = 0;
+ chip->system_suspend = 0;
+ }
+ chip->num_suspended_intf--;
err_out:
atomic_dec(&chip->active); /* allow autopm after this point */
diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h
index 6d6492195bdc..4ec491011b19 100644
--- a/sound/usb/quirks-table.h
+++ b/sound/usb/quirks-table.h
@@ -40,6 +40,18 @@
.ifnum = QUIRK_NO_INTERFACE \
}
+/* HP Thunderbolt Dock Audio Headset */
+{
+ USB_DEVICE(0x03f0, 0x0269),
+ QUIRK_DEVICE_PROFILE("HP", "Thunderbolt Dock Audio Headset",
+ "HP-Thunderbolt-Dock-Audio-Headset"),
+},
+/* HP Thunderbolt Dock Audio Module */
+{
+ USB_DEVICE(0x03f0, 0x0567),
+ QUIRK_DEVICE_PROFILE("HP", "Thunderbolt Dock Audio Module",
+ "HP-Thunderbolt-Dock-Audio-Module"),
+},
/* FTDI devices */
{
USB_DEVICE(0x0403, 0xb8d8),
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index 1c892c7f14d7..b91c4c0807ec 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -19,14 +19,16 @@
struct media_device;
struct media_intf_devnode;
+#define MAX_CARD_INTERFACES 16
+
struct snd_usb_audio {
int index;
struct usb_device *dev;
struct snd_card *card;
- struct usb_interface *pm_intf;
+ struct usb_interface *intf[MAX_CARD_INTERFACES];
u32 usb_id;
struct mutex mutex;
- unsigned int autosuspended:1;
+ unsigned int system_suspend;
atomic_t active;
atomic_t shutdown;
atomic_t usage_count;