Commit 65f0d241 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'sound-5.11-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound

Pull sound fixes from Takashi Iwai:
 "Here are some piled fixes, hopefully the last big one for 5.11.

  All changes are device-specific small fixes, and majority of commits
  are for ASoC while USB-audio got a bit large changes for addressing
  the regression for devices with quirks"

* tag 'sound-5.11-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (31 commits)
  ALSA: hda/hdmi - enable runtime pm for CI AMD display audio
  ALSA: firewire-tascam: Fix integer overflow in midi_port_work()
  ALSA: fireface: Fix integer overflow in transmit_midi_msg()
  ALSA: hda/tegra: fix tegra-hda on tegra30 soc
  clk: tegra30: Add hda clock default rates to clock driver
  ALSA: doc: Fix reference to mixart.rst
  ALSA: usb-audio: Fix implicit feedback sync setup for Pioneer devices
  ALSA: usb-audio: Annotate the endpoint index in audioformat
  ALSA: usb-audio: Avoid unnecessary interface re-setup
  ALSA: usb-audio: Choose audioformat of a counter-part substream
  ALSA: usb-audio: Fix the missing endpoints creations for quirks
  ALSA: hda/realtek: fix right sounds and mute/micmute LEDs for HP machines
  ASoC: AMD Renoir - add DMI entry for Lenovo ThinkPad X395
  ASoC: amd: Replacing MSI with Legacy IRQ model
  ASoC: AMD Renoir - add DMI entry for Lenovo ThinkPad E14 Gen 2
  ASoC: meson: axg-tdm-interface: fix loopback
  ASoC: meson: axg-tdmin: fix axg skew offset
  ASoC: max98373: don't access volatile registers in bias level off
  ASoC: rt711: mutex between calibration and power state changes
  ASoC: Intel: haswell: Add missing pm_ops
  ...
