aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
Diffstat (limited to 'sound')
-rw-r--r--sound/ac97/bus.c5
-rw-r--r--sound/ac97_bus.c2
-rw-r--r--sound/core/Makefile1
-rw-r--r--sound/core/pcm.c7
-rw-r--r--sound/core/pcm_drm_eld.c1
-rw-r--r--sound/core/pcm_lib.c34
-rw-r--r--sound/core/pcm_native.c60
-rw-r--r--sound/core/seq/seq_memory.c3
-rw-r--r--sound/core/ump.c4
-rw-r--r--sound/drivers/aloop.c23
-rw-r--r--sound/drivers/serial-generic.c4
-rw-r--r--sound/firewire/amdtp-stream.c2
-rw-r--r--sound/hda/hda_bus_type.c2
-rw-r--r--sound/hda/hdac_device.c156
-rw-r--r--sound/hda/hdac_stream.c9
-rw-r--r--sound/hda/intel-dsp-config.c10
-rw-r--r--sound/isa/wavefront/wavefront_fx.c6
-rw-r--r--sound/pci/au88x0/au88x0.c2
-rw-r--r--sound/pci/au88x0/au88x0_core.c10
-rw-r--r--sound/pci/hda/Kconfig4
-rw-r--r--sound/pci/hda/cs35l41_hda.c25
-rw-r--r--sound/pci/hda/cs35l41_hda.h12
-rw-r--r--sound/pci/hda/cs35l41_hda_i2c.c2
-rw-r--r--sound/pci/hda/cs35l41_hda_property.c140
-rw-r--r--sound/pci/hda/cs35l41_hda_spi.c2
-rw-r--r--sound/pci/hda/cs35l56_hda.c136
-rw-r--r--sound/pci/hda/cs35l56_hda_spi.c2
-rw-r--r--sound/pci/hda/hda_codec.c2
-rw-r--r--sound/pci/hda/hda_controller.c13
-rw-r--r--sound/pci/hda/hda_generic.c1
-rw-r--r--sound/pci/hda/hda_intel.c8
-rw-r--r--sound/pci/hda/patch_ca0132.c3
-rw-r--r--sound/pci/hda/patch_conexant.c133
-rw-r--r--sound/pci/hda/patch_cs8409.c1
-rw-r--r--sound/pci/hda/patch_hdmi.c12
-rw-r--r--sound/pci/hda/patch_realtek.c112
-rw-r--r--sound/pci/hda/tas2781_hda_i2c.c119
-rw-r--r--sound/pci/oxygen/oxygen_mixer.c2
-rw-r--r--sound/soc/amd/acp-config.c39
-rw-r--r--sound/soc/amd/acp/Kconfig13
-rw-r--r--sound/soc/amd/acp/Makefile2
-rw-r--r--sound/soc/amd/acp/acp-mach-common.c75
-rw-r--r--sound/soc/amd/acp/acp-mach.h3
-rw-r--r--sound/soc/amd/acp/acp-renoir.c37
-rw-r--r--sound/soc/amd/acp/acp-sof-mach.c8
-rw-r--r--sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c11
-rw-r--r--sound/soc/amd/acp/acp63.c22
-rw-r--r--sound/soc/amd/acp/acp70.c28
-rw-r--r--sound/soc/amd/vangogh/acp5x-mach.c35
-rw-r--r--sound/soc/amd/vangogh/pci-acp5x.c22
-rw-r--r--sound/soc/amd/yc/acp6x-mach.c42
-rw-r--r--sound/soc/amd/yc/pci-acp6x.c1
-rw-r--r--sound/soc/cirrus/edb93xx.c1
-rw-r--r--sound/soc/codecs/Kconfig4
-rw-r--r--sound/soc/codecs/Makefile2
-rw-r--r--sound/soc/codecs/aw88395/aw88395_device.h1
-rw-r--r--sound/soc/codecs/aw88395/aw88395_lib.c124
-rw-r--r--sound/soc/codecs/aw88395/aw88395_reg.h3
-rw-r--r--sound/soc/codecs/aw88399.c1
-rw-r--r--sound/soc/codecs/aw88399.h1
-rw-r--r--sound/soc/codecs/cs35l32.c1
-rw-r--r--sound/soc/codecs/cs35l33.c4
-rw-r--r--sound/soc/codecs/cs35l34.c4
-rw-r--r--sound/soc/codecs/cs35l35.c2
-rw-r--r--sound/soc/codecs/cs35l36.c3
-rw-r--r--sound/soc/codecs/cs35l45.c2
-rw-r--r--sound/soc/codecs/cs35l56-shared.c142
-rw-r--r--sound/soc/codecs/cs35l56.c383
-rw-r--r--sound/soc/codecs/cs35l56.h2
-rw-r--r--sound/soc/codecs/cs4271.c39
-rw-r--r--sound/soc/codecs/cs42l42.c1
-rw-r--r--sound/soc/codecs/cs42l42.h2
-rw-r--r--sound/soc/codecs/cs42l43-jack.c4
-rw-r--r--sound/soc/codecs/cs42l43.c136
-rw-r--r--sound/soc/codecs/cs42l43.h9
-rw-r--r--sound/soc/codecs/cs43130.c320
-rw-r--r--sound/soc/codecs/cs43130.h3
-rw-r--r--sound/soc/codecs/cs4349.c1
-rw-r--r--sound/soc/codecs/es8326.c393
-rw-r--r--sound/soc/codecs/es8326.h3
-rw-r--r--sound/soc/codecs/es83xx-dsm-common.c89
-rw-r--r--sound/soc/codecs/es83xx-dsm-common.h393
-rw-r--r--sound/soc/codecs/hda-dai.c7
-rw-r--r--sound/soc/codecs/hda.c2
-rw-r--r--sound/soc/codecs/hdac_hda.c8
-rw-r--r--sound/soc/codecs/hdac_hdmi.c11
-rw-r--r--sound/soc/codecs/hdmi-codec.c1
-rw-r--r--sound/soc/codecs/lpass-wsa-macro.c7
-rw-r--r--sound/soc/codecs/madera.c2
-rw-r--r--sound/soc/codecs/nau8810.c9
-rw-r--r--sound/soc/codecs/nau8821.c7
-rw-r--r--sound/soc/codecs/nau8821.h3
-rw-r--r--sound/soc/codecs/rt5645.c137
-rw-r--r--sound/soc/codecs/rt5645.h3
-rw-r--r--sound/soc/codecs/rt5663.c11
-rw-r--r--sound/soc/codecs/rt5682s.c23
-rw-r--r--sound/soc/codecs/rt5682s.h7
-rw-r--r--sound/soc/codecs/rt722-sdca-sdw.c3
-rw-r--r--sound/soc/codecs/rtq9128.c73
-rw-r--r--sound/soc/codecs/tas2562.c3
-rw-r--r--sound/soc/codecs/tas2781-comlib.c18
-rw-r--r--sound/soc/codecs/tas2781-fmwlib.c1
-rw-r--r--sound/soc/codecs/tas2781-i2c.c10
-rw-r--r--sound/soc/codecs/wcd9335.c4
-rw-r--r--sound/soc/codecs/wcd934x.c1
-rw-r--r--sound/soc/codecs/wcd938x.c8
-rw-r--r--sound/soc/codecs/wm0010.c44
-rw-r--r--sound/soc/codecs/wm1250-ev1.c119
-rw-r--r--sound/soc/codecs/wm2200.c67
-rw-r--r--sound/soc/codecs/wm5100.c107
-rw-r--r--sound/soc/codecs/wm8962.c29
-rw-r--r--sound/soc/codecs/wm8996.c58
-rw-r--r--sound/soc/codecs/wm_adsp.c73
-rw-r--r--sound/soc/codecs/wsa883x.c6
-rw-r--r--sound/soc/codecs/wsa884x.c7
-rw-r--r--sound/soc/fsl/Kconfig14
-rw-r--r--sound/soc/fsl/Makefile4
-rw-r--r--sound/soc/fsl/fsl-asoc-card.c3
-rw-r--r--sound/soc/fsl/fsl_mqs.c2
-rw-r--r--sound/soc/fsl/fsl_qmc_audio.c2
-rw-r--r--sound/soc/fsl/fsl_xcvr.c12
-rw-r--r--sound/soc/fsl/imx-rpmsg.c61
-rw-r--r--sound/soc/fsl/mpc8610_hpcd.c451
-rw-r--r--sound/soc/generic/audio-graph-card.c1
-rw-r--r--sound/soc/generic/audio-graph-card2-custom-sample.dtsi380
-rw-r--r--sound/soc/generic/audio-graph-card2.c284
-rw-r--r--sound/soc/generic/simple-card-utils.c20
-rw-r--r--sound/soc/hisilicon/hi6210-i2s.c1
-rw-r--r--sound/soc/intel/avs/boards/da7219.c10
-rw-r--r--sound/soc/intel/avs/boards/dmic.c10
-rw-r--r--sound/soc/intel/avs/boards/es8336.c10
-rw-r--r--sound/soc/intel/avs/boards/hdaudio.c16
-rw-r--r--sound/soc/intel/avs/boards/i2s_test.c10
-rw-r--r--sound/soc/intel/avs/boards/max98357a.c10
-rw-r--r--sound/soc/intel/avs/boards/max98373.c10
-rw-r--r--sound/soc/intel/avs/boards/max98927.c10
-rw-r--r--sound/soc/intel/avs/boards/nau8825.c10
-rw-r--r--sound/soc/intel/avs/boards/probe.c10
-rw-r--r--sound/soc/intel/avs/boards/rt274.c10
-rw-r--r--sound/soc/intel/avs/boards/rt286.c10
-rw-r--r--sound/soc/intel/avs/boards/rt298.c10
-rw-r--r--sound/soc/intel/avs/boards/rt5514.c10
-rw-r--r--sound/soc/intel/avs/boards/rt5663.c10
-rw-r--r--sound/soc/intel/avs/boards/rt5682.c10
-rw-r--r--sound/soc/intel/avs/boards/ssm4567.c10
-rw-r--r--sound/soc/intel/avs/core.c3
-rw-r--r--sound/soc/intel/avs/loader.c4
-rw-r--r--sound/soc/intel/avs/path.c2
-rw-r--r--sound/soc/intel/avs/pcm.c58
-rw-r--r--sound/soc/intel/avs/probes.c3
-rw-r--r--sound/soc/intel/avs/topology.c15
-rw-r--r--sound/soc/intel/boards/Kconfig2
-rw-r--r--sound/soc/intel/boards/Makefile3
-rw-r--r--sound/soc/intel/boards/bxt_da7219_max98357a.c6
-rw-r--r--sound/soc/intel/boards/bxt_rt298.c3
-rw-r--r--sound/soc/intel/boards/bytcht_cx2072x.c3
-rw-r--r--sound/soc/intel/boards/bytcht_da7213.c3
-rw-r--r--sound/soc/intel/boards/bytcht_es8316.c74
-rw-r--r--sound/soc/intel/boards/bytcr_rt5640.c15
-rw-r--r--sound/soc/intel/boards/bytcr_rt5651.c3
-rw-r--r--sound/soc/intel/boards/bytcr_wm5102.c3
-rw-r--r--sound/soc/intel/boards/cht_bsw_rt5645.c15
-rw-r--r--sound/soc/intel/boards/cht_bsw_rt5672.c11
-rw-r--r--sound/soc/intel/boards/sof_board_helpers.c330
-rw-r--r--sound/soc/intel/boards/sof_board_helpers.h31
-rw-r--r--sound/soc/intel/boards/sof_cs42l42.c229
-rw-r--r--sound/soc/intel/boards/sof_maxim_common.c13
-rw-r--r--sound/soc/intel/boards/sof_nau8825.c270
-rw-r--r--sound/soc/intel/boards/sof_rt5682.c337
-rw-r--r--sound/soc/intel/boards/sof_sdw.c64
-rw-r--r--sound/soc/intel/boards/sof_sdw_common.h18
-rw-r--r--sound/soc/intel/boards/sof_sdw_cs_amp.c30
-rw-r--r--sound/soc/intel/boards/sof_sdw_rt722_sdca.c97
-rw-r--r--sound/soc/intel/boards/sof_sdw_rt_sdca_jack_common.c15
-rw-r--r--sound/soc/intel/boards/sof_ssp_amp.c160
-rw-r--r--sound/soc/intel/boards/sof_ssp_common.c21
-rw-r--r--sound/soc/intel/boards/sof_ssp_common.h1
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-adl-match.c8
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-glk-match.c14
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-lnl-match.c71
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-mtl-match.c86
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-rpl-match.c6
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-tgl-match.c78
-rw-r--r--sound/soc/intel/skylake/skl-pcm.c13
-rw-r--r--sound/soc/mediatek/Kconfig1
-rw-r--r--sound/soc/mediatek/common/mtk-dsp-sof-common.c2
-rw-r--r--sound/soc/mediatek/mt7986/mt7986-wm8960.c2
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-mt6359.c125
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c3
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-afe-pcm.c33
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-mt6359.c41
-rw-r--r--sound/soc/pxa/Kconfig1
-rw-r--r--sound/soc/qcom/Kconfig12
-rw-r--r--sound/soc/qcom/Makefile2
-rw-r--r--sound/soc/qcom/apq8016_sbc.c2
-rw-r--r--sound/soc/qcom/apq8096.c2
-rw-r--r--sound/soc/qcom/common.c2
-rw-r--r--sound/soc/qcom/lpass-apq8016.c2
-rw-r--r--sound/soc/qcom/lpass-cdc-dma.c2
-rw-r--r--sound/soc/qcom/lpass-cpu.c2
-rw-r--r--sound/soc/qcom/lpass-hdmi.c2
-rw-r--r--sound/soc/qcom/lpass-ipq806x.c2
-rw-r--r--sound/soc/qcom/lpass-platform.c2
-rw-r--r--sound/soc/qcom/lpass-sc7180.c2
-rw-r--r--sound/soc/qcom/lpass.h2
-rw-r--r--sound/soc/qcom/qdsp6/audioreach.c55
-rw-r--r--sound/soc/qcom/qdsp6/audioreach.h2
-rw-r--r--sound/soc/qcom/qdsp6/q6afe.c8
-rw-r--r--sound/soc/qcom/qdsp6/q6apm-dai.c12
-rw-r--r--sound/soc/qcom/qdsp6/q6asm.h20
-rw-r--r--sound/soc/qcom/qdsp6/topology.c3
-rw-r--r--sound/soc/qcom/sc7180.c3
-rw-r--r--sound/soc/qcom/sc8280xp.c36
-rw-r--r--sound/soc/qcom/sdm845.c2
-rw-r--r--sound/soc/qcom/sdw.c47
-rw-r--r--sound/soc/qcom/sdw.h1
-rw-r--r--sound/soc/qcom/sm8250.c17
-rw-r--r--sound/soc/qcom/storm.c2
-rw-r--r--sound/soc/qcom/x1e80100.c168
-rw-r--r--sound/soc/rockchip/rk3399_gru_sound.c2
-rw-r--r--sound/soc/samsung/odroid.c3
-rw-r--r--sound/soc/sh/fsi.c4
-rw-r--r--sound/soc/sh/rcar/adg.c7
-rw-r--r--sound/soc/soc-card.c24
-rw-r--r--sound/soc/soc-core.c130
-rw-r--r--sound/soc/soc-dapm.c75
-rw-r--r--sound/soc/soc-pcm.c62
-rw-r--r--sound/soc/soc-topology-test.c10
-rw-r--r--sound/soc/sof/Kconfig11
-rw-r--r--sound/soc/sof/Makefile3
-rw-r--r--sound/soc/sof/amd/acp-common.c1
-rw-r--r--sound/soc/sof/amd/acp-ipc.c2
-rw-r--r--sound/soc/sof/amd/acp.c27
-rw-r--r--sound/soc/sof/amd/acp.h5
-rw-r--r--sound/soc/sof/core.c311
-rw-r--r--sound/soc/sof/fw-file-profile.c322
-rw-r--r--sound/soc/sof/imx/imx8.c1
-rw-r--r--sound/soc/sof/imx/imx8m.c8
-rw-r--r--sound/soc/sof/imx/imx8ulp.c1
-rw-r--r--sound/soc/sof/intel/apl.c2
-rw-r--r--sound/soc/sof/intel/cnl.c2
-rw-r--r--sound/soc/sof/intel/hda-dai-ops.c21
-rw-r--r--sound/soc/sof/intel/hda-dai.c3
-rw-r--r--sound/soc/sof/intel/hda-loader.c10
-rw-r--r--sound/soc/sof/intel/hda.c6
-rw-r--r--sound/soc/sof/intel/hda.h1
-rw-r--r--sound/soc/sof/intel/icl.c2
-rw-r--r--sound/soc/sof/intel/lnl.c8
-rw-r--r--sound/soc/sof/intel/mtl.c51
-rw-r--r--sound/soc/sof/intel/mtl.h3
-rw-r--r--sound/soc/sof/intel/pci-lnl.c2
-rw-r--r--sound/soc/sof/intel/pci-mtl.c33
-rw-r--r--sound/soc/sof/intel/pci-tgl.c64
-rw-r--r--sound/soc/sof/intel/skl.c2
-rw-r--r--sound/soc/sof/intel/tgl.c4
-rw-r--r--sound/soc/sof/ipc3-pcm.c11
-rw-r--r--sound/soc/sof/ipc3-topology.c114
-rw-r--r--sound/soc/sof/ipc3.c2
-rw-r--r--sound/soc/sof/ipc4-control.c179
-rw-r--r--sound/soc/sof/ipc4-loader.c14
-rw-r--r--sound/soc/sof/ipc4-pcm.c17
-rw-r--r--sound/soc/sof/ipc4-priv.h4
-rw-r--r--sound/soc/sof/ipc4-topology.c32
-rw-r--r--sound/soc/sof/ipc4.c117
-rw-r--r--sound/soc/sof/mediatek/adsp_helper.h4
-rw-r--r--sound/soc/sof/mediatek/mt8186/mt8186.c49
-rw-r--r--sound/soc/sof/mediatek/mt8195/mt8195.c49
-rw-r--r--sound/soc/sof/sof-acpi-dev.c16
-rw-r--r--sound/soc/sof/sof-audio.c120
-rw-r--r--sound/soc/sof/sof-audio.h1
-rw-r--r--sound/soc/sof/sof-client-probes.c4
-rw-r--r--sound/soc/sof/sof-of-dev.c13
-rw-r--r--sound/soc/sof/sof-pci-dev.c102
-rw-r--r--sound/soc/sof/sof-priv.h9
-rw-r--r--sound/soc/sof/topology.c10
-rw-r--r--sound/soc/sprd/sprd-pcm-compress.c6
-rw-r--r--sound/soc/sti/sti_uniperif.c7
-rw-r--r--sound/soc/sunxi/sun4i-spdif.c5
-rw-r--r--sound/soc/tegra/tegra20_ac97.c55
-rw-r--r--sound/soc/tegra/tegra20_ac97.h4
-rw-r--r--sound/soc/tegra/tegra_pcm.c4
-rw-r--r--sound/usb/clock.c34
-rw-r--r--sound/usb/format.c20
-rw-r--r--sound/usb/midi.c73
-rw-r--r--sound/usb/midi2.c2
-rw-r--r--sound/usb/mixer_quirks.c3
-rw-r--r--sound/usb/mixer_scarlett2.c4643
-rw-r--r--sound/usb/quirks.c38
-rw-r--r--sound/virtio/virtio_card.c2
-rw-r--r--sound/virtio/virtio_ctl_msg.c2
-rw-r--r--sound/virtio/virtio_pcm_msg.c2
-rw-r--r--sound/x86/intel_hdmi_audio.c1
292 files changed, 11296 insertions, 4194 deletions
diff --git a/sound/ac97/bus.c b/sound/ac97/bus.c
index 3173e9d98927..5e46b972a3da 100644
--- a/sound/ac97/bus.c
+++ b/sound/ac97/bus.c
@@ -15,6 +15,7 @@
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
+#include <sound/ac97_codec.h>
#include <sound/ac97/codec.h>
#include <sound/ac97/controller.h>
#include <sound/ac97/regs.h>
@@ -28,8 +29,6 @@ static DEFINE_MUTEX(ac97_controllers_mutex);
static DEFINE_IDR(ac97_adapter_idr);
static LIST_HEAD(ac97_controllers);
-static struct bus_type ac97_bus_type;
-
static inline struct ac97_controller*
to_ac97_controller(struct device *ac97_adapter)
{
@@ -531,7 +530,7 @@ static void ac97_bus_remove(struct device *dev)
pm_runtime_disable(dev);
}
-static struct bus_type ac97_bus_type = {
+const struct bus_type ac97_bus_type = {
.name = "ac97bus",
.dev_groups = ac97_dev_groups,
.match = ac97_bus_match,
diff --git a/sound/ac97_bus.c b/sound/ac97_bus.c
index c7aee8c42c55..7ea274c55900 100644
--- a/sound/ac97_bus.c
+++ b/sound/ac97_bus.c
@@ -75,7 +75,7 @@ int snd_ac97_reset(struct snd_ac97 *ac97, bool try_warm, unsigned int id,
}
EXPORT_SYMBOL_GPL(snd_ac97_reset);
-struct bus_type ac97_bus_type = {
+const struct bus_type ac97_bus_type = {
.name = "ac97",
};
diff --git a/sound/core/Makefile b/sound/core/Makefile
index a6b444ee2832..f6526b337137 100644
--- a/sound/core/Makefile
+++ b/sound/core/Makefile
@@ -32,7 +32,6 @@ snd-ump-objs := ump.o
snd-ump-$(CONFIG_SND_UMP_LEGACY_RAWMIDI) += ump_convert.o
snd-timer-objs := timer.o
snd-hrtimer-objs := hrtimer.o
-snd-rtctimer-objs := rtctimer.o
snd-hwdep-objs := hwdep.o
snd-seq-device-objs := seq_device.o
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index 6d0c9c37796c..d0788126cbab 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -211,6 +211,10 @@ static const char * const snd_pcm_format_names[] = {
FORMAT(DSD_U32_LE),
FORMAT(DSD_U16_BE),
FORMAT(DSD_U32_BE),
+ FORMAT(S20_LE),
+ FORMAT(S20_BE),
+ FORMAT(U20_LE),
+ FORMAT(U20_BE),
};
/**
@@ -266,6 +270,9 @@ static const char * const snd_pcm_access_names[] = {
static const char * const snd_pcm_subformat_names[] = {
SUBFORMAT(STD),
+ SUBFORMAT(MSBITS_MAX),
+ SUBFORMAT(MSBITS_20),
+ SUBFORMAT(MSBITS_24),
};
static const char * const snd_pcm_tstamp_mode_names[] = {
diff --git a/sound/core/pcm_drm_eld.c b/sound/core/pcm_drm_eld.c
index 07075071972d..1cdca4d4fc9c 100644
--- a/sound/core/pcm_drm_eld.c
+++ b/sound/core/pcm_drm_eld.c
@@ -6,6 +6,7 @@
#include <linux/export.h>
#include <linux/hdmi.h>
#include <drm/drm_edid.h>
+#include <drm/drm_eld.h>
#include <sound/pcm.h>
#include <sound/pcm_drm_eld.h>
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index a11cd7d6295f..41103e5c43ce 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -1706,6 +1706,40 @@ int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm,
}
EXPORT_SYMBOL(snd_pcm_hw_param_last);
+/**
+ * snd_pcm_hw_params_bits - Get the number of bits per the sample.
+ * @p: hardware parameters
+ *
+ * Return: The number of bits per sample based on the format,
+ * subformat and msbits the specified hw params has.
+ */
+int snd_pcm_hw_params_bits(const struct snd_pcm_hw_params *p)
+{
+ snd_pcm_subformat_t subformat = params_subformat(p);
+ snd_pcm_format_t format = params_format(p);
+
+ switch (format) {
+ case SNDRV_PCM_FORMAT_S32_LE:
+ case SNDRV_PCM_FORMAT_U32_LE:
+ case SNDRV_PCM_FORMAT_S32_BE:
+ case SNDRV_PCM_FORMAT_U32_BE:
+ switch (subformat) {
+ case SNDRV_PCM_SUBFORMAT_MSBITS_20:
+ return 20;
+ case SNDRV_PCM_SUBFORMAT_MSBITS_24:
+ return 24;
+ case SNDRV_PCM_SUBFORMAT_MSBITS_MAX:
+ case SNDRV_PCM_SUBFORMAT_STD:
+ default:
+ break;
+ }
+ fallthrough;
+ default:
+ return snd_pcm_format_width(format);
+ }
+}
+EXPORT_SYMBOL(snd_pcm_hw_params_bits);
+
static int snd_pcm_lib_ioctl_reset(struct snd_pcm_substream *substream,
void *arg)
{
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index f610b08f5a2b..21baf6bf7e25 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -479,12 +479,34 @@ static int fixup_unreferenced_params(struct snd_pcm_substream *substream,
{
const struct snd_interval *i;
const struct snd_mask *m;
+ struct snd_mask *m_rw;
int err;
if (!params->msbits) {
i = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
if (snd_interval_single(i))
params->msbits = snd_interval_value(i);
+ m = hw_param_mask_c(params, SNDRV_PCM_HW_PARAM_FORMAT);
+ if (snd_mask_single(m)) {
+ snd_pcm_format_t format = (__force snd_pcm_format_t)snd_mask_min(m);
+ params->msbits = snd_pcm_format_width(format);
+ }
+ }
+
+ if (params->msbits) {
+ m = hw_param_mask_c(params, SNDRV_PCM_HW_PARAM_FORMAT);
+ if (snd_mask_single(m)) {
+ snd_pcm_format_t format = (__force snd_pcm_format_t)snd_mask_min(m);
+
+ if (snd_pcm_format_linear(format) &&
+ snd_pcm_format_width(format) != params->msbits) {
+ m_rw = hw_param_mask(params, SNDRV_PCM_HW_PARAM_SUBFORMAT);
+ snd_mask_reset(m_rw,
+ (__force unsigned)SNDRV_PCM_SUBFORMAT_MSBITS_MAX);
+ if (snd_mask_empty(m_rw))
+ return -EINVAL;
+ }
+ }
}
if (!params->rate_den) {
@@ -2483,6 +2505,41 @@ static int snd_pcm_hw_rule_buffer_bytes_max(struct snd_pcm_hw_params *params,
return snd_interval_refine(hw_param_interval(params, rule->var), &t);
}
+static int snd_pcm_hw_rule_subformats(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_mask *sfmask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_SUBFORMAT);
+ struct snd_mask *fmask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+ u32 *subformats = rule->private;
+ snd_pcm_format_t f;
+ struct snd_mask m;
+
+ snd_mask_none(&m);
+ /* All PCMs support at least the default STD subformat. */
+ snd_mask_set(&m, (__force unsigned)SNDRV_PCM_SUBFORMAT_STD);
+
+ pcm_for_each_format(f) {
+ if (!snd_mask_test(fmask, (__force unsigned)f))
+ continue;
+
+ if (f == SNDRV_PCM_FORMAT_S32_LE && *subformats)
+ m.bits[0] |= *subformats;
+ else if (snd_pcm_format_linear(f))
+ snd_mask_set(&m, (__force unsigned)SNDRV_PCM_SUBFORMAT_MSBITS_MAX);
+ }
+
+ return snd_mask_refine(sfmask, &m);
+}
+
+static int snd_pcm_hw_constraint_subformats(struct snd_pcm_runtime *runtime,
+ unsigned int cond, u32 *subformats)
+{
+ return snd_pcm_hw_rule_add(runtime, cond, -1,
+ snd_pcm_hw_rule_subformats, (void *)subformats,
+ SNDRV_PCM_HW_PARAM_SUBFORMAT,
+ SNDRV_PCM_HW_PARAM_FORMAT, -1);
+}
+
static int snd_pcm_hw_constraints_init(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -2634,8 +2691,7 @@ static int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream)
if (err < 0)
return err;
- err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_SUBFORMAT,
- PARAM_MASK_BIT(SNDRV_PCM_SUBFORMAT_STD));
+ err = snd_pcm_hw_constraint_subformats(runtime, 0, &hw->subformats);
if (err < 0)
return err;
diff --git a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c
index b603bb93f896..e705e7538118 100644
--- a/sound/core/seq/seq_memory.c
+++ b/sound/core/seq/seq_memory.c
@@ -442,7 +442,8 @@ int snd_seq_pool_init(struct snd_seq_pool *pool)
if (snd_BUG_ON(!pool))
return -EINVAL;
- cellptr = kvmalloc_array(sizeof(struct snd_seq_event_cell), pool->size,
+ cellptr = kvmalloc_array(pool->size,
+ sizeof(struct snd_seq_event_cell),
GFP_KERNEL);
if (!cellptr)
return -ENOMEM;
diff --git a/sound/core/ump.c b/sound/core/ump.c
index 3bef1944e955..fe7911498cc4 100644
--- a/sound/core/ump.c
+++ b/sound/core/ump.c
@@ -985,7 +985,7 @@ static int snd_ump_legacy_open(struct snd_rawmidi_substream *substream)
struct snd_ump_endpoint *ump = substream->rmidi->private_data;
int dir = substream->stream;
int group = ump->legacy_mapping[substream->number];
- int err;
+ int err = 0;
mutex_lock(&ump->open_mutex);
if (ump->legacy_substreams[dir][group]) {
@@ -1009,7 +1009,7 @@ static int snd_ump_legacy_open(struct snd_rawmidi_substream *substream)
spin_unlock_irq(&ump->legacy_locks[dir]);
unlock:
mutex_unlock(&ump->open_mutex);
- return 0;
+ return err;
}
static int snd_ump_legacy_close(struct snd_rawmidi_substream *substream)
diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c
index e87dc67f33c6..1c65e0a3b13c 100644
--- a/sound/drivers/aloop.c
+++ b/sound/drivers/aloop.c
@@ -322,6 +322,17 @@ static int loopback_snd_timer_close_cable(struct loopback_pcm *dpcm)
return 0;
}
+static bool is_access_interleaved(snd_pcm_access_t access)
+{
+ switch (access) {
+ case SNDRV_PCM_ACCESS_MMAP_INTERLEAVED:
+ case SNDRV_PCM_ACCESS_RW_INTERLEAVED:
+ return true;
+ default:
+ return false;
+ }
+};
+
static int loopback_check_format(struct loopback_cable *cable, int stream)
{
struct snd_pcm_runtime *runtime, *cruntime;
@@ -341,7 +352,8 @@ static int loopback_check_format(struct loopback_cable *cable, int stream)
check = runtime->format != cruntime->format ||
runtime->rate != cruntime->rate ||
runtime->channels != cruntime->channels ||
- runtime->access != cruntime->access;
+ is_access_interleaved(runtime->access) !=
+ is_access_interleaved(cruntime->access);
if (!check)
return 0;
if (stream == SNDRV_PCM_STREAM_CAPTURE) {
@@ -369,7 +381,8 @@ static int loopback_check_format(struct loopback_cable *cable, int stream)
&setup->channels_id);
setup->channels = runtime->channels;
}
- if (setup->access != runtime->access) {
+ if (is_access_interleaved(setup->access) !=
+ is_access_interleaved(runtime->access)) {
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
&setup->access_id);
setup->access = runtime->access;
@@ -584,8 +597,7 @@ static void copy_play_buf(struct loopback_pcm *play,
size = play->pcm_buffer_size - src_off;
if (dst_off + size > capt->pcm_buffer_size)
size = capt->pcm_buffer_size - dst_off;
- if (runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED ||
- runtime->access == SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED)
+ if (!is_access_interleaved(runtime->access))
copy_play_buf_part_n(play, capt, size, src_off, dst_off);
else
memcpy(dst + dst_off, src + src_off, size);
@@ -1544,8 +1556,7 @@ static int loopback_access_get(struct snd_kcontrol *kcontrol,
mutex_lock(&loopback->cable_lock);
access = loopback->setup[kcontrol->id.subdevice][kcontrol->id.device].access;
- ucontrol->value.enumerated.item[0] = access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED ||
- access == SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED;
+ ucontrol->value.enumerated.item[0] = !is_access_interleaved(access);
mutex_unlock(&loopback->cable_lock);
return 0;
diff --git a/sound/drivers/serial-generic.c b/sound/drivers/serial-generic.c
index c8db6c75d133..d6e5aafd697c 100644
--- a/sound/drivers/serial-generic.c
+++ b/sound/drivers/serial-generic.c
@@ -100,8 +100,8 @@ static void snd_serial_generic_write_wakeup(struct serdev_device *serdev)
snd_serial_generic_tx_wakeup(drvdata);
}
-static int snd_serial_generic_receive_buf(struct serdev_device *serdev,
- const unsigned char *buf, size_t count)
+static ssize_t snd_serial_generic_receive_buf(struct serdev_device *serdev,
+ const u8 *buf, size_t count)
{
int ret;
struct snd_serial_generic *drvdata = serdev_device_get_drvdata(serdev);
diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c
index a13c0b408aad..7be17bca257f 100644
--- a/sound/firewire/amdtp-stream.c
+++ b/sound/firewire/amdtp-stream.c
@@ -951,7 +951,7 @@ static int generate_tx_packet_descs(struct amdtp_stream *s, struct pkt_desc *des
// to the reason.
unsigned int safe_cycle = increment_ohci_cycle_count(next_cycle,
IR_JUMBO_PAYLOAD_MAX_SKIP_CYCLES);
- lost = (compare_ohci_cycle_count(safe_cycle, cycle) > 0);
+ lost = (compare_ohci_cycle_count(safe_cycle, cycle) < 0);
}
if (lost) {
dev_err(&s->unit->device, "Detect discontinuity of cycle: %d %d\n",
diff --git a/sound/hda/hda_bus_type.c b/sound/hda/hda_bus_type.c
index 4cd94178df9f..cce2c30511a2 100644
--- a/sound/hda/hda_bus_type.c
+++ b/sound/hda/hda_bus_type.c
@@ -76,7 +76,7 @@ static int hda_uevent(const struct device *dev, struct kobj_uevent_env *env)
return 0;
}
-struct bus_type snd_hda_bus_type = {
+const struct bus_type snd_hda_bus_type = {
.name = "hdaudio",
.match = hda_bus_match,
.uevent = hda_uevent,
diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c
index bbf7bcdb449a..7f7b67fe1b65 100644
--- a/sound/hda/hdac_device.c
+++ b/sound/hda/hdac_device.c
@@ -13,6 +13,7 @@
#include <sound/hdaudio.h>
#include <sound/hda_regmap.h>
#include <sound/pcm.h>
+#include <sound/pcm_params.h>
#include "local.h"
static void setup_fg_nodes(struct hdac_device *codec);
@@ -725,32 +726,77 @@ static const struct hda_rate_tbl rate_bits[] = {
{ 0 } /* terminator */
};
+static snd_pcm_format_t snd_hdac_format_normalize(snd_pcm_format_t format)
+{
+ switch (format) {
+ case SNDRV_PCM_FORMAT_S20_LE:
+ case SNDRV_PCM_FORMAT_S24_LE:
+ return SNDRV_PCM_FORMAT_S32_LE;
+
+ case SNDRV_PCM_FORMAT_U20_LE:
+ case SNDRV_PCM_FORMAT_U24_LE:
+ return SNDRV_PCM_FORMAT_U32_LE;
+
+ case SNDRV_PCM_FORMAT_S20_BE:
+ case SNDRV_PCM_FORMAT_S24_BE:
+ return SNDRV_PCM_FORMAT_S32_BE;
+
+ case SNDRV_PCM_FORMAT_U20_BE:
+ case SNDRV_PCM_FORMAT_U24_BE:
+ return SNDRV_PCM_FORMAT_U32_BE;
+
+ default:
+ return format;
+ }
+}
+
/**
- * snd_hdac_calc_stream_format - calculate the format bitset
- * @rate: the sample rate
- * @channels: the number of channels
- * @format: the PCM format (SNDRV_PCM_FORMAT_XXX)
- * @maxbps: the max. bps
- * @spdif_ctls: HD-audio SPDIF status bits (0 if irrelevant)
+ * snd_hdac_stream_format_bits - obtain bits per sample value.
+ * @format: the PCM format.
+ * @subformat: the PCM subformat.
+ * @maxbits: the maximum bits per sample.
*
- * Calculate the format bitset from the given rate, channels and th PCM format.
+ * Return: The number of bits per sample.
+ */
+unsigned int snd_hdac_stream_format_bits(snd_pcm_format_t format, snd_pcm_subformat_t subformat,
+ unsigned int maxbits)
+{
+ struct snd_pcm_hw_params params;
+ unsigned int bits;
+
+ memset(&params, 0, sizeof(params));
+
+ params_set_format(&params, snd_hdac_format_normalize(format));
+ snd_mask_set(hw_param_mask(&params, SNDRV_PCM_HW_PARAM_SUBFORMAT),
+ (__force unsigned int)subformat);
+
+ bits = snd_pcm_hw_params_bits(&params);
+ if (maxbits)
+ return min(bits, maxbits);
+ return bits;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_format_bits);
+
+/**
+ * snd_hdac_stream_format - convert format parameters to SDxFMT value.
+ * @channels: the number of channels.
+ * @bits: bits per sample.
+ * @rate: the sample rate.
*
- * Return zero if invalid.
+ * Return: The format bitset or zero if invalid.
*/
-unsigned int snd_hdac_calc_stream_format(unsigned int rate,
- unsigned int channels,
- snd_pcm_format_t format,
- unsigned int maxbps,
- unsigned short spdif_ctls)
+unsigned int snd_hdac_stream_format(unsigned int channels, unsigned int bits, unsigned int rate)
{
- int i;
unsigned int val = 0;
+ int i;
- for (i = 0; rate_bits[i].hz; i++)
+ for (i = 0; rate_bits[i].hz; i++) {
if (rate_bits[i].hz == rate) {
val = rate_bits[i].hda_fmt;
break;
}
+ }
+
if (!rate_bits[i].hz)
return 0;
@@ -758,7 +804,7 @@ unsigned int snd_hdac_calc_stream_format(unsigned int rate,
return 0;
val |= channels - 1;
- switch (snd_pcm_format_width(format)) {
+ switch (bits) {
case 8:
val |= AC_FMT_BITS_8;
break;
@@ -766,25 +812,42 @@ unsigned int snd_hdac_calc_stream_format(unsigned int rate,
val |= AC_FMT_BITS_16;
break;
case 20:
+ val |= AC_FMT_BITS_20;
+ break;
case 24:
+ val |= AC_FMT_BITS_24;
+ break;
case 32:
- if (maxbps >= 32 || format == SNDRV_PCM_FORMAT_FLOAT_LE)
- val |= AC_FMT_BITS_32;
- else if (maxbps >= 24)
- val |= AC_FMT_BITS_24;
- else
- val |= AC_FMT_BITS_20;
+ val |= AC_FMT_BITS_32;
break;
default:
return 0;
}
- if (spdif_ctls & AC_DIG1_NONAUDIO)
+ return val;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_format);
+
+/**
+ * snd_hdac_spdif_stream_format - convert format parameters to SDxFMT value.
+ * @channels: the number of channels.
+ * @bits: bits per sample.
+ * @rate: the sample rate.
+ * @spdif_ctls: HD-audio SPDIF status bits (0 if irrelevant).
+ *
+ * Return: The format bitset or zero if invalid.
+ */
+unsigned int snd_hdac_spdif_stream_format(unsigned int channels, unsigned int bits,
+ unsigned int rate, unsigned short spdif_ctls)
+{
+ unsigned int val = snd_hdac_stream_format(channels, bits, rate);
+
+ if (val && spdif_ctls & AC_DIG1_NONAUDIO)
val |= AC_FMT_TYPE_NON_PCM;
return val;
}
-EXPORT_SYMBOL_GPL(snd_hdac_calc_stream_format);
+EXPORT_SYMBOL_GPL(snd_hdac_spdif_stream_format);
static unsigned int query_pcm_param(struct hdac_device *codec, hda_nid_t nid)
{
@@ -817,15 +880,17 @@ static unsigned int query_stream_param(struct hdac_device *codec, hda_nid_t nid)
* @nid: NID to query
* @ratesp: the pointer to store the detected rate bitflags
* @formatsp: the pointer to store the detected formats
+ * @subformatsp: the pointer to store the detected subformats for S32_LE format
* @bpsp: the pointer to store the detected format widths
*
- * Queries the supported PCM rates and formats. The NULL @ratesp, @formatsp
- * or @bsps argument is ignored.
+ * Queries the supported PCM rates and formats. The NULL @ratesp, @formatsp,
+ * @subformatsp or @bpsp argument is ignored.
*
* Returns 0 if successful, otherwise a negative error code.
*/
int snd_hdac_query_supported_pcm(struct hdac_device *codec, hda_nid_t nid,
- u32 *ratesp, u64 *formatsp, unsigned int *bpsp)
+ u32 *ratesp, u64 *formatsp, u32 *subformatsp,
+ unsigned int *bpsp)
{
unsigned int i, val, wcaps;
@@ -848,9 +913,10 @@ int snd_hdac_query_supported_pcm(struct hdac_device *codec, hda_nid_t nid,
*ratesp = rates;
}
- if (formatsp || bpsp) {
- u64 formats = 0;
+ if (formatsp || subformatsp || bpsp) {
unsigned int streams, bps;
+ u32 subformats = 0;
+ u64 formats = 0;
streams = query_stream_param(codec, nid);
if (!streams)
@@ -866,24 +932,24 @@ int snd_hdac_query_supported_pcm(struct hdac_device *codec, hda_nid_t nid,
formats |= SNDRV_PCM_FMTBIT_S16_LE;
bps = 16;
}
- if (wcaps & AC_WCAP_DIGITAL) {
- if (val & AC_SUPPCM_BITS_32)
+ if (val & AC_SUPPCM_BITS_20) {
+ formats |= SNDRV_PCM_FMTBIT_S32_LE;
+ subformats |= SNDRV_PCM_SUBFMTBIT_MSBITS_20;
+ bps = 20;
+ }
+ if (val & AC_SUPPCM_BITS_24) {
+ formats |= SNDRV_PCM_FMTBIT_S32_LE;
+ subformats |= SNDRV_PCM_SUBFMTBIT_MSBITS_24;
+ bps = 24;
+ }
+ if (val & AC_SUPPCM_BITS_32) {
+ if (wcaps & AC_WCAP_DIGITAL) {
formats |= SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE;
- if (val & (AC_SUPPCM_BITS_20|AC_SUPPCM_BITS_24))
+ } else {
formats |= SNDRV_PCM_FMTBIT_S32_LE;
- if (val & AC_SUPPCM_BITS_24)
- bps = 24;
- else if (val & AC_SUPPCM_BITS_20)
- bps = 20;
- } else if (val & (AC_SUPPCM_BITS_20|AC_SUPPCM_BITS_24|
- AC_SUPPCM_BITS_32)) {
- formats |= SNDRV_PCM_FMTBIT_S32_LE;
- if (val & AC_SUPPCM_BITS_32)
+ subformats |= SNDRV_PCM_SUBFMTBIT_MSBITS_MAX;
bps = 32;
- else if (val & AC_SUPPCM_BITS_24)
- bps = 24;
- else if (val & AC_SUPPCM_BITS_20)
- bps = 20;
+ }
}
}
#if 0 /* FIXME: CS4206 doesn't work, which is the only codec supporting float */
@@ -911,6 +977,8 @@ int snd_hdac_query_supported_pcm(struct hdac_device *codec, hda_nid_t nid,
}
if (formatsp)
*formatsp = formats;
+ if (subformatsp)
+ *subformatsp = subformats;
if (bpsp)
*bpsp = bps;
}
diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c
index 6ce24e248f8e..610ea7a33cd8 100644
--- a/sound/hda/hdac_stream.c
+++ b/sound/hda/hdac_stream.c
@@ -671,17 +671,15 @@ void snd_hdac_stream_timecounter_init(struct hdac_stream *azx_dev,
struct hdac_stream *s;
bool inited = false;
u64 cycle_last = 0;
- int i = 0;
list_for_each_entry(s, &bus->stream_list, list) {
- if (streams & (1 << i)) {
+ if ((streams & (1 << s->index))) {
azx_timecounter_init(s, inited, cycle_last);
if (!inited) {
inited = true;
cycle_last = s->tc.cycle_last;
}
}
- i++;
}
snd_pcm_gettime(runtime, &runtime->trigger_tstamp);
@@ -726,14 +724,13 @@ void snd_hdac_stream_sync(struct hdac_stream *azx_dev, bool start,
unsigned int streams)
{
struct hdac_bus *bus = azx_dev->bus;
- int i, nwait, timeout;
+ int nwait, timeout;
struct hdac_stream *s;
for (timeout = 5000; timeout; timeout--) {
nwait = 0;
- i = 0;
list_for_each_entry(s, &bus->stream_list, list) {
- if (!(streams & (1 << i++)))
+ if (!(streams & (1 << s->index)))
continue;
if (start) {
diff --git a/sound/hda/intel-dsp-config.c b/sound/hda/intel-dsp-config.c
index 756fa0aa69bb..6a384b922e4f 100644
--- a/sound/hda/intel-dsp-config.c
+++ b/sound/hda/intel-dsp-config.c
@@ -521,6 +521,16 @@ static const struct config_entry config_table[] = {
.flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
.device = PCI_DEVICE_ID_INTEL_HDA_MTL,
},
+ /* ArrowLake-S */
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_ARL_S,
+ },
+ /* ArrowLake */
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_ARL,
+ },
#endif
/* Lunar Lake */
diff --git a/sound/isa/wavefront/wavefront_fx.c b/sound/isa/wavefront/wavefront_fx.c
index 3c21324b2a0e..0273b7dfaf12 100644
--- a/sound/isa/wavefront/wavefront_fx.c
+++ b/sound/isa/wavefront/wavefront_fx.c
@@ -191,9 +191,9 @@ snd_wavefront_fx_ioctl (struct snd_hwdep *sdev, struct file *file,
"> 512 bytes to FX\n");
return -EIO;
}
- page_data = memdup_user((unsigned char __user *)
- r.data[3],
- r.data[2] * sizeof(short));
+ page_data = memdup_array_user((unsigned char __user *)
+ r.data[3],
+ r.data[2], sizeof(short));
if (IS_ERR(page_data))
return PTR_ERR(page_data);
pd = page_data;
diff --git a/sound/pci/au88x0/au88x0.c b/sound/pci/au88x0/au88x0.c
index eb234153691b..62b10b0e07b1 100644
--- a/sound/pci/au88x0/au88x0.c
+++ b/sound/pci/au88x0/au88x0.c
@@ -10,7 +10,7 @@
* Thanks to the ALSA developers, they helped a lot working out
* the ALSA part.
* Thanks also to Sourceforge for maintaining the old binary drivers,
- * and the forum, where developers could comunicate.
+ * and the forum, where developers could communicate.
*
* Now at least i can play Legacy DOOM with MIDI music :-)
*/
diff --git a/sound/pci/au88x0/au88x0_core.c b/sound/pci/au88x0/au88x0_core.c
index f217c02dfdfa..e5d867637336 100644
--- a/sound/pci/au88x0/au88x0_core.c
+++ b/sound/pci/au88x0/au88x0_core.c
@@ -1195,7 +1195,7 @@ static int vortex_adbdma_bufshift(vortex_t * vortex, int adbdma)
VORTEX_ADBDMA_BUFBASE + (((adbdma << 2) + pp) << 2),
snd_pcm_sgbuf_get_addr(dma->substream,
dma->period_bytes * p));
- /* Force write thru cache. */
+ /* Force write through cache. */
hwread(vortex->mmio, VORTEX_ADBDMA_BUFBASE +
(((adbdma << 2) + pp) << 2));
}
@@ -1237,7 +1237,7 @@ static void vortex_adbdma_resetup(vortex_t *vortex, int adbdma) {
VORTEX_ADBDMA_BUFBASE + (((adbdma << 2) + pp) << 2),
snd_pcm_sgbuf_get_addr(dma->substream,
dma->period_bytes * p));
- /* Force write thru cache. */
+ /* Force write through cache. */
hwread(vortex->mmio, VORTEX_ADBDMA_BUFBASE + (((adbdma << 2)+pp) << 2));
}
}
@@ -1466,7 +1466,7 @@ static int vortex_wtdma_bufshift(vortex_t * vortex, int wtdma)
(((wtdma << 2) + pp) << 2),
snd_pcm_sgbuf_get_addr(dma->substream,
dma->period_bytes * p));
- /* Force write thru cache. */
+ /* Force write through cache. */
hwread(vortex->mmio, VORTEX_WTDMA_BUFBASE +
(((wtdma << 2) + pp) << 2));
}
@@ -1854,7 +1854,7 @@ vortex_connection_mixin_mix(vortex_t * vortex, int en, unsigned char mixin,
vortex_mix_disableinput(vortex, mix, mixin, a);
}
-// Connect absolut address to mixin.
+// Connect absolute address to mixin.
static void
vortex_connection_adb_mixin(vortex_t * vortex, int en,
unsigned char channel, unsigned char source,
@@ -1880,7 +1880,7 @@ vortex_connection_src_src_adbdma(vortex_t * vortex, int en,
ADB_DMA(adbdma));
}
-// mix to absolut address.
+// mix to absolute address.
static void
vortex_connection_mix_adb(vortex_t * vortex, int en, unsigned char ch,
unsigned char mix, unsigned char dest)
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
index 21a90b3c4cc7..8e0ff70fb610 100644
--- a/sound/pci/hda/Kconfig
+++ b/sound/pci/hda/Kconfig
@@ -156,7 +156,7 @@ config SND_HDA_SCODEC_CS35L56_I2C
depends on I2C
depends on ACPI || COMPILE_TEST
depends on SND_SOC
- select CS_DSP
+ select FW_CS_DSP
select SND_HDA_GENERIC
select SND_SOC_CS35L56_SHARED
select SND_HDA_SCODEC_CS35L56
@@ -171,7 +171,7 @@ config SND_HDA_SCODEC_CS35L56_SPI
depends on SPI_MASTER
depends on ACPI || COMPILE_TEST
depends on SND_SOC
- select CS_DSP
+ select FW_CS_DSP
select SND_HDA_GENERIC
select SND_SOC_CS35L56_SHARED
select SND_HDA_SCODEC_CS35L56
diff --git a/sound/pci/hda/cs35l41_hda.c b/sound/pci/hda/cs35l41_hda.c
index 92ca2b3b6c92..d3fa6e136744 100644
--- a/sound/pci/hda/cs35l41_hda.c
+++ b/sound/pci/hda/cs35l41_hda.c
@@ -12,6 +12,7 @@
#include <sound/hda_codec.h>
#include <sound/soc.h>
#include <linux/pm_runtime.h>
+#include <linux/spi/spi.h>
#include "hda_local.h"
#include "hda_auto_parser.h"
#include "hda_jack.h"
@@ -996,6 +997,11 @@ static int cs35l41_smart_amp(struct cs35l41_hda *cs35l41)
__be32 halo_sts;
int ret;
+ if (cs35l41->bypass_fw) {
+ dev_warn(cs35l41->dev, "Bypassing Firmware.\n");
+ return 0;
+ }
+
ret = cs35l41_init_dsp(cs35l41);
if (ret) {
dev_warn(cs35l41->dev, "Cannot Initialize Firmware. Error: %d\n", ret);
@@ -1588,6 +1594,7 @@ static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, i
u32 values[HDA_MAX_COMPONENTS];
struct acpi_device *adev;
struct device *physdev;
+ struct spi_device *spi;
const char *sub;
char *property;
size_t nval;
@@ -1610,7 +1617,7 @@ static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, i
ret = cs35l41_add_dsd_properties(cs35l41, physdev, id, hid);
if (!ret) {
dev_info(cs35l41->dev, "Using extra _DSD properties, bypassing _DSD in ACPI\n");
- goto put_physdev;
+ goto out;
}
property = "cirrus,dev-index";
@@ -1701,8 +1708,20 @@ static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, i
hw_cfg->bst_type = CS35L41_EXT_BOOST;
hw_cfg->valid = true;
+out:
put_device(physdev);
+ cs35l41->bypass_fw = false;
+ if (cs35l41->control_bus == SPI) {
+ spi = to_spi_device(cs35l41->dev);
+ if (spi->max_speed_hz < CS35L41_MAX_ACCEPTABLE_SPI_SPEED_HZ) {
+ dev_warn(cs35l41->dev,
+ "SPI speed is too slow to support firmware download: %d Hz.\n",
+ spi->max_speed_hz);
+ cs35l41->bypass_fw = true;
+ }
+ }
+
return 0;
err:
@@ -1711,14 +1730,13 @@ err:
hw_cfg->gpio1.valid = false;
hw_cfg->gpio2.valid = false;
acpi_dev_put(cs35l41->dacpi);
-put_physdev:
put_device(physdev);
return ret;
}
int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int irq,
- struct regmap *regmap)
+ struct regmap *regmap, enum control_bus control_bus)
{
unsigned int regid, reg_revid;
struct cs35l41_hda *cs35l41;
@@ -1737,6 +1755,7 @@ int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int i
cs35l41->dev = dev;
cs35l41->irq = irq;
cs35l41->regmap = regmap;
+ cs35l41->control_bus = control_bus;
dev_set_drvdata(dev, cs35l41);
ret = cs35l41_hda_read_acpi(cs35l41, device_name, id);
diff --git a/sound/pci/hda/cs35l41_hda.h b/sound/pci/hda/cs35l41_hda.h
index 3d925d677213..43d55292b327 100644
--- a/sound/pci/hda/cs35l41_hda.h
+++ b/sound/pci/hda/cs35l41_hda.h
@@ -20,6 +20,8 @@
#include <linux/firmware/cirrus/cs_dsp.h>
#include <linux/firmware/cirrus/wmfw.h>
+#define CS35L41_MAX_ACCEPTABLE_SPI_SPEED_HZ 1000000
+
struct cs35l41_amp_cal_data {
u32 calTarget[2];
u32 calTime[2];
@@ -46,6 +48,11 @@ enum cs35l41_hda_gpio_function {
CS35l41_SYNC,
};
+enum control_bus {
+ I2C,
+ SPI
+};
+
struct cs35l41_hda {
struct device *dev;
struct regmap *regmap;
@@ -74,6 +81,9 @@ struct cs35l41_hda {
struct cs_dsp cs_dsp;
struct acpi_device *dacpi;
bool mute_override;
+ enum control_bus control_bus;
+ bool bypass_fw;
+
};
enum halo_state {
@@ -85,7 +95,7 @@ enum halo_state {
extern const struct dev_pm_ops cs35l41_hda_pm_ops;
int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int irq,
- struct regmap *regmap);
+ struct regmap *regmap, enum control_bus control_bus);
void cs35l41_hda_remove(struct device *dev);
int cs35l41_get_speaker_id(struct device *dev, int amp_index, int num_amps, int fixed_gpio_id);
diff --git a/sound/pci/hda/cs35l41_hda_i2c.c b/sound/pci/hda/cs35l41_hda_i2c.c
index b44536fbba17..603e9bff3a71 100644
--- a/sound/pci/hda/cs35l41_hda_i2c.c
+++ b/sound/pci/hda/cs35l41_hda_i2c.c
@@ -30,7 +30,7 @@ static int cs35l41_hda_i2c_probe(struct i2c_client *clt)
return -ENODEV;
return cs35l41_hda_probe(&clt->dev, device_name, clt->addr, clt->irq,
- devm_regmap_init_i2c(clt, &cs35l41_regmap_i2c));
+ devm_regmap_init_i2c(clt, &cs35l41_regmap_i2c), I2C);
}
static void cs35l41_hda_i2c_remove(struct i2c_client *clt)
diff --git a/sound/pci/hda/cs35l41_hda_property.c b/sound/pci/hda/cs35l41_hda_property.c
index c1afb721b4c6..e436d4dab317 100644
--- a/sound/pci/hda/cs35l41_hda_property.c
+++ b/sound/pci/hda/cs35l41_hda_property.c
@@ -16,10 +16,6 @@
struct cs35l41_config {
const char *ssid;
- enum {
- SPI,
- I2C
- } bus;
int num_amps;
enum {
INTERNAL,
@@ -35,42 +31,79 @@ struct cs35l41_config {
};
static const struct cs35l41_config cs35l41_config_table[] = {
+ { "10280B27", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "10280B28", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "10280BEB", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, -1, 0, 0, 0, 0 },
+ { "10280C4D", 4, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, CS35L41_LEFT, CS35L41_RIGHT }, 0, 1, -1, 1000, 4500, 24 },
/*
* Device 103C89C6 does have _DSD, however it is setup to use the wrong boost type.
* We can override the _DSD to correct the boost type here.
* Since this laptop has valid ACPI, we do not need to handle cs-gpios, since that already exists
* in the ACPI. The Reset GPIO is also valid, so we can use the Reset defined in _DSD.
*/
- { "103C89C6", SPI, 2, INTERNAL, { CS35L41_RIGHT, CS35L41_LEFT, 0, 0 }, -1, -1, -1, 1000, 4500, 24 },
- { "104312AF", SPI, 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
- { "10431433", I2C, 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
- { "10431463", I2C, 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
- { "10431473", SPI, 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, -1, 0, 1000, 4500, 24 },
- { "10431483", SPI, 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, -1, 0, 1000, 4500, 24 },
- { "10431493", SPI, 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
- { "104314D3", SPI, 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
- { "104314E3", I2C, 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
- { "10431503", I2C, 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
- { "10431533", I2C, 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
- { "10431573", SPI, 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
- { "10431663", SPI, 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, -1, 0, 1000, 4500, 24 },
- { "104316D3", SPI, 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 },
- { "104316F3", SPI, 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 },
- { "104317F3", I2C, 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
- { "10431863", SPI, 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
- { "104318D3", I2C, 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 },
- { "10431C9F", SPI, 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
- { "10431CAF", SPI, 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
- { "10431CCF", SPI, 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
- { "10431CDF", SPI, 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
- { "10431CEF", SPI, 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
- { "10431D1F", I2C, 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
- { "10431DA2", SPI, 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 },
- { "10431E02", SPI, 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 },
- { "10431EE2", I2C, 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 },
- { "10431F12", I2C, 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
- { "10431F1F", SPI, 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, -1, 0, 0, 0, 0 },
- { "10431F62", SPI, 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 },
+ { "103C89C6", 2, INTERNAL, { CS35L41_RIGHT, CS35L41_LEFT, 0, 0 }, -1, -1, -1, 1000, 4500, 24 },
+ { "103C8A28", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8A29", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8A2A", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8A2B", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8A2C", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8A2D", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8A2E", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8A30", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8A31", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8BB3", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8BB4", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8BDF", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8BE0", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8BE1", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8BE2", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8BE9", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8BDD", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8BDE", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8BE3", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8BE5", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8BE6", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8B3A", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "104312AF", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "10431433", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
+ { "10431463", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
+ { "10431473", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, -1, 0, 1000, 4500, 24 },
+ { "10431483", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, -1, 0, 1000, 4500, 24 },
+ { "10431493", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "104314D3", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "104314E3", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
+ { "10431503", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
+ { "10431533", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
+ { "10431573", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "10431663", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, -1, 0, 1000, 4500, 24 },
+ { "10431683", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 },
+ { "104316A3", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 },
+ { "104316D3", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 },
+ { "104316F3", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 },
+ { "104317F3", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
+ { "10431863", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "104318D3", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 },
+ { "10431A83", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
+ { "10431C9F", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "10431CAF", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "10431CCF", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "10431CDF", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "10431CEF", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "10431D1F", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
+ { "10431DA2", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 },
+ { "10431E02", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 },
+ { "10431E12", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 },
+ { "10431EE2", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 },
+ { "10431F12", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
+ { "10431F1F", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, -1, 0, 0, 0, 0 },
+ { "10431F62", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 },
+ { "17AA386F", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 },
+ { "17AA38A9", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 2, -1, 0, 0, 0 },
+ { "17AA38AB", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 2, -1, 0, 0, 0 },
+ { "17AA38B4", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 },
+ { "17AA38B5", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 },
+ { "17AA38B6", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 },
+ { "17AA38B7", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 },
{}
};
@@ -208,7 +241,7 @@ static int generic_dsd_config(struct cs35l41_hda *cs35l41, struct device *physde
"_DSD already exists.\n");
}
- if (cfg->bus == SPI) {
+ if (cs35l41->control_bus == SPI) {
cs35l41->index = id;
/*
@@ -345,7 +378,33 @@ struct cs35l41_prop_model {
static const struct cs35l41_prop_model cs35l41_prop_model_table[] = {
{ "CLSA0100", NULL, lenovo_legion_no_acpi },
{ "CLSA0101", NULL, lenovo_legion_no_acpi },
+ { "CSC3551", "10280B27", generic_dsd_config },
+ { "CSC3551", "10280B28", generic_dsd_config },
+ { "CSC3551", "10280BEB", generic_dsd_config },
+ { "CSC3551", "10280C4D", generic_dsd_config },
{ "CSC3551", "103C89C6", generic_dsd_config },
+ { "CSC3551", "103C8A28", generic_dsd_config },
+ { "CSC3551", "103C8A29", generic_dsd_config },
+ { "CSC3551", "103C8A2A", generic_dsd_config },
+ { "CSC3551", "103C8A2B", generic_dsd_config },
+ { "CSC3551", "103C8A2C", generic_dsd_config },
+ { "CSC3551", "103C8A2D", generic_dsd_config },
+ { "CSC3551", "103C8A2E", generic_dsd_config },
+ { "CSC3551", "103C8A30", generic_dsd_config },
+ { "CSC3551", "103C8A31", generic_dsd_config },
+ { "CSC3551", "103C8BB3", generic_dsd_config },
+ { "CSC3551", "103C8BB4", generic_dsd_config },
+ { "CSC3551", "103C8BDF", generic_dsd_config },
+ { "CSC3551", "103C8BE0", generic_dsd_config },
+ { "CSC3551", "103C8BE1", generic_dsd_config },
+ { "CSC3551", "103C8BE2", generic_dsd_config },
+ { "CSC3551", "103C8BE9", generic_dsd_config },
+ { "CSC3551", "103C8BDD", generic_dsd_config },
+ { "CSC3551", "103C8BDE", generic_dsd_config },
+ { "CSC3551", "103C8BE3", generic_dsd_config },
+ { "CSC3551", "103C8BE5", generic_dsd_config },
+ { "CSC3551", "103C8BE6", generic_dsd_config },
+ { "CSC3551", "103C8B3A", generic_dsd_config },
{ "CSC3551", "104312AF", generic_dsd_config },
{ "CSC3551", "10431433", generic_dsd_config },
{ "CSC3551", "10431463", generic_dsd_config },
@@ -358,11 +417,14 @@ static const struct cs35l41_prop_model cs35l41_prop_model_table[] = {
{ "CSC3551", "10431533", generic_dsd_config },
{ "CSC3551", "10431573", generic_dsd_config },
{ "CSC3551", "10431663", generic_dsd_config },
+ { "CSC3551", "10431683", generic_dsd_config },
+ { "CSC3551", "104316A3", generic_dsd_config },
{ "CSC3551", "104316D3", generic_dsd_config },
{ "CSC3551", "104316F3", generic_dsd_config },
{ "CSC3551", "104317F3", generic_dsd_config },
{ "CSC3551", "10431863", generic_dsd_config },
{ "CSC3551", "104318D3", generic_dsd_config },
+ { "CSC3551", "10431A83", generic_dsd_config },
{ "CSC3551", "10431C9F", generic_dsd_config },
{ "CSC3551", "10431CAF", generic_dsd_config },
{ "CSC3551", "10431CCF", generic_dsd_config },
@@ -371,10 +433,18 @@ static const struct cs35l41_prop_model cs35l41_prop_model_table[] = {
{ "CSC3551", "10431D1F", generic_dsd_config },
{ "CSC3551", "10431DA2", generic_dsd_config },
{ "CSC3551", "10431E02", generic_dsd_config },
+ { "CSC3551", "10431E12", generic_dsd_config },
{ "CSC3551", "10431EE2", generic_dsd_config },
{ "CSC3551", "10431F12", generic_dsd_config },
{ "CSC3551", "10431F1F", generic_dsd_config },
{ "CSC3551", "10431F62", generic_dsd_config },
+ { "CSC3551", "17AA386F", generic_dsd_config },
+ { "CSC3551", "17AA38A9", generic_dsd_config },
+ { "CSC3551", "17AA38AB", generic_dsd_config },
+ { "CSC3551", "17AA38B4", generic_dsd_config },
+ { "CSC3551", "17AA38B5", generic_dsd_config },
+ { "CSC3551", "17AA38B6", generic_dsd_config },
+ { "CSC3551", "17AA38B7", generic_dsd_config },
{}
};
diff --git a/sound/pci/hda/cs35l41_hda_spi.c b/sound/pci/hda/cs35l41_hda_spi.c
index eb287aa5f782..b76c0dfd5fef 100644
--- a/sound/pci/hda/cs35l41_hda_spi.c
+++ b/sound/pci/hda/cs35l41_hda_spi.c
@@ -26,7 +26,7 @@ static int cs35l41_hda_spi_probe(struct spi_device *spi)
return -ENODEV;
return cs35l41_hda_probe(&spi->dev, device_name, spi_get_chipselect(spi, 0), spi->irq,
- devm_regmap_init_spi(spi, &cs35l41_regmap_spi));
+ devm_regmap_init_spi(spi, &cs35l41_regmap_spi), SPI);
}
static void cs35l41_hda_spi_remove(struct spi_device *spi)
diff --git a/sound/pci/hda/cs35l56_hda.c b/sound/pci/hda/cs35l56_hda.c
index b61e1de8c4bf..75a14ba54fcd 100644
--- a/sound/pci/hda/cs35l56_hda.c
+++ b/sound/pci/hda/cs35l56_hda.c
@@ -30,14 +30,23 @@
* 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
+ *
+ * Override any Windows-specific mixer settings applied by the firmware.
*/
static const struct reg_sequence cs35l56_hda_dai_config[] = {
{ CS35L56_ASP1_CONTROL1, 0x00000021 },
{ CS35L56_ASP1_CONTROL2, 0x20200200 },
{ CS35L56_ASP1_CONTROL3, 0x00000003 },
+ { CS35L56_ASP1_FRAME_CONTROL1, 0x03020100 },
+ { CS35L56_ASP1_FRAME_CONTROL5, 0x00020100 },
{ CS35L56_ASP1_DATA_CONTROL5, 0x00000018 },
{ CS35L56_ASP1_DATA_CONTROL1, 0x00000018 },
{ CS35L56_ASP1_ENABLES1, 0x00000000 },
+ { CS35L56_ASP1TX1_INPUT, 0x00000018 },
+ { CS35L56_ASP1TX2_INPUT, 0x00000019 },
+ { CS35L56_ASP1TX3_INPUT, 0x00000020 },
+ { CS35L56_ASP1TX4_INPUT, 0x00000028 },
+
};
static void cs35l56_hda_play(struct cs35l56_hda *cs35l56)
@@ -133,6 +142,10 @@ static int cs35l56_hda_runtime_resume(struct device *dev)
}
}
+ ret = cs35l56_force_sync_asp1_registers_from_cache(&cs35l56->base);
+ if (ret)
+ goto err;
+
return 0;
err:
@@ -384,7 +397,7 @@ static const struct cs_dsp_client_ops cs35l56_hda_client_ops = {
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 *base_name, const char *system_name,
const char *amp_name,
const char *filetype)
{
@@ -392,17 +405,13 @@ static int cs35l56_hda_request_firmware_file(struct cs35l56_hda *cs35l56,
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,
+ *filename = kasprintf(GFP_KERNEL, "%s-%s-%s.%s", base_name,
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,
+ *filename = kasprintf(GFP_KERNEL, "%s-%s.%s", base_name,
system_name, filetype);
else
- *filename = kasprintf(GFP_KERNEL, "%scs35l56%s-%02x-dsp1-misc.%s", dir,
- cs35l56->base.secured ? "s" : "", cs35l56->base.rev,
- filetype);
+ *filename = kasprintf(GFP_KERNEL, "%s.%s", base_name, filetype);
if (!*filename)
return -ENOMEM;
@@ -435,8 +444,8 @@ static int cs35l56_hda_request_firmware_file(struct cs35l56_hda *cs35l56,
return 0;
}
-static const char cirrus_dir[] = "cirrus/";
static void cs35l56_hda_request_firmware_files(struct cs35l56_hda *cs35l56,
+ unsigned int preloaded_fw_ver,
const struct firmware **wmfw_firmware,
char **wmfw_filename,
const struct firmware **coeff_firmware,
@@ -444,55 +453,73 @@ static void cs35l56_hda_request_firmware_files(struct cs35l56_hda *cs35l56,
{
const char *system_name = cs35l56->system_name;
const char *amp_name = cs35l56->amp_name;
+ char base_name[37];
int ret;
+ if (preloaded_fw_ver) {
+ snprintf(base_name, sizeof(base_name),
+ "cirrus/cs35l56-%02x%s-%06x-dsp1-misc",
+ cs35l56->base.rev,
+ cs35l56->base.secured ? "-s" : "",
+ preloaded_fw_ver & 0xffffff);
+ } else {
+ snprintf(base_name, sizeof(base_name),
+ "cirrus/cs35l56-%02x%s-dsp1-misc",
+ cs35l56->base.rev,
+ cs35l56->base.secured ? "-s" : "");
+ }
+
if (system_name && amp_name) {
if (!cs35l56_hda_request_firmware_file(cs35l56, wmfw_firmware, wmfw_filename,
- cirrus_dir, system_name, amp_name, "wmfw")) {
+ base_name, system_name, amp_name, "wmfw")) {
cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename,
- cirrus_dir, system_name, amp_name, "bin");
+ base_name, 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")) {
+ base_name, system_name, NULL, "wmfw")) {
if (amp_name)
cs35l56_hda_request_firmware_file(cs35l56,
coeff_firmware, coeff_filename,
- cirrus_dir, system_name,
+ base_name, system_name,
amp_name, "bin");
if (!*coeff_firmware)
cs35l56_hda_request_firmware_file(cs35l56,
coeff_firmware, coeff_filename,
- cirrus_dir, system_name,
+ base_name, system_name,
NULL, "bin");
return;
}
+
+ /*
+ * Check for system-specific bin files without wmfw before
+ * falling back to generic firmware
+ */
+ if (amp_name)
+ cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename,
+ base_name, system_name, amp_name, "bin");
+ if (!*coeff_firmware)
+ cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename,
+ base_name, system_name, NULL, "bin");
+
+ if (*coeff_firmware)
+ return;
}
ret = cs35l56_hda_request_firmware_file(cs35l56, wmfw_firmware, wmfw_filename,
- cirrus_dir, NULL, NULL, "wmfw");
+ base_name, NULL, NULL, "wmfw");
if (!ret) {
cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename,
- cirrus_dir, NULL, NULL, "bin");
+ base_name, 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");
+ base_name, NULL, NULL, "bin");
}
static void cs35l56_hda_release_firmware_files(const struct firmware *wmfw_firmware,
@@ -526,7 +553,8 @@ static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
const struct firmware *wmfw_firmware = NULL;
char *coeff_filename = NULL;
char *wmfw_filename = NULL;
- unsigned int firmware_missing;
+ unsigned int preloaded_fw_ver;
+ bool firmware_missing;
int ret = 0;
/* Prepare for a new DSP power-up */
@@ -537,24 +565,21 @@ static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
pm_runtime_get_sync(cs35l56->base.dev);
- ret = regmap_read(cs35l56->base.regmap, CS35L56_PROTECTION_STATUS, &firmware_missing);
- if (ret) {
- dev_err(cs35l56->base.dev, "Failed to read PROTECTION_STATUS: %d\n", ret);
+ /*
+ * The firmware can only be upgraded if it is currently running
+ * from the built-in ROM. If not, the wmfw/bin must be for the
+ * version of firmware that is running on the chip.
+ */
+ ret = cs35l56_read_prot_status(&cs35l56->base, &firmware_missing, &preloaded_fw_ver);
+ if (ret)
goto err_pm_put;
- }
- firmware_missing &= CS35L56_FIRMWARE_MISSING;
+ if (firmware_missing)
+ preloaded_fw_ver = 0;
- /*
- * Firmware can only be downloaded if the CS35L56 is secured or is
- * running from the built-in ROM. If it is secured the BIOS will have
- * downloaded firmware, and the wmfw/bin files will only contain
- * tunings that are safe to download with the firmware running.
- */
- if (cs35l56->base.secured || firmware_missing) {
- cs35l56_hda_request_firmware_files(cs35l56, &wmfw_firmware, &wmfw_filename,
- &coeff_firmware, &coeff_filename);
- }
+ cs35l56_hda_request_firmware_files(cs35l56, preloaded_fw_ver,
+ &wmfw_firmware, &wmfw_filename,
+ &coeff_firmware, &coeff_filename);
/*
* If the BIOS didn't patch the firmware a bin file is mandatory to
@@ -569,12 +594,12 @@ static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
mutex_lock(&cs35l56->base.irq_lock);
/*
- * 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 the firmware hasn't been patched it must be shutdown before
+ * doing a full patch and reset afterwards. If it is already
+ * running a patched version the firmware files only contain
+ * tunings and we can use the lower cost reinit sequence instead.
*/
- if (!cs35l56->base.secured && (wmfw_firmware || coeff_firmware)) {
+ if (firmware_missing && (wmfw_firmware || coeff_firmware)) {
ret = cs35l56_firmware_shutdown(&cs35l56->base);
if (ret)
goto err;
@@ -593,7 +618,7 @@ static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
if (coeff_filename)
dev_dbg(cs35l56->base.dev, "Loaded Coefficients: %s\n", coeff_filename);
- if (cs35l56->base.secured) {
+ if (!firmware_missing) {
ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT);
if (ret)
goto err_powered_up;
@@ -976,6 +1001,9 @@ int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int id)
regmap_multi_reg_write(cs35l56->base.regmap, cs35l56_hda_dai_config,
ARRAY_SIZE(cs35l56_hda_dai_config));
+ ret = cs35l56_force_sync_asp1_registers_from_cache(&cs35l56->base);
+ if (ret)
+ goto err;
/*
* By default only enable one ASP1TXn, where n=amplifier index,
@@ -1035,16 +1063,6 @@ const struct dev_pm_ops cs35l56_hda_pm_ops = {
};
EXPORT_SYMBOL_NS_GPL(cs35l56_hda_pm_ops, SND_HDA_SCODEC_CS35L56);
-#if IS_ENABLED(CONFIG_SND_HDA_SCODEC_CS35L56_KUNIT_TEST)
-/* Hooks to export static function to KUnit test */
-
-int cs35l56_hda_test_hook_get_speaker_id(struct device *dev, int amp_index, int num_amps)
-{
- return cs35l56_hda_get_speaker_id(dev, amp_index, num_amps);
-}
-EXPORT_SYMBOL_NS_GPL(cs35l56_hda_test_hook_get_speaker_id, SND_HDA_SCODEC_CS35L56);
-#endif
-
MODULE_DESCRIPTION("CS35L56 HDA Driver");
MODULE_IMPORT_NS(SND_HDA_CIRRUS_SCODEC);
MODULE_IMPORT_NS(SND_HDA_CS_DSP_CONTROLS);
diff --git a/sound/pci/hda/cs35l56_hda_spi.c b/sound/pci/hda/cs35l56_hda_spi.c
index 27d7fbc56b4c..080426de9083 100644
--- a/sound/pci/hda/cs35l56_hda_spi.c
+++ b/sound/pci/hda/cs35l56_hda_spi.c
@@ -33,7 +33,7 @@ static int cs35l56_hda_spi_probe(struct spi_device *spi)
return ret;
}
- ret = cs35l56_hda_common_probe(cs35l56, spi->chip_select);
+ ret = cs35l56_hda_common_probe(cs35l56, spi_get_chipselect(spi, 0));
if (ret)
return ret;
ret = cs35l56_irq_request(&cs35l56->base, spi->irq);
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 01718b1fc9a7..12f02cdc9659 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -3163,6 +3163,7 @@ static int set_pcm_default_values(struct hda_codec *codec,
err = snd_hda_query_supported_pcm(codec, info->nid,
info->rates ? NULL : &info->rates,
info->formats ? NULL : &info->formats,
+ info->subformats ? NULL : &info->subformats,
info->maxbps ? NULL : &info->maxbps);
if (err < 0)
return err;
@@ -3757,6 +3758,7 @@ int snd_hda_multi_out_analog_open(struct hda_codec *codec,
snd_hda_query_supported_pcm(codec, mout->dig_out_nid,
&mout->spdif_rates,
&mout->spdif_formats,
+ NULL,
&mout->spdif_maxbps);
}
mutex_lock(&codec->spdif_mutex);
diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c
index c42e9ffff9db..efe98f6f19a3 100644
--- a/sound/pci/hda/hda_controller.c
+++ b/sound/pci/hda/hda_controller.c
@@ -151,7 +151,7 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)
struct azx_dev *azx_dev = get_azx_dev(substream);
struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned int format_val, stream_tag;
+ unsigned int format_val, stream_tag, bits;
int err;
struct hda_spdif_out *spdif =
snd_hda_spdif_out_of_nid(apcm->codec, hinfo->nid);
@@ -165,11 +165,9 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)
}
snd_hdac_stream_reset(azx_stream(azx_dev));
- format_val = snd_hdac_calc_stream_format(runtime->rate,
- runtime->channels,
- runtime->format,
- hinfo->maxbps,
- ctls);
+ bits = snd_hdac_stream_format_bits(runtime->format, SNDRV_PCM_SUBFORMAT_STD, hinfo->maxbps);
+
+ format_val = snd_hdac_spdif_stream_format(runtime->channels, bits, runtime->rate, ctls);
if (!format_val) {
dev_err(chip->card->dev,
"invalid format_val, rate=%d, ch=%d, format=%d\n",
@@ -1209,6 +1207,9 @@ int azx_probe_codecs(struct azx *chip, unsigned int max_slots)
dev_warn(chip->card->dev,
"Codec #%d probe error; disabling it...\n", c);
bus->codec_mask &= ~(1 << c);
+ /* no codecs */
+ if (bus->codec_mask == 0)
+ break;
/* More badly, accessing to a non-existing
* codec often screws up the controller chip,
* and disturbs the further communications.
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index bf685d01259d..de2a3d08c73c 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -3946,7 +3946,6 @@ static int create_mute_led_cdev(struct hda_codec *codec,
cdev->max_brightness = 1;
cdev->default_trigger = micmute ? "audio-micmute" : "audio-mute";
cdev->brightness_set_blocking = callback;
- cdev->brightness = ledtrig_audio_get(idx);
cdev->flags = LED_CORE_SUSPENDRESUME;
err = led_classdev_register(&codec->core.dev, cdev);
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 2d1df3654424..1b550c42db09 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -1729,9 +1729,11 @@ static int default_bdl_pos_adj(struct azx *chip)
/* some exceptions: Atoms seem problematic with value 1 */
if (chip->pci->vendor == PCI_VENDOR_ID_INTEL) {
switch (chip->pci->device) {
- case 0x0f04: /* Baytrail */
- case 0x2284: /* Braswell */
+ case PCI_DEVICE_ID_INTEL_HDA_BYT:
+ case PCI_DEVICE_ID_INTEL_HDA_BSW:
return 32;
+ case PCI_DEVICE_ID_INTEL_HDA_APL:
+ return 64;
}
}
@@ -2504,6 +2506,8 @@ static const struct pci_device_id azx_ids[] = {
{ PCI_DEVICE_DATA(INTEL, HDA_LNL_P, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
/* Arrow Lake-S */
{ PCI_DEVICE_DATA(INTEL, HDA_ARL_S, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* Arrow Lake */
+ { PCI_DEVICE_DATA(INTEL, HDA_ARL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
/* Apollolake (Broxton-P) */
{ PCI_DEVICE_DATA(INTEL, HDA_APL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON) },
/* Gemini-Lake */
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
index 748a3c40966e..aa312441604f 100644
--- a/sound/pci/hda/patch_ca0132.c
+++ b/sound/pci/hda/patch_ca0132.c
@@ -3022,8 +3022,7 @@ static int dma_convert_to_hda_format(struct hda_codec *codec,
{
unsigned int format_val;
- format_val = snd_hdac_calc_stream_format(sample_rate,
- channels, SNDRV_PCM_FORMAT_S32_LE, 32, 0);
+ format_val = snd_hdac_stream_format(channels, 32, sample_rate);
if (hda_format)
*hda_format = (unsigned short)format_val;
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index a889cccdd607..e8209178d87b 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -21,6 +21,12 @@
#include "hda_jack.h"
#include "hda_generic.h"
+enum {
+ CX_HEADSET_NOPRESENT = 0,
+ CX_HEADSET_PARTPRESENT,
+ CX_HEADSET_ALLPRESENT,
+};
+
struct conexant_spec {
struct hda_gen_spec gen;
@@ -42,7 +48,8 @@ struct conexant_spec {
unsigned int gpio_led;
unsigned int gpio_mute_led_mask;
unsigned int gpio_mic_led_mask;
-
+ unsigned int headset_present_flag;
+ bool is_cx8070_sn6140;
};
@@ -164,6 +171,27 @@ static void cxt_init_gpio_led(struct hda_codec *codec)
}
}
+static void cx_fixup_headset_recog(struct hda_codec *codec)
+{
+ unsigned int mic_persent;
+
+ /* fix some headset type recognize fail issue, such as EDIFIER headset */
+ /* set micbiasd output current comparator threshold from 66% to 55%. */
+ snd_hda_codec_write(codec, 0x1c, 0, 0x320, 0x010);
+ /* set OFF voltage for DFET from -1.2V to -0.8V, set headset micbias registor
+ * value adjustment trim from 2.2K ohms to 2.0K ohms.
+ */
+ snd_hda_codec_write(codec, 0x1c, 0, 0x3b0, 0xe10);
+ /* fix reboot headset type recognize fail issue */
+ mic_persent = snd_hda_codec_read(codec, 0x19, 0, AC_VERB_GET_PIN_SENSE, 0x0);
+ if (mic_persent & AC_PINSENSE_PRESENCE)
+ /* enable headset mic VREF */
+ snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24);
+ else
+ /* disable headset mic VREF */
+ snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20);
+}
+
static int cx_auto_init(struct hda_codec *codec)
{
struct conexant_spec *spec = codec->spec;
@@ -174,6 +202,9 @@ static int cx_auto_init(struct hda_codec *codec)
cxt_init_gpio_led(codec);
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT);
+ if (spec->is_cx8070_sn6140)
+ cx_fixup_headset_recog(codec);
+
return 0;
}
@@ -192,6 +223,77 @@ static void cx_auto_free(struct hda_codec *codec)
snd_hda_gen_free(codec);
}
+static void cx_process_headset_plugin(struct hda_codec *codec)
+{
+ unsigned int val;
+ unsigned int count = 0;
+
+ /* Wait headset detect done. */
+ do {
+ val = snd_hda_codec_read(codec, 0x1c, 0, 0xca0, 0x0);
+ if (val & 0x080) {
+ codec_dbg(codec, "headset type detect done!\n");
+ break;
+ }
+ msleep(20);
+ count++;
+ } while (count < 3);
+ val = snd_hda_codec_read(codec, 0x1c, 0, 0xcb0, 0x0);
+ if (val & 0x800) {
+ codec_dbg(codec, "headset plugin, type is CTIA\n");
+ snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24);
+ } else if (val & 0x400) {
+ codec_dbg(codec, "headset plugin, type is OMTP\n");
+ snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24);
+ } else {
+ codec_dbg(codec, "headphone plugin\n");
+ }
+}
+
+static void cx_update_headset_mic_vref(struct hda_codec *codec, unsigned int res)
+{
+ unsigned int phone_present, mic_persent, phone_tag, mic_tag;
+ struct conexant_spec *spec = codec->spec;
+
+ /* In cx8070 and sn6140, the node 16 can only be config to headphone or disabled,
+ * the node 19 can only be config to microphone or disabled.
+ * Check hp&mic tag to process headset pulgin&plugout.
+ */
+ phone_tag = snd_hda_codec_read(codec, 0x16, 0, AC_VERB_GET_UNSOLICITED_RESPONSE, 0x0);
+ mic_tag = snd_hda_codec_read(codec, 0x19, 0, AC_VERB_GET_UNSOLICITED_RESPONSE, 0x0);
+ if ((phone_tag & (res >> AC_UNSOL_RES_TAG_SHIFT)) ||
+ (mic_tag & (res >> AC_UNSOL_RES_TAG_SHIFT))) {
+ phone_present = snd_hda_codec_read(codec, 0x16, 0, AC_VERB_GET_PIN_SENSE, 0x0);
+ if (!(phone_present & AC_PINSENSE_PRESENCE)) {/* headphone plugout */
+ spec->headset_present_flag = CX_HEADSET_NOPRESENT;
+ snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20);
+ return;
+ }
+ if (spec->headset_present_flag == CX_HEADSET_NOPRESENT) {
+ spec->headset_present_flag = CX_HEADSET_PARTPRESENT;
+ } else if (spec->headset_present_flag == CX_HEADSET_PARTPRESENT) {
+ mic_persent = snd_hda_codec_read(codec, 0x19, 0,
+ AC_VERB_GET_PIN_SENSE, 0x0);
+ /* headset is present */
+ if ((phone_present & AC_PINSENSE_PRESENCE) &&
+ (mic_persent & AC_PINSENSE_PRESENCE)) {
+ cx_process_headset_plugin(codec);
+ spec->headset_present_flag = CX_HEADSET_ALLPRESENT;
+ }
+ }
+ }
+}
+
+static void cx_jack_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+ struct conexant_spec *spec = codec->spec;
+
+ if (spec->is_cx8070_sn6140)
+ cx_update_headset_mic_vref(codec, res);
+
+ snd_hda_jack_unsol_event(codec, res);
+}
+
#ifdef CONFIG_PM
static int cx_auto_suspend(struct hda_codec *codec)
{
@@ -205,7 +307,7 @@ static const struct hda_codec_ops cx_auto_patch_ops = {
.build_pcms = snd_hda_gen_build_pcms,
.init = cx_auto_init,
.free = cx_auto_free,
- .unsol_event = snd_hda_jack_unsol_event,
+ .unsol_event = cx_jack_unsol_event,
#ifdef CONFIG_PM
.suspend = cx_auto_suspend,
.check_power_status = snd_hda_gen_check_power_status,
@@ -242,6 +344,7 @@ enum {
CXT_FIXUP_HP_ZBOOK_MUTE_LED,
CXT_FIXUP_HEADSET_MIC,
CXT_FIXUP_HP_MIC_NO_PRESENCE,
+ CXT_PINCFG_SWS_JS201D,
};
/* for hda_fixup_thinkpad_acpi() */
@@ -739,6 +842,17 @@ static const struct hda_pintbl cxt_pincfg_lemote[] = {
{}
};
+/* SuoWoSi/South-holding JS201D with sn6140 */
+static const struct hda_pintbl cxt_pincfg_sws_js201d[] = {
+ { 0x16, 0x03211040 }, /* hp out */
+ { 0x17, 0x91170110 }, /* SPK/Class_D */
+ { 0x18, 0x95a70130 }, /* Internal mic */
+ { 0x19, 0x03a11020 }, /* Headset Mic */
+ { 0x1a, 0x40f001f0 }, /* Not used */
+ { 0x21, 0x40f001f0 }, /* Not used */
+ {}
+};
+
static const struct hda_fixup cxt_fixups[] = {
[CXT_PINCFG_LENOVO_X200] = {
.type = HDA_FIXUP_PINS,
@@ -894,6 +1008,10 @@ static const struct hda_fixup cxt_fixups[] = {
.chained = true,
.chain_id = CXT_FIXUP_HEADSET_MIC,
},
+ [CXT_PINCFG_SWS_JS201D] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = cxt_pincfg_sws_js201d,
+ },
};
static const struct snd_pci_quirk cxt5045_fixups[] = {
@@ -967,6 +1085,7 @@ static const struct snd_pci_quirk cxt5066_fixups[] = {
SND_PCI_QUIRK(0x103c, 0x8457, "HP Z2 G4 mini", CXT_FIXUP_HP_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x103c, 0x8458, "HP Z2 G4 mini premium", CXT_FIXUP_HP_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1043, 0x138d, "Asus", CXT_FIXUP_HEADPHONE_MIC_PIN),
+ SND_PCI_QUIRK(0x14f1, 0x0265, "SWS JS201D", CXT_PINCFG_SWS_JS201D),
SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT_FIXUP_OLPC_XO),
SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400", CXT_PINCFG_LENOVO_TP410),
SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo T410", CXT_PINCFG_LENOVO_TP410),
@@ -1007,6 +1126,7 @@ static const struct hda_model_fixup cxt5066_fixup_models[] = {
{ .id = CXT_FIXUP_HP_ZBOOK_MUTE_LED, .name = "hp-zbook-mute-led" },
{ .id = CXT_FIXUP_HP_MIC_NO_PRESENCE, .name = "hp-mic-fix" },
{ .id = CXT_PINCFG_LENOVO_NOTEBOOK, .name = "lenovo-20149" },
+ { .id = CXT_PINCFG_SWS_JS201D, .name = "sws-js201d" },
{}
};
@@ -1042,6 +1162,15 @@ static int patch_conexant_auto(struct hda_codec *codec)
codec->spec = spec;
codec->patch_ops = cx_auto_patch_ops;
+ /* init cx8070/sn6140 flag and reset headset_present_flag */
+ switch (codec->core.vendor_id) {
+ case 0x14f11f86:
+ case 0x14f11f87:
+ spec->is_cx8070_sn6140 = true;
+ spec->headset_present_flag = CX_HEADSET_NOPRESENT;
+ break;
+ }
+
cx_auto_parse_eapd(codec);
spec->gen.own_eapd_ctl = 1;
diff --git a/sound/pci/hda/patch_cs8409.c b/sound/pci/hda/patch_cs8409.c
index 627899959ffe..e41316e2e983 100644
--- a/sound/pci/hda/patch_cs8409.c
+++ b/sound/pci/hda/patch_cs8409.c
@@ -1371,6 +1371,7 @@ void dolphin_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int ac
spec->scodecs[CS8409_CODEC1] = &dolphin_cs42l42_1;
spec->scodecs[CS8409_CODEC1]->codec = codec;
spec->num_scodecs = 2;
+ spec->gen.suppress_vmaster = 1;
codec->patch_ops = cs8409_dolphin_patch_ops;
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index 78cee53fee02..495d63101186 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -1655,7 +1655,6 @@ static void hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin,
#define I915_SILENT_RATE 48000
#define I915_SILENT_CHANNELS 2
-#define I915_SILENT_FORMAT SNDRV_PCM_FORMAT_S16_LE
#define I915_SILENT_FORMAT_BITS 16
#define I915_SILENT_FMT_MASK 0xf
@@ -1668,8 +1667,8 @@ static void silent_stream_enable_i915(struct hda_codec *codec,
per_pin->dev_id, I915_SILENT_RATE);
/* trigger silent stream generation in hw */
- format = snd_hdac_calc_stream_format(I915_SILENT_RATE, I915_SILENT_CHANNELS,
- I915_SILENT_FORMAT, I915_SILENT_FORMAT_BITS, 0);
+ format = snd_hdac_stream_format(I915_SILENT_CHANNELS, I915_SILENT_FORMAT_BITS,
+ I915_SILENT_RATE);
snd_hda_codec_setup_stream(codec, per_pin->cvt_nid,
I915_SILENT_FMT_MASK, I915_SILENT_FMT_MASK, format);
usleep_range(100, 200);
@@ -1977,6 +1976,7 @@ static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
err = snd_hda_query_supported_pcm(codec, cvt_nid,
&per_cvt->rates,
&per_cvt->formats,
+ NULL,
&per_cvt->maxbps);
if (err < 0)
return err;
@@ -2301,6 +2301,7 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec)
codec_dbg(codec, "hdmi: pcm_num set to %d\n", pcm_num);
for (idx = 0; idx < pcm_num; idx++) {
+ struct hdmi_spec_per_cvt *per_cvt;
struct hda_pcm *info;
struct hda_pcm_stream *pstr;
@@ -2316,6 +2317,11 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec)
pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK];
pstr->substreams = 1;
pstr->ops = generic_ops;
+
+ per_cvt = get_cvt(spec, 0);
+ pstr->channels_min = per_cvt->channels_min;
+ pstr->channels_max = per_cvt->channels_max;
+
/* pcm number is less than pcm_rec array size */
if (spec->pcm_used >= ARRAY_SIZE(spec->pcm_rec))
break;
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 70b17b08d4ff..a1facdb98d9a 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -439,6 +439,10 @@ static void alc_fill_eapd_coef(struct hda_codec *codec)
alc_update_coef_idx(codec, 0x67, 0xf000, 0x3000);
fallthrough;
case 0x10ec0215:
+ case 0x10ec0285:
+ case 0x10ec0289:
+ alc_update_coef_idx(codec, 0x36, 1<<13, 0);
+ fallthrough;
case 0x10ec0230:
case 0x10ec0233:
case 0x10ec0235:
@@ -452,9 +456,7 @@ static void alc_fill_eapd_coef(struct hda_codec *codec)
case 0x10ec0283:
case 0x10ec0286:
case 0x10ec0288:
- case 0x10ec0285:
case 0x10ec0298:
- case 0x10ec0289:
case 0x10ec0300:
alc_update_coef_idx(codec, 0x10, 1<<9, 0);
break;
@@ -3682,6 +3684,7 @@ static void alc285_hp_init(struct hda_codec *codec)
int i, val;
int coef38, coef0d, coef36;
+ alc_write_coefex_idx(codec, 0x58, 0x00, 0x1888); /* write default value */
alc_update_coef_idx(codec, 0x4a, 1<<15, 1<<15); /* Reset HP JD */
coef38 = alc_read_coef_idx(codec, 0x38); /* Amp control */
coef0d = alc_read_coef_idx(codec, 0x0d); /* Digital Misc control */
@@ -6956,6 +6959,11 @@ static void cs35l41_fixup_i2c_two(struct hda_codec *cdc, const struct hda_fixup
cs35l41_generic_fixup(cdc, action, "i2c", "CSC3551", 2);
}
+static void cs35l41_fixup_i2c_four(struct hda_codec *cdc, const struct hda_fixup *fix, int action)
+{
+ cs35l41_generic_fixup(cdc, action, "i2c", "CSC3551", 4);
+}
+
static void cs35l41_fixup_spi_two(struct hda_codec *codec, const struct hda_fixup *fix, int action)
{
cs35l41_generic_fixup(codec, action, "spi", "CSC3551", 2);
@@ -6984,6 +6992,12 @@ static void tas2781_fixup_i2c(struct hda_codec *cdc,
tas2781_generic_fixup(cdc, action, "i2c", "TIAS2781");
}
+static void yoga7_14arb7_fixup_i2c(struct hda_codec *cdc,
+ const struct hda_fixup *fix, int action)
+{
+ tas2781_generic_fixup(cdc, action, "i2c", "INT8866");
+}
+
/* for alc295_fixup_hp_top_speakers */
#include "hp_x360_helper.c"
@@ -7431,6 +7445,7 @@ enum {
ALC287_FIXUP_LEGION_15IMHG05_AUTOMUTE,
ALC287_FIXUP_YOGA7_14ITL_SPEAKERS,
ALC298_FIXUP_LENOVO_C940_DUET7,
+ ALC287_FIXUP_LENOVO_14IRP8_DUETITL,
ALC287_FIXUP_13S_GEN2_SPEAKERS,
ALC256_FIXUP_SET_COEF_DEFAULTS,
ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE,
@@ -7441,6 +7456,7 @@ enum {
ALC287_FIXUP_LEGION_16ACHG6,
ALC287_FIXUP_CS35L41_I2C_2,
ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED,
+ ALC287_FIXUP_CS35L41_I2C_4,
ALC245_FIXUP_CS35L41_SPI_2,
ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED,
ALC245_FIXUP_CS35L41_SPI_4,
@@ -7454,6 +7470,7 @@ enum {
ALC236_FIXUP_DELL_DUAL_CODECS,
ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI,
ALC287_FIXUP_TAS2781_I2C,
+ ALC287_FIXUP_YOGA7_14ARB7_I2C,
ALC245_FIXUP_HP_MUTE_LED_COEFBIT,
ALC245_FIXUP_HP_X360_MUTE_LEDS,
ALC287_FIXUP_THINKPAD_I2S_SPK,
@@ -7480,6 +7497,26 @@ static void alc298_fixup_lenovo_c940_duet7(struct hda_codec *codec,
__snd_hda_apply_fixup(codec, id, action, 0);
}
+/* A special fixup for Lenovo Slim/Yoga Pro 9 14IRP8 and Yoga DuetITL 2021;
+ * 14IRP8 PCI SSID will mistakenly be matched with the DuetITL codec SSID,
+ * so we need to apply a different fixup in this case. The only DuetITL codec
+ * SSID reported so far is the 17aa:3802 while the 14IRP8 has the 17aa:38be
+ * and 17aa:38bf. If it weren't for the PCI SSID, the 14IRP8 models would
+ * have matched correctly by their codecs.
+ */
+static void alc287_fixup_lenovo_14irp8_duetitl(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ int id;
+
+ if (codec->core.subsystem_id == 0x17aa3802)
+ id = ALC287_FIXUP_YOGA7_14ITL_SPEAKERS; /* DuetITL */
+ else
+ id = ALC287_FIXUP_TAS2781_I2C; /* 14IRP8 */
+ __snd_hda_apply_fixup(codec, id, action, 0);
+}
+
static const struct hda_fixup alc269_fixups[] = {
[ALC269_FIXUP_GPIO2] = {
.type = HDA_FIXUP_FUNC,
@@ -9364,6 +9401,10 @@ static const struct hda_fixup alc269_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc298_fixup_lenovo_c940_duet7,
},
+ [ALC287_FIXUP_LENOVO_14IRP8_DUETITL] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc287_fixup_lenovo_14irp8_duetitl,
+ },
[ALC287_FIXUP_13S_GEN2_SPEAKERS] = {
.type = HDA_FIXUP_VERBS,
.v.verbs = (const struct hda_verb[]) {
@@ -9427,6 +9468,10 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC285_FIXUP_HP_MUTE_LED,
},
+ [ALC287_FIXUP_CS35L41_I2C_4] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs35l41_fixup_i2c_four,
+ },
[ALC245_FIXUP_CS35L41_SPI_2] = {
.type = HDA_FIXUP_FUNC,
.v.func = cs35l41_fixup_spi_two,
@@ -9560,13 +9605,19 @@ static const struct hda_fixup alc269_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = cs35l41_fixup_i2c_two,
.chained = true,
- .chain_id = ALC269_FIXUP_THINKPAD_ACPI,
+ .chain_id = ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK,
},
[ALC287_FIXUP_TAS2781_I2C] = {
.type = HDA_FIXUP_FUNC,
.v.func = tas2781_fixup_i2c,
.chained = true,
- .chain_id = ALC269_FIXUP_THINKPAD_ACPI,
+ .chain_id = ALC285_FIXUP_THINKPAD_HEADSET_JACK,
+ },
+ [ALC287_FIXUP_YOGA7_14ARB7_I2C] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = yoga7_14arb7_fixup_i2c,
+ .chained = true,
+ .chain_id = ALC285_FIXUP_THINKPAD_HEADSET_JACK,
},
[ALC245_FIXUP_HP_MUTE_LED_COEFBIT] = {
.type = HDA_FIXUP_FUNC,
@@ -9581,6 +9632,8 @@ static const struct hda_fixup alc269_fixups[] = {
[ALC287_FIXUP_THINKPAD_I2S_SPK] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc287_fixup_bind_dacs,
+ .chained = true,
+ .chain_id = ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK,
},
[ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD] = {
.type = HDA_FIXUP_FUNC,
@@ -9630,6 +9683,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1025, 0x1247, "Acer vCopperbox", ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS),
SND_PCI_QUIRK(0x1025, 0x1248, "Acer Veriton N4660G", ALC269VC_FIXUP_ACER_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1025, 0x1269, "Acer SWIFT SF314-54", ALC256_FIXUP_ACER_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1025, 0x126a, "Acer Swift SF114-32", ALC256_FIXUP_ACER_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1025, 0x128f, "Acer Veriton Z6860G", ALC286_FIXUP_ACER_AIO_HEADSET_MIC),
SND_PCI_QUIRK(0x1025, 0x1290, "Acer Veriton Z4860G", ALC286_FIXUP_ACER_AIO_HEADSET_MIC),
SND_PCI_QUIRK(0x1025, 0x1291, "Acer Veriton Z4660G", ALC286_FIXUP_ACER_AIO_HEADSET_MIC),
@@ -9703,16 +9757,23 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1028, 0x0a9e, "Dell Latitude 5430", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x0b19, "Dell XPS 15 9520", ALC289_FIXUP_DUAL_SPK),
SND_PCI_QUIRK(0x1028, 0x0b1a, "Dell Precision 5570", ALC289_FIXUP_DUAL_SPK),
+ SND_PCI_QUIRK(0x1028, 0x0b27, "Dell", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1028, 0x0b28, "Dell", ALC245_FIXUP_CS35L41_SPI_2),
SND_PCI_QUIRK(0x1028, 0x0b37, "Dell Inspiron 16 Plus 7620 2-in-1", ALC295_FIXUP_DELL_INSPIRON_TOP_SPEAKERS),
SND_PCI_QUIRK(0x1028, 0x0b71, "Dell Inspiron 16 Plus 7620", ALC295_FIXUP_DELL_INSPIRON_TOP_SPEAKERS),
SND_PCI_QUIRK(0x1028, 0x0beb, "Dell XPS 15 9530 (2023)", ALC289_FIXUP_DELL_CS35L41_SPI_2),
SND_PCI_QUIRK(0x1028, 0x0c03, "Dell Precision 5340", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x0c0b, "Dell Oasis 14 RPL-P", ALC289_FIXUP_RTK_AMP_DUAL_SPK),
+ SND_PCI_QUIRK(0x1028, 0x0c0d, "Dell Oasis", ALC289_FIXUP_RTK_AMP_DUAL_SPK),
+ SND_PCI_QUIRK(0x1028, 0x0c0e, "Dell Oasis 16", ALC289_FIXUP_RTK_AMP_DUAL_SPK),
SND_PCI_QUIRK(0x1028, 0x0c19, "Dell Precision 3340", ALC236_FIXUP_DELL_DUAL_CODECS),
SND_PCI_QUIRK(0x1028, 0x0c1a, "Dell Precision 3340", ALC236_FIXUP_DELL_DUAL_CODECS),
SND_PCI_QUIRK(0x1028, 0x0c1b, "Dell Precision 3440", ALC236_FIXUP_DELL_DUAL_CODECS),
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, 0x0c28, "Dell Inspiron 16 Plus 7630", ALC295_FIXUP_DELL_INSPIRON_TOP_SPEAKERS),
+ SND_PCI_QUIRK(0x1028, 0x0c4d, "Dell", ALC287_FIXUP_CS35L41_I2C_4),
SND_PCI_QUIRK(0x1028, 0x0cbd, "Dell Oasis 13 CS MTL-U", ALC289_FIXUP_DELL_CS35L41_SPI_2),
SND_PCI_QUIRK(0x1028, 0x0cbe, "Dell Oasis 13 2-IN-1 MTL-U", ALC289_FIXUP_DELL_CS35L41_SPI_2),
SND_PCI_QUIRK(0x1028, 0x0cbf, "Dell Oasis 13 Low Weight MTU-L", ALC289_FIXUP_DELL_CS35L41_SPI_2),
@@ -9816,6 +9877,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8735, "HP ProBook 435 G7", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
SND_PCI_QUIRK(0x103c, 0x8736, "HP", ALC285_FIXUP_HP_GPIO_AMP_INIT),
SND_PCI_QUIRK(0x103c, 0x8760, "HP", ALC285_FIXUP_HP_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x876e, "HP ENVY x360 Convertible 13-ay0xxx", ALC245_FIXUP_HP_X360_MUTE_LEDS),
SND_PCI_QUIRK(0x103c, 0x877a, "HP", ALC285_FIXUP_HP_MUTE_LED),
SND_PCI_QUIRK(0x103c, 0x877d, "HP", ALC236_FIXUP_HP_MUTE_LED),
SND_PCI_QUIRK(0x103c, 0x8780, "HP ZBook Fury 17 G7 Mobile Workstation",
@@ -9825,6 +9887,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8786, "HP OMEN 15", ALC285_FIXUP_HP_MUTE_LED),
SND_PCI_QUIRK(0x103c, 0x8787, "HP OMEN 15", ALC285_FIXUP_HP_MUTE_LED),
SND_PCI_QUIRK(0x103c, 0x8788, "HP OMEN 15", ALC285_FIXUP_HP_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x87b7, "HP Laptop 14-fq0xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
SND_PCI_QUIRK(0x103c, 0x87c8, "HP", ALC287_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x87e5, "HP ProBook 440 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x87e7, "HP ProBook 450 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED),
@@ -9834,6 +9897,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x87f5, "HP", ALC287_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x87f6, "HP Spectre x360 14", ALC245_FIXUP_HP_X360_AMP),
SND_PCI_QUIRK(0x103c, 0x87f7, "HP Spectre x360 14", ALC245_FIXUP_HP_X360_AMP),
+ SND_PCI_QUIRK(0x103c, 0x87fe, "HP Laptop 15s-fq2xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
SND_PCI_QUIRK(0x103c, 0x8805, "HP ProBook 650 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x880d, "HP EliteBook 830 G8 Notebook PC", ALC285_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8811, "HP Spectre x360 15-eb1xxx", ALC285_FIXUP_HP_SPECTRE_X360_EB1),
@@ -9865,6 +9929,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8973, "HP EliteBook 860 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8974, "HP EliteBook 840 Aero G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8975, "HP EliteBook x360 840 Aero G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x897d, "HP mt440 Mobile Thin Client U74", ALC236_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8981, "HP Elite Dragonfly G3", ALC245_FIXUP_CS35L41_SPI_4),
SND_PCI_QUIRK(0x103c, 0x898e, "HP EliteBook 835 G9", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x103c, 0x898f, "HP EliteBook 835 G9", ALC287_FIXUP_CS35L41_I2C_2),
@@ -9890,16 +9955,20 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8aa3, "HP ProBook 450 G9 (MB 8AA1)", ALC236_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8aa8, "HP EliteBook 640 G9 (MB 8AA6)", ALC236_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8aab, "HP EliteBook 650 G9 (MB 8AA9)", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8ab9, "HP EliteBook 840 G8 (MB 8AB8)", ALC285_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8abb, "HP ZBook Firefly 14 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8ad1, "HP EliteBook 840 14 inch G9 Notebook PC", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8ad2, "HP EliteBook 860 16 inch G9 Notebook PC", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8b0f, "HP Elite mt645 G7 Mobile Thin Client U81", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
SND_PCI_QUIRK(0x103c, 0x8b2f, "HP 255 15.6 inch G10 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
+ SND_PCI_QUIRK(0x103c, 0x8b3f, "HP mt440 Mobile Thin Client U91", ALC236_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8b42, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8b43, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8b44, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8b45, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8b46, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8b47, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8b59, "HP Elite mt645 G7 Mobile Thin Client U89", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
SND_PCI_QUIRK(0x103c, 0x8b5d, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
SND_PCI_QUIRK(0x103c, 0x8b5e, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
SND_PCI_QUIRK(0x103c, 0x8b63, "HP Elite Dragonfly 13.5 inch G4", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED),
@@ -9927,7 +9996,14 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8c70, "HP EliteBook 835 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8c71, "HP EliteBook 845 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8c72, "HP EliteBook 865 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c8a, "HP EliteBook 630", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c8c, "HP EliteBook 660", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c90, "HP EliteBook 640", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c91, "HP EliteBook 660", ALC236_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8c96, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8c97, "HP ZBook", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8ca1, "HP ZBook Power", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8ca2, "HP ZBook Power", ALC236_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8ca4, "HP ZBook Fury", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8ca7, "HP ZBook Fury", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8cf5, "HP ZBook Studio 16", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED),
@@ -9965,6 +10041,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1043, 0x1662, "ASUS GV301QH", ALC294_FIXUP_ASUS_DUAL_SPK),
SND_PCI_QUIRK(0x1043, 0x1663, "ASUS GU603ZI/ZJ/ZQ/ZU/ZV", ALC285_FIXUP_ASUS_HEADSET_MIC),
SND_PCI_QUIRK(0x1043, 0x1683, "ASUS UM3402YAR", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1043, 0x16a3, "ASUS UX3402VA", ALC245_FIXUP_CS35L41_SPI_2),
SND_PCI_QUIRK(0x1043, 0x16b2, "ASUS GU603", ALC289_FIXUP_ASUS_GA401),
SND_PCI_QUIRK(0x1043, 0x16d3, "ASUS UX5304VA", ALC245_FIXUP_CS35L41_SPI_2),
SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_FIXUP_STEREO_DMIC),
@@ -10008,14 +10085,12 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1043, 0x1d4e, "ASUS TM420", ALC256_FIXUP_ASUS_HPE),
SND_PCI_QUIRK(0x1043, 0x1da2, "ASUS UP6502ZA/ZD", ALC245_FIXUP_CS35L41_SPI_2),
SND_PCI_QUIRK(0x1043, 0x1e02, "ASUS UX3402ZA", ALC245_FIXUP_CS35L41_SPI_2),
- SND_PCI_QUIRK(0x1043, 0x16a3, "ASUS UX3402VA", ALC245_FIXUP_CS35L41_SPI_2),
- SND_PCI_QUIRK(0x1043, 0x1f62, "ASUS UX7602ZM", ALC245_FIXUP_CS35L41_SPI_2),
SND_PCI_QUIRK(0x1043, 0x1e11, "ASUS Zephyrus G15", ALC289_FIXUP_ASUS_GA502),
- SND_PCI_QUIRK(0x1043, 0x1e12, "ASUS UM6702RA/RC", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1043, 0x1e12, "ASUS UM3402", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x1043, 0x1e51, "ASUS Zephyrus M15", ALC294_FIXUP_ASUS_GU502_PINS),
SND_PCI_QUIRK(0x1043, 0x1e5e, "ASUS ROG Strix G513", ALC294_FIXUP_ASUS_G513_PINS),
SND_PCI_QUIRK(0x1043, 0x1e8e, "ASUS Zephyrus G15", ALC289_FIXUP_ASUS_GA401),
- SND_PCI_QUIRK(0x1043, 0x1ee2, "ASUS UM3402", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1043, 0x1ee2, "ASUS UM6702RA/RC", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x1043, 0x1c52, "ASUS Zephyrus G15 2022", ALC289_FIXUP_ASUS_GA401),
SND_PCI_QUIRK(0x1043, 0x1f11, "ASUS Zephyrus G14", ALC289_FIXUP_ASUS_GA401),
SND_PCI_QUIRK(0x1043, 0x1f12, "ASUS UM5302", ALC287_FIXUP_CS35L41_I2C_2),
@@ -10204,8 +10279,9 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x3176, "ThinkCentre Station", ALC283_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x17aa, 0x3178, "ThinkCentre Station", ALC283_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x17aa, 0x31af, "ThinkCentre Station", ALC623_FIXUP_LENOVO_THINKSTATION_P340),
+ SND_PCI_QUIRK(0x17aa, 0x334b, "Lenovo ThinkCentre M70 Gen5", ALC283_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x17aa, 0x3801, "Lenovo Yoga9 14IAP7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN),
- SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo Yoga DuetITL 2021", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS),
+ SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo Yoga Pro 9 14IRP8 / DuetITL 2021", ALC287_FIXUP_LENOVO_14IRP8_DUETITL),
SND_PCI_QUIRK(0x17aa, 0x3813, "Legion 7i 15IMHG05", ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS),
SND_PCI_QUIRK(0x17aa, 0x3818, "Lenovo C940 / Yoga Duet 7", ALC298_FIXUP_LENOVO_C940_DUET7),
SND_PCI_QUIRK(0x17aa, 0x3819, "Lenovo 13s Gen2 ITL", ALC287_FIXUP_13S_GEN2_SPEAKERS),
@@ -10221,6 +10297,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x3853, "Lenovo Yoga 7 15ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS),
SND_PCI_QUIRK(0x17aa, 0x3855, "Legion 7 16ITHG6", ALC287_FIXUP_LEGION_16ITHG6),
SND_PCI_QUIRK(0x17aa, 0x3869, "Lenovo Yoga7 14IAL7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN),
+ SND_PCI_QUIRK(0x17aa, 0x386f, "Legion 7i 16IAX7", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x17aa, 0x3870, "Lenovo Yoga 7 14ARB7", ALC287_FIXUP_YOGA7_14ARB7_I2C),
SND_PCI_QUIRK(0x17aa, 0x387d, "Yoga S780-16 pro Quad AAC", ALC287_FIXUP_TAS2781_I2C),
SND_PCI_QUIRK(0x17aa, 0x387e, "Yoga S780-16 pro Quad YC", ALC287_FIXUP_TAS2781_I2C),
SND_PCI_QUIRK(0x17aa, 0x3881, "YB9 dual power mode2 YC", ALC287_FIXUP_TAS2781_I2C),
@@ -10229,6 +10307,12 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x3886, "Y780 VECO DUAL", ALC287_FIXUP_TAS2781_I2C),
SND_PCI_QUIRK(0x17aa, 0x38a7, "Y780P AMD YG dual", ALC287_FIXUP_TAS2781_I2C),
SND_PCI_QUIRK(0x17aa, 0x38a8, "Y780P AMD VECO dual", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38a9, "Thinkbook 16P", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x17aa, 0x38ab, "Thinkbook 16P", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x17aa, 0x38b4, "Legion Slim 7 16IRH8", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x17aa, 0x38b5, "Legion Slim 7 16IRH8", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x17aa, 0x38b6, "Legion Slim 7 16APH8", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x17aa, 0x38b7, "Legion Slim 7 16APH8", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x17aa, 0x38ba, "Yoga S780-14.5 Air AMD quad YC", ALC287_FIXUP_TAS2781_I2C),
SND_PCI_QUIRK(0x17aa, 0x38bb, "Yoga S780-14.5 Air AMD quad AAC", ALC287_FIXUP_TAS2781_I2C),
SND_PCI_QUIRK(0x17aa, 0x38be, "Yoga S980-14.5 proX YC Dual", ALC287_FIXUP_TAS2781_I2C),
@@ -10287,6 +10371,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1d72, 0x1945, "Redmi G", ALC256_FIXUP_ASUS_HEADSET_MIC),
SND_PCI_QUIRK(0x1d72, 0x1947, "RedmiBook Air", ALC255_FIXUP_XIAOMI_HEADSET_MIC),
SND_PCI_QUIRK(0x2782, 0x0232, "CHUWI CoreBook XPro", ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO),
+ SND_PCI_QUIRK(0x2782, 0x1707, "Vaio VJFE-ADL", ALC298_FIXUP_SPK_VOLUME),
SND_PCI_QUIRK(0x8086, 0x2074, "Intel NUC 8", ALC233_FIXUP_INTEL_NUC8_DMIC),
SND_PCI_QUIRK(0x8086, 0x2080, "Intel NUC 8 Rugged", ALC256_FIXUP_INTEL_NUC8_RUGGED),
SND_PCI_QUIRK(0x8086, 0x2081, "Intel NUC 10", ALC256_FIXUP_INTEL_NUC10),
@@ -10916,6 +11001,8 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
* at most one tbl is allowed to define for the same vendor and same codec
*/
static const struct snd_hda_pin_quirk alc269_fallback_pin_fixup_tbl[] = {
+ SND_HDA_PIN_QUIRK(0x10ec0256, 0x1025, "Acer", ALC2XX_FIXUP_HEADSET_MIC,
+ {0x19, 0x40000000}),
SND_HDA_PIN_QUIRK(0x10ec0289, 0x1028, "Dell", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE,
{0x19, 0x40000000},
{0x1b, 0x40000000}),
@@ -11605,8 +11692,7 @@ static void alc897_hp_automute_hook(struct hda_codec *codec,
snd_hda_gen_hp_automute(codec, jack);
vref = spec->gen.hp_jack_present ? (PIN_HP | AC_PINCTL_VREF_100) : PIN_HP;
- snd_hda_codec_write(codec, 0x1b, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- vref);
+ snd_hda_set_pin_ctl(codec, 0x1b, vref);
}
static void alc897_fixup_lenovo_headset_mic(struct hda_codec *codec,
@@ -11615,6 +11701,10 @@ static void alc897_fixup_lenovo_headset_mic(struct hda_codec *codec,
struct alc_spec *spec = codec->spec;
if (action == HDA_FIXUP_ACT_PRE_PROBE) {
spec->gen.hp_automute_hook = alc897_hp_automute_hook;
+ spec->no_shutup_pins = 1;
+ }
+ if (action == HDA_FIXUP_ACT_PROBE) {
+ snd_hda_set_pin_ctl_cache(codec, 0x1a, PIN_IN | AC_PINCTL_VREF_100);
}
}
diff --git a/sound/pci/hda/tas2781_hda_i2c.c b/sound/pci/hda/tas2781_hda_i2c.c
index dfe281b57aa6..1bfb00102a77 100644
--- a/sound/pci/hda/tas2781_hda_i2c.c
+++ b/sound/pci/hda/tas2781_hda_i2c.c
@@ -65,6 +65,24 @@ enum calib_data {
CALIB_MAX
};
+#define TAS2563_MAX_CHANNELS 4
+
+#define TAS2563_CAL_POWER TASDEVICE_REG(0, 0x0d, 0x3c)
+#define TAS2563_CAL_R0 TASDEVICE_REG(0, 0x0f, 0x34)
+#define TAS2563_CAL_INVR0 TASDEVICE_REG(0, 0x0f, 0x40)
+#define TAS2563_CAL_R0_LOW TASDEVICE_REG(0, 0x0f, 0x48)
+#define TAS2563_CAL_TLIM TASDEVICE_REG(0, 0x10, 0x14)
+#define TAS2563_CAL_N 5
+#define TAS2563_CAL_DATA_SIZE 4
+#define TAS2563_CAL_CH_SIZE 20
+#define TAS2563_CAL_ARRAY_SIZE 80
+
+static unsigned int cal_regs[TAS2563_CAL_N] = {
+ TAS2563_CAL_POWER, TAS2563_CAL_R0, TAS2563_CAL_INVR0,
+ TAS2563_CAL_R0_LOW, TAS2563_CAL_TLIM,
+};
+
+
struct tas2781_hda {
struct device *dev;
struct tasdevice_priv *priv;
@@ -81,7 +99,7 @@ static int tas2781_get_i2c_res(struct acpi_resource *ares, void *data)
if (i2c_acpi_get_i2c_resource(ares, &sb)) {
if (tas_priv->ndev < TASDEVICE_MAX_CHANNELS &&
- sb->slave_address != TAS2781_GLOBAL_ADDR) {
+ sb->slave_address != tas_priv->global_addr) {
tas_priv->tasdevice[tas_priv->ndev].dev_addr =
(unsigned int)sb->slave_address;
tas_priv->ndev++;
@@ -404,6 +422,69 @@ static const struct snd_kcontrol_new tas2781_dsp_conf_ctrl = {
.put = tasdevice_config_put,
};
+static void tas2563_apply_calib(struct tasdevice_priv *tas_priv)
+{
+ int offset = 0;
+ __be32 data;
+ int ret;
+
+ for (int i = 0; i < tas_priv->ndev; i++) {
+ for (int j = 0; j < TAS2563_CAL_N; ++j) {
+ data = cpu_to_be32(
+ *(uint32_t *)&tas_priv->cali_data.data[offset]);
+ ret = tasdevice_dev_bulk_write(tas_priv, i, cal_regs[j],
+ (unsigned char *)&data, TAS2563_CAL_DATA_SIZE);
+ if (ret)
+ dev_err(tas_priv->dev,
+ "Error writing calib regs\n");
+ offset += TAS2563_CAL_DATA_SIZE;
+ }
+ }
+}
+
+static int tas2563_save_calibration(struct tasdevice_priv *tas_priv)
+{
+ static efi_guid_t efi_guid = EFI_GUID(0x1f52d2a1, 0xbb3a, 0x457d, 0xbc,
+ 0x09, 0x43, 0xa3, 0xf4, 0x31, 0x0a, 0x92);
+
+ static efi_char16_t *efi_vars[TAS2563_MAX_CHANNELS][TAS2563_CAL_N] = {
+ { L"Power_1", L"R0_1", L"InvR0_1", L"R0_Low_1", L"TLim_1" },
+ { L"Power_2", L"R0_2", L"InvR0_2", L"R0_Low_2", L"TLim_2" },
+ { L"Power_3", L"R0_3", L"InvR0_3", L"R0_Low_3", L"TLim_3" },
+ { L"Power_4", L"R0_4", L"InvR0_4", L"R0_Low_4", L"TLim_4" },
+ };
+
+ unsigned long max_size = TAS2563_CAL_DATA_SIZE;
+ unsigned int offset = 0;
+ efi_status_t status;
+ unsigned int attr;
+
+ tas_priv->cali_data.data = devm_kzalloc(tas_priv->dev,
+ TAS2563_CAL_ARRAY_SIZE, GFP_KERNEL);
+ if (!tas_priv->cali_data.data)
+ return -ENOMEM;
+
+ for (int i = 0; i < tas_priv->ndev; ++i) {
+ for (int j = 0; j < TAS2563_CAL_N; ++j) {
+ status = efi.get_variable(efi_vars[i][j],
+ &efi_guid, &attr, &max_size,
+ &tas_priv->cali_data.data[offset]);
+ if (status != EFI_SUCCESS ||
+ max_size != TAS2563_CAL_DATA_SIZE) {
+ dev_warn(tas_priv->dev,
+ "Calibration data read failed %ld\n", status);
+ return -EINVAL;
+ }
+ offset += TAS2563_CAL_DATA_SIZE;
+ }
+ }
+
+ tas_priv->cali_data.total_sz = offset;
+ tasdevice_apply_calibration(tas_priv);
+
+ return 0;
+}
+
static void tas2781_apply_calib(struct tasdevice_priv *tas_priv)
{
static const unsigned char page_array[CALIB_MAX] = {
@@ -479,7 +560,7 @@ static int tas2781_save_calibration(struct tasdevice_priv *tas_priv)
dev_dbg(tas_priv->dev, "%4ld-%2d-%2d, %2d:%2d:%2d\n",
tm->tm_year, tm->tm_mon, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec);
- tas2781_apply_calib(tas_priv);
+ tasdevice_apply_calibration(tas_priv);
} else
tas_priv->cali_data.total_sz = 0;
@@ -582,7 +663,9 @@ static void tasdev_fw_ready(const struct firmware *fmw, void *context)
/* If calibrated data occurs error, dsp will still works with default
* calibrated data inside algo.
*/
- tas2781_save_calibration(tas_priv);
+ tasdevice_save_calibration(tas_priv);
+
+ tasdevice_tuning_switch(tas_hda->priv, 0);
out:
mutex_unlock(&tas_hda->priv->codec_lock);
@@ -627,7 +710,7 @@ static int tas2781_hda_bind(struct device *dev, struct device *master,
strscpy(comps->name, dev_name(dev), sizeof(comps->name));
- ret = tascodec_init(tas_hda->priv, codec, tasdev_fw_ready);
+ ret = tascodec_init(tas_hda->priv, codec, THIS_MODULE, tasdev_fw_ready);
if (!ret)
comps->playback_hook = tas2781_hda_playback_hook;
@@ -683,10 +766,6 @@ static int tas2781_hda_i2c_probe(struct i2c_client *clt)
const char *device_name;
int ret;
- if (strstr(dev_name(&clt->dev), "TIAS2781"))
- device_name = "TIAS2781";
- else
- return -ENODEV;
tas_hda = devm_kzalloc(&clt->dev, sizeof(*tas_hda), GFP_KERNEL);
if (!tas_hda)
@@ -699,6 +778,19 @@ static int tas2781_hda_i2c_probe(struct i2c_client *clt)
if (!tas_hda->priv)
return -ENOMEM;
+ if (strstr(dev_name(&clt->dev), "TIAS2781")) {
+ device_name = "TIAS2781";
+ tas_hda->priv->save_calibration = tas2781_save_calibration;
+ tas_hda->priv->apply_calibration = tas2781_apply_calib;
+ tas_hda->priv->global_addr = TAS2781_GLOBAL_ADDR;
+ } else if (strstr(dev_name(&clt->dev), "INT8866")) {
+ device_name = "INT8866";
+ tas_hda->priv->save_calibration = tas2563_save_calibration;
+ tas_hda->priv->apply_calibration = tas2563_apply_calib;
+ tas_hda->priv->global_addr = TAS2563_GLOBAL_ADDR;
+ } else
+ return -ENODEV;
+
tas_hda->priv->irq_info.irq = clt->irq;
ret = tas2781_read_acpi(tas_hda->priv, device_name);
if (ret)
@@ -765,8 +857,6 @@ static int tas2781_runtime_suspend(struct device *dev)
static int tas2781_runtime_resume(struct device *dev)
{
struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
- unsigned long calib_data_sz =
- tas_hda->priv->ndev * TASDEVICE_SPEAKER_CALIBRATION_SIZE;
dev_dbg(tas_hda->dev, "Runtime Resume\n");
@@ -777,8 +867,7 @@ static int tas2781_runtime_resume(struct device *dev)
/* If calibrated data occurs error, dsp will still works with default
* calibrated data inside algo.
*/
- if (tas_hda->priv->cali_data.total_sz > calib_data_sz)
- tas2781_apply_calib(tas_hda->priv);
+ tasdevice_apply_calibration(tas_hda->priv);
mutex_unlock(&tas_hda->priv->codec_lock);
@@ -809,8 +898,6 @@ static int tas2781_system_suspend(struct device *dev)
static int tas2781_system_resume(struct device *dev)
{
struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
- unsigned long calib_data_sz =
- tas_hda->priv->ndev * TASDEVICE_SPEAKER_CALIBRATION_SIZE;
int i, ret;
dev_info(tas_hda->priv->dev, "System Resume\n");
@@ -832,8 +919,7 @@ static int tas2781_system_resume(struct device *dev)
/* If calibrated data occurs error, dsp will still work with default
* calibrated data inside algo.
*/
- if (tas_hda->priv->cali_data.total_sz > calib_data_sz)
- tas2781_apply_calib(tas_hda->priv);
+ tasdevice_apply_calibration(tas_hda->priv);
mutex_unlock(&tas_hda->priv->codec_lock);
return 0;
@@ -851,6 +937,7 @@ static const struct i2c_device_id tas2781_hda_i2c_id[] = {
static const struct acpi_device_id tas2781_acpi_hda_match[] = {
{"TIAS2781", 0 },
+ {"INT8866", 0 },
{}
};
MODULE_DEVICE_TABLE(acpi, tas2781_acpi_hda_match);
diff --git a/sound/pci/oxygen/oxygen_mixer.c b/sound/pci/oxygen/oxygen_mixer.c
index 46705ec77b48..eb3aca16359c 100644
--- a/sound/pci/oxygen/oxygen_mixer.c
+++ b/sound/pci/oxygen/oxygen_mixer.c
@@ -718,7 +718,7 @@ static int ac97_fp_rec_volume_put(struct snd_kcontrol *ctl,
oldreg = oxygen_read_ac97(chip, 1, AC97_REC_GAIN);
newreg = oldreg & ~0x0707;
newreg = newreg | (value->value.integer.value[0] & 7);
- newreg = newreg | ((value->value.integer.value[0] & 7) << 8);
+ newreg = newreg | ((value->value.integer.value[1] & 7) << 8);
change = newreg != oldreg;
if (change)
oxygen_write_ac97(chip, 1, AC97_REC_GAIN, newreg);
diff --git a/sound/soc/amd/acp-config.c b/sound/soc/amd/acp-config.c
index 3bc4b2e41650..42c2322cd11b 100644
--- a/sound/soc/amd/acp-config.c
+++ b/sound/soc/amd/acp-config.c
@@ -3,7 +3,7 @@
// 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) 2021 Advanced Micro Devices, Inc.
+// Copyright(c) 2021, 2023 Advanced Micro Devices, Inc.
//
// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
//
@@ -19,6 +19,8 @@
#include "../sof/amd/acp.h"
#include "mach-config.h"
+#define ACP_7_0_REV 0x70
+
static int acp_quirk_data;
static const struct config_entry config_table[] = {
@@ -48,6 +50,19 @@ static const struct config_entry config_table[] = {
},
},
{
+ .flags = FLAG_AMD_LEGACY,
+ .device = ACP_PCI_DEV_ID,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Valve"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Jupiter"),
+ },
+ },
+ {}
+ },
+ },
+ {
.flags = FLAG_AMD_SOF,
.device = ACP_PCI_DEV_ID,
.dmi_table = (const struct dmi_system_id []) {
@@ -55,7 +70,6 @@ static const struct config_entry config_table[] = {
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Valve"),
DMI_MATCH(DMI_PRODUCT_NAME, "Galileo"),
- DMI_MATCH(DMI_PRODUCT_FAMILY, "Sephiroth"),
},
},
{}
@@ -147,15 +161,33 @@ static const struct config_entry config_table[] = {
},
};
+static int snd_amd_acp_acpi_find_config(struct pci_dev *pci)
+{
+ const union acpi_object *obj;
+ int acp_flag = FLAG_AMD_LEGACY_ONLY_DMIC;
+
+ if (!acpi_dev_get_property(ACPI_COMPANION(&pci->dev), "acp-audio-config-flag",
+ ACPI_TYPE_INTEGER, &obj))
+ acp_flag = obj->integer.value;
+
+ return acp_flag;
+}
+
int snd_amd_acp_find_config(struct pci_dev *pci)
{
const struct config_entry *table = config_table;
u16 device = pci->device;
int i;
- /* Do not enable FLAGS on older platforms with Rev id zero */
+ /* Do not enable FLAGS on older platforms with Rev Id zero
+ * For platforms which has ACP 7.0 or higher, read the acp
+ * config flag from BIOS ACPI table and for older platforms
+ * read it from DMI tables.
+ */
if (!pci->revision)
return 0;
+ else if (pci->revision >= ACP_7_0_REV)
+ return snd_amd_acp_acpi_find_config(pci);
for (i = 0; i < ARRAY_SIZE(config_table); i++, table++) {
if (table->device != device)
@@ -289,4 +321,5 @@ struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_sof_machines[] = {
};
EXPORT_SYMBOL(snd_soc_acpi_amd_acp63_sof_machines);
+MODULE_DESCRIPTION("AMD ACP Machine Configuration Module");
MODULE_LICENSE("Dual BSD/GPL");
diff --git a/sound/soc/amd/acp/Kconfig b/sound/soc/amd/acp/Kconfig
index 5fb322212938..84c963241dc5 100644
--- a/sound/soc/amd/acp/Kconfig
+++ b/sound/soc/amd/acp/Kconfig
@@ -73,6 +73,19 @@ config SND_AMD_ASOC_ACP63
Say Y if you want to enable AUDIO on ACP6.3
If unsure select "N".
+config SND_AMD_ASOC_ACP70
+ tristate "AMD ACP ASOC Acp7.0 Support"
+ depends on X86 && PCI
+ depends on ACPI
+ 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
+ help
+ This option enables Acp7.0 PDM support on AMD platform.
+ Say Y if you want to enable AUDIO on ACP7.0
+ If unsure select "N".
+
config SND_SOC_AMD_MACH_COMMON
tristate
depends on X86 && PCI && I2C
diff --git a/sound/soc/amd/acp/Makefile b/sound/soc/amd/acp/Makefile
index dd85700f1c5f..ff5f7893b81e 100644
--- a/sound/soc/amd/acp/Makefile
+++ b/sound/soc/amd/acp/Makefile
@@ -15,6 +15,7 @@ snd-acp-pci-objs := acp-pci.o
snd-acp-renoir-objs := acp-renoir.o
snd-acp-rembrandt-objs := acp-rembrandt.o
snd-acp63-objs := acp63.o
+snd-acp70-objs := acp70.o
#machine specific driver
snd-acp-mach-objs := acp-mach-common.o
@@ -30,6 +31,7 @@ obj-$(CONFIG_SND_SOC_AMD_ACP_PCI) += snd-acp-pci.o
obj-$(CONFIG_SND_AMD_ASOC_RENOIR) += snd-acp-renoir.o
obj-$(CONFIG_SND_AMD_ASOC_REMBRANDT) += snd-acp-rembrandt.o
obj-$(CONFIG_SND_AMD_ASOC_ACP63) += snd-acp63.o
+obj-$(CONFIG_SND_AMD_ASOC_ACP70) += snd-acp70.o
obj-$(CONFIG_SND_SOC_AMD_MACH_COMMON) += snd-acp-mach.o
obj-$(CONFIG_SND_SOC_AMD_LEGACY_MACH) += snd-acp-legacy-mach.o
diff --git a/sound/soc/amd/acp/acp-mach-common.c b/sound/soc/amd/acp/acp-mach-common.c
index 34b14f2611ba..504d1b8c4cbb 100644
--- a/sound/soc/amd/acp/acp-mach-common.c
+++ b/sound/soc/amd/acp/acp-mach-common.c
@@ -3,7 +3,7 @@
// 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) 2021 Advanced Micro Devices, Inc.
+// Copyright(c) 2021, 2023 Advanced Micro Devices, Inc.
//
// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
// Vijendar Mukunda <Vijendar.Mukunda@amd.com>
@@ -282,6 +282,22 @@ static int acp_card_rt5682_hw_params(struct snd_pcm_substream *substream,
return ret;
}
+ if (drvdata->tdm_mode) {
+ ret = snd_soc_dai_set_pll(codec_dai, RT5682S_PLL1, RT5682S_PLL_S_BCLK1,
+ 6144000, 49152000);
+ if (ret < 0) {
+ dev_err(rtd->dev, "Failed to set codec PLL: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5682S_SCLK_S_PLL1,
+ 49152000, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(rtd->dev, "Failed to set codec SYSCLK: %d\n", ret);
+ return ret;
+ }
+ }
+
/* Set tdm/i2s1 master bclk ratio */
ret = snd_soc_dai_set_bclk_ratio(codec_dai, ch * format);
if (ret < 0) {
@@ -464,6 +480,22 @@ static int acp_card_rt5682s_hw_params(struct snd_pcm_substream *substream,
return ret;
}
+ if (drvdata->tdm_mode) {
+ ret = snd_soc_dai_set_pll(codec_dai, RT5682S_PLL1, RT5682S_PLL_S_BCLK1,
+ 6144000, 49152000);
+ if (ret < 0) {
+ dev_err(rtd->dev, "Failed to set codec PLL: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5682S_SCLK_S_PLL1,
+ 49152000, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(rtd->dev, "Failed to set codec SYSCLK: %d\n", ret);
+ return ret;
+ }
+ }
+
/* Set tdm/i2s1 master bclk ratio */
ret = snd_soc_dai_set_bclk_ratio(codec_dai, ch * format);
if (ret < 0) {
@@ -473,6 +505,13 @@ static int acp_card_rt5682s_hw_params(struct snd_pcm_substream *substream,
clk_set_rate(drvdata->wclk, srate);
clk_set_rate(drvdata->bclk, srate * ch * format);
+ if (!drvdata->soc_mclk) {
+ ret = acp_clk_enable(drvdata, srate, ch * format);
+ if (ret < 0) {
+ dev_err(rtd->card->dev, "Failed to enable HS clk: %d\n", ret);
+ return ret;
+ }
+ }
return 0;
}
@@ -1290,6 +1329,8 @@ SND_SOC_DAILINK_DEF(sof_hs,
DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-hs")));
SND_SOC_DAILINK_DEF(sof_hs_virtual,
DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-hs-virtual")));
+SND_SOC_DAILINK_DEF(sof_bt,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-bt")));
SND_SOC_DAILINK_DEF(sof_dmic,
DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-dmic")));
SND_SOC_DAILINK_DEF(pdm_dmic,
@@ -1348,6 +1389,8 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card)
if (drv_data->hs_cpu_id)
num_links++;
+ if (drv_data->bt_cpu_id)
+ num_links++;
if (drv_data->amp_cpu_id)
num_links++;
if (drv_data->dmic_cpu_id)
@@ -1428,8 +1471,13 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card)
if (drv_data->amp_cpu_id == I2S_SP) {
links[i].name = "acp-amp-codec";
links[i].id = AMP_BE_ID;
- links[i].cpus = sof_sp_virtual;
- links[i].num_cpus = ARRAY_SIZE(sof_sp_virtual);
+ if (drv_data->platform == RENOIR) {
+ links[i].cpus = sof_sp;
+ links[i].num_cpus = ARRAY_SIZE(sof_sp);
+ } else {
+ links[i].cpus = sof_sp_virtual;
+ links[i].num_cpus = ARRAY_SIZE(sof_sp_virtual);
+ }
links[i].platforms = sof_component;
links[i].num_platforms = ARRAY_SIZE(sof_component);
links[i].dpcm_playback = 1;
@@ -1479,6 +1527,7 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card)
links[i].init = acp_card_maxim_init;
}
if (drv_data->amp_codec_id == MAX98388) {
+ links[i].dpcm_capture = 1;
links[i].codecs = max98388;
links[i].num_codecs = ARRAY_SIZE(max98388);
links[i].ops = &acp_max98388_ops;
@@ -1497,6 +1546,25 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card)
i++;
}
+ if (drv_data->bt_cpu_id == I2S_BT) {
+ links[i].name = "acp-bt-codec";
+ links[i].id = BT_BE_ID;
+ links[i].cpus = sof_bt;
+ links[i].num_cpus = ARRAY_SIZE(sof_bt);
+ links[i].platforms = sof_component;
+ links[i].num_platforms = ARRAY_SIZE(sof_component);
+ links[i].dpcm_playback = 1;
+ links[i].dpcm_capture = 1;
+ links[i].nonatomic = true;
+ links[i].no_pcm = 1;
+ if (!drv_data->bt_codec_id) {
+ /* Use dummy codec if codec id not specified */
+ links[i].codecs = &snd_soc_dummy_dlc;
+ links[i].num_codecs = 1;
+ }
+ i++;
+ }
+
if (drv_data->dmic_cpu_id == DMIC) {
links[i].name = "acp-dmic-codec";
links[i].id = DMIC_BE_ID;
@@ -1717,4 +1785,5 @@ int acp_legacy_dai_links_create(struct snd_soc_card *card)
}
EXPORT_SYMBOL_NS_GPL(acp_legacy_dai_links_create, SND_SOC_AMD_MACH);
+MODULE_DESCRIPTION("AMD ACP Common Machine driver");
MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/acp/acp-mach.h b/sound/soc/amd/acp/acp-mach.h
index cd681101bea7..a48546d8d407 100644
--- a/sound/soc/amd/acp/acp-mach.h
+++ b/sound/soc/amd/acp/acp-mach.h
@@ -28,6 +28,7 @@ enum be_id {
HEADSET_BE_ID = 0,
AMP_BE_ID,
DMIC_BE_ID,
+ BT_BE_ID,
};
enum cpu_endpoints {
@@ -68,9 +69,11 @@ struct acp_mach_ops {
struct acp_card_drvdata {
unsigned int hs_cpu_id;
unsigned int amp_cpu_id;
+ unsigned int bt_cpu_id;
unsigned int dmic_cpu_id;
unsigned int hs_codec_id;
unsigned int amp_codec_id;
+ unsigned int bt_codec_id;
unsigned int dmic_codec_id;
unsigned int dai_fmt;
unsigned int platform;
diff --git a/sound/soc/amd/acp/acp-renoir.c b/sound/soc/amd/acp/acp-renoir.c
index a591482a0726..b0e181c9a733 100644
--- a/sound/soc/amd/acp/acp-renoir.c
+++ b/sound/soc/amd/acp/acp-renoir.c
@@ -20,6 +20,7 @@
#include <sound/soc.h>
#include <sound/soc-dai.h>
#include <linux/dma-mapping.h>
+#include <linux/pm_runtime.h>
#include "amd.h"
#include "acp-mach.h"
@@ -196,6 +197,11 @@ static int renoir_audio_probe(struct platform_device *pdev)
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;
}
@@ -208,11 +214,42 @@ static void renoir_audio_remove(struct platform_device *pdev)
acp_platform_unregister(dev);
}
+static int __maybe_unused rn_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;
+
+ 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 rn_dma_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(NULL, rn_pcm_resume)
+};
+
static struct platform_driver renoir_driver = {
.probe = renoir_audio_probe,
.remove_new = renoir_audio_remove,
.driver = {
.name = "acp_asoc_renoir",
+ .pm = &rn_dma_pm_ops,
},
};
diff --git a/sound/soc/amd/acp/acp-sof-mach.c b/sound/soc/amd/acp/acp-sof-mach.c
index 5223033a122f..20b94814a046 100644
--- a/sound/soc/amd/acp/acp-sof-mach.c
+++ b/sound/soc/amd/acp/acp-sof-mach.c
@@ -3,7 +3,7 @@
// 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) 2021 Advanced Micro Devices, Inc.
+// Copyright(c) 2021, 2023 Advanced Micro Devices, Inc.
//
// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
//
@@ -48,6 +48,7 @@ static struct acp_card_drvdata sof_rt5682s_rt1019_data = {
.hs_codec_id = RT5682S,
.amp_codec_id = RT1019,
.dmic_codec_id = DMIC,
+ .platform = RENOIR,
.tdm_mode = false,
};
@@ -58,6 +59,7 @@ static struct acp_card_drvdata sof_rt5682s_max_data = {
.hs_codec_id = RT5682S,
.amp_codec_id = MAX98360A,
.dmic_codec_id = DMIC,
+ .platform = RENOIR,
.tdm_mode = false,
};
@@ -68,6 +70,7 @@ static struct acp_card_drvdata sof_nau8825_data = {
.hs_codec_id = NAU8825,
.amp_codec_id = MAX98360A,
.dmic_codec_id = DMIC,
+ .platform = REMBRANDT,
.soc_mclk = true,
.tdm_mode = false,
};
@@ -79,6 +82,7 @@ static struct acp_card_drvdata sof_rt5682s_hs_rt1019_data = {
.hs_codec_id = RT5682S,
.amp_codec_id = RT1019,
.dmic_codec_id = DMIC,
+ .platform = REMBRANDT,
.soc_mclk = true,
.tdm_mode = false,
};
@@ -86,9 +90,11 @@ static struct acp_card_drvdata sof_rt5682s_hs_rt1019_data = {
static struct acp_card_drvdata sof_nau8821_max98388_data = {
.hs_cpu_id = I2S_SP,
.amp_cpu_id = I2S_HS,
+ .bt_cpu_id = I2S_BT,
.dmic_cpu_id = NONE,
.hs_codec_id = NAU8821,
.amp_codec_id = MAX98388,
+ .bt_codec_id = NONE,
.dmic_codec_id = NONE,
.soc_mclk = true,
.tdm_mode = false,
diff --git a/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c b/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c
index 6cd3352dc38d..2b0aa270a3e9 100644
--- a/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c
+++ b/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c
@@ -222,7 +222,6 @@ static int acp3x_es83xx_resume_post(struct snd_soc_card *card)
static int acp3x_es83xx_configure_gpios(struct acp3x_es83xx_private *priv)
{
- int ret = 0;
priv->enable_spk_gpio.crs_entry_index = 0;
priv->enable_hp_gpio.crs_entry_index = 1;
@@ -245,7 +244,7 @@ static int acp3x_es83xx_configure_gpios(struct acp3x_es83xx_private *priv)
priv->enable_spk_gpio.active_low ? "low" : "high",
priv->enable_hp_gpio.crs_entry_index,
priv->enable_hp_gpio.active_low ? "low" : "high");
- return ret;
+ return 0;
}
static int acp3x_es83xx_configure_mics(struct acp3x_es83xx_private *priv)
@@ -359,6 +358,14 @@ static const struct dmi_system_id acp3x_es83xx_dmi_table[] = {
.matches = {
DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HVY-WXX9"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"),
+ },
+ .driver_data = (void *)(ES83XX_ENABLE_DMIC),
+ },
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HVY-WXX9"),
DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1020"),
},
.driver_data = (void *)(ES83XX_ENABLE_DMIC),
diff --git a/sound/soc/amd/acp/acp63.c b/sound/soc/amd/acp/acp63.c
index b871a216a6af..4d342441a650 100644
--- a/sound/soc/amd/acp/acp63.c
+++ b/sound/soc/amd/acp/acp63.c
@@ -283,18 +283,16 @@ static int __maybe_unused acp63_pcm_resume(struct device *dev)
spin_lock(&adata->acp_lock);
list_for_each_entry(stream, &adata->stream_list, list) {
- if (stream) {
- 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);
- }
+ 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);
diff --git a/sound/soc/amd/acp/acp70.c b/sound/soc/amd/acp/acp70.c
index dd384c966ae9..0d7cdd4017e5 100644
--- a/sound/soc/amd/acp/acp70.c
+++ b/sound/soc/amd/acp/acp70.c
@@ -52,8 +52,8 @@ static struct snd_soc_dai_driver acp70_dai[] = {
.playback = {
.stream_name = "I2S SP Playback",
.rates = SNDRV_PCM_RATE_8000_96000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
- SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
.channels_min = 2,
.channels_max = 8,
.rate_min = 8000,
@@ -62,8 +62,8 @@ static struct snd_soc_dai_driver acp70_dai[] = {
.capture = {
.stream_name = "I2S SP Capture",
.rates = SNDRV_PCM_RATE_8000_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
- SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
.channels_min = 2,
.channels_max = 2,
.rate_min = 8000,
@@ -77,8 +77,8 @@ static struct snd_soc_dai_driver acp70_dai[] = {
.playback = {
.stream_name = "I2S BT Playback",
.rates = SNDRV_PCM_RATE_8000_96000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
- SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
.channels_min = 2,
.channels_max = 8,
.rate_min = 8000,
@@ -87,8 +87,8 @@ static struct snd_soc_dai_driver acp70_dai[] = {
.capture = {
.stream_name = "I2S BT Capture",
.rates = SNDRV_PCM_RATE_8000_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
- SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
.channels_min = 2,
.channels_max = 2,
.rate_min = 8000,
@@ -102,8 +102,8 @@ static struct snd_soc_dai_driver acp70_dai[] = {
.playback = {
.stream_name = "I2S HS Playback",
.rates = SNDRV_PCM_RATE_8000_96000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
- SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
.channels_min = 2,
.channels_max = 8,
.rate_min = 8000,
@@ -112,8 +112,8 @@ static struct snd_soc_dai_driver acp70_dai[] = {
.capture = {
.stream_name = "I2S HS Capture",
.rates = SNDRV_PCM_RATE_8000_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
- SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
.channels_min = 2,
.channels_max = 8,
.rate_min = 8000,
@@ -229,8 +229,8 @@ static int __maybe_unused acp70_pcm_resume(struct device *dev)
}
}
}
- spin_unlock(&adata->acp_lock);
- return 0;
+ spin_unlock(&adata->acp_lock);
+ return 0;
}
static const struct dev_pm_ops acp70_dma_pm_ops = {
diff --git a/sound/soc/amd/vangogh/acp5x-mach.c b/sound/soc/amd/vangogh/acp5x-mach.c
index de4b478a983d..7878e061ecb9 100644
--- a/sound/soc/amd/vangogh/acp5x-mach.c
+++ b/sound/soc/amd/vangogh/acp5x-mach.c
@@ -439,7 +439,15 @@ static const struct dmi_system_id acp5x_vg_quirk_table[] = {
.matches = {
DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Valve"),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Jupiter"),
- }
+ },
+ .driver_data = (void *)&acp5x_8821_35l41_card,
+ },
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Valve"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Galileo"),
+ },
+ .driver_data = (void *)&acp5x_8821_98388_card,
},
{}
};
@@ -452,25 +460,15 @@ static int acp5x_probe(struct platform_device *pdev)
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;
- }
+ dmi_id = dmi_first_match(acp5x_vg_quirk_table);
+ if (!dmi_id || !dmi_id->driver_data)
+ return -ENODEV;
machine = devm_kzalloc(dev, sizeof(*machine), GFP_KERNEL);
if (!machine)
return -ENOMEM;
+ card = dmi_id->driver_data;
card->dev = dev;
platform_set_drvdata(pdev, card);
snd_soc_card_set_drvdata(card, machine);
@@ -482,17 +480,10 @@ 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 = DRV_NAME,
.pm = &snd_soc_pm_ops,
- .acpi_match_table = acp5x_acpi_match,
},
.probe = acp5x_probe,
};
diff --git a/sound/soc/amd/vangogh/pci-acp5x.c b/sound/soc/amd/vangogh/pci-acp5x.c
index c4634a8a17cd..af56ff09f02a 100644
--- a/sound/soc/amd/vangogh/pci-acp5x.c
+++ b/sound/soc/amd/vangogh/pci-acp5x.c
@@ -2,7 +2,7 @@
//
// AMD Vangogh ACP PCI Driver
//
-// Copyright (C) 2021 Advanced Micro Devices, Inc. All rights reserved.
+// Copyright (C) 2021, 2023 Advanced Micro Devices, Inc. All rights reserved.
#include <linux/pci.h>
#include <linux/module.h>
@@ -13,6 +13,7 @@
#include <linux/pm_runtime.h>
#include "acp5x.h"
+#include "../mach-config.h"
struct acp5x_dev_data {
void __iomem *acp5x_base;
@@ -129,9 +130,13 @@ static int snd_acp5x_probe(struct pci_dev *pci,
int ret, i;
u32 addr, val;
- /* Return if acp config flag is defined */
+ /*
+ * Return if ACP config flag is defined, except when board
+ * supports SOF while it is not being enabled in kernel config.
+ */
flag = snd_amd_acp_find_config(pci);
- if (flag)
+ if (flag != FLAG_AMD_LEGACY &&
+ (flag != FLAG_AMD_SOF || IS_ENABLED(CONFIG_SND_SOC_SOF_AMD_VANGOGH)))
return -ENODEV;
irqflags = IRQF_SHARED;
@@ -259,7 +264,7 @@ disable_pci:
return ret;
}
-static int __maybe_unused snd_acp5x_suspend(struct device *dev)
+static int snd_acp5x_suspend(struct device *dev)
{
int ret;
struct acp5x_dev_data *adata;
@@ -274,7 +279,7 @@ static int __maybe_unused snd_acp5x_suspend(struct device *dev)
return ret;
}
-static int __maybe_unused snd_acp5x_resume(struct device *dev)
+static int snd_acp5x_resume(struct device *dev)
{
int ret;
struct acp5x_dev_data *adata;
@@ -289,9 +294,8 @@ static int __maybe_unused snd_acp5x_resume(struct device *dev)
}
static const struct dev_pm_ops acp5x_pm = {
- SET_RUNTIME_PM_OPS(snd_acp5x_suspend,
- snd_acp5x_resume, NULL)
- SET_SYSTEM_SLEEP_PM_OPS(snd_acp5x_suspend, snd_acp5x_resume)
+ RUNTIME_PM_OPS(snd_acp5x_suspend, snd_acp5x_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(snd_acp5x_suspend, snd_acp5x_resume)
};
static void snd_acp5x_remove(struct pci_dev *pci)
@@ -327,7 +331,7 @@ static struct pci_driver acp5x_driver = {
.probe = snd_acp5x_probe,
.remove = snd_acp5x_remove,
.driver = {
- .pm = &acp5x_pm,
+ .pm = pm_ptr(&acp5x_pm),
}
};
diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c
index d83cb6e4c62a..90360f8b3e81 100644
--- a/sound/soc/amd/yc/acp6x-mach.c
+++ b/sound/soc/amd/yc/acp6x-mach.c
@@ -203,6 +203,20 @@ static const struct dmi_system_id yc_acp_quirk_table[] = {
.driver_data = &acp6x_card,
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21J2"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21J0"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "21J5"),
}
},
@@ -238,6 +252,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = {
.driver_data = &acp6x_card,
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "82UU"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "82V2"),
}
},
@@ -251,6 +272,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = {
{
.driver_data = &acp6x_card,
.matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83AS"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "UM5302TA"),
}
@@ -300,6 +328,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = {
{
.driver_data = &acp6x_card,
.matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Micro-Star International Co., Ltd."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Bravo 15 C7VF"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Alienware"),
DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m17 R5 AMD"),
}
@@ -384,6 +419,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = {
{
.driver_data = &acp6x_card,
.matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
+ DMI_MATCH(DMI_BOARD_NAME, "8BD6"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "MECHREVO"),
DMI_MATCH(DMI_BOARD_NAME, "MRID6"),
}
diff --git a/sound/soc/amd/yc/pci-acp6x.c b/sound/soc/amd/yc/pci-acp6x.c
index 7af6a349b1d4..694b8e313902 100644
--- a/sound/soc/amd/yc/pci-acp6x.c
+++ b/sound/soc/amd/yc/pci-acp6x.c
@@ -162,6 +162,7 @@ static int snd_acp6x_probe(struct pci_dev *pci,
/* Yellow Carp device check */
switch (pci->revision) {
case 0x60:
+ case 0x63:
case 0x6f:
break;
default:
diff --git a/sound/soc/cirrus/edb93xx.c b/sound/soc/cirrus/edb93xx.c
index 6b6817256331..8bb67d7d2b4b 100644
--- a/sound/soc/cirrus/edb93xx.c
+++ b/sound/soc/cirrus/edb93xx.c
@@ -11,7 +11,6 @@
*/
#include <linux/platform_device.h>
-#include <linux/gpio.h>
#include <linux/module.h>
#include <linux/soc/cirrus/ep93xx.h>
#include <sound/core.h>
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 3429419ca694..59f9742e9ff4 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -1076,6 +1076,10 @@ config SND_SOC_ES7134
config SND_SOC_ES7241
tristate "Everest Semi ES7241 CODEC"
+config SND_SOC_ES83XX_DSM_COMMON
+ depends on ACPI
+ tristate
+
config SND_SOC_ES8316
tristate "Everest Semi ES8316 CODEC"
depends on I2C
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 2078bb0d981e..f53baa2b9565 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -116,6 +116,7 @@ snd-soc-da9055-objs := da9055.o
snd-soc-dmic-objs := dmic.o
snd-soc-es7134-objs := es7134.o
snd-soc-es7241-objs := es7241.o
+snd-soc-es83xx-dsm-common-objs := es83xx-dsm-common.o
snd-soc-es8316-objs := es8316.o
snd-soc-es8326-objs := es8326.o
snd-soc-es8328-objs := es8328.o
@@ -505,6 +506,7 @@ obj-$(CONFIG_SND_SOC_DA9055) += snd-soc-da9055.o
obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o
obj-$(CONFIG_SND_SOC_ES7134) += snd-soc-es7134.o
obj-$(CONFIG_SND_SOC_ES7241) += snd-soc-es7241.o
+obj-$(CONFIG_SND_SOC_ES83XX_DSM_COMMON) += snd-soc-es83xx-dsm-common.o
obj-$(CONFIG_SND_SOC_ES8316) += snd-soc-es8316.o
obj-$(CONFIG_SND_SOC_ES8326) += snd-soc-es8326.o
obj-$(CONFIG_SND_SOC_ES8328) += snd-soc-es8328.o
diff --git a/sound/soc/codecs/aw88395/aw88395_device.h b/sound/soc/codecs/aw88395/aw88395_device.h
index 791c8c106557..0f750f654f3e 100644
--- a/sound/soc/codecs/aw88395/aw88395_device.h
+++ b/sound/soc/codecs/aw88395/aw88395_device.h
@@ -146,6 +146,7 @@ struct aw_device {
unsigned int channel;
unsigned int fade_step;
+ unsigned int prof_data_type;
struct i2c_client *i2c;
struct device *dev;
diff --git a/sound/soc/codecs/aw88395/aw88395_lib.c b/sound/soc/codecs/aw88395/aw88395_lib.c
index 9ebe7c510109..f25f6e0d4428 100644
--- a/sound/soc/codecs/aw88395/aw88395_lib.c
+++ b/sound/soc/codecs/aw88395/aw88395_lib.c
@@ -11,7 +11,6 @@
#include <linux/i2c.h>
#include "aw88395_lib.h"
#include "aw88395_device.h"
-#include "aw88395_reg.h"
#define AW88395_CRC8_POLYNOMIAL 0x8C
DECLARE_CRC8_TABLE(aw_crc8_table);
@@ -456,14 +455,6 @@ static int aw_dev_parse_reg_bin_with_hdr(struct aw_device *aw_dev,
goto parse_bin_failed;
}
- if (aw_dev->chip_id == AW88261_CHIP_ID) {
- if (aw_bin->header_info[0].valid_data_len % 4) {
- dev_err(aw_dev->dev, "bin data len get error!");
- ret = -EINVAL;
- goto parse_bin_failed;
- }
- }
-
prof_desc->sec_desc[AW88395_DATA_TYPE_REG].data =
data + aw_bin->header_info[0].valid_data_addr;
prof_desc->sec_desc[AW88395_DATA_TYPE_REG].len =
@@ -528,7 +519,7 @@ static int aw_dev_parse_dev_type(struct aw_device *aw_dev,
cfg_dde[i].dev_profile);
return -EINVAL;
}
-
+ aw_dev->prof_data_type = cfg_dde[i].data_type;
ret = aw_dev_parse_data_by_sec_type(aw_dev, prof_hdr, &cfg_dde[i],
&all_prof_info->prof_desc[cfg_dde[i].dev_profile]);
if (ret < 0) {
@@ -564,6 +555,7 @@ static int aw_dev_parse_dev_default_type(struct aw_device *aw_dev,
cfg_dde[i].dev_profile);
return -EINVAL;
}
+ aw_dev->prof_data_type = cfg_dde[i].data_type;
ret = aw_dev_parse_data_by_sec_type(aw_dev, prof_hdr, &cfg_dde[i],
&all_prof_info->prof_desc[cfg_dde[i].dev_profile]);
if (ret < 0) {
@@ -582,7 +574,7 @@ static int aw_dev_parse_dev_default_type(struct aw_device *aw_dev,
return 0;
}
-static int aw88261_dev_cfg_get_valid_prof(struct aw_device *aw_dev,
+static int aw_dev_cfg_get_reg_valid_prof(struct aw_device *aw_dev,
struct aw_all_prof_info *all_prof_info)
{
struct aw_prof_desc *prof_desc = all_prof_info->prof_desc;
@@ -624,7 +616,7 @@ static int aw88261_dev_cfg_get_valid_prof(struct aw_device *aw_dev,
return 0;
}
-static int aw88395_dev_cfg_get_valid_prof(struct aw_device *aw_dev,
+static int aw_dev_cfg_get_multiple_valid_prof(struct aw_device *aw_dev,
struct aw_all_prof_info *all_prof_info)
{
struct aw_prof_desc *prof_desc = all_prof_info->prof_desc;
@@ -703,26 +695,20 @@ static int aw_dev_load_cfg_by_hdr(struct aw_device *aw_dev,
goto exit;
}
- switch (aw_dev->chip_id) {
- case AW88395_CHIP_ID:
- case AW88399_CHIP_ID:
- ret = aw88395_dev_cfg_get_valid_prof(aw_dev, all_prof_info);
- if (ret < 0)
- goto exit;
+ switch (aw_dev->prof_data_type) {
+ case ACF_SEC_TYPE_MULTIPLE_BIN:
+ ret = aw_dev_cfg_get_multiple_valid_prof(aw_dev, all_prof_info);
break;
- case AW88261_CHIP_ID:
- case AW87390_CHIP_ID:
- ret = aw88261_dev_cfg_get_valid_prof(aw_dev, all_prof_info);
- if (ret < 0)
- goto exit;
+ case ACF_SEC_TYPE_HDR_REG:
+ ret = aw_dev_cfg_get_reg_valid_prof(aw_dev, all_prof_info);
break;
default:
- dev_err(aw_dev->dev, "valid prof unsupported");
+ dev_err(aw_dev->dev, "unsupport data type\n");
ret = -EINVAL;
break;
}
-
- aw_dev->prof_info.prof_name_list = profile_name;
+ if (!ret)
+ aw_dev->prof_info.prof_name_list = profile_name;
exit:
devm_kfree(aw_dev->dev, all_prof_info);
@@ -791,39 +777,23 @@ static int aw_get_dev_scene_count_v1(struct aw_device *aw_dev, struct aw_contain
struct aw_cfg_dde_v1 *cfg_dde =
(struct aw_cfg_dde_v1 *)(aw_cfg->data + cfg_hdr->hdr_offset);
unsigned int i;
- int ret;
- switch (aw_dev->chip_id) {
- case AW88395_CHIP_ID:
- case AW88399_CHIP_ID:
- for (i = 0; i < cfg_hdr->ddt_num; ++i) {
- if ((cfg_dde[i].data_type == ACF_SEC_TYPE_MULTIPLE_BIN) &&
- (aw_dev->chip_id == cfg_dde[i].chip_id) &&
- (aw_dev->i2c->adapter->nr == cfg_dde[i].dev_bus) &&
- (aw_dev->i2c->addr == cfg_dde[i].dev_addr))
- (*scene_num)++;
- }
- ret = 0;
- break;
- case AW88261_CHIP_ID:
- case AW87390_CHIP_ID:
- for (i = 0; i < cfg_hdr->ddt_num; ++i) {
- if (((cfg_dde[i].data_type == ACF_SEC_TYPE_REG) ||
- (cfg_dde[i].data_type == ACF_SEC_TYPE_HDR_REG)) &&
- (aw_dev->chip_id == cfg_dde[i].chip_id) &&
- (aw_dev->i2c->adapter->nr == cfg_dde[i].dev_bus) &&
- (aw_dev->i2c->addr == cfg_dde[i].dev_addr))
- (*scene_num)++;
- }
- ret = 0;
- break;
- default:
- dev_err(aw_dev->dev, "unsupported device");
- ret = -EINVAL;
- break;
+ for (i = 0; i < cfg_hdr->ddt_num; ++i) {
+ if (((cfg_dde[i].data_type == ACF_SEC_TYPE_REG) ||
+ (cfg_dde[i].data_type == ACF_SEC_TYPE_HDR_REG) ||
+ (cfg_dde[i].data_type == ACF_SEC_TYPE_MULTIPLE_BIN)) &&
+ (aw_dev->chip_id == cfg_dde[i].chip_id) &&
+ (aw_dev->i2c->adapter->nr == cfg_dde[i].dev_bus) &&
+ (aw_dev->i2c->addr == cfg_dde[i].dev_addr))
+ (*scene_num)++;
}
- return ret;
+ if ((*scene_num) == 0) {
+ dev_err(aw_dev->dev, "failed to obtain scene, scenu_num = %d\n", (*scene_num));
+ return -EINVAL;
+ }
+
+ return 0;
}
static int aw_get_default_scene_count_v1(struct aw_device *aw_dev,
@@ -834,37 +804,23 @@ static int aw_get_default_scene_count_v1(struct aw_device *aw_dev,
struct aw_cfg_dde_v1 *cfg_dde =
(struct aw_cfg_dde_v1 *)(aw_cfg->data + cfg_hdr->hdr_offset);
unsigned int i;
- int ret;
- switch (aw_dev->chip_id) {
- case AW88395_CHIP_ID:
- case AW88399_CHIP_ID:
- for (i = 0; i < cfg_hdr->ddt_num; ++i) {
- if ((cfg_dde[i].data_type == ACF_SEC_TYPE_MULTIPLE_BIN) &&
- (aw_dev->chip_id == cfg_dde[i].chip_id) &&
- (aw_dev->channel == cfg_dde[i].dev_index))
- (*scene_num)++;
- }
- ret = 0;
- break;
- case AW88261_CHIP_ID:
- case AW87390_CHIP_ID:
- for (i = 0; i < cfg_hdr->ddt_num; ++i) {
- if (((cfg_dde[i].data_type == ACF_SEC_TYPE_REG) ||
- (cfg_dde[i].data_type == ACF_SEC_TYPE_HDR_REG)) &&
- (aw_dev->chip_id == cfg_dde[i].chip_id) &&
- (aw_dev->channel == cfg_dde[i].dev_index))
- (*scene_num)++;
- }
- ret = 0;
- break;
- default:
- dev_err(aw_dev->dev, "unsupported device");
- ret = -EINVAL;
- break;
+
+ for (i = 0; i < cfg_hdr->ddt_num; ++i) {
+ if (((cfg_dde[i].data_type == ACF_SEC_TYPE_MULTIPLE_BIN) ||
+ (cfg_dde[i].data_type == ACF_SEC_TYPE_REG) ||
+ (cfg_dde[i].data_type == ACF_SEC_TYPE_HDR_REG)) &&
+ (aw_dev->chip_id == cfg_dde[i].chip_id) &&
+ (aw_dev->channel == cfg_dde[i].dev_index))
+ (*scene_num)++;
}
- return ret;
+ if ((*scene_num) == 0) {
+ dev_err(aw_dev->dev, "failed to obtain scene, scenu_num = %d\n", (*scene_num));
+ return -EINVAL;
+ }
+
+ return 0;
}
static int aw_dev_parse_scene_count_v1(struct aw_device *aw_dev,
diff --git a/sound/soc/codecs/aw88395/aw88395_reg.h b/sound/soc/codecs/aw88395/aw88395_reg.h
index ede7deab6a9c..e64f24e97150 100644
--- a/sound/soc/codecs/aw88395/aw88395_reg.h
+++ b/sound/soc/codecs/aw88395/aw88395_reg.h
@@ -95,10 +95,7 @@
#define AW88395_TM_REG (0x7C)
enum aw88395_id {
- AW88399_CHIP_ID = 0x2183,
AW88395_CHIP_ID = 0x2049,
- AW88261_CHIP_ID = 0x2113,
- AW87390_CHIP_ID = 0x76,
};
#define AW88395_REG_MAX (0x7D)
diff --git a/sound/soc/codecs/aw88399.c b/sound/soc/codecs/aw88399.c
index 54f8457e8497..9fcb805bf971 100644
--- a/sound/soc/codecs/aw88399.c
+++ b/sound/soc/codecs/aw88399.c
@@ -15,7 +15,6 @@
#include <sound/soc.h>
#include "aw88399.h"
#include "aw88395/aw88395_device.h"
-#include "aw88395/aw88395_reg.h"
static const struct regmap_config aw88399_remap_config = {
.val_bits = 16,
diff --git a/sound/soc/codecs/aw88399.h b/sound/soc/codecs/aw88399.h
index 4f391099d0f2..5e9cdf725d3d 100644
--- a/sound/soc/codecs/aw88399.h
+++ b/sound/soc/codecs/aw88399.h
@@ -491,6 +491,7 @@
#define AW88399_CRC_FW_BASE_ADDR (0x4C0)
#define AW88399_ACF_FILE "aw88399_acf.bin"
#define AW88399_DEV_SYSST_CHECK_MAX (10)
+#define AW88399_CHIP_ID 0x2183
#define AW88399_I2C_NAME "aw88399"
diff --git a/sound/soc/codecs/cs35l32.c b/sound/soc/codecs/cs35l32.c
index 138040618438..d1350ffbf3bd 100644
--- a/sound/soc/codecs/cs35l32.c
+++ b/sound/soc/codecs/cs35l32.c
@@ -13,7 +13,6 @@
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/i2c.h>
-#include <linux/gpio.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
diff --git a/sound/soc/codecs/cs35l33.c b/sound/soc/codecs/cs35l33.c
index 4010a2d33a33..a19a2bafb37c 100644
--- a/sound/soc/codecs/cs35l33.c
+++ b/sound/soc/codecs/cs35l33.c
@@ -22,13 +22,11 @@
#include <sound/soc-dapm.h>
#include <sound/initval.h>
#include <sound/tlv.h>
-#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <sound/cs35l33.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/regulator/machine.h>
-#include <linux/of_gpio.h>
#include <linux/of.h>
#include "cs35l33.h"
@@ -1165,7 +1163,7 @@ static int cs35l33_i2c_probe(struct i2c_client *i2c_client)
/* We could issue !RST or skip it based on AMP topology */
cs35l33->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev,
- "reset-gpios", GPIOD_OUT_HIGH);
+ "reset", GPIOD_OUT_HIGH);
if (IS_ERR(cs35l33->reset_gpio)) {
dev_err(&i2c_client->dev, "%s ERROR: Can't get reset GPIO\n",
__func__);
diff --git a/sound/soc/codecs/cs35l34.c b/sound/soc/codecs/cs35l34.c
index e5871736fa29..cca59de66b73 100644
--- a/sound/soc/codecs/cs35l34.c
+++ b/sound/soc/codecs/cs35l34.c
@@ -20,14 +20,12 @@
#include <linux/regulator/machine.h>
#include <linux/pm_runtime.h>
#include <linux/of.h>
-#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
-#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <sound/initval.h>
#include <sound/tlv.h>
@@ -1061,7 +1059,7 @@ static int cs35l34_i2c_probe(struct i2c_client *i2c_client)
dev_err(&i2c_client->dev, "Failed to request IRQ: %d\n", ret);
cs35l34->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev,
- "reset-gpios", GPIOD_OUT_LOW);
+ "reset", GPIOD_OUT_LOW);
if (IS_ERR(cs35l34->reset_gpio)) {
ret = PTR_ERR(cs35l34->reset_gpio);
goto err_regulator;
diff --git a/sound/soc/codecs/cs35l35.c b/sound/soc/codecs/cs35l35.c
index 63a538f747d3..ddb7d63213a3 100644
--- a/sound/soc/codecs/cs35l35.c
+++ b/sound/soc/codecs/cs35l35.c
@@ -18,14 +18,12 @@
#include <linux/regulator/consumer.h>
#include <linux/gpio/consumer.h>
#include <linux/of.h>
-#include <linux/of_gpio.h>
#include <linux/regmap.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
-#include <linux/gpio.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#include <sound/cs35l35.h>
diff --git a/sound/soc/codecs/cs35l36.c b/sound/soc/codecs/cs35l36.c
index f2fde6e652b9..f5bd32e434a0 100644
--- a/sound/soc/codecs/cs35l36.c
+++ b/sound/soc/codecs/cs35l36.c
@@ -17,15 +17,14 @@
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/gpio/consumer.h>
+#include <linux/irq.h>
#include <linux/of.h>
-#include <linux/of_gpio.h>
#include <linux/regmap.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
-#include <linux/gpio.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#include <sound/cs35l36.h>
diff --git a/sound/soc/codecs/cs35l45.c b/sound/soc/codecs/cs35l45.c
index 44c221745c3b..2392c6effed8 100644
--- a/sound/soc/codecs/cs35l45.c
+++ b/sound/soc/codecs/cs35l45.c
@@ -184,7 +184,7 @@ static int cs35l45_activate_ctl(struct snd_soc_component *component,
else
snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s", ctl_name);
- kcontrol = snd_soc_card_get_kcontrol(component->card, name);
+ kcontrol = snd_soc_card_get_kcontrol_locked(component->card, name);
if (!kcontrol) {
dev_err(component->dev, "Can't find kcontrol %s\n", name);
return -EINVAL;
diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c
index 953ba066bab1..cb4e83126b08 100644
--- a/sound/soc/codecs/cs35l56-shared.c
+++ b/sound/soc/codecs/cs35l56-shared.c
@@ -5,6 +5,7 @@
// Copyright (C) 2023 Cirrus Logic, Inc. and
// Cirrus Logic International Semiconductor Ltd.
+#include <linux/gpio/consumer.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/types.h>
@@ -12,6 +13,15 @@
#include "cs35l56.h"
static const struct reg_sequence cs35l56_patch[] = {
+ /*
+ * Firmware can change these to non-defaults to satisfy SDCA.
+ * Ensure that they are at known defaults.
+ */
+ { CS35L56_SWIRE_DP3_CH1_INPUT, 0x00000018 },
+ { CS35L56_SWIRE_DP3_CH2_INPUT, 0x00000019 },
+ { CS35L56_SWIRE_DP3_CH3_INPUT, 0x00000029 },
+ { CS35L56_SWIRE_DP3_CH4_INPUT, 0x00000028 },
+
/* These are not reset by a soft-reset, so patch to defaults. */
{ CS35L56_MAIN_RENDER_USER_MUTE, 0x00000000 },
{ CS35L56_MAIN_RENDER_USER_VOLUME, 0x00000000 },
@@ -34,15 +44,13 @@ static const struct reg_default cs35l56_reg_defaults[] = {
{ CS35L56_ASP1_FRAME_CONTROL5, 0x00020100 },
{ CS35L56_ASP1_DATA_CONTROL1, 0x00000018 },
{ CS35L56_ASP1_DATA_CONTROL5, 0x00000018 },
- { CS35L56_ASP1TX1_INPUT, 0x00000018 },
- { CS35L56_ASP1TX2_INPUT, 0x00000019 },
- { CS35L56_ASP1TX3_INPUT, 0x00000020 },
- { CS35L56_ASP1TX4_INPUT, 0x00000028 },
+
+ /* no defaults for ASP1TX mixer */
+
{ CS35L56_SWIRE_DP3_CH1_INPUT, 0x00000018 },
{ CS35L56_SWIRE_DP3_CH2_INPUT, 0x00000019 },
{ CS35L56_SWIRE_DP3_CH3_INPUT, 0x00000029 },
{ CS35L56_SWIRE_DP3_CH4_INPUT, 0x00000028 },
- { CS35L56_IRQ1_CFG, 0x00000000 },
{ CS35L56_IRQ1_MASK_1, 0x83ffffff },
{ CS35L56_IRQ1_MASK_2, 0xffff7fff },
{ CS35L56_IRQ1_MASK_4, 0xe0ffffff },
@@ -195,6 +203,47 @@ static bool cs35l56_volatile_reg(struct device *dev, unsigned int reg)
}
}
+/*
+ * The firmware boot sequence can overwrite the ASP1 config registers so that
+ * they don't match regmap's view of their values. Rewrite the values from the
+ * regmap cache into the hardware registers.
+ */
+int cs35l56_force_sync_asp1_registers_from_cache(struct cs35l56_base *cs35l56_base)
+{
+ struct reg_sequence asp1_regs[] = {
+ { .reg = CS35L56_ASP1_ENABLES1 },
+ { .reg = CS35L56_ASP1_CONTROL1 },
+ { .reg = CS35L56_ASP1_CONTROL2 },
+ { .reg = CS35L56_ASP1_CONTROL3 },
+ { .reg = CS35L56_ASP1_FRAME_CONTROL1 },
+ { .reg = CS35L56_ASP1_FRAME_CONTROL5 },
+ { .reg = CS35L56_ASP1_DATA_CONTROL1 },
+ { .reg = CS35L56_ASP1_DATA_CONTROL5 },
+ };
+ int i, ret;
+
+ /* Read values from regmap cache into a write sequence */
+ for (i = 0; i < ARRAY_SIZE(asp1_regs); ++i) {
+ ret = regmap_read(cs35l56_base->regmap, asp1_regs[i].reg, &asp1_regs[i].def);
+ if (ret)
+ goto err;
+ }
+
+ /* Write the values cache-bypassed so that they will be written to silicon */
+ ret = regmap_multi_reg_write_bypassed(cs35l56_base->regmap, asp1_regs,
+ ARRAY_SIZE(asp1_regs));
+ if (ret)
+ goto err;
+
+ return 0;
+
+err:
+ dev_err(cs35l56_base->dev, "Failed to sync ASP1 registers: %d\n", ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_force_sync_asp1_registers_from_cache, SND_SOC_CS35L56_SHARED);
+
int cs35l56_mbox_send(struct cs35l56_base *cs35l56_base, unsigned int command)
{
unsigned int val;
@@ -286,6 +335,7 @@ void cs35l56_wait_min_reset_pulse(void)
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_DSP1_HALO_STATE, 0),
REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_SYSTEM_RESET),
};
@@ -400,17 +450,6 @@ 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.
@@ -590,10 +629,35 @@ void cs35l56_init_cs_dsp(struct cs35l56_base *cs35l56_base, struct cs_dsp *cs_ds
}
EXPORT_SYMBOL_NS_GPL(cs35l56_init_cs_dsp, SND_SOC_CS35L56_SHARED);
+int cs35l56_read_prot_status(struct cs35l56_base *cs35l56_base,
+ bool *fw_missing, unsigned int *fw_version)
+{
+ unsigned int prot_status;
+ int ret;
+
+ ret = regmap_read(cs35l56_base->regmap, CS35L56_PROTECTION_STATUS, &prot_status);
+ if (ret) {
+ dev_err(cs35l56_base->dev, "Get PROTECTION_STATUS failed: %d\n", ret);
+ return ret;
+ }
+
+ *fw_missing = !!(prot_status & CS35L56_FIRMWARE_MISSING);
+
+ ret = regmap_read(cs35l56_base->regmap, CS35L56_DSP1_FW_VER, fw_version);
+ if (ret) {
+ dev_err(cs35l56_base->dev, "Get FW VER failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_read_prot_status, SND_SOC_CS35L56_SHARED);
+
int cs35l56_hw_init(struct cs35l56_base *cs35l56_base)
{
int ret;
- unsigned int devid, revid, otpid, secured;
+ unsigned int devid, revid, otpid, secured, fw_ver;
+ bool fw_missing;
/*
* When the system is not using a reset_gpio ensure the device is
@@ -652,8 +716,13 @@ int cs35l56_hw_init(struct cs35l56_base *cs35l56_base)
return ret;
}
- dev_info(cs35l56_base->dev, "Cirrus Logic CS35L56%s Rev %02X OTP%d\n",
- cs35l56_base->secured ? "s" : "", cs35l56_base->rev, otpid);
+ ret = cs35l56_read_prot_status(cs35l56_base, &fw_missing, &fw_ver);
+ if (ret)
+ return ret;
+
+ dev_info(cs35l56_base->dev, "Cirrus Logic CS35L56%s Rev %02X OTP%d fw:%d.%d.%d (patched=%u)\n",
+ cs35l56_base->secured ? "s" : "", cs35l56_base->rev, otpid,
+ fw_ver >> 16, (fw_ver >> 8) & 0xff, fw_ver & 0xff, !fw_missing);
/* Wake source and *_BLOCKED interrupts default to unmasked, so mask them */
regmap_write(cs35l56_base->regmap, CS35L56_IRQ1_MASK_20, 0xffffffff);
@@ -668,6 +737,41 @@ int cs35l56_hw_init(struct cs35l56_base *cs35l56_base)
}
EXPORT_SYMBOL_NS_GPL(cs35l56_hw_init, SND_SOC_CS35L56_SHARED);
+int cs35l56_get_speaker_id(struct cs35l56_base *cs35l56_base)
+{
+ struct gpio_descs *descs;
+ int speaker_id;
+ int i, ret;
+
+ /* Read the speaker type qualifier from the motherboard GPIOs */
+ descs = gpiod_get_array_optional(cs35l56_base->dev, "spk-id", GPIOD_IN);
+ if (!descs) {
+ return -ENOENT;
+ } else if (IS_ERR(descs)) {
+ ret = PTR_ERR(descs);
+ return dev_err_probe(cs35l56_base->dev, ret, "Failed to get spk-id-gpios\n");
+ }
+
+ speaker_id = 0;
+ for (i = 0; i < descs->ndescs; i++) {
+ ret = gpiod_get_value_cansleep(descs->desc[i]);
+ if (ret < 0) {
+ dev_err_probe(cs35l56_base->dev, ret, "Failed to read spk-id[%d]\n", i);
+ goto err;
+ }
+
+ speaker_id |= (ret << i);
+ }
+
+ dev_dbg(cs35l56_base->dev, "Speaker ID = %d\n", speaker_id);
+ ret = speaker_id;
+err:
+ gpiod_put_array(descs);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_get_speaker_id, SND_SOC_CS35L56_SHARED);
+
static const u32 cs35l56_bclk_valid_for_pll_freq_table[] = {
[0x0C] = 128000,
[0x0F] = 256000,
diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c
index 45b4de3eff94..6dd0319bc843 100644
--- a/sound/soc/codecs/cs35l56.c
+++ b/sound/soc/codecs/cs35l56.c
@@ -5,6 +5,7 @@
// Copyright (C) 2023 Cirrus Logic, Inc. and
// Cirrus Logic International Semiconductor Ltd.
+#include <linux/acpi.h>
#include <linux/completion.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
@@ -15,6 +16,7 @@
#include <linux/module.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
+#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
@@ -59,6 +61,131 @@ static int cs35l56_dspwait_put_volsw(struct snd_kcontrol *kcontrol,
return snd_soc_put_volsw(kcontrol, ucontrol);
}
+static const unsigned short cs35l56_asp1_mixer_regs[] = {
+ CS35L56_ASP1TX1_INPUT, CS35L56_ASP1TX2_INPUT,
+ CS35L56_ASP1TX3_INPUT, CS35L56_ASP1TX4_INPUT,
+};
+
+static const char * const cs35l56_asp1_mux_control_names[] = {
+ "ASP1 TX1 Source", "ASP1 TX2 Source", "ASP1 TX3 Source", "ASP1 TX4 Source"
+};
+
+static int cs35l56_sync_asp1_mixer_widgets_with_firmware(struct cs35l56_private *cs35l56)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cs35l56->component);
+ const char *prefix = cs35l56->component->name_prefix;
+ char full_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ const char *name;
+ struct snd_kcontrol *kcontrol;
+ struct soc_enum *e;
+ unsigned int val[4];
+ int i, item, ret;
+
+ if (cs35l56->asp1_mixer_widgets_initialized)
+ return 0;
+
+ /*
+ * Resume so we can read the registers from silicon if the regmap
+ * cache has not yet been populated.
+ */
+ ret = pm_runtime_resume_and_get(cs35l56->base.dev);
+ if (ret < 0)
+ return ret;
+
+ /* Wait for firmware download and reboot */
+ cs35l56_wait_dsp_ready(cs35l56);
+
+ ret = regmap_bulk_read(cs35l56->base.regmap, CS35L56_ASP1TX1_INPUT,
+ val, ARRAY_SIZE(val));
+
+ pm_runtime_mark_last_busy(cs35l56->base.dev);
+ pm_runtime_put_autosuspend(cs35l56->base.dev);
+
+ if (ret) {
+ dev_err(cs35l56->base.dev, "Failed to read ASP1 mixer regs: %d\n", ret);
+ return ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(cs35l56_asp1_mux_control_names); ++i) {
+ name = cs35l56_asp1_mux_control_names[i];
+
+ if (prefix) {
+ snprintf(full_name, sizeof(full_name), "%s %s", prefix, name);
+ name = full_name;
+ }
+
+ kcontrol = snd_soc_card_get_kcontrol_locked(dapm->card, name);
+ if (!kcontrol) {
+ dev_warn(cs35l56->base.dev, "Could not find control %s\n", name);
+ continue;
+ }
+
+ e = (struct soc_enum *)kcontrol->private_value;
+ item = snd_soc_enum_val_to_item(e, val[i] & CS35L56_ASP_TXn_SRC_MASK);
+ snd_soc_dapm_mux_update_power(dapm, kcontrol, item, e, NULL);
+ }
+
+ cs35l56->asp1_mixer_widgets_initialized = true;
+
+ return 0;
+}
+
+static int cs35l56_dspwait_asp1tx_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol);
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ int index = e->shift_l;
+ unsigned int addr, val;
+ int ret;
+
+ ret = cs35l56_sync_asp1_mixer_widgets_with_firmware(cs35l56);
+ if (ret)
+ return ret;
+
+ addr = cs35l56_asp1_mixer_regs[index];
+ ret = regmap_read(cs35l56->base.regmap, addr, &val);
+ if (ret)
+ return ret;
+
+ val &= CS35L56_ASP_TXn_SRC_MASK;
+ ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(e, val);
+
+ return 0;
+}
+
+static int cs35l56_dspwait_asp1tx_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ int item = ucontrol->value.enumerated.item[0];
+ int index = e->shift_l;
+ unsigned int addr, val;
+ bool changed;
+ int ret;
+
+ ret = cs35l56_sync_asp1_mixer_widgets_with_firmware(cs35l56);
+ if (ret)
+ return ret;
+
+ addr = cs35l56_asp1_mixer_regs[index];
+ val = snd_soc_enum_item_to_val(e, item);
+
+ ret = regmap_update_bits_check(cs35l56->base.regmap, addr,
+ CS35L56_ASP_TXn_SRC_MASK, val, &changed);
+ if (ret)
+ return ret;
+
+ if (changed)
+ snd_soc_dapm_mux_update_power(dapm, kcontrol, item, e, NULL);
+
+ return changed;
+}
+
static DECLARE_TLV_DB_SCALE(vol_tlv, -10000, 25, 0);
static const struct snd_kcontrol_new cs35l56_controls[] = {
@@ -77,40 +204,44 @@ static const struct snd_kcontrol_new cs35l56_controls[] = {
};
static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx1_enum,
- CS35L56_ASP1TX1_INPUT,
- 0, CS35L56_ASP_TXn_SRC_MASK,
+ SND_SOC_NOPM,
+ 0, 0,
cs35l56_tx_input_texts,
cs35l56_tx_input_values);
static const struct snd_kcontrol_new asp1_tx1_mux =
- SOC_DAPM_ENUM("ASP1TX1 SRC", cs35l56_asp1tx1_enum);
+ SOC_DAPM_ENUM_EXT("ASP1TX1 SRC", cs35l56_asp1tx1_enum,
+ cs35l56_dspwait_asp1tx_get, cs35l56_dspwait_asp1tx_put);
static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx2_enum,
- CS35L56_ASP1TX2_INPUT,
- 0, CS35L56_ASP_TXn_SRC_MASK,
+ SND_SOC_NOPM,
+ 1, 0,
cs35l56_tx_input_texts,
cs35l56_tx_input_values);
static const struct snd_kcontrol_new asp1_tx2_mux =
- SOC_DAPM_ENUM("ASP1TX2 SRC", cs35l56_asp1tx2_enum);
+ SOC_DAPM_ENUM_EXT("ASP1TX2 SRC", cs35l56_asp1tx2_enum,
+ cs35l56_dspwait_asp1tx_get, cs35l56_dspwait_asp1tx_put);
static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx3_enum,
- CS35L56_ASP1TX3_INPUT,
- 0, CS35L56_ASP_TXn_SRC_MASK,
+ SND_SOC_NOPM,
+ 2, 0,
cs35l56_tx_input_texts,
cs35l56_tx_input_values);
static const struct snd_kcontrol_new asp1_tx3_mux =
- SOC_DAPM_ENUM("ASP1TX3 SRC", cs35l56_asp1tx3_enum);
+ SOC_DAPM_ENUM_EXT("ASP1TX3 SRC", cs35l56_asp1tx3_enum,
+ cs35l56_dspwait_asp1tx_get, cs35l56_dspwait_asp1tx_put);
static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx4_enum,
- CS35L56_ASP1TX4_INPUT,
- 0, CS35L56_ASP_TXn_SRC_MASK,
+ SND_SOC_NOPM,
+ 3, 0,
cs35l56_tx_input_texts,
cs35l56_tx_input_values);
static const struct snd_kcontrol_new asp1_tx4_mux =
- SOC_DAPM_ENUM("ASP1TX4 SRC", cs35l56_asp1tx4_enum);
+ SOC_DAPM_ENUM_EXT("ASP1TX4 SRC", cs35l56_asp1tx4_enum,
+ cs35l56_dspwait_asp1tx_get, cs35l56_dspwait_asp1tx_put);
static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_sdw1tx1_enum,
CS35L56_SWIRE_DP3_CH1_INPUT,
@@ -148,6 +279,21 @@ static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_sdw1tx4_enum,
static const struct snd_kcontrol_new sdw1_tx4_mux =
SOC_DAPM_ENUM("SDW1TX4 SRC", cs35l56_sdw1tx4_enum);
+static int cs35l56_asp1_cfg_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Override register values set by firmware boot */
+ return cs35l56_force_sync_asp1_registers_from_cache(&cs35l56->base);
+ default:
+ return 0;
+ }
+}
+
static int cs35l56_play_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
@@ -184,6 +330,9 @@ static const struct snd_soc_dapm_widget cs35l56_dapm_widgets[] = {
SND_SOC_DAPM_REGULATOR_SUPPLY("VDD_B", 0, 0),
SND_SOC_DAPM_REGULATOR_SUPPLY("VDD_AMP", 0, 0),
+ SND_SOC_DAPM_SUPPLY("ASP1 CFG", SND_SOC_NOPM, 0, 0, cs35l56_asp1_cfg_event,
+ SND_SOC_DAPM_PRE_PMU),
+
SND_SOC_DAPM_SUPPLY("PLAY", SND_SOC_NOPM, 0, 0, cs35l56_play_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
@@ -251,6 +400,9 @@ static const struct snd_soc_dapm_route cs35l56_audio_map[] = {
{ "AMP", NULL, "VDD_B" },
{ "AMP", NULL, "VDD_AMP" },
+ { "ASP1 Playback", NULL, "ASP1 CFG" },
+ { "ASP1 Capture", NULL, "ASP1 CFG" },
+
{ "ASP1 Playback", NULL, "PLAY" },
{ "SDW1 Playback", NULL, "PLAY" },
@@ -650,7 +802,7 @@ static struct snd_soc_dai_driver cs35l56_dai[] = {
}
};
-static void cs35l56_secure_patch(struct cs35l56_private *cs35l56)
+static void cs35l56_reinit_patch(struct cs35l56_private *cs35l56)
{
int ret;
@@ -662,19 +814,10 @@ static void cs35l56_secure_patch(struct cs35l56_private *cs35l56)
cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT);
}
-static void cs35l56_patch(struct cs35l56_private *cs35l56)
+static void cs35l56_patch(struct cs35l56_private *cs35l56, bool firmware_missing)
{
- unsigned int firmware_missing;
int ret;
- ret = regmap_read(cs35l56->base.regmap, CS35L56_PROTECTION_STATUS, &firmware_missing);
- if (ret) {
- dev_err(cs35l56->base.dev, "Failed to read PROTECTION_STATUS: %d\n", ret);
- return;
- }
-
- firmware_missing &= CS35L56_FIRMWARE_MISSING;
-
/*
* Disable SoundWire interrupts to prevent race with IRQ work.
* Setting sdw_irq_no_unmask prevents the handler re-enabling
@@ -747,23 +890,51 @@ static void cs35l56_dsp_work(struct work_struct *work)
struct cs35l56_private *cs35l56 = container_of(work,
struct cs35l56_private,
dsp_work);
+ unsigned int firmware_version;
+ bool firmware_missing;
+ int ret;
if (!cs35l56->base.init_done)
return;
pm_runtime_get_sync(cs35l56->base.dev);
+ ret = cs35l56_read_prot_status(&cs35l56->base, &firmware_missing, &firmware_version);
+ if (ret)
+ goto err;
+
+ /* Populate fw file qualifier with the revision and security state */
+ kfree(cs35l56->dsp.fwf_name);
+ if (firmware_missing) {
+ cs35l56->dsp.fwf_name = kasprintf(GFP_KERNEL, "%02x-dsp1", cs35l56->base.rev);
+ } else {
+ /* Firmware files must match the running firmware version */
+ cs35l56->dsp.fwf_name = kasprintf(GFP_KERNEL,
+ "%02x%s-%06x-dsp1",
+ cs35l56->base.rev,
+ cs35l56->base.secured ? "-s" : "",
+ firmware_version);
+ }
+
+ if (!cs35l56->dsp.fwf_name)
+ goto err;
+
+ dev_dbg(cs35l56->base.dev, "DSP fwf name: '%s' system name: '%s'\n",
+ cs35l56->dsp.fwf_name, cs35l56->dsp.system_name);
+
/*
- * 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.
+ * The firmware cannot be patched if it is already running from
+ * patch RAM. In this case the firmware files are versioned to
+ * match the running firmware version and will only contain
+ * tunings. We do not need to shutdown the firmware to apply
+ * tunings so can use the lower cost reinit sequence instead.
*/
- if (cs35l56->base.secured)
- cs35l56_secure_patch(cs35l56);
+ if (!firmware_missing)
+ cs35l56_reinit_patch(cs35l56);
else
- cs35l56_patch(cs35l56);
+ cs35l56_patch(cs35l56, firmware_missing);
+err:
pm_runtime_mark_last_busy(cs35l56->base.dev);
pm_runtime_put_autosuspend(cs35l56->base.dev);
}
@@ -778,10 +949,19 @@ static int cs35l56_component_probe(struct snd_soc_component *component)
if (!cs35l56->dsp.system_name &&
(snd_soc_card_get_pci_ssid(component->card, &vendor, &device) == 0)) {
- cs35l56->dsp.system_name = devm_kasprintf(cs35l56->base.dev,
- GFP_KERNEL,
- "%04x%04x",
- vendor, device);
+ /* Append a speaker qualifier if there is a speaker ID */
+ if (cs35l56->speaker_id >= 0) {
+ cs35l56->dsp.system_name = devm_kasprintf(cs35l56->base.dev,
+ GFP_KERNEL,
+ "%04x%04x-spkid%d",
+ vendor, device,
+ cs35l56->speaker_id);
+ } else {
+ cs35l56->dsp.system_name = devm_kasprintf(cs35l56->base.dev,
+ GFP_KERNEL,
+ "%04x%04x",
+ vendor, device);
+ }
if (!cs35l56->dsp.system_name)
return -ENOMEM;
}
@@ -799,6 +979,13 @@ static int cs35l56_component_probe(struct snd_soc_component *component)
debugfs_create_bool("can_hibernate", 0444, debugfs_root, &cs35l56->base.can_hibernate);
debugfs_create_bool("fw_patched", 0444, debugfs_root, &cs35l56->base.fw_patched);
+ /*
+ * The widgets for the ASP1TX mixer can't be initialized
+ * until the firmware has been downloaded and rebooted.
+ */
+ regcache_drop_region(cs35l56->base.regmap, CS35L56_ASP1TX1_INPUT, CS35L56_ASP1TX4_INPUT);
+ cs35l56->asp1_mixer_widgets_initialized = false;
+
queue_work(cs35l56->dsp_wq, &cs35l56->dsp_work);
return 0;
@@ -809,6 +996,16 @@ static void cs35l56_component_remove(struct snd_soc_component *component)
struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
cancel_work_sync(&cs35l56->dsp_work);
+
+ if (cs35l56->dsp.cs_dsp.booted)
+ wm_adsp_power_down(&cs35l56->dsp);
+
+ wm_adsp2_component_remove(&cs35l56->dsp, component);
+
+ kfree(cs35l56->dsp.fwf_name);
+ cs35l56->dsp.fwf_name = NULL;
+
+ cs35l56->component = NULL;
}
static int cs35l56_set_bias_level(struct snd_soc_component *component,
@@ -1050,7 +1247,13 @@ static int cs35l56_get_firmware_uid(struct cs35l56_private *cs35l56)
if (ret < 0)
return 0;
- cs35l56->dsp.system_name = devm_kstrdup(dev, prop, GFP_KERNEL);
+ /* Append a speaker qualifier if there is a speaker ID */
+ if (cs35l56->speaker_id >= 0)
+ cs35l56->dsp.system_name = devm_kasprintf(dev, GFP_KERNEL, "%s-spkid%d",
+ prop, cs35l56->speaker_id);
+ else
+ cs35l56->dsp.system_name = devm_kstrdup(dev, prop, GFP_KERNEL);
+
if (cs35l56->dsp.system_name == NULL)
return -ENOMEM;
@@ -1059,12 +1262,101 @@ static int cs35l56_get_firmware_uid(struct cs35l56_private *cs35l56)
return 0;
}
+/*
+ * Some SoundWire laptops have a spk-id-gpios property but it points to
+ * the wrong ACPI Device node so can't be used to get the GPIO. Try to
+ * find the SDCA node containing the GpioIo resource and add a GPIO
+ * mapping to it.
+ */
+static const struct acpi_gpio_params cs35l56_af01_first_gpio = { 0, 0, false };
+static const struct acpi_gpio_mapping cs35l56_af01_spkid_gpios_mapping[] = {
+ { "spk-id-gpios", &cs35l56_af01_first_gpio, 1 },
+ { }
+};
+
+static void cs35l56_acpi_dev_release_driver_gpios(void *adev)
+{
+ acpi_dev_remove_driver_gpios(adev);
+}
+
+static int cs35l56_try_get_broken_sdca_spkid_gpio(struct cs35l56_private *cs35l56)
+{
+ struct fwnode_handle *af01_fwnode;
+ const union acpi_object *obj;
+ struct gpio_desc *desc;
+ int ret;
+
+ /* Find the SDCA node containing the GpioIo */
+ af01_fwnode = device_get_named_child_node(cs35l56->base.dev, "AF01");
+ if (!af01_fwnode) {
+ dev_dbg(cs35l56->base.dev, "No AF01 node\n");
+ return -ENOENT;
+ }
+
+ ret = acpi_dev_get_property(ACPI_COMPANION(cs35l56->base.dev),
+ "spk-id-gpios", ACPI_TYPE_PACKAGE, &obj);
+ if (ret) {
+ dev_dbg(cs35l56->base.dev, "Could not get spk-id-gpios package: %d\n", ret);
+ return -ENOENT;
+ }
+
+ /* The broken properties we can handle are a 4-element package (one GPIO) */
+ if (obj->package.count != 4) {
+ dev_warn(cs35l56->base.dev, "Unexpected spk-id element count %d\n",
+ obj->package.count);
+ return -ENOENT;
+ }
+
+ /* Add a GPIO mapping if it doesn't already have one */
+ if (!fwnode_property_present(af01_fwnode, "spk-id-gpios")) {
+ struct acpi_device *adev = to_acpi_device_node(af01_fwnode);
+
+ /*
+ * Can't use devm_acpi_dev_add_driver_gpios() because the
+ * mapping isn't being added to the node pointed to by
+ * ACPI_COMPANION().
+ */
+ ret = acpi_dev_add_driver_gpios(adev, cs35l56_af01_spkid_gpios_mapping);
+ if (ret) {
+ return dev_err_probe(cs35l56->base.dev, ret,
+ "Failed to add gpio mapping to AF01\n");
+ }
+
+ ret = devm_add_action_or_reset(cs35l56->base.dev,
+ cs35l56_acpi_dev_release_driver_gpios,
+ adev);
+ if (ret)
+ return ret;
+
+ dev_dbg(cs35l56->base.dev, "Added spk-id-gpios mapping to AF01\n");
+ }
+
+ desc = fwnode_gpiod_get_index(af01_fwnode, "spk-id", 0, GPIOD_IN, NULL);
+ if (IS_ERR(desc)) {
+ ret = PTR_ERR(desc);
+ return dev_err_probe(cs35l56->base.dev, ret, "Get GPIO from AF01 failed\n");
+ }
+
+ ret = gpiod_get_value_cansleep(desc);
+ gpiod_put(desc);
+
+ if (ret < 0) {
+ dev_err_probe(cs35l56->base.dev, ret, "Error reading spk-id GPIO\n");
+ return ret;
+ }
+
+ dev_info(cs35l56->base.dev, "Got spk-id from AF01\n");
+
+ return ret;
+}
+
int cs35l56_common_probe(struct cs35l56_private *cs35l56)
{
int ret;
init_completion(&cs35l56->init_completion);
mutex_init(&cs35l56->base.irq_lock);
+ cs35l56->speaker_id = -ENOENT;
dev_set_drvdata(cs35l56->base.dev, cs35l56);
@@ -1101,6 +1393,15 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56)
gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 1);
}
+ ret = cs35l56_get_speaker_id(&cs35l56->base);
+ if (ACPI_COMPANION(cs35l56->base.dev) && cs35l56->sdw_peripheral && (ret == -ENOENT))
+ ret = cs35l56_try_get_broken_sdca_spkid_gpio(cs35l56);
+
+ if ((ret < 0) && (ret != -ENOENT))
+ goto err;
+
+ cs35l56->speaker_id = ret;
+
ret = cs35l56_get_firmware_uid(cs35l56);
if (ret != 0)
goto err;
@@ -1152,11 +1453,9 @@ int cs35l56_init(struct cs35l56_private *cs35l56)
if (ret < 0)
return ret;
- /* Populate the DSP information with the revision and security state */
- 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;
+ ret = cs35l56_set_patch(&cs35l56->base);
+ if (ret)
+ return ret;
if (!cs35l56->base.reset_gpio) {
dev_dbg(cs35l56->base.dev, "No reset gpio: using soft reset\n");
@@ -1190,10 +1489,6 @@ post_soft_reset:
if (ret)
return ret;
- ret = cs35l56_set_patch(&cs35l56->base);
- if (ret)
- return ret;
-
/* Registers could be dirty after soft reset or SoundWire enumeration */
regcache_sync(cs35l56->base.regmap);
diff --git a/sound/soc/codecs/cs35l56.h b/sound/soc/codecs/cs35l56.h
index 8159c3e217d9..b000e7365e40 100644
--- a/sound/soc/codecs/cs35l56.h
+++ b/sound/soc/codecs/cs35l56.h
@@ -44,12 +44,14 @@ struct cs35l56_private {
bool sdw_attached;
struct completion init_completion;
+ int speaker_id;
u32 rx_mask;
u32 tx_mask;
u8 asp_slot_width;
u8 asp_slot_count;
bool tdm_mode;
bool sysclk_set;
+ bool asp1_mixer_widgets_initialized;
u8 old_sdw_clock_scale;
};
diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c
index 9e6f8a048dd5..e864188ae5eb 100644
--- a/sound/soc/codecs/cs4271.c
+++ b/sound/soc/codecs/cs4271.c
@@ -13,9 +13,8 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/of.h>
-#include <linux/of_gpio.h>
#include <linux/regulator/consumer.h>
#include <sound/pcm.h>
#include <sound/soc.h>
@@ -160,9 +159,7 @@ struct cs4271_private {
/* Current sample rate for de-emphasis control */
int rate;
/* GPIO driving Reset pin, if any */
- int gpio_nreset;
- /* GPIO that disable serial bus, if any */
- int gpio_disable;
+ struct gpio_desc *reset;
/* enable soft reset workaround */
bool enable_soft_reset;
struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
@@ -487,12 +484,10 @@ static int cs4271_reset(struct snd_soc_component *component)
{
struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component);
- if (gpio_is_valid(cs4271->gpio_nreset)) {
- gpio_direction_output(cs4271->gpio_nreset, 0);
- mdelay(1);
- gpio_set_value(cs4271->gpio_nreset, 1);
- mdelay(1);
- }
+ gpiod_direction_output(cs4271->reset, 1);
+ mdelay(1);
+ gpiod_set_value(cs4271->reset, 0);
+ mdelay(1);
return 0;
}
@@ -612,9 +607,8 @@ static void cs4271_component_remove(struct snd_soc_component *component)
{
struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component);
- if (gpio_is_valid(cs4271->gpio_nreset))
- /* Set codec to the reset state */
- gpio_set_value(cs4271->gpio_nreset, 0);
+ /* Set codec to the reset state */
+ gpiod_set_value(cs4271->reset, 1);
regcache_mark_dirty(cs4271->regmap);
regulator_bulk_disable(ARRAY_SIZE(cs4271->supplies), cs4271->supplies);
@@ -639,7 +633,6 @@ static const struct snd_soc_component_driver soc_component_dev_cs4271 = {
static int cs4271_common_probe(struct device *dev,
struct cs4271_private **c)
{
- struct cs4271_platform_data *cs4271plat = dev->platform_data;
struct cs4271_private *cs4271;
int i, ret;
@@ -647,17 +640,11 @@ static int cs4271_common_probe(struct device *dev,
if (!cs4271)
return -ENOMEM;
- cs4271->gpio_nreset = of_get_named_gpio(dev->of_node, "reset-gpio", 0);
-
- if (cs4271plat)
- cs4271->gpio_nreset = cs4271plat->gpio_nreset;
-
- if (gpio_is_valid(cs4271->gpio_nreset)) {
- ret = devm_gpio_request(dev, cs4271->gpio_nreset,
- "CS4271 Reset");
- if (ret < 0)
- return ret;
- }
+ cs4271->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS);
+ if (IS_ERR(cs4271->reset))
+ return dev_err_probe(dev, PTR_ERR(cs4271->reset),
+ "error retrieving RESET GPIO\n");
+ gpiod_set_consumer_name(cs4271->reset, "CS4271 Reset");
for (i = 0; i < ARRAY_SIZE(supply_names); i++)
cs4271->supplies[i].supply = supply_names[i];
diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c
index 94bcab812629..2d11c5125f73 100644
--- a/sound/soc/codecs/cs42l42.c
+++ b/sound/soc/codecs/cs42l42.c
@@ -15,7 +15,6 @@
#include <linux/types.h>
#include <linux/init.h>
#include <linux/delay.h>
-#include <linux/gpio.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/acpi.h>
diff --git a/sound/soc/codecs/cs42l42.h b/sound/soc/codecs/cs42l42.h
index 7785125b73ab..3d85ebc59489 100644
--- a/sound/soc/codecs/cs42l42.h
+++ b/sound/soc/codecs/cs42l42.h
@@ -14,7 +14,7 @@
#include <dt-bindings/sound/cs42l42.h>
#include <linux/device.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/mutex.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
diff --git a/sound/soc/codecs/cs42l43-jack.c b/sound/soc/codecs/cs42l43-jack.c
index 54a3ea606443..24a598f2ed9a 100644
--- a/sound/soc/codecs/cs42l43-jack.c
+++ b/sound/soc/codecs/cs42l43-jack.c
@@ -507,7 +507,7 @@ static void cs42l43_start_load_detect(struct cs42l43_codec *priv)
priv->load_detect_running = true;
- if (priv->hp_ena) {
+ if (priv->hp_ena && !priv->hp_ilimited) {
unsigned long time_left;
reinit_completion(&priv->hp_shutdown);
@@ -572,7 +572,7 @@ static void cs42l43_stop_load_detect(struct cs42l43_codec *priv)
CS42L43_ADC1_EN_MASK | CS42L43_ADC2_EN_MASK,
priv->adc_ena);
- if (priv->hp_ena) {
+ if (priv->hp_ena && !priv->hp_ilimited) {
unsigned long time_left;
reinit_completion(&priv->hp_startup);
diff --git a/sound/soc/codecs/cs42l43.c b/sound/soc/codecs/cs42l43.c
index d62c9f26c632..a97ccb512deb 100644
--- a/sound/soc/codecs/cs42l43.c
+++ b/sound/soc/codecs/cs42l43.c
@@ -138,7 +138,87 @@ CS42L43_IRQ_ERROR(spkr_therm_warm)
CS42L43_IRQ_ERROR(spkl_therm_warm)
CS42L43_IRQ_ERROR(spkr_sc_detect)
CS42L43_IRQ_ERROR(spkl_sc_detect)
-CS42L43_IRQ_ERROR(hp_ilimit)
+
+static void cs42l43_hp_ilimit_clear_work(struct work_struct *work)
+{
+ struct cs42l43_codec *priv = container_of(work, struct cs42l43_codec,
+ hp_ilimit_clear_work.work);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(priv->component);
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ priv->hp_ilimit_count--;
+
+ if (priv->hp_ilimit_count)
+ queue_delayed_work(system_wq, &priv->hp_ilimit_clear_work,
+ msecs_to_jiffies(CS42L43_HP_ILIMIT_DECAY_MS));
+
+ snd_soc_dapm_mutex_unlock(dapm);
+}
+
+static void cs42l43_hp_ilimit_work(struct work_struct *work)
+{
+ struct cs42l43_codec *priv = container_of(work, struct cs42l43_codec,
+ hp_ilimit_work);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(priv->component);
+ struct cs42l43 *cs42l43 = priv->core;
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ if (priv->hp_ilimit_count < CS42L43_HP_ILIMIT_MAX_COUNT) {
+ if (!priv->hp_ilimit_count)
+ queue_delayed_work(system_wq, &priv->hp_ilimit_clear_work,
+ msecs_to_jiffies(CS42L43_HP_ILIMIT_DECAY_MS));
+
+ priv->hp_ilimit_count++;
+ snd_soc_dapm_mutex_unlock(dapm);
+ return;
+ }
+
+ dev_err(priv->dev, "Disabling headphone for %dmS, due to frequent current limit\n",
+ CS42L43_HP_ILIMIT_BACKOFF_MS);
+
+ priv->hp_ilimited = true;
+
+ // No need to wait for disable, as just disabling for a period of time
+ regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN8,
+ CS42L43_HP_EN_MASK, 0);
+
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ msleep(CS42L43_HP_ILIMIT_BACKOFF_MS);
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ if (priv->hp_ena && !priv->load_detect_running) {
+ unsigned long time_left;
+
+ reinit_completion(&priv->hp_startup);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN8,
+ CS42L43_HP_EN_MASK, priv->hp_ena);
+
+ time_left = wait_for_completion_timeout(&priv->hp_startup,
+ msecs_to_jiffies(CS42L43_HP_TIMEOUT_MS));
+ if (!time_left)
+ dev_err(priv->dev, "ilimit HP restore timed out\n");
+ }
+
+ priv->hp_ilimited = false;
+
+ snd_soc_dapm_mutex_unlock(dapm);
+}
+
+static irqreturn_t cs42l43_hp_ilimit(int irq, void *data)
+{
+ struct cs42l43_codec *priv = data;
+
+ dev_dbg(priv->dev, "headphone ilimit IRQ\n");
+
+ queue_work(system_long_wq, &priv->hp_ilimit_work);
+
+ return IRQ_HANDLED;
+}
#define CS42L43_IRQ_COMPLETE(name) \
static irqreturn_t cs42l43_##name(int irq, void *data) \
@@ -1452,13 +1532,13 @@ static int cs42l43_hp_ev(struct snd_soc_dapm_widget *w,
if (ret)
return ret;
- if (!priv->load_detect_running)
+ if (!priv->load_detect_running && !priv->hp_ilimited)
regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN8,
mask, val);
break;
case SND_SOC_DAPM_POST_PMU:
case SND_SOC_DAPM_POST_PMD:
- if (priv->load_detect_running)
+ if (priv->load_detect_running || priv->hp_ilimited)
break;
ret = cs42l43_dapm_wait_completion(&priv->hp_startup,
@@ -2169,13 +2249,18 @@ static int cs42l43_codec_probe(struct platform_device *pdev)
INIT_DELAYED_WORK(&priv->tip_sense_work, cs42l43_tip_sense_work);
INIT_DELAYED_WORK(&priv->bias_sense_timeout, cs42l43_bias_sense_timeout);
INIT_DELAYED_WORK(&priv->button_press_work, cs42l43_button_press_work);
+ INIT_DELAYED_WORK(&priv->hp_ilimit_clear_work, cs42l43_hp_ilimit_clear_work);
INIT_WORK(&priv->button_release_work, cs42l43_button_release_work);
+ INIT_WORK(&priv->hp_ilimit_work, cs42l43_hp_ilimit_work);
pm_runtime_set_autosuspend_delay(priv->dev, 100);
pm_runtime_use_autosuspend(priv->dev);
pm_runtime_set_active(priv->dev);
pm_runtime_get_noresume(priv->dev);
- devm_pm_runtime_enable(priv->dev);
+
+ ret = devm_pm_runtime_enable(priv->dev);
+ if (ret)
+ goto err_pm;
for (i = 0; i < ARRAY_SIZE(cs42l43_irqs); i++) {
ret = cs42l43_request_irq(priv, dom, cs42l43_irqs[i].name,
@@ -2251,8 +2336,47 @@ static int cs42l43_codec_runtime_resume(struct device *dev)
return 0;
}
-DEFINE_RUNTIME_DEV_PM_OPS(cs42l43_codec_pm_ops, NULL,
- cs42l43_codec_runtime_resume, NULL);
+static int cs42l43_codec_suspend(struct device *dev)
+{
+ struct cs42l43 *cs42l43 = dev_get_drvdata(dev);
+
+ disable_irq(cs42l43->irq);
+
+ return 0;
+}
+
+static int cs42l43_codec_suspend_noirq(struct device *dev)
+{
+ struct cs42l43 *cs42l43 = dev_get_drvdata(dev);
+
+ enable_irq(cs42l43->irq);
+
+ return 0;
+}
+
+static int cs42l43_codec_resume(struct device *dev)
+{
+ struct cs42l43 *cs42l43 = dev_get_drvdata(dev);
+
+ enable_irq(cs42l43->irq);
+
+ return 0;
+}
+
+static int cs42l43_codec_resume_noirq(struct device *dev)
+{
+ struct cs42l43 *cs42l43 = dev_get_drvdata(dev);
+
+ disable_irq(cs42l43->irq);
+
+ return 0;
+}
+
+static const struct dev_pm_ops cs42l43_codec_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(cs42l43_codec_suspend, cs42l43_codec_resume)
+ NOIRQ_SYSTEM_SLEEP_PM_OPS(cs42l43_codec_suspend_noirq, cs42l43_codec_resume_noirq)
+ RUNTIME_PM_OPS(NULL, cs42l43_codec_runtime_resume, NULL)
+};
static const struct platform_device_id cs42l43_codec_id_table[] = {
{ "cs42l43-codec", },
diff --git a/sound/soc/codecs/cs42l43.h b/sound/soc/codecs/cs42l43.h
index bf4f728eea3e..125e36861d5d 100644
--- a/sound/soc/codecs/cs42l43.h
+++ b/sound/soc/codecs/cs42l43.h
@@ -28,6 +28,10 @@
#define CS42L43_HP_TIMEOUT_MS 2000
#define CS42L43_LOAD_TIMEOUT_MS 1000
+#define CS42L43_HP_ILIMIT_BACKOFF_MS 1000
+#define CS42L43_HP_ILIMIT_DECAY_MS 300
+#define CS42L43_HP_ILIMIT_MAX_COUNT 4
+
#define CS42L43_ASP_MAX_CHANNELS 6
#define CS42L43_N_EQ_COEFFS 15
@@ -88,6 +92,11 @@ struct cs42l43_codec {
bool button_detect_running;
bool jack_present;
int jack_override;
+
+ struct work_struct hp_ilimit_work;
+ struct delayed_work hp_ilimit_clear_work;
+ bool hp_ilimited;
+ int hp_ilimit_count;
};
#if IS_REACHABLE(CONFIG_SND_SOC_CS42L43_SDW)
diff --git a/sound/soc/codecs/cs43130.c b/sound/soc/codecs/cs43130.c
index d8ec325b9cc9..b6d829bbe3cc 100644
--- a/sound/soc/codecs/cs43130.c
+++ b/sound/soc/codecs/cs43130.c
@@ -11,12 +11,11 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
-#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/i2c.h>
-#include <linux/of.h>
+#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <sound/core.h>
@@ -26,7 +25,6 @@
#include <sound/soc-dapm.h>
#include <sound/initval.h>
#include <sound/tlv.h>
-#include <linux/of_gpio.h>
#include <linux/regulator/consumer.h>
#include <linux/pm_runtime.h>
#include <linux/completion.h>
@@ -238,7 +236,7 @@ static int cs43130_pll_config(struct snd_soc_component *component)
struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component);
const struct cs43130_pll_params *pll_entry;
- dev_dbg(component->dev, "cs43130->mclk = %u, cs43130->mclk_int = %u\n",
+ dev_dbg(cs43130->dev, "cs43130->mclk = %u, cs43130->mclk_int = %u\n",
cs43130->mclk, cs43130->mclk_int);
pll_entry = cs43130_get_pll_table(cs43130->mclk, cs43130->mclk_int);
@@ -303,7 +301,7 @@ static int cs43130_set_pll(struct snd_soc_component *component, int pll_id, int
cs43130->mclk = freq_in;
break;
default:
- dev_err(component->dev,
+ dev_err(cs43130->dev,
"unsupported pll input reference clock:%d\n", freq_in);
return -EINVAL;
}
@@ -316,16 +314,44 @@ static int cs43130_set_pll(struct snd_soc_component *component, int pll_id, int
cs43130->mclk_int = freq_out;
break;
default:
- dev_err(component->dev,
+ dev_err(cs43130->dev,
"unsupported pll output ref clock: %u\n", freq_out);
return -EINVAL;
}
ret = cs43130_pll_config(component);
- dev_dbg(component->dev, "cs43130->pll_bypass = %d", cs43130->pll_bypass);
+ dev_dbg(cs43130->dev, "cs43130->pll_bypass = %d", cs43130->pll_bypass);
return ret;
}
+static int cs43130_wait_for_completion(struct cs43130_private *cs43130, struct completion *to_poll,
+ int time)
+{
+ int stickies, offset, flag, ret;
+
+ if (cs43130->has_irq_line) {
+ ret = wait_for_completion_timeout(to_poll, msecs_to_jiffies(time));
+ if (ret == 0)
+ return -ETIMEDOUT;
+ else
+ return 0; // Discard number of jiffies left till timeout and return success
+ }
+
+ if (to_poll == &cs43130->xtal_rdy) {
+ offset = 0;
+ flag = CS43130_XTAL_RDY_INT;
+ } else if (to_poll == &cs43130->pll_rdy) {
+ offset = 0;
+ flag = CS43130_PLL_RDY_INT;
+ } else {
+ return -EINVAL;
+ }
+
+ return regmap_read_poll_timeout(cs43130->regmap, CS43130_INT_STATUS_1 + offset,
+ stickies, (stickies & flag),
+ 1000, time * 1000);
+}
+
static int cs43130_change_clksrc(struct snd_soc_component *component,
enum cs43130_mclk_src_sel src)
{
@@ -346,7 +372,7 @@ static int cs43130_change_clksrc(struct snd_soc_component *component,
mclk_int_decoded = CS43130_MCLK_24P5;
break;
default:
- dev_err(component->dev, "Invalid MCLK INT freq: %u\n", cs43130->mclk_int);
+ dev_err(cs43130->dev, "Invalid MCLK INT freq: %u\n", cs43130->mclk_int);
return -EINVAL;
}
@@ -364,14 +390,13 @@ static int cs43130_change_clksrc(struct snd_soc_component *component,
CS43130_XTAL_RDY_INT_MASK, 0);
regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL,
CS43130_PDN_XTAL_MASK, 0);
- ret = wait_for_completion_timeout(&cs43130->xtal_rdy,
- msecs_to_jiffies(100));
+ ret = cs43130_wait_for_completion(cs43130, &cs43130->xtal_rdy, 100);
regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1,
CS43130_XTAL_RDY_INT_MASK,
1 << CS43130_XTAL_RDY_INT_SHIFT);
- if (ret == 0) {
- dev_err(component->dev, "Timeout waiting for XTAL_READY interrupt\n");
- return -ETIMEDOUT;
+ if (ret) {
+ dev_err(cs43130->dev, "Error waiting for XTAL_READY interrupt: %d\n", ret);
+ return ret;
}
}
@@ -400,14 +425,13 @@ static int cs43130_change_clksrc(struct snd_soc_component *component,
CS43130_XTAL_RDY_INT_MASK, 0);
regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL,
CS43130_PDN_XTAL_MASK, 0);
- ret = wait_for_completion_timeout(&cs43130->xtal_rdy,
- msecs_to_jiffies(100));
+ ret = cs43130_wait_for_completion(cs43130, &cs43130->xtal_rdy, 100);
regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1,
CS43130_XTAL_RDY_INT_MASK,
1 << CS43130_XTAL_RDY_INT_SHIFT);
- if (ret == 0) {
- dev_err(component->dev, "Timeout waiting for XTAL_READY interrupt\n");
- return -ETIMEDOUT;
+ if (ret) {
+ dev_err(cs43130->dev, "Error waiting for XTAL_READY interrupt: %d\n", ret);
+ return ret;
}
}
@@ -416,14 +440,13 @@ static int cs43130_change_clksrc(struct snd_soc_component *component,
CS43130_PLL_RDY_INT_MASK, 0);
regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL,
CS43130_PDN_PLL_MASK, 0);
- ret = wait_for_completion_timeout(&cs43130->pll_rdy,
- msecs_to_jiffies(100));
+ ret = cs43130_wait_for_completion(cs43130, &cs43130->pll_rdy, 100);
regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1,
CS43130_PLL_RDY_INT_MASK,
1 << CS43130_PLL_RDY_INT_SHIFT);
- if (ret == 0) {
- dev_err(component->dev, "Timeout waiting for PLL_READY interrupt\n");
- return -ETIMEDOUT;
+ if (ret) {
+ dev_err(cs43130->dev, "Error waiting for PLL_READY interrupt: %d\n", ret);
+ return ret;
}
regmap_update_bits(cs43130->regmap, CS43130_SYS_CLK_CTL_1,
@@ -453,7 +476,7 @@ static int cs43130_change_clksrc(struct snd_soc_component *component,
1 << CS43130_PDN_PLL_SHIFT);
break;
default:
- dev_err(component->dev, "Invalid MCLK source value\n");
+ dev_err(cs43130->dev, "Invalid MCLK source value\n");
return -EINVAL;
}
@@ -595,6 +618,27 @@ static int cs43130_set_sp_fmt(int dai_id, unsigned int bitwidth_sclk,
return -EINVAL;
}
+ switch (cs43130->dais[dai_id].dai_invert) {
+ case SND_SOC_DAIFMT_NB_NF:
+ sclk_edge = 1;
+ lrck_edge = 0;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ sclk_edge = 0;
+ lrck_edge = 0;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ sclk_edge = 1;
+ lrck_edge = 1;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ sclk_edge = 0;
+ lrck_edge = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
switch (cs43130->dais[dai_id].dai_mode) {
case SND_SOC_DAIFMT_CBS_CFS:
dai_mode_val = 0;
@@ -607,8 +651,6 @@ static int cs43130_set_sp_fmt(int dai_id, unsigned int bitwidth_sclk,
}
frm_size = bitwidth_sclk * params_channels(params);
- sclk_edge = 1;
- lrck_edge = 0;
loc_ch1 = 0;
loc_ch2 = bitwidth_sclk * (params_channels(params) - 1);
@@ -804,7 +846,7 @@ static int cs43130_dsd_hw_params(struct snd_pcm_substream *substream,
dsd_speed = 1;
break;
default:
- dev_err(component->dev, "Rate(%u) not supported\n",
+ dev_err(cs43130->dev, "Rate(%u) not supported\n",
params_rate(params));
return -EINVAL;
}
@@ -875,7 +917,7 @@ static int cs43130_hw_params(struct snd_pcm_substream *substream,
dsd_speed = 1;
break;
default:
- dev_err(component->dev, "Rate(%u) not supported\n",
+ dev_err(cs43130->dev, "Rate(%u) not supported\n",
params_rate(params));
return -EINVAL;
}
@@ -892,7 +934,7 @@ static int cs43130_hw_params(struct snd_pcm_substream *substream,
regmap_write(cs43130->regmap, CS43130_SP_SRATE, rate_map->val);
break;
default:
- dev_err(component->dev, "Invalid DAI (%d)\n", dai->id);
+ dev_err(cs43130->dev, "Invalid DAI (%d)\n", dai->id);
return -EINVAL;
}
@@ -916,21 +958,21 @@ static int cs43130_hw_params(struct snd_pcm_substream *substream,
if (!sclk) {
/* at this point, SCLK must be set */
- dev_err(component->dev, "SCLK freq is not set\n");
+ dev_err(cs43130->dev, "SCLK freq is not set\n");
return -EINVAL;
}
bitwidth_sclk = (sclk / params_rate(params)) / params_channels(params);
if (bitwidth_sclk < bitwidth_dai) {
- dev_err(component->dev, "Format not supported: SCLK freq is too low\n");
+ dev_err(cs43130->dev, "Format not supported: SCLK freq is too low\n");
return -EINVAL;
}
- dev_dbg(component->dev,
+ dev_dbg(cs43130->dev,
"sclk = %u, fs = %d, bitwidth_dai = %u\n",
sclk, params_rate(params), bitwidth_dai);
- dev_dbg(component->dev,
+ dev_dbg(cs43130->dev,
"bitwidth_sclk = %u, num_ch = %u\n",
bitwidth_sclk, params_channels(params));
@@ -1189,7 +1231,7 @@ static int cs43130_dsd_event(struct snd_soc_dapm_widget *w,
}
break;
default:
- dev_err(component->dev, "Invalid event = 0x%x\n", event);
+ dev_err(cs43130->dev, "Invalid event = 0x%x\n", event);
return -EINVAL;
}
return 0;
@@ -1246,7 +1288,7 @@ static int cs43130_pcm_event(struct snd_soc_dapm_widget *w,
}
break;
default:
- dev_err(component->dev, "Invalid event = 0x%x\n", event);
+ dev_err(cs43130->dev, "Invalid event = 0x%x\n", event);
return -EINVAL;
}
return 0;
@@ -1322,7 +1364,7 @@ static int cs43130_dac_event(struct snd_soc_dapm_widget *w,
}
break;
default:
- dev_err(component->dev, "Invalid DAC event = 0x%x\n", event);
+ dev_err(cs43130->dev, "Invalid DAC event = 0x%x\n", event);
return -EINVAL;
}
return 0;
@@ -1360,13 +1402,21 @@ static int cs43130_hpin_event(struct snd_soc_dapm_widget *w,
ARRAY_SIZE(hpin_postpmu_seq));
break;
default:
- dev_err(component->dev, "Invalid HPIN event = 0x%x\n", event);
+ dev_err(cs43130->dev, "Invalid HPIN event = 0x%x\n", event);
return -EINVAL;
}
return 0;
}
+static const char * const bypass_mux_text[] = {
+ "Internal",
+ "Alternative",
+};
+static SOC_ENUM_SINGLE_DECL(bypass_enum, SND_SOC_NOPM, 0, bypass_mux_text);
+static const struct snd_kcontrol_new bypass_ctrl = SOC_DAPM_ENUM("Switch", bypass_enum);
+
static const struct snd_soc_dapm_widget digital_hp_widgets[] = {
+ SND_SOC_DAPM_MUX("Bypass Switch", SND_SOC_NOPM, 0, 0, &bypass_ctrl),
SND_SOC_DAPM_OUTPUT("HPOUTA"),
SND_SOC_DAPM_OUTPUT("HPOUTB"),
@@ -1419,13 +1469,13 @@ static const struct snd_soc_dapm_route digital_hp_routes[] = {
{"DSD", NULL, "XSPIN DSD"},
{"HiFi DAC", NULL, "ASPIN PCM"},
{"HiFi DAC", NULL, "DSD"},
- {"HPOUTA", NULL, "HiFi DAC"},
- {"HPOUTB", NULL, "HiFi DAC"},
+ {"Bypass Switch", "Internal", "HiFi DAC"},
+ {"HPOUTA", NULL, "Bypass Switch"},
+ {"HPOUTB", NULL, "Bypass Switch"},
};
static const struct snd_soc_dapm_route analog_hp_routes[] = {
- {"HPOUTA", NULL, "Analog Playback"},
- {"HPOUTB", NULL, "Analog Playback"},
+ {"Bypass Switch", "Alternative", "Analog Playback"},
};
static struct snd_soc_dapm_route all_hp_routes[
@@ -1479,7 +1529,26 @@ static int cs43130_pcm_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
cs43130->dais[codec_dai->id].dai_mode = SND_SOC_DAIFMT_CBM_CFM;
break;
default:
- dev_err(component->dev, "unsupported mode\n");
+ dev_err(cs43130->dev, "unsupported mode\n");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ cs43130->dais[codec_dai->id].dai_invert = SND_SOC_DAIFMT_NB_NF;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ cs43130->dais[codec_dai->id].dai_invert = SND_SOC_DAIFMT_IB_NF;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ cs43130->dais[codec_dai->id].dai_invert = SND_SOC_DAIFMT_NB_IF;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ cs43130->dais[codec_dai->id].dai_invert = SND_SOC_DAIFMT_IB_IF;
+ break;
+ default:
+ dev_err(cs43130->dev, "Unsupported invert mode 0x%x\n",
+ fmt & SND_SOC_DAIFMT_INV_MASK);
return -EINVAL;
}
@@ -1497,12 +1566,12 @@ static int cs43130_pcm_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
cs43130->dais[codec_dai->id].dai_format = SND_SOC_DAIFMT_DSP_B;
break;
default:
- dev_err(component->dev,
+ dev_err(cs43130->dev,
"unsupported audio format\n");
return -EINVAL;
}
- dev_dbg(component->dev, "dai_id = %d, dai_mode = %u, dai_format = %u\n",
+ dev_dbg(cs43130->dev, "dai_id = %d, dai_mode = %u, dai_format = %u\n",
codec_dai->id,
cs43130->dais[codec_dai->id].dai_mode,
cs43130->dais[codec_dai->id].dai_format);
@@ -1523,11 +1592,11 @@ static int cs43130_dsd_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
cs43130->dais[codec_dai->id].dai_mode = SND_SOC_DAIFMT_CBM_CFM;
break;
default:
- dev_err(component->dev, "Unsupported DAI format.\n");
+ dev_err(cs43130->dev, "Unsupported DAI format.\n");
return -EINVAL;
}
- dev_dbg(component->dev, "dai_mode = 0x%x\n",
+ dev_dbg(cs43130->dev, "dai_mode = 0x%x\n",
cs43130->dais[codec_dai->id].dai_mode);
return 0;
@@ -1540,7 +1609,7 @@ static int cs43130_set_sysclk(struct snd_soc_dai *codec_dai,
struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component);
cs43130->dais[codec_dai->id].sclk = freq;
- dev_dbg(component->dev, "dai_id = %d, sclk = %u\n", codec_dai->id,
+ dev_dbg(cs43130->dev, "dai_id = %d, sclk = %u\n", codec_dai->id,
cs43130->dais[codec_dai->id].sclk);
return 0;
@@ -1630,7 +1699,7 @@ static int cs43130_component_set_sysclk(struct snd_soc_component *component,
{
struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component);
- dev_dbg(component->dev, "clk_id = %d, source = %d, freq = %d, dir = %d\n",
+ dev_dbg(cs43130->dev, "clk_id = %d, source = %d, freq = %d, dir = %d\n",
clk_id, source, freq, dir);
switch (freq) {
@@ -1639,14 +1708,14 @@ static int cs43130_component_set_sysclk(struct snd_soc_component *component,
cs43130->mclk = freq;
break;
default:
- dev_err(component->dev, "Invalid MCLK INT freq: %u\n", freq);
+ dev_err(cs43130->dev, "Invalid MCLK INT freq: %u\n", freq);
return -EINVAL;
}
if (source == CS43130_MCLK_SRC_EXT) {
cs43130->pll_bypass = true;
} else {
- dev_err(component->dev, "Invalid MCLK source\n");
+ dev_err(cs43130->dev, "Invalid MCLK source\n");
return -EINVAL;
}
@@ -1933,7 +2002,6 @@ static int cs43130_update_hpload(unsigned int msk, int ac_idx,
unsigned int reg;
u32 addr;
u16 impedance;
- struct snd_soc_component *component = cs43130->component;
switch (msk) {
case CS43130_HPLOAD_DC_INT:
@@ -1963,7 +2031,7 @@ static int cs43130_update_hpload(unsigned int msk, int ac_idx,
else
cs43130->hpload_dc[HP_RIGHT] = impedance;
- dev_dbg(component->dev, "HP DC impedance (Ch %u): %u\n", !left_ch,
+ dev_dbg(cs43130->dev, "HP DC impedance (Ch %u): %u\n", !left_ch,
impedance);
} else {
if (left_ch)
@@ -1971,7 +2039,7 @@ static int cs43130_update_hpload(unsigned int msk, int ac_idx,
else
cs43130->hpload_ac[ac_idx][HP_RIGHT] = impedance;
- dev_dbg(component->dev, "HP AC (%u Hz) impedance (Ch %u): %u\n",
+ dev_dbg(cs43130->dev, "HP AC (%u Hz) impedance (Ch %u): %u\n",
cs43130->ac_freq[ac_idx], !left_ch, impedance);
}
@@ -1985,7 +2053,6 @@ static int cs43130_hpload_proc(struct cs43130_private *cs43130,
int ret;
unsigned int msk;
u16 ac_reg_val;
- struct snd_soc_component *component = cs43130->component;
reinit_completion(&cs43130->hpload_evt);
@@ -2008,17 +2075,17 @@ static int cs43130_hpload_proc(struct cs43130_private *cs43130,
msecs_to_jiffies(1000));
regmap_read(cs43130->regmap, CS43130_INT_MASK_4, &msk);
if (!ret) {
- dev_err(component->dev, "Timeout waiting for HPLOAD interrupt\n");
- return -1;
+ dev_err(cs43130->dev, "Timeout waiting for HPLOAD interrupt\n");
+ return -ETIMEDOUT;
}
- dev_dbg(component->dev, "HP load stat: %x, INT_MASK_4: %x\n",
+ dev_dbg(cs43130->dev, "HP load stat: %x, INT_MASK_4: %x\n",
cs43130->hpload_stat, msk);
if ((cs43130->hpload_stat & (CS43130_HPLOAD_NO_DC_INT |
CS43130_HPLOAD_UNPLUG_INT |
CS43130_HPLOAD_OOR_INT)) ||
!(cs43130->hpload_stat & rslt_msk)) {
- dev_dbg(component->dev, "HP load measure failed\n");
+ dev_dbg(cs43130->dev, "HP load measure failed\n");
return -1;
}
@@ -2129,9 +2196,9 @@ static void cs43130_imp_meas(struct work_struct *wk)
snd_soc_jack_report(&cs43130->jack, CS43130_JACK_HEADPHONE,
CS43130_JACK_MASK);
- dev_dbg(component->dev, "Set HP output control. DC threshold\n");
+ dev_dbg(cs43130->dev, "Set HP output control. DC threshold\n");
for (i = 0; i < CS43130_DC_THRESHOLD; i++)
- dev_dbg(component->dev, "DC threshold[%d]: %u.\n", i,
+ dev_dbg(cs43130->dev, "DC threshold[%d]: %u.\n", i,
cs43130->dc_threshold[i]);
cs43130_set_hv(cs43130->regmap, cs43130->hpload_dc[HP_LEFT],
@@ -2165,7 +2232,6 @@ exit:
static irqreturn_t cs43130_irq_thread(int irq, void *data)
{
struct cs43130_private *cs43130 = (struct cs43130_private *)data;
- struct snd_soc_component *component = cs43130->component;
unsigned int stickies[CS43130_NUM_INT];
unsigned int irq_occurrence = 0;
unsigned int masks[CS43130_NUM_INT];
@@ -2183,8 +2249,6 @@ static irqreturn_t cs43130_irq_thread(int irq, void *data)
for (j = 0; j < 8; j++)
irq_occurrence += (stickies[i] >> j) & 1;
}
- dev_dbg(component->dev, "number of interrupts occurred (%u)\n",
- irq_occurrence);
if (!irq_occurrence)
return IRQ_NONE;
@@ -2201,7 +2265,7 @@ static irqreturn_t cs43130_irq_thread(int irq, void *data)
if (stickies[3] & CS43130_HPLOAD_NO_DC_INT) {
cs43130->hpload_stat = stickies[3];
- dev_err(component->dev,
+ dev_err(cs43130->dev,
"DC load has not completed before AC load (%x)\n",
cs43130->hpload_stat);
complete(&cs43130->hpload_evt);
@@ -2210,7 +2274,7 @@ static irqreturn_t cs43130_irq_thread(int irq, void *data)
if (stickies[3] & CS43130_HPLOAD_UNPLUG_INT) {
cs43130->hpload_stat = stickies[3];
- dev_err(component->dev, "HP unplugged during measurement (%x)\n",
+ dev_err(cs43130->dev, "HP unplugged during measurement (%x)\n",
cs43130->hpload_stat);
complete(&cs43130->hpload_evt);
return IRQ_HANDLED;
@@ -2218,7 +2282,7 @@ static irqreturn_t cs43130_irq_thread(int irq, void *data)
if (stickies[3] & CS43130_HPLOAD_OOR_INT) {
cs43130->hpload_stat = stickies[3];
- dev_err(component->dev, "HP load out of range (%x)\n",
+ dev_err(cs43130->dev, "HP load out of range (%x)\n",
cs43130->hpload_stat);
complete(&cs43130->hpload_evt);
return IRQ_HANDLED;
@@ -2226,7 +2290,7 @@ static irqreturn_t cs43130_irq_thread(int irq, void *data)
if (stickies[3] & CS43130_HPLOAD_AC_INT) {
cs43130->hpload_stat = stickies[3];
- dev_dbg(component->dev, "HP AC load measurement done (%x)\n",
+ dev_dbg(cs43130->dev, "HP AC load measurement done (%x)\n",
cs43130->hpload_stat);
complete(&cs43130->hpload_evt);
return IRQ_HANDLED;
@@ -2234,7 +2298,7 @@ static irqreturn_t cs43130_irq_thread(int irq, void *data)
if (stickies[3] & CS43130_HPLOAD_DC_INT) {
cs43130->hpload_stat = stickies[3];
- dev_dbg(component->dev, "HP DC load measurement done (%x)\n",
+ dev_dbg(cs43130->dev, "HP DC load measurement done (%x)\n",
cs43130->hpload_stat);
complete(&cs43130->hpload_evt);
return IRQ_HANDLED;
@@ -2242,7 +2306,7 @@ static irqreturn_t cs43130_irq_thread(int irq, void *data)
if (stickies[3] & CS43130_HPLOAD_ON_INT) {
cs43130->hpload_stat = stickies[3];
- dev_dbg(component->dev, "HP load state machine on done (%x)\n",
+ dev_dbg(cs43130->dev, "HP load state machine on done (%x)\n",
cs43130->hpload_stat);
complete(&cs43130->hpload_evt);
return IRQ_HANDLED;
@@ -2250,19 +2314,19 @@ static irqreturn_t cs43130_irq_thread(int irq, void *data)
if (stickies[3] & CS43130_HPLOAD_OFF_INT) {
cs43130->hpload_stat = stickies[3];
- dev_dbg(component->dev, "HP load state machine off done (%x)\n",
+ dev_dbg(cs43130->dev, "HP load state machine off done (%x)\n",
cs43130->hpload_stat);
complete(&cs43130->hpload_evt);
return IRQ_HANDLED;
}
if (stickies[0] & CS43130_XTAL_ERR_INT) {
- dev_err(component->dev, "Crystal err: clock is not running\n");
+ dev_err(cs43130->dev, "Crystal err: clock is not running\n");
return IRQ_HANDLED;
}
if (stickies[0] & CS43130_HP_UNPLUG_INT) {
- dev_dbg(component->dev, "HP unplugged\n");
+ dev_dbg(cs43130->dev, "HP unplugged\n");
cs43130->hpload_done = false;
snd_soc_jack_report(&cs43130->jack, 0, CS43130_JACK_MASK);
return IRQ_HANDLED;
@@ -2271,7 +2335,7 @@ static irqreturn_t cs43130_irq_thread(int irq, void *data)
if (stickies[0] & CS43130_HP_PLUG_INT) {
if (cs43130->dc_meas && !cs43130->hpload_done &&
!work_busy(&cs43130->work)) {
- dev_dbg(component->dev, "HP load queue work\n");
+ dev_dbg(cs43130->dev, "HP load queue work\n");
queue_work(cs43130->wq, &cs43130->work);
}
@@ -2303,19 +2367,19 @@ static int cs43130_probe(struct snd_soc_component *component)
ret = snd_soc_card_jack_new(card, "Headphone", CS43130_JACK_MASK,
&cs43130->jack);
if (ret < 0) {
- dev_err(component->dev, "Cannot create jack\n");
+ dev_err(cs43130->dev, "Cannot create jack\n");
return ret;
}
cs43130->hpload_done = false;
if (cs43130->dc_meas) {
- ret = sysfs_create_groups(&component->dev->kobj, hpload_groups);
+ ret = sysfs_create_groups(&cs43130->dev->kobj, hpload_groups);
if (ret)
return ret;
cs43130->wq = create_singlethread_workqueue("cs43130_hp");
if (!cs43130->wq) {
- sysfs_remove_groups(&component->dev->kobj, hpload_groups);
+ sysfs_remove_groups(&cs43130->dev->kobj, hpload_groups);
return -ENOMEM;
}
INIT_WORK(&cs43130->work, cs43130_imp_meas);
@@ -2367,14 +2431,12 @@ static const u16 cs43130_dc_threshold[CS43130_DC_THRESHOLD] = {
120,
};
-static int cs43130_handle_device_data(struct i2c_client *i2c_client,
- struct cs43130_private *cs43130)
+static int cs43130_handle_device_data(struct cs43130_private *cs43130)
{
- struct device_node *np = i2c_client->dev.of_node;
unsigned int val;
int i;
- if (of_property_read_u32(np, "cirrus,xtal-ibias", &val) < 0) {
+ if (device_property_read_u32(cs43130->dev, "cirrus,xtal-ibias", &val) < 0) {
/* Crystal is unused. System clock is used for external MCLK */
cs43130->xtal_ibias = CS43130_XTAL_UNUSED;
return 0;
@@ -2391,23 +2453,23 @@ static int cs43130_handle_device_data(struct i2c_client *i2c_client,
cs43130->xtal_ibias = CS43130_XTAL_IBIAS_15UA;
break;
default:
- dev_err(&i2c_client->dev,
+ dev_err(cs43130->dev,
"Invalid cirrus,xtal-ibias value: %d\n", val);
return -EINVAL;
}
- cs43130->dc_meas = of_property_read_bool(np, "cirrus,dc-measure");
- cs43130->ac_meas = of_property_read_bool(np, "cirrus,ac-measure");
+ cs43130->dc_meas = device_property_read_bool(cs43130->dev, "cirrus,dc-measure");
+ cs43130->ac_meas = device_property_read_bool(cs43130->dev, "cirrus,ac-measure");
- if (of_property_read_u16_array(np, "cirrus,ac-freq", cs43130->ac_freq,
- CS43130_AC_FREQ) < 0) {
+ if (!device_property_read_u16_array(cs43130->dev, "cirrus,ac-freq", cs43130->ac_freq,
+ CS43130_AC_FREQ)) {
for (i = 0; i < CS43130_AC_FREQ; i++)
cs43130->ac_freq[i] = cs43130_ac_freq[i];
}
- if (of_property_read_u16_array(np, "cirrus,dc-threshold",
+ if (!device_property_read_u16_array(cs43130->dev, "cirrus,dc-threshold",
cs43130->dc_threshold,
- CS43130_DC_THRESHOLD) < 0) {
+ CS43130_DC_THRESHOLD)) {
for (i = 0; i < CS43130_DC_THRESHOLD; i++)
cs43130->dc_threshold[i] = cs43130_dc_threshold[i];
}
@@ -2426,6 +2488,8 @@ static int cs43130_i2c_probe(struct i2c_client *client)
if (!cs43130)
return -ENOMEM;
+ cs43130->dev = &client->dev;
+
i2c_set_clientdata(client, cs43130);
cs43130->regmap = devm_regmap_init_i2c(client, &cs43130_regmap);
@@ -2434,29 +2498,30 @@ static int cs43130_i2c_probe(struct i2c_client *client)
return ret;
}
- if (client->dev.of_node) {
- ret = cs43130_handle_device_data(client, cs43130);
+ if (dev_fwnode(cs43130->dev)) {
+ ret = cs43130_handle_device_data(cs43130);
if (ret != 0)
return ret;
}
+
for (i = 0; i < ARRAY_SIZE(cs43130->supplies); i++)
cs43130->supplies[i].supply = cs43130_supply_names[i];
- ret = devm_regulator_bulk_get(&client->dev,
+ ret = devm_regulator_bulk_get(cs43130->dev,
ARRAY_SIZE(cs43130->supplies),
cs43130->supplies);
if (ret != 0) {
- dev_err(&client->dev, "Failed to request supplies: %d\n", ret);
+ dev_err(cs43130->dev, "Failed to request supplies: %d\n", ret);
return ret;
}
ret = regulator_bulk_enable(ARRAY_SIZE(cs43130->supplies),
cs43130->supplies);
if (ret != 0) {
- dev_err(&client->dev, "Failed to enable supplies: %d\n", ret);
+ dev_err(cs43130->dev, "Failed to enable supplies: %d\n", ret);
return ret;
}
- cs43130->reset_gpio = devm_gpiod_get_optional(&client->dev,
+ cs43130->reset_gpio = devm_gpiod_get_optional(cs43130->dev,
"reset", GPIOD_OUT_LOW);
if (IS_ERR(cs43130->reset_gpio)) {
ret = PTR_ERR(cs43130->reset_gpio);
@@ -2470,7 +2535,7 @@ static int cs43130_i2c_probe(struct i2c_client *client)
devid = cirrus_read_device_id(cs43130->regmap, CS43130_DEVID_AB);
if (devid < 0) {
ret = devid;
- dev_err(&client->dev, "Failed to read device ID: %d\n", ret);
+ dev_err(cs43130->dev, "Failed to read device ID: %d\n", ret);
goto err;
}
@@ -2481,7 +2546,7 @@ static int cs43130_i2c_probe(struct i2c_client *client)
case CS43198_CHIP_ID:
break;
default:
- dev_err(&client->dev,
+ dev_err(cs43130->dev,
"CS43130 Device ID %X. Expected ID %X, %X, %X or %X\n",
devid, CS43130_CHIP_ID, CS4399_CHIP_ID,
CS43131_CHIP_ID, CS43198_CHIP_ID);
@@ -2492,11 +2557,11 @@ static int cs43130_i2c_probe(struct i2c_client *client)
cs43130->dev_id = devid;
ret = regmap_read(cs43130->regmap, CS43130_REV_ID, &reg);
if (ret < 0) {
- dev_err(&client->dev, "Get Revision ID failed\n");
+ dev_err(cs43130->dev, "Get Revision ID failed\n");
goto err;
}
- dev_info(&client->dev,
+ dev_info(cs43130->dev,
"Cirrus Logic CS43130 (%x), Revision: %02X\n", devid,
reg & 0xFF);
@@ -2506,21 +2571,27 @@ static int cs43130_i2c_probe(struct i2c_client *client)
init_completion(&cs43130->pll_rdy);
init_completion(&cs43130->hpload_evt);
- ret = devm_request_threaded_irq(&client->dev, client->irq,
- NULL, cs43130_irq_thread,
- IRQF_ONESHOT | IRQF_TRIGGER_LOW,
- "cs43130", cs43130);
- if (ret != 0) {
- dev_err(&client->dev, "Failed to request IRQ: %d\n", ret);
- goto err;
+ if (!client->irq) {
+ dev_dbg(cs43130->dev, "IRQ not found, will poll instead\n");
+ cs43130->has_irq_line = 0;
+ } else {
+ ret = devm_request_threaded_irq(cs43130->dev, client->irq,
+ NULL, cs43130_irq_thread,
+ IRQF_ONESHOT | IRQF_TRIGGER_LOW,
+ "cs43130", cs43130);
+ if (ret != 0) {
+ dev_err(cs43130->dev, "Failed to request IRQ: %d\n", ret);
+ goto err;
+ }
+ cs43130->has_irq_line = 1;
}
cs43130->mclk_int_src = CS43130_MCLK_SRC_RCO;
- pm_runtime_set_autosuspend_delay(&client->dev, 100);
- pm_runtime_use_autosuspend(&client->dev);
- pm_runtime_set_active(&client->dev);
- pm_runtime_enable(&client->dev);
+ pm_runtime_set_autosuspend_delay(cs43130->dev, 100);
+ pm_runtime_use_autosuspend(cs43130->dev);
+ pm_runtime_set_active(cs43130->dev);
+ pm_runtime_enable(cs43130->dev);
switch (cs43130->dev_id) {
case CS43130_CHIP_ID:
@@ -2556,11 +2627,11 @@ static int cs43130_i2c_probe(struct i2c_client *client)
break;
}
- ret = devm_snd_soc_register_component(&client->dev,
+ ret = devm_snd_soc_register_component(cs43130->dev,
&soc_component_dev_cs43130,
cs43130_dai, ARRAY_SIZE(cs43130_dai));
if (ret < 0) {
- dev_err(&client->dev,
+ dev_err(cs43130->dev,
"snd_soc_register_component failed with ret = %d\n", ret);
goto err;
}
@@ -2598,15 +2669,15 @@ static void cs43130_i2c_remove(struct i2c_client *client)
cancel_work_sync(&cs43130->work);
flush_workqueue(cs43130->wq);
- device_remove_file(&client->dev, &dev_attr_hpload_dc_l);
- device_remove_file(&client->dev, &dev_attr_hpload_dc_r);
- device_remove_file(&client->dev, &dev_attr_hpload_ac_l);
- device_remove_file(&client->dev, &dev_attr_hpload_ac_r);
+ device_remove_file(cs43130->dev, &dev_attr_hpload_dc_l);
+ device_remove_file(cs43130->dev, &dev_attr_hpload_dc_r);
+ device_remove_file(cs43130->dev, &dev_attr_hpload_ac_l);
+ device_remove_file(cs43130->dev, &dev_attr_hpload_ac_r);
}
gpiod_set_value_cansleep(cs43130->reset_gpio, 0);
- pm_runtime_disable(&client->dev);
+ pm_runtime_disable(cs43130->dev);
regulator_bulk_disable(CS43130_NUM_SUPPLIES, cs43130->supplies);
}
@@ -2669,6 +2740,7 @@ static const struct dev_pm_ops cs43130_runtime_pm = {
NULL)
};
+#if IS_ENABLED(CONFIG_OF)
static const struct of_device_id cs43130_of_match[] = {
{.compatible = "cirrus,cs43130",},
{.compatible = "cirrus,cs4399",},
@@ -2678,6 +2750,17 @@ static const struct of_device_id cs43130_of_match[] = {
};
MODULE_DEVICE_TABLE(of, cs43130_of_match);
+#endif
+
+#if IS_ENABLED(CONFIG_ACPI)
+static const struct acpi_device_id cs43130_acpi_match[] = {
+ { "CSC4399", 0 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(acpi, cs43130_acpi_match);
+#endif
+
static const struct i2c_device_id cs43130_i2c_id[] = {
{"cs43130", 0},
@@ -2691,9 +2774,10 @@ MODULE_DEVICE_TABLE(i2c, cs43130_i2c_id);
static struct i2c_driver cs43130_i2c_driver = {
.driver = {
- .name = "cs43130",
- .of_match_table = cs43130_of_match,
- .pm = &cs43130_runtime_pm,
+ .name = "cs43130",
+ .of_match_table = of_match_ptr(cs43130_of_match),
+ .acpi_match_table = ACPI_PTR(cs43130_acpi_match),
+ .pm = &cs43130_runtime_pm,
},
.id_table = cs43130_i2c_id,
.probe = cs43130_i2c_probe,
diff --git a/sound/soc/codecs/cs43130.h b/sound/soc/codecs/cs43130.h
index 90e8895275e7..dbdb5b262f1b 100644
--- a/sound/soc/codecs/cs43130.h
+++ b/sound/soc/codecs/cs43130.h
@@ -497,15 +497,18 @@ struct cs43130_dai {
unsigned int sclk;
unsigned int dai_format;
unsigned int dai_mode;
+ unsigned int dai_invert;
};
struct cs43130_private {
+ struct device *dev;
struct snd_soc_component *component;
struct regmap *regmap;
struct regulator_bulk_data supplies[CS43130_NUM_SUPPLIES];
struct gpio_desc *reset_gpio;
unsigned int dev_id; /* codec device ID */
int xtal_ibias;
+ bool has_irq_line;
/* shared by both DAIs */
struct mutex clk_mutex;
diff --git a/sound/soc/codecs/cs4349.c b/sound/soc/codecs/cs4349.c
index 9083228495d4..ca8f21aa4837 100644
--- a/sound/soc/codecs/cs4349.c
+++ b/sound/soc/codecs/cs4349.c
@@ -13,7 +13,6 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
-#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c
index 6c263086c44d..cbcd02ec6ba4 100644
--- a/sound/soc/codecs/es8326.c
+++ b/sound/soc/codecs/es8326.c
@@ -45,6 +45,82 @@ struct es8326_priv {
int jack_remove_retry;
};
+static int es8326_crosstalk1_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+ unsigned int crosstalk_h, crosstalk_l;
+ unsigned int crosstalk;
+
+ regmap_read(es8326->regmap, ES8326_DAC_RAMPRATE, &crosstalk_h);
+ regmap_read(es8326->regmap, ES8326_DAC_CROSSTALK, &crosstalk_l);
+ crosstalk_h &= 0x20;
+ crosstalk_l &= 0xf0;
+ crosstalk = crosstalk_h >> 1 | crosstalk_l >> 4;
+ ucontrol->value.integer.value[0] = crosstalk;
+
+ return 0;
+}
+
+static int es8326_crosstalk1_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+ unsigned int crosstalk_h, crosstalk_l;
+ unsigned int crosstalk;
+
+ crosstalk = ucontrol->value.integer.value[0];
+ regmap_read(es8326->regmap, ES8326_DAC_CROSSTALK, &crosstalk_l);
+ crosstalk_h = (crosstalk & 0x10) << 1;
+ crosstalk_l &= 0x0f;
+ crosstalk_l |= (crosstalk & 0x0f) << 4;
+ regmap_update_bits(es8326->regmap, ES8326_DAC_RAMPRATE,
+ 0x20, crosstalk_h);
+ regmap_write(es8326->regmap, ES8326_DAC_CROSSTALK, crosstalk_l);
+
+ return 0;
+}
+
+static int es8326_crosstalk2_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+ unsigned int crosstalk_h, crosstalk_l;
+ unsigned int crosstalk;
+
+ regmap_read(es8326->regmap, ES8326_DAC_RAMPRATE, &crosstalk_h);
+ regmap_read(es8326->regmap, ES8326_DAC_CROSSTALK, &crosstalk_l);
+ crosstalk_h &= 0x10;
+ crosstalk_l &= 0x0f;
+ crosstalk = crosstalk_h | crosstalk_l;
+ ucontrol->value.integer.value[0] = crosstalk;
+
+ return 0;
+}
+
+static int es8326_crosstalk2_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+ unsigned int crosstalk_h, crosstalk_l;
+ unsigned int crosstalk;
+
+ crosstalk = ucontrol->value.integer.value[0];
+ regmap_read(es8326->regmap, ES8326_DAC_CROSSTALK, &crosstalk_l);
+ crosstalk_h = crosstalk & 0x10;
+ crosstalk_l &= 0xf0;
+ crosstalk_l |= crosstalk & 0x0f;
+ regmap_update_bits(es8326->regmap, ES8326_DAC_RAMPRATE,
+ 0x10, crosstalk_h);
+ regmap_write(es8326->regmap, ES8326_DAC_CROSSTALK, crosstalk_l);
+
+ return 0;
+}
+
static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(dac_vol_tlv, -9550, 50, 0);
static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(adc_vol_tlv, -9550, 50, 0);
static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(adc_analog_pga_tlv, 0, 300, 0);
@@ -102,6 +178,10 @@ static const struct snd_kcontrol_new es8326_snd_controls[] = {
SOC_SINGLE_TLV("ALC Capture Target Level", ES8326_ALC_LEVEL,
0, 0x0f, 0, drc_target_tlv),
+ SOC_SINGLE_EXT("CROSSTALK1", SND_SOC_NOPM, 0, 31, 0,
+ es8326_crosstalk1_get, es8326_crosstalk1_set),
+ SOC_SINGLE_EXT("CROSSTALK2", SND_SOC_NOPM, 0, 31, 0,
+ es8326_crosstalk2_get, es8326_crosstalk2_set),
};
static const struct snd_soc_dapm_widget es8326_dapm_widgets[] = {
@@ -117,12 +197,6 @@ static const struct snd_soc_dapm_widget es8326_dapm_widgets[] = {
SND_SOC_DAPM_AIF_OUT("I2S OUT", "I2S1 Capture", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("I2S IN", "I2S1 Playback", 0, SND_SOC_NOPM, 0, 0),
- /* ADC Digital Mute */
- SND_SOC_DAPM_PGA("ADC L1", ES8326_ADC_MUTE, 0, 1, NULL, 0),
- SND_SOC_DAPM_PGA("ADC R1", ES8326_ADC_MUTE, 1, 1, NULL, 0),
- SND_SOC_DAPM_PGA("ADC L2", ES8326_ADC_MUTE, 2, 1, NULL, 0),
- SND_SOC_DAPM_PGA("ADC R2", ES8326_ADC_MUTE, 3, 1, NULL, 0),
-
/* 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),
@@ -132,20 +206,20 @@ static const struct snd_soc_dapm_widget es8326_dapm_widgets[] = {
SND_SOC_DAPM_PGA("LHPMIX", ES8326_DAC2HPMIX, 7, 0, NULL, 0),
SND_SOC_DAPM_PGA("RHPMIX", ES8326_DAC2HPMIX, 3, 0, NULL, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_supply, "HPOR Supply", ES8326_HP_CAL,
+ 4, 7, 0, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_supply, "HPOL Supply", ES8326_HP_CAL,
+ 0, 7, 0, 0),
+
SND_SOC_DAPM_OUTPUT("HPOL"),
SND_SOC_DAPM_OUTPUT("HPOR"),
};
static const struct snd_soc_dapm_route es8326_dapm_routes[] = {
- {"ADC L1", NULL, "MIC1"},
- {"ADC R1", NULL, "MIC2"},
- {"ADC L2", NULL, "MIC3"},
- {"ADC R2", NULL, "MIC4"},
-
- {"ADC L", NULL, "ADC L1"},
- {"ADC R", NULL, "ADC R1"},
- {"ADC L", NULL, "ADC L2"},
- {"ADC R", NULL, "ADC R2"},
+ {"ADC L", NULL, "MIC1"},
+ {"ADC R", NULL, "MIC2"},
+ {"ADC L", NULL, "MIC3"},
+ {"ADC R", NULL, "MIC4"},
{"I2S OUT", NULL, "ADC L"},
{"I2S OUT", NULL, "ADC R"},
@@ -156,6 +230,9 @@ static const struct snd_soc_dapm_route es8326_dapm_routes[] = {
{"LHPMIX", NULL, "Left DAC"},
{"RHPMIX", NULL, "Right DAC"},
+ {"HPOR", NULL, "HPOR Supply"},
+ {"HPOL", NULL, "HPOL Supply"},
+
{"HPOL", NULL, "LHPMIX"},
{"HPOR", NULL, "RHPMIX"},
};
@@ -198,77 +275,108 @@ struct _coeff_div {
/* codec hifi mclk clock divider coefficients */
/* {ratio, LRCK, MCLK, REG04, REG05, REG06, REG07, REG08, REG09, REG10, REG11} */
-static const struct _coeff_div coeff_div[] = {
- {32, 8000, 256000, 0x60, 0x00, 0x0F, 0x75, 0x0A, 0x1B, 0x1F, 0x7F},
- {32, 16000, 512000, 0x20, 0x00, 0x0D, 0x75, 0x0A, 0x1B, 0x1F, 0x3F},
- {32, 44100, 1411200, 0x00, 0x00, 0x13, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F},
- {32, 48000, 1536000, 0x00, 0x00, 0x13, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F},
- {36, 8000, 288000, 0x20, 0x00, 0x0D, 0x75, 0x0A, 0x1B, 0x23, 0x47},
- {36, 16000, 576000, 0x20, 0x00, 0x0D, 0x75, 0x0A, 0x1B, 0x23, 0x47},
- {48, 8000, 384000, 0x60, 0x02, 0x1F, 0x75, 0x0A, 0x1B, 0x1F, 0x7F},
- {48, 16000, 768000, 0x20, 0x02, 0x0F, 0x75, 0x0A, 0x1B, 0x1F, 0x3F},
- {48, 48000, 2304000, 0x00, 0x02, 0x0D, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F},
- {64, 8000, 512000, 0x60, 0x00, 0x0D, 0x75, 0x0A, 0x1B, 0x1F, 0x7F},
- {64, 16000, 1024000, 0x20, 0x00, 0x05, 0x75, 0x0A, 0x1B, 0x1F, 0x3F},
-
- {64, 44100, 2822400, 0x00, 0x00, 0x11, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F},
- {64, 48000, 3072000, 0x00, 0x00, 0x11, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F},
- {72, 8000, 576000, 0x20, 0x00, 0x13, 0x35, 0x0A, 0x1B, 0x23, 0x47},
- {72, 16000, 1152000, 0x20, 0x00, 0x05, 0x75, 0x0A, 0x1B, 0x23, 0x47},
- {96, 8000, 768000, 0x60, 0x02, 0x1D, 0x75, 0x0A, 0x1B, 0x1F, 0x7F},
- {96, 16000, 1536000, 0x20, 0x02, 0x0D, 0x75, 0x0A, 0x1B, 0x1F, 0x3F},
- {100, 48000, 4800000, 0x04, 0x04, 0x3F, 0x6D, 0x38, 0x08, 0x4f, 0x1f},
- {125, 48000, 6000000, 0x04, 0x04, 0x1F, 0x2D, 0x0A, 0x0A, 0x27, 0x27},
- {128, 8000, 1024000, 0x60, 0x00, 0x13, 0x35, 0x0A, 0x1B, 0x1F, 0x7F},
- {128, 16000, 2048000, 0x20, 0x00, 0x11, 0x35, 0x0A, 0x1B, 0x1F, 0x3F},
-
- {128, 44100, 5644800, 0x00, 0x00, 0x01, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F},
- {128, 48000, 6144000, 0x00, 0x00, 0x01, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F},
- {144, 8000, 1152000, 0x20, 0x00, 0x03, 0x35, 0x0A, 0x1B, 0x23, 0x47},
- {144, 16000, 2304000, 0x20, 0x00, 0x11, 0x35, 0x0A, 0x1B, 0x23, 0x47},
- {192, 8000, 1536000, 0x60, 0x02, 0x0D, 0x75, 0x0A, 0x1B, 0x1F, 0x7F},
- {192, 16000, 3072000, 0x20, 0x02, 0x05, 0x75, 0x0A, 0x1B, 0x1F, 0x3F},
- {200, 48000, 9600000, 0x04, 0x04, 0x0F, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F},
- {250, 48000, 12000000, 0x04, 0x04, 0x0F, 0x2D, 0x0A, 0x0A, 0x27, 0x27},
- {256, 8000, 2048000, 0x60, 0x00, 0x11, 0x35, 0x0A, 0x1B, 0x1F, 0x7F},
- {256, 16000, 4096000, 0x20, 0x00, 0x01, 0x35, 0x0A, 0x1B, 0x1F, 0x3F},
-
- {256, 44100, 11289600, 0x00, 0x00, 0x10, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F},
- {256, 48000, 12288000, 0x00, 0x00, 0x30, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F},
- {288, 8000, 2304000, 0x20, 0x00, 0x01, 0x35, 0x0A, 0x1B, 0x23, 0x47},
- {384, 8000, 3072000, 0x60, 0x02, 0x05, 0x75, 0x0A, 0x1B, 0x1F, 0x7F},
- {384, 16000, 6144000, 0x20, 0x02, 0x03, 0x35, 0x0A, 0x1B, 0x1F, 0x3F},
- {384, 48000, 18432000, 0x00, 0x02, 0x01, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F},
- {400, 48000, 19200000, 0x09, 0x04, 0x0f, 0x6d, 0x3a, 0x0A, 0x4F, 0x1F},
- {500, 48000, 24000000, 0x18, 0x04, 0x1F, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F},
- {512, 8000, 4096000, 0x60, 0x00, 0x01, 0x35, 0x0A, 0x1B, 0x1F, 0x7F},
- {512, 16000, 8192000, 0x20, 0x00, 0x10, 0x35, 0x0A, 0x1B, 0x1F, 0x3F},
-
- {512, 44100, 22579200, 0x00, 0x00, 0x00, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F},
- {512, 48000, 24576000, 0x00, 0x00, 0x00, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F},
- {768, 8000, 6144000, 0x60, 0x02, 0x11, 0x35, 0x0A, 0x1B, 0x1F, 0x7F},
- {768, 16000, 12288000, 0x20, 0x02, 0x01, 0x35, 0x0A, 0x1B, 0x1F, 0x3F},
- {800, 48000, 38400000, 0x00, 0x18, 0x13, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F},
- {1024, 8000, 8192000, 0x60, 0x00, 0x10, 0x35, 0x0A, 0x1B, 0x1F, 0x7F},
+static const struct _coeff_div coeff_div_v0[] = {
+ {64, 8000, 512000, 0x60, 0x01, 0x0F, 0x75, 0x0A, 0x1B, 0x1F, 0x7F},
+ {64, 16000, 1024000, 0x20, 0x00, 0x33, 0x35, 0x0A, 0x1B, 0x1F, 0x3F},
+ {64, 44100, 2822400, 0xE0, 0x00, 0x03, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F},
+ {64, 48000, 3072000, 0xE0, 0x00, 0x03, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F},
+ {128, 8000, 1024000, 0x60, 0x00, 0x33, 0x35, 0x0A, 0x1B, 0x1F, 0x7F},
+ {128, 16000, 2048000, 0x20, 0x00, 0x03, 0x35, 0x0A, 0x1B, 0x1F, 0x3F},
+ {128, 44100, 5644800, 0xE0, 0x01, 0x03, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F},
+ {128, 48000, 6144000, 0xE0, 0x01, 0x03, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F},
+
+ {192, 32000, 6144000, 0xE0, 0x02, 0x03, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F},
+ {256, 8000, 2048000, 0x60, 0x00, 0x03, 0x35, 0x0A, 0x1B, 0x1F, 0x7F},
+ {256, 16000, 4096000, 0x20, 0x01, 0x03, 0x35, 0x0A, 0x1B, 0x1F, 0x3F},
+ {256, 44100, 11289600, 0xE0, 0x00, 0x30, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F},
+ {256, 48000, 12288000, 0xE0, 0x00, 0x30, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F},
+ {384, 32000, 12288000, 0xE0, 0x05, 0x03, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F},
+ {400, 48000, 19200000, 0xE9, 0x04, 0x0F, 0x6d, 0x4A, 0x0A, 0x1F, 0x1F},
+
+ {500, 48000, 24000000, 0xF8, 0x04, 0x3F, 0x6D, 0x4A, 0x0A, 0x1F, 0x1F},
+ {512, 8000, 4096000, 0x60, 0x01, 0x03, 0x35, 0x0A, 0x1B, 0x1F, 0x7F},
+ {512, 16000, 8192000, 0x20, 0x00, 0x30, 0x35, 0x0A, 0x1B, 0x1F, 0x3F},
+ {512, 44100, 22579200, 0xE0, 0x00, 0x00, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F},
+ {512, 48000, 24576000, 0xE0, 0x00, 0x00, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F},
+ {768, 32000, 24576000, 0xE0, 0x02, 0x30, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F},
+ {1024, 8000, 8192000, 0x60, 0x00, 0x30, 0x35, 0x0A, 0x1B, 0x1F, 0x7F},
{1024, 16000, 16384000, 0x20, 0x00, 0x00, 0x35, 0x0A, 0x1B, 0x1F, 0x3F},
- {1152, 16000, 18432000, 0x20, 0x08, 0x11, 0x35, 0x0A, 0x1B, 0x1F, 0x3F},
- {1536, 8000, 12288000, 0x60, 0x02, 0x01, 0x35, 0x0A, 0x1B, 0x1F, 0x7F},
-
- {1536, 16000, 24576000, 0x20, 0x02, 0x10, 0x35, 0x0A, 0x1B, 0x1F, 0x3F},
- {1625, 8000, 13000000, 0x0C, 0x18, 0x1F, 0x2D, 0x0A, 0x0A, 0x27, 0x27},
- {1625, 16000, 26000000, 0x0C, 0x18, 0x1F, 0x2D, 0x0A, 0x0A, 0x27, 0x27},
- {2048, 8000, 16384000, 0x60, 0x00, 0x00, 0x35, 0x0A, 0x1B, 0x1F, 0x7F},
- {2304, 8000, 18432000, 0x40, 0x02, 0x10, 0x35, 0x0A, 0x1B, 0x1F, 0x5F},
- {3072, 8000, 24576000, 0x60, 0x02, 0x10, 0x35, 0x0A, 0x1B, 0x1F, 0x7F},
- {3250, 8000, 26000000, 0x0C, 0x18, 0x0F, 0x2D, 0x0A, 0x0A, 0x27, 0x27},
+};
+static const struct _coeff_div coeff_div_v3[] = {
+ {32, 8000, 256000, 0x60, 0x00, 0x0F, 0x75, 0x8A, 0x1B, 0x1F, 0x7F},
+ {32, 16000, 512000, 0x20, 0x00, 0x0D, 0x75, 0x8A, 0x1B, 0x1F, 0x3F},
+ {32, 44100, 1411200, 0x00, 0x00, 0x13, 0x2D, 0x8A, 0x0A, 0x1F, 0x1F},
+ {32, 48000, 1536000, 0x00, 0x00, 0x13, 0x2D, 0x8A, 0x0A, 0x1F, 0x1F},
+ {36, 8000, 288000, 0x20, 0x00, 0x0D, 0x75, 0x8A, 0x1B, 0x23, 0x47},
+ {36, 16000, 576000, 0x20, 0x00, 0x0D, 0x75, 0x8A, 0x1B, 0x23, 0x47},
+ {48, 8000, 384000, 0x60, 0x02, 0x1F, 0x75, 0x8A, 0x1B, 0x1F, 0x7F},
+ {48, 16000, 768000, 0x20, 0x02, 0x0F, 0x75, 0x8A, 0x1B, 0x1F, 0x3F},
+ {48, 48000, 2304000, 0x00, 0x02, 0x0D, 0x2D, 0x8A, 0x0A, 0x1F, 0x1F},
+
+ {64, 8000, 512000, 0x60, 0x00, 0x35, 0x75, 0x8A, 0x1B, 0x1F, 0x7F},
+ {64, 16000, 1024000, 0x20, 0x00, 0x05, 0x75, 0x8A, 0x1B, 0x1F, 0x3F},
+ {64, 44100, 2822400, 0xE0, 0x00, 0x31, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F},
+ {64, 48000, 3072000, 0xE0, 0x00, 0x31, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F},
+ {72, 8000, 576000, 0x20, 0x00, 0x13, 0x35, 0x8A, 0x1B, 0x23, 0x47},
+ {72, 16000, 1152000, 0x20, 0x00, 0x05, 0x75, 0x8A, 0x1B, 0x23, 0x47},
+ {96, 8000, 768000, 0x60, 0x02, 0x1D, 0x75, 0x8A, 0x1B, 0x1F, 0x7F},
+ {96, 16000, 1536000, 0x20, 0x02, 0x0D, 0x75, 0x8A, 0x1B, 0x1F, 0x3F},
+ {100, 48000, 4800000, 0x04, 0x04, 0x3F, 0x6D, 0xB8, 0x08, 0x4f, 0x1f},
+ {125, 48000, 6000000, 0x04, 0x04, 0x1F, 0x2D, 0x8A, 0x0A, 0x27, 0x27},
+
+ {128, 8000, 1024000, 0x60, 0x00, 0x05, 0x75, 0x8A, 0x1B, 0x1F, 0x7F},
+ {128, 16000, 2048000, 0x20, 0x00, 0x31, 0x35, 0x8A, 0x1B, 0x1F, 0x3F},
+ {128, 44100, 5644800, 0xE0, 0x00, 0x01, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F},
+ {128, 48000, 6144000, 0xE0, 0x00, 0x01, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F},
+ {144, 8000, 1152000, 0x20, 0x00, 0x03, 0x35, 0x8A, 0x1B, 0x23, 0x47},
+ {144, 16000, 2304000, 0x20, 0x00, 0x11, 0x35, 0x8A, 0x1B, 0x23, 0x47},
+ {192, 8000, 1536000, 0x60, 0x02, 0x0D, 0x75, 0x8A, 0x1B, 0x1F, 0x7F},
+ {192, 32000, 6144000, 0xE0, 0x02, 0x31, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F},
+ {192, 16000, 3072000, 0x20, 0x02, 0x05, 0x75, 0xCA, 0x1B, 0x1F, 0x3F},
+
+ {200, 48000, 9600000, 0x04, 0x04, 0x0F, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F},
+ {250, 48000, 12000000, 0x04, 0x04, 0x0F, 0x2D, 0xCA, 0x0A, 0x27, 0x27},
+ {256, 8000, 2048000, 0x60, 0x00, 0x31, 0x35, 0x8A, 0x1B, 0x1F, 0x7F},
+ {256, 16000, 4096000, 0x20, 0x00, 0x01, 0x35, 0x8A, 0x1B, 0x1F, 0x3F},
+ {256, 44100, 11289600, 0xE0, 0x00, 0x30, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F},
+ {256, 48000, 12288000, 0xE0, 0x00, 0x30, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F},
+ {288, 8000, 2304000, 0x20, 0x00, 0x01, 0x35, 0x8A, 0x1B, 0x23, 0x47},
+ {384, 8000, 3072000, 0x60, 0x02, 0x05, 0x75, 0x8A, 0x1B, 0x1F, 0x7F},
+ {384, 16000, 6144000, 0x20, 0x02, 0x03, 0x35, 0x8A, 0x1B, 0x1F, 0x3F},
+ {384, 32000, 12288000, 0xE0, 0x02, 0x01, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F},
+ {384, 48000, 18432000, 0x00, 0x02, 0x01, 0x2D, 0x8A, 0x0A, 0x1F, 0x1F},
+
+ {400, 48000, 19200000, 0xE4, 0x04, 0x35, 0x6d, 0xCA, 0x0A, 0x1F, 0x1F},
+ {500, 48000, 24000000, 0xF8, 0x04, 0x3F, 0x6D, 0xCA, 0x0A, 0x1F, 0x1F},
+ {512, 8000, 4096000, 0x60, 0x00, 0x01, 0x35, 0x8A, 0x1B, 0x1F, 0x7F},
+ {512, 16000, 8192000, 0x20, 0x00, 0x30, 0x35, 0x8A, 0x1B, 0x1F, 0x3F},
+ {512, 44100, 22579200, 0xE0, 0x00, 0x00, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F},
+ {512, 48000, 24576000, 0xE0, 0x00, 0x00, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F},
+ {768, 8000, 6144000, 0x60, 0x02, 0x11, 0x35, 0x8A, 0x1B, 0x1F, 0x7F},
+ {768, 16000, 12288000, 0x20, 0x02, 0x01, 0x35, 0x8A, 0x1B, 0x1F, 0x3F},
+ {768, 32000, 24576000, 0xE0, 0x02, 0x30, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F},
+ {800, 48000, 38400000, 0x00, 0x18, 0x13, 0x2D, 0x8A, 0x0A, 0x1F, 0x1F},
+
+ {1024, 8000, 8192000, 0x60, 0x00, 0x30, 0x35, 0x8A, 0x1B, 0x1F, 0x7F},
+ {1024, 16000, 16384000, 0x20, 0x00, 0x00, 0x35, 0x8A, 0x1B, 0x1F, 0x3F},
+ {1152, 16000, 18432000, 0x20, 0x08, 0x11, 0x35, 0x8A, 0x1B, 0x1F, 0x3F},
+ {1536, 8000, 12288000, 0x60, 0x02, 0x01, 0x35, 0x8A, 0x1B, 0x1F, 0x7F},
+ {1536, 16000, 24576000, 0x20, 0x02, 0x10, 0x35, 0x8A, 0x1B, 0x1F, 0x3F},
+ {1625, 8000, 13000000, 0x0C, 0x18, 0x1F, 0x2D, 0x8A, 0x0A, 0x27, 0x27},
+ {1625, 16000, 26000000, 0x0C, 0x18, 0x1F, 0x2D, 0x8A, 0x0A, 0x27, 0x27},
+ {2048, 8000, 16384000, 0x60, 0x00, 0x00, 0x35, 0x8A, 0x1B, 0x1F, 0x7F},
+ {2304, 8000, 18432000, 0x40, 0x02, 0x10, 0x35, 0x8A, 0x1B, 0x1F, 0x5F},
+ {3072, 8000, 24576000, 0x60, 0x02, 0x10, 0x35, 0x8A, 0x1B, 0x1F, 0x7F},
+ {3250, 8000, 26000000, 0x0C, 0x18, 0x0F, 0x2D, 0x8A, 0x0A, 0x27, 0x27},
};
-static inline int get_coeff(int mclk, int rate)
+static inline int get_coeff(int mclk, int rate, int array,
+ const struct _coeff_div *coeff_div)
{
int i;
- for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
+ for (i = 0; i < array; i++) {
if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
return i;
}
@@ -333,11 +441,19 @@ static int es8326_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_component *component = dai->component;
+ const struct _coeff_div *coeff_div;
struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
u8 srate = 0;
- int coeff;
+ int coeff, array;
- coeff = get_coeff(es8326->sysclk, params_rate(params));
+ if (es8326->version == 0) {
+ coeff_div = coeff_div_v0;
+ array = ARRAY_SIZE(coeff_div_v0);
+ } else {
+ coeff_div = coeff_div_v3;
+ array = ARRAY_SIZE(coeff_div_v3);
+ }
+ coeff = get_coeff(es8326->sysclk, params_rate(params), array, coeff_div);
/* bit size */
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
@@ -393,10 +509,16 @@ static int es8326_mute(struct snd_soc_dai *dai, int mute, int direction)
unsigned int offset_l, offset_r;
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);
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_OFF);
+ regmap_update_bits(es8326->regmap, ES8326_DAC_MUTE,
+ ES8326_MUTE_MASK, ES8326_MUTE);
+ regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF,
+ 0x30, 0x00);
+ } else {
+ regmap_update_bits(es8326->regmap, ES8326_ADC_MUTE,
+ 0x0F, 0x0F);
+ }
} else {
if (!es8326->calibrated) {
regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_FORCE_CAL);
@@ -409,11 +531,22 @@ static int es8326_mute(struct snd_soc_dai *dai, int mute, int direction)
regmap_write(es8326->regmap, ES8326_HPR_OFFSET_INI, offset_r);
es8326->calibrated = true;
}
- regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xa0);
- regmap_write(es8326->regmap, ES8326_HP_VOL, 0x80);
- regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_ON);
- regmap_update_bits(es8326->regmap, ES8326_DAC_MUTE,
- ES8326_MUTE_MASK, ~(ES8326_MUTE));
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(es8326->regmap, ES8326_DAC_DSM, 0x01, 0x01);
+ usleep_range(1000, 5000);
+ regmap_update_bits(es8326->regmap, ES8326_DAC_DSM, 0x01, 0x00);
+ usleep_range(1000, 5000);
+ regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF, 0x30, 0x20);
+ regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF, 0x30, 0x30);
+ regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xa1);
+ regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_ON);
+ regmap_update_bits(es8326->regmap, ES8326_DAC_MUTE,
+ ES8326_MUTE_MASK, ~(ES8326_MUTE));
+ } else {
+ msleep(300);
+ regmap_update_bits(es8326->regmap, ES8326_ADC_MUTE,
+ 0x0F, 0x00);
+ }
}
return 0;
}
@@ -430,29 +563,26 @@ static int es8326_set_bias_level(struct snd_soc_component *codec,
if (ret)
return ret;
- regmap_write(es8326->regmap, ES8326_RESET, 0x9f);
- msleep(20);
- regmap_update_bits(es8326->regmap, ES8326_DAC_DSM, 0x01, 0x00);
+ regmap_update_bits(es8326->regmap, ES8326_RESET, 0x02, 0x02);
+ usleep_range(5000, 10000);
regmap_write(es8326->regmap, ES8326_INTOUT_IO, es8326->interrupt_clk);
regmap_write(es8326->regmap, ES8326_SDINOUT1_IO,
(ES8326_IO_DMIC_CLK << ES8326_SDINOUT1_SHIFT));
- regmap_write(es8326->regmap, ES8326_VMIDSEL, 0x0E);
regmap_write(es8326->regmap, ES8326_PGA_PDN, 0x40);
regmap_write(es8326->regmap, ES8326_ANA_PDN, 0x00);
regmap_update_bits(es8326->regmap, ES8326_CLK_CTL, 0x20, 0x20);
- regmap_write(es8326->regmap, ES8326_RESET, ES8326_CSM_ON);
+ regmap_update_bits(es8326->regmap, ES8326_RESET, 0x02, 0x00);
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
- break;
- case SND_SOC_BIAS_OFF:
- clk_disable_unprepare(es8326->mclk);
regmap_write(es8326->regmap, ES8326_ANA_PDN, 0x3b);
- regmap_write(es8326->regmap, ES8326_VMIDSEL, 0x00);
regmap_update_bits(es8326->regmap, ES8326_CLK_CTL, 0x20, 0x00);
regmap_write(es8326->regmap, ES8326_SDINOUT1_IO, ES8326_IO_INPUT);
break;
+ case SND_SOC_BIAS_OFF:
+ clk_disable_unprepare(es8326->mclk);
+ break;
}
return 0;
@@ -466,7 +596,7 @@ static const struct snd_soc_dai_ops es8326_ops = {
.set_fmt = es8326_set_dai_fmt,
.set_sysclk = es8326_set_dai_sysclk,
.mute_stream = es8326_mute,
- .no_capture_mute = 1,
+ .no_capture_mute = 0,
};
static struct snd_soc_dai_driver es8326_dai = {
@@ -594,7 +724,7 @@ static void es8326_jack_detect_handler(struct work_struct *work)
iface = snd_soc_component_read(comp, ES8326_HPDET_STA);
dev_dbg(comp->dev, "gpio flag %#04x", iface);
- if (es8326->jack_remove_retry == 1) {
+ if ((es8326->jack_remove_retry == 1) && (es8326->version != ES8326_VERSION_B)) {
if (iface & ES8326_HPINSERT_FLAG)
es8326->jack_remove_retry = 2;
else
@@ -625,10 +755,12 @@ static void es8326_jack_detect_handler(struct work_struct *work)
es8326->hp = 0;
}
regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x01);
+ regmap_write(es8326->regmap, ES8326_SYS_BIAS, 0x0a);
+ regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF, 0x0f, 0x03);
/*
* Inverted HPJACK_POL bit to trigger one IRQ to double check HP Removal event
*/
- if (es8326->jack_remove_retry == 0) {
+ if ((es8326->jack_remove_retry == 0) && (es8326->version != ES8326_VERSION_B)) {
es8326->jack_remove_retry = 1;
dev_dbg(comp->dev, "remove event check, invert HPJACK_POL, cnt = %d\n",
es8326->jack_remove_retry);
@@ -644,14 +776,17 @@ static void es8326_jack_detect_handler(struct work_struct *work)
if (es8326->hp == 0) {
dev_dbg(comp->dev, "First insert, start OMTP/CTIA type check\n");
/*
- * set auto-check mode, then restart jack_detect_work after 100ms.
+ * set auto-check mode, then restart jack_detect_work after 400ms.
* Don't report jack status.
*/
regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x01);
+ es8326_enable_micbias(es8326->component);
usleep_range(50000, 70000);
regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x00);
+ regmap_write(es8326->regmap, ES8326_SYS_BIAS, 0x1f);
+ regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF, 0x0f, 0x08);
queue_delayed_work(system_wq, &es8326->jack_detect_work,
- msecs_to_jiffies(100));
+ msecs_to_jiffies(400));
es8326->hp = 1;
goto exit;
}
@@ -689,19 +824,16 @@ exit:
static irqreturn_t es8326_irq(int irq, void *dev_id)
{
struct es8326_priv *es8326 = dev_id;
- struct snd_soc_component *comp = es8326->component;
if (!es8326->jack)
goto out;
- es8326_enable_micbias(comp);
-
if (es8326->jack->status & SND_JACK_HEADSET)
queue_delayed_work(system_wq, &es8326->jack_detect_work,
msecs_to_jiffies(10));
else
queue_delayed_work(system_wq, &es8326->jack_detect_work,
- msecs_to_jiffies(600));
+ msecs_to_jiffies(300));
out:
return IRQ_HANDLED;
@@ -719,16 +851,18 @@ static int es8326_calibrate(struct snd_soc_component *component)
if ((es8326->version == ES8326_VERSION_B) && (es8326->calibrated == false)) {
dev_dbg(component->dev, "ES8326_VERSION_B, calibrating\n");
regmap_write(es8326->regmap, ES8326_CLK_INV, 0xc0);
- regmap_write(es8326->regmap, ES8326_CLK_DIV1, 0x01);
+ regmap_write(es8326->regmap, ES8326_CLK_DIV1, 0x03);
regmap_write(es8326->regmap, ES8326_CLK_DLL, 0x30);
regmap_write(es8326->regmap, ES8326_CLK_MUX, 0xed);
+ regmap_write(es8326->regmap, ES8326_CLK_DAC_SEL, 0x08);
regmap_write(es8326->regmap, ES8326_CLK_TRI, 0xc1);
regmap_write(es8326->regmap, ES8326_DAC_MUTE, 0x03);
regmap_write(es8326->regmap, ES8326_ANA_VSEL, 0x7f);
- regmap_write(es8326->regmap, ES8326_VMIDLOW, 0x33);
+ regmap_write(es8326->regmap, ES8326_VMIDLOW, 0x23);
regmap_write(es8326->regmap, ES8326_DAC2HPMIX, 0x88);
- regmap_write(es8326->regmap, ES8326_HP_VOL, 0x80);
+ usleep_range(15000, 20000);
regmap_write(es8326->regmap, ES8326_HP_OFFSET_CAL, 0x8c);
+ usleep_range(15000, 20000);
regmap_write(es8326->regmap, ES8326_RESET, 0xc0);
usleep_range(15000, 20000);
@@ -765,28 +899,29 @@ static int es8326_resume(struct snd_soc_component *component)
/* reset internal clock state */
regmap_write(es8326->regmap, ES8326_RESET, 0x1f);
regmap_write(es8326->regmap, ES8326_VMIDSEL, 0x0E);
+ regmap_write(es8326->regmap, ES8326_ANA_LP, 0xf0);
usleep_range(10000, 15000);
- regmap_write(es8326->regmap, ES8326_HPJACK_TIMER, 0x88);
+ regmap_write(es8326->regmap, ES8326_HPJACK_TIMER, 0xe9);
+ regmap_write(es8326->regmap, ES8326_ANA_MICBIAS, 0xcb);
/* set headphone default type and detect pin */
- regmap_write(es8326->regmap, ES8326_HPDET_TYPE, 0x81);
+ regmap_write(es8326->regmap, ES8326_HPDET_TYPE, 0x83);
regmap_write(es8326->regmap, ES8326_CLK_RESAMPLE, 0x05);
/* set internal oscillator as clock source of headpone cp */
- regmap_write(es8326->regmap, ES8326_CLK_DIV_CPC, 0x84);
+ regmap_write(es8326->regmap, ES8326_CLK_DIV_CPC, 0x89);
regmap_write(es8326->regmap, ES8326_CLK_CTL, ES8326_CLK_ON);
/* clock manager reset release */
regmap_write(es8326->regmap, ES8326_RESET, 0x17);
/* set headphone detection as half scan mode */
- regmap_write(es8326->regmap, ES8326_HP_MISC, 0x08);
+ regmap_write(es8326->regmap, ES8326_HP_MISC, 0x3d);
regmap_write(es8326->regmap, ES8326_PULLUP_CTL, 0x00);
/* enable headphone driver */
+ regmap_write(es8326->regmap, ES8326_HP_VOL, 0xc4);
regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xa7);
usleep_range(2000, 5000);
- regmap_write(es8326->regmap, ES8326_HP_DRIVER_REF, 0xab);
- usleep_range(2000, 5000);
- regmap_write(es8326->regmap, ES8326_HP_DRIVER_REF, 0xbb);
- usleep_range(2000, 5000);
+ regmap_write(es8326->regmap, ES8326_HP_DRIVER_REF, 0x23);
+ regmap_write(es8326->regmap, ES8326_HP_DRIVER_REF, 0x33);
regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xa1);
regmap_write(es8326->regmap, ES8326_CLK_INV, 0x00);
@@ -795,14 +930,13 @@ static int es8326_resume(struct snd_soc_component *component)
regmap_write(es8326->regmap, ES8326_CLK_CAL_TIME, 0x00);
/* calibrate for B version */
es8326_calibrate(component);
+ regmap_write(es8326->regmap, ES8326_DAC_CROSSTALK, 0xaa);
+ regmap_write(es8326->regmap, ES8326_DAC_RAMPRATE, 0x00);
/* turn off headphone out */
regmap_write(es8326->regmap, ES8326_HP_CAL, 0x00);
/* set ADC and DAC in low power mode */
regmap_write(es8326->regmap, ES8326_ANA_LP, 0xf0);
- /* force micbias on */
- regmap_write(es8326->regmap, ES8326_ANA_MICBIAS, 0x4f);
- regmap_write(es8326->regmap, ES8326_SYS_BIAS, 0x08);
regmap_write(es8326->regmap, ES8326_ANA_VSEL, 0x7F);
/* select vdda as micbias source */
regmap_write(es8326->regmap, ES8326_VMIDLOW, 0x23);
@@ -810,6 +944,14 @@ static int es8326_resume(struct snd_soc_component *component)
regmap_write(es8326->regmap, ES8326_DAC_DSM, 0x08);
regmap_write(es8326->regmap, ES8326_DAC_VPPSCALE, 0x15);
+ regmap_write(es8326->regmap, ES8326_HPDET_TYPE, 0x80 |
+ ((es8326->version == ES8326_VERSION_B) ?
+ (ES8326_HP_DET_SRC_PIN9 | es8326->jack_pol) :
+ (ES8326_HP_DET_SRC_PIN9 | es8326->jack_pol | 0x04)));
+ usleep_range(5000, 10000);
+ es8326_enable_micbias(es8326->component);
+ usleep_range(50000, 70000);
+ regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x00);
regmap_write(es8326->regmap, ES8326_INT_SOURCE,
(ES8326_INT_SRC_PIN9 | ES8326_INT_SRC_BUTTON));
regmap_write(es8326->regmap, ES8326_INTOUT_IO,
@@ -818,7 +960,7 @@ static int es8326_resume(struct snd_soc_component *component)
(ES8326_IO_DMIC_CLK << ES8326_SDINOUT1_SHIFT));
regmap_write(es8326->regmap, ES8326_SDINOUT23_IO, ES8326_IO_INPUT);
- regmap_write(es8326->regmap, ES8326_ANA_PDN, 0x3b);
+ regmap_write(es8326->regmap, ES8326_ANA_PDN, 0x00);
regmap_write(es8326->regmap, ES8326_RESET, ES8326_CSM_ON);
regmap_update_bits(es8326->regmap, ES8326_PGAGAIN, ES8326_MIC_SEL_MASK,
ES8326_MIC1_SEL);
@@ -826,10 +968,7 @@ static int es8326_resume(struct snd_soc_component *component)
regmap_update_bits(es8326->regmap, ES8326_DAC_MUTE, ES8326_MUTE_MASK,
ES8326_MUTE);
- regmap_write(es8326->regmap, ES8326_HPDET_TYPE, 0x80 |
- ((es8326->version == ES8326_VERSION_B) ?
- (ES8326_HP_DET_SRC_PIN9 | es8326->jack_pol) :
- (ES8326_HP_DET_SRC_PIN9 | es8326->jack_pol | 0x04)));
+ regmap_write(es8326->regmap, ES8326_ADC_MUTE, 0x0f);
es8326->jack_remove_retry = 0;
es8326->hp = 0;
diff --git a/sound/soc/codecs/es8326.h b/sound/soc/codecs/es8326.h
index 90a08351d6ac..4234bbb900c4 100644
--- a/sound/soc/codecs/es8326.h
+++ b/sound/soc/codecs/es8326.h
@@ -72,6 +72,7 @@
#define ES8326_DAC_VOL 0x50
#define ES8326_DRC_RECOVERY 0x53
#define ES8326_DRC_WINSIZE 0x54
+#define ES8326_DAC_CROSSTALK 0x55
#define ES8326_HPJACK_TIMER 0x56
#define ES8326_HPDET_TYPE 0x57
#define ES8326_INT_SOURCE 0x58
@@ -100,7 +101,7 @@
#define ES8326_MUTE (3 << 0)
/* ES8326_CLK_CTL */
-#define ES8326_CLK_ON (0x7f << 0)
+#define ES8326_CLK_ON (0x7e << 0)
#define ES8326_CLK_OFF (0 << 0)
/* ES8326_CLK_INV */
diff --git a/sound/soc/codecs/es83xx-dsm-common.c b/sound/soc/codecs/es83xx-dsm-common.c
new file mode 100644
index 000000000000..94fd7d54c53b
--- /dev/null
+++ b/sound/soc/codecs/es83xx-dsm-common.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright (c) Intel Corporation, 2022
+// Copyright Everest Semiconductor Co.,Ltd
+
+#include <linux/module.h>
+#include <linux/acpi.h>
+#include "es83xx-dsm-common.h"
+
+/* UUID ("a9800c04-e016-343e-41f4-6bcce70f4332") */
+static const guid_t es83xx_dsm_guid =
+ GUID_INIT(0xa9800c04, 0xe016, 0x343e,
+ 0x41, 0xf4, 0x6b, 0xcc, 0xe7, 0x0f, 0x43, 0x32);
+
+#define ES83xx_DSM_REVID 1
+
+int es83xx_dsm(struct device *dev, int arg, int *value)
+{
+ acpi_handle dhandle;
+ union acpi_object *obj;
+ int ret = 0;
+
+ dhandle = ACPI_HANDLE(dev);
+ if (!dhandle)
+ return -ENOENT;
+
+ obj = acpi_evaluate_dsm(dhandle, &es83xx_dsm_guid, ES83xx_DSM_REVID,
+ arg, NULL);
+ if (!obj) {
+ dev_err(dev, "%s: acpi_evaluate_dsm() failed\n", __func__);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (obj->type != ACPI_TYPE_INTEGER) {
+ dev_err(dev, "%s: object is not ACPI_TYPE_INTEGER\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ *value = obj->integer.value;
+err:
+ ACPI_FREE(obj);
+out:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(es83xx_dsm);
+
+int es83xx_dsm_dump(struct device *dev)
+{
+ int value;
+ int ret;
+
+ ret = es83xx_dsm(dev, PLATFORM_MAINMIC_TYPE_ARG, &value);
+ if (ret < 0)
+ return ret;
+ dev_info(dev, "PLATFORM_MAINMIC_TYPE %#x\n", value);
+
+ ret = es83xx_dsm(dev, PLATFORM_HPMIC_TYPE_ARG, &value);
+ if (ret < 0)
+ return ret;
+ dev_info(dev, "PLATFORM_HPMIC_TYPE %#x\n", value);
+
+ ret = es83xx_dsm(dev, PLATFORM_SPK_TYPE_ARG, &value);
+ if (ret < 0)
+ return ret;
+ dev_info(dev, "PLATFORM_SPK_TYPE %#x\n", value);
+
+ ret = es83xx_dsm(dev, PLATFORM_HPDET_INV_ARG, &value);
+ if (ret < 0)
+ return ret;
+ dev_info(dev, "PLATFORM_HPDET_INV %#x\n", value);
+
+ ret = es83xx_dsm(dev, PLATFORM_PCM_TYPE_ARG, &value);
+ if (ret < 0)
+ return ret;
+ dev_info(dev, "PLATFORM_PCM_TYPE %#x\n", value);
+
+ ret = es83xx_dsm(dev, PLATFORM_MIC_DE_POP_ARG, &value);
+ if (ret < 0)
+ return ret;
+ dev_info(dev, "PLATFORM_MIC_DE_POP %#x\n", value);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(es83xx_dsm_dump);
+
+MODULE_DESCRIPTION("Everest Semi ES83xx DSM helpers");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/es83xx-dsm-common.h b/sound/soc/codecs/es83xx-dsm-common.h
new file mode 100644
index 000000000000..91c9a89e75e9
--- /dev/null
+++ b/sound/soc/codecs/es83xx-dsm-common.h
@@ -0,0 +1,393 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) Intel Corporation, 2022
+ * Copyright Everest Semiconductor Co.,Ltd
+ */
+
+/* Definitions extracted from ASL file provided at
+ * https://github.com/thesofproject/linux/files/9398723/ESSX8326.zip
+ */
+
+#ifndef _ES83XX_DSM_COMMON_H
+#define _ES83XX_DSM_COMMON_H
+
+/***************************************************
+ * DSM arguments *
+ ***************************************************/
+
+#define PLATFORM_MAINMIC_TYPE_ARG 0x00
+#define PLATFORM_HPMIC_TYPE_ARG 0x01
+#define PLATFORM_SPK_TYPE_ARG 0x02
+#define PLATFORM_HPDET_INV_ARG 0x03
+#define PLATFORM_PCM_TYPE_ARG 0x04
+
+#define PLATFORM_MIC_DE_POP_ARG 0x06
+#define PLATFORM_CODEC_TYPE_ARG 0x0E
+#define PLATFORM_BUS_SLOT_ARG 0x0F
+
+#define HP_CODEC_LINEIN_PGA_GAIN_ARG 0x10
+#define MAIN_CODEC_LINEIN_PGA_GAIN_ARG 0x20
+
+#define HP_CODEC_D2SEPGA_GAIN_ARG 0x11
+#define MAIN_CODEC_D2SEPGA_GAIN_ARG 0x21
+
+#define HP_CODEC_ADC_VOLUME_ARG 0x12
+#define MAIN_CODEC_ADC_VOLUME_ARG 0x22
+
+#define HP_CODEC_ADC_ALC_ENABLE_ARG 0x13
+#define MAIN_CODEC_ADC_ALC_ENABLE_ARG 0x23
+
+#define HP_CODEC_ADC_ALC_TARGET_LEVEL_ARG 0x14
+#define MAIN_CODEC_ADC_ALC_TARGET_LEVEL_ARG 0x24
+
+#define HP_CODEC_ADC_ALC_MAXGAIN_ARG 0x15
+#define MAIN_CODEC_ADC_ALC_MAXGAIN_ARG 0x25
+
+#define HP_CODEC_ADC_ALC_MINGAIN_ARG 0x16
+#define MAIN_CODEC_ADC_ALC_MINGAIN_ARG 0x26
+
+#define HP_CODEC_ADC_ALC_HLDTIME_ARG 0x17
+#define MAIN_CODEC_ADC_ALC_HLDTIME_ARG 0x27
+
+#define HP_CODEC_ADC_ALC_DCYTIME_ARG 0x18
+#define MAIN_CODEC_ADC_ALC_DCYTIME_ARG 0x28
+
+#define HP_CODEC_ADC_ALC_ATKTIME_ARG 0x19
+#define MAIN_CODEC_ADC_ALC_ATKTIME_ARG 0x29
+
+#define HP_CODEC_ADC_ALC_NGTYPE_ARG 0x1a
+#define MAIN_CODEC_ADC_ALC_NGTYPE_ARG 0x2a
+
+#define HP_CODEC_ADC_ALC_NGTHLD_ARG 0x1b
+#define MAIN_CODEC_ADC_ALC_NGTHLD_ARG 0x2b
+
+#define MAIN_CODEC_ADC_GUI_STEP_ARG 0x2c
+#define MAIN_CODEC_ADC_GUI_GAIN_RANGE_ARG 0x2c
+
+#define HEADPHONE_DUMMY_REMOVE_ENABLE_ARG 0x2e
+
+#define HP_CODEC_DAC_HPMIX_HIGAIN_ARG 0x40
+#define SPK_CODEC_DAC_HPMIX_HIGAIN_ARG 0x50
+
+#define HP_CODEC_DAC_HPMIX_VOLUME_ARG 0x41
+#define SPK_CODEC_DAC_HPMIX_VOLUME_ARG 0x51
+
+#define HP_CODEC_DAC_HPOUT_VOLUME_ARG 0x42
+#define SPK_CODEC_DAC_HPOUT_VOLUME_ARG 0x52
+
+#define HP_CODEC_LDAC_VOLUME_ARG 0x44
+#define HP_CODEC_RDAC_VOLUME_ARG 0x54
+
+#define SPK_CODEC_LDAC_VOLUME_ARG 0x45
+#define SPK_CODEC_RDAC_VOLUME_ARG 0x55
+
+#define HP_CODEC_DAC_AUTOMUTE_ARG 0x46
+#define SPK_CODEC_DAC_AUTOMUTE_ARG 0x56
+
+#define HP_CODEC_DAC_MONO_ARG 0x4A
+#define SPK_CODEC_DAC_MONO_ARG 0x5A
+
+#define HP_CTL_IO_LEVEL_ARG 0x4B
+#define SPK_CTL_IO_LEVEL_ARG 0x5B
+
+#define CODEC_GPIO0_FUNC_ARG 0x80
+#define CODEC_GPIO1_FUNC_ARG 0x81
+#define CODEC_GPIO2_FUNC_ARG 0x82
+#define CODEC_GPIO3_FUNC_ARG 0x83
+#define CODEC_GPIO4_FUNC_ARG 0x84
+
+#define PLATFORM_MCLK_LRCK_FREQ_ARG 0x85
+
+/***************************************************
+ * Values for arguments *
+ ***************************************************/
+
+/* Main and HP Mic */
+#define PLATFORM_MIC_DMIC_HIGH_LEVEL 0xAA
+#define PLATFORM_MIC_DMIC_LOW_LEVEL 0x55
+#define PLATFORM_MIC_AMIC_LIN1RIN1 0xBB
+#define PLATFORM_MIC_AMIC_LIN2RIN2 0xCC
+
+/* Speaker */
+#define PLATFORM_SPK_NONE 0x00
+#define PLATFORM_SPK_MONO 0x01
+#define PLATFORM_SPK_STEREO 0x02
+
+/* Jack Detection */
+#define PLATFORM_HPDET_NORMAL 0x00
+#define PLATFORM_HPDET_INVERTED 0x01
+
+/* PCM type (Port number + protocol) */
+/*
+ * RETURNED VALUE = 0x00, PCM PORT0, I2S
+ * 0x01, PCM PORT0, LJ
+ * 0x02, PCM PORT0, RJ
+ * 0x03, PCM PORT0, DSP-A
+ * 0x04, PCM PORT0, DSP-B
+ * 0x10, PCM PORT1, I2S
+ * 0x11, PCM PORT1, LJ
+ * 0x12, PCM PORT1, RJ
+ * 0x13, PCM PORT1, DSP-A
+ * 0x14, PCM PORT1, DSP-B
+ * 0xFF, Use default
+ *
+ * This is not used in Linux (defined by topology) and in
+ * Windows it's always DSP-A
+ */
+
+/* Depop */
+#define PLATFORM_MIC_DE_POP_OFF 0x00
+#define PLATFORM_MIC_DE_POP_ON 0x01
+
+/* Codec type */
+#define PLATFORM_CODEC_8316 16
+#define PLATFORM_CODEC_8326 26
+#define PLATFORM_CODEC_8336 36
+#define PLATFORM_CODEC_8395 95
+#define PLATFORM_CODEC_8396 96
+
+/* Bus slot (on the host) */
+/* BIT[3:0] FOR BUS NUMBER, BIT[7:4] FOR SLOT NUMBER
+ * BIT[3:0] 0 for I2S0, 1 for IS21, 2 for I2S2.
+ *
+ * On Intel platforms this refers to SSP0..2. This information
+ * is not really useful for Linux, the information is already
+ * inferred from NHLT but can be used to double-check NHLT
+ */
+
+/* Volume - Gain */
+#define LINEIN_GAIN_0db 0x00 /* gain = 0db */
+#define LINEIN_GAIN_3db 0x01 /* gain = +3db */
+#define LINEIN_GAIN_6db 0x02 /* gain = +6db */
+#define LINEIN_GAIN_9db 0x03 /* gain = +9db */
+#define LINEIN_GAIN_12db 0x04 /* gain = +12db */
+#define LINEIN_GAIN_15db 0x05 /* gain = +15db */
+#define LINEIN_GAIN_18db 0x06 /* gain = +18db */
+#define LINEIN_GAIN_21db 0x07 /* gain = +21db */
+#define LINEIN_GAIN_24db 0x08 /* gain = +24db */
+#define LINEIN_GAIN_27db 0x09 /* gain = +27db */
+#define LINEIN_GAIN_30db 0x0a /* gain = +30db */
+
+#define ADC_GUI_STEP_3db 0x03 /* gain = +3db */
+#define ADC_GUI_STEP_6db 0x06 /* gain = +6db */
+#define ADC_GUI_STEP_10db 0x0a /* gain = +10db */
+
+#define D2SEPGA_GAIN_0db 0x00 /* gain = 0db */
+#define D2SEPGA_GAIN_15db 0x01 /* gain = +15db */
+
+/* ADC volume: base = 0db, -0.5db/setp, 0xc0 <-> -96db */
+
+#define ADC_ALC_DISABLE 0x00
+#define ADC_ALC_ENABLE 0x01
+
+#define ADC_ALC_TARGET_LEVEL_m16_5db 0x00 /* gain = -16.5db */
+#define ADC_ALC_TARGET_LEVEL_m15db 0x01 /* gain = -15db */
+#define ADC_ALC_TARGET_LEVEL_m13_5db 0x02 /* gain = -13.5db */
+#define ADC_ALC_TARGET_LEVEL_m12db 0x03 /* gain = -12db */
+#define ADC_ALC_TARGET_LEVEL_m10_5db 0x04 /* gain = -10.5db */
+#define ADC_ALC_TARGET_LEVEL_m9db 0x05 /* gain = -9db */
+#define ADC_ALC_TARGET_LEVEL_m7_5db 0x06 /* gain = -7.5db */
+#define ADC_ALC_TARGET_LEVEL_m6db 0x07 /* gain = -6db */
+#define ADC_ALC_TARGET_LEVEL_m4_5db 0x08 /* gain = -4.5db */
+#define ADC_ALC_TARGET_LEVEL_m_3db 0x09 /* gain = -3db */
+#define ADC_ALC_TARGET_LEVEL_m1_5db 0x0a /* gain = -1.5db */
+
+#define ADC_ALC_MAXGAIN_m6_5db 0x00 /* gain = -6.5db */
+#define ADC_ALC_MAXGAIN_m5db 0x01 /* gain = -5db */
+#define ADC_ALC_MAXGAIN_m3_5db 0x02 /* gain = -3.5db */
+#define ADC_ALC_MAXGAIN_m2db 0x03 /* gain = -2db */
+#define ADC_ALC_MAXGAIN_m0_5db 0x04 /* gain = -0.5db */
+#define ADC_ALC_MAXGAIN_1db 0x05 /* gain = +1db */
+#define ADC_ALC_MAXGAIN_2_5db 0x06 /* gain = +2.5db */
+#define ADC_ALC_MAXGAIN_4db 0x07 /* gain = +4db */
+#define ADC_ALC_MAXGAIN_5_5db 0x08 /* gain = +5.5db */
+#define ADC_ALC_MAXGAIN_7db 0x09 /* gain = +7db */
+#define ADC_ALC_MAXGAIN_8_5db 0x0a /* gain = +8.5db */
+#define ADC_ALC_MAXGAIN_10db 0x0b /* gain = +10db */
+#define ADC_ALC_MAXGAIN_11_5db 0x0c /* gain = +11.5db */
+#define ADC_ALC_MAXGAIN_13db 0x0d /* gain = +13db */
+#define ADC_ALC_MAXGAIN_14_5db 0x0e /* gain = +14.5db */
+#define ADC_ALC_MAXGAIN_16db 0x0f /* gain = +16db */
+#define ADC_ALC_MAXGAIN_17_5db 0x10 /* gain = +17.5db */
+#define ADC_ALC_MAXGAIN_19db 0x11 /* gain = +19db */
+#define ADC_ALC_MAXGAIN_20_5db 0x12 /* gain = +20.5db */
+#define ADC_ALC_MAXGAIN_22db 0x13 /* gain = +22db */
+#define ADC_ALC_MAXGAIN_23_5db 0x14 /* gain = +23.5db */
+#define ADC_ALC_MAXGAIN_25db 0x15 /* gain = +25db */
+#define ADC_ALC_MAXGAIN_26_5db 0x16 /* gain = +26.5db */
+#define ADC_ALC_MAXGAIN_28db 0x17 /* gain = +28db */
+#define ADC_ALC_MAXGAIN_29_5db 0x18 /* gain = +29.5db */
+#define ADC_ALC_MAXGAIN_31db 0x19 /* gain = +31db */
+#define ADC_ALC_MAXGAIN_32_5db 0x1a /* gain = +32.5db */
+#define ADC_ALC_MAXGAIN_34db 0x1b /* gain = +34db */
+#define ADC_ALC_MAXGAIN_35_5db 0x1c /* gain = +35.5db */
+
+#define ADC_ALC_MINGAIN_m12db 0x00 /* gain = -12db */
+#define ADC_ALC_MINGAIN_m10_5db 0x01 /* gain = -10.5db */
+#define ADC_ALC_MINGAIN_m9db 0x02 /* gain = -9db */
+#define ADC_ALC_MINGAIN_m7_5db 0x03 /* gain = -7.5db */
+#define ADC_ALC_MINGAIN_m6db 0x04 /* gain = -6db */
+#define ADC_ALC_MINGAIN_m4_51db 0x05 /* gain = -4.51db */
+#define ADC_ALC_MINGAIN_m3db 0x06 /* gain = -3db */
+#define ADC_ALC_MINGAIN_m1_5db 0x07 /* gain = -1.5db */
+#define ADC_ALC_MINGAIN_0db 0x08 /* gain = 0db */
+#define ADC_ALC_MINGAIN_1_5db 0x09 /* gain = +1.5db */
+#define ADC_ALC_MINGAIN_3db 0x0a /* gain = +3db */
+#define ADC_ALC_MINGAIN_4_5db 0x0b /* gain = +4.5db */
+#define ADC_ALC_MINGAIN_6db 0x0c /* gain = +6db */
+#define ADC_ALC_MINGAIN_7_5db 0x0d /* gain = +7.5db */
+#define ADC_ALC_MINGAIN_9db 0x0e /* gain = +9db */
+#define ADC_ALC_MINGAIN_10_5db 0x0f /* gain = +10.5db */
+#define ADC_ALC_MINGAIN_12db 0x10 /* gain = +12db */
+#define ADC_ALC_MINGAIN_13_5db 0x11 /* gain = +13.5db */
+#define ADC_ALC_MINGAIN_15db 0x12 /* gain = +15db */
+#define ADC_ALC_MINGAIN_16_5db 0x13 /* gain = +16.5db */
+#define ADC_ALC_MINGAIN_18db 0x14 /* gain = +18db */
+#define ADC_ALC_MINGAIN_19_5db 0x15 /* gain = +19.5db */
+#define ADC_ALC_MINGAIN_21db 0x16 /* gain = +21db */
+#define ADC_ALC_MINGAIN_22_5db 0x17 /* gain = +22.5db */
+#define ADC_ALC_MINGAIN_24db 0x18 /* gain = +24db */
+#define ADC_ALC_MINGAIN_25_5db 0x19 /* gain = +25.5db */
+#define ADC_ALC_MINGAIN_27db 0x1a /* gain = +27db */
+#define ADC_ALC_MINGAIN_28_5db 0x1b /* gain = +28.5db */
+#define ADC_ALC_MINGAIN_30db 0x1c /* gain = +30db */
+
+/* ADC volume: step 1dB */
+
+/* ALC Hold, Decay, Attack */
+#define ADC_ALC_HLDTIME_0_US 0x00
+#define ADC_ALC_HLDTIME_0000266_US 0x01 //time = 2.67ms
+#define ADC_ALC_HLDTIME_0000533_US 0x02 //time = 5.33ms
+#define ADC_ALC_HLDTIME_0001066_US 0x03 //time = 10.66ms
+#define ADC_ALC_HLDTIME_0002132_US 0x04 //time = 21.32ms
+#define ADC_ALC_HLDTIME_0004264_US 0x05 //time = 42.64ms
+#define ADC_ALC_HLDTIME_0008538_US 0x06 //time = 85.38ms
+#define ADC_ALC_HLDTIME_0017076_US 0x07 //time = 170.76ms
+#define ADC_ALC_HLDTIME_0034152_US 0x08 //time = 341.52ms
+#define ADC_ALC_HLDTIME_0680000_US 0x09 //time = 0.68s
+#define ADC_ALC_HLDTIME_1360000_US 0x0a //time = 1.36s
+
+#define ADC_ALC_DCYTIME_000410_US 0x00 //time = 410us
+#define ADC_ALC_DCYTIME_000820_US 0x01 //time = 820us
+#define ADC_ALC_DCYTIME_001640_US 0x02 //time = 1.64ms
+#define ADC_ALC_DCYTIME_003280_US 0x03 //time = 3.28ms
+#define ADC_ALC_DCYTIME_006560_US 0x04 //time = 6.56ms
+#define ADC_ALC_DCYTIME_013120_US 0x05 //time = 13.12ms
+#define ADC_ALC_DCYTIME_026240_US 0x06 //time = 26.24ms
+#define ADC_ALC_DCYTIME_058480_US 0x07 //time = 52.48ms
+#define ADC_ALC_DCYTIME_104960_US 0x08 //time = 104.96ms
+#define ADC_ALC_DCYTIME_209920_US 0x09 //time = 209.92ms
+#define ADC_ALC_DCYTIME_420000_US 0x0a //time = 420ms
+
+#define ADC_ALC_ATKTIME_000104_US 0x00 //time = 104us
+#define ADC_ALC_ATKTIME_000208_US 0x01 //time = 208us
+#define ADC_ALC_ATKTIME_000416_US 0x02 //time = 416ms
+#define ADC_ALC_ATKTIME_003832_US 0x03 //time = 832ms
+#define ADC_ALC_ATKTIME_001664_US 0x04 //time = 1.664ms
+#define ADC_ALC_ATKTIME_003328_US 0x05 //time = 3.328ms
+#define ADC_ALC_ATKTIME_006656_US 0x06 //time = 6.656ms
+#define ADC_ALC_ATKTIME_013312_US 0x07 //time = 13.312ms
+#define ADC_ALC_ATKTIME_026624_US 0x08 //time = 26.624ms
+#define ADC_ALC_ATKTIME_053248_US 0x09 //time = 53.248ms
+#define ADC_ALC_ATKTIME_106496_US 0x0a //time = 106.496ms
+
+/* ALC Noise Gate */
+#define ADC_ALC_NGTYPE_DISABLE 0x00 //noise gate disable
+#define ADC_ALC_NGTYPE_ENABLE_HOLD 0x01 //noise gate enable, hold gain type
+#define ADC_ALC_NGTYPE_ENABLE_MUTE 0x03 //noise gate enable, mute type
+
+#define ADC_ALC_NGTHLD_m76_5db 0x00 /* Threshold = -76.5db */
+#define ADC_ALC_NGTHLD_m75db 0x01 /* Threshold = -75db */
+#define ADC_ALC_NGTHLD_m73_5db 0x02 /* Threshold = -73.5db */
+#define ADC_ALC_NGTHLD_m72db 0x03 /* Threshold = -72db */
+#define ADC_ALC_NGTHLD_m70_5db 0x04 /* Threshold = -70.5db */
+#define ADC_ALC_NGTHLD_m69db 0x05 /* Threshold = -69db */
+#define ADC_ALC_NGTHLD_m67_5db 0x06 /* Threshold = -67.5db */
+#define ADC_ALC_NGTHLD_m66db 0x07 /* Threshold = -66db */
+#define ADC_ALC_NGTHLD_m64_5db 0x08 /* Threshold = -64.5db */
+#define ADC_ALC_NGTHLD_m63db 0x09 /* Threshold = -63db */
+#define ADC_ALC_NGTHLD_m61_5db 0x0a /* Threshold = -61.5db */
+#define ADC_ALC_NGTHLD_m60db 0x0b /* Threshold = -60db */
+#define ADC_ALC_NGTHLD_m58_5db 0x0c /* Threshold = -58.5db */
+#define ADC_ALC_NGTHLD_m57db 0x0d /* Threshold = -57db */
+#define ADC_ALC_NGTHLD_m55_5db 0x0e /* Threshold = -55.5db */
+#define ADC_ALC_NGTHLD_m54db 0x0f /* Threshold = -54db */
+#define ADC_ALC_NGTHLD_m52_5db 0x10 /* Threshold = -52.5db */
+#define ADC_ALC_NGTHLD_m51db 0x11 /* Threshold = -51db */
+#define ADC_ALC_NGTHLD_m49_5db 0x12 /* Threshold = -49.5db */
+#define ADC_ALC_NGTHLD_m48db 0x13 /* Threshold = -48db */
+#define ADC_ALC_NGTHLD_m46_5db 0x14 /* Threshold = -46.5db */
+#define ADC_ALC_NGTHLD_m45db 0x15 /* Threshold = -45db */
+#define ADC_ALC_NGTHLD_m43_5db 0x16 /* Threshold = -43.5db */
+#define ADC_ALC_NGTHLD_m42db 0x17 /* Threshold = -42db */
+#define ADC_ALC_NGTHLD_m40_5db 0x18 /* Threshold = -40.5db */
+#define ADC_ALC_NGTHLD_m39db 0x19 /* Threshold = -39db */
+#define ADC_ALC_NGTHLD_m37_5db 0x1a /* Threshold = -37.5db */
+#define ADC_ALC_NGTHLD_m36db 0x1b /* Threshold = -36db */
+#define ADC_ALC_NGTHLD_m34_5db 0x1c /* Threshold = -34.5db */
+#define ADC_ALC_NGTHLD_m33db 0x1d /* Threshold = -33db */
+#define ADC_ALC_NGTHLD_m31_5db 0x1e /* Threshold = -31.5db */
+#define ADC_ALC_NGTHLD_m30db 0x1f /* Threshold = -30db */
+
+/* Headphone dummy - Windows Specific flag, not needed for Linux */
+
+/* HPMIX HIGAIN and VOLUME */
+#define DAC_HPMIX_HIGAIN_0db 0x00 /* gain = 0db */
+#define DAC_HPMIX_HIGAIN_m6db 0x88 /* gain = -6db */
+
+#define DAC_HPMIX_VOLUME_m12db 0x00 /* volume = -12db */
+#define DAC_HPMIX_VOLUME_m10_5db 0x11 /* volume = -10.5db */
+#define DAC_HPMIX_VOLUME_m9db 0x22 /* volume = -9db */
+#define DAC_HPMIX_VOLUME_m7_5db 0x33 /* volume = -7.5db */
+#define DAC_HPMIX_VOLUME_m6db 0x44 /* volume = -6db */
+#define DAC_HPMIX_VOLUME_m4_5db 0x88 /* volume = -4.5db */
+#define DAC_HPMIX_VOLUME_m3db 0x99 /* volume = -3db */
+#define DAC_HPMIX_VOLUME_m1_5db 0xaa /* volume = -1.5db */
+#define DAC_HPMIX_VOLUME_0db 0xbb /* volume = 0db */
+
+/* HPOUT VOLUME */
+#define DAC_HPOUT_VOLUME_0db 0x00 /* volume = 0db */
+#define DAC_HPOUT_VOLUME_m12db 0x11 /* volume = -12db */
+#define DAC_HPOUT_VOLUME_m24db 0x22 /* volume = -24db */
+#define DAC_HPOUT_VOLUME_m48db 0x33 /* volume = -48db */
+
+/* LDAC/RDAC volume = 0db, -0.5db/setp, 0xc0 <-> -96db */
+
+/* Automute */
+#define DAC_AUTOMUTE_NONE 0x00 /* no automute */
+#define DAC_AUTOMUTE_DIGITAL 0x01 /* digital mute */
+#define DAC_AUTOMUTE_ANALOG 0x02 /* analog mute */
+
+/* Mono - Windows specific, on Linux the information comes from DAI/topology */
+#define HEADPHONE_MONO 0x01 /* on channel */
+#define HEADPHONE_STEREO 0x00 /* stereo */
+
+/* Speaker and headphone GPIO control */
+#define GPIO_CTL_IO_LEVEL_LOW 0x00 /* low level enable */
+#define GPIO_CTL_IO_LEVEL_HIGH 0x01 /* high level enable */
+
+/* GPIO */
+/* FIXME: for ES8396, no need to use */
+
+/* Platform clocks */
+/*
+ * BCLK AND MCLK FREQ
+ * BIT[7:4] MCLK FREQ
+ * 0 - 19.2MHz
+ * 1 - 24MHz
+ * 2 - 12.288MHz
+ * F - Default for 19.2MHz
+ *
+ * BIT[3:0] BCLK FREQ
+ * 0 - 4.8MHz
+ * 1 - 2.4MHz
+ * 2 - 2.304MHz
+ * 3 - 3.072MHz
+ * 4 - 4.096MHz
+ * F - Default for 4.8MHz
+ */
+
+int es83xx_dsm(struct device *dev, int arg, int *value);
+int es83xx_dsm_dump(struct device *dev);
+
+#endif
diff --git a/sound/soc/codecs/hda-dai.c b/sound/soc/codecs/hda-dai.c
index 5371ff086261..7bd7ddcd810f 100644
--- a/sound/soc/codecs/hda-dai.c
+++ b/sound/soc/codecs/hda-dai.c
@@ -76,13 +76,16 @@ static int hda_codec_dai_prepare(struct snd_pcm_substream *substream, struct snd
struct hdac_stream *stream;
struct hda_codec *codec;
unsigned int format;
+ unsigned int bits;
int ret;
codec = dev_to_hda_codec(dai->dev);
stream = substream->runtime->private_data;
stream_info = snd_soc_dai_get_dma_data(dai, substream);
- format = snd_hdac_calc_stream_format(runtime->rate, runtime->channels, runtime->format,
- runtime->sample_bits, 0);
+
+ bits = snd_hdac_stream_format_bits(runtime->format, runtime->subformat,
+ stream_info->maxbps);
+ format = snd_hdac_stream_format(runtime->channels, bits, runtime->rate);
ret = snd_hda_codec_prepare(codec, stream_info, stream->stream_tag, format, substream);
if (ret < 0) {
diff --git a/sound/soc/codecs/hda.c b/sound/soc/codecs/hda.c
index d57b043d6bfe..d2117e36ddd1 100644
--- a/sound/soc/codecs/hda.c
+++ b/sound/soc/codecs/hda.c
@@ -52,6 +52,7 @@ static int hda_codec_create_dais(struct hda_codec *codec, int pcm_count,
stream->channels_max = pcm->stream[dir].channels_max;
stream->rates = pcm->stream[dir].rates;
stream->formats = pcm->stream[dir].formats;
+ stream->subformats = pcm->stream[dir].subformats;
stream->sig_bits = pcm->stream[dir].maxbps;
capture_dais:
@@ -71,6 +72,7 @@ capture_dais:
stream->channels_max = pcm->stream[dir].channels_max;
stream->rates = pcm->stream[dir].rates;
stream->formats = pcm->stream[dir].formats;
+ stream->subformats = pcm->stream[dir].subformats;
stream->sig_bits = pcm->stream[dir].maxbps;
}
diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c
index b075689db2dc..6aa3223985be 100644
--- a/sound/soc/codecs/hdac_hda.c
+++ b/sound/soc/codecs/hdac_hda.c
@@ -218,18 +218,16 @@ static int hdac_hda_dai_hw_params(struct snd_pcm_substream *substream,
struct hdac_hda_priv *hda_pvt;
unsigned int format_val;
unsigned int maxbps;
+ unsigned int bits;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
maxbps = dai->driver->playback.sig_bits;
else
maxbps = dai->driver->capture.sig_bits;
+ bits = snd_hdac_stream_format_bits(params_format(params), SNDRV_PCM_SUBFORMAT_STD, maxbps);
hda_pvt = snd_soc_component_get_drvdata(component);
- format_val = snd_hdac_calc_stream_format(params_rate(params),
- params_channels(params),
- params_format(params),
- maxbps,
- 0);
+ format_val = snd_hdac_stream_format(params_channels(params), bits, params_rate(params));
if (!format_val) {
dev_err(dai->dev,
"invalid format_val, rate=%d, ch=%d, format=%d, maxbps=%d\n",
diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c
index b9c5ffbfb5ba..e1a7f0b0c0f3 100644
--- a/sound/soc/codecs/hdac_hdmi.c
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -16,6 +16,7 @@
#include <linux/pm_runtime.h>
#include <linux/hdmi.h>
#include <drm/drm_edid.h>
+#include <drm/drm_eld.h>
#include <sound/pcm_params.h>
#include <sound/jack.h>
#include <sound/soc.h>
@@ -468,13 +469,14 @@ static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream,
struct hdac_hdmi_priv *hdmi = snd_soc_dai_get_drvdata(dai);
struct hdac_hdmi_dai_port_map *dai_map;
struct hdac_hdmi_pcm *pcm;
+ unsigned int bits;
int format;
dai_map = &hdmi->dai_map[dai->id];
- format = snd_hdac_calc_stream_format(params_rate(hparams),
- params_channels(hparams), params_format(hparams),
- dai->driver->playback.sig_bits, 0);
+ bits = snd_hdac_stream_format_bits(params_format(hparams), SNDRV_PCM_SUBFORMAT_STD,
+ dai->driver->playback.sig_bits);
+ format = snd_hdac_stream_format(params_channels(hparams), bits, params_rate(hparams));
pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, dai_map->cvt);
if (!pcm)
@@ -670,6 +672,7 @@ hdac_hdmi_query_cvt_params(struct hdac_device *hdev, struct hdac_hdmi_cvt *cvt)
err = snd_hdac_query_supported_pcm(hdev, cvt->nid,
&cvt->params.rates,
&cvt->params.formats,
+ NULL,
&cvt->params.maxbps);
if (err < 0)
dev_err(&hdev->dev,
@@ -1577,7 +1580,7 @@ static int hdac_hdmi_create_dais(struct hdac_device *hdev,
list_for_each_entry(cvt, &hdmi->cvt_list, head) {
ret = snd_hdac_query_supported_pcm(hdev, cvt->nid,
- &rates, &formats, &bps);
+ &rates, &formats, NULL, &bps);
if (ret)
return ret;
diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c
index 0938671700c6..d3abb7ce2153 100644
--- a/sound/soc/codecs/hdmi-codec.c
+++ b/sound/soc/codecs/hdmi-codec.c
@@ -17,6 +17,7 @@
#include <sound/pcm_iec958.h>
#include <drm/drm_crtc.h> /* This is only to get MAX_ELD_BYTES */
+#include <drm/drm_eld.h>
#define HDMI_CODEC_CHMAP_IDX_UNKNOWN -1
diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c
index 7e21cec3c2fb..6ce309980cd1 100644
--- a/sound/soc/codecs/lpass-wsa-macro.c
+++ b/sound/soc/codecs/lpass-wsa-macro.c
@@ -1584,7 +1584,6 @@ static int wsa_macro_enable_interpolator(struct snd_soc_dapm_widget *w,
u16 gain_reg;
u16 reg;
int val;
- int offset_val = 0;
struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
if (w->shift == WSA_MACRO_COMP1) {
@@ -1623,10 +1622,8 @@ static int wsa_macro_enable_interpolator(struct snd_soc_dapm_widget *w,
CDC_WSA_RX1_RX_PATH_MIX_SEC0,
CDC_WSA_RX_PGA_HALF_DB_MASK,
CDC_WSA_RX_PGA_HALF_DB_ENABLE);
- offset_val = -2;
}
val = snd_soc_component_read(component, gain_reg);
- val += offset_val;
snd_soc_component_write(component, gain_reg, val);
wsa_macro_config_ear_spkr_gain(component, wsa,
event, gain_reg);
@@ -1654,10 +1651,6 @@ static int wsa_macro_enable_interpolator(struct snd_soc_dapm_widget *w,
CDC_WSA_RX1_RX_PATH_MIX_SEC0,
CDC_WSA_RX_PGA_HALF_DB_MASK,
CDC_WSA_RX_PGA_HALF_DB_DISABLE);
- offset_val = 2;
- val = snd_soc_component_read(component, gain_reg);
- val += offset_val;
- snd_soc_component_write(component, gain_reg, val);
}
wsa_macro_config_ear_spkr_gain(component, wsa,
event, gain_reg);
diff --git a/sound/soc/codecs/madera.c b/sound/soc/codecs/madera.c
index b9f19fbd2911..b24d6472ad5f 100644
--- a/sound/soc/codecs/madera.c
+++ b/sound/soc/codecs/madera.c
@@ -3884,7 +3884,7 @@ static inline int madera_set_fll_clks(struct madera_fll *fll, int base, bool ena
return madera_set_fll_clks_reg(fll, ena,
base + MADERA_FLL_CONTROL_6_OFFS,
MADERA_FLL1_REFCLK_SRC_MASK,
- MADERA_FLL1_REFCLK_DIV_SHIFT);
+ MADERA_FLL1_REFCLK_SRC_SHIFT);
}
static inline int madera_set_fllao_clks(struct madera_fll *fll, int base, bool ena)
diff --git a/sound/soc/codecs/nau8810.c b/sound/soc/codecs/nau8810.c
index 47f000cd4d99..97a54059474c 100644
--- a/sound/soc/codecs/nau8810.c
+++ b/sound/soc/codecs/nau8810.c
@@ -169,6 +169,7 @@ static int nau8810_eq_get(struct snd_kcontrol *kcontrol,
struct soc_bytes_ext *params = (void *)kcontrol->private_value;
int i, reg, reg_val;
u16 *val;
+ __be16 tmp;
val = (u16 *)ucontrol->value.bytes.data;
reg = NAU8810_REG_EQ1;
@@ -177,8 +178,8 @@ static int nau8810_eq_get(struct snd_kcontrol *kcontrol,
/* conversion of 16-bit integers between native CPU format
* and big endian format
*/
- reg_val = cpu_to_be16(reg_val);
- memcpy(val + i, &reg_val, sizeof(reg_val));
+ tmp = cpu_to_be16(reg_val);
+ memcpy(val + i, &tmp, sizeof(tmp));
}
return 0;
@@ -201,6 +202,7 @@ static int nau8810_eq_put(struct snd_kcontrol *kcontrol,
void *data;
u16 *val, value;
int i, reg, ret;
+ __be16 *tmp;
data = kmemdup(ucontrol->value.bytes.data,
params->max, GFP_KERNEL | GFP_DMA);
@@ -213,7 +215,8 @@ static int nau8810_eq_put(struct snd_kcontrol *kcontrol,
/* conversion of 16-bit integers between native CPU format
* and big endian format
*/
- value = be16_to_cpu(*(val + i));
+ tmp = (__be16 *)(val + i);
+ value = be16_to_cpup(tmp);
ret = regmap_write(nau8810->regmap, reg + i, value);
if (ret) {
dev_err(component->dev, "EQ configuration fail, register: %x ret: %d\n",
diff --git a/sound/soc/codecs/nau8821.c b/sound/soc/codecs/nau8821.c
index 6e1b6b26298a..012e347e6391 100644
--- a/sound/soc/codecs/nau8821.c
+++ b/sound/soc/codecs/nau8821.c
@@ -1738,6 +1738,10 @@ static int nau8821_read_device_properties(struct device *dev,
&nau8821->dmic_clk_threshold);
if (ret)
nau8821->dmic_clk_threshold = 3072000;
+ ret = device_property_read_u32(dev, "nuvoton,dmic-slew-rate",
+ &nau8821->dmic_slew_rate);
+ if (ret)
+ nau8821->dmic_slew_rate = 0;
return 0;
}
@@ -1797,6 +1801,9 @@ static void nau8821_init_regs(struct nau8821 *nau8821)
NAU8821_ADC_SYNC_DOWN_MASK, NAU8821_ADC_SYNC_DOWN_64);
regmap_update_bits(regmap, NAU8821_R2C_DAC_CTRL1,
NAU8821_DAC_OVERSAMPLE_MASK, NAU8821_DAC_OVERSAMPLE_64);
+ regmap_update_bits(regmap, NAU8821_R13_DMIC_CTRL,
+ NAU8821_DMIC_SLEW_MASK, nau8821->dmic_slew_rate <<
+ NAU8821_DMIC_SLEW_SFT);
if (nau8821->left_input_single_end) {
regmap_update_bits(regmap, NAU8821_R6B_PGA_MUTE,
NAU8821_MUTE_MICNL_EN, NAU8821_MUTE_MICNL_EN);
diff --git a/sound/soc/codecs/nau8821.h b/sound/soc/codecs/nau8821.h
index 00a888ed07ce..62eaad130b2e 100644
--- a/sound/soc/codecs/nau8821.h
+++ b/sound/soc/codecs/nau8821.h
@@ -236,6 +236,8 @@
#define NAU8821_DMIC_SRC_MASK (0x3 << NAU8821_DMIC_SRC_SFT)
#define NAU8821_CLK_DMIC_SRC (0x2 << NAU8821_DMIC_SRC_SFT)
#define NAU8821_DMIC_EN_SFT 0
+#define NAU8821_DMIC_SLEW_SFT 8
+#define NAU8821_DMIC_SLEW_MASK (0x7 << NAU8821_DMIC_SLEW_SFT)
/* GPIO12_CTRL (0x1a) */
#define NAU8821_JKDET_PULL_UP (0x1 << 11) /* 0 - pull down, 1 - pull up */
@@ -573,6 +575,7 @@ struct nau8821 {
int jack_eject_debounce;
int fs;
int dmic_clk_threshold;
+ int dmic_slew_rate;
int key_enable;
};
diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c
index a0d01d71d8b5..20191a4473c2 100644
--- a/sound/soc/codecs/rt5645.c
+++ b/sound/soc/codecs/rt5645.c
@@ -428,6 +428,9 @@ struct rt5645_platform_data {
/* Invert HP detect status polarity */
bool inv_hp_pol;
+ /* Only 1 speaker connected */
+ bool mono_speaker;
+
/* Value to assign to snd_soc_card.long_name */
const char *long_name;
@@ -3314,6 +3317,7 @@ static void rt5645_jack_detect_work(struct work_struct *work)
report, SND_JACK_HEADPHONE);
snd_soc_jack_report(rt5645->mic_jack,
report, SND_JACK_MICROPHONE);
+ mutex_unlock(&rt5645->jd_mutex);
return;
case 4:
val = snd_soc_component_read(rt5645->component, RT5645_A_JD_CTRL1) & 0x0020;
@@ -3660,6 +3664,7 @@ static const struct rt5645_platform_data buddy_platform_data = {
static const struct rt5645_platform_data gpd_win_platform_data = {
.jd_mode = 3,
.inv_jd1_1 = true,
+ .mono_speaker = true,
.long_name = "gpd-win-pocket-rt5645",
/* The GPD pocket has a diff. mic, for the win this does not matter. */
.in2_diff = true,
@@ -3683,6 +3688,16 @@ static const struct rt5645_platform_data lenovo_ideapad_miix_310_pdata = {
.in2_diff = true,
};
+static const struct rt5645_platform_data jd_mode3_monospk_platform_data = {
+ .jd_mode = 3,
+ .mono_speaker = true,
+};
+
+static const struct rt5645_platform_data jd_mode3_inv_data = {
+ .jd_mode = 3,
+ .inv_jd1_1 = true,
+};
+
static const struct rt5645_platform_data jd_mode3_platform_data = {
.jd_mode = 3,
};
@@ -3802,7 +3817,7 @@ static const struct dmi_system_id dmi_platform_data[] = {
DMI_MATCH(DMI_SYS_VENDOR, "TECLAST"),
DMI_MATCH(DMI_PRODUCT_NAME, "X80 Pro"),
},
- .driver_data = (void *)&jd_mode3_platform_data,
+ .driver_data = (void *)&jd_mode3_monospk_platform_data,
},
{
.ident = "Lenovo Ideapad Miix 310",
@@ -3828,6 +3843,16 @@ static const struct dmi_system_id dmi_platform_data[] = {
DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
DMI_EXACT_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"),
DMI_EXACT_MATCH(DMI_BOARD_VERSION, "Default string"),
+ /*
+ * Above strings are too generic, LattePanda BIOS versions for
+ * all 4 hw revisions are:
+ * DF-BI-7-S70CR100-*
+ * DF-BI-7-S70CR110-*
+ * DF-BI-7-S70CR200-*
+ * LP-BS-7-S70CR700-*
+ * Do a partial match for S70CR to avoid false positive matches.
+ */
+ DMI_MATCH(DMI_BIOS_VERSION, "S70CR"),
},
.driver_data = (void *)&lattepanda_board_platform_data,
},
@@ -3855,12 +3880,22 @@ static const struct dmi_system_id dmi_platform_data[] = {
.driver_data = (void *)&ecs_ef20_platform_data,
},
{
- .ident = "EF20EA",
- .callback = cht_rt5645_ef20_quirk_cb,
+ .ident = "Acer Switch V 10 (SW5-017)",
.matches = {
- DMI_MATCH(DMI_PRODUCT_NAME, "EF20EA"),
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "SW5-017"),
},
- .driver_data = (void *)&ecs_ef20_platform_data,
+ .driver_data = (void *)&intel_braswell_platform_data,
+ },
+ {
+ .ident = "Meegopad T08",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Default string"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Default string"),
+ DMI_MATCH(DMI_BOARD_NAME, "T3 MRD"),
+ DMI_MATCH(DMI_BOARD_VERSION, "V1.1"),
+ },
+ .driver_data = (void *)&jd_mode3_inv_data,
},
{ }
};
@@ -3876,24 +3911,68 @@ static bool rt5645_check_dp(struct device *dev)
return false;
}
-static int rt5645_parse_dt(struct rt5645_priv *rt5645, struct device *dev)
+static void rt5645_parse_dt(struct device *dev, struct rt5645_platform_data *pdata)
{
- rt5645->pdata.in2_diff = device_property_read_bool(dev,
- "realtek,in2-differential");
- device_property_read_u32(dev,
- "realtek,dmic1-data-pin", &rt5645->pdata.dmic1_data_pin);
- device_property_read_u32(dev,
- "realtek,dmic2-data-pin", &rt5645->pdata.dmic2_data_pin);
- device_property_read_u32(dev,
- "realtek,jd-mode", &rt5645->pdata.jd_mode);
+ pdata->in2_diff = device_property_read_bool(dev, "realtek,in2-differential");
+ device_property_read_u32(dev, "realtek,dmic1-data-pin", &pdata->dmic1_data_pin);
+ device_property_read_u32(dev, "realtek,dmic2-data-pin", &pdata->dmic2_data_pin);
+ device_property_read_u32(dev, "realtek,jd-mode", &pdata->jd_mode);
+}
- return 0;
+static void rt5645_get_pdata(struct device *codec_dev, struct rt5645_platform_data *pdata)
+{
+ const struct dmi_system_id *dmi_data;
+
+ dmi_data = dmi_first_match(dmi_platform_data);
+ if (dmi_data) {
+ dev_info(codec_dev, "Detected %s platform\n", dmi_data->ident);
+ *pdata = *((struct rt5645_platform_data *)dmi_data->driver_data);
+ } else if (rt5645_check_dp(codec_dev)) {
+ rt5645_parse_dt(codec_dev, pdata);
+ } else {
+ *pdata = jd_mode3_platform_data;
+ }
+
+ if (quirk != -1) {
+ pdata->in2_diff = QUIRK_IN2_DIFF(quirk);
+ pdata->level_trigger_irq = QUIRK_LEVEL_IRQ(quirk);
+ pdata->inv_jd1_1 = QUIRK_INV_JD1_1(quirk);
+ pdata->inv_hp_pol = QUIRK_INV_HP_POL(quirk);
+ pdata->jd_mode = QUIRK_JD_MODE(quirk);
+ pdata->dmic1_data_pin = QUIRK_DMIC1_DATA_PIN(quirk);
+ pdata->dmic2_data_pin = QUIRK_DMIC2_DATA_PIN(quirk);
+ }
+}
+
+const char *rt5645_components(struct device *codec_dev)
+{
+ struct rt5645_platform_data pdata = { };
+ static char buf[32];
+ const char *mic;
+ int spk = 2;
+
+ rt5645_get_pdata(codec_dev, &pdata);
+
+ if (pdata.mono_speaker)
+ spk = 1;
+
+ if (pdata.dmic1_data_pin && pdata.dmic2_data_pin)
+ mic = "dmics12";
+ else if (pdata.dmic1_data_pin)
+ mic = "dmic1";
+ else if (pdata.dmic2_data_pin)
+ mic = "dmic2";
+ else
+ mic = "in2";
+
+ snprintf(buf, sizeof(buf), "cfg-spk:%d cfg-mic:%s", spk, mic);
+
+ return buf;
}
+EXPORT_SYMBOL_GPL(rt5645_components);
static int rt5645_i2c_probe(struct i2c_client *i2c)
{
- struct rt5645_platform_data *pdata = NULL;
- const struct dmi_system_id *dmi_data;
struct rt5645_priv *rt5645;
int ret, i;
unsigned int val;
@@ -3906,29 +3985,7 @@ static int rt5645_i2c_probe(struct i2c_client *i2c)
rt5645->i2c = i2c;
i2c_set_clientdata(i2c, rt5645);
-
- dmi_data = dmi_first_match(dmi_platform_data);
- if (dmi_data) {
- dev_info(&i2c->dev, "Detected %s platform\n", dmi_data->ident);
- pdata = dmi_data->driver_data;
- }
-
- if (pdata)
- rt5645->pdata = *pdata;
- else if (rt5645_check_dp(&i2c->dev))
- rt5645_parse_dt(rt5645, &i2c->dev);
- else
- rt5645->pdata = jd_mode3_platform_data;
-
- if (quirk != -1) {
- rt5645->pdata.in2_diff = QUIRK_IN2_DIFF(quirk);
- rt5645->pdata.level_trigger_irq = QUIRK_LEVEL_IRQ(quirk);
- rt5645->pdata.inv_jd1_1 = QUIRK_INV_JD1_1(quirk);
- rt5645->pdata.inv_hp_pol = QUIRK_INV_HP_POL(quirk);
- rt5645->pdata.jd_mode = QUIRK_JD_MODE(quirk);
- rt5645->pdata.dmic1_data_pin = QUIRK_DMIC1_DATA_PIN(quirk);
- rt5645->pdata.dmic2_data_pin = QUIRK_DMIC2_DATA_PIN(quirk);
- }
+ rt5645_get_pdata(&i2c->dev, &rt5645->pdata);
if (has_acpi_companion(&i2c->dev)) {
if (cht_rt5645_gpios) {
diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h
index ac3de6f3bc2f..90816b2c5489 100644
--- a/sound/soc/codecs/rt5645.h
+++ b/sound/soc/codecs/rt5645.h
@@ -2201,4 +2201,7 @@ int rt5645_sel_asrc_clk_src(struct snd_soc_component *component,
int rt5645_set_jack_detect(struct snd_soc_component *component,
struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack,
struct snd_soc_jack *btn_jack);
+
+const char *rt5645_components(struct device *codec_dev);
+
#endif /* __RT5645_H__ */
diff --git a/sound/soc/codecs/rt5663.c b/sound/soc/codecs/rt5663.c
index 77246f84de29..9550492605ac 100644
--- a/sound/soc/codecs/rt5663.c
+++ b/sound/soc/codecs/rt5663.c
@@ -74,6 +74,7 @@ struct rt5663_priv {
int pll_out;
int jack_type;
+ unsigned int irq;
};
static const struct reg_sequence rt5663_patch_list[] = {
@@ -3186,6 +3187,12 @@ static int rt5663_suspend(struct snd_soc_component *component)
{
struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component);
+ if (rt5663->irq)
+ disable_irq(rt5663->irq);
+
+ cancel_delayed_work_sync(&rt5663->jack_detect_work);
+ cancel_delayed_work_sync(&rt5663->jd_unplug_work);
+
regcache_cache_only(rt5663->regmap, true);
regcache_mark_dirty(rt5663->regmap);
@@ -3201,6 +3208,9 @@ static int rt5663_resume(struct snd_soc_component *component)
rt5663_irq(0, rt5663);
+ if (rt5663->irq)
+ enable_irq(rt5663->irq);
+
return 0;
}
#else
@@ -3686,6 +3696,7 @@ static int rt5663_i2c_probe(struct i2c_client *i2c)
__func__, ret);
goto err_enable;
}
+ rt5663->irq = i2c->irq;
}
ret = devm_snd_soc_register_component(&i2c->dev,
diff --git a/sound/soc/codecs/rt5682s.c b/sound/soc/codecs/rt5682s.c
index c261c33c4be7..3322056bbb3b 100644
--- a/sound/soc/codecs/rt5682s.c
+++ b/sound/soc/codecs/rt5682s.c
@@ -2971,6 +2971,8 @@ static int rt5682s_parse_dt(struct rt5682s_priv *rt5682s, struct device *dev)
&rt5682s->pdata.dmic_delay);
device_property_read_u32(dev, "realtek,amic-delay-ms",
&rt5682s->pdata.amic_delay);
+ device_property_read_u32(dev, "realtek,ldo-sel",
+ &rt5682s->pdata.ldo_dacref);
if (device_property_read_string_array(dev, "clock-output-names",
rt5682s->pdata.dai_clk_names,
@@ -3250,6 +3252,27 @@ static int rt5682s_i2c_probe(struct i2c_client *i2c)
break;
}
+ /* LDO output voltage control */
+ switch (rt5682s->pdata.ldo_dacref) {
+ case RT5682S_LDO_1_607V:
+ break;
+ case RT5682S_LDO_1_5V:
+ regmap_update_bits(rt5682s->regmap, RT5682S_BIAS_CUR_CTRL_7,
+ RT5682S_LDO_DACREF_MASK, RT5682S_LDO_DACREF_1_5V);
+ break;
+ case RT5682S_LDO_1_406V:
+ regmap_update_bits(rt5682s->regmap, RT5682S_BIAS_CUR_CTRL_7,
+ RT5682S_LDO_DACREF_MASK, RT5682S_LDO_DACREF_1_406V);
+ break;
+ case RT5682S_LDO_1_731V:
+ regmap_update_bits(rt5682s->regmap, RT5682S_BIAS_CUR_CTRL_7,
+ RT5682S_LDO_DACREF_MASK, RT5682S_LDO_DACREF_1_731V);
+ break;
+ default:
+ dev_warn(&i2c->dev, "invalid LDO output setting.\n");
+ break;
+ }
+
INIT_DELAYED_WORK(&rt5682s->jack_detect_work, rt5682s_jack_detect_handler);
INIT_DELAYED_WORK(&rt5682s->jd_check_work, rt5682s_jd_check_handler);
diff --git a/sound/soc/codecs/rt5682s.h b/sound/soc/codecs/rt5682s.h
index 1d79d432d0d8..67f42898de96 100644
--- a/sound/soc/codecs/rt5682s.h
+++ b/sound/soc/codecs/rt5682s.h
@@ -1263,6 +1263,13 @@
#define RT5682S_JDH_NO_PLUG (0x1 << 4)
#define RT5682S_JDH_PLUG (0x0 << 4)
+/* Bias current control 7 (0x0110) */
+#define RT5682S_LDO_DACREF_MASK (0x3 << 4)
+#define RT5682S_LDO_DACREF_1_607V (0x0 << 4)
+#define RT5682S_LDO_DACREF_1_5V (0x1 << 4)
+#define RT5682S_LDO_DACREF_1_406V (0x2 << 4)
+#define RT5682S_LDO_DACREF_1_731V (0x3 << 4)
+
/* Charge Pump Internal Register1 (0x0125) */
#define RT5682S_CP_CLK_HP_MASK (0x3 << 4)
#define RT5682S_CP_CLK_HP_100KHZ (0x0 << 4)
diff --git a/sound/soc/codecs/rt722-sdca-sdw.c b/sound/soc/codecs/rt722-sdca-sdw.c
index a38ec5862214..e24b9cbdc10c 100644
--- a/sound/soc/codecs/rt722-sdca-sdw.c
+++ b/sound/soc/codecs/rt722-sdca-sdw.c
@@ -256,6 +256,9 @@ static int rt722_sdca_read_prop(struct sdw_slave *slave)
/* wake-up event */
prop->wake_capable = 1;
+ /* Three data lanes are supported by rt722-sdca codec */
+ prop->lane_control_support = true;
+
return 0;
}
diff --git a/sound/soc/codecs/rtq9128.c b/sound/soc/codecs/rtq9128.c
index c22b047115cc..aa3eadecd974 100644
--- a/sound/soc/codecs/rtq9128.c
+++ b/sound/soc/codecs/rtq9128.c
@@ -59,6 +59,7 @@
struct rtq9128_data {
struct gpio_desc *enable;
+ unsigned int daifmt;
int tdm_slots;
int tdm_slot_width;
bool tdm_input_data2_select;
@@ -391,7 +392,11 @@ static int rtq9128_component_probe(struct snd_soc_component *comp)
unsigned int val;
int i, ret;
- pm_runtime_resume_and_get(comp->dev);
+ ret = pm_runtime_resume_and_get(comp->dev);
+ if (ret < 0) {
+ dev_err(comp->dev, "Failed to resume device (%d)\n", ret);
+ return ret;
+ }
val = snd_soc_component_read(comp, RTQ9128_REG_EFUSE_DATA);
@@ -437,10 +442,7 @@ static const struct snd_soc_component_driver rtq9128_comp_driver = {
static int rtq9128_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct rtq9128_data *data = snd_soc_dai_get_drvdata(dai);
- struct snd_soc_component *comp = dai->component;
struct device *dev = dai->dev;
- unsigned int audfmt, fmtval;
- int ret;
dev_dbg(dev, "%s: fmt 0x%8x\n", __func__, fmt);
@@ -450,35 +452,10 @@ static int rtq9128_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return -EINVAL;
}
- fmtval = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
- if (data->tdm_slots && fmtval != SND_SOC_DAIFMT_DSP_A && fmtval != SND_SOC_DAIFMT_DSP_B) {
- dev_err(dev, "TDM is used, format only support DSP_A or DSP_B\n");
- return -EINVAL;
- }
+ /* Store here and will be used in runtime hw_params for DAI format setting */
+ data->daifmt = fmt;
- switch (fmtval) {
- case SND_SOC_DAIFMT_I2S:
- audfmt = 8;
- break;
- case SND_SOC_DAIFMT_LEFT_J:
- audfmt = 9;
- break;
- case SND_SOC_DAIFMT_RIGHT_J:
- audfmt = 10;
- break;
- case SND_SOC_DAIFMT_DSP_A:
- audfmt = data->tdm_slots ? 12 : 11;
- break;
- case SND_SOC_DAIFMT_DSP_B:
- audfmt = data->tdm_slots ? 4 : 3;
- break;
- default:
- dev_err(dev, "Unsupported format 0x%8x\n", fmt);
- return -EINVAL;
- }
-
- ret = snd_soc_component_write_field(comp, RTQ9128_REG_I2S_OPT, RTQ9128_AUDFMT_MASK, audfmt);
- return ret < 0 ? ret : 0;
+ return 0;
}
static int rtq9128_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
@@ -554,10 +531,38 @@ static int rtq9128_dai_hw_params(struct snd_pcm_substream *stream, struct snd_pc
unsigned int width, slot_width, bitrate, audbit, dolen;
struct snd_soc_component *comp = dai->component;
struct device *dev = dai->dev;
+ unsigned int fmtval, audfmt;
int ret;
dev_dbg(dev, "%s: width %d\n", __func__, params_width(param));
+ fmtval = FIELD_GET(SND_SOC_DAIFMT_FORMAT_MASK, data->daifmt);
+ if (data->tdm_slots && fmtval != SND_SOC_DAIFMT_DSP_A && fmtval != SND_SOC_DAIFMT_DSP_B) {
+ dev_err(dev, "TDM is used, format only support DSP_A or DSP_B\n");
+ return -EINVAL;
+ }
+
+ switch (fmtval) {
+ case SND_SOC_DAIFMT_I2S:
+ audfmt = 8;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ audfmt = 9;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ audfmt = 10;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ audfmt = data->tdm_slots ? 12 : 11;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ audfmt = data->tdm_slots ? 4 : 3;
+ break;
+ default:
+ dev_err(dev, "Unsupported format 0x%8x\n", fmtval);
+ return -EINVAL;
+ }
+
switch (width = params_width(param)) {
case 16:
audbit = 0;
@@ -611,6 +616,10 @@ static int rtq9128_dai_hw_params(struct snd_pcm_substream *stream, struct snd_pc
return -EINVAL;
}
+ ret = snd_soc_component_write_field(comp, RTQ9128_REG_I2S_OPT, RTQ9128_AUDFMT_MASK, audfmt);
+ if (ret < 0)
+ return ret;
+
ret = snd_soc_component_write_field(comp, RTQ9128_REG_I2S_OPT, RTQ9128_AUDBIT_MASK, audbit);
if (ret < 0)
return ret;
diff --git a/sound/soc/codecs/tas2562.c b/sound/soc/codecs/tas2562.c
index 962c2cdfa017..54561ae598b8 100644
--- a/sound/soc/codecs/tas2562.c
+++ b/sound/soc/codecs/tas2562.c
@@ -59,7 +59,6 @@ struct tas2562_data {
enum tas256x_model {
TAS2562,
- TAS2563,
TAS2564,
TAS2110,
};
@@ -721,7 +720,6 @@ static int tas2562_parse_dt(struct tas2562_data *tas2562)
static const struct i2c_device_id tas2562_id[] = {
{ "tas2562", TAS2562 },
- { "tas2563", TAS2563 },
{ "tas2564", TAS2564 },
{ "tas2110", TAS2110 },
{ }
@@ -770,7 +768,6 @@ static int tas2562_probe(struct i2c_client *client)
#ifdef CONFIG_OF
static const struct of_device_id tas2562_of_match[] = {
{ .compatible = "ti,tas2562", },
- { .compatible = "ti,tas2563", },
{ .compatible = "ti,tas2564", },
{ .compatible = "ti,tas2110", },
{ },
diff --git a/sound/soc/codecs/tas2781-comlib.c b/sound/soc/codecs/tas2781-comlib.c
index 00e35169ae49..5d0e5348b361 100644
--- a/sound/soc/codecs/tas2781-comlib.c
+++ b/sound/soc/codecs/tas2781-comlib.c
@@ -267,6 +267,7 @@ void tas2781_reset(struct tasdevice_priv *tas_dev)
EXPORT_SYMBOL_GPL(tas2781_reset);
int tascodec_init(struct tasdevice_priv *tas_priv, void *codec,
+ struct module *module,
void (*cont)(const struct firmware *fw, void *context))
{
int ret = 0;
@@ -280,7 +281,7 @@ int tascodec_init(struct tasdevice_priv *tas_priv, void *codec,
tas_priv->dev_name, tas_priv->ndev);
crc8_populate_msb(tas_priv->crc8_lkp_tbl, TASDEVICE_CRC8_POLYNOMIAL);
tas_priv->codec = codec;
- ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT,
+ ret = request_firmware_nowait(module, FW_ACTION_UEVENT,
tas_priv->rca_binaryname, tas_priv->dev, GFP_KERNEL, tas_priv,
cont);
if (ret)
@@ -412,6 +413,21 @@ void tasdevice_remove(struct tasdevice_priv *tas_priv)
}
EXPORT_SYMBOL_GPL(tasdevice_remove);
+int tasdevice_save_calibration(struct tasdevice_priv *tas_priv)
+{
+ if (tas_priv->save_calibration)
+ return tas_priv->save_calibration(tas_priv);
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(tasdevice_save_calibration);
+
+void tasdevice_apply_calibration(struct tasdevice_priv *tas_priv)
+{
+ if (tas_priv->apply_calibration && tas_priv->cali_data.total_sz)
+ tas_priv->apply_calibration(tas_priv);
+}
+EXPORT_SYMBOL_GPL(tasdevice_apply_calibration);
+
static int tasdevice_clamp(int val, int max, unsigned int invert)
{
if (val > max)
diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib.c
index 5c09e441a936..85e14ff61769 100644
--- a/sound/soc/codecs/tas2781-fmwlib.c
+++ b/sound/soc/codecs/tas2781-fmwlib.c
@@ -1982,6 +1982,7 @@ static int tasdevice_dspfw_ready(const struct firmware *fmw,
case 0x301:
case 0x302:
case 0x502:
+ case 0x503:
tas_priv->fw_parse_variable_header =
fw_parse_variable_header_kernel;
tas_priv->fw_parse_program_data =
diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c
index 917b1c15f71d..b5abff230e43 100644
--- a/sound/soc/codecs/tas2781-i2c.c
+++ b/sound/soc/codecs/tas2781-i2c.c
@@ -1,13 +1,13 @@
// SPDX-License-Identifier: GPL-2.0
//
-// ALSA SoC Texas Instruments TAS2781 Audio Smart Amplifier
+// ALSA SoC Texas Instruments TAS2563/TAS2781 Audio Smart Amplifier
//
// Copyright (C) 2022 - 2023 Texas Instruments Incorporated
// https://www.ti.com
//
-// The TAS2781 driver implements a flexible and configurable
+// The TAS2563/TAS2781 driver implements a flexible and configurable
// algo coefficient setting for one, two, or even multiple
-// TAS2781 chips.
+// TAS2563/TAS2781 chips.
//
// Author: Shenghao Ding <shenghao-ding@ti.com>
// Author: Kevin Lu <kevin-lu@ti.com>
@@ -32,6 +32,7 @@
#include <sound/tas2781-tlv.h>
static const struct i2c_device_id tasdevice_id[] = {
+ { "tas2563", TAS2563 },
{ "tas2781", TAS2781 },
{}
};
@@ -39,6 +40,7 @@ MODULE_DEVICE_TABLE(i2c, tasdevice_id);
#ifdef CONFIG_OF
static const struct of_device_id tasdevice_of_match[] = {
+ { .compatible = "ti,tas2563" },
{ .compatible = "ti,tas2781" },
{},
};
@@ -564,7 +566,7 @@ static int tasdevice_codec_probe(struct snd_soc_component *codec)
{
struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
- return tascodec_init(tas_priv, codec, tasdevice_fw_ready);
+ return tascodec_init(tas_priv, codec, THIS_MODULE, tasdevice_fw_ready);
}
static void tasdevice_deinit(void *context)
diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c
index 43c648efd0d9..deb15b95992d 100644
--- a/sound/soc/codecs/wcd9335.c
+++ b/sound/soc/codecs/wcd9335.c
@@ -3033,7 +3033,6 @@ static int wcd9335_codec_enable_mix_path(struct snd_soc_dapm_widget *w,
{
struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
u16 gain_reg;
- int offset_val = 0;
int val = 0;
switch (w->reg) {
@@ -3073,7 +3072,6 @@ static int wcd9335_codec_enable_mix_path(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_POST_PMU:
val = snd_soc_component_read(comp, gain_reg);
- val += offset_val;
snd_soc_component_write(comp, gain_reg, val);
break;
case SND_SOC_DAPM_POST_PMD:
@@ -3294,7 +3292,6 @@ static int wcd9335_codec_enable_interpolator(struct snd_soc_dapm_widget *w,
u16 gain_reg;
u16 reg;
int val;
- int offset_val = 0;
if (!(snd_soc_dapm_widget_name_cmp(w, "RX INT0 INTERP"))) {
reg = WCD9335_CDC_RX0_RX_PATH_CTL;
@@ -3337,7 +3334,6 @@ static int wcd9335_codec_enable_interpolator(struct snd_soc_dapm_widget *w,
case SND_SOC_DAPM_POST_PMU:
wcd9335_config_compander(comp, w->shift, event);
val = snd_soc_component_read(comp, gain_reg);
- val += offset_val;
snd_soc_component_write(comp, gain_reg, val);
break;
case SND_SOC_DAPM_POST_PMD:
diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c
index 1b6e376f3833..6813268e6a19 100644
--- a/sound/soc/codecs/wcd934x.c
+++ b/sound/soc/codecs/wcd934x.c
@@ -13,7 +13,6 @@
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
-#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/slimbus.h>
#include <sound/pcm_params.h>
diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c
index faf8d3f9b3c5..6021aa5a5689 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 DECLARE_TLV_DB_SCALE(line_gain, -3000, 150, -3000);
+static const DECLARE_TLV_DB_SCALE(line_gain, -3000, 150, 0);
static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(analog_gain, 0, 3000);
struct wcd938x_mbhc_zdet_param {
@@ -3587,10 +3587,8 @@ static int wcd938x_probe(struct platform_device *pdev)
mutex_init(&wcd938x->micb_lock);
ret = wcd938x_populate_dt_data(wcd938x, dev);
- if (ret) {
- dev_err(dev, "%s: Fail to obtain platform data\n", __func__);
- return -EINVAL;
- }
+ if (ret)
+ return ret;
ret = wcd938x_add_slave_components(wcd938x, dev, &match);
if (ret)
diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c
index 1d4259433f47..8f862729a2ca 100644
--- a/sound/soc/codecs/wm0010.c
+++ b/sound/soc/codecs/wm0010.c
@@ -18,7 +18,7 @@
#include <linux/firmware.h>
#include <linux/delay.h>
#include <linux/fs.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/regulator/consumer.h>
#include <linux/mutex.h>
#include <linux/workqueue.h>
@@ -94,8 +94,7 @@ struct wm0010_priv {
struct wm0010_pdata pdata;
- int gpio_reset;
- int gpio_reset_value;
+ struct gpio_desc *reset;
struct regulator_bulk_data core_supplies[2];
struct regulator *dbvdd;
@@ -174,8 +173,7 @@ static void wm0010_halt(struct snd_soc_component *component)
case WM0010_STAGE2:
case WM0010_FIRMWARE:
/* Remember to put chip back into reset */
- gpio_set_value_cansleep(wm0010->gpio_reset,
- wm0010->gpio_reset_value);
+ gpiod_set_value_cansleep(wm0010->reset, 1);
/* Disable the regulators */
regulator_disable(wm0010->dbvdd);
regulator_bulk_disable(ARRAY_SIZE(wm0010->core_supplies),
@@ -610,7 +608,7 @@ static int wm0010_boot(struct snd_soc_component *component)
}
/* Release reset */
- gpio_set_value_cansleep(wm0010->gpio_reset, !wm0010->gpio_reset_value);
+ gpiod_set_value_cansleep(wm0010->reset, 0);
spin_lock_irqsave(&wm0010->irq_lock, flags);
wm0010->state = WM0010_OUT_OF_RESET;
spin_unlock_irqrestore(&wm0010->irq_lock, flags);
@@ -863,7 +861,6 @@ static int wm0010_probe(struct snd_soc_component *component)
static int wm0010_spi_probe(struct spi_device *spi)
{
- unsigned long gpio_flags;
int ret;
int trigger;
int irq;
@@ -903,31 +900,11 @@ static int wm0010_spi_probe(struct spi_device *spi)
return ret;
}
- if (wm0010->pdata.gpio_reset) {
- wm0010->gpio_reset = wm0010->pdata.gpio_reset;
-
- if (wm0010->pdata.reset_active_high)
- wm0010->gpio_reset_value = 1;
- else
- wm0010->gpio_reset_value = 0;
-
- if (wm0010->gpio_reset_value)
- gpio_flags = GPIOF_OUT_INIT_HIGH;
- else
- gpio_flags = GPIOF_OUT_INIT_LOW;
-
- ret = devm_gpio_request_one(wm0010->dev, wm0010->gpio_reset,
- gpio_flags, "wm0010 reset");
- if (ret < 0) {
- dev_err(wm0010->dev,
- "Failed to request GPIO for DSP reset: %d\n",
- ret);
- return ret;
- }
- } else {
- dev_err(wm0010->dev, "No reset GPIO configured\n");
- return -EINVAL;
- }
+ wm0010->reset = devm_gpiod_get(wm0010->dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(wm0010->reset))
+ return dev_err_probe(wm0010->dev, PTR_ERR(wm0010->reset),
+ "could not get RESET GPIO\n");
+ gpiod_set_consumer_name(wm0010->reset, "wm0010 reset");
wm0010->state = WM0010_POWER_OFF;
@@ -972,8 +949,7 @@ static void wm0010_spi_remove(struct spi_device *spi)
{
struct wm0010_priv *wm0010 = spi_get_drvdata(spi);
- gpio_set_value_cansleep(wm0010->gpio_reset,
- wm0010->gpio_reset_value);
+ gpiod_set_value_cansleep(wm0010->reset, 1);
irq_set_irq_wake(wm0010->irq, 0);
diff --git a/sound/soc/codecs/wm1250-ev1.c b/sound/soc/codecs/wm1250-ev1.c
index d7eeb41ba60f..9fa6df48799b 100644
--- a/sound/soc/codecs/wm1250-ev1.c
+++ b/sound/soc/codecs/wm1250-ev1.c
@@ -9,34 +9,23 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/i2c.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
-#include <sound/wm1250-ev1.h>
-
-static const char *wm1250_gpio_names[WM1250_EV1_NUM_GPIOS] = {
- "WM1250 CLK_ENA",
- "WM1250 CLK_SEL0",
- "WM1250 CLK_SEL1",
- "WM1250 OSR",
- "WM1250 MASTER",
-};
struct wm1250_priv {
- struct gpio gpios[WM1250_EV1_NUM_GPIOS];
+ struct gpio_desc *clk_ena;
+ struct gpio_desc *clk_sel0;
+ struct gpio_desc *clk_sel1;
+ struct gpio_desc *osr;
+ struct gpio_desc *master;
};
static int wm1250_ev1_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
struct wm1250_priv *wm1250 = dev_get_drvdata(component->dev);
- int ena;
-
- if (wm1250)
- ena = wm1250->gpios[WM1250_EV1_GPIO_CLK_ENA].gpio;
- else
- ena = -1;
switch (level) {
case SND_SOC_BIAS_ON:
@@ -46,13 +35,11 @@ static int wm1250_ev1_set_bias_level(struct snd_soc_component *component,
break;
case SND_SOC_BIAS_STANDBY:
- if (ena >= 0)
- gpio_set_value_cansleep(ena, 1);
+ gpiod_set_value_cansleep(wm1250->clk_ena, 1);
break;
case SND_SOC_BIAS_OFF:
- if (ena >= 0)
- gpio_set_value_cansleep(ena, 0);
+ gpiod_set_value_cansleep(wm1250->clk_ena, 0);
break;
}
@@ -80,28 +67,20 @@ static int wm1250_ev1_hw_params(struct snd_pcm_substream *substream,
switch (params_rate(params)) {
case 8000:
- gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL0].gpio,
- 1);
- gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL1].gpio,
- 1);
+ gpiod_set_value(wm1250->clk_sel0, 1);
+ gpiod_set_value(wm1250->clk_sel1, 1);
break;
case 16000:
- gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL0].gpio,
- 0);
- gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL1].gpio,
- 1);
+ gpiod_set_value(wm1250->clk_sel0, 0);
+ gpiod_set_value(wm1250->clk_sel1, 1);
break;
case 32000:
- gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL0].gpio,
- 1);
- gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL1].gpio,
- 0);
+ gpiod_set_value(wm1250->clk_sel0, 1);
+ gpiod_set_value(wm1250->clk_sel1, 0);
break;
case 64000:
- gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL0].gpio,
- 0);
- gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL1].gpio,
- 0);
+ gpiod_set_value(wm1250->clk_sel0, 0);
+ gpiod_set_value(wm1250->clk_sel1, 0);
break;
default:
return -EINVAL;
@@ -150,45 +129,42 @@ static int wm1250_ev1_pdata(struct i2c_client *i2c)
{
struct wm1250_ev1_pdata *pdata = dev_get_platdata(&i2c->dev);
struct wm1250_priv *wm1250;
- int i, ret;
if (!pdata)
return 0;
wm1250 = devm_kzalloc(&i2c->dev, sizeof(*wm1250), GFP_KERNEL);
- if (!wm1250) {
- ret = -ENOMEM;
- goto err;
- }
-
- for (i = 0; i < ARRAY_SIZE(wm1250->gpios); i++) {
- wm1250->gpios[i].gpio = pdata->gpios[i];
- wm1250->gpios[i].label = wm1250_gpio_names[i];
- wm1250->gpios[i].flags = GPIOF_OUT_INIT_LOW;
- }
- wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL0].flags = GPIOF_OUT_INIT_HIGH;
- wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL1].flags = GPIOF_OUT_INIT_HIGH;
-
- ret = gpio_request_array(wm1250->gpios, ARRAY_SIZE(wm1250->gpios));
- if (ret != 0) {
- dev_err(&i2c->dev, "Failed to get GPIOs: %d\n", ret);
- goto err;
- }
+ if (!wm1250)
+ return -ENOMEM;
+
+ wm1250->clk_ena = devm_gpiod_get(&i2c->dev, "clk-ena", GPIOD_OUT_LOW);
+ if (IS_ERR(wm1250->clk_ena))
+ return dev_err_probe(&i2c->dev, PTR_ERR(wm1250->clk_ena),
+ "failed to get clock enable GPIO\n");
+
+ wm1250->clk_sel0 = devm_gpiod_get(&i2c->dev, "clk-sel0", GPIOD_OUT_HIGH);
+ if (IS_ERR(wm1250->clk_sel0))
+ return dev_err_probe(&i2c->dev, PTR_ERR(wm1250->clk_sel0),
+ "failed to get clock sel0 GPIO\n");
+
+ wm1250->clk_sel1 = devm_gpiod_get(&i2c->dev, "clk-sel1", GPIOD_OUT_HIGH);
+ if (IS_ERR(wm1250->clk_sel1))
+ return dev_err_probe(&i2c->dev, PTR_ERR(wm1250->clk_sel1),
+ "failed to get clock sel1 GPIO\n");
+
+ wm1250->osr = devm_gpiod_get(&i2c->dev, "osr", GPIOD_OUT_LOW);
+ if (IS_ERR(wm1250->osr))
+ return dev_err_probe(&i2c->dev, PTR_ERR(wm1250->osr),
+ "failed to get OSR GPIO\n");
+
+ wm1250->master = devm_gpiod_get(&i2c->dev, "master", GPIOD_OUT_LOW);
+ if (IS_ERR(wm1250->master))
+ return dev_err_probe(&i2c->dev, PTR_ERR(wm1250->master),
+ "failed to get MASTER GPIO\n");
dev_set_drvdata(&i2c->dev, wm1250);
- return ret;
-
-err:
- return ret;
-}
-
-static void wm1250_ev1_free(struct i2c_client *i2c)
-{
- struct wm1250_priv *wm1250 = dev_get_drvdata(&i2c->dev);
-
- if (wm1250)
- gpio_free_array(wm1250->gpios, ARRAY_SIZE(wm1250->gpios));
+ return 0;
}
static int wm1250_ev1_probe(struct i2c_client *i2c)
@@ -221,18 +197,12 @@ static int wm1250_ev1_probe(struct i2c_client *i2c)
&wm1250_ev1_dai, 1);
if (ret != 0) {
dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret);
- wm1250_ev1_free(i2c);
return ret;
}
return 0;
}
-static void wm1250_ev1_remove(struct i2c_client *i2c)
-{
- wm1250_ev1_free(i2c);
-}
-
static const struct i2c_device_id wm1250_ev1_i2c_id[] = {
{ "wm1250-ev1", 0 },
{ }
@@ -244,7 +214,6 @@ static struct i2c_driver wm1250_ev1_i2c_driver = {
.name = "wm1250-ev1",
},
.probe = wm1250_ev1_probe,
- .remove = wm1250_ev1_remove,
.id_table = wm1250_ev1_i2c_id,
};
diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c
index 9679906c6bd5..69c9c2bd7e7b 100644
--- a/sound/soc/codecs/wm2200.c
+++ b/sound/soc/codecs/wm2200.c
@@ -14,7 +14,7 @@
#include <linux/pm.h>
#include <linux/firmware.h>
#include <linux/gcd.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
@@ -79,6 +79,8 @@ struct wm2200_priv {
struct snd_soc_component *component;
struct wm2200_pdata pdata;
struct regulator_bulk_data core_supplies[WM2200_NUM_CORE_SUPPLIES];
+ struct gpio_desc *ldo_ena;
+ struct gpio_desc *reset;
struct completion fll_lock;
int fll_fout;
@@ -975,9 +977,10 @@ static const struct reg_sequence wm2200_reva_patch[] = {
static int wm2200_reset(struct wm2200_priv *wm2200)
{
- if (wm2200->pdata.reset) {
- gpio_set_value_cansleep(wm2200->pdata.reset, 0);
- gpio_set_value_cansleep(wm2200->pdata.reset, 1);
+ if (wm2200->reset) {
+ /* Descriptor flagged active low, so this will be inverted */
+ gpiod_set_value_cansleep(wm2200->reset, 1);
+ gpiod_set_value_cansleep(wm2200->reset, 0);
return 0;
} else {
@@ -2246,28 +2249,28 @@ static int wm2200_i2c_probe(struct i2c_client *i2c)
return ret;
}
- if (wm2200->pdata.ldo_ena) {
- ret = devm_gpio_request_one(&i2c->dev, wm2200->pdata.ldo_ena,
- GPIOF_OUT_INIT_HIGH,
- "WM2200 LDOENA");
- if (ret < 0) {
- dev_err(&i2c->dev, "Failed to request LDOENA %d: %d\n",
- wm2200->pdata.ldo_ena, ret);
- goto err_enable;
- }
+ wm2200->ldo_ena = devm_gpiod_get_optional(&i2c->dev, "wlf,ldo1ena",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(wm2200->ldo_ena)) {
+ ret = PTR_ERR(wm2200->ldo_ena);
+ dev_err(&i2c->dev, "Failed to request LDOENA GPIO %d\n",
+ ret);
+ goto err_enable;
+ }
+ if (wm2200->ldo_ena) {
+ gpiod_set_consumer_name(wm2200->ldo_ena, "WM2200 LDOENA");
msleep(2);
}
- if (wm2200->pdata.reset) {
- ret = devm_gpio_request_one(&i2c->dev, wm2200->pdata.reset,
- GPIOF_OUT_INIT_HIGH,
- "WM2200 /RESET");
- if (ret < 0) {
- dev_err(&i2c->dev, "Failed to request /RESET %d: %d\n",
- wm2200->pdata.reset, ret);
- goto err_ldo;
- }
+ wm2200->reset = devm_gpiod_get_optional(&i2c->dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(wm2200->reset)) {
+ ret = PTR_ERR(wm2200->reset);
+ dev_err(&i2c->dev, "Failed to request RESET GPIO %d\n",
+ ret);
+ goto err_ldo;
}
+ gpiod_set_consumer_name(wm2200->reset, "WM2200 /RESET");
ret = regmap_read(wm2200->regmap, WM2200_SOFTWARE_RESET, &reg);
if (ret < 0) {
@@ -2403,11 +2406,9 @@ err_pm_runtime:
if (i2c->irq)
free_irq(i2c->irq, wm2200);
err_reset:
- if (wm2200->pdata.reset)
- gpio_set_value_cansleep(wm2200->pdata.reset, 0);
+ gpiod_set_value_cansleep(wm2200->reset, 1);
err_ldo:
- if (wm2200->pdata.ldo_ena)
- gpio_set_value_cansleep(wm2200->pdata.ldo_ena, 0);
+ gpiod_set_value_cansleep(wm2200->ldo_ena, 0);
err_enable:
regulator_bulk_disable(ARRAY_SIZE(wm2200->core_supplies),
wm2200->core_supplies);
@@ -2421,10 +2422,9 @@ static void wm2200_i2c_remove(struct i2c_client *i2c)
pm_runtime_disable(&i2c->dev);
if (i2c->irq)
free_irq(i2c->irq, wm2200);
- if (wm2200->pdata.reset)
- gpio_set_value_cansleep(wm2200->pdata.reset, 0);
- if (wm2200->pdata.ldo_ena)
- gpio_set_value_cansleep(wm2200->pdata.ldo_ena, 0);
+ /* Assert RESET, disable LDO */
+ gpiod_set_value_cansleep(wm2200->reset, 1);
+ gpiod_set_value_cansleep(wm2200->ldo_ena, 0);
regulator_bulk_disable(ARRAY_SIZE(wm2200->core_supplies),
wm2200->core_supplies);
}
@@ -2436,8 +2436,7 @@ static int wm2200_runtime_suspend(struct device *dev)
regcache_cache_only(wm2200->regmap, true);
regcache_mark_dirty(wm2200->regmap);
- if (wm2200->pdata.ldo_ena)
- gpio_set_value_cansleep(wm2200->pdata.ldo_ena, 0);
+ gpiod_set_value_cansleep(wm2200->ldo_ena, 0);
regulator_bulk_disable(ARRAY_SIZE(wm2200->core_supplies),
wm2200->core_supplies);
@@ -2457,8 +2456,8 @@ static int wm2200_runtime_resume(struct device *dev)
return ret;
}
- if (wm2200->pdata.ldo_ena) {
- gpio_set_value_cansleep(wm2200->pdata.ldo_ena, 1);
+ if (wm2200->ldo_ena) {
+ gpiod_set_value_cansleep(wm2200->ldo_ena, 1);
msleep(2);
}
diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c
index ff63723928a1..7ee4b45c0834 100644
--- a/sound/soc/codecs/wm5100.c
+++ b/sound/soc/codecs/wm5100.c
@@ -15,7 +15,7 @@
#include <linux/pm.h>
#include <linux/gcd.h>
#include <linux/gpio/driver.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
@@ -55,6 +55,9 @@ struct wm5100_priv {
struct snd_soc_component *component;
struct regulator_bulk_data core_supplies[WM5100_NUM_CORE_SUPPLIES];
+ struct gpio_desc *reset;
+ struct gpio_desc *ldo_ena;
+ struct gpio_desc *hp_pol;
int rev;
@@ -205,9 +208,9 @@ static void wm5100_free_sr(struct snd_soc_component *component, int rate)
static int wm5100_reset(struct wm5100_priv *wm5100)
{
- if (wm5100->pdata.reset) {
- gpio_set_value_cansleep(wm5100->pdata.reset, 0);
- gpio_set_value_cansleep(wm5100->pdata.reset, 1);
+ if (wm5100->reset) {
+ gpiod_set_value_cansleep(wm5100->reset, 1);
+ gpiod_set_value_cansleep(wm5100->reset, 0);
return 0;
} else {
@@ -1974,7 +1977,7 @@ static void wm5100_set_detect_mode(struct wm5100_priv *wm5100, int the_mode)
if (WARN_ON(the_mode >= ARRAY_SIZE(wm5100->pdata.jack_modes)))
return;
- gpio_set_value_cansleep(wm5100->pdata.hp_pol, mode->hp_pol);
+ gpiod_set_value_cansleep(wm5100->hp_pol, mode->hp_pol);
regmap_update_bits(wm5100->regmap, WM5100_ACCESSORY_DETECT_MODE_1,
WM5100_ACCDET_BIAS_SRC_MASK |
WM5100_ACCDET_SRC,
@@ -2299,11 +2302,7 @@ static void wm5100_init_gpio(struct i2c_client *i2c)
wm5100->gpio_chip = wm5100_template_chip;
wm5100->gpio_chip.ngpio = 6;
wm5100->gpio_chip.parent = &i2c->dev;
-
- if (wm5100->pdata.gpio_base)
- wm5100->gpio_chip.base = wm5100->pdata.gpio_base;
- else
- wm5100->gpio_chip.base = -1;
+ wm5100->gpio_chip.base = -1;
ret = gpiochip_add_data(&wm5100->gpio_chip, wm5100);
if (ret != 0)
@@ -2349,35 +2348,20 @@ static int wm5100_probe(struct snd_soc_component *component)
snd_soc_dapm_new_controls(dapm, wm5100_dapm_widgets_noirq,
ARRAY_SIZE(wm5100_dapm_widgets_noirq));
- if (wm5100->pdata.hp_pol) {
- ret = gpio_request_one(wm5100->pdata.hp_pol,
- GPIOF_OUT_INIT_HIGH, "WM5100 HP_POL");
- if (ret < 0) {
- dev_err(&i2c->dev, "Failed to request HP_POL %d: %d\n",
- wm5100->pdata.hp_pol, ret);
- goto err_gpio;
- }
+ wm5100->hp_pol = devm_gpiod_get_optional(&i2c->dev, "hp-pol",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(wm5100->hp_pol)) {
+ ret = PTR_ERR(wm5100->hp_pol);
+ dev_err(&i2c->dev, "Failed to request HP_POL GPIO: %d\n",
+ ret);
+ return ret;
}
return 0;
-
-err_gpio:
-
- return ret;
-}
-
-static void wm5100_remove(struct snd_soc_component *component)
-{
- struct wm5100_priv *wm5100 = snd_soc_component_get_drvdata(component);
-
- if (wm5100->pdata.hp_pol) {
- gpio_free(wm5100->pdata.hp_pol);
- }
}
static const struct snd_soc_component_driver soc_component_dev_wm5100 = {
.probe = wm5100_probe,
- .remove = wm5100_remove,
.set_sysclk = wm5100_set_sysclk,
.set_pll = wm5100_set_fll,
.seq_notifier = wm5100_seq_notifier,
@@ -2460,26 +2444,26 @@ static int wm5100_i2c_probe(struct i2c_client *i2c)
goto err;
}
- if (wm5100->pdata.ldo_ena) {
- ret = gpio_request_one(wm5100->pdata.ldo_ena,
- GPIOF_OUT_INIT_HIGH, "WM5100 LDOENA");
- if (ret < 0) {
- dev_err(&i2c->dev, "Failed to request LDOENA %d: %d\n",
- wm5100->pdata.ldo_ena, ret);
- goto err_enable;
- }
+ wm5100->ldo_ena = devm_gpiod_get_optional(&i2c->dev, "wlf,ldo1ena",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(wm5100->ldo_ena)) {
+ ret = PTR_ERR(wm5100->ldo_ena);
+ dev_err(&i2c->dev, "Failed to request LDOENA GPIO: %d\n", ret);
+ goto err_enable;
+ }
+ if (wm5100->ldo_ena) {
+ gpiod_set_consumer_name(wm5100->ldo_ena, "WM5100 LDOENA");
msleep(2);
}
- if (wm5100->pdata.reset) {
- ret = gpio_request_one(wm5100->pdata.reset,
- GPIOF_OUT_INIT_HIGH, "WM5100 /RESET");
- if (ret < 0) {
- dev_err(&i2c->dev, "Failed to request /RESET %d: %d\n",
- wm5100->pdata.reset, ret);
- goto err_ldo;
- }
+ wm5100->reset = devm_gpiod_get_optional(&i2c->dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(wm5100->reset)) {
+ ret = PTR_ERR(wm5100->reset);
+ dev_err(&i2c->dev, "Failed to request /RESET GPIO: %d\n", ret);
+ goto err_ldo;
}
+ gpiod_set_consumer_name(wm5100->reset, "WM5100 /RESET");
ret = regmap_read(wm5100->regmap, WM5100_SOFTWARE_RESET, &reg);
if (ret < 0) {
@@ -2619,15 +2603,9 @@ err_reset:
if (i2c->irq)
free_irq(i2c->irq, wm5100);
wm5100_free_gpio(i2c);
- if (wm5100->pdata.reset) {
- gpio_set_value_cansleep(wm5100->pdata.reset, 0);
- gpio_free(wm5100->pdata.reset);
- }
+ gpiod_set_value_cansleep(wm5100->reset, 1);
err_ldo:
- if (wm5100->pdata.ldo_ena) {
- gpio_set_value_cansleep(wm5100->pdata.ldo_ena, 0);
- gpio_free(wm5100->pdata.ldo_ena);
- }
+ gpiod_set_value_cansleep(wm5100->ldo_ena, 0);
err_enable:
regulator_bulk_disable(ARRAY_SIZE(wm5100->core_supplies),
wm5100->core_supplies);
@@ -2643,14 +2621,8 @@ static void wm5100_i2c_remove(struct i2c_client *i2c)
if (i2c->irq)
free_irq(i2c->irq, wm5100);
wm5100_free_gpio(i2c);
- if (wm5100->pdata.reset) {
- gpio_set_value_cansleep(wm5100->pdata.reset, 0);
- gpio_free(wm5100->pdata.reset);
- }
- if (wm5100->pdata.ldo_ena) {
- gpio_set_value_cansleep(wm5100->pdata.ldo_ena, 0);
- gpio_free(wm5100->pdata.ldo_ena);
- }
+ gpiod_set_value_cansleep(wm5100->reset, 1);
+ gpiod_set_value_cansleep(wm5100->ldo_ena, 0);
}
#ifdef CONFIG_PM
@@ -2660,8 +2632,7 @@ static int wm5100_runtime_suspend(struct device *dev)
regcache_cache_only(wm5100->regmap, true);
regcache_mark_dirty(wm5100->regmap);
- if (wm5100->pdata.ldo_ena)
- gpio_set_value_cansleep(wm5100->pdata.ldo_ena, 0);
+ gpiod_set_value_cansleep(wm5100->ldo_ena, 0);
regulator_bulk_disable(ARRAY_SIZE(wm5100->core_supplies),
wm5100->core_supplies);
@@ -2681,8 +2652,8 @@ static int wm5100_runtime_resume(struct device *dev)
return ret;
}
- if (wm5100->pdata.ldo_ena) {
- gpio_set_value_cansleep(wm5100->pdata.ldo_ena, 1);
+ if (wm5100->ldo_ena) {
+ gpiod_set_value_cansleep(wm5100->ldo_ena, 1);
msleep(2);
}
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
index fb90ae6a8a34..7c6ed2983128 100644
--- a/sound/soc/codecs/wm8962.c
+++ b/sound/soc/codecs/wm8962.c
@@ -2229,6 +2229,9 @@ SND_SOC_DAPM_PGA_E("HPOUT", SND_SOC_NOPM, 0, 0, NULL, 0, hp_event,
SND_SOC_DAPM_OUTPUT("HPOUTL"),
SND_SOC_DAPM_OUTPUT("HPOUTR"),
+
+SND_SOC_DAPM_PGA("SPKOUTL Output", WM8962_CLASS_D_CONTROL_1, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA("SPKOUTR Output", WM8962_CLASS_D_CONTROL_1, 7, 0, NULL, 0),
};
static const struct snd_soc_dapm_widget wm8962_dapm_spk_mono_widgets[] = {
@@ -2236,7 +2239,6 @@ SND_SOC_DAPM_MIXER("Speaker Mixer", WM8962_MIXER_ENABLES, 1, 0,
spkmixl, ARRAY_SIZE(spkmixl)),
SND_SOC_DAPM_MUX_E("Speaker PGA", WM8962_PWR_MGMT_2, 4, 0, &spkoutl_mux,
out_pga_event, SND_SOC_DAPM_POST_PMU),
-SND_SOC_DAPM_PGA("Speaker Output", WM8962_CLASS_D_CONTROL_1, 7, 0, NULL, 0),
SND_SOC_DAPM_OUTPUT("SPKOUT"),
};
@@ -2251,9 +2253,6 @@ SND_SOC_DAPM_MUX_E("SPKOUTL PGA", WM8962_PWR_MGMT_2, 4, 0, &spkoutl_mux,
SND_SOC_DAPM_MUX_E("SPKOUTR PGA", WM8962_PWR_MGMT_2, 3, 0, &spkoutr_mux,
out_pga_event, SND_SOC_DAPM_POST_PMU),
-SND_SOC_DAPM_PGA("SPKOUTR Output", WM8962_CLASS_D_CONTROL_1, 7, 0, NULL, 0),
-SND_SOC_DAPM_PGA("SPKOUTL Output", WM8962_CLASS_D_CONTROL_1, 6, 0, NULL, 0),
-
SND_SOC_DAPM_OUTPUT("SPKOUTL"),
SND_SOC_DAPM_OUTPUT("SPKOUTR"),
};
@@ -2366,12 +2365,18 @@ static const struct snd_soc_dapm_route wm8962_spk_mono_intercon[] = {
{ "Speaker PGA", "Mixer", "Speaker Mixer" },
{ "Speaker PGA", "DAC", "DACL" },
- { "Speaker Output", NULL, "Speaker PGA" },
- { "Speaker Output", NULL, "SYSCLK" },
- { "Speaker Output", NULL, "TOCLK" },
- { "Speaker Output", NULL, "TEMP_SPK" },
+ { "SPKOUTL Output", NULL, "Speaker PGA" },
+ { "SPKOUTL Output", NULL, "SYSCLK" },
+ { "SPKOUTL Output", NULL, "TOCLK" },
+ { "SPKOUTL Output", NULL, "TEMP_SPK" },
- { "SPKOUT", NULL, "Speaker Output" },
+ { "SPKOUTR Output", NULL, "Speaker PGA" },
+ { "SPKOUTR Output", NULL, "SYSCLK" },
+ { "SPKOUTR Output", NULL, "TOCLK" },
+ { "SPKOUTR Output", NULL, "TEMP_SPK" },
+
+ { "SPKOUT", NULL, "SPKOUTL Output" },
+ { "SPKOUT", NULL, "SPKOUTR Output" },
};
static const struct snd_soc_dapm_route wm8962_spk_stereo_intercon[] = {
@@ -2914,8 +2919,12 @@ static int wm8962_set_fll(struct snd_soc_component *component, int fll_id, int s
switch (fll_id) {
case WM8962_FLL_MCLK:
case WM8962_FLL_BCLK:
+ fll1 |= (fll_id - 1) << WM8962_FLL_REFCLK_SRC_SHIFT;
+ break;
case WM8962_FLL_OSC:
fll1 |= (fll_id - 1) << WM8962_FLL_REFCLK_SRC_SHIFT;
+ snd_soc_component_update_bits(component, WM8962_PLL2,
+ WM8962_OSC_ENA, WM8962_OSC_ENA);
break;
case WM8962_FLL_INT:
snd_soc_component_update_bits(component, WM8962_FLL_CONTROL_1,
@@ -2924,7 +2933,7 @@ static int wm8962_set_fll(struct snd_soc_component *component, int fll_id, int s
WM8962_FLL_FRC_NCO, WM8962_FLL_FRC_NCO);
break;
default:
- dev_err(component->dev, "Unknown FLL source %d\n", ret);
+ dev_err(component->dev, "Unknown FLL source %d\n", source);
return -EINVAL;
}
diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c
index df6195778c57..e738326e33ed 100644
--- a/sound/soc/codecs/wm8996.c
+++ b/sound/soc/codecs/wm8996.c
@@ -14,7 +14,7 @@
#include <linux/pm.h>
#include <linux/gcd.h>
#include <linux/gpio/driver.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
@@ -51,7 +51,7 @@ struct wm8996_priv {
struct regmap *regmap;
struct snd_soc_component *component;
- int ldo1ena;
+ struct gpio_desc *ldo_ena;
int sysclk;
int sysclk_src;
@@ -1596,9 +1596,9 @@ static int wm8996_set_bias_level(struct snd_soc_component *component,
return ret;
}
- if (wm8996->pdata.ldo_ena >= 0) {
- gpio_set_value_cansleep(wm8996->pdata.ldo_ena,
- 1);
+ if (wm8996->ldo_ena) {
+ gpiod_set_value_cansleep(wm8996->ldo_ena,
+ 1);
msleep(5);
}
@@ -1615,8 +1615,8 @@ static int wm8996_set_bias_level(struct snd_soc_component *component,
case SND_SOC_BIAS_OFF:
regcache_cache_only(wm8996->regmap, true);
- if (wm8996->pdata.ldo_ena >= 0) {
- gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 0);
+ if (wm8996->ldo_ena) {
+ gpiod_set_value_cansleep(wm8996->ldo_ena, 0);
regcache_cache_only(wm8996->regmap, true);
}
regulator_bulk_disable(ARRAY_SIZE(wm8996->supplies),
@@ -2188,6 +2188,8 @@ static const struct gpio_chip wm8996_template_chip = {
.direction_input = wm8996_gpio_direction_in,
.get = wm8996_gpio_get,
.can_sleep = 1,
+ .ngpio = 5,
+ .base = -1,
};
static void wm8996_init_gpio(struct wm8996_priv *wm8996)
@@ -2195,14 +2197,8 @@ static void wm8996_init_gpio(struct wm8996_priv *wm8996)
int ret;
wm8996->gpio_chip = wm8996_template_chip;
- wm8996->gpio_chip.ngpio = 5;
wm8996->gpio_chip.parent = wm8996->dev;
- if (wm8996->pdata.gpio_base)
- wm8996->gpio_chip.base = wm8996->pdata.gpio_base;
- else
- wm8996->gpio_chip.base = -1;
-
ret = gpiochip_add_data(&wm8996->gpio_chip, wm8996);
if (ret != 0)
dev_err(wm8996->dev, "Failed to add GPIOs: %d\n", ret);
@@ -2771,15 +2767,15 @@ static int wm8996_i2c_probe(struct i2c_client *i2c)
memcpy(&wm8996->pdata, dev_get_platdata(&i2c->dev),
sizeof(wm8996->pdata));
- if (wm8996->pdata.ldo_ena > 0) {
- ret = gpio_request_one(wm8996->pdata.ldo_ena,
- GPIOF_OUT_INIT_LOW, "WM8996 ENA");
- if (ret < 0) {
- dev_err(&i2c->dev, "Failed to request GPIO %d: %d\n",
- wm8996->pdata.ldo_ena, ret);
- goto err;
- }
+ wm8996->ldo_ena = devm_gpiod_get_optional(&i2c->dev, "wlf,ldo1ena",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(wm8996->ldo_ena)) {
+ ret = PTR_ERR(wm8996->ldo_ena);
+ dev_err(&i2c->dev, "Failed to request LDO ENA GPIO: %d\n",
+ ret);
+ goto err;
}
+ gpiod_set_consumer_name(wm8996->ldo_ena, "WM8996 ENA");
for (i = 0; i < ARRAY_SIZE(wm8996->supplies); i++)
wm8996->supplies[i].supply = wm8996_supply_names[i];
@@ -2814,8 +2810,8 @@ static int wm8996_i2c_probe(struct i2c_client *i2c)
goto err_gpio;
}
- if (wm8996->pdata.ldo_ena > 0) {
- gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 1);
+ if (wm8996->ldo_ena) {
+ gpiod_set_value_cansleep(wm8996->ldo_ena, 1);
msleep(5);
}
@@ -2847,8 +2843,8 @@ static int wm8996_i2c_probe(struct i2c_client *i2c)
dev_info(&i2c->dev, "revision %c\n",
(reg & WM8996_CHIP_REV_MASK) + 'A');
- if (wm8996->pdata.ldo_ena > 0) {
- gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 0);
+ if (wm8996->ldo_ena) {
+ gpiod_set_value_cansleep(wm8996->ldo_ena, 0);
regcache_cache_only(wm8996->regmap, true);
} else {
ret = regmap_write(wm8996->regmap, WM8996_SOFTWARE_RESET,
@@ -3054,12 +3050,10 @@ err_gpiolib:
wm8996_free_gpio(wm8996);
err_regmap:
err_enable:
- if (wm8996->pdata.ldo_ena > 0)
- gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 0);
+ if (wm8996->ldo_ena)
+ gpiod_set_value_cansleep(wm8996->ldo_ena, 0);
regulator_bulk_disable(ARRAY_SIZE(wm8996->supplies), wm8996->supplies);
err_gpio:
- if (wm8996->pdata.ldo_ena > 0)
- gpio_free(wm8996->pdata.ldo_ena);
err:
return ret;
@@ -3070,10 +3064,8 @@ static void wm8996_i2c_remove(struct i2c_client *client)
struct wm8996_priv *wm8996 = i2c_get_clientdata(client);
wm8996_free_gpio(wm8996);
- if (wm8996->pdata.ldo_ena > 0) {
- gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 0);
- gpio_free(wm8996->pdata.ldo_ena);
- }
+ if (wm8996->ldo_ena)
+ gpiod_set_value_cansleep(wm8996->ldo_ena, 0);
}
static const struct i2c_device_id wm8996_i2c_id[] = {
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index c01e31175015..36ea0dcdc7ab 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -739,19 +739,25 @@ static int wm_adsp_request_firmware_file(struct wm_adsp *dsp,
const char *filetype)
{
struct cs_dsp *cs_dsp = &dsp->cs_dsp;
+ const char *fwf;
char *s, c;
int ret = 0;
+ if (dsp->fwf_name)
+ fwf = dsp->fwf_name;
+ else
+ fwf = dsp->cs_dsp.name;
+
if (system_name && asoc_component_prefix)
*filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s-%s.%s", dir, dsp->part,
- dsp->fwf_name, wm_adsp_fw[dsp->fw].file, system_name,
+ fwf, wm_adsp_fw[dsp->fw].file, system_name,
asoc_component_prefix, filetype);
else if (system_name)
*filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s.%s", dir, dsp->part,
- dsp->fwf_name, wm_adsp_fw[dsp->fw].file, system_name,
+ fwf, wm_adsp_fw[dsp->fw].file, system_name,
filetype);
else
- *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s.%s", dir, dsp->part, dsp->fwf_name,
+ *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s.%s", dir, dsp->part, fwf,
wm_adsp_fw[dsp->fw].file, filetype);
if (*filename == NULL)
@@ -823,6 +829,23 @@ static int wm_adsp_request_firmware_files(struct wm_adsp *dsp,
}
}
+ /* Check system-specific bin without wmfw before falling back to generic */
+ if (dsp->wmfw_optional && system_name) {
+ if (asoc_component_prefix)
+ wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
+ cirrus_dir, system_name,
+ asoc_component_prefix, "bin");
+
+ if (!*coeff_firmware)
+ wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
+ cirrus_dir, system_name,
+ NULL, "bin");
+
+ if (*coeff_firmware)
+ return 0;
+ }
+
+ /* Check legacy location */
if (!wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename,
"", NULL, NULL, "wmfw")) {
wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
@@ -830,62 +853,28 @@ static int wm_adsp_request_firmware_files(struct wm_adsp *dsp,
return 0;
}
+ /* Fall back to generic wmfw and optional matching bin */
ret = wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename,
cirrus_dir, NULL, NULL, "wmfw");
- if (!ret) {
+ if (!ret || dsp->wmfw_optional) {
wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
cirrus_dir, NULL, NULL, "bin");
return 0;
}
- if (dsp->wmfw_optional) {
- if (system_name) {
- if (asoc_component_prefix)
- wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
- cirrus_dir, system_name,
- asoc_component_prefix, "bin");
-
- if (!*coeff_firmware)
- wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
- cirrus_dir, system_name,
- NULL, "bin");
- }
-
- if (!*coeff_firmware)
- wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
- "", NULL, NULL, "bin");
-
- if (!*coeff_firmware)
- wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
- cirrus_dir, NULL, NULL, "bin");
-
- return 0;
- }
-
adsp_err(dsp, "Failed to request firmware <%s>%s-%s-%s<-%s<%s>>.wmfw\n",
- cirrus_dir, dsp->part, dsp->fwf_name, wm_adsp_fw[dsp->fw].file,
- system_name, asoc_component_prefix);
+ cirrus_dir, dsp->part,
+ dsp->fwf_name ? dsp->fwf_name : dsp->cs_dsp.name,
+ wm_adsp_fw[dsp->fw].file, system_name, asoc_component_prefix);
return -ENOENT;
}
static int wm_adsp_common_init(struct wm_adsp *dsp)
{
- char *p;
-
INIT_LIST_HEAD(&dsp->compr_list);
INIT_LIST_HEAD(&dsp->buffer_list);
- if (!dsp->fwf_name) {
- p = devm_kstrdup(dsp->cs_dsp.dev, dsp->cs_dsp.name, GFP_KERNEL);
- if (!p)
- return -ENOMEM;
-
- dsp->fwf_name = p;
- for (; *p != 0; ++p)
- *p = tolower(*p);
- }
-
return 0;
}
diff --git a/sound/soc/codecs/wsa883x.c b/sound/soc/codecs/wsa883x.c
index cb83c569e18d..a2e86ef7d18f 100644
--- a/sound/soc/codecs/wsa883x.c
+++ b/sound/soc/codecs/wsa883x.c
@@ -1098,7 +1098,11 @@ static int wsa_dev_mode_put(struct snd_kcontrol *kcontrol,
return 1;
}
-static const DECLARE_TLV_DB_SCALE(pa_gain, -300, 150, -300);
+static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(pa_gain,
+ 0, 14, TLV_DB_SCALE_ITEM(-300, 0, 0),
+ 15, 29, TLV_DB_SCALE_ITEM(-300, 150, 0),
+ 30, 31, TLV_DB_SCALE_ITEM(1800, 0, 0),
+);
static int wsa883x_get_swr_port(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
diff --git a/sound/soc/codecs/wsa884x.c b/sound/soc/codecs/wsa884x.c
index 993d76b18b53..f2653df84e4a 100644
--- a/sound/soc/codecs/wsa884x.c
+++ b/sound/soc/codecs/wsa884x.c
@@ -1654,15 +1654,9 @@ static int wsa884x_spkr_event(struct snd_soc_dapm_widget *w,
snd_soc_component_write_field(component, WSA884X_PDM_WD_CTL,
WSA884X_PDM_WD_CTL_PDM_WD_EN_MASK,
0x1);
- snd_soc_component_write_field(component, WSA884X_PA_FSM_EN,
- WSA884X_PA_FSM_EN_GLOBAL_PA_EN_MASK,
- 0x1);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_component_write_field(component, WSA884X_PA_FSM_EN,
- WSA884X_PA_FSM_EN_GLOBAL_PA_EN_MASK,
- 0x0);
snd_soc_component_write_field(component, WSA884X_PDM_WD_CTL,
WSA884X_PDM_WD_CTL_PDM_WD_EN_MASK,
0x0);
@@ -1786,6 +1780,7 @@ static const struct snd_soc_dai_ops wsa884x_dai_ops = {
.hw_free = wsa884x_hw_free,
.mute_stream = wsa884x_mute_stream,
.set_stream = wsa884x_set_stream,
+ .mute_unmute_on_trigger = true,
};
static struct snd_soc_dai_driver wsa884x_dais[] = {
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index be342ee03fb9..270726c134b3 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -121,6 +121,7 @@ config SND_SOC_FSL_UTILS
config SND_SOC_FSL_RPMSG
tristate "NXP Audio Base On RPMSG support"
depends on COMMON_CLK
+ depends on OF && I2C
depends on RPMSG
depends on SND_IMX_SOC || SND_IMX_SOC = n
select SND_SOC_IMX_RPMSG if SND_IMX_SOC != n
@@ -183,19 +184,6 @@ config SND_SOC_POWERPC_QMC_AUDIO
comment "SoC Audio support for Freescale PPC boards:"
-config SND_SOC_MPC8610_HPCD
- tristate "ALSA SoC support for the Freescale MPC8610 HPCD board"
- # I2C is necessary for the CS4270 driver
- depends on MPC8610_HPCD && I2C
- select SND_SOC_FSL_SSI
- select SND_SOC_FSL_UTILS
- select SND_SOC_POWERPC_DMA
- select SND_SOC_CS4270
- select SND_SOC_CS4270_VD33_ERRATA
- default y if MPC8610_HPCD
- help
- Say Y if you want to enable audio on the Freescale MPC8610 HPCD.
-
config SND_SOC_P1022_DS
tristate "ALSA SoC support for the Freescale P1022 DS board"
# I2C is necessary for the WM8776 driver
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index 8db7e97d0bd5..b45eda80c196 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -1,8 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
-# MPC8610 HPCD Machine Support
-snd-soc-mpc8610-hpcd-objs := mpc8610_hpcd.o
-obj-$(CONFIG_SND_SOC_MPC8610_HPCD) += snd-soc-mpc8610-hpcd.o
-
# P1022 DS Machine Support
snd-soc-p1022-ds-objs := p1022_ds.o
obj-$(CONFIG_SND_SOC_P1022_DS) += snd-soc-p1022-ds.o
diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c
index 7518ab9d768e..bc07f26ba303 100644
--- a/sound/soc/fsl/fsl-asoc-card.c
+++ b/sound/soc/fsl/fsl-asoc-card.c
@@ -305,8 +305,7 @@ SND_SOC_DAILINK_DEFS(hifi_fe,
SND_SOC_DAILINK_DEFS(hifi_be,
DAILINK_COMP_ARRAY(COMP_EMPTY()),
- DAILINK_COMP_ARRAY(COMP_EMPTY()),
- DAILINK_COMP_ARRAY(COMP_DUMMY()));
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
static const struct snd_soc_dai_link fsl_asoc_card_dai[] = {
/* Default ASoC DAI Link*/
diff --git a/sound/soc/fsl/fsl_mqs.c b/sound/soc/fsl/fsl_mqs.c
index f2d74ec05cdf..60929c36a0e3 100644
--- a/sound/soc/fsl/fsl_mqs.c
+++ b/sound/soc/fsl/fsl_mqs.c
@@ -10,9 +10,7 @@
#include <linux/moduleparam.h>
#include <linux/mfd/syscon.h>
#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
-#include <linux/of.h>
#include <linux/pm_runtime.h>
-#include <linux/of.h>
#include <linux/pm.h>
#include <linux/slab.h>
#include <sound/soc.h>
diff --git a/sound/soc/fsl/fsl_qmc_audio.c b/sound/soc/fsl/fsl_qmc_audio.c
index 56d6b0b039a2..bfaaa451735b 100644
--- a/sound/soc/fsl/fsl_qmc_audio.c
+++ b/sound/soc/fsl/fsl_qmc_audio.c
@@ -99,7 +99,7 @@ static void qmc_audio_pcm_write_complete(void *context)
snd_pcm_period_elapsed(prtd->substream);
}
-static void qmc_audio_pcm_read_complete(void *context, size_t length)
+static void qmc_audio_pcm_read_complete(void *context, size_t length, unsigned int flags)
{
struct qmc_dai_prtd *prtd = context;
int ret;
diff --git a/sound/soc/fsl/fsl_xcvr.c b/sound/soc/fsl/fsl_xcvr.c
index f0fb33d719c2..c46f64557a7f 100644
--- a/sound/soc/fsl/fsl_xcvr.c
+++ b/sound/soc/fsl/fsl_xcvr.c
@@ -174,7 +174,9 @@ static int fsl_xcvr_activate_ctl(struct snd_soc_dai *dai, const char *name,
struct snd_kcontrol *kctl;
bool enabled;
- kctl = snd_soc_card_get_kcontrol(card, name);
+ lockdep_assert_held(&card->snd_card->controls_rwsem);
+
+ kctl = snd_soc_card_get_kcontrol_locked(card, name);
if (kctl == NULL)
return -ENOENT;
@@ -576,10 +578,14 @@ static int fsl_xcvr_startup(struct snd_pcm_substream *substream,
xcvr->streams |= BIT(substream->stream);
if (!xcvr->soc_data->spdif_only) {
+ struct snd_soc_card *card = dai->component->card;
+
/* Disable XCVR controls if there is stream started */
+ down_read(&card->snd_card->controls_rwsem);
fsl_xcvr_activate_ctl(dai, fsl_xcvr_mode_kctl.name, false);
fsl_xcvr_activate_ctl(dai, fsl_xcvr_arc_mode_kctl.name, false);
fsl_xcvr_activate_ctl(dai, fsl_xcvr_earc_capds_kctl.name, false);
+ up_read(&card->snd_card->controls_rwsem);
}
return 0;
@@ -598,11 +604,15 @@ static void fsl_xcvr_shutdown(struct snd_pcm_substream *substream,
/* Enable XCVR controls if there is no stream started */
if (!xcvr->streams) {
if (!xcvr->soc_data->spdif_only) {
+ struct snd_soc_card *card = dai->component->card;
+
+ down_read(&card->snd_card->controls_rwsem);
fsl_xcvr_activate_ctl(dai, fsl_xcvr_mode_kctl.name, true);
fsl_xcvr_activate_ctl(dai, fsl_xcvr_arc_mode_kctl.name,
(xcvr->mode == FSL_XCVR_MODE_ARC));
fsl_xcvr_activate_ctl(dai, fsl_xcvr_earc_capds_kctl.name,
(xcvr->mode == FSL_XCVR_MODE_EARC));
+ up_read(&card->snd_card->controls_rwsem);
}
ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_IER0,
FSL_XCVR_IRQ_EARC_ALL, 0);
diff --git a/sound/soc/fsl/imx-rpmsg.c b/sound/soc/fsl/imx-rpmsg.c
index a0c5c35817dd..e5bd63dab10c 100644
--- a/sound/soc/fsl/imx-rpmsg.c
+++ b/sound/soc/fsl/imx-rpmsg.c
@@ -2,9 +2,8 @@
// Copyright 2017-2020 NXP
#include <linux/module.h>
-#include <linux/of.h>
+#include <linux/of_platform.h>
#include <linux/of_reserved_mem.h>
-#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/of_gpio.h>
#include <linux/slab.h>
@@ -21,8 +20,11 @@ struct imx_rpmsg {
struct snd_soc_dai_link dai;
struct snd_soc_card card;
unsigned long sysclk;
+ bool lpa;
};
+static struct dev_pm_ops lpa_pm;
+
static const struct snd_soc_dapm_widget imx_rpmsg_dapm_widgets[] = {
SND_SOC_DAPM_HP("Headphone Jack", NULL),
SND_SOC_DAPM_SPK("Ext Spk", NULL),
@@ -39,6 +41,58 @@ static int imx_rpmsg_late_probe(struct snd_soc_card *card)
struct device *dev = card->dev;
int ret;
+ if (data->lpa) {
+ struct snd_soc_component *codec_comp;
+ struct device_node *codec_np;
+ struct device_driver *codec_drv;
+ struct device *codec_dev = NULL;
+
+ codec_np = data->dai.codecs->of_node;
+ if (codec_np) {
+ struct platform_device *codec_pdev;
+ struct i2c_client *codec_i2c;
+
+ codec_i2c = of_find_i2c_device_by_node(codec_np);
+ if (codec_i2c)
+ codec_dev = &codec_i2c->dev;
+ if (!codec_dev) {
+ codec_pdev = of_find_device_by_node(codec_np);
+ if (codec_pdev)
+ codec_dev = &codec_pdev->dev;
+ }
+ }
+ if (codec_dev) {
+ codec_comp = snd_soc_lookup_component_nolocked(codec_dev, NULL);
+ if (codec_comp) {
+ int i, num_widgets;
+ const char *widgets;
+ struct snd_soc_dapm_context *dapm;
+
+ num_widgets = of_property_count_strings(data->card.dev->of_node,
+ "ignore-suspend-widgets");
+ for (i = 0; i < num_widgets; i++) {
+ of_property_read_string_index(data->card.dev->of_node,
+ "ignore-suspend-widgets",
+ i, &widgets);
+ dapm = snd_soc_component_get_dapm(codec_comp);
+ snd_soc_dapm_ignore_suspend(dapm, widgets);
+ }
+ }
+ codec_drv = codec_dev->driver;
+ if (codec_drv->pm) {
+ memcpy(&lpa_pm, codec_drv->pm, sizeof(lpa_pm));
+ lpa_pm.suspend = NULL;
+ lpa_pm.resume = NULL;
+ lpa_pm.freeze = NULL;
+ lpa_pm.thaw = NULL;
+ lpa_pm.poweroff = NULL;
+ lpa_pm.restore = NULL;
+ codec_drv->pm = &lpa_pm;
+ }
+ put_device(codec_dev);
+ }
+ }
+
if (!data->sysclk)
return 0;
@@ -138,6 +192,9 @@ static int imx_rpmsg_probe(struct platform_device *pdev)
goto fail;
}
+ if (of_property_read_bool(np, "fsl,enable-lpa"))
+ data->lpa = true;
+
data->card.dev = &pdev->dev;
data->card.owner = THIS_MODULE;
data->card.dapm_widgets = imx_rpmsg_dapm_widgets;
diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c
deleted file mode 100644
index 52fb9e7bcca4..000000000000
--- a/sound/soc/fsl/mpc8610_hpcd.c
+++ /dev/null
@@ -1,451 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-//
-// Freescale MPC8610HPCD ALSA SoC Machine driver
-//
-// Author: Timur Tabi <timur@freescale.com>
-//
-// Copyright 2007-2010 Freescale Semiconductor, Inc.
-
-#include <linux/module.h>
-#include <linux/interrupt.h>
-#include <linux/fsl/guts.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/slab.h>
-#include <sound/soc.h>
-
-#include "fsl_dma.h"
-#include "fsl_ssi.h"
-#include "fsl_utils.h"
-
-/* There's only one global utilities register */
-static phys_addr_t guts_phys;
-
-/**
- * mpc8610_hpcd_data: machine-specific ASoC device data
- *
- * This structure contains data for a single sound platform device on an
- * MPC8610 HPCD. Some of the data is taken from the device tree.
- */
-struct mpc8610_hpcd_data {
- struct snd_soc_dai_link dai[2];
- struct snd_soc_card card;
- unsigned int dai_format;
- unsigned int codec_clk_direction;
- unsigned int cpu_clk_direction;
- unsigned int clk_frequency;
- unsigned int ssi_id; /* 0 = SSI1, 1 = SSI2, etc */
- unsigned int dma_id[2]; /* 0 = DMA1, 1 = DMA2, etc */
- unsigned int dma_channel_id[2]; /* 0 = ch 0, 1 = ch 1, etc*/
- char codec_dai_name[DAI_NAME_SIZE];
- char platform_name[2][DAI_NAME_SIZE]; /* One for each DMA channel */
-};
-
-/**
- * mpc8610_hpcd_machine_probe: initialize the board
- *
- * This function is used to initialize the board-specific hardware.
- *
- * Here we program the DMACR and PMUXCR registers.
- */
-static int mpc8610_hpcd_machine_probe(struct snd_soc_card *card)
-{
- struct mpc8610_hpcd_data *machine_data =
- container_of(card, struct mpc8610_hpcd_data, card);
- struct ccsr_guts __iomem *guts;
-
- guts = ioremap(guts_phys, sizeof(struct ccsr_guts));
- if (!guts) {
- dev_err(card->dev, "could not map global utilities\n");
- return -ENOMEM;
- }
-
- /* Program the signal routing between the SSI and the DMA */
- guts_set_dmacr(guts, machine_data->dma_id[0],
- machine_data->dma_channel_id[0],
- CCSR_GUTS_DMACR_DEV_SSI);
- guts_set_dmacr(guts, machine_data->dma_id[1],
- machine_data->dma_channel_id[1],
- CCSR_GUTS_DMACR_DEV_SSI);
-
- guts_set_pmuxcr_dma(guts, machine_data->dma_id[0],
- machine_data->dma_channel_id[0], 0);
- guts_set_pmuxcr_dma(guts, machine_data->dma_id[1],
- machine_data->dma_channel_id[1], 0);
-
- switch (machine_data->ssi_id) {
- case 0:
- clrsetbits_be32(&guts->pmuxcr,
- CCSR_GUTS_PMUXCR_SSI1_MASK, CCSR_GUTS_PMUXCR_SSI1_SSI);
- break;
- case 1:
- clrsetbits_be32(&guts->pmuxcr,
- CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI2_SSI);
- break;
- }
-
- iounmap(guts);
-
- return 0;
-}
-
-/**
- * mpc8610_hpcd_startup: program the board with various hardware parameters
- *
- * This function takes board-specific information, like clock frequencies
- * and serial data formats, and passes that information to the codec and
- * transport drivers.
- */
-static int mpc8610_hpcd_startup(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
- struct mpc8610_hpcd_data *machine_data =
- container_of(rtd->card, struct mpc8610_hpcd_data, card);
- struct device *dev = rtd->card->dev;
- int ret = 0;
-
- /* Tell the codec driver what the serial protocol is. */
- ret = snd_soc_dai_set_fmt(snd_soc_rtd_to_codec(rtd, 0), machine_data->dai_format);
- if (ret < 0) {
- dev_err(dev, "could not set codec driver audio format\n");
- return ret;
- }
-
- /*
- * Tell the codec driver what the MCLK frequency is, and whether it's
- * a slave or master.
- */
- ret = snd_soc_dai_set_sysclk(snd_soc_rtd_to_codec(rtd, 0), 0,
- machine_data->clk_frequency,
- machine_data->codec_clk_direction);
- if (ret < 0) {
- dev_err(dev, "could not set codec driver clock params\n");
- return ret;
- }
-
- return 0;
-}
-
-/**
- * mpc8610_hpcd_machine_remove: Remove the sound device
- *
- * This function is called to remove the sound device for one SSI. We
- * de-program the DMACR and PMUXCR register.
- */
-static int mpc8610_hpcd_machine_remove(struct snd_soc_card *card)
-{
- struct mpc8610_hpcd_data *machine_data =
- container_of(card, struct mpc8610_hpcd_data, card);
- struct ccsr_guts __iomem *guts;
-
- guts = ioremap(guts_phys, sizeof(struct ccsr_guts));
- if (!guts) {
- dev_err(card->dev, "could not map global utilities\n");
- return -ENOMEM;
- }
-
- /* Restore the signal routing */
-
- guts_set_dmacr(guts, machine_data->dma_id[0],
- machine_data->dma_channel_id[0], 0);
- guts_set_dmacr(guts, machine_data->dma_id[1],
- machine_data->dma_channel_id[1], 0);
-
- switch (machine_data->ssi_id) {
- case 0:
- clrsetbits_be32(&guts->pmuxcr,
- CCSR_GUTS_PMUXCR_SSI1_MASK, CCSR_GUTS_PMUXCR_SSI1_LA);
- break;
- case 1:
- clrsetbits_be32(&guts->pmuxcr,
- CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI2_LA);
- break;
- }
-
- iounmap(guts);
-
- return 0;
-}
-
-/**
- * mpc8610_hpcd_ops: ASoC machine driver operations
- */
-static const struct snd_soc_ops mpc8610_hpcd_ops = {
- .startup = mpc8610_hpcd_startup,
-};
-
-/**
- * mpc8610_hpcd_probe: platform probe function for the machine driver
- *
- * Although this is a machine driver, the SSI node is the "master" node with
- * respect to audio hardware connections. Therefore, we create a new ASoC
- * device for each new SSI node that has a codec attached.
- */
-static int mpc8610_hpcd_probe(struct platform_device *pdev)
-{
- struct device *dev = pdev->dev.parent;
- /* ssi_pdev is the platform device for the SSI node that probed us */
- struct platform_device *ssi_pdev = to_platform_device(dev);
- struct device_node *np = ssi_pdev->dev.of_node;
- struct device_node *codec_np = NULL;
- struct mpc8610_hpcd_data *machine_data;
- struct snd_soc_dai_link_component *comp;
- int ret;
- const char *sprop;
- const u32 *iprop;
-
- /* Find the codec node for this SSI. */
- codec_np = of_parse_phandle(np, "codec-handle", 0);
- if (!codec_np) {
- dev_err(dev, "invalid codec node\n");
- return -EINVAL;
- }
-
- machine_data = kzalloc(sizeof(struct mpc8610_hpcd_data), GFP_KERNEL);
- if (!machine_data) {
- ret = -ENOMEM;
- goto error_alloc;
- }
-
- comp = devm_kzalloc(&pdev->dev, 6 * sizeof(*comp), GFP_KERNEL);
- if (!comp) {
- ret = -ENOMEM;
- goto error_alloc;
- }
-
- machine_data->dai[0].cpus = &comp[0];
- machine_data->dai[0].codecs = &comp[1];
- machine_data->dai[0].platforms = &comp[2];
-
- machine_data->dai[0].num_cpus = 1;
- machine_data->dai[0].num_codecs = 1;
- machine_data->dai[0].num_platforms = 1;
-
- machine_data->dai[1].cpus = &comp[3];
- machine_data->dai[1].codecs = &comp[4];
- machine_data->dai[1].platforms = &comp[5];
-
- machine_data->dai[1].num_cpus = 1;
- machine_data->dai[1].num_codecs = 1;
- machine_data->dai[1].num_platforms = 1;
-
- machine_data->dai[0].cpus->dai_name = dev_name(&ssi_pdev->dev);
- machine_data->dai[0].ops = &mpc8610_hpcd_ops;
-
- /* ASoC core can match codec with device node */
- machine_data->dai[0].codecs->of_node = codec_np;
-
- /* The DAI name from the codec (snd_soc_dai_driver.name) */
- machine_data->dai[0].codecs->dai_name = "cs4270-hifi";
-
- /* We register two DAIs per SSI, one for playback and the other for
- * capture. Currently, we only support codecs that have one DAI for
- * both playback and capture.
- */
- memcpy(&machine_data->dai[1], &machine_data->dai[0],
- sizeof(struct snd_soc_dai_link));
-
- /* Get the device ID */
- iprop = of_get_property(np, "cell-index", NULL);
- if (!iprop) {
- dev_err(&pdev->dev, "cell-index property not found\n");
- ret = -EINVAL;
- goto error;
- }
- machine_data->ssi_id = be32_to_cpup(iprop);
-
- /* Get the serial format and clock direction. */
- sprop = of_get_property(np, "fsl,mode", NULL);
- if (!sprop) {
- dev_err(&pdev->dev, "fsl,mode property not found\n");
- ret = -EINVAL;
- goto error;
- }
-
- if (strcasecmp(sprop, "i2s-slave") == 0) {
- machine_data->dai_format =
- SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBP_CFP;
- machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT;
- machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN;
-
- /* In i2s-slave mode, the codec has its own clock source, so we
- * need to get the frequency from the device tree and pass it to
- * the codec driver.
- */
- iprop = of_get_property(codec_np, "clock-frequency", NULL);
- if (!iprop || !*iprop) {
- dev_err(&pdev->dev, "codec bus-frequency "
- "property is missing or invalid\n");
- ret = -EINVAL;
- goto error;
- }
- machine_data->clk_frequency = be32_to_cpup(iprop);
- } else if (strcasecmp(sprop, "i2s-master") == 0) {
- machine_data->dai_format =
- SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBC_CFC;
- machine_data->codec_clk_direction = SND_SOC_CLOCK_IN;
- machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT;
- } else if (strcasecmp(sprop, "lj-slave") == 0) {
- machine_data->dai_format =
- SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_CBP_CFP;
- machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT;
- machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN;
- } else if (strcasecmp(sprop, "lj-master") == 0) {
- machine_data->dai_format =
- SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_CBC_CFC;
- machine_data->codec_clk_direction = SND_SOC_CLOCK_IN;
- machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT;
- } else if (strcasecmp(sprop, "rj-slave") == 0) {
- machine_data->dai_format =
- SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_CBP_CFP;
- machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT;
- machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN;
- } else if (strcasecmp(sprop, "rj-master") == 0) {
- machine_data->dai_format =
- SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_CBC_CFC;
- machine_data->codec_clk_direction = SND_SOC_CLOCK_IN;
- machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT;
- } else if (strcasecmp(sprop, "ac97-slave") == 0) {
- machine_data->dai_format =
- SND_SOC_DAIFMT_AC97 | SND_SOC_DAIFMT_CBP_CFP;
- machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT;
- machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN;
- } else if (strcasecmp(sprop, "ac97-master") == 0) {
- machine_data->dai_format =
- SND_SOC_DAIFMT_AC97 | SND_SOC_DAIFMT_CBC_CFC;
- machine_data->codec_clk_direction = SND_SOC_CLOCK_IN;
- machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT;
- } else {
- dev_err(&pdev->dev,
- "unrecognized fsl,mode property '%s'\n", sprop);
- ret = -EINVAL;
- goto error;
- }
-
- if (!machine_data->clk_frequency) {
- dev_err(&pdev->dev, "unknown clock frequency\n");
- ret = -EINVAL;
- goto error;
- }
-
- /* Find the playback DMA channel to use. */
- machine_data->dai[0].platforms->name = machine_data->platform_name[0];
- ret = fsl_asoc_get_dma_channel(np, "fsl,playback-dma",
- &machine_data->dai[0],
- &machine_data->dma_channel_id[0],
- &machine_data->dma_id[0]);
- if (ret) {
- dev_err(&pdev->dev, "missing/invalid playback DMA phandle\n");
- goto error;
- }
-
- /* Find the capture DMA channel to use. */
- machine_data->dai[1].platforms->name = machine_data->platform_name[1];
- ret = fsl_asoc_get_dma_channel(np, "fsl,capture-dma",
- &machine_data->dai[1],
- &machine_data->dma_channel_id[1],
- &machine_data->dma_id[1]);
- if (ret) {
- dev_err(&pdev->dev, "missing/invalid capture DMA phandle\n");
- goto error;
- }
-
- /* Initialize our DAI data structure. */
- machine_data->dai[0].stream_name = "playback";
- machine_data->dai[1].stream_name = "capture";
- machine_data->dai[0].name = machine_data->dai[0].stream_name;
- machine_data->dai[1].name = machine_data->dai[1].stream_name;
-
- machine_data->card.probe = mpc8610_hpcd_machine_probe;
- machine_data->card.remove = mpc8610_hpcd_machine_remove;
- machine_data->card.name = pdev->name; /* The platform driver name */
- machine_data->card.owner = THIS_MODULE;
- machine_data->card.dev = &pdev->dev;
- machine_data->card.num_links = 2;
- machine_data->card.dai_link = machine_data->dai;
-
- /* Register with ASoC */
- ret = snd_soc_register_card(&machine_data->card);
- if (ret) {
- dev_err(&pdev->dev, "could not register card\n");
- goto error;
- }
-
- of_node_put(codec_np);
-
- return 0;
-
-error:
- kfree(machine_data);
-error_alloc:
- of_node_put(codec_np);
- return ret;
-}
-
-/**
- * mpc8610_hpcd_remove: remove the platform device
- *
- * This function is called when the platform device is removed.
- */
-static void mpc8610_hpcd_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
- struct mpc8610_hpcd_data *machine_data =
- container_of(card, struct mpc8610_hpcd_data, card);
-
- snd_soc_unregister_card(card);
- kfree(machine_data);
-}
-
-static struct platform_driver mpc8610_hpcd_driver = {
- .probe = mpc8610_hpcd_probe,
- .remove_new = mpc8610_hpcd_remove,
- .driver = {
- /* The name must match 'compatible' property in the device tree,
- * in lowercase letters.
- */
- .name = "snd-soc-mpc8610hpcd",
- },
-};
-
-/**
- * mpc8610_hpcd_init: machine driver initialization.
- *
- * This function is called when this module is loaded.
- */
-static int __init mpc8610_hpcd_init(void)
-{
- struct device_node *guts_np;
- struct resource res;
-
- pr_info("Freescale MPC8610 HPCD ALSA SoC machine driver\n");
-
- /* Get the physical address of the global utilities registers */
- guts_np = of_find_compatible_node(NULL, NULL, "fsl,mpc8610-guts");
- if (of_address_to_resource(guts_np, 0, &res)) {
- pr_err("mpc8610-hpcd: missing/invalid global utilities node\n");
- of_node_put(guts_np);
- return -EINVAL;
- }
- guts_phys = res.start;
- of_node_put(guts_np);
-
- return platform_driver_register(&mpc8610_hpcd_driver);
-}
-
-/**
- * mpc8610_hpcd_exit: machine driver exit
- *
- * This function is called when this driver is unloaded.
- */
-static void __exit mpc8610_hpcd_exit(void)
-{
- platform_driver_unregister(&mpc8610_hpcd_driver);
-}
-
-module_init(mpc8610_hpcd_init);
-module_exit(mpc8610_hpcd_exit);
-
-MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
-MODULE_DESCRIPTION("Freescale MPC8610 HPCD ALSA SoC machine driver");
-MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c
index 76a9f1e8cdd5..83e3ba773fbd 100644
--- a/sound/soc/generic/audio-graph-card.c
+++ b/sound/soc/generic/audio-graph-card.c
@@ -9,7 +9,6 @@
#include <linux/clk.h>
#include <linux/device.h>
-#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/of.h>
diff --git a/sound/soc/generic/audio-graph-card2-custom-sample.dtsi b/sound/soc/generic/audio-graph-card2-custom-sample.dtsi
index 8acaa2ddb335..9efd31206c9b 100644
--- a/sound/soc/generic/audio-graph-card2-custom-sample.dtsi
+++ b/sound/soc/generic/audio-graph-card2-custom-sample.dtsi
@@ -58,12 +58,32 @@
* | |-> codec13
* +-+
*
- * [Multi-CPU/Codec]
+ * [Multi-CPU/Codec-0]
* +-+ +-+
* cpu1 <--| |<-@--------->| |-> codec1
* cpu2 <--| | | |-> codec2
* +-+ +-+
*
+ * [Multi-CPU/Codec-1]
+ *
+ * +-+ +-+
+ * | |<-@--------->| |
+ * | | | |
+ * cpu8 <--| |<----------->| |-> codec14
+ * cpu9 <--| |<---+------->| |-> codec15
+ * +-+ \------>| |-> codec16
+ * +-+
+ *
+ * [Multi-CPU/Codec-2]
+ *
+ * +-+ +-+
+ * | |<-@--------->| |
+ * | | | |
+ * cpu10 <-| |<----------->| |-> codec17
+ * cpu11 <-| |<-----+----->| |-> codec18
+ * cpu12 <-| |<----/ +-+
+ * +-+
+ *
* [DPCM]
*
* CPU3/CPU4 are converting rate to 44100
@@ -144,16 +164,39 @@
*/
&cpu0
- /* [Semi-Multi] */
+ /*
+ * [Semi-Multi]
+ * cpu7/codec12/codec13
+ */
&sm0
/*
- * [Multi-CPU/Codec]: cpu side only
+ * [Multi-CPU/Codec-0]: cpu side only
* cpu1/cpu2/codec1/codec2
*/
&mcpu0
/*
+ * [Multi-CPU/Codec-1]: cpu side only
+ * cpu8/cpu9/codec14/codec15/codec16
+ *
+ * Because it will reach to the maximum of sound minor number,
+ * disable it so far.
+ * If you want to try it, please disable some other one instead.
+ */
+ //&mcpu1
+
+ /*
+ * [Multi-CPU/Codec-2]: cpu side only
+ * cpu10/cpu11/cpu12/codec17/codec18
+ *
+ * Because it will reach to the maximum of sound minor number,
+ * disable it so far.
+ * If you want to try it, please disable some other one instead.
+ */
+ //&mcpu2
+
+ /*
* [DPCM]: both FE / BE
* cpu3/cpu4/codec3
*/
@@ -182,64 +225,259 @@
#address-cells = <1>;
#size-cells = <0>;
+ /*
+ * [Multi-CPU-0]
+ *
+ * +---+ +---+
+ * cpu1 <--|A X|<-@------->|x a|-> codec1
+ * cpu2 <--|B | | b|-> codec2
+ * +---+ +---+
+ */
ports@0 {
reg = <0>;
#address-cells = <1>;
#size-cells = <0>;
- /* [Multi-CPU] */
- mcpu0: port@0 { reg = <0>; mcpu0_ep: endpoint { remote-endpoint = <&mcodec0_ep>; }; };
- port@1 { reg = <1>; mcpu1_ep: endpoint { remote-endpoint = <&cpu1_ep>; }; };
- port@2 { reg = <2>; mcpu2_ep: endpoint { remote-endpoint = <&cpu2_ep>; }; };
+ mcpu0: port@0 { reg = <0>; mcpu00_ep: endpoint { remote-endpoint = <&mcodec00_ep>; };};/* (X) to pair */
+ port@1 { reg = <1>; mcpu01_ep: endpoint { remote-endpoint = <&cpu1_ep>; };};/* (A) Multi Element */
+ port@2 { reg = <2>; mcpu02_ep: endpoint { remote-endpoint = <&cpu2_ep>; };};/* (B) Multi Element */
};
- /* [Multi-Codec] */
+ /*
+ * [Multi-Codec-0]
+ *
+ * +---+ +---+
+ * cpu1 <--|A X|<-@------->|x a|-> codec1
+ * cpu2 <--|B | | b|-> codec2
+ * +---+ +---+
+ */
ports@1 {
reg = <1>;
#address-cells = <1>;
#size-cells = <0>;
- port@0 { reg = <0>; mcodec0_ep: endpoint { remote-endpoint = <&mcpu0_ep>; }; };
- port@1 { reg = <1>; mcodec1_ep: endpoint { remote-endpoint = <&codec1_ep>; }; };
- port@2 { reg = <2>; mcodec2_ep: endpoint { remote-endpoint = <&codec2_ep>; }; };
+ port@0 { reg = <0>; mcodec00_ep: endpoint { remote-endpoint = <&mcpu00_ep>; };};/* (x) to pair */
+ port@1 { reg = <1>; mcodec01_ep: endpoint { remote-endpoint = <&codec1_ep>; };};/* (a) Multi Element */
+ port@2 { reg = <2>; mcodec02_ep: endpoint { remote-endpoint = <&codec2_ep>; };};/* (b) Multi Element */
};
- /* [DPCM-Multi]::BE */
+ /*
+ * [DPCM-Multi]::BE
+ *
+ * FE BE
+ * **** +---+
+ * cpu5 <-@--* *-----@--->|x a|-> codec4
+ * cpu6 <-@--* * | b|-> codec5
+ * **** +---+
+ */
ports@2 {
reg = <2>;
#address-cells = <1>;
#size-cells = <0>;
- port@0 { reg = <0>; mbe_ep: endpoint { remote-endpoint = <&be10_ep>; }; };
- port@1 { reg = <1>; mbe1_ep: endpoint { remote-endpoint = <&codec4_ep>; }; };
- port@2 { reg = <2>; mbe2_ep: endpoint { remote-endpoint = <&codec5_ep>; }; };
+ port@0 { reg = <0>; mbe_ep: endpoint { remote-endpoint = <&be10_ep>; };};/* (x) to pair */
+ port@1 { reg = <1>; mbe1_ep: endpoint { remote-endpoint = <&codec4_ep>; };};/* (a) Multi Element */
+ port@2 { reg = <2>; mbe2_ep: endpoint { remote-endpoint = <&codec5_ep>; };};/* (b) Multi Element */
};
- /* [Codec2Codec-Multi]::CPU */
+ /*
+ * [Codec2Codec-Multi]::CPU
+ *
+ * +---+
+ * +-@->|X A|-> codec8
+ * | | B|-> codec9
+ * | +---+
+ * | +---+
+ * +--->|x a|-> codec10
+ * | b|-> codec11
+ * +---+
+ */
ports@3 {
reg = <3>;
#address-cells = <1>;
#size-cells = <0>;
- port@0 { reg = <0>; mc2c0_ep: endpoint { remote-endpoint = <&c2cmf_ep>; }; };
- port@1 { reg = <1>; mc2c00_ep: endpoint { remote-endpoint = <&codec8_ep>; }; };
- port@2 { reg = <2>; mc2c01_ep: endpoint { remote-endpoint = <&codec9_ep>; }; };
+ port@0 { reg = <0>; mc2c0_ep: endpoint { remote-endpoint = <&c2cmf_ep>; };};/* (X) to pair */
+ port@1 { reg = <1>; mc2c00_ep: endpoint { remote-endpoint = <&codec8_ep>; };};/* (A) Multi Element */
+ port@2 { reg = <2>; mc2c01_ep: endpoint { remote-endpoint = <&codec9_ep>; };};/* (B) Multi Element */
};
- /* [Codec2Codec-Multi]::Codec */
+ /*
+ * [Codec2Codec-Multi]::Codec
+ *
+ * +---+
+ * +-@->|X A|-> codec8
+ * | | B|-> codec9
+ * | +---+
+ * | +---+
+ * +--->|x a|-> codec10
+ * | b|-> codec11
+ * +---+
+ */
ports@4 {
reg = <4>;
#address-cells = <1>;
#size-cells = <0>;
- port@0 { reg = <0>; mc2c1_ep: endpoint { remote-endpoint = <&c2cmb_ep>; }; };
- port@1 { reg = <1>; mc2c10_ep: endpoint { remote-endpoint = <&codec10_ep>; }; };
- port@2 { reg = <2>; mc2c11_ep: endpoint { remote-endpoint = <&codec11_ep>; }; };
+ port@0 { reg = <0>; mc2c1_ep: endpoint { remote-endpoint = <&c2cmb_ep>; };};/* (x) to pair */
+ port@1 { reg = <1>; mc2c10_ep: endpoint { remote-endpoint = <&codec10_ep>; };};/* (a) Multi Element */
+ port@2 { reg = <2>; mc2c11_ep: endpoint { remote-endpoint = <&codec11_ep>; };};/* (b) Multi Element */
};
- /* [Semi-Multi] */
+ /*
+ * [Semi-Multi]
+ *
+ * +---+
+ * cpu7 <-@------->|X A|-> codec12
+ * | B|-> codec13
+ * +---+
+ */
ports@5 {
reg = <5>;
#address-cells = <1>;
#size-cells = <0>;
- port@0 { reg = <0>; smcodec0_ep: endpoint { remote-endpoint = <&cpu7_ep>; }; };
- port@1 { reg = <1>; smcodec1_ep: endpoint { remote-endpoint = <&codec12_ep>; }; };
- port@2 { reg = <2>; smcodec2_ep: endpoint { remote-endpoint = <&codec13_ep>; }; };
+ port@0 { reg = <0>; smcodec0_ep: endpoint { remote-endpoint = <&cpu7_ep>; };};/* (X) to pair */
+ port@1 { reg = <1>; smcodec1_ep: endpoint { remote-endpoint = <&codec12_ep>; };};/* (A) Multi Element */
+ port@2 { reg = <2>; smcodec2_ep: endpoint { remote-endpoint = <&codec13_ep>; };};/* (B) Multi Element */
+ };
+
+ /*
+ * [Multi-CPU-1]
+ *
+ * +---+ +---+
+ * | X|<-@------->|x |
+ * | | | |
+ * cpu8 <--|A 1|<--------->|3 a|-> codec14
+ * cpu9 <--|B 2|<---+----->|4 b|-> codec15
+ * +---+ \---->|5 c|-> codec16
+ * +---+
+ */
+ ports@6 {
+ reg = <6>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ mcpu1: port@0 { reg = <0>; mcpu10_ep: endpoint { remote-endpoint = <&mcodec10_ep>; };}; /* (X) to pair */
+ port@1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+ mcpu11_ep: endpoint@0 { reg = <0>; remote-endpoint = <&cpu8_ep>; }; /* (A) Multi Element */
+ mcpu11_ep_0: endpoint@1 { reg = <1>; remote-endpoint = <&mcodec11_ep_0>; }; /* (1) connected Codec */
+ };
+ port@2 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <2>;
+ mcpu12_ep: endpoint@0 { reg = <0>; remote-endpoint = <&cpu9_ep>; }; /* (B) Multi Element */
+ mcpu12_ep_0: endpoint@1 { reg = <1>; remote-endpoint = <&mcodec12_ep_0>; }; /* (2) connected Codec */
+ mcpu12_ep_1: endpoint@2 { reg = <2>; remote-endpoint = <&mcodec13_ep_0>; }; /* (2) connected Codec */
+ };
+ };
+
+ /*
+ * [Multi-Codec-1]
+ *
+ * +---+ +---+
+ * | X|<-@------->|x |
+ * | | | |
+ * cpu8 <--|A 1|<--------->|3 a|-> codec14
+ * cpu9 <--|B 2|<---+----->|4 b|-> codec15
+ * +---+ \---->|5 c|-> codec16
+ * +---+
+ */
+ ports@7 {
+ reg = <7>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ port@0 { reg = <0>; mcodec10_ep: endpoint { remote-endpoint = <&mcpu10_ep>; };}; /* (x) to pair */
+ port@1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+ mcodec11_ep: endpoint@0 { reg = <0>; remote-endpoint = <&codec14_ep>; }; /* (a) Multi Element */
+ mcodec11_ep_0: endpoint@1 { reg = <1>; remote-endpoint = <&mcpu11_ep_0>; }; /* (3) connected CPU */
+ };
+ port@2 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <2>;
+ mcodec12_ep: endpoint@0 { reg = <0>; remote-endpoint = <&codec15_ep>; }; /* (b) Multi Element */
+ mcodec12_ep_0: endpoint@1 { reg = <1>; remote-endpoint = <&mcpu12_ep_0>; }; /* (4) connected CPU */
+ };
+ port@3 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <3>;
+ mcodec13_ep: endpoint@0 { reg = <0>; remote-endpoint = <&codec16_ep>; }; /* (c) Multi Element */
+ mcodec13_ep_0: endpoint@1 { reg = <1>; remote-endpoint = <&mcpu12_ep_1>; }; /* (5) connected CPU */
+ };
+ };
+
+ /*
+ * [Multi-CPU-2]
+ *
+ * +---+ +---+
+ * | X|<-@------->|x |
+ * | | | |
+ * cpu10 <-|A 1|<--------->|4 a|-> codec17
+ * cpu11 <-|B 2|<-----+--->|5 b|-> codec18
+ * cpu12 <-|C 3|<----/ +---+
+ * +---+
+ */
+ ports@8 {
+ reg = <8>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ mcpu2: port@0 { reg = <0>; mcpu20_ep: endpoint { remote-endpoint = <&mcodec20_ep>; };}; /* (X) to pair */
+ port@1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+ mcpu21_ep: endpoint@0 { reg = <0>; remote-endpoint = <&cpu10_ep>; }; /* (A) Multi Element */
+ mcpu21_ep_0: endpoint@1 { reg = <1>; remote-endpoint = <&mcodec21_ep_0>; }; /* (1) connected Codec */
+ };
+ port@2 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <2>;
+ mcpu22_ep: endpoint@0 { reg = <0>; remote-endpoint = <&cpu11_ep>; }; /* (B) Multi Element */
+ mcpu22_ep_0: endpoint@1 { reg = <1>; remote-endpoint = <&mcodec22_ep_0>; }; /* (2) connected Codec */
+ };
+ port@3 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <3>;
+ mcpu23_ep: endpoint@0 { reg = <0>; remote-endpoint = <&cpu12_ep>; }; /* (C) Multi Element */
+ mcpu23_ep_0: endpoint@1 { reg = <1>; remote-endpoint = <&mcodec22_ep_1>; }; /* (3) connected Codec */
+ };
+ };
+
+ /*
+ * [Multi-Codec-2]
+ *
+ * +---+ +---+
+ * | X|<-@------->|x |
+ * | | | |
+ * cpu10 <-|A 1|<--------->|4 a|-> codec17
+ * cpu11 <-|B 2|<-----+--->|5 b|-> codec18
+ * cpu12 <-|C 3|<----/ +---+
+ * +---+
+ */
+ ports@9 {
+ reg = <9>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ port@0 { reg = <0>; mcodec20_ep: endpoint { remote-endpoint = <&mcpu20_ep>; };}; /* (x) to pair */
+ port@1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+ mcodec21_ep: endpoint@0 { reg = <0>; remote-endpoint = <&codec17_ep>; }; /* (a) Multi Element */
+ mcodec21_ep_0: endpoint@1 { reg = <1>; remote-endpoint = <&mcpu21_ep_0>; }; /* (4) connected CPU */
+ };
+ port@2 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <2>;
+ mcodec22_ep: endpoint@0 { reg = <0>; remote-endpoint = <&codec18_ep>; }; /* (b) Multi Element */
+ mcodec22_ep_0: endpoint@1 { reg = <1>; remote-endpoint = <&mcpu22_ep_0>; }; /* (5) connected CPU */
+ mcodec22_ep_1: endpoint@2 { reg = <2>; remote-endpoint = <&mcpu23_ep_0>; }; /* (5) connected CPU */
+ };
};
};
@@ -252,11 +490,27 @@
#address-cells = <1>;
#size-cells = <0>;
- /* [DPCM]::FE */
+ /*
+ * [DPCM]::FE
+ *
+ * FE BE
+ * ****
+ * cpu3 <-@(fe00)--* *--(be0)@--> codec3
+ * cpu4 <-@(fe01)--* * (44.1kHz)
+ * ****
+ */
fe00: port@0 { reg = <0>; fe00_ep: endpoint { remote-endpoint = <&cpu3_ep>; }; };
fe01: port@1 { reg = <1>; fe01_ep: endpoint { remote-endpoint = <&cpu4_ep>; }; };
- /* [DPCM-Multi]::FE */
+ /*
+ * [DPCM-Multi]::FE
+ *
+ * FE BE
+ * **** +-+
+ * cpu5 <-@(fe10)--* *---(be1)@-->| |-> codec4
+ * cpu6 <-@(fe11)--* * | |-> codec5
+ * **** +-+
+ */
fe10: port@2 { reg = <2>; fe10_ep: endpoint { remote-endpoint = <&cpu5_ep>; }; };
fe11: port@3 { reg = <3>; fe11_ep: endpoint { remote-endpoint = <&cpu6_ep>; }; };
};
@@ -266,10 +520,26 @@
#address-cells = <1>;
#size-cells = <0>;
- /* [DPCM]::BE */
+ /*
+ * [DPCM]::BE
+ *
+ * FE BE
+ * ****
+ * cpu3 <-@(fe00)--* *--(be0)@--> codec3
+ * cpu4 <-@(fe01)--* * (44.1kHz)
+ * ****
+ */
be0: port@0 { reg = <0>; be00_ep: endpoint { remote-endpoint = <&codec3_ep>; }; };
- /* [DPCM-Multi]::BE */
+ /*
+ * [DPCM-Multi]::BE
+ *
+ * FE BE
+ * **** +-+
+ * cpu5 <-@(fe10)--* *---(be1)@-->| |-> codec4
+ * cpu6 <-@(fe11)--* * | |-> codec5
+ * **** +-+
+ */
be1: port@1 { reg = <1>; be10_ep: endpoint { remote-endpoint = <&mbe_ep>; }; };
};
};
@@ -277,7 +547,13 @@
codec2codec {
#address-cells = <1>;
#size-cells = <0>;
- /* [Codec2Codec] */
+ /*
+ * [Codec2Codec]
+ *
+ * +-@(c2c)-> codec6
+ * |
+ * +--------> codec7
+ */
ports@0 {
reg = <0>;
@@ -289,7 +565,18 @@
port@1 { reg = <1>; c2cb_ep: endpoint { remote-endpoint = <&codec7_ep>; }; };
};
- /* [Codec2Codec-Multi] */
+ /*
+ * [Codec2Codec-Multi]
+ *
+ * +-+
+ * +-@(c2c_m)-->| |-> codec8
+ * | | |-> codec9
+ * | +-+
+ * | +-+
+ * +----------->| |-> codec10
+ * | |-> codec11
+ * +-+
+ */
ports@1 {
reg = <1>;
@@ -323,9 +610,9 @@
/* [Normal] */
cpu0: port@0 { reg = <0>; cpu0_ep: endpoint { remote-endpoint = <&codec0_ep>; }; };
- /* [Multi-CPU] */
- port@1 { reg = <1>; cpu1_ep: endpoint { remote-endpoint = <&mcpu1_ep>; }; };
- port@2 { reg = <2>; cpu2_ep: endpoint { remote-endpoint = <&mcpu2_ep>; }; };
+ /* [Multi-CPU-0] */
+ port@1 { reg = <1>; cpu1_ep: endpoint { remote-endpoint = <&mcpu01_ep>; }; };
+ port@2 { reg = <2>; cpu2_ep: endpoint { remote-endpoint = <&mcpu02_ep>; }; };
/* [DPCM]::FE */
port@3 { reg = <3>; cpu3_ep: endpoint { remote-endpoint = <&fe00_ep>; }; };
@@ -337,6 +624,14 @@
/* [Semi-Multi] */
sm0: port@7 { reg = <7>; cpu7_ep: endpoint { remote-endpoint = <&smcodec0_ep>; }; };
+
+ /* [Multi-CPU-1] */
+ port@8 { reg = <8>; cpu8_ep: endpoint { remote-endpoint = <&mcpu11_ep>; }; };
+ port@9 { reg = <9>; cpu9_ep: endpoint { remote-endpoint = <&mcpu12_ep>; }; };
+ /* [Multi-CPU-2] */
+ port@a { reg = <10>; cpu10_ep: endpoint { remote-endpoint = <&mcpu21_ep>; }; };
+ port@b { reg = <11>; cpu11_ep: endpoint { remote-endpoint = <&mcpu22_ep>; }; };
+ port@c { reg = <12>; cpu12_ep: endpoint { remote-endpoint = <&mcpu23_ep>; }; };
};
};
@@ -363,9 +658,9 @@
/* [Normal] */
port@0 { reg = <0>; codec0_ep: endpoint { remote-endpoint = <&cpu0_ep>; }; };
- /* [Multi-Codec] */
- port@1 { reg = <1>; codec1_ep: endpoint { remote-endpoint = <&mcodec1_ep>; }; };
- port@2 { reg = <2>; codec2_ep: endpoint { remote-endpoint = <&mcodec2_ep>; }; };
+ /* [Multi-Codec-0] */
+ port@1 { reg = <1>; codec1_ep: endpoint { remote-endpoint = <&mcodec01_ep>; }; };
+ port@2 { reg = <2>; codec2_ep: endpoint { remote-endpoint = <&mcodec02_ep>; }; };
/* [DPCM]::BE */
port@3 {
@@ -395,6 +690,13 @@
port@c { reg = <12>; codec12_ep: endpoint { remote-endpoint = <&smcodec1_ep>; }; };
port@d { reg = <13>; codec13_ep: endpoint { remote-endpoint = <&smcodec2_ep>; }; };
+ /* [Multi-Codec-1] */
+ port@e { reg = <14>; codec14_ep: endpoint { remote-endpoint = <&mcodec11_ep>; }; };
+ port@f { reg = <15>; codec15_ep: endpoint { remote-endpoint = <&mcodec12_ep>; }; };
+ port@10 { reg = <16>; codec16_ep: endpoint { remote-endpoint = <&mcodec13_ep>; }; };
+ /* [Multi-Codec-2] */
+ port@11 { reg = <17>; codec17_ep: endpoint { remote-endpoint = <&mcodec21_ep>; }; };
+ port@12 { reg = <18>; codec18_ep: endpoint { remote-endpoint = <&mcodec22_ep>; }; };
};
};
};
diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c
index 7146611df730..62606e20be9a 100644
--- a/sound/soc/generic/audio-graph-card2.c
+++ b/sound/soc/generic/audio-graph-card2.c
@@ -8,7 +8,6 @@
// based on ${LINUX}/sound/soc/generic/audio-graph-card.c
#include <linux/clk.h>
#include <linux/device.h>
-#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/of.h>
@@ -83,32 +82,32 @@
Multi-CPU/Codec
************************************
-It has connection part (= X) and list part (= y).
-links indicates connection part of CPU side (= A).
+It has link connection part (= X,x) and list part (= A,B,a,b).
+"links" is connection part of CPU side (= @).
- +-+ (A) +-+
- CPU1 --(y) | | <-(X)--(X)-> | | (y)-- Codec1
- CPU2 --(y) | | | | (y)-- Codec2
- +-+ +-+
+ +----+ +---+
+ CPU1 --|A X| <-@----> |x a|-- Codec1
+ CPU2 --|B | | b|-- Codec2
+ +----+ +---+
- sound {
- compatible = "audio-graph-card2";
+ sound {
+ compatible = "audio-graph-card2";
-(A) links = <&mcpu>;
+(@) links = <&mcpu>;
- multi {
- ports@0 {
-(X) (A) mcpu: port@0 { mcpu0_ep: endpoint { remote-endpoint = <&mcodec0_ep>; }; };
-(y) port@1 { mcpu1_ep: endpoint { remote-endpoint = <&cpu1_ep>; }; };
-(y) port@2 { mcpu2_ep: endpoint { remote-endpoint = <&cpu2_ep>; }; };
- };
- ports@1 {
-(X) port@0 { mcodec0_ep: endpoint { remote-endpoint = <&mcpu0_ep>; }; };
-(y) port@1 { mcodec1_ep: endpoint { remote-endpoint = <&codec1_ep>; }; };
-(y) port@2 { mcodec2_ep: endpoint { remote-endpoint = <&codec2_ep>; }; };
- };
+ multi {
+ ports@0 {
+(@) mcpu: port@0 { mcpu0_ep: endpoint { remote-endpoint = <&mcodec0_ep>; }; }; // (X) to pair
+ port@1 { mcpu1_ep: endpoint { remote-endpoint = <&cpu1_ep>; }; }; // (A) Multi Element
+ port@2 { mcpu2_ep: endpoint { remote-endpoint = <&cpu2_ep>; }; }; // (B) Multi Element
+ };
+ ports@1 {
+ port@0 { mcodec0_ep: endpoint { remote-endpoint = <&mcpu0_ep>; }; }; // (x) to pair
+ port@1 { mcodec1_ep: endpoint { remote-endpoint = <&codec1_ep>; }; }; // (a) Multi Element
+ port@2 { mcodec2_ep: endpoint { remote-endpoint = <&codec2_ep>; }; }; // (b) Multi Element
};
};
+ };
CPU {
ports {
@@ -328,9 +327,9 @@ static struct device_node *graph_get_next_multi_ep(struct device_node **port)
/*
* multi {
* ports {
- * => lnk: port@0 { ... };
- * port@1 { ep { ... = rep0 } };
- * port@2 { ep { ... = rep1 } };
+ * => lnk: port@0 { ... }; // to pair
+ * port@1 { ep { ... = rep0 } }; // Multi Element
+ * port@2 { ep { ... = rep1 } }; // Multi Element
* ...
* };
* };
@@ -504,40 +503,203 @@ static int __graph_parse_node(struct simple_util_priv *priv,
return 0;
}
-static int graph_parse_node(struct simple_util_priv *priv,
- enum graph_type gtype,
- struct device_node *port,
- struct link_info *li, int is_cpu)
+static int graph_parse_node_multi_nm(struct snd_soc_dai_link *dai_link,
+ int *nm_idx, int cpu_idx,
+ struct device_node *mcpu_port)
{
- struct device_node *ep;
- int ret = 0;
+ /*
+ * +---+ +---+
+ * | X|<-@------->|x |
+ * | | | |
+ * cpu0 <--|A 1|<--------->|4 a|-> codec0
+ * cpu1 <--|B 2|<-----+--->|5 b|-> codec1
+ * cpu2 <--|C 3|<----/ +---+
+ * +---+
+ *
+ * multi {
+ * ports {
+ * port@0 { mcpu_top_ep {... = mcodec_ep; }; }; // (X) to pair
+ * <mcpu_port> port@1 { mcpu0_ep { ... = cpu0_ep; }; // (A) Multi Element
+ * mcpu0_ep_0 { ... = mcodec0_ep_0; }; }; // (1) connected Codec
+ * port@2 { mcpu1_ep { ... = cpu1_ep; }; // (B) Multi Element
+ * mcpu1_ep_0 { ... = mcodec1_ep_0; }; }; // (2) connected Codec
+ * port@3 { mcpu2_ep { ... = cpu2_ep; }; // (C) Multi Element
+ * mcpu2_ep_0 { ... = mcodec1_ep_1; }; }; // (3) connected Codec
+ * };
+ *
+ * ports {
+ * port@0 { mcodec_top_ep {... = mcpu_ep; }; }; // (x) to pair
+ * <mcodec_port>port@1 { mcodec0_ep { ... = codec0_ep; }; // (a) Multi Element
+ * mcodec0_ep_0 { ... = mcpu0_ep_0; }; }; // (4) connected CPU
+ * port@2 { mcodec1_ep { ... = codec1_ep; }; // (b) Multi Element
+ * mcodec1_ep_0 { ... = mcpu1_ep_0; }; // (5) connected CPU
+ * mcodec1_ep_1 { ... = mcpu2_ep_0; }; }; // (5) connected CPU
+ * };
+ * };
+ */
+ struct device_node *mcpu_ep = port_to_endpoint(mcpu_port);
+ struct device_node *mcpu_ep_n = mcpu_ep;
+ struct device_node *mcpu_port_top = of_get_next_child(of_get_parent(mcpu_port), NULL);
+ struct device_node *mcpu_ep_top = port_to_endpoint(mcpu_port_top);
+ struct device_node *mcodec_ep_top = of_graph_get_remote_endpoint(mcpu_ep_top);
+ struct device_node *mcodec_port_top = of_get_parent(mcodec_ep_top);
+ struct device_node *mcodec_ports = of_get_parent(mcodec_port_top);
+ int nm_max = max(dai_link->num_cpus, dai_link->num_codecs);
+ int ret = -EINVAL;
- if (graph_lnk_is_multi(port)) {
- int idx;
+ if (cpu_idx > dai_link->num_cpus)
+ goto mcpu_err;
- of_node_get(port);
+ while (1) {
+ struct device_node *mcodec_ep_n;
+ struct device_node *mcodec_port_i;
+ struct device_node *mcodec_port;
+ int codec_idx;
- for (idx = 0;; idx++) {
- ep = graph_get_next_multi_ep(&port);
- if (!ep)
- break;
+ if (*nm_idx > nm_max)
+ break;
- ret = __graph_parse_node(priv, gtype, ep,
- li, is_cpu, idx);
- of_node_put(ep);
- if (ret < 0)
+ mcpu_ep_n = of_get_next_child(mcpu_port, mcpu_ep_n);
+ if (!mcpu_ep_n) {
+ ret = 0;
+ break;
+ }
+
+ mcodec_ep_n = of_graph_get_remote_endpoint(mcpu_ep_n);
+ mcodec_port = of_get_parent(mcodec_ep_n);
+
+ if (mcodec_ports != of_get_parent(mcodec_port))
+ goto mcpu_err;
+
+ codec_idx = 0;
+ mcodec_port_i = of_get_next_child(mcodec_ports, NULL);
+ while (1) {
+ if (codec_idx > dai_link->num_codecs)
+ goto mcodec_err;
+
+ mcodec_port_i = of_get_next_child(mcodec_ports, mcodec_port_i);
+
+ if (!mcodec_port_i)
+ goto mcodec_err;
+
+ if (mcodec_port_i == mcodec_port)
break;
+
+ codec_idx++;
}
- } else {
- /* Single CPU / Codec */
- ep = port_to_endpoint(port);
- ret = __graph_parse_node(priv, gtype, ep, li, is_cpu, 0);
+
+ dai_link->ch_maps[*nm_idx].cpu = cpu_idx;
+ dai_link->ch_maps[*nm_idx].codec = codec_idx;
+
+ (*nm_idx)++;
+
+ of_node_put(mcodec_port_i);
+mcodec_err:
+ of_node_put(mcodec_port);
+ of_node_put(mcpu_ep_n);
+ of_node_put(mcodec_ep_n);
+ }
+mcpu_err:
+ of_node_put(mcpu_ep);
+ of_node_put(mcpu_port_top);
+ of_node_put(mcpu_ep_top);
+ of_node_put(mcodec_ep_top);
+ of_node_put(mcodec_port_top);
+ of_node_put(mcodec_ports);
+
+ return ret;
+}
+
+static int graph_parse_node_multi(struct simple_util_priv *priv,
+ enum graph_type gtype,
+ struct device_node *port,
+ struct link_info *li, int is_cpu)
+{
+ struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
+ struct device *dev = simple_priv_to_dev(priv);
+ struct device_node *ep;
+ int ret = -ENOMEM;
+ int nm_idx = 0;
+ int nm_max = max(dai_link->num_cpus, dai_link->num_codecs);
+
+ /*
+ * create ch_maps if CPU:Codec = N:M
+ * DPCM is out of scope
+ */
+ if (gtype != GRAPH_DPCM && !dai_link->ch_maps &&
+ dai_link->num_cpus > 1 && dai_link->num_codecs > 1 &&
+ dai_link->num_cpus != dai_link->num_codecs) {
+
+ dai_link->ch_maps = devm_kcalloc(dev, nm_max,
+ sizeof(struct snd_soc_dai_link_ch_map), GFP_KERNEL);
+ if (!dai_link->ch_maps)
+ goto multi_err;
+ }
+
+ for (int idx = 0;; idx++) {
+ /*
+ * multi {
+ * ports {
+ * <port> port@0 { ... }; // to pair
+ * port@1 { mcpu1_ep { ... = cpu1_ep };}; // Multi Element
+ * port@2 { mcpu2_ep { ... = cpu2_ep };}; // Multi Element
+ * };
+ * };
+ *
+ * cpu {
+ * ports {
+ * <ep> port@0 { cpu1_ep { ... = mcpu1_ep };};
+ * };
+ * };
+ */
+ ep = graph_get_next_multi_ep(&port);
+ if (!ep)
+ break;
+
+ ret = __graph_parse_node(priv, gtype, ep, li, is_cpu, idx);
of_node_put(ep);
+ if (ret < 0)
+ goto multi_err;
+
+ /* CPU:Codec = N:M */
+ if (is_cpu && dai_link->ch_maps) {
+ ret = graph_parse_node_multi_nm(dai_link, &nm_idx, idx, port);
+ if (ret < 0)
+ goto multi_err;
+ }
}
+ if (is_cpu && dai_link->ch_maps && (nm_idx != nm_max))
+ ret = -EINVAL;
+
+multi_err:
return ret;
}
+static int graph_parse_node_single(struct simple_util_priv *priv,
+ enum graph_type gtype,
+ struct device_node *port,
+ struct link_info *li, int is_cpu)
+{
+ struct device_node *ep = port_to_endpoint(port);
+ int ret = __graph_parse_node(priv, gtype, ep, li, is_cpu, 0);
+
+ of_node_put(ep);
+
+ return ret;
+}
+
+static int graph_parse_node(struct simple_util_priv *priv,
+ enum graph_type gtype,
+ struct device_node *port,
+ struct link_info *li, int is_cpu)
+{
+ if (graph_lnk_is_multi(port))
+ return graph_parse_node_multi(priv, gtype, port, li, is_cpu);
+ else
+ return graph_parse_node_single(priv, gtype, port, li, is_cpu);
+}
+
static void graph_parse_daifmt(struct device_node *node,
unsigned int *daifmt, unsigned int *bit_frame)
{
@@ -597,6 +759,7 @@ static void graph_link_init(struct simple_util_priv *priv,
struct device_node *ep;
struct device_node *ports;
unsigned int daifmt = 0, daiclk = 0;
+ bool playback_only = 0, capture_only = 0;
unsigned int bit_frame = 0;
if (graph_lnk_is_multi(port)) {
@@ -635,6 +798,11 @@ static void graph_link_init(struct simple_util_priv *priv,
if (is_cpu_node)
daiclk = snd_soc_daifmt_clock_provider_flipped(daiclk);
+ graph_util_parse_link_direction(port, &playback_only, &capture_only);
+
+ dai_link->playback_only = playback_only;
+ dai_link->capture_only = capture_only;
+
dai_link->dai_fmt = daifmt | daiclk;
dai_link->init = simple_util_dai_init;
dai_link->ops = &graph_ops;
@@ -920,17 +1088,33 @@ static int graph_counter(struct device_node *lnk)
*
* multi {
* ports {
- * => lnk: port@0 { ... };
- * port@1 { ... };
- * port@2 { ... };
+ * => lnk: port@0 { ... }; // to pair
+ * port@1 { ... }; // Multi Element
+ * port@2 { ... }; // Multi Element
* ...
* };
* };
*
* ignore first lnk part
*/
- if (graph_lnk_is_multi(lnk))
- return of_graph_get_endpoint_count(of_get_parent(lnk)) - 1;
+ if (graph_lnk_is_multi(lnk)) {
+ struct device_node *ports = of_get_parent(lnk);
+ struct device_node *port = NULL;
+ int cnt = 0;
+
+ /*
+ * CPU/Codec = N:M case has many endpoints.
+ * We can't use of_graph_get_endpoint_count() here
+ */
+ while(1) {
+ port = of_get_next_child(ports, port);
+ if (!port)
+ break;
+ cnt++;
+ }
+
+ return cnt - 1;
+ }
/*
* Single CPU / Codec
*/
diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c
index cfa70a56ff0f..81077d16d22f 100644
--- a/sound/soc/generic/simple-card-utils.c
+++ b/sound/soc/generic/simple-card-utils.c
@@ -5,7 +5,6 @@
// Copyright (c) 2016 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
#include <linux/clk.h>
-#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/of.h>
@@ -1129,6 +1128,25 @@ parse_dai_end:
}
EXPORT_SYMBOL_GPL(graph_util_parse_dai);
+int graph_util_parse_link_direction(struct device_node *np,
+ bool *playback_only, bool *capture_only)
+{
+ bool is_playback_only = false;
+ bool is_capture_only = false;
+
+ is_playback_only = of_property_read_bool(np, "playback-only");
+ is_capture_only = of_property_read_bool(np, "capture-only");
+
+ if (is_playback_only && is_capture_only)
+ return -EINVAL;
+
+ *playback_only = is_playback_only;
+ *capture_only = is_capture_only;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(graph_util_parse_link_direction);
+
/* Module information */
MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
MODULE_DESCRIPTION("ALSA SoC Simple Card Utils");
diff --git a/sound/soc/hisilicon/hi6210-i2s.c b/sound/soc/hisilicon/hi6210-i2s.c
index dd7d2a077248..250ae3781d14 100644
--- a/sound/soc/hisilicon/hi6210-i2s.c
+++ b/sound/soc/hisilicon/hi6210-i2s.c
@@ -15,7 +15,6 @@
#include <linux/clk.h>
#include <linux/jiffies.h>
#include <linux/io.h>
-#include <linux/gpio.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
diff --git a/sound/soc/intel/avs/boards/da7219.c b/sound/soc/intel/avs/boards/da7219.c
index 6060894954df..c018f84fe025 100644
--- a/sound/soc/intel/avs/boards/da7219.c
+++ b/sound/soc/intel/avs/boards/da7219.c
@@ -277,16 +277,24 @@ static int avs_da7219_probe(struct platform_device *pdev)
return devm_snd_soc_register_card(dev, card);
}
+static const struct platform_device_id avs_da7219_driver_ids[] = {
+ {
+ .name = "avs_da7219",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_da7219_driver_ids);
+
static struct platform_driver avs_da7219_driver = {
.probe = avs_da7219_probe,
.driver = {
.name = "avs_da7219",
.pm = &snd_soc_pm_ops,
},
+ .id_table = avs_da7219_driver_ids,
};
module_platform_driver(avs_da7219_driver);
MODULE_AUTHOR("Cezary Rojewski <cezary.rojewski@intel.com>");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:avs_da7219");
diff --git a/sound/soc/intel/avs/boards/dmic.c b/sound/soc/intel/avs/boards/dmic.c
index c270646faf86..ba2bc7f689eb 100644
--- a/sound/soc/intel/avs/boards/dmic.c
+++ b/sound/soc/intel/avs/boards/dmic.c
@@ -77,15 +77,23 @@ static int avs_dmic_probe(struct platform_device *pdev)
return devm_snd_soc_register_card(dev, card);
}
+static const struct platform_device_id avs_dmic_driver_ids[] = {
+ {
+ .name = "avs_dmic",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_dmic_driver_ids);
+
static struct platform_driver avs_dmic_driver = {
.probe = avs_dmic_probe,
.driver = {
.name = "avs_dmic",
.pm = &snd_soc_pm_ops,
},
+ .id_table = avs_dmic_driver_ids,
};
module_platform_driver(avs_dmic_driver);
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:avs_dmic");
diff --git a/sound/soc/intel/avs/boards/es8336.c b/sound/soc/intel/avs/boards/es8336.c
index f972ef64d284..1090082e7d5b 100644
--- a/sound/soc/intel/avs/boards/es8336.c
+++ b/sound/soc/intel/avs/boards/es8336.c
@@ -307,15 +307,23 @@ static int avs_es8336_probe(struct platform_device *pdev)
return devm_snd_soc_register_card(dev, card);
}
+static const struct platform_device_id avs_es8336_driver_ids[] = {
+ {
+ .name = "avs_es8336",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_es8336_driver_ids);
+
static struct platform_driver avs_es8336_driver = {
.probe = avs_es8336_probe,
.driver = {
.name = "avs_es8336",
.pm = &snd_soc_pm_ops,
},
+ .id_table = avs_es8336_driver_ids,
};
module_platform_driver(avs_es8336_driver);
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:avs_es8336");
diff --git a/sound/soc/intel/avs/boards/hdaudio.c b/sound/soc/intel/avs/boards/hdaudio.c
index 8876558f19a1..79b4aca41333 100644
--- a/sound/soc/intel/avs/boards/hdaudio.c
+++ b/sound/soc/intel/avs/boards/hdaudio.c
@@ -155,8 +155,6 @@ static int avs_probing_link_init(struct snd_soc_pcm_runtime *rtm)
return 0;
}
-SND_SOC_DAILINK_DEF(dummy, DAILINK_COMP_ARRAY(COMP_DUMMY()));
-
static struct snd_soc_dai_link probing_link = {
.name = "probing-LINK",
.id = -1,
@@ -164,8 +162,8 @@ static struct snd_soc_dai_link probing_link = {
.no_pcm = 1,
.dpcm_playback = 1,
.dpcm_capture = 1,
- .cpus = dummy,
- .num_cpus = ARRAY_SIZE(dummy),
+ .cpus = &snd_soc_dummy_dlc,
+ .num_cpus = 1,
.init = avs_probing_link_init,
};
@@ -218,12 +216,21 @@ static int avs_hdaudio_probe(struct platform_device *pdev)
return devm_snd_soc_register_card(dev, card);
}
+static const struct platform_device_id avs_hdaudio_driver_ids[] = {
+ {
+ .name = "avs_hdaudio",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_hdaudio_driver_ids);
+
static struct platform_driver avs_hdaudio_driver = {
.probe = avs_hdaudio_probe,
.driver = {
.name = "avs_hdaudio",
.pm = &snd_soc_pm_ops,
},
+ .id_table = avs_hdaudio_driver_ids,
};
module_platform_driver(avs_hdaudio_driver)
@@ -231,4 +238,3 @@ module_platform_driver(avs_hdaudio_driver)
MODULE_DESCRIPTION("Intel HD-Audio machine driver");
MODULE_AUTHOR("Cezary Rojewski <cezary.rojewski@intel.com>");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:avs_hdaudio");
diff --git a/sound/soc/intel/avs/boards/i2s_test.c b/sound/soc/intel/avs/boards/i2s_test.c
index 3d03e1eed3a9..28f254eb0d03 100644
--- a/sound/soc/intel/avs/boards/i2s_test.c
+++ b/sound/soc/intel/avs/boards/i2s_test.c
@@ -185,15 +185,23 @@ static int avs_i2s_test_probe(struct platform_device *pdev)
return devm_snd_soc_register_card(dev, card);
}
+static const struct platform_device_id avs_i2s_test_driver_ids[] = {
+ {
+ .name = "avs_i2s_test",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_i2s_test_driver_ids);
+
static struct platform_driver avs_i2s_test_driver = {
.probe = avs_i2s_test_probe,
.driver = {
.name = "avs_i2s_test",
.pm = &snd_soc_pm_ops,
},
+ .id_table = avs_i2s_test_driver_ids,
};
module_platform_driver(avs_i2s_test_driver);
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:avs_i2s_test");
diff --git a/sound/soc/intel/avs/boards/max98357a.c b/sound/soc/intel/avs/boards/max98357a.c
index 6ba7b6564279..a83b95f25129 100644
--- a/sound/soc/intel/avs/boards/max98357a.c
+++ b/sound/soc/intel/avs/boards/max98357a.c
@@ -135,15 +135,23 @@ static int avs_max98357a_probe(struct platform_device *pdev)
return devm_snd_soc_register_card(dev, card);
}
+static const struct platform_device_id avs_max98357a_driver_ids[] = {
+ {
+ .name = "avs_max98357a",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_max98357a_driver_ids);
+
static struct platform_driver avs_max98357a_driver = {
.probe = avs_max98357a_probe,
.driver = {
.name = "avs_max98357a",
.pm = &snd_soc_pm_ops,
},
+ .id_table = avs_max98357a_driver_ids,
};
module_platform_driver(avs_max98357a_driver)
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:avs_max98357a");
diff --git a/sound/soc/intel/avs/boards/max98373.c b/sound/soc/intel/avs/boards/max98373.c
index cc7dfdf72083..3b980a025e6f 100644
--- a/sound/soc/intel/avs/boards/max98373.c
+++ b/sound/soc/intel/avs/boards/max98373.c
@@ -192,15 +192,23 @@ static int avs_max98373_probe(struct platform_device *pdev)
return devm_snd_soc_register_card(dev, card);
}
+static const struct platform_device_id avs_max98373_driver_ids[] = {
+ {
+ .name = "avs_max98373",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_max98373_driver_ids);
+
static struct platform_driver avs_max98373_driver = {
.probe = avs_max98373_probe,
.driver = {
.name = "avs_max98373",
.pm = &snd_soc_pm_ops,
},
+ .id_table = avs_max98373_driver_ids,
};
module_platform_driver(avs_max98373_driver)
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:avs_max98373");
diff --git a/sound/soc/intel/avs/boards/max98927.c b/sound/soc/intel/avs/boards/max98927.c
index fb0175f37d61..86dd2b228df3 100644
--- a/sound/soc/intel/avs/boards/max98927.c
+++ b/sound/soc/intel/avs/boards/max98927.c
@@ -189,15 +189,23 @@ static int avs_max98927_probe(struct platform_device *pdev)
return devm_snd_soc_register_card(dev, card);
}
+static const struct platform_device_id avs_max98927_driver_ids[] = {
+ {
+ .name = "avs_max98927",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_max98927_driver_ids);
+
static struct platform_driver avs_max98927_driver = {
.probe = avs_max98927_probe,
.driver = {
.name = "avs_max98927",
.pm = &snd_soc_pm_ops,
},
+ .id_table = avs_max98927_driver_ids,
};
module_platform_driver(avs_max98927_driver)
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:avs_max98927");
diff --git a/sound/soc/intel/avs/boards/nau8825.c b/sound/soc/intel/avs/boards/nau8825.c
index d98b5deb78c9..1c1e2083f474 100644
--- a/sound/soc/intel/avs/boards/nau8825.c
+++ b/sound/soc/intel/avs/boards/nau8825.c
@@ -294,15 +294,23 @@ static int avs_nau8825_probe(struct platform_device *pdev)
return devm_snd_soc_register_card(dev, card);
}
+static const struct platform_device_id avs_nau8825_driver_ids[] = {
+ {
+ .name = "avs_nau8825",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_nau8825_driver_ids);
+
static struct platform_driver avs_nau8825_driver = {
.probe = avs_nau8825_probe,
.driver = {
.name = "avs_nau8825",
.pm = &snd_soc_pm_ops,
},
+ .id_table = avs_nau8825_driver_ids,
};
module_platform_driver(avs_nau8825_driver)
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:avs_nau8825");
diff --git a/sound/soc/intel/avs/boards/probe.c b/sound/soc/intel/avs/boards/probe.c
index 411acaee74f9..a9469b5ecb40 100644
--- a/sound/soc/intel/avs/boards/probe.c
+++ b/sound/soc/intel/avs/boards/probe.c
@@ -50,15 +50,23 @@ static int avs_probe_mb_probe(struct platform_device *pdev)
return devm_snd_soc_register_card(dev, card);
}
+static const struct platform_device_id avs_probe_mb_driver_ids[] = {
+ {
+ .name = "avs_probe_mb",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_probe_mb_driver_ids);
+
static struct platform_driver avs_probe_mb_driver = {
.probe = avs_probe_mb_probe,
.driver = {
.name = "avs_probe_mb",
.pm = &snd_soc_pm_ops,
},
+ .id_table = avs_probe_mb_driver_ids,
};
module_platform_driver(avs_probe_mb_driver);
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:avs_probe_mb");
diff --git a/sound/soc/intel/avs/boards/rt274.c b/sound/soc/intel/avs/boards/rt274.c
index 157183b1de24..bfcb8845fd15 100644
--- a/sound/soc/intel/avs/boards/rt274.c
+++ b/sound/soc/intel/avs/boards/rt274.c
@@ -257,15 +257,23 @@ static int avs_rt274_probe(struct platform_device *pdev)
return devm_snd_soc_register_card(dev, card);
}
+static const struct platform_device_id avs_rt274_driver_ids[] = {
+ {
+ .name = "avs_rt274",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_rt274_driver_ids);
+
static struct platform_driver avs_rt274_driver = {
.probe = avs_rt274_probe,
.driver = {
.name = "avs_rt274",
.pm = &snd_soc_pm_ops,
},
+ .id_table = avs_rt274_driver_ids,
};
module_platform_driver(avs_rt274_driver);
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:avs_rt274");
diff --git a/sound/soc/intel/avs/boards/rt286.c b/sound/soc/intel/avs/boards/rt286.c
index 131237471e3e..28d7d86b1cc9 100644
--- a/sound/soc/intel/avs/boards/rt286.c
+++ b/sound/soc/intel/avs/boards/rt286.c
@@ -228,15 +228,23 @@ static int avs_rt286_probe(struct platform_device *pdev)
return devm_snd_soc_register_card(dev, card);
}
+static const struct platform_device_id avs_rt286_driver_ids[] = {
+ {
+ .name = "avs_rt286",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_rt286_driver_ids);
+
static struct platform_driver avs_rt286_driver = {
.probe = avs_rt286_probe,
.driver = {
.name = "avs_rt286",
.pm = &snd_soc_pm_ops,
},
+ .id_table = avs_rt286_driver_ids,
};
module_platform_driver(avs_rt286_driver);
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:avs_rt286");
diff --git a/sound/soc/intel/avs/boards/rt298.c b/sound/soc/intel/avs/boards/rt298.c
index ea32a7690c8a..80f490b9e118 100644
--- a/sound/soc/intel/avs/boards/rt298.c
+++ b/sound/soc/intel/avs/boards/rt298.c
@@ -247,15 +247,23 @@ static int avs_rt298_probe(struct platform_device *pdev)
return devm_snd_soc_register_card(dev, card);
}
+static const struct platform_device_id avs_rt298_driver_ids[] = {
+ {
+ .name = "avs_rt298",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_rt298_driver_ids);
+
static struct platform_driver avs_rt298_driver = {
.probe = avs_rt298_probe,
.driver = {
.name = "avs_rt298",
.pm = &snd_soc_pm_ops,
},
+ .id_table = avs_rt298_driver_ids,
};
module_platform_driver(avs_rt298_driver);
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:avs_rt298");
diff --git a/sound/soc/intel/avs/boards/rt5514.c b/sound/soc/intel/avs/boards/rt5514.c
index ad486a52e5e3..60105f453ae2 100644
--- a/sound/soc/intel/avs/boards/rt5514.c
+++ b/sound/soc/intel/avs/boards/rt5514.c
@@ -173,15 +173,23 @@ static int avs_rt5514_probe(struct platform_device *pdev)
return devm_snd_soc_register_card(dev, card);
}
+static const struct platform_device_id avs_rt5514_driver_ids[] = {
+ {
+ .name = "avs_rt5514",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_rt5514_driver_ids);
+
static struct platform_driver avs_rt5514_driver = {
.probe = avs_rt5514_probe,
.driver = {
.name = "avs_rt5514",
.pm = &snd_soc_pm_ops,
},
+ .id_table = avs_rt5514_driver_ids,
};
module_platform_driver(avs_rt5514_driver);
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:avs_rt5514");
diff --git a/sound/soc/intel/avs/boards/rt5663.c b/sound/soc/intel/avs/boards/rt5663.c
index 3effd789a45e..b4762c2a7bf2 100644
--- a/sound/soc/intel/avs/boards/rt5663.c
+++ b/sound/soc/intel/avs/boards/rt5663.c
@@ -246,15 +246,23 @@ static int avs_rt5663_probe(struct platform_device *pdev)
return devm_snd_soc_register_card(dev, card);
}
+static const struct platform_device_id avs_rt5663_driver_ids[] = {
+ {
+ .name = "avs_rt5663",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_rt5663_driver_ids);
+
static struct platform_driver avs_rt5663_driver = {
.probe = avs_rt5663_probe,
.driver = {
.name = "avs_rt5663",
.pm = &snd_soc_pm_ops,
},
+ .id_table = avs_rt5663_driver_ids,
};
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 84e850c0b085..243f979fda98 100644
--- a/sound/soc/intel/avs/boards/rt5682.c
+++ b/sound/soc/intel/avs/boards/rt5682.c
@@ -322,16 +322,24 @@ static int avs_rt5682_probe(struct platform_device *pdev)
return devm_snd_soc_register_card(dev, card);
}
+static const struct platform_device_id avs_rt5682_driver_ids[] = {
+ {
+ .name = "avs_rt5682",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_rt5682_driver_ids);
+
static struct platform_driver avs_rt5682_driver = {
.probe = avs_rt5682_probe,
.driver = {
.name = "avs_rt5682",
.pm = &snd_soc_pm_ops,
},
+ .id_table = avs_rt5682_driver_ids,
};
module_platform_driver(avs_rt5682_driver)
MODULE_AUTHOR("Cezary Rojewski <cezary.rojewski@intel.com>");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:avs_rt5682");
diff --git a/sound/soc/intel/avs/boards/ssm4567.c b/sound/soc/intel/avs/boards/ssm4567.c
index 6bcab9deae5c..4a0e136835ff 100644
--- a/sound/soc/intel/avs/boards/ssm4567.c
+++ b/sound/soc/intel/avs/boards/ssm4567.c
@@ -181,15 +181,23 @@ static int avs_ssm4567_probe(struct platform_device *pdev)
return devm_snd_soc_register_card(dev, card);
}
+static const struct platform_device_id avs_ssm4567_driver_ids[] = {
+ {
+ .name = "avs_ssm4567",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_ssm4567_driver_ids);
+
static struct platform_driver avs_ssm4567_driver = {
.probe = avs_ssm4567_probe,
.driver = {
.name = "avs_ssm4567",
.pm = &snd_soc_pm_ops,
},
+ .id_table = avs_ssm4567_driver_ids,
};
module_platform_driver(avs_ssm4567_driver)
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:avs_ssm4567");
diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c
index 59c3793f65df..db78eb2f0108 100644
--- a/sound/soc/intel/avs/core.c
+++ b/sound/soc/intel/avs/core.c
@@ -477,6 +477,9 @@ static int avs_pci_probe(struct pci_dev *pci, const struct pci_device_id *id)
return 0;
err_i915_init:
+ pci_free_irq(pci, 0, adev);
+ pci_free_irq(pci, 0, bus);
+ pci_free_irq_vectors(pci);
pci_clear_master(pci);
pci_set_drvdata(pci, NULL);
err_acquire_irq:
diff --git a/sound/soc/intel/avs/loader.c b/sound/soc/intel/avs/loader.c
index 65dd8f140fc1..e83ce6a35755 100644
--- a/sound/soc/intel/avs/loader.c
+++ b/sound/soc/intel/avs/loader.c
@@ -371,7 +371,7 @@ int avs_hda_load_basefw(struct avs_dev *adev, struct firmware *fw)
hstream = hdac_stream(estream);
/* code loading performed with default format */
- sdfmt = snd_hdac_calc_stream_format(48000, 1, SNDRV_PCM_FORMAT_S32_LE, 32, 0);
+ sdfmt = snd_hdac_stream_format(1, 32, 48000);
ret = snd_hdac_dsp_prepare(hstream, sdfmt, fw->size, &dmab);
if (ret < 0)
goto release_stream;
@@ -438,7 +438,7 @@ int avs_hda_load_library(struct avs_dev *adev, struct firmware *lib, u32 id)
stream = hdac_stream(estream);
/* code loading performed with default format */
- sdfmt = snd_hdac_calc_stream_format(48000, 1, SNDRV_PCM_FORMAT_S32_LE, 32, 0);
+ sdfmt = snd_hdac_stream_format(1, 32, 48000);
ret = snd_hdac_dsp_prepare(stream, sdfmt, lib->size, &dmab);
if (ret < 0)
goto release_stream;
diff --git a/sound/soc/intel/avs/path.c b/sound/soc/intel/avs/path.c
index aa8b50b931c3..3aa16ee8d34c 100644
--- a/sound/soc/intel/avs/path.c
+++ b/sound/soc/intel/avs/path.c
@@ -87,7 +87,7 @@ static bool avs_test_hw_params(struct snd_pcm_hw_params *params,
return (params_rate(params) == fmt->sampling_freq &&
params_channels(params) == fmt->num_channels &&
params_physical_width(params) == fmt->bit_depth &&
- params_width(params) == fmt->valid_bit_depth);
+ snd_pcm_hw_params_bits(params) == fmt->valid_bit_depth);
}
static struct avs_tplg_path *
diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c
index 463dbba18426..4dfc5a1ebb7c 100644
--- a/sound/soc/intel/avs/pcm.c
+++ b/sound/soc/intel/avs/pcm.c
@@ -17,6 +17,7 @@
#include "avs.h"
#include "path.h"
#include "topology.h"
+#include "../../codecs/hda.h"
struct avs_dma_data {
struct avs_tplg_path_template *template;
@@ -335,20 +336,25 @@ static int avs_dai_hda_be_prepare(struct snd_pcm_substream *substream, struct sn
{
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
- struct hdac_ext_stream *link_stream = runtime->private_data;
+ struct snd_soc_pcm_stream *stream_info;
+ struct hdac_ext_stream *link_stream;
struct hdac_ext_link *link;
struct hda_codec *codec;
struct hdac_bus *bus;
unsigned int format_val;
+ unsigned int bits;
int ret;
+ link_stream = runtime->private_data;
if (link_stream->link_prepared)
return 0;
codec = dev_to_hda_codec(snd_soc_rtd_to_codec(rtd, 0)->dev);
bus = &codec->bus->core;
- format_val = snd_hdac_calc_stream_format(runtime->rate, runtime->channels, runtime->format,
- runtime->sample_bits, 0);
+ stream_info = snd_soc_dai_get_pcm_stream(dai, substream->stream);
+ bits = snd_hdac_stream_format_bits(runtime->format, runtime->subformat,
+ stream_info->sig_bits);
+ format_val = snd_hdac_stream_format(runtime->channels, bits, runtime->rate);
snd_hdac_ext_stream_reset(link_stream);
snd_hdac_ext_stream_setup(link_stream, format_val);
@@ -600,10 +606,12 @@ static int avs_dai_fe_hw_free(struct snd_pcm_substream *substream, struct snd_so
static int avs_dai_fe_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{
struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_stream *stream_info;
struct avs_dma_data *data;
struct avs_dev *adev = to_avs_dev(dai->dev);
struct hdac_ext_stream *host_stream;
unsigned int format_val;
+ unsigned int bits;
int ret;
data = snd_soc_dai_get_dma_data(dai, substream);
@@ -614,8 +622,10 @@ static int avs_dai_fe_prepare(struct snd_pcm_substream *substream, struct snd_so
snd_hdac_stream_reset(hdac_stream(host_stream));
- format_val = snd_hdac_calc_stream_format(runtime->rate, runtime->channels, runtime->format,
- runtime->sample_bits, 0);
+ stream_info = snd_soc_dai_get_pcm_stream(dai, substream->stream);
+ bits = snd_hdac_stream_format_bits(runtime->format, runtime->subformat,
+ stream_info->sig_bits);
+ format_val = snd_hdac_stream_format(runtime->channels, bits, runtime->rate);
ret = snd_hdac_stream_set_params(hdac_stream(host_stream), format_val);
if (ret < 0)
@@ -1064,8 +1074,10 @@ static const struct snd_pcm_hardware avs_pcm_hardware = {
SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE,
+ .subformats = SNDRV_PCM_SUBFMTBIT_MSBITS_20 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_24 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_MAX,
.buffer_bytes_max = AZX_MAX_BUF_SIZE,
.period_bytes_min = 128,
.period_bytes_max = AZX_MAX_BUF_SIZE / 2,
@@ -1216,8 +1228,10 @@ static const struct snd_soc_dai_driver i2s_dai_template = {
.rates = SNDRV_PCM_RATE_8000_192000 |
SNDRV_PCM_RATE_KNOT,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE,
+ .subformats = SNDRV_PCM_SUBFMTBIT_MSBITS_20 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_24 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_MAX,
},
.capture = {
.channels_min = 1,
@@ -1225,8 +1239,10 @@ static const struct snd_soc_dai_driver i2s_dai_template = {
.rates = SNDRV_PCM_RATE_8000_192000 |
SNDRV_PCM_RATE_KNOT,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE,
+ .subformats = SNDRV_PCM_SUBFMTBIT_MSBITS_20 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_24 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_MAX,
},
};
@@ -1301,16 +1317,20 @@ static const struct snd_soc_dai_driver hda_cpu_dai = {
.channels_max = 8,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE,
+ .subformats = SNDRV_PCM_SUBFMTBIT_MSBITS_20 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_24 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_MAX,
},
.capture = {
.channels_min = 1,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE,
+ .subformats = SNDRV_PCM_SUBFMTBIT_MSBITS_20 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_24 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_MAX,
},
};
@@ -1387,6 +1407,15 @@ static int avs_component_hda_probe(struct snd_soc_component *component)
ret = -ENOMEM;
goto exit;
}
+
+ if (!hda_codec_is_display(codec)) {
+ dais[i].playback.formats = pcm->stream[0].formats;
+ dais[i].playback.subformats = pcm->stream[0].subformats;
+ dais[i].playback.rates = pcm->stream[0].rates;
+ dais[i].playback.channels_min = pcm->stream[0].channels_min;
+ dais[i].playback.channels_max = pcm->stream[0].channels_max;
+ dais[i].playback.sig_bits = pcm->stream[0].maxbps;
+ }
}
if (pcm->stream[1].substreams) {
@@ -1397,6 +1426,15 @@ static int avs_component_hda_probe(struct snd_soc_component *component)
ret = -ENOMEM;
goto exit;
}
+
+ if (!hda_codec_is_display(codec)) {
+ dais[i].capture.formats = pcm->stream[1].formats;
+ dais[i].capture.subformats = pcm->stream[1].subformats;
+ dais[i].capture.rates = pcm->stream[1].rates;
+ dais[i].capture.channels_min = pcm->stream[1].channels_min;
+ dais[i].capture.channels_max = pcm->stream[1].channels_max;
+ dais[i].capture.sig_bits = pcm->stream[1].maxbps;
+ }
}
dai = snd_soc_register_dai(component, &dais[i], false);
diff --git a/sound/soc/intel/avs/probes.c b/sound/soc/intel/avs/probes.c
index bdc6b30dc009..817e543036f2 100644
--- a/sound/soc/intel/avs/probes.c
+++ b/sound/soc/intel/avs/probes.c
@@ -140,8 +140,7 @@ static int avs_probe_compr_set_params(struct snd_compr_stream *cstream,
bps = snd_pcm_format_physical_width(format);
if (bps < 0)
return bps;
- format_val = snd_hdac_calc_stream_format(params->codec.sample_rate, params->codec.ch_out,
- format, bps, 0);
+ format_val = snd_hdac_stream_format(params->codec.ch_out, bps, params->codec.sample_rate);
ret = snd_hdac_stream_set_params(hdac_stream(host_stream), format_val);
if (ret < 0)
return ret;
diff --git a/sound/soc/intel/avs/topology.c b/sound/soc/intel/avs/topology.c
index c74e9d622e4c..48b3c67c9103 100644
--- a/sound/soc/intel/avs/topology.c
+++ b/sound/soc/intel/avs/topology.c
@@ -857,7 +857,7 @@ assign_copier_gtw_instance(struct snd_soc_component *comp, struct avs_tplg_modcf
}
/* If topology sets value don't overwrite it */
- if (cfg->copier.vindex.i2s.instance)
+ if (cfg->copier.vindex.val)
return;
mach = dev_get_platdata(comp->card->dev);
@@ -1514,8 +1514,16 @@ static int avs_dai_load(struct snd_soc_component *comp, int index,
struct snd_soc_dai_driver *dai_drv, struct snd_soc_tplg_pcm *pcm,
struct snd_soc_dai *dai)
{
- if (pcm)
+ u32 fe_subformats = SNDRV_PCM_SUBFMTBIT_MSBITS_20 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_24 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_MAX;
+
+ if (pcm) {
dai_drv->ops = &avs_dai_fe_ops;
+ dai_drv->capture.subformats = fe_subformats;
+ dai_drv->playback.subformats = fe_subformats;
+ }
+
return 0;
}
@@ -1534,6 +1542,9 @@ static int avs_link_load(struct snd_soc_component *comp, int index, struct snd_s
/* Open LINK (BE) pipes last and close them first to prevent xruns. */
link->trigger[0] = SND_SOC_DPCM_TRIGGER_PRE;
link->trigger[1] = SND_SOC_DPCM_TRIGGER_PRE;
+ } else {
+ /* Do not ignore codec capabilities. */
+ link->dpcm_merged_format = 1;
}
return 0;
diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig
index 9e427f00deac..8fd5e7f83054 100644
--- a/sound/soc/intel/boards/Kconfig
+++ b/sound/soc/intel/boards/Kconfig
@@ -229,6 +229,7 @@ config SND_SOC_INTEL_BYT_CHT_ES8316_MACH
depends on GPIOLIB || COMPILE_TEST
select SND_SOC_ACPI
select SND_SOC_ES8316
+ select SND_SOC_ES83XX_DSM_COMMON
help
This adds support for ASoC machine driver for Intel(R) Baytrail &
Cherrytrail platforms with ES8316 audio codec.
@@ -686,6 +687,7 @@ config SND_SOC_INTEL_SOUNDWIRE_SOF_MACH
select SND_SOC_RT712_SDCA_DMIC_SDW
select SND_SOC_RT715_SDW
select SND_SOC_RT715_SDCA_SDW
+ select SND_SOC_RT722_SDCA_SDW
select SND_SOC_RT1308_SDW
select SND_SOC_RT1308
select SND_SOC_RT1316_SDW
diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile
index 943bf8b80e01..bbf796a5f7ba 100644
--- a/sound/soc/intel/boards/Makefile
+++ b/sound/soc/intel/boards/Makefile
@@ -41,9 +41,10 @@ snd-soc-sof-sdw-objs += sof_sdw.o \
sof_sdw_rt5682.o sof_sdw_rt700.o \
sof_sdw_rt711.o sof_sdw_rt_sdca_jack_common.o \
sof_sdw_rt712_sdca.o sof_sdw_rt715.o \
- sof_sdw_rt715_sdca.o sof_sdw_dmic.o \
+ sof_sdw_rt715_sdca.o sof_sdw_rt722_sdca.o \
sof_sdw_cs42l42.o sof_sdw_cs42l43.o \
sof_sdw_cs_amp.o \
+ sof_sdw_dmic.o \
sof_sdw_hdmi.o
obj-$(CONFIG_SND_SOC_INTEL_SOF_RT5682_MACH) += snd-soc-sof_rt5682.o
obj-$(CONFIG_SND_SOC_INTEL_SOF_CS42L42_MACH) += snd-soc-sof_cs42l42.o
diff --git a/sound/soc/intel/boards/bxt_da7219_max98357a.c b/sound/soc/intel/boards/bxt_da7219_max98357a.c
index 816fad8c1ff0..540f7a29310a 100644
--- a/sound/soc/intel/boards/bxt_da7219_max98357a.c
+++ b/sound/soc/intel/boards/bxt_da7219_max98357a.c
@@ -797,6 +797,9 @@ static int broxton_audio_probe(struct platform_device *pdev)
broxton_audio_card.name = "glkda7219max";
/* Fixup the SSP entries for geminilake */
for (i = 0; i < ARRAY_SIZE(broxton_dais); i++) {
+ if (!broxton_dais[i].codecs->dai_name)
+ continue;
+
/* MAXIM_CODEC is connected to SSP1. */
if (!strcmp(broxton_dais[i].codecs->dai_name,
BXT_MAXIM_CODEC_DAI)) {
@@ -822,6 +825,9 @@ static int broxton_audio_probe(struct platform_device *pdev)
broxton_audio_card.name = "cmlda7219max";
for (i = 0; i < ARRAY_SIZE(broxton_dais); i++) {
+ if (!broxton_dais[i].codecs->dai_name)
+ continue;
+
/* MAXIM_CODEC is connected to SSP1. */
if (!strcmp(broxton_dais[i].codecs->dai_name,
BXT_MAXIM_CODEC_DAI)) {
diff --git a/sound/soc/intel/boards/bxt_rt298.c b/sound/soc/intel/boards/bxt_rt298.c
index 4631106f2a28..c0eb65c14aa9 100644
--- a/sound/soc/intel/boards/bxt_rt298.c
+++ b/sound/soc/intel/boards/bxt_rt298.c
@@ -604,7 +604,8 @@ static int broxton_audio_probe(struct platform_device *pdev)
int i;
for (i = 0; i < ARRAY_SIZE(broxton_rt298_dais); i++) {
- if (!strncmp(card->dai_link[i].codecs->name, "i2c-INT343A:00",
+ if (card->dai_link[i].codecs->name &&
+ !strncmp(card->dai_link[i].codecs->name, "i2c-INT343A:00",
I2C_NAME_SIZE)) {
if (!strncmp(card->name, "broxton-rt298",
PLATFORM_NAME_SIZE)) {
diff --git a/sound/soc/intel/boards/bytcht_cx2072x.c b/sound/soc/intel/boards/bytcht_cx2072x.c
index 10a84a2c1036..c014d85a08b2 100644
--- a/sound/soc/intel/boards/bytcht_cx2072x.c
+++ b/sound/soc/intel/boards/bytcht_cx2072x.c
@@ -241,7 +241,8 @@ static int snd_byt_cht_cx2072x_probe(struct platform_device *pdev)
/* fix index of codec dai */
for (i = 0; i < ARRAY_SIZE(byt_cht_cx2072x_dais); i++) {
- if (!strcmp(byt_cht_cx2072x_dais[i].codecs->name,
+ if (byt_cht_cx2072x_dais[i].codecs->name &&
+ !strcmp(byt_cht_cx2072x_dais[i].codecs->name,
"i2c-14F10720:00")) {
dai_index = i;
break;
diff --git a/sound/soc/intel/boards/bytcht_da7213.c b/sound/soc/intel/boards/bytcht_da7213.c
index 7e5eea690023..f4ac3ddd148b 100644
--- a/sound/soc/intel/boards/bytcht_da7213.c
+++ b/sound/soc/intel/boards/bytcht_da7213.c
@@ -245,7 +245,8 @@ static int bytcht_da7213_probe(struct platform_device *pdev)
/* fix index of codec dai */
for (i = 0; i < ARRAY_SIZE(dailink); i++) {
- if (!strcmp(dailink[i].codecs->name, "i2c-DLGS7213:00")) {
+ if (dailink[i].codecs->name &&
+ !strcmp(dailink[i].codecs->name, "i2c-DLGS7213:00")) {
dai_index = i;
break;
}
diff --git a/sound/soc/intel/boards/bytcht_es8316.c b/sound/soc/intel/boards/bytcht_es8316.c
index 8a0b0e864fbb..2fcec2e02bb5 100644
--- a/sound/soc/intel/boards/bytcht_es8316.c
+++ b/sound/soc/intel/boards/bytcht_es8316.c
@@ -27,6 +27,7 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-acpi.h>
+#include "../../codecs/es83xx-dsm-common.h"
#include "../atom/sst-atom-controls.h"
#include "../common/soc-intel-quirks.h"
@@ -461,6 +462,66 @@ static const struct dmi_system_id byt_cht_es8316_quirk_table[] = {
{}
};
+static int byt_cht_es8316_get_quirks_from_dsm(struct byt_cht_es8316_private *priv,
+ bool is_bytcr)
+{
+ int ret, val1, val2, dsm_quirk = 0;
+
+ if (is_bytcr)
+ dsm_quirk |= BYT_CHT_ES8316_SSP0;
+
+ ret = es83xx_dsm(priv->codec_dev, PLATFORM_MAINMIC_TYPE_ARG, &val1);
+ if (ret < 0)
+ return ret;
+
+ ret = es83xx_dsm(priv->codec_dev, PLATFORM_HPMIC_TYPE_ARG, &val2);
+ if (ret < 0)
+ return ret;
+
+ if (val1 == PLATFORM_MIC_AMIC_LIN1RIN1 && val2 == PLATFORM_MIC_AMIC_LIN2RIN2) {
+ dsm_quirk |= BYT_CHT_ES8316_INTMIC_IN1_MAP;
+ } else if (val1 == PLATFORM_MIC_AMIC_LIN2RIN2 && val2 == PLATFORM_MIC_AMIC_LIN1RIN1) {
+ dsm_quirk |= BYT_CHT_ES8316_INTMIC_IN2_MAP;
+ } else {
+ dev_warn(priv->codec_dev, "Unknown mic settings mainmic 0x%02x hpmic 0x%02x\n",
+ val1, val2);
+ return -EINVAL;
+ }
+
+ ret = es83xx_dsm(priv->codec_dev, PLATFORM_SPK_TYPE_ARG, &val1);
+ if (ret < 0)
+ return ret;
+
+ switch (val1) {
+ case PLATFORM_SPK_MONO:
+ dsm_quirk |= BYT_CHT_ES8316_MONO_SPEAKER;
+ break;
+ case PLATFORM_SPK_STEREO:
+ break;
+ default:
+ dev_warn(priv->codec_dev, "Unknown speaker setting 0x%02x\n", val1);
+ return -EINVAL;
+ }
+
+ ret = es83xx_dsm(priv->codec_dev, PLATFORM_HPDET_INV_ARG, &val1);
+ if (ret < 0)
+ return ret;
+
+ switch (val1) {
+ case PLATFORM_HPDET_NORMAL:
+ break;
+ case PLATFORM_HPDET_INVERTED:
+ dsm_quirk |= BYT_CHT_ES8316_JD_INVERTED;
+ break;
+ default:
+ dev_warn(priv->codec_dev, "Unknown hpdet-inv setting 0x%02x\n", val1);
+ return -EINVAL;
+ }
+
+ quirk = dsm_quirk;
+ return 0;
+}
+
static int snd_byt_cht_es8316_mc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -470,10 +531,10 @@ static int snd_byt_cht_es8316_mc_probe(struct platform_device *pdev)
struct byt_cht_es8316_private *priv;
const struct dmi_system_id *dmi_id;
struct fwnode_handle *fwnode;
+ bool sof_parent, is_bytcr;
const char *platform_name;
struct acpi_device *adev;
struct device *codec_dev;
- bool sof_parent;
unsigned int cnt = 0;
int dai_index = 0;
int i;
@@ -485,7 +546,8 @@ static int snd_byt_cht_es8316_mc_probe(struct platform_device *pdev)
/* fix index of codec dai */
for (i = 0; i < ARRAY_SIZE(byt_cht_es8316_dais); i++) {
- if (!strcmp(byt_cht_es8316_dais[i].codecs->name,
+ if (byt_cht_es8316_dais[i].codecs->name &&
+ !strcmp(byt_cht_es8316_dais[i].codecs->name,
"i2c-ESSX8316:00")) {
dai_index = i;
break;
@@ -520,12 +582,16 @@ static int snd_byt_cht_es8316_mc_probe(struct platform_device *pdev)
return ret;
}
+ es83xx_dsm_dump(priv->codec_dev);
+
/* Check for BYTCR or other platform and setup quirks */
+ is_bytcr = soc_intel_is_byt() && mach->mach_params.acpi_ipc_irq_index == 0;
dmi_id = dmi_first_match(byt_cht_es8316_quirk_table);
if (dmi_id) {
quirk = (unsigned long)dmi_id->driver_data;
- } else if (soc_intel_is_byt() &&
- mach->mach_params.acpi_ipc_irq_index == 0) {
+ } else if (!byt_cht_es8316_get_quirks_from_dsm(priv, is_bytcr)) {
+ dev_info(dev, "Using ACPI DSM info for quirks\n");
+ } else if (is_bytcr) {
/* On BYTCR default to SSP0, internal-mic-in2-map, mono-spk */
quirk = BYT_CHT_ES8316_SSP0 | BYT_CHT_ES8316_INTMIC_IN2_MAP |
BYT_CHT_ES8316_MONO_SPEAKER;
diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c
index 42466b4b1ca4..05f38d1f7d82 100644
--- a/sound/soc/intel/boards/bytcr_rt5640.c
+++ b/sound/soc/intel/boards/bytcr_rt5640.c
@@ -685,6 +685,18 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = {
BYT_RT5640_SSP0_AIF1 |
BYT_RT5640_MCLK_EN),
},
+ { /* Chuwi Vi8 dual-boot (CWI506) */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Insyde"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "i86"),
+ /* The above are too generic, also match BIOS info */
+ DMI_MATCH(DMI_BIOS_VERSION, "CHUWI2.D86JHBNR02"),
+ },
+ .driver_data = (void *)(BYTCR_INPUT_DEFAULTS |
+ BYT_RT5640_MONO_SPEAKER |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN),
+ },
{
/* Chuwi Vi10 (CWI505) */
.matches = {
@@ -1652,7 +1664,8 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
/* fix index of codec dai */
for (i = 0; i < ARRAY_SIZE(byt_rt5640_dais); i++) {
- if (!strcmp(byt_rt5640_dais[i].codecs->name,
+ if (byt_rt5640_dais[i].codecs->name &&
+ !strcmp(byt_rt5640_dais[i].codecs->name,
"i2c-10EC5640:00")) {
dai_index = i;
break;
diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c
index f9fe8414f454..80c841b000a3 100644
--- a/sound/soc/intel/boards/bytcr_rt5651.c
+++ b/sound/soc/intel/boards/bytcr_rt5651.c
@@ -910,7 +910,8 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev)
/* fix index of codec dai */
for (i = 0; i < ARRAY_SIZE(byt_rt5651_dais); i++) {
- if (!strcmp(byt_rt5651_dais[i].codecs->name,
+ if (byt_rt5651_dais[i].codecs->name &&
+ !strcmp(byt_rt5651_dais[i].codecs->name,
"i2c-10EC5651:00")) {
dai_index = i;
break;
diff --git a/sound/soc/intel/boards/bytcr_wm5102.c b/sound/soc/intel/boards/bytcr_wm5102.c
index 6978ebde6693..cccb5e90c0fe 100644
--- a/sound/soc/intel/boards/bytcr_wm5102.c
+++ b/sound/soc/intel/boards/bytcr_wm5102.c
@@ -605,7 +605,8 @@ static int snd_byt_wm5102_mc_probe(struct platform_device *pdev)
/* find index of codec dai */
for (i = 0; i < ARRAY_SIZE(byt_wm5102_dais); i++) {
- if (!strcmp(byt_wm5102_dais[i].codecs->name,
+ if (byt_wm5102_dais[i].codecs->name &&
+ !strcmp(byt_wm5102_dais[i].codecs->name,
"wm5102-codec")) {
dai_index = i;
break;
diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c
index df23a581c10e..eb41b7115d01 100644
--- a/sound/soc/intel/boards/cht_bsw_rt5645.c
+++ b/sound/soc/intel/boards/cht_bsw_rt5645.c
@@ -40,7 +40,6 @@ struct cht_acpi_card {
struct cht_mc_private {
struct snd_soc_jack jack;
struct cht_acpi_card *acpi_card;
- char codec_name[SND_ACPI_I2C_ID_LEN];
struct clk *mclk;
};
@@ -534,6 +533,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
const char *platform_name;
struct cht_mc_private *drv;
struct acpi_device *adev;
+ struct device *codec_dev;
bool sof_parent;
bool found = false;
bool is_bytcr = false;
@@ -566,14 +566,14 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
}
card->dev = &pdev->dev;
- sprintf(drv->codec_name, "i2c-%s:00", drv->acpi_card->codec_id);
/* set correct codec name */
for (i = 0; i < ARRAY_SIZE(cht_dailink); i++)
- if (!strcmp(card->dai_link[i].codecs->name,
+ if (cht_dailink[i].codecs->name &&
+ !strcmp(cht_dailink[i].codecs->name,
"i2c-10EC5645:00")) {
- card->dai_link[i].codecs->name = drv->codec_name;
dai_index = i;
+ break;
}
/* fixup codec name based on HID */
@@ -583,7 +583,14 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
"i2c-%s", acpi_dev_name(adev));
cht_dailink[dai_index].codecs->name = cht_rt5645_codec_name;
}
+ /* acpi_get_first_physical_node() returns a borrowed ref, no need to deref */
+ codec_dev = acpi_get_first_physical_node(adev);
acpi_dev_put(adev);
+ if (!codec_dev)
+ return -EPROBE_DEFER;
+
+ snd_soc_card_chtrt5645.components = rt5645_components(codec_dev);
+ snd_soc_card_chtrt5650.components = rt5645_components(codec_dev);
/*
* swap SSP0 if bytcr is detected
diff --git a/sound/soc/intel/boards/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c
index f6da24f3c466..be2d1a8dbca8 100644
--- a/sound/soc/intel/boards/cht_bsw_rt5672.c
+++ b/sound/soc/intel/boards/cht_bsw_rt5672.c
@@ -93,8 +93,12 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w,
* when codec is runtime suspended. Codec needs clock for jack
* detection and button press.
*/
- snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_RCCLK,
- 48000 * 512, SND_SOC_CLOCK_IN);
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_RCCLK,
+ 48000 * 512, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(card->dev, "failed to set codec sysclk: %d\n", ret);
+ return ret;
+ }
if (ctx->mclk)
clk_disable_unprepare(ctx->mclk);
@@ -462,7 +466,8 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
/* find index of codec dai */
for (i = 0; i < ARRAY_SIZE(cht_dailink); i++) {
- if (!strcmp(cht_dailink[i].codecs->name, RT5672_I2C_DEFAULT)) {
+ if (cht_dailink[i].codecs->name &&
+ !strcmp(cht_dailink[i].codecs->name, RT5672_I2C_DEFAULT)) {
dai_index = i;
break;
}
diff --git a/sound/soc/intel/boards/sof_board_helpers.c b/sound/soc/intel/boards/sof_board_helpers.c
index ce2967850c2d..4f2cb8e52971 100644
--- a/sound/soc/intel/boards/sof_board_helpers.c
+++ b/sound/soc/intel/boards/sof_board_helpers.c
@@ -3,6 +3,7 @@
// Copyright(c) 2023 Intel Corporation. All rights reserved.
#include <sound/soc.h>
+#include "../common/soc-intel-quirks.h"
#include "hda_dsp_common.h"
#include "sof_board_helpers.h"
@@ -86,6 +87,55 @@ static struct snd_soc_dai_link_component platform_component[] = {
}
};
+int sof_intel_board_set_codec_link(struct device *dev,
+ struct snd_soc_dai_link *link, int be_id,
+ enum sof_ssp_codec codec_type, int ssp_codec)
+{
+ struct snd_soc_dai_link_component *cpus;
+
+ dev_dbg(dev, "link %d: codec %s, ssp %d\n", be_id,
+ sof_ssp_get_codec_name(codec_type), ssp_codec);
+
+ /* link name */
+ link->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_codec);
+ if (!link->name)
+ return -ENOMEM;
+
+ /* cpus */
+ cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component),
+ GFP_KERNEL);
+ if (!cpus)
+ return -ENOMEM;
+
+ if (soc_intel_is_byt() || soc_intel_is_cht()) {
+ /* backward-compatibility for BYT/CHT boards */
+ cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "ssp%d-port",
+ ssp_codec);
+ } else {
+ cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin",
+ ssp_codec);
+ }
+ if (!cpus->dai_name)
+ return -ENOMEM;
+
+ link->cpus = cpus;
+ link->num_cpus = 1;
+
+ /* codecs - caller to handle */
+
+ /* platforms */
+ link->platforms = platform_component;
+ link->num_platforms = ARRAY_SIZE(platform_component);
+
+ link->id = be_id;
+ link->no_pcm = 1;
+ link->dpcm_capture = 1;
+ link->dpcm_playback = 1;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(sof_intel_board_set_codec_link, SND_SOC_INTEL_SOF_BOARD_HELPERS);
+
int sof_intel_board_set_dmic_link(struct device *dev,
struct snd_soc_dai_link *link, int be_id,
enum sof_dmic_be_type be_type)
@@ -198,7 +248,287 @@ int sof_intel_board_set_intel_hdmi_link(struct device *dev,
}
EXPORT_SYMBOL_NS(sof_intel_board_set_intel_hdmi_link, SND_SOC_INTEL_SOF_BOARD_HELPERS);
+int sof_intel_board_set_ssp_amp_link(struct device *dev,
+ struct snd_soc_dai_link *link, int be_id,
+ enum sof_ssp_codec amp_type, int ssp_amp)
+{
+ struct snd_soc_dai_link_component *cpus;
+
+ dev_dbg(dev, "link %d: ssp amp %s, ssp %d\n", be_id,
+ sof_ssp_get_codec_name(amp_type), ssp_amp);
+
+ /* link name */
+ link->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_amp);
+ if (!link->name)
+ return -ENOMEM;
+
+ /* cpus */
+ cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component),
+ GFP_KERNEL);
+ if (!cpus)
+ return -ENOMEM;
+
+ cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", ssp_amp);
+ if (!cpus->dai_name)
+ return -ENOMEM;
+
+ link->cpus = cpus;
+ link->num_cpus = 1;
+
+ /* codecs - caller to handle */
+
+ /* platforms */
+ link->platforms = platform_component;
+ link->num_platforms = ARRAY_SIZE(platform_component);
+
+ link->id = be_id;
+ link->no_pcm = 1;
+ link->dpcm_capture = 1; /* feedback stream or firmware-generated echo reference */
+ link->dpcm_playback = 1;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(sof_intel_board_set_ssp_amp_link, SND_SOC_INTEL_SOF_BOARD_HELPERS);
+
+int sof_intel_board_set_bt_link(struct device *dev,
+ struct snd_soc_dai_link *link, int be_id,
+ int ssp_bt)
+{
+ struct snd_soc_dai_link_component *cpus;
+
+ dev_dbg(dev, "link %d: bt offload, ssp %d\n", be_id, ssp_bt);
+
+ /* link name */
+ link->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-BT", ssp_bt);
+ if (!link->name)
+ return -ENOMEM;
+
+ /* cpus */
+ cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component),
+ GFP_KERNEL);
+ if (!cpus)
+ return -ENOMEM;
+
+ cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", ssp_bt);
+ if (!cpus->dai_name)
+ return -ENOMEM;
+
+ link->cpus = cpus;
+ link->num_cpus = 1;
+
+ /* codecs */
+ link->codecs = &snd_soc_dummy_dlc;
+ link->num_codecs = 1;
+
+ /* platforms */
+ link->platforms = platform_component;
+ link->num_platforms = ARRAY_SIZE(platform_component);
+
+ link->id = be_id;
+ link->no_pcm = 1;
+ link->dpcm_capture = 1;
+ link->dpcm_playback = 1;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(sof_intel_board_set_bt_link, SND_SOC_INTEL_SOF_BOARD_HELPERS);
+
+int sof_intel_board_set_hdmi_in_link(struct device *dev,
+ struct snd_soc_dai_link *link, int be_id,
+ int ssp_hdmi)
+{
+ struct snd_soc_dai_link_component *cpus;
+
+ dev_dbg(dev, "link %d: hdmi-in, ssp %d\n", be_id, ssp_hdmi);
+
+ /* link name */
+ link->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-HDMI", ssp_hdmi);
+ if (!link->name)
+ return -ENOMEM;
+
+ /* cpus */
+ cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component),
+ GFP_KERNEL);
+ if (!cpus)
+ return -ENOMEM;
+
+ cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", ssp_hdmi);
+ if (!cpus->dai_name)
+ return -ENOMEM;
+
+ link->cpus = cpus;
+ link->num_cpus = 1;
+
+ /* codecs */
+ link->codecs = &snd_soc_dummy_dlc;
+ link->num_codecs = 1;
+
+ /* platforms */
+ link->platforms = platform_component;
+ link->num_platforms = ARRAY_SIZE(platform_component);
+
+ link->id = be_id;
+ link->no_pcm = 1;
+ link->dpcm_capture = 1;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(sof_intel_board_set_hdmi_in_link, SND_SOC_INTEL_SOF_BOARD_HELPERS);
+
+static int calculate_num_links(struct sof_card_private *ctx)
+{
+ int num_links = 0;
+
+ /* headphone codec */
+ if (ctx->codec_type != CODEC_NONE)
+ num_links++;
+
+ /* dmic01 and dmic16k */
+ if (ctx->dmic_be_num > 0)
+ num_links++;
+
+ if (ctx->dmic_be_num > 1)
+ num_links++;
+
+ /* idisp HDMI */
+ num_links += ctx->hdmi_num;
+
+ /* speaker amp */
+ if (ctx->amp_type != CODEC_NONE)
+ num_links++;
+
+ /* BT audio offload */
+ if (ctx->bt_offload_present)
+ num_links++;
+
+ /* HDMI-In */
+ num_links += hweight32(ctx->ssp_mask_hdmi_in);
+
+ return num_links;
+}
+
+int sof_intel_board_set_dai_link(struct device *dev, struct snd_soc_card *card,
+ struct sof_card_private *ctx)
+{
+ struct snd_soc_dai_link *links;
+ int num_links;
+ int i;
+ int idx = 0;
+ int ret;
+ int ssp_hdmi_in = 0;
+
+ num_links = calculate_num_links(ctx);
+
+ links = devm_kcalloc(dev, num_links, sizeof(struct snd_soc_dai_link),
+ GFP_KERNEL);
+ if (!links)
+ return -ENOMEM;
+
+ /* headphone codec */
+ if (ctx->codec_type != CODEC_NONE) {
+ ret = sof_intel_board_set_codec_link(dev, &links[idx], idx,
+ ctx->codec_type,
+ ctx->ssp_codec);
+ if (ret) {
+ dev_err(dev, "fail to set codec link, ret %d\n", ret);
+ return ret;
+ }
+
+ ctx->codec_link = &links[idx];
+ idx++;
+ }
+
+ /* dmic01 and dmic16k */
+ if (ctx->dmic_be_num > 0) {
+ /* at least we have dmic01 */
+ ret = sof_intel_board_set_dmic_link(dev, &links[idx], idx,
+ SOF_DMIC_01);
+ if (ret) {
+ dev_err(dev, "fail to set dmic01 link, ret %d\n", ret);
+ return ret;
+ }
+
+ idx++;
+ }
+
+ if (ctx->dmic_be_num > 1) {
+ /* set up 2 BE links at most */
+ ret = sof_intel_board_set_dmic_link(dev, &links[idx], idx,
+ SOF_DMIC_16K);
+ if (ret) {
+ dev_err(dev, "fail to set dmic16k link, ret %d\n", ret);
+ return ret;
+ }
+
+ idx++;
+ }
+
+ /* idisp HDMI */
+ for (i = 1; i <= ctx->hdmi_num; i++) {
+ ret = sof_intel_board_set_intel_hdmi_link(dev, &links[idx], idx,
+ i,
+ ctx->hdmi.idisp_codec);
+ if (ret) {
+ dev_err(dev, "fail to set hdmi link, ret %d\n", ret);
+ return ret;
+ }
+
+ idx++;
+ }
+
+ /* speaker amp */
+ if (ctx->amp_type != CODEC_NONE) {
+ ret = sof_intel_board_set_ssp_amp_link(dev, &links[idx], idx,
+ ctx->amp_type,
+ ctx->ssp_amp);
+ if (ret) {
+ dev_err(dev, "fail to set amp link, ret %d\n", ret);
+ return ret;
+ }
+
+ ctx->amp_link = &links[idx];
+ idx++;
+ }
+
+ /* BT audio offload */
+ if (ctx->bt_offload_present) {
+ ret = sof_intel_board_set_bt_link(dev, &links[idx], idx,
+ ctx->ssp_bt);
+ if (ret) {
+ dev_err(dev, "fail to set bt link, ret %d\n", ret);
+ return ret;
+ }
+
+ idx++;
+ }
+
+ /* HDMI-In */
+ for_each_set_bit(ssp_hdmi_in, &ctx->ssp_mask_hdmi_in, 32) {
+ ret = sof_intel_board_set_hdmi_in_link(dev, &links[idx], idx,
+ ssp_hdmi_in);
+ if (ret) {
+ dev_err(dev, "fail to set hdmi-in link, ret %d\n", ret);
+ return ret;
+ }
+
+ idx++;
+ }
+
+ if (idx != num_links) {
+ dev_err(dev, "link number mismatch, idx %d, num_links %d\n", idx,
+ num_links);
+ return -EINVAL;
+ }
+
+ card->dai_link = links;
+ card->num_links = num_links;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(sof_intel_board_set_dai_link, SND_SOC_INTEL_SOF_BOARD_HELPERS);
+
MODULE_DESCRIPTION("ASoC Intel SOF Machine Driver Board Helpers");
MODULE_AUTHOR("Brent Lu <brent.lu@intel.com>");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON);
+MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_SSP_COMMON);
diff --git a/sound/soc/intel/boards/sof_board_helpers.h b/sound/soc/intel/boards/sof_board_helpers.h
index df99f576c1d8..3b36058118ca 100644
--- a/sound/soc/intel/boards/sof_board_helpers.h
+++ b/sound/soc/intel/boards/sof_board_helpers.h
@@ -30,6 +30,13 @@ struct sof_rt5682_private {
* @amp_type: type of speaker amplifier
* @dmic_be_num: number of Intel PCH DMIC BE link
* @hdmi_num: number of Intel HDMI BE link
+ * @ssp_codec: ssp port number of headphone BE link
+ * @ssp_amp: ssp port number of speaker BE link
+ * @ssp_bt: ssp port number of BT offload BE link
+ * @ssp_mask_hdmi_in: ssp port mask of HDMI-IN BE link
+ * @bt_offload_present: true to create BT offload BE link
+ * @codec_link: pointer to headset codec dai link
+ * @amp_link: pointer to speaker amplifier dai link
* @rt5682: private data for rt5682 machine driver
*/
struct sof_card_private {
@@ -42,6 +49,16 @@ struct sof_card_private {
int dmic_be_num;
int hdmi_num;
+ int ssp_codec;
+ int ssp_amp;
+ int ssp_bt;
+ unsigned long ssp_mask_hdmi_in;
+
+ bool bt_offload_present;
+
+ struct snd_soc_dai_link *codec_link;
+ struct snd_soc_dai_link *amp_link;
+
union {
struct sof_rt5682_private rt5682;
};
@@ -53,12 +70,26 @@ enum sof_dmic_be_type {
};
int sof_intel_board_card_late_probe(struct snd_soc_card *card);
+int sof_intel_board_set_dai_link(struct device *dev, struct snd_soc_card *card,
+ struct sof_card_private *ctx);
+int sof_intel_board_set_codec_link(struct device *dev,
+ struct snd_soc_dai_link *link, int be_id,
+ enum sof_ssp_codec codec_type, int ssp_codec);
int sof_intel_board_set_dmic_link(struct device *dev,
struct snd_soc_dai_link *link, int be_id,
enum sof_dmic_be_type be_type);
int sof_intel_board_set_intel_hdmi_link(struct device *dev,
struct snd_soc_dai_link *link, int be_id,
int hdmi_id, bool idisp_codec);
+int sof_intel_board_set_ssp_amp_link(struct device *dev,
+ struct snd_soc_dai_link *link, int be_id,
+ enum sof_ssp_codec amp_type, int ssp_amp);
+int sof_intel_board_set_bt_link(struct device *dev,
+ struct snd_soc_dai_link *link, int be_id,
+ int ssp_bt);
+int sof_intel_board_set_hdmi_in_link(struct device *dev,
+ struct snd_soc_dai_link *link, int be_id,
+ int ssp_hdmi);
#endif /* __SOF_INTEL_BOARD_HELPERS_H */
diff --git a/sound/soc/intel/boards/sof_cs42l42.c b/sound/soc/intel/boards/sof_cs42l42.c
index 1f760fc4cab2..c2442bf8ced0 100644
--- a/sound/soc/intel/boards/sof_cs42l42.c
+++ b/sound/soc/intel/boards/sof_cs42l42.c
@@ -138,13 +138,6 @@ static const struct snd_soc_ops sof_cs42l42_ops = {
.hw_params = sof_cs42l42_hw_params,
};
-static struct snd_soc_dai_link_component platform_component[] = {
- {
- /* name might be overridden during probe */
- .name = "0000:00:1f.3"
- }
-};
-
static int sof_card_late_probe(struct snd_soc_card *card)
{
return sof_intel_board_card_late_probe(card);
@@ -189,147 +182,11 @@ static struct snd_soc_dai_link_component cs42l42_component[] = {
}
};
-static int create_spk_amp_dai_links(struct device *dev,
- struct snd_soc_dai_link *links,
- struct snd_soc_dai_link_component *cpus,
- int *id, enum sof_ssp_codec amp_type,
- int ssp_amp)
-{
- int ret = 0;
-
- /* speaker amp */
- if (amp_type == CODEC_NONE)
- return 0;
-
- links[*id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec",
- ssp_amp);
- if (!links[*id].name) {
- ret = -ENOMEM;
- goto devm_err;
- }
-
- links[*id].id = *id;
-
- switch (amp_type) {
- case CODEC_MAX98357A:
- max_98357a_dai_link(&links[*id]);
- break;
- case CODEC_MAX98360A:
- max_98360a_dai_link(&links[*id]);
- break;
- default:
- dev_err(dev, "invalid amp type %d\n", amp_type);
- return -EINVAL;
- }
-
- links[*id].platforms = platform_component;
- links[*id].num_platforms = ARRAY_SIZE(platform_component);
- links[*id].dpcm_playback = 1;
- /* firmware-generated echo reference */
- links[*id].dpcm_capture = 1;
-
- links[*id].no_pcm = 1;
- links[*id].cpus = &cpus[*id];
- links[*id].num_cpus = 1;
-
- links[*id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
- "SSP%d Pin", ssp_amp);
- if (!links[*id].cpus->dai_name) {
- ret = -ENOMEM;
- goto devm_err;
- }
-
- (*id)++;
-
-devm_err:
- return ret;
-}
-
-static int create_hp_codec_dai_links(struct device *dev,
- struct snd_soc_dai_link *links,
- struct snd_soc_dai_link_component *cpus,
- int *id, int ssp_codec)
-{
- /* codec SSP */
- links[*id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec",
- ssp_codec);
- if (!links[*id].name)
- goto devm_err;
-
- links[*id].id = *id;
- links[*id].codecs = cs42l42_component;
- links[*id].num_codecs = ARRAY_SIZE(cs42l42_component);
- links[*id].platforms = platform_component;
- links[*id].num_platforms = ARRAY_SIZE(platform_component);
- links[*id].init = sof_cs42l42_init;
- links[*id].exit = sof_cs42l42_exit;
- links[*id].ops = &sof_cs42l42_ops;
- links[*id].dpcm_playback = 1;
- links[*id].dpcm_capture = 1;
- links[*id].no_pcm = 1;
- links[*id].cpus = &cpus[*id];
- links[*id].num_cpus = 1;
-
- links[*id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
- "SSP%d Pin",
- ssp_codec);
- if (!links[*id].cpus->dai_name)
- goto devm_err;
-
- (*id)++;
-
- return 0;
-
-devm_err:
- return -ENOMEM;
-}
-
-static int create_bt_offload_dai_links(struct device *dev,
- struct snd_soc_dai_link *links,
- struct snd_soc_dai_link_component *cpus,
- int *id, int ssp_bt)
-{
- /* bt offload */
- if (!(sof_cs42l42_quirk & SOF_BT_OFFLOAD_PRESENT))
- return 0;
-
- links[*id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-BT",
- ssp_bt);
- if (!links[*id].name)
- goto devm_err;
-
- links[*id].id = *id;
- links[*id].codecs = &snd_soc_dummy_dlc;
- links[*id].num_codecs = 1;
- links[*id].platforms = platform_component;
- links[*id].num_platforms = ARRAY_SIZE(platform_component);
-
- links[*id].dpcm_playback = 1;
- links[*id].dpcm_capture = 1;
- links[*id].no_pcm = 1;
- links[*id].cpus = &cpus[*id];
- links[*id].num_cpus = 1;
-
- links[*id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
- "SSP%d Pin",
- ssp_bt);
- if (!links[*id].cpus->dai_name)
- goto devm_err;
-
- (*id)++;
-
- return 0;
-
-devm_err:
- return -ENOMEM;
-}
-
static struct snd_soc_dai_link *
sof_card_dai_links_create(struct device *dev, enum sof_ssp_codec amp_type,
int ssp_codec, int ssp_amp, int ssp_bt,
int dmic_be_num, int hdmi_num, bool idisp_codec)
{
- struct snd_soc_dai_link_component *cpus;
struct snd_soc_dai_link *links;
int ret;
int id = 0;
@@ -338,9 +195,7 @@ sof_card_dai_links_create(struct device *dev, enum sof_ssp_codec amp_type,
links = devm_kcalloc(dev, sof_audio_card_cs42l42.num_links,
sizeof(struct snd_soc_dai_link), GFP_KERNEL);
- cpus = devm_kcalloc(dev, sof_audio_card_cs42l42.num_links,
- sizeof(struct snd_soc_dai_link_component), GFP_KERNEL);
- if (!links || !cpus)
+ if (!links)
goto devm_err;
link_seq = (sof_cs42l42_quirk & SOF_CS42L42_DAILINK_MASK) >> SOF_CS42L42_DAILINK_SHIFT;
@@ -350,20 +205,52 @@ sof_card_dai_links_create(struct device *dev, enum sof_ssp_codec amp_type,
switch (link_type) {
case LINK_HP:
- ret = create_hp_codec_dai_links(dev, links, cpus, &id, ssp_codec);
- if (ret < 0) {
+ ret = sof_intel_board_set_codec_link(dev, &links[id], id,
+ CODEC_CS42L42,
+ ssp_codec);
+ if (ret) {
dev_err(dev, "fail to create hp codec dai links, ret %d\n",
ret);
goto devm_err;
}
+
+ /* codec-specific fields */
+ links[id].codecs = cs42l42_component;
+ links[id].num_codecs = ARRAY_SIZE(cs42l42_component);
+ links[id].init = sof_cs42l42_init;
+ links[id].exit = sof_cs42l42_exit;
+ links[id].ops = &sof_cs42l42_ops;
+
+ id++;
break;
case LINK_SPK:
- ret = create_spk_amp_dai_links(dev, links, cpus, &id,
- amp_type, ssp_amp);
- if (ret < 0) {
- dev_err(dev, "fail to create spk amp dai links, ret %d\n",
- ret);
- goto devm_err;
+ if (amp_type != CODEC_NONE) {
+ ret = sof_intel_board_set_ssp_amp_link(dev,
+ &links[id],
+ id,
+ amp_type,
+ ssp_amp);
+ if (ret) {
+ dev_err(dev, "fail to create spk amp dai links, ret %d\n",
+ ret);
+ goto devm_err;
+ }
+
+ /* codec-specific fields */
+ switch (amp_type) {
+ case CODEC_MAX98357A:
+ max_98357a_dai_link(&links[id]);
+ break;
+ case CODEC_MAX98360A:
+ max_98360a_dai_link(&links[id]);
+ break;
+ default:
+ dev_err(dev, "invalid amp type %d\n",
+ amp_type);
+ goto devm_err;
+ }
+
+ id++;
}
break;
case LINK_DMIC:
@@ -413,11 +300,17 @@ sof_card_dai_links_create(struct device *dev, enum sof_ssp_codec amp_type,
}
break;
case LINK_BT:
- ret = create_bt_offload_dai_links(dev, links, cpus, &id, ssp_bt);
- if (ret < 0) {
- dev_err(dev, "fail to create bt offload dai links, ret %d\n",
- ret);
- goto devm_err;
+ if (sof_cs42l42_quirk & SOF_BT_OFFLOAD_PRESENT) {
+ ret = sof_intel_board_set_bt_link(dev,
+ &links[id], id,
+ ssp_bt);
+ if (ret) {
+ dev_err(dev, "fail to create bt offload dai links, ret %d\n",
+ ret);
+ goto devm_err;
+ }
+
+ id++;
}
break;
case LINK_NONE:
@@ -440,7 +333,7 @@ static int sof_audio_probe(struct platform_device *pdev)
struct snd_soc_acpi_mach *mach = pdev->dev.platform_data;
struct snd_soc_dai_link *dai_links;
struct sof_card_private *ctx;
- int ret, ssp_bt, ssp_amp, ssp_codec;
+ int ret;
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
@@ -469,25 +362,29 @@ static int sof_audio_probe(struct platform_device *pdev)
dev_dbg(&pdev->dev, "sof_cs42l42_quirk = %lx\n", sof_cs42l42_quirk);
- ssp_bt = (sof_cs42l42_quirk & SOF_CS42L42_SSP_BT_MASK) >>
+ /* port number of peripherals attached to ssp interface */
+ ctx->ssp_bt = (sof_cs42l42_quirk & SOF_CS42L42_SSP_BT_MASK) >>
SOF_CS42L42_SSP_BT_SHIFT;
- ssp_amp = (sof_cs42l42_quirk & SOF_CS42L42_SSP_AMP_MASK) >>
+ ctx->ssp_amp = (sof_cs42l42_quirk & SOF_CS42L42_SSP_AMP_MASK) >>
SOF_CS42L42_SSP_AMP_SHIFT;
- ssp_codec = sof_cs42l42_quirk & SOF_CS42L42_SSP_CODEC_MASK;
+ ctx->ssp_codec = sof_cs42l42_quirk & SOF_CS42L42_SSP_CODEC_MASK;
/* compute number of dai links */
sof_audio_card_cs42l42.num_links = 1 + ctx->dmic_be_num + ctx->hdmi_num;
if (ctx->amp_type != CODEC_NONE)
sof_audio_card_cs42l42.num_links++;
- if (sof_cs42l42_quirk & SOF_BT_OFFLOAD_PRESENT)
+ if (sof_cs42l42_quirk & SOF_BT_OFFLOAD_PRESENT) {
+ ctx->bt_offload_present = true;
sof_audio_card_cs42l42.num_links++;
+ }
dai_links = sof_card_dai_links_create(&pdev->dev, ctx->amp_type,
- ssp_codec, ssp_amp, ssp_bt,
- ctx->dmic_be_num, ctx->hdmi_num,
+ ctx->ssp_codec, ctx->ssp_amp,
+ ctx->ssp_bt, ctx->dmic_be_num,
+ ctx->hdmi_num,
ctx->hdmi.idisp_codec);
if (!dai_links)
return -ENOMEM;
diff --git a/sound/soc/intel/boards/sof_maxim_common.c b/sound/soc/intel/boards/sof_maxim_common.c
index 3c00afc32805..cf2974718271 100644
--- a/sound/soc/intel/boards/sof_maxim_common.c
+++ b/sound/soc/intel/boards/sof_maxim_common.c
@@ -61,16 +61,21 @@ static int max_98373_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_soc_dai *codec_dai;
+ int ret = 0;
int j;
for_each_rtd_codec_dais(rtd, j, codec_dai) {
if (!strcmp(codec_dai->component->name, MAX_98373_DEV0_NAME)) {
/* DEV0 tdm slot configuration */
- snd_soc_dai_set_tdm_slot(codec_dai, 0x03, 3, 8, 32);
- }
- if (!strcmp(codec_dai->component->name, MAX_98373_DEV1_NAME)) {
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x03, 3, 8, 32);
+ } else if (!strcmp(codec_dai->component->name, MAX_98373_DEV1_NAME)) {
/* DEV1 tdm slot configuration */
- snd_soc_dai_set_tdm_slot(codec_dai, 0x0C, 3, 8, 32);
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x0C, 3, 8, 32);
+ }
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "fail to set tdm slot, ret %d\n",
+ ret);
+ return ret;
}
}
return 0;
diff --git a/sound/soc/intel/boards/sof_nau8825.c b/sound/soc/intel/boards/sof_nau8825.c
index dc2821a012d4..719c2fbaf515 100644
--- a/sound/soc/intel/boards/sof_nau8825.c
+++ b/sound/soc/intel/boards/sof_nau8825.c
@@ -138,13 +138,6 @@ static struct snd_soc_ops sof_nau8825_ops = {
.hw_params = sof_nau8825_hw_params,
};
-static struct snd_soc_dai_link_component platform_component[] = {
- {
- /* name might be overridden during probe */
- .name = "0000:00:1f.3"
- }
-};
-
static int sof_card_late_probe(struct snd_soc_card *card)
{
struct sof_card_private *ctx = snd_soc_card_get_drvdata(card);
@@ -207,168 +200,69 @@ static struct snd_soc_dai_link_component nau8825_component[] = {
}
};
-static struct snd_soc_dai_link *
-sof_card_dai_links_create(struct device *dev, enum sof_ssp_codec amp_type,
- int ssp_codec, int ssp_amp, int dmic_be_num,
- int hdmi_num, bool idisp_codec)
+static int
+sof_card_dai_links_create(struct device *dev, struct snd_soc_card *card,
+ struct sof_card_private *ctx)
{
- struct snd_soc_dai_link_component *cpus;
- struct snd_soc_dai_link *links;
- int i;
- int id = 0;
int ret;
- links = devm_kcalloc(dev, sof_audio_card_nau8825.num_links,
- sizeof(struct snd_soc_dai_link), GFP_KERNEL);
- cpus = devm_kcalloc(dev, sof_audio_card_nau8825.num_links,
- sizeof(struct snd_soc_dai_link_component), GFP_KERNEL);
- if (!links || !cpus)
- goto devm_err;
-
- /* codec SSP */
- links[id].name = devm_kasprintf(dev, GFP_KERNEL,
- "SSP%d-Codec", ssp_codec);
- if (!links[id].name)
- goto devm_err;
-
- links[id].id = id;
- links[id].codecs = nau8825_component;
- links[id].num_codecs = ARRAY_SIZE(nau8825_component);
- links[id].platforms = platform_component;
- links[id].num_platforms = ARRAY_SIZE(platform_component);
- links[id].init = sof_nau8825_codec_init;
- links[id].exit = sof_nau8825_codec_exit;
- links[id].ops = &sof_nau8825_ops;
- links[id].dpcm_playback = 1;
- links[id].dpcm_capture = 1;
- links[id].no_pcm = 1;
- links[id].cpus = &cpus[id];
- links[id].num_cpus = 1;
-
- links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
- "SSP%d Pin",
- ssp_codec);
- if (!links[id].cpus->dai_name)
- goto devm_err;
-
- id++;
-
- /* dmic */
- if (dmic_be_num > 0) {
- /* at least we have dmic01 */
- ret = sof_intel_board_set_dmic_link(dev, &links[id], id,
- SOF_DMIC_01);
- if (ret)
- return NULL;
-
- id++;
- }
-
- if (dmic_be_num > 1) {
- /* set up 2 BE links at most */
- ret = sof_intel_board_set_dmic_link(dev, &links[id], id,
- SOF_DMIC_16K);
- if (ret)
- return NULL;
+ ret = sof_intel_board_set_dai_link(dev, card, ctx);
+ if (ret)
+ return ret;
- id++;
+ if (!ctx->codec_link) {
+ dev_err(dev, "codec link not available");
+ return -EINVAL;
}
- /* HDMI */
- for (i = 1; i <= hdmi_num; i++) {
- ret = sof_intel_board_set_intel_hdmi_link(dev, &links[id], id,
- i, idisp_codec);
- if (ret)
- return NULL;
+ /* codec-specific fields for headphone codec */
+ ctx->codec_link->codecs = nau8825_component;
+ ctx->codec_link->num_codecs = ARRAY_SIZE(nau8825_component);
+ ctx->codec_link->init = sof_nau8825_codec_init;
+ ctx->codec_link->exit = sof_nau8825_codec_exit;
+ ctx->codec_link->ops = &sof_nau8825_ops;
- id++;
- }
+ if (ctx->amp_type == CODEC_NONE)
+ return 0;
- /* speaker amp */
- if (amp_type != CODEC_NONE) {
- links[id].name = devm_kasprintf(dev, GFP_KERNEL,
- "SSP%d-Codec", ssp_amp);
- if (!links[id].name)
- goto devm_err;
-
- links[id].id = id;
-
- switch (amp_type) {
- case CODEC_MAX98360A:
- max_98360a_dai_link(&links[id]);
- break;
- case CODEC_MAX98373:
- links[id].codecs = max_98373_components;
- links[id].num_codecs = ARRAY_SIZE(max_98373_components);
- links[id].init = max_98373_spk_codec_init;
- links[id].ops = &max_98373_ops;
- break;
- case CODEC_NAU8318:
- nau8318_set_dai_link(&links[id]);
- break;
- case CODEC_RT1015P:
- sof_rt1015p_dai_link(&links[id]);
- break;
- case CODEC_RT1019P:
- sof_rt1019p_dai_link(&links[id]);
- break;
- default:
- dev_err(dev, "invalid amp type %d\n", amp_type);
- return NULL;
- }
-
- links[id].platforms = platform_component;
- links[id].num_platforms = ARRAY_SIZE(platform_component);
- links[id].dpcm_playback = 1;
- /* feedback stream or firmware-generated echo reference */
- links[id].dpcm_capture = 1;
-
- links[id].no_pcm = 1;
- links[id].cpus = &cpus[id];
- links[id].num_cpus = 1;
- links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
- "SSP%d Pin",
- ssp_amp);
- if (!links[id].cpus->dai_name)
- goto devm_err;
- id++;
+ if (!ctx->amp_link) {
+ dev_err(dev, "amp link not available");
+ return -EINVAL;
}
- /* BT audio offload */
- if (sof_nau8825_quirk & SOF_SSP_BT_OFFLOAD_PRESENT) {
- int port = (sof_nau8825_quirk & SOF_BT_OFFLOAD_SSP_MASK) >>
- SOF_BT_OFFLOAD_SSP_SHIFT;
-
- links[id].id = id;
- links[id].cpus = &cpus[id];
- links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
- "SSP%d Pin", port);
- if (!links[id].cpus->dai_name)
- goto devm_err;
- links[id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-BT", port);
- if (!links[id].name)
- goto devm_err;
- links[id].codecs = &snd_soc_dummy_dlc;
- links[id].num_codecs = 1;
- links[id].platforms = platform_component;
- links[id].num_platforms = ARRAY_SIZE(platform_component);
- links[id].dpcm_playback = 1;
- links[id].dpcm_capture = 1;
- links[id].no_pcm = 1;
- links[id].num_cpus = 1;
+ /* codec-specific fields for speaker amplifier */
+ switch (ctx->amp_type) {
+ case CODEC_MAX98360A:
+ max_98360a_dai_link(ctx->amp_link);
+ break;
+ case CODEC_MAX98373:
+ ctx->amp_link->codecs = max_98373_components;
+ ctx->amp_link->num_codecs = ARRAY_SIZE(max_98373_components);
+ ctx->amp_link->init = max_98373_spk_codec_init;
+ ctx->amp_link->ops = &max_98373_ops;
+ break;
+ case CODEC_NAU8318:
+ nau8318_set_dai_link(ctx->amp_link);
+ break;
+ case CODEC_RT1015P:
+ sof_rt1015p_dai_link(ctx->amp_link);
+ break;
+ case CODEC_RT1019P:
+ sof_rt1019p_dai_link(ctx->amp_link);
+ break;
+ default:
+ dev_err(dev, "invalid amp type %d\n", ctx->amp_type);
+ return -EINVAL;
}
- return links;
-devm_err:
- return NULL;
+ return 0;
}
static int sof_audio_probe(struct platform_device *pdev)
{
struct snd_soc_acpi_mach *mach = pdev->dev.platform_data;
- struct snd_soc_dai_link *dai_links;
struct sof_card_private *ctx;
- int ret, ssp_amp, ssp_codec;
+ int ret;
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
@@ -393,28 +287,22 @@ static int sof_audio_probe(struct platform_device *pdev)
if (mach->mach_params.codec_mask & IDISP_CODEC_MASK)
ctx->hdmi.idisp_codec = true;
- ssp_amp = (sof_nau8825_quirk & SOF_NAU8825_SSP_AMP_MASK) >>
- SOF_NAU8825_SSP_AMP_SHIFT;
+ /* port number of peripherals attached to ssp interface */
+ ctx->ssp_bt = (sof_nau8825_quirk & SOF_BT_OFFLOAD_SSP_MASK) >>
+ SOF_BT_OFFLOAD_SSP_SHIFT;
- ssp_codec = sof_nau8825_quirk & SOF_NAU8825_SSP_CODEC_MASK;
-
- /* compute number of dai links */
- sof_audio_card_nau8825.num_links = 1 + ctx->dmic_be_num + ctx->hdmi_num;
+ ctx->ssp_amp = (sof_nau8825_quirk & SOF_NAU8825_SSP_AMP_MASK) >>
+ SOF_NAU8825_SSP_AMP_SHIFT;
- if (ctx->amp_type != CODEC_NONE)
- sof_audio_card_nau8825.num_links++;
+ ctx->ssp_codec = sof_nau8825_quirk & SOF_NAU8825_SSP_CODEC_MASK;
if (sof_nau8825_quirk & SOF_SSP_BT_OFFLOAD_PRESENT)
- sof_audio_card_nau8825.num_links++;
-
- dai_links = sof_card_dai_links_create(&pdev->dev, ctx->amp_type,
- ssp_codec, ssp_amp,
- ctx->dmic_be_num, ctx->hdmi_num,
- ctx->hdmi.idisp_codec);
- if (!dai_links)
- return -ENOMEM;
+ ctx->bt_offload_present = true;
- sof_audio_card_nau8825.dai_link = dai_links;
+ /* update dai_link */
+ ret = sof_card_dai_links_create(&pdev->dev, &sof_audio_card_nau8825, ctx);
+ if (ret)
+ return ret;
/* update codec_conf */
switch (ctx->amp_type) {
@@ -465,49 +353,7 @@ static const struct platform_device_id board_ids[] = {
SOF_NAU8825_NUM_HDMIDEV(4)),
},
{
- .name = "adl_max98373_8825",
- .driver_data = (kernel_ulong_t)(SOF_NAU8825_SSP_CODEC(0) |
- SOF_NAU8825_SSP_AMP(1) |
- SOF_NAU8825_NUM_HDMIDEV(4) |
- SOF_BT_OFFLOAD_SSP(2) |
- SOF_SSP_BT_OFFLOAD_PRESENT),
- },
- {
- /* The limitation of length of char array, shorten the name */
- .name = "adl_mx98360a_8825",
- .driver_data = (kernel_ulong_t)(SOF_NAU8825_SSP_CODEC(0) |
- SOF_NAU8825_SSP_AMP(1) |
- SOF_NAU8825_NUM_HDMIDEV(4) |
- SOF_BT_OFFLOAD_SSP(2) |
- SOF_SSP_BT_OFFLOAD_PRESENT),
-
- },
- {
- .name = "adl_rt1015p_8825",
- .driver_data = (kernel_ulong_t)(SOF_NAU8825_SSP_CODEC(0) |
- SOF_NAU8825_SSP_AMP(1) |
- SOF_NAU8825_NUM_HDMIDEV(4) |
- SOF_BT_OFFLOAD_SSP(2) |
- SOF_SSP_BT_OFFLOAD_PRESENT),
- },
- {
- .name = "adl_nau8318_8825",
- .driver_data = (kernel_ulong_t)(SOF_NAU8825_SSP_CODEC(0) |
- SOF_NAU8825_SSP_AMP(1) |
- SOF_NAU8825_NUM_HDMIDEV(4) |
- SOF_BT_OFFLOAD_SSP(2) |
- SOF_SSP_BT_OFFLOAD_PRESENT),
- },
- {
- .name = "rpl_max98373_8825",
- .driver_data = (kernel_ulong_t)(SOF_NAU8825_SSP_CODEC(0) |
- SOF_NAU8825_SSP_AMP(1) |
- SOF_NAU8825_NUM_HDMIDEV(4) |
- SOF_BT_OFFLOAD_SSP(2) |
- SOF_SSP_BT_OFFLOAD_PRESENT),
- },
- {
- .name = "rpl_mx98360a_8825",
+ .name = "adl_nau8825_def",
.driver_data = (kernel_ulong_t)(SOF_NAU8825_SSP_CODEC(0) |
SOF_NAU8825_SSP_AMP(1) |
SOF_NAU8825_NUM_HDMIDEV(4) |
@@ -515,7 +361,7 @@ static const struct platform_device_id board_ids[] = {
SOF_SSP_BT_OFFLOAD_PRESENT),
},
{
- .name = "rpl_nau8318_8825",
+ .name = "rpl_nau8825_def",
.driver_data = (kernel_ulong_t)(SOF_NAU8825_SSP_CODEC(0) |
SOF_NAU8825_SSP_AMP(1) |
SOF_NAU8825_NUM_HDMIDEV(4) |
diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c
index 06ad15af46de..cd50f26d1edb 100644
--- a/sound/soc/intel/boards/sof_rt5682.c
+++ b/sound/soc/intel/boards/sof_rt5682.c
@@ -463,13 +463,6 @@ static struct snd_soc_ops sof_rt5682_ops = {
.hw_params = sof_rt5682_hw_params,
};
-static struct snd_soc_dai_link_component platform_component[] = {
- {
- /* name might be overridden during probe */
- .name = "0000:00:1f.3"
- }
-};
-
static int sof_card_late_probe(struct snd_soc_card *card)
{
struct sof_card_private *ctx = snd_soc_card_get_drvdata(card);
@@ -570,69 +563,45 @@ static struct snd_soc_dai_link_component rt5650_components[] = {
}
};
-static struct snd_soc_dai_link *
-sof_card_dai_links_create(struct device *dev, enum sof_ssp_codec codec_type,
- enum sof_ssp_codec amp_type, int ssp_codec,
- int ssp_amp, int dmic_be_num, int hdmi_num,
- bool idisp_codec, bool is_legacy_cpu)
+static int
+sof_card_dai_links_create(struct device *dev, struct snd_soc_card *card,
+ struct sof_card_private *ctx)
{
- struct snd_soc_dai_link_component *cpus;
- struct snd_soc_dai_link *links;
- int i;
- int id = 0;
int ret;
- int hdmi_id_offset = 0;
-
- links = devm_kcalloc(dev, sof_audio_card_rt5682.num_links,
- sizeof(struct snd_soc_dai_link), GFP_KERNEL);
- cpus = devm_kcalloc(dev, sof_audio_card_rt5682.num_links,
- sizeof(struct snd_soc_dai_link_component), GFP_KERNEL);
- if (!links || !cpus)
- goto devm_err;
- /* codec SSP */
- links[id].name = devm_kasprintf(dev, GFP_KERNEL,
- "SSP%d-Codec", ssp_codec);
- if (!links[id].name)
- goto devm_err;
+ ret = sof_intel_board_set_dai_link(dev, card, ctx);
+ if (ret)
+ return ret;
- links[id].id = id;
+ if (!ctx->codec_link) {
+ dev_err(dev, "codec link not available");
+ return -EINVAL;
+ }
- switch (codec_type) {
+ /* codec-specific fields for headphone codec */
+ switch (ctx->codec_type) {
case CODEC_RT5650:
- links[id].codecs = &rt5650_components[0];
- links[id].num_codecs = 1;
+ ctx->codec_link->codecs = &rt5650_components[0];
+ ctx->codec_link->num_codecs = 1;
break;
case CODEC_RT5682:
- links[id].codecs = rt5682_component;
- links[id].num_codecs = ARRAY_SIZE(rt5682_component);
+ ctx->codec_link->codecs = rt5682_component;
+ ctx->codec_link->num_codecs = ARRAY_SIZE(rt5682_component);
break;
case CODEC_RT5682S:
- links[id].codecs = rt5682s_component;
- links[id].num_codecs = ARRAY_SIZE(rt5682s_component);
+ ctx->codec_link->codecs = rt5682s_component;
+ ctx->codec_link->num_codecs = ARRAY_SIZE(rt5682s_component);
break;
default:
- dev_err(dev, "invalid codec type %d\n", codec_type);
- return NULL;
+ dev_err(dev, "invalid codec type %d\n", ctx->codec_type);
+ return -EINVAL;
}
- links[id].platforms = platform_component;
- links[id].num_platforms = ARRAY_SIZE(platform_component);
- links[id].init = sof_rt5682_codec_init;
- links[id].exit = sof_rt5682_codec_exit;
- links[id].ops = &sof_rt5682_ops;
- links[id].dpcm_playback = 1;
- links[id].dpcm_capture = 1;
- links[id].no_pcm = 1;
- links[id].cpus = &cpus[id];
- links[id].num_cpus = 1;
- if (is_legacy_cpu) {
- links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
- "ssp%d-port",
- ssp_codec);
- if (!links[id].cpus->dai_name)
- goto devm_err;
- } else {
+ ctx->codec_link->init = sof_rt5682_codec_init;
+ ctx->codec_link->exit = sof_rt5682_codec_exit;
+ ctx->codec_link->ops = &sof_rt5682_ops;
+
+ if (!ctx->rt5682.is_legacy_cpu) {
/*
* Currently, On SKL+ platforms MCLK will be turned off in sof
* runtime suspended, and it will go into runtime suspended
@@ -642,184 +611,66 @@ sof_card_dai_links_create(struct device *dev, enum sof_ssp_codec codec_type,
* avoid the noise.
* It can be removed once we can control MCLK by driver.
*/
- links[id].ignore_pmdown_time = 1;
- links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
- "SSP%d Pin",
- ssp_codec);
- if (!links[id].cpus->dai_name)
- goto devm_err;
- }
- id++;
-
- /* dmic */
- if (dmic_be_num > 0) {
- /* at least we have dmic01 */
- ret = sof_intel_board_set_dmic_link(dev, &links[id], id,
- SOF_DMIC_01);
- if (ret)
- return NULL;
-
- id++;
- }
-
- if (dmic_be_num > 1) {
- /* set up 2 BE links at most */
- ret = sof_intel_board_set_dmic_link(dev, &links[id], id,
- SOF_DMIC_16K);
- if (ret)
- return NULL;
-
- id++;
+ ctx->codec_link->ignore_pmdown_time = 1;
}
- /* HDMI */
- for (i = 1; i <= hdmi_num; i++) {
- ret = sof_intel_board_set_intel_hdmi_link(dev, &links[id], id,
- i, idisp_codec);
- if (ret)
- return NULL;
-
- id++;
- }
-
- /* speaker amp */
- if (amp_type != CODEC_NONE) {
- links[id].name = devm_kasprintf(dev, GFP_KERNEL,
- "SSP%d-Codec", ssp_amp);
- if (!links[id].name)
- goto devm_err;
-
- links[id].id = id;
-
- switch (amp_type) {
- case CODEC_MAX98357A:
- max_98357a_dai_link(&links[id]);
- break;
- case CODEC_MAX98360A:
- max_98360a_dai_link(&links[id]);
- break;
- case CODEC_MAX98373:
- links[id].codecs = max_98373_components;
- links[id].num_codecs = ARRAY_SIZE(max_98373_components);
- links[id].init = max_98373_spk_codec_init;
- links[id].ops = &max_98373_ops;
- break;
- case CODEC_MAX98390:
- max_98390_dai_link(dev, &links[id]);
- break;
- case CODEC_RT1011:
- sof_rt1011_dai_link(&links[id]);
- break;
- case CODEC_RT1015:
- sof_rt1015_dai_link(&links[id]);
- break;
- case CODEC_RT1015P:
- sof_rt1015p_dai_link(&links[id]);
- break;
- case CODEC_RT1019P:
- sof_rt1019p_dai_link(&links[id]);
- break;
- case CODEC_RT5650:
- /* use AIF2 to support speaker pipeline */
- links[id].codecs = &rt5650_components[1];
- links[id].num_codecs = 1;
- links[id].init = rt5650_spk_init;
- links[id].ops = &sof_rt5682_ops;
- break;
- default:
- dev_err(dev, "invalid amp type %d\n", amp_type);
- return NULL;
- }
-
- links[id].platforms = platform_component;
- links[id].num_platforms = ARRAY_SIZE(platform_component);
- links[id].dpcm_playback = 1;
- /* feedback stream or firmware-generated echo reference */
- links[id].dpcm_capture = 1;
-
- links[id].no_pcm = 1;
- links[id].cpus = &cpus[id];
- links[id].num_cpus = 1;
- if (is_legacy_cpu) {
- links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
- "ssp%d-port",
- ssp_amp);
- if (!links[id].cpus->dai_name)
- goto devm_err;
-
- } else {
- links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
- "SSP%d Pin",
- ssp_amp);
- if (!links[id].cpus->dai_name)
- goto devm_err;
- }
- id++;
- }
+ if (ctx->amp_type == CODEC_NONE)
+ return 0;
- /* BT audio offload */
- if (sof_rt5682_quirk & SOF_SSP_BT_OFFLOAD_PRESENT) {
- int port = (sof_rt5682_quirk & SOF_BT_OFFLOAD_SSP_MASK) >>
- SOF_BT_OFFLOAD_SSP_SHIFT;
-
- links[id].id = id;
- links[id].cpus = &cpus[id];
- links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
- "SSP%d Pin", port);
- if (!links[id].cpus->dai_name)
- goto devm_err;
- links[id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-BT", port);
- if (!links[id].name)
- goto devm_err;
- links[id].codecs = &snd_soc_dummy_dlc;
- links[id].num_codecs = 1;
- links[id].platforms = platform_component;
- links[id].num_platforms = ARRAY_SIZE(platform_component);
- links[id].dpcm_playback = 1;
- links[id].dpcm_capture = 1;
- links[id].no_pcm = 1;
- links[id].num_cpus = 1;
+ if (!ctx->amp_link) {
+ dev_err(dev, "amp link not available");
+ return -EINVAL;
}
- /* HDMI-In SSP */
- if (sof_rt5682_quirk & SOF_SSP_HDMI_CAPTURE_PRESENT_MASK) {
- unsigned long hdmi_in_ssp = (sof_rt5682_quirk &
- SOF_SSP_HDMI_CAPTURE_PRESENT_MASK) >>
- SOF_NO_OF_HDMI_CAPTURE_SSP_SHIFT;
- int port = 0;
-
- for_each_set_bit(port, &hdmi_in_ssp, 32) {
- links[id].cpus = &cpus[id];
- links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
- "SSP%d Pin", port);
- if (!links[id].cpus->dai_name)
- return NULL;
- links[id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-HDMI", port);
- if (!links[id].name)
- return NULL;
- links[id].id = id + hdmi_id_offset;
- links[id].codecs = &snd_soc_dummy_dlc;
- links[id].num_codecs = 1;
- links[id].platforms = platform_component;
- links[id].num_platforms = ARRAY_SIZE(platform_component);
- links[id].dpcm_capture = 1;
- links[id].no_pcm = 1;
- links[id].num_cpus = 1;
- id++;
- }
+ /* codec-specific fields for speaker amplifier */
+ switch (ctx->amp_type) {
+ case CODEC_MAX98357A:
+ max_98357a_dai_link(ctx->amp_link);
+ break;
+ case CODEC_MAX98360A:
+ max_98360a_dai_link(ctx->amp_link);
+ break;
+ case CODEC_MAX98373:
+ ctx->amp_link->codecs = max_98373_components;
+ ctx->amp_link->num_codecs = ARRAY_SIZE(max_98373_components);
+ ctx->amp_link->init = max_98373_spk_codec_init;
+ ctx->amp_link->ops = &max_98373_ops;
+ break;
+ case CODEC_MAX98390:
+ max_98390_dai_link(dev, ctx->amp_link);
+ break;
+ case CODEC_RT1011:
+ sof_rt1011_dai_link(ctx->amp_link);
+ break;
+ case CODEC_RT1015:
+ sof_rt1015_dai_link(ctx->amp_link);
+ break;
+ case CODEC_RT1015P:
+ sof_rt1015p_dai_link(ctx->amp_link);
+ break;
+ case CODEC_RT1019P:
+ sof_rt1019p_dai_link(ctx->amp_link);
+ break;
+ case CODEC_RT5650:
+ /* use AIF2 to support speaker pipeline */
+ ctx->amp_link->codecs = &rt5650_components[1];
+ ctx->amp_link->num_codecs = 1;
+ ctx->amp_link->init = rt5650_spk_init;
+ ctx->amp_link->ops = &sof_rt5682_ops;
+ break;
+ default:
+ dev_err(dev, "invalid amp type %d\n", ctx->amp_type);
+ return -EINVAL;
}
- return links;
-devm_err:
- return NULL;
+ return 0;
}
static int sof_audio_probe(struct platform_device *pdev)
{
struct snd_soc_acpi_mach *mach = pdev->dev.platform_data;
- struct snd_soc_dai_link *dai_links;
struct sof_card_private *ctx;
- int ret, ssp_amp, ssp_codec;
+ int ret;
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
@@ -885,34 +736,25 @@ static int sof_audio_probe(struct platform_device *pdev)
dev_dbg(&pdev->dev, "sof_rt5682_quirk = %lx\n", sof_rt5682_quirk);
- ssp_amp = (sof_rt5682_quirk & SOF_RT5682_SSP_AMP_MASK) >>
- SOF_RT5682_SSP_AMP_SHIFT;
+ /* port number/mask of peripherals attached to ssp interface */
+ ctx->ssp_mask_hdmi_in = (sof_rt5682_quirk & SOF_SSP_HDMI_CAPTURE_PRESENT_MASK) >>
+ SOF_NO_OF_HDMI_CAPTURE_SSP_SHIFT;
- ssp_codec = sof_rt5682_quirk & SOF_RT5682_SSP_CODEC_MASK;
+ ctx->ssp_bt = (sof_rt5682_quirk & SOF_BT_OFFLOAD_SSP_MASK) >>
+ SOF_BT_OFFLOAD_SSP_SHIFT;
- /* compute number of dai links */
- sof_audio_card_rt5682.num_links = 1 + ctx->dmic_be_num + ctx->hdmi_num;
+ ctx->ssp_amp = (sof_rt5682_quirk & SOF_RT5682_SSP_AMP_MASK) >>
+ SOF_RT5682_SSP_AMP_SHIFT;
- if (ctx->amp_type != CODEC_NONE)
- sof_audio_card_rt5682.num_links++;
+ ctx->ssp_codec = sof_rt5682_quirk & SOF_RT5682_SSP_CODEC_MASK;
if (sof_rt5682_quirk & SOF_SSP_BT_OFFLOAD_PRESENT)
- sof_audio_card_rt5682.num_links++;
-
- if (sof_rt5682_quirk & SOF_SSP_HDMI_CAPTURE_PRESENT_MASK)
- sof_audio_card_rt5682.num_links +=
- hweight32((sof_rt5682_quirk & SOF_SSP_HDMI_CAPTURE_PRESENT_MASK) >>
- SOF_NO_OF_HDMI_CAPTURE_SSP_SHIFT);
-
- dai_links = sof_card_dai_links_create(&pdev->dev, ctx->codec_type,
- ctx->amp_type, ssp_codec, ssp_amp,
- ctx->dmic_be_num, ctx->hdmi_num,
- ctx->hdmi.idisp_codec,
- ctx->rt5682.is_legacy_cpu);
- if (!dai_links)
- return -ENOMEM;
+ ctx->bt_offload_present = true;
- sof_audio_card_rt5682.dai_link = dai_links;
+ /* update dai_link */
+ ret = sof_card_dai_links_create(&pdev->dev, &sof_audio_card_rt5682, ctx);
+ if (ret)
+ return ret;
/* update codec_conf */
switch (ctx->amp_type) {
@@ -1147,6 +989,15 @@ static const struct platform_device_id board_ids[] = {
SOF_RT5682_SSP_AMP(0) |
SOF_RT5682_NUM_HDMIDEV(3)),
},
+ {
+ .name = "mtl_rt5650",
+ .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
+ SOF_RT5682_SSP_CODEC(2) |
+ SOF_RT5682_SSP_AMP(0) |
+ SOF_RT5682_NUM_HDMIDEV(3) |
+ SOF_BT_OFFLOAD_SSP(1) |
+ SOF_SSP_BT_OFFLOAD_PRESENT),
+ },
{ }
};
MODULE_DEVICE_TABLE(platform, board_ids);
diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c
index 4e4284729773..300391fbc2fc 100644
--- a/sound/soc/intel/boards/sof_sdw.c
+++ b/sound/soc/intel/boards/sof_sdw.c
@@ -570,16 +570,14 @@ int sdw_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai_link_ch_map *ch_maps;
int ch = params_channels(params);
- struct snd_soc_dai *codec_dai;
- struct snd_soc_dai *cpu_dai;
unsigned int ch_mask;
int num_codecs;
int step;
int i;
- int j;
- if (!rtd->dai_link->codec_ch_maps)
+ if (!rtd->dai_link->ch_maps)
return 0;
/* Identical data will be sent to all codecs in playback */
@@ -605,13 +603,9 @@ int sdw_hw_params(struct snd_pcm_substream *substream,
* link has more than one codec DAIs. Set codec channel mask and
* ASoC will set the corresponding channel numbers for each cpu dai.
*/
- for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
- for_each_rtd_codec_dais(rtd, j, codec_dai) {
- if (rtd->dai_link->codec_ch_maps[j].connected_cpu_id != i)
- continue;
- rtd->dai_link->codec_ch_maps[j].ch_mask = ch_mask << (j * step);
- }
- }
+ for_each_link_ch_maps(rtd->dai_link, i, ch_maps)
+ ch_maps->ch_mask = ch_mask << (i * step);
+
return 0;
}
@@ -861,6 +855,36 @@ static struct sof_sdw_codec_info codec_info_list[] = {
.dai_num = 1,
},
{
+ .part_id = 0x722,
+ .version_id = 3,
+ .dais = {
+ {
+ .direction = {true, true},
+ .dai_name = "rt722-sdca-aif1",
+ .dai_type = SOF_SDW_DAI_TYPE_JACK,
+ .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID},
+ .init = sof_sdw_rt_sdca_jack_init,
+ .exit = sof_sdw_rt_sdca_jack_exit,
+ },
+ {
+ .direction = {true, false},
+ .dai_name = "rt722-sdca-aif2",
+ .dai_type = SOF_SDW_DAI_TYPE_AMP,
+ /* No feedback capability is provided by rt722-sdca codec driver*/
+ .dailink = {SDW_AMP_OUT_DAI_ID, SDW_UNUSED_DAI_ID},
+ .init = sof_sdw_rt722_spk_init,
+ },
+ {
+ .direction = {false, true},
+ .dai_name = "rt722-sdca-aif3",
+ .dai_type = SOF_SDW_DAI_TYPE_MIC,
+ .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID},
+ .init = sof_sdw_rt722_sdca_dmic_init,
+ },
+ },
+ .dai_num = 3,
+ },
+ {
.part_id = 0x8373,
.dais = {
{
@@ -1232,11 +1256,11 @@ static int fill_sdw_codec_dlc(struct device *dev,
else if (is_unique_device(adr_link, sdw_version, mfg_id, part_id,
class_id, adr_index))
codec->name = devm_kasprintf(dev, GFP_KERNEL,
- "sdw:%01x:%04x:%04x:%02x", link_id,
+ "sdw:0:%01x:%04x:%04x:%02x", link_id,
mfg_id, part_id, class_id);
else
codec->name = devm_kasprintf(dev, GFP_KERNEL,
- "sdw:%01x:%04x:%04x:%02x:%01x", link_id,
+ "sdw:0:%01x:%04x:%04x:%02x:%01x", link_id,
mfg_id, part_id, class_id, unique_id);
if (!codec->name)
@@ -1350,15 +1374,17 @@ static int get_slave_info(const struct snd_soc_acpi_link_adr *adr_link,
return 0;
}
-static void set_dailink_map(struct snd_soc_dai_link_codec_ch_map *sdw_codec_ch_maps,
+static void set_dailink_map(struct snd_soc_dai_link_ch_map *sdw_codec_ch_maps,
int codec_num, int cpu_num)
{
int step;
int i;
step = codec_num / cpu_num;
- for (i = 0; i < codec_num; i++)
- sdw_codec_ch_maps[i].connected_cpu_id = i / step;
+ for (i = 0; i < codec_num; i++) {
+ sdw_codec_ch_maps[i].cpu = i / step;
+ sdw_codec_ch_maps[i].codec = i;
+ }
}
static const char * const type_strings[] = {"SimpleJack", "SmartAmp", "SmartMic"};
@@ -1453,7 +1479,7 @@ static int create_sdw_dailink(struct snd_soc_card *card, int *link_index,
*ignore_pch_dmic = true;
for_each_pcm_streams(stream) {
- struct snd_soc_dai_link_codec_ch_map *sdw_codec_ch_maps;
+ struct snd_soc_dai_link_ch_map *sdw_codec_ch_maps;
char *name, *cpu_name;
int playback, capture;
static const char * const sdw_stream_name[] = {
@@ -1530,7 +1556,7 @@ static int create_sdw_dailink(struct snd_soc_card *card, int *link_index,
dai_links[*link_index].nonatomic = true;
set_dailink_map(sdw_codec_ch_maps, codec_num, cpu_dai_num);
- dai_links[*link_index].codec_ch_maps = sdw_codec_ch_maps;
+ dai_links[*link_index].ch_maps = sdw_codec_ch_maps;
ret = set_codec_init_func(card, adr_link, dai_links + (*link_index)++,
playback, group_id, adr_index, dai_index);
if (ret < 0) {
@@ -1947,7 +1973,7 @@ static int mc_probe(struct platform_device *pdev)
/* Register the card */
ret = devm_snd_soc_register_card(card->dev, card);
if (ret) {
- dev_err(card->dev, "snd_soc_register_card failed %d\n", ret);
+ dev_err_probe(card->dev, ret, "snd_soc_register_card failed %d\n", ret);
mc_dailink_exit_loop(card);
return ret;
}
diff --git a/sound/soc/intel/boards/sof_sdw_common.h b/sound/soc/intel/boards/sof_sdw_common.h
index e6b98523b4e7..f16456945edb 100644
--- a/sound/soc/intel/boards/sof_sdw_common.h
+++ b/sound/soc/intel/boards/sof_sdw_common.h
@@ -138,12 +138,6 @@ int sof_sdw_rt_sdca_jack_init(struct snd_soc_card *card,
int sof_sdw_rt_sdca_jack_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link);
/* RT712-SDCA support */
-int sof_sdw_rt712_sdca_init(struct snd_soc_card *card,
- const struct snd_soc_acpi_link_adr *link,
- struct snd_soc_dai_link *dai_links,
- struct sof_sdw_codec_info *info,
- bool playback);
-int sof_sdw_rt712_sdca_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link);
int sof_sdw_rt712_spk_init(struct snd_soc_card *card,
const struct snd_soc_acpi_link_adr *link,
struct snd_soc_dai_link *dai_links,
@@ -189,6 +183,18 @@ int sof_sdw_rt715_sdca_init(struct snd_soc_card *card,
struct sof_sdw_codec_info *info,
bool playback);
+/* RT722-SDCA support */
+int sof_sdw_rt722_spk_init(struct snd_soc_card *card,
+ const struct snd_soc_acpi_link_adr *link,
+ struct snd_soc_dai_link *dai_links,
+ struct sof_sdw_codec_info *info,
+ bool playback);
+int sof_sdw_rt722_sdca_dmic_init(struct snd_soc_card *card,
+ const struct snd_soc_acpi_link_adr *link,
+ struct snd_soc_dai_link *dai_links,
+ struct sof_sdw_codec_info *info,
+ bool playback);
+
/* MAXIM codec support */
int sof_sdw_maxim_init(struct snd_soc_card *card,
const struct snd_soc_acpi_link_adr *link,
diff --git a/sound/soc/intel/boards/sof_sdw_cs_amp.c b/sound/soc/intel/boards/sof_sdw_cs_amp.c
index 98f6546f484b..f88c01552a92 100644
--- a/sound/soc/intel/boards/sof_sdw_cs_amp.c
+++ b/sound/soc/intel/boards/sof_sdw_cs_amp.c
@@ -9,15 +9,24 @@
#include <linux/errno.h>
#include <sound/soc.h>
#include <sound/soc-acpi.h>
+#include <sound/soc-dai.h>
#include "sof_sdw_common.h"
#define CODEC_NAME_SIZE 8
+static const struct snd_soc_dapm_widget sof_widgets[] = {
+ SND_SOC_DAPM_SPK("Speakers", NULL),
+};
+
static int cs_spk_init(struct snd_soc_pcm_runtime *rtd)
{
const char *dai_name = rtd->dai_link->codecs->dai_name;
struct snd_soc_card *card = rtd->card;
char codec_name[CODEC_NAME_SIZE];
+ char widget_name[16];
+ struct snd_soc_dapm_route route = { "Speakers", NULL, widget_name };
+ struct snd_soc_dai *codec_dai;
+ int i, ret;
snprintf(codec_name, CODEC_NAME_SIZE, "%s", dai_name);
card->components = devm_kasprintf(card->dev, GFP_KERNEL,
@@ -26,17 +35,34 @@ static int cs_spk_init(struct snd_soc_pcm_runtime *rtd)
if (!card->components)
return -ENOMEM;
+ ret = snd_soc_dapm_new_controls(&card->dapm, sof_widgets,
+ ARRAY_SIZE(sof_widgets));
+ if (ret) {
+ dev_err(card->dev, "widgets addition failed: %d\n", ret);
+ return ret;
+ }
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ if (!strstr(codec_dai->name, "cs35l56"))
+ continue;
+
+ snprintf(widget_name, sizeof(widget_name), "%s SPK",
+ codec_dai->component->name_prefix);
+ ret = snd_soc_dapm_add_routes(&card->dapm, &route, 1);
+ if (ret)
+ return ret;
+ }
+
return 0;
}
-
int sof_sdw_cs_amp_init(struct snd_soc_card *card,
const struct snd_soc_acpi_link_adr *link,
struct snd_soc_dai_link *dai_links,
struct sof_sdw_codec_info *info,
bool playback)
{
- /* Count amp number and do init on playback link only. */
+ /* Do init on playback link only. */
if (!playback)
return 0;
diff --git a/sound/soc/intel/boards/sof_sdw_rt722_sdca.c b/sound/soc/intel/boards/sof_sdw_rt722_sdca.c
new file mode 100644
index 000000000000..fe3a2bff95bc
--- /dev/null
+++ b/sound/soc/intel/boards/sof_sdw_rt722_sdca.c
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2023 Intel Corporation
+
+/*
+ * sof_sdw_rt722_sdca - Helpers to handle RT722-SDCA from generic machine driver
+ */
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/control.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-dapm.h>
+#include "sof_sdw_common.h"
+
+static const struct snd_soc_dapm_widget rt722_spk_widgets[] = {
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+};
+
+static const struct snd_soc_dapm_route rt722_spk_map[] = {
+ { "Speaker", NULL, "rt722 SPK" },
+};
+
+static const struct snd_kcontrol_new rt722_spk_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Speaker"),
+};
+
+static int rt722_spk_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ int ret;
+
+ card->components = devm_kasprintf(card->dev, GFP_KERNEL,
+ "%s spk:rt722",
+ card->components);
+ if (!card->components)
+ return -ENOMEM;
+
+ ret = snd_soc_add_card_controls(card, rt722_spk_controls,
+ ARRAY_SIZE(rt722_spk_controls));
+ if (ret) {
+ dev_err(card->dev, "failed to add rt722 spk controls: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dapm_new_controls(&card->dapm, rt722_spk_widgets,
+ ARRAY_SIZE(rt722_spk_widgets));
+ if (ret) {
+ dev_err(card->dev, "failed to add rt722 spk widgets: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dapm_add_routes(&card->dapm, rt722_spk_map, ARRAY_SIZE(rt722_spk_map));
+ if (ret)
+ dev_err(rtd->dev, "failed to add rt722 spk map: %d\n", ret);
+
+ return ret;
+}
+
+int sof_sdw_rt722_spk_init(struct snd_soc_card *card,
+ const struct snd_soc_acpi_link_adr *link,
+ struct snd_soc_dai_link *dai_links,
+ struct sof_sdw_codec_info *info,
+ bool playback)
+{
+ dai_links->init = rt722_spk_init;
+
+ return 0;
+}
+
+static int rt722_sdca_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_component *component = codec_dai->component;
+
+ card->components = devm_kasprintf(card->dev, GFP_KERNEL,
+ "%s mic:%s",
+ card->components, component->name_prefix);
+ if (!card->components)
+ return -ENOMEM;
+
+ return 0;
+}
+
+int sof_sdw_rt722_sdca_dmic_init(struct snd_soc_card *card,
+ const struct snd_soc_acpi_link_adr *link,
+ struct snd_soc_dai_link *dai_links,
+ struct sof_sdw_codec_info *info,
+ bool playback)
+{
+ dai_links->init = rt722_sdca_dmic_rtd_init;
+
+ return 0;
+}
diff --git a/sound/soc/intel/boards/sof_sdw_rt_sdca_jack_common.c b/sound/soc/intel/boards/sof_sdw_rt_sdca_jack_common.c
index 65bbcee88d6d..d9c283829fc7 100644
--- a/sound/soc/intel/boards/sof_sdw_rt_sdca_jack_common.c
+++ b/sound/soc/intel/boards/sof_sdw_rt_sdca_jack_common.c
@@ -63,6 +63,11 @@ static const struct snd_soc_dapm_route rt713_sdca_map[] = {
{ "rt713 MIC2", NULL, "Headset Mic" },
};
+static const struct snd_soc_dapm_route rt722_sdca_map[] = {
+ { "Headphone", NULL, "rt722 HP" },
+ { "rt722 MIC2", NULL, "Headset Mic" },
+};
+
static const struct snd_kcontrol_new rt_sdca_jack_controls[] = {
SOC_DAPM_PIN_SWITCH("Headphone"),
SOC_DAPM_PIN_SWITCH("Headset Mic"),
@@ -117,6 +122,9 @@ static int rt_sdca_jack_rtd_init(struct snd_soc_pcm_runtime *rtd)
} else if (strstr(component->name_prefix, "rt713")) {
ret = snd_soc_dapm_add_routes(&card->dapm, rt713_sdca_map,
ARRAY_SIZE(rt713_sdca_map));
+ } else if (strstr(component->name_prefix, "rt722")) {
+ ret = snd_soc_dapm_add_routes(&card->dapm, rt722_sdca_map,
+ ARRAY_SIZE(rt722_sdca_map));
} else {
dev_err(card->dev, "%s is not supported\n", component->name_prefix);
return -EINVAL;
@@ -168,6 +176,7 @@ int sof_sdw_rt_sdca_jack_exit(struct snd_soc_card *card, struct snd_soc_dai_link
device_remove_software_node(ctx->headset_codec_dev);
put_device(ctx->headset_codec_dev);
+ ctx->headset_codec_dev = NULL;
return 0;
}
@@ -183,10 +192,10 @@ int sof_sdw_rt_sdca_jack_init(struct snd_soc_card *card,
int ret;
/*
- * headset should be initialized once.
- * Do it with dai link for playback.
+ * Jack detection should be only initialized once for headsets since
+ * the playback/capture is sharing the same jack
*/
- if (!playback)
+ if (ctx->headset_codec_dev)
return 0;
sdw_dev = bus_find_device_by_name(&sdw_bus_type, NULL, dai_links->codecs[0].name);
diff --git a/sound/soc/intel/boards/sof_ssp_amp.c b/sound/soc/intel/boards/sof_ssp_amp.c
index 137ba64254bc..ee2e813bf4c0 100644
--- a/sound/soc/intel/boards/sof_ssp_amp.c
+++ b/sound/soc/intel/boards/sof_ssp_amp.c
@@ -27,21 +27,10 @@
#define SOF_AMPLIFIER_SSP_MASK (GENMASK(3, 0))
/* HDMI capture*/
-#define SOF_SSP_HDMI_CAPTURE_PRESENT BIT(4)
-#define SOF_NO_OF_HDMI_CAPTURE_SSP_SHIFT 5
-#define SOF_NO_OF_HDMI_CAPTURE_SSP_MASK (GENMASK(6, 5))
-#define SOF_NO_OF_HDMI_CAPTURE_SSP(quirk) \
- (((quirk) << SOF_NO_OF_HDMI_CAPTURE_SSP_SHIFT) & SOF_NO_OF_HDMI_CAPTURE_SSP_MASK)
-
-#define SOF_HDMI_CAPTURE_1_SSP_SHIFT 7
-#define SOF_HDMI_CAPTURE_1_SSP_MASK (GENMASK(9, 7))
-#define SOF_HDMI_CAPTURE_1_SSP(quirk) \
- (((quirk) << SOF_HDMI_CAPTURE_1_SSP_SHIFT) & SOF_HDMI_CAPTURE_1_SSP_MASK)
-
-#define SOF_HDMI_CAPTURE_2_SSP_SHIFT 10
-#define SOF_HDMI_CAPTURE_2_SSP_MASK (GENMASK(12, 10))
-#define SOF_HDMI_CAPTURE_2_SSP(quirk) \
- (((quirk) << SOF_HDMI_CAPTURE_2_SSP_SHIFT) & SOF_HDMI_CAPTURE_2_SSP_MASK)
+#define SOF_HDMI_CAPTURE_SSP_MASK_SHIFT 4
+#define SOF_HDMI_CAPTURE_SSP_MASK_MASK (GENMASK(9, 4))
+#define SOF_HDMI_CAPTURE_SSP_MASK(quirk) \
+ (((quirk) << SOF_HDMI_CAPTURE_SSP_MASK_SHIFT) & SOF_HDMI_CAPTURE_SSP_MASK_MASK)
/* HDMI playback */
#define SOF_HDMI_PLAYBACK_PRESENT BIT(13)
@@ -82,13 +71,6 @@ static struct snd_soc_card sof_ssp_amp_card = {
.late_probe = sof_card_late_probe,
};
-static struct snd_soc_dai_link_component platform_component[] = {
- {
- /* name might be overridden during probe */
- .name = "0000:00:1f.3"
- }
-};
-
/* BE ID defined in sof-tgl-rt1308-hdmi-ssp.m4 */
#define HDMI_IN_BE_ID 0
#define SPK_BE_ID 2
@@ -98,66 +80,53 @@ static struct snd_soc_dai_link_component platform_component[] = {
static struct snd_soc_dai_link *
sof_card_dai_links_create(struct device *dev, enum sof_ssp_codec amp_type,
- int ssp_codec, int dmic_be_num, int hdmi_num,
+ int ssp_amp, int dmic_be_num, int hdmi_num,
bool idisp_codec)
{
- struct snd_soc_dai_link_component *cpus;
struct snd_soc_dai_link *links;
int i;
int id = 0;
int ret;
bool fixed_be = false;
int be_id;
+ unsigned long ssp_mask_hdmi_in;
links = devm_kcalloc(dev, sof_ssp_amp_card.num_links,
sizeof(struct snd_soc_dai_link), GFP_KERNEL);
- cpus = devm_kcalloc(dev, sof_ssp_amp_card.num_links,
- sizeof(struct snd_soc_dai_link_component), GFP_KERNEL);
- if (!links || !cpus)
+ if (!links)
return NULL;
/* HDMI-In SSP */
- if (sof_ssp_amp_quirk & SOF_SSP_HDMI_CAPTURE_PRESENT) {
- int num_of_hdmi_ssp = (sof_ssp_amp_quirk & SOF_NO_OF_HDMI_CAPTURE_SSP_MASK) >>
- SOF_NO_OF_HDMI_CAPTURE_SSP_SHIFT;
+ ssp_mask_hdmi_in = (sof_ssp_amp_quirk & SOF_HDMI_CAPTURE_SSP_MASK_MASK) >>
+ SOF_HDMI_CAPTURE_SSP_MASK_SHIFT;
+
+ if (ssp_mask_hdmi_in) {
+ int port = 0;
/* the topology supports HDMI-IN uses fixed BE ID for DAI links */
fixed_be = true;
- for (i = 1; i <= num_of_hdmi_ssp; i++) {
- int port = (i == 1 ? (sof_ssp_amp_quirk & SOF_HDMI_CAPTURE_1_SSP_MASK) >>
- SOF_HDMI_CAPTURE_1_SSP_SHIFT :
- (sof_ssp_amp_quirk & SOF_HDMI_CAPTURE_2_SSP_MASK) >>
- SOF_HDMI_CAPTURE_2_SSP_SHIFT);
-
- links[id].cpus = &cpus[id];
- links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
- "SSP%d Pin", port);
- if (!links[id].cpus->dai_name)
- return NULL;
- links[id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-HDMI", port);
- if (!links[id].name)
+ be_id = HDMI_IN_BE_ID;
+ for_each_set_bit(port, &ssp_mask_hdmi_in, 32) {
+ ret = sof_intel_board_set_hdmi_in_link(dev, &links[id],
+ be_id, port);
+ if (ret)
return NULL;
- links[id].id = fixed_be ? (HDMI_IN_BE_ID + i - 1) : id;
- links[id].codecs = &snd_soc_dummy_dlc;
- links[id].num_codecs = 1;
- links[id].platforms = platform_component;
- links[id].num_platforms = ARRAY_SIZE(platform_component);
- links[id].dpcm_capture = 1;
- links[id].no_pcm = 1;
- links[id].num_cpus = 1;
+
id++;
+ be_id++;
}
}
/* codec SSP */
if (amp_type != CODEC_NONE) {
- links[id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_codec);
- if (!links[id].name)
+ be_id = fixed_be ? SPK_BE_ID : id;
+ ret = sof_intel_board_set_ssp_amp_link(dev, &links[id], be_id,
+ amp_type, ssp_amp);
+ if (ret)
return NULL;
- links[id].id = fixed_be ? SPK_BE_ID : id;
-
+ /* codec-specific fields */
switch (amp_type) {
case CODEC_CS35L41:
cs35l41_set_dai_link(&links[id]);
@@ -170,18 +139,6 @@ sof_card_dai_links_create(struct device *dev, enum sof_ssp_codec amp_type,
return NULL;
}
- links[id].platforms = platform_component;
- links[id].num_platforms = ARRAY_SIZE(platform_component);
- links[id].dpcm_playback = 1;
- /* feedback from amplifier or firmware-generated echo reference */
- links[id].dpcm_capture = 1;
- links[id].no_pcm = 1;
- links[id].cpus = &cpus[id];
- links[id].num_cpus = 1;
- links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", ssp_codec);
- if (!links[id].cpus->dai_name)
- return NULL;
-
id++;
}
@@ -224,29 +181,14 @@ sof_card_dai_links_create(struct device *dev, enum sof_ssp_codec amp_type,
int port = (sof_ssp_amp_quirk & SOF_BT_OFFLOAD_SSP_MASK) >>
SOF_BT_OFFLOAD_SSP_SHIFT;
- links[id].id = id;
- links[id].cpus = &cpus[id];
- links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
- "SSP%d Pin", port);
- if (!links[id].cpus->dai_name)
- goto devm_err;
- links[id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-BT", port);
- if (!links[id].name)
- goto devm_err;
- links[id].codecs = &snd_soc_dummy_dlc;
- links[id].num_codecs = 1;
- links[id].platforms = platform_component;
- links[id].num_platforms = ARRAY_SIZE(platform_component);
- links[id].dpcm_playback = 1;
- links[id].dpcm_capture = 1;
- links[id].no_pcm = 1;
- links[id].num_cpus = 1;
+ ret = sof_intel_board_set_bt_link(dev, &links[id], id, port);
+ if (ret)
+ return NULL;
+
id++;
}
return links;
-devm_err:
- return NULL;
}
static int sof_ssp_amp_probe(struct platform_device *pdev)
@@ -254,7 +196,7 @@ static int sof_ssp_amp_probe(struct platform_device *pdev)
struct snd_soc_acpi_mach *mach = pdev->dev.platform_data;
struct snd_soc_dai_link *dai_links;
struct sof_card_private *ctx;
- int ret, ssp_codec;
+ int ret;
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
@@ -270,7 +212,14 @@ static int sof_ssp_amp_probe(struct platform_device *pdev)
else
ctx->dmic_be_num = 0;
- ssp_codec = sof_ssp_amp_quirk & SOF_AMPLIFIER_SSP_MASK;
+ /* port number/mask of peripherals attached to ssp interface */
+ ctx->ssp_mask_hdmi_in = (sof_ssp_amp_quirk & SOF_HDMI_CAPTURE_SSP_MASK_MASK) >>
+ SOF_HDMI_CAPTURE_SSP_MASK_SHIFT;
+
+ ctx->ssp_bt = (sof_ssp_amp_quirk & SOF_BT_OFFLOAD_SSP_MASK) >>
+ SOF_BT_OFFLOAD_SSP_SHIFT;
+
+ ctx->ssp_amp = sof_ssp_amp_quirk & SOF_AMPLIFIER_SSP_MASK;
/* set number of dai links */
sof_ssp_amp_card.num_links = ctx->dmic_be_num;
@@ -278,9 +227,8 @@ static int sof_ssp_amp_probe(struct platform_device *pdev)
if (ctx->amp_type != CODEC_NONE)
sof_ssp_amp_card.num_links++;
- if (sof_ssp_amp_quirk & SOF_SSP_HDMI_CAPTURE_PRESENT)
- sof_ssp_amp_card.num_links += (sof_ssp_amp_quirk & SOF_NO_OF_HDMI_CAPTURE_SSP_MASK) >>
- SOF_NO_OF_HDMI_CAPTURE_SSP_SHIFT;
+ if (ctx->ssp_mask_hdmi_in)
+ sof_ssp_amp_card.num_links += hweight32(ctx->ssp_mask_hdmi_in);
if (sof_ssp_amp_quirk & SOF_HDMI_PLAYBACK_PRESENT) {
ctx->hdmi_num = (sof_ssp_amp_quirk & SOF_NO_OF_HDMI_PLAYBACK_MASK) >>
@@ -297,11 +245,13 @@ static int sof_ssp_amp_probe(struct platform_device *pdev)
ctx->hdmi_num = 0;
}
- if (sof_ssp_amp_quirk & SOF_SSP_BT_OFFLOAD_PRESENT)
+ if (sof_ssp_amp_quirk & SOF_SSP_BT_OFFLOAD_PRESENT) {
+ ctx->bt_offload_present = true;
sof_ssp_amp_card.num_links++;
+ }
dai_links = sof_card_dai_links_create(&pdev->dev, ctx->amp_type,
- ssp_codec, ctx->dmic_be_num,
+ ctx->ssp_amp, ctx->dmic_be_num,
ctx->hdmi_num,
ctx->hdmi.idisp_codec);
if (!dai_links)
@@ -343,10 +293,8 @@ static const struct platform_device_id board_ids[] = {
{
.name = "tgl_rt1308_hdmi_ssp",
.driver_data = (kernel_ulong_t)(SOF_AMPLIFIER_SSP(2) |
- SOF_NO_OF_HDMI_CAPTURE_SSP(2) |
- SOF_HDMI_CAPTURE_1_SSP(1) |
- SOF_HDMI_CAPTURE_2_SSP(5) |
- SOF_SSP_HDMI_CAPTURE_PRESENT),
+ SOF_HDMI_CAPTURE_SSP_MASK(0x22)),
+ /* SSP 1 and SSP 5 are used for HDMI IN */
},
{
.name = "adl_cs35l41",
@@ -358,28 +306,22 @@ static const struct platform_device_id board_ids[] = {
},
{
.name = "adl_lt6911_hdmi_ssp",
- .driver_data = (kernel_ulong_t)(SOF_NO_OF_HDMI_CAPTURE_SSP(2) |
- SOF_HDMI_CAPTURE_1_SSP(0) |
- SOF_HDMI_CAPTURE_2_SSP(2) |
- SOF_SSP_HDMI_CAPTURE_PRESENT |
+ .driver_data = (kernel_ulong_t)(SOF_HDMI_CAPTURE_SSP_MASK(0x5) |
+ /* SSP 0 and SSP 2 are used for HDMI IN */
SOF_NO_OF_HDMI_PLAYBACK(3) |
SOF_HDMI_PLAYBACK_PRESENT),
},
{
.name = "rpl_lt6911_hdmi_ssp",
- .driver_data = (kernel_ulong_t)(SOF_NO_OF_HDMI_CAPTURE_SSP(2) |
- SOF_HDMI_CAPTURE_1_SSP(0) |
- SOF_HDMI_CAPTURE_2_SSP(2) |
- SOF_SSP_HDMI_CAPTURE_PRESENT |
+ .driver_data = (kernel_ulong_t)(SOF_HDMI_CAPTURE_SSP_MASK(0x5) |
+ /* SSP 0 and SSP 2 are used for HDMI IN */
SOF_NO_OF_HDMI_PLAYBACK(3) |
SOF_HDMI_PLAYBACK_PRESENT),
},
{
.name = "mtl_lt6911_hdmi_ssp",
- .driver_data = (kernel_ulong_t)(SOF_NO_OF_HDMI_CAPTURE_SSP(2) |
- SOF_HDMI_CAPTURE_1_SSP(0) |
- SOF_HDMI_CAPTURE_2_SSP(2) |
- SOF_SSP_HDMI_CAPTURE_PRESENT |
+ .driver_data = (kernel_ulong_t)(SOF_HDMI_CAPTURE_SSP_MASK(0x5) |
+ /* SSP 0 and SSP 2 are used for HDMI IN */
SOF_NO_OF_HDMI_PLAYBACK(3) |
SOF_HDMI_PLAYBACK_PRESENT),
},
diff --git a/sound/soc/intel/boards/sof_ssp_common.c b/sound/soc/intel/boards/sof_ssp_common.c
index 41a258e45a61..96072790e9c0 100644
--- a/sound/soc/intel/boards/sof_ssp_common.c
+++ b/sound/soc/intel/boards/sof_ssp_common.c
@@ -96,6 +96,27 @@ enum sof_ssp_codec sof_ssp_detect_amp_type(struct device *dev)
}
EXPORT_SYMBOL_NS(sof_ssp_detect_amp_type, SND_SOC_INTEL_SOF_SSP_COMMON);
+const char *sof_ssp_get_codec_name(enum sof_ssp_codec codec_type)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(codecs); i++) {
+ if (codecs[i].codec_type != codec_type)
+ continue;
+
+ return codecs[i].name;
+ }
+ for (i = 0; i < ARRAY_SIZE(amps); i++) {
+ if (amps[i].codec_type != codec_type)
+ continue;
+
+ return amps[i].name;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_NS(sof_ssp_get_codec_name, SND_SOC_INTEL_SOF_SSP_COMMON);
+
MODULE_DESCRIPTION("ASoC Intel SOF Common Machine Driver Helpers");
MODULE_AUTHOR("Brent Lu <brent.lu@intel.com>");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/intel/boards/sof_ssp_common.h b/sound/soc/intel/boards/sof_ssp_common.h
index e3fd6fb1db1c..6d827103479b 100644
--- a/sound/soc/intel/boards/sof_ssp_common.h
+++ b/sound/soc/intel/boards/sof_ssp_common.h
@@ -67,5 +67,6 @@ enum sof_ssp_codec {
enum sof_ssp_codec sof_ssp_detect_codec_type(struct device *dev);
enum sof_ssp_codec sof_ssp_detect_amp_type(struct device *dev);
+const char *sof_ssp_get_codec_name(enum sof_ssp_codec codec_type);
#endif /* __SOF_SSP_COMMON_H */
diff --git a/sound/soc/intel/common/soc-acpi-intel-adl-match.c b/sound/soc/intel/common/soc-acpi-intel-adl-match.c
index 6e712ad954c8..d3d913458c60 100644
--- a/sound/soc/intel/common/soc-acpi-intel-adl-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-adl-match.c
@@ -528,14 +528,14 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = {
},
{
.id = "10508825",
- .drv_name = "adl_max98373_8825",
+ .drv_name = "adl_nau8825_def",
.machine_quirk = snd_soc_acpi_codec_list,
.quirk_data = &adl_max98373_amp,
.sof_tplg_filename = "sof-adl-max98373-nau8825.tplg",
},
{
.id = "10508825",
- .drv_name = "adl_mx98360a_8825",
+ .drv_name = "adl_nau8825_def",
.machine_quirk = snd_soc_acpi_codec_list,
.quirk_data = &adl_max98360a_amp,
.sof_tplg_filename = "sof-adl-max98360a-nau8825.tplg",
@@ -549,14 +549,14 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = {
},
{
.id = "10508825",
- .drv_name = "adl_rt1015p_8825",
+ .drv_name = "adl_nau8825_def",
.machine_quirk = snd_soc_acpi_codec_list,
.quirk_data = &adl_rt1015p_amp,
.sof_tplg_filename = "sof-adl-rt1015-nau8825.tplg",
},
{
.id = "10508825",
- .drv_name = "adl_nau8318_8825",
+ .drv_name = "adl_nau8825_def",
.machine_quirk = snd_soc_acpi_codec_list,
.quirk_data = &adl_nau8318_amp,
.sof_tplg_filename = "sof-adl-nau8318-nau8825.tplg",
diff --git a/sound/soc/intel/common/soc-acpi-intel-glk-match.c b/sound/soc/intel/common/soc-acpi-intel-glk-match.c
index 387e73100884..8911c90bbaf6 100644
--- a/sound/soc/intel/common/soc-acpi-intel-glk-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-glk-match.c
@@ -19,6 +19,11 @@ static const struct snd_soc_acpi_codecs glk_codecs = {
.codecs = {"MX98357A"}
};
+static const struct snd_soc_acpi_codecs glk_rt5682_rt5682s_hp = {
+ .num_codecs = 2,
+ .codecs = {"10EC5682", "RTL5682"},
+};
+
struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[] = {
{
.id = "INT343A",
@@ -35,7 +40,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[] = {
.sof_tplg_filename = "sof-glk-da7219.tplg",
},
{
- .id = "10EC5682",
+ .comp_ids = &glk_rt5682_rt5682s_hp,
.drv_name = "glk_rt5682_mx98357a",
.fw_filename = "intel/dsp_fw_glk.bin",
.machine_quirk = snd_soc_acpi_codec_list,
@@ -43,13 +48,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[] = {
.sof_tplg_filename = "sof-glk-rt5682.tplg",
},
{
- .id = "RTL5682",
- .drv_name = "glk_rt5682_max98357a",
- .machine_quirk = snd_soc_acpi_codec_list,
- .quirk_data = &glk_codecs,
- .sof_tplg_filename = "sof-glk-rt5682.tplg",
- },
- {
.id = "10134242",
.drv_name = "glk_cs4242_mx98357a",
.fw_filename = "intel/dsp_fw_glk.bin",
diff --git a/sound/soc/intel/common/soc-acpi-intel-lnl-match.c b/sound/soc/intel/common/soc-acpi-intel-lnl-match.c
index 9f35b77deb11..5897bb6b28b8 100644
--- a/sound/soc/intel/common/soc-acpi-intel-lnl-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-lnl-match.c
@@ -22,6 +22,20 @@ static const struct snd_soc_acpi_endpoint single_endpoint = {
.group_id = 0,
};
+static const struct snd_soc_acpi_endpoint spk_l_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 0,
+ .group_id = 1,
+};
+
+static const struct snd_soc_acpi_endpoint spk_r_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 1,
+ .group_id = 1,
+};
+
static const struct snd_soc_acpi_adr_device rt711_sdca_0_adr[] = {
{
.adr = 0x000030025D071101ull,
@@ -31,6 +45,33 @@ static const struct snd_soc_acpi_adr_device rt711_sdca_0_adr[] = {
}
};
+static const struct snd_soc_acpi_adr_device rt1316_2_group1_adr[] = {
+ {
+ .adr = 0x000230025D131601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "rt1316-1"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1316_3_group1_adr[] = {
+ {
+ .adr = 0x000331025D131601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "rt1316-2"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt714_1_adr[] = {
+ {
+ .adr = 0x000130025D071401ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt714"
+ }
+};
+
static const struct snd_soc_acpi_link_adr lnl_rvp[] = {
{
.mask = BIT(0),
@@ -40,6 +81,30 @@ static const struct snd_soc_acpi_link_adr lnl_rvp[] = {
{}
};
+static const struct snd_soc_acpi_link_adr lnl_3_in_1_sdca[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_sdca_0_adr),
+ .adr_d = rt711_sdca_0_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt1316_2_group1_adr),
+ .adr_d = rt1316_2_group1_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt1316_3_group1_adr),
+ .adr_d = rt1316_3_group1_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt714_1_adr),
+ .adr_d = rt714_1_adr,
+ },
+ {}
+};
+
/* this table is used when there is no I2S codec present */
struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = {
/* mockup tests need to be first */
@@ -62,6 +127,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = {
.sof_tplg_filename = "sof-lnl-rt715-rt711-rt1308-mono.tplg",
},
{
+ .link_mask = GENMASK(3, 0),
+ .links = lnl_3_in_1_sdca,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-lnl-rt711-l0-rt1316-l23-rt714-l1.tplg",
+ },
+ {
.link_mask = BIT(0),
.links = lnl_rvp,
.drv_name = "sof_sdw",
diff --git a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c
index 9008b6768205..feb12c6c85d1 100644
--- a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c
@@ -40,6 +40,11 @@ static const struct snd_soc_acpi_codecs mtl_lt6911_hdmi = {
.codecs = {"INTC10B0"}
};
+static const struct snd_soc_acpi_codecs mtl_rt5650_amp = {
+ .num_codecs = 1,
+ .codecs = {"10EC5650"}
+};
+
struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_machines[] = {
{
.comp_ids = &mtl_rt5682_rt5682s_hp,
@@ -77,6 +82,13 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_machines[] = {
SND_SOC_ACPI_TPLG_INTEL_SSP_MSB |
SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER,
},
+ {
+ .id = "10EC5650",
+ .drv_name = "mtl_rt5650",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &mtl_rt5650_amp,
+ .sof_tplg_filename = "sof-mtl-rt5650.tplg",
+ },
/* place amp-only boards in the end of table */
{
.id = "INTC10B0",
@@ -123,6 +135,31 @@ static const struct snd_soc_acpi_endpoint rt712_endpoints[] = {
},
};
+/*
+ * RT722 is a multi-function codec, three endpoints are created for
+ * its headset, amp and dmic functions.
+ */
+static const struct snd_soc_acpi_endpoint rt722_endpoints[] = {
+ {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ {
+ .num = 1,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ {
+ .num = 2,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+};
+
static const struct snd_soc_acpi_endpoint spk_2_endpoint = {
.num = 0,
.aggregated = 1,
@@ -164,6 +201,15 @@ static const struct snd_soc_acpi_adr_device rt1712_3_single_adr[] = {
}
};
+static const struct snd_soc_acpi_adr_device rt722_0_single_adr[] = {
+ {
+ .adr = 0x000030025d072201ull,
+ .num_endpoints = ARRAY_SIZE(rt722_endpoints),
+ .endpoints = rt722_endpoints,
+ .name_prefix = "rt722"
+ }
+};
+
static const struct snd_soc_acpi_adr_device rt713_0_single_adr[] = {
{
.adr = 0x000031025D071301ull,
@@ -355,6 +401,15 @@ static const struct snd_soc_acpi_link_adr mtl_rvp[] = {
{}
};
+static const struct snd_soc_acpi_link_adr mtl_rt722_only[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt722_0_single_adr),
+ .adr_d = rt722_0_single_adr,
+ },
+ {}
+};
+
static const struct snd_soc_acpi_link_adr mtl_3_in_1_sdca[] = {
{
.mask = BIT(0),
@@ -422,6 +477,25 @@ static const struct snd_soc_acpi_link_adr mtl_rt713_l0_rt1316_l12_rt1713_l3[] =
{}
};
+static const struct snd_soc_acpi_link_adr mtl_rt713_l0_rt1316_l12[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt713_0_single_adr),
+ .adr_d = rt713_0_single_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1316_1_group2_adr),
+ .adr_d = rt1316_1_group2_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt1316_2_group2_adr),
+ .adr_d = rt1316_2_group2_adr,
+ },
+ {}
+};
+
static const struct snd_soc_acpi_adr_device mx8363_2_adr[] = {
{
.adr = 0x000230019F836300ull,
@@ -508,6 +582,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = {
.sof_tplg_filename = "sof-mtl-rt713-l0-rt1316-l12-rt1713-l3.tplg",
},
{
+ .link_mask = GENMASK(2, 0),
+ .links = mtl_rt713_l0_rt1316_l12,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-mtl-rt713-l0-rt1316-l12.tplg",
+ },
+ {
.link_mask = BIT(3) | BIT(0),
.links = mtl_712_only,
.drv_name = "sof_sdw",
@@ -533,6 +613,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = {
},
{
.link_mask = BIT(0),
+ .links = mtl_rt722_only,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-mtl-rt722-l0.tplg",
+ },
+ {
+ .link_mask = BIT(0),
.links = mtl_rvp,
.drv_name = "sof_sdw",
.sof_tplg_filename = "sof-mtl-rt711.tplg",
diff --git a/sound/soc/intel/common/soc-acpi-intel-rpl-match.c b/sound/soc/intel/common/soc-acpi-intel-rpl-match.c
index 5b6f57e3a583..c0a643f4725a 100644
--- a/sound/soc/intel/common/soc-acpi-intel-rpl-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-rpl-match.c
@@ -402,21 +402,21 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_rpl_machines[] = {
},
{
.id = "10508825",
- .drv_name = "rpl_max98373_8825",
+ .drv_name = "rpl_nau8825_def",
.machine_quirk = snd_soc_acpi_codec_list,
.quirk_data = &rpl_max98373_amp,
.sof_tplg_filename = "sof-rpl-max98373-nau8825.tplg",
},
{
.id = "10508825",
- .drv_name = "rpl_mx98360a_8825",
+ .drv_name = "rpl_nau8825_def",
.machine_quirk = snd_soc_acpi_codec_list,
.quirk_data = &rpl_max98360a_amp,
.sof_tplg_filename = "sof-rpl-max98360a-nau8825.tplg",
},
{
.id = "10508825",
- .drv_name = "rpl_nau8318_8825",
+ .drv_name = "rpl_nau8825_def",
.machine_quirk = snd_soc_acpi_codec_list,
.quirk_data = &rpl_nau8318_amp,
.sof_tplg_filename = "sof-rpl-nau8318-nau8825.tplg",
diff --git a/sound/soc/intel/common/soc-acpi-intel-tgl-match.c b/sound/soc/intel/common/soc-acpi-intel-tgl-match.c
index 5804926c8b56..e5f721ba5ed4 100644
--- a/sound/soc/intel/common/soc-acpi-intel-tgl-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-tgl-match.c
@@ -41,6 +41,20 @@ static const struct snd_soc_acpi_endpoint spk_r_endpoint = {
.group_id = 1,
};
+static const struct snd_soc_acpi_endpoint spk_2_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 2,
+ .group_id = 1,
+};
+
+static const struct snd_soc_acpi_endpoint spk_3_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 3,
+ .group_id = 1,
+};
+
static const struct snd_soc_acpi_endpoint rt712_endpoints[] = {
{
.num = 0,
@@ -400,6 +414,64 @@ static const struct snd_soc_acpi_link_adr tgl_712_only[] = {
{}
};
+static const struct snd_soc_acpi_adr_device cs42l43_3_adr[] = {
+ {
+ .adr = 0x00033001FA424301ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "cs42l43"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device cs35l56_0_adr[] = {
+ {
+ .adr = 0x00003301FA355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "AMP1"
+ },
+ {
+ .adr = 0x00003201FA355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_3_endpoint,
+ .name_prefix = "AMP2"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device cs35l56_1_adr[] = {
+ {
+ .adr = 0x00013701FA355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "AMP8"
+ },
+ {
+ .adr = 0x00013601FA355601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_2_endpoint,
+ .name_prefix = "AMP7"
+ }
+};
+
+static const struct snd_soc_acpi_link_adr tgl_cs42l43_cs35l56[] = {
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(cs42l43_3_adr),
+ .adr_d = cs42l43_3_adr,
+ },
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(cs35l56_0_adr),
+ .adr_d = cs35l56_0_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(cs35l56_1_adr),
+ .adr_d = cs35l56_1_adr,
+ },
+ {}
+};
+
static const struct snd_soc_acpi_codecs tgl_max98373_amp = {
.num_codecs = 1,
.codecs = {"MX98373"}
@@ -495,6 +567,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_sdw_machines[] = {
.sof_tplg_filename = "sof-tgl-rt715-rt711-rt1308-mono.tplg",
},
{
+ .link_mask = 0xB,
+ .links = tgl_cs42l43_cs35l56,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-tgl-cs42l43-l3-cs35l56-l01.tplg",
+ },
+ {
.link_mask = 0xF, /* 4 active links required */
.links = tgl_3_in_1_default,
.drv_name = "sof_sdw",
diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c
index 174aae6e0398..613b27b8da13 100644
--- a/sound/soc/intel/skylake/skl-pcm.c
+++ b/sound/soc/intel/skylake/skl-pcm.c
@@ -127,6 +127,7 @@ int skl_pcm_host_dma_prepare(struct device *dev, struct skl_pipe_params *params)
unsigned int format_val;
struct hdac_stream *hstream;
struct hdac_ext_stream *stream;
+ unsigned int bits;
int err;
hstream = snd_hdac_get_stream(bus, params->stream,
@@ -137,8 +138,9 @@ int skl_pcm_host_dma_prepare(struct device *dev, struct skl_pipe_params *params)
stream = stream_to_hdac_ext_stream(hstream);
snd_hdac_ext_stream_decouple(bus, stream, true);
- format_val = snd_hdac_calc_stream_format(params->s_freq,
- params->ch, params->format, params->host_bps, 0);
+ bits = snd_hdac_stream_format_bits(params->format, SNDRV_PCM_SUBFORMAT_STD,
+ params->host_bps);
+ format_val = snd_hdac_stream_format(params->ch, bits, params->s_freq);
dev_dbg(dev, "format_val=%d, rate=%d, ch=%d, format=%d\n",
format_val, params->s_freq, params->ch, params->format);
@@ -165,6 +167,7 @@ int skl_pcm_link_dma_prepare(struct device *dev, struct skl_pipe_params *params)
struct hdac_ext_stream *stream;
struct hdac_ext_link *link;
unsigned char stream_tag;
+ unsigned int bits;
hstream = snd_hdac_get_stream(bus, params->stream,
params->link_dma_id + 1);
@@ -173,8 +176,10 @@ int skl_pcm_link_dma_prepare(struct device *dev, struct skl_pipe_params *params)
stream = stream_to_hdac_ext_stream(hstream);
snd_hdac_ext_stream_decouple(bus, stream, true);
- format_val = snd_hdac_calc_stream_format(params->s_freq, params->ch,
- params->format, params->link_bps, 0);
+
+ bits = snd_hdac_stream_format_bits(params->format, SNDRV_PCM_SUBFORMAT_STD,
+ params->link_bps);
+ format_val = snd_hdac_stream_format(params->ch, bits, params->s_freq);
dev_dbg(dev, "format_val=%d, rate=%d, ch=%d, format=%d\n",
format_val, params->s_freq, params->ch, params->format);
diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig
index b93d455744ab..296b434caf81 100644
--- a/sound/soc/mediatek/Kconfig
+++ b/sound/soc/mediatek/Kconfig
@@ -252,6 +252,7 @@ config SND_SOC_MT8188_MT6359
select SND_SOC_NAU8315
select SND_SOC_NAU8825
select SND_SOC_RT5682S
+ select SND_SOC_ES8326
help
This adds support for ASoC machine driver for MediaTek MT8188
boards with the MT6359 and other I2S audio codecs.
diff --git a/sound/soc/mediatek/common/mtk-dsp-sof-common.c b/sound/soc/mediatek/common/mtk-dsp-sof-common.c
index f3894010f656..7ec8965a70c0 100644
--- a/sound/soc/mediatek/common/mtk-dsp-sof-common.c
+++ b/sound/soc/mediatek/common/mtk-dsp-sof-common.c
@@ -24,7 +24,7 @@ int mtk_sof_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_soc_dai_link *sof_dai_link = NULL;
const struct sof_conn_stream *conn = &sof_priv->conn_streams[i];
- if (strcmp(rtd->dai_link->name, conn->normal_link))
+ if (conn->normal_link && strcmp(rtd->dai_link->name, conn->normal_link))
continue;
for_each_card_rtds(card, runtime) {
diff --git a/sound/soc/mediatek/mt7986/mt7986-wm8960.c b/sound/soc/mediatek/mt7986/mt7986-wm8960.c
index c1390b373410..6982e833421d 100644
--- a/sound/soc/mediatek/mt7986/mt7986-wm8960.c
+++ b/sound/soc/mediatek/mt7986/mt7986-wm8960.c
@@ -144,7 +144,7 @@ static int mt7986_wm8960_machine_probe(struct platform_device *pdev)
ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret) {
- dev_err(&pdev->dev, "%s snd_soc_register_card fail: %d\n", __func__, ret);
+ dev_err_probe(&pdev->dev, ret, "%s snd_soc_register_card fail\n", __func__);
goto err_of_node_put;
}
diff --git a/sound/soc/mediatek/mt8188/mt8188-mt6359.c b/sound/soc/mediatek/mt8188/mt8188-mt6359.c
index 33d477cc2e54..a391066ab204 100644
--- a/sound/soc/mediatek/mt8188/mt8188-mt6359.c
+++ b/sound/soc/mediatek/mt8188/mt8188-mt6359.c
@@ -34,6 +34,8 @@
#define NAU8825_HS_PRESENT BIT(0)
#define RT5682S_HS_PRESENT BIT(1)
+#define ES8326_HS_PRESENT BIT(2)
+#define MAX98390_TWO_AMP BIT(3)
/*
* Maxim MAX98390
*/
@@ -48,6 +50,11 @@
*/
#define NAU8825_CODEC_DAI "nau8825-hifi"
+/*
+ * ES8326
+ */
+#define ES8326_CODEC_DAI "ES8326 HiFi"
+
#define SOF_DMA_DL2 "SOF_DMA_DL2"
#define SOF_DMA_DL3 "SOF_DMA_DL3"
#define SOF_DMA_UL4 "SOF_DMA_UL4"
@@ -726,7 +733,7 @@ static int mt8188_max98390_codec_init(struct snd_soc_pcm_runtime *rtd)
return 0;
}
-static int mt8188_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd)
+static int mt8188_headset_codec_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_card *card = rtd->card;
struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(card);
@@ -775,68 +782,13 @@ static int mt8188_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd)
return 0;
};
-static int mt8188_rt5682s_codec_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_card *card = rtd->card;
- struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(card);
- struct mt8188_mt6359_priv *priv = soc_card_data->mach_priv;
- struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
- struct snd_soc_jack *jack = &priv->headset_jack;
- int ret;
-
- ret = snd_soc_dapm_new_controls(&card->dapm, mt8188_nau8825_widgets,
- ARRAY_SIZE(mt8188_nau8825_widgets));
- if (ret) {
- dev_err(rtd->dev, "unable to add rt5682s card widget, ret %d\n", ret);
- return ret;
- }
-
- ret = snd_soc_add_card_controls(card, mt8188_nau8825_controls,
- ARRAY_SIZE(mt8188_nau8825_controls));
- if (ret) {
- dev_err(rtd->dev, "unable to add rt5682s card controls, ret %d\n", ret);
- return ret;
- }
-
- ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
- SND_JACK_HEADSET | SND_JACK_BTN_0 |
- SND_JACK_BTN_1 | SND_JACK_BTN_2 |
- SND_JACK_BTN_3,
- jack,
- nau8825_jack_pins,
- ARRAY_SIZE(nau8825_jack_pins));
- if (ret) {
- dev_err(rtd->dev, "Headset Jack creation failed: %d\n", 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);
- ret = snd_soc_component_set_jack(component, jack, NULL);
-
- if (ret) {
- dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret);
- return ret;
- }
-
- return 0;
-};
-
-static void mt8188_nau8825_codec_exit(struct snd_soc_pcm_runtime *rtd)
+static void mt8188_headset_codec_exit(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
snd_soc_component_set_jack(component, NULL, NULL);
}
-static void mt8188_rt5682s_codec_exit(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
-
- snd_soc_component_set_jack(component, NULL, NULL);
-}
static int mt8188_nau8825_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
@@ -943,6 +895,30 @@ static const struct snd_soc_ops mt8188_sof_be_ops = {
.hw_params = mt8188_sof_be_hw_params,
};
+static int mt8188_es8326_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ unsigned int rate = params_rate(params);
+ int ret;
+
+ /* Configure MCLK for codec */
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, rate * 256, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "can't set MCLK %d\n", ret);
+ return ret;
+ }
+
+ /* Configure MCLK for cpu */
+ return snd_soc_dai_set_sysclk(cpu_dai, 0, rate * 256, SND_SOC_CLOCK_OUT);
+}
+
+static const struct snd_soc_ops mt8188_es8326_ops = {
+ .hw_params = mt8188_es8326_hw_params,
+};
+
static struct snd_soc_dai_link mt8188_mt6359_dai_links[] = {
/* FE */
[DAI_LINK_DL2_FE] = {
@@ -1252,7 +1228,7 @@ static void mt8188_fixup_controls(struct snd_soc_card *card)
struct mt8188_card_data *card_data = (struct mt8188_card_data *)priv->private_data;
struct snd_kcontrol *kctl;
- if (card_data->quirk & (NAU8825_HS_PRESENT | RT5682S_HS_PRESENT)) {
+ if (card_data->quirk & (NAU8825_HS_PRESENT | RT5682S_HS_PRESENT | ES8326_HS_PRESENT)) {
struct snd_soc_dapm_widget *w, *next_w;
for_each_card_widgets_safe(card, w, next_w) {
@@ -1293,6 +1269,7 @@ static int mt8188_mt6359_dev_probe(struct platform_device *pdev)
struct mt8188_card_data *card_data;
struct snd_soc_dai_link *dai_link;
bool init_mt6359 = false;
+ bool init_es8326 = false;
bool init_nau8825 = false;
bool init_rt5682s = false;
bool init_max98390 = false;
@@ -1399,7 +1376,14 @@ static int mt8188_mt6359_dev_probe(struct platform_device *pdev)
strcmp(dai_link->name, "ETDM1_IN_BE") == 0 ||
strcmp(dai_link->name, "ETDM2_IN_BE") == 0) {
if (!strcmp(dai_link->codecs->dai_name, MAX98390_CODEC_DAI)) {
- dai_link->ops = &mt8188_max98390_ops;
+ /*
+ * The TDM protocol settings with fixed 4 slots are defined in
+ * mt8188_max98390_ops. Two amps is I2S mode,
+ * SOC and codec don't require TDM settings.
+ */
+ if (!(card_data->quirk & MAX98390_TWO_AMP)) {
+ dai_link->ops = &mt8188_max98390_ops;
+ }
if (!init_max98390) {
dai_link->init = mt8188_max98390_codec_init;
init_max98390 = true;
@@ -1407,17 +1391,24 @@ static int mt8188_mt6359_dev_probe(struct platform_device *pdev)
} else if (!strcmp(dai_link->codecs->dai_name, NAU8825_CODEC_DAI)) {
dai_link->ops = &mt8188_nau8825_ops;
if (!init_nau8825) {
- dai_link->init = mt8188_nau8825_codec_init;
- dai_link->exit = mt8188_nau8825_codec_exit;
+ dai_link->init = mt8188_headset_codec_init;
+ dai_link->exit = mt8188_headset_codec_exit;
init_nau8825 = true;
}
} else if (!strcmp(dai_link->codecs->dai_name, RT5682S_CODEC_DAI)) {
dai_link->ops = &mt8188_rt5682s_i2s_ops;
if (!init_rt5682s) {
- dai_link->init = mt8188_rt5682s_codec_init;
- dai_link->exit = mt8188_rt5682s_codec_exit;
+ dai_link->init = mt8188_headset_codec_init;
+ dai_link->exit = mt8188_headset_codec_exit;
init_rt5682s = true;
}
+ } else if (!strcmp(dai_link->codecs->dai_name, ES8326_CODEC_DAI)) {
+ dai_link->ops = &mt8188_es8326_ops;
+ if (!init_es8326) {
+ dai_link->init = mt8188_headset_codec_init;
+ dai_link->exit = mt8188_headset_codec_exit;
+ init_es8326 = true;
+ }
} else {
if (strcmp(dai_link->codecs->dai_name, "snd-soc-dummy-dai")) {
if (!init_dumb) {
@@ -1457,13 +1448,19 @@ static struct mt8188_card_data mt8188_nau8825_card = {
static struct mt8188_card_data mt8188_rt5682s_card = {
.name = "mt8188_rt5682s",
- .quirk = RT5682S_HS_PRESENT,
+ .quirk = RT5682S_HS_PRESENT | MAX98390_TWO_AMP,
+};
+
+static struct mt8188_card_data mt8188_es8326_card = {
+ .name = "mt8188_es8326",
+ .quirk = ES8326_HS_PRESENT | MAX98390_TWO_AMP,
};
static const struct of_device_id mt8188_mt6359_dt_match[] = {
{ .compatible = "mediatek,mt8188-mt6359-evb", .data = &mt8188_evb_card, },
{ .compatible = "mediatek,mt8188-nau8825", .data = &mt8188_nau8825_card, },
{ .compatible = "mediatek,mt8188-rt5682s", .data = &mt8188_rt5682s_card, },
+ { .compatible = "mediatek,mt8188-es8326", .data = &mt8188_es8326_card, },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, mt8188_mt6359_dt_match);
diff --git a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c
index 5bd6addd1450..bfcb2c486c39 100644
--- a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c
+++ b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c
@@ -1208,7 +1208,8 @@ static int mt8192_mt6359_dev_probe(struct platform_device *pdev)
dai_link->ignore = 0;
}
- if (strcmp(dai_link->codecs[0].dai_name, RT1015_CODEC_DAI) == 0)
+ if (dai_link->num_codecs && dai_link->codecs[0].dai_name &&
+ strcmp(dai_link->codecs[0].dai_name, RT1015_CODEC_DAI) == 0)
dai_link->ops = &mt8192_rt1015_i2s_ops;
if (!dai_link->platforms->name)
diff --git a/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c b/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c
index 1e33863c85ca..620d7ade1992 100644
--- a/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c
+++ b/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c
@@ -1795,10 +1795,6 @@ static const struct snd_kcontrol_new mt8195_memif_controls[] = {
MT8195_AFE_IRQ_28),
};
-static const struct snd_soc_component_driver mt8195_afe_pcm_dai_component = {
- .name = "mt8195-afe-pcm-dai",
-};
-
static const struct mtk_base_memif_data memif_data[MT8195_AFE_MEMIF_NUM] = {
[MT8195_AFE_MEMIF_DL2] = {
.name = "DL2",
@@ -3037,7 +3033,6 @@ static int mt8195_afe_pcm_dev_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct reset_control *rstc;
int i, irq_id, ret;
- struct snd_soc_component *component;
ret = of_reserved_mem_device_init(dev);
if (ret)
@@ -3170,36 +3165,12 @@ static int mt8195_afe_pcm_dev_probe(struct platform_device *pdev)
/* register component */
ret = devm_snd_soc_register_component(dev, &mt8195_afe_component,
- NULL, 0);
+ afe->dai_drivers, afe->num_dai_drivers);
if (ret) {
dev_warn(dev, "err_platform\n");
goto err_pm_put;
}
- component = devm_kzalloc(dev, sizeof(*component), GFP_KERNEL);
- if (!component) {
- ret = -ENOMEM;
- goto err_pm_put;
- }
-
- ret = snd_soc_component_initialize(component,
- &mt8195_afe_pcm_dai_component,
- dev);
- if (ret)
- goto err_pm_put;
-
-#ifdef CONFIG_DEBUG_FS
- component->debugfs_prefix = "pcm";
-#endif
-
- ret = snd_soc_add_component(component,
- afe->dai_drivers,
- afe->num_dai_drivers);
- if (ret) {
- dev_warn(dev, "err_dai_component\n");
- goto err_pm_put;
- }
-
ret = regmap_multi_reg_write(afe->regmap, mt8195_afe_reg_defaults,
ARRAY_SIZE(mt8195_afe_reg_defaults));
if (ret)
@@ -3224,8 +3195,6 @@ err_pm_put:
static void mt8195_afe_pcm_dev_remove(struct platform_device *pdev)
{
- snd_soc_unregister_component(&pdev->dev);
-
pm_runtime_disable(&pdev->dev);
if (!pm_runtime_status_suspended(&pdev->dev))
mt8195_afe_runtime_suspend(&pdev->dev);
diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359.c b/sound/soc/mediatek/mt8195/mt8195-mt6359.c
index 4feb9fb76967..53fd8a897b9d 100644
--- a/sound/soc/mediatek/mt8195/mt8195-mt6359.c
+++ b/sound/soc/mediatek/mt8195/mt8195-mt6359.c
@@ -934,12 +934,11 @@ SND_SOC_DAILINK_DEFS(ETDM1_IN_BE,
SND_SOC_DAILINK_DEFS(ETDM2_IN_BE,
DAILINK_COMP_ARRAY(COMP_CPU("ETDM2_IN")),
- DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
SND_SOC_DAILINK_DEFS(ETDM1_OUT_BE,
DAILINK_COMP_ARRAY(COMP_CPU("ETDM1_OUT")),
- DAILINK_COMP_ARRAY(COMP_DUMMY()),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
SND_SOC_DAILINK_DEFS(ETDM2_OUT_BE,
@@ -1237,8 +1236,6 @@ static struct snd_soc_dai_link mt8195_mt6359_dai_links[] = {
SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS,
.dpcm_capture = 1,
- .init = mt8195_rt5682_init,
- .ops = &mt8195_rt5682_etdm_ops,
.be_hw_params_fixup = mt8195_etdm_hw_params_fixup,
SND_SOC_DAILINK_REG(ETDM2_IN_BE),
},
@@ -1249,7 +1246,6 @@ static struct snd_soc_dai_link mt8195_mt6359_dai_links[] = {
SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS,
.dpcm_playback = 1,
- .ops = &mt8195_rt5682_etdm_ops,
.be_hw_params_fixup = mt8195_etdm_hw_params_fixup,
SND_SOC_DAILINK_REG(ETDM1_OUT_BE),
},
@@ -1381,7 +1377,7 @@ static int mt8195_mt6359_dev_probe(struct platform_device *pdev)
struct snd_soc_dai_link *dai_link;
struct mtk_soc_card_data *soc_card_data;
struct mt8195_mt6359_priv *mach_priv;
- struct device_node *platform_node, *adsp_node, *dp_node, *hdmi_node;
+ struct device_node *platform_node, *adsp_node, *codec_node, *dp_node, *hdmi_node;
struct mt8195_card_data *card_data;
int is5682s = 0;
int init6359 = 0;
@@ -1401,8 +1397,12 @@ static int mt8195_mt6359_dev_probe(struct platform_device *pdev)
if (!card->name)
card->name = card_data->name;
- if (strstr(card->name, "_5682s"))
+ if (strstr(card->name, "_5682s")) {
+ codec_node = of_find_compatible_node(NULL, NULL, "realtek,rt5682s");
is5682s = 1;
+ } else
+ codec_node = of_find_compatible_node(NULL, NULL, "realtek,rt5682i");
+
soc_card_data = devm_kzalloc(&pdev->dev, sizeof(*card_data), GFP_KERNEL);
if (!soc_card_data)
return -ENOMEM;
@@ -1488,12 +1488,27 @@ static int mt8195_mt6359_dev_probe(struct platform_device *pdev)
dai_link->codecs->dai_name = "i2s-hifi";
dai_link->init = mt8195_hdmi_codec_init;
}
- } else if (strcmp(dai_link->name, "ETDM1_OUT_BE") == 0 ||
- strcmp(dai_link->name, "ETDM2_IN_BE") == 0) {
- dai_link->codecs->name =
- is5682s ? RT5682S_DEV0_NAME : RT5682_DEV0_NAME;
- dai_link->codecs->dai_name =
- is5682s ? RT5682S_CODEC_DAI : RT5682_CODEC_DAI;
+ } else if (strcmp(dai_link->name, "ETDM1_OUT_BE") == 0) {
+ if (!codec_node) {
+ dev_err(&pdev->dev, "Codec not found!\n");
+ } else {
+ dai_link->codecs->of_node = codec_node;
+ dai_link->codecs->name = NULL;
+ dai_link->codecs->dai_name =
+ is5682s ? RT5682S_CODEC_DAI : RT5682_CODEC_DAI;
+ dai_link->init = mt8195_rt5682_init;
+ dai_link->ops = &mt8195_rt5682_etdm_ops;
+ }
+ } else if (strcmp(dai_link->name, "ETDM2_IN_BE") == 0) {
+ if (!codec_node) {
+ dev_err(&pdev->dev, "Codec not found!\n");
+ } else {
+ dai_link->codecs->of_node = codec_node;
+ dai_link->codecs->name = NULL;
+ dai_link->codecs->dai_name =
+ is5682s ? RT5682S_CODEC_DAI : RT5682_CODEC_DAI;
+ dai_link->ops = &mt8195_rt5682_etdm_ops;
+ }
} else if (strcmp(dai_link->name, "DL_SRC_BE") == 0 ||
strcmp(dai_link->name, "UL_SRC1_BE") == 0 ||
strcmp(dai_link->name, "UL_SRC2_BE") == 0) {
diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig
index e6bca9070953..f03c74809324 100644
--- a/sound/soc/pxa/Kconfig
+++ b/sound/soc/pxa/Kconfig
@@ -35,7 +35,6 @@ config SND_MMP_SOC_SSPA
tristate "SoC Audio via MMP SSPA ports"
depends on ARCH_MMP
select SND_SOC_GENERIC_DMAENGINE_PCM
- select SND_ARM
help
Say Y if you want to add support for codecs attached to
the MMP SSPA interface.
diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig
index e7b00d1d9e99..762491d6f2f2 100644
--- a/sound/soc/qcom/Kconfig
+++ b/sound/soc/qcom/Kconfig
@@ -221,4 +221,16 @@ config SND_SOC_SC7280
SC7280 SoC-based systems.
Say Y or M if you want to use audio device on this SoCs.
+config SND_SOC_X1E80100
+ tristate "SoC Machine driver for X1E80100 boards"
+ depends on QCOM_APR && SOUNDWIRE
+ depends on COMMON_CLK
+ select SND_SOC_QDSP6
+ select SND_SOC_QCOM_COMMON
+ select SND_SOC_QCOM_SDW
+ help
+ Add support for audio on Qualcomm Technologies Inc.
+ X1E80100 SoC-based systems.
+ Say Y or M if you want to use audio device on this SoCs.
+
endif #SND_SOC_QCOM
diff --git a/sound/soc/qcom/Makefile b/sound/soc/qcom/Makefile
index 254350d9dc06..34f3fcb8ee9a 100644
--- a/sound/soc/qcom/Makefile
+++ b/sound/soc/qcom/Makefile
@@ -29,6 +29,7 @@ snd-soc-sm8250-objs := sm8250.o
snd-soc-sc8280xp-objs := sc8280xp.o
snd-soc-qcom-common-objs := common.o
snd-soc-qcom-sdw-objs := sdw.o
+snd-soc-x1e80100-objs := x1e80100.o
obj-$(CONFIG_SND_SOC_STORM) += snd-soc-storm.o
obj-$(CONFIG_SND_SOC_APQ8016_SBC) += snd-soc-apq8016-sbc.o
@@ -40,6 +41,7 @@ obj-$(CONFIG_SND_SOC_SDM845) += snd-soc-sdm845.o
obj-$(CONFIG_SND_SOC_SM8250) += snd-soc-sm8250.o
obj-$(CONFIG_SND_SOC_QCOM_COMMON) += snd-soc-qcom-common.o
obj-$(CONFIG_SND_SOC_QCOM_SDW) += snd-soc-qcom-sdw.o
+obj-$(CONFIG_SND_SOC_X1E80100) += snd-soc-x1e80100.o
#DSP lib
obj-$(CONFIG_SND_SOC_QDSP6) += qdsp6/
diff --git a/sound/soc/qcom/apq8016_sbc.c b/sound/soc/qcom/apq8016_sbc.c
index efbdbb4dd753..4834a56eaa88 100644
--- a/sound/soc/qcom/apq8016_sbc.c
+++ b/sound/soc/qcom/apq8016_sbc.c
@@ -344,4 +344,4 @@ module_platform_driver(apq8016_sbc_platform_driver);
MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org");
MODULE_DESCRIPTION("APQ8016 ASoC Machine Driver");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/apq8096.c b/sound/soc/qcom/apq8096.c
index 7ee6df02b906..4f6594cc723c 100644
--- a/sound/soc/qcom/apq8096.c
+++ b/sound/soc/qcom/apq8096.c
@@ -142,4 +142,4 @@ static struct platform_driver msm_snd_apq8096_driver = {
module_platform_driver(msm_snd_apq8096_driver);
MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org");
MODULE_DESCRIPTION("APQ8096 ASoC Machine Driver");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/common.c b/sound/soc/qcom/common.c
index 483bbf53a541..756706d5b493 100644
--- a/sound/soc/qcom/common.c
+++ b/sound/soc/qcom/common.c
@@ -239,4 +239,4 @@ int qcom_snd_wcd_jack_setup(struct snd_soc_pcm_runtime *rtd,
return 0;
}
EXPORT_SYMBOL_GPL(qcom_snd_wcd_jack_setup);
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/lpass-apq8016.c b/sound/soc/qcom/lpass-apq8016.c
index 8e58e814a95f..9005c85f8c54 100644
--- a/sound/soc/qcom/lpass-apq8016.c
+++ b/sound/soc/qcom/lpass-apq8016.c
@@ -305,5 +305,5 @@ static struct platform_driver apq8016_lpass_cpu_platform_driver = {
module_platform_driver(apq8016_lpass_cpu_platform_driver);
MODULE_DESCRIPTION("APQ8016 LPASS CPU Driver");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/lpass-cdc-dma.c b/sound/soc/qcom/lpass-cdc-dma.c
index 48b03e60e3a3..8106c586f68a 100644
--- a/sound/soc/qcom/lpass-cdc-dma.c
+++ b/sound/soc/qcom/lpass-cdc-dma.c
@@ -259,7 +259,7 @@ static int lpass_cdc_dma_daiops_trigger(struct snd_pcm_substream *substream,
int cmd, struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream);
- struct lpaif_dmactl *dmactl;
+ struct lpaif_dmactl *dmactl = NULL;
int ret = 0, id;
switch (cmd) {
diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c
index 88b80ed45c66..b0f3e02cb043 100644
--- a/sound/soc/qcom/lpass-cpu.c
+++ b/sound/soc/qcom/lpass-cpu.c
@@ -1294,4 +1294,4 @@ void asoc_qcom_lpass_cpu_platform_shutdown(struct platform_device *pdev)
EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_platform_shutdown);
MODULE_DESCRIPTION("QTi LPASS CPU Driver");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/lpass-hdmi.c b/sound/soc/qcom/lpass-hdmi.c
index 24b1a7523adb..ce753ebc0894 100644
--- a/sound/soc/qcom/lpass-hdmi.c
+++ b/sound/soc/qcom/lpass-hdmi.c
@@ -251,4 +251,4 @@ const struct snd_soc_dai_ops asoc_qcom_lpass_hdmi_dai_ops = {
EXPORT_SYMBOL_GPL(asoc_qcom_lpass_hdmi_dai_ops);
MODULE_DESCRIPTION("QTi LPASS HDMI Driver");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/lpass-ipq806x.c b/sound/soc/qcom/lpass-ipq806x.c
index e0e9ad35821c..5c874139f39d 100644
--- a/sound/soc/qcom/lpass-ipq806x.c
+++ b/sound/soc/qcom/lpass-ipq806x.c
@@ -177,4 +177,4 @@ static struct platform_driver ipq806x_lpass_cpu_platform_driver = {
module_platform_driver(ipq806x_lpass_cpu_platform_driver);
MODULE_DESCRIPTION("QTi LPASS CPU Driver");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c
index 333c427cfdb0..addd2c4bdd3e 100644
--- a/sound/soc/qcom/lpass-platform.c
+++ b/sound/soc/qcom/lpass-platform.c
@@ -1384,4 +1384,4 @@ int asoc_qcom_lpass_platform_register(struct platform_device *pdev)
EXPORT_SYMBOL_GPL(asoc_qcom_lpass_platform_register);
MODULE_DESCRIPTION("QTi LPASS Platform Driver");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/lpass-sc7180.c b/sound/soc/qcom/lpass-sc7180.c
index 22063b834554..e6bcdf6ed796 100644
--- a/sound/soc/qcom/lpass-sc7180.c
+++ b/sound/soc/qcom/lpass-sc7180.c
@@ -322,4 +322,4 @@ static struct platform_driver sc7180_lpass_cpu_platform_driver = {
module_platform_driver(sc7180_lpass_cpu_platform_driver);
MODULE_DESCRIPTION("SC7180 LPASS CPU DRIVER");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h
index 2f222bd4ffcc..27a2bf9a6613 100644
--- a/sound/soc/qcom/lpass.h
+++ b/sound/soc/qcom/lpass.h
@@ -398,7 +398,7 @@ struct lpass_pcm_data {
};
/* register the platform driver from the CPU DAI driver */
-int asoc_qcom_lpass_platform_register(struct platform_device *);
+int asoc_qcom_lpass_platform_register(struct platform_device *pdev);
void asoc_qcom_lpass_cpu_platform_remove(struct platform_device *pdev);
void asoc_qcom_lpass_cpu_platform_shutdown(struct platform_device *pdev);
int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev);
diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c
index 5974c7929dd3..5291deac0a0b 100644
--- a/sound/soc/qcom/qdsp6/audioreach.c
+++ b/sound/soc/qcom/qdsp6/audioreach.c
@@ -267,6 +267,21 @@ void *audioreach_alloc_apm_cmd_pkt(int pkt_size, uint32_t opcode, uint32_t token
}
EXPORT_SYMBOL_GPL(audioreach_alloc_apm_cmd_pkt);
+static void audioreach_set_channel_mapping(u8 *ch_map, int num_channels)
+{
+ if (num_channels == 1) {
+ ch_map[0] = PCM_CHANNEL_FL;
+ } else if (num_channels == 2) {
+ ch_map[0] = PCM_CHANNEL_FL;
+ ch_map[1] = PCM_CHANNEL_FR;
+ } else if (num_channels == 4) {
+ ch_map[0] = PCM_CHANNEL_FL;
+ ch_map[1] = PCM_CHANNEL_FR;
+ ch_map[2] = PCM_CHANNEL_LS;
+ ch_map[3] = PCM_CHANNEL_RS;
+ }
+}
+
static void apm_populate_container_config(struct apm_container_obj *cfg,
struct audioreach_container *cont)
{
@@ -829,10 +844,15 @@ static int audioreach_mfc_set_media_format(struct q6apm_graph *graph,
media_format->num_channels = cfg->num_channels;
if (num_channels == 1) {
- media_format->channel_mapping[0] = PCM_CHANNEL_L;
+ media_format->channel_mapping[0] = PCM_CHANNEL_FL;
} else if (num_channels == 2) {
- media_format->channel_mapping[0] = PCM_CHANNEL_L;
- media_format->channel_mapping[1] = PCM_CHANNEL_R;
+ media_format->channel_mapping[0] = PCM_CHANNEL_FL;
+ media_format->channel_mapping[1] = PCM_CHANNEL_FR;
+ } else if (num_channels == 4) {
+ media_format->channel_mapping[0] = PCM_CHANNEL_FL;
+ media_format->channel_mapping[1] = PCM_CHANNEL_FR;
+ media_format->channel_mapping[2] = PCM_CHANNEL_LS;
+ media_format->channel_mapping[3] = PCM_CHANNEL_RS;
}
rc = q6apm_send_cmd_sync(graph->apm, pkt, 0);
@@ -864,12 +884,8 @@ static int audioreach_set_compr_media_format(struct media_format *media_fmt_hdr,
mp3_cfg->endianness = PCM_LITTLE_ENDIAN;
mp3_cfg->num_channels = mcfg->num_channels;
- if (mcfg->num_channels == 1) {
- mp3_cfg->channel_mapping[0] = PCM_CHANNEL_L;
- } else if (mcfg->num_channels == 2) {
- mp3_cfg->channel_mapping[0] = PCM_CHANNEL_L;
- mp3_cfg->channel_mapping[1] = PCM_CHANNEL_R;
- }
+ audioreach_set_channel_mapping(mp3_cfg->channel_mapping,
+ mcfg->num_channels);
break;
case SND_AUDIOCODEC_AAC:
media_fmt_hdr->data_format = DATA_FORMAT_RAW_COMPRESSED;
@@ -1057,7 +1073,7 @@ static int audioreach_pcm_set_media_format(struct q6apm_graph *graph,
int rc, payload_size;
struct gpr_pkt *pkt;
- if (num_channels > 2) {
+ if (num_channels > 4) {
dev_err(graph->dev, "Error: Invalid channels (%d)!\n", num_channels);
return -EINVAL;
}
@@ -1089,13 +1105,8 @@ static int audioreach_pcm_set_media_format(struct q6apm_graph *graph,
media_cfg->q_factor = mcfg->bit_width - 1;
media_cfg->bits_per_sample = mcfg->bit_width;
- if (num_channels == 1) {
- media_cfg->channel_mapping[0] = PCM_CHANNEL_L;
- } else if (num_channels == 2) {
- media_cfg->channel_mapping[0] = PCM_CHANNEL_L;
- media_cfg->channel_mapping[1] = PCM_CHANNEL_R;
-
- }
+ audioreach_set_channel_mapping(media_cfg->channel_mapping,
+ num_channels);
rc = q6apm_send_cmd_sync(graph->apm, pkt, 0);
@@ -1116,7 +1127,7 @@ static int audioreach_shmem_set_media_format(struct q6apm_graph *graph,
struct gpr_pkt *pkt;
void *p;
- if (num_channels > 2) {
+ if (num_channels > 4) {
dev_err(graph->dev, "Error: Invalid channels (%d)!\n", num_channels);
return -EINVAL;
}
@@ -1153,12 +1164,8 @@ static int audioreach_shmem_set_media_format(struct q6apm_graph *graph,
cfg->endianness = PCM_LITTLE_ENDIAN;
cfg->num_channels = mcfg->num_channels;
- if (mcfg->num_channels == 1)
- cfg->channel_mapping[0] = PCM_CHANNEL_L;
- else if (num_channels == 2) {
- cfg->channel_mapping[0] = PCM_CHANNEL_L;
- cfg->channel_mapping[1] = PCM_CHANNEL_R;
- }
+ audioreach_set_channel_mapping(cfg->channel_mapping,
+ num_channels);
} else {
rc = audioreach_set_compr_media_format(header, p, mcfg);
if (rc) {
diff --git a/sound/soc/qcom/qdsp6/audioreach.h b/sound/soc/qcom/qdsp6/audioreach.h
index e38111ffd7b9..2c82917b7162 100644
--- a/sound/soc/qcom/qdsp6/audioreach.h
+++ b/sound/soc/qcom/qdsp6/audioreach.h
@@ -158,8 +158,6 @@ struct param_id_enc_bitrate_param {
#define MEDIA_FMT_ID_PCM 0x09001000
#define MEDIA_FMT_ID_MP3 0x09001009
-#define PCM_CHANNEL_L 1
-#define PCM_CHANNEL_R 2
#define SAMPLE_RATE_48K 48000
#define BIT_WIDTH_16 16
diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c
index 91d39f6ad0bd..ef7557be5d66 100644
--- a/sound/soc/qcom/qdsp6/q6afe.c
+++ b/sound/soc/qcom/qdsp6/q6afe.c
@@ -553,13 +553,13 @@ struct q6afe_port {
};
struct afe_cmd_remote_lpass_core_hw_vote_request {
- uint32_t hw_block_id;
- char client_name[8];
+ uint32_t hw_block_id;
+ char client_name[8];
} __packed;
struct afe_cmd_remote_lpass_core_hw_devote_request {
- uint32_t hw_block_id;
- uint32_t client_handle;
+ uint32_t hw_block_id;
+ uint32_t client_handle;
} __packed;
diff --git a/sound/soc/qcom/qdsp6/q6apm-dai.c b/sound/soc/qcom/qdsp6/q6apm-dai.c
index b799ac724627..00bbd291be5c 100644
--- a/sound/soc/qcom/qdsp6/q6apm-dai.c
+++ b/sound/soc/qcom/qdsp6/q6apm-dai.c
@@ -123,7 +123,7 @@ static struct snd_pcm_hardware q6apm_dai_hardware_playback = {
.fifo_size = 0,
};
-static void event_handler(uint32_t opcode, uint32_t token, uint32_t *payload, void *priv)
+static void event_handler(uint32_t opcode, uint32_t token, void *payload, void *priv)
{
struct q6apm_dai_rtd *prtd = priv;
struct snd_pcm_substream *substream = prtd->substream;
@@ -134,7 +134,7 @@ static void event_handler(uint32_t opcode, uint32_t token, uint32_t *payload, vo
prtd->state = Q6APM_STREAM_STOPPED;
break;
case APM_CLIENT_EVENT_DATA_WRITE_DONE:
- spin_lock_irqsave(&prtd->lock, flags);
+ spin_lock_irqsave(&prtd->lock, flags);
prtd->pos += prtd->pcm_count;
spin_unlock_irqrestore(&prtd->lock, flags);
snd_pcm_period_elapsed(substream);
@@ -143,7 +143,7 @@ static void event_handler(uint32_t opcode, uint32_t token, uint32_t *payload, vo
break;
case APM_CLIENT_EVENT_DATA_READ_DONE:
- spin_lock_irqsave(&prtd->lock, flags);
+ spin_lock_irqsave(&prtd->lock, flags);
prtd->pos += prtd->pcm_count;
spin_unlock_irqrestore(&prtd->lock, flags);
snd_pcm_period_elapsed(substream);
@@ -157,7 +157,7 @@ static void event_handler(uint32_t opcode, uint32_t token, uint32_t *payload, vo
}
static void event_handler_compr(uint32_t opcode, uint32_t token,
- uint32_t *payload, void *priv)
+ void *payload, void *priv)
{
struct q6apm_dai_rtd *prtd = priv;
struct snd_compr_stream *substream = prtd->cstream;
@@ -352,7 +352,7 @@ static int q6apm_dai_open(struct snd_soc_component *component,
spin_lock_init(&prtd->lock);
prtd->substream = substream;
- prtd->graph = q6apm_graph_open(dev, (q6apm_cb)event_handler, prtd, graph_id);
+ prtd->graph = q6apm_graph_open(dev, event_handler, prtd, graph_id);
if (IS_ERR(prtd->graph)) {
dev_err(dev, "%s: Could not allocate memory\n", __func__);
ret = PTR_ERR(prtd->graph);
@@ -496,7 +496,7 @@ static int q6apm_dai_compr_open(struct snd_soc_component *component,
return -ENOMEM;
prtd->cstream = stream;
- prtd->graph = q6apm_graph_open(dev, (q6apm_cb)event_handler_compr, prtd, graph_id);
+ prtd->graph = q6apm_graph_open(dev, event_handler_compr, prtd, graph_id);
if (IS_ERR(prtd->graph)) {
ret = PTR_ERR(prtd->graph);
kfree(prtd);
diff --git a/sound/soc/qcom/qdsp6/q6asm.h b/sound/soc/qcom/qdsp6/q6asm.h
index 0103d8dae5da..519e1b3a3f7c 100644
--- a/sound/soc/qcom/qdsp6/q6asm.h
+++ b/sound/soc/qcom/qdsp6/q6asm.h
@@ -35,16 +35,16 @@ enum {
#define ASM_LAST_BUFFER_FLAG BIT(30)
struct q6asm_flac_cfg {
- u32 sample_rate;
- u32 ext_sample_rate;
- u32 min_frame_size;
- u32 max_frame_size;
- u16 stream_info_present;
- u16 min_blk_size;
- u16 max_blk_size;
- u16 ch_cfg;
- u16 sample_size;
- u16 md5_sum;
+ u32 sample_rate;
+ u32 ext_sample_rate;
+ u32 min_frame_size;
+ u32 max_frame_size;
+ u16 stream_info_present;
+ u16 min_blk_size;
+ u16 max_blk_size;
+ u16 ch_cfg;
+ u16 sample_size;
+ u16 md5_sum;
};
struct q6asm_wma_cfg {
diff --git a/sound/soc/qcom/qdsp6/topology.c b/sound/soc/qcom/qdsp6/topology.c
index 130b22a34fb3..70572c83e101 100644
--- a/sound/soc/qcom/qdsp6/topology.c
+++ b/sound/soc/qcom/qdsp6/topology.c
@@ -545,6 +545,7 @@ static struct audioreach_module *audioreach_parse_common_tokens(struct q6apm *ap
if (mod) {
int pn, id = 0;
+
mod->module_id = module_id;
mod->max_ip_port = max_ip_port;
mod->max_op_port = max_op_port;
@@ -1271,7 +1272,7 @@ int audioreach_tplg_init(struct snd_soc_component *component)
ret = request_firmware(&fw, tplg_fw_name, dev);
if (ret < 0) {
- dev_err(dev, "tplg firmware loading %s failed %d \n", tplg_fw_name, ret);
+ dev_err(dev, "tplg firmware loading %s failed %d\n", tplg_fw_name, ret);
goto err;
}
diff --git a/sound/soc/qcom/sc7180.c b/sound/soc/qcom/sc7180.c
index b0320a74d508..029780d6fe6d 100644
--- a/sound/soc/qcom/sc7180.c
+++ b/sound/soc/qcom/sc7180.c
@@ -6,7 +6,6 @@
#include <dt-bindings/sound/sc7180-lpass.h>
#include <dt-bindings/sound/qcom,q6afe.h>
-#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/of.h>
@@ -578,4 +577,4 @@ static struct platform_driver sc7180_snd_driver = {
module_platform_driver(sc7180_snd_driver);
MODULE_DESCRIPTION("sc7180 ASoC Machine Driver");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c
index 39cb0b889aff..b7fd503a1666 100644
--- a/sound/soc/qcom/sc8280xp.c
+++ b/sound/soc/qcom/sc8280xp.c
@@ -14,8 +14,6 @@
#include "common.h"
#include "sdw.h"
-#define DRIVER_NAME "sc8280xp"
-
struct sc8280xp_snd_data {
bool stream_prepared[AFE_PORT_MAX];
struct snd_soc_card *card;
@@ -34,12 +32,14 @@ static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd)
case WSA_CODEC_DMA_RX_0:
case WSA_CODEC_DMA_RX_1:
/*
- * set limit of 0dB on Digital Volume for Speakers,
- * this can prevent damage of speakers to some extent without
- * active speaker protection
+ * Set limit of -3 dB on Digital Volume and 0 dB on PA Volume
+ * to reduce the risk of speaker damage until we have active
+ * speaker protection in place.
*/
- snd_soc_limit_volume(card, "WSA_RX0 Digital Volume", 84);
- snd_soc_limit_volume(card, "WSA_RX1 Digital Volume", 84);
+ snd_soc_limit_volume(card, "WSA_RX0 Digital Volume", 81);
+ snd_soc_limit_volume(card, "WSA_RX1 Digital Volume", 81);
+ snd_soc_limit_volume(card, "SpkrLeft PA Volume", 17);
+ snd_soc_limit_volume(card, "SpkrRight PA Volume", 17);
break;
default:
break;
@@ -48,6 +48,17 @@ static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd)
return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup);
}
+static void sc8280xp_snd_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct sc8280xp_snd_data *pdata = snd_soc_card_get_drvdata(rtd->card);
+ struct sdw_stream_runtime *sruntime = pdata->sruntime[cpu_dai->id];
+
+ pdata->sruntime[cpu_dai->id] = NULL;
+ sdw_release_stream(sruntime);
+}
+
static int sc8280xp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
{
@@ -108,6 +119,8 @@ static int sc8280xp_snd_hw_free(struct snd_pcm_substream *substream)
}
static const struct snd_soc_ops sc8280xp_be_ops = {
+ .startup = qcom_snd_sdw_startup,
+ .shutdown = sc8280xp_snd_shutdown,
.hw_params = sc8280xp_snd_hw_params,
.hw_free = sc8280xp_snd_hw_free,
.prepare = sc8280xp_snd_prepare,
@@ -150,13 +163,16 @@ static int sc8280xp_platform_probe(struct platform_device *pdev)
if (ret)
return ret;
- card->driver_name = DRIVER_NAME;
+ card->driver_name = of_device_get_match_data(dev);
sc8280xp_add_be_ops(card);
return devm_snd_soc_register_card(dev, card);
}
static const struct of_device_id snd_sc8280xp_dt_match[] = {
- {.compatible = "qcom,sc8280xp-sndcard",},
+ {.compatible = "qcom,sc8280xp-sndcard", "sc8280xp"},
+ {.compatible = "qcom,sm8450-sndcard", "sm8450"},
+ {.compatible = "qcom,sm8550-sndcard", "sm8550"},
+ {.compatible = "qcom,sm8650-sndcard", "sm8650"},
{}
};
@@ -172,4 +188,4 @@ static struct platform_driver snd_sc8280xp_driver = {
module_platform_driver(snd_sc8280xp_driver);
MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org");
MODULE_DESCRIPTION("SC8280XP ASoC Machine Driver");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/sdm845.c b/sound/soc/qcom/sdm845.c
index 252a0f0819be..75701546b6ea 100644
--- a/sound/soc/qcom/sdm845.c
+++ b/sound/soc/qcom/sdm845.c
@@ -625,4 +625,4 @@ static struct platform_driver sdm845_snd_driver = {
module_platform_driver(sdm845_snd_driver);
MODULE_DESCRIPTION("sdm845 ASoC Machine Driver");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/sdw.c b/sound/soc/qcom/sdw.c
index dd275123d31d..7f5089bbe022 100644
--- a/sound/soc/qcom/sdw.c
+++ b/sound/soc/qcom/sdw.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
-// Copyright (c) 2018, Linaro Limited.
+// Copyright (c) 2018-2023, Linaro Limited.
// Copyright (c) 2018, The Linux Foundation. All rights reserved.
#include <dt-bindings/sound/qcom,q6afe.h>
@@ -7,6 +7,49 @@
#include <sound/soc.h>
#include "sdw.h"
+/**
+ * qcom_snd_sdw_startup() - Helper to start Soundwire stream for SoC audio card
+ * @substream: The PCM substream from audio, as passed to snd_soc_ops->startup()
+ *
+ * Helper for the SoC audio card (snd_soc_ops->startup()) to allocate and set
+ * Soundwire stream runtime to each codec DAI.
+ *
+ * The shutdown() callback should call sdw_release_stream() on the same
+ * sdw_stream_runtime.
+ *
+ * Return: 0 or errno
+ */
+int qcom_snd_sdw_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct sdw_stream_runtime *sruntime;
+ struct snd_soc_dai *codec_dai;
+ int ret, i;
+
+ sruntime = sdw_alloc_stream(cpu_dai->name);
+ if (!sruntime)
+ return -ENOMEM;
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ ret = snd_soc_dai_set_stream(codec_dai, sruntime,
+ substream->stream);
+ if (ret < 0 && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "Failed to set sdw stream on %s\n",
+ codec_dai->name);
+ goto err_set_stream;
+ }
+ }
+
+ return 0;
+
+err_set_stream:
+ sdw_release_stream(sruntime);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(qcom_snd_sdw_startup);
+
int qcom_snd_sdw_prepare(struct snd_pcm_substream *substream,
struct sdw_stream_runtime *sruntime,
bool *stream_prepared)
@@ -117,4 +160,4 @@ int qcom_snd_sdw_hw_free(struct snd_pcm_substream *substream,
return 0;
}
EXPORT_SYMBOL_GPL(qcom_snd_sdw_hw_free);
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/sdw.h b/sound/soc/qcom/sdw.h
index d74cbb84da13..392e3455f1b1 100644
--- a/sound/soc/qcom/sdw.h
+++ b/sound/soc/qcom/sdw.h
@@ -6,6 +6,7 @@
#include <linux/soundwire/sdw.h>
+int qcom_snd_sdw_startup(struct snd_pcm_substream *substream);
int qcom_snd_sdw_prepare(struct snd_pcm_substream *substream,
struct sdw_stream_runtime *runtime,
bool *stream_prepared);
diff --git a/sound/soc/qcom/sm8250.c b/sound/soc/qcom/sm8250.c
index 9cc869fd70ac..d70df72c0160 100644
--- a/sound/soc/qcom/sm8250.c
+++ b/sound/soc/qcom/sm8250.c
@@ -66,7 +66,19 @@ static int sm8250_snd_startup(struct snd_pcm_substream *substream)
default:
break;
}
- return 0;
+
+ return qcom_snd_sdw_startup(substream);
+}
+
+static void sm2450_snd_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct sm8250_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
+ struct sdw_stream_runtime *sruntime = data->sruntime[cpu_dai->id];
+
+ data->sruntime[cpu_dai->id] = NULL;
+ sdw_release_stream(sruntime);
}
static int sm8250_snd_hw_params(struct snd_pcm_substream *substream,
@@ -103,6 +115,7 @@ static int sm8250_snd_hw_free(struct snd_pcm_substream *substream)
static const struct snd_soc_ops sm8250_be_ops = {
.startup = sm8250_snd_startup,
+ .shutdown = sm2450_snd_shutdown,
.hw_params = sm8250_snd_hw_params,
.hw_free = sm8250_snd_hw_free,
.prepare = sm8250_snd_prepare,
@@ -169,4 +182,4 @@ static struct platform_driver snd_sm8250_driver = {
module_platform_driver(snd_sm8250_driver);
MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org");
MODULE_DESCRIPTION("SM8250 ASoC Machine Driver");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/storm.c b/sound/soc/qcom/storm.c
index 553165f11d30..c8d5ac43a176 100644
--- a/sound/soc/qcom/storm.c
+++ b/sound/soc/qcom/storm.c
@@ -140,4 +140,4 @@ static struct platform_driver storm_platform_driver = {
module_platform_driver(storm_platform_driver);
MODULE_DESCRIPTION("QTi IPQ806x-based Storm Machine Driver");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/x1e80100.c b/sound/soc/qcom/x1e80100.c
new file mode 100644
index 000000000000..c3c8bf7ffb5b
--- /dev/null
+++ b/sound/soc/qcom/x1e80100.c
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2023, Linaro Limited
+
+#include <dt-bindings/sound/qcom,q6afe.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/soundwire/sdw.h>
+#include <sound/pcm.h>
+#include <sound/jack.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include "common.h"
+#include "qdsp6/q6afe.h"
+#include "sdw.h"
+
+struct x1e80100_snd_data {
+ bool stream_prepared[AFE_PORT_MAX];
+ struct snd_soc_card *card;
+ struct sdw_stream_runtime *sruntime[AFE_PORT_MAX];
+ struct snd_soc_jack jack;
+ bool jack_setup;
+};
+
+static int x1e80100_snd_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct x1e80100_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
+
+ return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup);
+}
+
+static void x1e80100_snd_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct x1e80100_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
+ struct sdw_stream_runtime *sruntime = data->sruntime[cpu_dai->id];
+
+ data->sruntime[cpu_dai->id] = NULL;
+ sdw_release_stream(sruntime);
+}
+
+static int x1e80100_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ rate->min = rate->max = 48000;
+ switch (cpu_dai->id) {
+ case TX_CODEC_DMA_TX_0:
+ case TX_CODEC_DMA_TX_1:
+ case TX_CODEC_DMA_TX_2:
+ case TX_CODEC_DMA_TX_3:
+ channels->min = 1;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int x1e80100_snd_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_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct x1e80100_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
+
+ return qcom_snd_sdw_hw_params(substream, params, &data->sruntime[cpu_dai->id]);
+}
+
+static int x1e80100_snd_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct x1e80100_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
+ struct sdw_stream_runtime *sruntime = data->sruntime[cpu_dai->id];
+
+ return qcom_snd_sdw_prepare(substream, sruntime,
+ &data->stream_prepared[cpu_dai->id]);
+}
+
+static int x1e80100_snd_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct x1e80100_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct sdw_stream_runtime *sruntime = data->sruntime[cpu_dai->id];
+
+ return qcom_snd_sdw_hw_free(substream, sruntime,
+ &data->stream_prepared[cpu_dai->id]);
+}
+
+static const struct snd_soc_ops x1e80100_be_ops = {
+ .startup = qcom_snd_sdw_startup,
+ .shutdown = x1e80100_snd_shutdown,
+ .hw_params = x1e80100_snd_hw_params,
+ .hw_free = x1e80100_snd_hw_free,
+ .prepare = x1e80100_snd_prepare,
+};
+
+static void x1e80100_add_be_ops(struct snd_soc_card *card)
+{
+ struct snd_soc_dai_link *link;
+ int i;
+
+ for_each_card_prelinks(card, i, link) {
+ if (link->no_pcm == 1) {
+ link->init = x1e80100_snd_init;
+ link->be_hw_params_fixup = x1e80100_be_hw_params_fixup;
+ link->ops = &x1e80100_be_ops;
+ }
+ }
+}
+
+static int x1e80100_platform_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card;
+ struct x1e80100_snd_data *data;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+ /* Allocate the private data */
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ card->owner = THIS_MODULE;
+ card->dev = dev;
+ dev_set_drvdata(dev, card);
+ snd_soc_card_set_drvdata(card, data);
+
+ ret = qcom_snd_parse_of(card);
+ if (ret)
+ return ret;
+
+ card->driver_name = "x1e80100";
+ x1e80100_add_be_ops(card);
+
+ return devm_snd_soc_register_card(dev, card);
+}
+
+static const struct of_device_id snd_x1e80100_dt_match[] = {
+ { .compatible = "qcom,x1e80100-sndcard", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, snd_x1e80100_dt_match);
+
+static struct platform_driver snd_x1e80100_driver = {
+ .probe = x1e80100_platform_probe,
+ .driver = {
+ .name = "snd-x1e80100",
+ .of_match_table = snd_x1e80100_dt_match,
+ },
+};
+module_platform_driver(snd_x1e80100_driver);
+MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org");
+MODULE_AUTHOR("Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>");
+MODULE_DESCRIPTION("Qualcomm X1E80100 ASoC Machine Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/rockchip/rk3399_gru_sound.c b/sound/soc/rockchip/rk3399_gru_sound.c
index 1a504ebd3a0e..6c89c7331229 100644
--- a/sound/soc/rockchip/rk3399_gru_sound.c
+++ b/sound/soc/rockchip/rk3399_gru_sound.c
@@ -446,7 +446,7 @@ static const struct rockchip_sound_route rockchip_routes[] = {
struct dailink_match_data {
const char *compatible;
- struct bus_type *bus_type;
+ const struct bus_type *bus_type;
};
static const struct dailink_match_data dailink_match[] = {
diff --git a/sound/soc/samsung/odroid.c b/sound/soc/samsung/odroid.c
index e95f3d3f0401..110ae14dd7ea 100644
--- a/sound/soc/samsung/odroid.c
+++ b/sound/soc/samsung/odroid.c
@@ -157,8 +157,7 @@ SND_SOC_DAILINK_DEFS(primary,
SND_SOC_DAILINK_DEFS(mixer,
DAILINK_COMP_ARRAY(COMP_DUMMY()),
- DAILINK_COMP_ARRAY(COMP_EMPTY()),
- DAILINK_COMP_ARRAY(COMP_DUMMY()));
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
SND_SOC_DAILINK_DEFS(secondary,
DAILINK_COMP_ARRAY(COMP_EMPTY()),
diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c
index 2ef47aa2c778..84601ba43b7d 100644
--- a/sound/soc/sh/fsi.c
+++ b/sound/soc/sh/fsi.c
@@ -1379,7 +1379,9 @@ static int fsi_dma_probe(struct fsi_priv *fsi, struct fsi_stream *io, struct dev
io->chan = dma_request_channel(mask, shdma_chan_filter,
(void *)io->dma_id);
#else
- io->chan = dma_request_slave_channel(dev, is_play ? "tx" : "rx");
+ io->chan = dma_request_chan(dev, is_play ? "tx" : "rx");
+ if (IS_ERR(io->chan))
+ io->chan = NULL;
#endif
if (io->chan) {
struct dma_slave_config cfg = {};
diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c
index 230c48648af3..afd69c6eb654 100644
--- a/sound/soc/sh/rcar/adg.c
+++ b/sound/soc/sh/rcar/adg.c
@@ -111,6 +111,13 @@ static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io)
ws = 7;
break;
}
+ } else {
+ /*
+ * SSI8 is not connected to ADG.
+ * Thus SSI9 is using ws = 8
+ */
+ if (id == 9)
+ ws = 8;
}
return (0x6 + ws) << 8;
diff --git a/sound/soc/soc-card.c b/sound/soc/soc-card.c
index 285ab4c9c716..8a2f163da6bc 100644
--- a/sound/soc/soc-card.c
+++ b/sound/soc/soc-card.c
@@ -5,6 +5,9 @@
// Copyright (C) 2019 Renesas Electronics Corp.
// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
//
+
+#include <linux/lockdep.h>
+#include <linux/rwsem.h>
#include <sound/soc.h>
#include <sound/jack.h>
@@ -26,12 +29,15 @@ static inline int _soc_card_ret(struct snd_soc_card *card,
return ret;
}
-struct snd_kcontrol *snd_soc_card_get_kcontrol(struct snd_soc_card *soc_card,
- const char *name)
+struct snd_kcontrol *snd_soc_card_get_kcontrol_locked(struct snd_soc_card *soc_card,
+ const char *name)
{
struct snd_card *card = soc_card->snd_card;
struct snd_kcontrol *kctl;
+ /* must be held read or write */
+ lockdep_assert_held(&card->controls_rwsem);
+
if (unlikely(!name))
return NULL;
@@ -40,6 +46,20 @@ struct snd_kcontrol *snd_soc_card_get_kcontrol(struct snd_soc_card *soc_card,
return kctl;
return NULL;
}
+EXPORT_SYMBOL_GPL(snd_soc_card_get_kcontrol_locked);
+
+struct snd_kcontrol *snd_soc_card_get_kcontrol(struct snd_soc_card *soc_card,
+ const char *name)
+{
+ struct snd_card *card = soc_card->snd_card;
+ struct snd_kcontrol *kctl;
+
+ down_read(&card->controls_rwsem);
+ kctl = snd_soc_card_get_kcontrol_locked(soc_card, name);
+ up_read(&card->controls_rwsem);
+
+ return kctl;
+}
EXPORT_SYMBOL_GPL(snd_soc_card_get_kcontrol);
static int jack_new(struct snd_soc_card *card, const char *id, int type,
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index b2bd45e87bc3..516350533e73 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -576,6 +576,28 @@ free_rtd:
return NULL;
}
+static void snd_soc_fill_dummy_dai(struct snd_soc_card *card)
+{
+ struct snd_soc_dai_link *dai_link;
+ int i;
+
+ /*
+ * COMP_DUMMY() creates size 0 array on dai_link.
+ * Fill it as dummy DAI in case of CPU/Codec here.
+ * Do nothing for Platform.
+ */
+ for_each_card_prelinks(card, i, dai_link) {
+ if (dai_link->num_cpus == 0 && dai_link->cpus) {
+ dai_link->num_cpus = 1;
+ dai_link->cpus = &snd_soc_dummy_dlc;
+ }
+ if (dai_link->num_codecs == 0 && dai_link->codecs) {
+ dai_link->num_codecs = 1;
+ dai_link->codecs = &snd_soc_dummy_dlc;
+ }
+ }
+}
+
static void snd_soc_flush_all_delayed_work(struct snd_soc_card *card)
{
struct snd_soc_pcm_runtime *rtd;
@@ -1015,6 +1037,97 @@ component_dai_empty:
return -EINVAL;
}
+#define MAX_DEFAULT_CH_MAP_SIZE 8
+static struct snd_soc_dai_link_ch_map default_ch_map_sync[MAX_DEFAULT_CH_MAP_SIZE] = {
+ { .cpu = 0, .codec = 0 },
+ { .cpu = 1, .codec = 1 },
+ { .cpu = 2, .codec = 2 },
+ { .cpu = 3, .codec = 3 },
+ { .cpu = 4, .codec = 4 },
+ { .cpu = 5, .codec = 5 },
+ { .cpu = 6, .codec = 6 },
+ { .cpu = 7, .codec = 7 },
+};
+static struct snd_soc_dai_link_ch_map default_ch_map_1cpu[MAX_DEFAULT_CH_MAP_SIZE] = {
+ { .cpu = 0, .codec = 0 },
+ { .cpu = 0, .codec = 1 },
+ { .cpu = 0, .codec = 2 },
+ { .cpu = 0, .codec = 3 },
+ { .cpu = 0, .codec = 4 },
+ { .cpu = 0, .codec = 5 },
+ { .cpu = 0, .codec = 6 },
+ { .cpu = 0, .codec = 7 },
+};
+static struct snd_soc_dai_link_ch_map default_ch_map_1codec[MAX_DEFAULT_CH_MAP_SIZE] = {
+ { .cpu = 0, .codec = 0 },
+ { .cpu = 1, .codec = 0 },
+ { .cpu = 2, .codec = 0 },
+ { .cpu = 3, .codec = 0 },
+ { .cpu = 4, .codec = 0 },
+ { .cpu = 5, .codec = 0 },
+ { .cpu = 6, .codec = 0 },
+ { .cpu = 7, .codec = 0 },
+};
+static int snd_soc_compensate_channel_connection_map(struct snd_soc_card *card,
+ struct snd_soc_dai_link *dai_link)
+{
+ struct snd_soc_dai_link_ch_map *ch_maps;
+ int i;
+
+ /*
+ * dai_link->ch_maps indicates how CPU/Codec are connected.
+ * It will be a map seen from a larger number of DAI.
+ * see
+ * soc.h :: [dai_link->ch_maps Image sample]
+ */
+
+ /* it should have ch_maps if connection was N:M */
+ if (dai_link->num_cpus > 1 && dai_link->num_codecs > 1 &&
+ dai_link->num_cpus != dai_link->num_codecs && !dai_link->ch_maps) {
+ dev_err(card->dev, "need to have ch_maps when N:M connection (%s)",
+ dai_link->name);
+ return -EINVAL;
+ }
+
+ /* do nothing if it has own maps */
+ if (dai_link->ch_maps)
+ goto sanity_check;
+
+ /* check default map size */
+ if (dai_link->num_cpus > MAX_DEFAULT_CH_MAP_SIZE ||
+ dai_link->num_codecs > MAX_DEFAULT_CH_MAP_SIZE) {
+ dev_err(card->dev, "soc-core.c needs update default_connection_maps");
+ return -EINVAL;
+ }
+
+ /* Compensate missing map for ... */
+ if (dai_link->num_cpus == dai_link->num_codecs)
+ dai_link->ch_maps = default_ch_map_sync; /* for 1:1 or N:N */
+ else if (dai_link->num_cpus < dai_link->num_codecs)
+ dai_link->ch_maps = default_ch_map_1cpu; /* for 1:N */
+ else
+ dai_link->ch_maps = default_ch_map_1codec; /* for N:1 */
+
+sanity_check:
+ dev_dbg(card->dev, "dai_link %s\n", dai_link->stream_name);
+ for_each_link_ch_maps(dai_link, i, ch_maps) {
+ if ((ch_maps->cpu >= dai_link->num_cpus) ||
+ (ch_maps->codec >= dai_link->num_codecs)) {
+ dev_err(card->dev,
+ "unexpected dai_link->ch_maps[%d] index (cpu(%d/%d) codec(%d/%d))",
+ i,
+ ch_maps->cpu, dai_link->num_cpus,
+ ch_maps->codec, dai_link->num_codecs);
+ return -EINVAL;
+ }
+
+ dev_dbg(card->dev, " [%d] cpu%d <-> codec%d\n",
+ i, ch_maps->cpu, ch_maps->codec);
+ }
+
+ return 0;
+}
+
/**
* snd_soc_remove_pcm_runtime - Remove a pcm_runtime from card
* @card: The ASoC card to which the pcm_runtime has
@@ -1121,8 +1234,13 @@ int snd_soc_add_pcm_runtimes(struct snd_soc_card *card,
int num_dai_link)
{
for (int i = 0; i < num_dai_link; i++) {
- int ret = snd_soc_add_pcm_runtime(card, dai_link + i);
+ int ret;
+ ret = snd_soc_compensate_channel_connection_map(card, dai_link + i);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_add_pcm_runtime(card, dai_link + i);
if (ret < 0)
return ret;
}
@@ -1206,7 +1324,7 @@ found:
*
* To avoid such issue, loop from 63 to 0 here.
* Small number of SND_SOC_POSSIBLE_xxx will be Hi priority.
- * Basic/Default settings of each part and aboves are defined
+ * Basic/Default settings of each part and above are defined
* as Hi priority (= small number) of SND_SOC_POSSIBLE_xxx.
*/
for (i = 63; i >= 0; i--) {
@@ -1752,7 +1870,7 @@ static void append_dmi_string(struct snd_soc_card *card, const char *str)
* @flavour: The flavour "differentiator" for the card amongst its peers.
*
* An Intel machine driver may be used by many different devices but are
- * difficult for userspace to differentiate, since machine drivers ususally
+ * difficult for userspace to differentiate, since machine drivers usually
* use their own name as the card short name and leave the card long name
* blank. To differentiate such devices and fix bugs due to lack of
* device-specific configurations, this function allows DMI info to be used
@@ -1773,7 +1891,7 @@ static void append_dmi_string(struct snd_soc_card *card, const char *str)
* We only keep number and alphabet characters and a few separator characters
* in the card long name since UCM in the user space uses the card long names
* as card configuration directory names and AudoConf cannot support special
- * charactors like SPACE.
+ * characters like SPACE.
*
* Returns 0 on success, otherwise a negative error code.
*/
@@ -2038,6 +2156,8 @@ static int snd_soc_bind_card(struct snd_soc_card *card)
mutex_lock(&client_mutex);
snd_soc_card_mutex_lock_root(card);
+ snd_soc_fill_dummy_dai(card);
+
snd_soc_dapm_init(&card->dapm, card, NULL);
/* check whether any platform is ignore machine FE and using topology */
@@ -2368,7 +2488,7 @@ EXPORT_SYMBOL_GPL(snd_soc_add_card_controls);
/**
* snd_soc_add_dai_controls - add an array of controls to a DAI.
- * Convienience function to add a list of controls.
+ * Convenience function to add a list of controls.
*
* @dai: DAI to add controls to
* @controls: array of controls to add
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 3844f777c87b..bffeea80277f 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -320,7 +320,8 @@ EXPORT_SYMBOL_GPL(dapm_mark_endpoints_dirty);
/* create a new dapm widget */
static inline struct snd_soc_dapm_widget *dapm_cnew_widget(
- const struct snd_soc_dapm_widget *_widget)
+ const struct snd_soc_dapm_widget *_widget,
+ const char *prefix)
{
struct snd_soc_dapm_widget *w;
@@ -328,13 +329,19 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget(
if (!w)
return NULL;
- /*
- * w->name is duplicated in caller, but w->sname isn't.
- * Duplicate it here if defined
- */
+ if (prefix)
+ w->name = kasprintf(GFP_KERNEL, "%s %s", prefix, _widget->name);
+ else
+ w->name = kstrdup_const(_widget->name, GFP_KERNEL);
+ if (!w->name) {
+ kfree(w);
+ return NULL;
+ }
+
if (_widget->sname) {
w->sname = kstrdup_const(_widget->sname, GFP_KERNEL);
if (!w->sname) {
+ kfree_const(w->name);
kfree(w);
return NULL;
}
@@ -3629,20 +3636,12 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm,
{
enum snd_soc_dapm_direction dir;
struct snd_soc_dapm_widget *w;
- const char *prefix;
int ret = -ENOMEM;
- if ((w = dapm_cnew_widget(widget)) == NULL)
+ w = dapm_cnew_widget(widget, soc_dapm_prefix(dapm));
+ if (!w)
goto cnew_failed;
- prefix = soc_dapm_prefix(dapm);
- if (prefix)
- w->name = kasprintf(GFP_KERNEL, "%s %s", prefix, widget->name);
- else
- w->name = kstrdup_const(widget->name, GFP_KERNEL);
- if (!w->name)
- goto name_failed;
-
switch (w->id) {
case snd_soc_dapm_regulator_supply:
w->regulator = devm_regulator_get(dapm->dev, widget->name);
@@ -3767,7 +3766,6 @@ request_failed:
dev_err_probe(dapm->dev, ret, "ASoC: Failed to request %s\n",
w->name);
kfree_const(w->name);
-name_failed:
kfree_const(w->sname);
kfree(w);
cnew_failed:
@@ -4438,11 +4436,14 @@ static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream,
void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card)
{
struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_dai *cpu_dai;
struct snd_soc_dai *codec_dai;
- int i;
/* for each BE DAI link... */
for_each_card_rtds(card, rtd) {
+ struct snd_soc_dai_link_ch_map *ch_maps;
+ int i;
+
/*
* dynamic FE links have no fixed DAI mapping.
* CODEC<->CODEC links have no direct connection.
@@ -4450,39 +4451,15 @@ void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card)
if (rtd->dai_link->dynamic)
continue;
- if (rtd->dai_link->num_cpus == 1) {
- for_each_rtd_codec_dais(rtd, i, codec_dai)
- dapm_connect_dai_pair(card, rtd, codec_dai,
- snd_soc_rtd_to_cpu(rtd, 0));
- } else if (rtd->dai_link->num_codecs == rtd->dai_link->num_cpus) {
- for_each_rtd_codec_dais(rtd, i, codec_dai)
- dapm_connect_dai_pair(card, rtd, codec_dai,
- snd_soc_rtd_to_cpu(rtd, i));
- } else if (rtd->dai_link->num_codecs > rtd->dai_link->num_cpus) {
- int cpu_id;
-
- if (!rtd->dai_link->codec_ch_maps) {
- dev_err(card->dev, "%s: no codec channel mapping table provided\n",
- __func__);
- continue;
- }
+ /*
+ * see
+ * soc.h :: [dai_link->ch_maps Image sample]
+ */
+ for_each_rtd_ch_maps(rtd, i, ch_maps) {
+ cpu_dai = snd_soc_rtd_to_cpu(rtd, ch_maps->cpu);
+ codec_dai = snd_soc_rtd_to_codec(rtd, ch_maps->codec);
- for_each_rtd_codec_dais(rtd, i, codec_dai) {
- cpu_id = rtd->dai_link->codec_ch_maps[i].connected_cpu_id;
- if (cpu_id >= rtd->dai_link->num_cpus) {
- dev_err(card->dev,
- "%s: dai_link %s cpu_id %d too large, num_cpus is %d\n",
- __func__, rtd->dai_link->name, cpu_id,
- rtd->dai_link->num_cpus);
- continue;
- }
- dapm_connect_dai_pair(card, rtd, codec_dai,
- snd_soc_rtd_to_cpu(rtd, cpu_id));
- }
- } else {
- dev_err(card->dev,
- "%s: codec number %d < cpu number %d is not supported\n",
- __func__, rtd->dai_link->num_codecs, rtd->dai_link->num_cpus);
+ dapm_connect_dai_pair(card, rtd, codec_dai, cpu_dai);
}
}
}
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index f6d1b2e11795..77ee103b7cd1 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -292,6 +292,7 @@ static void dpcm_set_be_update_state(struct snd_soc_pcm_runtime *be,
void snd_soc_runtime_action(struct snd_soc_pcm_runtime *rtd,
int stream, int action)
{
+ struct snd_soc_component *component;
struct snd_soc_dai *dai;
int i;
@@ -299,6 +300,13 @@ void snd_soc_runtime_action(struct snd_soc_pcm_runtime *rtd,
for_each_rtd_dais(rtd, i, dai)
snd_soc_dai_action(dai, stream, action);
+
+ /* Increments/Decrements the active count for components without DAIs */
+ for_each_rtd_components(rtd, i, component) {
+ if (component->num_dai)
+ continue;
+ component->active += action;
+ }
}
EXPORT_SYMBOL_GPL(snd_soc_runtime_action);
@@ -554,6 +562,12 @@ static void soc_pcm_hw_update_format(struct snd_pcm_hardware *hw,
hw->formats &= p->formats;
}
+static void soc_pcm_hw_update_subformat(struct snd_pcm_hardware *hw,
+ struct snd_soc_pcm_stream *p)
+{
+ hw->subformats &= p->subformats;
+}
+
/**
* snd_soc_runtime_calc_hw() - Calculate hw limits for a PCM stream
* @rtd: ASoC PCM runtime
@@ -592,6 +606,7 @@ int snd_soc_runtime_calc_hw(struct snd_soc_pcm_runtime *rtd,
soc_pcm_hw_update_chan(hw, cpu_stream);
soc_pcm_hw_update_rate(hw, cpu_stream);
soc_pcm_hw_update_format(hw, cpu_stream);
+ soc_pcm_hw_update_subformat(hw, cpu_stream);
}
cpu_chan_min = hw->channels_min;
cpu_chan_max = hw->channels_max;
@@ -613,6 +628,7 @@ int snd_soc_runtime_calc_hw(struct snd_soc_pcm_runtime *rtd,
soc_pcm_hw_update_chan(hw, codec_stream);
soc_pcm_hw_update_rate(hw, codec_stream);
soc_pcm_hw_update_format(hw, codec_stream);
+ soc_pcm_hw_update_subformat(hw, codec_stream);
}
/* Verify both a valid CPU DAI and a valid CODEC DAI were found */
@@ -1047,6 +1063,7 @@ static int __soc_pcm_hw_params(struct snd_soc_pcm_runtime *rtd,
}
for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
+ struct snd_soc_dai_link_ch_map *ch_maps;
unsigned int ch_mask = 0;
int j;
@@ -1060,22 +1077,20 @@ static int __soc_pcm_hw_params(struct snd_soc_pcm_runtime *rtd,
/* copy params for each cpu */
tmp_params = *params;
- if (!rtd->dai_link->codec_ch_maps)
- goto hw_params;
/*
* construct cpu channel mask by combining ch_mask of each
* codec which maps to the cpu.
+ * see
+ * soc.h :: [dai_link->ch_maps Image sample]
*/
- for_each_rtd_codec_dais(rtd, j, codec_dai) {
- if (rtd->dai_link->codec_ch_maps[j].connected_cpu_id == i)
- ch_mask |= rtd->dai_link->codec_ch_maps[j].ch_mask;
- }
+ for_each_rtd_ch_maps(rtd, j, ch_maps)
+ if (ch_maps->cpu == i)
+ ch_mask |= ch_maps->ch_mask;
/* fixup cpu channel number */
if (ch_mask)
soc_pcm_codec_params_fixup(&tmp_params, ch_mask);
-hw_params:
ret = snd_soc_dai_hw_params(cpu_dai, substream, &tmp_params);
if (ret < 0)
goto out;
@@ -1710,6 +1725,7 @@ static void dpcm_runtime_setup_fe(struct snd_pcm_substream *substream)
soc_pcm_hw_update_rate(hw, cpu_stream);
soc_pcm_hw_update_chan(hw, cpu_stream);
soc_pcm_hw_update_format(hw, cpu_stream);
+ soc_pcm_hw_update_subformat(hw, cpu_stream);
}
}
@@ -1747,6 +1763,7 @@ static void dpcm_runtime_setup_be_format(struct snd_pcm_substream *substream)
codec_stream = snd_soc_dai_get_pcm_stream(dai, stream);
soc_pcm_hw_update_format(hw, codec_stream);
+ soc_pcm_hw_update_subformat(hw, codec_stream);
}
}
}
@@ -2823,35 +2840,20 @@ static int soc_get_playback_capture(struct snd_soc_pcm_runtime *rtd,
}
}
} else {
+ struct snd_soc_dai_link_ch_map *ch_maps;
struct snd_soc_dai *codec_dai;
/* Adapt stream for codec2codec links */
int cpu_capture = snd_soc_get_stream_cpu(dai_link, SNDRV_PCM_STREAM_CAPTURE);
int cpu_playback = snd_soc_get_stream_cpu(dai_link, SNDRV_PCM_STREAM_PLAYBACK);
- for_each_rtd_codec_dais(rtd, i, codec_dai) {
- if (dai_link->num_cpus == 1) {
- cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
- } else if (dai_link->num_cpus == dai_link->num_codecs) {
- cpu_dai = snd_soc_rtd_to_cpu(rtd, i);
- } else if (rtd->dai_link->num_codecs > rtd->dai_link->num_cpus) {
- int cpu_id;
-
- if (!rtd->dai_link->codec_ch_maps) {
- dev_err(rtd->card->dev, "%s: no codec channel mapping table provided\n",
- __func__);
- return -EINVAL;
- }
-
- cpu_id = rtd->dai_link->codec_ch_maps[i].connected_cpu_id;
- cpu_dai = snd_soc_rtd_to_cpu(rtd, cpu_id);
- } else {
- dev_err(rtd->card->dev,
- "%s codec number %d < cpu number %d is not supported\n",
- __func__, rtd->dai_link->num_codecs,
- rtd->dai_link->num_cpus);
- return -EINVAL;
- }
+ /*
+ * see
+ * soc.h :: [dai_link->ch_maps Image sample]
+ */
+ for_each_rtd_ch_maps(rtd, i, ch_maps) {
+ cpu_dai = snd_soc_rtd_to_cpu(rtd, ch_maps->cpu);
+ codec_dai = snd_soc_rtd_to_codec(rtd, ch_maps->codec);
if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_PLAYBACK) &&
snd_soc_dai_stream_valid(cpu_dai, cpu_playback))
diff --git a/sound/soc/soc-topology-test.c b/sound/soc/soc-topology-test.c
index 2cd3540cec04..70cbccc42a42 100644
--- a/sound/soc/soc-topology-test.c
+++ b/sound/soc/soc-topology-test.c
@@ -9,6 +9,7 @@
#include <sound/core.h>
#include <sound/soc.h>
#include <sound/soc-topology.h>
+#include <kunit/device.h>
#include <kunit/test.h>
/* ===== HELPER FUNCTIONS =================================================== */
@@ -21,26 +22,19 @@
*/
static struct device *test_dev;
-static struct device_driver test_drv = {
- .name = "sound-soc-topology-test-driver",
-};
-
static int snd_soc_tplg_test_init(struct kunit *test)
{
- test_dev = root_device_register("sound-soc-topology-test");
+ test_dev = kunit_device_register(test, "sound-soc-topology-test");
test_dev = get_device(test_dev);
if (!test_dev)
return -ENODEV;
- test_dev->driver = &test_drv;
-
return 0;
}
static void snd_soc_tplg_test_exit(struct kunit *test)
{
put_device(test_dev);
- root_device_unregister(test_dev);
}
/*
diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig
index a741ed96e789..32ffd345e07f 100644
--- a/sound/soc/sof/Kconfig
+++ b/sound/soc/sof/Kconfig
@@ -126,6 +126,17 @@ config SND_SOC_SOF_STRICT_ABI_CHECKS
If you are not involved in SOF releases and CI development,
select "N".
+config SND_SOC_SOF_ALLOW_FALLBACK_TO_NEWER_IPC_VERSION
+ bool "SOF allow fallback to newer IPC version"
+ help
+ This option will allow the kernel to try to 'fallback' to a newer IPC
+ version if there are missing firmware files to satisfy the default IPC
+ version.
+ IPC version fallback to older versions is not affected by this option,
+ it is always available.
+ Say Y if you are involved in SOF development and need this option.
+ If not, select N.
+
config SND_SOC_SOF_DEBUG
bool "SOF debugging features"
help
diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile
index ef6fd43d0b72..3624124575af 100644
--- a/sound/soc/sof/Makefile
+++ b/sound/soc/sof/Makefile
@@ -1,7 +1,8 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
snd-sof-objs := core.o ops.o loader.o ipc.o pcm.o pm.o debug.o topology.o\
- control.o trace.o iomem-utils.o sof-audio.o stream-ipc.o
+ control.o trace.o iomem-utils.o sof-audio.o stream-ipc.o\
+ fw-file-profile.o
# IPC implementations
ifneq ($(CONFIG_SND_SOC_SOF_IPC3),)
diff --git a/sound/soc/sof/amd/acp-common.c b/sound/soc/sof/amd/acp-common.c
index 3a0c7688dcfe..2d72c6d55dc8 100644
--- a/sound/soc/sof/amd/acp-common.c
+++ b/sound/soc/sof/amd/acp-common.c
@@ -13,7 +13,6 @@
#include "../sof-priv.h"
#include "../sof-audio.h"
#include "../ops.h"
-#include "../sof-audio.h"
#include "acp.h"
#include "acp-dsp-offset.h"
#include <sound/sof/xtensa.h>
diff --git a/sound/soc/sof/amd/acp-ipc.c b/sound/soc/sof/amd/acp-ipc.c
index fcb54f545fea..b44b1b1adb6e 100644
--- a/sound/soc/sof/amd/acp-ipc.c
+++ b/sound/soc/sof/amd/acp-ipc.c
@@ -3,7 +3,7 @@
// 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) 2021 Advanced Micro Devices, Inc.
+// Copyright(c) 2021, 2023 Advanced Micro Devices, Inc.
//
// Authors: Balakishore Pati <Balakishore.pati@amd.com>
// Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c
index 603ea5fc0d0d..07632ae6ccf5 100644
--- a/sound/soc/sof/amd/acp.c
+++ b/sound/soc/sof/amd/acp.c
@@ -278,6 +278,17 @@ int configure_and_run_sha_dma(struct acp_dev_data *adata, void *image_addr,
return ret;
}
+ /* psp_send_cmd only required for vangogh platform (rev - 5) */
+ if (desc->rev == 5) {
+ /* Modify IRAM and DRAM size */
+ ret = psp_send_cmd(adata, MBOX_ACP_IRAM_DRAM_FENCE_COMMAND | IRAM_DRAM_FENCE_2);
+ if (ret)
+ return ret;
+ ret = psp_send_cmd(adata, MBOX_ACP_IRAM_DRAM_FENCE_COMMAND | MBOX_ISREADY_FLAG);
+ 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,
ACP_REG_POLL_INTERVAL, ACP_DMA_COMPLETE_TIMEOUT_US);
@@ -343,13 +354,15 @@ static irqreturn_t acp_irq_thread(int irq, void *context)
const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
unsigned int count = ACP_HW_SEM_RETRY_COUNT;
- while (snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->hw_semaphore_offset)) {
- /* Wait until acquired HW Semaphore lock or timeout */
- count--;
- if (!count) {
- dev_err(sdev->dev, "%s: Failed to acquire HW lock\n", __func__);
- return IRQ_NONE;
- }
+ spin_lock_irq(&sdev->ipc_lock);
+ /* Wait until acquired HW Semaphore lock or timeout */
+ while (snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->hw_semaphore_offset) && --count)
+ ;
+ spin_unlock_irq(&sdev->ipc_lock);
+
+ if (!count) {
+ dev_err(sdev->dev, "%s: Failed to acquire HW lock\n", __func__);
+ return IRQ_NONE;
}
sof_ops(sdev)->irq_thread(irq, sdev);
diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h
index c536cfde0e44..c645aee216fd 100644
--- a/sound/soc/sof/amd/acp.h
+++ b/sound/soc/sof/amd/acp.h
@@ -74,9 +74,14 @@
#define MP0_C2PMSG_114_REG 0x3810AC8
#define MP0_C2PMSG_73_REG 0x3810A24
#define MBOX_ACP_SHA_DMA_COMMAND 0x70000
+#define MBOX_ACP_IRAM_DRAM_FENCE_COMMAND 0x80000
#define MBOX_DELAY_US 1000
#define MBOX_READY_MASK 0x80000000
#define MBOX_STATUS_MASK 0xFFFF
+#define MBOX_ISREADY_FLAG 0x40000000
+#define IRAM_DRAM_FENCE_0 0X0
+#define IRAM_DRAM_FENCE_1 0X01
+#define IRAM_DRAM_FENCE_2 0X02
#define BOX_SIZE_512 0x200
#define BOX_SIZE_1024 0x400
diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c
index d7b090224f1b..425b023b03b4 100644
--- a/sound/soc/sof/core.c
+++ b/sound/soc/sof/core.c
@@ -13,6 +13,7 @@
#include <sound/soc.h>
#include <sound/sof.h>
#include "sof-priv.h"
+#include "sof-of-dev.h"
#include "ops.h"
#define CREATE_TRACE_POINTS
@@ -143,6 +144,233 @@ void sof_set_fw_state(struct snd_sof_dev *sdev, enum sof_fw_state new_state)
}
EXPORT_SYMBOL(sof_set_fw_state);
+static struct snd_sof_of_mach *sof_of_machine_select(struct snd_sof_dev *sdev)
+{
+ struct snd_sof_pdata *sof_pdata = sdev->pdata;
+ const struct sof_dev_desc *desc = sof_pdata->desc;
+ struct snd_sof_of_mach *mach = desc->of_machines;
+
+ if (!mach)
+ return NULL;
+
+ for (; mach->compatible; mach++) {
+ if (of_machine_is_compatible(mach->compatible)) {
+ sof_pdata->tplg_filename = mach->sof_tplg_filename;
+ if (mach->fw_filename)
+ sof_pdata->fw_filename = mach->fw_filename;
+
+ return mach;
+ }
+ }
+
+ return NULL;
+}
+
+/* SOF Driver enumeration */
+static int sof_machine_check(struct snd_sof_dev *sdev)
+{
+ struct snd_sof_pdata *sof_pdata = sdev->pdata;
+ const struct sof_dev_desc *desc = sof_pdata->desc;
+ struct snd_soc_acpi_mach *mach;
+
+ if (!IS_ENABLED(CONFIG_SND_SOC_SOF_FORCE_NOCODEC_MODE)) {
+ const struct snd_sof_of_mach *of_mach;
+
+ if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) &&
+ sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC))
+ goto nocodec;
+
+ /* find machine */
+ mach = snd_sof_machine_select(sdev);
+ if (mach) {
+ sof_pdata->machine = mach;
+
+ if (sof_pdata->subsystem_id_set) {
+ mach->mach_params.subsystem_vendor = sof_pdata->subsystem_vendor;
+ mach->mach_params.subsystem_device = sof_pdata->subsystem_device;
+ mach->mach_params.subsystem_id_set = true;
+ }
+
+ snd_sof_set_mach_params(mach, sdev);
+ return 0;
+ }
+
+ of_mach = sof_of_machine_select(sdev);
+ if (of_mach) {
+ sof_pdata->of_machine = of_mach;
+ return 0;
+ }
+
+ if (!IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC)) {
+ dev_err(sdev->dev, "error: no matching ASoC machine driver found - aborting probe\n");
+ return -ENODEV;
+ }
+ } else {
+ dev_warn(sdev->dev, "Force to use nocodec mode\n");
+ }
+
+nocodec:
+ /* select nocodec mode */
+ dev_warn(sdev->dev, "Using nocodec machine driver\n");
+ mach = devm_kzalloc(sdev->dev, sizeof(*mach), GFP_KERNEL);
+ if (!mach)
+ return -ENOMEM;
+
+ mach->drv_name = "sof-nocodec";
+ if (!sof_pdata->tplg_filename)
+ sof_pdata->tplg_filename = desc->nocodec_tplg_filename;
+
+ sof_pdata->machine = mach;
+ snd_sof_set_mach_params(mach, sdev);
+
+ return 0;
+}
+
+static int sof_select_ipc_and_paths(struct snd_sof_dev *sdev)
+{
+ struct snd_sof_pdata *plat_data = sdev->pdata;
+ struct sof_loadable_file_profile *base_profile = &plat_data->ipc_file_profile_base;
+ struct sof_loadable_file_profile out_profile;
+ struct device *dev = sdev->dev;
+ int ret;
+
+ if (base_profile->ipc_type != plat_data->desc->ipc_default)
+ dev_info(dev,
+ "Module parameter used, overriding default IPC %d to %d\n",
+ plat_data->desc->ipc_default, base_profile->ipc_type);
+
+ if (base_profile->fw_path)
+ dev_dbg(dev, "Module parameter used, changed fw path to %s\n",
+ base_profile->fw_path);
+ else if (base_profile->fw_path_postfix)
+ dev_dbg(dev, "Path postfix appended to default fw path: %s\n",
+ base_profile->fw_path_postfix);
+
+ if (base_profile->fw_lib_path)
+ dev_dbg(dev, "Module parameter used, changed fw_lib path to %s\n",
+ base_profile->fw_lib_path);
+ else if (base_profile->fw_lib_path_postfix)
+ dev_dbg(dev, "Path postfix appended to default fw_lib path: %s\n",
+ base_profile->fw_lib_path_postfix);
+
+ if (base_profile->fw_name)
+ dev_dbg(dev, "Module parameter used, changed fw filename to %s\n",
+ base_profile->fw_name);
+
+ if (base_profile->tplg_path)
+ dev_dbg(dev, "Module parameter used, changed tplg path to %s\n",
+ base_profile->tplg_path);
+
+ if (base_profile->tplg_name)
+ dev_dbg(dev, "Module parameter used, changed tplg name to %s\n",
+ base_profile->tplg_name);
+
+ ret = sof_create_ipc_file_profile(sdev, base_profile, &out_profile);
+ if (ret)
+ return ret;
+
+ plat_data->ipc_type = out_profile.ipc_type;
+ plat_data->fw_filename = out_profile.fw_name;
+ plat_data->fw_filename_prefix = out_profile.fw_path;
+ plat_data->fw_lib_prefix = out_profile.fw_lib_path;
+ plat_data->tplg_filename_prefix = out_profile.tplg_path;
+
+ return 0;
+}
+
+static int validate_sof_ops(struct snd_sof_dev *sdev)
+{
+ int ret;
+
+ /* init ops, if necessary */
+ ret = sof_ops_init(sdev);
+ if (ret < 0)
+ return ret;
+
+ /* check all mandatory ops */
+ if (!sof_ops(sdev) || !sof_ops(sdev)->probe) {
+ dev_err(sdev->dev, "missing mandatory ops\n");
+ sof_ops_free(sdev);
+ return -EINVAL;
+ }
+
+ if (!sdev->dspless_mode_selected &&
+ (!sof_ops(sdev)->run || !sof_ops(sdev)->block_read ||
+ !sof_ops(sdev)->block_write || !sof_ops(sdev)->send_msg ||
+ !sof_ops(sdev)->load_firmware || !sof_ops(sdev)->ipc_msg_data)) {
+ dev_err(sdev->dev, "missing mandatory DSP ops\n");
+ sof_ops_free(sdev);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int sof_init_sof_ops(struct snd_sof_dev *sdev)
+{
+ struct snd_sof_pdata *plat_data = sdev->pdata;
+ struct sof_loadable_file_profile *base_profile = &plat_data->ipc_file_profile_base;
+
+ /* check IPC support */
+ if (!(BIT(base_profile->ipc_type) & plat_data->desc->ipc_supported_mask)) {
+ dev_err(sdev->dev,
+ "ipc_type %d is not supported on this platform, mask is %#x\n",
+ base_profile->ipc_type, plat_data->desc->ipc_supported_mask);
+ return -EINVAL;
+ }
+
+ /*
+ * Save the selected IPC type and a topology name override before
+ * selecting ops since platform code might need this information
+ */
+ plat_data->ipc_type = base_profile->ipc_type;
+ plat_data->tplg_filename = base_profile->tplg_name;
+
+ return validate_sof_ops(sdev);
+}
+
+static int sof_init_environment(struct snd_sof_dev *sdev)
+{
+ struct snd_sof_pdata *plat_data = sdev->pdata;
+ struct sof_loadable_file_profile *base_profile = &plat_data->ipc_file_profile_base;
+ int ret;
+
+ /* probe the DSP hardware */
+ ret = snd_sof_probe(sdev);
+ if (ret < 0) {
+ dev_err(sdev->dev, "failed to probe DSP %d\n", ret);
+ sof_ops_free(sdev);
+ return ret;
+ }
+
+ /* check machine info */
+ ret = sof_machine_check(sdev);
+ if (ret < 0) {
+ dev_err(sdev->dev, "failed to get machine info %d\n", ret);
+ goto err_machine_check;
+ }
+
+ ret = sof_select_ipc_and_paths(sdev);
+ if (!ret && plat_data->ipc_type != base_profile->ipc_type) {
+ /* IPC type changed, re-initialize the ops */
+ sof_ops_free(sdev);
+
+ ret = validate_sof_ops(sdev);
+ if (ret < 0) {
+ snd_sof_remove(sdev);
+ return ret;
+ }
+ }
+
+err_machine_check:
+ if (ret) {
+ snd_sof_remove(sdev);
+ sof_ops_free(sdev);
+ }
+
+ return ret;
+}
+
/*
* FW Boot State Transition Diagram
*
@@ -188,23 +416,13 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
struct snd_sof_pdata *plat_data = sdev->pdata;
int ret;
- /* probe the DSP hardware */
- ret = snd_sof_probe(sdev);
- if (ret < 0) {
- dev_err(sdev->dev, "error: failed to probe DSP %d\n", ret);
- goto probe_err;
- }
+ /* Initialize loadable file paths and check the environment validity */
+ ret = sof_init_environment(sdev);
+ if (ret)
+ return ret;
sof_set_fw_state(sdev, SOF_FW_BOOT_PREPARE);
- /* check machine info */
- ret = sof_machine_check(sdev);
- if (ret < 0) {
- dev_err(sdev->dev, "error: failed to get machine info %d\n",
- ret);
- goto dsp_err;
- }
-
/* set up platform component driver */
snd_sof_new_platform_drv(sdev);
@@ -324,9 +542,7 @@ fw_load_err:
ipc_err:
dbg_err:
snd_sof_free_debug(sdev);
-dsp_err:
snd_sof_remove(sdev);
-probe_err:
snd_sof_remove_late(sdev);
sof_ops_free(sdev);
@@ -381,34 +597,11 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
}
}
- /* check IPC support */
- if (!(BIT(plat_data->ipc_type) & plat_data->desc->ipc_supported_mask)) {
- dev_err(dev, "ipc_type %d is not supported on this platform, mask is %#x\n",
- plat_data->ipc_type, plat_data->desc->ipc_supported_mask);
- return -EINVAL;
- }
-
- /* init ops, if necessary */
- ret = sof_ops_init(sdev);
- if (ret < 0)
+ /* Initialize sof_ops based on the initial selected IPC version */
+ ret = sof_init_sof_ops(sdev);
+ if (ret)
return ret;
- /* check all mandatory ops */
- if (!sof_ops(sdev) || !sof_ops(sdev)->probe) {
- sof_ops_free(sdev);
- dev_err(dev, "missing mandatory ops\n");
- return -EINVAL;
- }
-
- if (!sdev->dspless_mode_selected &&
- (!sof_ops(sdev)->run || !sof_ops(sdev)->block_read ||
- !sof_ops(sdev)->block_write || !sof_ops(sdev)->send_msg ||
- !sof_ops(sdev)->load_firmware || !sof_ops(sdev)->ipc_msg_data)) {
- sof_ops_free(sdev);
- dev_err(dev, "missing mandatory DSP ops\n");
- return -EINVAL;
- }
-
INIT_LIST_HEAD(&sdev->pcm_list);
INIT_LIST_HEAD(&sdev->kcontrol_list);
INIT_LIST_HEAD(&sdev->widget_list);
@@ -527,6 +720,40 @@ int snd_sof_device_shutdown(struct device *dev)
}
EXPORT_SYMBOL(snd_sof_device_shutdown);
+/* Machine driver registering and unregistering */
+int sof_machine_register(struct snd_sof_dev *sdev, void *pdata)
+{
+ struct snd_sof_pdata *plat_data = pdata;
+ const char *drv_name;
+ const void *mach;
+ int size;
+
+ drv_name = plat_data->machine->drv_name;
+ mach = plat_data->machine;
+ size = sizeof(*plat_data->machine);
+
+ /* register machine driver, pass machine info as pdata */
+ plat_data->pdev_mach =
+ platform_device_register_data(sdev->dev, drv_name,
+ PLATFORM_DEVID_NONE, mach, size);
+ if (IS_ERR(plat_data->pdev_mach))
+ return PTR_ERR(plat_data->pdev_mach);
+
+ dev_dbg(sdev->dev, "created machine %s\n",
+ dev_name(&plat_data->pdev_mach->dev));
+
+ return 0;
+}
+EXPORT_SYMBOL(sof_machine_register);
+
+void sof_machine_unregister(struct snd_sof_dev *sdev, void *pdata)
+{
+ struct snd_sof_pdata *plat_data = pdata;
+
+ platform_device_unregister(plat_data->pdev_mach);
+}
+EXPORT_SYMBOL(sof_machine_unregister);
+
MODULE_AUTHOR("Liam Girdwood");
MODULE_DESCRIPTION("Sound Open Firmware (SOF) Core");
MODULE_LICENSE("Dual BSD/GPL");
diff --git a/sound/soc/sof/fw-file-profile.c b/sound/soc/sof/fw-file-profile.c
new file mode 100644
index 000000000000..138a1ca2c4a8
--- /dev/null
+++ b/sound/soc/sof/fw-file-profile.c
@@ -0,0 +1,322 @@
+// 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 Intel Corporation. All rights reserved.
+//
+
+#include <linux/firmware.h>
+#include <sound/sof.h>
+#include <sound/sof/ext_manifest4.h>
+#include "sof-priv.h"
+
+static int sof_test_firmware_file(struct device *dev,
+ struct sof_loadable_file_profile *profile,
+ enum sof_ipc_type *ipc_type_to_adjust)
+{
+ enum sof_ipc_type fw_ipc_type;
+ const struct firmware *fw;
+ const char *fw_filename;
+ const u32 *magic;
+ int ret;
+
+ fw_filename = kasprintf(GFP_KERNEL, "%s/%s", profile->fw_path,
+ profile->fw_name);
+ if (!fw_filename)
+ return -ENOMEM;
+
+ ret = firmware_request_nowarn(&fw, fw_filename, dev);
+ if (ret < 0) {
+ dev_dbg(dev, "Failed to open firmware file: %s\n", fw_filename);
+ kfree(fw_filename);
+ return ret;
+ }
+
+ /* firmware file exists, check the magic number */
+ magic = (const u32 *)fw->data;
+ switch (*magic) {
+ case SOF_EXT_MAN_MAGIC_NUMBER:
+ fw_ipc_type = SOF_IPC_TYPE_3;
+ break;
+ case SOF_EXT_MAN4_MAGIC_NUMBER:
+ fw_ipc_type = SOF_IPC_TYPE_4;
+ break;
+ default:
+ dev_err(dev, "Invalid firmware magic: %#x\n", *magic);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (ipc_type_to_adjust) {
+ *ipc_type_to_adjust = fw_ipc_type;
+ } else if (fw_ipc_type != profile->ipc_type) {
+ dev_err(dev,
+ "ipc type mismatch between %s and expected: %d vs %d\n",
+ fw_filename, fw_ipc_type, profile->ipc_type);
+ ret = -EINVAL;
+ }
+out:
+ release_firmware(fw);
+ kfree(fw_filename);
+
+ return ret;
+}
+
+static int sof_test_topology_file(struct device *dev,
+ struct sof_loadable_file_profile *profile)
+{
+ const struct firmware *fw;
+ const char *tplg_filename;
+ int ret;
+
+ if (!profile->tplg_path || !profile->tplg_name)
+ return 0;
+
+ tplg_filename = kasprintf(GFP_KERNEL, "%s/%s", profile->tplg_path,
+ profile->tplg_name);
+ if (!tplg_filename)
+ return -ENOMEM;
+
+ ret = firmware_request_nowarn(&fw, tplg_filename, dev);
+ if (!ret)
+ release_firmware(fw);
+ else
+ dev_dbg(dev, "Failed to open topology file: %s\n", tplg_filename);
+
+ kfree(tplg_filename);
+
+ return ret;
+}
+
+static int
+sof_file_profile_for_ipc_type(struct snd_sof_dev *sdev,
+ enum sof_ipc_type ipc_type,
+ const struct sof_dev_desc *desc,
+ struct sof_loadable_file_profile *base_profile,
+ struct sof_loadable_file_profile *out_profile)
+{
+ struct snd_sof_pdata *plat_data = sdev->pdata;
+ bool fw_lib_path_allocated = false;
+ struct device *dev = sdev->dev;
+ bool fw_path_allocated = false;
+ int ret = 0;
+
+ /* firmware path */
+ if (base_profile->fw_path) {
+ out_profile->fw_path = base_profile->fw_path;
+ } else if (base_profile->fw_path_postfix) {
+ out_profile->fw_path = devm_kasprintf(dev, GFP_KERNEL, "%s/%s",
+ desc->default_fw_path[ipc_type],
+ base_profile->fw_path_postfix);
+ if (!out_profile->fw_path)
+ return -ENOMEM;
+
+ fw_path_allocated = true;
+ } else {
+ out_profile->fw_path = desc->default_fw_path[ipc_type];
+ }
+
+ /* firmware filename */
+ if (base_profile->fw_name)
+ out_profile->fw_name = base_profile->fw_name;
+ else
+ out_profile->fw_name = desc->default_fw_filename[ipc_type];
+
+ /*
+ * Check the custom firmware path/filename and adjust the ipc_type to
+ * match with the existing file for the remaining path configuration.
+ *
+ * For default path and firmware name do a verification before
+ * continuing further.
+ */
+ if (base_profile->fw_path || base_profile->fw_name) {
+ ret = sof_test_firmware_file(dev, out_profile, &ipc_type);
+ if (ret)
+ return ret;
+
+ if (!(desc->ipc_supported_mask & BIT(ipc_type))) {
+ dev_err(dev, "Unsupported IPC type %d needed by %s/%s\n",
+ ipc_type, out_profile->fw_path,
+ out_profile->fw_name);
+ return -EINVAL;
+ }
+ }
+
+ /* firmware library path */
+ if (base_profile->fw_lib_path) {
+ out_profile->fw_lib_path = base_profile->fw_lib_path;
+ } else if (desc->default_lib_path[ipc_type]) {
+ if (base_profile->fw_lib_path_postfix) {
+ out_profile->fw_lib_path = devm_kasprintf(dev,
+ GFP_KERNEL, "%s/%s",
+ desc->default_lib_path[ipc_type],
+ base_profile->fw_lib_path_postfix);
+ if (!out_profile->fw_lib_path) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ fw_lib_path_allocated = true;
+ } else {
+ out_profile->fw_lib_path = desc->default_lib_path[ipc_type];
+ }
+ }
+
+ if (base_profile->fw_path_postfix)
+ out_profile->fw_path_postfix = base_profile->fw_path_postfix;
+
+ if (base_profile->fw_lib_path_postfix)
+ out_profile->fw_lib_path_postfix = base_profile->fw_lib_path_postfix;
+
+ /* topology path */
+ if (base_profile->tplg_path)
+ out_profile->tplg_path = base_profile->tplg_path;
+ else
+ out_profile->tplg_path = desc->default_tplg_path[ipc_type];
+
+ /* topology name */
+ out_profile->tplg_name = plat_data->tplg_filename;
+
+ out_profile->ipc_type = ipc_type;
+
+ /* Test only default firmware file */
+ if (!base_profile->fw_path && !base_profile->fw_name)
+ ret = sof_test_firmware_file(dev, out_profile, NULL);
+
+ if (!ret)
+ ret = sof_test_topology_file(dev, out_profile);
+
+out:
+ if (ret) {
+ /* Free up path strings created with devm_kasprintf */
+ if (fw_path_allocated)
+ devm_kfree(dev, out_profile->fw_path);
+ if (fw_lib_path_allocated)
+ devm_kfree(dev, out_profile->fw_lib_path);
+
+ memset(out_profile, 0, sizeof(*out_profile));
+ }
+
+ return ret;
+}
+
+static void
+sof_print_missing_firmware_info(struct snd_sof_dev *sdev,
+ enum sof_ipc_type ipc_type,
+ struct sof_loadable_file_profile *base_profile)
+{
+ struct snd_sof_pdata *plat_data = sdev->pdata;
+ const struct sof_dev_desc *desc = plat_data->desc;
+ struct device *dev = sdev->dev;
+ int ipc_type_count, i;
+ char *marker;
+
+ dev_err(dev, "SOF firmware and/or topology file not found.\n");
+ dev_info(dev, "Supported default profiles\n");
+
+ if (IS_ENABLED(CONFIG_SND_SOC_SOF_ALLOW_FALLBACK_TO_NEWER_IPC_VERSION))
+ ipc_type_count = SOF_IPC_TYPE_COUNT - 1;
+ else
+ ipc_type_count = base_profile->ipc_type;
+
+ for (i = 0; i <= ipc_type_count; i++) {
+ if (!(desc->ipc_supported_mask & BIT(i)))
+ continue;
+
+ if (i == ipc_type)
+ marker = "Requested";
+ else
+ marker = "Fallback";
+
+ dev_info(dev, "- ipc type %d (%s):\n", i, marker);
+ if (base_profile->fw_path_postfix)
+ dev_info(dev, " Firmware file: %s/%s/%s\n",
+ desc->default_fw_path[i],
+ base_profile->fw_path_postfix,
+ desc->default_fw_filename[i]);
+ else
+ dev_info(dev, " Firmware file: %s/%s\n",
+ desc->default_fw_path[i],
+ desc->default_fw_filename[i]);
+
+ dev_info(dev, " Topology file: %s/%s\n",
+ desc->default_tplg_path[i],
+ plat_data->tplg_filename);
+ }
+
+ if (base_profile->fw_path || base_profile->fw_name ||
+ base_profile->tplg_path || base_profile->tplg_name)
+ dev_info(dev, "Verify the path/name override module parameters.\n");
+
+ dev_info(dev, "Check if you have 'sof-firmware' package installed.\n");
+ dev_info(dev, "Optionally it can be manually downloaded from:\n");
+ dev_info(dev, " https://github.com/thesofproject/sof-bin/\n");
+}
+
+static void sof_print_profile_info(struct snd_sof_dev *sdev,
+ enum sof_ipc_type ipc_type,
+ struct sof_loadable_file_profile *profile)
+{
+ struct device *dev = sdev->dev;
+
+ if (ipc_type != profile->ipc_type)
+ dev_info(dev,
+ "Using fallback IPC type %d (requested type was %d)\n",
+ profile->ipc_type, ipc_type);
+
+ dev_info(dev, "Firmware paths/files for ipc type %d:\n", profile->ipc_type);
+
+ dev_info(dev, " Firmware file: %s/%s\n", profile->fw_path, profile->fw_name);
+ if (profile->fw_lib_path)
+ dev_info(dev, " Firmware lib path: %s\n", profile->fw_lib_path);
+ dev_info(dev, " Topology file: %s/%s\n", profile->tplg_path, profile->tplg_name);
+}
+
+int sof_create_ipc_file_profile(struct snd_sof_dev *sdev,
+ struct sof_loadable_file_profile *base_profile,
+ struct sof_loadable_file_profile *out_profile)
+{
+ const struct sof_dev_desc *desc = sdev->pdata->desc;
+ int ipc_fallback_start, ret, i;
+
+ memset(out_profile, 0, sizeof(*out_profile));
+
+ ret = sof_file_profile_for_ipc_type(sdev, base_profile->ipc_type, desc,
+ base_profile, out_profile);
+ if (!ret)
+ goto out;
+
+ /*
+ * No firmware file was found for the requested IPC type, as fallback
+ * if SND_SOC_SOF_ALLOW_FALLBACK_TO_NEWER_IPC_VERSION is selected, check
+ * all IPC versions in a backwards direction (from newer to older)
+ * if SND_SOC_SOF_ALLOW_FALLBACK_TO_NEWER_IPC_VERSION is not selected,
+ * check only older IPC versions than the selected/default version
+ */
+ if (IS_ENABLED(CONFIG_SND_SOC_SOF_ALLOW_FALLBACK_TO_NEWER_IPC_VERSION))
+ ipc_fallback_start = SOF_IPC_TYPE_COUNT - 1;
+ else
+ ipc_fallback_start = (int)base_profile->ipc_type - 1;
+
+ for (i = ipc_fallback_start; i >= 0 ; i--) {
+ if (i == base_profile->ipc_type ||
+ !(desc->ipc_supported_mask & BIT(i)))
+ continue;
+
+ ret = sof_file_profile_for_ipc_type(sdev, i, desc, base_profile,
+ out_profile);
+ if (!ret)
+ break;
+ }
+
+out:
+ if (ret)
+ sof_print_missing_firmware_info(sdev, base_profile->ipc_type,
+ base_profile);
+ else
+ sof_print_profile_info(sdev, base_profile->ipc_type, out_profile);
+
+ return ret;
+}
+EXPORT_SYMBOL(sof_create_ipc_file_profile);
diff --git a/sound/soc/sof/imx/imx8.c b/sound/soc/sof/imx/imx8.c
index 170740bce839..d777e70250ef 100644
--- a/sound/soc/sof/imx/imx8.c
+++ b/sound/soc/sof/imx/imx8.c
@@ -603,6 +603,7 @@ static struct snd_sof_dsp_ops sof_imx8x_ops = {
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_BATCH |
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP
};
diff --git a/sound/soc/sof/imx/imx8m.c b/sound/soc/sof/imx/imx8m.c
index 2680f061ba42..1b976fa500aa 100644
--- a/sound/soc/sof/imx/imx8m.c
+++ b/sound/soc/sof/imx/imx8m.c
@@ -313,6 +313,13 @@ static struct snd_soc_dai_driver imx8m_dai[] = {
.channels_max = 32,
},
},
+{
+ .name = "micfil",
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+},
};
static int imx8m_dsp_set_power_state(struct snd_sof_dev *sdev,
@@ -465,6 +472,7 @@ static struct snd_sof_dsp_ops sof_imx8m_ops = {
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_BATCH |
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
};
diff --git a/sound/soc/sof/imx/imx8ulp.c b/sound/soc/sof/imx/imx8ulp.c
index ca6edb85ff71..2badca75782b 100644
--- a/sound/soc/sof/imx/imx8ulp.c
+++ b/sound/soc/sof/imx/imx8ulp.c
@@ -463,6 +463,7 @@ static struct snd_sof_dsp_ops sof_imx8ulp_ops = {
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_BATCH |
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
/* PM */
diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c
index 776b66389c34..dee6c7f73e80 100644
--- a/sound/soc/sof/intel/apl.c
+++ b/sound/soc/sof/intel/apl.c
@@ -55,7 +55,7 @@ int sof_apl_ops_init(struct snd_sof_dev *sdev)
if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) {
struct sof_ipc4_fw_data *ipc4_data;
- sdev->private = devm_kzalloc(sdev->dev, sizeof(*ipc4_data), GFP_KERNEL);
+ sdev->private = kzalloc(sizeof(*ipc4_data), GFP_KERNEL);
if (!sdev->private)
return -ENOMEM;
diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c
index 598cf50abadb..85e1e4760d0e 100644
--- a/sound/soc/sof/intel/cnl.c
+++ b/sound/soc/sof/intel/cnl.c
@@ -402,7 +402,7 @@ int sof_cnl_ops_init(struct snd_sof_dev *sdev)
if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) {
struct sof_ipc4_fw_data *ipc4_data;
- sdev->private = devm_kzalloc(sdev->dev, sizeof(*ipc4_data), GFP_KERNEL);
+ sdev->private = kzalloc(sizeof(*ipc4_data), GFP_KERNEL);
if (!sdev->private)
return -ENOMEM;
diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c
index 87935554b1e4..55ce75db23e5 100644
--- a/sound/soc/sof/intel/hda-dai-ops.c
+++ b/sound/soc/sof/intel/hda-dai-ops.c
@@ -208,14 +208,16 @@ static unsigned int hda_calc_stream_format(struct snd_sof_dev *sdev,
struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
unsigned int link_bps;
unsigned int format_val;
+ unsigned int bits;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
link_bps = codec_dai->driver->playback.sig_bits;
else
link_bps = codec_dai->driver->capture.sig_bits;
- format_val = snd_hdac_calc_stream_format(params_rate(params), params_channels(params),
- params_format(params), link_bps, 0);
+ bits = snd_hdac_stream_format_bits(params_format(params), SNDRV_PCM_SUBFORMAT_STD,
+ link_bps);
+ format_val = snd_hdac_stream_format(params_channels(params), bits, params_rate(params));
dev_dbg(sdev->dev, "format_val=%#x, rate=%d, ch=%d, format=%d\n", format_val,
params_rate(params), params_channels(params), params_format(params));
@@ -238,11 +240,11 @@ static unsigned int generic_calc_stream_format(struct snd_sof_dev *sdev,
struct snd_pcm_hw_params *params)
{
unsigned int format_val;
+ unsigned int bits;
- format_val = snd_hdac_calc_stream_format(params_rate(params), params_channels(params),
- params_format(params),
- params_physical_width(params),
- 0);
+ bits = snd_hdac_stream_format_bits(params_format(params), SNDRV_PCM_SUBFORMAT_STD,
+ params_physical_width(params));
+ format_val = snd_hdac_stream_format(params_channels(params), bits, params_rate(params));
dev_dbg(sdev->dev, "format_val=%#x, rate=%d, ch=%d, format=%d\n", format_val,
params_rate(params), params_channels(params), params_format(params));
@@ -258,6 +260,7 @@ static unsigned int dmic_calc_stream_format(struct snd_sof_dev *sdev,
snd_pcm_format_t format;
unsigned int channels;
unsigned int width;
+ unsigned int bits;
channels = params_channels(params);
format = params_format(params);
@@ -269,10 +272,8 @@ static unsigned int dmic_calc_stream_format(struct snd_sof_dev *sdev,
width = 32;
}
- format_val = snd_hdac_calc_stream_format(params_rate(params), channels,
- format,
- width,
- 0);
+ bits = snd_hdac_stream_format_bits(format, SNDRV_PCM_SUBFORMAT_STD, width);
+ format_val = snd_hdac_stream_format(channels, bits, params_rate(params));
dev_dbg(sdev->dev, "format_val=%#x, rate=%d, ch=%d, format=%d\n", format_val,
params_rate(params), channels, format);
diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c
index a20deaf3b428..f4cbc0ad5de3 100644
--- a/sound/soc/sof/intel/hda-dai.c
+++ b/sound/soc/sof/intel/hda-dai.c
@@ -621,6 +621,9 @@ void hda_ops_free(struct snd_sof_dev *sdev)
if (!hda_use_tplg_nhlt)
intel_nhlt_free(ipc4_data->nhlt);
+
+ kfree(sdev->private);
+ sdev->private = NULL;
}
}
EXPORT_SYMBOL_NS(hda_ops_free, SND_SOC_SOF_INTEL_HDA_COMMON);
diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c
index 46fb2d1425e9..b81f231abee3 100644
--- a/sound/soc/sof/intel/hda-loader.c
+++ b/sound/soc/sof/intel/hda-loader.c
@@ -510,9 +510,8 @@ cleanup:
return chip_info->init_core_mask;
/* disable DSP */
- snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR,
- SOF_HDA_REG_PP_PPCTL,
- SOF_HDA_PPCTL_GPROCEN, 0);
+ hda_dsp_ctrl_ppcap_enable(sdev, false);
+
return ret;
}
@@ -520,14 +519,15 @@ int hda_dsp_ipc4_load_library(struct snd_sof_dev *sdev,
struct sof_ipc4_fw_library *fw_lib, bool reload)
{
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
+ struct sof_ipc4_fw_data *ipc4_data = sdev->private;
struct hdac_ext_stream *hext_stream;
struct firmware stripped_firmware;
struct sof_ipc4_msg msg = {};
struct snd_dma_buffer dmab;
int ret, ret1;
- /* IMR booting will restore the libraries as well, skip the loading */
- if (reload && hda->booted_from_imr)
+ /* if IMR booting is enabled and fw context is saved for D3 state, skip the loading */
+ if (reload && hda->booted_from_imr && ipc4_data->fw_context_save)
return 0;
/* the fw_lib has been verified during loading, we can trust the validity here */
diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c
index 744c0dd5766d..fe4ae349dad5 100644
--- a/sound/soc/sof/intel/hda.c
+++ b/sound/soc/sof/intel/hda.c
@@ -1350,8 +1350,7 @@ void hda_dsp_remove(struct snd_sof_dev *sdev)
if (!sdev->dspless_mode_selected) {
/* disable DSP IRQ */
- snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL,
- SOF_HDA_PPCTL_PIE, 0);
+ hda_dsp_ctrl_ppcap_int_enable(sdev, false);
}
/* disable CIE and GIE interrupts */
@@ -1366,8 +1365,7 @@ void hda_dsp_remove(struct snd_sof_dev *sdev)
chip->power_down_dsp(sdev);
/* disable DSP */
- snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL,
- SOF_HDA_PPCTL_GPROCEN, 0);
+ hda_dsp_ctrl_ppcap_enable(sdev, false);
skip_disable_dsp:
free_irq(sdev->ipc_irq, sdev);
diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h
index d628d6a3a7e5..1592e27bc14d 100644
--- a/sound/soc/sof/intel/hda.h
+++ b/sound/soc/sof/intel/hda.h
@@ -882,6 +882,7 @@ extern const struct sof_intel_dsp_desc ehl_chip_info;
extern const struct sof_intel_dsp_desc jsl_chip_info;
extern const struct sof_intel_dsp_desc adls_chip_info;
extern const struct sof_intel_dsp_desc mtl_chip_info;
+extern const struct sof_intel_dsp_desc arl_s_chip_info;
extern const struct sof_intel_dsp_desc lnl_chip_info;
/* Probes support */
diff --git a/sound/soc/sof/intel/icl.c b/sound/soc/sof/intel/icl.c
index 8e29d6bb6fe8..040698591992 100644
--- a/sound/soc/sof/intel/icl.c
+++ b/sound/soc/sof/intel/icl.c
@@ -123,7 +123,7 @@ int sof_icl_ops_init(struct snd_sof_dev *sdev)
if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) {
struct sof_ipc4_fw_data *ipc4_data;
- sdev->private = devm_kzalloc(sdev->dev, sizeof(*ipc4_data), GFP_KERNEL);
+ sdev->private = kzalloc(sizeof(*ipc4_data), GFP_KERNEL);
if (!sdev->private)
return -ENOMEM;
diff --git a/sound/soc/sof/intel/lnl.c b/sound/soc/sof/intel/lnl.c
index db94b45e53af..30712ea05a7a 100644
--- a/sound/soc/sof/intel/lnl.c
+++ b/sound/soc/sof/intel/lnl.c
@@ -120,7 +120,11 @@ int sof_lnl_ops_init(struct snd_sof_dev *sdev)
sof_lnl_ops.get_stream_position = mtl_dsp_get_stream_hda_link_position;
- sdev->private = devm_kzalloc(sdev->dev, sizeof(struct sof_ipc4_fw_data), GFP_KERNEL);
+ /* dsp core get/put */
+ sof_lnl_ops.core_get = mtl_dsp_core_get;
+ sof_lnl_ops.core_put = mtl_dsp_core_put;
+
+ sdev->private = kzalloc(sizeof(struct sof_ipc4_fw_data), GFP_KERNEL);
if (!sdev->private)
return -ENOMEM;
@@ -129,6 +133,8 @@ int sof_lnl_ops_init(struct snd_sof_dev *sdev)
ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_2;
+ ipc4_data->fw_context_save = true;
+
/* External library loading support */
ipc4_data->load_library = hda_dsp_ipc4_load_library;
diff --git a/sound/soc/sof/intel/mtl.c b/sound/soc/sof/intel/mtl.c
index 254dbbeac1d0..df05dc77b8d5 100644
--- a/sound/soc/sof/intel/mtl.c
+++ b/sound/soc/sof/intel/mtl.c
@@ -440,7 +440,8 @@ int mtl_dsp_cl_init(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot)
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
const struct sof_intel_dsp_desc *chip = hda->desc;
unsigned int status;
- u32 ipc_hdr;
+ u32 ipc_hdr, flags;
+ char *dump_msg;
int ret;
/* step 1: purge FW request */
@@ -493,8 +494,18 @@ int mtl_dsp_cl_init(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot)
return 0;
err:
- snd_sof_dsp_dbg_dump(sdev, "MTL DSP init fail", 0);
+ flags = SOF_DBG_DUMP_PCI | SOF_DBG_DUMP_MBOX | SOF_DBG_DUMP_OPTIONAL;
+
+ /* after max boot attempts make sure that the dump is printed */
+ if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS)
+ flags &= ~SOF_DBG_DUMP_OPTIONAL;
+
+ dump_msg = kasprintf(GFP_KERNEL, "Boot iteration failed: %d/%d",
+ hda->boot_iteration, HDA_FW_BOOT_ATTEMPTS);
+ snd_sof_dsp_dbg_dump(sdev, dump_msg, flags);
mtl_dsp_core_power_down(sdev, SOF_DSP_PRIMARY_CORE);
+
+ kfree(dump_msg);
return ret;
}
@@ -627,7 +638,7 @@ u64 mtl_dsp_get_stream_hda_link_position(struct snd_sof_dev *sdev,
return ((u64)llp_u << 32) | llp_l;
}
-static int mtl_dsp_core_get(struct snd_sof_dev *sdev, int core)
+int mtl_dsp_core_get(struct snd_sof_dev *sdev, int core)
{
const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm;
@@ -640,7 +651,7 @@ static int mtl_dsp_core_get(struct snd_sof_dev *sdev, int core)
return 0;
}
-static int mtl_dsp_core_put(struct snd_sof_dev *sdev, int core)
+int mtl_dsp_core_put(struct snd_sof_dev *sdev, int core)
{
const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm;
int ret;
@@ -698,7 +709,7 @@ int sof_mtl_ops_init(struct snd_sof_dev *sdev)
sof_mtl_ops.get_stream_position = mtl_dsp_get_stream_hda_link_position;
- sdev->private = devm_kzalloc(sdev->dev, sizeof(struct sof_ipc4_fw_data), GFP_KERNEL);
+ sdev->private = kzalloc(sizeof(struct sof_ipc4_fw_data), GFP_KERNEL);
if (!sdev->private)
return -ENOMEM;
@@ -707,6 +718,8 @@ int sof_mtl_ops_init(struct snd_sof_dev *sdev)
ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_2;
+ ipc4_data->fw_context_save = true;
+
/* External library loading support */
ipc4_data->load_library = hda_dsp_ipc4_load_library;
@@ -746,3 +759,31 @@ const struct sof_intel_dsp_desc mtl_chip_info = {
.hw_ip_version = SOF_INTEL_ACE_1_0,
};
EXPORT_SYMBOL_NS(mtl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON);
+
+const struct sof_intel_dsp_desc arl_s_chip_info = {
+ .cores_num = 2,
+ .init_core_mask = BIT(0),
+ .host_managed_cores_mask = BIT(0),
+ .ipc_req = MTL_DSP_REG_HFIPCXIDR,
+ .ipc_req_mask = MTL_DSP_REG_HFIPCXIDR_BUSY,
+ .ipc_ack = MTL_DSP_REG_HFIPCXIDA,
+ .ipc_ack_mask = MTL_DSP_REG_HFIPCXIDA_DONE,
+ .ipc_ctl = MTL_DSP_REG_HFIPCXCTL,
+ .rom_status_reg = MTL_DSP_ROM_STS,
+ .rom_init_timeout = 300,
+ .ssp_count = MTL_SSP_COUNT,
+ .ssp_base_offset = CNL_SSP_BASE_OFFSET,
+ .sdw_shim_base = SDW_SHIM_BASE_ACE,
+ .sdw_alh_base = SDW_ALH_BASE_ACE,
+ .d0i3_offset = MTL_HDA_VS_D0I3C,
+ .read_sdw_lcount = hda_sdw_check_lcount_common,
+ .enable_sdw_irq = mtl_enable_sdw_irq,
+ .check_sdw_irq = mtl_dsp_check_sdw_irq,
+ .check_sdw_wakeen_irq = hda_sdw_check_wakeen_irq_common,
+ .check_ipc_irq = mtl_dsp_check_ipc_irq,
+ .cl_init = mtl_dsp_cl_init,
+ .power_down_dsp = mtl_power_down_dsp,
+ .disable_interrupts = mtl_dsp_disable_interrupts,
+ .hw_ip_version = SOF_INTEL_ACE_1_0,
+};
+EXPORT_SYMBOL_NS(arl_s_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON);
diff --git a/sound/soc/sof/intel/mtl.h b/sound/soc/sof/intel/mtl.h
index 95696b3d7c4c..cc5a1f46fd09 100644
--- a/sound/soc/sof/intel/mtl.h
+++ b/sound/soc/sof/intel/mtl.h
@@ -106,3 +106,6 @@ void mtl_ipc_dump(struct snd_sof_dev *sdev);
u64 mtl_dsp_get_stream_hda_link_position(struct snd_sof_dev *sdev,
struct snd_soc_component *component,
struct snd_pcm_substream *substream);
+
+int mtl_dsp_core_get(struct snd_sof_dev *sdev, int core);
+int mtl_dsp_core_put(struct snd_sof_dev *sdev, int core);
diff --git a/sound/soc/sof/intel/pci-lnl.c b/sound/soc/sof/intel/pci-lnl.c
index 78a57eb9cbc3..b26ffe767fab 100644
--- a/sound/soc/sof/intel/pci-lnl.c
+++ b/sound/soc/sof/intel/pci-lnl.c
@@ -36,7 +36,7 @@ static const struct sof_dev_desc lnl_desc = {
[SOF_IPC_TYPE_4] = "intel/sof-ipc4/lnl",
},
.default_tplg_path = {
- [SOF_IPC_TYPE_4] = "intel/sof-ace-tplg",
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-tplg",
},
.default_fw_filename = {
[SOF_IPC_TYPE_4] = "sof-lnl.ri",
diff --git a/sound/soc/sof/intel/pci-mtl.c b/sound/soc/sof/intel/pci-mtl.c
index 0f378f45486d..cacc985d80f4 100644
--- a/sound/soc/sof/intel/pci-mtl.c
+++ b/sound/soc/sof/intel/pci-mtl.c
@@ -80,10 +80,41 @@ static const struct sof_dev_desc arl_desc = {
.ops_free = hda_ops_free,
};
+static const struct sof_dev_desc arl_s_desc = {
+ .use_acpi_target_states = true,
+ .machines = snd_soc_acpi_intel_arl_machines,
+ .alt_machines = snd_soc_acpi_intel_arl_sdw_machines,
+ .resindex_lpe_base = 0,
+ .resindex_pcicfg_base = -1,
+ .resindex_imr_base = -1,
+ .irqindex_host_ipc = -1,
+ .chip_info = &arl_s_chip_info,
+ .ipc_supported_mask = BIT(SOF_IPC_TYPE_4),
+ .ipc_default = SOF_IPC_TYPE_4,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
+ .default_fw_path = {
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4/arl-s",
+ },
+ .default_lib_path = {
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-lib/arl-s",
+ },
+ .default_tplg_path = {
+ [SOF_IPC_TYPE_4] = "intel/sof-ace-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC_TYPE_4] = "sof-arl-s.ri",
+ },
+ .nocodec_tplg_filename = "sof-arl-nocodec.tplg",
+ .ops = &sof_mtl_ops,
+ .ops_init = sof_mtl_ops_init,
+ .ops_free = hda_ops_free,
+};
+
/* PCI IDs */
static const struct pci_device_id sof_pci_ids[] = {
{ PCI_DEVICE_DATA(INTEL, HDA_MTL, &mtl_desc) },
- { PCI_DEVICE_DATA(INTEL, HDA_ARL_S, &arl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_ARL_S, &arl_s_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_ARL, &arl_desc) },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, sof_pci_ids);
diff --git a/sound/soc/sof/intel/pci-tgl.c b/sound/soc/sof/intel/pci-tgl.c
index 0660d4b2ac96..a361ee9d1107 100644
--- a/sound/soc/sof/intel/pci-tgl.c
+++ b/sound/soc/sof/intel/pci-tgl.c
@@ -33,18 +33,18 @@ static const struct sof_dev_desc tgl_desc = {
.dspless_mode_supported = true, /* Only supported for HDaudio */
.default_fw_path = {
[SOF_IPC_TYPE_3] = "intel/sof",
- [SOF_IPC_TYPE_4] = "intel/avs/tgl",
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4/tgl",
},
.default_lib_path = {
- [SOF_IPC_TYPE_4] = "intel/avs-lib/tgl",
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-lib/tgl",
},
.default_tplg_path = {
[SOF_IPC_TYPE_3] = "intel/sof-tplg",
- [SOF_IPC_TYPE_4] = "intel/avs-tplg",
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-tplg",
},
.default_fw_filename = {
[SOF_IPC_TYPE_3] = "sof-tgl.ri",
- [SOF_IPC_TYPE_4] = "dsp_basefw.bin",
+ [SOF_IPC_TYPE_4] = "sof-tgl.ri",
},
.nocodec_tplg_filename = "sof-tgl-nocodec.tplg",
.ops = &sof_tgl_ops,
@@ -66,18 +66,18 @@ static const struct sof_dev_desc tglh_desc = {
.dspless_mode_supported = true, /* Only supported for HDaudio */
.default_fw_path = {
[SOF_IPC_TYPE_3] = "intel/sof",
- [SOF_IPC_TYPE_4] = "intel/avs/tgl-h",
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4/tgl-h",
},
.default_lib_path = {
- [SOF_IPC_TYPE_4] = "intel/avs-lib/tgl-h",
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-lib/tgl-h",
},
.default_tplg_path = {
[SOF_IPC_TYPE_3] = "intel/sof-tplg",
- [SOF_IPC_TYPE_4] = "intel/avs-tplg",
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-tplg",
},
.default_fw_filename = {
[SOF_IPC_TYPE_3] = "sof-tgl-h.ri",
- [SOF_IPC_TYPE_4] = "dsp_basefw.bin",
+ [SOF_IPC_TYPE_4] = "sof-tgl-h.ri",
},
.nocodec_tplg_filename = "sof-tgl-nocodec.tplg",
.ops = &sof_tgl_ops,
@@ -98,18 +98,18 @@ static const struct sof_dev_desc ehl_desc = {
.dspless_mode_supported = true, /* Only supported for HDaudio */
.default_fw_path = {
[SOF_IPC_TYPE_3] = "intel/sof",
- [SOF_IPC_TYPE_4] = "intel/avs/ehl",
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4/ehl",
},
.default_lib_path = {
- [SOF_IPC_TYPE_4] = "intel/avs-lib/ehl",
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-lib/ehl",
},
.default_tplg_path = {
[SOF_IPC_TYPE_3] = "intel/sof-tplg",
- [SOF_IPC_TYPE_4] = "intel/avs-tplg",
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-tplg",
},
.default_fw_filename = {
[SOF_IPC_TYPE_3] = "sof-ehl.ri",
- [SOF_IPC_TYPE_4] = "dsp_basefw.bin",
+ [SOF_IPC_TYPE_4] = "sof-ehl.ri",
},
.nocodec_tplg_filename = "sof-ehl-nocodec.tplg",
.ops = &sof_tgl_ops,
@@ -131,18 +131,18 @@ static const struct sof_dev_desc adls_desc = {
.dspless_mode_supported = true, /* Only supported for HDaudio */
.default_fw_path = {
[SOF_IPC_TYPE_3] = "intel/sof",
- [SOF_IPC_TYPE_4] = "intel/avs/adl-s",
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4/adl-s",
},
.default_lib_path = {
- [SOF_IPC_TYPE_4] = "intel/avs-lib/adl-s",
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-lib/adl-s",
},
.default_tplg_path = {
[SOF_IPC_TYPE_3] = "intel/sof-tplg",
- [SOF_IPC_TYPE_4] = "intel/avs-tplg",
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-tplg",
},
.default_fw_filename = {
[SOF_IPC_TYPE_3] = "sof-adl-s.ri",
- [SOF_IPC_TYPE_4] = "dsp_basefw.bin",
+ [SOF_IPC_TYPE_4] = "sof-adl-s.ri",
},
.nocodec_tplg_filename = "sof-adl-nocodec.tplg",
.ops = &sof_tgl_ops,
@@ -164,18 +164,18 @@ static const struct sof_dev_desc adl_desc = {
.dspless_mode_supported = true, /* Only supported for HDaudio */
.default_fw_path = {
[SOF_IPC_TYPE_3] = "intel/sof",
- [SOF_IPC_TYPE_4] = "intel/avs/adl",
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4/adl",
},
.default_lib_path = {
- [SOF_IPC_TYPE_4] = "intel/avs-lib/adl",
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-lib/adl",
},
.default_tplg_path = {
[SOF_IPC_TYPE_3] = "intel/sof-tplg",
- [SOF_IPC_TYPE_4] = "intel/avs-tplg",
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-tplg",
},
.default_fw_filename = {
[SOF_IPC_TYPE_3] = "sof-adl.ri",
- [SOF_IPC_TYPE_4] = "dsp_basefw.bin",
+ [SOF_IPC_TYPE_4] = "sof-adl.ri",
},
.nocodec_tplg_filename = "sof-adl-nocodec.tplg",
.ops = &sof_tgl_ops,
@@ -197,18 +197,18 @@ static const struct sof_dev_desc adl_n_desc = {
.dspless_mode_supported = true, /* Only supported for HDaudio */
.default_fw_path = {
[SOF_IPC_TYPE_3] = "intel/sof",
- [SOF_IPC_TYPE_4] = "intel/avs/adl-n",
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4/adl-n",
},
.default_lib_path = {
- [SOF_IPC_TYPE_4] = "intel/avs-lib/adl-n",
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-lib/adl-n",
},
.default_tplg_path = {
[SOF_IPC_TYPE_3] = "intel/sof-tplg",
- [SOF_IPC_TYPE_4] = "intel/avs-tplg",
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-tplg",
},
.default_fw_filename = {
[SOF_IPC_TYPE_3] = "sof-adl-n.ri",
- [SOF_IPC_TYPE_4] = "dsp_basefw.bin",
+ [SOF_IPC_TYPE_4] = "sof-adl-n.ri",
},
.nocodec_tplg_filename = "sof-adl-nocodec.tplg",
.ops = &sof_tgl_ops,
@@ -230,18 +230,18 @@ static const struct sof_dev_desc rpls_desc = {
.dspless_mode_supported = true, /* Only supported for HDaudio */
.default_fw_path = {
[SOF_IPC_TYPE_3] = "intel/sof",
- [SOF_IPC_TYPE_4] = "intel/avs/rpl-s",
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4/rpl-s",
},
.default_lib_path = {
- [SOF_IPC_TYPE_4] = "intel/avs-lib/rpl-s",
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-lib/rpl-s",
},
.default_tplg_path = {
[SOF_IPC_TYPE_3] = "intel/sof-tplg",
- [SOF_IPC_TYPE_4] = "intel/avs-tplg",
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-tplg",
},
.default_fw_filename = {
[SOF_IPC_TYPE_3] = "sof-rpl-s.ri",
- [SOF_IPC_TYPE_4] = "dsp_basefw.bin",
+ [SOF_IPC_TYPE_4] = "sof-rpl-s.ri",
},
.nocodec_tplg_filename = "sof-rpl-nocodec.tplg",
.ops = &sof_tgl_ops,
@@ -263,18 +263,18 @@ static const struct sof_dev_desc rpl_desc = {
.dspless_mode_supported = true, /* Only supported for HDaudio */
.default_fw_path = {
[SOF_IPC_TYPE_3] = "intel/sof",
- [SOF_IPC_TYPE_4] = "intel/avs/rpl",
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4/rpl",
},
.default_lib_path = {
- [SOF_IPC_TYPE_4] = "intel/avs-lib/rpl",
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-lib/rpl",
},
.default_tplg_path = {
[SOF_IPC_TYPE_3] = "intel/sof-tplg",
- [SOF_IPC_TYPE_4] = "intel/avs-tplg",
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-tplg",
},
.default_fw_filename = {
[SOF_IPC_TYPE_3] = "sof-rpl.ri",
- [SOF_IPC_TYPE_4] = "dsp_basefw.bin",
+ [SOF_IPC_TYPE_4] = "sof-rpl.ri",
},
.nocodec_tplg_filename = "sof-rpl-nocodec.tplg",
.ops = &sof_tgl_ops,
diff --git a/sound/soc/sof/intel/skl.c b/sound/soc/sof/intel/skl.c
index d24e64e71b58..93824e6ce573 100644
--- a/sound/soc/sof/intel/skl.c
+++ b/sound/soc/sof/intel/skl.c
@@ -62,7 +62,7 @@ int sof_skl_ops_init(struct snd_sof_dev *sdev)
/* probe/remove/shutdown */
sof_skl_ops.shutdown = hda_dsp_shutdown;
- sdev->private = devm_kzalloc(sdev->dev, sizeof(*ipc4_data), GFP_KERNEL);
+ sdev->private = kzalloc(sizeof(*ipc4_data), GFP_KERNEL);
if (!sdev->private)
return -ENOMEM;
diff --git a/sound/soc/sof/intel/tgl.c b/sound/soc/sof/intel/tgl.c
index f7de1f5ba06d..c2bb04c89b9d 100644
--- a/sound/soc/sof/intel/tgl.c
+++ b/sound/soc/sof/intel/tgl.c
@@ -82,7 +82,7 @@ int sof_tgl_ops_init(struct snd_sof_dev *sdev)
if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) {
struct sof_ipc4_fw_data *ipc4_data;
- sdev->private = devm_kzalloc(sdev->dev, sizeof(*ipc4_data), GFP_KERNEL);
+ sdev->private = kzalloc(sizeof(*ipc4_data), GFP_KERNEL);
if (!sdev->private)
return -ENOMEM;
@@ -91,6 +91,8 @@ int sof_tgl_ops_init(struct snd_sof_dev *sdev)
ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_2;
+ ipc4_data->fw_context_save = true;
+
/* External library loading support */
ipc4_data->load_library = hda_dsp_ipc4_load_library;
diff --git a/sound/soc/sof/ipc3-pcm.c b/sound/soc/sof/ipc3-pcm.c
index 2d0addcbc819..330f04bcd75d 100644
--- a/sound/soc/sof/ipc3-pcm.c
+++ b/sound/soc/sof/ipc3-pcm.c
@@ -384,6 +384,17 @@ static int sof_ipc3_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
dev_dbg(component->dev, "AMD_DMIC channels_min: %d channels_max: %d\n",
channels->min, channels->max);
break;
+ case SOF_DAI_IMX_MICFIL:
+ rate->min = private->dai_config->micfil.pdm_rate;
+ rate->max = private->dai_config->micfil.pdm_rate;
+ channels->min = private->dai_config->micfil.pdm_ch;
+ channels->max = private->dai_config->micfil.pdm_ch;
+
+ dev_dbg(component->dev,
+ "MICFIL PDM rate_min: %d rate_max: %d\n", rate->min, rate->max);
+ dev_dbg(component->dev, "MICFIL PDM channels_min: %d channels_max: %d\n",
+ channels->min, channels->max);
+ break;
default:
dev_err(component->dev, "Invalid DAI type %d\n", private->dai_config->type);
break;
diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c
index 2c7a5e7a364c..d47698f4be2d 100644
--- a/sound/soc/sof/ipc3-topology.c
+++ b/sound/soc/sof/ipc3-topology.c
@@ -72,6 +72,8 @@ static const struct sof_topology_token buffer_tokens[] = {
offsetof(struct sof_ipc_buffer, size)},
{SOF_TKN_BUF_CAPS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
offsetof(struct sof_ipc_buffer, caps)},
+ {SOF_TKN_BUF_FLAGS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_buffer, flags)},
};
/* DAI */
@@ -286,6 +288,16 @@ static const struct sof_topology_token acpi2s_tokens[] = {
offsetof(struct sof_ipc_dai_acp_params, tdm_mode)},
};
+/* MICFIL PDM */
+static const struct sof_topology_token micfil_pdm_tokens[] = {
+ {SOF_TKN_IMX_MICFIL_RATE,
+ SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_dai_micfil_params, pdm_rate)},
+ {SOF_TKN_IMX_MICFIL_CH,
+ SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_dai_micfil_params, pdm_ch)},
+};
+
/* Core tokens */
static const struct sof_topology_token core_tokens[] = {
{SOF_TKN_COMP_CORE_ID, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
@@ -322,6 +334,8 @@ static const struct sof_token_info ipc3_token_list[SOF_TOKEN_COUNT] = {
[SOF_AFE_TOKENS] = {"AFE tokens", afe_tokens, ARRAY_SIZE(afe_tokens)},
[SOF_ACPDMIC_TOKENS] = {"ACPDMIC tokens", acpdmic_tokens, ARRAY_SIZE(acpdmic_tokens)},
[SOF_ACPI2S_TOKENS] = {"ACPI2S tokens", acpi2s_tokens, ARRAY_SIZE(acpi2s_tokens)},
+ [SOF_MICFIL_TOKENS] = {"MICFIL PDM tokens",
+ micfil_pdm_tokens, ARRAY_SIZE(micfil_pdm_tokens)},
};
/**
@@ -1138,6 +1152,37 @@ static int sof_link_esai_load(struct snd_soc_component *scomp, struct snd_sof_da
return 0;
}
+static int sof_link_micfil_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink,
+ struct sof_ipc_dai_config *config, struct snd_sof_dai *dai)
+{
+ struct snd_soc_tplg_hw_config *hw_config = slink->hw_configs;
+ struct sof_dai_private_data *private = dai->private;
+ u32 size = sizeof(*config);
+ int ret;
+
+ /* handle master/slave and inverted clocks */
+ sof_dai_set_format(hw_config, config);
+
+ config->hdr.size = size;
+
+ /* parse the required set of MICFIL PDM tokens based on num_hw_cfgs */
+ ret = sof_update_ipc_object(scomp, &config->micfil, SOF_MICFIL_TOKENS, slink->tuples,
+ slink->num_tuples, size, slink->num_hw_configs);
+ if (ret < 0)
+ return ret;
+
+ dev_info(scomp->dev, "MICFIL PDM config dai_index %d channel %d rate %d\n",
+ config->dai_index, config->micfil.pdm_ch, config->micfil.pdm_rate);
+
+ dai->number_configs = 1;
+ dai->current_config = 0;
+ private->dai_config = kmemdup(config, size, GFP_KERNEL);
+ if (!private->dai_config)
+ return -ENOMEM;
+
+ return 0;
+}
+
static int sof_link_acp_dmic_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink,
struct sof_ipc_dai_config *config, struct snd_sof_dai *dai)
{
@@ -1176,6 +1221,7 @@ static int sof_link_acp_bt_load(struct snd_soc_component *scomp, struct snd_sof_
struct snd_soc_tplg_hw_config *hw_config = slink->hw_configs;
struct sof_dai_private_data *private = dai->private;
u32 size = sizeof(*config);
+ int ret;
/* handle master/slave and inverted clocks */
sof_dai_set_format(hw_config, config);
@@ -1184,12 +1230,14 @@ static int sof_link_acp_bt_load(struct snd_soc_component *scomp, struct snd_sof_
memset(&config->acpbt, 0, sizeof(config->acpbt));
config->hdr.size = size;
- config->acpbt.fsync_rate = le32_to_cpu(hw_config->fsync_rate);
- config->acpbt.tdm_slots = le32_to_cpu(hw_config->tdm_slots);
+ ret = sof_update_ipc_object(scomp, &config->acpbt, SOF_ACPI2S_TOKENS, slink->tuples,
+ slink->num_tuples, size, slink->num_hw_configs);
+ if (ret < 0)
+ return ret;
- dev_info(scomp->dev, "ACP_BT config ACP%d channel %d rate %d\n",
+ dev_info(scomp->dev, "ACP_BT config ACP%d channel %d rate %d tdm_mode %d\n",
config->dai_index, config->acpbt.tdm_slots,
- config->acpbt.fsync_rate);
+ config->acpbt.fsync_rate, config->acpbt.tdm_mode);
dai->number_configs = 1;
dai->current_config = 0;
@@ -1561,6 +1609,9 @@ static int sof_ipc3_widget_setup_comp_dai(struct snd_sof_widget *swidget)
case SOF_DAI_IMX_ESAI:
ret = sof_link_esai_load(scomp, slink, config, dai);
break;
+ case SOF_DAI_IMX_MICFIL:
+ ret = sof_link_micfil_load(scomp, slink, config, dai);
+ break;
case SOF_DAI_AMD_BT:
ret = sof_link_acp_bt_load(scomp, slink, config, dai);
break;
@@ -2309,27 +2360,16 @@ static int sof_tear_down_left_over_pipelines(struct snd_sof_dev *sdev)
return 0;
}
-/*
- * For older firmware, this function doesn't free widgets for static pipelines during suspend.
- * It only resets use_count for all widgets.
- */
-static int sof_ipc3_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verify)
+static int sof_ipc3_free_widgets_in_list(struct snd_sof_dev *sdev, bool include_scheduler,
+ bool *dyn_widgets, bool verify)
{
struct sof_ipc_fw_version *v = &sdev->fw_ready.version;
struct snd_sof_widget *swidget;
- struct snd_sof_route *sroute;
- bool dyn_widgets = false;
int ret;
- /*
- * This function is called during suspend and for one-time topology verification during
- * first boot. In both cases, there is no need to protect swidget->use_count and
- * sroute->setup because during suspend all running streams are suspended and during
- * topology loading the sound card unavailable to open PCMs.
- */
list_for_each_entry(swidget, &sdev->widget_list, list) {
if (swidget->dynamic_pipeline_widget) {
- dyn_widgets = true;
+ *dyn_widgets = true;
continue;
}
@@ -2344,11 +2384,49 @@ static int sof_ipc3_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verif
continue;
}
+ if (include_scheduler && swidget->id != snd_soc_dapm_scheduler)
+ continue;
+
+ if (!include_scheduler && swidget->id == snd_soc_dapm_scheduler)
+ continue;
+
ret = sof_widget_free(sdev, swidget);
if (ret < 0)
return ret;
}
+ return 0;
+}
+
+/*
+ * For older firmware, this function doesn't free widgets for static pipelines during suspend.
+ * It only resets use_count for all widgets.
+ */
+static int sof_ipc3_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verify)
+{
+ struct sof_ipc_fw_version *v = &sdev->fw_ready.version;
+ struct snd_sof_widget *swidget;
+ struct snd_sof_route *sroute;
+ bool dyn_widgets = false;
+ int ret;
+
+ /*
+ * This function is called during suspend and for one-time topology verification during
+ * first boot. In both cases, there is no need to protect swidget->use_count and
+ * sroute->setup because during suspend all running streams are suspended and during
+ * topology loading the sound card unavailable to open PCMs. Do not free the scheduler
+ * widgets yet so that the secondary cores do not get powered down before all the widgets
+ * associated with the scheduler are freed.
+ */
+ ret = sof_ipc3_free_widgets_in_list(sdev, false, &dyn_widgets, verify);
+ if (ret < 0)
+ return ret;
+
+ /* free all the scheduler widgets now */
+ ret = sof_ipc3_free_widgets_in_list(sdev, true, &dyn_widgets, verify);
+ if (ret < 0)
+ return ret;
+
/*
* Tear down all pipelines associated with PCMs that did not get suspended
* and unset the prepare flag so that they can be set up again during resume.
diff --git a/sound/soc/sof/ipc3.c b/sound/soc/sof/ipc3.c
index fb40378ad084..c03dd513fbff 100644
--- a/sound/soc/sof/ipc3.c
+++ b/sound/soc/sof/ipc3.c
@@ -1067,7 +1067,7 @@ static void sof_ipc3_rx_msg(struct snd_sof_dev *sdev)
return;
}
- if (hdr.size < sizeof(hdr)) {
+ if (hdr.size < sizeof(hdr) || hdr.size > SOF_IPC_MSG_MAX_SIZE) {
dev_err(sdev->dev, "The received message size is invalid\n");
return;
}
diff --git a/sound/soc/sof/ipc4-control.c b/sound/soc/sof/ipc4-control.c
index b4cdcec33e12..1be9519de909 100644
--- a/sound/soc/sof/ipc4-control.c
+++ b/sound/soc/sof/ipc4-control.c
@@ -240,6 +240,50 @@ sof_ipc4_set_generic_control_data(struct snd_sof_dev *sdev,
return ret;
}
+static void sof_ipc4_refresh_generic_control(struct snd_sof_control *scontrol)
+{
+ struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
+ struct snd_soc_component *scomp = scontrol->scomp;
+ struct sof_ipc4_control_msg_payload *data;
+ struct sof_ipc4_msg *msg = &cdata->msg;
+ size_t data_size;
+ unsigned int i;
+ int ret;
+
+ if (!scontrol->comp_data_dirty)
+ return;
+
+ if (!pm_runtime_active(scomp->dev))
+ return;
+
+ data_size = struct_size(data, chanv, scontrol->num_channels);
+ data = kmalloc(data_size, GFP_KERNEL);
+ if (!data)
+ return;
+
+ data->id = cdata->index;
+ data->num_elems = scontrol->num_channels;
+ msg->data_ptr = data;
+ msg->data_size = data_size;
+
+ scontrol->comp_data_dirty = false;
+ ret = sof_ipc4_set_get_kcontrol_data(scontrol, false, true);
+ msg->data_ptr = NULL;
+ msg->data_size = 0;
+ if (!ret) {
+ for (i = 0; i < scontrol->num_channels; i++) {
+ cdata->chanv[i].channel = data->chanv[i].channel;
+ cdata->chanv[i].value = data->chanv[i].value;
+ }
+ } else {
+ dev_err(scomp->dev, "Failed to read control data for %s\n",
+ scontrol->name);
+ scontrol->comp_data_dirty = true;
+ }
+
+ kfree(data);
+}
+
static bool sof_ipc4_switch_put(struct snd_sof_control *scontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -290,6 +334,8 @@ static int sof_ipc4_switch_get(struct snd_sof_control *scontrol,
struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
unsigned int i;
+ sof_ipc4_refresh_generic_control(scontrol);
+
/* read back each channel */
for (i = 0; i < scontrol->num_channels; i++)
ucontrol->value.integer.value[i] = cdata->chanv[i].value;
@@ -347,6 +393,8 @@ static int sof_ipc4_enum_get(struct snd_sof_control *scontrol,
struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
unsigned int i;
+ sof_ipc4_refresh_generic_control(scontrol);
+
/* read back each channel */
for (i = 0; i < scontrol->num_channels; i++)
ucontrol->value.enumerated.item[i] = cdata->chanv[i].value;
@@ -601,6 +649,136 @@ sof_ipc4_volsw_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget,
return sof_ipc4_set_volume_data(sdev, swidget, scontrol, false);
}
+#define PARAM_ID_FROM_EXTENSION(_ext) (((_ext) & SOF_IPC4_MOD_EXT_MSG_PARAM_ID_MASK) \
+ >> SOF_IPC4_MOD_EXT_MSG_PARAM_ID_SHIFT)
+
+static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message)
+{
+ struct sof_ipc4_msg *ipc4_msg = ipc_message;
+ struct sof_ipc4_notify_module_data *ndata = ipc4_msg->data_ptr;
+ struct sof_ipc4_control_msg_payload *msg_data;
+ struct sof_ipc4_control_data *cdata;
+ struct snd_soc_dapm_widget *widget;
+ struct snd_sof_control *scontrol;
+ struct snd_sof_widget *swidget;
+ struct snd_kcontrol *kc = NULL;
+ bool scontrol_found = false;
+ u32 event_param_id;
+ int i, type;
+
+ if (ndata->event_data_size < sizeof(*msg_data)) {
+ dev_err(sdev->dev,
+ "%s: Invalid event data size for module %u.%u: %u\n",
+ __func__, ndata->module_id, ndata->instance_id,
+ ndata->event_data_size);
+ return;
+ }
+
+ event_param_id = ndata->event_id & SOF_IPC4_NOTIFY_MODULE_EVENTID_ALSA_PARAMID_MASK;
+ switch (event_param_id) {
+ case SOF_IPC4_SWITCH_CONTROL_PARAM_ID:
+ type = SND_SOC_TPLG_TYPE_MIXER;
+ break;
+ case SOF_IPC4_ENUM_CONTROL_PARAM_ID:
+ type = SND_SOC_TPLG_TYPE_ENUM;
+ break;
+ default:
+ dev_err(sdev->dev,
+ "%s: Invalid control type for module %u.%u: %u\n",
+ __func__, ndata->module_id, ndata->instance_id,
+ event_param_id);
+ return;
+ }
+
+ /* Find the swidget based on ndata->module_id and ndata->instance_id */
+ swidget = sof_ipc4_find_swidget_by_ids(sdev, ndata->module_id,
+ ndata->instance_id);
+ if (!swidget) {
+ dev_err(sdev->dev, "%s: Failed to find widget for module %u.%u\n",
+ __func__, ndata->module_id, ndata->instance_id);
+ return;
+ }
+
+ /* Find the scontrol which is the source of the notification */
+ msg_data = (struct sof_ipc4_control_msg_payload *)ndata->event_data;
+ list_for_each_entry(scontrol, &sdev->kcontrol_list, list) {
+ if (scontrol->comp_id == swidget->comp_id) {
+ u32 local_param_id;
+
+ cdata = scontrol->ipc_control_data;
+ /*
+ * The scontrol's param_id is stored in the IPC message
+ * template's extension
+ */
+ local_param_id = PARAM_ID_FROM_EXTENSION(cdata->msg.extension);
+ if (local_param_id == event_param_id &&
+ msg_data->id == cdata->index) {
+ scontrol_found = true;
+ break;
+ }
+ }
+ }
+
+ if (!scontrol_found) {
+ dev_err(sdev->dev,
+ "%s: Failed to find control on widget %s: %u:%u\n",
+ __func__, swidget->widget->name, ndata->event_id & 0xffff,
+ msg_data->id);
+ return;
+ }
+
+ if (msg_data->num_elems) {
+ /*
+ * The message includes the updated value/data, update the
+ * control's local cache using the received notification
+ */
+ for (i = 0; i < msg_data->num_elems; i++) {
+ u32 channel = msg_data->chanv[i].channel;
+
+ if (channel >= scontrol->num_channels) {
+ dev_warn(sdev->dev,
+ "Invalid channel index for %s: %u\n",
+ scontrol->name, i);
+
+ /*
+ * Mark the scontrol as dirty to force a refresh
+ * on next read
+ */
+ scontrol->comp_data_dirty = true;
+ break;
+ }
+
+ cdata->chanv[channel].value = msg_data->chanv[i].value;
+ }
+ } else {
+ /*
+ * Mark the scontrol as dirty because the value/data is changed
+ * in firmware, forcing a refresh on next read access
+ */
+ scontrol->comp_data_dirty = true;
+ }
+
+ /*
+ * Look up the ALSA kcontrol of the scontrol to be able to send a
+ * notification to user space
+ */
+ widget = swidget->widget;
+ for (i = 0; i < widget->num_kcontrols; i++) {
+ /* skip non matching types or non matching indexes within type */
+ if (widget->dobj.widget.kcontrol_type[i] == type &&
+ widget->kcontrol_news[i].index == cdata->index) {
+ kc = widget->kcontrols[i];
+ break;
+ }
+ }
+
+ if (!kc)
+ return;
+
+ snd_ctl_notify_one(swidget->scomp->card->snd_card,
+ SNDRV_CTL_EVENT_MASK_VALUE, kc, 0);
+}
+
/* set up all controls for the widget */
static int sof_ipc4_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
{
@@ -674,6 +852,7 @@ const struct sof_ipc_tplg_control_ops tplg_ipc4_control_ops = {
.bytes_ext_put = sof_ipc4_bytes_ext_put,
.bytes_ext_get = sof_ipc4_bytes_ext_get,
.bytes_ext_volatile_get = sof_ipc4_bytes_ext_volatile_get,
+ .update = sof_ipc4_control_update,
.widget_kcontrol_setup = sof_ipc4_widget_kcontrol_setup,
.set_up_volume_table = sof_ipc4_set_up_volume_table,
};
diff --git a/sound/soc/sof/ipc4-loader.c b/sound/soc/sof/ipc4-loader.c
index eaa04762eb11..c79479afa8d0 100644
--- a/sound/soc/sof/ipc4-loader.c
+++ b/sound/soc/sof/ipc4-loader.c
@@ -391,6 +391,9 @@ int sof_ipc4_query_fw_configuration(struct snd_sof_dev *sdev)
goto out;
}
break;
+ case SOF_IPC4_FW_CONTEXT_SAVE:
+ ipc4_data->fw_context_save = *tuple->value;
+ break;
default:
break;
}
@@ -479,13 +482,10 @@ void sof_ipc4_update_cpc_from_manifest(struct snd_sof_dev *sdev,
msg = "No CPC match in the firmware file's manifest";
no_cpc:
- dev_warn(sdev->dev, "%s (UUID: %pUL): %s (ibs/obs: %u/%u)\n",
- fw_module->man4_module_entry.name,
- &fw_module->man4_module_entry.uuid, msg, basecfg->ibs,
- basecfg->obs);
- dev_warn_once(sdev->dev, "Please try to update the firmware.\n");
- dev_warn_once(sdev->dev, "If the issue persists, file a bug at\n");
- dev_warn_once(sdev->dev, "https://github.com/thesofproject/sof/issues/\n");
+ dev_dbg(sdev->dev, "%s (UUID: %pUL): %s (ibs/obs: %u/%u)\n",
+ fw_module->man4_module_entry.name,
+ &fw_module->man4_module_entry.uuid, msg, basecfg->ibs,
+ basecfg->obs);
}
const struct sof_ipc_fw_loader_ops ipc4_loader_ops = {
diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c
index 39039a647cca..07eb5c6d4adf 100644
--- a/sound/soc/sof/ipc4-pcm.c
+++ b/sound/soc/sof/ipc4-pcm.c
@@ -413,7 +413,18 @@ skip_pause_transition:
ret = sof_ipc4_set_multi_pipeline_state(sdev, state, trigger_list);
if (ret < 0) {
dev_err(sdev->dev, "failed to set final state %d for all pipelines\n", state);
- goto free;
+ /*
+ * workaround: if the firmware is crashed while setting the
+ * pipelines to reset state we must ignore the error code and
+ * reset it to 0.
+ * Since the firmware is crashed we will not send IPC messages
+ * and we are going to see errors printed, but the state of the
+ * widgets will be correct for the next boot.
+ */
+ if (sdev->fw_state != SOF_FW_CRASHED || state != SOF_IPC4_PIPE_RESET)
+ goto free;
+
+ ret = 0;
}
/* update RUNNING/RESET state for all pipelines that were just triggered */
@@ -768,10 +779,8 @@ static void sof_ipc4_build_time_info(struct snd_sof_dev *sdev, struct snd_sof_pc
info->llp_offset = offsetof(struct sof_ipc4_fw_registers, llp_evad_reading_slot) +
sdev->fw_info_box.offset;
sof_mailbox_read(sdev, info->llp_offset, &llp_slot, sizeof(llp_slot));
- if (llp_slot.node_id != dai_copier->data.gtw_cfg.node_id) {
- dev_info(sdev->dev, "no llp found, fall back to default HDA path");
+ if (llp_slot.node_id != dai_copier->data.gtw_cfg.node_id)
info->llp_offset = 0;
- }
}
static int sof_ipc4_pcm_hw_params(struct snd_soc_component *component,
diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h
index 9e69b7c29117..1d39836d5efa 100644
--- a/sound/soc/sof/ipc4-priv.h
+++ b/sound/soc/sof/ipc4-priv.h
@@ -81,6 +81,7 @@ struct sof_ipc4_fw_data {
u32 mtrace_log_bytes;
int max_num_pipelines;
u32 max_libs_count;
+ bool fw_context_save;
int (*load_library)(struct snd_sof_dev *sdev,
struct sof_ipc4_fw_library *fw_lib, bool reload);
@@ -115,6 +116,9 @@ int sof_ipc4_reload_fw_libraries(struct snd_sof_dev *sdev);
struct sof_ipc4_fw_module *sof_ipc4_find_module_by_uuid(struct snd_sof_dev *sdev,
const guid_t *uuid);
+struct snd_sof_widget *sof_ipc4_find_swidget_by_ids(struct snd_sof_dev *sdev,
+ u32 module_id, int instance_id);
+
struct sof_ipc4_base_module_cfg;
void sof_ipc4_update_cpc_from_manifest(struct snd_sof_dev *sdev,
struct sof_ipc4_fw_module *fw_module,
diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c
index e012b6e166ac..f779156fe0e6 100644
--- a/sound/soc/sof/ipc4-topology.c
+++ b/sound/soc/sof/ipc4-topology.c
@@ -167,6 +167,26 @@ static const struct sof_token_info ipc4_token_list[SOF_TOKEN_COUNT] = {
[SOF_SRC_TOKENS] = {"SRC tokens", src_tokens, ARRAY_SIZE(src_tokens)},
};
+struct snd_sof_widget *sof_ipc4_find_swidget_by_ids(struct snd_sof_dev *sdev,
+ u32 module_id, int instance_id)
+{
+ struct snd_sof_widget *swidget;
+
+ list_for_each_entry(swidget, &sdev->widget_list, list) {
+ struct sof_ipc4_fw_module *fw_module = swidget->module_info;
+
+ /* Only active module instances have valid instance_id */
+ if (!swidget->use_count)
+ continue;
+
+ if (fw_module && fw_module->man4_module_entry.id == module_id &&
+ swidget->instance_id == instance_id)
+ return swidget;
+ }
+
+ return NULL;
+}
+
static void sof_ipc4_dbg_audio_format(struct device *dev, struct sof_ipc4_pin_format *pin_fmt,
int num_formats)
{
@@ -2372,6 +2392,8 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget
}
if (swidget->id != snd_soc_dapm_scheduler) {
+ int module_id = msg->primary & SOF_IPC4_MOD_ID_MASK;
+
ret = sof_ipc4_widget_assign_instance_id(sdev, swidget);
if (ret < 0) {
dev_err(sdev->dev, "failed to assign instance id for %s\n",
@@ -2387,9 +2409,15 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget
msg->extension &= ~SOF_IPC4_MOD_EXT_PPL_ID_MASK;
msg->extension |= SOF_IPC4_MOD_EXT_PPL_ID(pipe_widget->instance_id);
+
+ dev_dbg(sdev->dev, "Create widget %s (pipe %d) - ID %d, instance %d, core %d\n",
+ swidget->widget->name, swidget->pipeline_id, module_id,
+ swidget->instance_id, swidget->core);
+ } else {
+ dev_dbg(sdev->dev, "Create pipeline %s (pipe %d) - instance %d, core %d\n",
+ swidget->widget->name, swidget->pipeline_id,
+ swidget->instance_id, swidget->core);
}
- dev_dbg(sdev->dev, "Create widget %s instance %d - pipe %d - core %d\n",
- swidget->widget->name, swidget->instance_id, swidget->pipeline_id, swidget->core);
msg->data_size = ipc_size;
msg->data_ptr = ipc_data;
diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c
index 8441f4ae4065..ac5c6bc66d2a 100644
--- a/sound/soc/sof/ipc4.c
+++ b/sound/soc/sof/ipc4.c
@@ -78,6 +78,9 @@ static const struct sof_ipc4_fw_status {
{165, "Reserved (ADSP_IPC_PIPELINE_ALREADY_EXISTS removed)"},
};
+typedef void (*ipc4_notification_handler)(struct snd_sof_dev *sdev,
+ struct sof_ipc4_msg *msg);
+
static int sof_ipc4_check_reply_status(struct snd_sof_dev *sdev, u32 status)
{
int i, ret;
@@ -573,46 +576,64 @@ EXPORT_SYMBOL(sof_ipc4_find_debug_slot_offset_by_type);
static int ipc4_fw_ready(struct snd_sof_dev *sdev, struct sof_ipc4_msg *ipc4_msg)
{
- int inbox_offset, inbox_size, outbox_offset, outbox_size;
-
/* no need to re-check version/ABI for subsequent boots */
if (!sdev->first_boot)
return 0;
- /* Set up the windows for IPC communication */
- inbox_offset = snd_sof_dsp_get_mailbox_offset(sdev);
- if (inbox_offset < 0) {
- dev_err(sdev->dev, "%s: No mailbox offset\n", __func__);
- return inbox_offset;
- }
- inbox_size = SOF_IPC4_MSG_MAX_SIZE;
- outbox_offset = snd_sof_dsp_get_window_offset(sdev, SOF_IPC4_OUTBOX_WINDOW_IDX);
- outbox_size = SOF_IPC4_MSG_MAX_SIZE;
+ sof_ipc4_create_exception_debugfs_node(sdev);
- sdev->fw_info_box.offset = snd_sof_dsp_get_window_offset(sdev, SOF_IPC4_INBOX_WINDOW_IDX);
- sdev->fw_info_box.size = sizeof(struct sof_ipc4_fw_registers);
- sdev->dsp_box.offset = inbox_offset;
- sdev->dsp_box.size = inbox_size;
- sdev->host_box.offset = outbox_offset;
- sdev->host_box.size = outbox_size;
+ return sof_ipc4_init_msg_memory(sdev);
+}
- sdev->debug_box.offset = snd_sof_dsp_get_window_offset(sdev,
- SOF_IPC4_DEBUG_WINDOW_IDX);
+static void sof_ipc4_module_notification_handler(struct snd_sof_dev *sdev,
+ struct sof_ipc4_msg *ipc4_msg)
+{
+ struct sof_ipc4_notify_module_data *data = ipc4_msg->data_ptr;
+
+ /*
+ * If the notification includes additional, module specific data, then
+ * we need to re-allocate the buffer and re-read the whole payload,
+ * including the event_data
+ */
+ if (data->event_data_size) {
+ void *new;
+ int ret;
+
+ ipc4_msg->data_size += data->event_data_size;
+
+ new = krealloc(ipc4_msg->data_ptr, ipc4_msg->data_size, GFP_KERNEL);
+ if (!new) {
+ ipc4_msg->data_size -= data->event_data_size;
+ return;
+ }
- sof_ipc4_create_exception_debugfs_node(sdev);
+ /* re-read the whole payload */
+ ipc4_msg->data_ptr = new;
+ ret = snd_sof_ipc_msg_data(sdev, NULL, ipc4_msg->data_ptr,
+ ipc4_msg->data_size);
+ if (ret < 0) {
+ dev_err(sdev->dev,
+ "Failed to read the full module notification: %d\n",
+ ret);
+ return;
+ }
+ data = ipc4_msg->data_ptr;
+ }
- dev_dbg(sdev->dev, "mailbox upstream 0x%x - size 0x%x\n",
- inbox_offset, inbox_size);
- dev_dbg(sdev->dev, "mailbox downstream 0x%x - size 0x%x\n",
- outbox_offset, outbox_size);
- dev_dbg(sdev->dev, "debug box 0x%x\n", sdev->debug_box.offset);
+ /* Handle ALSA kcontrol notification */
+ if ((data->event_id & SOF_IPC4_NOTIFY_MODULE_EVENTID_ALSA_MAGIC_MASK) ==
+ SOF_IPC4_NOTIFY_MODULE_EVENTID_ALSA_MAGIC_VAL) {
+ const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
- return sof_ipc4_init_msg_memory(sdev);
+ if (tplg_ops->control->update)
+ tplg_ops->control->update(sdev, ipc4_msg);
+ }
}
static void sof_ipc4_rx_msg(struct snd_sof_dev *sdev)
{
struct sof_ipc4_msg *ipc4_msg = sdev->ipc->msg.rx_data;
+ ipc4_notification_handler handler_func = NULL;
size_t data_size = 0;
int err;
@@ -648,6 +669,10 @@ static void sof_ipc4_rx_msg(struct snd_sof_dev *sdev)
case SOF_IPC4_NOTIFY_EXCEPTION_CAUGHT:
snd_sof_dsp_panic(sdev, 0, true);
break;
+ case SOF_IPC4_NOTIFY_MODULE_NOTIFICATION:
+ data_size = sizeof(struct sof_ipc4_notify_module_data);
+ handler_func = sof_ipc4_module_notification_handler;
+ break;
default:
dev_dbg(sdev->dev, "Unhandled DSP message: %#x|%#x\n",
ipc4_msg->primary, ipc4_msg->extension);
@@ -660,9 +685,20 @@ static void sof_ipc4_rx_msg(struct snd_sof_dev *sdev)
return;
ipc4_msg->data_size = data_size;
- snd_sof_ipc_msg_data(sdev, NULL, ipc4_msg->data_ptr, ipc4_msg->data_size);
+ err = snd_sof_ipc_msg_data(sdev, NULL, ipc4_msg->data_ptr, ipc4_msg->data_size);
+ if (err < 0) {
+ dev_err(sdev->dev, "failed to read IPC notification data: %d\n", err);
+ kfree(ipc4_msg->data_ptr);
+ ipc4_msg->data_ptr = NULL;
+ ipc4_msg->data_size = 0;
+ return;
+ }
}
+ /* Handle notifications with payload */
+ if (handler_func)
+ handler_func(sdev, ipc4_msg);
+
sof_ipc4_log_header(sdev->dev, "ipc rx done ", ipc4_msg, true);
if (data_size) {
@@ -732,11 +768,38 @@ static const struct sof_ipc_pm_ops ipc4_pm_ops = {
static int sof_ipc4_init(struct snd_sof_dev *sdev)
{
struct sof_ipc4_fw_data *ipc4_data = sdev->private;
+ int inbox_offset;
mutex_init(&ipc4_data->pipeline_state_mutex);
xa_init_flags(&ipc4_data->fw_lib_xa, XA_FLAGS_ALLOC);
+ /* Set up the windows for IPC communication */
+ inbox_offset = snd_sof_dsp_get_mailbox_offset(sdev);
+ if (inbox_offset < 0) {
+ dev_err(sdev->dev, "%s: No mailbox offset\n", __func__);
+ return inbox_offset;
+ }
+
+ sdev->dsp_box.offset = inbox_offset;
+ sdev->dsp_box.size = SOF_IPC4_MSG_MAX_SIZE;
+ sdev->host_box.offset = snd_sof_dsp_get_window_offset(sdev,
+ SOF_IPC4_OUTBOX_WINDOW_IDX);
+ sdev->host_box.size = SOF_IPC4_MSG_MAX_SIZE;
+
+ sdev->debug_box.offset = snd_sof_dsp_get_window_offset(sdev,
+ SOF_IPC4_DEBUG_WINDOW_IDX);
+
+ sdev->fw_info_box.offset = snd_sof_dsp_get_window_offset(sdev,
+ SOF_IPC4_INBOX_WINDOW_IDX);
+ sdev->fw_info_box.size = sizeof(struct sof_ipc4_fw_registers);
+
+ dev_dbg(sdev->dev, "mailbox upstream %#x - size %#x\n",
+ sdev->dsp_box.offset, SOF_IPC4_MSG_MAX_SIZE);
+ dev_dbg(sdev->dev, "mailbox downstream %#x - size %#x\n",
+ sdev->host_box.offset, SOF_IPC4_MSG_MAX_SIZE);
+ dev_dbg(sdev->dev, "debug box %#x\n", sdev->debug_box.offset);
+
return 0;
}
diff --git a/sound/soc/sof/mediatek/adsp_helper.h b/sound/soc/sof/mediatek/adsp_helper.h
index d41e904e6614..35527567962e 100644
--- a/sound/soc/sof/mediatek/adsp_helper.h
+++ b/sound/soc/sof/mediatek/adsp_helper.h
@@ -15,17 +15,13 @@
struct mtk_adsp_chip_info {
phys_addr_t pa_sram;
phys_addr_t pa_dram; /* adsp dram physical base */
- phys_addr_t pa_shared_dram; /* adsp dram physical base */
phys_addr_t pa_cfgreg;
u32 sramsize;
u32 dramsize;
u32 cfgregsize;
- u32 shared_size;
void __iomem *va_sram; /* corresponding to pa_sram */
void __iomem *va_dram; /* corresponding to pa_dram */
void __iomem *va_cfgreg;
- void __iomem *shared_sram; /* part of va_sram */
- void __iomem *shared_dram; /* part of va_dram */
phys_addr_t adsp_bootup_addr;
int dram_offset; /*dram offset between system and dsp view*/
diff --git a/sound/soc/sof/mediatek/mt8186/mt8186.c b/sound/soc/sof/mediatek/mt8186/mt8186.c
index b69fa788b16f..0d2d7d697de0 100644
--- a/sound/soc/sof/mediatek/mt8186/mt8186.c
+++ b/sound/soc/sof/mediatek/mt8186/mt8186.c
@@ -96,29 +96,6 @@ static int platform_parse_resource(struct platform_device *pdev, void *data)
struct mtk_adsp_chip_info *adsp = data;
int ret;
- mem_region = of_parse_phandle(dev->of_node, "memory-region", 0);
- if (!mem_region) {
- dev_err(dev, "no dma memory-region phandle\n");
- return -ENODEV;
- }
-
- ret = of_address_to_resource(mem_region, 0, &res);
- of_node_put(mem_region);
- if (ret) {
- dev_err(dev, "of_address_to_resource dma failed\n");
- return ret;
- }
-
- dev_dbg(dev, "DMA %pR\n", &res);
-
- adsp->pa_shared_dram = (phys_addr_t)res.start;
- adsp->shared_size = resource_size(&res);
- if (adsp->pa_shared_dram & DRAM_REMAP_MASK) {
- dev_err(dev, "adsp shared dma memory(%#x) is not 4K-aligned\n",
- (u32)adsp->pa_shared_dram);
- return -EINVAL;
- }
-
ret = of_reserved_mem_device_init(dev);
if (ret) {
dev_err(dev, "of_reserved_mem_device_init failed\n");
@@ -248,26 +225,6 @@ static int adsp_memory_remap_init(struct snd_sof_dev *sdev, struct mtk_adsp_chip
return 0;
}
-static int adsp_shared_base_ioremap(struct platform_device *pdev, void *data)
-{
- struct device *dev = &pdev->dev;
- struct mtk_adsp_chip_info *adsp = data;
-
- /* remap shared-dram base to be non-cachable */
- adsp->shared_dram = devm_ioremap(dev, adsp->pa_shared_dram,
- adsp->shared_size);
- if (!adsp->shared_dram) {
- dev_err(dev, "failed to ioremap base %pa size %#x\n",
- adsp->shared_dram, adsp->shared_size);
- return -ENOMEM;
- }
-
- dev_dbg(dev, "shared-dram vbase=%p, phy addr :%pa, size=%#x\n",
- adsp->shared_dram, &adsp->pa_shared_dram, adsp->shared_size);
-
- return 0;
-}
-
static int mt8186_run(struct snd_sof_dev *sdev)
{
u32 adsp_bootup_addr;
@@ -324,12 +281,6 @@ static int mt8186_dsp_probe(struct snd_sof_dev *sdev)
priv->adsp->va_dram = sdev->bar[SOF_FW_BLK_TYPE_SRAM];
- ret = adsp_shared_base_ioremap(pdev, priv->adsp);
- if (ret) {
- dev_err(sdev->dev, "adsp_shared_base_ioremap fail!\n");
- return ret;
- }
-
sdev->bar[DSP_REG_BAR] = priv->adsp->va_cfgreg;
sdev->bar[DSP_SECREG_BAR] = priv->adsp->va_secreg;
sdev->bar[DSP_BUSREG_BAR] = priv->adsp->va_busreg;
diff --git a/sound/soc/sof/mediatek/mt8195/mt8195.c b/sound/soc/sof/mediatek/mt8195/mt8195.c
index cac0a085f60a..8ee7ee246344 100644
--- a/sound/soc/sof/mediatek/mt8195/mt8195.c
+++ b/sound/soc/sof/mediatek/mt8195/mt8195.c
@@ -96,29 +96,6 @@ static int platform_parse_resource(struct platform_device *pdev, void *data)
struct mtk_adsp_chip_info *adsp = data;
int ret;
- mem_region = of_parse_phandle(dev->of_node, "memory-region", 0);
- if (!mem_region) {
- dev_err(dev, "no dma memory-region phandle\n");
- return -ENODEV;
- }
-
- ret = of_address_to_resource(mem_region, 0, &res);
- of_node_put(mem_region);
- if (ret) {
- dev_err(dev, "of_address_to_resource dma failed\n");
- return ret;
- }
-
- dev_dbg(dev, "DMA %pR\n", &res);
-
- adsp->pa_shared_dram = (phys_addr_t)res.start;
- adsp->shared_size = resource_size(&res);
- if (adsp->pa_shared_dram & DRAM_REMAP_MASK) {
- dev_err(dev, "adsp shared dma memory(%#x) is not 4K-aligned\n",
- (u32)adsp->pa_shared_dram);
- return -EINVAL;
- }
-
ret = of_reserved_mem_device_init(dev);
if (ret) {
dev_err(dev, "of_reserved_mem_device_init failed\n");
@@ -238,26 +215,6 @@ static int adsp_memory_remap_init(struct device *dev, struct mtk_adsp_chip_info
return 0;
}
-static int adsp_shared_base_ioremap(struct platform_device *pdev, void *data)
-{
- struct device *dev = &pdev->dev;
- struct mtk_adsp_chip_info *adsp = data;
-
- /* remap shared-dram base to be non-cachable */
- adsp->shared_dram = devm_ioremap(dev, adsp->pa_shared_dram,
- adsp->shared_size);
- if (!adsp->shared_dram) {
- dev_err(dev, "failed to ioremap base %pa size %#x\n",
- adsp->shared_dram, adsp->shared_size);
- return -ENOMEM;
- }
-
- dev_dbg(dev, "shared-dram vbase=%p, phy addr :%pa, size=%#x\n",
- adsp->shared_dram, &adsp->pa_shared_dram, adsp->shared_size);
-
- return 0;
-}
-
static int mt8195_run(struct snd_sof_dev *sdev)
{
u32 adsp_bootup_addr;
@@ -338,12 +295,6 @@ static int mt8195_dsp_probe(struct snd_sof_dev *sdev)
}
priv->adsp->va_dram = sdev->bar[SOF_FW_BLK_TYPE_SRAM];
- ret = adsp_shared_base_ioremap(pdev, priv->adsp);
- if (ret) {
- dev_err(sdev->dev, "adsp_shared_base_ioremap fail!\n");
- goto err_adsp_sram_power_off;
- }
-
sdev->bar[DSP_REG_BAR] = priv->adsp->va_cfgreg;
sdev->mmio_bar = SOF_FW_BLK_TYPE_SRAM;
diff --git a/sound/soc/sof/sof-acpi-dev.c b/sound/soc/sof/sof-acpi-dev.c
index 84a4a0a3318e..2977f0a63fba 100644
--- a/sound/soc/sof/sof-acpi-dev.c
+++ b/sound/soc/sof/sof-acpi-dev.c
@@ -74,18 +74,10 @@ int sof_acpi_probe(struct platform_device *pdev, const struct sof_dev_desc *desc
sof_pdata->desc = desc;
sof_pdata->dev = &pdev->dev;
- sof_pdata->fw_filename = desc->default_fw_filename[SOF_IPC_TYPE_3];
-
- /* alternate fw and tplg filenames ? */
- if (fw_path)
- sof_pdata->fw_filename_prefix = fw_path;
- else
- sof_pdata->fw_filename_prefix = desc->default_fw_path[SOF_IPC_TYPE_3];
-
- if (tplg_path)
- sof_pdata->tplg_filename_prefix = tplg_path;
- else
- sof_pdata->tplg_filename_prefix = desc->default_tplg_path[SOF_IPC_TYPE_3];
+
+ sof_pdata->ipc_file_profile_base.ipc_type = desc->ipc_default;
+ sof_pdata->ipc_file_profile_base.fw_path = fw_path;
+ sof_pdata->ipc_file_profile_base.tplg_path = tplg_path;
/* set callback to be called on successful device probe to enable runtime_pm */
sof_pdata->sof_probe_complete = sof_acpi_probe_complete;
diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c
index 77cc64ac7113..9163975c9c3f 100644
--- a/sound/soc/sof/sof-audio.c
+++ b/sound/soc/sof/sof-audio.c
@@ -11,7 +11,6 @@
#include <linux/bitfield.h>
#include <trace/events/sof.h>
#include "sof-audio.h"
-#include "sof-of-dev.h"
#include "ops.h"
static bool is_virtual_widget(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
@@ -1006,122 +1005,3 @@ int sof_dai_get_bclk(struct snd_soc_pcm_runtime *rtd)
return sof_dai_get_clk(rtd, SOF_DAI_CLK_INTEL_SSP_BCLK);
}
EXPORT_SYMBOL(sof_dai_get_bclk);
-
-static struct snd_sof_of_mach *sof_of_machine_select(struct snd_sof_dev *sdev)
-{
- struct snd_sof_pdata *sof_pdata = sdev->pdata;
- const struct sof_dev_desc *desc = sof_pdata->desc;
- struct snd_sof_of_mach *mach = desc->of_machines;
-
- if (!mach)
- return NULL;
-
- for (; mach->compatible; mach++) {
- if (of_machine_is_compatible(mach->compatible)) {
- sof_pdata->tplg_filename = mach->sof_tplg_filename;
- if (mach->fw_filename)
- sof_pdata->fw_filename = mach->fw_filename;
-
- return mach;
- }
- }
-
- return NULL;
-}
-
-/*
- * SOF Driver enumeration.
- */
-int sof_machine_check(struct snd_sof_dev *sdev)
-{
- struct snd_sof_pdata *sof_pdata = sdev->pdata;
- const struct sof_dev_desc *desc = sof_pdata->desc;
- struct snd_soc_acpi_mach *mach;
-
- if (!IS_ENABLED(CONFIG_SND_SOC_SOF_FORCE_NOCODEC_MODE)) {
- const struct snd_sof_of_mach *of_mach;
-
- if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) &&
- sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC))
- goto nocodec;
-
- /* find machine */
- mach = snd_sof_machine_select(sdev);
- if (mach) {
- sof_pdata->machine = mach;
-
- if (sof_pdata->subsystem_id_set) {
- mach->mach_params.subsystem_vendor = sof_pdata->subsystem_vendor;
- mach->mach_params.subsystem_device = sof_pdata->subsystem_device;
- mach->mach_params.subsystem_id_set = true;
- }
-
- snd_sof_set_mach_params(mach, sdev);
- return 0;
- }
-
- of_mach = sof_of_machine_select(sdev);
- if (of_mach) {
- sof_pdata->of_machine = of_mach;
- return 0;
- }
-
- if (!IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC)) {
- dev_err(sdev->dev, "error: no matching ASoC machine driver found - aborting probe\n");
- return -ENODEV;
- }
- } else {
- dev_warn(sdev->dev, "Force to use nocodec mode\n");
- }
-
-nocodec:
- /* select nocodec mode */
- dev_warn(sdev->dev, "Using nocodec machine driver\n");
- mach = devm_kzalloc(sdev->dev, sizeof(*mach), GFP_KERNEL);
- if (!mach)
- return -ENOMEM;
-
- mach->drv_name = "sof-nocodec";
- if (!sof_pdata->tplg_filename)
- sof_pdata->tplg_filename = desc->nocodec_tplg_filename;
-
- sof_pdata->machine = mach;
- snd_sof_set_mach_params(mach, sdev);
-
- return 0;
-}
-EXPORT_SYMBOL(sof_machine_check);
-
-int sof_machine_register(struct snd_sof_dev *sdev, void *pdata)
-{
- struct snd_sof_pdata *plat_data = pdata;
- const char *drv_name;
- const void *mach;
- int size;
-
- drv_name = plat_data->machine->drv_name;
- mach = plat_data->machine;
- size = sizeof(*plat_data->machine);
-
- /* register machine driver, pass machine info as pdata */
- plat_data->pdev_mach =
- platform_device_register_data(sdev->dev, drv_name,
- PLATFORM_DEVID_NONE, mach, size);
- if (IS_ERR(plat_data->pdev_mach))
- return PTR_ERR(plat_data->pdev_mach);
-
- dev_dbg(sdev->dev, "created machine %s\n",
- dev_name(&plat_data->pdev_mach->dev));
-
- return 0;
-}
-EXPORT_SYMBOL(sof_machine_register);
-
-void sof_machine_unregister(struct snd_sof_dev *sdev, void *pdata)
-{
- struct snd_sof_pdata *plat_data = pdata;
-
- if (!IS_ERR_OR_NULL(plat_data->pdev_mach))
- platform_device_unregister(plat_data->pdev_mach);
-}
-EXPORT_SYMBOL(sof_machine_unregister);
diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h
index a6d6bcd00cee..8874ee5f557f 100644
--- a/sound/soc/sof/sof-audio.h
+++ b/sound/soc/sof/sof-audio.h
@@ -275,6 +275,7 @@ enum sof_tokens {
SOF_GAIN_TOKENS,
SOF_ACPDMIC_TOKENS,
SOF_ACPI2S_TOKENS,
+ SOF_MICFIL_TOKENS,
/* this should be the last */
SOF_TOKEN_COUNT,
diff --git a/sound/soc/sof/sof-client-probes.c b/sound/soc/sof/sof-client-probes.c
index 7cc9e8f18de7..30f771ac7bbf 100644
--- a/sound/soc/sof/sof-client-probes.c
+++ b/sound/soc/sof/sof-client-probes.c
@@ -381,8 +381,6 @@ static const struct snd_soc_component_driver sof_probes_component = {
.legacy_dai_naming = 1,
};
-SND_SOC_DAILINK_DEF(dummy, DAILINK_COMP_ARRAY(COMP_DUMMY()));
-
static int sof_probes_client_probe(struct auxiliary_device *auxdev,
const struct auxiliary_device_id *id)
{
@@ -475,7 +473,7 @@ static int sof_probes_client_probe(struct auxiliary_device *auxdev,
links[0].cpus = &cpus[0];
links[0].num_cpus = 1;
links[0].cpus->dai_name = "Probe Extraction CPU DAI";
- links[0].codecs = dummy;
+ links[0].codecs = &snd_soc_dummy_dlc;
links[0].num_codecs = 1;
links[0].platforms = platform_component;
links[0].num_platforms = ARRAY_SIZE(platform_component);
diff --git a/sound/soc/sof/sof-of-dev.c b/sound/soc/sof/sof-of-dev.c
index c6be8a91e74b..b9a499e92b9a 100644
--- a/sound/soc/sof/sof-of-dev.c
+++ b/sound/soc/sof/sof-of-dev.c
@@ -64,17 +64,10 @@ int sof_of_probe(struct platform_device *pdev)
sof_pdata->desc = desc;
sof_pdata->dev = &pdev->dev;
- sof_pdata->fw_filename = desc->default_fw_filename[SOF_IPC_TYPE_3];
- if (fw_path)
- sof_pdata->fw_filename_prefix = fw_path;
- else
- sof_pdata->fw_filename_prefix = desc->default_fw_path[SOF_IPC_TYPE_3];
-
- if (tplg_path)
- sof_pdata->tplg_filename_prefix = tplg_path;
- else
- sof_pdata->tplg_filename_prefix = desc->default_tplg_path[SOF_IPC_TYPE_3];
+ sof_pdata->ipc_file_profile_base.ipc_type = desc->ipc_default;
+ sof_pdata->ipc_file_profile_base.fw_path = fw_path;
+ sof_pdata->ipc_file_profile_base.tplg_path = tplg_path;
/* set callback to be called on successful device probe to enable runtime_pm */
sof_pdata->sof_probe_complete = sof_of_probe_complete;
diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c
index 64b326e3ef85..aab5c900cecf 100644
--- a/sound/soc/sof/sof-pci-dev.c
+++ b/sound/soc/sof/sof-pci-dev.c
@@ -190,6 +190,7 @@ static void sof_pci_probe_complete(struct device *dev)
int sof_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
{
+ struct sof_loadable_file_profile *path_override;
struct device *dev = &pci->dev;
const struct sof_dev_desc *desc =
(const struct sof_dev_desc *)pci_id->driver_data;
@@ -232,106 +233,39 @@ int sof_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
sof_pdata->desc = desc;
sof_pdata->dev = dev;
- sof_pdata->ipc_type = desc->ipc_default;
+ path_override = &sof_pdata->ipc_file_profile_base;
if (sof_pci_ipc_type < 0) {
- sof_pdata->ipc_type = desc->ipc_default;
+ path_override->ipc_type = desc->ipc_default;
+ } else if (sof_pci_ipc_type < SOF_IPC_TYPE_COUNT) {
+ path_override->ipc_type = sof_pci_ipc_type;
} else {
- dev_info(dev, "overriding default IPC %d to requested %d\n",
- desc->ipc_default, sof_pci_ipc_type);
- if (sof_pci_ipc_type >= SOF_IPC_TYPE_COUNT) {
- dev_err(dev, "invalid request value %d\n", sof_pci_ipc_type);
- ret = -EINVAL;
- goto out;
- }
- if (!(BIT(sof_pci_ipc_type) & desc->ipc_supported_mask)) {
- dev_err(dev, "invalid request value %d, supported mask is %#x\n",
- sof_pci_ipc_type, desc->ipc_supported_mask);
- ret = -EINVAL;
- goto out;
- }
- sof_pdata->ipc_type = sof_pci_ipc_type;
+ dev_err(dev, "Invalid IPC type requested: %d\n", sof_pci_ipc_type);
+ ret = -EINVAL;
+ goto out;
}
- if (fw_filename) {
- sof_pdata->fw_filename = fw_filename;
+ path_override->fw_path = fw_path;
+ path_override->fw_name = fw_filename;
+ path_override->fw_lib_path = lib_path;
+ path_override->tplg_path = tplg_path;
- dev_dbg(dev, "Module parameter used, changed fw filename to %s\n",
- sof_pdata->fw_filename);
- } else {
- sof_pdata->fw_filename = desc->default_fw_filename[sof_pdata->ipc_type];
+ if (dmi_check_system(community_key_platforms) &&
+ sof_dmi_use_community_key) {
+ path_override->fw_path_postfix = "community";
+ path_override->fw_lib_path_postfix = "community";
}
/*
- * for platforms using the SOF community key, change the
- * default path automatically to pick the right files from the
- * linux-firmware tree. This can be overridden with the
- * fw_path kernel parameter, e.g. for developers.
- */
-
- /* alternate fw and tplg filenames ? */
- if (fw_path) {
- sof_pdata->fw_filename_prefix = fw_path;
-
- dev_dbg(dev,
- "Module parameter used, changed fw path to %s\n",
- sof_pdata->fw_filename_prefix);
-
- } else if (dmi_check_system(community_key_platforms) && sof_dmi_use_community_key) {
- sof_pdata->fw_filename_prefix =
- devm_kasprintf(dev, GFP_KERNEL, "%s/%s",
- sof_pdata->desc->default_fw_path[sof_pdata->ipc_type],
- "community");
-
- dev_dbg(dev,
- "Platform uses community key, changed fw path to %s\n",
- sof_pdata->fw_filename_prefix);
- } else {
- sof_pdata->fw_filename_prefix =
- sof_pdata->desc->default_fw_path[sof_pdata->ipc_type];
- }
-
- if (lib_path) {
- sof_pdata->fw_lib_prefix = lib_path;
-
- dev_dbg(dev, "Module parameter used, changed fw_lib path to %s\n",
- sof_pdata->fw_lib_prefix);
-
- } else if (sof_pdata->desc->default_lib_path[sof_pdata->ipc_type]) {
- if (dmi_check_system(community_key_platforms) && sof_dmi_use_community_key) {
- sof_pdata->fw_lib_prefix =
- devm_kasprintf(dev, GFP_KERNEL, "%s/%s",
- sof_pdata->desc->default_lib_path[sof_pdata->ipc_type],
- "community");
-
- dev_dbg(dev,
- "Platform uses community key, changed fw_lib path to %s\n",
- sof_pdata->fw_lib_prefix);
- } else {
- sof_pdata->fw_lib_prefix =
- sof_pdata->desc->default_lib_path[sof_pdata->ipc_type];
- }
- }
-
- if (tplg_path)
- sof_pdata->tplg_filename_prefix = tplg_path;
- else
- sof_pdata->tplg_filename_prefix =
- sof_pdata->desc->default_tplg_path[sof_pdata->ipc_type];
-
- /*
* the topology filename will be provided in the machine descriptor, unless
* it is overridden by a module parameter or DMI quirk.
*/
if (tplg_filename) {
- sof_pdata->tplg_filename = tplg_filename;
-
- dev_dbg(dev, "Module parameter used, changed tplg filename to %s\n",
- sof_pdata->tplg_filename);
+ path_override->tplg_name = tplg_filename;
} else {
dmi_check_system(sof_tplg_table);
if (sof_dmi_override_tplg_name)
- sof_pdata->tplg_filename = sof_dmi_override_tplg_name;
+ path_override->tplg_name = sof_dmi_override_tplg_name;
}
/* set callback to be called on successful device probe to enable runtime_pm */
diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h
index f4185012eb69..6d7897bf9607 100644
--- a/sound/soc/sof/sof-priv.h
+++ b/sound/soc/sof/sof-priv.h
@@ -696,6 +696,13 @@ void snd_sof_new_platform_drv(struct snd_sof_dev *sdev);
extern struct snd_compress_ops sof_compressed_ops;
/*
+ * Firmware (firmware, libraries, topologies) file location
+ */
+int sof_create_ipc_file_profile(struct snd_sof_dev *sdev,
+ struct sof_loadable_file_profile *base_profile,
+ struct sof_loadable_file_profile *out_profile);
+
+/*
* Firmware loading.
*/
int snd_sof_load_firmware_raw(struct snd_sof_dev *sdev);
@@ -814,8 +821,6 @@ int sof_stream_pcm_open(struct snd_sof_dev *sdev,
int sof_stream_pcm_close(struct snd_sof_dev *sdev,
struct snd_pcm_substream *substream);
-int sof_machine_check(struct snd_sof_dev *sdev);
-
/* SOF client support */
#if IS_ENABLED(CONFIG_SND_SOC_SOF_CLIENT)
int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, u32 id,
diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c
index 37ec671a2d76..617a225fff24 100644
--- a/sound/soc/sof/topology.c
+++ b/sound/soc/sof/topology.c
@@ -289,13 +289,14 @@ static const struct sof_dai_types sof_dais[] = {
{"ALH", SOF_DAI_INTEL_ALH},
{"SAI", SOF_DAI_IMX_SAI},
{"ESAI", SOF_DAI_IMX_ESAI},
- {"ACP", SOF_DAI_AMD_BT},
+ {"ACPBT", SOF_DAI_AMD_BT},
{"ACPSP", SOF_DAI_AMD_SP},
{"ACPDMIC", SOF_DAI_AMD_DMIC},
{"ACPHS", SOF_DAI_AMD_HS},
{"AFE", SOF_DAI_MEDIATEK_AFE},
{"ACPSP_VIRTUAL", SOF_DAI_AMD_SP_VIRTUAL},
{"ACPHS_VIRTUAL", SOF_DAI_AMD_HS_VIRTUAL},
+ {"MICFIL", SOF_DAI_IMX_MICFIL},
};
@@ -1134,7 +1135,7 @@ static void sof_disconnect_dai_widget(struct snd_soc_component *scomp,
list_for_each_entry(rtd, &card->rtd_list, list) {
/* does stream match DAI link ? */
if (!rtd->dai_link->stream_name ||
- strcmp(sname, rtd->dai_link->stream_name))
+ !strstr(rtd->dai_link->stream_name, sname))
continue;
for_each_rtd_cpu_dais(rtd, i, cpu_dai)
@@ -1955,6 +1956,7 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_
token_id = SOF_ACPDMIC_TOKENS;
num_tuples += token_list[SOF_ACPDMIC_TOKENS].count;
break;
+ case SOF_DAI_AMD_BT:
case SOF_DAI_AMD_SP:
case SOF_DAI_AMD_HS:
case SOF_DAI_AMD_SP_VIRTUAL:
@@ -1962,6 +1964,10 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_
token_id = SOF_ACPI2S_TOKENS;
num_tuples += token_list[SOF_ACPI2S_TOKENS].count;
break;
+ case SOF_DAI_IMX_MICFIL:
+ token_id = SOF_MICFIL_TOKENS;
+ num_tuples += token_list[SOF_MICFIL_TOKENS].count;
+ break;
default:
break;
}
diff --git a/sound/soc/sprd/sprd-pcm-compress.c b/sound/soc/sprd/sprd-pcm-compress.c
index 6cfab8844d0f..57bd1a0728ac 100644
--- a/sound/soc/sprd/sprd-pcm-compress.c
+++ b/sound/soc/sprd/sprd-pcm-compress.c
@@ -160,7 +160,7 @@ static int sprd_platform_compr_dma_config(struct snd_soc_component *component,
return -ENODEV;
}
- sgt = sg = devm_kcalloc(dev, sg_num, sizeof(*sg), GFP_KERNEL);
+ sgt = sg = kcalloc(sg_num, sizeof(*sg), GFP_KERNEL);
if (!sg) {
ret = -ENOMEM;
goto sg_err;
@@ -250,12 +250,12 @@ static int sprd_platform_compr_dma_config(struct snd_soc_component *component,
dma->desc->callback_param = cstream;
}
- devm_kfree(dev, sg);
+ kfree(sg);
return 0;
config_err:
- devm_kfree(dev, sg);
+ kfree(sg);
sg_err:
dma_release_channel(dma->chan);
return ret;
diff --git a/sound/soc/sti/sti_uniperif.c b/sound/soc/sti/sti_uniperif.c
index 2c21a86421e6..ba824f14a39c 100644
--- a/sound/soc/sti/sti_uniperif.c
+++ b/sound/soc/sti/sti_uniperif.c
@@ -461,10 +461,6 @@ static int sti_uniperiph_cpu_dai_of(struct device_node *node,
return 0;
}
-static const struct snd_dmaengine_pcm_config dmaengine_pcm_config = {
- .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
-};
-
static int sti_uniperiph_probe(struct platform_device *pdev)
{
struct sti_uniperiph_data *priv;
@@ -493,8 +489,7 @@ static int sti_uniperiph_probe(struct platform_device *pdev)
if (ret < 0)
return ret;
- return devm_snd_dmaengine_pcm_register(&pdev->dev,
- &dmaengine_pcm_config, 0);
+ return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
}
static struct platform_driver sti_uniperiph_driver = {
diff --git a/sound/soc/sunxi/sun4i-spdif.c b/sound/soc/sunxi/sun4i-spdif.c
index 702386823d17..f41c30955857 100644
--- a/sound/soc/sunxi/sun4i-spdif.c
+++ b/sound/soc/sunxi/sun4i-spdif.c
@@ -577,6 +577,11 @@ static const struct of_device_id sun4i_spdif_of_match[] = {
.compatible = "allwinner,sun50i-h6-spdif",
.data = &sun50i_h6_spdif_quirks,
},
+ {
+ .compatible = "allwinner,sun50i-h616-spdif",
+ /* Essentially the same as the H6, but without RX */
+ .data = &sun50i_h6_spdif_quirks,
+ },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, sun4i_spdif_of_match);
diff --git a/sound/soc/tegra/tegra20_ac97.c b/sound/soc/tegra/tegra20_ac97.c
index e713feca25fa..8011afe93c96 100644
--- a/sound/soc/tegra/tegra20_ac97.c
+++ b/sound/soc/tegra/tegra20_ac97.c
@@ -12,12 +12,11 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/io.h>
#include <linux/jiffies.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/reset.h>
@@ -39,11 +38,15 @@ static void tegra20_ac97_codec_reset(struct snd_ac97 *ac97)
u32 readback;
unsigned long timeout;
- /* reset line is not driven by DAC pad group, have to toggle GPIO */
- gpio_set_value(workdata->reset_gpio, 0);
+ /*
+ * The reset line is not driven by DAC pad group, have to toggle GPIO.
+ * The RESET line is active low but this is abstracted by the GPIO
+ * library.
+ */
+ gpiod_set_value(workdata->reset_gpio, 1);
udelay(2);
- gpio_set_value(workdata->reset_gpio, 1);
+ gpiod_set_value(workdata->reset_gpio, 0);
udelay(2);
timeout = jiffies + msecs_to_jiffies(100);
@@ -66,14 +69,10 @@ static void tegra20_ac97_codec_warm_reset(struct snd_ac97 *ac97)
* the controller cmd is not working, have to toggle sync line
* manually.
*/
- gpio_request(workdata->sync_gpio, "codec-sync");
-
- gpio_direction_output(workdata->sync_gpio, 1);
-
+ gpiod_direction_output(workdata->sync_gpio, 1);
udelay(2);
- gpio_set_value(workdata->sync_gpio, 0);
+ gpiod_set_value(workdata->sync_gpio, 0);
udelay(2);
- gpio_free(workdata->sync_gpio);
timeout = jiffies + msecs_to_jiffies(100);
@@ -342,28 +341,26 @@ static int tegra20_ac97_platform_probe(struct platform_device *pdev)
goto err_clk_put;
}
- ac97->reset_gpio = of_get_named_gpio(pdev->dev.of_node,
- "nvidia,codec-reset-gpio", 0);
- if (gpio_is_valid(ac97->reset_gpio)) {
- ret = devm_gpio_request_one(&pdev->dev, ac97->reset_gpio,
- GPIOF_OUT_INIT_HIGH, "codec-reset");
- if (ret) {
- dev_err(&pdev->dev, "could not get codec-reset GPIO\n");
- goto err_clk_put;
- }
- } else {
- dev_err(&pdev->dev, "no codec-reset GPIO supplied\n");
- ret = -EINVAL;
+ /* Obtain RESET de-asserted */
+ ac97->reset_gpio = devm_gpiod_get(&pdev->dev,
+ "nvidia,codec-reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(ac97->reset_gpio)) {
+ ret = PTR_ERR(ac97->reset_gpio);
+ dev_err(&pdev->dev, "no RESET GPIO supplied: %d\n", ret);
goto err_clk_put;
}
-
- ac97->sync_gpio = of_get_named_gpio(pdev->dev.of_node,
- "nvidia,codec-sync-gpio", 0);
- if (!gpio_is_valid(ac97->sync_gpio)) {
- dev_err(&pdev->dev, "no codec-sync GPIO supplied\n");
- ret = -EINVAL;
+ gpiod_set_consumer_name(ac97->reset_gpio, "codec-reset");
+
+ ac97->sync_gpio = devm_gpiod_get(&pdev->dev,
+ "nvidia,codec-sync",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(ac97->sync_gpio)) {
+ ret = PTR_ERR(ac97->sync_gpio);
+ dev_err(&pdev->dev, "no codec-sync GPIO supplied: %d\n", ret);
goto err_clk_put;
}
+ gpiod_set_consumer_name(ac97->sync_gpio, "codec-sync");
ac97->capture_dma_data.addr = mem->start + TEGRA20_AC97_FIFO_RX1;
ac97->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
diff --git a/sound/soc/tegra/tegra20_ac97.h b/sound/soc/tegra/tegra20_ac97.h
index 870ea09ff301..116d7b2db27e 100644
--- a/sound/soc/tegra/tegra20_ac97.h
+++ b/sound/soc/tegra/tegra20_ac97.h
@@ -80,7 +80,7 @@ struct tegra20_ac97 {
struct snd_dmaengine_dai_dma_data playback_dma_data;
struct reset_control *reset;
struct regmap *regmap;
- int reset_gpio;
- int sync_gpio;
+ struct gpio_desc *reset_gpio;
+ struct gpio_desc *sync_gpio;
};
#endif /* __TEGRA20_AC97_H__ */
diff --git a/sound/soc/tegra/tegra_pcm.c b/sound/soc/tegra/tegra_pcm.c
index 142e8d4eefd5..42acb56543db 100644
--- a/sound/soc/tegra/tegra_pcm.c
+++ b/sound/soc/tegra/tegra_pcm.c
@@ -98,8 +98,8 @@ int tegra_pcm_open(struct snd_soc_component *component,
return ret;
}
- chan = dma_request_slave_channel(cpu_dai->dev, dmap->chan_name);
- if (!chan) {
+ chan = dma_request_chan(cpu_dai->dev, dmap->chan_name);
+ if (IS_ERR(chan)) {
dev_err(cpu_dai->dev,
"dmaengine request slave channel failed! (%s)\n",
dmap->chan_name);
diff --git a/sound/usb/clock.c b/sound/usb/clock.c
index 33db334e6556..60fcb872a80b 100644
--- a/sound/usb/clock.c
+++ b/sound/usb/clock.c
@@ -261,6 +261,8 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip,
int ret, i, cur, err, pins, clock_id;
const u8 *sources;
int proto = fmt->protocol;
+ bool readable, writeable;
+ u32 bmControls;
entity_id &= 0xff;
@@ -292,11 +294,27 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip,
sources = GET_VAL(selector, proto, baCSourceID);
cur = 0;
+ if (proto == UAC_VERSION_3)
+ bmControls = le32_to_cpu(*(__le32 *)(&selector->v3.baCSourceID[0] + pins));
+ else
+ bmControls = *(__u8 *)(&selector->v2.baCSourceID[0] + pins);
+
+ readable = uac_v2v3_control_is_readable(bmControls,
+ UAC2_CX_CLOCK_SELECTOR);
+ writeable = uac_v2v3_control_is_writeable(bmControls,
+ UAC2_CX_CLOCK_SELECTOR);
+
if (pins == 1) {
ret = 1;
goto find_source;
}
+ /* for now just warn about buggy device */
+ if (!readable)
+ usb_audio_warn(chip,
+ "%s(): clock selector control is not readable, id %d\n",
+ __func__, clock_id);
+
/* the entity ID we are looking at is a selector.
* find out what it currently selects */
ret = uac_clock_selector_get_val(chip, clock_id);
@@ -325,17 +343,29 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip,
visited, validate);
if (ret > 0) {
/* Skip setting clock selector again for some devices */
- if (chip->quirk_flags & QUIRK_FLAG_SKIP_CLOCK_SELECTOR)
+ if (chip->quirk_flags & QUIRK_FLAG_SKIP_CLOCK_SELECTOR ||
+ !writeable)
return ret;
err = uac_clock_selector_set_val(chip, entity_id, cur);
- if (err < 0)
+ if (err < 0) {
+ if (pins == 1) {
+ usb_audio_dbg(chip,
+ "%s(): selector returned an error, "
+ "assuming a firmware bug, id %d, ret %d\n",
+ __func__, clock_id, err);
+ return ret;
+ }
return err;
+ }
}
if (!validate || ret > 0 || !chip->autoclock)
return ret;
find_others:
+ if (!writeable)
+ return -ENXIO;
+
/* The current clock source is invalid, try others. */
for (i = 1; i <= pins; i++) {
if (i == cur)
diff --git a/sound/usb/format.c b/sound/usb/format.c
index ab5fed9f55b6..3b45d0ee7693 100644
--- a/sound/usb/format.c
+++ b/sound/usb/format.c
@@ -470,9 +470,11 @@ static int validate_sample_rate_table_v2v3(struct snd_usb_audio *chip,
int clock)
{
struct usb_device *dev = chip->dev;
+ struct usb_host_interface *alts;
unsigned int *table;
unsigned int nr_rates;
int i, err;
+ u32 bmControls;
/* performing the rate verification may lead to unexpected USB bus
* behavior afterwards by some unknown reason. Do this only for the
@@ -481,6 +483,24 @@ static int validate_sample_rate_table_v2v3(struct snd_usb_audio *chip,
if (!(chip->quirk_flags & QUIRK_FLAG_VALIDATE_RATES))
return 0; /* don't perform the validation as default */
+ alts = snd_usb_get_host_interface(chip, fp->iface, fp->altsetting);
+ if (!alts)
+ return 0;
+
+ if (fp->protocol == UAC_VERSION_3) {
+ struct uac3_as_header_descriptor *as = snd_usb_find_csint_desc(
+ alts->extra, alts->extralen, NULL, UAC_AS_GENERAL);
+ bmControls = le32_to_cpu(as->bmControls);
+ } else {
+ struct uac2_as_header_descriptor *as = snd_usb_find_csint_desc(
+ alts->extra, alts->extralen, NULL, UAC_AS_GENERAL);
+ bmControls = as->bmControls;
+ }
+
+ if (!uac_v2v3_control_is_readable(bmControls,
+ UAC2_AS_VAL_ALT_SETTINGS))
+ return 0;
+
table = kcalloc(fp->nr_rates, sizeof(*table), GFP_KERNEL);
if (!table)
return -ENOMEM;
diff --git a/sound/usb/midi.c b/sound/usb/midi.c
index 6b0993258e03..c1f2e5a03de9 100644
--- a/sound/usb/midi.c
+++ b/sound/usb/midi.c
@@ -1742,50 +1742,44 @@ static void snd_usbmidi_get_port_info(struct snd_rawmidi *rmidi, int number,
}
}
-static struct usb_midi_in_jack_descriptor *find_usb_in_jack_descriptor(
- struct usb_host_interface *hostif, uint8_t jack_id)
+/* return iJack for the corresponding jackID */
+static int find_usb_ijack(struct usb_host_interface *hostif, uint8_t jack_id)
{
unsigned char *extra = hostif->extra;
int extralen = hostif->extralen;
+ struct usb_descriptor_header *h;
+ struct usb_midi_out_jack_descriptor *outjd;
+ struct usb_midi_in_jack_descriptor *injd;
+ size_t sz;
while (extralen > 4) {
- struct usb_midi_in_jack_descriptor *injd =
- (struct usb_midi_in_jack_descriptor *)extra;
+ h = (struct usb_descriptor_header *)extra;
+ if (h->bDescriptorType != USB_DT_CS_INTERFACE)
+ goto next;
+ outjd = (struct usb_midi_out_jack_descriptor *)h;
+ if (h->bLength >= sizeof(*outjd) &&
+ outjd->bDescriptorSubtype == UAC_MIDI_OUT_JACK &&
+ outjd->bJackID == jack_id) {
+ sz = USB_DT_MIDI_OUT_SIZE(outjd->bNrInputPins);
+ if (outjd->bLength < sz)
+ goto next;
+ return *(extra + sz - 1);
+ }
+
+ injd = (struct usb_midi_in_jack_descriptor *)h;
if (injd->bLength >= sizeof(*injd) &&
- injd->bDescriptorType == USB_DT_CS_INTERFACE &&
injd->bDescriptorSubtype == UAC_MIDI_IN_JACK &&
- injd->bJackID == jack_id)
- return injd;
- if (!extra[0])
- break;
- extralen -= extra[0];
- extra += extra[0];
- }
- return NULL;
-}
-
-static struct usb_midi_out_jack_descriptor *find_usb_out_jack_descriptor(
- struct usb_host_interface *hostif, uint8_t jack_id)
-{
- unsigned char *extra = hostif->extra;
- int extralen = hostif->extralen;
+ injd->bJackID == jack_id)
+ return injd->iJack;
- while (extralen > 4) {
- struct usb_midi_out_jack_descriptor *outjd =
- (struct usb_midi_out_jack_descriptor *)extra;
-
- if (outjd->bLength >= sizeof(*outjd) &&
- outjd->bDescriptorType == USB_DT_CS_INTERFACE &&
- outjd->bDescriptorSubtype == UAC_MIDI_OUT_JACK &&
- outjd->bJackID == jack_id)
- return outjd;
+next:
if (!extra[0])
break;
extralen -= extra[0];
extra += extra[0];
}
- return NULL;
+ return 0;
}
static void snd_usbmidi_init_substream(struct snd_usb_midi *umidi,
@@ -1796,13 +1790,10 @@ static void snd_usbmidi_init_substream(struct snd_usb_midi *umidi,
const char *name_format;
struct usb_interface *intf;
struct usb_host_interface *hostif;
- struct usb_midi_in_jack_descriptor *injd;
- struct usb_midi_out_jack_descriptor *outjd;
uint8_t jack_name_buf[32];
uint8_t *default_jack_name = "MIDI";
uint8_t *jack_name = default_jack_name;
uint8_t iJack;
- size_t sz;
int res;
struct snd_rawmidi_substream *substream =
@@ -1816,21 +1807,7 @@ static void snd_usbmidi_init_substream(struct snd_usb_midi *umidi,
intf = umidi->iface;
if (intf && jack_id >= 0) {
hostif = intf->cur_altsetting;
- iJack = 0;
- if (stream != SNDRV_RAWMIDI_STREAM_OUTPUT) {
- /* in jacks connect to outs */
- outjd = find_usb_out_jack_descriptor(hostif, jack_id);
- if (outjd) {
- sz = USB_DT_MIDI_OUT_SIZE(outjd->bNrInputPins);
- if (outjd->bLength >= sz)
- iJack = *(((uint8_t *) outjd) + sz - sizeof(uint8_t));
- }
- } else {
- /* and out jacks connect to ins */
- injd = find_usb_in_jack_descriptor(hostif, jack_id);
- if (injd)
- iJack = injd->iJack;
- }
+ iJack = find_usb_ijack(hostif, jack_id);
if (iJack != 0) {
res = usb_string(umidi->dev, iJack, jack_name_buf,
ARRAY_SIZE(jack_name_buf));
diff --git a/sound/usb/midi2.c b/sound/usb/midi2.c
index 1ec177fe284e..820d3e4b672a 100644
--- a/sound/usb/midi2.c
+++ b/sound/usb/midi2.c
@@ -1085,7 +1085,7 @@ int snd_usb_midi_v2_create(struct snd_usb_audio *chip,
}
if ((quirk && quirk->type != QUIRK_MIDI_STANDARD_INTERFACE) ||
iface->num_altsetting < 2) {
- usb_audio_info(chip, "Quirk or no altest; falling back to MIDI 1.0\n");
+ usb_audio_info(chip, "Quirk or no altset; falling back to MIDI 1.0\n");
goto fallback_to_midi1;
}
hostif = &iface->altsetting[1];
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index c8d48566e175..065a4be0d771 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -3447,6 +3447,9 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
case USB_ID(0x1235, 0x8213): /* Focusrite Scarlett 8i6 3rd Gen */
case USB_ID(0x1235, 0x8214): /* Focusrite Scarlett 18i8 3rd Gen */
case USB_ID(0x1235, 0x8215): /* Focusrite Scarlett 18i20 3rd Gen */
+ case USB_ID(0x1235, 0x8218): /* Focusrite Scarlett Solo 4th Gen */
+ case USB_ID(0x1235, 0x8219): /* Focusrite Scarlett 2i2 4th Gen */
+ case USB_ID(0x1235, 0x821a): /* Focusrite Scarlett 4i4 4th Gen */
case USB_ID(0x1235, 0x8206): /* Focusrite Clarett 2Pre USB */
case USB_ID(0x1235, 0x8207): /* Focusrite Clarett 4Pre USB */
case USB_ID(0x1235, 0x8208): /* Focusrite Clarett 8Pre USB */
diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c
index 33a3d1161885..6de605a601e5 100644
--- a/sound/usb/mixer_scarlett2.c
+++ b/sound/usb/mixer_scarlett2.c
@@ -1,12 +1,13 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Focusrite Scarlett 2 Protocol Driver for ALSA
- * (including Scarlett 2nd Gen, 3rd Gen, Clarett USB, and Clarett+
- * series products)
+ * (including Scarlett 2nd Gen, 3rd Gen, 4th Gen, Clarett USB, and
+ * Clarett+ series products)
*
* Supported models:
* - 6i6/18i8/18i20 Gen 2
* - Solo/2i2/4i4/8i6/18i8/18i20 Gen 3
+ * - Solo/2i2/4i4 Gen 4
* - Clarett 2Pre/4Pre/8Pre USB
* - Clarett+ 2Pre/4Pre/8Pre
*
@@ -68,6 +69,12 @@
*
* Support for Clarett 2Pre and 4Pre USB added in Oct 2023.
*
+ * Support for firmware updates added in Dec 2023.
+ *
+ * Support for Scarlett Solo/2i2/4i4 Gen 4 added in Dec 2023 (thanks
+ * to many LinuxMusicians people and to Focusrite for hardware
+ * donations).
+ *
* This ALSA mixer gives access to (model-dependent):
* - input, output, mixer-matrix muxes
* - mixer-matrix gain stages
@@ -78,6 +85,8 @@
* controls
* - disable/enable MSD mode
* - disable/enable standalone mode
+ * - input gain, autogain, safe mode
+ * - direct monitor mixes
*
* <ditaa>
* /--------------\ 18chn 20chn /--------------\
@@ -130,7 +139,7 @@
* \--------------/
* </ditaa>
*
- * Gen 3 devices have a Mass Storage Device (MSD) mode where a small
+ * Gen 3/4 devices have a Mass Storage Device (MSD) mode where a small
* disk with registration and driver download information is presented
* to the host. To access the full functionality of the device without
* proprietary software, MSD mode can be disabled by:
@@ -146,6 +155,9 @@
#include <sound/control.h>
#include <sound/tlv.h>
+#include <sound/hwdep.h>
+
+#include <uapi/sound/scarlett2.h>
#include "usbaudio.h"
#include "mixer.h"
@@ -161,6 +173,7 @@
/* some gui mixers can't handle negative ctl values */
#define SCARLETT2_VOLUME_BIAS 127
+#define SCARLETT2_GAIN_BIAS 70
/* mixer range from -80dB to +6dB in 0.5dB steps */
#define SCARLETT2_MIXER_MIN_DB -80
@@ -193,11 +206,12 @@ static const u16 scarlett2_mixer_values[SCARLETT2_MIXER_VALUE_COUNT] = {
/* Maximum number of analogue outputs */
#define SCARLETT2_ANALOGUE_MAX 10
-/* Maximum number of level and pad switches */
+/* Maximum number of various input controls */
#define SCARLETT2_LEVEL_SWITCH_MAX 2
#define SCARLETT2_PAD_SWITCH_MAX 8
#define SCARLETT2_AIR_SWITCH_MAX 8
#define SCARLETT2_PHANTOM_SWITCH_MAX 2
+#define SCARLETT2_INPUT_GAIN_MAX 2
/* Maximum number of inputs to the mixer */
#define SCARLETT2_INPUT_MIX_MAX 25
@@ -205,6 +219,16 @@ static const u16 scarlett2_mixer_values[SCARLETT2_MIXER_VALUE_COUNT] = {
/* Maximum number of outputs from the mixer */
#define SCARLETT2_OUTPUT_MIX_MAX 12
+/* Maximum number of mixer gain controls */
+#define SCARLETT2_MIX_MAX (SCARLETT2_INPUT_MIX_MAX * SCARLETT2_OUTPUT_MIX_MAX)
+
+/* Maximum number of direct monitor mixer gain controls
+ * 1 (Solo) or 2 (2i2) direct monitor selections (Mono & Stereo)
+ * 2 Mix outputs (A/Left & B/Right)
+ * 4 Mix inputs
+ */
+#define SCARLETT2_MONITOR_MIX_MAX (2 * 2 * 4)
+
/* Maximum size of the data in the USB mux assignment message:
* 20 inputs, 20 outputs, 25 matrix inputs, 12 spare
*/
@@ -216,17 +240,6 @@ static const u16 scarlett2_mixer_values[SCARLETT2_MIXER_VALUE_COUNT] = {
/* Maximum number of meters (sum of output port counts) */
#define SCARLETT2_MAX_METERS 65
-/* There are different sets of configuration parameters across the
- * devices, dependent on series and model.
- */
-enum {
- SCARLETT2_CONFIG_SET_GEN_2 = 0,
- SCARLETT2_CONFIG_SET_GEN_3A = 1,
- SCARLETT2_CONFIG_SET_GEN_3B = 2,
- SCARLETT2_CONFIG_SET_CLARETT = 3,
- SCARLETT2_CONFIG_SET_COUNT = 4
-};
-
/* Hardware port types:
* - None (no input to mux)
* - Analogue I/O
@@ -236,33 +249,515 @@ enum {
* - PCM I/O
*/
enum {
- SCARLETT2_PORT_TYPE_NONE = 0,
- SCARLETT2_PORT_TYPE_ANALOGUE = 1,
- SCARLETT2_PORT_TYPE_SPDIF = 2,
- SCARLETT2_PORT_TYPE_ADAT = 3,
- SCARLETT2_PORT_TYPE_MIX = 4,
- SCARLETT2_PORT_TYPE_PCM = 5,
- SCARLETT2_PORT_TYPE_COUNT = 6,
+ SCARLETT2_PORT_TYPE_NONE,
+ SCARLETT2_PORT_TYPE_ANALOGUE,
+ SCARLETT2_PORT_TYPE_SPDIF,
+ SCARLETT2_PORT_TYPE_ADAT,
+ SCARLETT2_PORT_TYPE_MIX,
+ SCARLETT2_PORT_TYPE_PCM,
+ SCARLETT2_PORT_TYPE_COUNT
};
/* I/O count of each port type kept in struct scarlett2_ports */
enum {
- SCARLETT2_PORT_IN = 0,
- SCARLETT2_PORT_OUT = 1,
- SCARLETT2_PORT_DIRNS = 2,
+ SCARLETT2_PORT_IN,
+ SCARLETT2_PORT_OUT,
+ SCARLETT2_PORT_DIRNS
};
/* Dim/Mute buttons on the 18i20 */
enum {
- SCARLETT2_BUTTON_MUTE = 0,
- SCARLETT2_BUTTON_DIM = 1,
- SCARLETT2_DIM_MUTE_COUNT = 2,
+ SCARLETT2_BUTTON_MUTE,
+ SCARLETT2_BUTTON_DIM,
+ SCARLETT2_DIM_MUTE_COUNT
+};
+
+/* Flash Write State */
+enum {
+ SCARLETT2_FLASH_WRITE_STATE_IDLE,
+ SCARLETT2_FLASH_WRITE_STATE_SELECTED,
+ SCARLETT2_FLASH_WRITE_STATE_ERASING,
+ SCARLETT2_FLASH_WRITE_STATE_WRITE
};
static const char *const scarlett2_dim_mute_names[SCARLETT2_DIM_MUTE_COUNT] = {
"Mute Playback Switch", "Dim Playback Switch"
};
+/* Autogain Status Values */
+enum {
+ SCARLETT2_AUTOGAIN_STATUS_STOPPED,
+ SCARLETT2_AUTOGAIN_STATUS_RUNNING,
+ SCARLETT2_AUTOGAIN_STATUS_FAILED,
+ SCARLETT2_AUTOGAIN_STATUS_CANCELLED,
+ SCARLETT2_AUTOGAIN_STATUS_UNKNOWN,
+ SCARLETT2_AUTOGAIN_STATUS_COUNT
+};
+
+/* Power Status Values */
+enum {
+ SCARLETT2_POWER_STATUS_EXT,
+ SCARLETT2_POWER_STATUS_BUS,
+ SCARLETT2_POWER_STATUS_FAIL,
+ SCARLETT2_POWER_STATUS_COUNT
+};
+
+/* Notification callback functions */
+struct scarlett2_notification {
+ u32 mask;
+ void (*func)(struct usb_mixer_interface *mixer);
+};
+
+static void scarlett2_notify_sync(struct usb_mixer_interface *mixer);
+static void scarlett2_notify_dim_mute(struct usb_mixer_interface *mixer);
+static void scarlett2_notify_monitor(struct usb_mixer_interface *mixer);
+static void scarlett2_notify_volume(struct usb_mixer_interface *mixer);
+static void scarlett2_notify_input_level(struct usb_mixer_interface *mixer);
+static void scarlett2_notify_input_pad(struct usb_mixer_interface *mixer);
+static void scarlett2_notify_input_air(struct usb_mixer_interface *mixer);
+static void scarlett2_notify_input_phantom(struct usb_mixer_interface *mixer);
+static void scarlett2_notify_input_other(struct usb_mixer_interface *mixer);
+static void scarlett2_notify_input_select(struct usb_mixer_interface *mixer);
+static void scarlett2_notify_input_gain(struct usb_mixer_interface *mixer);
+static void scarlett2_notify_autogain(struct usb_mixer_interface *mixer);
+static void scarlett2_notify_input_safe(struct usb_mixer_interface *mixer);
+static void scarlett2_notify_monitor_other(struct usb_mixer_interface *mixer);
+static void scarlett2_notify_direct_monitor(struct usb_mixer_interface *mixer);
+static void scarlett2_notify_power_status(struct usb_mixer_interface *mixer);
+static void scarlett2_notify_pcm_input_switch(
+ struct usb_mixer_interface *mixer);
+
+/* Arrays of notification callback functions */
+
+static const struct scarlett2_notification scarlett2_notifications[] = {
+ { 0x00000001, NULL }, /* ack, gets ignored */
+ { 0x00000008, scarlett2_notify_sync },
+ { 0x00200000, scarlett2_notify_dim_mute },
+ { 0x00400000, scarlett2_notify_monitor },
+ { 0x00800000, scarlett2_notify_input_other },
+ { 0x01000000, scarlett2_notify_monitor_other },
+ { 0, NULL }
+};
+
+static const struct scarlett2_notification scarlett3a_notifications[] = {
+ { 0x00000001, NULL }, /* ack, gets ignored */
+ { 0x00800000, scarlett2_notify_input_other },
+ { 0x01000000, scarlett2_notify_direct_monitor },
+ { 0, NULL }
+};
+
+static const struct scarlett2_notification scarlett4_solo_notifications[] = {
+ { 0x00000001, NULL }, /* ack, gets ignored */
+ { 0x00000008, scarlett2_notify_sync },
+ { 0x00400000, scarlett2_notify_input_air },
+ { 0x00800000, scarlett2_notify_direct_monitor },
+ { 0x01000000, scarlett2_notify_input_level },
+ { 0x02000000, scarlett2_notify_input_phantom },
+ { 0x04000000, scarlett2_notify_pcm_input_switch },
+ { 0, NULL }
+};
+
+static const struct scarlett2_notification scarlett4_2i2_notifications[] = {
+ { 0x00000001, NULL }, /* ack, gets ignored */
+ { 0x00000008, scarlett2_notify_sync },
+ { 0x00200000, scarlett2_notify_input_safe },
+ { 0x00400000, scarlett2_notify_autogain },
+ { 0x00800000, scarlett2_notify_input_air },
+ { 0x01000000, scarlett2_notify_direct_monitor },
+ { 0x02000000, scarlett2_notify_input_select },
+ { 0x04000000, scarlett2_notify_input_level },
+ { 0x08000000, scarlett2_notify_input_phantom },
+ { 0x10000000, NULL }, /* power status, ignored */
+ { 0x40000000, scarlett2_notify_input_gain },
+ { 0x80000000, NULL }, /* power status, ignored */
+ { 0, NULL }
+};
+
+static const struct scarlett2_notification scarlett4_4i4_notifications[] = {
+ { 0x00000001, NULL }, /* ack, gets ignored */
+ { 0x00000008, scarlett2_notify_sync },
+ { 0x00200000, scarlett2_notify_input_safe },
+ { 0x00400000, scarlett2_notify_autogain },
+ { 0x00800000, scarlett2_notify_input_air },
+ { 0x01000000, scarlett2_notify_input_select },
+ { 0x02000000, scarlett2_notify_input_level },
+ { 0x04000000, scarlett2_notify_input_phantom },
+ { 0x08000000, scarlett2_notify_power_status }, /* power external */
+ { 0x20000000, scarlett2_notify_input_gain },
+ { 0x40000000, scarlett2_notify_power_status }, /* power status */
+ { 0x80000000, scarlett2_notify_volume },
+ { 0, NULL }
+};
+
+/* Configuration parameters that can be read and written */
+enum {
+ SCARLETT2_CONFIG_DIM_MUTE,
+ SCARLETT2_CONFIG_LINE_OUT_VOLUME,
+ SCARLETT2_CONFIG_MUTE_SWITCH,
+ SCARLETT2_CONFIG_SW_HW_SWITCH,
+ SCARLETT2_CONFIG_MASTER_VOLUME,
+ SCARLETT2_CONFIG_HEADPHONE_VOLUME,
+ SCARLETT2_CONFIG_LEVEL_SWITCH,
+ SCARLETT2_CONFIG_PAD_SWITCH,
+ SCARLETT2_CONFIG_MSD_SWITCH,
+ SCARLETT2_CONFIG_AIR_SWITCH,
+ SCARLETT2_CONFIG_STANDALONE_SWITCH,
+ SCARLETT2_CONFIG_PHANTOM_SWITCH,
+ SCARLETT2_CONFIG_PHANTOM_PERSISTENCE,
+ SCARLETT2_CONFIG_DIRECT_MONITOR,
+ SCARLETT2_CONFIG_MONITOR_OTHER_SWITCH,
+ SCARLETT2_CONFIG_MONITOR_OTHER_ENABLE,
+ SCARLETT2_CONFIG_TALKBACK_MAP,
+ SCARLETT2_CONFIG_AUTOGAIN_SWITCH,
+ SCARLETT2_CONFIG_AUTOGAIN_STATUS,
+ SCARLETT2_CONFIG_INPUT_GAIN,
+ SCARLETT2_CONFIG_SAFE_SWITCH,
+ SCARLETT2_CONFIG_INPUT_SELECT_SWITCH,
+ SCARLETT2_CONFIG_INPUT_LINK_SWITCH,
+ SCARLETT2_CONFIG_POWER_EXT,
+ SCARLETT2_CONFIG_POWER_STATUS,
+ SCARLETT2_CONFIG_PCM_INPUT_SWITCH,
+ SCARLETT2_CONFIG_DIRECT_MONITOR_GAIN,
+ SCARLETT2_CONFIG_COUNT
+};
+
+/* Location, size, and activation command number for the configuration
+ * parameters. Size is in bits and may be 0, 1, 8, or 16.
+ *
+ * A size of 0 indicates that the parameter is a byte-sized Scarlett
+ * Gen 4 configuration which is written through the gen4_write_addr
+ * location (but still read through the given offset location).
+ *
+ * Some Gen 4 configuration parameters are written with 0x02 for a
+ * desired value of 0x01, and 0x03 for 0x00. These are indicated with
+ * mute set to 1. 0x02 and 0x03 are temporary values while the device
+ * makes the change and the channel and/or corresponding DSP channel
+ * output is muted.
+ */
+struct scarlett2_config {
+ u16 offset;
+ u8 size;
+ u8 activate;
+ u8 mute;
+};
+
+struct scarlett2_config_set {
+ const struct scarlett2_notification *notifications;
+ u16 gen4_write_addr;
+ const struct scarlett2_config items[SCARLETT2_CONFIG_COUNT];
+};
+
+/* Gen 2 devices without SW/HW volume switch: 6i6, 18i8 */
+
+static const struct scarlett2_config_set scarlett2_config_set_gen2a = {
+ .notifications = scarlett2_notifications,
+ .items = {
+ [SCARLETT2_CONFIG_LINE_OUT_VOLUME] = {
+ .offset = 0x34, .size = 16, .activate = 1 },
+
+ [SCARLETT2_CONFIG_MUTE_SWITCH] = {
+ .offset = 0x5c, .size = 8, .activate = 1 },
+
+ [SCARLETT2_CONFIG_LEVEL_SWITCH] = {
+ .offset = 0x7c, .size = 8, .activate = 7 },
+
+ [SCARLETT2_CONFIG_PAD_SWITCH] = {
+ .offset = 0x84, .size = 8, .activate = 8 },
+
+ [SCARLETT2_CONFIG_STANDALONE_SWITCH] = {
+ .offset = 0x8d, .size = 8, .activate = 6 },
+ }
+};
+
+/* Gen 2 devices with SW/HW volume switch: 18i20 */
+
+static const struct scarlett2_config_set scarlett2_config_set_gen2b = {
+ .notifications = scarlett2_notifications,
+ .items = {
+ [SCARLETT2_CONFIG_DIM_MUTE] = {
+ .offset = 0x31, .size = 8, .activate = 2 },
+
+ [SCARLETT2_CONFIG_LINE_OUT_VOLUME] = {
+ .offset = 0x34, .size = 16, .activate = 1 },
+
+ [SCARLETT2_CONFIG_MUTE_SWITCH] = {
+ .offset = 0x5c, .size = 8, .activate = 1 },
+
+ [SCARLETT2_CONFIG_SW_HW_SWITCH] = {
+ .offset = 0x66, .size = 8, .activate = 3 },
+
+ [SCARLETT2_CONFIG_MASTER_VOLUME] = {
+ .offset = 0x76, .size = 16 },
+
+ [SCARLETT2_CONFIG_LEVEL_SWITCH] = {
+ .offset = 0x7c, .size = 8, .activate = 7 },
+
+ [SCARLETT2_CONFIG_PAD_SWITCH] = {
+ .offset = 0x84, .size = 8, .activate = 8 },
+
+ [SCARLETT2_CONFIG_STANDALONE_SWITCH] = {
+ .offset = 0x8d, .size = 8, .activate = 6 },
+ }
+};
+
+/* Gen 3 devices without a mixer (Solo and 2i2) */
+static const struct scarlett2_config_set scarlett2_config_set_gen3a = {
+ .notifications = scarlett3a_notifications,
+ .items = {
+ [SCARLETT2_CONFIG_MSD_SWITCH] = {
+ .offset = 0x04, .size = 8, .activate = 6 },
+
+ [SCARLETT2_CONFIG_PHANTOM_PERSISTENCE] = {
+ .offset = 0x05, .size = 8, .activate = 6 },
+
+ [SCARLETT2_CONFIG_PHANTOM_SWITCH] = {
+ .offset = 0x06, .size = 8, .activate = 3 },
+
+ [SCARLETT2_CONFIG_DIRECT_MONITOR] = {
+ .offset = 0x07, .size = 8, .activate = 4 },
+
+ [SCARLETT2_CONFIG_LEVEL_SWITCH] = {
+ .offset = 0x08, .size = 1, .activate = 7 },
+
+ [SCARLETT2_CONFIG_AIR_SWITCH] = {
+ .offset = 0x09, .size = 1, .activate = 8 },
+ }
+};
+
+/* Gen 3 devices without SW/HW volume switch: 4i4, 8i6 */
+static const struct scarlett2_config_set scarlett2_config_set_gen3b = {
+ .notifications = scarlett2_notifications,
+ .items = {
+ [SCARLETT2_CONFIG_LINE_OUT_VOLUME] = {
+ .offset = 0x34, .size = 16, .activate = 1 },
+
+ [SCARLETT2_CONFIG_MUTE_SWITCH] = {
+ .offset = 0x5c, .size = 8, .activate = 1 },
+
+ [SCARLETT2_CONFIG_LEVEL_SWITCH] = {
+ .offset = 0x7c, .size = 8, .activate = 7 },
+
+ [SCARLETT2_CONFIG_PAD_SWITCH] = {
+ .offset = 0x84, .size = 8, .activate = 8 },
+
+ [SCARLETT2_CONFIG_AIR_SWITCH] = {
+ .offset = 0x8c, .size = 8, .activate = 8 },
+
+ [SCARLETT2_CONFIG_STANDALONE_SWITCH] = {
+ .offset = 0x95, .size = 8, .activate = 6 },
+
+ [SCARLETT2_CONFIG_PHANTOM_SWITCH] = {
+ .offset = 0x9c, .size = 1, .activate = 8 },
+
+ [SCARLETT2_CONFIG_MSD_SWITCH] = {
+ .offset = 0x9d, .size = 8, .activate = 6 },
+
+ [SCARLETT2_CONFIG_PHANTOM_PERSISTENCE] = {
+ .offset = 0x9e, .size = 8, .activate = 6 },
+ }
+};
+
+/* Gen 3 devices with SW/HW volume switch: 18i8, 18i20 */
+static const struct scarlett2_config_set scarlett2_config_set_gen3c = {
+ .notifications = scarlett2_notifications,
+ .items = {
+ [SCARLETT2_CONFIG_DIM_MUTE] = {
+ .offset = 0x31, .size = 8, .activate = 2 },
+
+ [SCARLETT2_CONFIG_LINE_OUT_VOLUME] = {
+ .offset = 0x34, .size = 16, .activate = 1 },
+
+ [SCARLETT2_CONFIG_MUTE_SWITCH] = {
+ .offset = 0x5c, .size = 8, .activate = 1 },
+
+ [SCARLETT2_CONFIG_SW_HW_SWITCH] = {
+ .offset = 0x66, .size = 8, .activate = 3 },
+
+ [SCARLETT2_CONFIG_MASTER_VOLUME] = {
+ .offset = 0x76, .size = 16 },
+
+ [SCARLETT2_CONFIG_LEVEL_SWITCH] = {
+ .offset = 0x7c, .size = 8, .activate = 7 },
+
+ [SCARLETT2_CONFIG_PAD_SWITCH] = {
+ .offset = 0x84, .size = 8, .activate = 8 },
+
+ [SCARLETT2_CONFIG_AIR_SWITCH] = {
+ .offset = 0x8c, .size = 8, .activate = 8 },
+
+ [SCARLETT2_CONFIG_STANDALONE_SWITCH] = {
+ .offset = 0x95, .size = 8, .activate = 6 },
+
+ [SCARLETT2_CONFIG_PHANTOM_SWITCH] = {
+ .offset = 0x9c, .size = 1, .activate = 8 },
+
+ [SCARLETT2_CONFIG_MSD_SWITCH] = {
+ .offset = 0x9d, .size = 8, .activate = 6 },
+
+ [SCARLETT2_CONFIG_PHANTOM_PERSISTENCE] = {
+ .offset = 0x9e, .size = 8, .activate = 6 },
+
+ [SCARLETT2_CONFIG_MONITOR_OTHER_SWITCH] = {
+ .offset = 0x9f, .size = 1, .activate = 10 },
+
+ [SCARLETT2_CONFIG_MONITOR_OTHER_ENABLE] = {
+ .offset = 0xa0, .size = 1, .activate = 10 },
+
+ [SCARLETT2_CONFIG_TALKBACK_MAP] = {
+ .offset = 0xb0, .size = 16, .activate = 10 },
+ }
+};
+
+/* Solo Gen 4 */
+static const struct scarlett2_config_set scarlett2_config_set_gen4_solo = {
+ .notifications = scarlett4_solo_notifications,
+ .gen4_write_addr = 0xd8,
+ .items = {
+ [SCARLETT2_CONFIG_MSD_SWITCH] = {
+ .offset = 0x47, .size = 8, .activate = 4 },
+
+ [SCARLETT2_CONFIG_DIRECT_MONITOR] = {
+ .offset = 0x108, .activate = 12 },
+
+ [SCARLETT2_CONFIG_PHANTOM_SWITCH] = {
+ .offset = 0x46, .activate = 9, .mute = 1 },
+
+ [SCARLETT2_CONFIG_LEVEL_SWITCH] = {
+ .offset = 0x3d, .activate = 10, .mute = 1 },
+
+ [SCARLETT2_CONFIG_AIR_SWITCH] = {
+ .offset = 0x3e, .activate = 11 },
+
+ [SCARLETT2_CONFIG_PCM_INPUT_SWITCH] = {
+ .offset = 0x206, .activate = 25 },
+
+ [SCARLETT2_CONFIG_DIRECT_MONITOR_GAIN] = {
+ .offset = 0x232, .size = 16, .activate = 26 }
+ }
+};
+
+/* 2i2 Gen 4 */
+static const struct scarlett2_config_set scarlett2_config_set_gen4_2i2 = {
+ .notifications = scarlett4_2i2_notifications,
+ .gen4_write_addr = 0xfc,
+ .items = {
+ [SCARLETT2_CONFIG_MSD_SWITCH] = {
+ .offset = 0x49, .size = 8, .activate = 4 }, // 0x41 ??
+
+ [SCARLETT2_CONFIG_DIRECT_MONITOR] = {
+ .offset = 0x14a, .activate = 16 },
+
+ [SCARLETT2_CONFIG_AUTOGAIN_SWITCH] = {
+ .offset = 0x135, .activate = 10 },
+
+ [SCARLETT2_CONFIG_AUTOGAIN_STATUS] = {
+ .offset = 0x137 },
+
+ [SCARLETT2_CONFIG_PHANTOM_SWITCH] = {
+ .offset = 0x48, .activate = 11, .mute = 1 },
+
+ [SCARLETT2_CONFIG_INPUT_GAIN] = {
+ .offset = 0x4b, .activate = 12 },
+
+ [SCARLETT2_CONFIG_LEVEL_SWITCH] = {
+ .offset = 0x3c, .activate = 13, .mute = 1 },
+
+ [SCARLETT2_CONFIG_SAFE_SWITCH] = {
+ .offset = 0x147, .activate = 14 },
+
+ [SCARLETT2_CONFIG_AIR_SWITCH] = {
+ .offset = 0x3e, .activate = 15 },
+
+ [SCARLETT2_CONFIG_INPUT_SELECT_SWITCH] = {
+ .offset = 0x14b, .activate = 17 },
+
+ [SCARLETT2_CONFIG_INPUT_LINK_SWITCH] = {
+ .offset = 0x14e, .activate = 18 },
+
+ [SCARLETT2_CONFIG_DIRECT_MONITOR_GAIN] = {
+ .offset = 0x2a0, .size = 16, .activate = 36 }
+ }
+};
+
+/* 4i4 Gen 4 */
+static const struct scarlett2_config_set scarlett2_config_set_gen4_4i4 = {
+ .notifications = scarlett4_4i4_notifications,
+ .gen4_write_addr = 0x130,
+ .items = {
+ [SCARLETT2_CONFIG_MSD_SWITCH] = {
+ .offset = 0x5c, .size = 8, .activate = 4 },
+
+ [SCARLETT2_CONFIG_AUTOGAIN_SWITCH] = {
+ .offset = 0x13e, .activate = 10 },
+
+ [SCARLETT2_CONFIG_AUTOGAIN_STATUS] = {
+ .offset = 0x140 },
+
+ [SCARLETT2_CONFIG_PHANTOM_SWITCH] = {
+ .offset = 0x5a, .activate = 11, .mute = 1 },
+
+ [SCARLETT2_CONFIG_INPUT_GAIN] = {
+ .offset = 0x5e, .activate = 12 },
+
+ [SCARLETT2_CONFIG_LEVEL_SWITCH] = {
+ .offset = 0x4e, .activate = 13, .mute = 1 },
+
+ [SCARLETT2_CONFIG_SAFE_SWITCH] = {
+ .offset = 0x150, .activate = 14 },
+
+ [SCARLETT2_CONFIG_AIR_SWITCH] = {
+ .offset = 0x50, .activate = 15 },
+
+ [SCARLETT2_CONFIG_INPUT_SELECT_SWITCH] = {
+ .offset = 0x153, .activate = 16 },
+
+ [SCARLETT2_CONFIG_INPUT_LINK_SWITCH] = {
+ .offset = 0x156, .activate = 17 },
+
+ [SCARLETT2_CONFIG_MASTER_VOLUME] = {
+ .offset = 0x32, .size = 16 },
+
+ [SCARLETT2_CONFIG_HEADPHONE_VOLUME] = {
+ .offset = 0x3a, .size = 16 },
+
+ [SCARLETT2_CONFIG_POWER_EXT] = {
+ .offset = 0x168 },
+
+ [SCARLETT2_CONFIG_POWER_STATUS] = {
+ .offset = 0x66 }
+ }
+};
+
+/* Clarett USB and Clarett+ devices: 2Pre, 4Pre, 8Pre */
+static const struct scarlett2_config_set scarlett2_config_set_clarett = {
+ .notifications = scarlett2_notifications,
+ .items = {
+ [SCARLETT2_CONFIG_DIM_MUTE] = {
+ .offset = 0x31, .size = 8, .activate = 2 },
+
+ [SCARLETT2_CONFIG_LINE_OUT_VOLUME] = {
+ .offset = 0x34, .size = 16, .activate = 1 },
+
+ [SCARLETT2_CONFIG_MUTE_SWITCH] = {
+ .offset = 0x5c, .size = 8, .activate = 1 },
+
+ [SCARLETT2_CONFIG_SW_HW_SWITCH] = {
+ .offset = 0x66, .size = 8, .activate = 3 },
+
+ [SCARLETT2_CONFIG_MASTER_VOLUME] = {
+ .offset = 0x76, .size = 16 },
+
+ [SCARLETT2_CONFIG_LEVEL_SWITCH] = {
+ .offset = 0x7c, .size = 8, .activate = 7 },
+
+ [SCARLETT2_CONFIG_AIR_SWITCH] = {
+ .offset = 0x95, .size = 8, .activate = 8 },
+
+ [SCARLETT2_CONFIG_STANDALONE_SWITCH] = {
+ .offset = 0x8d, .size = 8, .activate = 6 },
+ }
+};
+
/* Description of each hardware port type:
* - id: hardware ID of this port type
* - src_descr: printf format string for mux input selections
@@ -274,6 +769,8 @@ struct scarlett2_port {
const char * const src_descr;
int src_num_offset;
const char * const dst_descr;
+ const char * const dsp_src_descr;
+ const char * const dsp_dst_descr;
};
static const struct scarlett2_port scarlett2_ports[SCARLETT2_PORT_TYPE_COUNT] = {
@@ -303,7 +800,9 @@ static const struct scarlett2_port scarlett2_ports[SCARLETT2_PORT_TYPE_COUNT] =
.id = 0x300,
.src_descr = "Mix %c",
.src_num_offset = 'A',
- .dst_descr = "Mixer Input %02d Capture"
+ .dst_descr = "Mixer Input %02d Capture",
+ .dsp_src_descr = "DSP %d",
+ .dsp_dst_descr = "DSP Input %d Capture"
},
[SCARLETT2_PORT_TYPE_PCM] = {
.id = 0x600,
@@ -344,17 +843,11 @@ struct scarlett2_meter_entry {
};
struct scarlett2_device_info {
- /* Gen 3 devices have an internal MSD mode switch that needs
- * to be disabled in order to access the full functionality of
- * the device.
- */
- u8 has_msd_mode;
-
/* which set of configuration parameters the device uses */
- u8 config_set;
+ const struct scarlett2_config_set *config_set;
- /* line out hw volume is sw controlled */
- u8 line_out_hw_vol;
+ /* minimum firmware version required */
+ u16 min_firmware_version;
/* support for main/alt speaker switching */
u8 has_speaker_switching;
@@ -380,17 +873,35 @@ struct scarlett2_device_info {
*/
u8 air_input_count;
+ /* the first input with an air control (0-based) */
+ u8 air_input_first;
+
+ /* number of additional air options
+ * 0 for air presence only (Gen 3)
+ * 1 for air presence+drive (Gen 4)
+ */
+ u8 air_option;
+
/* the number of phantom (48V) software switchable controls */
u8 phantom_count;
+ /* the first input with phantom power control (0-based) */
+ u8 phantom_first;
+
/* the number of inputs each phantom switch controls */
u8 inputs_per_phantom;
+ /* the number of inputs with software-controllable gain */
+ u8 gain_input_count;
+
/* the number of direct monitor options
* (0 = none, 1 = mono only, 2 = mono/stereo)
*/
u8 direct_monitor;
+ /* the number of DSP channels */
+ u8 dsp_count;
+
/* remap analogue outputs; 18i8 Gen 3 has "line 3/4" connected
* internally to the analogue 7/8 outputs
*/
@@ -419,25 +930,48 @@ struct scarlett2_data {
struct usb_mixer_interface *mixer;
struct mutex usb_mutex; /* prevent sending concurrent USB requests */
struct mutex data_mutex; /* lock access to this data */
+ u8 hwdep_in_use;
+ u8 selected_flash_segment_id;
+ u8 flash_write_state;
struct delayed_work work;
const struct scarlett2_device_info *info;
+ const struct scarlett2_config_set *config_set;
const char *series_name;
__u8 bInterfaceNumber;
__u8 bEndpointAddress;
__u16 wMaxPacketSize;
__u8 bInterval;
- int num_mux_srcs;
- int num_mux_dsts;
+ u8 num_mux_srcs;
+ u8 num_mux_dsts;
+ u8 num_mix_in;
+ u8 num_mix_out;
+ u8 num_line_out;
+ u8 num_monitor_mix_ctls;
u32 firmware_version;
+ u8 flash_segment_nums[SCARLETT2_SEGMENT_ID_COUNT];
+ u8 flash_segment_blocks[SCARLETT2_SEGMENT_ID_COUNT];
u16 scarlett2_seq;
u8 sync_updated;
u8 vol_updated;
- u8 input_other_updated;
+ u8 dim_mute_updated;
+ u8 input_level_updated;
+ u8 input_pad_updated;
+ u8 input_air_updated;
+ u8 input_phantom_updated;
+ u8 input_select_updated;
+ u8 input_gain_updated;
+ u8 autogain_updated;
+ u8 input_safe_updated;
+ u8 pcm_input_switch_updated;
u8 monitor_other_updated;
+ u8 direct_monitor_updated;
u8 mux_updated;
+ u8 mix_updated;
u8 speaker_switching_switched;
+ u8 power_status_updated;
u8 sync;
u8 master_vol;
+ u8 headphone_vol;
u8 vol[SCARLETT2_ANALOGUE_MAX];
u8 vol_sw_hw_switch[SCARLETT2_ANALOGUE_MAX];
u8 mute_switch[SCARLETT2_ANALOGUE_MAX];
@@ -447,15 +981,24 @@ struct scarlett2_data {
u8 air_switch[SCARLETT2_AIR_SWITCH_MAX];
u8 phantom_switch[SCARLETT2_PHANTOM_SWITCH_MAX];
u8 phantom_persistence;
+ u8 input_select_switch;
+ u8 input_link_switch[SCARLETT2_INPUT_GAIN_MAX / 2];
+ u8 gain[SCARLETT2_INPUT_GAIN_MAX];
+ u8 autogain_switch[SCARLETT2_INPUT_GAIN_MAX];
+ u8 autogain_status[SCARLETT2_INPUT_GAIN_MAX];
+ u8 safe_switch[SCARLETT2_INPUT_GAIN_MAX];
+ u8 pcm_input_switch;
u8 direct_monitor_switch;
u8 speaker_switching_switch;
u8 talkback_switch;
u8 talkback_map[SCARLETT2_OUTPUT_MIX_MAX];
u8 msd_switch;
u8 standalone_switch;
+ u8 power_status;
u8 meter_level_map[SCARLETT2_MAX_METERS];
struct snd_kcontrol *sync_ctl;
struct snd_kcontrol *master_vol_ctl;
+ struct snd_kcontrol *headphone_vol_ctl;
struct snd_kcontrol *vol_ctls[SCARLETT2_ANALOGUE_MAX];
struct snd_kcontrol *sw_hw_ctls[SCARLETT2_ANALOGUE_MAX];
struct snd_kcontrol *mute_ctls[SCARLETT2_ANALOGUE_MAX];
@@ -464,18 +1007,28 @@ struct scarlett2_data {
struct snd_kcontrol *pad_ctls[SCARLETT2_PAD_SWITCH_MAX];
struct snd_kcontrol *air_ctls[SCARLETT2_AIR_SWITCH_MAX];
struct snd_kcontrol *phantom_ctls[SCARLETT2_PHANTOM_SWITCH_MAX];
+ struct snd_kcontrol *input_select_ctl;
+ struct snd_kcontrol *input_link_ctls[SCARLETT2_INPUT_GAIN_MAX / 2];
+ struct snd_kcontrol *input_gain_ctls[SCARLETT2_INPUT_GAIN_MAX];
+ struct snd_kcontrol *autogain_ctls[SCARLETT2_INPUT_GAIN_MAX];
+ struct snd_kcontrol *autogain_status_ctls[SCARLETT2_INPUT_GAIN_MAX];
+ struct snd_kcontrol *safe_ctls[SCARLETT2_INPUT_GAIN_MAX];
+ struct snd_kcontrol *pcm_input_switch_ctl;
struct snd_kcontrol *mux_ctls[SCARLETT2_MUX_MAX];
+ struct snd_kcontrol *mix_ctls[SCARLETT2_MIX_MAX];
struct snd_kcontrol *direct_monitor_ctl;
struct snd_kcontrol *speaker_switching_ctl;
struct snd_kcontrol *talkback_ctl;
+ struct snd_kcontrol *power_status_ctl;
u8 mux[SCARLETT2_MUX_MAX];
- u8 mix[SCARLETT2_INPUT_MIX_MAX * SCARLETT2_OUTPUT_MIX_MAX];
+ u8 mix[SCARLETT2_MIX_MAX];
+ u8 monitor_mix[SCARLETT2_MONITOR_MIX_MAX];
};
/*** Model-specific data ***/
static const struct scarlett2_device_info s6i6_gen2_info = {
- .config_set = SCARLETT2_CONFIG_SET_GEN_2,
+ .config_set = &scarlett2_config_set_gen2a,
.level_input_count = 2,
.pad_input_count = 2,
@@ -525,7 +1078,7 @@ static const struct scarlett2_device_info s6i6_gen2_info = {
};
static const struct scarlett2_device_info s18i8_gen2_info = {
- .config_set = SCARLETT2_CONFIG_SET_GEN_2,
+ .config_set = &scarlett2_config_set_gen2a,
.level_input_count = 2,
.pad_input_count = 4,
@@ -578,8 +1131,7 @@ static const struct scarlett2_device_info s18i8_gen2_info = {
};
static const struct scarlett2_device_info s18i20_gen2_info = {
- .config_set = SCARLETT2_CONFIG_SET_GEN_2,
- .line_out_hw_vol = 1,
+ .config_set = &scarlett2_config_set_gen2b,
.line_out_descrs = {
"Monitor L",
@@ -636,8 +1188,7 @@ static const struct scarlett2_device_info s18i20_gen2_info = {
};
static const struct scarlett2_device_info solo_gen3_info = {
- .has_msd_mode = 1,
- .config_set = SCARLETT2_CONFIG_SET_GEN_3A,
+ .config_set = &scarlett2_config_set_gen3a,
.level_input_count = 1,
.level_input_first = 1,
.air_input_count = 1,
@@ -647,8 +1198,7 @@ static const struct scarlett2_device_info solo_gen3_info = {
};
static const struct scarlett2_device_info s2i2_gen3_info = {
- .has_msd_mode = 1,
- .config_set = SCARLETT2_CONFIG_SET_GEN_3A,
+ .config_set = &scarlett2_config_set_gen3a,
.level_input_count = 2,
.air_input_count = 2,
.phantom_count = 1,
@@ -657,8 +1207,7 @@ static const struct scarlett2_device_info s2i2_gen3_info = {
};
static const struct scarlett2_device_info s4i4_gen3_info = {
- .has_msd_mode = 1,
- .config_set = SCARLETT2_CONFIG_SET_GEN_3B,
+ .config_set = &scarlett2_config_set_gen3b,
.level_input_count = 2,
.pad_input_count = 2,
.air_input_count = 2,
@@ -707,8 +1256,7 @@ static const struct scarlett2_device_info s4i4_gen3_info = {
};
static const struct scarlett2_device_info s8i6_gen3_info = {
- .has_msd_mode = 1,
- .config_set = SCARLETT2_CONFIG_SET_GEN_3B,
+ .config_set = &scarlett2_config_set_gen3b,
.level_input_count = 2,
.pad_input_count = 2,
.air_input_count = 2,
@@ -766,9 +1314,7 @@ static const struct scarlett2_device_info s8i6_gen3_info = {
};
static const struct scarlett2_device_info s18i8_gen3_info = {
- .has_msd_mode = 1,
- .config_set = SCARLETT2_CONFIG_SET_GEN_3B,
- .line_out_hw_vol = 1,
+ .config_set = &scarlett2_config_set_gen3c,
.has_speaker_switching = 1,
.level_input_count = 2,
.pad_input_count = 4,
@@ -847,9 +1393,7 @@ static const struct scarlett2_device_info s18i8_gen3_info = {
};
static const struct scarlett2_device_info s18i20_gen3_info = {
- .has_msd_mode = 1,
- .config_set = SCARLETT2_CONFIG_SET_GEN_3B,
- .line_out_hw_vol = 1,
+ .config_set = &scarlett2_config_set_gen3c,
.has_speaker_switching = 1,
.has_talkback = 1,
.level_input_count = 2,
@@ -918,9 +1462,162 @@ static const struct scarlett2_device_info s18i20_gen3_info = {
}
};
+static const struct scarlett2_device_info solo_gen4_info = {
+ .config_set = &scarlett2_config_set_gen4_solo,
+ .min_firmware_version = 2115,
+
+ .level_input_count = 1,
+ .air_input_count = 1,
+ .air_input_first = 1,
+ .air_option = 1,
+ .phantom_count = 1,
+ .phantom_first = 1,
+ .inputs_per_phantom = 1,
+ .direct_monitor = 1,
+ .dsp_count = 2,
+
+ .port_count = {
+ [SCARLETT2_PORT_TYPE_NONE] = { 1, 0 },
+ [SCARLETT2_PORT_TYPE_ANALOGUE] = { 2, 2 },
+ [SCARLETT2_PORT_TYPE_MIX] = { 8, 6 },
+ [SCARLETT2_PORT_TYPE_PCM] = { 2, 4 },
+ },
+
+ .mux_assignment = { {
+ { SCARLETT2_PORT_TYPE_MIX, 4, 2 },
+ { SCARLETT2_PORT_TYPE_MIX, 2, 2 },
+ { SCARLETT2_PORT_TYPE_PCM, 0, 4 },
+ { SCARLETT2_PORT_TYPE_MIX, 0, 2 },
+ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 2 },
+ { 0, 0, 0 },
+ }, {
+ { SCARLETT2_PORT_TYPE_MIX, 4, 2 },
+ { SCARLETT2_PORT_TYPE_MIX, 2, 2 },
+ { SCARLETT2_PORT_TYPE_PCM, 0, 4 },
+ { SCARLETT2_PORT_TYPE_MIX, 0, 2 },
+ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 2 },
+ { 0, 0, 0 },
+ }, {
+ { SCARLETT2_PORT_TYPE_MIX, 4, 2 },
+ { SCARLETT2_PORT_TYPE_MIX, 2, 2 },
+ { SCARLETT2_PORT_TYPE_PCM, 0, 4 },
+ { SCARLETT2_PORT_TYPE_MIX, 0, 2 },
+ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 2 },
+ { 0, 0, 0 },
+ } },
+
+ .meter_map = {
+ { 6, 2 },
+ { 4, 2 },
+ { 8, 4 },
+ { 2, 2 },
+ { 0, 2 },
+ { 0, 0 }
+ }
+};
+
+static const struct scarlett2_device_info s2i2_gen4_info = {
+ .config_set = &scarlett2_config_set_gen4_2i2,
+ .min_firmware_version = 2115,
+
+ .level_input_count = 2,
+ .air_input_count = 2,
+ .air_option = 1,
+ .phantom_count = 1,
+ .inputs_per_phantom = 2,
+ .gain_input_count = 2,
+ .direct_monitor = 2,
+ .dsp_count = 2,
+
+ .port_count = {
+ [SCARLETT2_PORT_TYPE_NONE] = { 1, 0 },
+ [SCARLETT2_PORT_TYPE_ANALOGUE] = { 2, 2 },
+ [SCARLETT2_PORT_TYPE_MIX] = { 6, 6 },
+ [SCARLETT2_PORT_TYPE_PCM] = { 2, 4 },
+ },
+
+ .mux_assignment = { {
+ { SCARLETT2_PORT_TYPE_MIX, 4, 2 },
+ { SCARLETT2_PORT_TYPE_MIX, 2, 2 },
+ { SCARLETT2_PORT_TYPE_PCM, 0, 4 },
+ { SCARLETT2_PORT_TYPE_MIX, 0, 2 },
+ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 2 },
+ { 0, 0, 0 },
+ }, {
+ { SCARLETT2_PORT_TYPE_MIX, 4, 2 },
+ { SCARLETT2_PORT_TYPE_MIX, 2, 2 },
+ { SCARLETT2_PORT_TYPE_PCM, 0, 4 },
+ { SCARLETT2_PORT_TYPE_MIX, 0, 2 },
+ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 2 },
+ { 0, 0, 0 },
+ }, {
+ { SCARLETT2_PORT_TYPE_MIX, 4, 2 },
+ { SCARLETT2_PORT_TYPE_MIX, 2, 2 },
+ { SCARLETT2_PORT_TYPE_PCM, 0, 4 },
+ { SCARLETT2_PORT_TYPE_MIX, 0, 2 },
+ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 2 },
+ { 0, 0, 0 },
+ } },
+
+ .meter_map = {
+ { 6, 2 },
+ { 4, 2 },
+ { 8, 4 },
+ { 2, 2 },
+ { 0, 2 },
+ { 0, 0 }
+ }
+};
+
+static const struct scarlett2_device_info s4i4_gen4_info = {
+ .config_set = &scarlett2_config_set_gen4_4i4,
+ .min_firmware_version = 2089,
+
+ .level_input_count = 2,
+ .air_input_count = 2,
+ .air_option = 1,
+ .phantom_count = 2,
+ .inputs_per_phantom = 1,
+ .gain_input_count = 2,
+ .dsp_count = 2,
+
+ .port_count = {
+ [SCARLETT2_PORT_TYPE_NONE] = { 1, 0 },
+ [SCARLETT2_PORT_TYPE_ANALOGUE] = { 4, 6 },
+ [SCARLETT2_PORT_TYPE_MIX] = { 8, 12 },
+ [SCARLETT2_PORT_TYPE_PCM] = { 6, 6 },
+ },
+
+ .mux_assignment = { {
+ { SCARLETT2_PORT_TYPE_MIX, 10, 2 },
+ { SCARLETT2_PORT_TYPE_PCM, 0, 6 },
+ { SCARLETT2_PORT_TYPE_MIX, 0, 10 },
+ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 6 },
+ { 0, 0, 0 },
+ }, {
+ { SCARLETT2_PORT_TYPE_MIX, 10, 2 },
+ { SCARLETT2_PORT_TYPE_PCM, 0, 6 },
+ { SCARLETT2_PORT_TYPE_MIX, 0, 10 },
+ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 6 },
+ { 0, 0, 0 },
+ }, {
+ { SCARLETT2_PORT_TYPE_MIX, 10, 2 },
+ { SCARLETT2_PORT_TYPE_PCM, 0, 6 },
+ { SCARLETT2_PORT_TYPE_MIX, 0, 10 },
+ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 6 },
+ { 0, 0, 0 },
+ } },
+
+ .meter_map = {
+ { 16, 8 },
+ { 6, 10 },
+ { 0, 6 },
+ { 0, 0 }
+ }
+};
+
static const struct scarlett2_device_info clarett_2pre_info = {
- .config_set = SCARLETT2_CONFIG_SET_CLARETT,
- .line_out_hw_vol = 1,
+ .config_set = &scarlett2_config_set_clarett,
.level_input_count = 2,
.air_input_count = 2,
@@ -967,8 +1664,7 @@ static const struct scarlett2_device_info clarett_2pre_info = {
};
static const struct scarlett2_device_info clarett_4pre_info = {
- .config_set = SCARLETT2_CONFIG_SET_CLARETT,
- .line_out_hw_vol = 1,
+ .config_set = &scarlett2_config_set_clarett,
.level_input_count = 2,
.air_input_count = 4,
@@ -1020,8 +1716,7 @@ static const struct scarlett2_device_info clarett_4pre_info = {
};
static const struct scarlett2_device_info clarett_8pre_info = {
- .config_set = SCARLETT2_CONFIG_SET_CLARETT,
- .line_out_hw_vol = 1,
+ .config_set = &scarlett2_config_set_clarett,
.level_input_count = 2,
.air_input_count = 8,
@@ -1098,6 +1793,11 @@ static const struct scarlett2_device_entry scarlett2_devices[] = {
{ USB_ID(0x1235, 0x8214), &s18i8_gen3_info, "Scarlett Gen 3" },
{ USB_ID(0x1235, 0x8215), &s18i20_gen3_info, "Scarlett Gen 3" },
+ /* Supported Gen 4 devices */
+ { USB_ID(0x1235, 0x8218), &solo_gen4_info, "Scarlett Gen 4" },
+ { USB_ID(0x1235, 0x8219), &s2i2_gen4_info, "Scarlett Gen 4" },
+ { USB_ID(0x1235, 0x821a), &s4i4_gen4_info, "Scarlett Gen 4" },
+
/* Supported Clarett USB/Clarett+ devices */
{ USB_ID(0x1235, 0x8206), &clarett_2pre_info, "Clarett USB" },
{ USB_ID(0x1235, 0x8207), &clarett_4pre_info, "Clarett USB" },
@@ -1125,203 +1825,40 @@ static int scarlett2_get_port_start_num(
/*** USB Interactions ***/
-/* Notifications from the interface */
-#define SCARLETT2_USB_NOTIFY_SYNC 0x00000008
-#define SCARLETT2_USB_NOTIFY_DIM_MUTE 0x00200000
-#define SCARLETT2_USB_NOTIFY_MONITOR 0x00400000
-#define SCARLETT2_USB_NOTIFY_INPUT_OTHER 0x00800000
-#define SCARLETT2_USB_NOTIFY_MONITOR_OTHER 0x01000000
-
/* Commands for sending/receiving requests/responses */
#define SCARLETT2_USB_CMD_INIT 0
#define SCARLETT2_USB_CMD_REQ 2
#define SCARLETT2_USB_CMD_RESP 3
-#define SCARLETT2_USB_INIT_1 0x00000000
-#define SCARLETT2_USB_INIT_2 0x00000002
-#define SCARLETT2_USB_GET_METER 0x00001001
-#define SCARLETT2_USB_GET_MIX 0x00002001
-#define SCARLETT2_USB_SET_MIX 0x00002002
-#define SCARLETT2_USB_GET_MUX 0x00003001
-#define SCARLETT2_USB_SET_MUX 0x00003002
-#define SCARLETT2_USB_GET_SYNC 0x00006004
-#define SCARLETT2_USB_GET_DATA 0x00800000
-#define SCARLETT2_USB_SET_DATA 0x00800001
-#define SCARLETT2_USB_DATA_CMD 0x00800002
+#define SCARLETT2_USB_INIT_1 0x00000000
+#define SCARLETT2_USB_INIT_2 0x00000002
+#define SCARLETT2_USB_REBOOT 0x00000003
+#define SCARLETT2_USB_GET_METER 0x00001001
+#define SCARLETT2_USB_GET_MIX 0x00002001
+#define SCARLETT2_USB_SET_MIX 0x00002002
+#define SCARLETT2_USB_GET_MUX 0x00003001
+#define SCARLETT2_USB_SET_MUX 0x00003002
+#define SCARLETT2_USB_INFO_FLASH 0x00004000
+#define SCARLETT2_USB_INFO_SEGMENT 0x00004001
+#define SCARLETT2_USB_ERASE_SEGMENT 0x00004002
+#define SCARLETT2_USB_GET_ERASE 0x00004003
+#define SCARLETT2_USB_WRITE_SEGMENT 0x00004004
+#define SCARLETT2_USB_GET_SYNC 0x00006004
+#define SCARLETT2_USB_GET_DATA 0x00800000
+#define SCARLETT2_USB_SET_DATA 0x00800001
+#define SCARLETT2_USB_DATA_CMD 0x00800002
#define SCARLETT2_USB_CONFIG_SAVE 6
-#define SCARLETT2_USB_VOLUME_STATUS_OFFSET 0x31
#define SCARLETT2_USB_METER_LEVELS_GET_MAGIC 1
-/* volume status is read together (matches scarlett2_config_items[1]) */
-struct scarlett2_usb_volume_status {
- /* dim/mute buttons */
- u8 dim_mute[SCARLETT2_DIM_MUTE_COUNT];
-
- u8 pad1;
-
- /* software volume setting */
- s16 sw_vol[SCARLETT2_ANALOGUE_MAX];
-
- /* actual volume of output inc. dim (-18dB) */
- s16 hw_vol[SCARLETT2_ANALOGUE_MAX];
-
- /* internal mute buttons */
- u8 mute_switch[SCARLETT2_ANALOGUE_MAX];
-
- /* sw (0) or hw (1) controlled */
- u8 sw_hw_switch[SCARLETT2_ANALOGUE_MAX];
-
- u8 pad3[6];
-
- /* front panel volume knob */
- s16 master_vol;
-} __packed;
-
-/* Configuration parameters that can be read and written */
-enum {
- SCARLETT2_CONFIG_DIM_MUTE = 0,
- SCARLETT2_CONFIG_LINE_OUT_VOLUME = 1,
- SCARLETT2_CONFIG_MUTE_SWITCH = 2,
- SCARLETT2_CONFIG_SW_HW_SWITCH = 3,
- SCARLETT2_CONFIG_LEVEL_SWITCH = 4,
- SCARLETT2_CONFIG_PAD_SWITCH = 5,
- SCARLETT2_CONFIG_MSD_SWITCH = 6,
- SCARLETT2_CONFIG_AIR_SWITCH = 7,
- SCARLETT2_CONFIG_STANDALONE_SWITCH = 8,
- SCARLETT2_CONFIG_PHANTOM_SWITCH = 9,
- SCARLETT2_CONFIG_PHANTOM_PERSISTENCE = 10,
- SCARLETT2_CONFIG_DIRECT_MONITOR = 11,
- SCARLETT2_CONFIG_MONITOR_OTHER_SWITCH = 12,
- SCARLETT2_CONFIG_MONITOR_OTHER_ENABLE = 13,
- SCARLETT2_CONFIG_TALKBACK_MAP = 14,
- SCARLETT2_CONFIG_COUNT = 15
-};
-
-/* Location, size, and activation command number for the configuration
- * parameters. Size is in bits and may be 1, 8, or 16.
- */
-struct scarlett2_config {
- u8 offset;
- u8 size;
- u8 activate;
-};
-
-static const struct scarlett2_config
- scarlett2_config_items[SCARLETT2_CONFIG_SET_COUNT]
- [SCARLETT2_CONFIG_COUNT] =
-
-/* Gen 2 devices: 6i6, 18i8, 18i20 */
-{ {
- [SCARLETT2_CONFIG_DIM_MUTE] = {
- .offset = 0x31, .size = 8, .activate = 2 },
-
- [SCARLETT2_CONFIG_LINE_OUT_VOLUME] = {
- .offset = 0x34, .size = 16, .activate = 1 },
-
- [SCARLETT2_CONFIG_MUTE_SWITCH] = {
- .offset = 0x5c, .size = 8, .activate = 1 },
-
- [SCARLETT2_CONFIG_SW_HW_SWITCH] = {
- .offset = 0x66, .size = 8, .activate = 3 },
-
- [SCARLETT2_CONFIG_LEVEL_SWITCH] = {
- .offset = 0x7c, .size = 8, .activate = 7 },
-
- [SCARLETT2_CONFIG_PAD_SWITCH] = {
- .offset = 0x84, .size = 8, .activate = 8 },
-
- [SCARLETT2_CONFIG_STANDALONE_SWITCH] = {
- .offset = 0x8d, .size = 8, .activate = 6 },
-
-/* Gen 3 devices without a mixer (Solo and 2i2) */
-}, {
- [SCARLETT2_CONFIG_MSD_SWITCH] = {
- .offset = 0x04, .size = 8, .activate = 6 },
-
- [SCARLETT2_CONFIG_PHANTOM_PERSISTENCE] = {
- .offset = 0x05, .size = 8, .activate = 6 },
-
- [SCARLETT2_CONFIG_PHANTOM_SWITCH] = {
- .offset = 0x06, .size = 8, .activate = 3 },
-
- [SCARLETT2_CONFIG_DIRECT_MONITOR] = {
- .offset = 0x07, .size = 8, .activate = 4 },
-
- [SCARLETT2_CONFIG_LEVEL_SWITCH] = {
- .offset = 0x08, .size = 1, .activate = 7 },
-
- [SCARLETT2_CONFIG_AIR_SWITCH] = {
- .offset = 0x09, .size = 1, .activate = 8 },
-
-/* Gen 3 devices: 4i4, 8i6, 18i8, 18i20 */
-}, {
- [SCARLETT2_CONFIG_DIM_MUTE] = {
- .offset = 0x31, .size = 8, .activate = 2 },
-
- [SCARLETT2_CONFIG_LINE_OUT_VOLUME] = {
- .offset = 0x34, .size = 16, .activate = 1 },
-
- [SCARLETT2_CONFIG_MUTE_SWITCH] = {
- .offset = 0x5c, .size = 8, .activate = 1 },
-
- [SCARLETT2_CONFIG_SW_HW_SWITCH] = {
- .offset = 0x66, .size = 8, .activate = 3 },
-
- [SCARLETT2_CONFIG_LEVEL_SWITCH] = {
- .offset = 0x7c, .size = 8, .activate = 7 },
-
- [SCARLETT2_CONFIG_PAD_SWITCH] = {
- .offset = 0x84, .size = 8, .activate = 8 },
-
- [SCARLETT2_CONFIG_AIR_SWITCH] = {
- .offset = 0x8c, .size = 8, .activate = 8 },
-
- [SCARLETT2_CONFIG_STANDALONE_SWITCH] = {
- .offset = 0x95, .size = 8, .activate = 6 },
-
- [SCARLETT2_CONFIG_PHANTOM_SWITCH] = {
- .offset = 0x9c, .size = 1, .activate = 8 },
-
- [SCARLETT2_CONFIG_MSD_SWITCH] = {
- .offset = 0x9d, .size = 8, .activate = 6 },
-
- [SCARLETT2_CONFIG_PHANTOM_PERSISTENCE] = {
- .offset = 0x9e, .size = 8, .activate = 6 },
-
- [SCARLETT2_CONFIG_MONITOR_OTHER_SWITCH] = {
- .offset = 0x9f, .size = 1, .activate = 10 },
-
- [SCARLETT2_CONFIG_MONITOR_OTHER_ENABLE] = {
- .offset = 0xa0, .size = 1, .activate = 10 },
-
- [SCARLETT2_CONFIG_TALKBACK_MAP] = {
- .offset = 0xb0, .size = 16, .activate = 10 },
-
-/* Clarett USB and Clarett+ devices: 2Pre, 4Pre, 8Pre */
-}, {
- [SCARLETT2_CONFIG_DIM_MUTE] = {
- .offset = 0x31, .size = 8, .activate = 2 },
-
- [SCARLETT2_CONFIG_LINE_OUT_VOLUME] = {
- .offset = 0x34, .size = 16, .activate = 1 },
-
- [SCARLETT2_CONFIG_MUTE_SWITCH] = {
- .offset = 0x5c, .size = 8, .activate = 1 },
-
- [SCARLETT2_CONFIG_SW_HW_SWITCH] = {
- .offset = 0x66, .size = 8, .activate = 3 },
-
- [SCARLETT2_CONFIG_LEVEL_SWITCH] = {
- .offset = 0x7c, .size = 8, .activate = 7 },
+#define SCARLETT2_FLASH_BLOCK_SIZE 4096
+#define SCARLETT2_FLASH_WRITE_MAX 1024
+#define SCARLETT2_SEGMENT_NUM_MIN 1
+#define SCARLETT2_SEGMENT_NUM_MAX 4
- [SCARLETT2_CONFIG_AIR_SWITCH] = {
- .offset = 0x95, .size = 8, .activate = 8 },
-
- [SCARLETT2_CONFIG_STANDALONE_SWITCH] = {
- .offset = 0x8d, .size = 8, .activate = 6 },
-} };
+#define SCARLETT2_SEGMENT_SETTINGS_NAME "App_Settings"
+#define SCARLETT2_SEGMENT_FIRMWARE_NAME "App_Upgrade"
/* proprietary request/response format */
struct scarlett2_usb_packet {
@@ -1419,6 +1956,16 @@ static int scarlett2_usb(
/* validate the response */
if (err != resp_buf_size) {
+
+ /* ESHUTDOWN and EPROTO are valid responses to a
+ * reboot request
+ */
+ if (cmd == SCARLETT2_USB_REBOOT &&
+ (err == -ESHUTDOWN || err == -EPROTO)) {
+ err = 0;
+ goto unlock;
+ }
+
usb_audio_err(
mixer->chip,
"%s USB response result cmd %x was %d expected %zu\n",
@@ -1478,22 +2025,39 @@ static int scarlett2_usb_get(
&req, sizeof(req), buf, size);
}
+/* Return true if the given configuration item is present in the
+ * configuration set used by this device.
+ */
+static int scarlett2_has_config_item(
+ struct scarlett2_data *private, int config_item_num)
+{
+ return !!private->config_set->items[config_item_num].offset;
+}
+
/* Send a USB message to get configuration parameters; result placed in *buf */
static int scarlett2_usb_get_config(
struct usb_mixer_interface *mixer,
int config_item_num, int count, void *buf)
{
struct scarlett2_data *private = mixer->private_data;
- const struct scarlett2_device_info *info = private->info;
const struct scarlett2_config *config_item =
- &scarlett2_config_items[info->config_set][config_item_num];
+ &private->config_set->items[config_item_num];
int size, err, i;
u8 *buf_8;
u8 value;
+ /* Check that the configuration item is present in the
+ * configuration set used by this device
+ */
+ if (!config_item->offset)
+ return -EFAULT;
+
+ /* Gen 4 style parameters are always 1 byte */
+ size = config_item->size ? config_item->size : 8;
+
/* For byte-sized parameters, retrieve directly into buf */
- if (config_item->size >= 8) {
- size = config_item->size / 8 * count;
+ if (size >= 8) {
+ size = size / 8 * count;
err = scarlett2_usb_get(mixer, config_item->offset, buf, size);
if (err < 0)
return err;
@@ -1519,43 +2083,92 @@ static int scarlett2_usb_get_config(
return 0;
}
-/* Send SCARLETT2_USB_DATA_CMD SCARLETT2_USB_CONFIG_SAVE */
-static void scarlett2_config_save(struct usb_mixer_interface *mixer)
+/* Send a SCARLETT2_USB_SET_DATA command.
+ * offset: location in the device's data space
+ * size: size in bytes of the value (1, 2, 4)
+ */
+static int scarlett2_usb_set_data(
+ struct usb_mixer_interface *mixer,
+ int offset, int size, int value)
{
- __le32 req = cpu_to_le32(SCARLETT2_USB_CONFIG_SAVE);
+ struct scarlett2_data *private = mixer->private_data;
+ struct {
+ __le32 offset;
+ __le32 size;
+ __le32 value;
+ } __packed req;
- scarlett2_usb(mixer, SCARLETT2_USB_DATA_CMD,
- &req, sizeof(u32),
- NULL, 0);
+ req.offset = cpu_to_le32(offset);
+ req.size = cpu_to_le32(size);
+ req.value = cpu_to_le32(value);
+ return scarlett2_usb(private->mixer, SCARLETT2_USB_SET_DATA,
+ &req, sizeof(u32) * 2 + size, NULL, 0);
}
-/* Delayed work to save config */
-static void scarlett2_config_save_work(struct work_struct *work)
+/* Send a SCARLETT2_USB_DATA_CMD command.
+ * Configuration changes require activation with this after they have
+ * been uploaded by a previous SCARLETT2_USB_SET_DATA.
+ * The value for activate needed is determined by the configuration
+ * item.
+ */
+static int scarlett2_usb_activate_config(
+ struct usb_mixer_interface *mixer, int activate)
{
- struct scarlett2_data *private =
- container_of(work, struct scarlett2_data, work.work);
+ __le32 req;
- scarlett2_config_save(private->mixer);
+ req = cpu_to_le32(activate);
+ return scarlett2_usb(mixer, SCARLETT2_USB_DATA_CMD,
+ &req, sizeof(req), NULL, 0);
}
-/* Send a USB message to set a SCARLETT2_CONFIG_* parameter */
+/* Send USB messages to set a SCARLETT2_CONFIG_* parameter */
static int scarlett2_usb_set_config(
struct usb_mixer_interface *mixer,
int config_item_num, int index, int value)
{
struct scarlett2_data *private = mixer->private_data;
- const struct scarlett2_device_info *info = private->info;
+ const struct scarlett2_config_set *config_set = private->config_set;
const struct scarlett2_config *config_item =
- &scarlett2_config_items[info->config_set][config_item_num];
- struct {
- __le32 offset;
- __le32 bytes;
- __le32 value;
- } __packed req;
- __le32 req2;
+ &config_set->items[config_item_num];
int offset, size;
int err;
+ /* Check that the configuration item is present in the
+ * configuration set used by this device
+ */
+ if (!config_item->offset)
+ return -EFAULT;
+
+ /* Gen 4 style writes are selected with size = 0;
+ * these are only byte-sized values written through a shared
+ * location, different to the read address
+ */
+ if (!config_item->size) {
+ if (!config_set->gen4_write_addr)
+ return -EFAULT;
+
+ /* Place index in gen4_write_addr + 1 */
+ err = scarlett2_usb_set_data(
+ mixer, config_set->gen4_write_addr + 1, 1, index);
+ if (err < 0)
+ return err;
+
+ /* Place value in gen4_write_addr */
+ err = scarlett2_usb_set_data(
+ mixer, config_set->gen4_write_addr, 1, value);
+ if (err < 0)
+ return err;
+
+ /* Request the interface do the write */
+ return scarlett2_usb_activate_config(
+ mixer, config_item->activate);
+ }
+
+ /* Not-Gen 4 style needs NVRAM save, supports
+ * bit-modification, and writing is done to the same place
+ * that the value can be read from
+ */
+
/* Cancel any pending NVRAM save */
cancel_delayed_work_sync(&private->work);
@@ -1575,7 +2188,10 @@ static int scarlett2_usb_set_config(
size = 1;
offset = config_item->offset;
- scarlett2_usb_get(mixer, offset, &tmp, 1);
+ err = scarlett2_usb_get(mixer, offset, &tmp, 1);
+ if (err < 0)
+ return err;
+
if (value)
tmp |= (1 << index);
else
@@ -1585,22 +2201,19 @@ static int scarlett2_usb_set_config(
}
/* Send the configuration parameter data */
- req.offset = cpu_to_le32(offset);
- req.bytes = cpu_to_le32(size);
- req.value = cpu_to_le32(value);
- err = scarlett2_usb(mixer, SCARLETT2_USB_SET_DATA,
- &req, sizeof(u32) * 2 + size,
- NULL, 0);
+ err = scarlett2_usb_set_data(mixer, offset, size, value);
if (err < 0)
return err;
/* Activate the change */
- req2 = cpu_to_le32(config_item->activate);
- err = scarlett2_usb(mixer, SCARLETT2_USB_DATA_CMD,
- &req2, sizeof(req2), NULL, 0);
+ err = scarlett2_usb_activate_config(mixer, config_item->activate);
if (err < 0)
return err;
+ /* Gen 2 style writes to Gen 4 devices don't need saving */
+ if (config_set->gen4_write_addr)
+ return 0;
+
/* Schedule the change to be written to NVRAM */
if (config_item->activate != SCARLETT2_USB_CONFIG_SAVE)
schedule_delayed_work(&private->work, msecs_to_jiffies(2000));
@@ -1608,6 +2221,25 @@ static int scarlett2_usb_set_config(
return 0;
}
+/* Send SCARLETT2_USB_DATA_CMD SCARLETT2_USB_CONFIG_SAVE */
+static void scarlett2_config_save(struct usb_mixer_interface *mixer)
+{
+ int err;
+
+ err = scarlett2_usb_activate_config(mixer, SCARLETT2_USB_CONFIG_SAVE);
+ if (err < 0)
+ usb_audio_err(mixer->chip, "config save failed: %d\n", err);
+}
+
+/* Delayed work to save config */
+static void scarlett2_config_save_work(struct work_struct *work)
+{
+ struct scarlett2_data *private =
+ container_of(work, struct scarlett2_data, work.work);
+
+ scarlett2_config_save(private->mixer);
+}
+
/* Send a USB message to get sync status; result placed in *sync */
static int scarlett2_usb_get_sync_status(
struct usb_mixer_interface *mixer,
@@ -1625,13 +2257,23 @@ static int scarlett2_usb_get_sync_status(
return 0;
}
-/* Send a USB message to get volume status; result placed in *buf */
-static int scarlett2_usb_get_volume_status(
- struct usb_mixer_interface *mixer,
- struct scarlett2_usb_volume_status *buf)
+/* Return true if the device has a mixer that we can control */
+static int scarlett2_has_mixer(struct scarlett2_data *private)
+{
+ return !!private->info->mux_assignment[0][0].count;
+}
+
+/* Map from mixer value to (db + 80) * 2
+ * (reverse of scarlett2_mixer_values[])
+ */
+static int scarlett2_mixer_value_to_db(int value)
{
- return scarlett2_usb_get(mixer, SCARLETT2_USB_VOLUME_STATUS_OFFSET,
- buf, sizeof(*buf));
+ int i;
+
+ for (i = 0; i < SCARLETT2_MIXER_VALUE_COUNT; i++)
+ if (scarlett2_mixer_values[i] >= value)
+ return i;
+ return SCARLETT2_MIXER_MAX_VALUE;
}
/* Send a USB message to get the volumes for all inputs of one mix
@@ -1641,11 +2283,9 @@ static int scarlett2_usb_get_mix(struct usb_mixer_interface *mixer,
int mix_num)
{
struct scarlett2_data *private = mixer->private_data;
- const struct scarlett2_device_info *info = private->info;
- int num_mixer_in =
- info->port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_OUT];
- int err, i, j, k;
+ int num_mixer_in = private->num_mix_in;
+ int err, i, j;
struct {
__le16 mix_num;
@@ -1663,16 +2303,9 @@ static int scarlett2_usb_get_mix(struct usb_mixer_interface *mixer,
if (err < 0)
return err;
- for (i = 0, j = mix_num * num_mixer_in; i < num_mixer_in; i++, j++) {
- u16 mixer_value = le16_to_cpu(data[i]);
-
- for (k = 0; k < SCARLETT2_MIXER_VALUE_COUNT; k++)
- if (scarlett2_mixer_values[k] >= mixer_value)
- break;
- if (k == SCARLETT2_MIXER_VALUE_COUNT)
- k = SCARLETT2_MIXER_MAX_VALUE;
- private->mix[j] = k;
- }
+ for (i = 0, j = mix_num * num_mixer_in; i < num_mixer_in; i++, j++)
+ private->mix[j] = scarlett2_mixer_value_to_db(
+ le16_to_cpu(data[i]));
return 0;
}
@@ -1684,7 +2317,6 @@ static int scarlett2_usb_set_mix(struct usb_mixer_interface *mixer,
int mix_num)
{
struct scarlett2_data *private = mixer->private_data;
- const struct scarlett2_device_info *info = private->info;
struct {
__le16 mix_num;
@@ -1692,8 +2324,7 @@ static int scarlett2_usb_set_mix(struct usb_mixer_interface *mixer,
} __packed req;
int i, j;
- int num_mixer_in =
- info->port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_OUT];
+ int num_mixer_in = private->num_mix_in;
req.mix_num = cpu_to_le16(mix_num);
@@ -1804,9 +2435,6 @@ static void scarlett2_usb_populate_mux(struct scarlett2_data *private,
static void scarlett2_update_meter_level_map(struct scarlett2_data *private)
{
const struct scarlett2_device_info *info = private->info;
- const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count;
- int line_out_count =
- port_count[SCARLETT2_PORT_TYPE_ANALOGUE][SCARLETT2_PORT_OUT];
const struct scarlett2_meter_entry *entry;
/* sources already assigned to a destination
@@ -1835,7 +2463,7 @@ static void scarlett2_update_meter_level_map(struct scarlett2_data *private)
/* convert mux_idx using line_out_unmap[] */
int map_mux_idx = (
info->line_out_remap_enable &&
- mux_idx < line_out_count
+ mux_idx < private->num_line_out
) ? info->line_out_unmap[mux_idx]
: mux_idx;
@@ -1984,6 +2612,15 @@ static int scarlett2_usb_get_meter_levels(struct usb_mixer_interface *mixer,
return 0;
}
+/* For config items with mute=1, xor bits 0 & 1 together to get the
+ * current/next state. This won't have any effect on values which are
+ * only ever 0/1.
+ */
+static uint8_t scarlett2_decode_muteable(uint8_t v)
+{
+ return (v ^ (v >> 1)) & 1;
+}
+
/*** Control Functions ***/
/* helper function to create a new control */
@@ -2067,6 +2704,46 @@ static int scarlett2_add_firmware_version_ctl(
return scarlett2_add_new_ctl(mixer, &scarlett2_firmware_version_ctl,
0, 0, "Firmware Version", NULL);
}
+
+/*** Minimum Firmware Version Control ***/
+
+static int scarlett2_min_firmware_version_ctl_get(
+ struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct scarlett2_data *private = elem->head.mixer->private_data;
+
+ ucontrol->value.integer.value[0] = private->info->min_firmware_version;
+
+ return 0;
+}
+
+static int scarlett2_min_firmware_version_ctl_info(
+ struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new scarlett2_min_firmware_version_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .name = "",
+ .info = scarlett2_min_firmware_version_ctl_info,
+ .get = scarlett2_min_firmware_version_ctl_get
+};
+
+static int scarlett2_add_min_firmware_version_ctl(
+ struct usb_mixer_interface *mixer)
+{
+ return scarlett2_add_new_ctl(mixer, &scarlett2_min_firmware_version_ctl,
+ 0, 0, "Minimum Firmware Version", NULL);
+}
+
/*** Sync Control ***/
/* Update sync control after receiving notification that the status
@@ -2095,14 +2772,25 @@ static int scarlett2_sync_ctl_get(struct snd_kcontrol *kctl,
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
+ int err = 0;
mutex_lock(&private->data_mutex);
- if (private->sync_updated)
- scarlett2_update_sync(mixer);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ if (private->sync_updated) {
+ err = scarlett2_update_sync(mixer);
+ if (err < 0)
+ goto unlock;
+ }
ucontrol->value.enumerated.item[0] = private->sync;
- mutex_unlock(&private->data_mutex);
- return 0;
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
}
static const struct snd_kcontrol_new scarlett2_sync_ctl = {
@@ -2118,13 +2806,938 @@ static int scarlett2_add_sync_ctl(struct usb_mixer_interface *mixer)
struct scarlett2_data *private = mixer->private_data;
/* devices without a mixer also don't support reporting sync status */
- if (private->info->config_set == SCARLETT2_CONFIG_SET_GEN_3A)
+ if (!scarlett2_has_mixer(private))
return 0;
return scarlett2_add_new_ctl(mixer, &scarlett2_sync_ctl,
0, 1, "Sync Status", &private->sync_ctl);
}
+/*** Autogain Switch and Status Controls ***/
+
+/* Forward declarations as phantom power and autogain can disable each other */
+static int scarlett2_check_input_phantom_updated(struct usb_mixer_interface *);
+static int scarlett2_phantom_is_switching(struct scarlett2_data *, int);
+
+/* Set the access mode of a control to read-only (val = 0) or
+ * read-write (val = 1).
+ */
+static void scarlett2_set_ctl_access(struct snd_kcontrol *kctl, int val)
+{
+ if (val)
+ kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_WRITE;
+ else
+ kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_WRITE;
+}
+
+/* Check if autogain is running on any input */
+static int scarlett2_autogain_is_running(struct scarlett2_data *private)
+{
+ int i;
+
+ for (i = 0; i < private->info->gain_input_count; i++)
+ if (private->autogain_status[i] ==
+ SCARLETT2_AUTOGAIN_STATUS_RUNNING)
+ return 1;
+
+ return 0;
+}
+
+static int scarlett2_update_autogain(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+ int err, i;
+ u8 raw_autogain_status[SCARLETT2_INPUT_GAIN_MAX];
+
+ private->autogain_updated = 0;
+
+ if (!info->gain_input_count)
+ return 0;
+
+ err = scarlett2_usb_get_config(
+ mixer, SCARLETT2_CONFIG_AUTOGAIN_SWITCH,
+ info->gain_input_count, private->autogain_switch);
+ if (err < 0)
+ return err;
+ err = scarlett2_usb_get_config(
+ mixer, SCARLETT2_CONFIG_AUTOGAIN_STATUS,
+ info->gain_input_count, raw_autogain_status);
+ if (err < 0)
+ return err;
+
+ /* Translate autogain_switch and raw_autogain_status into
+ * autogain_status
+ */
+ for (i = 0; i < info->gain_input_count; i++)
+ if (private->autogain_switch[i])
+ private->autogain_status[i] =
+ SCARLETT2_AUTOGAIN_STATUS_RUNNING;
+ else if (raw_autogain_status[i] == 0)
+ private->autogain_status[i] =
+ SCARLETT2_AUTOGAIN_STATUS_STOPPED;
+ else if (raw_autogain_status[i] >= 2 &&
+ raw_autogain_status[i] <= 5)
+ private->autogain_status[i] =
+ SCARLETT2_AUTOGAIN_STATUS_FAILED;
+ else if (raw_autogain_status[i] == 6)
+ private->autogain_status[i] =
+ SCARLETT2_AUTOGAIN_STATUS_CANCELLED;
+ else
+ private->autogain_status[i] =
+ SCARLETT2_AUTOGAIN_STATUS_UNKNOWN;
+
+ return 0;
+}
+
+/* Update access mode for controls affected by autogain */
+static void scarlett2_autogain_update_access(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+ int val = !scarlett2_autogain_is_running(private);
+ int i;
+
+ scarlett2_set_ctl_access(private->input_select_ctl, val);
+ for (i = 0; i < info->gain_input_count / 2; i++)
+ scarlett2_set_ctl_access(private->input_link_ctls[i], val);
+ for (i = 0; i < info->gain_input_count; i++) {
+ scarlett2_set_ctl_access(private->input_gain_ctls[i], val);
+ scarlett2_set_ctl_access(private->safe_ctls[i], val);
+ }
+ for (i = 0; i < info->level_input_count; i++)
+ scarlett2_set_ctl_access(private->level_ctls[i], val);
+ for (i = 0; i < info->air_input_count; i++)
+ scarlett2_set_ctl_access(private->air_ctls[i], val);
+ for (i = 0; i < info->phantom_count; i++)
+ scarlett2_set_ctl_access(private->phantom_ctls[i], val);
+}
+
+/* Notify of access mode change for all controls read-only while
+ * autogain runs.
+ */
+static void scarlett2_autogain_notify_access(struct usb_mixer_interface *mixer)
+{
+ struct snd_card *card = mixer->chip->card;
+ struct scarlett2_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+ int i;
+
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
+ &private->input_select_ctl->id);
+ for (i = 0; i < info->gain_input_count / 2; i++)
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
+ &private->input_link_ctls[i]->id);
+ for (i = 0; i < info->gain_input_count; i++) {
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
+ &private->input_gain_ctls[i]->id);
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
+ &private->safe_ctls[i]->id);
+ }
+ for (i = 0; i < info->level_input_count; i++)
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
+ &private->level_ctls[i]->id);
+ for (i = 0; i < info->air_input_count; i++)
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
+ &private->air_ctls[i]->id);
+ for (i = 0; i < info->phantom_count; i++)
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
+ &private->phantom_ctls[i]->id);
+}
+
+/* Call scarlett2_update_autogain() and
+ * scarlett2_autogain_update_access() if autogain_updated is set.
+ */
+static int scarlett2_check_autogain_updated(
+ struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+ int err;
+
+ if (!private->autogain_updated)
+ return 0;
+
+ err = scarlett2_update_autogain(mixer);
+ if (err < 0)
+ return err;
+
+ scarlett2_autogain_update_access(mixer);
+
+ return 0;
+}
+
+/* If autogain_updated is set when a *_ctl_put() function for a
+ * control that is meant to be read-only while autogain is running,
+ * update the autogain status and access mode of affected controls.
+ * Return -EPERM if autogain is running.
+ */
+static int scarlett2_check_put_during_autogain(
+ struct usb_mixer_interface *mixer)
+{
+ int err = scarlett2_check_autogain_updated(mixer);
+
+ if (err < 0)
+ return err;
+
+ if (scarlett2_autogain_is_running(mixer->private_data))
+ return -EPERM;
+
+ return 0;
+}
+
+static int scarlett2_autogain_switch_ctl_info(
+ struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = mixer->private_data;
+ int err;
+
+ mutex_lock(&private->data_mutex);
+
+ err = scarlett2_check_input_phantom_updated(mixer);
+ if (err < 0)
+ goto unlock;
+
+ err = snd_ctl_boolean_mono_info(kctl, uinfo);
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static int scarlett2_autogain_switch_ctl_get(
+ struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = mixer->private_data;
+ int err;
+
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ err = scarlett2_check_autogain_updated(mixer);
+ if (err < 0)
+ goto unlock;
+
+ ucontrol->value.enumerated.item[0] =
+ private->autogain_switch[elem->control];
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static int scarlett2_autogain_status_ctl_get(
+ struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = mixer->private_data;
+ int err;
+
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ err = scarlett2_check_autogain_updated(mixer);
+ if (err < 0)
+ goto unlock;
+
+ ucontrol->value.enumerated.item[0] =
+ private->autogain_status[elem->control];
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static int scarlett2_autogain_switch_ctl_put(
+ struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = mixer->private_data;
+
+ int index = elem->control;
+ int oval, val, err;
+
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ err = scarlett2_check_input_phantom_updated(mixer);
+ if (err < 0)
+ goto unlock;
+
+ if (scarlett2_phantom_is_switching(private, index)) {
+ err = -EPERM;
+ goto unlock;
+ }
+
+ oval = private->autogain_switch[index];
+ val = !!ucontrol->value.integer.value[0];
+
+ if (oval == val)
+ goto unlock;
+
+ private->autogain_switch[index] = val;
+
+ /* Send switch change to the device */
+ err = scarlett2_usb_set_config(
+ mixer, SCARLETT2_CONFIG_AUTOGAIN_SWITCH, index, val);
+ if (err == 0)
+ err = 1;
+
+ scarlett2_autogain_update_access(mixer);
+ scarlett2_autogain_notify_access(mixer);
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static int scarlett2_autogain_status_ctl_info(
+ struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo)
+{
+ static const char *const values[SCARLETT2_AUTOGAIN_STATUS_COUNT] = {
+ "Stopped", "Running", "Failed", "Cancelled", "Unknown"
+ };
+
+ return snd_ctl_enum_info(
+ uinfo, 1, SCARLETT2_AUTOGAIN_STATUS_COUNT, values);
+}
+
+static const struct snd_kcontrol_new scarlett2_autogain_switch_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "",
+ .info = scarlett2_autogain_switch_ctl_info,
+ .get = scarlett2_autogain_switch_ctl_get,
+ .put = scarlett2_autogain_switch_ctl_put
+};
+
+static const struct snd_kcontrol_new scarlett2_autogain_status_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .name = "",
+ .info = scarlett2_autogain_status_ctl_info,
+ .get = scarlett2_autogain_status_ctl_get,
+};
+
+/*** Input Select Control ***/
+
+static int scarlett2_update_input_select(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+ int link_count = info->gain_input_count / 2;
+ int err;
+
+ private->input_select_updated = 0;
+
+ if (!link_count)
+ return 0;
+
+ err = scarlett2_usb_get_config(
+ mixer, SCARLETT2_CONFIG_INPUT_SELECT_SWITCH,
+ 1, &private->input_select_switch);
+ if (err < 0)
+ return err;
+
+ err = scarlett2_usb_get_config(
+ mixer, SCARLETT2_CONFIG_INPUT_LINK_SWITCH,
+ link_count, private->input_link_switch);
+ if (err < 0)
+ return err;
+
+ /* simplified because no model yet has link_count > 1 */
+ if (private->input_link_switch[0])
+ private->input_select_switch = 0;
+
+ return 0;
+}
+
+static int scarlett2_input_select_ctl_get(
+ struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = mixer->private_data;
+ int err = 0;
+
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ if (private->input_select_updated) {
+ err = scarlett2_update_input_select(mixer);
+ if (err < 0)
+ goto unlock;
+ }
+ ucontrol->value.enumerated.item[0] = private->input_select_switch;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static int scarlett2_input_select_ctl_put(
+ struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = mixer->private_data;
+
+ int oval, val, err;
+ int max_val = private->input_link_switch[0] ? 0 : 1;
+
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ err = scarlett2_check_put_during_autogain(mixer);
+ if (err < 0)
+ goto unlock;
+
+ oval = private->input_select_switch;
+ val = ucontrol->value.integer.value[0];
+
+ if (val < 0)
+ val = 0;
+ else if (val > max_val)
+ val = max_val;
+
+ if (oval == val)
+ goto unlock;
+
+ private->input_select_switch = val;
+
+ /* Send switch change to the device if inputs not linked */
+ if (!private->input_link_switch[0])
+ err = scarlett2_usb_set_config(
+ mixer, SCARLETT2_CONFIG_INPUT_SELECT_SWITCH,
+ 1, val);
+ if (err == 0)
+ err = 1;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static int scarlett2_input_select_ctl_info(
+ struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = mixer->private_data;
+
+ int inputs = private->info->gain_input_count;
+ int i, j;
+ int err;
+ char **values = kcalloc(inputs, sizeof(char *), GFP_KERNEL);
+
+ if (!values)
+ return -ENOMEM;
+
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ err = scarlett2_check_autogain_updated(mixer);
+ if (err < 0)
+ goto unlock;
+
+ /* Loop through each input
+ * Linked inputs have one value for the pair
+ */
+ for (i = 0, j = 0; i < inputs; i++) {
+ if (private->input_link_switch[i / 2]) {
+ values[j++] = kasprintf(
+ GFP_KERNEL, "Input %d-%d", i + 1, i + 2);
+ i++;
+ } else {
+ values[j++] = kasprintf(
+ GFP_KERNEL, "Input %d", i + 1);
+ }
+ }
+
+ err = snd_ctl_enum_info(uinfo, 1, j,
+ (const char * const *)values);
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+
+ for (i = 0; i < inputs; i++)
+ kfree(values[i]);
+ kfree(values);
+
+ return err;
+}
+
+static const struct snd_kcontrol_new scarlett2_input_select_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "",
+ .info = scarlett2_input_select_ctl_info,
+ .get = scarlett2_input_select_ctl_get,
+ .put = scarlett2_input_select_ctl_put,
+};
+
+/*** Input Link Switch Controls ***/
+
+/* snd_ctl_boolean_mono_info() with autogain-updated check
+ * (for controls that are read-only while autogain is running)
+ */
+static int scarlett2_autogain_disables_ctl_info(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = mixer->private_data;
+ int err;
+
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ err = scarlett2_check_autogain_updated(mixer);
+ if (err < 0)
+ goto unlock;
+
+ err = snd_ctl_boolean_mono_info(kctl, uinfo);
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static int scarlett2_input_link_ctl_get(
+ struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = mixer->private_data;
+ int err = 0;
+
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ if (private->input_select_updated) {
+ err = scarlett2_update_input_select(mixer);
+ if (err < 0)
+ goto unlock;
+ }
+ ucontrol->value.enumerated.item[0] =
+ private->input_link_switch[elem->control];
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static int scarlett2_input_link_ctl_put(
+ struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = mixer->private_data;
+
+ int index = elem->control;
+ int oval, val, err;
+
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ err = scarlett2_check_put_during_autogain(mixer);
+ if (err < 0)
+ goto unlock;
+
+ oval = private->input_link_switch[index];
+ val = !!ucontrol->value.integer.value[0];
+
+ if (oval == val)
+ goto unlock;
+
+ private->input_link_switch[index] = val;
+
+ /* Notify of change in input select options available */
+ snd_ctl_notify(mixer->chip->card,
+ SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO,
+ &private->input_select_ctl->id);
+ private->input_select_updated = 1;
+
+ /* Send switch change to the device
+ * Link for channels 1-2 is at index 1
+ * No device yet has more than 2 channels linked
+ */
+ err = scarlett2_usb_set_config(
+ mixer, SCARLETT2_CONFIG_INPUT_LINK_SWITCH, index + 1, val);
+ if (err == 0)
+ err = 1;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static const struct snd_kcontrol_new scarlett2_input_link_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "",
+ .info = scarlett2_autogain_disables_ctl_info,
+ .get = scarlett2_input_link_ctl_get,
+ .put = scarlett2_input_link_ctl_put
+};
+
+/*** Input Gain Controls ***/
+
+static int scarlett2_update_input_gain(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+
+ private->input_gain_updated = 0;
+
+ if (!info->gain_input_count)
+ return 0;
+
+ return scarlett2_usb_get_config(
+ mixer, SCARLETT2_CONFIG_INPUT_GAIN,
+ info->gain_input_count, private->gain);
+}
+
+static int scarlett2_input_gain_ctl_info(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = mixer->private_data;
+ int err;
+
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ err = scarlett2_check_autogain_updated(mixer);
+ if (err < 0)
+ goto unlock;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = elem->channels;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = SCARLETT2_GAIN_BIAS;
+ uinfo->value.integer.step = 1;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static int scarlett2_input_gain_ctl_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = mixer->private_data;
+ int err = 0;
+
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ if (private->input_gain_updated) {
+ err = scarlett2_update_input_gain(mixer);
+ if (err < 0)
+ goto unlock;
+ }
+ ucontrol->value.integer.value[0] =
+ private->gain[elem->control];
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static int scarlett2_input_gain_ctl_put(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = mixer->private_data;
+
+ int index = elem->control;
+ int oval, val, err;
+
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ err = scarlett2_check_put_during_autogain(mixer);
+ if (err < 0)
+ goto unlock;
+
+ oval = private->gain[index];
+ val = ucontrol->value.integer.value[0];
+
+ if (oval == val)
+ goto unlock;
+
+ private->gain[index] = val;
+
+ /* Send gain change to the device */
+ err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_INPUT_GAIN,
+ index, val);
+ if (err == 0)
+ err = 1;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static const DECLARE_TLV_DB_MINMAX(
+ db_scale_scarlett2_gain, -SCARLETT2_GAIN_BIAS * 100, 0
+);
+
+static const struct snd_kcontrol_new scarlett2_input_gain_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+ .name = "",
+ .info = scarlett2_input_gain_ctl_info,
+ .get = scarlett2_input_gain_ctl_get,
+ .put = scarlett2_input_gain_ctl_put,
+ .private_value = 0, /* max value */
+ .tlv = { .p = db_scale_scarlett2_gain }
+};
+
+/*** Safe Controls ***/
+
+static int scarlett2_update_input_safe(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+
+ private->input_safe_updated = 0;
+
+ if (!info->gain_input_count)
+ return 0;
+
+ return scarlett2_usb_get_config(
+ mixer, SCARLETT2_CONFIG_SAFE_SWITCH,
+ info->gain_input_count, private->safe_switch);
+}
+
+static int scarlett2_safe_ctl_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = mixer->private_data;
+ int err = 0;
+
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ if (private->input_safe_updated) {
+ err = scarlett2_update_input_safe(mixer);
+ if (err < 0)
+ goto unlock;
+ }
+ ucontrol->value.integer.value[0] =
+ private->safe_switch[elem->control];
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static int scarlett2_safe_ctl_put(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = mixer->private_data;
+
+ int index = elem->control;
+ int oval, val, err;
+
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ err = scarlett2_check_put_during_autogain(mixer);
+ if (err < 0)
+ goto unlock;
+
+ oval = private->safe_switch[index];
+ val = !!ucontrol->value.integer.value[0];
+
+ if (oval == val)
+ goto unlock;
+
+ private->safe_switch[index] = val;
+
+ /* Send switch change to the device */
+ err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_SAFE_SWITCH,
+ index, val);
+ if (err == 0)
+ err = 1;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static const struct snd_kcontrol_new scarlett2_safe_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "",
+ .info = scarlett2_autogain_disables_ctl_info,
+ .get = scarlett2_safe_ctl_get,
+ .put = scarlett2_safe_ctl_put,
+};
+
+/*** PCM Input Control ***/
+
+static int scarlett2_update_pcm_input_switch(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+ int err;
+
+ private->pcm_input_switch_updated = 0;
+
+ err = scarlett2_usb_get_config(
+ mixer, SCARLETT2_CONFIG_PCM_INPUT_SWITCH,
+ 1, &private->pcm_input_switch);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int scarlett2_pcm_input_switch_ctl_get(
+ struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = elem->head.mixer->private_data;
+ int err = 0;
+
+ mutex_lock(&private->data_mutex);
+
+ if (private->pcm_input_switch_updated) {
+ err = scarlett2_update_pcm_input_switch(mixer);
+ if (err < 0)
+ goto unlock;
+ }
+ ucontrol->value.enumerated.item[0] = private->pcm_input_switch;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static int scarlett2_pcm_input_switch_ctl_put(
+ struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = mixer->private_data;
+
+ int oval, val, err = 0;
+
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ oval = private->pcm_input_switch;
+ val = !!ucontrol->value.integer.value[0];
+
+ if (oval == val)
+ goto unlock;
+
+ private->pcm_input_switch = val;
+
+ /* Send switch change to the device */
+ err = scarlett2_usb_set_config(
+ mixer, SCARLETT2_CONFIG_PCM_INPUT_SWITCH,
+ 0, val);
+ if (err == 0)
+ err = 1;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static int scarlett2_pcm_input_switch_ctl_info(
+ struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo)
+{
+ static const char *const values[2] = {
+ "Direct", "Mixer"
+ };
+
+ return snd_ctl_enum_info(
+ uinfo, 1, 2, values);
+}
+
+static const struct snd_kcontrol_new scarlett2_pcm_input_switch_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "",
+ .info = scarlett2_pcm_input_switch_ctl_info,
+ .get = scarlett2_pcm_input_switch_ctl_get,
+ .put = scarlett2_pcm_input_switch_ctl_put
+};
+
/*** Analogue Line Out Volume Controls ***/
/* Update hardware volume controls after receiving notification that
@@ -2133,35 +3746,40 @@ static int scarlett2_add_sync_ctl(struct usb_mixer_interface *mixer)
static int scarlett2_update_volumes(struct usb_mixer_interface *mixer)
{
struct scarlett2_data *private = mixer->private_data;
- const struct scarlett2_device_info *info = private->info;
- const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count;
- struct scarlett2_usb_volume_status volume_status;
- int num_line_out =
- port_count[SCARLETT2_PORT_TYPE_ANALOGUE][SCARLETT2_PORT_OUT];
+ s16 vol;
int err, i;
- int mute;
private->vol_updated = 0;
- err = scarlett2_usb_get_volume_status(mixer, &volume_status);
- if (err < 0)
- return err;
+ if (scarlett2_has_config_item(private,
+ SCARLETT2_CONFIG_MASTER_VOLUME)) {
+ err = scarlett2_usb_get_config(
+ mixer, SCARLETT2_CONFIG_MASTER_VOLUME,
+ 1, &vol);
+ if (err < 0)
+ return err;
- private->master_vol = clamp(
- volume_status.master_vol + SCARLETT2_VOLUME_BIAS,
- 0, SCARLETT2_VOLUME_BIAS);
+ private->master_vol = clamp(vol + SCARLETT2_VOLUME_BIAS,
+ 0, SCARLETT2_VOLUME_BIAS);
- if (info->line_out_hw_vol)
- for (i = 0; i < SCARLETT2_DIM_MUTE_COUNT; i++)
- private->dim_mute[i] = !!volume_status.dim_mute[i];
+ if (scarlett2_has_config_item(private,
+ SCARLETT2_CONFIG_SW_HW_SWITCH))
+ for (i = 0; i < private->num_line_out; i++)
+ if (private->vol_sw_hw_switch[i])
+ private->vol[i] = private->master_vol;
+ }
- mute = private->dim_mute[SCARLETT2_BUTTON_MUTE];
+ if (scarlett2_has_config_item(private,
+ SCARLETT2_CONFIG_HEADPHONE_VOLUME)) {
+ err = scarlett2_usb_get_config(
+ mixer, SCARLETT2_CONFIG_HEADPHONE_VOLUME,
+ 1, &vol);
+ if (err < 0)
+ return err;
- for (i = 0; i < num_line_out; i++)
- if (private->vol_sw_hw_switch[i]) {
- private->vol[i] = private->master_vol;
- private->mute_switch[i] = mute;
- }
+ private->headphone_vol = clamp(vol + SCARLETT2_VOLUME_BIAS,
+ 0, SCARLETT2_VOLUME_BIAS);
+ }
return 0;
}
@@ -2185,27 +3803,63 @@ static int scarlett2_master_volume_ctl_get(struct snd_kcontrol *kctl,
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
+ int err = 0;
mutex_lock(&private->data_mutex);
- if (private->vol_updated)
- scarlett2_update_volumes(mixer);
- mutex_unlock(&private->data_mutex);
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ if (private->vol_updated) {
+ err = scarlett2_update_volumes(mixer);
+ if (err < 0)
+ goto unlock;
+ }
ucontrol->value.integer.value[0] = private->master_vol;
- return 0;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static int scarlett2_headphone_volume_ctl_get(
+ struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = mixer->private_data;
+ int err = 0;
+
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ if (private->vol_updated) {
+ err = scarlett2_update_volumes(mixer);
+ if (err < 0)
+ goto unlock;
+ }
+ ucontrol->value.integer.value[0] = private->headphone_vol;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
}
static int line_out_remap(struct scarlett2_data *private, int index)
{
const struct scarlett2_device_info *info = private->info;
- const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count;
- int line_out_count =
- port_count[SCARLETT2_PORT_TYPE_ANALOGUE][SCARLETT2_PORT_OUT];
if (!info->line_out_remap_enable)
return index;
- if (index >= line_out_count)
+ if (index >= private->num_line_out)
return index;
return info->line_out_remap[index];
@@ -2218,14 +3872,25 @@ static int scarlett2_volume_ctl_get(struct snd_kcontrol *kctl,
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
int index = line_out_remap(private, elem->control);
+ int err = 0;
mutex_lock(&private->data_mutex);
- if (private->vol_updated)
- scarlett2_update_volumes(mixer);
- mutex_unlock(&private->data_mutex);
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ if (private->vol_updated) {
+ err = scarlett2_update_volumes(mixer);
+ if (err < 0)
+ goto unlock;
+ }
ucontrol->value.integer.value[0] = private->vol[index];
- return 0;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
}
static int scarlett2_volume_ctl_put(struct snd_kcontrol *kctl,
@@ -2239,6 +3904,11 @@ static int scarlett2_volume_ctl_put(struct snd_kcontrol *kctl,
mutex_lock(&private->data_mutex);
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
oval = private->vol[index];
val = ucontrol->value.integer.value[0];
@@ -2257,7 +3927,7 @@ unlock:
}
static const DECLARE_TLV_DB_MINMAX(
- db_scale_scarlett2_gain, -SCARLETT2_VOLUME_BIAS * 100, 0
+ db_scale_scarlett2_volume, -SCARLETT2_VOLUME_BIAS * 100, 0
);
static const struct snd_kcontrol_new scarlett2_master_volume_ctl = {
@@ -2268,7 +3938,18 @@ static const struct snd_kcontrol_new scarlett2_master_volume_ctl = {
.info = scarlett2_volume_ctl_info,
.get = scarlett2_master_volume_ctl_get,
.private_value = 0, /* max value */
- .tlv = { .p = db_scale_scarlett2_gain }
+ .tlv = { .p = db_scale_scarlett2_volume }
+};
+
+static const struct snd_kcontrol_new scarlett2_headphone_volume_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+ .name = "",
+ .info = scarlett2_volume_ctl_info,
+ .get = scarlett2_headphone_volume_ctl_get,
+ .private_value = 0, /* max value */
+ .tlv = { .p = db_scale_scarlett2_volume }
};
static const struct snd_kcontrol_new scarlett2_line_out_volume_ctl = {
@@ -2280,11 +3961,40 @@ static const struct snd_kcontrol_new scarlett2_line_out_volume_ctl = {
.get = scarlett2_volume_ctl_get,
.put = scarlett2_volume_ctl_put,
.private_value = 0, /* max value */
- .tlv = { .p = db_scale_scarlett2_gain }
+ .tlv = { .p = db_scale_scarlett2_volume }
};
/*** Mute Switch Controls ***/
+static int scarlett2_update_dim_mute(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+ int err, i;
+ u8 mute;
+
+ private->dim_mute_updated = 0;
+
+ if (!scarlett2_has_config_item(private, SCARLETT2_CONFIG_SW_HW_SWITCH))
+ return 0;
+
+ err = scarlett2_usb_get_config(
+ mixer, SCARLETT2_CONFIG_DIM_MUTE,
+ SCARLETT2_DIM_MUTE_COUNT, private->dim_mute);
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < SCARLETT2_DIM_MUTE_COUNT; i++)
+ private->dim_mute[i] = !!private->dim_mute[i];
+
+ mute = private->dim_mute[SCARLETT2_BUTTON_MUTE];
+
+ for (i = 0; i < private->num_line_out; i++)
+ if (private->vol_sw_hw_switch[i])
+ private->mute_switch[i] = mute;
+
+ return 0;
+}
+
static int scarlett2_mute_ctl_get(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *ucontrol)
{
@@ -2292,14 +4002,25 @@ static int scarlett2_mute_ctl_get(struct snd_kcontrol *kctl,
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
int index = line_out_remap(private, elem->control);
+ int err = 0;
mutex_lock(&private->data_mutex);
- if (private->vol_updated)
- scarlett2_update_volumes(mixer);
- mutex_unlock(&private->data_mutex);
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ if (private->dim_mute_updated) {
+ err = scarlett2_update_dim_mute(mixer);
+ if (err < 0)
+ goto unlock;
+ }
ucontrol->value.integer.value[0] = private->mute_switch[index];
- return 0;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
}
static int scarlett2_mute_ctl_put(struct snd_kcontrol *kctl,
@@ -2313,6 +4034,11 @@ static int scarlett2_mute_ctl_put(struct snd_kcontrol *kctl,
mutex_lock(&private->data_mutex);
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
oval = private->mute_switch[index];
val = !!ucontrol->value.integer.value[0];
@@ -2452,6 +4178,11 @@ static int scarlett2_sw_hw_enum_ctl_put(struct snd_kcontrol *kctl,
mutex_lock(&private->data_mutex);
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
oval = private->vol_sw_hw_switch[index];
val = !!ucontrol->value.enumerated.item[0];
@@ -2477,53 +4208,20 @@ static const struct snd_kcontrol_new scarlett2_sw_hw_enum_ctl = {
/*** Line Level/Instrument Level Switch Controls ***/
-static int scarlett2_update_input_other(struct usb_mixer_interface *mixer)
+static int scarlett2_update_input_level(struct usb_mixer_interface *mixer)
{
struct scarlett2_data *private = mixer->private_data;
const struct scarlett2_device_info *info = private->info;
- private->input_other_updated = 0;
+ private->input_level_updated = 0;
- if (info->level_input_count) {
- int err = scarlett2_usb_get_config(
- mixer, SCARLETT2_CONFIG_LEVEL_SWITCH,
- info->level_input_count + info->level_input_first,
- private->level_switch);
- if (err < 0)
- return err;
- }
-
- if (info->pad_input_count) {
- int err = scarlett2_usb_get_config(
- mixer, SCARLETT2_CONFIG_PAD_SWITCH,
- info->pad_input_count, private->pad_switch);
- if (err < 0)
- return err;
- }
-
- if (info->air_input_count) {
- int err = scarlett2_usb_get_config(
- mixer, SCARLETT2_CONFIG_AIR_SWITCH,
- info->air_input_count, private->air_switch);
- if (err < 0)
- return err;
- }
-
- if (info->phantom_count) {
- int err = scarlett2_usb_get_config(
- mixer, SCARLETT2_CONFIG_PHANTOM_SWITCH,
- info->phantom_count, private->phantom_switch);
- if (err < 0)
- return err;
-
- err = scarlett2_usb_get_config(
- mixer, SCARLETT2_CONFIG_PHANTOM_PERSISTENCE,
- 1, &private->phantom_persistence);
- if (err < 0)
- return err;
- }
+ if (!info->level_input_count)
+ return 0;
- return 0;
+ return scarlett2_usb_get_config(
+ mixer, SCARLETT2_CONFIG_LEVEL_SWITCH,
+ info->level_input_count + info->level_input_first,
+ private->level_switch);
}
static int scarlett2_level_enum_ctl_info(struct snd_kcontrol *kctl,
@@ -2532,8 +4230,27 @@ static int scarlett2_level_enum_ctl_info(struct snd_kcontrol *kctl,
static const char *const values[2] = {
"Line", "Inst"
};
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = mixer->private_data;
+ int err;
- return snd_ctl_enum_info(uinfo, 1, 2, values);
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ err = scarlett2_check_autogain_updated(mixer);
+ if (err < 0)
+ goto unlock;
+
+ err = snd_ctl_enum_info(uinfo, 1, 2, values);
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
}
static int scarlett2_level_enum_ctl_get(struct snd_kcontrol *kctl,
@@ -2545,14 +4262,26 @@ static int scarlett2_level_enum_ctl_get(struct snd_kcontrol *kctl,
const struct scarlett2_device_info *info = private->info;
int index = elem->control + info->level_input_first;
+ int err = 0;
mutex_lock(&private->data_mutex);
- if (private->input_other_updated)
- scarlett2_update_input_other(mixer);
- ucontrol->value.enumerated.item[0] = private->level_switch[index];
- mutex_unlock(&private->data_mutex);
- return 0;
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ if (private->input_level_updated) {
+ err = scarlett2_update_input_level(mixer);
+ if (err < 0)
+ goto unlock;
+ }
+ ucontrol->value.enumerated.item[0] = scarlett2_decode_muteable(
+ private->level_switch[index]);
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
}
static int scarlett2_level_enum_ctl_put(struct snd_kcontrol *kctl,
@@ -2564,10 +4293,19 @@ static int scarlett2_level_enum_ctl_put(struct snd_kcontrol *kctl,
const struct scarlett2_device_info *info = private->info;
int index = elem->control + info->level_input_first;
- int oval, val, err = 0;
+ int oval, val, err;
mutex_lock(&private->data_mutex);
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ err = scarlett2_check_put_during_autogain(mixer);
+ if (err < 0)
+ goto unlock;
+
oval = private->level_switch[index];
val = !!ucontrol->value.enumerated.item[0];
@@ -2576,6 +4314,10 @@ static int scarlett2_level_enum_ctl_put(struct snd_kcontrol *kctl,
private->level_switch[index] = val;
+ /* To set the Gen 4 muteable controls, bit 1 gets set instead */
+ if (private->config_set->items[SCARLETT2_CONFIG_LEVEL_SWITCH].mute)
+ val = (!val) | 0x02;
+
/* Send switch change to the device */
err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_LEVEL_SWITCH,
index, val);
@@ -2597,21 +4339,47 @@ static const struct snd_kcontrol_new scarlett2_level_enum_ctl = {
/*** Pad Switch Controls ***/
+static int scarlett2_update_input_pad(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+
+ private->input_pad_updated = 0;
+
+ if (!info->pad_input_count)
+ return 0;
+
+ return scarlett2_usb_get_config(
+ mixer, SCARLETT2_CONFIG_PAD_SWITCH,
+ info->pad_input_count, private->pad_switch);
+}
+
static int scarlett2_pad_ctl_get(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *ucontrol)
{
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
+ int err = 0;
mutex_lock(&private->data_mutex);
- if (private->input_other_updated)
- scarlett2_update_input_other(mixer);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ if (private->input_pad_updated) {
+ err = scarlett2_update_input_pad(mixer);
+ if (err < 0)
+ goto unlock;
+ }
ucontrol->value.integer.value[0] =
private->pad_switch[elem->control];
- mutex_unlock(&private->data_mutex);
- return 0;
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
}
static int scarlett2_pad_ctl_put(struct snd_kcontrol *kctl,
@@ -2626,6 +4394,11 @@ static int scarlett2_pad_ctl_put(struct snd_kcontrol *kctl,
mutex_lock(&private->data_mutex);
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
oval = private->pad_switch[index];
val = !!ucontrol->value.integer.value[0];
@@ -2655,20 +4428,46 @@ static const struct snd_kcontrol_new scarlett2_pad_ctl = {
/*** Air Switch Controls ***/
+static int scarlett2_update_input_air(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+
+ private->input_air_updated = 0;
+
+ if (!info->air_input_count)
+ return 0;
+
+ return scarlett2_usb_get_config(
+ mixer, SCARLETT2_CONFIG_AIR_SWITCH,
+ info->air_input_count, private->air_switch);
+}
+
static int scarlett2_air_ctl_get(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *ucontrol)
{
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
+ int err = 0;
mutex_lock(&private->data_mutex);
- if (private->input_other_updated)
- scarlett2_update_input_other(mixer);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ if (private->input_air_updated) {
+ err = scarlett2_update_input_air(mixer);
+ if (err < 0)
+ goto unlock;
+ }
ucontrol->value.integer.value[0] = private->air_switch[elem->control];
- mutex_unlock(&private->data_mutex);
- return 0;
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
}
static int scarlett2_air_ctl_put(struct snd_kcontrol *kctl,
@@ -2679,12 +4478,21 @@ static int scarlett2_air_ctl_put(struct snd_kcontrol *kctl,
struct scarlett2_data *private = mixer->private_data;
int index = elem->control;
- int oval, val, err = 0;
+ int oval, val, err;
mutex_lock(&private->data_mutex);
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ err = scarlett2_check_put_during_autogain(mixer);
+ if (err < 0)
+ goto unlock;
+
oval = private->air_switch[index];
- val = !!ucontrol->value.integer.value[0];
+ val = ucontrol->value.integer.value[0];
if (oval == val)
goto unlock;
@@ -2702,31 +4510,169 @@ unlock:
return err;
}
-static const struct snd_kcontrol_new scarlett2_air_ctl = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "",
- .info = snd_ctl_boolean_mono_info,
- .get = scarlett2_air_ctl_get,
- .put = scarlett2_air_ctl_put,
+static int scarlett2_air_with_drive_ctl_info(
+ struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo)
+{
+ static const char *const values[3] = {
+ "Off", "Presence", "Presence + Drive"
+ };
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = mixer->private_data;
+ int err;
+
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ err = scarlett2_check_autogain_updated(mixer);
+ if (err < 0)
+ goto unlock;
+
+ err = snd_ctl_enum_info(uinfo, 1, 3, values);
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static const struct snd_kcontrol_new scarlett2_air_ctl[2] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "",
+ .info = snd_ctl_boolean_mono_info,
+ .get = scarlett2_air_ctl_get,
+ .put = scarlett2_air_ctl_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "",
+ .info = scarlett2_air_with_drive_ctl_info,
+ .get = scarlett2_air_ctl_get,
+ .put = scarlett2_air_ctl_put,
+ }
};
/*** Phantom Switch Controls ***/
+static int scarlett2_update_input_phantom(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+ int err;
+
+ private->input_phantom_updated = 0;
+
+ if (!info->phantom_count)
+ return 0;
+
+ err = scarlett2_usb_get_config(
+ mixer, SCARLETT2_CONFIG_PHANTOM_SWITCH,
+ info->phantom_count, private->phantom_switch);
+ if (err < 0)
+ return err;
+
+ if (scarlett2_has_config_item(private,
+ SCARLETT2_CONFIG_PHANTOM_PERSISTENCE)) {
+ err = scarlett2_usb_get_config(
+ mixer, SCARLETT2_CONFIG_PHANTOM_PERSISTENCE,
+ 1, &private->phantom_persistence);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/* Check if phantom power on the given input is currently changing state */
+static int scarlett2_phantom_is_switching(
+ struct scarlett2_data *private, int line_num)
+{
+ const struct scarlett2_device_info *info = private->info;
+ int index = line_num / info->inputs_per_phantom;
+
+ return !!(private->phantom_switch[index] & 0x02);
+}
+
+/* Update autogain controls' access mode when phantom power changes state */
+static void scarlett2_phantom_update_access(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+ int i;
+
+ /* Disable autogain controls if phantom power is changing state */
+ for (i = 0; i < info->gain_input_count; i++) {
+ int val = !scarlett2_phantom_is_switching(private, i);
+
+ scarlett2_set_ctl_access(private->autogain_ctls[i], val);
+ }
+}
+
+/* Notify of access mode change for autogain which can't be enabled
+ * while phantom power is changing.
+ */
+static void scarlett2_phantom_notify_access(struct usb_mixer_interface *mixer)
+{
+ struct snd_card *card = mixer->chip->card;
+ struct scarlett2_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+ int i;
+
+ for (i = 0; i < info->gain_input_count; i++)
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
+ &private->autogain_ctls[i]->id);
+}
+
+/* Call scarlett2_update_input_phantom() and
+ * scarlett2_phantom_update_access() if input_phantom_updated is set.
+ */
+static int scarlett2_check_input_phantom_updated(
+ struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+ int err;
+
+ if (!private->input_phantom_updated)
+ return 0;
+
+ err = scarlett2_update_input_phantom(mixer);
+ if (err < 0)
+ return err;
+
+ scarlett2_phantom_update_access(mixer);
+
+ return 0;
+}
+
static int scarlett2_phantom_ctl_get(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *ucontrol)
{
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
+ int err;
mutex_lock(&private->data_mutex);
- if (private->input_other_updated)
- scarlett2_update_input_other(mixer);
- ucontrol->value.integer.value[0] =
- private->phantom_switch[elem->control];
- mutex_unlock(&private->data_mutex);
- return 0;
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ err = scarlett2_check_input_phantom_updated(mixer);
+ if (err < 0)
+ goto unlock;
+
+ ucontrol->value.integer.value[0] = scarlett2_decode_muteable(
+ private->phantom_switch[elem->control]);
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
}
static int scarlett2_phantom_ctl_put(struct snd_kcontrol *kctl,
@@ -2735,12 +4681,22 @@ static int scarlett2_phantom_ctl_put(struct snd_kcontrol *kctl,
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
int index = elem->control;
- int oval, val, err = 0;
+ int oval, val, err;
mutex_lock(&private->data_mutex);
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ err = scarlett2_check_put_during_autogain(mixer);
+ if (err < 0)
+ goto unlock;
+
oval = private->phantom_switch[index];
val = !!ucontrol->value.integer.value[0];
@@ -2749,12 +4705,19 @@ static int scarlett2_phantom_ctl_put(struct snd_kcontrol *kctl,
private->phantom_switch[index] = val;
+ /* To set the Gen 4 muteable controls, bit 1 gets set */
+ if (private->config_set->items[SCARLETT2_CONFIG_PHANTOM_SWITCH].mute)
+ val = (!val) | 0x02;
+
/* Send switch change to the device */
err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_PHANTOM_SWITCH,
- index, val);
+ index + info->phantom_first, val);
if (err == 0)
err = 1;
+ scarlett2_phantom_update_access(mixer);
+ scarlett2_phantom_notify_access(mixer);
+
unlock:
mutex_unlock(&private->data_mutex);
return err;
@@ -2763,7 +4726,7 @@ unlock:
static const struct snd_kcontrol_new scarlett2_phantom_ctl = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "",
- .info = snd_ctl_boolean_mono_info,
+ .info = scarlett2_autogain_disables_ctl_info,
.get = scarlett2_phantom_ctl_get,
.put = scarlett2_phantom_ctl_put,
};
@@ -2792,6 +4755,11 @@ static int scarlett2_phantom_persistence_ctl_put(
mutex_lock(&private->data_mutex);
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
oval = private->phantom_persistence;
val = !!ucontrol->value.integer.value[0];
@@ -2819,7 +4787,7 @@ static const struct snd_kcontrol_new scarlett2_phantom_persistence_ctl = {
.put = scarlett2_phantom_persistence_ctl_put,
};
-/*** Direct Monitor Control ***/
+/*** Speaker Switching Control ***/
static int scarlett2_update_monitor_other(struct usb_mixer_interface *mixer)
{
@@ -2839,11 +4807,6 @@ static int scarlett2_update_monitor_other(struct usb_mixer_interface *mixer)
private->monitor_other_updated = 0;
- if (info->direct_monitor)
- return scarlett2_usb_get_config(
- mixer, SCARLETT2_CONFIG_DIRECT_MONITOR,
- 1, &private->direct_monitor_switch);
-
/* if it doesn't do speaker switching then it also doesn't do
* talkback
*/
@@ -2868,10 +4831,6 @@ static int scarlett2_update_monitor_other(struct usb_mixer_interface *mixer)
private->speaker_switching_switch = monitor_other_switch[0] + 1;
if (info->has_talkback) {
- const int (*port_count)[SCARLETT2_PORT_DIRNS] =
- info->port_count;
- int num_mixes =
- port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_IN];
u16 bitmap;
int i;
@@ -2885,110 +4844,13 @@ static int scarlett2_update_monitor_other(struct usb_mixer_interface *mixer)
1, &bitmap);
if (err < 0)
return err;
- for (i = 0; i < num_mixes; i++, bitmap >>= 1)
+ for (i = 0; i < private->num_mix_out; i++, bitmap >>= 1)
private->talkback_map[i] = bitmap & 1;
}
return 0;
}
-static int scarlett2_direct_monitor_ctl_get(
- struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol)
-{
- struct usb_mixer_elem_info *elem = kctl->private_data;
- struct usb_mixer_interface *mixer = elem->head.mixer;
- struct scarlett2_data *private = elem->head.mixer->private_data;
-
- mutex_lock(&private->data_mutex);
- if (private->monitor_other_updated)
- scarlett2_update_monitor_other(mixer);
- ucontrol->value.enumerated.item[0] = private->direct_monitor_switch;
- mutex_unlock(&private->data_mutex);
-
- return 0;
-}
-
-static int scarlett2_direct_monitor_ctl_put(
- struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol)
-{
- struct usb_mixer_elem_info *elem = kctl->private_data;
- struct usb_mixer_interface *mixer = elem->head.mixer;
- struct scarlett2_data *private = mixer->private_data;
-
- int index = elem->control;
- int oval, val, err = 0;
-
- mutex_lock(&private->data_mutex);
-
- oval = private->direct_monitor_switch;
- val = min(ucontrol->value.enumerated.item[0], 2U);
-
- if (oval == val)
- goto unlock;
-
- private->direct_monitor_switch = val;
-
- /* Send switch change to the device */
- err = scarlett2_usb_set_config(
- mixer, SCARLETT2_CONFIG_DIRECT_MONITOR, index, val);
- if (err == 0)
- err = 1;
-
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
-}
-
-static int scarlett2_direct_monitor_stereo_enum_ctl_info(
- struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo)
-{
- static const char *const values[3] = {
- "Off", "Mono", "Stereo"
- };
-
- return snd_ctl_enum_info(uinfo, 1, 3, values);
-}
-
-/* Direct Monitor for Solo is mono-only and only needs a boolean control
- * Direct Monitor for 2i2 is selectable between Off/Mono/Stereo
- */
-static const struct snd_kcontrol_new scarlett2_direct_monitor_ctl[2] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "",
- .info = snd_ctl_boolean_mono_info,
- .get = scarlett2_direct_monitor_ctl_get,
- .put = scarlett2_direct_monitor_ctl_put,
- },
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "",
- .info = scarlett2_direct_monitor_stereo_enum_ctl_info,
- .get = scarlett2_direct_monitor_ctl_get,
- .put = scarlett2_direct_monitor_ctl_put,
- }
-};
-
-static int scarlett2_add_direct_monitor_ctl(struct usb_mixer_interface *mixer)
-{
- struct scarlett2_data *private = mixer->private_data;
- const struct scarlett2_device_info *info = private->info;
- const char *s;
-
- if (!info->direct_monitor)
- return 0;
-
- s = info->direct_monitor == 1
- ? "Direct Monitor Playback Switch"
- : "Direct Monitor Playback Enum";
-
- return scarlett2_add_new_ctl(
- mixer, &scarlett2_direct_monitor_ctl[info->direct_monitor - 1],
- 0, 1, s, &private->direct_monitor_ctl);
-}
-
-/*** Speaker Switching Control ***/
-
static int scarlett2_speaker_switch_enum_ctl_info(
struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo)
{
@@ -3005,14 +4867,25 @@ static int scarlett2_speaker_switch_enum_ctl_get(
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
+ int err = 0;
mutex_lock(&private->data_mutex);
- if (private->monitor_other_updated)
- scarlett2_update_monitor_other(mixer);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ if (private->monitor_other_updated) {
+ err = scarlett2_update_monitor_other(mixer);
+ if (err < 0)
+ goto unlock;
+ }
ucontrol->value.enumerated.item[0] = private->speaker_switching_switch;
- mutex_unlock(&private->data_mutex);
- return 0;
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
}
/* when speaker switching gets enabled, switch the main/alt speakers
@@ -3083,6 +4956,11 @@ static int scarlett2_speaker_switch_enum_ctl_put(
mutex_lock(&private->data_mutex);
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
oval = private->speaker_switching_switch;
val = min(ucontrol->value.enumerated.item[0], 2U);
@@ -3127,8 +5005,7 @@ static const struct snd_kcontrol_new scarlett2_speaker_switch_enum_ctl = {
.put = scarlett2_speaker_switch_enum_ctl_put,
};
-static int scarlett2_add_speaker_switch_ctl(
- struct usb_mixer_interface *mixer)
+static int scarlett2_add_speaker_switch_ctl(struct usb_mixer_interface *mixer)
{
struct scarlett2_data *private = mixer->private_data;
const struct scarlett2_device_info *info = private->info;
@@ -3160,14 +5037,25 @@ static int scarlett2_talkback_enum_ctl_get(
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
+ int err = 0;
mutex_lock(&private->data_mutex);
- if (private->monitor_other_updated)
- scarlett2_update_monitor_other(mixer);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ if (private->monitor_other_updated) {
+ err = scarlett2_update_monitor_other(mixer);
+ if (err < 0)
+ goto unlock;
+ }
ucontrol->value.enumerated.item[0] = private->talkback_switch;
- mutex_unlock(&private->data_mutex);
- return 0;
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
}
static int scarlett2_talkback_enum_ctl_put(
@@ -3181,6 +5069,11 @@ static int scarlett2_talkback_enum_ctl_put(
mutex_lock(&private->data_mutex);
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
oval = private->talkback_switch;
val = min(ucontrol->value.enumerated.item[0], 2U);
@@ -3235,16 +5128,17 @@ static int scarlett2_talkback_map_ctl_put(
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
- const int (*port_count)[SCARLETT2_PORT_DIRNS] =
- private->info->port_count;
- int num_mixes = port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_IN];
-
int index = elem->control;
int oval, val, err = 0, i;
u16 bitmap = 0;
mutex_lock(&private->data_mutex);
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
oval = private->talkback_map[index];
val = !!ucontrol->value.integer.value[0];
@@ -3253,7 +5147,7 @@ static int scarlett2_talkback_map_ctl_put(
private->talkback_map[index] = val;
- for (i = 0; i < num_mixes; i++)
+ for (i = 0; i < private->num_mix_out; i++)
bitmap |= private->talkback_map[i] << i;
/* Send updated bitmap to the device */
@@ -3275,13 +5169,10 @@ static const struct snd_kcontrol_new scarlett2_talkback_map_ctl = {
.put = scarlett2_talkback_map_ctl_put,
};
-static int scarlett2_add_talkback_ctls(
- struct usb_mixer_interface *mixer)
+static int scarlett2_add_talkback_ctls(struct usb_mixer_interface *mixer)
{
struct scarlett2_data *private = mixer->private_data;
const struct scarlett2_device_info *info = private->info;
- const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count;
- int num_mixes = port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_IN];
int err, i;
char s[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
@@ -3295,7 +5186,7 @@ static int scarlett2_add_talkback_ctls(
if (err < 0)
return err;
- for (i = 0; i < num_mixes; i++) {
+ for (i = 0; i < private->num_mix_out; i++) {
snprintf(s, sizeof(s),
"Talkback Mix %c Playback Switch", i + 'A');
err = scarlett2_add_new_ctl(mixer, &scarlett2_talkback_map_ctl,
@@ -3315,14 +5206,25 @@ static int scarlett2_dim_mute_ctl_get(struct snd_kcontrol *kctl,
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
+ int err = 0;
mutex_lock(&private->data_mutex);
- if (private->vol_updated)
- scarlett2_update_volumes(mixer);
- mutex_unlock(&private->data_mutex);
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ if (private->dim_mute_updated) {
+ err = scarlett2_update_dim_mute(mixer);
+ if (err < 0)
+ goto unlock;
+ }
ucontrol->value.integer.value[0] = private->dim_mute[elem->control];
- return 0;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
}
static int scarlett2_dim_mute_ctl_put(struct snd_kcontrol *kctl,
@@ -3331,16 +5233,16 @@ static int scarlett2_dim_mute_ctl_put(struct snd_kcontrol *kctl,
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
- const struct scarlett2_device_info *info = private->info;
- const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count;
- int num_line_out =
- port_count[SCARLETT2_PORT_TYPE_ANALOGUE][SCARLETT2_PORT_OUT];
-
int index = elem->control;
int oval, val, err = 0, i;
mutex_lock(&private->data_mutex);
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
oval = private->dim_mute[index];
val = !!ucontrol->value.integer.value[0];
@@ -3356,7 +5258,7 @@ static int scarlett2_dim_mute_ctl_put(struct snd_kcontrol *kctl,
err = 1;
if (index == SCARLETT2_BUTTON_MUTE)
- for (i = 0; i < num_line_out; i++) {
+ for (i = 0; i < private->num_line_out; i++) {
int line_index = line_out_remap(private, i);
if (private->vol_sw_hw_switch[line_index]) {
@@ -3386,14 +5288,12 @@ static int scarlett2_add_line_out_ctls(struct usb_mixer_interface *mixer)
{
struct scarlett2_data *private = mixer->private_data;
const struct scarlett2_device_info *info = private->info;
- const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count;
- int num_line_out =
- port_count[SCARLETT2_PORT_TYPE_ANALOGUE][SCARLETT2_PORT_OUT];
int err, i;
char s[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
/* Add R/O HW volume control */
- if (info->line_out_hw_vol) {
+ if (scarlett2_has_config_item(private,
+ SCARLETT2_CONFIG_MASTER_VOLUME)) {
snprintf(s, sizeof(s), "Master HW Playback Volume");
err = scarlett2_add_new_ctl(mixer,
&scarlett2_master_volume_ctl,
@@ -3402,8 +5302,27 @@ static int scarlett2_add_line_out_ctls(struct usb_mixer_interface *mixer)
return err;
}
+ /* Add R/O headphone volume control */
+ if (scarlett2_has_config_item(private,
+ SCARLETT2_CONFIG_HEADPHONE_VOLUME)) {
+ snprintf(s, sizeof(s), "Headphone Playback Volume");
+ err = scarlett2_add_new_ctl(mixer,
+ &scarlett2_headphone_volume_ctl,
+ 0, 1, s,
+ &private->headphone_vol_ctl);
+ if (err < 0)
+ return err;
+ }
+
+ /* Remaining controls are only applicable if the device
+ * has per-channel line-out volume controls.
+ */
+ if (!scarlett2_has_config_item(private,
+ SCARLETT2_CONFIG_LINE_OUT_VOLUME))
+ return 0;
+
/* Add volume controls */
- for (i = 0; i < num_line_out; i++) {
+ for (i = 0; i < private->num_line_out; i++) {
int index = line_out_remap(private, i);
/* Fader */
@@ -3432,17 +5351,19 @@ static int scarlett2_add_line_out_ctls(struct usb_mixer_interface *mixer)
if (err < 0)
return err;
- /* Make the fader and mute controls read-only if the
- * SW/HW switch is set to HW
- */
- if (private->vol_sw_hw_switch[index])
- scarlett2_vol_ctl_set_writable(mixer, i, 0);
-
/* SW/HW Switch */
- if (info->line_out_hw_vol) {
- snprintf(s, sizeof(s),
- "Line Out %02d Volume Control Playback Enum",
- i + 1);
+ if (scarlett2_has_config_item(private,
+ SCARLETT2_CONFIG_SW_HW_SWITCH)) {
+
+ /* Make the fader and mute controls read-only if the
+ * SW/HW switch is set to HW
+ */
+ if (private->vol_sw_hw_switch[index])
+ scarlett2_vol_ctl_set_writable(mixer, i, 0);
+
+ scnprintf(s, sizeof(s),
+ "Line Out %02d Volume Control Playback Enum",
+ i + 1);
err = scarlett2_add_new_ctl(mixer,
&scarlett2_sw_hw_enum_ctl,
i, 1, s,
@@ -3459,7 +5380,7 @@ static int scarlett2_add_line_out_ctls(struct usb_mixer_interface *mixer)
}
/* Add dim/mute controls */
- if (info->line_out_hw_vol)
+ if (scarlett2_has_config_item(private, SCARLETT2_CONFIG_DIM_MUTE))
for (i = 0; i < SCARLETT2_DIM_MUTE_COUNT; i++) {
err = scarlett2_add_new_ctl(
mixer, &scarlett2_dim_mute_ctl,
@@ -3485,8 +5406,8 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer)
/* Add input level (line/inst) controls */
for (i = 0; i < info->level_input_count; i++) {
- snprintf(s, sizeof(s), fmt, i + 1 + info->level_input_first,
- "Level", "Enum");
+ scnprintf(s, sizeof(s), fmt, i + 1 + info->level_input_first,
+ "Level", "Enum");
err = scarlett2_add_new_ctl(mixer, &scarlett2_level_enum_ctl,
i, 1, s, &private->level_ctls[i]);
if (err < 0)
@@ -3495,7 +5416,7 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer)
/* Add input pad controls */
for (i = 0; i < info->pad_input_count; i++) {
- snprintf(s, sizeof(s), fmt, i + 1, "Pad", "Switch");
+ scnprintf(s, sizeof(s), fmt, i + 1, "Pad", "Switch");
err = scarlett2_add_new_ctl(mixer, &scarlett2_pad_ctl,
i, 1, s, &private->pad_ctls[i]);
if (err < 0)
@@ -3504,9 +5425,11 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer)
/* Add input air controls */
for (i = 0; i < info->air_input_count; i++) {
- snprintf(s, sizeof(s), fmt, i + 1, "Air", "Switch");
- err = scarlett2_add_new_ctl(mixer, &scarlett2_air_ctl,
- i, 1, s, &private->air_ctls[i]);
+ scnprintf(s, sizeof(s), fmt, i + 1 + info->air_input_first,
+ "Air", info->air_option ? "Enum" : "Switch");
+ err = scarlett2_add_new_ctl(
+ mixer, &scarlett2_air_ctl[info->air_option],
+ i, 1, s, &private->air_ctls[i]);
if (err < 0)
return err;
}
@@ -3514,7 +5437,8 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer)
/* Add input phantom controls */
if (info->inputs_per_phantom == 1) {
for (i = 0; i < info->phantom_count; i++) {
- scnprintf(s, sizeof(s), fmt, i + 1,
+ scnprintf(s, sizeof(s), fmt,
+ i + 1 + info->phantom_first,
"Phantom Power", "Switch");
err = scarlett2_add_new_ctl(
mixer, &scarlett2_phantom_ctl,
@@ -3536,7 +5460,9 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer)
return err;
}
}
- if (info->phantom_count) {
+ if (info->phantom_count &&
+ scarlett2_has_config_item(private,
+ SCARLETT2_CONFIG_PHANTOM_PERSISTENCE)) {
err = scarlett2_add_new_ctl(
mixer, &scarlett2_phantom_persistence_ctl, 0, 1,
"Phantom Power Persistence Capture Switch", NULL);
@@ -3544,11 +5470,92 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer)
return err;
}
+ /* Add software-controllable input gain controls */
+ if (info->gain_input_count) {
+ err = scarlett2_add_new_ctl(
+ mixer, &scarlett2_input_select_ctl, 0, 1,
+ "Input Select Capture Enum",
+ &private->input_select_ctl);
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < info->gain_input_count; i++) {
+ if (i % 2) {
+ scnprintf(s, sizeof(s),
+ "Line In %d-%d Link Capture Switch",
+ i, i + 1);
+ err = scarlett2_add_new_ctl(
+ mixer, &scarlett2_input_link_ctl,
+ i / 2, 1, s,
+ &private->input_link_ctls[i / 2]);
+ if (err < 0)
+ return err;
+ }
+
+ scnprintf(s, sizeof(s), fmt, i + 1,
+ "Gain", "Volume");
+ err = scarlett2_add_new_ctl(
+ mixer, &scarlett2_input_gain_ctl,
+ i, 1, s, &private->input_gain_ctls[i]);
+ if (err < 0)
+ return err;
+
+ scnprintf(s, sizeof(s), fmt, i + 1,
+ "Autogain", "Switch");
+ err = scarlett2_add_new_ctl(
+ mixer, &scarlett2_autogain_switch_ctl,
+ i, 1, s, &private->autogain_ctls[i]);
+ if (err < 0)
+ return err;
+
+ scnprintf(s, sizeof(s), fmt, i + 1,
+ "Autogain Status", "Enum");
+ err = scarlett2_add_new_ctl(
+ mixer, &scarlett2_autogain_status_ctl,
+ i, 1, s, &private->autogain_status_ctls[i]);
+
+ scnprintf(s, sizeof(s), fmt, i + 1,
+ "Safe", "Switch");
+ err = scarlett2_add_new_ctl(
+ mixer, &scarlett2_safe_ctl,
+ i, 1, s, &private->safe_ctls[i]);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ /* Add PCM Input Switch control */
+ if (scarlett2_has_config_item(private,
+ SCARLETT2_CONFIG_PCM_INPUT_SWITCH)) {
+ err = scarlett2_add_new_ctl(
+ mixer, &scarlett2_pcm_input_switch_ctl, 0, 1,
+ "PCM Input Capture Switch",
+ &private->pcm_input_switch_ctl);
+ if (err < 0)
+ return err;
+ }
+
return 0;
}
/*** Mixer Volume Controls ***/
+static int scarlett2_update_mix(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+ int i, err;
+
+ private->mix_updated = 0;
+
+ for (i = 0; i < private->num_mix_out; i++) {
+ err = scarlett2_usb_get_mix(mixer, i);
+ if (err < 0)
+ return err;
+ }
+
+ return 1;
+}
+
static int scarlett2_mixer_ctl_info(struct snd_kcontrol *kctl,
struct snd_ctl_elem_info *uinfo)
{
@@ -3566,10 +5573,27 @@ static int scarlett2_mixer_ctl_get(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *ucontrol)
{
struct usb_mixer_elem_info *elem = kctl->private_data;
- struct scarlett2_data *private = elem->head.mixer->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = mixer->private_data;
+ int err = 0;
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ if (private->mix_updated) {
+ err = scarlett2_update_mix(mixer);
+ if (err < 0)
+ goto unlock;
+ }
ucontrol->value.integer.value[0] = private->mix[elem->control];
- return 0;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
}
static int scarlett2_mixer_ctl_put(struct snd_kcontrol *kctl,
@@ -3578,17 +5602,20 @@ static int scarlett2_mixer_ctl_put(struct snd_kcontrol *kctl,
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
- const struct scarlett2_device_info *info = private->info;
- const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count;
- int oval, val, num_mixer_in, mix_num, err = 0;
+ int oval, val, mix_num, err = 0;
int index = elem->control;
mutex_lock(&private->data_mutex);
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
oval = private->mix[index];
- val = ucontrol->value.integer.value[0];
- num_mixer_in = port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_OUT];
- mix_num = index / num_mixer_in;
+ val = clamp(ucontrol->value.integer.value[0],
+ 0L, (long)SCARLETT2_MIXER_MAX_VALUE);
+ mix_num = index / private->num_mix_in;
if (oval == val)
goto unlock;
@@ -3624,24 +5651,18 @@ static const struct snd_kcontrol_new scarlett2_mixer_ctl = {
static int scarlett2_add_mixer_ctls(struct usb_mixer_interface *mixer)
{
struct scarlett2_data *private = mixer->private_data;
- const struct scarlett2_device_info *info = private->info;
- const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count;
int err, i, j;
int index;
char s[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
- int num_inputs =
- port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_OUT];
- int num_outputs =
- port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_IN];
-
- for (i = 0, index = 0; i < num_outputs; i++)
- for (j = 0; j < num_inputs; j++, index++) {
+ for (i = 0, index = 0; i < private->num_mix_out; i++)
+ for (j = 0; j < private->num_mix_in; j++, index++) {
snprintf(s, sizeof(s),
"Mix %c Input %02d Playback Volume",
'A' + i, j + 1);
err = scarlett2_add_new_ctl(mixer, &scarlett2_mixer_ctl,
- index, 1, s, NULL);
+ index, 1, s,
+ &private->mix_ctls[index]);
if (err < 0)
return err;
}
@@ -3649,6 +5670,252 @@ static int scarlett2_add_mixer_ctls(struct usb_mixer_interface *mixer)
return 0;
}
+/*** Direct Monitor Control ***/
+
+static int scarlett2_update_direct_monitor(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+
+ private->direct_monitor_updated = 0;
+
+ if (!private->info->direct_monitor)
+ return 0;
+
+ return scarlett2_usb_get_config(
+ mixer, SCARLETT2_CONFIG_DIRECT_MONITOR,
+ 1, &private->direct_monitor_switch);
+}
+
+static int scarlett2_update_monitor_mix(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+ int err, i;
+ u16 mix_values[SCARLETT2_MONITOR_MIX_MAX];
+
+ if (!private->num_monitor_mix_ctls)
+ return 0;
+
+ err = scarlett2_usb_get_config(
+ mixer, SCARLETT2_CONFIG_DIRECT_MONITOR_GAIN,
+ private->num_monitor_mix_ctls, mix_values);
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < private->num_monitor_mix_ctls; i++)
+ private->monitor_mix[i] = scarlett2_mixer_value_to_db(
+ mix_values[i]);
+
+ return 0;
+}
+
+static int scarlett2_direct_monitor_ctl_get(
+ struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = mixer->private_data;
+ int err = 0;
+
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ if (private->direct_monitor_updated) {
+ err = scarlett2_update_direct_monitor(mixer);
+ if (err < 0)
+ goto unlock;
+ }
+ ucontrol->value.enumerated.item[0] = private->direct_monitor_switch;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static int scarlett2_direct_monitor_ctl_put(
+ struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = mixer->private_data;
+
+ int index = elem->control;
+ int oval, val, err = 0;
+
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ oval = private->direct_monitor_switch;
+ val = min(ucontrol->value.enumerated.item[0], 2U);
+
+ if (oval == val)
+ goto unlock;
+
+ private->direct_monitor_switch = val;
+
+ /* Send switch change to the device */
+ err = scarlett2_usb_set_config(
+ mixer, SCARLETT2_CONFIG_DIRECT_MONITOR, index, val);
+ if (err == 0)
+ err = 1;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static int scarlett2_direct_monitor_stereo_enum_ctl_info(
+ struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo)
+{
+ static const char *const values[3] = {
+ "Off", "Mono", "Stereo"
+ };
+
+ return snd_ctl_enum_info(uinfo, 1, 3, values);
+}
+
+/* Direct Monitor for Solo is mono-only and only needs a boolean control
+ * Direct Monitor for 2i2 is selectable between Off/Mono/Stereo
+ */
+static const struct snd_kcontrol_new scarlett2_direct_monitor_ctl[2] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "",
+ .info = snd_ctl_boolean_mono_info,
+ .get = scarlett2_direct_monitor_ctl_get,
+ .put = scarlett2_direct_monitor_ctl_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "",
+ .info = scarlett2_direct_monitor_stereo_enum_ctl_info,
+ .get = scarlett2_direct_monitor_ctl_get,
+ .put = scarlett2_direct_monitor_ctl_put,
+ }
+};
+
+static int scarlett2_monitor_mix_ctl_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct scarlett2_data *private = elem->head.mixer->private_data;
+
+ ucontrol->value.integer.value[0] = private->monitor_mix[elem->control];
+
+ return 0;
+}
+
+static int scarlett2_monitor_mix_ctl_put(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = mixer->private_data;
+ int oval, val, err = 0;
+ int index = elem->control;
+
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ oval = private->monitor_mix[index];
+ val = clamp(ucontrol->value.integer.value[0],
+ 0L, (long)SCARLETT2_MIXER_MAX_VALUE);
+
+ if (oval == val)
+ goto unlock;
+
+ private->monitor_mix[index] = val;
+ err = scarlett2_usb_set_config(
+ mixer, SCARLETT2_CONFIG_DIRECT_MONITOR_GAIN,
+ index, scarlett2_mixer_values[val]);
+ if (err == 0)
+ err = 1;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static const struct snd_kcontrol_new scarlett2_monitor_mix_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+ .name = "",
+ .info = scarlett2_mixer_ctl_info,
+ .get = scarlett2_monitor_mix_ctl_get,
+ .put = scarlett2_monitor_mix_ctl_put,
+ .private_value = SCARLETT2_MIXER_MAX_DB, /* max value */
+ .tlv = { .p = db_scale_scarlett2_mixer }
+};
+
+static int scarlett2_add_direct_monitor_ctls(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+ const char *s;
+ int err, i, j, k, index;
+
+ if (!info->direct_monitor)
+ return 0;
+
+ s = info->direct_monitor == 1
+ ? "Direct Monitor Playback Switch"
+ : "Direct Monitor Playback Enum";
+
+ err = scarlett2_add_new_ctl(
+ mixer, &scarlett2_direct_monitor_ctl[info->direct_monitor - 1],
+ 0, 1, s, &private->direct_monitor_ctl);
+ if (err < 0)
+ return err;
+
+ if (!private->num_monitor_mix_ctls)
+ return 0;
+
+ /* 1 or 2 direct monitor selections (Mono & Stereo) */
+ for (i = 0, index = 0; i < info->direct_monitor; i++) {
+ const char * const format =
+ "Monitor %sMix %c Input %02d Playback Volume";
+ const char *mix_type;
+
+ if (info->direct_monitor == 1)
+ mix_type = "";
+ else if (i == 0)
+ mix_type = "1 ";
+ else
+ mix_type = "2 ";
+
+ /* 2 Mix outputs, A/Left & B/Right */
+ for (j = 0; j < 2; j++)
+
+ /* Mix inputs */
+ for (k = 0; k < private->num_mix_in; k++, index++) {
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+
+ scnprintf(name, sizeof(name), format,
+ mix_type, 'A' + j, k + 1);
+
+ err = scarlett2_add_new_ctl(
+ mixer, &scarlett2_monitor_mix_ctl,
+ index, 1, name, NULL);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ return 0;
+}
+
/*** Mux Source Selection Controls ***/
static int scarlett2_mux_src_enum_ctl_info(struct snd_kcontrol *kctl,
@@ -3676,8 +5943,16 @@ static int scarlett2_mux_src_enum_ctl_info(struct snd_kcontrol *kctl,
const struct scarlett2_port *port =
&scarlett2_ports[port_type];
- sprintf(uinfo->value.enumerated.name,
- port->src_descr, item + port->src_num_offset);
+ if (port_type == SCARLETT2_PORT_TYPE_MIX &&
+ item >= private->num_mix_out)
+ sprintf(uinfo->value.enumerated.name,
+ port->dsp_src_descr,
+ item - private->num_mix_out + 1);
+ else
+ sprintf(uinfo->value.enumerated.name,
+ port->src_descr,
+ item + port->src_num_offset);
+
return 0;
}
item -= port_count[port_type][SCARLETT2_PORT_IN];
@@ -3693,14 +5968,25 @@ static int scarlett2_mux_src_enum_ctl_get(struct snd_kcontrol *kctl,
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
int index = line_out_remap(private, elem->control);
+ int err = 0;
mutex_lock(&private->data_mutex);
- if (private->mux_updated)
- scarlett2_usb_get_mux(mixer);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ if (private->mux_updated) {
+ err = scarlett2_usb_get_mux(mixer);
+ if (err < 0)
+ goto unlock;
+ }
ucontrol->value.enumerated.item[0] = private->mux[index];
- mutex_unlock(&private->data_mutex);
- return 0;
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
}
static int scarlett2_mux_src_enum_ctl_put(struct snd_kcontrol *kctl,
@@ -3714,6 +6000,11 @@ static int scarlett2_mux_src_enum_ctl_put(struct snd_kcontrol *kctl,
mutex_lock(&private->data_mutex);
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
oval = private->mux[index];
val = min(ucontrol->value.enumerated.item[0],
private->num_mux_srcs - 1U);
@@ -3754,10 +6045,18 @@ static int scarlett2_add_mux_enums(struct usb_mixer_interface *mixer)
channel++, i++) {
int err;
char s[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
- const char *const descr =
- scarlett2_ports[port_type].dst_descr;
+ int channel_num = channel + 1;
+ const struct scarlett2_port *port =
+ &scarlett2_ports[port_type];
+ const char *descr = port->dst_descr;
+
+ if (port_type == SCARLETT2_PORT_TYPE_MIX &&
+ channel >= private->num_mix_in) {
+ channel_num -= private->num_mix_in;
+ descr = port->dsp_dst_descr;
+ }
- snprintf(s, sizeof(s) - 5, descr, channel + 1);
+ snprintf(s, sizeof(s) - 5, descr, channel_num);
strcat(s, " Enum");
err = scarlett2_add_new_ctl(mixer,
@@ -3791,15 +6090,23 @@ static int scarlett2_meter_ctl_get(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *ucontrol)
{
struct usb_mixer_elem_info *elem = kctl->private_data;
- struct scarlett2_data *private = elem->head.mixer->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = mixer->private_data;
u8 *meter_level_map = private->meter_level_map;
u16 meter_levels[SCARLETT2_MAX_METERS];
int i, err;
- err = scarlett2_usb_get_meter_levels(elem->head.mixer, elem->channels,
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ err = scarlett2_usb_get_meter_levels(mixer, elem->channels,
meter_levels);
if (err < 0)
- return err;
+ goto unlock;
/* copy & translate from meter_levels[] using meter_level_map[] */
for (i = 0; i < elem->channels; i++) {
@@ -3814,7 +6121,10 @@ static int scarlett2_meter_ctl_get(struct snd_kcontrol *kctl,
ucontrol->value.integer.value[i] = value;
}
- return 0;
+unlock:
+ mutex_unlock(&private->data_mutex);
+
+ return err;
}
static const struct snd_kcontrol_new scarlett2_meter_ctl = {
@@ -3830,7 +6140,7 @@ static int scarlett2_add_meter_ctl(struct usb_mixer_interface *mixer)
struct scarlett2_data *private = mixer->private_data;
/* devices without a mixer also don't support reporting levels */
- if (private->info->config_set == SCARLETT2_CONFIG_SET_GEN_3A)
+ if (!scarlett2_has_mixer(private))
return 0;
return scarlett2_add_new_ctl(mixer, &scarlett2_meter_ctl,
@@ -3861,6 +6171,11 @@ static int scarlett2_msd_ctl_put(struct snd_kcontrol *kctl,
mutex_lock(&private->data_mutex);
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
oval = private->msd_switch;
val = !!ucontrol->value.integer.value[0];
@@ -3891,9 +6206,8 @@ static const struct snd_kcontrol_new scarlett2_msd_ctl = {
static int scarlett2_add_msd_ctl(struct usb_mixer_interface *mixer)
{
struct scarlett2_data *private = mixer->private_data;
- const struct scarlett2_device_info *info = private->info;
- if (!info->has_msd_mode)
+ if (!scarlett2_has_config_item(private, SCARLETT2_CONFIG_MSD_SWITCH))
return 0;
/* If MSD mode is off, hide the switch by default */
@@ -3928,6 +6242,11 @@ static int scarlett2_standalone_ctl_put(struct snd_kcontrol *kctl,
mutex_lock(&private->data_mutex);
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
oval = private->standalone_switch;
val = !!ucontrol->value.integer.value[0];
@@ -3960,7 +6279,8 @@ static int scarlett2_add_standalone_ctl(struct usb_mixer_interface *mixer)
{
struct scarlett2_data *private = mixer->private_data;
- if (private->info->config_set == SCARLETT2_CONFIG_SET_GEN_3A)
+ if (!scarlett2_has_config_item(private,
+ SCARLETT2_CONFIG_STANDALONE_SWITCH))
return 0;
/* Add standalone control */
@@ -3968,6 +6288,91 @@ static int scarlett2_add_standalone_ctl(struct usb_mixer_interface *mixer)
0, 1, "Standalone Switch", NULL);
}
+/*** Power Status ***/
+
+static int scarlett2_update_power_status(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+ int err;
+ u8 power_ext;
+ u8 power_status;
+
+ private->power_status_updated = 0;
+
+ err = scarlett2_usb_get_config(mixer, SCARLETT2_CONFIG_POWER_EXT,
+ 1, &power_ext);
+ if (err < 0)
+ return err;
+
+ err = scarlett2_usb_get_config(mixer, SCARLETT2_CONFIG_POWER_STATUS,
+ 1, &power_status);
+ if (err < 0)
+ return err;
+
+ if (power_status > 1)
+ private->power_status = SCARLETT2_POWER_STATUS_FAIL;
+ else if (power_ext)
+ private->power_status = SCARLETT2_POWER_STATUS_EXT;
+ else
+ private->power_status = SCARLETT2_POWER_STATUS_BUS;
+
+ return 0;
+}
+
+static int scarlett2_power_status_ctl_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = mixer->private_data;
+ int err = 0;
+
+ mutex_lock(&private->data_mutex);
+
+ if (private->power_status_updated) {
+ err = scarlett2_update_power_status(mixer);
+ if (err < 0)
+ goto unlock;
+ }
+ ucontrol->value.integer.value[0] = private->power_status;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static int scarlett2_power_status_ctl_info(
+ struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo)
+{
+ static const char *const values[3] = {
+ "External", "Bus", "Fail"
+ };
+
+ return snd_ctl_enum_info(uinfo, 1, 3, values);
+}
+
+static const struct snd_kcontrol_new scarlett2_power_status_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .name = "",
+ .info = scarlett2_power_status_ctl_info,
+ .get = scarlett2_power_status_ctl_get,
+};
+
+static int scarlett2_add_power_status_ctl(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+
+ if (!scarlett2_has_config_item(private,
+ SCARLETT2_CONFIG_POWER_EXT))
+ return 0;
+
+ /* Add power status control */
+ return scarlett2_add_new_ctl(mixer, &scarlett2_power_status_ctl,
+ 0, 1, "Power Status Card Enum",
+ &private->power_status_ctl);
+}
+
/*** Cleanup/Suspend Callbacks ***/
static void scarlett2_private_free(struct usb_mixer_interface *mixer)
@@ -3989,12 +6394,13 @@ static void scarlett2_private_suspend(struct usb_mixer_interface *mixer)
/*** Initialisation ***/
-static void scarlett2_count_mux_io(struct scarlett2_data *private)
+static void scarlett2_count_io(struct scarlett2_data *private)
{
const struct scarlett2_device_info *info = private->info;
const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count;
int port_type, srcs = 0, dsts = 0;
+ /* Count the number of mux sources and destinations */
for (port_type = 0;
port_type < SCARLETT2_PORT_TYPE_COUNT;
port_type++) {
@@ -4004,6 +6410,26 @@ static void scarlett2_count_mux_io(struct scarlett2_data *private)
private->num_mux_srcs = srcs;
private->num_mux_dsts = dsts;
+
+ /* Mixer inputs are mux outputs and vice versa.
+ * Scarlett Gen 4 DSP I/O uses SCARLETT2_PORT_TYPE_MIX but
+ * doesn't have mixer controls.
+ */
+ private->num_mix_in =
+ port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_OUT] -
+ info->dsp_count;
+
+ private->num_mix_out =
+ port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_IN] -
+ info->dsp_count;
+
+ /* Number of analogue line outputs */
+ private->num_line_out =
+ port_count[SCARLETT2_PORT_TYPE_ANALOGUE][SCARLETT2_PORT_OUT];
+
+ /* Number of monitor mix controls */
+ private->num_monitor_mix_ctls =
+ info->direct_monitor * 2 * private->num_mix_in;
}
/* Look through the interface descriptors for the Focusrite Control
@@ -4057,8 +6483,9 @@ static int scarlett2_init_private(struct usb_mixer_interface *mixer,
mixer->private_suspend = scarlett2_private_suspend;
private->info = entry->info;
+ private->config_set = entry->info->config_set;
private->series_name = entry->series_name;
- scarlett2_count_mux_io(private);
+ scarlett2_count_io(private);
private->scarlett2_seq = 0;
private->mixer = mixer;
@@ -4107,97 +6534,254 @@ static int scarlett2_usb_init(struct usb_mixer_interface *mixer)
return 0;
}
+/* Get the flash segment numbers for the App_Settings and App_Upgrade
+ * segments and put them in the private data
+ */
+static int scarlett2_get_flash_segment_nums(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+ int err, count, i;
+
+ struct {
+ __le32 size;
+ __le32 count;
+ u8 unknown[8];
+ } __packed flash_info;
+
+ struct {
+ __le32 size;
+ __le32 flags;
+ char name[16];
+ } __packed segment_info;
+
+ err = scarlett2_usb(mixer, SCARLETT2_USB_INFO_FLASH,
+ NULL, 0,
+ &flash_info, sizeof(flash_info));
+ if (err < 0)
+ return err;
+
+ count = le32_to_cpu(flash_info.count);
+
+ /* sanity check count */
+ if (count < SCARLETT2_SEGMENT_NUM_MIN ||
+ count > SCARLETT2_SEGMENT_NUM_MAX + 1) {
+ usb_audio_err(mixer->chip,
+ "invalid flash segment count: %d\n", count);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < count; i++) {
+ __le32 segment_num_req = cpu_to_le32(i);
+ int flash_segment_id;
+
+ err = scarlett2_usb(mixer, SCARLETT2_USB_INFO_SEGMENT,
+ &segment_num_req, sizeof(segment_num_req),
+ &segment_info, sizeof(segment_info));
+ if (err < 0) {
+ usb_audio_err(mixer->chip,
+ "failed to get flash segment info %d: %d\n",
+ i, err);
+ return err;
+ }
+
+ if (!strncmp(segment_info.name,
+ SCARLETT2_SEGMENT_SETTINGS_NAME, 16))
+ flash_segment_id = SCARLETT2_SEGMENT_ID_SETTINGS;
+ else if (!strncmp(segment_info.name,
+ SCARLETT2_SEGMENT_FIRMWARE_NAME, 16))
+ flash_segment_id = SCARLETT2_SEGMENT_ID_FIRMWARE;
+ else
+ continue;
+
+ private->flash_segment_nums[flash_segment_id] = i;
+ private->flash_segment_blocks[flash_segment_id] =
+ le32_to_cpu(segment_info.size) /
+ SCARLETT2_FLASH_BLOCK_SIZE;
+ }
+
+ /* segment 0 is App_Gold and we never want to touch that, so
+ * use 0 as the "not-found" value
+ */
+ if (!private->flash_segment_nums[SCARLETT2_SEGMENT_ID_SETTINGS]) {
+ usb_audio_err(mixer->chip,
+ "failed to find flash segment %s\n",
+ SCARLETT2_SEGMENT_SETTINGS_NAME);
+ return -EINVAL;
+ }
+ if (!private->flash_segment_nums[SCARLETT2_SEGMENT_ID_FIRMWARE]) {
+ usb_audio_err(mixer->chip,
+ "failed to find flash segment %s\n",
+ SCARLETT2_SEGMENT_FIRMWARE_NAME);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
/* Read configuration from the interface on start */
static int scarlett2_read_configs(struct usb_mixer_interface *mixer)
{
struct scarlett2_data *private = mixer->private_data;
const struct scarlett2_device_info *info = private->info;
- const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count;
- int num_line_out =
- port_count[SCARLETT2_PORT_TYPE_ANALOGUE][SCARLETT2_PORT_OUT];
- int num_mixer_out =
- port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_IN];
- struct scarlett2_usb_volume_status volume_status;
int err, i;
- if (info->has_msd_mode) {
+ if (scarlett2_has_config_item(private, SCARLETT2_CONFIG_MSD_SWITCH)) {
err = scarlett2_usb_get_config(
mixer, SCARLETT2_CONFIG_MSD_SWITCH,
1, &private->msd_switch);
if (err < 0)
return err;
+ }
- /* no other controls are created if MSD mode is on */
- if (private->msd_switch)
- return 0;
+ if (private->firmware_version < info->min_firmware_version) {
+ usb_audio_err(mixer->chip,
+ "Focusrite %s firmware version %d is too old; "
+ "need %d",
+ private->series_name,
+ private->firmware_version,
+ info->min_firmware_version);
+ return 0;
}
- err = scarlett2_update_input_other(mixer);
+ /* no other controls are created if MSD mode is on */
+ if (private->msd_switch)
+ return 0;
+
+ err = scarlett2_update_input_level(mixer);
if (err < 0)
return err;
- err = scarlett2_update_monitor_other(mixer);
+ err = scarlett2_update_input_pad(mixer);
+ if (err < 0)
+ return err;
+
+ err = scarlett2_update_input_air(mixer);
+ if (err < 0)
+ return err;
+
+ err = scarlett2_update_input_phantom(mixer);
+ if (err < 0)
+ return err;
+
+ err = scarlett2_update_direct_monitor(mixer);
if (err < 0)
return err;
/* the rest of the configuration is for devices with a mixer */
- if (info->config_set == SCARLETT2_CONFIG_SET_GEN_3A)
+ if (!scarlett2_has_mixer(private))
return 0;
- err = scarlett2_usb_get_config(
- mixer, SCARLETT2_CONFIG_STANDALONE_SWITCH,
- 1, &private->standalone_switch);
+ err = scarlett2_update_monitor_mix(mixer);
if (err < 0)
return err;
- err = scarlett2_update_sync(mixer);
+ err = scarlett2_update_monitor_other(mixer);
if (err < 0)
return err;
- err = scarlett2_usb_get_volume_status(mixer, &volume_status);
+ if (scarlett2_has_config_item(private,
+ SCARLETT2_CONFIG_STANDALONE_SWITCH)) {
+ err = scarlett2_usb_get_config(
+ mixer, SCARLETT2_CONFIG_STANDALONE_SWITCH,
+ 1, &private->standalone_switch);
+ if (err < 0)
+ return err;
+ }
+
+ if (scarlett2_has_config_item(private,
+ SCARLETT2_CONFIG_POWER_EXT)) {
+ err = scarlett2_update_power_status(mixer);
+ if (err < 0)
+ return err;
+ }
+
+ err = scarlett2_update_sync(mixer);
if (err < 0)
return err;
- if (info->line_out_hw_vol)
- for (i = 0; i < SCARLETT2_DIM_MUTE_COUNT; i++)
- private->dim_mute[i] = !!volume_status.dim_mute[i];
+ if (scarlett2_has_config_item(private,
+ SCARLETT2_CONFIG_LINE_OUT_VOLUME)) {
+ s16 sw_vol[SCARLETT2_ANALOGUE_MAX];
- private->master_vol = clamp(
- volume_status.master_vol + SCARLETT2_VOLUME_BIAS,
- 0, SCARLETT2_VOLUME_BIAS);
+ /* read SW line out volume */
+ err = scarlett2_usb_get_config(
+ mixer, SCARLETT2_CONFIG_LINE_OUT_VOLUME,
+ private->num_line_out, &sw_vol);
+ if (err < 0)
+ return err;
- for (i = 0; i < num_line_out; i++) {
- int volume, mute;
+ for (i = 0; i < private->num_line_out; i++)
+ private->vol[i] = clamp(
+ sw_vol[i] + SCARLETT2_VOLUME_BIAS,
+ 0, SCARLETT2_VOLUME_BIAS);
- private->vol_sw_hw_switch[i] =
- info->line_out_hw_vol
- && volume_status.sw_hw_switch[i];
+ /* read SW mute */
+ err = scarlett2_usb_get_config(
+ mixer, SCARLETT2_CONFIG_MUTE_SWITCH,
+ private->num_line_out, &private->mute_switch);
+ if (err < 0)
+ return err;
- volume = private->vol_sw_hw_switch[i]
- ? volume_status.master_vol
- : volume_status.sw_vol[i];
- volume = clamp(volume + SCARLETT2_VOLUME_BIAS,
- 0, SCARLETT2_VOLUME_BIAS);
- private->vol[i] = volume;
+ for (i = 0; i < private->num_line_out; i++)
+ private->mute_switch[i] =
+ !!private->mute_switch[i];
+
+ /* read SW/HW switches */
+ if (scarlett2_has_config_item(private,
+ SCARLETT2_CONFIG_SW_HW_SWITCH)) {
+ err = scarlett2_usb_get_config(
+ mixer, SCARLETT2_CONFIG_SW_HW_SWITCH,
+ private->num_line_out,
+ &private->vol_sw_hw_switch);
+ if (err < 0)
+ return err;
- mute = private->vol_sw_hw_switch[i]
- ? private->dim_mute[SCARLETT2_BUTTON_MUTE]
- : volume_status.mute_switch[i];
- private->mute_switch[i] = mute;
+ for (i = 0; i < private->num_line_out; i++)
+ private->vol_sw_hw_switch[i] =
+ !!private->vol_sw_hw_switch[i];
+ }
}
- for (i = 0; i < num_mixer_out; i++) {
- err = scarlett2_usb_get_mix(mixer, i);
+ err = scarlett2_update_volumes(mixer);
+ if (err < 0)
+ return err;
+
+ err = scarlett2_update_dim_mute(mixer);
+ if (err < 0)
+ return err;
+
+ err = scarlett2_update_input_select(mixer);
+ if (err < 0)
+ return err;
+
+ err = scarlett2_update_input_gain(mixer);
+ if (err < 0)
+ return err;
+
+ err = scarlett2_update_autogain(mixer);
+ if (err < 0)
+ return err;
+
+ err = scarlett2_update_input_safe(mixer);
+ if (err < 0)
+ return err;
+
+ if (scarlett2_has_config_item(private,
+ SCARLETT2_CONFIG_PCM_INPUT_SWITCH)) {
+ err = scarlett2_update_pcm_input_switch(mixer);
if (err < 0)
return err;
}
+ err = scarlett2_update_mix(mixer);
+ if (err < 0)
+ return err;
+
return scarlett2_usb_get_mux(mixer);
}
/* Notify on sync change */
-static void scarlett2_notify_sync(
- struct usb_mixer_interface *mixer)
+static void scarlett2_notify_sync(struct usb_mixer_interface *mixer)
{
struct scarlett2_data *private = mixer->private_data;
@@ -4207,20 +6791,14 @@ static void scarlett2_notify_sync(
&private->sync_ctl->id);
}
-/* Notify on monitor change */
-static void scarlett2_notify_monitor(
- struct usb_mixer_interface *mixer)
+/* Notify on monitor change (Gen 2/3) */
+static void scarlett2_notify_monitor(struct usb_mixer_interface *mixer)
{
struct snd_card *card = mixer->chip->card;
struct scarlett2_data *private = mixer->private_data;
- const struct scarlett2_device_info *info = private->info;
- const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count;
- int num_line_out =
- port_count[SCARLETT2_PORT_TYPE_ANALOGUE][SCARLETT2_PORT_OUT];
int i;
- /* if line_out_hw_vol is 0, there are no controls to update */
- if (!info->line_out_hw_vol)
+ if (!scarlett2_has_config_item(private, SCARLETT2_CONFIG_SW_HW_SWITCH))
return;
private->vol_updated = 1;
@@ -4228,82 +6806,208 @@ static void scarlett2_notify_monitor(
snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
&private->master_vol_ctl->id);
- for (i = 0; i < num_line_out; i++)
+ for (i = 0; i < private->num_line_out; i++)
if (private->vol_sw_hw_switch[line_out_remap(private, i)])
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
&private->vol_ctls[i]->id);
}
+/* Notify on volume change (Gen 4) */
+static void scarlett2_notify_volume(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+
+ private->vol_updated = 1;
+
+ snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &private->master_vol_ctl->id);
+ snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &private->headphone_vol_ctl->id);
+}
+
/* Notify on dim/mute change */
-static void scarlett2_notify_dim_mute(
- struct usb_mixer_interface *mixer)
+static void scarlett2_notify_dim_mute(struct usb_mixer_interface *mixer)
{
struct snd_card *card = mixer->chip->card;
struct scarlett2_data *private = mixer->private_data;
- const struct scarlett2_device_info *info = private->info;
- const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count;
- int num_line_out =
- port_count[SCARLETT2_PORT_TYPE_ANALOGUE][SCARLETT2_PORT_OUT];
int i;
- private->vol_updated = 1;
-
- if (!info->line_out_hw_vol)
+ if (!scarlett2_has_config_item(private, SCARLETT2_CONFIG_SW_HW_SWITCH))
return;
+ private->dim_mute_updated = 1;
+
for (i = 0; i < SCARLETT2_DIM_MUTE_COUNT; i++)
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
&private->dim_mute_ctls[i]->id);
- for (i = 0; i < num_line_out; i++)
+ for (i = 0; i < private->num_line_out; i++)
if (private->vol_sw_hw_switch[line_out_remap(private, i)])
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
&private->mute_ctls[i]->id);
}
-/* Notify on "input other" change (level/pad/air) */
-static void scarlett2_notify_input_other(
- struct usb_mixer_interface *mixer)
+/* Notify on input level switch change */
+static void scarlett2_notify_input_level(struct usb_mixer_interface *mixer)
{
struct snd_card *card = mixer->chip->card;
struct scarlett2_data *private = mixer->private_data;
const struct scarlett2_device_info *info = private->info;
int i;
- private->input_other_updated = 1;
+ private->input_level_updated = 1;
for (i = 0; i < info->level_input_count; i++)
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
&private->level_ctls[i]->id);
+}
+
+/* Notify on input pad switch change */
+static void scarlett2_notify_input_pad(struct usb_mixer_interface *mixer)
+{
+ struct snd_card *card = mixer->chip->card;
+ struct scarlett2_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+ int i;
+
+ private->input_pad_updated = 1;
+
for (i = 0; i < info->pad_input_count; i++)
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
&private->pad_ctls[i]->id);
+}
+
+/* Notify on input air switch change */
+static void scarlett2_notify_input_air(struct usb_mixer_interface *mixer)
+{
+ struct snd_card *card = mixer->chip->card;
+ struct scarlett2_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+ int i;
+
+ private->input_air_updated = 1;
+
for (i = 0; i < info->air_input_count; i++)
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
&private->air_ctls[i]->id);
+}
+
+/* Notify on input phantom switch change */
+static void scarlett2_notify_input_phantom(struct usb_mixer_interface *mixer)
+{
+ struct snd_card *card = mixer->chip->card;
+ struct scarlett2_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+ int i;
+
+ private->input_phantom_updated = 1;
+
for (i = 0; i < info->phantom_count; i++)
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
&private->phantom_ctls[i]->id);
+
+ scarlett2_phantom_notify_access(mixer);
}
-/* Notify on "monitor other" change (direct monitor, speaker
- * switching, talkback)
- */
-static void scarlett2_notify_monitor_other(
- struct usb_mixer_interface *mixer)
+/* Notify on "input other" change (level/pad/air/phantom) */
+static void scarlett2_notify_input_other(struct usb_mixer_interface *mixer)
+{
+ scarlett2_notify_input_level(mixer);
+ scarlett2_notify_input_pad(mixer);
+ scarlett2_notify_input_air(mixer);
+ scarlett2_notify_input_phantom(mixer);
+}
+
+/* Notify on input select change */
+static void scarlett2_notify_input_select(struct usb_mixer_interface *mixer)
{
struct snd_card *card = mixer->chip->card;
struct scarlett2_data *private = mixer->private_data;
const struct scarlett2_device_info *info = private->info;
+ int i;
- private->monitor_other_updated = 1;
+ if (!info->gain_input_count)
+ return;
- if (info->direct_monitor) {
+ private->input_select_updated = 1;
+
+ snd_ctl_notify(card,
+ SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO,
+ &private->input_select_ctl->id);
+
+ for (i = 0; i < info->gain_input_count / 2; i++)
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
- &private->direct_monitor_ctl->id);
+ &private->input_link_ctls[i]->id);
+}
+
+/* Notify on input gain change */
+static void scarlett2_notify_input_gain(struct usb_mixer_interface *mixer)
+{
+ struct snd_card *card = mixer->chip->card;
+ struct scarlett2_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+ int i;
+
+ if (!info->gain_input_count)
+ return;
+
+ private->input_gain_updated = 1;
+
+ for (i = 0; i < info->gain_input_count; i++)
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &private->input_gain_ctls[i]->id);
+}
+
+/* Notify on autogain change */
+static void scarlett2_notify_autogain(struct usb_mixer_interface *mixer)
+{
+ struct snd_card *card = mixer->chip->card;
+ struct scarlett2_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+ int i;
+
+ if (!info->gain_input_count)
return;
+
+ private->autogain_updated = 1;
+
+ for (i = 0; i < info->gain_input_count; i++) {
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &private->autogain_ctls[i]->id);
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &private->autogain_status_ctls[i]->id);
}
+ scarlett2_autogain_notify_access(mixer);
+}
+
+/* Notify on input safe switch change */
+static void scarlett2_notify_input_safe(struct usb_mixer_interface *mixer)
+{
+ struct snd_card *card = mixer->chip->card;
+ struct scarlett2_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+ int i;
+
+ if (!info->gain_input_count)
+ return;
+
+ private->input_safe_updated = 1;
+
+ for (i = 0; i < info->gain_input_count; i++)
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &private->safe_ctls[i]->id);
+}
+
+/* Notify on "monitor other" change (speaker switching, talkback) */
+static void scarlett2_notify_monitor_other(struct usb_mixer_interface *mixer)
+{
+ struct snd_card *card = mixer->chip->card;
+ struct scarlett2_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+
+ private->monitor_other_updated = 1;
+
if (info->has_speaker_switching)
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
&private->speaker_switching_ctl->id);
@@ -4329,6 +7033,70 @@ static void scarlett2_notify_monitor_other(
}
}
+/* Notify on direct monitor switch change */
+static void scarlett2_notify_direct_monitor(struct usb_mixer_interface *mixer)
+{
+ struct snd_card *card = mixer->chip->card;
+ struct scarlett2_data *private = mixer->private_data;
+ int count = private->num_mix_in * private->num_mix_out;
+ int i;
+
+ private->direct_monitor_updated = 1;
+
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &private->direct_monitor_ctl->id);
+
+ if (!scarlett2_has_mixer(private))
+ return;
+
+ private->mix_updated = 1;
+
+ /* Notify of change to the mix controls */
+ for (i = 0; i < count; i++)
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &private->mix_ctls[i]->id);
+}
+
+/* Notify on power change */
+static void scarlett2_notify_power_status(struct usb_mixer_interface *mixer)
+{
+ struct snd_card *card = mixer->chip->card;
+ struct scarlett2_data *private = mixer->private_data;
+
+ private->power_status_updated = 1;
+
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &private->power_status_ctl->id);
+}
+
+/* Notify on mux change */
+static void scarlett2_notify_mux(struct usb_mixer_interface *mixer)
+{
+ struct snd_card *card = mixer->chip->card;
+ struct scarlett2_data *private = mixer->private_data;
+ int i;
+
+ private->mux_updated = 1;
+
+ for (i = 0; i < private->num_mux_dsts; i++)
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &private->mux_ctls[i]->id);
+}
+
+/* Notify on PCM input switch change */
+static void scarlett2_notify_pcm_input_switch(struct usb_mixer_interface *mixer)
+{
+ struct snd_card *card = mixer->chip->card;
+ struct scarlett2_data *private = mixer->private_data;
+
+ private->pcm_input_switch_updated = 1;
+
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &private->pcm_input_switch_ctl->id);
+
+ scarlett2_notify_mux(mixer);
+}
+
/* Interrupt callback */
static void scarlett2_notify(struct urb *urb)
{
@@ -4336,21 +7104,28 @@ static void scarlett2_notify(struct urb *urb)
int len = urb->actual_length;
int ustatus = urb->status;
u32 data;
+ struct scarlett2_data *private = mixer->private_data;
+ const struct scarlett2_notification *notifications =
+ private->config_set->notifications;
if (ustatus != 0 || len != 8)
goto requeue;
data = le32_to_cpu(*(__le32 *)urb->transfer_buffer);
- if (data & SCARLETT2_USB_NOTIFY_SYNC)
- scarlett2_notify_sync(mixer);
- if (data & SCARLETT2_USB_NOTIFY_MONITOR)
- scarlett2_notify_monitor(mixer);
- if (data & SCARLETT2_USB_NOTIFY_DIM_MUTE)
- scarlett2_notify_dim_mute(mixer);
- if (data & SCARLETT2_USB_NOTIFY_INPUT_OTHER)
- scarlett2_notify_input_other(mixer);
- if (data & SCARLETT2_USB_NOTIFY_MONITOR_OTHER)
- scarlett2_notify_monitor_other(mixer);
+
+ while (data && notifications->mask) {
+ if (data & notifications->mask) {
+ data &= ~notifications->mask;
+ if (notifications->func)
+ notifications->func(mixer);
+ }
+ notifications++;
+ }
+
+ if (data)
+ usb_audio_warn(mixer->chip,
+ "%s: Unhandled notification: 0x%08x\n",
+ __func__, data);
requeue:
if (ustatus != -ENOENT &&
@@ -4410,6 +7185,7 @@ static int snd_scarlett2_controls_create(
struct usb_mixer_interface *mixer,
const struct scarlett2_device_entry *entry)
{
+ struct scarlett2_data *private;
int err;
/* Initialise private data */
@@ -4417,16 +7193,28 @@ static int snd_scarlett2_controls_create(
if (err < 0)
return err;
+ private = mixer->private_data;
+
/* Send proprietary USB initialisation sequence */
err = scarlett2_usb_init(mixer);
if (err < 0)
return err;
+ /* Get the upgrade & settings flash segment numbers */
+ err = scarlett2_get_flash_segment_nums(mixer);
+ if (err < 0)
+ return err;
+
/* Add firmware version control */
err = scarlett2_add_firmware_version_ctl(mixer);
if (err < 0)
return err;
+ /* Add minimum firmware version control */
+ err = scarlett2_add_min_firmware_version_ctl(mixer);
+ if (err < 0)
+ return err;
+
/* Read volume levels and controls from the interface */
err = scarlett2_read_configs(mixer);
if (err < 0)
@@ -4437,8 +7225,11 @@ static int snd_scarlett2_controls_create(
if (err < 0)
return err;
- /* If MSD mode is enabled, don't create any other controls */
- if (((struct scarlett2_data *)mixer->private_data)->msd_switch)
+ /* If MSD mode is enabled, or if the firmware version is too
+ * old, don't create any other controls
+ */
+ if (private->msd_switch ||
+ private->firmware_version < private->info->min_firmware_version)
return 0;
/* Create the analogue output controls */
@@ -4471,8 +7262,8 @@ static int snd_scarlett2_controls_create(
if (err < 0)
return err;
- /* Create the direct monitor control */
- err = scarlett2_add_direct_monitor_ctl(mixer);
+ /* Create the direct monitor control(s) */
+ err = scarlett2_add_direct_monitor_ctls(mixer);
if (err < 0)
return err;
@@ -4491,6 +7282,19 @@ static int snd_scarlett2_controls_create(
if (err < 0)
return err;
+ /* Create the power status control */
+ err = scarlett2_add_power_status_ctl(mixer);
+ if (err < 0)
+ return err;
+
+ /* Set the access mode of controls disabled during
+ * autogain/phantom power switching.
+ */
+ if (private->info->gain_input_count) {
+ scarlett2_autogain_update_access(mixer);
+ scarlett2_phantom_update_access(mixer);
+ }
+
/* Set up the interrupt polling */
err = scarlett2_init_notify(mixer);
if (err < 0)
@@ -4499,6 +7303,393 @@ static int snd_scarlett2_controls_create(
return 0;
}
+/*** hwdep interface ***/
+
+/* Set private->hwdep_in_use; prevents access to the ALSA controls
+ * while doing a config erase/firmware upgrade.
+ */
+static void scarlett2_lock(struct scarlett2_data *private)
+{
+ mutex_lock(&private->data_mutex);
+ private->hwdep_in_use = 1;
+ mutex_unlock(&private->data_mutex);
+}
+
+/* Call SCARLETT2_USB_GET_ERASE to get the erase progress */
+static int scarlett2_get_erase_progress(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+ int segment_id, segment_num, err;
+ u8 erase_resp;
+
+ struct {
+ __le32 segment_num;
+ __le32 pad;
+ } __packed erase_req;
+
+ segment_id = private->selected_flash_segment_id;
+ segment_num = private->flash_segment_nums[segment_id];
+
+ if (segment_num < SCARLETT2_SEGMENT_NUM_MIN ||
+ segment_num > SCARLETT2_SEGMENT_NUM_MAX)
+ return -EFAULT;
+
+ /* Send the erase progress request */
+ erase_req.segment_num = cpu_to_le32(segment_num);
+ erase_req.pad = 0;
+
+ err = scarlett2_usb(mixer, SCARLETT2_USB_GET_ERASE,
+ &erase_req, sizeof(erase_req),
+ &erase_resp, sizeof(erase_resp));
+ if (err < 0)
+ return err;
+
+ return erase_resp;
+}
+
+/* Repeatedly call scarlett2_get_erase_progress() until it returns
+ * 0xff (erase complete) or we've waited 10 seconds (it usually takes
+ * <3 seconds).
+ */
+static int scarlett2_wait_for_erase(struct usb_mixer_interface *mixer)
+{
+ int i, err;
+
+ for (i = 0; i < 100; i++) {
+ err = scarlett2_get_erase_progress(mixer);
+ if (err < 0)
+ return err;
+
+ if (err == 0xff)
+ return 0;
+
+ msleep(100);
+ }
+
+ return -ETIMEDOUT;
+}
+
+/* Reboot the device; wait for the erase to complete if one is in
+ * progress.
+ */
+static int scarlett2_reboot(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+
+ if (private->flash_write_state ==
+ SCARLETT2_FLASH_WRITE_STATE_ERASING) {
+ int err = scarlett2_wait_for_erase(mixer);
+
+ if (err < 0)
+ return err;
+ }
+
+ return scarlett2_usb(mixer, SCARLETT2_USB_REBOOT, NULL, 0, NULL, 0);
+}
+
+/* Select a flash segment for erasing (and possibly writing to) */
+static int scarlett2_ioctl_select_flash_segment(
+ struct usb_mixer_interface *mixer,
+ unsigned long arg)
+{
+ struct scarlett2_data *private = mixer->private_data;
+ int segment_id, segment_num;
+
+ if (get_user(segment_id, (int __user *)arg))
+ return -EFAULT;
+
+ /* Check the segment ID and segment number */
+ if (segment_id < 0 || segment_id >= SCARLETT2_SEGMENT_ID_COUNT)
+ return -EINVAL;
+
+ segment_num = private->flash_segment_nums[segment_id];
+ if (segment_num < SCARLETT2_SEGMENT_NUM_MIN ||
+ segment_num > SCARLETT2_SEGMENT_NUM_MAX) {
+ usb_audio_err(mixer->chip,
+ "%s: invalid segment number %d\n",
+ __func__, segment_id);
+ return -EFAULT;
+ }
+
+ /* If erasing, wait for it to complete */
+ if (private->flash_write_state == SCARLETT2_FLASH_WRITE_STATE_ERASING) {
+ int err = scarlett2_wait_for_erase(mixer);
+
+ if (err < 0)
+ return err;
+ }
+
+ /* Save the selected segment ID and set the state to SELECTED */
+ private->selected_flash_segment_id = segment_id;
+ private->flash_write_state = SCARLETT2_FLASH_WRITE_STATE_SELECTED;
+
+ return 0;
+}
+
+/* Erase the previously-selected flash segment */
+static int scarlett2_ioctl_erase_flash_segment(
+ struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+ int segment_id, segment_num, err;
+
+ struct {
+ __le32 segment_num;
+ __le32 pad;
+ } __packed erase_req;
+
+ if (private->flash_write_state != SCARLETT2_FLASH_WRITE_STATE_SELECTED)
+ return -EINVAL;
+
+ segment_id = private->selected_flash_segment_id;
+ segment_num = private->flash_segment_nums[segment_id];
+
+ if (segment_num < SCARLETT2_SEGMENT_NUM_MIN ||
+ segment_num > SCARLETT2_SEGMENT_NUM_MAX)
+ return -EFAULT;
+
+ /* Prevent access to ALSA controls that access the device from
+ * here on
+ */
+ scarlett2_lock(private);
+
+ /* Send the erase request */
+ erase_req.segment_num = cpu_to_le32(segment_num);
+ erase_req.pad = 0;
+
+ err = scarlett2_usb(mixer, SCARLETT2_USB_ERASE_SEGMENT,
+ &erase_req, sizeof(erase_req),
+ NULL, 0);
+ if (err < 0)
+ return err;
+
+ /* On success, change the state from SELECTED to ERASING */
+ private->flash_write_state = SCARLETT2_FLASH_WRITE_STATE_ERASING;
+
+ return 0;
+}
+
+/* Get the erase progress from the device */
+static int scarlett2_ioctl_get_erase_progress(
+ struct usb_mixer_interface *mixer,
+ unsigned long arg)
+{
+ struct scarlett2_data *private = mixer->private_data;
+ struct scarlett2_flash_segment_erase_progress progress;
+ int segment_id, segment_num, err;
+ u8 erase_resp;
+
+ struct {
+ __le32 segment_num;
+ __le32 pad;
+ } __packed erase_req;
+
+ /* Check that we're erasing */
+ if (private->flash_write_state != SCARLETT2_FLASH_WRITE_STATE_ERASING)
+ return -EINVAL;
+
+ segment_id = private->selected_flash_segment_id;
+ segment_num = private->flash_segment_nums[segment_id];
+
+ if (segment_num < SCARLETT2_SEGMENT_NUM_MIN ||
+ segment_num > SCARLETT2_SEGMENT_NUM_MAX)
+ return -EFAULT;
+
+ /* Send the erase progress request */
+ erase_req.segment_num = cpu_to_le32(segment_num);
+ erase_req.pad = 0;
+
+ err = scarlett2_usb(mixer, SCARLETT2_USB_GET_ERASE,
+ &erase_req, sizeof(erase_req),
+ &erase_resp, sizeof(erase_resp));
+ if (err < 0)
+ return err;
+
+ progress.progress = erase_resp;
+ progress.num_blocks = private->flash_segment_blocks[segment_id];
+
+ if (copy_to_user((void __user *)arg, &progress, sizeof(progress)))
+ return -EFAULT;
+
+ /* If the erase is complete, change the state from ERASING to
+ * WRITE.
+ */
+ if (progress.progress == 0xff)
+ private->flash_write_state = SCARLETT2_FLASH_WRITE_STATE_WRITE;
+
+ return 0;
+}
+
+static int scarlett2_hwdep_open(struct snd_hwdep *hw, struct file *file)
+{
+ struct usb_mixer_interface *mixer = hw->private_data;
+ struct scarlett2_data *private = mixer->private_data;
+
+ /* If erasing, wait for it to complete */
+ if (private->flash_write_state ==
+ SCARLETT2_FLASH_WRITE_STATE_ERASING) {
+ int err = scarlett2_wait_for_erase(mixer);
+
+ if (err < 0)
+ return err;
+ }
+
+ /* Set the state to IDLE */
+ private->flash_write_state = SCARLETT2_FLASH_WRITE_STATE_IDLE;
+
+ return 0;
+}
+
+static int scarlett2_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct usb_mixer_interface *mixer = hw->private_data;
+
+ switch (cmd) {
+
+ case SCARLETT2_IOCTL_PVERSION:
+ return put_user(SCARLETT2_HWDEP_VERSION,
+ (int __user *)arg) ? -EFAULT : 0;
+
+ case SCARLETT2_IOCTL_REBOOT:
+ return scarlett2_reboot(mixer);
+
+ case SCARLETT2_IOCTL_SELECT_FLASH_SEGMENT:
+ return scarlett2_ioctl_select_flash_segment(mixer, arg);
+
+ case SCARLETT2_IOCTL_ERASE_FLASH_SEGMENT:
+ return scarlett2_ioctl_erase_flash_segment(mixer);
+
+ case SCARLETT2_IOCTL_GET_ERASE_PROGRESS:
+ return scarlett2_ioctl_get_erase_progress(mixer, arg);
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+static long scarlett2_hwdep_write(struct snd_hwdep *hw,
+ const char __user *buf,
+ long count, loff_t *offset)
+{
+ struct usb_mixer_interface *mixer = hw->private_data;
+ struct scarlett2_data *private = mixer->private_data;
+ int segment_id, segment_num, err, len;
+ int flash_size;
+
+ /* SCARLETT2_USB_WRITE_SEGMENT request data */
+ struct {
+ __le32 segment_num;
+ __le32 offset;
+ __le32 pad;
+ u8 data[];
+ } __packed *req;
+
+ /* Calculate the maximum permitted in data[] */
+ const size_t max_data_size = SCARLETT2_FLASH_WRITE_MAX -
+ offsetof(typeof(*req), data);
+
+ /* If erasing, wait for it to complete */
+ if (private->flash_write_state ==
+ SCARLETT2_FLASH_WRITE_STATE_ERASING) {
+ err = scarlett2_wait_for_erase(mixer);
+ if (err < 0)
+ return err;
+ private->flash_write_state = SCARLETT2_FLASH_WRITE_STATE_WRITE;
+
+ /* Check that an erase has been done & completed */
+ } else if (private->flash_write_state !=
+ SCARLETT2_FLASH_WRITE_STATE_WRITE) {
+ return -EINVAL;
+ }
+
+ /* Check that we're writing to the upgrade firmware */
+ segment_id = private->selected_flash_segment_id;
+ if (segment_id != SCARLETT2_SEGMENT_ID_FIRMWARE)
+ return -EINVAL;
+
+ segment_num = private->flash_segment_nums[segment_id];
+ if (segment_num < SCARLETT2_SEGMENT_NUM_MIN ||
+ segment_num > SCARLETT2_SEGMENT_NUM_MAX)
+ return -EFAULT;
+
+ /* Validate the offset and count */
+ flash_size = private->flash_segment_blocks[segment_id] *
+ SCARLETT2_FLASH_BLOCK_SIZE;
+
+ if (count < 0 || *offset < 0 || *offset + count >= flash_size)
+ return -EINVAL;
+
+ if (!count)
+ return 0;
+
+ /* Limit the *req size to SCARLETT2_FLASH_WRITE_MAX */
+ if (count > max_data_size)
+ count = max_data_size;
+
+ /* Create and send the request */
+ len = struct_size(req, data, count);
+ req = kzalloc(len, GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ req->segment_num = cpu_to_le32(segment_num);
+ req->offset = cpu_to_le32(*offset);
+ req->pad = 0;
+
+ if (copy_from_user(req->data, buf, count)) {
+ err = -EFAULT;
+ goto error;
+ }
+
+ err = scarlett2_usb(mixer, SCARLETT2_USB_WRITE_SEGMENT,
+ req, len, NULL, 0);
+ if (err < 0)
+ goto error;
+
+ *offset += count;
+ err = count;
+
+error:
+ kfree(req);
+ return err;
+}
+
+static int scarlett2_hwdep_release(struct snd_hwdep *hw, struct file *file)
+{
+ struct usb_mixer_interface *mixer = hw->private_data;
+ struct scarlett2_data *private = mixer->private_data;
+
+ /* Return from the SELECTED or WRITE state to IDLE.
+ * The ERASING state is left as-is, and checked on next open.
+ */
+ if (private &&
+ private->hwdep_in_use &&
+ private->flash_write_state != SCARLETT2_FLASH_WRITE_STATE_ERASING)
+ private->flash_write_state = SCARLETT2_FLASH_WRITE_STATE_IDLE;
+
+ return 0;
+}
+
+static int scarlett2_hwdep_init(struct usb_mixer_interface *mixer)
+{
+ struct snd_hwdep *hw;
+ int err;
+
+ err = snd_hwdep_new(mixer->chip->card, "Focusrite Control", 0, &hw);
+ if (err < 0)
+ return err;
+
+ hw->private_data = mixer;
+ hw->exclusive = 1;
+ hw->ops.open = scarlett2_hwdep_open;
+ hw->ops.ioctl = scarlett2_hwdep_ioctl;
+ hw->ops.write = scarlett2_hwdep_write;
+ hw->ops.release = scarlett2_hwdep_release;
+
+ return 0;
+}
+
int snd_scarlett2_init(struct usb_mixer_interface *mixer)
{
struct snd_usb_audio *chip = mixer->chip;
@@ -4534,16 +7725,26 @@ int snd_scarlett2_init(struct usb_mixer_interface *mixer)
usb_audio_info(chip,
"Focusrite %s Mixer Driver enabled (pid=0x%04x); "
- "report any issues to g@b4.vu",
+ "report any issues to "
+ "https://github.com/geoffreybennett/scarlett-gen2/issues",
entry->series_name,
USB_ID_PRODUCT(chip->usb_id));
err = snd_scarlett2_controls_create(mixer, entry);
- if (err < 0)
+ if (err < 0) {
usb_audio_err(mixer->chip,
"Error initialising %s Mixer Driver: %d",
entry->series_name,
err);
+ return err;
+ }
+
+ err = scarlett2_hwdep_init(mixer);
+ if (err < 0)
+ usb_audio_err(mixer->chip,
+ "Error creating %s hwdep device: %d",
+ entry->series_name,
+ err);
return err;
}
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 07cc6a201579..09712e61c606 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -2031,10 +2031,14 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_IGNORE_CTL_ERROR),
DEVICE_FLG(0x0499, 0x1509, /* Steinberg UR22 */
QUIRK_FLAG_GENERIC_IMPLICIT_FB),
+ DEVICE_FLG(0x0499, 0x3108, /* Yamaha YIT-W12TX */
+ QUIRK_FLAG_GET_SAMPLE_RATE),
DEVICE_FLG(0x04d8, 0xfeea, /* Benchmark DAC1 Pre */
QUIRK_FLAG_GET_SAMPLE_RATE),
DEVICE_FLG(0x04e8, 0xa051, /* Samsung USBC Headset (AKG) */
QUIRK_FLAG_SKIP_CLOCK_SELECTOR | QUIRK_FLAG_CTL_MSG_DELAY_5M),
+ DEVICE_FLG(0x0525, 0xa4ad, /* Hamedal C20 usb camero */
+ QUIRK_FLAG_IFACE_SKIP_CLOSE),
DEVICE_FLG(0x054c, 0x0b8c, /* Sony WALKMAN NW-A45 DAC */
QUIRK_FLAG_SET_IFACE_FIRST),
DEVICE_FLG(0x0556, 0x0014, /* Phoenix Audio TMX320VC */
@@ -2073,14 +2077,22 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
QUIRK_FLAG_GENERIC_IMPLICIT_FB),
DEVICE_FLG(0x0763, 0x2031, /* M-Audio Fast Track C600 */
QUIRK_FLAG_GENERIC_IMPLICIT_FB),
+ DEVICE_FLG(0x07fd, 0x000b, /* MOTU M Series 2nd hardware revision */
+ QUIRK_FLAG_CTL_MSG_DELAY_1M),
DEVICE_FLG(0x08bb, 0x2702, /* LineX FM Transmitter */
QUIRK_FLAG_IGNORE_CTL_ERROR),
DEVICE_FLG(0x0951, 0x16ad, /* Kingston HyperX */
QUIRK_FLAG_CTL_MSG_DELAY_1M),
DEVICE_FLG(0x0b0e, 0x0349, /* Jabra 550a */
QUIRK_FLAG_CTL_MSG_DELAY_1M),
+ DEVICE_FLG(0x0ecb, 0x205c, /* JBL Quantum610 Wireless */
+ QUIRK_FLAG_FIXED_RATE),
+ DEVICE_FLG(0x0ecb, 0x2069, /* JBL Quantum810 Wireless */
+ QUIRK_FLAG_FIXED_RATE),
DEVICE_FLG(0x0fd9, 0x0008, /* Hauppauge HVR-950Q */
QUIRK_FLAG_SHARE_MEDIA_DEVICE | QUIRK_FLAG_ALIGN_TRANSFER),
+ DEVICE_FLG(0x1224, 0x2a25, /* Jieli Technology USB PHY 2.0 */
+ QUIRK_FLAG_GET_SAMPLE_RATE),
DEVICE_FLG(0x1395, 0x740a, /* Sennheiser DECT */
QUIRK_FLAG_GET_SAMPLE_RATE),
DEVICE_FLG(0x1397, 0x0507, /* Behringer UMC202HD */
@@ -2113,6 +2125,10 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
QUIRK_FLAG_ITF_USB_DSD_DAC | QUIRK_FLAG_CTL_MSG_DELAY),
DEVICE_FLG(0x1901, 0x0191, /* GE B850V3 CP2114 audio interface */
QUIRK_FLAG_GET_SAMPLE_RATE),
+ DEVICE_FLG(0x19f7, 0x0035, /* RODE NT-USB+ */
+ QUIRK_FLAG_GET_SAMPLE_RATE),
+ DEVICE_FLG(0x1bcf, 0x2283, /* NexiGo N930AF FHD Webcam */
+ QUIRK_FLAG_GET_SAMPLE_RATE),
DEVICE_FLG(0x2040, 0x7200, /* Hauppauge HVR-950Q */
QUIRK_FLAG_SHARE_MEDIA_DEVICE | QUIRK_FLAG_ALIGN_TRANSFER),
DEVICE_FLG(0x2040, 0x7201, /* Hauppauge HVR-950Q-MXL */
@@ -2155,6 +2171,12 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
QUIRK_FLAG_IGNORE_CTL_ERROR),
DEVICE_FLG(0x2912, 0x30c8, /* Audioengine D1 */
QUIRK_FLAG_GET_SAMPLE_RATE),
+ DEVICE_FLG(0x2b53, 0x0023, /* Fiero SC-01 (firmware v1.0.0 @ 48 kHz) */
+ QUIRK_FLAG_GENERIC_IMPLICIT_FB),
+ DEVICE_FLG(0x2b53, 0x0024, /* Fiero SC-01 (firmware v1.0.0 @ 96 kHz) */
+ QUIRK_FLAG_GENERIC_IMPLICIT_FB),
+ DEVICE_FLG(0x2b53, 0x0031, /* Fiero SC-01 (firmware v1.1.0) */
+ QUIRK_FLAG_GENERIC_IMPLICIT_FB),
DEVICE_FLG(0x30be, 0x0101, /* Schiit Hel */
QUIRK_FLAG_IGNORE_CTL_ERROR),
DEVICE_FLG(0x413c, 0xa506, /* Dell AE515 sound bar */
@@ -2163,22 +2185,6 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
QUIRK_FLAG_ALIGN_TRANSFER),
DEVICE_FLG(0x534d, 0x2109, /* MacroSilicon MS2109 */
QUIRK_FLAG_ALIGN_TRANSFER),
- DEVICE_FLG(0x1224, 0x2a25, /* Jieli Technology USB PHY 2.0 */
- QUIRK_FLAG_GET_SAMPLE_RATE),
- DEVICE_FLG(0x2b53, 0x0023, /* Fiero SC-01 (firmware v1.0.0 @ 48 kHz) */
- QUIRK_FLAG_GENERIC_IMPLICIT_FB),
- DEVICE_FLG(0x2b53, 0x0024, /* Fiero SC-01 (firmware v1.0.0 @ 96 kHz) */
- QUIRK_FLAG_GENERIC_IMPLICIT_FB),
- DEVICE_FLG(0x2b53, 0x0031, /* Fiero SC-01 (firmware v1.1.0) */
- QUIRK_FLAG_GENERIC_IMPLICIT_FB),
- DEVICE_FLG(0x0525, 0xa4ad, /* Hamedal C20 usb camero */
- QUIRK_FLAG_IFACE_SKIP_CLOSE),
- DEVICE_FLG(0x0ecb, 0x205c, /* JBL Quantum610 Wireless */
- QUIRK_FLAG_FIXED_RATE),
- DEVICE_FLG(0x0ecb, 0x2069, /* JBL Quantum810 Wireless */
- QUIRK_FLAG_FIXED_RATE),
- DEVICE_FLG(0x1bcf, 0x2283, /* NexiGo N930AF FHD Webcam */
- QUIRK_FLAG_GET_SAMPLE_RATE),
/* Vendor matches */
VENDOR_FLG(0x045e, /* MS Lifecam */
diff --git a/sound/virtio/virtio_card.c b/sound/virtio/virtio_card.c
index e2847c040f75..b158c3cb8e5f 100644
--- a/sound/virtio/virtio_card.c
+++ b/sound/virtio/virtio_card.c
@@ -91,8 +91,6 @@ static void virtsnd_event_notify_cb(struct virtqueue *vqueue)
virtsnd_event_dispatch(snd, event);
virtsnd_event_send(vqueue, event, true, GFP_ATOMIC);
}
- if (unlikely(virtqueue_is_broken(vqueue)))
- break;
} while (!virtqueue_enable_cb(vqueue));
spin_unlock_irqrestore(&queue->lock, flags);
}
diff --git a/sound/virtio/virtio_ctl_msg.c b/sound/virtio/virtio_ctl_msg.c
index 18dc5aca2e0c..9dabea01277f 100644
--- a/sound/virtio/virtio_ctl_msg.c
+++ b/sound/virtio/virtio_ctl_msg.c
@@ -303,8 +303,6 @@ void virtsnd_ctl_notify_cb(struct virtqueue *vqueue)
virtqueue_disable_cb(vqueue);
while ((msg = virtqueue_get_buf(vqueue, &length)))
virtsnd_ctl_msg_complete(msg);
- if (unlikely(virtqueue_is_broken(vqueue)))
- break;
} while (!virtqueue_enable_cb(vqueue));
spin_unlock_irqrestore(&queue->lock, flags);
}
diff --git a/sound/virtio/virtio_pcm_msg.c b/sound/virtio/virtio_pcm_msg.c
index 542446c4c7ba..8c32efaf4c52 100644
--- a/sound/virtio/virtio_pcm_msg.c
+++ b/sound/virtio/virtio_pcm_msg.c
@@ -358,8 +358,6 @@ static inline void virtsnd_pcm_notify_cb(struct virtio_snd_queue *queue)
virtqueue_disable_cb(queue->vqueue);
while ((msg = virtqueue_get_buf(queue->vqueue, &written_bytes)))
virtsnd_pcm_msg_complete(msg, written_bytes);
- if (unlikely(virtqueue_is_broken(queue->vqueue)))
- break;
} while (!virtqueue_enable_cb(queue->vqueue));
spin_unlock_irqrestore(&queue->lock, flags);
}
diff --git a/sound/x86/intel_hdmi_audio.c b/sound/x86/intel_hdmi_audio.c
index ab95fb34a635..02f5a7f9b728 100644
--- a/sound/x86/intel_hdmi_audio.c
+++ b/sound/x86/intel_hdmi_audio.c
@@ -30,6 +30,7 @@
#include <sound/control.h>
#include <sound/jack.h>
#include <drm/drm_edid.h>
+#include <drm/drm_eld.h>
#include <drm/intel_lpe_audio.h>
#include "intel_hdmi_audio.h"