Commit e0154bd4 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'sound-fix-5.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound

Pull sound fixes from Takashi Iwai:
 "Here are last-minute fixes gathered before merge window close; a few
  fixes are for the core while the rest majority are driver fixes.

   - PCM locking annotation fixes and the possible self-lock fix

   - ASoC DPCM regression fixes with multi-CPU DAI

   - A fix for inconsistent resume from system-PM on USB-audio

   - Improved runtime-PM handling with multiple USB interfaces

   - Quirks for HD-audio and USB-audio

   - Hardened firmware handling in max98390 codec

   - A couple of fixes for meson"

* tag 'sound-fix-5.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (21 commits)
  ASoC: rt5645: Add platform-data for Asus T101HA
  ASoC: Intel: bytcr_rt5640: Add quirk for Toshiba Encore WT10-A tablet
  ASoC: SOF: nocodec: conditionally set dpcm_capture/dpcm_playback flags
  ASoC: Intel: boards: replace capture_only by dpcm_capture
  ASoC: core: only convert non DPCM link to DPCM link
  ASoC: soc-pcm: dpcm: fix playback/capture checks
  ASoC: meson: add missing free_irq() in error path
  ALSA: pcm: disallow linking stream to itself
  ALSA: usb-audio: Manage auto-pm of all bundled interfaces
  ALSA: hda/realtek - add a pintbl quirk for several Lenovo machines
  ALSA: pcm: fix snd_pcm_link() lockdep splat
  ALSA: usb-audio: Use the new macro for HP Dock rename quirks
  ALSA: usb-audio: Add vendor, product and profile name for HP Thunderbolt Dock
  ALSA: emu10k1: delete an unnecessary condition
  dt-bindings: ASoc: Fix tdm-slot documentation spelling error
  ASoC: meson: fix memory leak of links if allocation of ldata fails
  ALSA: usb-audio: Fix inconsistent card PM state after resume
  ASoC: max98390: Fix potential crash during param fw loading
  ASoC: max98390: Fix incorrect printf qualifier
  ASoC: fsl-asoc-card: Defer probe when fail to find codec device
  ...