parents e609571b 20c7842e
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
# Copyright (C) 2020 Texas Instruments Incorporated
# Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
%YAML 1.2 %YAML 1.2
--- ---
$id: http://devicetree.org/schemas/sound/ti,j721e-cpb-audio.yaml# $id: http://devicetree.org/schemas/sound/ti,j721e-cpb-audio.yaml#
...@@ -7,7 +9,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# ...@@ -7,7 +9,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Texas Instruments J721e Common Processor Board Audio Support title: Texas Instruments J721e Common Processor Board Audio Support
maintainers: maintainers:
- Peter Ujfalusi <peter.ujfalusi@ti.com> - Peter Ujfalusi <peter.ujfalusi@gmail.com>
description: | description: |
The audio support on the board is using pcm3168a codec connected to McASP10 The audio support on the board is using pcm3168a codec connected to McASP10
......
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
# Copyright (C) 2020 Texas Instruments Incorporated
# Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
%YAML 1.2 %YAML 1.2
--- ---
$id: http://devicetree.org/schemas/sound/ti,j721e-cpb-ivi-audio.yaml# $id: http://devicetree.org/schemas/sound/ti,j721e-cpb-ivi-audio.yaml#
...@@ -7,7 +9,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# ...@@ -7,7 +9,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Texas Instruments J721e Common Processor Board Audio Support title: Texas Instruments J721e Common Processor Board Audio Support
maintainers: maintainers:
- Peter Ujfalusi <peter.ujfalusi@ti.com> - Peter Ujfalusi <peter.ujfalusi@gmail.com>
description: | description: |
The Infotainment board plugs into the Common Processor Board, the support of the The Infotainment board plugs into the Common Processor Board, the support of the
......
...@@ -1501,7 +1501,7 @@ Module for Digigram miXart8 sound cards. ...@@ -1501,7 +1501,7 @@ Module for Digigram miXart8 sound cards.
This module supports multiple cards. This module supports multiple cards.
Note: One miXart8 board will be represented as 4 alsa cards. Note: One miXart8 board will be represented as 4 alsa cards.
See MIXART.txt for details. See Documentation/sound/cards/mixart.rst for details.
When the driver is compiled as a module and the hotplug firmware When the driver is compiled as a module and the hotplug firmware
is supported, the firmware data is loaded via hotplug automatically. is supported, the firmware data is loaded via hotplug automatically.
......
...@@ -12848,7 +12848,7 @@ F: include/misc/ocxl* ...@@ -12848,7 +12848,7 @@ F: include/misc/ocxl*
F: include/uapi/misc/ocxl.h F: include/uapi/misc/ocxl.h
OMAP AUDIO SUPPORT OMAP AUDIO SUPPORT
M: Peter Ujfalusi <peter.ujfalusi@ti.com> M: Peter Ujfalusi <peter.ujfalusi@gmail.com>
M: Jarkko Nikula <jarkko.nikula@bitmer.com> M: Jarkko Nikula <jarkko.nikula@bitmer.com>
L: alsa-devel@alsa-project.org (moderated for non-subscribers) L: alsa-devel@alsa-project.org (moderated for non-subscribers)
L: linux-omap@vger.kernel.org L: linux-omap@vger.kernel.org
...@@ -17541,7 +17541,7 @@ F: arch/xtensa/ ...@@ -17541,7 +17541,7 @@ F: arch/xtensa/
F: drivers/irqchip/irq-xtensa-* F: drivers/irqchip/irq-xtensa-*
TEXAS INSTRUMENTS ASoC DRIVERS TEXAS INSTRUMENTS ASoC DRIVERS
M: Peter Ujfalusi <peter.ujfalusi@ti.com> M: Peter Ujfalusi <peter.ujfalusi@gmail.com>
L: alsa-devel@alsa-project.org (moderated for non-subscribers) L: alsa-devel@alsa-project.org (moderated for non-subscribers)
S: Maintained S: Maintained
F: sound/soc/ti/ F: sound/soc/ti/
...@@ -17851,7 +17851,7 @@ F: Documentation/devicetree/bindings/net/nfc/trf7970a.txt ...@@ -17851,7 +17851,7 @@ F: Documentation/devicetree/bindings/net/nfc/trf7970a.txt
F: drivers/nfc/trf7970a.c F: drivers/nfc/trf7970a.c
TI TWL4030 SERIES SOC CODEC DRIVER TI TWL4030 SERIES SOC CODEC DRIVER
M: Peter Ujfalusi <peter.ujfalusi@ti.com> M: Peter Ujfalusi <peter.ujfalusi@gmail.com>
L: alsa-devel@alsa-project.org (moderated for non-subscribers) L: alsa-devel@alsa-project.org (moderated for non-subscribers)
S: Maintained S: Maintained
F: sound/soc/codecs/twl4030* F: sound/soc/codecs/twl4030*
......
...@@ -1256,6 +1256,8 @@ static struct tegra_clk_init_table init_table[] __initdata = { ...@@ -1256,6 +1256,8 @@ static struct tegra_clk_init_table init_table[] __initdata = {
{ TEGRA30_CLK_I2S3_SYNC, TEGRA30_CLK_CLK_MAX, 24000000, 0 }, { TEGRA30_CLK_I2S3_SYNC, TEGRA30_CLK_CLK_MAX, 24000000, 0 },
{ TEGRA30_CLK_I2S4_SYNC, TEGRA30_CLK_CLK_MAX, 24000000, 0 }, { TEGRA30_CLK_I2S4_SYNC, TEGRA30_CLK_CLK_MAX, 24000000, 0 },
{ TEGRA30_CLK_VIMCLK_SYNC, TEGRA30_CLK_CLK_MAX, 24000000, 0 }, { TEGRA30_CLK_VIMCLK_SYNC, TEGRA30_CLK_CLK_MAX, 24000000, 0 },
{ TEGRA30_CLK_HDA, TEGRA30_CLK_PLL_P, 102000000, 0 },
{ TEGRA30_CLK_HDA2CODEC_2X, TEGRA30_CLK_PLL_P, 48000000, 0 },
/* must be the last entry */ /* must be the last entry */
{ TEGRA30_CLK_CLK_MAX, TEGRA30_CLK_CLK_MAX, 0, 0 }, { TEGRA30_CLK_CLK_MAX, TEGRA30_CLK_CLK_MAX, 0, 0 },
}; };
......
...@@ -88,7 +88,7 @@ static void transmit_midi_msg(struct snd_ff *ff, unsigned int port) ...@@ -88,7 +88,7 @@ static void transmit_midi_msg(struct snd_ff *ff, unsigned int port)
/* Set interval to next transaction. */ /* Set interval to next transaction. */
ff->next_ktime[port] = ktime_add_ns(ktime_get(), ff->next_ktime[port] = ktime_add_ns(ktime_get(),
ff->rx_bytes[port] * 8 * NSEC_PER_SEC / 31250); ff->rx_bytes[port] * 8 * (NSEC_PER_SEC / 31250));
if (quad_count == 1) if (quad_count == 1)
tcode = TCODE_WRITE_QUADLET_REQUEST; tcode = TCODE_WRITE_QUADLET_REQUEST;
......
...@@ -209,7 +209,7 @@ static void midi_port_work(struct work_struct *work) ...@@ -209,7 +209,7 @@ static void midi_port_work(struct work_struct *work)
/* Set interval to next transaction. */ /* Set interval to next transaction. */
port->next_ktime = ktime_add_ns(ktime_get(), port->next_ktime = ktime_add_ns(ktime_get(),
port->consume_bytes * 8 * NSEC_PER_SEC / 31250); port->consume_bytes * 8 * (NSEC_PER_SEC / 31250));
/* Start this transaction. */ /* Start this transaction. */
port->idling = false; port->idling = false;
......
...@@ -2598,7 +2598,8 @@ static const struct pci_device_id azx_ids[] = { ...@@ -2598,7 +2598,8 @@ static const struct pci_device_id azx_ids[] = {
.driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_AMD_SB }, .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_AMD_SB },
/* ATI HDMI */ /* ATI HDMI */
{ PCI_DEVICE(0x1002, 0x0002), { PCI_DEVICE(0x1002, 0x0002),
.driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS }, .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
AZX_DCAPS_PM_RUNTIME },
{ PCI_DEVICE(0x1002, 0x1308), { PCI_DEVICE(0x1002, 0x1308),
.driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS }, .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
{ PCI_DEVICE(0x1002, 0x157a), { PCI_DEVICE(0x1002, 0x157a),
...@@ -2660,9 +2661,11 @@ static const struct pci_device_id azx_ids[] = { ...@@ -2660,9 +2661,11 @@ static const struct pci_device_id azx_ids[] = {
{ PCI_DEVICE(0x1002, 0xaab0), { PCI_DEVICE(0x1002, 0xaab0),
.driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS }, .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
{ PCI_DEVICE(0x1002, 0xaac0), { PCI_DEVICE(0x1002, 0xaac0),
.driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS }, .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
AZX_DCAPS_PM_RUNTIME },
{ PCI_DEVICE(0x1002, 0xaac8), { PCI_DEVICE(0x1002, 0xaac8),
.driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS }, .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
AZX_DCAPS_PM_RUNTIME },
{ PCI_DEVICE(0x1002, 0xaad8), { PCI_DEVICE(0x1002, 0xaad8),
.driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS | .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
AZX_DCAPS_PM_RUNTIME }, AZX_DCAPS_PM_RUNTIME },
......
...@@ -388,7 +388,7 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev) ...@@ -388,7 +388,7 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev)
* in powers of 2, next available ratio is 16 which can be * in powers of 2, next available ratio is 16 which can be
* used as a limiting factor here. * used as a limiting factor here.
*/ */
if (of_device_is_compatible(np, "nvidia,tegra194-hda")) if (of_device_is_compatible(np, "nvidia,tegra30-hda"))
chip->bus.core.sdo_limit = 16; chip->bus.core.sdo_limit = 16;
/* codec detection */ /* codec detection */
......
...@@ -7970,6 +7970,10 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { ...@@ -7970,6 +7970,10 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8760, "HP", ALC285_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x8760, "HP", ALC285_FIXUP_HP_MUTE_LED),
SND_PCI_QUIRK(0x103c, 0x877a, "HP", ALC285_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x877a, "HP", ALC285_FIXUP_HP_MUTE_LED),
SND_PCI_QUIRK(0x103c, 0x877d, "HP", ALC236_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x877d, "HP", ALC236_FIXUP_HP_MUTE_LED),
SND_PCI_QUIRK(0x103c, 0x8780, "HP ZBook Fury 17 G7 Mobile Workstation",
ALC285_FIXUP_HP_GPIO_AMP_INIT),
SND_PCI_QUIRK(0x103c, 0x8783, "HP ZBook Fury 15 G7 Mobile Workstation",
ALC285_FIXUP_HP_GPIO_AMP_INIT),
SND_PCI_QUIRK(0x103c, 0x87c8, "HP", ALC287_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x87c8, "HP", ALC287_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x87f4, "HP", ALC287_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x87f4, "HP", ALC287_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x87f5, "HP", ALC287_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x87f5, "HP", ALC287_FIXUP_HP_GPIO_LED),
......
...@@ -140,21 +140,14 @@ static int snd_acp3x_probe(struct pci_dev *pci, ...@@ -140,21 +140,14 @@ static int snd_acp3x_probe(struct pci_dev *pci,
goto release_regions; goto release_regions;
} }
/* check for msi interrupt support */ irqflags = IRQF_SHARED;
ret = pci_enable_msi(pci);
if (ret)
/* msi is not enabled */
irqflags = IRQF_SHARED;
else
/* msi is enabled */
irqflags = 0;
addr = pci_resource_start(pci, 0); addr = pci_resource_start(pci, 0);
adata->acp3x_base = devm_ioremap(&pci->dev, addr, adata->acp3x_base = devm_ioremap(&pci->dev, addr,
pci_resource_len(pci, 0)); pci_resource_len(pci, 0));
if (!adata->acp3x_base) { if (!adata->acp3x_base) {
ret = -ENOMEM; ret = -ENOMEM;
goto disable_msi; goto release_regions;
} }
pci_set_master(pci); pci_set_master(pci);
pci_set_drvdata(pci, adata); pci_set_drvdata(pci, adata);
...@@ -162,7 +155,7 @@ static int snd_acp3x_probe(struct pci_dev *pci, ...@@ -162,7 +155,7 @@ static int snd_acp3x_probe(struct pci_dev *pci,
adata->pme_en = rv_readl(adata->acp3x_base + mmACP_PME_EN); adata->pme_en = rv_readl(adata->acp3x_base + mmACP_PME_EN);
ret = acp3x_init(adata); ret = acp3x_init(adata);
if (ret) if (ret)
goto disable_msi; goto release_regions;
val = rv_readl(adata->acp3x_base + mmACP_I2S_PIN_CONFIG); val = rv_readl(adata->acp3x_base + mmACP_I2S_PIN_CONFIG);
switch (val) { switch (val) {
...@@ -251,8 +244,6 @@ static int snd_acp3x_probe(struct pci_dev *pci, ...@@ -251,8 +244,6 @@ static int snd_acp3x_probe(struct pci_dev *pci,
de_init: de_init:
if (acp3x_deinit(adata->acp3x_base)) if (acp3x_deinit(adata->acp3x_base))
dev_err(&pci->dev, "ACP de-init failed\n"); dev_err(&pci->dev, "ACP de-init failed\n");
disable_msi:
pci_disable_msi(pci);
release_regions: release_regions:
pci_release_regions(pci); pci_release_regions(pci);
disable_pci: disable_pci:
...@@ -311,7 +302,6 @@ static void snd_acp3x_remove(struct pci_dev *pci) ...@@ -311,7 +302,6 @@ static void snd_acp3x_remove(struct pci_dev *pci)
dev_err(&pci->dev, "ACP de-init failed\n"); dev_err(&pci->dev, "ACP de-init failed\n");
pm_runtime_forbid(&pci->dev); pm_runtime_forbid(&pci->dev);
pm_runtime_get_noresume(&pci->dev); pm_runtime_get_noresume(&pci->dev);
pci_disable_msi(pci);
pci_release_regions(pci); pci_release_regions(pci);
pci_disable_device(pci); pci_disable_device(pci);
} }
......
...@@ -171,6 +171,20 @@ static const struct dmi_system_id rn_acp_quirk_table[] = { ...@@ -171,6 +171,20 @@ static const struct dmi_system_id rn_acp_quirk_table[] = {
DMI_EXACT_MATCH(DMI_BOARD_NAME, "LNVNB161216"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "LNVNB161216"),
} }
}, },
{
/* Lenovo ThinkPad E14 Gen 2 */
.matches = {
DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_EXACT_MATCH(DMI_BOARD_NAME, "20T6CTO1WW"),
}
},
{
/* Lenovo ThinkPad X395 */
.matches = {
DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_EXACT_MATCH(DMI_BOARD_NAME, "20NLCTO1WW"),
}
},
{} {}
}; };
......
...@@ -143,7 +143,7 @@ config SND_MCHP_SOC_SPDIFTX ...@@ -143,7 +143,7 @@ config SND_MCHP_SOC_SPDIFTX
- sama7g5 - sama7g5
This S/PDIF TX driver is compliant with IEC-60958 standard and This S/PDIF TX driver is compliant with IEC-60958 standard and
includes programable User Data and Channel Status fields. includes programmable User Data and Channel Status fields.
config SND_MCHP_SOC_SPDIFRX config SND_MCHP_SOC_SPDIFRX
tristate "Microchip ASoC driver for boards using S/PDIF RX" tristate "Microchip ASoC driver for boards using S/PDIF RX"
...@@ -157,5 +157,5 @@ config SND_MCHP_SOC_SPDIFRX ...@@ -157,5 +157,5 @@ config SND_MCHP_SOC_SPDIFRX
- sama7g5 - sama7g5
This S/PDIF RX driver is compliant with IEC-60958 standard and This S/PDIF RX driver is compliant with IEC-60958 standard and
includes programable User Data and Channel Status fields. includes programmable User Data and Channel Status fields.
endif endif
...@@ -457,7 +457,7 @@ config SND_SOC_ADAU7118_HW ...@@ -457,7 +457,7 @@ config SND_SOC_ADAU7118_HW
help help
Enable support for the Analog Devices ADAU7118 8 Channel PDM-to-I2S/TDM Enable support for the Analog Devices ADAU7118 8 Channel PDM-to-I2S/TDM
Converter. In this mode, the device works in standalone mode which Converter. In this mode, the device works in standalone mode which
means that there is no bus to comunicate with it. Stereo mode is not means that there is no bus to communicate with it. Stereo mode is not
supported in this mode. supported in this mode.
To compile this driver as a module, choose M here: the module To compile this driver as a module, choose M here: the module
......
...@@ -19,6 +19,12 @@ ...@@ -19,6 +19,12 @@
#include <sound/tlv.h> #include <sound/tlv.h>
#include "max98373.h" #include "max98373.h"
static const u32 max98373_i2c_cache_reg[] = {
MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK,
MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK,
MAX98373_R20B6_BDE_CUR_STATE_READBACK,
};
static struct reg_default max98373_reg[] = { static struct reg_default max98373_reg[] = {
{MAX98373_R2000_SW_RESET, 0x00}, {MAX98373_R2000_SW_RESET, 0x00},
{MAX98373_R2001_INT_RAW1, 0x00}, {MAX98373_R2001_INT_RAW1, 0x00},
...@@ -472,6 +478,11 @@ static struct snd_soc_dai_driver max98373_dai[] = { ...@@ -472,6 +478,11 @@ static struct snd_soc_dai_driver max98373_dai[] = {
static int max98373_suspend(struct device *dev) static int max98373_suspend(struct device *dev)
{ {
struct max98373_priv *max98373 = dev_get_drvdata(dev); struct max98373_priv *max98373 = dev_get_drvdata(dev);
int i;
/* cache feedback register values before suspend */
for (i = 0; i < max98373->cache_num; i++)
regmap_read(max98373->regmap, max98373->cache[i].reg, &max98373->cache[i].val);
regcache_cache_only(max98373->regmap, true); regcache_cache_only(max98373->regmap, true);
regcache_mark_dirty(max98373->regmap); regcache_mark_dirty(max98373->regmap);
...@@ -509,6 +520,7 @@ static int max98373_i2c_probe(struct i2c_client *i2c, ...@@ -509,6 +520,7 @@ static int max98373_i2c_probe(struct i2c_client *i2c,
{ {
int ret = 0; int ret = 0;
int reg = 0; int reg = 0;
int i;
struct max98373_priv *max98373 = NULL; struct max98373_priv *max98373 = NULL;
max98373 = devm_kzalloc(&i2c->dev, sizeof(*max98373), GFP_KERNEL); max98373 = devm_kzalloc(&i2c->dev, sizeof(*max98373), GFP_KERNEL);
...@@ -534,6 +546,14 @@ static int max98373_i2c_probe(struct i2c_client *i2c, ...@@ -534,6 +546,14 @@ static int max98373_i2c_probe(struct i2c_client *i2c,
return ret; return ret;
} }
max98373->cache_num = ARRAY_SIZE(max98373_i2c_cache_reg);
max98373->cache = devm_kcalloc(&i2c->dev, max98373->cache_num,
sizeof(*max98373->cache),
GFP_KERNEL);
for (i = 0; i < max98373->cache_num; i++)
max98373->cache[i].reg = max98373_i2c_cache_reg[i];
/* voltage/current slot & gpio configuration */ /* voltage/current slot & gpio configuration */
max98373_slot_config(&i2c->dev, max98373); max98373_slot_config(&i2c->dev, max98373);
......
...@@ -23,6 +23,12 @@ struct sdw_stream_data { ...@@ -23,6 +23,12 @@ struct sdw_stream_data {
struct sdw_stream_runtime *sdw_stream; struct sdw_stream_runtime *sdw_stream;
}; };
static const u32 max98373_sdw_cache_reg[] = {
MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK,
MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK,
MAX98373_R20B6_BDE_CUR_STATE_READBACK,
};
static struct reg_default max98373_reg[] = { static struct reg_default max98373_reg[] = {
{MAX98373_R0040_SCP_INIT_STAT_1, 0x00}, {MAX98373_R0040_SCP_INIT_STAT_1, 0x00},
{MAX98373_R0041_SCP_INIT_MASK_1, 0x00}, {MAX98373_R0041_SCP_INIT_MASK_1, 0x00},
...@@ -245,6 +251,11 @@ static const struct regmap_config max98373_sdw_regmap = { ...@@ -245,6 +251,11 @@ static const struct regmap_config max98373_sdw_regmap = {
static __maybe_unused int max98373_suspend(struct device *dev) static __maybe_unused int max98373_suspend(struct device *dev)
{ {
struct max98373_priv *max98373 = dev_get_drvdata(dev); struct max98373_priv *max98373 = dev_get_drvdata(dev);
int i;
/* cache feedback register values before suspend */
for (i = 0; i < max98373->cache_num; i++)
regmap_read(max98373->regmap, max98373->cache[i].reg, &max98373->cache[i].val);
regcache_cache_only(max98373->regmap, true); regcache_cache_only(max98373->regmap, true);
...@@ -757,6 +768,7 @@ static int max98373_init(struct sdw_slave *slave, struct regmap *regmap) ...@@ -757,6 +768,7 @@ static int max98373_init(struct sdw_slave *slave, struct regmap *regmap)
{ {
struct max98373_priv *max98373; struct max98373_priv *max98373;
int ret; int ret;
int i;
struct device *dev = &slave->dev; struct device *dev = &slave->dev;
/* Allocate and assign private driver data structure */ /* Allocate and assign private driver data structure */
...@@ -768,6 +780,14 @@ static int max98373_init(struct sdw_slave *slave, struct regmap *regmap) ...@@ -768,6 +780,14 @@ static int max98373_init(struct sdw_slave *slave, struct regmap *regmap)
max98373->regmap = regmap; max98373->regmap = regmap;
max98373->slave = slave; max98373->slave = slave;
max98373->cache_num = ARRAY_SIZE(max98373_sdw_cache_reg);
max98373->cache = devm_kcalloc(dev, max98373->cache_num,
sizeof(*max98373->cache),
GFP_KERNEL);
for (i = 0; i < max98373->cache_num; i++)
max98373->cache[i].reg = max98373_sdw_cache_reg[i];
/* Read voltage and slot configuration */ /* Read voltage and slot configuration */
max98373_slot_config(dev, max98373); max98373_slot_config(dev, max98373);
......
...@@ -168,6 +168,31 @@ static SOC_ENUM_SINGLE_DECL(max98373_adc_samplerate_enum, ...@@ -168,6 +168,31 @@ static SOC_ENUM_SINGLE_DECL(max98373_adc_samplerate_enum,
MAX98373_R2051_MEAS_ADC_SAMPLING_RATE, 0, MAX98373_R2051_MEAS_ADC_SAMPLING_RATE, 0,
max98373_ADC_samplerate_text); max98373_ADC_samplerate_text);
static int max98373_feedback_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
struct max98373_priv *max98373 = snd_soc_component_get_drvdata(component);
int i;
if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
/*
* Register values will be cached before suspend. The cached value
* will be a valid value and userspace will happy with that.
*/
for (i = 0; i < max98373->cache_num; i++) {
if (mc->reg == max98373->cache[i].reg) {
ucontrol->value.integer.value[0] = max98373->cache[i].val;
return 0;
}
}
}
return snd_soc_put_volsw(kcontrol, ucontrol);
}
static const struct snd_kcontrol_new max98373_snd_controls[] = { static const struct snd_kcontrol_new max98373_snd_controls[] = {
SOC_SINGLE("Digital Vol Sel Switch", MAX98373_R203F_AMP_DSP_CFG, SOC_SINGLE("Digital Vol Sel Switch", MAX98373_R203F_AMP_DSP_CFG,
MAX98373_AMP_VOL_SEL_SHIFT, 1, 0), MAX98373_AMP_VOL_SEL_SHIFT, 1, 0),
...@@ -209,8 +234,10 @@ SOC_SINGLE("ADC PVDD FLT Switch", MAX98373_R2052_MEAS_ADC_PVDD_FLT_CFG, ...@@ -209,8 +234,10 @@ SOC_SINGLE("ADC PVDD FLT Switch", MAX98373_R2052_MEAS_ADC_PVDD_FLT_CFG,
MAX98373_FLT_EN_SHIFT, 1, 0), MAX98373_FLT_EN_SHIFT, 1, 0),
SOC_SINGLE("ADC TEMP FLT Switch", MAX98373_R2053_MEAS_ADC_THERM_FLT_CFG, SOC_SINGLE("ADC TEMP FLT Switch", MAX98373_R2053_MEAS_ADC_THERM_FLT_CFG,
MAX98373_FLT_EN_SHIFT, 1, 0), MAX98373_FLT_EN_SHIFT, 1, 0),
SOC_SINGLE("ADC PVDD", MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK, 0, 0xFF, 0), SOC_SINGLE_EXT("ADC PVDD", MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK, 0, 0xFF, 0,
SOC_SINGLE("ADC TEMP", MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK, 0, 0xFF, 0), max98373_feedback_get, NULL),
SOC_SINGLE_EXT("ADC TEMP", MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK, 0, 0xFF, 0,
max98373_feedback_get, NULL),
SOC_SINGLE("ADC PVDD FLT Coeff", MAX98373_R2052_MEAS_ADC_PVDD_FLT_CFG, SOC_SINGLE("ADC PVDD FLT Coeff", MAX98373_R2052_MEAS_ADC_PVDD_FLT_CFG,
0, 0x3, 0), 0, 0x3, 0),
SOC_SINGLE("ADC TEMP FLT Coeff", MAX98373_R2053_MEAS_ADC_THERM_FLT_CFG, SOC_SINGLE("ADC TEMP FLT Coeff", MAX98373_R2053_MEAS_ADC_THERM_FLT_CFG,
...@@ -226,7 +253,8 @@ SOC_SINGLE("BDE LVL1 Thresh", MAX98373_R2097_BDE_L1_THRESH, 0, 0xFF, 0), ...@@ -226,7 +253,8 @@ SOC_SINGLE("BDE LVL1 Thresh", MAX98373_R2097_BDE_L1_THRESH, 0, 0xFF, 0),
SOC_SINGLE("BDE LVL2 Thresh", MAX98373_R2098_BDE_L2_THRESH, 0, 0xFF, 0), SOC_SINGLE("BDE LVL2 Thresh", MAX98373_R2098_BDE_L2_THRESH, 0, 0xFF, 0),
SOC_SINGLE("BDE LVL3 Thresh", MAX98373_R2099_BDE_L3_THRESH, 0, 0xFF, 0), SOC_SINGLE("BDE LVL3 Thresh", MAX98373_R2099_BDE_L3_THRESH, 0, 0xFF, 0),
SOC_SINGLE("BDE LVL4 Thresh", MAX98373_R209A_BDE_L4_THRESH, 0, 0xFF, 0), SOC_SINGLE("BDE LVL4 Thresh", MAX98373_R209A_BDE_L4_THRESH, 0, 0xFF, 0),
SOC_SINGLE("BDE Active Level", MAX98373_R20B6_BDE_CUR_STATE_READBACK, 0, 8, 0), SOC_SINGLE_EXT("BDE Active Level", MAX98373_R20B6_BDE_CUR_STATE_READBACK, 0, 8, 0,
max98373_feedback_get, NULL),
SOC_SINGLE("BDE Clip Mode Switch", MAX98373_R2092_BDE_CLIPPER_MODE, 0, 1, 0), SOC_SINGLE("BDE Clip Mode Switch", MAX98373_R2092_BDE_CLIPPER_MODE, 0, 1, 0),
SOC_SINGLE("BDE Thresh Hysteresis", MAX98373_R209B_BDE_THRESH_HYST, 0, 0xFF, 0), SOC_SINGLE("BDE Thresh Hysteresis", MAX98373_R209B_BDE_THRESH_HYST, 0, 0xFF, 0),
SOC_SINGLE("BDE Hold Time", MAX98373_R2090_BDE_LVL_HOLD, 0, 0xFF, 0), SOC_SINGLE("BDE Hold Time", MAX98373_R2090_BDE_LVL_HOLD, 0, 0xFF, 0),
......
...@@ -203,6 +203,11 @@ ...@@ -203,6 +203,11 @@
/* MAX98373_R2000_SW_RESET */ /* MAX98373_R2000_SW_RESET */
#define MAX98373_SOFT_RESET (0x1 << 0) #define MAX98373_SOFT_RESET (0x1 << 0)
struct max98373_cache {
u32 reg;
u32 val;
};
struct max98373_priv { struct max98373_priv {
struct regmap *regmap; struct regmap *regmap;
int reset_gpio; int reset_gpio;
...@@ -212,6 +217,9 @@ struct max98373_priv { ...@@ -212,6 +217,9 @@ struct max98373_priv {
bool interleave_mode; bool interleave_mode;
unsigned int ch_size; unsigned int ch_size;
bool tdm_mode; bool tdm_mode;
/* cache for reading a valid fake feedback value */
struct max98373_cache *cache;
int cache_num;
/* variables to support soundwire */ /* variables to support soundwire */
struct sdw_slave *slave; struct sdw_slave *slave;
bool hw_init; bool hw_init;
......
...@@ -462,6 +462,8 @@ static int rt711_set_amp_gain_put(struct snd_kcontrol *kcontrol, ...@@ -462,6 +462,8 @@ static int rt711_set_amp_gain_put(struct snd_kcontrol *kcontrol,
unsigned int read_ll, read_rl; unsigned int read_ll, read_rl;
int i; int i;
mutex_lock(&rt711->calibrate_mutex);
/* Can't use update bit function, so read the original value first */ /* Can't use update bit function, so read the original value first */
addr_h = mc->reg; addr_h = mc->reg;
addr_l = mc->rreg; addr_l = mc->rreg;
...@@ -547,6 +549,8 @@ static int rt711_set_amp_gain_put(struct snd_kcontrol *kcontrol, ...@@ -547,6 +549,8 @@ static int rt711_set_amp_gain_put(struct snd_kcontrol *kcontrol,
if (dapm->bias_level <= SND_SOC_BIAS_STANDBY) if (dapm->bias_level <= SND_SOC_BIAS_STANDBY)
regmap_write(rt711->regmap, regmap_write(rt711->regmap,
RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3);
mutex_unlock(&rt711->calibrate_mutex);
return 0; return 0;
} }
...@@ -859,9 +863,11 @@ static int rt711_set_bias_level(struct snd_soc_component *component, ...@@ -859,9 +863,11 @@ static int rt711_set_bias_level(struct snd_soc_component *component,
break; break;
case SND_SOC_BIAS_STANDBY: case SND_SOC_BIAS_STANDBY:
mutex_lock(&rt711->calibrate_mutex);
regmap_write(rt711->regmap, regmap_write(rt711->regmap,
RT711_SET_AUDIO_POWER_STATE, RT711_SET_AUDIO_POWER_STATE,
AC_PWRST_D3); AC_PWRST_D3);
mutex_unlock(&rt711->calibrate_mutex);
break; break;
default: default:
......
...@@ -164,6 +164,7 @@ static int imx_hdmi_probe(struct platform_device *pdev) ...@@ -164,6 +164,7 @@ static int imx_hdmi_probe(struct platform_device *pdev)
if ((hdmi_out && hdmi_in) || (!hdmi_out && !hdmi_in)) { if ((hdmi_out && hdmi_in) || (!hdmi_out && !hdmi_in)) {
dev_err(&pdev->dev, "Invalid HDMI DAI link\n"); dev_err(&pdev->dev, "Invalid HDMI DAI link\n");
ret = -EINVAL;
goto fail; goto fail;
} }
......
...@@ -189,6 +189,7 @@ static struct platform_driver haswell_audio = { ...@@ -189,6 +189,7 @@ static struct platform_driver haswell_audio = {
.probe = haswell_audio_probe, .probe = haswell_audio_probe,
.driver = { .driver = {
.name = "haswell-audio", .name = "haswell-audio",
.pm = &snd_soc_pm_ops,
}, },
}; };
......
...@@ -224,6 +224,7 @@ static int cnl_set_dsp_D0(struct sst_dsp *ctx, unsigned int core_id) ...@@ -224,6 +224,7 @@ static int cnl_set_dsp_D0(struct sst_dsp *ctx, unsigned int core_id)
"dsp boot timeout, status=%#x error=%#x\n", "dsp boot timeout, status=%#x error=%#x\n",
sst_dsp_shim_read(ctx, CNL_ADSP_FW_STATUS), sst_dsp_shim_read(ctx, CNL_ADSP_FW_STATUS),
sst_dsp_shim_read(ctx, CNL_ADSP_ERROR_CODE)); sst_dsp_shim_read(ctx, CNL_ADSP_ERROR_CODE));
ret = -ETIMEDOUT;
goto err; goto err;
} }
} else { } else {
......
...@@ -467,8 +467,20 @@ static int axg_tdm_iface_set_bias_level(struct snd_soc_component *component, ...@@ -467,8 +467,20 @@ static int axg_tdm_iface_set_bias_level(struct snd_soc_component *component,
return ret; return ret;
} }
static const struct snd_soc_dapm_widget axg_tdm_iface_dapm_widgets[] = {
SND_SOC_DAPM_SIGGEN("Playback Signal"),
};
static const struct snd_soc_dapm_route axg_tdm_iface_dapm_routes[] = {
{ "Loopback", NULL, "Playback Signal" },
};
static const struct snd_soc_component_driver axg_tdm_iface_component_drv = { static const struct snd_soc_component_driver axg_tdm_iface_component_drv = {
.set_bias_level = axg_tdm_iface_set_bias_level, .dapm_widgets = axg_tdm_iface_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(axg_tdm_iface_dapm_widgets),
.dapm_routes = axg_tdm_iface_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(axg_tdm_iface_dapm_routes),
.set_bias_level = axg_tdm_iface_set_bias_level,
}; };
static const struct of_device_id axg_tdm_iface_of_match[] = { static const struct of_device_id axg_tdm_iface_of_match[] = {
......
...@@ -224,15 +224,6 @@ static const struct axg_tdm_formatter_ops axg_tdmin_ops = { ...@@ -224,15 +224,6 @@ static const struct axg_tdm_formatter_ops axg_tdmin_ops = {
}; };
static const struct axg_tdm_formatter_driver axg_tdmin_drv = { static const struct axg_tdm_formatter_driver axg_tdmin_drv = {
.component_drv = &axg_tdmin_component_drv,
.regmap_cfg = &axg_tdmin_regmap_cfg,
.ops = &axg_tdmin_ops,
.quirks = &(const struct axg_tdm_formatter_hw) {
.skew_offset = 2,
},
};
static const struct axg_tdm_formatter_driver g12a_tdmin_drv = {
.component_drv = &axg_tdmin_component_drv, .component_drv = &axg_tdmin_component_drv,
.regmap_cfg = &axg_tdmin_regmap_cfg, .regmap_cfg = &axg_tdmin_regmap_cfg,
.ops = &axg_tdmin_ops, .ops = &axg_tdmin_ops,
...@@ -247,10 +238,10 @@ static const struct of_device_id axg_tdmin_of_match[] = { ...@@ -247,10 +238,10 @@ static const struct of_device_id axg_tdmin_of_match[] = {
.data = &axg_tdmin_drv, .data = &axg_tdmin_drv,
}, { }, {
.compatible = "amlogic,g12a-tdmin", .compatible = "amlogic,g12a-tdmin",
.data = &g12a_tdmin_drv, .data = &axg_tdmin_drv,
}, { }, {
.compatible = "amlogic,sm1-tdmin", .compatible = "amlogic,sm1-tdmin",
.data = &g12a_tdmin_drv, .data = &axg_tdmin_drv,
}, {} }, {}
}; };
MODULE_DEVICE_TABLE(of, axg_tdmin_of_match); MODULE_DEVICE_TABLE(of, axg_tdmin_of_match);
......
...@@ -270,18 +270,6 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream, ...@@ -270,18 +270,6 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
struct lpaif_i2sctl *i2sctl = drvdata->i2sctl; struct lpaif_i2sctl *i2sctl = drvdata->i2sctl;
unsigned int id = dai->driver->id; unsigned int id = dai->driver->id;
int ret = -EINVAL; int ret = -EINVAL;
unsigned int val = 0;
ret = regmap_read(drvdata->lpaif_map,
LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), &val);
if (ret) {
dev_err(dai->dev, "error reading from i2sctl reg: %d\n", ret);
return ret;
}
if (val == LPAIF_I2SCTL_RESET_STATE) {
dev_err(dai->dev, "error in i2sctl register state\n");
return -ENOTRECOVERABLE;
}
switch (cmd) { switch (cmd) {
case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_START:
...@@ -454,20 +442,16 @@ static bool lpass_cpu_regmap_volatile(struct device *dev, unsigned int reg) ...@@ -454,20 +442,16 @@ static bool lpass_cpu_regmap_volatile(struct device *dev, unsigned int reg)
struct lpass_variant *v = drvdata->variant; struct lpass_variant *v = drvdata->variant;
int i; int i;
for (i = 0; i < v->i2s_ports; ++i)
if (reg == LPAIF_I2SCTL_REG(v, i))
return true;
for (i = 0; i < v->irq_ports; ++i) for (i = 0; i < v->irq_ports; ++i)
if (reg == LPAIF_IRQSTAT_REG(v, i)) if (reg == LPAIF_IRQSTAT_REG(v, i))
return true; return true;
for (i = 0; i < v->rdma_channels; ++i) for (i = 0; i < v->rdma_channels; ++i)
if (reg == LPAIF_RDMACURR_REG(v, i) || reg == LPAIF_RDMACTL_REG(v, i)) if (reg == LPAIF_RDMACURR_REG(v, i))
return true; return true;
for (i = 0; i < v->wrdma_channels; ++i) for (i = 0; i < v->wrdma_channels; ++i)
if (reg == LPAIF_WRDMACURR_REG(v, i + v->wrdma_channel_start) || if (reg == LPAIF_WRDMACURR_REG(v, i + v->wrdma_channel_start))
reg == LPAIF_WRDMACTL_REG(v, i + v->wrdma_channel_start))
return true; return true;
return false; return false;
......
...@@ -452,7 +452,6 @@ static int lpass_platform_pcmops_trigger(struct snd_soc_component *component, ...@@ -452,7 +452,6 @@ static int lpass_platform_pcmops_trigger(struct snd_soc_component *component,
unsigned int reg_irqclr = 0, val_irqclr = 0; unsigned int reg_irqclr = 0, val_irqclr = 0;
unsigned int reg_irqen = 0, val_irqen = 0, val_mask = 0; unsigned int reg_irqen = 0, val_irqen = 0, val_mask = 0;
unsigned int dai_id = cpu_dai->driver->id; unsigned int dai_id = cpu_dai->driver->id;
unsigned int dma_ctrl_reg = 0;
ch = pcm_data->dma_ch; ch = pcm_data->dma_ch;
if (dir == SNDRV_PCM_STREAM_PLAYBACK) { if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
...@@ -469,17 +468,7 @@ static int lpass_platform_pcmops_trigger(struct snd_soc_component *component, ...@@ -469,17 +468,7 @@ static int lpass_platform_pcmops_trigger(struct snd_soc_component *component,
id = pcm_data->dma_ch - v->wrdma_channel_start; id = pcm_data->dma_ch - v->wrdma_channel_start;
map = drvdata->lpaif_map; map = drvdata->lpaif_map;
} }
ret = regmap_read(map, LPAIF_DMACTL_REG(v, ch, dir, dai_id), &dma_ctrl_reg);
if (ret) {
dev_err(soc_runtime->dev, "error reading from rdmactl reg: %d\n", ret);
return ret;
}
if (dma_ctrl_reg == LPAIF_DMACTL_RESET_STATE ||
dma_ctrl_reg == LPAIF_DMACTL_RESET_STATE + 1) {
dev_err(soc_runtime->dev, "error in rdmactl register state\n");
return -ENOTRECOVERABLE;
}
switch (cmd) { switch (cmd) {
case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_RESUME:
...@@ -500,7 +489,6 @@ static int lpass_platform_pcmops_trigger(struct snd_soc_component *component, ...@@ -500,7 +489,6 @@ static int lpass_platform_pcmops_trigger(struct snd_soc_component *component,
"error writing to rdmactl reg: %d\n", ret); "error writing to rdmactl reg: %d\n", ret);
return ret; return ret;
} }
map = drvdata->hdmiif_map;
reg_irqclr = LPASS_HDMITX_APP_IRQCLEAR_REG(v); reg_irqclr = LPASS_HDMITX_APP_IRQCLEAR_REG(v);
val_irqclr = (LPAIF_IRQ_ALL(ch) | val_irqclr = (LPAIF_IRQ_ALL(ch) |
LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(ch) | LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(ch) |
...@@ -519,7 +507,6 @@ static int lpass_platform_pcmops_trigger(struct snd_soc_component *component, ...@@ -519,7 +507,6 @@ static int lpass_platform_pcmops_trigger(struct snd_soc_component *component,
break; break;
case MI2S_PRIMARY: case MI2S_PRIMARY:
case MI2S_SECONDARY: case MI2S_SECONDARY:
map = drvdata->lpaif_map;
reg_irqclr = LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST); reg_irqclr = LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST);
val_irqclr = LPAIF_IRQ_ALL(ch); val_irqclr = LPAIF_IRQ_ALL(ch);
...@@ -563,7 +550,6 @@ static int lpass_platform_pcmops_trigger(struct snd_soc_component *component, ...@@ -563,7 +550,6 @@ static int lpass_platform_pcmops_trigger(struct snd_soc_component *component,
"error writing to rdmactl reg: %d\n", ret); "error writing to rdmactl reg: %d\n", ret);
return ret; return ret;
} }
map = drvdata->hdmiif_map;
reg_irqen = LPASS_HDMITX_APP_IRQEN_REG(v); reg_irqen = LPASS_HDMITX_APP_IRQEN_REG(v);
val_mask = (LPAIF_IRQ_ALL(ch) | val_mask = (LPAIF_IRQ_ALL(ch) |
LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(ch) | LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(ch) |
...@@ -573,7 +559,6 @@ static int lpass_platform_pcmops_trigger(struct snd_soc_component *component, ...@@ -573,7 +559,6 @@ static int lpass_platform_pcmops_trigger(struct snd_soc_component *component,
break; break;
case MI2S_PRIMARY: case MI2S_PRIMARY:
case MI2S_SECONDARY: case MI2S_SECONDARY:
map = drvdata->lpaif_map;
reg_irqen = LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST); reg_irqen = LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST);
val_mask = LPAIF_IRQ_ALL(ch); val_mask = LPAIF_IRQ_ALL(ch);
val_irqen = 0; val_irqen = 0;
...@@ -838,6 +823,39 @@ static void lpass_platform_pcm_free(struct snd_soc_component *component, ...@@ -838,6 +823,39 @@ static void lpass_platform_pcm_free(struct snd_soc_component *component,
} }
} }
static int lpass_platform_pcmops_suspend(struct snd_soc_component *component)
{
struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
struct regmap *map;
unsigned int dai_id = component->id;
if (dai_id == LPASS_DP_RX)
map = drvdata->hdmiif_map;
else
map = drvdata->lpaif_map;
regcache_cache_only(map, true);
regcache_mark_dirty(map);
return 0;
}
static int lpass_platform_pcmops_resume(struct snd_soc_component *component)
{
struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
struct regmap *map;
unsigned int dai_id = component->id;
if (dai_id == LPASS_DP_RX)
map = drvdata->hdmiif_map;
else
map = drvdata->lpaif_map;
regcache_cache_only(map, false);
return regcache_sync(map);
}
static const struct snd_soc_component_driver lpass_component_driver = { static const struct snd_soc_component_driver lpass_component_driver = {
.name = DRV_NAME, .name = DRV_NAME,
.open = lpass_platform_pcmops_open, .open = lpass_platform_pcmops_open,
...@@ -850,6 +868,8 @@ static const struct snd_soc_component_driver lpass_component_driver = { ...@@ -850,6 +868,8 @@ static const struct snd_soc_component_driver lpass_component_driver = {
.mmap = lpass_platform_pcmops_mmap, .mmap = lpass_platform_pcmops_mmap,
.pcm_construct = lpass_platform_pcm_new, .pcm_construct = lpass_platform_pcm_new,
.pcm_destruct = lpass_platform_pcm_free, .pcm_destruct = lpass_platform_pcm_free,
.suspend = lpass_platform_pcmops_suspend,
.resume = lpass_platform_pcmops_resume,
}; };
......
...@@ -366,25 +366,27 @@ void rsnd_adg_clk_control(struct rsnd_priv *priv, int enable) ...@@ -366,25 +366,27 @@ void rsnd_adg_clk_control(struct rsnd_priv *priv, int enable)
struct rsnd_adg *adg = rsnd_priv_to_adg(priv); struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
struct device *dev = rsnd_priv_to_dev(priv); struct device *dev = rsnd_priv_to_dev(priv);
struct clk *clk; struct clk *clk;
int i, ret; int i;
for_each_rsnd_clk(clk, adg, i) { for_each_rsnd_clk(clk, adg, i) {
ret = 0;
if (enable) { if (enable) {
ret = clk_prepare_enable(clk); int ret = clk_prepare_enable(clk);
/* /*
* We shouldn't use clk_get_rate() under * We shouldn't use clk_get_rate() under
* atomic context. Let's keep it when * atomic context. Let's keep it when
* rsnd_adg_clk_enable() was called * rsnd_adg_clk_enable() was called
*/ */
adg->clk_rate[i] = clk_get_rate(adg->clk[i]); adg->clk_rate[i] = 0;
if (ret < 0)
dev_warn(dev, "can't use clk %d\n", i);
else
adg->clk_rate[i] = clk_get_rate(clk);
} else { } else {
clk_disable_unprepare(clk); if (adg->clk_rate[i])
clk_disable_unprepare(clk);
adg->clk_rate[i] = 0;
} }
if (ret < 0)
dev_warn(dev, "can't use clk %d\n", i);
} }
} }
......
...@@ -2486,6 +2486,7 @@ void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w) ...@@ -2486,6 +2486,7 @@ void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w)
enum snd_soc_dapm_direction dir; enum snd_soc_dapm_direction dir;
list_del(&w->list); list_del(&w->list);
list_del(&w->dirty);
/* /*
* remove source and sink paths associated to this widget. * remove source and sink paths associated to this widget.
* While removing the path, remove reference to it from both * While removing the path, remove reference to it from both
......
...@@ -122,7 +122,7 @@ config SND_SOC_SOF_DEBUG_XRUN_STOP ...@@ -122,7 +122,7 @@ config SND_SOC_SOF_DEBUG_XRUN_STOP
bool "SOF stop on XRUN" bool "SOF stop on XRUN"
help help
This option forces PCMs to stop on any XRUN event. This is useful to This option forces PCMs to stop on any XRUN event. This is useful to
preserve any trace data ond pipeline status prior to the XRUN. preserve any trace data and pipeline status prior to the XRUN.
Say Y if you are debugging SOF FW pipeline XRUNs. Say Y if you are debugging SOF FW pipeline XRUNs.
If unsure select "N". If unsure select "N".
......
...@@ -450,10 +450,8 @@ lookup_device_name(u32 id) ...@@ -450,10 +450,8 @@ lookup_device_name(u32 id)
static void snd_usb_audio_free(struct snd_card *card) static void snd_usb_audio_free(struct snd_card *card)
{ {
struct snd_usb_audio *chip = card->private_data; struct snd_usb_audio *chip = card->private_data;
struct snd_usb_endpoint *ep, *n;
list_for_each_entry_safe(ep, n, &chip->ep_list, list) snd_usb_endpoint_free_all(chip);
snd_usb_endpoint_free(ep);
mutex_destroy(&chip->mutex); mutex_destroy(&chip->mutex);
if (!atomic_read(&chip->shutdown)) if (!atomic_read(&chip->shutdown))
...@@ -611,6 +609,7 @@ static int snd_usb_audio_create(struct usb_interface *intf, ...@@ -611,6 +609,7 @@ static int snd_usb_audio_create(struct usb_interface *intf,
chip->usb_id = usb_id; chip->usb_id = usb_id;
INIT_LIST_HEAD(&chip->pcm_list); INIT_LIST_HEAD(&chip->pcm_list);
INIT_LIST_HEAD(&chip->ep_list); INIT_LIST_HEAD(&chip->ep_list);
INIT_LIST_HEAD(&chip->iface_ref_list);
INIT_LIST_HEAD(&chip->midi_list); INIT_LIST_HEAD(&chip->midi_list);
INIT_LIST_HEAD(&chip->mixer_list); INIT_LIST_HEAD(&chip->mixer_list);
......
...@@ -18,6 +18,7 @@ struct audioformat { ...@@ -18,6 +18,7 @@ struct audioformat {
unsigned int frame_size; /* samples per frame for non-audio */ unsigned int frame_size; /* samples per frame for non-audio */
unsigned char iface; /* interface number */ unsigned char iface; /* interface number */
unsigned char altsetting; /* corresponding alternate setting */ unsigned char altsetting; /* corresponding alternate setting */
unsigned char ep_idx; /* endpoint array index */
unsigned char altset_idx; /* array index of altenate setting */ unsigned char altset_idx; /* array index of altenate setting */
unsigned char attributes; /* corresponding attributes of cs endpoint */ unsigned char attributes; /* corresponding attributes of cs endpoint */
unsigned char endpoint; /* endpoint */ unsigned char endpoint; /* endpoint */
...@@ -42,6 +43,7 @@ struct audioformat { ...@@ -42,6 +43,7 @@ struct audioformat {
}; };
struct snd_usb_substream; struct snd_usb_substream;
struct snd_usb_iface_ref;
struct snd_usb_endpoint; struct snd_usb_endpoint;
struct snd_usb_power_domain; struct snd_usb_power_domain;
...@@ -58,6 +60,7 @@ struct snd_urb_ctx { ...@@ -58,6 +60,7 @@ struct snd_urb_ctx {
struct snd_usb_endpoint { struct snd_usb_endpoint {
struct snd_usb_audio *chip; struct snd_usb_audio *chip;
struct snd_usb_iface_ref *iface_ref;
int opened; /* open refcount; protect with chip->mutex */ int opened; /* open refcount; protect with chip->mutex */
atomic_t running; /* running status */ atomic_t running; /* running status */
......
...@@ -24,6 +24,14 @@ ...@@ -24,6 +24,14 @@
#define EP_FLAG_RUNNING 1 #define EP_FLAG_RUNNING 1
#define EP_FLAG_STOPPING 2 #define EP_FLAG_STOPPING 2
/* interface refcounting */
struct snd_usb_iface_ref {
unsigned char iface;
bool need_setup;
int opened;
struct list_head list;
};
/* /*
* snd_usb_endpoint is a model that abstracts everything related to an * snd_usb_endpoint is a model that abstracts everything related to an
* USB endpoint and its streaming. * USB endpoint and its streaming.
...@@ -488,6 +496,28 @@ static void snd_complete_urb(struct urb *urb) ...@@ -488,6 +496,28 @@ static void snd_complete_urb(struct urb *urb)
clear_bit(ctx->index, &ep->active_mask); clear_bit(ctx->index, &ep->active_mask);
} }
/*
* Find or create a refcount object for the given interface
*
* The objects are released altogether in snd_usb_endpoint_free_all()
*/
static struct snd_usb_iface_ref *
iface_ref_find(struct snd_usb_audio *chip, int iface)
{
struct snd_usb_iface_ref *ip;
list_for_each_entry(ip, &chip->iface_ref_list, list)
if (ip->iface == iface)
return ip;
ip = kzalloc(sizeof(*ip), GFP_KERNEL);
if (!ip)
return NULL;
ip->iface = iface;
list_add_tail(&ip->list, &chip->iface_ref_list);
return ip;
}
/* /*
* Get the existing endpoint object corresponding EP * Get the existing endpoint object corresponding EP
* Returns NULL if not present. * Returns NULL if not present.
...@@ -520,8 +550,8 @@ snd_usb_get_endpoint(struct snd_usb_audio *chip, int ep_num) ...@@ -520,8 +550,8 @@ snd_usb_get_endpoint(struct snd_usb_audio *chip, int ep_num)
* *
* Returns zero on success or a negative error code. * Returns zero on success or a negative error code.
* *
* New endpoints will be added to chip->ep_list and must be freed by * New endpoints will be added to chip->ep_list and freed by
* calling snd_usb_endpoint_free(). * calling snd_usb_endpoint_free_all().
* *
* For SND_USB_ENDPOINT_TYPE_SYNC, the caller needs to guarantee that * For SND_USB_ENDPOINT_TYPE_SYNC, the caller needs to guarantee that
* bNumEndpoints > 1 beforehand. * bNumEndpoints > 1 beforehand.
...@@ -653,11 +683,17 @@ snd_usb_endpoint_open(struct snd_usb_audio *chip, ...@@ -653,11 +683,17 @@ snd_usb_endpoint_open(struct snd_usb_audio *chip,
} else { } else {
ep->iface = fp->iface; ep->iface = fp->iface;
ep->altsetting = fp->altsetting; ep->altsetting = fp->altsetting;
ep->ep_idx = 0; ep->ep_idx = fp->ep_idx;
} }
usb_audio_dbg(chip, "Open EP 0x%x, iface=%d:%d, idx=%d\n", usb_audio_dbg(chip, "Open EP 0x%x, iface=%d:%d, idx=%d\n",
ep_num, ep->iface, ep->altsetting, ep->ep_idx); ep_num, ep->iface, ep->altsetting, ep->ep_idx);
ep->iface_ref = iface_ref_find(chip, ep->iface);
if (!ep->iface_ref) {
ep = NULL;
goto unlock;
}
ep->cur_audiofmt = fp; ep->cur_audiofmt = fp;
ep->cur_channels = fp->channels; ep->cur_channels = fp->channels;
ep->cur_rate = params_rate(params); ep->cur_rate = params_rate(params);
...@@ -681,6 +717,11 @@ snd_usb_endpoint_open(struct snd_usb_audio *chip, ...@@ -681,6 +717,11 @@ snd_usb_endpoint_open(struct snd_usb_audio *chip,
ep->implicit_fb_sync); ep->implicit_fb_sync);
} else { } else {
if (WARN_ON(!ep->iface_ref)) {
ep = NULL;
goto unlock;
}
if (!endpoint_compatible(ep, fp, params)) { if (!endpoint_compatible(ep, fp, params)) {
usb_audio_err(chip, "Incompatible EP setup for 0x%x\n", usb_audio_err(chip, "Incompatible EP setup for 0x%x\n",
ep_num); ep_num);
...@@ -692,6 +733,9 @@ snd_usb_endpoint_open(struct snd_usb_audio *chip, ...@@ -692,6 +733,9 @@ snd_usb_endpoint_open(struct snd_usb_audio *chip,
ep_num, ep->opened); ep_num, ep->opened);
} }
if (!ep->iface_ref->opened++)
ep->iface_ref->need_setup = true;
ep->opened++; ep->opened++;
unlock: unlock:
...@@ -760,12 +804,16 @@ void snd_usb_endpoint_close(struct snd_usb_audio *chip, ...@@ -760,12 +804,16 @@ void snd_usb_endpoint_close(struct snd_usb_audio *chip,
mutex_lock(&chip->mutex); mutex_lock(&chip->mutex);
usb_audio_dbg(chip, "Closing EP 0x%x (count %d)\n", usb_audio_dbg(chip, "Closing EP 0x%x (count %d)\n",
ep->ep_num, ep->opened); ep->ep_num, ep->opened);
if (!--ep->opened) {
if (!--ep->iface_ref->opened)
endpoint_set_interface(chip, ep, false); endpoint_set_interface(chip, ep, false);
if (!--ep->opened) {
ep->iface = 0; ep->iface = 0;
ep->altsetting = 0; ep->altsetting = 0;
ep->cur_audiofmt = NULL; ep->cur_audiofmt = NULL;
ep->cur_rate = 0; ep->cur_rate = 0;
ep->iface_ref = NULL;
usb_audio_dbg(chip, "EP 0x%x closed\n", ep->ep_num); usb_audio_dbg(chip, "EP 0x%x closed\n", ep->ep_num);
} }
mutex_unlock(&chip->mutex); mutex_unlock(&chip->mutex);
...@@ -775,6 +823,8 @@ void snd_usb_endpoint_close(struct snd_usb_audio *chip, ...@@ -775,6 +823,8 @@ void snd_usb_endpoint_close(struct snd_usb_audio *chip,
void snd_usb_endpoint_suspend(struct snd_usb_endpoint *ep) void snd_usb_endpoint_suspend(struct snd_usb_endpoint *ep)
{ {
ep->need_setup = true; ep->need_setup = true;
if (ep->iface_ref)
ep->iface_ref->need_setup = true;
} }
/* /*
...@@ -1195,11 +1245,13 @@ int snd_usb_endpoint_configure(struct snd_usb_audio *chip, ...@@ -1195,11 +1245,13 @@ int snd_usb_endpoint_configure(struct snd_usb_audio *chip,
int err = 0; int err = 0;
mutex_lock(&chip->mutex); mutex_lock(&chip->mutex);
if (WARN_ON(!ep->iface_ref))
goto unlock;
if (!ep->need_setup) if (!ep->need_setup)
goto unlock; goto unlock;
/* No need to (re-)configure the sync EP belonging to the same altset */ /* If the interface has been already set up, just set EP parameters */
if (ep->ep_idx) { if (!ep->iface_ref->need_setup) {
err = snd_usb_endpoint_set_params(chip, ep); err = snd_usb_endpoint_set_params(chip, ep);
if (err < 0) if (err < 0)
goto unlock; goto unlock;
...@@ -1242,6 +1294,8 @@ int snd_usb_endpoint_configure(struct snd_usb_audio *chip, ...@@ -1242,6 +1294,8 @@ int snd_usb_endpoint_configure(struct snd_usb_audio *chip,
goto unlock; goto unlock;
} }
ep->iface_ref->need_setup = false;
done: done:
ep->need_setup = false; ep->need_setup = false;
err = 1; err = 1;
...@@ -1387,15 +1441,21 @@ void snd_usb_endpoint_release(struct snd_usb_endpoint *ep) ...@@ -1387,15 +1441,21 @@ void snd_usb_endpoint_release(struct snd_usb_endpoint *ep)
} }
/** /**
* snd_usb_endpoint_free: Free the resources of an snd_usb_endpoint * snd_usb_endpoint_free_all: Free the resources of an snd_usb_endpoint
* @card: The chip
* *
* @ep: the endpoint to free * This free all endpoints and those resources
*
* This free all resources of the given ep.
*/ */
void snd_usb_endpoint_free(struct snd_usb_endpoint *ep) void snd_usb_endpoint_free_all(struct snd_usb_audio *chip)
{ {
kfree(ep); struct snd_usb_endpoint *ep, *en;
struct snd_usb_iface_ref *ip, *in;
list_for_each_entry_safe(ep, en, &chip->ep_list, list)
kfree(ep);
list_for_each_entry_safe(ip, in, &chip->iface_ref_list, list)
kfree(ip);
} }
/* /*
......
...@@ -42,7 +42,7 @@ void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep); ...@@ -42,7 +42,7 @@ void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep);
void snd_usb_endpoint_suspend(struct snd_usb_endpoint *ep); void snd_usb_endpoint_suspend(struct snd_usb_endpoint *ep);
int snd_usb_endpoint_activate(struct snd_usb_endpoint *ep); int snd_usb_endpoint_activate(struct snd_usb_endpoint *ep);
void snd_usb_endpoint_release(struct snd_usb_endpoint *ep); void snd_usb_endpoint_release(struct snd_usb_endpoint *ep);
void snd_usb_endpoint_free(struct snd_usb_endpoint *ep); void snd_usb_endpoint_free_all(struct snd_usb_audio *chip);
int snd_usb_endpoint_implicit_feedback_sink(struct snd_usb_endpoint *ep); int snd_usb_endpoint_implicit_feedback_sink(struct snd_usb_endpoint *ep);
int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep, int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep,
......
...@@ -58,8 +58,6 @@ static const struct snd_usb_implicit_fb_match playback_implicit_fb_quirks[] = { ...@@ -58,8 +58,6 @@ static const struct snd_usb_implicit_fb_match playback_implicit_fb_quirks[] = {
IMPLICIT_FB_FIXED_DEV(0x0499, 0x172f, 0x81, 2), /* Steinberg UR22C */ IMPLICIT_FB_FIXED_DEV(0x0499, 0x172f, 0x81, 2), /* Steinberg UR22C */
IMPLICIT_FB_FIXED_DEV(0x0d9a, 0x00df, 0x81, 2), /* RTX6001 */ IMPLICIT_FB_FIXED_DEV(0x0d9a, 0x00df, 0x81, 2), /* RTX6001 */
IMPLICIT_FB_FIXED_DEV(0x22f0, 0x0006, 0x81, 3), /* Allen&Heath Qu-16 */ IMPLICIT_FB_FIXED_DEV(0x22f0, 0x0006, 0x81, 3), /* Allen&Heath Qu-16 */
IMPLICIT_FB_FIXED_DEV(0x2b73, 0x000a, 0x82, 0), /* Pioneer DJ DJM-900NXS2 */
IMPLICIT_FB_FIXED_DEV(0x2b73, 0x0017, 0x82, 0), /* Pioneer DJ DJM-250MK2 */
IMPLICIT_FB_FIXED_DEV(0x1686, 0xf029, 0x82, 2), /* Zoom UAC-2 */ IMPLICIT_FB_FIXED_DEV(0x1686, 0xf029, 0x82, 2), /* Zoom UAC-2 */
IMPLICIT_FB_FIXED_DEV(0x2466, 0x8003, 0x86, 2), /* Fractal Audio Axe-Fx II */ IMPLICIT_FB_FIXED_DEV(0x2466, 0x8003, 0x86, 2), /* Fractal Audio Axe-Fx II */
IMPLICIT_FB_FIXED_DEV(0x0499, 0x172a, 0x86, 2), /* Yamaha MODX */ IMPLICIT_FB_FIXED_DEV(0x0499, 0x172a, 0x86, 2), /* Yamaha MODX */
...@@ -100,7 +98,7 @@ static const struct snd_usb_implicit_fb_match capture_implicit_fb_quirks[] = { ...@@ -100,7 +98,7 @@ static const struct snd_usb_implicit_fb_match capture_implicit_fb_quirks[] = {
/* set up sync EP information on the audioformat */ /* set up sync EP information on the audioformat */
static int add_implicit_fb_sync_ep(struct snd_usb_audio *chip, static int add_implicit_fb_sync_ep(struct snd_usb_audio *chip,
struct audioformat *fmt, struct audioformat *fmt,
int ep, int ifnum, int ep, int ep_idx, int ifnum,
const struct usb_host_interface *alts) const struct usb_host_interface *alts)
{ {
struct usb_interface *iface; struct usb_interface *iface;
...@@ -115,7 +113,7 @@ static int add_implicit_fb_sync_ep(struct snd_usb_audio *chip, ...@@ -115,7 +113,7 @@ static int add_implicit_fb_sync_ep(struct snd_usb_audio *chip,
fmt->sync_ep = ep; fmt->sync_ep = ep;
fmt->sync_iface = ifnum; fmt->sync_iface = ifnum;
fmt->sync_altsetting = alts->desc.bAlternateSetting; fmt->sync_altsetting = alts->desc.bAlternateSetting;
fmt->sync_ep_idx = 0; fmt->sync_ep_idx = ep_idx;
fmt->implicit_fb = 1; fmt->implicit_fb = 1;
usb_audio_dbg(chip, usb_audio_dbg(chip,
"%d:%d: added %s implicit_fb sync_ep %x, iface %d:%d\n", "%d:%d: added %s implicit_fb sync_ep %x, iface %d:%d\n",
...@@ -147,7 +145,7 @@ static int add_generic_uac2_implicit_fb(struct snd_usb_audio *chip, ...@@ -147,7 +145,7 @@ static int add_generic_uac2_implicit_fb(struct snd_usb_audio *chip,
(epd->bmAttributes & USB_ENDPOINT_USAGE_MASK) != (epd->bmAttributes & USB_ENDPOINT_USAGE_MASK) !=
USB_ENDPOINT_USAGE_IMPLICIT_FB) USB_ENDPOINT_USAGE_IMPLICIT_FB)
return 0; return 0;
return add_implicit_fb_sync_ep(chip, fmt, epd->bEndpointAddress, return add_implicit_fb_sync_ep(chip, fmt, epd->bEndpointAddress, 0,
ifnum, alts); ifnum, alts);
} }
...@@ -173,10 +171,32 @@ static int add_roland_implicit_fb(struct snd_usb_audio *chip, ...@@ -173,10 +171,32 @@ static int add_roland_implicit_fb(struct snd_usb_audio *chip,
(epd->bmAttributes & USB_ENDPOINT_USAGE_MASK) != (epd->bmAttributes & USB_ENDPOINT_USAGE_MASK) !=
USB_ENDPOINT_USAGE_IMPLICIT_FB) USB_ENDPOINT_USAGE_IMPLICIT_FB)
return 0; return 0;
return add_implicit_fb_sync_ep(chip, fmt, epd->bEndpointAddress, return add_implicit_fb_sync_ep(chip, fmt, epd->bEndpointAddress, 0,
ifnum, alts); ifnum, alts);
} }
/* Pioneer devices: playback and capture streams sharing the same iface/altset
*/
static int add_pioneer_implicit_fb(struct snd_usb_audio *chip,
struct audioformat *fmt,
struct usb_host_interface *alts)
{
struct usb_endpoint_descriptor *epd;
if (alts->desc.bNumEndpoints != 2)
return 0;
epd = get_endpoint(alts, 1);
if (!usb_endpoint_is_isoc_in(epd) ||
(epd->bmAttributes & USB_ENDPOINT_SYNCTYPE) != USB_ENDPOINT_SYNC_ASYNC ||
((epd->bmAttributes & USB_ENDPOINT_USAGE_MASK) !=
USB_ENDPOINT_USAGE_DATA &&
(epd->bmAttributes & USB_ENDPOINT_USAGE_MASK) !=
USB_ENDPOINT_USAGE_IMPLICIT_FB))
return 0;
return add_implicit_fb_sync_ep(chip, fmt, epd->bEndpointAddress, 1,
alts->desc.bInterfaceNumber, alts);
}
static int __add_generic_implicit_fb(struct snd_usb_audio *chip, static int __add_generic_implicit_fb(struct snd_usb_audio *chip,
struct audioformat *fmt, struct audioformat *fmt,
...@@ -197,7 +217,7 @@ static int __add_generic_implicit_fb(struct snd_usb_audio *chip, ...@@ -197,7 +217,7 @@ static int __add_generic_implicit_fb(struct snd_usb_audio *chip,
if (!usb_endpoint_is_isoc_in(epd) || if (!usb_endpoint_is_isoc_in(epd) ||
(epd->bmAttributes & USB_ENDPOINT_SYNCTYPE) != USB_ENDPOINT_SYNC_ASYNC) (epd->bmAttributes & USB_ENDPOINT_SYNCTYPE) != USB_ENDPOINT_SYNC_ASYNC)
return 0; return 0;
return add_implicit_fb_sync_ep(chip, fmt, epd->bEndpointAddress, return add_implicit_fb_sync_ep(chip, fmt, epd->bEndpointAddress, 0,
iface, alts); iface, alts);
} }
...@@ -250,7 +270,7 @@ static int audioformat_implicit_fb_quirk(struct snd_usb_audio *chip, ...@@ -250,7 +270,7 @@ static int audioformat_implicit_fb_quirk(struct snd_usb_audio *chip,
case IMPLICIT_FB_NONE: case IMPLICIT_FB_NONE:
return 0; /* No quirk */ return 0; /* No quirk */
case IMPLICIT_FB_FIXED: case IMPLICIT_FB_FIXED:
return add_implicit_fb_sync_ep(chip, fmt, p->ep_num, return add_implicit_fb_sync_ep(chip, fmt, p->ep_num, 0,
p->iface, NULL); p->iface, NULL);
} }
} }
...@@ -278,6 +298,14 @@ static int audioformat_implicit_fb_quirk(struct snd_usb_audio *chip, ...@@ -278,6 +298,14 @@ static int audioformat_implicit_fb_quirk(struct snd_usb_audio *chip,
return 1; return 1;
} }
/* Pioneer devices implicit feedback with vendor spec class */
if (attr == USB_ENDPOINT_SYNC_ASYNC &&
alts->desc.bInterfaceClass == USB_CLASS_VENDOR_SPEC &&
USB_ID_VENDOR(chip->usb_id) == 0x2b73 /* Pioneer */) {
if (add_pioneer_implicit_fb(chip, fmt, alts))
return 1;
}
/* Try the generic implicit fb if available */ /* Try the generic implicit fb if available */
if (chip->generic_implicit_fb) if (chip->generic_implicit_fb)
return add_generic_implicit_fb(chip, fmt, alts); return add_generic_implicit_fb(chip, fmt, alts);
...@@ -295,8 +323,8 @@ static int audioformat_capture_quirk(struct snd_usb_audio *chip, ...@@ -295,8 +323,8 @@ static int audioformat_capture_quirk(struct snd_usb_audio *chip,
p = find_implicit_fb_entry(chip, capture_implicit_fb_quirks, alts); p = find_implicit_fb_entry(chip, capture_implicit_fb_quirks, alts);
if (p && p->type == IMPLICIT_FB_FIXED) if (p && p->type == IMPLICIT_FB_FIXED)
return add_implicit_fb_sync_ep(chip, fmt, p->ep_num, p->iface, return add_implicit_fb_sync_ep(chip, fmt, p->ep_num, 0,
NULL); p->iface, NULL);
return 0; return 0;
} }
...@@ -378,20 +406,19 @@ snd_usb_find_implicit_fb_sync_format(struct snd_usb_audio *chip, ...@@ -378,20 +406,19 @@ snd_usb_find_implicit_fb_sync_format(struct snd_usb_audio *chip,
int stream) int stream)
{ {
struct snd_usb_substream *subs; struct snd_usb_substream *subs;
const struct audioformat *fp, *sync_fmt; const struct audioformat *fp, *sync_fmt = NULL;
int score, high_score; int score, high_score;
/* When sharing the same altset, use the original audioformat */ /* Use the original audioformat as fallback for the shared altset */
if (target->iface == target->sync_iface && if (target->iface == target->sync_iface &&
target->altsetting == target->sync_altsetting) target->altsetting == target->sync_altsetting)
return target; sync_fmt = target;
subs = find_matching_substream(chip, stream, target->sync_ep, subs = find_matching_substream(chip, stream, target->sync_ep,
target->fmt_type); target->fmt_type);
if (!subs) if (!subs)
return NULL; return sync_fmt;
sync_fmt = NULL;
high_score = 0; high_score = 0;
list_for_each_entry(fp, &subs->fmt_list, list) { list_for_each_entry(fp, &subs->fmt_list, list) {
score = match_endpoint_audioformats(subs, fp, score = match_endpoint_audioformats(subs, fp,
......
...@@ -3362,6 +3362,7 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"), ...@@ -3362,6 +3362,7 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"),
.altsetting = 1, .altsetting = 1,
.altset_idx = 1, .altset_idx = 1,
.endpoint = 0x86, .endpoint = 0x86,
.ep_idx = 1,
.ep_attr = USB_ENDPOINT_XFER_ISOC| .ep_attr = USB_ENDPOINT_XFER_ISOC|
USB_ENDPOINT_SYNC_ASYNC| USB_ENDPOINT_SYNC_ASYNC|
USB_ENDPOINT_USAGE_IMPLICIT_FB, USB_ENDPOINT_USAGE_IMPLICIT_FB,
...@@ -3450,6 +3451,7 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"), ...@@ -3450,6 +3451,7 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"),
.altsetting = 1, .altsetting = 1,
.altset_idx = 1, .altset_idx = 1,
.endpoint = 0x82, .endpoint = 0x82,
.ep_idx = 1,
.ep_attr = USB_ENDPOINT_XFER_ISOC| .ep_attr = USB_ENDPOINT_XFER_ISOC|
USB_ENDPOINT_SYNC_ASYNC| USB_ENDPOINT_SYNC_ASYNC|
USB_ENDPOINT_USAGE_IMPLICIT_FB, USB_ENDPOINT_USAGE_IMPLICIT_FB,
...@@ -3506,6 +3508,7 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"), ...@@ -3506,6 +3508,7 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"),
.altsetting = 1, .altsetting = 1,
.altset_idx = 1, .altset_idx = 1,
.endpoint = 0x82, .endpoint = 0x82,
.ep_idx = 1,
.ep_attr = USB_ENDPOINT_XFER_ISOC| .ep_attr = USB_ENDPOINT_XFER_ISOC|
USB_ENDPOINT_SYNC_ASYNC| USB_ENDPOINT_SYNC_ASYNC|
USB_ENDPOINT_USAGE_IMPLICIT_FB, USB_ENDPOINT_USAGE_IMPLICIT_FB,
...@@ -3562,6 +3565,7 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"), ...@@ -3562,6 +3565,7 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"),
.altsetting = 1, .altsetting = 1,
.altset_idx = 1, .altset_idx = 1,
.endpoint = 0x82, .endpoint = 0x82,
.ep_idx = 1,
.ep_attr = USB_ENDPOINT_XFER_ISOC| .ep_attr = USB_ENDPOINT_XFER_ISOC|
USB_ENDPOINT_SYNC_ASYNC| USB_ENDPOINT_SYNC_ASYNC|
USB_ENDPOINT_USAGE_IMPLICIT_FB, USB_ENDPOINT_USAGE_IMPLICIT_FB,
...@@ -3619,6 +3623,7 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"), ...@@ -3619,6 +3623,7 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"),
.altsetting = 1, .altsetting = 1,
.altset_idx = 1, .altset_idx = 1,
.endpoint = 0x82, .endpoint = 0x82,
.ep_idx = 1,
.ep_attr = USB_ENDPOINT_XFER_ISOC| .ep_attr = USB_ENDPOINT_XFER_ISOC|
USB_ENDPOINT_SYNC_ASYNC| USB_ENDPOINT_SYNC_ASYNC|
USB_ENDPOINT_USAGE_IMPLICIT_FB, USB_ENDPOINT_USAGE_IMPLICIT_FB,
...@@ -3679,6 +3684,7 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"), ...@@ -3679,6 +3684,7 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"),
.altsetting = 1, .altsetting = 1,
.altset_idx = 1, .altset_idx = 1,
.endpoint = 0x82, .endpoint = 0x82,
.ep_idx = 1,
.ep_attr = USB_ENDPOINT_XFER_ISOC| .ep_attr = USB_ENDPOINT_XFER_ISOC|
USB_ENDPOINT_SYNC_ASYNC| USB_ENDPOINT_SYNC_ASYNC|
USB_ENDPOINT_USAGE_IMPLICIT_FB, USB_ENDPOINT_USAGE_IMPLICIT_FB,
......
...@@ -120,6 +120,40 @@ static int create_standard_audio_quirk(struct snd_usb_audio *chip, ...@@ -120,6 +120,40 @@ static int create_standard_audio_quirk(struct snd_usb_audio *chip,
return 0; return 0;
} }
/* create the audio stream and the corresponding endpoints from the fixed
* audioformat object; this is used for quirks with the fixed EPs
*/
static int add_audio_stream_from_fixed_fmt(struct snd_usb_audio *chip,
struct audioformat *fp)
{
int stream, err;
stream = (fp->endpoint & USB_DIR_IN) ?
SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
snd_usb_audioformat_set_sync_ep(chip, fp);
err = snd_usb_add_audio_stream(chip, stream, fp);
if (err < 0)
return err;
err = snd_usb_add_endpoint(chip, fp->endpoint,
SND_USB_ENDPOINT_TYPE_DATA);
if (err < 0)
return err;
if (fp->sync_ep) {
err = snd_usb_add_endpoint(chip, fp->sync_ep,
fp->implicit_fb ?
SND_USB_ENDPOINT_TYPE_DATA :
SND_USB_ENDPOINT_TYPE_SYNC);
if (err < 0)
return err;
}
return 0;
}
/* /*
* create a stream for an endpoint/altsetting without proper descriptors * create a stream for an endpoint/altsetting without proper descriptors
*/ */
...@@ -131,8 +165,8 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip, ...@@ -131,8 +165,8 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip,
struct audioformat *fp; struct audioformat *fp;
struct usb_host_interface *alts; struct usb_host_interface *alts;
struct usb_interface_descriptor *altsd; struct usb_interface_descriptor *altsd;
int stream, err;
unsigned *rate_table = NULL; unsigned *rate_table = NULL;
int err;
fp = kmemdup(quirk->data, sizeof(*fp), GFP_KERNEL); fp = kmemdup(quirk->data, sizeof(*fp), GFP_KERNEL);
if (!fp) if (!fp)
...@@ -153,11 +187,6 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip, ...@@ -153,11 +187,6 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip,
fp->rate_table = rate_table; fp->rate_table = rate_table;
} }
stream = (fp->endpoint & USB_DIR_IN)
? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
err = snd_usb_add_audio_stream(chip, stream, fp);
if (err < 0)
goto error;
if (fp->iface != get_iface_desc(&iface->altsetting[0])->bInterfaceNumber || if (fp->iface != get_iface_desc(&iface->altsetting[0])->bInterfaceNumber ||
fp->altset_idx >= iface->num_altsetting) { fp->altset_idx >= iface->num_altsetting) {
err = -EINVAL; err = -EINVAL;
...@@ -165,7 +194,7 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip, ...@@ -165,7 +194,7 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip,
} }
alts = &iface->altsetting[fp->altset_idx]; alts = &iface->altsetting[fp->altset_idx];
altsd = get_iface_desc(alts); altsd = get_iface_desc(alts);
if (altsd->bNumEndpoints < 1) { if (altsd->bNumEndpoints <= fp->ep_idx) {
err = -EINVAL; err = -EINVAL;
goto error; goto error;
} }
...@@ -175,7 +204,14 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip, ...@@ -175,7 +204,14 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip,
if (fp->datainterval == 0) if (fp->datainterval == 0)
fp->datainterval = snd_usb_parse_datainterval(chip, alts); fp->datainterval = snd_usb_parse_datainterval(chip, alts);
if (fp->maxpacksize == 0) if (fp->maxpacksize == 0)
fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize); fp->maxpacksize = le16_to_cpu(get_endpoint(alts, fp->ep_idx)->wMaxPacketSize);
if (!fp->fmt_type)
fp->fmt_type = UAC_FORMAT_TYPE_I;
err = add_audio_stream_from_fixed_fmt(chip, fp);
if (err < 0)
goto error;
usb_set_interface(chip->dev, fp->iface, 0); usb_set_interface(chip->dev, fp->iface, 0);
snd_usb_init_pitch(chip, fp); snd_usb_init_pitch(chip, fp);
snd_usb_init_sample_rate(chip, fp, fp->rate_max); snd_usb_init_sample_rate(chip, fp, fp->rate_max);
...@@ -417,7 +453,7 @@ static int create_uaxx_quirk(struct snd_usb_audio *chip, ...@@ -417,7 +453,7 @@ static int create_uaxx_quirk(struct snd_usb_audio *chip,
struct usb_host_interface *alts; struct usb_host_interface *alts;
struct usb_interface_descriptor *altsd; struct usb_interface_descriptor *altsd;
struct audioformat *fp; struct audioformat *fp;
int stream, err; int err;
/* both PCM and MIDI interfaces have 2 or more altsettings */ /* both PCM and MIDI interfaces have 2 or more altsettings */
if (iface->num_altsetting < 2) if (iface->num_altsetting < 2)
...@@ -482,9 +518,7 @@ static int create_uaxx_quirk(struct snd_usb_audio *chip, ...@@ -482,9 +518,7 @@ static int create_uaxx_quirk(struct snd_usb_audio *chip,
return -ENXIO; return -ENXIO;
} }
stream = (fp->endpoint & USB_DIR_IN) err = add_audio_stream_from_fixed_fmt(chip, fp);
? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
err = snd_usb_add_audio_stream(chip, stream, fp);
if (err < 0) { if (err < 0) {
list_del(&fp->list); /* unlink for avoiding double-free */ list_del(&fp->list); /* unlink for avoiding double-free */
kfree(fp); kfree(fp);
......
...@@ -44,6 +44,7 @@ struct snd_usb_audio { ...@@ -44,6 +44,7 @@ struct snd_usb_audio {
struct list_head pcm_list; /* list of pcm streams */ struct list_head pcm_list; /* list of pcm streams */
struct list_head ep_list; /* list of audio-related endpoints */ struct list_head ep_list; /* list of audio-related endpoints */
struct list_head iface_ref_list; /* list of interface refcounts */
int pcm_devs; int pcm_devs;
struct list_head midi_list; /* list of midi interfaces */ struct list_head midi_list; /* list of midi interfaces */
......
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