summaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
Diffstat (limited to 'sound')
-rw-r--r--sound/Kconfig4
-rw-r--r--sound/core/seq/seq_ports.c1
-rw-r--r--sound/pci/hda/Kconfig31
-rw-r--r--sound/pci/hda/Makefile6
-rw-r--r--sound/pci/hda/cs35l56_hda.c995
-rw-r--r--sound/pci/hda/cs35l56_hda.h48
-rw-r--r--sound/pci/hda/cs35l56_hda_i2c.c69
-rw-r--r--sound/pci/hda/cs35l56_hda_spi.c68
-rw-r--r--sound/pci/hda/hda_auto_parser.h2
-rw-r--r--sound/pci/hda/hda_generic.h3
-rw-r--r--sound/pci/hda/patch_realtek.c62
-rw-r--r--sound/soc/Kconfig11
-rw-r--r--sound/soc/amd/Kconfig5
-rw-r--r--sound/soc/amd/acp-config.c33
-rw-r--r--sound/soc/amd/acp/Kconfig8
-rw-r--r--sound/soc/amd/acp/Makefile2
-rw-r--r--sound/soc/amd/acp/acp-i2s.c2
-rw-r--r--sound/soc/amd/acp/acp-legacy-common.c347
-rw-r--r--sound/soc/amd/acp/acp-mach-common.c267
-rw-r--r--sound/soc/amd/acp/acp-mach.h2
-rw-r--r--sound/soc/amd/acp/acp-pci.c57
-rw-r--r--sound/soc/amd/acp/acp-pdm.c13
-rw-r--r--sound/soc/amd/acp/acp-platform.c6
-rw-r--r--sound/soc/amd/acp/acp-rembrandt.c184
-rw-r--r--sound/soc/amd/acp/acp-renoir.c115
-rw-r--r--sound/soc/amd/acp/acp-sof-mach.c16
-rw-r--r--sound/soc/amd/acp/amd.h58
-rw-r--r--sound/soc/amd/mach-config.h1
-rw-r--r--sound/soc/amd/ps/acp63.h22
-rw-r--r--sound/soc/amd/ps/pci-ps.c4
-rw-r--r--sound/soc/amd/ps/ps-sdw-dma.c21
-rw-r--r--sound/soc/amd/vangogh/acp5x-mach.c149
-rw-r--r--sound/soc/bcm/bcm63xx-i2s-whistler.c16
-rw-r--r--sound/soc/codecs/Kconfig15
-rw-r--r--sound/soc/codecs/Makefile2
-rw-r--r--sound/soc/codecs/ad1836.c2
-rw-r--r--sound/soc/codecs/ad1980.c2
-rw-r--r--sound/soc/codecs/adau1372.c2
-rw-r--r--sound/soc/codecs/adau1373.c2
-rw-r--r--sound/soc/codecs/adau1701.c2
-rw-r--r--sound/soc/codecs/adau1761.c2
-rw-r--r--sound/soc/codecs/adau1781.c2
-rw-r--r--sound/soc/codecs/adau1977.c2
-rw-r--r--sound/soc/codecs/adau7118-i2c.c2
-rw-r--r--sound/soc/codecs/adav80x.c2
-rw-r--r--sound/soc/codecs/audio-iio-aux.c344
-rw-r--r--sound/soc/codecs/cs35l36.c2
-rw-r--r--sound/soc/codecs/cs35l41-lib.c4
-rw-r--r--sound/soc/codecs/cs35l45-tables.c4
-rw-r--r--sound/soc/codecs/cs35l56-i2c.c14
-rw-r--r--sound/soc/codecs/cs35l56-sdw.c72
-rw-r--r--sound/soc/codecs/cs35l56-shared.c482
-rw-r--r--sound/soc/codecs/cs35l56-spi.c10
-rw-r--r--sound/soc/codecs/cs35l56.c651
-rw-r--r--sound/soc/codecs/cs35l56.h15
-rw-r--r--sound/soc/codecs/cs4265.c2
-rw-r--r--sound/soc/codecs/cs4270.c2
-rw-r--r--sound/soc/codecs/cs42l51-i2c.c6
-rw-r--r--sound/soc/codecs/cs42l51.c9
-rw-r--r--sound/soc/codecs/cs42l51.h1
-rw-r--r--sound/soc/codecs/cs42l52.c2
-rw-r--r--sound/soc/codecs/cs42l56.c2
-rw-r--r--sound/soc/codecs/cs42xx8.c2
-rw-r--r--sound/soc/codecs/cs4349.c2
-rw-r--r--sound/soc/codecs/es8316.c5
-rw-r--r--sound/soc/codecs/es8326.c75
-rw-r--r--sound/soc/codecs/es8326.h9
-rw-r--r--sound/soc/codecs/lpass-rx-macro.c10
-rw-r--r--sound/soc/codecs/lpass-tx-macro.c10
-rw-r--r--sound/soc/codecs/lpass-va-macro.c6
-rw-r--r--sound/soc/codecs/lpass-wsa-macro.c10
-rw-r--r--sound/soc/codecs/msm8916-wcd-analog.c56
-rw-r--r--sound/soc/codecs/nau8825.c93
-rw-r--r--sound/soc/codecs/rt5640.c13
-rw-r--r--sound/soc/codecs/rt5645.c56
-rw-r--r--sound/soc/codecs/rt5677.c117
-rw-r--r--sound/soc/codecs/rt5677.h92
-rw-r--r--sound/soc/codecs/rt722-sdca-sdw.c2
-rw-r--r--sound/soc/codecs/rt722-sdca.c3
-rw-r--r--sound/soc/codecs/tas2781-i2c.c1
-rw-r--r--sound/soc/codecs/wcd-mbhc-v2.c57
-rw-r--r--sound/soc/codecs/wcd9335.c2
-rw-r--r--sound/soc/codecs/wcd934x.c20
-rw-r--r--sound/soc/codecs/wcd938x-sdw.c2
-rw-r--r--sound/soc/codecs/wcd938x.c99
-rw-r--r--sound/soc/codecs/wm2200.c2
-rw-r--r--sound/soc/codecs/wm5100.c2
-rw-r--r--sound/soc/codecs/wm8510.c2
-rw-r--r--sound/soc/codecs/wm8523.c2
-rw-r--r--sound/soc/codecs/wm8580.c2
-rw-r--r--sound/soc/codecs/wm8711.c2
-rw-r--r--sound/soc/codecs/wm8728.c2
-rw-r--r--sound/soc/codecs/wm8731.c2
-rw-r--r--sound/soc/codecs/wm8737.c2
-rw-r--r--sound/soc/codecs/wm8741.c2
-rw-r--r--sound/soc/codecs/wm8750.c2
-rw-r--r--sound/soc/codecs/wm8753.c2
-rw-r--r--sound/soc/codecs/wm8770.c2
-rw-r--r--sound/soc/codecs/wm8776.c2
-rw-r--r--sound/soc/codecs/wm8804.c2
-rw-r--r--sound/soc/codecs/wm8900.c2
-rw-r--r--sound/soc/codecs/wm8903.c2
-rw-r--r--sound/soc/codecs/wm8904.c2
-rw-r--r--sound/soc/codecs/wm8940.c2
-rw-r--r--sound/soc/codecs/wm8955.c2
-rw-r--r--sound/soc/codecs/wm8960.c12
-rw-r--r--sound/soc/codecs/wm8961.c2
-rw-r--r--sound/soc/codecs/wm8962.c2
-rw-r--r--sound/soc/codecs/wm8971.c2
-rw-r--r--sound/soc/codecs/wm8978.c2
-rw-r--r--sound/soc/codecs/wm8983.c2
-rw-r--r--sound/soc/codecs/wm8985.c2
-rw-r--r--sound/soc/codecs/wm8988.c2
-rw-r--r--sound/soc/codecs/wm8991.c2
-rw-r--r--sound/soc/codecs/wm8993.c2
-rw-r--r--sound/soc/codecs/wm8995.c2
-rw-r--r--sound/soc/codecs/wm8996.c2
-rw-r--r--sound/soc/codecs/wm9081.c2
-rw-r--r--sound/soc/codecs/wm9090.c2
-rw-r--r--sound/soc/codecs/wm9705.c2
-rw-r--r--sound/soc/codecs/wm9712.c2
-rw-r--r--sound/soc/codecs/wm9713.c2
-rw-r--r--sound/soc/codecs/wsa881x.c2
-rw-r--r--sound/soc/codecs/wsa883x.c2
-rw-r--r--sound/soc/dwc/dwc-i2s.c65
-rw-r--r--sound/soc/dwc/dwc-pcm.c8
-rw-r--r--sound/soc/dwc/local.h24
-rw-r--r--sound/soc/fsl/fsl_rpmsg.c8
-rw-r--r--sound/soc/fsl/fsl_sai.c8
-rw-r--r--sound/soc/fsl/fsl_sai.h1
-rw-r--r--sound/soc/fsl/fsl_spdif.c8
-rw-r--r--sound/soc/fsl/fsl_spdif.h6
-rw-r--r--sound/soc/fsl/imx-pcm-rpmsg.c12
-rw-r--r--sound/soc/generic/audio-graph-card.c2
-rw-r--r--sound/soc/generic/audio-graph-card2.c2
-rw-r--r--sound/soc/generic/simple-card-utils.c23
-rw-r--r--sound/soc/generic/simple-card.c66
-rw-r--r--sound/soc/intel/avs/board_selection.c16
-rw-r--r--sound/soc/intel/avs/boards/Kconfig20
-rw-r--r--sound/soc/intel/avs/boards/Makefile4
-rw-r--r--sound/soc/intel/avs/boards/es8336.c315
-rw-r--r--sound/soc/intel/avs/boards/rt5663.c254
-rw-r--r--sound/soc/intel/avs/boards/rt5682.c67
-rw-r--r--sound/soc/intel/boards/Kconfig5
-rw-r--r--sound/soc/intel/boards/sof_rt5682.c80
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-jsl-match.c12
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-afe-pcm.c4
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-afe-pcm.c8
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-mt6359.c15
-rw-r--r--sound/soc/pxa/pxa2xx-i2s.c13
-rw-r--r--sound/soc/qcom/qdsp6/q6afe-dai.c2
-rw-r--r--sound/soc/qcom/qdsp6/q6apm-dai.c1
-rw-r--r--sound/soc/qcom/qdsp6/q6apm.c9
-rw-r--r--sound/soc/qcom/qdsp6/topology.c4
-rw-r--r--sound/soc/soc-core.c279
-rw-r--r--sound/soc/soc-topology.c10
-rw-r--r--sound/soc/sof/amd/Kconfig8
-rw-r--r--sound/soc/sof/amd/Makefile1
-rw-r--r--sound/soc/sof/amd/acp-common.c4
-rw-r--r--sound/soc/sof/amd/acp-ipc.c26
-rw-r--r--sound/soc/sof/amd/acp-probes.c147
-rw-r--r--sound/soc/sof/amd/acp.c44
-rw-r--r--sound/soc/sof/amd/acp.h13
-rw-r--r--sound/soc/sof/amd/pci-rmb.c2
-rw-r--r--sound/soc/sof/amd/pci-rn.c2
-rw-r--r--sound/soc/sof/ipc3-dtrace.c9
-rw-r--r--sound/soc/sof/sof-client-probes.c1
-rw-r--r--sound/soc/starfive/jh7110_tdm.c5
-rw-r--r--sound/soc/tegra/tegra20_ac97.c3
-rw-r--r--sound/soc/tegra/tegra210_adx.c34
-rw-r--r--sound/soc/tegra/tegra210_amx.c40
-rw-r--r--sound/soc/ti/omap-dmic.c4
-rw-r--r--sound/soc/ti/omap-mcbsp.c4
-rw-r--r--sound/soc/ti/omap-mcpdm.c3
174 files changed, 5183 insertions, 1635 deletions
diff --git a/sound/Kconfig b/sound/Kconfig
index 0ddfb717b81d..f0e15822e858 100644
--- a/sound/Kconfig
+++ b/sound/Kconfig
@@ -39,8 +39,6 @@ config SOUND_OSS_CORE_PRECLAIM
source "sound/oss/dmasound/Kconfig"
-if !UML
-
menuconfig SND
tristate "Advanced Linux Sound Architecture"
help
@@ -103,8 +101,6 @@ source "sound/virtio/Kconfig"
endif # SND
-endif # !UML
-
endif # SOUND
config AC97_BUS
diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c
index 9b80f8275026..f3f14ff0f80f 100644
--- a/sound/core/seq/seq_ports.c
+++ b/sound/core/seq/seq_ports.c
@@ -149,6 +149,7 @@ int snd_seq_create_port(struct snd_seq_client *client, int port,
write_lock_irq(&client->ports_lock);
list_for_each_entry(p, &client->ports_list_head, list) {
if (p->addr.port == port) {
+ kfree(new_port);
num = -EBUSY;
goto unlock;
}
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
index 886255a03e8b..dd6922267a4b 100644
--- a/sound/pci/hda/Kconfig
+++ b/sound/pci/hda/Kconfig
@@ -130,6 +130,37 @@ config SND_HDA_SCODEC_CS35L41_SPI
comment "Set to Y if you want auto-loading the side codec driver"
depends on SND_HDA=y && SND_HDA_SCODEC_CS35L41_SPI=m
+config SND_HDA_SCODEC_CS35L56
+ tristate
+
+config SND_HDA_SCODEC_CS35L56_I2C
+ tristate "Build CS35L56 HD-audio side codec support for I2C Bus"
+ depends on I2C
+ depends on ACPI || COMPILE_TEST
+ depends on SND_SOC
+ select CS_DSP
+ select SND_HDA_GENERIC
+ select SND_SOC_CS35L56_SHARED
+ select SND_HDA_SCODEC_CS35L56
+ select SND_HDA_CS_DSP_CONTROLS
+ help
+ Say Y or M here to include CS35L56 amplifier support with
+ I2C control.
+
+config SND_HDA_SCODEC_CS35L56_SPI
+ tristate "Build CS35L56 HD-audio side codec support for SPI Bus"
+ depends on SPI_MASTER
+ depends on ACPI || COMPILE_TEST
+ depends on SND_SOC
+ select CS_DSP
+ select SND_HDA_GENERIC
+ select SND_SOC_CS35L56_SHARED
+ select SND_HDA_SCODEC_CS35L56
+ select SND_HDA_CS_DSP_CONTROLS
+ help
+ Say Y or M here to include CS35L56 amplifier support with
+ SPI control.
+
config SND_HDA_CODEC_REALTEK
tristate "Build Realtek HD-audio codec support"
select SND_HDA_GENERIC
diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile
index 00d306104484..c6e6509e7b8e 100644
--- a/sound/pci/hda/Makefile
+++ b/sound/pci/hda/Makefile
@@ -31,6 +31,9 @@ snd-hda-codec-hdmi-objs := patch_hdmi.o hda_eld.o
snd-hda-scodec-cs35l41-objs := cs35l41_hda.o
snd-hda-scodec-cs35l41-i2c-objs := cs35l41_hda_i2c.o
snd-hda-scodec-cs35l41-spi-objs := cs35l41_hda_spi.o
+snd-hda-scodec-cs35l56-objs := cs35l56_hda.o
+snd-hda-scodec-cs35l56-i2c-objs := cs35l56_hda_i2c.o
+snd-hda-scodec-cs35l56-spi-objs := cs35l56_hda_spi.o
snd-hda-cs-dsp-ctls-objs := hda_cs_dsp_ctl.o
# common driver
@@ -55,6 +58,9 @@ obj-$(CONFIG_SND_HDA_CODEC_HDMI) += snd-hda-codec-hdmi.o
obj-$(CONFIG_SND_HDA_SCODEC_CS35L41) += snd-hda-scodec-cs35l41.o
obj-$(CONFIG_SND_HDA_SCODEC_CS35L41_I2C) += snd-hda-scodec-cs35l41-i2c.o
obj-$(CONFIG_SND_HDA_SCODEC_CS35L41_SPI) += snd-hda-scodec-cs35l41-spi.o
+obj-$(CONFIG_SND_HDA_SCODEC_CS35L56) += snd-hda-scodec-cs35l56.o
+obj-$(CONFIG_SND_HDA_SCODEC_CS35L56_I2C) += snd-hda-scodec-cs35l56-i2c.o
+obj-$(CONFIG_SND_HDA_SCODEC_CS35L56_SPI) += snd-hda-scodec-cs35l56-spi.o
obj-$(CONFIG_SND_HDA_CS_DSP_CONTROLS) += snd-hda-cs-dsp-ctls.o
# this must be the last entry after codec drivers;
diff --git a/sound/pci/hda/cs35l56_hda.c b/sound/pci/hda/cs35l56_hda.c
new file mode 100644
index 000000000000..71e95e64f8a4
--- /dev/null
+++ b/sound/pci/hda/cs35l56_hda.c
@@ -0,0 +1,995 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// HDA audio driver for Cirrus Logic CS35L56 smart amp
+//
+// Copyright (C) 2023 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+//
+
+#include <linux/acpi.h>
+#include <linux/debugfs.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/hda_codec.h>
+#include <sound/tlv.h>
+#include "cs35l56_hda.h"
+#include "hda_component.h"
+#include "hda_cs_dsp_ctl.h"
+#include "hda_generic.h"
+
+ /*
+ * The cs35l56_hda_dai_config[] reg sequence configures the device as
+ * ASP1_BCLK_FREQ = 3.072 MHz
+ * ASP1_RX_WIDTH = 32 cycles per slot, ASP1_TX_WIDTH = 32 cycles per slot, ASP1_FMT = I2S
+ * ASP1_DOUT_HIZ_CONTROL = Hi-Z during unused timeslots
+ * ASP1_RX_WL = 24 bits per sample
+ * ASP1_TX_WL = 24 bits per sample
+ * ASP1_RXn_EN 1..3 and ASP1_TXn_EN 1..4 disabled
+ */
+static const struct reg_sequence cs35l56_hda_dai_config[] = {
+ { CS35L56_ASP1_CONTROL1, 0x00000021 },
+ { CS35L56_ASP1_CONTROL2, 0x20200200 },
+ { CS35L56_ASP1_CONTROL3, 0x00000003 },
+ { CS35L56_ASP1_DATA_CONTROL5, 0x00000018 },
+ { CS35L56_ASP1_DATA_CONTROL1, 0x00000018 },
+ { CS35L56_ASP1_ENABLES1, 0x00000000 },
+};
+
+static void cs35l56_hda_play(struct cs35l56_hda *cs35l56)
+{
+ unsigned int val;
+ int ret;
+
+ pm_runtime_get_sync(cs35l56->base.dev);
+ ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_PLAY);
+ if (ret == 0) {
+ /* Wait for firmware to enter PS0 power state */
+ ret = regmap_read_poll_timeout(cs35l56->base.regmap,
+ CS35L56_TRANSDUCER_ACTUAL_PS,
+ val, (val == CS35L56_PS0),
+ CS35L56_PS0_POLL_US,
+ CS35L56_PS0_TIMEOUT_US);
+ if (ret)
+ dev_warn(cs35l56->base.dev, "PS0 wait failed: %d\n", ret);
+ }
+ regmap_set_bits(cs35l56->base.regmap, CS35L56_ASP1_ENABLES1,
+ BIT(CS35L56_ASP_RX1_EN_SHIFT) | BIT(CS35L56_ASP_RX2_EN_SHIFT) |
+ cs35l56->asp_tx_mask);
+ cs35l56->playing = true;
+}
+
+static void cs35l56_hda_pause(struct cs35l56_hda *cs35l56)
+{
+ cs35l56->playing = false;
+ cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_PAUSE);
+ regmap_clear_bits(cs35l56->base.regmap, CS35L56_ASP1_ENABLES1,
+ BIT(CS35L56_ASP_RX1_EN_SHIFT) | BIT(CS35L56_ASP_RX2_EN_SHIFT) |
+ BIT(CS35L56_ASP_TX1_EN_SHIFT) | BIT(CS35L56_ASP_TX2_EN_SHIFT) |
+ BIT(CS35L56_ASP_TX3_EN_SHIFT) | BIT(CS35L56_ASP_TX4_EN_SHIFT));
+
+ pm_runtime_mark_last_busy(cs35l56->base.dev);
+ pm_runtime_put_autosuspend(cs35l56->base.dev);
+}
+
+static void cs35l56_hda_playback_hook(struct device *dev, int action)
+{
+ struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
+
+ dev_dbg(cs35l56->base.dev, "%s()%d: action: %d\n", __func__, __LINE__, action);
+
+ switch (action) {
+ case HDA_GEN_PCM_ACT_PREPARE:
+ if (cs35l56->playing)
+ break;
+
+ /* If we're suspended: flag that resume should start playback */
+ if (cs35l56->suspended) {
+ cs35l56->playing = true;
+ break;
+ }
+
+ cs35l56_hda_play(cs35l56);
+ break;
+ case HDA_GEN_PCM_ACT_CLEANUP:
+ if (!cs35l56->playing)
+ break;
+
+ cs35l56_hda_pause(cs35l56);
+ break;
+ default:
+ break;
+ }
+}
+
+static int __maybe_unused cs35l56_hda_runtime_suspend(struct device *dev)
+{
+ struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
+
+ if (cs35l56->cs_dsp.booted)
+ cs_dsp_stop(&cs35l56->cs_dsp);
+
+ return cs35l56_runtime_suspend_common(&cs35l56->base);
+}
+
+static int __maybe_unused cs35l56_hda_runtime_resume(struct device *dev)
+{
+ struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = cs35l56_runtime_resume_common(&cs35l56->base, false);
+ if (ret < 0)
+ return ret;
+
+ if (cs35l56->cs_dsp.booted) {
+ ret = cs_dsp_run(&cs35l56->cs_dsp);
+ if (ret) {
+ dev_dbg(cs35l56->base.dev, "%s: cs_dsp_run ret %d\n", __func__, ret);
+ goto err;
+ }
+ }
+
+ return 0;
+
+err:
+ cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_ALLOW_AUTO_HIBERNATE);
+ regmap_write(cs35l56->base.regmap, CS35L56_DSP_VIRTUAL1_MBOX_1,
+ CS35L56_MBOX_CMD_HIBERNATE_NOW);
+
+ regcache_cache_only(cs35l56->base.regmap, true);
+
+ return ret;
+}
+
+static int cs35l56_hda_mixer_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = CS35L56_NUM_INPUT_SRC;
+ if (uinfo->value.enumerated.item >= CS35L56_NUM_INPUT_SRC)
+ uinfo->value.enumerated.item = CS35L56_NUM_INPUT_SRC - 1;
+ strscpy(uinfo->value.enumerated.name, cs35l56_tx_input_texts[uinfo->value.enumerated.item],
+ sizeof(uinfo->value.enumerated.name));
+
+ return 0;
+}
+
+static int cs35l56_hda_mixer_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct cs35l56_hda *cs35l56 = (struct cs35l56_hda *)kcontrol->private_data;
+ unsigned int reg_val;
+ int i;
+
+ regmap_read(cs35l56->base.regmap, kcontrol->private_value, &reg_val);
+ reg_val &= CS35L56_ASP_TXn_SRC_MASK;
+
+ for (i = 0; i < CS35L56_NUM_INPUT_SRC; ++i) {
+ if (cs35l56_tx_input_values[i] == reg_val) {
+ ucontrol->value.enumerated.item[0] = i;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int cs35l56_hda_mixer_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct cs35l56_hda *cs35l56 = (struct cs35l56_hda *)kcontrol->private_data;
+ unsigned int item = ucontrol->value.enumerated.item[0];
+ bool changed;
+
+ if (item >= CS35L56_NUM_INPUT_SRC)
+ return -EINVAL;
+
+ regmap_update_bits_check(cs35l56->base.regmap, kcontrol->private_value,
+ CS35L56_INPUT_MASK, cs35l56_tx_input_values[item],
+ &changed);
+
+ return changed;
+}
+
+static int cs35l56_hda_posture_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = CS35L56_MAIN_POSTURE_MIN;
+ uinfo->value.integer.max = CS35L56_MAIN_POSTURE_MAX;
+ return 0;
+}
+
+static int cs35l56_hda_posture_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct cs35l56_hda *cs35l56 = (struct cs35l56_hda *)kcontrol->private_data;
+ unsigned int pos;
+ int ret;
+
+ ret = regmap_read(cs35l56->base.regmap, CS35L56_MAIN_POSTURE_NUMBER, &pos);
+ if (ret)
+ return ret;
+
+ ucontrol->value.integer.value[0] = pos;
+
+ return ret;
+}
+
+static int cs35l56_hda_posture_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct cs35l56_hda *cs35l56 = (struct cs35l56_hda *)kcontrol->private_data;
+ unsigned long pos = ucontrol->value.integer.value[0];
+ bool changed;
+ int ret;
+
+ if ((pos < CS35L56_MAIN_POSTURE_MIN) ||
+ (pos > CS35L56_MAIN_POSTURE_MAX))
+ return -EINVAL;
+
+ ret = regmap_update_bits_check(cs35l56->base.regmap,
+ CS35L56_MAIN_POSTURE_NUMBER,
+ CS35L56_MAIN_POSTURE_MASK,
+ pos, &changed);
+ if (ret)
+ return ret;
+
+ return changed;
+}
+
+static const struct {
+ const char *name;
+ unsigned int reg;
+} cs35l56_hda_mixer_controls[] = {
+ { "ASP1 TX1 Source", CS35L56_ASP1TX1_INPUT },
+ { "ASP1 TX2 Source", CS35L56_ASP1TX2_INPUT },
+ { "ASP1 TX3 Source", CS35L56_ASP1TX3_INPUT },
+ { "ASP1 TX4 Source", CS35L56_ASP1TX4_INPUT },
+};
+
+static const DECLARE_TLV_DB_SCALE(cs35l56_hda_vol_tlv, -10000, 25, 0);
+
+static int cs35l56_hda_vol_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.step = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = CS35L56_MAIN_RENDER_USER_VOLUME_MAX -
+ CS35L56_MAIN_RENDER_USER_VOLUME_MIN;
+
+ return 0;
+}
+
+static int cs35l56_hda_vol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct cs35l56_hda *cs35l56 = (struct cs35l56_hda *)kcontrol->private_data;
+ unsigned int raw_vol;
+ int vol;
+ int ret;
+
+ ret = regmap_read(cs35l56->base.regmap, CS35L56_MAIN_RENDER_USER_VOLUME, &raw_vol);
+
+ if (ret)
+ return ret;
+
+ vol = (s16)(raw_vol & 0xFFFF);
+ vol >>= CS35L56_MAIN_RENDER_USER_VOLUME_SHIFT;
+
+ if (vol & BIT(CS35L56_MAIN_RENDER_USER_VOLUME_SIGNBIT))
+ vol |= ~((int)(BIT(CS35L56_MAIN_RENDER_USER_VOLUME_SIGNBIT) - 1));
+
+ ucontrol->value.integer.value[0] = vol - CS35L56_MAIN_RENDER_USER_VOLUME_MIN;
+
+ return 0;
+}
+
+static int cs35l56_hda_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct cs35l56_hda *cs35l56 = (struct cs35l56_hda *)kcontrol->private_data;
+ long vol = ucontrol->value.integer.value[0];
+ unsigned int raw_vol;
+ bool changed;
+ int ret;
+
+ if ((vol < 0) || (vol > (CS35L56_MAIN_RENDER_USER_VOLUME_MAX -
+ CS35L56_MAIN_RENDER_USER_VOLUME_MIN)))
+ return -EINVAL;
+
+ raw_vol = (vol + CS35L56_MAIN_RENDER_USER_VOLUME_MIN) <<
+ CS35L56_MAIN_RENDER_USER_VOLUME_SHIFT;
+
+ ret = regmap_update_bits_check(cs35l56->base.regmap,
+ CS35L56_MAIN_RENDER_USER_VOLUME,
+ CS35L56_MAIN_RENDER_USER_VOLUME_MASK,
+ raw_vol, &changed);
+ if (ret)
+ return ret;
+
+ return changed;
+}
+
+static void cs35l56_hda_create_controls(struct cs35l56_hda *cs35l56)
+{
+ struct snd_kcontrol_new ctl_template = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = cs35l56_hda_posture_info,
+ .get = cs35l56_hda_posture_get,
+ .put = cs35l56_hda_posture_put,
+ };
+ char name[64];
+ int i;
+
+ snprintf(name, sizeof(name), "%s Posture Number", cs35l56->amp_name);
+ ctl_template.name = name;
+ cs35l56->posture_ctl = snd_ctl_new1(&ctl_template, cs35l56);
+ if (snd_ctl_add(cs35l56->codec->card, cs35l56->posture_ctl))
+ dev_err(cs35l56->base.dev, "Failed to add KControl: %s\n", ctl_template.name);
+
+ /* Mixer controls */
+ ctl_template.info = cs35l56_hda_mixer_info;
+ ctl_template.get = cs35l56_hda_mixer_get;
+ ctl_template.put = cs35l56_hda_mixer_put;
+
+ BUILD_BUG_ON(ARRAY_SIZE(cs35l56->mixer_ctl) != ARRAY_SIZE(cs35l56_hda_mixer_controls));
+
+ for (i = 0; i < ARRAY_SIZE(cs35l56_hda_mixer_controls); ++i) {
+ snprintf(name, sizeof(name), "%s %s", cs35l56->amp_name,
+ cs35l56_hda_mixer_controls[i].name);
+ ctl_template.private_value = cs35l56_hda_mixer_controls[i].reg;
+ cs35l56->mixer_ctl[i] = snd_ctl_new1(&ctl_template, cs35l56);
+ if (snd_ctl_add(cs35l56->codec->card, cs35l56->mixer_ctl[i])) {
+ dev_err(cs35l56->base.dev, "Failed to add KControl: %s\n",
+ ctl_template.name);
+ }
+ }
+
+ ctl_template.info = cs35l56_hda_vol_info;
+ ctl_template.get = cs35l56_hda_vol_get;
+ ctl_template.put = cs35l56_hda_vol_put;
+ ctl_template.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ);
+ ctl_template.tlv.p = cs35l56_hda_vol_tlv;
+ snprintf(name, sizeof(name), "%s Speaker Playback Volume", cs35l56->amp_name);
+ ctl_template.name = name;
+ cs35l56->volume_ctl = snd_ctl_new1(&ctl_template, cs35l56);
+ if (snd_ctl_add(cs35l56->codec->card, cs35l56->volume_ctl))
+ dev_err(cs35l56->base.dev, "Failed to add KControl: %s\n", ctl_template.name);
+}
+
+static void cs35l56_hda_remove_controls(struct cs35l56_hda *cs35l56)
+{
+ int i;
+
+ for (i = ARRAY_SIZE(cs35l56->mixer_ctl) - 1; i >= 0; i--)
+ snd_ctl_remove(cs35l56->codec->card, cs35l56->mixer_ctl[i]);
+
+ snd_ctl_remove(cs35l56->codec->card, cs35l56->posture_ctl);
+ snd_ctl_remove(cs35l56->codec->card, cs35l56->volume_ctl);
+}
+
+static const struct cs_dsp_client_ops cs35l56_hda_client_ops = {
+ .control_remove = hda_cs_dsp_control_remove,
+};
+
+static int cs35l56_hda_request_firmware_file(struct cs35l56_hda *cs35l56,
+ const struct firmware **firmware, char **filename,
+ const char *dir, const char *system_name,
+ const char *amp_name,
+ const char *filetype)
+{
+ char *s, c;
+ int ret = 0;
+
+ if (system_name && amp_name)
+ *filename = kasprintf(GFP_KERNEL, "%scs35l56%s-%02x-dsp1-misc-%s-%s.%s", dir,
+ cs35l56->base.secured ? "s" : "", cs35l56->base.rev,
+ system_name, amp_name, filetype);
+ else if (system_name)
+ *filename = kasprintf(GFP_KERNEL, "%scs35l56%s-%02x-dsp1-misc-%s.%s", dir,
+ cs35l56->base.secured ? "s" : "", cs35l56->base.rev,
+ system_name, filetype);
+ else
+ *filename = kasprintf(GFP_KERNEL, "%scs35l56%s-%02x-dsp1-misc.%s", dir,
+ cs35l56->base.secured ? "s" : "", cs35l56->base.rev,
+ filetype);
+
+ if (!*filename)
+ return -ENOMEM;
+
+ /*
+ * Make sure that filename is lower-case and any non alpha-numeric
+ * characters except full stop and forward slash are replaced with
+ * hyphens.
+ */
+ s = *filename;
+ while (*s) {
+ c = *s;
+ if (isalnum(c))
+ *s = tolower(c);
+ else if (c != '.' && c != '/')
+ *s = '-';
+ s++;
+ }
+
+ ret = firmware_request_nowarn(firmware, *filename, cs35l56->base.dev);
+ if (ret) {
+ dev_dbg(cs35l56->base.dev, "Failed to request '%s'\n", *filename);
+ kfree(*filename);
+ *filename = NULL;
+ return ret;
+ }
+
+ dev_dbg(cs35l56->base.dev, "Found '%s'\n", *filename);
+
+ return 0;
+}
+
+static const char cirrus_dir[] = "cirrus/";
+static void cs35l56_hda_request_firmware_files(struct cs35l56_hda *cs35l56,
+ const struct firmware **wmfw_firmware,
+ char **wmfw_filename,
+ const struct firmware **coeff_firmware,
+ char **coeff_filename)
+{
+ const char *system_name = cs35l56->system_name;
+ const char *amp_name = cs35l56->amp_name;
+ int ret;
+
+ if (system_name && amp_name) {
+ if (!cs35l56_hda_request_firmware_file(cs35l56, wmfw_firmware, wmfw_filename,
+ cirrus_dir, system_name, amp_name, "wmfw")) {
+ cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename,
+ cirrus_dir, system_name, amp_name, "bin");
+ return;
+ }
+ }
+
+ if (system_name) {
+ if (!cs35l56_hda_request_firmware_file(cs35l56, wmfw_firmware, wmfw_filename,
+ cirrus_dir, system_name, NULL, "wmfw")) {
+ if (amp_name)
+ cs35l56_hda_request_firmware_file(cs35l56,
+ coeff_firmware, coeff_filename,
+ cirrus_dir, system_name,
+ amp_name, "bin");
+ if (!*coeff_firmware)
+ cs35l56_hda_request_firmware_file(cs35l56,
+ coeff_firmware, coeff_filename,
+ cirrus_dir, system_name,
+ NULL, "bin");
+ return;
+ }
+ }
+
+ ret = cs35l56_hda_request_firmware_file(cs35l56, wmfw_firmware, wmfw_filename,
+ cirrus_dir, NULL, NULL, "wmfw");
+ if (!ret) {
+ cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename,
+ cirrus_dir, NULL, NULL, "bin");
+ return;
+ }
+
+ /* When a firmware file is not found must still search for the coeff files */
+ if (system_name) {
+ if (amp_name)
+ cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename,
+ cirrus_dir, system_name, amp_name, "bin");
+ if (!*coeff_firmware)
+ cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename,
+ cirrus_dir, system_name, NULL, "bin");
+ }
+
+ if (!*coeff_firmware)
+ cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename,
+ cirrus_dir, NULL, NULL, "bin");
+}
+
+static void cs35l56_hda_release_firmware_files(const struct firmware *wmfw_firmware,
+ char *wmfw_filename,
+ const struct firmware *coeff_firmware,
+ char *coeff_filename)
+{
+ if (wmfw_firmware)
+ release_firmware(wmfw_firmware);
+ kfree(wmfw_filename);
+
+ if (coeff_firmware)
+ release_firmware(coeff_firmware);
+ kfree(coeff_filename);
+}
+
+static void cs35l56_hda_add_dsp_controls(struct cs35l56_hda *cs35l56)
+{
+ struct hda_cs_dsp_ctl_info info;
+
+ info.device_name = cs35l56->amp_name;
+ info.fw_type = HDA_CS_DSP_FW_MISC;
+ info.card = cs35l56->codec->card;
+
+ hda_cs_dsp_add_controls(&cs35l56->cs_dsp, &info);
+}
+
+static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
+{
+ const struct firmware *coeff_firmware = NULL;
+ const struct firmware *wmfw_firmware = NULL;
+ char *coeff_filename = NULL;
+ char *wmfw_filename = NULL;
+ int ret = 0;
+
+ cs35l56_hda_request_firmware_files(cs35l56, &wmfw_firmware, &wmfw_filename,
+ &coeff_firmware, &coeff_filename);
+
+ /* Nothing to do - no firmware files were found to download */
+ if (!wmfw_filename && !coeff_filename)
+ return 0;
+
+ mutex_lock(&cs35l56->base.irq_lock);
+ pm_runtime_get_sync(cs35l56->base.dev);
+
+ /*
+ * When the device is running in secure mode the firmware files can
+ * only contain insecure tunings and therefore we do not need to
+ * shutdown the firmware to apply them and can use the lower cost
+ * reinit sequence instead.
+ */
+ if (!cs35l56->base.secured) {
+ ret = cs35l56_firmware_shutdown(&cs35l56->base);
+ if (ret)
+ goto err;
+ }
+
+ ret = cs_dsp_power_up(&cs35l56->cs_dsp, wmfw_firmware, wmfw_filename,
+ coeff_firmware, coeff_filename, "misc");
+ if (ret) {
+ dev_dbg(cs35l56->base.dev, "%s: cs_dsp_power_up ret %d\n", __func__, ret);
+ goto err;
+ }
+
+ if (wmfw_filename)
+ dev_dbg(cs35l56->base.dev, "Loaded WMFW Firmware: %s\n", wmfw_filename);
+
+ if (coeff_filename)
+ dev_dbg(cs35l56->base.dev, "Loaded Coefficients: %s\n", coeff_filename);
+
+ ret = cs_dsp_run(&cs35l56->cs_dsp);
+ if (ret) {
+ dev_dbg(cs35l56->base.dev, "%s: cs_dsp_run ret %d\n", __func__, ret);
+ goto err;
+ }
+
+ if (cs35l56->base.secured) {
+ ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT);
+ if (ret)
+ goto err;
+ } else {
+ /* Reset the device and wait for it to boot */
+ cs35l56_system_reset(&cs35l56->base, false);
+ ret = cs35l56_wait_for_firmware_boot(&cs35l56->base);
+ if (ret)
+ goto err;
+ }
+
+ /* Disable auto-hibernate so that runtime_pm has control */
+ ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE);
+ if (ret)
+ goto err;
+
+ regcache_mark_dirty(cs35l56->base.regmap);
+ regcache_sync(cs35l56->base.regmap);
+
+ regmap_clear_bits(cs35l56->base.regmap, CS35L56_PROTECTION_STATUS,
+ CS35L56_FIRMWARE_MISSING);
+ cs35l56->base.fw_patched = true;
+err:
+ pm_runtime_put(cs35l56->base.dev);
+ mutex_unlock(&cs35l56->base.irq_lock);
+
+ cs35l56_hda_release_firmware_files(wmfw_firmware, wmfw_filename,
+ coeff_firmware, coeff_filename);
+
+ return ret;
+}
+
+static int cs35l56_hda_bind(struct device *dev, struct device *master, void *master_data)
+{
+ struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
+ struct hda_component *comps = master_data;
+ int ret;
+
+ if (!comps || cs35l56->index < 0 || cs35l56->index >= HDA_MAX_COMPONENTS)
+ return -EINVAL;
+
+ comps = &comps[cs35l56->index];
+ if (comps->dev)
+ return -EBUSY;
+
+ comps->dev = dev;
+ cs35l56->codec = comps->codec;
+ strscpy(comps->name, dev_name(dev), sizeof(comps->name));
+ comps->playback_hook = cs35l56_hda_playback_hook;
+
+ ret = cs35l56_hda_fw_load(cs35l56);
+ if (ret)
+ return ret;
+
+ cs35l56_hda_create_controls(cs35l56);
+ cs35l56_hda_add_dsp_controls(cs35l56);
+
+#if IS_ENABLED(CONFIG_SND_DEBUG)
+ cs35l56->debugfs_root = debugfs_create_dir(dev_name(cs35l56->base.dev), sound_debugfs_root);
+ cs_dsp_init_debugfs(&cs35l56->cs_dsp, cs35l56->debugfs_root);
+#endif
+
+ dev_dbg(cs35l56->base.dev, "Bound\n");
+
+ return 0;
+}
+
+static void cs35l56_hda_unbind(struct device *dev, struct device *master, void *master_data)
+{
+ struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
+ struct hda_component *comps = master_data;
+
+ cs35l56_hda_remove_controls(cs35l56);
+
+#if IS_ENABLED(CONFIG_SND_DEBUG)
+ cs_dsp_cleanup_debugfs(&cs35l56->cs_dsp);
+ debugfs_remove_recursive(cs35l56->debugfs_root);
+#endif
+
+ cs_dsp_remove(&cs35l56->cs_dsp);
+
+ if (comps[cs35l56->index].dev == dev)
+ memset(&comps[cs35l56->index], 0, sizeof(*comps));
+
+ dev_dbg(cs35l56->base.dev, "Unbound\n");
+}
+
+static const struct component_ops cs35l56_hda_comp_ops = {
+ .bind = cs35l56_hda_bind,
+ .unbind = cs35l56_hda_unbind,
+};
+
+static int cs35l56_hda_system_suspend(struct device *dev)
+{
+ struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
+
+ if (cs35l56->playing)
+ cs35l56_hda_pause(cs35l56);
+
+ cs35l56->suspended = true;
+
+ /*
+ * The interrupt line is normally shared, but after we start suspending
+ * we can't check if our device is the source of an interrupt, and can't
+ * clear it. Prevent this race by temporarily disabling the parent irq
+ * until we reach _no_irq.
+ */
+ if (cs35l56->base.irq)
+ disable_irq(cs35l56->base.irq);
+
+ return pm_runtime_force_suspend(dev);
+}
+
+static int cs35l56_hda_system_suspend_late(struct device *dev)
+{
+ struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
+
+ /*
+ * RESET is usually shared by all amps so it must not be asserted until
+ * all driver instances have done their suspend() stage.
+ */
+ if (cs35l56->base.reset_gpio) {
+ gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0);
+ cs35l56_wait_min_reset_pulse();
+ }
+
+ return 0;
+}
+
+static int cs35l56_hda_system_suspend_no_irq(struct device *dev)
+{
+ struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
+
+ /* Handlers are now disabled so the parent IRQ can safely be re-enabled. */
+ if (cs35l56->base.irq)
+ enable_irq(cs35l56->base.irq);
+
+ return 0;
+}
+
+static int cs35l56_hda_system_resume_no_irq(struct device *dev)
+{
+ struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
+
+ /*
+ * WAKE interrupts unmask if the CS35L56 hibernates, which can cause
+ * spurious interrupts, and the interrupt line is normally shared.
+ * We can't check if our device is the source of an interrupt, and can't
+ * clear it, until it has fully resumed. Prevent this race by temporarily
+ * disabling the parent irq until we complete resume().
+ */
+ if (cs35l56->base.irq)
+ disable_irq(cs35l56->base.irq);
+
+ return 0;
+}
+
+static int cs35l56_hda_system_resume_early(struct device *dev)
+{
+ struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
+
+ /* Ensure a spec-compliant RESET pulse. */
+ if (cs35l56->base.reset_gpio) {
+ gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0);
+ cs35l56_wait_min_reset_pulse();
+
+ /* Release shared RESET before drivers start resume(). */
+ gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 1);
+ cs35l56_wait_control_port_ready();
+ }
+
+ return 0;
+}
+
+static int cs35l56_hda_system_resume(struct device *dev)
+{
+ struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
+ int ret;
+
+ /* Undo pm_runtime_force_suspend() before re-enabling the irq */
+ ret = pm_runtime_force_resume(dev);
+ if (cs35l56->base.irq)
+ enable_irq(cs35l56->base.irq);
+
+ if (ret)
+ return ret;
+
+ cs35l56->suspended = false;
+
+ ret = cs35l56_is_fw_reload_needed(&cs35l56->base);
+ dev_dbg(cs35l56->base.dev, "fw_reload_needed: %d\n", ret);
+ if (ret > 0) {
+ ret = cs35l56_hda_fw_load(cs35l56);
+ if (ret)
+ return ret;
+ }
+
+ if (cs35l56->playing)
+ cs35l56_hda_play(cs35l56);
+
+ return 0;
+}
+
+static int cs35l56_hda_read_acpi(struct cs35l56_hda *cs35l56, int id)
+{
+ u32 values[HDA_MAX_COMPONENTS];
+ struct acpi_device *adev;
+ const char *property, *sub;
+ size_t nval;
+ int i, ret;
+
+ /*
+ * ACPI_COMPANION isn't available when this driver was instantiated by
+ * the serial-multi-instantiate driver, so lookup the node by HID
+ */
+ if (!ACPI_COMPANION(cs35l56->base.dev)) {
+ adev = acpi_dev_get_first_match_dev("CSC3556", NULL, -1);
+ if (!adev) {
+ dev_err(cs35l56->base.dev, "Failed to find an ACPI device for %s\n",
+ dev_name(cs35l56->base.dev));
+ return -ENODEV;
+ }
+ ACPI_COMPANION_SET(cs35l56->base.dev, adev);
+ }
+
+ property = "cirrus,dev-index";
+ ret = device_property_count_u32(cs35l56->base.dev, property);
+ if (ret <= 0)
+ goto err;
+
+ if (ret > ARRAY_SIZE(values)) {
+ ret = -EINVAL;
+ goto err;
+ }
+ nval = ret;
+
+ ret = device_property_read_u32_array(cs35l56->base.dev, property, values, nval);
+ if (ret)
+ goto err;
+
+ cs35l56->index = -1;
+ for (i = 0; i < nval; i++) {
+ if (values[i] == id) {
+ cs35l56->index = i;
+ break;
+ }
+ }
+ if (cs35l56->index == -1) {
+ dev_err(cs35l56->base.dev, "No index found in %s\n", property);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ sub = acpi_get_subsystem_id(ACPI_HANDLE(cs35l56->base.dev));
+
+ if (IS_ERR(sub)) {
+ /* If no ACPI SUB, return 0 and fallback to legacy firmware path, otherwise fail */
+ if (PTR_ERR(sub) == -ENODATA)
+ return 0;
+ else
+ return PTR_ERR(sub);
+ }
+
+ cs35l56->system_name = sub;
+
+ cs35l56->base.reset_gpio = devm_gpiod_get_index_optional(cs35l56->base.dev,
+ "reset",
+ cs35l56->index,
+ GPIOD_OUT_LOW);
+ if (IS_ERR(cs35l56->base.reset_gpio)) {
+ ret = PTR_ERR(cs35l56->base.reset_gpio);
+
+ /*
+ * If RESET is shared the first amp to probe will grab the reset
+ * line and reset all the amps
+ */
+ if (ret != -EBUSY)
+ return dev_err_probe(cs35l56->base.dev, ret, "Failed to get reset GPIO\n");
+
+ dev_info(cs35l56->base.dev, "Reset GPIO busy, assume shared reset\n");
+ cs35l56->base.reset_gpio = NULL;
+ }
+
+ return 0;
+
+err:
+ dev_err(cs35l56->base.dev, "Failed property %s: %d\n", property, ret);
+
+ return ret;
+}
+
+int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int id)
+{
+ int ret;
+
+ mutex_init(&cs35l56->base.irq_lock);
+ dev_set_drvdata(cs35l56->base.dev, cs35l56);
+
+ ret = cs35l56_hda_read_acpi(cs35l56, id);
+ if (ret) {
+ dev_err_probe(cs35l56->base.dev, ret, "Platform not supported\n");
+ goto err;
+ }
+
+ cs35l56->amp_name = devm_kasprintf(cs35l56->base.dev, GFP_KERNEL, "AMP%d",
+ cs35l56->index + 1);
+ if (!cs35l56->amp_name) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ cs35l56_init_cs_dsp(&cs35l56->base, &cs35l56->cs_dsp);
+ cs35l56->cs_dsp.client_ops = &cs35l56_hda_client_ops;
+
+ if (cs35l56->base.reset_gpio) {
+ dev_dbg(cs35l56->base.dev, "Hard reset\n");
+
+ /*
+ * The GPIOD_OUT_LOW to *_gpiod_get_*() will be ignored if the
+ * ACPI defines a different default state. So explicitly set low.
+ */
+ gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0);
+ cs35l56_wait_min_reset_pulse();
+ gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 1);
+ }
+
+ ret = cs35l56_hw_init(&cs35l56->base);
+ if (ret < 0)
+ goto err;
+
+ /* Reset the device and wait for it to boot */
+ cs35l56_system_reset(&cs35l56->base, false);
+ ret = cs35l56_wait_for_firmware_boot(&cs35l56->base);
+ if (ret)
+ goto err;
+
+ ret = cs35l56_set_patch(&cs35l56->base);
+ if (ret)
+ return ret;
+
+ regcache_mark_dirty(cs35l56->base.regmap);
+ regcache_sync(cs35l56->base.regmap);
+
+ /* Disable auto-hibernate so that runtime_pm has control */
+ ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE);
+ if (ret)
+ goto err;
+
+ ret = cs_dsp_halo_init(&cs35l56->cs_dsp);
+ if (ret) {
+ dev_err_probe(cs35l56->base.dev, ret, "cs_dsp_halo_init failed\n");
+ goto err;
+ }
+
+ dev_dbg(cs35l56->base.dev, "DSP system name: '%s', amp name: '%s'\n",
+ cs35l56->system_name, cs35l56->amp_name);
+
+ regmap_multi_reg_write(cs35l56->base.regmap, cs35l56_hda_dai_config,
+ ARRAY_SIZE(cs35l56_hda_dai_config));
+
+ /*
+ * By default only enable one ASP1TXn, where n=amplifier index,
+ * This prevents multiple amps trying to drive the same slot.
+ */
+ cs35l56->asp_tx_mask = BIT(cs35l56->index);
+
+ pm_runtime_set_autosuspend_delay(cs35l56->base.dev, 3000);
+ pm_runtime_use_autosuspend(cs35l56->base.dev);
+ pm_runtime_set_active(cs35l56->base.dev);
+ pm_runtime_mark_last_busy(cs35l56->base.dev);
+ pm_runtime_enable(cs35l56->base.dev);
+
+ ret = component_add(cs35l56->base.dev, &cs35l56_hda_comp_ops);
+ if (ret) {
+ dev_err(cs35l56->base.dev, "Register component failed: %d\n", ret);
+ goto pm_err;
+ }
+
+ cs35l56->base.init_done = true;
+
+ return 0;
+
+pm_err:
+ pm_runtime_disable(cs35l56->base.dev);
+err:
+ gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_hda_common_probe, SND_HDA_SCODEC_CS35L56);
+
+void cs35l56_hda_remove(struct device *dev)
+{
+ struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
+
+ pm_runtime_get_sync(cs35l56->base.dev);
+ pm_runtime_disable(cs35l56->base.dev);
+
+ component_del(cs35l56->base.dev, &cs35l56_hda_comp_ops);
+
+ kfree(cs35l56->system_name);
+ pm_runtime_put_noidle(cs35l56->base.dev);
+
+ gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0);
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_hda_remove, SND_HDA_SCODEC_CS35L56);
+
+const struct dev_pm_ops cs35l56_hda_pm_ops = {
+ SET_RUNTIME_PM_OPS(cs35l56_hda_runtime_suspend, cs35l56_hda_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(cs35l56_hda_system_suspend, cs35l56_hda_system_resume)
+ LATE_SYSTEM_SLEEP_PM_OPS(cs35l56_hda_system_suspend_late,
+ cs35l56_hda_system_resume_early)
+ NOIRQ_SYSTEM_SLEEP_PM_OPS(cs35l56_hda_system_suspend_no_irq,
+ cs35l56_hda_system_resume_no_irq)
+};
+EXPORT_SYMBOL_NS_GPL(cs35l56_hda_pm_ops, SND_HDA_SCODEC_CS35L56);
+
+MODULE_DESCRIPTION("CS35L56 HDA Driver");
+MODULE_IMPORT_NS(SND_HDA_CS_DSP_CONTROLS);
+MODULE_IMPORT_NS(SND_SOC_CS35L56_SHARED);
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(FW_CS_DSP);
diff --git a/sound/pci/hda/cs35l56_hda.h b/sound/pci/hda/cs35l56_hda.h
new file mode 100644
index 000000000000..6e5bc5397db5
--- /dev/null
+++ b/sound/pci/hda/cs35l56_hda.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ * HDA audio driver for Cirrus Logic CS35L56 smart amp
+ *
+ * Copyright (C) 2023 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+
+#ifndef __CS35L56_HDA_H__
+#define __CS35L56_HDA_H__
+
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/firmware/cirrus/cs_dsp.h>
+#include <linux/firmware/cirrus/wmfw.h>
+#include <linux/regulator/consumer.h>
+#include <sound/cs35l56.h>
+
+struct dentry;
+
+struct cs35l56_hda {
+ struct cs35l56_base base;
+ struct hda_codec *codec;
+
+ int index;
+ const char *system_name;
+ const char *amp_name;
+
+ struct cs_dsp cs_dsp;
+ bool playing;
+ bool suspended;
+ u8 asp_tx_mask;
+
+ struct snd_kcontrol *posture_ctl;
+ struct snd_kcontrol *volume_ctl;
+ struct snd_kcontrol *mixer_ctl[4];
+
+#if IS_ENABLED(CONFIG_SND_DEBUG)
+ struct dentry *debugfs_root;
+#endif
+};
+
+extern const struct dev_pm_ops cs35l56_hda_pm_ops;
+
+int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int id);
+void cs35l56_hda_remove(struct device *dev);
+
+#endif /*__CS35L56_HDA_H__*/
diff --git a/sound/pci/hda/cs35l56_hda_i2c.c b/sound/pci/hda/cs35l56_hda_i2c.c
new file mode 100644
index 000000000000..83e4acdd89ac
--- /dev/null
+++ b/sound/pci/hda/cs35l56_hda_i2c.c
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// CS35L56 HDA audio driver I2C binding
+//
+// Copyright (C) 2023 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#include "cs35l56_hda.h"
+
+static int cs35l56_hda_i2c_probe(struct i2c_client *clt)
+{
+ struct cs35l56_hda *cs35l56;
+ int ret;
+
+ cs35l56 = devm_kzalloc(&clt->dev, sizeof(*cs35l56), GFP_KERNEL);
+ if (!cs35l56)
+ return -ENOMEM;
+
+ cs35l56->base.dev = &clt->dev;
+ cs35l56->base.can_hibernate = true;
+ cs35l56->base.regmap = devm_regmap_init_i2c(clt, &cs35l56_regmap_i2c);
+ if (IS_ERR(cs35l56->base.regmap)) {
+ ret = PTR_ERR(cs35l56->base.regmap);
+ dev_err(cs35l56->base.dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = cs35l56_hda_common_probe(cs35l56, clt->addr);
+ if (ret)
+ return ret;
+ ret = cs35l56_irq_request(&cs35l56->base, clt->irq);
+ if (ret < 0)
+ cs35l56_hda_remove(cs35l56->base.dev);
+
+ return ret;
+}
+
+static void cs35l56_hda_i2c_remove(struct i2c_client *clt)
+{
+ cs35l56_hda_remove(&clt->dev);
+}
+
+static const struct i2c_device_id cs35l56_hda_i2c_id[] = {
+ { "cs35l56-hda", 0 },
+ {}
+};
+
+static struct i2c_driver cs35l56_hda_i2c_driver = {
+ .driver = {
+ .name = "cs35l56-hda",
+ .pm = &cs35l56_hda_pm_ops,
+ },
+ .id_table = cs35l56_hda_i2c_id,
+ .probe = cs35l56_hda_i2c_probe,
+ .remove = cs35l56_hda_i2c_remove,
+};
+module_i2c_driver(cs35l56_hda_i2c_driver);
+
+MODULE_DESCRIPTION("HDA CS35L56 I2C driver");
+MODULE_IMPORT_NS(SND_HDA_SCODEC_CS35L56);
+MODULE_IMPORT_NS(SND_SOC_CS35L56_SHARED);
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/pci/hda/cs35l56_hda_spi.c b/sound/pci/hda/cs35l56_hda_spi.c
new file mode 100644
index 000000000000..756aec342eab
--- /dev/null
+++ b/sound/pci/hda/cs35l56_hda_spi.c
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// CS35L56 HDA audio driver SPI binding
+//
+// Copyright (C) 2023 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+
+#include "cs35l56_hda.h"
+
+static int cs35l56_hda_spi_probe(struct spi_device *spi)
+{
+ struct cs35l56_hda *cs35l56;
+ int ret;
+
+ cs35l56 = devm_kzalloc(&spi->dev, sizeof(*cs35l56), GFP_KERNEL);
+ if (!cs35l56)
+ return -ENOMEM;
+
+ cs35l56->base.dev = &spi->dev;
+ cs35l56->base.regmap = devm_regmap_init_spi(spi, &cs35l56_regmap_spi);
+ if (IS_ERR(cs35l56->base.regmap)) {
+ ret = PTR_ERR(cs35l56->base.regmap);
+ dev_err(cs35l56->base.dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = cs35l56_hda_common_probe(cs35l56, spi->chip_select);
+ if (ret)
+ return ret;
+ ret = cs35l56_irq_request(&cs35l56->base, spi->irq);
+ if (ret < 0)
+ cs35l56_hda_remove(cs35l56->base.dev);
+
+ return ret;
+}
+
+static void cs35l56_hda_spi_remove(struct spi_device *spi)
+{
+ cs35l56_hda_remove(&spi->dev);
+}
+
+static const struct spi_device_id cs35l56_hda_spi_id[] = {
+ { "cs35l56-hda", 0 },
+ {}
+};
+
+static struct spi_driver cs35l56_hda_spi_driver = {
+ .driver = {
+ .name = "cs35l56-hda",
+ .pm = &cs35l56_hda_pm_ops,
+ },
+ .id_table = cs35l56_hda_spi_id,
+ .probe = cs35l56_hda_spi_probe,
+ .remove = cs35l56_hda_spi_remove,
+};
+module_spi_driver(cs35l56_hda_spi_driver);
+
+MODULE_DESCRIPTION("HDA CS35L56 SPI driver");
+MODULE_IMPORT_NS(SND_HDA_SCODEC_CS35L56);
+MODULE_IMPORT_NS(SND_SOC_CS35L56_SHARED);
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/pci/hda/hda_auto_parser.h b/sound/pci/hda/hda_auto_parser.h
index df63d66af1ab..579b11beac71 100644
--- a/sound/pci/hda/hda_auto_parser.h
+++ b/sound/pci/hda/hda_auto_parser.h
@@ -8,6 +8,8 @@
#ifndef __SOUND_HDA_AUTO_PARSER_H
#define __SOUND_HDA_AUTO_PARSER_H
+#include "hda_local.h"
+
/*
* Helper for automatic pin configuration
*/
diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h
index 34eba40cc6e6..a8eea8367629 100644
--- a/sound/pci/hda/hda_generic.h
+++ b/sound/pci/hda/hda_generic.h
@@ -9,6 +9,9 @@
#define __SOUND_HDA_GENERIC_H
#include <linux/leds.h>
+#include "hda_auto_parser.h"
+
+struct hda_jack_callback;
/* table entry for multi-io paths */
struct hda_multi_io {
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 7d14f9dbe95e..a6585afb7707 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -122,6 +122,7 @@ struct alc_spec {
unsigned int ultra_low_power:1;
unsigned int has_hs_key:1;
unsigned int no_internal_mic_pin:1;
+ unsigned int en_3kpull_low:1;
/* for PLL fix */
hda_nid_t pll_nid;
@@ -3622,6 +3623,7 @@ static void alc256_shutup(struct hda_codec *codec)
if (!hp_pin)
hp_pin = 0x21;
+ alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */
hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
if (hp_pin_sense)
@@ -3638,8 +3640,7 @@ static void alc256_shutup(struct hda_codec *codec)
/* If disable 3k pulldown control for alc257, the Mic detection will not work correctly
* when booting with headset plugged. So skip setting it for the codec alc257
*/
- if (codec->core.vendor_id != 0x10ec0236 &&
- codec->core.vendor_id != 0x10ec0257)
+ if (spec->en_3kpull_low)
alc_update_coef_idx(codec, 0x46, 0, 3 << 12);
if (!spec->no_shutup_pins)
@@ -4623,6 +4624,21 @@ static void alc236_fixup_hp_mute_led_coefbit(struct hda_codec *codec,
}
}
+static void alc236_fixup_hp_mute_led_coefbit2(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->mute_led_polarity = 0;
+ spec->mute_led_coef.idx = 0x07;
+ spec->mute_led_coef.mask = 1;
+ spec->mute_led_coef.on = 1;
+ spec->mute_led_coef.off = 0;
+ snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set);
+ }
+}
+
/* turn on/off mic-mute LED per capture hook by coef bit */
static int coef_micmute_led_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
@@ -7151,6 +7167,7 @@ enum {
ALC285_FIXUP_HP_GPIO_LED,
ALC285_FIXUP_HP_MUTE_LED,
ALC285_FIXUP_HP_SPECTRE_X360_MUTE_LED,
+ ALC236_FIXUP_HP_MUTE_LED_COEFBIT2,
ALC236_FIXUP_HP_GPIO_LED,
ALC236_FIXUP_HP_MUTE_LED,
ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF,
@@ -7221,6 +7238,7 @@ enum {
ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN,
ALC295_FIXUP_DELL_INSPIRON_TOP_SPEAKERS,
ALC236_FIXUP_DELL_DUAL_CODECS,
+ ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI,
};
/* A special fixup for Lenovo C940 and Yoga Duet 7;
@@ -8640,6 +8658,10 @@ static const struct hda_fixup alc269_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc285_fixup_hp_spectre_x360_mute_led,
},
+ [ALC236_FIXUP_HP_MUTE_LED_COEFBIT2] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc236_fixup_hp_mute_led_coefbit2,
+ },
[ALC236_FIXUP_HP_GPIO_LED] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc236_fixup_hp_gpio_led,
@@ -9153,8 +9175,6 @@ static const struct hda_fixup alc269_fixups[] = {
[ALC287_FIXUP_CS35L41_I2C_2] = {
.type = HDA_FIXUP_FUNC,
.v.func = cs35l41_fixup_i2c_two,
- .chained = true,
- .chain_id = ALC269_FIXUP_THINKPAD_ACPI,
},
[ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED] = {
.type = HDA_FIXUP_FUNC,
@@ -9291,6 +9311,12 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
},
+ [ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs35l41_fixup_i2c_two,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_THINKPAD_ACPI,
+ },
};
static const struct snd_pci_quirk alc269_fixup_tbl[] = {
@@ -9401,6 +9427,14 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1028, 0x0c1c, "Dell Precision 3540", ALC236_FIXUP_DELL_DUAL_CODECS),
SND_PCI_QUIRK(0x1028, 0x0c1d, "Dell Precision 3440", ALC236_FIXUP_DELL_DUAL_CODECS),
SND_PCI_QUIRK(0x1028, 0x0c1e, "Dell Precision 3540", ALC236_FIXUP_DELL_DUAL_CODECS),
+ SND_PCI_QUIRK(0x1028, 0x0cbd, "Dell Oasis 13 CS MTL-U", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1028, 0x0cbe, "Dell Oasis 13 2-IN-1 MTL-U", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1028, 0x0cbf, "Dell Oasis 13 Low Weight MTU-L", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1028, 0x0cc1, "Dell Oasis 14 MTL-H/U", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1028, 0x0cc2, "Dell Oasis 14 2-in-1 MTL-H/U", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1028, 0x0cc3, "Dell Oasis 14 Low Weight MTL-U", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1028, 0x0cc4, "Dell Oasis 16 MTL-H/U", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1028, 0x0cc5, "Dell Oasis MLK 14 RPL-P", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x1028, 0x164a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x164b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2),
@@ -9524,6 +9558,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x886d, "HP ZBook Fury 17.3 Inch G8 Mobile Workstation PC", ALC285_FIXUP_HP_GPIO_AMP_INIT),
SND_PCI_QUIRK(0x103c, 0x8870, "HP ZBook Fury 15.6 Inch G8 Mobile Workstation PC", ALC285_FIXUP_HP_GPIO_AMP_INIT),
SND_PCI_QUIRK(0x103c, 0x8873, "HP ZBook Studio 15.6 Inch G8 Mobile Workstation PC", ALC285_FIXUP_HP_GPIO_AMP_INIT),
+ SND_PCI_QUIRK(0x103c, 0x887a, "HP Laptop 15s-eq2xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
SND_PCI_QUIRK(0x103c, 0x888d, "HP ZBook Power 15.6 inch G8 Mobile Workstation PC", ALC236_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8895, "HP EliteBook 855 G8 Notebook PC", ALC285_FIXUP_HP_SPEAKERS_MICMUTE_LED),
SND_PCI_QUIRK(0x103c, 0x8896, "HP EliteBook 855 G8 Notebook PC", ALC285_FIXUP_HP_MUTE_LED),
@@ -9735,6 +9770,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1558, 0x5157, "Clevo W517GU1", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1558, 0x51a1, "Clevo NS50MU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1558, 0x51b1, "Clevo NS50AU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x51b3, "Clevo NS70AU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1558, 0x5630, "Clevo NP50RNJS", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1558, 0x70a1, "Clevo NB70T[HJK]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1558, 0x70b3, "Clevo NK70SB", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
@@ -9818,14 +9854,14 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x22be, "Thinkpad X1 Carbon 8th", ALC285_FIXUP_THINKPAD_HEADSET_JACK),
SND_PCI_QUIRK(0x17aa, 0x22c1, "Thinkpad P1 Gen 3", ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK),
SND_PCI_QUIRK(0x17aa, 0x22c2, "Thinkpad X1 Extreme Gen 3", ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK),
- SND_PCI_QUIRK(0x17aa, 0x22f1, "Thinkpad", ALC287_FIXUP_CS35L41_I2C_2),
- SND_PCI_QUIRK(0x17aa, 0x22f2, "Thinkpad", ALC287_FIXUP_CS35L41_I2C_2),
- SND_PCI_QUIRK(0x17aa, 0x22f3, "Thinkpad", ALC287_FIXUP_CS35L41_I2C_2),
- SND_PCI_QUIRK(0x17aa, 0x2316, "Thinkpad P1 Gen 6", ALC287_FIXUP_CS35L41_I2C_2),
- SND_PCI_QUIRK(0x17aa, 0x2317, "Thinkpad P1 Gen 6", ALC287_FIXUP_CS35L41_I2C_2),
- SND_PCI_QUIRK(0x17aa, 0x2318, "Thinkpad Z13 Gen2", ALC287_FIXUP_CS35L41_I2C_2),
- SND_PCI_QUIRK(0x17aa, 0x2319, "Thinkpad Z16 Gen2", ALC287_FIXUP_CS35L41_I2C_2),
- SND_PCI_QUIRK(0x17aa, 0x231a, "Thinkpad Z16 Gen2", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x17aa, 0x22f1, "Thinkpad", ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI),
+ SND_PCI_QUIRK(0x17aa, 0x22f2, "Thinkpad", ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI),
+ SND_PCI_QUIRK(0x17aa, 0x22f3, "Thinkpad", ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI),
+ SND_PCI_QUIRK(0x17aa, 0x2316, "Thinkpad P1 Gen 6", ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI),
+ SND_PCI_QUIRK(0x17aa, 0x2317, "Thinkpad P1 Gen 6", ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI),
+ SND_PCI_QUIRK(0x17aa, 0x2318, "Thinkpad Z13 Gen2", ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI),
+ SND_PCI_QUIRK(0x17aa, 0x2319, "Thinkpad Z16 Gen2", ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI),
+ SND_PCI_QUIRK(0x17aa, 0x231a, "Thinkpad Z16 Gen2", ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI),
SND_PCI_QUIRK(0x17aa, 0x30bb, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
SND_PCI_QUIRK(0x17aa, 0x30e2, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
SND_PCI_QUIRK(0x17aa, 0x310c, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION),
@@ -10690,6 +10726,8 @@ static int patch_alc269(struct hda_codec *codec)
spec->shutup = alc256_shutup;
spec->init_hook = alc256_init;
spec->gen.mixer_nid = 0; /* ALC256 does not have any loopback mixer path */
+ if (codec->bus->pci->vendor == PCI_VENDOR_ID_AMD)
+ spec->en_3kpull_low = true;
break;
case 0x10ec0257:
spec->codec_variant = ALC269_TYPE_ALC257;
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index bfa9622e1ab1..439fa631c342 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -38,6 +38,17 @@ config SND_SOC_TOPOLOGY
bool
select SND_DYNAMIC_MINORS
+config SND_SOC_TOPOLOGY_BUILD
+ bool "Build topology core"
+ select SND_SOC_TOPOLOGY
+ depends on KUNIT
+ help
+ This option exists to facilitate running the KUnit tests for
+ the topology core, KUnit is frequently tested in virtual
+ environments with minimal drivers enabled but the topology
+ core is usually selected by drivers. There is little reason
+ to enable it if not doing a KUnit build.
+
config SND_SOC_TOPOLOGY_KUNIT_TEST
tristate "KUnit tests for SoC topology"
depends on KUNIT
diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig
index 1dd8579e8034..273688c05317 100644
--- a/sound/soc/amd/Kconfig
+++ b/sound/soc/amd/Kconfig
@@ -79,14 +79,15 @@ config SND_SOC_AMD_ACP5x
ACP DMA driver, CPU DAI driver.
config SND_SOC_AMD_VANGOGH_MACH
- tristate "AMD Vangogh support for NAU8821 CS35L41"
+ tristate "AMD Vangogh support for NAU8821/CS35L41/MAX98388"
select SND_SOC_NAU8821
select SND_SOC_CS35L41_SPI
+ select SND_SOC_MAX98388
select SND_AMD_ACP_CONFIG
depends on SND_SOC_AMD_ACP5x && I2C && SPI_MASTER
help
This option enables machine driver for Vangogh platform
- using NAU8821 and CS35L41 codecs.
+ using NAU8821 and either CS35L41 or MAX98388 codecs.
Say m if you have such a device.
If unsure select "N".
diff --git a/sound/soc/amd/acp-config.c b/sound/soc/amd/acp-config.c
index 0932473b6394..f27c27580009 100644
--- a/sound/soc/amd/acp-config.c
+++ b/sound/soc/amd/acp-config.c
@@ -47,6 +47,20 @@ static const struct config_entry config_table[] = {
{}
},
},
+ {
+ .flags = FLAG_AMD_SOF,
+ .device = ACP_PCI_DEV_ID,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Valve"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Galileo"),
+ DMI_MATCH(DMI_PRODUCT_FAMILY, "Sephiroth"),
+ },
+ },
+ {}
+ },
+ },
};
int snd_amd_acp_find_config(struct pci_dev *pci)
@@ -82,6 +96,11 @@ static struct snd_soc_acpi_codecs amp_max = {
.codecs = {"MX98360A"}
};
+static struct snd_soc_acpi_codecs amp_max98388 = {
+ .num_codecs = 1,
+ .codecs = {"ADS8388"}
+};
+
struct snd_soc_acpi_mach snd_soc_acpi_amd_sof_machines[] = {
{
.id = "10EC5682",
@@ -130,6 +149,20 @@ struct snd_soc_acpi_mach snd_soc_acpi_amd_sof_machines[] = {
};
EXPORT_SYMBOL(snd_soc_acpi_amd_sof_machines);
+struct snd_soc_acpi_mach snd_soc_acpi_amd_vangogh_sof_machines[] = {
+ {
+ .id = "NVTN2020",
+ .drv_name = "nau8821-max",
+ .pdata = &acp_quirk_data,
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &amp_max98388,
+ .fw_filename = "sof-vangogh.ri",
+ .sof_tplg_filename = "sof-vangogh-nau8821-max.tplg",
+ },
+ {},
+};
+EXPORT_SYMBOL(snd_soc_acpi_amd_vangogh_sof_machines);
+
struct snd_soc_acpi_mach snd_soc_acpi_amd_rmb_sof_machines[] = {
{
.id = "AMDI1019",
diff --git a/sound/soc/amd/acp/Kconfig b/sound/soc/amd/acp/Kconfig
index ce0037810743..631cdf96d637 100644
--- a/sound/soc/amd/acp/Kconfig
+++ b/sound/soc/amd/acp/Kconfig
@@ -18,6 +18,9 @@ if SND_SOC_AMD_ACP_COMMON
config SND_SOC_AMD_ACP_PDM
tristate
+config SND_SOC_AMD_ACP_LEGACY_COMMON
+ tristate
+
config SND_SOC_AMD_ACP_I2S
tristate
@@ -27,6 +30,7 @@ config SND_SOC_AMD_ACP_PCM
config SND_SOC_AMD_ACP_PCI
tristate "AMD ACP PCI Driver Support"
+ select SND_SOC_AMD_ACP_LEGACY_COMMON
depends on X86 && PCI
help
This options enables generic PCI driver for ACP device.
@@ -36,6 +40,7 @@ config SND_AMD_ASOC_RENOIR
select SND_SOC_AMD_ACP_PCM
select SND_SOC_AMD_ACP_I2S
select SND_SOC_AMD_ACP_PDM
+ select SND_SOC_AMD_ACP_LEGACY_COMMON
depends on X86 && PCI
help
This option enables Renoir I2S support on AMD platform.
@@ -45,6 +50,7 @@ config SND_AMD_ASOC_REMBRANDT
select SND_SOC_AMD_ACP_PCM
select SND_SOC_AMD_ACP_I2S
select SND_SOC_AMD_ACP_PDM
+ select SND_SOC_AMD_ACP_LEGACY_COMMON
depends on X86 && PCI
help
This option enables Rembrandt I2S support on AMD platform.
@@ -61,6 +67,8 @@ config SND_SOC_AMD_MACH_COMMON
select SND_SOC_MAX98357A
select SND_SOC_RT5682S
select SND_SOC_NAU8825
+ select SND_SOC_NAU8821
+ select SND_SOC_MAX98388
help
This option enables common Machine driver module for ACP.
diff --git a/sound/soc/amd/acp/Makefile b/sound/soc/amd/acp/Makefile
index d9abb0ee5218..4e65fdbc8dca 100644
--- a/sound/soc/amd/acp/Makefile
+++ b/sound/soc/amd/acp/Makefile
@@ -8,6 +8,7 @@
snd-acp-pcm-objs := acp-platform.o
snd-acp-i2s-objs := acp-i2s.o
snd-acp-pdm-objs := acp-pdm.o
+snd-acp-legacy-common-objs := acp-legacy-common.o
snd-acp-pci-objs := acp-pci.o
#platform specific driver
@@ -22,6 +23,7 @@ snd-acp-sof-mach-objs := acp-sof-mach.o
obj-$(CONFIG_SND_SOC_AMD_ACP_PCM) += snd-acp-pcm.o
obj-$(CONFIG_SND_SOC_AMD_ACP_I2S) += snd-acp-i2s.o
obj-$(CONFIG_SND_SOC_AMD_ACP_PDM) += snd-acp-pdm.o
+obj-$(CONFIG_SND_SOC_AMD_ACP_LEGACY_COMMON) += snd-acp-legacy-common.o
obj-$(CONFIG_SND_SOC_AMD_ACP_PCI) += snd-acp-pci.o
obj-$(CONFIG_SND_AMD_ASOC_RENOIR) += snd-acp-renoir.o
diff --git a/sound/soc/amd/acp/acp-i2s.c b/sound/soc/amd/acp/acp-i2s.c
index 09b6511c0a26..09dc5f2c0bfc 100644
--- a/sound/soc/amd/acp/acp-i2s.c
+++ b/sound/soc/amd/acp/acp-i2s.c
@@ -149,6 +149,7 @@ static int acp_i2s_hwparams(struct snd_pcm_substream *substream, struct snd_pcm_
dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
return -EINVAL;
}
+ adata->xfer_tx_resolution[dai->driver->id - 1] = xfer_resolution;
} else {
switch (dai->driver->id) {
case I2S_BT_INSTANCE:
@@ -167,6 +168,7 @@ static int acp_i2s_hwparams(struct snd_pcm_substream *substream, struct snd_pcm_
dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
return -EINVAL;
}
+ adata->xfer_rx_resolution[dai->driver->id - 1] = xfer_resolution;
}
val = readl(adata->acp_base + reg_val);
diff --git a/sound/soc/amd/acp/acp-legacy-common.c b/sound/soc/amd/acp/acp-legacy-common.c
new file mode 100644
index 000000000000..ba58165cc6e6
--- /dev/null
+++ b/sound/soc/amd/acp/acp-legacy-common.c
@@ -0,0 +1,347 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2023 Advanced Micro Devices, Inc.
+//
+// Authors: Syed Saba Kareem <Syed.SabaKareem@amd.com>
+//
+
+/*
+ * Common file to be used by amd platforms
+ */
+
+#include "amd.h"
+#include <linux/pci.h>
+#include <linux/export.h>
+
+void acp_enable_interrupts(struct acp_dev_data *adata)
+{
+ struct acp_resource *rsrc = adata->rsrc;
+ u32 ext_intr_ctrl;
+
+ writel(0x01, ACP_EXTERNAL_INTR_ENB(adata));
+ ext_intr_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used));
+ ext_intr_ctrl |= ACP_ERROR_MASK;
+ writel(ext_intr_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used));
+}
+EXPORT_SYMBOL_NS_GPL(acp_enable_interrupts, SND_SOC_ACP_COMMON);
+
+void acp_disable_interrupts(struct acp_dev_data *adata)
+{
+ struct acp_resource *rsrc = adata->rsrc;
+
+ writel(ACP_EXT_INTR_STAT_CLEAR_MASK, ACP_EXTERNAL_INTR_STAT(adata, rsrc->irqp_used));
+ writel(0x00, ACP_EXTERNAL_INTR_ENB(adata));
+}
+EXPORT_SYMBOL_NS_GPL(acp_disable_interrupts, SND_SOC_ACP_COMMON);
+
+static void set_acp_pdm_ring_buffer(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct acp_stream *stream = runtime->private_data;
+ struct device *dev = dai->component->dev;
+ struct acp_dev_data *adata = dev_get_drvdata(dev);
+
+ u32 physical_addr, pdm_size, period_bytes;
+
+ period_bytes = frames_to_bytes(runtime, runtime->period_size);
+ pdm_size = frames_to_bytes(runtime, runtime->buffer_size);
+ physical_addr = stream->reg_offset + MEM_WINDOW_START;
+
+ /* Init ACP PDM Ring buffer */
+ writel(physical_addr, adata->acp_base + ACP_WOV_RX_RINGBUFADDR);
+ writel(pdm_size, adata->acp_base + ACP_WOV_RX_RINGBUFSIZE);
+ writel(period_bytes, adata->acp_base + ACP_WOV_RX_INTR_WATERMARK_SIZE);
+ writel(0x01, adata->acp_base + ACPAXI2AXI_ATU_CTRL);
+}
+
+static void set_acp_pdm_clk(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct device *dev = dai->component->dev;
+ struct acp_dev_data *adata = dev_get_drvdata(dev);
+ unsigned int pdm_ctrl;
+
+ /* Enable default ACP PDM clk */
+ writel(PDM_CLK_FREQ_MASK, adata->acp_base + ACP_WOV_CLK_CTRL);
+ pdm_ctrl = readl(adata->acp_base + ACP_WOV_MISC_CTRL);
+ pdm_ctrl |= PDM_MISC_CTRL_MASK;
+ writel(pdm_ctrl, adata->acp_base + ACP_WOV_MISC_CTRL);
+ set_acp_pdm_ring_buffer(substream, dai);
+}
+
+void restore_acp_pdm_params(struct snd_pcm_substream *substream,
+ struct acp_dev_data *adata)
+{
+ struct snd_soc_dai *dai;
+ struct snd_soc_pcm_runtime *soc_runtime;
+ u32 ext_int_ctrl;
+
+ soc_runtime = asoc_substream_to_rtd(substream);
+ dai = asoc_rtd_to_cpu(soc_runtime, 0);
+ /* Programming channel mask and sampling rate */
+ writel(adata->ch_mask, adata->acp_base + ACP_WOV_PDM_NO_OF_CHANNELS);
+ writel(PDM_DEC_64, adata->acp_base + ACP_WOV_PDM_DECIMATION_FACTOR);
+
+ /* Enabling ACP Pdm interuppts */
+ ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, 0));
+ ext_int_ctrl |= PDM_DMA_INTR_MASK;
+ writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, 0));
+ set_acp_pdm_clk(substream, dai);
+}
+EXPORT_SYMBOL_NS_GPL(restore_acp_pdm_params, SND_SOC_ACP_COMMON);
+
+static int set_acp_i2s_dma_fifo(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct device *dev = dai->component->dev;
+ struct acp_dev_data *adata = dev_get_drvdata(dev);
+ struct acp_resource *rsrc = adata->rsrc;
+ struct acp_stream *stream = substream->runtime->private_data;
+ u32 reg_dma_size, reg_fifo_size, reg_fifo_addr;
+ u32 phy_addr, acp_fifo_addr, ext_int_ctrl;
+ unsigned int dir = substream->stream;
+
+ switch (dai->driver->id) {
+ case I2S_SP_INSTANCE:
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
+ reg_dma_size = ACP_I2S_TX_DMA_SIZE;
+ acp_fifo_addr = rsrc->sram_pte_offset +
+ SP_PB_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_I2S_TX_FIFOADDR;
+ reg_fifo_size = ACP_I2S_TX_FIFOSIZE;
+ phy_addr = I2S_SP_TX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, adata->acp_base + ACP_I2S_TX_RINGBUFADDR);
+ } else {
+ reg_dma_size = ACP_I2S_RX_DMA_SIZE;
+ acp_fifo_addr = rsrc->sram_pte_offset +
+ SP_CAPT_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_I2S_RX_FIFOADDR;
+ reg_fifo_size = ACP_I2S_RX_FIFOSIZE;
+ phy_addr = I2S_SP_RX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, adata->acp_base + ACP_I2S_RX_RINGBUFADDR);
+ }
+ break;
+ case I2S_BT_INSTANCE:
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
+ reg_dma_size = ACP_BT_TX_DMA_SIZE;
+ acp_fifo_addr = rsrc->sram_pte_offset +
+ BT_PB_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_BT_TX_FIFOADDR;
+ reg_fifo_size = ACP_BT_TX_FIFOSIZE;
+ phy_addr = I2S_BT_TX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, adata->acp_base + ACP_BT_TX_RINGBUFADDR);
+ } else {
+ reg_dma_size = ACP_BT_RX_DMA_SIZE;
+ acp_fifo_addr = rsrc->sram_pte_offset +
+ BT_CAPT_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_BT_RX_FIFOADDR;
+ reg_fifo_size = ACP_BT_RX_FIFOSIZE;
+ phy_addr = I2S_BT_TX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, adata->acp_base + ACP_BT_RX_RINGBUFADDR);
+ }
+ break;
+ case I2S_HS_INSTANCE:
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
+ reg_dma_size = ACP_HS_TX_DMA_SIZE;
+ acp_fifo_addr = rsrc->sram_pte_offset +
+ HS_PB_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_HS_TX_FIFOADDR;
+ reg_fifo_size = ACP_HS_TX_FIFOSIZE;
+ phy_addr = I2S_HS_TX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, adata->acp_base + ACP_HS_TX_RINGBUFADDR);
+ } else {
+ reg_dma_size = ACP_HS_RX_DMA_SIZE;
+ acp_fifo_addr = rsrc->sram_pte_offset +
+ HS_CAPT_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_HS_RX_FIFOADDR;
+ reg_fifo_size = ACP_HS_RX_FIFOSIZE;
+ phy_addr = I2S_HS_RX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, adata->acp_base + ACP_HS_RX_RINGBUFADDR);
+ }
+ break;
+ default:
+ dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
+ return -EINVAL;
+ }
+
+ writel(DMA_SIZE, adata->acp_base + reg_dma_size);
+ writel(acp_fifo_addr, adata->acp_base + reg_fifo_addr);
+ writel(FIFO_SIZE, adata->acp_base + reg_fifo_size);
+
+ ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used));
+ ext_int_ctrl |= BIT(I2S_RX_THRESHOLD(rsrc->offset)) |
+ BIT(BT_RX_THRESHOLD(rsrc->offset)) |
+ BIT(I2S_TX_THRESHOLD(rsrc->offset)) |
+ BIT(BT_TX_THRESHOLD(rsrc->offset)) |
+ BIT(HS_RX_THRESHOLD(rsrc->offset)) |
+ BIT(HS_TX_THRESHOLD(rsrc->offset));
+
+ writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used));
+ return 0;
+}
+
+int restore_acp_i2s_params(struct snd_pcm_substream *substream,
+ struct acp_dev_data *adata,
+ struct acp_stream *stream)
+{
+ struct snd_soc_dai *dai;
+ struct snd_soc_pcm_runtime *soc_runtime;
+ u32 tdm_fmt, reg_val, fmt_reg, val;
+
+ soc_runtime = asoc_substream_to_rtd(substream);
+ dai = asoc_rtd_to_cpu(soc_runtime, 0);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ tdm_fmt = adata->tdm_tx_fmt[stream->dai_id - 1];
+ switch (stream->dai_id) {
+ case I2S_BT_INSTANCE:
+ reg_val = ACP_BTTDM_ITER;
+ fmt_reg = ACP_BTTDM_TXFRMT;
+ break;
+ case I2S_SP_INSTANCE:
+ reg_val = ACP_I2STDM_ITER;
+ fmt_reg = ACP_I2STDM_TXFRMT;
+ break;
+ case I2S_HS_INSTANCE:
+ reg_val = ACP_HSTDM_ITER;
+ fmt_reg = ACP_HSTDM_TXFRMT;
+ break;
+ default:
+ pr_err("Invalid dai id %x\n", stream->dai_id);
+ return -EINVAL;
+ }
+ val = adata->xfer_tx_resolution[stream->dai_id - 1] << 3;
+ } else {
+ tdm_fmt = adata->tdm_rx_fmt[stream->dai_id - 1];
+ switch (stream->dai_id) {
+ case I2S_BT_INSTANCE:
+ reg_val = ACP_BTTDM_IRER;
+ fmt_reg = ACP_BTTDM_RXFRMT;
+ break;
+ case I2S_SP_INSTANCE:
+ reg_val = ACP_I2STDM_IRER;
+ fmt_reg = ACP_I2STDM_RXFRMT;
+ break;
+ case I2S_HS_INSTANCE:
+ reg_val = ACP_HSTDM_IRER;
+ fmt_reg = ACP_HSTDM_RXFRMT;
+ break;
+ default:
+ pr_err("Invalid dai id %x\n", stream->dai_id);
+ return -EINVAL;
+ }
+ val = adata->xfer_rx_resolution[stream->dai_id - 1] << 3;
+ }
+ writel(val, adata->acp_base + reg_val);
+ if (adata->tdm_mode == TDM_ENABLE) {
+ writel(tdm_fmt, adata->acp_base + fmt_reg);
+ val = readl(adata->acp_base + reg_val);
+ writel(val | 0x2, adata->acp_base + reg_val);
+ }
+ return set_acp_i2s_dma_fifo(substream, dai);
+}
+EXPORT_SYMBOL_NS_GPL(restore_acp_i2s_params, SND_SOC_ACP_COMMON);
+
+static int acp_power_on(struct acp_chip_info *chip)
+{
+ u32 val, acp_pgfsm_stat_reg, acp_pgfsm_ctrl_reg;
+ void __iomem *base;
+
+ base = chip->base;
+ switch (chip->acp_rev) {
+ case ACP3X_DEV:
+ acp_pgfsm_stat_reg = ACP_PGFSM_STATUS;
+ acp_pgfsm_ctrl_reg = ACP_PGFSM_CONTROL;
+ break;
+ case ACP6X_DEV:
+ acp_pgfsm_stat_reg = ACP6X_PGFSM_STATUS;
+ acp_pgfsm_ctrl_reg = ACP6X_PGFSM_CONTROL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ val = readl(base + acp_pgfsm_stat_reg);
+ if (val == ACP_POWERED_ON)
+ return 0;
+
+ if ((val & ACP_PGFSM_STATUS_MASK) != ACP_POWER_ON_IN_PROGRESS)
+ writel(ACP_PGFSM_CNTL_POWER_ON_MASK, base + acp_pgfsm_ctrl_reg);
+
+ return readl_poll_timeout(base + acp_pgfsm_stat_reg, val,
+ !val, DELAY_US, ACP_TIMEOUT);
+}
+
+static int acp_reset(void __iomem *base)
+{
+ u32 val;
+ int ret;
+
+ writel(1, base + ACP_SOFT_RESET);
+ ret = readl_poll_timeout(base + ACP_SOFT_RESET, val, val & ACP_SOFT_RST_DONE_MASK,
+ DELAY_US, ACP_TIMEOUT);
+ if (ret)
+ return ret;
+
+ writel(0, base + ACP_SOFT_RESET);
+ return readl_poll_timeout(base + ACP_SOFT_RESET, val, !val, DELAY_US, ACP_TIMEOUT);
+}
+
+int acp_init(struct acp_chip_info *chip)
+{
+ int ret;
+
+ /* power on */
+ ret = acp_power_on(chip);
+ if (ret) {
+ pr_err("ACP power on failed\n");
+ return ret;
+ }
+ writel(0x01, chip->base + ACP_CONTROL);
+
+ /* Reset */
+ ret = acp_reset(chip->base);
+ if (ret) {
+ pr_err("ACP reset failed\n");
+ return ret;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp_init, SND_SOC_ACP_COMMON);
+
+int acp_deinit(void __iomem *base)
+{
+ int ret;
+
+ /* Reset */
+ ret = acp_reset(base);
+ if (ret)
+ return ret;
+
+ writel(0, base + ACP_CONTROL);
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp_deinit, SND_SOC_ACP_COMMON);
+
+int smn_write(struct pci_dev *dev, u32 smn_addr, u32 data)
+{
+ pci_write_config_dword(dev, 0x60, smn_addr);
+ pci_write_config_dword(dev, 0x64, data);
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(smn_write, SND_SOC_ACP_COMMON);
+
+int smn_read(struct pci_dev *dev, u32 smn_addr)
+{
+ u32 data;
+
+ pci_write_config_dword(dev, 0x60, smn_addr);
+ pci_read_config_dword(dev, 0x64, &data);
+ return data;
+}
+EXPORT_SYMBOL_NS_GPL(smn_read, SND_SOC_ACP_COMMON);
+
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/sound/soc/amd/acp/acp-mach-common.c b/sound/soc/amd/acp/acp-mach-common.c
index 6da17140beea..ff5cbc4a6427 100644
--- a/sound/soc/amd/acp/acp-mach-common.c
+++ b/sound/soc/amd/acp/acp-mach-common.c
@@ -25,12 +25,18 @@
#include "../../codecs/rt1019.h"
#include "../../codecs/rt5682s.h"
#include "../../codecs/nau8825.h"
+#include "../../codecs/nau8821.h"
#include "acp-mach.h"
+static struct snd_soc_jack vg_headset;
#define PCO_PLAT_CLK 48000000
#define RT5682_PLL_FREQ (48000 * 512)
#define DUAL_CHANNEL 2
#define FOUR_CHANNEL 4
+#define NAU8821_CODEC_DAI "nau8821-hifi"
+#define NAU8821_BCLK 1536000
+#define NAU8821_FREQ_OUT 12288000
+#define MAX98388_CODEC_DAI "max98388-aif1"
#define TDM_MODE_ENABLE 1
@@ -661,6 +667,97 @@ static const struct snd_soc_ops acp_card_maxim_ops = {
.hw_params = acp_card_maxim_hw_params,
};
+SND_SOC_DAILINK_DEF(max98388,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-ADS8388:00", "max98388-aif1"),
+ COMP_CODEC("i2c-ADS8388:01", "max98388-aif1")));
+
+static const struct snd_soc_dapm_widget max98388_widgets[] = {
+ SND_SOC_DAPM_SPK("SPK", NULL),
+};
+
+static const struct snd_soc_dapm_route max98388_map[] = {
+ { "SPK", NULL, "Left BE_OUT" },
+ { "SPK", NULL, "Right BE_OUT" },
+};
+
+static struct snd_soc_codec_conf max98388_conf[] = {
+ {
+ .dlc = COMP_CODEC_CONF("i2c-ADS8388:00"),
+ .name_prefix = "Left",
+ },
+ {
+ .dlc = COMP_CODEC_CONF("i2c-ADS8388:01"),
+ .name_prefix = "Right",
+ },
+};
+
+static const unsigned int max98388_format[] = {16};
+
+static struct snd_pcm_hw_constraint_list constraints_sample_bits_max = {
+ .list = max98388_format,
+ .count = ARRAY_SIZE(max98388_format),
+};
+
+static int acp_card_max98388_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+ &constraints_sample_bits_max);
+
+ return 0;
+}
+
+static int acp_card_max98388_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ int ret;
+
+ if (drvdata->amp_codec_id != MAX98388)
+ return -EINVAL;
+
+ ret = snd_soc_dapm_new_controls(&card->dapm, max98388_widgets,
+ ARRAY_SIZE(max98388_widgets));
+
+ if (ret) {
+ dev_err(rtd->dev, "unable to add widget dapm controls, ret %d\n", ret);
+ /* Don't need to add routes if widget addition failed */
+ return ret;
+ }
+ return snd_soc_dapm_add_routes(&rtd->card->dapm, max98388_map,
+ ARRAY_SIZE(max98388_map));
+}
+
+static int acp_max98388_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dai *codec_dai =
+ snd_soc_card_get_codec_dai(card,
+ MAX98388_CODEC_DAI);
+ int ret;
+
+ ret = snd_soc_dai_set_fmt(codec_dai,
+ SND_SOC_DAIFMT_CBS_CFS | SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF);
+ if (ret < 0)
+ return ret;
+
+ return ret;
+}
+
+static const struct snd_soc_ops acp_max98388_ops = {
+ .startup = acp_card_max98388_startup,
+ .hw_params = acp_max98388_hw_params,
+};
+
/* Declare nau8825 codec components */
SND_SOC_DAILINK_DEF(nau8825,
DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10508825:00", "nau8825-hifi")));
@@ -790,6 +887,162 @@ static const struct snd_soc_ops acp_card_nau8825_ops = {
.hw_params = acp_nau8825_hw_params,
};
+static int platform_clock_control(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_dapm_context *dapm = w->dapm;
+ struct snd_soc_card *card = dapm->card;
+ struct snd_soc_dai *codec_dai;
+ int ret = 0;
+
+ codec_dai = snd_soc_card_get_codec_dai(card, NAU8821_CODEC_DAI);
+ if (!codec_dai) {
+ dev_err(card->dev, "Codec dai not found\n");
+ return -EIO;
+ }
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ ret = snd_soc_dai_set_sysclk(codec_dai, NAU8821_CLK_INTERNAL,
+ 0, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(card->dev, "set sysclk err = %d\n", ret);
+ return -EIO;
+ }
+ } else {
+ ret = snd_soc_dai_set_sysclk(codec_dai, NAU8821_CLK_FLL_BLK, 0,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ dev_err(codec_dai->dev, "can't set FS clock %d\n", ret);
+ ret = snd_soc_dai_set_pll(codec_dai, 0, 0, NAU8821_BCLK,
+ NAU8821_FREQ_OUT);
+ if (ret < 0)
+ dev_err(codec_dai->dev, "can't set FLL: %d\n", ret);
+ }
+ return ret;
+}
+
+static const struct snd_soc_dapm_widget nau8821_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Int Mic", NULL),
+ SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
+ platform_clock_control, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route nau8821_audio_route[] = {
+ /* HP jack connectors - unknown if we have jack detection */
+ { "Headphone jack", NULL, "HPOL" },
+ { "Headphone jack", NULL, "HPOR" },
+ { "MICL", NULL, "Headset Mic" },
+ { "MICR", NULL, "Headset Mic" },
+ { "DMIC", NULL, "Int Mic" },
+ { "Headphone jack", NULL, "Platform Clock" },
+ { "Headset Mic", NULL, "Platform Clock" },
+ { "Int Mic", NULL, "Platform Clock" },
+};
+
+static const unsigned int nau8821_format[] = {16};
+
+static struct snd_pcm_hw_constraint_list constraints_sample_bits = {
+ .list = nau8821_format,
+ .count = ARRAY_SIZE(nau8821_format),
+};
+
+static int acp_8821_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ struct snd_soc_component *component = codec_dai->component;
+ int ret;
+
+ dev_info(rtd->dev, "codec dai name = %s\n", codec_dai->name);
+
+ ret = snd_soc_dapm_new_controls(&card->dapm, nau8821_widgets,
+ ARRAY_SIZE(nau8821_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add widget dapm controls, ret %d\n", ret);
+ // Don't need to add routes if widget addition failed
+ return ret;
+ }
+
+ ret = snd_soc_card_jack_new(card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_LINEOUT |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &vg_headset);
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack creation failed %d\n", ret);
+ return ret;
+ }
+ snd_jack_set_key(vg_headset.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(vg_headset.jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(vg_headset.jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(vg_headset.jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+
+ nau8821_enable_jack_detect(component, &vg_headset);
+
+ return snd_soc_dapm_add_routes(&rtd->card->dapm, nau8821_audio_route,
+ ARRAY_SIZE(nau8821_audio_route));
+}
+
+static int acp_8821_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+ &constraints_sample_bits);
+ return 0;
+}
+
+static int acp_nau8821_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ int ret;
+ unsigned int fmt;
+
+ if (drvdata->soc_mclk)
+ fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ else
+ fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP;
+
+ ret = snd_soc_dai_set_fmt(codec_dai, fmt);
+ if (ret < 0) {
+ dev_err(rtd->card->dev, "Failed to set dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, NAU8821_CLK_FLL_BLK, 0,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ dev_err(card->dev, "can't set FS clock %d\n", ret);
+ ret = snd_soc_dai_set_pll(codec_dai, 0, 0, snd_soc_params_to_bclk(params),
+ params_rate(params) * 256);
+ if (ret < 0)
+ dev_err(card->dev, "can't set FLL: %d\n", ret);
+
+ return ret;
+}
+
+static const struct snd_soc_ops acp_8821_ops = {
+ .startup = acp_8821_startup,
+ .hw_params = acp_nau8821_hw_params,
+};
+
+SND_SOC_DAILINK_DEF(nau8821,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-NVTN2020:00",
+ "nau8821-hifi")));
+
/* Declare DMIC codec components */
SND_SOC_DAILINK_DEF(dmic_codec,
DAILINK_COMP_ARRAY(COMP_CODEC("dmic-codec", "dmic-hifi")));
@@ -920,6 +1173,12 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card)
links[i].init = acp_card_rt5682s_init;
links[i].ops = &acp_card_rt5682s_ops;
}
+ if (drv_data->hs_codec_id == NAU8821) {
+ links[i].codecs = nau8821;
+ links[i].num_codecs = ARRAY_SIZE(nau8821);
+ links[i].init = acp_8821_init;
+ links[i].ops = &acp_8821_ops;
+ }
i++;
}
@@ -1007,6 +1266,14 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card)
links[i].ops = &acp_card_maxim_ops;
links[i].init = acp_card_maxim_init;
}
+ if (drv_data->amp_codec_id == MAX98388) {
+ links[i].codecs = max98388;
+ links[i].num_codecs = ARRAY_SIZE(max98388);
+ links[i].ops = &acp_max98388_ops;
+ links[i].init = acp_card_max98388_init;
+ card->codec_conf = max98388_conf;
+ card->num_configs = ARRAY_SIZE(max98388_conf);
+ }
if (drv_data->amp_codec_id == RT1019) {
links[i].codecs = rt1019;
links[i].num_codecs = ARRAY_SIZE(rt1019);
diff --git a/sound/soc/amd/acp/acp-mach.h b/sound/soc/amd/acp/acp-mach.h
index 165f407697c0..2b3ec6594023 100644
--- a/sound/soc/amd/acp/acp-mach.h
+++ b/sound/soc/amd/acp/acp-mach.h
@@ -41,6 +41,8 @@ enum codec_endpoints {
MAX98360A,
RT5682S,
NAU8825,
+ NAU8821,
+ MAX98388,
};
enum platform_end_point {
diff --git a/sound/soc/amd/acp/acp-pci.c b/sound/soc/amd/acp/acp-pci.c
index 8154fbfd1229..a32c14a109b7 100644
--- a/sound/soc/amd/acp/acp-pci.c
+++ b/sound/soc/amd/acp/acp-pci.c
@@ -16,6 +16,7 @@
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/module.h>
+#include <linux/pm_runtime.h>
#include "amd.h"
#include "../mach-config.h"
@@ -106,6 +107,7 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id
goto unregister_dmic_dev;
}
+ acp_init(chip);
res = devm_kcalloc(&pci->dev, num_res, sizeof(struct resource), GFP_KERNEL);
if (!res) {
ret = -ENOMEM;
@@ -139,7 +141,12 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id
ret = PTR_ERR(pdev);
goto unregister_dmic_dev;
}
-
+ chip->chip_pdev = pdev;
+ dev_set_drvdata(&pci->dev, chip);
+ pm_runtime_set_autosuspend_delay(&pci->dev, 2000);
+ pm_runtime_use_autosuspend(&pci->dev);
+ pm_runtime_put_noidle(&pci->dev);
+ pm_runtime_allow(&pci->dev);
return ret;
unregister_dmic_dev:
@@ -152,12 +159,56 @@ disable_pci:
return ret;
};
+static int __maybe_unused snd_acp_suspend(struct device *dev)
+{
+ struct acp_chip_info *chip;
+ int ret;
+
+ chip = dev_get_drvdata(dev);
+ ret = acp_deinit(chip->base);
+ if (ret)
+ dev_err(dev, "ACP de-init failed\n");
+ return ret;
+}
+
+static int __maybe_unused snd_acp_resume(struct device *dev)
+{
+ struct acp_chip_info *chip;
+ struct acp_dev_data *adata;
+ struct device child;
+ int ret;
+
+ chip = dev_get_drvdata(dev);
+ ret = acp_init(chip);
+ if (ret)
+ dev_err(dev, "ACP init failed\n");
+ child = chip->chip_pdev->dev;
+ adata = dev_get_drvdata(&child);
+ if (adata)
+ acp_enable_interrupts(adata);
+ return ret;
+}
+
+static const struct dev_pm_ops acp_pm_ops = {
+ SET_RUNTIME_PM_OPS(snd_acp_suspend, snd_acp_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(snd_acp_suspend, snd_acp_resume)
+};
+
static void acp_pci_remove(struct pci_dev *pci)
{
+ struct acp_chip_info *chip;
+ int ret;
+
+ chip = pci_get_drvdata(pci);
+ pm_runtime_forbid(&pci->dev);
+ pm_runtime_get_noresume(&pci->dev);
if (dmic_dev)
platform_device_unregister(dmic_dev);
if (pdev)
platform_device_unregister(pdev);
+ ret = acp_deinit(chip->base);
+ if (ret)
+ dev_err(&pci->dev, "ACP de-init failed\n");
}
/* PCI IDs */
@@ -173,8 +224,12 @@ static struct pci_driver snd_amd_acp_pci_driver = {
.id_table = acp_pci_ids,
.probe = acp_pci_probe,
.remove = acp_pci_remove,
+ .driver = {
+ .pm = &acp_pm_ops,
+ },
};
module_pci_driver(snd_amd_acp_pci_driver);
MODULE_LICENSE("Dual BSD/GPL");
+MODULE_IMPORT_NS(SND_SOC_ACP_COMMON);
MODULE_ALIAS(DRV_NAME);
diff --git a/sound/soc/amd/acp/acp-pdm.c b/sound/soc/amd/acp/acp-pdm.c
index f8030b79ac17..f754bf79b5e3 100644
--- a/sound/soc/amd/acp/acp-pdm.c
+++ b/sound/soc/amd/acp/acp-pdm.c
@@ -25,18 +25,6 @@
#define DRV_NAME "acp-pdm"
-#define PDM_DMA_STAT 0x10
-#define PDM_DMA_INTR_MASK 0x10000
-#define PDM_DEC_64 0x2
-#define PDM_CLK_FREQ_MASK 0x07
-#define PDM_MISC_CTRL_MASK 0x10
-#define PDM_ENABLE 0x01
-#define PDM_DISABLE 0x00
-#define DMA_EN_MASK 0x02
-#define DELAY_US 5
-#define PDM_TIMEOUT 1000
-#define ACP_REGION2_OFFSET 0x02000000
-
static int acp_dmic_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
@@ -135,6 +123,7 @@ static int acp_dmic_hwparams(struct snd_pcm_substream *substream,
return -EINVAL;
}
+ adata->ch_mask = ch_mask;
if (params_format(hwparams) != SNDRV_PCM_FORMAT_S32_LE) {
dev_err(dai->dev, "Invalid format:%d\n", params_format(hwparams));
return -EINVAL;
diff --git a/sound/soc/amd/acp/acp-platform.c b/sound/soc/amd/acp/acp-platform.c
index f220378ec20e..f516daf6fef4 100644
--- a/sound/soc/amd/acp/acp-platform.c
+++ b/sound/soc/amd/acp/acp-platform.c
@@ -127,7 +127,7 @@ static irqreturn_t i2s_irq_handler(int irq, void *data)
return IRQ_NONE;
}
-static void config_pte_for_stream(struct acp_dev_data *adata, struct acp_stream *stream)
+void config_pte_for_stream(struct acp_dev_data *adata, struct acp_stream *stream)
{
struct acp_resource *rsrc = adata->rsrc;
u32 pte_reg, pte_size, reg_val;
@@ -143,8 +143,9 @@ static void config_pte_for_stream(struct acp_dev_data *adata, struct acp_stream
writel(PAGE_SIZE_4K_ENABLE, adata->acp_base + pte_size);
writel(0x01, adata->acp_base + ACPAXI2AXI_ATU_CTRL);
}
+EXPORT_SYMBOL_NS_GPL(config_pte_for_stream, SND_SOC_ACP_COMMON);
-static void config_acp_dma(struct acp_dev_data *adata, struct acp_stream *stream, int size)
+void config_acp_dma(struct acp_dev_data *adata, struct acp_stream *stream, int size)
{
struct snd_pcm_substream *substream = stream->substream;
struct acp_resource *rsrc = adata->rsrc;
@@ -168,6 +169,7 @@ static void config_acp_dma(struct acp_dev_data *adata, struct acp_stream *stream
addr += PAGE_SIZE;
}
}
+EXPORT_SYMBOL_NS_GPL(config_acp_dma, SND_SOC_ACP_COMMON);
static int acp_dma_open(struct snd_soc_component *component, struct snd_pcm_substream *substream)
{
diff --git a/sound/soc/amd/acp/acp-rembrandt.c b/sound/soc/amd/acp/acp-rembrandt.c
index 1b997837c7d8..cc8284f417c0 100644
--- a/sound/soc/amd/acp/acp-rembrandt.c
+++ b/sound/soc/amd/acp/acp-rembrandt.c
@@ -19,30 +19,17 @@
#include <sound/soc.h>
#include <sound/soc-dai.h>
#include <linux/dma-mapping.h>
+#include <linux/pci.h>
+#include <linux/pm_runtime.h>
#include "amd.h"
#define DRV_NAME "acp_asoc_rembrandt"
-#define ACP6X_PGFSM_CONTROL 0x1024
-#define ACP6X_PGFSM_STATUS 0x1028
-
-#define ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK 0x00010001
-
-#define ACP_PGFSM_CNTL_POWER_ON_MASK 0x01
-#define ACP_PGFSM_CNTL_POWER_OFF_MASK 0x00
-#define ACP_PGFSM_STATUS_MASK 0x03
-#define ACP_POWERED_ON 0x00
-#define ACP_POWER_ON_IN_PROGRESS 0x01
-#define ACP_POWERED_OFF 0x02
-#define ACP_POWER_OFF_IN_PROGRESS 0x03
-
-#define ACP_ERROR_MASK 0x20000000
-#define ACP_EXT_INTR_STAT_CLEAR_MASK 0xFFFFFFFF
-
-
-static int rmb_acp_init(void __iomem *base);
-static int rmb_acp_deinit(void __iomem *base);
+#define MP1_C2PMSG_69 0x3B10A14
+#define MP1_C2PMSG_85 0x3B10A54
+#define MP1_C2PMSG_93 0x3B10A74
+#define HOST_BRIDGE_ID 0x14B5
static struct acp_resource rsrc = {
.offset = 0,
@@ -180,108 +167,22 @@ static struct snd_soc_dai_driver acp_rmb_dai[] = {
},
};
-static int acp6x_power_on(void __iomem *base)
-{
- u32 val;
- int timeout;
-
- val = readl(base + ACP6X_PGFSM_STATUS);
-
- if (val == ACP_POWERED_ON)
- return 0;
-
- if ((val & ACP_PGFSM_STATUS_MASK) !=
- ACP_POWER_ON_IN_PROGRESS)
- writel(ACP_PGFSM_CNTL_POWER_ON_MASK,
- base + ACP6X_PGFSM_CONTROL);
- timeout = 0;
- while (++timeout < 500) {
- val = readl(base + ACP6X_PGFSM_STATUS);
- if (!val)
- return 0;
- udelay(1);
- }
- return -ETIMEDOUT;
-}
-
-static int acp6x_reset(void __iomem *base)
+static int acp6x_master_clock_generate(struct device *dev)
{
- u32 val;
- int timeout;
-
- writel(1, base + ACP_SOFT_RESET);
- timeout = 0;
- while (++timeout < 500) {
- val = readl(base + ACP_SOFT_RESET);
- if (val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK)
- break;
- cpu_relax();
- }
- writel(0, base + ACP_SOFT_RESET);
- timeout = 0;
- while (++timeout < 500) {
- val = readl(base + ACP_SOFT_RESET);
- if (!val)
- return 0;
- cpu_relax();
- }
- return -ETIMEDOUT;
-}
-
-static void acp6x_enable_interrupts(struct acp_dev_data *adata)
-{
- struct acp_resource *rsrc = adata->rsrc;
- u32 ext_intr_ctrl;
-
- writel(0x01, ACP_EXTERNAL_INTR_ENB(adata));
- ext_intr_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used));
- ext_intr_ctrl |= ACP_ERROR_MASK;
- writel(ext_intr_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used));
-}
-
-static void acp6x_disable_interrupts(struct acp_dev_data *adata)
-{
- struct acp_resource *rsrc = adata->rsrc;
-
- writel(ACP_EXT_INTR_STAT_CLEAR_MASK,
- ACP_EXTERNAL_INTR_STAT(adata, rsrc->irqp_used));
- writel(0x00, ACP_EXTERNAL_INTR_ENB(adata));
-}
+ int data = 0;
+ struct pci_dev *smn_dev;
-static int rmb_acp_init(void __iomem *base)
-{
- int ret;
-
- /* power on */
- ret = acp6x_power_on(base);
- if (ret) {
- pr_err("ACP power on failed\n");
- return ret;
- }
- writel(0x01, base + ACP_CONTROL);
-
- /* Reset */
- ret = acp6x_reset(base);
- if (ret) {
- pr_err("ACP reset failed\n");
- return ret;
- }
-
- return 0;
-}
-
-static int rmb_acp_deinit(void __iomem *base)
-{
- int ret = 0;
-
- /* Reset */
- ret = acp6x_reset(base);
- if (ret) {
- pr_err("ACP reset failed\n");
- return ret;
+ smn_dev = pci_get_device(PCI_VENDOR_ID_AMD, HOST_BRIDGE_ID, NULL);
+ if (!smn_dev) {
+ dev_err(dev, "Failed to get host bridge device\n");
+ return -ENODEV;
}
- writel(0x00, base + ACP_CONTROL);
+ smn_write(smn_dev, MP1_C2PMSG_93, 0);
+ smn_write(smn_dev, MP1_C2PMSG_85, 0xC4);
+ smn_write(smn_dev, MP1_C2PMSG_69, 0x4);
+ read_poll_timeout(smn_read, data, data, DELAY_US,
+ ACP_TIMEOUT, false, smn_dev, MP1_C2PMSG_93);
return 0;
}
@@ -303,8 +204,6 @@ static int rembrandt_audio_probe(struct platform_device *pdev)
return -ENODEV;
}
- rmb_acp_init(chip->base);
-
adata = devm_kzalloc(dev, sizeof(struct acp_dev_data), GFP_KERNEL);
if (!adata)
return -ENOMEM;
@@ -335,9 +234,14 @@ static int rembrandt_audio_probe(struct platform_device *pdev)
acp_machine_select(adata);
dev_set_drvdata(dev, adata);
- acp6x_enable_interrupts(adata);
+ acp6x_master_clock_generate(dev);
+ acp_enable_interrupts(adata);
acp_platform_register(dev);
-
+ pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_mark_last_busy(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
return 0;
}
@@ -345,19 +249,49 @@ static void rembrandt_audio_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct acp_dev_data *adata = dev_get_drvdata(dev);
- struct acp_chip_info *chip = dev_get_platdata(dev);
-
- rmb_acp_deinit(chip->base);
- acp6x_disable_interrupts(adata);
+ acp_disable_interrupts(adata);
acp_platform_unregister(dev);
+ pm_runtime_disable(&pdev->dev);
}
+static int __maybe_unused rmb_pcm_resume(struct device *dev)
+{
+ struct acp_dev_data *adata = dev_get_drvdata(dev);
+ struct acp_stream *stream;
+ struct snd_pcm_substream *substream;
+ snd_pcm_uframes_t buf_in_frames;
+ u64 buf_size;
+
+ acp6x_master_clock_generate(dev);
+ spin_lock(&adata->acp_lock);
+ list_for_each_entry(stream, &adata->stream_list, list) {
+ substream = stream->substream;
+ if (substream && substream->runtime) {
+ buf_in_frames = (substream->runtime->buffer_size);
+ buf_size = frames_to_bytes(substream->runtime, buf_in_frames);
+ config_pte_for_stream(adata, stream);
+ config_acp_dma(adata, stream, buf_size);
+ if (stream->dai_id)
+ restore_acp_i2s_params(substream, adata, stream);
+ else
+ restore_acp_pdm_params(substream, adata);
+ }
+ }
+ spin_unlock(&adata->acp_lock);
+ return 0;
+}
+
+static const struct dev_pm_ops rmb_dma_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(NULL, rmb_pcm_resume)
+};
+
static struct platform_driver rembrandt_driver = {
.probe = rembrandt_audio_probe,
.remove_new = rembrandt_audio_remove,
.driver = {
.name = "acp_asoc_rembrandt",
+ .pm = &rmb_dma_pm_ops,
},
};
diff --git a/sound/soc/amd/acp/acp-renoir.c b/sound/soc/amd/acp/acp-renoir.c
index f188365fe214..1899658ab25d 100644
--- a/sound/soc/amd/acp/acp-renoir.c
+++ b/sound/soc/amd/acp/acp-renoir.c
@@ -25,20 +25,6 @@
#define DRV_NAME "acp_asoc_renoir"
-#define ACP_SOFT_RST_DONE_MASK 0x00010001
-
-#define ACP_PWR_ON_MASK 0x01
-#define ACP_PWR_OFF_MASK 0x00
-#define ACP_PGFSM_STAT_MASK 0x03
-#define ACP_POWERED_ON 0x00
-#define ACP_PWR_ON_IN_PROGRESS 0x01
-#define ACP_POWERED_OFF 0x02
-#define DELAY_US 5
-#define ACP_TIMEOUT 500
-
-#define ACP_ERROR_MASK 0x20000000
-#define ACP_EXT_INTR_STAT_CLEAR_MASK 0xFFFFFFFF
-
static struct acp_resource rsrc = {
.offset = 20,
.no_of_ctrls = 1,
@@ -154,89 +140,7 @@ static struct snd_soc_dai_driver acp_renoir_dai[] = {
},
};
-static int acp3x_power_on(void __iomem *base)
-{
- u32 val;
-
- val = readl(base + ACP_PGFSM_STATUS);
-
- if (val == ACP_POWERED_ON)
- return 0;
-
- if ((val & ACP_PGFSM_STAT_MASK) != ACP_PWR_ON_IN_PROGRESS)
- writel(ACP_PWR_ON_MASK, base + ACP_PGFSM_CONTROL);
-
- return readl_poll_timeout(base + ACP_PGFSM_STATUS, val, !val, DELAY_US, ACP_TIMEOUT);
-}
-
-static int acp3x_reset(void __iomem *base)
-{
- u32 val;
- int ret;
-
- writel(1, base + ACP_SOFT_RESET);
-
- ret = readl_poll_timeout(base + ACP_SOFT_RESET, val, val & ACP_SOFT_RST_DONE_MASK,
- DELAY_US, ACP_TIMEOUT);
- if (ret)
- return ret;
-
- writel(0, base + ACP_SOFT_RESET);
-
- return readl_poll_timeout(base + ACP_SOFT_RESET, val, !val, DELAY_US, ACP_TIMEOUT);
-}
-
-static void acp3x_enable_interrupts(struct acp_dev_data *adata)
-{
- struct acp_resource *rsrc = adata->rsrc;
- u32 ext_intr_ctrl;
-
- writel(0x01, ACP_EXTERNAL_INTR_ENB(adata));
- ext_intr_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used));
- ext_intr_ctrl |= ACP_ERROR_MASK;
- writel(ext_intr_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used));
-}
-
-static void acp3x_disable_interrupts(struct acp_dev_data *adata)
-{
- struct acp_resource *rsrc = adata->rsrc;
-
- writel(ACP_EXT_INTR_STAT_CLEAR_MASK,
- ACP_EXTERNAL_INTR_STAT(adata, rsrc->irqp_used));
- writel(0x00, ACP_EXTERNAL_INTR_ENB(adata));
-}
-static int rn_acp_init(void __iomem *base)
-{
- int ret;
-
- /* power on */
- ret = acp3x_power_on(base);
- if (ret)
- return ret;
-
- writel(0x01, base + ACP_CONTROL);
-
- /* Reset */
- ret = acp3x_reset(base);
- if (ret)
- return ret;
-
- return 0;
-}
-
-static int rn_acp_deinit(void __iomem *base)
-{
- int ret = 0;
-
- /* Reset */
- ret = acp3x_reset(base);
- if (ret)
- return ret;
-
- writel(0x00, base + ACP_CONTROL);
- return 0;
-}
static int renoir_audio_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -256,12 +160,6 @@ static int renoir_audio_probe(struct platform_device *pdev)
return -ENODEV;
}
- ret = rn_acp_init(chip->base);
- if (ret) {
- dev_err(&pdev->dev, "ACP Init failed\n");
- return -EINVAL;
- }
-
adata = devm_kzalloc(dev, sizeof(struct acp_dev_data), GFP_KERNEL);
if (!adata)
return -ENOMEM;
@@ -290,7 +188,7 @@ static int renoir_audio_probe(struct platform_device *pdev)
acp_machine_select(adata);
dev_set_drvdata(dev, adata);
- acp3x_enable_interrupts(adata);
+ acp_enable_interrupts(adata);
acp_platform_register(dev);
return 0;
@@ -300,17 +198,8 @@ static void renoir_audio_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct acp_dev_data *adata = dev_get_drvdata(dev);
- struct acp_chip_info *chip;
- int ret;
-
- chip = dev_get_platdata(&pdev->dev);
-
- acp3x_disable_interrupts(adata);
-
- ret = rn_acp_deinit(chip->base);
- if (ret)
- dev_err(&pdev->dev, "ACP de-init Failed (%pe)\n", ERR_PTR(ret));
+ acp_disable_interrupts(adata);
acp_platform_unregister(dev);
}
diff --git a/sound/soc/amd/acp/acp-sof-mach.c b/sound/soc/amd/acp/acp-sof-mach.c
index 99a7d3879340..a1c893f33f74 100644
--- a/sound/soc/amd/acp/acp-sof-mach.c
+++ b/sound/soc/amd/acp/acp-sof-mach.c
@@ -83,6 +83,17 @@ static struct acp_card_drvdata sof_rt5682s_hs_rt1019_data = {
.tdm_mode = false,
};
+static struct acp_card_drvdata sof_nau8821_max98388_data = {
+ .hs_cpu_id = I2S_SP,
+ .amp_cpu_id = I2S_HS,
+ .dmic_cpu_id = NONE,
+ .hs_codec_id = NAU8821,
+ .amp_codec_id = MAX98388,
+ .dmic_codec_id = NONE,
+ .soc_mclk = true,
+ .tdm_mode = false,
+};
+
static const struct snd_kcontrol_new acp_controls[] = {
SOC_DAPM_PIN_SWITCH("Headphone Jack"),
SOC_DAPM_PIN_SWITCH("Headset Mic"),
@@ -166,6 +177,10 @@ static const struct platform_device_id board_ids[] = {
.name = "rt5682s-hs-rt1019",
.driver_data = (kernel_ulong_t)&sof_rt5682s_hs_rt1019_data
},
+ {
+ .name = "nau8821-max",
+ .driver_data = (kernel_ulong_t)&sof_nau8821_max98388_data
+ },
{ }
};
static struct platform_driver acp_asoc_audio = {
@@ -187,4 +202,5 @@ MODULE_ALIAS("platform:rt5682s-max");
MODULE_ALIAS("platform:rt5682s-rt1019");
MODULE_ALIAS("platform:nau8825-max");
MODULE_ALIAS("platform:rt5682s-hs-rt1019");
+MODULE_ALIAS("platform:nau8821-max");
MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/acp/amd.h b/sound/soc/amd/acp/amd.h
index 5f2119f42271..2ebe2099cbb5 100644
--- a/sound/soc/amd/acp/amd.h
+++ b/sound/soc/amd/acp/amd.h
@@ -92,10 +92,43 @@
#define SLOT_WIDTH_24 0x18
#define SLOT_WIDTH_32 0x20
+#define ACP6X_PGFSM_CONTROL 0x1024
+#define ACP6X_PGFSM_STATUS 0x1028
+
+#define ACP_SOFT_RST_DONE_MASK 0x00010001
+
+#define ACP_PGFSM_CNTL_POWER_ON_MASK 0x01
+#define ACP_PGFSM_CNTL_POWER_OFF_MASK 0x00
+#define ACP_PGFSM_STATUS_MASK 0x03
+#define ACP_POWERED_ON 0x00
+#define ACP_POWER_ON_IN_PROGRESS 0x01
+#define ACP_POWERED_OFF 0x02
+#define ACP_POWER_OFF_IN_PROGRESS 0x03
+
+#define ACP_ERROR_MASK 0x20000000
+#define ACP_EXT_INTR_STAT_CLEAR_MASK 0xffffffff
+
+#define ACP_TIMEOUT 500
+#define DELAY_US 5
+#define ACP_SUSPEND_DELAY_MS 2000
+
+#define PDM_DMA_STAT 0x10
+#define PDM_DMA_INTR_MASK 0x10000
+#define PDM_DEC_64 0x2
+#define PDM_CLK_FREQ_MASK 0x07
+#define PDM_MISC_CTRL_MASK 0x10
+#define PDM_ENABLE 0x01
+#define PDM_DISABLE 0x00
+#define DMA_EN_MASK 0x02
+#define DELAY_US 5
+#define PDM_TIMEOUT 1000
+#define ACP_REGION2_OFFSET 0x02000000
+
struct acp_chip_info {
char *name; /* Platform name */
unsigned int acp_rev; /* ACP Revision id */
void __iomem *base; /* ACP memory PCI base */
+ struct platform_device *chip_pdev;
};
struct acp_stream {
@@ -144,8 +177,11 @@ struct acp_dev_data {
u32 lrclk_div;
struct acp_resource *rsrc;
+ u32 ch_mask;
u32 tdm_tx_fmt[3];
u32 tdm_rx_fmt[3];
+ u32 xfer_tx_resolution[3];
+ u32 xfer_rx_resolution[3];
};
union acp_i2stdm_mstrclkgen {
@@ -168,12 +204,27 @@ int acp_platform_unregister(struct device *dev);
int acp_machine_select(struct acp_dev_data *adata);
+int smn_read(struct pci_dev *dev, u32 smn_addr);
+int smn_write(struct pci_dev *dev, u32 smn_addr, u32 data);
+
+int acp_init(struct acp_chip_info *chip);
+int acp_deinit(void __iomem *base);
+void acp_enable_interrupts(struct acp_dev_data *adata);
+void acp_disable_interrupts(struct acp_dev_data *adata);
/* Machine configuration */
int snd_amd_acp_find_config(struct pci_dev *pci);
+void config_pte_for_stream(struct acp_dev_data *adata, struct acp_stream *stream);
+void config_acp_dma(struct acp_dev_data *adata, struct acp_stream *stream, int size);
+void restore_acp_pdm_params(struct snd_pcm_substream *substream,
+ struct acp_dev_data *adata);
+
+int restore_acp_i2s_params(struct snd_pcm_substream *substream,
+ struct acp_dev_data *adata, struct acp_stream *stream);
+
static inline u64 acp_get_byte_count(struct acp_dev_data *adata, int dai_id, int direction)
{
- u64 byte_count, low = 0, high = 0;
+ u64 byte_count = 0, low = 0, high = 0;
if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
switch (dai_id) {
@@ -191,7 +242,7 @@ static inline u64 acp_get_byte_count(struct acp_dev_data *adata, int dai_id, int
break;
default:
dev_err(adata->dev, "Invalid dai id %x\n", dai_id);
- return -EINVAL;
+ goto POINTER_RETURN_BYTES;
}
} else {
switch (dai_id) {
@@ -213,12 +264,13 @@ static inline u64 acp_get_byte_count(struct acp_dev_data *adata, int dai_id, int
break;
default:
dev_err(adata->dev, "Invalid dai id %x\n", dai_id);
- return -EINVAL;
+ goto POINTER_RETURN_BYTES;
}
}
/* Get 64 bit value from two 32 bit registers */
byte_count = (high << 32) | low;
+POINTER_RETURN_BYTES:
return byte_count;
}
diff --git a/sound/soc/amd/mach-config.h b/sound/soc/amd/mach-config.h
index 7b4c625da40d..d392e6d6e6e1 100644
--- a/sound/soc/amd/mach-config.h
+++ b/sound/soc/amd/mach-config.h
@@ -20,6 +20,7 @@
extern struct snd_soc_acpi_mach snd_soc_acpi_amd_sof_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_amd_rmb_sof_machines[];
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_vangogh_sof_machines[];
struct config_entry {
u32 flags;
diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h
index e96e6dc9d90f..8b853b8d0219 100644
--- a/sound/soc/amd/ps/acp63.h
+++ b/sound/soc/amd/ps/acp63.h
@@ -116,8 +116,28 @@
#define ACP63_SDW0_DMA_MAX_STREAMS 6
#define ACP63_SDW1_DMA_MAX_STREAMS 2
#define ACP_P1_AUDIO_TX_THRESHOLD 6
+
+/*
+ * Below entries describes SDW0 instance DMA stream id and DMA irq bit mapping
+ * in ACP_EXTENAL_INTR_CNTL register.
+ * Stream id IRQ Bit
+ * 0 (SDW0_AUDIO0_TX) 28
+ * 1 (SDW0_AUDIO1_TX) 26
+ * 2 (SDW0_AUDIO2_TX) 24
+ * 3 (SDW0_AUDIO0_RX) 27
+ * 4 (SDW0_AUDIO1_RX) 25
+ * 5 (SDW0_AUDIO2_RX) 23
+ */
#define SDW0_DMA_TX_IRQ_MASK(i) (ACP_AUDIO0_TX_THRESHOLD - (2 * (i)))
-#define SDW0_DMA_RX_IRQ_MASK(i) (ACP_AUDIO0_RX_THRESHOLD - (2 * (i)))
+#define SDW0_DMA_RX_IRQ_MASK(i) (ACP_AUDIO0_RX_THRESHOLD - (2 * ((i) - 3)))
+
+/*
+ * Below entries describes SDW1 instance DMA stream id and DMA irq bit mapping
+ * in ACP_EXTENAL_INTR_CNTL1 register.
+ * Stream id IRQ Bit
+ * 0 (SDW1_AUDIO1_TX) 6
+ * 1 (SDW1_AUDIO1_RX) 5
+ */
#define SDW1_DMA_IRQ_MASK(i) (ACP_P1_AUDIO_TX_THRESHOLD - (i))
#define ACP_DELAY_US 5
diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c
index 5b46dc8573f8..4af3c3665387 100644
--- a/sound/soc/amd/ps/pci-ps.c
+++ b/sound/soc/amd/ps/pci-ps.c
@@ -257,7 +257,7 @@ static int sdw_amd_scan_controller(struct device *dev)
&sdw_manager_bitmap, 1);
if (ret) {
- dev_err(dev, "Failed to read mipi-sdw-manager-list: %d\n", ret);
+ dev_dbg(dev, "Failed to read mipi-sdw-manager-list: %d\n", ret);
return -EINVAL;
}
count = hweight32(sdw_manager_bitmap);
@@ -641,7 +641,7 @@ static int snd_acp63_probe(struct pci_dev *pci,
ret = get_acp63_device_config(val, pci, adata);
/* ACP PCI driver probe should be continued even PDM or SoundWire Devices are not found */
if (ret) {
- dev_err(&pci->dev, "get acp device config failed:%d\n", ret);
+ dev_dbg(&pci->dev, "get acp device config failed:%d\n", ret);
goto skip_pdev_creation;
}
ret = create_acp63_platform_devs(pci, adata, addr);
diff --git a/sound/soc/amd/ps/ps-sdw-dma.c b/sound/soc/amd/ps/ps-sdw-dma.c
index ade130a8062a..6230d1b12225 100644
--- a/sound/soc/amd/ps/ps-sdw-dma.c
+++ b/sound/soc/amd/ps/ps-sdw-dma.c
@@ -30,7 +30,7 @@ static struct sdw_dma_ring_buf_reg sdw0_dma_ring_buf_reg[ACP63_SDW0_DMA_MAX_STRE
ACP_AUDIO2_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO2_TX_LINEARPOSITIONCNTR_HIGH},
{ACP_AUDIO0_RX_DMA_SIZE, ACP_AUDIO0_RX_FIFOADDR, ACP_AUDIO0_RX_FIFOSIZE,
ACP_AUDIO0_RX_RINGBUFSIZE, ACP_AUDIO0_RX_RINGBUFADDR, ACP_AUDIO0_RX_INTR_WATERMARK_SIZE,
- ACP_AUDIO0_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO0_TX_LINEARPOSITIONCNTR_HIGH},
+ ACP_AUDIO0_RX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO0_RX_LINEARPOSITIONCNTR_HIGH},
{ACP_AUDIO1_RX_DMA_SIZE, ACP_AUDIO1_RX_FIFOADDR, ACP_AUDIO1_RX_FIFOSIZE,
ACP_AUDIO1_RX_RINGBUFSIZE, ACP_AUDIO1_RX_RINGBUFADDR, ACP_AUDIO1_RX_INTR_WATERMARK_SIZE,
ACP_AUDIO1_RX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO1_RX_LINEARPOSITIONCNTR_HIGH},
@@ -39,6 +39,11 @@ static struct sdw_dma_ring_buf_reg sdw0_dma_ring_buf_reg[ACP63_SDW0_DMA_MAX_STRE
ACP_AUDIO2_RX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO2_RX_LINEARPOSITIONCNTR_HIGH}
};
+/*
+ * SDW1 instance supports one TX stream and one RX stream.
+ * For TX/RX streams DMA registers programming for SDW1 instance, it uses ACP_P1_AUDIO1 register
+ * set as per hardware register documentation
+ */
static struct sdw_dma_ring_buf_reg sdw1_dma_ring_buf_reg[ACP63_SDW1_DMA_MAX_STREAMS] = {
{ACP_P1_AUDIO1_TX_DMA_SIZE, ACP_P1_AUDIO1_TX_FIFOADDR, ACP_P1_AUDIO1_TX_FIFOSIZE,
ACP_P1_AUDIO1_TX_RINGBUFSIZE, ACP_P1_AUDIO1_TX_RINGBUFADDR,
@@ -59,6 +64,12 @@ static u32 sdw0_dma_enable_reg[ACP63_SDW0_DMA_MAX_STREAMS] = {
ACP_SW0_AUDIO2_RX_EN,
};
+/*
+ * SDW1 instance supports one TX stream and one RX stream.
+ * For TX/RX streams DMA enable register programming for SDW1 instance,
+ * it uses ACP_SW1_AUDIO1_TX_EN and ACP_SW1_AUDIO1_RX_EN registers
+ * as per hardware register documentation.
+ */
static u32 sdw1_dma_enable_reg[ACP63_SDW1_DMA_MAX_STREAMS] = {
ACP_SW1_AUDIO1_TX_EN,
ACP_SW1_AUDIO1_RX_EN,
@@ -307,12 +318,13 @@ static u64 acp63_sdw_get_byte_count(struct acp_sdw_dma_stream *stream, void __io
pos_high_reg = sdw1_dma_ring_buf_reg[stream->stream_id].pos_high_reg;
break;
default:
- return -EINVAL;
+ goto POINTER_RETURN_BYTES;
}
if (pos_low_reg) {
byte_count.bcount.high = readl(acp_base + pos_high_reg);
byte_count.bcount.low = readl(acp_base + pos_low_reg);
}
+POINTER_RETURN_BYTES:
return byte_count.bytescount;
}
@@ -476,10 +488,9 @@ static int acp63_sdw_platform_probe(struct platform_device *pdev)
return 0;
}
-static int acp63_sdw_platform_remove(struct platform_device *pdev)
+static void acp63_sdw_platform_remove(struct platform_device *pdev)
{
pm_runtime_disable(&pdev->dev);
- return 0;
}
static int acp_restore_sdw_dma_config(struct sdw_dma_dev_data *sdw_data)
@@ -540,7 +551,7 @@ static const struct dev_pm_ops acp63_pm_ops = {
static struct platform_driver acp63_sdw_dma_driver = {
.probe = acp63_sdw_platform_probe,
- .remove = acp63_sdw_platform_remove,
+ .remove_new = acp63_sdw_platform_remove,
.driver = {
.name = "amd_ps_sdw_dma",
.pm = &acp63_pm_ops,
diff --git a/sound/soc/amd/vangogh/acp5x-mach.c b/sound/soc/amd/vangogh/acp5x-mach.c
index e5bcd1e6eb73..125a8e93478d 100644
--- a/sound/soc/amd/vangogh/acp5x-mach.c
+++ b/sound/soc/amd/vangogh/acp5x-mach.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0+
/*
- * Machine driver for AMD Vangogh platform using NAU8821 & CS35L41
- * codecs.
+ * Machine driver for AMD Vangogh platform using either
+ * NAU8821 & CS35L41 or NAU8821 & MAX98388 codecs.
*
* Copyright 2021 Advanced Micro Devices, Inc.
*/
@@ -22,7 +22,6 @@
#define DRV_NAME "acp5x_mach"
#define DUAL_CHANNEL 2
-#define VG_JUPITER 1
#define ACP5X_NAU8821_BCLK 3072000
#define ACP5X_NAU8821_FREQ_OUT 12288000
#define ACP5X_NAU8821_COMP_NAME "i2c-NVTN2020:00"
@@ -30,8 +29,10 @@
#define ACP5X_CS35L41_COMP_LNAME "spi-VLV1776:00"
#define ACP5X_CS35L41_COMP_RNAME "spi-VLV1776:01"
#define ACP5X_CS35L41_DAI_NAME "cs35l41-pcm"
+#define ACP5X_MAX98388_COMP_LNAME "i2c-ADS8388:00"
+#define ACP5X_MAX98388_COMP_RNAME "i2c-ADS8388:01"
+#define ACP5X_MAX98388_DAI_NAME "max98388-aif1"
-static unsigned long acp5x_machine_id;
static struct snd_soc_jack vg_headset;
SND_SOC_DAILINK_DEF(platform, DAILINK_COMP_ARRAY(COMP_PLATFORM("acp5x_i2s_dma.0")));
@@ -242,7 +243,6 @@ static int acp5x_cs35l41_hw_params(struct snd_pcm_substream *substream,
}
return 0;
-
}
static const struct snd_soc_ops acp5x_cs35l41_play_ops = {
@@ -292,8 +292,6 @@ static struct snd_soc_dai_link acp5x_8821_35l41_dai[] = {
},
};
-
-
static const struct snd_soc_dapm_widget acp5x_8821_35l41_widgets[] = {
SND_SOC_DAPM_HP("Headphone", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
@@ -331,16 +329,110 @@ static struct snd_soc_card acp5x_8821_35l41_card = {
.num_controls = ARRAY_SIZE(acp5x_8821_controls),
};
-static int acp5x_vg_quirk_cb(const struct dmi_system_id *id)
+static int acp5x_max98388_startup(struct snd_pcm_substream *substream)
{
- acp5x_machine_id = VG_JUPITER;
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct acp5x_platform_info *machine = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_pcm_runtime *runtime = substream->runtime;
- return 1;
+ machine->play_i2s_instance = I2S_HS_INSTANCE;
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+ return 0;
}
+static const struct snd_soc_ops acp5x_max98388_play_ops = {
+ .startup = acp5x_max98388_startup,
+};
+
+static struct snd_soc_codec_conf acp5x_max98388_conf[] = {
+ {
+ .dlc = COMP_CODEC_CONF(ACP5X_MAX98388_COMP_LNAME),
+ .name_prefix = "Left",
+ },
+ {
+ .dlc = COMP_CODEC_CONF(ACP5X_MAX98388_COMP_RNAME),
+ .name_prefix = "Right",
+ },
+};
+
+SND_SOC_DAILINK_DEF(max98388, DAILINK_COMP_ARRAY(COMP_CODEC(ACP5X_MAX98388_COMP_LNAME,
+ ACP5X_MAX98388_DAI_NAME),
+ COMP_CODEC(ACP5X_MAX98388_COMP_RNAME,
+ ACP5X_MAX98388_DAI_NAME)));
+
+static struct snd_soc_dai_link acp5x_8821_98388_dai[] = {
+ {
+ .name = "acp5x-8821-play",
+ .stream_name = "Playback/Capture",
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .ops = &acp5x_8821_ops,
+ .init = acp5x_8821_init,
+ SND_SOC_DAILINK_REG(acp5x_i2s, nau8821, platform),
+ },
+ {
+ .name = "acp5x-max98388-play",
+ .stream_name = "MAX98388 Playback",
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ .dpcm_playback = 1,
+ .playback_only = 1,
+ .ops = &acp5x_max98388_play_ops,
+ SND_SOC_DAILINK_REG(acp5x_bt, max98388, platform),
+ },
+};
+
+static const struct snd_soc_dapm_widget acp5x_8821_98388_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Int Mic", NULL),
+ SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
+ platform_clock_control,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SPK("SPK", NULL),
+};
+
+static const struct snd_soc_dapm_route acp5x_8821_98388_route[] = {
+ { "Headphone", NULL, "HPOL" },
+ { "Headphone", NULL, "HPOR" },
+ { "MICL", NULL, "Headset Mic" },
+ { "MICR", NULL, "Headset Mic" },
+ { "DMIC", NULL, "Int Mic" },
+
+ { "Headphone", NULL, "Platform Clock" },
+ { "Headset Mic", NULL, "Platform Clock" },
+ { "Int Mic", NULL, "Platform Clock" },
+
+ { "SPK", NULL, "Left BE_OUT" },
+ { "SPK", NULL, "Right BE_OUT" },
+};
+
+static struct snd_soc_card acp5x_8821_98388_card = {
+ .name = "acp5x-max98388",
+ .owner = THIS_MODULE,
+ .dai_link = acp5x_8821_98388_dai,
+ .num_links = ARRAY_SIZE(acp5x_8821_98388_dai),
+ .dapm_widgets = acp5x_8821_98388_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(acp5x_8821_98388_widgets),
+ .dapm_routes = acp5x_8821_98388_route,
+ .num_dapm_routes = ARRAY_SIZE(acp5x_8821_98388_route),
+ .codec_conf = acp5x_max98388_conf,
+ .num_configs = ARRAY_SIZE(acp5x_max98388_conf),
+ .controls = acp5x_8821_controls,
+ .num_controls = ARRAY_SIZE(acp5x_8821_controls),
+};
+
static const struct dmi_system_id acp5x_vg_quirk_table[] = {
{
- .callback = acp5x_vg_quirk_cb,
.matches = {
DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Valve"),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Jupiter"),
@@ -351,23 +443,31 @@ static const struct dmi_system_id acp5x_vg_quirk_table[] = {
static int acp5x_probe(struct platform_device *pdev)
{
+ const struct dmi_system_id *dmi_id;
struct acp5x_platform_info *machine;
struct device *dev = &pdev->dev;
struct snd_soc_card *card;
int ret;
+ card = (struct snd_soc_card *)device_get_match_data(dev);
+ if (!card) {
+ /*
+ * This is normally the result of directly probing the driver
+ * in pci-acp5x through platform_device_register_full(), which
+ * is necessary for the CS35L41 variant, as it doesn't support
+ * ACPI probing and relies on DMI quirks.
+ */
+ dmi_id = dmi_first_match(acp5x_vg_quirk_table);
+ if (!dmi_id)
+ return -ENODEV;
+
+ card = &acp5x_8821_35l41_card;
+ }
+
machine = devm_kzalloc(dev, sizeof(*machine), GFP_KERNEL);
if (!machine)
return -ENOMEM;
- dmi_check_system(acp5x_vg_quirk_table);
- switch (acp5x_machine_id) {
- case VG_JUPITER:
- card = &acp5x_8821_35l41_card;
- break;
- default:
- return -ENODEV;
- }
card->dev = dev;
platform_set_drvdata(pdev, card);
snd_soc_card_set_drvdata(card, machine);
@@ -379,10 +479,17 @@ static int acp5x_probe(struct platform_device *pdev)
return 0;
}
+static const struct acpi_device_id acp5x_acpi_match[] = {
+ { "AMDI8821", (kernel_ulong_t)&acp5x_8821_98388_card },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, acp5x_acpi_match);
+
static struct platform_driver acp5x_mach_driver = {
.driver = {
- .name = "acp5x_mach",
+ .name = DRV_NAME,
.pm = &snd_soc_pm_ops,
+ .acpi_match_table = acp5x_acpi_match,
},
.probe = acp5x_probe,
};
@@ -390,6 +497,6 @@ static struct platform_driver acp5x_mach_driver = {
module_platform_driver(acp5x_mach_driver);
MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
-MODULE_DESCRIPTION("NAU8821 & CS35L41 audio support");
+MODULE_DESCRIPTION("NAU8821/CS35L41 & NAU8821/MAX98388 audio support");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/bcm/bcm63xx-i2s-whistler.c b/sound/soc/bcm/bcm63xx-i2s-whistler.c
index 18c51dbbc8dc..c64609718738 100644
--- a/sound/soc/bcm/bcm63xx-i2s-whistler.c
+++ b/sound/soc/bcm/bcm63xx-i2s-whistler.c
@@ -225,7 +225,6 @@ static int bcm63xx_i2s_dev_probe(struct platform_device *pdev)
{
int ret = 0;
void __iomem *regs;
- struct resource *r_mem, *region;
struct bcm_i2s_priv *i2s_priv;
struct regmap *regmap_i2s;
struct clk *i2s_clk;
@@ -241,20 +240,7 @@ static int bcm63xx_i2s_dev_probe(struct platform_device *pdev)
return PTR_ERR(i2s_clk);
}
- r_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!r_mem) {
- dev_err(&pdev->dev, "Unable to get register resource.\n");
- return -ENODEV;
- }
-
- region = devm_request_mem_region(&pdev->dev, r_mem->start,
- resource_size(r_mem), DRV_NAME);
- if (!region) {
- dev_err(&pdev->dev, "Memory region already claimed\n");
- return -EBUSY;
- }
-
- regs = devm_ioremap_resource(&pdev->dev, r_mem);
+ regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(regs)) {
ret = PTR_ERR(regs);
return ret;
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 2a62dbd5339e..88c3dbe47d71 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -53,6 +53,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_AK5558
imply SND_SOC_ALC5623
imply SND_SOC_ALC5632
+ imply SND_SOC_AUDIO_IIO_AUX
imply SND_SOC_AW8738
imply SND_SOC_AW88395
imply SND_SOC_BT_SCO
@@ -614,6 +615,17 @@ config SND_SOC_ALC5632
tristate
depends on I2C
+config SND_SOC_AUDIO_IIO_AUX
+ tristate "Audio IIO Auxiliary device"
+ depends on IIO
+ help
+ Enable support for Industrial I/O devices as audio auxiliary devices.
+ This allows to have an IIO device present in the audio path and
+ controlled using mixer controls.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-soc-audio-iio-aux.
+
config SND_SOC_AW8738
tristate "Awinic AW8738 Audio Amplifier"
select GPIOLIB
@@ -715,6 +727,7 @@ config SND_SOC_CS35L41_I2C
config SND_SOC_CS35L45
tristate
+ select REGMAP_IRQ
config SND_SOC_CS35L45_SPI
tristate "Cirrus Logic CS35L45 CODEC (SPI)"
@@ -1707,6 +1720,7 @@ config SND_SOC_STA529
config SND_SOC_STAC9766
tristate
depends on SND_SOC_AC97_BUS
+ select REGMAP_AC97
config SND_SOC_STI_SAS
tristate "codec Audio support for STI SAS codec"
@@ -1942,6 +1956,7 @@ config SND_SOC_WCD934X
tristate "WCD9340/WCD9341 Codec"
depends on COMMON_CLK
depends on SLIMBUS
+ select REGMAP_IRQ
select REGMAP_SLIMBUS
select SND_SOC_WCD_MBHC
depends on MFD_WCD934X || COMPILE_TEST
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index b48a9a323b84..32dcc6de58bd 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -45,6 +45,7 @@ snd-soc-ak4671-objs := ak4671.o
snd-soc-ak5386-objs := ak5386.o
snd-soc-ak5558-objs := ak5558.o
snd-soc-arizona-objs := arizona.o arizona-jack.o
+snd-soc-audio-iio-aux-objs := audio-iio-aux.o
snd-soc-aw8738-objs := aw8738.o
snd-soc-aw88395-lib-objs := aw88395/aw88395_lib.o
snd-soc-aw88395-objs := aw88395/aw88395.o \
@@ -428,6 +429,7 @@ obj-$(CONFIG_SND_SOC_AK5558) += snd-soc-ak5558.o
obj-$(CONFIG_SND_SOC_ALC5623) += snd-soc-alc5623.o
obj-$(CONFIG_SND_SOC_ALC5632) += snd-soc-alc5632.o
obj-$(CONFIG_SND_SOC_ARIZONA) += snd-soc-arizona.o
+obj-$(CONFIG_SND_SOC_AUDIO_IIO_AUX) += snd-soc-audio-iio-aux.o
obj-$(CONFIG_SND_SOC_AW8738) += snd-soc-aw8738.o
obj-$(CONFIG_SND_SOC_AW88395_LIB) += snd-soc-aw88395-lib.o
obj-$(CONFIG_SND_SOC_AW88395) +=snd-soc-aw88395.o
diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c
index 2c64df96b5ce..949077108bef 100644
--- a/sound/soc/codecs/ad1836.c
+++ b/sound/soc/codecs/ad1836.c
@@ -358,7 +358,7 @@ static const struct regmap_config ad1836_regmap_config = {
.max_register = AD1836_ADC_CTRL3,
.reg_defaults = ad1836_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(ad1836_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static int ad1836_spi_probe(struct spi_device *spi)
diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c
index 5e777d7fd5d9..3c1ae13c1aae 100644
--- a/sound/soc/codecs/ad1980.c
+++ b/sound/soc/codecs/ad1980.c
@@ -92,7 +92,7 @@ static const struct regmap_config ad1980_regmap_config = {
.reg_stride = 2,
.val_bits = 16,
.max_register = 0x7e,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.volatile_reg = regmap_ac97_default_volatile,
.readable_reg = ad1980_readable_reg,
diff --git a/sound/soc/codecs/adau1372.c b/sound/soc/codecs/adau1372.c
index d9bde7eb043a..98380a7ce64d 100644
--- a/sound/soc/codecs/adau1372.c
+++ b/sound/soc/codecs/adau1372.c
@@ -1056,7 +1056,7 @@ const struct regmap_config adau1372_regmap_config = {
.reg_defaults = adau1372_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(adau1372_reg_defaults),
.volatile_reg = adau1372_volatile_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
EXPORT_SYMBOL_GPL(adau1372_regmap_config);
diff --git a/sound/soc/codecs/adau1373.c b/sound/soc/codecs/adau1373.c
index c5b087b8fffc..b0ab0a69b207 100644
--- a/sound/soc/codecs/adau1373.c
+++ b/sound/soc/codecs/adau1373.c
@@ -1451,7 +1451,7 @@ static const struct regmap_config adau1373_regmap_config = {
.volatile_reg = adau1373_register_volatile,
.max_register = ADAU1373_SOFT_RESET,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = adau1373_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(adau1373_reg_defaults),
};
diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c
index 8c8de3b3c901..94831aad7ac6 100644
--- a/sound/soc/codecs/adau1701.c
+++ b/sound/soc/codecs/adau1701.c
@@ -778,7 +778,7 @@ static const struct regmap_config adau1701_regmap = {
.reg_bits = 16,
.val_bits = 32,
.max_register = ADAU1701_MAX_REGISTER,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.volatile_reg = adau1701_volatile_reg,
.reg_write = adau1701_reg_write,
.reg_read = adau1701_reg_read,
diff --git a/sound/soc/codecs/adau1761.c b/sound/soc/codecs/adau1761.c
index 3ccc7acac205..1f09ea385f8a 100644
--- a/sound/soc/codecs/adau1761.c
+++ b/sound/soc/codecs/adau1761.c
@@ -1014,7 +1014,7 @@ const struct regmap_config adau1761_regmap_config = {
.readable_reg = adau1761_readable_register,
.volatile_reg = adau17x1_volatile_register,
.precious_reg = adau17x1_precious_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
EXPORT_SYMBOL_GPL(adau1761_regmap_config);
diff --git a/sound/soc/codecs/adau1781.c b/sound/soc/codecs/adau1781.c
index ff6be24863bf..faad2f9f8dd2 100644
--- a/sound/soc/codecs/adau1781.c
+++ b/sound/soc/codecs/adau1781.c
@@ -472,7 +472,7 @@ const struct regmap_config adau1781_regmap_config = {
.readable_reg = adau1781_readable_register,
.volatile_reg = adau17x1_volatile_register,
.precious_reg = adau17x1_precious_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
EXPORT_SYMBOL_GPL(adau1781_regmap_config);
diff --git a/sound/soc/codecs/adau1977.c b/sound/soc/codecs/adau1977.c
index 7a9672f94fc6..ae59efb38f26 100644
--- a/sound/soc/codecs/adau1977.c
+++ b/sound/soc/codecs/adau1977.c
@@ -991,7 +991,7 @@ const struct regmap_config adau1977_regmap_config = {
.max_register = ADAU1977_REG_DC_HPF_CAL,
.volatile_reg = adau1977_register_volatile,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = adau1977_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(adau1977_reg_defaults),
};
diff --git a/sound/soc/codecs/adau7118-i2c.c b/sound/soc/codecs/adau7118-i2c.c
index 73f181f7757e..b302b28eca7c 100644
--- a/sound/soc/codecs/adau7118-i2c.c
+++ b/sound/soc/codecs/adau7118-i2c.c
@@ -43,7 +43,7 @@ static const struct regmap_config adau7118_regmap_config = {
.val_bits = 8,
.reg_defaults = adau7118_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(adau7118_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.max_register = ADAU7118_REG_RESET,
.volatile_reg = adau7118_volatile,
};
diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c
index fcff35f26cec..bb08969c5917 100644
--- a/sound/soc/codecs/adav80x.c
+++ b/sound/soc/codecs/adav80x.c
@@ -870,7 +870,7 @@ const struct regmap_config adav80x_regmap_config = {
.max_register = ADAV80X_PLL_OUTE,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = adav80x_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(adav80x_reg_defaults),
};
diff --git a/sound/soc/codecs/audio-iio-aux.c b/sound/soc/codecs/audio-iio-aux.c
new file mode 100644
index 000000000000..a8bf14239bd7
--- /dev/null
+++ b/sound/soc/codecs/audio-iio-aux.c
@@ -0,0 +1,344 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// ALSA SoC glue to use IIO devices as audio components
+//
+// Copyright 2023 CS GROUP France
+//
+// Author: Herve Codina <herve.codina@bootlin.com>
+
+#include <linux/iio/consumer.h>
+#include <linux/minmax.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/string_helpers.h>
+
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+struct audio_iio_aux_chan {
+ struct iio_channel *iio_chan;
+ const char *name;
+ int max;
+ int min;
+ bool is_invert_range;
+};
+
+struct audio_iio_aux {
+ struct device *dev;
+ struct audio_iio_aux_chan *chans;
+ unsigned int num_chans;
+};
+
+static int audio_iio_aux_info_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct audio_iio_aux_chan *chan = (struct audio_iio_aux_chan *)kcontrol->private_value;
+
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = chan->max - chan->min;
+ uinfo->type = (uinfo->value.integer.max == 1) ?
+ SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+ return 0;
+}
+
+static int audio_iio_aux_get_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct audio_iio_aux_chan *chan = (struct audio_iio_aux_chan *)kcontrol->private_value;
+ int max = chan->max;
+ int min = chan->min;
+ bool invert_range = chan->is_invert_range;
+ int ret;
+ int val;
+
+ ret = iio_read_channel_raw(chan->iio_chan, &val);
+ if (ret < 0)
+ return ret;
+
+ ucontrol->value.integer.value[0] = val - min;
+ if (invert_range)
+ ucontrol->value.integer.value[0] = max - ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+static int audio_iio_aux_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct audio_iio_aux_chan *chan = (struct audio_iio_aux_chan *)kcontrol->private_value;
+ int max = chan->max;
+ int min = chan->min;
+ bool invert_range = chan->is_invert_range;
+ int val;
+ int ret;
+ int tmp;
+
+ val = ucontrol->value.integer.value[0];
+ if (val < 0)
+ return -EINVAL;
+ if (val > max - min)
+ return -EINVAL;
+
+ val = val + min;
+ if (invert_range)
+ val = max - val;
+
+ ret = iio_read_channel_raw(chan->iio_chan, &tmp);
+ if (ret < 0)
+ return ret;
+
+ if (tmp == val)
+ return 0;
+
+ ret = iio_write_channel_raw(chan->iio_chan, val);
+ if (ret)
+ return ret;
+
+ return 1; /* The value changed */
+}
+
+static int audio_iio_aux_add_controls(struct snd_soc_component *component,
+ struct audio_iio_aux_chan *chan)
+{
+ struct snd_kcontrol_new control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = chan->name,
+ .info = audio_iio_aux_info_volsw,
+ .get = audio_iio_aux_get_volsw,
+ .put = audio_iio_aux_put_volsw,
+ .private_value = (unsigned long)chan,
+ };
+
+ return snd_soc_add_component_controls(component, &control, 1);
+}
+
+/*
+ * These data could be on stack but they are pretty big.
+ * As ASoC internally copy them and protect them against concurrent accesses
+ * (snd_soc_bind_card() protects using client_mutex), keep them in the global
+ * data area.
+ */
+static struct snd_soc_dapm_widget widgets[3];
+static struct snd_soc_dapm_route routes[2];
+
+/* Be sure sizes are correct (need 3 widgets and 2 routes) */
+static_assert(ARRAY_SIZE(widgets) >= 3, "3 widgets are needed");
+static_assert(ARRAY_SIZE(routes) >= 2, "2 routes are needed");
+
+static int audio_iio_aux_add_dapms(struct snd_soc_component *component,
+ struct audio_iio_aux_chan *chan)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ char *output_name;
+ char *input_name;
+ char *pga_name;
+ int ret;
+
+ input_name = kasprintf(GFP_KERNEL, "%s IN", chan->name);
+ if (!input_name)
+ return -ENOMEM;
+
+ output_name = kasprintf(GFP_KERNEL, "%s OUT", chan->name);
+ if (!output_name) {
+ ret = -ENOMEM;
+ goto out_free_input_name;
+ }
+
+ pga_name = kasprintf(GFP_KERNEL, "%s PGA", chan->name);
+ if (!pga_name) {
+ ret = -ENOMEM;
+ goto out_free_output_name;
+ }
+
+ widgets[0] = SND_SOC_DAPM_INPUT(input_name);
+ widgets[1] = SND_SOC_DAPM_OUTPUT(output_name);
+ widgets[2] = SND_SOC_DAPM_PGA(pga_name, SND_SOC_NOPM, 0, 0, NULL, 0);
+ ret = snd_soc_dapm_new_controls(dapm, widgets, 3);
+ if (ret)
+ goto out_free_pga_name;
+
+ routes[0].sink = pga_name;
+ routes[0].control = NULL;
+ routes[0].source = input_name;
+ routes[1].sink = output_name;
+ routes[1].control = NULL;
+ routes[1].source = pga_name;
+ ret = snd_soc_dapm_add_routes(dapm, routes, 2);
+
+ /* Allocated names are no more needed (duplicated in ASoC internals) */
+
+out_free_pga_name:
+ kfree(pga_name);
+out_free_output_name:
+ kfree(output_name);
+out_free_input_name:
+ kfree(input_name);
+ return ret;
+}
+
+static int audio_iio_aux_component_probe(struct snd_soc_component *component)
+{
+ struct audio_iio_aux *iio_aux = snd_soc_component_get_drvdata(component);
+ struct audio_iio_aux_chan *chan;
+ int ret;
+ int i;
+
+ for (i = 0; i < iio_aux->num_chans; i++) {
+ chan = iio_aux->chans + i;
+
+ ret = iio_read_max_channel_raw(chan->iio_chan, &chan->max);
+ if (ret)
+ return dev_err_probe(component->dev, ret,
+ "chan[%d] %s: Cannot get max raw value\n",
+ i, chan->name);
+
+ ret = iio_read_min_channel_raw(chan->iio_chan, &chan->min);
+ if (ret)
+ return dev_err_probe(component->dev, ret,
+ "chan[%d] %s: Cannot get min raw value\n",
+ i, chan->name);
+
+ if (chan->min > chan->max) {
+ /*
+ * This should never happen but to avoid any check
+ * later, just swap values here to ensure that the
+ * minimum value is lower than the maximum value.
+ */
+ dev_dbg(component->dev, "chan[%d] %s: Swap min and max\n",
+ i, chan->name);
+ swap(chan->min, chan->max);
+ }
+
+ /* Set initial value */
+ ret = iio_write_channel_raw(chan->iio_chan,
+ chan->is_invert_range ? chan->max : chan->min);
+ if (ret)
+ return dev_err_probe(component->dev, ret,
+ "chan[%d] %s: Cannot set initial value\n",
+ i, chan->name);
+
+ ret = audio_iio_aux_add_controls(component, chan);
+ if (ret)
+ return ret;
+
+ ret = audio_iio_aux_add_dapms(component, chan);
+ if (ret)
+ return ret;
+
+ dev_dbg(component->dev, "chan[%d]: Added %s (min=%d, max=%d, invert=%s)\n",
+ i, chan->name, chan->min, chan->max,
+ str_on_off(chan->is_invert_range));
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver audio_iio_aux_component_driver = {
+ .probe = audio_iio_aux_component_probe,
+};
+
+static int audio_iio_aux_probe(struct platform_device *pdev)
+{
+ struct audio_iio_aux_chan *iio_aux_chan;
+ struct device *dev = &pdev->dev;
+ struct audio_iio_aux *iio_aux;
+ const char **names;
+ u32 *invert_ranges;
+ int count;
+ int ret;
+ int i;
+
+ iio_aux = devm_kzalloc(dev, sizeof(*iio_aux), GFP_KERNEL);
+ if (!iio_aux)
+ return -ENOMEM;
+
+ iio_aux->dev = dev;
+
+ count = device_property_string_array_count(dev, "io-channel-names");
+ if (count < 0)
+ return dev_err_probe(dev, count, "failed to count io-channel-names\n");
+
+ iio_aux->num_chans = count;
+
+ iio_aux->chans = devm_kmalloc_array(dev, iio_aux->num_chans,
+ sizeof(*iio_aux->chans), GFP_KERNEL);
+ if (!iio_aux->chans)
+ return -ENOMEM;
+
+ names = kcalloc(iio_aux->num_chans, sizeof(*names), GFP_KERNEL);
+ if (!names)
+ return -ENOMEM;
+
+ invert_ranges = kcalloc(iio_aux->num_chans, sizeof(*invert_ranges), GFP_KERNEL);
+ if (!invert_ranges) {
+ ret = -ENOMEM;
+ goto out_free_names;
+ }
+
+ ret = device_property_read_string_array(dev, "io-channel-names",
+ names, iio_aux->num_chans);
+ if (ret < 0) {
+ dev_err_probe(dev, ret, "failed to read io-channel-names\n");
+ goto out_free_invert_ranges;
+ }
+
+ /*
+ * snd-control-invert-range is optional and can contain fewer items
+ * than the number of channels. Unset values default to 0.
+ */
+ count = device_property_count_u32(dev, "snd-control-invert-range");
+ if (count > 0) {
+ count = min_t(unsigned int, count, iio_aux->num_chans);
+ ret = device_property_read_u32_array(dev, "snd-control-invert-range",
+ invert_ranges, count);
+ if (ret < 0) {
+ dev_err_probe(dev, ret, "failed to read snd-control-invert-range\n");
+ goto out_free_invert_ranges;
+ }
+ }
+
+ for (i = 0; i < iio_aux->num_chans; i++) {
+ iio_aux_chan = iio_aux->chans + i;
+ iio_aux_chan->name = names[i];
+ iio_aux_chan->is_invert_range = invert_ranges[i];
+
+ iio_aux_chan->iio_chan = devm_iio_channel_get(dev, iio_aux_chan->name);
+ if (IS_ERR(iio_aux_chan->iio_chan)) {
+ ret = PTR_ERR(iio_aux_chan->iio_chan);
+ dev_err_probe(dev, ret, "get IIO channel '%s' failed\n",
+ iio_aux_chan->name);
+ goto out_free_invert_ranges;
+ }
+ }
+
+ platform_set_drvdata(pdev, iio_aux);
+
+ ret = devm_snd_soc_register_component(dev, &audio_iio_aux_component_driver,
+ NULL, 0);
+out_free_invert_ranges:
+ kfree(invert_ranges);
+out_free_names:
+ kfree(names);
+ return ret;
+}
+
+static const struct of_device_id audio_iio_aux_ids[] = {
+ { .compatible = "audio-iio-aux" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, audio_iio_aux_ids);
+
+static struct platform_driver audio_iio_aux_driver = {
+ .driver = {
+ .name = "audio-iio-aux",
+ .of_match_table = audio_iio_aux_ids,
+ },
+ .probe = audio_iio_aux_probe,
+};
+module_platform_driver(audio_iio_aux_driver);
+
+MODULE_AUTHOR("Herve Codina <herve.codina@bootlin.com>");
+MODULE_DESCRIPTION("IIO ALSA SoC aux driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l36.c b/sound/soc/codecs/cs35l36.c
index 04ba7f25012e..20084c7d3acb 100644
--- a/sound/soc/codecs/cs35l36.c
+++ b/sound/soc/codecs/cs35l36.c
@@ -1312,7 +1312,7 @@ static struct regmap_config cs35l36_regmap = {
.precious_reg = cs35l36_precious_reg,
.volatile_reg = cs35l36_volatile_reg,
.readable_reg = cs35l36_readable_reg,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static irqreturn_t cs35l36_irq(int irq, void *data)
diff --git a/sound/soc/codecs/cs35l41-lib.c b/sound/soc/codecs/cs35l41-lib.c
index a9c559a676e7..4ec306cd2f47 100644
--- a/sound/soc/codecs/cs35l41-lib.c
+++ b/sound/soc/codecs/cs35l41-lib.c
@@ -743,7 +743,7 @@ struct regmap_config cs35l41_regmap_i2c = {
.volatile_reg = cs35l41_volatile_reg,
.readable_reg = cs35l41_readable_reg,
.precious_reg = cs35l41_precious_reg,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
EXPORT_SYMBOL_GPL(cs35l41_regmap_i2c);
@@ -760,7 +760,7 @@ struct regmap_config cs35l41_regmap_spi = {
.volatile_reg = cs35l41_volatile_reg,
.readable_reg = cs35l41_readable_reg,
.precious_reg = cs35l41_precious_reg,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
EXPORT_SYMBOL_GPL(cs35l41_regmap_spi);
diff --git a/sound/soc/codecs/cs35l45-tables.c b/sound/soc/codecs/cs35l45-tables.c
index 066f83c0c7ac..621af1785979 100644
--- a/sound/soc/codecs/cs35l45-tables.c
+++ b/sound/soc/codecs/cs35l45-tables.c
@@ -255,7 +255,7 @@ const struct regmap_config cs35l45_i2c_regmap = {
.num_reg_defaults = ARRAY_SIZE(cs35l45_defaults),
.volatile_reg = cs35l45_volatile_reg,
.readable_reg = cs35l45_readable_reg,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
EXPORT_SYMBOL_NS_GPL(cs35l45_i2c_regmap, SND_SOC_CS35L45);
@@ -271,7 +271,7 @@ const struct regmap_config cs35l45_spi_regmap = {
.num_reg_defaults = ARRAY_SIZE(cs35l45_defaults),
.volatile_reg = cs35l45_volatile_reg,
.readable_reg = cs35l45_readable_reg,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
EXPORT_SYMBOL_NS_GPL(cs35l45_spi_regmap, SND_SOC_CS35L45);
diff --git a/sound/soc/codecs/cs35l56-i2c.c b/sound/soc/codecs/cs35l56-i2c.c
index ed2a41943d97..888fdfa5f5db 100644
--- a/sound/soc/codecs/cs35l56-i2c.c
+++ b/sound/soc/codecs/cs35l56-i2c.c
@@ -26,14 +26,14 @@ static int cs35l56_i2c_probe(struct i2c_client *client)
if (!cs35l56)
return -ENOMEM;
- cs35l56->dev = dev;
- cs35l56->can_hibernate = true;
+ cs35l56->base.dev = dev;
+ cs35l56->base.can_hibernate = true;
i2c_set_clientdata(client, cs35l56);
- cs35l56->regmap = devm_regmap_init_i2c(client, regmap_config);
- if (IS_ERR(cs35l56->regmap)) {
- ret = PTR_ERR(cs35l56->regmap);
- return dev_err_probe(cs35l56->dev, ret, "Failed to allocate register map\n");
+ cs35l56->base.regmap = devm_regmap_init_i2c(client, regmap_config);
+ if (IS_ERR(cs35l56->base.regmap)) {
+ ret = PTR_ERR(cs35l56->base.regmap);
+ return dev_err_probe(cs35l56->base.dev, ret, "Failed to allocate register map\n");
}
ret = cs35l56_common_probe(cs35l56);
@@ -42,7 +42,7 @@ static int cs35l56_i2c_probe(struct i2c_client *client)
ret = cs35l56_init(cs35l56);
if (ret == 0)
- ret = cs35l56_irq_request(cs35l56, client->irq);
+ ret = cs35l56_irq_request(&cs35l56->base, client->irq);
if (ret < 0)
cs35l56_remove(cs35l56);
diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c
index 2cde78605ba9..b433266b7844 100644
--- a/sound/soc/codecs/cs35l56-sdw.c
+++ b/sound/soc/codecs/cs35l56-sdw.c
@@ -166,13 +166,13 @@ static void cs35l56_sdw_init(struct sdw_slave *peripheral)
struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev);
int ret;
- pm_runtime_get_noresume(cs35l56->dev);
+ pm_runtime_get_noresume(cs35l56->base.dev);
- regcache_cache_only(cs35l56->regmap, false);
+ regcache_cache_only(cs35l56->base.regmap, false);
ret = cs35l56_init(cs35l56);
if (ret < 0) {
- regcache_cache_only(cs35l56->regmap, true);
+ regcache_cache_only(cs35l56->base.regmap, true);
goto out;
}
@@ -180,15 +180,15 @@ static void cs35l56_sdw_init(struct sdw_slave *peripheral)
* cs35l56_init can return with !init_done if it triggered
* a soft reset.
*/
- if (cs35l56->init_done) {
+ if (cs35l56->base.init_done) {
/* Enable SoundWire interrupts */
sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1,
CS35L56_SDW_INT_MASK_CODEC_IRQ);
}
out:
- pm_runtime_mark_last_busy(cs35l56->dev);
- pm_runtime_put_autosuspend(cs35l56->dev);
+ pm_runtime_mark_last_busy(cs35l56->base.dev);
+ pm_runtime_put_autosuspend(cs35l56->base.dev);
}
static int cs35l56_sdw_interrupt(struct sdw_slave *peripheral,
@@ -198,7 +198,7 @@ static int cs35l56_sdw_interrupt(struct sdw_slave *peripheral,
/* SoundWire core holds our pm_runtime when calling this function. */
- dev_dbg(cs35l56->dev, "int control_port=%#x\n", status->control_port);
+ dev_dbg(cs35l56->base.dev, "int control_port=%#x\n", status->control_port);
if ((status->control_port & SDW_SCP_INT1_IMPL_DEF) == 0)
return 0;
@@ -207,7 +207,7 @@ static int cs35l56_sdw_interrupt(struct sdw_slave *peripheral,
* Prevent bus manager suspending and possibly issuing a
* bus-reset before the queued work has run.
*/
- pm_runtime_get_noresume(cs35l56->dev);
+ pm_runtime_get_noresume(cs35l56->base.dev);
/*
* Mask and clear until it has been handled. The read of GEN_INT_STAT_1
@@ -230,14 +230,14 @@ static void cs35l56_sdw_irq_work(struct work_struct *work)
struct cs35l56_private,
sdw_irq_work);
- cs35l56_irq(-1, cs35l56);
+ cs35l56_irq(-1, &cs35l56->base);
/* unmask interrupts */
if (!cs35l56->sdw_irq_no_unmask)
sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1,
CS35L56_SDW_INT_MASK_CODEC_IRQ);
- pm_runtime_put_autosuspend(cs35l56->dev);
+ pm_runtime_put_autosuspend(cs35l56->base.dev);
}
static int cs35l56_sdw_read_prop(struct sdw_slave *peripheral)
@@ -246,7 +246,7 @@ static int cs35l56_sdw_read_prop(struct sdw_slave *peripheral)
struct sdw_slave_prop *prop = &peripheral->prop;
struct sdw_dpn_prop *ports;
- ports = devm_kcalloc(cs35l56->dev, 2, sizeof(*ports), GFP_KERNEL);
+ ports = devm_kcalloc(cs35l56->base.dev, 2, sizeof(*ports), GFP_KERNEL);
if (!ports)
return -ENOMEM;
@@ -279,17 +279,17 @@ static int cs35l56_sdw_update_status(struct sdw_slave *peripheral,
switch (status) {
case SDW_SLAVE_ATTACHED:
- dev_dbg(cs35l56->dev, "%s: ATTACHED\n", __func__);
+ dev_dbg(cs35l56->base.dev, "%s: ATTACHED\n", __func__);
if (cs35l56->sdw_attached)
break;
- if (!cs35l56->init_done || cs35l56->soft_resetting)
+ if (!cs35l56->base.init_done || cs35l56->soft_resetting)
cs35l56_sdw_init(peripheral);
cs35l56->sdw_attached = true;
break;
case SDW_SLAVE_UNATTACHED:
- dev_dbg(cs35l56->dev, "%s: UNATTACHED\n", __func__);
+ dev_dbg(cs35l56->base.dev, "%s: UNATTACHED\n", __func__);
cs35l56->sdw_attached = false;
break;
default:
@@ -305,7 +305,7 @@ static int cs35l56_a1_kick_divider(struct cs35l56_private *cs35l56,
unsigned int curr_scale_reg, next_scale_reg;
int curr_scale, next_scale, ret;
- if (!cs35l56->init_done)
+ if (!cs35l56->base.init_done)
return 0;
if (peripheral->bus->params.curr_bank) {
@@ -324,13 +324,13 @@ static int cs35l56_a1_kick_divider(struct cs35l56_private *cs35l56,
*/
curr_scale = sdw_read_no_pm(peripheral, curr_scale_reg);
if (curr_scale < 0) {
- dev_err(cs35l56->dev, "Failed to read current clock scale: %d\n", curr_scale);
+ dev_err(cs35l56->base.dev, "Failed to read current clock scale: %d\n", curr_scale);
return curr_scale;
}
next_scale = sdw_read_no_pm(peripheral, next_scale_reg);
if (next_scale < 0) {
- dev_err(cs35l56->dev, "Failed to read next clock scale: %d\n", next_scale);
+ dev_err(cs35l56->base.dev, "Failed to read next clock scale: %d\n", next_scale);
return next_scale;
}
@@ -338,7 +338,8 @@ static int cs35l56_a1_kick_divider(struct cs35l56_private *cs35l56,
next_scale = cs35l56->old_sdw_clock_scale;
ret = sdw_write_no_pm(peripheral, next_scale_reg, next_scale);
if (ret < 0) {
- dev_err(cs35l56->dev, "Failed to modify current clock scale: %d\n", ret);
+ dev_err(cs35l56->base.dev, "Failed to modify current clock scale: %d\n",
+ ret);
return ret;
}
}
@@ -346,11 +347,11 @@ static int cs35l56_a1_kick_divider(struct cs35l56_private *cs35l56,
cs35l56->old_sdw_clock_scale = curr_scale;
ret = sdw_write_no_pm(peripheral, curr_scale_reg, CS35L56_SDW_INVALID_BUS_SCALE);
if (ret < 0) {
- dev_err(cs35l56->dev, "Failed to modify current clock scale: %d\n", ret);
+ dev_err(cs35l56->base.dev, "Failed to modify current clock scale: %d\n", ret);
return ret;
}
- dev_dbg(cs35l56->dev, "Next bus scale: %#x\n", next_scale);
+ dev_dbg(cs35l56->base.dev, "Next bus scale: %#x\n", next_scale);
return 0;
}
@@ -362,9 +363,10 @@ static int cs35l56_sdw_bus_config(struct sdw_slave *peripheral,
int sclk;
sclk = params->curr_dr_freq / 2;
- dev_dbg(cs35l56->dev, "%s: sclk=%u c=%u r=%u\n", __func__, sclk, params->col, params->row);
+ dev_dbg(cs35l56->base.dev, "%s: sclk=%u c=%u r=%u\n",
+ __func__, sclk, params->col, params->row);
- if (cs35l56->rev < 0xb0)
+ if (cs35l56->base.rev < 0xb0)
return cs35l56_a1_kick_divider(cs35l56, peripheral);
return 0;
@@ -376,7 +378,7 @@ static int __maybe_unused cs35l56_sdw_clk_stop(struct sdw_slave *peripheral,
{
struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev);
- dev_dbg(cs35l56->dev, "%s: mode:%d type:%d\n", __func__, mode, type);
+ dev_dbg(cs35l56->base.dev, "%s: mode:%d type:%d\n", __func__, mode, type);
return 0;
}
@@ -397,10 +399,10 @@ static int __maybe_unused cs35l56_sdw_handle_unattach(struct cs35l56_private *cs
if (peripheral->unattach_request) {
/* Cannot access registers until bus is re-initialized. */
- dev_dbg(cs35l56->dev, "Wait for initialization_complete\n");
+ dev_dbg(cs35l56->base.dev, "Wait for initialization_complete\n");
if (!wait_for_completion_timeout(&peripheral->initialization_complete,
msecs_to_jiffies(5000))) {
- dev_err(cs35l56->dev, "initialization_complete timed out\n");
+ dev_err(cs35l56->base.dev, "initialization_complete timed out\n");
return -ETIMEDOUT;
}
@@ -419,10 +421,10 @@ static int __maybe_unused cs35l56_sdw_runtime_suspend(struct device *dev)
{
struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
- if (!cs35l56->init_done)
+ if (!cs35l56->base.init_done)
return 0;
- return cs35l56_runtime_suspend(dev);
+ return cs35l56_runtime_suspend_common(&cs35l56->base);
}
static int __maybe_unused cs35l56_sdw_runtime_resume(struct device *dev)
@@ -432,14 +434,14 @@ static int __maybe_unused cs35l56_sdw_runtime_resume(struct device *dev)
dev_dbg(dev, "Runtime resume\n");
- if (!cs35l56->init_done)
+ if (!cs35l56->base.init_done)
return 0;
ret = cs35l56_sdw_handle_unattach(cs35l56);
if (ret < 0)
return ret;
- ret = cs35l56_runtime_resume_common(cs35l56);
+ ret = cs35l56_runtime_resume_common(&cs35l56->base, true);
if (ret)
return ret;
@@ -454,7 +456,7 @@ static int __maybe_unused cs35l56_sdw_system_suspend(struct device *dev)
{
struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
- if (!cs35l56->init_done)
+ if (!cs35l56->base.init_done)
return 0;
/*
@@ -493,21 +495,21 @@ static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_devi
if (!cs35l56)
return -ENOMEM;
- cs35l56->dev = dev;
+ cs35l56->base.dev = dev;
cs35l56->sdw_peripheral = peripheral;
INIT_WORK(&cs35l56->sdw_irq_work, cs35l56_sdw_irq_work);
dev_set_drvdata(dev, cs35l56);
- cs35l56->regmap = devm_regmap_init(dev, &cs35l56_regmap_bus_sdw,
+ cs35l56->base.regmap = devm_regmap_init(dev, &cs35l56_regmap_bus_sdw,
peripheral, &cs35l56_regmap_sdw);
- if (IS_ERR(cs35l56->regmap)) {
- ret = PTR_ERR(cs35l56->regmap);
+ if (IS_ERR(cs35l56->base.regmap)) {
+ ret = PTR_ERR(cs35l56->base.regmap);
return dev_err_probe(dev, ret, "Failed to allocate register map\n");
}
/* Start in cache-only until device is enumerated */
- regcache_cache_only(cs35l56->regmap, true);
+ regcache_cache_only(cs35l56->base.regmap, true);
ret = cs35l56_common_probe(cs35l56);
if (ret != 0)
diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c
index 60da8c75b7b9..ae373f335ea8 100644
--- a/sound/soc/codecs/cs35l56-shared.c
+++ b/sound/soc/codecs/cs35l56-shared.c
@@ -11,6 +11,20 @@
#include "cs35l56.h"
+static const struct reg_sequence cs35l56_patch[] = {
+ /* These are not reset by a soft-reset, so patch to defaults. */
+ { CS35L56_MAIN_RENDER_USER_MUTE, 0x00000000 },
+ { CS35L56_MAIN_RENDER_USER_VOLUME, 0x00000000 },
+ { CS35L56_MAIN_POSTURE_NUMBER, 0x00000000 },
+};
+
+int cs35l56_set_patch(struct cs35l56_base *cs35l56_base)
+{
+ return regmap_register_patch(cs35l56_base->regmap, cs35l56_patch,
+ ARRAY_SIZE(cs35l56_patch));
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_set_patch, SND_SOC_CS35L56_SHARED);
+
static const struct reg_default cs35l56_reg_defaults[] = {
{ CS35L56_ASP1_ENABLES1, 0x00000000 },
{ CS35L56_ASP1_CONTROL1, 0x00000028 },
@@ -35,9 +49,9 @@ static const struct reg_default cs35l56_reg_defaults[] = {
{ CS35L56_IRQ1_MASK_8, 0xfc000fff },
{ CS35L56_IRQ1_MASK_18, 0x1f7df0ff },
{ CS35L56_IRQ1_MASK_20, 0x15c00000 },
- /* CS35L56_MAIN_RENDER_USER_MUTE - soft register, no default */
- /* CS35L56_MAIN_RENDER_USER_VOLUME - soft register, no default */
- /* CS35L56_MAIN_POSTURE_NUMBER - soft register, no default */
+ { CS35L56_MAIN_RENDER_USER_MUTE, 0x00000000 },
+ { CS35L56_MAIN_RENDER_USER_VOLUME, 0x00000000 },
+ { CS35L56_MAIN_POSTURE_NUMBER, 0x00000000 },
};
static bool cs35l56_is_dsp_memory(unsigned int reg)
@@ -181,33 +195,463 @@ static bool cs35l56_volatile_reg(struct device *dev, unsigned int reg)
}
}
-static const u32 cs35l56_firmware_registers[] = {
- CS35L56_MAIN_RENDER_USER_MUTE,
- CS35L56_MAIN_RENDER_USER_VOLUME,
- CS35L56_MAIN_POSTURE_NUMBER,
+int cs35l56_mbox_send(struct cs35l56_base *cs35l56_base, unsigned int command)
+{
+ unsigned int val;
+ int ret;
+
+ regmap_write(cs35l56_base->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1, command);
+ ret = regmap_read_poll_timeout(cs35l56_base->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1,
+ val, (val == 0),
+ CS35L56_MBOX_POLL_US, CS35L56_MBOX_TIMEOUT_US);
+ if (ret) {
+ dev_warn(cs35l56_base->dev, "MBOX command %#x failed: %d\n", command, ret);
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_mbox_send, SND_SOC_CS35L56_SHARED);
+
+int cs35l56_firmware_shutdown(struct cs35l56_base *cs35l56_base)
+{
+ int ret;
+ unsigned int reg;
+ unsigned int val;
+
+ ret = cs35l56_mbox_send(cs35l56_base, CS35L56_MBOX_CMD_SHUTDOWN);
+ if (ret)
+ return ret;
+
+ if (cs35l56_base->rev < CS35L56_REVID_B0)
+ reg = CS35L56_DSP1_PM_CUR_STATE_A1;
+ else
+ reg = CS35L56_DSP1_PM_CUR_STATE;
+
+ ret = regmap_read_poll_timeout(cs35l56_base->regmap, reg,
+ val, (val == CS35L56_HALO_STATE_SHUTDOWN),
+ CS35L56_HALO_STATE_POLL_US,
+ CS35L56_HALO_STATE_TIMEOUT_US);
+ if (ret < 0)
+ dev_err(cs35l56_base->dev, "Failed to poll PM_CUR_STATE to 1 is %d (ret %d)\n",
+ val, ret);
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_firmware_shutdown, SND_SOC_CS35L56_SHARED);
+
+int cs35l56_wait_for_firmware_boot(struct cs35l56_base *cs35l56_base)
+{
+ unsigned int reg;
+ unsigned int val;
+ int ret;
+
+ if (cs35l56_base->rev < CS35L56_REVID_B0)
+ reg = CS35L56_DSP1_HALO_STATE_A1;
+ else
+ reg = CS35L56_DSP1_HALO_STATE;
+
+ ret = regmap_read_poll_timeout(cs35l56_base->regmap, reg,
+ val,
+ (val < 0xFFFF) && (val >= CS35L56_HALO_STATE_BOOT_DONE),
+ CS35L56_HALO_STATE_POLL_US,
+ CS35L56_HALO_STATE_TIMEOUT_US);
+
+ if ((ret < 0) && (ret != -ETIMEDOUT)) {
+ dev_err(cs35l56_base->dev, "Failed to read HALO_STATE: %d\n", ret);
+ return ret;
+ }
+
+ if ((ret == -ETIMEDOUT) || (val != CS35L56_HALO_STATE_BOOT_DONE)) {
+ dev_err(cs35l56_base->dev, "Firmware boot fail: HALO_STATE=%#x\n", val);
+ return -EIO;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_wait_for_firmware_boot, SND_SOC_CS35L56_SHARED);
+
+void cs35l56_wait_control_port_ready(void)
+{
+ /* Wait for control port to be ready (datasheet tIRS). */
+ usleep_range(CS35L56_CONTROL_PORT_READY_US, 2 * CS35L56_CONTROL_PORT_READY_US);
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_wait_control_port_ready, SND_SOC_CS35L56_SHARED);
+
+void cs35l56_wait_min_reset_pulse(void)
+{
+ /* Satisfy minimum reset pulse width spec */
+ usleep_range(CS35L56_RESET_PULSE_MIN_US, 2 * CS35L56_RESET_PULSE_MIN_US);
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_wait_min_reset_pulse, SND_SOC_CS35L56_SHARED);
+
+static const struct reg_sequence cs35l56_system_reset_seq[] = {
+ REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_SYSTEM_RESET),
};
-void cs35l56_reread_firmware_registers(struct device *dev, struct regmap *regmap)
+void cs35l56_system_reset(struct cs35l56_base *cs35l56_base, bool is_soundwire)
+{
+ /*
+ * Must enter cache-only first so there can't be any more register
+ * accesses other than the controlled system reset sequence below.
+ */
+ regcache_cache_only(cs35l56_base->regmap, true);
+ regmap_multi_reg_write_bypassed(cs35l56_base->regmap,
+ cs35l56_system_reset_seq,
+ ARRAY_SIZE(cs35l56_system_reset_seq));
+
+ /* On SoundWire the registers won't be accessible until it re-enumerates. */
+ if (is_soundwire)
+ return;
+
+ cs35l56_wait_control_port_ready();
+ regcache_cache_only(cs35l56_base->regmap, false);
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_system_reset, SND_SOC_CS35L56_SHARED);
+
+int cs35l56_irq_request(struct cs35l56_base *cs35l56_base, int irq)
+{
+ int ret;
+
+ if (!irq)
+ return 0;
+
+ ret = devm_request_threaded_irq(cs35l56_base->dev, irq, NULL, cs35l56_irq,
+ IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW,
+ "cs35l56", cs35l56_base);
+ if (!ret)
+ cs35l56_base->irq = irq;
+ else
+ dev_err(cs35l56_base->dev, "Failed to get IRQ: %d\n", ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_irq_request, SND_SOC_CS35L56_SHARED);
+
+irqreturn_t cs35l56_irq(int irq, void *data)
+{
+ struct cs35l56_base *cs35l56_base = data;
+ unsigned int status1 = 0, status8 = 0, status20 = 0;
+ unsigned int mask1, mask8, mask20;
+ unsigned int val;
+ int rv;
+
+ irqreturn_t ret = IRQ_NONE;
+
+ if (!cs35l56_base->init_done)
+ return IRQ_NONE;
+
+ mutex_lock(&cs35l56_base->irq_lock);
+
+ rv = pm_runtime_resume_and_get(cs35l56_base->dev);
+ if (rv < 0) {
+ dev_err(cs35l56_base->dev, "irq: failed to get pm_runtime: %d\n", rv);
+ goto err_unlock;
+ }
+
+ regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_STATUS, &val);
+ if ((val & CS35L56_IRQ1_STS_MASK) == 0) {
+ dev_dbg(cs35l56_base->dev, "Spurious IRQ: no pending interrupt\n");
+ goto err;
+ }
+
+ /* Ack interrupts */
+ regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_EINT_1, &status1);
+ regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_MASK_1, &mask1);
+ status1 &= ~mask1;
+ regmap_write(cs35l56_base->regmap, CS35L56_IRQ1_EINT_1, status1);
+
+ regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_EINT_8, &status8);
+ regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_MASK_8, &mask8);
+ status8 &= ~mask8;
+ regmap_write(cs35l56_base->regmap, CS35L56_IRQ1_EINT_8, status8);
+
+ regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_EINT_20, &status20);
+ regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_MASK_20, &mask20);
+ status20 &= ~mask20;
+ /* We don't want EINT20 but they default to unmasked: force mask */
+ regmap_write(cs35l56_base->regmap, CS35L56_IRQ1_MASK_20, 0xffffffff);
+
+ dev_dbg(cs35l56_base->dev, "%s: %#x %#x\n", __func__, status1, status8);
+
+ /* Check to see if unmasked bits are active */
+ if (!status1 && !status8 && !status20)
+ goto err;
+
+ if (status1 & CS35L56_AMP_SHORT_ERR_EINT1_MASK)
+ dev_crit(cs35l56_base->dev, "Amp short error\n");
+
+ if (status8 & CS35L56_TEMP_ERR_EINT1_MASK)
+ dev_crit(cs35l56_base->dev, "Overtemp error\n");
+
+ ret = IRQ_HANDLED;
+
+err:
+ pm_runtime_put(cs35l56_base->dev);
+err_unlock:
+ mutex_unlock(&cs35l56_base->irq_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_irq, SND_SOC_CS35L56_SHARED);
+
+int cs35l56_is_fw_reload_needed(struct cs35l56_base *cs35l56_base)
+{
+ unsigned int val;
+ int ret;
+
+ /* Nothing to re-patch if we haven't done any patching yet. */
+ if (!cs35l56_base->fw_patched)
+ return false;
+
+ /*
+ * If we have control of RESET we will have asserted it so the firmware
+ * will need re-patching.
+ */
+ if (cs35l56_base->reset_gpio)
+ return true;
+
+ /*
+ * In secure mode FIRMWARE_MISSING is cleared by the BIOS loader so
+ * can't be used here to test for memory retention.
+ * Assume that tuning must be re-loaded.
+ */
+ if (cs35l56_base->secured)
+ return true;
+
+ ret = pm_runtime_resume_and_get(cs35l56_base->dev);
+ if (ret) {
+ dev_err(cs35l56_base->dev, "Failed to runtime_get: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_read(cs35l56_base->regmap, CS35L56_PROTECTION_STATUS, &val);
+ if (ret)
+ dev_err(cs35l56_base->dev, "Failed to read PROTECTION_STATUS: %d\n", ret);
+ else
+ ret = !!(val & CS35L56_FIRMWARE_MISSING);
+
+ pm_runtime_put_autosuspend(cs35l56_base->dev);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_is_fw_reload_needed, SND_SOC_CS35L56_SHARED);
+
+static const struct reg_sequence cs35l56_hibernate_seq[] = {
+ /* This must be the last register access */
+ REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_HIBERNATE_NOW),
+};
+
+static const struct reg_sequence cs35l56_hibernate_wake_seq[] = {
+ REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_WAKEUP),
+};
+
+int cs35l56_runtime_suspend_common(struct cs35l56_base *cs35l56_base)
{
- int i;
unsigned int val;
+ int ret;
+
+ if (!cs35l56_base->init_done)
+ return 0;
- for (i = 0; i < ARRAY_SIZE(cs35l56_firmware_registers); i++) {
- regmap_read(regmap, cs35l56_firmware_registers[i], &val);
- dev_dbg(dev, "%s: %d: %#x: %#x\n", __func__,
- i, cs35l56_firmware_registers[i], val);
+ /* Firmware must have entered a power-save state */
+ ret = regmap_read_poll_timeout(cs35l56_base->regmap,
+ CS35L56_TRANSDUCER_ACTUAL_PS,
+ val, (val >= CS35L56_PS3),
+ CS35L56_PS3_POLL_US,
+ CS35L56_PS3_TIMEOUT_US);
+ if (ret)
+ dev_warn(cs35l56_base->dev, "PS3 wait failed: %d\n", ret);
+
+ /* Clear BOOT_DONE so it can be used to detect a reboot */
+ regmap_write(cs35l56_base->regmap, CS35L56_IRQ1_EINT_4, CS35L56_OTP_BOOT_DONE_MASK);
+
+ if (!cs35l56_base->can_hibernate) {
+ regcache_cache_only(cs35l56_base->regmap, true);
+ dev_dbg(cs35l56_base->dev, "Suspended: no hibernate");
+
+ return 0;
}
+
+ /*
+ * Enable auto-hibernate. If it is woken by some other wake source
+ * it will automatically return to hibernate.
+ */
+ cs35l56_mbox_send(cs35l56_base, CS35L56_MBOX_CMD_ALLOW_AUTO_HIBERNATE);
+
+ /*
+ * Must enter cache-only first so there can't be any more register
+ * accesses other than the controlled hibernate sequence below.
+ */
+ regcache_cache_only(cs35l56_base->regmap, true);
+
+ regmap_multi_reg_write_bypassed(cs35l56_base->regmap,
+ cs35l56_hibernate_seq,
+ ARRAY_SIZE(cs35l56_hibernate_seq));
+
+ dev_dbg(cs35l56_base->dev, "Suspended: hibernate");
+
+ return 0;
}
-EXPORT_SYMBOL_NS_GPL(cs35l56_reread_firmware_registers, SND_SOC_CS35L56_SHARED);
+EXPORT_SYMBOL_NS_GPL(cs35l56_runtime_suspend_common, SND_SOC_CS35L56_SHARED);
+
+int cs35l56_runtime_resume_common(struct cs35l56_base *cs35l56_base, bool is_soundwire)
+{
+ unsigned int val;
+ int ret;
+
+ if (!cs35l56_base->init_done)
+ return 0;
+
+ if (!cs35l56_base->can_hibernate)
+ goto out_sync;
+
+ if (!is_soundwire) {
+ /*
+ * Dummy transaction to trigger I2C/SPI auto-wake. This will NAK on I2C.
+ * Must be done before releasing cache-only.
+ */
+ regmap_multi_reg_write_bypassed(cs35l56_base->regmap,
+ cs35l56_hibernate_wake_seq,
+ ARRAY_SIZE(cs35l56_hibernate_wake_seq));
-const struct cs_dsp_region cs35l56_dsp1_regions[] = {
+ cs35l56_wait_control_port_ready();
+ }
+
+out_sync:
+ regcache_cache_only(cs35l56_base->regmap, false);
+
+ ret = cs35l56_wait_for_firmware_boot(cs35l56_base);
+ if (ret) {
+ dev_err(cs35l56_base->dev, "Hibernate wake failed: %d\n", ret);
+ goto err;
+ }
+
+ ret = cs35l56_mbox_send(cs35l56_base, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE);
+ if (ret)
+ goto err;
+
+ /* BOOT_DONE will be 1 if the amp reset */
+ regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_EINT_4, &val);
+ if (val & CS35L56_OTP_BOOT_DONE_MASK) {
+ dev_dbg(cs35l56_base->dev, "Registers reset in suspend\n");
+ regcache_mark_dirty(cs35l56_base->regmap);
+ }
+
+ regcache_sync(cs35l56_base->regmap);
+
+ dev_dbg(cs35l56_base->dev, "Resumed");
+
+ return 0;
+
+err:
+ regmap_write(cs35l56_base->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1,
+ CS35L56_MBOX_CMD_HIBERNATE_NOW);
+
+ regcache_cache_only(cs35l56_base->regmap, true);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_runtime_resume_common, SND_SOC_CS35L56_SHARED);
+
+static const struct cs_dsp_region cs35l56_dsp1_regions[] = {
{ .type = WMFW_HALO_PM_PACKED, .base = CS35L56_DSP1_PMEM_0 },
{ .type = WMFW_HALO_XM_PACKED, .base = CS35L56_DSP1_XMEM_PACKED_0 },
{ .type = WMFW_HALO_YM_PACKED, .base = CS35L56_DSP1_YMEM_PACKED_0 },
{ .type = WMFW_ADSP2_XM, .base = CS35L56_DSP1_XMEM_UNPACKED24_0 },
{ .type = WMFW_ADSP2_YM, .base = CS35L56_DSP1_YMEM_UNPACKED24_0 },
};
-EXPORT_SYMBOL_NS_GPL(cs35l56_dsp1_regions, SND_SOC_CS35L56_SHARED);
+
+void cs35l56_init_cs_dsp(struct cs35l56_base *cs35l56_base, struct cs_dsp *cs_dsp)
+{
+ cs_dsp->num = 1;
+ cs_dsp->type = WMFW_HALO;
+ cs_dsp->rev = 0;
+ cs_dsp->dev = cs35l56_base->dev;
+ cs_dsp->regmap = cs35l56_base->regmap;
+ cs_dsp->base = CS35L56_DSP1_CORE_BASE;
+ cs_dsp->base_sysinfo = CS35L56_DSP1_SYS_INFO_ID;
+ cs_dsp->mem = cs35l56_dsp1_regions;
+ cs_dsp->num_mems = ARRAY_SIZE(cs35l56_dsp1_regions);
+ cs_dsp->no_core_startstop = true;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_init_cs_dsp, SND_SOC_CS35L56_SHARED);
+
+int cs35l56_hw_init(struct cs35l56_base *cs35l56_base)
+{
+ int ret;
+ unsigned int devid, revid, otpid, secured;
+
+ /*
+ * If the system is not using a reset_gpio then issue a
+ * dummy read to force a wakeup.
+ */
+ if (!cs35l56_base->reset_gpio)
+ regmap_read(cs35l56_base->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1, &devid);
+
+ cs35l56_wait_control_port_ready();
+
+ /*
+ * The HALO_STATE register is in different locations on Ax and B0
+ * devices so the REVID needs to be determined before waiting for the
+ * firmware to boot.
+ */
+ ret = regmap_read(cs35l56_base->regmap, CS35L56_REVID, &revid);
+ if (ret < 0) {
+ dev_err(cs35l56_base->dev, "Get Revision ID failed\n");
+ return ret;
+ }
+ cs35l56_base->rev = revid & (CS35L56_AREVID_MASK | CS35L56_MTLREVID_MASK);
+
+ ret = cs35l56_wait_for_firmware_boot(cs35l56_base);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(cs35l56_base->regmap, CS35L56_DEVID, &devid);
+ if (ret < 0) {
+ dev_err(cs35l56_base->dev, "Get Device ID failed\n");
+ return ret;
+ }
+ devid &= CS35L56_DEVID_MASK;
+
+ switch (devid) {
+ case 0x35A56:
+ break;
+ default:
+ dev_err(cs35l56_base->dev, "Unknown device %x\n", devid);
+ return ret;
+ }
+
+ ret = regmap_read(cs35l56_base->regmap, CS35L56_DSP_RESTRICT_STS1, &secured);
+ if (ret) {
+ dev_err(cs35l56_base->dev, "Get Secure status failed\n");
+ return ret;
+ }
+
+ /* When any bus is restricted treat the device as secured */
+ if (secured & CS35L56_RESTRICTED_MASK)
+ cs35l56_base->secured = true;
+
+ ret = regmap_read(cs35l56_base->regmap, CS35L56_OTPID, &otpid);
+ if (ret < 0) {
+ dev_err(cs35l56_base->dev, "Get OTP ID failed\n");
+ return ret;
+ }
+
+ dev_info(cs35l56_base->dev, "Cirrus Logic CS35L56%s Rev %02X OTP%d\n",
+ cs35l56_base->secured ? "s" : "", cs35l56_base->rev, otpid);
+
+ /* Wake source and *_BLOCKED interrupts default to unmasked, so mask them */
+ regmap_write(cs35l56_base->regmap, CS35L56_IRQ1_MASK_20, 0xffffffff);
+ regmap_update_bits(cs35l56_base->regmap, CS35L56_IRQ1_MASK_1,
+ CS35L56_AMP_SHORT_ERR_EINT1_MASK,
+ 0);
+ regmap_update_bits(cs35l56_base->regmap, CS35L56_IRQ1_MASK_8,
+ CS35L56_TEMP_ERR_EINT1_MASK,
+ 0);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_hw_init, SND_SOC_CS35L56_SHARED);
static const u32 cs35l56_bclk_valid_for_pll_freq_table[] = {
[0x0C] = 128000,
@@ -319,7 +763,7 @@ struct regmap_config cs35l56_regmap_i2c = {
.volatile_reg = cs35l56_volatile_reg,
.readable_reg = cs35l56_readable_reg,
.precious_reg = cs35l56_precious_reg,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
EXPORT_SYMBOL_NS_GPL(cs35l56_regmap_i2c, SND_SOC_CS35L56_SHARED);
@@ -336,7 +780,7 @@ struct regmap_config cs35l56_regmap_spi = {
.volatile_reg = cs35l56_volatile_reg,
.readable_reg = cs35l56_readable_reg,
.precious_reg = cs35l56_precious_reg,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
EXPORT_SYMBOL_NS_GPL(cs35l56_regmap_spi, SND_SOC_CS35L56_SHARED);
@@ -352,7 +796,7 @@ struct regmap_config cs35l56_regmap_sdw = {
.volatile_reg = cs35l56_volatile_reg,
.readable_reg = cs35l56_readable_reg,
.precious_reg = cs35l56_precious_reg,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
EXPORT_SYMBOL_NS_GPL(cs35l56_regmap_sdw, SND_SOC_CS35L56_SHARED);
diff --git a/sound/soc/codecs/cs35l56-spi.c b/sound/soc/codecs/cs35l56-spi.c
index 996aab10500e..2057fce435be 100644
--- a/sound/soc/codecs/cs35l56-spi.c
+++ b/sound/soc/codecs/cs35l56-spi.c
@@ -25,13 +25,13 @@ static int cs35l56_spi_probe(struct spi_device *spi)
return -ENOMEM;
spi_set_drvdata(spi, cs35l56);
- cs35l56->regmap = devm_regmap_init_spi(spi, regmap_config);
- if (IS_ERR(cs35l56->regmap)) {
- ret = PTR_ERR(cs35l56->regmap);
+ cs35l56->base.regmap = devm_regmap_init_spi(spi, regmap_config);
+ if (IS_ERR(cs35l56->base.regmap)) {
+ ret = PTR_ERR(cs35l56->base.regmap);
return dev_err_probe(&spi->dev, ret, "Failed to allocate register map\n");
}
- cs35l56->dev = &spi->dev;
+ cs35l56->base.dev = &spi->dev;
ret = cs35l56_common_probe(cs35l56);
if (ret != 0)
@@ -39,7 +39,7 @@ static int cs35l56_spi_probe(struct spi_device *spi)
ret = cs35l56_init(cs35l56);
if (ret == 0)
- ret = cs35l56_irq_request(cs35l56, spi->irq);
+ ret = cs35l56_irq_request(&cs35l56->base, spi->irq);
if (ret < 0)
cs35l56_remove(cs35l56);
diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c
index c03f9d3c9a13..19b6b4fbe5de 100644
--- a/sound/soc/codecs/cs35l56.c
+++ b/sound/soc/codecs/cs35l56.c
@@ -34,23 +34,6 @@
static int cs35l56_dsp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event);
-static int cs35l56_mbox_send(struct cs35l56_private *cs35l56, unsigned int command)
-{
- unsigned int val;
- int ret;
-
- regmap_write(cs35l56->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1, command);
- ret = regmap_read_poll_timeout(cs35l56->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1,
- val, (val == 0),
- CS35L56_MBOX_POLL_US, CS35L56_MBOX_TIMEOUT_US);
- if (ret) {
- dev_warn(cs35l56->dev, "MBOX command %#x failed: %d\n", command, ret);
- return ret;
- }
-
- return 0;
-}
-
static void cs35l56_wait_dsp_ready(struct cs35l56_private *cs35l56)
{
/* Wait for patching to complete */
@@ -174,25 +157,25 @@ static int cs35l56_play_event(struct snd_soc_dapm_widget *w,
unsigned int val;
int ret;
- dev_dbg(cs35l56->dev, "play: %d\n", event);
+ dev_dbg(cs35l56->base.dev, "play: %d\n", event);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
/* Don't wait for ACK, we check in POST_PMU that it completed */
- return regmap_write(cs35l56->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1,
+ return regmap_write(cs35l56->base.regmap, CS35L56_DSP_VIRTUAL1_MBOX_1,
CS35L56_MBOX_CMD_AUDIO_PLAY);
case SND_SOC_DAPM_POST_PMU:
/* Wait for firmware to enter PS0 power state */
- ret = regmap_read_poll_timeout(cs35l56->regmap,
+ ret = regmap_read_poll_timeout(cs35l56->base.regmap,
CS35L56_TRANSDUCER_ACTUAL_PS,
val, (val == CS35L56_PS0),
CS35L56_PS0_POLL_US,
CS35L56_PS0_TIMEOUT_US);
if (ret)
- dev_err(cs35l56->dev, "PS0 wait failed: %d\n", ret);
+ dev_err(cs35l56->base.dev, "PS0 wait failed: %d\n", ret);
return ret;
case SND_SOC_DAPM_POST_PMD:
- return cs35l56_mbox_send(cs35l56, CS35L56_MBOX_CMD_AUDIO_PAUSE);
+ return cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_PAUSE);
default:
return 0;
}
@@ -310,109 +293,23 @@ static int cs35l56_dsp_event(struct snd_soc_dapm_widget *w,
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
- dev_dbg(cs35l56->dev, "%s: %d\n", __func__, event);
+ dev_dbg(cs35l56->base.dev, "%s: %d\n", __func__, event);
return wm_adsp_event(w, kcontrol, event);
}
-irqreturn_t cs35l56_irq(int irq, void *data)
-{
- struct cs35l56_private *cs35l56 = data;
- unsigned int status1 = 0, status8 = 0, status20 = 0;
- unsigned int mask1, mask8, mask20;
- unsigned int val;
- int rv;
-
- irqreturn_t ret = IRQ_NONE;
-
- if (!cs35l56->init_done)
- return IRQ_NONE;
-
- mutex_lock(&cs35l56->irq_lock);
-
- rv = pm_runtime_resume_and_get(cs35l56->dev);
- if (rv < 0) {
- dev_err(cs35l56->dev, "irq: failed to get pm_runtime: %d\n", rv);
- goto err_unlock;
- }
-
- regmap_read(cs35l56->regmap, CS35L56_IRQ1_STATUS, &val);
- if ((val & CS35L56_IRQ1_STS_MASK) == 0) {
- dev_dbg(cs35l56->dev, "Spurious IRQ: no pending interrupt\n");
- goto err;
- }
-
- /* Ack interrupts */
- regmap_read(cs35l56->regmap, CS35L56_IRQ1_EINT_1, &status1);
- regmap_read(cs35l56->regmap, CS35L56_IRQ1_MASK_1, &mask1);
- status1 &= ~mask1;
- regmap_write(cs35l56->regmap, CS35L56_IRQ1_EINT_1, status1);
-
- regmap_read(cs35l56->regmap, CS35L56_IRQ1_EINT_8, &status8);
- regmap_read(cs35l56->regmap, CS35L56_IRQ1_MASK_8, &mask8);
- status8 &= ~mask8;
- regmap_write(cs35l56->regmap, CS35L56_IRQ1_EINT_8, status8);
-
- regmap_read(cs35l56->regmap, CS35L56_IRQ1_EINT_20, &status20);
- regmap_read(cs35l56->regmap, CS35L56_IRQ1_MASK_20, &mask20);
- status20 &= ~mask20;
- /* We don't want EINT20 but they default to unmasked: force mask */
- regmap_write(cs35l56->regmap, CS35L56_IRQ1_MASK_20, 0xffffffff);
-
- dev_dbg(cs35l56->dev, "%s: %#x %#x\n", __func__, status1, status8);
-
- /* Check to see if unmasked bits are active */
- if (!status1 && !status8 && !status20)
- goto err;
-
- if (status1 & CS35L56_AMP_SHORT_ERR_EINT1_MASK)
- dev_crit(cs35l56->dev, "Amp short error\n");
-
- if (status8 & CS35L56_TEMP_ERR_EINT1_MASK)
- dev_crit(cs35l56->dev, "Overtemp error\n");
-
- ret = IRQ_HANDLED;
-
-err:
- pm_runtime_put(cs35l56->dev);
-err_unlock:
- mutex_unlock(&cs35l56->irq_lock);
-
- return ret;
-}
-EXPORT_SYMBOL_NS_GPL(cs35l56_irq, SND_SOC_CS35L56_CORE);
-
-int cs35l56_irq_request(struct cs35l56_private *cs35l56, int irq)
-{
- int ret;
-
- if (!irq)
- return 0;
-
- ret = devm_request_threaded_irq(cs35l56->dev, irq, NULL, cs35l56_irq,
- IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW,
- "cs35l56", cs35l56);
- if (!ret)
- cs35l56->irq = irq;
- else
- dev_err(cs35l56->dev, "Failed to get IRQ: %d\n", ret);
-
- return ret;
-}
-EXPORT_SYMBOL_NS_GPL(cs35l56_irq_request, SND_SOC_CS35L56_CORE);
-
static int cs35l56_asp_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(codec_dai->component);
unsigned int val;
- dev_dbg(cs35l56->dev, "%s: %#x\n", __func__, fmt);
+ dev_dbg(cs35l56->base.dev, "%s: %#x\n", __func__, fmt);
switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
- dev_err(cs35l56->dev, "Unsupported clock source mode\n");
+ dev_err(cs35l56->base.dev, "Unsupported clock source mode\n");
return -EINVAL;
}
@@ -426,7 +323,7 @@ static int cs35l56_asp_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int f
cs35l56->tdm_mode = false;
break;
default:
- dev_err(cs35l56->dev, "Unsupported DAI format\n");
+ dev_err(cs35l56->base.dev, "Unsupported DAI format\n");
return -EINVAL;
}
@@ -443,18 +340,18 @@ static int cs35l56_asp_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int f
case SND_SOC_DAIFMT_NB_NF:
break;
default:
- dev_err(cs35l56->dev, "Invalid clock invert\n");
+ dev_err(cs35l56->base.dev, "Invalid clock invert\n");
return -EINVAL;
}
- regmap_update_bits(cs35l56->regmap,
+ regmap_update_bits(cs35l56->base.regmap,
CS35L56_ASP1_CONTROL2,
CS35L56_ASP_FMT_MASK |
CS35L56_ASP_BCLK_INV_MASK | CS35L56_ASP_FSYNC_INV_MASK,
val);
/* Hi-Z DOUT in unused slots and when all TX are disabled */
- regmap_update_bits(cs35l56->regmap, CS35L56_ASP1_CONTROL3,
+ regmap_update_bits(cs35l56->base.regmap, CS35L56_ASP1_CONTROL3,
CS35L56_ASP1_DOUT_HIZ_CTRL_MASK,
CS35L56_ASP_UNUSED_HIZ_OFF_HIZ);
@@ -485,7 +382,7 @@ static void cs35l56_set_asp_slot_positions(struct cs35l56_private *cs35l56,
channel_shift += 8;
}
- regmap_write(cs35l56->regmap, reg, reg_val);
+ regmap_write(cs35l56->base.regmap, reg, reg_val);
}
static int cs35l56_asp_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
@@ -494,20 +391,20 @@ static int cs35l56_asp_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx
struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component);
if ((slots == 0) || (slot_width == 0)) {
- dev_dbg(cs35l56->dev, "tdm config cleared\n");
+ dev_dbg(cs35l56->base.dev, "tdm config cleared\n");
cs35l56->asp_slot_width = 0;
cs35l56->asp_slot_count = 0;
return 0;
}
if (slot_width > (CS35L56_ASP_RX_WIDTH_MASK >> CS35L56_ASP_RX_WIDTH_SHIFT)) {
- dev_err(cs35l56->dev, "tdm invalid slot width %d\n", slot_width);
+ dev_err(cs35l56->base.dev, "tdm invalid slot width %d\n", slot_width);
return -EINVAL;
}
/* More than 32 slots would give an unsupportable BCLK frequency */
if (slots > 32) {
- dev_err(cs35l56->dev, "tdm invalid slot count %d\n", slots);
+ dev_err(cs35l56->base.dev, "tdm invalid slot count %d\n", slots);
return -EINVAL;
}
@@ -524,7 +421,7 @@ static int cs35l56_asp_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx
cs35l56_set_asp_slot_positions(cs35l56, CS35L56_ASP1_FRAME_CONTROL1, rx_mask);
cs35l56_set_asp_slot_positions(cs35l56, CS35L56_ASP1_FRAME_CONTROL5, tx_mask);
- dev_dbg(cs35l56->dev, "tdm slot width: %u count: %u tx_mask: %#x rx_mask: %#x\n",
+ dev_dbg(cs35l56->base.dev, "tdm slot width: %u count: %u tx_mask: %#x rx_mask: %#x\n",
cs35l56->asp_slot_width, cs35l56->asp_slot_count, tx_mask, rx_mask);
return 0;
@@ -544,7 +441,8 @@ static int cs35l56_asp_dai_hw_params(struct snd_pcm_substream *substream,
else
asp_width = asp_wl;
- dev_dbg(cs35l56->dev, "%s: wl=%d, width=%d, rate=%d", __func__, asp_wl, asp_width, rate);
+ dev_dbg(cs35l56->base.dev, "%s: wl=%d, width=%d, rate=%d",
+ __func__, asp_wl, asp_width, rate);
if (!cs35l56->sysclk_set) {
unsigned int slots = cs35l56->asp_slot_count;
@@ -562,26 +460,26 @@ static int cs35l56_asp_dai_hw_params(struct snd_pcm_substream *substream,
bclk_freq = asp_width * slots * rate;
freq_id = cs35l56_get_bclk_freq_id(bclk_freq);
if (freq_id < 0) {
- dev_err(cs35l56->dev, "%s: Invalid BCLK %u\n", __func__, bclk_freq);
+ dev_err(cs35l56->base.dev, "%s: Invalid BCLK %u\n", __func__, bclk_freq);
return -EINVAL;
}
- regmap_update_bits(cs35l56->regmap, CS35L56_ASP1_CONTROL1,
+ regmap_update_bits(cs35l56->base.regmap, CS35L56_ASP1_CONTROL1,
CS35L56_ASP_BCLK_FREQ_MASK,
freq_id << CS35L56_ASP_BCLK_FREQ_SHIFT);
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- regmap_update_bits(cs35l56->regmap, CS35L56_ASP1_CONTROL2,
+ regmap_update_bits(cs35l56->base.regmap, CS35L56_ASP1_CONTROL2,
CS35L56_ASP_RX_WIDTH_MASK, asp_width <<
CS35L56_ASP_RX_WIDTH_SHIFT);
- regmap_update_bits(cs35l56->regmap, CS35L56_ASP1_DATA_CONTROL5,
+ regmap_update_bits(cs35l56->base.regmap, CS35L56_ASP1_DATA_CONTROL5,
CS35L56_ASP_RX_WL_MASK, asp_wl);
} else {
- regmap_update_bits(cs35l56->regmap, CS35L56_ASP1_CONTROL2,
+ regmap_update_bits(cs35l56->base.regmap, CS35L56_ASP1_CONTROL2,
CS35L56_ASP_TX_WIDTH_MASK, asp_width <<
CS35L56_ASP_TX_WIDTH_SHIFT);
- regmap_update_bits(cs35l56->regmap, CS35L56_ASP1_DATA_CONTROL1,
+ regmap_update_bits(cs35l56->base.regmap, CS35L56_ASP1_DATA_CONTROL1,
CS35L56_ASP_TX_WL_MASK, asp_wl);
}
@@ -603,7 +501,7 @@ static int cs35l56_asp_dai_set_sysclk(struct snd_soc_dai *dai,
if (freq_id < 0)
return freq_id;
- regmap_update_bits(cs35l56->regmap, CS35L56_ASP1_CONTROL1,
+ regmap_update_bits(cs35l56->base.regmap, CS35L56_ASP1_CONTROL1,
CS35L56_ASP_BCLK_FREQ_MASK,
freq_id << CS35L56_ASP_BCLK_FREQ_SHIFT);
cs35l56->sysclk_set = true;
@@ -646,9 +544,9 @@ static int cs35l56_sdw_dai_hw_params(struct snd_pcm_substream *substream,
struct sdw_port_config pconfig;
int ret;
- dev_dbg(cs35l56->dev, "%s: rate %d\n", __func__, params_rate(params));
+ dev_dbg(cs35l56->base.dev, "%s: rate %d\n", __func__, params_rate(params));
- if (!cs35l56->init_done)
+ if (!cs35l56->base.init_done)
return -ENODEV;
if (!sdw_stream)
@@ -761,67 +659,6 @@ static struct snd_soc_dai_driver cs35l56_dai[] = {
}
};
-static int cs35l56_wait_for_firmware_boot(struct cs35l56_private *cs35l56)
-{
- unsigned int reg;
- unsigned int val;
- int ret;
-
- if (cs35l56->rev < CS35L56_REVID_B0)
- reg = CS35L56_DSP1_HALO_STATE_A1;
- else
- reg = CS35L56_DSP1_HALO_STATE;
-
- ret = regmap_read_poll_timeout(cs35l56->regmap, reg,
- val,
- (val < 0xFFFF) && (val >= CS35L56_HALO_STATE_BOOT_DONE),
- CS35L56_HALO_STATE_POLL_US,
- CS35L56_HALO_STATE_TIMEOUT_US);
-
- if ((ret < 0) && (ret != -ETIMEDOUT)) {
- dev_err(cs35l56->dev, "Failed to read HALO_STATE: %d\n", ret);
- return ret;
- }
-
- if ((ret == -ETIMEDOUT) || (val != CS35L56_HALO_STATE_BOOT_DONE)) {
- dev_err(cs35l56->dev, "Firmware boot fail: HALO_STATE=%#x\n", val);
- return -EIO;
- }
-
- return 0;
-}
-
-static inline void cs35l56_wait_min_reset_pulse(void)
-{
- /* Satisfy minimum reset pulse width spec */
- usleep_range(CS35L56_RESET_PULSE_MIN_US, 2 * CS35L56_RESET_PULSE_MIN_US);
-}
-
-static const struct reg_sequence cs35l56_system_reset_seq[] = {
- REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_SYSTEM_RESET),
-};
-
-static void cs35l56_system_reset(struct cs35l56_private *cs35l56)
-{
- cs35l56->soft_resetting = true;
-
- /*
- * Must enter cache-only first so there can't be any more register
- * accesses other than the controlled system reset sequence below.
- */
- regcache_cache_only(cs35l56->regmap, true);
- regmap_multi_reg_write_bypassed(cs35l56->regmap,
- cs35l56_system_reset_seq,
- ARRAY_SIZE(cs35l56_system_reset_seq));
-
- /* On SoundWire the registers won't be accessible until it re-enumerates. */
- if (cs35l56->sdw_peripheral)
- return;
-
- usleep_range(CS35L56_CONTROL_PORT_READY_US, CS35L56_CONTROL_PORT_READY_US + 400);
- regcache_cache_only(cs35l56->regmap, false);
-}
-
static void cs35l56_secure_patch(struct cs35l56_private *cs35l56)
{
int ret;
@@ -829,15 +666,13 @@ static void cs35l56_secure_patch(struct cs35l56_private *cs35l56)
/* Use wm_adsp to load and apply the firmware patch and coefficient files */
ret = wm_adsp_power_up(&cs35l56->dsp);
if (ret)
- dev_dbg(cs35l56->dev, "%s: wm_adsp_power_up ret %d\n", __func__, ret);
+ dev_dbg(cs35l56->base.dev, "%s: wm_adsp_power_up ret %d\n", __func__, ret);
else
- cs35l56_mbox_send(cs35l56, CS35L56_MBOX_CMD_AUDIO_REINIT);
+ cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT);
}
static void cs35l56_patch(struct cs35l56_private *cs35l56)
{
- unsigned int reg;
- unsigned int val;
int ret;
/*
@@ -854,35 +689,23 @@ static void cs35l56_patch(struct cs35l56_private *cs35l56)
flush_work(&cs35l56->sdw_irq_work);
}
- ret = cs35l56_mbox_send(cs35l56, CS35L56_MBOX_CMD_SHUTDOWN);
+ ret = cs35l56_firmware_shutdown(&cs35l56->base);
if (ret)
goto err;
- if (cs35l56->rev < CS35L56_REVID_B0)
- reg = CS35L56_DSP1_PM_CUR_STATE_A1;
- else
- reg = CS35L56_DSP1_PM_CUR_STATE;
-
- ret = regmap_read_poll_timeout(cs35l56->regmap, reg,
- val, (val == CS35L56_HALO_STATE_SHUTDOWN),
- CS35L56_HALO_STATE_POLL_US,
- CS35L56_HALO_STATE_TIMEOUT_US);
- if (ret < 0)
- dev_err(cs35l56->dev, "Failed to poll PM_CUR_STATE to 1 is %d (ret %d)\n",
- val, ret);
-
/* Use wm_adsp to load and apply the firmware patch and coefficient files */
ret = wm_adsp_power_up(&cs35l56->dsp);
if (ret) {
- dev_dbg(cs35l56->dev, "%s: wm_adsp_power_up ret %d\n", __func__, ret);
+ dev_dbg(cs35l56->base.dev, "%s: wm_adsp_power_up ret %d\n", __func__, ret);
goto err;
}
- mutex_lock(&cs35l56->irq_lock);
+ mutex_lock(&cs35l56->base.irq_lock);
init_completion(&cs35l56->init_completion);
- cs35l56_system_reset(cs35l56);
+ cs35l56->soft_resetting = true;
+ cs35l56_system_reset(&cs35l56->base, !!cs35l56->sdw_peripheral);
if (cs35l56->sdw_peripheral) {
/*
@@ -892,18 +715,20 @@ static void cs35l56_patch(struct cs35l56_private *cs35l56)
*/
if (!wait_for_completion_timeout(&cs35l56->init_completion,
msecs_to_jiffies(5000))) {
- dev_err(cs35l56->dev, "%s: init_completion timed out (SDW)\n", __func__);
+ dev_err(cs35l56->base.dev, "%s: init_completion timed out (SDW)\n",
+ __func__);
goto err_unlock;
}
} else if (cs35l56_init(cs35l56)) {
goto err_unlock;
}
- regmap_clear_bits(cs35l56->regmap, CS35L56_PROTECTION_STATUS, CS35L56_FIRMWARE_MISSING);
- cs35l56->fw_patched = true;
+ regmap_clear_bits(cs35l56->base.regmap, CS35L56_PROTECTION_STATUS,
+ CS35L56_FIRMWARE_MISSING);
+ cs35l56->base.fw_patched = true;
err_unlock:
- mutex_unlock(&cs35l56->irq_lock);
+ mutex_unlock(&cs35l56->base.irq_lock);
err:
/* Re-enable SoundWire interrupts */
if (cs35l56->sdw_peripheral) {
@@ -919,10 +744,10 @@ static void cs35l56_dsp_work(struct work_struct *work)
struct cs35l56_private,
dsp_work);
- if (!cs35l56->init_done)
+ if (!cs35l56->base.init_done)
return;
- pm_runtime_get_sync(cs35l56->dev);
+ pm_runtime_get_sync(cs35l56->base.dev);
/*
* When the device is running in secure mode the firmware files can
@@ -930,13 +755,13 @@ static void cs35l56_dsp_work(struct work_struct *work)
* shutdown the firmware to apply them and can use the lower cost
* reinit sequence instead.
*/
- if (cs35l56->secured)
+ if (cs35l56->base.secured)
cs35l56_secure_patch(cs35l56);
else
cs35l56_patch(cs35l56);
- pm_runtime_mark_last_busy(cs35l56->dev);
- pm_runtime_put_autosuspend(cs35l56->dev);
+ pm_runtime_mark_last_busy(cs35l56->base.dev);
+ pm_runtime_put_autosuspend(cs35l56->base.dev);
}
static int cs35l56_component_probe(struct snd_soc_component *component)
@@ -948,16 +773,16 @@ static int cs35l56_component_probe(struct snd_soc_component *component)
if (!wait_for_completion_timeout(&cs35l56->init_completion,
msecs_to_jiffies(5000))) {
- dev_err(cs35l56->dev, "%s: init_completion timed out\n", __func__);
+ dev_err(cs35l56->base.dev, "%s: init_completion timed out\n", __func__);
return -ENODEV;
}
cs35l56->component = component;
wm_adsp2_component_probe(&cs35l56->dsp, component);
- debugfs_create_bool("init_done", 0444, debugfs_root, &cs35l56->init_done);
- debugfs_create_bool("can_hibernate", 0444, debugfs_root, &cs35l56->can_hibernate);
- debugfs_create_bool("fw_patched", 0444, debugfs_root, &cs35l56->fw_patched);
+ debugfs_create_bool("init_done", 0444, debugfs_root, &cs35l56->base.init_done);
+ debugfs_create_bool("can_hibernate", 0444, debugfs_root, &cs35l56->base.can_hibernate);
+ debugfs_create_bool("fw_patched", 0444, debugfs_root, &cs35l56->base.fw_patched);
queue_work(cs35l56->dsp_wq, &cs35l56->dsp_work);
@@ -1009,171 +834,18 @@ static const struct snd_soc_component_driver soc_component_dev_cs35l56 = {
.suspend_bias_off = 1, /* see cs35l56_system_resume() */
};
-static const struct reg_sequence cs35l56_hibernate_seq[] = {
- /* This must be the last register access */
- REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_HIBERNATE_NOW),
-};
-
-static const struct reg_sequence cs35l56_hibernate_wake_seq[] = {
- REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_WAKEUP),
-};
-
-int cs35l56_runtime_suspend(struct device *dev)
+static int __maybe_unused cs35l56_runtime_suspend_i2c_spi(struct device *dev)
{
struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
- unsigned int val;
- int ret;
-
- if (!cs35l56->init_done)
- return 0;
- /* Firmware must have entered a power-save state */
- ret = regmap_read_poll_timeout(cs35l56->regmap,
- CS35L56_TRANSDUCER_ACTUAL_PS,
- val, (val >= CS35L56_PS3),
- CS35L56_PS3_POLL_US,
- CS35L56_PS3_TIMEOUT_US);
- if (ret)
- dev_warn(cs35l56->dev, "PS3 wait failed: %d\n", ret);
-
- /* Clear BOOT_DONE so it can be used to detect a reboot */
- regmap_write(cs35l56->regmap, CS35L56_IRQ1_EINT_4, CS35L56_OTP_BOOT_DONE_MASK);
-
- if (!cs35l56->can_hibernate) {
- regcache_cache_only(cs35l56->regmap, true);
- dev_dbg(dev, "Suspended: no hibernate");
-
- return 0;
- }
-
- /*
- * Enable auto-hibernate. If it is woken by some other wake source
- * it will automatically return to hibernate.
- */
- cs35l56_mbox_send(cs35l56, CS35L56_MBOX_CMD_ALLOW_AUTO_HIBERNATE);
-
- /*
- * Must enter cache-only first so there can't be any more register
- * accesses other than the controlled hibernate sequence below.
- */
- regcache_cache_only(cs35l56->regmap, true);
-
- regmap_multi_reg_write_bypassed(cs35l56->regmap,
- cs35l56_hibernate_seq,
- ARRAY_SIZE(cs35l56_hibernate_seq));
-
- dev_dbg(dev, "Suspended: hibernate");
-
- return 0;
+ return cs35l56_runtime_suspend_common(&cs35l56->base);
}
-EXPORT_SYMBOL_NS_GPL(cs35l56_runtime_suspend, SND_SOC_CS35L56_CORE);
static int __maybe_unused cs35l56_runtime_resume_i2c_spi(struct device *dev)
{
struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
- if (!cs35l56->init_done)
- return 0;
-
- return cs35l56_runtime_resume_common(cs35l56);
-}
-
-int cs35l56_runtime_resume_common(struct cs35l56_private *cs35l56)
-{
- unsigned int val;
- int ret;
-
- if (!cs35l56->can_hibernate)
- goto out_sync;
-
- if (!cs35l56->sdw_peripheral) {
- /*
- * Dummy transaction to trigger I2C/SPI auto-wake. This will NAK on I2C.
- * Must be done before releasing cache-only.
- */
- regmap_multi_reg_write_bypassed(cs35l56->regmap,
- cs35l56_hibernate_wake_seq,
- ARRAY_SIZE(cs35l56_hibernate_wake_seq));
-
- usleep_range(CS35L56_CONTROL_PORT_READY_US,
- CS35L56_CONTROL_PORT_READY_US + 400);
- }
-
-out_sync:
- regcache_cache_only(cs35l56->regmap, false);
-
- ret = cs35l56_wait_for_firmware_boot(cs35l56);
- if (ret) {
- dev_err(cs35l56->dev, "Hibernate wake failed: %d\n", ret);
- goto err;
- }
-
- ret = cs35l56_mbox_send(cs35l56, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE);
- if (ret)
- goto err;
-
- /* BOOT_DONE will be 1 if the amp reset */
- regmap_read(cs35l56->regmap, CS35L56_IRQ1_EINT_4, &val);
- if (val & CS35L56_OTP_BOOT_DONE_MASK) {
- dev_dbg(cs35l56->dev, "Registers reset in suspend\n");
- regcache_mark_dirty(cs35l56->regmap);
- }
-
- regcache_sync(cs35l56->regmap);
-
- dev_dbg(cs35l56->dev, "Resumed");
-
- return 0;
-
-err:
- regmap_write(cs35l56->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1,
- CS35L56_MBOX_CMD_HIBERNATE_NOW);
-
- regcache_cache_only(cs35l56->regmap, true);
-
- return ret;
-}
-EXPORT_SYMBOL_NS_GPL(cs35l56_runtime_resume_common, SND_SOC_CS35L56_CORE);
-
-static int cs35l56_is_fw_reload_needed(struct cs35l56_private *cs35l56)
-{
- unsigned int val;
- int ret;
-
- /* Nothing to re-patch if we haven't done any patching yet. */
- if (!cs35l56->fw_patched)
- return false;
-
- /*
- * If we have control of RESET we will have asserted it so the firmware
- * will need re-patching.
- */
- if (cs35l56->reset_gpio)
- return true;
-
- /*
- * In secure mode FIRMWARE_MISSING is cleared by the BIOS loader so
- * can't be used here to test for memory retention.
- * Assume that tuning must be re-loaded.
- */
- if (cs35l56->secured)
- return true;
-
- ret = pm_runtime_resume_and_get(cs35l56->dev);
- if (ret) {
- dev_err(cs35l56->dev, "Failed to runtime_get: %d\n", ret);
- return ret;
- }
-
- ret = regmap_read(cs35l56->regmap, CS35L56_PROTECTION_STATUS, &val);
- if (ret)
- dev_err(cs35l56->dev, "Failed to read PROTECTION_STATUS: %d\n", ret);
- else
- ret = !!(val & CS35L56_FIRMWARE_MISSING);
-
- pm_runtime_put_autosuspend(cs35l56->dev);
-
- return ret;
+ return cs35l56_runtime_resume_common(&cs35l56->base, false);
}
int cs35l56_system_suspend(struct device *dev)
@@ -1191,8 +863,8 @@ int cs35l56_system_suspend(struct device *dev)
* clear it. Prevent this race by temporarily disabling the parent irq
* until we reach _no_irq.
*/
- if (cs35l56->irq)
- disable_irq(cs35l56->irq);
+ if (cs35l56->base.irq)
+ disable_irq(cs35l56->base.irq);
return pm_runtime_force_suspend(dev);
}
@@ -1209,8 +881,8 @@ int cs35l56_system_suspend_late(struct device *dev)
* RESET is usually shared by all amps so it must not be asserted until
* all driver instances have done their suspend() stage.
*/
- if (cs35l56->reset_gpio) {
- gpiod_set_value_cansleep(cs35l56->reset_gpio, 0);
+ if (cs35l56->base.reset_gpio) {
+ gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0);
cs35l56_wait_min_reset_pulse();
}
@@ -1227,8 +899,8 @@ int cs35l56_system_suspend_no_irq(struct device *dev)
dev_dbg(dev, "system_suspend_no_irq\n");
/* Handlers are now disabled so the parent IRQ can safely be re-enabled. */
- if (cs35l56->irq)
- enable_irq(cs35l56->irq);
+ if (cs35l56->base.irq)
+ enable_irq(cs35l56->base.irq);
return 0;
}
@@ -1247,8 +919,8 @@ int cs35l56_system_resume_no_irq(struct device *dev)
* clear it, until it has fully resumed. Prevent this race by temporarily
* disabling the parent irq until we complete resume().
*/
- if (cs35l56->irq)
- disable_irq(cs35l56->irq);
+ if (cs35l56->base.irq)
+ disable_irq(cs35l56->base.irq);
return 0;
}
@@ -1262,8 +934,8 @@ int cs35l56_system_resume_early(struct device *dev)
dev_dbg(dev, "system_resume_early\n");
/* Ensure a spec-compliant RESET pulse. */
- if (cs35l56->reset_gpio) {
- gpiod_set_value_cansleep(cs35l56->reset_gpio, 0);
+ if (cs35l56->base.reset_gpio) {
+ gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0);
cs35l56_wait_min_reset_pulse();
}
@@ -1275,7 +947,7 @@ int cs35l56_system_resume_early(struct device *dev)
}
/* Release shared RESET before drivers start resume(). */
- gpiod_set_value_cansleep(cs35l56->reset_gpio, 1);
+ gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 1);
return 0;
}
@@ -1290,8 +962,8 @@ int cs35l56_system_resume(struct device *dev)
/* Undo pm_runtime_force_suspend() before re-enabling the irq */
ret = pm_runtime_force_resume(dev);
- if (cs35l56->irq)
- enable_irq(cs35l56->irq);
+ if (cs35l56->base.irq)
+ enable_irq(cs35l56->base.irq);
if (ret)
return ret;
@@ -1300,12 +972,12 @@ int cs35l56_system_resume(struct device *dev)
if (!cs35l56->component)
return 0;
- ret = cs35l56_is_fw_reload_needed(cs35l56);
- dev_dbg(cs35l56->dev, "fw_reload_needed: %d\n", ret);
+ ret = cs35l56_is_fw_reload_needed(&cs35l56->base);
+ dev_dbg(cs35l56->base.dev, "fw_reload_needed: %d\n", ret);
if (ret < 1)
return ret;
- cs35l56->fw_patched = false;
+ cs35l56->base.fw_patched = false;
queue_work(cs35l56->dsp_wq, &cs35l56->dsp_work);
/*
@@ -1329,25 +1001,16 @@ static int cs35l56_dsp_init(struct cs35l56_private *cs35l56)
INIT_WORK(&cs35l56->dsp_work, cs35l56_dsp_work);
dsp = &cs35l56->dsp;
+ cs35l56_init_cs_dsp(&cs35l56->base, &dsp->cs_dsp);
dsp->part = "cs35l56";
- dsp->cs_dsp.num = 1;
- dsp->cs_dsp.type = WMFW_HALO;
- dsp->cs_dsp.rev = 0;
dsp->fw = 12;
- dsp->cs_dsp.dev = cs35l56->dev;
- dsp->cs_dsp.regmap = cs35l56->regmap;
- dsp->cs_dsp.base = CS35L56_DSP1_CORE_BASE;
- dsp->cs_dsp.base_sysinfo = CS35L56_DSP1_SYS_INFO_ID;
- dsp->cs_dsp.mem = cs35l56_dsp1_regions;
- dsp->cs_dsp.num_mems = ARRAY_SIZE(cs35l56_dsp1_regions);
- dsp->cs_dsp.no_core_startstop = true;
dsp->wmfw_optional = true;
- dev_dbg(cs35l56->dev, "DSP system name: '%s'\n", dsp->system_name);
+ dev_dbg(cs35l56->base.dev, "DSP system name: '%s'\n", dsp->system_name);
ret = wm_halo_init(dsp);
if (ret != 0) {
- dev_err(cs35l56->dev, "wm_halo_init failed\n");
+ dev_err(cs35l56->base.dev, "wm_halo_init failed\n");
return ret;
}
@@ -1356,7 +1019,7 @@ static int cs35l56_dsp_init(struct cs35l56_private *cs35l56)
static int cs35l56_acpi_get_name(struct cs35l56_private *cs35l56)
{
- acpi_handle handle = ACPI_HANDLE(cs35l56->dev);
+ acpi_handle handle = ACPI_HANDLE(cs35l56->base.dev);
const char *sub;
/* If there is no ACPI_HANDLE, there is no ACPI for this system, return 0 */
@@ -1373,7 +1036,7 @@ static int cs35l56_acpi_get_name(struct cs35l56_private *cs35l56)
}
cs35l56->dsp.system_name = sub;
- dev_dbg(cs35l56->dev, "Subsystem ID: %s\n", cs35l56->dsp.system_name);
+ dev_dbg(cs35l56->base.dev, "Subsystem ID: %s\n", cs35l56->dsp.system_name);
return 0;
}
@@ -1383,38 +1046,39 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56)
int ret;
init_completion(&cs35l56->init_completion);
- mutex_init(&cs35l56->irq_lock);
+ mutex_init(&cs35l56->base.irq_lock);
- dev_set_drvdata(cs35l56->dev, cs35l56);
+ dev_set_drvdata(cs35l56->base.dev, cs35l56);
cs35l56_fill_supply_names(cs35l56->supplies);
- ret = devm_regulator_bulk_get(cs35l56->dev, ARRAY_SIZE(cs35l56->supplies),
+ ret = devm_regulator_bulk_get(cs35l56->base.dev, ARRAY_SIZE(cs35l56->supplies),
cs35l56->supplies);
if (ret != 0)
- return dev_err_probe(cs35l56->dev, ret, "Failed to request supplies\n");
+ return dev_err_probe(cs35l56->base.dev, ret, "Failed to request supplies\n");
/* Reset could be controlled by the BIOS or shared by multiple amps */
- cs35l56->reset_gpio = devm_gpiod_get_optional(cs35l56->dev, "reset", GPIOD_OUT_LOW);
- if (IS_ERR(cs35l56->reset_gpio)) {
- ret = PTR_ERR(cs35l56->reset_gpio);
+ cs35l56->base.reset_gpio = devm_gpiod_get_optional(cs35l56->base.dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(cs35l56->base.reset_gpio)) {
+ ret = PTR_ERR(cs35l56->base.reset_gpio);
/*
* If RESET is shared the first amp to probe will grab the reset
* line and reset all the amps
*/
if (ret != -EBUSY)
- return dev_err_probe(cs35l56->dev, ret, "Failed to get reset GPIO\n");
+ return dev_err_probe(cs35l56->base.dev, ret, "Failed to get reset GPIO\n");
- dev_info(cs35l56->dev, "Reset GPIO busy, assume shared reset\n");
- cs35l56->reset_gpio = NULL;
+ dev_info(cs35l56->base.dev, "Reset GPIO busy, assume shared reset\n");
+ cs35l56->base.reset_gpio = NULL;
}
ret = regulator_bulk_enable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies);
if (ret != 0)
- return dev_err_probe(cs35l56->dev, ret, "Failed to enable supplies\n");
+ return dev_err_probe(cs35l56->base.dev, ret, "Failed to enable supplies\n");
- if (cs35l56->reset_gpio) {
+ if (cs35l56->base.reset_gpio) {
cs35l56_wait_min_reset_pulse();
- gpiod_set_value_cansleep(cs35l56->reset_gpio, 1);
+ gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 1);
}
ret = cs35l56_acpi_get_name(cs35l56);
@@ -1423,22 +1087,22 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56)
ret = cs35l56_dsp_init(cs35l56);
if (ret < 0) {
- dev_err_probe(cs35l56->dev, ret, "DSP init failed\n");
+ dev_err_probe(cs35l56->base.dev, ret, "DSP init failed\n");
goto err;
}
- ret = devm_snd_soc_register_component(cs35l56->dev,
+ ret = devm_snd_soc_register_component(cs35l56->base.dev,
&soc_component_dev_cs35l56,
cs35l56_dai, ARRAY_SIZE(cs35l56_dai));
if (ret < 0) {
- dev_err_probe(cs35l56->dev, ret, "Register codec failed\n");
+ dev_err_probe(cs35l56->base.dev, ret, "Register codec failed\n");
goto err;
}
return 0;
err:
- gpiod_set_value_cansleep(cs35l56->reset_gpio, 0);
+ gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0);
regulator_bulk_disable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies);
return ret;
@@ -1448,7 +1112,6 @@ EXPORT_SYMBOL_NS_GPL(cs35l56_common_probe, SND_SOC_CS35L56_CORE);
int cs35l56_init(struct cs35l56_private *cs35l56)
{
int ret;
- unsigned int devid, revid, otpid, secured;
/*
* Check whether the actions associated with soft reset or one time
@@ -1457,96 +1120,31 @@ int cs35l56_init(struct cs35l56_private *cs35l56)
if (cs35l56->soft_resetting)
goto post_soft_reset;
- if (cs35l56->init_done)
+ if (cs35l56->base.init_done)
return 0;
- pm_runtime_set_autosuspend_delay(cs35l56->dev, 100);
- pm_runtime_use_autosuspend(cs35l56->dev);
- pm_runtime_set_active(cs35l56->dev);
- pm_runtime_enable(cs35l56->dev);
-
- /*
- * If the system is not using a reset_gpio then issue a
- * dummy read to force a wakeup.
- */
- if (!cs35l56->reset_gpio)
- regmap_read(cs35l56->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1, &devid);
-
- /* Wait for control port to be ready (datasheet tIRS). */
- usleep_range(CS35L56_CONTROL_PORT_READY_US,
- CS35L56_CONTROL_PORT_READY_US + 400);
-
- /*
- * The HALO_STATE register is in different locations on Ax and B0
- * devices so the REVID needs to be determined before waiting for the
- * firmware to boot.
- */
- ret = regmap_read(cs35l56->regmap, CS35L56_REVID, &revid);
- if (ret < 0) {
- dev_err(cs35l56->dev, "Get Revision ID failed\n");
- return ret;
- }
- cs35l56->rev = revid & (CS35L56_AREVID_MASK | CS35L56_MTLREVID_MASK);
-
- ret = cs35l56_wait_for_firmware_boot(cs35l56);
- if (ret)
- return ret;
-
- ret = regmap_read(cs35l56->regmap, CS35L56_DEVID, &devid);
- if (ret < 0) {
- dev_err(cs35l56->dev, "Get Device ID failed\n");
- return ret;
- }
- devid &= CS35L56_DEVID_MASK;
-
- switch (devid) {
- case 0x35A56:
- break;
- default:
- dev_err(cs35l56->dev, "Unknown device %x\n", devid);
- return ret;
- }
-
- ret = regmap_read(cs35l56->regmap, CS35L56_DSP_RESTRICT_STS1, &secured);
- if (ret) {
- dev_err(cs35l56->dev, "Get Secure status failed\n");
- return ret;
- }
-
- /* When any bus is restricted treat the device as secured */
- if (secured & CS35L56_RESTRICTED_MASK)
- cs35l56->secured = true;
+ pm_runtime_set_autosuspend_delay(cs35l56->base.dev, 100);
+ pm_runtime_use_autosuspend(cs35l56->base.dev);
+ pm_runtime_set_active(cs35l56->base.dev);
+ pm_runtime_enable(cs35l56->base.dev);
- ret = regmap_read(cs35l56->regmap, CS35L56_OTPID, &otpid);
- if (ret < 0) {
- dev_err(cs35l56->dev, "Get OTP ID failed\n");
+ ret = cs35l56_hw_init(&cs35l56->base);
+ if (ret < 0)
return ret;
- }
-
- dev_info(cs35l56->dev, "Cirrus Logic CS35L56%s Rev %02X OTP%d\n",
- cs35l56->secured ? "s" : "", cs35l56->rev, otpid);
/* Populate the DSP information with the revision and security state */
- cs35l56->dsp.part = devm_kasprintf(cs35l56->dev, GFP_KERNEL, "cs35l56%s-%02x",
- cs35l56->secured ? "s" : "", cs35l56->rev);
+ cs35l56->dsp.part = devm_kasprintf(cs35l56->base.dev, GFP_KERNEL, "cs35l56%s-%02x",
+ cs35l56->base.secured ? "s" : "", cs35l56->base.rev);
if (!cs35l56->dsp.part)
return -ENOMEM;
- /* Wake source and *_BLOCKED interrupts default to unmasked, so mask them */
- regmap_write(cs35l56->regmap, CS35L56_IRQ1_MASK_20, 0xffffffff);
- regmap_update_bits(cs35l56->regmap, CS35L56_IRQ1_MASK_1,
- CS35L56_AMP_SHORT_ERR_EINT1_MASK,
- 0);
- regmap_update_bits(cs35l56->regmap, CS35L56_IRQ1_MASK_8,
- CS35L56_TEMP_ERR_EINT1_MASK,
- 0);
-
- if (!cs35l56->reset_gpio) {
- dev_dbg(cs35l56->dev, "No reset gpio: using soft reset\n");
- cs35l56_system_reset(cs35l56);
+ if (!cs35l56->base.reset_gpio) {
+ dev_dbg(cs35l56->base.dev, "No reset gpio: using soft reset\n");
+ cs35l56->soft_resetting = true;
+ cs35l56_system_reset(&cs35l56->base, !!cs35l56->sdw_peripheral);
if (cs35l56->sdw_peripheral) {
/* Keep alive while we wait for re-enumeration */
- pm_runtime_get_noresume(cs35l56->dev);
+ pm_runtime_get_noresume(cs35l56->base.dev);
return 0;
}
}
@@ -1556,29 +1154,30 @@ post_soft_reset:
cs35l56->soft_resetting = false;
/* Done re-enumerating after one-time init so release the keep-alive */
- if (cs35l56->sdw_peripheral && !cs35l56->init_done)
- pm_runtime_put_noidle(cs35l56->dev);
+ if (cs35l56->sdw_peripheral && !cs35l56->base.init_done)
+ pm_runtime_put_noidle(cs35l56->base.dev);
- regcache_mark_dirty(cs35l56->regmap);
- ret = cs35l56_wait_for_firmware_boot(cs35l56);
+ regcache_mark_dirty(cs35l56->base.regmap);
+ ret = cs35l56_wait_for_firmware_boot(&cs35l56->base);
if (ret)
return ret;
- dev_dbg(cs35l56->dev, "Firmware rebooted after soft reset\n");
+ dev_dbg(cs35l56->base.dev, "Firmware rebooted after soft reset\n");
}
/* Disable auto-hibernate so that runtime_pm has control */
- ret = cs35l56_mbox_send(cs35l56, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE);
+ ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE);
if (ret)
return ret;
- /* Populate soft registers in the regmap cache */
- cs35l56_reread_firmware_registers(cs35l56->dev, cs35l56->regmap);
+ ret = cs35l56_set_patch(&cs35l56->base);
+ if (ret)
+ return ret;
/* Registers could be dirty after soft reset or SoundWire enumeration */
- regcache_sync(cs35l56->regmap);
+ regcache_sync(cs35l56->base.regmap);
- cs35l56->init_done = true;
+ cs35l56->base.init_done = true;
complete(&cs35l56->init_completion);
return 0;
@@ -1587,32 +1186,32 @@ EXPORT_SYMBOL_NS_GPL(cs35l56_init, SND_SOC_CS35L56_CORE);
void cs35l56_remove(struct cs35l56_private *cs35l56)
{
- cs35l56->init_done = false;
+ cs35l56->base.init_done = false;
/*
* WAKE IRQs unmask if CS35L56 hibernates so free the handler to
* prevent it racing with remove().
*/
- if (cs35l56->irq)
- devm_free_irq(cs35l56->dev, cs35l56->irq, cs35l56);
+ if (cs35l56->base.irq)
+ devm_free_irq(cs35l56->base.dev, cs35l56->base.irq, &cs35l56->base);
flush_workqueue(cs35l56->dsp_wq);
destroy_workqueue(cs35l56->dsp_wq);
- pm_runtime_suspend(cs35l56->dev);
- pm_runtime_disable(cs35l56->dev);
+ pm_runtime_suspend(cs35l56->base.dev);
+ pm_runtime_disable(cs35l56->base.dev);
- regcache_cache_only(cs35l56->regmap, true);
+ regcache_cache_only(cs35l56->base.regmap, true);
kfree(cs35l56->dsp.system_name);
- gpiod_set_value_cansleep(cs35l56->reset_gpio, 0);
+ gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0);
regulator_bulk_disable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies);
}
EXPORT_SYMBOL_NS_GPL(cs35l56_remove, SND_SOC_CS35L56_CORE);
const struct dev_pm_ops cs35l56_pm_ops_i2c_spi = {
- SET_RUNTIME_PM_OPS(cs35l56_runtime_suspend, cs35l56_runtime_resume_i2c_spi, NULL)
+ SET_RUNTIME_PM_OPS(cs35l56_runtime_suspend_i2c_spi, cs35l56_runtime_resume_i2c_spi, NULL)
SYSTEM_SLEEP_PM_OPS(cs35l56_system_suspend, cs35l56_system_resume)
LATE_SYSTEM_SLEEP_PM_OPS(cs35l56_system_suspend_late, cs35l56_system_resume_early)
NOIRQ_SYSTEM_SLEEP_PM_OPS(cs35l56_system_suspend_no_irq, cs35l56_system_resume_no_irq)
diff --git a/sound/soc/codecs/cs35l56.h b/sound/soc/codecs/cs35l56.h
index 1f7894662fcb..8159c3e217d9 100644
--- a/sound/soc/codecs/cs35l56.h
+++ b/sound/soc/codecs/cs35l56.h
@@ -32,26 +32,17 @@ struct sdw_slave;
struct cs35l56_private {
struct wm_adsp dsp; /* must be first member */
+ struct cs35l56_base base;
struct work_struct dsp_work;
struct workqueue_struct *dsp_wq;
- struct mutex irq_lock;
struct snd_soc_component *component;
- struct device *dev;
- struct regmap *regmap;
struct regulator_bulk_data supplies[CS35L56_NUM_BULK_SUPPLIES];
- int irq;
struct sdw_slave *sdw_peripheral;
- u8 rev;
struct work_struct sdw_irq_work;
- bool secured;
bool sdw_irq_no_unmask;
bool soft_resetting;
- bool init_done;
bool sdw_attached;
- bool fw_patched;
- bool can_hibernate;
struct completion init_completion;
- struct gpio_desc *reset_gpio;
u32 rx_mask;
u32 tx_mask;
@@ -64,8 +55,6 @@ struct cs35l56_private {
extern const struct dev_pm_ops cs35l56_pm_ops_i2c_spi;
-int cs35l56_runtime_suspend(struct device *dev);
-int cs35l56_runtime_resume_common(struct cs35l56_private *cs35l56);
int cs35l56_system_suspend(struct device *dev);
int cs35l56_system_suspend_late(struct device *dev);
int cs35l56_system_suspend_no_irq(struct device *dev);
@@ -73,7 +62,7 @@ int cs35l56_system_resume_no_irq(struct device *dev);
int cs35l56_system_resume_early(struct device *dev);
int cs35l56_system_resume(struct device *dev);
irqreturn_t cs35l56_irq(int irq, void *data);
-int cs35l56_irq_request(struct cs35l56_private *cs35l56, int irq);
+int cs35l56_irq_request(struct cs35l56_base *cs35l56_base, int irq);
int cs35l56_common_probe(struct cs35l56_private *cs35l56);
int cs35l56_init(struct cs35l56_private *cs35l56);
void cs35l56_remove(struct cs35l56_private *cs35l56);
diff --git a/sound/soc/codecs/cs4265.c b/sound/soc/codecs/cs4265.c
index 0cfc5ab36a13..1ed1e60d8e53 100644
--- a/sound/soc/codecs/cs4265.c
+++ b/sound/soc/codecs/cs4265.c
@@ -564,7 +564,7 @@ static const struct regmap_config cs4265_regmap = {
.num_reg_defaults = ARRAY_SIZE(cs4265_reg_defaults),
.readable_reg = cs4265_readable_register,
.volatile_reg = cs4265_volatile_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static int cs4265_i2c_probe(struct i2c_client *i2c_client)
diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c
index ab32f15e3b44..3df567214952 100644
--- a/sound/soc/codecs/cs4270.c
+++ b/sound/soc/codecs/cs4270.c
@@ -636,7 +636,7 @@ static const struct regmap_config cs4270_regmap = {
.max_register = CS4270_LASTREG,
.reg_defaults = cs4270_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(cs4270_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.write_flag_mask = CS4270_I2C_INCR,
.readable_reg = cs4270_reg_is_readable,
diff --git a/sound/soc/codecs/cs42l51-i2c.c b/sound/soc/codecs/cs42l51-i2c.c
index b2106ff6a7cb..e7db7bcd0296 100644
--- a/sound/soc/codecs/cs42l51-i2c.c
+++ b/sound/soc/codecs/cs42l51-i2c.c
@@ -19,6 +19,12 @@ static struct i2c_device_id cs42l51_i2c_id[] = {
};
MODULE_DEVICE_TABLE(i2c, cs42l51_i2c_id);
+const struct of_device_id cs42l51_of_match[] = {
+ { .compatible = "cirrus,cs42l51", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, cs42l51_of_match);
+
static int cs42l51_i2c_probe(struct i2c_client *i2c)
{
struct regmap_config config;
diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c
index a67cd3ee84e0..e4827b8c2bde 100644
--- a/sound/soc/codecs/cs42l51.c
+++ b/sound/soc/codecs/cs42l51.c
@@ -703,7 +703,7 @@ const struct regmap_config cs42l51_regmap = {
.volatile_reg = cs42l51_volatile_reg,
.writeable_reg = cs42l51_writeable_reg,
.max_register = CS42L51_CHARGE_FREQ,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
EXPORT_SYMBOL_GPL(cs42l51_regmap);
@@ -823,13 +823,6 @@ int __maybe_unused cs42l51_resume(struct device *dev)
}
EXPORT_SYMBOL_GPL(cs42l51_resume);
-const struct of_device_id cs42l51_of_match[] = {
- { .compatible = "cirrus,cs42l51", },
- { }
-};
-MODULE_DEVICE_TABLE(of, cs42l51_of_match);
-EXPORT_SYMBOL_GPL(cs42l51_of_match);
-
MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
MODULE_DESCRIPTION("Cirrus Logic CS42L51 ALSA SoC Codec Driver");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs42l51.h b/sound/soc/codecs/cs42l51.h
index a79343e8a54e..125703ede113 100644
--- a/sound/soc/codecs/cs42l51.h
+++ b/sound/soc/codecs/cs42l51.h
@@ -16,7 +16,6 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap);
void cs42l51_remove(struct device *dev);
int __maybe_unused cs42l51_suspend(struct device *dev);
int __maybe_unused cs42l51_resume(struct device *dev);
-extern const struct of_device_id cs42l51_of_match[];
#define CS42L51_CHIP_ID 0x1B
#define CS42L51_CHIP_REV_A 0x00
diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c
index 1f1ded0ff0ac..4fc8a6ae8d92 100644
--- a/sound/soc/codecs/cs42l52.c
+++ b/sound/soc/codecs/cs42l52.c
@@ -1084,7 +1084,7 @@ static const struct regmap_config cs42l52_regmap = {
.num_reg_defaults = ARRAY_SIZE(cs42l52_reg_defaults),
.readable_reg = cs42l52_readable_register,
.volatile_reg = cs42l52_volatile_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static int cs42l52_i2c_probe(struct i2c_client *i2c_client)
diff --git a/sound/soc/codecs/cs42l56.c b/sound/soc/codecs/cs42l56.c
index 4c646e8d72aa..1714857594fb 100644
--- a/sound/soc/codecs/cs42l56.c
+++ b/sound/soc/codecs/cs42l56.c
@@ -1125,7 +1125,7 @@ static const struct regmap_config cs42l56_regmap = {
.num_reg_defaults = ARRAY_SIZE(cs42l56_reg_defaults),
.readable_reg = cs42l56_readable_register,
.volatile_reg = cs42l56_volatile_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static int cs42l56_handle_of_data(struct i2c_client *i2c_client,
diff --git a/sound/soc/codecs/cs42xx8.c b/sound/soc/codecs/cs42xx8.c
index 4558ec38a7ac..9c44b6283b8f 100644
--- a/sound/soc/codecs/cs42xx8.c
+++ b/sound/soc/codecs/cs42xx8.c
@@ -458,7 +458,7 @@ const struct regmap_config cs42xx8_regmap_config = {
.num_reg_defaults = ARRAY_SIZE(cs42xx8_reg),
.volatile_reg = cs42xx8_volatile_register,
.writeable_reg = cs42xx8_writeable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
EXPORT_SYMBOL_GPL(cs42xx8_regmap_config);
diff --git a/sound/soc/codecs/cs4349.c b/sound/soc/codecs/cs4349.c
index 8365dd0ebe2a..ef08e51901b5 100644
--- a/sound/soc/codecs/cs4349.c
+++ b/sound/soc/codecs/cs4349.c
@@ -271,7 +271,7 @@ static const struct regmap_config cs4349_regmap = {
.num_reg_defaults = ARRAY_SIZE(cs4349_reg_defaults),
.readable_reg = cs4349_readable_register,
.writeable_reg = cs4349_writeable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static int cs4349_i2c_probe(struct i2c_client *client)
diff --git a/sound/soc/codecs/es8316.c b/sound/soc/codecs/es8316.c
index 34cf60769b62..7e5eb13af428 100644
--- a/sound/soc/codecs/es8316.c
+++ b/sound/soc/codecs/es8316.c
@@ -27,9 +27,9 @@
* MCLK/LRCK ratios, but we also add ratio 400, which is commonly used on
* Intel Cherry Trail platforms (19.2MHz MCLK, 48kHz LRCK).
*/
-#define NR_SUPPORTED_MCLK_LRCK_RATIOS 6
+#define NR_SUPPORTED_MCLK_LRCK_RATIOS ARRAY_SIZE(supported_mclk_lrck_ratios)
static const unsigned int supported_mclk_lrck_ratios[] = {
- 256, 384, 400, 512, 768, 1024
+ 256, 384, 400, 500, 512, 768, 1024
};
struct es8316_priv {
@@ -494,6 +494,7 @@ static int es8316_pcm_hw_params(struct snd_pcm_substream *substream,
bclk_divider /= 20;
break;
case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_S24_3LE:
wordlen = ES8316_SERDATA2_LEN_24;
bclk_divider /= 24;
break;
diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c
index 7cb5b57ae655..a7fbb758eeee 100644
--- a/sound/soc/codecs/es8326.c
+++ b/sound/soc/codecs/es8326.c
@@ -38,6 +38,9 @@ struct es8326_priv {
u8 interrupt_clk;
bool jd_inverted;
unsigned int sysclk;
+
+ bool calibrated;
+ int version;
};
static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(dac_vol_tlv, -9550, 50, 0);
@@ -121,33 +124,12 @@ static const struct snd_soc_dapm_widget es8326_dapm_widgets[] = {
/* Analog Power Supply*/
SND_SOC_DAPM_DAC("Right DAC", NULL, ES8326_ANA_PDN, 0, 1),
SND_SOC_DAPM_DAC("Left DAC", NULL, ES8326_ANA_PDN, 1, 1),
- SND_SOC_DAPM_SUPPLY("Analog Power", ES8326_ANA_PDN, 7, 1, NULL, 0),
- SND_SOC_DAPM_SUPPLY("IBias Power", ES8326_ANA_PDN, 6, 1, NULL, 0),
- SND_SOC_DAPM_SUPPLY("ADC Vref", ES8326_ANA_PDN, 5, 1, NULL, 0),
- SND_SOC_DAPM_SUPPLY("DAC Vref", ES8326_ANA_PDN, 4, 1, NULL, 0),
- SND_SOC_DAPM_SUPPLY("Vref Power", ES8326_ANA_PDN, 3, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY("MICBIAS1", ES8326_ANA_MICBIAS, 2, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("MICBIAS2", ES8326_ANA_MICBIAS, 3, 0, NULL, 0),
SND_SOC_DAPM_PGA("LHPMIX", ES8326_DAC2HPMIX, 7, 0, NULL, 0),
SND_SOC_DAPM_PGA("RHPMIX", ES8326_DAC2HPMIX, 3, 0, NULL, 0),
- /* Headphone Charge Pump and Output */
- SND_SOC_DAPM_SUPPLY("HPOR Cal", ES8326_HP_CAL, 7, 1, NULL, 0),
- SND_SOC_DAPM_SUPPLY("HPOL Cal", ES8326_HP_CAL, 3, 1, NULL, 0),
- SND_SOC_DAPM_SUPPLY("Headphone Charge Pump", ES8326_HP_DRIVER,
- 3, 1, NULL, 0),
- SND_SOC_DAPM_SUPPLY("Headphone Driver Bias", ES8326_HP_DRIVER,
- 2, 1, NULL, 0),
- SND_SOC_DAPM_SUPPLY("Headphone LDO", ES8326_HP_DRIVER,
- 1, 1, NULL, 0),
- SND_SOC_DAPM_SUPPLY("Headphone Reference", ES8326_HP_DRIVER,
- 0, 1, NULL, 0),
- SND_SOC_DAPM_REG(snd_soc_dapm_supply, "HPOR Supply", ES8326_HP_CAL,
- ES8326_HPOR_SHIFT, 7, 7, 0),
- SND_SOC_DAPM_REG(snd_soc_dapm_supply, "HPOL Supply", ES8326_HP_CAL,
- 0, 7, 7, 0),
-
SND_SOC_DAPM_OUTPUT("HPOL"),
SND_SOC_DAPM_OUTPUT("HPOR"),
};
@@ -166,34 +148,12 @@ static const struct snd_soc_dapm_route es8326_dapm_routes[] = {
{"I2S OUT", NULL, "ADC L"},
{"I2S OUT", NULL, "ADC R"},
- {"I2S OUT", NULL, "Analog Power"},
- {"I2S OUT", NULL, "ADC Vref"},
- {"I2S OUT", NULL, "Vref Power"},
- {"I2S OUT", NULL, "IBias Power"},
- {"I2S IN", NULL, "Analog Power"},
- {"I2S IN", NULL, "DAC Vref"},
- {"I2S IN", NULL, "Vref Power"},
- {"I2S IN", NULL, "IBias Power"},
-
{"Right DAC", NULL, "I2S IN"},
{"Left DAC", NULL, "I2S IN"},
{"LHPMIX", NULL, "Left DAC"},
{"RHPMIX", NULL, "Right DAC"},
- {"HPOR", NULL, "HPOR Cal"},
- {"HPOL", NULL, "HPOL Cal"},
- {"HPOR", NULL, "HPOR Supply"},
- {"HPOL", NULL, "HPOL Supply"},
- {"HPOL", NULL, "Headphone Charge Pump"},
- {"HPOR", NULL, "Headphone Charge Pump"},
- {"HPOL", NULL, "Headphone Driver Bias"},
- {"HPOR", NULL, "Headphone Driver Bias"},
- {"HPOL", NULL, "Headphone LDO"},
- {"HPOR", NULL, "Headphone LDO"},
- {"HPOL", NULL, "Headphone Reference"},
- {"HPOR", NULL, "Headphone Reference"},
-
{"HPOL", NULL, "LHPMIX"},
{"HPOR", NULL, "RHPMIX"},
};
@@ -419,6 +379,31 @@ static int es8326_pcm_hw_params(struct snd_pcm_substream *substream,
return 0;
}
+static int es8326_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+
+ if (mute) {
+ regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_OFF);
+ regmap_update_bits(es8326->regmap, ES8326_DAC_MUTE,
+ ES8326_MUTE_MASK, ES8326_MUTE);
+ regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xf0);
+ } else {
+ if (!es8326->calibrated) {
+ regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_FORCE_CAL);
+ msleep(30);
+ es8326->calibrated = true;
+ }
+ regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xa0);
+ regmap_write(es8326->regmap, ES8326_HP_VOL, 0x00);
+ regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_ON);
+ regmap_update_bits(es8326->regmap, ES8326_DAC_MUTE,
+ ES8326_MUTE_MASK, ~(ES8326_MUTE));
+ }
+ return 0;
+}
+
static int es8326_set_bias_level(struct snd_soc_component *codec,
enum snd_soc_bias_level level)
{
@@ -469,6 +454,8 @@ static const struct snd_soc_dai_ops es8326_ops = {
.hw_params = es8326_pcm_hw_params,
.set_fmt = es8326_set_dai_fmt,
.set_sysclk = es8326_set_dai_sysclk,
+ .mute_stream = es8326_mute,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver es8326_dai = {
@@ -691,7 +678,7 @@ static int es8326_suspend(struct snd_soc_component *component)
cancel_delayed_work_sync(&es8326->jack_detect_work);
es8326_disable_micbias(component);
-
+ es8326->calibrated = false;
regmap_write(es8326->regmap, ES8326_CLK_CTL, ES8326_CLK_OFF);
regcache_cache_only(es8326->regmap, true);
regcache_mark_dirty(es8326->regmap);
diff --git a/sound/soc/codecs/es8326.h b/sound/soc/codecs/es8326.h
index 8e5ffe5ee10d..3f8f7da58062 100644
--- a/sound/soc/codecs/es8326.h
+++ b/sound/soc/codecs/es8326.h
@@ -9,8 +9,6 @@
#ifndef _ES8326_H
#define _ES8326_H
-#define CONFIG_HHTECH_MINIPMP 1
-
/* ES8326 register space */
#define ES8326_RESET 0x00
#define ES8326_CLK_CTL 0x01
@@ -94,6 +92,8 @@
#define ES8326_PWRUP_SEQ_EN (1 << 5)
#define ES8326_CODEC_RESET (0x0f << 0)
#define ES8326_CSM_OFF (0 << 7)
+#define ES8326_MUTE_MASK (3 << 0)
+#define ES8326_MUTE (3 << 0)
/* ES8326_CLK_CTL */
#define ES8326_CLK_ON (0x7f << 0)
@@ -122,7 +122,9 @@
#define ES8326_MIC2_SEL (1 << 5)
/* ES8326_HP_CAL */
-#define ES8326_HPOR_SHIFT 4
+#define ES8326_HP_OFF 0
+#define ES8326_HP_FORCE_CAL ((1 << 7) | (1 << 3))
+#define ES8326_HP_ON ((7 << 4) | (7 << 0))
/* ES8326_ADC1_SRC */
#define ES8326_ADC1_SHIFT 0
@@ -180,3 +182,4 @@
#define ES8326_VERSION_B (1 << 0)
#endif
+
diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c
index 685ca95ef4a9..29197d34ec09 100644
--- a/sound/soc/codecs/lpass-rx-macro.c
+++ b/sound/soc/codecs/lpass-rx-macro.c
@@ -3537,25 +3537,25 @@ static int rx_macro_probe(struct platform_device *pdev)
rx->macro = devm_clk_get_optional(dev, "macro");
if (IS_ERR(rx->macro))
- return PTR_ERR(rx->macro);
+ return dev_err_probe(dev, PTR_ERR(rx->macro), "unable to get macro clock\n");
rx->dcodec = devm_clk_get_optional(dev, "dcodec");
if (IS_ERR(rx->dcodec))
- return PTR_ERR(rx->dcodec);
+ return dev_err_probe(dev, PTR_ERR(rx->dcodec), "unable to get dcodec clock\n");
rx->mclk = devm_clk_get(dev, "mclk");
if (IS_ERR(rx->mclk))
- return PTR_ERR(rx->mclk);
+ return dev_err_probe(dev, PTR_ERR(rx->mclk), "unable to get mclk clock\n");
if (flags & LPASS_MACRO_FLAG_HAS_NPL_CLOCK) {
rx->npl = devm_clk_get(dev, "npl");
if (IS_ERR(rx->npl))
- return PTR_ERR(rx->npl);
+ return dev_err_probe(dev, PTR_ERR(rx->npl), "unable to get npl clock\n");
}
rx->fsgen = devm_clk_get(dev, "fsgen");
if (IS_ERR(rx->fsgen))
- return PTR_ERR(rx->fsgen);
+ return dev_err_probe(dev, PTR_ERR(rx->fsgen), "unable to get fsgen clock\n");
rx->pds = lpass_macro_pds_init(dev);
if (IS_ERR(rx->pds))
diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c
index de978c3d70b7..3e33418898e8 100644
--- a/sound/soc/codecs/lpass-tx-macro.c
+++ b/sound/soc/codecs/lpass-tx-macro.c
@@ -1967,25 +1967,25 @@ static int tx_macro_probe(struct platform_device *pdev)
tx->macro = devm_clk_get_optional(dev, "macro");
if (IS_ERR(tx->macro))
- return PTR_ERR(tx->macro);
+ return dev_err_probe(dev, PTR_ERR(tx->macro), "unable to get macro clock\n");
tx->dcodec = devm_clk_get_optional(dev, "dcodec");
if (IS_ERR(tx->dcodec))
- return PTR_ERR(tx->dcodec);
+ return dev_err_probe(dev, PTR_ERR(tx->dcodec), "unable to get dcodec clock\n");
tx->mclk = devm_clk_get(dev, "mclk");
if (IS_ERR(tx->mclk))
- return PTR_ERR(tx->mclk);
+ return dev_err_probe(dev, PTR_ERR(tx->mclk), "unable to get mclk clock\n");
if (flags & LPASS_MACRO_FLAG_HAS_NPL_CLOCK) {
tx->npl = devm_clk_get(dev, "npl");
if (IS_ERR(tx->npl))
- return PTR_ERR(tx->npl);
+ return dev_err_probe(dev, PTR_ERR(tx->npl), "unable to get npl clock\n");
}
tx->fsgen = devm_clk_get(dev, "fsgen");
if (IS_ERR(tx->fsgen))
- return PTR_ERR(tx->fsgen);
+ return dev_err_probe(dev, PTR_ERR(tx->fsgen), "unable to get fsgen clock\n");
tx->pds = lpass_macro_pds_init(dev);
if (IS_ERR(tx->pds))
diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c
index 74724448da50..b71ef03c4aef 100644
--- a/sound/soc/codecs/lpass-va-macro.c
+++ b/sound/soc/codecs/lpass-va-macro.c
@@ -1457,15 +1457,15 @@ static int va_macro_probe(struct platform_device *pdev)
va->macro = devm_clk_get_optional(dev, "macro");
if (IS_ERR(va->macro))
- return PTR_ERR(va->macro);
+ return dev_err_probe(dev, PTR_ERR(va->macro), "unable to get macro clock\n");
va->dcodec = devm_clk_get_optional(dev, "dcodec");
if (IS_ERR(va->dcodec))
- return PTR_ERR(va->dcodec);
+ return dev_err_probe(dev, PTR_ERR(va->dcodec), "unable to get dcodec clock\n");
va->mclk = devm_clk_get(dev, "mclk");
if (IS_ERR(va->mclk))
- return PTR_ERR(va->mclk);
+ return dev_err_probe(dev, PTR_ERR(va->mclk), "unable to get mclk clock\n");
va->pds = lpass_macro_pds_init(dev);
if (IS_ERR(va->pds))
diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c
index 8ba7dc89daaa..ec6859ec0d38 100644
--- a/sound/soc/codecs/lpass-wsa-macro.c
+++ b/sound/soc/codecs/lpass-wsa-macro.c
@@ -2396,25 +2396,25 @@ static int wsa_macro_probe(struct platform_device *pdev)
wsa->macro = devm_clk_get_optional(dev, "macro");
if (IS_ERR(wsa->macro))
- return PTR_ERR(wsa->macro);
+ return dev_err_probe(dev, PTR_ERR(wsa->macro), "unable to get macro clock\n");
wsa->dcodec = devm_clk_get_optional(dev, "dcodec");
if (IS_ERR(wsa->dcodec))
- return PTR_ERR(wsa->dcodec);
+ return dev_err_probe(dev, PTR_ERR(wsa->dcodec), "unable to get dcodec clock\n");
wsa->mclk = devm_clk_get(dev, "mclk");
if (IS_ERR(wsa->mclk))
- return PTR_ERR(wsa->mclk);
+ return dev_err_probe(dev, PTR_ERR(wsa->mclk), "unable to get mclk clock\n");
if (flags & LPASS_MACRO_FLAG_HAS_NPL_CLOCK) {
wsa->npl = devm_clk_get(dev, "npl");
if (IS_ERR(wsa->npl))
- return PTR_ERR(wsa->npl);
+ return dev_err_probe(dev, PTR_ERR(wsa->npl), "unable to get npl clock\n");
}
wsa->fsgen = devm_clk_get(dev, "fsgen");
if (IS_ERR(wsa->fsgen))
- return PTR_ERR(wsa->fsgen);
+ return dev_err_probe(dev, PTR_ERR(wsa->fsgen), "unable to get fsgen clock\n");
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
diff --git a/sound/soc/codecs/msm8916-wcd-analog.c b/sound/soc/codecs/msm8916-wcd-analog.c
index cec90cf920ff..9ca381812975 100644
--- a/sound/soc/codecs/msm8916-wcd-analog.c
+++ b/sound/soc/codecs/msm8916-wcd-analog.c
@@ -7,7 +7,6 @@
#include <linux/delay.h>
#include <linux/regulator/consumer.h>
#include <linux/types.h>
-#include <linux/clk.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
@@ -1198,12 +1197,6 @@ static int pm8916_wcd_analog_spmi_probe(struct platform_device *pdev)
if (ret < 0)
return ret;
- priv->mclk = devm_clk_get(dev, "mclk");
- if (IS_ERR(priv->mclk)) {
- dev_err(dev, "failed to get mclk\n");
- return PTR_ERR(priv->mclk);
- }
-
for (i = 0; i < ARRAY_SIZE(supply_names); i++)
priv->supplies[i].supply = supply_names[i];
@@ -1214,55 +1207,48 @@ static int pm8916_wcd_analog_spmi_probe(struct platform_device *pdev)
return ret;
}
- ret = clk_prepare_enable(priv->mclk);
- if (ret < 0) {
- dev_err(dev, "failed to enable mclk %d\n", ret);
- return ret;
- }
-
irq = platform_get_irq_byname(pdev, "mbhc_switch_int");
- if (irq < 0) {
- ret = irq;
- goto err_disable_clk;
- }
+ if (irq < 0)
+ return irq;
ret = devm_request_threaded_irq(dev, irq, NULL,
pm8916_mbhc_switch_irq_handler,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
IRQF_ONESHOT,
"mbhc switch irq", priv);
- if (ret)
+ if (ret) {
dev_err(dev, "cannot request mbhc switch irq\n");
+ return ret;
+ }
if (priv->mbhc_btn_enabled) {
irq = platform_get_irq_byname(pdev, "mbhc_but_press_det");
- if (irq < 0) {
- ret = irq;
- goto err_disable_clk;
- }
+ if (irq < 0)
+ return irq;
ret = devm_request_threaded_irq(dev, irq, NULL,
mbhc_btn_press_irq_handler,
IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"mbhc btn press irq", priv);
- if (ret)
+ if (ret) {
dev_err(dev, "cannot request mbhc button press irq\n");
+ return ret;
+ }
irq = platform_get_irq_byname(pdev, "mbhc_but_rel_det");
- if (irq < 0) {
- ret = irq;
- goto err_disable_clk;
- }
+ if (irq < 0)
+ return irq;
ret = devm_request_threaded_irq(dev, irq, NULL,
mbhc_btn_release_irq_handler,
IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"mbhc btn release irq", priv);
- if (ret)
+ if (ret) {
dev_err(dev, "cannot request mbhc button release irq\n");
-
+ return ret;
+ }
}
dev_set_drvdata(dev, priv);
@@ -1270,17 +1256,6 @@ static int pm8916_wcd_analog_spmi_probe(struct platform_device *pdev)
return devm_snd_soc_register_component(dev, &pm8916_wcd_analog,
pm8916_wcd_analog_dai,
ARRAY_SIZE(pm8916_wcd_analog_dai));
-
-err_disable_clk:
- clk_disable_unprepare(priv->mclk);
- return ret;
-}
-
-static void pm8916_wcd_analog_spmi_remove(struct platform_device *pdev)
-{
- struct pm8916_wcd_analog_priv *priv = dev_get_drvdata(&pdev->dev);
-
- clk_disable_unprepare(priv->mclk);
}
static const struct of_device_id pm8916_wcd_analog_spmi_match_table[] = {
@@ -1296,7 +1271,6 @@ static struct platform_driver pm8916_wcd_analog_spmi_driver = {
.of_match_table = pm8916_wcd_analog_spmi_match_table,
},
.probe = pm8916_wcd_analog_spmi_probe,
- .remove_new = pm8916_wcd_analog_spmi_remove,
};
module_platform_driver(pm8916_wcd_analog_spmi_driver);
diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c
index 9e0e4ddf128e..5cb0de648bd3 100644
--- a/sound/soc/codecs/nau8825.c
+++ b/sound/soc/codecs/nau8825.c
@@ -11,6 +11,7 @@
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/init.h>
+#include <linux/int_log.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/slab.h>
@@ -38,7 +39,6 @@
#define NAU_FVCO_MIN 90000000
/* cross talk suppression detection */
-#define LOG10_MAGIC 646456993
#define GAIN_AUGMENT 22500
#define SIDETONE_BASE 207000
@@ -219,42 +219,6 @@ static const struct reg_sequence nau8825_regmap_patch[] = {
{ NAU8825_REG_MIC_BIAS, 0x0046 },
};
-
-static const unsigned short logtable[256] = {
- 0x0000, 0x0171, 0x02e0, 0x044e, 0x05ba, 0x0725, 0x088e, 0x09f7,
- 0x0b5d, 0x0cc3, 0x0e27, 0x0f8a, 0x10eb, 0x124b, 0x13aa, 0x1508,
- 0x1664, 0x17bf, 0x1919, 0x1a71, 0x1bc8, 0x1d1e, 0x1e73, 0x1fc6,
- 0x2119, 0x226a, 0x23ba, 0x2508, 0x2656, 0x27a2, 0x28ed, 0x2a37,
- 0x2b80, 0x2cc8, 0x2e0f, 0x2f54, 0x3098, 0x31dc, 0x331e, 0x345f,
- 0x359f, 0x36de, 0x381b, 0x3958, 0x3a94, 0x3bce, 0x3d08, 0x3e41,
- 0x3f78, 0x40af, 0x41e4, 0x4319, 0x444c, 0x457f, 0x46b0, 0x47e1,
- 0x4910, 0x4a3f, 0x4b6c, 0x4c99, 0x4dc5, 0x4eef, 0x5019, 0x5142,
- 0x526a, 0x5391, 0x54b7, 0x55dc, 0x5700, 0x5824, 0x5946, 0x5a68,
- 0x5b89, 0x5ca8, 0x5dc7, 0x5ee5, 0x6003, 0x611f, 0x623a, 0x6355,
- 0x646f, 0x6588, 0x66a0, 0x67b7, 0x68ce, 0x69e4, 0x6af8, 0x6c0c,
- 0x6d20, 0x6e32, 0x6f44, 0x7055, 0x7165, 0x7274, 0x7383, 0x7490,
- 0x759d, 0x76aa, 0x77b5, 0x78c0, 0x79ca, 0x7ad3, 0x7bdb, 0x7ce3,
- 0x7dea, 0x7ef0, 0x7ff6, 0x80fb, 0x81ff, 0x8302, 0x8405, 0x8507,
- 0x8608, 0x8709, 0x8809, 0x8908, 0x8a06, 0x8b04, 0x8c01, 0x8cfe,
- 0x8dfa, 0x8ef5, 0x8fef, 0x90e9, 0x91e2, 0x92db, 0x93d2, 0x94ca,
- 0x95c0, 0x96b6, 0x97ab, 0x98a0, 0x9994, 0x9a87, 0x9b7a, 0x9c6c,
- 0x9d5e, 0x9e4f, 0x9f3f, 0xa02e, 0xa11e, 0xa20c, 0xa2fa, 0xa3e7,
- 0xa4d4, 0xa5c0, 0xa6ab, 0xa796, 0xa881, 0xa96a, 0xaa53, 0xab3c,
- 0xac24, 0xad0c, 0xadf2, 0xaed9, 0xafbe, 0xb0a4, 0xb188, 0xb26c,
- 0xb350, 0xb433, 0xb515, 0xb5f7, 0xb6d9, 0xb7ba, 0xb89a, 0xb97a,
- 0xba59, 0xbb38, 0xbc16, 0xbcf4, 0xbdd1, 0xbead, 0xbf8a, 0xc065,
- 0xc140, 0xc21b, 0xc2f5, 0xc3cf, 0xc4a8, 0xc580, 0xc658, 0xc730,
- 0xc807, 0xc8de, 0xc9b4, 0xca8a, 0xcb5f, 0xcc34, 0xcd08, 0xcddc,
- 0xceaf, 0xcf82, 0xd054, 0xd126, 0xd1f7, 0xd2c8, 0xd399, 0xd469,
- 0xd538, 0xd607, 0xd6d6, 0xd7a4, 0xd872, 0xd93f, 0xda0c, 0xdad9,
- 0xdba5, 0xdc70, 0xdd3b, 0xde06, 0xded0, 0xdf9a, 0xe063, 0xe12c,
- 0xe1f5, 0xe2bd, 0xe385, 0xe44c, 0xe513, 0xe5d9, 0xe69f, 0xe765,
- 0xe82a, 0xe8ef, 0xe9b3, 0xea77, 0xeb3b, 0xebfe, 0xecc1, 0xed83,
- 0xee45, 0xef06, 0xefc8, 0xf088, 0xf149, 0xf209, 0xf2c8, 0xf387,
- 0xf446, 0xf505, 0xf5c3, 0xf680, 0xf73e, 0xf7fb, 0xf8b7, 0xf973,
- 0xfa2f, 0xfaea, 0xfba5, 0xfc60, 0xfd1a, 0xfdd4, 0xfe8e, 0xff47
-};
-
/**
* nau8825_sema_acquire - acquire the semaphore of nau88l25
* @nau8825: component to register the codec private data with
@@ -368,65 +332,14 @@ static void nau8825_hpvol_ramp(struct nau8825 *nau8825,
}
/**
- * nau8825_intlog10_dec3 - Computes log10 of a value
- * the result is round off to 3 decimal. This function takes reference to
- * dvb-math. The source code locates as the following.
- * Linux/drivers/media/dvb-core/dvb_math.c
+ * nau8825_intlog10_dec3 - Computes log10 of a value, rounding the result to 3 decimal places.
* @value: input for log10
*
* return log10(value) * 1000
*/
static u32 nau8825_intlog10_dec3(u32 value)
{
- u32 msb, logentry, significand, interpolation, log10val;
- u64 log2val;
-
- /* first detect the msb (count begins at 0) */
- msb = fls(value) - 1;
- /**
- * now we use a logtable after the following method:
- *
- * log2(2^x * y) * 2^24 = x * 2^24 + log2(y) * 2^24
- * where x = msb and therefore 1 <= y < 2
- * first y is determined by shifting the value left
- * so that msb is bit 31
- * 0x00231f56 -> 0x8C7D5800
- * the result is y * 2^31 -> "significand"
- * then the highest 9 bits are used for a table lookup
- * the highest bit is discarded because it's always set
- * the highest nine bits in our example are 100011000
- * so we would use the entry 0x18
- */
- significand = value << (31 - msb);
- logentry = (significand >> 23) & 0xff;
- /**
- * last step we do is interpolation because of the
- * limitations of the log table the error is that part of
- * the significand which isn't used for lookup then we
- * compute the ratio between the error and the next table entry
- * and interpolate it between the log table entry used and the
- * next one the biggest error possible is 0x7fffff
- * (in our example it's 0x7D5800)
- * needed value for next table entry is 0x800000
- * so the interpolation is
- * (error / 0x800000) * (logtable_next - logtable_current)
- * in the implementation the division is moved to the end for
- * better accuracy there is also an overflow correction if
- * logtable_next is 256
- */
- interpolation = ((significand & 0x7fffff) *
- ((logtable[(logentry + 1) & 0xff] -
- logtable[logentry]) & 0xffff)) >> 15;
-
- log2val = ((msb << 24) + (logtable[logentry] << 8) + interpolation);
- /**
- * log10(x) = log2(x) * log10(2)
- */
- log10val = (log2val * LOG10_MAGIC) >> 31;
- /**
- * the result is round off to 3 decimal
- */
- return log10val / ((1 << 24) / 1000);
+ return intlog10(value) / ((1 << 24) / 1000);
}
/**
diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c
index 0ed4fa261abf..eceed8209787 100644
--- a/sound/soc/codecs/rt5640.c
+++ b/sound/soc/codecs/rt5640.c
@@ -53,7 +53,6 @@ static const struct reg_sequence init_list[] = {
{RT5640_PR_BASE + 0x3d, 0x3600},
{RT5640_PR_BASE + 0x12, 0x0aa8},
{RT5640_PR_BASE + 0x14, 0x0aaa},
- {RT5640_PR_BASE + 0x20, 0x6110},
{RT5640_PR_BASE + 0x21, 0xe0e0},
{RT5640_PR_BASE + 0x23, 0x1804},
};
@@ -2567,9 +2566,10 @@ static void rt5640_enable_jack_detect(struct snd_soc_component *component,
if (jack_data && jack_data->use_platform_clock)
rt5640->use_platform_clock = jack_data->use_platform_clock;
- ret = request_irq(rt5640->irq, rt5640_irq,
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
- "rt5640", rt5640);
+ ret = devm_request_threaded_irq(component->dev, rt5640->irq,
+ NULL, rt5640_irq,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "rt5640", rt5640);
if (ret) {
dev_warn(component->dev, "Failed to reguest IRQ %d: %d\n", rt5640->irq, ret);
rt5640_disable_jack_detect(component);
@@ -2622,8 +2622,9 @@ static void rt5640_enable_hda_jack_detect(
rt5640->jack = jack;
- ret = request_irq(rt5640->irq, rt5640_irq,
- IRQF_TRIGGER_RISING | IRQF_ONESHOT, "rt5640", rt5640);
+ ret = devm_request_threaded_irq(component->dev, rt5640->irq,
+ NULL, rt5640_irq, IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "rt5640", rt5640);
if (ret) {
dev_warn(component->dev, "Failed to reguest IRQ %d: %d\n", rt5640->irq, ret);
rt5640->irq = -ENXIO;
diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c
index acc7fb1581b2..5be5ec0260e9 100644
--- a/sound/soc/codecs/rt5645.c
+++ b/sound/soc/codecs/rt5645.c
@@ -3258,6 +3258,22 @@ int rt5645_set_jack_detect(struct snd_soc_component *component,
}
EXPORT_SYMBOL_GPL(rt5645_set_jack_detect);
+static int rt5645_component_set_jack(struct snd_soc_component *component,
+ struct snd_soc_jack *hs_jack, void *data)
+{
+ struct snd_soc_jack *mic_jack = NULL;
+ struct snd_soc_jack *btn_jack = NULL;
+ int *type = (int *)data;
+
+ if (*type & SND_JACK_MICROPHONE)
+ mic_jack = hs_jack;
+ if (*type & (SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3))
+ btn_jack = hs_jack;
+
+ return rt5645_set_jack_detect(component, hs_jack, mic_jack, btn_jack);
+}
+
static void rt5645_jack_detect_work(struct work_struct *work)
{
struct rt5645_priv *rt5645 =
@@ -3532,6 +3548,7 @@ static const struct snd_soc_component_driver soc_component_dev_rt5645 = {
.num_dapm_widgets = ARRAY_SIZE(rt5645_dapm_widgets),
.dapm_routes = rt5645_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(rt5645_dapm_routes),
+ .set_jack = rt5645_component_set_jack,
.use_pmdown_time = 1,
.endianness = 1,
};
@@ -3950,7 +3967,11 @@ static int rt5645_i2c_probe(struct i2c_client *i2c)
* read and power On.
*/
msleep(TIME_TO_POWER_MS);
- regmap_read(regmap, RT5645_VENDOR_ID2, &val);
+ ret = regmap_read(regmap, RT5645_VENDOR_ID2, &val);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to read: 0x%02X\n, ret = %d", RT5645_VENDOR_ID2, ret);
+ goto err_enable;
+ }
switch (val) {
case RT5645_DEVICE_ID:
@@ -4178,11 +4199,44 @@ static void rt5645_i2c_shutdown(struct i2c_client *i2c)
regmap_write(rt5645->regmap, RT5645_RESET, 0);
}
+static int __maybe_unused rt5645_sys_suspend(struct device *dev)
+{
+ struct rt5645_priv *rt5645 = dev_get_drvdata(dev);
+
+ del_timer_sync(&rt5645->btn_check_timer);
+ cancel_delayed_work_sync(&rt5645->jack_detect_work);
+ cancel_delayed_work_sync(&rt5645->rcclock_work);
+
+ regcache_cache_only(rt5645->regmap, true);
+ regcache_mark_dirty(rt5645->regmap);
+ return 0;
+}
+
+static int __maybe_unused rt5645_sys_resume(struct device *dev)
+{
+ struct rt5645_priv *rt5645 = dev_get_drvdata(dev);
+
+ regcache_cache_only(rt5645->regmap, false);
+ regcache_sync(rt5645->regmap);
+
+ if (rt5645->hp_jack) {
+ rt5645->jack_type = 0;
+ queue_delayed_work(system_power_efficient_wq,
+ &rt5645->jack_detect_work, msecs_to_jiffies(0));
+ }
+ return 0;
+}
+
+static const struct dev_pm_ops rt5645_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(rt5645_sys_suspend, rt5645_sys_resume)
+};
+
static struct i2c_driver rt5645_i2c_driver = {
.driver = {
.name = "rt5645",
.of_match_table = of_match_ptr(rt5645_of_match),
.acpi_match_table = ACPI_PTR(rt5645_acpi_match),
+ .pm = &rt5645_pm,
},
.probe = rt5645_i2c_probe,
.remove = rt5645_i2c_remove,
diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c
index ad14d18860fc..0e70a3ab42b5 100644
--- a/sound/soc/codecs/rt5677.c
+++ b/sound/soc/codecs/rt5677.c
@@ -6,23 +6,21 @@
* Author: Oder Chiou <oder_chiou@realtek.com>
*/
-#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
#include <linux/fs.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irqdomain.h>
+#include <linux/irq.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
-#include <linux/init.h>
-#include <linux/delay.h>
+#include <linux/platform_device.h>
#include <linux/pm.h>
+#include <linux/property.h>
#include <linux/regmap.h>
-#include <linux/i2c.h>
-#include <linux/platform_device.h>
#include <linux/spi/spi.h>
-#include <linux/firmware.h>
-#include <linux/of_device.h>
-#include <linux/property.h>
-#include <linux/irq.h>
-#include <linux/interrupt.h>
-#include <linux/irqdomain.h>
#include <linux/workqueue.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -4717,50 +4715,34 @@ static int rt5677_set_bias_level(struct snd_soc_component *component,
return 0;
}
+static int rt5677_update_gpio_bits(struct rt5677_priv *rt5677, unsigned offset, int m, int v)
+{
+ unsigned int bank = offset / 5;
+ unsigned int shift = (offset % 5) * 3;
+ unsigned int reg = bank ? RT5677_GPIO_CTRL3 : RT5677_GPIO_CTRL2;
+
+ return regmap_update_bits(rt5677->regmap, reg, m << shift, v << shift);
+}
+
#ifdef CONFIG_GPIOLIB
static void rt5677_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
struct rt5677_priv *rt5677 = gpiochip_get_data(chip);
+ int level = value ? RT5677_GPIOx_OUT_HI : RT5677_GPIOx_OUT_LO;
+ int m = RT5677_GPIOx_OUT_MASK;
- switch (offset) {
- case RT5677_GPIO1 ... RT5677_GPIO5:
- regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL2,
- 0x1 << (offset * 3 + 1), !!value << (offset * 3 + 1));
- break;
-
- case RT5677_GPIO6:
- regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL3,
- RT5677_GPIO6_OUT_MASK, !!value << RT5677_GPIO6_OUT_SFT);
- break;
-
- default:
- break;
- }
+ rt5677_update_gpio_bits(rt5677, offset, m, level);
}
static int rt5677_gpio_direction_out(struct gpio_chip *chip,
unsigned offset, int value)
{
struct rt5677_priv *rt5677 = gpiochip_get_data(chip);
+ int level = value ? RT5677_GPIOx_OUT_HI : RT5677_GPIOx_OUT_LO;
+ int m = RT5677_GPIOx_DIR_MASK | RT5677_GPIOx_OUT_MASK;
+ int v = RT5677_GPIOx_DIR_OUT | level;
- switch (offset) {
- case RT5677_GPIO1 ... RT5677_GPIO5:
- regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL2,
- 0x3 << (offset * 3 + 1),
- (0x2 | !!value) << (offset * 3 + 1));
- break;
-
- case RT5677_GPIO6:
- regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL3,
- RT5677_GPIO6_DIR_MASK | RT5677_GPIO6_OUT_MASK,
- RT5677_GPIO6_DIR_OUT | !!value << RT5677_GPIO6_OUT_SFT);
- break;
-
- default:
- break;
- }
-
- return 0;
+ return rt5677_update_gpio_bits(rt5677, offset, m, v);
}
static int rt5677_gpio_get(struct gpio_chip *chip, unsigned offset)
@@ -4778,26 +4760,14 @@ static int rt5677_gpio_get(struct gpio_chip *chip, unsigned offset)
static int rt5677_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
{
struct rt5677_priv *rt5677 = gpiochip_get_data(chip);
+ int m = RT5677_GPIOx_DIR_MASK;
+ int v = RT5677_GPIOx_DIR_IN;
- switch (offset) {
- case RT5677_GPIO1 ... RT5677_GPIO5:
- regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL2,
- 0x1 << (offset * 3 + 2), 0x0);
- break;
-
- case RT5677_GPIO6:
- regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL3,
- RT5677_GPIO6_DIR_MASK, RT5677_GPIO6_DIR_IN);
- break;
-
- default:
- break;
- }
-
- return 0;
+ return rt5677_update_gpio_bits(rt5677, offset, m, v);
}
-/** Configures the gpio as
+/*
+ * Configures the GPIO as
* 0 - floating
* 1 - pull down
* 2 - pull up
@@ -5539,7 +5509,7 @@ static int rt5677_init_irq(struct i2c_client *i2c)
RT5677_GPIO1_PIN_MASK, RT5677_GPIO1_PIN_IRQ);
/* Ready to listen for interrupts */
- rt5677->domain = irq_domain_add_linear(i2c->dev.of_node,
+ rt5677->domain = irq_domain_create_linear(dev_fwnode(&i2c->dev),
RT5677_IRQ_NUM, &rt5677_domain_ops, rt5677);
if (!rt5677->domain) {
dev_err(&i2c->dev, "Failed to create IRQ domain\n");
@@ -5559,6 +5529,7 @@ static int rt5677_init_irq(struct i2c_client *i2c)
static int rt5677_i2c_probe(struct i2c_client *i2c)
{
+ struct device *dev = &i2c->dev;
struct rt5677_priv *rt5677;
int ret;
unsigned int val;
@@ -5573,21 +5544,9 @@ static int rt5677_i2c_probe(struct i2c_client *i2c)
INIT_DELAYED_WORK(&rt5677->dsp_work, rt5677_dsp_work);
i2c_set_clientdata(i2c, rt5677);
- if (i2c->dev.of_node) {
- const struct of_device_id *match_id;
-
- match_id = of_match_device(rt5677_of_match, &i2c->dev);
- if (match_id)
- rt5677->type = (enum rt5677_type)match_id->data;
- } else if (ACPI_HANDLE(&i2c->dev)) {
- const struct acpi_device_id *acpi_id;
-
- acpi_id = acpi_match_device(rt5677_acpi_match, &i2c->dev);
- if (acpi_id)
- rt5677->type = (enum rt5677_type)acpi_id->driver_data;
- } else {
+ rt5677->type = (enum rt5677_type)(uintptr_t)device_get_match_data(dev);
+ if (rt5677->type == 0)
return -EINVAL;
- }
rt5677_read_device_properties(rt5677, &i2c->dev);
@@ -5673,9 +5632,9 @@ static int rt5677_i2c_probe(struct i2c_client *i2c)
regmap_update_bits(rt5677->regmap, RT5677_GEN_CTRL2,
RT5677_GPIO5_FUNC_MASK,
RT5677_GPIO5_FUNC_DMIC);
- regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL2,
- RT5677_GPIO5_DIR_MASK,
- RT5677_GPIO5_DIR_OUT);
+ rt5677_update_gpio_bits(rt5677, RT5677_GPIO5,
+ RT5677_GPIOx_DIR_MASK,
+ RT5677_GPIOx_DIR_OUT);
}
if (rt5677->pdata.micbias1_vdd_3v3)
@@ -5702,7 +5661,7 @@ static struct i2c_driver rt5677_i2c_driver = {
.driver = {
.name = RT5677_DRV_NAME,
.of_match_table = rt5677_of_match,
- .acpi_match_table = ACPI_PTR(rt5677_acpi_match),
+ .acpi_match_table = rt5677_acpi_match,
},
.probe = rt5677_i2c_probe,
.remove = rt5677_i2c_remove,
diff --git a/sound/soc/codecs/rt5677.h b/sound/soc/codecs/rt5677.h
index 944ae02aafc2..d67ebae067d9 100644
--- a/sound/soc/codecs/rt5677.h
+++ b/sound/soc/codecs/rt5677.h
@@ -1587,81 +1587,19 @@
#define RT5677_FUNC_MODE_DMIC_GPIO (0x0 << 13)
#define RT5677_FUNC_MODE_JTAG (0x1 << 13)
-/* GPIO Control 2 (0xc1) */
-#define RT5677_GPIO5_DIR_MASK (0x1 << 14)
-#define RT5677_GPIO5_DIR_SFT 14
-#define RT5677_GPIO5_DIR_IN (0x0 << 14)
-#define RT5677_GPIO5_DIR_OUT (0x1 << 14)
-#define RT5677_GPIO5_OUT_MASK (0x1 << 13)
-#define RT5677_GPIO5_OUT_SFT 13
-#define RT5677_GPIO5_OUT_LO (0x0 << 13)
-#define RT5677_GPIO5_OUT_HI (0x1 << 13)
-#define RT5677_GPIO5_P_MASK (0x1 << 12)
-#define RT5677_GPIO5_P_SFT 12
-#define RT5677_GPIO5_P_NOR (0x0 << 12)
-#define RT5677_GPIO5_P_INV (0x1 << 12)
-#define RT5677_GPIO4_DIR_MASK (0x1 << 11)
-#define RT5677_GPIO4_DIR_SFT 11
-#define RT5677_GPIO4_DIR_IN (0x0 << 11)
-#define RT5677_GPIO4_DIR_OUT (0x1 << 11)
-#define RT5677_GPIO4_OUT_MASK (0x1 << 10)
-#define RT5677_GPIO4_OUT_SFT 10
-#define RT5677_GPIO4_OUT_LO (0x0 << 10)
-#define RT5677_GPIO4_OUT_HI (0x1 << 10)
-#define RT5677_GPIO4_P_MASK (0x1 << 9)
-#define RT5677_GPIO4_P_SFT 9
-#define RT5677_GPIO4_P_NOR (0x0 << 9)
-#define RT5677_GPIO4_P_INV (0x1 << 9)
-#define RT5677_GPIO3_DIR_MASK (0x1 << 8)
-#define RT5677_GPIO3_DIR_SFT 8
-#define RT5677_GPIO3_DIR_IN (0x0 << 8)
-#define RT5677_GPIO3_DIR_OUT (0x1 << 8)
-#define RT5677_GPIO3_OUT_MASK (0x1 << 7)
-#define RT5677_GPIO3_OUT_SFT 7
-#define RT5677_GPIO3_OUT_LO (0x0 << 7)
-#define RT5677_GPIO3_OUT_HI (0x1 << 7)
-#define RT5677_GPIO3_P_MASK (0x1 << 6)
-#define RT5677_GPIO3_P_SFT 6
-#define RT5677_GPIO3_P_NOR (0x0 << 6)
-#define RT5677_GPIO3_P_INV (0x1 << 6)
-#define RT5677_GPIO2_DIR_MASK (0x1 << 5)
-#define RT5677_GPIO2_DIR_SFT 5
-#define RT5677_GPIO2_DIR_IN (0x0 << 5)
-#define RT5677_GPIO2_DIR_OUT (0x1 << 5)
-#define RT5677_GPIO2_OUT_MASK (0x1 << 4)
-#define RT5677_GPIO2_OUT_SFT 4
-#define RT5677_GPIO2_OUT_LO (0x0 << 4)
-#define RT5677_GPIO2_OUT_HI (0x1 << 4)
-#define RT5677_GPIO2_P_MASK (0x1 << 3)
-#define RT5677_GPIO2_P_SFT 3
-#define RT5677_GPIO2_P_NOR (0x0 << 3)
-#define RT5677_GPIO2_P_INV (0x1 << 3)
-#define RT5677_GPIO1_DIR_MASK (0x1 << 2)
-#define RT5677_GPIO1_DIR_SFT 2
-#define RT5677_GPIO1_DIR_IN (0x0 << 2)
-#define RT5677_GPIO1_DIR_OUT (0x1 << 2)
-#define RT5677_GPIO1_OUT_MASK (0x1 << 1)
-#define RT5677_GPIO1_OUT_SFT 1
-#define RT5677_GPIO1_OUT_LO (0x0 << 1)
-#define RT5677_GPIO1_OUT_HI (0x1 << 1)
-#define RT5677_GPIO1_P_MASK (0x1 << 0)
-#define RT5677_GPIO1_P_SFT 0
-#define RT5677_GPIO1_P_NOR (0x0 << 0)
-#define RT5677_GPIO1_P_INV (0x1 << 0)
-
-/* GPIO Control 3 (0xc2) */
-#define RT5677_GPIO6_DIR_MASK (0x1 << 2)
-#define RT5677_GPIO6_DIR_SFT 2
-#define RT5677_GPIO6_DIR_IN (0x0 << 2)
-#define RT5677_GPIO6_DIR_OUT (0x1 << 2)
-#define RT5677_GPIO6_OUT_MASK (0x1 << 1)
-#define RT5677_GPIO6_OUT_SFT 1
-#define RT5677_GPIO6_OUT_LO (0x0 << 1)
-#define RT5677_GPIO6_OUT_HI (0x1 << 1)
-#define RT5677_GPIO6_P_MASK (0x1 << 0)
-#define RT5677_GPIO6_P_SFT 0
-#define RT5677_GPIO6_P_NOR (0x0 << 0)
-#define RT5677_GPIO6_P_INV (0x1 << 0)
+/* GPIO Control 2 (0xc1) & 3 (0xc2) common bits */
+#define RT5677_GPIOx_DIR_MASK (0x1 << 2)
+#define RT5677_GPIOx_DIR_SFT 2
+#define RT5677_GPIOx_DIR_IN (0x0 << 2)
+#define RT5677_GPIOx_DIR_OUT (0x1 << 2)
+#define RT5677_GPIOx_OUT_MASK (0x1 << 1)
+#define RT5677_GPIOx_OUT_SFT 1
+#define RT5677_GPIOx_OUT_LO (0x0 << 1)
+#define RT5677_GPIOx_OUT_HI (0x1 << 1)
+#define RT5677_GPIOx_P_MASK (0x1 << 0)
+#define RT5677_GPIOx_P_SFT 0
+#define RT5677_GPIOx_P_NOR (0x0 << 0)
+#define RT5677_GPIOx_P_INV (0x1 << 0)
/* General Control (0xfa) */
#define RT5677_IRQ_DEBOUNCE_SEL_MASK (0x3 << 3)
@@ -1753,8 +1691,8 @@ enum {
};
enum rt5677_type {
- RT5677,
- RT5676,
+ RT5677 = 1,
+ RT5676 = 2,
};
/* ASRC clock source selection */
diff --git a/sound/soc/codecs/rt722-sdca-sdw.c b/sound/soc/codecs/rt722-sdca-sdw.c
index cc57e4e27805..a9416bd163e8 100644
--- a/sound/soc/codecs/rt722-sdca-sdw.c
+++ b/sound/soc/codecs/rt722-sdca-sdw.c
@@ -175,7 +175,7 @@ static int rt722_sdca_update_status(struct sdw_slave *slave,
* This also could sync with the cache value as the rt722_sdca_jack_init set.
*/
sdw_write_no_pm(rt722->slave, SDW_SCP_SDCA_INTMASK1,
- SDW_SCP_SDCA_INTMASK_SDCA_0 | SDW_SCP_SDCA_INTMASK_SDCA_6);
+ SDW_SCP_SDCA_INTMASK_SDCA_6);
sdw_write_no_pm(rt722->slave, SDW_SCP_SDCA_INTMASK2,
SDW_SCP_SDCA_INTMASK_SDCA_8);
}
diff --git a/sound/soc/codecs/rt722-sdca.c b/sound/soc/codecs/rt722-sdca.c
index 9c0d34366c9e..0e1c65a20392 100644
--- a/sound/soc/codecs/rt722-sdca.c
+++ b/sound/soc/codecs/rt722-sdca.c
@@ -191,8 +191,7 @@ static void rt722_sdca_jack_detect_handler(struct work_struct *work)
return;
/* SDW_SCP_SDCA_INT_SDCA_6 is used for jack detection */
- if (rt722->scp_sdca_stat1 & SDW_SCP_SDCA_INT_SDCA_6 ||
- rt722->scp_sdca_stat1 & SDW_SCP_SDCA_INT_SDCA_0) {
+ if (rt722->scp_sdca_stat1 & SDW_SCP_SDCA_INT_SDCA_6) {
ret = rt722_sdca_headset_detect(rt722);
if (ret < 0)
return;
diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c
index 4c59429a42b7..55cd5e3c23a5 100644
--- a/sound/soc/codecs/tas2781-i2c.c
+++ b/sound/soc/codecs/tas2781-i2c.c
@@ -743,7 +743,6 @@ MODULE_DEVICE_TABLE(acpi, tasdevice_acpi_match);
static struct i2c_driver tasdevice_i2c_driver = {
.driver = {
.name = "tas2781-codec",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(tasdevice_of_match),
#ifdef CONFIG_ACPI
.acpi_match_table = ACPI_PTR(tasdevice_acpi_match),
diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c
index 1911750f7445..5da1934527f3 100644
--- a/sound/soc/codecs/wcd-mbhc-v2.c
+++ b/sound/soc/codecs/wcd-mbhc-v2.c
@@ -1454,7 +1454,7 @@ struct wcd_mbhc *wcd_mbhc_init(struct snd_soc_component *component,
return ERR_PTR(-EINVAL);
}
- mbhc = devm_kzalloc(dev, sizeof(*mbhc), GFP_KERNEL);
+ mbhc = kzalloc(sizeof(*mbhc), GFP_KERNEL);
if (!mbhc)
return ERR_PTR(-ENOMEM);
@@ -1474,61 +1474,76 @@ struct wcd_mbhc *wcd_mbhc_init(struct snd_soc_component *component,
INIT_WORK(&mbhc->correct_plug_swch, wcd_correct_swch_plug);
- ret = devm_request_threaded_irq(dev, mbhc->intr_ids->mbhc_sw_intr, NULL,
+ ret = request_threaded_irq(mbhc->intr_ids->mbhc_sw_intr, NULL,
wcd_mbhc_mech_plug_detect_irq,
IRQF_ONESHOT | IRQF_TRIGGER_RISING,
"mbhc sw intr", mbhc);
if (ret)
- goto err;
+ goto err_free_mbhc;
- ret = devm_request_threaded_irq(dev, mbhc->intr_ids->mbhc_btn_press_intr, NULL,
+ ret = request_threaded_irq(mbhc->intr_ids->mbhc_btn_press_intr, NULL,
wcd_mbhc_btn_press_handler,
IRQF_ONESHOT | IRQF_TRIGGER_RISING,
"Button Press detect", mbhc);
if (ret)
- goto err;
+ goto err_free_sw_intr;
- ret = devm_request_threaded_irq(dev, mbhc->intr_ids->mbhc_btn_release_intr, NULL,
+ ret = request_threaded_irq(mbhc->intr_ids->mbhc_btn_release_intr, NULL,
wcd_mbhc_btn_release_handler,
IRQF_ONESHOT | IRQF_TRIGGER_RISING,
"Button Release detect", mbhc);
if (ret)
- goto err;
+ goto err_free_btn_press_intr;
- ret = devm_request_threaded_irq(dev, mbhc->intr_ids->mbhc_hs_ins_intr, NULL,
+ ret = request_threaded_irq(mbhc->intr_ids->mbhc_hs_ins_intr, NULL,
wcd_mbhc_adc_hs_ins_irq,
IRQF_ONESHOT | IRQF_TRIGGER_RISING,
"Elect Insert", mbhc);
if (ret)
- goto err;
+ goto err_free_btn_release_intr;
disable_irq_nosync(mbhc->intr_ids->mbhc_hs_ins_intr);
- ret = devm_request_threaded_irq(dev, mbhc->intr_ids->mbhc_hs_rem_intr, NULL,
+ ret = request_threaded_irq(mbhc->intr_ids->mbhc_hs_rem_intr, NULL,
wcd_mbhc_adc_hs_rem_irq,
IRQF_ONESHOT | IRQF_TRIGGER_RISING,
"Elect Remove", mbhc);
if (ret)
- goto err;
+ goto err_free_hs_ins_intr;
disable_irq_nosync(mbhc->intr_ids->mbhc_hs_rem_intr);
- ret = devm_request_threaded_irq(dev, mbhc->intr_ids->hph_left_ocp, NULL,
+ ret = request_threaded_irq(mbhc->intr_ids->hph_left_ocp, NULL,
wcd_mbhc_hphl_ocp_irq,
IRQF_ONESHOT | IRQF_TRIGGER_RISING,
"HPH_L OCP detect", mbhc);
if (ret)
- goto err;
+ goto err_free_hs_rem_intr;
- ret = devm_request_threaded_irq(dev, mbhc->intr_ids->hph_right_ocp, NULL,
+ ret = request_threaded_irq(mbhc->intr_ids->hph_right_ocp, NULL,
wcd_mbhc_hphr_ocp_irq,
IRQF_ONESHOT | IRQF_TRIGGER_RISING,
"HPH_R OCP detect", mbhc);
if (ret)
- goto err;
+ goto err_free_hph_left_ocp;
return mbhc;
-err:
+
+err_free_hph_left_ocp:
+ free_irq(mbhc->intr_ids->hph_left_ocp, mbhc);
+err_free_hs_rem_intr:
+ free_irq(mbhc->intr_ids->mbhc_hs_rem_intr, mbhc);
+err_free_hs_ins_intr:
+ free_irq(mbhc->intr_ids->mbhc_hs_ins_intr, mbhc);
+err_free_btn_release_intr:
+ free_irq(mbhc->intr_ids->mbhc_btn_release_intr, mbhc);
+err_free_btn_press_intr:
+ free_irq(mbhc->intr_ids->mbhc_btn_press_intr, mbhc);
+err_free_sw_intr:
+ free_irq(mbhc->intr_ids->mbhc_sw_intr, mbhc);
+err_free_mbhc:
+ kfree(mbhc);
+
dev_err(dev, "Failed to request mbhc interrupts %d\n", ret);
return ERR_PTR(ret);
@@ -1537,9 +1552,19 @@ EXPORT_SYMBOL(wcd_mbhc_init);
void wcd_mbhc_deinit(struct wcd_mbhc *mbhc)
{
+ free_irq(mbhc->intr_ids->hph_right_ocp, mbhc);
+ free_irq(mbhc->intr_ids->hph_left_ocp, mbhc);
+ free_irq(mbhc->intr_ids->mbhc_hs_rem_intr, mbhc);
+ free_irq(mbhc->intr_ids->mbhc_hs_ins_intr, mbhc);
+ free_irq(mbhc->intr_ids->mbhc_btn_release_intr, mbhc);
+ free_irq(mbhc->intr_ids->mbhc_btn_press_intr, mbhc);
+ free_irq(mbhc->intr_ids->mbhc_sw_intr, mbhc);
+
mutex_lock(&mbhc->lock);
wcd_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
mutex_unlock(&mbhc->lock);
+
+ kfree(mbhc);
}
EXPORT_SYMBOL(wcd_mbhc_deinit);
diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c
index 8bf3510a3ea3..a05b553e6472 100644
--- a/sound/soc/codecs/wcd9335.c
+++ b/sound/soc/codecs/wcd9335.c
@@ -4968,7 +4968,7 @@ static bool wcd9335_is_volatile_register(struct device *dev, unsigned int reg)
static struct regmap_config wcd9335_regmap_config = {
.reg_bits = 16,
.val_bits = 8,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.max_register = WCD9335_MAX_REGISTER,
.can_multi_write = true,
.ranges = wcd9335_ranges,
diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c
index c0d1fa36d841..1b6e376f3833 100644
--- a/sound/soc/codecs/wcd934x.c
+++ b/sound/soc/codecs/wcd934x.c
@@ -2642,7 +2642,7 @@ static int wcd934x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *compon
return rc;
}
-static inline void wcd934x_mbhc_get_result_params(struct wcd934x_codec *wcd934x,
+static void wcd934x_mbhc_get_result_params(struct wcd934x_codec *wcd934x,
s16 *d1_a, u16 noff,
int32_t *zdet)
{
@@ -2683,7 +2683,7 @@ static inline void wcd934x_mbhc_get_result_params(struct wcd934x_codec *wcd934x,
else if (x1 < minCode_param[noff])
*zdet = WCD934X_ZDET_FLOATING_IMPEDANCE;
- dev_info(wcd934x->dev, "%s: d1=%d, c1=%d, x1=0x%x, z_val=%d(milliOhm)\n",
+ dev_dbg(wcd934x->dev, "%s: d1=%d, c1=%d, x1=0x%x, z_val=%di (milliohm)\n",
__func__, d1, c1, x1, *zdet);
ramp_down:
i = 0;
@@ -2740,8 +2740,8 @@ z_right:
*zr = zdet;
}
-static inline void wcd934x_wcd_mbhc_qfuse_cal(struct snd_soc_component *component,
- int32_t *z_val, int flag_l_r)
+static void wcd934x_wcd_mbhc_qfuse_cal(struct snd_soc_component *component,
+ int32_t *z_val, int flag_l_r)
{
s16 q1;
int q1_cal;
@@ -3044,6 +3044,17 @@ static int wcd934x_mbhc_init(struct snd_soc_component *component)
return 0;
}
+
+static void wcd934x_mbhc_deinit(struct snd_soc_component *component)
+{
+ struct wcd934x_codec *wcd = snd_soc_component_get_drvdata(component);
+
+ if (!wcd->mbhc)
+ return;
+
+ wcd_mbhc_deinit(wcd->mbhc);
+}
+
static int wcd934x_comp_probe(struct snd_soc_component *component)
{
struct wcd934x_codec *wcd = dev_get_drvdata(component->dev);
@@ -3077,6 +3088,7 @@ static void wcd934x_comp_remove(struct snd_soc_component *comp)
{
struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev);
+ wcd934x_mbhc_deinit(comp);
wcd_clsh_ctrl_free(wcd->clsh_ctrl);
}
diff --git a/sound/soc/codecs/wcd938x-sdw.c b/sound/soc/codecs/wcd938x-sdw.c
index bd0e9fbc12eb..6951120057e5 100644
--- a/sound/soc/codecs/wcd938x-sdw.c
+++ b/sound/soc/codecs/wcd938x-sdw.c
@@ -1183,7 +1183,7 @@ static const struct regmap_config wcd938x_regmap_config = {
.name = "wcd938x_csr",
.reg_bits = 32,
.val_bits = 8,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = wcd938x_defaults,
.num_reg_defaults = ARRAY_SIZE(wcd938x_defaults),
.max_register = WCD938X_MAX_REGISTER,
diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c
index e7d6a02cdec0..a3c680661377 100644
--- a/sound/soc/codecs/wcd938x.c
+++ b/sound/soc/codecs/wcd938x.c
@@ -210,7 +210,7 @@ struct wcd938x_priv {
};
static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(ear_pa_gain, 600, -1800);
-static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(line_gain, 600, -3000);
+static const DECLARE_TLV_DB_SCALE(line_gain, -3000, 150, -3000);
static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(analog_gain, 0, 3000);
struct wcd938x_mbhc_zdet_param {
@@ -2124,10 +2124,11 @@ static int wcd938x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *compon
return wcd938x_mbhc_micb_adjust_voltage(component, micb_mv, MIC_BIAS_2);
}
-static inline void wcd938x_mbhc_get_result_params(struct wcd938x_priv *wcd938x,
+static void wcd938x_mbhc_get_result_params(struct snd_soc_component *component,
s16 *d1_a, u16 noff,
int32_t *zdet)
{
+ struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
int i;
int val, val1;
s16 c1;
@@ -2154,8 +2155,8 @@ static inline void wcd938x_mbhc_get_result_params(struct wcd938x_priv *wcd938x,
usleep_range(5000, 5050);
if (!c1 || !x1) {
- pr_err("%s: Impedance detect ramp error, c1=%d, x1=0x%x\n",
- __func__, c1, x1);
+ dev_err(component->dev, "Impedance detect ramp error, c1=%d, x1=0x%x\n",
+ c1, x1);
goto ramp_down;
}
d1 = d1_a[c1];
@@ -2165,7 +2166,7 @@ static inline void wcd938x_mbhc_get_result_params(struct wcd938x_priv *wcd938x,
else if (x1 < minCode_param[noff])
*zdet = WCD938X_ZDET_FLOATING_IMPEDANCE;
- pr_err("%s: d1=%d, c1=%d, x1=0x%x, z_val=%d(milliOhm)\n",
+ dev_dbg(component->dev, "%s: d1=%d, c1=%d, x1=0x%x, z_val=%d (milliohm)\n",
__func__, d1, c1, x1, *zdet);
ramp_down:
i = 0;
@@ -2210,7 +2211,7 @@ static void wcd938x_mbhc_zdet_ramp(struct snd_soc_component *component,
WCD938X_ANA_MBHC_ZDET, 0x80, 0x80);
dev_dbg(component->dev, "%s: ramp for HPH_L, noff = %d\n",
__func__, zdet_param->noff);
- wcd938x_mbhc_get_result_params(wcd938x, d1_a, zdet_param->noff, &zdet);
+ wcd938x_mbhc_get_result_params(component, d1_a, zdet_param->noff, &zdet);
regmap_update_bits(wcd938x->regmap,
WCD938X_ANA_MBHC_ZDET, 0x80, 0x00);
@@ -2224,15 +2225,15 @@ z_right:
WCD938X_ANA_MBHC_ZDET, 0x40, 0x40);
dev_dbg(component->dev, "%s: ramp for HPH_R, noff = %d\n",
__func__, zdet_param->noff);
- wcd938x_mbhc_get_result_params(wcd938x, d1_a, zdet_param->noff, &zdet);
+ wcd938x_mbhc_get_result_params(component, d1_a, zdet_param->noff, &zdet);
regmap_update_bits(wcd938x->regmap,
WCD938X_ANA_MBHC_ZDET, 0x40, 0x00);
*zr = zdet;
}
-static inline void wcd938x_wcd_mbhc_qfuse_cal(struct snd_soc_component *component,
- int32_t *z_val, int flag_l_r)
+static void wcd938x_wcd_mbhc_qfuse_cal(struct snd_soc_component *component,
+ int32_t *z_val, int flag_l_r)
{
s16 q1;
int q1_cal;
@@ -2625,6 +2626,8 @@ static int wcd938x_mbhc_init(struct snd_soc_component *component)
WCD938X_IRQ_HPHR_OCP_INT);
wcd938x->wcd_mbhc = wcd_mbhc_init(component, &mbhc_cb, intr_ids, wcd_mbhc_fields, true);
+ if (IS_ERR(wcd938x->wcd_mbhc))
+ return PTR_ERR(wcd938x->wcd_mbhc);
snd_soc_add_component_controls(component, impedance_detect_controls,
ARRAY_SIZE(impedance_detect_controls));
@@ -2633,6 +2636,14 @@ static int wcd938x_mbhc_init(struct snd_soc_component *component)
return 0;
}
+
+static void wcd938x_mbhc_deinit(struct snd_soc_component *component)
+{
+ struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
+
+ wcd_mbhc_deinit(wcd938x->wcd_mbhc);
+}
+
/* END MBHC */
static const struct snd_kcontrol_new wcd938x_snd_controls[] = {
@@ -2652,8 +2663,8 @@ static const struct snd_kcontrol_new wcd938x_snd_controls[] = {
wcd938x_get_swr_port, wcd938x_set_swr_port),
SOC_SINGLE_EXT("DSD_R Switch", WCD938X_DSD_R, 0, 1, 0,
wcd938x_get_swr_port, wcd938x_set_swr_port),
- SOC_SINGLE_TLV("HPHL Volume", WCD938X_HPH_L_EN, 0, 0x18, 0, line_gain),
- SOC_SINGLE_TLV("HPHR Volume", WCD938X_HPH_R_EN, 0, 0x18, 0, line_gain),
+ SOC_SINGLE_TLV("HPHL Volume", WCD938X_HPH_L_EN, 0, 0x18, 1, line_gain),
+ SOC_SINGLE_TLV("HPHR Volume", WCD938X_HPH_R_EN, 0, 0x18, 1, line_gain),
WCD938X_EAR_PA_GAIN_TLV("EAR_PA Volume", WCD938X_ANA_EAR_COMPANDER_CTL,
2, 0x10, 0, ear_pa_gain),
SOC_SINGLE_EXT("ADC1 Switch", WCD938X_ADC1, 1, 1, 0,
@@ -3080,16 +3091,33 @@ static int wcd938x_irq_init(struct wcd938x_priv *wcd, struct device *dev)
static int wcd938x_soc_codec_probe(struct snd_soc_component *component)
{
struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
+ struct sdw_slave *tx_sdw_dev = wcd938x->tx_sdw_dev;
struct device *dev = component->dev;
+ unsigned long time_left;
int ret, i;
+ time_left = wait_for_completion_timeout(&tx_sdw_dev->initialization_complete,
+ msecs_to_jiffies(2000));
+ if (!time_left) {
+ dev_err(dev, "soundwire device init timeout\n");
+ return -ETIMEDOUT;
+ }
+
snd_soc_component_init_regmap(component, wcd938x->regmap);
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0)
+ return ret;
+
wcd938x->variant = snd_soc_component_read_field(component,
WCD938X_DIGITAL_EFUSE_REG_0,
WCD938X_ID_MASK);
wcd938x->clsh_info = wcd_clsh_ctrl_alloc(component, WCD938X);
+ if (IS_ERR(wcd938x->clsh_info)) {
+ pm_runtime_put(dev);
+ return PTR_ERR(wcd938x->clsh_info);
+ }
wcd938x_io_init(wcd938x);
/* Set all interrupts as edge triggered */
@@ -3098,6 +3126,8 @@ static int wcd938x_soc_codec_probe(struct snd_soc_component *component)
(WCD938X_DIGITAL_INTR_LEVEL_0 + i), 0);
}
+ pm_runtime_put(dev);
+
wcd938x->hphr_pdm_wd_int = regmap_irq_get_virq(wcd938x->irq_chip,
WCD938X_IRQ_HPHR_PDM_WD_INT);
wcd938x->hphl_pdm_wd_int = regmap_irq_get_virq(wcd938x->irq_chip,
@@ -3109,20 +3139,26 @@ static int wcd938x_soc_codec_probe(struct snd_soc_component *component)
ret = request_threaded_irq(wcd938x->hphr_pdm_wd_int, NULL, wcd938x_wd_handle_irq,
IRQF_ONESHOT | IRQF_TRIGGER_RISING,
"HPHR PDM WD INT", wcd938x);
- if (ret)
+ if (ret) {
dev_err(dev, "Failed to request HPHR WD interrupt (%d)\n", ret);
+ goto err_free_clsh_ctrl;
+ }
ret = request_threaded_irq(wcd938x->hphl_pdm_wd_int, NULL, wcd938x_wd_handle_irq,
IRQF_ONESHOT | IRQF_TRIGGER_RISING,
"HPHL PDM WD INT", wcd938x);
- if (ret)
+ if (ret) {
dev_err(dev, "Failed to request HPHL WD interrupt (%d)\n", ret);
+ goto err_free_hphr_pdm_wd_int;
+ }
ret = request_threaded_irq(wcd938x->aux_pdm_wd_int, NULL, wcd938x_wd_handle_irq,
IRQF_ONESHOT | IRQF_TRIGGER_RISING,
"AUX PDM WD INT", wcd938x);
- if (ret)
+ if (ret) {
dev_err(dev, "Failed to request Aux WD interrupt (%d)\n", ret);
+ goto err_free_hphl_pdm_wd_int;
+ }
/* Disable watchdog interrupt for HPH and AUX */
disable_irq_nosync(wcd938x->hphr_pdm_wd_int);
@@ -3137,7 +3173,7 @@ static int wcd938x_soc_codec_probe(struct snd_soc_component *component)
dev_err(component->dev,
"%s: Failed to add snd ctrls for variant: %d\n",
__func__, wcd938x->variant);
- goto err;
+ goto err_free_aux_pdm_wd_int;
}
break;
case WCD9385:
@@ -3147,7 +3183,7 @@ static int wcd938x_soc_codec_probe(struct snd_soc_component *component)
dev_err(component->dev,
"%s: Failed to add snd ctrls for variant: %d\n",
__func__, wcd938x->variant);
- goto err;
+ goto err_free_aux_pdm_wd_int;
}
break;
default:
@@ -3155,12 +3191,38 @@ static int wcd938x_soc_codec_probe(struct snd_soc_component *component)
}
ret = wcd938x_mbhc_init(component);
- if (ret)
+ if (ret) {
dev_err(component->dev, "mbhc initialization failed\n");
-err:
+ goto err_free_aux_pdm_wd_int;
+ }
+
+ return 0;
+
+err_free_aux_pdm_wd_int:
+ free_irq(wcd938x->aux_pdm_wd_int, wcd938x);
+err_free_hphl_pdm_wd_int:
+ free_irq(wcd938x->hphl_pdm_wd_int, wcd938x);
+err_free_hphr_pdm_wd_int:
+ free_irq(wcd938x->hphr_pdm_wd_int, wcd938x);
+err_free_clsh_ctrl:
+ wcd_clsh_ctrl_free(wcd938x->clsh_info);
+
return ret;
}
+static void wcd938x_soc_codec_remove(struct snd_soc_component *component)
+{
+ struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
+
+ wcd938x_mbhc_deinit(component);
+
+ free_irq(wcd938x->aux_pdm_wd_int, wcd938x);
+ free_irq(wcd938x->hphl_pdm_wd_int, wcd938x);
+ free_irq(wcd938x->hphr_pdm_wd_int, wcd938x);
+
+ wcd_clsh_ctrl_free(wcd938x->clsh_info);
+}
+
static int wcd938x_codec_set_jack(struct snd_soc_component *comp,
struct snd_soc_jack *jack, void *data)
{
@@ -3177,6 +3239,7 @@ static int wcd938x_codec_set_jack(struct snd_soc_component *comp,
static const struct snd_soc_component_driver soc_codec_dev_wcd938x = {
.name = "wcd938x_codec",
.probe = wcd938x_soc_codec_probe,
+ .remove = wcd938x_soc_codec_remove,
.controls = wcd938x_snd_controls,
.num_controls = ARRAY_SIZE(wcd938x_snd_controls),
.dapm_widgets = wcd938x_dapm_widgets,
diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c
index 277b8c468c78..36cdf97993a5 100644
--- a/sound/soc/codecs/wm2200.c
+++ b/sound/soc/codecs/wm2200.c
@@ -2151,7 +2151,7 @@ static const struct regmap_config wm2200_regmap = {
.num_reg_defaults = ARRAY_SIZE(wm2200_reg_defaults),
.volatile_reg = wm2200_volatile_register,
.readable_reg = wm2200_readable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.ranges = wm2200_ranges,
.num_ranges = ARRAY_SIZE(wm2200_ranges),
};
diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c
index a86eacb2a9bb..ff63723928a1 100644
--- a/sound/soc/codecs/wm5100.c
+++ b/sound/soc/codecs/wm5100.c
@@ -2400,7 +2400,7 @@ static const struct regmap_config wm5100_regmap = {
.num_reg_defaults = ARRAY_SIZE(wm5100_reg_defaults),
.volatile_reg = wm5100_volatile_register,
.readable_reg = wm5100_readable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static const unsigned int wm5100_mic_ctrl_reg[] = {
diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c
index c0ed76d5b65f..6636a70f3895 100644
--- a/sound/soc/codecs/wm8510.c
+++ b/sound/soc/codecs/wm8510.c
@@ -607,7 +607,7 @@ static const struct regmap_config wm8510_regmap = {
.reg_defaults = wm8510_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8510_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.volatile_reg = wm8510_volatile,
};
diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c
index 55c72c5ac845..ea87cd3cc0d6 100644
--- a/sound/soc/codecs/wm8523.c
+++ b/sound/soc/codecs/wm8523.c
@@ -437,7 +437,7 @@ static const struct regmap_config wm8523_regmap = {
.reg_defaults = wm8523_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8523_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.volatile_reg = wm8523_volatile_register,
};
diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c
index 34ae7fe05398..6d22f7d40ec2 100644
--- a/sound/soc/codecs/wm8580.c
+++ b/sound/soc/codecs/wm8580.c
@@ -975,7 +975,7 @@ static const struct regmap_config wm8580_regmap = {
.reg_defaults = wm8580_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8580_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.volatile_reg = wm8580_volatile,
};
diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c
index 903a0147d584..916f297164de 100644
--- a/sound/soc/codecs/wm8711.c
+++ b/sound/soc/codecs/wm8711.c
@@ -393,7 +393,7 @@ static const struct regmap_config wm8711_regmap = {
.reg_defaults = wm8711_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8711_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.volatile_reg = wm8711_volatile,
};
diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c
index 5ea6d8fd10f6..0c943e7d4159 100644
--- a/sound/soc/codecs/wm8728.c
+++ b/sound/soc/codecs/wm8728.c
@@ -236,7 +236,7 @@ static const struct regmap_config wm8728_regmap = {
.reg_defaults = wm8728_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8728_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
#if defined(CONFIG_SPI_MASTER)
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
index d5ab3ba126a6..efc160c75f40 100644
--- a/sound/soc/codecs/wm8731.c
+++ b/sound/soc/codecs/wm8731.c
@@ -642,7 +642,7 @@ const struct regmap_config wm8731_regmap = {
.max_register = WM8731_RESET,
.volatile_reg = wm8731_volatile,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = wm8731_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8731_reg_defaults),
};
diff --git a/sound/soc/codecs/wm8737.c b/sound/soc/codecs/wm8737.c
index 9f4e372e90ea..0d231c289ef3 100644
--- a/sound/soc/codecs/wm8737.c
+++ b/sound/soc/codecs/wm8737.c
@@ -599,7 +599,7 @@ static const struct regmap_config wm8737_regmap = {
.reg_defaults = wm8737_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8737_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.volatile_reg = wm8737_volatile,
};
diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c
index 787156b980a1..19e8fc4062c7 100644
--- a/sound/soc/codecs/wm8741.c
+++ b/sound/soc/codecs/wm8741.c
@@ -543,7 +543,7 @@ static const struct regmap_config wm8741_regmap = {
.reg_defaults = wm8741_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8741_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static int wm8741_set_pdata(struct device *dev, struct wm8741_priv *wm8741)
diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c
index 20dc9ff9fea9..2d2feaf95e49 100644
--- a/sound/soc/codecs/wm8750.c
+++ b/sound/soc/codecs/wm8750.c
@@ -735,7 +735,7 @@ static const struct regmap_config wm8750_regmap = {
.reg_defaults = wm8750_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8750_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
#if defined(CONFIG_SPI_MASTER)
diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c
index 5e8a8eb41b2b..b5d8290c37d9 100644
--- a/sound/soc/codecs/wm8753.c
+++ b/sound/soc/codecs/wm8753.c
@@ -1507,7 +1507,7 @@ static const struct regmap_config wm8753_regmap = {
.max_register = WM8753_ADCTL2,
.volatile_reg = wm8753_volatile,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = wm8753_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8753_reg_defaults),
};
diff --git a/sound/soc/codecs/wm8770.c b/sound/soc/codecs/wm8770.c
index e03fee8869c3..2469f4f3bea3 100644
--- a/sound/soc/codecs/wm8770.c
+++ b/sound/soc/codecs/wm8770.c
@@ -632,7 +632,7 @@ static const struct regmap_config wm8770_regmap = {
.reg_defaults = wm8770_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8770_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.volatile_reg = wm8770_volatile_reg,
};
diff --git a/sound/soc/codecs/wm8776.c b/sound/soc/codecs/wm8776.c
index 212224a68006..0673bbd32bab 100644
--- a/sound/soc/codecs/wm8776.c
+++ b/sound/soc/codecs/wm8776.c
@@ -451,7 +451,7 @@ static const struct regmap_config wm8776_regmap = {
.reg_defaults = wm8776_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8776_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.volatile_reg = wm8776_volatile,
};
diff --git a/sound/soc/codecs/wm8804.c b/sound/soc/codecs/wm8804.c
index 0b234bae480e..bbb4b6e3b41c 100644
--- a/sound/soc/codecs/wm8804.c
+++ b/sound/soc/codecs/wm8804.c
@@ -555,7 +555,7 @@ const struct regmap_config wm8804_regmap_config = {
.max_register = WM8804_MAX_REGISTER,
.volatile_reg = wm8804_volatile,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = wm8804_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8804_reg_defaults),
};
diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c
index 320ccd92f318..84d06c190411 100644
--- a/sound/soc/codecs/wm8900.c
+++ b/sound/soc/codecs/wm8900.c
@@ -1223,7 +1223,7 @@ static const struct regmap_config wm8900_regmap = {
.reg_defaults = wm8900_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8900_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.volatile_reg = wm8900_volatile_register,
};
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
index 901b65ef8de5..84ae1102ac88 100644
--- a/sound/soc/codecs/wm8903.c
+++ b/sound/soc/codecs/wm8903.c
@@ -1902,7 +1902,7 @@ static const struct regmap_config wm8903_regmap = {
.volatile_reg = wm8903_volatile_register,
.readable_reg = wm8903_readable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = wm8903_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8903_reg_defaults),
};
diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c
index 068e610b1b4c..ac4e1654a967 100644
--- a/sound/soc/codecs/wm8904.c
+++ b/sound/soc/codecs/wm8904.c
@@ -2148,7 +2148,7 @@ static const struct regmap_config wm8904_regmap = {
.volatile_reg = wm8904_volatile_register,
.readable_reg = wm8904_readable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = wm8904_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8904_reg_defaults),
};
diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c
index 53c27986d216..b9432f8b64e5 100644
--- a/sound/soc/codecs/wm8940.c
+++ b/sound/soc/codecs/wm8940.c
@@ -815,7 +815,7 @@ static const struct regmap_config wm8940_regmap = {
.max_register = WM8940_MONOMIX,
.reg_defaults = wm8940_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8940_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.readable_reg = wm8940_readable_register,
.volatile_reg = wm8940_volatile_register,
diff --git a/sound/soc/codecs/wm8955.c b/sound/soc/codecs/wm8955.c
index 78044f580a67..4f4338326438 100644
--- a/sound/soc/codecs/wm8955.c
+++ b/sound/soc/codecs/wm8955.c
@@ -962,7 +962,7 @@ static const struct regmap_config wm8955_regmap = {
.volatile_reg = wm8955_volatile,
.writeable_reg = wm8955_writeable,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = wm8955_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8955_reg_defaults),
};
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c
index 366f5d769d6d..c2bd9ef41ebb 100644
--- a/sound/soc/codecs/wm8960.c
+++ b/sound/soc/codecs/wm8960.c
@@ -155,6 +155,7 @@ static const char *wm8960_adc_data_output_sel[] = {
"Left Data = Right ADC; Right Data = Left ADC",
};
static const char *wm8960_dmonomix[] = {"Stereo", "Mono"};
+static const char *wm8960_dacslope[] = {"Normal", "Sloping"};
static const struct soc_enum wm8960_enum[] = {
SOC_ENUM_SINGLE(WM8960_DACCTL1, 5, 4, wm8960_polarity),
@@ -165,6 +166,7 @@ static const struct soc_enum wm8960_enum[] = {
SOC_ENUM_SINGLE(WM8960_ALC3, 8, 2, wm8960_alcmode),
SOC_ENUM_SINGLE(WM8960_ADDCTL1, 2, 4, wm8960_adc_data_output_sel),
SOC_ENUM_SINGLE(WM8960_ADDCTL1, 4, 2, wm8960_dmonomix),
+ SOC_ENUM_SINGLE(WM8960_DACCTL2, 1, 2, wm8960_dacslope),
};
static const int deemph_settings[] = { 0, 32000, 44100, 48000 };
@@ -307,6 +309,7 @@ SOC_SINGLE_TLV("Right Output Mixer RINPUT3 Volume",
SOC_ENUM("ADC Data Output Select", wm8960_enum[6]),
SOC_ENUM("DAC Mono Mix", wm8960_enum[7]),
+SOC_ENUM("DAC Filter Characteristics", wm8960_enum[8]),
};
static const struct snd_kcontrol_new wm8960_lin_boost[] = {
@@ -1387,7 +1390,7 @@ static const struct regmap_config wm8960_regmap = {
.reg_defaults = wm8960_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8960_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.volatile_reg = wm8960_volatile,
};
@@ -1415,6 +1418,7 @@ static int wm8960_i2c_probe(struct i2c_client *i2c)
struct wm8960_data *pdata = dev_get_platdata(&i2c->dev);
struct wm8960_priv *wm8960;
int ret;
+ u8 val;
wm8960 = devm_kzalloc(&i2c->dev, sizeof(struct wm8960_priv),
GFP_KERNEL);
@@ -1436,6 +1440,12 @@ static int wm8960_i2c_probe(struct i2c_client *i2c)
else if (i2c->dev.of_node)
wm8960_set_pdata_from_of(i2c, &wm8960->pdata);
+ ret = i2c_master_recv(i2c, &val, sizeof(val));
+ if (ret >= 0) {
+ dev_err(&i2c->dev, "Not wm8960, wm8960 reg can not read by i2c\n");
+ return -EINVAL;
+ }
+
ret = wm8960_reset(wm8960->regmap);
if (ret != 0) {
dev_err(&i2c->dev, "Failed to issue reset\n");
diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c
index c076f78d04ce..8f8330efb341 100644
--- a/sound/soc/codecs/wm8961.c
+++ b/sound/soc/codecs/wm8961.c
@@ -904,7 +904,7 @@ static const struct regmap_config wm8961_regmap = {
.reg_defaults = wm8961_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8961_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.volatile_reg = wm8961_volatile,
.readable_reg = wm8961_readable,
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
index 68ea15be7330..83ce5dbecc45 100644
--- a/sound/soc/codecs/wm8962.c
+++ b/sound/soc/codecs/wm8962.c
@@ -3573,7 +3573,7 @@ static const struct regmap_config wm8962_regmap = {
.num_reg_defaults = ARRAY_SIZE(wm8962_reg),
.volatile_reg = wm8962_volatile_register,
.readable_reg = wm8962_readable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static int wm8962_set_pdata_from_of(struct i2c_client *i2c,
diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c
index b22d8f0b59be..e88f323d28b2 100644
--- a/sound/soc/codecs/wm8971.c
+++ b/sound/soc/codecs/wm8971.c
@@ -668,7 +668,7 @@ static const struct regmap_config wm8971_regmap = {
.reg_defaults = wm8971_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8971_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static int wm8971_i2c_probe(struct i2c_client *i2c)
diff --git a/sound/soc/codecs/wm8978.c b/sound/soc/codecs/wm8978.c
index 5c829301cf4c..718bfef302cc 100644
--- a/sound/soc/codecs/wm8978.c
+++ b/sound/soc/codecs/wm8978.c
@@ -1014,7 +1014,7 @@ static const struct regmap_config wm8978_regmap_config = {
.max_register = WM8978_MAX_REGISTER,
.volatile_reg = wm8978_volatile,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = wm8978_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8978_reg_defaults),
};
diff --git a/sound/soc/codecs/wm8983.c b/sound/soc/codecs/wm8983.c
index 2bd26e2478d9..b26d6a68e8d2 100644
--- a/sound/soc/codecs/wm8983.c
+++ b/sound/soc/codecs/wm8983.c
@@ -995,7 +995,7 @@ static const struct regmap_config wm8983_regmap = {
.reg_defaults = wm8983_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8983_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.max_register = WM8983_MAX_REGISTER,
.writeable_reg = wm8983_writeable,
diff --git a/sound/soc/codecs/wm8985.c b/sound/soc/codecs/wm8985.c
index c0816bcfa294..8606e0752a60 100644
--- a/sound/soc/codecs/wm8985.c
+++ b/sound/soc/codecs/wm8985.c
@@ -1125,7 +1125,7 @@ static const struct regmap_config wm8985_regmap = {
.max_register = WM8985_MAX_REGISTER,
.writeable_reg = wm8985_writeable,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = wm8985_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8985_reg_defaults),
};
diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c
index b440719cca7d..76f214f12ce0 100644
--- a/sound/soc/codecs/wm8988.c
+++ b/sound/soc/codecs/wm8988.c
@@ -832,7 +832,7 @@ static const struct regmap_config wm8988_regmap = {
.max_register = WM8988_LPPB,
.writeable_reg = wm8988_writeable,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = wm8988_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8988_reg_defaults),
};
diff --git a/sound/soc/codecs/wm8991.c b/sound/soc/codecs/wm8991.c
index 8cb2ae829699..590318aafaea 100644
--- a/sound/soc/codecs/wm8991.c
+++ b/sound/soc/codecs/wm8991.c
@@ -1253,7 +1253,7 @@ static const struct regmap_config wm8991_regmap = {
.volatile_reg = wm8991_volatile,
.reg_defaults = wm8991_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8991_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static int wm8991_i2c_probe(struct i2c_client *i2c)
diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c
index feb997c698e2..5b788f35e5e4 100644
--- a/sound/soc/codecs/wm8993.c
+++ b/sound/soc/codecs/wm8993.c
@@ -1608,7 +1608,7 @@ static const struct regmap_config wm8993_regmap = {
.volatile_reg = wm8993_volatile,
.readable_reg = wm8993_readable,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = wm8993_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8993_reg_defaults),
};
diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c
index 90588614edcc..4ffa1896faab 100644
--- a/sound/soc/codecs/wm8995.c
+++ b/sound/soc/codecs/wm8995.c
@@ -2193,7 +2193,7 @@ static const struct regmap_config wm8995_regmap = {
.num_reg_defaults = ARRAY_SIZE(wm8995_reg_defaults),
.volatile_reg = wm8995_volatile,
.readable_reg = wm8995_readable,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
#if defined(CONFIG_SPI_MASTER)
diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c
index 5d0eb0ae0475..df6195778c57 100644
--- a/sound/soc/codecs/wm8996.c
+++ b/sound/soc/codecs/wm8996.c
@@ -2610,7 +2610,7 @@ static const struct regmap_config wm8996_regmap = {
.num_reg_defaults = ARRAY_SIZE(wm8996_reg),
.volatile_reg = wm8996_volatile_register,
.readable_reg = wm8996_readable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static int wm8996_probe(struct snd_soc_component *component)
diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c
index 34a07db7342a..e7ec799573d3 100644
--- a/sound/soc/codecs/wm9081.c
+++ b/sound/soc/codecs/wm9081.c
@@ -1295,7 +1295,7 @@ static const struct regmap_config wm9081_regmap = {
.num_reg_defaults = ARRAY_SIZE(wm9081_reg),
.volatile_reg = wm9081_volatile_register,
.readable_reg = wm9081_readable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static int wm9081_i2c_probe(struct i2c_client *i2c)
diff --git a/sound/soc/codecs/wm9090.c b/sound/soc/codecs/wm9090.c
index 432729c753dd..50c1cbccfdb9 100644
--- a/sound/soc/codecs/wm9090.c
+++ b/sound/soc/codecs/wm9090.c
@@ -553,7 +553,7 @@ static const struct regmap_config wm9090_regmap = {
.volatile_reg = wm9090_volatile,
.readable_reg = wm9090_readable,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = wm9090_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm9090_reg_defaults),
};
diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c
index d04902ef1d5f..5c6aebe29cf1 100644
--- a/sound/soc/codecs/wm9705.c
+++ b/sound/soc/codecs/wm9705.c
@@ -64,7 +64,7 @@ static const struct regmap_config wm9705_regmap_config = {
.reg_stride = 2,
.val_bits = 16,
.max_register = 0x7e,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.volatile_reg = regmap_ac97_default_volatile,
diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c
index df9b7980706b..e63921de0c37 100644
--- a/sound/soc/codecs/wm9712.c
+++ b/sound/soc/codecs/wm9712.c
@@ -86,7 +86,7 @@ static const struct regmap_config wm9712_regmap_config = {
.reg_stride = 2,
.val_bits = 16,
.max_register = 0x7e,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.volatile_reg = wm9712_volatile_reg,
diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c
index 5d2e54e06e30..64b69316e4c7 100644
--- a/sound/soc/codecs/wm9713.c
+++ b/sound/soc/codecs/wm9713.c
@@ -727,7 +727,7 @@ static const struct regmap_config wm9713_regmap_config = {
.reg_stride = 2,
.val_bits = 16,
.max_register = 0x7e,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = wm9713_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm9713_reg_defaults),
diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c
index 97f6873a0a8c..3c025dabaf7a 100644
--- a/sound/soc/codecs/wsa881x.c
+++ b/sound/soc/codecs/wsa881x.c
@@ -637,7 +637,7 @@ static bool wsa881x_volatile_register(struct device *dev, unsigned int reg)
static struct regmap_config wsa881x_regmap_config = {
.reg_bits = 32,
.val_bits = 8,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = wsa881x_defaults,
.max_register = WSA881X_SPKR_STATUS3,
.num_reg_defaults = ARRAY_SIZE(wsa881x_defaults),
diff --git a/sound/soc/codecs/wsa883x.c b/sound/soc/codecs/wsa883x.c
index e40d583a1ce6..197fae23762f 100644
--- a/sound/soc/codecs/wsa883x.c
+++ b/sound/soc/codecs/wsa883x.c
@@ -938,7 +938,7 @@ static bool wsa883x_volatile_register(struct device *dev, unsigned int reg)
static struct regmap_config wsa883x_regmap_config = {
.reg_bits = 32,
.val_bits = 8,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = wsa883x_defaults,
.max_register = WSA883X_MAX_REGISTER,
.num_reg_defaults = ARRAY_SIZE(wsa883x_defaults),
diff --git a/sound/soc/dwc/dwc-i2s.c b/sound/soc/dwc/dwc-i2s.c
index 97d652f0e84d..1f1ee14b04e6 100644
--- a/sound/soc/dwc/dwc-i2s.c
+++ b/sound/soc/dwc/dwc-i2s.c
@@ -183,7 +183,15 @@ static void i2s_start(struct dw_i2s_dev *dev,
{
struct i2s_clk_config_data *config = &dev->config;
- i2s_write_reg(dev->i2s_base, IER, 1);
+ u32 reg = IER_IEN;
+
+ if (dev->tdm_slots) {
+ reg |= (dev->tdm_slots - 1) << IER_TDM_SLOTS_SHIFT;
+ reg |= IER_INTF_TYPE;
+ reg |= dev->frame_offset << IER_FRAME_OFF_SHIFT;
+ }
+
+ i2s_write_reg(dev->i2s_base, IER, reg);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
i2s_write_reg(dev->i2s_base, ITER, 1);
@@ -233,13 +241,15 @@ static void dw_i2s_config(struct dw_i2s_dev *dev, int stream)
dev->xfer_resolution);
i2s_write_reg(dev->i2s_base, TFCR(ch_reg),
dev->fifo_th - 1);
- i2s_write_reg(dev->i2s_base, TER(ch_reg), 1);
+ i2s_write_reg(dev->i2s_base, TER(ch_reg), TER_TXCHEN |
+ dev->tdm_mask << TER_TXSLOT_SHIFT);
} else {
i2s_write_reg(dev->i2s_base, RCR(ch_reg),
dev->xfer_resolution);
i2s_write_reg(dev->i2s_base, RFCR(ch_reg),
dev->fifo_th - 1);
- i2s_write_reg(dev->i2s_base, RER(ch_reg), 1);
+ i2s_write_reg(dev->i2s_base, RER(ch_reg), RER_RXCHEN |
+ dev->tdm_mask << RER_RXSLOT_SHIFT);
}
}
@@ -276,6 +286,9 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
+ if (dev->tdm_slots)
+ config->data_width = 32;
+
config->chan_nr = params_channels(params);
switch (config->chan_nr) {
@@ -384,14 +397,58 @@ static int dw_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
ret = -EINVAL;
break;
}
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_LEFT_J:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ dev->frame_offset = 1;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ dev->frame_offset = 0;
+ break;
+ default:
+ dev_err(dev->dev, "DAI format unsupported");
+ return -EINVAL;
+ }
+
return ret;
}
+static int dw_i2s_set_tdm_slot(struct snd_soc_dai *cpu_dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
+
+ if (slot_width != 32)
+ return -EINVAL;
+
+ if (slots < 0 || slots > 16)
+ return -EINVAL;
+
+ if (rx_mask != tx_mask)
+ return -EINVAL;
+
+ if (!rx_mask)
+ return -EINVAL;
+
+ dev->tdm_slots = slots;
+ dev->tdm_mask = rx_mask;
+
+ dev->l_reg = RSLOT_TSLOT(ffs(rx_mask) - 1);
+ dev->r_reg = RSLOT_TSLOT(fls(rx_mask) - 1);
+
+ return 0;
+}
+
static const struct snd_soc_dai_ops dw_i2s_dai_ops = {
.hw_params = dw_i2s_hw_params,
.prepare = dw_i2s_prepare,
.trigger = dw_i2s_trigger,
.set_fmt = dw_i2s_set_fmt,
+ .set_tdm_slot = dw_i2s_set_tdm_slot,
};
#ifdef CONFIG_PM
@@ -726,6 +783,8 @@ static int dw_i2s_probe(struct platform_device *pdev)
if (irq >= 0) {
ret = dw_pcm_register(pdev);
dev->use_pio = true;
+ dev->l_reg = LRBR_LTHR(0);
+ dev->r_reg = RRBR_RTHR(0);
} else {
ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL,
0);
diff --git a/sound/soc/dwc/dwc-pcm.c b/sound/soc/dwc/dwc-pcm.c
index 9f25631d43d3..f99262b89008 100644
--- a/sound/soc/dwc/dwc-pcm.c
+++ b/sound/soc/dwc/dwc-pcm.c
@@ -31,8 +31,8 @@ static unsigned int dw_pcm_tx_##sample_bits(struct dw_i2s_dev *dev, \
int i; \
\
for (i = 0; i < dev->fifo_th; i++) { \
- iowrite32(p[tx_ptr][0], dev->i2s_base + LRBR_LTHR(0)); \
- iowrite32(p[tx_ptr][1], dev->i2s_base + RRBR_RTHR(0)); \
+ iowrite32(p[tx_ptr][0], dev->i2s_base + dev->l_reg); \
+ iowrite32(p[tx_ptr][1], dev->i2s_base + dev->r_reg); \
period_pos++; \
if (++tx_ptr >= runtime->buffer_size) \
tx_ptr = 0; \
@@ -51,8 +51,8 @@ static unsigned int dw_pcm_rx_##sample_bits(struct dw_i2s_dev *dev, \
int i; \
\
for (i = 0; i < dev->fifo_th; i++) { \
- p[rx_ptr][0] = ioread32(dev->i2s_base + LRBR_LTHR(0)); \
- p[rx_ptr][1] = ioread32(dev->i2s_base + RRBR_RTHR(0)); \
+ p[rx_ptr][0] = ioread32(dev->i2s_base + dev->l_reg); \
+ p[rx_ptr][1] = ioread32(dev->i2s_base + dev->r_reg); \
period_pos++; \
if (++rx_ptr >= runtime->buffer_size) \
rx_ptr = 0; \
diff --git a/sound/soc/dwc/local.h b/sound/soc/dwc/local.h
index ba4e397099be..4ce96bac2f39 100644
--- a/sound/soc/dwc/local.h
+++ b/sound/soc/dwc/local.h
@@ -25,6 +25,13 @@
#define RXFFR 0x014
#define TXFFR 0x018
+/* Enable register fields */
+#define IER_TDM_SLOTS_SHIFT 8
+#define IER_FRAME_OFF_SHIFT 5
+#define IER_FRAME_OFF BIT(5)
+#define IER_INTF_TYPE BIT(1)
+#define IER_IEN BIT(0)
+
/* Interrupt status register fields */
#define ISR_TXFO BIT(5)
#define ISR_TXFE BIT(4)
@@ -46,6 +53,15 @@
#define TFCR(x) (0x40 * x + 0x04C)
#define RFF(x) (0x40 * x + 0x050)
#define TFF(x) (0x40 * x + 0x054)
+#define RSLOT_TSLOT(x) (0x4 * (x) + 0x224)
+
+/* Receive enable register fields */
+#define RER_RXSLOT_SHIFT 8
+#define RER_RXCHEN BIT(0)
+
+/* Transmit enable register fields */
+#define TER_TXSLOT_SHIFT 8
+#define TER_TXCHEN BIT(0)
/* I2SCOMPRegisters */
#define I2S_COMP_PARAM_2 0x01F0
@@ -105,6 +121,8 @@ struct dw_i2s_dev {
u32 ccr;
u32 xfer_resolution;
u32 fifo_th;
+ u32 l_reg;
+ u32 r_reg;
/* data related to DMA transfers b/w i2s and DMAC */
union dw_i2s_snd_dma_data play_dma_data;
@@ -114,6 +132,12 @@ struct dw_i2s_dev {
/* data related to PIO transfers */
bool use_pio;
+
+ /* data related to TDM mode */
+ u32 tdm_slots;
+ u32 tdm_mask;
+ u32 frame_offset;
+
struct snd_pcm_substream __rcu *tx_substream;
struct snd_pcm_substream __rcu *rx_substream;
unsigned int (*tx_fn)(struct dw_i2s_dev *dev,
diff --git a/sound/soc/fsl/fsl_rpmsg.c b/sound/soc/fsl/fsl_rpmsg.c
index 15b48b5ea856..abe19a8a7aa7 100644
--- a/sound/soc/fsl/fsl_rpmsg.c
+++ b/sound/soc/fsl/fsl_rpmsg.c
@@ -170,12 +170,20 @@ static const struct fsl_rpmsg_soc_data imx8mp_data = {
SNDRV_PCM_FMTBIT_S32_LE,
};
+static const struct fsl_rpmsg_soc_data imx93_data = {
+ .rates = SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+};
+
static const struct of_device_id fsl_rpmsg_ids[] = {
{ .compatible = "fsl,imx7ulp-rpmsg-audio", .data = &imx7ulp_data},
{ .compatible = "fsl,imx8mm-rpmsg-audio", .data = &imx8mm_data},
{ .compatible = "fsl,imx8mn-rpmsg-audio", .data = &imx8mn_data},
{ .compatible = "fsl,imx8mp-rpmsg-audio", .data = &imx8mp_data},
{ .compatible = "fsl,imx8ulp-rpmsg-audio", .data = &imx7ulp_data},
+ { .compatible = "fsl,imx93-rpmsg-audio", .data = &imx93_data},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, fsl_rpmsg_ids);
diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c
index 5e09f634c61b..f7676d30c82f 100644
--- a/sound/soc/fsl/fsl_sai.c
+++ b/sound/soc/fsl/fsl_sai.c
@@ -507,12 +507,6 @@ static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq)
savediv / 2 - 1);
}
- if (sai->soc_data->max_register >= FSL_SAI_MCTL) {
- /* SAI is in master mode at this point, so enable MCLK */
- regmap_update_bits(sai->regmap, FSL_SAI_MCTL,
- FSL_SAI_MCTL_MCLK_EN, FSL_SAI_MCTL_MCLK_EN);
- }
-
return 0;
}
@@ -719,7 +713,7 @@ static void fsl_sai_config_disable(struct fsl_sai *sai, int dir)
u32 xcsr, count = 100;
regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs),
- FSL_SAI_CSR_TERE, 0);
+ FSL_SAI_CSR_TERE | FSL_SAI_CSR_BCE, 0);
/* TERE will remain set till the end of current frame */
do {
diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h
index 8254c3547b87..550df87b6a06 100644
--- a/sound/soc/fsl/fsl_sai.h
+++ b/sound/soc/fsl/fsl_sai.h
@@ -91,6 +91,7 @@
/* SAI Transmit/Receive Control Register */
#define FSL_SAI_CSR_TERE BIT(31)
#define FSL_SAI_CSR_SE BIT(30)
+#define FSL_SAI_CSR_BCE BIT(28)
#define FSL_SAI_CSR_FR BIT(25)
#define FSL_SAI_CSR_SR BIT(24)
#define FSL_SAI_CSR_xF_SHIFT 16
diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c
index 015c3708aa04..95e639711eba 100644
--- a/sound/soc/fsl/fsl_spdif.c
+++ b/sound/soc/fsl/fsl_spdif.c
@@ -514,6 +514,10 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
int ret;
switch (sample_rate) {
+ case 22050:
+ rate = SPDIF_TXRATE_22050;
+ csfs = IEC958_AES3_CON_FS_22050;
+ break;
case 32000:
rate = SPDIF_TXRATE_32000;
csfs = IEC958_AES3_CON_FS_32000;
@@ -1422,7 +1426,7 @@ static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv,
struct clk *clk, u64 savesub,
enum spdif_txrate index, bool round)
{
- static const u32 rate[] = { 32000, 44100, 48000, 88200, 96000, 176400,
+ static const u32 rate[] = { 22050, 32000, 44100, 48000, 88200, 96000, 176400,
192000, };
bool is_sysclk = clk_is_match(clk, spdif_priv->sysclk);
u64 rate_ideal, rate_actual, sub;
@@ -1483,7 +1487,7 @@ out:
static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv,
enum spdif_txrate index)
{
- static const u32 rate[] = { 32000, 44100, 48000, 88200, 96000, 176400,
+ static const u32 rate[] = { 22050, 32000, 44100, 48000, 88200, 96000, 176400,
192000, };
struct platform_device *pdev = spdif_priv->pdev;
struct device *dev = &pdev->dev;
diff --git a/sound/soc/fsl/fsl_spdif.h b/sound/soc/fsl/fsl_spdif.h
index 75b42a692c90..2bc1b10c17d4 100644
--- a/sound/soc/fsl/fsl_spdif.h
+++ b/sound/soc/fsl/fsl_spdif.h
@@ -175,7 +175,8 @@ enum spdif_gainsel {
/* SPDIF tx rate */
enum spdif_txrate {
- SPDIF_TXRATE_32000 = 0,
+ SPDIF_TXRATE_22050 = 0,
+ SPDIF_TXRATE_32000,
SPDIF_TXRATE_44100,
SPDIF_TXRATE_48000,
SPDIF_TXRATE_88200,
@@ -191,7 +192,8 @@ enum spdif_txrate {
#define SPDIF_QSUB_SIZE (SPDIF_UBITS_SIZE / 8)
-#define FSL_SPDIF_RATES_PLAYBACK (SNDRV_PCM_RATE_32000 | \
+#define FSL_SPDIF_RATES_PLAYBACK (SNDRV_PCM_RATE_22050 | \
+ SNDRV_PCM_RATE_32000 | \
SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_48000 | \
SNDRV_PCM_RATE_88200 | \
diff --git a/sound/soc/fsl/imx-pcm-rpmsg.c b/sound/soc/fsl/imx-pcm-rpmsg.c
index 765dad607bf6..d63782b8bdef 100644
--- a/sound/soc/fsl/imx-pcm-rpmsg.c
+++ b/sound/soc/fsl/imx-pcm-rpmsg.c
@@ -228,6 +228,10 @@ static int imx_rpmsg_pcm_open(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct rpmsg_info *info = dev_get_drvdata(component->dev);
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+ struct fsl_rpmsg *rpmsg = dev_get_drvdata(cpu_dai->dev);
+ struct snd_pcm_hardware pcm_hardware;
struct rpmsg_msg *msg;
int ret = 0;
int cmd;
@@ -255,10 +259,11 @@ static int imx_rpmsg_pcm_open(struct snd_soc_component *component,
info->send_message(msg, info);
- imx_rpmsg_pcm_hardware.period_bytes_max =
- imx_rpmsg_pcm_hardware.buffer_bytes_max / 2;
+ pcm_hardware = imx_rpmsg_pcm_hardware;
+ pcm_hardware.buffer_bytes_max = rpmsg->buffer_size;
+ pcm_hardware.period_bytes_max = pcm_hardware.buffer_bytes_max / 2;
- snd_soc_set_runtime_hwparams(substream, &imx_rpmsg_pcm_hardware);
+ snd_soc_set_runtime_hwparams(substream, &pcm_hardware);
ret = snd_pcm_hw_constraint_integer(substream->runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
@@ -597,7 +602,6 @@ static int imx_rpmsg_pcm_new(struct snd_soc_component *component,
if (ret)
return ret;
- imx_rpmsg_pcm_hardware.buffer_bytes_max = rpmsg->buffer_size;
return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_WC,
pcm->card->dev, rpmsg->buffer_size);
}
diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c
index c6e0f9132193..0b8258b6bd8e 100644
--- a/sound/soc/generic/audio-graph-card.c
+++ b/sound/soc/generic/audio-graph-card.c
@@ -126,7 +126,7 @@ static int graph_parse_node(struct asoc_simple_priv *priv,
graph_parse_mclk_fs(top, ep, dai_props);
- ret = asoc_graph_parse_dai(ep, dlc, cpu);
+ ret = asoc_graph_parse_dai(dev, ep, dlc, cpu);
if (ret < 0)
return ret;
diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c
index 542c4a114940..98732468a992 100644
--- a/sound/soc/generic/audio-graph-card2.c
+++ b/sound/soc/generic/audio-graph-card2.c
@@ -407,7 +407,7 @@ static int __graph_parse_node(struct asoc_simple_priv *priv,
graph_parse_mclk_fs(ep, dai_props);
- ret = asoc_graph_parse_dai(ep, dlc, &is_single_links);
+ ret = asoc_graph_parse_dai(dev, ep, dlc, &is_single_links);
if (ret < 0)
return ret;
diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c
index 3019626b0592..5b18a4af022f 100644
--- a/sound/soc/generic/simple-card-utils.c
+++ b/sound/soc/generic/simple-card-utils.c
@@ -649,7 +649,7 @@ void asoc_simple_canonicalize_platform(struct snd_soc_dai_link_component *platfo
* simple-card.c :: simple_count_noml()
*/
if (!platforms->of_node)
- platforms->of_node = cpus->of_node;
+ snd_soc_dlc_use_cpu_as_platform(platforms, cpus);
}
EXPORT_SYMBOL_GPL(asoc_simple_canonicalize_platform);
@@ -1066,12 +1066,12 @@ static int graph_get_dai_id(struct device_node *ep)
return id;
}
-int asoc_graph_parse_dai(struct device_node *ep,
- struct snd_soc_dai_link_component *dlc,
- int *is_single_link)
+int asoc_graph_parse_dai(struct device *dev, struct device_node *ep,
+ struct snd_soc_dai_link_component *dlc, int *is_single_link)
{
struct device_node *node;
struct of_phandle_args args = {};
+ struct snd_soc_dai *dai;
int ret;
if (!ep)
@@ -1079,6 +1079,20 @@ int asoc_graph_parse_dai(struct device_node *ep,
node = of_graph_get_port_parent(ep);
+ /*
+ * Try to find from DAI node
+ */
+ args.np = ep;
+ dai = snd_soc_get_dai_via_args(&args);
+ if (dai) {
+ dlc->dai_name = snd_soc_dai_name_get(dai);
+ dlc->dai_args = snd_soc_copy_dai_args(dev, &args);
+ if (!dlc->dai_args)
+ return -ENOMEM;
+
+ goto parse_dai_end;
+ }
+
/* Get dai->name */
args.np = node;
args.args[0] = graph_get_dai_id(ep);
@@ -1109,6 +1123,7 @@ int asoc_graph_parse_dai(struct device_node *ep,
return ret;
}
+parse_dai_end:
if (is_single_link)
*is_single_link = of_graph_get_endpoint_count(node) == 1;
diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c
index 0745bf6a09aa..190f11366e84 100644
--- a/sound/soc/generic/simple-card.c
+++ b/sound/soc/generic/simple-card.c
@@ -52,11 +52,13 @@ static int asoc_simple_parse_platform(struct device_node *node,
return 0;
}
-static int asoc_simple_parse_dai(struct device_node *node,
+static int asoc_simple_parse_dai(struct device *dev,
+ struct device_node *node,
struct snd_soc_dai_link_component *dlc,
int *is_single_link)
{
struct of_phandle_args args;
+ struct snd_soc_dai *dai;
int ret;
if (!node)
@@ -71,6 +73,19 @@ static int asoc_simple_parse_dai(struct device_node *node,
return ret;
/*
+ * Try to find from DAI args
+ */
+ dai = snd_soc_get_dai_via_args(&args);
+ if (dai) {
+ dlc->dai_name = snd_soc_dai_name_get(dai);
+ dlc->dai_args = snd_soc_copy_dai_args(dev, &args);
+ if (!dlc->dai_args)
+ return -ENOMEM;
+
+ goto parse_dai_end;
+ }
+
+ /*
* FIXME
*
* Here, dlc->dai_name is pointer to CPU/Codec DAI name.
@@ -93,6 +108,7 @@ static int asoc_simple_parse_dai(struct device_node *node,
if (ret < 0)
return ret;
+parse_dai_end:
if (is_single_link)
*is_single_link = !args.args_count;
@@ -156,7 +172,7 @@ static int simple_parse_node(struct asoc_simple_priv *priv,
simple_parse_mclk_fs(top, np, dai_props, prefix);
- ret = asoc_simple_parse_dai(np, dlc, cpu);
+ ret = asoc_simple_parse_dai(dev, np, dlc, cpu);
if (ret)
return ret;
@@ -346,6 +362,7 @@ static int __simple_for_each_link(struct asoc_simple_priv *priv,
struct device *dev = simple_priv_to_dev(priv);
struct device_node *top = dev->of_node;
struct device_node *node;
+ struct device_node *add_devs;
uintptr_t dpcm_selectable = (uintptr_t)of_device_get_match_data(dev);
bool is_top = 0;
int ret = 0;
@@ -357,6 +374,8 @@ static int __simple_for_each_link(struct asoc_simple_priv *priv,
is_top = 1;
}
+ add_devs = of_get_child_by_name(top, PREFIX "additional-devs");
+
/* loop for all dai-link */
do {
struct asoc_simple_data adata;
@@ -365,6 +384,12 @@ static int __simple_for_each_link(struct asoc_simple_priv *priv,
struct device_node *np;
int num = of_get_child_count(node);
+ /* Skip additional-devs node */
+ if (node == add_devs) {
+ node = of_get_next_child(top, node);
+ continue;
+ }
+
/* get codec */
codec = of_get_child_by_name(node, is_top ?
PREFIX "codec" : "codec");
@@ -378,12 +403,15 @@ static int __simple_for_each_link(struct asoc_simple_priv *priv,
/* get convert-xxx property */
memset(&adata, 0, sizeof(adata));
- for_each_child_of_node(node, np)
+ for_each_child_of_node(node, np) {
+ if (np == add_devs)
+ continue;
simple_parse_convert(dev, np, &adata);
+ }
/* loop for all CPU/Codec node */
for_each_child_of_node(node, np) {
- if (plat == np)
+ if (plat == np || add_devs == np)
continue;
/*
* It is DPCM
@@ -426,6 +454,7 @@ static int __simple_for_each_link(struct asoc_simple_priv *priv,
} while (!is_top && node);
error:
+ of_node_put(add_devs);
of_node_put(node);
return ret;
}
@@ -463,6 +492,31 @@ static int simple_for_each_link(struct asoc_simple_priv *priv,
return ret;
}
+static void simple_depopulate_aux(void *data)
+{
+ struct asoc_simple_priv *priv = data;
+
+ of_platform_depopulate(simple_priv_to_dev(priv));
+}
+
+static int simple_populate_aux(struct asoc_simple_priv *priv)
+{
+ struct device *dev = simple_priv_to_dev(priv);
+ struct device_node *node;
+ int ret;
+
+ node = of_get_child_by_name(dev->of_node, PREFIX "additional-devs");
+ if (!node)
+ return 0;
+
+ ret = of_platform_populate(node, NULL, NULL, dev);
+ of_node_put(node);
+ if (ret)
+ return ret;
+
+ return devm_add_action_or_reset(dev, simple_depopulate_aux, priv);
+}
+
static int simple_parse_of(struct asoc_simple_priv *priv, struct link_info *li)
{
struct snd_soc_card *card = simple_priv_to_card(priv);
@@ -492,6 +546,10 @@ static int simple_parse_of(struct asoc_simple_priv *priv, struct link_info *li)
if (ret < 0)
return ret;
+ ret = simple_populate_aux(priv);
+ if (ret < 0)
+ return ret;
+
ret = snd_soc_of_parse_aux_devs(card, PREFIX "aux-devs");
return ret;
diff --git a/sound/soc/intel/avs/board_selection.c b/sound/soc/intel/avs/board_selection.c
index b32e02940e30..59a13feec57b 100644
--- a/sound/soc/intel/avs/board_selection.c
+++ b/sound/soc/intel/avs/board_selection.c
@@ -136,6 +136,14 @@ static struct snd_soc_acpi_mach avs_kbl_i2s_machines[] = {
.tplg_filename = "max98927-tplg.bin",
},
{
+ .id = "10EC5663",
+ .drv_name = "avs_rt5663",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(1),
+ },
+ .tplg_filename = "rt5663-tplg.bin",
+ },
+ {
.id = "MX98373",
.drv_name = "avs_max98373",
.mach_params = {
@@ -159,6 +167,14 @@ static struct snd_soc_acpi_mach avs_kbl_i2s_machines[] = {
},
.tplg_filename = "da7219-tplg.bin",
},
+ {
+ .id = "ESSX8336",
+ .drv_name = "avs_es8336",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0),
+ },
+ .tplg_filename = "es8336-tplg.bin",
+ },
{},
};
diff --git a/sound/soc/intel/avs/boards/Kconfig b/sound/soc/intel/avs/boards/Kconfig
index e4c230efe8d7..07353d37ecae 100644
--- a/sound/soc/intel/avs/boards/Kconfig
+++ b/sound/soc/intel/avs/boards/Kconfig
@@ -22,6 +22,16 @@ config SND_SOC_INTEL_AVS_MACH_DMIC
Say Y or m if you have such a device. This is a recommended option.
If unsure select "N".
+config SND_SOC_INTEL_AVS_MACH_ES8336
+ tristate "es8336 I2S board"
+ depends on X86 && I2C
+ depends on MFD_INTEL_LPSS || COMPILE_TEST
+ select SND_SOC_ES8316
+ help
+ This adds support for AVS with ES8336 I2S codec configuration.
+ Say Y or m if you have such a device. This is a recommended option.
+ If unsure select "N".
+
config SND_SOC_INTEL_AVS_MACH_HDAUDIO
tristate "HD-Audio generic board"
select SND_SOC_HDA
@@ -115,6 +125,16 @@ config SND_SOC_INTEL_AVS_MACH_RT298
Say Y or m if you have such a device. This is a recommended option.
If unsure select "N".
+config SND_SOC_INTEL_AVS_MACH_RT5663
+ tristate "rt5663 in I2S mode"
+ depends on I2C
+ depends on MFD_INTEL_LPSS || COMPILE_TEST
+ select SND_SOC_RT5663
+ help
+ This adds support for ASoC machine driver with RT5663 I2S audio codec.
+ Say Y or m if you have such a device. This is a recommended option.
+ If unsure select "N".
+
config SND_SOC_INTEL_AVS_MACH_RT5682
tristate "rt5682 in I2S mode"
depends on I2C
diff --git a/sound/soc/intel/avs/boards/Makefile b/sound/soc/intel/avs/boards/Makefile
index b81343420370..34347bcd1e7d 100644
--- a/sound/soc/intel/avs/boards/Makefile
+++ b/sound/soc/intel/avs/boards/Makefile
@@ -2,6 +2,7 @@
snd-soc-avs-da7219-objs := da7219.o
snd-soc-avs-dmic-objs := dmic.o
+snd-soc-avs-es8336-objs := es8336.o
snd-soc-avs-hdaudio-objs := hdaudio.o
snd-soc-avs-i2s-test-objs := i2s_test.o
snd-soc-avs-max98927-objs := max98927.o
@@ -12,11 +13,13 @@ snd-soc-avs-probe-objs := probe.o
snd-soc-avs-rt274-objs := rt274.o
snd-soc-avs-rt286-objs := rt286.o
snd-soc-avs-rt298-objs := rt298.o
+snd-soc-avs-rt5663-objs := rt5663.o
snd-soc-avs-rt5682-objs := rt5682.o
snd-soc-avs-ssm4567-objs := ssm4567.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_DA7219) += snd-soc-avs-da7219.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_DMIC) += snd-soc-avs-dmic.o
+obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_ES8336) += snd-soc-avs-es8336.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_HDAUDIO) += snd-soc-avs-hdaudio.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_I2S_TEST) += snd-soc-avs-i2s-test.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_MAX98927) += snd-soc-avs-max98927.o
@@ -27,5 +30,6 @@ obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_PROBE) += snd-soc-avs-probe.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT274) += snd-soc-avs-rt274.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT286) += snd-soc-avs-rt286.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT298) += snd-soc-avs-rt298.o
+obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT5663) += snd-soc-avs-rt5663.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT5682) += snd-soc-avs-rt5682.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_SSM4567) += snd-soc-avs-ssm4567.o
diff --git a/sound/soc/intel/avs/boards/es8336.c b/sound/soc/intel/avs/boards/es8336.c
new file mode 100644
index 000000000000..0a023f871d93
--- /dev/null
+++ b/sound/soc/intel/avs/boards/es8336.c
@@ -0,0 +1,315 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2023 Intel Corporation. All rights reserved.
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/processor.h>
+#include <linux/slab.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include <asm/intel-family.h>
+
+#define ES8336_CODEC_DAI "ES8316 HiFi"
+
+struct avs_card_drvdata {
+ struct snd_soc_jack jack;
+ struct gpio_desc *gpiod;
+};
+
+static const struct acpi_gpio_params enable_gpio = { 0, 0, true };
+
+static const struct acpi_gpio_mapping speaker_gpios[] = {
+ { "speaker-enable-gpios", &enable_gpio, 1, ACPI_GPIO_QUIRK_ONLY_GPIOIO },
+ { }
+};
+
+static int avs_es8336_speaker_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_card *card = w->dapm->card;
+ struct avs_card_drvdata *data;
+ bool speaker_en;
+
+ data = snd_soc_card_get_drvdata(card);
+ /* As enable_gpio has active_low=true, logic is inverted. */
+ speaker_en = !SND_SOC_DAPM_EVENT_ON(event);
+
+ gpiod_set_value_cansleep(data->gpiod, speaker_en);
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget card_widgets[] = {
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Internal Mic", NULL),
+
+ SND_SOC_DAPM_SUPPLY("Speaker Power", SND_SOC_NOPM, 0, 0,
+ avs_es8336_speaker_power_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+};
+
+static const struct snd_soc_dapm_route card_routes[] = {
+ {"Headphone", NULL, "HPOL"},
+ {"Headphone", NULL, "HPOR"},
+
+ /*
+ * There is no separate speaker output instead the speakers are muxed to
+ * the HP outputs. The mux is controlled by the "Speaker Power" widget.
+ */
+ {"Speaker", NULL, "HPOL"},
+ {"Speaker", NULL, "HPOR"},
+ {"Speaker", NULL, "Speaker Power"},
+
+ /* Mic route map */
+ {"MIC1", NULL, "Internal Mic"},
+ {"MIC2", NULL, "Headset Mic"},
+};
+
+static const struct snd_kcontrol_new card_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Speaker"),
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Internal Mic"),
+};
+
+static struct snd_soc_jack_pin card_headset_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static int avs_es8336_codec_init(struct snd_soc_pcm_runtime *runtime)
+{
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(runtime, 0);
+ struct snd_soc_component *component = codec_dai->component;
+ struct snd_soc_card *card = runtime->card;
+ struct snd_soc_jack_pin *pins;
+ struct avs_card_drvdata *data;
+ struct gpio_desc *gpiod;
+ int num_pins, ret;
+
+ data = snd_soc_card_get_drvdata(card);
+ num_pins = ARRAY_SIZE(card_headset_pins);
+
+ pins = devm_kmemdup(card->dev, card_headset_pins, sizeof(*pins) * num_pins, GFP_KERNEL);
+ if (!pins)
+ return -ENOMEM;
+
+ ret = snd_soc_card_jack_new_pins(card, "Headset", SND_JACK_HEADSET | SND_JACK_BTN_0,
+ &data->jack, pins, num_pins);
+ if (ret)
+ return ret;
+
+ ret = devm_acpi_dev_add_driver_gpios(codec_dai->dev, speaker_gpios);
+ if (ret)
+ dev_warn(codec_dai->dev, "Unable to add GPIO mapping table\n");
+
+ gpiod = gpiod_get_optional(codec_dai->dev, "speaker-enable", GPIOD_OUT_LOW);
+ if (IS_ERR(gpiod))
+ return dev_err_probe(codec_dai->dev, PTR_ERR(gpiod), "Get gpiod failed: %ld\n",
+ PTR_ERR(gpiod));
+
+ data->gpiod = gpiod;
+ snd_jack_set_key(data->jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_soc_component_set_jack(component, &data->jack, NULL);
+
+ card->dapm.idle_bias_off = true;
+
+ return 0;
+}
+
+static void avs_es8336_codec_exit(struct snd_soc_pcm_runtime *runtime)
+{
+ struct avs_card_drvdata *data = snd_soc_card_get_drvdata(runtime->card);
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(runtime, 0);
+
+ snd_soc_component_set_jack(codec_dai->component, NULL, NULL);
+ gpiod_put(data->gpiod);
+}
+
+static int avs_es8336_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *runtime = asoc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(runtime, 0);
+ int clk_freq;
+ int ret;
+
+ switch (boot_cpu_data.x86_model) {
+ case INTEL_FAM6_KABYLAKE_L:
+ case INTEL_FAM6_KABYLAKE:
+ clk_freq = 24000000;
+ break;
+ default:
+ clk_freq = 19200000;
+ break;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, 1, clk_freq, SND_SOC_CLOCK_OUT);
+ if (ret < 0)
+ dev_err(runtime->dev, "Set codec sysclk failed: %d\n", ret);
+
+ return ret;
+}
+
+static const struct snd_soc_ops avs_es8336_ops = {
+ .hw_params = avs_es8336_hw_params,
+};
+
+static int avs_es8336_be_fixup(struct snd_soc_pcm_runtime *runtime,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate, *channels;
+ struct snd_mask *fmt;
+
+ rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+
+ /* The ADSP will convert the FE rate to 48k, stereo */
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+
+ /* set SSPN to 24 bit */
+ snd_mask_none(fmt);
+ snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_3LE);
+
+ return 0;
+}
+static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port,
+ struct snd_soc_dai_link **dai_link)
+{
+ struct snd_soc_dai_link_component *platform;
+ struct snd_soc_dai_link *dl;
+
+ dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL);
+ platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
+ if (!dl || !platform)
+ return -ENOMEM;
+
+ platform->name = platform_name;
+
+ dl->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_port);
+ dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
+ dl->codecs = devm_kzalloc(dev, sizeof(*dl->codecs), GFP_KERNEL);
+ if (!dl->name || !dl->cpus || !dl->codecs)
+ return -ENOMEM;
+
+ dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", ssp_port);
+ dl->codecs->name = devm_kasprintf(dev, GFP_KERNEL, "i2c-ESSX8336:00");
+ dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, ES8336_CODEC_DAI);
+ if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
+ return -ENOMEM;
+
+ dl->num_cpus = 1;
+ dl->num_codecs = 1;
+ dl->platforms = platform;
+ dl->num_platforms = 1;
+ dl->id = 0;
+ dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ dl->init = avs_es8336_codec_init;
+ dl->exit = avs_es8336_codec_exit;
+ dl->be_hw_params_fixup = avs_es8336_be_fixup;
+ dl->ops = &avs_es8336_ops;
+ dl->nonatomic = 1;
+ dl->no_pcm = 1;
+ dl->dpcm_capture = 1;
+ dl->dpcm_playback = 1;
+
+ *dai_link = dl;
+
+ return 0;
+}
+
+static int avs_card_suspend_pre(struct snd_soc_card *card)
+{
+ struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, ES8336_CODEC_DAI);
+
+ return snd_soc_component_set_jack(codec_dai->component, NULL, NULL);
+}
+
+static int avs_card_resume_post(struct snd_soc_card *card)
+{
+ struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, ES8336_CODEC_DAI);
+ struct avs_card_drvdata *data = snd_soc_card_get_drvdata(card);
+
+ return snd_soc_component_set_jack(codec_dai->component, &data->jack, NULL);
+}
+
+static int avs_es8336_probe(struct platform_device *pdev)
+{
+ struct snd_soc_dai_link *dai_link;
+ struct snd_soc_acpi_mach *mach;
+ struct avs_card_drvdata *data;
+ struct snd_soc_card *card;
+ struct device *dev = &pdev->dev;
+ const char *pname;
+ int ssp_port, ret;
+
+ mach = dev_get_platdata(dev);
+ pname = mach->mach_params.platform;
+ ssp_port = __ffs(mach->mach_params.i2s_link_mask);
+
+ ret = avs_create_dai_link(dev, pname, ssp_port, &dai_link);
+ if (ret) {
+ dev_err(dev, "Failed to create dai link: %d", ret);
+ return ret;
+ }
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!data || !card)
+ return -ENOMEM;
+
+ card->name = "avs_es8336";
+ card->dev = dev;
+ card->owner = THIS_MODULE;
+ card->suspend_pre = avs_card_suspend_pre;
+ card->resume_post = avs_card_resume_post;
+ card->dai_link = dai_link;
+ card->num_links = 1;
+ card->controls = card_controls;
+ card->num_controls = ARRAY_SIZE(card_controls);
+ card->dapm_widgets = card_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
+ card->dapm_routes = card_routes;
+ card->num_dapm_routes = ARRAY_SIZE(card_routes);
+ card->fully_routed = true;
+ snd_soc_card_set_drvdata(card, data);
+
+ ret = snd_soc_fixup_dai_links_platform_name(card, pname);
+ if (ret)
+ return ret;
+
+ return devm_snd_soc_register_card(dev, card);
+}
+
+static struct platform_driver avs_es8336_driver = {
+ .probe = avs_es8336_probe,
+ .driver = {
+ .name = "avs_es8336",
+ .pm = &snd_soc_pm_ops,
+ },
+};
+
+module_platform_driver(avs_es8336_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:avs_es8336");
diff --git a/sound/soc/intel/avs/boards/rt5663.c b/sound/soc/intel/avs/boards/rt5663.c
new file mode 100644
index 000000000000..770b36d05bf4
--- /dev/null
+++ b/sound/soc/intel/avs/boards/rt5663.c
@@ -0,0 +1,254 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2022-2023 Intel Corporation. All rights reserved.
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/clk.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include "../../../codecs/rt5663.h"
+
+#define RT5663_CODEC_DAI "rt5663-aif"
+
+struct rt5663_private {
+ struct snd_soc_jack jack;
+};
+
+static const struct snd_kcontrol_new card_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static const struct snd_soc_dapm_widget card_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route card_routes[] = {
+ /* HP jack connectors */
+ { "Headphone Jack", NULL, "HPOL" },
+ { "Headphone Jack", NULL, "HPOR" },
+
+ /* Mic jacks */
+ { "IN1P", NULL, "Headset Mic" },
+ { "IN1N", NULL, "Headset Mic" },
+};
+
+static struct snd_soc_jack_pin card_headset_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static int avs_rt5663_codec_init(struct snd_soc_pcm_runtime *runtime)
+{
+ struct snd_soc_card *card = runtime->card;
+ struct rt5663_private *priv = snd_soc_card_get_drvdata(card);
+ struct snd_soc_jack_pin *pins;
+ struct snd_soc_jack *jack;
+ int num_pins, ret;
+
+ jack = &priv->jack;
+ num_pins = ARRAY_SIZE(card_headset_pins);
+
+ pins = devm_kmemdup(card->dev, card_headset_pins, sizeof(*pins) * num_pins, GFP_KERNEL);
+ if (!pins)
+ return -ENOMEM;
+
+ ret = snd_soc_card_jack_new_pins(card, "Headset Jack", SND_JACK_HEADSET | SND_JACK_BTN_0 |
+ SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3, jack,
+ pins, num_pins);
+ if (ret)
+ return ret;
+
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+
+ snd_soc_component_set_jack(asoc_rtd_to_codec(runtime, 0)->component, jack, NULL);
+
+ return 0;
+}
+
+static void avs_rt5663_codec_exit(struct snd_soc_pcm_runtime *runtime)
+{
+ snd_soc_component_set_jack(asoc_rtd_to_codec(runtime, 0)->component, NULL, NULL);
+}
+
+static int
+avs_rt5663_be_fixup(struct snd_soc_pcm_runtime *runtime, struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate, *channels;
+ struct snd_mask *fmt;
+
+ rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+
+ /* The ADSP will convert the FE rate to 48k, stereo */
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+
+ /* set SSPN to 24 bit */
+ snd_mask_none(fmt);
+ snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE);
+
+ return 0;
+}
+
+static int avs_rt5663_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ int ret;
+
+ /* use ASRC for internal clocks, as PLL rate isn't multiple of BCLK */
+ rt5663_sel_asrc_clk_src(codec_dai->component,
+ RT5663_DA_STEREO_FILTER | RT5663_AD_STEREO_FILTER,
+ RT5663_CLK_SEL_I2S1_ASRC);
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5663_SCLK_S_MCLK, 24576000, SND_SOC_CLOCK_IN);
+
+ return ret;
+}
+
+static const struct snd_soc_ops avs_rt5663_ops = {
+ .hw_params = avs_rt5663_hw_params,
+};
+
+
+static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port,
+ struct snd_soc_dai_link **dai_link)
+{
+ struct snd_soc_dai_link_component *platform;
+ struct snd_soc_dai_link *dl;
+
+ dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL);
+ platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
+ if (!dl || !platform)
+ return -ENOMEM;
+
+ platform->name = platform_name;
+
+ dl->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_port);
+ dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
+ dl->codecs = devm_kzalloc(dev, sizeof(*dl->codecs), GFP_KERNEL);
+ if (!dl->name || !dl->cpus || !dl->codecs)
+ return -ENOMEM;
+
+ dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", ssp_port);
+ dl->codecs->name = devm_kasprintf(dev, GFP_KERNEL, "i2c-10EC5663:00");
+ dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, RT5663_CODEC_DAI);
+ if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
+ return -ENOMEM;
+
+ dl->num_cpus = 1;
+ dl->num_codecs = 1;
+ dl->platforms = platform;
+ dl->num_platforms = 1;
+ dl->id = 0;
+ dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ dl->init = avs_rt5663_codec_init;
+ dl->exit = avs_rt5663_codec_exit;
+ dl->be_hw_params_fixup = avs_rt5663_be_fixup;
+ dl->nonatomic = 1;
+ dl->no_pcm = 1;
+ dl->dpcm_capture = 1;
+ dl->dpcm_playback = 1;
+ dl->ops = &avs_rt5663_ops;
+
+ *dai_link = dl;
+
+ return 0;
+}
+
+static int avs_card_suspend_pre(struct snd_soc_card *card)
+{
+ struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, RT5663_CODEC_DAI);
+
+ return snd_soc_component_set_jack(codec_dai->component, NULL, NULL);
+}
+
+static int avs_card_resume_post(struct snd_soc_card *card)
+{
+ struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, RT5663_CODEC_DAI);
+ struct snd_soc_jack *jack = snd_soc_card_get_drvdata(card);
+
+ return snd_soc_component_set_jack(codec_dai->component, jack, NULL);
+}
+
+static int avs_rt5663_probe(struct platform_device *pdev)
+{
+ struct snd_soc_dai_link *dai_link;
+ struct snd_soc_acpi_mach *mach;
+ struct snd_soc_card *card;
+ struct rt5663_private *priv;
+ struct device *dev = &pdev->dev;
+ const char *pname;
+ int ssp_port, ret;
+
+ mach = dev_get_platdata(dev);
+ pname = mach->mach_params.platform;
+ ssp_port = __ffs(mach->mach_params.i2s_link_mask);
+
+ ret = avs_create_dai_link(dev, pname, ssp_port, &dai_link);
+ if (ret) {
+ dev_err(dev, "Failed to create dai link: %d", ret);
+ return ret;
+ }
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!priv || !card)
+ return -ENOMEM;
+
+ card->name = "avs_rt5663";
+ card->dev = dev;
+ card->owner = THIS_MODULE;
+ card->suspend_pre = avs_card_suspend_pre;
+ card->resume_post = avs_card_resume_post;
+ card->dai_link = dai_link;
+ card->num_links = 1;
+ card->controls = card_controls;
+ card->num_controls = ARRAY_SIZE(card_controls);
+ card->dapm_widgets = card_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
+ card->dapm_routes = card_routes;
+ card->num_dapm_routes = ARRAY_SIZE(card_routes);
+ card->fully_routed = true;
+ snd_soc_card_set_drvdata(card, priv);
+
+ ret = snd_soc_fixup_dai_links_platform_name(card, pname);
+ if (ret)
+ return ret;
+
+ return devm_snd_soc_register_card(dev, card);
+}
+
+static struct platform_driver avs_rt5663_driver = {
+ .probe = avs_rt5663_probe,
+ .driver = {
+ .name = "avs_rt5663",
+ .pm = &snd_soc_pm_ops,
+ },
+};
+
+module_platform_driver(avs_rt5663_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:avs_rt5663");
diff --git a/sound/soc/intel/avs/boards/rt5682.c b/sound/soc/intel/avs/boards/rt5682.c
index 7142a67900bf..b93468ae0977 100644
--- a/sound/soc/intel/avs/boards/rt5682.c
+++ b/sound/soc/intel/avs/boards/rt5682.c
@@ -79,14 +79,31 @@ static const struct snd_soc_dapm_route card_base_routes[] = {
{ "IN1P", NULL, "Headset Mic" },
};
+static struct snd_soc_jack_pin card_jack_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
static int avs_rt5682_codec_init(struct snd_soc_pcm_runtime *runtime)
{
struct snd_soc_component *component = asoc_rtd_to_codec(runtime, 0)->component;
- struct snd_soc_jack *jack;
struct snd_soc_card *card = runtime->card;
- int ret;
+ struct snd_soc_jack_pin *pins;
+ struct snd_soc_jack *jack;
+ int num_pins, ret;
jack = snd_soc_card_get_drvdata(card);
+ num_pins = ARRAY_SIZE(card_jack_pins);
+
+ pins = devm_kmemdup(card->dev, card_jack_pins, sizeof(*pins) * num_pins, GFP_KERNEL);
+ if (!pins)
+ return -ENOMEM;
/* Need to enable ASRC function for 24MHz mclk rate */
if ((avs_rt5682_quirk & AVS_RT5682_MCLK_EN) &&
@@ -95,12 +112,10 @@ static int avs_rt5682_codec_init(struct snd_soc_pcm_runtime *runtime)
RT5682_AD_STEREO1_FILTER, RT5682_CLK_SEL_I2S1_ASRC);
}
- /*
- * Headset buttons map to the google Reference headset.
- * These can be configured by userspace.
- */
- ret = snd_soc_card_jack_new(card, "Headset", SND_JACK_HEADSET | SND_JACK_BTN_0 |
- SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3, jack);
+
+ ret = snd_soc_card_jack_new_pins(card, "Headset Jack", SND_JACK_HEADSET | SND_JACK_BTN_0 |
+ SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3, jack,
+ pins, num_pins);
if (ret) {
dev_err(card->dev, "Headset Jack creation failed: %d\n", ret);
return ret;
@@ -130,39 +145,36 @@ avs_rt5682_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_para
{
struct snd_soc_pcm_runtime *runtime = asoc_substream_to_rtd(substream);
struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(runtime, 0);
- int clk_id, clk_freq;
- int pll_out, ret;
+ int pll_source, freq_in, freq_out;
+ int ret;
if (avs_rt5682_quirk & AVS_RT5682_MCLK_EN) {
- clk_id = RT5682_PLL1_S_MCLK;
+ pll_source = RT5682_PLL1_S_MCLK;
if (avs_rt5682_quirk & AVS_RT5682_MCLK_24MHZ)
- clk_freq = 24000000;
+ freq_in = 24000000;
else
- clk_freq = 19200000;
+ freq_in = 19200000;
} else {
- clk_id = RT5682_PLL1_S_BCLK1;
- clk_freq = params_rate(params) * 50;
+ pll_source = RT5682_PLL1_S_BCLK1;
+ freq_in = params_rate(params) * 50;
}
- pll_out = params_rate(params) * 512;
+ freq_out = params_rate(params) * 512;
- ret = snd_soc_dai_set_pll(codec_dai, 0, clk_id, clk_freq, pll_out);
+ ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL1, pll_source, freq_in, freq_out);
if (ret < 0)
- dev_err(runtime->dev, "snd_soc_dai_set_pll err = %d\n", ret);
+ dev_err(runtime->dev, "Set PLL failed: %d\n", ret);
- /* Configure sysclk for codec */
- ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL1, pll_out, SND_SOC_CLOCK_IN);
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL1, freq_out, SND_SOC_CLOCK_IN);
if (ret < 0)
- dev_err(runtime->dev, "snd_soc_dai_set_sysclk err = %d\n", ret);
+ dev_err(runtime->dev, "Set sysclk failed: %d\n", ret);
- /* slot_width should equal or large than data length, set them be the same */
+ /* slot_width should be equal or larger than data length. */
ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x0, 0x0, 2, params_width(params));
- if (ret < 0) {
- dev_err(runtime->dev, "set TDM slot err:%d\n", ret);
- return ret;
- }
+ if (ret < 0)
+ dev_err(runtime->dev, "Set TDM slot failed: %d\n", ret);
- return 0;
+ return ret;
}
static const struct snd_soc_ops avs_rt5682_ops = {
@@ -220,6 +232,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in
dl->platforms = platform;
dl->num_platforms = 1;
dl->id = 0;
+ dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
dl->init = avs_rt5682_codec_init;
dl->exit = avs_rt5682_codec_exit;
dl->be_hw_params_fixup = avs_rt5682_be_fixup;
diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig
index f472f603ab75..1fe830af2b84 100644
--- a/sound/soc/intel/boards/Kconfig
+++ b/sound/soc/intel/boards/Kconfig
@@ -475,7 +475,7 @@ endif ## SND_SOC_INTEL_SKYLAKE_HDAUDIO_CODEC || SND_SOC_SOF_HDA_AUDIO_CODEC
if SND_SOC_SOF_HDA_LINK || SND_SOC_SOF_BAYTRAIL
config SND_SOC_INTEL_SOF_RT5682_MACH
- tristate "SOF with rt5682 codec in I2S Mode"
+ tristate "SOF with rt5650/rt5682 codec in I2S Mode"
depends on I2C && ACPI
depends on ((SND_HDA_CODEC_HDMI && SND_SOC_SOF_HDA_AUDIO_CODEC) &&\
(MFD_INTEL_LPSS || COMPILE_TEST)) ||\
@@ -485,6 +485,7 @@ config SND_SOC_INTEL_SOF_RT5682_MACH
select SND_SOC_RT1011
select SND_SOC_RT1015
select SND_SOC_RT1015P
+ select SND_SOC_RT5645
select SND_SOC_RT5682_I2C
select SND_SOC_RT5682S
select SND_SOC_DMIC
@@ -494,7 +495,7 @@ config SND_SOC_INTEL_SOF_RT5682_MACH
select SND_SOC_INTEL_SOF_REALTEK_COMMON
help
This adds support for ASoC machine driver for SOF platforms
- with rt5682 codec.
+ with rt5650 or rt5682 codec.
Say Y if you have such a device.
If unsure select "N".
diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c
index 7c034d671cf3..b4f07bdcf8b4 100644
--- a/sound/soc/intel/boards/sof_rt5682.c
+++ b/sound/soc/intel/boards/sof_rt5682.c
@@ -22,6 +22,7 @@
#include <sound/soc-acpi.h>
#include "../../codecs/rt5682.h"
#include "../../codecs/rt5682s.h"
+#include "../../codecs/rt5645.h"
#include "../../codecs/hdac_hdmi.h"
#include "../common/soc-intel-quirks.h"
#include "hda_dsp_common.h"
@@ -60,6 +61,7 @@
#define SOF_MAX98390_SPEAKER_AMP_PRESENT BIT(24)
#define SOF_MAX98390_TWEETER_SPEAKER_PRESENT BIT(25)
#define SOF_RT1019_SPEAKER_AMP_PRESENT BIT(26)
+#define SOF_RT5650_HEADPHONE_CODEC_PRESENT BIT(27)
/* Default: MCLK on, MCLK 19.2M, SSP0 */
@@ -305,6 +307,7 @@ static int sof_rt5682_codec_init(struct snd_soc_pcm_runtime *rtd)
struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card);
struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
struct snd_soc_jack *jack;
+ int extra_jack_data;
int ret;
/* need to enable ASRC function for 24MHz mclk rate */
@@ -315,7 +318,16 @@ static int sof_rt5682_codec_init(struct snd_soc_pcm_runtime *rtd)
RT5682S_DA_STEREO1_FILTER |
RT5682S_AD_STEREO1_FILTER,
RT5682S_CLK_SEL_I2S1_ASRC);
- else
+ else if (sof_rt5682_quirk & SOF_RT5650_HEADPHONE_CODEC_PRESENT) {
+ rt5645_sel_asrc_clk_src(component,
+ RT5645_DA_STEREO_FILTER |
+ RT5645_AD_STEREO_FILTER,
+ RT5645_CLK_SEL_I2S1_ASRC);
+ rt5645_sel_asrc_clk_src(component,
+ RT5645_DA_MONO_L_FILTER |
+ RT5645_DA_MONO_R_FILTER,
+ RT5645_CLK_SEL_I2S2_ASRC);
+ } else
rt5682_sel_asrc_clk_src(component,
RT5682_DA_STEREO1_FILTER |
RT5682_AD_STEREO1_FILTER,
@@ -365,7 +377,12 @@ static int sof_rt5682_codec_init(struct snd_soc_pcm_runtime *rtd)
snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
- ret = snd_soc_component_set_jack(component, jack, NULL);
+
+ if (sof_rt5682_quirk & SOF_RT5650_HEADPHONE_CODEC_PRESENT) {
+ extra_jack_data = SND_JACK_MICROPHONE | SND_JACK_BTN_0;
+ ret = snd_soc_component_set_jack(component, jack, &extra_jack_data);
+ } else
+ ret = snd_soc_component_set_jack(component, jack, NULL);
if (ret) {
dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret);
@@ -402,6 +419,8 @@ static int sof_rt5682_hw_params(struct snd_pcm_substream *substream,
if (sof_rt5682_quirk & SOF_RT5682S_HEADPHONE_CODEC_PRESENT)
pll_source = RT5682S_PLL_S_MCLK;
+ else if (sof_rt5682_quirk & SOF_RT5650_HEADPHONE_CODEC_PRESENT)
+ pll_source = RT5645_PLL1_S_MCLK;
else
pll_source = RT5682_PLL1_S_MCLK;
@@ -422,6 +441,8 @@ static int sof_rt5682_hw_params(struct snd_pcm_substream *substream,
} else {
if (sof_rt5682_quirk & SOF_RT5682S_HEADPHONE_CODEC_PRESENT)
pll_source = RT5682S_PLL_S_BCLK1;
+ else if (sof_rt5682_quirk & SOF_RT5650_HEADPHONE_CODEC_PRESENT)
+ pll_source = RT5645_PLL1_S_BCLK1;
else
pll_source = RT5682_PLL1_S_BCLK1;
@@ -431,6 +452,9 @@ static int sof_rt5682_hw_params(struct snd_pcm_substream *substream,
if (sof_rt5682_quirk & SOF_RT5682S_HEADPHONE_CODEC_PRESENT) {
pll_id = RT5682S_PLL2;
clk_id = RT5682S_SCLK_S_PLL2;
+ } else if (sof_rt5682_quirk & SOF_RT5650_HEADPHONE_CODEC_PRESENT) {
+ pll_id = 0; /* not used in codec driver */
+ clk_id = RT5645_SCLK_S_PLL1;
} else {
pll_id = RT5682_PLL1;
clk_id = RT5682_SCLK_S_PLL1;
@@ -559,11 +583,30 @@ static const struct snd_soc_dapm_route sof_map[] = {
{ "IN1P", NULL, "Headset Mic" },
};
+static const struct snd_soc_dapm_route rt5650_spk_dapm_routes[] = {
+ /* speaker */
+ { "Left Spk", NULL, "SPOL" },
+ { "Right Spk", NULL, "SPOR" },
+};
+
static const struct snd_soc_dapm_route dmic_map[] = {
/* digital mics */
{"DMic", NULL, "SoC DMIC"},
};
+static int rt5650_spk_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ int ret;
+
+ ret = snd_soc_dapm_add_routes(&card->dapm, rt5650_spk_dapm_routes,
+ ARRAY_SIZE(rt5650_spk_dapm_routes));
+ if (ret)
+ dev_err(rtd->dev, "fail to add dapm routes, ret=%d\n", ret);
+
+ return ret;
+}
+
static int dmic_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_card *card = rtd->card;
@@ -614,6 +657,17 @@ static struct snd_soc_dai_link_component rt5682s_component[] = {
}
};
+static struct snd_soc_dai_link_component rt5650_components[] = {
+ {
+ .name = "i2c-10EC5650:00",
+ .dai_name = "rt5645-aif1",
+ },
+ {
+ .name = "i2c-10EC5650:00",
+ .dai_name = "rt5645-aif2",
+ }
+};
+
static struct snd_soc_dai_link_component dmic_component[] = {
{
.name = "dmic-codec",
@@ -652,6 +706,9 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
if (sof_rt5682_quirk & SOF_RT5682S_HEADPHONE_CODEC_PRESENT) {
links[id].codecs = rt5682s_component;
links[id].num_codecs = ARRAY_SIZE(rt5682s_component);
+ } else if (sof_rt5682_quirk & SOF_RT5650_HEADPHONE_CODEC_PRESENT) {
+ links[id].codecs = &rt5650_components[0];
+ links[id].num_codecs = 1;
} else {
links[id].codecs = rt5682_component;
links[id].num_codecs = ARRAY_SIZE(rt5682_component);
@@ -804,6 +861,11 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
links[id].init = max_98390_spk_codec_init;
links[id].ops = &max_98390_ops;
+ } else if (sof_rt5682_quirk & SOF_RT5650_HEADPHONE_CODEC_PRESENT) {
+ links[id].codecs = &rt5650_components[1];
+ links[id].num_codecs = 1;
+ links[id].init = rt5650_spk_init;
+ links[id].ops = &sof_rt5682_ops;
} else {
max_98357a_dai_link(&links[id]);
}
@@ -890,6 +952,12 @@ static int sof_audio_probe(struct platform_device *pdev)
/* Detect the headset codec variant */
if (acpi_dev_present("RTL5682", NULL, -1))
sof_rt5682_quirk |= SOF_RT5682S_HEADPHONE_CODEC_PRESENT;
+ else if (acpi_dev_present("10EC5650", NULL, -1)) {
+ sof_rt5682_quirk |= SOF_RT5650_HEADPHONE_CODEC_PRESENT;
+
+ sof_audio_card_rt5682.name = devm_kstrdup(&pdev->dev, "rt5650",
+ GFP_KERNEL);
+ }
if (soc_intel_is_byt() || soc_intel_is_cht()) {
is_legacy_cpu = 1;
@@ -1178,6 +1246,14 @@ static const struct platform_device_id board_ids[] = {
SOF_RT5682_SSP_AMP(0) |
SOF_RT5682_NUM_HDMIDEV(3)),
},
+ {
+ .name = "jsl_rt5650",
+ .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
+ SOF_RT5682_MCLK_24MHZ |
+ SOF_RT5682_SSP_CODEC(0) |
+ SOF_SPEAKER_AMP_PRESENT |
+ SOF_RT5682_SSP_AMP(1)),
+ },
{ }
};
MODULE_DEVICE_TABLE(platform, board_ids);
diff --git a/sound/soc/intel/common/soc-acpi-intel-jsl-match.c b/sound/soc/intel/common/soc-acpi-intel-jsl-match.c
index f5c7e1bbded0..f56bd7d656e9 100644
--- a/sound/soc/intel/common/soc-acpi-intel-jsl-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-jsl-match.c
@@ -34,6 +34,11 @@ static const struct snd_soc_acpi_codecs mx98360a_spk = {
.codecs = {"MX98360A"}
};
+static struct snd_soc_acpi_codecs rt5650_spk = {
+ .num_codecs = 1,
+ .codecs = {"10EC5650"}
+};
+
static const struct snd_soc_acpi_codecs rt5682_rt5682s_hp = {
.num_codecs = 2,
.codecs = {"10EC5682", "RTL5682"},
@@ -98,6 +103,13 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_jsl_machines[] = {
SND_SOC_ACPI_TPLG_INTEL_SSP_MSB |
SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER,
},
+ {
+ .id = "10EC5650",
+ .drv_name = "jsl_rt5650",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &rt5650_spk,
+ .sof_tplg_filename = "sof-jsl-rt5650.tplg",
+ },
{},
};
EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_jsl_machines);
diff --git a/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c b/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c
index a868a04ed4e7..b86159f70a33 100644
--- a/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c
+++ b/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c
@@ -2815,7 +2815,6 @@ static int mt8186_afe_pcm_dev_probe(struct platform_device *pdev)
{
struct mtk_base_afe *afe;
struct mt8186_afe_private *afe_priv;
- struct resource *res;
struct reset_control *rstc;
struct device *dev = &pdev->dev;
int i, ret, irq_id;
@@ -2836,8 +2835,7 @@ static int mt8186_afe_pcm_dev_probe(struct platform_device *pdev)
afe_priv = afe->platform_priv;
afe->dev = &pdev->dev;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- afe->base_addr = devm_ioremap_resource(dev, res);
+ afe->base_addr = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(afe->base_addr))
return PTR_ERR(afe->base_addr);
diff --git a/sound/soc/mediatek/mt8188/mt8188-afe-pcm.c b/sound/soc/mediatek/mt8188/mt8188-afe-pcm.c
index 6a24b339444b..5e14655c5617 100644
--- a/sound/soc/mediatek/mt8188/mt8188-afe-pcm.c
+++ b/sound/soc/mediatek/mt8188/mt8188-afe-pcm.c
@@ -16,6 +16,7 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
+#include <linux/of_reserved_mem.h>
#include <linux/pm_runtime.h>
#include <linux/soc/mediatek/infracfg.h>
#include <linux/reset.h>
@@ -3193,11 +3194,15 @@ static int mt8188_afe_pcm_dev_probe(struct platform_device *pdev)
{
struct mtk_base_afe *afe;
struct mt8188_afe_private *afe_priv;
- struct device *dev;
+ struct device *dev = &pdev->dev;
struct reset_control *rstc;
struct regmap *infra_ao;
int i, irq_id, ret;
+ ret = of_reserved_mem_device_init(dev);
+ if (ret)
+ dev_dbg(dev, "failed to assign memory region: %d\n", ret);
+
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(33));
if (ret)
return ret;
@@ -3213,7 +3218,6 @@ static int mt8188_afe_pcm_dev_probe(struct platform_device *pdev)
afe_priv = afe->platform_priv;
afe->dev = &pdev->dev;
- dev = afe->dev;
afe->base_addr = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(afe->base_addr))
diff --git a/sound/soc/mediatek/mt8188/mt8188-mt6359.c b/sound/soc/mediatek/mt8188/mt8188-mt6359.c
index 6ebcc9497ea0..9017f48b6272 100644
--- a/sound/soc/mediatek/mt8188/mt8188-mt6359.c
+++ b/sound/soc/mediatek/mt8188/mt8188-mt6359.c
@@ -723,6 +723,9 @@ static struct snd_soc_dai_link mt8188_mt6359_dai_links[] = {
},
.dynamic = 1,
.dpcm_playback = 1,
+ .dpcm_merged_chan = 1,
+ .dpcm_merged_rate = 1,
+ .dpcm_merged_format = 1,
SND_SOC_DAILINK_REG(playback2),
},
[DAI_LINK_DL3_FE] = {
@@ -734,6 +737,9 @@ static struct snd_soc_dai_link mt8188_mt6359_dai_links[] = {
},
.dynamic = 1,
.dpcm_playback = 1,
+ .dpcm_merged_chan = 1,
+ .dpcm_merged_rate = 1,
+ .dpcm_merged_format = 1,
SND_SOC_DAILINK_REG(playback3),
},
[DAI_LINK_DL6_FE] = {
@@ -745,6 +751,9 @@ static struct snd_soc_dai_link mt8188_mt6359_dai_links[] = {
},
.dynamic = 1,
.dpcm_playback = 1,
+ .dpcm_merged_chan = 1,
+ .dpcm_merged_rate = 1,
+ .dpcm_merged_format = 1,
SND_SOC_DAILINK_REG(playback6),
},
[DAI_LINK_DL7_FE] = {
@@ -833,6 +842,9 @@ static struct snd_soc_dai_link mt8188_mt6359_dai_links[] = {
},
.dynamic = 1,
.dpcm_capture = 1,
+ .dpcm_merged_chan = 1,
+ .dpcm_merged_rate = 1,
+ .dpcm_merged_format = 1,
SND_SOC_DAILINK_REG(capture4),
},
[DAI_LINK_UL5_FE] = {
@@ -844,6 +856,9 @@ static struct snd_soc_dai_link mt8188_mt6359_dai_links[] = {
},
.dynamic = 1,
.dpcm_capture = 1,
+ .dpcm_merged_chan = 1,
+ .dpcm_merged_rate = 1,
+ .dpcm_merged_format = 1,
SND_SOC_DAILINK_REG(capture5),
},
[DAI_LINK_UL6_FE] = {
diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c
index 3e4c70403672..10636506b622 100644
--- a/sound/soc/pxa/pxa2xx-i2s.c
+++ b/sound/soc/pxa/pxa2xx-i2s.c
@@ -370,18 +370,11 @@ static const struct snd_soc_component_driver pxa_i2s_component = {
static int pxa2xx_i2s_drv_probe(struct platform_device *pdev)
{
- struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ struct resource *res;
- if (!res) {
- dev_err(&pdev->dev, "missing MMIO resource\n");
- return -ENXIO;
- }
-
- i2s_reg_base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(i2s_reg_base)) {
- dev_err(&pdev->dev, "ioremap failed\n");
+ i2s_reg_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(i2s_reg_base))
return PTR_ERR(i2s_reg_base);
- }
pxa2xx_i2s_pcm_stereo_out.addr = res->start + SADR;
pxa2xx_i2s_pcm_stereo_in.addr = res->start + SADR;
diff --git a/sound/soc/qcom/qdsp6/q6afe-dai.c b/sound/soc/qcom/qdsp6/q6afe-dai.c
index 31e0bad71e95..dbff55a97162 100644
--- a/sound/soc/qcom/qdsp6/q6afe-dai.c
+++ b/sound/soc/qcom/qdsp6/q6afe-dai.c
@@ -476,7 +476,7 @@ static int q6afe_mi2s_set_sysclk(struct snd_soc_dai *dai,
static const struct snd_soc_dapm_route q6afe_dapm_routes[] = {
{"HDMI Playback", NULL, "HDMI_RX"},
- {"Display Port Playback", NULL, "DISPLAY_PORT_RX"},
+ {"DISPLAY_PORT_RX_0 Playback", NULL, "DISPLAY_PORT_RX"},
{"Slimbus Playback", NULL, "SLIMBUS_0_RX"},
{"Slimbus1 Playback", NULL, "SLIMBUS_1_RX"},
{"Slimbus2 Playback", NULL, "SLIMBUS_2_RX"},
diff --git a/sound/soc/qcom/qdsp6/q6apm-dai.c b/sound/soc/qcom/qdsp6/q6apm-dai.c
index 5eb0b864c740..c90db6daabbd 100644
--- a/sound/soc/qcom/qdsp6/q6apm-dai.c
+++ b/sound/soc/qcom/qdsp6/q6apm-dai.c
@@ -840,6 +840,7 @@ static const struct snd_soc_component_driver q6apm_fe_dai_component = {
.pointer = q6apm_dai_pointer,
.trigger = q6apm_dai_trigger,
.compress_ops = &q6apm_dai_compress_ops,
+ .use_dai_pcm_id = true,
};
static int q6apm_dai_probe(struct platform_device *pdev)
diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c
index 7bfac9492ab5..2a2a5bd98110 100644
--- a/sound/soc/qcom/qdsp6/q6apm.c
+++ b/sound/soc/qcom/qdsp6/q6apm.c
@@ -27,7 +27,7 @@ struct apm_graph_mgmt_cmd {
#define APM_GRAPH_MGMT_PSIZE(p, n) ALIGN(struct_size(p, sub_graph_id_list, n), 8)
-struct q6apm *g_apm;
+static struct q6apm *g_apm;
int q6apm_send_cmd_sync(struct q6apm *apm, struct gpr_pkt *pkt, uint32_t rsp_opcode)
{
@@ -511,6 +511,8 @@ static int graph_callback(struct gpr_resp_pkt *data, void *priv, int op)
switch (hdr->opcode) {
case DATA_CMD_RSP_WR_SH_MEM_EP_DATA_BUFFER_DONE_V2:
+ if (!graph->ar_graph)
+ break;
client_event = APM_CLIENT_EVENT_DATA_WRITE_DONE;
mutex_lock(&graph->lock);
token = hdr->token & APM_WRITE_TOKEN_MASK;
@@ -544,6 +546,8 @@ static int graph_callback(struct gpr_resp_pkt *data, void *priv, int op)
wake_up(&graph->cmd_wait);
break;
case DATA_CMD_RSP_RD_SH_MEM_EP_DATA_BUFFER_V2:
+ if (!graph->ar_graph)
+ break;
client_event = APM_CLIENT_EVENT_DATA_READ_DONE;
mutex_lock(&graph->lock);
rd_done = data->payload;
@@ -649,8 +653,9 @@ int q6apm_graph_close(struct q6apm_graph *graph)
{
struct audioreach_graph *ar_graph = graph->ar_graph;
- gpr_free_port(graph->port);
+ graph->ar_graph = NULL;
kref_put(&ar_graph->refcount, q6apm_put_audioreach_graph);
+ gpr_free_port(graph->port);
kfree(graph);
return 0;
diff --git a/sound/soc/qcom/qdsp6/topology.c b/sound/soc/qcom/qdsp6/topology.c
index cccc59b570b9..130b22a34fb3 100644
--- a/sound/soc/qcom/qdsp6/topology.c
+++ b/sound/soc/qcom/qdsp6/topology.c
@@ -1277,8 +1277,8 @@ int audioreach_tplg_init(struct snd_soc_component *component)
ret = snd_soc_tplg_component_load(component, &audioreach_tplg_ops, fw);
if (ret < 0) {
- dev_err(dev, "tplg component load failed%d\n", ret);
- ret = -EINVAL;
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "tplg component load failed: %d\n", ret);
}
release_firmware(fw);
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 11bc5250ffd0..a5b96c17633a 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -238,6 +238,81 @@ static inline void snd_soc_debugfs_exit(void) { }
#endif
+static int snd_soc_is_match_dai_args(struct of_phandle_args *args1,
+ struct of_phandle_args *args2)
+{
+ if (!args1 || !args2)
+ return 0;
+
+ if (args1->np != args2->np)
+ return 0;
+
+ for (int i = 0; i < args1->args_count; i++)
+ if (args1->args[i] != args2->args[i])
+ return 0;
+
+ return 1;
+}
+
+static inline int snd_soc_dlc_component_is_empty(struct snd_soc_dai_link_component *dlc)
+{
+ return !(dlc->dai_args || dlc->name || dlc->of_node);
+}
+
+static inline int snd_soc_dlc_component_is_invalid(struct snd_soc_dai_link_component *dlc)
+{
+ return (dlc->name && dlc->of_node);
+}
+
+static inline int snd_soc_dlc_dai_is_empty(struct snd_soc_dai_link_component *dlc)
+{
+ return !(dlc->dai_args || dlc->dai_name);
+}
+
+static int snd_soc_is_matching_dai(const struct snd_soc_dai_link_component *dlc,
+ struct snd_soc_dai *dai)
+{
+ if (!dlc)
+ return 0;
+
+ if (dlc->dai_args)
+ return snd_soc_is_match_dai_args(dai->driver->dai_args, dlc->dai_args);
+
+ if (!dlc->dai_name)
+ return 1;
+
+ /* see snd_soc_dai_name_get() */
+
+ if (strcmp(dlc->dai_name, dai->name) == 0)
+ return 1;
+
+ if (dai->driver->name &&
+ strcmp(dai->driver->name, dlc->dai_name) == 0)
+ return 1;
+
+ if (dai->component->name &&
+ strcmp(dlc->dai_name, dai->component->name) == 0)
+ return 1;
+
+ return 0;
+}
+
+const char *snd_soc_dai_name_get(struct snd_soc_dai *dai)
+{
+ /* see snd_soc_is_matching_dai() */
+ if (dai->name)
+ return dai->name;
+
+ if (dai->driver->name)
+ return dai->driver->name;
+
+ if (dai->component->name)
+ return dai->component->name;
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_name_get);
+
static int snd_soc_rtd_add_component(struct snd_soc_pcm_runtime *rtd,
struct snd_soc_component *component)
{
@@ -734,6 +809,19 @@ static struct device_node
return of_node;
}
+struct of_phandle_args *snd_soc_copy_dai_args(struct device *dev, struct of_phandle_args *args)
+{
+ struct of_phandle_args *ret = devm_kzalloc(dev, sizeof(*ret), GFP_KERNEL);
+
+ if (!ret)
+ return NULL;
+
+ *ret = *args;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_copy_dai_args);
+
static int snd_soc_is_matching_component(
const struct snd_soc_dai_link_component *dlc,
struct snd_soc_component *component)
@@ -743,6 +831,15 @@ static int snd_soc_is_matching_component(
if (!dlc)
return 0;
+ if (dlc->dai_args) {
+ struct snd_soc_dai *dai;
+
+ for_each_component_dais(component, dai)
+ if (snd_soc_is_matching_dai(dlc, dai))
+ return 1;
+ return 0;
+ }
+
component_of_node = soc_component_to_node(component);
if (dlc->of_node && component_of_node != dlc->of_node)
@@ -795,18 +892,11 @@ struct snd_soc_dai *snd_soc_find_dai(
lockdep_assert_held(&client_mutex);
/* Find CPU DAI from registered DAIs */
- for_each_component(component) {
- if (!snd_soc_is_matching_component(dlc, component))
- continue;
- for_each_component_dais(component, dai) {
- if (dlc->dai_name && strcmp(dai->name, dlc->dai_name)
- && (!dai->driver->name
- || strcmp(dai->driver->name, dlc->dai_name)))
- continue;
-
- return dai;
- }
- }
+ for_each_component(component)
+ if (snd_soc_is_matching_component(dlc, component))
+ for_each_component_dais(component, dai)
+ if (snd_soc_is_matching_dai(dlc, dai))
+ return dai;
return NULL;
}
@@ -829,102 +919,100 @@ static int soc_dai_link_sanity_check(struct snd_soc_card *card,
struct snd_soc_dai_link *link)
{
int i;
- struct snd_soc_dai_link_component *cpu, *codec, *platform;
+ struct snd_soc_dai_link_component *dlc;
- for_each_link_codecs(link, i, codec) {
+ /* Codec check */
+ for_each_link_codecs(link, i, dlc) {
/*
* Codec must be specified by 1 of name or OF node,
* not both or neither.
*/
- if (!!codec->name == !!codec->of_node) {
- dev_err(card->dev, "ASoC: Neither/both codec name/of_node are set for %s\n",
- link->name);
- return -EINVAL;
- }
+ if (snd_soc_dlc_component_is_invalid(dlc))
+ goto component_invalid;
+
+ if (snd_soc_dlc_component_is_empty(dlc))
+ goto component_empty;
/* Codec DAI name must be specified */
- if (!codec->dai_name) {
- dev_err(card->dev, "ASoC: codec_dai_name not set for %s\n",
- link->name);
- return -EINVAL;
- }
+ if (snd_soc_dlc_dai_is_empty(dlc))
+ goto dai_empty;
/*
* Defer card registration if codec component is not added to
* component list.
*/
- if (!soc_find_component(codec)) {
- dev_dbg(card->dev,
- "ASoC: codec component %s not found for link %s\n",
- codec->name, link->name);
- return -EPROBE_DEFER;
- }
+ if (!soc_find_component(dlc))
+ goto component_not_find;
}
- for_each_link_platforms(link, i, platform) {
+ /* Platform check */
+ for_each_link_platforms(link, i, dlc) {
/*
* Platform may be specified by either name or OF node, but it
* can be left unspecified, then no components will be inserted
* in the rtdcom list
*/
- if (!!platform->name == !!platform->of_node) {
- dev_err(card->dev,
- "ASoC: Neither/both platform name/of_node are set for %s\n",
- link->name);
- return -EINVAL;
- }
+ if (snd_soc_dlc_component_is_invalid(dlc))
+ goto component_invalid;
+
+ if (snd_soc_dlc_component_is_empty(dlc))
+ goto component_empty;
/*
* Defer card registration if platform component is not added to
* component list.
*/
- if (!soc_find_component(platform)) {
- dev_dbg(card->dev,
- "ASoC: platform component %s not found for link %s\n",
- platform->name, link->name);
- return -EPROBE_DEFER;
- }
+ if (!soc_find_component(dlc))
+ goto component_not_find;
}
- for_each_link_cpus(link, i, cpu) {
+ /* CPU check */
+ for_each_link_cpus(link, i, dlc) {
/*
* CPU device may be specified by either name or OF node, but
* can be left unspecified, and will be matched based on DAI
* name alone..
*/
- if (cpu->name && cpu->of_node) {
- dev_err(card->dev,
- "ASoC: Neither/both cpu name/of_node are set for %s\n",
- link->name);
- return -EINVAL;
- }
+ if (snd_soc_dlc_component_is_invalid(dlc))
+ goto component_invalid;
- /*
- * Defer card registration if cpu dai component is not added to
- * component list.
- */
- if ((cpu->of_node || cpu->name) &&
- !soc_find_component(cpu)) {
- dev_dbg(card->dev,
- "ASoC: cpu component %s not found for link %s\n",
- cpu->name, link->name);
- return -EPROBE_DEFER;
- }
- /*
- * At least one of CPU DAI name or CPU device name/node must be
- * specified
- */
- if (!cpu->dai_name &&
- !(cpu->name || cpu->of_node)) {
- dev_err(card->dev,
- "ASoC: Neither cpu_dai_name nor cpu_name/of_node are set for %s\n",
- link->name);
- return -EINVAL;
+ if (snd_soc_dlc_component_is_empty(dlc)) {
+ /*
+ * At least one of CPU DAI name or CPU device name/node must be specified
+ */
+ if (snd_soc_dlc_dai_is_empty(dlc))
+ goto component_dai_empty;
+ } else {
+ /*
+ * Defer card registration if Component is not added
+ */
+ if (!soc_find_component(dlc))
+ goto component_not_find;
}
}
return 0;
+
+component_invalid:
+ dev_err(card->dev, "ASoC: Both Component name/of_node are set for %s\n", link->name);
+ return -EINVAL;
+
+component_empty:
+ dev_err(card->dev, "ASoC: Neither Component name/of_node are set for %s\n", link->name);
+ return -EINVAL;
+
+component_not_find:
+ dev_err(card->dev, "ASoC: Component %s not found for link %s\n", dlc->name, link->name);
+ return -EPROBE_DEFER;
+
+dai_empty:
+ dev_err(card->dev, "ASoC: DAI name is not set for %s\n", link->name);
+ return -EINVAL;
+
+component_dai_empty:
+ dev_err(card->dev, "ASoC: Neither DAI/Component name/of_node are set for %s\n", link->name);
+ return -EINVAL;
}
/**
@@ -1988,8 +2076,10 @@ static int snd_soc_bind_card(struct snd_soc_card *card)
/* probe all components used by DAI links on this card */
ret = soc_probe_link_components(card);
if (ret < 0) {
- dev_err(card->dev,
- "ASoC: failed to instantiate card %d\n", ret);
+ if (ret != -EPROBE_DEFER) {
+ dev_err(card->dev,
+ "ASoC: failed to instantiate card %d\n", ret);
+ }
goto probe_end;
}
@@ -2928,6 +3018,14 @@ int snd_soc_of_parse_tdm_slot(struct device_node *np,
}
EXPORT_SYMBOL_GPL(snd_soc_of_parse_tdm_slot);
+void snd_soc_dlc_use_cpu_as_platform(struct snd_soc_dai_link_component *platforms,
+ struct snd_soc_dai_link_component *cpus)
+{
+ platforms->of_node = cpus->of_node;
+ platforms->dai_args = cpus->dai_args;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dlc_use_cpu_as_platform);
+
void snd_soc_of_parse_node_prefix(struct device_node *np,
struct snd_soc_codec_conf *codec_conf,
struct device_node *of_node,
@@ -3233,11 +3331,12 @@ EXPORT_SYMBOL_GPL(snd_soc_get_stream_cpu);
int snd_soc_get_dai_id(struct device_node *ep)
{
struct snd_soc_component *component;
- struct snd_soc_dai_link_component dlc;
+ struct snd_soc_dai_link_component dlc = {
+ .of_node = of_graph_get_port_parent(ep),
+ };
int ret;
- dlc.of_node = of_graph_get_port_parent(ep);
- dlc.name = NULL;
+
/*
* For example HDMI case, HDMI has video/sound port,
* but ALSA SoC needs sound port number only.
@@ -3262,8 +3361,6 @@ int snd_soc_get_dlc(const struct of_phandle_args *args, struct snd_soc_dai_link_
struct snd_soc_component *pos;
int ret = -EPROBE_DEFER;
- dlc->of_node = args->np;
-
mutex_lock(&client_mutex);
for_each_component(pos) {
struct device_node *component_of_node = soc_component_to_node(pos);
@@ -3302,9 +3399,7 @@ int snd_soc_get_dlc(const struct of_phandle_args *args, struct snd_soc_dai_link_
id--;
}
- dlc->dai_name = dai->driver->name;
- if (!dlc->dai_name)
- dlc->dai_name = pos->name;
+ dlc->dai_name = snd_soc_dai_name_get(dai);
} else if (ret) {
/*
* if another error than ENOTSUPP is returned go on and
@@ -3317,6 +3412,10 @@ int snd_soc_get_dlc(const struct of_phandle_args *args, struct snd_soc_dai_link_
break;
}
+
+ if (ret == 0)
+ dlc->of_node = args->np;
+
mutex_unlock(&client_mutex);
return ret;
}
@@ -3368,6 +3467,24 @@ int snd_soc_of_get_dai_name(struct device_node *of_node,
}
EXPORT_SYMBOL_GPL(snd_soc_of_get_dai_name);
+struct snd_soc_dai *snd_soc_get_dai_via_args(struct of_phandle_args *dai_args)
+{
+ struct snd_soc_dai *dai;
+ struct snd_soc_component *component;
+
+ mutex_lock(&client_mutex);
+ for_each_component(component) {
+ for_each_component_dais(component, dai)
+ if (snd_soc_is_match_dai_args(dai->driver->dai_args, dai_args))
+ goto found;
+ }
+ dai = NULL;
+found:
+ mutex_unlock(&client_mutex);
+ return dai;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_dai_via_args);
+
static void __snd_soc_of_put_component(struct snd_soc_dai_link_component *component)
{
if (component->of_node) {
diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c
index 03ec3c5adc3a..3592ff09f692 100644
--- a/sound/soc/soc-topology.c
+++ b/sound/soc/soc-topology.c
@@ -1732,7 +1732,8 @@ static int soc_tplg_fe_link_create(struct soc_tplg *tplg,
ret = snd_soc_add_pcm_runtimes(tplg->comp->card, link, 1);
if (ret < 0) {
- dev_err(tplg->dev, "ASoC: adding FE link failed\n");
+ if (ret != -EPROBE_DEFER)
+ dev_err(tplg->dev, "ASoC: adding FE link failed\n");
goto err;
}
@@ -2492,8 +2493,11 @@ static int soc_tplg_process_headers(struct soc_tplg *tplg)
/* load the header object */
ret = soc_tplg_load_header(tplg, hdr);
if (ret < 0) {
- dev_err(tplg->dev,
- "ASoC: topology: could not load header: %d\n", ret);
+ if (ret != -EPROBE_DEFER) {
+ dev_err(tplg->dev,
+ "ASoC: topology: could not load header: %d\n",
+ ret);
+ }
return ret;
}
diff --git a/sound/soc/sof/amd/Kconfig b/sound/soc/sof/amd/Kconfig
index 1cb92d6030e3..7dbc8df5cfe6 100644
--- a/sound/soc/sof/amd/Kconfig
+++ b/sound/soc/sof/amd/Kconfig
@@ -21,6 +21,7 @@ config SND_SOC_SOF_AMD_COMMON
select SND_SOC_SOF_PCI_DEV
select SND_AMD_ACP_CONFIG
select SND_SOC_SOF_XTENSA
+ select SND_SOC_SOF_ACP_PROBES
select SND_SOC_ACPI if ACPI
help
This option is not user-selectable but automatically handled by
@@ -42,4 +43,11 @@ config SND_SOC_SOF_AMD_REMBRANDT
Say Y if you want to enable SOF on Rembrandt.
If unsure select "N".
+config SND_SOC_SOF_ACP_PROBES
+ tristate
+ select SND_SOC_SOF_DEBUG_PROBES
+ help
+ This option is not user-selectable but automatically handled by
+ 'select' statements at a higher level
+
endif
diff --git a/sound/soc/sof/amd/Makefile b/sound/soc/sof/amd/Makefile
index 5626d13b3e69..ef9f7df4e379 100644
--- a/sound/soc/sof/amd/Makefile
+++ b/sound/soc/sof/amd/Makefile
@@ -5,6 +5,7 @@
# Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved.
snd-sof-amd-acp-objs := acp.o acp-loader.o acp-ipc.o acp-pcm.o acp-stream.o acp-trace.o acp-common.o
+snd-sof-amd-acp-$(CONFIG_SND_SOC_SOF_ACP_PROBES) = acp-probes.o
snd-sof-amd-renoir-objs := pci-rn.o renoir.o
snd-sof-amd-rembrandt-objs := pci-rmb.o rembrandt.o
diff --git a/sound/soc/sof/amd/acp-common.c b/sound/soc/sof/amd/acp-common.c
index df36b411a12e..3a0c7688dcfe 100644
--- a/sound/soc/sof/amd/acp-common.c
+++ b/sound/soc/sof/amd/acp-common.c
@@ -196,6 +196,10 @@ struct snd_sof_dsp_ops sof_acp_common_ops = {
.dbg_dump = amd_sof_dump,
.debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem,
.dsp_arch_ops = &sof_xtensa_arch_ops,
+
+ /* probe client device registation */
+ .register_ipc_clients = acp_probes_register,
+ .unregister_ipc_clients = acp_probes_unregister,
};
EXPORT_SYMBOL_NS(sof_acp_common_ops, SND_SOC_SOF_AMD_COMMON);
diff --git a/sound/soc/sof/amd/acp-ipc.c b/sound/soc/sof/amd/acp-ipc.c
index 8a0fc635a997..81a2c096a185 100644
--- a/sound/soc/sof/amd/acp-ipc.c
+++ b/sound/soc/sof/amd/acp-ipc.c
@@ -155,6 +155,8 @@ out:
irqreturn_t acp_sof_ipc_irq_thread(int irq, void *context)
{
struct snd_sof_dev *sdev = context;
+ const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
+ struct acp_dev_data *adata = sdev->pdata->hw_pdata;
unsigned int dsp_msg_write = sdev->debug_box.offset +
offsetof(struct scratch_ipc_conf, sof_dsp_msg_write);
unsigned int dsp_ack_write = sdev->debug_box.offset +
@@ -200,6 +202,30 @@ irqreturn_t acp_sof_ipc_irq_thread(int irq, void *context)
return IRQ_HANDLED;
}
+ if (desc->probe_reg_offset) {
+ u32 val;
+ u32 posn;
+
+ /* Probe register consists of two parts
+ * (0-30) bit has cumulative position value
+ * 31 bit is a synchronization flag between DSP and CPU
+ * for the position update
+ */
+ val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->probe_reg_offset);
+ if (val & PROBE_STATUS_BIT) {
+ posn = val & ~PROBE_STATUS_BIT;
+ if (adata->probe_stream) {
+ /* Probe related posn value is of 31 bits limited to 2GB
+ * once wrapped DSP won't send posn interrupt.
+ */
+ adata->probe_stream->cstream_posn = posn;
+ snd_compr_fragment_elapsed(adata->probe_stream->cstream);
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->probe_reg_offset, posn);
+ ipc_irq = true;
+ }
+ }
+ }
+
if (!ipc_irq)
dev_dbg_ratelimited(sdev->dev, "nothing to do in IPC IRQ thread\n");
diff --git a/sound/soc/sof/amd/acp-probes.c b/sound/soc/sof/amd/acp-probes.c
new file mode 100644
index 000000000000..778cf1a8b610
--- /dev/null
+++ b/sound/soc/sof/amd/acp-probes.c
@@ -0,0 +1,147 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2023 Advanced Micro Devices, Inc.
+//
+// Authors: V Sujith Kumar Reddy <Vsujithkumar.Reddy@amd.com>
+
+/*
+ * Probe interface for generic AMD audio ACP DSP block
+ */
+
+#include <linux/module.h>
+#include <sound/soc.h>
+#include "../sof-priv.h"
+#include "../sof-client-probes.h"
+#include "../sof-client.h"
+#include "../ops.h"
+#include "acp.h"
+#include "acp-dsp-offset.h"
+
+static int acp_probes_compr_startup(struct sof_client_dev *cdev,
+ struct snd_compr_stream *cstream,
+ struct snd_soc_dai *dai, u32 *stream_id)
+{
+ struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
+ struct acp_dsp_stream *stream;
+ struct acp_dev_data *adata;
+
+ adata = sdev->pdata->hw_pdata;
+ stream = acp_dsp_stream_get(sdev, 0);
+ if (!stream)
+ return -ENODEV;
+
+ stream->cstream = cstream;
+ cstream->runtime->private_data = stream;
+
+ adata->probe_stream = stream;
+ *stream_id = stream->stream_tag;
+
+ return 0;
+}
+
+static int acp_probes_compr_shutdown(struct sof_client_dev *cdev,
+ struct snd_compr_stream *cstream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
+ struct acp_dsp_stream *stream = cstream->runtime->private_data;
+ struct acp_dev_data *adata;
+ int ret;
+
+ ret = acp_dsp_stream_put(sdev, stream);
+ if (ret < 0) {
+ dev_err(sdev->dev, "Failed to release probe compress stream\n");
+ return ret;
+ }
+
+ adata = sdev->pdata->hw_pdata;
+ stream->cstream = NULL;
+ cstream->runtime->private_data = NULL;
+ adata->probe_stream = NULL;
+
+ return 0;
+}
+
+static int acp_probes_compr_set_params(struct sof_client_dev *cdev,
+ struct snd_compr_stream *cstream,
+ struct snd_compr_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
+ struct acp_dsp_stream *stream = cstream->runtime->private_data;
+ unsigned int buf_offset, index;
+ u32 size;
+ int ret;
+
+ stream->dmab = cstream->runtime->dma_buffer_p;
+ stream->num_pages = PFN_UP(cstream->runtime->dma_bytes);
+ size = cstream->runtime->buffer_size;
+
+ ret = acp_dsp_stream_config(sdev, stream);
+ if (ret < 0) {
+ acp_dsp_stream_put(sdev, stream);
+ return ret;
+ }
+
+ /* write buffer size of stream in scratch memory */
+
+ buf_offset = sdev->debug_box.offset +
+ offsetof(struct scratch_reg_conf, buf_size);
+ index = stream->stream_tag - 1;
+ buf_offset = buf_offset + index * 4;
+
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + buf_offset, size);
+
+ return 0;
+}
+
+static int acp_probes_compr_trigger(struct sof_client_dev *cdev,
+ struct snd_compr_stream *cstream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ /* Nothing to do here, as it is a mandatory callback just defined */
+ return 0;
+}
+
+static int acp_probes_compr_pointer(struct sof_client_dev *cdev,
+ struct snd_compr_stream *cstream,
+ struct snd_compr_tstamp *tstamp,
+ struct snd_soc_dai *dai)
+{
+ struct acp_dsp_stream *stream = cstream->runtime->private_data;
+ struct snd_soc_pcm_stream *pstream;
+
+ pstream = &dai->driver->capture;
+ tstamp->copied_total = stream->cstream_posn;
+ tstamp->sampling_rate = snd_pcm_rate_bit_to_rate(pstream->rates);
+
+ return 0;
+}
+
+/* SOF client implementation */
+static const struct sof_probes_host_ops acp_probes_ops = {
+ .startup = acp_probes_compr_startup,
+ .shutdown = acp_probes_compr_shutdown,
+ .set_params = acp_probes_compr_set_params,
+ .trigger = acp_probes_compr_trigger,
+ .pointer = acp_probes_compr_pointer,
+};
+
+int acp_probes_register(struct snd_sof_dev *sdev)
+{
+ return sof_client_dev_register(sdev, "acp-probes", 0, &acp_probes_ops,
+ sizeof(acp_probes_ops));
+}
+EXPORT_SYMBOL_NS(acp_probes_register, SND_SOC_SOF_AMD_COMMON);
+
+void acp_probes_unregister(struct snd_sof_dev *sdev)
+{
+ sof_client_dev_unregister(sdev, "acp-probes", 0);
+}
+EXPORT_SYMBOL_NS(acp_probes_unregister, SND_SOC_SOF_AMD_COMMON);
+
+MODULE_IMPORT_NS(SND_SOC_SOF_CLIENT);
+
diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c
index 2ae76bcd3590..c450931ae77e 100644
--- a/sound/soc/sof/amd/acp.c
+++ b/sound/soc/sof/amd/acp.c
@@ -28,12 +28,14 @@ static int smn_write(struct pci_dev *dev, u32 smn_addr, u32 data)
return 0;
}
-static int smn_read(struct pci_dev *dev, u32 smn_addr, u32 *data)
+static int smn_read(struct pci_dev *dev, u32 smn_addr)
{
+ u32 data = 0;
+
pci_write_config_dword(dev, 0x60, smn_addr);
- pci_read_config_dword(dev, 0x64, data);
+ pci_read_config_dword(dev, 0x64, &data);
- return 0;
+ return data;
}
static void init_dma_descriptor(struct acp_dev_data *adata)
@@ -150,15 +152,13 @@ int configure_and_run_dma(struct acp_dev_data *adata, unsigned int src_addr,
static int psp_mbox_ready(struct acp_dev_data *adata, bool ack)
{
struct snd_sof_dev *sdev = adata->dev;
- int timeout;
+ int ret;
u32 data;
- for (timeout = ACP_PSP_TIMEOUT_COUNTER; timeout > 0; timeout--) {
- msleep(20);
- smn_read(adata->smn_dev, MP0_C2PMSG_114_REG, &data);
- if (data & MBOX_READY_MASK)
- return 0;
- }
+ ret = read_poll_timeout(smn_read, data, data & MBOX_READY_MASK, MBOX_DELAY_US,
+ ACP_PSP_TIMEOUT_US, false, adata->smn_dev, MP0_C2PMSG_114_REG);
+ if (!ret)
+ return 0;
dev_err(sdev->dev, "PSP error status %x\n", data & MBOX_STATUS_MASK);
@@ -177,23 +177,19 @@ static int psp_mbox_ready(struct acp_dev_data *adata, bool ack)
static int psp_send_cmd(struct acp_dev_data *adata, int cmd)
{
struct snd_sof_dev *sdev = adata->dev;
- int ret, timeout;
+ int ret;
u32 data;
if (!cmd)
return -EINVAL;
/* Get a non-zero Doorbell value from PSP */
- for (timeout = ACP_PSP_TIMEOUT_COUNTER; timeout > 0; timeout--) {
- msleep(MBOX_DELAY);
- smn_read(adata->smn_dev, MP0_C2PMSG_73_REG, &data);
- if (data)
- break;
- }
+ ret = read_poll_timeout(smn_read, data, data, MBOX_DELAY_US, ACP_PSP_TIMEOUT_US, false,
+ adata->smn_dev, MP0_C2PMSG_73_REG);
- if (!timeout) {
+ if (ret) {
dev_err(sdev->dev, "Failed to get Doorbell from MBOX %x\n", MP0_C2PMSG_73_REG);
- return -EINVAL;
+ return ret;
}
/* Check if PSP is ready for new command */
@@ -217,6 +213,7 @@ int configure_and_run_sha_dma(struct acp_dev_data *adata, void *image_addr,
unsigned int image_length)
{
struct snd_sof_dev *sdev = adata->dev;
+ const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
unsigned int tx_count, fw_qualifier, val;
int ret;
@@ -251,9 +248,12 @@ int configure_and_run_sha_dma(struct acp_dev_data *adata, void *image_addr,
return ret;
}
- ret = psp_send_cmd(adata, MBOX_ACP_SHA_DMA_COMMAND);
- if (ret)
- return ret;
+ /* psp_send_cmd only required for renoir platform (rev - 3) */
+ if (desc->rev == 3) {
+ ret = psp_send_cmd(adata, MBOX_ACP_SHA_DMA_COMMAND);
+ if (ret)
+ return ret;
+ }
ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_SHA_DSP_FW_QUALIFIER,
fw_qualifier, fw_qualifier & DSP_FW_RUN_ENABLE,
diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h
index dc624f727aa3..72fa0af971f0 100644
--- a/sound/soc/sof/amd/acp.h
+++ b/sound/soc/sof/amd/acp.h
@@ -61,12 +61,12 @@
#define HOST_BRIDGE_CZN 0x1630
#define HOST_BRIDGE_RMB 0x14B5
#define ACP_SHA_STAT 0x8000
-#define ACP_PSP_TIMEOUT_COUNTER 5
+#define ACP_PSP_TIMEOUT_US 1000000
#define ACP_EXT_INTR_ERROR_STAT 0x20000000
#define MP0_C2PMSG_114_REG 0x3810AC8
#define MP0_C2PMSG_73_REG 0x3810A24
#define MBOX_ACP_SHA_DMA_COMMAND 0x70000
-#define MBOX_DELAY 1000
+#define MBOX_DELAY_US 1000
#define MBOX_READY_MASK 0x80000000
#define MBOX_STATUS_MASK 0xFFFF
@@ -77,6 +77,7 @@
#define AMD_STACK_DUMP_SIZE 32
#define SRAM1_SIZE 0x13A000
+#define PROBE_STATUS_BIT BIT(31)
enum clock_source {
ACP_CLOCK_96M = 0,
@@ -156,6 +157,8 @@ struct acp_dsp_stream {
int active;
unsigned int reg_offset;
size_t posn_offset;
+ struct snd_compr_stream *cstream;
+ u64 cstream_posn;
};
struct sof_amd_acp_desc {
@@ -168,6 +171,7 @@ struct sof_amd_acp_desc {
u32 hw_semaphore_offset;
u32 acp_clkmux_sel;
u32 fusion_dsp_offset;
+ u32 probe_reg_offset;
};
/* Common device data struct for ACP devices */
@@ -186,6 +190,7 @@ struct acp_dev_data {
struct acp_dsp_stream stream_buf[ACP_MAX_STREAM];
struct acp_dsp_stream *dtrace_stream;
struct pci_dev *smn_dev;
+ struct acp_dsp_stream *probe_stream;
};
void memcpy_to_scratch(struct snd_sof_dev *sdev, u32 offset, unsigned int *src, size_t bytes);
@@ -273,4 +278,8 @@ static inline const struct sof_amd_acp_desc *get_chip_info(struct snd_sof_pdata
return desc->chip_info;
}
+
+int acp_probes_register(struct snd_sof_dev *sdev);
+void acp_probes_unregister(struct snd_sof_dev *sdev);
+
#endif
diff --git a/sound/soc/sof/amd/pci-rmb.c b/sound/soc/sof/amd/pci-rmb.c
index 58b3092425f1..9935e457b467 100644
--- a/sound/soc/sof/amd/pci-rmb.c
+++ b/sound/soc/sof/amd/pci-rmb.c
@@ -25,6 +25,7 @@
#define ACP6x_REG_START 0x1240000
#define ACP6x_REG_END 0x125C000
+#define ACP6X_FUTURE_REG_ACLK_0 0x1854
static const struct sof_amd_acp_desc rembrandt_chip_info = {
.rev = 6,
@@ -36,6 +37,7 @@ static const struct sof_amd_acp_desc rembrandt_chip_info = {
.hw_semaphore_offset = ACP6X_AXI2DAGB_SEM_0,
.acp_clkmux_sel = ACP6X_CLKMUX_SEL,
.fusion_dsp_offset = ACP6X_DSP_FUSION_RUNSTALL,
+ .probe_reg_offset = ACP6X_FUTURE_REG_ACLK_0,
};
static const struct sof_dev_desc rembrandt_desc = {
diff --git a/sound/soc/sof/amd/pci-rn.c b/sound/soc/sof/amd/pci-rn.c
index 7409e21ce5aa..c72d5d8aff8e 100644
--- a/sound/soc/sof/amd/pci-rn.c
+++ b/sound/soc/sof/amd/pci-rn.c
@@ -25,6 +25,7 @@
#define ACP3x_REG_START 0x1240000
#define ACP3x_REG_END 0x125C000
+#define ACP3X_FUTURE_REG_ACLK_0 0x1860
static const struct sof_amd_acp_desc renoir_chip_info = {
.rev = 3,
@@ -35,6 +36,7 @@ static const struct sof_amd_acp_desc renoir_chip_info = {
.sram_pte_offset = ACP3X_SRAM_PTE_OFFSET,
.hw_semaphore_offset = ACP3X_AXI2DAGB_SEM_0,
.acp_clkmux_sel = ACP3X_CLKMUX_SEL,
+ .probe_reg_offset = ACP3X_FUTURE_REG_ACLK_0,
};
static const struct sof_dev_desc renoir_desc = {
diff --git a/sound/soc/sof/ipc3-dtrace.c b/sound/soc/sof/ipc3-dtrace.c
index 1d3bca2d28dd..35da85a45a9a 100644
--- a/sound/soc/sof/ipc3-dtrace.c
+++ b/sound/soc/sof/ipc3-dtrace.c
@@ -186,7 +186,6 @@ static ssize_t dfsentry_trace_filter_write(struct file *file, const char __user
struct snd_sof_dfsentry *dfse = file->private_data;
struct sof_ipc_trace_filter_elem *elems = NULL;
struct snd_sof_dev *sdev = dfse->sdev;
- loff_t pos = 0;
int num_elems;
char *string;
int ret;
@@ -201,11 +200,11 @@ static ssize_t dfsentry_trace_filter_write(struct file *file, const char __user
if (!string)
return -ENOMEM;
- /* assert null termination */
- string[count] = 0;
- ret = simple_write_to_buffer(string, count, &pos, from, count);
- if (ret < 0)
+ if (copy_from_user(string, from, count)) {
+ ret = -EFAULT;
goto error;
+ }
+ string[count] = '\0';
ret = trace_filter_parse(sdev, string, &num_elems, &elems);
if (ret < 0)
diff --git a/sound/soc/sof/sof-client-probes.c b/sound/soc/sof/sof-client-probes.c
index 8d9e9d5f40e4..5530b5d793d0 100644
--- a/sound/soc/sof/sof-client-probes.c
+++ b/sound/soc/sof/sof-client-probes.c
@@ -523,6 +523,7 @@ static void sof_probes_client_remove(struct auxiliary_device *auxdev)
static const struct auxiliary_device_id sof_probes_client_id_table[] = {
{ .name = "snd_sof.hda-probes", },
+ { .name = "snd_sof.acp-probes", },
{},
};
MODULE_DEVICE_TABLE(auxiliary, sof_probes_client_id_table);
diff --git a/sound/soc/starfive/jh7110_tdm.c b/sound/soc/starfive/jh7110_tdm.c
index 5f5a6ca7dbda..705f1420097b 100644
--- a/sound/soc/starfive/jh7110_tdm.c
+++ b/sound/soc/starfive/jh7110_tdm.c
@@ -634,10 +634,9 @@ err_pm_disable:
return ret;
}
-static int jh7110_tdm_dev_remove(struct platform_device *pdev)
+static void jh7110_tdm_dev_remove(struct platform_device *pdev)
{
pm_runtime_disable(&pdev->dev);
- return 0;
}
static const struct of_device_id jh7110_tdm_of_match[] = {
@@ -661,7 +660,7 @@ static struct platform_driver jh7110_tdm_driver = {
.pm = pm_ptr(&jh7110_tdm_pm_ops),
},
.probe = jh7110_tdm_probe,
- .remove = jh7110_tdm_dev_remove,
+ .remove_new = jh7110_tdm_dev_remove,
};
module_platform_driver(jh7110_tdm_driver);
diff --git a/sound/soc/tegra/tegra20_ac97.c b/sound/soc/tegra/tegra20_ac97.c
index a4073a746ae3..60e7df41c64c 100644
--- a/sound/soc/tegra/tegra20_ac97.c
+++ b/sound/soc/tegra/tegra20_ac97.c
@@ -328,8 +328,7 @@ static int tegra20_ac97_platform_probe(struct platform_device *pdev)
goto err;
}
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- regs = devm_ioremap_resource(&pdev->dev, mem);
+ regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem);
if (IS_ERR(regs)) {
ret = PTR_ERR(regs);
goto err_clk_put;
diff --git a/sound/soc/tegra/tegra210_adx.c b/sound/soc/tegra/tegra210_adx.c
index bd0b10c70c4c..7d003f0c8d0f 100644
--- a/sound/soc/tegra/tegra210_adx.c
+++ b/sound/soc/tegra/tegra210_adx.c
@@ -2,7 +2,7 @@
//
// tegra210_adx.c - Tegra210 ADX driver
//
-// Copyright (c) 2021 NVIDIA CORPORATION. All rights reserved.
+// Copyright (c) 2021-2023 NVIDIA CORPORATION. All rights reserved.
#include <linux/clk.h>
#include <linux/device.h>
@@ -175,10 +175,20 @@ static int tegra210_adx_get_byte_map(struct snd_kcontrol *kcontrol,
mc = (struct soc_mixer_control *)kcontrol->private_value;
enabled = adx->byte_mask[mc->reg / 32] & (1 << (mc->reg % 32));
+ /*
+ * TODO: Simplify this logic to just return from bytes_map[]
+ *
+ * Presently below is required since bytes_map[] is
+ * tightly packed and cannot store the control value of 256.
+ * Byte mask state is used to know if 256 needs to be returned.
+ * Note that for control value of 256, the put() call stores 0
+ * in the bytes_map[] and disables the corresponding bit in
+ * byte_mask[].
+ */
if (enabled)
ucontrol->value.integer.value[0] = bytes_map[mc->reg];
else
- ucontrol->value.integer.value[0] = 0;
+ ucontrol->value.integer.value[0] = 256;
return 0;
}
@@ -192,19 +202,19 @@ static int tegra210_adx_put_byte_map(struct snd_kcontrol *kcontrol,
int value = ucontrol->value.integer.value[0];
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int mask_val = adx->byte_mask[mc->reg / 32];
- if (value == bytes_map[mc->reg])
+ if (value >= 0 && value <= 255)
+ mask_val |= (1 << (mc->reg % 32));
+ else
+ mask_val &= ~(1 << (mc->reg % 32));
+
+ if (mask_val == adx->byte_mask[mc->reg / 32])
return 0;
- if (value >= 0 && value <= 255) {
- /* update byte map and enable slot */
- bytes_map[mc->reg] = value;
- adx->byte_mask[mc->reg / 32] |= (1 << (mc->reg % 32));
- } else {
- /* reset byte map and disable slot */
- bytes_map[mc->reg] = 0;
- adx->byte_mask[mc->reg / 32] &= ~(1 << (mc->reg % 32));
- }
+ /* Update byte map and slot */
+ bytes_map[mc->reg] = value % 256;
+ adx->byte_mask[mc->reg / 32] = mask_val;
return 1;
}
diff --git a/sound/soc/tegra/tegra210_amx.c b/sound/soc/tegra/tegra210_amx.c
index 782a141b65c0..179876949b30 100644
--- a/sound/soc/tegra/tegra210_amx.c
+++ b/sound/soc/tegra/tegra210_amx.c
@@ -2,7 +2,7 @@
//
// tegra210_amx.c - Tegra210 AMX driver
//
-// Copyright (c) 2021 NVIDIA CORPORATION. All rights reserved.
+// Copyright (c) 2021-2023 NVIDIA CORPORATION. All rights reserved.
#include <linux/clk.h>
#include <linux/device.h>
@@ -203,10 +203,20 @@ static int tegra210_amx_get_byte_map(struct snd_kcontrol *kcontrol,
else
enabled = amx->byte_mask[0] & (1 << reg);
+ /*
+ * TODO: Simplify this logic to just return from bytes_map[]
+ *
+ * Presently below is required since bytes_map[] is
+ * tightly packed and cannot store the control value of 256.
+ * Byte mask state is used to know if 256 needs to be returned.
+ * Note that for control value of 256, the put() call stores 0
+ * in the bytes_map[] and disables the corresponding bit in
+ * byte_mask[].
+ */
if (enabled)
ucontrol->value.integer.value[0] = bytes_map[reg];
else
- ucontrol->value.integer.value[0] = 0;
+ ucontrol->value.integer.value[0] = 256;
return 0;
}
@@ -221,25 +231,19 @@ static int tegra210_amx_put_byte_map(struct snd_kcontrol *kcontrol,
unsigned char *bytes_map = (unsigned char *)&amx->map;
int reg = mc->reg;
int value = ucontrol->value.integer.value[0];
+ unsigned int mask_val = amx->byte_mask[reg / 32];
- if (value == bytes_map[reg])
+ if (value >= 0 && value <= 255)
+ mask_val |= (1 << (reg % 32));
+ else
+ mask_val &= ~(1 << (reg % 32));
+
+ if (mask_val == amx->byte_mask[reg / 32])
return 0;
- if (value >= 0 && value <= 255) {
- /* Update byte map and enable slot */
- bytes_map[reg] = value;
- if (reg > 31)
- amx->byte_mask[1] |= (1 << (reg - 32));
- else
- amx->byte_mask[0] |= (1 << reg);
- } else {
- /* Reset byte map and disable slot */
- bytes_map[reg] = 0;
- if (reg > 31)
- amx->byte_mask[1] &= ~(1 << (reg - 32));
- else
- amx->byte_mask[0] &= ~(1 << reg);
- }
+ /* Update byte map and slot */
+ bytes_map[reg] = value % 256;
+ amx->byte_mask[reg / 32] = mask_val;
return 1;
}
diff --git a/sound/soc/ti/omap-dmic.c b/sound/soc/ti/omap-dmic.c
index 825c70a443da..cb60af36dbc3 100644
--- a/sound/soc/ti/omap-dmic.c
+++ b/sound/soc/ti/omap-dmic.c
@@ -488,12 +488,10 @@ static int asoc_dmic_probe(struct platform_device *pdev)
dmic->dma_data.filter_data = "up_link";
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu");
- dmic->io_base = devm_ioremap_resource(&pdev->dev, res);
+ dmic->io_base = devm_platform_ioremap_resource_byname(pdev, "mpu");
if (IS_ERR(dmic->io_base))
return PTR_ERR(dmic->io_base);
-
ret = devm_snd_soc_register_component(&pdev->dev,
&omap_dmic_component,
&omap_dmic_dai, 1);
diff --git a/sound/soc/ti/omap-mcbsp.c b/sound/soc/ti/omap-mcbsp.c
index 21fa7b978799..f9fe96b61852 100644
--- a/sound/soc/ti/omap-mcbsp.c
+++ b/sound/soc/ti/omap-mcbsp.c
@@ -70,8 +70,8 @@ static int omap2_mcbsp_set_clks_src(struct omap_mcbsp *mcbsp, u8 fck_src_id)
fck_src = clk_get(mcbsp->dev, src);
if (IS_ERR(fck_src)) {
- dev_err(mcbsp->dev, "CLKS: could not clk_get() %s\n", src);
- return -EINVAL;
+ dev_info(mcbsp->dev, "CLKS: could not clk_get() %s\n", src);
+ return 0;
}
pm_runtime_put_sync(mcbsp->dev);
diff --git a/sound/soc/ti/omap-mcpdm.c b/sound/soc/ti/omap-mcpdm.c
index 0b18a7bfd3fd..35deceb73427 100644
--- a/sound/soc/ti/omap-mcpdm.c
+++ b/sound/soc/ti/omap-mcpdm.c
@@ -563,8 +563,7 @@ static int asoc_mcpdm_probe(struct platform_device *pdev)
mcpdm->dma_data[0].filter_data = "dn_link";
mcpdm->dma_data[1].filter_data = "up_link";
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu");
- mcpdm->io_base = devm_ioremap_resource(&pdev->dev, res);
+ mcpdm->io_base = devm_platform_ioremap_resource_byname(pdev, "mpu");
if (IS_ERR(mcpdm->io_base))
return PTR_ERR(mcpdm->io_base);