parents d4e181f2 a4f55d92
...@@ -14,8 +14,8 @@ For instance: ...@@ -14,8 +14,8 @@ For instance:
dai-tdm-slot-tx-mask = <0 1>; dai-tdm-slot-tx-mask = <0 1>;
dai-tdm-slot-rx-mask = <1 0>; dai-tdm-slot-rx-mask = <1 0>;
And for each spcified driver, there could be one .of_xlate_tdm_slot_mask() And for each specified 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 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 the default snd_soc_of_xlate_tdm_slot_mask() will be used to generating the
tx and rx masks. tx and rx masks.
......
...@@ -138,6 +138,16 @@ void snd_pcm_stream_lock_irq(struct snd_pcm_substream *substream) ...@@ -138,6 +138,16 @@ void snd_pcm_stream_lock_irq(struct snd_pcm_substream *substream)
} }
EXPORT_SYMBOL_GPL(snd_pcm_stream_lock_irq); 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 * snd_pcm_stream_unlock_irq - Unlock the PCM stream
* @substream: PCM substream * @substream: PCM substream
...@@ -2166,6 +2176,12 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd) ...@@ -2166,6 +2176,12 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
} }
pcm_file = f.file->private_data; pcm_file = f.file->private_data;
substream1 = pcm_file->substream; substream1 = pcm_file->substream;
if (substream == substream1) {
res = -EINVAL;
goto _badf;
}
group = kzalloc(sizeof(*group), GFP_KERNEL); group = kzalloc(sizeof(*group), GFP_KERNEL);
if (!group) { if (!group) {
res = -ENOMEM; res = -ENOMEM;
...@@ -2194,7 +2210,7 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd) ...@@ -2194,7 +2210,7 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
snd_pcm_stream_unlock_irq(substream); snd_pcm_stream_unlock_irq(substream);
snd_pcm_group_lock_irq(target_group, nonatomic); 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); snd_pcm_group_assign(substream1, target_group);
refcount_inc(&target_group->refs); refcount_inc(&target_group->refs);
snd_pcm_stream_unlock(substream1); snd_pcm_stream_unlock(substream1);
...@@ -2210,7 +2226,7 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd) ...@@ -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) 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_group_assign(substream, &substream->self_group);
snd_pcm_stream_unlock(substream); snd_pcm_stream_unlock(substream);
} }
......
...@@ -1040,7 +1040,7 @@ static void snd_emu10k1x_proc_reg_write(struct snd_info_entry *entry, ...@@ -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) if (sscanf(line, "%x %x %x", &reg, &channel_id, &val) != 3)
continue; continue;
if (reg < 0x49 && val <= 0xffffffff && channel_id <= 2) if (reg < 0x49 && channel_id <= 2)
snd_emu10k1x_ptr_write(emu, reg, channel_id, val); snd_emu10k1x_ptr_write(emu, reg, channel_id, val);
} }
} }
......
...@@ -8161,6 +8161,12 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { ...@@ -8161,6 +8161,12 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
ALC225_STANDARD_PINS, ALC225_STANDARD_PINS,
{0x12, 0xb7a60130}, {0x12, 0xb7a60130},
{0x17, 0x90170110}), {0x17, 0x90170110}),
SND_HDA_PIN_QUIRK(0x10ec0623, 0x17aa, "Lenovo", ALC283_FIXUP_HEADSET_MIC,
{0x14, 0x01014010},
{0x17, 0x90170120},
{0x18, 0x02a11030},
{0x19, 0x02a1103f},
{0x21, 0x0221101f}),
{} {}
}; };
......
...@@ -754,6 +754,7 @@ static struct snd_soc_dai_driver max98390_dai[] = { ...@@ -754,6 +754,7 @@ static struct snd_soc_dai_driver max98390_dai[] = {
static int max98390_dsm_init(struct snd_soc_component *component) static int max98390_dsm_init(struct snd_soc_component *component)
{ {
int ret; int ret;
int param_size, param_start_addr;
char filename[128]; char filename[128];
const char *vendor, *product; const char *vendor, *product;
struct max98390_priv *max98390 = struct max98390_priv *max98390 =
...@@ -778,16 +779,31 @@ static int max98390_dsm_init(struct snd_soc_component *component) ...@@ -778,16 +779,31 @@ static int max98390_dsm_init(struct snd_soc_component *component)
} }
dev_dbg(component->dev, dev_dbg(component->dev,
"max98390: param fw size %ld\n", "max98390: param fw size %zd\n",
fw->size); 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; 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; dsm_param += MAX98390_DSM_PAYLOAD_OFFSET;
regmap_bulk_write(max98390->regmap, DSM_EQ_BQ1_B0_BYTE0, regmap_bulk_write(max98390->regmap, param_start_addr,
dsm_param, dsm_param, param_size);
fw->size - MAX98390_DSM_PAYLOAD_OFFSET);
release_firmware(fw);
regmap_write(max98390->regmap, MAX98390_R23E1_DSP_GLOBAL_EN, 0x01); regmap_write(max98390->regmap, MAX98390_R23E1_DSP_GLOBAL_EN, 0x01);
err_alloc:
release_firmware(fw);
err: err:
return ret; return ret;
} }
......
...@@ -650,7 +650,8 @@ ...@@ -650,7 +650,8 @@
/* DSM register offset */ /* DSM register offset */
#define MAX98390_DSM_PAYLOAD_OFFSET 16 #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 max98390_priv {
struct regmap *regmap; struct regmap *regmap;
......
...@@ -80,8 +80,8 @@ int rl6231_calc_dmic_clk(int rate) ...@@ -80,8 +80,8 @@ int rl6231_calc_dmic_clk(int rate)
for (i = 0; i < ARRAY_SIZE(div); i++) { for (i = 0; i < ARRAY_SIZE(div); i++) {
if ((div[i] % 3) == 0) if ((div[i] % 3) == 0)
continue; continue;
/* find divider that gives DMIC frequency below 3.072MHz */ /* find divider that gives DMIC frequency below 1.536MHz */
if (3072000 * div[i] >= rate) if (1536000 * div[i] >= rate)
return i; return i;
} }
......
...@@ -3625,6 +3625,12 @@ static const struct rt5645_platform_data asus_t100ha_platform_data = { ...@@ -3625,6 +3625,12 @@ static const struct rt5645_platform_data asus_t100ha_platform_data = {
.inv_jd1_1 = true, .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 = { static const struct rt5645_platform_data lenovo_ideapad_miix_310_pdata = {
.jd_mode = 3, .jd_mode = 3,
.in2_diff = true, .in2_diff = true,
...@@ -3708,6 +3714,14 @@ static const struct dmi_system_id dmi_platform_data[] = { ...@@ -3708,6 +3714,14 @@ static const struct dmi_system_id dmi_platform_data[] = {
}, },
.driver_data = (void *)&asus_t100ha_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", .ident = "MINIX Z83-4",
.matches = { .matches = {
......
...@@ -581,7 +581,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) ...@@ -581,7 +581,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
if (!fsl_asoc_card_is_ac97(priv) && !codec_dev) { if (!fsl_asoc_card_is_ac97(priv) && !codec_dev) {
dev_err(&pdev->dev, "failed to find codec device\n"); dev_err(&pdev->dev, "failed to find codec device\n");
ret = -EINVAL; ret = -EPROBE_DEFER;
goto asrc_fail; goto asrc_fail;
} }
......
...@@ -754,6 +754,18 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { ...@@ -754,6 +754,18 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = {
BYT_RT5640_JD_NOT_INV | BYT_RT5640_JD_NOT_INV |
BYT_RT5640_MCLK_EN), 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 */ { /* Catch-all for generic Insyde tablets, must be last */
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Insyde"), DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
......
...@@ -407,7 +407,7 @@ static struct snd_soc_dai_link geminilake_dais[] = { ...@@ -407,7 +407,7 @@ static struct snd_soc_dai_link geminilake_dais[] = {
.name = "Glk Audio Echo Reference cap", .name = "Glk Audio Echo Reference cap",
.stream_name = "Echoreference Capture", .stream_name = "Echoreference Capture",
.init = NULL, .init = NULL,
.capture_only = 1, .dpcm_capture = 1,
.nonatomic = 1, .nonatomic = 1,
.dynamic = 1, .dynamic = 1,
SND_SOC_DAILINK_REG(echoref, dummy, platform), SND_SOC_DAILINK_REG(echoref, dummy, platform),
......
...@@ -692,7 +692,7 @@ static struct snd_soc_dai_link kabylake_dais[] = { ...@@ -692,7 +692,7 @@ static struct snd_soc_dai_link kabylake_dais[] = {
.name = "Kbl Audio Echo Reference cap", .name = "Kbl Audio Echo Reference cap",
.stream_name = "Echoreference Capture", .stream_name = "Echoreference Capture",
.init = NULL, .init = NULL,
.capture_only = 1, .dpcm_capture = 1,
.nonatomic = 1, .nonatomic = 1,
SND_SOC_DAILINK_REG(echoref, dummy, platform), SND_SOC_DAILINK_REG(echoref, dummy, platform),
}, },
...@@ -858,7 +858,7 @@ static struct snd_soc_dai_link kabylake_max98_927_373_dais[] = { ...@@ -858,7 +858,7 @@ static struct snd_soc_dai_link kabylake_max98_927_373_dais[] = {
.name = "Kbl Audio Echo Reference cap", .name = "Kbl Audio Echo Reference cap",
.stream_name = "Echoreference Capture", .stream_name = "Echoreference Capture",
.init = NULL, .init = NULL,
.capture_only = 1, .dpcm_capture = 1,
.nonatomic = 1, .nonatomic = 1,
SND_SOC_DAILINK_REG(echoref, dummy, platform), SND_SOC_DAILINK_REG(echoref, dummy, platform),
}, },
......
...@@ -672,7 +672,7 @@ static struct snd_soc_dai_link kabylake_dais[] = { ...@@ -672,7 +672,7 @@ static struct snd_soc_dai_link kabylake_dais[] = {
.name = "Kbl Audio Echo Reference cap", .name = "Kbl Audio Echo Reference cap",
.stream_name = "Echoreference Capture", .stream_name = "Echoreference Capture",
.init = NULL, .init = NULL,
.capture_only = 1, .dpcm_capture = 1,
.nonatomic = 1, .nonatomic = 1,
SND_SOC_DAILINK_REG(echoref, dummy, platform), SND_SOC_DAILINK_REG(echoref, dummy, platform),
}, },
......
...@@ -566,7 +566,7 @@ static struct snd_soc_dai_link kabylake_dais[] = { ...@@ -566,7 +566,7 @@ static struct snd_soc_dai_link kabylake_dais[] = {
.name = "Kbl Audio Echo Reference cap", .name = "Kbl Audio Echo Reference cap",
.stream_name = "Echoreference Capture", .stream_name = "Echoreference Capture",
.init = NULL, .init = NULL,
.capture_only = 1, .dpcm_capture = 1,
.nonatomic = 1, .nonatomic = 1,
SND_SOC_DAILINK_REG(echoref, dummy, platform), SND_SOC_DAILINK_REG(echoref, dummy, platform),
}, },
......
...@@ -249,7 +249,7 @@ int axg_fifo_pcm_open(struct snd_soc_component *component, ...@@ -249,7 +249,7 @@ int axg_fifo_pcm_open(struct snd_soc_component *component,
/* Enable pclk to access registers and clock the fifo ip */ /* Enable pclk to access registers and clock the fifo ip */
ret = clk_prepare_enable(fifo->pclk); ret = clk_prepare_enable(fifo->pclk);
if (ret) if (ret)
return ret; goto free_irq;
/* Setup status2 so it reports the memory pointer */ /* Setup status2 so it reports the memory pointer */
regmap_update_bits(fifo->map, FIFO_CTRL1, regmap_update_bits(fifo->map, FIFO_CTRL1,
...@@ -269,8 +269,14 @@ int axg_fifo_pcm_open(struct snd_soc_component *component, ...@@ -269,8 +269,14 @@ int axg_fifo_pcm_open(struct snd_soc_component *component,
/* Take memory arbitror out of reset */ /* Take memory arbitror out of reset */
ret = reset_control_deassert(fifo->arb); ret = reset_control_deassert(fifo->arb);
if (ret) 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; return ret;
} }
EXPORT_SYMBOL_GPL(axg_fifo_pcm_open); EXPORT_SYMBOL_GPL(axg_fifo_pcm_open);
......
...@@ -49,19 +49,26 @@ int meson_card_reallocate_links(struct snd_soc_card *card, ...@@ -49,19 +49,26 @@ int meson_card_reallocate_links(struct snd_soc_card *card,
links = krealloc(priv->card.dai_link, links = krealloc(priv->card.dai_link,
num_links * sizeof(*priv->card.dai_link), num_links * sizeof(*priv->card.dai_link),
GFP_KERNEL | __GFP_ZERO); GFP_KERNEL | __GFP_ZERO);
if (!links)
goto err_links;
ldata = krealloc(priv->link_data, ldata = krealloc(priv->link_data,
num_links * sizeof(*priv->link_data), num_links * sizeof(*priv->link_data),
GFP_KERNEL | __GFP_ZERO); GFP_KERNEL | __GFP_ZERO);
if (!ldata)
if (!links || !ldata) { goto err_ldata;
dev_err(priv->card.dev, "failed to allocate links\n");
return -ENOMEM;
}
priv->card.dai_link = links; priv->card.dai_link = links;
priv->link_data = ldata; priv->link_data = ldata;
priv->card.num_links = num_links; priv->card.num_links = num_links;
return 0; 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); EXPORT_SYMBOL_GPL(meson_card_reallocate_links);
......
...@@ -1648,9 +1648,25 @@ static void soc_check_tplg_fes(struct snd_soc_card *card) ...@@ -1648,9 +1648,25 @@ static void soc_check_tplg_fes(struct snd_soc_card *card)
dai_link->platforms->name = component->name; dai_link->platforms->name = component->name;
/* convert non BE into BE */ /* convert non BE into BE */
dai_link->no_pcm = 1; if (!dai_link->no_pcm) {
dai_link->dpcm_playback = 1; dai_link->no_pcm = 1;
dai_link->dpcm_capture = 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 * override any BE fixups
......
...@@ -2789,20 +2789,44 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) ...@@ -2789,20 +2789,44 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
struct snd_pcm *pcm; struct snd_pcm *pcm;
char new_name[64]; char new_name[64];
int ret = 0, playback = 0, capture = 0; int ret = 0, playback = 0, capture = 0;
int stream;
int i; 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) { if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm) {
cpu_dai = asoc_rtd_to_cpu(rtd, 0); if (rtd->dai_link->dpcm_playback) {
if (rtd->num_cpus > 1) { stream = SNDRV_PCM_STREAM_PLAYBACK;
dev_err(rtd->dev,
"DPCM doesn't support Multi CPU yet\n"); for_each_rtd_cpu_dais(rtd, i, cpu_dai)
return -EINVAL; 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 { } else {
/* Adapt stream for codec2codec links */ /* Adapt stream for codec2codec links */
int cpu_capture = rtd->dai_link->params ? int cpu_capture = rtd->dai_link->params ?
......
...@@ -52,8 +52,10 @@ static int sof_nocodec_bes_setup(struct device *dev, ...@@ -52,8 +52,10 @@ static int sof_nocodec_bes_setup(struct device *dev,
links[i].platforms->name = dev_name(dev); links[i].platforms->name = dev_name(dev);
links[i].codecs->dai_name = "snd-soc-dummy-dai"; links[i].codecs->dai_name = "snd-soc-dummy-dai";
links[i].codecs->name = "snd-soc-dummy"; links[i].codecs->name = "snd-soc-dummy";
links[i].dpcm_playback = 1; if (ops->drv[i].playback.channels_min)
links[i].dpcm_capture = 1; links[i].dpcm_playback = 1;
if (ops->drv[i].capture.channels_min)
links[i].dpcm_capture = 1;
} }
card->dai_link = links; card->dai_link = links;
......
...@@ -634,7 +634,6 @@ static int usb_audio_probe(struct usb_interface *intf, ...@@ -634,7 +634,6 @@ static int usb_audio_probe(struct usb_interface *intf,
id, &chip); id, &chip);
if (err < 0) if (err < 0)
goto __error; goto __error;
chip->pm_intf = intf;
break; break;
} else if (vid[i] != -1 || pid[i] != -1) { } else if (vid[i] != -1 || pid[i] != -1) {
dev_info(&dev->dev, dev_info(&dev->dev,
...@@ -651,6 +650,13 @@ static int usb_audio_probe(struct usb_interface *intf, ...@@ -651,6 +650,13 @@ static int usb_audio_probe(struct usb_interface *intf,
goto __error; 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); dev_set_drvdata(&dev->dev, chip);
/* /*
...@@ -703,6 +709,7 @@ static int usb_audio_probe(struct usb_interface *intf, ...@@ -703,6 +709,7 @@ static int usb_audio_probe(struct usb_interface *intf,
} }
usb_chip[chip->index] = chip; usb_chip[chip->index] = chip;
chip->intf[chip->num_interfaces] = intf;
chip->num_interfaces++; chip->num_interfaces++;
usb_set_intfdata(intf, chip); usb_set_intfdata(intf, chip);
atomic_dec(&chip->active); atomic_dec(&chip->active);
...@@ -818,19 +825,37 @@ void snd_usb_unlock_shutdown(struct snd_usb_audio *chip) ...@@ -818,19 +825,37 @@ void snd_usb_unlock_shutdown(struct snd_usb_audio *chip)
int snd_usb_autoresume(struct snd_usb_audio *chip) int snd_usb_autoresume(struct snd_usb_audio *chip)
{ {
int i, err;
if (atomic_read(&chip->shutdown)) if (atomic_read(&chip->shutdown))
return -EIO; return -EIO;
if (atomic_inc_return(&chip->active) == 1) if (atomic_inc_return(&chip->active) != 1)
return usb_autopm_get_interface(chip->pm_intf); 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; return 0;
} }
void snd_usb_autosuspend(struct snd_usb_audio *chip) void snd_usb_autosuspend(struct snd_usb_audio *chip)
{ {
int i;
if (atomic_read(&chip->shutdown)) if (atomic_read(&chip->shutdown))
return; return;
if (atomic_dec_and_test(&chip->active)) if (!atomic_dec_and_test(&chip->active))
usb_autopm_put_interface(chip->pm_intf); 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) 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) ...@@ -843,9 +868,6 @@ static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message)
if (chip == (void *)-1L) if (chip == (void *)-1L)
return 0; 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++) { if (!chip->num_suspended_intf++) {
list_for_each_entry(as, &chip->pcm_list, list) { list_for_each_entry(as, &chip->pcm_list, list) {
snd_usb_pcm_suspend(as); snd_usb_pcm_suspend(as);
...@@ -858,6 +880,11 @@ static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message) ...@@ -858,6 +880,11 @@ static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message)
snd_usb_mixer_suspend(mixer); 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; return 0;
} }
...@@ -871,10 +898,10 @@ static int __usb_audio_resume(struct usb_interface *intf, bool reset_resume) ...@@ -871,10 +898,10 @@ static int __usb_audio_resume(struct usb_interface *intf, bool reset_resume)
if (chip == (void *)-1L) if (chip == (void *)-1L)
return 0; return 0;
if (--chip->num_suspended_intf)
return 0;
atomic_inc(&chip->active); /* avoid autopm */ atomic_inc(&chip->active); /* avoid autopm */
if (chip->num_suspended_intf > 1)
goto out;
list_for_each_entry(as, &chip->pcm_list, list) { list_for_each_entry(as, &chip->pcm_list, list) {
err = snd_usb_pcm_resume(as); err = snd_usb_pcm_resume(as);
...@@ -896,9 +923,12 @@ static int __usb_audio_resume(struct usb_interface *intf, bool reset_resume) ...@@ -896,9 +923,12 @@ static int __usb_audio_resume(struct usb_interface *intf, bool reset_resume)
snd_usbmidi_resume(p); 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); snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0);
chip->autosuspended = 0; chip->system_suspend = 0;
}
chip->num_suspended_intf--;
err_out: err_out:
atomic_dec(&chip->active); /* allow autopm after this point */ atomic_dec(&chip->active); /* allow autopm after this point */
......
...@@ -40,6 +40,18 @@ ...@@ -40,6 +40,18 @@
.ifnum = QUIRK_NO_INTERFACE \ .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 */ /* FTDI devices */
{ {
USB_DEVICE(0x0403, 0xb8d8), USB_DEVICE(0x0403, 0xb8d8),
......
...@@ -19,14 +19,16 @@ ...@@ -19,14 +19,16 @@
struct media_device; struct media_device;
struct media_intf_devnode; struct media_intf_devnode;
#define MAX_CARD_INTERFACES 16
struct snd_usb_audio { struct snd_usb_audio {
int index; int index;
struct usb_device *dev; struct usb_device *dev;
struct snd_card *card; struct snd_card *card;
struct usb_interface *pm_intf; struct usb_interface *intf[MAX_CARD_INTERFACES];
u32 usb_id; u32 usb_id;
struct mutex mutex; struct mutex mutex;
unsigned int autosuspended:1; unsigned int system_suspend;
atomic_t active; atomic_t active;
atomic_t shutdown; atomic_t shutdown;
atomic_t usage_count; atomic_t usage_count;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment